github.com/igoogolx/clash@v1.19.8/common/pool/alloc.go (about) 1 package pool 2 3 // Inspired by https://github.com/xtaci/smux/blob/master/alloc.go 4 5 import ( 6 "errors" 7 "math/bits" 8 "sync" 9 ) 10 11 var defaultAllocator = NewAllocator() 12 13 // Allocator for incoming frames, optimized to prevent overwriting after zeroing 14 type Allocator struct { 15 buffers []sync.Pool 16 } 17 18 // NewAllocator initiates a []byte allocator for frames less than 65536 bytes, 19 // the waste(memory fragmentation) of space allocation is guaranteed to be 20 // no more than 50%. 21 func NewAllocator() *Allocator { 22 alloc := new(Allocator) 23 alloc.buffers = make([]sync.Pool, 17) // 1B -> 64K 24 for k := range alloc.buffers { 25 i := k 26 alloc.buffers[k].New = func() any { 27 return make([]byte, 1<<uint32(i)) 28 } 29 } 30 return alloc 31 } 32 33 // Get a []byte from pool with most appropriate cap 34 func (alloc *Allocator) Get(size int) []byte { 35 switch { 36 case size < 0: 37 panic("alloc.Get: len out of range") 38 case size == 0: 39 return nil 40 case size > 65536: 41 return make([]byte, size) 42 default: 43 bits := msb(size) 44 if size == 1<<bits { 45 return alloc.buffers[bits].Get().([]byte)[:size] 46 } 47 48 return alloc.buffers[bits+1].Get().([]byte)[:size] 49 } 50 } 51 52 // Put returns a []byte to pool for future use, 53 // which the cap must be exactly 2^n 54 func (alloc *Allocator) Put(buf []byte) error { 55 if cap(buf) == 0 || cap(buf) > 65536 { 56 return nil 57 } 58 59 bits := msb(cap(buf)) 60 if cap(buf) != 1<<bits { 61 return errors.New("allocator Put() incorrect buffer size") 62 } 63 64 //nolint 65 //lint:ignore SA6002 ignore temporarily 66 alloc.buffers[bits].Put(buf) 67 return nil 68 } 69 70 // msb return the pos of most significant bit 71 func msb(size int) uint16 { 72 return uint16(bits.Len32(uint32(size)) - 1) 73 }