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 snapshot.sh
tests/go_gen tests/go_gen
CMakeLists.txt.user 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}) # source_group(Tests FILES ${FlatBuffers_Tests_SRCS})
if(APPLE) 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") 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() endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Werror -Wextra")
if(FLATBUFFERS_CODE_COVERAGE) if(FLATBUFFERS_CODE_COVERAGE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fprofile-arcs -ftest-coverage") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fprofile-arcs -ftest-coverage")
set(CMAKE_EXE_LINKER_FLAGS 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 class="title">Formal Grammar of the schema language </div> </div>
</div><!--header--> </div><!--header-->
<div class="contents"> <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>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>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> <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]; } inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; }
</pre><p>Unions share a lot with enums. </p><pre class="fragment">struct Vec3; </pre><p>Unions share a lot with enums. </p><pre class="fragment">struct Vec3;
struct Monster; 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: private:
float x_; float x_;
float y_; 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> <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> <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> <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> <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> <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> <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>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> <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> </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> <h2>Gotchas</h2>
<h3>Schemas and version control</h3> <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> <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). - `-S` : Generate strict JSON (field names are enclosed in quotes).
By default, no quotes are generated. 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 and since it may cause the buffer to be brought into cache before
reading, the actual overhead may be even lower than expected. 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 ## Text & schema parsing
Using binary buffers with the generated header provides a super low Using binary buffers with the generated header provides a super low

View File

@@ -1,6 +1,9 @@
# Formal Grammar of the schema language # 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 )* `;` namespace\_decl = `namespace` ident ( `.` ident )* `;`

View File

