summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVladimir Glavnyy <31897320+vglavnyy@users.noreply.github.com>2021-01-08 02:24:59 +0700
committerGitHub <noreply@github.com>2021-01-07 11:24:59 -0800
commit83ce29cc22cc80546b38e94e59f95f9a72fc1c6d (patch)
tree9089380f0fc76c50e65ee6564a4eb6ba56c79fb1 /src
parent4363c1d2cb898768073128bb92db9d4c63b43300 (diff)
downloadflatbuffers-83ce29cc22cc80546b38e94e59f95f9a72fc1c6d.tar.gz
flatbuffers-83ce29cc22cc80546b38e94e59f95f9a72fc1c6d.tar.bz2
flatbuffers-83ce29cc22cc80546b38e94e59f95f9a72fc1c6d.zip
[C++, JSON] Fix nullptr access when reading a key with a default value. (#6375)
This commit fixes handling of default and NULL `key` fields in `Parser::ParseVector` (#5928). The JSON generator updated. It outputs `key` fields even if the `--force-defaults` option is inactive. Additional test cases for `key` added.
Diffstat (limited to 'src')
-rw-r--r--src/idl_gen_text.cpp2
-rw-r--r--src/idl_parser.cpp123
2 files changed, 82 insertions, 43 deletions
diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp
index e4d21824..903c41ec 100644
--- a/src/idl_gen_text.cpp
+++ b/src/idl_gen_text.cpp
@@ -292,7 +292,7 @@ struct JsonPrinter {
it != struct_def.fields.vec.end(); ++it) {
FieldDef &fd = **it;
auto is_present = struct_def.fixed || table->CheckField(fd.value.offset);
- auto output_anyway = opts.output_default_scalars_in_json &&
+ auto output_anyway = (opts.output_default_scalars_in_json || fd.key) &&
IsScalar(fd.value.type.base_type) && !fd.deprecated;
if (is_present || output_anyway) {
if (fieldout++) { AddComma(); }
diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp
index 92dee3ee..31d702fd 100644
--- a/src/idl_parser.cpp
+++ b/src/idl_parser.cpp
@@ -1351,22 +1351,71 @@ CheckedError Parser::ParseVectorDelimiters(uoffset_t &count, F body) {
return NoError();
}
-static bool CompareType(const uint8_t *a, const uint8_t *b, BaseType ftype) {
- switch (ftype) {
-#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \
- case BASE_TYPE_##ENUM: return ReadScalar<CTYPE>(a) < ReadScalar<CTYPE>(b);
+static bool CompareSerializedScalars(const uint8_t *a, const uint8_t *b,
+ const FieldDef &key) {
+ switch (key.value.type.base_type) {
+#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \
+ case BASE_TYPE_##ENUM: { \
+ CTYPE def = static_cast<CTYPE>(0); \
+ if (!a || !b) { StringToNumber(key.value.constant.c_str(), &def); } \
+ const auto av = a ? ReadScalar<CTYPE>(a) : def; \
+ const auto bv = b ? ReadScalar<CTYPE>(b) : def; \
+ return av < bv; \
+ }
FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
- case BASE_TYPE_STRING:
- // Indirect offset pointer to string pointer.
- a += ReadScalar<uoffset_t>(a);
- b += ReadScalar<uoffset_t>(b);
- return *reinterpret_cast<const String *>(a) <
- *reinterpret_cast<const String *>(b);
- default: return false;
+ default: {
+ FLATBUFFERS_ASSERT(false && "scalar type expected");
+ return false;
+ }
+ }
+}
+
+static bool CompareTablesByScalarKey(const Offset<Table> *_a,
+ const Offset<Table> *_b,
+ const FieldDef &key) {
+ const voffset_t offset = key.value.offset;
+ // Indirect offset pointer to table pointer.
+ auto a = reinterpret_cast<const uint8_t *>(_a) + ReadScalar<uoffset_t>(_a);
+ auto b = reinterpret_cast<const uint8_t *>(_b) + ReadScalar<uoffset_t>(_b);
+ // Fetch field address from table.
+ a = reinterpret_cast<const Table *>(a)->GetAddressOf(offset);
+ b = reinterpret_cast<const Table *>(b)->GetAddressOf(offset);
+ return CompareSerializedScalars(a, b, key);
+}
+
+static bool CompareTablesByStringKey(const Offset<Table> *_a,
+ const Offset<Table> *_b,
+ const FieldDef &key) {
+ const voffset_t offset = key.value.offset;
+ // Indirect offset pointer to table pointer.
+ auto a = reinterpret_cast<const uint8_t *>(_a) + ReadScalar<uoffset_t>(_a);
+ auto b = reinterpret_cast<const uint8_t *>(_b) + ReadScalar<uoffset_t>(_b);
+ // Fetch field address from table.
+ a = reinterpret_cast<const Table *>(a)->GetAddressOf(offset);
+ b = reinterpret_cast<const Table *>(b)->GetAddressOf(offset);
+ if (a && b) {
+ // Indirect offset pointer to string pointer.
+ a += ReadScalar<uoffset_t>(a);
+ b += ReadScalar<uoffset_t>(b);
+ return *reinterpret_cast<const String *>(a) <
+ *reinterpret_cast<const String *>(b);
+ } else {
+ return a ? true : false;
}
}
+static void SwapSerializedTables(Offset<Table> *a, Offset<Table> *b) {
+ // These are serialized offsets, so are relative where they are
+ // stored in memory, so compute the distance between these pointers:
+ ptrdiff_t diff = (b - a) * sizeof(Offset<Table>);
+ FLATBUFFERS_ASSERT(diff >= 0); // Guaranteed by SimpleQsort.
+ auto udiff = static_cast<uoffset_t>(diff);
+ a->o = EndianScalar(ReadScalar<uoffset_t>(a) - udiff);
+ b->o = EndianScalar(ReadScalar<uoffset_t>(b) + udiff);
+ std::swap(*a, *b);
+}
+
// See below for why we need our own sort :(
template<typename T, typename F, typename S>
void SimpleQsort(T *begin, T *end, size_t width, F comparator, S swapper) {
@@ -1451,23 +1500,21 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue,
// globals, making parsing thread-unsafe.
// So for now, we use SimpleQsort above.
// TODO: replace with something better, preferably not recursive.
- voffset_t offset = key->value.offset;
- BaseType ftype = key->value.type.base_type;
if (type.struct_def->fixed) {
+ const voffset_t offset = key->value.offset;
+ const size_t struct_size = type.struct_def->bytesize;
auto v =
reinterpret_cast<VectorOfAny *>(builder_.GetCurrentBufferPointer());
SimpleQsort<uint8_t>(
v->Data(), v->Data() + v->size() * type.struct_def->bytesize,
type.struct_def->bytesize,
- [&](const uint8_t *a, const uint8_t *b) -> bool {
- return CompareType(a + offset, b + offset, ftype);
+ [offset, key](const uint8_t *a, const uint8_t *b) -> bool {
+ return CompareSerializedScalars(a + offset, b + offset, *key);
},
- [&](uint8_t *a, uint8_t *b) {
+ [struct_size](uint8_t *a, uint8_t *b) {
// FIXME: faster?
- for (size_t i = 0; i < type.struct_def->bytesize; i++) {
- std::swap(a[i], b[i]);
- }
+ for (size_t i = 0; i < struct_size; i++) { std::swap(a[i], b[i]); }
});
} else {
auto v = reinterpret_cast<Vector<Offset<Table>> *>(
@@ -1475,29 +1522,21 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue,
// Here also can't use std::sort. We do have an iterator type for it,
// but it is non-standard as it will dereference the offsets, and thus
// can't be used to swap elements.
- SimpleQsort<Offset<Table>>(
- v->data(), v->data() + v->size(), 1,
- [&](const Offset<Table> *_a, const Offset<Table> *_b) -> bool {
- // Indirect offset pointer to table pointer.
- auto a = reinterpret_cast<const uint8_t *>(_a) +
- ReadScalar<uoffset_t>(_a);
- auto b = reinterpret_cast<const uint8_t *>(_b) +
- ReadScalar<uoffset_t>(_b);
- // Fetch field address from table.
- a = reinterpret_cast<const Table *>(a)->GetAddressOf(offset);
- b = reinterpret_cast<const Table *>(b)->GetAddressOf(offset);
- return CompareType(a, b, ftype);
- },
- [&](Offset<Table> *a, Offset<Table> *b) {
- // These are serialized offsets, so are relative where they are
- // stored in memory, so compute the distance between these pointers:
- ptrdiff_t diff = (b - a) * sizeof(Offset<Table>);
- FLATBUFFERS_ASSERT(diff >= 0); // Guaranteed by SimpleQsort.
- auto udiff = static_cast<uoffset_t>(diff);
- a->o = EndianScalar(ReadScalar<uoffset_t>(a) - udiff);
- b->o = EndianScalar(ReadScalar<uoffset_t>(b) + udiff);
- std::swap(*a, *b);
- });
+ if (key->value.type.base_type == BASE_TYPE_STRING) {
+ SimpleQsort<Offset<Table>>(
+ v->data(), v->data() + v->size(), 1,
+ [key](const Offset<Table> *_a, const Offset<Table> *_b) -> bool {
+ return CompareTablesByStringKey(_a, _b, *key);
+ },
+ SwapSerializedTables);
+ } else {
+ SimpleQsort<Offset<Table>>(
+ v->data(), v->data() + v->size(), 1,
+ [key](const Offset<Table> *_a, const Offset<Table> *_b) -> bool {
+ return CompareTablesByScalarKey(_a, _b, *key);
+ },
+ SwapSerializedTables);
+ }
}
}
return NoError();