| /* |
| * Copyright (c) 2015, 2016, 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. |
| * |
| * 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 the exception on overflow. |
| * @param len String length |
| * @return length |
| */ |
| private static int checkOverflow(int len) { |
| if (len < 0) { |
| throw new OutOfMemoryError("Overflow: String length out of range"); |
| } |
| return len; |
| } |
| |
| /** |
| * Mix value length into current length |
| * @param current current length |
| * @param value value to mix in |
| * @return new length |
| */ |
| static int mixLen(int current, boolean value) { |
| return checkOverflow(current + (value ? 4 : 5)); |
| } |
| |
| /** |
| * Mix value length into current length |
| * @param current current length |
| * @param value value to mix in |
| * @return new length |
| */ |
| static int mixLen(int current, byte value) { |
| return mixLen(current, (int)value); |
| } |
| |
| /** |
| * Mix value length into current length |
| * @param current current length |
| * @param value value to mix in |
| * @return new length |
| */ |
| static int mixLen(int current, char value) { |
| return checkOverflow(current + 1); |
| } |
| |
| /** |
| * Mix value length into current length |
| * @param current current length |
| * @param value value to mix in |
| * @return new length |
| */ |
| static int mixLen(int current, short value) { |
| return mixLen(current, (int)value); |
| } |
| |
| /** |
| * Mix value length into current length |
| * @param current current length |
| * @param value value to mix in |
| * @return new length |
| */ |
| static int mixLen(int current, int value) { |
| return checkOverflow(current + Integer.stringSize(value)); |
| } |
| |
| /** |
| * Mix value length into current length |
| * @param current current length |
| * @param value value to mix in |
| * @return new length |
| */ |
| static int mixLen(int current, long value) { |
| return checkOverflow(current + Long.stringSize(value)); |
| } |
| |
| /** |
| * Mix value length into current length |
| * @param current current length |
| * @param value value to mix in |
| * @return new length |
| */ |
| static int mixLen(int current, String value) { |
| return checkOverflow(current + value.length()); |
| } |
| |
| /** |
| * Mix coder into current coder |
| * @param current current coder |
| * @param value value to mix in |
| * @return new coder |
| */ |
| static byte mixCoder(byte current, char value) { |
| return (byte)(current | (StringLatin1.canEncode(value) ? 0 : 1)); |
| } |
| |
| /** |
| * Mix coder into current coder |
| * @param current current coder |
| * @param value value to mix in |
| * @return new coder |
| */ |
| static byte mixCoder(byte current, String value) { |
| return (byte)(current | value.coder()); |
| } |
| |
| /** |
| * Mix coder into current coder |
| * @param current current coder |
| * @param value value to mix in |
| * @return new coder |
| */ |
| static byte mixCoder(byte current, boolean value) { |
| // Booleans are represented with Latin1 |
| return current; |
| } |
| |
| /** |
| * Mix coder into current coder |
| * @param current current coder |
| * @param value value to mix in |
| * @return new coder |
| */ |
| static byte mixCoder(byte current, byte value) { |
| // Bytes are represented with Latin1 |
| return current; |
| } |
| |
| /** |
| * Mix coder into current coder |
| * @param current current coder |
| * @param value value to mix in |
| * @return new coder |
| */ |
| static byte mixCoder(byte current, short value) { |
| // Shorts are represented with Latin1 |
| return current; |
| } |
| |
| /** |
| * Mix coder into current coder |
| * @param current current coder |
| * @param value value to mix in |
| * @return new coder |
| */ |
| static byte mixCoder(byte current, int value) { |
| // Ints are represented with Latin1 |
| return current; |
| } |
| |
| /** |
| * Mix coder into current coder |
| * @param current current coder |
| * @param value value to mix in |
| * @return new coder |
| */ |
| static byte mixCoder(byte current, long value) { |
| // Longs are represented with Latin1 |
| return 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 index final char index in the buffer |
| * @param buf buffer to append to |
| * @param coder coder to add with |
| * @param value boolean value to encode |
| * @return new index |
| */ |
| static int prepend(int index, byte[] buf, byte coder, boolean value) { |
| if (coder == String.LATIN1) { |
| 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'; |
| } |
| } 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; |
| } |
| |
| /** |
| * Prepends the stringly representation of byte value into buffer, |
| * given the coder and final index. Index is measured in chars, not in bytes! |
| * |
| * @param index final char index in the buffer |
| * @param buf buffer to append to |
| * @param coder coder to add with |
| * @param value byte value to encode |
| * @return new index |
| */ |
| static int prepend(int index, byte[] buf, byte coder, byte value) { |
| return prepend(index, buf, coder, (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 index final char index in the buffer |
| * @param buf buffer to append to |
| * @param coder coder to add with |
| * @param value char value to encode |
| * @return new index |
| */ |
| static int prepend(int index, byte[] buf, byte coder, char value) { |
| if (coder == String.LATIN1) { |
| buf[--index] = (byte) (value & 0xFF); |
| } else { |
| StringUTF16.putChar(buf, --index, value); |
| } |
| return index; |
| } |
| |
| /** |
| * Prepends the stringly representation of short value into buffer, |
| * given the coder and final index. Index is measured in chars, not in bytes! |
| * |
| * @param index final char index in the buffer |
| * @param buf buffer to append to |
| * @param coder coder to add with |
| * @param value short value to encode |
| * @return new index |
| */ |
| static int prepend(int index, byte[] buf, byte coder, short value) { |
| return prepend(index, buf, coder, (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 index final char index in the buffer |
| * @param buf buffer to append to |
| * @param coder coder to add with |
| * @param value integer value to encode |
| * @return new index |
| */ |
| static int prepend(int index, byte[] buf, byte coder, int value) { |
| if (coder == String.LATIN1) { |
| return Integer.getChars(value, index, buf); |
| } else { |
| return Integer.getCharsUTF16(value, index, buf); |
| } |
| } |
| |
| /** |
| * Prepends the stringly representation of long value into buffer, |
| * given the coder and final index. Index is measured in chars, not in bytes! |
| * |
| * @param index final char index in the buffer |
| * @param buf buffer to append to |
| * @param coder coder to add with |
| * @param value long value to encode |
| * @return new index |
| */ |
| static int prepend(int index, byte[] buf, byte coder, long value) { |
| if (coder == String.LATIN1) { |
| return Long.getChars(value, index, buf); |
| } else { |
| return Long.getCharsUTF16(value, index, buf); |
| } |
| } |
| |
| /** |
| * Prepends the stringly representation of String value into buffer, |
| * given the coder and final index. Index is measured in chars, not in bytes! |
| * |
| * @param index final char index in the buffer |
| * @param buf buffer to append to |
| * @param coder coder to add with |
| * @param value String value to encode |
| * @return new index |
| */ |
| static int prepend(int index, byte[] buf, byte coder, String value) { |
| index -= value.length(); |
| value.getBytes(buf, index, coder); |
| return index; |
| } |
| |
| /** |
| * Instantiates the String with given buffer and coder |
| * @param buf buffer to use |
| * @param index remaining index |
| * @param coder coder to use |
| * @return String resulting string |
| */ |
| static String newString(byte[] buf, int index, byte coder) { |
| // Use the private, non-copying constructor (unsafe!) |
| if (index != 0) { |
| throw new InternalError("Storage is not completely initialized, " + index + " bytes left"); |
| } |
| return new String(buf, coder); |
| } |
| |
| /** |
| * Provides the initial coder for the String. |
| * @return initial coder |
| */ |
| static byte initialCoder() { |
| return String.COMPACT_STRINGS ? String.LATIN1 : String.UTF16; |
| } |
| |
| } |