PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/nt/bptree/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
nt / bptree
Files
8 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/nt/bptree/v0
gno
0.9

Files (8)

  • README.mdmarkdown
  • gnomod.tomltoml
  • doc.gnogno
  • node.gnogno
  • tree.gnogno
  • example_test.gnogno
  • tree_test.gnogno
  • PLAN.mdmarkdown
  • tree.gnogno
    1package bptree23type ITree interface {4	Size() int5	Has(key string) bool6	Get(key string) any7

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    GetByIndex(index int) (key string, value any)
    8 Iterate(start, end string, cb IterCbFn) bool
    9 ReverseIterate(start, end string, cb IterCbFn) bool
    10 IterateByOffset(offset int, count int, cb IterCbFn) bool
    11 ReverseIterateByOffset(offset int, count int, cb IterCbFn) bool
    12 Set(key string, value any) (updated bool)
    13 Remove(key string) (value any, removed bool)
    14}
    15
    16type IterCbFn func(key string, value any) bool
    17
    18// Verify BPTree implements ITree.
    19var _ ITree = (*BPTree)(nil)
    20
    21// The zero value is usable as an empty tree with fanout 32.
    22type BPTree struct {
    23 root node
    24 size int
    25 fanout int
    26}
    27
    28// NewBPTreeN creates a new empty B+ tree with the given fanout.
    29// It panics when fanout is lower than 4.
    30func NewBPTreeN(fanout int) *BPTree {
    31 if fanout < 4 {
    32 panic("bptree: fanout must be >= 4")
    33 }
    34 return &BPTree{fanout: fanout}
    35}
    36
    37// NewBPTree32 creates a new empty B+ tree with fanout 32.
    38func NewBPTree32() *BPTree {
    39 return NewBPTreeN(32)
    40}
    41
    42func (t *BPTree) Size() int {
    43 return t.size
    44}
    45
    46func (t *BPTree) Has(key string) bool {
    47 if t.root == nil {
    48 return false
    49 }
    50 n := t.root
    51 for !n.isLeaf() {
    52 inner := n.(*innerNode)
    53 idx := inner.findChild(key)
    54 n = inner.children[idx]
    55 }
    56 leaf := n.(*leafNode)
    57 _, found := leaf.find(key)
    58 return found
    59}
    60
    61// Get retrieves the value associated with the given key.
    62// It returns the value if the key exists, or nil if it doesn't.
    63// This allows for a simpler usage pattern with type assertions:
    64//
    65// if value, ok := tree.Get("key").(MyType); ok {
    66// // use value
    67// }
    68//
    69// Use Has to distinguish a stored nil value from a missing key.
    70func (t *BPTree) Get(key string) any {
    71 if t.root == nil {
    72 return nil
    73 }
    74 n := t.root
    75 for !n.isLeaf() {
    76 inner := n.(*innerNode)
    77 idx := inner.findChild(key)
    78 n = inner.children[idx]
    79 }
    80 leaf := n.(*leafNode)
    81 pos, found := leaf.find(key)
    82 if !found {
    83 return nil
    84 }
    85 return *leaf.values[pos]
    86}
    87
    88// GetByIndex returns the key-value pair at the given 0-based index.
    89// Panics if index is out of range.
    90func (t *BPTree) GetByIndex(index int) (key string, value any) {
    91 if t.root == nil || index < 0 || index >= t.size {
    92 panic("GetByIndex asked for invalid index")
    93 }
    94 n := t.root
    95 rem := index
    96 for !n.isLeaf() {
    97 inner := n.(*innerNode)
    98 found := false
    99 for i, s := range inner.sizes {
    100 if rem < s {
    101 n = inner.children[i]
    102 found = true
    103 break
    104 }
    105 rem -= s
    106 }
    107 if !found {
    108 panic("GetByIndex asked for invalid index")
    109 }
    110 }
    111 leaf := n.(*leafNode)
    112 return leaf.keys[rem], *leaf.values[rem]
    113}
    114
    115//----------------------------------------
    116// Set
    117
    118// pathEntry records a step in the root-to-leaf descent.
    119type pathEntry struct {
    120 inner *innerNode
    121 childIdx int
    122}
    123
    124// Set inserts or updates a key-value pair. Returns true if the key already existed.
    125func (t *BPTree) Set(key string, value any) (updated bool) {
    126 if t.fanout == 0 {
    127 t.fanout = 32
    128 }
    129 fanout := t.fanout
    130
    131 vp := &value // wrap value in *any for lazy loading
    132
    133 // Empty tree: create a single leaf.
    134 if t.root == nil {
    135 leaf := newLeafNode(fanout)
    136 leaf.keys = append(leaf.keys, key)
    137 leaf.values = append(leaf.values, vp)
    138 t.root = leaf
    139 t.size = 1
    140 return false
    141 }
    142
    143 // Descend to the leaf, recording the path.
    144 var path []pathEntry
    145 n := t.root
    146 for !n.isLeaf() {
    147 inner := n.(*innerNode)
    148 idx := inner.findChild(key)
    149 path = append(path, pathEntry{inner, idx})
    150 n = inner.children[idx]
    151 }
    152 leaf := n.(*leafNode)
    153
    154 // Check if key already exists.
    155 pos, found := leaf.find(key)
    156 if found {
    157 *leaf.values[pos] = value
    158 return true
    159 }
    160
    161 // Insert new key-value.
    162 leaf.insertAt(pos, key, vp)
    163 t.size++
    164
    165 // Increment sizes up the path.
    166 for i := range path {
    167 path[i].inner.sizes[path[i].childIdx]++
    168 }
    169
    170 // Split if leaf overflows.
    171 if len(leaf.keys) > fanout {
    172 t.splitLeaf(leaf, pos, path)
    173 }
    174
    175 return false
    176}
    177
    178// splitLeaf splits an overflowed leaf node. Uses 90/10 split for append
    179// patterns (insertPos == fanout) and 50/50 otherwise.
    180func (t *BPTree) splitLeaf(leaf *leafNode, insertPos int, path []pathEntry) {
    181 fanout := t.fanout
    182
    183 // 90/10 split for append pattern: new key ended up at position fanout
    184 // (greater than all pre-existing keys).
    185 var mid int
    186 if insertPos == fanout {
    187 mid = fanout - 1
    188 } else {
    189 mid = (fanout + 1) / 2
    190 }
    191
    192 // Create right leaf with entries [mid, fanout+1).
    193 right := newLeafNode(fanout)
    194 right.keys = append(right.keys, leaf.keys[mid:]...)
    195 right.values = append(right.values, leaf.values[mid:]...)
    196
    197 // Truncate left leaf to [0, mid).
    198 for i := mid; i < len(leaf.keys); i++ {
    199 leaf.keys[i] = ""
    200 leaf.values[i] = nil
    201 }
    202 leaf.keys = leaf.keys[:mid]
    203 leaf.values = leaf.values[:mid]
    204
    205 // Promote separator to parent.
    206 sep := right.keys[0]
    207 leftSize := len(leaf.keys)
    208 rightSize := len(right.keys)
    209
    210 if len(path) == 0 {
    211 // Root was the leaf; create a new inner root.
    212 newRoot := newInnerNode(fanout)
    213 newRoot.keys = append(newRoot.keys, sep)
    214 newRoot.children = append(newRoot.children, leaf, right)
    215 newRoot.sizes = append(newRoot.sizes, leftSize, rightSize)
    216 t.root = newRoot
    217 return
    218 }
    219
    220 // Insert into parent.
    221 pe := path[len(path)-1]
    222 parent := pe.inner
    223 childIdx := pe.childIdx
    224
    225 parent.sizes[childIdx] = leftSize
    226 parent.insertChildAt(childIdx+1, sep, right, rightSize)
    227
    228 if len(parent.children) > fanout {
    229 t.splitInner(parent, path[:len(path)-1])
    230 }
    231}
    232
    233// splitInner splits an overflowed inner node (always 50/50).
    234func (t *BPTree) splitInner(inner *innerNode, path []pathEntry) {
    235 fanout := t.fanout
    236 mid := (fanout + 1) / 2
    237
    238 promotedKey := inner.keys[mid-1]
    239
    240 right := newInnerNode(fanout)
    241 right.keys = append(right.keys, inner.keys[mid:]...)
    242 right.children = append(right.children, inner.children[mid:]...)
    243 right.sizes = append(right.sizes, inner.sizes[mid:]...)
    244
    245 for i := mid - 1; i < len(inner.keys); i++ {
    246 inner.keys[i] = ""
    247 }
    248 for i := mid; i < len(inner.children); i++ {
    249 inner.children[i] = nil
    250 }
    251 inner.keys = inner.keys[:mid-1]
    252 inner.children = inner.children[:mid]
    253 inner.sizes = inner.sizes[:mid]
    254
    255 if len(path) == 0 {
    256 newRoot := newInnerNode(fanout)
    257 newRoot.keys = append(newRoot.keys, promotedKey)
    258 newRoot.children = append(newRoot.children, inner, right)
    259 newRoot.sizes = append(newRoot.sizes, inner.nodeSize(), right.nodeSize())
    260 t.root = newRoot
    261 return
    262 }
    263
    264 pe := path[len(path)-1]
    265 parent := pe.inner
    266 childIdx := pe.childIdx
    267
    268 parent.sizes[childIdx] = inner.nodeSize()
    269 parent.insertChildAt(childIdx+1, promotedKey, right, right.nodeSize())
    270
    271 if len(parent.children) > fanout {
    272 t.splitInner(parent, path[:len(path)-1])
    273 }
    274}
    275
    276//----------------------------------------
    277// Remove
    278
    279// Remove deletes a key. Returns the old value and true if the key was found.
    280func (t *BPTree) Remove(key string) (value any, removed bool) {
    281 if t.root == nil {
    282 return nil, false
    283 }
    284 fanout := t.fanout
    285
    286 var path []pathEntry
    287 n := t.root
    288 for !n.isLeaf() {
    289 inner := n.(*innerNode)
    290 idx := inner.findChild(key)
    291 path = append(path, pathEntry{inner, idx})
    292 n = inner.children[idx]
    293 }
    294 leaf := n.(*leafNode)
    295
    296 pos, found := leaf.find(key)
    297 if !found {
    298 return nil, false
    299 }
    300
    301 var vp *any
    302 _, vp = leaf.removeAt(pos)
    303 value = *vp
    304 t.size--
    305
    306 for i := range path {
    307 path[i].inner.sizes[path[i].childIdx]--
    308 }
    309
    310 // Handle root leaf.
    311 if len(path) == 0 {
    312 if len(leaf.keys) == 0 {
    313 t.root = nil
    314 }
    315 return value, true
    316 }
    317
    318 // Check underflow.
    319 minKeys := fanout / 2
    320 if len(leaf.keys) >= minKeys {
    321 if pos == 0 {
    322 t.updateSeparator(path)
    323 }
    324 return value, true
    325 }
    326
    327 // If the minimum key was removed, fix ancestor separators before rebalancing.
    328 if pos == 0 {
    329 t.updateSeparator(path)
    330 }
    331
    332 // Rebalance.
    333 t.rebalanceLeaf(leaf, path)
    334 return value, true
    335}
    336
    337// updateSeparator fixes the parent's separator key if the child's min key changed
    338// (e.g., after removing the leftmost entry).
    339func (t *BPTree) updateSeparator(path []pathEntry) {
    340 for i := len(path) - 1; i >= 0; i-- {
    341 pe := path[i]
    342 if pe.childIdx > 0 {
    343 child := pe.inner.children[pe.childIdx]
    344 pe.inner.keys[pe.childIdx-1] = child.minKey()
    345 return
    346 }
    347 }
    348}
    349
    350// rebalanceLeaf handles a leaf that has underflowed (< fanout/2 entries).
    351// Tries redistribute from left, then right, then merges with a sibling.
    352func (t *BPTree) rebalanceLeaf(leaf *leafNode, path []pathEntry) {
    353 fanout := t.fanout
    354 minKeys := fanout / 2
    355 pe := path[len(path)-1]
    356 parent := pe.inner
    357 childIdx := pe.childIdx
    358
    359 // Try redistribute from left sibling.
    360 if childIdx > 0 {
    361 leftSib := parent.children[childIdx-1].(*leafNode)
    362 if len(leftSib.keys) > minKeys {
    363 k, v := leftSib.removeAt(len(leftSib.keys) - 1)
    364 leaf.insertAt(0, k, v)
    365 parent.keys[childIdx-1] = leaf.keys[0]
    366 parent.sizes[childIdx-1] = len(leftSib.keys)
    367 parent.sizes[childIdx] = len(leaf.keys)
    368 return
    369 }
    370 }
    371
    372 // Try redistribute from right sibling.
    373 if childIdx < len(parent.children)-1 {
    374 rightSib := parent.children[childIdx+1].(*leafNode)
    375 if len(rightSib.keys) > minKeys {
    376 k, v := rightSib.removeAt(0)
    377 leaf.insertAt(len(leaf.keys), k, v)
    378 parent.keys[childIdx] = rightSib.keys[0]
    379 parent.sizes[childIdx] = len(leaf.keys)
    380 parent.sizes[childIdx+1] = len(rightSib.keys)
    381 return
    382 }
    383 }
    384
    385 // Merge.
    386 if childIdx > 0 {
    387 leftSib := parent.children[childIdx-1].(*leafNode)
    388 mergeLeaves(leftSib, leaf, parent, childIdx)
    389 } else {
    390 rightSib := parent.children[childIdx+1].(*leafNode)
    391 mergeLeaves(leaf, rightSib, parent, childIdx+1)
    392 }
    393
    394 t.rebalanceInner(path[:len(path)-1])
    395}
    396
    397// mergeLeaves merges right leaf into left and removes right from parent.
    398func mergeLeaves(left, right *leafNode, parent *innerNode, rightIdx int) {
    399 left.keys = append(left.keys, right.keys...)
    400 left.values = append(left.values, right.values...)
    401 parent.removeChildAt(rightIdx)
    402 parent.sizes[rightIdx-1] = len(left.keys)
    403}
    404
    405// rebalanceInner handles an inner node that has underflowed after a child merge.
    406func (t *BPTree) rebalanceInner(path []pathEntry) {
    407 if len(path) == 0 {
    408 root := t.root.(*innerNode)
    409 if len(root.children) == 1 {
    410 t.root = root.children[0]
    411 }
    412 return
    413 }
    414
    415 pe := path[len(path)-1]
    416 parent := pe.inner
    417 childIdx := pe.childIdx
    418 child := parent.children[childIdx].(*innerNode)
    419 fanout := t.fanout
    420 minChildren := fanout / 2
    421
    422 if len(child.children) >= minChildren {
    423 if childIdx > 0 {
    424 parent.keys[childIdx-1] = child.minKey()
    425 }
    426 return
    427 }
    428
    429 // Try redistribute from left sibling.
    430 if childIdx > 0 {
    431 leftSib := parent.children[childIdx-1].(*innerNode)
    432 if len(leftSib.children) > minChildren {
    433 redistributeInnerLeft(parent, childIdx, leftSib, child)
    434 return
    435 }
    436 }
    437
    438 // Try redistribute from right sibling.
    439 if childIdx < len(parent.children)-1 {
    440 rightSib := parent.children[childIdx+1].(*innerNode)
    441 if len(rightSib.children) > minChildren {
    442 redistributeInnerRight(parent, childIdx, child, rightSib)
    443 return
    444 }
    445 }
    446
    447 // Merge.
    448 if childIdx > 0 {
    449 leftSib := parent.children[childIdx-1].(*innerNode)
    450 mergeInner(leftSib, child, parent, childIdx)
    451 } else {
    452 rightSib := parent.children[childIdx+1].(*innerNode)
    453 mergeInner(child, rightSib, parent, childIdx+1)
    454 }
    455
    456 grandPath := path[:len(path)-1]
    457 if len(grandPath) == 0 {
    458 root := t.root.(*innerNode)
    459 if len(root.children) == 1 {
    460 t.root = root.children[0]
    461 }
    462 return
    463 }
    464
    465 gpe := grandPath[len(grandPath)-1]
    466 gparent := gpe.inner
    467 gchildIdx := gpe.childIdx
    468 gchild := gparent.children[gchildIdx].(*innerNode)
    469 minChildren = t.fanout / 2
    470
    471 if len(gchild.children) < minChildren {
    472 t.rebalanceInner(grandPath)
    473 } else if gchildIdx > 0 {
    474 gparent.keys[gchildIdx-1] = gchild.minKey()
    475 }
    476}
    477
    478// redistributeInnerLeft moves the last child from left sibling to the
    479// deficient child, pulling down the parent separator and pushing up a new one.
    480func redistributeInnerLeft(parent *innerNode, childIdx int, leftSib, child *innerNode) {
    481 sep := parent.keys[childIdx-1]
    482 child.keys = append(child.keys, "")
    483 copy(child.keys[1:], child.keys)
    484 child.keys[0] = sep
    485
    486 lastChild := leftSib.children[len(leftSib.children)-1]
    487 lastSize := leftSib.sizes[len(leftSib.sizes)-1]
    488 child.children = append(child.children, nil)
    489 copy(child.children[1:], child.children)
    490 child.children[0] = lastChild
    491 child.sizes = append(child.sizes, 0)
    492 copy(child.sizes[1:], child.sizes)
    493 child.sizes[0] = lastSize
    494
    495 parent.keys[childIdx-1] = leftSib.keys[len(leftSib.keys)-1]
    496
    497 leftSib.keys[len(leftSib.keys)-1] = ""
    498 leftSib.keys = leftSib.keys[:len(leftSib.keys)-1]
    499 leftSib.children[len(leftSib.children)-1] = nil
    500 leftSib.children = leftSib.children[:len(leftSib.children)-1]
    501 leftSib.sizes = leftSib.sizes[:len(leftSib.sizes)-1]
    502
    503 parent.sizes[childIdx-1] = leftSib.nodeSize()
    504 parent.sizes[childIdx] = child.nodeSize()
    505}
    506
    507// redistributeInnerRight moves the first child from right sibling to the
    508// deficient child, pulling down the parent separator and pushing up a new one.
    509func redistributeInnerRight(parent *innerNode, childIdx int, child, rightSib *innerNode) {
    510 sep := parent.keys[childIdx]
    511 child.keys = append(child.keys, sep)
    512
    513 firstChild := rightSib.children[0]
    514 firstSize := rightSib.sizes[0]
    515 child.children = append(child.children, firstChild)
    516 child.sizes = append(child.sizes, firstSize)
    517
    518 parent.keys[childIdx] = rightSib.keys[0]
    519
    520 copy(rightSib.keys, rightSib.keys[1:])
    521 rightSib.keys[len(rightSib.keys)-1] = ""
    522 rightSib.keys = rightSib.keys[:len(rightSib.keys)-1]
    523 copy(rightSib.children, rightSib.children[1:])
    524 rightSib.children[len(rightSib.children)-1] = nil
    525 rightSib.children = rightSib.children[:len(rightSib.children)-1]
    526 copy(rightSib.sizes, rightSib.sizes[1:])
    527 rightSib.sizes = rightSib.sizes[:len(rightSib.sizes)-1]
    528
    529 parent.sizes[childIdx] = child.nodeSize()
    530 parent.sizes[childIdx+1] = rightSib.nodeSize()
    531}
    532
    533// mergeInner merges right inner node into left, pulling down the parent
    534// separator, and removes right from parent.
    535func mergeInner(left, right *innerNode, parent *innerNode, rightIdx int) {
    536 sep := parent.keys[rightIdx-1]
    537 left.keys = append(left.keys, sep)
    538 left.keys = append(left.keys, right.keys...)
    539 left.children = append(left.children, right.children...)
    540 left.sizes = append(left.sizes, right.sizes...)
    541 parent.removeChildAt(rightIdx)
    542 parent.sizes[rightIdx-1] = left.nodeSize()
    543}
    544
    545//----------------------------------------
    546// Stack-based iteration
    547
    548// iterStack is a stack of (innerNode, childIdx) for traversal.
    549// It is ephemeral — built during iteration, discarded after.
    550type iterStack []pathEntry
    551
    552// descendLeft descends to the leftmost leaf, pushing inner nodes onto the stack.
    553func descendLeft(n node, stack *iterStack) *leafNode {
    554 for !n.isLeaf() {
    555 inner := n.(*innerNode)
    556 *stack = append(*stack, pathEntry{inner, 0})
    557 n = inner.children[0]
    558 }
    559 return n.(*leafNode)
    560}
    561
    562// descendRight descends to the rightmost leaf, pushing inner nodes onto the stack.
    563func descendRight(n node, stack *iterStack) *leafNode {
    564 for !n.isLeaf() {
    565 inner := n.(*innerNode)
    566 last := len(inner.children) - 1
    567 *stack = append(*stack, pathEntry{inner, last})
    568 n = inner.children[last]
    569 }
    570 return n.(*leafNode)
    571}
    572
    573// advanceLeaf moves to the next leaf in ascending order via the stack.
    574// Returns nil if there is no next leaf.
    575func advanceLeaf(stack *iterStack) *leafNode {
    576 for len(*stack) > 0 {
    577 top := &(*stack)[len(*stack)-1]
    578 top.childIdx++
    579 if top.childIdx < len(top.inner.children) {
    580 n := top.inner.children[top.childIdx]
    581 return descendLeft(n, stack)
    582 }
    583 *stack = (*stack)[:len(*stack)-1]
    584 }
    585 return nil
    586}
    587
    588// retreatLeaf moves to the previous leaf in descending order via the stack.
    589// Returns nil if there is no previous leaf.
    590func retreatLeaf(stack *iterStack) *leafNode {
    591 for len(*stack) > 0 {
    592 top := &(*stack)[len(*stack)-1]
    593 top.childIdx--
    594 if top.childIdx >= 0 {
    595 n := top.inner.children[top.childIdx]
    596 return descendRight(n, stack)
    597 }
    598 *stack = (*stack)[:len(*stack)-1]
    599 }
    600 return nil
    601}
    602
    603//----------------------------------------
    604// Iterate / ReverseIterate
    605
    606// Iterate calls cb for each key-value pair in [start, end) ascending order.
    607// Empty start/end means no bound. Returns true if stopped early by cb.
    608// The tree must not be modified during iteration (no Set or Remove from the callback).
    609func (t *BPTree) Iterate(start, end string, cb IterCbFn) bool {
    610 if t.root == nil {
    611 return false
    612 }
    613
    614 var stack iterStack
    615 var leaf *leafNode
    616 var pos int
    617
    618 if start == "" {
    619 leaf = descendLeft(t.root, &stack)
    620 pos = 0
    621 } else {
    622 leaf, pos, stack = t.descendToGE(start)
    623 if leaf == nil {
    624 return false
    625 }
    626 }
    627
    628 for leaf != nil {
    629 for pos < len(leaf.keys) {
    630 k := leaf.keys[pos]
    631 if end != "" && k >= end {
    632 return false
    633 }
    634 if cb(k, *leaf.values[pos]) {
    635 return true
    636 }
    637 pos++
    638 }
    639 leaf = advanceLeaf(&stack)
    640 pos = 0
    641 }
    642 return false
    643}
    644
    645// ReverseIterate calls cb for each key-value pair in [start, end] descending order.
    646// Empty start/end means no bound. Returns true if stopped early by cb.
    647// The tree must not be modified during iteration (no Set or Remove from the callback).
    648func (t *BPTree) ReverseIterate(start, end string, cb IterCbFn) bool {
    649 if t.root == nil {
    650 return false
    651 }
    652
    653 var stack iterStack
    654 var leaf *leafNode
    655 var pos int
    656
    657 if end == "" {
    658 leaf = descendRight(t.root, &stack)
    659 pos = len(leaf.keys) - 1
    660 } else {
    661 leaf, pos, stack = t.descendToLE(end)
    662 if leaf == nil {
    663 return false
    664 }
    665 }
    666
    667 for leaf != nil {
    668 for pos >= 0 {
    669 k := leaf.keys[pos]
    670 if start != "" && k < start {
    671 return false
    672 }
    673 if cb(k, *leaf.values[pos]) {
    674 return true
    675 }
    676 pos--
    677 }
    678 leaf = retreatLeaf(&stack)
    679 if leaf != nil {
    680 pos = len(leaf.keys) - 1
    681 }
    682 }
    683 return false
    684}
    685
    686// IterateByOffset calls cb for count entries starting at the offset-th entry
    687// in ascending order. Returns true if stopped early by cb.
    688// The tree must not be modified during iteration (no Set or Remove from the callback).
    689func (t *BPTree) IterateByOffset(offset int, count int, cb IterCbFn) bool {
    690 if t.root == nil || offset >= t.size || count <= 0 {
    691 return false
    692 }
    693 if offset < 0 {
    694 offset = 0
    695 }
    696
    697 leaf, pos, stack := t.descendToOffset(offset)
    698
    699 visited := 0
    700 for leaf != nil && visited < count {
    701 for pos < len(leaf.keys) && visited < count {
    702 if cb(leaf.keys[pos], *leaf.values[pos]) {
    703 return true
    704 }
    705 visited++
    706 pos++
    707 }
    708 leaf = advanceLeaf(&stack)
    709 pos = 0
    710 }
    711 return false
    712}
    713
    714// ReverseIterateByOffset calls cb for count entries starting at the offset-th
    715// entry from the end, in descending order. Returns true if stopped early by cb.
    716// The tree must not be modified during iteration (no Set or Remove from the callback).
    717func (t *BPTree) ReverseIterateByOffset(offset int, count int, cb IterCbFn) bool {
    718 if t.root == nil || offset >= t.size || count <= 0 {
    719 return false
    720 }
    721 if offset < 0 {
    722 offset = 0
    723 }
    724
    725 ascIdx := t.size - 1 - offset
    726 leaf, pos, stack := t.descendToOffset(ascIdx)
    727
    728 visited := 0
    729 for leaf != nil && visited < count {
    730 for pos >= 0 && visited < count {
    731 if cb(leaf.keys[pos], *leaf.values[pos]) {
    732 return true
    733 }
    734 visited++
    735 pos--
    736 }
    737 leaf = retreatLeaf(&stack)
    738 if leaf != nil {
    739 pos = len(leaf.keys) - 1
    740 }
    741 }
    742 return false
    743}
    744
    745//----------------------------------------
    746// Descent helpers for iteration
    747
    748// descendToGE descends to the first key >= key, returning the leaf, position, and stack.
    749func (t *BPTree) descendToGE(key string) (*leafNode, int, iterStack) {
    750 var stack iterStack
    751 n := t.root
    752 for !n.isLeaf() {
    753 inner := n.(*innerNode)
    754 idx := inner.findChild(key)
    755 stack = append(stack, pathEntry{inner, idx})
    756 n = inner.children[idx]
    757 }
    758 leaf := n.(*leafNode)
    759 pos, _ := leaf.find(key)
    760 if pos >= len(leaf.keys) {
    761 next := advanceLeaf(&stack)
    762 if next == nil {
    763 return nil, 0, nil
    764 }
    765 return next, 0, stack
    766 }
    767 return leaf, pos, stack
    768}
    769
    770// descendToLE descends to the last key <= key, returning the leaf, position, and stack.
    771func (t *BPTree) descendToLE(key string) (*leafNode, int, iterStack) {
    772 var stack iterStack
    773 n := t.root
    774 for !n.isLeaf() {
    775 inner := n.(*innerNode)
    776 idx := inner.findChild(key)
    777 stack = append(stack, pathEntry{inner, idx})
    778 n = inner.children[idx]
    779 }
    780 leaf := n.(*leafNode)
    781 pos, found := leaf.find(key)
    782 if found {
    783 return leaf, pos, stack
    784 }
    785 if pos > 0 {
    786 return leaf, pos - 1, stack
    787 }
    788 prev := retreatLeaf(&stack)
    789 if prev == nil {
    790 return nil, 0, nil
    791 }
    792 return prev, len(prev.keys) - 1, stack
    793}
    794
    795// descendToOffset descends to the offset-th entry using sizes[],
    796// returning the leaf, position within leaf, and stack.
    797func (t *BPTree) descendToOffset(offset int) (*leafNode, int, iterStack) {
    798 var stack iterStack
    799 n := t.root
    800 rem := offset
    801 for !n.isLeaf() {
    802 inner := n.(*innerNode)
    803 found := false
    804 for i, s := range inner.sizes {
    805 if rem < s {
    806 stack = append(stack, pathEntry{inner, i})
    807 n = inner.children[i]
    808 found = true
    809 break
    810 }
    811 rem -= s
    812 }
    813 if !found {
    814 panic("descendToOffset: offset out of range")
    815 }
    816 }
    817 return n.(*leafNode), rem, stack
    818}
    819