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