Skip to content

Commit f34debd

Browse files
author
Nicolas Laurent
committed
implement RopeOperations#hashForRange non-recursively
1 parent 35f1444 commit f34debd

File tree

1 file changed

+77
-59
lines changed

1 file changed

+77
-59
lines changed

src/main/java/org/truffleruby/core/rope/RopeOperations.java

Lines changed: 77 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -538,74 +538,92 @@ private static int computeLoopCount(int offset, int times, int length, int patte
538538

539539
@TruffleBoundary
540540
public static int hashForRange(Rope rope, int startingHashCode, int offset, int length) {
541-
if (rope instanceof LeafRope) {
542-
return Hashing.stringHash(rope.getBytes(), startingHashCode, offset, length);
543-
} else if (rope instanceof SubstringRope) {
544-
final SubstringRope substringRope = (SubstringRope) rope;
545-
546-
return hashForRange(
547-
substringRope.getChild(),
548-
startingHashCode,
549-
offset + substringRope.getByteOffset(),
550-
length);
551-
} else if (rope instanceof ConcatRope) {
552-
final ConcatRope concatRope = (ConcatRope) rope;
553-
final Rope left = concatRope.getLeft();
554-
final Rope right = concatRope.getRight();
555-
556-
int hash = startingHashCode;
557-
final int leftLength = left.byteLength();
541+
class Params {
542+
final Rope rope;
543+
final int startingHashCode;
544+
final int offset;
545+
final int length;
546+
final boolean readResult;
547+
final int repeatCount;
548+
549+
Params(Rope rope, int startingHashCode, int offset, int length, boolean readResult, int repeatCount) {
550+
this.rope = rope;
551+
this.startingHashCode = startingHashCode;
552+
this.offset = offset;
553+
this.length = length;
554+
this.readResult = readResult;
555+
this.repeatCount = repeatCount;
556+
}
557+
}
558558

559-
if (offset < leftLength) {
560-
// The left branch might not be large enough to extract the full hash code we want. In that case,
561-
// we'll extract what we can and extract the difference from the right side.
562-
if (offset + length > leftLength) {
563-
final int coveredByLeft = leftLength - offset;
564-
hash = hashForRange(left, hash, offset, coveredByLeft);
565-
hash = hashForRange(right, hash, 0, length - coveredByLeft);
559+
final Deque<Params> workStack = new ArrayDeque<>();
560+
workStack.push(new Params(rope, startingHashCode, offset, length, false, -1));
561+
int resultHash = 0;
566562

567-
return hash;
563+
while (!workStack.isEmpty()) {
564+
final Params params = workStack.pop();
565+
rope = params.rope;
566+
startingHashCode = params.readResult ? resultHash : params.startingHashCode;
567+
offset = params.offset;
568+
length = params.length;
569+
final byte[] bytes = rope.getRawBytes();
570+
571+
if (bytes != null) {
572+
resultHash = Hashing.stringHash(bytes, startingHashCode, offset, length);
573+
} else if (rope instanceof SubstringRope) {
574+
final SubstringRope substringRope = (SubstringRope) rope;
575+
workStack.push(
576+
new Params(
577+
substringRope.getChild(),
578+
startingHashCode,
579+
offset + substringRope.getByteOffset(),
580+
length,
581+
false,
582+
-1));
583+
} else if (rope instanceof ConcatRope) {
584+
final ConcatRope concatRope = (ConcatRope) rope;
585+
final Rope left = concatRope.getLeft();
586+
final Rope right = concatRope.getRight();
587+
final int leftLength = left.byteLength();
588+
589+
if (offset >= leftLength) {
590+
// range fully contained in right child
591+
workStack.push(new Params(right, startingHashCode, offset - leftLength, length, false, -1));
592+
} else if (offset + length <= leftLength) {
593+
// range fully contained in left child
594+
workStack.push(new Params(left, startingHashCode, offset, length, false, -1));
568595
} else {
569-
return hashForRange(left, hash, offset, length);
596+
final int coveredByLeft = leftLength - offset;
597+
// push right node first, starting hash is the result from the left node
598+
workStack.push(new Params(right, 0, 0, length - coveredByLeft, true, -1));
599+
workStack.push(new Params(left, startingHashCode, offset, coveredByLeft, false, -1));
570600
}
571-
}
572-
573-
return hashForRange(right, hash, offset - leftLength, length);
574-
} else if (rope instanceof RepeatingRope) {
575-
final RepeatingRope repeatingRope = (RepeatingRope) rope;
576-
final Rope child = repeatingRope.getChild();
577-
578-
int bytesToHash = length;
579-
final int patternLength = child.byteLength();
580-
581-
// Fix the offset to be appropriate for a given child. The offset is reset the first time it is
582-
// consumed, so there's no need to worry about adversely affecting anything by adjusting it here.
583-
offset %= child.byteLength();
601+
} else if (rope instanceof RepeatingRope) {
602+
final RepeatingRope repeatingRope = (RepeatingRope) rope;
603+
final Rope child = repeatingRope.getChild();
604+
final int patternLength = child.byteLength();
584605

585-
final int loopCount = computeLoopCount(offset, repeatingRope.getTimes(), bytesToHash, patternLength);
606+
int count = params.repeatCount;
607+
assert count != 0;
608+
if (count < 0) {
609+
offset %= patternLength; // the offset is always 0 when count > 0
610+
count = computeLoopCount(offset, repeatingRope.getTimes(), length, patternLength);
611+
}
586612

587-
int hash = startingHashCode;
588-
for (int i = 0; i < loopCount; i++) {
589-
final int bytesToHashThisPass = bytesToHash + offset > patternLength
613+
if (count > 1) {
614+
// loop - 1 iteration, reset offset to 0, starting hash is the result from previous iteration
615+
workStack.push(new Params(rope, 0, 0, length - patternLength, true, count - 1));
616+
}
617+
// one iteration
618+
final int bytesToHashThisPass = length + offset > patternLength
590619
? patternLength - offset
591-
: bytesToHash;
592-
hash = hashForRange(
593-
child,
594-
hash,
595-
offset,
596-
bytesToHashThisPass);
597-
offset = 0;
598-
bytesToHash -= bytesToHashThisPass;
620+
: length;
621+
workStack.push(new Params(child, startingHashCode, offset, bytesToHashThisPass, false, -1));
622+
} else {
623+
resultHash = Hashing.stringHash(rope.getBytes(), startingHashCode, offset, length);
599624
}
600-
601-
return hash;
602-
} else if (rope instanceof LazyIntRope) {
603-
return Hashing.stringHash(rope.getBytes(), startingHashCode, offset, length);
604-
} else if (rope instanceof NativeRope) {
605-
return Hashing.stringHash(rope.getBytes(), startingHashCode, offset, length);
606-
} else {
607-
throw new RuntimeException("Hash code not supported for rope of type: " + rope.getClass().getName());
608625
}
626+
return resultHash;
609627
}
610628

611629
public static RopeBuilder toRopeBuilderCopy(Rope rope) {

0 commit comments

Comments
 (0)