// Example how to use Flatbuffers to create and read binary buffers. func main() { builder := flatbuffers.NewBuilder(0) // Create some weapons for our Monster ("Sword" and "Axe"). weaponOne := builder.CreateString("Sword") weaponTwo := builder.CreateString("Axe") sample.WeaponStart(builder) sample.WeaponAddName(builder, weaponOne) sample.WeaponAddDamage(builder, 3) sword := sample.WeaponEnd(builder) sample.WeaponStart(builder) sample.WeaponAddName(builder, weaponTwo) sample.WeaponAddDamage(builder, 5) axe := sample.WeaponEnd(builder) // Serialize the FlatBuffer data. name := builder.CreateString("Orc") sample.MonsterStartInventoryVector(builder, 10) // Note: Since we prepend the bytes, this loop iterates in reverse. for i := 9; i >= 0; i-- { builder.PrependByte(byte(i)) } inv := builder.EndVector(10) sample.MonsterStartWeaponsVector(builder, 2) // Note: Since we prepend the weapons, prepend in reverse order. builder.PrependUOffsetT(axe) builder.PrependUOffsetT(sword) weapons := builder.EndVector(2) pos := sample.CreateVec3(builder, 1.0, 2.0, 3.0) sample.MonsterStart(builder) sample.MonsterAddPos(builder, pos) sample.MonsterAddHp(builder, 300) sample.MonsterAddName(builder, name) sample.MonsterAddInventory(builder, inv) sample.MonsterAddColor(builder, sample.ColorRed) sample.MonsterAddWeapons(builder, weapons) sample.MonsterAddEquippedType(builder, sample.EquipmentWeapon) sample.MonsterAddEquipped(builder, axe) orc := sample.MonsterEnd(builder) builder.Finish(orc) // We now have a FlatBuffer that we could store on disk or send over a network. // ...Saving to file or sending over a network code goes here... // Instead, we are going to access this buffer right away (as if we just received it). buf := builder.FinishedBytes() // Note: We use `0` for the offset here, since we got the data using the // `builder.FinishedBytes()` method. This simulates the data you would store/receive in your // FlatBuffer. If you wanted to read from the `builder.Bytes` directly, you would need to // pass in the offset of `builder.Head()`, as the builder actually constructs the buffer // backwards. monster := sample.GetRootAsMonster(buf, 0) // Note: We did not set the `mana` field explicitly, so we get the // default value. assert(monster.Mana() == 150, "`monster.Mana()`", strconv.Itoa(int(monster.Mana())), "150") assert(monster.Hp() == 300, "`monster.Hp()`", strconv.Itoa(int(monster.Hp())), "300") assert(string(monster.Name()) == "Orc", "`string(monster.Name())`", string(monster.Name()), "\"Orc\"") assert(monster.Color() == sample.ColorRed, "`monster.Color()`", strconv.Itoa(int(monster.Color())), strconv.Itoa(int(sample.ColorRed))) // Note: Whenever you access a new object, like in `Pos()`, a new temporary accessor object // gets created. If your code is very performance sensitive, you can pass in a pointer to an // existing `Vec3` instead of `nil`. This allows you to reuse it across many calls to reduce // the amount of object allocation/garbage collection. assert(monster.Pos(nil).X() == 1.0, "`monster.Pos(nil).X()`", strconv.FormatFloat(float64(monster.Pos(nil).X()), 'f', 1, 32), "1.0") assert(monster.Pos(nil).Y() == 2.0, "`monster.Pos(nil).Y()`", strconv.FormatFloat(float64(monster.Pos(nil).Y()), 'f', 1, 32), "2.0") assert(monster.Pos(nil).Z() == 3.0, "`monster.Pos(nil).Z()`", strconv.FormatFloat(float64(monster.Pos(nil).Z()), 'f', 1, 32), "3.0") // For vectors, like `Inventory`, they have a method suffixed with 'Length' that can be used // to query the length of the vector. You can index the vector by passing an index value // into the accessor. for i := 0; i < monster.InventoryLength(); i++ { assert(monster.Inventory(i) == byte(i), "`monster.Inventory(i)`", strconv.Itoa(int(monster.Inventory(i))), strconv.Itoa(int(byte(i)))) } expectedWeaponNames := []string{"Sword", "Axe"} expectedWeaponDamages := []int{3, 5} weapon := new(sample.Weapon) // We need a `sample.Weapon` to pass into `monster.Weapons()` // to capture the output of that function. for i := 0; i < monster.WeaponsLength(); i++ { if monster.Weapons(weapon, i) { assert(string(weapon.Name()) == expectedWeaponNames[i], "`weapon.Name()`", string(weapon.Name()), expectedWeaponNames[i]) assert(int(weapon.Damage()) == expectedWeaponDamages[i], "`weapon.Damage()`", strconv.Itoa(int(weapon.Damage())), strconv.Itoa(expectedWeaponDamages[i])) } } // For FlatBuffer `union`s, you can get the type of the union, as well as the union // data itself. assert(monster.EquippedType() == sample.EquipmentWeapon, "`monster.EquippedType()`", strconv.Itoa(int(monster.EquippedType())), strconv.Itoa(int(sample.EquipmentWeapon))) unionTable := new(flatbuffers.Table) if monster.Equipped(unionTable) { // An example of how you can appropriately convert the table depending on the // FlatBuffer `union` type. You could add `else if` and `else` clauses to handle // other FlatBuffer `union` types for this field. (Similarly, this could be // done in a switch statement.) if monster.EquippedType() == sample.EquipmentWeapon { unionWeapon := new(sample.Weapon) unionWeapon.Init(unionTable.Bytes, unionTable.Pos) assert(string(unionWeapon.Name()) == "Axe", "`unionWeapon.Name()`", string(unionWeapon.Name()), "Axe") assert(int(unionWeapon.Damage()) == 5, "`unionWeapon.Damage()`", strconv.Itoa(int(unionWeapon.Damage())), strconv.Itoa(5)) } } fmt.Printf("The FlatBuffer was successfully created and verified!\n") }
// 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, 4, 5, 6)) example.MonsterAddHp(builder, 80) example.MonsterAddName(builder, str) example.MonsterAddInventory(builder, inv) example.MonsterAddTestType(builder, 1) // example.MonsterAddTest(builder, mon2) // example.MonsterAddTest4(builder, test4s) _ = example.MonsterEnd(builder) }
func BenchmarkMarshalByFlatBuffers(b *testing.B) { builder := flatbuffers.NewBuilder(0) b.ResetTimer() for i := 0; i < b.N; i++ { serializeByFlatBuffers(builder, &group) } }
// 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() } }
func ToAndFromFlat(uids []uint64) (error, int) { b := flatbuffers.NewBuilder(0) fuids.UidListStartUidsVector(b, len(uids)) for i := len(uids) - 1; i >= 0; i-- { b.PrependUint64(uids[i]) } ve := b.EndVector(len(uids)) fuids.UidListStart(b) fuids.UidListAddUids(b, ve) ue := fuids.UidListEnd(b) b.Finish(ue) data := b.FinishedBytes() nl := fuids.GetRootAsUidList(data, 0) if nl.UidsLength() != len(uids) { return fmt.Errorf("Length doesn't match"), 0 } for i := 0; i < len(uids); i++ { if nl.Uids(i) != uids[i] { return fmt.Errorf("ID doesn't match at index: %v Expected: %v. Got: %v", i, uids[i], nl.Uids(i)), 0 } } return nil, len(data) }
// 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, 2, 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) } }
func BenchmarkFlatBuffersUnmarshal(b *testing.B) { b.StopTimer() builder := flatbuffers.NewBuilder(0) data := generate() ser := make([][]byte, len(data)) for i, d := range data { ser[i] = serializeUsingFlatBuffers(builder, d) } b.ReportAllocs() b.StartTimer() for i := 0; i < b.N; i++ { n := rand.Intn(len(ser)) o := FlatBufferA{} sData := ser[n] o.Init(sData, flatbuffers.GetUOffsetT(sData)) if validate != "" { i := data[n] spouseVal := o.Spouse() == byte(1) correct := o.Name() == i.Name && o.Phone() == i.Phone && int(o.Siblings()) == i.Siblings && spouseVal == i.Spouse && o.Money() == i.Money && o.BirthDay() == i.BirthDay.Unix() if !correct { b.Fatalf("unmarshaled object differed:\n%v\n%v", i, o) } } } }
func makeNoticeMsg(str string, p uint16) []byte { t := transport.TcpMessage{} builder := flatbuffers.NewBuilder(0) ct := builder.CreateString(str) proto.NoticeStart(builder) proto.NoticeAddContent(builder, ct) payload := proto.NoticeEnd(builder) builder.Finish(payload) t.Payload = builder.Bytes[builder.Head():] // 填充协议头信息 t.Header.Proto = p t.Header.Flag = 0xdcba t.Header.Size = uint16(len(t.Payload)) ret, err := t.Pack() if err != nil { log.Fatal(err.Error()) return nil } return ret }
func BenchmarkUnmarshalByFlatBuffers(b *testing.B) { builder := flatbuffers.NewBuilder(0) bytes := serializeByFlatBuffers(builder, &group) b.ResetTimer() for i := 0; i < b.N; i++ { _ = GetRootAsFlatBufferColorGroup(bytes, 0) } }
func main() { builder := flatbuffers.NewBuilder(0) buf := MakeMonster(builder) monster := example.GetRootAsMonster(buf, 0) monster_name := monster.Name() fmt.Printf("Monster named '%s' encoded in %d bytes.\n", monster_name, len(buf)) }
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) } }
// 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() }
func BenchmarkFlatbuffersMarshal(b *testing.B) { b.StopTimer() data := generate() builder := flatbuffers.NewBuilder(0) b.ReportAllocs() b.StartTimer() for i := 0; i < b.N; i++ { serializeUsingFlatBuffers(builder, data[rand.Intn(len(data))]) } }
// 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) }
func BenchmarkWrite(b *testing.B) { builder := flatbuffers.NewBuilder(0) b.ReportAllocs() for i := 0; i < b.N; i++ { builder.Reset() buf := MakeUser(builder, []byte("Arthur Dent"), 42) if i == 0 { b.SetBytes(int64(len(buf))) } } }
// CheckObjectIsNestedError verifies that an object can not be created inside // another object. func CheckObjectIsNestedError(fail func(string, ...interface{})) { b := flatbuffers.NewBuilder(0) b.StartObject(0) defer func() { r := recover() if r == nil { fail("expected panic in CheckObjectIsNestedError") } }() b.StartObject(0) }
// 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")) }
// 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() }
func BenchmarkRead(b *testing.B) { builder := flatbuffers.NewBuilder(0) name := []byte("Arthur Dent") buf := MakeUser(builder, name, 42) b.SetBytes(int64(len(buf))) b.ReportAllocs() for i := 0; i < b.N; i++ { got_name, _ := ReadUser(buf) // do some work to prevent cheating the benchmark: bytes.Equal(got_name, name) } }
func BenchmarkUnmarshalByFlatBuffers_withFields(b *testing.B) { builder := flatbuffers.NewBuilder(0) bytes := serializeByFlatBuffers(builder, &group) b.ResetTimer() for i := 0; i < b.N; i++ { result := GetRootAsFlatBufferColorGroup(bytes, 0) result.CgId() result.Name() colorsLen := result.ColorsLength() for j := 0; j < colorsLen; j++ { result.Colors(j) } } }
func BenchmarkRoundtrip(b *testing.B) { builder := flatbuffers.NewBuilder(0) b.ReportAllocs() for i := 0; i < b.N; i++ { builder.Reset() buf := MakeUser(builder, []byte("Arthur Dent"), 42) got_name, _ := ReadUser(buf) if i == 0 { b.SetBytes(int64(len(buf))) } // do some work to prevent cheating the benchmark: bytes.Equal(got_name, []byte("Arthur Dent")) } }
func BenchmarkMarshal(b *testing.B) { b.Run("colfer", func(b *testing.B) { b.ReportAllocs() for i := b.N; i > 0; i-- { var err error holdSerial, err = testData[i%len(testData)].MarshalBinary() if err != nil { b.Fatal(err) } } }) b.Run("protobuf", func(b *testing.B) { b.ReportAllocs() for i := b.N; i > 0; i-- { var err error holdSerial, err = protoTestData[i%len(testData)].Marshal() if err != nil { b.Fatal(err) } } }) b.Run("flatbuf", func(b *testing.B) { b.ReportAllocs() for i := b.N; i > 0; i-- { o := testData[i%len(testData)] builder := flatbuffers.NewBuilder(0) host := builder.CreateString(o.Host) FlatBuffersStart(builder) FlatBuffersAddKey(builder, o.Key) FlatBuffersAddHost(builder, host) FlatBuffersAddPort(builder, o.Port) FlatBuffersAddSize(builder, o.Size) FlatBuffersAddHash(builder, o.Hash) FlatBuffersAddRatio(builder, o.Ratio) if o.Route { FlatBuffersAddRoute(builder, 1) } else { FlatBuffersAddRoute(builder, 0) } builder.Finish(FlatBuffersEnd(builder)) holdSerial = builder.Bytes[builder.Head():] } }) }
// 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, 2, 5, 6) example.MonsterAddPos(b, pos) example.MonsterAddHp(b, 80) example.MonsterAddName(b, str) 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() }
func createMonsterBuffer() []byte { builder := flatbuffers.NewBuilder(0) weaponOne := builder.CreateString("Sword") weaponTwo := builder.CreateString("Axe") sample.WeaponStart(builder) sample.WeaponAddName(builder, weaponOne) sample.WeaponAddDamage(builder, 3) sword := sample.WeaponEnd(builder) sample.WeaponStart(builder) sample.WeaponAddName(builder, weaponTwo) sample.WeaponAddDamage(builder, 5) axe := sample.WeaponEnd(builder) name := builder.CreateString("Orc") // Create a `vector` representing the inventory of the Orc. Each number // could correspond to an item that can be claimed after he is slain. // Note: Since we prepend the bytes, this loop iterates in reverse. sample.MonsterStartInventoryVector(builder, 10) for i := 9; i >= 0; i-- { builder.PrependByte(byte(i)) } inv := builder.EndVector(10) sample.MonsterStartWeaponsVector(builder, 2) builder.PrependUOffsetT(axe) builder.PrependUOffsetT(sword) weapons := builder.EndVector(2) pos := sample.CreateVec3(builder, 1.0, 2.0, 3.0) sample.MonsterStart(builder) sample.MonsterAddPos(builder, pos) sample.MonsterAddHp(builder, 300) sample.MonsterAddName(builder, name) sample.MonsterAddInventory(builder, inv) sample.MonsterAddColor(builder, sample.ColorRed) sample.MonsterAddWeapons(builder, weapons) sample.MonsterAddEquippedType(builder, sample.EquipmentWeapon) sample.MonsterAddEquipped(builder, axe) orc := sample.MonsterEnd(builder) builder.Finish(orc) return builder.FinishedBytes() }
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)) } }
func init() { for i, o := range testData { var err error colferSerials[i], err = o.MarshalBinary() if err != nil { panic(err) } } for i, o := range protoTestData { var err error protoSerials[i], err = o.Marshal() if err != nil { panic(err) } } for i, o := range testData { builder := flatbuffers.NewBuilder(0) host := builder.CreateString(o.Host) FlatBuffersStart(builder) FlatBuffersAddKey(builder, o.Key) FlatBuffersAddHost(builder, host) FlatBuffersAddPort(builder, o.Port) FlatBuffersAddSize(builder, o.Size) FlatBuffersAddHash(builder, o.Hash) FlatBuffersAddRatio(builder, o.Ratio) if o.Route { FlatBuffersAddRoute(builder, 1) } else { FlatBuffersAddRoute(builder, 0) } builder.Finish(FlatBuffersEnd(builder)) flatSerials[i] = builder.FinishedBytes() } }
// 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() }
// 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{ 6, 0, // vtable bytes 4, 0, // end of object from here 0, 0, // entry 1 is zero 6, 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 }) }
// 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 := int(l.Next()) % 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 }
// 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{b.Bytes, flatbuffers.UOffsetT(len(b.Bytes)) - obj0} table1 := &flatbuffers.Table{b.Bytes, flatbuffers.UOffsetT(len(b.Bytes)) - obj1} table2 := &flatbuffers.Table{b.Bytes, 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) }