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

com.oracle.truffle.api.strings.MutableTruffleString 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 static com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
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.isUTF16;
import static com.oracle.truffle.api.strings.TStringGuards.isUTF32;
import static com.oracle.truffle.api.strings.TStringGuards.isUTF8;

import java.util.Arrays;

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.profiles.ValueProfile;
import com.oracle.truffle.api.strings.TruffleString.AsTruffleStringNode;
import com.oracle.truffle.api.strings.TruffleString.Encoding;

/**
 * Represents a mutable variant of a {@link TruffleString}. This class also accepts all operations
 * of TruffleString. This class is not thread-safe and allows overwriting bytes in its internal byte
 * array or native pointer via {@link WriteByteNode}. The internal array or native pointer may also
 * be modified externally, but the corresponding MutableTruffleString must be notified of this via
 * {@link #notifyExternalMutation()}. MutableTruffleString is not a Truffle interop type, and must
 * be converted to an immutable {@link TruffleString} via {@link AsTruffleStringNode} before passing
 * a language boundary.
 *
 * @see TruffleString
 * @since 22.1
 */
public final class MutableTruffleString extends AbstractTruffleString {

    private int codePointLength = -1;
    private byte codeRange = (byte) TSCodeRange.getUnknown();

    private MutableTruffleString(Object data, int offset, int length, int stride, Encoding encoding) {
        super(data, offset, length, stride, encoding, 0);
        assert data instanceof byte[] || data instanceof NativePointer;
        if (Encoding.isFixedWidth(encoding)) {
            codePointLength = encoding.isSupported() ? length : length / JCodings.getInstance().minLength(encoding.jCoding);
        }
    }

    private static MutableTruffleString create(Object data, int offset, int length, Encoding encoding) {
        MutableTruffleString string = new MutableTruffleString(data, offset, length, encoding.naturalStride, encoding);
        if (AbstractTruffleString.DEBUG_ALWAYS_CREATE_JAVA_STRING) {
            string.toJavaStringUncached();
        }
        return string;
    }

    int codePointLength() {
        return codePointLength;
    }

    int codeRange() {
        return codeRange;
    }

    void updateCachedAttributes(int newCodePointLength, int newCodeRange) {
        assert newCodePointLength >= 0;
        assert TSCodeRange.isCodeRange(newCodeRange);
        this.codePointLength = newCodePointLength;
        this.codeRange = (byte) newCodeRange;
    }

    void invalidateCachedAttributes() {
        if (!Encoding.isFixedWidth(encoding())) {
            codePointLength = -1;
        }
        codeRange = (byte) TSCodeRange.getUnknown();
        hashCode = 0;
        if (data() instanceof NativePointer) {
            ((NativePointer) data()).invalidateCachedByteArray();
        }
    }

    /**
     * Notify this mutable string of an external modification of its internal content. This method
     * must be called after every direct write (not via {@link WriteByteNode}) to the byte array or
     * native pointer the string is using as internal storage. Exemplary usage scenario: Suppose a
     * {@link MutableTruffleString} was created by wrapping a native pointer via
     * {@link FromNativePointerNode}. If the native pointer is passed to a native function that may
     * modify the pointer's contents, this method must be called afterwards, to ensure consistency.
     *
     * @since 22.1
     */
    public void notifyExternalMutation() {
        invalidateCachedAttributes();
    }

    /**
     * Node to create a new {@link MutableTruffleString} from a byte array. See
     * {@link #execute(byte[], int, int, TruffleString.Encoding, boolean)} for details.
     *
     * @since 22.1
     */
    @ImportStatic(TStringGuards.class)
    @GeneratePackagePrivate
    @GenerateUncached
    public abstract static class FromByteArrayNode extends Node {

        FromByteArrayNode() {
        }

        /**
         * Creates a new {@link MutableTruffleString} from a byte array. The array content is
         * assumed to be encoded in the given encoding already. This operation allows non-copying
         * string creation, i.e. the array parameter can be used directly by passing
         * {@code copy = false}. If the array is modified after non-copying string creation, the
         * string must be notified of this via {@link MutableTruffleString#notifyExternalMutation()}
         * .
         *
         * @since 22.1
         */
        public abstract MutableTruffleString execute(byte[] value, int byteOffset, int byteLength, Encoding encoding, boolean copy);

