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

org.pkl.thirdparty.truffle.api.strings.JCodingsImpl Maven / Gradle / Ivy

Go to download

Shaded fat Jar for pkl-config-java, a Java config library based on the Pkl config language.

There is a newer version: 0.27.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 org.pkl.thirdparty.truffle.api.strings;

import static org.pkl.thirdparty.truffle.api.strings.AbstractTruffleString.checkArrayRange;
import static org.pkl.thirdparty.truffle.api.strings.TStringGuards.isStride0;
import static org.pkl.thirdparty.truffle.api.strings.TStringGuards.isStride1;
import static org.pkl.thirdparty.truffle.api.strings.TStringGuards.isUTF16;
import static org.pkl.thirdparty.truffle.api.strings.TStringGuards.isUTF16Or32;
import static org.pkl.thirdparty.truffle.api.strings.TStringGuards.isUTF32;
import static org.pkl.thirdparty.truffle.api.strings.TStringGuards.isUTF8;

import java.util.Arrays;

import org.pkl.thirdparty.graalvm.collections.EconomicMap;
import org.pkl.thirdparty.graalvm.shadowed.org.jcodings.EncodingDB;
import org.pkl.thirdparty.graalvm.shadowed.org.jcodings.Ptr;
import org.pkl.thirdparty.graalvm.shadowed.org.jcodings.transcode.EConv;
import org.pkl.thirdparty.graalvm.shadowed.org.jcodings.transcode.EConvFlags;
import org.pkl.thirdparty.graalvm.shadowed.org.jcodings.transcode.EConvResult;
import org.pkl.thirdparty.graalvm.shadowed.org.jcodings.transcode.TranscoderDB;
import org.pkl.thirdparty.graalvm.shadowed.org.jcodings.util.CaseInsensitiveBytesHash;

import org.pkl.thirdparty.truffle.api.CompilerDirectives;
import org.pkl.thirdparty.truffle.api.CompilerDirectives.CompilationFinal;
import org.pkl.thirdparty.truffle.api.CompilerDirectives.TruffleBoundary;
import org.pkl.thirdparty.truffle.api.nodes.Node;
import org.pkl.thirdparty.truffle.api.profiles.InlinedBranchProfile;
import org.pkl.thirdparty.truffle.api.profiles.InlinedConditionProfile;
import org.pkl.thirdparty.truffle.api.strings.TruffleString.ErrorHandling;

final class JCodingsImpl implements JCodings {

    private static final class EncodingWrapper implements Encoding {
        private final org.pkl.thirdparty.graalvm.shadowed.org.jcodings.Encoding encoding;

        private EncodingWrapper(org.pkl.thirdparty.graalvm.shadowed.org.jcodings.Encoding encoding) {
            this.encoding = encoding;
        }
    }

    private static final int MAX_J_CODINGS_INDEX_VALUE = 0x7f;

    @CompilationFinal private static final EconomicMap J_CODINGS_MAP = createJCodingsMap();

    @TruffleBoundary
    private static EconomicMap createJCodingsMap() {
        CaseInsensitiveBytesHash encodings = EncodingDB.getEncodings();
        if (encodings.size() > MAX_J_CODINGS_INDEX_VALUE) {
            throw new RuntimeException(String.format("Assumption broken: org.pkl.thirdparty.graalvm.shadowed.org.jcodings has more than %d encodings (actual: %d)!", MAX_J_CODINGS_INDEX_VALUE, encodings.size()));
        }
        EconomicMap allEncodings = EconomicMap.create(encodings.size());
        for (EncodingDB.Entry entry : encodings) {
            org.pkl.thirdparty.graalvm.shadowed.org.jcodings.Encoding enc = entry.getEncoding();
            int i = enc.getIndex();
            if (i < 0 || i >= encodings.size()) {
                throw new RuntimeException(String.format(
                                "Assumption broken: index of org.pkl.thirdparty.graalvm.shadowed.org.jcodings encoding \"%s\" is greater than number of encodings (index: %d, number of encodings: %d)!", enc, i,
                                encodings.size()));
            }
            allEncodings.put(toEnumName(enc.toString()), new EncodingWrapper(enc));
        }
        return allEncodings;
    }

