github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/perf/bbp/sized.go (about) 1 package bbp 2 3 import ( 4 "math/bits" 5 "sync" 6 "unsafe" 7 8 "github.com/jxskiss/gopkg/v2/internal/unsafeheader" 9 ) 10 11 const ( 12 minShift = 6 // at least 64B 13 maxShift = 25 // max 32MB 14 15 // Min and max buffer size provided in this package. 16 minBufSize = 1 << minShift 17 maxBufSize = 1 << maxShift 18 19 minPoolIdx = 6 20 maxPoolIdx = maxShift 21 poolSize = maxPoolIdx + 1 22 ) 23 24 // Get returns a byte slice from the pool with specified length and capacity. 25 // When you finish the work with the buffer, you may call Put to put it back 26 // to the pool for reusing. 27 func Get(length, capacity int) []byte { 28 if capacity > maxBufSize { 29 return make([]byte, length, capacity) 30 } 31 32 // Manually inlining. 33 // return get(length, capacity) 34 idx := indexGet(capacity) 35 return sizedPools[idx].Get(length) 36 } 37 38 // Put puts back a byte slice to the pool for reusing. 39 // 40 // The byte slice mustn't be touched after returning it to the pool, 41 // otherwise data races will occur. 42 func Put(buf []byte) { put(buf) } 43 44 // Grow checks capacity of buf, it returns a new byte buffer from the pool, 45 // if necessary, to guarantee space for another n bytes. 46 // After Grow(n), at least n bytes can be appended to the returned buffer 47 // without another allocation. 48 // If n is negative, Grow will panic. 49 // 50 // Note that if reuseBuf is true and a new slice is returned, the old 51 // buf will be put back to the pool, the caller must not retain reference 52 // to the old buf and must not access it again, else data race happens. 53 func Grow(buf []byte, n int, reuseBuf bool) []byte { 54 if n < 0 { 55 panic("bbp.Grow: negative size to grow") 56 } 57 if cap(buf) >= len(buf)+n { 58 return buf 59 } 60 return grow(buf, len(buf)+n, reuseBuf) 61 } 62 63 // -------- sized pools -------- // 64 65 var ( 66 sizedPools [poolSize]*bufPool 67 ) 68 69 func init() { 70 for i := 0; i < poolSize; i++ { 71 size := 1 << i 72 sizedPools[i] = &bufPool{size: size} 73 } 74 } 75 76 type bufPool struct { 77 size int 78 pool sync.Pool 79 } 80 81 func (p *bufPool) Get(length int) []byte { 82 if ptr := p.pool.Get(); ptr != nil { 83 return _toBuf(ptr.(unsafe.Pointer), length) 84 } 85 return make([]byte, length, p.size) 86 } 87 88 func (p *bufPool) Put(buf []byte) { 89 if cap(buf) >= p.size { 90 p.pool.Put(_toPtr(buf)) 91 } 92 } 93 94 func _toBuf(ptr unsafe.Pointer, length int) []byte { 95 size := *(*int)(ptr) 96 return *(*[]byte)(unsafe.Pointer(&unsafeheader.SliceHeader{ 97 Data: ptr, 98 Len: length, 99 Cap: size, 100 })) 101 } 102 103 func _toPtr(buf []byte) unsafe.Pointer { 104 h := *(*unsafeheader.SliceHeader)(unsafe.Pointer(&buf)) 105 *(*int)(h.Data) = h.Cap 106 return h.Data 107 } 108 109 // callers must guarantee that capacity is not greater than maxBufSize. 110 func get(length, capacity int) []byte { 111 idx := indexGet(capacity) 112 return sizedPools[idx].Get(length) 113 } 114 115 func put(buf []byte) { 116 c := cap(buf) 117 if c >= minBufSize && c <= maxBufSize { 118 idx := indexPut(c) 119 ptr := _toPtr(buf) 120 sizedPools[idx].pool.Put(ptr) 121 } 122 } 123 124 func grow(buf []byte, capacity int, reuseBuf bool) []byte { 125 var newBuf []byte 126 if capacity > maxBufSize { 127 newBuf = make([]byte, len(buf), capacity) 128 } else { 129 // Manually inlining. 130 // newBuf = get(len(buf), capacity) 131 idx := indexGet(capacity) 132 newBuf = sizedPools[idx].Get(len(buf)) 133 } 134 copy(newBuf, buf) 135 if reuseBuf { 136 put(buf) 137 } 138 return newBuf 139 } 140 141 // indexGet finds the pool index for the given size to get buffer from, 142 // if size not equals to a predefined size, it returns the index of the 143 // next predefined size. 144 func indexGet(size int) int { 145 if size <= minBufSize { 146 return minPoolIdx 147 } 148 149 // Manually inline bsr and isPowerOfTwo here. 150 idx := bits.Len32(uint32(size)) 151 if size&(size-1) == 0 { 152 idx -= 1 //nolint:revive 153 } 154 return idx 155 } 156 157 // indexPut finds the pool index for the given size to put buffer back, 158 // if size not equals to a predefined size, it returns the index of the 159 // previous predefined size. 160 func indexPut(size int) int { 161 // Manually inline bsr. 162 return bits.Len32(uint32(size)) - 1 163 } 164 165 // bsr. 166 // 167 // Callers within this package guarantee that n doesn't overflow int32. 168 // 169 //nolint:unused 170 func bsr(n int) int { 171 return bits.Len32(uint32(n)) - 1 172 } 173 174 // isPowerOfTwo reports whether n is a power of two. 175 // 176 //nolint:unused 177 func isPowerOfTwo(n int) bool { 178 return n&(n-1) == 0 179 }