diff --git a/docs/source/JavaCsharpUsage.md b/docs/source/JavaCsharpUsage.md index cc58f8562..8f99d3a88 100755 --- a/docs/source/JavaCsharpUsage.md +++ b/docs/source/JavaCsharpUsage.md @@ -147,19 +147,20 @@ To use it: array. - Instead of calling standard generated method, e.g.: `Monster.createTestarrayoftablesVector`, - call `CreateMySortedVectorOfTables` in C# or + call `CreateSortedVectorOfMonster` in C# or `createSortedVectorOfTables` (from the `FlatBufferBuilder` object) in Java, which will first sort all offsets such that the tables they refer to are sorted by the key field, then serialize it. -- Now when you're accessing the FlatBuffer, you can use `LookupByKey` - to access elements of the vector, e.g.: - `Monster.lookupByKey(tablesVectorOffset, "Frodo", dataBuffer)`, +- Now when you're accessing the FlatBuffer, you can use + the `ByKey` accessor to access elements of the vector, e.g.: + `monster.testarrayoftablesByKey("Frodo")` in Java or + `monster.TestarrayoftablesByKey("Frodo")` in C#, which returns an object of the corresponding table type, or `null` if not found. - `LookupByKey` performs a binary search, so should have a similar speed to - `Dictionary`, though may be faster because of better caching. `LookupByKey` - only works if the vector has been sorted, it will likely not find elements - if it hasn't been sorted. + `ByKey` performs a binary search, so should have a similar + speed to `Dictionary`, though may be faster because of better caching. + `ByKey` only works if the vector has been sorted, it will + likely not find elements if it hasn't been sorted. ## Text parsing diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index 5e5b1e229..248d57d65 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -1005,6 +1005,26 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { code += lang_.accessor_prefix + "__vector_len(o) : 0; "; code += lang_.getter_suffix; 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. if ((field.value.type.base_type == BASE_TYPE_VECTOR && @@ -1302,7 +1322,8 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { } else { code += "\n public static VectorOffset "; - code += "CreateMySortedVectorOfTables(FlatBufferBuilder builder, "; + code += "CreateSortedVectorOf" + struct_def.name; + code += "(FlatBufferBuilder builder, "; code += "Offset<" + struct_def.name + ">"; code += "[] offsets) {\n"; 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 += " " + FunctionStart('L') + "ookupByKey(" + GenVectorOffsetType(); - code += " vectorOffset, " + GenTypeNameDest(key_field->value.type); + code += " __lookup_by_key(int vectorLocation, "; + code += GenTypeNameDest(key_field->value.type); code += " key, ByteBuffer bb) {\n"; if (key_field->value.type.base_type == BASE_TYPE_STRING) { code += " byte[] byteKey = "; @@ -1322,13 +1343,9 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { else code += "System.Text.Encoding.UTF8.GetBytes(key);\n"; } - code += " int vectorLocation = " + GenByteBufferLength("bb"); - code += " - vectorOffset"; - if (lang_.language == IDLOptions::kCSharp) code += ".Value"; - code += ";\n int span = "; - code += "bb." + FunctionStart('G') + "etInt(vectorLocation);\n"; + code += " int span = "; + code += "bb." + FunctionStart('G') + "etInt(vectorLocation - 4);\n"; code += " int start = 0;\n"; - code += " vectorLocation += 4;\n"; code += " while (span != 0) {\n"; code += " int middle = span / 2;\n"; code += GenLookupKeyGetter(key_field); diff --git a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs index d032d7e46..ccd5b05de 100644 --- a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs +++ b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs @@ -50,7 +50,7 @@ namespace FlatBuffers.Test Monster.StartMonster(fbb); Monster.AddName(fbb, names[2]); 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: @@ -123,9 +123,9 @@ namespace FlatBuffers.Test Assert.AreEqual(monster.Testarrayoftables(2).Value.Name, "Wilma"); // Example of searching for a table by the key - Assert.IsTrue(Monster.LookupByKey(sortMons, "Frodo", fbb.DataBuffer) != null); - Assert.IsTrue(Monster.LookupByKey(sortMons, "Barney", fbb.DataBuffer) != null); - Assert.IsTrue(Monster.LookupByKey(sortMons, "Wilma", fbb.DataBuffer)!= null); + Assert.IsTrue(monster.TestarrayoftablesByKey("Frodo") != null); + Assert.IsTrue(monster.TestarrayoftablesByKey("Barney") != null); + Assert.IsTrue(monster.TestarrayoftablesByKey("Wilma") != null); // testType is an existing field and mutating it should succeed Assert.AreEqual(monster.TestType, Any.Monster); diff --git a/tests/JavaTest.java b/tests/JavaTest.java index d53e97327..57fe6fb62 100755 --- a/tests/JavaTest.java +++ b/tests/JavaTest.java @@ -63,7 +63,7 @@ class JavaTest { Monster.addName(fbb, names[2]); off[2] = Monster.endMonster(fbb); int sortMons = fbb.createSortedVectorOfTables(new Monster(), off); - + // We set up the same values as monsterdata.json: int str = fbb.createString("MyMonster"); @@ -135,16 +135,16 @@ class JavaTest { // the mana field should retain its default value TestEq(monster.mutateMana((short)10), false); TestEq(monster.mana(), (short)150); - + // Accessing a vector of sorted by the key tables TestEq(monster.testarrayoftables(0).name(), "Barney"); TestEq(monster.testarrayoftables(1).name(), "Frodo"); TestEq(monster.testarrayoftables(2).name(), "Wilma"); - + // Example of searching for a table by the key - TestEq(Monster.lookupByKey(sortMons, "Frodo", fbb.dataBuffer()).name(), "Frodo"); - TestEq(Monster.lookupByKey(sortMons, "Barney", fbb.dataBuffer()).name(), "Barney"); - TestEq(Monster.lookupByKey(sortMons, "Wilma", fbb.dataBuffer()).name(), "Wilma"); + TestEq(monster.testarrayoftablesByKey("Frodo").name(), "Frodo"); + TestEq(monster.testarrayoftablesByKey("Barney").name(), "Barney"); + TestEq(monster.testarrayoftablesByKey("Wilma").name(), "Wilma"); // testType is an existing field and mutating it should succeed TestEq(monster.testType(), (byte)Any.Monster); @@ -201,7 +201,7 @@ class JavaTest { static void TestBuffer(ByteBuffer bb) { TestEq(Monster.MonsterBufferHasIdentifier(bb), true); - + Monster monster = Monster.getRootAsMonster(bb); TestEq(monster.hp(), (short)80); @@ -259,25 +259,25 @@ class JavaTest { TestEq(monster.testhashu32Fnv1(), Integer.MAX_VALUE + 1L); } - + static void TestNamespaceNesting() { // reference / manipulate these to verify compilation FlatBufferBuilder fbb = new FlatBufferBuilder(1); - + TableInNestedNS.startTableInNestedNS(fbb); TableInNestedNS.addFoo(fbb, 1234); int nestedTableOff = TableInNestedNS.endTableInNestedNS(fbb); - - TableInFirstNS.startTableInFirstNS(fbb); + + TableInFirstNS.startTableInFirstNS(fbb); TableInFirstNS.addFooTable(fbb, nestedTableOff); int off = TableInFirstNS.endTableInFirstNS(fbb); } - + static void TestNestedFlatBuffer() { final String nestedMonsterName = "NestedMonsterName"; final short nestedMonsterHp = 600; final short nestedMonsterMana = 1024; - + FlatBufferBuilder fbb1 = new FlatBufferBuilder(16); int str1 = fbb1.createString(nestedMonsterName); Monster.startMonster(fbb1); @@ -288,8 +288,8 @@ class JavaTest { Monster.finishMonsterBuffer(fbb1, monster1); byte[] fbb1Bytes = fbb1.sizedByteArray(); fbb1 = null; - - FlatBufferBuilder fbb2 = new FlatBufferBuilder(16); + + FlatBufferBuilder fbb2 = new FlatBufferBuilder(16); int str2 = fbb2.createString("My Monster"); int nestedBuffer = Monster.createTestnestedflatbufferVector(fbb2, fbb1Bytes); Monster.startMonster(fbb2); @@ -299,7 +299,7 @@ class JavaTest { Monster.addTestnestedflatbuffer(fbb2, nestedBuffer); int monster = Monster.endMonster(fbb2); Monster.finishMonsterBuffer(fbb2, monster); - + // Now test the data extracted from the nested buffer Monster mons = Monster.getRootAsMonster(fbb2.dataBuffer()); Monster nestedMonster = mons.testnestedflatbufferAsMonster(); diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs index 8f76cfd8a..6eee25dc9 100644 --- a/tests/MyGame/Example/Monster.cs +++ b/tests/MyGame/Example/Monster.cs @@ -41,6 +41,7 @@ public struct Monster : IFlatbufferObject /// 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 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 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; } } @@ -132,17 +133,15 @@ public struct Monster : IFlatbufferObject } public static void FinishMonsterBuffer(FlatBufferBuilder builder, Offset offset) { builder.Finish(offset.Value, "MONS"); } - public static VectorOffset CreateMySortedVectorOfTables(FlatBufferBuilder builder, Offset[] offsets) { + public static VectorOffset CreateSortedVectorOfMonster(FlatBufferBuilder builder, Offset[] offsets) { Array.Sort(offsets, (Offset o1, Offset o2) => Table.CompareStrings(Table.__offset(10, o1.Value, builder.DataBuffer), Table.__offset(10, o2.Value, builder.DataBuffer), builder.DataBuffer)); 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); - int vectorLocation = bb.Length - vectorOffset.Value; - int span = bb.GetInt(vectorLocation); + int span = bb.GetInt(vectorLocation - 4); int start = 0; - vectorLocation += 4; while (span != 0) { int middle = span / 2; int tableOffset = Table.__indirect(vectorLocation + 4 * (start + middle), bb); diff --git a/tests/MyGame/Example/Monster.java b/tests/MyGame/Example/Monster.java index 5bde80a3f..8ccbe3daf 100644 --- a/tests/MyGame/Example/Monster.java +++ b/tests/MyGame/Example/Monster.java @@ -47,6 +47,7 @@ public final class Monster extends Table { 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 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(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; } @@ -145,12 +146,10 @@ public final class Monster extends Table { @Override 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()); - int vectorLocation = bb.array().length - vectorOffset; - int span = bb.getInt(vectorLocation); + int span = bb.getInt(vectorLocation - 4); int start = 0; - vectorLocation += 4; while (span != 0) { int middle = span / 2; int tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb);