diff --git a/src/idl_gen_csharp.cpp b/src/idl_gen_csharp.cpp index b530f4f84..0d216b549 100644 --- a/src/idl_gen_csharp.cpp +++ b/src/idl_gen_csharp.cpp @@ -459,20 +459,24 @@ class CSharpGenerator : public BaseGenerator { } } + std::string GetObjectConstructor(flatbuffers::StructDef &struct_def, + const std::string &data_buffer, + const std::string &offset) const { + // Use the generated type directly, to properly handle default values that + // might not be written to the buffer. + return "new " + Name(struct_def) + "().__assign(" + offset + ", " + + data_buffer + ")"; + } + // Returns the function name that is able to read a value of the given type. - std::string GenGetterForLookupByKey(flatbuffers::FieldDef *key_field, + std::string GenGetterForLookupByKey(flatbuffers::StructDef &struct_def, + flatbuffers::FieldDef *key_field, const std::string &data_buffer, - const char *num = nullptr) const { - auto type = key_field->value.type; - auto dest_mask = ""; - auto dest_cast = DestinationCast(type); - auto getter = data_buffer + ".Get"; - if (GenTypeBasic(type, false) != "byte") { - getter += ConvertCase(GenTypeBasic(type, false), Case::kUpperCamel); - } - getter = dest_cast + getter + "(" + GenOffsetGetter(key_field, num) + ")" + - dest_mask; - return getter; + const std::string &offset) const { + // Use the generated type directly, to properly handle default values that + // might not be written to the buffer. + return GetObjectConstructor(struct_def, data_buffer, offset) + "." + + Name(*key_field); } // Direct mutation is only allowed for scalar fields. @@ -605,37 +609,15 @@ class CSharpGenerator : public BaseGenerator { return key_offset; } - std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) const { - std::string key_getter = " "; - key_getter += "int tableOffset = Table."; - key_getter += "__indirect(vectorLocation + 4 * (start + middle)"; - key_getter += ", bb);\n "; - if (IsString(key_field->value.type)) { - key_getter += "int comp = Table."; - key_getter += "CompareStrings("; - key_getter += GenOffsetGetter(key_field); - key_getter += ", byteKey, bb);\n"; - } else { - auto get_val = GenGetterForLookupByKey(key_field, "bb"); - key_getter += "int comp = " + get_val + ".CompareTo(key);\n"; - } - return key_getter; - } - - std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const { - std::string key_getter = ""; - auto data_buffer = "builder.DataBuffer"; - if (IsString(key_field->value.type)) { - key_getter += "Table.CompareStrings("; - key_getter += GenOffsetGetter(key_field, "o1") + ", "; - key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")"; - } else { - auto field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o1"); - key_getter += field_getter; - field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2"); - key_getter += ".CompareTo(" + field_getter + ")"; - } - return key_getter; + std::string GenKeyGetter(flatbuffers::StructDef &struct_def, + flatbuffers::FieldDef *key_field) const { + // Get the getter for the key of the struct. + return GenGetterForLookupByKey(struct_def, key_field, "builder.DataBuffer", + "builder.DataBuffer.Length - o1.Value") + + ".CompareTo(" + + GenGetterForLookupByKey(struct_def, key_field, "builder.DataBuffer", + "builder.DataBuffer.Length - o2.Value") + + ")"; } void GenStruct(StructDef &struct_def, std::string *code_ptr, @@ -1240,7 +1222,8 @@ class CSharpGenerator : public BaseGenerator { code += "); return "; code += "builder.EndVector(); }\n"; - // add Create...VectorBlock() overloads for T[], ArraySegment and IntPtr + // add Create...VectorBlock() overloads for T[], ArraySegment and + // IntPtr code += " public static VectorOffset "; code += "Create"; code += Name(field); @@ -1269,7 +1252,8 @@ class CSharpGenerator : public BaseGenerator { code += "VectorBlock(FlatBufferBuilder builder, "; code += "IntPtr dataPtr, int sizeInBytes) "; code += "{ builder.StartVector(1, sizeInBytes, 1); "; - code += "builder.Add<" + GenTypeBasic(vector_type) + ">(dataPtr, sizeInBytes); return builder.EndVector(); }\n"; + code += "builder.Add<" + GenTypeBasic(vector_type) + + ">(dataPtr, sizeInBytes); return builder.EndVector(); }\n"; } // Generate a method to start a vector, data to be added manually // after. @@ -1322,9 +1306,10 @@ class CSharpGenerator : public BaseGenerator { code += "(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 += " Array.Sort(offsets,\n"; + code += " (Offset<" + struct_def.name + + "> o1, Offset<" + struct_def.name + "> o2) =>\n"; + code += " "+ GenKeyGetter(struct_def, key_field); code += ");\n"; code += " return builder.CreateVectorOfTables(offsets);\n }\n"; @@ -1333,16 +1318,20 @@ class CSharpGenerator : public BaseGenerator { code += "int vectorLocation, "; code += GenTypeGet(key_field->value.type); code += " key, ByteBuffer bb) {\n"; - if (IsString(key_field->value.type)) { - code += " byte[] byteKey = "; - code += "System.Text.Encoding.UTF8.GetBytes(key);\n"; - } + code += + " " + struct_def.name + " obj_ = new " + struct_def.name + "();\n"; code += " int span = "; code += "bb.GetInt(vectorLocation - 4);\n"; code += " int start = 0;\n"; code += " while (span != 0) {\n"; code += " int middle = span / 2;\n"; - code += GenLookupKeyGetter(key_field); + code += + " int tableOffset = Table.__indirect(vectorLocation + 4 * " + "(start + middle), bb);\n"; + + code += " obj_.__assign(tableOffset, bb);\n"; + code += + " int comp = obj_." + Name(*key_field) + ".CompareTo(key);\n"; code += " if (comp > 0) {\n"; code += " span = middle;\n"; code += " } else if (comp < 0) {\n"; @@ -1350,9 +1339,7 @@ class CSharpGenerator : public BaseGenerator { code += " start += middle;\n"; code += " span -= middle;\n"; code += " } else {\n"; - code += " return "; - code += "new " + struct_def.name + "()"; - code += ".__assign(tableOffset, bb);\n"; + code += " return obj_;\n"; code += " }\n }\n"; code += " return null;\n"; code += " }\n"; @@ -1608,8 +1595,7 @@ class CSharpGenerator : public BaseGenerator { } code += NamespacedName(enum_def) + "Union();\n"; code += indent + varialbe_name + ".Type = this." + camel_name_short + - "Type" + - type_suffix + ";\n"; + "Type" + type_suffix + ";\n"; code += indent + "switch (this." + camel_name_short + "Type" + type_suffix + ") {\n"; for (auto eit = enum_def.Vals().begin(); eit != enum_def.Vals().end(); diff --git a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs index ec3b2eaa5..2b1fe2d3e 100644 --- a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs +++ b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs @@ -156,8 +156,11 @@ namespace FlatBuffers.Test // Example of searching for a table by the key Assert.IsTrue(monster.TestarrayoftablesByKey("Frodo") != null); + Assert.AreEqual(monster.TestarrayoftablesByKey("Frodo").Value.Name, "Frodo"); Assert.IsTrue(monster.TestarrayoftablesByKey("Barney") != null); + Assert.AreEqual(monster.TestarrayoftablesByKey("Barney").Value.Name, "Barney"); Assert.IsTrue(monster.TestarrayoftablesByKey("Wilma") != null); + Assert.AreEqual(monster.TestarrayoftablesByKey("Wilma").Value.Name, "Wilma"); // testType is an existing field Assert.AreEqual(monster.TestType, Any.Monster); @@ -1152,5 +1155,41 @@ namespace FlatBuffers.Test ScalarStuff scalarStuff = ScalarStuff.GetRootAsScalarStuff(fbb.DataBuffer); Assert.AreEqual(null, scalarStuff.MaybeEnum); } + + + [FlatBuffersTestMethod] + public void SortKey_WithDefaultedValue_IsFindable() { + // This checks if using the `key` attribute that includes the + // default value (e.g., 0) is still searchable. This is a regression + // test for https://github.com/google/flatbuffers/issues/7380. + var fbb = new FlatBufferBuilder(1); + + // Create a vector of Stat objects, with Count being the key. + var stat_offsets = new Offset[4]; + for(ushort i = 0; i < stat_offsets.Length; i++) { + Stat.StartStat(fbb); + Stat.AddCount(fbb, i); + stat_offsets[stat_offsets.Length - 1 - i] = Stat.EndStat(fbb); + } + + // Ensure the sort works. + var sort = Stat.CreateSortedVectorOfStat(fbb, stat_offsets); + + // Create the monster with the sorted vector of Stat objects. + var str = fbb.CreateString("MyMonster"); + Monster.StartMonster(fbb); + Monster.AddName(fbb, str); + Monster.AddScalarKeySortedTables(fbb, sort); + fbb.Finish(Monster.EndMonster(fbb).Value); + + // Get the monster. + var monster = Monster.GetRootAsMonster(fbb.DataBuffer); + + // Ensure each key is findable. + for(ushort i =0 ; i < stat_offsets.Length; i++) { + Assert.IsTrue(monster.ScalarKeySortedTablesByKey(i) != null); + Assert.AreEqual(monster.ScalarKeySortedTablesByKey(i).Value.Count, i); + } + } } } diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs index c258ffccc..124fb79aa 100644 --- a/tests/MyGame/Example/Monster.cs +++ b/tests/MyGame/Example/Monster.cs @@ -478,18 +478,21 @@ public struct Monster : IFlatbufferObject public static void FinishSizePrefixedMonsterBuffer(FlatBufferBuilder builder, Offset offset) { builder.FinishSizePrefixed(offset.Value, "MONS"); } 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)); + Array.Sort(offsets, + (Offset o1, Offset o2) => + new Monster().__assign(builder.DataBuffer.Length - o1.Value, builder.DataBuffer).Name.CompareTo(new Monster().__assign(builder.DataBuffer.Length - o2.Value, builder.DataBuffer).Name)); return builder.CreateVectorOfTables(offsets); } public static Monster? __lookup_by_key(int vectorLocation, string key, ByteBuffer bb) { - byte[] byteKey = System.Text.Encoding.UTF8.GetBytes(key); + Monster obj_ = new Monster(); int span = bb.GetInt(vectorLocation - 4); int start = 0; while (span != 0) { int middle = span / 2; int tableOffset = Table.__indirect(vectorLocation + 4 * (start + middle), bb); - int comp = Table.CompareStrings(Table.__offset(10, bb.Length - tableOffset, bb), byteKey, bb); + obj_.__assign(tableOffset, bb); + int comp = obj_.Name.CompareTo(key); if (comp > 0) { span = middle; } else if (comp < 0) { @@ -497,7 +500,7 @@ public struct Monster : IFlatbufferObject start += middle; span -= middle; } else { - return new Monster().__assign(tableOffset, bb); + return obj_; } } return null; diff --git a/tests/MyGame/Example/Referrable.cs b/tests/MyGame/Example/Referrable.cs index e6f3146f3..1af3105ef 100644 --- a/tests/MyGame/Example/Referrable.cs +++ b/tests/MyGame/Example/Referrable.cs @@ -37,17 +37,21 @@ public struct Referrable : IFlatbufferObject } public static VectorOffset CreateSortedVectorOfReferrable(FlatBufferBuilder builder, Offset[] offsets) { - Array.Sort(offsets, (Offset o1, Offset o2) => builder.DataBuffer.GetUlong(Table.__offset(4, o1.Value, builder.DataBuffer)).CompareTo(builder.DataBuffer.GetUlong(Table.__offset(4, o2.Value, builder.DataBuffer)))); + Array.Sort(offsets, + (Offset o1, Offset o2) => + new Referrable().__assign(builder.DataBuffer.Length - o1.Value, builder.DataBuffer).Id.CompareTo(new Referrable().__assign(builder.DataBuffer.Length - o2.Value, builder.DataBuffer).Id)); return builder.CreateVectorOfTables(offsets); } public static Referrable? __lookup_by_key(int vectorLocation, ulong key, ByteBuffer bb) { + Referrable obj_ = new Referrable(); int span = bb.GetInt(vectorLocation - 4); int start = 0; while (span != 0) { int middle = span / 2; int tableOffset = Table.__indirect(vectorLocation + 4 * (start + middle), bb); - int comp = bb.GetUlong(Table.__offset(4, bb.Length - tableOffset, bb)).CompareTo(key); + obj_.__assign(tableOffset, bb); + int comp = obj_.Id.CompareTo(key); if (comp > 0) { span = middle; } else if (comp < 0) { @@ -55,7 +59,7 @@ public struct Referrable : IFlatbufferObject start += middle; span -= middle; } else { - return new Referrable().__assign(tableOffset, bb); + return obj_; } } return null; diff --git a/tests/MyGame/Example/Stat.cs b/tests/MyGame/Example/Stat.cs index 149b136fc..333941947 100644 --- a/tests/MyGame/Example/Stat.cs +++ b/tests/MyGame/Example/Stat.cs @@ -52,17 +52,21 @@ public struct Stat : IFlatbufferObject } public static VectorOffset CreateSortedVectorOfStat(FlatBufferBuilder builder, Offset[] offsets) { - Array.Sort(offsets, (Offset o1, Offset o2) => builder.DataBuffer.GetUshort(Table.__offset(8, o1.Value, builder.DataBuffer)).CompareTo(builder.DataBuffer.GetUshort(Table.__offset(8, o2.Value, builder.DataBuffer)))); + Array.Sort(offsets, + (Offset o1, Offset o2) => + new Stat().__assign(builder.DataBuffer.Length - o1.Value, builder.DataBuffer).Count.CompareTo(new Stat().__assign(builder.DataBuffer.Length - o2.Value, builder.DataBuffer).Count)); return builder.CreateVectorOfTables(offsets); } public static Stat? __lookup_by_key(int vectorLocation, ushort key, ByteBuffer bb) { + Stat obj_ = new Stat(); int span = bb.GetInt(vectorLocation - 4); int start = 0; while (span != 0) { int middle = span / 2; int tableOffset = Table.__indirect(vectorLocation + 4 * (start + middle), bb); - int comp = bb.GetUshort(Table.__offset(8, bb.Length - tableOffset, bb)).CompareTo(key); + obj_.__assign(tableOffset, bb); + int comp = obj_.Count.CompareTo(key); if (comp > 0) { span = middle; } else if (comp < 0) { @@ -70,7 +74,7 @@ public struct Stat : IFlatbufferObject start += middle; span -= middle; } else { - return new Stat().__assign(tableOffset, bb); + return obj_; } } return null;