Fixed LookupByKey for Java & C#

Change-Id: I05c02223675dee241d1ae8cb466e5186444058c8
Tested: on Linux.
This commit is contained in:
Wouter van Oortmerssen
2017-05-08 18:24:47 -07:00
parent 0920d663d5
commit 8468ea1ab4
6 changed files with 62 additions and 46 deletions

View File

@@ -147,19 +147,20 @@ To use it:
array. array.
- Instead of calling standard generated method, - Instead of calling standard generated method,
e.g.: `Monster.createTestarrayoftablesVector`, e.g.: `Monster.createTestarrayoftablesVector`,
call `CreateMySortedVectorOfTables` in C# or call `CreateSortedVectorOfMonster` in C# or
`createSortedVectorOfTables` (from the `FlatBufferBuilder` object) in Java, `createSortedVectorOfTables` (from the `FlatBufferBuilder` object) in Java,
which will first sort all offsets such that the tables they refer to which will first sort all offsets such that the tables they refer to
are sorted by the key field, then serialize it. are sorted by the key field, then serialize it.
- Now when you're accessing the FlatBuffer, you can use `LookupByKey` - Now when you're accessing the FlatBuffer, you can use
to access elements of the vector, e.g.: the `ByKey` accessor to access elements of the vector, e.g.:
`Monster.lookupByKey(tablesVectorOffset, "Frodo", dataBuffer)`, `monster.testarrayoftablesByKey("Frodo")` in Java or
`monster.TestarrayoftablesByKey("Frodo")` in C#,
which returns an object of the corresponding table type, which returns an object of the corresponding table type,
or `null` if not found. or `null` if not found.
`LookupByKey` performs a binary search, so should have a similar speed to `ByKey` performs a binary search, so should have a similar
`Dictionary`, though may be faster because of better caching. `LookupByKey` speed to `Dictionary`, though may be faster because of better caching.
only works if the vector has been sorted, it will likely not find elements `ByKey` only works if the vector has been sorted, it will
if it hasn't been sorted. likely not find elements if it hasn't been sorted.
## Text parsing ## Text parsing

View File

