refactor: Unify upperBound and lowerBound into boundHelper - #7943
Draft
bthomee wants to merge 1 commit into
Draft
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Refactors SHAMap bound traversal logic by consolidating the duplicated upperBound/lowerBound implementations into a single parameterized helper, and expands traversal test coverage to include empty and single-item maps.
Changes:
- Introduces
SHAMap::boundHelper(id, direction)to unify predecessor/successor search behavior. - Re-implements
upperBoundandlowerBoundas thin wrappers overboundHelper. - Adds traversal tests covering empty-map and single-item-map bound behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/libxrpl/shamap/SHAMap.cpp |
Collapses duplicated bound-walk logic into boundHelper and delegates upperBound/lowerBound to it. |
include/xrpl/shamap/SHAMap.h |
Declares the new private boundHelper used by bound queries. |
src/tests/libxrpl/shamap/SHAMap.cpp |
Adds empty/single-item bound tests to cover previously unexercised traversal branches. |
Suppressed comments (1)
src/tests/libxrpl/shamap/SHAMap.cpp:451
- This comment claims boundHelper’s leaf branch decides the outcome “before root_'s own inner-node scan would ever run”, but for probes equal to the single key the leaf comparison fails and boundHelper pops the leaf then scans the root (which then falls through to end()). This matters for understanding why upperBound(key)/lowerBound(key) return end().
// it was constructed with, with the single leaf one level below it. So here the stack holds
// that inner root plus the leaf, and boundHelper's leaf branch, examined first, decides the
// outcome before root_'s own inner-node scan would ever run.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+428
to
+430
| // The root is a childless inner node, so boundHelper's inner-node branch must scan all 16 | ||
| // branches, find every one empty, and fall through to end() rather than dereference a child. | ||
| EXPECT_EQ(map.upperBound(uint256{}), map.end()); |
bthomee
force-pushed
the
bthomee/shamap-stack-05-derive-traversal-node-ids
branch
from
August 3, 2026 23:25
199fe0d to
651f082
Compare
bthomee
force-pushed
the
bthomee/shamap-stack-06-unify-upper-lower-bound
branch
from
August 3, 2026 23:25
cfd2adf to
c208afa
Compare
bthomee
force-pushed
the
bthomee/shamap-stack-05-derive-traversal-node-ids
branch
from
August 4, 2026 00:12
651f082 to
fcbd9fe
Compare
bthomee
force-pushed
the
bthomee/shamap-stack-06-unify-upper-lower-bound
branch
from
August 4, 2026 00:12
c208afa to
dc9f746
Compare
The two functions were near duplicates: walk to the key, then look for the nearest leaf on one side. Only the scan direction, the comparison deciding a leaf qualifies, and whether to take the first or last leaf below the subtree differed, exactly the distinction `BelowDirection` already draws for `belowHelper`, so the pair collapse into one parameterised walk. Also drops the stale `// TODO: what to return here?` above `lowerBound`'s `return end()`: no predecessor is the correct answer for the smallest key, and the tests pin it. Existing coverage only exercised `boundHelper`'s inner-node branch, every test map had at least three items, so the root was always an inner node and the leaf branch at the top of the function was never reached with a real answer to give. Adds coverage for a single-item map, where the leaf branch alone decides the outcome, and an empty map, where the scan must find nothing on every branch before falling through to `end()`. Fixes the single-item test's own comment, which claimed `root_` becomes a leaf, when in fact `root_` stays the inner node it was constructed with for any map built via `addItem`; only a single-item map synced from a peer (`addRootNode`) ever replaces `root_` with a leaf directly.
bthomee
force-pushed
the
bthomee/shamap-stack-05-derive-traversal-node-ids
branch
from
August 4, 2026 00:49
fcbd9fe to
6318ca4
Compare
bthomee
force-pushed
the
bthomee/shamap-stack-06-unify-upper-lower-bound
branch
from
August 4, 2026 00:49
dc9f746 to
8022ede
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 6/8 of a stack. Base: part 5 (
bthomee/shamap-stack-05-derive-traversal-node-ids).The two functions were near duplicates: walk to the key, then look for the
nearest leaf on one side. Only the scan direction, the comparison deciding a
leaf qualifies, and whether to take the first or last leaf below the subtree
differed, exactly the distinction
BelowDirectionalready draws forbelowHelper, so the pair collapse into one parameterised walk. Also dropsthe stale
// TODO: what to return here?abovelowerBound'sreturn end():no predecessor is the correct answer for the smallest key, and the tests pin
it.
Existing coverage only exercised
boundHelper's inner-node branch, every testmap had at least three items, so the root was always an inner node and the
leaf branch at the top of the function was never reached with a real answer to
give. Adds coverage for a single-item map, where the leaf branch alone decides
the outcome, and an empty map, where the scan must find nothing on every
branch before falling through to
end().Fixes the single-item test's own comment, which claimed
root_becomes aleaf, when in fact
root_stays the inner node it was constructed with forany map built via
addItem; only a single-item map synced from a peer(
addRootNode) ever replacesroot_with a leaf directly.