        @Specialization
        static MutableTruffleString fromByteArray(byte[] value, int byteOffset, int byteLength, Encoding enc, boolean copy) {
            checkArrayRange(value, byteOffset, byteLength);
            checkByteLength(byteLength, enc);
            final byte[] array;
            final int offset;
            if (copy) {
                array = Arrays.copyOfRange(value, byteOffset, byteOffset + byteLength);
                offset = 0;
            } else {
                array = value;
                offset = byteOffset;
            }
            return MutableTruffleString.create(array, offset, byteLength >> enc.naturalStride, enc);
        }

        /**
         * Create a new {@link FromByteArrayNode}.
         *
         * @since 22.1
         */
        public static FromByteArrayNode create() {
            return MutableTruffleStringFactory.FromByteArrayNodeGen.create();
        }

        /**
         * Get the uncached version of {@link FromByteArrayNode}.
         *
         * @since 22.1
         */
        public static FromByteArrayNode getUncached() {
            return MutableTruffleStringFactory.FromByteArrayNodeGen.getUncached();
        }
    }

    /**
     * Shorthand for calling the uncached version of {@link FromByteArrayNode}.
     *
     * @since 22.1
     */
    @TruffleBoundary
    public static MutableTruffleString fromByteArrayUncached(byte[] value, int byteOffset, int byteLength, Encoding encoding, boolean copy) {
        return FromByteArrayNode.getUncached().execute(value, byteOffset, byteLength, encoding, copy);
    }

    /**
     * Node to create a new {@link MutableTruffleString} from an interop object representing a
     * native pointer. See {@link #execute(Object, int, int, TruffleString.Encoding, boolean)} for
     * details.
     *
     * @since 22.1
     */
    @ImportStatic({TStringGuards.class, TStringAccessor.class})
    @GeneratePackagePrivate
    @GenerateUncached
    public abstract static class FromNativePointerNode extends Node {

        FromNativePointerNode() {
        }

