jldb/mdb/pfile/index.go

111 lines
2.5 KiB
Go

package pfile
import (
"git.crumpington.com/public/jldb/lib/errs"
"git.crumpington.com/public/jldb/mdb/change"
)
type Index struct {
fList *freeList
aList allocList
seen map[[2]uint64]struct{}
mask []bool
}
func NewIndex(f *File) (*Index, error) {
maxPage, err := f.maxPage()
if err != nil {
return nil, err
}
idx := &Index{
fList: newFreeList(maxPage),
aList: *newAllocList(),
seen: map[[2]uint64]struct{}{},
mask: []bool{},
}
err = f.iterate(func(pageID uint64, page dataPage) error {
header := page.Header()
switch header.PageType {
case pageTypeHead:
idx.aList.Create(header.CollectionID, header.ItemID, pageID)
case pageTypeData:
if !idx.aList.Push(header.CollectionID, header.ItemID, pageID) {
return errs.Corrupt.WithMsg("encountered data page with no corresponding head page")
}
case pageTypeFree:
idx.fList.Push(pageID)
}
return nil
})
return idx, err
}
func (idx *Index) StageChanges(changes []change.Change) {
clear(idx.seen)
if cap(idx.mask) < len(changes) {
idx.mask = make([]bool, len(changes))
}
idx.mask = idx.mask[:len(changes)]
for i := len(changes) - 1; i >= 0; i-- {
key := [2]uint64{changes[i].CollectionID, changes[i].ItemID}
if _, ok := idx.seen[key]; ok {
idx.mask[i] = false
continue
}
idx.seen[key] = struct{}{}
idx.mask[i] = true
}
for i, active := range idx.mask {
if !active {
continue
}
if changes[i].Store {
count := idx.getPageCountForData(len(changes[i].Data))
changes[i].WritePageIDs = idx.fList.Pop(count, changes[i].WritePageIDs)
}
if pages := idx.aList.Remove(changes[i].CollectionID, changes[i].ItemID); pages != nil {
changes[i].ClearPageIDs = pages
}
}
}
func (idx *Index) UnstageChanges(changes []change.Change) {
for i := range changes {
if len(changes[i].WritePageIDs) > 0 {
idx.fList.Push(changes[i].WritePageIDs...)
changes[i].WritePageIDs = changes[i].WritePageIDs[:0]
}
if len(changes[i].ClearPageIDs) > 0 {
idx.aList.Store(changes[i].CollectionID, changes[i].ItemID, changes[i].ClearPageIDs)
changes[i].ClearPageIDs = changes[i].ClearPageIDs[:0]
}
}
}
func (idx *Index) ApplyChanges(changes []change.Change) {
for i := range changes {
if len(changes[i].WritePageIDs) > 0 {
idx.aList.Store(changes[i].CollectionID, changes[i].ItemID, changes[i].WritePageIDs)
}
if len(changes[i].ClearPageIDs) > 0 {
idx.fList.Push(changes[i].ClearPageIDs...)
}
}
}
func (idx *Index) getPageCountForData(dataSize int) int {
count := dataSize / pageDataSize
if dataSize%pageDataSize != 0 {
count++
}
return count
}