Skip to content

Commit 95bd95c

Browse files
committed
node.forEach methods that allow us to track node positions and extract all nodes IPLD objects (including internal nodes)
1 parent ecc4261 commit 95bd95c

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

node.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,145 @@ func (n *node) forEachAt(ctx context.Context, bs cbor.IpldStore, bitWidth uint,
348348
return nil
349349
}
350350

351+
// Recursive implementation backing ForEach and ForEachAt. Performs a
352+
// depth-first walk of the tree, beginning at the 'start' index. The 'offset'
353+
// argument helps us locate the lateral position of the current node so we can
354+
// figure out the appropriate 'index', since indexes are not stored with values
355+
// and can only be determined by knowing how far a leaf node is removed from
356+
// the left-most leaf node.
357+
// This method also provides the trail of indices at each height/level in the way to the current node, which can be used to formulate a selector suffixes
358+
func (n *node) forEachAtTracked(ctx context.Context, bs cbor.IpldStore, trail []int, bitWidth uint, height int, start, offset uint64, cb func(uint64, *cbg.Deferred, []int) error) error {
359+
if height == 0 {
360+
// height=0 means we're at leaf nodes and get to use our callback
361+
for i, v := range n.values {
362+
if v != nil {
363+
ix := offset + uint64(i)
364+
if ix < start {
365+
// if we're here, 'start' is probably somewhere in the
366+
// middle of this node's elements
367+
continue
368+
}
369+
370+
// use 'offset' to determine the actual index for this element, it
371+
// tells us how distant we are from the left-most leaf node
372+
if err := cb(ix, v, append(trail, i)); err != nil {
373+
return err
374+
}
375+
}
376+
}
377+
378+
return nil
379+
}
380+
381+
subCount := nodesForHeight(bitWidth, height)
382+
for i, ln := range n.links {
383+
if ln == nil {
384+
continue
385+
}
386+
387+
// 'offs' tells us the index of the left-most element of the subtree defined
388+
// by 'sub'
389+
offs := offset + (uint64(i) * subCount)
390+
nextOffs := offs + subCount
391+
// nextOffs > offs checks for overflow at MaxIndex (where the next offset wraps back
392+
// to 0).
393+
if nextOffs >= offs && start >= nextOffs {
394+
// if we're here, 'start' lets us skip this entire sub-tree
395+
continue
396+
}
397+
398+
subn, err := ln.load(ctx, bs, bitWidth, height-1)
399+
if err != nil {
400+
return err
401+
}
402+
403+
// recurse into the child node, providing 'offs' to tell it where it's
404+
// located in the tree
405+
if err := subn.forEachAtTracked(ctx, bs, append(trail, i), bitWidth, height-1, start, offs, cb); err != nil {
406+
return err
407+
}
408+
}
409+
return nil
410+
}
411+
412+
// b *bytes.Buffer, sink func(node ipld.Node) error
413+
// Recursive implementation backing ForEach and ForEachAt. Performs a
414+
// depth-first walk of the tree, beginning at the 'start' index. The 'offset'
415+
// argument helps us locate the lateral position of the current node so we can
416+
// figure out the appropriate 'index', since indexes are not stored with values
417+
// and can only be determined by knowing how far a leaf node is removed from
418+
// the left-most leaf node.
419+
// This method also provides the trail of indices at each height/level in the way to the current node, which can be used to formulate a selector suffixes
420+
func (n *node) forEachAtTrackedWithNodeSink(ctx context.Context, bs cbor.IpldStore, trail []int, bitWidth uint, height int, start, offset uint64, b *bytes.Buffer, sink cbg.CBORUnmarshaler, cb func(uint64, *cbg.Deferred, []int) error) error {
421+
if sink != nil {
422+
if b == nil {
423+
b = bytes.NewBuffer(nil)
424+
}
425+
b.Reset()
426+
internalNode, err := n.compact(ctx, bitWidth, height)
427+
if err != nil {
428+
return err
429+
}
430+
if err := internalNode.MarshalCBOR(b); err != nil {
431+
return err
432+
}
433+
if err := sink.UnmarshalCBOR(b); err != nil {
434+
return err
435+
}
436+
}
437+
if height == 0 {
438+
// height=0 means we're at leaf nodes and get to use our callback
439+
for i, v := range n.values {
440+
if v != nil {
441+
ix := offset + uint64(i)
442+
if ix < start {
443+
// if we're here, 'start' is probably somewhere in the
444+
// middle of this node's elements
445+
continue
446+
}
447+
448+
// use 'offset' to determine the actual index for this element, it
449+
// tells us how distant we are from the left-most leaf node
450+
if err := cb(ix, v, append(trail, i)); err != nil {
451+
return err
452+
}
453+
}
454+
}
455+
456+
return nil
457+
}
458+
459+
subCount := nodesForHeight(bitWidth, height)
460+
for i, ln := range n.links {
461+
if ln == nil {
462+
continue
463+
}
464+
465+
// 'offs' tells us the index of the left-most element of the subtree defined
466+
// by 'sub'
467+
offs := offset + (uint64(i) * subCount)
468+
nextOffs := offs + subCount
469+
// nextOffs > offs checks for overflow at MaxIndex (where the next offset wraps back
470+
// to 0).
471+
if nextOffs >= offs && start >= nextOffs {
472+
// if we're here, 'start' lets us skip this entire sub-tree
473+
continue
474+
}
475+
476+
subn, err := ln.load(ctx, bs, bitWidth, height-1)
477+
if err != nil {
478+
return err
479+
}
480+
481+
// recurse into the child node, providing 'offs' to tell it where it's
482+
// located in the tree
483+
if err := subn.forEachAtTracked(ctx, bs, append(trail, i), bitWidth, height-1, start, offs, cb); err != nil {
484+
return err
485+
}
486+
}
487+
return nil
488+
}
489+
351490
var errNoVals = fmt.Errorf("no values")
352491

353492
// Recursive implementation of FirstSetIndex that's performed on the left-most
@@ -494,6 +633,37 @@ func (n *node) flush(ctx context.Context, bs cbor.IpldStore, bitWidth uint, heig
494633
return nd, nil
495634
}
496635

636+
// compact converts a node into its internal.Node representation
637+
func (n *node) compact(ctx context.Context, bitWidth uint, height int) (*internal.Node, error) {
638+
nd := new(internal.Node)
639+
nd.Bmap = make([]byte, bmapBytes(bitWidth))
640+
641+
if height == 0 {
642+
// leaf node, we're storing values in this node
643+
for i, val := range n.values {
644+
if val == nil {
645+
continue
646+
}
647+
nd.Values = append(nd.Values, val)
648+
// set the bit in the bitmap for this position to indicate its presence
649+
nd.Bmap[i/8] |= 1 << (uint(i) % 8)
650+
}
651+
return nd, nil
652+
}
653+
654+
// non-leaf node, we're only storing Links in this node
655+
for i, ln := range n.links {
656+
if ln == nil {
657+
continue
658+
}
659+
nd.Links = append(nd.Links, ln.cid)
660+
// set the bit in the bitmap for this position to indicate its presence
661+
nd.Bmap[i/8] |= 1 << (uint(i) % 8)
662+
}
663+
664+
return nd, nil
665+
}
666+
497667
func (n *node) setLink(bitWidth uint, i uint64, l *link) {
498668
if n.links == nil {
499669
if l == nil {

0 commit comments

Comments
 (0)