-
Notifications
You must be signed in to change notification settings - Fork 3
CNDB-15669: implement mayHaveContent metadata flag #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: CNDB-15669
Are you sure you want to change the base?
Changes from all commits
d381439
b04f905
13c9f55
5e3fdb7
fc08e14
f0e7739
d546b17
7949190
2c64c6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -137,6 +137,12 @@ interface Cursor<T> | |
| /// Used for sets and ranges to correctly define the range states for branch-inclusive ranges. | ||
| long ON_RETURN_PATH_BIT = 1L << 19; | ||
|
|
||
| /// Mask for the bits used for flags that should not affect position comparison. | ||
| long FLAGS_MASK = 0xFFFFL; | ||
|
|
||
| /// Flag indicating whether this position may have content. | ||
| long MAY_HAVE_CONTENT_BIT = 1L; | ||
|
|
||
| /// Mask of the transition bits including the direction. We apply xor with this value to form a position in the | ||
| /// reverse direction. | ||
| long TRANSITION_MASK = 0x8FFL << TRANSITION_SHIFT; | ||
|
|
@@ -198,9 +204,37 @@ static boolean isOnReturnPath(long encodedPosition) | |
| static long compare(long encoded1, long encoded2) | ||
| { | ||
| // This can support depth of 2^31 - 1 without overflowing. | ||
| return encoded1 - encoded2; | ||
| // Normalise the flag bits to the same value in both operands before subtracting. | ||
| return (encoded1 | FLAGS_MASK) - (encoded2 | FLAGS_MASK); | ||
| } | ||
|
|
||
|
|
||
| /// Returns a new position with flags from pos2 combined using union operation. | ||
| /// Union: (pos1 | pos2) & flags | (pos1 & ~flags) | ||
| /// This preserves the structural bits (depth, transition) from pos1 and combines the specified flags from both positions. | ||
| static long unionFlags(long pos1, long pos2, long flags) | ||
| { | ||
| return (pos1 | pos2) & flags | (pos1 & ~flags); | ||
| } | ||
|
|
||
| /// Returns a new position with flags from pos2 combined using union operation. | ||
| /// This version is optimized for cases where the position bits (depth and incoming transition) are known to match. | ||
| /// The assertion validates this invariant in debug builds. | ||
| static long unionFlagsMatchingPositions(long pos1, long pos2) | ||
| { | ||
| assert compare(pos1, pos2) == 0 : | ||
| String.format("Position mismatch in unionFlagsMatchingPositions: compare(%016x, %016x) = %d", pos1, pos2, compare(pos1, pos2)); | ||
| return pos1 | pos2; | ||
| } | ||
|
|
||
| /// Returns a new position with flags from pos2 combined using intersection operation. | ||
| /// Intersection: pos1 & pos2 & flags | (pos1 & ~flags) | ||
| static long intersectionFlags(long pos1, long pos2, long flags) | ||
| { | ||
| return pos1 & pos2 & flags | (pos1 & ~flags); | ||
| } | ||
|
|
||
|
|
||
| static long rootPosition(Direction direction) | ||
| { | ||
| return direction.select(ROOT_POSITION_FORWARD, ROOT_POSITION_REVERSE); | ||
|
|
@@ -225,7 +259,7 @@ static long exhaustedPosition(long prevPosition) | |
|
|
||
| static boolean isRootPosition(long encodedPosition) | ||
| { | ||
| return encodedPosition == ROOT_POSITION_FORWARD || encodedPosition == ROOT_POSITION_REVERSE; | ||
| return compare(encodedPosition, ROOT_POSITION_FORWARD) == 0 || compare(encodedPosition, ROOT_POSITION_REVERSE) == 0; | ||
| } | ||
|
|
||
| static long encode(int depth, int transition, Direction direction) | ||
|
|
@@ -252,7 +286,7 @@ static long positionForDescentWithByte(long encodedPosition, int incomingByte) | |
| /// returned encoded position is a valid `skipTo` position for the current state. | ||
| static long positionForSkippingBranch(long encodedBranchPosition) | ||
| { | ||
| return encodedBranchPosition + (1L << TRANSITION_SHIFT); | ||
| return (encodedBranchPosition & ~FLAGS_MASK) + (1L << TRANSITION_SHIFT); | ||
| } | ||
|
|
||
| /// Returns true if the given `currPosition` as returned by `advance`, `advanceMultiple` or `skipTo` is the result | ||
|
|
@@ -267,10 +301,11 @@ static boolean ascended(long currPosition, long prevPosition) | |
|
|
||
| static String toString(long encodedPosition) | ||
| { | ||
| return String.format("depth %d incomingTransition %02x%s %s", | ||
| return String.format("depth %d incomingTransition %02x%s%s %s", | ||
| depth(encodedPosition), | ||
| incomingTransition(encodedPosition), | ||
| isOnReturnPath(encodedPosition) ? "↑" : " ", | ||
| (encodedPosition & MAY_HAVE_CONTENT_BIT) != 0 ? "C" : " ", | ||
| direction(encodedPosition)); | ||
| } | ||
|
|
||
|
|
@@ -361,9 +396,12 @@ default T advanceToContent(ResettingTransitionsReceiver receiver) | |
| if (isOnReturnPath(currPosition)) | ||
| receiver.onReturnPath(); | ||
| } | ||
| T content = content(); | ||
| if (content != null) | ||
| return content; | ||
| if ((currPosition & MAY_HAVE_CONTENT_BIT) != 0) | ||
| { | ||
| T content = content(); | ||
| if (content != null) | ||
| return content; | ||
| } | ||
| prevPosition = currPosition; | ||
| } | ||
| } | ||
|
|
@@ -406,7 +444,8 @@ default boolean descendAlong(ByteSource bytes) | |
| while (next != ByteSource.END_OF_STREAM) | ||
| { | ||
| long nextPosition = positionForDescentWithByte(position, next); | ||
| if (compare(skipTo(nextPosition), nextPosition) != 0) | ||
| long arrived = skipTo(nextPosition); | ||
| if (compare(arrived, nextPosition) != 0) | ||
| return false; | ||
| next = bytes.next(); | ||
| position = nextPosition; | ||
|
|
@@ -463,7 +502,8 @@ interface Walker<T, R> extends Cursor.ResettingTransitionsReceiver | |
| default <R> R process(Cursor.Walker<? super T, R> walker) | ||
| { | ||
| assertFresh(); | ||
| T content = content(); // handle content on the root node | ||
| long currentPosition = encodedPosition(); | ||
| T content = (currentPosition & MAY_HAVE_CONTENT_BIT) != 0 ? content() : null; | ||
| if (content == null) | ||
| content = advanceToContent(walker); | ||
|
|
||
|
|
@@ -487,7 +527,8 @@ default <R> R process(Cursor.Walker<? super T, R> walker) | |
| default <R> R processSkippingBranches(Cursor.Walker<? super T, R> walker) | ||
| { | ||
| assertFresh(); | ||
| T content = content(); // handle content on the root node | ||
| long currentPosition = encodedPosition(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about making |
||
| T content = (currentPosition & MAY_HAVE_CONTENT_BIT) != 0 ? content() : null; | ||
| if (content != null) | ||
| { | ||
| walker.content(content); | ||
|
|
@@ -504,7 +545,7 @@ default <R> R processSkippingBranches(Cursor.Walker<? super T, R> walker) | |
| break; | ||
| walker.resetPathLength(depth(current) - 1); | ||
| walker.addPathByte(incomingTransition(current)); | ||
| content = content(); | ||
| content = (current & MAY_HAVE_CONTENT_BIT) != 0 ? content() : null; | ||
| if (content == null) | ||
| content = advanceToContent(walker); | ||
| } | ||
|
|
@@ -541,7 +582,7 @@ public ByteComparable.Version byteComparableVersion() | |
| @Override | ||
| public Cursor<T> tailCursor(Direction direction) | ||
| { | ||
| assert position == Cursor.rootPosition(direction) : "tailTrie called on exhausted cursor"; | ||
| assert compare(position, Cursor.rootPosition(direction)) == 0 : "tailTrie called on exhausted cursor"; | ||
| return new Empty<>(direction, byteComparableVersion); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,15 +87,21 @@ default <R> R process(DeletionAwareWalker<? super T, ? super D, R> walker) | |
|
|
||
| while (true) | ||
| { | ||
| T content = content(); // handle content on the root node | ||
| if (content != null) | ||
| walker.content(content); | ||
| // Always check for deletion branches as they are independent of content | ||
| RangeCursor<D> deletionBranch = deletionBranchCursor(direction()); | ||
| if (deletionBranch != null && walker.enterDeletionsBranch()) | ||
| { | ||
| processDeletionBranch(walker, deletionBranch); | ||
| walker.exitDeletionsBranch(); | ||
| } | ||
|
|
||
| // MAY_HAVE_CONTENT_BIT optimization: only call content() if flag indicates potential content | ||
| if ((currentPosition & MAY_HAVE_CONTENT_BIT) != 0) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's a good idea for the deletion branch to be presented before content that covers it. Dumps, for one, look weird with this order. |
||
| { | ||
| T content = content(); | ||
| if (content != null) | ||
| walker.content(content); | ||
| } | ||
|
|
||
| long prevPosition = currentPosition; | ||
| currentPosition = advanceMultiple(walker); | ||
|
|
@@ -113,7 +119,7 @@ default <R> R process(DeletionAwareWalker<? super T, ? super D, R> walker) | |
| private static <D> void processDeletionBranch(DeletionAwareWalker<?, ? super D, ?> walker, Cursor<D> cursor) | ||
| { | ||
| cursor.assertFresh(); | ||
| D content = cursor.content(); // handle content on the root node | ||
| D content = (cursor.encodedPosition() & MAY_HAVE_CONTENT_BIT) != 0 ? cursor.content() : null; | ||
| if (content == null) | ||
| content = cursor.advanceToContent(walker); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be able to put this in a static
Cursor.content(cursor, cursorPosition)method without changing the performance.We can also add a line in the
content()javadoc to say it's preferable to get this via the static. If you prefer, call itCursor.checkFlagAndGetContentor something similar.