Flatbuffers library added to the list of third party libraries.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.flatbuffers;
|
||||
|
||||
import static com.google.flatbuffers.Constants.*;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/// @file
|
||||
/// @addtogroup flatbuffers_java_api
|
||||
/// @{
|
||||
|
||||
/**
|
||||
* Class that collects utility functions around `ByteBuffer`.
|
||||
*/
|
||||
public class ByteBufferUtil {
|
||||
|
||||
/**
|
||||
* Extract the size prefix from a `ByteBuffer`.
|
||||
*
|
||||
* @param bb a size-prefixed buffer
|
||||
* @return the size prefix
|
||||
*/
|
||||
public static int getSizePrefix(ByteBuffer bb) {
|
||||
return bb.getInt(bb.position());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a duplicate of a size-prefixed `ByteBuffer` that has its position
|
||||
* advanced just past the size prefix.
|
||||
*
|
||||
* @param bb a size-prefixed buffer
|
||||
* @return a new buffer on the same underlying data that has skipped the
|
||||
* size prefix
|
||||
*/
|
||||
public static ByteBuffer removeSizePrefix(ByteBuffer bb) {
|
||||
ByteBuffer s = bb.duplicate();
|
||||
s.position(s.position() + SIZE_PREFIX_LENGTH);
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.flatbuffers;
|
||||
|
||||
/// @cond FLATBUFFERS_INTERNAL
|
||||
|
||||
/**
|
||||
* Class that holds shared constants
|
||||
*/
|
||||
public class Constants {
|
||||
// Java doesn't seem to have these.
|
||||
/** The number of bytes in an `byte`. */
|
||||
static final int SIZEOF_BYTE = 1;
|
||||
/** The number of bytes in a `short`. */
|
||||
static final int SIZEOF_SHORT = 2;
|
||||
/** The number of bytes in an `int`. */
|
||||
static final int SIZEOF_INT = 4;
|
||||
/** The number of bytes in an `float`. */
|
||||
static final int SIZEOF_FLOAT = 4;
|
||||
/** The number of bytes in an `long`. */
|
||||
static final int SIZEOF_LONG = 8;
|
||||
/** The number of bytes in an `double`. */
|
||||
static final int SIZEOF_DOUBLE = 8;
|
||||
/** The number of bytes in a file identifier. */
|
||||
static final int FILE_IDENTIFIER_LENGTH = 4;
|
||||
/** The number of bytes in a size prefix. */
|
||||
public static final int SIZE_PREFIX_LENGTH = 4;
|
||||
/** A version identifier to force a compile error if someone
|
||||
accidentally tries to build generated code with a runtime of
|
||||
two mismatched version. Versions need to always match, as
|
||||
the runtime and generated code are modified in sync.
|
||||
Changes to the Java implementation need to be sure to change
|
||||
the version here and in the code generator on every possible
|
||||
incompatible change */
|
||||
public static void FLATBUFFERS_1_11_1() {}
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.flatbuffers;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/// @cond FLATBUFFERS_INTERNAL
|
||||
|
||||
/**
|
||||
* All structs in the generated code derive from this class, and add their own accessors.
|
||||
*/
|
||||
public class Struct {
|
||||
/** Used to hold the position of the `bb` buffer. */
|
||||
protected int bb_pos;
|
||||
/** The underlying ByteBuffer to hold the data of the Struct. */
|
||||
protected ByteBuffer bb;
|
||||
|
||||
/**
|
||||
* Re-init the internal state with an external buffer {@code ByteBuffer} and an offset within.
|
||||
*
|
||||
* This method exists primarily to allow recycling Table instances without risking memory leaks
|
||||
* due to {@code ByteBuffer} references.
|
||||
*/
|
||||
protected void __reset(int _i, ByteBuffer _bb) {
|
||||
bb = _bb;
|
||||
if (bb != null) {
|
||||
bb_pos = _i;
|
||||
} else {
|
||||
bb_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets internal state with a null {@code ByteBuffer} and a zero position.
|
||||
*
|
||||
* This method exists primarily to allow recycling Struct instances without risking memory leaks
|
||||
* due to {@code ByteBuffer} references. The instance will be unusable until it is assigned
|
||||
* again to a {@code ByteBuffer}.
|
||||
*
|
||||
* @param struct the instance to reset to initial state
|
||||
*/
|
||||
public void __reset() {
|
||||
__reset(0, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.flatbuffers;
|
||||
|
||||
import static com.google.flatbuffers.Constants.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/// @cond FLATBUFFERS_INTERNAL
|
||||
|
||||
/**
|
||||
* All tables in the generated code derive from this class, and add their own accessors.
|
||||
*/
|
||||
public class Table {
|
||||
public final static ThreadLocal<Charset> UTF8_CHARSET = new ThreadLocal<Charset>() {
|
||||
@Override
|
||||
protected Charset initialValue() {
|
||||
return Charset.forName("UTF-8");
|
||||
}
|
||||
};
|
||||
/** Used to hold the position of the `bb` buffer. */
|
||||
protected int bb_pos;
|
||||
/** The underlying ByteBuffer to hold the data of the Table. */
|
||||
protected ByteBuffer bb;
|
||||
/** Used to hold the vtable position. */
|
||||
private int vtable_start;
|
||||
/** Used to hold the vtable size. */
|
||||
private int vtable_size;
|
||||
Utf8 utf8 = Utf8.getDefault();
|
||||
|
||||
/**
|
||||
* Get the underlying ByteBuffer.
|
||||
*
|
||||
* @return Returns the Table's ByteBuffer.
|
||||
*/
|
||||
public ByteBuffer getByteBuffer() { return bb; }
|
||||
|
||||
/**
|
||||
* Look up a field in the vtable.
|
||||
*
|
||||
* @param vtable_offset An `int` offset to the vtable in the Table's ByteBuffer.
|
||||
* @return Returns an offset into the object, or `0` if the field is not present.
|
||||
*/
|
||||
protected int __offset(int vtable_offset) {
|
||||
return vtable_offset < vtable_size ? bb.getShort(vtable_start + vtable_offset) : 0;
|
||||
}
|
||||
|
||||
protected static int __offset(int vtable_offset, int offset, ByteBuffer bb) {
|
||||
int vtable = bb.capacity() - offset;
|
||||
return bb.getShort(vtable + vtable_offset - bb.getInt(vtable)) + vtable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a relative offset.
|
||||
*
|
||||
* @param offset An `int` index into the Table's ByteBuffer containing the relative offset.
|
||||
* @return Returns the relative offset stored at `offset`.
|
||||
*/
|
||||
protected int __indirect(int offset) {
|
||||
return offset + bb.getInt(offset);
|
||||
}
|
||||
|
||||
protected static int __indirect(int offset, ByteBuffer bb) {
|
||||
return offset + bb.getInt(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Java `String` from UTF-8 data stored inside the FlatBuffer.
|
||||
*
|
||||
* This allocates a new string and converts to wide chars upon each access,
|
||||
* which is not very efficient. Instead, each FlatBuffer string also comes with an
|
||||
* accessor based on __vector_as_bytebuffer below, which is much more efficient,
|
||||
* assuming your Java program can handle UTF-8 data directly.
|
||||
*
|
||||
* @param offset An `int` index into the Table's ByteBuffer.
|
||||
* @return Returns a `String` from the data stored inside the FlatBuffer at `offset`.
|
||||
*/
|
||||
protected String __string(int offset) {
|
||||
offset += bb.getInt(offset);
|
||||
int length = bb.getInt(offset);
|
||||
return utf8.decodeUtf8(bb, offset + SIZEOF_INT, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of a vector.
|
||||
*
|
||||
* @param offset An `int` index into the Table's ByteBuffer.
|
||||
* @return Returns the length of the vector whose offset is stored at `offset`.
|
||||
*/
|
||||
protected int __vector_len(int offset) {
|
||||
offset += bb_pos;
|
||||
offset += bb.getInt(offset);
|
||||
return bb.getInt(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the start data of a vector.
|
||||
*
|
||||
* @param offset An `int` index into the Table's ByteBuffer.
|
||||
* @return Returns the start of the vector data whose offset is stored at `offset`.
|
||||
*/
|
||||
protected int __vector(int offset) {
|
||||
offset += bb_pos;
|
||||
return offset + bb.getInt(offset) + SIZEOF_INT; // data starts after the length
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a whole vector as a ByteBuffer.
|
||||
*
|
||||
* This is efficient, since it only allocates a new {@link ByteBuffer} object,
|
||||
* but does not actually copy the data, it still refers to the same bytes
|
||||
* as the original ByteBuffer. Also useful with nested FlatBuffers, etc.
|
||||
*
|
||||
* @param vector_offset The position of the vector in the byte buffer
|
||||
* @param elem_size The size of each element in the array
|
||||
* @return The {@link ByteBuffer} for the array
|
||||
*/
|
||||
protected ByteBuffer __vector_as_bytebuffer(int vector_offset, int elem_size) {
|
||||
int o = __offset(vector_offset);
|
||||
if (o == 0) return null;
|
||||
ByteBuffer bb = this.bb.duplicate().order(ByteOrder.LITTLE_ENDIAN);
|
||||
int vectorstart = __vector(o);
|
||||
bb.position(vectorstart);
|
||||
bb.limit(vectorstart + __vector_len(o) * elem_size);
|
||||
return bb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize vector as a ByteBuffer.
|
||||
*
|
||||
* This is more efficient than using duplicate, since it doesn't copy the data
|
||||
* nor allocattes a new {@link ByteBuffer}, creating no garbage to be collected.
|
||||
*
|
||||
* @param bb The {@link ByteBuffer} for the array
|
||||
* @param vector_offset The position of the vector in the byte buffer
|
||||
* @param elem_size The size of each element in the array
|
||||
* @return The {@link ByteBuffer} for the array
|
||||
*/
|
||||
protected ByteBuffer __vector_in_bytebuffer(ByteBuffer bb, int vector_offset, int elem_size) {
|
||||
int o = this.__offset(vector_offset);
|
||||
if (o == 0) return null;
|
||||
int vectorstart = __vector(o);
|
||||
bb.rewind();
|
||||
bb.limit(vectorstart + __vector_len(o) * elem_size);
|
||||
bb.position(vectorstart);
|
||||
return bb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize any Table-derived type to point to the union at the given `offset`.
|
||||
*
|
||||
* @param t A `Table`-derived type that should point to the union at `offset`.
|
||||
* @param offset An `int` index into the Table's ByteBuffer.
|
||||
* @return Returns the Table that points to the union at `offset`.
|
||||
*/
|
||||
protected Table __union(Table t, int offset) {
|
||||
offset += bb_pos;
|
||||
t.bb_pos = offset + bb.getInt(offset);
|
||||
t.bb = bb;
|
||||
t.vtable_start = t.bb_pos - bb.getInt(t.bb_pos);
|
||||
t.vtable_size = bb.getShort(t.vtable_start);
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a {@link ByteBuffer} contains a file identifier.
|
||||
*
|
||||
* @param bb A {@code ByteBuffer} to check if it contains the identifier
|
||||
* `ident`.
|
||||
* @param ident A `String` identifier of the FlatBuffer file.
|
||||
* @return True if the buffer contains the file identifier
|
||||
*/
|
||||
protected static boolean __has_identifier(ByteBuffer bb, String ident) {
|
||||
if (ident.length() != FILE_IDENTIFIER_LENGTH)
|
||||
throw new AssertionError("FlatBuffers: file identifier must be length " +
|
||||
FILE_IDENTIFIER_LENGTH);
|
||||
for (int i = 0; i < FILE_IDENTIFIER_LENGTH; i++) {
|
||||
if (ident.charAt(i) != (char)bb.get(bb.position() + SIZEOF_INT + i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort tables by the key.
|
||||
*
|
||||
* @param offsets An 'int' indexes of the tables into the bb.
|
||||
* @param bb A {@code ByteBuffer} to get the tables.
|
||||
*/
|
||||
protected void sortTables(int[] offsets, final ByteBuffer bb) {
|
||||
Integer[] off = new Integer[offsets.length];
|
||||
for (int i = 0; i < offsets.length; i++) off[i] = offsets[i];
|
||||
java.util.Arrays.sort(off, new java.util.Comparator<Integer>() {
|
||||
public int compare(Integer o1, Integer o2) {
|
||||
return keysCompare(o1, o2, bb);
|
||||
}
|
||||
});
|
||||
for (int i = 0; i < offsets.length; i++) offsets[i] = off[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two tables by the key.
|
||||
*
|
||||
* @param o1 An 'Integer' index of the first key into the bb.
|
||||
* @param o2 An 'Integer' index of the second key into the bb.
|
||||
* @param bb A {@code ByteBuffer} to get the keys.
|
||||
*/
|
||||
protected int keysCompare(Integer o1, Integer o2, ByteBuffer bb) { return 0; }
|
||||
|
||||
/**
|
||||
* Compare two strings in the buffer.
|
||||
*
|
||||
* @param offset_1 An 'int' index of the first string into the bb.
|
||||
* @param offset_2 An 'int' index of the second string into the bb.
|
||||
* @param bb A {@code ByteBuffer} to get the strings.
|
||||
*/
|
||||
protected static int compareStrings(int offset_1, int offset_2, ByteBuffer bb) {
|
||||
offset_1 += bb.getInt(offset_1);
|
||||
offset_2 += bb.getInt(offset_2);
|
||||
int len_1 = bb.getInt(offset_1);
|
||||
int len_2 = bb.getInt(offset_2);
|
||||
int startPos_1 = offset_1 + SIZEOF_INT;
|
||||
int startPos_2 = offset_2 + SIZEOF_INT;
|
||||
int len = Math.min(len_1, len_2);
|
||||
for(int i = 0; i < len; i++) {
|
||||
if (bb.get(i + startPos_1) != bb.get(i + startPos_2))
|
||||
return bb.get(i + startPos_1) - bb.get(i + startPos_2);
|
||||
}
|
||||
return len_1 - len_2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare string from the buffer with the 'String' object.
|
||||
*
|
||||
* @param offset_1 An 'int' index of the first string into the bb.
|
||||
* @param key Second string as a byte array.
|
||||
* @param bb A {@code ByteBuffer} to get the first string.
|
||||
*/
|
||||
protected static int compareStrings(int offset_1, byte[] key, ByteBuffer bb) {
|
||||
offset_1 += bb.getInt(offset_1);
|
||||
int len_1 = bb.getInt(offset_1);
|
||||
int len_2 = key.length;
|
||||
int startPos_1 = offset_1 + Constants.SIZEOF_INT;
|
||||
int len = Math.min(len_1, len_2);
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (bb.get(i + startPos_1) != key[i])
|
||||
return bb.get(i + startPos_1) - key[i];
|
||||
}
|
||||
return len_1 - len_2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-init the internal state with an external buffer {@code ByteBuffer} and an offset within.
|
||||
*
|
||||
* This method exists primarily to allow recycling Table instances without risking memory leaks
|
||||
* due to {@code ByteBuffer} references.
|
||||
*/
|
||||
protected void __reset(int _i, ByteBuffer _bb) {
|
||||
bb = _bb;
|
||||
if (bb != null) {
|
||||
bb_pos = _i;
|
||||
vtable_start = bb_pos - bb.getInt(bb_pos);
|
||||
vtable_size = bb.getShort(vtable_start);
|
||||
} else {
|
||||
bb_pos = 0;
|
||||
vtable_start = 0;
|
||||
vtable_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the internal state with a null {@code ByteBuffer} and a zero position.
|
||||
*
|
||||
* This method exists primarily to allow recycling Table instances without risking memory leaks
|
||||
* due to {@code ByteBuffer} references. The instance will be unusable until it is assigned
|
||||
* again to a {@code ByteBuffer}.
|
||||
*/
|
||||
public void __reset() {
|
||||
__reset(0, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.flatbuffers;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static java.lang.Character.MIN_HIGH_SURROGATE;
|
||||
import static java.lang.Character.MIN_LOW_SURROGATE;
|
||||
import static java.lang.Character.MIN_SUPPLEMENTARY_CODE_POINT;
|
||||
|
||||
public abstract class Utf8 {
|
||||
|
||||
/**
|
||||
* Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string,
|
||||
* this method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in
|
||||
* both time and space.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired
|
||||
* surrogates)
|
||||
*/
|
||||
public abstract int encodedLength(CharSequence sequence);
|
||||
|
||||
/**
|
||||
* Encodes the given characters to the target {@link ByteBuffer} using UTF-8 encoding.
|
||||
*
|
||||
* <p>Selects an optimal algorithm based on the type of {@link ByteBuffer} (i.e. heap or direct)
|
||||
* and the capabilities of the platform.
|
||||
*
|
||||
* @param in the source string to be encoded
|
||||
* @param out the target buffer to receive the encoded string.
|
||||
*/
|
||||
public abstract void encodeUtf8(CharSequence in, ByteBuffer out);
|
||||
|
||||
/**
|
||||
* Decodes the given UTF-8 portion of the {@link ByteBuffer} into a {@link String}.
|
||||
*
|
||||
* @throws IllegalArgumentException if the input is not valid UTF-8.
|
||||
*/
|
||||
public abstract String decodeUtf8(ByteBuffer buffer, int offset, int length);
|
||||
|
||||
private static Utf8 DEFAULT;
|
||||
|
||||
/**
|
||||
* Get the default UTF-8 processor.
|
||||
* @return the default processor
|
||||
*/
|
||||
public static Utf8 getDefault() {
|
||||
if (DEFAULT == null) {
|
||||
DEFAULT = new Utf8Safe();
|
||||
}
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default instance of the UTF-8 processor.
|
||||
* @param instance the new instance to use
|
||||
*/
|
||||
public static void setDefault(Utf8 instance) {
|
||||
DEFAULT = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility methods for decoding bytes into {@link String}. Callers are responsible for extracting
|
||||
* bytes (possibly using Unsafe methods), and checking remaining bytes. All other UTF-8 validity
|
||||
* checks and codepoint conversion happen in this class.
|
||||
*/
|
||||
static class DecodeUtil {
|
||||
|
||||
/**
|
||||
* Returns whether this is a single-byte codepoint (i.e., ASCII) with the form '0XXXXXXX'.
|
||||
*/
|
||||
static boolean isOneByte(byte b) {
|
||||
return b >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this is a two-byte codepoint with the form '10XXXXXX'.
|
||||
*/
|
||||
static boolean isTwoBytes(byte b) {
|
||||
return b < (byte) 0xE0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this is a three-byte codepoint with the form '110XXXXX'.
|
||||
*/
|
||||
static boolean isThreeBytes(byte b) {
|
||||
return b < (byte) 0xF0;
|
||||
}
|
||||
|
||||
static void handleOneByte(byte byte1, char[] resultArr, int resultPos) {
|
||||
resultArr[resultPos] = (char) byte1;
|
||||
}
|
||||
|
||||
static void handleTwoBytes(
|
||||
byte byte1, byte byte2, char[] resultArr, int resultPos)
|
||||
throws IllegalArgumentException {
|
||||
// Simultaneously checks for illegal trailing-byte in leading position (<= '11000000') and
|
||||
// overlong 2-byte, '11000001'.
|
||||
if (byte1 < (byte) 0xC2) {
|
||||
throw new IllegalArgumentException("Invalid UTF-8: Illegal leading byte in 2 bytes utf");
|
||||
}
|
||||
if (isNotTrailingByte(byte2)) {
|
||||
throw new IllegalArgumentException("Invalid UTF-8: Illegal trailing byte in 2 bytes utf");
|
||||
}
|
||||
resultArr[resultPos] = (char) (((byte1 & 0x1F) << 6) | trailingByteValue(byte2));
|
||||
}
|
||||
|
||||
static void handleThreeBytes(
|
||||
byte byte1, byte byte2, byte byte3, char[] resultArr, int resultPos)
|
||||
throws IllegalArgumentException {
|
||||
if (isNotTrailingByte(byte2)
|
||||
// overlong? 5 most significant bits must not all be zero
|
||||
|| (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
|
||||
// check for illegal surrogate codepoints
|
||||
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
|
||||
|| isNotTrailingByte(byte3)) {
|
||||
throw new IllegalArgumentException("Invalid UTF-8");
|
||||
}
|
||||
resultArr[resultPos] = (char)
|
||||
(((byte1 & 0x0F) << 12) | (trailingByteValue(byte2) << 6) | trailingByteValue(byte3));
|
||||
}
|
||||
|
||||
static void handleFourBytes(
|
||||
byte byte1, byte byte2, byte byte3, byte byte4, char[] resultArr, int resultPos)
|
||||
throws IllegalArgumentException{
|
||||
if (isNotTrailingByte(byte2)
|
||||
// Check that 1 <= plane <= 16. Tricky optimized form of:
|
||||
// valid 4-byte leading byte?
|
||||
// if (byte1 > (byte) 0xF4 ||
|
||||
// overlong? 4 most significant bits must not all be zero
|
||||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
|
||||
// codepoint larger than the highest code point (U+10FFFF)?
|
||||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|
||||
|| (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
|
||||
|| isNotTrailingByte(byte3)
|
||||
|| isNotTrailingByte(byte4)) {
|
||||
throw new IllegalArgumentException("Invalid UTF-8");
|
||||
}
|
||||
int codepoint = ((byte1 & 0x07) << 18)
|
||||
| (trailingByteValue(byte2) << 12)
|
||||
| (trailingByteValue(byte3) << 6)
|
||||
| trailingByteValue(byte4);
|
||||
resultArr[resultPos] = DecodeUtil.highSurrogate(codepoint);
|
||||
resultArr[resultPos + 1] = DecodeUtil.lowSurrogate(codepoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the byte is not a valid continuation of the form '10XXXXXX'.
|
||||
*/
|
||||
private static boolean isNotTrailingByte(byte b) {
|
||||
return b > (byte) 0xBF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual value of the trailing byte (removes the prefix '10') for composition.
|
||||
*/
|
||||
private static int trailingByteValue(byte b) {
|
||||
return b & 0x3F;
|
||||
}
|
||||
|
||||
private static char highSurrogate(int codePoint) {
|
||||
return (char) ((MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT >>> 10))
|
||||
+ (codePoint >>> 10));
|
||||
}
|
||||
|
||||
private static char lowSurrogate(int codePoint) {
|
||||
return (char) (MIN_LOW_SURROGATE + (codePoint & 0x3ff));
|
||||
}
|
||||
}
|
||||
|
||||
// These UTF-8 handling methods are copied from Guava's Utf8Unsafe class with a modification to throw
|
||||
// a protocol buffer local exception. This exception is then caught in CodedOutputStream so it can
|
||||
// fallback to more lenient behavior.
|
||||
static class UnpairedSurrogateException extends IllegalArgumentException {
|
||||
UnpairedSurrogateException(int index, int length) {
|
||||
super("Unpaired surrogate at index " + index + " of " + length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.flatbuffers;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* This class implements the Utf8 API using the Java Utf8 encoder. Use
|
||||
* Utf8.setDefault(new Utf8Old()); to use it.
|
||||
*/
|
||||
public class Utf8Old extends Utf8 {
|
||||
|
||||
private static class Cache {
|
||||
final CharsetEncoder encoder;
|
||||
final CharsetDecoder decoder;
|
||||
CharSequence lastInput = null;
|
||||
ByteBuffer lastOutput = null;
|
||||
|
||||
Cache() {
|
||||
encoder = StandardCharsets.UTF_8.newEncoder();
|
||||
decoder = StandardCharsets.UTF_8.newDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
private static final ThreadLocal<Cache> CACHE =
|
||||
ThreadLocal.withInitial(() -> new Cache());
|
||||
|
||||
// Play some games so that the old encoder doesn't pay twice for computing
|
||||
// the length of the encoded string.
|
||||
|
||||
@Override
|
||||
public int encodedLength(CharSequence in) {
|
||||
final Cache cache = CACHE.get();
|
||||
int estimated = (int) (in.length() * cache.encoder.maxBytesPerChar());
|
||||
if (cache.lastOutput == null || cache.lastOutput.capacity() < estimated) {
|
||||
cache.lastOutput = ByteBuffer.allocate(Math.max(128, estimated));
|
||||
}
|
||||
cache.lastOutput.clear();
|
||||
cache.lastInput = in;
|
||||
CharBuffer wrap = (in instanceof CharBuffer) ?
|
||||
(CharBuffer) in : CharBuffer.wrap(in);
|
||||
CoderResult result = cache.encoder.encode(wrap, cache.lastOutput, true);
|
||||
if (result.isError()) {
|
||||
try {
|
||||
result.throwException();
|
||||
} catch (CharacterCodingException e) {
|
||||
throw new IllegalArgumentException("bad character encoding", e);
|
||||
}
|
||||
}
|
||||
cache.lastOutput.flip();
|
||||
return cache.lastOutput.remaining();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeUtf8(CharSequence in, ByteBuffer out) {
|
||||
final Cache cache = CACHE.get();
|
||||
if (cache.lastInput != in) {
|
||||
// Update the lastOutput to match our input, although flatbuffer should
|
||||
// never take this branch.
|
||||
encodedLength(in);
|
||||
}
|
||||
out.put(cache.lastOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeUtf8(ByteBuffer buffer, int offset, int length) {
|
||||
CharsetDecoder decoder = CACHE.get().decoder;
|
||||
decoder.reset();
|
||||
buffer = buffer.duplicate();
|
||||
buffer.position(offset);
|
||||
buffer.limit(offset + length);
|
||||
try {
|
||||
CharBuffer result = decoder.decode(buffer);
|
||||
return result.toString();
|
||||
} catch (CharacterCodingException e) {
|
||||
throw new IllegalArgumentException("Bad encoding", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.flatbuffers;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import static java.lang.Character.MAX_SURROGATE;
|
||||
import static java.lang.Character.MIN_SUPPLEMENTARY_CODE_POINT;
|
||||
import static java.lang.Character.MIN_SURROGATE;
|
||||
import static java.lang.Character.isSurrogatePair;
|
||||
import static java.lang.Character.toCodePoint;
|
||||
|
||||
/**
|
||||
* A set of low-level, high-performance static utility methods related
|
||||
* to the UTF-8 character encoding. This class has no dependencies
|
||||
* outside of the core JDK libraries.
|
||||
*
|
||||
* <p>There are several variants of UTF-8. The one implemented by
|
||||
* this class is the restricted definition of UTF-8 introduced in
|
||||
* Unicode 3.1, which mandates the rejection of "overlong" byte
|
||||
* sequences as well as rejection of 3-byte surrogate codepoint byte
|
||||
* sequences. Note that the UTF-8 decoder included in Oracle's JDK
|
||||
* has been modified to also reject "overlong" byte sequences, but (as
|
||||
* of 2011) still accepts 3-byte surrogate codepoint byte sequences.
|
||||
*
|
||||
* <p>The byte sequences considered valid by this class are exactly
|
||||
* those that can be roundtrip converted to Strings and back to bytes
|
||||
* using the UTF-8 charset, without loss: <pre> {@code
|
||||
* Arrays.equals(bytes, new String(bytes, Internal.UTF_8).getBytes(Internal.UTF_8))
|
||||
* }</pre>
|
||||
*
|
||||
* <p>See the Unicode Standard,</br>
|
||||
* Table 3-6. <em>UTF-8 Bit Distribution</em>,</br>
|
||||
* Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
|
||||
*/
|
||||
final public class Utf8Safe extends Utf8 {
|
||||
|
||||
/**
|
||||
* Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string,
|
||||
* this method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in
|
||||
* both time and space.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired
|
||||
* surrogates)
|
||||
*/
|
||||
private static int computeEncodedLength(CharSequence sequence) {
|
||||
// Warning to maintainers: this implementation is highly optimized.
|
||||
int utf16Length = sequence.length();
|
||||
int utf8Length = utf16Length;
|
||||
int i = 0;
|
||||
|
||||
// This loop optimizes for pure ASCII.
|
||||
while (i < utf16Length && sequence.charAt(i) < 0x80) {
|
||||
i++;
|
||||
}
|
||||
|
||||
// This loop optimizes for chars less than 0x800.
|
||||
for (; i < utf16Length; i++) {
|
||||
char c = sequence.charAt(i);
|
||||
if (c < 0x800) {
|
||||
utf8Length += ((0x7f - c) >>> 31); // branch free!
|
||||
} else {
|
||||
utf8Length += encodedLengthGeneral(sequence, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (utf8Length < utf16Length) {
|
||||
// Necessary and sufficient condition for overflow because of maximum 3x expansion
|
||||
throw new IllegalArgumentException("UTF-8 length does not fit in int: "
|
||||
+ (utf8Length + (1L << 32)));
|
||||
}
|
||||
return utf8Length;
|
||||
}
|
||||
|
||||
private static int encodedLengthGeneral(CharSequence sequence, int start) {
|
||||
int utf16Length = sequence.length();
|
||||
int utf8Length = 0;
|
||||
for (int i = start; i < utf16Length; i++) {
|
||||
char c = sequence.charAt(i);
|
||||
if (c < 0x800) {
|
||||
utf8Length += (0x7f - c) >>> 31; // branch free!
|
||||
} else {
|
||||
utf8Length += 2;
|
||||
// jdk7+: if (Character.isSurrogate(c)) {
|
||||
if (Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) {
|
||||
// Check that we have a well-formed surrogate pair.
|
||||
int cp = Character.codePointAt(sequence, i);
|
||||
if (cp < MIN_SUPPLEMENTARY_CODE_POINT) {
|
||||
throw new Utf8Safe.UnpairedSurrogateException(i, utf16Length);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return utf8Length;
|
||||
}
|
||||
|
||||
private static String decodeUtf8Array(byte[] bytes, int index, int size) {
|
||||
// Bitwise OR combines the sign bits so any negative value fails the check.
|
||||
if ((index | size | bytes.length - index - size) < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
String.format("buffer length=%d, index=%d, size=%d", bytes.length, index, size));
|
||||
}
|
||||
|
||||
int offset = index;
|
||||
final int limit = offset + size;
|
||||
|
||||
// The longest possible resulting String is the same as the number of input bytes, when it is
|
||||
// all ASCII. For other cases, this over-allocates and we will truncate in the end.
|
||||
char[] resultArr = new char[size];
|
||||
int resultPos = 0;
|
||||
|
||||
// Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
|
||||
// This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
|
||||
while (offset < limit) {
|
||||
byte b = bytes[offset];
|
||||
if (!DecodeUtil.isOneByte(b)) {
|
||||
break;
|
||||
}
|
||||
offset++;
|
||||
DecodeUtil.handleOneByte(b, resultArr, resultPos++);
|
||||
}
|
||||
|
||||
while (offset < limit) {
|
||||
byte byte1 = bytes[offset++];
|
||||
if (DecodeUtil.isOneByte(byte1)) {
|
||||
DecodeUtil.handleOneByte(byte1, resultArr, resultPos++);
|
||||
// It's common for there to be multiple ASCII characters in a run mixed in, so add an
|
||||
// extra optimized loop to take care of these runs.
|
||||
while (offset < limit) {
|
||||
byte b = bytes[offset];
|
||||
if (!DecodeUtil.isOneByte(b)) {
|
||||
break;
|
||||
}
|
||||
offset++;
|
||||
DecodeUtil.handleOneByte(b, resultArr, resultPos++);
|
||||
}
|
||||
} else if (DecodeUtil.isTwoBytes(byte1)) {
|
||||
if (offset >= limit) {
|
||||
throw new IllegalArgumentException("Invalid UTF-8");
|
||||
}
|
||||
DecodeUtil.handleTwoBytes(byte1, /* byte2 */ bytes[offset++], resultArr, resultPos++);
|
||||
} else if (DecodeUtil.isThreeBytes(byte1)) {
|
||||
if (offset >= limit - 1) {
|
||||
throw new IllegalArgumentException("Invalid UTF-8");
|
||||
}
|
||||
DecodeUtil.handleThreeBytes(
|
||||
byte1,
|
||||
/* byte2 */ bytes[offset++],
|
||||
/* byte3 */ bytes[offset++],
|
||||
resultArr,
|
||||
resultPos++);
|
||||
} else {
|
||||
if (offset >= limit - 2) {
|
||||
throw new IllegalArgumentException("Invalid UTF-8");
|
||||
}
|
||||
DecodeUtil.handleFourBytes(
|
||||
byte1,
|
||||
/* byte2 */ bytes[offset++],
|
||||
/* byte3 */ bytes[offset++],
|
||||
/* byte4 */ bytes[offset++],
|
||||
resultArr,
|
||||
resultPos++);
|
||||
// 4-byte case requires two chars.
|
||||
resultPos++;
|
||||
}
|
||||
}
|
||||
|
||||
return new String(resultArr, 0, resultPos);
|
||||
}
|
||||
|
||||
private static String decodeUtf8Buffer(ByteBuffer buffer, int offset,
|
||||
int length) {
|
||||
// Bitwise OR combines the sign bits so any negative value fails the check.
|
||||
if ((offset | length | buffer.limit() - offset - length) < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
String.format("buffer limit=%d, index=%d, limit=%d", buffer.limit(),
|
||||
offset, length));
|
||||
}
|
||||
|
||||
final int limit = offset + length;
|
||||
|
||||
// The longest possible resulting String is the same as the number of input bytes, when it is
|
||||
// all ASCII. For other cases, this over-allocates and we will truncate in the end.
|
||||
char[] resultArr = new char[length];
|
||||
int resultPos = 0;
|
||||
|
||||
// Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
|
||||
// This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
|
||||
while (offset < limit) {
|
||||
byte b = buffer.get(offset);
|
||||
if (!DecodeUtil.isOneByte(b)) {
|
||||
break;
|
||||
}
|
||||
offset++;
|
||||
DecodeUtil.handleOneByte(b, resultArr, resultPos++);
|
||||
}
|
||||
|
||||
while (offset < limit) {
|
||||
byte byte1 = buffer.get(offset++);
|
||||
if (DecodeUtil.isOneByte(byte1)) {
|
||||
DecodeUtil.handleOneByte(byte1, resultArr, resultPos++);
|
||||
// It's common for there to be multiple ASCII characters in a run mixed in, so add an
|
||||
// extra optimized loop to take care of these runs.
|
||||
while (offset < limit) {
|
||||
byte b = buffer.get(offset);
|
||||
if (!DecodeUtil.isOneByte(b)) {
|
||||
break;
|
||||
}
|
||||
offset++;
|
||||
DecodeUtil.handleOneByte(b, resultArr, resultPos++);
|
||||
}
|
||||
} else if (DecodeUtil.isTwoBytes(byte1)) {
|
||||
if (offset >= limit) {
|
||||
throw new IllegalArgumentException("Invalid UTF-8");
|
||||
}
|
||||
DecodeUtil.handleTwoBytes(
|
||||
byte1, /* byte2 */ buffer.get(offset++), resultArr, resultPos++);
|
||||
} else if (DecodeUtil.isThreeBytes(byte1)) {
|
||||
if (offset >= limit - 1) {
|
||||
throw new IllegalArgumentException("Invalid UTF-8");
|
||||
}
|
||||
DecodeUtil.handleThreeBytes(
|
||||
byte1,
|
||||
/* byte2 */ buffer.get(offset++),
|
||||
/* byte3 */ buffer.get(offset++),
|
||||
resultArr,
|
||||
resultPos++);
|
||||
} else {
|
||||
if (offset >= limit - 2) {
|
||||
throw new IllegalArgumentException("Invalid UTF-8");
|
||||
}
|
||||
DecodeUtil.handleFourBytes(
|
||||
byte1,
|
||||
/* byte2 */ buffer.get(offset++),
|
||||
/* byte3 */ buffer.get(offset++),
|
||||
/* byte4 */ buffer.get(offset++),
|
||||
resultArr,
|
||||
resultPos++);
|
||||
// 4-byte case requires two chars.
|
||||
resultPos++;
|
||||
}
|
||||
}
|
||||
|
||||
return new String(resultArr, 0, resultPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int encodedLength(CharSequence in) {
|
||||
return computeEncodedLength(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the given UTF-8 portion of the {@link ByteBuffer} into a {@link String}.
|
||||
*
|
||||
* @throws IllegalArgumentException if the input is not valid UTF-8.
|
||||
*/
|
||||
@Override
|
||||
public String decodeUtf8(ByteBuffer buffer, int offset, int length)
|
||||
throws IllegalArgumentException {
|
||||
if (buffer.hasArray()) {
|
||||
return decodeUtf8Array(buffer.array(), buffer.arrayOffset() + offset, length);
|
||||
} else {
|
||||
return decodeUtf8Buffer(buffer, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void encodeUtf8Buffer(CharSequence in, ByteBuffer out) {
|
||||
final int inLength = in.length();
|
||||
int outIx = out.position();
|
||||
int inIx = 0;
|
||||
|
||||
// Since ByteBuffer.putXXX() already checks boundaries for us, no need to explicitly check
|
||||
// access. Assume the buffer is big enough and let it handle the out of bounds exception
|
||||
// if it occurs.
|
||||
try {
|
||||
// Designed to take advantage of
|
||||
// https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
|
||||
for (char c; inIx < inLength && (c = in.charAt(inIx)) < 0x80; ++inIx) {
|
||||
out.put(outIx + inIx, (byte) c);
|
||||
}
|
||||
if (inIx == inLength) {
|
||||
// Successfully encoded the entire string.
|
||||
out.position(outIx + inIx);
|
||||
return;
|
||||
}
|
||||
|
||||
outIx += inIx;
|
||||
for (char c; inIx < inLength; ++inIx, ++outIx) {
|
||||
c = in.charAt(inIx);
|
||||
if (c < 0x80) {
|
||||
// One byte (0xxx xxxx)
|
||||
out.put(outIx, (byte) c);
|
||||
} else if (c < 0x800) {
|
||||
// Two bytes (110x xxxx 10xx xxxx)
|
||||
|
||||
// Benchmarks show put performs better than putShort here (for HotSpot).
|
||||
out.put(outIx++, (byte) (0xC0 | (c >>> 6)));
|
||||
out.put(outIx, (byte) (0x80 | (0x3F & c)));
|
||||
} else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
||||
// Three bytes (1110 xxxx 10xx xxxx 10xx xxxx)
|
||||
// Maximum single-char code point is 0xFFFF, 16 bits.
|
||||
|
||||
// Benchmarks show put performs better than putShort here (for HotSpot).
|
||||
out.put(outIx++, (byte) (0xE0 | (c >>> 12)));
|
||||
out.put(outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
|
||||
out.put(outIx, (byte) (0x80 | (0x3F & c)));
|
||||
} else {
|
||||
// Four bytes (1111 xxxx 10xx xxxx 10xx xxxx 10xx xxxx)
|
||||
|
||||
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
|
||||
// bytes
|
||||
final char low;
|
||||
if (inIx + 1 == inLength || !isSurrogatePair(c, (low = in.charAt(++inIx)))) {
|
||||
throw new UnpairedSurrogateException(inIx, inLength);
|
||||
}
|
||||
// TODO(nathanmittler): Consider using putInt() to improve performance.
|
||||
int codePoint = toCodePoint(c, low);
|
||||
out.put(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
|
||||
out.put(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
|
||||
out.put(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
|
||||
out.put(outIx, (byte) (0x80 | (0x3F & codePoint)));
|
||||
}
|
||||
}
|
||||
|
||||
// Successfully encoded the entire string.
|
||||
out.position(outIx);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// TODO(nathanmittler): Consider making the API throw IndexOutOfBoundsException instead.
|
||||
|
||||
// If we failed in the outer ASCII loop, outIx will not have been updated. In this case,
|
||||
// use inIx to determine the bad write index.
|
||||
int badWriteIndex = out.position() + Math.max(inIx, outIx - out.position() + 1);
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
"Failed writing " + in.charAt(inIx) + " at index " + badWriteIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static int encodeUtf8Array(CharSequence in, byte[] out,
|
||||
int offset, int length) {
|
||||
int utf16Length = in.length();
|
||||
int j = offset;
|
||||
int i = 0;
|
||||
int limit = offset + length;
|
||||
// Designed to take advantage of
|
||||
// https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
|
||||
for (char c; i < utf16Length && i + j < limit && (c = in.charAt(i)) < 0x80; i++) {
|
||||
out[j + i] = (byte) c;
|
||||
}
|
||||
if (i == utf16Length) {
|
||||
return j + utf16Length;
|
||||
}
|
||||
j += i;
|
||||
for (char c; i < utf16Length; i++) {
|
||||
c = in.charAt(i);
|
||||
if (c < 0x80 && j < limit) {
|
||||
out[j++] = (byte) c;
|
||||
} else if (c < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes
|
||||
out[j++] = (byte) ((0xF << 6) | (c >>> 6));
|
||||
out[j++] = (byte) (0x80 | (0x3F & c));
|
||||
} else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && j <= limit - 3) {
|
||||
// Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
|
||||
out[j++] = (byte) ((0xF << 5) | (c >>> 12));
|
||||
out[j++] = (byte) (0x80 | (0x3F & (c >>> 6)));
|
||||
out[j++] = (byte) (0x80 | (0x3F & c));
|
||||
} else if (j <= limit - 4) {
|
||||
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits,
|
||||
// four UTF-8 bytes
|
||||
final char low;
|
||||
if (i + 1 == in.length()
|
||||
|| !Character.isSurrogatePair(c, (low = in.charAt(++i)))) {
|
||||
throw new UnpairedSurrogateException((i - 1), utf16Length);
|
||||
}
|
||||
int codePoint = Character.toCodePoint(c, low);
|
||||
out[j++] = (byte) ((0xF << 4) | (codePoint >>> 18));
|
||||
out[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 12)));
|
||||
out[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 6)));
|
||||
out[j++] = (byte) (0x80 | (0x3F & codePoint));
|
||||
} else {
|
||||
// If we are surrogates and we're not a surrogate pair, always throw an
|
||||
// UnpairedSurrogateException instead of an ArrayOutOfBoundsException.
|
||||
if ((Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE)
|
||||
&& (i + 1 == in.length()
|
||||
|| !Character.isSurrogatePair(c, in.charAt(i + 1)))) {
|
||||
throw new UnpairedSurrogateException(i, utf16Length);
|
||||
}
|
||||
throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j);
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given characters to the target {@link ByteBuffer} using UTF-8 encoding.
|
||||
*
|
||||
* <p>Selects an optimal algorithm based on the type of {@link ByteBuffer} (i.e. heap or direct)
|
||||
* and the capabilities of the platform.
|
||||
*
|
||||
* @param in the source string to be encoded
|
||||
* @param out the target buffer to receive the encoded string.
|
||||
*/
|
||||
@Override
|
||||
public void encodeUtf8(CharSequence in, ByteBuffer out) {
|
||||
if (out.hasArray()) {
|
||||
int start = out.arrayOffset();
|
||||
int end = encodeUtf8Array(in, out.array(), start + out.position(),
|
||||
out.remaining());
|
||||
out.position(end - start);
|
||||
} else {
|
||||
encodeUtf8Buffer(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
// These UTF-8 handling methods are copied from Guava's Utf8Unsafe class with
|
||||
// a modification to throw a local exception. This exception can be caught
|
||||
// to fallback to more lenient behavior.
|
||||
static class UnpairedSurrogateException extends IllegalArgumentException {
|
||||
UnpairedSurrogateException(int index, int length) {
|
||||
super("Unpaired surrogate at index " + index + " of " + length);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user