    @Override
    public Encoding get(String encodingName) {
        return J_CODINGS_MAP.get(encodingName);
    }

    @Override
    public Encoding get(TruffleString.Encoding encoding) {
        return encoding.jCoding;
    }

    @Override
    public String name(Encoding jCoding) {
        return unwrap(jCoding).toString();
    }

    @Override
    public int minLength(Encoding jCoding) {
        return unwrap(jCoding).minLength();
    }

    @Override
    public int maxLength(Encoding jCoding) {
        return unwrap(jCoding).maxLength();
    }

    @Override
    public boolean isFixedWidth(Encoding jCoding) {
        return unwrap(jCoding).isFixedWidth() && isSingleByte(jCoding);
    }

    @Override
    public boolean isSingleByte(Encoding jCoding) {
        return unwrap(jCoding).isSingleByte();
    }

    @Override
    @TruffleBoundary
    public int getCodePointLength(Encoding jCoding, int codepoint) {
        return unwrap(jCoding).codeToMbcLength(codepoint);
    }

    @Override
    @TruffleBoundary
    public int getPreviousCodePointIndex(Encoding jCoding, byte[] array, int arrayBegin, int index, int arrayEnd) {
        return unwrap(jCoding).prevCharHead(array, arrayBegin, index, arrayEnd);
    }

    @Override
    @TruffleBoundary
    public int getCodePointLength(Encoding jCoding, byte[] array, int index, int arrayLength) {
        return unwrap(jCoding).length(array, index, arrayLength);
    }

    @Override
    @TruffleBoundary
    public int readCodePoint(Encoding jCoding, byte[] array, int index, int arrayEnd, ErrorHandling errorHandling) {
        org.pkl.thirdparty.graalvm.shadowed.org.jcodings.Encoding jc = unwrap(jCoding);
        int codePoint = jc.mbcToCode(array, index, arrayEnd);
        if (jc.isUnicode() && Encodings.isUTF16Surrogate(codePoint)) {
            return Encodings.invalidCodepointReturnValue(errorHandling);
        }
        return codePoint;
    }

    @Override
    @TruffleBoundary
    public int writeCodePoint(Encoding jCoding, int codepoint, byte[] array, int index) {
        return unwrap(jCoding).codeToMbc(codepoint, array, index);
    }

    @Override
    @TruffleBoundary
    public int codePointIndexToRaw(Node location, AbstractTruffleString a, byte[] arrayA, int extraOffsetRaw, int index, boolean isLength, Encoding jCoding) {
        if (unwrap(jCoding).isFixedWidth()) {
            return index * minLength(jCoding);
        }
        int offset = a.byteArrayOffset() + extraOffsetRaw;
        int end = a.byteArrayOffset() + a.length();
        int cpi = 0;
        int i = 0;
        while (i < a.length() - extraOffsetRaw) {
            if (cpi == index) {
                return i;
            }
            int length = unwrap(jCoding).length(arrayA, offset + i, end);
            if (length < 1) {
                if (length < -1) {
                    // broken multibyte codepoint at end of string
                    if (isLength) {
                        return a.length() - extraOffsetRaw;
                    } else {
                        throw InternalErrors.indexOutOfBounds();
                    }
                } else {
                    i += unwrap(jCoding).minLength();
                }
            } else {
                i += length;
            }
            cpi++;
            TStringConstants.truffleSafePointPoll(location, cpi);
        }
        return TStringInternalNodes.CodePointIndexToRawNode.atEnd(a, extraOffsetRaw, index, isLength, cpi);
    }

    @Override
    public int decode(AbstractTruffleString a, byte[] arrayA, int rawIndex, Encoding jCoding, ErrorHandling errorHandling) {
        int p = a.byteArrayOffset() + rawIndex;
        int end = a.byteArrayOffset() + a.length();
        int length = getCodePointLength(jCoding, arrayA, p, end);
        if (length < 1) {
            return Encodings.invalidCodepointReturnValue(errorHandling);
        }
        return readCodePoint(jCoding, arrayA, p, end, errorHandling);
    }

