com.oracle.truffle.api.strings.TStringInternalNodes Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of truffle-api Show documentation
Show all versions of truffle-api Show documentation
Truffle is a multi-language framework for executing dynamic languages
that achieves high performance when combined with Graal.
/*
* 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 static com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import static com.oracle.truffle.api.dsl.Cached.Shared;
import static com.oracle.truffle.api.strings.AbstractTruffleString.checkArrayRange;
import static com.oracle.truffle.api.strings.AbstractTruffleString.checkByteLengthUTF16;
import static com.oracle.truffle.api.strings.AbstractTruffleString.checkByteLengthUTF32;
import static com.oracle.truffle.api.strings.Encodings.isUTF8ContinuationByte;
import static com.oracle.truffle.api.strings.TStringGuards.indexOfCannotMatch;
import static com.oracle.truffle.api.strings.TStringGuards.is16Bit;
import static com.oracle.truffle.api.strings.TStringGuards.is7Bit;
import static com.oracle.truffle.api.strings.TStringGuards.is7Or8Bit;
import static com.oracle.truffle.api.strings.TStringGuards.is8Bit;
import static com.oracle.truffle.api.strings.TStringGuards.isAscii;
import static com.oracle.truffle.api.strings.TStringGuards.isAsciiBytesOrLatin1;
import static com.oracle.truffle.api.strings.TStringGuards.isBrokenFixedWidth;
import static com.oracle.truffle.api.strings.TStringGuards.isBrokenMultiByte;
import static com.oracle.truffle.api.strings.TStringGuards.isBytes;
import static com.oracle.truffle.api.strings.TStringGuards.isFixedWidth;
import static com.oracle.truffle.api.strings.TStringGuards.isStride0;
import static com.oracle.truffle.api.strings.TStringGuards.isStride1;
import static com.oracle.truffle.api.strings.TStringGuards.isStride2;
import static com.oracle.truffle.api.strings.TStringGuards.isSupportedEncoding;
import static com.oracle.truffle.api.strings.TStringGuards.isUTF16;
import static com.oracle.truffle.api.strings.TStringGuards.isUTF16Or32;
import static com.oracle.truffle.api.strings.TStringGuards.isUTF32;
import static com.oracle.truffle.api.strings.TStringGuards.isUTF8;
import static com.oracle.truffle.api.strings.TStringGuards.isUnsupportedEncoding;
import static com.oracle.truffle.api.strings.TStringGuards.isValidFixedWidth;
import static com.oracle.truffle.api.strings.TStringGuards.isValidMultiByte;
import static com.oracle.truffle.api.strings.TStringOps.readS0;
import static com.oracle.truffle.api.strings.TStringOps.writeToByteArray;
import static com.oracle.truffle.api.strings.TStringOpsNodes.RawIndexOfStringNode;
import static com.oracle.truffle.api.strings.TStringOpsNodes.RawLastIndexOfStringNode;
import java.util.Arrays;
import com.oracle.truffle.api.CompilerAsserts;
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.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.strings.AbstractTruffleString.NativePointer;
import com.oracle.truffle.api.strings.TruffleString.Encoding;
import com.oracle.truffle.api.strings.TruffleString.ErrorHandling;
final class TStringInternalNodes {
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class GetCodeRangeNode extends Node {
abstract int execute(AbstractTruffleString a);
@Specialization
int immutable(TruffleString a) {
return a.codeRange();
}
@Specialization(guards = "!isUnknown(a.codeRange())")
int mutableCacheHit(MutableTruffleString a) {
return a.codeRange();
}
@Specialization(guards = "isUnknown(a.codeRange())")
int mutableCacheMiss(MutableTruffleString a,
@Cached MutableTruffleString.CalcLazyAttributesNode calcLazyAttributesNode) {
calcLazyAttributesNode.execute(a);
return a.codeRange();
}
static GetCodeRangeNode getUncached() {
return TStringInternalNodesFactory.GetCodeRangeNodeGen.getUncached();
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class GetCodePointLengthNode extends Node {
abstract int execute(AbstractTruffleString a);
@Specialization
int immutable(TruffleString a) {
return a.codePointLength();
}
@Specialization(guards = "a.codePointLength() >= 0")
int mutableCacheHit(MutableTruffleString a) {
return a.codePointLength();
}
@Specialization(guards = "a.codePointLength() < 0")
int mutableCacheMiss(MutableTruffleString a,
@Cached MutableTruffleString.CalcLazyAttributesNode calcLazyAttributesNode) {
calcLazyAttributesNode.execute(a);
return a.codePointLength();
}
static GetCodePointLengthNode getUncached() {
return TStringInternalNodesFactory.GetCodePointLengthNodeGen.getUncached();
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class CreateSubstringNode extends Node {
abstract TruffleString execute(AbstractTruffleString a, Object array, int offset, int length, int stride, Encoding encoding, int codeRange);
@Specialization(guards = {"encoding == cachedEncoding", "stride == cachedStride"}, limit = "6")
TruffleString doCached(AbstractTruffleString a, Object array, int offset, int length, @SuppressWarnings("unused") int stride, @SuppressWarnings("unused") Encoding encoding, int codeRange,
@Cached(value = "encoding") Encoding cachedEncoding,
@Cached(value = "stride") int cachedStride,
@Cached CalcStringAttributesNode calcAttributesNode) {
return createString(a, array, offset, length, cachedStride, cachedEncoding, codeRange, calcAttributesNode, this);
}
@Specialization(replaces = "doCached")
TruffleString doUncached(AbstractTruffleString a, Object array, int offset, int length, int stride, Encoding encoding, int codeRange,
@Cached CalcStringAttributesNode calcAttributesNode) {
return createString(a, array, offset, length, stride, encoding, codeRange, calcAttributesNode, this);
}
private static TruffleString createString(AbstractTruffleString a, Object array, int offset, int length, int stride, Encoding encoding, int codeRange,
CalcStringAttributesNode calcAttributesNode, CreateSubstringNode location) {
long attrs = calcAttributesNode.execute(a, array, offset, length, stride, encoding, codeRange);
int newStride = Stride.fromCodeRange(StringAttributes.getCodeRange(attrs), encoding);
byte[] newBytes = new byte[length << newStride];
TStringOps.arraycopyWithStride(location,
array, offset, stride, 0,
newBytes, 0, newStride, 0, length);
return TruffleString.createFromByteArray(newBytes, length, newStride, encoding, StringAttributes.getCodePointLength(attrs), StringAttributes.getCodeRange(attrs));
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class FromBufferWithStringCompactionNode extends Node {
abstract TruffleString execute(Object arrayA, int offsetA, int byteLength, Encoding encoding, boolean copy, boolean isCacheHead);
@Specialization
TruffleString fromBufferWithStringCompaction(Object arrayA, int offsetA, int byteLength, Encoding encoding, boolean copy, boolean isCacheHead,
@Cached ConditionProfile asciiLatinBytesProfile,
@Cached ConditionProfile utf8Profile,
@Cached ConditionProfile utf8BrokenProfile,
@Cached ConditionProfile utf16Profile,
@Cached ConditionProfile utf16CompactProfile,
@Cached ConditionProfile utf32Profile,
@Cached ConditionProfile utf32Compact0Profile,
@Cached ConditionProfile utf32Compact1Profile,
@Cached ConditionProfile exoticValidProfile,
@Cached ConditionProfile exoticFixedWidthProfile) {
if (byteLength == 0) {
return encoding.getEmpty();
}
final int offset;
final int length;
final int stride;
final int codePointLength;
final int codeRange;
final Object array;
if (utf16Profile.profile(isUTF16(encoding))) {
checkByteLengthUTF16(byteLength);
length = byteLength >> 1;
long attrs = TStringOps.calcStringAttributesUTF16(this, arrayA, offsetA, length, false);
codePointLength = StringAttributes.getCodePointLength(attrs);
codeRange = StringAttributes.getCodeRange(attrs);
stride = Stride.fromCodeRangeUTF16(codeRange);
if (copy || stride == 0) {
offset = 0;
array = new byte[length << stride];
if (utf16CompactProfile.profile(stride == 0)) {
TStringOps.arraycopyWithStride(this, arrayA, offsetA, 1, 0, array, offset, 0, 0, length);
} else {
TStringOps.arraycopyWithStride(this, arrayA, offsetA, 1, 0, array, offset, 1, 0, length);
}
} else {
offset = offsetA;
array = arrayA;
}
} else if (utf32Profile.profile(isUTF32(encoding))) {
checkByteLengthUTF32(byteLength);
length = byteLength >> 2;
codeRange = TStringOps.calcStringAttributesUTF32(this, arrayA, offsetA, length);
codePointLength = length;
stride = Stride.fromCodeRangeUTF32(codeRange);
if (copy || stride < 2) {
offset = 0;
array = new byte[length << stride];
if (utf32Compact0Profile.profile(stride == 0)) {
TStringOps.arraycopyWithStride(this, arrayA, offsetA, 2, 0, array, offset, 0, 0, length);
} else if (utf32Compact1Profile.profile(stride == 1)) {
TStringOps.arraycopyWithStride(this, arrayA, offsetA, 2, 0, array, offset, 1, 0, length);
} else {
TStringOps.arraycopyWithStride(this, arrayA, offsetA, 2, 0, array, offset, 2, 0, length);
}
} else {
offset = offsetA;
array = arrayA;
}
} else {
length = byteLength;
stride = 0;
if (utf8Profile.profile(isUTF8(encoding))) {
long attrs = TStringOps.calcStringAttributesUTF8(this, arrayA, offsetA, length, false, false, utf8BrokenProfile);
codeRange = StringAttributes.getCodeRange(attrs);
codePointLength = StringAttributes.getCodePointLength(attrs);
} else if (asciiLatinBytesProfile.profile(isAsciiBytesOrLatin1(encoding))) {
int cr = TStringOps.calcStringAttributesLatin1(this, arrayA, offsetA, length);
codeRange = is8Bit(cr) ? TSCodeRange.asciiLatinBytesNonAsciiCodeRange(encoding) : cr;
codePointLength = length;
} else {
if (arrayA instanceof NativePointer) {
((NativePointer) arrayA).materializeByteArray(length << stride, ConditionProfile.getUncached());
}
long attrs = JCodings.getInstance().calcStringAttributes(this, arrayA, offsetA, length, encoding, exoticValidProfile, exoticFixedWidthProfile);
codeRange = StringAttributes.getCodeRange(attrs);
codePointLength = StringAttributes.getCodePointLength(attrs);
}
if (copy) {
offset = 0;
array = TStringOps.arraycopyOfWithStride(this, arrayA, offsetA, length, 0, length, 0);
} else {
offset = offsetA;
array = arrayA;
}
}
return TruffleString.createFromArray(array, offset, length, stride, encoding, codePointLength, codeRange, isCacheHead);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class FromBufferWithStringCompactionKnownAttributesNode extends Node {
abstract TruffleString execute(Object arrayA, int offsetA, int byteLength, Encoding encoding, int codePointLength, int codeRange);
@Specialization
TruffleString fromBufferWithStringCompaction(Object arrayA, int offsetA, int byteLength, Encoding encoding, int codePointLength, int codeRange,
@Cached ConditionProfile utf16Profile,
@Cached ConditionProfile utf16CompactProfile,
@Cached ConditionProfile utf32Profile,
@Cached ConditionProfile utf32Compact0Profile,
@Cached ConditionProfile utf32Compact1Profile) {
if (byteLength == 0) {
return encoding.getEmpty();
}
final int offset = 0;
final int length;
final int stride;
final Object array;
if (utf16Profile.profile(isUTF16(encoding))) {
checkByteLengthUTF16(byteLength);
length = byteLength >> 1;
stride = Stride.fromCodeRangeUTF16(codeRange);
array = new byte[length << stride];
if (utf16CompactProfile.profile(stride == 0)) {
TStringOps.arraycopyWithStride(this, arrayA, offsetA, 1, 0, array, offset, 0, 0, length);
} else {
TStringOps.arraycopyWithStride(this, arrayA, offsetA, 1, 0, array, offset, 1, 0, length);
}
} else if (utf32Profile.profile(isUTF32(encoding))) {
checkByteLengthUTF32(byteLength);
length = byteLength >> 2;
stride = Stride.fromCodeRangeUTF32(codeRange);
array = new byte[length << stride];
if (utf32Compact0Profile.profile(stride == 0)) {
TStringOps.arraycopyWithStride(this, arrayA, offsetA, 2, 0, array, offset, 0, 0, length);
} else if (utf32Compact1Profile.profile(stride == 1)) {
TStringOps.arraycopyWithStride(this, arrayA, offsetA, 2, 0, array, offset, 1, 0, length);
} else {
TStringOps.arraycopyWithStride(this, arrayA, offsetA, 2, 0, array, offset, 2, 0, length);
}
} else {
length = byteLength;
stride = 0;
array = TStringOps.arraycopyOfWithStride(this, arrayA, offsetA, length, 0, length, 0);
}
return TruffleString.createFromArray(array, offset, length, stride, encoding, codePointLength, codeRange, true);
}
static FromBufferWithStringCompactionKnownAttributesNode getUncached() {
return TStringInternalNodesFactory.FromBufferWithStringCompactionKnownAttributesNodeGen.getUncached();
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class FromNativePointerNode extends Node {
abstract TruffleString execute(NativePointer pointer, int byteOffset, int byteLength, Encoding encoding, boolean isCacheHead);
@Specialization
TruffleString fromNativePointerInternal(NativePointer pointer, int byteOffset, int byteLength, Encoding encoding, boolean isCacheHead,
@Cached ConditionProfile asciiLatinBytesProfile,
@Cached ConditionProfile utf8Profile,
@Cached ConditionProfile utf8BrokenProfile,
@Cached ConditionProfile utf16Profile,
@Cached ConditionProfile utf32Profile,
@Cached ConditionProfile exoticValidProfile,
@Cached ConditionProfile exoticFixedWidthProfile) {
if (byteLength == 0) {
return encoding.getEmpty();
}
final int length;
final int stride;
final int codePointLength;
final int codeRange;
if (utf16Profile.profile(isUTF16(encoding))) {
checkByteLengthUTF16(byteLength);
length = byteLength >> 1;
long attrs = TStringOps.calcStringAttributesUTF16(this, pointer, byteOffset, length, false);
codePointLength = StringAttributes.getCodePointLength(attrs);
codeRange = StringAttributes.getCodeRange(attrs);
stride = 1;
} else if (utf32Profile.profile(isUTF32(encoding))) {
checkByteLengthUTF32(byteLength);
length = byteLength >> 2;
codeRange = TStringOps.calcStringAttributesUTF32(this, pointer, byteOffset, length);
codePointLength = length;
stride = 2;
} else {
length = byteLength;
stride = 0;
if (utf8Profile.profile(isUTF8(encoding))) {
long attrs = TStringOps.calcStringAttributesUTF8(this, pointer, byteOffset, length, false, false, utf8BrokenProfile);
codeRange = StringAttributes.getCodeRange(attrs);
codePointLength = StringAttributes.getCodePointLength(attrs);
} else if (asciiLatinBytesProfile.profile(isAsciiBytesOrLatin1(encoding))) {
int cr = TStringOps.calcStringAttributesLatin1(this, pointer, byteOffset, length);
codeRange = is8Bit(cr) ? TSCodeRange.asciiLatinBytesNonAsciiCodeRange(encoding) : cr;
codePointLength = length;
} else {
pointer.materializeByteArray(byteLength, ConditionProfile.getUncached());
long attrs = JCodings.getInstance().calcStringAttributes(this, pointer, byteOffset, length, encoding, exoticValidProfile, exoticFixedWidthProfile);
codeRange = StringAttributes.getCodeRange(attrs);
codePointLength = StringAttributes.getCodePointLength(attrs);
}
}
return TruffleString.createFromArray(pointer, byteOffset, length, stride, encoding, codePointLength, codeRange, isCacheHead);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class ByteLengthOfCodePointNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int index, ErrorHandling errorHandling);
@SuppressWarnings("unused")
@Specialization(guards = {"isFixedWidth(codeRangeA)", "isBestEffort(errorHandling)"})
int doFixed(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int index, ErrorHandling errorHandling) {
return 1 << encoding.naturalStride;
}
@SuppressWarnings("unused")
@Specialization(guards = {"isUpToValidFixedWidth(codeRangeA)", "isReturnNegative(errorHandling)"})
int doFixedValidReturnNegative(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int index, ErrorHandling errorHandling) {
return 1 << encoding.naturalStride;
}
@SuppressWarnings("unused")
@Specialization(guards = {"isAscii(encoding)", "isBrokenFixedWidth(codeRangeA)", "isReturnNegative(errorHandling)"})
int doASCIIBrokenReturnNegative(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int index, ErrorHandling errorHandling) {
assert isStride0(a);
return readS0(a, arrayA, index) < 0x80 ? 1 : -1;
}
@SuppressWarnings("unused")
@Specialization(guards = {"isUTF32(encoding)", "isBrokenFixedWidth(codeRangeA)", "isReturnNegative(errorHandling)"})
int doUTF32BrokenReturnNegative(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int index, ErrorHandling errorHandling,
@Cached CodePointAtRawNode codePointAtRawNode) {
return codePointAtRawNode.execute(a, arrayA, codeRangeA, encoding, index, ErrorHandling.RETURN_NEGATIVE) < 0 ? -1 : 4;
}
@SuppressWarnings("unused")
@Specialization(guards = {"isUTF8(encoding)", "isValidMultiByte(codeRangeA)"})
int utf8Valid(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int index, ErrorHandling errorHandling) {
assert isStride0(a);
int firstByte = readS0(a, arrayA, index);
return firstByte <= 0x7f ? 1 : Encodings.utf8CodePointLength(firstByte);
}
@SuppressWarnings("unused")
@Specialization(guards = {"isUTF8(encoding)", "isBrokenMultiByte(codeRangeA)"})
int utf8Broken(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int index, ErrorHandling errorHandling) {
assert isStride0(a);
return Encodings.utf8GetCodePointLength(a, arrayA, index, errorHandling);
}
@SuppressWarnings("unused")
@Specialization(guards = {"isUTF16(encoding)", "isValidMultiByte(codeRangeA)"})
int utf16Valid(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int index, ErrorHandling errorHandling) {
assert isStride1(a);
return Encodings.isUTF16HighSurrogate(TStringOps.readS1(a, arrayA, index)) ? 4 : 2;
}
@SuppressWarnings("unused")
@Specialization(guards = {"isUTF16(encoding)", "isBrokenMultiByte(codeRangeA)"})
int utf16Broken(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int index, ErrorHandling errorHandling) {
assert isStride1(a);
return Encodings.utf16BrokenGetCodePointByteLength(a, arrayA, index, errorHandling);
}
@SuppressWarnings("unused")
@TruffleBoundary
@Specialization(guards = "isUnsupportedEncoding(encoding)")
int unsupported(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int index, ErrorHandling errorHandling) {
assert isStride0(a);
JCodings.Encoding jCoding = JCodings.getInstance().get(encoding);
int cpLength = JCodings.getInstance().getCodePointLength(jCoding, JCodings.asByteArray(arrayA), a.byteArrayOffset() + index, a.byteArrayOffset() + a.length());
int regionLength = a.length() - index;
if (errorHandling == ErrorHandling.BEST_EFFORT) {
if (cpLength > 0 && cpLength <= regionLength) {
return cpLength;
} else {
return Math.min(JCodings.getInstance().minLength(jCoding), regionLength);
}
} else {
assert errorHandling == ErrorHandling.RETURN_NEGATIVE;
if (cpLength <= regionLength) {
return cpLength;
} else {
return -1 - (cpLength - regionLength);
}
}
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class RawIndexToCodePointIndexNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int offset, int index);
@SuppressWarnings("unused")
@Specialization(guards = "isFixedWidth(codeRangeA)")
int doFixed(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int offset, int index) {
return index;
}
@Specialization(guards = {"isUTF8(encoding)", "isValidMultiByte(codeRangeA)"})
int utf8Valid(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int offset, int index,
@Cached ConditionProfile brokenProfile) {
return StringAttributes.getCodePointLength(TStringOps.calcStringAttributesUTF8(this, arrayA, offset, index, true, offset + index == a.offset() + a.length(), brokenProfile));
}
@Specialization(guards = {"isUTF8(encoding)", "isBrokenMultiByte(codeRangeA)"})
int utf8Broken(@SuppressWarnings("unused") AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int offset,
int index,
@Cached ConditionProfile brokenProfile) {
return StringAttributes.getCodePointLength(TStringOps.calcStringAttributesUTF8(this, arrayA, offset, index, false, false, brokenProfile));
}
@Specialization(guards = {"isUTF16(encoding)", "isValidMultiByte(codeRangeA)"})
int utf16Valid(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int offset, int index) {
assert isStride1(a);
return StringAttributes.getCodePointLength(TStringOps.calcStringAttributesUTF16(this, arrayA, offset, index, true));
}
@Specialization(guards = {"isUTF16(encoding)", "isBrokenMultiByte(codeRangeA)"})
int utf16Broken(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int offset, int index) {
assert isStride1(a);
return StringAttributes.getCodePointLength(TStringOps.calcStringAttributesUTF16(this, arrayA, offset, index, false));
}
@TruffleBoundary
@Specialization(guards = "isUnsupportedEncoding(encoding)")
int unsupported(@SuppressWarnings("unused") AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, Encoding encoding, int offset, int index,
@Cached ConditionProfile validProfile,
@Cached ConditionProfile fixedWidthProfile) {
return StringAttributes.getCodePointLength(JCodings.getInstance().calcStringAttributes(this, arrayA, offset, index, encoding, validProfile, fixedWidthProfile));
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class CodePointIndexToRawNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int extraOffsetRaw, int index, boolean isLength);
@SuppressWarnings("unused")
@Specialization(guards = "isFixedWidth(codeRangeA)")
int doFixed(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int extraOffsetRaw, int index, boolean isLength) {
return index;
}
@Specialization(guards = {"isUTF8(encoding)", "isValidMultiByte(codeRangeA)"})
int utf8Valid(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int extraOffsetRaw, int index,
boolean isLength) {
assert isStride0(a);
int cpi = 0;
for (int i = extraOffsetRaw; i < a.length(); i++) {
if (!isUTF8ContinuationByte(TStringOps.readS0(a, arrayA, i))) {
if (cpi == index) {
return i - extraOffsetRaw;
}
cpi++;
}
TStringConstants.truffleSafePointPoll(this, i + 1);
}
return atEnd(a, extraOffsetRaw, index, isLength, cpi);
}
@Specialization(guards = {"isUTF8(encoding)", "isBrokenMultiByte(codeRangeA)"})
int utf8Broken(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int extraOffsetRaw, int index,
boolean isLength) {
assert isStride0(a);
int cpi = 0;
for (int i = extraOffsetRaw; i < a.length(); i += Encodings.utf8GetCodePointLength(a, arrayA, i, ErrorHandling.BEST_EFFORT)) {
if (cpi == index) {
return i - extraOffsetRaw;
}
cpi++;
TStringConstants.truffleSafePointPoll(this, cpi);
}
return atEnd(a, extraOffsetRaw, index, isLength, cpi);
}
@Specialization(guards = {"isUTF16(encoding)", "isValidMultiByte(codeRangeA)"})
int utf16Valid(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int extraOffsetRaw, int index,
boolean isLength) {
assert isStride1(a);
int cpi = 0;
for (int i = extraOffsetRaw; i < a.length(); i++) {
if (!Encodings.isUTF16LowSurrogate(TStringOps.readS1(a, arrayA, i))) {
if (cpi == index) {
return i - extraOffsetRaw;
}
cpi++;
}
TStringConstants.truffleSafePointPoll(this, i + 1);
}
return atEnd(a, extraOffsetRaw, index, isLength, cpi);
}
@Specialization(guards = {"isUTF16(encoding)", "isBrokenMultiByte(codeRangeA)"})
int utf16Broken(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int extraOffsetRaw, int index,
boolean isLength) {
assert isStride1(a);
int cpi = 0;
for (int i = extraOffsetRaw; i < a.length(); i++) {
if (!(i > extraOffsetRaw && Encodings.isUTF16LowSurrogate(TStringOps.readS1(a, arrayA, i)) && Encodings.isUTF16HighSurrogate(TStringOps.readS1(a, arrayA, i - 1)))) {
if (cpi == index) {
return i - extraOffsetRaw;
}
cpi++;
}
TStringConstants.truffleSafePointPoll(this, i + 1);
}
return atEnd(a, extraOffsetRaw, index, isLength, cpi);
}
@TruffleBoundary
@Specialization(guards = "isUnsupportedEncoding(encoding)")
int unsupported(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, Encoding encoding, int extraOffsetRaw, int index, boolean isLength) {
JCodings.Encoding jCoding = JCodings.getInstance().get(encoding);
return JCodings.getInstance().codePointIndexToRaw(this, a, JCodings.asByteArray(arrayA), extraOffsetRaw, index, isLength, jCoding);
}
static int atEnd(AbstractTruffleString a, int extraOffsetRaw, int index, boolean isLength, int cpi) {
if (isLength && cpi == index) {
return a.length() - extraOffsetRaw;
}
throw InternalErrors.indexOutOfBounds();
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class ReadByteNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int i, Encoding encoding);
@Specialization(guards = "isUTF16(encoding)")
static int doUTF16(AbstractTruffleString a, Object arrayA, int i, @SuppressWarnings("unused") Encoding encoding,
@Cached ConditionProfile stride0Profile) {
a.boundsCheckByteIndexUTF16(i);
final int index;
if (stride0Profile.profile(isStride0(a))) {
// simplified from:
// (TStringGuards.bigEndian() ? (i & 1) == 0 : (i & 1) != 0)
if ((TStringGuards.bigEndian()) == ((i & 1) == 0)) {
return 0;
} else {
index = i >> 1;
}
} else {
assert isStride1(a);
index = i;
}
return TStringOps.readS0(arrayA, a.offset(), a.length() << a.stride(), index);
}
@Specialization(guards = "isUTF32(encoding)")
static int doUTF32(AbstractTruffleString a, Object arrayA, int i, @SuppressWarnings("unused") Encoding encoding,
@Cached ConditionProfile stride0Profile,
@Cached ConditionProfile stride1Profile) {
a.boundsCheckByteIndexUTF32(i);
final int index;
if (stride0Profile.profile(isStride0(a))) {
if ((i & 3) != (TStringGuards.bigEndian() ? 3 : 0)) {
return 0;
} else {
index = i >> 2;
}
} else if (stride1Profile.profile(isStride1(a))) {
// simplified from:
// (TStringGuards.bigEndian() ? (i & 2) == 0 : (i & 2) != 0)
if ((TStringGuards.bigEndian()) == ((i & 2) == 0)) {
return 0;
} else {
if (TStringGuards.bigEndian()) {
index = (i >> 1) | (i & 1);
} else {
index = ((i >> 1) & ~1) | (i & 1);
}
}
} else {
assert isStride2(a);
index = i;
}
return TStringOps.readS0(arrayA, a.offset(), a.length() << a.stride(), index);
}
@Specialization(guards = "!isUTF16Or32(encoding)")
static int doRest(AbstractTruffleString a, Object arrayA, int i, @SuppressWarnings("unused") Encoding encoding) {
a.boundsCheckByteIndexS0(i);
return TStringOps.readS0(a, arrayA, i);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class CodePointAtNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int i, ErrorHandling errorHandling);
@Specialization(guards = "isUTF16(encoding)")
int utf16(AbstractTruffleString a, Object arrayA, int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int i, ErrorHandling errorHandling,
@Cached ConditionProfile fixedWidthProfile,
@Cached ConditionProfile stride0Profile,
@Cached ConditionProfile validProfile) {
if (fixedWidthProfile.profile(isFixedWidth(codeRangeA))) {
if (stride0Profile.profile(isStride0(a))) {
return TStringOps.readS0(a, arrayA, i);
} else {
assert isStride1(a);
return TStringOps.readS1(a, arrayA, i);
}
} else if (validProfile.profile(isValidMultiByte(codeRangeA))) {
assert isStride1(a);
return Encodings.utf16DecodeValid(a, arrayA, Encodings.utf16ValidCodePointToCharIndex(this, a, arrayA, i));
} else {
assert isStride1(a);
assert TStringGuards.isBrokenMultiByte(codeRangeA);
return Encodings.utf16DecodeBroken(a, arrayA, Encodings.utf16BrokenCodePointToCharIndex(this, a, arrayA, i), errorHandling);
}
}
@Specialization(guards = "isUTF32(encoding)")
static int utf32(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int i, ErrorHandling errorHandling,
@Cached ConditionProfile stride0Profile,
@Cached ConditionProfile stride1Profile) {
return CodePointAtRawNode.utf32(a, arrayA, codeRangeA, encoding, i, errorHandling, stride0Profile, stride1Profile);
}
@Specialization(guards = "isUTF8(encoding)")
int utf8(AbstractTruffleString a, Object arrayA, int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int i, ErrorHandling errorHandling,
@Cached ConditionProfile fixedWidthProfile,
@Cached ConditionProfile validProfile) {
if (fixedWidthProfile.profile(is7Bit(codeRangeA))) {
return TStringOps.readS0(a, arrayA, i);
} else {
int byteIndex = Encodings.utf8CodePointToByteIndex(this, a, arrayA, i);
if (validProfile.profile(isValidMultiByte(codeRangeA))) {
return Encodings.utf8DecodeValid(a, arrayA, byteIndex);
} else {
assert TStringGuards.isBrokenMultiByte(codeRangeA);
return Encodings.utf8DecodeBroken(a, arrayA, byteIndex, errorHandling);
}
}
}
@Specialization(guards = {"!isUTF16Or32(encoding)", "!isUTF8(encoding)", "isBytes(encoding) || is7Or8Bit(codeRangeA)"})
static int doFixed(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int i,
@SuppressWarnings("unused") ErrorHandling errorHandling) {
return CodePointAtRawNode.doFixed(a, arrayA, codeRangeA, encoding, i, errorHandling);
}
@Specialization(guards = {"isAscii(encoding)", "!is7Or8Bit(codeRangeA)"})
static int doAsciiBroken(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int i,
ErrorHandling errorHandling) {
return CodePointAtRawNode.doAsciiBroken(a, arrayA, codeRangeA, encoding, i, errorHandling);
}
@Specialization(guards = {"isUnsupportedEncoding(encoding)", "!is7Or8Bit(codeRangeA)"})
int unsupported(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, Encoding encoding, int i, ErrorHandling errorHandling) {
assert isStride0(a);
JCodings.Encoding jCoding = JCodings.getInstance().get(encoding);
byte[] bytes = JCodings.asByteArray(arrayA);
return JCodings.getInstance().decode(a, bytes, JCodings.getInstance().codePointIndexToRaw(this, a, bytes, 0, i, false, jCoding), jCoding, errorHandling);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class CodePointAtRawNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int i, ErrorHandling errorHandling);
@Specialization(guards = "isUTF16(encoding)")
static int utf16(AbstractTruffleString a, Object arrayA, int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int i, ErrorHandling errorHandling,
@Cached ConditionProfile fixedWidthProfile,
@Cached ConditionProfile validProfile,
@Cached ConditionProfile stride0Profile) {
if (fixedWidthProfile.profile(isFixedWidth(codeRangeA))) {
if (stride0Profile.profile(isStride0(a))) {
return TStringOps.readS0(a, arrayA, i);
} else {
assert isStride1(a);
return TStringOps.readS1(a, arrayA, i);
}
} else if (validProfile.profile(isValidMultiByte(codeRangeA))) {
return Encodings.utf16DecodeValid(a, arrayA, i);
} else {
assert TStringGuards.isBrokenMultiByte(codeRangeA);
return Encodings.utf16DecodeBroken(a, arrayA, i, errorHandling);
}
}
@Specialization(guards = "isUTF32(encoding)")
static int utf32(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int i, ErrorHandling errorHandling,
@Cached ConditionProfile stride0Profile,
@Cached ConditionProfile stride1Profile) {
if (stride0Profile.profile(isStride0(a))) {
return TStringOps.readS0(a, arrayA, i);
} else if (stride1Profile.profile(isStride1(a))) {
char c = TStringOps.readS1(a, arrayA, i);
if (errorHandling == ErrorHandling.RETURN_NEGATIVE && Encodings.isUTF16Surrogate(c)) {
return -1;
}
return c;
} else {
assert isStride2(a);
int c = TStringOps.readS2(a, arrayA, i);
if (errorHandling == ErrorHandling.RETURN_NEGATIVE && !Encodings.isValidUnicodeCodepoint(c)) {
return -1;
}
return c;
}
}
@Specialization(guards = "isUTF8(encoding)")
static int utf8(AbstractTruffleString a, Object arrayA, int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int i, ErrorHandling errorHandling,
@Cached ConditionProfile fixedWidthProfile,
@Cached ConditionProfile validProfile) {
if (fixedWidthProfile.profile(is7Bit(codeRangeA))) {
return TStringOps.readS0(a, arrayA, i);
} else if (validProfile.profile(isValidMultiByte(codeRangeA))) {
return Encodings.utf8DecodeValid(a, arrayA, i);
} else {
assert TStringGuards.isBrokenMultiByte(codeRangeA);
return Encodings.utf8DecodeBroken(a, arrayA, i, errorHandling);
}
}
@Specialization(guards = {"!isUTF16Or32(encoding)", "!isUTF8(encoding)", "isBytes(encoding) || is7Or8Bit(codeRangeA)"})
static int doFixed(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int i,
@SuppressWarnings("unused") ErrorHandling errorHandling) {
assert isStride0(a);
return TStringOps.readS0(a, arrayA, i);
}
@Specialization(guards = {"isAscii(encoding)", "!is7Or8Bit(codeRangeA)"})
static int doAsciiBroken(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int i,
ErrorHandling errorHandling) {
assert isStride0(a);
int c = readS0(a, arrayA, i);
if (errorHandling == ErrorHandling.RETURN_NEGATIVE && c > 0x7f) {
return -1;
}
assert errorHandling == ErrorHandling.BEST_EFFORT;
return c;
}
@Specialization(guards = {"isUnsupportedEncoding(encoding)", "!is7Or8Bit(codeRangeA)"})
static int unsupported(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int i, ErrorHandling errorHandling) {
return JCodings.getInstance().decode(a, JCodings.asByteArray(arrayA), i, JCodings.getInstance().get(encoding), errorHandling);
}
}
static int indexOfFixedWidth(AbstractTruffleString a, Object arrayA, int codeRangeA, int codepoint, int fromIndex, int toIndex,
TStringOpsNodes.RawIndexOfCodePointNode indexOfNode) {
if (fromIndex == toIndex || !TSCodeRange.isInCodeRange(codepoint, codeRangeA)) {
return -1;
}
return indexOfNode.execute(a, arrayA, codepoint, fromIndex, toIndex);
}
static int lastIndexOfFixedWidth(AbstractTruffleString a, Object arrayA, int codeRangeA, int codepoint, int fromIndex, int toIndex,
TStringOpsNodes.RawLastIndexOfCodePointNode indexOfNode) {
if (fromIndex == toIndex || !TSCodeRange.isInCodeRange(codepoint, codeRangeA)) {
return -1;
}
return indexOfNode.execute(a, arrayA, codepoint, fromIndex, toIndex);
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class IndexOfCodePointNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int codepoint, int fromIndex, int toIndex);
@Specialization(guards = "isFixedWidth(codeRangeA)")
static int doFixedWidth(AbstractTruffleString a, Object arrayA, int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int codepoint, int fromIndex, int toIndex,
@Cached TStringOpsNodes.RawIndexOfCodePointNode indexOfNode) {
return indexOfFixedWidth(a, arrayA, codeRangeA, codepoint, fromIndex, toIndex, indexOfNode);
}
@Specialization(guards = "!isFixedWidth(codeRangeA)")
int decode(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int codepoint, int fromIndex, int toIndex,
@Cached TruffleStringIterator.NextNode nextNode) {
return TruffleStringIterator.indexOf(this, AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, encoding), codepoint, fromIndex, toIndex, nextNode);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class IndexOfCodePointRawNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int codepoint, int fromIndex, int toIndex);
@Specialization(guards = {"isFixedWidth(codeRangeA)"})
static int utf8Fixed(AbstractTruffleString a, Object arrayA, int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int codepoint, int fromIndex, int toIndex,
@Cached TStringOpsNodes.RawIndexOfCodePointNode indexOfNode) {
return indexOfFixedWidth(a, arrayA, codeRangeA, codepoint, fromIndex, toIndex, indexOfNode);
}
@Specialization(guards = {"isUTF8(encoding)", "!isFixedWidth(codeRangeA)"})
int utf8Variable(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int codepoint, int fromIndex, int toIndex) {
assert isStride0(a);
int encodedSize = Encodings.utf8EncodedSize(codepoint);
if (encodedSize > toIndex - fromIndex) {
return -1;
}
if (encodedSize == 1) {
return TStringOps.indexOfCodePointWithStride(this, a, arrayA, 0, fromIndex, toIndex, codepoint);
}
byte[] encoded = Encodings.utf8EncodeNonAscii(codepoint, encodedSize);
TruffleString b = TruffleString.createFromByteArray(encoded, encoded.length, 0, Encoding.UTF_8, 1, TSCodeRange.getValidMultiByte());
return TStringOps.indexOfStringWithOrMaskWithStride(this, a, arrayA, 0, b, encoded, 0, fromIndex, toIndex, null);
}
@Specialization(guards = {"isUTF16(encoding)", "!isFixedWidth(codeRangeA)"})
int utf16Variable(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int codepoint, int fromIndex,
int toIndex) {
assert isStride1(a);
int encodedSize = Encodings.utf16EncodedSize(codepoint);
if (encodedSize > toIndex - fromIndex) {
return -1;
}
if (encodedSize == 1) {
return TStringOps.indexOfCodePointWithStride(this, a, arrayA, 1, fromIndex, toIndex, codepoint);
}
return TStringOps.indexOf2ConsecutiveWithStride(
this, a, arrayA, 1, fromIndex, toIndex, Character.highSurrogate(codepoint), Character.lowSurrogate(codepoint));
}
@Specialization(guards = {"isUnsupportedEncoding(encoding)", "!isFixedWidth(codeRangeA)"})
int unsupported(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int codepoint, int fromIndex, int toIndex,
@Cached TruffleStringIterator.NextNode nextNode) {
final TruffleStringIterator it = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, encoding);
it.setRawIndex(fromIndex);
int loopCount = 0;
while (it.hasNext() && it.getRawIndex() < toIndex) {
int ret = it.getRawIndex();
if (nextNode.execute(it) == codepoint) {
return ret;
}
TStringConstants.truffleSafePointPoll(this, ++loopCount);
}
return -1;
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class LastIndexOfCodePointNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int codepoint, int fromIndex, int toIndex);
@Specialization(guards = "isFixedWidth(codeRangeA)")
static int doFixedWidth(AbstractTruffleString a, Object arrayA, int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int codepoint, int fromIndex, int toIndex,
@Cached TStringOpsNodes.RawLastIndexOfCodePointNode lastIndexOfNode) {
return lastIndexOfFixedWidth(a, arrayA, codeRangeA, codepoint, fromIndex, toIndex, lastIndexOfNode);
}
@Specialization(guards = "!isFixedWidth(codeRangeA)")
int decode(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int codepoint, int fromIndex, int toIndex,
@Cached TruffleStringIterator.NextNode nextNode) {
return TruffleStringIterator.lastIndexOf(this, AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, encoding), codepoint, fromIndex, toIndex, nextNode);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class LastIndexOfCodePointRawNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int codepoint, int fromIndex, int toIndex);
@Specialization(guards = {"isFixedWidth(codeRangeA)"})
static int utf8Fixed(AbstractTruffleString a, Object arrayA, int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int codepoint, int fromIndex, int toIndex,
@Cached @Shared("lastIndexOfNode") TStringOpsNodes.RawLastIndexOfCodePointNode lastIndexOfNode) {
return lastIndexOfFixedWidth(a, arrayA, codeRangeA, codepoint, fromIndex, toIndex, lastIndexOfNode);
}
@Specialization(guards = {"isUTF8(encoding)", "!isFixedWidth(codeRangeA)"})
int utf8Variable(AbstractTruffleString a, Object arrayA, int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int codepoint, int fromIndex, int toIndex,
@Cached @Shared("lastIndexOfNode") TStringOpsNodes.RawLastIndexOfCodePointNode lastIndexOfNode) {
int encodedSize = Encodings.utf8EncodedSize(codepoint);
if (encodedSize > fromIndex - toIndex) {
return -1;
}
if (encodedSize == 1) {
return lastIndexOfFixedWidth(a, arrayA, codeRangeA, codepoint, fromIndex, toIndex, lastIndexOfNode);
}
byte[] encoded = Encodings.utf8EncodeNonAscii(codepoint, encodedSize);
TruffleString b = TruffleString.createFromByteArray(encoded, encoded.length, 0, Encoding.UTF_8, 1, TSCodeRange.getValidMultiByte());
return TStringOps.lastIndexOfStringWithOrMaskWithStride(this, a, arrayA, 0, b, encoded, 0, fromIndex, toIndex, null);
}
@Specialization(guards = {"isUTF16(encoding)", "!isFixedWidth(codeRangeA)"})
int utf16Variable(AbstractTruffleString a, Object arrayA, int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int codepoint, int fromIndex, int toIndex,
@Cached @Shared("lastIndexOfNode") TStringOpsNodes.RawLastIndexOfCodePointNode lastIndexOfNode) {
assert isStride1(a);
int encodedSize = Encodings.utf16EncodedSize(codepoint);
if (encodedSize > fromIndex - toIndex) {
return -1;
}
if (encodedSize == 1) {
return lastIndexOfFixedWidth(a, arrayA, codeRangeA, codepoint, fromIndex, toIndex, lastIndexOfNode);
}
return TStringOps.lastIndexOf2ConsecutiveWithOrMaskWithStride(
this, a, arrayA, 1, fromIndex, toIndex, Character.highSurrogate(codepoint), Character.lowSurrogate(codepoint), 0, 0);
}
@Specialization(guards = {"isUnsupportedEncoding(encoding)", "!isFixedWidth(codeRangeA)"})
int unsupported(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int codepoint, int fromIndex, int toIndex,
@Cached TruffleStringIterator.PreviousNode prevNode) {
final TruffleStringIterator it = TruffleString.backwardIterator(a, arrayA, codeRangeA, encoding);
it.setRawIndex(fromIndex);
int loopCount = 0;
while (it.hasPrevious() && it.getRawIndex() >= toIndex) {
if (prevNode.execute(it) == codepoint) {
return it.getRawIndex();
}
TStringConstants.truffleSafePointPoll(this, ++loopCount);
}
return -1;
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class SubstringNode extends Node {
abstract TruffleString execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int fromIndex, int length, boolean lazy);
@SuppressWarnings("unused")
@Specialization(guards = "length == 0")
static TruffleString lengthZero(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int fromIndex, int length, boolean lazy) {
return encoding.getEmpty();
}
@SuppressWarnings("unused")
@Specialization(guards = {"fromIndex == 0", "length == length(a)"})
static TruffleString sameStr(TruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int fromIndex, int length, boolean lazy) {
return a;
}
@Specialization(guards = {"length > 0", "length != length(a) || a.isMutable()", "!lazy"})
static TruffleString materializeSubstring(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int fromIndex, int length, @SuppressWarnings("unused") boolean lazy,
@Cached CreateSubstringNode createSubstringNode) {
return createSubstringNode.execute(a, arrayA, a.offset() + (fromIndex << a.stride()), length, a.stride(), encoding, codeRangeA);
}
@Specialization(guards = {"length > 0", "length != length(a)", "lazy"})
TruffleString createLazySubstring(TruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int fromIndex, int length, @SuppressWarnings("unused") boolean lazy,
@Cached CalcStringAttributesNode calcAttributesNode,
@Cached ConditionProfile stride1MustMaterializeProfile,
@Cached ConditionProfile stride2MustMaterializeProfile) {
int lazyOffset = a.offset() + (fromIndex << a.stride());
long attrs = calcAttributesNode.execute(a, arrayA, lazyOffset, length, a.stride(), encoding, codeRangeA);
int codeRange = StringAttributes.getCodeRange(attrs);
int codePointLength = StringAttributes.getCodePointLength(attrs);
final Object array;
final int offset;
final int stride;
if (stride1MustMaterializeProfile.profile(a.stride() == 1 && TSCodeRange.isMoreRestrictiveOrEqual(codeRange, TSCodeRange.get8Bit()))) {
assert isUTF16Or32(encoding);
stride = 0;
offset = 0;
final byte[] newBytes = new byte[length];
TStringOps.arraycopyWithStride(this,
arrayA, lazyOffset, 1, 0,
newBytes, offset, 0, 0, length);
array = newBytes;
} else if (stride2MustMaterializeProfile.profile(a.stride() == 2 && TSCodeRange.isMoreRestrictiveOrEqual(codeRange, TSCodeRange.get16Bit()))) {
// Always materialize 4-byte UTF-32 strings when they can be compacted. Otherwise,
// they could get re-interpreted as UTF-16 and break the assumption that all UTF-16
// strings are stride 0 or 1.
assert isUTF32(encoding);
stride = Stride.fromCodeRangeUTF32(StringAttributes.getCodeRange(attrs));
offset = 0;
final byte[] newBytes = new byte[length << stride];
if (stride == 0) {
TStringOps.arraycopyWithStride(this,
arrayA, lazyOffset, 2, 0,
newBytes, offset, 0, 0, length);
} else {
assert stride == 1;
TStringOps.arraycopyWithStride(this,
arrayA, lazyOffset, 2, 0,
newBytes, offset, 1, 0, length);
}
array = newBytes;
} else {
if (isUnsupportedEncoding(encoding) && arrayA instanceof NativePointer) {
// avoid conflicts in NativePointer#getBytes
array = ((NativePointer) arrayA).copy(lazyOffset);
} else {
array = arrayA;
}
offset = lazyOffset;
stride = a.stride();
}
return TruffleString.createFromArray(array, offset, length, stride, encoding, codePointLength, codeRange);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class ConcatEagerNode extends Node {
abstract TruffleString execute(AbstractTruffleString a, AbstractTruffleString b, Encoding encoding, int concatLength, int concatStride, int concatCodeRange);
@Specialization
static TruffleString concat(AbstractTruffleString a, AbstractTruffleString b, Encoding encoding, int concatLength, int concatStride, int concatCodeRange,
@Cached TruffleString.ToIndexableNode toIndexableNodeA,
@Cached TruffleString.ToIndexableNode toIndexableNodeB,
@Cached GetCodePointLengthNode getCodePointLengthANode,
@Cached GetCodePointLengthNode getCodePointLengthBNode,
@Cached ConcatMaterializeBytesNode materializeBytesNode,
@Cached CalcStringAttributesNode calculateAttributesNode,
@Cached ConditionProfile brokenProfile) {
final byte[] bytes = materializeBytesNode.execute(a, toIndexableNodeA.execute(a, a.data()), b, toIndexableNodeB.execute(b, b.data()), encoding, concatLength, concatStride);
final int codeRange;
final int codePointLength;
if (brokenProfile.profile(isBrokenMultiByte(concatCodeRange))) {
final long attrs = calculateAttributesNode.execute(null, bytes, 0, concatLength, concatStride, encoding, TSCodeRange.getUnknown());
codePointLength = StringAttributes.getCodePointLength(attrs);
codeRange = StringAttributes.getCodeRange(attrs);
} else {
codePointLength = getCodePointLengthANode.execute(a) + getCodePointLengthBNode.execute(b);
codeRange = concatCodeRange;
}
return TruffleString.createFromByteArray(bytes, concatLength, concatStride, encoding, codePointLength, codeRange);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class ConcatMaterializeBytesNode extends Node {
abstract byte[] execute(AbstractTruffleString a, Object arrayA, AbstractTruffleString b, Object arrayB, Encoding encoding, int concatLength, int concatStride);
@Specialization(guards = "isUTF16(encoding) || isUTF32(encoding)")
byte[] doWithCompression(AbstractTruffleString a, Object arrayA, AbstractTruffleString b, Object arrayB, @SuppressWarnings("unused") Encoding encoding, int concatLength, int concatStride) {
final byte[] bytes = new byte[concatLength << concatStride];
TStringOps.arraycopyWithStride(this,
arrayA, a.offset(), a.stride(), 0,
bytes, 0, concatStride, 0, a.length());
TStringOps.arraycopyWithStride(this,
arrayB, b.offset(), b.stride(), 0,
bytes, 0, concatStride, a.length(), b.length());
return bytes;
}
@Specialization(guards = {"!isUTF16(encoding)", "!isUTF32(encoding)"})
byte[] doNoCompression(AbstractTruffleString a, Object arrayA, AbstractTruffleString b, Object arrayB, @SuppressWarnings("unused") Encoding encoding, int concatLength, int concatStride) {
assert isStride0(a);
assert isStride0(b);
assert concatStride == 0;
final byte[] bytes = new byte[concatLength];
TStringOps.arraycopyWithStride(this,
arrayA, a.offset(), 0, 0,
bytes, 0, 0, 0, a.length());
TStringOps.arraycopyWithStride(this,
arrayB, b.offset(), 0, 0,
bytes, 0, 0, a.length(), b.length());
return bytes;
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class RegionEqualsNode extends Node {
abstract boolean execute(
AbstractTruffleString a, Object arrayA, int codeRangeA, int fromIndexA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndexB, int length, Encoding encoding);
@Specialization(guards = {"isFixedWidth(codeRangeA, codeRangeB)"})
boolean direct(
AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, int fromIndexA,
AbstractTruffleString b, Object arrayB, @SuppressWarnings("unused") int codeRangeB, int fromIndexB, int length, @SuppressWarnings("unused") Encoding encoding) {
return TStringOps.regionEqualsWithOrMaskWithStride(this, a, arrayA, a.stride(), fromIndexA, b, arrayB, b.stride(), fromIndexB, null, length);
}
@Specialization(guards = {"!isFixedWidth(codeRangeA, codeRangeB)"})
boolean decode(
AbstractTruffleString a, Object arrayA, int codeRangeA, int fromIndexA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndexB, int length, Encoding encoding,
@Cached TruffleStringIterator.NextNode nextNodeA,
@Cached TruffleStringIterator.NextNode nextNodeB) {
TruffleStringIterator aIt = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, encoding);
TruffleStringIterator bIt = AbstractTruffleString.forwardIterator(b, arrayB, codeRangeB, encoding);
for (int i = 0; i < fromIndexA; i++) {
if (!aIt.hasNext()) {
return false;
}
nextNodeA.execute(aIt);
TStringConstants.truffleSafePointPoll(this, i + 1);
}
for (int i = 0; i < fromIndexB; i++) {
if (!bIt.hasNext()) {
return false;
}
nextNodeB.execute(bIt);
TStringConstants.truffleSafePointPoll(this, i + 1);
}
for (int i = 0; i < length; i++) {
if (!(aIt.hasNext() && bIt.hasNext()) || nextNodeA.execute(aIt) != nextNodeB.execute(bIt)) {
return false;
}
TStringConstants.truffleSafePointPoll(this, i + 1);
}
return true;
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class IndexOfStringNode extends Node {
abstract int execute(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, Encoding encoding);
@Specialization(guards = {"isFixedWidth(codeRangeA, codeRangeB)"})
static int direct(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, @SuppressWarnings("unused") Encoding encoding,
@Cached RawIndexOfStringNode indexOfStringNode) {
assert !b.isEmpty() && !indexOfCannotMatch(codeRangeA, b, codeRangeB, toIndex - fromIndex, GetCodePointLengthNode.getUncached());
return indexOfStringNode.execute(a, arrayA, b, arrayB, fromIndex, toIndex, null);
}
@Specialization(guards = {"!isFixedWidth(codeRangeA, codeRangeB)"})
int decode(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, Encoding encoding,
@Cached TruffleStringIterator.NextNode nextNodeA,
@Cached TruffleStringIterator.NextNode nextNodeB) {
assert !b.isEmpty() && !indexOfCannotMatch(codeRangeA, b, codeRangeB, toIndex - fromIndex, GetCodePointLengthNode.getUncached());
TruffleStringIterator aIt = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, encoding);
TruffleStringIterator bIt = AbstractTruffleString.forwardIterator(b, arrayB, codeRangeB, encoding);
return TruffleStringIterator.indexOfString(this, aIt, bIt, fromIndex, toIndex, nextNodeA, nextNodeB);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class IndexOfStringRawNode extends Node {
abstract int execute(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, byte[] mask, Encoding encoding);
@Specialization(guards = {"isSupportedEncoding(encoding) || isFixedWidth(codeRangeA)"})
static int supported(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, byte[] mask, @SuppressWarnings("unused") Encoding encoding,
@Cached TStringOpsNodes.RawIndexOfStringNode indexOfStringNode) {
assert !b.isEmpty() && !indexOfCannotMatch(codeRangeA, b, codeRangeB, mask, toIndex - fromIndex);
return indexOfStringNode.execute(a, arrayA, b, arrayB, fromIndex, toIndex, mask);
}
@Specialization(guards = {"isUnsupportedEncoding(encoding)", "!isFixedWidth(codeRangeA)"})
int unsupported(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, @SuppressWarnings("unused") byte[] mask, Encoding encoding,
@Cached TruffleStringIterator.NextNode nextNodeA,
@Cached TruffleStringIterator.NextNode nextNodeB) {
assert mask == null;
assert !b.isEmpty() && !indexOfCannotMatch(codeRangeA, b, codeRangeB, mask, toIndex - fromIndex);
TruffleStringIterator aIt = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, encoding);
TruffleStringIterator bIt = AbstractTruffleString.forwardIterator(b, arrayB, codeRangeB, encoding);
return TruffleStringIterator.byteIndexOfString(this, aIt, bIt, fromIndex, toIndex, nextNodeA, nextNodeB);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class LastIndexOfStringNode extends Node {
abstract int execute(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, Encoding encoding);
@Specialization(guards = {"isFixedWidth(codeRangeA, codeRangeB)"})
static int direct(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, @SuppressWarnings("unused") Encoding encoding,
@Cached RawLastIndexOfStringNode indexOfStringNode) {
assert !b.isEmpty() && !indexOfCannotMatch(codeRangeA, b, codeRangeB, fromIndex - toIndex, GetCodePointLengthNode.getUncached());
return indexOfStringNode.execute(a, arrayA, b, arrayB, fromIndex, toIndex, null);
}
@Specialization(guards = {"!isFixedWidth(codeRangeA, codeRangeB)"})
int decode(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, Encoding encoding,
@Cached TruffleStringIterator.NextNode nextNodeA,
@Cached TruffleStringIterator.PreviousNode prevNodeA,
@Cached TruffleStringIterator.PreviousNode prevNodeB) {
assert !b.isEmpty() && !indexOfCannotMatch(codeRangeA, b, codeRangeB, fromIndex - toIndex, GetCodePointLengthNode.getUncached());
TruffleStringIterator aIt = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, encoding);
TruffleStringIterator bIt = AbstractTruffleString.backwardIterator(b, arrayB, codeRangeB, encoding);
return TruffleStringIterator.lastIndexOfString(this, aIt, bIt, fromIndex, toIndex, nextNodeA, prevNodeA, prevNodeB);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class LastIndexOfStringRawNode extends Node {
abstract int execute(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, byte[] mask, Encoding encoding);
@Specialization(guards = {"isSupportedEncoding(encoding) || isFixedWidth(codeRangeA)"})
static int lios8SameEncoding(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, byte[] mask, @SuppressWarnings("unused") Encoding encoding,
@Cached TStringOpsNodes.RawLastIndexOfStringNode indexOfStringNode) {
assert !b.isEmpty() && !indexOfCannotMatch(codeRangeA, b, codeRangeB, mask, fromIndex - toIndex);
return indexOfStringNode.execute(a, arrayA, b, arrayB, fromIndex, toIndex, mask);
}
@Specialization(guards = {"isUnsupportedEncoding(encoding)", "!isFixedWidth(codeRangeA)"})
int unsupported(
AbstractTruffleString a, Object arrayA, int codeRangeA,
AbstractTruffleString b, Object arrayB, int codeRangeB, int fromIndex, int toIndex, @SuppressWarnings("unused") byte[] mask, Encoding encoding,
@Cached TruffleStringIterator.NextNode nextNodeA,
@Cached TruffleStringIterator.PreviousNode prevNodeA,
@Cached TruffleStringIterator.PreviousNode prevNodeB) {
assert mask == null;
assert !b.isEmpty() && !indexOfCannotMatch(codeRangeA, b, codeRangeB, mask, fromIndex - toIndex);
TruffleStringIterator aIt = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, encoding);
TruffleStringIterator bIt = AbstractTruffleString.backwardIterator(b, arrayB, codeRangeB, encoding);
return TruffleStringIterator.lastByteIndexOfString(this, aIt, bIt, fromIndex, toIndex, nextNodeA, prevNodeA, prevNodeB);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class StrideFromCodeRangeNode extends Node {
abstract int execute(int codeRange, Encoding encoding);
@Specialization(guards = "isUTF16(encoding)")
int doUTF16(int codeRange, @SuppressWarnings("unused") Encoding encoding) {
return Stride.fromCodeRangeUTF16(codeRange);
}
@Specialization(guards = "isUTF32(encoding)")
int doUTF32(int codeRange, @SuppressWarnings("unused") Encoding encoding) {
return Stride.fromCodeRangeUTF32(codeRange);
}
@Specialization(guards = {"!isUTF16(encoding)", "!isUTF32(encoding)"})
int doOther(@SuppressWarnings("unused") int codeRange, @SuppressWarnings("unused") Encoding encoding) {
return 0;
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class CalcStringAttributesNode extends Node {
abstract long execute(AbstractTruffleString a, Object array, int offset, int length, int stride, Encoding encoding, int knownCodeRange);
@SuppressWarnings("unused")
@Specialization(guards = "is7Bit(knownCodeRange)")
long ascii(AbstractTruffleString a, Object array, int offset, int length, int stride, Encoding encoding, int knownCodeRange) {
assert length > 0;
return StringAttributes.create(length, TSCodeRange.get7Bit());
}
@Specialization(guards = "!is7Bit(knownCodeRange)")
long notAscii(AbstractTruffleString a, Object array, int offset, int length, int stride, Encoding encoding, int knownCodeRange,
@Cached CalcStringAttributesInnerNode calcNode) {
assert length > 0;
return calcNode.execute(a, array, offset, length, stride, encoding, knownCodeRange);
}
static CalcStringAttributesNode getUncached() {
return TStringInternalNodesFactory.CalcStringAttributesNodeGen.getUncached();
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class CalcStringAttributesInnerNode extends Node {
abstract long execute(AbstractTruffleString a, Object array, int offset, int length, int stride, Encoding encoding, int knownCodeRange);
@Specialization(guards = {"is8Bit(knownCodeRange) || isAsciiBytesOrLatin1(encoding)", "stride == 0"})
long doLatin1(@SuppressWarnings("unused") AbstractTruffleString a, Object array, int offset, int length, @SuppressWarnings("unused") int stride, Encoding encoding,
@SuppressWarnings("unused") int knownCodeRange) {
int codeRange = TStringOps.calcStringAttributesLatin1(this, array, offset, length);
return StringAttributes.create(length, is8Bit(codeRange) && isAsciiBytesOrLatin1(encoding) ? TSCodeRange.asciiLatinBytesNonAsciiCodeRange(encoding) : codeRange);
}
@Specialization(guards = {"isUpTo16Bit(knownCodeRange)", "stride == 1"})
long doBMP(@SuppressWarnings("unused") AbstractTruffleString a, Object array, int offset, int length, @SuppressWarnings("unused") int stride, @SuppressWarnings("unused") Encoding encoding,
@SuppressWarnings("unused") int knownCodeRange) {
return StringAttributes.create(length, TStringOps.calcStringAttributesBMP(this, array, offset, length));
}
@Specialization(guards = {"isUTF8(encoding)", "!isFixedWidth(knownCodeRange)"})
long doUTF8(AbstractTruffleString a, Object array, int offset, int length, int stride, @SuppressWarnings("unused") Encoding encoding, int knownCodeRange,
@Cached ConditionProfile brokenProfile) {
assert stride == 0;
if (isValidMultiByte(knownCodeRange) && a != null) {
return TStringOps.calcStringAttributesUTF8(this, array, offset, length, true, offset + length == a.offset() + a.length(), brokenProfile);
} else {
return TStringOps.calcStringAttributesUTF8(this, array, offset, length, false, false, brokenProfile);
}
}
@Specialization(guards = {"isUTF16(encoding)", "isValidMultiByte(knownCodeRange)"})
long doUTF16Valid(@SuppressWarnings("unused") AbstractTruffleString a, Object array, int offset, int length, int stride, @SuppressWarnings("unused") Encoding encoding,
@SuppressWarnings("unused") int knownCodeRange) {
assert stride == 1;
return TStringOps.calcStringAttributesUTF16(this, array, offset, length, true);
}
@Specialization(guards = {"isUTF16(encoding)", "isBrokenMultiByteOrUnknown(knownCodeRange)"})
long doUTF16Unknown(@SuppressWarnings("unused") AbstractTruffleString a, Object array, int offset, int length, int stride, @SuppressWarnings("unused") Encoding encoding,
@SuppressWarnings("unused") int knownCodeRange) {
assert stride == 1;
return TStringOps.calcStringAttributesUTF16(this, array, offset, length, false);
}
@Specialization(guards = {"stride == 2"})
long doUTF32(@SuppressWarnings("unused") AbstractTruffleString a, Object array, int offset, int length, @SuppressWarnings("unused") int stride, Encoding encoding,
@SuppressWarnings("unused") int knownCodeRange) {
assert isUTF32(encoding);
return StringAttributes.create(length, TStringOps.calcStringAttributesUTF32(this, array, offset, length));
}
@Specialization(guards = "isUnsupportedEncoding(encoding)")
long doGeneric(@SuppressWarnings("unused") AbstractTruffleString a, Object array, int offset, int length, int stride, Encoding encoding, @SuppressWarnings("unused") int knownCodeRange,
@Cached ConditionProfile validCharacterProfile,
@Cached ConditionProfile fixedWidthProfile) {
assert stride == 0;
return JCodings.getInstance().calcStringAttributes(this, array, offset, length, encoding, validCharacterProfile, fixedWidthProfile);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class ParseIntNode extends Node {
abstract int execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int radix) throws TruffleString.NumberFormatException;
@Specialization(guards = {"is7Bit(codeRangeA)", "cachedStride == a.stride()"})
static int do7Bit(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int radix,
@Cached(value = "a.stride()", allowUncached = true) int cachedStride,
@Cached BranchProfile errorProfile) throws TruffleString.NumberFormatException {
return NumberConversion.parseInt7Bit(a, arrayA, cachedStride, radix, errorProfile);
}
@Specialization(guards = "!is7Bit(codeRangeA)")
static int doGeneric(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int radix,
@Cached TruffleStringIterator.NextNode nextNode,
@Cached BranchProfile errorProfile) throws TruffleString.NumberFormatException {
return NumberConversion.parseInt(AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, encoding), radix, errorProfile, nextNode);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class ParseLongNode extends Node {
abstract long execute(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int radix) throws TruffleString.NumberFormatException;
@Specialization(guards = {"is7Bit(codeRangeA)", "cachedStride == a.stride()"})
static long do7Bit(AbstractTruffleString a, Object arrayA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding encoding, int radix,
@Cached(value = "a.stride()", allowUncached = true) int cachedStride,
@Cached BranchProfile errorProfile) throws TruffleString.NumberFormatException {
return NumberConversion.parseLong7Bit(a, arrayA, cachedStride, radix, errorProfile);
}
@Specialization(guards = "!is7Bit(codeRangeA)")
static long parseLong(AbstractTruffleString a, Object arrayA, int codeRangeA, Encoding encoding, int radix,
@Cached TruffleStringIterator.NextNode nextNode,
@Cached BranchProfile errorProfile) throws TruffleString.NumberFormatException {
return NumberConversion.parseLong(AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, encoding), radix, errorProfile, nextNode);
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class ParseDoubleNode extends Node {
abstract double execute(AbstractTruffleString a, Object arrayA) throws TruffleString.NumberFormatException;
@Specialization(guards = "cachedStride == a.stride()")
double doParse(AbstractTruffleString a, Object arrayA,
@Cached(value = "a.stride()", allowUncached = true) int cachedStride,
@Cached BranchProfile errorProfile) throws TruffleString.NumberFormatException {
return FastDoubleParser.parseDouble(this, a, arrayA, cachedStride, 0, a.length(), errorProfile);
}
}
@ImportStatic(TStringGuards.class)
@GeneratePackagePrivate
@GenerateUncached
abstract static class FromJavaStringUTF16Node extends Node {
FromJavaStringUTF16Node() {
}
abstract TruffleString execute(String value, int charOffset, int length, boolean copy);
@Specialization
TruffleString doNonEmpty(String javaString, int charOffset, int length, final boolean copy,
@Cached ConditionProfile utf16CompactProfile) {
checkArrayRange(javaString.length(), charOffset, length);
CompilerAsserts.partialEvaluationConstant(copy);
if (length == 0) {
return Encoding.UTF_16.getEmpty();
}
final byte[] array;
final int offset;
final int stride;
final int codeRange;
final int codePointLength;
int strideJS = TStringUnsafe.getJavaStringStride(javaString);
int offsetJS = charOffset << 1;
byte[] arrayJS = TStringUnsafe.getJavaStringArray(javaString);
if (utf16CompactProfile.profile(strideJS == 0)) {
if (length == 1) {
return TStringConstants.getSingleByte(Encoding.UTF_16, Byte.toUnsignedInt(arrayJS[charOffset]));
}
codeRange = TStringOps.calcStringAttributesLatin1(this, arrayJS, offsetJS, length);
codePointLength = length;
} else {
assert strideJS == 1;
if (length == 1 && TStringOps.readFromByteArray(arrayJS, 1, charOffset) <= 0xff) {
return TStringConstants.getSingleByte(Encoding.UTF_16, TStringOps.readFromByteArray(arrayJS, 1, charOffset));
}
final long attrs = TStringOps.calcStringAttributesUTF16(this, arrayJS, offsetJS, length, false);
codePointLength = StringAttributes.getCodePointLength(attrs);
codeRange = StringAttributes.getCodeRange(attrs);
}
if (!copy || length == javaString.length()) {
stride = strideJS;
offset = offsetJS;
array = arrayJS;
} else {
stride = Stride.fromCodeRangeUTF16(codeRange);
array = new byte[length << stride];
offset = 0;
if (strideJS == 1 && stride == 0) {
TStringOps.arraycopyWithStride(this, arrayJS, offsetJS, 1, 0, array, offset, 0, 0, length);
} else {
assert strideJS == stride;
TStringOps.arraycopyWithStride(this, arrayJS, offsetJS, 0, 0, array, offset, 0, 0, length << stride);
}
}
TruffleString ret = TruffleString.createFromArray(array, offset, length, stride, Encoding.UTF_16, codePointLength, codeRange);
if (length == javaString.length()) {
assert charOffset == 0;
TruffleString wrapped = TruffleString.createWrapJavaString(javaString, codePointLength, codeRange);
ret.cacheInsertFirstBeforePublished(wrapped);
}
return ret;
}
}
@ImportStatic({TStringGuards.class, Encoding.class})
@GenerateUncached
abstract static class ToJavaStringNode extends Node {
abstract TruffleString execute(TruffleString a, Object arrayA);
@Specialization(guards = "a.isCompatibleTo(UTF_16)")
static TruffleString doUTF16(TruffleString a, Object arrayA,
@Cached @Shared("createStringNode") CreateJavaStringNode createStringNode) {
return TruffleString.createWrapJavaString(createStringNode.execute(a, arrayA), a.codePointLength(), a.codeRange());
}
@Specialization(guards = "!a.isCompatibleTo(UTF_16)")
static TruffleString doGeneric(TruffleString a, Object arrayA,
@Cached GetCodePointLengthNode getCodePointLengthNode,
@Cached GetCodeRangeNode getCodeRangeNode,
@Cached TransCodeNode transCodeNode,
@Cached @Shared("createStringNode") CreateJavaStringNode createStringNode) {
TruffleString utf16 = transCodeNode.execute(a, arrayA, getCodePointLengthNode.execute(a), getCodeRangeNode.execute(a), Encoding.UTF_16);
if (!utf16.isCacheHead()) {
a.cacheInsert(utf16);
}
return TruffleString.createWrapJavaString(createStringNode.execute(utf16, utf16.data()), utf16.codePointLength(), utf16.codeRange());
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class CreateJavaStringNode extends Node {
abstract String execute(AbstractTruffleString a, Object arrayA);
@Specialization
String createJavaString(AbstractTruffleString a, Object arrayA,
@Cached ConditionProfile reuseProfile,
@Cached GetCodeRangeNode getCodeRangeNode) {
assert isUTF16Compatible(a);
final int codeRange = getCodeRangeNode.execute(a);
final int stride = Stride.fromCodeRangeUTF16(codeRange);
final byte[] bytes;
if (reuseProfile.profile(a instanceof TruffleString && arrayA instanceof byte[] && a.length() << a.stride() == ((byte[]) arrayA).length && a.stride() == stride)) {
assert a.offset() == 0;
bytes = (byte[]) arrayA;
} else {
bytes = new byte[a.length() << stride];
TStringOps.arraycopyWithStride(this,
arrayA, a.offset(), a.stride(), 0,
bytes, 0, stride, 0, a.length());
}
return TStringUnsafe.createJavaString(bytes, stride);
}
private static boolean isUTF16Compatible(AbstractTruffleString a) {
return a.isCompatibleTo(Encoding.UTF_16) || a instanceof MutableTruffleString && ((MutableTruffleString) a).codeRange() < Encoding.UTF_16.maxCompatibleCodeRange;
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class TransCodeNode extends Node {
abstract TruffleString execute(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding targetEncoding);
@Specialization
TruffleString transcode(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding targetEncoding,
@Cached ConditionProfile asciiBytesInvalidProfile,
@Cached TransCodeIntlNode transCodeIntlNode) {
if (AbstractTruffleString.DEBUG_STRICT_ENCODING_CHECKS && a.isImmutable() && codeRangeA < targetEncoding.maxCompatibleCodeRange) {
if (a.stride() == 0) {
return TruffleString.createFromArray(arrayA, a.offset(), a.length(), 0, targetEncoding, codePointLengthA, codeRangeA, false);
}
int targetStride = Stride.fromCodeRange(codeRangeA, targetEncoding);
Object array = TStringOps.arraycopyOfWithStride(this, arrayA, a.offset(), a.length(), a.stride(), a.length(), targetStride);
return TruffleString.createFromArray(array, 0, a.length(), targetStride, targetEncoding, codePointLengthA, codeRangeA, false);
}
assert a.length() > 0;
if (asciiBytesInvalidProfile.profile((isAscii(a.encoding()) || isBytes(a.encoding())) && isSupportedEncoding(targetEncoding))) {
assert (isBrokenFixedWidth(codeRangeA) || isValidFixedWidth(codeRangeA)) && isStride0(a) && codePointLengthA == a.length();
byte[] buffer = new byte[codePointLengthA];
for (int i = 0; i < buffer.length; i++) {
int c = readS0(a, arrayA, i);
buffer[i] = (byte) (c > 0x7f ? '?' : c);
TStringConstants.truffleSafePointPoll(this, i + 1);
}
return TransCodeIntlNode.create(a, buffer, buffer.length, 0, targetEncoding, codePointLengthA, TSCodeRange.get7Bit(), true);
} else {
return transCodeIntlNode.execute(a, arrayA, codePointLengthA, codeRangeA, Encoding.get(a.encoding()), targetEncoding);
}
}
}
@ImportStatic(TStringGuards.class)
@GenerateUncached
abstract static class TransCodeIntlNode extends Node {
abstract TruffleString execute(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, Encoding targetEncoding);
@SuppressWarnings("unused")
@Specialization(guards = {"isSupportedEncoding(sourceEncoding)", "isAscii(targetEncoding) || isBytes(targetEncoding)"})
TruffleString targetAscii(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, Encoding targetEncoding,
@Cached @Shared("iteratorNextNode") TruffleStringIterator.NextNode iteratorNextNode) {
assert !is7Bit(codeRangeA);
byte[] buffer = new byte[codePointLengthA];
TruffleStringIterator it = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, sourceEncoding);
int i = 0;
while (it.hasNext()) {
int codepoint = iteratorNextNode.execute(it);
buffer[i++] = codepoint > 0x7f ? (byte) '?' : (byte) codepoint;
TStringConstants.truffleSafePointPoll(this, i);
}
return create(a, buffer, buffer.length, 0, targetEncoding, codePointLengthA, TSCodeRange.get7Bit(), true);
}
@SuppressWarnings("unused")
@Specialization(guards = {"isSupportedEncoding(sourceEncoding)", "isLatin1(targetEncoding)"})
TruffleString latin1Transcode(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, Encoding targetEncoding,
@Cached @Shared("iteratorNextNode") TruffleStringIterator.NextNode iteratorNextNode) {
assert !is7Or8Bit(codeRangeA);
byte[] buffer = new byte[codePointLengthA];
TruffleStringIterator it = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, sourceEncoding);
int codeRange = TSCodeRange.get7Bit();
int i = 0;
while (it.hasNext()) {
int codepoint = iteratorNextNode.execute(it);
byte latin1 = codepoint > 0xff ? (byte) '?' : (byte) codepoint;
buffer[i++] = latin1;
if (latin1 < 0) {
codeRange = TSCodeRange.get8Bit();
}
TStringConstants.truffleSafePointPoll(this, i);
}
return create(a, buffer, codePointLengthA, 0, Encoding.ISO_8859_1, codePointLengthA, codeRange, true);
}
@Specialization(guards = {"isSupportedEncoding(sourceEncoding)", "!isLarge(codePointLengthA)", "isUTF8(targetEncoding)"})
TruffleString utf8TranscodeRegular(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, @SuppressWarnings("unused") Encoding targetEncoding,
@Cached @Shared("iteratorNextNode") TruffleStringIterator.NextNode iteratorNextNode,
@Cached @Shared("brokenProfile") ConditionProfile brokenProfile,
@Cached @Shared("outOfMemoryProfile") BranchProfile outOfMemoryProfile) {
return utf8Transcode(a, arrayA, codePointLengthA, codeRangeA, sourceEncoding, iteratorNextNode, false, brokenProfile, outOfMemoryProfile);
}
@Specialization(guards = {"isSupportedEncoding(sourceEncoding)", "isLarge(codePointLengthA)", "isUTF8(targetEncoding)"})
TruffleString utf8TranscodeLarge(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, @SuppressWarnings("unused") Encoding targetEncoding,
@Cached @Shared("iteratorNextNode") TruffleStringIterator.NextNode iteratorNextNode,
@Cached @Shared("brokenProfile") ConditionProfile brokenProfile,
@Cached @Shared("outOfMemoryProfile") BranchProfile outOfMemoryProfile) {
return utf8Transcode(a, arrayA, codePointLengthA, codeRangeA, sourceEncoding, iteratorNextNode, true, brokenProfile, outOfMemoryProfile);
}
static boolean isLarge(int codePointLengthA) {
return codePointLengthA > TStringConstants.MAX_ARRAY_SIZE / 4;
}
private TruffleString utf8Transcode(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding,
TruffleStringIterator.NextNode iteratorNextNode, boolean isLarge, ConditionProfile brokenProfile, BranchProfile outOfMemoryProfile) {
assert !is7Bit(codeRangeA);
TruffleStringIterator it = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, sourceEncoding);
byte[] buffer = new byte[isLarge ? TStringConstants.MAX_ARRAY_SIZE : codePointLengthA * 4];
int codeRange = TSCodeRange.getValidMultiByte();
int length = 0;
int loopCount = 0;
while (it.hasNext()) {
int codepoint = iteratorNextNode.execute(it);
if (Encodings.isUTF16Surrogate(codepoint) || Integer.toUnsignedLong(codepoint) > Character.MAX_CODE_POINT) {
codeRange = TSCodeRange.getBrokenMultiByte();
codepoint = Encodings.invalidCodepoint();
}
int n = Encodings.utf8EncodedSize(codepoint);
assert isLarge || length + n <= buffer.length;
if (isLarge && length > TStringConstants.MAX_ARRAY_SIZE - n) {
outOfMemoryProfile.enter();
throw InternalErrors.outOfMemory();
}
Encodings.utf8Encode(codepoint, buffer, length, n);
length += n;
TStringConstants.truffleSafePointPoll(this, ++loopCount);
}
final int codePointLength;
if (isBrokenMultiByte(codeRange)) {
long attrs = TStringOps.calcStringAttributesUTF8(this, buffer, 0, length, false, false, brokenProfile);
codePointLength = StringAttributes.getCodePointLength(attrs);
codeRange = StringAttributes.getCodeRange(attrs);
} else {
codePointLength = codePointLengthA;
}
return create(a, Arrays.copyOf(buffer, length), length, 0, Encoding.UTF_8, codePointLength, codeRange, isBrokenMultiByte(codeRange));
}
@Specialization(guards = {"isUTF32(sourceEncoding)", "isUTF16(targetEncoding)"})
TruffleString utf16Fixed32Bit(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, @SuppressWarnings("unused") Encoding sourceEncoding,
@SuppressWarnings("unused") Encoding targetEncoding) {
assert TStringGuards.isValidFixedWidth(codeRangeA) || TStringGuards.isBrokenFixedWidth(codeRangeA);
assert isStride2(a);
byte[] buffer = new byte[codePointLengthA * 4];
int length = 0;
int codeRange = TStringGuards.isValidFixedWidth(codeRangeA) ? TSCodeRange.getValidMultiByte() : TSCodeRange.getBrokenMultiByte();
for (int i = 0; i < a.length(); i++) {
int codepoint = TStringOps.readS2(a, arrayA, i);
length += Encodings.utf16Encode(codepoint, buffer, length);
TStringConstants.truffleSafePointPoll(this, i + 1);
}
final int codePointLength;
if (isBrokenMultiByte(codeRange)) {
long attrs = TStringOps.calcStringAttributesUTF16(this, buffer, 0, length, false);
codePointLength = StringAttributes.getCodePointLength(attrs);
codeRange = StringAttributes.getCodeRange(attrs);
} else {
codePointLength = codePointLengthA;
}
return create(a, Arrays.copyOf(buffer, length * 2), length, 1, Encoding.UTF_16, codePointLength, codeRange, isBrokenMultiByte(codeRange));
}
@Specialization(guards = {"isSupportedEncoding(sourceEncoding)", "!isFixedWidth(codeRangeA)", "!isLarge(codePointLengthA)", "isUTF16(targetEncoding)"})
TruffleString utf16TranscodeRegular(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, @SuppressWarnings("unused") Encoding targetEncoding,
@Cached @Shared("iteratorNextNode") TruffleStringIterator.NextNode iteratorNextNode,
@Cached @Shared("outOfMemoryProfile") BranchProfile outOfMemoryProfile) {
return utf16Transcode(a, arrayA, codePointLengthA, codeRangeA, sourceEncoding, iteratorNextNode, false, outOfMemoryProfile);
}
@Specialization(guards = {"isSupportedEncoding(sourceEncoding)", "!isFixedWidth(codeRangeA)", "isLarge(codePointLengthA)", "isUTF16(targetEncoding)"})
TruffleString utf16TranscodeLarge(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, @SuppressWarnings("unused") Encoding targetEncoding,
@Cached @Shared("iteratorNextNode") TruffleStringIterator.NextNode iteratorNextNode,
@Cached @Shared("outOfMemoryProfile") BranchProfile outOfMemoryProfile) {
return utf16Transcode(a, arrayA, codePointLengthA, codeRangeA, sourceEncoding, iteratorNextNode, true, outOfMemoryProfile);
}
private TruffleString utf16Transcode(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding,
TruffleStringIterator.NextNode iteratorNextNode, boolean isLarge, BranchProfile outOfMemoryProfile) {
assert TStringGuards.isValidBrokenOrUnknownMultiByte(codeRangeA);
TruffleStringIterator it = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, sourceEncoding);
byte[] buffer = new byte[codePointLengthA];
int codePointLength = codePointLengthA;
int length = 0;
int codeRange = TSCodeRange.get7Bit();
while (it.hasNext()) {
int curIndex = it.getRawIndex();
int codepoint = iteratorNextNode.execute(it);
if (codepoint > 0xff) {
buffer = TStringOps.arraycopyOfWithStride(this, buffer, 0, length, 0, codePointLengthA, 1);
codeRange = TSCodeRange.get16Bit();
it.setRawIndex(curIndex);
break;
}
if (codepoint > 0x7f) {
codeRange = TSCodeRange.get8Bit();
}
buffer[length++] = (byte) codepoint;
TStringConstants.truffleSafePointPoll(this, length);
}
if (!it.hasNext()) {
assert length == codePointLengthA;
return create(a, buffer, length, 0, Encoding.UTF_16, codePointLengthA, codeRange, false);
}
while (it.hasNext()) {
int curIndex = it.getRawIndex();
int codepoint = iteratorNextNode.execute(it);
if (codepoint > 0xffff) {
buffer = Arrays.copyOf(buffer, isLarge ? TStringConstants.MAX_ARRAY_SIZE : buffer.length * 2);
codeRange = TSCodeRange.commonCodeRange(codeRange, TSCodeRange.getValidMultiByte());
it.setRawIndex(curIndex);
break;
}
if (Encodings.isUTF16Surrogate(codepoint)) {
codeRange = TSCodeRange.getBrokenMultiByte();
}
writeToByteArray(buffer, 1, length++, codepoint);
TStringConstants.truffleSafePointPoll(this, length);
}
if (!it.hasNext()) {
assert length == codePointLengthA;
if (isBrokenMultiByte(codeRange)) {
long attrs = TStringOps.calcStringAttributesUTF16(this, buffer, 0, length, false);
codePointLength = StringAttributes.getCodePointLength(attrs);
codeRange = StringAttributes.getCodeRange(attrs);
}
return create(a, buffer, length, 1, Encoding.UTF_16, codePointLength, codeRange, isBrokenMultiByte(codeRange));
}
int loopCount = 0;
while (it.hasNext()) {
int codepoint = iteratorNextNode.execute(it);
if (Encodings.isUTF16Surrogate(codepoint) || Integer.toUnsignedLong(codepoint) > Character.MAX_CODE_POINT) {
codeRange = TSCodeRange.getBrokenMultiByte();
}
if (isLarge && length + Encodings.utf16EncodedSize(codepoint) > TStringConstants.MAX_ARRAY_SIZE_S1) {
outOfMemoryProfile.enter();
throw InternalErrors.outOfMemory();
}
length += Encodings.utf16Encode(codepoint, buffer, length);
TStringConstants.truffleSafePointPoll(this, ++loopCount);
}
if (isBrokenMultiByte(codeRange)) {
long attrs = TStringOps.calcStringAttributesUTF16(this, buffer, 0, length, false);
codePointLength = StringAttributes.getCodePointLength(attrs);
codeRange = StringAttributes.getCodeRange(attrs);
}
return create(a, Arrays.copyOf(buffer, length * 2), length, 1, Encoding.UTF_16, codePointLength, codeRange, isBrokenMultiByte(codeRange));
}
@Specialization(guards = {"!isUTF16(sourceEncoding)", "isSupportedEncoding(sourceEncoding)", "!isFixedWidth(codeRangeA)", "!isLarge(codePointLengthA)", "isUTF32(targetEncoding)"})
TruffleString utf32TranscodeRegular(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, @SuppressWarnings("unused") Encoding targetEncoding,
@Cached @Shared("iteratorNextNode") TruffleStringIterator.NextNode iteratorNextNode) {
return utf32Transcode(a, arrayA, codePointLengthA, codeRangeA, sourceEncoding, iteratorNextNode);
}
@SuppressWarnings("unused")
@Specialization(guards = {"isSupportedEncoding(sourceEncoding)", "!isFixedWidth(codeRangeA)", "isLarge(codePointLengthA)", "isUTF32(targetEncoding)"})
static TruffleString utf32TranscodeLarge(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, Encoding targetEncoding) {
throw InternalErrors.outOfMemory();
}
@Specialization(guards = {"isUTF16(sourceEncoding)", "!isFixedWidth(codeRangeA)", "!isLarge(codePointLengthA)", "isUTF32(targetEncoding)"})
TruffleString utf32TranscodeUTF16(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, @SuppressWarnings("unused") Encoding targetEncoding,
@Cached @Shared("iteratorNextNode") TruffleStringIterator.NextNode iteratorNextNode) {
assert containsSurrogates(a);
TruffleStringIterator it = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, sourceEncoding);
byte[] buffer = new byte[codePointLengthA << 2];
int length = 0;
while (it.hasNext()) {
writeToByteArray(buffer, 2, length++, iteratorNextNode.execute(it));
TStringConstants.truffleSafePointPoll(this, length);
}
assert length == codePointLengthA;
boolean isBroken = isBrokenMultiByte(codeRangeA);
return create(a, buffer, length, 2, Encoding.UTF_32, codePointLengthA, isBroken ? TSCodeRange.getBrokenFixedWidth() : TSCodeRange.getValidFixedWidth(), isBroken);
}
private TruffleString utf32Transcode(AbstractTruffleString a, Object arrayA, int codePointLengthA, int codeRangeA, Encoding sourceEncoding, TruffleStringIterator.NextNode iteratorNextNode) {
assert TStringGuards.isValidBrokenOrUnknownMultiByte(codeRangeA);
TruffleStringIterator it = AbstractTruffleString.forwardIterator(a, arrayA, codeRangeA, sourceEncoding);
byte[] buffer = new byte[codePointLengthA];
int length = 0;
int codeRange = TSCodeRange.get7Bit();
int codepoint = 0;
while (it.hasNext()) {
int curIndex = it.getRawIndex();
codepoint = iteratorNextNode.execute(it);
if (codepoint > 0xff) {
if (Encodings.isUTF16Surrogate(codepoint)) {
buffer = TStringOps.arraycopyOfWithStride(this, buffer, 0, length, 0, codePointLengthA, 2);
codeRange = TSCodeRange.getBrokenFixedWidth();
} else {
buffer = TStringOps.arraycopyOfWithStride(this, buffer, 0, length, 0, codePointLengthA, 1);
codeRange = TSCodeRange.get16Bit();
}
it.setRawIndex(curIndex);
break;
}
if (codepoint > 0x7f) {
codeRange = TSCodeRange.get8Bit();
}
buffer[length++] = (byte) codepoint;
TStringConstants.truffleSafePointPoll(this, length);
}
if (!it.hasNext()) {
assert length == codePointLengthA;
return create(a, buffer, length, 0, Encoding.UTF_32, codePointLengthA, codeRange, isBrokenFixedWidth(codeRange));
}
if (is16Bit(codeRange)) {
while (it.hasNext()) {
int curIndex = it.getRawIndex();
codepoint = iteratorNextNode.execute(it);
if (codepoint > 0xffff || Encodings.isUTF16Surrogate(codepoint)) {
buffer = TStringOps.arraycopyOfWithStride(this, buffer, 0, length, 1, codePointLengthA, 2);
codeRange = Encodings.isValidUnicodeCodepoint(codepoint) ? TSCodeRange.getValidFixedWidth() : TSCodeRange.getBrokenFixedWidth();
it.setRawIndex(curIndex);
break;
}
writeToByteArray(buffer, 1, length++, codepoint);
TStringConstants.truffleSafePointPoll(this, length);
}
}
if (!it.hasNext()) {
assert length == codePointLengthA;
return create(a, buffer, length, 1, Encoding.UTF_32, codePointLengthA, codeRange, isBrokenFixedWidth(codeRange));
}
while (it.hasNext()) {
codepoint = iteratorNextNode.execute(it);
if (!Encodings.isValidUnicodeCodepoint(codepoint)) {
codeRange = TSCodeRange.getBrokenFixedWidth();
}
writeToByteArray(buffer, 2, length++, codepoint);
TStringConstants.truffleSafePointPoll(this, length);
}
return create(a, buffer, length, 2, Encoding.UTF_32, codePointLengthA, codeRange, isBrokenFixedWidth(codeRange));
}
@Specialization(guards = {"isUnsupportedEncoding(sourceEncoding) || isUnsupportedEncoding(targetEncoding)"})
TruffleString unsupported(AbstractTruffleString a, Object arrayA, int codePointLengthA, @SuppressWarnings("unused") int codeRangeA, @SuppressWarnings("unused") Encoding sourceEncoding,
Encoding targetEncoding,
@Cached BranchProfile outOfMemoryProfile,
@Cached ConditionProfile nativeProfile,
@Cached FromBufferWithStringCompactionNode fromBufferWithStringCompactionNode) {
return JCodings.getInstance().transcode(this, a, arrayA, codePointLengthA, targetEncoding, outOfMemoryProfile, nativeProfile, fromBufferWithStringCompactionNode);
}
private static TruffleString create(AbstractTruffleString a, byte[] buffer, int length, int stride, Encoding encoding, int codePointLength, int codeRange, boolean isCacheHead) {
return TruffleString.createFromByteArray(buffer, length, stride, encoding, codePointLength, codeRange, isCacheHead || a.isMutable());
}
@TruffleBoundary
private static boolean containsSurrogates(AbstractTruffleString a) {
CompilerAsserts.neverPartOfCompilation();
for (int i = 0; i < a.length(); i++) {
if (Encodings.isUTF16Surrogate(a.readCharUTF16Uncached(i))) {
return true;
}
}
return false;
}
}
}