/* * 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 pizza "Pizza" "encoding/json" optional_scalars "optional_scalars" // refers to generated code order "order" "bytes" "flag" "fmt" "os" "reflect" "sort" "testing" "testing/quick" 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") } // 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}) ) func TestMain(m *testing.M) { flag.Parse() if cppData == "" { fmt.Fprintf(os.Stderr, "cpp_data argument is required\n") os.Exit(1) } os.Exit(m.Run()) } // TestTextParsing test if text parsing works with object API. func TestTextParsing(t *testing.T) { expectedMonster := example.MonsterT{ Mana: 42, Name: "foo", LongEnumNormalDefault: example.LongEnumLongTwo, } buf := new(bytes.Buffer) if err := json.NewEncoder(buf).Encode(expectedMonster); err != nil { t.Fatal(err) } var monster example.MonsterT if err := json.NewDecoder(buf).Decode(&monster); err != nil { t.Fatal(err) } if monster.Mana != expectedMonster.Mana { t.Fatal("wrong mana:", monster.Mana) } if monster.Name != expectedMonster.Name { t.Fatal("wrong name:", monster.Name) } if monster.LongEnumNormalDefault != expectedMonster.LongEnumNormalDefault { t.Fatal("wrong enum:", monster.LongEnumNormalDefault) } } func CheckNoNamespaceImport(fail func(string, ...interface{})) { const size = 13 // Order a pizza with specific size builder := flatbuffers.NewBuilder(0) ordered_pizza := pizza.PizzaT{Size: size} food := order.FoodT{Pizza: &ordered_pizza} builder.Finish(food.Pack(builder)) // Receive order received_food := order.GetRootAsFood(builder.FinishedBytes(), 0) received_pizza := received_food.Pizza(nil).UnPack() // Check if received pizza is equal to ordered pizza if !reflect.DeepEqual(ordered_pizza, *received_pizza) { fail(FailString("no namespace import", ordered_pizza, received_pizza)) } } // 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) CheckSharedStrings(t.Fatalf) CheckEmptiedBuilder(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(false, false, t.Fatalf) // Verify that the buffer generated by Go code is readable by the // generated Go code: CheckReadBuffer(generated, off, false, t.Fatalf) CheckMutateBuffer(generated, off, false, t.Fatalf) CheckObjectAPI(generated, off, false, t.Fatalf) // Generate the buffer again, with file identifier. generated, off = CheckGeneratedBuild(false, true, t.Fatalf) // Check that this buffer with file identifier is usable // and that the file identifier is correct. CheckReadBuffer(generated, off, false, t.Fatalf) CheckMutateBuffer(generated, off, false, t.Fatalf) CheckObjectAPI(generated, off, false, t.Fatalf) CheckFileIdentifier(generated, off, false, t.Fatalf) // Verify that the buffer generated by C++ code is readable by the // generated Go code: monsterDataCpp, err := os.ReadFile(cppData) if err != nil { t.Fatal(err) } CheckReadBuffer(monsterDataCpp, 0, false, t.Fatalf) CheckMutateBuffer(monsterDataCpp, 0, false, t.Fatalf) CheckObjectAPI(monsterDataCpp, 0, false, t.Fatalf) CheckFileIdentifier(monsterDataCpp, 0, false, 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) // Check a no namespace import CheckNoNamespaceImport(t.Fatalf) // Check size-prefixed flatbuffers CheckSizePrefixedBuffer(t.Fatalf) // Check that optional scalars works CheckOptionalScalars(t.Fatalf) // Check that getting vector element by key works CheckByKey(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 := os.ReadFile(javaData) if err != nil { t.Fatal(err) } CheckReadBuffer(monsterDataJava, 0, false, 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 = os.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, sizePrefix bool, fail func(string, ...interface{})) { // try the two ways of generating a monster var monster1 *example.Monster monster2 := &example.Monster{} 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)) } // 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)) } } } // CheckFileIdentifier checks the "MONS" file identifier func CheckFileIdentifier(buf []byte, offset flatbuffers.UOffsetT, sizePrefix bool, fail func(string, ...interface{})) { // Strip offset buf = buf[offset:] var fileIdentifier string var hasFileIdentifier bool if sizePrefix { fileIdentifier = flatbuffers.GetSizePrefixedBufferIdentifier(buf) hasFileIdentifier = example.SizePrefixedMonsterBufferHasIdentifier(buf) } else { fileIdentifier = flatbuffers.GetBufferIdentifier(buf) hasFileIdentifier = example.MonsterBufferHasIdentifier(buf) } expectedFileIdentifier := "MONS" if fileIdentifier != expectedFileIdentifier { fail("expected file identifier %q, got %q", expectedFileIdentifier, fileIdentifier) } if !hasFileIdentifier { fail("did not find file identifier") } } // 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, 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 var monster *example.Monster if sizePrefix { monster = example.GetSizePrefixedRootAsMonster(buf, offset) } else { 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 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") } } // 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. 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) 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") } } 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)) } // default if got := monster.Mana; 150 != got { fail(FailString("mana", 150, got)) } if monster.Test != nil && monster.Test.Type == example.AnyMonster { monster.Test.Value.(*example.MonsterT).NanDefault = 0.0 } if monster.Enemy != nil { monster.Enemy.NanDefault = 0.0 } monster.NanDefault = 0.0 builder := flatbuffers.NewBuilder(0) builder.Finish(monster.Pack(builder)) monster2 := example.GetRootAsMonster(builder.FinishedBytes(), 0).UnPack() if !reflect.DeepEqual(monster, monster2) { fail(FailString("Pack/Unpack()", monster, monster2)) } } // 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 16b: same as test 16, size prefixed b = flatbuffers.NewBuilder(0) b.StartObject(2) b.PrependInt8Slot(0, 33, 0) b.PrependInt16Slot(1, 66, 0) off = b.EndObject() b.FinishSizePrefixed(off) check([]byte{ 20, 0, 0, 0, // size prefix 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 16c: same as test 16, with file identifier b = flatbuffers.NewBuilder(0) b.StartObject(2) b.PrependInt8Slot(0, 33, 0) b.PrependInt16Slot(1, 66, 0) off = b.EndObject() b.FinishWithFileIdentifier(off, []byte("TEST")) check([]byte{ 16, 0, 0, 0, // root of table: points to vtable offset 'T', 'E', 'S', 'T', // file identifier 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 16d: same as test 16, size prefixed with file identifier b = flatbuffers.NewBuilder(0) b.StartObject(2) b.PrependInt8Slot(0, 33, 0) b.PrependInt16Slot(1, 66, 0) off = b.EndObject() b.FinishSizePrefixedWithFileIdentifier(off, []byte("TEST")) check([]byte{ 24, 0, 0, 0, // size prefix 16, 0, 0, 0, // root of table: points to vtable offset 'T', 'E', 'S', 'T', // file identifier 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(sizePrefix, fileIdentifier bool, 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) if fileIdentifier { if sizePrefix { example.FinishSizePrefixedMonsterBuffer(b, mon) } else { example.FinishMonsterBuffer(b, mon) } } else { if sizePrefix { b.FinishSizePrefixed(mon) } else { 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") } func CheckEmptiedBuilder(fail func(string, ...interface{})) { f := func(a, b string) bool { if a == b { return true } builder := flatbuffers.NewBuilder(0) a1 := builder.CreateSharedString(a) b1 := builder.CreateSharedString(b) builder.Reset() b2 := builder.CreateSharedString(b) a2 := builder.CreateSharedString(a) return !(a1 == a2 || b1 == b2) } if err := quick.Check(f, nil); err != nil { fail("expected different offset") } } func CheckSharedStrings(fail func(string, ...interface{})) { f := func(strings []string) bool { b := flatbuffers.NewBuilder(0) for _, s1 := range strings { for _, s2 := range strings { off1 := b.CreateSharedString(s1) off2 := b.CreateSharedString(s2) if (s1 == s2) && (off1 != off2) { return false } if (s1 != s2) && (off1 == off2) { return false } } } return true } if err := quick.Check(f, nil); err != nil { fail("expected same offset") } } // 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") } } } func CheckSizePrefixedBuffer(fail func(string, ...interface{})) { // Generate a size-prefixed flatbuffer, first without file identifier generated, off := CheckGeneratedBuild(true, false, fail) // Check that the buffer can be used as expected CheckReadBuffer(generated, off, true, fail) CheckMutateBuffer(generated, off, true, fail) CheckObjectAPI(generated, off, true, fail) // Now generate a size-prefixed flatbuffer with file identifier generated, off = CheckGeneratedBuild(true, true, fail) // Check that the size prefix is the size of monsterdata_go_wire.mon, // plus 4 bytes for padding size := flatbuffers.GetSizePrefix(generated, off) expectedSize := uint32(228) if size != expectedSize { fail("mismatch between size prefix (%d) and expected size (%d)", size, expectedSize) } // Check that the buffer can be used as expected CheckReadBuffer(generated, off, true, fail) CheckMutateBuffer(generated, off, true, fail) CheckObjectAPI(generated, off, true, fail) CheckFileIdentifier(generated, off, true, fail) // Write generated buffer out to a file if err := os.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 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") } } } // CheckOptionalScalars verifies against the ScalarStuff schema. func CheckOptionalScalars(fail func(string, ...interface{})) { type testCase struct { what string result, expect interface{} } makeDefaultTestCases := func(s *optional_scalars.ScalarStuff) []testCase { return []testCase{ {"justI8", s.JustI8(), int8(0)}, {"maybeI8", s.MaybeI8(), (*int8)(nil)}, {"defaultI8", s.DefaultI8(), int8(42)}, {"justU8", s.JustU8(), byte(0)}, {"maybeU8", s.MaybeU8(), (*byte)(nil)}, {"defaultU8", s.DefaultU8(), byte(42)}, {"justI16", s.JustI16(), int16(0)}, {"maybeI16", s.MaybeI16(), (*int16)(nil)}, {"defaultI16", s.DefaultI16(), int16(42)}, {"justU16", s.JustU16(), uint16(0)}, {"maybeU16", s.MaybeU16(), (*uint16)(nil)}, {"defaultU16", s.DefaultU16(), uint16(42)}, {"justI32", s.JustI32(), int32(0)}, {"maybeI32", s.MaybeI32(), (*int32)(nil)}, {"defaultI32", s.DefaultI32(), int32(42)}, {"justU32", s.JustU32(), uint32(0)}, {"maybeU32", s.MaybeU32(), (*uint32)(nil)}, {"defaultU32", s.DefaultU32(), uint32(42)}, {"justI64", s.JustI64(), int64(0)}, {"maybeI64", s.MaybeI64(), (*int64)(nil)}, {"defaultI64", s.DefaultI64(), int64(42)}, {"justU64", s.JustU64(), uint64(0)}, {"maybeU64", s.MaybeU64(), (*uint64)(nil)}, {"defaultU64", s.DefaultU64(), uint64(42)}, {"justF32", s.JustF32(), float32(0)}, {"maybeF32", s.MaybeF32(), (*float32)(nil)}, {"defaultF32", s.DefaultF32(), float32(42)}, {"justF64", s.JustF64(), float64(0)}, {"maybeF64", s.MaybeF64(), (*float64)(nil)}, {"defaultF64", s.DefaultF64(), float64(42)}, {"justBool", s.JustBool(), false}, {"maybeBool", s.MaybeBool(), (*bool)(nil)}, {"defaultBool", s.DefaultBool(), true}, {"justEnum", s.JustEnum(), optional_scalars.OptionalByte(0)}, {"maybeEnum", s.MaybeEnum(), (*optional_scalars.OptionalByte)(nil)}, {"defaultEnum", s.DefaultEnum(), optional_scalars.OptionalByteOne}, } } makeAssignedTestCases := func(s *optional_scalars.ScalarStuff) []testCase { return []testCase{ {"justI8", s.JustI8(), int8(5)}, {"maybeI8", s.MaybeI8(), int8(5)}, {"defaultI8", s.DefaultI8(), int8(5)}, {"justU8", s.JustU8(), byte(6)}, {"maybeU8", s.MaybeU8(), byte(6)}, {"defaultU8", s.DefaultU8(), byte(6)}, {"justI16", s.JustI16(), int16(7)}, {"maybeI16", s.MaybeI16(), int16(7)}, {"defaultI16", s.DefaultI16(), int16(7)}, {"justU16", s.JustU16(), uint16(8)}, {"maybeU16", s.MaybeU16(), uint16(8)}, {"defaultU16", s.DefaultU16(), uint16(8)}, {"justI32", s.JustI32(), int32(9)}, {"maybeI32", s.MaybeI32(), int32(9)}, {"defaultI32", s.DefaultI32(), int32(9)}, {"justU32", s.JustU32(), uint32(10)}, {"maybeU32", s.MaybeU32(), uint32(10)}, {"defaultU32", s.DefaultU32(), uint32(10)}, {"justI64", s.JustI64(), int64(11)}, {"maybeI64", s.MaybeI64(), int64(11)}, {"defaultI64", s.DefaultI64(), int64(11)}, {"justU64", s.JustU64(), uint64(12)}, {"maybeU64", s.MaybeU64(), uint64(12)}, {"defaultU64", s.DefaultU64(), uint64(12)}, {"justF32", s.JustF32(), float32(13)}, {"maybeF32", s.MaybeF32(), float32(13)}, {"defaultF32", s.DefaultF32(), float32(13)}, {"justF64", s.JustF64(), float64(14)}, {"maybeF64", s.MaybeF64(), float64(14)}, {"defaultF64", s.DefaultF64(), float64(14)}, {"justBool", s.JustBool(), true}, {"maybeBool", s.MaybeBool(), true}, {"defaultBool", s.DefaultBool(), false}, {"justEnum", s.JustEnum(), optional_scalars.OptionalByteTwo}, {"maybeEnum", s.MaybeEnum(), optional_scalars.OptionalByteTwo}, {"defaultEnum", s.DefaultEnum(), optional_scalars.OptionalByteTwo}, } } resolvePointer := func(v interface{}) interface{} { switch v := v.(type) { case *int8: return *v case *byte: return *v case *int16: return *v case *uint16: return *v case *int32: return *v case *uint32: return *v case *int64: return *v case *uint64: return *v case *float32: return *v case *float64: return *v case *bool: return *v case *optional_scalars.OptionalByte: return *v default: return v } } buildAssignedTable := func(b *flatbuffers.Builder) *optional_scalars.ScalarStuff { optional_scalars.ScalarStuffStart(b) optional_scalars.ScalarStuffAddJustI8(b, int8(5)) optional_scalars.ScalarStuffAddMaybeI8(b, int8(5)) optional_scalars.ScalarStuffAddDefaultI8(b, int8(5)) optional_scalars.ScalarStuffAddJustU8(b, byte(6)) optional_scalars.ScalarStuffAddMaybeU8(b, byte(6)) optional_scalars.ScalarStuffAddDefaultU8(b, byte(6)) optional_scalars.ScalarStuffAddJustI16(b, int16(7)) optional_scalars.ScalarStuffAddMaybeI16(b, int16(7)) optional_scalars.ScalarStuffAddDefaultI16(b, int16(7)) optional_scalars.ScalarStuffAddJustU16(b, uint16(8)) optional_scalars.ScalarStuffAddMaybeU16(b, uint16(8)) optional_scalars.ScalarStuffAddDefaultU16(b, uint16(8)) optional_scalars.ScalarStuffAddJustI32(b, int32(9)) optional_scalars.ScalarStuffAddMaybeI32(b, int32(9)) optional_scalars.ScalarStuffAddDefaultI32(b, int32(9)) optional_scalars.ScalarStuffAddJustU32(b, uint32(10)) optional_scalars.ScalarStuffAddMaybeU32(b, uint32(10)) optional_scalars.ScalarStuffAddDefaultU32(b, uint32(10)) optional_scalars.ScalarStuffAddJustI64(b, int64(11)) optional_scalars.ScalarStuffAddMaybeI64(b, int64(11)) optional_scalars.ScalarStuffAddDefaultI64(b, int64(11)) optional_scalars.ScalarStuffAddJustU64(b, uint64(12)) optional_scalars.ScalarStuffAddMaybeU64(b, uint64(12)) optional_scalars.ScalarStuffAddDefaultU64(b, uint64(12)) optional_scalars.ScalarStuffAddJustF32(b, float32(13)) optional_scalars.ScalarStuffAddMaybeF32(b, float32(13)) optional_scalars.ScalarStuffAddDefaultF32(b, float32(13)) optional_scalars.ScalarStuffAddJustF64(b, float64(14)) optional_scalars.ScalarStuffAddMaybeF64(b, float64(14)) optional_scalars.ScalarStuffAddDefaultF64(b, float64(14)) optional_scalars.ScalarStuffAddJustBool(b, true) optional_scalars.ScalarStuffAddMaybeBool(b, true) optional_scalars.ScalarStuffAddDefaultBool(b, false) optional_scalars.ScalarStuffAddJustEnum(b, optional_scalars.OptionalByteTwo) optional_scalars.ScalarStuffAddMaybeEnum(b, optional_scalars.OptionalByteTwo) optional_scalars.ScalarStuffAddDefaultEnum(b, optional_scalars.OptionalByteTwo) b.Finish(optional_scalars.ScalarStuffEnd(b)) return optional_scalars.GetRootAsScalarStuff(b.FinishedBytes(), 0) } // test default values fbb := flatbuffers.NewBuilder(1) optional_scalars.ScalarStuffStart(fbb) fbb.Finish(optional_scalars.ScalarStuffEnd(fbb)) ss := optional_scalars.GetRootAsScalarStuff(fbb.FinishedBytes(), 0) for _, tc := range makeDefaultTestCases(ss) { if tc.result != tc.expect { fail(FailString("Default ScalarStuff: "+tc.what, tc.expect, tc.result)) } } // test assigned values fbb.Reset() ss = buildAssignedTable(fbb) for _, tc := range makeAssignedTestCases(ss) { if resolvePointer(tc.result) != tc.expect { fail(FailString("Assigned ScalarStuff: "+tc.what, tc.expect, tc.result)) } } // test native object pack fbb.Reset() i8 := int8(5) u8 := byte(6) i16 := int16(7) u16 := uint16(8) i32 := int32(9) u32 := uint32(10) i64 := int64(11) u64 := uint64(12) f32 := float32(13) f64 := float64(14) b := true enum := optional_scalars.OptionalByteTwo obj := optional_scalars.ScalarStuffT{ JustI8: 5, MaybeI8: &i8, DefaultI8: 5, JustU8: 6, MaybeU8: &u8, DefaultU8: 6, JustI16: 7, MaybeI16: &i16, DefaultI16: 7, JustU16: 8, MaybeU16: &u16, DefaultU16: 8, JustI32: 9, MaybeI32: &i32, DefaultI32: 9, JustU32: 10, MaybeU32: &u32, DefaultU32: 10, JustI64: 11, MaybeI64: &i64, DefaultI64: 11, JustU64: 12, MaybeU64: &u64, DefaultU64: 12, JustF32: 13, MaybeF32: &f32, DefaultF32: 13, JustF64: 14, MaybeF64: &f64, DefaultF64: 14, JustBool: true, MaybeBool: &b, DefaultBool: false, JustEnum: optional_scalars.OptionalByteTwo, MaybeEnum: &enum, DefaultEnum: optional_scalars.OptionalByteTwo, } fbb.Finish(obj.Pack(fbb)) ss = optional_scalars.GetRootAsScalarStuff(fbb.FinishedBytes(), 0) for _, tc := range makeAssignedTestCases(ss) { if resolvePointer(tc.result) != tc.expect { fail(FailString("Native Object ScalarStuff: "+tc.what, tc.expect, tc.result)) } } // test native object unpack fbb.Reset() ss = buildAssignedTable(fbb) ss.UnPackTo(&obj) expectEq := func(what string, a, b interface{}) { if resolvePointer(a) != b { fail(FailString("Native Object Unpack ScalarStuff: "+what, b, a)) } } expectEq("justI8", obj.JustI8, int8(5)) expectEq("maybeI8", obj.MaybeI8, int8(5)) expectEq("defaultI8", obj.DefaultI8, int8(5)) expectEq("justU8", obj.JustU8, byte(6)) expectEq("maybeU8", obj.MaybeU8, byte(6)) expectEq("defaultU8", obj.DefaultU8, byte(6)) expectEq("justI16", obj.JustI16, int16(7)) expectEq("maybeI16", obj.MaybeI16, int16(7)) expectEq("defaultI16", obj.DefaultI16, int16(7)) expectEq("justU16", obj.JustU16, uint16(8)) expectEq("maybeU16", obj.MaybeU16, uint16(8)) expectEq("defaultU16", obj.DefaultU16, uint16(8)) expectEq("justI32", obj.JustI32, int32(9)) expectEq("maybeI32", obj.MaybeI32, int32(9)) expectEq("defaultI32", obj.DefaultI32, int32(9)) expectEq("justU32", obj.JustU32, uint32(10)) expectEq("maybeU32", obj.MaybeU32, uint32(10)) expectEq("defaultU32", obj.DefaultU32, uint32(10)) expectEq("justI64", obj.JustI64, int64(11)) expectEq("maybeI64", obj.MaybeI64, int64(11)) expectEq("defaultI64", obj.DefaultI64, int64(11)) expectEq("justU64", obj.JustU64, uint64(12)) expectEq("maybeU64", obj.MaybeU64, uint64(12)) expectEq("defaultU64", obj.DefaultU64, uint64(12)) expectEq("justF32", obj.JustF32, float32(13)) expectEq("maybeF32", obj.MaybeF32, float32(13)) expectEq("defaultF32", obj.DefaultF32, float32(13)) expectEq("justF64", obj.JustF64, float64(14)) expectEq("maybeF64", obj.MaybeF64, float64(14)) expectEq("defaultF64", obj.DefaultF64, float64(14)) expectEq("justBool", obj.JustBool, true) expectEq("maybeBool", obj.MaybeBool, true) expectEq("defaultBool", obj.DefaultBool, false) expectEq("justEnum", obj.JustEnum, optional_scalars.OptionalByteTwo) expectEq("maybeEnum", obj.MaybeEnum, optional_scalars.OptionalByteTwo) expectEq("defaultEnum", obj.DefaultEnum, optional_scalars.OptionalByteTwo) } func CheckByKey(fail func(string, ...interface{})) { expectEq := func(what string, a, b interface{}) { if a != b { fail(FailString("Lookup by key: "+what, b, a)) } } b := flatbuffers.NewBuilder(0) name := b.CreateString("Boss") slime := &example.MonsterT{Name: "Slime"} pig := &example.MonsterT{Name: "Pig"} slimeBoss := &example.MonsterT{Name: "SlimeBoss"} mushroom := &example.MonsterT{Name: "Mushroom"} ironPig := &example.MonsterT{Name: "Iron Pig"} monsterOffsets := make([]flatbuffers.UOffsetT, 5) monsterOffsets[0] = slime.Pack(b) monsterOffsets[1] = pig.Pack(b) monsterOffsets[2] = slimeBoss.Pack(b) monsterOffsets[3] = mushroom.Pack(b) monsterOffsets[4] = ironPig.Pack(b) testarrayoftables := b.CreateVectorOfSortedTables(monsterOffsets, example.MonsterKeyCompare) str := &example.StatT{Id: "Strength", Count: 42} luk := &example.StatT{Id: "Luck", Count: 51} hp := &example.StatT{Id: "Health", Count: 12} // Test default count value of 0 mp := &example.StatT{Id: "Mana"} statOffsets := make([]flatbuffers.UOffsetT, 4) statOffsets[0] = str.Pack(b) statOffsets[1] = luk.Pack(b) statOffsets[2] = hp.Pack(b) statOffsets[3] = mp.Pack(b) scalarKeySortedTablesOffset := b.CreateVectorOfSortedTables(statOffsets, example.StatKeyCompare) example.MonsterStart(b) example.MonsterAddName(b, name) example.MonsterAddTestarrayoftables(b, testarrayoftables) example.MonsterAddScalarKeySortedTables(b, scalarKeySortedTablesOffset) moff := example.MonsterEnd(b) b.Finish(moff) monster := example.GetRootAsMonster(b.Bytes, b.Head()) slimeMon := &example.Monster{} monster.TestarrayoftablesByKey(slimeMon, slime.Name) mushroomMon := &example.Monster{} monster.TestarrayoftablesByKey(mushroomMon, mushroom.Name) slimeBossMon := &example.Monster{} monster.TestarrayoftablesByKey(slimeBossMon, slimeBoss.Name) strStat := &example.Stat{} monster.ScalarKeySortedTablesByKey(strStat, str.Count) lukStat := &example.Stat{} monster.ScalarKeySortedTablesByKey(lukStat, luk.Count) mpStat := &example.Stat{} monster.ScalarKeySortedTablesByKey(mpStat, mp.Count) expectEq("Boss name", string(monster.Name()), "Boss") expectEq("Slime name", string(slimeMon.Name()), slime.Name) expectEq("Mushroom name", string(mushroomMon.Name()), mushroom.Name) expectEq("SlimeBoss name", string(slimeBossMon.Name()), slimeBoss.Name) expectEq("Strength Id", string(strStat.Id()), str.Id) expectEq("Strength Count", strStat.Count(), str.Count) expectEq("Luck Id", string(lukStat.Id()), luk.Id) expectEq("Luck Count", lukStat.Count(), luk.Count) expectEq("Mana Id", string(mpStat.Id()), mp.Id) // Use default count value as key expectEq("Mana Count", mpStat.Count(), uint16(0)) } // 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(false, false, 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(false, false, 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) } }