All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.truffle.api.strings.TruffleStringIterator Maven / Gradle / Ivy

Go to download

Truffle is a multi-language framework for executing dynamic languages that achieves high performance when combined with Graal.

There is a newer version: 24.1.1
Show newest version
/*
 * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.truffle.api.strings;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GeneratePackagePrivate;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.strings.TruffleString.Encoding;

// Checkstyle: stop
/**
 * An iterator object that allows iterating over a {@link TruffleString}'s codepoints, without
 * having to re-calculate codepoint indices on every access.
 * 

* Usage Example: * *

 * 
 * {@code
 * abstract static class SomeNode extends Node {
 *
 *     @Specialization
 *     static void someSpecialization(
 *                     TruffleString string,
 *                     @Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode,
 *                     @Cached TruffleStringIterator.NextNode nextNode,
 *                     @Cached TruffleString.CodePointLengthNode codePointLengthNode,
 *                     @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode) {
 *
 *         // iterating over a string's code points using TruffleStringIterator
 *         TruffleStringIterator iterator = createCodePointIteratorNode.execute(string, Encoding.UTF_8);
 *         while (iterator.hasNext()) {
 *             System.out.printf("%x%n", nextNode.execute(iterator));
 *         }
 *
 *         // uncached variant:
 *         TruffleStringIterator iterator2 = string.createCodePointIteratorUncached(Encoding.UTF_8);
 *         while (iterator2.hasNext()) {
 *             System.out.printf("%x%n", iterator2.nextUncached());
 *         }
 *
 *         // suboptimal variant: using CodePointAtIndexNode in a loop
 *         int codePointLength = codePointLengthNode.execute(string, Encoding.UTF_8);
 *         for (int i = 0; i < codePointLength; i++) {
 *             // performance problem: codePointAtIndexNode may have to calculate the byte index corresponding
 *             // to codepoint index i for every loop iteration
 *             System.out.printf("%x%n", codePointAtIndexNode.execute(string, i, Encoding.UTF_8));
 *         }
 *     }
 * }
 * }
 * 
 * 
* * @since 22.1 */ // Checkstyle: resume public final class TruffleStringIterator { final AbstractTruffleString a; final Object arrayA; final byte codeRangeA; final Encoding encoding; final TruffleString.ErrorHandling errorHandling; private int rawIndex; TruffleStringIterator(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, TruffleString.ErrorHandling errorHandling, int rawIndex) { assert TSCodeRange.isCodeRange(codeRangeA); this.a = a; this.arrayA = arrayA; this.codeRangeA = (byte) codeRangeA; this.encoding = encoding; this.errorHandling = errorHandling; this.rawIndex = rawIndex; } /** * Returns {@code true} if there are more codepoints remaining. * * @since 22.1 */ public boolean hasNext() { return rawIndex < a.length(); } /** * Returns {@code true} if there are more codepoints remaining in reverse direction. * * @since 22.1 */ public boolean hasPrevious() { return rawIndex > 0; } /** * Returns the next codepoint's byte index, where "byte index" refers the codepoint's first byte * in forward mode, while in backward mode it refers to the first byte after the * codepoint. * * @since 22.3 */ public int getByteIndex() { return rawIndex << encoding.naturalStride; } /** * Returns the next codepoint in the string. * * @since 22.1 */ @ImportStatic(TStringGuards.class) @GeneratePackagePrivate @GenerateUncached public abstract static class NextNode extends Node { NextNode() { } /** * Returns the next codepoint in the string. * * @since 22.1 */ public final int execute(TruffleStringIterator it) { if (!it.hasNext()) { throw InternalErrors.illegalState("end of string has been reached already"); } return executeInternal(it); } abstract int executeInternal(TruffleStringIterator it); @Specialization(guards = {"isFixedWidth(it.codeRangeA)", "isBestEffort(it.errorHandling)"}) static int fixed(TruffleStringIterator it, @Cached TStringOpsNodes.RawReadValueNode readNode) { return readAndInc(it, readNode); } @Specialization(guards = {"isUpToValidFixedWidth(it.codeRangeA)", "isReturnNegative(it.errorHandling)"}) static int fixedValid(TruffleStringIterator it, @Cached TStringOpsNodes.RawReadValueNode readNode) { return readAndInc(it, readNode); } @Specialization(guards = {"isAscii(it.encoding)", "isBrokenFixedWidth(it.codeRangeA)", "isReturnNegative(it.errorHandling)"}) static int brokenAscii(TruffleStringIterator it, @Cached TStringOpsNodes.RawReadValueNode readNode) { int codepoint = readAndInc(it, readNode); return codepoint < 0x80 ? codepoint : -1; } @Specialization(guards = {"isUTF32(it.encoding)", "isBrokenFixedWidth(it.codeRangeA)", "isReturnNegative(it.errorHandling)"}) static int brokenUTF32(TruffleStringIterator it, @Cached TStringOpsNodes.RawReadValueNode readNode) { int codepoint = readAndInc(it, readNode); return Encodings.isValidUnicodeCodepoint(codepoint) ? codepoint : -1; } @SuppressWarnings("fallthrough") @Specialization(guards = {"isUTF8(it.encoding)", "isValidMultiByte(it.codeRangeA)"}) static int utf8Valid(TruffleStringIterator it) { int b = it.readAndIncS0(); if (b < 0x80) { return b; } int nBytes = Integer.numberOfLeadingZeros(~(b << 24)); int codepoint = b & (0xff >>> nBytes); assert 1 < nBytes && nBytes < 5 : nBytes; assert it.rawIndex + nBytes - 1 <= it.a.length(); // Checkstyle: stop switch (nBytes) { case 4: assert it.curIsUtf8ContinuationByte(); codepoint = codepoint << 6 | (it.readAndIncS0() & 0x3f); case 3: assert it.curIsUtf8ContinuationByte(); codepoint = codepoint << 6 | (it.readAndIncS0() & 0x3f); default: assert it.curIsUtf8ContinuationByte(); codepoint = codepoint << 6 | (it.readAndIncS0() & 0x3f); } // Checkstyle: resume return codepoint; } @SuppressWarnings("fallthrough") @Specialization(guards = {"isUTF8(it.encoding)", "isBrokenMultiByteOrUnknown(it.codeRangeA)"}) static int utf8Broken(TruffleStringIterator it) { int b = it.readAndIncS0(); if (b < 0x80) { return b; } int resetIndex = it.rawIndex; int nBytes = Integer.numberOfLeadingZeros(~(b << 24)); int codepoint = b & (0xff >>> nBytes); // Checkstyle: stop switch (nBytes) { case 4: if (!it.hasNext() || !it.curIsUtf8ContinuationByte()) { return Encodings.invalidCodepointReturnValue(it.errorHandling); } codepoint = codepoint << 6 | (it.readAndIncS0() & 0x3f); case 3: if (!it.hasNext() || !it.curIsUtf8ContinuationByte()) { it.setRawIndex(resetIndex); return Encodings.invalidCodepointReturnValue(it.errorHandling); } codepoint = codepoint << 6 | (it.readAndIncS0() & 0x3f); case 2: if (!it.hasNext() || !it.curIsUtf8ContinuationByte()) { it.setRawIndex(resetIndex); return Encodings.invalidCodepointReturnValue(it.errorHandling); } codepoint = codepoint << 6 | (it.readAndIncS0() & 0x3f); break; default: return Encodings.invalidCodepointReturnValue(it.errorHandling); } // Checkstyle: resume if (Encodings.utf8IsInvalidCodePoint(codepoint, nBytes)) { it.setRawIndex(resetIndex); return Encodings.invalidCodepointReturnValue(it.errorHandling); } return codepoint; } @Specialization(guards = {"isUTF16(it.encoding)", "isValidMultiByte(it.codeRangeA)"}) static int utf16Valid(TruffleStringIterator it) { char c = (char) it.readAndIncS1(); if (Encodings.isUTF16HighSurrogate(c)) { assert it.hasNext(); assert Encodings.isUTF16LowSurrogate(it.readFwdS1()); return Character.toCodePoint(c, (char) it.readAndIncS1()); } return c; } @Specialization(guards = {"isUTF16(it.encoding)", "isBrokenMultiByteOrUnknown(it.codeRangeA)"}) static int utf16Broken(TruffleStringIterator it) { char c = (char) it.readAndIncS1(); if (it.errorHandling == TruffleString.ErrorHandling.RETURN_NEGATIVE) { if (Encodings.isUTF16Surrogate(c)) { if (Encodings.isUTF16HighSurrogate(c) && it.hasNext()) { char c2 = (char) it.readFwdS1(); if (Encodings.isUTF16LowSurrogate(c2)) { it.rawIndex++; return Character.toCodePoint(c, c2); } } return -1; } } else { if (Encodings.isUTF16HighSurrogate(c) && it.hasNext()) { char c2 = (char) it.readFwdS1(); if (Encodings.isUTF16LowSurrogate(c2)) { it.rawIndex++; return Character.toCodePoint(c, c2); } } } return c; } @Specialization(guards = {"isUnsupportedEncoding(it.encoding)"}) static int unsupported(TruffleStringIterator it) { assert it.hasNext(); byte[] bytes = JCodings.asByteArray(it.arrayA); int p = it.a.byteArrayOffset() + it.rawIndex; int end = it.a.byteArrayOffset() + it.a.length(); JCodings.Encoding jCoding = JCodings.getInstance().get(it.encoding); int length = JCodings.getInstance().getCodePointLength(jCoding, bytes, p, end); if (length < 1) { if (length < -1) { // broken multibyte codepoint at end of string it.rawIndex = it.a.length(); } else { it.rawIndex++; } return Encodings.invalidCodepointReturnValue(it.errorHandling); } it.rawIndex += length; return JCodings.getInstance().readCodePoint(jCoding, bytes, p, end); } /** * Create a new {@link NextNode}. * * @since 22.1 */ public static NextNode create() { return TruffleStringIteratorFactory.NextNodeGen.create(); } /** * Get the uncached version of {@link NextNode}. * * @since 22.1 */ public static NextNode getUncached() { return TruffleStringIteratorFactory.NextNodeGen.getUncached(); } } /** * Shorthand for calling the uncached version of {@link NextNode}. * * @since 22.1 */ @TruffleBoundary public int nextUncached() { return NextNode.getUncached().execute(this); } /** * Returns the previous codepoint in the string. * * @since 22.1 */ @ImportStatic(TStringGuards.class) @GeneratePackagePrivate @GenerateUncached public abstract static class PreviousNode extends Node { PreviousNode() { } /** * Returns the previous codepoint in the string. * * @since 22.1 */ public final int execute(TruffleStringIterator it) { if (!it.hasPrevious()) { throw InternalErrors.illegalState("beginning of string has been reached already"); } return executeInternal(it); } abstract int executeInternal(TruffleStringIterator it); @Specialization(guards = {"isFixedWidth(it.codeRangeA)", "isBestEffort(it.errorHandling)"}) static int fixed(TruffleStringIterator it, @Cached TStringOpsNodes.RawReadValueNode readNode) { return readAndDec(it, readNode); } @Specialization(guards = {"isUpToValidFixedWidth(it.codeRangeA)", "isReturnNegative(it.errorHandling)"}) static int fixedValid(TruffleStringIterator it, @Cached TStringOpsNodes.RawReadValueNode readNode) { return readAndDec(it, readNode); } @Specialization(guards = {"isAscii(it.encoding)", "isBrokenFixedWidth(it.codeRangeA)", "isReturnNegative(it.errorHandling)"}) static int brokenAscii(TruffleStringIterator it, @Cached TStringOpsNodes.RawReadValueNode readNode) { int codepoint = readAndDec(it, readNode); return codepoint < 0x80 ? codepoint : -1; } @Specialization(guards = {"isUTF32(it.encoding)", "isBrokenFixedWidth(it.codeRangeA)", "isReturnNegative(it.errorHandling)"}) static int brokenUTF32(TruffleStringIterator it, @Cached TStringOpsNodes.RawReadValueNode readNode) { int codepoint = readAndDec(it, readNode); return Encodings.isValidUnicodeCodepoint(codepoint) ? codepoint : -1; } @Specialization(guards = {"isUTF8(it.encoding)", "isValidMultiByte(it.codeRangeA)"}) static int utf8Valid(TruffleStringIterator it) { int b = it.readAndDecS0(); if (b < 0x80) { return b; } assert Encodings.isUTF8ContinuationByte(b); int codepoint = b & 0x3f; for (int j = 1; j < 4; j++) { b = it.readAndDecS0(); if (j < 3 && Encodings.isUTF8ContinuationByte(b)) { codepoint |= (b & 0x3f) << (6 * j); } else { break; } } int nBytes = Integer.numberOfLeadingZeros(~(b << 24)); assert 1 < nBytes && nBytes < 5 : nBytes; return codepoint | (b & (0xff >>> nBytes)) << (6 * (nBytes - 1)); } @Specialization(guards = {"isUTF8(it.encoding)", "isBrokenMultiByteOrUnknown(it.codeRangeA)"}) static int utf8Broken(TruffleStringIterator it) { int initialIndex = it.rawIndex; int b = it.readAndDecS0(); if (b < 0x80) { return b; } if (!Encodings.isUTF8ContinuationByte(b)) { return Encodings.invalidCodepointReturnValue(it.errorHandling); } int codepoint = b & 0x3f; for (int j = 1; j < 4 && it.hasPrevious(); j++) { b = it.readAndDecS0(); if (j < 3 && Encodings.isUTF8ContinuationByte(b)) { codepoint |= (b & 0x3f) << (6 * j); } else { break; } } int nBytes = Integer.numberOfLeadingZeros(~(b << 24)); codepoint |= (b & (0xff >>> nBytes)) << (6 * (nBytes - 1)); if (nBytes < 2 || nBytes != initialIndex - it.rawIndex || Encodings.utf8IsInvalidCodePoint(codepoint, nBytes)) { it.rawIndex = initialIndex - 1; return Encodings.invalidCodepointReturnValue(it.errorHandling); } return codepoint; } @Specialization(guards = {"isUTF16(it.encoding)", "isValidMultiByte(it.codeRangeA)"}) static int utf16Valid(TruffleStringIterator it) { char c = (char) it.readAndDecS1(); if (Encodings.isUTF16LowSurrogate(c)) { assert Encodings.isUTF16HighSurrogate((char) it.readBckS1()); return Character.toCodePoint((char) it.readAndDecS1(), c); } return c; } @Specialization(guards = {"isUTF16(it.encoding)", "isBrokenMultiByteOrUnknown(it.codeRangeA)"}) static int utf16Broken(TruffleStringIterator it) { char c = (char) it.readAndDecS1(); if (it.errorHandling == TruffleString.ErrorHandling.RETURN_NEGATIVE) { if (Encodings.isUTF16Surrogate(c)) { if (Encodings.isUTF16LowSurrogate(c) && it.hasPrevious()) { char c2 = (char) it.readBckS1(); if (Encodings.isUTF16HighSurrogate(c2)) { it.rawIndex--; return Character.toCodePoint(c2, c); } } return -1; } } else { if (Encodings.isUTF16LowSurrogate(c) && it.hasPrevious()) { char c2 = (char) it.readBckS1(); if (Encodings.isUTF16HighSurrogate(c2)) { it.rawIndex--; return Character.toCodePoint(c2, c); } } } return c; } @Specialization(guards = {"isUnsupportedEncoding(it.encoding)"}) static int unsupported(TruffleStringIterator it) { assert it.hasPrevious(); byte[] bytes = JCodings.asByteArray(it.arrayA); int start = it.a.byteArrayOffset(); int index = it.a.byteArrayOffset() + it.rawIndex; int end = it.a.byteArrayOffset() + it.a.length(); JCodings.Encoding jCoding = JCodings.getInstance().get(it.encoding); int prevIndex = JCodings.getInstance().getPreviousCodePointIndex(jCoding, bytes, start, index, end); if (prevIndex < 0) { it.rawIndex--; return Encodings.invalidCodepointReturnValue(it.errorHandling); } assert prevIndex >= it.a.byteArrayOffset(); assert prevIndex < index; it.rawIndex = prevIndex - it.a.byteArrayOffset(); return JCodings.getInstance().readCodePoint(jCoding, bytes, prevIndex, end); } /** * Create a new {@link PreviousNode}. * * @since 22.1 */ public static PreviousNode create() { return TruffleStringIteratorFactory.PreviousNodeGen.create(); } /** * Get the uncached version of {@link PreviousNode}. * * @since 22.1 */ public static PreviousNode getUncached() { return TruffleStringIteratorFactory.PreviousNodeGen.getUncached(); } } /** * Shorthand for calling the uncached version of {@link PreviousNode}. * * @since 22.1 */ @TruffleBoundary public int previousUncached() { return PreviousNode.getUncached().execute(this); } int getRawIndex() { return rawIndex; } void setRawIndex(int i) { rawIndex = i; } private int readFwdS0() { assert a.stride() == 0; assert hasNext(); return TStringOps.readS0(a, arrayA, rawIndex); } private int readFwdS1() { assert a.stride() == 1; assert hasNext(); return TStringOps.readS1(a, arrayA, rawIndex); } private int readBckS1() { assert a.stride() == 1; assert hasPrevious(); return TStringOps.readS1(a, arrayA, rawIndex - 1); } private static int readAndInc(TruffleStringIterator it, TStringOpsNodes.RawReadValueNode readNode) { assert it.hasNext(); return readNode.execute(it.a, it.arrayA, it.rawIndex++); } private int readAndIncS0() { assert a.stride() == 0; assert hasNext(); return TStringOps.readS0(a, arrayA, rawIndex++); } private int readAndIncS1() { assert a.stride() == 1; assert hasNext(); return TStringOps.readS1(a, arrayA, rawIndex++); } private static int readAndDec(TruffleStringIterator it, TStringOpsNodes.RawReadValueNode readNode) { assert it.hasPrevious(); return readNode.execute(it.a, it.arrayA, --it.rawIndex); } private int readAndDecS0() { assert a.stride() == 0; assert hasPrevious(); return TStringOps.readS0(a, arrayA, --rawIndex); } private int readAndDecS1() { assert a.stride() == 1; assert hasPrevious(); return TStringOps.readS1(a, arrayA, --rawIndex); } private boolean curIsUtf8ContinuationByte() { return Encodings.isUTF8ContinuationByte(readFwdS0()); } static int indexOf(Node location, TruffleStringIterator it, int codepoint, int fromIndex, int toIndex, NextNode nextNode) { int aCodepointIndex = 0; while (aCodepointIndex < fromIndex && it.hasNext()) { nextNode.execute(it); aCodepointIndex++; TStringConstants.truffleSafePointPoll(location, aCodepointIndex); } if (aCodepointIndex < fromIndex) { return -1; } while (it.hasNext() && aCodepointIndex < toIndex) { if (nextNode.execute(it) == codepoint) { return aCodepointIndex; } aCodepointIndex++; TStringConstants.truffleSafePointPoll(location, aCodepointIndex); } return -1; } static int lastIndexOf(Node location, TruffleStringIterator it, int codepoint, int fromIndex, int toIndex, NextNode nextNode) { int aCodepointIndex = 0; int result = -1; // the code point index is based on the beginning of the string, so we have to count // from there while (aCodepointIndex < fromIndex && it.hasNext()) { if (nextNode.execute(it) == codepoint) { result = aCodepointIndex; } aCodepointIndex++; TStringConstants.truffleSafePointPoll(location, aCodepointIndex); } if (aCodepointIndex < toIndex) { // fromIndex was out of bounds return -1; } return result; } static int indexOfString(Node location, TruffleStringIterator aIt, TruffleStringIterator bIt, int fromIndex, int toIndex, NextNode nextNodeA, NextNode nextNodeB) { if (!bIt.hasNext()) { return fromIndex; } int aCodepointIndex = 0; while (aCodepointIndex < fromIndex && aIt.hasNext()) { nextNodeA.execute(aIt); aCodepointIndex++; TStringConstants.truffleSafePointPoll(location, aCodepointIndex); } if (aCodepointIndex < fromIndex) { return -1; } int bFirst = nextNodeB.execute(bIt); int bSecondIndex = bIt.getRawIndex(); while (aIt.hasNext() && aCodepointIndex < toIndex) { if (nextNodeA.execute(aIt) == bFirst) { if (!bIt.hasNext()) { return aCodepointIndex; } int aCurIndex = aIt.getRawIndex(); int innerLoopCount = 0; while (bIt.hasNext()) { if (!aIt.hasNext()) { return -1; } if (nextNodeA.execute(aIt) != nextNodeB.execute(bIt)) { break; } if (!bIt.hasNext()) { return aCodepointIndex; } TStringConstants.truffleSafePointPoll(location, ++innerLoopCount); } aIt.setRawIndex(aCurIndex); bIt.setRawIndex(bSecondIndex); } aCodepointIndex++; TStringConstants.truffleSafePointPoll(location, aCodepointIndex); } return -1; } static int byteIndexOfString(Node location, TruffleStringIterator aIt, TruffleStringIterator bIt, int fromByteIndex, int toByteIndex, NextNode nextNodeA, NextNode nextNodeB) { if (!bIt.hasNext()) { return fromByteIndex; } aIt.setRawIndex(fromByteIndex); int bFirst = nextNodeB.execute(bIt); int bSecondIndex = bIt.getRawIndex(); int loopCount = 0; while (aIt.hasNext() && aIt.getRawIndex() < toByteIndex) { int ret = aIt.getRawIndex(); if (nextNodeA.execute(aIt) == bFirst) { if (!bIt.hasNext()) { return ret; } int aCurIndex = aIt.getRawIndex(); while (bIt.hasNext()) { if (!aIt.hasNext()) { return -1; } if (nextNodeA.execute(aIt) != nextNodeB.execute(bIt)) { break; } if (!bIt.hasNext()) { return ret; } TStringConstants.truffleSafePointPoll(location, ++loopCount); } aIt.setRawIndex(aCurIndex); bIt.setRawIndex(bSecondIndex); } TStringConstants.truffleSafePointPoll(location, ++loopCount); } return -1; } static int lastIndexOfString(Node location, TruffleStringIterator aIt, TruffleStringIterator bIt, int fromIndex, int toIndex, NextNode nextNodeA, PreviousNode prevNodeA, PreviousNode prevNodeB) { if (!bIt.hasPrevious()) { return fromIndex; } int bFirstCodePoint = prevNodeB.execute(bIt); int lastMatchIndex = -1; int lastMatchByteIndex = -1; int aCodepointIndex = 0; while (aCodepointIndex < fromIndex && aIt.hasNext()) { if (nextNodeA.execute(aIt) == bFirstCodePoint) { lastMatchIndex = aCodepointIndex; lastMatchByteIndex = aIt.getRawIndex(); } aCodepointIndex++; TStringConstants.truffleSafePointPoll(location, aCodepointIndex); } if (aCodepointIndex < fromIndex || lastMatchIndex < 0) { return -1; } aCodepointIndex = lastMatchIndex; aIt.setRawIndex(lastMatchByteIndex); int bSecondIndex = bIt.getRawIndex(); while (aIt.hasPrevious() && aCodepointIndex >= toIndex) { if (prevNodeA.execute(aIt) == bFirstCodePoint) { if (!bIt.hasPrevious()) { return aCodepointIndex; } int aCurIndex = aIt.getRawIndex(); int aCurCodePointIndex = aCodepointIndex; while (bIt.hasPrevious()) { if (!aIt.hasPrevious()) { return -1; } if (prevNodeA.execute(aIt) != prevNodeB.execute(bIt)) { break; } aCurCodePointIndex--; if (!bIt.hasPrevious() && aCurCodePointIndex >= toIndex) { return aCurCodePointIndex; } TStringConstants.truffleSafePointPoll(location, aCurCodePointIndex); } aIt.setRawIndex(aCurIndex); bIt.setRawIndex(bSecondIndex); } aCodepointIndex--; TStringConstants.truffleSafePointPoll(location, aCodepointIndex); } return -1; } static int lastByteIndexOfString(Node location, TruffleStringIterator aIt, TruffleStringIterator bIt, int fromByteIndex, int toByteIndex, NextNode nextNodeA, PreviousNode prevNodeA, PreviousNode prevNodeB) { if (!bIt.hasPrevious()) { return fromByteIndex; } int bFirstCodePoint = prevNodeB.execute(bIt); int lastMatchByteIndex = -1; int loopCount = 0; while (aIt.getRawIndex() < fromByteIndex && aIt.hasNext()) { if (nextNodeA.execute(aIt) == bFirstCodePoint) { lastMatchByteIndex = aIt.getRawIndex(); } TStringConstants.truffleSafePointPoll(location, ++loopCount); } if (aIt.getRawIndex() < fromByteIndex || lastMatchByteIndex < 0) { return -1; } aIt.setRawIndex(lastMatchByteIndex); int bSecondIndex = bIt.getRawIndex(); while (aIt.hasPrevious() && aIt.getRawIndex() > toByteIndex) { if (prevNodeA.execute(aIt) == bFirstCodePoint) { if (!bIt.hasPrevious()) { return aIt.getRawIndex(); } int aCurIndex = aIt.getRawIndex(); while (bIt.hasPrevious()) { if (!aIt.hasPrevious()) { return -1; } if (prevNodeA.execute(aIt) != prevNodeB.execute(bIt)) { break; } if (!bIt.hasPrevious() && aIt.getRawIndex() >= toByteIndex) { return aIt.getRawIndex(); } TStringConstants.truffleSafePointPoll(location, ++loopCount); } aIt.setRawIndex(aCurIndex); bIt.setRawIndex(bSecondIndex); } TStringConstants.truffleSafePointPoll(location, ++loopCount); } return -1; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy