···1+package bufpool
2+3+// Append copies the provided bytes onto the end of the buffer, growing its
4+// capacity if required. If src is empty, the method does nothing.
5+//
6+// The receiver retains ownership of the data; the caller may reuse src freely.
7+func (buf *Buffer) Append(src []byte) {
8+ if len(src) == 0 {
9+ return
10+ }
11+12+ start := len(buf.buf)
13+ buf.ensureCapacity(start + len(src))
14+ buf.buf = buf.buf[:start+len(src)]
15+ copy(buf.buf[start:], src)
16+}
+31
internal/bufpool/borrow.go
···0000000000000000000000000000000
···1+package bufpool
2+3+// Borrow retrieves a Buffer suitable for storing up to capHint bytes.
4+// The returned Buffer may come from an internal sync.Pool.
5+//
6+// If capHint is smaller than DefaultBufferCap, it is automatically raised
7+// to DefaultBufferCap. If no pooled buffer has sufficient capacity, a new
8+// unpooled buffer is allocated.
9+//
10+// The caller must call Release() when finished using the returned Buffer.
11+func Borrow(capHint int) Buffer {
12+ if capHint < DefaultBufferCap {
13+ capHint = DefaultBufferCap
14+ }
15+16+ classIdx, classCap, pooled := classFor(capHint)
17+ if !pooled {
18+ newBuf := make([]byte, 0, capHint)
19+20+ return Buffer{buf: newBuf, pool: unpooled}
21+ }
22+ //nolint:forcetypeassert
23+ buf := bufferPools[classIdx].Get().(*[]byte)
24+ if cap(*buf) < classCap {
25+ *buf = make([]byte, 0, classCap)
26+ }
27+28+ slice := (*buf)[:0]
29+30+ return Buffer{buf: slice, pool: poolIndex(classIdx)} //#nosec G115
31+}
+24
internal/bufpool/buffer.go
···000000000000000000000000
···1+package bufpool
2+3+// Buffer is a growable byte container that optionally participates in a
4+// memory pool. A Buffer may be obtained through Borrow() or constructed
5+// directly from owned data via FromOwned().
6+//
7+// A Buffer's underlying slice may grow as needed. When finished with a
8+// pooled buffer, the caller should invoke Release() to return it to the pool.
9+//
10+// Buffers must not be copied after first use; doing so can cause double-returns
11+// to the pool and data races.
12+//
13+// In general, pass Buffer around when used internally, and directly .Bytes() when
14+// returning output across our API boundary. It is neither necessary nor efficient
15+// to copy/append the .Bytes() to a newly-allocated slice; in cases where we do
16+// want the raw byte slice out of our API boundary, it is perfectly acceptable to
17+// simply not call Release().
18+//
19+//go:nocopy
20+type Buffer struct {
21+ _ struct{} // for nocopy
22+ buf []byte
23+ pool poolIndex
24+}
-214
internal/bufpool/buffers.go
···1-// Package bufpool provides a lightweight byte-buffer type with optional
2-// pooling.
3-package bufpool
4-5-import "sync"
6-7-const (
8- // DefaultBufferCap is the minimum capacity a borrowed buffer will have.
9- // Borrow() will allocate or retrieve a buffer with at least this capacity.
10- DefaultBufferCap = 32 * 1024
11-12- // maxPooledBuffer defines the maximum capacity of a buffer that may be
13- // returned to the pool. Buffers larger than this will not be pooled to
14- // avoid unbounded memory usage.
15- maxPooledBuffer = 8 << 20
16-)
17-18-// Buffer is a growable byte container that optionally participates in a
19-// memory pool. A Buffer may be obtained through Borrow() or constructed
20-// directly from owned data via FromOwned().
21-//
22-// A Buffer's underlying slice may grow as needed. When finished with a
23-// pooled buffer, the caller should invoke Release() to return it to the pool.
24-//
25-// Buffers must not be copied after first use; doing so can cause double-returns
26-// to the pool and data races.
27-//
28-// In general, pass Buffer around when used internally, and directly .Bytes() when
29-// returning output across our API boundary. It is neither necessary nor efficient
30-// to copy/append the .Bytes() to a newly-allocated slice; in cases where we do
31-// want the raw byte slice out of our API boundary, it is perfectly acceptable to
32-// simply not call Release().
33-//
34-//go:nocopy
35-type Buffer struct {
36- _ struct{} // for nocopy
37- buf []byte
38- pool poolIndex
39-}
40-41-type poolIndex int8
42-43-const (
44- unpooled poolIndex = -1
45-)
46-47-var sizeClasses = [...]int{
48- DefaultBufferCap,
49- 64 << 10,
50- 128 << 10,
51- 256 << 10,
52- 512 << 10,
53- 1 << 20,
54- 2 << 20,
55- 4 << 20,
56- maxPooledBuffer,
57-}
58-59-var bufferPools = func() []sync.Pool {
60- pools := make([]sync.Pool, len(sizeClasses))
61- for i, classCap := range sizeClasses {
62- capCopy := classCap
63- pools[i].New = func() any {
64- buf := make([]byte, 0, capCopy)
65-66- return &buf
67- }
68- }
69-70- return pools
71-}()
72-73-// Borrow retrieves a Buffer suitable for storing up to capHint bytes.
74-// The returned Buffer may come from an internal sync.Pool.
75-//
76-// If capHint is smaller than DefaultBufferCap, it is automatically raised
77-// to DefaultBufferCap. If no pooled buffer has sufficient capacity, a new
78-// unpooled buffer is allocated.
79-//
80-// The caller must call Release() when finished using the returned Buffer.
81-func Borrow(capHint int) Buffer {
82- if capHint < DefaultBufferCap {
83- capHint = DefaultBufferCap
84- }
85-86- classIdx, classCap, pooled := classFor(capHint)
87- if !pooled {
88- newBuf := make([]byte, 0, capHint)
89-90- return Buffer{buf: newBuf, pool: unpooled}
91- }
92- //nolint:forcetypeassert
93- buf := bufferPools[classIdx].Get().(*[]byte)
94- if cap(*buf) < classCap {
95- *buf = make([]byte, 0, classCap)
96- }
97-98- slice := (*buf)[:0]
99-100- return Buffer{buf: slice, pool: poolIndex(classIdx)} //#nosec G115
101-}
102-103-// FromOwned constructs a Buffer from a caller-owned byte slice. The resulting
104-// Buffer does not participate in pooling and will never be returned to the
105-// internal pool when released.
106-func FromOwned(buf []byte) Buffer {
107- return Buffer{buf: buf, pool: unpooled}
108-}
109-110-// Resize adjusts the length of the buffer to n bytes. If n exceeds the current
111-// capacity, the underlying storage is grown. If n is negative, it is treated
112-// as zero.
113-//
114-// The buffer's new contents beyond the previous length are undefined.
115-func (buf *Buffer) Resize(n int) {
116- if n < 0 {
117- n = 0
118- }
119-120- buf.ensureCapacity(n)
121- buf.buf = buf.buf[:n]
122-}
123-124-// Append copies the provided bytes onto the end of the buffer, growing its
125-// capacity if required. If src is empty, the method does nothing.
126-//
127-// The receiver retains ownership of the data; the caller may reuse src freely.
128-func (buf *Buffer) Append(src []byte) {
129- if len(src) == 0 {
130- return
131- }
132-133- start := len(buf.buf)
134- buf.ensureCapacity(start + len(src))
135- buf.buf = buf.buf[:start+len(src)]
136- copy(buf.buf[start:], src)
137-}
138-139-// Bytes returns the underlying byte slice that represents the current contents
140-// of the buffer. Modifying the returned slice modifies the Buffer itself.
141-func (buf *Buffer) Bytes() []byte {
142- return buf.buf
143-}
144-145-// Release returns the buffer to the global pool if it originated from the
146-// pool and its capacity is no larger than maxPooledBuffer. After release, the
147-// Buffer becomes invalid and should not be used further.
148-//
149-// Releasing a non-pooled buffer has no effect beyond clearing its internal
150-// storage.
151-func (buf *Buffer) Release() {
152- if buf.buf == nil {
153- return
154- }
155-156- buf.returnToPool()
157- buf.buf = nil
158- buf.pool = unpooled
159-}
160-161-// ensureCapacity grows the underlying buffer to accommodate the requested
162-// number of bytes. Growth doubles the capacity by default unless a larger
163-// expansion is needed. If the previous storage was pooled and not oversized,
164-// it is returned to the pool.
165-func (buf *Buffer) ensureCapacity(needed int) {
166- if cap(buf.buf) >= needed {
167- return
168- }
169-170- classIdx, classCap, pooled := classFor(needed)
171-172- var newBuf []byte
173-174- if pooled {
175- //nolint:forcetypeassert
176- raw := bufferPools[classIdx].Get().(*[]byte)
177- if cap(*raw) < classCap {
178- *raw = make([]byte, 0, classCap)
179- }
180-181- newBuf = (*raw)[:len(buf.buf)]
182- } else {
183- newBuf = make([]byte, len(buf.buf), classCap)
184- }
185-186- copy(newBuf, buf.buf)
187- buf.returnToPool()
188-189- buf.buf = newBuf
190- if pooled {
191- buf.pool = poolIndex(classIdx) //#nosec G115
192- } else {
193- buf.pool = unpooled
194- }
195-}
196-197-func classFor(size int) (idx, classCap int, ok bool) {
198- for i, class := range sizeClasses {
199- if size <= class {
200- return i, class, true
201- }
202- }
203-204- return -1, size, false
205-}
206-207-func (buf *Buffer) returnToPool() {
208- if buf.pool == unpooled {
209- return
210- }
211-212- tmp := buf.buf[:0]
213- bufferPools[int(buf.pool)].Put(&tmp)
214-}
···1+package bufpool
2+3+// Bytes returns the underlying byte slice that represents the current contents
4+// of the buffer. Modifying the returned slice modifies the Buffer itself.
5+func (buf *Buffer) Bytes() []byte {
6+ return buf.buf
7+}
+37
internal/bufpool/capacity.go
···0000000000000000000000000000000000000
···1+package bufpool
2+3+// ensureCapacity grows the underlying buffer to accommodate the requested
4+// number of bytes. Growth doubles the capacity by default unless a larger
5+// expansion is needed. If the previous storage was pooled and not oversized,
6+// it is returned to the pool.
7+func (buf *Buffer) ensureCapacity(needed int) {
8+ if cap(buf.buf) >= needed {
9+ return
10+ }
11+12+ classIdx, classCap, pooled := classFor(needed)
13+14+ var newBuf []byte
15+16+ if pooled {
17+ //nolint:forcetypeassert
18+ raw := bufferPools[classIdx].Get().(*[]byte)
19+ if cap(*raw) < classCap {
20+ *raw = make([]byte, 0, classCap)
21+ }
22+23+ newBuf = (*raw)[:len(buf.buf)]
24+ } else {
25+ newBuf = make([]byte, len(buf.buf), classCap)
26+ }
27+28+ copy(newBuf, buf.buf)
29+ buf.returnToPool()
30+31+ buf.buf = newBuf
32+ if pooled {
33+ buf.pool = poolIndex(classIdx) //#nosec G115
34+ } else {
35+ buf.pool = unpooled
36+ }
37+}
+23
internal/bufpool/class.go
···00000000000000000000000
···1+package bufpool
2+3+var sizeClasses = [...]int{
4+ DefaultBufferCap,
5+ 64 << 10,
6+ 128 << 10,
7+ 256 << 10,
8+ 512 << 10,
9+ 1 << 20,
10+ 2 << 20,
11+ 4 << 20,
12+ maxPooledBuffer,
13+}
14+15+func classFor(size int) (idx, classCap int, ok bool) {
16+ for i, class := range sizeClasses {
17+ if size <= class {
18+ return i, class, true
19+ }
20+ }
21+22+ return -1, size, false
23+}
+12
internal/bufpool/consts.go
···000000000000
···1+package bufpool
2+3+const (
4+ // DefaultBufferCap is the minimum capacity a borrowed buffer will have.
5+ // Borrow() will allocate or retrieve a buffer with at least this capacity.
6+ DefaultBufferCap = 32 * 1024
7+8+ // maxPooledBuffer defines the maximum capacity of a buffer that may be
9+ // returned to the pool. Buffers larger than this will not be pooled to
10+ // avoid unbounded memory usage.
11+ maxPooledBuffer = 8 << 20
12+)
+3
internal/bufpool/doc.go
···000
···1+// Package bufpool provides a lightweight byte-buffer type with optional
2+// pooling.
3+package bufpool
+8
internal/bufpool/from_owned.go
···00000000
···1+package bufpool
2+3+// FromOwned constructs a Buffer from a caller-owned byte slice. The resulting
4+// Buffer does not participate in pooling and will never be returned to the
5+// internal pool when released.
6+func FromOwned(buf []byte) Buffer {
7+ return Buffer{buf: buf, pool: unpooled}
8+}
···1+package bufpool
2+3+// Release returns the buffer to the global pool if it originated from the
4+// pool and its capacity is no larger than maxPooledBuffer. After release, the
5+// Buffer becomes invalid and should not be used further.
6+//
7+// Releasing a non-pooled buffer has no effect beyond clearing its internal
8+// storage.
9+func (buf *Buffer) Release() {
10+ if buf.buf == nil {
11+ return
12+ }
13+14+ buf.returnToPool()
15+ buf.buf = nil
16+ buf.pool = unpooled
17+}
+15
internal/bufpool/resize.go
···000000000000000
···1+package bufpool
2+3+// Resize adjusts the length of the buffer to n bytes. If n exceeds the current
4+// capacity, the underlying storage is grown. If n is negative, it is treated
5+// as zero.
6+//
7+// The buffer's new contents beyond the previous length are undefined.
8+func (buf *Buffer) Resize(n int) {
9+ if n < 0 {
10+ n = 0
11+ }
12+13+ buf.ensureCapacity(n)
14+ buf.buf = buf.buf[:n]
15+}