value_map_[key] = value;
}
+ std::string GetValue(const std::string &key) const {
+ const auto it = value_map_.find(key);
+ return it == value_map_.end() ? "" : it->second;
+ }
+
// Appends the given text to the generated code as well as a newline
// character. Any text within {{ and }} delimeters is replaced by values
// previously stored in the CodeWriter by calling SetValue above. The newline
inline bool IsBool (BaseType t) { return t == BASE_TYPE_BOOL; }
inline bool IsOneByte(BaseType t) { return t >= BASE_TYPE_UTYPE &&
t <= BASE_TYPE_UCHAR; }
+
+inline bool IsUnsigned(BaseType t) {
+ return (t == BASE_TYPE_UTYPE) || (t == BASE_TYPE_UCHAR) ||
+ (t == BASE_TYPE_USHORT) || (t == BASE_TYPE_UINT) ||
+ (t == BASE_TYPE_ULONG);
+}
+
// clang-format on
extern const char *const kTypeNames[];
return IsStruct(type) ? type.struct_def->minalign : SizeOf(type.base_type);
}
-struct EnumVal {
- EnumVal(const std::string &_name, int64_t _val) : name(_name), value(_val) {}
- EnumVal() : value(0) {}
+struct EnumDef;
+struct EnumValBuilder;
+struct EnumVal {
Offset<reflection::EnumVal> Serialize(FlatBufferBuilder *builder, const Parser &parser) const;
bool Deserialize(const Parser &parser, const reflection::EnumVal *val);
+
+ uint64_t GetAsUInt64() const { return static_cast<uint64_t>(value); }
+ int64_t GetAsInt64() const { return value; }
bool IsZero() const { return 0 == value; }
bool IsNonZero() const { return !IsZero(); }
std::string name;
std::vector<std::string> doc_comment;
- int64_t value;
Type union_type;
+
+ private:
+ friend EnumDef;
+ friend EnumValBuilder;
+ friend bool operator==(const EnumVal &lhs, const EnumVal &rhs);
+
+ EnumVal(const std::string &_name, int64_t _val) : name(_name), value(_val) {}
+ EnumVal() : value(0) {}
+
+ int64_t value;
};
struct EnumDef : public Definition {
EnumDef() : is_union(false), uses_multiple_type_instances(false) {}
- EnumVal *ReverseLookup(int64_t enum_idx, bool skip_union_default = true) {
- for (auto it = Vals().begin() +
- static_cast<int>(is_union && skip_union_default);
- it != Vals().end(); ++it) {
- if ((*it)->value == enum_idx) { return *it; }
- }
- return nullptr;
- }
-
- Offset<reflection::Enum> Serialize(FlatBufferBuilder *builder, const Parser &parser) const;
+ Offset<reflection::Enum> Serialize(FlatBufferBuilder *builder,
+ const Parser &parser) const;
bool Deserialize(Parser &parser, const reflection::Enum *values);
+ template<typename T> void ChangeEnumValue(EnumVal *ev, T new_val);
+ void SortByValue();
+ void RemoveDuplicates();
+
+ std::string AllFlags() const;
+ const EnumVal *MinValue() const;
+ const EnumVal *MaxValue() const;
+ // Returns the number of integer steps from v1 to v2.
+ uint64_t Distance(const EnumVal *v1, const EnumVal *v2) const;
+ // Returns the number of integer steps from Min to Max.
+ uint64_t Distance() const { return Distance(MinValue(), MaxValue()); }
+
+ EnumVal *ReverseLookup(int64_t enum_idx,
+ bool skip_union_default = false) const;
+ EnumVal *FindByValue(const std::string &constant) const;
+
+ std::string ToString(const EnumVal &ev) const {
+ return IsUInt64() ? NumToString(ev.GetAsUInt64())
+ : NumToString(ev.GetAsInt64());
+ }
+
size_t size() const { return vals.vec.size(); }
const std::vector<EnumVal *> &Vals() const {
+ FLATBUFFERS_ASSERT(false == vals.vec.empty());
return vals.vec;
}
- SymbolTable<EnumVal> vals;
+ const EnumVal *Lookup(const std::string &enum_name) const {
+ return vals.Lookup(enum_name);
+ }
+
bool is_union;
// Type is a union which uses type aliases where at least one type is
// available under two different names.
bool uses_multiple_type_instances;
Type underlying_type;
+
+ private:
+ bool IsUInt64() const {
+ return (BASE_TYPE_ULONG == underlying_type.base_type);
+ }
+
+ friend EnumValBuilder;
+ SymbolTable<EnumVal> vals;
};
+inline bool operator==(const EnumVal &lhs, const EnumVal &rhs) {
+ return lhs.value == rhs.value;
+}
+inline bool operator!=(const EnumVal &lhs, const EnumVal &rhs) {
+ return !(lhs == rhs);
+}
+
inline bool EqualByName(const Type &a, const Type &b) {
return a.base_type == b.base_type && a.element == b.element &&
(a.struct_def == b.struct_def ||
// Pedantic warning free version of toupper().
inline char ToUpper(char c) { return static_cast<char>(::toupper(c)); }
+// Make numerical literal with type-suffix.
+// This function is only needed for C++! Other languages do not need it.
+static inline std::string NumToStringCpp(std::string val, BaseType type) {
+ // Avoid issues with -2147483648, -9223372036854775808.
+ switch (type) {
+ case BASE_TYPE_INT:
+ return (val != "-2147483648") ? val : ("(-2147483647 - 1)");
+ case BASE_TYPE_ULONG: return (val == "0") ? val : (val + "ULL");
+ case BASE_TYPE_LONG:
+ if (val == "-9223372036854775808")
+ return "(-9223372036854775807LL - 1LL)";
+ else
+ return (val == "0") ? val : (val + "LL");
+ default: return val;
+ }
+}
+
static std::string GeneratedFileName(const std::string &path,
const std::string &file_name) {
return path + file_name + "_generated.h";
code_.SetValue("NUM_FIELDS", NumToString(num_fields));
std::vector<std::string> names;
std::vector<Type> types;
- bool consecutive_enum_from_zero = true;
+
if (struct_def) {
for (auto it = struct_def->fields.vec.begin();
it != struct_def->fields.vec.end(); ++it) {
names.push_back(Name(ev));
types.push_back(enum_def->is_union ? ev.union_type
: Type(enum_def->underlying_type));
- if (static_cast<int64_t>(it - enum_def->Vals().begin()) != ev.value) {
- consecutive_enum_from_zero = false;
- }
}
}
std::string ts;
ns += "\"" + *it + "\"";
}
std::string vs;
+ const auto consecutive_enum_from_zero =
+ enum_def && enum_def->MinValue()->IsZero() &&
+ ((enum_def->size() - 1) == enum_def->Distance());
if (enum_def && !consecutive_enum_from_zero) {
for (auto it = enum_def->Vals().begin(); it != enum_def->Vals().end();
++it) {
const auto &ev = **it;
if (!vs.empty()) vs += ", ";
- vs += NumToString(ev.value);
+ vs += NumToStringCpp(enum_def->ToString(ev),
+ enum_def->underlying_type.base_type);
}
} else if (struct_def && struct_def->fixed) {
for (auto it = struct_def->fields.vec.begin();
code_ += " };";
}
if (!vs.empty()) {
+ // Problem with uint64_t values greater than 9223372036854775807ULL.
code_ += " static const int64_t values[] = { {{VALUES}} };";
}
auto has_names =
// Generate an enum declaration,
// an enum string lookup table,
// and an enum array of values
+
void GenEnum(const EnumDef &enum_def) {
code_.SetValue("ENUM_NAME", Name(enum_def));
code_.SetValue("BASE_TYPE", GenTypeBasic(enum_def.underlying_type, false));
GenComment(enum_def.doc_comment);
code_ += GenEnumDecl(enum_def) + "\\";
- if (parser_.opts.scoped_enums) code_ += " : {{BASE_TYPE}}\\";
+ // MSVC doesn't support int64/uint64 enum without explicitly declared enum
+ // type. The value 4611686018427387904ULL is truncated to zero with warning:
+ // "warning C4309: 'initializing': truncation of constant value".
+ auto add_type = parser_.opts.scoped_enums;
+ add_type |= (enum_def.underlying_type.base_type == BASE_TYPE_LONG);
+ add_type |= (enum_def.underlying_type.base_type == BASE_TYPE_ULONG);
+ if (add_type) code_ += " : {{BASE_TYPE}}\\";
code_ += " {";
- int64_t anyv = 0;
- const EnumVal *minv = nullptr, *maxv = nullptr;
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
const auto &ev = **it;
-
- GenComment(ev.doc_comment, " ");
+ if (!ev.doc_comment.empty()) {
+ auto prefix = code_.GetValue("SEP") + " ";
+ GenComment(ev.doc_comment, prefix.c_str());
+ code_.SetValue("SEP", "");
+ }
code_.SetValue("KEY", GenEnumValDecl(enum_def, Name(ev)));
- code_.SetValue("VALUE", NumToString(ev.value));
+ code_.SetValue("VALUE",
+ NumToStringCpp(enum_def.ToString(ev),
+ enum_def.underlying_type.base_type));
code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
code_.SetValue("SEP", ",\n");
-
- minv = !minv || minv->value > ev.value ? &ev : minv;
- maxv = !maxv || maxv->value < ev.value ? &ev : maxv;
- anyv |= ev.value;
}
+ const EnumVal *minv = enum_def.MinValue();
+ const EnumVal *maxv = enum_def.MaxValue();
if (parser_.opts.scoped_enums || parser_.opts.prefixed_enums) {
FLATBUFFERS_ASSERT(minv && maxv);
code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
code_.SetValue("KEY", GenEnumValDecl(enum_def, "ANY"));
- code_.SetValue("VALUE", NumToString(anyv));
+ code_.SetValue("VALUE",
+ NumToStringCpp(enum_def.AllFlags(),
+ enum_def.underlying_type.base_type));
code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
} else { // MIN & MAX are useless for bit_flags
code_.SetValue("KEY", GenEnumValDecl(enum_def, "MIN"));
// Problem is, if values are very sparse that could generate really big
// tables. Ideally in that case we generate a map lookup instead, but for
// the moment we simply don't output a table at all.
- auto range =
- enum_def.vals.vec.back()->value - enum_def.vals.vec.front()->value + 1;
+ auto range = enum_def.Distance();
// Average distance between values above which we consider a table
// "too sparse". Change at will.
- static const int kMaxSparseness = 5;
- if (range / static_cast<int64_t>(enum_def.vals.vec.size()) <
- kMaxSparseness) {
+ static const uint64_t kMaxSparseness = 5;
+ if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
code_ += "inline const char * const *EnumNames{{ENUM_NAME}}() {";
code_ += " static const char * const names[] = {";
- auto val = enum_def.Vals().front()->value;
+ auto val = enum_def.Vals().front();
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
++it) {
- const auto &ev = **it;
- while (val++ != ev.value) { code_ += " \"\","; }
- code_ += " \"" + Name(ev) + "\",";
+ auto ev = *it;
+ for (auto k = enum_def.Distance(val, ev); k > 1; --k) {
+ code_ += " \"\",";
+ }
+ val = ev;
+ code_ += " \"" + Name(*ev) + "\",";
}
code_ += " nullptr";
code_ += " };";
code_ += "inline const char *EnumName{{ENUM_NAME}}({{ENUM_NAME}} e) {";
- code_ += " if (e < " +
- GetEnumValUse(enum_def, *enum_def.vals.vec.front()) +
- " || e > " + GetEnumValUse(enum_def, *enum_def.vals.vec.back()) +
+ code_ += " if (e < " + GetEnumValUse(enum_def, *enum_def.MinValue()) +
+ " || e > " + GetEnumValUse(enum_def, *enum_def.MaxValue()) +
") return \"\";";
code_ += " const size_t index = static_cast<size_t>(e)\\";
- if (enum_def.vals.vec.front()->value) {
- auto vals = GetEnumValUse(enum_def, *enum_def.vals.vec.front());
+ if (enum_def.MinValue()->IsNonZero()) {
+ auto vals = GetEnumValUse(enum_def, *enum_def.MinValue());
code_ += " - static_cast<size_t>(" + vals + ")\\";
}
code_ += ";";
if (parser_.opts.generate_object_based_api && enum_def.is_union) {
// Generate a union type
code_.SetValue("NAME", Name(enum_def));
- code_.SetValue("NONE",
- GetEnumValUse(enum_def, *enum_def.vals.Lookup("NONE")));
+ FLATBUFFERS_ASSERT(enum_def.Lookup("NONE"));
+ code_.SetValue("NONE", GetEnumValUse(enum_def, *enum_def.Lookup("NONE")));
code_ += "struct {{NAME}}Union {";
code_ += " {{NAME}} type;";
code_ += "";
// Union Reset() function.
- code_.SetValue("NONE",
- GetEnumValUse(enum_def, *enum_def.vals.Lookup("NONE")));
+ FLATBUFFERS_ASSERT(enum_def.Lookup("NONE"));
+ code_.SetValue("NONE", GetEnumValUse(enum_def, *enum_def.Lookup("NONE")));
code_ += "inline void {{ENUM_NAME}}Union::Reset() {";
code_ += " switch (type) {";
if (IsFloat(field.value.type.base_type))
return float_const_gen_.GenFloatConstant(field);
else
- return field.value.constant;
+ return NumToStringCpp(field.value.constant, field.value.type.base_type);
}
std::string GetDefaultScalarValue(const FieldDef &field, bool is_ctor) {
if (field.value.type.enum_def && IsScalar(field.value.type.base_type)) {
- auto ev = field.value.type.enum_def->ReverseLookup(
- StringToInt(field.value.constant.c_str()), false);
+ auto ev = field.value.type.enum_def->FindByValue(field.value.constant);
if (ev) {
return WrapInNameSpace(field.value.type.enum_def->defined_namespace,
GetEnumValUse(*field.value.type.enum_def, *ev));
} else {
- return GenUnderlyingCast(field, true, field.value.constant);
+ return GenUnderlyingCast(
+ field, true,
+ NumToStringCpp(field.value.constant, field.value.type.base_type));
}
} else if (field.value.type.base_type == BASE_TYPE_BOOL) {
return field.value.constant == "0" ? "false" : "true";
// holes.
if (!is_bit_flags) {
code += " static const int minValue = " +
- NumToString(enum_def.vals.vec.front()->value) + ";\n";
+ enum_def.ToString(*enum_def.MinValue()) + ";\n";
code += " static const int maxValue = " +
- NumToString(enum_def.vals.vec.back()->value) + ";\n";
+ enum_def.ToString(*enum_def.MaxValue()) + ";\n";
}
code +=
GenDocComment(ev.doc_comment, &code, "", " ");
}
code += " static const " + name + " " + ev.name + " = ";
- code += "const " + name + "._(" + NumToString(ev.value) + ");\n";
+ code += "const " + name + "._(" + enum_def.ToString(ev) + ");\n";
}
code += " static get values => {";
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
auto &ev = **it;
- code += NumToString(ev.value) + ": " + ev.name + ",";
+ code += enum_def.ToString(ev) + ": " + ev.name + ",";
}
code += "};\n\n";
auto &ev = **en_it;
auto enum_name = NamespaceAliasFromUnionType(ev.name);
- code += " case " + NumToString(ev.value) + ": return " +
+ code += " case " + enum_def.ToString(ev) + ": return " +
enum_name + ".reader.vTableGet(_bc, _bcOffset, " +
NumToString(field.value.offset) + ", null);\n";
}
if (enum_def.is_union)
schema += " " + GenType(ev.union_type) + ",\n";
else
- schema += " " + ev.name + " = " + NumToString(ev.value) + ",\n";
+ schema += " " + ev.name + " = " + enum_def.ToString(ev) + ",\n";
}
schema += "}\n\n";
}
}
std::string GenEnumDefaultValue(const FieldDef &field) const {
- auto& value = field.value;
- auto enum_def = value.type.enum_def;
- auto vec = enum_def->vals.vec;
- auto default_value = StringToInt(value.constant.c_str());
-
- auto result = value.constant;
- for (auto it = vec.begin(); it != vec.end(); ++it) {
- auto enum_val = **it;
- if (enum_val.value == default_value) {
- result = WrapInNameSpace(*enum_def) + "." + enum_val.name;
- break;
- }
- }
-
- return result;
+ auto &value = field.value;
+ FLATBUFFERS_ASSERT(value.type.enum_def);
+ auto &enum_def = *value.type.enum_def;
+ auto enum_val = enum_def.FindByValue(value.constant);
+ return enum_val ? (WrapInNameSpace(enum_def) + "." + enum_val->name)
+ : value.constant;
}
std::string GenDefaultValue(const FieldDef &field, bool enableLangOverrides) const {
code += GenTypeBasic(enum_def.underlying_type, false);
}
code += " " + ev.name + " = ";
- code += NumToString(ev.value);
+ code += enum_def.ToString(ev);
code += lang_.enum_separator;
}
// Problem is, if values are very sparse that could generate really big
// tables. Ideally in that case we generate a map lookup instead, but for
// the moment we simply don't output a table at all.
- auto range = enum_def.vals.vec.back()->value -
- enum_def.vals.vec.front()->value + 1;
+ auto range = enum_def.Distance();
// Average distance between values above which we consider a table
// "too sparse". Change at will.
- static const int kMaxSparseness = 5;
- if (range / static_cast<int64_t>(enum_def.vals.vec.size()) <
- kMaxSparseness) {
+ static const uint64_t kMaxSparseness = 5;
+ if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
code += "\n public static";
code += lang_.const_decl;
code += lang_.string_type;
code += "[] names = { ";
- auto val = enum_def.Vals().front()->value;
+ auto val = enum_def.Vals().front();
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
++it) {
- while (val++ != (*it)->value) code += "\"\", ";
+ auto ev = *it;
+ for (auto k = enum_def.Distance(val, ev); k > 1; --k)
+ code += "\"\", ";
+ val = ev;
code += "\"" + (*it)->name + "\", ";
}
code += "};\n\n";
code += lang_.string_type;
code += " " + MakeCamel("name", lang_.first_camel_upper);
code += "(int e) { return names[e";
- if (enum_def.vals.vec.front()->value)
- code += " - " + enum_def.vals.vec.front()->name;
+ if (enum_def.MinValue()->IsNonZero())
+ code += " - " + enum_def.MinValue()->name;
code += "]; }\n";
}
}
code += " ";
code += GetEnumTypeName(enum_def);
code += " = ";
- code += NumToString(ev.value) + "\n";
+ code += enum_def.ToString(ev) + "\n";
}
// End enum code.
// Generate mapping between EnumName: EnumValue(int)
if (reverse) {
- code += " " + NumToString(ev.value);
+ code += " " + enum_def.ToString(ev);
code += lang_.language == IDLOptions::kTs ? "= " : ": ";
code += "'" + ev.name + "'";
} else {
code += " " + ev.name;
code += lang_.language == IDLOptions::kTs ? "= " : ": ";
- code += NumToString(ev.value);
+ code += enum_def.ToString(ev);
}
code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
std::string GenDefaultValue(const Value &value, const std::string &context) {
if (value.type.enum_def) {
- if (auto val = value.type.enum_def->ReverseLookup(
- StringToInt(value.constant.c_str()), false)) {
+ if (auto val = value.type.enum_def->FindByValue(value.constant)) {
if (lang_.language == IDLOptions::kTs) {
return GenPrefixedTypeName(WrapInNameSpace(*value.type.enum_def),
value.type.enum_def->file) +
auto &ev = **it;
GenComment(ev.doc_comment, code_ptr, nullptr, " ");
code += " " + enum_def.name + "_" + NormalizedName(ev) + " = " +
- NumToString(ev.value);
+ enum_def.ToString(ev);
if (it + 1 != enum_def.Vals().end()) code += ",";
code += "\n";
}
// A single enum member.
void EnumMember(const EnumDef &enum_def, const EnumVal &ev, std::string *code_ptr) {
std::string &code = *code_ptr;
- code += std::string(Indent) + NormalizedName(ev) + " = " + NumToString(ev.value) + ",\n";
- (void)enum_def;
+ code += std::string(Indent) + NormalizedName(ev) + " = " +
+ enum_def.ToString(ev) + ",\n";
}
// End enum code.
code += Indent + "const ";
code += ev.name;
code += " = ";
- code += NumToString(ev.value) + ";\n";
- (void)enum_def;
+ code += enum_def.ToString(ev) + ";\n";
}
// End enum code.
std::string GenDefaultValue(const Value &value) {
if (value.type.enum_def) {
- if (auto val = value.type.enum_def->ReverseLookup(
- StringToInt(value.constant.c_str()), false)) {
+ if (auto val = value.type.enum_def->FindByValue(value.constant)) {
return WrapInNameSpace(*value.type.enum_def) + "::" + val->name;
}
}
code += Indent;
code += NormalizedName(ev);
code += " = ";
- code += NumToString(ev.value) + "\n";
- (void)enum_def;
+ code += enum_def.ToString(ev) + "\n";
}
// End enum code.
code_ += "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]";
code_ += "pub enum " + Name(enum_def) + " {";
- int64_t anyv = 0;
- const EnumVal *minv = nullptr, *maxv = nullptr;
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
const auto &ev = **it;
GenComment(ev.doc_comment, " ");
code_.SetValue("KEY", Name(ev));
- code_.SetValue("VALUE", NumToString(ev.value));
+ code_.SetValue("VALUE", enum_def.ToString(ev));
code_ += " {{KEY}} = {{VALUE}},";
-
- minv = !minv || minv->value > ev.value ? &ev : minv;
- maxv = !maxv || maxv->value < ev.value ? &ev : maxv;
- anyv |= ev.value;
}
+ const EnumVal *minv = enum_def.MinValue();
+ const EnumVal *maxv = enum_def.MaxValue();
+ FLATBUFFERS_ASSERT(minv && maxv);
code_ += "";
code_ += "}";
code_.SetValue("ENUM_NAME", Name(enum_def));
code_.SetValue("ENUM_NAME_SNAKE", MakeSnakeCase(Name(enum_def)));
code_.SetValue("ENUM_NAME_CAPS", MakeUpper(MakeSnakeCase(Name(enum_def))));
- code_.SetValue("ENUM_MIN_BASE_VALUE", NumToString(minv->value));
- code_.SetValue("ENUM_MAX_BASE_VALUE", NumToString(maxv->value));
+ code_.SetValue("ENUM_MIN_BASE_VALUE", enum_def.ToString(*minv));
+ code_.SetValue("ENUM_MAX_BASE_VALUE", enum_def.ToString(*maxv));
// Generate enum constants, and impls for Follow, EndianScalar, and Push.
code_ += "const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\";
// Problem is, if values are very sparse that could generate really big
// tables. Ideally in that case we generate a map lookup instead, but for
// the moment we simply don't output a table at all.
- auto range =
- enum_def.vals.vec.back()->value - enum_def.vals.vec.front()->value + 1;
+ auto range = enum_def.Distance();
// Average distance between values above which we consider a table
// "too sparse". Change at will.
- static const int kMaxSparseness = 5;
- if (range / static_cast<int64_t>(enum_def.vals.vec.size()) <
- kMaxSparseness) {
+ static const uint64_t kMaxSparseness = 5;
+ if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
code_ += "#[allow(non_camel_case_types)]";
code_ += "const ENUM_NAMES_{{ENUM_NAME_CAPS}}:[&'static str; " +
- NumToString(range) + "] = [";
+ NumToString(range + 1) + "] = [";
- auto val = enum_def.Vals().front()->value;
+ auto val = enum_def.Vals().front();
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
++it) {
- const auto &ev = **it;
- while (val++ != ev.value) { code_ += " \"\","; }
- auto suffix = *it != enum_def.vals.vec.back() ? "," : "";
- code_ += " \"" + Name(ev) + "\"" + suffix;
+ auto ev = *it;
+ for (auto k = enum_def.Distance(val, ev); k > 1; --k) {
+ code_ += " \"\",";
+ }
+ val = ev;
+ auto suffix = *it != enum_def.Vals().back() ? "," : "";
+ code_ += " \"" + Name(*ev) + "\"" + suffix;
}
code_ += "];";
code_ += "";
- code_ += "pub fn enum_name_{{ENUM_NAME_SNAKE}}(e: {{ENUM_NAME}}) -> "
- "&'static str {";
+ code_ +=
+ "pub fn enum_name_{{ENUM_NAME_SNAKE}}(e: {{ENUM_NAME}}) -> "
+ "&'static str {";
code_ += " let index = e as {{BASE_TYPE}}\\";
- if (enum_def.vals.vec.front()->value) {
- auto vals = GetEnumValUse(enum_def, *enum_def.vals.vec.front());
+ if (enum_def.MinValue()->IsNonZero()) {
+ auto vals = GetEnumValUse(enum_def, *enum_def.MinValue());
code_ += " - " + vals + " as {{BASE_TYPE}}\\";
}
code_ += ";";
}
case ftUnionKey:
case ftEnumKey: {
- auto ev = field.value.type.enum_def->ReverseLookup(
- StringToInt(field.value.constant.c_str()), false);
+ auto ev = field.value.type.enum_def->FindByValue(field.value.constant);
assert(ev);
return WrapInNameSpace(field.value.type.enum_def->defined_namespace,
GetEnumValUse(*field.value.type.enum_def, *ev));
const IDLOptions &opts, std::string *_text) {
std::string &text = *_text;
if (type.enum_def && opts.output_enum_identifiers) {
- auto enum_val = type.enum_def->ReverseLookup(static_cast<int64_t>(val));
- if (enum_val) {
+ auto ev = type.enum_def->ReverseLookup(static_cast<int64_t>(val));
+ if (ev) {
text += "\"";
- text += enum_val->name;
+ text += ev->name;
text += "\"";
return true;
}
}
if (fd.value.type.base_type == BASE_TYPE_UTYPE) {
auto enum_val = fd.value.type.enum_def->ReverseLookup(
- table->GetField<uint8_t>(fd.value.offset, 0));
+ table->GetField<uint8_t>(fd.value.offset, 0), true);
union_type = enum_val ? &enum_val->union_type : nullptr;
}
}
#include <algorithm>
#include <list>
#include <string>
+#include <utility>
#include <math.h>
return Error(
"default values currently only supported for scalars in tables");
}
- if (type.enum_def &&
- !type.enum_def->is_union &&
- !type.enum_def->attributes.Lookup("bit_flags") &&
- !type.enum_def->ReverseLookup(StringToInt(
- field->value.constant.c_str()))) {
- return Error("default value of " + field->value.constant + " for field " +
- name + " is not part of enum " + type.enum_def->name);
- }
// Append .0 if the value has not it (skip hex and scientific floats).
// This suffix needed for generated C++ code.
if (IsFloat(type.base_type)) {
field->value.constant += ".0";
}
}
-
- if (type.enum_def && IsScalar(type.base_type) && !struct_def.fixed &&
- !type.enum_def->attributes.Lookup("bit_flags") &&
- !type.enum_def->ReverseLookup(StringToInt(
- field->value.constant.c_str())))
- Warning("enum " + type.enum_def->name +
- " does not have a declaration for this field\'s default of " +
- field->value.constant);
+ if (type.enum_def) {
+ // The type.base_type can only be scalar, union or vector.
+ // Table, struct or string can't have enum_def.
+ // Default value of union and vector in NONE, NULL translated to "0".
+ FLATBUFFERS_ASSERT(IsInteger(type.base_type) ||
+ (type.base_type == BASE_TYPE_UNION) ||
+ (type.base_type == BASE_TYPE_VECTOR));
+ if (type.base_type == BASE_TYPE_VECTOR) {
+ // Vector can't use initialization list.
+ FLATBUFFERS_ASSERT(field->value.constant == "0");
+ } else {
+ // All unions should have the NONE ("0") enum value.
+ auto in_enum = type.enum_def->attributes.Lookup("bit_flags") ||
+ type.enum_def->FindByValue(field->value.constant);
+ if (false == in_enum)
+ return Error("default value of " + field->value.constant +
+ " for field " + name + " is not part of enum " +
+ type.enum_def->name);
+ }
+ }
field->doc_comment = dc;
ECHECK(ParseMetaData(&field->attributes));
} else {
ECHECK(atot(constant.c_str(), *this, &enum_idx));
}
- auto enum_val = val.type.enum_def->ReverseLookup(enum_idx);
+ auto enum_val = val.type.enum_def->ReverseLookup(enum_idx, true);
if (!enum_val) return Error("illegal type id for: " + field->name);
if (enum_val->union_type.base_type == BASE_TYPE_STRUCT) {
ECHECK(ParseTable(*enum_val->union_type.struct_def, &val.constant,
CheckedError Parser::ParseEnumFromString(const Type &type,
std::string *result) {
- int64_t i64 = 0;
- // Parse one or more enum identifiers, separated by spaces.
- const char *next = attribute_.c_str();
- do {
- const char *divider = strchr(next, ' ');
- std::string word;
- if (divider) {
- word = std::string(next, divider);
- next = divider + strspn(divider, " ");
+ const auto base_type =
+ type.enum_def ? type.enum_def->underlying_type.base_type : type.base_type;
+ if (!IsInteger(base_type)) return Error("not a valid value for this field");
+ uint64_t u64 = 0;
+ for (size_t pos = 0; pos != std::string::npos;) {
+ const auto delim = attribute_.find_first_of(' ', pos);
+ const auto last = (std::string::npos == delim);
+ auto word = attribute_.substr(pos, !last ? delim - pos : std::string::npos);
+ pos = !last ? delim + 1 : std::string::npos;
+ const EnumVal *ev = nullptr;
+ if (type.enum_def) {
+ ev = type.enum_def->Lookup(word);
} else {
- word = next;
- next += word.length();
- }
- if (type.enum_def) { // The field has an enum type
- auto enum_val = type.enum_def->vals.Lookup(word);
- if (!enum_val)
- return Error("unknown enum value: " + word +
- ", for enum: " + type.enum_def->name);
- i64 |= enum_val->value;
- } else { // No enum type, probably integral field.
- if (!IsInteger(type.base_type))
- return Error("not a valid value for this field: " + word);
- // TODO: could check if its a valid number constant here.
- const char *dot = strrchr(word.c_str(), '.');
- if (!dot)
+ auto dot = word.find_first_of('.');
+ if (std::string::npos == dot)
return Error("enum values need to be qualified by an enum type");
- std::string enum_def_str(word.c_str(), dot);
- std::string enum_val_str(dot + 1, word.c_str() + word.length());
- auto enum_def = LookupEnum(enum_def_str);
+ auto enum_def_str = word.substr(0, dot);
+ const auto enum_def = LookupEnum(enum_def_str);
if (!enum_def) return Error("unknown enum: " + enum_def_str);
- auto enum_val = enum_def->vals.Lookup(enum_val_str);
- if (!enum_val) return Error("unknown enum value: " + enum_val_str);
- i64 |= enum_val->value;
+ auto enum_val_str = word.substr(dot + 1);
+ ev = enum_def->Lookup(enum_val_str);
}
- } while (*next);
- *result = NumToString(i64);
+ if (!ev) return Error("unknown enum value: " + word);
+ u64 |= ev->GetAsUInt64();
+ }
+ *result = IsUnsigned(base_type) ? NumToString(u64)
+ : NumToString(static_cast<int64_t>(u64));
return NoError();
}
return struct_def;
}
-CheckedError Parser::ParseEnum(bool is_union, EnumDef **dest) {
+const EnumVal *EnumDef::MinValue() const {
+ return vals.vec.empty() ? nullptr : vals.vec.front();
+}
+const EnumVal *EnumDef::MaxValue() const {
+ return vals.vec.empty() ? nullptr : vals.vec.back();
+}
+
+template<typename T> static uint64_t EnumDistanceImpl(T e1, T e2) {
+ if (e1 < e2) { std::swap(e1, e2); } // use std for scalars
+ // Signed overflow may occur, use unsigned calculation.
+ // The unsigned overflow is well-defined by C++ standard (modulo 2^n).
+ return static_cast<uint64_t>(e1) - static_cast<uint64_t>(e2);
+}
+
+uint64_t EnumDef::Distance(const EnumVal *v1, const EnumVal *v2) const {
+ return IsUInt64() ? EnumDistanceImpl(v1->GetAsUInt64(), v2->GetAsUInt64())
+ : EnumDistanceImpl(v1->GetAsInt64(), v2->GetAsInt64());
+}
+
+std::string EnumDef::AllFlags() const {
+ FLATBUFFERS_ASSERT(attributes.Lookup("bit_flags"));
+ uint64_t u64 = 0;
+ for (auto it = Vals().begin(); it != Vals().end(); ++it) {
+ u64 |= (*it)->GetAsUInt64();
+ }
+ return IsUInt64() ? NumToString(u64) : NumToString(static_cast<int64_t>(u64));
+}
+
+EnumVal *EnumDef::ReverseLookup(int64_t enum_idx,
+ bool skip_union_default) const {
+ auto skip_first = static_cast<int>(is_union && skip_union_default);
+ for (auto it = Vals().begin() + skip_first; it != Vals().end(); ++it) {
+ if ((*it)->GetAsInt64() == enum_idx) { return *it; }
+ }
+ return nullptr;
+}
+
+EnumVal *EnumDef::FindByValue(const std::string &constant) const {
+ int64_t i64;
+ auto done = false;
+ if (IsUInt64()) {
+ uint64_t u64; // avoid reinterpret_cast of pointers
+ done = StringToNumber(constant.c_str(), &u64);
+ i64 = static_cast<int64_t>(u64);
+ } else {
+ done = StringToNumber(constant.c_str(), &i64);
+ }
+ FLATBUFFERS_ASSERT(done);
+ if (!done) return nullptr;
+ return ReverseLookup(i64, false);
+}
+
+void EnumDef::SortByValue() {
+ auto &v = vals.vec;
+ if (IsUInt64())
+ std::sort(v.begin(), v.end(), [](const EnumVal *e1, const EnumVal *e2) {
+ return e1->GetAsUInt64() < e2->GetAsUInt64();
+ });
+ else
+ std::sort(v.begin(), v.end(), [](const EnumVal *e1, const EnumVal *e2) {
+ return e1->GetAsInt64() < e2->GetAsInt64();
+ });
+}
+
+void EnumDef::RemoveDuplicates() {
+ // This method depends form SymbolTable implementation!
+ // 1) vals.vec - owner (raw pointer)
+ // 2) vals.dict - access map
+ auto first = vals.vec.begin();
+ auto last = vals.vec.end();
+ if (first == last) return;
+ auto result = first;
+ while (++first != last) {
+ if ((*result)->value != (*first)->value) {
+ *(++result) = *first;
+ } else {
+ auto ev = *first;
+ for (auto it = vals.dict.begin(); it != vals.dict.end(); ++it) {
+ if (it->second == ev) it->second = *result; // reassign
+ }
+ delete ev; // delete enum value
+ *first = nullptr;
+ }
+ }
+ vals.vec.erase(++result, last);
+}
+
+template<typename T> void EnumDef::ChangeEnumValue(EnumVal *ev, T new_value) {
+ ev->value = static_cast<int64_t>(new_value);
+}
+
+namespace EnumHelper {
+template<BaseType E> struct EnumValType { typedef int64_t type; };
+template<> struct EnumValType<BASE_TYPE_ULONG> { typedef uint64_t type; };
+} // namespace EnumHelper
+
+struct EnumValBuilder {
+ EnumVal *CreateEnumerator(const std::string &ev_name) {
+ FLATBUFFERS_ASSERT(!temp);
+ auto first = enum_def.vals.vec.empty();
+ user_value = first;
+ temp = new EnumVal(ev_name, first ? 0 : enum_def.vals.vec.back()->value);
+ return temp;
+ }
+
+ EnumVal *CreateEnumerator(const std::string &ev_name, int64_t val) {
+ FLATBUFFERS_ASSERT(!temp);
+ user_value = true;
+ temp = new EnumVal(ev_name, val);
+ return temp;
+ }
+
+ FLATBUFFERS_CHECKED_ERROR AcceptEnumerator(const std::string &name) {
+ FLATBUFFERS_ASSERT(temp);
+ ECHECK(ValidateValue(&temp->value, false == user_value));
+ FLATBUFFERS_ASSERT((temp->union_type.enum_def == nullptr) ||
+ (temp->union_type.enum_def == &enum_def));
+ auto not_unique = enum_def.vals.Add(name, temp);
+ temp = nullptr;
+ if (not_unique) return parser.Error("enum value already exists: " + name);
+ return NoError();
+ }
+
+ FLATBUFFERS_CHECKED_ERROR AcceptEnumerator() {
+ return AcceptEnumerator(temp->name);
+ }
+
+ FLATBUFFERS_CHECKED_ERROR AssignEnumeratorValue(const std::string &value) {
+ user_value = true;
+ auto fit = false;
+ auto ascending = false;
+ if (enum_def.IsUInt64()) {
+ uint64_t u64;
+ fit = StringToNumber(value.c_str(), &u64);
+ ascending = u64 > temp->GetAsUInt64();
+ temp->value = static_cast<int64_t>(u64); // well-defined since C++20.
+ } else {
+ int64_t i64;
+ fit = StringToNumber(value.c_str(), &i64);
+ ascending = i64 > temp->GetAsInt64();
+ temp->value = i64;
+ }
+ if (!fit) return parser.Error("enum value does not fit, \"" + value + "\"");
+ if (!ascending && strict_ascending && !enum_def.vals.vec.empty())
+ return parser.Error("enum values must be specified in ascending order");
+ return NoError();
+ }
+
+ template<BaseType E, typename CTYPE>
+ inline FLATBUFFERS_CHECKED_ERROR ValidateImpl(int64_t *ev, int m) {
+ typedef typename EnumHelper::EnumValType<E>::type T; // int64_t or uint64_t
+ static_assert(sizeof(T) == sizeof(int64_t), "invalid EnumValType");
+ const auto v = static_cast<T>(*ev);
+ auto up = static_cast<T>((flatbuffers::numeric_limits<CTYPE>::max)());
+ auto dn = static_cast<T>((flatbuffers::numeric_limits<CTYPE>::lowest)());
+ if (v < dn || v > (up - m)) {
+ return parser.Error("enum value does not fit, \"" + NumToString(v) +
+ (m ? " + 1\"" : "\"") + " out of " +
+ TypeToIntervalString<CTYPE>());
+ }
+ *ev = static_cast<int64_t>(v + m); // well-defined since C++20.
+ return NoError();
+ }
+
+ FLATBUFFERS_CHECKED_ERROR ValidateValue(int64_t *ev, bool next) {
+ // clang-format off
+ switch (enum_def.underlying_type.base_type) {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \
+ PTYPE, RTYPE) \
+ case BASE_TYPE_##ENUM: { \
+ if (!IsInteger(BASE_TYPE_##ENUM)) break; \
+ return ValidateImpl<BASE_TYPE_##ENUM, CTYPE>(ev, next ? 1 : 0); \
+ }
+ FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
+ #undef FLATBUFFERS_TD
+ default: break;
+ }
+ // clang-format on
+ return parser.Error("fatal: invalid enum underlying type");
+ }
+
+ EnumValBuilder(Parser &_parser, EnumDef &_enum_def, bool strict_order = true)
+ : parser(_parser),
+ enum_def(_enum_def),
+ temp(nullptr),
+ strict_ascending(strict_order),
+ user_value(false) {}
+
+ ~EnumValBuilder() { delete temp; }
+
+ Parser &parser;
+ EnumDef &enum_def;
+ EnumVal *temp;
+ const bool strict_ascending;
+ bool user_value;
+};
+
+CheckedError Parser::ParseEnum(const bool is_union, EnumDef **dest) {
std::vector<std::string> enum_comment = doc_comment_;
NEXT();
std::string enum_name = attribute_;
enum_def->underlying_type.enum_def = enum_def;
}
ECHECK(ParseMetaData(&enum_def->attributes));
+ const auto underlying_type = enum_def->underlying_type.base_type;
+ if (enum_def->attributes.Lookup("bit_flags") &&
+ !IsUnsigned(underlying_type)) {
+ // todo: Convert to the Error in the future?
+ Warning("underlying type of bit_flags enum must be unsigned");
+ }
+ // Protobuf allows them to be specified in any order, so sort afterwards.
+ const auto strict_ascending = (false == opts.proto_mode);
+ EnumValBuilder evb(*this, *enum_def, strict_ascending);
EXPECT('{');
- if (is_union) enum_def->vals.Add("NONE", new EnumVal("NONE", 0));
- std::set<std::pair<BaseType, StructDef*>> union_types;
- for (;;) {
+ // A lot of code generatos expect that an enum is not-empty.
+ if ((is_union || Is('}')) && !opts.proto_mode) {
+ evb.CreateEnumerator("NONE");
+ ECHECK(evb.AcceptEnumerator());
+ }
+ std::set<std::pair<BaseType, StructDef *>> union_types;
+ while (!Is('}')) {
if (opts.proto_mode && attribute_ == "option") {
ECHECK(ParseProtoOption());
} else {
- auto value_name = attribute_;
- auto full_name = value_name;
- std::vector<std::string> value_comment = doc_comment_;
+ auto &ev = *evb.CreateEnumerator(attribute_);
+ auto full_name = ev.name;
+ ev.doc_comment = doc_comment_;
EXPECT(kTokenIdentifier);
if (is_union) {
- ECHECK(ParseNamespacing(&full_name, &value_name));
+ ECHECK(ParseNamespacing(&full_name, &ev.name));
if (opts.union_value_namespacing) {
// Since we can't namespace the actual enum identifiers, turn
// namespace parts into part of the identifier.
- value_name = full_name;
- std::replace(value_name.begin(), value_name.end(), '.', '_');
+ ev.name = full_name;
+ std::replace(ev.name.begin(), ev.name.end(), '.', '_');
}
- }
- auto prevsize = enum_def->vals.vec.size();
- auto prevvalue = prevsize > 0 ? enum_def->vals.vec.back()->value : 0;
- auto &ev = *new EnumVal(value_name, 0);
- if (enum_def->vals.Add(value_name, &ev))
- return Error("enum value already exists: " + value_name);
- ev.doc_comment = value_comment;
- if (is_union) {
if (Is(':')) {
NEXT();
ECHECK(ParseType(ev.union_type));
ev.union_type = Type(BASE_TYPE_STRUCT, LookupCreateStruct(full_name));
}
if (!enum_def->uses_multiple_type_instances) {
- auto union_type_key = std::make_pair(ev.union_type.base_type, ev.union_type.struct_def);
- if (union_types.count(union_type_key) > 0) {
- enum_def->uses_multiple_type_instances = true;
- } else {
- union_types.insert(union_type_key);
- }
+ auto ins = union_types.insert(std::make_pair(
+ ev.union_type.base_type, ev.union_type.struct_def));
+ enum_def->uses_multiple_type_instances = (false == ins.second);
}
}
+
if (Is('=')) {
NEXT();
- ECHECK(atot(attribute_.c_str(), *this, &ev.value));
+ ECHECK(evb.AssignEnumeratorValue(attribute_));
EXPECT(kTokenIntegerConstant);
- if (!opts.proto_mode && prevsize &&
- enum_def->vals.vec[prevsize - 1]->value >= ev.value)
- return Error("enum values must be specified in ascending order");
- } else if (prevsize == 0) {
- // already set to zero
- } else if (prevvalue != flatbuffers::numeric_limits<int64_t>::max()) {
- ev.value = prevvalue + 1;
- } else {
- return Error("enum value overflows");
+ } else if (false == strict_ascending) {
+ // The opts.proto_mode flag is active.
+ return Error("Protobuf mode doesn't allow implicit enum values.");
}
- // Check that value fits into the underlying type.
- switch (enum_def->underlying_type.base_type) {
- // clang-format off
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \
- PTYPE, RTYPE) \
- case BASE_TYPE_##ENUM: { \
- int64_t min_value = static_cast<int64_t>( \
- flatbuffers::numeric_limits<CTYPE>::lowest()); \
- int64_t max_value = static_cast<int64_t>( \
- flatbuffers::numeric_limits<CTYPE>::max()); \
- if (ev.value < min_value || ev.value > max_value) { \
- return Error( \
- "enum value does not fit [" + NumToString(min_value) + \
- "; " + NumToString(max_value) + "]"); \
- } \
- break; \
- }
- FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
- #undef FLATBUFFERS_TD
- default: break;
- // clang-format on
- }
+ ECHECK(evb.AcceptEnumerator());
if (opts.proto_mode && Is('[')) {
NEXT();
}
if (!Is(opts.proto_mode ? ';' : ',')) break;
NEXT();
- if (Is('}')) break;
}
EXPECT('}');
+
+ // At this point, the enum can be empty if input is invalid proto-file.
+ if (!enum_def->size())
+ return Error("incomplete enum declaration, values not found");
+
if (enum_def->attributes.Lookup("bit_flags")) {
- for (auto it = enum_def->vals.vec.begin(); it != enum_def->vals.vec.end();
+ const auto base_width = static_cast<uint64_t>(8 * SizeOf(underlying_type));
+ for (auto it = enum_def->Vals().begin(); it != enum_def->Vals().end();
++it) {
- if (static_cast<size_t>((*it)->value) >=
- SizeOf(enum_def->underlying_type.base_type) * 8)
+ auto ev = *it;
+ const auto u = ev->GetAsUInt64();
+ // Stop manipulations with the sign.
+ if (!IsUnsigned(underlying_type) && u == (base_width - 1))
+ return Error("underlying type of bit_flags enum must be unsigned");
+ if (u >= base_width)
return Error("bit flag out of range of underlying integral type");
- (*it)->value = 1LL << (*it)->value;
+ enum_def->ChangeEnumValue(ev, 1ULL << u);
}
}
+
+ if (false == strict_ascending)
+ enum_def->SortByValue(); // Must be sorted to use MinValue/MaxValue.
+
if (dest) *dest = enum_def;
types_.Add(current_namespace_->GetFullyQualifiedName(enum_def->name),
new Type(BASE_TYPE_UNION, nullptr, enum_def));
return NoError();
}
-static bool compareEnumVals(const EnumVal *a, const EnumVal *b) {
- return a->value < b->value;
-}
-
// Best effort parsing of .proto declarations, with the aim to turn them
// in the closest corresponding FlatBuffer equivalent.
// We parse everything as identifiers instead of keywords, since we don't
EnumDef *enum_def;
ECHECK(ParseEnum(false, &enum_def));
if (Is(';')) NEXT();
- // Protobuf allows them to be specified in any order, so sort afterwards.
- auto &v = enum_def->vals.vec;
- std::sort(v.begin(), v.end(), compareEnumVals);
-
// Temp: remove any duplicates, as .fbs files can't handle them.
- for (auto it = v.begin(); it != v.end();) {
- if (it != v.begin() && it[0]->value == it[-1]->value) {
- auto ref = it[-1];
- auto ev = it[0];
- for (auto dit = enum_def->vals.dict.begin();
- dit != enum_def->vals.dict.end(); ++dit) {
- if (dit->second == ev) dit->second = ref; // reassign
- }
- delete ev; // delete enum value
- it = v.erase(it);
- } else {
- ++it;
- }
- }
+ enum_def->RemoveDuplicates();
} else if (IsIdent("syntax")) { // Skip these.
NEXT();
EXPECT('=');
return Error("oneof '" + name +
"' cannot be mapped to a union because member '" +
oneof_field.name + "' is not a table type.");
- auto enum_val = new EnumVal(oneof_type.struct_def->name,
- oneof_union->vals.vec.size());
- enum_val->union_type = oneof_type;
- enum_val->doc_comment = oneof_field.doc_comment;
- oneof_union->vals.Add(oneof_field.name, enum_val);
+ EnumValBuilder evb(*this, *oneof_union);
+ auto ev = evb.CreateEnumerator(oneof_type.struct_def->name);
+ ev->union_type = oneof_type;
+ ev->doc_comment = oneof_field.doc_comment;
+ ECHECK(evb.AcceptEnumerator(oneof_field.name));
}
} else {
EXPECT(';');
for (auto evit = enum_def.Vals().begin(); evit != enum_def.Vals().end();
++evit) {
auto &enum_val = **evit;
- auto enum_val_base = enum_def_base->vals.Lookup(enum_val.name);
+ auto enum_val_base = enum_def_base->Lookup(enum_val.name);
if (enum_val_base) {
- if (enum_val.value != enum_val_base->value)
+ if (enum_val != *enum_val_base)
return "values differ for enum: " + enum_val.name;
}
}
TestError("enum X:float {}", "underlying");
TestError("enum X:byte { Y, Y }", "value already");
TestError("enum X:byte { Y=2, Z=1 }", "ascending");
- TestError("enum X:byte (bit_flags) { Y=8 }", "bit flag out");
TestError("table X { Y:int; } table X {", "datatype already");
TestError("struct X (force_align: 7) { Y:int; }", "force_align");
TestError("struct X {}", "size 0");
"root_type T;"
"{ F:[ \"E.C\", \"E.A E.B E.C\" ] }"),
true);
+ // unsigned bit_flags
+ flatbuffers::Parser parser3;
+ TEST_EQ(
+ parser3.Parse("enum E:uint16 (bit_flags) { F0, F07=7, F08, F14=14, F15 }"
+ " table T { F: E = \"F15 F08\"; }"
+ "root_type T;"),
+ true);
}
void EnumNamesTest() {
void EnumOutOfRangeTest() {
TestError("enum X:byte { Y = 128 }", "enum value does not fit");
TestError("enum X:byte { Y = -129 }", "enum value does not fit");
- TestError("enum X:byte { Y = 127, Z }", "enum value does not fit");
+ TestError("enum X:byte { Y = 126, Z0, Z1 }", "enum value does not fit");
TestError("enum X:ubyte { Y = -1 }", "enum value does not fit");
TestError("enum X:ubyte { Y = 256 }", "enum value does not fit");
+ TestError("enum X:ubyte { Y = 255, Z }", "enum value does not fit");
// Unions begin with an implicit "NONE = 0".
TestError("table Y{} union X { Y = -1 }",
"enum values must be specified in ascending order");
TestError("enum X:int { Y = 2147483648 }", "enum value does not fit");
TestError("enum X:uint { Y = -1 }", "enum value does not fit");
TestError("enum X:uint { Y = 4294967297 }", "enum value does not fit");
- TestError("enum X:long { Y = 9223372036854775808 }", "constant does not fit");
- TestError("enum X:long { Y = 9223372036854775807, Z }", "enum value overflows");
- TestError("enum X:ulong { Y = -1 }", "enum value does not fit");
- // TODO: these are perfectly valid constants that shouldn't fail
- TestError("enum X:ulong { Y = 13835058055282163712 }", "constant does not fit");
- TestError("enum X:ulong { Y = 18446744073709551615 }", "constant does not fit");
+ TestError("enum X:long { Y = 9223372036854775808 }", "does not fit");
+ TestError("enum X:long { Y = 9223372036854775807, Z }", "enum value does not fit");
+ TestError("enum X:ulong { Y = -1 }", "does not fit");
+ TestError("enum X:ubyte (bit_flags) { Y=8 }", "bit flag out");
+ TestError("enum X:byte (bit_flags) { Y=7 }", "must be unsigned"); // -128
+ // bit_flgs out of range
+ TestError("enum X:ubyte (bit_flags) { Y0,Y1,Y2,Y3,Y4,Y5,Y6,Y7,Y8 }", "out of range");
}
void EnumValueTest() {
// Generate json with defaults and check.
TEST_EQ(TestValue<int>(nullptr, "E", "enum E:int { Z, V=5 }"), 0);
TEST_EQ(TestValue<int>(nullptr, "E=V", "enum E:int { Z, V=5 }"), 5);
+ // u84 test
+ TEST_EQ(TestValue<uint64_t>(nullptr, "E=V",
+ "enum E:ulong { V = 13835058055282163712 }"),
+ 13835058055282163712ULL);
+ TEST_EQ(TestValue<uint64_t>(nullptr, "E=V",
+ "enum E:ulong { V = 18446744073709551615 }"),
+ 18446744073709551615ULL);
+ // Assign non-enum value to enum field. Is it right?
+ TEST_EQ(TestValue<int>("{ Y:7 }", "E", "enum E:int { V = 0 }"), 7);
}
void IntegerOutOfRangeTest() {
std::string req_locale;
if (flatbuffers::ReadEnvironmentVariable("FLATBUFFERS_TEST_LOCALE",
- &req_locale)) {
+ &req_locale)) {
TEST_OUTPUT_LINE("The environment variable FLATBUFFERS_TEST_LOCALE=%s",
req_locale.c_str());
req_locale = flatbuffers::RemoveStringQuotes(req_locale);