mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-12-24 13:48:04 +08:00
123 lines
2.5 KiB
Go
123 lines
2.5 KiB
Go
package util
|
|
|
|
import (
|
|
"errors"
|
|
)
|
|
|
|
type Buddy struct {
|
|
size int
|
|
longests []int
|
|
}
|
|
|
|
var (
|
|
InValidParameterErr = errors.New("buddy: invalid parameter")
|
|
NotFoundErr = errors.New("buddy: can't find block")
|
|
)
|
|
|
|
// NewBuddy creates a buddy instance.
|
|
// If the parameter isn't valid, return the nil and error as well
|
|
func NewBuddy(size int) *Buddy {
|
|
if !isPowerOf2(size) {
|
|
size = fixSize(size)
|
|
}
|
|
nodeCount := 2*size - 1
|
|
longests := make([]int, nodeCount)
|
|
for nodeSize, i := 2*size, 0; i < nodeCount; i++ {
|
|
if isPowerOf2(i + 1) {
|
|
nodeSize /= 2
|
|
}
|
|
longests[i] = nodeSize
|
|
}
|
|
return &Buddy{size, longests}
|
|
}
|
|
|
|
// Alloc find a unused block according to the size
|
|
// return the offset of the block(regard 0 as the beginning)
|
|
// and parameter error if any
|
|
func (b *Buddy) Alloc(size int) (offset int, err error) {
|
|
if size <= 0 {
|
|
err = InValidParameterErr
|
|
return
|
|
}
|
|
if !isPowerOf2(size) {
|
|
size = fixSize(size)
|
|
}
|
|
if size > b.longests[0] {
|
|
err = NotFoundErr
|
|
return
|
|
}
|
|
index := 0
|
|
for nodeSize := b.size; nodeSize != size; nodeSize /= 2 {
|
|
if left := leftChild(index); b.longests[left] >= size {
|
|
index = left
|
|
} else {
|
|
index = rightChild(index)
|
|
}
|
|
}
|
|
b.longests[index] = 0 // mark zero as used
|
|
offset = (index+1)*size - b.size
|
|
// update the parent node's size
|
|
for index != 0 {
|
|
index = parent(index)
|
|
b.longests[index] = max(b.longests[leftChild(index)], b.longests[rightChild(index)])
|
|
}
|
|
return
|
|
}
|
|
|
|
// Free find a block according to the offset and mark it as unused
|
|
// return error if not found or parameter invalid
|
|
func (b *Buddy) Free(offset int) error {
|
|
if offset < 0 || offset >= b.size {
|
|
return InValidParameterErr
|
|
}
|
|
nodeSize := 1
|
|
index := offset + b.size - 1
|
|
for ; b.longests[index] != 0; index = parent(index) {
|
|
nodeSize *= 2
|
|
if index == 0 {
|
|
return NotFoundErr
|
|
}
|
|
}
|
|
b.longests[index] = nodeSize
|
|
// update parent node's size
|
|
for index != 0 {
|
|
index = parent(index)
|
|
nodeSize *= 2
|
|
|
|
leftSize := b.longests[leftChild(index)]
|
|
rightSize := b.longests[rightChild(index)]
|
|
if leftSize+rightSize == nodeSize {
|
|
b.longests[index] = nodeSize
|
|
} else {
|
|
b.longests[index] = max(leftSize, rightSize)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// helpers
|
|
func isPowerOf2(size int) bool {
|
|
return size&(size-1) == 0
|
|
}
|
|
|
|
func fixSize(size int) int {
|
|
size |= size >> 1
|
|
size |= size >> 2
|
|
size |= size >> 4
|
|
size |= size >> 8
|
|
size |= size >> 16
|
|
return size + 1
|
|
}
|
|
|
|
func leftChild(index int) int {
|
|
return 2*index + 1
|
|
}
|
|
|
|
func rightChild(index int) int {
|
|
return 2*index + 2
|
|
}
|
|
|
|
func parent(index int) int {
|
|
return (index+1)/2 - 1
|
|
}
|