Compare commits

...

20 Commits

Author SHA1 Message Date
Wouter van Oortmerssen
ea57dfe897 Fixed missing <functional> header (VS).
Change-Id: I89d0f9b18bfe4d27be325c7f7205dee14bc7e1be
Tested: on Windows.
2014-09-03 16:39:22 -07:00
Wouter van Oortmerssen
9f506f57c0 Made sure GetRootAs..() functions are generated for all Java tables.
Previously they were only generated for the root_type, making it
impossible to use the other types in the file as the root of a buffer.

Bug: 17206174
Change-Id: Ie71bed42ac3b22dcceae6385cbd5846c37e5f1b8
Tested: on Linux
2014-09-03 15:34:40 -07:00
Wouter van Oortmerssen
57b614587c Setting a field twice in a JSON object now gives error.
Before, it would crash in FlatBufferBuilder.

Bug: 17357164
Change-Id: I6e6dbada5261745220345379eb53eb3eb113e8f8
Tested: on Linux.
2014-09-03 14:48:37 -07:00
Wouter van Oortmerssen
15dc1a86cd Fixed flatc silently accepting non-scalars as default values.
Bug: 17304016
Change-Id: I4873f8ef32fbb2657f15fc53a2c8f767e10f2d96
Tested: on Linux
2014-09-03 12:23:15 -07:00
Wouter van Oortmerssen
766d0df797 Fixed C++ codegen ignoring multiple padding scalars per field.
Bug: 17373251, 17221979
Change-Id: Ib8b77835f0acd3290f0a5e7d0f683d9fdcbf7230
Tested: on Linux
2014-09-03 11:34:47 -07:00
Wouter van Oortmerssen
4507594812 Made reading read-only ByteBuffers work.
Also added new constructor that allows ByteBuffer reuse.

Change-Id: I9c20ea96c67533066461f4e23b0d03b9b47cd068
Tested: on OS X.
2014-09-03 11:03:02 -07:00
Wouter van Oortmerssen
8e40902d52 Fixed compiler warning for int to char conversion.
Change-Id: Idc6c152ebf9e733ac72c01f3888b69e3b5f33aa9
Tested: on Linux.
2014-09-02 17:32:12 -07:00
Wouter van Oortmerssen
bc5fa9d52f Fixed compile errors in VS / gcc for recent commits.
Tested: on Windows & Linux.

Change-Id: I90e18c448fc2fafeb83a6cdc3776174479874562
2014-08-25 10:42:38 -07:00
Wouter van Oortmerssen
11b743688c Improved the verifier to be even more resilient.
Theoretically, an attacker could construct a FlatBuffer with the
sole purpose of making verification really expensive, essentially
DOS-ing a server that uses verification on FlatBuffers. This adds
a max table depth and max table amount at which point the
verifier declares the buffer malformed.

Bug: 16301336
Change-Id: I6b098c31d030d24c19e852b33609110658e66aa9
Tested: on OS X
2014-08-22 14:14:32 -07:00
Wouter van Oortmerssen
ffb3dec573 Prefixing of enum value identifiers in C++ is now optional.
See -P option to flatc.

Bug: 16814856
Change-Id: I855973df6afa27e0efa27cf9c4b4aee8a1fcdd22
Tested: on OS X.
2014-08-22 14:02:32 -07:00
Wouter van Oortmerssen
51ba48ae40 flatc now outputs the filename with error messages.
Bug: 16683956
Change-Id: Id8435e868899ca0bbf0add84852a54bfaea63b4a
Tested: on OS X.
2014-08-22 14:02:32 -07:00
Wouter van Oortmerssen
c553b6b950 The list of enum values is now allowed to end in a comma.
Bug: 16490424
Change-Id: Ic3dd5f06efb5cf2dc4aefbd3f2db64c7b59b6b93
Tested: on OS X.
2014-08-22 14:02:32 -07:00
Wouter van Oortmerssen
541b06759f Checking for clashes between field names and generated field names.
This happens when the schema is parsed, to avoid compile time errors
later, which would be harder to understand.

Bug: 16325216
Change-Id: I24cabf1adaf1700796b91e3a9641bca43a68bfbd
Tested: on OS X.
2014-08-22 14:02:32 -07:00
Wouter van Oortmerssen
30af866e5a flatc now creates the output directory if it doesn't exist yet.
Also cleaned up the general mkdir functionality.

Tested: on OS X.

Tested that a command line like: ./flatc -j -o foo/bar/baz tests/monster_test.fbs
generates files in foo/bar/baz/MyGame/Example/*.java which previously didn't exist.

Windows code was previously tested but has been moved, so needs to be tested again.

Change-Id: Iee943121b3be4f92961a3ec94c2cb578165f114a
2014-08-22 14:02:31 -07:00
Wouter van Oortmerssen
ebac1e1940 Support all JSON escape codes (including \u) for parsing & text gen.
Bug: 16624362
Change-Id: Ia09ea404c0c11dd1dc6993a8cbd155bf8152b65f
Tested: on Windows & Linux.
2014-08-22 14:02:31 -07:00
Wouter van Oortmerssen
f7b0d130b6 Fixed warnings for unused variables in Visual Studio.
Change-Id: I51eeed20c3e0a4914280bf33585ca03b9a9952aa
Tested: on Windows.
2014-08-20 11:25:58 -07:00
Wouter van Oortmerssen
c2ba7fd251 Referring to types from other namespaces in C++ now works correctly.
Previously, it would ignore the fact that the type comes from a
different namespace. Now they are pre-declared in their own namespace,
and referenced with a qualified name if necessary.

Bug: 16851682
Change-Id: I5cb625b86d28e7436b9e93c70a0fa16a600d9884
Tested: on Linux
2014-08-19 17:20:08 -07:00
Wouter van Oortmerssen
be894f09df Schemas now support include files.
Bug: 15521443
Change-Id: I2e1ef97e7225a1a0ecf2ca65e31d49d443003747
Tested: on Linux.
2014-08-19 16:44:14 -07:00
Wouter van Oortmerssen
293a8110c4 Fixed "unused private field" warning for clang.
Bug: 17095037
Change-Id: Iedbe56f6ddc0ba7876896b0bb7ed9da8e6a85f7e
Tested: on Linux & OS X.
2014-08-19 14:25:55 -07:00
Wouter van Oortmerssen
8c5d7f7dea Clarified a documentation statement about circular references.
Change-Id: I8f3c50085f89037995ab9f26b28d3c83667e9d6f
2014-08-19 14:25:55 -07:00
30 changed files with 690 additions and 233 deletions

4
.gitignore vendored
View File

@@ -35,3 +35,7 @@ flatsampletext
snapshot.sh
tests/go_gen
CMakeLists.txt.user
CMakeScripts/**
build/Xcode/FlatBuffers.xcodeproj/project.xcworkspace/**
build/Xcode/FlatBuffers.xcodeproj/xcuserdata/**

View File

@@ -52,13 +52,13 @@ set(CMAKE_BUILD_TYPE Debug)
# source_group(Tests FILES ${FlatBuffers_Tests_SRCS})
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++ -Wall -pedantic -Werror -Wextra")
elseif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -std=c++0x -Wall -pedantic -Werror -Wextra")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Werror -Wextra")
if(FLATBUFFERS_CODE_COVERAGE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fprofile-arcs -ftest-coverage")
set(CMAKE_EXE_LINKER_FLAGS

View File

@@ -53,7 +53,8 @@ $(document).ready(function(){initNavTree('md__grammar.html','');});
<div class="title">Formal Grammar of the schema language </div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><p>schema = namespace_decl | type_decl | enum_decl | root_decl | object</p>
<div class="textblock"><p>schema = include* ( namespace_decl | type_decl | enum_decl | root_decl | object )*</p>
<p>include = <code>include</code> string_constant <code>;</code></p>
<p>namespace_decl = <code>namespace</code> ident ( <code>.</code> ident )* <code>;</code></p>
<p>type_decl = ( <code>table</code> | <code>struct</code> ) ident metadata <code>{</code> field_decl+ <code>}</code></p>
<p>enum_decl = ( <code>enum</code> | <code>union</code> ) ident [ <code>:</code> type ] metadata <code>{</code> commasep( enumval_decl ) <code>}</code></p>

View File

@@ -107,7 +107,7 @@ inline const char **EnumNamesAny() {
inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; }
</pre><p>Unions share a lot with enums. </p><pre class="fragment">struct Vec3;
struct Monster;
</pre><p>Predeclare all datatypes since there may be circular references. </p><pre class="fragment">MANUALLY_ALIGNED_STRUCT(4) Vec3 {
</pre><p>Predeclare all data types since circular references between types are allowed (circular references between object are not, though). </p><pre class="fragment">MANUALLY_ALIGNED_STRUCT(4) Vec3 {
private:
float x_;
float y_;

View File

@@ -111,6 +111,10 @@ root_type Monster;
<p>Unions share a lot of properties with enums, but instead of new names for constants, you use names of tables. You can then declare a union field which can hold a reference to any of those types, and additionally a hidden field with the suffix <code>_type</code> is generated that holds the corresponding enum value, allowing you to know which type to cast to at runtime.</p>
<h3>Namespaces</h3>
<p>These will generate the corresponding namespace in C++ for all helper code, and packages in Java. You can use <code>.</code> to specify nested namespaces / packages.</p>
<h3>Includes</h3>
<p>You can include other schemas files in your current one, e.g.: </p><pre class="fragment">include "mydefinitions.fbs"
</pre><p>This makes it easier to refer to types defined elsewhere. <code>include</code> automatically ensures each file is parsed just once, even when referred to more than once.</p>
<p>When using the <code>flatc</code> compiler to generate code for schema definitions, only definitions in the current file will be generated, not those from the included files (those you still generate separately).</p>
<h3>Root type</h3>
<p>This declares what you consider to be the root table (or struct) of the serialized data. This is particular important for parsing JSON data, which doesn't include object type information.</p>
<h3>File identification and extension</h3>
@@ -140,6 +144,20 @@ root_type Monster;
<li>It accepts field names with and without quotes, like many JSON parsers already do. It outputs them without quotes as well, though can be made to output them using the <code>strict_json</code> flag.</li>
<li>If a field has an enum type, the parser will recognize symbolic enum values (with or without quotes) instead of numbers, e.g. <code>field: EnumVal</code>. If a field is of integral type, you can still use symbolic names, but values need to be prefixed with their type and need to be quoted, e.g. <code>field: "Enum.EnumVal"</code>. For enums representing flags, you may place multiple inside a string separated by spaces to OR them, e.g. <code>field: "EnumVal1 EnumVal2"</code> or <code>field: "Enum.EnumVal1 Enum.EnumVal2"</code>.</li>
</ul>
<p>When parsing JSON, it recognizes the following escape codes in strings:</p>
<ul>
<li><code>\n</code> - linefeed.</li>
<li><code>\t</code> - tab.</li>
<li><code>\r</code> - carriage return.</li>
<li><code>\b</code> - backspace.</li>
<li><code>\f</code> - form feed.</li>
<li><code>\"</code> - double quote.</li>
<li><code>\\</code> - backslash.</li>
<li><code>\/</code> - forward slash.</li>
<li><code>\uXXXX</code> - 16-bit unicode code point, converted to the equivalent UTF-8 representation.</li>
<li><code>\xXX</code> - 8-bit binary hexadecimal number XX. This is the only one that is not in the JSON spec (see <a href="http://json.org/">http://json.org/</a>), but is needed to be able to encode arbitrary binary in strings to text and back without losing information (e.g. the byte 0xFF can't be represented in standard JSON).</li>
</ul>
<p>It also generates these escape codes back again when generating JSON from a binary representation.</p>
<h2>Gotchas</h2>
<h3>Schemas and version control</h3>
<p>FlatBuffers relies on new field declarations being added at the end, and earlier declarations to not be removed, but be marked deprecated when needed. We think this is an improvement over the manual number assignment that happens in Protocol Buffers (and which is still an option using the <code>id</code> attribute mentioned above).</p>

View File

@@ -34,3 +34,6 @@ be generated for each file processed:
- `-S` : Generate strict JSON (field names are enclosed in quotes).
By default, no quotes are generated.
- `-P` : Don't prefix enum values in generated C++ by their enum type.

View File

@@ -188,6 +188,11 @@ a full traversal (since any scalar data is not actually touched),
and since it may cause the buffer to be brought into cache before
reading, the actual overhead may be even lower than expected.
In specialized cases where a denial of service attack is possible,
the verifier has two additional constructor arguments that allow
you to limit the nesting depth and total amount of tables the
verifier may encounter before declaring the buffer malformed.
## Text & schema parsing
Using binary buffers with the generated header provides a super low

View File

@@ -1,6 +1,9 @@
# Formal Grammar of the schema language
schema = namespace\_decl | type\_decl | enum\_decl | root\_decl | object
schema = include*
( namespace\_decl | type\_decl | enum\_decl | root\_decl | object )*
include = `include` string\_constant `;`
namespace\_decl = `namespace` ident ( `.` ident )* `;`

View File

@@ -159,7 +159,8 @@ Unions share a lot with enums.
struct Vec3;
struct Monster;
Predeclare all datatypes since there may be circular references.
Predeclare all data types since circular references between types are allowed
(circular references between object are not, though).
MANUALLY_ALIGNED_STRUCT(4) Vec3 {
private:

View File

@@ -141,6 +141,20 @@ These will generate the corresponding namespace in C++ for all helper
code, and packages in Java. You can use `.` to specify nested namespaces /
packages.
### Includes
You can include other schemas files in your current one, e.g.:
include "mydefinitions.fbs"
This makes it easier to refer to types defined elsewhere. `include`
automatically ensures each file is parsed just once, even when referred to
more than once.
When using the `flatc` compiler to generate code for schema definitions,
only definitions in the current file will be generated, not those from the
included files (those you still generate separately).
### Root type
This declares what you consider to be the root table (or struct) of the
@@ -254,6 +268,26 @@ JSON:
separated by spaces to OR them, e.g.
`field: "EnumVal1 EnumVal2"` or `field: "Enum.EnumVal1 Enum.EnumVal2"`.
When parsing JSON, it recognizes the following escape codes in strings:
- `\n` - linefeed.
- `\t` - tab.
- `\r` - carriage return.
- `\b` - backspace.
- `\f` - form feed.
- `\"` - double quote.
- `\\` - backslash.
- `\/` - forward slash.
- `\uXXXX` - 16-bit unicode code point, converted to the equivalent UTF-8
representation.
- `\xXX` - 8-bit binary hexadecimal number XX. This is the only one that is
not in the JSON spec (see http://json.org/), but is needed to be able to
encode arbitrary binary in strings to text and back without losing
information (e.g. the byte 0xFF can't be represented in standard JSON).
It also generates these escape codes back again when generating JSON from a
binary representation.
## Gotchas
### Schemas and version control

View File

@@ -669,8 +669,10 @@ inline bool BufferHasIdentifier(const void *buf, const char *identifier) {
// Helper class to verify the integrity of a FlatBuffer
class Verifier {
public:
Verifier(const uint8_t *buf, size_t buf_len)
: buf_(buf), end_(buf + buf_len)
Verifier(const uint8_t *buf, size_t buf_len, size_t _max_depth = 64,
size_t _max_tables = 1000000)
: buf_(buf), end_(buf + buf_len), depth_(0), max_depth_(_max_depth),
num_tables_(0), max_tables_(_max_tables)
{}
// Verify any range within the buffer.
@@ -688,7 +690,7 @@ class Verifier {
}
// Verify a pointer (may be NULL) of a table type.
template<typename T> bool VerifyTable(const T *table) const {
template<typename T> bool VerifyTable(const T *table) {
return !table || table->Verify(*this);
}
@@ -733,8 +735,7 @@ class Verifier {
}
// Special case for table contents, after the above has been called.
template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec)
const {
template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec) {
if (vec) {
for (uoffset_t i = 0; i < vec->Length(); i++) {
if (!vec->Get(i)->Verify(*this)) return false;
@@ -744,16 +745,40 @@ class Verifier {
}
// Verify this whole buffer, starting with root type T.
template<typename T> bool VerifyBuffer() const {
template<typename T> bool VerifyBuffer() {
// Call T::Verify, which must be in the generated code for this type.
return Verify<uoffset_t>(buf_) &&
reinterpret_cast<const T *>(buf_ + ReadScalar<uoffset_t>(buf_))->
Verify(*this);
}
// Called at the start of a table to increase counters measuring data
// structure depth and amount, and possibly bails out with false if
// limits set by the constructor have been hit. Needs to be balanced
// with EndTable().
bool VerifyComplexity() {
depth_++;
num_tables_++;
bool too_complex = depth_ > max_depth_ || num_tables_ > max_tables_;
#ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
assert(!too_complex);
#endif
return !too_complex;
}
// Called at the end of a table to pop the depth count.
bool EndTable() {
depth_--;
return true;
}
private:
const uint8_t *buf_;
const uint8_t *end_;
size_t depth_;
size_t max_depth_;
size_t num_tables_;
size_t max_tables_;
};
// "structs" are flat structures that do not have an offset table, thus
@@ -828,12 +853,13 @@ class Table {
// Verify the vtable of this table.
// Call this once per table, followed by VerifyField once per field.
bool VerifyTable(const Verifier &verifier) const {
bool VerifyTableStart(Verifier &verifier) const {
// Check the vtable offset.
if (!verifier.Verify<soffset_t>(data_)) return false;
auto vtable = data_ - ReadScalar<soffset_t>(data_);
// Check the vtable size field, then check vtable fits in its entirety.
return verifier.Verify<voffset_t>(vtable) &&
return verifier.VerifyComplexity() &&
verifier.Verify<voffset_t>(vtable) &&
verifier.Verify(vtable, ReadScalar<voffset_t>(vtable));
}

View File

@@ -19,6 +19,7 @@
#include <map>
#include <memory>
#include <functional>
#include "flatbuffers/flatbuffers.h"
@@ -159,6 +160,11 @@ template<typename T> class SymbolTable {
std::vector<T *> vec; // Used to iterate in order of insertion
};
// A name space, as set in the schema.
struct Namespace {
std::vector<std::string> components;
};
// Base class for all definition types (fields, structs_, enums_).
struct Definition {
Definition() : generated(false) {}
@@ -183,7 +189,8 @@ struct StructDef : public Definition {
predecl(true),
sortbysize(true),
minalign(1),
bytesize(0)
bytesize(0),
defined_namespace(nullptr)
{}
void PadLastField(size_t minalign) {
@@ -198,6 +205,7 @@ struct StructDef : public Definition {
bool sortbysize; // Whether fields come in the declaration or size order.
size_t minalign; // What the whole object needs to be aligned to.
size_t bytesize; // Size if fixed.
Namespace *defined_namespace; // Where it was defined.
};
inline bool IsStruct(const Type &type) {
@@ -245,16 +253,31 @@ class Parser {
root_struct_def(nullptr),
source_(nullptr),
cursor_(nullptr),
line_(1) {}
line_(1) {
// Just in case none are declared:
namespaces_.push_back(new Namespace());
}
~Parser() {
for (auto it = namespaces_.begin(); it != namespaces_.end(); ++it) {
delete *it;
}
}
// Parse the string containing either schema or JSON data, which will
// populate the SymbolTable's or the FlatBufferBuilder above.
bool Parse(const char *_source);
// filepath indicates the file that _source was loaded from, it is
// used to resolve any include statements.
bool Parse(const char *_source, const char *filepath);
// Set the root type. May override the one set in the schema.
bool SetRootType(const char *name);
// Mark all definitions as already having code generated.
void MarkGenerated();
private:
int64_t ParseHexNum(int nibbles);
void Next();
bool IsNext(int t);
void Expect(int t);
@@ -279,7 +302,7 @@ class Parser {
public:
SymbolTable<StructDef> structs_;
SymbolTable<EnumDef> enums_;
std::vector<std::string> name_space_; // As set in the schema.
std::vector<Namespace *> namespaces_;
std::string error_; // User readable error_ if Parse() == false
FlatBufferBuilder builder_; // any data contained in the file
@@ -295,6 +318,7 @@ class Parser {
std::vector<std::pair<Value, FieldDef *>> field_stack_;
std::vector<uint8_t> struct_stack_;
std::map<std::string, bool> included_files_;
};
// Utility functions for generators:
@@ -319,9 +343,10 @@ struct GeneratorOptions {
bool strict_json;
int indent_step;
bool output_enum_identifiers;
bool prefixed_enums;
GeneratorOptions() : strict_json(false), indent_step(2),
output_enum_identifiers(true) {}
output_enum_identifiers(true), prefixed_enums(true) {}
};
// Generate text (JSON) from a given FlatBuffer, and a given Parser
@@ -338,7 +363,8 @@ extern void GenerateText(const Parser &parser,
// Generate a C++ header from the definitions in the Parser object.
// See idl_gen_cpp.
extern std::string GenerateCPP(const Parser &parser,
const std::string &include_guard_ident);
const std::string &include_guard_ident,
const GeneratorOptions &opts);
extern bool GenerateCPP(const Parser &parser,
const std::string &path,
const std::string &file_name,

View File

@@ -22,16 +22,14 @@
#include <string>
#include <sstream>
#include <stdlib.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <sys/stat.h>
#endif
namespace flatbuffers {
static const char kPosixPathSeparator = '/';
#ifdef _WIN32
static const char kPathSeparator = '\\';
#else
static const char kPathSeparator = kPosixPathSeparator;
#endif // _WIN32
// Convert an integer or floating point value to a string.
// In contrast to std::stringstream, "char" values are
// converted to a string of digits.
@@ -51,13 +49,11 @@ template<> inline std::string NumToString<unsigned char>(unsigned char t) {
}
// Convert an integer value to a hexadecimal string.
// The returned string length is the number of nibbles in
// the supplied value prefixed by 0 digits. For example,
// IntToStringHex(static_cast<int>(0x23)) returns the
// string "00000023".
template<typename T> std::string IntToStringHex(T i) {
// The returned string length is always xdigits long, prefixed by 0 digits.
// For example, IntToStringHex(0x23, 8) returns the string "00000023".
inline std::string IntToStringHex(int i, int xdigits) {
std::stringstream ss;
ss << std::setw(sizeof(T) * 2)
ss << std::setw(xdigits)
<< std::setfill('0')
<< std::hex
<< std::uppercase
@@ -66,11 +62,11 @@ template<typename T> std::string IntToStringHex(T i) {
}
// Portable implementation of strtoull().
inline int64_t StringToInt(const char *str) {
inline int64_t StringToInt(const char *str, int base = 10) {
#ifdef _MSC_VER
return _strtoui64(str, nullptr, 10);
return _strtoui64(str, nullptr, base);
#else
return strtoull(str, nullptr, 10);
return strtoull(str, nullptr, base);
#endif
}
@@ -107,6 +103,101 @@ inline bool SaveFile(const char *name, const std::string &buf, bool binary) {
return SaveFile(name, buf.c_str(), buf.size(), binary);
}
// Functionality for minimalistic portable path handling:
static const char kPosixPathSeparator = '/';
#ifdef _WIN32
static const char kPathSeparator = '\\';
static const char *PathSeparatorSet = "\\/"; // Intentionally no ':'
#else
static const char kPathSeparator = kPosixPathSeparator;
static const char *PathSeparatorSet = "/";
#endif // _WIN32
// Returns the path with the extension, if any, removed.
inline std::string StripExtension(const std::string &filepath) {
size_t i = filepath.find_last_of(".");
return i != std::string::npos ? filepath.substr(0, i) : filepath;
}
// Return the last component of the path, after the last separator.
inline std::string StripPath(const std::string &filepath) {
size_t i = filepath.find_last_of(PathSeparatorSet);
return i != std::string::npos ? filepath.substr(i + 1) : filepath;
}
// Strip the last component of the path + separator.
inline std::string StripFileName(const std::string &filepath) {
size_t i = filepath.find_last_of(PathSeparatorSet);
return i != std::string::npos ? filepath.substr(0, i) : "";
}
// This function ensure a directory exists, by recursively
// creating dirs for any parts of the path that don't exist yet.
inline void EnsureDirExists(const std::string &filepath) {
auto parent = StripFileName(filepath);
if (parent.length()) EnsureDirExists(parent);
#ifdef _WIN32
_mkdir(filepath.c_str());
#else
mkdir(filepath.c_str(), S_IRWXU|S_IRGRP|S_IXGRP);
#endif
}
// To and from UTF-8 unicode conversion functions
// Convert a unicode code point into a UTF-8 representation by appending it
// to a string. Returns the number of bytes generated.
inline int ToUTF8(uint32_t ucc, std::string *out) {
assert(!(ucc & 0x80000000)); // Top bit can't be set.
// 6 possible encodings: http://en.wikipedia.org/wiki/UTF-8
for (int i = 0; i < 6; i++) {
// Max bits this encoding can represent.
uint32_t max_bits = 6 + i * 5 + static_cast<int>(!i);
if (ucc < (1u << max_bits)) { // does it fit?
// Remaining bits not encoded in the first byte, store 6 bits each
uint32_t remain_bits = i * 6;
// Store first byte:
(*out) += static_cast<char>((0xFE << (max_bits - remain_bits)) |
(ucc >> remain_bits));
// Store remaining bytes:
for (int j = i - 1; j >= 0; j--) {
(*out) += static_cast<char>(((ucc >> (j * 6)) & 0x3F) | 0x80);
}
return i + 1; // Return the number of bytes added.
}
}
assert(0); // Impossible to arrive here.
return -1;
}
// Converts whatever prefix of the incoming string corresponds to a valid
// UTF-8 sequence into a unicode code. The incoming pointer will have been
// advanced past all bytes parsed.
// returns -1 upon corrupt UTF-8 encoding (ignore the incoming pointer in
// this case).
inline int FromUTF8(const char **in) {
int len = 0;
// Count leading 1 bits.
for (int mask = 0x80; mask >= 0x04; mask >>= 1) {
if (**in & mask) {
len++;
} else {
break;
}
}
if ((**in << len) & 0x80) return -1; // Bit after leading 1's must be 0.
if (!len) return *(*in)++;
// Grab initial bits of the code.
int ucc = *(*in)++ & ((1 << (7 - len)) - 1);
for (int i = 0; i < len - 1; i++) {
if ((**in & 0xC0) != 0x80) return -1; // Upper bits must 1 0.
ucc <<= 6;
ucc |= *(*in)++ & 0x3F; // Grab 6 more bits of the code.
}
return ucc;
}
} // namespace flatbuffers
#endif // FLATBUFFERS_UTIL_H_

View File

@@ -30,7 +30,7 @@ public class FlatBufferBuilder {
int space; // Remaining space in the ByteBuffer.
static final Charset utf8charset = Charset.forName("UTF-8");
int minalign = 1; // Minimum alignment encountered so far.
int[] vtable; // The vtable for the current table, null otherwise.
int[] vtable = null; // The vtable for the current table, null otherwise.
int object_start; // Starting offset of the current struct/table.
int[] vtables = new int[16]; // List of offsets of all vtables.
int num_vtables = 0; // Number of entries in `vtables` in use.
@@ -47,6 +47,14 @@ public class FlatBufferBuilder {
bb = newByteBuffer(new byte[initial_size]);
}
// Alternative constructor allowing reuse of ByteBuffers
public FlatBufferBuilder(ByteBuffer existing_bb) {
bb = existing_bb;
bb.clear();
bb.order(ByteOrder.LITTLE_ENDIAN);
space = bb.capacity();
}
ByteBuffer newByteBuffer(byte[] buf) {
ByteBuffer newbb = ByteBuffer.wrap(buf);
newbb.order(ByteOrder.LITTLE_ENDIAN);

View File

@@ -42,7 +42,17 @@ public class Table {
// Create a java String from UTF-8 data stored inside the flatbuffer.
protected String __string(int offset) {
offset += bb.getInt(offset);
return new String(bb.array(), offset + SIZEOF_INT, bb.getInt(offset), Charset.forName("UTF-8"));
if (bb.hasArray()) {
return new String(bb.array(), offset + SIZEOF_INT, bb.getInt(offset), Charset.forName("UTF-8"));
} else {
// We can't access .array(), since the ByteBuffer is read-only.
// We're forced to make an extra copy:
bb.position(offset + SIZEOF_INT);
byte[] copy = new byte[bb.getInt(offset)];
bb.get(copy);
bb.position(0);
return new String(copy, 0, copy.length, Charset.forName("UTF-8"));
}
}
// Get the length of a vector whose offset is stored at "offset" in this object.

View File

@@ -5,9 +5,13 @@
#include "flatbuffers/flatbuffers.h"
namespace MyGame {
namespace Sample {
struct Vec3;
struct Monster;
enum {
Color_Red = 0,
Color_Green = 1,
@@ -33,10 +37,7 @@ inline const char **EnumNamesAny() {
inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; }
bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type);
struct Vec3;
struct Monster;
bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type);
MANUALLY_ALIGNED_STRUCT(4) Vec3 {
private:
@@ -46,7 +47,7 @@ MANUALLY_ALIGNED_STRUCT(4) Vec3 {
public:
Vec3(float x, float y, float z)
: x_(flatbuffers::EndianScalar(x)), y_(flatbuffers::EndianScalar(y)), z_(flatbuffers::EndianScalar(z)) {}
: x_(flatbuffers::EndianScalar(x)), y_(flatbuffers::EndianScalar(y)), z_(flatbuffers::EndianScalar(z)) { }
float x() const { return flatbuffers::EndianScalar(x_); }
float y() const { return flatbuffers::EndianScalar(y_); }
@@ -61,8 +62,8 @@ struct Monster : private flatbuffers::Table {
const flatbuffers::String *name() const { return GetPointer<const flatbuffers::String *>(10); }
const flatbuffers::Vector<uint8_t> *inventory() const { return GetPointer<const flatbuffers::Vector<uint8_t> *>(14); }
int8_t color() const { return GetField<int8_t>(16, 2); }
bool Verify(const flatbuffers::Verifier &verifier) const {
return VerifyTable(verifier) &&
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<Vec3>(verifier, 4 /* pos */) &&
VerifyField<int16_t>(verifier, 6 /* mana */) &&
VerifyField<int16_t>(verifier, 8 /* hp */) &&
@@ -70,7 +71,8 @@ struct Monster : private flatbuffers::Table {
verifier.Verify(name()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 14 /* inventory */) &&
verifier.Verify(inventory()) &&
VerifyField<int8_t>(verifier, 16 /* color */);
VerifyField<int8_t>(verifier, 16 /* color */) &&
verifier.EndTable();
}
};
@@ -105,7 +107,7 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder
return builder_.Finish();
}
bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type) {
bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type) {
switch (type) {
case Any_NONE: return true;
case Any_Monster: return verifier.VerifyTable(reinterpret_cast<const Monster *>(union_obj));
@@ -115,7 +117,9 @@ bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uin
inline const Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<Monster>(buf); }
inline bool VerifyMonsterBuffer(const flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<Monster>(); }
inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<Monster>(); }
inline void FinishMonsterBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset<Monster> root) { fbb.Finish(root); }
} // namespace Sample
} // namespace MyGame

