mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-19 18:07:45 +08:00

Removes gogo/protobuf from buildx and updates to a version of moby/buildkit where gogo is removed. This also changes how the proto files are generated. This is because newer versions of protobuf are more strict about name conflicts. If two files have the same name (even if they are relative paths) and are used in different protoc commands, they'll conflict in the registry. Since protobuf file generation doesn't work very well with `paths=source_relative`, this removes the `go:generate` expression and just relies on the dockerfile to perform the generation. Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
253 lines
6.2 KiB
Go
253 lines
6.2 KiB
Go
/*
|
|
*
|
|
* Copyright 2024 gRPC authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
// Package mem provides utilities that facilitate memory reuse in byte slices
|
|
// that are used as buffers.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: All APIs in this package are EXPERIMENTAL and may be changed or
|
|
// removed in a later release.
|
|
package mem
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
// A Buffer represents a reference counted piece of data (in bytes) that can be
|
|
// acquired by a call to NewBuffer() or Copy(). A reference to a Buffer may be
|
|
// released by calling Free(), which invokes the free function given at creation
|
|
// only after all references are released.
|
|
//
|
|
// Note that a Buffer is not safe for concurrent access and instead each
|
|
// goroutine should use its own reference to the data, which can be acquired via
|
|
// a call to Ref().
|
|
//
|
|
// Attempts to access the underlying data after releasing the reference to the
|
|
// Buffer will panic.
|
|
type Buffer interface {
|
|
// ReadOnlyData returns the underlying byte slice. Note that it is undefined
|
|
// behavior to modify the contents of this slice in any way.
|
|
ReadOnlyData() []byte
|
|
// Ref increases the reference counter for this Buffer.
|
|
Ref()
|
|
// Free decrements this Buffer's reference counter and frees the underlying
|
|
// byte slice if the counter reaches 0 as a result of this call.
|
|
Free()
|
|
// Len returns the Buffer's size.
|
|
Len() int
|
|
|
|
split(n int) (left, right Buffer)
|
|
read(buf []byte) (int, Buffer)
|
|
}
|
|
|
|
var (
|
|
bufferPoolingThreshold = 1 << 10
|
|
|
|
bufferObjectPool = sync.Pool{New: func() any { return new(buffer) }}
|
|
refObjectPool = sync.Pool{New: func() any { return new(atomic.Int32) }}
|
|
)
|
|
|
|
func IsBelowBufferPoolingThreshold(size int) bool {
|
|
return size <= bufferPoolingThreshold
|
|
}
|
|
|
|
type buffer struct {
|
|
origData *[]byte
|
|
data []byte
|
|
refs *atomic.Int32
|
|
pool BufferPool
|
|
}
|
|
|
|
func newBuffer() *buffer {
|
|
return bufferObjectPool.Get().(*buffer)
|
|
}
|
|
|
|
// NewBuffer creates a new Buffer from the given data, initializing the reference
|
|
// counter to 1. The data will then be returned to the given pool when all
|
|
// references to the returned Buffer are released. As a special case to avoid
|
|
// additional allocations, if the given buffer pool is nil, the returned buffer
|
|
// will be a "no-op" Buffer where invoking Buffer.Free() does nothing and the
|
|
// underlying data is never freed.
|
|
//
|
|
// Note that the backing array of the given data is not copied.
|
|
func NewBuffer(data *[]byte, pool BufferPool) Buffer {
|
|
if pool == nil || IsBelowBufferPoolingThreshold(len(*data)) {
|
|
return (SliceBuffer)(*data)
|
|
}
|
|
b := newBuffer()
|
|
b.origData = data
|
|
b.data = *data
|
|
b.pool = pool
|
|
b.refs = refObjectPool.Get().(*atomic.Int32)
|
|
b.refs.Add(1)
|
|
return b
|
|
}
|
|
|
|
// Copy creates a new Buffer from the given data, initializing the reference
|
|
// counter to 1.
|
|
//
|
|
// It acquires a []byte from the given pool and copies over the backing array
|
|
// of the given data. The []byte acquired from the pool is returned to the
|
|
// pool when all references to the returned Buffer are released.
|
|
func Copy(data []byte, pool BufferPool) Buffer {
|
|
if IsBelowBufferPoolingThreshold(len(data)) {
|
|
buf := make(SliceBuffer, len(data))
|
|
copy(buf, data)
|
|
return buf
|
|
}
|
|
|
|
buf := pool.Get(len(data))
|
|
copy(*buf, data)
|
|
return NewBuffer(buf, pool)
|
|
}
|
|
|
|
func (b *buffer) ReadOnlyData() []byte {
|
|
if b.refs == nil {
|
|
panic("Cannot read freed buffer")
|
|
}
|
|
return b.data
|
|
}
|
|
|
|
func (b *buffer) Ref() {
|
|
if b.refs == nil {
|
|
panic("Cannot ref freed buffer")
|
|
}
|
|
b.refs.Add(1)
|
|
}
|
|
|
|
func (b *buffer) Free() {
|
|
if b.refs == nil {
|
|
panic("Cannot free freed buffer")
|
|
}
|
|
|
|
refs := b.refs.Add(-1)
|
|
switch {
|
|
case refs > 0:
|
|
return
|
|
case refs == 0:
|
|
if b.pool != nil {
|
|
b.pool.Put(b.origData)
|
|
}
|
|
|
|
refObjectPool.Put(b.refs)
|
|
b.origData = nil
|
|
b.data = nil
|
|
b.refs = nil
|
|
b.pool = nil
|
|
bufferObjectPool.Put(b)
|
|
default:
|
|
panic("Cannot free freed buffer")
|
|
}
|
|
}
|
|
|
|
func (b *buffer) Len() int {
|
|
return len(b.ReadOnlyData())
|
|
}
|
|
|
|
func (b *buffer) split(n int) (Buffer, Buffer) {
|
|
if b.refs == nil {
|
|
panic("Cannot split freed buffer")
|
|
}
|
|
|
|
b.refs.Add(1)
|
|
split := newBuffer()
|
|
split.origData = b.origData
|
|
split.data = b.data[n:]
|
|
split.refs = b.refs
|
|
split.pool = b.pool
|
|
|
|
b.data = b.data[:n]
|
|
|
|
return b, split
|
|
}
|
|
|
|
func (b *buffer) read(buf []byte) (int, Buffer) {
|
|
if b.refs == nil {
|
|
panic("Cannot read freed buffer")
|
|
}
|
|
|
|
n := copy(buf, b.data)
|
|
if n == len(b.data) {
|
|
b.Free()
|
|
return n, nil
|
|
}
|
|
|
|
b.data = b.data[n:]
|
|
return n, b
|
|
}
|
|
|
|
// String returns a string representation of the buffer. May be used for
|
|
// debugging purposes.
|
|
func (b *buffer) String() string {
|
|
return fmt.Sprintf("mem.Buffer(%p, data: %p, length: %d)", b, b.ReadOnlyData(), len(b.ReadOnlyData()))
|
|
}
|
|
|
|
func ReadUnsafe(dst []byte, buf Buffer) (int, Buffer) {
|
|
return buf.read(dst)
|
|
}
|
|
|
|
// SplitUnsafe modifies the receiver to point to the first n bytes while it
|
|
// returns a new reference to the remaining bytes. The returned Buffer functions
|
|
// just like a normal reference acquired using Ref().
|
|
func SplitUnsafe(buf Buffer, n int) (left, right Buffer) {
|
|
return buf.split(n)
|
|
}
|
|
|
|
type emptyBuffer struct{}
|
|
|
|
func (e emptyBuffer) ReadOnlyData() []byte {
|
|
return nil
|
|
}
|
|
|
|
func (e emptyBuffer) Ref() {}
|
|
func (e emptyBuffer) Free() {}
|
|
|
|
func (e emptyBuffer) Len() int {
|
|
return 0
|
|
}
|
|
|
|
func (e emptyBuffer) split(n int) (left, right Buffer) {
|
|
return e, e
|
|
}
|
|
|
|
func (e emptyBuffer) read(buf []byte) (int, Buffer) {
|
|
return 0, e
|
|
}
|
|
|
|
type SliceBuffer []byte
|
|
|
|
func (s SliceBuffer) ReadOnlyData() []byte { return s }
|
|
func (s SliceBuffer) Ref() {}
|
|
func (s SliceBuffer) Free() {}
|
|
func (s SliceBuffer) Len() int { return len(s) }
|
|
|
|
func (s SliceBuffer) split(n int) (left, right Buffer) {
|
|
return s[:n], s[n:]
|
|
}
|
|
|
|
func (s SliceBuffer) read(buf []byte) (int, Buffer) {
|
|
n := copy(buf, s)
|
|
if n == len(s) {
|
|
return n, nil
|
|
}
|
|
return n, s[n:]
|
|
}
|