mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
222 lines
4.6 KiB
Go
222 lines
4.6 KiB
Go
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
package roaring
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
type bTreeContainers struct {
|
|
tree *tree
|
|
|
|
lastKey uint64
|
|
lastContainer *Container
|
|
}
|
|
|
|
func newBTreeContainers() *bTreeContainers {
|
|
return &bTreeContainers{
|
|
tree: treeNew(),
|
|
}
|
|
}
|
|
|
|
func NewBTreeBitmap(a ...uint64) *Bitmap {
|
|
b := &Bitmap{
|
|
Containers: newBTreeContainers(),
|
|
}
|
|
// We have no way to report this.
|
|
// Because we just created Bitmap, its OpWriter is nil, so there
|
|
// is no code path which would cause Add() to return an error.
|
|
// Therefore, it's safe to swallow this error.
|
|
_, _ = b.Add(a...)
|
|
return b
|
|
}
|
|
|
|
func (btc *bTreeContainers) Get(key uint64) *Container {
|
|
// Check the last* cache for same container.
|
|
if key == btc.lastKey {
|
|
return btc.lastContainer
|
|
}
|
|
|
|
var c *Container
|
|
el, ok := btc.tree.Get(key)
|
|
if ok {
|
|
c = el
|
|
btc.lastKey = key
|
|
btc.lastContainer = c
|
|
}
|
|
return c
|
|
}
|
|
|
|
func (btc *bTreeContainers) Put(key uint64, c *Container) {
|
|
// If we don't do this, a Put on a container we just got from
|
|
// Get can result in the tree containing a different container
|
|
// than we'll get on next lookup.
|
|
btc.lastKey, btc.lastContainer = key, c
|
|
btc.tree.Set(key, c)
|
|
}
|
|
|
|
func (btc *bTreeContainers) Remove(key uint64) {
|
|
btc.tree.Delete(key)
|
|
if key == btc.lastKey {
|
|
btc.lastKey = ^uint64(0)
|
|
btc.lastContainer = nil
|
|
}
|
|
}
|
|
|
|
func (btc *bTreeContainers) GetOrCreate(key uint64) *Container {
|
|
// Check the last* cache for same container.
|
|
if key == btc.lastKey {
|
|
return btc.lastContainer
|
|
}
|
|
|
|
btc.lastKey = key
|
|
v, ok := btc.tree.Get(key)
|
|
if !ok {
|
|
cont := NewContainer()
|
|
btc.tree.Set(key, cont)
|
|
btc.lastContainer = cont
|
|
return cont
|
|
}
|
|
btc.lastContainer = v
|
|
return btc.lastContainer
|
|
}
|
|
|
|
func (btc *bTreeContainers) Count() (n uint64) {
|
|
e, _ := btc.tree.Seek(0)
|
|
_, c, err := e.Next()
|
|
for err != io.EOF {
|
|
n += uint64(c.N())
|
|
_, c, err = e.Next()
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (btc *bTreeContainers) Clone() Containers {
|
|
nbtc := newBTreeContainers()
|
|
|
|
itr, err := btc.tree.SeekFirst()
|
|
if err == io.EOF {
|
|
return nbtc
|
|
}
|
|
for {
|
|
k, v, err := itr.Next()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
nbtc.tree.Set(k, v.Clone())
|
|
}
|
|
return nbtc
|
|
}
|
|
|
|
func (btc *bTreeContainers) Freeze() Containers {
|
|
nbtc := newBTreeContainers()
|
|
|
|
itr, err := btc.tree.SeekFirst()
|
|
if err == io.EOF {
|
|
return nbtc
|
|
}
|
|
for {
|
|
k, v, err := itr.Next()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
nbtc.tree.Set(k, v.Freeze())
|
|
}
|
|
return nbtc
|
|
}
|
|
|
|
func (btc *bTreeContainers) Last() (key uint64, c *Container) {
|
|
if btc.tree.Len() == 0 {
|
|
return 0, nil
|
|
}
|
|
k, v := btc.tree.Last()
|
|
return k, v
|
|
}
|
|
|
|
func (btc *bTreeContainers) Size() int {
|
|
return btc.tree.Len()
|
|
}
|
|
|
|
func (btc *bTreeContainers) Reset() {
|
|
btc.tree = treeNew()
|
|
// use a definitely-invalid key, so we can distinguish between "you
|
|
// just looked that up, and it was a nil container" and "you have
|
|
// never looked that up before."
|
|
btc.lastKey = ^uint64(0)
|
|
btc.lastContainer = nil
|
|
}
|
|
|
|
func (btc *bTreeContainers) ResetN(n int) {
|
|
// we ignore n because it's impractical to preallocate the tree
|
|
btc.Reset()
|
|
}
|
|
|
|
func (btc *bTreeContainers) Iterator(key uint64) (citer ContainerIterator, found bool) {
|
|
e, ok := btc.tree.Seek(key)
|
|
if ok {
|
|
found = true
|
|
}
|
|
|
|
return &btcIterator{
|
|
e: e,
|
|
}, found
|
|
}
|
|
|
|
func (btc *bTreeContainers) Repair() {
|
|
e, _ := btc.tree.Seek(0)
|
|
_, c, err := e.Next()
|
|
for err != io.EOF {
|
|
c.Repair()
|
|
_, c, err = e.Next()
|
|
}
|
|
}
|
|
|
|
// Update calls fn (existing-container, existed), and expects
|
|
// (new-container, write). If write is true, the container is used to
|
|
// replace the given container.
|
|
func (btc *bTreeContainers) Update(key uint64, fn func(*Container, bool) (*Container, bool)) {
|
|
_, _ = btc.tree.Put(key, fn)
|
|
btc.lastKey = ^uint64(0)
|
|
btc.lastContainer = nil
|
|
}
|
|
|
|
// UpdateEvery calls fn (existing-container, existed), and expects
|
|
// (new-container, write). If write is true, the container is used to
|
|
// replace the given container.
|
|
func (btc *bTreeContainers) UpdateEvery(fn func(uint64, *Container, bool) (*Container, bool)) {
|
|
e, _ := btc.tree.Seek(0)
|
|
// currently not handling the error from this, but in practice it has
|
|
// to be io.EOF.
|
|
_ = e.Every(fn)
|
|
// invalidate cache.
|
|
btc.lastKey = ^uint64(0)
|
|
btc.lastContainer = nil
|
|
}
|
|
|
|
type btcIterator struct {
|
|
e *enumerator
|
|
key uint64
|
|
val *Container
|
|
}
|
|
|
|
func (i *btcIterator) Close() {}
|
|
|
|
func (i *btcIterator) Next() bool {
|
|
k, v, err := i.e.Next()
|
|
if err == io.EOF {
|
|
return false
|
|
}
|
|
if roaringParanoia {
|
|
if v == nil {
|
|
panic(fmt.Sprintf("got nil container for key %d", k))
|
|
}
|
|
}
|
|
i.key = k
|
|
i.val = v
|
|
return true
|
|
}
|
|
|
|
func (i *btcIterator) Value() (uint64, *Container) {
|
|
return i.key, i.val
|
|
}
|