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.
This commit is contained in:
parent
f20204180d
commit
625c989875
|
@ -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,26 +52,51 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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
|
||||
|
@ -80,7 +105,7 @@ public class FlatBufferBuilder {
|
|||
* @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);
|
||||
|
@ -106,6 +134,39 @@ public class FlatBufferBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -690,7 +690,7 @@ void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
|
|||
std::string GenByteBufferLength(const char *bb_name) {
|
||||
std::string bb_len = bb_name;
|
||||
if (lang_.language == IDLOptions::kCSharp) bb_len += ".Length";
|
||||
else bb_len += ".array().length";
|
||||
else bb_len += ".capacity()";
|
||||
return bb_len;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
import MyGame.Example.*;
|
||||
import NamespaceA.*;
|
||||
import NamespaceA.NamespaceB.*;
|
||||
|
@ -51,133 +53,7 @@ class JavaTest {
|
|||
// better for performance.
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(1);
|
||||
|
||||
int[] names = {fbb.createString("Frodo"), fbb.createString("Barney"), fbb.createString("Wilma")};
|
||||
int[] off = new int[3];
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addName(fbb, names[0]);
|
||||
off[0] = Monster.endMonster(fbb);
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addName(fbb, names[1]);
|
||||
off[1] = Monster.endMonster(fbb);
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addName(fbb, names[2]);
|
||||
off[2] = Monster.endMonster(fbb);
|
||||
int sortMons = fbb.createSortedVectorOfTables(new Monster(), off);
|
||||
|
||||
// We set up the same values as monsterdata.json:
|
||||
|
||||
int str = fbb.createString("MyMonster");
|
||||
|
||||
int inv = Monster.createInventoryVector(fbb, new byte[] { 0, 1, 2, 3, 4 });
|
||||
|
||||
int fred = fbb.createString("Fred");
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addName(fbb, fred);
|
||||
int mon2 = Monster.endMonster(fbb);
|
||||
|
||||
Monster.startTest4Vector(fbb, 2);
|
||||
Test.createTest(fbb, (short)10, (byte)20);
|
||||
Test.createTest(fbb, (short)30, (byte)40);
|
||||
int test4 = fbb.endVector();
|
||||
|
||||
int testArrayOfString = Monster.createTestarrayofstringVector(fbb, new int[] {
|
||||
fbb.createString("test1"),
|
||||
fbb.createString("test2")
|
||||
});
|
||||
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0,
|
||||
Color.Green, (short)5, (byte)6));
|
||||
Monster.addHp(fbb, (short)80);
|
||||
Monster.addName(fbb, str);
|
||||
Monster.addInventory(fbb, inv);
|
||||
Monster.addTestType(fbb, (byte)Any.Monster);
|
||||
Monster.addTest(fbb, mon2);
|
||||
Monster.addTest4(fbb, test4);
|
||||
Monster.addTestarrayofstring(fbb, testArrayOfString);
|
||||
Monster.addTestbool(fbb, false);
|
||||
Monster.addTesthashu32Fnv1(fbb, Integer.MAX_VALUE + 1L);
|
||||
Monster.addTestarrayoftables(fbb, sortMons);
|
||||
int mon = Monster.endMonster(fbb);
|
||||
|
||||
Monster.finishMonsterBuffer(fbb, mon);
|
||||
|
||||
// Write the result to a file for debugging purposes:
|
||||
// Note that the binaries are not necessarily identical, since the JSON
|
||||
// parser may serialize in a slightly different order than the above
|
||||
// Java code. They are functionally equivalent though.
|
||||
|
||||
try {
|
||||
DataOutputStream os = new DataOutputStream(new FileOutputStream(
|
||||
"monsterdata_java_wire.mon"));
|
||||
os.write(fbb.dataBuffer().array(), fbb.dataBuffer().position(), fbb.offset());
|
||||
os.close();
|
||||
} catch(java.io.IOException e) {
|
||||
System.out.println("FlatBuffers test: couldn't write file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test it:
|
||||
TestExtendedBuffer(fbb.dataBuffer());
|
||||
|
||||
// Make sure it also works with read only ByteBuffers. This is slower,
|
||||
// since creating strings incurs an additional copy
|
||||
// (see Table.__string).
|
||||
TestExtendedBuffer(fbb.dataBuffer().asReadOnlyBuffer());
|
||||
|
||||
TestEnums();
|
||||
|
||||
//Attempt to mutate Monster fields and check whether the buffer has been mutated properly
|
||||
// revert to original values after testing
|
||||
Monster monster = Monster.getRootAsMonster(fbb.dataBuffer());
|
||||
|
||||
// mana is optional and does not exist in the buffer so the mutation should fail
|
||||
// the mana field should retain its default value
|
||||
TestEq(monster.mutateMana((short)10), false);
|
||||
TestEq(monster.mana(), (short)150);
|
||||
|
||||
// Accessing a vector of sorted by the key tables
|
||||
TestEq(monster.testarrayoftables(0).name(), "Barney");
|
||||
TestEq(monster.testarrayoftables(1).name(), "Frodo");
|
||||
TestEq(monster.testarrayoftables(2).name(), "Wilma");
|
||||
|
||||
// Example of searching for a table by the key
|
||||
TestEq(monster.testarrayoftablesByKey("Frodo").name(), "Frodo");
|
||||
TestEq(monster.testarrayoftablesByKey("Barney").name(), "Barney");
|
||||
TestEq(monster.testarrayoftablesByKey("Wilma").name(), "Wilma");
|
||||
|
||||
// testType is an existing field and mutating it should succeed
|
||||
TestEq(monster.testType(), (byte)Any.Monster);
|
||||
TestEq(monster.mutateTestType(Any.NONE), true);
|
||||
TestEq(monster.testType(), (byte)Any.NONE);
|
||||
TestEq(monster.mutateTestType(Any.Monster), true);
|
||||
TestEq(monster.testType(), (byte)Any.Monster);
|
||||
|
||||
//mutate the inventory vector
|
||||
TestEq(monster.mutateInventory(0, 1), true);
|
||||
TestEq(monster.mutateInventory(1, 2), true);
|
||||
TestEq(monster.mutateInventory(2, 3), true);
|
||||
TestEq(monster.mutateInventory(3, 4), true);
|
||||
TestEq(monster.mutateInventory(4, 5), true);
|
||||
|
||||
for (int i = 0; i < monster.inventoryLength(); i++) {
|
||||
TestEq(monster.inventory(i), i + 1);
|
||||
}
|
||||
|
||||
//reverse mutation
|
||||
TestEq(monster.mutateInventory(0, 0), true);
|
||||
TestEq(monster.mutateInventory(1, 1), true);
|
||||
TestEq(monster.mutateInventory(2, 2), true);
|
||||
TestEq(monster.mutateInventory(3, 3), true);
|
||||
TestEq(monster.mutateInventory(4, 4), true);
|
||||
|
||||
// get a struct field and edit one of its fields
|
||||
Vec3 pos = monster.pos();
|
||||
TestEq(pos.x(), 1.0f);
|
||||
pos.mutateX(55.0f);
|
||||
TestEq(pos.x(), 55.0f);
|
||||
pos.mutateX(1.0f);
|
||||
TestEq(pos.x(), 1.0f);
|
||||
TestBuilderBasics(fbb);
|
||||
|
||||
TestExtendedBuffer(fbb.dataBuffer().asReadOnlyBuffer());
|
||||
|
||||
|
@ -189,6 +65,10 @@ class JavaTest {
|
|||
|
||||
TestCreateUninitializedVector();
|
||||
|
||||
TestByteBufferFactory();
|
||||
|
||||
TestSizedInputStream();
|
||||
|
||||
System.out.println("FlatBuffers test: completed successfully");
|
||||
}
|
||||
|
||||
|
@ -347,6 +227,179 @@ class JavaTest {
|
|||
TestEq(ByteBuffer.wrap(inventory), monsterObject.inventoryAsByteBuffer());
|
||||
}
|
||||
|
||||
static void TestByteBufferFactory() {
|
||||
final class MappedByteBufferFactory implements FlatBufferBuilder.ByteBufferFactory {
|
||||
@Override
|
||||
public ByteBuffer newByteBuffer(int capacity) {
|
||||
ByteBuffer bb;
|
||||
try {
|
||||
bb = new RandomAccessFile("javatest.bin", "rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 0, capacity).order(ByteOrder.LITTLE_ENDIAN);
|
||||
} catch(Throwable e) {
|
||||
System.out.println("FlatBuffers test: couldn't map ByteBuffer to a file");
|
||||
bb = null;
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
}
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(1, new MappedByteBufferFactory());
|
||||
|
||||
TestBuilderBasics(fbb);
|
||||
}
|
||||
|
||||
static void TestSizedInputStream() {
|
||||
// Test on default FlatBufferBuilder that uses HeapByteBuffer
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(1);
|
||||
|
||||
TestBuilderBasics(fbb);
|
||||
|
||||
InputStream in = fbb.sizedInputStream();
|
||||
byte[] array = fbb.sizedByteArray();
|
||||
int count = 0;
|
||||
int currentVal = 0;
|
||||
|
||||
while (currentVal != -1 && count < array.length) {
|
||||
try {
|
||||
currentVal = in.read();
|
||||
} catch(java.io.IOException e) {
|
||||
System.out.println("FlatBuffers test: couldn't read from InputStream");
|
||||
return;
|
||||
}
|
||||
TestEq((byte)currentVal, array[count]);
|
||||
count++;
|
||||
}
|
||||
TestEq(count, array.length);
|
||||
}
|
||||
|
||||
static void TestBuilderBasics(FlatBufferBuilder fbb) {
|
||||
int[] names = {fbb.createString("Frodo"), fbb.createString("Barney"), fbb.createString("Wilma")};
|
||||
int[] off = new int[3];
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addName(fbb, names[0]);
|
||||
off[0] = Monster.endMonster(fbb);
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addName(fbb, names[1]);
|
||||
off[1] = Monster.endMonster(fbb);
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addName(fbb, names[2]);
|
||||
off[2] = Monster.endMonster(fbb);
|
||||
int sortMons = fbb.createSortedVectorOfTables(new Monster(), off);
|
||||
|
||||
// We set up the same values as monsterdata.json:
|
||||
|
||||
int str = fbb.createString("MyMonster");
|
||||
|
||||
int inv = Monster.createInventoryVector(fbb, new byte[] { 0, 1, 2, 3, 4 });
|
||||
|
||||
int fred = fbb.createString("Fred");
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addName(fbb, fred);
|
||||
int mon2 = Monster.endMonster(fbb);
|
||||
|
||||
Monster.startTest4Vector(fbb, 2);
|
||||
Test.createTest(fbb, (short)10, (byte)20);
|
||||
Test.createTest(fbb, (short)30, (byte)40);
|
||||
int test4 = fbb.endVector();
|
||||
|
||||
int testArrayOfString = Monster.createTestarrayofstringVector(fbb, new int[] {
|
||||
fbb.createString("test1"),
|
||||
fbb.createString("test2")
|
||||
});
|
||||
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0,
|
||||
Color.Green, (short)5, (byte)6));
|
||||
Monster.addHp(fbb, (short)80);
|
||||
Monster.addName(fbb, str);
|
||||
Monster.addInventory(fbb, inv);
|
||||
Monster.addTestType(fbb, (byte)Any.Monster);
|
||||
Monster.addTest(fbb, mon2);
|
||||
Monster.addTest4(fbb, test4);
|
||||
Monster.addTestarrayofstring(fbb, testArrayOfString);
|
||||
Monster.addTestbool(fbb, false);
|
||||
Monster.addTesthashu32Fnv1(fbb, Integer.MAX_VALUE + 1L);
|
||||
Monster.addTestarrayoftables(fbb, sortMons);
|
||||
int mon = Monster.endMonster(fbb);
|
||||
|
||||
Monster.finishMonsterBuffer(fbb, mon);
|
||||
|
||||
// Write the result to a file for debugging purposes:
|
||||
// Note that the binaries are not necessarily identical, since the JSON
|
||||
// parser may serialize in a slightly different order than the above
|
||||
// Java code. They are functionally equivalent though.
|
||||
|
||||
try {
|
||||
FileChannel fc = new FileOutputStream("monsterdata_java_wire.mon").getChannel();
|
||||
fc.write(fbb.dataBuffer().duplicate());
|
||||
fc.close();
|
||||
} catch(java.io.IOException e) {
|
||||
System.out.println("FlatBuffers test: couldn't write file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test it:
|
||||
TestExtendedBuffer(fbb.dataBuffer());
|
||||
|
||||
// Make sure it also works with read only ByteBuffers. This is slower,
|
||||
// since creating strings incurs an additional copy
|
||||
// (see Table.__string).
|
||||
TestExtendedBuffer(fbb.dataBuffer().asReadOnlyBuffer());
|
||||
|
||||
TestEnums();
|
||||
|
||||
//Attempt to mutate Monster fields and check whether the buffer has been mutated properly
|
||||
// revert to original values after testing
|
||||
Monster monster = Monster.getRootAsMonster(fbb.dataBuffer());
|
||||
|
||||
// mana is optional and does not exist in the buffer so the mutation should fail
|
||||
// the mana field should retain its default value
|
||||
TestEq(monster.mutateMana((short)10), false);
|
||||
TestEq(monster.mana(), (short)150);
|
||||
|
||||
// Accessing a vector of sorted by the key tables
|
||||
TestEq(monster.testarrayoftables(0).name(), "Barney");
|
||||
TestEq(monster.testarrayoftables(1).name(), "Frodo");
|
||||
TestEq(monster.testarrayoftables(2).name(), "Wilma");
|
||||
|
||||
// Example of searching for a table by the key
|
||||
TestEq(monster.testarrayoftablesByKey("Frodo").name(), "Frodo");
|
||||
TestEq(monster.testarrayoftablesByKey("Barney").name(), "Barney");
|
||||
TestEq(monster.testarrayoftablesByKey("Wilma").name(), "Wilma");
|
||||
|
||||
// testType is an existing field and mutating it should succeed
|
||||
TestEq(monster.testType(), (byte)Any.Monster);
|
||||
TestEq(monster.mutateTestType(Any.NONE), true);
|
||||
TestEq(monster.testType(), (byte)Any.NONE);
|
||||
TestEq(monster.mutateTestType(Any.Monster), true);
|
||||
TestEq(monster.testType(), (byte)Any.Monster);
|
||||
|
||||
//mutate the inventory vector
|
||||
TestEq(monster.mutateInventory(0, 1), true);
|
||||
TestEq(monster.mutateInventory(1, 2), true);
|
||||
TestEq(monster.mutateInventory(2, 3), true);
|
||||
TestEq(monster.mutateInventory(3, 4), true);
|
||||
TestEq(monster.mutateInventory(4, 5), true);
|
||||
|
||||
for (int i = 0; i < monster.inventoryLength(); i++) {
|
||||
TestEq(monster.inventory(i), i + 1);
|
||||
}
|
||||
|
||||
//reverse mutation
|
||||
TestEq(monster.mutateInventory(0, 0), true);
|
||||
TestEq(monster.mutateInventory(1, 1), true);
|
||||
TestEq(monster.mutateInventory(2, 2), true);
|
||||
TestEq(monster.mutateInventory(3, 3), true);
|
||||
TestEq(monster.mutateInventory(4, 4), true);
|
||||
|
||||
// get a struct field and edit one of its fields
|
||||
Vec3 pos = monster.pos();
|
||||
TestEq(pos.x(), 1.0f);
|
||||
pos.mutateX(55.0f);
|
||||
TestEq(pos.x(), 55.0f);
|
||||
pos.mutateX(1.0f);
|
||||
TestEq(pos.x(), 1.0f);
|
||||
}
|
||||
|
||||
static <T> void TestEq(T a, T b) {
|
||||
if (!a.equals(b)) {
|
||||
System.out.println("" + a.getClass().getName() + " " + b.getClass().getName());
|
||||
|
|
|
@ -160,7 +160,7 @@ public final class Monster extends Table {
|
|||
while (span != 0) {
|
||||
int middle = span / 2;
|
||||
int tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb);
|
||||
int comp = compareStrings(__offset(10, bb.array().length - tableOffset, bb), byteKey, bb);
|
||||
int comp = compareStrings(__offset(10, bb.capacity() - tableOffset, bb), byteKey, bb);
|
||||
if (comp > 0) {
|
||||
span = middle;
|
||||
} else if (comp < 0) {
|
||||
|
|
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
import * as NS9459827973991502386 from "./namespace_test1_generated";
|
||||
import * as NS3029350602 from "./namespace_test1_generated";
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
|
@ -39,24 +39,24 @@ static getRootAsTableInFirstNS(bb:flatbuffers.ByteBuffer, obj?:TableInFirstNS):T
|
|||
* @param {NamespaceA.NamespaceB.TableInNestedNS=} obj
|
||||
* @returns {NamespaceA.NamespaceB.TableInNestedNS|null}
|
||||
*/
|
||||
fooTable(obj?:NS9459827973991502386.NamespaceA.NamespaceB.TableInNestedNS):NS9459827973991502386.NamespaceA.NamespaceB.TableInNestedNS|null {
|
||||
fooTable(obj?:NS3029350602.NamespaceA.NamespaceB.TableInNestedNS):NS3029350602.NamespaceA.NamespaceB.TableInNestedNS|null {
|
||||
var offset = this.bb.__offset(this.bb_pos, 4);
|
||||
return offset ? (obj || new NS9459827973991502386.NamespaceA.NamespaceB.TableInNestedNS).__init(this.bb.__indirect(this.bb_pos + offset), this.bb) : null;
|
||||
return offset ? (obj || new NS3029350602.NamespaceA.NamespaceB.TableInNestedNS).__init(this.bb.__indirect(this.bb_pos + offset), this.bb) : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {NamespaceA.NamespaceB.EnumInNestedNS}
|
||||
*/
|
||||
fooEnum():NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS {
|
||||
fooEnum():NS3029350602.NamespaceA.NamespaceB.EnumInNestedNS {
|
||||
var offset = this.bb.__offset(this.bb_pos, 6);
|
||||
return offset ? /** @type {NamespaceA.NamespaceB.EnumInNestedNS} */ (this.bb.readInt8(this.bb_pos + offset)) : NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS.A;
|
||||
return offset ? /** @type {NamespaceA.NamespaceB.EnumInNestedNS} */ (this.bb.readInt8(this.bb_pos + offset)) : NS3029350602.NamespaceA.NamespaceB.EnumInNestedNS.A;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {NamespaceA.NamespaceB.EnumInNestedNS} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
mutate_foo_enum(value:NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS):boolean {
|
||||
mutate_foo_enum(value:NS3029350602.NamespaceA.NamespaceB.EnumInNestedNS):boolean {
|
||||
var offset = this.bb.__offset(this.bb_pos, 6);
|
||||
|
||||
if (offset === 0) {
|
||||
|
@ -71,9 +71,9 @@ mutate_foo_enum(value:NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS
|
|||
* @param {NamespaceA.NamespaceB.StructInNestedNS=} obj
|
||||
* @returns {NamespaceA.NamespaceB.StructInNestedNS|null}
|
||||
*/
|
||||
fooStruct(obj?:NS9459827973991502386.NamespaceA.NamespaceB.StructInNestedNS):NS9459827973991502386.NamespaceA.NamespaceB.StructInNestedNS|null {
|
||||
fooStruct(obj?:NS3029350602.NamespaceA.NamespaceB.StructInNestedNS):NS3029350602.NamespaceA.NamespaceB.StructInNestedNS|null {
|
||||
var offset = this.bb.__offset(this.bb_pos, 8);
|
||||
return offset ? (obj || new NS9459827973991502386.NamespaceA.NamespaceB.StructInNestedNS).__init(this.bb_pos + offset, this.bb) : null;
|
||||
return offset ? (obj || new NS3029350602.NamespaceA.NamespaceB.StructInNestedNS).__init(this.bb_pos + offset, this.bb) : null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -95,8 +95,8 @@ static addFooTable(builder:flatbuffers.Builder, fooTableOffset:flatbuffers.Offse
|
|||
* @param {flatbuffers.Builder} builder
|
||||
* @param {NamespaceA.NamespaceB.EnumInNestedNS} fooEnum
|
||||
*/
|
||||
static addFooEnum(builder:flatbuffers.Builder, fooEnum:NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS) {
|
||||
builder.addFieldInt8(1, fooEnum, NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS.A);
|
||||
static addFooEnum(builder:flatbuffers.Builder, fooEnum:NS3029350602.NamespaceA.NamespaceB.EnumInNestedNS) {
|
||||
builder.addFieldInt8(1, fooEnum, NS3029350602.NamespaceA.NamespaceB.EnumInNestedNS.A);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue