358 lines
11 KiB
Java
358 lines
11 KiB
Java
/*
|
|
* 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;
|
|
}
|
|
|
|
}
|