commit
4d213c2d06
|
@ -31,11 +31,38 @@ func NewBuilder(initialSize int) *Builder {
|
|||
return b
|
||||
}
|
||||
|
||||
// Reset truncates the underlying Builder buffer, facilitating alloc-free
|
||||
// reuse of a Builder.
|
||||
func (b *Builder) Reset() {
|
||||
if b.Bytes != nil {
|
||||
b.Bytes = b.Bytes[:cap(b.Bytes)]
|
||||
}
|
||||
|
||||
if b.vtables != nil {
|
||||
b.vtables = b.vtables[:0]
|
||||
}
|
||||
|
||||
if b.vtable != nil {
|
||||
b.vtable = b.vtable[:0]
|
||||
}
|
||||
|
||||
b.head = UOffsetT(len(b.Bytes))
|
||||
b.minalign = 1
|
||||
}
|
||||
|
||||
// StartObject initializes bookkeeping for writing a new object.
|
||||
func (b *Builder) StartObject(numfields int) {
|
||||
b.notNested()
|
||||
// use 32-bit offsets so that arithmetic doesn't overflow.
|
||||
b.vtable = make([]UOffsetT, numfields)
|
||||
if cap(b.vtable) < numfields || b.vtable == nil {
|
||||
b.vtable = make([]UOffsetT, numfields)
|
||||
} else {
|
||||
b.vtable = b.vtable[:numfields]
|
||||
for i := 0; i < len(b.vtable); i++ {
|
||||
b.vtable[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
b.objectEnd = b.Offset()
|
||||
b.minalign = 1
|
||||
}
|
||||
|
@ -137,7 +164,7 @@ func (b *Builder) WriteVtable() (n UOffsetT) {
|
|||
SOffsetT(existingVtable)-SOffsetT(objectOffset))
|
||||
}
|
||||
|
||||
b.vtable = nil
|
||||
b.vtable = b.vtable[:0]
|
||||
return objectOffset
|
||||
}
|
||||
|
||||
|
@ -155,13 +182,20 @@ func (b *Builder) growByteBuffer() {
|
|||
if (int64(len(b.Bytes)) & int64(0xC0000000)) != 0 {
|
||||
panic("cannot grow buffer beyond 2 gigabytes")
|
||||
}
|
||||
newSize := len(b.Bytes) * 2
|
||||
if newSize == 0 {
|
||||
newSize = 1
|
||||
newLen := len(b.Bytes) * 2
|
||||
if newLen == 0 {
|
||||
newLen = 1
|
||||
}
|
||||
bytes2 := make([]byte, newSize)
|
||||
copy(bytes2[newSize-len(b.Bytes):], b.Bytes)
|
||||
b.Bytes = bytes2
|
||||
|
||||
if cap(b.Bytes) >= newLen {
|
||||
b.Bytes = b.Bytes[:newLen]
|
||||
} else {
|
||||
extension := make([]byte, newLen-len(b.Bytes))
|
||||
b.Bytes = append(b.Bytes, extension...)
|
||||
}
|
||||
|
||||
middle := newLen / 2
|
||||
copy(b.Bytes[middle:], b.Bytes[:middle])
|
||||
}
|
||||
|
||||
// Head gives the start of useful data in the underlying byte buffer.
|
||||
|
@ -247,16 +281,20 @@ func (b *Builder) EndVector(vectorNumElems int) UOffsetT {
|
|||
|
||||
// CreateString writes a null-terminated string as a vector.
|
||||
func (b *Builder) CreateString(s string) UOffsetT {
|
||||
return b.CreateByteString([]byte(s))
|
||||
}
|
||||
|
||||
// CreateByteString writes a byte slice as a string (null-terminated).
|
||||
func (b *Builder) CreateByteString(s []byte) UOffsetT {
|
||||
b.Prep(int(SizeUOffsetT), (len(s)+1)*SizeByte)
|
||||
b.PlaceByte(0)
|
||||
|
||||
x := []byte(s)
|
||||
l := UOffsetT(len(x))
|
||||
l := UOffsetT(len(s))
|
||||
|
||||
b.head -= l
|
||||
copy(b.Bytes[b.head:b.head+l], x)
|
||||
copy(b.Bytes[b.head:b.head+l], s)
|
||||
|
||||
return b.EndVector(len(x))
|
||||
return b.EndVector(len(s))
|
||||
}
|
||||
|
||||
// CreateByteVector writes a ubyte vector
|
||||
|
@ -274,7 +312,7 @@ func (b *Builder) CreateByteVector(v []byte) UOffsetT {
|
|||
func (b *Builder) notNested() {
|
||||
// Check that no other objects are being built while making this
|
||||
// object. If not, panic:
|
||||
if b.vtable != nil {
|
||||
if len(b.vtable) > 0 {
|
||||
panic("non-inline data write inside of object")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -238,7 +238,7 @@ static void GetStringField(const StructDef &struct_def,
|
|||
code += " " + MakeCamel(field.name);
|
||||
code += "() " + TypeName(field) + " ";
|
||||
code += OffsetPrefix(field) + "\t\treturn " + GenGetter(field.value.type);
|
||||
code += "(o + rcv._tab.Pos)\n\t}\n\treturn \"\"\n";
|
||||
code += "(o + rcv._tab.Pos)\n\t}\n\treturn nil\n";
|
||||
code += "}\n\n";
|
||||
}
|
||||
|
||||
|
@ -301,7 +301,7 @@ static void GetMemberOfVectorOfNonStruct(const StructDef &struct_def,
|
|||
code += NumToString(InlineSize(vectortype)) + "))\n";
|
||||
code += "\t}\n";
|
||||
if (vectortype.base_type == BASE_TYPE_STRING) {
|
||||
code += "\treturn \"\"\n";
|
||||
code += "\treturn nil\n";
|
||||
} else {
|
||||
code += "\treturn 0\n";
|
||||
}
|
||||
|
@ -573,7 +573,7 @@ static void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
|
|||
// Returns the function name that is able to read a value of the given type.
|
||||
static std::string GenGetter(const Type &type) {
|
||||
switch (type.base_type) {
|
||||
case BASE_TYPE_STRING: return "rcv._tab.String";
|
||||
case BASE_TYPE_STRING: return "rcv._tab.ByteVector";
|
||||
case BASE_TYPE_UNION: return "rcv._tab.Union";
|
||||
case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
|
||||
default:
|
||||
|
@ -626,7 +626,7 @@ static std::string GenTypeBasic(const Type &type) {
|
|||
static std::string GenTypePointer(const Type &type) {
|
||||
switch (type.base_type) {
|
||||
case BASE_TYPE_STRING:
|
||||
return "string";
|
||||
return "[]byte";
|
||||
case BASE_TYPE_VECTOR:
|
||||
return GenTypeGet(type.VectorType());
|
||||
case BASE_TYPE_STRUCT:
|
||||
|
|
|
@ -971,6 +971,8 @@ void Parser::ParseDecl() {
|
|||
CheckClash("Type", BASE_TYPE_UNION);
|
||||
CheckClash("_length", BASE_TYPE_VECTOR);
|
||||
CheckClash("Length", BASE_TYPE_VECTOR);
|
||||
CheckClash("_byte_vector", BASE_TYPE_STRING);
|
||||
CheckClash("ByteVector", BASE_TYPE_STRING);
|
||||
Expect('}');
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,8 @@ GOPATH=${go_path} go test flatbuffers_test \
|
|||
--test.coverpkg=github.com/google/flatbuffers/go \
|
||||
--cpp_data=${test_dir}/monsterdata_test.mon \
|
||||
--out_data=${test_dir}/monsterdata_go_wire.mon \
|
||||
--test.bench=. \
|
||||
--test.benchtime=3s \
|
||||
--fuzz=true \
|
||||
--fuzz_fields=4 \
|
||||
--fuzz_objects=10000
|
||||
|
|
|
@ -50,12 +50,12 @@ func (rcv *Monster) Hp() int16 {
|
|||
return 100
|
||||
}
|
||||
|
||||
func (rcv *Monster) Name() string {
|
||||
func (rcv *Monster) Name() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(10))
|
||||
if o != 0 {
|
||||
return rcv._tab.String(o + rcv._tab.Pos)
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *Monster) Inventory(j int) byte {
|
||||
|
@ -130,13 +130,13 @@ func (rcv *Monster) Test4Length() int {
|
|||
return 0
|
||||
}
|
||||
|
||||
func (rcv *Monster) Testarrayofstring(j int) string {
|
||||
func (rcv *Monster) Testarrayofstring(j int) []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(24))
|
||||
if o != 0 {
|
||||
a := rcv._tab.Vector(o)
|
||||
return rcv._tab.String(a + flatbuffers.UOffsetT(j * 4))
|
||||
return rcv._tab.ByteVector(a + flatbuffers.UOffsetT(j * 4))
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *Monster) TestarrayofstringLength() int {
|
||||
|
|
|
@ -14,12 +14,12 @@ func (rcv *Stat) Init(buf []byte, i flatbuffers.UOffsetT) {
|
|||
rcv._tab.Pos = i
|
||||
}
|
||||
|
||||
func (rcv *Stat) Id() string {
|
||||
func (rcv *Stat) Id() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
|
||||
if o != 0 {
|
||||
return rcv._tab.String(o + rcv._tab.Pos)
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *Stat) Val() int64 {
|
||||
|
|
142
tests/go_test.go
142
tests/go_test.go
|
@ -132,7 +132,7 @@ func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string,
|
|||
fail(FailString("mana", 150, got))
|
||||
}
|
||||
|
||||
if got := monster.Name(); "MyMonster" != got {
|
||||
if got := monster.Name(); !bytes.Equal([]byte("MyMonster"), got) {
|
||||
fail(FailString("name", "MyMonster", got))
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string,
|
|||
var monster2 example.Monster
|
||||
monster2.Init(table2.Bytes, table2.Pos)
|
||||
|
||||
if got := monster2.Name(); "Fred" != got {
|
||||
if got := monster2.Name(); !bytes.Equal([]byte("Fred"), got) {
|
||||
fail(FailString("monster2.Name()", "Fred", got))
|
||||
}
|
||||
|
||||
|
@ -268,11 +268,11 @@ func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string,
|
|||
fail(FailString("Testarrayofstring length", 2, got))
|
||||
}
|
||||
|
||||
if got := monster.Testarrayofstring(0); "test1" != got {
|
||||
if got := monster.Testarrayofstring(0); !bytes.Equal([]byte("test1"), got) {
|
||||
fail(FailString("Testarrayofstring(0)", "test1", got))
|
||||
}
|
||||
|
||||
if got := monster.Testarrayofstring(1); "test2" != got {
|
||||
if got := monster.Testarrayofstring(1); !bytes.Equal([]byte("test2"), got) {
|
||||
fail(FailString("Testarrayofstring(1)", "test2", got))
|
||||
}
|
||||
}
|
||||
|
@ -533,6 +533,15 @@ func CheckByteLayout(fail func(string, ...interface{})) {
|
|||
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: 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)
|
||||
|
@ -1185,3 +1194,128 @@ func BenchmarkVtableDeduplication(b *testing.B) {
|
|||
builder.EndObject()
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkParseGold measures the speed of parsing the 'gold' data
|
||||
// used throughout this test suite.
|
||||
func BenchmarkParseGold(b *testing.B) {
|
||||
buf, offset := CheckGeneratedBuild(b.Fatalf)
|
||||
monster := example.GetRootAsMonster(buf, offset)
|
||||
|
||||
// use these to prevent allocations:
|
||||
reuse_pos := example.Vec3{}
|
||||
reuse_test3 := example.Test{}
|
||||
reuse_table2 := flatbuffers.Table{}
|
||||
reuse_monster2 := example.Monster{}
|
||||
reuse_test4_0 := example.Test{}
|
||||
reuse_test4_1 := example.Test{}
|
||||
|
||||
b.SetBytes(int64(len(buf[offset:])))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
monster.Hp()
|
||||
monster.Mana()
|
||||
name := monster.Name()
|
||||
_ = name[0]
|
||||
_ = name[len(name)-1]
|
||||
|
||||
monster.Pos(&reuse_pos)
|
||||
reuse_pos.X()
|
||||
reuse_pos.Y()
|
||||
reuse_pos.Z()
|
||||
reuse_pos.Test1()
|
||||
reuse_pos.Test2()
|
||||
reuse_pos.Test3(&reuse_test3)
|
||||
reuse_test3.A()
|
||||
reuse_test3.B()
|
||||
monster.TestType()
|
||||
monster.Test(&reuse_table2)
|
||||
reuse_monster2.Init(reuse_table2.Bytes, reuse_table2.Pos)
|
||||
name2 := reuse_monster2.Name()
|
||||
_ = name2[0]
|
||||
_ = name2[len(name2)-1]
|
||||
monster.InventoryLength()
|
||||
l := monster.InventoryLength()
|
||||
for i := 0; i < l; i++ {
|
||||
monster.Inventory(i)
|
||||
}
|
||||
monster.Test4Length()
|
||||
monster.Test4(&reuse_test4_0, 0)
|
||||
monster.Test4(&reuse_test4_1, 1)
|
||||
|
||||
reuse_test4_0.A()
|
||||
reuse_test4_0.B()
|
||||
reuse_test4_1.A()
|
||||
reuse_test4_1.B()
|
||||
|
||||
monster.TestarrayofstringLength()
|
||||
str0 := monster.Testarrayofstring(0)
|
||||
_ = str0[0]
|
||||
_ = str0[len(str0)-1]
|
||||
str1 := monster.Testarrayofstring(1)
|
||||
_ = str1[0]
|
||||
_ = str1[len(str1)-1]
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkBuildGold uses generated code to build the example Monster.
|
||||
func BenchmarkBuildGold(b *testing.B) {
|
||||
buf, offset := CheckGeneratedBuild(b.Fatalf)
|
||||
bytes_length := int64(len(buf[offset:]))
|
||||
|
||||
reuse_str := []byte("MyMonster")
|
||||
reuse_test1 := []byte("test1")
|
||||
reuse_test2 := []byte("test2")
|
||||
reuse_fred := []byte("Fred")
|
||||
|
||||
b.SetBytes(bytes_length)
|
||||
bldr := flatbuffers.NewBuilder(0)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bldr.Reset()
|
||||
|
||||
str := bldr.CreateByteString(reuse_str)
|
||||
test1 := bldr.CreateByteString(reuse_test1)
|
||||
test2 := bldr.CreateByteString(reuse_test2)
|
||||
fred := bldr.CreateByteString(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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue