syncthing/vendor/github.com/cznic/lldb/btree.go
2017-04-05 14:34:41 +00:00

2347 lines
51 KiB
Go

// Copyright 2014 The lldb Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package lldb
import (
"bytes"
"errors"
"fmt"
"io"
"sort"
"strings"
"github.com/cznic/fileutil"
"github.com/cznic/internal/buffer"
"github.com/cznic/sortutil"
)
const (
kData = 256 // [1, 512]
kIndex = 256 // [2, 2048]
kKV = 19 // Size of the key/value field in btreeDataPage
kSz = kKV - 1 - 7 // Content prefix size
kH = kKV - 7 // Content field offset for handle
tagBTreeDataPage = 1
tagBTreeIndexPage = 0
)
// BTree is a B+tree[1][2], i.e. a variant which speeds up
// enumeration/iteration of the BTree. According to its origin it can be
// volatile (backed only by memory) or non-volatile (backed by a non-volatile
// Allocator).
//
// The specific implementation of BTrees in this package are B+trees with
// delayed split/concatenation (discussed in e.g. [3]).
//
// Note: No BTree methods returns io.EOF for physical Filer reads/writes. The
// io.EOF is returned only by bTreeEnumerator methods to indicate "no more K-V
// pair".
//
// [1]: http://en.wikipedia.org/wiki/B+tree
// [2]: http://zgking.com:8080/home/donghui/publications/books/dshandbook_BTree.pdf
// [3]: http://people.cs.aau.dk/~simas/aalg06/UbiquitBtree.pdf
type BTree struct {
store btreeStore
root btree
collate func(a, b []byte) int
serial uint64
}
// NewBTree returns a new, memory-only BTree.
func NewBTree(collate func(a, b []byte) int) *BTree {
store := newMemBTreeStore()
root, err := newBTree(store)
if err != nil { // should not happen
panic(err.Error())
}
return &BTree{store, root, collate, 0}
}
// IsMem reports if t is a memory only BTree.
func (t *BTree) IsMem() (r bool) {
_, r = t.store.(*memBTreeStore)
return
}
// Clear empties the tree.
func (t *BTree) Clear() (err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
t.serial++
return t.root.clear(t.store)
}
// Delete deletes key and its associated value from the tree.
func (t *BTree) Delete(key []byte) (err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
t.serial++
_, err = t.root.extract(t.store, nil, t.collate, key)
return
}
// DeleteAny deletes one key and its associated value from the tree. If the
// tree is empty on return then empty is true.
func (t *BTree) DeleteAny() (empty bool, err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
t.serial++
return t.root.deleteAny(t.store)
}
func elem(v interface{}) string {
switch x := v.(type) {
default:
panic("internal error")
case nil:
return "nil"
case bool:
if x {
return "true"
}
return "false"
case int64:
return fmt.Sprint(x)
case uint64:
return fmt.Sprint(x)
case float64:
s := fmt.Sprintf("%g", x)
if !strings.Contains(s, ".") {
s += "."
}
return s
case complex128:
s := fmt.Sprint(x)
return s[1 : len(s)-1]
case []byte:
return fmt.Sprintf("[]byte{% 02x}", x)
case string:
return fmt.Sprintf("%q", x)
}
}
// Dump outputs a human readable dump of t to w. It is usable iff t keys and
// values are encoded scalars (see EncodeScalars). Intended use is only for
// examples or debugging. Some type information is lost in the rendering, for
// example a float value '17.' and an integer value '17' may both output as
// '17'.
func (t *BTree) Dump(w io.Writer) (err error) {
enum, err := t.seekFirst()
if err != nil {
return
}
for {
bkey, bval, err := enum.current()
if err != nil {
return err
}
key, err := DecodeScalars(bkey)
if err != nil {
return err
}
val, err := DecodeScalars(bval)
if err != nil {
return err
}
kk := []string{}
if key == nil {
kk = []string{"null"}
}
for _, v := range key {
kk = append(kk, elem(v))
}
vv := []string{}
if val == nil {
vv = []string{"null"}
}
for _, v := range val {
vv = append(vv, elem(v))
}
skey := strings.Join(kk, ", ")
sval := strings.Join(vv, ", ")
if len(vv) > 1 {
sval = fmt.Sprintf("[]interface{%s}", sval)
}
if _, err = fmt.Fprintf(w, "%s → %s\n", skey, sval); err != nil {
return err
}
err = enum.next()
if err != nil {
if fileutil.IsEOF(err) {
err = nil
break
}
return err
}
}
return
}
// Extract is a combination of Get and Delete. If the key exists in the tree,
// it is returned (like Get) and also deleted from a tree in a more efficient
// way which doesn't walk it twice. The returned slice may be a sub-slice of
// buf if buf was large enough to hold the entire content. Otherwise, a newly
// allocated slice will be returned. It is valid to pass a nil buf.
func (t *BTree) Extract(buf, key []byte) (value []byte, err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
t.serial++
return t.root.extract(t.store, buf, t.collate, key)
}
// First returns the first KV pair of the tree, if it exists. Otherwise key == nil
// and value == nil.
func (t *BTree) First() (key, value []byte, err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
var p btreeDataPage
if _, p, err = t.root.first(t.store); err != nil || p == nil {
return
}
if key, err = p.key(t.store, 0); err != nil {
return
}
value, err = p.value(t.store, 0)
return
}
// Get returns the value associated with key, or nil if no such value exists.
// The returned slice may be a sub-slice of buf if buf was large enough to hold
// the entire content. Otherwise, a newly allocated slice will be returned.
// It is valid to pass a nil buf.
//
// Get is safe for concurrent access by multiple goroutines iff no other
// goroutine mutates the tree.
func (t *BTree) Get(buf, key []byte) (value []byte, err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
pbuffer := buffer.Get(maxBuf)
defer buffer.Put(pbuffer)
buffer := *pbuffer
if buffer, err = t.root.get(t.store, buffer, t.collate, key); buffer == nil || err != nil {
return
}
if len(buffer) != 0 {
// The buffer cache returns nil for empty buffers, bypass it
value = need(len(buffer), buf)
} else {
value = []byte{}
}
copy(value, buffer)
return
}
// Handle reports t's handle.
func (t *BTree) Handle() int64 {
return int64(t.root)
}
// Last returns the last KV pair of the tree, if it exists. Otherwise key == nil
// and value == nil.
func (t *BTree) Last() (key, value []byte, err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
var p btreeDataPage
if _, p, err = t.root.last(t.store); err != nil || p == nil {
return
}
index := p.len() - 1
if key, err = p.key(t.store, index); err != nil {
return
}
value, err = p.value(t.store, index)
return
}
// Put combines Get and Set in a more efficient way where the tree is walked
// only once. The upd(ater) receives the current (key, old-value), if that
// exists or (key, nil) otherwise. It can then return a (new-value, true, nil)
// to create or overwrite the existing value in the KV pair, or (whatever,
// false, nil) if it decides not to create or not to update the value of the KV
// pair.
//
// tree.Set(k, v)
//
// conceptually equals
//
// tree.Put(k, func(k, v []byte){ return v, true }([]byte, bool))
//
// modulo the differing return values.
//
// The returned slice may be a sub-slice of buf if buf was large enough to hold
// the entire content. Otherwise, a newly allocated slice will be returned.
// It is valid to pass a nil buf.
func (t *BTree) Put(buf, key []byte, upd func(key, old []byte) (new []byte, write bool, err error)) (old []byte, written bool, err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
t.serial++
return t.root.put2(buf, t.store, t.collate, key, upd)
}
// Seek returns an Enumerator with "position" or an error of any. Normally the
// position is on a KV pair such that key >= KV.key. Then hit is key == KV.key.
// The position is possibly "after" the last KV pair, but that is not an error.
//
// Seek is safe for concurrent access by multiple goroutines iff no other
// goroutine mutates the tree.
func (t *BTree) Seek(key []byte) (enum *BTreeEnumerator, hit bool, err error) {
enum0, hit, err := t.seek(key)
if err != nil {
return
}
enum = &BTreeEnumerator{
enum: enum0,
firstHit: hit,
key: append([]byte(nil), key...),
}
return
}
func (t *BTree) seek(key []byte) (enum *bTreeEnumerator, hit bool, err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
r := &bTreeEnumerator{t: t, collate: t.collate, serial: t.serial}
if r.p, r.index, hit, err = t.root.seek(t.store, r.collate, key); err != nil {
return
}
enum = r
return
}
// IndexSeek returns an Enumerator with "position" or an error of any. Normally
// the position is on a KV pair such that key >= KV.key. Then hit is key ==
// KV.key. The position is possibly "after" the last KV pair, but that is not
// an error. The collate function originally passed to CreateBTree is used for
// enumerating the tree but a custom collate function c is used for IndexSeek.
//
// IndexSeek is safe for concurrent access by multiple goroutines iff no other
// goroutine mutates the tree.
func (t *BTree) IndexSeek(key []byte, c func(a, b []byte) int) (enum *BTreeEnumerator, hit bool, err error) { //TODO +test
enum0, hit, err := t.indexSeek(key, c)
if err != nil {
return
}
enum = &BTreeEnumerator{
enum: enum0,
firstHit: hit,
key: append([]byte(nil), key...),
}
return
}
func (t *BTree) indexSeek(key []byte, c func(a, b []byte) int) (enum *bTreeEnumerator, hit bool, err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
r := &bTreeEnumerator{t: t, collate: t.collate, serial: t.serial}
if r.p, r.index, hit, err = t.root.seek(t.store, c, key); err != nil {
return
}
enum = r
return
}
// seekFirst returns an enumerator positioned on the first KV pair in the tree,
// if any. For an empty tree, err == io.EOF is returend.
//
// SeekFirst is safe for concurrent access by multiple goroutines iff no other
// goroutine mutates the tree.
func (t *BTree) SeekFirst() (enum *BTreeEnumerator, err error) {
enum0, err := t.seekFirst()
if err != nil {
return
}
var key []byte
if key, _, err = enum0.current(); err != nil {
return
}
enum = &BTreeEnumerator{
enum: enum0,
firstHit: true,
key: append([]byte(nil), key...),
}
return
}
func (t *BTree) seekFirst() (enum *bTreeEnumerator, err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
var p btreeDataPage
if _, p, err = t.root.first(t.store); err == nil && p == nil {
err = io.EOF
}
if err != nil {
return
}
return &bTreeEnumerator{t: t, collate: t.collate, p: p, index: 0, serial: t.serial}, nil
}
// seekLast returns an enumerator positioned on the last KV pair in the tree,
// if any. For an empty tree, err == io.EOF is returend.
//
// SeekLast is safe for concurrent access by multiple goroutines iff no other
// goroutine mutates the tree.
func (t *BTree) SeekLast() (enum *BTreeEnumerator, err error) {
enum0, err := t.seekLast()
if err != nil {
return
}
var key []byte
if key, _, err = enum0.current(); err != nil {
return
}
enum = &BTreeEnumerator{
enum: enum0,
firstHit: true,
key: append([]byte(nil), key...),
}
return
}
func (t *BTree) seekLast() (enum *bTreeEnumerator, err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
var p btreeDataPage
if _, p, err = t.root.last(t.store); err == nil && p == nil {
err = io.EOF
}
if err != nil {
return
}
return &bTreeEnumerator{t: t, collate: t.collate, p: p, index: p.len() - 1, serial: t.serial}, nil
}
// Set sets the value associated with key. Any previous value, if existed, is
// overwritten by the new one.
func (t *BTree) Set(key, value []byte) (err error) {
if t == nil {
err = errors.New("BTree method invoked on nil receiver")
return
}
t.serial++
pdst := buffer.Get(maxBuf)
dst := *pdst
_, err = t.root.put(dst, t.store, t.collate, key, value, true)
buffer.Put(pdst)
return
}
// bTreeEnumerator is a closure of a BTree and a position. It is returned from
// BTree.seek.
//
// NOTE: bTreeEnumerator cannot be used after its BTree was mutated after the
// bTreeEnumerator was acquired from any of the seek, seekFirst, seekLast
// methods.
type bTreeEnumerator struct {
t *BTree
collate func(a, b []byte) int
p btreeDataPage
index int
serial uint64
}
// Current returns the KV pair the enumerator is currently positioned on. If
// the position is before the first KV pair in the tree or after the last KV
// pair in the tree then err == io.EOF is returned.
//
// If the enumerator has been invalidated by updating the tree, ErrINVAL is
// returned.
func (e *bTreeEnumerator) current() (key, value []byte, err error) {
if e == nil {
err = errors.New("bTreeEnumerator method invoked on nil receiver")
return
}
if e.serial != e.t.serial {
err = &ErrINVAL{Src: "bTreeEnumerator invalidated by updating the tree"}
return
}
if e.p == nil || e.index == e.p.len() {
return nil, nil, io.EOF
}
if key, err = e.p.key(e.t.store, e.index); err != nil {
return
}
value, err = e.p.value(e.t.store, e.index)
return
}
// Next attempts to position the enumerator onto the next KV pair wrt the
// current position. If there is no "next" KV pair, io.EOF is returned.
//
// If the enumerator has been invalidated by updating the tree, ErrINVAL is
// returned.
func (e *bTreeEnumerator) next() (err error) {
if e == nil {
err = errors.New("bTreeEnumerator method invoked on nil receiver")
return
}
if e.serial != e.t.serial {
err = &ErrINVAL{Src: "bTreeEnumerator invalidated by updating the tree"}
return
}
if e.p == nil {
return io.EOF
}
switch {
case e.index < e.p.len()-1:
e.index++
default:
ph := e.p.next()
if ph == 0 {
err = io.EOF
break
}
if e.p, err = e.t.store.Get(e.p, ph); err != nil {
e.p = nil
return
}
e.index = 0
}
return
}
// Prev attempts to position the enumerator onto the previous KV pair wrt the
// current position. If there is no "previous" KV pair, io.EOF is returned.
//
// If the enumerator has been invalidated by updating the tree, ErrINVAL is
// returned.
func (e *bTreeEnumerator) prev() (err error) {
if e == nil {
err = errors.New("bTreeEnumerator method invoked on nil receiver")
return
}
if e.serial != e.t.serial {
err = &ErrINVAL{Src: "bTreeEnumerator invalidated by updating the tree"}
return
}
if e.p == nil {
return io.EOF
}
switch {
case e.index > 0:
e.index--
default:
ph := e.p.prev()
if ph == 0 {
err = io.EOF
break
}
if e.p, err = e.t.store.Get(e.p, ph); err != nil {
e.p = nil
return
}
e.index = e.p.len() - 1
}
return
}
// BTreeEnumerator captures the state of enumerating a tree. It is returned
// from the Seek* methods. The enumerator is aware of any mutations made to
// the tree in the process of enumerating it and automatically resumes the
// enumeration.
type BTreeEnumerator struct {
enum *bTreeEnumerator
err error
key []byte
firstHit bool
}
// Next returns the currently enumerated KV pair, if it exists and moves to the
// next KV in the key collation order. If there is no KV pair to return, err ==
// io.EOF is returned.
//
// Next is safe for concurrent access by multiple goroutines iff no other
// goroutine mutates the tree.
func (e *BTreeEnumerator) Next() (key, value []byte, err error) {
if err = e.err; err != nil {
return
}
canRetry := true
retry:
if e.enum.p == nil {
e.err = io.EOF
return nil, nil, e.err
}
if e.enum.index == e.enum.p.len() && e.enum.serial == e.enum.t.serial {
if err = e.enum.next(); err != nil {
e.err = err
return nil, nil, e.err
}
}
if key, value, err = e.enum.current(); err != nil {
if _, ok := err.(*ErrINVAL); !ok || !canRetry {
e.err = err
return
}
canRetry = false
var hit bool
if e.enum, hit, err = e.enum.t.seek(e.key); err != nil {
e.err = err
return
}
if !e.firstHit && hit {
err = e.enum.next()
if err != nil {
e.err = err
return
}
}
goto retry
}
e.firstHit = false
e.key = append([]byte(nil), key...)
e.err = e.enum.next()
return
}
// Prev returns the currently enumerated KV pair, if it exists and moves to the
// previous KV in the key collation order. If there is no KV pair to return,
// err == io.EOF is returned.
//
// Prev is safe for concurrent access by multiple goroutines iff no other
// goroutine mutates the tree.
func (e *BTreeEnumerator) Prev() (key, value []byte, err error) {
if err = e.err; err != nil {
return
}
canRetry := true
retry:
if key, value, err = e.enum.current(); err != nil {
if _, ok := err.(*ErrINVAL); !ok || !canRetry {
e.err = err
return
}
canRetry = false
var hit bool
if e.enum, hit, err = e.enum.t.seek(e.key); err != nil {
e.err = err
return
}
if !e.firstHit && hit {
err = e.enum.prev()
if err != nil {
e.err = err
return
}
}
goto retry
}
e.firstHit = false
e.key = append([]byte(nil), key...)
e.err = e.enum.prev()
return
}
// CreateBTree creates a new BTree in store. It returns the tree, its (freshly
// assigned) handle (for OpenBTree or RemoveBTree) or an error, if any.
func CreateBTree(store *Allocator, collate func(a, b []byte) int) (bt *BTree, handle int64, err error) {
r := &BTree{store: store, collate: collate}
if r.root, err = newBTree(store); err != nil {
return
}
return r, int64(r.root), nil
}
// OpenBTree opens a store's BTree using handle. It returns the tree or an
// error, if any. The same tree may be opened more than once, but operations on
// the separate instances should not ever overlap or void the other instances.
// However, the intended API usage is to open the same tree handle only once
// (handled by some upper layer "dispatcher").
func OpenBTree(store *Allocator, collate func(a, b []byte) int, handle int64) (bt *BTree, err error) {
r := &BTree{store: store, root: btree(handle), collate: collate}
pb := buffer.Get(7)
defer buffer.Put(pb)
b := *pb
if b, err = store.Get(b, handle); err != nil {
return
}
if len(b) != 7 {
return nil, &ErrILSEQ{Off: h2off(handle), More: "btree.go:671"}
}
return r, nil
}
// RemoveBTree removes tree, represented by handle from store. Empty trees are
// cheap, each uses only few bytes of the store. If there's a chance that a
// tree will eventually get reused (non empty again), it's recommended to
// not/never remove it. One advantage of such approach is a stable handle of
// such tree.
func RemoveBTree(store *Allocator, handle int64) (err error) {
tree, err := OpenBTree(store, nil, handle)
if err != nil {
return
}
if err = tree.Clear(); err != nil {
return
}
return store.Free(handle)
}
type btreeStore interface {
Alloc(b []byte) (handle int64, err error)
Free(handle int64) (err error)
Get(dst []byte, handle int64) (b []byte, err error)
Realloc(handle int64, b []byte) (err error)
}
// Read only zero bytes
var zeros [2 * kKV]byte
func init() {
if kData < 1 || kData > 512 {
panic(fmt.Errorf("kData %d: out of limits", kData))
}
if kIndex < 2 || kIndex > 2048 {
panic(fmt.Errorf("kIndex %d: out of limits", kIndex))
}
if kKV < 8 || kKV > 23 {
panic(fmt.Errorf("kKV %d: out of limits", kKV))
}
if n := len(zeros); n < 15 {
panic(fmt.Errorf("not enough zeros: %d", n))
}
}
type memBTreeStore struct {
h int64
m map[int64][]byte
}
func newMemBTreeStore() *memBTreeStore {
return &memBTreeStore{h: 0, m: map[int64][]byte{}}
}
func (s *memBTreeStore) String() string {
var a sortutil.Int64Slice
for k := range s.m {
a = append(a, k)
}
sort.Sort(a)
var sa []string
for _, k := range a {
sa = append(sa, fmt.Sprintf("%#x:|% x|", k, s.m[k]))
}
return strings.Join(sa, "\n")
}
func (s *memBTreeStore) Alloc(b []byte) (handle int64, err error) {
s.h++
handle = s.h
s.m[handle] = bpack(b)
return
}
func (s *memBTreeStore) Free(handle int64) (err error) {
if _, ok := s.m[handle]; !ok {
return &ErrILSEQ{Type: ErrOther, Off: h2off(handle), More: "btree.go:754"}
}
delete(s.m, handle)
return
}
func (s *memBTreeStore) Get(dst []byte, handle int64) (b []byte, err error) {
r, ok := s.m[handle]
if !ok {
return nil, &ErrILSEQ{Type: ErrOther, Off: h2off(handle), More: "btree.go:764"}
}
b = need(len(r), dst)
copy(b, r)
return
}
func (s *memBTreeStore) Realloc(handle int64, b []byte) (err error) {
if _, ok := s.m[handle]; !ok {
return &ErrILSEQ{Type: ErrOther, Off: h2off(handle), More: "btree.go:774"}
}
s.m[handle] = bpack(b)
return
}
/*
0...0 (1 bytes):
Flag
0
+---+
| 0 |
+---+
0 indicates an index page
1...count*14-1
"array" of items, 14 bytes each. Count of items in kIndex-1..2*kIndex+2
Count = (len(raw) - 8) / 14
0..6 7..13
+-------+----------+
| Child | DataPage |
+-------+----------+
Child == handle of a child index page
DataPage == handle of a data page
Offsets into the raw []byte:
Child[X] == 1+14*X
DataPage[X] == 8+14*X
*/
type btreeIndexPage []byte
func newBTreeIndexPage(leftmostChild int64) (p btreeIndexPage) {
p = (*buffer.Get(1 + (kIndex+1)*2*7))[:8]
p[0] = tagBTreeIndexPage
h2b(p[1:], leftmostChild)
return
}
func (p btreeIndexPage) len() int {
return (len(p) - 8) / 14
}
func (p btreeIndexPage) child(index int) int64 {
return b2h(p[1+14*index:])
}
func (p btreeIndexPage) setChild(index int, dp int64) {
h2b(p[1+14*index:], dp)
}
func (p btreeIndexPage) dataPage(index int) int64 {
return b2h(p[8+14*index:])
}
func (p btreeIndexPage) setDataPage(index int, dp int64) {
h2b(p[8+14*index:], dp)
}
func (q btreeIndexPage) insert(index int) btreeIndexPage {
switch len0 := q.len(); {
case index < len0:
has := len(q)
need := has + 14
switch {
case cap(q) >= need:
q = q[:need]
default:
q = append(q, zeros[:14]...)
}
copy(q[8+14*(index+1):8+14*(index+1)+2*(len0-index)*7], q[8+14*index:])
case index == len0:
has := len(q)
need := has + 14
switch {
case cap(q) >= need:
q = q[:need]
default:
q = append(q, zeros[:14]...)
}
}
return q
}
func (p btreeIndexPage) insert3(index int, dataPage, child int64) btreeIndexPage {
p = p.insert(index)
p.setDataPage(index, dataPage)
p.setChild(index+1, child)
return p
}
func (p btreeIndexPage) cmp(a btreeStore, c func(a, b []byte) int, keyA []byte, keyBIndex int) (int, error) {
pb := buffer.Get(maxBuf)
defer buffer.Put(pb)
b := *pb
dp, err := a.Get(b, p.dataPage(keyBIndex))
if err != nil {
return 0, err
}
return btreeDataPage(dp).cmp(a, c, keyA, 0)
}
func (q btreeIndexPage) setLen(n int) btreeIndexPage {
q = q[:cap(q)]
need := 8 + 14*n
if need < len(q) {
return q[:need]
}
return append(q, make([]byte, need-len(q))...)
}
func (p btreeIndexPage) split(a btreeStore, root btree, ph *int64, parent int64, parentIndex int, index *int) (btreeIndexPage, error) {
right := newBTreeIndexPage(0)
right = right.setLen(kIndex)
copy(right[1:1+(2*kIndex+1)*7], p[1+14*(kIndex+1):])
p = p.setLen(kIndex)
if err := a.Realloc(*ph, p); err != nil {
return nil, err
}
rh, err := a.Alloc(right)
if err != nil {
return nil, err
}
if parentIndex >= 0 {
ppp := buffer.Get(maxBuf)
defer buffer.Put(ppp)
pp := btreeIndexPage(*ppp)
if pp, err = a.Get(pp, parent); err != nil {
return nil, err
}
pp = pp.insert3(parentIndex, p.dataPage(kIndex), rh)
if err = a.Realloc(parent, pp); err != nil {
return nil, err
}
} else {
nr := newBTreeIndexPage(*ph)
nr = nr.insert3(0, p.dataPage(kIndex), rh)
nrh, err := a.Alloc(nr)
if err != nil {
return nil, err
}
if err = a.Realloc(int64(root), h2b(make([]byte, 7), nrh)); err != nil {
return nil, err
}
}
if *index > kIndex {
p = right
*ph = rh
*index -= kIndex + 1
}
return p, nil
}
// p is dirty on return
func (p btreeIndexPage) extract(index int) btreeIndexPage {
n := p.len() - 1
if index < n {
sz := (n-index)*14 + 7
copy(p[1+14*index:1+14*index+sz], p[1+14*(index+1):])
}
return p.setLen(n)
}
// must persist all changes made
func (p btreeIndexPage) underflow(a btreeStore, root, iroot, parent int64, ph *int64, parentIndex int, index *int) (btreeIndexPage, error) {
lh, rh, err := checkSiblings(a, parent, parentIndex)
if err != nil {
return nil, err
}
pleft := buffer.Get(maxBuf)
defer buffer.Put(pleft)
left := btreeIndexPage(*pleft)
if lh != 0 {
if left, err = a.Get(left, lh); err != nil {
return nil, err
}
if lc := left.len(); lc > kIndex {
ppp := buffer.Get(maxBuf)
defer buffer.Put(ppp)
pp := *ppp
if pp, err = a.Get(pp, parent); err != nil {
return nil, err
}
pc := p.len()
p = p.setLen(pc + 1)
di, si, sz := 1+1*14, 1+0*14, (2*pc+1)*7
copy(p[di:di+sz], p[si:])
p.setChild(0, left.child(lc))
p.setDataPage(0, btreeIndexPage(pp).dataPage(parentIndex-1))
*index++
btreeIndexPage(pp).setDataPage(parentIndex-1, left.dataPage(lc-1))
left = left.setLen(lc - 1)
if err = a.Realloc(parent, pp); err != nil {
return nil, err
}
if err = a.Realloc(*ph, p); err != nil {
return nil, err
}
return p, a.Realloc(lh, left)
}
}
if rh != 0 {
pright := buffer.Get(maxBuf)
defer buffer.Put(pright)
right := *pright
if right, err = a.Get(right, rh); err != nil {
return nil, err
}
if rc := btreeIndexPage(right).len(); rc > kIndex {
ppp := buffer.Get(maxBuf)
defer buffer.Put(ppp)
pp := *ppp
if pp, err = a.Get(pp, parent); err != nil {
return nil, err
}
pc := p.len()
p = p.setLen(pc + 1)
p.setDataPage(pc, btreeIndexPage(pp).dataPage(parentIndex))
pc++
p.setChild(pc, btreeIndexPage(right).child(0))
btreeIndexPage(pp).setDataPage(parentIndex, btreeIndexPage(right).dataPage(0))
di, si, sz := 1+0*14, 1+1*14, (2*rc+1)*7
copy(right[di:di+sz], right[si:])
right = btreeIndexPage(right).setLen(rc - 1)
if err = a.Realloc(parent, pp); err != nil {
return nil, err
}
if err = a.Realloc(*ph, p); err != nil {
return nil, err
}
return p, a.Realloc(rh, right)
}
}
if lh != 0 {
*index += left.len() + 1
if left, err = left.concat(a, root, iroot, parent, lh, *ph, parentIndex-1); err != nil {
return p, err
}
p, *ph = left, lh
return p, nil
}
return p.concat(a, root, iroot, parent, *ph, rh, parentIndex)
}
// must persist all changes made
func (p btreeIndexPage) concat(a btreeStore, root, iroot, parent, ph, rh int64, parentIndex int) (btreeIndexPage, error) {
ppp := buffer.Get(maxBuf)
defer buffer.Put(ppp)
pp := *ppp
pp, err := a.Get(pp, parent)
if err != nil {
return nil, err
}
pright := buffer.Get(maxBuf)
defer buffer.Put(pright)
right := *pright
if right, err = a.Get(right, rh); err != nil {
return nil, err
}
pc := p.len()
rc := btreeIndexPage(right).len()
p = p.setLen(pc + rc + 1)
p.setDataPage(pc, btreeIndexPage(pp).dataPage(parentIndex))
di, si, sz := 1+14*(pc+1), 1+0*14, (2*rc+1)*7
copy(p[di:di+sz], right[si:])
if err := a.Realloc(ph, p); err != nil {
return nil, err
}
if err := a.Free(rh); err != nil {
return nil, err
}
if pc := btreeIndexPage(pp).len(); pc > 1 {
if parentIndex < pc-1 {
di, si, sz := 8+parentIndex*14, 8+(parentIndex+1)*14, 2*(pc-1-parentIndex)*7
copy(pp[di:si+sz], pp[si:])
}
pp = btreeIndexPage(pp).setLen(pc - 1)
return p, a.Realloc(parent, pp)
}
if err := a.Free(iroot); err != nil {
return nil, err
}
pb7 := buffer.Get(7)
defer buffer.Put(pb7)
b7 := *pb7
return p, a.Realloc(root, h2b(b7[:7], ph))
}
/*
0...0 (1 bytes):
Flag
0
+---+
| 1 |
+---+
1 indicates a data page
1...14 (14 bytes)
1..7 8..14
+------+------+
| Prev | Next |
+------+------+
Prev, Next == Handles of the data pages doubly linked list
Count = (len(raw) - 15) / (2*kKV)
15...count*2*kKV-1
"array" of items, 2*kKV bytes each. Count of items in kData-1..2*kData
Item
0..kKV-1 kKV..2*kKV-1
+----------+--------------+
| Key | Value |
+----------+--------------+
Key/Value encoding
Length 0...kKV-1
0 1...N N+1...kKV-1
+---+---------+-------------+
| N | Data | Padding |
+---+---------+-------------+
N == content length
Data == Key or Value content
Padding == MUST be zero bytes
Length >= kKV
0 1...kkV-8 kKV-7...kkV-1
+------+-----------+--------------+
| 0xFF | Data | H |
+------+-----------+--------------+
Data == Key or Value content, first kKV-7 bytes
H == Handle to THE REST of the content, w/o the first bytes in Data.
Offsets into the raw []byte:
Key[X] == 15+2*kKV*X
Value[X] == 15+kKV+2*kKV*X
*/
type btreeDataPage []byte
func newBTreeDataPage() (p btreeDataPage) {
p = (*buffer.CGet(1 + 2*7 + (kData+1)*2*kKV))[:1+2*7]
p[0] = tagBTreeDataPage
return
}
func newBTreeDataPageAlloc(a btreeStore) (p btreeDataPage, h int64, err error) {
p = newBTreeDataPage()
h, err = a.Alloc(p)
return
}
func (p btreeDataPage) len() int {
return (len(p) - 15) / (2 * kKV)
}
func (q btreeDataPage) setLen(n int) btreeDataPage {
q = q[:cap(q)]
need := 15 + 2*kKV*n
if need < len(q) {
return q[:need]
}
return append(q, make([]byte, need-len(q))...)
}
func (p btreeDataPage) prev() int64 {
return b2h(p[1:])
}
func (p btreeDataPage) next() int64 {
return b2h(p[8:])
}
func (p btreeDataPage) setPrev(h int64) {
h2b(p[1:], h)
}
func (p btreeDataPage) setNext(h int64) {
h2b(p[8:], h)
}
func (q btreeDataPage) insert(index int) btreeDataPage {
switch len0 := q.len(); {
case index < len0:
has := len(q)
need := has + 2*kKV
switch {
case cap(q) >= need:
q = q[:need]
default:
q = append(q, zeros[:2*kKV]...)
}
q.copy(q, index+1, index, len0-index)
return q
case index == len0:
has := len(q)
need := has + 2*kKV
switch {
case cap(q) >= need:
return q[:need]
default:
return append(q, zeros[:2*kKV]...)
}
}
panic("internal error")
}
func (p btreeDataPage) contentField(off int) (b []byte, h int64) {
p = p[off:]
switch n := int(p[0]); {
case n >= kKV: // content has a handle
b = append([]byte(nil), p[1:1+kSz]...)
h = b2h(p[kH:])
default: // content is embedded
b, h = append([]byte(nil), p[1:1+n]...), 0
}
return
}
func (p btreeDataPage) content(a btreeStore, off int) (b []byte, err error) {
b, h := p.contentField(off)
if h == 0 {
return
}
// content has a handle
b2, err := a.Get(nil, h) //TODO buffers: Later, not a public API
if err != nil {
return nil, err
}
return append(b, b2...), nil
}
func (p btreeDataPage) setContent(a btreeStore, off int, b []byte) (err error) {
p = p[off:]
switch {
case p[0] >= kKV: // existing content has a handle
switch n := len(b); {
case n < kKV:
p[0] = byte(n)
if err = a.Free(b2h(p[kH:])); err != nil {
return
}
copy(p[1:], b)
default:
// reuse handle
copy(p[1:1+kSz], b)
return a.Realloc(b2h(p[kH:]), b[kSz:])
}
default: // existing content is embedded
switch n := len(b); {
case n < kKV:
p[0] = byte(n)
copy(p[1:], b)
default:
p[0] = 0xff
copy(p[1:1+kSz], b)
h, err := a.Alloc(b[kSz:])
if err != nil {
return err
}
h2b(p[kH:], h)
}
}
return
}
func (p btreeDataPage) keyField(index int) (b []byte, h int64) {
return p.contentField(15 + 2*kKV*index)
}
func (p btreeDataPage) key(a btreeStore, index int) (b []byte, err error) {
return p.content(a, 15+2*kKV*index)
}
func (p btreeDataPage) valueField(index int) (b []byte, h int64) {
return p.contentField(15 + kKV + 2*kKV*index)
}
func (p btreeDataPage) value(a btreeStore, index int) (b []byte, err error) {
value, err := p.content(a, 15+kKV+2*kKV*index)
if err == nil && value == nil {
// We have a valid page, no fetch error, the key is valid so return
// non-nil data
return []byte{}, nil
}
return value, err
}
func (p btreeDataPage) valueCopy(a btreeStore, index int) (b []byte, err error) {
if b, err = p.content(a, 15+kKV+2*kKV*index); err != nil {
return
}
return append([]byte(nil), b...), nil
}
func (p btreeDataPage) setKey(a btreeStore, index int, key []byte) (err error) {
return p.setContent(a, 15+2*kKV*index, key)
}
func (p btreeDataPage) setValue(a btreeStore, index int, value []byte) (err error) {
return p.setContent(a, 15+kKV+2*kKV*index, value)
}
func (p btreeDataPage) cmp(a btreeStore, c func(a, b []byte) int, keyA []byte, keyBIndex int) (y int, err error) {
var keyB []byte
if keyB, err = p.content(a, 15+2*kKV*keyBIndex); err != nil {
return
}
return c(keyA, keyB), nil
}
func (p btreeDataPage) copy(src btreeDataPage, di, si, n int) {
do, so := 15+2*kKV*di, 15+2*kKV*si
copy(p[do:do+2*kKV*n], src[so:])
}
// {p,left} dirty on exit
func (p btreeDataPage) moveLeft(left btreeDataPage, n int) (btreeDataPage, btreeDataPage) {
nl, np := left.len(), p.len()
left = left.setLen(nl + n)
left.copy(p, nl, 0, n)
p.copy(p, 0, n, np-n)
return p.setLen(np - n), left
}
func (p btreeDataPage) moveRight(right btreeDataPage, n int) (btreeDataPage, btreeDataPage) {
nr, np := right.len(), p.len()
right = right.setLen(nr + n)
right.copy(right, n, 0, nr)
right.copy(p, 0, np-n, n)
return p.setLen(np - n), right
}
func (p btreeDataPage) insertItem(a btreeStore, index int, key, value []byte) (btreeDataPage, error) {
p = p.insert(index)
di, sz := 15+2*kKV*index, 2*kKV
copy(p[di:di+sz], zeros[:sz])
if err := p.setKey(a, index, key); err != nil {
return nil, err
}
return p, p.setValue(a, index, value)
}
func (p btreeDataPage) split(a btreeStore, root, ph, parent int64, parentIndex, index int, key, value []byte) (btreeDataPage, error) {
right, rh, err := newBTreeDataPageAlloc(a)
if err != nil {
return nil, err
}
if next := p.next(); next != 0 {
right.setNext(p.next())
nxh := right.next()
pnx := buffer.Get(maxBuf)
defer buffer.Put(pnx)
nx := *pnx
if nx, err = a.Get(nx, nxh); err != nil {
return nil, err
}
btreeDataPage(nx).setPrev(rh)
if err = a.Realloc(nxh, nx); err != nil {
return nil, err
}
}
p.setNext(rh)
right.setPrev(ph)
right = right.setLen(kData)
right.copy(p, 0, kData, kData)
p = p.setLen(kData)
if parentIndex >= 0 {
ppp := buffer.Get(maxBuf)
defer buffer.Put(ppp)
pp := btreeIndexPage(*ppp)
if pp, err = a.Get(pp, parent); err != nil {
return nil, err
}
pp = pp.insert3(parentIndex, rh, rh)
if err = a.Realloc(parent, pp); err != nil {
return nil, err
}
} else {
nr := newBTreeIndexPage(ph)
nr = nr.insert3(0, rh, rh)
var nrh int64
if nrh, err = a.Alloc(nr); err != nil {
return nil, err
}
if err = a.Realloc(root, h2b(make([]byte, 7), nrh)); err != nil {
return nil, err
}
}
if index > kData {
if right, err = right.insertItem(a, index-kData, key, value); err != nil {
return nil, err
}
} else {
if p, err = p.insertItem(a, index, key, value); err != nil {
return nil, err
}
}
if err = a.Realloc(ph, p); err != nil {
return nil, err
}
return p, a.Realloc(rh, right)
}
func (p btreeDataPage) overflow(a btreeStore, root, ph, parent int64, parentIndex, index int, key, value []byte) (btreeDataPage, error) {
leftH, rightH, err := checkSiblings(a, parent, parentIndex)
if err != nil {
return nil, err
}
if leftH != 0 {
pleft := buffer.Get(maxBuf)
defer buffer.Put(pleft)
left := btreeDataPage(*pleft)
if left, err = a.Get(left, leftH); err != nil {
return nil, err
}
if left.len() < 2*kData && index > 0 {
p, left = p.moveLeft(left, 1)
if err = a.Realloc(leftH, left); err != nil {
return nil, err
}
if p, err = p.insertItem(a, index-1, key, value); err != nil {
return nil, err
}
return p, a.Realloc(ph, p)
}
}
if rightH != 0 {
pright := buffer.Get(maxBuf)
defer buffer.Put(pright)
right := btreeDataPage(*pright)
if right, err = a.Get(right, rightH); err != nil {
return nil, err
}
if right.len() < 2*kData {
if index < 2*kData {
p, right = p.moveRight(right, 1)
if err = a.Realloc(rightH, right); err != nil {
return nil, err
}
if p, err = p.insertItem(a, index, key, value); err != nil {
return nil, err
}
return p, a.Realloc(ph, p)
} else {
if right, err = right.insertItem(a, 0, key, value); err != nil {
return nil, err
}
return p, a.Realloc(rightH, right)
}
}
}
return p.split(a, root, ph, parent, parentIndex, index, key, value)
}
func (p btreeDataPage) swap(a btreeStore, di int, value []byte, canOverwrite bool) (oldValue []byte, err error) {
if oldValue, err = p.value(a, di); err != nil {
return
}
if !canOverwrite {
return
}
oldValue = append([]byte(nil), oldValue...)
err = p.setValue(a, di, value)
return
}
type btreePage []byte
func (p btreePage) isIndex() bool {
return p[0] == tagBTreeIndexPage
}
func (p btreePage) len() int {
if p.isIndex() {
return btreeIndexPage(p).len()
}
return btreeDataPage(p).len()
}
func (p btreePage) find(a btreeStore, c func(a, b []byte) int, key []byte) (index int, ok bool, err error) {
l := 0
h := p.len() - 1
isIndex := p.isIndex()
if c == nil {
c = bytes.Compare
}
for l <= h {
index = (l + h) >> 1
var cmp int
if isIndex {
if cmp, err = btreeIndexPage(p).cmp(a, c, key, index); err != nil {
return
}
} else {
if cmp, err = btreeDataPage(p).cmp(a, c, key, index); err != nil {
return
}
}
switch ok = cmp == 0; {
case cmp > 0:
l = index + 1
case ok:
return
default:
h = index - 1
}
}
return l, false, nil
}
// p is dirty after extract!
func (p btreeDataPage) extract(a btreeStore, index int) (btreeDataPage, []byte, error) {
value, err := p.valueCopy(a, index)
if err != nil {
return nil, nil, err
}
if _, h := p.keyField(index); h != 0 {
if err = a.Free(h); err != nil {
return nil, nil, err
}
}
if _, h := p.valueField(index); h != 0 {
if err = a.Free(h); err != nil {
return nil, nil, err
}
}
n := p.len() - 1
if index < n {
p.copy(p, index, index+1, n-index)
}
return p.setLen(n), value, nil
}
func checkSiblings(a btreeStore, parent int64, parentIndex int) (left, right int64, err error) {
if parentIndex >= 0 {
pp := buffer.Get(maxBuf)
defer buffer.Put(pp)
p := btreeIndexPage(*pp)
if p, err = a.Get(p, parent); err != nil {
return
}
if parentIndex > 0 {
left = p.child(parentIndex - 1)
}
if parentIndex < p.len() {
right = p.child(parentIndex + 1)
}
}
return
}
// underflow must persist all changes made.
func (p btreeDataPage) underflow(a btreeStore, root, iroot, parent, ph int64, parentIndex int) (err error) {
lh, rh, err := checkSiblings(a, parent, parentIndex)
if err != nil {
return err
}
if lh != 0 {
pleft := buffer.Get(maxBuf)
defer buffer.Put(pleft)
left := *pleft
if left, err = a.Get(left, lh); err != nil {
return err
}
if btreeDataPage(left).len()+p.len() >= 2*kData {
left, p = btreeDataPage(left).moveRight(p, 1)
if err = a.Realloc(lh, left); err != nil {
return err
}
return a.Realloc(ph, p)
}
}
if rh != 0 {
pright := buffer.Get(maxBuf)
defer buffer.Put(pright)
right := *pright
if right, err = a.Get(right, rh); err != nil {
return err
}
if p.len()+btreeDataPage(right).len() > 2*kData {
right, p = btreeDataPage(right).moveLeft(p, 1)
if err = a.Realloc(rh, right); err != nil {
return err
}
return a.Realloc(ph, p)
}
}
if lh != 0 {
pleft := buffer.Get(maxBuf)
defer buffer.Put(pleft)
left := *pleft
if left, err = a.Get(left, lh); err != nil {
return err
}
if err = a.Realloc(ph, p); err != nil {
return err
}
return btreeDataPage(left).concat(a, root, iroot, parent, lh, ph, parentIndex-1)
}
return p.concat(a, root, iroot, parent, ph, rh, parentIndex)
}
// concat must persist all changes made.
func (p btreeDataPage) concat(a btreeStore, root, iroot, parent, ph, rh int64, parentIndex int) (err error) {
pright := buffer.Get(maxBuf)
defer buffer.Put(pright)
right := *pright
if right, err = a.Get(right, rh); err != nil {
return err
}
right, p = btreeDataPage(right).moveLeft(p, btreeDataPage(right).len())
nxh := btreeDataPage(right).next()
if nxh != 0 {
pnx := buffer.Get(maxBuf)
defer buffer.Put(pnx)
nx := *pnx
if nx, err = a.Get(nx, nxh); err != nil {
return err
}
btreeDataPage(nx).setPrev(ph)
if err = a.Realloc(nxh, nx); err != nil {
return err
}
}
p.setNext(nxh)
if err = a.Free(rh); err != nil {
return err
}
ppp := buffer.Get(maxBuf)
defer buffer.Put(ppp)
pp := *ppp
if pp, err = a.Get(pp, parent); err != nil {
return err
}
if btreeIndexPage(pp).len() > 1 {
pp = btreeIndexPage(pp).extract(parentIndex)
btreeIndexPage(pp).setChild(parentIndex, ph)
if err = a.Realloc(parent, pp); err != nil {
return err
}
return a.Realloc(ph, p)
}
if err = a.Free(iroot); err != nil {
return err
}
if err = a.Realloc(ph, p); err != nil {
return err
}
var b7 [7]byte
return a.Realloc(root, h2b(b7[:], ph))
}
// external "root" is stable and contains the real root.
type btree int64
func newBTree(a btreeStore) (btree, error) {
r, err := a.Alloc(zeros[:7])
return btree(r), err
}
func (root btree) String(a btreeStore) string {
pr := buffer.Get(16)
defer buffer.Put(pr)
r := *pr
r, err := a.Get(r, int64(root))
if err != nil {
panic(err)
}
iroot := b2h(r)
m := map[int64]bool{int64(root): true}
s := []string{fmt.Sprintf("tree %#x -> %#x\n====", root, iroot)}
if iroot == 0 {
return s[0]
}
var f func(int64, string)
f = func(h int64, ind string) {
if m[h] {
return
}
m[h] = true
pb := buffer.Get(maxBuf)
defer buffer.Put(pb)
b := btreePage(*pb)
var err error
if b, err = a.Get(b, h); err != nil {
panic(err)
}
s = append(s, fmt.Sprintf("%s@%#x", ind, h))
switch b.isIndex() {
case true:
da := []int64{}
b := btreeIndexPage(b)
for i := 0; i < b.len(); i++ {
c, d := b.child(i), b.dataPage(i)
s = append(s, fmt.Sprintf("%schild[%d] %#x dataPage[%d] %#x", ind, i, c, i, d))
da = append(da, c)
da = append(da, d)
}
i := b.len()
c := b.child(i)
s = append(s, fmt.Sprintf("%schild[%d] %#x", ind, i, c))
for _, c := range da {
f(c, ind+" ")
}
f(c, ind+" ")
case false:
b := btreeDataPage(b)
s = append(s, fmt.Sprintf("%sprev %#x next %#x", ind, b.prev(), b.next()))
for i := 0; i < b.len(); i++ {
k, err := b.key(a, i)
if err != nil {
panic(err)
}
v, err := b.value(a, i)
if err != nil {
panic(err)
}
s = append(s, fmt.Sprintf("%sK[%d]|% x| V[%d]|% x|", ind, i, k, i, v))
}
}
}
f(iroot, "")
return strings.Join(s, "\n")
}
func (root btree) put(dst []byte, a btreeStore, c func(a, b []byte) int, key, value []byte, canOverwrite bool) (prev []byte, err error) {
prev, _, err = root.put2(dst, a, c, key, func(key, old []byte) (new []byte, write bool, err error) {
new, write = value, true
return
})
return
}
func (root btree) put2(dst []byte, a btreeStore, c func(a, b []byte) int, key []byte, upd func(key, old []byte) (new []byte, write bool, err error)) (old []byte, written bool, err error) {
var r, value []byte
if r, err = a.Get(dst, int64(root)); err != nil {
return
}
iroot := b2h(r)
var h int64
if iroot == 0 {
p := newBTreeDataPage()
if value, written, err = upd(key, nil); err != nil || !written {
return
}
if p, err = p.insertItem(a, 0, key, value); err != nil {
return
}
h, err = a.Alloc(p)
if err != nil {
return nil, true, err
}
err = a.Realloc(int64(root), h2b(r, h)[:7])
return
}
parentIndex := -1
var parent int64
ph := iroot
pp := buffer.Get(maxBuf)
defer buffer.Put(pp)
p := *pp
for {
if p, err = a.Get(p[:cap(p)], ph); err != nil {
return
}
var index int
var ok bool
if index, ok, err = btreePage(p).find(a, c, key); err != nil {
return
}
switch {
case ok: // Key found
if btreePage(p).isIndex() {
ph = btreeIndexPage(p).dataPage(index)
if p, err = a.Get(p, ph); err != nil {
return
}
if old, err = btreeDataPage(p).valueCopy(a, 0); err != nil {
return
}
if value, written, err = upd(key, old); err != nil || !written {
return
}
if _, err = btreeDataPage(p).swap(a, 0, value, true); err != nil {
return
}
err = a.Realloc(ph, p)
return
}
if old, err = btreeDataPage(p).valueCopy(a, index); err != nil {
return
}
if value, written, err = upd(key, old); err != nil || !written {
return
}
if _, err = btreeDataPage(p).swap(a, index, value, true); err != nil {
return
}
err = a.Realloc(ph, p)
return
case btreePage(p).isIndex():
if btreePage(p).len() > 2*kIndex {
if p, err = btreeIndexPage(p).split(a, root, &ph, parent, parentIndex, &index); err != nil {
return
}
}
parentIndex = index
parent = ph
ph = btreeIndexPage(p).child(index)
default:
if value, written, err = upd(key, nil); err != nil || !written {
return
}
if btreePage(p).len() < 2*kData { // page is not full
if p, err = btreeDataPage(p).insertItem(a, index, key, value); err != nil {
return
}
err = a.Realloc(ph, p)
return
}
// page is full
p, err = btreeDataPage(p).overflow(a, int64(root), ph, parent, parentIndex, index, key, value)
return
}
}
}
//TODO actually use 'dst' to return 'value'
func (root btree) get(a btreeStore, dst []byte, c func(a, b []byte) int, key []byte) (b []byte, err error) {
var r []byte
if r, err = a.Get(dst, int64(root)); err != nil {
return
}
iroot := b2h(r)
if iroot == 0 {
return
}
ph := iroot
for {
var p btreePage
if p, err = a.Get(p, ph); err != nil {
return
}
var index int
var ok bool
if index, ok, err = p.find(a, c, key); err != nil {
return
}
switch {
case ok:
if p.isIndex() {
dh := btreeIndexPage(p).dataPage(index)
dp, err := a.Get(dst, dh)
if err != nil {
return nil, err
}
return btreeDataPage(dp).value(a, 0)
}
return btreeDataPage(p).value(a, index)
case p.isIndex():
ph = btreeIndexPage(p).child(index)
default:
return
}
}
}
//TODO actually use 'dst' to return 'value'
func (root btree) extract(a btreeStore, dst []byte, c func(a, b []byte) int, key []byte) (value []byte, err error) {
var r []byte
if r, err = a.Get(dst, int64(root)); err != nil {
return
}
iroot := b2h(r)
if iroot == 0 {
return
}
ph := iroot
parentIndex := -1
var parent int64
pp := buffer.Get(maxBuf)
defer buffer.Put(pp)
p := *pp
for {
if p, err = a.Get(p[:cap(p)], ph); err != nil {
return
}
var index int
var ok bool
if index, ok, err = btreePage(p).find(a, c, key); err != nil {
return
}
if ok {
if btreePage(p).isIndex() {
dph := btreeIndexPage(p).dataPage(index)
var dp []byte
if dp, err = a.Get(dst, dph); err != nil {
return nil, err
}
if btreeDataPage(dp).len() > kData {
if dp, value, err = btreeDataPage(dp).extract(a, 0); err != nil {
return nil, err
}
return value, a.Realloc(dph, dp)
}
if btreeIndexPage(p).len() < kIndex && ph != iroot {
if p, err = btreeIndexPage(p).underflow(a, int64(root), iroot, parent, &ph, parentIndex, &index); err != nil {
return nil, err
}
}
parentIndex = index + 1
parent = ph
ph = btreeIndexPage(p).child(parentIndex)
continue
}
p, value, err = btreeDataPage(p).extract(a, index)
if btreePage(p).len() >= kData {
err = a.Realloc(ph, p)
return
}
if ph != iroot {
err = btreeDataPage(p).underflow(a, int64(root), iroot, parent, ph, parentIndex)
return
}
if btreePage(p).len() == 0 {
if err = a.Free(ph); err != nil {
return
}
err = a.Realloc(int64(root), zeros[:7])
return
}
err = a.Realloc(ph, p)
return
}
if !btreePage(p).isIndex() {
return
}
if btreePage(p).len() < kIndex && ph != iroot {
if p, err = btreeIndexPage(p).underflow(a, int64(root), iroot, parent, &ph, parentIndex, &index); err != nil {
return nil, err
}
}
parentIndex = index
parent = ph
ph = btreeIndexPage(p).child(index)
}
}
func (root btree) deleteAny(a btreeStore) (bool, error) {
pr := buffer.Get(7)
defer buffer.Put(pr)
r := *pr
var err error
if r, err = a.Get(r, int64(root)); err != nil {
return false, err
}
iroot := b2h(r)
if iroot == 0 {
return true, nil
}
ph := iroot
parentIndex := -1
var parent int64
pp := buffer.Get(maxBuf)
defer buffer.Put(pp)
p := *pp
for {
if p, err = a.Get(p, ph); err != nil {
return false, err
}
index := btreePage(p).len() / 2
if btreePage(p).isIndex() {
dph := btreeIndexPage(p).dataPage(index)
pdp := buffer.Get(maxBuf)
defer buffer.Put(pdp)
dp := *pdp
if dp, err = a.Get(dp, dph); err != nil {
return false, err
}
if btreeDataPage(dp).len() > kData {
if dp, _, err = btreeDataPage(dp).extract(a, 0); err != nil {
return false, err
}
return false, a.Realloc(dph, dp)
}
if btreeIndexPage(p).len() < kIndex && ph != iroot {
if p, err = btreeIndexPage(p).underflow(a, int64(root), iroot, parent, &ph, parentIndex, &index); err != nil {
return false, err
}
}
parentIndex = index + 1
parent = ph
ph = btreeIndexPage(p).child(parentIndex)
continue
}
p, _, err = btreeDataPage(p).extract(a, index)
if btreePage(p).len() >= kData {
err = a.Realloc(ph, p)
return false, err
}
if ph != iroot {
err = btreeDataPage(p).underflow(a, int64(root), iroot, parent, ph, parentIndex)
return false, err
}
if btreePage(p).len() == 0 {
if err = a.Free(ph); err != nil {
return true, err
}
return true, a.Realloc(int64(root), zeros[:7])
}
return false, a.Realloc(ph, p)
}
}
func (root btree) first(a btreeStore) (ph int64, p btreeDataPage, err error) {
pr := buffer.Get(7)
defer buffer.Put(pr)
r := *pr
if r, err = a.Get(r, int64(root)); err != nil {
return
}
for ph = b2h(r); ph != 0; ph = btreeIndexPage(p).child(0) {
if p, err = a.Get(p, ph); err != nil {
return
}
if !btreePage(p).isIndex() {
break
}
}
return
}
func (root btree) last(a btreeStore) (ph int64, p btreeDataPage, err error) {
pr := buffer.Get(7)
defer buffer.Put(pr)
r := *pr
if r, err = a.Get(r, int64(root)); err != nil {
return
}
for ph = b2h(r); ph != 0; ph = btreeIndexPage(p).child(btreeIndexPage(p).len()) {
if p, err = a.Get(p, ph); err != nil {
return
}
if !btreePage(p).isIndex() {
break
}
}
return
}
// key >= p[index].key
func (root btree) seek(a btreeStore, c func(a, b []byte) int, key []byte) (p btreeDataPage, index int, equal bool, err error) {
pr := buffer.Get(7)
defer buffer.Put(pr)
r := *pr
if r, err = a.Get(r, int64(root)); err != nil {
return
}
for ph := b2h(r); ph != 0; ph = btreeIndexPage(p).child(index) {
if p, err = a.Get(p, ph); err != nil {
break
}
if index, equal, err = btreePage(p).find(a, c, key); err != nil {
break
}
if equal {
if !btreePage(p).isIndex() {
break
}
p, err = a.Get(p, btreeIndexPage(p).dataPage(index))
index = 0
break
}
if !btreePage(p).isIndex() {
break
}
}
return
}
func (root btree) clear(a btreeStore) (err error) {
pr := buffer.Get(7)
defer buffer.Put(pr)
r := *pr
if r, err = a.Get(r, int64(root)); err != nil {
return
}
iroot := b2h(r)
if iroot == 0 {
return
}
if err = root.clear2(a, iroot); err != nil {
return
}
var b [7]byte
return a.Realloc(int64(root), b[:])
}
func (root btree) clear2(a btreeStore, ph int64) (err error) {
pp := buffer.Get(maxBuf)
defer buffer.Put(pp)
p := *pp
if p, err = a.Get(p, ph); err != nil {
return
}
switch btreePage(p).isIndex() {
case true:
ip := btreeIndexPage(p)
for i := 0; i <= ip.len(); i++ {
if err = root.clear2(a, ip.child(i)); err != nil {
return err
}
}
case false:
dp := btreeDataPage(p)
for i := 0; i < dp.len(); i++ {
if err = dp.setKey(a, i, nil); err != nil {
return
}
if err = dp.setValue(a, i, nil); err != nil {
return
}
}
}
return a.Free(ph)
}