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:
Alex Wasserman
2017-07-13 10:33:32 -05:00
committed by Wouter van Oortmerssen
parent f20204180d
commit 625c989875
7 changed files with 308 additions and 173 deletions

View File

@@ -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());