bufferpool.go 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. package util
  2. import (
  3. "math"
  4. "math/bits"
  5. "sync"
  6. )
  7. // bufferPool holds "tiered" []byte `sync.Pool` instances by capacity up to math.MaxUint16
  8. type bufferPool struct {
  9. pools [17]sync.Pool
  10. }
  11. func newBufferPool() *bufferPool {
  12. bp := new(bufferPool)
  13. for i := 0; i < 17; i++ {
  14. length := 1 << i
  15. bp.pools[i].New = func() any {
  16. return make([]byte, length)
  17. }
  18. }
  19. return bp
  20. }
  21. // poolIndex returns the pool index for a buffer of the given size.
  22. func poolIndex(length int) int {
  23. return bits.Len32(uint32(length - 1))
  24. }
  25. // putIndex returns the pool index for returning a buffer with the given capacity.
  26. // It is the inverse of poolIndex: given a capacity that was originally handed out
  27. // by Get, it finds the pool that owns it.
  28. //
  29. // Because Get always returns buffers with capacity 1<<i, the capacity here will
  30. // always be a power of two. bits.Len32(1<<i) = i+1, so we subtract 1 to recover i.
  31. func putIndex(capacity int) int {
  32. return bits.Len32(uint32(capacity)) - 1
  33. }
  34. func isPowerOfTwo(capacity int) bool {
  35. return capacity&(capacity-1) == 0
  36. }
  37. func (bp *bufferPool) Get(length int) []byte {
  38. if length <= 0 {
  39. return nil
  40. }
  41. // Beyond our pool range: allocate directly
  42. if length > math.MaxUint16 {
  43. return make([]byte, length)
  44. }
  45. i := poolIndex(length)
  46. buf := bp.pools[i].Get().([]byte)
  47. return buf[:length]
  48. }
  49. func (bp *bufferPool) Put(buf []byte) {
  50. capacity := cap(buf)
  51. if capacity == 0 || capacity > math.MaxUint16 || !isPowerOfTwo(capacity) {
  52. return
  53. }
  54. i := putIndex(capacity)
  55. bp.pools[i].Put(buf[:cap(buf)])
  56. }