···11+package bufpool
22+33+// Append copies the provided bytes onto the end of the buffer, growing its
44+// capacity if required. If src is empty, the method does nothing.
55+//
66+// The receiver retains ownership of the data; the caller may reuse src freely.
77+func (buf *Buffer) Append(src []byte) {
88+ if len(src) == 0 {
99+ return
1010+ }
1111+1212+ start := len(buf.buf)
1313+ buf.ensureCapacity(start + len(src))
1414+ buf.buf = buf.buf[:start+len(src)]
1515+ copy(buf.buf[start:], src)
1616+}
+31
internal/bufpool/borrow.go
···11+package bufpool
22+33+// Borrow retrieves a Buffer suitable for storing up to capHint bytes.
44+// The returned Buffer may come from an internal sync.Pool.
55+//
66+// If capHint is smaller than DefaultBufferCap, it is automatically raised
77+// to DefaultBufferCap. If no pooled buffer has sufficient capacity, a new
88+// unpooled buffer is allocated.
99+//
1010+// The caller must call Release() when finished using the returned Buffer.
1111+func Borrow(capHint int) Buffer {
1212+ if capHint < DefaultBufferCap {
1313+ capHint = DefaultBufferCap
1414+ }
1515+1616+ classIdx, classCap, pooled := classFor(capHint)
1717+ if !pooled {
1818+ newBuf := make([]byte, 0, capHint)
1919+2020+ return Buffer{buf: newBuf, pool: unpooled}
2121+ }
2222+ //nolint:forcetypeassert
2323+ buf := bufferPools[classIdx].Get().(*[]byte)
2424+ if cap(*buf) < classCap {
2525+ *buf = make([]byte, 0, classCap)
2626+ }
2727+2828+ slice := (*buf)[:0]
2929+3030+ return Buffer{buf: slice, pool: poolIndex(classIdx)} //#nosec G115
3131+}
+24
internal/bufpool/buffer.go
···11+package bufpool
22+33+// Buffer is a growable byte container that optionally participates in a
44+// memory pool. A Buffer may be obtained through Borrow() or constructed
55+// directly from owned data via FromOwned().
66+//
77+// A Buffer's underlying slice may grow as needed. When finished with a
88+// pooled buffer, the caller should invoke Release() to return it to the pool.
99+//
1010+// Buffers must not be copied after first use; doing so can cause double-returns
1111+// to the pool and data races.
1212+//
1313+// In general, pass Buffer around when used internally, and directly .Bytes() when
1414+// returning output across our API boundary. It is neither necessary nor efficient
1515+// to copy/append the .Bytes() to a newly-allocated slice; in cases where we do
1616+// want the raw byte slice out of our API boundary, it is perfectly acceptable to
1717+// simply not call Release().
1818+//
1919+//go:nocopy
2020+type Buffer struct {
2121+ _ struct{} // for nocopy
2222+ buf []byte
2323+ pool poolIndex
2424+}
-214
internal/bufpool/buffers.go
···11-// Package bufpool provides a lightweight byte-buffer type with optional
22-// pooling.
33-package bufpool
44-55-import "sync"
66-77-const (
88- // DefaultBufferCap is the minimum capacity a borrowed buffer will have.
99- // Borrow() will allocate or retrieve a buffer with at least this capacity.
1010- DefaultBufferCap = 32 * 1024
1111-1212- // maxPooledBuffer defines the maximum capacity of a buffer that may be
1313- // returned to the pool. Buffers larger than this will not be pooled to
1414- // avoid unbounded memory usage.
1515- maxPooledBuffer = 8 << 20
1616-)
1717-1818-// Buffer is a growable byte container that optionally participates in a
1919-// memory pool. A Buffer may be obtained through Borrow() or constructed
2020-// directly from owned data via FromOwned().
2121-//
2222-// A Buffer's underlying slice may grow as needed. When finished with a
2323-// pooled buffer, the caller should invoke Release() to return it to the pool.
2424-//
2525-// Buffers must not be copied after first use; doing so can cause double-returns
2626-// to the pool and data races.
2727-//
2828-// In general, pass Buffer around when used internally, and directly .Bytes() when
2929-// returning output across our API boundary. It is neither necessary nor efficient
3030-// to copy/append the .Bytes() to a newly-allocated slice; in cases where we do
3131-// want the raw byte slice out of our API boundary, it is perfectly acceptable to
3232-// simply not call Release().
3333-//
3434-//go:nocopy
3535-type Buffer struct {
3636- _ struct{} // for nocopy
3737- buf []byte
3838- pool poolIndex
3939-}
4040-4141-type poolIndex int8
4242-4343-const (
4444- unpooled poolIndex = -1
4545-)
4646-4747-var sizeClasses = [...]int{
4848- DefaultBufferCap,
4949- 64 << 10,
5050- 128 << 10,
5151- 256 << 10,
5252- 512 << 10,
5353- 1 << 20,
5454- 2 << 20,
5555- 4 << 20,
5656- maxPooledBuffer,
5757-}
5858-5959-var bufferPools = func() []sync.Pool {
6060- pools := make([]sync.Pool, len(sizeClasses))
6161- for i, classCap := range sizeClasses {
6262- capCopy := classCap
6363- pools[i].New = func() any {
6464- buf := make([]byte, 0, capCopy)
6565-6666- return &buf
6767- }
6868- }
6969-7070- return pools
7171-}()
7272-7373-// Borrow retrieves a Buffer suitable for storing up to capHint bytes.
7474-// The returned Buffer may come from an internal sync.Pool.
7575-//
7676-// If capHint is smaller than DefaultBufferCap, it is automatically raised
7777-// to DefaultBufferCap. If no pooled buffer has sufficient capacity, a new
7878-// unpooled buffer is allocated.
7979-//
8080-// The caller must call Release() when finished using the returned Buffer.
8181-func Borrow(capHint int) Buffer {
8282- if capHint < DefaultBufferCap {
8383- capHint = DefaultBufferCap
8484- }
8585-8686- classIdx, classCap, pooled := classFor(capHint)
8787- if !pooled {
8888- newBuf := make([]byte, 0, capHint)
8989-9090- return Buffer{buf: newBuf, pool: unpooled}
9191- }
9292- //nolint:forcetypeassert
9393- buf := bufferPools[classIdx].Get().(*[]byte)
9494- if cap(*buf) < classCap {
9595- *buf = make([]byte, 0, classCap)
9696- }
9797-9898- slice := (*buf)[:0]
9999-100100- return Buffer{buf: slice, pool: poolIndex(classIdx)} //#nosec G115
101101-}
102102-103103-// FromOwned constructs a Buffer from a caller-owned byte slice. The resulting
104104-// Buffer does not participate in pooling and will never be returned to the
105105-// internal pool when released.
106106-func FromOwned(buf []byte) Buffer {
107107- return Buffer{buf: buf, pool: unpooled}
108108-}
109109-110110-// Resize adjusts the length of the buffer to n bytes. If n exceeds the current
111111-// capacity, the underlying storage is grown. If n is negative, it is treated
112112-// as zero.
113113-//
114114-// The buffer's new contents beyond the previous length are undefined.
115115-func (buf *Buffer) Resize(n int) {
116116- if n < 0 {
117117- n = 0
118118- }
119119-120120- buf.ensureCapacity(n)
121121- buf.buf = buf.buf[:n]
122122-}
123123-124124-// Append copies the provided bytes onto the end of the buffer, growing its
125125-// capacity if required. If src is empty, the method does nothing.
126126-//
127127-// The receiver retains ownership of the data; the caller may reuse src freely.
128128-func (buf *Buffer) Append(src []byte) {
129129- if len(src) == 0 {
130130- return
131131- }
132132-133133- start := len(buf.buf)
134134- buf.ensureCapacity(start + len(src))
135135- buf.buf = buf.buf[:start+len(src)]
136136- copy(buf.buf[start:], src)
137137-}
138138-139139-// Bytes returns the underlying byte slice that represents the current contents
140140-// of the buffer. Modifying the returned slice modifies the Buffer itself.
141141-func (buf *Buffer) Bytes() []byte {
142142- return buf.buf
143143-}
144144-145145-// Release returns the buffer to the global pool if it originated from the
146146-// pool and its capacity is no larger than maxPooledBuffer. After release, the
147147-// Buffer becomes invalid and should not be used further.
148148-//
149149-// Releasing a non-pooled buffer has no effect beyond clearing its internal
150150-// storage.
151151-func (buf *Buffer) Release() {
152152- if buf.buf == nil {
153153- return
154154- }
155155-156156- buf.returnToPool()
157157- buf.buf = nil
158158- buf.pool = unpooled
159159-}
160160-161161-// ensureCapacity grows the underlying buffer to accommodate the requested
162162-// number of bytes. Growth doubles the capacity by default unless a larger
163163-// expansion is needed. If the previous storage was pooled and not oversized,
164164-// it is returned to the pool.
165165-func (buf *Buffer) ensureCapacity(needed int) {
166166- if cap(buf.buf) >= needed {
167167- return
168168- }
169169-170170- classIdx, classCap, pooled := classFor(needed)
171171-172172- var newBuf []byte
173173-174174- if pooled {
175175- //nolint:forcetypeassert
176176- raw := bufferPools[classIdx].Get().(*[]byte)
177177- if cap(*raw) < classCap {
178178- *raw = make([]byte, 0, classCap)
179179- }
180180-181181- newBuf = (*raw)[:len(buf.buf)]
182182- } else {
183183- newBuf = make([]byte, len(buf.buf), classCap)
184184- }
185185-186186- copy(newBuf, buf.buf)
187187- buf.returnToPool()
188188-189189- buf.buf = newBuf
190190- if pooled {
191191- buf.pool = poolIndex(classIdx) //#nosec G115
192192- } else {
193193- buf.pool = unpooled
194194- }
195195-}
196196-197197-func classFor(size int) (idx, classCap int, ok bool) {
198198- for i, class := range sizeClasses {
199199- if size <= class {
200200- return i, class, true
201201- }
202202- }
203203-204204- return -1, size, false
205205-}
206206-207207-func (buf *Buffer) returnToPool() {
208208- if buf.pool == unpooled {
209209- return
210210- }
211211-212212- tmp := buf.buf[:0]
213213- bufferPools[int(buf.pool)].Put(&tmp)
214214-}
+7
internal/bufpool/bytes.go
···11+package bufpool
22+33+// Bytes returns the underlying byte slice that represents the current contents
44+// of the buffer. Modifying the returned slice modifies the Buffer itself.
55+func (buf *Buffer) Bytes() []byte {
66+ return buf.buf
77+}
+37
internal/bufpool/capacity.go
···11+package bufpool
22+33+// ensureCapacity grows the underlying buffer to accommodate the requested
44+// number of bytes. Growth doubles the capacity by default unless a larger
55+// expansion is needed. If the previous storage was pooled and not oversized,
66+// it is returned to the pool.
77+func (buf *Buffer) ensureCapacity(needed int) {
88+ if cap(buf.buf) >= needed {
99+ return
1010+ }
1111+1212+ classIdx, classCap, pooled := classFor(needed)
1313+1414+ var newBuf []byte
1515+1616+ if pooled {
1717+ //nolint:forcetypeassert
1818+ raw := bufferPools[classIdx].Get().(*[]byte)
1919+ if cap(*raw) < classCap {
2020+ *raw = make([]byte, 0, classCap)
2121+ }
2222+2323+ newBuf = (*raw)[:len(buf.buf)]
2424+ } else {
2525+ newBuf = make([]byte, len(buf.buf), classCap)
2626+ }
2727+2828+ copy(newBuf, buf.buf)
2929+ buf.returnToPool()
3030+3131+ buf.buf = newBuf
3232+ if pooled {
3333+ buf.pool = poolIndex(classIdx) //#nosec G115
3434+ } else {
3535+ buf.pool = unpooled
3636+ }
3737+}
+23
internal/bufpool/class.go
···11+package bufpool
22+33+var sizeClasses = [...]int{
44+ DefaultBufferCap,
55+ 64 << 10,
66+ 128 << 10,
77+ 256 << 10,
88+ 512 << 10,
99+ 1 << 20,
1010+ 2 << 20,
1111+ 4 << 20,
1212+ maxPooledBuffer,
1313+}
1414+1515+func classFor(size int) (idx, classCap int, ok bool) {
1616+ for i, class := range sizeClasses {
1717+ if size <= class {
1818+ return i, class, true
1919+ }
2020+ }
2121+2222+ return -1, size, false
2323+}
+12
internal/bufpool/consts.go
···11+package bufpool
22+33+const (
44+ // DefaultBufferCap is the minimum capacity a borrowed buffer will have.
55+ // Borrow() will allocate or retrieve a buffer with at least this capacity.
66+ DefaultBufferCap = 32 * 1024
77+88+ // maxPooledBuffer defines the maximum capacity of a buffer that may be
99+ // returned to the pool. Buffers larger than this will not be pooled to
1010+ // avoid unbounded memory usage.
1111+ maxPooledBuffer = 8 << 20
1212+)
+3
internal/bufpool/doc.go
···11+// Package bufpool provides a lightweight byte-buffer type with optional
22+// pooling.
33+package bufpool
+8
internal/bufpool/from_owned.go
···11+package bufpool
22+33+// FromOwned constructs a Buffer from a caller-owned byte slice. The resulting
44+// Buffer does not participate in pooling and will never be returned to the
55+// internal pool when released.
66+func FromOwned(buf []byte) Buffer {
77+ return Buffer{buf: buf, pool: unpooled}
88+}
···11+package bufpool
22+33+// Release returns the buffer to the global pool if it originated from the
44+// pool and its capacity is no larger than maxPooledBuffer. After release, the
55+// Buffer becomes invalid and should not be used further.
66+//
77+// Releasing a non-pooled buffer has no effect beyond clearing its internal
88+// storage.
99+func (buf *Buffer) Release() {
1010+ if buf.buf == nil {
1111+ return
1212+ }
1313+1414+ buf.returnToPool()
1515+ buf.buf = nil
1616+ buf.pool = unpooled
1717+}
+15
internal/bufpool/resize.go
···11+package bufpool
22+33+// Resize adjusts the length of the buffer to n bytes. If n exceeds the current
44+// capacity, the underlying storage is grown. If n is negative, it is treated
55+// as zero.
66+//
77+// The buffer's new contents beyond the previous length are undefined.
88+func (buf *Buffer) Resize(n int) {
99+ if n < 0 {
1010+ n = 0
1111+ }
1212+1313+ buf.ensureCapacity(n)
1414+ buf.buf = buf.buf[:n]
1515+}