github.com/metacubex/mihomo@v1.18.5/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 [11]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  	return &Allocator{
    23  		buffers: [...]sync.Pool{ // 64B -> 64K
    24  			{New: func() any { return new([1 << 6]byte) }},
    25  			{New: func() any { return new([1 << 7]byte) }},
    26  			{New: func() any { return new([1 << 8]byte) }},
    27  			{New: func() any { return new([1 << 9]byte) }},
    28  			{New: func() any { return new([1 << 10]byte) }},
    29  			{New: func() any { return new([1 << 11]byte) }},
    30  			{New: func() any { return new([1 << 12]byte) }},
    31  			{New: func() any { return new([1 << 13]byte) }},
    32  			{New: func() any { return new([1 << 14]byte) }},
    33  			{New: func() any { return new([1 << 15]byte) }},
    34  			{New: func() any { return new([1 << 16]byte) }},
    35  		},
    36  	}
    37  }
    38  
    39  // Get a []byte from pool with most appropriate cap
    40  func (alloc *Allocator) Get(size int) []byte {
    41  	switch {
    42  	case size < 0:
    43  		panic("alloc.Get: len out of range")
    44  	case size == 0:
    45  		return nil
    46  	case size > 65536:
    47  		return make([]byte, size)
    48  	default:
    49  		var index uint16
    50  		if size > 64 {
    51  			index = msb(size)
    52  			if size != 1<<index {
    53  				index += 1
    54  			}
    55  			index -= 6
    56  		}
    57  
    58  		buffer := alloc.buffers[index].Get()
    59  		switch index {
    60  		case 0:
    61  			return buffer.(*[1 << 6]byte)[:size]
    62  		case 1:
    63  			return buffer.(*[1 << 7]byte)[:size]
    64  		case 2:
    65  			return buffer.(*[1 << 8]byte)[:size]
    66  		case 3:
    67  			return buffer.(*[1 << 9]byte)[:size]
    68  		case 4:
    69  			return buffer.(*[1 << 10]byte)[:size]
    70  		case 5:
    71  			return buffer.(*[1 << 11]byte)[:size]
    72  		case 6:
    73  			return buffer.(*[1 << 12]byte)[:size]
    74  		case 7:
    75  			return buffer.(*[1 << 13]byte)[:size]
    76  		case 8:
    77  			return buffer.(*[1 << 14]byte)[:size]
    78  		case 9:
    79  			return buffer.(*[1 << 15]byte)[:size]
    80  		case 10:
    81  			return buffer.(*[1 << 16]byte)[:size]
    82  		default:
    83  			panic("invalid pool index")
    84  		}
    85  	}
    86  }
    87  
    88  // Put returns a []byte to pool for future use,
    89  // which the cap must be exactly 2^n
    90  func (alloc *Allocator) Put(buf []byte) error {
    91  	if cap(buf) == 0 || cap(buf) > 65536 {
    92  		return nil
    93  	}
    94  
    95  	bits := msb(cap(buf))
    96  	if cap(buf) != 1<<bits {
    97  		return errors.New("allocator Put() incorrect buffer size")
    98  	}
    99  	if cap(buf) < 1<<6 {
   100  		return nil
   101  	}
   102  	bits -= 6
   103  	buf = buf[:cap(buf)]
   104  
   105  	//nolint
   106  	//lint:ignore SA6002 ignore temporarily
   107  	switch bits {
   108  	case 0:
   109  		alloc.buffers[bits].Put((*[1 << 6]byte)(buf))
   110  	case 1:
   111  		alloc.buffers[bits].Put((*[1 << 7]byte)(buf))
   112  	case 2:
   113  		alloc.buffers[bits].Put((*[1 << 8]byte)(buf))
   114  	case 3:
   115  		alloc.buffers[bits].Put((*[1 << 9]byte)(buf))
   116  	case 4:
   117  		alloc.buffers[bits].Put((*[1 << 10]byte)(buf))
   118  	case 5:
   119  		alloc.buffers[bits].Put((*[1 << 11]byte)(buf))
   120  	case 6:
   121  		alloc.buffers[bits].Put((*[1 << 12]byte)(buf))
   122  	case 7:
   123  		alloc.buffers[bits].Put((*[1 << 13]byte)(buf))
   124  	case 8:
   125  		alloc.buffers[bits].Put((*[1 << 14]byte)(buf))
   126  	case 9:
   127  		alloc.buffers[bits].Put((*[1 << 15]byte)(buf))
   128  	case 10:
   129  		alloc.buffers[bits].Put((*[1 << 16]byte)(buf))
   130  	default:
   131  		panic("invalid pool index")
   132  	}
   133  	return nil
   134  }
   135  
   136  // msb return the pos of most significant bit
   137  func msb(size int) uint16 {
   138  	return uint16(bits.Len32(uint32(size)) - 1)
   139  }