@@ -497,6 +497,36 @@ public static byte[] flattenBytes(Rope rope) {
497
497
return buffer ;
498
498
}
499
499
500
+ /** Used to implement {@link Rope#getByteSlow(int)} in a non-recursive fashion for some Rope subclasses. Do not call
501
+ * directly, call {@link Rope#getByteSlow(int) instead.} */
502
+ @ TruffleBoundary
503
+ static byte getByteSlow (Rope rope , int index ) {
504
+ while (true ) {
505
+ final byte [] rawBytes = rope .getRawBytes ();
506
+ if (rawBytes != null ) {
507
+ return rawBytes [index ];
508
+ } else if (rope instanceof ConcatRope ) {
509
+ final ConcatRope concatRope = (ConcatRope ) rope ;
510
+ final Rope left = concatRope .getLeft ();
511
+ if (index < left .byteLength ()) {
512
+ rope = left ;
513
+ } else {
514
+ rope = concatRope .getRight ();
515
+ index -= left .byteLength ();
516
+ }
517
+ } else if (rope instanceof SubstringRope ) {
518
+ final SubstringRope substringRope = (SubstringRope ) rope ;
519
+ rope = substringRope .getChild ();
520
+ index += substringRope .getByteOffset ();
521
+ } else if (rope instanceof RepeatingRope ) {
522
+ rope = ((RepeatingRope ) rope ).getChild ();
523
+ index %= rope .byteLength ();
524
+ } else {
525
+ return rope .getByteSlow (index );
526
+ }
527
+ }
528
+ }
529
+
500
530
private static int computeLoopCount (int offset , int times , int length , int patternLength ) {
501
531
// The loopCount has to be precisely determined so every repetition has at least some parts used.
502
532
// It has to account for the beginning we don't need (offset), has to reach the end but, and must not
@@ -508,74 +538,92 @@ private static int computeLoopCount(int offset, int times, int length, int patte
508
538
509
539
@ TruffleBoundary
510
540
public static int hashForRange (Rope rope , int startingHashCode , int offset , int length ) {
511
- if ( rope instanceof LeafRope ) {
512
- return Hashing . stringHash ( rope . getBytes (), startingHashCode , offset , length ) ;
513
- } else if ( rope instanceof SubstringRope ) {
514
- final SubstringRope substringRope = ( SubstringRope ) rope ;
515
-
516
- return hashForRange (
517
- substringRope . getChild (),
518
- startingHashCode ,
519
- offset + substringRope . getByteOffset (),
520
- length ) ;
521
- } else if ( rope instanceof ConcatRope ) {
522
- final ConcatRope concatRope = ( ConcatRope ) rope ;
523
- final Rope left = concatRope . getLeft () ;
524
- final Rope right = concatRope . getRight () ;
525
-
526
- int hash = startingHashCode ;
527
- 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
+ }
528
558
529
- if (offset < leftLength ) {
530
- // The left branch might not be large enough to extract the full hash code we want. In that case,
531
- // we'll extract what we can and extract the difference from the right side.
532
- if (offset + length > leftLength ) {
533
- final int coveredByLeft = leftLength - offset ;
534
- hash = hashForRange (left , hash , offset , coveredByLeft );
535
- 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 ;
536
562
537
- 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 ));
538
595
} else {
539
- 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 ));
540
600
}
541
- }
542
-
543
- return hashForRange (right , hash , offset - leftLength , length );
544
- } else if (rope instanceof RepeatingRope ) {
545
- final RepeatingRope repeatingRope = (RepeatingRope ) rope ;
546
- final Rope child = repeatingRope .getChild ();
547
-
548
- int bytesToHash = length ;
549
- final int patternLength = child .byteLength ();
550
-
551
- // Fix the offset to be appropriate for a given child. The offset is reset the first time it is
552
- // consumed, so there's no need to worry about adversely affecting anything by adjusting it here.
553
- 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 ();
554
605
555
- 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
+ }
556
612
557
- int hash = startingHashCode ;
558
- for (int i = 0 ; i < loopCount ; i ++) {
559
- 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
560
619
? patternLength - offset
561
- : bytesToHash ;
562
- hash = hashForRange (
563
- child ,
564
- hash ,
565
- offset ,
566
- bytesToHashThisPass );
567
- offset = 0 ;
568
- 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 );
569
624
}
570
-
571
- return hash ;
572
- } else if (rope instanceof LazyIntRope ) {
573
- return Hashing .stringHash (rope .getBytes (), startingHashCode , offset , length );
574
- } else if (rope instanceof NativeRope ) {
575
- return Hashing .stringHash (rope .getBytes (), startingHashCode , offset , length );
576
- } else {
577
- throw new RuntimeException ("Hash code not supported for rope of type: " + rope .getClass ().getName ());
578
625
}
626
+ return resultHash ;
579
627
}
580
628
581
629
public static RopeBuilder toRopeBuilderCopy (Rope rope ) {
0 commit comments