/* * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.lang; /** * Helper for string concatenation. These methods are mostly looked up with private lookups * from {@link java.lang.invoke.StringConcatFactory}, and used in {@link java.lang.invoke.MethodHandle} * combinators there. */ final class StringConcatHelper { private StringConcatHelper() { // no instantiation } /** * Check for overflow, throw exception on overflow. * @param lengthCoder String length and coder * @return lengthCoder */ private static long checkOverflow(long lengthCoder) { if ((int)lengthCoder >= 0) { return lengthCoder; } throw new OutOfMemoryError("Overflow: String length out of range"); } /** * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in * @return new length and coder */ static long mix(long current, boolean value) { return checkOverflow(current + (value ? 4 : 5)); } /** * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in * @return new length and coder */ static long mix(long current, byte value) { return mix(current, (int)value); } /** * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in * @return new length and coder */ static long mix(long current, char value) { return checkOverflow(current + 1) | (StringLatin1.canEncode(value) ? 0 : UTF16); } /** * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in * @return new length and coder */ static long mix(long current, short value) { return mix(current, (int)value); } /** * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in * @return new length and coder */ static long mix(long current, int value) { return checkOverflow(current + Integer.stringSize(value)); } /** * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in * @return new length and coder */ static long mix(long current, long value) { return checkOverflow(current + Long.stringSize(value)); } /** * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in * @return new length and coder */ static long mix(long current, String value) { current += value.length(); if (value.coder() == String.UTF16) { current |= UTF16; } return checkOverflow(current); } /** * Prepends the stringly representation of boolean value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to * @param value boolean value to encode * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, boolean value) { int index = (int)indexCoder; if (indexCoder < UTF16) { if (value) { buf[--index] = 'e'; buf[--index] = 'u'; buf[--index] = 'r'; buf[--index] = 't'; } else { buf[--index] = 'e'; buf[--index] = 's'; buf[--index] = 'l'; buf[--index] = 'a'; buf[--index] = 'f'; } return index; } else { if (value) { StringUTF16.putChar(buf, --index, 'e'); StringUTF16.putChar(buf, --index, 'u'); StringUTF16.putChar(buf, --index, 'r'); StringUTF16.putChar(buf, --index, 't'); } else { StringUTF16.putChar(buf, --index, 'e'); StringUTF16.putChar(buf, --index, 's'); StringUTF16.putChar(buf, --index, 'l'); StringUTF16.putChar(buf, --index, 'a'); StringUTF16.putChar(buf, --index, 'f'); } return index | UTF16; } } /** * Prepends the stringly representation of byte value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to * @param value byte value to encode * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, byte value) { return prepend(indexCoder, buf, (int)value); } /** * Prepends the stringly representation of char value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to * @param value char value to encode * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, char value) { if (indexCoder < UTF16) { buf[(int)(--indexCoder)] = (byte) (value & 0xFF); } else { StringUTF16.putChar(buf, (int)(--indexCoder), value); } return indexCoder; } /** * Prepends the stringly representation of short value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to * @param value short value to encode * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, short value) { return prepend(indexCoder, buf, (int)value); } /** * Prepends the stringly representation of integer value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to * @param value integer value to encode * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, int value) { if (indexCoder < UTF16) { return Integer.getChars(value, (int)indexCoder, buf); } else { return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16; } } /** * Prepends the stringly representation of long value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to * @param value long value to encode * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, long value) { if (indexCoder < UTF16) { return Long.getChars(value, (int)indexCoder, buf); } else { return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16; } } /** * Prepends the stringly representation of String value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to * @param value String value to encode * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, String value) { indexCoder -= value.length(); if (indexCoder < UTF16) { value.getBytes(buf, (int)indexCoder, String.LATIN1); } else { value.getBytes(buf, (int)indexCoder, String.UTF16); } return indexCoder; } /** * Instantiates the String with given buffer and coder * @param buf buffer to use * @param indexCoder remaining index (should be zero) and coder * @return String resulting string */ static String newString(byte[] buf, long indexCoder) { // Use the private, non-copying constructor (unsafe!) if (indexCoder == LATIN1) { return new String(buf, String.LATIN1); } else if (indexCoder == UTF16) { return new String(buf, String.UTF16); } else { throw new InternalError("Storage is not completely initialized, " + (int)indexCoder + " bytes left"); } } private static final long LATIN1 = (long)String.LATIN1 << 32; private static final long UTF16 = (long)String.UTF16 << 32; /** * Provides the initial coder for the String. * @return initial coder, adjusted into the upper half */ static long initialCoder() { return String.COMPACT_STRINGS ? LATIN1 : UTF16; } }