@@ -1005,6 +1005,26 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) {
code += lang_.accessor_prefix + "__vector_len(o) : 0; "; code += lang_.accessor_prefix + "__vector_len(o) : 0; ";
code += lang_.getter_suffix; code += lang_.getter_suffix;
code += "}\n"; code += "}\n";
// See if we should generate a by-key accessor.
if (field.value.type.element == BASE_TYPE_STRUCT &&
!field.value.type.struct_def->fixed) {
auto &sd = *field.value.type.struct_def;
auto &fields = sd.fields.vec;
for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
auto &key_field = **kit;
if (key_field.key) {
code += " public " + sd.name + lang_.optional_suffix + " ";
code += MakeCamel(field.name, lang_.first_camel_upper) + "ByKey(";
code += GenTypeNameDest(key_field.value.type) + " key)";
code += offset_prefix;
code += sd.name + ".__lookup_by_key(";
code += lang_.accessor_prefix + "__vector(o), key, ";
code += lang_.accessor_prefix + "bb) : null; ";
code += "}\n";
break;
}
}
}
} }
// Generate a ByteBuffer accessor for strings & vectors of scalars. // Generate a ByteBuffer accessor for strings & vectors of scalars.
if ((field.value.type.base_type == BASE_TYPE_VECTOR && if ((field.value.type.base_type == BASE_TYPE_VECTOR &&
@@ -1302,7 +1322,8 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) {
} }
else { else {
code += "\n public static VectorOffset "; code += "\n public static VectorOffset ";
code += "CreateMySortedVectorOfTables(FlatBufferBuilder builder, "; code += "CreateSortedVectorOf" + struct_def.name;
code += "(FlatBufferBuilder builder, ";
code += "Offset<" + struct_def.name + ">"; code += "Offset<" + struct_def.name + ">";
code += "[] offsets) {\n"; code += "[] offsets) {\n";
code += " Array.Sort(offsets, (Offset<" + struct_def.name + code += " Array.Sort(offsets, (Offset<" + struct_def.name +
@@ -1312,8 +1333,8 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) {
} }
code += "\n public static " + struct_def.name + lang_.optional_suffix; code += "\n public static " + struct_def.name + lang_.optional_suffix;
code += " " + FunctionStart('L') + "ookupByKey(" + GenVectorOffsetType(); code += " __lookup_by_key(int vectorLocation, ";
code += " vectorOffset, " + GenTypeNameDest(key_field->value.type); code += GenTypeNameDest(key_field->value.type);
code += " key, ByteBuffer bb) {\n"; code += " key, ByteBuffer bb) {\n";
if (key_field->value.type.base_type == BASE_TYPE_STRING) { if (key_field->value.type.base_type == BASE_TYPE_STRING) {
code += " byte[] byteKey = "; code += " byte[] byteKey = ";
@@ -1322,13 +1343,9 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) {
else else
code += "System.Text.Encoding.UTF8.GetBytes(key);\n"; code += "System.Text.Encoding.UTF8.GetBytes(key);\n";
} }
code += " int vectorLocation = " + GenByteBufferLength("bb"); code += " int span = ";
code += " - vectorOffset"; code += "bb." + FunctionStart('G') + "etInt(vectorLocation - 4);\n";
if (lang_.language == IDLOptions::kCSharp) code += ".Value";
code += ";\n int span = ";
code += "bb." + FunctionStart('G') + "etInt(vectorLocation);\n";
code += " int start = 0;\n"; code += " int start = 0;\n";
code += " vectorLocation += 4;\n";
code += " while (span != 0) {\n"; code += " while (span != 0) {\n";
code += " int middle = span / 2;\n"; code += " int middle = span / 2;\n";
code += GenLookupKeyGetter(key_field); code += GenLookupKeyGetter(key_field);

View File

@@ -50,7 +50,7 @@ namespace FlatBuffers.Test
Monster.StartMonster(fbb); Monster.StartMonster(fbb);
Monster.AddName(fbb, names[2]); Monster.AddName(fbb, names[2]);
off[2] = Monster.EndMonster(fbb); off[2] = Monster.EndMonster(fbb);
var sortMons = Monster.CreateMySortedVectorOfTables(fbb, off); var sortMons = Monster.CreateSortedVectorOfMonster(fbb, off);
// We set up the same values as monsterdata.json: // We set up the same values as monsterdata.json:
@@ -123,9 +123,9 @@ namespace FlatBuffers.Test
Assert.AreEqual(monster.Testarrayoftables(2).Value.Name, "Wilma"); Assert.AreEqual(monster.Testarrayoftables(2).Value.Name, "Wilma");
// Example of searching for a table by the key // Example of searching for a table by the key
Assert.IsTrue(Monster.LookupByKey(sortMons, "Frodo", fbb.DataBuffer) != null); Assert.IsTrue(monster.TestarrayoftablesByKey("Frodo") != null);
Assert.IsTrue(Monster.LookupByKey(sortMons, "Barney", fbb.DataBuffer) != null); Assert.IsTrue(monster.TestarrayoftablesByKey("Barney") != null);
Assert.IsTrue(Monster.LookupByKey(sortMons, "Wilma", fbb.DataBuffer)!= null); Assert.IsTrue(monster.TestarrayoftablesByKey("Wilma") != null);
// testType is an existing field and mutating it should succeed // testType is an existing field and mutating it should succeed
Assert.AreEqual(monster.TestType, Any.Monster); Assert.AreEqual(monster.TestType, Any.Monster);

View File

@@ -142,9 +142,9 @@ class JavaTest {
TestEq(monster.testarrayoftables(2).name(), "Wilma"); TestEq(monster.testarrayoftables(2).name(), "Wilma");
// Example of searching for a table by the key // Example of searching for a table by the key
TestEq(Monster.lookupByKey(sortMons, "Frodo", fbb.dataBuffer()).name(), "Frodo"); TestEq(monster.testarrayoftablesByKey("Frodo").name(), "Frodo");
TestEq(Monster.lookupByKey(sortMons, "Barney", fbb.dataBuffer()).name(), "Barney"); TestEq(monster.testarrayoftablesByKey("Barney").name(), "Barney");
TestEq(Monster.lookupByKey(sortMons, "Wilma", fbb.dataBuffer()).name(), "Wilma"); TestEq(monster.testarrayoftablesByKey("Wilma").name(), "Wilma");
// testType is an existing field and mutating it should succeed // testType is an existing field and mutating it should succeed
TestEq(monster.testType(), (byte)Any.Monster); TestEq(monster.testType(), (byte)Any.Monster);

View File

@@ -41,6 +41,7 @@ public struct Monster : IFlatbufferObject
/// multiline too /// multiline too
public Monster? Testarrayoftables(int j) { int o = __p.__offset(26); return o != 0 ? (Monster?)(new Monster()).__assign(__p.__indirect(__p.__vector(o) + j * 4), __p.bb) : null; } public Monster? Testarrayoftables(int j) { int o = __p.__offset(26); return o != 0 ? (Monster?)(new Monster()).__assign(__p.__indirect(__p.__vector(o) + j * 4), __p.bb) : null; }
public int TestarrayoftablesLength { get { int o = __p.__offset(26); return o != 0 ? __p.__vector_len(o) : 0; } } public int TestarrayoftablesLength { get { int o = __p.__offset(26); return o != 0 ? __p.__vector_len(o) : 0; } }
public Monster? TestarrayoftablesByKey(string key) { int o = __p.__offset(26); return o != 0 ? Monster.__lookup_by_key(__p.__vector(o), key, __p.bb) : null; }
public Monster? Enemy { get { int o = __p.__offset(28); return o != 0 ? (Monster?)(new Monster()).__assign(__p.__indirect(o + __p.bb_pos), __p.bb) : null; } } public Monster? Enemy { get { int o = __p.__offset(28); return o != 0 ? (Monster?)(new Monster()).__assign(__p.__indirect(o + __p.bb_pos), __p.bb) : null; } }
public byte Testnestedflatbuffer(int j) { int o = __p.__offset(30); return o != 0 ? __p.bb.Get(__p.__vector(o) + j * 1) : (byte)0; } public byte Testnestedflatbuffer(int j) { int o = __p.__offset(30); return o != 0 ? __p.bb.Get(__p.__vector(o) + j * 1) : (byte)0; }
public int TestnestedflatbufferLength { get { int o = __p.__offset(30); return o != 0 ? __p.__vector_len(o) : 0; } } public int TestnestedflatbufferLength { get { int o = __p.__offset(30); return o != 0 ? __p.__vector_len(o) : 0; } }
@@ -132,17 +133,15 @@ public struct Monster : IFlatbufferObject
} }
public static void FinishMonsterBuffer(FlatBufferBuilder builder, Offset<Monster> offset) { builder.Finish(offset.Value, "MONS"); } public static void FinishMonsterBuffer(FlatBufferBuilder builder, Offset<Monster> offset) { builder.Finish(offset.Value, "MONS"); }
public static VectorOffset CreateMySortedVectorOfTables(FlatBufferBuilder builder, Offset<Monster>[] offsets) { public static VectorOffset CreateSortedVectorOfMonster(FlatBufferBuilder builder, Offset<Monster>[] offsets) {
Array.Sort(offsets, (Offset<Monster> o1, Offset<Monster> o2) => Table.CompareStrings(Table.__offset(10, o1.Value, builder.DataBuffer), Table.__offset(10, o2.Value, builder.DataBuffer), builder.DataBuffer)); Array.Sort(offsets, (Offset<Monster> o1, Offset<Monster> o2) => Table.CompareStrings(Table.__offset(10, o1.Value, builder.DataBuffer), Table.__offset(10, o2.Value, builder.DataBuffer), builder.DataBuffer));
return builder.CreateVectorOfTables(offsets); return builder.CreateVectorOfTables(offsets);
} }
public static Monster? LookupByKey(VectorOffset vectorOffset, string key, ByteBuffer bb) { public static Monster? __lookup_by_key(int vectorLocation, string key, ByteBuffer bb) {
byte[] byteKey = System.Text.Encoding.UTF8.GetBytes(key); byte[] byteKey = System.Text.Encoding.UTF8.GetBytes(key);
int vectorLocation = bb.Length - vectorOffset.Value; int span = bb.GetInt(vectorLocation - 4);
int span = bb.GetInt(vectorLocation);
int start = 0; int start = 0;
vectorLocation += 4;
while (span != 0) { while (span != 0) {
int middle = span / 2; int middle = span / 2;
int tableOffset = Table.__indirect(vectorLocation + 4 * (start + middle), bb); int tableOffset = Table.__indirect(vectorLocation + 4 * (start + middle), bb);

View File

@@ -47,6 +47,7 @@ public final class Monster extends Table {
public Monster testarrayoftables(int j) { return testarrayoftables(new Monster(), j); } public Monster testarrayoftables(int j) { return testarrayoftables(new Monster(), j); }
public Monster testarrayoftables(Monster obj, int j) { int o = __offset(26); return o != 0 ? obj.__assign(__indirect(__vector(o) + j * 4), bb) : null; } public Monster testarrayoftables(Monster obj, int j) { int o = __offset(26); return o != 0 ? obj.__assign(__indirect(__vector(o) + j * 4), bb) : null; }
public int testarrayoftablesLength() { int o = __offset(26); return o != 0 ? __vector_len(o) : 0; } public int testarrayoftablesLength() { int o = __offset(26); return o != 0 ? __vector_len(o) : 0; }
public Monster testarrayoftablesByKey(String key) { int o = __offset(26); return o != 0 ? Monster.__lookup_by_key(__vector(o), key, bb) : null; }
public Monster enemy() { return enemy(new Monster()); } public Monster enemy() { return enemy(new Monster()); }
public Monster enemy(Monster obj) { int o = __offset(28); return o != 0 ? obj.__assign(__indirect(o + bb_pos), bb) : null; } public Monster enemy(Monster obj) { int o = __offset(28); return o != 0 ? obj.__assign(__indirect(o + bb_pos), bb) : null; }
public int testnestedflatbuffer(int j) { int o = __offset(30); return o != 0 ? bb.get(__vector(o) + j * 1) & 0xFF : 0; } public int testnestedflatbuffer(int j) { int o = __offset(30); return o != 0 ? bb.get(__vector(o) + j * 1) & 0xFF : 0; }
@@ -145,12 +146,10 @@ public final class Monster extends Table {
@Override @Override
protected int keysCompare(Integer o1, Integer o2, ByteBuffer _bb) { return compareStrings(__offset(10, o1, _bb), __offset(10, o2, _bb), _bb); } protected int keysCompare(Integer o1, Integer o2, ByteBuffer _bb) { return compareStrings(__offset(10, o1, _bb), __offset(10, o2, _bb), _bb); }
public static Monster lookupByKey(int vectorOffset, String key, ByteBuffer bb) { public static Monster __lookup_by_key(int vectorLocation, String key, ByteBuffer bb) {
byte[] byteKey = key.getBytes(Table.UTF8_CHARSET.get()); byte[] byteKey = key.getBytes(Table.UTF8_CHARSET.get());
int vectorLocation = bb.array().length - vectorOffset; int span = bb.getInt(vectorLocation - 4);
int span = bb.getInt(vectorLocation);
int start = 0; int start = 0;
vectorLocation += 4;
while (span != 0) { while (span != 0) {
int middle = span / 2; int middle = span / 2;
int tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb); int tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb);