//go:build !disable_rm package util import ( "container/list" "fmt" "io" "slices" "sync" "unsafe" ) type RecyclableMemory struct { allocator *ScalableMemoryAllocator Memory recycleIndexes []int } func (r *RecyclableMemory) InitRecycleIndexes(max int) { r.recycleIndexes = make([]int, 0, max) } func (r *RecyclableMemory) GetAllocator() *ScalableMemoryAllocator { return r.allocator } func (r *RecyclableMemory) NextN(size int) (memory []byte) { memory = r.allocator.Malloc(size) if r.recycleIndexes != nil { r.recycleIndexes = append(r.recycleIndexes, r.Count()) } r.AppendOne(memory) return } func (r *RecyclableMemory) AddRecycleBytes(b []byte) { if r.recycleIndexes != nil { r.recycleIndexes = append(r.recycleIndexes, r.Count()) } r.AppendOne(b) } func (r *RecyclableMemory) SetAllocator(allocator *ScalableMemoryAllocator) { r.allocator = allocator } func (r *RecyclableMemory) Recycle() { if r.recycleIndexes != nil { for _, index := range r.recycleIndexes { r.allocator.Free(r.Buffers[index]) } r.recycleIndexes = r.recycleIndexes[:0] } else { for _, buf := range r.Buffers { r.allocator.Free(buf) } } } var ( memoryPool [BuddySize]byte buddy = NewBuddy(BuddySize >> MinPowerOf2) lock sync.Mutex poolStart = int64(uintptr(unsafe.Pointer(&memoryPool[0]))) blockPool = list.New() //EnableCheckSize bool = false ) type MemoryAllocator struct { allocator *Allocator start int64 memory []byte Size int } func GetMemoryAllocator(size int) (ret *MemoryAllocator) { lock.Lock() offset, err := buddy.Alloc(size >> MinPowerOf2) if blockPool.Len() > 0 { ret = blockPool.Remove(blockPool.Front()).(*MemoryAllocator) } else { ret = &MemoryAllocator{ allocator: NewAllocator(size), } } lock.Unlock() ret.Size = size ret.allocator.Init(size) if err != nil { ret.memory = make([]byte, size) ret.start = int64(uintptr(unsafe.Pointer(&ret.memory[0]))) return } offset = offset << MinPowerOf2 ret.memory = memoryPool[offset : offset+size] ret.start = poolStart + int64(offset) return } func (ma *MemoryAllocator) Recycle() { ma.allocator.Recycle() lock.Lock() blockPool.PushBack(ma) _ = buddy.Free(int((poolStart - ma.start) >> MinPowerOf2)) ma.memory = nil lock.Unlock() } func (ma *MemoryAllocator) Find(size int) (memory []byte) { if offset := ma.allocator.Find(size); offset != -1 { memory = ma.memory[offset : offset+size] } return } func (ma *MemoryAllocator) Malloc(size int) (memory []byte) { if offset := ma.allocator.Allocate(size); offset != -1 { memory = ma.memory[offset : offset+size] } return } func (ma *MemoryAllocator) free(start, size int) (ret bool) { if start < 0 || start+size > ma.Size { return } ma.allocator.Free(start, size) return true } //func (ma *MemoryAllocator) Free(mem []byte) bool { // start := int(int64(uintptr(unsafe.Pointer(&mem[0]))) - ma.start) // return ma.free(start, len(mem)) //} func (ma *MemoryAllocator) GetBlocks() (blocks []*Block) { return ma.allocator.GetBlocks() } type ScalableMemoryAllocator struct { children []*MemoryAllocator totalMalloc int64 totalFree int64 size int childSize int } func NewScalableMemoryAllocator(size int) (ret *ScalableMemoryAllocator) { return &ScalableMemoryAllocator{children: []*MemoryAllocator{GetMemoryAllocator(size)}, size: size, childSize: size} } func (sma *ScalableMemoryAllocator) checkSize() { var totalFree int for _, child := range sma.children { totalFree += child.allocator.GetFreeSize() } if inUse := sma.totalMalloc - sma.totalFree; totalFree != sma.size-int(inUse) { panic("CheckSize") } else { if inUse > 3000000 { fmt.Println(uintptr(unsafe.Pointer(sma)), inUse) } } } func (sma *ScalableMemoryAllocator) addMallocCount(size int) { sma.totalMalloc += int64(size) } func (sma *ScalableMemoryAllocator) addFreeCount(size int) { sma.totalFree += int64(size) } func (sma *ScalableMemoryAllocator) GetTotalMalloc() int64 { return sma.totalMalloc } func (sma *ScalableMemoryAllocator) GetTotalFree() int64 { return sma.totalFree } func (sma *ScalableMemoryAllocator) GetChildren() []*MemoryAllocator { return sma.children } func (sma *ScalableMemoryAllocator) Recycle() { for _, child := range sma.children { child.Recycle() } sma.children = nil } // Borrow = Malloc + Free = Find, must use the memory at once func (sma *ScalableMemoryAllocator) Borrow(size int) (memory []byte) { if sma == nil || size > MaxBlockSize { return } defer sma.addMallocCount(size) var child *MemoryAllocator for _, child = range sma.children { if memory = child.Find(size); memory != nil { return } } for sma.childSize < MaxBlockSize { sma.childSize = sma.childSize << 1 if sma.childSize >= size { break } } child = GetMemoryAllocator(sma.childSize) sma.size += child.Size memory = child.Find(size) sma.children = append(sma.children, child) return } func (sma *ScalableMemoryAllocator) Malloc(size int) (memory []byte) { if sma == nil || size > MaxBlockSize { return make([]byte, size) } //if EnableCheckSize { // defer sma.checkSize() //} defer sma.addMallocCount(size) var child *MemoryAllocator for _, child = range sma.children { if memory = child.Malloc(size); memory != nil { return } } for sma.childSize < MaxBlockSize { sma.childSize = sma.childSize << 1 if sma.childSize >= size { break } } child = GetMemoryAllocator(sma.childSize) sma.size += child.Size memory = child.Malloc(size) sma.children = append(sma.children, child) return } func (sma *ScalableMemoryAllocator) GetAllocator() *ScalableMemoryAllocator { return sma } func (sma *ScalableMemoryAllocator) Read(reader io.Reader, n int) (mem []byte, err error) { mem = sma.Malloc(n) meml := n if n, err = reader.Read(mem); err == nil { if n < meml { sma.Free(mem[n:]) mem = mem[:n] } } else { sma.Free(mem) } return } func (sma *ScalableMemoryAllocator) FreeRest(mem *[]byte, keep int) { if m := *mem; keep < len(m) { sma.Free(m[keep:]) *mem = m[:keep] } } func (sma *ScalableMemoryAllocator) Free(mem []byte) bool { if sma == nil { return false } //if EnableCheckSize { // defer sma.checkSize() //} ptr := int64(uintptr(unsafe.Pointer(&mem[0]))) size := len(mem) for i, child := range sma.children { if start := int(ptr - child.start); start >= 0 && start < child.Size && child.free(start, size) { sma.addFreeCount(size) if len(sma.children) > 1 && child.allocator.sizeTree.End-child.allocator.sizeTree.Start == child.Size { child.Recycle() sma.children = slices.Delete(sma.children, i, i+1) sma.size -= child.Size } return true } } return false } //func (r *RecyclableMemory) RemoveRecycleBytes(index int) (buf []byte) { // if index < 0 { // index = r.Count() + index // } // buf = r.Buffers[index] // if r.recycleIndexes != nil { // i := slices.Index(r.recycleIndexes, index) // r.recycleIndexes = slices.Delete(r.recycleIndexes, i, i+1) // } // r.Buffers = slices.Delete(r.Buffers, index, index+1) // r.Size -= len(buf) // return //}