// 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")
}
Beispiel #2
0
// 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)
}
Beispiel #3
0
func BenchmarkMarshalByFlatBuffers(b *testing.B) {
	builder := flatbuffers.NewBuilder(0)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		serializeByFlatBuffers(builder, &group)
	}
}
Beispiel #4
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()
	}
}
Beispiel #5
0
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)
}
Beispiel #6
0
// 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
}
Beispiel #9
0
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))
}
Beispiel #11
0
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)
	}
}
Beispiel #12
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()
}
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))])
	}
}
Beispiel #14
0
// 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)
}
Beispiel #15
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)))
		}
	}
}
Beispiel #16
0
// 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)
}
Beispiel #17
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"))
}
Beispiel #18
0
// 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()
}
Beispiel #19
0
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)
	}
}
Beispiel #20
0
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)
		}
	}
}
Beispiel #21
0
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"))
	}
}
Beispiel #22
0
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():]
		}
	})
}
Beispiel #23
0
// 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()
}
Beispiel #24
0
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()
}
Beispiel #25
0
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))
	}
}
Beispiel #26
0
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()
	}
}
Beispiel #27
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()
}
Beispiel #28
0
// 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
	})
}
Beispiel #29
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
}
Beispiel #30
0
// 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)
}