@@ -159,7 +159,8 @@ Unions share a lot with enums.
struct Vec3; struct Vec3;
struct Monster; 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 { MANUALLY_ALIGNED_STRUCT(4) Vec3 {
private: 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 / code, and packages in Java. You can use `.` to specify nested namespaces /
packages. 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 ### Root type
This declares what you consider to be the root table (or struct) of the 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. separated by spaces to OR them, e.g.
`field: "EnumVal1 EnumVal2"` or `field: "Enum.EnumVal1 Enum.EnumVal2"`. `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 ## Gotchas
### Schemas and version control ### 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 // Helper class to verify the integrity of a FlatBuffer
class Verifier { class Verifier {
public: public:
Verifier(const uint8_t *buf, size_t buf_len) Verifier(const uint8_t *buf, size_t buf_len, size_t _max_depth = 64,
: buf_(buf), end_(buf + buf_len) 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. // Verify any range within the buffer.
@@ -688,7 +690,7 @@ class Verifier {
} }
// Verify a pointer (may be NULL) of a table type. // 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); return !table || table->Verify(*this);
} }
@@ -733,8 +735,7 @@ class Verifier {
} }
// Special case for table contents, after the above has been called. // Special case for table contents, after the above has been called.
template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec) template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec) {
const {
if (vec) { if (vec) {
for (uoffset_t i = 0; i < vec->Length(); i++) { for (uoffset_t i = 0; i < vec->Length(); i++) {
if (!vec->Get(i)->Verify(*this)) return false; if (!vec->Get(i)->Verify(*this)) return false;
@@ -744,16 +745,40 @@ class Verifier {
} }
// Verify this whole buffer, starting with root type T. // 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. // Call T::Verify, which must be in the generated code for this type.
return Verify<uoffset_t>(buf_) && return Verify<uoffset_t>(buf_) &&
reinterpret_cast<const T *>(buf_ + ReadScalar<uoffset_t>(buf_))-> reinterpret_cast<const T *>(buf_ + ReadScalar<uoffset_t>(buf_))->
Verify(*this); 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: private:
const uint8_t *buf_; const uint8_t *buf_;
const uint8_t *end_; 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 // "structs" are flat structures that do not have an offset table, thus
@@ -828,12 +853,13 @@ class Table {
// Verify the vtable of this table. // Verify the vtable of this table.
// Call this once per table, followed by VerifyField once per field. // 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. // Check the vtable offset.
if (!verifier.Verify<soffset_t>(data_)) return false; if (!verifier.Verify<soffset_t>(data_)) return false;
auto vtable = data_ - ReadScalar<soffset_t>(data_); auto vtable = data_ - ReadScalar<soffset_t>(data_);
// Check the vtable size field, then check vtable fits in its entirety. // 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)); verifier.Verify(vtable, ReadScalar<voffset_t>(vtable));
} }

View File

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

View File

@@ -22,16 +22,14 @@
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <stdlib.h> #include <stdlib.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <sys/stat.h>
#endif
namespace flatbuffers { 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. // Convert an integer or floating point value to a string.
// In contrast to std::stringstream, "char" values are // In contrast to std::stringstream, "char" values are
// converted to a string of digits. // 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. // Convert an integer value to a hexadecimal string.
// The returned string length is the number of nibbles in // The returned string length is always xdigits long, prefixed by 0 digits.
// the supplied value prefixed by 0 digits. For example, // For example, IntToStringHex(0x23, 8) returns the string "00000023".
// IntToStringHex(static_cast<int>(0x23)) returns the inline std::string IntToStringHex(int i, int xdigits) {
// string "00000023".
template<typename T> std::string IntToStringHex(T i) {
std::stringstream ss; std::stringstream ss;
ss << std::setw(sizeof(T) * 2) ss << std::setw(xdigits)
<< std::setfill('0') << std::setfill('0')
<< std::hex << std::hex
<< std::uppercase << std::uppercase
@@ -66,11 +62,11 @@ template<typename T> std::string IntToStringHex(T i) {
} }
// Portable implementation of strtoull(). // Portable implementation of strtoull().
inline int64_t StringToInt(const char *str) { inline int64_t StringToInt(const char *str, int base = 10) {
#ifdef _MSC_VER #ifdef _MSC_VER
return _strtoui64(str, nullptr, 10); return _strtoui64(str, nullptr, base);
#else #else
return strtoull(str, nullptr, 10); return strtoull(str, nullptr, base);
#endif #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); 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 } // namespace flatbuffers
#endif // FLATBUFFERS_UTIL_H_ #endif // FLATBUFFERS_UTIL_H_

View File

@@ -30,7 +30,7 @@ public class FlatBufferBuilder {
int space; // Remaining space in the ByteBuffer. int space; // Remaining space in the ByteBuffer.
static final Charset utf8charset = Charset.forName("UTF-8"); static final Charset utf8charset = Charset.forName("UTF-8");
int minalign = 1; // Minimum alignment encountered so far. 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 object_start; // Starting offset of the current struct/table.
int[] vtables = new int[16]; // List of offsets of all vtables. int[] vtables = new int[16]; // List of offsets of all vtables.
int num_vtables = 0; // Number of entries in `vtables` in use. int num_vtables = 0; // Number of entries in `vtables` in use.
@@ -47,6 +47,14 @@ public class FlatBufferBuilder {
bb = newByteBuffer(new byte[initial_size]); 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 newByteBuffer(byte[] buf) {
ByteBuffer newbb = ByteBuffer.wrap(buf); ByteBuffer newbb = ByteBuffer.wrap(buf);
newbb.order(ByteOrder.LITTLE_ENDIAN); 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. // Create a java String from UTF-8 data stored inside the flatbuffer.
protected String __string(int offset) { protected String __string(int offset) {
offset += bb.getInt(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. // 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" #include "flatbuffers/flatbuffers.h"
namespace MyGame { namespace MyGame {
namespace Sample { namespace Sample {
struct Vec3;
struct Monster;
enum { enum {
Color_Red = 0, Color_Red = 0,
Color_Green = 1, Color_Green = 1,
@@ -33,10 +37,7 @@ inline const char **EnumNamesAny() {
inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; } inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; }
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);
struct Vec3;
struct Monster;
MANUALLY_ALIGNED_STRUCT(4) Vec3 { MANUALLY_ALIGNED_STRUCT(4) Vec3 {
private: private:
@@ -46,7 +47,7 @@ MANUALLY_ALIGNED_STRUCT(4) Vec3 {
public: public:
Vec3(float x, float y, float z) 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 x() const { return flatbuffers::EndianScalar(x_); }
float y() const { return flatbuffers::EndianScalar(y_); } 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::String *name() const { return GetPointer<const flatbuffers::String *>(10); }
const flatbuffers::Vector<uint8_t> *inventory() const { return GetPointer<const flatbuffers::Vector<uint8_t> *>(14); } 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); } int8_t color() const { return GetField<int8_t>(16, 2); }
bool Verify(const flatbuffers::Verifier &verifier) const { bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTable(verifier) && return VerifyTableStart(verifier) &&
VerifyField<Vec3>(verifier, 4 /* pos */) && VerifyField<Vec3>(verifier, 4 /* pos */) &&
VerifyField<int16_t>(verifier, 6 /* mana */) && VerifyField<int16_t>(verifier, 6 /* mana */) &&
VerifyField<int16_t>(verifier, 8 /* hp */) && VerifyField<int16_t>(verifier, 8 /* hp */) &&
@@ -70,7 +71,8 @@ struct Monster : private flatbuffers::Table {
verifier.Verify(name()) && verifier.Verify(name()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 14 /* inventory */) && VerifyField<flatbuffers::uoffset_t>(verifier, 14 /* inventory */) &&
verifier.Verify(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(); 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) { switch (type) {
case Any_NONE: return true; case Any_NONE: return true;
case Any_Monster: return verifier.VerifyTable(reinterpret_cast<const Monster *>(union_obj)); 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 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 Sample
} // namespace MyGame } // namespace MyGame

View File

@@ -54,9 +54,11 @@ int main(int /*argc*/, const char * /*argv*/[]) {
auto pos = monster->pos(); auto pos = monster->pos();
assert(pos); assert(pos);
assert(pos->z() == 3); assert(pos->z() == 3);
(void)pos;
auto inv = monster->inventory(); auto inv = monster->inventory();
assert(inv); assert(inv);
assert(inv->Get(9) == 9); 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 // parse schema first, so we can use it to parse the data after
flatbuffers::Parser parser; flatbuffers::Parser parser;
ok = parser.Parse(schemafile.c_str()) && ok = parser.Parse(schemafile.c_str(), "samples/") &&
parser.Parse(jsonfile.c_str()); parser.Parse(jsonfile.c_str(), "samples/");
assert(ok); assert(ok);
// here, parser.builder_ contains a binary buffer that is the parsed data. // 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(" -%s %s.\n", generators[i].extension, generators[i].help);
printf(" -o PATH Prefix PATH to all generated files.\n" printf(" -o PATH Prefix PATH to all generated files.\n"
" -S Strict JSON: add quotes to field names.\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 may depend on declarations in earlier files.\n"
"FILEs after the -- must be binary flatbuffer format files.\n" "FILEs after the -- must be binary flatbuffer format files.\n"
"Output files are named using the base file name of the input," "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); 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[]) { int main(int argc, const char *argv[]) {
program_name = argv[0]; program_name = argv[0];
flatbuffers::Parser parser; flatbuffers::Parser parser;
@@ -145,6 +130,9 @@ int main(int argc, const char *argv[]) {
case 'S': case 'S':
opts.strict_json = true; opts.strict_json = true;
break; break;
case 'P':
opts.prefixed_enums = false;
break;
case '-': // Separator between text and binary input files. case '-': // Separator between text and binary input files.
binary_files_from = filenames.size(); binary_files_from = filenames.size();
break; break;
@@ -187,14 +175,16 @@ int main(int argc, const char *argv[]) {
reinterpret_cast<const uint8_t *>(contents.c_str()), reinterpret_cast<const uint8_t *>(contents.c_str()),
contents.length()); contents.length());
} else { } else {
if (!parser.Parse(contents.c_str())) if (!parser.Parse(contents.c_str(), file_it->c_str()))
Error(parser.error_.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) { for (size_t i = 0; i < num_generators; ++i) {
if (generator_enabled[i]) { if (generator_enabled[i]) {
flatbuffers::EnsureDirExists(output_path);
if (!generators[i].generate(parser, output_path, filebase, opts)) { if (!generators[i].generate(parser, output_path, filebase, opts)) {
Error((std::string("Unable to generate ") + Error((std::string("Unable to generate ") +
generators[i].name + generators[i].name +
@@ -204,17 +194,9 @@ int main(int argc, const char *argv[]) {
} }
} }
// Since the Parser object retains definitions across files, we must // We do not want to generate code for the definitions in this file
// ensure we only output code for these once, in the file they are first // in any files coming up next.
// declared: parser.MarkGenerated();
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;
}
} }
return 0; return 0;

View File

@@ -33,18 +33,31 @@ static std::string GenTypeBasic(const Type &type) {
return ctypename[type.base_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, // Return a C++ pointer type, specialized to the actual struct/table types,
// and vector element 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) { switch (type.base_type) {
case BASE_TYPE_STRING: case BASE_TYPE_STRING:
return "flatbuffers::String"; return "flatbuffers::String";
case BASE_TYPE_VECTOR: case BASE_TYPE_VECTOR:
return "flatbuffers::Vector<" + GenTypeWire(type.VectorType(), "") + ">"; return "flatbuffers::Vector<" +
case BASE_TYPE_STRUCT: GenTypeWire(parser, type.VectorType(), "") + ">";
return type.struct_def->name; 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: case BASE_TYPE_UNION:
// fall through // fall through
default: default:
@@ -54,31 +67,33 @@ static std::string GenTypePointer(const Type &type) {
// Return a C++ type for any type (scalar/pointer) specifically for // Return a C++ type for any type (scalar/pointer) specifically for
// building a flatbuffer. // 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) return IsScalar(type.base_type)
? GenTypeBasic(type) + postfix ? GenTypeBasic(type) + postfix
: IsStruct(type) : IsStruct(type)
? "const " + GenTypePointer(type) + " *" ? "const " + GenTypePointer(parser, type) + " *"
: "flatbuffers::Offset<" + GenTypePointer(type) + ">" + postfix; : "flatbuffers::Offset<" + GenTypePointer(parser, type) + ">" + postfix;
} }
// Return a C++ type for any type (scalar/pointer) that reflects its // Return a C++ type for any type (scalar/pointer) that reflects its
// serialized size. // serialized size.
static std::string GenTypeSize(const Type &type) { static std::string GenTypeSize(const Parser &parser, const Type &type) {
return IsScalar(type.base_type) return IsScalar(type.base_type)
? GenTypeBasic(type) ? GenTypeBasic(type)
: IsStruct(type) : IsStruct(type)
? GenTypePointer(type) ? GenTypePointer(parser, type)
: "flatbuffers::uoffset_t"; : "flatbuffers::uoffset_t";
} }
// Return a C++ type for any type (scalar/pointer) specifically for // Return a C++ type for any type (scalar/pointer) specifically for
// using a flatbuffer. // using a flatbuffer.
static std::string GenTypeGet(const Type &type, const char *afterbasic, static std::string GenTypeGet(const Parser &parser, const Type &type,
const char *beforeptr, const char *afterptr) { const char *afterbasic, const char *beforeptr,
const char *afterptr) {
return IsScalar(type.base_type) return IsScalar(type.base_type)
? GenTypeBasic(type) + afterbasic ? GenTypeBasic(type) + afterbasic
: beforeptr + GenTypePointer(type) + afterptr; : beforeptr + GenTypePointer(parser, type) + afterptr;
} }
// Generate a documentation comment, if available. // 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. // Generate an enum declaration and an enum string lookup table.
static void GenEnum(EnumDef &enum_def, std::string *code_ptr, static void GenEnum(EnumDef &enum_def, std::string *code_ptr,
std::string *code_ptr_post) { std::string *code_ptr_post,
const GeneratorOptions &opts) {
if (enum_def.generated) return; if (enum_def.generated) return;
std::string &code = *code_ptr; std::string &code = *code_ptr;
std::string &code_post = *code_ptr_post; std::string &code_post = *code_ptr_post;
@@ -104,7 +126,7 @@ static void GenComment(const std::string &dc,
++it) { ++it) {
auto &ev = **it; auto &ev = **it;
GenComment(ev.doc_comment, code_ptr, " "); GenComment(ev.doc_comment, code_ptr, " ");
code += " " + enum_def.name + "_" + ev.name + " = "; code += " " + GenEnumVal(enum_def, ev, opts) + " = ";
code += NumToString(ev.value); code += NumToString(ev.value);
code += (it + 1) != enum_def.vals.vec.end() ? ",\n" : "\n"; 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 += "inline const char *EnumName" + enum_def.name;
code += "(int e) { return EnumNames" + enum_def.name + "()[e"; code += "(int e) { return EnumNames" + enum_def.name + "()[e";
if (enum_def.vals.vec.front()->value) 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"; 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 // has been corrupted, since the verifiers will simply fail when called
// on the wrong type. // on the wrong type.
auto signature = "bool Verify" + enum_def.name + auto signature = "bool Verify" + enum_def.name +
"(const flatbuffers::Verifier &verifier, " + "(flatbuffers::Verifier &verifier, " +
"const void *union_obj, uint8_t type)"; "const void *union_obj, uint8_t type)";
code += signature + ";\n\n"; code += signature + ";\n\n";
code_post += signature + " {\n switch (type) {\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 != enum_def.vals.vec.end();
++it) { ++it) {
auto &ev = **it; auto &ev = **it;
code_post += " case " + enum_def.name + "_" + ev.name; code_post += " case " + GenEnumVal(enum_def, ev, opts);
if (!ev.value) { if (!ev.value) {
code_post += ": return true;\n"; // "NONE" enum value. code_post += ": return true;\n"; // "NONE" enum value.
} else { } else {
@@ -181,13 +203,13 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
auto &field = **it; auto &field = **it;
if (!field.deprecated) { // Deprecated fields won't be accessible. if (!field.deprecated) { // Deprecated fields won't be accessible.
GenComment(field.doc_comment, code_ptr, " "); GenComment(field.doc_comment, code_ptr, " ");
code += " " + GenTypeGet(field.value.type, " ", "const ", " *"); code += " " + GenTypeGet(parser, field.value.type, " ", "const ", " *");
code += field.name + "() const { return "; code += field.name + "() const { return ";
// Call a different accessor for pointers, that indirects. // Call a different accessor for pointers, that indirects.
code += IsScalar(field.value.type.base_type) code += IsScalar(field.value.type.base_type)
? "GetField<" ? "GetField<"
: (IsStruct(field.value.type) ? "GetStruct<" : "GetPointer<"); : (IsStruct(field.value.type) ? "GetStruct<" : "GetPointer<");
code += GenTypeGet(field.value.type, "", "const ", " *") + ">("; code += GenTypeGet(parser, field.value.type, "", "const ", " *") + ">(";
code += NumToString(field.value.offset); code += NumToString(field.value.offset);
// Default value as second arg for non-pointer types. // Default value as second arg for non-pointer types.
if (IsScalar(field.value.type.base_type)) 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 // Generate a verifier function that can check a buffer from an untrusted
// source will never cause reads outside the buffer. // source will never cause reads outside the buffer.
code += " bool Verify(const flatbuffers::Verifier &verifier) const {\n"; code += " bool Verify(flatbuffers::Verifier &verifier) const {\n";
code += " return VerifyTable(verifier)"; code += " return VerifyTableStart(verifier)";
std::string prefix = " &&\n "; std::string prefix = " &&\n ";
for (auto it = struct_def.fields.vec.begin(); for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); it != struct_def.fields.vec.end();
++it) { ++it) {
auto &field = **it; auto &field = **it;
if (!field.deprecated) { 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 += ">(verifier, " + NumToString(field.value.offset);
code += " /* " + field.name + " */)"; code += " /* " + field.name + " */)";
switch (field.value.type.base_type) { 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";
code += "};\n\n"; code += "};\n\n";
@@ -268,9 +291,10 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
auto &field = **it; auto &field = **it;
if (!field.deprecated) { if (!field.deprecated) {
code += " void add_" + field.name + "("; 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)) 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)) else if (IsStruct(field.value.type))
code += "Struct"; code += "Struct";
else else
@@ -301,8 +325,8 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
++it) { ++it) {
auto &field = **it; auto &field = **it;
if (!field.deprecated) { if (!field.deprecated) {
code += ",\n " + GenTypeWire(field.value.type, " ") + field.name; code += ",\n " + GenTypeWire(parser, field.value.type, " ");
code += " = " + field.value.constant; code += field.name + " = " + field.value.constant;
} }
} }
code += ") {\n " + struct_def.name + "Builder builder_(_fbb);\n"; 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"; 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. // 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; if (struct_def.generated) return;
std::string &code = *code_ptr; 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 != struct_def.fields.vec.end();
++it) { ++it) {
auto &field = **it; auto &field = **it;
code += " " + GenTypeGet(field.value.type, " ", "", " "); code += " " + GenTypeGet(parser, field.value.type, " ", "", " ");
code += field.name + "_;\n"; code += field.name + "_;\n";
if (field.padding) { GenPadding(field, [&code, &padding_id](int bits) {
for (int i = 0; i < 4; i++) code += " int" + NumToString(bits) +
if (static_cast<int>(field.padding) & (1 << i)) "_t __padding" + NumToString(padding_id++) + ";\n";
code += " int" + NumToString((1 << i) * 8) + });
"_t __padding" + NumToString(padding_id++) + ";\n";
assert(!(field.padding & ~0xF));
}
} }
// Generate a constructor that takes all fields as arguments. // Generate a constructor that takes all fields as arguments.
@@ -359,7 +390,8 @@ static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
++it) { ++it) {
auto &field = **it; auto &field = **it;
if (it != struct_def.fields.vec.begin()) code += ", "; 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 : "; code += ")\n : ";
padding_id = 0; padding_id = 0;
@@ -373,10 +405,23 @@ static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
code += "flatbuffers::EndianScalar(" + field.name + "))"; code += "flatbuffers::EndianScalar(" + field.name + "))";
else else
code += field.name + ")"; code += field.name + ")";
if (field.padding) GenPadding(field, [&code, &padding_id](int bits) {
(void)bits;
code += ", __padding" + NumToString(padding_id++) + "(0)"; 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: // Generate accessor methods of the form:
// type name() const { return flatbuffers::EndianScalar(name_); } // type name() const { return flatbuffers::EndianScalar(name_); }
@@ -385,7 +430,7 @@ static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
++it) { ++it) {
auto &field = **it; auto &field = **it;
GenComment(field.doc_comment, code_ptr, " "); GenComment(field.doc_comment, code_ptr, " ");
code += " " + GenTypeGet(field.value.type, " ", "const ", " &"); code += " " + GenTypeGet(parser, field.value.type, " ", "const ", " &");
code += field.name + "() const { return "; code += field.name + "() const { return ";
if (IsScalar(field.value.type.base_type)) if (IsScalar(field.value.type.base_type))
code += "flatbuffers::EndianScalar(" + field.name + "_)"; 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"; 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 } // namespace cpp
// Iterate through all definitions we haven't generate code for (enums, structs, // Iterate through all definitions we haven't generate code for (enums, structs,
// and tables) and output them to a single file. // 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; using namespace cpp;
// Generate code for all the enum declarations. // Generate code for all the enum declarations.
std::string enum_code, enum_code_post; std::string enum_code, enum_code_post;
for (auto it = parser.enums_.vec.begin(); for (auto it = parser.enums_.vec.begin();
it != parser.enums_.vec.end(); ++it) { 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 // Generate forward declarations for all structs/tables, since they may
// have circular references. // 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(); for (auto it = parser.structs_.vec.begin();
it != parser.structs_.vec.end(); ++it) { it != parser.structs_.vec.end(); ++it) {
if (!(*it)->generated) auto &struct_def = **it;
forward_decl_code += "struct " + (*it)->name + ";\n"; 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. // Generate code for all structs, then all tables.
std::string decl_code; std::string decl_code;
for (auto it = parser.structs_.vec.begin(); for (auto it = parser.structs_.vec.begin();
it != parser.structs_.vec.end(); ++it) { 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(); for (auto it = parser.structs_.vec.begin();
it != parser.structs_.vec.end(); ++it) { 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. // 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; std::string code;
code = "// automatically generated by the FlatBuffers compiler," code = "// automatically generated by the FlatBuffers compiler,"
" do not modify\n\n"; " do not modify\n\n";
@@ -440,8 +524,9 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
// Generate include guard. // Generate include guard.
std::string include_guard = "FLATBUFFERS_GENERATED_" + include_guard_ident; std::string include_guard = "FLATBUFFERS_GENERATED_" + include_guard_ident;
include_guard += "_"; include_guard += "_";
for (auto it = parser.name_space_.begin(); auto name_space = parser.namespaces_.back();
it != parser.name_space_.end(); ++it) { for (auto it = name_space->components.begin();
it != name_space->components.end(); ++it) {
include_guard += *it + "_"; include_guard += *it + "_";
} }
include_guard += "H_"; 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"; code += "#include \"flatbuffers/flatbuffers.h\"\n\n";
// Generate nested namespaces. code += forward_decl_code_other_namespace;
for (auto it = parser.name_space_.begin(); code += "\n";
it != parser.name_space_.end(); ++it) {
code += "namespace " + *it + " {\n"; GenerateNestedNameSpaces(name_space, &code);
} code += "\n";
code += forward_decl_code_same_namespace;
code += "\n";
// Output the main declaration code from above. // Output the main declaration code from above.
code += "\n";
code += enum_code; code += enum_code;
code += forward_decl_code;
code += "\n";
code += decl_code; code += decl_code;
code += enum_code_post; code += enum_code_post;
@@ -477,7 +562,7 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
// The root verifier: // The root verifier:
code += "inline bool Verify"; code += "inline bool Verify";
code += parser.root_struct_def->name; code += parser.root_struct_def->name;
code += "Buffer(const flatbuffers::Verifier &verifier) { " code += "Buffer(flatbuffers::Verifier &verifier) { "
"return verifier.VerifyBuffer<"; "return verifier.VerifyBuffer<";
code += parser.root_struct_def->name + ">(); }\n\n"; 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. CloseNestedNameSpaces(name_space, &code);
for (auto it = parser.name_space_.rbegin();
it != parser.name_space_.rend(); ++it) {
code += "} // namespace " + *it + "\n";
}
// Close the include guard. // Close the include guard.
code += "\n#endif // " + include_guard + "\n"; 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, bool GenerateCPP(const Parser &parser,
const std::string &path, const std::string &path,
const std::string &file_name, const std::string &file_name,
const GeneratorOptions & /*opts*/) { const GeneratorOptions &opts) {
auto code = GenerateCPP(parser, file_name); auto code = GenerateCPP(parser, file_name, opts);
return !code.length() || return !code.length() ||
SaveFile((path + file_name + "_generated.h").c_str(), code, false); 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) { bool needs_imports) {
if (!classcode.length()) return true; if (!classcode.length()) return true;
std::string name_space_name; std::string namespace_name;
std::string name_space_dir = path; std::string namespace_dir = path;
for (auto it = parser.name_space_.begin(); auto &namespaces = parser.namespaces_.back()->components;
it != parser.name_space_.end(); ++it) { for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
if (name_space_name.length()) { if (namespace_name.length()) {
name_space_name += "."; namespace_name += ".";
name_space_dir += PATH_SEPARATOR; namespace_dir += PATH_SEPARATOR;
} }
name_space_name = *it; namespace_name = *it;
name_space_dir += *it; namespace_dir += *it;
mkdir(name_space_dir.c_str(), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); mkdir(namespace_dir.c_str(), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
} }
std::string code = ""; std::string code = "";
BeginFile(name_space_name, needs_imports, &code); BeginFile(namespace_name, needs_imports, &code);
code += classcode; 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); return SaveFile(filename.c_str(), code, false);
} }

View File

@@ -20,13 +20,6 @@
#include "flatbuffers/idl.h" #include "flatbuffers/idl.h"
#include "flatbuffers/util.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 flatbuffers {
namespace java { namespace java {
@@ -161,9 +154,7 @@ static void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
} }
} }
static void GenStruct(StructDef &struct_def, static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
std::string *code_ptr,
StructDef *root_struct_def) {
if (struct_def.generated) return; if (struct_def.generated) return;
std::string &code = *code_ptr; std::string &code = *code_ptr;
@@ -177,9 +168,9 @@ static void GenStruct(StructDef &struct_def,
code += "public class " + struct_def.name + " extends "; code += "public class " + struct_def.name + " extends ";
code += struct_def.fixed ? "Struct" : "Table"; code += struct_def.fixed ? "Struct" : "Table";
code += " {\n"; code += " {\n";
if (&struct_def == root_struct_def) { if (!struct_def.fixed) {
// Generate a special accessor for the table that has been declared as // Generate a special accessor for the table that when used as the root
// the root type. // of a FlatBuffer
code += " public static " + struct_def.name + " getRootAs"; code += " public static " + struct_def.name + " getRootAs";
code += struct_def.name; code += struct_def.name;
code += "(ByteBuffer _bb, int offset) { "; code += "(ByteBuffer _bb, int offset) { ";
@@ -341,27 +332,27 @@ static bool SaveClass(const Parser &parser, const Definition &def,
bool needs_imports) { bool needs_imports) {
if (!classcode.length()) return true; if (!classcode.length()) return true;
std::string name_space_java; std::string namespace_java;
std::string name_space_dir = path; std::string namespace_dir = path;
for (auto it = parser.name_space_.begin(); auto &namespaces = parser.namespaces_.back()->components;
it != parser.name_space_.end(); ++it) { for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
if (name_space_java.length()) { if (namespace_java.length()) {
name_space_java += "."; namespace_java += ".";
name_space_dir += kPathSeparator; namespace_dir += kPathSeparator;
} }
name_space_java += *it; namespace_java += *it;
name_space_dir += *it; namespace_dir += *it;
mkdir(name_space_dir.c_str(), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
} }
EnsureDirExists(namespace_dir);
std::string code = "// automatically generated, do not modify\n\n"; 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) { if (needs_imports) {
code += "import java.nio.*;\nimport java.lang.*;\nimport java.util.*;\n"; code += "import java.nio.*;\nimport java.lang.*;\nimport java.util.*;\n";
code += "import flatbuffers.*;\n\n"; code += "import flatbuffers.*;\n\n";
} }
code += classcode; 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); return SaveFile(filename.c_str(), code, false);
} }
@@ -384,7 +375,7 @@ bool GenerateJava(const Parser &parser,
for (auto it = parser.structs_.vec.begin(); for (auto it = parser.structs_.vec.begin();
it != parser.structs_.vec.end(); ++it) { it != parser.structs_.vec.end(); ++it) {
std::string declcode; std::string declcode;
GenStruct(**it, &declcode, parser.root_struct_def); GenStruct(**it, &declcode);
if (!SaveClass(parser, **it, declcode, path, true)) if (!SaveClass(parser, **it, declcode, path, true))
return false; 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 // If indentation is less than 0, that indicates we don't want any newlines
// either. // either.
const char *NewLine(int indent_step) { const char *NewLine(const GeneratorOptions &opts) {
return indent_step >= 0 ? "\n" : ""; 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. // 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) {
std::string &text = *_text; std::string &text = *_text;
text += "["; text += "[";
text += NewLine(opts.indent_step); text += NewLine(opts);
for (uoffset_t i = 0; i < v.Length(); i++) { for (uoffset_t i = 0; i < v.Length(); i++) {
if (i) { if (i) {
text += ","; text += ",";
text += NewLine(opts.indent_step); text += NewLine(opts);
} }
text.append(indent + opts.indent_step, ' '); text.append(indent + Indent(opts), ' ');
if (IsStruct(type)) if (IsStruct(type))
Print(v.GetStructFromOffset(i * type.struct_def->bytesize), type, Print(v.GetStructFromOffset(i * type.struct_def->bytesize), type,
indent + opts.indent_step, nullptr, opts, _text); indent + Indent(opts), nullptr, opts, _text);
else else
Print(v.Get(i), type, indent + opts.indent_step, nullptr, Print(v.Get(i), type, indent + Indent(opts), nullptr,
opts, _text); opts, _text);
} }
text += NewLine(opts.indent_step); text += NewLine(opts);
text.append(indent, ' '); text.append(indent, ' ');
text += "]"; text += "]";
} }
@@ -93,15 +97,29 @@ static void EscapeString(const String &s, std::string *_text) {
case '\n': text += "\\n"; break; case '\n': text += "\\n"; break;
case '\t': text += "\\t"; break; case '\t': text += "\\t"; break;
case '\r': text += "\\r"; break; case '\r': text += "\\r"; break;
case '\b': text += "\\b"; break;
case '\f': text += "\\f"; break;
case '\"': text += "\\\""; break; case '\"': text += "\\\""; break;
case '\\': text += "\\\\"; break; case '\\': text += "\\\\"; break;
default: default:
if (c >= ' ' && c <= '~') { if (c >= ' ' && c <= '~') {
text += c; text += c;
} else { } else {
auto u = static_cast<unsigned char>(c); // Not printable ASCII data. Let's see if it's valid UTF-8 first:
text += "\\x"; const char *utf8 = s.c_str() + i;
text += IntToStringHex(u); 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; break;
} }
@@ -202,15 +220,15 @@ static void GenStruct(const StructDef &struct_def, const Table *table,
if (fieldout++) { if (fieldout++) {
text += ","; text += ",";
} }
text += NewLine(opts.indent_step); text += NewLine(opts);
text.append(indent + opts.indent_step, ' '); text.append(indent + Indent(opts), ' ');
OutputIdentifier(fd.name, opts, _text); OutputIdentifier(fd.name, opts, _text);
text += ": "; text += ": ";
switch (fd.value.type.base_type) { switch (fd.value.type.base_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) \ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) \
case BASE_TYPE_ ## ENUM: \ case BASE_TYPE_ ## ENUM: \
GenField<CTYPE>(fd, table, struct_def.fixed, \ GenField<CTYPE>(fd, table, struct_def.fixed, \
opts, indent + opts.indent_step, _text); \ opts, indent + Indent(opts), _text); \
break; break;
FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD) FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD #undef FLATBUFFERS_TD
@@ -219,7 +237,7 @@ static void GenStruct(const StructDef &struct_def, const Table *table,
case BASE_TYPE_ ## ENUM: case BASE_TYPE_ ## ENUM:
FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD) FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
#undef 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); union_sd, opts, _text);
break; 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.append(indent, ' ');
text += "}"; text += "}";
} }
@@ -247,7 +265,7 @@ void GenerateText(const Parser &parser, const void *flatbuffer,
0, 0,
opts, opts,
_text); _text);
text += NewLine(opts.indent_step); text += NewLine(opts);
} }
} // namespace flatbuffers } // namespace flatbuffers

View File

@@ -83,7 +83,8 @@ template<> inline Offset<void> atot<Offset<void>>(const char *s) {
TD(NameSpace, 265, "namespace") \ TD(NameSpace, 265, "namespace") \
TD(RootType, 266, "root_type") \ TD(RootType, 266, "root_type") \
TD(FileIdentifier, 267, "file_identifier") \ TD(FileIdentifier, 267, "file_identifier") \
TD(FileExtension, 268, "file_extension") TD(FileExtension, 268, "file_extension") \
TD(Include, 269, "include")
#ifdef __GNUC__ #ifdef __GNUC__
__extension__ // Stop GCC complaining about trailing comma with -Wpendantic. __extension__ // Stop GCC complaining about trailing comma with -Wpendantic.
#endif #endif
@@ -107,13 +108,24 @@ static std::string TokenToString(int t) {
}; };
if (t < 256) { // A single ascii char token. if (t < 256) { // A single ascii char token.
std::string s; std::string s;
s.append(1, t); s.append(1, static_cast<char>(t));
return s; return s;
} else { // Other tokens. } else { // Other tokens.
return tokens[t - 256]; 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() { void Parser::Next() {
doc_comment_.clear(); doc_comment_.clear();
bool seen_newline = false; bool seen_newline = false;
@@ -141,8 +153,21 @@ void Parser::Next() {
case 'n': attribute_ += '\n'; cursor_++; break; case 'n': attribute_ += '\n'; cursor_++; break;
case 't': attribute_ += '\t'; cursor_++; break; case 't': attribute_ += '\t'; cursor_++; break;
case 'r': attribute_ += '\r'; 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 '\\': 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; default: Error("unknown escape code in string constant"); break;
} }
} else { // printable chars + UTF-8 bytes } else { // printable chars + UTF-8 bytes
@@ -196,6 +221,7 @@ void Parser::Next() {
if (attribute_ == "union") { token_ = kTokenUnion; return; } if (attribute_ == "union") { token_ = kTokenUnion; return; }
if (attribute_ == "namespace") { token_ = kTokenNameSpace; return; } if (attribute_ == "namespace") { token_ = kTokenNameSpace; return; }
if (attribute_ == "root_type") { token_ = kTokenRootType; return; } if (attribute_ == "root_type") { token_ = kTokenRootType; return; }
if (attribute_ == "include") { token_ = kTokenInclude; return; }
if (attribute_ == "file_identifier") { if (attribute_ == "file_identifier") {
token_ = kTokenFileIdentifier; token_ = kTokenFileIdentifier;
return; return;
@@ -338,6 +364,8 @@ void Parser::ParseField(StructDef &struct_def) {
if (token_ == '=') { if (token_ == '=') {
Next(); Next();
if (!IsScalar(type.base_type))
Error("default values currently only supported for scalars");
ParseSingleValue(field.value); ParseSingleValue(field.value);
} }
@@ -429,6 +457,10 @@ uoffset_t Parser::ParseTable(const StructDef &struct_def) {
|| struct_def.fields.vec[fieldn] != field)) { || struct_def.fields.vec[fieldn] != field)) {
Error("struct field appearing out of order: " + name); 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(':'); Expect(':');
Value val = field->value; Value val = field->value;
ParseAnyValue(val, field); ParseAnyValue(val, field);
@@ -645,6 +677,7 @@ StructDef *Parser::LookupCreateStruct(const std::string &name) {
structs_.Add(name, struct_def); structs_.Add(name, struct_def);
struct_def->name = name; struct_def->name = name;
struct_def->predecl = true; struct_def->predecl = true;
struct_def->defined_namespace = namespaces_.back();
} }
return struct_def; return struct_def;
} }
@@ -698,7 +731,7 @@ void Parser::ParseEnum(bool is_union) {
if (prevsize && enum_def.vals.vec[prevsize - 1]->value >= ev.value) if (prevsize && enum_def.vals.vec[prevsize - 1]->value >= ev.value)
Error("enum values must be specified in ascending order"); Error("enum values must be specified in ascending order");
} }
} while (IsNext(',')); } while (IsNext(',') && token_ != '}');
Expect('}'); Expect('}');
if (enum_def.attributes.Lookup("bit_flags")) { if (enum_def.attributes.Lookup("bit_flags")) {
for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); 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('}'); Expect('}');
} }
@@ -781,19 +839,66 @@ bool Parser::SetRootType(const char *name) {
return root_struct_def != nullptr; 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; source_ = cursor_ = source;
line_ = 1; line_ = 1;
error_.clear(); error_.clear();
builder_.Clear(); builder_.Clear();
try { try {
Next(); 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) { while (token_ != kTokenEof) {
if (token_ == kTokenNameSpace) { if (token_ == kTokenNameSpace) {
Next(); Next();
name_space_.clear(); auto ns = new Namespace();
namespaces_.push_back(ns);
for (;;) { for (;;) {
name_space_.push_back(attribute_); ns->components.push_back(attribute_);
Expect(kTokenIdentifier); Expect(kTokenIdentifier);
if (!IsNext('.')) break; if (!IsNext('.')) break;
} }
@@ -832,6 +937,8 @@ bool Parser::Parse(const char *source) {
file_extension_ = attribute_; file_extension_ = attribute_;
Expect(kTokenStringConstant); Expect(kTokenStringConstant);
Expect(';'); Expect(';');
} else if(token_ == kTokenInclude) {
Error("includes must come before declarations");
} else { } else {
ParseDecl(); ParseDecl();
} }

View File

@@ -103,9 +103,12 @@ class JavaTest {
} }
// Test it: // Test it:
TestBuffer(fbb.dataBuffer(), fbb.dataStart()); 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"); 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 // example IDL file
include "include_test1.fbs";
namespace MyGame.Example; 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 union Any { Monster } // TODO: add more elements

View File

@@ -5,9 +5,19 @@
#include "flatbuffers/flatbuffers.h" #include "flatbuffers/flatbuffers.h"
namespace MyGame {
namespace OtherNameSpace {
struct Unused;
} // namespace OtherNameSpace
} // namespace MyGame
namespace MyGame { namespace MyGame {
namespace Example { namespace Example {
struct Test;
struct Vec3;
struct Monster;
enum { enum {
Color_Red = 1, Color_Red = 1,
Color_Green = 2, Color_Green = 2,
@@ -33,11 +43,7 @@ inline const char **EnumNamesAny() {
inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; } inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; }
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);
struct Test;
struct Vec3;
struct Monster;
MANUALLY_ALIGNED_STRUCT(2) Test { MANUALLY_ALIGNED_STRUCT(2) Test {
private: private:
@@ -47,7 +53,7 @@ MANUALLY_ALIGNED_STRUCT(2) Test {
public: public:
Test(int16_t a, int8_t b) 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_); } int16_t a() const { return flatbuffers::EndianScalar(a_); }
int8_t b() const { return flatbuffers::EndianScalar(b_); } int8_t b() const { return flatbuffers::EndianScalar(b_); }
@@ -68,7 +74,7 @@ MANUALLY_ALIGNED_STRUCT(16) Vec3 {
public: public:
Vec3(float x, float y, float z, double test1, int8_t test2, const Test &test3) 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 x() const { return flatbuffers::EndianScalar(x_); }
float y() const { return flatbuffers::EndianScalar(y_); } 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 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 *testnestedflatbuffer_nested_root() { return flatbuffers::GetRoot<Monster>(testnestedflatbuffer()->Data()); }
const Monster *testempty() const { return GetPointer<const Monster *>(32); } const Monster *testempty() const { return GetPointer<const Monster *>(32); }
bool Verify(const flatbuffers::Verifier &verifier) const { bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTable(verifier) && return VerifyTableStart(verifier) &&
VerifyField<Vec3>(verifier, 4 /* pos */) && VerifyField<Vec3>(verifier, 4 /* pos */) &&
VerifyField<int16_t>(verifier, 6 /* mana */) && VerifyField<int16_t>(verifier, 6 /* mana */) &&
VerifyField<int16_t>(verifier, 8 /* hp */) && VerifyField<int16_t>(verifier, 8 /* hp */) &&
@@ -122,7 +128,8 @@ struct Monster : private flatbuffers::Table {
VerifyField<flatbuffers::uoffset_t>(verifier, 30 /* testnestedflatbuffer */) && VerifyField<flatbuffers::uoffset_t>(verifier, 30 /* testnestedflatbuffer */) &&
verifier.Verify(testnestedflatbuffer()) && verifier.Verify(testnestedflatbuffer()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 32 /* testempty */) && 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(); 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) { switch (type) {
case Any_NONE: return true; case Any_NONE: return true;
case Any_Monster: return verifier.VerifyTable(reinterpret_cast<const Monster *>(union_obj)); 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 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"); } 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 // parse schema first, so we can use it to parse the data after
flatbuffers::Parser parser; flatbuffers::Parser parser;
TEST_EQ(parser.Parse(schemafile.c_str()), true); TEST_EQ(parser.Parse(schemafile.c_str(), "tests/"), true);
TEST_EQ(parser.Parse(jsonfile.c_str()), true); TEST_EQ(parser.Parse(jsonfile.c_str(), "tests/"), true);
// here, parser.builder_ contains a binary buffer that is the parsed data. // 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 // Parse the schema, parse the generated data, then generate text back
// from the binary and compare against the original. // 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 = const std::string &json =
definitions[num_definitions - 1].instances[0] + "\n"; 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; std::string jsongen;
flatbuffers::GeneratorOptions opts; flatbuffers::GeneratorOptions opts;
@@ -443,7 +443,7 @@ void FuzzTest2() {
// Test that parser errors are actually generated. // Test that parser errors are actually generated.
void TestError(const char *src, const char *error_substr) { void TestError(const char *src, const char *error_substr) {
flatbuffers::Parser parser; 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 // Must be the error we're expecting
TEST_NOTNULL(strstr(parser.error_.c_str(), error_substr)); 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("struct X { Y:int; } root_type X;", "a table");
TestError("union X { Y }", "referenced"); TestError("union X { Y }", "referenced");
TestError("union Z { X } struct X { Y:int; }", "only tables"); 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. // Additional parser testing not covered elsewhere.
@@ -495,10 +498,10 @@ void ScientificTest() {
flatbuffers::Parser parser; flatbuffers::Parser parser;
// Simple schema. // 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 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()); auto root = flatbuffers::GetRoot<float>(parser.builder_.GetBufferPointer());
// root will point to the table, which is a 32bit vtable offset followed // root will point to the table, which is a 32bit vtable offset followed
// by a float: // by a float:
@@ -509,14 +512,26 @@ void EnumStringsTest() {
flatbuffers::Parser parser1; flatbuffers::Parser parser1;
TEST_EQ(parser1.Parse("enum E:byte { A, B, C } table T { F:[E]; }" TEST_EQ(parser1.Parse("enum E:byte { A, B, C } table T { F:[E]; }"
"root_type T;" "root_type T;"
"{ F:[ A, B, \"C\", \"A B C\" ] }"), true); "{ F:[ A, B, \"C\", \"A B C\" ] }", ""), true);
flatbuffers::Parser parser2; flatbuffers::Parser parser2;
TEST_EQ(parser2.Parse("enum E:byte { A, B, C } table T { F:[int]; }" TEST_EQ(parser2.Parse("enum E:byte { A, B, C } table T { F:[int]; }"
"root_type T;" "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*/[]) { int main(int /*argc*/, const char * /*argv*/[]) {
// Run our various test suites: // Run our various test suites:
@@ -534,6 +549,7 @@ int main(int /*argc*/, const char * /*argv*/[]) {
ErrorTest(); ErrorTest();
ScientificTest(); ScientificTest();
EnumStringsTest(); EnumStringsTest();
UnicodeTest();
if (!testing_fails) { if (!testing_fails) {
TEST_OUTPUT_LINE("ALL TESTS PASSED"); TEST_OUTPUT_LINE("ALL TESTS PASSED");