        /**
         * Create a new {@link MutableTruffleString} from an interop object representing a native
         * pointer ({@code isPointer(pointerObject)} must return {@code true}). The pointer is
         * immediately unboxed with ({@code asPointer(pointerObject)}) and saved until the end of
         * the string's lifetime, i.e. {@link MutableTruffleString} assumes that the pointer address
         * does not change. The pointer's content is assumed to be encoded in the given encoding
         * already. If {@code copy} is {@code false}, the native pointer is used directly as the new
         * string's backing storage. Caution: If the pointer's content is modified after string
         * creation, the string must be notified of this via
         * {@link MutableTruffleString#notifyExternalMutation()}.
         *
         * 

* WARNING: {@link MutableTruffleString} cannot reason about the lifetime of the * native pointer, so it is up to the user to make sure that the native pointer is valid * to access and not freed as long the {@code pointerObject} is alive (if {@code copy} * is {@code false}). To help with this the MutableTruffleString keeps a reference to the * given {@code pointerObject}, so the {@code pointerObject} is kept alive at least as long * as the MutableTruffleString is used. In order to be able to use the string past the * native pointer's life time, convert it to a managed string via * {@link MutableTruffleString.AsManagedNode} before the native pointer is freed. *

*

* If {@code copy} is {@code true}, the pointer's contents are copied to a Java byte array, * and the pointer can be freed safely after the operation completes. *

* This operation requires native access permissions * ({@code TruffleLanguage.Env#isNativeAccessAllowed()}). * * @since 22.1 */ public abstract MutableTruffleString execute(Object pointerObject, int byteOffset, int byteLength, Encoding encoding, boolean copy); @Specialization MutableTruffleString fromNativePointer(Object pointerObject, int byteOffset, int byteLength, Encoding enc, boolean copy, @Cached(value = "createInteropLibrary()", uncached = "getUncachedInteropLibrary()") Node interopLibrary) { checkByteLength(byteLength, enc); NativePointer nativePointer = NativePointer.create(this, pointerObject, interopLibrary, byteOffset); final Object array; final int offset; if (copy) { array = TStringOps.arraycopyOfWithStride(this, nativePointer, byteOffset, byteLength, 0, byteLength, 0); offset = 0; } else { array = nativePointer; offset = byteOffset; } return MutableTruffleString.create(array, offset, byteLength >> enc.naturalStride, enc); } /** * Create a new {@link FromNativePointerNode}. * * @since 22.1 */ public static FromNativePointerNode create() { return MutableTruffleStringFactory.FromNativePointerNodeGen.create(); } /** * Get the uncached version of {@link FromNativePointerNode}. * * @since 22.1 */ public static FromNativePointerNode getUncached() { return MutableTruffleStringFactory.FromNativePointerNodeGen.getUncached(); } } /** * Shorthand for calling the uncached version of {@link FromNativePointerNode}. * * @since 22.1 */ @TruffleBoundary public static MutableTruffleString fromNativePointerUncached(Object pointerObject, int byteOffset, int byteLength, Encoding encoding, boolean copy) { return FromNativePointerNode.getUncached().execute(pointerObject, byteOffset, byteLength, encoding, copy); } /** * Node to get a {@link AbstractTruffleString} as a {@link MutableTruffleString}. See * {@link #execute(AbstractTruffleString, TruffleString.Encoding)} for details. * * @since 22.1 */ @ImportStatic({TStringGuards.class, TStringAccessor.class}) @GeneratePackagePrivate @GenerateUncached public abstract static class AsMutableTruffleStringNode extends Node { AsMutableTruffleStringNode() { } /** * If the given string is already a {@link MutableTruffleString}, return it. If it is a * {@link TruffleString}, create a new {@link MutableTruffleString}, copying the immutable * string's contents. * * @since 22.1 */ public abstract MutableTruffleString execute(AbstractTruffleString a, Encoding expectedEncoding); @Specialization static MutableTruffleString mutable(MutableTruffleString a, Encoding expectedEncoding) { a.checkEncoding(expectedEncoding); return a; } @Specialization static MutableTruffleString fromTruffleString(TruffleString a, Encoding expectedEncoding, @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { return createCopying(a, expectedEncoding, copyToByteArrayNode); } /** * Create a new {@link AsMutableTruffleStringNode}. * * @since 22.1 */ public static AsMutableTruffleStringNode create() { return MutableTruffleStringFactory.AsMutableTruffleStringNodeGen.create(); } /** * Get the uncached version of {@link AsMutableTruffleStringNode}. * * @since 22.1 */ public static AsMutableTruffleStringNode getUncached() { return MutableTruffleStringFactory.AsMutableTruffleStringNodeGen.getUncached(); } } /** * Node to get the given {@link AbstractTruffleString} as a managed * {@link MutableTruffleString}, meaning that the resulting string's backing memory is not a * native pointer. See {@link #execute(AbstractTruffleString, TruffleString.Encoding)} for * details. * * @since 22.1 */ @ImportStatic({TStringGuards.class, TStringAccessor.class}) @GeneratePackagePrivate @GenerateUncached public abstract static class AsManagedNode extends Node { AsManagedNode() { } /** * If the given string is already a managed (i.e. not backed by a native pointer) string, * return it. Otherwise, copy the string's native pointer content into a Java byte array and * return a new string backed by the byte array. * * @since 22.1 */ public abstract MutableTruffleString execute(AbstractTruffleString a, Encoding expectedEncoding); @Specialization(guards = "!a.isNative()") static MutableTruffleString mutable(MutableTruffleString a, Encoding expectedEncoding) { a.checkEncoding(expectedEncoding); return a; } @Specialization(guards = "a.isNative() || a.isImmutable()") static MutableTruffleString fromTruffleString(AbstractTruffleString a, Encoding expectedEncoding, @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { return createCopying(a, expectedEncoding, copyToByteArrayNode); } /** * Create a new {@link AsManagedNode}. * * @since 22.1 */ public static AsManagedNode create() { return MutableTruffleStringFactory.AsManagedNodeGen.create(); } /** * Get the uncached version of {@link AsManagedNode}. * * @since 22.1 */ public static AsManagedNode getUncached() { return MutableTruffleStringFactory.AsManagedNodeGen.getUncached(); } } /** * Node to write a byte into a mutable string. * * @since 22.1 */ @ImportStatic(TStringGuards.class) @GeneratePackagePrivate @GenerateUncached public abstract static class WriteByteNode extends Node { WriteByteNode() { } /** * Writes a byte into the given mutable string. * * @since 22.1 */ public abstract void execute(MutableTruffleString a, int byteIndex, byte value, Encoding expectedEncoding); @Specialization static void writeByte(MutableTruffleString a, int byteIndex, byte value, Encoding expectedEncoding) { a.checkEncoding(expectedEncoding); int byteLength = a.length() << a.stride(); TruffleString.boundsCheckI(byteIndex, byteLength); TStringOps.writeS0(a.data(), a.offset(), byteLength, byteIndex, value); if (!(TSCodeRange.is7Bit(a.codeRange) && value >= 0)) { a.invalidateCachedAttributes(); } } /** * Create a new {@link WriteByteNode}. * * @since 22.1 */ public static WriteByteNode create() { return MutableTruffleStringFactory.WriteByteNodeGen.create(); } /** * Get the uncached version of {@link WriteByteNode}. * * @since 22.1 */ public static WriteByteNode getUncached() { return MutableTruffleStringFactory.WriteByteNodeGen.getUncached(); } } /** * Shorthand for calling the uncached version of {@link WriteByteNode}. * * @since 22.1 */ @TruffleBoundary public void writeByteUncached(int byteIndex, byte value, Encoding expectedEncoding) { WriteByteNode.getUncached().execute(this, byteIndex, value, expectedEncoding); } /** * Node to create a new {@link MutableTruffleString} by concatenating two strings. * * @since 22.1 */ @ImportStatic(TStringGuards.class) @GeneratePackagePrivate @GenerateUncached public abstract static class ConcatNode extends Node { ConcatNode() { } /** * Creates a new {@link MutableTruffleString} by concatenating two strings. The * concatenation is performed eagerly since return value is mutable. * * @since 22.1 */ public abstract MutableTruffleString execute(AbstractTruffleString a, AbstractTruffleString b, Encoding expectedEncoding); @Specialization static MutableTruffleString concat(AbstractTruffleString a, AbstractTruffleString b, Encoding expectedEncoding, @Cached TruffleString.ToIndexableNode toIndexableNodeA, @Cached TruffleString.ToIndexableNode toIndexableNodeB, @Cached TStringInternalNodes.ConcatMaterializeBytesNode materializeBytesNode, @Cached BranchProfile outOfMemoryProfile) { a.checkEncoding(expectedEncoding); b.checkEncoding(expectedEncoding); int length = TruffleString.ConcatNode.addByteLengths(a, b, expectedEncoding.naturalStride, outOfMemoryProfile); int offset = 0; byte[] array = materializeBytesNode.execute(a, toIndexableNodeA.execute(a, a.data()), b, toIndexableNodeB.execute(b, b.data()), expectedEncoding, length, expectedEncoding.naturalStride); return MutableTruffleString.create(array, offset, length, expectedEncoding); } /** * Create a new {@link ConcatNode}. * * @since 22.1 */ public static ConcatNode create() { return MutableTruffleStringFactory.ConcatNodeGen.create(); } /** * Get the uncached version of {@link ConcatNode}. * * @since 22.1 */ public static ConcatNode getUncached() { return MutableTruffleStringFactory.ConcatNodeGen.getUncached(); } } /** * Shorthand for calling the uncached version of {@link ConcatNode}. * * @since 22.1 */ @TruffleBoundary public MutableTruffleString concatUncached(AbstractTruffleString b, Encoding expectedEncoding) { return ConcatNode.getUncached().execute(this, b, expectedEncoding); } /** * Node to create a new mutable substring of a string. See * {@link #execute(AbstractTruffleString, int, int, TruffleString.Encoding)} for details. * * @since 22.1 */ @ImportStatic(TStringGuards.class) @GeneratePackagePrivate @GenerateUncached public abstract static class SubstringNode extends Node { SubstringNode() { } /** * Create a new mutable substring of {@code a}, starting from {@code fromIndex}, with length * {@code length}. The substring is performed eagerly since return value is mutable. * * @since 22.1 */ public abstract MutableTruffleString execute(AbstractTruffleString a, int fromIndex, int length, Encoding expectedEncoding); @Specialization static MutableTruffleString substring(AbstractTruffleString a, int fromIndex, int length, Encoding expectedEncoding, @Cached TruffleString.ToIndexableNode toIndexableNode, @Cached TStringInternalNodes.GetCodeRangeNode getCodeRangeANode, @Cached TStringInternalNodes.GetCodePointLengthNode getCodePointLengthNode, @Cached TStringInternalNodes.CodePointIndexToRawNode translateIndexNode, @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { a.checkEncoding(expectedEncoding); a.boundsCheckRegion(fromIndex, length, getCodePointLengthNode); Object arrayA = toIndexableNode.execute(a, a.data()); final int codeRangeA = getCodeRangeANode.execute(a); int fromIndexRaw = translateIndexNode.execute(a, arrayA, codeRangeA, expectedEncoding, 0, fromIndex, length == 0); int lengthRaw = translateIndexNode.execute(a, arrayA, codeRangeA, expectedEncoding, fromIndexRaw, length, true); int stride = expectedEncoding.naturalStride; return SubstringByteIndexNode.createSubstring(a, fromIndexRaw << stride, lengthRaw << stride, expectedEncoding, copyToByteArrayNode); } /** * Create a new {@link SubstringNode}. * * @since 22.1 */ public static SubstringNode create() { return MutableTruffleStringFactory.SubstringNodeGen.create(); } /** * Get the uncached version of {@link SubstringNode}. * * @since 22.1 */ public static SubstringNode getUncached() { return MutableTruffleStringFactory.SubstringNodeGen.getUncached(); } } /** * Shorthand for calling the uncached version of {@link SubstringNode}. * * @since 22.1 */ @TruffleBoundary public MutableTruffleString substringUncached(int byteOffset, int byteLength, Encoding expectedEncoding) { return SubstringNode.getUncached().execute(this, byteOffset, byteLength, expectedEncoding); } /** * {@link SubstringNode}, but with byte indices. * * @since 22.1 */ @ImportStatic(TStringGuards.class) @GeneratePackagePrivate @GenerateUncached public abstract static class SubstringByteIndexNode extends Node { SubstringByteIndexNode() { } /** * {@link SubstringNode}, but with byte indices. * * @since 22.1 */ public abstract MutableTruffleString execute(AbstractTruffleString a, int byteOffset, int byteLength, Encoding expectedEncoding); @Specialization static MutableTruffleString substringByteIndex(AbstractTruffleString a, int byteOffset, int byteLength, Encoding expectedEncoding, @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { return createSubstring(a, byteOffset, byteLength, expectedEncoding, copyToByteArrayNode); } static MutableTruffleString createSubstring(AbstractTruffleString a, int byteOffset, int byteLength, Encoding expectedEncoding, TruffleString.CopyToByteArrayNode copyToByteArrayNode) { a.checkEncoding(expectedEncoding); checkByteLength(byteLength, expectedEncoding); a.boundsCheckRegionRaw(rawIndex(byteOffset, expectedEncoding), rawIndex(byteLength, expectedEncoding)); final byte[] array = new byte[byteLength]; copyToByteArrayNode.execute(a, byteOffset, array, 0, byteLength, expectedEncoding); return MutableTruffleString.create(array, 0, byteLength >> expectedEncoding.naturalStride, expectedEncoding); } /** * Create a new {@link SubstringByteIndexNode}. * * @since 22.1 */ public static SubstringByteIndexNode create() { return MutableTruffleStringFactory.SubstringByteIndexNodeGen.create(); } /** * Get the uncached version of {@link SubstringByteIndexNode}. * * @since 22.1 */ public static SubstringByteIndexNode getUncached() { return MutableTruffleStringFactory.SubstringByteIndexNodeGen.getUncached(); } } /** * Shorthand for calling the uncached version of {@link SubstringByteIndexNode}. * * @since 22.1 */ @TruffleBoundary public MutableTruffleString substringByteIndexUncached(int byteOffset, int byteLength, Encoding expectedEncoding) { return SubstringByteIndexNode.getUncached().execute(this, byteOffset, byteLength, expectedEncoding); } /** * Node to get a given string in a specific encoding. See * {@link #execute(AbstractTruffleString, TruffleString.Encoding)} for details. * * @since 22.1 */ @ImportStatic(TStringGuards.class) @GeneratePackagePrivate @GenerateUncached public abstract static class SwitchEncodingNode extends Node { SwitchEncodingNode() { } /** * Returns a version of string {@code a} that is encoded in the given encoding, which may be * the string itself or a converted version. *

* If no lossless conversion is possible, the string is converted on a best-effort basis; no * exception is thrown and characters which cannot be mapped in the target encoding are * replaced by {@code '\ufffd'} (for UTF-*) or {@code '?'}. * * @since 22.1 */ public abstract MutableTruffleString execute(AbstractTruffleString a, Encoding encoding); @Specialization(guards = "a.isCompatibleTo(encoding)") static MutableTruffleString compatibleMutable(MutableTruffleString a, @SuppressWarnings("unused") Encoding encoding) { return a; } @Specialization(guards = "!a.isCompatibleTo(encoding) || a.isImmutable()") static MutableTruffleString transcodeAndCopy(AbstractTruffleString a, Encoding encoding, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, @Cached AsMutableTruffleStringNode asMutableTruffleStringNode) { TruffleString switched = switchEncodingNode.execute(a, encoding); return asMutableTruffleStringNode.execute(switched, encoding); } /** * Create a new {@link MutableTruffleString.SwitchEncodingNode}. * * @since 22.1 */ public static MutableTruffleString.SwitchEncodingNode create() { return MutableTruffleStringFactory.SwitchEncodingNodeGen.create(); } /** * Get the uncached version of {@link MutableTruffleString.SwitchEncodingNode}. * * @since 22.1 */ public static MutableTruffleString.SwitchEncodingNode getUncached() { return MutableTruffleStringFactory.SwitchEncodingNodeGen.getUncached(); } } /** * Node to forcibly assign any encoding to a string. See * {@link #execute(AbstractTruffleString, TruffleString.Encoding, TruffleString.Encoding)} for * details. * * @since 22.1 */ @ImportStatic(TStringGuards.class) @GeneratePackagePrivate @GenerateUncached public abstract static class ForceEncodingNode extends Node { ForceEncodingNode() { } /** * Returns a version of string {@code a} assigned to the given encoding. If the string is * already in the given encoding, it is returned. Otherwise, a new string containing the * same (copied) bytes but assigned to the new encoding is returned. This node does not * transcode the string's contents in any way, it is the "encoding-equivalent" to a C-style * reinterpret-cast. * * @since 22.1 */ public abstract MutableTruffleString execute(AbstractTruffleString a, Encoding expectedEncoding, Encoding targetEncoding); @Specialization(guards = "a.isCompatibleTo(targetEncoding)") static MutableTruffleString compatible(MutableTruffleString a, Encoding expectedEncoding, @SuppressWarnings("unused") Encoding targetEncoding) { a.checkEncoding(expectedEncoding); return a; } @Specialization(guards = "!a.isCompatibleTo(targetEncoding) || a.isImmutable()") static MutableTruffleString reinterpret(AbstractTruffleString a, Encoding expectedEncoding, Encoding targetEncoding, @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { a.checkEncoding(expectedEncoding); return createCopying(a, expectedEncoding, targetEncoding, copyToByteArrayNode); } /** * Create a new {@link MutableTruffleString.ForceEncodingNode}. * * @since 22.1 */ public static MutableTruffleString.ForceEncodingNode create() { return MutableTruffleStringFactory.ForceEncodingNodeGen.create(); } /** * Get the uncached version of {@link MutableTruffleString.ForceEncodingNode}. * * @since 22.1 */ public static MutableTruffleString.ForceEncodingNode getUncached() { return MutableTruffleStringFactory.ForceEncodingNodeGen.getUncached(); } } @GenerateUncached abstract static class CalcLazyAttributesNode extends Node { abstract void execute(MutableTruffleString a); @Specialization void calc(MutableTruffleString a, @Cached("createClassProfile()") ValueProfile dataClassProfile, @Cached ConditionProfile asciiBytesLatinProfile, @Cached ConditionProfile utf8Profile, @Cached ConditionProfile utf8BrokenProfile, @Cached ConditionProfile utf16Profile, @Cached ConditionProfile utf16S0Profile, @Cached ConditionProfile utf32Profile, @Cached ConditionProfile utf32S0Profile, @Cached ConditionProfile utf32S1Profile, @Cached ConditionProfile exoticMaterializeNativeProfile, @Cached ConditionProfile exoticValidProfile, @Cached ConditionProfile exoticFixedWidthProfile) { final Object data = dataClassProfile.profile(a.data()); final int encoding = a.encoding(); final int offset = a.offset(); final int length = a.length(); final int codePointLength; final int codeRange; if (utf16Profile.profile(isUTF16(encoding))) { if (utf16S0Profile.profile(isStride0(a))) { codeRange = TStringOps.calcStringAttributesLatin1(this, data, offset, length); codePointLength = length; } else { assert isStride1(a); long attrs = TStringOps.calcStringAttributesUTF16(this, data, offset, length, false); codePointLength = StringAttributes.getCodePointLength(attrs); codeRange = StringAttributes.getCodeRange(attrs); } } else if (utf32Profile.profile(isUTF32(encoding))) { if (utf32S0Profile.profile(isStride0(a))) { codeRange = TStringOps.calcStringAttributesLatin1(this, data, offset, length); } else if (utf32S1Profile.profile(isStride1(a))) { codeRange = TStringOps.calcStringAttributesBMP(this, data, offset, length); } else { assert isStride2(a); codeRange = TStringOps.calcStringAttributesUTF32(this, data, offset, length); } codePointLength = length; } else { if (utf8Profile.profile(isUTF8(encoding))) { long attrs = TStringOps.calcStringAttributesUTF8(this, data, offset, length, false, false, utf8BrokenProfile); codeRange = StringAttributes.getCodeRange(attrs); codePointLength = StringAttributes.getCodePointLength(attrs); } else if (asciiBytesLatinProfile.profile(TStringGuards.isAsciiBytesOrLatin1(encoding))) { int cr = TStringOps.calcStringAttributesLatin1(this, data, offset, length); codeRange = TStringGuards.is8Bit(cr) ? TSCodeRange.asciiLatinBytesNonAsciiCodeRange(encoding) : cr; codePointLength = length; } else { if (data instanceof NativePointer) { ((NativePointer) data).materializeByteArray(a, exoticMaterializeNativeProfile); } long attrs = JCodings.getInstance().calcStringAttributes(this, data, offset, length, Encoding.get(encoding), exoticValidProfile, exoticFixedWidthProfile); codeRange = StringAttributes.getCodeRange(attrs); codePointLength = StringAttributes.getCodePointLength(attrs); } } a.updateCachedAttributes(codePointLength, codeRange); } } static MutableTruffleString createCopying(AbstractTruffleString a, Encoding encoding, TruffleString.CopyToByteArrayNode copyToByteArrayNode) { return createCopying(a, encoding, encoding, a.byteLength(encoding), copyToByteArrayNode); } static MutableTruffleString createCopying(AbstractTruffleString a, Encoding expectedEncoding, Encoding targetEncoding, TruffleString.CopyToByteArrayNode copyToByteArrayNode) { int byteLength = a.byteLength(expectedEncoding); checkByteLength(byteLength, targetEncoding); return createCopying(a, expectedEncoding, targetEncoding, byteLength, copyToByteArrayNode); } static MutableTruffleString createCopying(AbstractTruffleString a, Encoding expectedEncoding, Encoding targetEncoding, int byteLength, TruffleString.CopyToByteArrayNode copyToByteArrayNode) { final byte[] array = new byte[byteLength]; copyToByteArrayNode.execute(a, 0, array, 0, byteLength, expectedEncoding); return MutableTruffleString.create(array, 0, byteLength >> targetEncoding.naturalStride, targetEncoding); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy