109 lines
2.4 KiB
Go
109 lines
2.4 KiB
Go
package freelist
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"go.etcd.io/bbolt/internal/common"
|
|
)
|
|
|
|
type array struct {
|
|
*shared
|
|
|
|
ids []common.Pgid // all free and available free page ids.
|
|
}
|
|
|
|
func (f *array) Init(ids common.Pgids) {
|
|
f.ids = ids
|
|
f.reindex()
|
|
}
|
|
|
|
func (f *array) Allocate(txid common.Txid, n int) common.Pgid {
|
|
if len(f.ids) == 0 {
|
|
return 0
|
|
}
|
|
|
|
var initial, previd common.Pgid
|
|
for i, id := range f.ids {
|
|
if id <= 1 {
|
|
panic(fmt.Sprintf("invalid page allocation: %d", id))
|
|
}
|
|
|
|
// Reset initial page if this is not contiguous.
|
|
if previd == 0 || id-previd != 1 {
|
|
initial = id
|
|
}
|
|
|
|
// If we found a contiguous block then remove it and return it.
|
|
if (id-initial)+1 == common.Pgid(n) {
|
|
// If we're allocating off the beginning then take the fast path
|
|
// and just adjust the existing slice. This will use extra memory
|
|
// temporarily but the append() in free() will realloc the slice
|
|
// as is necessary.
|
|
if (i + 1) == n {
|
|
f.ids = f.ids[i+1:]
|
|
} else {
|
|
copy(f.ids[i-n+1:], f.ids[i+1:])
|
|
f.ids = f.ids[:len(f.ids)-n]
|
|
}
|
|
|
|
// Remove from the free cache.
|
|
for i := common.Pgid(0); i < common.Pgid(n); i++ {
|
|
delete(f.cache, initial+i)
|
|
}
|
|
f.allocs[initial] = txid
|
|
return initial
|
|
}
|
|
|
|
previd = id
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (f *array) FreeCount() int {
|
|
return len(f.ids)
|
|
}
|
|
|
|
func (f *array) freePageIds() common.Pgids {
|
|
return f.ids
|
|
}
|
|
|
|
func (f *array) mergeSpans(ids common.Pgids) {
|
|
sort.Sort(ids)
|
|
common.Verify(func() {
|
|
idsIdx := make(map[common.Pgid]struct{})
|
|
for _, id := range f.ids {
|
|
// The existing f.ids shouldn't have duplicated free ID.
|
|
if _, ok := idsIdx[id]; ok {
|
|
panic(fmt.Sprintf("detected duplicated free page ID: %d in existing f.ids: %v", id, f.ids))
|
|
}
|
|
idsIdx[id] = struct{}{}
|
|
}
|
|
|
|
prev := common.Pgid(0)
|
|
for _, id := range ids {
|
|
// The ids shouldn't have duplicated free ID. Note page 0 and 1
|
|
// are reserved for meta pages, so they can never be free page IDs.
|
|
if prev == id {
|
|
panic(fmt.Sprintf("detected duplicated free ID: %d in ids: %v", id, ids))
|
|
}
|
|
prev = id
|
|
|
|
// The ids shouldn't have any overlap with the existing f.ids.
|
|
if _, ok := idsIdx[id]; ok {
|
|
panic(fmt.Sprintf("detected overlapped free page ID: %d between ids: %v and existing f.ids: %v", id, ids, f.ids))
|
|
}
|
|
}
|
|
})
|
|
f.ids = common.Pgids(f.ids).Merge(ids)
|
|
}
|
|
|
|
func NewArrayFreelist() Interface {
|
|
a := &array{
|
|
shared: newShared(),
|
|
ids: []common.Pgid{},
|
|
}
|
|
a.Interface = a
|
|
return a
|
|
}
|