forked from BigfootDev/flatbuffers
C++98 (stlport) support for core FlatBuffers and FlexBuffers.
* Added internal - limited - implementation of flatbuffers::unique_ptr for STLs that don't ship with std::unique_ptr. In C++11 and beyond this is just an alias for std::unique_ptr. * Aliased used type traits structs is_scalar is_floating_point is_unsigned into flatbuffers namespace so they can be replaced in C++98 implementations. Right now these point at stlport's TR1 implementations. * Wrapped vector::data() in vector_data(). * Wrapped vector::emplace_back() in vector_emplace_back(). * Wrapper string::back() in string_back(). * Added variants of FlatBufferBuilder::CreateVector() and FlatBufferBuilder::CreateVectorOfStructs() that allow the use of plain function pointers. Generated code has also been modified to use plain functions to build objects rather than std::function() so all generated code will work in C++98 applications. * Added flexbuffers::Builder::Vector(), flexbuffers::Builder::TypedVector() and flexbuffers::Builder::Map() methods that allow the use of plain function pointers. * Changed Parser to internally use plain function pointers when parsing table and vector delimiters. * Added specializations of NumToString() for 64-bit types that aren't supported by stringstream in stlport. * Overloaded numeric_limits for 64-bit types not supported by stlport. * Replaced build_apk.sh (which was broken by deprecation of the "android" tool in the Android SDK) with build.gradle and the appropriate gradle wrapper to build an APK. * Switched Android build to build against all STL variants. * Updated travis configuration to build Android test and sample. Tested: * Verified all tests continue to work on Linux, OSX and Android. * Verified Travis build is green. Change-Id: I9e634363793f85b9f141d21454b10686020a2065
This commit is contained in:
@@ -106,8 +106,8 @@ CheckedError Parser::CheckInRange(int64_t val, int64_t min, int64_t max) {
|
||||
template<typename T> inline CheckedError atot(const char *s, Parser &parser,
|
||||
T *val) {
|
||||
int64_t i = StringToInt(s);
|
||||
const int64_t min = std::numeric_limits<T>::min();
|
||||
const int64_t max = std::numeric_limits<T>::max();
|
||||
const int64_t min = flatbuffers::numeric_limits<T>::min();
|
||||
const int64_t max = flatbuffers::numeric_limits<T>::max();
|
||||
ECHECK(parser.CheckInRange(i, min, max));
|
||||
*val = (T)i;
|
||||
return NoError();
|
||||
@@ -870,7 +870,8 @@ void Parser::SerializeStruct(const StructDef &struct_def, const Value &val) {
|
||||
|
||||
CheckedError Parser::ParseTableDelimiters(size_t &fieldn,
|
||||
const StructDef *struct_def,
|
||||
const std::function<CheckedError(const std::string &name)> &body) {
|
||||
ParseTableDelimitersBody body,
|
||||
void *state) {
|
||||
// We allow tables both as JSON object{ .. } with field names
|
||||
// or vector[..] with all fields in order
|
||||
char terminator = '}';
|
||||
@@ -898,7 +899,7 @@ CheckedError Parser::ParseTableDelimiters(size_t &fieldn,
|
||||
}
|
||||
if (!opts.protobuf_ascii_alike || !(Is('{') || Is('['))) EXPECT(':');
|
||||
}
|
||||
ECHECK(body(name));
|
||||
ECHECK(body(name, fieldn, struct_def, state));
|
||||
if (Is(terminator)) break;
|
||||
ECHECK(ParseComma());
|
||||
}
|
||||
@@ -911,52 +912,56 @@ CheckedError Parser::ParseTableDelimiters(size_t &fieldn,
|
||||
|
||||
CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
|
||||
uoffset_t *ovalue) {
|
||||
size_t fieldn = 0;
|
||||
auto err = ParseTableDelimiters(fieldn, &struct_def,
|
||||
[&](const std::string &name) -> CheckedError {
|
||||
size_t fieldn_outer = 0;
|
||||
auto err = ParseTableDelimiters(fieldn_outer, &struct_def,
|
||||
[](const std::string &name, size_t &fieldn,
|
||||
const StructDef *struct_def_inner,
|
||||
void *state) -> CheckedError {
|
||||
Parser *parser = static_cast<Parser *>(state);
|
||||
if (name == "$schema") {
|
||||
EXPECT(kTokenStringConstant);
|
||||
ECHECK(parser->Expect(kTokenStringConstant));
|
||||
return NoError();
|
||||
}
|
||||
auto field = struct_def.fields.Lookup(name);
|
||||
auto field = struct_def_inner->fields.Lookup(name);
|
||||
if (!field) {
|
||||
if (!opts.skip_unexpected_fields_in_json) {
|
||||
return Error("unknown field: " + name);
|
||||
if (!parser->opts.skip_unexpected_fields_in_json) {
|
||||
return parser->Error("unknown field: " + name);
|
||||
} else {
|
||||
ECHECK(SkipAnyJsonValue());
|
||||
ECHECK(parser->SkipAnyJsonValue());
|
||||
}
|
||||
} else {
|
||||
if (Is(kTokenNull)) {
|
||||
NEXT(); // Ignore this field.
|
||||
if (parser->Is(kTokenNull)) {
|
||||
ECHECK(parser->Next()); // Ignore this field.
|
||||
} else {
|
||||
Value val = field->value;
|
||||
if (field->flexbuffer) {
|
||||
flexbuffers::Builder builder(1024,
|
||||
flexbuffers::BUILDER_FLAG_SHARE_ALL);
|
||||
ECHECK(ParseFlexBufferValue(&builder));
|
||||
ECHECK(parser->ParseFlexBufferValue(&builder));
|
||||
builder.Finish();
|
||||
auto off = builder_.CreateVector(builder.GetBuffer());
|
||||
auto off = parser->builder_.CreateVector(builder.GetBuffer());
|
||||
val.constant = NumToString(off.o);
|
||||
} else {
|
||||
ECHECK(ParseAnyValue(val, field, fieldn, &struct_def));
|
||||
ECHECK(parser->ParseAnyValue(val, field, fieldn, struct_def_inner));
|
||||
}
|
||||
// Hardcoded insertion-sort with error-check.
|
||||
// If fields are specified in order, then this loop exits immediately.
|
||||
auto elem = field_stack_.rbegin();
|
||||
for (; elem != field_stack_.rbegin() + fieldn; ++elem) {
|
||||
auto elem = parser->field_stack_.rbegin();
|
||||
for (; elem != parser->field_stack_.rbegin() + fieldn; ++elem) {
|
||||
auto existing_field = elem->second;
|
||||
if (existing_field == field)
|
||||
return Error("field set more than once: " + field->name);
|
||||
return parser->Error("field set more than once: " + field->name);
|
||||
if (existing_field->value.offset < field->value.offset) break;
|
||||
}
|
||||
// Note: elem points to before the insertion point, thus .base() points
|
||||
// to the correct spot.
|
||||
field_stack_.insert(elem.base(), std::make_pair(val, field));
|
||||
parser->field_stack_.insert(elem.base(),
|
||||
std::make_pair(val, field));
|
||||
fieldn++;
|
||||
}
|
||||
}
|
||||
return NoError();
|
||||
});
|
||||
}, this);
|
||||
ECHECK(err);
|
||||
|
||||
// Check if all required fields are parsed.
|
||||
@@ -968,7 +973,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
|
||||
continue;
|
||||
}
|
||||
bool found = false;
|
||||
for (auto pf_it = field_stack_.end() - fieldn;
|
||||
for (auto pf_it = field_stack_.end() - fieldn_outer;
|
||||
pf_it != field_stack_.end();
|
||||
++pf_it) {
|
||||
auto parsed_field = pf_it->second;
|
||||
@@ -982,7 +987,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
|
||||
}
|
||||
}
|
||||
|
||||
if (struct_def.fixed && fieldn != struct_def.fields.vec.size())
|
||||
if (struct_def.fixed && fieldn_outer != struct_def.fields.vec.size())
|
||||
return Error("struct: wrong number of initializers: " + struct_def.name);
|
||||
|
||||
auto start = struct_def.fixed
|
||||
@@ -993,8 +998,9 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
|
||||
size;
|
||||
size /= 2) {
|
||||
// Go through elements in reverse, since we're building the data backwards.
|
||||
for (auto it = field_stack_.rbegin();
|
||||
it != field_stack_.rbegin() + fieldn; ++it) {
|
||||
for (auto it = field_stack_.rbegin(); it != field_stack_.rbegin() +
|
||||
fieldn_outer;
|
||||
++it) {
|
||||
auto &field_value = it->first;
|
||||
auto field = it->second;
|
||||
if (!struct_def.sortbysize ||
|
||||
@@ -1035,7 +1041,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < fieldn; i++) field_stack_.pop_back();
|
||||
for (size_t i = 0; i < fieldn_outer; i++) field_stack_.pop_back();
|
||||
|
||||
if (struct_def.fixed) {
|
||||
builder_.ClearOffsets();
|
||||
@@ -1058,11 +1064,12 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
|
||||
}
|
||||
|
||||
CheckedError Parser::ParseVectorDelimiters(size_t &count,
|
||||
const std::function<CheckedError()> &body) {
|
||||
ParseVectorDelimitersBody body,
|
||||
void *state) {
|
||||
EXPECT('[');
|
||||
for (;;) {
|
||||
if ((!opts.strict_json || !count) && Is(']')) break;
|
||||
ECHECK(body());
|
||||
ECHECK(body(count, state));
|
||||
count++;
|
||||
if (Is(']')) break;
|
||||
ECHECK(ParseComma());
|
||||
@@ -1073,13 +1080,18 @@ CheckedError Parser::ParseVectorDelimiters(size_t &count,
|
||||
|
||||
CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue) {
|
||||
size_t count = 0;
|
||||
auto err = ParseVectorDelimiters(count, [&]() -> CheckedError {
|
||||
std::pair<Parser *, const Type &> parser_and_type_state(this, type);
|
||||
auto err = ParseVectorDelimiters(count,
|
||||
[](size_t &, void *state) -> CheckedError {
|
||||
auto *parser_and_type =
|
||||
static_cast<std::pair<Parser *, const Type &> *>(state);
|
||||
auto *parser = parser_and_type->first;
|
||||
Value val;
|
||||
val.type = type;
|
||||
ECHECK(ParseAnyValue(val, nullptr, 0, nullptr));
|
||||
field_stack_.push_back(std::make_pair(val, nullptr));
|
||||
val.type = parser_and_type->second;
|
||||
ECHECK(parser->ParseAnyValue(val, nullptr, 0, nullptr));
|
||||
parser->field_stack_.push_back(std::make_pair(val, nullptr));
|
||||
return NoError();
|
||||
});
|
||||
}, &parser_and_type_state);
|
||||
ECHECK(err);
|
||||
|
||||
builder_.StartVector(count * InlineSize(type) / InlineAlignment(type),
|
||||
@@ -1927,17 +1939,25 @@ CheckedError Parser::ParseTypeFromProtoType(Type *type) {
|
||||
CheckedError Parser::SkipAnyJsonValue() {
|
||||
switch (token_) {
|
||||
case '{': {
|
||||
size_t fieldn = 0;
|
||||
return ParseTableDelimiters(fieldn, nullptr,
|
||||
[&](const std::string &) -> CheckedError {
|
||||
ECHECK(SkipAnyJsonValue());
|
||||
fieldn++;
|
||||
return NoError();
|
||||
});
|
||||
size_t fieldn_outer = 0;
|
||||
return ParseTableDelimiters(fieldn_outer, nullptr,
|
||||
[](const std::string &,
|
||||
size_t &fieldn, const StructDef *,
|
||||
void *state) -> CheckedError {
|
||||
auto *parser = static_cast<Parser *>(state);
|
||||
ECHECK(parser->SkipAnyJsonValue());
|
||||
fieldn++;
|
||||
return NoError();
|
||||
},
|
||||
this);
|
||||
}
|
||||
case '[': {
|
||||
size_t count = 0;
|
||||
return ParseVectorDelimiters(count, [&]() { return SkipAnyJsonValue(); });
|
||||
return ParseVectorDelimiters(count, [](size_t &,
|
||||
void *state) -> CheckedError {
|
||||
return static_cast<Parser *>(state)->SkipAnyJsonValue();
|
||||
},
|
||||
this);
|
||||
}
|
||||
case kTokenStringConstant:
|
||||
EXPECT(kTokenStringConstant);
|
||||
@@ -1957,15 +1977,25 @@ CheckedError Parser::SkipAnyJsonValue() {
|
||||
CheckedError Parser::ParseFlexBufferValue(flexbuffers::Builder *builder) {
|
||||
switch (token_) {
|
||||
case '{': {
|
||||
std::pair<Parser *, flexbuffers::Builder *> parser_and_builder_state(
|
||||
this, builder);
|
||||
auto start = builder->StartMap();
|
||||
size_t fieldn = 0;
|
||||
auto err = ParseTableDelimiters(fieldn, nullptr,
|
||||
[&](const std::string &name) -> CheckedError {
|
||||
builder->Key(name);
|
||||
ECHECK(ParseFlexBufferValue(builder));
|
||||
fieldn++;
|
||||
return NoError();
|
||||
});
|
||||
size_t fieldn_outer = 0;
|
||||
auto err = ParseTableDelimiters(fieldn_outer, nullptr,
|
||||
[](const std::string &name,
|
||||
size_t &fieldn, const StructDef *,
|
||||
void *state) -> CheckedError {
|
||||
auto *parser_and_builder =
|
||||
static_cast<std::pair<Parser *, flexbuffers::Builder *> *>(
|
||||
state);
|
||||
auto *parser = parser_and_builder->first;
|
||||
auto *current_builder = parser_and_builder->second;
|
||||
current_builder->Key(name);
|
||||
ECHECK(parser->ParseFlexBufferValue(current_builder));
|
||||
fieldn++;
|
||||
return NoError();
|
||||
},
|
||||
&parser_and_builder_state);
|
||||
ECHECK(err);
|
||||
builder->EndMap(start);
|
||||
break;
|
||||
@@ -1973,9 +2003,17 @@ CheckedError Parser::ParseFlexBufferValue(flexbuffers::Builder *builder) {
|
||||
case '[':{
|
||||
auto start = builder->StartVector();
|
||||
size_t count = 0;
|
||||
ECHECK(ParseVectorDelimiters(count, [&]() {
|
||||
return ParseFlexBufferValue(builder);
|
||||
}));
|
||||
std::pair<Parser *, flexbuffers::Builder *> parser_and_builder_state(
|
||||
this, builder);
|
||||
ECHECK(ParseVectorDelimiters(count, [](size_t &,
|
||||
void *state) -> CheckedError {
|
||||
auto *parser_and_builder =
|
||||
static_cast<std::pair<Parser *, flexbuffers::Builder *> *>(
|
||||
state);
|
||||
return parser_and_builder->first->ParseFlexBufferValue(
|
||||
parser_and_builder->second);
|
||||
},
|
||||
&parser_and_builder_state));
|
||||
builder->EndVector(start, false, false);
|
||||
break;
|
||||
}
|
||||
@@ -2082,7 +2120,7 @@ CheckedError Parser::DoParse(const char *source,
|
||||
ECHECK(ParseProtoDecl());
|
||||
} else if (Is(kTokenNativeInclude)) {
|
||||
NEXT();
|
||||
native_included_files_.emplace_back(attribute_);
|
||||
vector_emplace_back(&native_included_files_, attribute_);
|
||||
EXPECT(kTokenStringConstant);
|
||||
} else if (Is(kTokenInclude) ||
|
||||
(opts.proto_mode &&
|
||||
@@ -2201,7 +2239,10 @@ std::set<std::string> Parser::GetIncludedFilesRecursive(
|
||||
to_process.pop_front();
|
||||
included_files.insert(current);
|
||||
|
||||
auto new_files = files_included_per_file_.at(current);
|
||||
// Workaround the lack of const accessor in C++98 maps.
|
||||
auto &new_files =
|
||||
(*const_cast<std::map<std::string, std::set<std::string>> *>(
|
||||
&files_included_per_file_))[current];
|
||||
for (auto it = new_files.begin(); it != new_files.end(); ++it) {
|
||||
if (included_files.find(*it) == included_files.end())
|
||||
to_process.push_back(*it);
|
||||
|
||||
Reference in New Issue
Block a user