Fix for #3853
Removes the following allocations: - ``CharsetDecoder`` is reused between calls - ``CharBuffer#wrap`` removed in favor of heap based char buffer that is reused - Temporary ``char[]``, an intermediate copy inside ``StringCoding`` - Another ``char[]``, this is needed because ``StringCoding`` uses a ``CharBuffer`` internally but returns a ``char[]``. Extra characters need to be trimmed so this means yet another allocation - Yet another ``char[]`` directly from ``__string`` for non-heap based buffers Removes the following copies - No copy is performed to trim the allocation since a ``CharBuffer`` is used directly - For non-heap based byte buffers, removes the copy that was previously done in the __string function This does need to get the TLS entry which implies at least some contention on the thread object table and a fence.
This commit is contained in:
parent
6f751d5d26
commit
286587d151
|
@ -19,6 +19,11 @@ package com.google.flatbuffers;
|
|||
import static com.google.flatbuffers.Constants.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
|
||||
/// @cond FLATBUFFERS_INTERNAL
|
||||
|
||||
|
@ -26,6 +31,13 @@ import java.nio.ByteOrder;
|
|||
* All tables in the generated code derive from this class, and add their own accessors.
|
||||
*/
|
||||
public class Table {
|
||||
private final static ThreadLocal<CharsetDecoder> UTF8_DECODER = new ThreadLocal<CharsetDecoder>() {
|
||||
@Override
|
||||
protected CharsetDecoder initialValue() {
|
||||
return Charset.forName("UTF-8").newDecoder();
|
||||
}
|
||||
};
|
||||
private final static ThreadLocal<CharBuffer> CHAR_BUFFER = new ThreadLocal<CharBuffer>();
|
||||
/** Used to hold the position of the `bb` buffer. */
|
||||
protected int bb_pos;
|
||||
/** The underlying ByteBuffer to hold the data of the Table. */
|
||||
|
@ -71,20 +83,34 @@ public class Table {
|
|||
* @return Returns a `String` from the data stored inside the FlatBuffer at `offset`.
|
||||
*/
|
||||
protected String __string(int offset) {
|
||||
CharsetDecoder decoder = UTF8_DECODER.get();
|
||||
decoder.reset();
|
||||
|
||||
offset += bb.getInt(offset);
|
||||
if (bb.hasArray()) {
|
||||
return new String(bb.array(), bb.arrayOffset() + offset + SIZEOF_INT, bb.getInt(offset),
|
||||
FlatBufferBuilder.utf8charset);
|
||||
} else {
|
||||
// We can't access .array(), since the ByteBuffer is read-only,
|
||||
// off-heap or a memory map
|
||||
ByteBuffer bb = this.bb.duplicate().order(ByteOrder.LITTLE_ENDIAN);
|
||||
// We're forced to make an extra copy:
|
||||
byte[] copy = new byte[bb.getInt(offset)];
|
||||
bb.position(offset + SIZEOF_INT);
|
||||
bb.get(copy);
|
||||
return new String(copy, 0, copy.length, FlatBufferBuilder.utf8charset);
|
||||
ByteBuffer src = bb.duplicate().order(ByteOrder.LITTLE_ENDIAN);
|
||||
int length = src.getInt(offset);
|
||||
src.position(offset + SIZEOF_INT);
|
||||
src.limit(offset + SIZEOF_INT + length);
|
||||
|
||||
int required = (int)((float)length * decoder.maxCharsPerByte());
|
||||
CharBuffer dst = CHAR_BUFFER.get();
|
||||
if (dst == null || dst.capacity() < required) {
|
||||
dst = CharBuffer.allocate(Math.max(required, 128));
|
||||
CHAR_BUFFER.set(dst);
|
||||
}
|
||||
|
||||
dst.clear();
|
||||
|
||||
try {
|
||||
CoderResult cr = decoder.decode(src, dst, true);
|
||||
if (!cr.isUnderflow()) {
|
||||
cr.throwException();
|
||||
}
|
||||
} catch (CharacterCodingException x) {
|
||||
throw new Error(x);
|
||||
}
|
||||
|
||||
return dst.flip().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue