Skip to content

Commit 0cfb7eb

Browse files
Fix handling non null-terminated string_views in LookupByKey (#8203)
* Reproduce the error in a unit test Reproduces #8200 * Overload KeyCompareWithValue to work for string-like objects This fixes #8200. * Extra tests --------- Co-authored-by: Derek Bailey <derekbailey@google.com>
1 parent 67eb95d commit 0cfb7eb

File tree

9 files changed

+117
-0
lines changed

9 files changed

+117
-0
lines changed

include/flatbuffers/reflection_generated.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ struct KeyValue FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
274274
int KeyCompareWithValue(const char *_key) const {
275275
return strcmp(key()->c_str(), _key);
276276
}
277+
template<typename StringType>
278+
int KeyCompareWithValue(const StringType& _key) const {
279+
if (key()->c_str() < _key) return -1;
280+
if (_key < key()->c_str()) return 1;
281+
return 0;
282+
}
277283
const ::flatbuffers::String *value() const {
278284
return GetPointer<const ::flatbuffers::String *>(VT_VALUE);
279285
}
@@ -464,6 +470,12 @@ struct Enum FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
464470
int KeyCompareWithValue(const char *_name) const {
465471
return strcmp(name()->c_str(), _name);
466472
}
473+
template<typename StringType>
474+
int KeyCompareWithValue(const StringType& _name) const {
475+
if (name()->c_str() < _name) return -1;
476+
if (_name < name()->c_str()) return 1;
477+
return 0;
478+
}
467479
const ::flatbuffers::Vector<::flatbuffers::Offset<reflection::EnumVal>> *values() const {
468480
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<reflection::EnumVal>> *>(VT_VALUES);
469481
}
@@ -616,6 +628,12 @@ struct Field FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
616628
int KeyCompareWithValue(const char *_name) const {
617629
return strcmp(name()->c_str(), _name);
618630
}
631+
template<typename StringType>
632+
int KeyCompareWithValue(const StringType& _name) const {
633+
if (name()->c_str() < _name) return -1;
634+
if (_name < name()->c_str()) return 1;
635+
return 0;
636+
}
619637
const reflection::Type *type() const {
620638
return GetPointer<const reflection::Type *>(VT_TYPE);
621639
}
@@ -834,6 +852,12 @@ struct Object FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
834852
int KeyCompareWithValue(const char *_name) const {
835853
return strcmp(name()->c_str(), _name);
836854
}
855+
template<typename StringType>
856+
int KeyCompareWithValue(const StringType& _name) const {
857+
if (name()->c_str() < _name) return -1;
858+
if (_name < name()->c_str()) return 1;
859+
return 0;
860+
}
837861
const ::flatbuffers::Vector<::flatbuffers::Offset<reflection::Field>> *fields() const {
838862
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<reflection::Field>> *>(VT_FIELDS);
839863
}
@@ -986,6 +1010,12 @@ struct RPCCall FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
9861010
int KeyCompareWithValue(const char *_name) const {
9871011
return strcmp(name()->c_str(), _name);
9881012
}
1013+
template<typename StringType>
1014+
int KeyCompareWithValue(const StringType& _name) const {
1015+
if (name()->c_str() < _name) return -1;
1016+
if (_name < name()->c_str()) return 1;
1017+
return 0;
1018+
}
9891019
const reflection::Object *request() const {
9901020
return GetPointer<const reflection::Object *>(VT_REQUEST);
9911021
}
@@ -1102,6 +1132,12 @@ struct Service FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
11021132
int KeyCompareWithValue(const char *_name) const {
11031133
return strcmp(name()->c_str(), _name);
11041134
}
1135+
template<typename StringType>
1136+
int KeyCompareWithValue(const StringType& _name) const {
1137+
if (name()->c_str() < _name) return -1;
1138+
if (_name < name()->c_str()) return 1;
1139+
return 0;
1140+
}
11051141
const ::flatbuffers::Vector<::flatbuffers::Offset<reflection::RPCCall>> *calls() const {
11061142
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<reflection::RPCCall>> *>(VT_CALLS);
11071143
}
@@ -1221,6 +1257,12 @@ struct SchemaFile FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
12211257
int KeyCompareWithValue(const char *_filename) const {
12221258
return strcmp(filename()->c_str(), _filename);
12231259
}
1260+
template<typename StringType>
1261+
int KeyCompareWithValue(const StringType& _filename) const {
1262+
if (filename()->c_str() < _filename) return -1;
1263+
if (_filename < filename()->c_str()) return 1;
1264+
return 0;
1265+
}
12241266
/// Names of included files, relative to project root.
12251267
const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *included_filenames() const {
12261268
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *>(VT_INCLUDED_FILENAMES);

src/idl_gen_cpp.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2417,8 +2417,20 @@ class CppGenerator : public BaseGenerator {
24172417

24182418
// Generate KeyCompareWithValue function
24192419
if (is_string) {
2420+
// Compares key against a null-terminated char array.
24202421
code_ += " int KeyCompareWithValue(const char *_{{FIELD_NAME}}) const {";
24212422
code_ += " return strcmp({{FIELD_NAME}}()->c_str(), _{{FIELD_NAME}});";
2423+
code_ += " }";
2424+
// Compares key against any string-like object (e.g. std::string_view or
2425+
// std::string) that implements operator< comparison with const char*.
2426+
code_ += " template<typename StringType>";
2427+
code_ +=
2428+
" int KeyCompareWithValue(const StringType& _{{FIELD_NAME}}) const "
2429+
"{";
2430+
code_ +=
2431+
" if ({{FIELD_NAME}}()->c_str() < _{{FIELD_NAME}}) return -1;";
2432+
code_ += " if (_{{FIELD_NAME}} < {{FIELD_NAME}}()->c_str()) return 1;";
2433+
code_ += " return 0;";
24222434
} else if (is_array) {
24232435
const auto &elem_type = field.value.type.VectorType();
24242436
std::string input_type = "::flatbuffers::Array<" +

tests/cpp17/generated_cpp17/monster_test_generated.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1436,6 +1436,12 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
14361436
int KeyCompareWithValue(const char *_name) const {
14371437
return strcmp(name()->c_str(), _name);
14381438
}
1439+
template<typename StringType>
1440+
int KeyCompareWithValue(const StringType& _name) const {
1441+
if (name()->c_str() < _name) return -1;
1442+
if (_name < name()->c_str()) return 1;
1443+
return 0;
1444+
}
14391445
const ::flatbuffers::Vector<uint8_t> *inventory() const {
14401446
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_INVENTORY);
14411447
}

tests/key_field/key_field_sample_generated.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,12 @@ struct FooTable FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
598598
int KeyCompareWithValue(const char *_c) const {
599599
return strcmp(c()->c_str(), _c);
600600
}
601+
template<typename StringType>
602+
int KeyCompareWithValue(const StringType& _c) const {
603+
if (c()->c_str() < _c) return -1;
604+
if (_c < c()->c_str()) return 1;
605+
return 0;
606+
}
601607
const ::flatbuffers::Vector<const keyfield::sample::Baz *> *d() const {
602608
return GetPointer<const ::flatbuffers::Vector<const keyfield::sample::Baz *> *>(VT_D);
603609
}

tests/monster_test.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,33 @@ void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length, bool pooled) {
313313
TEST_NOTNULL(vecoftables->LookupByKey("Fred"));
314314
TEST_NOTNULL(vecoftables->LookupByKey("Wilma"));
315315

316+
// Verify the same objects are returned for char*-based and string-based
317+
// lookups.
318+
TEST_EQ(vecoftables->LookupByKey("Barney"),
319+
vecoftables->LookupByKey(std::string("Barney")));
320+
TEST_EQ(vecoftables->LookupByKey("Fred"),
321+
vecoftables->LookupByKey(std::string("Fred")));
322+
TEST_EQ(vecoftables->LookupByKey("Wilma"),
323+
vecoftables->LookupByKey(std::string("Wilma")));
324+
325+
#ifdef FLATBUFFERS_HAS_STRING_VIEW
326+
// Tests for LookupByKey with a key that is a truncated
327+
// version of a longer, invalid key.
328+
const std::string invalid_key = "Barney123";
329+
std::string_view valid_truncated_key = invalid_key;
330+
valid_truncated_key.remove_suffix(3); // "Barney"
331+
TEST_NOTNULL(vecoftables->LookupByKey(valid_truncated_key));
332+
TEST_EQ(vecoftables->LookupByKey("Barney"),
333+
vecoftables->LookupByKey(valid_truncated_key));
334+
335+
// Tests for LookupByKey with a key that is a truncated
336+
// version of a longer, valid key.
337+
const std::string valid_key = "Barney";
338+
std::string_view invalid_truncated_key = valid_key;
339+
invalid_truncated_key.remove_suffix(3); // "Bar"
340+
TEST_NULL(vecoftables->LookupByKey(invalid_truncated_key));
341+
#endif // FLATBUFFERS_HAS_STRING_VIEW
342+
316343
// Test accessing a vector of sorted structs
317344
auto vecofstructs = monster->testarrayofsortedstruct();
318345
if (vecofstructs) { // not filled in monster_test.bfbs

tests/monster_test_generated.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,12 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
14321432
int KeyCompareWithValue(const char *_name) const {
14331433
return strcmp(name()->c_str(), _name);
14341434
}
1435+
template<typename StringType>
1436+
int KeyCompareWithValue(const StringType& _name) const {
1437+
if (name()->c_str() < _name) return -1;
1438+
if (_name < name()->c_str()) return 1;
1439+
return 0;
1440+
}
14351441
const ::flatbuffers::Vector<uint8_t> *inventory() const {
14361442
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_INVENTORY);
14371443
}

tests/monster_test_suffix/ext_only/monster_test_generated.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,12 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
14231423
int KeyCompareWithValue(const char *_name) const {
14241424
return strcmp(name()->c_str(), _name);
14251425
}
1426+
template<typename StringType>
1427+
int KeyCompareWithValue(const StringType& _name) const {
1428+
if (name()->c_str() < _name) return -1;
1429+
if (_name < name()->c_str()) return 1;
1430+
return 0;
1431+
}
14261432
const ::flatbuffers::Vector<uint8_t> *inventory() const {
14271433
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_INVENTORY);
14281434
}

tests/monster_test_suffix/filesuffix_only/monster_test_suffix.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,12 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
14231423
int KeyCompareWithValue(const char *_name) const {
14241424
return strcmp(name()->c_str(), _name);
14251425
}
1426+
template<typename StringType>
1427+
int KeyCompareWithValue(const StringType& _name) const {
1428+
if (name()->c_str() < _name) return -1;
1429+
if (_name < name()->c_str()) return 1;
1430+
return 0;
1431+
}
14261432
const ::flatbuffers::Vector<uint8_t> *inventory() const {
14271433
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_INVENTORY);
14281434
}

tests/monster_test_suffix/monster_test_suffix.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,12 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
14231423
int KeyCompareWithValue(const char *_name) const {
14241424
return strcmp(name()->c_str(), _name);
14251425
}
1426+
template<typename StringType>
1427+
int KeyCompareWithValue(const StringType& _name) const {
1428+
if (name()->c_str() < _name) return -1;
1429+
if (_name < name()->c_str()) return 1;
1430+
return 0;
1431+
}
14261432
const ::flatbuffers::Vector<uint8_t> *inventory() const {
14271433
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_INVENTORY);
14281434
}

0 commit comments

Comments
 (0)