From 38b38932110ccd0b533c92d520dce2cacf6532ff Mon Sep 17 00:00:00 2001 From: Raman Date: Sat, 23 Jul 2016 14:16:30 +0200 Subject: [PATCH 1/7] Update idl_gen_general.cpp --- src/idl_gen_general.cpp | 66 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index a96609c54..bd37fb12a 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -955,6 +955,7 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { } } code += "\n"; + flatbuffers::FieldDef *key_field = nullptr; if (struct_def.fixed) { // create a struct constructor function code += " public static " + GenOffsetType(struct_def) + " "; @@ -1042,6 +1043,7 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; + if (field.key) key_field = &field; if (field.deprecated) continue; code += " public static void " + FunctionStart('A') + "dd"; code += MakeCamel(field.name); @@ -1125,6 +1127,70 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { code += "); }\n"; } } + if (struct_def.has_key) { + std::string key_name = lang_.language == IDLOptions::kCSharp ? MakeCamel(key_field->name) : key_field->name + "()"; + std::string key_type = GenTypeGet(key_field->value.type); + + code += "\n public int " + FunctionStart('K') + "eyCompareLessThan(" + struct_def.name + " o) "; + if (key_field->value.type.base_type != BASE_TYPE_STRING) { + code += "{\n if (" + key_name + " < o." + key_name + ") return -1;\n"; + code += " else if (" + key_name + " == o." + key_name + ") return 0;\n"; + code += " else return 1;\n }\n"; + } + else { + code += "{ return " + key_name + "." + FunctionStart('C') + "ompareTo(o." + key_name + "); }\n"; + } + + code += " public int " + FunctionStart('K') + "eyCompareWithValue(" + key_type + " val) "; + if (key_field->value.type.base_type != BASE_TYPE_STRING) { + code += "{\n if (" + key_name + " < val) return -1;\n"; + code += " else if (" + key_name + " == val) return 0;\n"; + code += " else return 1;\n }\n"; + } + else { + code += "{ return " + key_name + "." + FunctionStart('C') + "ompareTo(val); }\n"; + } + + code += "\n public static " + struct_def.name + "[] " + FunctionStart('C') + "reateMyTableVector("; + code += (lang_.language == IDLOptions::kCSharp ? "Offset<" + struct_def.name + ">" : "int") + "[] offsets, ByteBuffer bb) {\n"; + code += " " + struct_def.name + "[] tables = new " + struct_def.name + "[offsets." + FunctionStart('L') + "ength];\n"; + code += " for (int i = 0; i < offsets." + FunctionStart('L') + "ength; i++)\n {\n"; + code += " tables[i] = new " + struct_def.name + "().__init(bb." + (lang_.language == IDLOptions::kCSharp ? "" : "array()."); + code += FunctionStart('L') + "ength - offsets[i]" + (lang_.language == IDLOptions::kCSharp ? ".Value" : "") + ", bb);\n"; + code += " }\n Array"; + code += lang_.language == IDLOptions::kCSharp ? "." : "s."; + code += FunctionStart('S') + "ort(tables, (" + struct_def.name + " first, " + struct_def.name + " second) "; + code += lang_.language == IDLOptions::kCSharp ? "=>" : "->"; + code += " first." + FunctionStart('K') + "eyCompareLessThan(second));\n"; + code += " return tables;\n }\n"; + + code += "\n public static " + struct_def.name + " " + FunctionStart('L') + "ookupByKey(" + struct_def.name; + code += "[] tables, " + key_type + " key) {\n"; + code += " int span = tables." + FunctionStart('L') + "ength;\n"; + code += " int start = 0;\n\n"; + code += " while (span != 0)\n"; + code += " {\n"; + code += " int middle = span / 2;\n"; + code += " " + struct_def.name + " table = tables[start + middle];\n"; + code += " int comp = table." + FunctionStart('K') + "eyCompareWithValue(key);\n\n"; + code += " if (comp > 0)\n"; + code += " {\n"; + code += " span = middle;\n"; + code += " }\n"; + code += " else if (comp < 0)\n"; + code += " {\n"; + code += " middle++;\n"; + code += " start += middle;\n"; + code += " span -= middle;\n"; + code += " }\n"; + code += " else\n"; + code += " {\n"; + code += " return table;\n"; + code += " }\n"; + code += " }\n"; + code += " return null;\n"; + code += " }\n"; + } code += "};\n\n"; } const LanguageParameters & lang_ = language_parameters[parser_.opts.lang]; From 1ba4d3c4c7cef05b7ba2322e218c023c031f14ed Mon Sep 17 00:00:00 2001 From: Raman Date: Mon, 25 Jul 2016 23:04:01 +0200 Subject: [PATCH 2/7] Update idl_gen_general.cpp --- src/idl_gen_general.cpp | 66 ----------------------------------------- 1 file changed, 66 deletions(-) diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index bd37fb12a..a96609c54 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -955,7 +955,6 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { } } code += "\n"; - flatbuffers::FieldDef *key_field = nullptr; if (struct_def.fixed) { // create a struct constructor function code += " public static " + GenOffsetType(struct_def) + " "; @@ -1043,7 +1042,6 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; - if (field.key) key_field = &field; if (field.deprecated) continue; code += " public static void " + FunctionStart('A') + "dd"; code += MakeCamel(field.name); @@ -1127,70 +1125,6 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { code += "); }\n"; } } - if (struct_def.has_key) { - std::string key_name = lang_.language == IDLOptions::kCSharp ? MakeCamel(key_field->name) : key_field->name + "()"; - std::string key_type = GenTypeGet(key_field->value.type); - - code += "\n public int " + FunctionStart('K') + "eyCompareLessThan(" + struct_def.name + " o) "; - if (key_field->value.type.base_type != BASE_TYPE_STRING) { - code += "{\n if (" + key_name + " < o." + key_name + ") return -1;\n"; - code += " else if (" + key_name + " == o." + key_name + ") return 0;\n"; - code += " else return 1;\n }\n"; - } - else { - code += "{ return " + key_name + "." + FunctionStart('C') + "ompareTo(o." + key_name + "); }\n"; - } - - code += " public int " + FunctionStart('K') + "eyCompareWithValue(" + key_type + " val) "; - if (key_field->value.type.base_type != BASE_TYPE_STRING) { - code += "{\n if (" + key_name + " < val) return -1;\n"; - code += " else if (" + key_name + " == val) return 0;\n"; - code += " else return 1;\n }\n"; - } - else { - code += "{ return " + key_name + "." + FunctionStart('C') + "ompareTo(val); }\n"; - } - - code += "\n public static " + struct_def.name + "[] " + FunctionStart('C') + "reateMyTableVector("; - code += (lang_.language == IDLOptions::kCSharp ? "Offset<" + struct_def.name + ">" : "int") + "[] offsets, ByteBuffer bb) {\n"; - code += " " + struct_def.name + "[] tables = new " + struct_def.name + "[offsets." + FunctionStart('L') + "ength];\n"; - code += " for (int i = 0; i < offsets." + FunctionStart('L') + "ength; i++)\n {\n"; - code += " tables[i] = new " + struct_def.name + "().__init(bb." + (lang_.language == IDLOptions::kCSharp ? "" : "array()."); - code += FunctionStart('L') + "ength - offsets[i]" + (lang_.language == IDLOptions::kCSharp ? ".Value" : "") + ", bb);\n"; - code += " }\n Array"; - code += lang_.language == IDLOptions::kCSharp ? "." : "s."; - code += FunctionStart('S') + "ort(tables, (" + struct_def.name + " first, " + struct_def.name + " second) "; - code += lang_.language == IDLOptions::kCSharp ? "=>" : "->"; - code += " first." + FunctionStart('K') + "eyCompareLessThan(second));\n"; - code += " return tables;\n }\n"; - - code += "\n public static " + struct_def.name + " " + FunctionStart('L') + "ookupByKey(" + struct_def.name; - code += "[] tables, " + key_type + " key) {\n"; - code += " int span = tables." + FunctionStart('L') + "ength;\n"; - code += " int start = 0;\n\n"; - code += " while (span != 0)\n"; - code += " {\n"; - code += " int middle = span / 2;\n"; - code += " " + struct_def.name + " table = tables[start + middle];\n"; - code += " int comp = table." + FunctionStart('K') + "eyCompareWithValue(key);\n\n"; - code += " if (comp > 0)\n"; - code += " {\n"; - code += " span = middle;\n"; - code += " }\n"; - code += " else if (comp < 0)\n"; - code += " {\n"; - code += " middle++;\n"; - code += " start += middle;\n"; - code += " span -= middle;\n"; - code += " }\n"; - code += " else\n"; - code += " {\n"; - code += " return table;\n"; - code += " }\n"; - code += " }\n"; - code += " return null;\n"; - code += " }\n"; - } code += "};\n\n"; } const LanguageParameters & lang_ = language_parameters[parser_.opts.lang]; From dc7f5bc0d80730ef45b368be90ac1208c1394707 Mon Sep 17 00:00:00 2001 From: TGIshib Date: Wed, 3 Aug 2016 13:29:50 +0300 Subject: [PATCH 3/7] Remake --- .../google/flatbuffers/FlatBufferBuilder.java | 33 +++++++++ java/com/google/flatbuffers/Table.java | 16 +++-- net/FlatBuffers/FlatBufferBuilder.cs | 12 ++++ net/FlatBuffers/Table.cs | 18 +++-- src/idl_gen_general.cpp | 67 +++++++++++++++++++ .../FlatBuffersExampleTests.cs | 25 +++++++ tests/JavaTest.java | 25 +++++++ tests/MyGame/Example/Monster.cs | 27 ++++++++ tests/MyGame/Example/Monster.java | 30 +++++++++ 9 files changed, 241 insertions(+), 12 deletions(-) diff --git a/java/com/google/flatbuffers/FlatBufferBuilder.java b/java/com/google/flatbuffers/FlatBufferBuilder.java index c2186fa7b..b3615a058 100644 --- a/java/com/google/flatbuffers/FlatBufferBuilder.java +++ b/java/com/google/flatbuffers/FlatBufferBuilder.java @@ -367,6 +367,39 @@ public class FlatBufferBuilder { } /// @endcond + /** + * Create a vector of tables. + * + * @param offsets Offsets of the tables. + * @return Returns offset of the vector. + */ + public int createVectorOfTables(int[] offsets) { + notNested(); + startVector(4, offsets.length, 4); + for(int i = offsets.length - 1; i >= 0; i--) addOffset(offsets[i]); + return endVector(); + } + + /** + * Create a vector of sorted by the key tables. + * + * @param type Type of the tables. + * @param offsets Offsets of the tables. + * @return Returns offset of the sorted vector. + */ + public int createSortedTableVector(Class type, int[] offsets) { + try{ + return (int)type.getMethod("createMySortedTableVector", FlatBufferBuilder.class, ByteBuffer.class, int[].class).invoke(null, this, bb, offsets); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (Exception e){ + e.printStackTrace(); + } + return 0; + } + /** * Encode the string `s` in the buffer using UTF-8. If {@code s} is * already a {@link CharBuffer}, this method is allocation free. diff --git a/java/com/google/flatbuffers/Table.java b/java/com/google/flatbuffers/Table.java index 408765420..9235c1653 100644 --- a/java/com/google/flatbuffers/Table.java +++ b/java/com/google/flatbuffers/Table.java @@ -56,9 +56,11 @@ public class Table { * @param vtable_offset An `int` offset to the vtable in the Table's ByteBuffer. * @return Returns an offset into the object, or `0` if the field is not present. */ - protected int __offset(int vtable_offset) { - int vtable = bb_pos - bb.getInt(bb_pos); - return vtable_offset < bb.getShort(vtable) ? bb.getShort(vtable + vtable_offset) : 0; + protected int __offset(int vtable_offset) { return __offset(vtable_offset, bb_pos - bb.getInt(bb_pos), bb, false); } + + protected static int __offset(int vtable_offset, int vtable, ByteBuffer _bb, boolean invoked_static) { + if (!invoked_static) return vtable_offset < _bb.getShort(vtable) ? _bb.getShort(vtable + vtable_offset) : 0; + else return _bb.getShort(vtable + vtable_offset - _bb.getInt(vtable)) + vtable; } /** @@ -83,11 +85,15 @@ public class Table { * @return Returns a `String` from the data stored inside the FlatBuffer at `offset`. */ protected String __string(int offset) { + return __string(offset, bb); + } + + protected static String __string(int offset, ByteBuffer _bb) { CharsetDecoder decoder = UTF8_DECODER.get(); decoder.reset(); - offset += bb.getInt(offset); - ByteBuffer src = bb.duplicate().order(ByteOrder.LITTLE_ENDIAN); + offset += _bb.getInt(offset); + ByteBuffer src = _bb.duplicate().order(ByteOrder.LITTLE_ENDIAN); int length = src.getInt(offset); src.position(offset + SIZEOF_INT); src.limit(offset + SIZEOF_INT + length); diff --git a/net/FlatBuffers/FlatBufferBuilder.cs b/net/FlatBuffers/FlatBufferBuilder.cs index 590e6ac0d..4d34ab979 100644 --- a/net/FlatBuffers/FlatBufferBuilder.cs +++ b/net/FlatBuffers/FlatBufferBuilder.cs @@ -295,6 +295,18 @@ namespace FlatBuffers PutInt(_vectorNumElems); return new VectorOffset(Offset); } + + /// + /// Creates a vector of tables. + /// + /// Offsets of the tables. + public VectorOffset CreateVectorOfTables(Offset[] offsets) where T : class + { + NotNested(); + StartVector(sizeof(int), offsets.Length, sizeof(int)); + for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value); + return EndVector(); + } /// @cond FLATBUFFERS_INTENRAL public void Nested(int obj) diff --git a/net/FlatBuffers/Table.cs b/net/FlatBuffers/Table.cs index bd5e36419..be1807666 100644 --- a/net/FlatBuffers/Table.cs +++ b/net/FlatBuffers/Table.cs @@ -31,10 +31,12 @@ namespace FlatBuffers // Look up a field in the vtable, return an offset into the object, or 0 if the field is not // present. - protected int __offset(int vtableOffset) + protected int __offset(int vtableOffset) { return __offset(vtableOffset, bb_pos - bb.GetInt(bb_pos), bb, false); } + + protected static int __offset(int vtableOffset, int vtable, ByteBuffer _bb, bool invoked_static) { - int vtable = bb_pos - bb.GetInt(bb_pos); - return vtableOffset < bb.GetShort(vtable) ? (int)bb.GetShort(vtable + vtableOffset) : 0; + if (!invoked_static) return vtableOffset < _bb.GetShort(vtable) ? (int)_bb.GetShort(vtable + vtableOffset) : 0; + else return (int)_bb.GetShort(vtable + vtableOffset - _bb.GetInt(vtable)) + vtable; } // Retrieve the relative offset stored at "offset" @@ -44,12 +46,14 @@ namespace FlatBuffers } // Create a .NET String from UTF-8 data stored inside the flatbuffer. - protected string __string(int offset) + protected string __string(int offset) { return __string(offset, bb); } + + protected static string __string(int offset, ByteBuffer _bb) { - offset += bb.GetInt(offset); - var len = bb.GetInt(offset); + offset += _bb.GetInt(offset); + var len = _bb.GetInt(offset); var startPos = offset + sizeof(int); - return Encoding.UTF8.GetString(bb.Data, startPos , len); + return Encoding.UTF8.GetString(_bb.Data, startPos, len); } // Get the length of a vector whose offset is stored at "offset" in this object. diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index 7e8e8c577..f0e65e69d 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -960,6 +960,7 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { } } code += "\n"; + flatbuffers::FieldDef *key_field = nullptr; if (struct_def.fixed) { // create a struct constructor function code += " public static " + GenOffsetType(struct_def) + " "; @@ -1048,6 +1049,7 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (field.deprecated) continue; + if (field.key) key_field = &field; code += " public static void " + FunctionStart('A') + "dd"; code += MakeCamel(field.name); code += "(FlatBufferBuilder builder, "; @@ -1130,6 +1132,71 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { code += "); }\n"; } } + if (struct_def.has_key) { + bool is_string = key_field->value.type.base_type == BASE_TYPE_STRING; + std::string key_name = lang_.language == IDLOptions::kCSharp ? MakeCamel(key_field->name) : key_field->name + "()"; + std::string key_type = GenTypeGet(key_field->value.type); + std::string key_offset = NumToString(key_field->value.offset); + std::string key_getter = is_string ? GenGetter(key_field->value.type) : GenGetter(key_field->value.type).substr(2); + std::string data_buffer = lang_.language == IDLOptions::kCSharp ? "builder.DataBuffer" : "bb"; + + code += "\n public static int " + FunctionStart('K') + "eysCompare(" + key_type + " o1, " + key_type + " o2) "; + if (lang_.language == IDLOptions::kJava && !is_string) { + code += "{\n if (o1 < o2) return -1;\n"; + code += " else if (o1 == o2) return 0;\n"; + code += " else return 1;\n }\n"; + } + else { + code += "{ return o1." + FunctionStart('C') + "ompareTo(o2); }\n"; + } + + code += "\n public int " + FunctionStart('K') + "eyCompareWithValue(" + key_type + " val) "; + if (lang_.language == IDLOptions::kJava && !is_string) { + code += "{\n if (" + key_name + " < val) return -1;\n"; + code += " else if (" + key_name + " == val) return 0;\n"; + code += " else return 1;\n }\n"; + } + else { + code += "{ return " + key_name + "." + FunctionStart('C') + "ompareTo(val); }\n"; + } + + code += "\n public static "; + code += (lang_.language == IDLOptions::kCSharp ? "VectorOffset " : "int ") + FunctionStart('C') + "reateMySortedTableVector(FlatBufferBuilder builder, "; + code += (lang_.language == IDLOptions::kCSharp ? " Offset<" + struct_def.name + ">" : "ByteBuffer bb, int") + "[] off" + (lang_.language == IDLOptions::kCSharp ? "sets" : "") + ") {\n"; + if (lang_.language == IDLOptions::kJava) { + code += " Integer[] offsets = new Integer[off.length];\n"; + code += " for (int i = 0; i < off.length; i++) offsets[i] = off[i];\n"; + } + code += " Array"; + code += (lang_.language == IDLOptions::kCSharp ? ".S" : "s.s"); + code += "ort(offsets, ("; + code += (lang_.language == IDLOptions::kCSharp ? "Offset<" + struct_def.name + "> o1, Offset<" + struct_def.name + "> o2) => " : "Integer o1, Integer o2) -> "); + code += FunctionStart('K') + "eysCompare("; + code += (is_string ? key_getter : data_buffer + key_getter) + "(__offset(" + key_offset + ", " + data_buffer + (lang_.language == IDLOptions::kCSharp ? ".Length - o1.Value" : ".array().length - o1"); + code += ", " + data_buffer + ", true)" + (is_string ? ", " + data_buffer : "") + "),\n "; + code += (is_string ? key_getter : data_buffer + key_getter) + "(__offset(" + key_offset + ", " + data_buffer + (lang_.language == IDLOptions::kCSharp ? ".Length - o2.Value" : ".array().length - o2"); + code += ", " + data_buffer + ", true)" + (is_string ? ", " + data_buffer : "") + ")));\n"; + if (lang_.language == IDLOptions::kJava) code += " for (int i = 0; i < off.length; i++) off[i] = offsets[i];\n"; + code += " return builder." + FunctionStart('C') + "reateVectorOfTables(off" + (lang_.language == IDLOptions::kCSharp ? "sets" : "") + ");\n }\n"; + + code += "\n public static " + struct_def.name + " " + FunctionStart('L') + "ookupByKey(" + struct_def.name; + code += "[] tables, " + key_type + " key) {\n"; + code += " int span = tables." + FunctionStart('L') + "ength, start = 0;\n"; + code += " while (span != 0) {\n"; + code += " int middle = span / 2;\n"; + code += " " + struct_def.name + " table = tables[start + middle];\n"; + code += " int comp = table." + FunctionStart('K') + "eyCompareWithValue(key);\n"; + code += " if (comp > 0) span = middle;\n"; + code += " else if (comp < 0) {\n"; + code += " middle++;\n"; + code += " start += middle;\n"; + code += " span -= middle;\n"; + code += " }\n"; + code += " else return table;\n"; + code += " }\n"; + code += " return null;\n"; + code += " }\n"; + } code += "}"; // Java does not need the closing semi-colon on class definitions. code += (lang_.language != IDLOptions::kJava) ? ";" : ""; diff --git a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs index 80791dd19..982dd8d7a 100644 --- a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs +++ b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs @@ -39,6 +39,19 @@ namespace FlatBuffers.Test // better for performance. var fbb = new FlatBufferBuilder(1); + StringOffset[] names = { fbb.CreateString("Frodo"), fbb.CreateString("Barney"), fbb.CreateString("Wilma") }; + Offset[] off = new Offset[3]; + Monster.StartMonster(fbb); + Monster.AddName(fbb, names[0]); + off[0] = Monster.EndMonster(fbb); + Monster.StartMonster(fbb); + Monster.AddName(fbb, names[1]); + off[1] = Monster.EndMonster(fbb); + Monster.StartMonster(fbb); + Monster.AddName(fbb, names[2]); + off[2] = Monster.EndMonster(fbb); + var sortMons = Monster.CreateMySortedTableVector(fbb, off); + // We set up the same values as monsterdata.json: var str = fbb.CreateString("MyMonster"); @@ -79,6 +92,7 @@ namespace FlatBuffers.Test Monster.AddTest4(fbb, test4); Monster.AddTestarrayofstring(fbb, testArrayOfString); Monster.AddTestbool(fbb, false); + Monster.AddTestarrayoftables(fbb, sortMons); var mon = Monster.EndMonster(fbb); Monster.FinishMonsterBuffer(fbb, mon); @@ -102,6 +116,17 @@ namespace FlatBuffers.Test // the mana field should retain its default value Assert.AreEqual(monster.MutateMana((short)10), false); Assert.AreEqual(monster.Mana, (short)150); + + // Accessing a vector of sorted by the key tables + Monster[] monsters = { monster.GetTestarrayoftables(0), monster.GetTestarrayoftables(1), monster.GetTestarrayoftables(2) }; + Assert.AreEqual(monster.GetTestarrayoftables(0).Name, "Barney"); + Assert.AreEqual(monster.GetTestarrayoftables(1).Name, "Frodo"); + Assert.AreEqual(monster.GetTestarrayoftables(2).Name, "Wilma"); + + // Example of searching for a table by the key + Assert.IsTrue(Monster.LookupByKey(monsters, "Frodo") != null); + Assert.IsTrue(Monster.LookupByKey(monsters, "Barney") != null); + Assert.IsTrue(Monster.LookupByKey(monsters, "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 154fdec67..a38f8199d 100755 --- a/tests/JavaTest.java +++ b/tests/JavaTest.java @@ -51,6 +51,19 @@ class JavaTest { // better for performance. FlatBufferBuilder fbb = new FlatBufferBuilder(1); + int[] names = {fbb.createString("Frodo"), fbb.createString("Barney"), fbb.createString("Wilma")}; + int[] off = new int[3]; + Monster.startMonster(fbb); + Monster.addName(fbb, names[0]); + off[0] = Monster.endMonster(fbb); + Monster.startMonster(fbb); + Monster.addName(fbb, names[1]); + off[1] = Monster.endMonster(fbb); + Monster.startMonster(fbb); + Monster.addName(fbb, names[2]); + off[2] = Monster.endMonster(fbb); + int sortMons = fbb.createSortedTableVector(Monster.class, off); + // We set up the same values as monsterdata.json: int str = fbb.createString("MyMonster"); @@ -84,6 +97,7 @@ class JavaTest { Monster.addTestarrayofstring(fbb, testArrayOfString); Monster.addTestbool(fbb, false); Monster.addTesthashu32Fnv1(fbb, Integer.MAX_VALUE + 1L); + Monster.addTestarrayoftables(fbb, sortMons); int mon = Monster.endMonster(fbb); Monster.finishMonsterBuffer(fbb, mon); @@ -121,6 +135,17 @@ 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 + Monster[] monsters = { monster.testarrayoftables(0), monster.testarrayoftables(1), monster.testarrayoftables(2) }; + 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(monsters, "Frodo").name(), "Frodo"); + TestEq(Monster.lookupByKey(monsters, "Barney").name(), "Barney"); + TestEq(Monster.lookupByKey(monsters, "Wilma").name(), "Wilma"); // testType is an existing field and mutating it should succeed TestEq(monster.testType(), (byte)Any.Monster); diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs index fdfd2b0a3..9702ed2a7 100644 --- a/tests/MyGame/Example/Monster.cs +++ b/tests/MyGame/Example/Monster.cs @@ -129,6 +129,33 @@ public sealed class Monster : Table { return new Offset(o); } public static void FinishMonsterBuffer(FlatBufferBuilder builder, Offset offset) { builder.Finish(offset.Value, "MONS"); } + + public static int KeysCompare(string o1, string o2) { return o1.CompareTo(o2); } + + public int KeyCompareWithValue(string val) { return Name.CompareTo(val); } + + public static VectorOffset CreateMySortedTableVector(FlatBufferBuilder builder, Offset[] offsets) { + Array.Sort(offsets, (Offset o1, Offset o2) => KeysCompare(__string(__offset(10, builder.DataBuffer.Length - o1.Value, builder.DataBuffer, true), builder.DataBuffer), + __string(__offset(10, builder.DataBuffer.Length - o2.Value, builder.DataBuffer, true), builder.DataBuffer))); + return builder.CreateVectorOfTables(offsets); + } + + public static Monster LookupByKey(Monster[] tables, string key) { + int span = tables.Length, start = 0; + while (span != 0) { + int middle = span / 2; + Monster table = tables[start + middle]; + int comp = table.KeyCompareWithValue(key); + if (comp > 0) span = middle; + else if (comp < 0) { + middle++; + start += middle; + span -= middle; + } + else return table; + } + return null; + } }; diff --git a/tests/MyGame/Example/Monster.java b/tests/MyGame/Example/Monster.java index dc27f8447..fe73ca969 100644 --- a/tests/MyGame/Example/Monster.java +++ b/tests/MyGame/Example/Monster.java @@ -135,5 +135,35 @@ public final class Monster extends Table { return o; } public static void finishMonsterBuffer(FlatBufferBuilder builder, int offset) { builder.finish(offset, "MONS"); } + + public static int keysCompare(String o1, String o2) { return o1.compareTo(o2); } + + public int keyCompareWithValue(String val) { return name().compareTo(val); } + + public static int createMySortedTableVector(FlatBufferBuilder builder, ByteBuffer bb, int[] off) { + Integer[] offsets = new Integer[off.length]; + for (int i = 0; i < off.length; i++) offsets[i] = off[i]; + Arrays.sort(offsets, (Integer o1, Integer o2) -> keysCompare(__string(__offset(10, bb.array().length - o1, bb, true), bb), + __string(__offset(10, bb.array().length - o2, bb, true), bb))); + for (int i = 0; i < off.length; i++) off[i] = offsets[i]; + return builder.createVectorOfTables(off); + } + + public static Monster lookupByKey(Monster[] tables, String key) { + int span = tables.length, start = 0; + while (span != 0) { + int middle = span / 2; + Monster table = tables[start + middle]; + int comp = table.keyCompareWithValue(key); + if (comp > 0) span = middle; + else if (comp < 0) { + middle++; + start += middle; + span -= middle; + } + else return table; + } + return null; + } } From 8fdced4e1141a98fb37e952058874423702347ca Mon Sep 17 00:00:00 2001 From: TGIshib Date: Sun, 14 Aug 2016 14:58:51 +0300 Subject: [PATCH 4/7] Update --- .../google/flatbuffers/FlatBufferBuilder.java | 18 +-- java/com/google/flatbuffers/Table.java | 65 +++++++-- net/FlatBuffers/Table.cs | 44 ++++-- src/idl_gen_general.cpp | 133 ++++++++++++------ tests/JavaTest.java | 2 +- tests/MyGame/Example/Monster.cs | 11 +- tests/MyGame/Example/Monster.java | 16 +-- 7 files changed, 187 insertions(+), 102 deletions(-) diff --git a/java/com/google/flatbuffers/FlatBufferBuilder.java b/java/com/google/flatbuffers/FlatBufferBuilder.java index b3615a058..787e4e6c9 100644 --- a/java/com/google/flatbuffers/FlatBufferBuilder.java +++ b/java/com/google/flatbuffers/FlatBufferBuilder.java @@ -375,7 +375,7 @@ public class FlatBufferBuilder { */ public int createVectorOfTables(int[] offsets) { notNested(); - startVector(4, offsets.length, 4); + startVector(Constants.SIZEOF_INT, offsets.length, Constants.SIZEOF_INT); for(int i = offsets.length - 1; i >= 0; i--) addOffset(offsets[i]); return endVector(); } @@ -383,21 +383,13 @@ public class FlatBufferBuilder { /** * Create a vector of sorted by the key tables. * - * @param type Type of the tables. + * @param obj Instance of the table class. * @param offsets Offsets of the tables. * @return Returns offset of the sorted vector. */ - public int createSortedTableVector(Class type, int[] offsets) { - try{ - return (int)type.getMethod("createMySortedTableVector", FlatBufferBuilder.class, ByteBuffer.class, int[].class).invoke(null, this, bb, offsets); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (SecurityException e) { - e.printStackTrace(); - } catch (Exception e){ - e.printStackTrace(); - } - return 0; + public int createSortedTableVector(T obj, int[] offsets) { + obj.sortTables(offsets, bb); + return createVectorOfTables(offsets); } /** diff --git a/java/com/google/flatbuffers/Table.java b/java/com/google/flatbuffers/Table.java index 9235c1653..c23ad6a55 100644 --- a/java/com/google/flatbuffers/Table.java +++ b/java/com/google/flatbuffers/Table.java @@ -56,11 +56,14 @@ public class Table { * @param vtable_offset An `int` offset to the vtable in the Table's ByteBuffer. * @return Returns an offset into the object, or `0` if the field is not present. */ - protected int __offset(int vtable_offset) { return __offset(vtable_offset, bb_pos - bb.getInt(bb_pos), bb, false); } + protected int __offset(int vtable_offset) { + int vtable = bb_pos - bb.getInt(bb_pos); + return vtable_offset < bb.getShort(vtable) ? bb.getShort(vtable + vtable_offset) : 0; + } - protected static int __offset(int vtable_offset, int vtable, ByteBuffer _bb, boolean invoked_static) { - if (!invoked_static) return vtable_offset < _bb.getShort(vtable) ? _bb.getShort(vtable + vtable_offset) : 0; - else return _bb.getShort(vtable + vtable_offset - _bb.getInt(vtable)) + vtable; + protected static int __offset(int vtable_offset, int offset, ByteBuffer bb) { + int vtable = bb.array().length - offset; + return bb.getShort(vtable + vtable_offset - bb.getInt(vtable)) + vtable; } /** @@ -85,15 +88,11 @@ public class Table { * @return Returns a `String` from the data stored inside the FlatBuffer at `offset`. */ protected String __string(int offset) { - return __string(offset, bb); - } - - protected static String __string(int offset, ByteBuffer _bb) { CharsetDecoder decoder = UTF8_DECODER.get(); decoder.reset(); - offset += _bb.getInt(offset); - ByteBuffer src = _bb.duplicate().order(ByteOrder.LITTLE_ENDIAN); + offset += bb.getInt(offset); + ByteBuffer src = bb.duplicate().order(ByteOrder.LITTLE_ENDIAN); int length = src.getInt(offset); src.position(offset + SIZEOF_INT); src.limit(offset + SIZEOF_INT + length); @@ -194,6 +193,52 @@ public class Table { } return true; } + + /** + * Sort tables by the key. + * + * @param offsets An 'int' indexes of the tables into the _bb. + * @param bb A {@code ByteBuffer} to get the tables. + */ + protected void sortTables(int[] offsets, ByteBuffer bb) { + Integer[] off = new Integer[offsets.length]; + for (int i = 0; i < offsets.length; i++) off[i] = offsets[i]; + Arrays.sort(off, (Integer o1, Integer o2) -> keysCompare(o1, o2, bb)); + for (int i = 0; i < offsets.length; i++) offsets[i] = off[i]; + } + + /** + * Compare two tables by the key. + * + * @param o1 An 'Integer' index of the first key into the _bb. + * @param o2 An 'Integer' index of the second key into the _bb. + * @param bb A {@code ByteBuffer} to get the keys. + */ + protected int keysCompare(Integer o1, Integer o2, ByteBuffer bb) { return 0; } + + /** + * Compare two strings in the buffer. + * + * @param offset_1 An 'int' index of the first string into the bb. + * @param offset_2 An 'int' index of the second string into the bb. + * @param bb A {@code ByteBuffer} to get the strings. + */ + protected static int compareStrings(int offset_1, int offset_2, ByteBuffer bb) { + offset_1 += bb.getInt(offset_1); + offset_2 += bb.getInt(offset_2); + int len_1 = bb.getInt(offset_1); + int len_2 = bb.getInt(offset_2); + int startPos_1 = offset_1 + SIZEOF_INT; + int startPos_2 = offset_2 + SIZEOF_INT; + int len = Math.min(len_1, len_2); + for(int i = 0; i < len; i++) { + if (bb.array()[i + startPos_1] != bb.array()[i + startPos_2]) + return bb.array()[i + startPos_1] - bb.array()[i + startPos_2]; + } + if (len_1 < len_2) return -1; + if (len_1 > len_2) return 1; + return 0; + } } /// @endcond diff --git a/net/FlatBuffers/Table.cs b/net/FlatBuffers/Table.cs index be1807666..2a452e3e6 100644 --- a/net/FlatBuffers/Table.cs +++ b/net/FlatBuffers/Table.cs @@ -31,12 +31,16 @@ namespace FlatBuffers // Look up a field in the vtable, return an offset into the object, or 0 if the field is not // present. - protected int __offset(int vtableOffset) { return __offset(vtableOffset, bb_pos - bb.GetInt(bb_pos), bb, false); } - - protected static int __offset(int vtableOffset, int vtable, ByteBuffer _bb, bool invoked_static) + protected int __offset(int vtableOffset) { - if (!invoked_static) return vtableOffset < _bb.GetShort(vtable) ? (int)_bb.GetShort(vtable + vtableOffset) : 0; - else return (int)_bb.GetShort(vtable + vtableOffset - _bb.GetInt(vtable)) + vtable; + int vtable = bb_pos - bb.GetInt(bb_pos); + return vtableOffset < bb.GetShort(vtable) ? (int)bb.GetShort(vtable + vtableOffset) : 0; + } + + protected static int __offset(int vtableOffset, int offset, ByteBuffer bb) + { + int vtable = bb.Length - offset; + return (int)bb.GetShort(vtable + vtableOffset - bb.GetInt(vtable)) + vtable; } // Retrieve the relative offset stored at "offset" @@ -46,14 +50,12 @@ namespace FlatBuffers } // Create a .NET String from UTF-8 data stored inside the flatbuffer. - protected string __string(int offset) { return __string(offset, bb); } - - protected static string __string(int offset, ByteBuffer _bb) + protected string __string(int offset) { - offset += _bb.GetInt(offset); - var len = _bb.GetInt(offset); + offset += bb.GetInt(offset); + var len = bb.GetInt(offset); var startPos = offset + sizeof(int); - return Encoding.UTF8.GetString(_bb.Data, startPos, len); + return Encoding.UTF8.GetString(bb.Data, startPos , len); } // Get the length of a vector whose offset is stored at "offset" in this object. @@ -107,7 +109,25 @@ namespace FlatBuffers return true; } - + + // Compare strings in the ByteBuffer. + protected static int CompareStrings(int offset_1, int offset_2, ByteBuffer bb) + { + offset_1 += bb.GetInt(offset_1); + offset_2 += bb.GetInt(offset_2); + var len_1 = bb.GetInt(offset_1); + var len_2 = bb.GetInt(offset_2); + var startPos_1 = offset_1 + sizeof(int); + var startPos_2 = offset_2 + sizeof(int); + var len = Math.Min(len_1, len_2); + for(int i = 0; i < len; i++) { + if (bb.Data[i + startPos_1] != bb.Data[i + startPos_2]) + return bb.Data[i + startPos_1] - bb.Data[i + startPos_2]; + } + if (len_1 < len_2) return -1; + if (len_1 > len_2) return 1; + return 0; + } } } diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index f0e65e69d..c1b66da4b 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -669,6 +669,60 @@ void GenStructBody(const StructDef &struct_def, std::string *code_ptr, const cha } } +void IncChar(std::string &str) { + static int index = -1, num = 1; + if (index < 0) { + for (size_t i = 0; i < str.length(); i++) { + if (str[i] == '!') { + index = i; + break; + } + } + } + str[index] = (char)(num + '0'); + num++; +} + +std::string GenKeyGetter(flatbuffers::FieldDef *key_field) { + auto data_buffer = (lang_.language == IDLOptions::kCSharp) ? + "builder.DataBuffer" : "_bb"; + auto get_offset = "__offset(" + + NumToString(key_field->value.offset) + ", o!" + + (lang_.language == IDLOptions::kCSharp ? ".Value" : "") + + ", " + data_buffer + ")"; + + std::string key_getter = ""; + if (key_field->value.type.base_type == BASE_TYPE_STRING) { + if (lang_.language == IDLOptions::kJava) + key_getter += " return "; + key_getter += FunctionStart('C') + "ompareStrings("; + IncChar(get_offset); + key_getter += get_offset + ", "; + IncChar(get_offset); + key_getter += get_offset + ", " + data_buffer + ")"; + if (lang_.language == IDLOptions::kJava) + key_getter += ";"; + } + else { + auto field_getter = data_buffer + GenGetter(key_field->value.type).substr(2) + + "(" + get_offset + ")"; + IncChar(field_getter); + if (lang_.language == IDLOptions::kCSharp) { + key_getter += field_getter; + IncChar(field_getter); + key_getter += ".CompareTo(" + field_getter + ")"; + } + else { + key_getter += "\n " + GenTypeGet(key_field->value.type) + " off1 = "; + key_getter += field_getter + ";\n " + GenTypeGet(key_field->value.type); + key_getter += " off2 = "; + IncChar(field_getter); + key_getter += field_getter + ";\n"; + } + } + return key_getter; +} + void GenStruct(StructDef &struct_def, std::string *code_ptr) { if (struct_def.generated) return; std::string &code = *code_ptr; @@ -1133,59 +1187,48 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { } } if (struct_def.has_key) { - bool is_string = key_field->value.type.base_type == BASE_TYPE_STRING; - std::string key_name = lang_.language == IDLOptions::kCSharp ? MakeCamel(key_field->name) : key_field->name + "()"; - std::string key_type = GenTypeGet(key_field->value.type); - std::string key_offset = NumToString(key_field->value.offset); - std::string key_getter = is_string ? GenGetter(key_field->value.type) : GenGetter(key_field->value.type).substr(2); - std::string data_buffer = lang_.language == IDLOptions::kCSharp ? "builder.DataBuffer" : "bb"; + auto is_string = key_field->value.type.base_type == BASE_TYPE_STRING; + auto key_name = "table." + + (lang_.language == IDLOptions::kCSharp ? + MakeCamel(key_field->name) : key_field->name + "()"); - code += "\n public static int " + FunctionStart('K') + "eysCompare(" + key_type + " o1, " + key_type + " o2) "; - if (lang_.language == IDLOptions::kJava && !is_string) { - code += "{\n if (o1 < o2) return -1;\n"; - code += " else if (o1 == o2) return 0;\n"; - code += " else return 1;\n }\n"; - } - else { - code += "{ return o1." + FunctionStart('C') + "ompareTo(o2); }\n"; - } - - code += "\n public int " + FunctionStart('K') + "eyCompareWithValue(" + key_type + " val) "; - if (lang_.language == IDLOptions::kJava && !is_string) { - code += "{\n if (" + key_name + " < val) return -1;\n"; - code += " else if (" + key_name + " == val) return 0;\n"; - code += " else return 1;\n }\n"; - } - else { - code += "{ return " + key_name + "." + FunctionStart('C') + "ompareTo(val); }\n"; - } - - code += "\n public static "; - code += (lang_.language == IDLOptions::kCSharp ? "VectorOffset " : "int ") + FunctionStart('C') + "reateMySortedTableVector(FlatBufferBuilder builder, "; - code += (lang_.language == IDLOptions::kCSharp ? " Offset<" + struct_def.name + ">" : "ByteBuffer bb, int") + "[] off" + (lang_.language == IDLOptions::kCSharp ? "sets" : "") + ") {\n"; if (lang_.language == IDLOptions::kJava) { - code += " Integer[] offsets = new Integer[off.length];\n"; - code += " for (int i = 0; i < off.length; i++) offsets[i] = off[i];\n"; + code += "\n @Override\n protected int keysCompare("; + code += "Integer o1, Integer o2, ByteBuffer _bb) {"; + code += GenKeyGetter(key_field); + if (!is_string) { + code += " if (off1 < off2) return -1;\n"; + code += " else if (off1 == off2) return 0;\n"; + code += " else return 1;\n "; + } + code += " }\n"; + } + else { + code += "\n public static VectorOffset "; + code += "CreateMySortedTableVector(FlatBufferBuilder builder, "; + code += "Offset<" + struct_def.name + ">"; + code += "[] offsets) {\n"; + code += " Array.Sort(offsets, (Offset<" + struct_def.name + + "> o1, Offset<" + struct_def.name + "> o2) => " + GenKeyGetter(key_field); + code += ");\n"; + code += " return builder.CreateVectorOfTables(offsets);\n }\n"; } - code += " Array"; - code += (lang_.language == IDLOptions::kCSharp ? ".S" : "s.s"); - code += "ort(offsets, ("; - code += (lang_.language == IDLOptions::kCSharp ? "Offset<" + struct_def.name + "> o1, Offset<" + struct_def.name + "> o2) => " : "Integer o1, Integer o2) -> "); - code += FunctionStart('K') + "eysCompare("; - code += (is_string ? key_getter : data_buffer + key_getter) + "(__offset(" + key_offset + ", " + data_buffer + (lang_.language == IDLOptions::kCSharp ? ".Length - o1.Value" : ".array().length - o1"); - code += ", " + data_buffer + ", true)" + (is_string ? ", " + data_buffer : "") + "),\n "; - code += (is_string ? key_getter : data_buffer + key_getter) + "(__offset(" + key_offset + ", " + data_buffer + (lang_.language == IDLOptions::kCSharp ? ".Length - o2.Value" : ".array().length - o2"); - code += ", " + data_buffer + ", true)" + (is_string ? ", " + data_buffer : "") + ")));\n"; - if (lang_.language == IDLOptions::kJava) code += " for (int i = 0; i < off.length; i++) off[i] = offsets[i];\n"; - code += " return builder." + FunctionStart('C') + "reateVectorOfTables(off" + (lang_.language == IDLOptions::kCSharp ? "sets" : "") + ");\n }\n"; - code += "\n public static " + struct_def.name + " " + FunctionStart('L') + "ookupByKey(" + struct_def.name; - code += "[] tables, " + key_type + " key) {\n"; + code += "\n public static " + struct_def.name + " " + FunctionStart('L'); + code += "ookupByKey(" + struct_def.name; + code += "[] tables, " + GenTypeGet(key_field->value.type) + " key) {\n"; code += " int span = tables." + FunctionStart('L') + "ength, start = 0;\n"; code += " while (span != 0) {\n"; code += " int middle = span / 2;\n"; code += " " + struct_def.name + " table = tables[start + middle];\n"; - code += " int comp = table." + FunctionStart('K') + "eyCompareWithValue(key);\n"; + if (lang_.language == IDLOptions::kCSharp || is_string) { + code += " int comp = " + key_name + "." + FunctionStart('C'); + code += "ompareTo(key);\n"; + } + else { + code += " int comp = " + key_name + " > key ? 1 : " + key_name; + code += " < key ? -1 : 0;\n"; + } code += " if (comp > 0) span = middle;\n"; code += " else if (comp < 0) {\n"; code += " middle++;\n"; diff --git a/tests/JavaTest.java b/tests/JavaTest.java index a38f8199d..5ca8a26cd 100755 --- a/tests/JavaTest.java +++ b/tests/JavaTest.java @@ -62,7 +62,7 @@ class JavaTest { Monster.startMonster(fbb); Monster.addName(fbb, names[2]); off[2] = Monster.endMonster(fbb); - int sortMons = fbb.createSortedTableVector(Monster.class, off); + int sortMons = fbb.createSortedTableVector(new Monster(), off); // We set up the same values as monsterdata.json: diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs index 9702ed2a7..1cd172a0c 100644 --- a/tests/MyGame/Example/Monster.cs +++ b/tests/MyGame/Example/Monster.cs @@ -130,13 +130,8 @@ public sealed class Monster : Table { } public static void FinishMonsterBuffer(FlatBufferBuilder builder, Offset offset) { builder.Finish(offset.Value, "MONS"); } - public static int KeysCompare(string o1, string o2) { return o1.CompareTo(o2); } - - public int KeyCompareWithValue(string val) { return Name.CompareTo(val); } - - public static VectorOffset CreateMySortedTableVector(FlatBufferBuilder builder, Offset[] offsets) { - Array.Sort(offsets, (Offset o1, Offset o2) => KeysCompare(__string(__offset(10, builder.DataBuffer.Length - o1.Value, builder.DataBuffer, true), builder.DataBuffer), - __string(__offset(10, builder.DataBuffer.Length - o2.Value, builder.DataBuffer, true), builder.DataBuffer))); + public static VectorOffset CreateMySortedTableVector(FlatBufferBuilder builder, Offset[] offsets) { + Array.Sort(offsets, (Offset o1, Offset o2) => CompareStrings(__offset(10, o1.Value, builder.DataBuffer), __offset(10, o2.Value, builder.DataBuffer), builder.DataBuffer)); return builder.CreateVectorOfTables(offsets); } @@ -145,7 +140,7 @@ public sealed class Monster : Table { while (span != 0) { int middle = span / 2; Monster table = tables[start + middle]; - int comp = table.KeyCompareWithValue(key); + int comp = table.Name.CompareTo(key); if (comp > 0) span = middle; else if (comp < 0) { middle++; diff --git a/tests/MyGame/Example/Monster.java b/tests/MyGame/Example/Monster.java index fe73ca969..ee1a718ba 100644 --- a/tests/MyGame/Example/Monster.java +++ b/tests/MyGame/Example/Monster.java @@ -135,26 +135,16 @@ public final class Monster extends Table { return o; } public static void finishMonsterBuffer(FlatBufferBuilder builder, int offset) { builder.finish(offset, "MONS"); } - - public static int keysCompare(String o1, String o2) { return o1.compareTo(o2); } - public int keyCompareWithValue(String val) { return name().compareTo(val); } - - public static int createMySortedTableVector(FlatBufferBuilder builder, ByteBuffer bb, int[] off) { - Integer[] offsets = new Integer[off.length]; - for (int i = 0; i < off.length; i++) offsets[i] = off[i]; - Arrays.sort(offsets, (Integer o1, Integer o2) -> keysCompare(__string(__offset(10, bb.array().length - o1, bb, true), bb), - __string(__offset(10, bb.array().length - o2, bb, true), bb))); - for (int i = 0; i < off.length; i++) off[i] = offsets[i]; - return builder.createVectorOfTables(off); - } + @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(Monster[] tables, String key) { int span = tables.length, start = 0; while (span != 0) { int middle = span / 2; Monster table = tables[start + middle]; - int comp = table.keyCompareWithValue(key); + int comp = table.name().compareTo(key); if (comp > 0) span = middle; else if (comp < 0) { middle++; From fa74ce6d162cc25fbf3af153b8067363049de7c3 Mon Sep 17 00:00:00 2001 From: TGIshib Date: Mon, 15 Aug 2016 13:30:30 +0300 Subject: [PATCH 5/7] Update idl_gen_general.cpp --- src/idl_gen_general.cpp | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index c1b66da4b..55f149303 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -669,55 +669,43 @@ void GenStructBody(const StructDef &struct_def, std::string *code_ptr, const cha } } -void IncChar(std::string &str) { - static int index = -1, num = 1; - if (index < 0) { - for (size_t i = 0; i < str.length(); i++) { - if (str[i] == '!') { - index = i; - break; - } - } - } - str[index] = (char)(num + '0'); - num++; +std::string GenOffsetGetter(flatbuffers::FieldDef *key_field, const char &num) { + return "__offset(" + + NumToString(key_field->value.offset) + ", o" + num + + (lang_.language == IDLOptions::kCSharp ? + ".Value, builder.DataBuffer)" : ", _bb)"); } std::string GenKeyGetter(flatbuffers::FieldDef *key_field) { auto data_buffer = (lang_.language == IDLOptions::kCSharp) ? "builder.DataBuffer" : "_bb"; - auto get_offset = "__offset(" + - NumToString(key_field->value.offset) + ", o!" + - (lang_.language == IDLOptions::kCSharp ? ".Value" : "") + - ", " + data_buffer + ")"; - std::string key_getter = ""; if (key_field->value.type.base_type == BASE_TYPE_STRING) { if (lang_.language == IDLOptions::kJava) key_getter += " return "; key_getter += FunctionStart('C') + "ompareStrings("; - IncChar(get_offset); - key_getter += get_offset + ", "; - IncChar(get_offset); - key_getter += get_offset + ", " + data_buffer + ")"; + key_getter += GenOffsetGetter(key_field, '1') + ", "; + key_getter += GenOffsetGetter(key_field, '2') + ", " + data_buffer + ")"; if (lang_.language == IDLOptions::kJava) key_getter += ";"; } else { auto field_getter = data_buffer + GenGetter(key_field->value.type).substr(2) + - "(" + get_offset + ")"; - IncChar(field_getter); + "(" + GenOffsetGetter(key_field, '1') + ")"; if (lang_.language == IDLOptions::kCSharp) { key_getter += field_getter; - IncChar(field_getter); + field_getter = data_buffer + GenGetter(key_field->value.type).substr(2) + + "(" + GenOffsetGetter(key_field, '2') + ")"; key_getter += ".CompareTo(" + field_getter + ")"; } else { key_getter += "\n " + GenTypeGet(key_field->value.type) + " off1 = "; key_getter += field_getter + ";\n " + GenTypeGet(key_field->value.type); key_getter += " off2 = "; - IncChar(field_getter); + field_getter = data_buffer + GenGetter(key_field->value.type).substr(2) + + "(" + GenOffsetGetter(key_field, '2') + ")"; key_getter += field_getter + ";\n"; + key_getter += " return off1 > off2 ? 1 : off1 < off2 ? -1 : 0;\n"; } } return key_getter; From 9f16090f901c30f872422f60db21a0370dde8bbf Mon Sep 17 00:00:00 2001 From: TGIshib Date: Mon, 22 Aug 2016 18:10:52 +0300 Subject: [PATCH 6/7] Improve `LookupByKey` , update docs --- docs/source/JavaCsharpUsage.md | 30 +++++ .../google/flatbuffers/FlatBufferBuilder.java | 4 +- java/com/google/flatbuffers/Table.java | 32 +++++- net/FlatBuffers/Table.cs | 22 ++++ src/idl_gen_general.cpp | 105 +++++++++++------- .../FlatBuffersExampleTests.cs | 9 +- tests/JavaTest.java | 9 +- tests/MyGame/Example/Monster.cs | 14 ++- tests/MyGame/Example/Monster.java | 12 +- 9 files changed, 173 insertions(+), 64 deletions(-) diff --git a/docs/source/JavaCsharpUsage.md b/docs/source/JavaCsharpUsage.md index 3af9cfa48..cc58f8562 100755 --- a/docs/source/JavaCsharpUsage.md +++ b/docs/source/JavaCsharpUsage.md @@ -131,6 +131,36 @@ object are prefixed with `Get`, e.g.: monster.GetPos(preconstructedPos); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## Storing dictionaries in a FlatBuffer + +FlatBuffers doesn't support dictionaries natively, but there is support to +emulate their behavior with vectors and binary search, which means you +can have fast lookups directly from a FlatBuffer without having to unpack +your data into a `Dictionary` or similar. + +To use it: +- Designate one of the fields in a table as they "key" field. You do this + by setting the `key` attribute on this field, e.g. + `name:string (key)`. + You may only have one key field, and it must be of string or scalar type. +- Write out tables of this type as usual, collect their offsets in an + array. +- Instead of calling standard generated method, + e.g.: `Monster.createTestarrayoftablesVector`, + call `CreateMySortedVectorOfTables` 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)`, + 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. + ## Text parsing There currently is no support for parsing text (Schema's and JSON) directly diff --git a/java/com/google/flatbuffers/FlatBufferBuilder.java b/java/com/google/flatbuffers/FlatBufferBuilder.java index 787e4e6c9..7ab24e10d 100644 --- a/java/com/google/flatbuffers/FlatBufferBuilder.java +++ b/java/com/google/flatbuffers/FlatBufferBuilder.java @@ -383,11 +383,11 @@ public class FlatBufferBuilder { /** * Create a vector of sorted by the key tables. * - * @param obj Instance of the table class. + * @param obj Instance of the table subclass. * @param offsets Offsets of the tables. * @return Returns offset of the sorted vector. */ - public int createSortedTableVector(T obj, int[] offsets) { + public int createSortedVectorOfTables(T obj, int[] offsets) { obj.sortTables(offsets, bb); return createVectorOfTables(offsets); } diff --git a/java/com/google/flatbuffers/Table.java b/java/com/google/flatbuffers/Table.java index c23ad6a55..7bf07113d 100644 --- a/java/com/google/flatbuffers/Table.java +++ b/java/com/google/flatbuffers/Table.java @@ -75,6 +75,10 @@ public class Table { protected int __indirect(int offset) { return offset + bb.getInt(offset); } + + protected static int __indirect(int offset, ByteBuffer bb) { + return offset + bb.getInt(offset); + } /** * Create a Java `String` from UTF-8 data stored inside the FlatBuffer. @@ -197,7 +201,7 @@ public class Table { /** * Sort tables by the key. * - * @param offsets An 'int' indexes of the tables into the _bb. + * @param offsets An 'int' indexes of the tables into the bb. * @param bb A {@code ByteBuffer} to get the tables. */ protected void sortTables(int[] offsets, ByteBuffer bb) { @@ -210,8 +214,8 @@ public class Table { /** * Compare two tables by the key. * - * @param o1 An 'Integer' index of the first key into the _bb. - * @param o2 An 'Integer' index of the second key into the _bb. + * @param o1 An 'Integer' index of the first key into the bb. + * @param o2 An 'Integer' index of the second key into the bb. * @param bb A {@code ByteBuffer} to get the keys. */ protected int keysCompare(Integer o1, Integer o2, ByteBuffer bb) { return 0; } @@ -239,6 +243,28 @@ public class Table { if (len_1 > len_2) return 1; return 0; } + + /** + * Compare string from the buffer with the 'String' object. + * + * @param offset_1 An 'int' index of the first string into the bb. + * @param key Second string. + * @param bb A {@code ByteBuffer} to get the first string. + */ + protected static int compareStrings(int offset_1, String key, ByteBuffer bb) { + offset_1 += bb.getInt(offset_1); + int len_1 = bb.getInt(offset_1); + int len_2 = key.length(); + int startPos_1 = offset_1 + Constants.SIZEOF_INT; + int len = Math.min(len_1, len_2); + for (int i = 0; i < len; i++) { + if (bb.array()[i + startPos_1] != key.charAt(i)) + return bb.array()[i + startPos_1] - key.charAt(i); + } + if (len_1 < len_2) return -1; + if (len_1 > len_2) return 1; + return 0; + } } /// @endcond diff --git a/net/FlatBuffers/Table.cs b/net/FlatBuffers/Table.cs index 2a452e3e6..20f41f6fe 100644 --- a/net/FlatBuffers/Table.cs +++ b/net/FlatBuffers/Table.cs @@ -48,6 +48,11 @@ namespace FlatBuffers { return offset + bb.GetInt(offset); } + + protected static int __indirect(int offset, ByteBuffer bb) + { + return offset + bb.GetInt(offset); + } // Create a .NET String from UTF-8 data stored inside the flatbuffer. protected string __string(int offset) @@ -128,6 +133,23 @@ namespace FlatBuffers if (len_1 > len_2) return 1; return 0; } + + // Compare string from the ByteBuffer with the string object + protected static int CompareStrings(int offset_1, string key, ByteBuffer bb) + { + offset_1 += bb.GetInt(offset_1); + var len_1 = bb.GetInt(offset_1); + var len_2 = key.Length; + var startPos_1 = offset_1 + sizeof(int); + var len = Math.Min(len_1, len_2); + for (int i = 0; i < len; i++) { + if (bb.Data[i + startPos_1] != key[i]) + return bb.Data[i + startPos_1] - key[i]; + } + if (len_1 < len_2) return -1; + if (len_1 > len_2) return 1; + return 0; + } } } diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index 55f149303..f2efe7192 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -669,43 +669,84 @@ void GenStructBody(const StructDef &struct_def, std::string *code_ptr, const cha } } -std::string GenOffsetGetter(flatbuffers::FieldDef *key_field, const char &num) { - return "__offset(" + - NumToString(key_field->value.offset) + ", o" + num + - (lang_.language == IDLOptions::kCSharp ? - ".Value, builder.DataBuffer)" : ", _bb)"); +std::string GenByteBufferLength(const char *bb_name) { + std::string bb_len = bb_name; + if (lang_.language == IDLOptions::kCSharp) bb_len += ".Length"; + else bb_len += ".array().length"; + return bb_len; } +std::string GenOffsetGetter(flatbuffers::FieldDef *key_field, const char *num = nullptr) { + std::string key_offset = ""; + key_offset += "__offset(" + + NumToString(key_field->value.offset) + ", "; + if (num) { + key_offset += num; + key_offset += (lang_.language == IDLOptions::kCSharp ? + ".Value, builder.DataBuffer)" : ", _bb)"); + } + else { + key_offset += GenByteBufferLength("bb"); + key_offset += " - tableOffset, bb)"; + } + return key_offset; +} + +std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) { + std::string key_getter = " "; + key_getter += "tableOffset = __indirect(vectorLocation + 4 * (start + middle)"; + key_getter += ", bb);\n "; + if (key_field->value.type.base_type == BASE_TYPE_STRING) { + key_getter += "comp = " + FunctionStart('C') + "ompareStrings("; + key_getter += GenOffsetGetter(key_field); + key_getter += ", key, bb);\n"; + } + else { + auto get_val = GenGetter(key_field->value.type) + + "(" + GenOffsetGetter(key_field) + ")"; + if (lang_.language == IDLOptions::kCSharp) { + key_getter += "comp = " + get_val + ".CompateTo(key);\n"; + } + else { + key_getter += GenTypeGet(key_field->value.type) + " val = "; + key_getter += get_val + ";\n"; + key_getter += " comp = val > key ? 1 : val < key ? -1 : 0;\n"; + } + } + return key_getter; +} + + std::string GenKeyGetter(flatbuffers::FieldDef *key_field) { + std::string key_getter = ""; auto data_buffer = (lang_.language == IDLOptions::kCSharp) ? "builder.DataBuffer" : "_bb"; - std::string key_getter = ""; if (key_field->value.type.base_type == BASE_TYPE_STRING) { if (lang_.language == IDLOptions::kJava) key_getter += " return "; key_getter += FunctionStart('C') + "ompareStrings("; - key_getter += GenOffsetGetter(key_field, '1') + ", "; - key_getter += GenOffsetGetter(key_field, '2') + ", " + data_buffer + ")"; + key_getter += GenOffsetGetter(key_field, "o1") + ", "; + key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")"; if (lang_.language == IDLOptions::kJava) key_getter += ";"; } else { auto field_getter = data_buffer + GenGetter(key_field->value.type).substr(2) + - "(" + GenOffsetGetter(key_field, '1') + ")"; + "(" + GenOffsetGetter(key_field, "o1") + ")"; if (lang_.language == IDLOptions::kCSharp) { key_getter += field_getter; field_getter = data_buffer + GenGetter(key_field->value.type).substr(2) + - "(" + GenOffsetGetter(key_field, '2') + ")"; + "(" + GenOffsetGetter(key_field, "o2") + ")"; key_getter += ".CompareTo(" + field_getter + ")"; } else { - key_getter += "\n " + GenTypeGet(key_field->value.type) + " off1 = "; + key_getter += "\n " + GenTypeGet(key_field->value.type) + " val_1 = "; key_getter += field_getter + ";\n " + GenTypeGet(key_field->value.type); - key_getter += " off2 = "; + key_getter += " val_2 = "; field_getter = data_buffer + GenGetter(key_field->value.type).substr(2) + - "(" + GenOffsetGetter(key_field, '2') + ")"; + "(" + GenOffsetGetter(key_field, "o2") + ")"; key_getter += field_getter + ";\n"; - key_getter += " return off1 > off2 ? 1 : off1 < off2 ? -1 : 0;\n"; + key_getter += " return val_1 > val_2 ? 1 : val_1 < val_2 ? -1 : 0;\n "; } } return key_getter; @@ -1175,25 +1216,15 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { } } if (struct_def.has_key) { - auto is_string = key_field->value.type.base_type == BASE_TYPE_STRING; - auto key_name = "table." + - (lang_.language == IDLOptions::kCSharp ? - MakeCamel(key_field->name) : key_field->name + "()"); - if (lang_.language == IDLOptions::kJava) { code += "\n @Override\n protected int keysCompare("; code += "Integer o1, Integer o2, ByteBuffer _bb) {"; code += GenKeyGetter(key_field); - if (!is_string) { - code += " if (off1 < off2) return -1;\n"; - code += " else if (off1 == off2) return 0;\n"; - code += " else return 1;\n "; - } code += " }\n"; } else { code += "\n public static VectorOffset "; - code += "CreateMySortedTableVector(FlatBufferBuilder builder, "; + code += "CreateMySortedVectorOfTables(FlatBufferBuilder builder, "; code += "Offset<" + struct_def.name + ">"; code += "[] offsets) {\n"; code += " Array.Sort(offsets, (Offset<" + struct_def.name + @@ -1203,27 +1234,25 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { } code += "\n public static " + struct_def.name + " " + FunctionStart('L'); - code += "ookupByKey(" + struct_def.name; - code += "[] tables, " + GenTypeGet(key_field->value.type) + " key) {\n"; - code += " int span = tables." + FunctionStart('L') + "ength, start = 0;\n"; + code += "ookupByKey(" + GenVectorOffsetType(); + code += " vectorOffset, " + GenTypeGet(key_field->value.type); + code += " key, ByteBuffer bb) {\n"; + code += " int vectorLocation = " + GenByteBufferLength("bb"); + code += " - vectorOffset.Value;\n int span = "; + code += "bb." + FunctionStart('G') + "etInt(vectorLocation), "; + code += "middle, start = 0, comp, tableOffset; \n"; + code += " vectorLocation += 4;\n"; code += " while (span != 0) {\n"; code += " int middle = span / 2;\n"; - code += " " + struct_def.name + " table = tables[start + middle];\n"; - if (lang_.language == IDLOptions::kCSharp || is_string) { - code += " int comp = " + key_name + "." + FunctionStart('C'); - code += "ompareTo(key);\n"; - } - else { - code += " int comp = " + key_name + " > key ? 1 : " + key_name; - code += " < key ? -1 : 0;\n"; - } + code += GenLookupKeyGetter(key_field); code += " if (comp > 0) span = middle;\n"; code += " else if (comp < 0) {\n"; code += " middle++;\n"; code += " start += middle;\n"; code += " span -= middle;\n"; code += " }\n"; - code += " else return table;\n"; + code += " else return new " + struct_def.name; + code += "().__init(tableOffset, bb);\n"; code += " }\n"; code += " return null;\n"; code += " }\n"; diff --git a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs index 982dd8d7a..ed44665a3 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.CreateMySortedTableVector(fbb, off); + var sortMons = Monster.CreateMySortedVectorOfTables(fbb, off); // We set up the same values as monsterdata.json: @@ -118,15 +118,14 @@ namespace FlatBuffers.Test Assert.AreEqual(monster.Mana, (short)150); // Accessing a vector of sorted by the key tables - Monster[] monsters = { monster.GetTestarrayoftables(0), monster.GetTestarrayoftables(1), monster.GetTestarrayoftables(2) }; Assert.AreEqual(monster.GetTestarrayoftables(0).Name, "Barney"); Assert.AreEqual(monster.GetTestarrayoftables(1).Name, "Frodo"); Assert.AreEqual(monster.GetTestarrayoftables(2).Name, "Wilma"); // Example of searching for a table by the key - Assert.IsTrue(Monster.LookupByKey(monsters, "Frodo") != null); - Assert.IsTrue(Monster.LookupByKey(monsters, "Barney") != null); - Assert.IsTrue(Monster.LookupByKey(monsters, "Wilma") != null); + 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); // 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 5ca8a26cd..9f46f3fae 100755 --- a/tests/JavaTest.java +++ b/tests/JavaTest.java @@ -62,7 +62,7 @@ class JavaTest { Monster.startMonster(fbb); Monster.addName(fbb, names[2]); off[2] = Monster.endMonster(fbb); - int sortMons = fbb.createSortedTableVector(new Monster(), off); + int sortMons = fbb.createSortedVectorOfTables(new Monster(), off); // We set up the same values as monsterdata.json: @@ -137,15 +137,14 @@ class JavaTest { TestEq(monster.mana(), (short)150); // Accessing a vector of sorted by the key tables - Monster[] monsters = { monster.testarrayoftables(0), monster.testarrayoftables(1), monster.testarrayoftables(2) }; 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(monsters, "Frodo").name(), "Frodo"); - TestEq(Monster.lookupByKey(monsters, "Barney").name(), "Barney"); - TestEq(Monster.lookupByKey(monsters, "Wilma").name(), "Wilma"); + 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"); // testType is an existing field and mutating it should succeed TestEq(monster.testType(), (byte)Any.Monster); diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs index 1cd172a0c..ce89443a4 100644 --- a/tests/MyGame/Example/Monster.cs +++ b/tests/MyGame/Example/Monster.cs @@ -130,24 +130,26 @@ public sealed class Monster : Table { } public static void FinishMonsterBuffer(FlatBufferBuilder builder, Offset offset) { builder.Finish(offset.Value, "MONS"); } - public static VectorOffset CreateMySortedTableVector(FlatBufferBuilder builder, Offset[] offsets) { + public static VectorOffset CreateMySortedVectorOfTables(FlatBufferBuilder builder, Offset[] offsets) { Array.Sort(offsets, (Offset o1, Offset o2) => CompareStrings(__offset(10, o1.Value, builder.DataBuffer), __offset(10, o2.Value, builder.DataBuffer), builder.DataBuffer)); return builder.CreateVectorOfTables(offsets); } - public static Monster LookupByKey(Monster[] tables, string key) { - int span = tables.Length, start = 0; + public static Monster LookupByKey(VectorOffset vectorOffset, string key, ByteBuffer bb) { + int vectorLocation = bb.Length - vectorOffset.Value; + int span = bb.GetInt(vectorLocation), middle, start = 0, comp, tableOffset; + vectorLocation += 4; while (span != 0) { int middle = span / 2; - Monster table = tables[start + middle]; - int comp = table.Name.CompareTo(key); + tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb); + comp = CompareStrings(__offset(10, bb.Length - tableOffset, bb), key, bb); if (comp > 0) span = middle; else if (comp < 0) { middle++; start += middle; span -= middle; } - else return table; + else return new Monster().__init(tableOffset, bb); } return null; } diff --git a/tests/MyGame/Example/Monster.java b/tests/MyGame/Example/Monster.java index ee1a718ba..2c20fa7d7 100644 --- a/tests/MyGame/Example/Monster.java +++ b/tests/MyGame/Example/Monster.java @@ -139,19 +139,21 @@ 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(Monster[] tables, String key) { - int span = tables.length, start = 0; + public static Monster lookupByKey(int vectorOffset, String key, ByteBuffer bb) { + int vectorLocation = bb.array().length - vectorOffset.Value; + int span = bb.getInt(vectorLocation), middle, start = 0, comp, tableOffset; + vectorLocation += 4; while (span != 0) { int middle = span / 2; - Monster table = tables[start + middle]; - int comp = table.name().compareTo(key); + tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb); + comp = compareStrings(__offset(10, bb.array().length - tableOffset, bb), key, bb); if (comp > 0) span = middle; else if (comp < 0) { middle++; start += middle; span -= middle; } - else return table; + else return new Monster().__init(tableOffset, bb); } return null; } From 7c69c5dc3d635e29e3442339caa8eb06b8b9775c Mon Sep 17 00:00:00 2001 From: TGIshib Date: Fri, 26 Aug 2016 19:41:32 +0300 Subject: [PATCH 7/7] Fix lookupByKey, improve compareStrings --- java/com/google/flatbuffers/Table.java | 24 +++++++++++------------- net/FlatBuffers/Table.cs | 20 +++++++++----------- src/idl_gen_general.cpp | 7 ++++++- tests/MyGame/Example/Monster.cs | 3 ++- tests/MyGame/Example/Monster.java | 3 ++- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/java/com/google/flatbuffers/Table.java b/java/com/google/flatbuffers/Table.java index 7bf07113d..c9c654561 100644 --- a/java/com/google/flatbuffers/Table.java +++ b/java/com/google/flatbuffers/Table.java @@ -235,35 +235,33 @@ public class Table { int startPos_1 = offset_1 + SIZEOF_INT; int startPos_2 = offset_2 + SIZEOF_INT; int len = Math.min(len_1, len_2); + byte[] bbArray = bb.array(); for(int i = 0; i < len; i++) { - if (bb.array()[i + startPos_1] != bb.array()[i + startPos_2]) - return bb.array()[i + startPos_1] - bb.array()[i + startPos_2]; + if (bbArray[i + startPos_1] != bbArray[i + startPos_2]) + return bbArray[i + startPos_1] - bbArray[i + startPos_2]; } - if (len_1 < len_2) return -1; - if (len_1 > len_2) return 1; - return 0; + return len_1 - len_2; } /** * Compare string from the buffer with the 'String' object. * * @param offset_1 An 'int' index of the first string into the bb. - * @param key Second string. + * @param key Second string as a byte array. * @param bb A {@code ByteBuffer} to get the first string. */ - protected static int compareStrings(int offset_1, String key, ByteBuffer bb) { + protected static int compareStrings(int offset_1, byte[] key, ByteBuffer bb) { offset_1 += bb.getInt(offset_1); int len_1 = bb.getInt(offset_1); - int len_2 = key.length(); + int len_2 = key.length; int startPos_1 = offset_1 + Constants.SIZEOF_INT; int len = Math.min(len_1, len_2); + byte[] bbArray = bb.array(); for (int i = 0; i < len; i++) { - if (bb.array()[i + startPos_1] != key.charAt(i)) - return bb.array()[i + startPos_1] - key.charAt(i); + if (bbArray[i + startPos_1] != key[i]) + return bbArray[i + startPos_1] - key[i]; } - if (len_1 < len_2) return -1; - if (len_1 > len_2) return 1; - return 0; + return len_1 - len_2; } } diff --git a/net/FlatBuffers/Table.cs b/net/FlatBuffers/Table.cs index 20f41f6fe..ca52d7d02 100644 --- a/net/FlatBuffers/Table.cs +++ b/net/FlatBuffers/Table.cs @@ -125,30 +125,28 @@ namespace FlatBuffers var startPos_1 = offset_1 + sizeof(int); var startPos_2 = offset_2 + sizeof(int); var len = Math.Min(len_1, len_2); + byte[] bbArray = bb.Data; for(int i = 0; i < len; i++) { - if (bb.Data[i + startPos_1] != bb.Data[i + startPos_2]) - return bb.Data[i + startPos_1] - bb.Data[i + startPos_2]; + if (bbArray[i + startPos_1] != bbArray[i + startPos_2]) + return bbArray[i + startPos_1] - bbArray[i + startPos_2]; } - if (len_1 < len_2) return -1; - if (len_1 > len_2) return 1; - return 0; + return len_1 - len_2; } // Compare string from the ByteBuffer with the string object - protected static int CompareStrings(int offset_1, string key, ByteBuffer bb) + protected static int CompareStrings(int offset_1, byte[] key, ByteBuffer bb) { offset_1 += bb.GetInt(offset_1); var len_1 = bb.GetInt(offset_1); var len_2 = key.Length; var startPos_1 = offset_1 + sizeof(int); var len = Math.Min(len_1, len_2); + byte[] bbArray = bb.Data; for (int i = 0; i < len; i++) { - if (bb.Data[i + startPos_1] != key[i]) - return bb.Data[i + startPos_1] - key[i]; + if (bbArray[i + startPos_1] != key[i]) + return bbArray[i + startPos_1] - key[i]; } - if (len_1 < len_2) return -1; - if (len_1 > len_2) return 1; - return 0; + return len_1 - len_2; } } diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index f2efe7192..04deb9a0b 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -699,7 +699,7 @@ std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) { if (key_field->value.type.base_type == BASE_TYPE_STRING) { key_getter += "comp = " + FunctionStart('C') + "ompareStrings("; key_getter += GenOffsetGetter(key_field); - key_getter += ", key, bb);\n"; + key_getter += ", byteKey, bb);\n"; } else { auto get_val = GenGetter(key_field->value.type) + @@ -1237,6 +1237,11 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { code += "ookupByKey(" + GenVectorOffsetType(); code += " vectorOffset, " + GenTypeGet(key_field->value.type); code += " key, ByteBuffer bb) {\n"; + code += " byte[] byteKey = "; + if (lang_.language == IDLOptions::kJava) + code += "key.getBytes(StandardCharsets.UTF_8);\n"; + else + code += "System.Text.Encoding.UTF8.GetBytes(key);\n"; code += " int vectorLocation = " + GenByteBufferLength("bb"); code += " - vectorOffset.Value;\n int span = "; code += "bb." + FunctionStart('G') + "etInt(vectorLocation), "; diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs index ce89443a4..95a303cf4 100644 --- a/tests/MyGame/Example/Monster.cs +++ b/tests/MyGame/Example/Monster.cs @@ -136,13 +136,14 @@ public sealed class Monster : Table { } public static Monster LookupByKey(VectorOffset vectorOffset, string key, ByteBuffer bb) { + byte[] byteKey = System.Text.Encoding.UTF8.GetBytes(key); int vectorLocation = bb.Length - vectorOffset.Value; int span = bb.GetInt(vectorLocation), middle, start = 0, comp, tableOffset; vectorLocation += 4; while (span != 0) { int middle = span / 2; tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb); - comp = CompareStrings(__offset(10, bb.Length - tableOffset, bb), key, bb); + comp = CompareStrings(__offset(10, bb.Length - tableOffset, bb), byteKey, bb); if (comp > 0) span = middle; else if (comp < 0) { middle++; diff --git a/tests/MyGame/Example/Monster.java b/tests/MyGame/Example/Monster.java index 2c20fa7d7..0633dff08 100644 --- a/tests/MyGame/Example/Monster.java +++ b/tests/MyGame/Example/Monster.java @@ -140,13 +140,14 @@ public final class Monster extends Table { 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) { + byte[] byteKey = key.getBytes(StandardCharsets.UTF_8); int vectorLocation = bb.array().length - vectorOffset.Value; int span = bb.getInt(vectorLocation), middle, start = 0, comp, tableOffset; vectorLocation += 4; while (span != 0) { int middle = span / 2; tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb); - comp = compareStrings(__offset(10, bb.array().length - tableOffset, bb), key, bb); + comp = compareStrings(__offset(10, bb.array().length - tableOffset, bb), byteKey, bb); if (comp > 0) span = middle; else if (comp < 0) { middle++;