/* * 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. */ package main import ( mygame "MyGame" // refers to generated code example "MyGame/Example" // refers to generated code "bytes" "flag" "fmt" "io/ioutil" "os" "reflect" "sort" "testing" flatbuffers "github.com/google/flatbuffers/go" ) var ( cppData, javaData, outData string fuzz bool fuzzFields, fuzzObjects int ) func init() { flag.StringVar(&cppData, "cpp_data", "", "location of monsterdata_test.mon to verify against (required)") flag.StringVar(&javaData, "java_data", "", "location of monsterdata_java_wire.mon to verify against (optional)") flag.StringVar(&outData, "out_data", "", "location to write generated Go data") flag.BoolVar(&fuzz, "fuzz", false, "perform fuzzing") flag.IntVar(&fuzzFields, "fuzz_fields", 4, "fields per fuzzer object") flag.IntVar(&fuzzObjects, "fuzz_objects", 10000, "number of fuzzer objects (higher is slower and more thorough") flag.Parse() if cppData == "" { fmt.Fprintf(os.Stderr, "cpp_data argument is required\n") os.Exit(1) } } // Store specific byte patterns in these variables for the fuzzer. These // values are taken verbatim from the C++ function FuzzTest1. var ( overflowingInt32Val = flatbuffers.GetInt32([]byte{0x83, 0x33, 0x33, 0x33}) overflowingInt64Val = flatbuffers.GetInt64([]byte{0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44}) ) // TestAll runs all checks, failing if any errors occur. func TestAll(t *testing.T) { // Verify that the Go FlatBuffers runtime library generates the // expected bytes (does not use any schema): CheckByteLayout(t.Fatalf) CheckMutateMethods(t.Fatalf) // Verify that panics are raised during exceptional conditions: CheckNotInObjectError(t.Fatalf) CheckStringIsNestedError(t.Fatalf) CheckByteStringIsNestedError(t.Fatalf) CheckStructIsNotInlineError(t.Fatalf) CheckFinishedBytesError(t.Fatalf) // 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: generated, off := CheckGeneratedBuild(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) // Verify that the buffer generated by C++ code is readable by the // generated Go code: monsterDataCpp, err := ioutil.ReadFile(cppData) if err != nil { t.Fatal(err) } CheckReadBuffer(monsterDataCpp, 0, t.Fatalf) CheckMutateBuffer(monsterDataCpp, 0, t.Fatalf) // Verify that vtables are deduplicated when written: CheckVtableDeduplication(t.Fatalf) // Verify the enum names CheckEnumNames(t.Fatalf) // Verify enum String methods CheckEnumString(t.Fatalf) // Verify the enum values maps CheckEnumValues(t.Fatalf) // Verify that the Go code used in FlatBuffers documentation passes // some sanity checks: CheckDocExample(generated, off, t.Fatalf) // Check Builder.CreateByteVector CheckCreateByteVector(t.Fatalf) // Check a parent namespace import CheckParentNamespace(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 javaData != "" { monsterDataJava, err := ioutil.ReadFile(javaData) if err != nil { t.Fatal(err) } CheckReadBuffer(monsterDataJava, 0, t.Fatalf) CheckByteEquality(generated[off:], monsterDataJava, t.Fatalf) } // Verify that various fuzzing scenarios produce a valid FlatBuffer. if fuzz { checkFuzz(fuzzFields, fuzzObjects, t.Fatalf) } // Write the generated buffer out to a file: err = ioutil.WriteFile(outData, generated[off:], os.FileMode(0644)) if err != nil { t.Fatal(err) } } // CheckReadBuffer checks that the given buffer is evaluated correctly // as the example Monster. func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string, ...interface{})) { // 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)) } if got := monster.Name(); !bytes.Equal([]byte("MyMonster"), got) { fail(FailString("name", "MyMonster", got)) } if got := monster.Color(); example.ColorBlue != got { fail(FailString("color", example.ColorBlue, got)) } if got := monster.Testbool(); true != got { fail(FailString("testbool", true, got)) } // 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") } // 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.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.Test2(); example.ColorGreen != got { fail(FailString("Pos.Test2", example.ColorGreen, got)) } // 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") } // 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 := 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 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)) } 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)) } 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)) } 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 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) 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.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)) } } } // 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{})) { // make a copy to mutate buf := make([]byte, len(org)) copy(buf, org) // load monster data from the buffer monster := example.GetRootAsMonster(buf, offset) // test case struct type testcase struct { field string testfn func() bool } testForOriginalValues := []testcase{ testcase{"Hp", func() bool { return monster.Hp() == 80 }}, testcase{"Mana", func() bool { return monster.Mana() == 150 }}, testcase{"Testbool", func() bool { return monster.Testbool() == true }}, testcase{"Pos.X'", func() bool { return monster.Pos(nil).X() == float32(1.0) }}, testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(2.0) }}, testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(3.0) }}, testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(3.0) }}, testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == example.ColorGreen }}, testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(5) }}, testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(6) }}, testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(2) }}, } testMutability := []testcase{ testcase{"Hp", func() bool { return monster.MutateHp(70) }}, testcase{"Mana", func() bool { return !monster.MutateMana(140) }}, testcase{"Testbool", func() bool { return monster.MutateTestbool(false) }}, testcase{"Pos.X", func() bool { return monster.Pos(nil).MutateX(10.0) }}, testcase{"Pos.Y", func() bool { return monster.Pos(nil).MutateY(20.0) }}, testcase{"Pos.Z", func() bool { return monster.Pos(nil).MutateZ(30.0) }}, testcase{"Pos.Test1", func() bool { return monster.Pos(nil).MutateTest1(30.0) }}, testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(example.ColorBlue) }}, testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).MutateA(50) }}, testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).MutateB(60) }}, testcase{"Inventory[2]", func() bool { return monster.MutateInventory(2, 200) }}, } testForMutatedValues := []testcase{ testcase{"Hp", func() bool { return monster.Hp() == 70 }}, testcase{"Mana", func() bool { return monster.Mana() == 150 }}, testcase{"Testbool", func() bool { return monster.Testbool() == false }}, testcase{"Pos.X'", func() bool { return monster.Pos(nil).X() == float32(10.0) }}, testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(20.0) }}, testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(30.0) }}, testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(30.0) }}, testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == example.ColorBlue }}, testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(50) }}, testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(60) }}, testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(200) }}, } testInvalidEnumValues := []testcase{ testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(example.Color(20)) }}, testcase{"Pos.Test2", func() bool { return monster.Pos(nil).Test2() == example.Color(20) }}, } // make sure original values are okay for _, t := range testForOriginalValues { if !t.testfn() { fail("field '" + t.field + "' doesn't have the expected original value") } } // try to mutate fields and check mutability for _, t := range testMutability { if !t.testfn() { fail(FailString("field '"+t.field+"' failed mutability test", true, false)) } } // test whether values have changed for _, t := range testForMutatedValues { if !t.testfn() { fail("field '" + t.field + "' doesn't have the expected mutated value") } } // make sure the buffer has changed if reflect.DeepEqual(buf, org) { fail("mutate buffer failed") } // To make sure the buffer has changed accordingly // Read data from the buffer and verify all fields monster = example.GetRootAsMonster(buf, offset) for _, t := range testForMutatedValues { if !t.testfn() { fail("field '" + t.field + "' doesn't have the expected mutated value") } } // a couple extra tests for "invalid" enum values, which don't correspond to // anything in the schema, but are allowed for _, t := range testInvalidEnumValues { if !t.testfn() { fail("field '" + t.field + "' doesn't work with an invalid enum value") } } // reverting all fields to original values should // re-create the original buffer. Mutate all fields // 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) monster.MutateHp(80) monster.MutateTestbool(true) monster.Pos(nil).MutateX(1.0) monster.Pos(nil).MutateY(2.0) monster.Pos(nil).MutateZ(3.0) monster.Pos(nil).MutateTest1(3.0) monster.Pos(nil).MutateTest2(example.ColorGreen) monster.Pos(nil).Test3(nil).MutateA(5) monster.Pos(nil).Test3(nil).MutateB(6) monster.MutateInventory(2, 2) for _, t := range testForOriginalValues { if !t.testfn() { fail("field '" + t.field + "' doesn't have the expected original value") } } // buffer should have original values if !reflect.DeepEqual(buf, org) { fail("revert changes failed") } } // Low level stress/fuzz test: serialize/deserialize a variety of // different kinds of data in different combinations func checkFuzz(fuzzFields, fuzzObjects int, fail func(string, ...interface{})) { // Values we're testing against: chosen to ensure no bits get chopped // off anywhere, and also be different from eachother. boolVal := true int8Val := int8(-127) // 0x81 uint8Val := uint8(0xFF) int16Val := int16(-32222) // 0x8222 uint16Val := uint16(0xFEEE) int32Val := int32(overflowingInt32Val) uint32Val := uint32(0xFDDDDDDD) int64Val := int64(overflowingInt64Val) uint64Val := uint64(0xFCCCCCCCCCCCCCCC) float32Val := float32(3.14159) float64Val := float64(3.14159265359) testValuesMax := 11 // hardcoded to the number of scalar types builder := flatbuffers.NewBuilder(0) l := NewLCG() objects := make([]flatbuffers.UOffsetT, fuzzObjects) // Generate fuzzObjects random objects each consisting of // fuzzFields fields, each of a random type. for i := 0; i < fuzzObjects; i++ { builder.StartObject(fuzzFields) for f := 0; f < fuzzFields; f++ { choice := l.Next() % uint32(testValuesMax) switch choice { case 0: builder.PrependBoolSlot(int(f), boolVal, false) case 1: builder.PrependInt8Slot(int(f), int8Val, 0) case 2: builder.PrependUint8Slot(int(f), uint8Val, 0) case 3: builder.PrependInt16Slot(int(f), int16Val, 0) case 4: builder.PrependUint16Slot(int(f), uint16Val, 0) case 5: builder.PrependInt32Slot(int(f), int32Val, 0) case 6: builder.PrependUint32Slot(int(f), uint32Val, 0) case 7: builder.PrependInt64Slot(int(f), int64Val, 0) case 8: builder.PrependUint64Slot(int(f), uint64Val, 0) case 9: builder.PrependFloat32Slot(int(f), float32Val, 0) case 10: builder.PrependFloat64Slot(int(f), float64Val, 0) } } off := builder.EndObject() // store the offset from the end of the builder buffer, // since it will keep growing: objects[i] = off } // Do some bookkeeping to generate stats on fuzzes: stats := map[string]int{} check := func(desc string, want, got interface{}) { stats[desc]++ if want != got { fail("%s want %v got %v", desc, want, got) } } l = NewLCG() // Reset. // 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 i := 0; i < fuzzObjects; i++ { table := &flatbuffers.Table{ Bytes: builder.Bytes, Pos: flatbuffers.UOffsetT(len(builder.Bytes)) - objects[i], } for j := 0; j < fuzzFields; j++ { f := flatbuffers.VOffsetT((flatbuffers.VtableMetadataFields + j) * flatbuffers.SizeVOffsetT) choice := l.Next() % uint32(testValuesMax) switch choice { case 0: check("bool", boolVal, table.GetBoolSlot(f, false)) case 1: check("int8", int8Val, table.GetInt8Slot(f, 0)) case 2: check("uint8", uint8Val, table.GetUint8Slot(f, 0)) case 3: check("int16", int16Val, table.GetInt16Slot(f, 0)) case 4: check("uint16", uint16Val, table.GetUint16Slot(f, 0)) case 5: check("int32", int32Val, table.GetInt32Slot(f, 0)) case 6: check("uint32", uint32Val, table.GetUint32Slot(f, 0)) case 7: check("int64", int64Val, table.GetInt64Slot(f, 0)) case 8: check("uint64", uint64Val, table.GetUint64Slot(f, 0)) case 9: check("float32", float32Val, table.GetFloat32Slot(f, 0)) case 10: check("float64", float64Val, table.GetFloat64Slot(f, 0)) } } } // If enough checks were made, verify that all scalar types were used: if fuzzFields*fuzzObjects >= testValuesMax { if len(stats) != testValuesMax { fail("fuzzing failed to test all scalar types") } } // Print some counts, if needed: if testing.Verbose() { if fuzzFields == 0 || fuzzObjects == 0 { fmt.Printf("fuzz\tfields: %d\tobjects: %d\t[none]\t%d\n", fuzzFields, fuzzObjects, 0) } else { keys := make([]string, 0, len(stats)) for k := range stats { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { fmt.Printf("fuzz\tfields: %d\tobjects: %d\t%s\t%d\n", fuzzFields, fuzzObjects, k, stats[k]) } } } return } // FailString makes a message for when expectations differ from reality. func FailString(name string, want, got interface{}) string { return fmt.Sprintf("bad %s: want %#v got %#v", name, want, got) } // CheckByteLayout verifies the bytes of a Builder in various scenarios. func CheckByteLayout(fail func(string, ...interface{})) { var b *flatbuffers.Builder var i int check := func(want []byte) { i++ got := b.Bytes[b.Head():] if !bytes.Equal(want, got) { fail("case %d: want\n%v\nbut got\n%v\n", i, want, got) } } // test 1: numbers b = flatbuffers.NewBuilder(0) check([]byte{}) b.PrependBool(true) check([]byte{1}) b.PrependInt8(-127) check([]byte{129, 1}) b.PrependUint8(255) check([]byte{255, 129, 1}) b.PrependInt16(-32222) check([]byte{0x22, 0x82, 0, 255, 129, 1}) // first pad b.PrependUint16(0xFEEE) check([]byte{0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1}) // no pad this time b.PrependInt32(-53687092) check([]byte{204, 204, 204, 252, 0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1}) b.PrependUint32(0x98765432) check([]byte{0x32, 0x54, 0x76, 0x98, 204, 204, 204, 252, 0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1}) // test 1b: numbers 2 b = flatbuffers.NewBuilder(0) b.PrependUint64(0x1122334455667788) check([]byte{0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11}) // test 2: 1xbyte vector b = flatbuffers.NewBuilder(0) check([]byte{}) b.StartVector(flatbuffers.SizeByte, 1, 1) check([]byte{0, 0, 0}) // align to 4bytes b.PrependByte(1) check([]byte{1, 0, 0, 0}) b.EndVector(1) check([]byte{1, 0, 0, 0, 1, 0, 0, 0}) // padding // test 3: 2xbyte vector b = flatbuffers.NewBuilder(0) b.StartVector(flatbuffers.SizeByte, 2, 1) check([]byte{0, 0}) // align to 4bytes b.PrependByte(1) check([]byte{1, 0, 0}) b.PrependByte(2) check([]byte{2, 1, 0, 0}) b.EndVector(2) check([]byte{2, 0, 0, 0, 2, 1, 0, 0}) // padding // test 3b: 11xbyte vector matches builder size b = flatbuffers.NewBuilder(12) b.StartVector(flatbuffers.SizeByte, 8, 1) start := []byte{} check(start) for i := 1; i < 12; i++ { b.PrependByte(byte(i)) start = append([]byte{byte(i)}, start...) check(start) } b.EndVector(8) check(append([]byte{8, 0, 0, 0}, start...)) // test 4: 1xuint16 vector b = flatbuffers.NewBuilder(0) b.StartVector(flatbuffers.SizeUint16, 1, 1) check([]byte{0, 0}) // align to 4bytes b.PrependUint16(1) check([]byte{1, 0, 0, 0}) b.EndVector(1) check([]byte{1, 0, 0, 0, 1, 0, 0, 0}) // padding // test 5: 2xuint16 vector b = flatbuffers.NewBuilder(0) b.StartVector(flatbuffers.SizeUint16, 2, 1) check([]byte{}) // align to 4bytes b.PrependUint16(0xABCD) check([]byte{0xCD, 0xAB}) b.PrependUint16(0xDCBA) check([]byte{0xBA, 0xDC, 0xCD, 0xAB}) b.EndVector(2) check([]byte{2, 0, 0, 0, 0xBA, 0xDC, 0xCD, 0xAB}) // test 6: CreateString b = flatbuffers.NewBuilder(0) b.CreateString("foo") check([]byte{3, 0, 0, 0, 'f', 'o', 'o', 0}) // 0-terminated, no pad b.CreateString("moop") check([]byte{4, 0, 0, 0, 'm', 'o', 'o', 'p', 0, 0, 0, 0, // 0-terminated, 3-byte pad 3, 0, 0, 0, 'f', 'o', 'o', 0}) // test 6b: CreateString unicode b = flatbuffers.NewBuilder(0) // These characters are chinese from blog.golang.org/strings // We use escape codes here so that editors without unicode support // aren't bothered: uni_str := "\u65e5\u672c\u8a9e" b.CreateString(uni_str) check([]byte{9, 0, 0, 0, 230, 151, 165, 230, 156, 172, 232, 170, 158, 0, // null-terminated, 2-byte pad 0, 0}) // test 6c: CreateByteString b = flatbuffers.NewBuilder(0) b.CreateByteString([]byte("foo")) check([]byte{3, 0, 0, 0, 'f', 'o', 'o', 0}) // 0-terminated, no pad b.CreateByteString([]byte("moop")) check([]byte{4, 0, 0, 0, 'm', 'o', 'o', 'p', 0, 0, 0, 0, // 0-terminated, 3-byte pad 3, 0, 0, 0, 'f', 'o', 'o', 0}) // test 7: empty vtable b = flatbuffers.NewBuilder(0) b.StartObject(0) check([]byte{}) b.EndObject() check([]byte{4, 0, 4, 0, 4, 0, 0, 0}) // test 8: vtable with one true bool b = flatbuffers.NewBuilder(0) check([]byte{}) b.StartObject(1) check([]byte{}) b.PrependBoolSlot(0, true, false) b.EndObject() check([]byte{ 6, 0, // vtable bytes 8, 0, // length of object including vtable offset 7, 0, // start of bool value 6, 0, 0, 0, // offset for start of vtable (int32) 0, 0, 0, // padded to 4 bytes 1, // bool value }) // test 9: vtable with one default bool b = flatbuffers.NewBuilder(0) check([]byte{}) b.StartObject(1) check([]byte{}) b.PrependBoolSlot(0, false, false) b.EndObject() check([]byte{ 4, 0, // vtable bytes 4, 0, // end of object from here // entry 1 is zero and not stored. 4, 0, 0, 0, // offset for start of vtable (int32) }) // test 10: vtable with one int16 b = flatbuffers.NewBuilder(0) b.StartObject(1) b.PrependInt16Slot(0, 0x789A, 0) b.EndObject() check([]byte{ 6, 0, // vtable bytes 8, 0, // end of object from here 6, 0, // offset to value 6, 0, 0, 0, // offset for start of vtable (int32) 0, 0, // padding to 4 bytes 0x9A, 0x78, }) // test 11: vtable with two int16 b = flatbuffers.NewBuilder(0) b.StartObject(2) b.PrependInt16Slot(0, 0x3456, 0) b.PrependInt16Slot(1, 0x789A, 0) b.EndObject() check([]byte{ 8, 0, // vtable bytes 8, 0, // end of object from here 6, 0, // offset to value 0 4, 0, // offset to value 1 8, 0, 0, 0, // offset for start of vtable (int32) 0x9A, 0x78, // value 1 0x56, 0x34, // value 0 }) // test 12: vtable with int16 and bool b = flatbuffers.NewBuilder(0) b.StartObject(2) b.PrependInt16Slot(0, 0x3456, 0) b.PrependBoolSlot(1, true, false) b.EndObject() check([]byte{ 8, 0, // vtable bytes 8, 0, // end of object from here 6, 0, // offset to value 0 5, 0, // offset to value 1 8, 0, 0, 0, // offset for start of vtable (int32) 0, // padding 1, // value 1 0x56, 0x34, // value 0 }) // test 12: vtable with empty vector b = flatbuffers.NewBuilder(0) b.StartVector(flatbuffers.SizeByte, 0, 1) vecend := b.EndVector(0) b.StartObject(1) b.PrependUOffsetTSlot(0, vecend, 0) b.EndObject() check([]byte{ 6, 0, // vtable bytes 8, 0, 4, 0, // offset to vector offset 6, 0, 0, 0, // offset for start of vtable (int32) 4, 0, 0, 0, 0, 0, 0, 0, // length of vector (not in struct) }) // test 12b: vtable with empty vector of byte and some scalars b = flatbuffers.NewBuilder(0) b.StartVector(flatbuffers.SizeByte, 0, 1) vecend = b.EndVector(0) b.StartObject(2) b.PrependInt16Slot(0, 55, 0) b.PrependUOffsetTSlot(1, vecend, 0) b.EndObject() check([]byte{ 8, 0, // vtable bytes 12, 0, 10, 0, // offset to value 0 4, 0, // offset to vector offset 8, 0, 0, 0, // vtable loc 8, 0, 0, 0, // value 1 0, 0, 55, 0, // value 0 0, 0, 0, 0, // length of vector (not in struct) }) // test 13: vtable with 1 int16 and 2-vector of int16 b = flatbuffers.NewBuilder(0) b.StartVector(flatbuffers.SizeInt16, 2, 1) b.PrependInt16(0x1234) b.PrependInt16(0x5678) vecend = b.EndVector(2) b.StartObject(2) b.PrependUOffsetTSlot(1, vecend, 0) b.PrependInt16Slot(0, 55, 0) b.EndObject() check([]byte{ 8, 0, // vtable bytes 12, 0, // length of object 6, 0, // start of value 0 from end of vtable 8, 0, // start of value 1 from end of buffer 8, 0, 0, 0, // offset for start of vtable (int32) 0, 0, // padding 55, 0, // value 0 4, 0, 0, 0, // vector position from here 2, 0, 0, 0, // length of vector (uint32) 0x78, 0x56, // vector value 1 0x34, 0x12, // vector value 0 }) // test 14: vtable with 1 struct of 1 int8, 1 int16, 1 int32 b = flatbuffers.NewBuilder(0) b.StartObject(1) b.Prep(4+4+4, 0) b.PrependInt8(55) b.Pad(3) b.PrependInt16(0x1234) b.Pad(2) b.PrependInt32(0x12345678) structStart := b.Offset() b.PrependStructSlot(0, structStart, 0) b.EndObject() check([]byte{ 6, 0, // vtable bytes 16, 0, // end of object from here 4, 0, // start of struct from here 6, 0, 0, 0, // offset for start of vtable (int32) 0x78, 0x56, 0x34, 0x12, // value 2 0, 0, // padding 0x34, 0x12, // value 1 0, 0, 0, // padding 55, // value 0 }) // test 15: vtable with 1 vector of 2 struct of 2 int8 b = flatbuffers.NewBuilder(0) b.StartVector(flatbuffers.SizeInt8*2, 2, 1) b.PrependInt8(33) b.PrependInt8(44) b.PrependInt8(55) b.PrependInt8(66) vecend = b.EndVector(2) b.StartObject(1) b.PrependUOffsetTSlot(0, vecend, 0) b.EndObject() check([]byte{ 6, 0, // vtable bytes 8, 0, 4, 0, // offset of vector offset 6, 0, 0, 0, // offset for start of vtable (int32) 4, 0, 0, 0, // vector start offset 2, 0, 0, 0, // vector length 66, // vector value 1,1 55, // vector value 1,0 44, // vector value 0,1 33, // vector value 0,0 }) // test 16: table with some elements b = flatbuffers.NewBuilder(0) b.StartObject(2) b.PrependInt8Slot(0, 33, 0) b.PrependInt16Slot(1, 66, 0) off := b.EndObject() b.Finish(off) check([]byte{ 12, 0, 0, 0, // root of table: points to vtable offset 8, 0, // vtable bytes 8, 0, // end of object from here 7, 0, // start of value 0 4, 0, // start of value 1 8, 0, 0, 0, // offset for start of vtable (int32) 66, 0, // value 1 0, // padding 33, // value 0 }) // test 17: one unfinished table and one finished table b = flatbuffers.NewBuilder(0) b.StartObject(2) b.PrependInt8Slot(0, 33, 0) b.PrependInt8Slot(1, 44, 0) off = b.EndObject() b.Finish(off) b.StartObject(3) b.PrependInt8Slot(0, 55, 0) b.PrependInt8Slot(1, 66, 0) b.PrependInt8Slot(2, 77, 0) off = b.EndObject() b.Finish(off) check([]byte{ 16, 0, 0, 0, // root of table: points to object 0, 0, // padding 10, 0, // vtable bytes 8, 0, // size of object 7, 0, // start of value 0 6, 0, // start of value 1 5, 0, // start of value 2 10, 0, 0, 0, // offset for start of vtable (int32) 0, // padding 77, // value 2 66, // value 1 55, // value 0 12, 0, 0, 0, // root of table: points to object 8, 0, // vtable bytes 8, 0, // size of object 7, 0, // start of value 0 6, 0, // start of value 1 8, 0, 0, 0, // offset for start of vtable (int32) 0, 0, // padding 44, // value 1 33, // value 0 }) // test 18: a bunch of bools b = flatbuffers.NewBuilder(0) b.StartObject(8) b.PrependBoolSlot(0, true, false) b.PrependBoolSlot(1, true, false) b.PrependBoolSlot(2, true, false) b.PrependBoolSlot(3, true, false) b.PrependBoolSlot(4, true, false) b.PrependBoolSlot(5, true, false) b.PrependBoolSlot(6, true, false) b.PrependBoolSlot(7, true, false) off = b.EndObject() b.Finish(off) check([]byte{ 24, 0, 0, 0, // root of table: points to vtable offset 20, 0, // vtable bytes 12, 0, // size of object 11, 0, // start of value 0 10, 0, // start of value 1 9, 0, // start of value 2 8, 0, // start of value 3 7, 0, // start of value 4 6, 0, // start of value 5 5, 0, // start of value 6 4, 0, // start of value 7 20, 0, 0, 0, // vtable offset 1, // value 7 1, // value 6 1, // value 5 1, // value 4 1, // value 3 1, // value 2 1, // value 1 1, // value 0 }) // test 19: three bools b = flatbuffers.NewBuilder(0) b.StartObject(3) b.PrependBoolSlot(0, true, false) b.PrependBoolSlot(1, true, false) b.PrependBoolSlot(2, true, false) off = b.EndObject() b.Finish(off) check([]byte{ 16, 0, 0, 0, // root of table: points to vtable offset 0, 0, // padding 10, 0, // vtable bytes 8, 0, // size of object 7, 0, // start of value 0 6, 0, // start of value 1 5, 0, // start of value 2 10, 0, 0, 0, // vtable offset from here 0, // padding 1, // value 2 1, // value 1 1, // value 0 }) // test 20: some floats b = flatbuffers.NewBuilder(0) b.StartObject(1) b.PrependFloat32Slot(0, 1.0, 0.0) off = b.EndObject() check([]byte{ 6, 0, // vtable bytes 8, 0, // size of object 4, 0, // start of value 0 6, 0, 0, 0, // vtable offset 0, 0, 128, 63, // value 0 }) } // CheckManualBuild builds a Monster manually. func CheckManualBuild(fail func(string, ...interface{})) ([]byte, flatbuffers.UOffsetT) { b := flatbuffers.NewBuilder(0) str := b.CreateString("MyMonster") b.StartVector(1, 5, 1) b.PrependByte(4) b.PrependByte(3) b.PrependByte(2) b.PrependByte(1) b.PrependByte(0) inv := b.EndVector(5) b.StartObject(13) b.PrependInt16Slot(2, 20, 100) mon2 := b.EndObject() // Test4Vector b.StartVector(4, 2, 1) // Test 0 b.Prep(2, 4) b.Pad(1) b.PlaceInt8(20) b.PlaceInt16(10) // Test 1 b.Prep(2, 4) b.Pad(1) b.PlaceInt8(40) b.PlaceInt16(30) // end testvector test4 := b.EndVector(2) b.StartObject(13) // a vec3 b.Prep(16, 32) b.Pad(2) b.Prep(2, 4) b.Pad(1) b.PlaceByte(6) b.PlaceInt16(5) b.Pad(1) b.PlaceByte(4) b.PlaceFloat64(3.0) b.Pad(4) b.PlaceFloat32(3.0) b.PlaceFloat32(2.0) b.PlaceFloat32(1.0) vec3Loc := b.Offset() // end vec3 b.PrependStructSlot(0, vec3Loc, 0) // vec3. noop b.PrependInt16Slot(2, 80, 100) // hp b.PrependUOffsetTSlot(3, str, 0) b.PrependUOffsetTSlot(5, inv, 0) // inventory b.PrependByteSlot(7, 1, 0) b.PrependUOffsetTSlot(8, mon2, 0) b.PrependUOffsetTSlot(9, test4, 0) mon := b.EndObject() b.Finish(mon) return b.Bytes, b.Head() } func CheckGetRootAsForNonRootTable(fail func(string, ...interface{})) { b := flatbuffers.NewBuilder(0) str := b.CreateString("MyStat") example.StatStart(b) example.StatAddId(b, str) example.StatAddVal(b, 12345678) example.StatAddCount(b, 12345) stat_end := example.StatEnd(b) b.Finish(stat_end) stat := example.GetRootAsStat(b.Bytes, b.Head()) if got := stat.Id(); !bytes.Equal([]byte("MyStat"), got) { fail(FailString("stat.Id()", "MyStat", got)) } if got := stat.Val(); 12345678 != got { fail(FailString("stat.Val()", 12345678, got)) } if got := stat.Count(); 12345 != got { fail(FailString("stat.Count()", 12345, got)) } } // CheckGeneratedBuild uses generated code to build the example Monster. func CheckGeneratedBuild(fail func(string, ...interface{})) ([]byte, flatbuffers.UOffsetT) { b := flatbuffers.NewBuilder(0) str := b.CreateString("MyMonster") test1 := b.CreateString("test1") test2 := b.CreateString("test2") fred := b.CreateString("Fred") example.MonsterStartInventoryVector(b, 5) b.PrependByte(4) b.PrependByte(3) b.PrependByte(2) b.PrependByte(1) b.PrependByte(0) inv := b.EndVector(5) example.MonsterStart(b) example.MonsterAddName(b, fred) mon2 := example.MonsterEnd(b) example.MonsterStartTest4Vector(b, 2) example.CreateTest(b, 10, 20) example.CreateTest(b, 30, 40) test4 := b.EndVector(2) example.MonsterStartTestarrayofstringVector(b, 2) b.PrependUOffsetT(test2) b.PrependUOffsetT(test1) testArrayOfString := b.EndVector(2) example.MonsterStart(b) pos := example.CreateVec3(b, 1.0, 2.0, 3.0, 3.0, example.ColorGreen, 5, 6) example.MonsterAddPos(b, pos) example.MonsterAddHp(b, 80) example.MonsterAddName(b, str) example.MonsterAddTestbool(b, true) example.MonsterAddInventory(b, inv) example.MonsterAddTestType(b, 1) example.MonsterAddTest(b, mon2) example.MonsterAddTest4(b, test4) example.MonsterAddTestarrayofstring(b, testArrayOfString) mon := example.MonsterEnd(b) b.Finish(mon) 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) b.StartObject(4) b.PrependByteSlot(0, 0, 0) b.PrependByteSlot(1, 11, 0) b.PrependByteSlot(2, 22, 0) b.PrependInt16Slot(3, 33, 0) obj0 := b.EndObject() b.StartObject(4) b.PrependByteSlot(0, 0, 0) b.PrependByteSlot(1, 44, 0) b.PrependByteSlot(2, 55, 0) b.PrependInt16Slot(3, 66, 0) obj1 := b.EndObject() b.StartObject(4) b.PrependByteSlot(0, 0, 0) b.PrependByteSlot(1, 77, 0) b.PrependByteSlot(2, 88, 0) b.PrependInt16Slot(3, 99, 0) obj2 := b.EndObject() got := b.Bytes[b.Head():] want := []byte{ 240, 255, 255, 255, // == -12. offset to dedupped vtable. 99, 0, 88, 77, 248, 255, 255, 255, // == -8. offset to dedupped vtable. 66, 0, 55, 44, 12, 0, 8, 0, 0, 0, 7, 0, 6, 0, 4, 0, 12, 0, 0, 0, 33, 0, 22, 11, } if !bytes.Equal(want, got) { fail("testVtableDeduplication want:\n%d %v\nbut got:\n%d %v\n", len(want), want, len(got), got) } table0 := &flatbuffers.Table{Bytes: b.Bytes, Pos: flatbuffers.UOffsetT(len(b.Bytes)) - obj0} table1 := &flatbuffers.Table{Bytes: b.Bytes, Pos: flatbuffers.UOffsetT(len(b.Bytes)) - obj1} table2 := &flatbuffers.Table{Bytes: b.Bytes, Pos: flatbuffers.UOffsetT(len(b.Bytes)) - obj2} testTable := func(tab *flatbuffers.Table, a flatbuffers.VOffsetT, b, c, d byte) { // vtable size if got := tab.GetVOffsetTSlot(0, 0); 12 != got { fail("failed 0, 0: %d", got) } // object size if got := tab.GetVOffsetTSlot(2, 0); 8 != got { fail("failed 2, 0: %d", got) } // default value if got := tab.GetVOffsetTSlot(4, 0); a != got { fail("failed 4, 0: %d", got) } if got := tab.GetByteSlot(6, 0); b != got { fail("failed 6, 0: %d", got) } if val := tab.GetByteSlot(8, 0); c != val { fail("failed 8, 0: %d", got) } if got := tab.GetByteSlot(10, 0); d != got { fail("failed 10, 0: %d", got) } } testTable(table0, 0, 11, 22, 33) testTable(table1, 0, 44, 55, 66) testTable(table2, 0, 77, 88, 99) } // CheckNotInObjectError verifies that `EndObject` fails if not inside an // object. func CheckNotInObjectError(fail func(string, ...interface{})) { b := flatbuffers.NewBuilder(0) defer func() { r := recover() if r == nil { fail("expected panic in CheckNotInObjectError") } }() b.EndObject() } // CheckStringIsNestedError verifies that a string can not be created inside // another object. func CheckStringIsNestedError(fail func(string, ...interface{})) { b := flatbuffers.NewBuilder(0) b.StartObject(0) defer func() { r := recover() if r == nil { fail("expected panic in CheckStringIsNestedError") } }() b.CreateString("foo") } // CheckByteStringIsNestedError verifies that a bytestring can not be created // inside another object. func CheckByteStringIsNestedError(fail func(string, ...interface{})) { b := flatbuffers.NewBuilder(0) b.StartObject(0) defer func() { r := recover() if r == nil { fail("expected panic in CheckByteStringIsNestedError") } }() b.CreateByteString([]byte("foo")) } // CheckStructIsNotInlineError verifies that writing a struct in a location // away from where it is used will cause a panic. func CheckStructIsNotInlineError(fail func(string, ...interface{})) { b := flatbuffers.NewBuilder(0) b.StartObject(0) defer func() { r := recover() if r == nil { fail("expected panic in CheckStructIsNotInlineError") } }() b.PrependStructSlot(0, 1, 0) } // CheckFinishedBytesError verifies that `FinishedBytes` panics if the table // is not finished. func CheckFinishedBytesError(fail func(string, ...interface{})) { b := flatbuffers.NewBuilder(0) defer func() { r := recover() if r == nil { fail("expected panic in CheckFinishedBytesError") } }() b.FinishedBytes() } // CheckEnumNames checks that the generated enum names are correct. func CheckEnumNames(fail func(string, ...interface{})) { { want := map[example.Any]string{ example.AnyNONE: "NONE", example.AnyMonster: "Monster", example.AnyTestSimpleTableWithEnum: "TestSimpleTableWithEnum", example.AnyMyGame_Example2_Monster: "MyGame_Example2_Monster", } got := example.EnumNamesAny if !reflect.DeepEqual(got, want) { fail("enum name is not equal") } } { want := map[example.Color]string{ example.ColorRed: "Red", example.ColorGreen: "Green", example.ColorBlue: "Blue", } got := example.EnumNamesColor if !reflect.DeepEqual(got, want) { fail("enum name is not equal") } } } // CheckEnumString checks the String method on generated enum types. func CheckEnumString(fail func(string, ...interface{})) { if got := example.AnyMonster.String(); got != "Monster" { fail("Monster.String: %q != %q", got, "Monster") } if got := fmt.Sprintf("color: %s", example.ColorGreen); got != "color: Green" { fail("color.String: %q != %q", got, "color: Green") } } // CheckEnumValues checks that the generated enum values maps are correct. func CheckEnumValues(fail func(string, ...interface{})) { { want := map[string]example.Any{ "NONE": example.AnyNONE, "Monster": example.AnyMonster, "TestSimpleTableWithEnum": example.AnyTestSimpleTableWithEnum, "MyGame_Example2_Monster": example.AnyMyGame_Example2_Monster, } got := example.EnumValuesAny if !reflect.DeepEqual(got, want) { fail("enum name is not equal") } } { want := map[string]example.Color{ "Red": example.ColorRed, "Green": example.ColorGreen, "Blue": example.ColorBlue, } got := example.EnumValuesColor if !reflect.DeepEqual(got, want) { fail("enum name is not equal") } } } // CheckDocExample checks that the code given in FlatBuffers documentation // is syntactically correct. func CheckDocExample(buf []byte, off flatbuffers.UOffsetT, fail func(string, ...interface{})) { monster := example.GetRootAsMonster(buf, off) _ = monster.Hp() _ = monster.Pos(nil) for i := 0; i < monster.InventoryLength(); i++ { _ = monster.Inventory(i) // do something here } builder := flatbuffers.NewBuilder(0) example.MonsterStartInventoryVector(builder, 5) for i := 4; i >= 0; i-- { builder.PrependByte(byte(i)) } inv := builder.EndVector(5) str := builder.CreateString("MyMonster") example.MonsterStart(builder) example.MonsterAddPos(builder, example.CreateVec3(builder, 1.0, 2.0, 3.0, 3.0, example.Color(4), 5, 6)) example.MonsterAddHp(builder, 80) example.MonsterAddName(builder, str) example.MonsterAddInventory(builder, inv) example.MonsterAddTestType(builder, 1) example.MonsterAddColor(builder, example.ColorRed) // example.MonsterAddTest(builder, mon2) // example.MonsterAddTest4(builder, test4s) _ = example.MonsterEnd(builder) } func CheckCreateByteVector(fail func(string, ...interface{})) { raw := [30]byte{} for i := 0; i < len(raw); i++ { raw[i] = byte(i) } for size := 0; size < len(raw); size++ { b1 := flatbuffers.NewBuilder(0) b2 := flatbuffers.NewBuilder(0) b1.StartVector(1, size, 1) for i := size - 1; i >= 0; i-- { b1.PrependByte(raw[i]) } b1.EndVector(size) b2.CreateByteVector(raw[:size]) CheckByteEquality(b1.Bytes, b2.Bytes, fail) } } func CheckParentNamespace(fail func(string, ...interface{})) { var empty, nonempty []byte // create monster with an empty parent namespace field { builder := flatbuffers.NewBuilder(0) example.MonsterStart(builder) m := example.MonsterEnd(builder) builder.Finish(m) empty = make([]byte, len(builder.FinishedBytes())) copy(empty, builder.FinishedBytes()) } // create monster with a non-empty parent namespace field { builder := flatbuffers.NewBuilder(0) mygame.InParentNamespaceStart(builder) pn := mygame.InParentNamespaceEnd(builder) example.MonsterStart(builder) example.MonsterAddParentNamespaceTest(builder, pn) m := example.MonsterEnd(builder) builder.Finish(m) nonempty = make([]byte, len(builder.FinishedBytes())) copy(nonempty, builder.FinishedBytes()) } // read monster with empty parent namespace field { m := example.GetRootAsMonster(empty, 0) if m.ParentNamespaceTest(nil) != nil { fail("expected nil ParentNamespaceTest for empty field") } } // read monster with non-empty parent namespace field { m := example.GetRootAsMonster(nonempty, 0) if m.ParentNamespaceTest(nil) == nil { fail("expected non-nil ParentNamespaceTest for non-empty field") } } } // 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 type LCG uint32 const InitialLCGSeed = 48271 func NewLCG() *LCG { n := uint32(InitialLCGSeed) l := LCG(n) return &l } func (lcg *LCG) Reset() { *lcg = InitialLCGSeed } func (lcg *LCG) Next() uint32 { n := uint32((uint64(*lcg) * uint64(279470273)) % uint64(4294967291)) *lcg = LCG(n) return n } // CheckByteEquality verifies that two byte buffers are the same. func CheckByteEquality(a, b []byte, fail func(string, ...interface{})) { if !bytes.Equal(a, b) { fail("objects are not byte-wise equal") } } // CheckMutateMethods checks all mutate methods one by one func CheckMutateMethods(fail func(string, ...interface{})) { b := flatbuffers.NewBuilder(0) b.StartObject(15) b.PrependBoolSlot(0, true, false) b.PrependByteSlot(1, 1, 0) b.PrependUint8Slot(2, 2, 0) b.PrependUint16Slot(3, 3, 0) b.PrependUint32Slot(4, 4, 0) b.PrependUint64Slot(5, 5, 0) b.PrependInt8Slot(6, 6, 0) b.PrependInt16Slot(7, 7, 0) b.PrependInt32Slot(8, 8, 0) b.PrependInt64Slot(9, 9, 0) b.PrependFloat32Slot(10, 10, 0) b.PrependFloat64Slot(11, 11, 0) b.PrependUOffsetTSlot(12, 12, 0) uoVal := b.Offset() - 12 b.PrependVOffsetT(13) b.Slot(13) b.PrependSOffsetT(14) b.Slot(14) soVal := flatbuffers.SOffsetT(b.Offset() - 14) offset := b.EndObject() t := &flatbuffers.Table{ Bytes: b.Bytes, Pos: flatbuffers.UOffsetT(len(b.Bytes)) - offset, } calcVOffsetT := func(slot int) (vtableOffset flatbuffers.VOffsetT) { return flatbuffers.VOffsetT((flatbuffers.VtableMetadataFields + slot) * flatbuffers.SizeVOffsetT) } calcUOffsetT := func(vtableOffset flatbuffers.VOffsetT) (valueOffset flatbuffers.UOffsetT) { return t.Pos + flatbuffers.UOffsetT(t.Offset(vtableOffset)) } type testcase struct { field string testfn func() bool } testForOriginalValues := []testcase{ testcase{"BoolSlot", func() bool { return t.GetBoolSlot(calcVOffsetT(0), true) == true }}, testcase{"ByteSlot", func() bool { return t.GetByteSlot(calcVOffsetT(1), 1) == 1 }}, testcase{"Uint8Slot", func() bool { return t.GetUint8Slot(calcVOffsetT(2), 2) == 2 }}, testcase{"Uint16Slot", func() bool { return t.GetUint16Slot(calcVOffsetT(3), 3) == 3 }}, testcase{"Uint32Slot", func() bool { return t.GetUint32Slot(calcVOffsetT(4), 4) == 4 }}, testcase{"Uint64Slot", func() bool { return t.GetUint64Slot(calcVOffsetT(5), 5) == 5 }}, testcase{"Int8Slot", func() bool { return t.GetInt8Slot(calcVOffsetT(6), 6) == 6 }}, testcase{"Int16Slot", func() bool { return t.GetInt16Slot(calcVOffsetT(7), 7) == 7 }}, testcase{"Int32Slot", func() bool { return t.GetInt32Slot(calcVOffsetT(8), 8) == 8 }}, testcase{"Int64Slot", func() bool { return t.GetInt64Slot(calcVOffsetT(9), 9) == 9 }}, testcase{"Float32Slot", func() bool { return t.GetFloat32Slot(calcVOffsetT(10), 10) == 10 }}, testcase{"Float64Slot", func() bool { return t.GetFloat64Slot(calcVOffsetT(11), 11) == 11 }}, testcase{"UOffsetTSlot", func() bool { return t.GetUOffsetT(calcUOffsetT(calcVOffsetT(12))) == uoVal }}, testcase{"VOffsetTSlot", func() bool { return t.GetVOffsetT(calcUOffsetT(calcVOffsetT(13))) == 13 }}, testcase{"SOffsetTSlot", func() bool { return t.GetSOffsetT(calcUOffsetT(calcVOffsetT(14))) == soVal }}, } testMutability := []testcase{ testcase{"BoolSlot", func() bool { return t.MutateBoolSlot(calcVOffsetT(0), false) }}, testcase{"ByteSlot", func() bool { return t.MutateByteSlot(calcVOffsetT(1), 2) }}, testcase{"Uint8Slot", func() bool { return t.MutateUint8Slot(calcVOffsetT(2), 4) }}, testcase{"Uint16Slot", func() bool { return t.MutateUint16Slot(calcVOffsetT(3), 6) }}, testcase{"Uint32Slot", func() bool { return t.MutateUint32Slot(calcVOffsetT(4), 8) }}, testcase{"Uint64Slot", func() bool { return t.MutateUint64Slot(calcVOffsetT(5), 10) }}, testcase{"Int8Slot", func() bool { return t.MutateInt8Slot(calcVOffsetT(6), 12) }}, testcase{"Int16Slot", func() bool { return t.MutateInt16Slot(calcVOffsetT(7), 14) }}, testcase{"Int32Slot", func() bool { return t.MutateInt32Slot(calcVOffsetT(8), 16) }}, testcase{"Int64Slot", func() bool { return t.MutateInt64Slot(calcVOffsetT(9), 18) }}, testcase{"Float32Slot", func() bool { return t.MutateFloat32Slot(calcVOffsetT(10), 20) }}, testcase{"Float64Slot", func() bool { return t.MutateFloat64Slot(calcVOffsetT(11), 22) }}, testcase{"UOffsetTSlot", func() bool { return t.MutateUOffsetT(calcUOffsetT(calcVOffsetT(12)), 24) }}, testcase{"VOffsetTSlot", func() bool { return t.MutateVOffsetT(calcUOffsetT(calcVOffsetT(13)), 26) }}, testcase{"SOffsetTSlot", func() bool { return t.MutateSOffsetT(calcUOffsetT(calcVOffsetT(14)), 28) }}, } testMutabilityWithoutSlot := []testcase{ testcase{"BoolSlot", func() bool { return t.MutateBoolSlot(calcVOffsetT(16), false) }}, testcase{"ByteSlot", func() bool { return t.MutateByteSlot(calcVOffsetT(16), 2) }}, testcase{"Uint8Slot", func() bool { return t.MutateUint8Slot(calcVOffsetT(16), 2) }}, testcase{"Uint16Slot", func() bool { return t.MutateUint16Slot(calcVOffsetT(16), 2) }}, testcase{"Uint32Slot", func() bool { return t.MutateUint32Slot(calcVOffsetT(16), 2) }}, testcase{"Uint64Slot", func() bool { return t.MutateUint64Slot(calcVOffsetT(16), 2) }}, testcase{"Int8Slot", func() bool { return t.MutateInt8Slot(calcVOffsetT(16), 2) }}, testcase{"Int16Slot", func() bool { return t.MutateInt16Slot(calcVOffsetT(16), 2) }}, testcase{"Int32Slot", func() bool { return t.MutateInt32Slot(calcVOffsetT(16), 2) }}, testcase{"Int64Slot", func() bool { return t.MutateInt64Slot(calcVOffsetT(16), 2) }}, testcase{"Float32Slot", func() bool { return t.MutateFloat32Slot(calcVOffsetT(16), 2) }}, testcase{"Float64Slot", func() bool { return t.MutateFloat64Slot(calcVOffsetT(16), 2) }}, } testForMutatedValues := []testcase{ testcase{"BoolSlot", func() bool { return t.GetBoolSlot(calcVOffsetT(0), true) == false }}, testcase{"ByteSlot", func() bool { return t.GetByteSlot(calcVOffsetT(1), 1) == 2 }}, testcase{"Uint8Slot", func() bool { return t.GetUint8Slot(calcVOffsetT(2), 1) == 4 }}, testcase{"Uint16Slot", func() bool { return t.GetUint16Slot(calcVOffsetT(3), 1) == 6 }}, testcase{"Uint32Slot", func() bool { return t.GetUint32Slot(calcVOffsetT(4), 1) == 8 }}, testcase{"Uint64Slot", func() bool { return t.GetUint64Slot(calcVOffsetT(5), 1) == 10 }}, testcase{"Int8Slot", func() bool { return t.GetInt8Slot(calcVOffsetT(6), 1) == 12 }}, testcase{"Int16Slot", func() bool { return t.GetInt16Slot(calcVOffsetT(7), 1) == 14 }}, testcase{"Int32Slot", func() bool { return t.GetInt32Slot(calcVOffsetT(8), 1) == 16 }}, testcase{"Int64Slot", func() bool { return t.GetInt64Slot(calcVOffsetT(9), 1) == 18 }}, testcase{"Float32Slot", func() bool { return t.GetFloat32Slot(calcVOffsetT(10), 1) == 20 }}, testcase{"Float64Slot", func() bool { return t.GetFloat64Slot(calcVOffsetT(11), 1) == 22 }}, testcase{"UOffsetTSlot", func() bool { return t.GetUOffsetT(calcUOffsetT(calcVOffsetT(12))) == 24 }}, testcase{"VOffsetTSlot", func() bool { return t.GetVOffsetT(calcUOffsetT(calcVOffsetT(13))) == 26 }}, testcase{"SOffsetTSlot", func() bool { return t.GetSOffsetT(calcUOffsetT(calcVOffsetT(14))) == 28 }}, } // make sure original values are okay for _, t := range testForOriginalValues { if !t.testfn() { fail(t.field + "' field doesn't have the expected original value") } } // try to mutate fields and check mutability for _, t := range testMutability { if !t.testfn() { fail(FailString(t.field+"' field failed mutability test", "passed", "failed")) } } // try to mutate fields and check mutability // these have wrong slots so should fail for _, t := range testMutabilityWithoutSlot { if t.testfn() { fail(FailString(t.field+"' field failed no slot mutability test", "failed", "passed")) } } // test whether values have changed for _, t := range testForMutatedValues { if !t.testfn() { fail(t.field + "' field doesn't have the expected mutated value") } } } // BenchmarkVtableDeduplication measures the speed of vtable deduplication // by creating prePop vtables, then populating b.N objects with a // different single vtable. // // When b.N is large (as in long benchmarks), memory usage may be high. func BenchmarkVtableDeduplication(b *testing.B) { prePop := 10 builder := flatbuffers.NewBuilder(0) // pre-populate some vtables: for i := 0; i < prePop; i++ { builder.StartObject(i) for j := 0; j < i; j++ { builder.PrependInt16Slot(j, int16(j), 0) } builder.EndObject() } // benchmark deduplication of a new vtable: b.ResetTimer() for i := 0; i < b.N; i++ { lim := prePop builder.StartObject(lim) for j := 0; j < lim; j++ { builder.PrependInt16Slot(j, int16(j), 0) } builder.EndObject() } } // BenchmarkParseGold measures the speed of parsing the 'gold' data // used throughout this test suite. func BenchmarkParseGold(b *testing.B) { buf, offset := CheckGeneratedBuild(b.Fatalf) monster := example.GetRootAsMonster(buf, offset) // use these to prevent allocations: reuse_pos := example.Vec3{} reuse_test3 := example.Test{} reuse_table2 := flatbuffers.Table{} reuse_monster2 := example.Monster{} reuse_test4_0 := example.Test{} reuse_test4_1 := example.Test{} b.SetBytes(int64(len(buf[offset:]))) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { monster.Hp() monster.Mana() name := monster.Name() _ = name[0] _ = name[len(name)-1] monster.Pos(&reuse_pos) reuse_pos.X() reuse_pos.Y() reuse_pos.Z() reuse_pos.Test1() reuse_pos.Test2() reuse_pos.Test3(&reuse_test3) reuse_test3.A() reuse_test3.B() monster.TestType() monster.Test(&reuse_table2) reuse_monster2.Init(reuse_table2.Bytes, reuse_table2.Pos) name2 := reuse_monster2.Name() _ = name2[0] _ = name2[len(name2)-1] monster.InventoryLength() l := monster.InventoryLength() for i := 0; i < l; i++ { monster.Inventory(i) } monster.Test4Length() monster.Test4(&reuse_test4_0, 0) monster.Test4(&reuse_test4_1, 1) reuse_test4_0.A() reuse_test4_0.B() reuse_test4_1.A() reuse_test4_1.B() monster.TestarrayofstringLength() str0 := monster.Testarrayofstring(0) _ = str0[0] _ = str0[len(str0)-1] str1 := monster.Testarrayofstring(1) _ = str1[0] _ = str1[len(str1)-1] } } // BenchmarkBuildGold uses generated code to build the example Monster. func BenchmarkBuildGold(b *testing.B) { buf, offset := CheckGeneratedBuild(b.Fatalf) bytes_length := int64(len(buf[offset:])) reuse_str := "MyMonster" reuse_test1 := "test1" reuse_test2 := "test2" reuse_fred := "Fred" b.SetBytes(bytes_length) bldr := flatbuffers.NewBuilder(0) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { bldr.Reset() str := bldr.CreateString(reuse_str) test1 := bldr.CreateString(reuse_test1) test2 := bldr.CreateString(reuse_test2) fred := bldr.CreateString(reuse_fred) example.MonsterStartInventoryVector(bldr, 5) bldr.PrependByte(4) bldr.PrependByte(3) bldr.PrependByte(2) bldr.PrependByte(1) bldr.PrependByte(0) inv := bldr.EndVector(5) example.MonsterStart(bldr) example.MonsterAddName(bldr, fred) mon2 := example.MonsterEnd(bldr) example.MonsterStartTest4Vector(bldr, 2) example.CreateTest(bldr, 10, 20) example.CreateTest(bldr, 30, 40) test4 := bldr.EndVector(2) example.MonsterStartTestarrayofstringVector(bldr, 2) bldr.PrependUOffsetT(test2) bldr.PrependUOffsetT(test1) testArrayOfString := bldr.EndVector(2) example.MonsterStart(bldr) pos := example.CreateVec3(bldr, 1.0, 2.0, 3.0, 3.0, example.ColorGreen, 5, 6) example.MonsterAddPos(bldr, pos) example.MonsterAddHp(bldr, 80) example.MonsterAddName(bldr, str) example.MonsterAddInventory(bldr, inv) example.MonsterAddTestType(bldr, 1) example.MonsterAddTest(bldr, mon2) example.MonsterAddTest4(bldr, test4) example.MonsterAddTestarrayofstring(bldr, testArrayOfString) mon := example.MonsterEnd(bldr) bldr.Finish(mon) } }