@@ -20,6 +20,7 @@ package mem
2020
2121import (
2222 "math/bits"
23+ "slices"
2324 "sort"
2425 "sync"
2526
@@ -112,11 +113,18 @@ func (p *tieredBufferPool) getPool(size int) BufferPool {
112113}
113114
114115type binaryTieredBufferPool struct {
115- indexOfNextLargestBit []int
116- indexOfPreviousLargestBit []int
117- sizedPools []* sizedBufferPool
118- fallbackPool simpleBufferPool
119- maxPoolCap int // Optimization: Cache max capacity
116+ // exponentToNextLargestPoolMap maps a power-of-two exponent (e.g., 12 for
117+ // 4KB) to the index of the next largest sizedBufferPool. This is used by
118+ // Get() to find the smallest pool that can satisfy a request for a given
119+ // size.
120+ exponentToNextLargestPoolMap []int
121+ // exponentToPreviousLargestPoolMap maps a power-of-two exponent to the
122+ // index of the previous largest sizedBufferPool. This is used by Put()
123+ // to return a buffer to the most appropriate pool based on its capacity.
124+ exponentToPreviousLargestPoolMap []int
125+ sizedPools []* sizedBufferPool
126+ fallbackPool simpleBufferPool
127+ maxPoolCap int // Optimization: Cache max capacity
120128}
121129
122130// NewBinaryTieredBufferPool returns a BufferPool implementation that uses
@@ -133,19 +141,19 @@ func NewBinaryTieredBufferPool(powerOfTwoExponents ...int) BufferPool {
133141 // Determine the maximum exponent we need to support.
134142 // bits.Len64(math.MaxUint64) is 63.
135143 const maxExponent = 63
136- indexOfNextLargestBit := make ([]int , maxExponent + 1 )
137- indexOfPreviousLargestBit := make ([]int , maxExponent + 1 )
138-
139- // Initialize with sentinel values
140- for i := range indexOfNextLargestBit {
141- indexOfNextLargestBit [i ] = - 1
142- indexOfPreviousLargestBit [i ] = - 1
143- }
144+ indexOfNextLargestBit := slices .Repeat ([]int {- 1 }, maxExponent + 1 )
145+ indexOfPreviousLargestBit := slices .Repeat ([]int {- 1 }, maxExponent + 1 )
144146
145147 maxCap := 0
146148 pools := make ([]* sizedBufferPool , 0 , len (powerOfTwoExponents ))
147149
148150 for i , exp := range powerOfTwoExponents {
151+ // Allocating slices of size > 2^maxExponent isn't possible on 64-bit
152+ // machines.
153+ //
154+ // Negative exponents would result in values in the range (0, 1). Since
155+ // buffer sizes are integers, such values don't make sense (and would
156+ // panic on bit shift). We ignore such values.
149157 if exp > maxExponent || exp < 0 {
150158 continue
151159 }
@@ -177,10 +185,10 @@ func NewBinaryTieredBufferPool(powerOfTwoExponents ...int) BufferPool {
177185 }
178186
179187 return & binaryTieredBufferPool {
180- indexOfNextLargestBit : indexOfNextLargestBit ,
181- indexOfPreviousLargestBit : indexOfPreviousLargestBit ,
182- sizedPools : pools ,
183- maxPoolCap : maxCap ,
188+ exponentToNextLargestPoolMap : indexOfNextLargestBit ,
189+ exponentToPreviousLargestPoolMap : indexOfPreviousLargestBit ,
190+ sizedPools : pools ,
191+ maxPoolCap : maxCap ,
184192 }
185193}
186194
@@ -200,7 +208,7 @@ func (b *binaryTieredBufferPool) poolForGet(size int) BufferPool {
200208 // size=16 (0b10000) -> size-1=15 (0b01111) -> bits.Len=4 -> Pool for 2^4
201209 // size=17 (0b10001) -> size-1=16 (0b10000) -> bits.Len=5 -> Pool for 2^5
202210 querySize := uint (size - 1 )
203- poolIdx := b .indexOfNextLargestBit [bits .Len (querySize )]
211+ poolIdx := b .exponentToNextLargestPoolMap [bits .Len (querySize )]
204212
205213 return b .sizedPools [poolIdx ]
206214}
@@ -228,7 +236,7 @@ func (b *binaryTieredBufferPool) poolForPut(bCap int) BufferPool {
228236 // cap=16 (0b10000) -> Len=5 -> 5-1=4 -> 2^4
229237 // cap=15 (0b01111) -> Len=4 -> 4-1=3 -> 2^3
230238 largestPowerOfTwo := bits .Len (uint (bCap )) - 1
231- poolIdx := b .indexOfPreviousLargestBit [largestPowerOfTwo ]
239+ poolIdx := b .exponentToPreviousLargestPoolMap [largestPowerOfTwo ]
232240 // The buffer is smaller than the smallest power of 2, discard it.
233241 if poolIdx == - 1 {
234242 // Buffer is smaller than our smallest pool bucket.
0 commit comments