Commit 199a49b5 authored by gonzaloserrano's avatar gonzaloserrano

Add a generic way to deserialize a flatbuffer in Go.

Similar to what protobufs does with its `Message` interface, introduce here such interface and create a generic `GetRootAs` method to deserialize a flatbuffer.
parent b36bd67b
package flatbuffers
// FlatBuffer is the interface that represents a flatbuffer.
type FlatBuffer interface {
Table() Table
Init(buf []byte, i UOffsetT)
}
// GetRootAs is a generic helper to initialize a FlatBuffer with the provided buffer bytes and its data offset.
func GetRootAs(buf []byte, offset UOffsetT, fb FlatBuffer) {
n := GetUOffsetT(buf[offset:])
fb.Init(buf, n+offset)
}
......@@ -144,6 +144,23 @@ static void InitializeExisting(const StructDef &struct_def,
code += "}\n\n";
}
// Implement the table accessor
static void GenTableAccessor(const StructDef &struct_def,
std::string *code_ptr) {
std::string &code = *code_ptr;
GenReceiver(struct_def, code_ptr);
code += " Table() flatbuffers.Table ";
code += "{\n";
if (struct_def.fixed) {
code += "\treturn rcv._tab.Table\n";
} else {
code += "\treturn rcv._tab\n";
}
code += "}\n\n";
}
// Get the length of a vector.
static void GetVectorLen(const StructDef &struct_def,
const FieldDef &field,
......@@ -594,6 +611,10 @@ static void GenStruct(const StructDef &struct_def,
// Generate the Init method that sets the field in a pre-existing
// accessor object. This is to allow object reuse.
InitializeExisting(struct_def, code_ptr);
// Generate _tab accessor
GenTableAccessor(struct_def, code_ptr);
// Generate struct fields accessors
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end();
++it) {
......@@ -604,6 +625,7 @@ static void GenStruct(const StructDef &struct_def,
GenStructMutator(struct_def, field, code_ptr);
}
// Generate builders
if (struct_def.fixed) {
// create a struct constructor function
GenStructBuilder(struct_def, code_ptr);
......
......@@ -23,6 +23,10 @@ func (rcv *Monster) Init(buf []byte, i flatbuffers.UOffsetT) {
rcv._tab.Pos = i
}
func (rcv *Monster) Table() flatbuffers.Table {
return rcv._tab
}
func (rcv *Monster) Pos(obj *Vec3) *Vec3 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
if o != 0 {
......
......@@ -22,6 +22,10 @@ func (rcv *Stat) Init(buf []byte, i flatbuffers.UOffsetT) {
rcv._tab.Pos = i
}
func (rcv *Stat) Table() flatbuffers.Table {
return rcv._tab
}
func (rcv *Stat) Id() []byte {
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
if o != 0 {
......
......@@ -15,6 +15,10 @@ func (rcv *Test) Init(buf []byte, i flatbuffers.UOffsetT) {
rcv._tab.Pos = i
}
func (rcv *Test) Table() flatbuffers.Table {
return rcv._tab.Table
}
func (rcv *Test) A() int16 {
return rcv._tab.GetInt16(rcv._tab.Pos + flatbuffers.UOffsetT(0))
}
......
......@@ -22,6 +22,10 @@ func (rcv *TestSimpleTableWithEnum) Init(buf []byte, i flatbuffers.UOffsetT) {
rcv._tab.Pos = i
}
func (rcv *TestSimpleTableWithEnum) Table() flatbuffers.Table {
return rcv._tab
}
func (rcv *TestSimpleTableWithEnum) Color() int8 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
if o != 0 {
......
......@@ -15,6 +15,10 @@ func (rcv *Vec3) Init(buf []byte, i flatbuffers.UOffsetT) {
rcv._tab.Pos = i
}
func (rcv *Vec3) Table() flatbuffers.Table {
return rcv._tab.Table
}
func (rcv *Vec3) X() float32 {
return rcv._tab.GetFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(0))
}
......
......@@ -22,6 +22,10 @@ func (rcv *Monster) Init(buf []byte, i flatbuffers.UOffsetT) {
rcv._tab.Pos = i
}
func (rcv *Monster) Table() flatbuffers.Table {
return rcv._tab
}
func MonsterStart(builder *flatbuffers.Builder) {
builder.StartObject(0)
}
......
......@@ -78,6 +78,7 @@ func TestAll(t *testing.T) {
// Verify that GetRootAs works for non-root tables
CheckGetRootAsForNonRootTable(t.Fatalf)
CheckTableAccessors(t.Fatalf)
// Verify that using the generated Go code builds a buffer without
// returning errors:
......@@ -137,155 +138,159 @@ func TestAll(t *testing.T) {
// CheckReadBuffer checks that the given buffer is evaluated correctly
// as the example Monster.
func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string, ...interface{})) {
monster := example.GetRootAsMonster(buf, offset)
if got := monster.Hp(); 80 != got {
fail(FailString("hp", 80, got))
}
// try the two ways of generating a monster
monster1 := example.GetRootAsMonster(buf, offset)
monster2 := &example.Monster{}
flatbuffers.GetRootAs(buf, offset, monster2)
for _, monster := range []*example.Monster{monster1, monster2} {
if got := monster.Hp(); 80 != got {
fail(FailString("hp", 80, got))
}
// default
if got := monster.Mana(); 150 != got {
fail(FailString("mana", 150, got))
}
// default
if got := monster.Mana(); 150 != got {
fail(FailString("mana", 150, got))
}
if got := monster.Name(); !bytes.Equal([]byte("MyMonster"), got) {
fail(FailString("name", "MyMonster", got))
}
if got := monster.Name(); !bytes.Equal([]byte("MyMonster"), got) {
fail(FailString("name", "MyMonster", got))
}
// initialize a Vec3 from Pos()
vec := new(example.Vec3)
vec = monster.Pos(vec)
if vec == nil {
fail("vec3 initialization failed")
}
// initialize a Vec3 from Pos()
vec := new(example.Vec3)
vec = monster.Pos(vec)
if vec == nil {
fail("vec3 initialization failed")
}
// check that new allocs equal given ones:
vec2 := monster.Pos(nil)
if !reflect.DeepEqual(vec, vec2) {
fail("fresh allocation failed")
}
// check that new allocs equal given ones:
vec2 := monster.Pos(nil)
if !reflect.DeepEqual(vec, vec2) {
fail("fresh allocation failed")
}
// verify the properties of the Vec3
if got := vec.X(); float32(1.0) != got {
fail(FailString("Pos.X", float32(1.0), got))
}
// verify the properties of the Vec3
if got := vec.X(); float32(1.0) != got {
fail(FailString("Pos.X", float32(1.0), got))
}
if got := vec.Y(); float32(2.0) != got {
fail(FailString("Pos.Y", float32(2.0), got))
}
if got := vec.Y(); float32(2.0) != got {
fail(FailString("Pos.Y", float32(2.0), got))
}
if got := vec.Z(); float32(3.0) != got {
fail(FailString("Pos.Z", float32(3.0), got))
}
if got := vec.Z(); float32(3.0) != got {
fail(FailString("Pos.Z", float32(3.0), got))
}
if got := vec.Test1(); float64(3.0) != got {
fail(FailString("Pos.Test1", float64(3.0), got))
}
if got := vec.Test1(); float64(3.0) != got {
fail(FailString("Pos.Test1", float64(3.0), got))
}
if got := vec.Test2(); int8(2) != got {
fail(FailString("Pos.Test2", int8(2), got))
}
if got := vec.Test2(); int8(2) != got {
fail(FailString("Pos.Test2", int8(2), got))
}
// initialize a Test from Test3(...)
t := new(example.Test)
t = vec.Test3(t)
if t == nil {
fail("vec.Test3(&t) failed")
}
// initialize a Test from Test3(...)
t := new(example.Test)
t = vec.Test3(t)
if t == nil {
fail("vec.Test3(&t) failed")
}
// check that new allocs equal given ones:
t2 := vec.Test3(nil)
if !reflect.DeepEqual(t, t2) {
fail("fresh allocation failed")
}
// check that new allocs equal given ones:
t2 := vec.Test3(nil)
if !reflect.DeepEqual(t, t2) {
fail("fresh allocation failed")
}
// verify the properties of the Test
if got := t.A(); int16(5) != got {
fail(FailString("t.A()", int16(5), got))
}
// verify the properties of the Test
if got := t.A(); int16(5) != got {
fail(FailString("t.A()", int16(5), got))
}
if got := t.B(); int8(6) != got {
fail(FailString("t.B()", int8(6), got))
}
if got := t.B(); int8(6) != got {
fail(FailString("t.B()", int8(6), got))
}
if got := monster.TestType(); example.AnyMonster != got {
fail(FailString("monster.TestType()", example.AnyMonster, got))
}
if got := monster.TestType(); example.AnyMonster != got {
fail(FailString("monster.TestType()", example.AnyMonster, got))
}
// initialize a Table from a union field Test(...)
var table2 flatbuffers.Table
if ok := monster.Test(&table2); !ok {
fail("monster.Test(&monster2) failed")
}
// initialize a Table from a union field Test(...)
var table2 flatbuffers.Table
if ok := monster.Test(&table2); !ok {
fail("monster.Test(&monster2) failed")
}
// initialize a Monster from the Table from the union
var monster2 example.Monster
monster2.Init(table2.Bytes, table2.Pos)
// initialize a Monster from the Table from the union
var monster2 example.Monster
monster2.Init(table2.Bytes, table2.Pos)
if got := monster2.Name(); !bytes.Equal([]byte("Fred"), got) {
fail(FailString("monster2.Name()", "Fred", got))
}
if got := monster2.Name(); !bytes.Equal([]byte("Fred"), got) {
fail(FailString("monster2.Name()", "Fred", got))
}
inventorySlice := monster.InventoryBytes()
if len(inventorySlice) != monster.InventoryLength() {
fail(FailString("len(monster.InventoryBytes) != monster.InventoryLength", len(inventorySlice), monster.InventoryLength()))
}
inventorySlice := monster.InventoryBytes()
if len(inventorySlice) != monster.InventoryLength() {
fail(FailString("len(monster.InventoryBytes) != monster.InventoryLength", len(inventorySlice), monster.InventoryLength()))
}
if got := monster.InventoryLength(); 5 != got {
fail(FailString("monster.InventoryLength", 5, got))
}
if got := monster.InventoryLength(); 5 != got {
fail(FailString("monster.InventoryLength", 5, got))
}
invsum := 0
l := monster.InventoryLength()
for i := 0; i < l; i++ {
v := monster.Inventory(i)
if v != inventorySlice[i] {
fail(FailString("monster inventory slice[i] != Inventory(i)", v, inventorySlice[i]))
invsum := 0
l := monster.InventoryLength()
for i := 0; i < l; i++ {
v := monster.Inventory(i)
if v != inventorySlice[i] {
fail(FailString("monster inventory slice[i] != Inventory(i)", v, inventorySlice[i]))
}
invsum += int(v)
}
if invsum != 10 {
fail(FailString("monster inventory sum", 10, invsum))
}
invsum += int(v)
}
if invsum != 10 {
fail(FailString("monster inventory sum", 10, invsum))
}
if got := monster.Test4Length(); 2 != got {
fail(FailString("monster.Test4Length()", 2, got))
}
if got := monster.Test4Length(); 2 != got {
fail(FailString("monster.Test4Length()", 2, got))
}
var test0 example.Test
ok := monster.Test4(&test0, 0)
if !ok {
fail(FailString("monster.Test4(&test0, 0)", true, ok))
}
var test0 example.Test
ok := monster.Test4(&test0, 0)
if !ok {
fail(FailString("monster.Test4(&test0, 0)", true, ok))
}
var test1 example.Test
ok = monster.Test4(&test1, 1)
if !ok {
fail(FailString("monster.Test4(&test1, 1)", true, ok))
}
var test1 example.Test
ok = monster.Test4(&test1, 1)
if !ok {
fail(FailString("monster.Test4(&test1, 1)", true, ok))
}
// the position of test0 and test1 are swapped in monsterdata_java_wire
// and monsterdata_test_wire, so ignore ordering
v0 := test0.A()
v1 := test0.B()
v2 := test1.A()
v3 := test1.B()
sum := int(v0) + int(v1) + int(v2) + int(v3)
// the position of test0 and test1 are swapped in monsterdata_java_wire
// and monsterdata_test_wire, so ignore ordering
v0 := test0.A()
v1 := test0.B()
v2 := test1.A()
v3 := test1.B()
sum := int(v0) + int(v1) + int(v2) + int(v3)
if 100 != sum {
fail(FailString("test0 and test1 sum", 100, sum))
}
if 100 != sum {
fail(FailString("test0 and test1 sum", 100, sum))
}
if got := monster.TestarrayofstringLength(); 2 != got {
fail(FailString("Testarrayofstring length", 2, got))
}
if got := monster.TestarrayofstringLength(); 2 != got {
fail(FailString("Testarrayofstring length", 2, got))
}
if got := monster.Testarrayofstring(0); !bytes.Equal([]byte("test1"), got) {
fail(FailString("Testarrayofstring(0)", "test1", got))
}
if got := monster.Testarrayofstring(0); !bytes.Equal([]byte("test1"), got) {
fail(FailString("Testarrayofstring(0)", "test1", got))
}
if got := monster.Testarrayofstring(1); !bytes.Equal([]byte("test2"), got) {
fail(FailString("Testarrayofstring(1)", "test2", got))
if got := monster.Testarrayofstring(1); !bytes.Equal([]byte("test2"), got) {
fail(FailString("Testarrayofstring(1)", "test2", got))
}
}
}
......@@ -1161,6 +1166,38 @@ func CheckGeneratedBuild(fail func(string, ...interface{})) ([]byte, flatbuffers
return b.Bytes, b.Head()
}
// CheckTableAccessors checks that the table accessors work as expected.
func CheckTableAccessors(fail func(string, ...interface{})) {
// test struct accessor
b := flatbuffers.NewBuilder(0)
pos := example.CreateVec3(b, 1.0, 2.0, 3.0, 3.0, 4, 5, 6)
b.Finish(pos)
vec3Bytes := b.FinishedBytes()
vec3 := &example.Vec3{}
flatbuffers.GetRootAs(vec3Bytes, 0, vec3)
if bytes.Compare(vec3Bytes, vec3.Table().Bytes) != 0 {
fail("invalid vec3 table")
}
// test table accessor
b = flatbuffers.NewBuilder(0)
str := b.CreateString("MyStat")
example.StatStart(b)
example.StatAddId(b, str)
example.StatAddVal(b, 12345678)
example.StatAddCount(b, 12345)
pos = example.StatEnd(b)
b.Finish(pos)
statBytes := b.FinishedBytes()
stat := &example.Stat{}
flatbuffers.GetRootAs(statBytes, 0, stat)
if bytes.Compare(statBytes, stat.Table().Bytes) != 0 {
fail("invalid stat table")
}
}
// CheckVtableDeduplication verifies that vtables are deduplicated.
func CheckVtableDeduplication(fail func(string, ...interface{})) {
b := flatbuffers.NewBuilder(0)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment