2023-10-13 09:43:27 +00:00
|
|
|
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) {
|
2023-12-04 19:25:37 +00:00
|
|
|
firstPage, err := f.pageCount()
|
2023-12-04 19:05:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-10-13 09:43:27 +00:00
|
|
|
idx := &Index{
|
2023-12-04 19:25:37 +00:00
|
|
|
fList: newFreeList(firstPage),
|
2023-10-13 09:43:27 +00:00
|
|
|
aList: *newAllocList(),
|
|
|
|
seen: map[[2]uint64]struct{}{},
|
|
|
|
mask: []bool{},
|
|
|
|
}
|
|
|
|
|
2023-12-04 19:05:15 +00:00
|
|
|
err = f.iterate(func(pageID uint64, page dataPage) error {
|
2023-10-13 09:43:27 +00:00
|
|
|
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
|
|
|
|
}
|