|
39 | 39 | import org.truffleruby.collections.IntStack;
|
40 | 40 | import org.truffleruby.core.Hashing;
|
41 | 41 | import org.truffleruby.core.encoding.EncodingManager;
|
| 42 | +import org.truffleruby.core.rope.ConcatRope.ConcatChildren; |
42 | 43 | import org.truffleruby.core.rope.RopeNodesFactory.WithEncodingNodeGen;
|
43 | 44 | import org.truffleruby.core.string.StringAttributes;
|
44 | 45 | import org.truffleruby.core.string.StringOperations;
|
@@ -384,28 +385,36 @@ public static byte[] flattenBytes(Rope rope) {
|
384 | 385 | if (current instanceof ConcatRope) {
|
385 | 386 | final ConcatRope concatRope = (ConcatRope) current;
|
386 | 387 |
|
| 388 | + final ConcatChildren children = concatRope.getChildrenOrNull(); |
| 389 | + if (children == null) { |
| 390 | + // The rope got concurrently flattened between entering the iteration and reaching here, |
| 391 | + // restart the iteration from the top. |
| 392 | + workStack.push(concatRope); |
| 393 | + continue; |
| 394 | + } |
| 395 | + |
387 | 396 | // In the absence of any SubstringRopes, we always take the full contents of the ConcatRope.
|
388 | 397 | if (substringLengths.isEmpty()) {
|
389 |
| - workStack.push(concatRope.getRight()); |
390 |
| - workStack.push(concatRope.getLeft()); |
| 398 | + workStack.push(children.right); |
| 399 | + workStack.push(children.left); |
391 | 400 | } else {
|
392 |
| - final int leftLength = concatRope.getLeft().byteLength(); |
| 401 | + final int leftLength = children.left.byteLength(); |
393 | 402 |
|
394 | 403 | // If we reach here, this ConcatRope is a descendant of a SubstringRope at some level. Based on
|
395 | 404 | // the currently calculated byte[] offset and the number of bytes to extract, determine which of
|
396 | 405 | // the ConcatRope's children we need to visit.
|
397 | 406 | if (byteOffset < leftLength) {
|
398 | 407 | if ((byteOffset + substringLengths.peek()) > leftLength) {
|
399 |
| - workStack.push(concatRope.getRight()); |
400 |
| - workStack.push(concatRope.getLeft()); |
| 408 | + workStack.push(children.right); |
| 409 | + workStack.push(children.left); |
401 | 410 | } else {
|
402 |
| - workStack.push(concatRope.getLeft()); |
| 411 | + workStack.push(children.left); |
403 | 412 | }
|
404 | 413 | } else {
|
405 | 414 | // If we can skip the left child entirely, we need to update the offset so it's accurate for
|
406 | 415 | // the right child as each child's starting point is 0.
|
407 | 416 | byteOffset -= leftLength;
|
408 |
| - workStack.push(concatRope.getRight()); |
| 417 | + workStack.push(children.right); |
409 | 418 | }
|
410 | 419 | }
|
411 | 420 | } else if (current instanceof SubstringRope) {
|
@@ -505,12 +514,16 @@ static byte getByteSlow(Rope rope, int index) {
|
505 | 514 | return rawBytes[index];
|
506 | 515 | } else if (rope instanceof ConcatRope) {
|
507 | 516 | final ConcatRope concatRope = (ConcatRope) rope;
|
508 |
| - final Rope left = concatRope.getLeft(); |
509 |
| - if (index < left.byteLength()) { |
510 |
| - rope = left; |
| 517 | + final ConcatChildren children = concatRope.getChildrenOrNull(); |
| 518 | + if (children == null) { |
| 519 | + // Rope got concurrently flattened. |
| 520 | + return concatRope.getRawBytes()[index]; |
| 521 | + } |
| 522 | + if (index < children.left.byteLength()) { |
| 523 | + rope = children.left; |
511 | 524 | } else {
|
512 |
| - rope = concatRope.getRight(); |
513 |
| - index -= left.byteLength(); |
| 525 | + rope = children.right; |
| 526 | + index -= children.left.byteLength(); |
514 | 527 | }
|
515 | 528 | } else if (rope instanceof SubstringRope) {
|
516 | 529 | final SubstringRope substringRope = (SubstringRope) rope;
|
@@ -573,21 +586,27 @@ class Params {
|
573 | 586 | workStack.push(new Params(child, startingHashCode, newOffset, length, false));
|
574 | 587 | } else if (rope instanceof ConcatRope) {
|
575 | 588 | final ConcatRope concatRope = (ConcatRope) rope;
|
576 |
| - final Rope left = concatRope.getLeft(); |
577 |
| - final Rope right = concatRope.getRight(); |
578 |
| - final int leftLength = left.byteLength(); |
579 |
| - |
580 |
| - if (offset >= leftLength) { |
581 |
| - // range fully contained in right child |
582 |
| - workStack.push(new Params(right, startingHashCode, offset - leftLength, length, false)); |
583 |
| - } else if (offset + length <= leftLength) { |
584 |
| - // range fully contained in left child |
585 |
| - workStack.push(new Params(left, startingHashCode, offset, length, false)); |
| 589 | + final ConcatChildren children = concatRope.getChildrenOrNull(); |
| 590 | + if (children == null) { |
| 591 | + // Rope got concurrently flattened. |
| 592 | + resultHash = Hashing.stringHash(bytes, startingHashCode, offset, length); |
586 | 593 | } else {
|
587 |
| - final int coveredByLeft = leftLength - offset; |
588 |
| - // push right node first, starting hash is the result from the left node |
589 |
| - workStack.push(new Params(right, 0, 0, length - coveredByLeft, true)); |
590 |
| - workStack.push(new Params(left, startingHashCode, offset, coveredByLeft, false)); |
| 594 | + final Rope left = children.left; |
| 595 | + final Rope right = children.right; |
| 596 | + final int leftLength = left.byteLength(); |
| 597 | + |
| 598 | + if (offset >= leftLength) { |
| 599 | + // range fully contained in right child |
| 600 | + workStack.push(new Params(right, startingHashCode, offset - leftLength, length, false)); |
| 601 | + } else if (offset + length <= leftLength) { |
| 602 | + // range fully contained in left child |
| 603 | + workStack.push(new Params(left, startingHashCode, offset, length, false)); |
| 604 | + } else { |
| 605 | + final int coveredByLeft = leftLength - offset; |
| 606 | + // push right node first, starting hash is the result from the left node |
| 607 | + workStack.push(new Params(right, 0, 0, length - coveredByLeft, true)); |
| 608 | + workStack.push(new Params(left, startingHashCode, offset, coveredByLeft, false)); |
| 609 | + } |
591 | 610 | }
|
592 | 611 | } else if (rope instanceof RepeatingRope) {
|
593 | 612 | final RepeatingRope repeatingRope = (RepeatingRope) rope;
|
@@ -668,14 +687,11 @@ public static boolean anyChildContains(Rope rope, String value) {
|
668 | 687 | if (rope instanceof SubstringRope) {
|
669 | 688 | return anyChildContains(((SubstringRope) rope).getChild(), value);
|
670 | 689 | } else if (rope instanceof ConcatRope) {
|
671 |
| - return anyChildContains(((ConcatRope) rope).getLeft(), value) || |
672 |
| - anyChildContains(((ConcatRope) rope).getRight(), value); |
673 |
| - } else { |
674 |
| - if (rope.byteLength() < value.length()) { |
675 |
| - return false; |
| 690 | + ConcatChildren children = ((ConcatRope) rope).getChildrenOrNull(); |
| 691 | + if (children != null) { |
| 692 | + return anyChildContains(children.left, value) || anyChildContains(children.right, value); |
676 | 693 | }
|
677 |
| - return RopeOperations.decodeRope(rope).contains(value); |
678 | 694 | }
|
| 695 | + return rope.byteLength() < value.length() && RopeOperations.decodeRope(rope).contains(value); |
679 | 696 | }
|
680 |
| - |
681 | 697 | }
|
0 commit comments