@@ -538,74 +538,92 @@ private static int computeLoopCount(int offset, int times, int length, int patte
538
538
539
539
@ TruffleBoundary
540
540
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
+ }
558
558
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 ;
566
562
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 ));
568
595
} 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 ));
570
600
}
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 ();
584
605
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
+ }
586
612
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
590
619
? 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 );
599
624
}
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 ());
608
625
}
626
+ return resultHash ;
609
627
}
610
628
611
629
public static RopeBuilder toRopeBuilderCopy (Rope rope ) {
0 commit comments