    @Override
    public long calcStringAttributes(Node location, Object array, int offset, int length, TruffleString.Encoding encoding, int fromIndex, InlinedConditionProfile validCharacterProfile,
                    InlinedConditionProfile fixedWidthProfile) {
        if (TStringGuards.is7BitCompatible(encoding) && TStringOps.calcStringAttributesLatin1(location, array, offset + fromIndex, length) == TSCodeRange.get7Bit()) {
            return StringAttributes.create(length, TSCodeRange.get7Bit());
        }
        byte[] bytes = JCodings.asByteArray(array);
        int offsetBytes = array instanceof AbstractTruffleString.NativePointer ? fromIndex : offset + fromIndex;
        Encoding enc = get(encoding);
        int codeRange = TSCodeRange.getValid(isSingleByte(enc));
        int characters = 0;
        int p = offsetBytes;
        final int end = offsetBytes + length;
        int loopCount = 0;
        for (; p < end; characters++) {
            final int lengthOfCurrentCharacter = getCodePointLength(enc, bytes, p, end);
            if (validCharacterProfile.profile(location, lengthOfCurrentCharacter > 0 && p + lengthOfCurrentCharacter <= end)) {
                p += lengthOfCurrentCharacter;
            } else {
                codeRange = TSCodeRange.getBroken(isSingleByte(enc));
                // If a string is detected as broken, and we already know the character length
                // due to a fixed width encoding, there's no value in visiting any more ptr.
                if (fixedWidthProfile.profile(location, unwrap(enc).isFixedWidth())) {
                    characters = (length + minLength(enc) - 1) / minLength(enc);
                    return StringAttributes.create(characters, codeRange);
                } else {
                    p += minLength(enc);
                }
            }
            TStringConstants.truffleSafePointPoll(location, ++loopCount);
        }
        return StringAttributes.create(characters, codeRange);
    }

    @TruffleBoundary
    private static EConv getEconvTranscoder(Encoding jCodingSrc, Encoding jCodingDst) {
        return TranscoderDB.open(unwrap(jCodingSrc).getName(), unwrap(jCodingDst).getName(), EConvFlags.INVALID_REPLACE | EConvFlags.UNDEF_REPLACE);
    }

    @TruffleBoundary
    private static void econvSetReplacement(Encoding jCodingDst, EConv econv, byte[] replacement) {
        econv.setReplacement(replacement, 0, replacement.length, unwrap(jCodingDst).getName());
    }

    @TruffleBoundary
    private static EConvResult econvConvert(byte[] arrayA, byte[] buffer, EConv econv, Ptr srcPtr, Ptr dstPtr, int inStop) {
        return econv.convert(arrayA, srcPtr, inStop, buffer, dstPtr, buffer.length, 0);
    }

    private static final byte[] CONVERSION_REPLACEMENT = {'?'};
    private static final byte[] CONVERSION_REPLACEMENT_UTF_8 = {(byte) 0xEF, (byte) 0xBF, (byte) 0xBD};
    private static final byte[] CONVERSION_REPLACEMENT_UTF_16 = TStringGuards.littleEndian() ? new byte[]{(byte) 0xFD, (byte) 0xFF} : new byte[]{(byte) 0xFF, (byte) 0xFD};
    private static final byte[] CONVERSION_REPLACEMENT_UTF_32 = TStringGuards.littleEndian() ? new byte[]{(byte) 0xFD, (byte) 0xFF, 0, 0} : new byte[]{0, 0, (byte) 0xFF, (byte) 0xFD};

    @Override
    public TruffleString transcode(Node location, AbstractTruffleString a, Object arrayA, int codePointLengthA, TruffleString.Encoding targetEncoding,
                    InlinedBranchProfile outOfMemoryProfile,
                    InlinedConditionProfile nativeProfile,
                    TStringInternalNodes.FromBufferWithStringCompactionNode fromBufferWithStringCompactionNode) {
        final TruffleString.Encoding encoding = TruffleString.Encoding.get(a.encoding());
        final JCodings.Encoding jCodingSrc;
        if (isUTF16Or32(encoding) && isStride0(a)) {
            jCodingSrc = TruffleString.Encoding.ISO_8859_1.jCoding;
        } else if (isUTF32(encoding) && isStride1(a)) {
            jCodingSrc = TruffleString.Encoding.UTF_16.jCoding;
        } else {
            jCodingSrc = JCodings.getInstance().get(encoding);
        }
        JCodings.Encoding jCodingDst = JCodings.getInstance().get(targetEncoding);
        byte[] buffer = new byte[(int) Math.min(TStringConstants.MAX_ARRAY_SIZE, ((long) codePointLengthA) * JCodings.getInstance().maxLength(jCodingDst))];
        int length = 0;
        EConv econv = getEconvTranscoder(jCodingSrc, jCodingDst);
        boolean undefinedConversion = false;
        if (econv == null) {
            undefinedConversion = true;
            int loopCount = 0;
            for (int i = 0; i < codePointLengthA; i++) {
                int ret = JCodings.getInstance().writeCodePoint(jCodingDst, isUTF8(targetEncoding) || isUTF16Or32(targetEncoding) ? 0xfffd : '?', buffer, length);
                assert ret > 0;
                length += ret;
                TStringConstants.truffleSafePointPoll(location, ++loopCount);
            }
        } else {
            final byte[] replacement;
            if (isUTF8(targetEncoding)) {
                replacement = CONVERSION_REPLACEMENT_UTF_8;
            } else if (isUTF16(targetEncoding)) {
                replacement = CONVERSION_REPLACEMENT_UTF_16;
            } else if (isUTF32(targetEncoding)) {
                replacement = CONVERSION_REPLACEMENT_UTF_32;
            } else {
                replacement = CONVERSION_REPLACEMENT;
            }
            final Ptr srcPtr = new Ptr();
            final Ptr dstPtr = new Ptr();
            srcPtr.p = a.byteArrayOffset();
            dstPtr.p = 0;
            int inStop = a.byteArrayOffset() + (a.length() << a.stride());
            if (arrayA instanceof AbstractTruffleString.NativePointer) {
                ((AbstractTruffleString.NativePointer) arrayA).materializeByteArray(location, a, nativeProfile);
            }
            byte[] bytes = JCodings.asByteArray(arrayA);
            EConvResult result = econvConvert(bytes, buffer, econv, srcPtr, dstPtr, inStop);
            while (!result.isFinished()) {
                if (result.isUndefinedConversion()) {
                    undefinedConversion = true;
                    econvSetReplacement(jCodingDst, econv, replacement);
                } else if (result.isDestinationBufferFull()) {
                    if (buffer.length == TStringConstants.MAX_ARRAY_SIZE) {
                        outOfMemoryProfile.enter(location);
                        throw InternalErrors.outOfMemory();
                    }
                    buffer = Arrays.copyOf(buffer, (int) Math.min(TStringConstants.MAX_ARRAY_SIZE, ((long) buffer.length) << 1));
                } else {
                    throw CompilerDirectives.shouldNotReachHere();
                }
                result = econvConvert(bytes, buffer, econv, srcPtr, dstPtr, inStop);
            }
            length = dstPtr.p;
        }
        checkArrayRange(buffer, 0, length);
        return fromBufferWithStringCompactionNode.execute(location,
                        buffer, 0, length, targetEncoding, length != buffer.length || targetEncoding.isSupported(), undefinedConversion || a.isMutable());
    }

    @TruffleBoundary
    private static String toEnumName(String encodingName) {
        if ("ASCII-8BIT".equals(encodingName)) {
            return "BYTES";
        }
        String capitalized = encodingName;
        if (Character.isLowerCase(encodingName.charAt(0))) {
            capitalized = Character.toUpperCase(encodingName.charAt(0)) + encodingName.substring(1);
        }
        return capitalized.replace('-', '_');
    }

    private static org.pkl.thirdparty.graalvm.shadowed.org.jcodings.Encoding unwrap(Encoding wrapped) {
        return ((EncodingWrapper) wrapped).encoding;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy