Skip to content

Commit

Permalink
Add support for boolean types in flexbuffers
Browse files Browse the repository at this point in the history
  • Loading branch information
rouzier committed Jul 20, 2017
1 parent 2e2063c commit e828a24
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 9 deletions.
52 changes: 50 additions & 2 deletions include/flatbuffers/flexbuffers.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@ enum Type {
TYPE_VECTOR_UINT4 = 23,
TYPE_VECTOR_FLOAT4 = 24,
TYPE_BLOB = 25,
TYPE_BOOL = 26,
};

inline bool IsInline(Type t) { return t <= TYPE_FLOAT; }
inline bool IsInline(Type t) { return t <= TYPE_FLOAT || t == TYPE_BOOL; }

inline bool IsTypedVectorElementType(Type t) {
return t >= TYPE_INT && t <= TYPE_STRING;
Expand Down Expand Up @@ -346,6 +347,7 @@ class Reference {
Type GetType() const { return type_; }

bool IsNull() const { return type_ == TYPE_NULL; }
bool IsBool() const { return type_ == TYPE_BOOL; }
bool IsInt() const { return type_ == TYPE_INT ||
type_ == TYPE_INDIRECT_INT; }
bool IsUInt() const { return type_ == TYPE_UINT||
Expand Down Expand Up @@ -377,12 +379,44 @@ class Reference {
case TYPE_NULL: return 0;
case TYPE_STRING: return flatbuffers::StringToInt(AsString().c_str());
case TYPE_VECTOR: return static_cast<int64_t>(AsVector().size());
case TYPE_BOOL: return static_cast<int64_t>(AsBool());
default:
// Convert other things to int.
return 0;
}
}

bool AsBool() const {
if (type_ == TYPE_BOOL) {
return ReadUInt64(data_, parent_width_) != 0;
}
switch (type_) {
case TYPE_INT: return ReadInt64(data_, parent_width_) != 0;
case TYPE_INDIRECT_INT: returnReadInt64(Indirect(), byte_width_) != 0;
case TYPE_UINT: return ReadUInt64(data_, parent_width_) != 0;
case TYPE_INDIRECT_UINT: return ReadUInt64(Indirect(), byte_width_) != 0;
case TYPE_FLOAT: return ReadDouble(data_, parent_width_) != 0;
case TYPE_INDIRECT_FLOAT: return ReadDouble(Indirect(), byte_width_) != 0.0;
case TYPE_STRING:
{
const char * tmp = AsString().c_str();
if (strcmp(tmp, "true") == 0) {
return true;
}
if (strcmp(tmp, "false") == 0) {
return false;
}
return flatbuffers::StringToInt(tmp) != 0;
}
case TYPE_VECTOR:
case TYPE_MAP:
return AsVector().size() != 0;
default:
// Everything else is false
return false;
}
}

// TODO: could specialize these to not use AsInt64() if that saves
// extension ops in generated code, and use a faster op than ReadInt64.
int32_t AsInt32() const { return static_cast<int32_t>(AsInt64()); }
Expand All @@ -404,6 +438,7 @@ class Reference {
case TYPE_NULL: return 0;
case TYPE_STRING: return flatbuffers::StringToUInt(AsString().c_str());
case TYPE_VECTOR: return static_cast<uint64_t>(AsVector().size());
case TYPE_BOOL: return static_cast<uint64_t>(AsBool());
default:
// Convert other things to uint.
return 0;
Expand Down Expand Up @@ -431,6 +466,7 @@ class Reference {
case TYPE_NULL: return 0.0;
case TYPE_STRING: return strtod(AsString().c_str(), nullptr);
case TYPE_VECTOR: return static_cast<double>(AsVector().size());
case TYPE_BOOL: return static_cast<double>(AsBool());
default:
// Convert strings and other things to float.
return 0;
Expand Down Expand Up @@ -490,6 +526,8 @@ class Reference {
s += flatbuffers::NumToString(AsDouble());
} else if (IsNull()) {
s += "null";
} else if (IsBool()) {
s += AsBool() ? "true" : "false";
} else if (IsMap()) {
s += "{ ";
auto m = AsMap();
Expand Down Expand Up @@ -584,6 +622,13 @@ class Reference {
}
}

bool MutateBool(bool b) {
if (type_ == TYPE_BOOL) {
return Mutate(data_, b, parent_width_, BIT_WIDTH_8);
}
return false;
}

bool MutateUInt(uint64_t u) {
if (type_ == TYPE_UINT) {
return Mutate(data_, u, parent_width_, WidthU(u));
Expand Down Expand Up @@ -812,7 +857,7 @@ class Builder FLATBUFFERS_FINAL_CLASS {
void Double(double f) { stack_.push_back(Value(f)); }
void Double(const char *key, double d) { Key(key); Double(d); }

void Bool(bool b) { Int(static_cast<int64_t>(b)); }
void Bool(bool b) { stack_.push_back(Value(b)); }
void Bool(const char *key, bool b) { Key(key); Bool(b); }

void IndirectInt(int64_t i) {
Expand Down Expand Up @@ -1194,6 +1239,8 @@ class Builder FLATBUFFERS_FINAL_CLASS {

Value() : i_(0), type_(TYPE_NULL), min_bit_width_(BIT_WIDTH_8) {}

Value(bool b) : u_(b ? 1 : 0), type_(TYPE_BOOL), min_bit_width_(BIT_WIDTH_8) {}

Value(int64_t i, Type t, BitWidth bw)
: i_(i), type_(t), min_bit_width_(bw) {}
Value(uint64_t u, Type t, BitWidth bw)
Expand Down Expand Up @@ -1252,6 +1299,7 @@ class Builder FLATBUFFERS_FINAL_CLASS {
case TYPE_INT:
Write(val.i_, byte_width);
break;
case TYPE_BOOL:
case TYPE_UINT:
Write(val.u_, byte_width);
break;
Expand Down
3 changes: 2 additions & 1 deletion include/flatbuffers/idl.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,12 +420,13 @@ struct IDLOptions {

// This encapsulates where the parser is in the current source file.
struct ParserState {
ParserState() : cursor_(nullptr), line_(1), token_(-1) {}
ParserState() : cursor_(nullptr), line_(1), token_(-1), is_bool_(false) {}

protected:
const char *cursor_;
int line_; // the current line being parsed
int token_;
bool is_bool_;

std::string attribute_;
std::vector<std::string> doc_comment_;
Expand Down
8 changes: 7 additions & 1 deletion src/idl_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ CheckedError Parser::Next() {
doc_comment_.clear();
bool seen_newline = false;
attribute_.clear();
is_bool_ = false;
for (;;) {
char c = *cursor_++;
token_ = c;
Expand Down Expand Up @@ -391,6 +392,7 @@ CheckedError Parser::Next() {
if (attribute_ == "true" || attribute_ == "false") {
attribute_ = NumToString(attribute_ == "true");
token_ = kTokenIntegerConstant;
is_bool_ = true;
return NoError();
}
// Check for declaration keywords:
Expand Down Expand Up @@ -1984,7 +1986,11 @@ CheckedError Parser::ParseFlexBufferValue(flexbuffers::Builder *builder) {
EXPECT(kTokenStringConstant);
break;
case kTokenIntegerConstant:
builder->Int(StringToInt(attribute_.c_str()));
if (is_bool_) {
builder->Bool(StringToInt(attribute_.c_str()) ? true : false);
} else {
builder->Int(StringToInt(attribute_.c_str()));
}
EXPECT(kTokenIntegerConstant);
break;
case kTokenFloatConstant:
Expand Down
31 changes: 26 additions & 5 deletions tests/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1565,19 +1565,25 @@ void FlexBuffersTest() {
flexbuffers::BUILDER_FLAG_SHARE_KEYS_AND_STRINGS);

// Write the equivalent of:
// { vec: [ -100, "Fred", 4.0 ], bar: [ 1, 2, 3 ], foo: 100 }
// { vec: [ -100, "Fred", 4.0, false ], bar: [ 1, 2, 3 ], bar3: [ 1, 2, 3 ], foo: 100, bool: true, mymap: { foo: "Fred", sbool1 : "true", sbool2: "false", sbool3: "0", sbool3: "1" } }
slb.Map([&]() {
slb.Vector("vec", [&]() {
slb += -100; // Equivalent to slb.Add(-100) or slb.Int(-100);
slb += "Fred";
slb.IndirectFloat(4.0f);
slb += false;
});
int ints[] = { 1, 2, 3 };
slb.Vector("bar", ints, 3);
slb.FixedTypedVector("bar3", ints, 3);
slb.Bool("bool", true);
slb.Double("foo", 100);
slb.Map("mymap", [&]() {
slb.String("foo", "Fred"); // Testing key and string reuse.
slb.String("sbool1", "true");
slb.String("sbool2", "false");
slb.String("sbool3", "1");
slb.String("sbool4", "0");
});
});
slb.Finish();
Expand All @@ -1587,27 +1593,36 @@ void FlexBuffersTest() {
printf("\n");

auto map = flexbuffers::GetRoot(slb.GetBuffer()).AsMap();
TEST_EQ(map.size(), 5);
TEST_EQ(map.size(), 6);
auto vec = map["vec"].AsVector();
TEST_EQ(vec.size(), 3);
TEST_EQ(vec.size(), 4);
TEST_EQ(vec[0].AsInt64(), -100);
TEST_EQ_STR(vec[1].AsString().c_str(), "Fred");
TEST_EQ(vec[1].AsInt64(), 0); // Number parsing failed.
TEST_EQ(vec[2].AsDouble(), 4.0);
TEST_EQ(vec[2].AsString().IsTheEmptyString(), true); // Wrong Type.
TEST_EQ_STR(vec[2].AsString().c_str(), ""); // This still works though.
TEST_EQ_STR(vec[2].ToString().c_str(), "4.0"); // Or have it converted.
TEST_EQ(vec[3].IsBool(), true); // This is a boolean
TEST_EQ(vec[3].AsBool(), false); // This is false
auto tvec = map["bar"].AsTypedVector();
TEST_EQ(tvec.size(), 3);
TEST_EQ(tvec[2].AsInt8(), 3);
auto tvec3 = map["bar3"].AsFixedTypedVector();
TEST_EQ(tvec3.size(), 3);
TEST_EQ(tvec3[2].AsInt8(), 3);
auto tboolRef = map["bool"];
TEST_EQ(tboolRef.AsBool(), true);
TEST_EQ(tboolRef.IsBool(), true);
TEST_EQ(map["foo"].AsUInt8(), 100);
TEST_EQ(map["unknown"].IsNull(), true);
auto mymap = map["mymap"].AsMap();
// These should be equal by pointer equality, since key and value are shared.
TEST_EQ(mymap.Keys()[0].AsKey(), map.Keys()[2].AsKey());
TEST_EQ(mymap["sbool1"].AsBool(), true);
TEST_EQ(mymap["sbool2"].AsBool(), false);
TEST_EQ(mymap["sbool3"].AsBool(), true);
TEST_EQ(mymap["sbool4"].AsBool(), false);
TEST_EQ(mymap.Keys()[0].AsKey(), map.Keys()[3].AsKey());
TEST_EQ(mymap.Values()[0].AsString().c_str(), vec[1].AsString().c_str());
// We can mutate values in the buffer.
TEST_EQ(vec[0].MutateInt(-99), true);
Expand All @@ -1618,11 +1633,13 @@ void FlexBuffersTest() {
TEST_EQ(vec[2].MutateFloat(2.0f), true);
TEST_EQ(vec[2].AsFloat(), 2.0f);
TEST_EQ(vec[2].MutateFloat(3.14159), false); // Double does not fit in float.
TEST_EQ(vec[3].MutateBool(true), true);
TEST_EQ(vec[3].AsBool(), true); // This is true

// Parse from JSON:
flatbuffers::Parser parser;
slb.Clear();
auto jsontest = "{ a: [ 123, 456.0 ], b: \"hello\" }";
auto jsontest = "{ a: [ 123, 456.0 ], b: \"hello\", c: true, d: false }";
TEST_EQ(parser.ParseFlexBuffer(jsontest, nullptr, &slb),
true);
auto jroot = flexbuffers::GetRoot(slb.GetBuffer());
Expand All @@ -1631,6 +1648,10 @@ void FlexBuffersTest() {
TEST_EQ(jvec[0].AsInt64(), 123);
TEST_EQ(jvec[1].AsDouble(), 456.0);
TEST_EQ_STR(jmap["b"].AsString().c_str(), "hello");
TEST_EQ(jmap["c"].IsBool(), true);
TEST_EQ(jmap["c"].AsBool(), true);
TEST_EQ(jmap["d"].IsBool(), true);
TEST_EQ(jmap["d"].AsBool(), false);
// And from FlexBuffer back to JSON:
auto jsonback = jroot.ToString();
TEST_EQ_STR(jsontest, jsonback.c_str());
Expand Down

0 comments on commit e828a24

Please sign in to comment.