}
const fileIdentifierLength = 4
+const sizePrefixLength = 4
// NewBuilder initializes a Builder of size `initial_size`.
// The internal buffer is grown as needed.
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
}
// 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:
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)
// 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:
if err != nil {
t.Fatal(err)
}
- CheckReadBuffer(monsterDataJava, 0, t.Fatalf)
+ CheckReadBuffer(monsterDataJava, 0, false, t.Fatalf)
CheckByteEquality(generated[off:], monsterDataJava, t.Fatalf)
}
// 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))
// 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 {
// 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")
// 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)
}
}
-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))
}
// 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")
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()
}
}
}
+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
// 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:
// 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"