mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-23 17:00:03 +00:00
Add key lookup support for tables in Go (#7644)
* Add support for key lookup for tables in Go * Run clang format * Run go fmt on tests * Remove TODO in tests * Update LookupByKey API * Update LookupByKey API * Don't use resolvePointer in expectEq * Use generated getters instead of reading values directly from buffer * Fix typo Co-authored-by: Derek Bailey <derekbailey@google.com>
This commit is contained in:
@@ -4,7 +4,6 @@ package Example
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
|
||||
MyGame__Example2 "MyGame/Example2"
|
||||
|
||||
@@ -4,7 +4,6 @@ package Example
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ package Example
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
|
||||
MyGame__Example2 "MyGame/Example2"
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
package Example
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
|
||||
MyGame "MyGame"
|
||||
@@ -568,6 +568,38 @@ func (rcv *Monster) Name() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func MonsterKeyCompare(o1, o2 flatbuffers.UOffsetT, buf []byte) bool {
|
||||
obj1 := &Monster{}
|
||||
obj2 := &Monster{}
|
||||
obj1.Init(buf, flatbuffers.UOffsetT(len(buf)) - o1)
|
||||
obj2.Init(buf, flatbuffers.UOffsetT(len(buf)) - o2)
|
||||
return string(obj1.Name()) < string(obj2.Name())
|
||||
}
|
||||
|
||||
func (rcv *Monster) LookupByKey(key string, vectorLocation flatbuffers.UOffsetT, buf []byte) bool {
|
||||
span := flatbuffers.GetUOffsetT(buf[vectorLocation - 4:])
|
||||
start := flatbuffers.UOffsetT(0)
|
||||
for span != 0 {
|
||||
middle := span / 2
|
||||
tableOffset := flatbuffers.GetIndirectOffset(buf, vectorLocation+ 4 * (start + middle))
|
||||
obj := &Monster{}
|
||||
obj.Init(buf, tableOffset)
|
||||
bKey := []byte(key)
|
||||
comp := bytes.Compare(obj.Name(), bKey)
|
||||
if comp > 0 {
|
||||
span = middle
|
||||
} else if comp < 0 {
|
||||
middle += 1
|
||||
start += middle
|
||||
span -= middle
|
||||
} else {
|
||||
rcv.Init(buf, tableOffset)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (rcv *Monster) Inventory(j int) byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(14))
|
||||
if o != 0 {
|
||||
@@ -685,6 +717,15 @@ func (rcv *Monster) Testarrayoftables(obj *Monster, j int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (rcv *Monster) TestarrayoftablesByKey(obj *Monster, key string) bool{
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(26))
|
||||
if o != 0 {
|
||||
x := rcv._tab.Vector(o)
|
||||
return obj.LookupByKey(key, x, rcv._tab.Bytes)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (rcv *Monster) TestarrayoftablesLength() int {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(26))
|
||||
if o != 0 {
|
||||
@@ -1091,6 +1132,15 @@ func (rcv *Monster) VectorOfReferrables(obj *Referrable, j int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (rcv *Monster) VectorOfReferrablesByKey(obj *Referrable, key uint64) bool{
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(74))
|
||||
if o != 0 {
|
||||
x := rcv._tab.Vector(o)
|
||||
return obj.LookupByKey(key, x, rcv._tab.Bytes)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (rcv *Monster) VectorOfReferrablesLength() int {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(74))
|
||||
if o != 0 {
|
||||
@@ -1149,6 +1199,15 @@ func (rcv *Monster) VectorOfStrongReferrables(obj *Referrable, j int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (rcv *Monster) VectorOfStrongReferrablesByKey(obj *Referrable, key uint64) bool{
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(80))
|
||||
if o != 0 {
|
||||
x := rcv._tab.Vector(o)
|
||||
return obj.LookupByKey(key, x, rcv._tab.Bytes)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (rcv *Monster) VectorOfStrongReferrablesLength() int {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(80))
|
||||
if o != 0 {
|
||||
@@ -1367,6 +1426,15 @@ func (rcv *Monster) ScalarKeySortedTables(obj *Stat, j int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (rcv *Monster) ScalarKeySortedTablesByKey(obj *Stat, key uint16) bool{
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(104))
|
||||
if o != 0 {
|
||||
x := rcv._tab.Vector(o)
|
||||
return obj.LookupByKey(key, x, rcv._tab.Bytes)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (rcv *Monster) ScalarKeySortedTablesLength() int {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(104))
|
||||
if o != 0 {
|
||||
|
||||
@@ -67,6 +67,43 @@ func (rcv *Referrable) MutateId(n uint64) bool {
|
||||
return rcv._tab.MutateUint64Slot(4, n)
|
||||
}
|
||||
|
||||
func ReferrableKeyCompare(o1, o2 flatbuffers.UOffsetT, buf []byte) bool {
|
||||
obj1 := &Referrable{}
|
||||
obj2 := &Referrable{}
|
||||
obj1.Init(buf, flatbuffers.UOffsetT(len(buf)) - o1)
|
||||
obj2.Init(buf, flatbuffers.UOffsetT(len(buf)) - o2)
|
||||
return obj1.Id() < obj2.Id()
|
||||
}
|
||||
|
||||
func (rcv *Referrable) LookupByKey(key uint64, vectorLocation flatbuffers.UOffsetT, buf []byte) bool {
|
||||
span := flatbuffers.GetUOffsetT(buf[vectorLocation - 4:])
|
||||
start := flatbuffers.UOffsetT(0)
|
||||
for span != 0 {
|
||||
middle := span / 2
|
||||
tableOffset := flatbuffers.GetIndirectOffset(buf, vectorLocation+ 4 * (start + middle))
|
||||
obj := &Referrable{}
|
||||
obj.Init(buf, tableOffset)
|
||||
val := obj.Id()
|
||||
comp := 0
|
||||
if val > key {
|
||||
comp = 1
|
||||
} else if val < key {
|
||||
comp = -1
|
||||
}
|
||||
if comp > 0 {
|
||||
span = middle
|
||||
} else if comp < 0 {
|
||||
middle += 1
|
||||
start += middle
|
||||
span -= middle
|
||||
} else {
|
||||
rcv.Init(buf, tableOffset)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ReferrableStart(builder *flatbuffers.Builder) {
|
||||
builder.StartObject(1)
|
||||
}
|
||||
|
||||
@@ -94,6 +94,43 @@ func (rcv *Stat) MutateCount(n uint16) bool {
|
||||
return rcv._tab.MutateUint16Slot(8, n)
|
||||
}
|
||||
|
||||
func StatKeyCompare(o1, o2 flatbuffers.UOffsetT, buf []byte) bool {
|
||||
obj1 := &Stat{}
|
||||
obj2 := &Stat{}
|
||||
obj1.Init(buf, flatbuffers.UOffsetT(len(buf)) - o1)
|
||||
obj2.Init(buf, flatbuffers.UOffsetT(len(buf)) - o2)
|
||||
return obj1.Count() < obj2.Count()
|
||||
}
|
||||
|
||||
func (rcv *Stat) LookupByKey(key uint16, vectorLocation flatbuffers.UOffsetT, buf []byte) bool {
|
||||
span := flatbuffers.GetUOffsetT(buf[vectorLocation - 4:])
|
||||
start := flatbuffers.UOffsetT(0)
|
||||
for span != 0 {
|
||||
middle := span / 2
|
||||
tableOffset := flatbuffers.GetIndirectOffset(buf, vectorLocation+ 4 * (start + middle))
|
||||
obj := &Stat{}
|
||||
obj.Init(buf, tableOffset)
|
||||
val := obj.Count()
|
||||
comp := 0
|
||||
if val > key {
|
||||
comp = 1
|
||||
} else if val < key {
|
||||
comp = -1
|
||||
}
|
||||
if comp > 0 {
|
||||
span = middle
|
||||
} else if comp < 0 {
|
||||
middle += 1
|
||||
start += middle
|
||||
span -= middle
|
||||
} else {
|
||||
rcv.Init(buf, tableOffset)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func StatStart(builder *flatbuffers.Builder) {
|
||||
builder.StartObject(3)
|
||||
}
|
||||
|
||||
@@ -186,9 +186,12 @@ func TestAll(t *testing.T) {
|
||||
// Check size-prefixed flatbuffers
|
||||
CheckSizePrefixedBuffer(t.Fatalf)
|
||||
|
||||
// Check that optional scalars work
|
||||
// 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:
|
||||
@@ -2215,6 +2218,78 @@ func CheckOptionalScalars(fail func(string, ...interface{})) {
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user