From 0fc78097daa69e908b19fc6905c93808dba31e3a Mon Sep 17 00:00:00 2001 From: mconcat Date: Wed, 13 Oct 2021 17:26:55 +0900 Subject: [PATCH 01/15] move traverse into CPS --- node.go | 122 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 55 deletions(-) diff --git a/node.go b/node.go index bec49eeb8..68abe00df 100644 --- a/node.go +++ b/node.go @@ -452,63 +452,75 @@ func (node *Node) traversePost(t *ImmutableTree, ascending bool, cb func(*Node) }) } -func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, post bool, cb func(*Node, uint8) bool) bool { - if node == nil { - return false - } - afterStart := start == nil || bytes.Compare(start, node.key) < 0 - startOrAfter := start == nil || bytes.Compare(start, node.key) <= 0 - beforeEnd := end == nil || bytes.Compare(node.key, end) < 0 - if inclusive { - beforeEnd = end == nil || bytes.Compare(node.key, end) <= 0 - } - - // Run callback per inner/leaf node. - stop := false - if !post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - stop = cb(node, depth) - if stop { - return stop - } - } +type traversal func() (*Node, uint8, traversal) - if !node.isLeaf() { - if ascending { - // check lower nodes, then higher - if afterStart { - stop = node.getLeftNode(t).traverseInRange(t, start, end, ascending, inclusive, depth+1, post, cb) - } - if stop { - return stop - } - if beforeEnd { - stop = node.getRightNode(t).traverseInRange(t, start, end, ascending, inclusive, depth+1, post, cb) - } - } else { - // check the higher nodes first - if beforeEnd { - stop = node.getRightNode(t).traverseInRange(t, start, end, ascending, inclusive, depth+1, post, cb) - } - if stop { - return stop - } - if afterStart { - stop = node.getLeftNode(t).traverseInRange(t, start, end, ascending, inclusive, depth+1, post, cb) - } - } - } - if stop { - return stop - } - - if post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - stop = cb(node, depth) - if stop { - return stop - } - } +func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, post bool, cb func(*Node, uint8) bool) bool { + stop := false + next := node.traversal(t, start, end, ascending, inclusive, depth, post, true, func() (*Node, uint8, traversal) { return nil, 0, nil }) + var node2 *Node + var depth2 uint8 + for next != nil { + node2, depth2, next = next() + if node2 == nil { + return stop + } + stop = cb(node2, depth2) + if stop { + return stop + } + } + return stop +} - return stop +func (node *Node) traversal(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, post bool, condition bool, continuation traversal) traversal { + return func() (*Node, uint8, traversal) { + if !condition { + if continuation == nil { + return nil, 0, nil + } + return continuation() + } + + if node == nil { + return nil, 0, nil + } + afterStart := start == nil || bytes.Compare(start, node.key) < 0 + startOrAfter := start == nil || bytes.Compare(start, node.key) <= 0 + beforeEnd := end == nil || bytes.Compare(node.key, end) < 0 + if inclusive { + beforeEnd = end == nil || bytes.Compare(node.key, end) <= 0 + } + + inner := func(continuation traversal) traversal { + if !node.isLeaf() { + if ascending { + // check lower nodes, then higher + return node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, + node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, + continuation)) + } else { + // check the higher nodes first + return node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, + node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, + continuation)) + } + } + return continuation + } + + // Run callback per inner/leaf node. + if !post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { + return node, depth, inner(continuation) + } + + if post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { + return inner(func() (*Node, uint8, traversal) { + return node, depth, continuation + })() + } + + return inner(continuation)() + } } // Only used in testing... From 4b30f083f4e08ebb599d07839a20085ef62d74a2 Mon Sep 17 00:00:00 2001 From: mconcat Date: Wed, 13 Oct 2021 21:04:12 +0900 Subject: [PATCH 02/15] add iterator --- immutable_tree.go | 52 +++++++++++++++++++++++++++++++++++++++++++++++ node.go | 15 +++++++++----- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/immutable_tree.go b/immutable_tree.go index d9f16e9a1..2f30b09f6 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -223,3 +223,55 @@ func (t *ImmutableTree) nodeSize() int { }) return size } + +type Iterator struct { + start, end []byte + + key, value []byte + + next traversal +} + +func (t *ImmutableTree) Iterator(start, end []byte) *Iterator { + iter := &Iterator { + start: start, + end: end, + next: t.root.traversal(t, start, end, true, false, 0, false, true, nil), + } + + iter.Next() + return iter +} + +var _ dbm.Iterator = &Iterator{} + +func (iter *Iterator) Domain() ([]byte, []byte) { + return iter.start, iter.end +} + +func (iter *Iterator) Valid() bool { + return iter.next != nil +} + +func (iter *Iterator) Key() []byte { + return iter.key +} + +func (iter *Iterator) Value() []byte { + return iter.value +} + +func (iter *Iterator) Next() { + node, _, next := iter.next() + iter.key, iter.value = node.key, node.value + iter.next = next +} + +func (iter *Iterator) Close() error { + iter.next = nil + return nil +} + +func (iter *Iterator) Error() error { + return nil +} diff --git a/node.go b/node.go index 68abe00df..88693b137 100644 --- a/node.go +++ b/node.go @@ -456,7 +456,7 @@ type traversal func() (*Node, uint8, traversal) func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, post bool, cb func(*Node, uint8) bool) bool { stop := false - next := node.traversal(t, start, end, ascending, inclusive, depth, post, true, func() (*Node, uint8, traversal) { return nil, 0, nil }) + next := node.traversal(t, start, end, ascending, inclusive, depth, post, true, nil) var node2 *Node var depth2 uint8 for next != nil { @@ -514,12 +514,17 @@ func (node *Node) traversal(t *ImmutableTree, start, end []byte, ascending bool, } if post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - return inner(func() (*Node, uint8, traversal) { - return node, depth, continuation - })() + continuation2 := continuation + continuation = func() (*Node, uint8, traversal) { + return node, depth, continuation2 + } } - return inner(continuation)() + next := inner(continuation) + if next == nil { + return nil, 0, nil + } + return next() } } From 82d9a9d4a23b6fad7c4e6bc5cf07a17ba2f05865 Mon Sep 17 00:00:00 2001 From: mconcat Date: Wed, 13 Oct 2021 23:44:38 +0900 Subject: [PATCH 03/15] add ascending --- immutable_tree.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/immutable_tree.go b/immutable_tree.go index 2f30b09f6..9f318bbe2 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -232,11 +232,11 @@ type Iterator struct { next traversal } -func (t *ImmutableTree) Iterator(start, end []byte) *Iterator { +func (t *ImmutableTree) Iterator(start, end []byte, ascending bool) *Iterator { iter := &Iterator { start: start, end: end, - next: t.root.traversal(t, start, end, true, false, 0, false, true, nil), + next: t.root.traversal(t, start, end, ascending, false, 0, false, true, nil), } iter.Next() From bd8137a572a88e447776e0a28aeb2a7eac3e715e Mon Sep 17 00:00:00 2001 From: mconcat Date: Thu, 14 Oct 2021 00:23:15 +0900 Subject: [PATCH 04/15] fix iter.valid --- immutable_tree.go | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/immutable_tree.go b/immutable_tree.go index 9f318bbe2..cc6401ed5 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -166,12 +166,23 @@ func (t *ImmutableTree) Iterate(fn func(key []byte, value []byte) bool) (stopped if t.root == nil { return false } + iter := t.Iterator(nil, nil, true) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + stopped = fn(iter.Key(), iter.Value()) + if stopped { + return stopped + } + } + return stopped + /* return t.root.traverse(t, true, func(node *Node) bool { if node.height == 0 { return fn(node.key, node.value) } return false }) + */ } // IterateRange makes a callback for all nodes with key between start and end non-inclusive. @@ -229,6 +240,8 @@ type Iterator struct { key, value []byte + valid bool + next traversal } @@ -236,6 +249,7 @@ func (t *ImmutableTree) Iterator(start, end []byte, ascending bool) *Iterator { iter := &Iterator { start: start, end: end, + valid: true, next: t.root.traversal(t, start, end, ascending, false, 0, false, true, nil), } @@ -250,7 +264,7 @@ func (iter *Iterator) Domain() ([]byte, []byte) { } func (iter *Iterator) Valid() bool { - return iter.next != nil + return iter.valid } func (iter *Iterator) Key() []byte { @@ -262,13 +276,24 @@ func (iter *Iterator) Value() []byte { } func (iter *Iterator) Next() { + if iter.next == nil { + iter.valid = false + return + } node, _, next := iter.next() - iter.key, iter.value = node.key, node.value iter.next = next + + if node.height == 0 { + iter.key, iter.value = node.key, node.value + return + } + + iter.Next() } func (iter *Iterator) Close() error { iter.next = nil + iter.valid = false return nil } From a06214cc05caf53ddc52b2e34858460e700208ab Mon Sep 17 00:00:00 2001 From: mconcat Date: Thu, 14 Oct 2021 00:27:02 +0900 Subject: [PATCH 05/15] fix test --- immutable_tree.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/immutable_tree.go b/immutable_tree.go index cc6401ed5..d0e901ac3 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -283,6 +283,10 @@ func (iter *Iterator) Next() { node, _, next := iter.next() iter.next = next + if node == nil { + iter.valid = false + return + } if node.height == 0 { iter.key, iter.value = node.key, node.value return From 688d385fe3615d1ce2d333b444d64935e7994e50 Mon Sep 17 00:00:00 2001 From: mconcat Date: Fri, 15 Oct 2021 23:56:39 +0900 Subject: [PATCH 06/15] gofmt --- immutable_tree.go | 108 ++++++++++++++++++------------------ node.go | 136 +++++++++++++++++++++++----------------------- 2 files changed, 122 insertions(+), 122 deletions(-) diff --git a/immutable_tree.go b/immutable_tree.go index d0e901ac3..ae901db7f 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -166,23 +166,23 @@ func (t *ImmutableTree) Iterate(fn func(key []byte, value []byte) bool) (stopped if t.root == nil { return false } - iter := t.Iterator(nil, nil, true) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - stopped = fn(iter.Key(), iter.Value()) - if stopped { - return stopped - } - } - return stopped - /* - return t.root.traverse(t, true, func(node *Node) bool { - if node.height == 0 { - return fn(node.key, node.value) + iter := t.Iterator(nil, nil, true) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + stopped = fn(iter.Key(), iter.Value()) + if stopped { + return stopped } - return false - }) - */ + } + return stopped + /* + return t.root.traverse(t, true, func(node *Node) bool { + if node.height == 0 { + return fn(node.key, node.value) + } + return false + }) + */ } // IterateRange makes a callback for all nodes with key between start and end non-inclusive. @@ -236,71 +236,71 @@ func (t *ImmutableTree) nodeSize() int { } type Iterator struct { - start, end []byte + start, end []byte - key, value []byte + key, value []byte - valid bool + valid bool - next traversal + next traversal } func (t *ImmutableTree) Iterator(start, end []byte, ascending bool) *Iterator { - iter := &Iterator { - start: start, - end: end, - valid: true, - next: t.root.traversal(t, start, end, ascending, false, 0, false, true, nil), - } - - iter.Next() - return iter + iter := &Iterator{ + start: start, + end: end, + valid: true, + next: t.root.traversal(t, start, end, ascending, false, 0, false, true, nil), + } + + iter.Next() + return iter } var _ dbm.Iterator = &Iterator{} func (iter *Iterator) Domain() ([]byte, []byte) { - return iter.start, iter.end + return iter.start, iter.end } func (iter *Iterator) Valid() bool { - return iter.valid + return iter.valid } func (iter *Iterator) Key() []byte { - return iter.key + return iter.key } func (iter *Iterator) Value() []byte { - return iter.value + return iter.value } func (iter *Iterator) Next() { - if iter.next == nil { - iter.valid = false - return - } - node, _, next := iter.next() - iter.next = next - - if node == nil { - iter.valid = false - return - } - if node.height == 0 { - iter.key, iter.value = node.key, node.value - return - } - - iter.Next() + if iter.next == nil { + iter.valid = false + return + } + node, _, next := iter.next() + iter.next = next + + if node == nil { + iter.valid = false + return + } + if node.height == 0 { + iter.key, iter.value = node.key, node.value + return + } + + iter.Next() } func (iter *Iterator) Close() error { - iter.next = nil - iter.valid = false - return nil + iter.next = nil + iter.valid = false + return nil } func (iter *Iterator) Error() error { - return nil + return nil } diff --git a/node.go b/node.go index 88693b137..cc2921903 100644 --- a/node.go +++ b/node.go @@ -455,77 +455,77 @@ func (node *Node) traversePost(t *ImmutableTree, ascending bool, cb func(*Node) type traversal func() (*Node, uint8, traversal) func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, post bool, cb func(*Node, uint8) bool) bool { - stop := false - next := node.traversal(t, start, end, ascending, inclusive, depth, post, true, nil) - var node2 *Node - var depth2 uint8 - for next != nil { - node2, depth2, next = next() - if node2 == nil { - return stop - } - stop = cb(node2, depth2) - if stop { - return stop - } - } - return stop + stop := false + next := node.traversal(t, start, end, ascending, inclusive, depth, post, true, nil) + var node2 *Node + var depth2 uint8 + for next != nil { + node2, depth2, next = next() + if node2 == nil { + return stop + } + stop = cb(node2, depth2) + if stop { + return stop + } + } + return stop } func (node *Node) traversal(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, post bool, condition bool, continuation traversal) traversal { - return func() (*Node, uint8, traversal) { - if !condition { - if continuation == nil { - return nil, 0, nil - } - return continuation() - } - - if node == nil { - return nil, 0, nil - } - afterStart := start == nil || bytes.Compare(start, node.key) < 0 - startOrAfter := start == nil || bytes.Compare(start, node.key) <= 0 - beforeEnd := end == nil || bytes.Compare(node.key, end) < 0 - if inclusive { - beforeEnd = end == nil || bytes.Compare(node.key, end) <= 0 - } - - inner := func(continuation traversal) traversal { - if !node.isLeaf() { - if ascending { - // check lower nodes, then higher - return node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, - node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, - continuation)) - } else { - // check the higher nodes first - return node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, - node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, - continuation)) - } - } - return continuation - } - - // Run callback per inner/leaf node. - if !post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - return node, depth, inner(continuation) - } - - if post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - continuation2 := continuation - continuation = func() (*Node, uint8, traversal) { - return node, depth, continuation2 - } - } - - next := inner(continuation) - if next == nil { - return nil, 0, nil - } - return next() - } + return func() (*Node, uint8, traversal) { + if !condition { + if continuation == nil { + return nil, 0, nil + } + return continuation() + } + + if node == nil { + return nil, 0, nil + } + afterStart := start == nil || bytes.Compare(start, node.key) < 0 + startOrAfter := start == nil || bytes.Compare(start, node.key) <= 0 + beforeEnd := end == nil || bytes.Compare(node.key, end) < 0 + if inclusive { + beforeEnd = end == nil || bytes.Compare(node.key, end) <= 0 + } + + inner := func(continuation traversal) traversal { + if !node.isLeaf() { + if ascending { + // check lower nodes, then higher + return node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, + node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, + continuation)) + } else { + // check the higher nodes first + return node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, + node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, + continuation)) + } + } + return continuation + } + + // Run callback per inner/leaf node. + if !post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { + return node, depth, inner(continuation) + } + + if post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { + continuation2 := continuation + continuation = func() (*Node, uint8, traversal) { + return node, depth, continuation2 + } + } + + next := inner(continuation) + if next == nil { + return nil, 0, nil + } + return next() + } } // Only used in testing... From 84c41c2d1a992108b775e46e80cb46bf852804af Mon Sep 17 00:00:00 2001 From: mconcat Date: Mon, 18 Oct 2021 02:49:07 +0900 Subject: [PATCH 07/15] simplify logicl, add comments --- immutable_tree.go | 4 --- node.go | 88 ++++++++++++++++++++++++----------------------- 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/immutable_tree.go b/immutable_tree.go index ae901db7f..ab0c376c5 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -283,10 +283,6 @@ func (iter *Iterator) Next() { node, _, next := iter.next() iter.next = next - if node == nil { - iter.valid = false - return - } if node.height == 0 { iter.key, iter.value = node.key, node.value return diff --git a/node.go b/node.go index cc2921903..15a023328 100644 --- a/node.go +++ b/node.go @@ -473,59 +473,61 @@ func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending } func (node *Node) traversal(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, post bool, condition bool, continuation traversal) traversal { - return func() (*Node, uint8, traversal) { - if !condition { - if continuation == nil { - return nil, 0, nil - } - return continuation() - } + if node == nil { + return continuation + } - if node == nil { - return nil, 0, nil - } - afterStart := start == nil || bytes.Compare(start, node.key) < 0 - startOrAfter := start == nil || bytes.Compare(start, node.key) <= 0 - beforeEnd := end == nil || bytes.Compare(node.key, end) < 0 - if inclusive { - beforeEnd = end == nil || bytes.Compare(node.key, end) <= 0 - } + if !condition { + return continuation + } + + afterStart := start == nil || bytes.Compare(start, node.key) < 0 + startOrAfter := start == nil || bytes.Compare(start, node.key) <= 0 + beforeEnd := end == nil || bytes.Compare(node.key, end) < 0 + if inclusive { + beforeEnd = end == nil || bytes.Compare(node.key, end) <= 0 + } - inner := func(continuation traversal) traversal { - if !node.isLeaf() { - if ascending { - // check lower nodes, then higher - return node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, - node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, - continuation)) - } else { - // check the higher nodes first - return node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, - node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, - continuation)) - } + inner := func(continuation traversal) traversal { + if !node.isLeaf() { + if ascending { + // check lower nodes, then higher + return node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, + node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, + continuation)) + } else { + // check the higher nodes first + return node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, + node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, + continuation)) } - return continuation } + return continuation + } - // Run callback per inner/leaf node. - if !post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - return node, depth, inner(continuation) - } + // Run callback per inner/leaf node. - if post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - continuation2 := continuation - continuation = func() (*Node, uint8, traversal) { - return node, depth, continuation2 - } + // case of preorder traversal. + // traversal for this node is first visited and then proceed to inner() + if !post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { + return func() (*Node, uint8, traversal) { + return node, depth, inner(continuation) } + } - next := inner(continuation) - if next == nil { - return nil, 0, nil + // case of postorder traversal. + // traversal for this node is captured in the continuation and + // passed into inner() + if post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { + return func() (*Node, uint8, traversal) { + return inner(func() (*Node, uint8, traversal) { + return node, depth, continuation + })() } - return next() } + + // ignore this node, keep traverse on children. + return inner(continuation) } // Only used in testing... From 1261f145a78171c7c9b44a1cb02e78e39cdd7c25 Mon Sep 17 00:00:00 2001 From: mconcat Date: Tue, 19 Oct 2021 17:05:54 +0900 Subject: [PATCH 08/15] rm using iterator inside iavl --- immutable_tree.go | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/immutable_tree.go b/immutable_tree.go index ab0c376c5..4f2877233 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -166,23 +166,12 @@ func (t *ImmutableTree) Iterate(fn func(key []byte, value []byte) bool) (stopped if t.root == nil { return false } - iter := t.Iterator(nil, nil, true) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - stopped = fn(iter.Key(), iter.Value()) - if stopped { - return stopped + return t.root.traverse(t, true, func(node *Node) bool { + if node.height == 0 { + return fn(node.key, node.value) } - } - return stopped - /* - return t.root.traverse(t, true, func(node *Node) bool { - if node.height == 0 { - return fn(node.key, node.value) - } - return false - }) - */ + return false + }) } // IterateRange makes a callback for all nodes with key between start and end non-inclusive. From a33fa2e01412d4e223f6c8f55acb31e0e1d611a6 Mon Sep 17 00:00:00 2001 From: mconcat Date: Tue, 19 Oct 2021 17:38:29 +0900 Subject: [PATCH 09/15] add detailed comments --- node.go | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/node.go b/node.go index 15a023328..19befa35b 100644 --- a/node.go +++ b/node.go @@ -461,9 +461,6 @@ func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending var depth2 uint8 for next != nil { node2, depth2, next = next() - if node2 == nil { - return stop - } stop = cb(node2, depth2) if stop { return stop @@ -472,6 +469,8 @@ func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending return stop } +// `traversal` returns the delayed execution of recursive traveral in order to give the caller the control of the execution flow. +// The caller can either call the next traversal to proceed, or simply discard the function variable to stop iteration. func (node *Node) traversal(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, post bool, condition bool, continuation traversal) traversal { if node == nil { return continuation @@ -491,43 +490,59 @@ func (node *Node) traversal(t *ImmutableTree, start, end []byte, ascending bool, inner := func(continuation traversal) traversal { if !node.isLeaf() { if ascending { - // check lower nodes, then higher + // if node is a branch node and the order is ascending, then; + // return the traversal for the left nodes, which will then proceed on the continuation of; + // the traversal for the right nodes, which will then proceed on the continuation of; + // the delayed traversal for the parent nodes and their children(which is passed as an argument) return node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, continuation)) } else { - // check the higher nodes first + // if node is a branch node and the order is not ascending, then; + // return the traversal for the right nodes, which will then proceed on the continuation of; + // the traversal for the left nodes, which will then proceed on the continuation of; + // the delayed traversal for the parent nodes and their children(which is passed as an argument) return node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, continuation)) } } + // if node is not a branch node, we don't care about it in this closure. return continuation } // Run callback per inner/leaf node. // case of preorder traversal. - // traversal for this node is first visited and then proceed to inner() if !post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { return func() (*Node, uint8, traversal) { - return node, depth, inner(continuation) + // return the traversal for this node, which will then proceed on the continuation of; + return node, depth, + // the traversal for the inner nodes, which will then proceed on the continuation of; + inner( + // the delayed traversal for the parent nodes and their children(which is passed as an argument) + continuation, + ) } } // case of postorder traversal. - // traversal for this node is captured in the continuation and - // passed into inner() if post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { return func() (*Node, uint8, traversal) { + // return the traversal for the inner nodes, which will then proceed on the continuation of; return inner(func() (*Node, uint8, traversal) { - return node, depth, continuation + // the traversal for this node, which will then proceed on the continuation of; + return node, depth, + // the delayed traversal for the parent nodes and their children(which is passed as an argument) + continuation })() } } - // ignore this node, keep traverse on children. - return inner(continuation) + // ignore this node, keep traverse on children and then proceed to delayed continuation. + return inner( + continuation, + ) } // Only used in testing... From 45a7cc291db397a6048c80d18640700b79f66d50 Mon Sep 17 00:00:00 2001 From: mconcat Date: Wed, 20 Oct 2021 02:31:45 +0900 Subject: [PATCH 10/15] add comments per review --- immutable_tree.go | 2 ++ node.go | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/immutable_tree.go b/immutable_tree.go index 4f2877233..2d4b05b08 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -231,6 +231,8 @@ type Iterator struct { valid bool + // next is the argument for retrieving the sequence starting from the next element. + // By calling next manually, Iterator can control the execution flow. next traversal } diff --git a/node.go b/node.go index 19befa35b..f21e55552 100644 --- a/node.go +++ b/node.go @@ -471,7 +471,16 @@ func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending // `traversal` returns the delayed execution of recursive traveral in order to give the caller the control of the execution flow. // The caller can either call the next traversal to proceed, or simply discard the function variable to stop iteration. -func (node *Node) traversal(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, post bool, condition bool, continuation traversal) traversal { +// The condition is to determine whether this traversal is required or not, depending on the caller is included in the search +// range or not. +func (node *Node) traversal( + t *ImmutableTree, + start, end []byte, ascending bool, inclusive bool, + depth uint8, + post bool, + condition bool, + continuation traversal, +) traversal { if node == nil { return continuation } @@ -492,18 +501,18 @@ func (node *Node) traversal(t *ImmutableTree, start, end []byte, ascending bool, if ascending { // if node is a branch node and the order is ascending, then; // return the traversal for the left nodes, which will then proceed on the continuation of; - // the traversal for the right nodes, which will then proceed on the continuation of; - // the delayed traversal for the parent nodes and their children(which is passed as an argument) return node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, + // the traversal for the right nodes, which will then proceed on the continuation of; node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, + // the delayed traversal for the parent nodes and their children(which is passed as an argument) continuation)) } else { // if node is a branch node and the order is not ascending, then; // return the traversal for the right nodes, which will then proceed on the continuation of; - // the traversal for the left nodes, which will then proceed on the continuation of; - // the delayed traversal for the parent nodes and their children(which is passed as an argument) return node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, + // the traversal for the left nodes, which will then proceed on the continuation of; node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, + // the delayed traversal for the parent nodes and their children(which is passed as an argument) continuation)) } } From cc81b59a1c1989a18c9730af394119d60fbb3c78 Mon Sep 17 00:00:00 2001 From: mconcat Date: Thu, 21 Oct 2021 02:26:08 +0900 Subject: [PATCH 11/15] modify to closure independent --- immutable_tree.go | 16 ++--- node.go | 168 ++++++++++++++++++++++++---------------------- proof_range.go | 4 +- 3 files changed, 98 insertions(+), 90 deletions(-) diff --git a/immutable_tree.go b/immutable_tree.go index 2d4b05b08..386181f46 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -181,7 +181,7 @@ func (t *ImmutableTree) IterateRange(start, end []byte, ascending bool, fn func( if t.root == nil { return false } - return t.root.traverseInRange(t, start, end, ascending, false, 0, false, func(node *Node, _ uint8) bool { + return t.root.traverseInRange(t, start, end, ascending, false, false, func(node *Node) bool { if node.height == 0 { return fn(node.key, node.value) } @@ -196,7 +196,7 @@ func (t *ImmutableTree) IterateRangeInclusive(start, end []byte, ascending bool, if t.root == nil { return false } - return t.root.traverseInRange(t, start, end, ascending, true, 0, false, func(node *Node, _ uint8) bool { + return t.root.traverseInRange(t, start, end, ascending, true, false, func(node *Node) bool { if node.height == 0 { return fn(node.key, node.value, node.version) } @@ -233,7 +233,7 @@ type Iterator struct { // next is the argument for retrieving the sequence starting from the next element. // By calling next manually, Iterator can control the execution flow. - next traversal + t *traversal } func (t *ImmutableTree) Iterator(start, end []byte, ascending bool) *Iterator { @@ -241,7 +241,7 @@ func (t *ImmutableTree) Iterator(start, end []byte, ascending bool) *Iterator { start: start, end: end, valid: true, - next: t.root.traversal(t, start, end, ascending, false, 0, false, true, nil), + t: t.root.newTraversal(t, start, end, ascending, false, false), } iter.Next() @@ -267,12 +267,12 @@ func (iter *Iterator) Value() []byte { } func (iter *Iterator) Next() { - if iter.next == nil { + node := iter.t.next() + if node == nil { + iter.t = nil iter.valid = false return } - node, _, next := iter.next() - iter.next = next if node.height == 0 { iter.key, iter.value = node.key, node.value @@ -283,7 +283,7 @@ func (iter *Iterator) Next() { } func (iter *Iterator) Close() error { - iter.next = nil + iter.t = nil iter.valid = false return nil } diff --git a/node.go b/node.go index f21e55552..4cdf3d77b 100644 --- a/node.go +++ b/node.go @@ -440,28 +440,49 @@ func (node *Node) calcBalance(t *ImmutableTree) int { // traverse is a wrapper over traverseInRange when we want the whole tree func (node *Node) traverse(t *ImmutableTree, ascending bool, cb func(*Node) bool) bool { - return node.traverseInRange(t, nil, nil, ascending, false, 0, false, func(node *Node, depth uint8) bool { + return node.traverseInRange(t, nil, nil, ascending, false, false, func(node *Node) bool { return cb(node) }) } // traversePost is a wrapper over traverseInRange when we want the whole tree post-order func (node *Node) traversePost(t *ImmutableTree, ascending bool, cb func(*Node) bool) bool { - return node.traverseInRange(t, nil, nil, ascending, false, 0, true, func(node *Node, depth uint8) bool { + return node.traverseInRange(t, nil, nil, ascending, false, true, func(node *Node) bool { return cb(node) }) } -type traversal func() (*Node, uint8, traversal) +type traversal struct { + tree *ImmutableTree + start, end []byte + ascending bool + inclusive bool + post bool + delayedNodes []delayedNode +} + +func (node *Node) newTraversal(tree *ImmutableTree, start, end []byte, ascending bool, inclusive bool, post bool) *traversal { + return &traversal{ + tree: tree, + start: start, + end: end, + ascending: ascending, + inclusive: inclusive, + post: post, + delayedNodes: []delayedNode{{node, true}}, + } +} -func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, post bool, cb func(*Node, uint8) bool) bool { +type delayedNode struct { + node *Node + delayed bool +} + +func (node *Node) traverseInRange(tree *ImmutableTree, start, end []byte, ascending bool, inclusive bool, post bool, cb func(*Node) bool) bool { stop := false - next := node.traversal(t, start, end, ascending, inclusive, depth, post, true, nil) - var node2 *Node - var depth2 uint8 - for next != nil { - node2, depth2, next = next() - stop = cb(node2, depth2) + t := node.newTraversal(tree, start, end, ascending, inclusive, post) + for node2 := t.next(); node2 != nil; node2 = t.next() { + stop = cb(node2) if stop { return stop } @@ -473,85 +494,72 @@ func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending // The caller can either call the next traversal to proceed, or simply discard the function variable to stop iteration. // The condition is to determine whether this traversal is required or not, depending on the caller is included in the search // range or not. -func (node *Node) traversal( - t *ImmutableTree, - start, end []byte, ascending bool, inclusive bool, - depth uint8, - post bool, - condition bool, - continuation traversal, -) traversal { - if node == nil { - return continuation - } - - if !condition { - return continuation - } - - afterStart := start == nil || bytes.Compare(start, node.key) < 0 - startOrAfter := start == nil || bytes.Compare(start, node.key) <= 0 - beforeEnd := end == nil || bytes.Compare(node.key, end) < 0 - if inclusive { - beforeEnd = end == nil || bytes.Compare(node.key, end) <= 0 - } - - inner := func(continuation traversal) traversal { - if !node.isLeaf() { - if ascending { - // if node is a branch node and the order is ascending, then; - // return the traversal for the left nodes, which will then proceed on the continuation of; - return node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, - // the traversal for the right nodes, which will then proceed on the continuation of; - node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, - // the delayed traversal for the parent nodes and their children(which is passed as an argument) - continuation)) - } else { - // if node is a branch node and the order is not ascending, then; - // return the traversal for the right nodes, which will then proceed on the continuation of; - return node.getRightNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, beforeEnd, - // the traversal for the left nodes, which will then proceed on the continuation of; - node.getLeftNode(t).traversal(t, start, end, ascending, inclusive, depth+1, post, afterStart, - // the delayed traversal for the parent nodes and their children(which is passed as an argument) - continuation)) - } - } - // if node is not a branch node, we don't care about it in this closure. - return continuation +func (t *traversal) next() *Node { + if len(t.delayedNodes) == 0 { + // end of traversal + return nil } - // Run callback per inner/leaf node. + delayed := t.delayedNodes[0].delayed + node := t.delayedNodes[0].node + t.delayedNodes = t.delayedNodes[1:] - // case of preorder traversal. - if !post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - return func() (*Node, uint8, traversal) { - // return the traversal for this node, which will then proceed on the continuation of; - return node, depth, - // the traversal for the inner nodes, which will then proceed on the continuation of; - inner( - // the delayed traversal for the parent nodes and their children(which is passed as an argument) - continuation, - ) - } + if !delayed { + return node + } + + if node == nil { + return node + } + + afterStart := t.start == nil || bytes.Compare(t.start, node.key) < 0 + startOrAfter := t.start == nil || bytes.Compare(t.start, node.key) <= 0 + beforeEnd := t.end == nil || bytes.Compare(node.key, t.end) < 0 + if t.inclusive { + beforeEnd = t.end == nil || bytes.Compare(node.key, t.end) <= 0 } // case of postorder traversal. - if post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - return func() (*Node, uint8, traversal) { - // return the traversal for the inner nodes, which will then proceed on the continuation of; - return inner(func() (*Node, uint8, traversal) { - // the traversal for this node, which will then proceed on the continuation of; - return node, depth, - // the delayed traversal for the parent nodes and their children(which is passed as an argument) - continuation - })() + if t.post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { + // set the continuation to first traverse the inner nodes, and then the currnet node as non-delayed, + // and then the existing delayed nodes. + t.delayedNodes = append([]delayedNode{{node, false}}, t.delayedNodes...) + } + + if !node.isLeaf() { + newDelays := []delayedNode{} + // if node is a branch node and the order is ascending, then; + if t.ascending { + if afterStart { + // push the delayed traversal for the left nodes, + newDelays = append(newDelays, delayedNode{node.getLeftNode(t.tree), true}) + } + if beforeEnd { + // push the delayed traversal for the right nodes, + newDelays = append(newDelays, delayedNode{node.getRightNode(t.tree), true}) + } + } else { + // if node is a branch node and the order is not ascending, then; + if beforeEnd { + // push the delayed traversal for the right nodes, + newDelays = append(newDelays, delayedNode{node.getRightNode(t.tree), true}) + } + if afterStart { + // push the delayed traversal for the left nodes, + newDelays = append(newDelays, delayedNode{node.getLeftNode(t.tree), true}) + } } + // then push the delayed traversal for the parent nodes and their children(which is passed as an argument) + t.delayedNodes = append(newDelays, t.delayedNodes...) + } + + // case of preorder traversal. + if !t.post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { + // set the continuation to first traverse the inner nodes, and then the existing delayed nodes. + return node } - // ignore this node, keep traverse on children and then proceed to delayed continuation. - return inner( - continuation, - ) + return t.next() } // Only used in testing... diff --git a/proof_range.go b/proof_range.go index 3514ffd70..5666148a4 100644 --- a/proof_range.go +++ b/proof_range.go @@ -433,8 +433,8 @@ func (t *ImmutableTree) getRangeProof(keyStart, keyEnd []byte, limit int) (proof var leafCount = 1 // from left above. var pathCount = 0 - t.root.traverseInRange(t, afterLeft, nil, true, false, 0, false, - func(node *Node, depth uint8) (stop bool) { + t.root.traverseInRange(t, afterLeft, nil, true, false, false, + func(node *Node) (stop bool) { // Track when we diverge from path, or when we've exhausted path, // since the first allPathToLeafs shouldn't include it. From 257c545462cce9572bc86a12c31d54ce2b001a79 Mon Sep 17 00:00:00 2001 From: mconcat Date: Mon, 25 Oct 2021 22:32:27 +0900 Subject: [PATCH 12/15] apply review, add docs, separate iterator.go --- immutable_tree.go | 68 -------------- iterator.go | 219 ++++++++++++++++++++++++++++++++++++++++++++++ node.go | 98 --------------------- 3 files changed, 219 insertions(+), 166 deletions(-) create mode 100644 iterator.go diff --git a/immutable_tree.go b/immutable_tree.go index 386181f46..f2c20298e 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -223,71 +223,3 @@ func (t *ImmutableTree) nodeSize() int { }) return size } - -type Iterator struct { - start, end []byte - - key, value []byte - - valid bool - - // next is the argument for retrieving the sequence starting from the next element. - // By calling next manually, Iterator can control the execution flow. - t *traversal -} - -func (t *ImmutableTree) Iterator(start, end []byte, ascending bool) *Iterator { - iter := &Iterator{ - start: start, - end: end, - valid: true, - t: t.root.newTraversal(t, start, end, ascending, false, false), - } - - iter.Next() - return iter -} - -var _ dbm.Iterator = &Iterator{} - -func (iter *Iterator) Domain() ([]byte, []byte) { - return iter.start, iter.end -} - -func (iter *Iterator) Valid() bool { - return iter.valid -} - -func (iter *Iterator) Key() []byte { - return iter.key -} - -func (iter *Iterator) Value() []byte { - return iter.value -} - -func (iter *Iterator) Next() { - node := iter.t.next() - if node == nil { - iter.t = nil - iter.valid = false - return - } - - if node.height == 0 { - iter.key, iter.value = node.key, node.value - return - } - - iter.Next() -} - -func (iter *Iterator) Close() error { - iter.t = nil - iter.valid = false - return nil -} - -func (iter *Iterator) Error() error { - return nil -} diff --git a/iterator.go b/iterator.go new file mode 100644 index 000000000..fef034d2f --- /dev/null +++ b/iterator.go @@ -0,0 +1,219 @@ +package iavl + +// NOTE: This file favors int64 as opposed to int for size/counts. +// The Tree on the other hand favors int. This is intentional. + +import ( + "bytes" + + dbm "github.com/tendermint/tm-db" +) + +type traversal struct { + tree *ImmutableTree + start, end []byte // iteration domain + ascending bool // ascending traversal + inclusive bool // end key inclusiveness + post bool // postorder traversal + delayedNodes *delayedNodes // delayed nodes to be traversed +} + +func (node *Node) newTraversal(tree *ImmutableTree, start, end []byte, ascending bool, inclusive bool, post bool) *traversal { + return &traversal{ + tree: tree, + start: start, + end: end, + ascending: ascending, + inclusive: inclusive, + post: post, + delayedNodes: &delayedNodes{{node, true}}, // set initial traverse to the node + } +} + +// delayedNode represents the delayed iteration on the nodes. +// When delayed is set to true, the delayedNode should be expanded, and their +// children should be traversed. When delayed is set to false, the delayedNode is +// already have expanded, and it could be immediately returned. +type delayedNode struct { + node *Node + delayed bool +} + +type delayedNodes []delayedNode + +func (nodes *delayedNodes) pop() (*Node, bool) { + node := (*nodes)[len(*nodes)-1] + *nodes = (*nodes)[:len(*nodes)-1] + return node.node, node.delayed +} + +func (nodes *delayedNodes) push(node *Node, delayed bool) { + *nodes = append(*nodes, delayedNode{node, delayed}) +} + +func (nodes *delayedNodes) length() int { + return len(*nodes) +} + +// `traversal` returns the delayed execution of recursive traversal on a tree. +// `traversal` has `delayedNodes`, which is an array of `delayedNode`. +// +// At the each step of `next`, the `delayedNodes` can have one of the three states: +// 1. It has length of 0, meaning that their is no more traversable nodes. +// 2. It has length of 1, meaning that the traverse is being started from the initial node. +// 3. It has length of 2>=, meaning that there are delayed nodes to be traversed. +// +// When the `delayedNodes` are not empty, `next` retrieves the first `delayedNode` and initially check: +// 1. If it is not an delayed node (delayedNodes[0].delayed == false) it immediately returns it. +// +// A. If the `delayedNodes[0]` is a branch node: +// 1. If the traversal is postorder, then prepend the current node to the t.delayedNodes, +// with `delayed` set to false. This makes the current node returned *after* all the children +// are traversed, without being expanded. +// 2. Prepend the traversable children nodes into the `delayedNodes`, with `delayed` set to true. This +// makes the children nodes to be traversed, and expanded with their respective children. +// 3. If the traversal is preorder, (with the children already pushed to the `delayedNodes`), +// returns the current node. +// 4. Call `traversal.next()` to further traverse through the `delayedNodes`. +// +// B. If the `delayedNodes[0]` is a leaf node, it will be returned without expand, by the following process: +// 1. If the traversal is postorder, the current node will be prepended to the `delayedNodes` with `delayed` +// set to false, and immediately returned at the subsequent call of `traversal.next()` at the last line. +// 2. If the traversal is preorder, the current node will be returned. +func (t *traversal) next() *Node { + // End of traversal. + if t.delayedNodes.length() == 0 { + return nil + } + + node, delayed := t.delayedNodes.pop() + + // Already expanded, immediately return. + if !delayed || node == nil { + return node + } + + afterStart := t.start == nil || bytes.Compare(t.start, node.key) < 0 + startOrAfter := afterStart || bytes.Compare(t.start, node.key) == 0 + beforeEnd := t.end == nil || bytes.Compare(node.key, t.end) < 0 + if t.inclusive { + beforeEnd = beforeEnd || bytes.Compare(node.key, t.end) == 0 + } + + // case of postorder. A-1 and B-1 + if t.post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { + t.delayedNodes.push(node, false) + } + + // case of branch node, traversing children. A-2. + if !node.isLeaf() { + // if node is a branch node and the order is ascending, then; + if t.ascending { + if beforeEnd { + // push the delayed traversal for the right nodes, + t.delayedNodes.push(node.getRightNode(t.tree), true) + } + if afterStart { + // push the delayed traversal for the left nodes, + t.delayedNodes.push(node.getLeftNode(t.tree), true) + } + } else { + // if node is a branch node and the order is not ascending, then; + if afterStart { + // push the delayed traversal for the left nodes, + t.delayedNodes.push(node.getLeftNode(t.tree), true) + } + if beforeEnd { + // push the delayed traversal for the right nodes, + t.delayedNodes.push(node.getRightNode(t.tree), true) + } + + } + } + + // case of preorder traversal. A-3 and B-2. + if !t.post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { + return node + } + + // Keep traversing and expanding the remaning delayed nodes. A-4. + return t.next() +} + +// Iterator is a dbm.Iterator for ImmutableTree +type Iterator struct { + start, end []byte + + key, value []byte + + valid bool + + t *traversal +} + +func (t *ImmutableTree) Iterator(start, end []byte, ascending bool) *Iterator { + iter := &Iterator{ + start: start, + end: end, + valid: true, + t: t.root.newTraversal(t, start, end, ascending, false, false), + } + + iter.Next() + return iter +} + +var _ dbm.Iterator = &Iterator{} + +// Domain implements dbm.Iterator. +func (iter *Iterator) Domain() ([]byte, []byte) { + return iter.start, iter.end +} + +// Valid implements dbm.Iterator. +func (iter *Iterator) Valid() bool { + return iter.valid +} + +// Key implements dbm.Iterator +func (iter *Iterator) Key() []byte { + return iter.key +} + +// Value implements dbm.Iterator +func (iter *Iterator) Value() []byte { + return iter.value +} + +// Next implements dbm.Iterator +func (iter *Iterator) Next() { + if iter.t == nil { + return + } + + node := iter.t.next() + if node == nil { + iter.t = nil + iter.valid = false + return + } + + if node.height == 0 { + iter.key, iter.value = node.key, node.value + return + } + + iter.Next() +} + +// Close implements dbm.Iterator +func (iter *Iterator) Close() error { + iter.t = nil + iter.valid = false + return nil +} + +// Error implements dbm.Iterator +func (iter *Iterator) Error() error { + return nil +} diff --git a/node.go b/node.go index 4cdf3d77b..7c4f96dde 100644 --- a/node.go +++ b/node.go @@ -452,32 +452,6 @@ func (node *Node) traversePost(t *ImmutableTree, ascending bool, cb func(*Node) }) } -type traversal struct { - tree *ImmutableTree - start, end []byte - ascending bool - inclusive bool - post bool - delayedNodes []delayedNode -} - -func (node *Node) newTraversal(tree *ImmutableTree, start, end []byte, ascending bool, inclusive bool, post bool) *traversal { - return &traversal{ - tree: tree, - start: start, - end: end, - ascending: ascending, - inclusive: inclusive, - post: post, - delayedNodes: []delayedNode{{node, true}}, - } -} - -type delayedNode struct { - node *Node - delayed bool -} - func (node *Node) traverseInRange(tree *ImmutableTree, start, end []byte, ascending bool, inclusive bool, post bool, cb func(*Node) bool) bool { stop := false t := node.newTraversal(tree, start, end, ascending, inclusive, post) @@ -490,78 +464,6 @@ func (node *Node) traverseInRange(tree *ImmutableTree, start, end []byte, ascend return stop } -// `traversal` returns the delayed execution of recursive traveral in order to give the caller the control of the execution flow. -// The caller can either call the next traversal to proceed, or simply discard the function variable to stop iteration. -// The condition is to determine whether this traversal is required or not, depending on the caller is included in the search -// range or not. -func (t *traversal) next() *Node { - if len(t.delayedNodes) == 0 { - // end of traversal - return nil - } - - delayed := t.delayedNodes[0].delayed - node := t.delayedNodes[0].node - t.delayedNodes = t.delayedNodes[1:] - - if !delayed { - return node - } - - if node == nil { - return node - } - - afterStart := t.start == nil || bytes.Compare(t.start, node.key) < 0 - startOrAfter := t.start == nil || bytes.Compare(t.start, node.key) <= 0 - beforeEnd := t.end == nil || bytes.Compare(node.key, t.end) < 0 - if t.inclusive { - beforeEnd = t.end == nil || bytes.Compare(node.key, t.end) <= 0 - } - - // case of postorder traversal. - if t.post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - // set the continuation to first traverse the inner nodes, and then the currnet node as non-delayed, - // and then the existing delayed nodes. - t.delayedNodes = append([]delayedNode{{node, false}}, t.delayedNodes...) - } - - if !node.isLeaf() { - newDelays := []delayedNode{} - // if node is a branch node and the order is ascending, then; - if t.ascending { - if afterStart { - // push the delayed traversal for the left nodes, - newDelays = append(newDelays, delayedNode{node.getLeftNode(t.tree), true}) - } - if beforeEnd { - // push the delayed traversal for the right nodes, - newDelays = append(newDelays, delayedNode{node.getRightNode(t.tree), true}) - } - } else { - // if node is a branch node and the order is not ascending, then; - if beforeEnd { - // push the delayed traversal for the right nodes, - newDelays = append(newDelays, delayedNode{node.getRightNode(t.tree), true}) - } - if afterStart { - // push the delayed traversal for the left nodes, - newDelays = append(newDelays, delayedNode{node.getLeftNode(t.tree), true}) - } - } - // then push the delayed traversal for the parent nodes and their children(which is passed as an argument) - t.delayedNodes = append(newDelays, t.delayedNodes...) - } - - // case of preorder traversal. - if !t.post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { - // set the continuation to first traverse the inner nodes, and then the existing delayed nodes. - return node - } - - return t.next() -} - // Only used in testing... func (node *Node) lmd(t *ImmutableTree) *Node { if node.isLeaf() { From 6d8132ca5a421e43c254913e0efbcfb90f3ba313 Mon Sep 17 00:00:00 2001 From: mconcat Date: Tue, 26 Oct 2021 01:44:55 +0900 Subject: [PATCH 13/15] fix lint --- iterator.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/iterator.go b/iterator.go index fef034d2f..ec9f113fa 100644 --- a/iterator.go +++ b/iterator.go @@ -64,20 +64,20 @@ func (nodes *delayedNodes) length() int { // 3. It has length of 2>=, meaning that there are delayed nodes to be traversed. // // When the `delayedNodes` are not empty, `next` retrieves the first `delayedNode` and initially check: -// 1. If it is not an delayed node (delayedNodes[0].delayed == false) it immediately returns it. +// 1. If it is not an delayed node (node.delayed == false) it immediately returns it. // -// A. If the `delayedNodes[0]` is a branch node: -// 1. If the traversal is postorder, then prepend the current node to the t.delayedNodes, +// A. If the `node` is a branch node: +// 1. If the traversal is postorder, then append the current node to the t.delayedNodes, // with `delayed` set to false. This makes the current node returned *after* all the children // are traversed, without being expanded. -// 2. Prepend the traversable children nodes into the `delayedNodes`, with `delayed` set to true. This +// 2. Append the traversable children nodes into the `delayedNodes`, with `delayed` set to true. This // makes the children nodes to be traversed, and expanded with their respective children. -// 3. If the traversal is preorder, (with the children already pushed to the `delayedNodes`), -// returns the current node. +// 3. If the traversal is preorder, (with the children to be traversed already pushed to the +// `delayedNodes`), returns the current node. // 4. Call `traversal.next()` to further traverse through the `delayedNodes`. // -// B. If the `delayedNodes[0]` is a leaf node, it will be returned without expand, by the following process: -// 1. If the traversal is postorder, the current node will be prepended to the `delayedNodes` with `delayed` +// B. If the `node` is a leaf node, it will be returned without expand, by the following process: +// 1. If the traversal is postorder, the current node will be append to the `delayedNodes` with `delayed` // set to false, and immediately returned at the subsequent call of `traversal.next()` at the last line. // 2. If the traversal is preorder, the current node will be returned. func (t *traversal) next() *Node { @@ -94,10 +94,10 @@ func (t *traversal) next() *Node { } afterStart := t.start == nil || bytes.Compare(t.start, node.key) < 0 - startOrAfter := afterStart || bytes.Compare(t.start, node.key) == 0 + startOrAfter := afterStart || bytes.Equal(t.start, node.key) beforeEnd := t.end == nil || bytes.Compare(node.key, t.end) < 0 if t.inclusive { - beforeEnd = beforeEnd || bytes.Compare(node.key, t.end) == 0 + beforeEnd = beforeEnd || bytes.Equal(node.key, t.end) } // case of postorder. A-1 and B-1 @@ -127,7 +127,6 @@ func (t *traversal) next() *Node { // push the delayed traversal for the right nodes, t.delayedNodes.push(node.getRightNode(t.tree), true) } - } } From 6cd99024a68d3d9e4f98acf02a24cbb2bab51b53 Mon Sep 17 00:00:00 2001 From: mconcat Date: Tue, 26 Oct 2021 01:47:21 +0900 Subject: [PATCH 14/15] fix fix lint --- iterator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterator.go b/iterator.go index ec9f113fa..f36fad029 100644 --- a/iterator.go +++ b/iterator.go @@ -72,7 +72,7 @@ func (nodes *delayedNodes) length() int { // are traversed, without being expanded. // 2. Append the traversable children nodes into the `delayedNodes`, with `delayed` set to true. This // makes the children nodes to be traversed, and expanded with their respective children. -// 3. If the traversal is preorder, (with the children to be traversed already pushed to the +// 3. If the traversal is preorder, (with the children to be traversed already pushed to the // `delayedNodes`), returns the current node. // 4. Call `traversal.next()` to further traverse through the `delayedNodes`. // From e806696fd6997215f0a01785c2296274ae274392 Mon Sep 17 00:00:00 2001 From: mconcat Date: Tue, 2 Nov 2021 14:16:46 +0000 Subject: [PATCH 15/15] add more docs --- iterator.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/iterator.go b/iterator.go index f36fad029..bd69fcd42 100644 --- a/iterator.go +++ b/iterator.go @@ -56,7 +56,13 @@ func (nodes *delayedNodes) length() int { } // `traversal` returns the delayed execution of recursive traversal on a tree. -// `traversal` has `delayedNodes`, which is an array of `delayedNode`. +// +// `traversal` will traverse the tree in a depth-first manner. To handle locating +// the next element, and to handle unwinding, the traversal maintains its future +// iteration under `delayedNodes`. At each call of `next()`, it will retrieve the +// next element from the `delayedNodes` and acts accordingly. The `next()` itself +// defines how to unwind the delayed nodes stack. The caller can either call the +// next traversal to proceed, or simply discard the `traversal` struct to stop iteration. // // At the each step of `next`, the `delayedNodes` can have one of the three states: // 1. It has length of 0, meaning that their is no more traversable nodes. @@ -101,13 +107,15 @@ func (t *traversal) next() *Node { } // case of postorder. A-1 and B-1 + // Recursively process left sub-tree, then right-subtree, then node itself. if t.post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { t.delayedNodes.push(node, false) } // case of branch node, traversing children. A-2. if !node.isLeaf() { - // if node is a branch node and the order is ascending, then; + // if node is a branch node and the order is ascending, + // We traverse through the left subtree, then the right subtree. if t.ascending { if beforeEnd { // push the delayed traversal for the right nodes, @@ -118,7 +126,8 @@ func (t *traversal) next() *Node { t.delayedNodes.push(node.getLeftNode(t.tree), true) } } else { - // if node is a branch node and the order is not ascending, then; + // if node is a branch node and the order is not ascending + // We traverse through the right subtree, then the left subtree. if afterStart { // push the delayed traversal for the left nodes, t.delayedNodes.push(node.getLeftNode(t.tree), true) @@ -131,6 +140,7 @@ func (t *traversal) next() *Node { } // case of preorder traversal. A-3 and B-2. + // Process root then (recursively) processing left child, then process right child if !t.post && (!node.isLeaf() || (startOrAfter && beforeEnd)) { return node }