mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-11 23:40:57 +00:00
Initial commit of the FlatBuffers code.
Change-Id: I4c9f0f722490b374257adb3fec63e44ae93da920 Tested: using VS2010 / Xcode / gcc on Linux.
This commit is contained in:
21
tests/JavaTest.bat
Executable file
21
tests/JavaTest.bat
Executable file
@@ -0,0 +1,21 @@
|
||||
@echo off
|
||||
rem Copyright 2014 Google Inc. All rights reserved.
|
||||
rem
|
||||
rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
rem you may not use this file except in compliance with the License.
|
||||
rem You may obtain a copy of the License at
|
||||
rem
|
||||
rem http://www.apache.org/licenses/LICENSE-2.0
|
||||
rem
|
||||
rem Unless required by applicable law or agreed to in writing, software
|
||||
rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
rem See the License for the specific language governing permissions and
|
||||
rem limitations under the License.
|
||||
|
||||
rem Compile then run the Java test.
|
||||
|
||||
set batch_file_dir=%~d0%~p0
|
||||
|
||||
javac -classpath %batch_file_dir%\..\java;%batch_file_dir% JavaTest.java
|
||||
java -classpath %batch_file_dir%\..\java;%batch_file_dir% JavaTest
|
||||
145
tests/JavaTest.java
Executable file
145
tests/JavaTest.java
Executable file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import MyGame.Example.*;
|
||||
import flatbuffers.FlatBufferBuilder;
|
||||
|
||||
class JavaTest {
|
||||
public static void main(String[] args) {
|
||||
|
||||
// First, let's test reading a FlatBuffer generated by C++ code:
|
||||
// This file was generated from monsterdata_test.json
|
||||
|
||||
byte[] data = null;
|
||||
File file = new File("monsterdata_test_wire.bin");
|
||||
RandomAccessFile f = null;
|
||||
try {
|
||||
f = new RandomAccessFile(file, "r");
|
||||
data = new byte[(int)f.length()];
|
||||
f.readFully(data);
|
||||
f.close();
|
||||
} catch(java.io.IOException e) {
|
||||
System.out.println("FlatBuffers test: couldn't read file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Now test it:
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(data);
|
||||
TestBuffer(bb, 0);
|
||||
|
||||
// Second, let's create a FlatBuffer from scratch in Java, and test it also.
|
||||
// We set up the same values as monsterdata.json:
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(1024);
|
||||
|
||||
int str = fbb.createString("MyMonster");
|
||||
|
||||
Monster.startInventoryVector(fbb, 5);
|
||||
for (byte i = 4; i >=0; i--) fbb.addByte(i);
|
||||
int inv = fbb.endVector();
|
||||
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addHp(fbb, (short)20);
|
||||
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();
|
||||
|
||||
Monster.startMonster(fbb);
|
||||
Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0,
|
||||
(byte)4, (short)5, (byte)6));
|
||||
Monster.addHp(fbb, (short)80);
|
||||
Monster.addName(fbb, str);
|
||||
Monster.addInventory(fbb, inv);
|
||||
Monster.addTestType(fbb, (byte)1);
|
||||
Monster.addTest(fbb, mon2);
|
||||
Monster.addTest4(fbb, test4);
|
||||
int mon = Monster.endMonster(fbb);
|
||||
|
||||
fbb.finish(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.bin"));
|
||||
os.write(fbb.dataBuffer().array(), fbb.dataStart(), fbb.offset());
|
||||
os.close();
|
||||
} catch(java.io.IOException e) {
|
||||
System.out.println("FlatBuffers test: couldn't write file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test it:
|
||||
|
||||
TestBuffer(fbb.dataBuffer(), fbb.dataStart());
|
||||
|
||||
System.out.println("FlatBuffers test: completed successfully");
|
||||
}
|
||||
|
||||
static void TestBuffer(ByteBuffer bb, int start) {
|
||||
Monster monster = Monster.getRootAsMonster(bb, start);
|
||||
|
||||
TestEq(monster.hp(), (short)80);
|
||||
TestEq(monster.mana(), (short)150); // default
|
||||
|
||||
TestEq(monster.name(), "MyMonster");
|
||||
// monster.friendly() // can't access, deprecated
|
||||
|
||||
Vec3 pos = monster.pos();
|
||||
TestEq(pos.x(), 1.0f);
|
||||
TestEq(pos.y(), 2.0f);
|
||||
TestEq(pos.z(), 3.0f);
|
||||
TestEq(pos.test1(), 3.0);
|
||||
TestEq(pos.test2(), (byte)4);
|
||||
Test t = pos.test3();
|
||||
TestEq(t.a(), (short)5);
|
||||
TestEq(t.b(), (byte)6);
|
||||
|
||||
TestEq(monster.testType(), (byte)Any.Monster);
|
||||
Monster monster2 = new Monster();
|
||||
TestEq(monster.test(monster2) != null, true);
|
||||
TestEq(monster2.hp(), (short)20);
|
||||
|
||||
TestEq(monster.inventoryLength(), 5);
|
||||
int invsum = 0;
|
||||
for (int i = 0; i < monster.inventoryLength(); i++)
|
||||
invsum += monster.inventory(i);
|
||||
TestEq(invsum, 10);
|
||||
|
||||
Test test_0 = monster.test4(0);
|
||||
Test test_1 = monster.test4(1);
|
||||
TestEq(monster.test4Length(), 2);
|
||||
TestEq(test_0.a() + test_0.b() + test_1.a() + test_1.b(), 100);
|
||||
}
|
||||
|
||||
static <T> void TestEq(T a, T b) {
|
||||
if (!a.equals(b)) {
|
||||
System.out.println("" + a.getClass().getName() + " " + b.getClass().getName());
|
||||
System.out.println("FlatBuffers test FAILED: \'" + a + "\' != \'" + b + "\'");
|
||||
assert false;
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
tests/MyGame/Example/Any.java
Executable file
14
tests/MyGame/Example/Any.java
Executable file
@@ -0,0 +1,14 @@
|
||||
// automatically generated, do not modify
|
||||
|
||||
package MyGame.Example;
|
||||
|
||||
import java.nio.*;
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
import flatbuffers.*;
|
||||
|
||||
public class Any {
|
||||
public static final byte NONE = 0;
|
||||
public static final byte Monster = 1;
|
||||
};
|
||||
|
||||
15
tests/MyGame/Example/Color.java
Executable file
15
tests/MyGame/Example/Color.java
Executable file
@@ -0,0 +1,15 @@
|
||||
// automatically generated, do not modify
|
||||
|
||||
package MyGame.Example;
|
||||
|
||||
import java.nio.*;
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
import flatbuffers.*;
|
||||
|
||||
public class Color {
|
||||
public static final byte Red = 0;
|
||||
public static final byte Green = 1;
|
||||
public static final byte Blue = 2;
|
||||
};
|
||||
|
||||
42
tests/MyGame/Example/Monster.java
Executable file
42
tests/MyGame/Example/Monster.java
Executable file
@@ -0,0 +1,42 @@
|
||||
// automatically generated, do not modify
|
||||
|
||||
package MyGame.Example;
|
||||
|
||||
import java.nio.*;
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
import flatbuffers.*;
|
||||
|
||||
public class Monster extends Table {
|
||||
public static Monster getRootAsMonster(ByteBuffer _bb, int offset) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (new Monster()).__init(_bb.getInt(offset) + offset, _bb); }
|
||||
public Monster __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
public Vec3 pos() { return pos(new Vec3()); }
|
||||
public Vec3 pos(Vec3 obj) { int o = __offset(4); return o != 0 ? obj.__init(o + bb_pos, bb) : null; }
|
||||
public short mana() { int o = __offset(6); return o != 0 ? bb.getShort(o + bb_pos) : 150; }
|
||||
public short hp() { int o = __offset(8); return o != 0 ? bb.getShort(o + bb_pos) : 100; }
|
||||
public String name() { int o = __offset(10); return o != 0 ? __string(o) : null; }
|
||||
public byte inventory(int j) { int o = __offset(14); return o != 0 ? bb.get(__vector(o) + j * 1) : 0; }
|
||||
public int inventoryLength() { int o = __offset(14); return o != 0 ? __vector_len(o) : 0; }
|
||||
/// an example documentation comment: this will end up in the generated code multiline too
|
||||
public byte color() { int o = __offset(16); return o != 0 ? bb.get(o + bb_pos) : 2; }
|
||||
public byte testType() { int o = __offset(18); return o != 0 ? bb.get(o + bb_pos) : 0; }
|
||||
public Table test(Table obj) { int o = __offset(20); return o != 0 ? __union(obj, o) : null; }
|
||||
public Test test4(int j) { return test4(new Test(), j); }
|
||||
public Test test4(Test obj, int j) { int o = __offset(22); return o != 0 ? obj.__init(__vector(o) + j * 4, bb) : null; }
|
||||
public int test4Length() { int o = __offset(22); return o != 0 ? __vector_len(o) : 0; }
|
||||
|
||||
public static void startMonster(FlatBufferBuilder builder) { builder.startObject(10); }
|
||||
public static void addPos(FlatBufferBuilder builder, int pos) { builder.addStruct(0, pos, 0); }
|
||||
public static void addMana(FlatBufferBuilder builder, short mana) { builder.addShort(1, mana, 150); }
|
||||
public static void addHp(FlatBufferBuilder builder, short hp) { builder.addShort(2, hp, 100); }
|
||||
public static void addName(FlatBufferBuilder builder, int name) { builder.addOffset(3, name, 0); }
|
||||
public static void addInventory(FlatBufferBuilder builder, int inventory) { builder.addOffset(5, inventory, 0); }
|
||||
public static void startInventoryVector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems); }
|
||||
public static void addColor(FlatBufferBuilder builder, byte color) { builder.addByte(6, color, 2); }
|
||||
public static void addTestType(FlatBufferBuilder builder, byte testType) { builder.addByte(7, testType, 0); }
|
||||
public static void addTest(FlatBufferBuilder builder, int test) { builder.addOffset(8, test, 0); }
|
||||
public static void addTest4(FlatBufferBuilder builder, int test4) { builder.addOffset(9, test4, 0); }
|
||||
public static void startTest4Vector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems); }
|
||||
public static int endMonster(FlatBufferBuilder builder) { return builder.endObject(); }
|
||||
};
|
||||
|
||||
23
tests/MyGame/Example/Test.java
Executable file
23
tests/MyGame/Example/Test.java
Executable file
@@ -0,0 +1,23 @@
|
||||
// automatically generated, do not modify
|
||||
|
||||
package MyGame.Example;
|
||||
|
||||
import java.nio.*;
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
import flatbuffers.*;
|
||||
|
||||
public class Test extends Struct {
|
||||
public Test __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
public short a() { return bb.getShort(bb_pos + 0); }
|
||||
public byte b() { return bb.get(bb_pos + 2); }
|
||||
|
||||
public static int createTest(FlatBufferBuilder builder, short a, byte b) {
|
||||
builder.prep(2, 0);
|
||||
builder.pad(1);
|
||||
builder.putByte(b);
|
||||
builder.putShort(a);
|
||||
return builder.offset();
|
||||
}
|
||||
};
|
||||
|
||||
37
tests/MyGame/Example/Vec3.java
Executable file
37
tests/MyGame/Example/Vec3.java
Executable file
@@ -0,0 +1,37 @@
|
||||
// automatically generated, do not modify
|
||||
|
||||
package MyGame.Example;
|
||||
|
||||
import java.nio.*;
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
import flatbuffers.*;
|
||||
|
||||
public class Vec3 extends Struct {
|
||||
public Vec3 __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
public float x() { return bb.getFloat(bb_pos + 0); }
|
||||
public float y() { return bb.getFloat(bb_pos + 4); }
|
||||
public float z() { return bb.getFloat(bb_pos + 8); }
|
||||
public double test1() { return bb.getDouble(bb_pos + 16); }
|
||||
public byte test2() { return bb.get(bb_pos + 24); }
|
||||
public Test test3() { return test3(new Test()); }
|
||||
public Test test3(Test obj) { return obj.__init(bb_pos + 26, bb); }
|
||||
|
||||
public static int createVec3(FlatBufferBuilder builder, float x, float y, float z, double test1, byte test2, short Test_a, byte Test_b) {
|
||||
builder.prep(16, 0);
|
||||
builder.pad(2);
|
||||
builder.prep(2, 0);
|
||||
builder.pad(1);
|
||||
builder.putByte(Test_b);
|
||||
builder.putShort(Test_a);
|
||||
builder.pad(1);
|
||||
builder.putByte(test2);
|
||||
builder.putDouble(test1);
|
||||
builder.pad(4);
|
||||
builder.putFloat(z);
|
||||
builder.putFloat(y);
|
||||
builder.putFloat(x);
|
||||
return builder.offset();
|
||||
}
|
||||
};
|
||||
|
||||
34
tests/monster_test.fbs
Executable file
34
tests/monster_test.fbs
Executable file
@@ -0,0 +1,34 @@
|
||||
// example IDL file
|
||||
|
||||
namespace MyGame.Example;
|
||||
|
||||
enum Color:byte { Red = 0, Green, Blue = 2 }
|
||||
|
||||
union Any { Monster } // TODO: add more elements
|
||||
|
||||
struct Test { a:short; b:byte; }
|
||||
|
||||
struct Vec3 (force_align: 16) {
|
||||
x:float;
|
||||
y:float;
|
||||
z:float;
|
||||
test1:double;
|
||||
test2:byte;
|
||||
test3:Test;
|
||||
}
|
||||
|
||||
table Monster {
|
||||
pos:Vec3;
|
||||
mana:short = 150;
|
||||
hp:short = 100;
|
||||
name:string;
|
||||
friendly:bool = false (deprecated, priority: 1);
|
||||
inventory:[ubyte];
|
||||
/// an example documentation comment: this will end up in the generated code
|
||||
/// multiline too
|
||||
color:Color = Blue;
|
||||
test:Any;
|
||||
test4:[Test];
|
||||
}
|
||||
|
||||
root_type Monster;
|
||||
123
tests/monster_test_generated.h
Executable file
123
tests/monster_test_generated.h
Executable file
@@ -0,0 +1,123 @@
|
||||
// automatically generated, do not modify
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
namespace MyGame {
|
||||
namespace Example {
|
||||
|
||||
enum {
|
||||
Color_Red = 0,
|
||||
Color_Green = 1,
|
||||
Color_Blue = 2,
|
||||
};
|
||||
|
||||
inline const char **EnumNamesColor() {
|
||||
static const char *names[] = { "Red", "Green", "Blue", nullptr };
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameColor(int e) { return EnumNamesColor()[e]; }
|
||||
|
||||
enum {
|
||||
Any_NONE = 0,
|
||||
Any_Monster = 1,
|
||||
};
|
||||
|
||||
inline const char **EnumNamesAny() {
|
||||
static const char *names[] = { "NONE", "Monster", nullptr };
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; }
|
||||
|
||||
struct Test;
|
||||
struct Vec3;
|
||||
struct Monster;
|
||||
|
||||
MANUALLY_ALIGNED_STRUCT(2) Test {
|
||||
private:
|
||||
int16_t a_;
|
||||
int8_t b_;
|
||||
int8_t __padding0;
|
||||
|
||||
public:
|
||||
Test(int16_t a, int8_t b)
|
||||
: a_(flatbuffers::EndianScalar(a)), b_(flatbuffers::EndianScalar(b)), __padding0(0) {}
|
||||
|
||||
int16_t a() const { return flatbuffers::EndianScalar(a_); }
|
||||
int8_t b() const { return flatbuffers::EndianScalar(b_); }
|
||||
};
|
||||
STRUCT_END(Test, 4);
|
||||
|
||||
MANUALLY_ALIGNED_STRUCT(16) Vec3 {
|
||||
private:
|
||||
float x_;
|
||||
float y_;
|
||||
float z_;
|
||||
int32_t __padding0;
|
||||
double test1_;
|
||||
int8_t test2_;
|
||||
int8_t __padding1;
|
||||
Test test3_;
|
||||
int16_t __padding2;
|
||||
|
||||
public:
|
||||
Vec3(float x, float y, float z, double test1, int8_t test2, const Test &test3)
|
||||
: x_(flatbuffers::EndianScalar(x)), y_(flatbuffers::EndianScalar(y)), z_(flatbuffers::EndianScalar(z)), __padding0(0), test1_(flatbuffers::EndianScalar(test1)), test2_(flatbuffers::EndianScalar(test2)), __padding1(0), test3_(test3), __padding2(0) {}
|
||||
|
||||
float x() const { return flatbuffers::EndianScalar(x_); }
|
||||
float y() const { return flatbuffers::EndianScalar(y_); }
|
||||
float z() const { return flatbuffers::EndianScalar(z_); }
|
||||
double test1() const { return flatbuffers::EndianScalar(test1_); }
|
||||
int8_t test2() const { return flatbuffers::EndianScalar(test2_); }
|
||||
const Test &test3() const { return test3_; }
|
||||
};
|
||||
STRUCT_END(Vec3, 32);
|
||||
|
||||
struct Monster : private flatbuffers::Table {
|
||||
const Vec3 *pos() const { return GetStruct<const Vec3 *>(4); }
|
||||
int16_t mana() const { return GetField<int16_t>(6, 150); }
|
||||
int16_t hp() const { return GetField<int16_t>(8, 100); }
|
||||
const flatbuffers::String *name() const { return GetPointer<const flatbuffers::String *>(10); }
|
||||
const flatbuffers::Vector<uint8_t> *inventory() const { return GetPointer<const flatbuffers::Vector<uint8_t> *>(14); }
|
||||
/// an example documentation comment: this will end up in the generated code multiline too
|
||||
int8_t color() const { return GetField<int8_t>(16, 2); }
|
||||
uint8_t test_type() const { return GetField<uint8_t>(18, 0); }
|
||||
const void *test() const { return GetPointer<const void *>(20); }
|
||||
const flatbuffers::Vector<const Test *> *test4() const { return GetPointer<const flatbuffers::Vector<const Test *> *>(22); }
|
||||
};
|
||||
|
||||
struct MonsterBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_pos(const Vec3 *pos) { fbb_.AddStruct(4, pos); }
|
||||
void add_mana(int16_t mana) { fbb_.AddElement<int16_t>(6, mana, 150); }
|
||||
void add_hp(int16_t hp) { fbb_.AddElement<int16_t>(8, hp, 100); }
|
||||
void add_name(flatbuffers::Offset<flatbuffers::String> name) { fbb_.AddOffset(10, name); }
|
||||
void add_inventory(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory) { fbb_.AddOffset(14, inventory); }
|
||||
void add_color(int8_t color) { fbb_.AddElement<int8_t>(16, color, 2); }
|
||||
void add_test_type(uint8_t test_type) { fbb_.AddElement<uint8_t>(18, test_type, 0); }
|
||||
void add_test(flatbuffers::Offset<void> test) { fbb_.AddOffset(20, test); }
|
||||
void add_test4(flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4) { fbb_.AddOffset(22, test4); }
|
||||
MonsterBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
flatbuffers::Offset<Monster> Finish() { return flatbuffers::Offset<Monster>(fbb_.EndTable(start_, 10)); }
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const Vec3 *pos, int16_t mana, int16_t hp, flatbuffers::Offset<flatbuffers::String> name, flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory, int8_t color, uint8_t test_type, flatbuffers::Offset<void> test, flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4) {
|
||||
MonsterBuilder builder_(_fbb);
|
||||
builder_.add_test4(test4);
|
||||
builder_.add_test(test);
|
||||
builder_.add_inventory(inventory);
|
||||
builder_.add_name(name);
|
||||
builder_.add_pos(pos);
|
||||
builder_.add_hp(hp);
|
||||
builder_.add_mana(mana);
|
||||
builder_.add_test_type(test_type);
|
||||
builder_.add_color(color);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline const Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<Monster>(buf); }
|
||||
|
||||
}; // namespace MyGame
|
||||
}; // namespace Example
|
||||
36
tests/monsterdata_test.json
Executable file
36
tests/monsterdata_test.json
Executable file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
pos: {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
test1: 3,
|
||||
test2: 4,
|
||||
test3: {
|
||||
a: 5,
|
||||
b: 6
|
||||
}
|
||||
},
|
||||
hp: 80,
|
||||
name: "MyMonster",
|
||||
inventory: [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
],
|
||||
test_type: 1,
|
||||
test: {
|
||||
hp: 20
|
||||
},
|
||||
test4: [
|
||||
{
|
||||
a: 10,
|
||||
b: 20
|
||||
},
|
||||
{
|
||||
a: 30,
|
||||
b: 40
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
tests/monsterdata_test_wire.bin
Executable file
BIN
tests/monsterdata_test_wire.bin
Executable file
Binary file not shown.
471
tests/test.cpp
Normal file
471
tests/test.cpp
Normal file
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
#include "flatbuffers/idl.h"
|
||||
#include "flatbuffers/util.h"
|
||||
|
||||
#include "monster_test_generated.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
using namespace MyGame::Example;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#define TEST_OUTPUT_LINE(...) \
|
||||
__android_log_print(ANDROID_LOG_INFO, "FlatBuffers", __VA_ARGS__)
|
||||
#else
|
||||
#define TEST_OUTPUT_LINE(...) \
|
||||
{ printf(__VA_ARGS__); printf("\n"); }
|
||||
#endif
|
||||
|
||||
bool testing_fails = 0;
|
||||
|
||||
template<typename T, typename U>
|
||||
void TestEq(T expval, U val, const char *exp, const char *file, int line) {
|
||||
if (expval != val) {
|
||||
auto expval_str = flatbuffers::NumToString(expval);
|
||||
auto val_str = flatbuffers::NumToString(val);
|
||||
TEST_OUTPUT_LINE("TEST FAILED: %s:%d, %s (%s) != %s", file, line,
|
||||
exp, expval_str.c_str(), val_str.c_str());
|
||||
assert(0);
|
||||
testing_fails++;
|
||||
}
|
||||
}
|
||||
|
||||
#define TEST_EQ(exp, val) TestEq( exp, val, #exp, __FILE__, __LINE__)
|
||||
#define TEST_NOTNULL(exp) TestEq(!exp, false, #exp, __FILE__, __LINE__)
|
||||
|
||||
// Include simple random number generator to ensure results will be the
|
||||
// same cross platform.
|
||||
// http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator
|
||||
uint32_t lcg_seed = 48271;
|
||||
uint32_t lcg_rand() {
|
||||
return lcg_seed = ((uint64_t)lcg_seed * 279470273UL) % 4294967291UL;
|
||||
}
|
||||
void lcg_reset() { lcg_seed = 48271; }
|
||||
|
||||
// example of how to build up a serialized buffer algorithmically:
|
||||
std::string CreateFlatBufferTest() {
|
||||
flatbuffers::FlatBufferBuilder builder;
|
||||
|
||||
auto vec = Vec3(1, 2, 3, 0, 0, Test(10, 20));
|
||||
|
||||
auto name = builder.CreateString("MyMonster");
|
||||
|
||||
unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
auto inventory = builder.CreateVector(inv_data, 10);
|
||||
|
||||
Test tests[] = { Test(10, 20), Test(30, 40) };
|
||||
auto testv = builder.CreateVectorOfStructs(tests, 2);
|
||||
|
||||
// create monster with very few fields set:
|
||||
// (same functionality as CreateMonster below, but sets fields manually)
|
||||
MonsterBuilder mb(builder);
|
||||
mb.add_hp(20);
|
||||
auto mloc2 = mb.Finish();
|
||||
|
||||
// shortcut for creating monster with all fields set:
|
||||
auto mloc = CreateMonster(builder, &vec, 150, 80, name, inventory, Color_Blue,
|
||||
Any_Monster, mloc2.Union(), // Store a union.
|
||||
testv);
|
||||
|
||||
builder.Finish(mloc);
|
||||
|
||||
#ifdef FLATBUFFERS_TEST_VERBOSE
|
||||
// print byte data for debugging:
|
||||
auto p = builder.GetBufferPointer();
|
||||
for (flatbuffers::uoffset_t i = 0; i < builder.GetSize(); i++)
|
||||
printf("%d ", p[i]);
|
||||
#endif
|
||||
|
||||
// return the buffer for the caller to use.
|
||||
return std::string(reinterpret_cast<const char *>(builder.GetBufferPointer()),
|
||||
builder.GetSize());
|
||||
}
|
||||
|
||||
// example of accessing a buffer loaded in memory:
|
||||
void AccessFlatBufferTest(const std::string &flatbuf) {
|
||||
|
||||
auto monster = GetMonster(flatbuf.c_str());
|
||||
|
||||
TEST_EQ(monster->hp(), 80);
|
||||
TEST_EQ(monster->mana(), 150); // default
|
||||
TEST_EQ(strcmp(monster->name()->c_str(), "MyMonster"), 0);
|
||||
// Can't access the following field, it is deprecated in the schema,
|
||||
// which means accessors are not generated:
|
||||
// monster.friendly()
|
||||
|
||||
auto pos = monster->pos();
|
||||
TEST_NOTNULL(pos);
|
||||
TEST_EQ(pos->z(), 3);
|
||||
TEST_EQ(pos->test3().a(), 10);
|
||||
TEST_EQ(pos->test3().b(), 20);
|
||||
|
||||
auto inventory = monster->inventory();
|
||||
TEST_NOTNULL(inventory);
|
||||
unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
for (flatbuffers::uoffset_t i = 0; i < inventory->Length(); i++)
|
||||
TEST_EQ(inventory->Get(i), inv_data[i]);
|
||||
|
||||
// Example of accessing a union:
|
||||
TEST_EQ(monster->test_type(), Any_Monster); // First make sure which it is.
|
||||
auto monster2 = reinterpret_cast<const Monster *>(monster->test());
|
||||
TEST_NOTNULL(monster2);
|
||||
TEST_EQ(monster2->hp(), 20);
|
||||
|
||||
// Since Flatbuffers uses explicit mechanisms to override the default
|
||||
// compiler alignment, double check that the compiler indeed obeys them:
|
||||
// (Test consists of a short and byte):
|
||||
TEST_EQ(flatbuffers::AlignOf<Test>(), static_cast<size_t>(2));
|
||||
TEST_EQ(sizeof(Test), static_cast<size_t>(4));
|
||||
|
||||
auto tests = monster->test4();
|
||||
TEST_NOTNULL(tests);
|
||||
auto &test_0 = tests->Get(0);
|
||||
auto &test_1 = tests->Get(1);
|
||||
TEST_EQ(test_0.a(), 10);
|
||||
TEST_EQ(test_0.b(), 20);
|
||||
TEST_EQ(test_1.a(), 30);
|
||||
TEST_EQ(test_1.b(), 40);
|
||||
}
|
||||
|
||||
// example of parsing text straight into a buffer, and generating
|
||||
// text back from it:
|
||||
void ParseAndGenerateTextTest() {
|
||||
|
||||
// load FlatBuffer schema (.fbs) and JSON from disk
|
||||
std::string schemafile;
|
||||
std::string jsonfile;
|
||||
TEST_EQ(flatbuffers::LoadFile(
|
||||
"tests/monster_test.fbs", false, &schemafile), true);
|
||||
TEST_EQ(flatbuffers::LoadFile(
|
||||
"tests/monsterdata_test.json", false, &jsonfile), true);
|
||||
|
||||
// parse schema first, so we can use it to parse the data after
|
||||
flatbuffers::Parser parser;
|
||||
TEST_EQ(parser.Parse(schemafile.c_str()), true);
|
||||
TEST_EQ(parser.Parse(jsonfile.c_str()), true);
|
||||
|
||||
// here, parser.builder_ contains a binary buffer that is the parsed data.
|
||||
|
||||
// to ensure it is correct, we now generate text back from the binary,
|
||||
// and compare the two:
|
||||
std::string jsongen;
|
||||
GenerateText(parser, parser.builder_.GetBufferPointer(), 2, &jsongen);
|
||||
|
||||
if (jsongen != jsonfile) {
|
||||
printf("%s----------------\n%s", jsongen.c_str(), jsonfile.c_str());
|
||||
TEST_NOTNULL(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void CompareTableFieldValue(flatbuffers::Table *table,
|
||||
int voffset, T val) {
|
||||
T read = table->GetField(voffset, static_cast<T>(0));
|
||||
TEST_EQ(read, val);
|
||||
}
|
||||
|
||||
// Low level stress/fuzz test: serialize/deserialize a variety of
|
||||
// different kinds of data in different combinations
|
||||
void FuzzTest1() {
|
||||
|
||||
// Values we're testing against: chosen to ensure no bits get chopped
|
||||
// off anywhere, and also be different from eachother.
|
||||
const uint8_t bool_val = true;
|
||||
const int8_t char_val = -127; // 0x81
|
||||
const uint8_t uchar_val = 0xFF;
|
||||
const int16_t short_val = -32222; // 0x8222;
|
||||
const uint16_t ushort_val = 0xFEEE;
|
||||
const int32_t int_val = 0x83333333;
|
||||
const uint32_t uint_val = 0xFDDDDDDD;
|
||||
const int64_t long_val = 0x8444444444444444;
|
||||
const uint64_t ulong_val = 0xFCCCCCCCCCCCCCCC;
|
||||
const float float_val = 3.14159f;
|
||||
const double double_val = 3.14159265359;
|
||||
|
||||
const int test_values_max = 11;
|
||||
const int fields_per_object = 4;
|
||||
const int num_fuzz_objects = 10000; // The higher, the more thorough :)
|
||||
|
||||
flatbuffers::FlatBufferBuilder builder;
|
||||
|
||||
lcg_reset(); // Keep it deterministic.
|
||||
|
||||
flatbuffers::uoffset_t objects[num_fuzz_objects];
|
||||
|
||||
// Generate num_fuzz_objects random objects each consisting of
|
||||
// fields_per_object fields, each of a random type.
|
||||
for (int i = 0; i < num_fuzz_objects; i++) {
|
||||
auto start = builder.StartTable();
|
||||
for (int f = 0; f < fields_per_object; f++) {
|
||||
int choice = lcg_rand() % test_values_max;
|
||||
flatbuffers::voffset_t off = flatbuffers::FieldIndexToOffset(f);
|
||||
switch (choice) {
|
||||
case 0: builder.AddElement<uint8_t >(off, bool_val, 0); break;
|
||||
case 1: builder.AddElement<int8_t >(off, char_val, 0); break;
|
||||
case 2: builder.AddElement<uint8_t >(off, uchar_val, 0); break;
|
||||
case 3: builder.AddElement<int16_t >(off, short_val, 0); break;
|
||||
case 4: builder.AddElement<uint16_t>(off, ushort_val, 0); break;
|
||||
case 5: builder.AddElement<int32_t >(off, int_val, 0); break;
|
||||
case 6: builder.AddElement<uint32_t>(off, uint_val, 0); break;
|
||||
case 7: builder.AddElement<int64_t >(off, long_val, 0); break;
|
||||
case 8: builder.AddElement<uint64_t>(off, ulong_val, 0); break;
|
||||
case 9: builder.AddElement<float >(off, float_val, 0); break;
|
||||
case 10: builder.AddElement<double >(off, double_val, 0); break;
|
||||
}
|
||||
}
|
||||
objects[i] = builder.EndTable(start, fields_per_object);
|
||||
}
|
||||
builder.PreAlign<flatbuffers::largest_scalar_t>(0); // Align whole buffer.
|
||||
|
||||
lcg_reset(); // Reset.
|
||||
|
||||
uint8_t *eob = builder.GetBufferPointer() + builder.GetSize();
|
||||
|
||||
// Test that all objects we generated are readable and return the
|
||||
// expected values. We generate random objects in the same order
|
||||
// so this is deterministic.
|
||||
for (int i = 0; i < num_fuzz_objects; i++) {
|
||||
auto table = reinterpret_cast<flatbuffers::Table *>(eob - objects[i]);
|
||||
for (int f = 0; f < fields_per_object; f++) {
|
||||
int choice = lcg_rand() % test_values_max;
|
||||
flatbuffers::voffset_t off = flatbuffers::FieldIndexToOffset(f);
|
||||
switch (choice) {
|
||||
case 0: CompareTableFieldValue(table, off, bool_val ); break;
|
||||
case 1: CompareTableFieldValue(table, off, char_val ); break;
|
||||
case 2: CompareTableFieldValue(table, off, uchar_val ); break;
|
||||
case 3: CompareTableFieldValue(table, off, short_val ); break;
|
||||
case 4: CompareTableFieldValue(table, off, ushort_val); break;
|
||||
case 5: CompareTableFieldValue(table, off, int_val ); break;
|
||||
case 6: CompareTableFieldValue(table, off, uint_val ); break;
|
||||
case 7: CompareTableFieldValue(table, off, long_val ); break;
|
||||
case 8: CompareTableFieldValue(table, off, ulong_val ); break;
|
||||
case 9: CompareTableFieldValue(table, off, float_val ); break;
|
||||
case 10: CompareTableFieldValue(table, off, double_val); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// High level stress/fuzz test: generate a big schema and
|
||||
// matching json data in random combinations, then parse both,
|
||||
// generate json back from the binary, and compare with the original.
|
||||
void FuzzTest2() {
|
||||
lcg_reset(); // Keep it deterministic.
|
||||
|
||||
const int num_definitions = 30;
|
||||
const int num_struct_definitions = 5; // Subset of num_definitions.
|
||||
const int fields_per_definition = 15;
|
||||
const int instances_per_definition = 5;
|
||||
|
||||
std::string schema = "namespace test;\n\n";
|
||||
|
||||
struct RndDef {
|
||||
std::string instances[instances_per_definition];
|
||||
};
|
||||
|
||||
RndDef definitions[num_definitions];
|
||||
|
||||
// We are going to generate num_definitions, the first
|
||||
// num_struct_definitions will be structs, the rest tables. For each
|
||||
// generate random fields, some of which may be struct/table types
|
||||
// referring to previously generated structs/tables.
|
||||
// Simultanenously, we generate instances_per_definition JSON data
|
||||
// definitions, which will have identical structure to the schema
|
||||
// being generated. We generate multiple instances such that when creating
|
||||
// hierarchy, we get some variety by picking one randomly.
|
||||
for (int definition = 0; definition < num_definitions; definition++) {
|
||||
// Since we're generating schema & and corresponding data in tandem,
|
||||
// this convenience function adds strings to both at once.
|
||||
auto AddToSchemaAndInstances = [&](const char *schema_add,
|
||||
const char *instance_add) {
|
||||
schema += schema_add;
|
||||
for (int i = 0; i < instances_per_definition; i++)
|
||||
definitions[definition].instances[i] += instance_add;
|
||||
};
|
||||
// Generate a default type if we can't generate something else.
|
||||
auto Dummy = [&]() { AddToSchemaAndInstances("byte", "1"); };
|
||||
|
||||
std::string definition_name = "D" + flatbuffers::NumToString(definition);
|
||||
|
||||
bool is_struct = definition < num_struct_definitions;
|
||||
|
||||
AddToSchemaAndInstances(
|
||||
((is_struct ? "struct " : "table ") + definition_name + " {\n").c_str(),
|
||||
"{\n");
|
||||
|
||||
for (int field = 0; field < fields_per_definition; field++) {
|
||||
std::string field_name = "f" + flatbuffers::NumToString(field);
|
||||
AddToSchemaAndInstances((" " + field_name + ":").c_str(),
|
||||
(field_name + ": ").c_str());
|
||||
// Pick random type:
|
||||
int base_type = lcg_rand() % (flatbuffers::BASE_TYPE_UNION + 1);
|
||||
switch (base_type) {
|
||||
case flatbuffers::BASE_TYPE_STRING:
|
||||
if (is_struct) {
|
||||
Dummy(); // No strings in structs,
|
||||
} else {
|
||||
AddToSchemaAndInstances("string", "\"hi\"");
|
||||
}
|
||||
break;
|
||||
case flatbuffers::BASE_TYPE_NONE:
|
||||
case flatbuffers::BASE_TYPE_UTYPE:
|
||||
case flatbuffers::BASE_TYPE_STRUCT:
|
||||
case flatbuffers::BASE_TYPE_UNION:
|
||||
case flatbuffers::BASE_TYPE_VECTOR:
|
||||
if (definition) {
|
||||
// Pick a random previous definition and random data instance of
|
||||
// that definition.
|
||||
int defref = lcg_rand() % definition;
|
||||
int instance = lcg_rand() % instances_per_definition;
|
||||
AddToSchemaAndInstances(
|
||||
("D" + flatbuffers::NumToString(defref)).c_str(),
|
||||
definitions[defref].instances[instance].c_str());
|
||||
} else {
|
||||
// If this is the first definition, we have no definition we can
|
||||
// refer to.
|
||||
Dummy();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// All the scalar types.
|
||||
AddToSchemaAndInstances(
|
||||
flatbuffers::kTypeNames[base_type],
|
||||
flatbuffers::NumToString(lcg_rand() % 128).c_str());
|
||||
}
|
||||
AddToSchemaAndInstances(
|
||||
";\n",
|
||||
field == fields_per_definition - 1 ? "\n" : ",\n");
|
||||
}
|
||||
AddToSchemaAndInstances("}\n\n", "}");
|
||||
}
|
||||
|
||||
schema += "root_type D" + flatbuffers::NumToString(num_definitions - 1);
|
||||
schema += ";\n";
|
||||
|
||||
flatbuffers::Parser parser;
|
||||
|
||||
// Will not compare against the original if we don't write defaults
|
||||
parser.builder_.ForceDefaults(true);
|
||||
|
||||
// Parse the schema, parse the generated data, then generate text back
|
||||
// from the binary and compare against the original.
|
||||
TEST_EQ(parser.Parse(schema.c_str()), true);
|
||||
|
||||
const std::string &json =
|
||||
definitions[num_definitions - 1].instances[0] + "\n";
|
||||
|
||||
TEST_EQ(parser.Parse(json.c_str()), true);
|
||||
|
||||
std::string jsongen;
|
||||
GenerateText(parser, parser.builder_.GetBufferPointer(), 0, &jsongen);
|
||||
|
||||
if (jsongen != json) {
|
||||
// These strings are larger than a megabyte, so we show the bytes around
|
||||
// the first bytes that are different rather than the whole string.
|
||||
size_t len = std::min(json.length(), jsongen.length());
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (json[i] != jsongen[i]) {
|
||||
i -= std::min(static_cast<size_t>(10), i); // show some context;
|
||||
size_t end = std::min(len, i + 20);
|
||||
for (; i < end; i++)
|
||||
printf("at %d: found \"%c\", expected \"%c\"\n",
|
||||
static_cast<int>(i), jsongen[i], json[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
TEST_NOTNULL(NULL);
|
||||
}
|
||||
|
||||
printf("%dk schema tested with %dk of json\n",
|
||||
static_cast<int>(schema.length() / 1024),
|
||||
static_cast<int>(json.length() / 1024));
|
||||
}
|
||||
|
||||
// Test that parser errors are actually generated.
|
||||
void TestError(const char *src, const char *error_substr) {
|
||||
flatbuffers::Parser parser;
|
||||
TEST_EQ(parser.Parse(src), false); // Must signal error
|
||||
// Must be the error we're expecting
|
||||
TEST_NOTNULL(strstr(parser.error_.c_str(), error_substr));
|
||||
}
|
||||
|
||||
// Test that parsing errors occur as we'd expect.
|
||||
// Also useful for coverage, making sure these paths are run.
|
||||
void ErrorTest() {
|
||||
// In order they appear in idl_parser.cpp
|
||||
TestError("table X { Y:byte; } root_type X; { Y: 999 }", "bit field");
|
||||
TestError(".0", "floating point");
|
||||
TestError("\"\0", "illegal");
|
||||
TestError("\"\\q", "escape code");
|
||||
TestError("table ///", "documentation");
|
||||
TestError("@", "illegal");
|
||||
TestError("table 1", "expecting");
|
||||
TestError("table X { Y:[[int]]; }", "nested vector");
|
||||
TestError("union Z { X } table X { Y:[Z]; }", "vector of union");
|
||||
TestError("table X { Y:1; }", "illegal type");
|
||||
TestError("table X { Y:int; Y:int; }", "field already");
|
||||
TestError("struct X { Y:string; }", "only scalar");
|
||||
TestError("struct X { Y:int (deprecated); }", "deprecate");
|
||||
TestError("union Z { X } table X { Y:Z; } root_type X; { Y: {",
|
||||
"missing type field");
|
||||
TestError("union Z { X } table X { Y:Z; } root_type X; { Y_type: 99, Y: {",
|
||||
"type id");
|
||||
TestError("table X { Y:int; } root_type X; { Z:", "unknown field");
|
||||
TestError("struct X { Y:int; Z:int; } table W { V:X; } root_type W; "
|
||||
"{ V:{ Y:1 } }", "incomplete");
|
||||
TestError("table X { Y:byte; } root_type X; { Y:U }", "valid enum");
|
||||
TestError("table X { Y:byte; } root_type X; { Y:; }", "starting");
|
||||
TestError("enum X:byte { Y } enum X {", "enum already");
|
||||
TestError("enum X:float {}", "underlying");
|
||||
TestError("enum X:byte { Y, Y }", "value already");
|
||||
TestError("enum X:byte { Y=2, Z=1 }", "ascending");
|
||||
TestError("table X { Y:int; } table X {", "datatype already");
|
||||
TestError("struct X (force_align: 7) { Y:int; }", "force_align");
|
||||
TestError("{}", "no root");
|
||||
TestError("table X { Y:byte; } root_type X; { Y:1 } { Y:1 }", "one json");
|
||||
TestError("root_type X;", "unknown root");
|
||||
TestError("struct X { Y:int; } root_type X;", "a table");
|
||||
TestError("union X { Y }", "referenced");
|
||||
TestError("union Z { X } struct X { Y:int; }", "only tables");
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Run our various test suites:
|
||||
|
||||
auto flatbuf = CreateFlatBufferTest();
|
||||
AccessFlatBufferTest(flatbuf);
|
||||
|
||||
#ifndef __ANDROID__ // requires file access
|
||||
ParseAndGenerateTextTest();
|
||||
#endif
|
||||
|
||||
FuzzTest1();
|
||||
FuzzTest2();
|
||||
|
||||
ErrorTest();
|
||||
|
||||
if (!testing_fails) {
|
||||
TEST_OUTPUT_LINE("ALL TESTS PASSED");
|
||||
return 0;
|
||||
} else {
|
||||
TEST_OUTPUT_LINE("%d FAILED TESTS", testing_fails);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user