mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-10-28 21:11:51 +08:00
304 lines
7.2 KiB
Go
304 lines
7.2 KiB
Go
package util
|
|
|
|
type (
|
|
Tree struct {
|
|
Left, Right *Block
|
|
Height int
|
|
}
|
|
Block struct {
|
|
Start, End int
|
|
trees [2]Tree
|
|
allocator *Allocator
|
|
}
|
|
// History struct {
|
|
// Malloc bool
|
|
// Offset int
|
|
// Size int
|
|
// }
|
|
Allocator struct {
|
|
pool []*Block
|
|
SizeTree *Block
|
|
OffsetTree *Block
|
|
Size int
|
|
// history []History
|
|
}
|
|
)
|
|
|
|
func (t *Tree) deleteLeft(b *Block, treeIndex int) {
|
|
t.Left = t.Left.delete(b, treeIndex)
|
|
}
|
|
|
|
func (t *Tree) deleteRight(b *Block, treeIndex int) {
|
|
t.Right = t.Right.delete(b, treeIndex)
|
|
}
|
|
|
|
func NewAllocator(size int) (result *Allocator) {
|
|
root := &Block{Start: 0, End: size}
|
|
result = &Allocator{
|
|
SizeTree: root,
|
|
OffsetTree: root,
|
|
Size: size,
|
|
}
|
|
root.allocator = result
|
|
return
|
|
}
|
|
func compareBySize(a, b *Block) bool {
|
|
if sizea, sizeb := a.End-a.Start, b.End-b.Start; sizea != sizeb {
|
|
return sizea < sizeb
|
|
}
|
|
return a.Start < b.Start
|
|
}
|
|
|
|
func compareByOffset(a, b *Block) bool {
|
|
return a.Start < b.Start
|
|
}
|
|
|
|
var compares = [...]func(a, b *Block) bool{compareBySize, compareByOffset}
|
|
var emptyTrees = [2]Tree{}
|
|
|
|
func (b *Block) recycle() {
|
|
b.allocator.putBlock(b)
|
|
}
|
|
|
|
func (b *Block) insert(block *Block, treeIndex int) *Block {
|
|
if b == nil {
|
|
return block
|
|
}
|
|
if tree := &b.trees[treeIndex]; compares[treeIndex](block, b) {
|
|
tree.Left = tree.Left.insert(block, treeIndex)
|
|
} else {
|
|
tree.Right = tree.Right.insert(block, treeIndex)
|
|
}
|
|
b.updateHeight(treeIndex)
|
|
return b.balance(treeIndex)
|
|
}
|
|
|
|
func (b *Block) getLeftHeight(treeIndex int) int {
|
|
return b.trees[treeIndex].Left.getHeight(treeIndex)
|
|
}
|
|
|
|
func (b *Block) getRightHeight(treeIndex int) int {
|
|
return b.trees[treeIndex].Right.getHeight(treeIndex)
|
|
}
|
|
|
|
func (b *Block) getHeight(treeIndex int) int {
|
|
if b == nil {
|
|
return 0
|
|
}
|
|
return b.trees[treeIndex].Height
|
|
}
|
|
|
|
func (b *Block) updateHeight(treeIndex int) {
|
|
b.trees[treeIndex].Height = 1 + max(b.getLeftHeight(treeIndex), b.getRightHeight(treeIndex))
|
|
}
|
|
|
|
func (b *Block) balance(treeIndex int) *Block {
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
if tree := &b.trees[treeIndex]; b.getLeftHeight(treeIndex)-b.getRightHeight(treeIndex) > 1 {
|
|
if tree.Left.getRightHeight(treeIndex) > tree.Left.getLeftHeight(treeIndex) {
|
|
tree.Left = tree.Left.rotateLeft(treeIndex)
|
|
}
|
|
return b.rotateRight(treeIndex)
|
|
} else if b.getRightHeight(treeIndex)-b.getLeftHeight(treeIndex) > 1 {
|
|
if tree.Right.getLeftHeight(treeIndex) > tree.Right.getRightHeight(treeIndex) {
|
|
tree.Right = tree.Right.rotateRight(treeIndex)
|
|
}
|
|
return b.rotateLeft(treeIndex)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (b *Block) rotateLeft(treeIndex int) *Block {
|
|
newRoot := b.trees[treeIndex].Right
|
|
b.trees[treeIndex].Right = newRoot.trees[treeIndex].Left
|
|
newRoot.trees[treeIndex].Left = b
|
|
b.updateHeight(treeIndex)
|
|
newRoot.updateHeight(treeIndex)
|
|
return newRoot
|
|
}
|
|
|
|
func (b *Block) rotateRight(treeIndex int) *Block {
|
|
newRoot := b.trees[treeIndex].Left
|
|
b.trees[treeIndex].Left = newRoot.trees[treeIndex].Right
|
|
newRoot.trees[treeIndex].Right = b
|
|
b.updateHeight(treeIndex)
|
|
newRoot.updateHeight(treeIndex)
|
|
return newRoot
|
|
}
|
|
|
|
func (b *Block) findMin(treeIndex int) *Block {
|
|
if left := b.trees[treeIndex].Left; left == nil {
|
|
return b
|
|
} else {
|
|
return left.findMin(treeIndex)
|
|
}
|
|
}
|
|
|
|
func (b *Block) delete(block *Block, treeIndex int) *Block {
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
if compareFunc, tree := compares[treeIndex], &b.trees[treeIndex]; b == block {
|
|
if tree.Left == nil {
|
|
return tree.Right
|
|
} else if tree.Right == nil {
|
|
return tree.Left
|
|
}
|
|
minBlock := tree.Right.findMin(treeIndex)
|
|
tree.deleteRight(minBlock, treeIndex)
|
|
minTree := &minBlock.trees[treeIndex]
|
|
minTree.Left = tree.Left
|
|
minTree.Right = tree.Right
|
|
minTree.Height = tree.Height
|
|
return minBlock
|
|
} else if compareFunc(block, b) {
|
|
tree.deleteLeft(block, treeIndex)
|
|
} else {
|
|
tree.deleteRight(block, treeIndex)
|
|
}
|
|
b.updateHeight(treeIndex)
|
|
return b.balance(treeIndex)
|
|
}
|
|
|
|
func (a *Allocator) Allocate(size int) (offset int) {
|
|
// a.history = append(a.history, History{Malloc: true, Size: size})
|
|
block := a.findAvailableBlock(a.SizeTree, size)
|
|
if block == nil {
|
|
return -1
|
|
}
|
|
offset = block.Start
|
|
a.deleteSizeTree(block)
|
|
a.deleteOffsetTree(block)
|
|
if newStart := offset + size; newStart < block.End {
|
|
newBlock := a.getBlock(newStart, block.End)
|
|
a.SizeTree = a.SizeTree.insert(newBlock, 0)
|
|
a.OffsetTree = a.OffsetTree.insert(newBlock, 1)
|
|
// block.End = block.Start + size
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *Allocator) findAvailableBlock(block *Block, size int) *Block {
|
|
if block == nil {
|
|
return nil
|
|
}
|
|
if tree := &block.trees[0]; block.End-block.Start < size {
|
|
if block1 := a.findAvailableBlock(tree.Left, size); block1 == nil {
|
|
return a.findAvailableBlock(tree.Right, size)
|
|
} else {
|
|
return block1
|
|
}
|
|
}
|
|
return block
|
|
}
|
|
|
|
func (a *Allocator) getBlock(start, end int) *Block {
|
|
if l := len(a.pool); l == 0 {
|
|
return &Block{Start: start, End: end, allocator: a}
|
|
} else {
|
|
block := a.pool[l-1]
|
|
a.pool = a.pool[:l-1]
|
|
block.Start, block.End = start, end
|
|
block.allocator = a
|
|
return block
|
|
}
|
|
}
|
|
|
|
func (a *Allocator) putBlock(b *Block) {
|
|
if b.allocator == nil {
|
|
return
|
|
}
|
|
b.trees = emptyTrees
|
|
b.allocator = nil
|
|
a.pool = append(a.pool, b)
|
|
}
|
|
|
|
func (a *Allocator) Free(offset, size int) {
|
|
// a.history = append(a.history, History{Malloc: false, Offset: offset, Size: size})
|
|
block := a.getBlock(offset, offset+size)
|
|
a.SizeTree, a.OffsetTree = a.SizeTree.insert(block, 0), a.OffsetTree.insert(block, 1)
|
|
a.mergeAdjacentBlocks(block)
|
|
}
|
|
|
|
func (a *Allocator) GetBlocks() (blocks []*Block) {
|
|
a.OffsetTree.Walk(func(block *Block) {
|
|
blocks = append(blocks, block)
|
|
}, 1)
|
|
return
|
|
}
|
|
|
|
func (a *Allocator) GetFreeSize() (ret int) {
|
|
a.OffsetTree.Walk(func(block *Block) {
|
|
ret += block.End - block.Start
|
|
}, 1)
|
|
return
|
|
}
|
|
|
|
func (a *Allocator) deleteSizeTree(block *Block) {
|
|
a.SizeTree = a.SizeTree.delete(block, 0)
|
|
block.trees[0] = emptyTrees[0]
|
|
}
|
|
|
|
func (a *Allocator) deleteOffsetTree(block *Block) {
|
|
a.OffsetTree = a.OffsetTree.delete(block, 1)
|
|
block.trees[1] = emptyTrees[1]
|
|
}
|
|
|
|
func (a *Allocator) mergeAdjacentBlocks(block *Block) {
|
|
if leftAdjacent := a.OffsetTree.findLeftAdjacentBlock(block.Start); leftAdjacent != nil {
|
|
a.deleteSizeTree(leftAdjacent)
|
|
a.deleteOffsetTree(leftAdjacent)
|
|
a.deleteSizeTree(block)
|
|
block.Start = leftAdjacent.Start
|
|
a.SizeTree = a.SizeTree.insert(block, 0)
|
|
leftAdjacent.recycle()
|
|
}
|
|
if rightAdjacent := a.OffsetTree.findRightAdjacentBlock(block.End); rightAdjacent != nil {
|
|
a.deleteSizeTree(rightAdjacent)
|
|
a.deleteOffsetTree(rightAdjacent)
|
|
a.deleteSizeTree(block)
|
|
block.End = rightAdjacent.End
|
|
a.SizeTree = a.SizeTree.insert(block, 0)
|
|
rightAdjacent.recycle()
|
|
}
|
|
}
|
|
|
|
func (b *Block) findLeftAdjacentBlock(offset int) *Block {
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
tree := &b.trees[1]
|
|
if b.End == offset {
|
|
return b
|
|
}
|
|
if b.End > offset {
|
|
return tree.Left.findLeftAdjacentBlock(offset)
|
|
}
|
|
return tree.Right.findLeftAdjacentBlock(offset)
|
|
}
|
|
|
|
func (b *Block) findRightAdjacentBlock(offset int) *Block {
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
if b.Start == offset {
|
|
return b
|
|
}
|
|
tree := &b.trees[1]
|
|
if b.Start < offset {
|
|
return tree.Right.findRightAdjacentBlock(offset)
|
|
}
|
|
return tree.Left.findRightAdjacentBlock(offset)
|
|
}
|
|
|
|
func (b *Block) Walk(fn func(*Block), index int) {
|
|
if b == nil {
|
|
return
|
|
}
|
|
b.trees[index].Left.Walk(fn, index)
|
|
fn(b)
|
|
b.trees[index].Right.Walk(fn, index)
|
|
}
|