mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-10 15:16:28 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea57dfe897 | ||
|
|
9f506f57c0 | ||
|
|
57b614587c | ||
|
|
15dc1a86cd | ||
|
|
766d0df797 | ||
|
|
4507594812 | ||
|
|
8e40902d52 | ||
|
|
bc5fa9d52f | ||
|
|
11b743688c | ||
|
|
ffb3dec573 | ||
|
|
51ba48ae40 | ||
|
|
c553b6b950 | ||
|
|
541b06759f | ||
|
|
30af866e5a | ||
|
|
ebac1e1940 | ||
|
|
f7b0d130b6 | ||
|
|
c2ba7fd251 | ||
|
|
be894f09df | ||
|
|
293a8110c4 | ||
|
|
8c5d7f7dea |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -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/**
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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_;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 )* `;`
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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_
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
5
tests/include_test1.fbs
Normal 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
9
tests/include_test2.fbs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
include "include_test2.fbs"; // should be skipped
|
||||||
|
|
||||||
|
namespace MyGame.OtherNameSpace;
|
||||||
|
|
||||||
|
enum FromInclude:long { IncludeVal }
|
||||||
|
|
||||||
|
struct Unused {}
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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"); }
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user