//go:build !twotree package util type ( Block struct { Start, End int parent, left, right *Block } History struct { Malloc bool Offset int Size int } Allocator struct { pool *Block sizeTree *Block // Single treap instead of sizeTree/offsetTree Size int // history []History } ) // Update NewAllocator func NewAllocator(size int) (result *Allocator) { result = &Allocator{ sizeTree: &Block{Start: 0, End: size}, Size: size, } return } func (p *Block) rotateDone(x, y *Block, a *Allocator) { x.parent = p if p == nil { a.sizeTree = x } else if p.left == y { p.left = x } else { p.right = x } } // Add rotation helpers similar to semaRoot func (x *Block) rotateLeft(a *Allocator) { p, y, b := x.parent, x.right, x.right.left y.left, x.parent, x.right = x, y, b if b != nil { b.parent = x } p.rotateDone(y, x, a) } func (y *Block) rotateRight(a *Allocator) { p, x, b := y.parent, y.left, y.left.right x.right, y.parent, y.left = y, x, b if b != nil { b.parent = y } p.rotateDone(x, y, a) } func (b *Block) insert(block *Block, allocator *Allocator) *Block { if b == nil { return block } if block.End == block.Start { panic("empty block") } // Insert as BST using Start value if block.Start < b.Start { b.left = b.left.insert(block, allocator) if b.left != nil { b.left.parent = b } } else { b.right = b.right.insert(block, allocator) if b.right != nil { b.right.parent = b } } // Heapify based on block size (End-Start) blockSize := block.End - block.Start nodeSize := b.End - b.Start if blockSize < nodeSize { // Need to rotate up if current node has smaller size if block == b.left { b.rotateRight(allocator) return block } else if block == b.right { b.rotateLeft(allocator) return block } } return b } func (b *Block) find(size int) (block *Block) { if b == nil { return nil } // First check if current block can be used if blockSize := b.End - b.Start; blockSize >= size { // If exact match, return this block if blockSize == size { return b } // Keep searching left for potentially better fit if left := b.left.find(size); left != nil { return left } // If no better fit found, use this block return b } // If current block too small, only check right side return b.right.find(size) } func (b *Block) Walk(fn func(*Block)) { if b == nil { return } b.left.Walk(fn) fn(b) b.right.Walk(fn) } func (a *Allocator) putBlock(block *Block) { block.right = nil block.left = nil block.parent = a.pool a.pool = block } func (a *Allocator) Allocate(size int) (offset int) { // a.history = append(a.history, History{Malloc: true, Size: size}) block := a.sizeTree.find(size) if block == nil { return -1 } offset = block.Start a.deleteBlock(block) if blockSize := block.End - block.Start; blockSize == size { // Remove entire block a.putBlock(block) } else { block.Start += size a.insert(block) } return offset } func (a *Allocator) deleteBlock(block *Block) { // Rotate block down to leaf for block.left != nil || block.right != nil { if block.right == nil || (block.left != nil && (block.left.End-block.left.Start) > (block.right.End-block.right.Start)) { block.rotateRight(a) } else { block.rotateLeft(a) } } // Remove leaf if p := block.parent; p != nil { if p.left == block { p.left = nil } else { p.right = nil } } else { a.sizeTree = nil } } func (a *Allocator) insert(block *Block) { // a.sizeTree.Walk(func(b *Block) { // if block.Start >= b.Start && block.Start < b.End { // out, _ := yaml.Marshal(a.history) // fmt.Println(string(out)) // } // }) a.sizeTree = a.sizeTree.insert(block, a) // if a.sizeTree.parent != nil { // panic("sizeTree parent is not nil") // } } func (a *Allocator) Free(offset, size int) { // a.history = append(a.history, History{Malloc: false, Offset: offset, Size: size}) // Try to merge with adjacent blocks // Find adjacent blocks switch left, right := a.findLeftAdjacent(offset), a.findRightAdjacent(offset+size); true { case left != nil && right != nil: a.deleteBlock(right) a.deleteBlock(left) left.End = right.End a.insert(left) a.putBlock(right) case left == nil && right == nil: block := a.getBlock(offset, offset+size) a.insert(block) case left != nil: a.deleteBlock(left) left.End = offset + size a.insert(left) case right != nil: a.deleteBlock(right) right.Start = offset a.insert(right) } } func (a *Allocator) findLeftAdjacent(offset int) (curr *Block) { curr = a.sizeTree for curr != nil { if curr.End == offset { return } if curr.End < offset { curr = curr.right } else { curr = curr.left } } return } func (a *Allocator) findRightAdjacent(offset int) (curr *Block) { curr = a.sizeTree for curr != nil { if curr.Start == offset { return } if curr.Start > offset { curr = curr.left } else { curr = curr.right } } return } func (a *Allocator) getBlock(start, end int) *Block { if a.pool == nil { return &Block{Start: start, End: end} } else { block := a.pool a.pool = block.parent block.parent = nil block.Start, block.End = start, end return block } } func (a *Allocator) GetFreeSize() (size int) { a.sizeTree.Walk(func(b *Block) { size += b.End - b.Start }) return } func (a *Allocator) Recycle() { a.sizeTree.Walk(a.putBlock) a.sizeTree = nil a.pool = nil } func (a *Allocator) Init(size int) { a.sizeTree = a.getBlock(0, size) a.Size = size } func (a *Allocator) Find(size int) (offset int) { block := a.sizeTree.find(size) if block == nil { return -1 } return block.Start } func (a *Allocator) GetBlocks() (blocks []*Block) { a.sizeTree.Walk(func(b *Block) { blocks = append(blocks, b) }) return }