diff options
author | Alex Wasserman <alexrwasserman@gmail.com> | 2017-07-13 10:33:32 -0500 |
---|---|---|
committer | Wouter van Oortmerssen <aardappel@gmail.com> | 2017-07-13 08:33:32 -0700 |
commit | 625c9898754a491a1c195743eafdb22908e4102c (patch) | |
tree | 7de8dd897ed8a2d5a4a43bce8ed2b4b33a73ced3 /java | |
parent | f20204180dbf58a53dcaf7929f0f996930d40bf3 (diff) | |
download | flatbuffers-625c9898754a491a1c195743eafdb22908e4102c.tar.gz flatbuffers-625c9898754a491a1c195743eafdb22908e4102c.tar.bz2 flatbuffers-625c9898754a491a1c195743eafdb22908e4102c.zip |
Java: Added ByteBufferFactory interface and sizedInputStream method. (#4379)
The ByteBufferFactory interface gives the user an option to specify
the method in which the internal ByteBuffer is allocated. This provides
flexibility in the type of ByteBuffer that can be used.
The sizedInputStream method is an alternative to sizedByteArray that
does not make a copy of the data in memory.
Diffstat (limited to 'java')
-rw-r--r-- | java/com/google/flatbuffers/FlatBufferBuilder.java | 138 | ||||
-rw-r--r-- | java/com/google/flatbuffers/Table.java | 12 |
2 files changed, 116 insertions, 34 deletions
diff --git a/java/com/google/flatbuffers/FlatBufferBuilder.java b/java/com/google/flatbuffers/FlatBufferBuilder.java index a138ed5f..3ef4282e 100644 --- a/java/com/google/flatbuffers/FlatBufferBuilder.java +++ b/java/com/google/flatbuffers/FlatBufferBuilder.java @@ -18,13 +18,13 @@ package com.google.flatbuffers; import static com.google.flatbuffers.Constants.*; -import java.nio.CharBuffer; +import java.io.IOException; +import java.io.InputStream; +import java.nio.*; import java.nio.charset.CharacterCodingException; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.util.Arrays; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.nio.charset.Charset; /// @file @@ -52,22 +52,34 @@ public class FlatBufferBuilder { boolean force_defaults = false; // False omits default values from the serialized data. CharsetEncoder encoder = utf8charset.newEncoder(); ByteBuffer dst; + ByteBufferFactory bb_factory; // Factory for allocating the internal buffer /// @endcond + /** + * Start with a buffer of size `initial_size`, then grow as required. + * + * @param initial_size The initial size of the internal buffer to use. + * @param bb_factory The factory to be used for allocating the internal buffer + */ + public FlatBufferBuilder(int initial_size, ByteBufferFactory bb_factory) { + if (initial_size <= 0) initial_size = 1; + space = initial_size; + this.bb_factory = bb_factory; + bb = bb_factory.newByteBuffer(initial_size); + } + /** * Start with a buffer of size `initial_size`, then grow as required. * * @param initial_size The initial size of the internal buffer to use. */ public FlatBufferBuilder(int initial_size) { - if (initial_size <= 0) initial_size = 1; - space = initial_size; - bb = newByteBuffer(initial_size); + this(initial_size, new HeapByteBufferFactory()); } - /** - * Start with a buffer of 1KiB, then grow as required. - */ + /** + * Start with a buffer of 1KiB, then grow as required. + */ public FlatBufferBuilder() { this(1024); } @@ -78,9 +90,22 @@ public class FlatBufferBuilder { * to call {@link #dataBuffer()} to obtain the resulting encoded message. * * @param existing_bb The byte buffer to reuse. + * @param bb_factory The factory to be used for allocating a new internal buffer if + * the existing buffer needs to grow + */ + public FlatBufferBuilder(ByteBuffer existing_bb, ByteBufferFactory bb_factory) { + init(existing_bb, bb_factory); + } + + /** + * Alternative constructor allowing reuse of {@link ByteBuffer}s. The builder + * can still grow the buffer as necessary. User classes should make sure + * to call {@link #dataBuffer()} to obtain the resulting encoded message. + * + * @param existing_bb The byte buffer to reuse. */ public FlatBufferBuilder(ByteBuffer existing_bb) { - init(existing_bb); + init(existing_bb, new HeapByteBufferFactory()); } /** @@ -89,9 +114,12 @@ public class FlatBufferBuilder { * objects that have been allocated for temporary storage. * * @param existing_bb The byte buffer to reuse. + * @param bb_factory The factory to be used for allocating a new internal buffer if + * the existing buffer needs to grow * @return Returns `this`. */ - public FlatBufferBuilder init(ByteBuffer existing_bb){ + public FlatBufferBuilder init(ByteBuffer existing_bb, ByteBufferFactory bb_factory){ + this.bb_factory = bb_factory; bb = existing_bb; bb.clear(); bb.order(ByteOrder.LITTLE_ENDIAN); @@ -107,6 +135,39 @@ public class FlatBufferBuilder { } /** + * An interface that provides a user of the FlatBufferBuilder class the ability to specify + * the method in which the internal buffer gets allocated. This allows for alternatives + * to the default behavior, which is to allocate memory for a new byte-array + * backed `ByteBuffer` array inside the JVM. + * + * The FlatBufferBuilder class contains the HeapByteBufferFactory class to + * preserve the default behavior in the event that the user does not provide + * their own implementation of this interface. + */ + public interface ByteBufferFactory { + /** + * Create a `ByteBuffer` with a given capacity. + * + * @param capacity The size of the `ByteBuffer` to allocate. + * @return Returns the new `ByteBuffer` that was allocated. + */ + ByteBuffer newByteBuffer(int capacity); + } + + /** + * An implementation of the ByteBufferFactory interface that is used when + * one is not provided by the user. + * + * Allocate memory for a new byte-array backed `ByteBuffer` array inside the JVM. + */ + public static final class HeapByteBufferFactory implements ByteBufferFactory { + @Override + public ByteBuffer newByteBuffer(int capacity) { + return ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN); + } + } + + /** * Reset the FlatBufferBuilder by purging all data that it holds. */ public void clear(){ @@ -122,34 +183,22 @@ public class FlatBufferBuilder { vector_num_elems = 0; } - /// @cond FLATBUFFERS_INTERNAL - /** - * Create a `ByteBuffer` with a given capacity. - * - * @param capacity The size of the `ByteBuffer` to allocate. - * @return Returns the new `ByteBuffer` that was allocated. - */ - static ByteBuffer newByteBuffer(int capacity) { - ByteBuffer newbb = ByteBuffer.allocate(capacity); - newbb.order(ByteOrder.LITTLE_ENDIAN); - return newbb; - } - /** * Doubles the size of the backing {@link ByteBuffer} and copies the old data towards the * end of the new buffer (since we build the buffer backwards). * * @param bb The current buffer with the existing data. + * @param bb_factory The factory to be used for allocating the new internal buffer * @return A new byte buffer with the old data copied copied to it. The data is * located at the end of the buffer. */ - static ByteBuffer growByteBuffer(ByteBuffer bb) { + static ByteBuffer growByteBuffer(ByteBuffer bb, ByteBufferFactory bb_factory) { int old_buf_size = bb.capacity(); if ((old_buf_size & 0xC0000000) != 0) // Ensure we don't grow beyond what fits in an int. throw new AssertionError("FlatBuffers: cannot grow buffer beyond 2 gigabytes."); int new_buf_size = old_buf_size << 1; bb.position(0); - ByteBuffer nbb = newByteBuffer(new_buf_size); + ByteBuffer nbb = bb_factory.newByteBuffer(new_buf_size); nbb.position(new_buf_size - old_buf_size); nbb.put(bb); return nbb; @@ -192,7 +241,7 @@ public class FlatBufferBuilder { // Reallocate the buffer if needed. while (space < align_size + size + additional_bytes) { int old_buf_size = bb.capacity(); - bb = growByteBuffer(bb); + bb = growByteBuffer(bb, bb_factory); space += bb.capacity() - old_buf_size; } pad(align_size); @@ -853,6 +902,41 @@ public class FlatBufferBuilder { public byte[] sizedByteArray() { return sizedByteArray(space, bb.capacity() - space); } + + /** + * A utility function to return an InputStream to the ByteBuffer data + * + * @return An InputStream that starts at the beginning of the ByteBuffer data + * and can read to the end of it. + */ + public InputStream sizedInputStream() { + finished(); + ByteBuffer duplicate = bb.duplicate(); + duplicate.position(space); + duplicate.limit(bb.capacity()); + return new ByteBufferBackedInputStream(duplicate); + } + + /** + * A class that allows a user to create an InputStream from a ByteBuffer. + */ + static class ByteBufferBackedInputStream extends InputStream { + + ByteBuffer buf; + + public ByteBufferBackedInputStream(ByteBuffer buf) { + this.buf = buf; + } + + public int read() throws IOException { + try { + return buf.get() & 0xFF; + } catch(BufferUnderflowException e) { + return -1; + } + } + } + } /// @} diff --git a/java/com/google/flatbuffers/Table.java b/java/com/google/flatbuffers/Table.java index b853842a..cc18d4f4 100644 --- a/java/com/google/flatbuffers/Table.java +++ b/java/com/google/flatbuffers/Table.java @@ -68,7 +68,7 @@ public class Table { } protected static int __offset(int vtable_offset, int offset, ByteBuffer bb) { - int vtable = bb.array().length - offset; + int vtable = bb.capacity() - offset; return bb.getShort(vtable + vtable_offset - bb.getInt(vtable)) + vtable; } @@ -245,10 +245,9 @@ public class Table { int startPos_1 = offset_1 + SIZEOF_INT; int startPos_2 = offset_2 + SIZEOF_INT; int len = Math.min(len_1, len_2); - byte[] bbArray = bb.array(); for(int i = 0; i < len; i++) { - if (bbArray[i + startPos_1] != bbArray[i + startPos_2]) - return bbArray[i + startPos_1] - bbArray[i + startPos_2]; + 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; } @@ -266,10 +265,9 @@ public class Table { int len_2 = key.length; int startPos_1 = offset_1 + Constants.SIZEOF_INT; int len = Math.min(len_1, len_2); - byte[] bbArray = bb.array(); for (int i = 0; i < len; i++) { - if (bbArray[i + startPos_1] != key[i]) - return bbArray[i + startPos_1] - key[i]; + if (bb.get(i + startPos_1) != key[i]) + return bb.get(i + startPos_1) - key[i]; } return len_1 - len_2; } |