From c9b29d088550d5b18a952fdc126f5894981bb011 Mon Sep 17 00:00:00 2001 From: Charlie Yin Date: Thu, 5 Nov 2020 11:23:56 -0800 Subject: [PATCH] Support size-prefixed buffers and add tests for size-prefixed messages (#6232) --- go/builder.go | 45 ++++++++- go/lib.go | 12 +++ src/idl_gen_go.cpp | 31 ++++-- tests/MyGame/Example/Monster.go | 7 ++ tests/MyGame/Example/Referrable.go | 7 ++ tests/MyGame/Example/Stat.go | 7 ++ .../MyGame/Example/TestSimpleTableWithEnum.go | 7 ++ tests/MyGame/Example/TypeAliases.go | 7 ++ tests/MyGame/Example2/Monster.go | 7 ++ tests/MyGame/InParentNamespace.go | 7 ++ tests/go_test.go | 99 +++++++++++++++---- .../NamespaceA/NamespaceB/TableInNestedNS.go | 7 ++ .../NamespaceA/SecondTableInA.go | 7 ++ .../NamespaceA/TableInFirstNS.go | 7 ++ tests/namespace_test/NamespaceC/TableInC.go | 7 ++ 15 files changed, 232 insertions(+), 32 deletions(-) diff --git a/go/builder.go b/go/builder.go index 115da2996..d99b590bb 100644 --- a/go/builder.go +++ b/go/builder.go @@ -22,6 +22,7 @@ type Builder struct { } const fileIdentifierLength = 4 +const sizePrefixLength = 4 // NewBuilder initializes a Builder of size `initial_size`. // The internal buffer is grown as needed. @@ -580,11 +581,53 @@ func (b *Builder) FinishWithFileIdentifier(rootTable UOffsetT, fid []byte) { b.Finish(rootTable) } +// FinishSizePrefixed finalizes a buffer, pointing to the given `rootTable`. +// The buffer is prefixed with the size of the buffer, excluding the size +// of the prefix itself. +func (b *Builder) FinishSizePrefixed(rootTable UOffsetT) { + b.finish(rootTable, true) +} + +// FinishSizePrefixedWithFileIdentifier finalizes a buffer, pointing to the given `rootTable` +// and applies a file identifier. The buffer is prefixed with the size of the buffer, +// excluding the size of the prefix itself. +func (b *Builder) FinishSizePrefixedWithFileIdentifier(rootTable UOffsetT, fid []byte) { + if fid == nil || len(fid) != fileIdentifierLength { + panic("incorrect file identifier length") + } + // In order to add a file identifier and size prefix to the flatbuffer message, + // we need to prepare an alignment, a size prefix length, and file identifier length + b.Prep(b.minalign, SizeInt32+fileIdentifierLength+sizePrefixLength) + for i := fileIdentifierLength - 1; i >= 0; i-- { + // place the file identifier + b.PlaceByte(fid[i]) + } + // finish + b.finish(rootTable, true) +} + // Finish finalizes a buffer, pointing to the given `rootTable`. func (b *Builder) Finish(rootTable UOffsetT) { + b.finish(rootTable, false) +} + +// finish finalizes a buffer, pointing to the given `rootTable` +// with an optional size prefix. +func (b *Builder) finish(rootTable UOffsetT, sizePrefix bool) { b.assertNotNested() - b.Prep(b.minalign, SizeUOffsetT) + + if sizePrefix { + b.Prep(b.minalign, SizeUOffsetT+sizePrefixLength) + } else { + b.Prep(b.minalign, SizeUOffsetT) + } + b.PrependUOffsetT(rootTable) + + if sizePrefix { + b.PlaceUint32(uint32(b.Offset())) + } + b.finished = true } diff --git a/go/lib.go b/go/lib.go index adfce52ef..9a333ff04 100644 --- a/go/lib.go +++ b/go/lib.go @@ -11,3 +11,15 @@ func GetRootAs(buf []byte, offset UOffsetT, fb FlatBuffer) { n := GetUOffsetT(buf[offset:]) fb.Init(buf, n+offset) } + +// GetSizePrefixedRootAs is a generic helper to initialize a FlatBuffer with the provided size-prefixed buffer +// bytes and its data offset +func GetSizePrefixedRootAs(buf []byte, offset UOffsetT, fb FlatBuffer) { + n := GetUOffsetT(buf[offset+sizePrefixLength:]) + fb.Init(buf, n+offset+sizePrefixLength) +} + +// GetSizePrefix reads the size from a size-prefixed flatbuffer +func GetSizePrefix(buf []byte, offset UOffsetT) uint32 { + return GetUint32(buf[offset:]) +} diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp index 95e9a5514..68cc01f9b 100644 --- a/src/idl_gen_go.cpp +++ b/src/idl_gen_go.cpp @@ -255,17 +255,28 @@ class GoGenerator : public BaseGenerator { void NewRootTypeFromBuffer(const StructDef &struct_def, std::string *code_ptr) { std::string &code = *code_ptr; + std::string size_prefix[] = { "", "SizePrefixed" }; - code += "func GetRootAs"; - code += struct_def.name; - code += "(buf []byte, offset flatbuffers.UOffsetT) "; - code += "*" + struct_def.name + ""; - code += " {\n"; - code += "\tn := flatbuffers.GetUOffsetT(buf[offset:])\n"; - code += "\tx := &" + struct_def.name + "{}\n"; - code += "\tx.Init(buf, n+offset)\n"; - code += "\treturn x\n"; - code += "}\n\n"; + for (int i = 0; i < 2; i++) { + code += "func Get" + size_prefix[i] + "RootAs"; + code += struct_def.name; + code += "(buf []byte, offset flatbuffers.UOffsetT) "; + code += "*" + struct_def.name + ""; + code += " {\n"; + if (i == 0) { + code += "\tn := flatbuffers.GetUOffsetT(buf[offset:])\n"; + } else { + code += "\tn := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])\n"; + } + code += "\tx := &" + struct_def.name + "{}\n"; + if (i == 0) { + code += "\tx.Init(buf, n+offset)\n"; + } else { + code += "\tx.Init(buf, n+offset+flatbuffers.SizeUint32)\n"; + } + code += "\treturn x\n"; + code += "}\n\n"; + } } // Initialize an existing object with other data, to avoid an allocation. diff --git a/tests/MyGame/Example/Monster.go b/tests/MyGame/Example/Monster.go index 7db88a9c1..b3472710f 100644 --- a/tests/MyGame/Example/Monster.go +++ b/tests/MyGame/Example/Monster.go @@ -442,6 +442,13 @@ func GetRootAsMonster(buf []byte, offset flatbuffers.UOffsetT) *Monster { return x } +func GetSizePrefixedRootAsMonster(buf []byte, offset flatbuffers.UOffsetT) *Monster { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &Monster{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *Monster) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/tests/MyGame/Example/Referrable.go b/tests/MyGame/Example/Referrable.go index 8f21e910a..66c59729c 100644 --- a/tests/MyGame/Example/Referrable.go +++ b/tests/MyGame/Example/Referrable.go @@ -39,6 +39,13 @@ func GetRootAsReferrable(buf []byte, offset flatbuffers.UOffsetT) *Referrable { return x } +func GetSizePrefixedRootAsReferrable(buf []byte, offset flatbuffers.UOffsetT) *Referrable { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &Referrable{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *Referrable) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/tests/MyGame/Example/Stat.go b/tests/MyGame/Example/Stat.go index 6034e01c8..5c060d1af 100644 --- a/tests/MyGame/Example/Stat.go +++ b/tests/MyGame/Example/Stat.go @@ -46,6 +46,13 @@ func GetRootAsStat(buf []byte, offset flatbuffers.UOffsetT) *Stat { return x } +func GetSizePrefixedRootAsStat(buf []byte, offset flatbuffers.UOffsetT) *Stat { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &Stat{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *Stat) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.go b/tests/MyGame/Example/TestSimpleTableWithEnum.go index 14805af72..b8cde12b8 100644 --- a/tests/MyGame/Example/TestSimpleTableWithEnum.go +++ b/tests/MyGame/Example/TestSimpleTableWithEnum.go @@ -39,6 +39,13 @@ func GetRootAsTestSimpleTableWithEnum(buf []byte, offset flatbuffers.UOffsetT) * return x } +func GetSizePrefixedRootAsTestSimpleTableWithEnum(buf []byte, offset flatbuffers.UOffsetT) *TestSimpleTableWithEnum { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &TestSimpleTableWithEnum{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *TestSimpleTableWithEnum) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/tests/MyGame/Example/TypeAliases.go b/tests/MyGame/Example/TypeAliases.go index 1c259aa52..d018fa1f6 100644 --- a/tests/MyGame/Example/TypeAliases.go +++ b/tests/MyGame/Example/TypeAliases.go @@ -98,6 +98,13 @@ func GetRootAsTypeAliases(buf []byte, offset flatbuffers.UOffsetT) *TypeAliases return x } +func GetSizePrefixedRootAsTypeAliases(buf []byte, offset flatbuffers.UOffsetT) *TypeAliases { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &TypeAliases{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *TypeAliases) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/tests/MyGame/Example2/Monster.go b/tests/MyGame/Example2/Monster.go index 33309f8bd..792011f24 100644 --- a/tests/MyGame/Example2/Monster.go +++ b/tests/MyGame/Example2/Monster.go @@ -36,6 +36,13 @@ func GetRootAsMonster(buf []byte, offset flatbuffers.UOffsetT) *Monster { return x } +func GetSizePrefixedRootAsMonster(buf []byte, offset flatbuffers.UOffsetT) *Monster { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &Monster{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *Monster) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/tests/MyGame/InParentNamespace.go b/tests/MyGame/InParentNamespace.go index 9c5adf72e..2c4a4e0e6 100644 --- a/tests/MyGame/InParentNamespace.go +++ b/tests/MyGame/InParentNamespace.go @@ -36,6 +36,13 @@ func GetRootAsInParentNamespace(buf []byte, offset flatbuffers.UOffsetT) *InPare return x } +func GetSizePrefixedRootAsInParentNamespace(buf []byte, offset flatbuffers.UOffsetT) *InParentNamespace { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &InParentNamespace{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *InParentNamespace) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/tests/go_test.go b/tests/go_test.go index 96d3e4977..9e64cca0d 100644 --- a/tests/go_test.go +++ b/tests/go_test.go @@ -87,13 +87,13 @@ func TestAll(t *testing.T) { // Verify that using the generated Go code builds a buffer without // returning errors: - generated, off := CheckGeneratedBuild(t.Fatalf) + generated, off := CheckGeneratedBuild(false, t.Fatalf) // Verify that the buffer generated by Go code is readable by the // generated Go code: - CheckReadBuffer(generated, off, t.Fatalf) - CheckMutateBuffer(generated, off, t.Fatalf) - CheckObjectAPI(generated, off, t.Fatalf) + CheckReadBuffer(generated, off, false, t.Fatalf) + CheckMutateBuffer(generated, off, false, t.Fatalf) + CheckObjectAPI(generated, off, false, t.Fatalf) // Verify that the buffer generated by C++ code is readable by the // generated Go code: @@ -101,9 +101,9 @@ func TestAll(t *testing.T) { if err != nil { t.Fatal(err) } - CheckReadBuffer(monsterDataCpp, 0, t.Fatalf) - CheckMutateBuffer(monsterDataCpp, 0, t.Fatalf) - CheckObjectAPI(monsterDataCpp, 0, t.Fatalf) + CheckReadBuffer(monsterDataCpp, 0, false, t.Fatalf) + CheckMutateBuffer(monsterDataCpp, 0, false, t.Fatalf) + CheckObjectAPI(monsterDataCpp, 0, false, t.Fatalf) // Verify that vtables are deduplicated when written: CheckVtableDeduplication(t.Fatalf) @@ -127,6 +127,9 @@ func TestAll(t *testing.T) { // Check a parent namespace import CheckParentNamespace(t.Fatalf) + // Check size-prefixed flatbuffers + CheckSizePrefixedBuffer(t.Fatalf) + // If the filename of the FlatBuffers file generated by the Java test // is given, check that Go code can read it, and that Go code // generates an identical buffer when used to create the example data: @@ -135,7 +138,7 @@ func TestAll(t *testing.T) { if err != nil { t.Fatal(err) } - CheckReadBuffer(monsterDataJava, 0, t.Fatalf) + CheckReadBuffer(monsterDataJava, 0, false, t.Fatalf) CheckByteEquality(generated[off:], monsterDataJava, t.Fatalf) } @@ -153,11 +156,19 @@ 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{})) { +func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, sizePrefix bool, fail func(string, ...interface{})) { // try the two ways of generating a monster - monster1 := example.GetRootAsMonster(buf, offset) + var monster1 *example.Monster monster2 := &example.Monster{} - flatbuffers.GetRootAs(buf, offset, monster2) + + if sizePrefix { + monster1 = example.GetSizePrefixedRootAsMonster(buf, offset) + flatbuffers.GetSizePrefixedRootAs(buf, offset, monster2) + } else { + monster1 = example.GetRootAsMonster(buf, offset) + flatbuffers.GetRootAs(buf, offset, monster2) + } + for _, monster := range []*example.Monster{monster1, monster2} { if got := monster.Hp(); 80 != got { fail(FailString("hp", 80, got)) @@ -320,13 +331,18 @@ func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string, // CheckMutateBuffer checks that the given buffer can be mutated correctly // as the example Monster. Only available scalar values are mutated. -func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string, ...interface{})) { +func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, sizePrefix bool, fail func(string, ...interface{})) { // make a copy to mutate buf := make([]byte, len(org)) copy(buf, org) // load monster data from the buffer - monster := example.GetRootAsMonster(buf, offset) + var monster *example.Monster + if sizePrefix { + monster = example.GetSizePrefixedRootAsMonster(buf, offset) + } else { + monster = example.GetRootAsMonster(buf, offset) + } // test case struct type testcase struct { @@ -409,7 +425,12 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string // To make sure the buffer has changed accordingly // Read data from the buffer and verify all fields - monster = example.GetRootAsMonster(buf, offset) + if sizePrefix { + monster = example.GetSizePrefixedRootAsMonster(buf, offset) + } else { + monster = example.GetRootAsMonster(buf, offset) + } + for _, t := range testForMutatedValues { if !t.testfn() { fail("field '" + t.field + "' doesn't have the expected mutated value") @@ -429,7 +450,12 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string // back to their original values and compare buffers. // This test is done to make sure mutations do not do // any unnecessary changes to the buffer. - monster = example.GetRootAsMonster(buf, offset) + if sizePrefix { + monster = example.GetSizePrefixedRootAsMonster(buf, offset) + } else { + monster = example.GetRootAsMonster(buf, offset) + } + monster.MutateHp(80) monster.MutateTestbool(true) monster.Pos(nil).MutateX(1.0) @@ -453,8 +479,14 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string } } -func CheckObjectAPI(buf []byte, offset flatbuffers.UOffsetT, fail func(string, ...interface{})) { - monster := example.GetRootAsMonster(buf, offset).UnPack() +func CheckObjectAPI(buf []byte, offset flatbuffers.UOffsetT, sizePrefix bool, fail func(string, ...interface{})) { + var monster *example.MonsterT + + if sizePrefix { + monster = example.GetSizePrefixedRootAsMonster(buf, offset).UnPack() + } else { + monster = example.GetRootAsMonster(buf, offset).UnPack() + } if got := monster.Hp; 80 != got { fail(FailString("hp", 80, got)) @@ -1183,7 +1215,7 @@ func CheckGetRootAsForNonRootTable(fail func(string, ...interface{})) { } // CheckGeneratedBuild uses generated code to build the example Monster. -func CheckGeneratedBuild(fail func(string, ...interface{})) ([]byte, flatbuffers.UOffsetT) { +func CheckGeneratedBuild(sizePrefix bool, fail func(string, ...interface{})) ([]byte, flatbuffers.UOffsetT) { b := flatbuffers.NewBuilder(0) str := b.CreateString("MyMonster") test1 := b.CreateString("test1") @@ -1227,7 +1259,11 @@ func CheckGeneratedBuild(fail func(string, ...interface{})) ([]byte, flatbuffers example.MonsterAddTestarrayofstring(b, testArrayOfString) mon := example.MonsterEnd(b) - b.Finish(mon) + if sizePrefix { + b.FinishSizePrefixed(mon) + } else { + b.Finish(mon) + } return b.Bytes, b.Head() } @@ -1626,6 +1662,27 @@ func CheckParentNamespace(fail func(string, ...interface{})) { } } +func CheckSizePrefixedBuffer(fail func(string, ...interface{})) { + // Generate a size-prefixed flatbuffer + generated, off := CheckGeneratedBuild(true, fail) + + // Check that the size prefix is the size of monsterdata_go_wire.mon minus 4 + size := flatbuffers.GetSizePrefix(generated, off) + if size != 220 { + fail("mismatch between size prefix and expected size") + } + + // Check that the buffer can be used as expected + CheckReadBuffer(generated, off, true, fail) + CheckMutateBuffer(generated, off, true, fail) + CheckObjectAPI(generated, off, true, fail) + + // Write generated bfufer out to a file + if err := ioutil.WriteFile(outData+".sp", generated[off:], os.FileMode(0644)); err != nil { + fail("failed to write file: %s", err) + } +} + // 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 @@ -1835,7 +1892,7 @@ func BenchmarkVtableDeduplication(b *testing.B) { // BenchmarkParseGold measures the speed of parsing the 'gold' data // used throughout this test suite. func BenchmarkParseGold(b *testing.B) { - buf, offset := CheckGeneratedBuild(b.Fatalf) + buf, offset := CheckGeneratedBuild(false, b.Fatalf) monster := example.GetRootAsMonster(buf, offset) // use these to prevent allocations: @@ -1897,7 +1954,7 @@ func BenchmarkParseGold(b *testing.B) { // BenchmarkBuildGold uses generated code to build the example Monster. func BenchmarkBuildGold(b *testing.B) { - buf, offset := CheckGeneratedBuild(b.Fatalf) + buf, offset := CheckGeneratedBuild(false, b.Fatalf) bytes_length := int64(len(buf[offset:])) reuse_str := "MyMonster" diff --git a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go index e719e90ba..378264567 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go +++ b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go @@ -39,6 +39,13 @@ func GetRootAsTableInNestedNS(buf []byte, offset flatbuffers.UOffsetT) *TableInN return x } +func GetSizePrefixedRootAsTableInNestedNS(buf []byte, offset flatbuffers.UOffsetT) *TableInNestedNS { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &TableInNestedNS{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *TableInNestedNS) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/tests/namespace_test/NamespaceA/SecondTableInA.go b/tests/namespace_test/NamespaceA/SecondTableInA.go index b50c8b684..f88a682c5 100644 --- a/tests/namespace_test/NamespaceA/SecondTableInA.go +++ b/tests/namespace_test/NamespaceA/SecondTableInA.go @@ -42,6 +42,13 @@ func GetRootAsSecondTableInA(buf []byte, offset flatbuffers.UOffsetT) *SecondTab return x } +func GetSizePrefixedRootAsSecondTableInA(buf []byte, offset flatbuffers.UOffsetT) *SecondTableInA { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &SecondTableInA{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *SecondTableInA) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/tests/namespace_test/NamespaceA/TableInFirstNS.go b/tests/namespace_test/NamespaceA/TableInFirstNS.go index 781198e5b..7f419b8c1 100644 --- a/tests/namespace_test/NamespaceA/TableInFirstNS.go +++ b/tests/namespace_test/NamespaceA/TableInFirstNS.go @@ -49,6 +49,13 @@ func GetRootAsTableInFirstNS(buf []byte, offset flatbuffers.UOffsetT) *TableInFi return x } +func GetSizePrefixedRootAsTableInFirstNS(buf []byte, offset flatbuffers.UOffsetT) *TableInFirstNS { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &TableInFirstNS{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *TableInFirstNS) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i diff --git a/tests/namespace_test/NamespaceC/TableInC.go b/tests/namespace_test/NamespaceC/TableInC.go index 88444a323..f17163427 100644 --- a/tests/namespace_test/NamespaceC/TableInC.go +++ b/tests/namespace_test/NamespaceC/TableInC.go @@ -46,6 +46,13 @@ func GetRootAsTableInC(buf []byte, offset flatbuffers.UOffsetT) *TableInC { return x } +func GetSizePrefixedRootAsTableInC(buf []byte, offset flatbuffers.UOffsetT) *TableInC { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &TableInC{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + func (rcv *TableInC) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i