View File

@@ -54,9 +54,11 @@ int main(int /*argc*/, const char * /*argv*/[]) {
auto pos = monster->pos();
assert(pos);
assert(pos->z() == 3);
(void)pos;
auto inv = monster->inventory();
assert(inv);
assert(inv->Get(9) == 9);
(void)inv;
}

View File

@@ -37,8 +37,8 @@ int main(int /*argc*/, const char * /*argv*/[]) {
// parse schema first, so we can use it to parse the data after
flatbuffers::Parser parser;
ok = parser.Parse(schemafile.c_str()) &&
parser.Parse(jsonfile.c_str());
ok = parser.Parse(schemafile.c_str(), "samples/") &&
parser.Parse(jsonfile.c_str(), "samples/");
assert(ok);
// here, parser.builder_ contains a binary buffer that is the parsed data.

View File

@@ -90,6 +90,7 @@ static void Error(const char *err, const char *obj, bool usage) {
printf(" -%s %s.\n", generators[i].extension, generators[i].help);
printf(" -o PATH Prefix PATH to all generated files.\n"
" -S Strict JSON: add quotes to field names.\n"
" -P Don\'t prefix enum values with the enum name in C++.\n"
"FILEs may depend on declarations in earlier files.\n"
"FILEs after the -- must be binary flatbuffer format files.\n"
"Output files are named using the base file name of the input,"
@@ -100,22 +101,6 @@ static void Error(const char *err, const char *obj, bool usage) {
exit(1);
}
std::string StripExtension(const std::string &filename) {
size_t i = filename.find_last_of(".");
return i != std::string::npos ? filename.substr(0, i) : filename;
}
std::string StripPath(const std::string &filename) {
size_t i = filename.find_last_of(
#ifdef _WIN32
"\\:"
#else
"/"
#endif
);
return i != std::string::npos ? filename.substr(i + 1) : filename;
}
int main(int argc, const char *argv[]) {
program_name = argv[0];
flatbuffers::Parser parser;
@@ -145,6 +130,9 @@ int main(int argc, const char *argv[]) {
case 'S':
opts.strict_json = true;
break;
case 'P':
opts.prefixed_enums = false;
break;
case '-': // Separator between text and binary input files.
binary_files_from = filenames.size();
break;
@@ -187,14 +175,16 @@ int main(int argc, const char *argv[]) {
reinterpret_cast<const uint8_t *>(contents.c_str()),
contents.length());
} else {
if (!parser.Parse(contents.c_str()))
Error(parser.error_.c_str());
if (!parser.Parse(contents.c_str(), file_it->c_str()))
Error((*file_it + ": " + parser.error_).c_str());
}
std::string filebase = StripPath(StripExtension(*file_it));
std::string filebase = flatbuffers::StripPath(
flatbuffers::StripExtension(*file_it));
for (size_t i = 0; i < num_generators; ++i) {
if (generator_enabled[i]) {
flatbuffers::EnsureDirExists(output_path);
if (!generators[i].generate(parser, output_path, filebase, opts)) {
Error((std::string("Unable to generate ") +
generators[i].name +
@@ -204,17 +194,9 @@ int main(int argc, const char *argv[]) {
}
}
// Since the Parser object retains definitions across files, we must
// ensure we only output code for these once, in the file they are first
// declared:
for (auto it = parser.enums_.vec.begin();
it != parser.enums_.vec.end(); ++it) {
(*it)->generated = true;
}
for (auto it = parser.structs_.vec.begin();
it != parser.structs_.vec.end(); ++it) {
(*it)->generated = true;
}
// We do not want to generate code for the definitions in this file
// in any files coming up next.
parser.MarkGenerated();
}
return 0;

View File

@@ -33,18 +33,31 @@ static std::string GenTypeBasic(const Type &type) {
return ctypename[type.base_type];
}
static std::string GenTypeWire(const Type &type, const char *postfix);
static std::string GenTypeWire(const Parser &parser, const Type &type,
const char *postfix);
// Return a C++ pointer type, specialized to the actual struct/table types,
// and vector element types.
static std::string GenTypePointer(const Type &type) {
static std::string GenTypePointer(const Parser &parser, const Type &type) {
switch (type.base_type) {
case BASE_TYPE_STRING:
return "flatbuffers::String";
case BASE_TYPE_VECTOR:
return "flatbuffers::Vector<" + GenTypeWire(type.VectorType(), "") + ">";
case BASE_TYPE_STRUCT:
return type.struct_def->name;
return "flatbuffers::Vector<" +
GenTypeWire(parser, type.VectorType(), "") + ">";
case BASE_TYPE_STRUCT: {
auto ns = type.struct_def->defined_namespace;
if (parser.namespaces_.back() != ns) {
std::string qualified_name;
for (auto it = ns->components.begin();
it != ns->components.end(); ++it) {
qualified_name += *it + "::";
}
return qualified_name + type.struct_def->name;
} else {
return type.struct_def->name;
}
}
case BASE_TYPE_UNION:
// fall through
default:
@@ -54,31 +67,33 @@ static std::string GenTypePointer(const Type &type) {
// Return a C++ type for any type (scalar/pointer) specifically for
// building a flatbuffer.
static std::string GenTypeWire(const Type &type, const char *postfix) {
static std::string GenTypeWire(const Parser &parser, const Type &type,
const char *postfix) {
return IsScalar(type.base_type)
? GenTypeBasic(type) + postfix
: IsStruct(type)
? "const " + GenTypePointer(type) + " *"
: "flatbuffers::Offset<" + GenTypePointer(type) + ">" + postfix;
? "const " + GenTypePointer(parser, type) + " *"
: "flatbuffers::Offset<" + GenTypePointer(parser, type) + ">" + postfix;
}
// Return a C++ type for any type (scalar/pointer) that reflects its
// serialized size.
static std::string GenTypeSize(const Type &type) {
static std::string GenTypeSize(const Parser &parser, const Type &type) {
return IsScalar(type.base_type)
? GenTypeBasic(type)
: IsStruct(type)
? GenTypePointer(type)
? GenTypePointer(parser, type)
: "flatbuffers::uoffset_t";
}
// Return a C++ type for any type (scalar/pointer) specifically for
// using a flatbuffer.
static std::string GenTypeGet(const Type &type, const char *afterbasic,
const char *beforeptr, const char *afterptr) {
static std::string GenTypeGet(const Parser &parser, const Type &type,
const char *afterbasic, const char *beforeptr,
const char *afterptr) {
return IsScalar(type.base_type)
? GenTypeBasic(type) + afterbasic
: beforeptr + GenTypePointer(type) + afterptr;
: beforeptr + GenTypePointer(parser, type) + afterptr;
}
// Generate a documentation comment, if available.
@@ -91,9 +106,16 @@ static void GenComment(const std::string &dc,
}
}
static std::string GenEnumVal(const EnumDef &enum_def, const EnumVal &enum_val,
const GeneratorOptions &opts) {
return opts.prefixed_enums ? enum_def.name + "_" + enum_val.name
: enum_val.name;
}
// Generate an enum declaration and an enum string lookup table.
static void GenEnum(EnumDef &enum_def, std::string *code_ptr,
std::string *code_ptr_post) {
static void GenEnum(EnumDef &enum_def, std::string *code_ptr,
std::string *code_ptr_post,
const GeneratorOptions &opts) {
if (enum_def.generated) return;
std::string &code = *code_ptr;
std::string &code_post = *code_ptr_post;
@@ -104,7 +126,7 @@ static void GenComment(const std::string &dc,
++it) {
auto &ev = **it;
GenComment(ev.doc_comment, code_ptr, " ");
code += " " + enum_def.name + "_" + ev.name + " = ";
code += " " + GenEnumVal(enum_def, ev, opts) + " = ";
code += NumToString(ev.value);
code += (it + 1) != enum_def.vals.vec.end() ? ",\n" : "\n";
}
@@ -133,7 +155,7 @@ static void GenComment(const std::string &dc,
code += "inline const char *EnumName" + enum_def.name;
code += "(int e) { return EnumNames" + enum_def.name + "()[e";
if (enum_def.vals.vec.front()->value)
code += " - " + enum_def.name + "_" + enum_def.vals.vec.front()->name;
code += " - " + GenEnumVal(enum_def, *enum_def.vals.vec.front(), opts);
code += "]; }\n\n";
}
@@ -144,7 +166,7 @@ static void GenComment(const std::string &dc,
// has been corrupted, since the verifiers will simply fail when called
// on the wrong type.
auto signature = "bool Verify" + enum_def.name +
"(const flatbuffers::Verifier &verifier, " +
"(flatbuffers::Verifier &verifier, " +
"const void *union_obj, uint8_t type)";
code += signature + ";\n\n";
code_post += signature + " {\n switch (type) {\n";
@@ -152,7 +174,7 @@ static void GenComment(const std::string &dc,
it != enum_def.vals.vec.end();
++it) {
auto &ev = **it;
code_post += " case " + enum_def.name + "_" + ev.name;
code_post += " case " + GenEnumVal(enum_def, ev, opts);
if (!ev.value) {
code_post += ": return true;\n"; // "NONE" enum value.
} else {
@@ -181,13 +203,13 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
auto &field = **it;
if (!field.deprecated) { // Deprecated fields won't be accessible.
GenComment(field.doc_comment, code_ptr, " ");
code += " " + GenTypeGet(field.value.type, " ", "const ", " *");
code += " " + GenTypeGet(parser, field.value.type, " ", "const ", " *");
code += field.name + "() const { return ";
// Call a different accessor for pointers, that indirects.
code += IsScalar(field.value.type.base_type)
? "GetField<"
: (IsStruct(field.value.type) ? "GetStruct<" : "GetPointer<");
code += GenTypeGet(field.value.type, "", "const ", " *") + ">(";
code += GenTypeGet(parser, field.value.type, "", "const ", " *") + ">(";
code += NumToString(field.value.offset);
// Default value as second arg for non-pointer types.
if (IsScalar(field.value.type.base_type))
@@ -205,15 +227,15 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
}
// Generate a verifier function that can check a buffer from an untrusted
// source will never cause reads outside the buffer.
code += " bool Verify(const flatbuffers::Verifier &verifier) const {\n";
code += " return VerifyTable(verifier)";
code += " bool Verify(flatbuffers::Verifier &verifier) const {\n";
code += " return VerifyTableStart(verifier)";
std::string prefix = " &&\n ";
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end();
++it) {
auto &field = **it;
if (!field.deprecated) {
code += prefix + "VerifyField<" + GenTypeSize(field.value.type);
code += prefix + "VerifyField<" + GenTypeSize(parser, field.value.type);
code += ">(verifier, " + NumToString(field.value.offset);
code += " /* " + field.name + " */)";
switch (field.value.type.base_type) {
@@ -254,6 +276,7 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
}
}
}
code += prefix + "verifier.EndTable()";
code += ";\n }\n";
code += "};\n\n";
@@ -268,9 +291,10 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
auto &field = **it;
if (!field.deprecated) {
code += " void add_" + field.name + "(";
code += GenTypeWire(field.value.type, " ") + field.name + ") { fbb_.Add";
code += GenTypeWire(parser, field.value.type, " ") + field.name;
code += ") { fbb_.Add";
if (IsScalar(field.value.type.base_type))
code += "Element<" + GenTypeWire(field.value.type, "") + ">";
code += "Element<" + GenTypeWire(parser, field.value.type, "") + ">";
else if (IsStruct(field.value.type))
code += "Struct";
else
@@ -301,8 +325,8 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
++it) {
auto &field = **it;
if (!field.deprecated) {
code += ",\n " + GenTypeWire(field.value.type, " ") + field.name;
code += " = " + field.value.constant;
code += ",\n " + GenTypeWire(parser, field.value.type, " ");
code += field.name + " = " + field.value.constant;
}
}
code += ") {\n " + struct_def.name + "Builder builder_(_fbb);\n";
@@ -323,8 +347,18 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
code += " return builder_.Finish();\n}\n\n";
}
static void GenPadding(const FieldDef &field, const std::function<void (int bits)> &f) {
if (field.padding) {
for (int i = 0; i < 4; i++)
if (static_cast<int>(field.padding) & (1 << i))
f((1 << i) * 8);
assert(!(field.padding & ~0xF));
}
}
// Generate an accessor struct with constructor for a flatbuffers struct.
static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
static void GenStruct(const Parser &parser, StructDef &struct_def,
std::string *code_ptr) {
if (struct_def.generated) return;
std::string &code = *code_ptr;
@@ -341,15 +375,12 @@ static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
it != struct_def.fields.vec.end();
++it) {
auto &field = **it;
code += " " + GenTypeGet(field.value.type, " ", "", " ");
code += " " + GenTypeGet(parser, field.value.type, " ", "", " ");
code += field.name + "_;\n";
if (field.padding) {
for (int i = 0; i < 4; i++)
if (static_cast<int>(field.padding) & (1 << i))
code += " int" + NumToString((1 << i) * 8) +
"_t __padding" + NumToString(padding_id++) + ";\n";
assert(!(field.padding & ~0xF));
}
GenPadding(field, [&code, &padding_id](int bits) {
code += " int" + NumToString(bits) +
"_t __padding" + NumToString(padding_id++) + ";\n";
});
}
// Generate a constructor that takes all fields as arguments.
@@ -359,7 +390,8 @@ static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
++it) {
auto &field = **it;
if (it != struct_def.fields.vec.begin()) code += ", ";
code += GenTypeGet(field.value.type, " ", "const ", " &") + field.name;
code += GenTypeGet(parser, field.value.type, " ", "const ", " &");
code += field.name;
}
code += ")\n : ";
padding_id = 0;
@@ -373,10 +405,23 @@ static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
code += "flatbuffers::EndianScalar(" + field.name + "))";
else
code += field.name + ")";
if (field.padding)
GenPadding(field, [&code, &padding_id](int bits) {
(void)bits;
code += ", __padding" + NumToString(padding_id++) + "(0)";
});
}
code += " {}\n\n";
code += " {";
padding_id = 0;
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end();
++it) {
auto &field = **it;
GenPadding(field, [&code, &padding_id](int bits) {
(void)bits;
code += " (void)__padding" + NumToString(padding_id++) + ";";
});
}
code += " }\n\n";
// Generate accessor methods of the form:
// type name() const { return flatbuffers::EndianScalar(name_); }
@@ -385,7 +430,7 @@ static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
++it) {
auto &field = **it;
GenComment(field.doc_comment, code_ptr, " ");
code += " " + GenTypeGet(field.value.type, " ", "const ", " &");
code += " " + GenTypeGet(parser, field.value.type, " ", "const ", " &");
code += field.name + "() const { return ";
if (IsScalar(field.value.type.base_type))
code += "flatbuffers::EndianScalar(" + field.name + "_)";
@@ -397,34 +442,73 @@ static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
code += NumToString(struct_def.bytesize) + ");\n\n";
}
void GenerateNestedNameSpaces(Namespace *ns, std::string *code_ptr) {
for (auto it = ns->components.begin(); it != ns->components.end(); ++it) {
*code_ptr += "namespace " + *it + " {\n";
}
}
void CloseNestedNameSpaces(Namespace *ns, std::string *code_ptr) {
for (auto it = ns->components.rbegin(); it != ns->components.rend(); ++it) {
*code_ptr += "} // namespace " + *it + "\n";
}
}
} // namespace cpp
// Iterate through all definitions we haven't generate code for (enums, structs,
// and tables) and output them to a single file.
std::string GenerateCPP(const Parser &parser, const std::string &include_guard_ident) {
std::string GenerateCPP(const Parser &parser,
const std::string &include_guard_ident,
const GeneratorOptions &opts) {
using namespace cpp;
// Generate code for all the enum declarations.
std::string enum_code, enum_code_post;
for (auto it = parser.enums_.vec.begin();
it != parser.enums_.vec.end(); ++it) {
GenEnum(**it, &enum_code, &enum_code_post);
GenEnum(**it, &enum_code, &enum_code_post, opts);
}
// Generate forward declarations for all structs/tables, since they may
// have circular references.
std::string forward_decl_code;
std::string forward_decl_code_same_namespace;
std::string forward_decl_code_other_namespace;
Namespace *cur_name_space = nullptr;
for (auto it = parser.structs_.vec.begin();
it != parser.structs_.vec.end(); ++it) {
if (!(*it)->generated)
forward_decl_code += "struct " + (*it)->name + ";\n";
auto &struct_def = **it;
auto decl = "struct " + struct_def.name + ";\n";
if (struct_def.defined_namespace == parser.namespaces_.back()) {
forward_decl_code_same_namespace += decl;
} else {
// Wrap this decl in the correct namespace. Only open a namespace if
// the adjacent one is different.
// TODO: this could be done more intelligently, by sorting to
// namespace path and only opening/closing what is necessary, but that's
// quite a bit more complexity.
if (cur_name_space != struct_def.defined_namespace) {
if (cur_name_space) {
CloseNestedNameSpaces(cur_name_space,
&forward_decl_code_other_namespace);
}
GenerateNestedNameSpaces(struct_def.defined_namespace,
&forward_decl_code_other_namespace);
cur_name_space = struct_def.defined_namespace;
}
forward_decl_code_other_namespace += decl;
}
}
if (cur_name_space) {
CloseNestedNameSpaces(cur_name_space,
&forward_decl_code_other_namespace);
}
// Generate code for all structs, then all tables.
std::string decl_code;
for (auto it = parser.structs_.vec.begin();
it != parser.structs_.vec.end(); ++it) {
if ((**it).fixed) GenStruct(**it, &decl_code);
if ((**it).fixed) GenStruct(parser, **it, &decl_code);
}
for (auto it = parser.structs_.vec.begin();
it != parser.structs_.vec.end(); ++it) {
@@ -432,7 +516,7 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
}
// Only output file-level code if there were any declarations.
if (enum_code.length() || forward_decl_code.length() || decl_code.length()) {
if (enum_code.length() || decl_code.length()) {
std::string code;
code = "// automatically generated by the FlatBuffers compiler,"
" do not modify\n\n";
@@ -440,8 +524,9 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
// Generate include guard.
std::string include_guard = "FLATBUFFERS_GENERATED_" + include_guard_ident;
include_guard += "_";
for (auto it = parser.name_space_.begin();
it != parser.name_space_.end(); ++it) {
auto name_space = parser.namespaces_.back();
for (auto it = name_space->components.begin();
it != name_space->components.end(); ++it) {
include_guard += *it + "_";
}
include_guard += "H_";
@@ -452,17 +537,17 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
code += "#include \"flatbuffers/flatbuffers.h\"\n\n";
// Generate nested namespaces.
for (auto it = parser.name_space_.begin();
it != parser.name_space_.end(); ++it) {
code += "namespace " + *it + " {\n";
}
code += forward_decl_code_other_namespace;
code += "\n";
GenerateNestedNameSpaces(name_space, &code);
code += "\n";
code += forward_decl_code_same_namespace;
code += "\n";
// Output the main declaration code from above.
code += "\n";
code += enum_code;
code += forward_decl_code;
code += "\n";
code += decl_code;
code += enum_code_post;
@@ -477,7 +562,7 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
// The root verifier:
code += "inline bool Verify";
code += parser.root_struct_def->name;
code += "Buffer(const flatbuffers::Verifier &verifier) { "
code += "Buffer(flatbuffers::Verifier &verifier) { "
"return verifier.VerifyBuffer<";
code += parser.root_struct_def->name + ">(); }\n\n";
@@ -498,11 +583,7 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
}
}
// Close the namespaces.
for (auto it = parser.name_space_.rbegin();
it != parser.name_space_.rend(); ++it) {
code += "} // namespace " + *it + "\n";
}
CloseNestedNameSpaces(name_space, &code);
// Close the include guard.
code += "\n#endif // " + include_guard + "\n";
@@ -516,8 +597,8 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
bool GenerateCPP(const Parser &parser,
const std::string &path,
const std::string &file_name,
const GeneratorOptions & /*opts*/) {
auto code = GenerateCPP(parser, file_name);
const GeneratorOptions &opts) {
auto code = GenerateCPP(parser, file_name, opts);
return !code.length() ||
SaveFile((path + file_name + "_generated.h").c_str(), code, false);
}

View File

@@ -585,24 +585,24 @@ static bool SaveType(const Parser &parser, const Definition &def,
bool needs_imports) {
if (!classcode.length()) return true;
std::string name_space_name;
std::string name_space_dir = path;
for (auto it = parser.name_space_.begin();
it != parser.name_space_.end(); ++it) {
if (name_space_name.length()) {
name_space_name += ".";
name_space_dir += PATH_SEPARATOR;
std::string namespace_name;
std::string namespace_dir = path;
auto &namespaces = parser.namespaces_.back()->components;
for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
if (namespace_name.length()) {
namespace_name += ".";
namespace_dir += PATH_SEPARATOR;
}
name_space_name = *it;
name_space_dir += *it;
mkdir(name_space_dir.c_str(), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
namespace_name = *it;
namespace_dir += *it;
mkdir(namespace_dir.c_str(), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
}
std::string code = "";
BeginFile(name_space_name, needs_imports, &code);
BeginFile(namespace_name, needs_imports, &code);
code += classcode;
std::string filename = name_space_dir + PATH_SEPARATOR + def.name + ".go";
std::string filename = namespace_dir + PATH_SEPARATOR + def.name + ".go";
return SaveFile(filename.c_str(), code, false);
}

View File

@@ -20,13 +20,6 @@
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"
#ifdef _WIN32
#include <direct.h>
#define mkdir(n, m) _mkdir(n)
#else
#include <sys/stat.h>
#endif
namespace flatbuffers {
namespace java {
@@ -161,9 +154,7 @@ static void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
}
}
static void GenStruct(StructDef &struct_def,
std::string *code_ptr,
StructDef *root_struct_def) {
static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
if (struct_def.generated) return;
std::string &code = *code_ptr;
@@ -177,9 +168,9 @@ static void GenStruct(StructDef &struct_def,
code += "public class " + struct_def.name + " extends ";
code += struct_def.fixed ? "Struct" : "Table";
code += " {\n";
if (&struct_def == root_struct_def) {
// Generate a special accessor for the table that has been declared as
// the root type.
if (!struct_def.fixed) {
// Generate a special accessor for the table that when used as the root
// of a FlatBuffer
code += " public static " + struct_def.name + " getRootAs";
code += struct_def.name;
code += "(ByteBuffer _bb, int offset) { ";
@@ -341,27 +332,27 @@ static bool SaveClass(const Parser &parser, const Definition &def,
bool needs_imports) {
if (!classcode.length()) return true;
std::string name_space_java;
std::string name_space_dir = path;
for (auto it = parser.name_space_.begin();
it != parser.name_space_.end(); ++it) {
if (name_space_java.length()) {
name_space_java += ".";
name_space_dir += kPathSeparator;
std::string namespace_java;
std::string namespace_dir = path;
auto &namespaces = parser.namespaces_.back()->components;
for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
if (namespace_java.length()) {
namespace_java += ".";
namespace_dir += kPathSeparator;
}
name_space_java += *it;
name_space_dir += *it;
mkdir(name_space_dir.c_str(), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
namespace_java += *it;
namespace_dir += *it;
}
EnsureDirExists(namespace_dir);
std::string code = "// automatically generated, do not modify\n\n";
code += "package " + name_space_java + ";\n\n";
code += "package " + namespace_java + ";\n\n";
if (needs_imports) {
code += "import java.nio.*;\nimport java.lang.*;\nimport java.util.*;\n";
code += "import flatbuffers.*;\n\n";
}
code += classcode;
auto filename = name_space_dir + kPathSeparator + def.name + ".java";
auto filename = namespace_dir + kPathSeparator + def.name + ".java";
return SaveFile(filename.c_str(), code, false);
}
@@ -384,7 +375,7 @@ bool GenerateJava(const Parser &parser,
for (auto it = parser.structs_.vec.begin();
it != parser.structs_.vec.end(); ++it) {
std::string declcode;
GenStruct(**it, &declcode, parser.root_struct_def);
GenStruct(**it, &declcode);
if (!SaveClass(parser, **it, declcode, path, true))
return false;
}

View File

@@ -28,8 +28,12 @@ static void GenStruct(const StructDef &struct_def, const Table *table,
// If indentation is less than 0, that indicates we don't want any newlines
// either.
const char *NewLine(int indent_step) {
return indent_step >= 0 ? "\n" : "";
const char *NewLine(const GeneratorOptions &opts) {
return opts.indent_step >= 0 ? "\n" : "";
}
int Indent(const GeneratorOptions &opts) {
return std::max(opts.indent_step, 0);
}
// Output an identifier with or without quotes depending on strictness.
@@ -65,21 +69,21 @@ template<typename T> void PrintVector(const Vector<T> &v, Type type,
std::string *_text) {
std::string &text = *_text;
text += "[";
text += NewLine(opts.indent_step);
text += NewLine(opts);
for (uoffset_t i = 0; i < v.Length(); i++) {
if (i) {
text += ",";
text += NewLine(opts.indent_step);
text += NewLine(opts);
}
text.append(indent + opts.indent_step, ' ');
text.append(indent + Indent(opts), ' ');
if (IsStruct(type))
Print(v.GetStructFromOffset(i * type.struct_def->bytesize), type,
indent + opts.indent_step, nullptr, opts, _text);
indent + Indent(opts), nullptr, opts, _text);
else
Print(v.Get(i), type, indent + opts.indent_step, nullptr,
Print(v.Get(i), type, indent + Indent(opts), nullptr,
opts, _text);
}
text += NewLine(opts.indent_step);
text += NewLine(opts);
text.append(indent, ' ');
text += "]";
}
@@ -93,15 +97,29 @@ static void EscapeString(const String &s, std::string *_text) {
case '\n': text += "\\n"; break;
case '\t': text += "\\t"; break;
case '\r': text += "\\r"; break;
case '\b': text += "\\b"; break;
case '\f': text += "\\f"; break;
case '\"': text += "\\\""; break;
case '\\': text += "\\\\"; break;
default:
if (c >= ' ' && c <= '~') {
text += c;
} else {
auto u = static_cast<unsigned char>(c);
text += "\\x";
text += IntToStringHex(u);
// Not printable ASCII data. Let's see if it's valid UTF-8 first:
const char *utf8 = s.c_str() + i;
int ucc = FromUTF8(&utf8);
if (ucc >= 0x80 && ucc <= 0xFFFF) {
// Parses as Unicode within JSON's \uXXXX range, so use that.
text += "\\u";
text += IntToStringHex(ucc, 4);
// Skip past characters recognized.
i = static_cast<uoffset_t>(utf8 - s.c_str() - 1);
} else {
// It's either unprintable ASCII, arbitrary binary, or Unicode data
// that doesn't fit \uXXXX, so use \xXX escape code instead.
text += "\\x";
text += IntToStringHex(static_cast<uint8_t>(c), 2);
}
}
break;
}
@@ -202,15 +220,15 @@ static void GenStruct(const StructDef &struct_def, const Table *table,
if (fieldout++) {
text += ",";
}
text += NewLine(opts.indent_step);
text.append(indent + opts.indent_step, ' ');
text += NewLine(opts);
text.append(indent + Indent(opts), ' ');
OutputIdentifier(fd.name, opts, _text);
text += ": ";
switch (fd.value.type.base_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) \
case BASE_TYPE_ ## ENUM: \
GenField<CTYPE>(fd, table, struct_def.fixed, \
opts, indent + opts.indent_step, _text); \
opts, indent + Indent(opts), _text); \
break;
FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -219,7 +237,7 @@ static void GenStruct(const StructDef &struct_def, const Table *table,
case BASE_TYPE_ ## ENUM:
FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
GenFieldOffset(fd, table, struct_def.fixed, indent + opts.indent_step,
GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts),
union_sd, opts, _text);
break;
}
@@ -231,7 +249,7 @@ static void GenStruct(const StructDef &struct_def, const Table *table,
}
}
}
text += NewLine(opts.indent_step);
text += NewLine(opts);
text.append(indent, ' ');
text += "}";
}
@@ -247,7 +265,7 @@ void GenerateText(const Parser &parser, const void *flatbuffer,
0,
opts,
_text);
text += NewLine(opts.indent_step);
text += NewLine(opts);
}
} // namespace flatbuffers

View File

@@ -83,7 +83,8 @@ template<> inline Offset<void> atot<Offset<void>>(const char *s) {
TD(NameSpace, 265, "namespace") \
TD(RootType, 266, "root_type") \
TD(FileIdentifier, 267, "file_identifier") \
TD(FileExtension, 268, "file_extension")
TD(FileExtension, 268, "file_extension") \
TD(Include, 269, "include")
#ifdef __GNUC__
__extension__ // Stop GCC complaining about trailing comma with -Wpendantic.
#endif
@@ -107,13 +108,24 @@ static std::string TokenToString(int t) {
};
if (t < 256) { // A single ascii char token.
std::string s;
s.append(1, t);
s.append(1, static_cast<char>(t));
return s;
} else { // Other tokens.
return tokens[t - 256];
}
}
// Parses exactly nibbles worth of hex digits into a number, or error.
int64_t Parser::ParseHexNum(int nibbles) {
for (int i = 0; i < nibbles; i++)
if (!isxdigit(cursor_[i]))
Error("escape code must be followed by " + NumToString(nibbles) +
" hex digits");
auto val = StringToInt(cursor_, 16);
cursor_ += nibbles;
return val;
}
void Parser::Next() {
doc_comment_.clear();
bool seen_newline = false;
@@ -141,8 +153,21 @@ void Parser::Next() {
case 'n': attribute_ += '\n'; cursor_++; break;
case 't': attribute_ += '\t'; cursor_++; break;
case 'r': attribute_ += '\r'; cursor_++; break;
case 'b': attribute_ += '\b'; cursor_++; break;
case 'f': attribute_ += '\f'; cursor_++; break;
case '\"': attribute_ += '\"'; cursor_++; break;
case '\\': attribute_ += '\\'; cursor_++; break;
case '/': attribute_ += '/'; cursor_++; break;
case 'x': { // Not in the JSON standard
cursor_++;
attribute_ += static_cast<char>(ParseHexNum(2));
break;
}
case 'u': {
cursor_++;
ToUTF8(static_cast<int>(ParseHexNum(4)), &attribute_);
break;
}
default: Error("unknown escape code in string constant"); break;
}
} else { // printable chars + UTF-8 bytes
@@ -196,6 +221,7 @@ void Parser::Next() {
if (attribute_ == "union") { token_ = kTokenUnion; return; }
if (attribute_ == "namespace") { token_ = kTokenNameSpace; return; }
if (attribute_ == "root_type") { token_ = kTokenRootType; return; }
if (attribute_ == "include") { token_ = kTokenInclude; return; }
if (attribute_ == "file_identifier") {
token_ = kTokenFileIdentifier;
return;
@@ -338,6 +364,8 @@ void Parser::ParseField(StructDef &struct_def) {
if (token_ == '=') {
Next();
if (!IsScalar(type.base_type))
Error("default values currently only supported for scalars");
ParseSingleValue(field.value);
}
@@ -429,6 +457,10 @@ uoffset_t Parser::ParseTable(const StructDef &struct_def) {
|| struct_def.fields.vec[fieldn] != field)) {
Error("struct field appearing out of order: " + name);
}
for (auto it = field_stack_.rbegin();
it != field_stack_.rbegin() + fieldn; ++it) {
if (it->second == field) Error("field already set: " + name);
}
Expect(':');
Value val = field->value;
ParseAnyValue(val, field);
@@ -645,6 +677,7 @@ StructDef *Parser::LookupCreateStruct(const std::string &name) {
structs_.Add(name, struct_def);
struct_def->name = name;
struct_def->predecl = true;
struct_def->defined_namespace = namespaces_.back();
}
return struct_def;
}
@@ -698,7 +731,7 @@ void Parser::ParseEnum(bool is_union) {
if (prevsize && enum_def.vals.vec[prevsize - 1]->value >= ev.value)
Error("enum values must be specified in ascending order");
}
} while (IsNext(','));
} while (IsNext(',') && token_ != '}');
Expect('}');
if (enum_def.attributes.Lookup("bit_flags")) {
for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
@@ -773,6 +806,31 @@ void Parser::ParseDecl() {
}
}
}
// Check that no identifiers clash with auto generated fields.
// This is not an ideal situation, but should occur very infrequently,
// and allows us to keep using very readable names for type & length fields
// without inducing compile errors.
auto CheckClash = [&fields, &struct_def](const char *suffix,
BaseType basetype) {
auto len = strlen(suffix);
for (auto it = fields.begin(); it != fields.end(); ++it) {
auto &name = (*it)->name;
if (name.length() > len &&
name.compare(name.length() - len, len, suffix) == 0 &&
(*it)->value.type.base_type != BASE_TYPE_UTYPE) {
auto field = struct_def.fields.Lookup(
name.substr(0, name.length() - len));
if (field && field->value.type.base_type == basetype)
Error("Field " + name +
" would clash with generated functions for field " +
field->name);
}
}
};
CheckClash("_type", BASE_TYPE_UNION);
CheckClash("Type", BASE_TYPE_UNION);
CheckClash("_length", BASE_TYPE_VECTOR);
CheckClash("Length", BASE_TYPE_VECTOR);
Expect('}');
}
@@ -781,19 +839,66 @@ bool Parser::SetRootType(const char *name) {
return root_struct_def != nullptr;
}
bool Parser::Parse(const char *source) {
void Parser::MarkGenerated() {
// Since the Parser object retains definitions across files, we must
// ensure we only output code for definitions once, in the file they are first
// declared. This function marks all existing definitions as having already
// been generated.
for (auto it = enums_.vec.begin();
it != enums_.vec.end(); ++it) {
(*it)->generated = true;
}
for (auto it = structs_.vec.begin();
it != structs_.vec.end(); ++it) {
(*it)->generated = true;
}
}
bool Parser::Parse(const char *source, const char *filepath) {
included_files_[filepath] = true;
// This is the starting point to reset to if we interrupted our parsing
// to deal with an include:
restart_parse_after_include:
source_ = cursor_ = source;
line_ = 1;
error_.clear();
builder_.Clear();
try {
Next();
// Includes must come first:
while (IsNext(kTokenInclude)) {
auto name = attribute_;
Expect(kTokenStringConstant);
auto path = StripFileName(filepath);
if (path.length()) name = path + kPathSeparator + name;
if (included_files_.find(name) == included_files_.end()) {
// We found an include file that we have not parsed yet.
// Load it and parse it.
std::string contents;
if (!LoadFile(name.c_str(), true, &contents))
Error("unable to load include file: " + name);
Parse(contents.c_str(), name.c_str());
// Any errors, we're done.
if (error_.length()) return false;
// We do not want to output code for any included files:
MarkGenerated();
// This is the easiest way to continue this file after an include:
// instead of saving and restoring all the state, we simply start the
// file anew. This will cause it to encounter the same include statement
// again, but this time it will skip it, because it was entered into
// included_files_.
goto restart_parse_after_include;
}
Expect(';');
}
// Now parse all other kinds of declarations:
while (token_ != kTokenEof) {
if (token_ == kTokenNameSpace) {
Next();
name_space_.clear();
auto ns = new Namespace();
namespaces_.push_back(ns);
for (;;) {
name_space_.push_back(attribute_);
ns->components.push_back(attribute_);
Expect(kTokenIdentifier);
if (!IsNext('.')) break;
}
@@ -832,6 +937,8 @@ bool Parser::Parse(const char *source) {
file_extension_ = attribute_;
Expect(kTokenStringConstant);
Expect(';');
} else if(token_ == kTokenInclude) {
Error("includes must come before declarations");
} else {
ParseDecl();
}

View File

@@ -103,9 +103,12 @@ class JavaTest {
}
// Test it:
TestBuffer(fbb.dataBuffer(), fbb.dataStart());
// Make sure it also works with read only ByteBuffers. This is slower, since
// creating strings incurs an additional copy (see Table.__string).
TestBuffer(fbb.dataBuffer().asReadOnlyBuffer(), fbb.dataStart());
System.out.println("FlatBuffers test: completed successfully");
}

5
tests/include_test1.fbs Normal file
View File

@@ -0,0 +1,5 @@
include "include_test2.fbs";
include "include_test2.fbs"; // should be skipped
include "include_test1.fbs"; // should be skipped

9
tests/include_test2.fbs Normal file
View File

@@ -0,0 +1,9 @@
include "include_test2.fbs"; // should be skipped
namespace MyGame.OtherNameSpace;
enum FromInclude:long { IncludeVal }
struct Unused {}

View File

@@ -1,8 +1,10 @@
// example IDL file
include "include_test1.fbs";
namespace MyGame.Example;
enum Color:byte (bit_flags) { Red = 0, Green, Blue = 3 }
enum Color:byte (bit_flags) { Red = 0, Green, Blue = 3, }
union Any { Monster } // TODO: add more elements

View File

@@ -5,9 +5,19 @@
#include "flatbuffers/flatbuffers.h"
namespace MyGame {
namespace OtherNameSpace {
struct Unused;
} // namespace OtherNameSpace
} // namespace MyGame
namespace MyGame {
namespace Example {
struct Test;
struct Vec3;
struct Monster;
enum {
Color_Red = 1,
Color_Green = 2,
@@ -33,11 +43,7 @@ inline const char **EnumNamesAny() {
inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; }
bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type);
struct Test;
struct Vec3;
struct Monster;
bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type);
MANUALLY_ALIGNED_STRUCT(2) Test {
private:
@@ -47,7 +53,7 @@ MANUALLY_ALIGNED_STRUCT(2) Test {
public:
Test(int16_t a, int8_t b)
: a_(flatbuffers::EndianScalar(a)), b_(flatbuffers::EndianScalar(b)), __padding0(0) {}
: a_(flatbuffers::EndianScalar(a)), b_(flatbuffers::EndianScalar(b)), __padding0(0) { (void)__padding0; }
int16_t a() const { return flatbuffers::EndianScalar(a_); }
int8_t b() const { return flatbuffers::EndianScalar(b_); }
@@ -68,7 +74,7 @@ MANUALLY_ALIGNED_STRUCT(16) Vec3 {
public:
Vec3(float x, float y, float z, double test1, int8_t test2, const Test &test3)
: x_(flatbuffers::EndianScalar(x)), y_(flatbuffers::EndianScalar(y)), z_(flatbuffers::EndianScalar(z)), __padding0(0), test1_(flatbuffers::EndianScalar(test1)), test2_(flatbuffers::EndianScalar(test2)), __padding1(0), test3_(test3), __padding2(0) {}
: x_(flatbuffers::EndianScalar(x)), y_(flatbuffers::EndianScalar(y)), z_(flatbuffers::EndianScalar(z)), __padding0(0), test1_(flatbuffers::EndianScalar(test1)), test2_(flatbuffers::EndianScalar(test2)), __padding1(0), test3_(test3), __padding2(0) { (void)__padding0; (void)__padding1; (void)__padding2; }
float x() const { return flatbuffers::EndianScalar(x_); }
float y() const { return flatbuffers::EndianScalar(y_); }
@@ -96,8 +102,8 @@ struct Monster : private flatbuffers::Table {
const flatbuffers::Vector<uint8_t> *testnestedflatbuffer() const { return GetPointer<const flatbuffers::Vector<uint8_t> *>(30); }
const Monster *testnestedflatbuffer_nested_root() { return flatbuffers::GetRoot<Monster>(testnestedflatbuffer()->Data()); }
const Monster *testempty() const { return GetPointer<const Monster *>(32); }
bool Verify(const flatbuffers::Verifier &verifier) const {
return VerifyTable(verifier) &&
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<Vec3>(verifier, 4 /* pos */) &&
VerifyField<int16_t>(verifier, 6 /* mana */) &&
VerifyField<int16_t>(verifier, 8 /* hp */) &&
@@ -122,7 +128,8 @@ struct Monster : private flatbuffers::Table {
VerifyField<flatbuffers::uoffset_t>(verifier, 30 /* testnestedflatbuffer */) &&
verifier.Verify(testnestedflatbuffer()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 32 /* testempty */) &&
verifier.VerifyTable(testempty());
verifier.VerifyTable(testempty()) &&
verifier.EndTable();
}
};
@@ -181,7 +188,7 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder
return builder_.Finish();
}
bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type) {
bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type) {
switch (type) {
case Any_NONE: return true;
case Any_Monster: return verifier.VerifyTable(reinterpret_cast<const Monster *>(union_obj));
@@ -191,7 +198,7 @@ bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uin
inline const Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<Monster>(buf); }
inline bool VerifyMonsterBuffer(const flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<Monster>(); }
inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<Monster>(); }
inline void FinishMonsterBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset<Monster> root) { fbb.Finish(root, "MONS"); }

View File

@@ -192,8 +192,8 @@ void ParseAndGenerateTextTest() {
// parse schema first, so we can use it to parse the data after
flatbuffers::Parser parser;
TEST_EQ(parser.Parse(schemafile.c_str()), true);
TEST_EQ(parser.Parse(jsonfile.c_str()), true);
TEST_EQ(parser.Parse(schemafile.c_str(), "tests/"), true);
TEST_EQ(parser.Parse(jsonfile.c_str(), "tests/"), true);
// here, parser.builder_ contains a binary buffer that is the parsed data.
@@ -406,12 +406,12 @@ void FuzzTest2() {
// Parse the schema, parse the generated data, then generate text back
// from the binary and compare against the original.
TEST_EQ(parser.Parse(schema.c_str()), true);
TEST_EQ(parser.Parse(schema.c_str(), ""), true);
const std::string &json =
definitions[num_definitions - 1].instances[0] + "\n";
TEST_EQ(parser.Parse(json.c_str()), true);
TEST_EQ(parser.Parse(json.c_str(), ""), true);
std::string jsongen;
flatbuffers::GeneratorOptions opts;
@@ -443,7 +443,7 @@ void FuzzTest2() {
// Test that parser errors are actually generated.
void TestError(const char *src, const char *error_substr) {
flatbuffers::Parser parser;
TEST_EQ(parser.Parse(src), false); // Must signal error
TEST_EQ(parser.Parse(src, ""), false); // Must signal error
// Must be the error we're expecting
TEST_NOTNULL(strstr(parser.error_.c_str(), error_substr));
}
@@ -488,6 +488,9 @@ void ErrorTest() {
TestError("struct X { Y:int; } root_type X;", "a table");
TestError("union X { Y }", "referenced");
TestError("union Z { X } struct X { Y:int; }", "only tables");
TestError("table X { Y:[int]; YLength:int; }", "clash");
TestError("table X { Y:string = 1; }", "scalar");
TestError("table X { Y:byte; } root_type X; { Y:1, Y:2 }", "already set");
}
// Additional parser testing not covered elsewhere.
@@ -495,10 +498,10 @@ void ScientificTest() {
flatbuffers::Parser parser;
// Simple schema.
TEST_EQ(parser.Parse("table X { Y:float; } root_type X;"), true);
TEST_EQ(parser.Parse("table X { Y:float; } root_type X;", ""), true);
// Test scientific notation numbers.
TEST_EQ(parser.Parse("{ Y:0.0314159e+2 }"), true);
TEST_EQ(parser.Parse("{ Y:0.0314159e+2 }", ""), true);
auto root = flatbuffers::GetRoot<float>(parser.builder_.GetBufferPointer());
// root will point to the table, which is a 32bit vtable offset followed
// by a float:
@@ -509,14 +512,26 @@ void EnumStringsTest() {
flatbuffers::Parser parser1;
TEST_EQ(parser1.Parse("enum E:byte { A, B, C } table T { F:[E]; }"
"root_type T;"
"{ F:[ A, B, \"C\", \"A B C\" ] }"), true);
"{ F:[ A, B, \"C\", \"A B C\" ] }", ""), true);
flatbuffers::Parser parser2;
TEST_EQ(parser2.Parse("enum E:byte { A, B, C } table T { F:[int]; }"
"root_type T;"
"{ F:[ \"E.C\", \"E.A E.B E.C\" ] }"), true);
"{ F:[ \"E.C\", \"E.A E.B E.C\" ] }", ""), true);
}
void UnicodeTest() {
flatbuffers::Parser parser;
TEST_EQ(parser.Parse("table T { F:string; }"
"root_type T;"
"{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
"\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\" }", ""), true);
std::string jsongen;
flatbuffers::GeneratorOptions opts;
opts.indent_step = -1;
GenerateText(parser, parser.builder_.GetBufferPointer(), opts, &jsongen);
TEST_EQ(jsongen == "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
"\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\"}", true);
}
int main(int /*argc*/, const char * /*argv*/[]) {
// Run our various test suites:
@@ -534,6 +549,7 @@ int main(int /*argc*/, const char * /*argv*/[]) {
ErrorTest();
ScientificTest();
EnumStringsTest();
UnicodeTest();
if (!testing_fails) {
TEST_OUTPUT_LINE("ALL TESTS PASSED");