1919package mem
2020
2121import (
22+ "fmt"
2223 "math/bits"
2324 "slices"
2425 "sort"
@@ -51,10 +52,17 @@ var defaultBufferPoolSizeExponents = []uint8{
5152 20 , // 1MB
5253}
5354
54- var defaultBufferPool BufferPool
55+ var (
56+ defaultBufferPool BufferPool
57+ uintSize = bits .UintSize // use a variable for mocking during tests.
58+ )
5559
5660func init () {
57- defaultBufferPool = NewBinaryTieredBufferPool (defaultBufferPoolSizeExponents ... )
61+ var err error
62+ defaultBufferPool , err = NewBinaryTieredBufferPool (defaultBufferPoolSizeExponents ... )
63+ if err != nil {
64+ panic (fmt .Sprintf ("Failed to create default buffer pool: %v" , err ))
65+ }
5866
5967 internal .SetDefaultBufferPoolForTesting = func (pool BufferPool ) {
6068 defaultBufferPool = pool
@@ -127,37 +135,33 @@ type binaryTieredBufferPool struct {
127135 maxPoolCap int // Optimization: Cache max capacity
128136}
129137
130- // NewBinaryTieredBufferPool returns a BufferPool implementation that uses
131- // multiple underlying pools of the given pool sizes. The pool sizes must be
132- // powers of 2. This enables O(1) lookup when getting or putting a buffer.
138+ // NewBinaryTieredBufferPool returns a BufferPool backed by multiple sub-pools.
139+ // This structure enables O(1) lookup time for Get and Put operations.
133140//
134- // Note that the argument passed to this functions are the powers of 2 of the
135- // capacity of the buffers in the pool, not the capacities of the buffers
136- // themselves. For example, if you wanted a pool that had buffers with a capacity
137- // of 16kb, you would pass 14 as the argument to this function.
138- func NewBinaryTieredBufferPool (powerOfTwoExponents ... uint8 ) BufferPool {
141+ // The arguments provided are the exponents for the buffer capacities (powers
142+ // of 2), not the raw byte sizes. For example, to create a pool of 16KB buffers
143+ // (2^14 bytes), pass 14 as the argument.
144+ func NewBinaryTieredBufferPool (powerOfTwoExponents ... uint8 ) (BufferPool , error ) {
139145 slices .Sort (powerOfTwoExponents )
140146
141- // Determine the maximum exponent we need to support.
142- // bits.Len64(math.MaxUint64) is 63 .
143- const maxExponent = 63
147+ // Determine the maximum exponent we need to support. This depends on the
148+ // word size (32-bit vs 64-bit) .
149+ maxExponent := uintSize - 1
144150 indexOfNextLargestBit := slices .Repeat ([]int {- 1 }, maxExponent + 1 )
145151 indexOfPreviousLargestBit := slices .Repeat ([]int {- 1 }, maxExponent + 1 )
146152
147- maxCap := 0
153+ maxTier := 0
148154 pools := make ([]* sizedBufferPool , 0 , len (powerOfTwoExponents ))
149155
150156 for i , exp := range powerOfTwoExponents {
151- // Allocating slices of size > 2^maxExponent isn't possible on 64-bit
152- // machines.
153- if exp > maxExponent {
154- continue
155- }
156- capSize := 1 << exp
157- pools = append (pools , newSizedBufferPool (capSize ))
158- if capSize > maxCap {
159- maxCap = capSize
157+ // Allocating slices of size > 2^maxExponent isn't possible on
158+ // maxExponent-bit machines.
159+ if int (exp ) > maxExponent {
160+ return nil , fmt .Errorf ("allocating slice of size 2^%d is not possible" , exp )
160161 }
162+ tierSize := 1 << exp
163+ pools = append (pools , newSizedBufferPool (tierSize ))
164+ maxTier = max (maxTier , tierSize )
161165
162166 // Map the exact power of 2 to this pool index.
163167 indexOfNextLargestBit [exp ] = i
@@ -184,8 +188,8 @@ func NewBinaryTieredBufferPool(powerOfTwoExponents ...uint8) BufferPool {
184188 exponentToNextLargestPoolMap : indexOfNextLargestBit ,
185189 exponentToPreviousLargestPoolMap : indexOfPreviousLargestBit ,
186190 sizedPools : pools ,
187- maxPoolCap : maxCap ,
188- }
191+ maxPoolCap : maxTier ,
192+ }, nil
189193}
190194
191195func (b * binaryTieredBufferPool ) Get (size int ) * []byte {
0 commit comments