aboutsummaryrefslogtreecommitdiff
path: root/flatcc/test
diff options
context:
space:
mode:
Diffstat (limited to 'flatcc/test')
-rw-r--r--flatcc/test/CMakeLists.txt27
-rw-r--r--flatcc/test/README.md19
-rw-r--r--flatcc/test/benchmark/README.md68
-rwxr-xr-xflatcc/test/benchmark/benchall.sh16
-rw-r--r--flatcc/test/benchmark/benchflatc/benchflatc.cpp70
-rw-r--r--flatcc/test/benchmark/benchflatc/flatbench_generated.h166
-rw-r--r--flatcc/test/benchmark/benchflatc/flatbuffers/flatbuffers.h1189
-rwxr-xr-xflatcc/test/benchmark/benchflatc/run.sh23
-rw-r--r--flatcc/test/benchmark/benchflatcc/benchflatcc.c98
-rwxr-xr-xflatcc/test/benchmark/benchflatcc/run.sh24
-rw-r--r--flatcc/test/benchmark/benchflatccjson/benchflatccjson.c182
-rwxr-xr-xflatcc/test/benchmark/benchflatccjson/run.sh23
-rw-r--r--flatcc/test/benchmark/benchmain/benchmain.h66
-rw-r--r--flatcc/test/benchmark/benchout-osx.txt169
-rw-r--r--flatcc/test/benchmark/benchout-ubuntu.txt169
-rw-r--r--flatcc/test/benchmark/benchraw/benchraw.c117
-rwxr-xr-xflatcc/test/benchmark/benchraw/run.sh21
-rw-r--r--flatcc/test/benchmark/schema/flatbench.fbs37
-rw-r--r--flatcc/test/cgen_test/CMakeLists.txt43
-rw-r--r--flatcc/test/cgen_test/cgen_test.c163
-rwxr-xr-xflatcc/test/cgen_test/cgen_test.sh22
-rwxr-xr-xflatcc/test/debug.sh8
-rw-r--r--flatcc/test/emit_test/CMakeLists.txt20
-rw-r--r--flatcc/test/emit_test/emit_test.c137
-rw-r--r--flatcc/test/emit_test/emit_test.fbs6
-rwxr-xr-xflatcc/test/emit_test/emit_test.sh19
-rw-r--r--flatcc/test/flatc_compat/.gitattributes2
-rw-r--r--flatcc/test/flatc_compat/CMakeLists.txt21
-rw-r--r--flatcc/test/flatc_compat/README.md10
-rw-r--r--flatcc/test/flatc_compat/flatc_compat.c226
-rwxr-xr-xflatcc/test/flatc_compat/flatc_compat.sh20
-rw-r--r--flatcc/test/flatc_compat/monsterdata_test.golden48
-rwxr-xr-xflatcc/test/flatc_compat/monsterdata_test.json51
-rw-r--r--flatcc/test/flatc_compat/monsterdata_test.monbin0 -> 336 bytes
-rw-r--r--flatcc/test/json_test/CMakeLists.txt64
-rw-r--r--flatcc/test/json_test/flatcc_golden.c45
-rwxr-xr-xflatcc/test/json_test/json_test.sh74
-rw-r--r--flatcc/test/json_test/test_basic_parse.c291
-rw-r--r--flatcc/test/json_test/test_json.c882
-rw-r--r--flatcc/test/json_test/test_json_parser.c164
-rw-r--r--flatcc/test/json_test/test_json_printer.c129
-rwxr-xr-xflatcc/test/leakcheck-full.sh9
-rwxr-xr-xflatcc/test/leakcheck.sh9
-rw-r--r--flatcc/test/load_test/CMakeLists.txt20
-rw-r--r--flatcc/test/load_test/load_test.c164
-rwxr-xr-xflatcc/test/load_test/load_test.sh22
-rw-r--r--flatcc/test/monster_test/CMakeLists.txt20
-rw-r--r--flatcc/test/monster_test/attributes.fbs6
-rw-r--r--flatcc/test/monster_test/include_test1.fbs5
-rw-r--r--flatcc/test/monster_test/include_test2.fbs11
-rw-r--r--flatcc/test/monster_test/monster_test.c2919
-rwxr-xr-xflatcc/test/monster_test/monster_test.fbs365
-rw-r--r--flatcc/test/monster_test_concat/CMakeLists.txt21
-rw-r--r--flatcc/test/monster_test_concat/README.txt2
-rw-r--r--flatcc/test/monster_test_concat/monster_test_concat.c24
-rw-r--r--flatcc/test/monster_test_cpp/CMakeLists.txt24
-rw-r--r--flatcc/test/monster_test_cpp/monster_test.cpp3
-rw-r--r--flatcc/test/monster_test_prefix/CMakeLists.txt20
-rw-r--r--flatcc/test/monster_test_prefix/monster_test_prefix.c24
-rw-r--r--flatcc/test/monster_test_solo/CMakeLists.txt21
-rw-r--r--flatcc/test/monster_test_solo/monster_test_solo.c24
-rw-r--r--flatcc/test/optional_scalars_test/CMakeLists.txt19
-rw-r--r--flatcc/test/optional_scalars_test/optional_scalars_test.c280
-rw-r--r--flatcc/test/optional_scalars_test/optional_scalars_test.fbs71
-rw-r--r--flatcc/test/reflection_test/CMakeLists.txt20
-rw-r--r--flatcc/test/reflection_test/reflection_test.c196
-rwxr-xr-xflatcc/test/reflection_test/reflection_test.sh24
-rwxr-xr-xflatcc/test/test.sh103
-rw-r--r--flatcc/test/union_vector_test/union_vector.fbs26
69 files changed, 9381 insertions, 0 deletions
diff --git a/flatcc/test/CMakeLists.txt b/flatcc/test/CMakeLists.txt
new file mode 100644
index 0000000..59a6132
--- /dev/null
+++ b/flatcc/test/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Note: some files under source control may be tested with binary comparison.
+# Under git such files are protected with the `.gitattributes` file.
+# Incorrect line endings may lead to failed tests.
+if (FLATCC_TEST)
+if (FLATCC_CXX_TEST)
+ # This is tests is primarly for making sure C++ users can use
+ # generated FlatCC code. It fails for pre GCC 4.7 C++ because both
+ # stdint.h and stdalign.h are not sufficiently supported and it
+ # is not worth attempting to support in flatcc/portable.
+ add_subdirectory(monster_test_cpp)
+endif()
+add_subdirectory(cgen_test)
+add_subdirectory(monster_test)
+add_subdirectory(monster_test_solo)
+add_subdirectory(monster_test_concat)
+add_subdirectory(monster_test_prefix)
+add_subdirectory(flatc_compat)
+add_subdirectory(json_test)
+add_subdirectory(emit_test)
+add_subdirectory(load_test)
+add_subdirectory(optional_scalars_test)
+# Reflection can break during development, so it is necessary
+# to disable until new reflection code generates cleanly.
+if (FLATCC_REFLECTION)
+ add_subdirectory(reflection_test)
+endif()
+endif()
diff --git a/flatcc/test/README.md b/flatcc/test/README.md
new file mode 100644
index 0000000..4c0b485
--- /dev/null
+++ b/flatcc/test/README.md
@@ -0,0 +1,19 @@
+NOTE: shell scripts driven by flatcc/test/test.sh have been ported to CMake.
+use flatcc/scripts/test.sh to drive CMake tests.
+
+Run `leakcheck.sh` and `leakcheck-full.sh` for memory checks.
+
+To install valgrind on OS-X Yosemite use `brew install --HEAD valgrind`
+
+For decoding valgrind error messages:
+<http://derickrethans.nl/valgrind-null.html>
+
+clang has built-in memory check, but only for `x86_64 Linux`:
+<http://clang.llvm.org/docs/LeakSanitizer.html>
+
+On OS-X Yosemite with valgrind that isn't officially supported for that
+platform, a few spurious unitialized memory access errors are reported
+when printing the filextension in `codegen_c.c`. and in the equivalent
+builder. After inspection, nothing suggests this is an actual bug - more
+likely it relates to a strnlen optimization in fprintf `"%.*s"` syntax
+that valgrind doesn't catch.
diff --git a/flatcc/test/benchmark/README.md b/flatcc/test/benchmark/README.md
new file mode 100644
index 0000000..c98dbbb
--- /dev/null
+++ b/flatcc/test/benchmark/README.md
@@ -0,0 +1,68 @@
+# FlatBench
+
+This is based on the Google FlatBuffer benchmark schema, but the
+benchmark itself is independent and not directly comparable, although
+roughly the same operations are being executed.
+
+The `benchflatc` folder contains C++ headers and code generated by Googles
+`flatc` compiler while `benchflatcc` contains material from this project.
+
+The `benchraw` folder contains structs similar to those used in Googles
+benchmark, but again the benchmark isn't directly comparable.
+
+It should be noted that allocation strategies differ. The C++ builder
+is constructed on each build iteration whereas theh C version resets
+instead. The benchmark is designed such that the C++ version could do
+the same if the builder supports it.
+
+## Execution
+
+Build and run each benchmark individually:
+
+ benchmark/benchflatc/run.sh
+ benchmark/benchflatcc/run.sh
+ benchmark/benchraw/run.sh
+ benchmark/benchflatccjson/run.sh
+
+Note that each benchmark runs in both debug and optimized versions!
+
+
+# Environment
+
+The the benchmark are designed for a `*nix environmen.
+
+- A C compiler named `cc` supporting -std=c11 is required for flatcc.
+- A C++ compiler named `c++` supporting -std=c++11 is requried for
+ flatc.
+- A C compiler named `cc` supporting <stdint.h> is required for raw benchmark.
+- Test is driven by a shell script.
+
+The time measurements in `elapsed.h` ought to work with Windows, but it
+has not been tested. The tests could be compiled for Windows with a
+separate set of `.bat` files that adapt to the relevant compiler settings
+(not provided).
+
+
+## Output
+
+The source and generated files and compiled binaries are placed in a
+dedicated folder under:
+
+ build/tmp/test/benchmark/
+
+Only flatcc includes files from the containing project - other
+benchmarks copy any relevant files into place.
+
+The optimized flatc C++ benchmark is 24K vs flatcc for C using 35K.
+
+
+## JSON numeric conversion
+
+The Json printer benchmark is significantly impacted by floating point
+conversion performance. By using the grisu3 algorithm instead of the
+printing speed more than doubles compared to sprintf "%.17g" method with
+clang glibc. The parsing, on the other hand, parsing slows down
+slightly because floats are always printed as double which increases the
+json text from 700 to 722 bytes. For comparision, RapidJSON also only
+supports double precision because the JSON spec does not specifically
+mention preicision.
diff --git a/flatcc/test/benchmark/benchall.sh b/flatcc/test/benchmark/benchall.sh
new file mode 100755
index 0000000..87d6983
--- /dev/null
+++ b/flatcc/test/benchmark/benchall.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+set -e
+
+cd `dirname $0`
+
+echo "running all benchmarks (raw, flatc C++, flatcc C)"
+
+echo "building and benchmarking raw structs"
+benchraw/run.sh
+echo "building and benchmarking flatc generated C++"
+benchflatc/run.sh
+echo "building and benchmarking flatcc generated C"
+benchflatcc/run.sh
+echo "building and benchmarking flatcc json generated C"
+benchflatccjson/run.sh
diff --git a/flatcc/test/benchmark/benchflatc/benchflatc.cpp b/flatcc/test/benchmark/benchflatc/benchflatc.cpp
new file mode 100644
index 0000000..ae24abd
--- /dev/null
+++ b/flatcc/test/benchmark/benchflatc/benchflatc.cpp
@@ -0,0 +1,70 @@
+#define BENCH_TITLE "flatc for C++"
+
+#define BENCHMARK_BUFSIZ 1000
+#define DECLARE_BENCHMARK(BM)\
+ void *BM = 0
+#define CLEAR_BENCHMARK(BM)
+
+#include <string.h>
+#include "flatbench_generated.h"
+
+using namespace flatbuffers;
+using namespace benchfb;
+
+/* The builder is created each time - perhaps fbb can be reused somehow? */
+int encode(void *bench, void *buffer, size_t *size)
+{
+ const int veclen = 3;
+ Offset<FooBar> vec[veclen];
+ FlatBufferBuilder fbb;
+
+ (void)bench;
+
+ for (int i = 0; i < veclen; i++) {
+ // We add + i to not make these identical copies for a more realistic
+ // compression test.
+ auto const &foo = Foo(0xABADCAFEABADCAFE + i, 10000 + i, '@' + i, 1000000 + i);
+ auto const &bar = Bar(foo, 123456 + i, 3.14159f + i, 10000 + i);
+ auto name = fbb.CreateString("Hello, World!");
+ auto foobar = CreateFooBar(fbb, &bar, name, 3.1415432432445543543 + i, '!' + i);
+ vec[i] = foobar;
+ }
+ auto location = fbb.CreateString("https://www.example.com/myurl/");
+ auto foobarvec = fbb.CreateVector(vec, veclen);
+ auto foobarcontainer = CreateFooBarContainer(fbb, foobarvec, true, Enum_Bananas, location);
+ fbb.Finish(foobarcontainer);
+ if (*size < fbb.GetSize()) {
+ return -1;
+ }
+ *size = fbb.GetSize();
+ memcpy(buffer, fbb.GetBufferPointer(), *size);
+ return 0;
+}
+
+int64_t decode(void *bench, void *buffer, size_t size, int64_t sum)
+{
+ auto foobarcontainer = GetFooBarContainer(buffer);
+
+ (void)bench;
+ sum += foobarcontainer->initialized();
+ sum += foobarcontainer->location()->Length();
+ sum += foobarcontainer->fruit();
+ for (unsigned int i = 0; i < foobarcontainer->list()->Length(); i++) {
+ auto foobar = foobarcontainer->list()->Get(i);
+ sum += foobar->name()->Length();
+ sum += foobar->postfix();
+ sum += static_cast<int64_t>(foobar->rating());
+ auto bar = foobar->sibling();
+ sum += static_cast<int64_t>(bar->ratio());
+ sum += bar->size();
+ sum += bar->time();
+ auto &foo = bar->parent();
+ sum += foo.count();
+ sum += foo.id();
+ sum += foo.length();
+ sum += foo.prefix();
+ }
+ return sum + 2 * sum;
+}
+
+#include "benchmain.h"
diff --git a/flatcc/test/benchmark/benchflatc/flatbench_generated.h b/flatcc/test/benchmark/benchflatc/flatbench_generated.h
new file mode 100644
index 0000000..0b2abc5
--- /dev/null
+++ b/flatcc/test/benchmark/benchflatc/flatbench_generated.h
@@ -0,0 +1,166 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+#ifndef FLATBUFFERS_GENERATED_FLATBENCH_BENCHFB_H_
+#define FLATBUFFERS_GENERATED_FLATBENCH_BENCHFB_H_
+
+#include "flatbuffers/flatbuffers.h"
+
+
+namespace benchfb {
+
+struct Foo;
+struct Bar;
+struct FooBar;
+struct FooBarContainer;
+
+enum Enum {
+ Enum_Apples = 0,
+ Enum_Pears = 1,
+ Enum_Bananas = 2
+};
+
+inline const char **EnumNamesEnum() {
+ static const char *names[] = { "Apples", "Pears", "Bananas", nullptr };
+ return names;
+}
+
+inline const char *EnumNameEnum(Enum e) { return EnumNamesEnum()[static_cast<int>(e)]; }
+
+MANUALLY_ALIGNED_STRUCT(8) Foo FLATBUFFERS_FINAL_CLASS {
+ private:
+ uint64_t id_;
+ int16_t count_;
+ int8_t prefix_;
+ int8_t __padding0;
+ uint32_t length_;
+
+ public:
+ Foo(uint64_t id, int16_t count, int8_t prefix, uint32_t length)
+ : id_(flatbuffers::EndianScalar(id)), count_(flatbuffers::EndianScalar(count)), prefix_(flatbuffers::EndianScalar(prefix)), __padding0(0), length_(flatbuffers::EndianScalar(length)) { (void)__padding0; }
+
+ uint64_t id() const { return flatbuffers::EndianScalar(id_); }
+ int16_t count() const { return flatbuffers::EndianScalar(count_); }
+ int8_t prefix() const { return flatbuffers::EndianScalar(prefix_); }
+ uint32_t length() const { return flatbuffers::EndianScalar(length_); }
+};
+STRUCT_END(Foo, 16);
+
+MANUALLY_ALIGNED_STRUCT(8) Bar FLATBUFFERS_FINAL_CLASS {
+ private:
+ Foo parent_;
+ int32_t time_;
+ float ratio_;
+ uint16_t size_;
+ int16_t __padding0;
+ int32_t __padding1;
+
+ public:
+ Bar(const Foo &parent, int32_t time, float ratio, uint16_t size)
+ : parent_(parent), time_(flatbuffers::EndianScalar(time)), ratio_(flatbuffers::EndianScalar(ratio)), size_(flatbuffers::EndianScalar(size)), __padding0(0), __padding1(0) { (void)__padding0; (void)__padding1; }
+
+ const Foo &parent() const { return parent_; }
+ int32_t time() const { return flatbuffers::EndianScalar(time_); }
+ float ratio() const { return flatbuffers::EndianScalar(ratio_); }
+ uint16_t size() const { return flatbuffers::EndianScalar(size_); }
+};
+STRUCT_END(Bar, 32);
+
+struct FooBar FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ const Bar *sibling() const { return GetStruct<const Bar *>(4); }
+ const flatbuffers::String *name() const { return GetPointer<const flatbuffers::String *>(6); }
+ double rating() const { return GetField<double>(8, 0); }
+ uint8_t postfix() const { return GetField<uint8_t>(10, 0); }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<Bar>(verifier, 4 /* sibling */) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, 6 /* name */) &&
+ verifier.Verify(name()) &&
+ VerifyField<double>(verifier, 8 /* rating */) &&
+ VerifyField<uint8_t>(verifier, 10 /* postfix */) &&
+ verifier.EndTable();
+ }
+};
+
+struct FooBarBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_sibling(const Bar *sibling) { fbb_.AddStruct(4, sibling); }
+ void add_name(flatbuffers::Offset<flatbuffers::String> name) { fbb_.AddOffset(6, name); }
+ void add_rating(double rating) { fbb_.AddElement<double>(8, rating, 0); }
+ void add_postfix(uint8_t postfix) { fbb_.AddElement<uint8_t>(10, postfix, 0); }
+ FooBarBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
+ FooBarBuilder &operator=(const FooBarBuilder &);
+ flatbuffers::Offset<FooBar> Finish() {
+ auto o = flatbuffers::Offset<FooBar>(fbb_.EndTable(start_, 4));
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<FooBar> CreateFooBar(flatbuffers::FlatBufferBuilder &_fbb,
+ const Bar *sibling = 0,
+ flatbuffers::Offset<flatbuffers::String> name = 0,
+ double rating = 0,
+ uint8_t postfix = 0) {
+ FooBarBuilder builder_(_fbb);
+ builder_.add_rating(rating);
+ builder_.add_name(name);
+ builder_.add_sibling(sibling);
+ builder_.add_postfix(postfix);
+ return builder_.Finish();
+}
+
+struct FooBarContainer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ const flatbuffers::Vector<flatbuffers::Offset<FooBar>> *list() const { return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<FooBar>> *>(4); }
+ uint8_t initialized() const { return GetField<uint8_t>(6, 0); }
+ Enum fruit() const { return static_cast<Enum>(GetField<int16_t>(8, 0)); }
+ const flatbuffers::String *location() const { return GetPointer<const flatbuffers::String *>(10); }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, 4 /* list */) &&
+ verifier.Verify(list()) &&
+ verifier.VerifyVectorOfTables(list()) &&
+ VerifyField<uint8_t>(verifier, 6 /* initialized */) &&
+ VerifyField<int16_t>(verifier, 8 /* fruit */) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, 10 /* location */) &&
+ verifier.Verify(location()) &&
+ verifier.EndTable();
+ }
+};
+
+struct FooBarContainerBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_list(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<FooBar>>> list) { fbb_.AddOffset(4, list); }
+ void add_initialized(uint8_t initialized) { fbb_.AddElement<uint8_t>(6, initialized, 0); }
+ void add_fruit(Enum fruit) { fbb_.AddElement<int16_t>(8, static_cast<int16_t>(fruit), 0); }
+ void add_location(flatbuffers::Offset<flatbuffers::String> location) { fbb_.AddOffset(10, location); }
+ FooBarContainerBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
+ FooBarContainerBuilder &operator=(const FooBarContainerBuilder &);
+ flatbuffers::Offset<FooBarContainer> Finish() {
+ auto o = flatbuffers::Offset<FooBarContainer>(fbb_.EndTable(start_, 4));
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<FooBarContainer> CreateFooBarContainer(flatbuffers::FlatBufferBuilder &_fbb,
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<FooBar>>> list = 0,
+ uint8_t initialized = 0,
+ Enum fruit = Enum_Apples,
+ flatbuffers::Offset<flatbuffers::String> location = 0) {
+ FooBarContainerBuilder builder_(_fbb);
+ builder_.add_location(location);
+ builder_.add_list(list);
+ builder_.add_fruit(fruit);
+ builder_.add_initialized(initialized);
+ return builder_.Finish();
+}
+
+inline const benchfb::FooBarContainer *GetFooBarContainer(const void *buf) { return flatbuffers::GetRoot<benchfb::FooBarContainer>(buf); }
+
+inline bool VerifyFooBarContainerBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<benchfb::FooBarContainer>(); }
+
+inline void FinishFooBarContainerBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset<benchfb::FooBarContainer> root) { fbb.Finish(root); }
+
+} // namespace benchfb
+
+#endif // FLATBUFFERS_GENERATED_FLATBENCH_BENCHFB_H_
diff --git a/flatcc/test/benchmark/benchflatc/flatbuffers/flatbuffers.h b/flatcc/test/benchmark/benchflatc/flatbuffers/flatbuffers.h
new file mode 100644
index 0000000..3482cbe
--- /dev/null
+++ b/flatcc/test/benchmark/benchflatc/flatbuffers/flatbuffers.h
@@ -0,0 +1,1189 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLATBUFFERS_H_
+#define FLATBUFFERS_H_
+
+#include <assert.h>
+
+#include <cstdint>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <type_traits>
+#include <vector>
+#include <algorithm>
+#include <functional>
+#include <memory>
+
+#if __cplusplus <= 199711L && \
+ (!defined(_MSC_VER) || _MSC_VER < 1600) && \
+ (!defined(__GNUC__) || \
+ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40603))
+ #error A C++11 compatible compiler is required for FlatBuffers.
+ #error __cplusplus _MSC_VER __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__
+#endif
+
+// The wire format uses a little endian encoding (since that's efficient for
+// the common platforms).
+#if !defined(FLATBUFFERS_LITTLEENDIAN)
+ #if defined(__GNUC__) || defined(__clang__)
+ #ifdef __BIG_ENDIAN__
+ #define FLATBUFFERS_LITTLEENDIAN 0
+ #else
+ #define FLATBUFFERS_LITTLEENDIAN 1
+ #endif // __BIG_ENDIAN__
+ #elif defined(_MSC_VER)
+ #if defined(_M_PPC)
+ #define FLATBUFFERS_LITTLEENDIAN 0
+ #else
+ #define FLATBUFFERS_LITTLEENDIAN 1
+ #endif
+ #else
+ #error Unable to determine endianness, define FLATBUFFERS_LITTLEENDIAN.
+ #endif
+#endif // !defined(FLATBUFFERS_LITTLEENDIAN)
+
+#define FLATBUFFERS_VERSION_MAJOR 1
+#define FLATBUFFERS_VERSION_MINOR 0
+#define FLATBUFFERS_VERSION_REVISION 0
+#define FLATBUFFERS_STRING_EXPAND(X) #X
+#define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X)
+
+#if (!defined(_MSC_VER) || _MSC_VER > 1600) && \
+ (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 407))
+ #define FLATBUFFERS_FINAL_CLASS final
+#else
+ #define FLATBUFFERS_FINAL_CLASS
+#endif
+
+namespace flatbuffers {
+
+// Our default offset / size type, 32bit on purpose on 64bit systems.
+// Also, using a consistent offset type maintains compatibility of serialized
+// offset values between 32bit and 64bit systems.
+typedef uint32_t uoffset_t;
+
+// Signed offsets for references that can go in both directions.
+typedef int32_t soffset_t;
+
+// Offset/index used in v-tables, can be changed to uint8_t in
+// format forks to save a bit of space if desired.
+typedef uint16_t voffset_t;
+
+typedef uintmax_t largest_scalar_t;
+
+// Pointer to relinquished memory.
+typedef std::unique_ptr<uint8_t, std::function<void(uint8_t * /* unused */)>>
+ unique_ptr_t;
+
+// Wrapper for uoffset_t to allow safe template specialization.
+template<typename T> struct Offset {
+ uoffset_t o;
+ Offset() : o(0) {}
+ Offset(uoffset_t _o) : o(_o) {}
+ Offset<void> Union() const { return Offset<void>(o); }
+};
+
+inline void EndianCheck() {
+ int endiantest = 1;
+ // If this fails, see FLATBUFFERS_LITTLEENDIAN above.
+ assert(*reinterpret_cast<char *>(&endiantest) == FLATBUFFERS_LITTLEENDIAN);
+ (void)endiantest;
+}
+
+template<typename T> T EndianScalar(T t) {
+ #if FLATBUFFERS_LITTLEENDIAN
+ return t;
+ #else
+ #if defined(_MSC_VER)
+ #pragma push_macro("__builtin_bswap16")
+ #pragma push_macro("__builtin_bswap32")
+ #pragma push_macro("__builtin_bswap64")
+ #define __builtin_bswap16 _byteswap_ushort
+ #define __builtin_bswap32 _byteswap_ulong
+ #define __builtin_bswap64 _byteswap_uint64
+ #endif
+ // If you're on the few remaining big endian platforms, we make the bold
+ // assumption you're also on gcc/clang, and thus have bswap intrinsics:
+ if (sizeof(T) == 1) { // Compile-time if-then's.
+ return t;
+ } else if (sizeof(T) == 2) {
+ auto r = __builtin_bswap16(*reinterpret_cast<uint16_t *>(&t));
+ return *reinterpret_cast<T *>(&r);
+ } else if (sizeof(T) == 4) {
+ auto r = __builtin_bswap32(*reinterpret_cast<uint32_t *>(&t));
+ return *reinterpret_cast<T *>(&r);
+ } else if (sizeof(T) == 8) {
+ auto r = __builtin_bswap64(*reinterpret_cast<uint64_t *>(&t));
+ return *reinterpret_cast<T *>(&r);
+ } else {
+ assert(0);
+ }
+ #if defined(_MSC_VER)
+ #pragma pop_macro("__builtin_bswap16")
+ #pragma pop_macro("__builtin_bswap32")
+ #pragma pop_macro("__builtin_bswap64")
+ #endif
+ #endif
+}
+
+template<typename T> T ReadScalar(const void *p) {
+ return EndianScalar(*reinterpret_cast<const T *>(p));
+}
+
+template<typename T> void WriteScalar(void *p, T t) {
+ *reinterpret_cast<T *>(p) = EndianScalar(t);
+}
+
+template<typename T> size_t AlignOf() {
+ #ifdef _MSC_VER
+ return __alignof(T);
+ #else
+ return alignof(T);
+ #endif
+}
+
+// When we read serialized data from memory, in the case of most scalars,
+// we want to just read T, but in the case of Offset, we want to actually
+// perform the indirection and return a pointer.
+// The template specialization below does just that.
+// It is wrapped in a struct since function templates can't overload on the
+// return type like this.
+// The typedef is for the convenience of callers of this function
+// (avoiding the need for a trailing return decltype)
+template<typename T> struct IndirectHelper {
+ typedef T return_type;
+ static const size_t element_stride = sizeof(T);
+ static return_type Read(const uint8_t *p, uoffset_t i) {
+ return EndianScalar((reinterpret_cast<const T *>(p))[i]);
+ }
+};
+template<typename T> struct IndirectHelper<Offset<T>> {
+ typedef const T *return_type;
+ static const size_t element_stride = sizeof(uoffset_t);
+ static return_type Read(const uint8_t *p, uoffset_t i) {
+ p += i * sizeof(uoffset_t);
+ return reinterpret_cast<return_type>(p + ReadScalar<uoffset_t>(p));
+ }
+};
+template<typename T> struct IndirectHelper<const T *> {
+ typedef const T *return_type;
+ static const size_t element_stride = sizeof(T);
+ static return_type Read(const uint8_t *p, uoffset_t i) {
+ return reinterpret_cast<const T *>(p + i * sizeof(T));
+ }
+};
+
+// An STL compatible iterator implementation for Vector below, effectively
+// calling Get() for every element.
+template<typename T, bool bConst>
+struct VectorIterator : public
+ std::iterator < std::input_iterator_tag,
+ typename std::conditional < bConst,
+ const typename IndirectHelper<T>::return_type,
+ typename IndirectHelper<T>::return_type > ::type, uoffset_t > {
+
+ typedef std::iterator<std::input_iterator_tag,
+ typename std::conditional<bConst,
+ const typename IndirectHelper<T>::return_type,
+ typename IndirectHelper<T>::return_type>::type, uoffset_t> super_type;
+
+public:
+ VectorIterator(const uint8_t *data, uoffset_t i) :
+ data_(data + IndirectHelper<T>::element_stride * i) {};
+ VectorIterator(const VectorIterator &other) : data_(other.data_) {}
+ VectorIterator(VectorIterator &&other) : data_(std::move(other.data_)) {}
+
+ VectorIterator &operator=(const VectorIterator &other) {
+ data_ = other.data_;
+ return *this;
+ }
+
+ VectorIterator &operator=(VectorIterator &&other) {
+ data_ = other.data_;
+ return *this;
+ }
+
+ bool operator==(const VectorIterator& other) const {
+ return data_ == other.data_;
+ }
+
+ bool operator!=(const VectorIterator& other) const {
+ return data_ != other.data_;
+ }
+
+ ptrdiff_t operator-(const VectorIterator& other) const {
+ return (data_ - other.data_) / IndirectHelper<T>::element_stride;
+ }
+
+ typename super_type::value_type operator *() const {
+ return IndirectHelper<T>::Read(data_, 0);
+ }
+
+ typename super_type::value_type operator->() const {
+ return IndirectHelper<T>::Read(data_, 0);
+ }
+
+ VectorIterator &operator++() {
+ data_ += IndirectHelper<T>::element_stride;
+ return *this;
+ }
+
+ VectorIterator operator++(int) {
+ VectorIterator temp(data_);
+ data_ += IndirectHelper<T>::element_stride;
+ return temp;
+ }
+
+private:
+ const uint8_t *data_;
+};
+
+// This is used as a helper type for accessing vectors.
+// Vector::data() assumes the vector elements start after the length field.
+template<typename T> class Vector {
+public:
+ typedef VectorIterator<T, false> iterator;
+ typedef VectorIterator<T, true> const_iterator;
+
+ uoffset_t size() const { return EndianScalar(length_); }
+
+ // Deprecated: use size(). Here for backwards compatibility.
+ uoffset_t Length() const { return size(); }
+
+ typedef typename IndirectHelper<T>::return_type return_type;
+
+ return_type Get(uoffset_t i) const {
+ assert(i < size());
+ return IndirectHelper<T>::Read(Data(), i);
+ }
+
+ return_type operator[](uoffset_t i) const { return Get(i); }
+
+ // If this is a Vector of enums, T will be its storage type, not the enum
+ // type. This function makes it convenient to retrieve value with enum
+ // type E.
+ template<typename E> E GetEnum(uoffset_t i) const {
+ return static_cast<E>(Get(i));
+ }
+
+ const void *GetStructFromOffset(size_t o) const {
+ return reinterpret_cast<const void *>(Data() + o);
+ }
+
+ iterator begin() { return iterator(Data(), 0); }
+ const_iterator begin() const { return const_iterator(Data(), 0); }
+
+ iterator end() { return iterator(Data(), size()); }
+ const_iterator end() const { return const_iterator(Data(), size()); }
+
+ // Change elements if you have a non-const pointer to this object.
+ // Scalars only. See reflection_reader.h, and the documentation.
+ void Mutate(uoffset_t i, T val) {
+ assert(i < size());
+ WriteScalar(data() + i, val);
+ }
+
+ // Change an element of a vector of tables (or strings).
+ // "val" points to the new table/string, as you can obtain from
+ // e.g. reflection::AddFlatBuffer().
+ void MutateOffset(uoffset_t i, const uint8_t *val) {
+ assert(i < size());
+ assert(sizeof(T) == sizeof(uoffset_t));
+ WriteScalar(data() + i, val - (Data() + i * sizeof(uoffset_t)));
+ }
+
+ // The raw data in little endian format. Use with care.
+ const uint8_t *Data() const {
+ return reinterpret_cast<const uint8_t *>(&length_ + 1);
+ }
+
+ uint8_t *Data() {
+ return reinterpret_cast<uint8_t *>(&length_ + 1);
+ }
+
+ // Similarly, but typed, much like std::vector::data
+ const T *data() const { return reinterpret_cast<const T *>(Data()); }
+ T *data() { return reinterpret_cast<T *>(Data()); }
+
+ template<typename K> return_type LookupByKey(K key) const {
+ void *search_result = std::bsearch(&key, Data(), size(),
+ IndirectHelper<T>::element_stride, KeyCompare<K>);
+
+ if (!search_result) {
+ return nullptr; // Key not found.
+ }
+
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(search_result);
+
+ return IndirectHelper<T>::Read(data, 0);
+ }
+
+protected:
+ // This class is only used to access pre-existing data. Don't ever
+ // try to construct these manually.
+ Vector();
+
+ uoffset_t length_;
+
+private:
+ template<typename K> static int KeyCompare(const void *ap, const void *bp) {
+ const K *key = reinterpret_cast<const K *>(ap);
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(bp);
+ auto table = IndirectHelper<T>::Read(data, 0);
+
+ // std::bsearch compares with the operands transposed, so we negate the
+ // result here.
+ return -table->KeyCompareWithValue(*key);
+ }
+};
+
+// Represent a vector much like the template above, but in this case we
+// don't know what the element types are (used with reflection.h).
+class VectorOfAny {
+public:
+ uoffset_t size() const { return EndianScalar(length_); }
+
+ const uint8_t *Data() const {
+ return reinterpret_cast<const uint8_t *>(&length_ + 1);
+ }
+ uint8_t *Data() {
+ return reinterpret_cast<uint8_t *>(&length_ + 1);
+ }
+protected:
+ VectorOfAny();
+
+ uoffset_t length_;
+};
+
+// Convenient helper function to get the length of any vector, regardless
+// of wether it is null or not (the field is not set).
+template<typename T> static inline size_t VectorLength(const Vector<T> *v) {
+ return v ? v->Length() : 0;
+}
+
+struct String : public Vector<char> {
+ const char *c_str() const { return reinterpret_cast<const char *>(Data()); }
+ std::string str() const { return c_str(); }
+
+ bool operator <(const String &o) const {
+ return strcmp(c_str(), o.c_str()) < 0;
+ }
+};
+
+// Simple indirection for buffer allocation, to allow this to be overridden
+// with custom allocation (see the FlatBufferBuilder constructor).
+class simple_allocator {
+ public:
+ virtual ~simple_allocator() {}
+ virtual uint8_t *allocate(size_t size) const { return new uint8_t[size]; }
+ virtual void deallocate(uint8_t *p) const { delete[] p; }
+};
+
+// This is a minimal replication of std::vector<uint8_t> functionality,
+// except growing from higher to lower addresses. i.e push_back() inserts data
+// in the lowest address in the vector.
+class vector_downward {
+ public:
+ explicit vector_downward(size_t initial_size,
+ const simple_allocator &allocator)
+ : reserved_(initial_size),
+ buf_(allocator.allocate(reserved_)),
+ cur_(buf_ + reserved_),
+ allocator_(allocator) {
+ assert((initial_size & (sizeof(largest_scalar_t) - 1)) == 0);
+ }
+
+ ~vector_downward() {
+ if (buf_)
+ allocator_.deallocate(buf_);
+ }
+
+ void clear() {
+ if (buf_ == nullptr)
+ buf_ = allocator_.allocate(reserved_);
+
+ cur_ = buf_ + reserved_;
+ }
+
+ // Relinquish the pointer to the caller.
+ unique_ptr_t release() {
+ // Actually deallocate from the start of the allocated memory.
+ std::function<void(uint8_t *)> deleter(
+ std::bind(&simple_allocator::deallocate, allocator_, buf_));
+
+ // Point to the desired offset.
+ unique_ptr_t retval(data(), deleter);
+
+ // Don't deallocate when this instance is destroyed.
+ buf_ = nullptr;
+ cur_ = nullptr;
+
+ return retval;
+ }
+
+ size_t growth_policy(size_t bytes) {
+ return (bytes / 2) & ~(sizeof(largest_scalar_t) - 1);
+ }
+
+ uint8_t *make_space(size_t len) {
+ if (len > static_cast<size_t>(cur_ - buf_)) {
+ auto old_size = size();
+ auto largest_align = AlignOf<largest_scalar_t>();
+ reserved_ += std::max(len, growth_policy(reserved_));
+ // Round up to avoid undefined behavior from unaligned loads and stores.
+ reserved_ = (reserved_ + (largest_align - 1)) & ~(largest_align - 1);
+ auto new_buf = allocator_.allocate(reserved_);
+ auto new_cur = new_buf + reserved_ - old_size;
+ memcpy(new_cur, cur_, old_size);
+ cur_ = new_cur;
+ allocator_.deallocate(buf_);
+ buf_ = new_buf;
+ }
+ cur_ -= len;
+ // Beyond this, signed offsets may not have enough range:
+ // (FlatBuffers > 2GB not supported).
+ assert(size() < (1UL << (sizeof(soffset_t) * 8 - 1)) - 1);
+ return cur_;
+ }
+
+ uoffset_t size() const {
+ assert(cur_ != nullptr && buf_ != nullptr);
+ return static_cast<uoffset_t>(reserved_ - (cur_ - buf_));
+ }
+
+ uint8_t *data() const {
+ assert(cur_ != nullptr);
+ return cur_;
+ }
+
+ uint8_t *data_at(size_t offset) { return buf_ + reserved_ - offset; }
+
+ // push() & fill() are most frequently called with small byte counts (<= 4),
+ // which is why we're using loops rather than calling memcpy/memset.
+ void push(const uint8_t *bytes, size_t num) {
+ auto dest = make_space(num);
+ for (size_t i = 0; i < num; i++) dest[i] = bytes[i];
+ }
+
+ void fill(size_t zero_pad_bytes) {
+ auto dest = make_space(zero_pad_bytes);
+ for (size_t i = 0; i < zero_pad_bytes; i++) dest[i] = 0;
+ }
+
+ void pop(size_t bytes_to_remove) { cur_ += bytes_to_remove; }
+
+ private:
+ // You shouldn't really be copying instances of this class.
+ vector_downward(const vector_downward &);
+ vector_downward &operator=(const vector_downward &);
+
+ size_t reserved_;
+ uint8_t *buf_;
+ uint8_t *cur_; // Points at location between empty (below) and used (above).
+ const simple_allocator &allocator_;
+};
+
+// Converts a Field ID to a virtual table offset.
+inline voffset_t FieldIndexToOffset(voffset_t field_id) {
+ // Should correspond to what EndTable() below builds up.
+ const int fixed_fields = 2; // Vtable size and Object Size.
+ return (field_id + fixed_fields) * sizeof(voffset_t);
+}
+
+// Computes how many bytes you'd have to pad to be able to write an
+// "scalar_size" scalar if the buffer had grown to "buf_size" (downwards in
+// memory).
+inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) {
+ return ((~buf_size) + 1) & (scalar_size - 1);
+}
+
+// Helper class to hold data needed in creation of a flat buffer.
+// To serialize data, you typically call one of the Create*() functions in
+// the generated code, which in turn call a sequence of StartTable/PushElement/
+// AddElement/EndTable, or the builtin CreateString/CreateVector functions.
+// Do this is depth-first order to build up a tree to the root.
+// Finish() wraps up the buffer ready for transport.
+class FlatBufferBuilder FLATBUFFERS_FINAL_CLASS {
+ public:
+ explicit FlatBufferBuilder(uoffset_t initial_size = 1024,
+ const simple_allocator *allocator = nullptr)
+ : buf_(initial_size, allocator ? *allocator : default_allocator),
+ minalign_(1), force_defaults_(false) {
+ offsetbuf_.reserve(16); // Avoid first few reallocs.
+ vtables_.reserve(16);
+ EndianCheck();
+ }
+
+ // Reset all the state in this FlatBufferBuilder so it can be reused
+ // to construct another buffer.
+ void Clear() {
+ buf_.clear();
+ offsetbuf_.clear();
+ vtables_.clear();
+ minalign_ = 1;
+ }
+
+ // The current size of the serialized buffer, counting from the end.
+ uoffset_t GetSize() const { return buf_.size(); }
+
+ // Get the serialized buffer (after you call Finish()).
+ uint8_t *GetBufferPointer() const { return buf_.data(); }
+
+ // Get the released pointer to the serialized buffer.
+ // Don't attempt to use this FlatBufferBuilder afterwards!
+ // The unique_ptr returned has a special allocator that knows how to
+ // deallocate this pointer (since it points to the middle of an allocation).
+ // Thus, do not mix this pointer with other unique_ptr's, or call release() /
+ // reset() on it.
+ unique_ptr_t ReleaseBufferPointer() { return buf_.release(); }
+
+ void ForceDefaults(bool fd) { force_defaults_ = fd; }
+
+ void Pad(size_t num_bytes) { buf_.fill(num_bytes); }
+
+ void Align(size_t elem_size) {
+ if (elem_size > minalign_) minalign_ = elem_size;
+ buf_.fill(PaddingBytes(buf_.size(), elem_size));
+ }
+
+ void PushBytes(const uint8_t *bytes, size_t size) {
+ buf_.push(bytes, size);
+ }
+
+ void PopBytes(size_t amount) { buf_.pop(amount); }
+
+ template<typename T> void AssertScalarT() {
+ // The code assumes power of 2 sizes and endian-swap-ability.
+ static_assert(std::is_scalar<T>::value
+ // The Offset<T> type is essentially a scalar but fails is_scalar.
+ || sizeof(T) == sizeof(Offset<void>),
+ "T must be a scalar type");
+ }
+
+ // Write a single aligned scalar to the buffer
+ template<typename T> uoffset_t PushElement(T element) {
+ AssertScalarT<T>();
+ T litle_endian_element = EndianScalar(element);
+ Align(sizeof(T));
+ PushBytes(reinterpret_cast<uint8_t *>(&litle_endian_element), sizeof(T));
+ return GetSize();
+ }
+
+ template<typename T> uoffset_t PushElement(Offset<T> off) {
+ // Special case for offsets: see ReferTo below.
+ return PushElement(ReferTo(off.o));
+ }
+
+ // When writing fields, we track where they are, so we can create correct
+ // vtables later.
+ void TrackField(voffset_t field, uoffset_t off) {
+ FieldLoc fl = { off, field };
+ offsetbuf_.push_back(fl);
+ }
+
+ // Like PushElement, but additionally tracks the field this represents.
+ template<typename T> void AddElement(voffset_t field, T e, T def) {
+ // We don't serialize values equal to the default.
+ if (e == def && !force_defaults_) return;
+ auto off = PushElement(e);
+ TrackField(field, off);
+ }
+
+ template<typename T> void AddOffset(voffset_t field, Offset<T> off) {
+ if (!off.o) return; // An offset of 0 means NULL, don't store.
+ AddElement(field, ReferTo(off.o), static_cast<uoffset_t>(0));
+ }
+
+ template<typename T> void AddStruct(voffset_t field, const T *structptr) {
+ if (!structptr) return; // Default, don't store.
+ Align(AlignOf<T>());
+ PushBytes(reinterpret_cast<const uint8_t *>(structptr), sizeof(T));
+ TrackField(field, GetSize());
+ }
+
+ void AddStructOffset(voffset_t field, uoffset_t off) {
+ TrackField(field, off);
+ }
+
+ // Offsets initially are relative to the end of the buffer (downwards).
+ // This function converts them to be relative to the current location
+ // in the buffer (when stored here), pointing upwards.
+ uoffset_t ReferTo(uoffset_t off) {
+ Align(sizeof(uoffset_t)); // To ensure GetSize() below is correct.
+ assert(off <= GetSize()); // Must refer to something already in buffer.
+ return GetSize() - off + sizeof(uoffset_t);
+ }
+
+ void NotNested() {
+ // If you hit this, you're trying to construct an object when another
+ // hasn't finished yet.
+ assert(!offsetbuf_.size());
+ }
+
+ // From generated code (or from the parser), we call StartTable/EndTable
+ // with a sequence of AddElement calls in between.
+ uoffset_t StartTable() {
+ NotNested();
+ return GetSize();
+ }
+
+ // This finishes one serialized object by generating the vtable if it's a
+ // table, comparing it against existing vtables, and writing the
+ // resulting vtable offset.
+ uoffset_t EndTable(uoffset_t start, voffset_t numfields) {
+ // Write the vtable offset, which is the start of any Table.
+ // We fill it's value later.
+ auto vtableoffsetloc = PushElement<soffset_t>(0);
+ // Write a vtable, which consists entirely of voffset_t elements.
+ // It starts with the number of offsets, followed by a type id, followed
+ // by the offsets themselves. In reverse:
+ buf_.fill(numfields * sizeof(voffset_t));
+ auto table_object_size = vtableoffsetloc - start;
+ assert(table_object_size < 0x10000); // Vtable use 16bit offsets.
+ PushElement<voffset_t>(static_cast<voffset_t>(table_object_size));
+ PushElement<voffset_t>(FieldIndexToOffset(numfields));
+ // Write the offsets into the table
+ for (auto field_location = offsetbuf_.begin();
+ field_location != offsetbuf_.end();
+ ++field_location) {
+ auto pos = static_cast<voffset_t>(vtableoffsetloc - field_location->off);
+ // If this asserts, it means you've set a field twice.
+ assert(!ReadScalar<voffset_t>(buf_.data() + field_location->id));
+ WriteScalar<voffset_t>(buf_.data() + field_location->id, pos);
+ }
+ offsetbuf_.clear();
+ auto vt1 = reinterpret_cast<voffset_t *>(buf_.data());
+ auto vt1_size = ReadScalar<voffset_t>(vt1);
+ auto vt_use = GetSize();
+ // See if we already have generated a vtable with this exact same
+ // layout before. If so, make it point to the old one, remove this one.
+ for (auto it = vtables_.begin(); it != vtables_.end(); ++it) {
+ auto vt2 = reinterpret_cast<voffset_t *>(buf_.data_at(*it));
+ auto vt2_size = *vt2;
+ if (vt1_size != vt2_size || memcmp(vt2, vt1, vt1_size)) continue;
+ vt_use = *it;
+ buf_.pop(GetSize() - vtableoffsetloc);
+ break;
+ }
+ // If this is a new vtable, remember it.
+ if (vt_use == GetSize()) {
+ vtables_.push_back(vt_use);
+ }
+ // Fill the vtable offset we created above.
+ // The offset points from the beginning of the object to where the
+ // vtable is stored.
+ // Offsets default direction is downward in memory for future format
+ // flexibility (storing all vtables at the start of the file).
+ WriteScalar(buf_.data_at(vtableoffsetloc),
+ static_cast<soffset_t>(vt_use) -
+ static_cast<soffset_t>(vtableoffsetloc));
+ return vtableoffsetloc;
+ }
+
+ // This checks a required field has been set in a given table that has
+ // just been constructed.
+ template<typename T> void Required(Offset<T> table, voffset_t field) {
+ auto table_ptr = buf_.data_at(table.o);
+ auto vtable_ptr = table_ptr - ReadScalar<soffset_t>(table_ptr);
+ bool ok = ReadScalar<voffset_t>(vtable_ptr + field) != 0;
+ // If this fails, the caller will show what field needs to be set.
+ assert(ok);
+ (void)ok;
+ }
+
+ uoffset_t StartStruct(size_t alignment) {
+ Align(alignment);
+ return GetSize();
+ }
+
+ uoffset_t EndStruct() { return GetSize(); }
+
+ void ClearOffsets() { offsetbuf_.clear(); }
+
+ // Aligns such that when "len" bytes are written, an object can be written
+ // after it with "alignment" without padding.
+ void PreAlign(size_t len, size_t alignment) {
+ buf_.fill(PaddingBytes(GetSize() + len, alignment));
+ }
+ template<typename T> void PreAlign(size_t len) {
+ AssertScalarT<T>();
+ PreAlign(len, sizeof(T));
+ }
+
+ // Functions to store strings, which are allowed to contain any binary data.
+ Offset<String> CreateString(const char *str, size_t len) {
+ NotNested();
+ PreAlign<uoffset_t>(len + 1); // Always 0-terminated.
+ buf_.fill(1);
+ PushBytes(reinterpret_cast<const uint8_t *>(str), len);
+ PushElement(static_cast<uoffset_t>(len));
+ return Offset<String>(GetSize());
+ }
+
+ Offset<String> CreateString(const char *str) {
+ return CreateString(str, strlen(str));
+ }
+
+ Offset<String> CreateString(const std::string &str) {
+ return CreateString(str.c_str(), str.length());
+ }
+
+ Offset<String> CreateString(const String *str) {
+ return CreateString(str->c_str(), str->Length());
+ }
+
+ uoffset_t EndVector(size_t len) {
+ return PushElement(static_cast<uoffset_t>(len));
+ }
+
+ void StartVector(size_t len, size_t elemsize) {
+ PreAlign<uoffset_t>(len * elemsize);
+ PreAlign(len * elemsize, elemsize); // Just in case elemsize > uoffset_t.
+ }
+
+ uint8_t *ReserveElements(size_t len, size_t elemsize) {
+ return buf_.make_space(len * elemsize);
+ }
+
+ template<typename T> Offset<Vector<T>> CreateVector(const T *v, size_t len) {
+ NotNested();
+ StartVector(len, sizeof(T));
+ for (auto i = len; i > 0; ) {
+ PushElement(v[--i]);
+ }
+ return Offset<Vector<T>>(EndVector(len));
+ }
+
+ template<typename T> Offset<Vector<T>> CreateVector(const std::vector<T> &v) {
+ return CreateVector(v.data(), v.size());
+ }
+
+ template<typename T> Offset<Vector<const T *>> CreateVectorOfStructs(
+ const T *v, size_t len) {
+ NotNested();
+ StartVector(len * sizeof(T) / AlignOf<T>(), AlignOf<T>());
+ PushBytes(reinterpret_cast<const uint8_t *>(v), sizeof(T) * len);
+ return Offset<Vector<const T *>>(EndVector(len));
+ }
+
+ template<typename T> Offset<Vector<const T *>> CreateVectorOfStructs(
+ const std::vector<T> &v) {
+ return CreateVectorOfStructs(v.data(), v.size());
+ }
+
+ template<typename T> Offset<Vector<Offset<T>>> CreateVectorOfSortedTables(
+ Offset<T> *v, size_t len) {
+ std::sort(v, v + len,
+ [this](const Offset<T> &a, const Offset<T> &b) -> bool {
+ auto table_a = reinterpret_cast<T *>(buf_.data_at(a.o));
+ auto table_b = reinterpret_cast<T *>(buf_.data_at(b.o));
+ return table_a->KeyCompareLessThan(table_b);
+ }
+ );
+ return CreateVector(v, len);
+ }
+
+ template<typename T> Offset<Vector<Offset<T>>> CreateVectorOfSortedTables(
+ std::vector<Offset<T>> *v) {
+ return CreateVectorOfSortedTables(v->data(), v->size());
+ }
+
+ // Specialized version for non-copying use cases. Write the data any time
+ // later to the returned buffer pointer `buf`.
+ uoffset_t CreateUninitializedVector(size_t len, size_t elemsize,
+ uint8_t **buf) {
+ NotNested();
+ StartVector(len, elemsize);
+ *buf = buf_.make_space(len * elemsize);
+ return EndVector(len);
+ }
+
+ template<typename T> Offset<Vector<T>> CreateUninitializedVector(
+ size_t len, T **buf) {
+ return CreateUninitializedVector(len, sizeof(T),
+ reinterpret_cast<uint8_t **>(buf));
+ }
+
+ static const size_t kFileIdentifierLength = 4;
+
+ // Finish serializing a buffer by writing the root offset.
+ // If a file_identifier is given, the buffer will be prefix with a standard
+ // FlatBuffers file header.
+ template<typename T> void Finish(Offset<T> root,
+ const char *file_identifier = nullptr) {
+ // This will cause the whole buffer to be aligned.
+ PreAlign(sizeof(uoffset_t) + (file_identifier ? kFileIdentifierLength : 0),
+ minalign_);
+ if (file_identifier) {
+ assert(strlen(file_identifier) == kFileIdentifierLength);
+ buf_.push(reinterpret_cast<const uint8_t *>(file_identifier),
+ kFileIdentifierLength);
+ }
+ PushElement(ReferTo(root.o)); // Location of root.
+ }
+
+ private:
+ // You shouldn't really be copying instances of this class.
+ FlatBufferBuilder(const FlatBufferBuilder &);
+ FlatBufferBuilder &operator=(const FlatBufferBuilder &);
+
+ struct FieldLoc {
+ uoffset_t off;
+ voffset_t id;
+ };
+
+ simple_allocator default_allocator;
+
+ vector_downward buf_;
+
+ // Accumulating offsets of table members while it is being built.
+ std::vector<FieldLoc> offsetbuf_;
+
+ std::vector<uoffset_t> vtables_; // todo: Could make this into a map?
+
+ size_t minalign_;
+
+ bool force_defaults_; // Serialize values equal to their defaults anyway.
+};
+
+// Helpers to get a typed pointer to the root object contained in the buffer.
+template<typename T> T *GetMutableRoot(void *buf) {
+ EndianCheck();
+ return reinterpret_cast<T *>(reinterpret_cast<uint8_t *>(buf) +
+ EndianScalar(*reinterpret_cast<uoffset_t *>(buf)));
+}
+
+template<typename T> const T *GetRoot(const void *buf) {
+ return GetMutableRoot<T>(const_cast<void *>(buf));
+}
+
+// Helper to see if the identifier in a buffer has the expected value.
+inline bool BufferHasIdentifier(const void *buf, const char *identifier) {
+ return strncmp(reinterpret_cast<const char *>(buf) + sizeof(uoffset_t),
+ identifier, FlatBufferBuilder::kFileIdentifierLength) == 0;
+}
+
+// Helper class to verify the integrity of a FlatBuffer
+class Verifier FLATBUFFERS_FINAL_CLASS {
+ public:
+ Verifier(const uint8_t *buf, size_t buf_len, size_t _max_depth = 64,
+ size_t _max_tables = 1000000)
+ : buf_(buf), end_(buf + buf_len), depth_(0), max_depth_(_max_depth),
+ num_tables_(0), max_tables_(_max_tables)
+ {}
+
+ // Central location where any verification failures register.
+ bool Check(bool ok) const {
+ #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
+ assert(ok);
+ #endif
+ return ok;
+ }
+
+ // Verify any range within the buffer.
+ bool Verify(const void *elem, size_t elem_len) const {
+ return Check(elem_len <= (size_t) (end_ - buf_) && elem >= buf_ && elem <= end_ - elem_len);
+ }
+
+ // Verify a range indicated by sizeof(T).
+ template<typename T> bool Verify(const void *elem) const {
+ return Verify(elem, sizeof(T));
+ }
+
+ // Verify a pointer (may be NULL) of a table type.
+ template<typename T> bool VerifyTable(const T *table) {
+ return !table || table->Verify(*this);
+ }
+
+ // Verify a pointer (may be NULL) of any vector type.
+ template<typename T> bool Verify(const Vector<T> *vec) const {
+ const uint8_t *end;
+ return !vec ||
+ VerifyVector(reinterpret_cast<const uint8_t *>(vec), sizeof(T),
+ &end);
+ }
+
+ // Verify a pointer (may be NULL) to string.
+ bool Verify(const String *str) const {
+ const uint8_t *end;
+ return !str ||
+ (VerifyVector(reinterpret_cast<const uint8_t *>(str), 1, &end) &&
+ Verify(end, 1) && // Must have terminator
+ Check(*end == '\0')); // Terminating byte must be 0.
+ }
+
+ // Common code between vectors and strings.
+ bool VerifyVector(const uint8_t *vec, size_t elem_size,
+ const uint8_t **end) const {
+ // Check we can read the size field.
+ if (!Verify<uoffset_t>(vec)) return false;
+ // Check the whole array. If this is a string, the byte past the array
+ // must be 0.
+ auto size = ReadScalar<uoffset_t>(vec);
+ auto byte_size = sizeof(size) + elem_size * size;
+ *end = vec + byte_size;
+ return Verify(vec, byte_size);
+ }
+
+ // Special case for string contents, after the above has been called.
+ bool VerifyVectorOfStrings(const Vector<Offset<String>> *vec) const {
+ if (vec) {
+ for (uoffset_t i = 0; i < vec->size(); i++) {
+ if (!Verify(vec->Get(i))) return false;
+ }
+ }
+ return true;
+ }
+
+ // Special case for table contents, after the above has been called.
+ template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec) {
+ if (vec) {
+ for (uoffset_t i = 0; i < vec->size(); i++) {
+ if (!vec->Get(i)->Verify(*this)) return false;
+ }
+ }
+ return true;
+ }
+
+ // Verify this whole buffer, starting with root type T.
+ template<typename T> bool VerifyBuffer() {
+ // Call T::Verify, which must be in the generated code for this type.
+ return Verify<uoffset_t>(buf_) &&
+ reinterpret_cast<const T *>(buf_ + ReadScalar<uoffset_t>(buf_))->
+ Verify(*this);
+ }
+
+ // Called at the start of a table to increase counters measuring data
+ // structure depth and amount, and possibly bails out with false if
+ // limits set by the constructor have been hit. Needs to be balanced
+ // with EndTable().
+ bool VerifyComplexity() {
+ depth_++;
+ num_tables_++;
+ return Check(depth_ <= max_depth_ && num_tables_ <= max_tables_);
+ }
+
+ // Called at the end of a table to pop the depth count.
+ bool EndTable() {
+ depth_--;
+ return true;
+ }
+
+ private:
+ const uint8_t *buf_;
+ const uint8_t *end_;
+ size_t depth_;
+ size_t max_depth_;
+ size_t num_tables_;
+ size_t max_tables_;
+};
+
+// "structs" are flat structures that do not have an offset table, thus
+// always have all members present and do not support forwards/backwards
+// compatible extensions.
+
+class Struct FLATBUFFERS_FINAL_CLASS {
+ public:
+ template<typename T> T GetField(uoffset_t o) const {
+ return ReadScalar<T>(&data_[o]);
+ }
+
+ template<typename T> T GetPointer(uoffset_t o) const {
+ auto p = &data_[o];
+ return reinterpret_cast<T>(p + ReadScalar<uoffset_t>(p));
+ }
+
+ template<typename T> T GetStruct(uoffset_t o) const {
+ return reinterpret_cast<T>(&data_[o]);
+ }
+
+ const uint8_t *GetAddressOf(uoffset_t o) const { return &data_[o]; }
+ uint8_t *GetAddressOf(uoffset_t o) { return &data_[o]; }
+
+ private:
+ uint8_t data_[1];
+};
+
+// "tables" use an offset table (possibly shared) that allows fields to be
+// omitted and added at will, but uses an extra indirection to read.
+class Table {
+ public:
+ // This gets the field offset for any of the functions below it, or 0
+ // if the field was not present.
+ voffset_t GetOptionalFieldOffset(voffset_t field) const {
+ // The vtable offset is always at the start.
+ auto vtable = data_ - ReadScalar<soffset_t>(data_);
+ // The first element is the size of the vtable (fields + type id + itself).
+ auto vtsize = ReadScalar<voffset_t>(vtable);
+ // If the field we're accessing is outside the vtable, we're reading older
+ // data, so it's the same as if the offset was 0 (not present).
+ return field < vtsize ? ReadScalar<voffset_t>(vtable + field) : 0;
+ }
+
+ template<typename T> T GetField(voffset_t field, T defaultval) const {
+ auto field_offset = GetOptionalFieldOffset(field);
+ return field_offset ? ReadScalar<T>(data_ + field_offset) : defaultval;
+ }
+
+ template<typename P> P GetPointer(voffset_t field) {
+ auto field_offset = GetOptionalFieldOffset(field);
+ auto p = data_ + field_offset;
+ return field_offset
+ ? reinterpret_cast<P>(p + ReadScalar<uoffset_t>(p))
+ : nullptr;
+ }
+ template<typename P> P GetPointer(voffset_t field) const {
+ return const_cast<Table *>(this)->GetPointer<P>(field);
+ }
+
+ template<typename P> P GetStruct(voffset_t field) const {
+ auto field_offset = GetOptionalFieldOffset(field);
+ auto p = const_cast<uint8_t *>(data_ + field_offset);
+ return field_offset ? reinterpret_cast<P>(p) : nullptr;
+ }
+
+ template<typename T> bool SetField(voffset_t field, T val) {
+ auto field_offset = GetOptionalFieldOffset(field);
+ if (!field_offset) return false;
+ WriteScalar(data_ + field_offset, val);
+ return true;
+ }
+
+ bool SetPointer(voffset_t field, const uint8_t *val) {
+ auto field_offset = GetOptionalFieldOffset(field);
+ if (!field_offset) return false;
+ WriteScalar(data_ + field_offset, val - (data_ + field_offset));
+ return true;
+ }
+
+ uint8_t *GetAddressOf(voffset_t field) {
+ auto field_offset = GetOptionalFieldOffset(field);
+ return field_offset ? data_ + field_offset : nullptr;
+ }
+ const uint8_t *GetAddressOf(voffset_t field) const {
+ return const_cast<Table *>(this)->GetAddressOf(field);
+ }
+
+ uint8_t *GetVTable() { return data_ - ReadScalar<soffset_t>(data_); }
+
+ bool CheckField(voffset_t field) const {
+ return GetOptionalFieldOffset(field) != 0;
+ }
+
+ // Verify the vtable of this table.
+ // Call this once per table, followed by VerifyField once per field.
+ bool VerifyTableStart(Verifier &verifier) const {
+ // Check the vtable offset.
+ if (!verifier.Verify<soffset_t>(data_)) return false;
+ auto vtable = data_ - ReadScalar<soffset_t>(data_);
+ // Check the vtable size field, then check vtable fits in its entirety.
+ return verifier.VerifyComplexity() &&
+ verifier.Verify<voffset_t>(vtable) &&
+ verifier.Verify(vtable, ReadScalar<voffset_t>(vtable));
+ }
+
+ // Verify a particular field.
+ template<typename T> bool VerifyField(const Verifier &verifier,
+ voffset_t field) const {
+ // Calling GetOptionalFieldOffset should be safe now thanks to
+ // VerifyTable().
+ auto field_offset = GetOptionalFieldOffset(field);
+ // Check the actual field.
+ return !field_offset || verifier.Verify<T>(data_ + field_offset);
+ }
+
+ // VerifyField for required fields.
+ template<typename T> bool VerifyFieldRequired(const Verifier &verifier,
+ voffset_t field) const {
+ auto field_offset = GetOptionalFieldOffset(field);
+ return verifier.Check(field_offset != 0) &&
+ verifier.Verify<T>(data_ + field_offset);
+ }
+
+ private:
+ // private constructor & copy constructor: you obtain instances of this
+ // class by pointing to existing data only
+ Table();
+ Table(const Table &other);
+
+ uint8_t data_[1];
+};
+
+// Utility function for reverse lookups on the EnumNames*() functions
+// (in the generated C++ code)
+// names must be NULL terminated.
+inline int LookupEnum(const char **names, const char *name) {
+ for (const char **p = names; *p; p++)
+ if (!strcmp(*p, name))
+ return static_cast<int>(p - names);
+ return -1;
+}
+
+// These macros allow us to layout a struct with a guarantee that they'll end
+// up looking the same on different compilers and platforms.
+// It does this by disallowing the compiler to do any padding, and then
+// does padding itself by inserting extra padding fields that make every
+// element aligned to its own size.
+// Additionally, it manually sets the alignment of the struct as a whole,
+// which is typically its largest element, or a custom size set in the schema
+// by the force_align attribute.
+// These are used in the generated code only.
+
+#if defined(_MSC_VER)
+ #define MANUALLY_ALIGNED_STRUCT(alignment) \
+ __pragma(pack(1)); \
+ struct __declspec(align(alignment))
+ #define STRUCT_END(name, size) \
+ __pragma(pack()); \
+ static_assert(sizeof(name) == size, "compiler breaks packing rules")
+#elif defined(__GNUC__) || defined(__clang__)
+ #define MANUALLY_ALIGNED_STRUCT(alignment) \
+ _Pragma("pack(1)") \
+ struct __attribute__((aligned(alignment)))
+ #define STRUCT_END(name, size) \
+ _Pragma("pack()") \
+ static_assert(sizeof(name) == size, "compiler breaks packing rules")
+#else
+ #error Unknown compiler, please define structure alignment macros
+#endif
+
+// String which identifies the current version of FlatBuffers.
+// flatbuffer_version_string is used by Google developers to identify which
+// applications uploaded to Google Play are using this library. This allows
+// the development team at Google to determine the popularity of the library.
+// How it works: Applications that are uploaded to the Google Play Store are
+// scanned for this version string. We track which applications are using it
+// to measure popularity. You are free to remove it (of course) but we would
+// appreciate if you left it in.
+
+// Weak linkage is culled by VS & doesn't work on cygwin.
+#if !defined(_WIN32) && !defined(__CYGWIN__)
+
+extern volatile __attribute__((weak)) const char *flatbuffer_version_string;
+volatile __attribute__((weak)) const char *flatbuffer_version_string =
+ "FlatBuffers "
+ FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MAJOR) "."
+ FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MINOR) "."
+ FLATBUFFERS_STRING(FLATBUFFERS_VERSION_REVISION);
+
+#endif // !defined(_WIN32) && !defined(__CYGWIN__)
+
+} // namespace flatbuffers
+
+#endif // FLATBUFFERS_H_
diff --git a/flatcc/test/benchmark/benchflatc/run.sh b/flatcc/test/benchmark/benchflatc/run.sh
new file mode 100755
index 0000000..4aff0b8
--- /dev/null
+++ b/flatcc/test/benchmark/benchflatc/run.sh
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../../..
+ROOT=`pwd`
+TMP=build/tmp/test/benchmark/benchflatc
+INC=$ROOT/include
+mkdir -p ${TMP}
+rm -rf ${TMP}/*
+
+CXX=${CXX:-c++}
+cp -r test/benchmark/benchmain/* ${TMP}
+cp -r test/benchmark/benchflatc/* ${TMP}
+#include include at root as it may conflict
+cp -r ${ROOT}/include/flatcc/support ${TMP}
+
+cd ${TMP}
+$CXX -g -std=c++11 benchflatc.cpp -o benchflatc_d -I $INC
+$CXX -O3 -DNDEBUG -std=c++11 benchflatc.cpp -o benchflatc -I $INC
+echo "running flatbench flatc for C++ (debug)"
+./benchflatc_d
+echo "running flatbench flatc for C++ (optimized)"
+./benchflatc
diff --git a/flatcc/test/benchmark/benchflatcc/benchflatcc.c b/flatcc/test/benchmark/benchflatcc/benchflatcc.c
new file mode 100644
index 0000000..682418a
--- /dev/null
+++ b/flatcc/test/benchmark/benchflatcc/benchflatcc.c
@@ -0,0 +1,98 @@
+#define BENCH_TITLE "flatcc for C"
+
+#define BENCHMARK_BUFSIZ 1000
+#define DECLARE_BENCHMARK(BM)\
+ flatcc_builder_t builder, *BM;\
+ BM = &builder;\
+ flatcc_builder_init(BM);
+
+#define CLEAR_BENCHMARK(BM) flatcc_builder_clear(BM);
+
+
+#include "flatbench_builder.h"
+
+#define C(x) FLATBUFFERS_WRAP_NAMESPACE(benchfb_FooBarContainer, x)
+#define FooBar(x) FLATBUFFERS_WRAP_NAMESPACE(benchfb_FooBar, x)
+#define Bar(x) FLATBUFFERS_WRAP_NAMESPACE(benchfb_Bar, x)
+#define Foo(x) FLATBUFFERS_WRAP_NAMESPACE(benchfb_Foo, x)
+#define Enum(x) FLATBUFFERS_WRAP_NAMESPACE(benchfb_Enum, x)
+#define True flatbuffers_true
+#define False flatbuffers_false
+#define StringLen flatbuffers_string_len
+
+int encode(flatcc_builder_t *B, void *buffer, size_t *size)
+{
+ int i, veclen = 3;
+ void *buffer_ok;
+
+ flatcc_builder_reset(B);
+
+ C(start_as_root(B));
+ C(list_start(B));
+ for (i = 0; i < veclen; ++i) {
+ /*
+ * By using push_start instead of push_create we can construct
+ * the sibling field (of Bar type) in-place on the stack,
+ * otherwise we would need to create a temporary Bar struct.
+ */
+ C(list_push_start(B));
+ FooBar(sibling_create(B,
+ 0xABADCAFEABADCAFE + i, 10000 + i, '@' + i, 1000000 + i,
+ 123456 + i, 3.14159f + i, 10000 + i));
+ FooBar(name_create_str(B, "Hello, World!"));
+ FooBar(rating_add(B, 3.1415432432445543543 + i));
+ FooBar(postfix_add(B, '!' + i));
+ C(list_push_end(B));
+ }
+ C(list_end(B));
+ C(location_create_str(B, "https://www.example.com/myurl/"));
+ C(fruit_add(B, Enum(Bananas)));
+ C(initialized_add(B, True));
+ C(end_as_root(B));
+
+ /*
+ * This only works with the default emitter and only if the buffer
+ * is larger enough. Otherwise use whatever custom operation the
+ * emitter provides.
+ */
+ buffer_ok = flatcc_builder_copy_buffer(B, buffer, *size);
+ *size = flatcc_builder_get_buffer_size(B);
+ return !buffer_ok;
+}
+
+int64_t decode(flatcc_builder_t *B, void *buffer, size_t size, int64_t sum)
+{
+ unsigned int i;
+ C(table_t) foobarcontainer;
+ FooBar(vec_t) list;
+ FooBar(table_t) foobar;
+ Bar(struct_t) bar;
+ Foo(struct_t) foo;
+
+ (void)B;
+
+ foobarcontainer = C(as_root(buffer));
+ sum += C(initialized(foobarcontainer));
+ sum += StringLen(C(location(foobarcontainer)));
+ sum += C(fruit(foobarcontainer));
+ list = C(list(foobarcontainer));
+ for (i = 0; i < FooBar(vec_len(list)); ++i) {
+ foobar = FooBar(vec_at(list, i));
+ sum += StringLen(FooBar(name(foobar)));
+ sum += FooBar(postfix(foobar));
+ sum += (int64_t)FooBar(rating(foobar));
+ bar = FooBar(sibling(foobar));
+ sum += (int64_t)Bar(ratio(bar));
+ sum += Bar(size(bar));
+ sum += Bar(time(bar));
+ foo = Bar(parent(bar));
+ sum += Foo(count(foo));
+ sum += Foo(id(foo));
+ sum += Foo(length(foo));
+ sum += Foo(prefix(foo));
+ }
+ return sum + 2 * sum;
+}
+
+/* Copy to same folder before compilation or use include directive. */
+#include "benchmain.h"
diff --git a/flatcc/test/benchmark/benchflatcc/run.sh b/flatcc/test/benchmark/benchflatcc/run.sh
new file mode 100755
index 0000000..2d63dae
--- /dev/null
+++ b/flatcc/test/benchmark/benchflatcc/run.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../../..
+ROOT=`pwd`
+TMP=build/tmp/test/benchmark/benchflatcc
+${ROOT}/scripts/build.sh
+mkdir -p ${TMP}
+rm -rf ${TMP}/*
+#bin/flatcc -a -o ${TMP} test/benchmark/schema/flatbench.fbs
+bin/flatcc --json-printer -a -o ${TMP} test/benchmark/schema/flatbench.fbs
+
+CC=${CC:-cc}
+cp -r test/benchmark/benchmain/* ${TMP}
+cp -r test/benchmark/benchflatcc/* ${TMP}
+cd ${TMP}
+$CC -g -std=c11 -I ${ROOT}/include benchflatcc.c \
+ ${ROOT}/lib/libflatccrt_d.a -o benchflatcc_d
+$CC -O3 -DNDEBUG -std=c11 -I ${ROOT}/include benchflatcc.c \
+ ${ROOT}/lib/libflatccrt.a -o benchflatcc
+echo "running flatbench flatcc for C (debug)"
+./benchflatcc_d
+echo "running flatbench flatcc for C (optimized)"
+./benchflatcc
diff --git a/flatcc/test/benchmark/benchflatccjson/benchflatccjson.c b/flatcc/test/benchmark/benchflatccjson/benchflatccjson.c
new file mode 100644
index 0000000..26ee291
--- /dev/null
+++ b/flatcc/test/benchmark/benchflatccjson/benchflatccjson.c
@@ -0,0 +1,182 @@
+#define BENCH_TITLE "flatcc json parser and printer for C"
+
+/*
+ * NOTE:
+ *
+ * Using dtoa_grisu3.c over sprintf("%.17g") more than doubles the
+ * encoding performance of this benchmark from 3.3 us/op to 1.3 us/op.
+ */
+
+#include <stdlib.h>
+
+/*
+ * Builder is only needed so we can create the initial buffer to encode
+ * json from, but it also includes the reader which is needed calculate
+ * the decoded checksum after parsing.
+ */
+#include "flatbench_builder.h"
+
+#include "flatbench_json_parser.h"
+#include "flatbench_json_printer.h"
+
+#define C(x) FLATBUFFERS_WRAP_NAMESPACE(benchfb_FooBarContainer, x)
+#define FooBar(x) FLATBUFFERS_WRAP_NAMESPACE(benchfb_FooBar, x)
+#define Bar(x) FLATBUFFERS_WRAP_NAMESPACE(benchfb_Bar, x)
+#define Foo(x) FLATBUFFERS_WRAP_NAMESPACE(benchfb_Foo, x)
+#define Enum(x) FLATBUFFERS_WRAP_NAMESPACE(benchfb_Enum, x)
+#define True flatbuffers_true
+#define False flatbuffers_false
+#define StringLen flatbuffers_string_len
+
+typedef struct flatcc_jsonbench {
+ flatcc_builder_t builder;
+ flatcc_json_parser_t parser;
+ flatcc_json_printer_t printer;
+
+ /* Holds the source data to print (encode) from. */
+ char bin[1000];
+ size_t bin_size;
+ /* Extra buffer for extracting the parse (decoded) into. */
+ char decode_buffer[1000];
+ /*
+ * The target encode / source decode buffer is provided by the
+ * benchmark framework.
+ */
+} flatcc_jsonbench_t;
+
+int flatcc_jsonbench_init(flatcc_jsonbench_t *bench)
+{
+ int i, veclen = 3;
+ void *buffer_ok;
+ flatcc_builder_t *B = &bench->builder;
+
+ flatcc_builder_init(B);
+
+ /* Generate the data needed to print from, just once. */
+ C(start_as_root(B));
+ C(list_start(B));
+ for (i = 0; i < veclen; ++i) {
+ /*
+ * By using push_start instead of push_create we can construct
+ * the sibling field (of Bar type) in-place on the stack,
+ * otherwise we would need to create a temporary Bar struct.
+ */
+ C(list_push_start(B));
+ FooBar(sibling_create(B,
+ 0xABADCAFEABADCAFE + i, 10000 + i, '@' + i, 1000000 + i,
+ 123456 + i, 3.14159f + i, 10000 + i));
+ FooBar(name_create_str(B, "Hello, World!"));
+ FooBar(rating_add(B, 3.1415432432445543543 + i));
+ FooBar(postfix_add(B, '!' + i));
+ C(list_push_end(B));
+ }
+ C(list_end(B));
+ C(location_create_str(B, "https://www.example.com/myurl/"));
+ C(fruit_add(B, Enum(Bananas)));
+ C(initialized_add(B, True));
+ C(end_as_root(B));
+
+ buffer_ok = flatcc_builder_copy_buffer(B, bench->bin, sizeof(bench->bin));
+ bench->bin_size = flatcc_builder_get_buffer_size(B);
+
+ flatcc_builder_reset(&bench->builder);
+ return !buffer_ok;
+}
+
+void flatcc_jsonbench_clear(flatcc_jsonbench_t *bench)
+{
+ flatcc_json_printer_clear(&bench->printer);
+ flatcc_builder_clear(&bench->builder);
+ // parser does not need to be cleared.
+}
+
+/*
+ * For a buffer large enough to hold encoded representation.
+ *
+ * 1000 is enough for compact json, but for pretty printing we must up.
+ */
+#define BENCHMARK_BUFSIZ 10000
+
+/* Interface to main benchmark logic. */
+#define DECLARE_BENCHMARK(BM) \
+ flatcc_jsonbench_t flatcc_jsonbench, *BM; \
+ BM = &flatcc_jsonbench; \
+ flatcc_jsonbench_init(BM);
+
+#define CLEAR_BENCHMARK(BM) flatcc_jsonbench_clear(BM);
+
+int encode(flatcc_jsonbench_t *bench, void *buffer, size_t *size)
+{
+ int ret;
+
+ flatcc_json_printer_init_buffer(&bench->printer, buffer, *size);
+ /*
+ * Normally avoid setting indentation - this yields compact
+ * spaceless json which is what you want in resource critical
+ * parsing and printing. But - it doesn't get that much slower,
+ * so interesting to benchmark. Improve by enabling SSE4_2, but
+ * generally not worth the trouble.
+ */
+ //flatcc_json_printer_set_indent(&bench->printer, 8);
+
+ /*
+ * Unquoted makes it slightly slower, noenum hardly makes a
+ * difference - for this particular data set.
+ */
+ // flatcc_json_printer_set_noenum(&bench->printer, 1);
+ // flatcc_json_printer_set_unquoted(&bench->printer, 1);
+ ret = flatbench_print_json(&bench->printer, bench->bin, bench->bin_size);
+ *size = flatcc_json_printer_flush(&bench->printer);
+
+ return ret < 0 ? ret : 0;
+}
+
+int64_t decode(flatcc_jsonbench_t *bench, void *buffer, size_t size, int64_t sum)
+{
+ unsigned int i;
+ int ret;
+ flatcc_builder_t *B = &bench->builder;
+
+ C(table_t) foobarcontainer;
+ FooBar(vec_t) list;
+ FooBar(table_t) foobar;
+ Bar(struct_t) bar;
+ Foo(struct_t) foo;
+
+ flatcc_builder_reset(B);
+ ret = flatbench_parse_json(B, &bench->parser, buffer, size, 0);
+ if (ret) {
+ return 0;
+ }
+ if (!flatcc_builder_copy_buffer(B,
+ bench->decode_buffer, sizeof(bench->decode_buffer))) {
+ return 0;
+ }
+
+ /* Traverse parsed result to calculate checksum. */
+
+ foobarcontainer = C(as_root(bench->decode_buffer));
+ sum += C(initialized(foobarcontainer));
+ sum += StringLen(C(location(foobarcontainer)));
+ sum += C(fruit(foobarcontainer));
+ list = C(list(foobarcontainer));
+ for (i = 0; i < FooBar(vec_len(list)); ++i) {
+ foobar = FooBar(vec_at(list, i));
+ sum += StringLen(FooBar(name(foobar)));
+ sum += FooBar(postfix(foobar));
+ sum += (int64_t)FooBar(rating(foobar));
+ bar = FooBar(sibling(foobar));
+ sum += (int64_t)Bar(ratio(bar));
+ sum += Bar(size(bar));
+ sum += Bar(time(bar));
+ foo = Bar(parent(bar));
+ sum += Foo(count(foo));
+ sum += Foo(id(foo));
+ sum += Foo(length(foo));
+ sum += Foo(prefix(foo));
+ }
+ return sum + 2 * sum;
+}
+
+/* Copy to same folder before compilation or use include directive. */
+#include "benchmain.h"
diff --git a/flatcc/test/benchmark/benchflatccjson/run.sh b/flatcc/test/benchmark/benchflatccjson/run.sh
new file mode 100755
index 0000000..c24e02e
--- /dev/null
+++ b/flatcc/test/benchmark/benchflatccjson/run.sh
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../../..
+ROOT=`pwd`
+TMP=build/tmp/test/benchmark/benchflatccjson
+${ROOT}/scripts/build.sh
+mkdir -p ${TMP}
+rm -rf ${TMP}/*
+bin/flatcc --json -crw -o ${TMP} test/benchmark/schema/flatbench.fbs
+
+CC=${CC:-cc}
+cp -r test/benchmark/benchmain/* ${TMP}
+cp -r test/benchmark/benchflatccjson/* ${TMP}
+cd ${TMP}
+$CC -g -std=c11 -I ${ROOT}/include benchflatccjson.c \
+ ${ROOT}/lib/libflatccrt_d.a -o benchflatccjson_d
+$CC -O3 -DNDEBUG -std=c11 -I ${ROOT}/include benchflatccjson.c \
+ ${ROOT}/lib/libflatccrt.a -o benchflatccjson
+echo "running flatbench flatcc json parse and print for C (debug)"
+./benchflatccjson_d
+echo "running flatbench flatcc json parse and print for C (optimized)"
+./benchflatccjson
diff --git a/flatcc/test/benchmark/benchmain/benchmain.h b/flatcc/test/benchmark/benchmain/benchmain.h
new file mode 100644
index 0000000..f29c548
--- /dev/null
+++ b/flatcc/test/benchmark/benchmain/benchmain.h
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "flatcc/support/elapsed.h"
+
+#ifdef NDEBUG
+#define COMPILE_TYPE "(optimized)"
+#else
+#define COMPILE_TYPE "(debug)"
+#endif
+
+int main(int argc, char *argv[])
+{
+ /*
+ * The size must be large enough to hold different representations,
+ * including printed json, but we know the printed json is close to
+ * 700 bytes.
+ */
+ const int bufsize = BENCHMARK_BUFSIZ, rep = 1000000;
+ void *buf;
+ size_t size, old_size;
+ double t1, t2, t3;
+ /* Use volatie to prevent over optimization. */
+ volatile int64_t total = 0;
+ int i, ret = 0;
+ DECLARE_BENCHMARK(BM);
+
+ buf = malloc(bufsize);
+
+ /* Warmup to preallocate internal buffers. */
+ size = bufsize;
+ old_size = size;
+ encode(BM, buf, &size);
+ t1 = elapsed_realtime();
+ for (i = 0; i < rep; ++i) {
+ size = bufsize;
+ ret |= encode(BM, buf, &size);
+ assert(ret == 0);
+ if (i > 0 && size != old_size) {
+ printf("abort on inconsistent encoding size\n");
+ goto done;
+ }
+ old_size = size;
+ }
+ t2 = elapsed_realtime();
+ for (i = 0; i < rep; ++i) {
+ total = decode(BM, buf, size, total);
+ }
+ t3 = elapsed_realtime();
+ if (total != -8725036910085654784LL) {
+ printf("ABORT ON CHECKSUM FAILURE\n");
+ goto done;
+ }
+ printf("----\n");
+ show_benchmark(BENCH_TITLE " encode " COMPILE_TYPE, t1, t2, size, rep, "1M");
+ printf("\n");
+ show_benchmark(BENCH_TITLE " decode/traverse " COMPILE_TYPE, t2, t3, size, rep, "1M");
+ printf("----\n");
+ ret = 0;
+done:
+ if (buf) {
+ free(buf);
+ }
+ CLEAR_BENCHMARK(BM);
+ return 0;
+}
diff --git a/flatcc/test/benchmark/benchout-osx.txt b/flatcc/test/benchmark/benchout-osx.txt
new file mode 100644
index 0000000..ab0ec63
--- /dev/null
+++ b/flatcc/test/benchmark/benchout-osx.txt
@@ -0,0 +1,169 @@
+running all benchmarks (raw, flatc C++, flatcc C)
+building and benchmarking raw strucs
+running flatbench for raw C structs (debug)
+----
+operation: flatbench for raw C structs encode (debug)
+elapsed time: 0.106 (s)
+iterations: 1000000
+size: 312 (bytes)
+bandwidth: 2956.926 (MB/s)
+throughput in ops per sec: 9477325.499
+throughput in 1M ops per sec: 9.477
+time per op: 105.515 (ns)
+
+operation: flatbench for raw C structs decode/traverse (debug)
+elapsed time: 0.074 (s)
+iterations: 1000000
+size: 312 (bytes)
+bandwidth: 4222.379 (MB/s)
+throughput in ops per sec: 13533264.765
+throughput in 1M ops per sec: 13.533
+time per op: 73.892 (ns)
+----
+running flatbench for raw C structs (optimized)
+----
+operation: flatbench for raw C structs encode (optimized)
+elapsed time: 0.052 (s)
+iterations: 1000000
+size: 312 (bytes)
+bandwidth: 5991.474 (MB/s)
+throughput in ops per sec: 19203441.257
+throughput in 1M ops per sec: 19.203
+time per op: 52.074 (ns)
+
+operation: flatbench for raw C structs decode/traverse (optimized)
+elapsed time: 0.012 (s)
+iterations: 1000000
+size: 312 (bytes)
+bandwidth: 26342.452 (MB/s)
+throughput in ops per sec: 84430935.495
+throughput in 1M ops per sec: 84.431
+time per op: 11.844 (ns)
+----
+building and benchmarking flatc generated C++
+running flatbench flatc for C++ (debug)
+----
+operation: flatc for C++ encode (debug)
+elapsed time: 5.338 (s)
+iterations: 1000000
+size: 344 (bytes)
+bandwidth: 64.444 (MB/s)
+throughput in ops per sec: 187337.801
+throughput in 1M ops per sec: 0.187
+time per op: 5.338 (us)
+
+operation: flatc for C++ decode/traverse (debug)
+elapsed time: 0.798 (s)
+iterations: 1000000
+size: 344 (bytes)
+bandwidth: 430.966 (MB/s)
+throughput in ops per sec: 1252809.425
+throughput in 1M ops per sec: 1.253
+time per op: 798.206 (ns)
+----
+running flatbench flatc for C++ (optimized)
+----
+operation: flatc for C++ encode (optimized)
+elapsed time: 0.716 (s)
+iterations: 1000000
+size: 344 (bytes)
+bandwidth: 480.630 (MB/s)
+throughput in ops per sec: 1397180.769
+throughput in 1M ops per sec: 1.397
+time per op: 715.727 (ns)
+
+operation: flatc for C++ decode/traverse (optimized)
+elapsed time: 0.029 (s)
+iterations: 1000000
+size: 344 (bytes)
+bandwidth: 12058.751 (MB/s)
+throughput in ops per sec: 35054509.763
+throughput in 1M ops per sec: 35.055
+time per op: 28.527 (ns)
+----
+building and benchmarking flatcc generated C
+[1/1] Linking C executable ../../bin/flatcc_d
+[1/1] Linking C executable ../../bin/flatcc
+running flatbench flatcc for C (debug)
+----
+operation: flatcc for C encode (debug)
+elapsed time: 1.975 (s)
+iterations: 1000000
+size: 336 (bytes)
+bandwidth: 170.157 (MB/s)
+throughput in ops per sec: 506418.346
+throughput in 1M ops per sec: 0.506
+time per op: 1.975 (us)
+
+operation: flatcc for C decode/traverse (debug)
+elapsed time: 0.566 (s)
+iterations: 1000000
+size: 336 (bytes)
+bandwidth: 593.408 (MB/s)
+throughput in ops per sec: 1766094.864
+throughput in 1M ops per sec: 1.766
+time per op: 566.221 (ns)
+----
+running flatbench flatcc for C (optimized)
+----
+operation: flatcc for C encode (optimized)
+elapsed time: 0.606 (s)
+iterations: 1000000
+size: 336 (bytes)
+bandwidth: 554.266 (MB/s)
+throughput in ops per sec: 1649601.539
+throughput in 1M ops per sec: 1.650
+time per op: 606.207 (ns)
+
+operation: flatcc for C decode/traverse (optimized)
+elapsed time: 0.029 (s)
+iterations: 1000000
+size: 336 (bytes)
+bandwidth: 11740.452 (MB/s)
+throughput in ops per sec: 34941821.867
+throughput in 1M ops per sec: 34.942
+time per op: 28.619 (ns)
+----
+building and benchmarking flatcc json generated C
+[1/1] Linking C executable ../../bin/flatcc_d
+[1/1] Linking C executable ../../bin/flatcc
+running flatbench flatcc json parse and print for C (debug)
+----
+operation: flatcc json parser and printer for C encode (debug)
+elapsed time: 4.633 (s)
+iterations: 1000000
+size: 722 (bytes)
+bandwidth: 155.855 (MB/s)
+throughput in ops per sec: 215866.116
+throughput in 1M ops per sec: 0.216
+time per op: 4.633 (us)
+
+operation: flatcc json parser and printer for C decode/traverse (debug)
+elapsed time: 6.957 (s)
+iterations: 1000000
+size: 722 (bytes)
+bandwidth: 103.781 (MB/s)
+throughput in ops per sec: 143740.882
+throughput in 1M ops per sec: 0.144
+time per op: 6.957 (us)
+----
+running flatbench flatcc json parse and print for C (optimized)
+----
+operation: flatcc json parser and printer for C encode (optimized)
+elapsed time: 1.358 (s)
+iterations: 1000000
+size: 722 (bytes)
+bandwidth: 531.528 (MB/s)
+throughput in ops per sec: 736188.912
+throughput in 1M ops per sec: 0.736
+time per op: 1.358 (us)
+
+operation: flatcc json parser and printer for C decode/traverse (optimized)
+elapsed time: 2.224 (s)
+iterations: 1000000
+size: 722 (bytes)
+bandwidth: 324.572 (MB/s)
+throughput in ops per sec: 449546.295
+throughput in 1M ops per sec: 0.450
+time per op: 2.224 (us)
+----
diff --git a/flatcc/test/benchmark/benchout-ubuntu.txt b/flatcc/test/benchmark/benchout-ubuntu.txt
new file mode 100644
index 0000000..b551901
--- /dev/null
+++ b/flatcc/test/benchmark/benchout-ubuntu.txt
@@ -0,0 +1,169 @@
+running all benchmarks (raw, flatc C++, flatcc C)
+building and benchmarking raw strucs
+running flatbench for raw C structs (debug)
+----
+operation: flatbench for raw C structs encode (debug)
+elapsed time: 0.065 (s)
+iterations: 1000000
+size: 312 (bytes)
+bandwidth: 4781.609 (MB/s)
+throughput in ops per sec: 15325670.498
+throughput in 1M ops per sec: 15.326
+time per op: 65.250 (ns)
+
+operation: flatbench for raw C structs decode/traverse (debug)
+elapsed time: 0.063 (s)
+iterations: 1000000
+size: 312 (bytes)
+bandwidth: 4931.325 (MB/s)
+throughput in ops per sec: 15805528.774
+throughput in 1M ops per sec: 15.806
+time per op: 63.269 (ns)
+----
+running flatbench for raw C structs (optimized)
+----
+operation: flatbench for raw C structs encode (optimized)
+elapsed time: 0.030 (s)
+iterations: 1000000
+size: 312 (bytes)
+bandwidth: 10521.346 (MB/s)
+throughput in ops per sec: 33722263.438
+throughput in 1M ops per sec: 33.722
+time per op: 29.654 (ns)
+
+operation: flatbench for raw C structs decode/traverse (optimized)
+elapsed time: 0.012 (s)
+iterations: 1000000
+size: 312 (bytes)
+bandwidth: 25409.235 (MB/s)
+throughput in ops per sec: 81439856.666
+throughput in 1M ops per sec: 81.440
+time per op: 12.279 (ns)
+----
+building and benchmarking flatc generated C++
+running flatbench flatc for C++ (debug)
+----
+operation: flatc for C++ encode (debug)
+elapsed time: 5.577 (s)
+iterations: 1000000
+size: 344 (bytes)
+bandwidth: 61.679 (MB/s)
+throughput in ops per sec: 179300.638
+throughput in 1M ops per sec: 0.179
+time per op: 5.577 (us)
+
+operation: flatc for C++ decode/traverse (debug)
+elapsed time: 0.892 (s)
+iterations: 1000000
+size: 344 (bytes)
+bandwidth: 385.522 (MB/s)
+throughput in ops per sec: 1120703.084
+throughput in 1M ops per sec: 1.121
+time per op: 892.297 (ns)
+----
+running flatbench flatc for C++ (optimized)
+----
+operation: flatc for C++ encode (optimized)
+elapsed time: 0.516 (s)
+iterations: 1000000
+size: 344 (bytes)
+bandwidth: 667.104 (MB/s)
+throughput in ops per sec: 1939254.783
+throughput in 1M ops per sec: 1.939
+time per op: 515.662 (ns)
+
+operation: flatc for C++ decode/traverse (optimized)
+elapsed time: 0.030 (s)
+iterations: 1000000
+size: 344 (bytes)
+bandwidth: 11479.294 (MB/s)
+throughput in ops per sec: 33370040.378
+throughput in 1M ops per sec: 33.370
+time per op: 29.967 (ns)
+----
+building and benchmarking flatcc generated C
+[1/1] Linking C executable ../../bin/flatcc_d
+[1/1] Linking C executable ../../bin/flatcc
+running flatbench flatcc for C (debug)
+----
+operation: flatcc for C encode (debug)
+elapsed time: 1.893 (s)
+iterations: 1000000
+size: 336 (bytes)
+bandwidth: 177.461 (MB/s)
+throughput in ops per sec: 528159.065
+throughput in 1M ops per sec: 0.528
+time per op: 1.893 (us)
+
+operation: flatcc for C decode/traverse (debug)
+elapsed time: 0.643 (s)
+iterations: 1000000
+size: 336 (bytes)
+bandwidth: 522.374 (MB/s)
+throughput in ops per sec: 1554685.277
+throughput in 1M ops per sec: 1.555
+time per op: 643.217 (ns)
+----
+running flatbench flatcc for C (optimized)
+----
+operation: flatcc for C encode (optimized)
+elapsed time: 0.531 (s)
+iterations: 1000000
+size: 336 (bytes)
+bandwidth: 632.498 (MB/s)
+throughput in ops per sec: 1882434.440
+throughput in 1M ops per sec: 1.882
+time per op: 531.227 (ns)
+
+operation: flatcc for C decode/traverse (optimized)
+elapsed time: 0.028 (s)
+iterations: 1000000
+size: 336 (bytes)
+bandwidth: 12200.879 (MB/s)
+throughput in ops per sec: 36312139.148
+throughput in 1M ops per sec: 36.312
+time per op: 27.539 (ns)
+----
+building and benchmarking flatcc json generated C
+[1/1] Linking C executable ../../bin/flatcc_d
+[1/1] Linking C executable ../../bin/flatcc
+running flatbench flatcc json parse and print for C (debug)
+----
+operation: flatcc json parser and printer for C encode (debug)
+elapsed time: 3.931 (s)
+iterations: 1000000
+size: 722 (bytes)
+bandwidth: 183.674 (MB/s)
+throughput in ops per sec: 254396.609
+throughput in 1M ops per sec: 0.254
+time per op: 3.931 (us)
+
+operation: flatcc json parser and printer for C decode/traverse (debug)
+elapsed time: 6.874 (s)
+iterations: 1000000
+size: 722 (bytes)
+bandwidth: 105.031 (MB/s)
+throughput in ops per sec: 145472.171
+throughput in 1M ops per sec: 0.145
+time per op: 6.874 (us)
+----
+running flatbench flatcc json parse and print for C (optimized)
+----
+operation: flatcc json parser and printer for C encode (optimized)
+elapsed time: 1.210 (s)
+iterations: 1000000
+size: 722 (bytes)
+bandwidth: 596.609 (MB/s)
+throughput in ops per sec: 826328.137
+throughput in 1M ops per sec: 0.826
+time per op: 1.210 (us)
+
+operation: flatcc json parser and printer for C decode/traverse (optimized)
+elapsed time: 1.772 (s)
+iterations: 1000000
+size: 722 (bytes)
+bandwidth: 407.372 (MB/s)
+throughput in ops per sec: 564227.736
+throughput in 1M ops per sec: 0.564
+time per op: 1.772 (us)
+----
diff --git a/flatcc/test/benchmark/benchraw/benchraw.c b/flatcc/test/benchmark/benchraw/benchraw.c
new file mode 100644
index 0000000..fd6a9ea
--- /dev/null
+++ b/flatcc/test/benchmark/benchraw/benchraw.c
@@ -0,0 +1,117 @@
+#define BENCH_TITLE "flatbench for raw C structs"
+
+#define BENCHMARK_BUFSIZ 1000
+#define DECLARE_BENCHMARK(BM)\
+ void *BM = 0
+#define CLEAR_BENCHMARK(BM)
+
+#include <string.h>
+#include <stdint.h>
+
+#define STRING_LEN 32
+#define VEC_LEN 3
+#define fb_bool uint8_t
+
+enum Enum { Apples, Pears, Bananas };
+
+struct Foo {
+ int64_t id;
+ short count;
+ char prefix;
+ int length;
+};
+
+struct Bar {
+ struct Foo parent;
+ int time;
+ float ratio;
+ unsigned short size;
+};
+
+struct FooBar {
+ struct Bar sibling;
+ int name_len;
+ char name[STRING_LEN];
+ double rating;
+ unsigned char postfix;
+};
+
+struct FooBarContainer {
+ struct FooBar list[VEC_LEN];
+ fb_bool initialized;
+ enum Enum fruit;
+ int location_len;
+ char location[STRING_LEN];
+};
+
+int encode(void *bench, void *buffer, size_t *size)
+{
+ int i;
+ struct FooBarContainer fbc;
+ struct FooBar *foobar;
+ struct Foo *foo;
+ struct Bar *bar;
+
+ (void)bench;
+
+ strcpy(fbc.location, "https://www.example.com/myurl/");
+ fbc.location_len = strlen(fbc.location);
+ fbc.fruit = Bananas;
+ fbc.initialized = 1;
+ for (i = 0; i < VEC_LEN; ++i) {
+ foobar = &fbc.list[i];
+ foobar->rating = 3.1415432432445543543 + i;
+ foobar->postfix = '!' + i;
+ strcpy(foobar->name, "Hello, World!");
+ foobar->name_len = strlen(foobar->name);
+ bar = &foobar->sibling;
+ bar->ratio = 3.14159f + i;
+ bar->size = 10000 + i;
+ bar->time = 123456 + i;
+ foo = &bar->parent;
+ foo->id = 0xABADCAFEABADCAFE + i;
+ foo->count = 10000 + i;
+ foo->length = 1000000 + i;
+ foo->prefix = '@' + i;
+ }
+ if (*size < sizeof(struct FooBarContainer)) {
+ return -1;
+ }
+ *size = sizeof(struct FooBarContainer);
+ memcpy(buffer, &fbc, *size);
+ return 0;
+}
+
+int64_t decode(void *bench, void *buffer, size_t size, int64_t sum)
+{
+ int i;
+ struct FooBarContainer *foobarcontainer;
+ struct FooBar *foobar;
+ struct Foo *foo;
+ struct Bar *bar;
+
+ (void)bench;
+
+ foobarcontainer = buffer;
+ sum += foobarcontainer->initialized;
+ sum += foobarcontainer->location_len;
+ sum += foobarcontainer->fruit;
+ for (i = 0; i < VEC_LEN; ++i) {
+ foobar = &foobarcontainer->list[i];
+ sum += foobar->name_len;
+ sum += foobar->postfix;
+ sum += (int64_t)foobar->rating;
+ bar = &foobar->sibling;
+ sum += (int64_t)bar->ratio;
+ sum += bar->size;
+ sum += bar->time;
+ foo = &bar->parent;
+ sum += foo->count;
+ sum += foo->id;
+ sum += foo->length;
+ sum += foo->prefix;
+ }
+ return sum + 2 * sum;
+}
+
+#include "benchmain.h"
diff --git a/flatcc/test/benchmark/benchraw/run.sh b/flatcc/test/benchmark/benchraw/run.sh
new file mode 100755
index 0000000..13e3333
--- /dev/null
+++ b/flatcc/test/benchmark/benchraw/run.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../../..
+ROOT=`pwd`
+TMP=build/tmp/test/benchmark/benchraw
+INC=$ROOT/include
+mkdir -p ${TMP}
+rm -rf ${TMP}/*
+
+CC=${CC:-cc}
+cp -r test/benchmark/benchmain/* ${TMP}
+cp -r test/benchmark/benchraw/* ${TMP}
+
+cd ${TMP}
+$CC -g benchraw.c -o benchraw_d -I $INC
+$CC -O3 -DNDEBUG benchraw.c -o benchraw -I $INC
+echo "running flatbench for raw C structs (debug)"
+./benchraw_d
+echo "running flatbench for raw C structs (optimized)"
+./benchraw
diff --git a/flatcc/test/benchmark/schema/flatbench.fbs b/flatcc/test/benchmark/schema/flatbench.fbs
new file mode 100644
index 0000000..34bd2df
--- /dev/null
+++ b/flatcc/test/benchmark/schema/flatbench.fbs
@@ -0,0 +1,37 @@
+// trying to represent a typical mix of datatypes:
+// 1 array of 3 elements, each element: 1 string, 3 nested objects, 9 scalars
+// root element has the array, additional string and an enum
+
+namespace benchfb;
+
+enum Enum : short { Apples, Pears, Bananas }
+
+struct Foo {
+ id:ulong;
+ count:short;
+ prefix:byte;
+ length:uint;
+}
+
+struct Bar {
+ parent:Foo;
+ time:int;
+ ratio:float;
+ size:ushort;
+}
+
+table FooBar {
+ sibling:Bar;
+ name:string;
+ rating:double;
+ postfix:ubyte;
+}
+
+table FooBarContainer {
+ list:[FooBar]; // 3 copies of the above
+ initialized:bool;
+ fruit:Enum;
+ location:string;
+}
+
+root_type FooBarContainer;
diff --git a/flatcc/test/cgen_test/CMakeLists.txt b/flatcc/test/cgen_test/CMakeLists.txt
new file mode 100644
index 0000000..2edc040
--- /dev/null
+++ b/flatcc/test/cgen_test/CMakeLists.txt
@@ -0,0 +1,43 @@
+include(CTest)
+
+include_directories (
+ "${PROJECT_SOURCE_DIR}/include"
+)
+
+add_executable(cgen_test
+ cgen_test.c
+)
+
+target_link_libraries(cgen_test
+ flatcc
+)
+
+add_test(cgen_test cgen_test${CMAKE_EXECUTABLE_SUFFIX})
+
+
+# Compilation of the generated code tests many import edge cases
+# in the parser and code generator but due to CMake limitations,
+# custom target dependencies only work for Make build targets.
+#
+# expansion of flags results in quotes the compiler won't eat,
+# separating arguments should fix this, but not sure how portable it is.
+# see also http://stackoverflow.com/questions/9870162/avoid-quoting-in-cmake-add-custom-command
+separate_arguments(CUSTOM_C_FLAGS UNIX_COMMAND "${CMAKE_C_FLAGS}")
+
+add_custom_target(test_generated
+ COMMAND ./cgen_test${CMAKE_EXECUTABLE_SUFFIX} > test_generated${CMAKE_EXECUTABLE_SUFFIX}.c
+ COMMAND ${CMAKE_C_COMPILER} ${CUSTOM_C_FLAGS} test_generated${CMAKE_EXECUTABLE_SUFFIX}.c -c
+ -I${CMAKE_SOURCE_DIR}/include WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test/cgen_test
+ )
+add_dependencies(test_generated cgen_test)
+
+# Might be related to:
+# https://cmake.org/Bug/view.php?id=14963#c37230
+# https://github.com/ninja-build/ninja/issues/760
+if(${CMAKE_MAKE_PROGRAM} MATCHES make)
+# this is now also broken for make - the system include path is not
+# visible so build fails on <assert.h> not found in the custom build
+# stage where CMAKE_C_COMPILER uses a compiler call that has this
+# behavior
+#add_test(test_generated ${CMAKE_MAKE_PROGRAM} test_generated)
+endif(${CMAKE_MAKE_PROGRAM} MATCHES make)
diff --git a/flatcc/test/cgen_test/cgen_test.c b/flatcc/test/cgen_test/cgen_test.c
new file mode 100644
index 0000000..6d58ed1
--- /dev/null
+++ b/flatcc/test/cgen_test/cgen_test.c
@@ -0,0 +1,163 @@
+/*
+ * Parse and verify schema complex nonsense schema, then generate common
+ * and schema specific files for reader and builder, all outfileenated to
+ * stdout, followed by the schema source in a comment.
+ *
+ * Notes:
+ *
+ * Later type declarations are visible in the same file regardles of
+ * namespace. Earlier type declarations in included files are also
+ * visible. Included files cannot see types in later included or
+ * including files. (We do not test file inclusion here though). This
+ * behaviour is chosen both because it seems sensible and because it
+ * allows for independent file generation of each schema file.
+ *
+ * Googles flatc compiler does in some cases allow for later references
+ * e.g. Monster reference itself, but require structs to be ordered.
+ * We do not required that so structs are sorted topologically before
+ * being given to the code generator. Tables use multiple layers of
+ * forward declarations in the generated C code.
+ *
+ * Note: The google flatc compiler does support multiple namspaces
+ * but apparently it not currently generate C++ correctly. As such is
+ * not well-defined what exactly the meaning of a namespace is. We have
+ * chosen it to mean everything from the definition until the next in
+ * the same file. Anything before a namespace declaration is global.
+ * Namespace declarations only affect the local file. Namespace
+ * references allow spaces between dots when used as an operator, but
+ * not when used as a string attribute - this matches flatc behavior.
+ * When a namespace prefix is not specified, but the current scope is
+ * searched first, then the global scope.
+ *
+ * There is no way to specify the global scope once a namespace has been
+ * declared, other than starting a new file. It could be considered to
+ * allow for "namespace ;".
+ *
+ * The flatc compiler does not allow a namespace prefixes in root_type,
+ * but that is likely an oversight. We support it.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include "flatcc/flatcc.h"
+
+int main(void)
+{
+ const char *name = "../xyzzy.fbs";
+
+ char input[] =
+ "\t//buffers do not support include statements\n"
+ "//\tinclude \"foobar.fbs\";\n"
+ "// in flatc, only one field can have a key, but we have no issues\n"
+ "// as long as the vector is sorted accordingly. The first key gets\n"
+ "// gets a shorter find alias method.\n"
+ "// (all scalar vectors can also be searched - they have find defined)\n"
+ "/* block comments are also allowed.\n */\n"
+ "//////////////////////////////////////////////////////////\n"
+ "//////////////////////////////////////////////////////////\n"
+ "table point { x : float (key); y: float; z: float (key); }\n"
+ "namespace mystic;\n"
+ "/************ ANOTHER DOC CASE *************/\n"
+ "table island { lattitude : int; longitude : int; }\n"
+ " /// There are two different point tables\n"
+ "// we test multi line doc comments here - this line should be ignored.\n"
+ " /// - one in each name space.\n"
+ "table point { interest: agent; blank: string; geo: mystic.island; }\n"
+ "enum agent:int { lot, pirate, vessel, navy, parrot }\n"
+ "\tnamespace the;\n"
+ "//root_type point;\n"
+ "attribute \"foo\";\n"
+ "//attribute \"\"; // ensure empty strings are accepted.\n"
+ "/// shade is for CG applications\n"
+ "struct shade (force_align:2) { x: byte; y: byte; z: byte;\n"
+ "/// alpha is unsigned!\n"
+ "alpha: ubyte (key); }\n"
+ "/// the.ui is a union\n"
+ "///\n"
+ "/// We got one blank comment line above.\n"
+ "union u1 { /// Note that the.point is different from mystic.point in other namespace.\n"
+ "point\n"
+ "= /// meaningless doc comment that should be stripped\n"
+ "2, foo = 4, mystic.island = 17, } enum e1:short { z = -2, one , two , three = 3, }\n"
+ "// key on enum not supported by flatc\n"
+ "table foo { m: u1; e: e1 = z (key); x : int = mystic.agent.vessel; interest: mystic.agent = pirate; strange: mystic.agent = flags2.zulu; }\n"
+ "// Unknown attributes can be repeated with varying types since behavior is unspecified.\n"
+ "enum flags : short (bit_flags, \n"
+ "/// foos are plentiful - here it is an enum of value 42\n"
+ "foo: 42, foo, foo: \"hello\") { f1 = 1, f2 = 13, f3 }\n"
+ "enum flags2 : uint (bit_flags) { zulu, alpha, bravo, charlie, delta, echo, foxtrot }\n"
+ "/// A boolean enum - all enums must be type.\n"
+ "enum confirm : bool { no, yes }\n"
+ "// enums are not formally permitted in structs, but can be enabled.\n"
+ "// This is advanced: boolean enum binary search on struct vector ...\n"
+ "struct notify { primary_recipient: confirm (key); secondary_recipient: confirm; flags : flags; }\n"
+ "// duplicates are disallowed by default, but can be enabled\n"
+ "// enum dupes : byte { large = 2, great = 2, small = 0, other }\n"
+ "table goo { hello: string (key, required); confirmations: [confirm];\n"
+ " never_mind: double = 3.1415 (deprecated);\n"
+ " embedded_t: [ubyte] (nested_flatbuffer: \"foo\");\n"
+ " embedded_s: [ubyte] (nested_flatbuffer: \"little.whale.c2\");\n"
+ " shady: shade;\n"
+ "}\n"
+ "struct s1 (force_align:4) { index: int (key); }\n"
+ "struct c1 { a: ubyte; x1 : little.whale.c2; x2:uint; x3: short; light: shade (deprecated); }\n"
+ "/// not all constructs support doc comments - this one doesn't\n"
+ "namespace little.whale;\n"
+ "struct c2 { y : c3; }\n"
+ "//struct c3 { z : c1; }\n"
+ "struct c3 { z : the.s1; }\n"
+ "file_identifier \"fbuz\";\n"
+ "file_extension \"cgen_test\";\n"
+ "root_type little.whale.c2;\n"
+ "//root_type c2;\n"
+ "//root_type the.goo;\n"
+ "table hop { einhorn: c3 (required); jupiter: c2; names: [string] (required); ehlist: [c3]; k2: the.goo; k2vec: [the.goo]; lunar: the.flags2 = bravo; }\n"
+ "table TestOrder { x0 : byte; x1: bool = true; x2: short; x3: the.shade; x4: string; x5 : the.u1; x6 : [string]; x7: double; }\n"
+ "table TestOrder2 (original_order) { x0 : byte; x1: bool = true; x1a : bool = 1; x2: short; x3: the.shade; x4: string; x5: the.u1; x6 : [string]; x7: double; }\n"
+ "table StoreResponse {}\n"
+ "rpc_service MonsterStorage {\n"
+ " Store(Monster):StoreResponse;\n"
+ " Retrieve(MonsterId):Monster;\n"
+ " RetrieveOne(MonsterId):Monster (deprecated);\n"
+ "}\n"
+ "/* \n"
+ "*/table Monster {}\ntable MonsterId{ id: int; }\n"
+ "/* \t/ */\n"; /* '\v' would give an error. */
+
+ flatcc_options_t opts;
+ flatcc_context_t ctx = 0;
+ int ret = -1;
+
+ flatcc_init_options(&opts);
+ opts.cgen_common_reader = 1;
+ opts.cgen_reader = 1;
+ opts.cgen_common_builder = 1;
+ opts.cgen_builder = 1;
+ opts.gen_stdout = 1;
+
+ /* The basename xyzzy is derived from path. */
+ if (!(ctx = flatcc_create_context(&opts, name, 0, 0))) {
+ fprintf(stderr, "unexpected: could not initialize context\n");
+ return -1;
+ }
+ if ((ret = flatcc_parse_buffer(ctx, input, sizeof(input)))) {
+ fprintf(stderr, "sorry, parse failed\n");
+ goto done;
+ } else {
+ fprintf(stderr, "schema is valid!\n");
+ fprintf(stderr, "now generating code for C ...\n\n");
+ if (flatcc_generate_files(ctx)) {
+ fprintf(stderr, "failed to generate output for C\n");
+ goto done;
+ };
+ fprintf(stdout,
+ "\n#if 0 /* FlatBuffers Schema Source */\n"
+ "%s\n"
+ "#endif /* FlatBuffers Schema Source */\n",
+ input);
+ }
+ ret = 0;
+done:
+ flatcc_destroy_context(ctx);
+ return ret;
+}
diff --git a/flatcc/test/cgen_test/cgen_test.sh b/flatcc/test/cgen_test/cgen_test.sh
new file mode 100755
index 0000000..0280e9d
--- /dev/null
+++ b/flatcc/test/cgen_test/cgen_test.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../..
+ROOT=`pwd`
+
+CC=${CC:-cc}
+TMP=${ROOT}/build/tmp/test/cgen_test
+INC=${ROOT}/include
+
+${ROOT}/scripts/build.sh
+
+mkdir -p ${TMP}
+rm -rf ${TMP}/*
+
+echo "generating test source for Debug" 1>&2
+${ROOT}/build/Debug/test/cgen_test/cgen_test_d > ${TMP}/test_generated_d.c
+cd ${TMP} && $CC test_generated_d.c -c -I${INC}
+
+echo "generating test source for Release" 1>&2
+${ROOT}/build/Release/test/cgen_test/cgen_test > ${TMP}/test_generated.c
+cd ${TMP} && $CC test_generated.c -c -Wall -O3 -I${INC}
diff --git a/flatcc/test/debug.sh b/flatcc/test/debug.sh
new file mode 100755
index 0000000..0efbcbe
--- /dev/null
+++ b/flatcc/test/debug.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/..
+mkdir -p build/tmp/out
+lldb -- \
+ bin/flatcc_d -a -o build/tmp/out --prefix zzz --common-prefix hello \
+ test/monster_test/monster_test.fbs
diff --git a/flatcc/test/emit_test/CMakeLists.txt b/flatcc/test/emit_test/CMakeLists.txt
new file mode 100644
index 0000000..54f3b28
--- /dev/null
+++ b/flatcc/test/emit_test/CMakeLists.txt
@@ -0,0 +1,20 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/test/emit_test")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_emit_test ALL)
+add_custom_command (
+ TARGET gen_emit_test
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND flatcc_cli -a -o "${GEN_DIR}" "${FBS_DIR}/emit_test.fbs"
+ DEPENDS flatcc_cli "${FBS}"
+)
+add_executable(emit_test emit_test.c)
+add_dependencies(emit_test gen_emit_test)
+target_link_libraries(emit_test flatccrt)
+
+add_test(emit_test emit_test${CMAKE_EXECUTABLE_SUFFIX})
diff --git a/flatcc/test/emit_test/emit_test.c b/flatcc/test/emit_test/emit_test.c
new file mode 100644
index 0000000..ddb973d
--- /dev/null
+++ b/flatcc/test/emit_test/emit_test.c
@@ -0,0 +1,137 @@
+#include <stdio.h>
+#include <assert.h>
+#include "emit_test_builder.h"
+#include "flatcc/support/hexdump.h"
+#include "flatcc/portable/pparsefp.h"
+
+#define test_assert(x) do { if (!(x)) { assert(0); return -1; }} while(0)
+/* Direct floating point comparisons are not always directly comparable,
+ * especially not for GCC 32-bit compilers. */
+#define test_assert_floateq(x, y) test_assert(parse_float_is_equal((x), (y)))
+#define test_assert_doubleeq(x, y) test_assert(parse_double_is_equal((x), (y)))
+
+int dbg_emitter(void *emit_context,
+ const flatcc_iovec_t *iov, int iov_count,
+ flatbuffers_soffset_t offset, size_t len)
+{
+ int i;
+
+ (void)emit_context;
+
+ printf("dbg: emit: iov_count: %d, offset: %ld, len: %ld\n",
+ (int)iov_count, (long)offset, (long)len);
+
+ for (i = 0; i < iov_count; ++i) {
+ if (iov[i].iov_base == flatcc_builder_padding_base) {
+ printf("dbg: padding at: %ld, len: %ld\n",
+ (long)offset, (long)iov[i].iov_len);
+ }
+ if (iov[i].iov_base == 0) {
+ printf("dbg: null vector reserved at: %ld, len: %ld\n",
+ (long)offset, (long)iov[i].iov_len);
+ }
+ offset += (flatbuffers_soffset_t)iov[i].iov_len;
+ }
+ return 0;
+}
+
+int debug_test(void)
+{
+ flatcc_builder_t builder, *B;
+ float x[10] = { 0 };
+
+ B = &builder;
+ printf("dbg: output is generated by a custom emitter that doesn't actually build a buffer\n");
+ flatcc_builder_custom_init(B, dbg_emitter, 0, 0, 0);
+ /* We can create a null vector because we have a custom emitter. */
+ main_create_as_root(B, 42, 1, flatbuffers_float_vec_create(B, x, 10));
+ flatcc_builder_clear(B);
+ return 0;
+}
+
+/*
+ * this assumes a very simple schema:
+ * "table { time: long; device: ubyte; samples: [float]; }"
+ */
+int emit_test(void)
+{
+ /*
+ * Note that there is some apparently unnecessary padding after 0x01
+ * which is caused by the end of the buffer content excluding
+ * vtables is forced to buffer alignment due to clustering and
+ * because alignment happens before the buffer is fully generated.
+ */
+ unsigned char expect[] =
+#if FLATBUFFERS_PROTOCOL_IS_LE
+ "\x04\x00\x00\x00\xd4\xff\xff\xff\x2a\x00\x00\x00\x00\x00\x00\x00"
+ "\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00"
+ "\x00\x00\x80\x3f\xcd\xcc\x8c\x3f\x9a\x99\x99\x3f\x66\x66\xa6\x3f"
+ "\x0a\x00\x11\x00\x04\x00\x10\x00\x0c\x00";
+#else
+
+ "\x00\x00\x00\x04\xff\xff\xff\xd4\x00\x00\x00\x00\x00\x00\x00\x2a"
+ "\x00\x00\x00\x0c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04"
+ "\x3f\x80\x00\x00\x3f\x8c\xcc\xcd\x3f\x99\x99\x9a\x3f\xa6\x66\x66"
+ "\x00\x0a\x00\x11\x00\x04\x00\x10\x00\x0c";
+#endif
+
+ size_t size;
+ uint8_t *buf;
+ flatcc_emitter_t *E;
+ flatcc_builder_t builder, *B;
+ flatbuffers_float_vec_ref_t vref;
+ float data[4] = { 1.0f, 1.1f, 1.2f, 1.3f };
+
+ main_table_t mt;
+ int64_t time;
+
+ (void)expect;
+ B = &builder;
+
+ flatcc_builder_init(B);
+
+ /* Get the default emitter. */
+ E = flatcc_builder_get_emit_context(B);
+
+ vref = flatbuffers_float_vec_create(B, data, 4);
+ //vref = 0;
+ main_create_as_root(B, 42, 1, vref);
+
+ /* We could also have used flatcc_builder API wrapper for this. */
+ buf = flatcc_emitter_get_direct_buffer(E, &size);
+ if (!buf) {
+ return -1;
+ }
+ test_assert(size == flatcc_emitter_get_buffer_size(E));
+ test_assert(size == flatcc_builder_get_buffer_size(B));
+
+ fprintf(stderr, "buffer size: %d\n", (int)size);
+ hexdump("emit_test", buf, size, stderr);
+
+ test_assert(size == 58);
+ test_assert(sizeof(expect) - 1 == size);
+ test_assert(0 == memcmp(buf, expect, size));
+
+ mt = main_as_root(buf);
+ time = main_time(mt);
+ test_assert(time == 42);
+ test_assert(main_device(mt) == 1);
+ test_assert(flatbuffers_float_vec_len(main_samples(mt)) == 4);
+ test_assert_floateq(flatbuffers_float_vec_at(main_samples(mt), 2), 1.2f);
+
+ /* We use get_direct_buffer, so we can't clear the builder until last. */
+ flatcc_builder_clear(B);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+
+ (void)argc;
+ (void)argv;
+
+ ret |= debug_test();
+ ret |= emit_test();
+ return ret;
+}
diff --git a/flatcc/test/emit_test/emit_test.fbs b/flatcc/test/emit_test/emit_test.fbs
new file mode 100644
index 0000000..973c111
--- /dev/null
+++ b/flatcc/test/emit_test/emit_test.fbs
@@ -0,0 +1,6 @@
+
+table main {
+ time: long;
+ device: ubyte;
+ samples: [float];
+}
diff --git a/flatcc/test/emit_test/emit_test.sh b/flatcc/test/emit_test/emit_test.sh
new file mode 100755
index 0000000..addecfc
--- /dev/null
+++ b/flatcc/test/emit_test/emit_test.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../..
+ROOT=`pwd`
+TMP=build/tmp/test
+${ROOT}/scripts/build.sh
+
+mkdir -p ${TMP}/emit_test
+rm -rf ${TMP}/emit_test/*
+bin/flatcc -a -o ${TMP}/emit_test test/emit_test/emit_test.fbs \
+ test/monster_test/monster_test.fbs
+cp test/emit_test/*.c ${TMP}/emit_test
+cd ${TMP}/emit_test
+cc -g -I ${ROOT}/include emit_test.c \
+ ${ROOT}/lib/libflatccrt.a -o emit_test_d
+echo "running emit test"
+./emit_test_d
+
diff --git a/flatcc/test/flatc_compat/.gitattributes b/flatcc/test/flatc_compat/.gitattributes
new file mode 100644
index 0000000..cf2b141
--- /dev/null
+++ b/flatcc/test/flatc_compat/.gitattributes
@@ -0,0 +1,2 @@
+# We do a binary comparison test, so we need it to be unchanged on Windows.
+monsterdata_test.golden -text
diff --git a/flatcc/test/flatc_compat/CMakeLists.txt b/flatcc/test/flatc_compat/CMakeLists.txt
new file mode 100644
index 0000000..a472979
--- /dev/null
+++ b/flatcc/test/flatc_compat/CMakeLists.txt
@@ -0,0 +1,21 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/test/monster_test")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_flatc_compat ALL)
+add_custom_command (
+ TARGET gen_flatc_compat
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/monsterdata_test.mon" ${CMAKE_CURRENT_BINARY_DIR}
+ COMMAND flatcc_cli -a -o "${GEN_DIR}" "${FBS_DIR}/monster_test.fbs"
+ DEPENDS flatcc_cli "${FBS_DIR}/monster_test.fbs" "${FBS_DIR}/include_test1.fbs" "${FBS_DIR}/include_test2.fbs"
+)
+add_executable(flatc_compat flatc_compat.c)
+add_dependencies(flatc_compat gen_flatc_compat)
+target_link_libraries(flatc_compat flatccrt)
+
+add_test(flatc_compat flatc_compat${CMAKE_EXECUTABLE_SUFFIX})
diff --git a/flatcc/test/flatc_compat/README.md b/flatcc/test/flatc_compat/README.md
new file mode 100644
index 0000000..ffc0bf0
--- /dev/null
+++ b/flatcc/test/flatc_compat/README.md
@@ -0,0 +1,10 @@
+Basic sanity check to verify that a `flatcc` generated reader can read
+binaries generated by Googles `flatc` compiler.
+
+`monsterdata_test.mon` is generated by Googles flatc compiler using the
+`monsterdata_test.golden` json as input and `monster_test.fbs` as schema.
+
+`monsterdata_test.json` was also copied for completeness - it apparently
+relates to undocumented hash attributed that converts json strings into
+hashes where the golden file has the correct target type with respect to
+the schema..
diff --git a/flatcc/test/flatc_compat/flatc_compat.c b/flatcc/test/flatc_compat/flatc_compat.c
new file mode 100644
index 0000000..24aae19
--- /dev/null
+++ b/flatcc/test/flatc_compat/flatc_compat.c
@@ -0,0 +1,226 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include "monster_test_reader.h"
+#include "monster_test_verifier.h"
+#include "flatcc/support/readfile.h"
+#include "flatcc/support/hexdump.h"
+
+#define align_up(alignment, size) \
+ (((size_t)(size) + (size_t)(alignment) - 1) & ~((size_t)(alignment) - 1))
+
+const char *filename = "monsterdata_test.mon";
+
+#undef ns
+#define ns(x) MyGame_Example_ ## x
+
+int verify_monster(void *buffer)
+{
+ ns(Monster_table_t) monster, mini;
+ ns(Vec3_struct_t) vec;
+ ns(Test_struct_t) test;
+ ns(Test_vec_t) testvec;
+ ns(Any_union_type_t) mini_type;
+ flatbuffers_string_t name;
+ size_t offset;
+ flatbuffers_uint8_vec_t inv;
+ flatbuffers_string_vec_t aofs;
+ flatbuffers_string_t s;
+ size_t i;
+
+ if (!(monster = ns(Monster_as_root(buffer)))) {
+ printf("Monster not available\n");
+ return -1;
+ }
+ if (ns(Monster_hp(monster)) != 80) {
+ printf("Health points are not as expected\n");
+ return -1;
+ }
+ if (!(vec = ns(Monster_pos(monster)))) {
+ printf("Position is absent\n");
+ return -1;
+ }
+ offset = (size_t)((char *)vec - (char *)buffer);
+ if (offset & 15) {
+ printf("Force align of Vec3 struct not correct\n");
+ return -1;
+ }
+ if (ns(Vec3_x(vec)) != 1) {
+ printf("Position failing on x coordinate\n");
+ return -1;
+ }
+ if (ns(Vec3_y(vec)) != 2) {
+ printf("Position failing on y coordinate\n");
+ return -1;
+ }
+ if (ns(Vec3_z(vec)) != 3) {
+ printf("Position failing on z coordinate\n");
+ return -1;
+ }
+ if (ns(Vec3_test1(vec)) != 3) {
+ printf("Vec3 test1 is not 3\n");
+ return -1;
+ }
+ if (ns(Vec3_test2(vec)) != ns(Color_Green)) {
+ printf("Vec3 test2 not Green\n");
+ return -1;
+ }
+ test = ns(Vec3_test3(vec));
+ if (ns(Test_a(test)) != 5 || ns(Test_b(test) != 6)) {
+ printf("test3 not valid in Vec3\n");
+ return -1;
+ }
+ name = ns(Monster_name(monster));
+ if (flatbuffers_string_len(name) != 9) {
+ printf("Name length is not correct\n");
+ return -1;
+ }
+ if (!name || strcmp(name, "MyMonster")) {
+ printf("Name is not correct\n");
+ return -1;
+ }
+ inv = ns(Monster_inventory(monster));
+ if (flatbuffers_uint8_vec_len(inv) != 5) {
+ printf("Inventory has wrong length\n");
+ return -1;
+ }
+ for (i = 0; i < 5; ++i) {
+ if (flatbuffers_uint8_vec_at(inv, i) != i) {
+ printf("Inventory item #%d is wrong\n", (int)i);
+ return -1;
+ }
+ }
+ if (!(aofs = ns(Monster_testarrayofstring(monster)))) {
+ printf("Array of string not present\n");
+ return -1;
+ }
+ if (flatbuffers_string_vec_len(aofs) != 2) {
+ printf("Array of string has wrong vector length\n");
+ return -1;
+ }
+ s = flatbuffers_string_vec_at(aofs, 0);
+ if (strcmp(s, "test1")) {
+ printf("First string array element is wrong\n");
+ return -1;
+ }
+ s = flatbuffers_string_vec_at(aofs, 1);
+ if (strcmp(s, "test2")) {
+ printf("Second string array element is wrong\n");
+ return -1;
+ }
+ mini_type = ns(Monster_test_type(monster));
+ if (mini_type != ns(Any_Monster)) {
+ printf("Not any monster\n");
+ return -1;
+ }
+ mini = ns(Monster_test(monster));
+ if (!mini) {
+ printf("test monster not there\n");
+ return -1;
+ }
+ if (strcmp(ns(Monster_name(mini)), "Fred")) {
+ printf("test monster isn't Fred\n");
+ return -1;
+ }
+ testvec = ns(Monster_test4(monster));
+ if (ns(Test_vec_len(testvec)) != 2) {
+ printf("Test struct vector has wrong length\n");
+ return -1;
+ }
+ test = ns(Test_vec_at(testvec, 0));
+ if (ns(Test_a(test) != 10)) {
+ printf("Testvec[0].a is wrong\n");
+ return -1;
+ }
+ if (ns(Test_b(test) != 20)) {
+ printf("Testvec[0].b is wrong\n");
+ return -1;
+ }
+ test = ns(Test_vec_at(testvec, 1));
+ if (ns(Test_a(test) != 30)) {
+ printf("Testvec[1].a is wrong\n");
+ return -1;
+ }
+ if (ns(Test_b(test) != 40)) {
+ printf("Testvec[1].b is wrong\n");
+ return -1;
+ }
+ assert(ns(Monster_testhashs32_fnv1(monster)) == -579221183L);
+ assert(ns(Monster_testhashu32_fnv1(monster)) == 3715746113L);
+ assert(ns(Monster_testhashs64_fnv1(monster)) == 7930699090847568257LL);
+ assert(ns(Monster_testhashu64_fnv1(monster)) == 7930699090847568257LL);
+ assert(ns(Monster_testhashs32_fnv1a(monster)) == -1904106383L);
+ assert(ns(Monster_testhashu32_fnv1a(monster)) == 2390860913L);
+ assert(ns(Monster_testhashs64_fnv1a(monster)) == 4898026182817603057LL);
+ assert(ns(Monster_testhashu64_fnv1a(monster)) == 4898026182817603057LL);
+ return 0;
+}
+
+
+/* We take arguments so test can run without copying sources. */
+#define usage \
+"wrong number of arguments:\n" \
+"usage: <program> [<input-filename>]\n"
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ size_t size;
+ void *buffer, *raw_buffer;
+
+ if (argc != 1 && argc != 2) {
+ fprintf(stderr, usage);
+ exit(1);
+ }
+ if (argc == 2) {
+ filename = argv[1];
+ }
+
+ raw_buffer = readfile(filename, 1024, &size);
+ buffer = aligned_alloc(256, align_up(256, size));
+ memcpy(buffer, raw_buffer, size);
+ free(raw_buffer);
+
+ if (!buffer) {
+ fprintf(stderr, "could not read binary test file: %s\n", filename);
+ return -1;
+ }
+ hexdump("monsterdata_test.mon", buffer, size, stderr);
+ /*
+ * Not automated, but verifying size - 3 fails as expected because the last
+ * object in the file is a string, and the zero termination fails.
+ * size - 1 and size - 2 verifies because the buffers contains
+ * padding. Note that `flatcc` does not pad at the end beyond whatever
+ * is stored (normally a vtable), but this is generated with `flatc
+ * v1.1`.
+ */
+ if (flatcc_verify_ok != ns(Monster_verify_as_root_with_identifier(buffer, size, "MONS"))) {
+#if FLATBUFFERS_PROTOCOL_IS_BE
+ fprintf(stderr, "flatc golden reference buffer was correctly rejected by flatcc verificiation\n"
+ "because flatc is little endian and flatcc has been compiled for big endian protocol format\n");
+ ret = 0;
+ goto done;
+#else
+ fprintf(stderr, "could not verify foreign monster file\n");
+ ret = -1;
+ goto done;
+#endif
+ }
+
+#if FLATBUFFERS_PROTOCOL_IS_BE
+ fprintf(stderr, "flatcc compiled with big endian protocol failed to reject reference little endian buffer\n");
+ ret = -1;
+ goto done;
+#else
+ if (flatcc_verify_ok != ns(Monster_verify_as_root(buffer, size))) {
+ fprintf(stderr, "could not verify foreign monster file with default identifier\n");
+ ret = -1;
+ goto done;
+ }
+ ret = verify_monster(buffer);
+#endif
+
+done:
+ aligned_free(buffer);
+ return ret;
+}
diff --git a/flatcc/test/flatc_compat/flatc_compat.sh b/flatcc/test/flatc_compat/flatc_compat.sh
new file mode 100755
index 0000000..1b2dbb9
--- /dev/null
+++ b/flatcc/test/flatc_compat/flatc_compat.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../..
+ROOT=`pwd`
+TMP=build/tmp/test
+
+${ROOT}/scripts/build.sh
+
+mkdir -p ${TMP}/flatc_compat
+rm -rf ${TMP}/flatc_compat/*
+bin/flatcc -a -o ${TMP}/flatc_compat test/monster_test/monster_test.fbs
+
+cp test/flatc_compat/*.{json,mon,c} ${TMP}/flatc_compat/
+cd ${TMP}/flatc_compat
+cc -g -I ${ROOT}/include flatc_compat.c \
+ ${ROOT}/lib/libflatccrt.a -o flatc_compat_d
+echo "Google FPL flatc compatibility test - reading flatc generated binary"
+./flatc_compat_d
+
diff --git a/flatcc/test/flatc_compat/monsterdata_test.golden b/flatcc/test/flatc_compat/monsterdata_test.golden
new file mode 100644
index 0000000..73afc42
--- /dev/null
+++ b/flatcc/test/flatc_compat/monsterdata_test.golden
@@ -0,0 +1,48 @@
+{
+ pos: {
+ x: 1,
+ y: 2,
+ z: 3,
+ test1: 3,
+ test2: Green,
+ test3: {
+ a: 5,
+ b: 6
+ }
+ },
+ hp: 80,
+ name: "MyMonster",
+ inventory: [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4
+ ],
+ test_type: Monster,
+ test: {
+ name: "Fred"
+ },
+ test4: [
+ {
+ a: 10,
+ b: 20
+ },
+ {
+ a: 30,
+ b: 40
+ }
+ ],
+ testarrayofstring: [
+ "test1",
+ "test2"
+ ],
+ testhashs32_fnv1: -579221183,
+ testhashu32_fnv1: 3715746113,
+ testhashs64_fnv1: 7930699090847568257,
+ testhashu64_fnv1: 7930699090847568257,
+ testhashs32_fnv1a: -1904106383,
+ testhashu32_fnv1a: 2390860913,
+ testhashs64_fnv1a: 4898026182817603057,
+ testhashu64_fnv1a: 4898026182817603057
+}
diff --git a/flatcc/test/flatc_compat/monsterdata_test.json b/flatcc/test/flatc_compat/monsterdata_test.json
new file mode 100755
index 0000000..a718efa
--- /dev/null
+++ b/flatcc/test/flatc_compat/monsterdata_test.json
@@ -0,0 +1,51 @@
+{
+ pos: {
+ x: 1,
+ y: 2,
+ z: 3,
+ test1: 3,
+ test2: Green,
+ test3: {
+ a: 5,
+ b: 6
+ }
+ },
+ hp: 80,
+ name: "MyMonster",
+ inventory: [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4
+ ],
+ test_type: Monster,
+ test: {
+ name: "Fred"
+ },
+ test4: [
+ {
+ a: 10,
+ b: 20
+ },
+ {
+ a: 30,
+ b: 40
+ }
+ ],
+ testarrayofstring: [
+ "test1",
+ "test2"
+ ],
+ testarrayofbools:[
+ true, false, true
+ ],
+ testhashs32_fnv1: "This string is being hashed!",
+ testhashu32_fnv1: "This string is being hashed!",
+ testhashs64_fnv1: "This string is being hashed!",
+ testhashu64_fnv1: "This string is being hashed!",
+ testhashs32_fnv1a: "This string is being hashed!",
+ testhashu32_fnv1a: "This string is being hashed!",
+ testhashs64_fnv1a: "This string is being hashed!",
+ testhashu64_fnv1a: "This string is being hashed!",
+}
diff --git a/flatcc/test/flatc_compat/monsterdata_test.mon b/flatcc/test/flatc_compat/monsterdata_test.mon
new file mode 100644
index 0000000..08646d4
--- /dev/null
+++ b/flatcc/test/flatc_compat/monsterdata_test.mon
Binary files differ
diff --git a/flatcc/test/json_test/CMakeLists.txt b/flatcc/test/json_test/CMakeLists.txt
new file mode 100644
index 0000000..332905d
--- /dev/null
+++ b/flatcc/test/json_test/CMakeLists.txt
@@ -0,0 +1,64 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/test/monster_test")
+
+set(DATA_DST "${CMAKE_CURRENT_BINARY_DIR}")
+set(DATA_SRC "${PROJECT_SOURCE_DIR}/test/flatc_compat")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_monster_test_json ALL)
+add_custom_command (
+ TARGET gen_monster_test_json
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND ${CMAKE_COMMAND} -E copy "${DATA_SRC}/monsterdata_test.golden" "${DATA_DST}"
+ COMMAND ${CMAKE_COMMAND} -E copy "${DATA_SRC}/monsterdata_test.mon" "${DATA_DST}"
+ COMMAND flatcc_cli -av --json -o "${GEN_DIR}" "${FBS_DIR}/monster_test.fbs"
+ DEPENDS flatcc_cli "${FBS_DIR}/monster_test.fbs" "${FBS_DIR}/include_test1.fbs" "${FBS_DIR}/include_test2.fbs"
+)
+
+add_executable(test_basic_parse test_basic_parse.c)
+add_executable(test_json_parser test_json_parser.c)
+add_executable(test_json_printer test_json_printer.c)
+add_executable(test_json test_json.c)
+
+add_dependencies(test_basic_parse gen_monster_test_json)
+add_dependencies(test_json_parser gen_monster_test_json)
+add_dependencies(test_json_printer gen_monster_test_json)
+add_dependencies(test_json gen_monster_test_json)
+
+target_link_libraries(test_basic_parse flatccrt)
+target_link_libraries(test_json_parser flatccrt)
+target_link_libraries(test_json_printer flatccrt)
+target_link_libraries(test_json flatccrt)
+
+add_test(test_basic_parse test_basic_parse${CMAKE_EXECUTABLE_SUFFIX})
+add_test(test_json_parser test_json_parser${CMAKE_EXECUTABLE_SUFFIX})
+add_test(test_json_printer test_json_printer${CMAKE_EXECUTABLE_SUFFIX})
+add_test(test_json test_json${CMAKE_EXECUTABLE_SUFFIX})
+
+# Compile without default library in order to test various runtime flags
+set(RTPATH "${PROJECT_SOURCE_DIR}/src/runtime")
+set(RTSRC
+ "${RTPATH}/builder.c"
+ "${RTPATH}/emitter.c"
+ "${RTPATH}/refmap.c"
+ "${RTPATH}/verifier.c"
+ "${RTPATH}/json_parser.c"
+ "${RTPATH}/json_printer.c"
+)
+
+macro(jstest trg flags)
+ add_executable(${trg} test_json.c ${RTSRC})
+ add_dependencies(${trg} gen_monster_test_json)
+ add_test(${trg} ${trg}${CMAKE_EXECUTABLE_SUFFIX})
+ set_target_properties(${trg} PROPERTIES COMPILE_FLAGS ${flags})
+endmacro()
+
+jstest(json_test_uql "-DFLATCC_JSON_PARSE_ALLOW_UNQUOTED_LIST=1")
+jstest(json_test_uql_off "-DFLATCC_JSON_PARSE_ALLOW_UNQUOTED_LIST=0")
+jstest(json_test_uq "-DFLATCC_JSON_PARSE_ALLOW_UNQUOTED=1")
+jstest(json_test_uq_off "-DFLATCC_JSON_PARSE_ALLOW_UNQUOTED=0")
+jstest(json_test "-DFLATCC_JSON_PARSE_WIDE_SPACE=1")
diff --git a/flatcc/test/json_test/flatcc_golden.c b/flatcc/test/json_test/flatcc_golden.c
new file mode 100644
index 0000000..a22e3d3
--- /dev/null
+++ b/flatcc/test/json_test/flatcc_golden.c
@@ -0,0 +1,45 @@
+/*
+ * Flatcc generated monster test binary based on parsing Google flatc's
+ * golden json file.
+ */
+static const unsigned char flatcc_golden_le[] = {
+ 0x0c, 0x00, 0x00, 0x00, 0x4d, 0x4f, 0x4e, 0x53, 0x00, 0x00, 0x00, 0x00, 0x20, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x02, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x41, 0xc9, 0x79, 0xdd,
+ 0x41, 0xc9, 0x79, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x81, 0x91, 0x7b, 0xf2, 0xcd, 0x80, 0x0f, 0x6e,
+ 0x81, 0x91, 0x7b, 0xf2, 0xcd, 0x80, 0x0f, 0x6e, 0x71, 0xa4, 0x81, 0x8e, 0x71, 0xa4, 0x81, 0x8e,
+ 0xf1, 0xdd, 0x67, 0xc7, 0xdc, 0x48, 0xf9, 0x43, 0xf1, 0xdd, 0x67, 0xc7, 0xdc, 0x48, 0xf9, 0x43,
+ 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x74, 0x65, 0x73, 0x74, 0x32, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74,
+ 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x1e, 0x00, 0x28, 0x00,
+ 0xd0, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x46, 0x72, 0x65, 0x64,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x4d, 0x79, 0x4d, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x72, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x34, 0x00, 0x74, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x24, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x34, 0x00,
+ 0x30, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x64, 0x00, 0x6c, 0x00,
+};
+
+static const unsigned char flatcc_golden_be[] = {
+ 0x00, 0x00, 0x00, 0x0c, 0x53, 0x4e, 0x4f, 0x4d, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x20,
+ 0x3f, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x74,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x38, 0xdd, 0x79, 0xc9, 0x41,
+ 0xdd, 0x79, 0xc9, 0x41, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x0f, 0x80, 0xcd, 0xf2, 0x7b, 0x91, 0x81,
+ 0x6e, 0x0f, 0x80, 0xcd, 0xf2, 0x7b, 0x91, 0x81, 0x8e, 0x81, 0xa4, 0x71, 0x8e, 0x81, 0xa4, 0x71,
+ 0x43, 0xf9, 0x48, 0xdc, 0xc7, 0x67, 0xdd, 0xf1, 0x43, 0xf9, 0x48, 0xdc, 0xc7, 0x67, 0xdd, 0xf1,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
+ 0x74, 0x65, 0x73, 0x74, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x74, 0x65, 0x73, 0x74,
+ 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x14, 0x00, 0x00, 0x1e, 0x28, 0x00,
+ 0xff, 0xff, 0xff, 0xd0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x46, 0x72, 0x65, 0x64,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x09, 0x4d, 0x79, 0x4d, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x72, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x34, 0x00, 0x74,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x34,
+ 0x00, 0x30, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x64, 0x00, 0x6c,
+};
diff --git a/flatcc/test/json_test/json_test.sh b/flatcc/test/json_test/json_test.sh
new file mode 100755
index 0000000..e08bab5
--- /dev/null
+++ b/flatcc/test/json_test/json_test.sh
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../..
+ROOT=`pwd`
+TMP=${ROOT}/build/tmp/test/json_test
+
+CC=${CC:-cc}
+${ROOT}/scripts/build.sh
+mkdir -p ${TMP}
+rm -rf ${TMP}/*
+
+# could also use --json to generate both at once
+bin/flatcc -av --json -o ${TMP} test/monster_test/monster_test.fbs
+
+cp test/json_test/*.c ${TMP}
+cp test/flatc_compat/monsterdata_test.golden ${TMP}
+cp test/flatc_compat/monsterdata_test.mon ${TMP}
+
+cd ${TMP}
+
+$CC -g -I ${ROOT}/include test_basic_parse.c \
+ ${ROOT}/lib/libflatccrt_d.a -o test_basic_parse_d
+
+$CC -g -I ${ROOT}/include test_json_parser.c \
+ ${ROOT}/lib/libflatccrt_d.a -o test_json_parser_d
+
+$CC -g -I ${ROOT}/include test_json_printer.c \
+ ${ROOT}/lib/libflatccrt_d.a -o test_json_printer_d
+
+$CC -g -I ${ROOT}/include test_json.c\
+ ${ROOT}/lib/libflatccrt_d.a -o test_json_d
+
+
+echo "running json basic parse test debug"
+./test_basic_parse_d
+
+echo "running json parser test debug"
+./test_json_parser_d
+
+echo "running json printer test debug"
+./test_json_printer_d
+
+echo "running json test debug"
+./test_json_d
+
+$CC -O2 -DNDEBUG -I ${ROOT}/include test_basic_parse.c \
+ ${ROOT}/lib/libflatccrt.a -o test_basic_parse
+
+#$CC -O3 -DNDEBUG -I ${ROOT}/include test_json_parser.c \
+#$CC -Os -save-temps -DNDEBUG -I ${ROOT}/include test_json_parser.c \
+
+$CC -O2 -DNDEBUG -I ${ROOT}/include test_json_parser.c \
+ ${ROOT}/lib/libflatccrt.a -o test_json_parser
+
+$CC -O2 -DNDEBUG -I ${ROOT}/include test_json_printer.c\
+ ${ROOT}/lib/libflatccrt.a -o test_json_printer
+
+$CC -O2 -DNDEBUG -I ${ROOT}/include test_json.c\
+ ${ROOT}/lib/libflatccrt.a -o test_json
+
+echo "running json basic parse test optimized"
+./test_basic_parse
+
+echo "running json parser test optimized"
+./test_json_parser
+
+echo "running json printer test optimimized"
+./test_json_printer
+
+echo "running json test optimized"
+./test_json
+
+echo "json tests passed"
diff --git a/flatcc/test/json_test/test_basic_parse.c b/flatcc/test/json_test/test_basic_parse.c
new file mode 100644
index 0000000..7b8f4ba
--- /dev/null
+++ b/flatcc/test/json_test/test_basic_parse.c
@@ -0,0 +1,291 @@
+#include <stdio.h>
+#include "flatcc/flatcc_builder.h"
+#include "flatcc/flatcc_json_parser.h"
+
+/*
+ * Helper macros for generating compile time tries.
+ *
+ * - this is for prototyping - codegenerator does this without macroes.
+ */
+#define __FLATCC_CHARW(x, p) (((uint64_t)(x)) << ((p) * 8))
+#define __FLATCC_KW1(s) (__FLATCC_CHARW(s[0], 7))
+#define __FLATCC_KW2(s) (__FLATCC_KW1(s) | __FLATCC_CHARW(s[1], 6))
+#define __FLATCC_KW3(s) (__FLATCC_KW2(s) | __FLATCC_CHARW(s[2], 5))
+#define __FLATCC_KW4(s) (__FLATCC_KW3(s) | __FLATCC_CHARW(s[3], 4))
+#define __FLATCC_KW5(s) (__FLATCC_KW4(s) | __FLATCC_CHARW(s[4], 3))
+#define __FLATCC_KW6(s) (__FLATCC_KW5(s) | __FLATCC_CHARW(s[5], 2))
+#define __FLATCC_KW7(s) (__FLATCC_KW6(s) | __FLATCC_CHARW(s[6], 1))
+#define __FLATCC_KW8(s) (__FLATCC_KW7(s) | __FLATCC_CHARW(s[7], 0))
+#define __FLATCC_KW(s, n) __FLATCC_KW ## n(s)
+
+#define __FLATCC_MASKKW(n) ((~(uint64_t)0) << ((8 - (n)) * 8))
+#define __FLATCC_MATCHKW(w, s, n) ((__FLATCC_MASKKW(n) & (w)) == __FLATCC_KW(s, n))
+#define __FLATCC_LTKW(w, s, n) ((__FLATCC_MASKKW(n) & (w)) < __FLATCC_KW(s, n))
+
+
+const char g_data[] = " \
+ \
+{ \r\n \
+ \"first\": 1, \
+ \"second\": 2.0, \
+ \"seconds left\": 42, \
+ \"seconds lead\": 1, \n \
+ \"zulu\": \"really\" \n \
+} \
+";
+
+/*
+ * This is proof of concept test before code-generation to evaluate
+ * efficient parsing and buffer construction principles while scanning
+ * text such as a JSON. We do no use a schema per se, but implicitly
+ * define one in the way that we construct the parser.
+ */
+
+#define match(x) if (end > buf && buf[0] == x) { ++buf; } \
+ else { fprintf(stderr, "failed to match '%c'\n", x); \
+ buf = flatcc_json_parser_set_error(ctx, buf, end, \
+ flatcc_json_parser_error_invalid_character); goto fail; }
+
+/* Space is optional, but we do expect more input. */
+#define space() { \
+ buf = flatcc_json_parser_space(ctx, buf, end); \
+ if (buf == end) { fprintf(stderr, "parse failed\n"); goto fail; }} \
+
+#ifdef FLATCC_JSON_ALLOW_UNKNOWN_FIELD
+#define ignore_field() { \
+ buf = flatcc_json_parser_symbol_end(ctx, buf, end); \
+ space(); match(':'); space(); \
+ buf = flatcc_json_parser_generic_json(ctx, buf, end); \
+ if (buf == end) { \
+ goto fail; \
+ }}
+#else
+#define ignore_field() { \
+ buf = flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unknown_symbol);\
+ goto fail; }
+#endif
+
+
+/*
+ * We build a flatbuffer dynamically without a schema, but we still need
+ * to assigned vtable entries.
+ */
+enum {
+ id_first = 0,
+ id_second = 1,
+ id_seconds_left = 2,
+ id_seconds_lead = 3,
+ id_zulu = 10
+};
+
+enum {
+ ctx_done = 0, ctx_t1_start, ctx_t1_again
+};
+
+const char *test(flatcc_builder_t *B, const char *buf, const char *end, int *ret)
+{
+ flatcc_json_parser_t parse_ctx, *ctx;
+ flatcc_builder_ref_t root = 0, ref, *p_ref;
+ uint64_t w;
+ const char *k;
+ char *s;
+ flatcc_json_parser_escape_buffer_t code;
+
+ void *p;
+
+ ctx = &parse_ctx;
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->line = 1;
+ ctx->line_start = buf;
+
+ flatcc_builder_start_buffer(B, "TEST", 0, 0);
+
+ space(); match('{'); space();
+ flatcc_builder_start_table(B, id_zulu + 1);
+
+t1_again:
+
+ buf = flatcc_json_parser_symbol_start(ctx, buf, end);
+ w = flatcc_json_parser_symbol_part(buf, end);
+ k = end - buf > 8 ? buf + 8 : end;
+ /*
+ * We implement a trie here. Because we compare big endian
+ * any trailing garbage in a word is least significant
+ * and masked out in MATCH tests.
+ *
+ * When a keyword is a prefix of another, the shorter keyword
+ * must be tested first because any trailing "garbage" will
+ * be larger (or equal if at buffer end or invalid nulls are
+ * contained) than the short keyword, but if testing the long
+ * keyword, the shorter keyword may be either larger or smaller
+ * depending on what content follows.
+ *
+ * Errors result in `buf` being set to `end` so we need not test
+ * for errors all the time. We use space as a convenient bailout
+ * point.
+ */
+ if (__FLATCC_LTKW(w, "second", 6)) {
+ if (!__FLATCC_MATCHKW(w, "first", 5)) {
+ ignore_field();
+ } else {
+ buf = flatcc_json_parser_symbol_end(ctx, buf + 5, end);
+ space(); match(':'); space();
+ p = flatcc_builder_table_add(B, id_first, 1, 1);
+ if (!p) { goto fail; }
+ k = buf;
+ buf = flatcc_json_parser_uint8(ctx, buf, end, p);
+ /* Here we could optionally parse for symbolic constants. */
+ if (k == buf) { goto fail; };
+ /* Successfully parsed field. */
+ }
+ } else {
+ if (__FLATCC_LTKW(w, "zulu", 4)) {
+ if (__FLATCC_LTKW(w, "seconds ", 8)) {
+ if (!__FLATCC_MATCHKW(w, "second", 6)) {
+ ignore_field();
+ } else {
+ buf = flatcc_json_parser_symbol_end(ctx, buf + 6, end);
+ space(); match(':'); space();
+ p = flatcc_builder_table_add(B, id_second, 8, 8);
+ if (!p) { goto fail; }
+ k = buf;
+ buf = flatcc_json_parser_double(ctx, buf, end, p);
+ /* Here we could optionally parse for symbolic constants. */
+ if (k == buf) { goto fail; };
+ /* Successfully parsed field. */
+ }
+ } else {
+ if (!__FLATCC_MATCHKW(w, "seconds ", 8)) {
+ ignore_field();
+ } else {
+ /* We have multiple keys matching the first word, so we load another. */
+ buf = k;
+ w = flatcc_json_parser_symbol_part(buf, end);
+ k = end - buf > 8 ? buf + 8 : end;
+ if (__FLATCC_LTKW(w, "left", 4)) {
+ if (!__FLATCC_MATCHKW(w, "lead", 4)) {
+ ignore_field();
+ } else {
+ buf = flatcc_json_parser_symbol_end(ctx, buf + 4, end);
+ space(); match(':'); space();
+ p = flatcc_builder_table_add(B, id_seconds_lead, 8, 8);
+ if (!p) { goto fail; }
+ k = buf;
+ buf = flatcc_json_parser_int64(ctx, buf, end, p);
+ /* Here we could optionally parse for symbolic constants. */
+ if (k == buf) { goto fail; };
+ /* Successfully parsed field. */
+ }
+ } else {
+ if (!__FLATCC_MATCHKW(w, "left", 4)) {
+ ignore_field();
+ } else {
+ buf = flatcc_json_parser_symbol_end(ctx, buf + 4, end);
+ space(); match(':'); space();
+ p = flatcc_builder_table_add(B, id_seconds_left, 4, 4);
+ if (!p) { goto fail; }
+ k = buf;
+ buf = flatcc_json_parser_uint32(ctx, buf, end, p);
+ /* Here we could optionally parse for symbolic constants. */
+ if (k == buf) { goto fail; };
+ /* Successfully parsed field. */
+ }
+ }
+ }
+ }
+ } else {
+ if (!__FLATCC_MATCHKW(w, "zulu", 4)) {
+ ignore_field();
+ } else {
+ buf = flatcc_json_parser_symbol_end(ctx, buf + 4, end);
+ space(); match(':'); space();
+ /*
+ * Parse field as string. If we are lucky, we can
+ * create the string in one go, which is faster.
+ * We can't if the string contains escape codes.
+ */
+ buf = flatcc_json_parser_string_start(ctx, buf, end);
+ k = buf;
+ buf = flatcc_json_parser_string_part(ctx, buf, end);
+ if (buf == end) {
+ goto fail;
+ }
+ if (buf[0] == '\"') {
+ ref = flatcc_builder_create_string(B, k, (size_t)(buf - k));
+ } else {
+ /* Start string with enough space for what we have. */
+ flatcc_builder_start_string(B);
+ s = flatcc_builder_extend_string(B, (size_t)(buf - k));
+ if (!s) { goto fail; }
+ memcpy(s, k, (size_t)(buf - k));
+ do {
+ buf = flatcc_json_parser_string_escape(ctx, buf, end, code);
+ flatcc_builder_append_string(B, code + 1, (size_t)code[0]);
+ k = buf;
+ buf = flatcc_json_parser_string_part(ctx, buf, end);
+ if (buf == end) {
+ goto fail;
+ }
+ flatcc_builder_append_string(B, k, (size_t)(buf - k));
+ } while (buf[0] != '\"');
+ ref = flatcc_builder_end_string(B);
+ }
+ if (!ref) {
+ goto fail;
+ }
+ /* Duplicate fields may fail or assert. */
+ p_ref = flatcc_builder_table_add_offset(B, id_zulu);
+ if (!p_ref) {
+ goto fail;
+ }
+ *p_ref = ref;
+ buf = flatcc_json_parser_string_end(ctx, buf, end);
+ /* Successfully parsed field. */
+ }
+ }
+ }
+ space();
+ if (*buf == ',') {
+ ++buf;
+ space();
+ if (*buf != '}') {
+ goto t1_again;
+ }
+#if !FLATCC_JSON_PARSE_ALLOW_TRAILING_COMMA
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_trailing_comma);
+#endif
+ }
+ match('}');
+ root = flatcc_builder_end_table(B);
+
+ flatcc_builder_end_buffer(B, root);
+#if !FLATCC_JSON_PARSE_IGNORE_TRAILING_DATA
+ buf = flatcc_json_parser_space(ctx, buf, end);
+ if (buf != end) {
+ fprintf(stderr, "extra characters in input\n");
+ goto fail;
+ }
+#endif
+fail:
+ if (ctx->error) {
+ fprintf(stderr, "%d:%d: %s\n", (int)ctx->line, (int)(ctx->error_loc - ctx->line_start + 1), flatcc_json_parser_error_string(ctx->error));
+ flatcc_builder_reset(B);
+ } else {
+ fprintf(stderr, "parse accepted\n");
+ }
+ *ret = ctx->error;
+ return buf;
+}
+
+int main(void)
+{
+ int ret = -1;
+ flatcc_builder_t builder;
+
+ flatcc_builder_init(&builder);
+
+ test(&builder, g_data, g_data + sizeof(g_data) - 1, &ret);
+
+ flatcc_builder_clear(&builder);
+ return ret;
+}
diff --git a/flatcc/test/json_test/test_json.c b/flatcc/test/json_test/test_json.c
new file mode 100644
index 0000000..aeee13a
--- /dev/null
+++ b/flatcc/test/json_test/test_json.c
@@ -0,0 +1,882 @@
+#include <stdio.h>
+#include "monster_test_json_parser.h"
+#include "monster_test_json_printer.h"
+#include "monster_test_verifier.h"
+
+#include "flatcc/support/hexdump.h"
+
+#define UQL FLATCC_JSON_PARSE_ALLOW_UNQUOTED_LIST
+#define UQ FLATCC_JSON_PARSE_ALLOW_UNQUOTED
+
+#undef ns
+#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Example, x)
+
+#undef nsf
+#define nsf(x) FLATBUFFERS_WRAP_NAMESPACE(Fantasy, x)
+
+struct test_scope {
+ const char *identifier;
+ flatcc_json_parser_table_f *parser;
+ flatcc_json_printer_table_f *printer;
+ flatcc_table_verifier_f *verifier;
+};
+
+static const struct test_scope Monster = {
+ /* The is the schema global file identifier. */
+ ns(Monster_file_identifier),
+ ns(Monster_parse_json_table),
+ ns(Monster_print_json_table),
+ ns(Monster_verify_table)
+};
+
+static const struct test_scope Alt = {
+ /* This is the type hash identifier. */
+ ns(Alt_type_identifier),
+ ns(Alt_parse_json_table),
+ ns(Alt_print_json_table),
+ ns(Alt_verify_table)
+};
+
+static const struct test_scope Movie = {
+ /* This is the type hash identifier. */
+ nsf(Movie_type_identifier),
+ nsf(Movie_parse_json_table),
+ nsf(Movie_print_json_table),
+ nsf(Movie_verify_table)
+};
+
+int test_json(const struct test_scope *scope, char *json,
+ char *expect, int expect_err,
+ flatcc_json_parser_flags_t parse_flags, flatcc_json_printer_flags_t print_flags, int line)
+{
+ int ret = -1;
+ int err;
+ void *flatbuffer = 0;
+ char *buf = 0;
+ size_t flatbuffer_size, buf_size;
+ flatcc_builder_t builder, *B;
+ flatcc_json_parser_t parser_ctx;
+ flatcc_json_printer_t printer_ctx;
+ int i;
+
+ B = &builder;
+ flatcc_builder_init(B);
+ flatcc_json_printer_init_dynamic_buffer(&printer_ctx, 0);
+ flatcc_json_printer_set_flags(&printer_ctx, print_flags);
+ err = flatcc_json_parser_table_as_root(B, &parser_ctx, json, strlen(json), parse_flags,
+ scope->identifier, scope->parser);
+ if (err != expect_err) {
+ if (expect_err) {
+ if (err) {
+ fprintf(stderr, "%d: json test: parse failed with: %s\n",
+ line, flatcc_json_parser_error_string(err));
+ fprintf(stderr, "but expected to fail with: %s\n",
+ flatcc_json_parser_error_string(expect_err));
+ fprintf(stderr, "%s\n", json);
+ } else {
+ fprintf(stderr, "%d: json test: parse successful, but expected to fail with: %s\n",
+ line, flatcc_json_parser_error_string(expect_err));
+ fprintf(stderr, "%s\n", json);
+ }
+ } else {
+ fprintf(stderr, "%d: json test: parse failed: %s\n", line, flatcc_json_parser_error_string(err));
+ fprintf(stderr, "%s\n", json);
+ }
+ for (i = 0; i < parser_ctx.pos - 1; ++i) {
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "^\n");
+ goto failed;
+ }
+ if (expect_err) {
+ ret = 0;
+ goto done;
+ }
+ flatbuffer = flatcc_builder_finalize_aligned_buffer(B, &flatbuffer_size);
+ if ((ret = flatcc_verify_table_as_root(flatbuffer, flatbuffer_size, scope->identifier, scope->verifier))) {
+ fprintf(stderr, "%s:%d: buffer verification failed: %s\n",
+ __FILE__, line, flatcc_verify_error_string(ret));
+ goto failed;
+ }
+
+ flatcc_json_printer_table_as_root(&printer_ctx, flatbuffer, flatbuffer_size, scope->identifier, scope->printer);
+ buf = flatcc_json_printer_get_buffer(&printer_ctx, &buf_size);
+ if (!buf || strcmp(expect, buf)) {
+ fprintf(stderr, "%d: json test: printed buffer not as expected, got:\n", line);
+ fprintf(stderr, "%s\n", buf);
+ fprintf(stderr, "expected:\n");
+ fprintf(stderr, "%s\n", expect);
+ goto failed;
+ }
+ ret = 0;
+
+done:
+ flatcc_builder_aligned_free(flatbuffer);
+ flatcc_builder_clear(B);
+ flatcc_json_printer_clear(&printer_ctx);
+ return ret;
+
+failed:
+ if (flatbuffer) {
+ hexdump("parsed buffer", flatbuffer, flatbuffer_size, stderr);
+ }
+ ret = -1;
+ goto done;
+}
+
+#define BEGIN_TEST(name) int ret = 0; const struct test_scope *scope = &name
+#define END_TEST() return ret;
+
+#define TEST(x, y) \
+ ret |= test_json(scope, (x), (y), 0, 0, 0, __LINE__);
+
+#define TEST_ERROR(x, err) \
+ ret |= test_json(scope, (x), 0, err, 0, 0, __LINE__);
+
+#define TEST_FLAGS(fparse, fprint, x, y) \
+ ret |= test_json(scope, (x), (y), 0, (fparse), (fprint), __LINE__);
+
+#define TEST_ERROR_FLAGS(fparse, fprint, x, err) \
+ ret |= test_json(scope, (x), 0, err, (fparse), (fprint), __LINE__);
+
+int edge_case_tests(void)
+{
+ BEGIN_TEST(Monster);
+/*
+ * Each symbolic value is type coerced and added. One might expect
+ * or'ing flags together, but it doesn't work with signed values
+ * and floating point target values. We would either need a much
+ * more complicated parser or restrict the places where symbols are
+ * allowed.
+ */
+#if 0
+ TEST( "{ name: \"Monster\", color: \"Green Blue Red Blue\"}",
+ "{\"name\":\"Monster\",\"color\":\"Red Green Blue\"}");
+#else
+#if UQ
+ TEST( "{ name: \"Monster\", color: \"Green Blue Red Blue\"}",
+ "{\"name\":\"Monster\",\"color\":19}");
+#else
+ TEST( "{ \"name\": \"Monster\", \"color\": \"Green Blue Red Blue\"}",
+ "{\"name\":\"Monster\",\"color\":19}");
+#endif
+#endif
+
+/*
+ * If a value is stored, even if default, it is also printed.
+ * This option can also be flagged compile time for better performance.
+ */
+ TEST_FLAGS(flatcc_json_parser_f_force_add, 0,
+ "{ \"name\": \"Monster\", \"color\": 8}",
+ "{\"name\":\"Monster\",\"color\":\"Blue\"}");
+
+ TEST_FLAGS(0, flatcc_json_printer_f_noenum,
+ "{ \"name\": \"Monster\", \"color\": \"Green\"}",
+ "{\"name\":\"Monster\",\"color\":2}");
+
+ TEST_FLAGS(flatcc_json_parser_f_force_add, flatcc_json_printer_f_skip_default,
+ "{ \"name\": \"Monster\", \"color\": 8}",
+ "{\"name\":\"Monster\"}");
+
+ TEST_FLAGS(0, flatcc_json_printer_f_force_default,
+ "{ \"name\": \"Monster\", \"testf\":3.0}",
+"{\"mana\":150,\"hp\":100,\"name\":\"Monster\",\"color\":\"Blue\",\"testbool\":true,\"testhashs32_fnv1\":0,\"testhashu32_fnv1\":0,\"testhashs64_fnv1\":0,\"testhashu64_fnv1\":0,\"testhashs32_fnv1a\":0,\"testhashu32_fnv1a\":0,\"testhashs64_fnv1a\":0,\"testhashu64_fnv1a\":0,\"testf\":3,\"testf2\":3,\"testf3\":0}");
+
+
+ /*
+ * Cannot test the default of testf field because float is printed as double with
+ * configuration dependent precision.
+ */
+#if 0
+ TEST_FLAGS(0, flatcc_json_printer_f_force_default,
+ "{ \"name\": \"Monster\", \"testf3\":3.14159012}",
+"{\"mana\":150,\"hp\":100,\"name\":\"Monster\",\"color\":\"Blue\",\"testbool\":true,\"testhashs32_fnv1\":0,\"testhashu32_fnv1\":0,\"testhashs64_fnv1\":0,\"testhashu64_fnv1\":0,\"testhashs32_fnv1a\":0,\"testhashu32_fnv1a\":0,\"testhashs64_fnv1a\":0,\"testhashu64_fnv1a\":0,\"testf\":3.14159,\"testf2\":3,\"testf3\":0}");
+#endif
+
+ TEST_FLAGS(flatcc_json_parser_f_force_add, 0,
+ "{ \"name\": \"Monster\", \"color\": \"Blue\"}",
+ "{\"name\":\"Monster\",\"color\":\"Blue\"}");
+
+ TEST_FLAGS(flatcc_json_parser_f_skip_unknown, 0,
+ "{ \"name\": \"Monster\", \"xcolor\": \"Green\", \"hp\": 42}",
+ "{\"hp\":42,\"name\":\"Monster\"}");
+
+ TEST_FLAGS(flatcc_json_parser_f_skip_unknown, flatcc_json_printer_f_unquote,
+ "{ \"name\": \"Monster\", \"xcolor\": \"Green\", \"hp\": 42}",
+ "{hp:42,name:\"Monster\"}");
+
+ /* Also test generic parser used with unions with late type field. */
+ TEST_FLAGS(flatcc_json_parser_f_skip_unknown, 0,
+ "{ \"name\": \"Monster\", \"xcolor\": \"Green\", "
+ "\"foobar\": { \"a\": [1, 2.0, ], \"a1\": {}, \"b\": null, \"c\":[], }, \"hp\": 42 }",
+ "{\"hp\":42,\"name\":\"Monster\"}");
+#if UQ
+/*
+ * If a value is stored, even if default, it is also printed.
+ * This option can also be flagged compile time for better performance.
+ */
+ TEST_FLAGS(flatcc_json_parser_f_force_add, 0,
+ "{ name: \"Monster\", color: 8}",
+ "{\"name\":\"Monster\",\"color\":\"Blue\"}");
+
+ TEST_FLAGS(0, flatcc_json_printer_f_noenum,
+ "{ name: \"Monster\", color: Green}",
+ "{\"name\":\"Monster\",\"color\":2}");
+
+ TEST_FLAGS(flatcc_json_parser_f_force_add, flatcc_json_printer_f_skip_default,
+ "{ name: \"Monster\", color: 8}",
+ "{\"name\":\"Monster\"}");
+
+ TEST_FLAGS(0, flatcc_json_printer_f_force_default,
+ "{ name: \"Monster\"}",
+"{\"mana\":150,\"hp\":100,\"name\":\"Monster\",\"color\":\"Blue\",\"testbool\":true,\"testhashs32_fnv1\":0,\"testhashu32_fnv1\":0,\"testhashs64_fnv1\":0,\"testhashu64_fnv1\":0,\"testhashs32_fnv1a\":0,\"testhashu32_fnv1a\":0,\"testhashs64_fnv1a\":0,\"testhashu64_fnv1a\":0,\"testf\":314159,\"testf2\":3,\"testf3\":0}");
+
+ TEST_FLAGS(flatcc_json_parser_f_force_add, 0,
+ "{ name: \"Monster\", color: Blue}",
+ "{\"name\":\"Monster\",\"color\":\"Blue\"}");
+
+ TEST_FLAGS(flatcc_json_parser_f_skip_unknown, 0,
+ "{ name: \"Monster\", xcolor: Green, hp: 42}",
+ "{\"hp\":42,\"name\":\"Monster\"}");
+
+ TEST_FLAGS(flatcc_json_parser_f_skip_unknown, flatcc_json_printer_f_unquote,
+ "{ name: \"Monster\", xcolor: Green, hp: 42}",
+ "{hp:42,name:\"Monster\"}");
+
+ /* Also test generic parser used with unions with late type field. */
+ TEST_FLAGS(flatcc_json_parser_f_skip_unknown, 0,
+ "{ name: \"Monster\", xcolor: Green, "
+ "foobar: { a: [1, 2.0, ], a1: {}, b: null, c:[], }, hp: 42 }",
+ "{\"hp\":42,\"name\":\"Monster\"}");
+#endif
+
+/* Without skip unknown, we should expect failure. */
+#if 0
+ TEST( "{ name: \"Monster\", xcolor: Green}",
+ "{\"name\":\"Monster\"}");
+#endif
+
+/* We do not support null. */
+#if 0
+ TEST(
+ "{ name: \"Monster\", test_type: null }",
+ "{\"name\":\"Monster\"}");
+#endif
+
+/*
+ * We do not allow empty flag strings because they might mean
+ * either default value, or 0.
+ */
+#if 0
+ /* Questionable if this really is an error. */
+ TEST( "{ name: \"Monster\", color: \"\"}",
+ "{\"name\":\"Monster\",\"color\":0}"); // TODO: should this be color:"" ?
+
+ TEST( "{ name: \"Monster\", color: \" \"}",
+ "{\"name\":\"Monster\",\"color\":0}");
+
+#endif
+
+ END_TEST();
+}
+
+int error_case_tests(void)
+{
+ BEGIN_TEST(Monster);
+
+ TEST_ERROR( "{ \"nickname\": \"Monster\" }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ \"name\": \"Monster\", \"test_type\": \"Monster\", \"test\": { \"nickname\": \"Joker\", \"color\": \"Red\" } } }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ \"name\": \"Monster\", \"test_type\": \"Monster\", \"test\": { \"name\": \"Joker\", \"colour\": \"Red\" } } }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ \"name\": \"Monster\", \"testarrayoftables\": [ { \"nickname\": \"Joker\", \"color\": \"Red\" } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ \"name\": \"Monster\", \"testarrayoftables\": [ { \"name\": \"Joker\", \"colour\": \"Red\" } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ \"name\": \"Monster\", \"testarrayoftables\": ["
+ "{ \"name\": \"Joker\", \"color\": \"Red\", \"test_type\": \"Monster\", \"test\": { \"nickname\": \"Harley\", \"color\": \"Blue\" } } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ \"name\": \"Monster\", \"testarrayoftables\": ["
+ "{ \"name\": \"Joker\", \"color\": \"Red\", \"test_type\": \"Monster\", \"test\": { \"name\": \"Harley\", \"colour\": \"Blue\" } } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ \"name\": \"Monster\", \"testarrayoftables\": ["
+ "{ \"name\": \"Joker\", \"test_type\": \"Monster\", \"test\": { \"nickname\": \"Harley\" } },"
+ "{ \"name\": \"Bonnie\", \"test_type\": \"Monster\", \"test\": { \"name\": \"Clyde\" } } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ \"name\": \"Monster\", \"testarrayoftables\": ["
+ "{ \"name\": \"Joker\", \"test_type\": \"Monster\", \"test\": { \"name\": \"Harley\" } },"
+ "{ \"name\": \"Bonnie\", \"test_type\": \"Monster\", \"test\": { \"nickname\": \"Clyde\" } } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+
+#if !UQ
+ TEST_ERROR( "{ nickname: \"Monster\" }",
+ flatcc_json_parser_error_unexpected_character );
+
+ TEST_ERROR( "{ \"name\": \"Monster\", \"color\": Green }",
+ flatcc_json_parser_error_unexpected_character );
+
+ TEST_ERROR( "{ \"name\": \"Monster\", \"color\": Green Red Blue }",
+ flatcc_json_parser_error_unexpected_character );
+#endif
+
+#if UQ
+ TEST_ERROR( "{ nickname: \"Monster\" }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ name: \"Monster\", test_type: Monster, test: { nickname: \"Joker\", color: \"Red\" } } }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ name: \"Monster\", test_type: Monster, test: { name: \"Joker\", colour: \"Red\" } } }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ name: \"Monster\", testarrayoftables: [ { nickname: \"Joker\", color: \"Red\" } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ name: \"Monster\", testarrayoftables: [ { name: \"Joker\", colour: \"Red\" } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ name: \"Monster\", testarrayoftables: ["
+ "{ name: \"Joker\", color: \"Red\", test_type: Monster, test: { nickname: \"Harley\", color: \"Blue\" } } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ name: \"Monster\", testarrayoftables: ["
+ "{ name: \"Joker\", color: \"Red\", test_type: Monster, test: { name: \"Harley\", colour: \"Blue\" } } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ name: \"Monster\", testarrayoftables: ["
+ "{ name: \"Joker\", test_type: Monster, test: { nickname: \"Harley\" } },"
+ "{ name: \"Bonnie\", test_type: Monster, test: { name: \"Clyde\" } } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+ TEST_ERROR( "{ name: \"Monster\", testarrayoftables: ["
+ "{ name: \"Joker\", test_type: Monster, test: { name: \"Harley\" } },"
+ "{ name: \"Bonnie\", test_type: Monster, test: { nickname: \"Clyde\" } } ] }",
+ flatcc_json_parser_error_unknown_symbol );
+
+#endif
+
+ END_TEST();
+}
+
+#define RANDOM_BASE64 "zLOuiUjH49tz4Ap2JnmpTX5NqoiMzlD8hSw45QCS2yaSp7UYoA" \
+ "oE8KpY/5pKYmk+54NI40hyeyZ1zRUE4vKQT0hEdVl0iXq2fqPamkVD1AZlVvQJ1m00PaoXOSgG+64Zv+Uygw=="
+
+#define RANDOM_BASE64_NOPAD "zLOuiUjH49tz4Ap2JnmpTX5NqoiMzlD8hSw45QCS2yaSp7UYoA" \
+ "oE8KpY/5pKYmk+54NI40hyeyZ1zRUE4vKQT0hEdVl0iXq2fqPamkVD1AZlVvQJ1m00PaoXOSgG+64Zv+Uygw"
+
+#define RANDOM_BASE64URL "zLOuiUjH49tz4Ap2JnmpTX5NqoiMzlD8hSw45QCS2yaSp7UYoA" \
+ "oE8KpY_5pKYmk-54NI40hyeyZ1zRUE4vKQT0hEdVl0iXq2fqPamkVD1AZlVvQJ1m00PaoXOSgG-64Zv-Uygw=="
+
+#define RANDOM_BASE64URL_NOPAD "zLOuiUjH49tz4Ap2JnmpTX5NqoiMzlD8hSw45QCS2yaSp7UYoA" \
+ "oE8KpY_5pKYmk-54NI40hyeyZ1zRUE4vKQT0hEdVl0iXq2fqPamkVD1AZlVvQJ1m00PaoXOSgG-64Zv-Uygw"
+
+int base64_tests(void)
+{
+ BEGIN_TEST(Monster);
+
+ /* Reference */
+ TEST( "{ \"name\": \"Monster\" }",
+ "{\"name\":\"Monster\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"testbase64\":{} }",
+ "{\"name\":\"Monster\",\"testbase64\":{}}");
+
+
+ TEST( "{ \"name\": \"Monster\", \"testbase64\":{ \"data\":\"" RANDOM_BASE64 "\"} }",
+ "{\"name\":\"Monster\",\"testbase64\":{\"data\":\"" RANDOM_BASE64 "\"}}");
+
+ TEST( "{ \"name\": \"Monster\", \"testbase64\":{ \"urldata\":\"" RANDOM_BASE64URL "\"} }",
+ "{\"name\":\"Monster\",\"testbase64\":{\"urldata\":\"" RANDOM_BASE64URL "\"}}");
+
+ TEST( "{ \"name\": \"Monster\", \"testbase64\":{ \"data\":\"" RANDOM_BASE64_NOPAD "\"} }",
+ "{\"name\":\"Monster\",\"testbase64\":{\"data\":\"" RANDOM_BASE64 "\"}}");
+
+ TEST( "{ \"name\": \"Monster\", \"testbase64\":{ \"urldata\":\"" RANDOM_BASE64URL_NOPAD "\"} }",
+ "{\"name\":\"Monster\",\"testbase64\":{\"urldata\":\"" RANDOM_BASE64URL "\"}}");
+
+ TEST_ERROR( "{ \"name\": \"Monster\", \"testbase64\":{ \"data\":\"" RANDOM_BASE64URL "\"} }",
+ flatcc_json_parser_error_base64);
+
+ TEST_ERROR( "{ \"name\": \"Monster\", \"testbase64\":{ \"urldata\":\"" RANDOM_BASE64 "\"} }",
+ flatcc_json_parser_error_base64url);
+
+/* Test case from Googles flatc implementation. */
+#if UQ
+ TEST( "{name: \"Monster\","
+ "testbase64: {"
+ "data: \"23A/47d450+sdfx9+wRYIS09ckas/asdFBQ=\","
+ "urldata: \"23A_47d450-sdfx9-wRYIS09ckas_asdFBQ=\","
+ "nested: \"FAAAAE1PTlMMAAwAAAAEAAYACAAMAAAAAAAAAAQAAAANAAAATmVzdGVkTW9uc3RlcgAAAA==\""
+ "}}",
+ "{\"name\":\"Monster\","
+ "\"testbase64\":{"
+ "\"data\":\"23A/47d450+sdfx9+wRYIS09ckas/asdFBQ=\","
+ "\"urldata\":\"23A_47d450-sdfx9-wRYIS09ckas_asdFBQ=\","
+ "\"nested\":\"FAAAAE1PTlMMAAwAAAAEAAYACAAMAAAAAAAAAAQAAAANAAAATmVzdGVkTW9uc3RlcgAAAA==\""
+ "}}");
+
+ TEST( "{name: \"Monster\","
+ "testbase64: {"
+ "data: \"23A/47d450+sdfx9+wRYIS09ckas/asdFBQ\","
+ "urldata: \"23A_47d450-sdfx9-wRYIS09ckas_asdFBQ\","
+ "nested: \"FAAAAE1PTlMMAAwAAAAEAAYACAAMAAAAAAAAAAQAAAANAAAATmVzdGVkTW9uc3RlcgAAAA\""
+ "}}",
+ "{\"name\":\"Monster\","
+ "\"testbase64\":{"
+ "\"data\":\"23A/47d450+sdfx9+wRYIS09ckas/asdFBQ=\","
+ "\"urldata\":\"23A_47d450-sdfx9-wRYIS09ckas_asdFBQ=\","
+ "\"nested\":\"FAAAAE1PTlMMAAwAAAAEAAYACAAMAAAAAAAAAAQAAAANAAAATmVzdGVkTW9uc3RlcgAAAA==\""
+ "}}");
+#endif
+
+ END_TEST();
+}
+
+int mixed_type_union_tests(void)
+{
+ BEGIN_TEST(Movie);
+
+ /* Reference */
+
+ TEST( "{ \"main_character_type\": \"Rapunzel\", \"main_character\": { \"hair_length\": 19 } }",
+ "{\"main_character_type\":\"Rapunzel\",\"main_character\":{\"hair_length\":19}}");
+
+ TEST( "{ \"main_character_type\": \"Rapunzel\", \"main_character\": { \"hair_length\": 19 },"
+ " \"side_kick_type\": \"Other\", \"side_kick\": \"a donkey\"}",
+ "{\"main_character_type\":\"Rapunzel\",\"main_character\":{\"hair_length\":19},"
+ "\"side_kick_type\":\"Other\",\"side_kick\":\"a donkey\"}");
+
+ TEST( "{ \"main_character_type\": \"Rapunzel\", \"main_character\": { \"hair_length\": 19 },"
+ " \"side_kick_type\": \"Fantasy.Character.Other\", \"side_kick\": \"a donkey\"}}",
+ "{\"main_character_type\":\"Rapunzel\",\"main_character\":{\"hair_length\":19},"
+ "\"side_kick_type\":\"Other\",\"side_kick\":\"a donkey\"}");
+
+ TEST( "{ \"main_character_type\": \"Rapunzel\", \"main_character\": { \"hair_length\": 19 },"
+ " \"side_kick_type\": \"Fantasy.Character.Other\", \"side_kick\": \"a donkey\","
+ " \"antagonist_type\": \"MuLan\", \"antagonist\": {\"sword_attack_damage\": 42}}",
+ "{\"main_character_type\":\"Rapunzel\",\"main_character\":{\"hair_length\":19},"
+ "\"antagonist_type\":\"MuLan\",\"antagonist\":{\"sword_attack_damage\":42},"
+ "\"side_kick_type\":\"Other\",\"side_kick\":\"a donkey\"}");
+
+ TEST( "{ \"main_character_type\": \"Rapunzel\", \"main_character\": { \"hair_length\": 19 },"
+ " \"side_kick_type\": \"Fantasy.Character.Other\", \"side_kick\": \"a donkey\","
+ " \"antagonist_type\": \"MuLan\", \"antagonist\": {\"sword_attack_damage\": 42},"
+ " \"characters_type\": [], \"characters\": []}",
+ "{\"main_character_type\":\"Rapunzel\",\"main_character\":{\"hair_length\":19},"
+ "\"antagonist_type\":\"MuLan\",\"antagonist\":{\"sword_attack_damage\":42},"
+ "\"side_kick_type\":\"Other\",\"side_kick\":\"a donkey\","
+ "\"characters_type\":[],\"characters\":[]}")
+
+ TEST( "{ \"main_character_type\": \"Rapunzel\", \"main_character\": { \"hair_length\": 19 },"
+ " \"side_kick_type\": \"Fantasy.Character.Other\", \"side_kick\": \"a donkey\","
+ " \"antagonist_type\": \"MuLan\", \"antagonist\": {\"sword_attack_damage\": 42},"
+ " \"characters_type\": [\"Fantasy.Character.Rapunzel\", \"Other\", 0, \"MuLan\"],"
+ " \"characters\": [{\"hair_length\":19}, \"unattributed extras\", null, {\"sword_attack_damage\":2}]}",
+ "{\"main_character_type\":\"Rapunzel\",\"main_character\":{\"hair_length\":19},"
+ "\"antagonist_type\":\"MuLan\",\"antagonist\":{\"sword_attack_damage\":42},"
+ "\"side_kick_type\":\"Other\",\"side_kick\":\"a donkey\","
+ "\"characters_type\":[\"Rapunzel\",\"Other\",\"NONE\",\"MuLan\"],"
+ "\"characters\":[{\"hair_length\":19},\"unattributed extras\",null,{\"sword_attack_damage\":2}]}")
+
+ TEST( "{ \"main_character_type\": \"Rapunzel\", \"main_character\": { \"hair_length\": 19 },"
+ " \"side_kick_type\": \"Character.Other\", \"side_kick\": \"a donkey\"}",
+ "{\"main_character_type\":\"Rapunzel\",\"main_character\":{\"hair_length\":19},"
+ "\"side_kick_type\":\"Other\",\"side_kick\":\"a donkey\"}");
+
+ END_TEST();
+}
+
+int union_vector_tests(void)
+{
+ BEGIN_TEST(Alt);
+ /* Union vector */
+
+ TEST( "{ \"manyany_type\": [ \"Monster\" ], \"manyany\": [{\"name\": \"Joe\"}] }",
+ "{\"manyany_type\":[\"Monster\"],\"manyany\":[{\"name\":\"Joe\"}]}");
+
+ TEST( "{\"manyany_type\": [ \"NONE\" ], \"manyany\": [ null ] }",
+ "{\"manyany_type\":[\"NONE\"],\"manyany\":[null]}");
+
+ TEST( "{\"manyany_type\": [ \"Monster\", \"NONE\" ], \"manyany\": [{\"name\": \"Joe\"}, null] }",
+ "{\"manyany_type\":[\"Monster\",\"NONE\"],\"manyany\":[{\"name\":\"Joe\"},null]}");
+
+ TEST( "{\"manyany_type\": [ \"Monster\" ], \"manyany\": [ { \"name\":\"Joe\", \"test_type\": \"Monster\", \"test\": { \"name\": \"any Monster\" } } ] }",
+ "{\"manyany_type\":[\"Monster\"],\"manyany\":[{\"name\":\"Joe\",\"test_type\":\"Monster\",\"test\":{\"name\":\"any Monster\"}}]}");
+
+ TEST( "{\"manyany\": [{\"name\": \"Joe\"}], \"manyany_type\": [ \"Monster\" ] }",
+ "{\"manyany_type\":[\"Monster\"],\"manyany\":[{\"name\":\"Joe\"}]}");
+
+ TEST( "{\"manyany\": [{\"manyany\":[null, null], \"manyany_type\": [\"NONE\", \"NONE\"]}], \"manyany_type\": [ \"Alt\" ] }",
+ "{\"manyany_type\":[\"Alt\"],\"manyany\":[{\"manyany_type\":[\"NONE\",\"NONE\"],\"manyany\":[null,null]}]}");
+
+ END_TEST();
+}
+
+int fixed_array_tests(void)
+{
+ BEGIN_TEST(Alt);
+ /* Fixed array */
+
+#if UQ
+ TEST( "{ \"fixed_array\": { \"foo\": [ 1.0, 2.0, 0, 0, 0, 0, 0,"
+ " 0, 0, 0, 0, 0, 0, 0, 0, 16.0], col:[\"Blue Red\", Green, Red],"
+ "tests:[ {b:4}, {a:1, b:2}],"
+ " \"bar\": [ 100, 0, 0, 0, 0, 0, 0, 0, 0, 1000],"
+ " \"text\":\"hello\"}}",
+ "{\"fixed_array\":{\"foo\":[1,2,0,0,0,0,0,"
+ "0,0,0,0,0,0,0,0,16],"
+ "\"bar\":[100,0,0,0,0,0,0,0,0,1000],"
+ "\"col\":[\"Red Blue\",\"Green\",\"Red\"],"
+ "\"tests\":[{\"a\":0,\"b\":4},{\"a\":1,\"b\":2}],"
+ "\"text\":\"hello\"}}");
+#else
+ TEST( "{ \"fixed_array\": { \"foo\": [ 1.0, 2.0, 0, 0, 0, 0, 0,"
+ " 0, 0, 0, 0, 0, 0, 0, 0, 16.0], \"col\":[\"Blue Red\", \"Green\", \"Red\"],"
+ "\"tests\":[ {\"b\":4}, {\"a\":1, \"b\":2}],"
+ " \"bar\": [ 100, 0, 0, 0, 0, 0, 0, 0, 0, 1000],"
+ " \"text\":\"hello\"}}",
+ "{\"fixed_array\":{\"foo\":[1,2,0,0,0,0,0,"
+ "0,0,0,0,0,0,0,0,16],"
+ "\"bar\":[100,0,0,0,0,0,0,0,0,1000],"
+ "\"col\":[\"Red Blue\",\"Green\",\"Red\"],"
+ "\"tests\":[{\"a\":0,\"b\":4},{\"a\":1,\"b\":2}],"
+ "\"text\":\"hello\"}}");
+#endif
+
+ TEST_FLAGS(flatcc_json_parser_f_skip_array_overflow, 0,
+ "{ \"fixed_array\": { \"foo\": [ 1.0, 2.0, 0, 0, 0, 0, 0,"
+ " 0, 0, 0, 0, 0, 0, 0, 0, 16.0, 99],"
+ " \"bar\": [ 100, 0, 0, 0, 0, 0, 0, 0, 0, 1000, 99],"
+ " \"text\":\"hello, world\"}}",
+ "{\"fixed_array\":{\"foo\":[1,2,0,0,0,0,0,"
+ "0,0,0,0,0,0,0,0,16],"
+ "\"bar\":[100,0,0,0,0,0,0,0,0,1000],"
+ "\"col\":[0,0,0],"
+ "\"tests\":[{\"a\":0,\"b\":0},{\"a\":0,\"b\":0}],"
+ "\"text\":\"hello\"}}");
+
+ TEST( "{ \"fixed_array\": { \"foo\": [ 1.0, 2.0 ],"
+ " \"bar\": [ 100 ], \"text\": \"K\\x00A\\x00\" }}",
+ "{\"fixed_array\":{\"foo\":[1,2,0,0,0,0,0,"
+ "0,0,0,0,0,0,0,0,0],"
+ "\"bar\":[100,0,0,0,0,0,0,0,0,0],"
+ "\"col\":[0,0,0],"
+ "\"tests\":[{\"a\":0,\"b\":0},{\"a\":0,\"b\":0}],"
+ "\"text\":\"K\\u0000A\"}}");
+
+ TEST_ERROR_FLAGS(flatcc_json_parser_f_reject_array_underflow, 0,
+ "{ \"fixed_array\": { \"foo\": [ 1.0, 2.0 ] }}",
+ flatcc_json_parser_error_array_underflow);
+
+ TEST_ERROR_FLAGS(flatcc_json_parser_f_reject_array_underflow, 0,
+ "{ \"fixed_array\": { \"text\": \"K\\x00A\\x00\" }}",
+ flatcc_json_parser_error_array_underflow);
+
+ TEST_ERROR(
+ "{ \"fixed_array\": { \"foo\": [ 1.0, 2.0, 0, 0, 0, 0, 0,"
+ " 0, 0, 0, 0, 0, 0, 0, 0, 16.0, 99],"
+ " \"bar\": [ 100, 0, 0, 0, 0, 0, 0, 0, 0, 1000, 99] }}",
+ flatcc_json_parser_error_array_overflow);
+
+ END_TEST();
+}
+
+/*
+ * Here we cover some border cases around unions and flag
+ * enumerations, and nested buffers.
+ *
+ * More complex objects with struct members etc. are reasonably
+ * covered in the printer and parser tests using the golden data
+ * set.
+ */
+int main(void)
+{
+ BEGIN_TEST(Monster);
+
+ ret |= edge_case_tests();
+ ret |= error_case_tests();
+ ret |= union_vector_tests();
+ ret |= fixed_array_tests();
+ ret |= base64_tests();
+ ret |= mixed_type_union_tests();
+
+ /* Allow trailing comma. */
+ TEST( "{ \"name\": \"Monster\", }",
+ "{\"name\":\"Monster\"}");
+
+ TEST( "{\"color\": \"Red\", \"name\": \"Monster\", }",
+ "{\"name\":\"Monster\",\"color\":\"Red\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": \"Green\" }",
+ "{\"name\":\"Monster\",\"color\":\"Green\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": \"Green Red Blue\" }",
+ "{\"name\":\"Monster\",\"color\":\"Red Green Blue\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": \" Green Red Blue \" }",
+ "{\"name\":\"Monster\",\"color\":\"Red Green Blue\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": \"Red\" }",
+ "{\"name\":\"Monster\",\"color\":\"Red\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\" :\"Green\" }",
+ "{\"name\":\"Monster\",\"color\":\"Green\"}");
+
+ /* Default value. */
+ TEST( "{ \"name\": \"Monster\", \"color\": \"Blue\" }",
+ "{\"name\":\"Monster\"}");
+
+ /* Default value. */
+ TEST( "{ \"name\": \"Monster\", \"color\": 8}",
+ "{\"name\":\"Monster\"}");
+#if UQ
+ /* Allow trailing comma. */
+ TEST( "{ name: \"Monster\", }",
+ "{\"name\":\"Monster\"}");
+
+ TEST( "{color: \"Red\", name: \"Monster\", }",
+ "{\"name\":\"Monster\",\"color\":\"Red\"}");
+
+ TEST( "{ name: \"Monster\", color: \"Green\" }",
+ "{\"name\":\"Monster\",\"color\":\"Green\"}");
+
+ TEST( "{ name: \"Monster\", color: \"Green Red Blue\" }",
+ "{\"name\":\"Monster\",\"color\":\"Red Green Blue\"}");
+
+ TEST( "{ name: \"Monster\", color: \" Green Red Blue \" }",
+ "{\"name\":\"Monster\",\"color\":\"Red Green Blue\"}");
+
+ TEST( "{ name: \"Monster\", color: Red }",
+ "{\"name\":\"Monster\",\"color\":\"Red\"}");
+
+ TEST( "{ name: \"Monster\", color: Green }",
+ "{\"name\":\"Monster\",\"color\":\"Green\"}");
+
+ /* Default value. */
+ TEST( "{ name: \"Monster\", color: Blue }",
+ "{\"name\":\"Monster\"}");
+
+ /* Default value. */
+ TEST( "{ name: \"Monster\", color: 8}",
+ "{\"name\":\"Monster\"}");
+#endif
+#if UQL
+ TEST( "{ name: \"Monster\", color: Green Red }",
+ "{\"name\":\"Monster\",\"color\":\"Red Green\"}");
+#endif
+
+#if UQL
+ /* No leading space in unquoted flag. */
+ TEST( "{ name: \"Monster\", color:Green Red }",
+ "{\"name\":\"Monster\",\"color\":\"Red Green\"}");
+
+ TEST( "{ name: \"Monster\", color: Green Red}",
+ "{\"name\":\"Monster\",\"color\":\"Red Green\"}");
+
+ TEST( "{ name: \"Monster\", color:Green Blue Red }",
+ "{\"name\":\"Monster\",\"color\":\"Red Green Blue\"}");
+#endif
+
+ TEST( "{ \"name\": \"Monster\", \"color\": 1}",
+ "{\"name\":\"Monster\",\"color\":\"Red\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": 2}",
+ "{\"name\":\"Monster\",\"color\":\"Green\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": 9}",
+ "{\"name\":\"Monster\",\"color\":\"Red Blue\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": 11}",
+ "{\"name\":\"Monster\",\"color\":\"Red Green Blue\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": 12}",
+ "{\"name\":\"Monster\",\"color\":12}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": 15}",
+ "{\"name\":\"Monster\",\"color\":15}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": 0}",
+ "{\"name\":\"Monster\",\"color\":0}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": \"Color.Red\"}",
+ "{\"name\":\"Monster\",\"color\":\"Red\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"color\": \"MyGame.Example.Color.Red\"}",
+ "{\"name\":\"Monster\",\"color\":\"Red\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"hp\": \"Color.Green\"}",
+ "{\"hp\":2,\"name\":\"Monster\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"hp\": \"Color.Green\"}",
+ "{\"hp\":2,\"name\":\"Monster\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"test_type\": \"Monster\", \"test\": { \"name\": \"any Monster\" } }",
+ "{\"name\":\"Monster\",\"test_type\":\"Monster\",\"test\":{\"name\":\"any Monster\"}}");
+
+ /* This is tricky because the test field must be reparsed after discovering the test type. */
+ TEST( "{ \"name\": \"Monster\", \"test\": { \"name\": \"second Monster\" }, \"test_type\": \"Monster\" }",
+ "{\"name\":\"Monster\",\"test_type\":\"Monster\",\"test\":{\"name\":\"second Monster\"}}");
+
+ /* Also test that parsing can continue after reparse. */
+ TEST( "{ \"name\": \"Monster\", \"test\": { \"name\": \"second Monster\" }, \"hp\":17, \"test_type\":\n \"Monster\", \"color\":\"Green\" }",
+ "{\"hp\":17,\"name\":\"Monster\",\"color\":\"Green\",\"test_type\":\"Monster\",\"test\":{\"name\":\"second Monster\"}}");
+
+ /* Test that NONE is recognized, and that we do not get a missing table error.*/
+ TEST( "{ \"name\": \"Monster\", \"test_type\": \"NONE\" }",
+ "{\"name\":\"Monster\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"test_type\": 0 }",
+ "{\"name\":\"Monster\"}");
+
+#if UQ
+ TEST( "{ name: \"Monster\", color: 1}",
+ "{\"name\":\"Monster\",\"color\":\"Red\"}");
+
+ TEST( "{ name: \"Monster\", color: 2}",
+ "{\"name\":\"Monster\",\"color\":\"Green\"}");
+
+ TEST( "{ name: \"Monster\", color: 9}",
+ "{\"name\":\"Monster\",\"color\":\"Red Blue\"}");
+
+ TEST( "{ name: \"Monster\", color: 11}",
+ "{\"name\":\"Monster\",\"color\":\"Red Green Blue\"}");
+
+ TEST( "{ name: \"Monster\", color: 12}",
+ "{\"name\":\"Monster\",\"color\":12}");
+
+ TEST( "{ name: \"Monster\", color: 15}",
+ "{\"name\":\"Monster\",\"color\":15}");
+
+ TEST( "{ name: \"Monster\", color: 0}",
+ "{\"name\":\"Monster\",\"color\":0}");
+
+ TEST( "{ name: \"Monster\", color: Color.Red}",
+ "{\"name\":\"Monster\",\"color\":\"Red\"}");
+
+ TEST( "{ name: \"Monster\", color: MyGame.Example.Color.Red}",
+ "{\"name\":\"Monster\",\"color\":\"Red\"}");
+
+ TEST( "{ name: \"Monster\", hp: Color.Green}",
+ "{\"hp\":2,\"name\":\"Monster\"}");
+
+ TEST( "{ name: \"Monster\", hp: Color.Green}",
+ "{\"hp\":2,\"name\":\"Monster\"}");
+
+ TEST( "{ name: \"Monster\", test_type: Monster, test: { name: \"any Monster\" } }",
+ "{\"name\":\"Monster\",\"test_type\":\"Monster\",\"test\":{\"name\":\"any Monster\"}}");
+
+ /* This is tricky because the test field must be reparsed after discovering the test type. */
+ TEST( "{ name: \"Monster\", test: { name: \"second Monster\" }, test_type: Monster }",
+ "{\"name\":\"Monster\",\"test_type\":\"Monster\",\"test\":{\"name\":\"second Monster\"}}");
+
+ /* Also test that parsing can continue after reparse. */
+ TEST( "{ name: \"Monster\", test: { name: \"second Monster\" }, hp:17, test_type:\n Monster, color:Green }",
+ "{\"hp\":17,\"name\":\"Monster\",\"color\":\"Green\",\"test_type\":\"Monster\",\"test\":{\"name\":\"second Monster\"}}");
+
+ /* Test that NONE is recognized, and that we do not get a missing table error.*/
+ TEST( "{ name: \"Monster\", test_type: NONE }",
+ "{\"name\":\"Monster\"}");
+
+ TEST( "{ name: \"Monster\", test_type: 0 }",
+ "{\"name\":\"Monster\"}");
+
+#endif
+
+#if UQL
+ /*
+ * Test that generic parsing handles multiple flags correctly during
+ * first pass before backtracking.
+ */
+ TEST( "{ name: \"Monster\", test: { name: \"second Monster\", color: Red Green }, test_type: Monster }",
+ "{\"name\":\"Monster\",\"test_type\":\"Monster\",\"test\":{\"name\":\"second Monster\",\"color\":\"Red Green\"}}");
+#endif
+
+ /* Ditto quoted flags. */
+ TEST( "{ \"name\": \"Monster\", \"test\": { \"name\": \"second Monster\", \"color\": \" Red Green \" }, \"test_type\": \"Monster\" }",
+ "{\"name\":\"Monster\",\"test_type\":\"Monster\",\"test\":{\"name\":\"second Monster\",\"color\":\"Red Green\"}}");
+
+ /*
+ * Note the '\/' becomes just '/', and that '/' also works in input.
+ *
+ * The json printer does not have a concept of \x it always uses
+ * unicode.
+ *
+ * We use json extension \x to inject a control 03 which extends
+ * to a printed unicode escape, while the \u00F8 is a valid
+ * character after encoding, and is thus not escaped after printing
+ * but rather becoems a two-byte utf-8 encoding of 'ø' which
+ * we use C encoding to form utf8 C3B8 == \u00F8.
+ */
+ TEST( "{ \"name\": \"Mon\xfF\xFf\\x03s\\xC3\\xF9\\u00F8ter\\b\\f\\n\\r\\t\\\"\\\\\\/'/\", }",
+ "{\"name\":\"Mon\xff\xff\\u0003s\xc3\xf9\xc3\xb8ter\\b\\f\\n\\r\\t\\\"\\\\/'/\"}");
+
+ TEST( "{ \"name\": \"\\u168B\\u1691\"}",
+ "{\"name\":\"\xe1\x9a\x8b\xe1\x9a\x91\"}");
+
+ /* Nested flatbuffer, either is a known object, or as a vector. */
+ TEST( "{ \"name\": \"Monster\", \"testnestedflatbuffer\":{ \"name\": \"sub Monster\" } }",
+ "{\"name\":\"Monster\",\"testnestedflatbuffer\":{\"name\":\"sub Monster\"}}");
+
+#if FLATBUFFERS_PROTOCOL_IS_LE
+ TEST( "{ \"name\": \"Monster\", \"testnestedflatbuffer\":"
+ "[" /* start of nested flatbuffer, implicit size: 40 */
+ "4,0,0,0," /* header: object offset = 4, no identifier */
+ "248,255,255,255," /* vtable offset */
+ "16,0,0,0," /* offset to name */
+ "12,0,8,0,0,0,0,0,0,0,4,0," /* vtable */
+ "11,0,0,0,115,117,98,32,77,111,110,115,116,101,114,0" /* name = "sub Monster" */
+ "]" /* end of nested flatbuffer */
+ "}",
+ "{\"name\":\"Monster\",\"testnestedflatbuffer\":{\"name\":\"sub Monster\"}}");
+#else
+ TEST( "{ \"name\": \"Monster\", \"testnestedflatbuffer\":"
+ "[" /* start of nested flatbuffer, implicit size: 40 */
+ "0,0,0,4," /* header: object offset = 4, no identifier */
+ "255,255,255,248," /* vtable offset */
+ "0,0,0,16," /* offset to name */
+ "0,12,0,8,0,0,0,0,0,0,0,4," /* vtable */
+ "0,0,0,11,115,117,98,32,77,111,110,115,116,101,114,0" /* name = "sub Monster" */
+ "]" /* end of nested flatbuffer */
+ "}",
+ "{\"name\":\"Monster\",\"testnestedflatbuffer\":{\"name\":\"sub Monster\"}}");
+#endif
+
+ /* Test empty table */
+ TEST( "{ \"name\": \"Monster\", \"testempty\": {} }",
+ "{\"name\":\"Monster\",\"testempty\":{}}");
+
+ /* Test empty array */
+ TEST( "{ \"name\": \"Monster\", \"testarrayoftables\": [] }",
+ "{\"name\":\"Monster\",\"testarrayoftables\":[]}");
+
+ /* Test JSON prefix parsing */
+ TEST( "{ \"name\": \"Monster\", \"test_type\":\"Alt\", \"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing\": { \"aaaa\": \"test\", \"aaaa12345\": 17 } }}}",
+ "{\"name\":\"Monster\",\"test_type\":\"Alt\",\"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing\":{\"aaaa\":\"test\",\"aaaa12345\":17}}}}");
+
+ /* TODO: this parses with the last to }} missing, although it does not add the broken objects. */
+ TEST( "{ \"name\": \"Monster\", \"test_type\":\"Alt\", \"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing\": { \"bbbb\": \"test\", \"bbbb1234\": 19 } }",
+ "{\"name\":\"Monster\"}");
+
+ TEST( "{ \"name\": \"Monster\", \"test_type\":\"Alt\", \"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing\": { \"bbbb\": \"test\", \"bbbb1234\": 19 } }}}",
+ "{\"name\":\"Monster\",\"test_type\":\"Alt\",\"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing\":{\"bbbb\":\"test\",\"bbbb1234\":19}}}}");
+
+ TEST( "{ \"name\": \"Monster\", \"test_type\":\"Alt\", \"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing\": { \"cccc\": \"test\", \"cccc1234\": 19, \"cccc12345\": 17 } }}}",
+ "{\"name\":\"Monster\",\"test_type\":\"Alt\",\"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing\":{\"cccc\":\"test\",\"cccc1234\":19,\"cccc12345\":17}}}}");
+
+ TEST( "{ \"name\": \"Monster\", \"test_type\":\"Alt\", \"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing\": { \"dddd1234\": 19, \"dddd12345\": 17 } }}}",
+ "{\"name\":\"Monster\",\"test_type\":\"Alt\",\"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing\":{\"dddd1234\":19,\"dddd12345\":17}}}}");
+
+ TEST( "{ \"name\": \"Monster\", \"test_type\":\"Alt\", \"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing2\": { \"aaaa_bbbb_steps\": 19, \"aaaa_bbbb_start_\": 17 } }}}",
+ "{\"name\":\"Monster\",\"test_type\":\"Alt\",\"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing2\":{\"aaaa_bbbb_steps\":19,\"aaaa_bbbb_start_\":17}}}}");
+
+ TEST( "{ \"name\": \"Monster\", \"test_type\":\"Alt\", \"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing3\": { \"aaaa_bbbb_steps\": 19, \"aaaa_bbbb_start_steps\": 17 } }}}",
+ "{\"name\":\"Monster\",\"test_type\":\"Alt\",\"test\":{\"prefix\":{"
+ "\"testjsonprefixparsing3\":{\"aaaa_bbbb_steps\":19,\"aaaa_bbbb_start_steps\":17}}}}");
+
+ return ret ? -1: 0;
+}
diff --git a/flatcc/test/json_test/test_json_parser.c b/flatcc/test/json_test/test_json_parser.c
new file mode 100644
index 0000000..a985cfa
--- /dev/null
+++ b/flatcc/test/json_test/test_json_parser.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+
+#ifndef FLATCC_BENCHMARK
+#define FLATCC_BENCHMARK 0
+#endif
+
+/* Only needed for verification. */
+#include "monster_test_reader.h"
+#include "monster_test_json_parser.h"
+#include "flatcc/support/hexdump.h"
+#include "flatcc/support/cdump.h"
+#include "flatcc/support/readfile.h"
+
+#if FLATCC_BENCHMARK
+#include "flatcc/support/elapsed.h"
+#endif
+
+const char *filename = "monsterdata_test.golden";
+
+#define BENCH_TITLE "monsterdata_test.golden"
+
+#ifdef NDEBUG
+#define COMPILE_TYPE "(optimized)"
+#else
+#define COMPILE_TYPE "(debug)"
+#endif
+
+#define FILE_SIZE_MAX (1024 * 10)
+
+#undef ns
+#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Example, x)
+
+#define test_assert(x) do { if (!(x)) { assert(0); return -1; }} while(0)
+
+/* A helper to simplify creating buffers vectors from C-arrays. */
+#define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
+
+int verify_parse(void *buffer)
+{
+ ns(Test_struct_t) test;
+ ns(Vec3_struct_t) pos;
+ ns(Monster_table_t) monster = ns(Monster_as_root_with_identifier)(buffer, ns(Monster_file_identifier));
+
+ pos = ns(Monster_pos(monster));
+ test_assert(pos);
+ test_assert(ns(Vec3_x(pos) == 1));
+ test_assert(ns(Vec3_y(pos) == 2));
+ test_assert(ns(Vec3_z(pos) == 3));
+ test_assert(ns(Vec3_test1(pos) == 3.0));
+ test_assert(ns(Vec3_test2(pos) == ns(Color_Green)));
+ test = ns(Vec3_test3(pos));
+ test_assert(test);
+ test_assert(ns(Test_a(test)) == 5);
+ test_assert(ns(Test_b(test)) == 6);
+
+ // TODO: hp and further fields
+
+ return 0;
+
+}
+// TODO:
+// when running benchmark with the wrong size argument (output size
+// instead of input size), the warmup loop iterates indefinitely in the
+// first iteration. This suggests there is an end check missing somwhere
+// and this needs to be debugged. The input size as of this writing is 701
+// bytes, and the output size is 288 bytes.
+int test_parse(void)
+{
+#if FLATCC_BENCHMARK
+ double t1, t2;
+ int i;
+ int rep = 1000000;
+ int warmup_rep = 1000000;
+#endif
+
+ const char *buf;
+ void *flatbuffer = 0;
+ size_t in_size, out_size;
+ flatcc_json_parser_t ctx;
+ flatcc_builder_t builder;
+ flatcc_builder_t *B = &builder;
+ int ret = -1;
+ flatcc_json_parser_flags_t flags = 0;
+
+ flatcc_builder_init(B);
+
+ buf = readfile(filename, FILE_SIZE_MAX, &in_size);
+ if (!buf) {
+ fprintf(stderr, "%s: could not read input json file\n", filename);
+ return -1;
+ }
+
+ if (monster_test_parse_json(B, &ctx, buf, in_size, flags)) {
+ goto failed;
+ }
+ fprintf(stderr, "%s: successfully parsed %d lines\n", filename, ctx.line);
+ flatbuffer = flatcc_builder_finalize_aligned_buffer(B, &out_size);
+ hexdump("parsed monsterdata_test.golden", flatbuffer, out_size, stdout);
+ fprintf(stderr, "input size: %lu, output size: %lu\n",
+ (unsigned long)in_size, (unsigned long)out_size);
+ verify_parse(flatbuffer);
+
+ cdump("golden", flatbuffer, out_size, stdout);
+
+ flatcc_builder_reset(B);
+#if FLATCC_BENCHMARK
+ fprintf(stderr, "Now warming up\n");
+ for (i = 0; i < warmup_rep; ++i) {
+ if (monster_test_parse_json(B, &ctx, buf, in_size, flags)) {
+ goto failed;
+ }
+ flatcc_builder_reset(B);
+ }
+
+ fprintf(stderr, "Now benchmarking\n");
+ t1 = elapsed_realtime();
+ for (i = 0; i < rep; ++i) {
+ if (monster_test_parse_json(B, &ctx, buf, in_size, flags)) {
+ goto failed;
+ }
+ flatcc_builder_reset(B);
+ }
+ t2 = elapsed_realtime();
+
+ printf("----\n");
+ show_benchmark(BENCH_TITLE " C generated JSON parse " COMPILE_TYPE, t1, t2, in_size, rep, "1M");
+#endif
+ ret = 0;
+
+done:
+ if (flatbuffer) {
+ flatcc_builder_aligned_free(flatbuffer);
+ }
+ if (buf) {
+ free((void *)buf);
+ }
+ flatcc_builder_clear(B);
+ return ret;
+
+failed:
+ fprintf(stderr, "%s:%d:%d: %s\n",
+ filename, (int)ctx.line, (int)(ctx.error_loc - ctx.line_start + 1),
+ flatcc_json_parser_error_string(ctx.error));
+ goto done;
+}
+
+/* We take arguments so test can run without copying sources. */
+#define usage \
+"wrong number of arguments:\n" \
+"usage: <program> [<input-filename>]\n"
+
+int main(int argc, const char *argv[])
+{
+ fprintf(stderr, "JSON parse test\n");
+
+ if (argc != 1 && argc != 2) {
+ fprintf(stderr, usage);
+ exit(1);
+ }
+ if (argc == 2) {
+ filename = argv[1];
+ }
+ return test_parse();
+}
diff --git a/flatcc/test/json_test/test_json_printer.c b/flatcc/test/json_test/test_json_printer.c
new file mode 100644
index 0000000..efbd572
--- /dev/null
+++ b/flatcc/test/json_test/test_json_printer.c
@@ -0,0 +1,129 @@
+#include <stdio.h>
+
+/* Only needed for verification. */
+#include "monster_test_json_printer.h"
+#include "flatcc/support/readfile.h"
+#include "flatcc_golden.c"
+
+#ifdef NDEBUG
+#define COMPILE_TYPE "(optimized)"
+#else
+#define COMPILE_TYPE "(debug)"
+#endif
+
+#define FILE_SIZE_MAX (1024 * 10)
+
+#undef ns
+#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Example, x)
+
+/* A helper to simplify creating buffers vectors from C-arrays. */
+#define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
+
+const char *filename = 0; /* "monsterdata_test.mon"; */
+const char *golden_filename = "monsterdata_test.golden";
+const char *target_filename = "monsterdata_test.json.txt";
+
+int test_print(void)
+{
+ int ret = 0;
+ const char *buf = 0;
+ const char *golden = 0;
+ const char *target = 0;
+ size_t size = 0, golden_size = 0, target_size = 0;
+ flatcc_json_printer_t ctx_obj, *ctx;
+ FILE *fp = 0;
+
+ ctx = &ctx_obj;
+
+ fp = fopen(target_filename, "wb");
+ if (!fp) {
+ fprintf(stderr, "%s: could not open output file\n", target_filename);
+ /* ctx not ready for clenaup, so exit directly. */
+ return -1;
+ }
+ flatcc_json_printer_init(ctx, fp);
+ /* Uses same formatting as golden reference file. */
+ flatcc_json_printer_set_nonstrict(ctx);
+
+ if (filename && strcmp(filename, "-")) {
+ buf = readfile(filename, FILE_SIZE_MAX, &size);
+ } else {
+#if FLATBUFFERS_PROTOCOL_IS_LE
+ buf = (const char *)flatcc_golden_le;
+ size = sizeof(flatcc_golden_le);
+#else
+ buf = (const char *)flatcc_golden_be;
+ size = sizeof(flatcc_golden_be);
+#endif
+ }
+
+ if (!buf) {
+ fprintf(stderr, "%s: could not read input flatbuffer file\n", filename);
+ goto fail;
+ }
+ golden = readfile(golden_filename, FILE_SIZE_MAX, &golden_size);
+ if (!golden) {
+ fprintf(stderr, "%s: could not read verification json file\n", golden_filename);
+ goto fail;
+ }
+ ns(Monster_print_json_as_root(ctx, buf, size, "MONS"));
+ flatcc_json_printer_flush(ctx);
+ if (flatcc_json_printer_get_error(ctx)) {
+ printf("could not print monster data\n");
+ }
+ fclose(fp);
+ fp = 0;
+ target = readfile(target_filename, FILE_SIZE_MAX, &target_size);
+ if (!target) {
+ fprintf(stderr, "%s: could not read back output file\n", target_filename);
+ goto fail;
+ }
+ if (target_size != golden_size || memcmp(target, golden, target_size)) {
+ fprintf(stderr, "generated output file did not match verification file\n");
+ goto fail;
+ }
+ fprintf(stderr, "json print test succeeded\n");
+
+done:
+ flatcc_json_printer_clear(ctx);
+ if (!filename) {
+ buf = 0;
+ }
+ if (buf) {
+ free((void *)buf);
+ }
+ if (golden) {
+ free((void *)golden);
+ }
+ if (target) {
+ free((void *)target);
+ }
+ if (fp) {
+ fclose(fp);
+ }
+ return ret;
+fail:
+ ret = -1;
+ goto done;
+}
+
+/* We take arguments so output file can be generated in build directory without copying sources. */
+#define usage \
+"wrong number of arguments:\n" \
+"usage: <program> [(<input-filename>|'-') <reference-filename> <output-filename>]\n" \
+" noargs, or '-' use default binary buffer matching endianness of flatbuffer format\n"
+
+int main(int argc, const char *argv[])
+{
+ fprintf(stderr, "running json print test\n");
+ if (argc != 1 && argc != 4) {
+ fprintf(stderr, usage);
+ exit(1);
+ }
+ if (argc == 4) {
+ filename = argv[1];
+ golden_filename = argv[2];
+ target_filename = argv[3];
+ }
+ return test_print();
+}
diff --git a/flatcc/test/leakcheck-full.sh b/flatcc/test/leakcheck-full.sh
new file mode 100755
index 0000000..db2f452
--- /dev/null
+++ b/flatcc/test/leakcheck-full.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+set -e
+../build.sh
+cd `dirname $0`/..
+mkdir -p build/tmp/leakcheck-full
+valgrind --leak-check=full --show-leak-kinds=all \
+ bin/flatcc_d -a -o build/tmp/leakcheck-full --prefix zzz --common-prefix \
+ hello test/monster_test/monster_test.fbs
diff --git a/flatcc/test/leakcheck.sh b/flatcc/test/leakcheck.sh
new file mode 100755
index 0000000..e77705d
--- /dev/null
+++ b/flatcc/test/leakcheck.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+set -e
+../build.sh
+cd `dirname $0`/..
+mkdir -p build/tmp/leakcheck
+valgrind --leak-check=yes \
+ bin/flatcc_d -a -o build/tmp/leakcheck --prefix zzz --common-prefix hello \
+ test/monster_test/monster_test.fbs
diff --git a/flatcc/test/load_test/CMakeLists.txt b/flatcc/test/load_test/CMakeLists.txt
new file mode 100644
index 0000000..0c146d1
--- /dev/null
+++ b/flatcc/test/load_test/CMakeLists.txt
@@ -0,0 +1,20 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/test/monster_test")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_load_test ALL)
+add_custom_command (
+ TARGET gen_load_test
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND flatcc_cli -a -o "${GEN_DIR}" "${FBS_DIR}/monster_test.fbs"
+ DEPENDS flatcc_cli "${FBS_DIR}/monster_test.fbs" "${FBS_DIR}/include_test1.fbs" "${FBS_DIR}/include_test2.fbs"
+)
+add_executable(load_test load_test.c)
+add_dependencies(load_test gen_load_test)
+target_link_libraries(load_test flatccrt)
+
+add_test(load_test load_test${CMAKE_EXECUTABLE_SUFFIX})
diff --git a/flatcc/test/load_test/load_test.c b/flatcc/test/load_test/load_test.c
new file mode 100644
index 0000000..466f5cc
--- /dev/null
+++ b/flatcc/test/load_test/load_test.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include "monster_test_builder.h"
+#include "flatcc/support/elapsed.h"
+
+#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Example, x)
+#define nsc(x) FLATBUFFERS_WRAP_NAMESPACE(flatbuffers, x)
+#define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
+
+#define MEASURE_DECODE 1
+#define MONSTER_REP 1000
+#define NAME_REP 100
+#define INVENTORY_REP 100
+
+static uint8_t invdata[1000];
+
+static ns(Monster_ref_t) create_monster(flatcc_builder_t *B)
+{
+ size_t i;
+
+ ns(Monster_start(B));
+ ns(Monster_name_start(B));
+ for (i = 0; i < NAME_REP; ++i) {
+ nsc(string_append(B, "Monster", 7));
+ }
+ ns(Monster_name_end(B));
+ ns(Monster_inventory_start(B));
+ for (i = 0; i < INVENTORY_REP; ++i) {
+ nsc(uint8_vec_append(B, invdata, c_vec_len(invdata)));
+ }
+ ns(Monster_inventory_end(B));
+ return ns(Monster_end(B));
+}
+
+static ns(Monster_vec_ref_t) create_monsters(flatcc_builder_t *B)
+{
+ size_t i;
+ ns(Monster_ref_t) m;
+
+ ns(Monster_vec_start(B));
+ for (i = 0; i < MONSTER_REP; ++i) {
+ m = create_monster(B);
+ assert(m);
+ ns(Monster_vec_push(B, m));
+ }
+ return ns(Monster_vec_end(B));
+}
+
+static int create_root_monster(flatcc_builder_t *B)
+{
+ ns(Monster_vec_ref_t) mv;
+
+ flatcc_builder_reset(B);
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "root_monster"));
+ mv = create_monsters(B);
+ assert(mv);
+ ns(Monster_testarrayoftables_add(B, mv));
+ ns(Monster_end_as_root(B));
+ return 0;
+}
+
+#if MEASURE_DECODE
+static int verify_monster(const char *base, ns(Monster_table_t) mon)
+{
+ size_t i;
+ nsc(string_t) s = ns(Monster_name(mon));
+ /*
+ * This only works because it is a byte, otherwise
+ * vec_at should be used to convert endian format.
+ */
+ const uint8_t *inv = ns(Monster_inventory(mon));
+
+ if (nsc(string_len(s)) != NAME_REP * 7) {
+ assert(0);
+ return -1;
+ }
+ if (nsc(uint8_vec_len(inv)) != INVENTORY_REP * c_vec_len(invdata)) {
+ assert(0);
+ return -1;
+ }
+ for (i = 0; i < NAME_REP; ++i) {
+ if (memcmp(s + i * 7, "Monster", 7)) {
+ printf("failed monster name at %lu: %s\n", (unsigned long)i, s ? s : "NULL");
+ printf("offset: %ld\n", (long)(s + i * 7 - base));
+ assert(0);
+ return -1;
+ }
+ }
+ for (i = 0; i < INVENTORY_REP; ++i) {
+ if (memcmp(inv + i * c_vec_len(invdata), invdata, c_vec_len(invdata))) {
+ assert(0);
+ return -1;
+ }
+ }
+ return 0;
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+ FILE *fp;
+ void *buffer;
+ size_t size;
+ flatcc_builder_t builder, *B;
+ ns(Monster_table_t) mon;
+ ns(Monster_vec_t) mv;
+ double t1, t2;
+ int rep = 10, i;
+ int ret = 0;
+
+#if MEASURE_DECODE
+ size_t j;
+#endif
+
+ (void)argc;
+ (void)argv;
+
+ B = &builder;
+ flatcc_builder_init(B);
+ create_root_monster(B);
+ buffer = flatcc_builder_finalize_buffer(B, &size);
+ fp = fopen("monster_load_test.dat", "wb");
+ if (!fp) {
+ ret = -1;
+ goto done;
+ }
+ ret |= size != fwrite(buffer, 1, size, fp);
+ fclose(fp);
+ if (ret) {
+ goto done;
+ }
+ printf("buffer size: %lu\n", (unsigned long)size);
+ printf("start timing ...\n");
+ t1 = elapsed_realtime();
+ for (i = 0; i < rep; ++i) {
+ create_root_monster(B);
+ flatcc_builder_copy_buffer(B, buffer, size);
+ mon = ns(Monster_as_root(buffer));
+ ret |= strcmp(ns(Monster_name(mon)), "root_monster");
+ assert(ret == 0);
+ mv = ns(Monster_testarrayoftables(mon));
+ /* Negated logic, 0 is OK. */
+ ret |= ns(Monster_vec_len(mv)) != MONSTER_REP;
+ assert(ret == 0);
+#if MEASURE_DECODE
+ for (j = 0; j < MONSTER_REP; ++j) {
+ ret |= verify_monster(buffer, ns(Monster_vec_at(mv, j)));
+ assert(ret == 0);
+ }
+#endif
+ if (ret) {
+ goto done;
+ }
+ }
+ t2 = elapsed_realtime();
+ show_benchmark("encode and partially decode large buffer", t1, t2, size, rep, 0);
+done:
+ flatcc_builder_clear(B);
+ free(buffer);
+ if (ret) {
+ printf("load test failed\n");
+ }
+ return ret;
+}
diff --git a/flatcc/test/load_test/load_test.sh b/flatcc/test/load_test/load_test.sh
new file mode 100755
index 0000000..d94fbb0
--- /dev/null
+++ b/flatcc/test/load_test/load_test.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../..
+ROOT=`pwd`
+TMP=build/tmp/test/load_test
+
+${ROOT}/scripts/build.sh
+mkdir -p ${TMP}
+rm -rf ${TMP}/*
+bin/flatcc -a -o ${TMP} test/monster_test/monster_test.fbs
+
+cp test/load_test/*.c ${TMP}
+cd ${TMP}
+cc -g -I ${ROOT}/include load_test.c \
+ ${ROOT}/lib/libflatccrt.a -o load_test_d
+cc -O3 -DNDEBUG -I ${ROOT}/include load_test.c \
+ ${ROOT}/lib/libflatccrt.a -o load_test
+echo "running load test debug"
+./load_test_d
+echo "running load test optimized"
+./load_test
diff --git a/flatcc/test/monster_test/CMakeLists.txt b/flatcc/test/monster_test/CMakeLists.txt
new file mode 100644
index 0000000..5860a37
--- /dev/null
+++ b/flatcc/test/monster_test/CMakeLists.txt
@@ -0,0 +1,20 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/test/monster_test")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_monster_test ALL)
+add_custom_command (
+ TARGET gen_monster_test
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND flatcc_cli -a -o "${GEN_DIR}" "${FBS_DIR}/monster_test.fbs"
+ DEPENDS flatcc_cli "${FBS_DIR}/monster_test.fbs" "${FBS_DIR}/include_test1.fbs" "${FBS_DIR}/include_test2.fbs"
+)
+add_executable(monster_test monster_test.c)
+add_dependencies(monster_test gen_monster_test)
+target_link_libraries(monster_test flatccrt)
+
+add_test(monster_test monster_test${CMAKE_EXECUTABLE_SUFFIX})
diff --git a/flatcc/test/monster_test/attributes.fbs b/flatcc/test/monster_test/attributes.fbs
new file mode 100644
index 0000000..bbf5c43
--- /dev/null
+++ b/flatcc/test/monster_test/attributes.fbs
@@ -0,0 +1,6 @@
+// Attributes not supported by flatcc
+attribute "flexbuffer";
+attribute "csharp_partial";
+attribute "cpp_type";
+attribute "streaming";
+attribute "idempotent";
diff --git a/flatcc/test/monster_test/include_test1.fbs b/flatcc/test/monster_test/include_test1.fbs
new file mode 100644
index 0000000..11aebe8
--- /dev/null
+++ b/flatcc/test/monster_test/include_test1.fbs
@@ -0,0 +1,5 @@
+include "include_test2.fbs";
+include "include_test2.fbs"; // should be skipped
+include "include_test1.fbs"; // should be skipped
+
+
diff --git a/flatcc/test/monster_test/include_test2.fbs b/flatcc/test/monster_test/include_test2.fbs
new file mode 100644
index 0000000..ff23e93
--- /dev/null
+++ b/flatcc/test/monster_test/include_test2.fbs
@@ -0,0 +1,11 @@
+include "include_test2.fbs"; // should be skipped
+
+attribute "included_attribute";
+
+namespace MyGame.OtherNameSpace;
+
+enum FromInclude:long { IncludeVal, Foo = 17 }
+
+struct Unused { unused: byte; }
+
+
diff --git a/flatcc/test/monster_test/monster_test.c b/flatcc/test/monster_test/monster_test.c
new file mode 100644
index 0000000..f3150d8
--- /dev/null
+++ b/flatcc/test/monster_test/monster_test.c
@@ -0,0 +1,2919 @@
+#include <stdio.h>
+
+#include "monster_test_builder.h"
+#include "monster_test_verifier.h"
+
+#include "flatcc/support/hexdump.h"
+#include "flatcc/support/elapsed.h"
+#include "flatcc/portable/pparsefp.h"
+#include "../../config/config.h"
+
+/*
+ * Convenience macro to deal with long namespace names,
+ * and to make code reusable with other namespaces.
+ *
+ * Note: we could also use
+ *
+ * #define ns(x) MyGame_Example_ ## x
+ *
+ * but it wouldn't doesn't handled nested ns calls.
+ *
+ * For historic reason some of this test does not use the ns macro
+ * and some avoid nesting ns calls by placing parenthesis differently
+ * although this isn't required with this wrapper macro.
+ */
+#undef ns
+#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Example, x)
+
+#undef nsf
+#define nsf(x) FLATBUFFERS_WRAP_NAMESPACE(Fantasy, x)
+
+/*
+ * Wrap the common namespace (flatbuffers_). Many operations in the
+ * common namespace such as `flatbuffers_string_create` are also mapped
+ * to member fields such as `MyGame_Example_Monster_name_create` and
+ * this macro provides a consistent interface to namespaces with
+ * `nsc(string_create)` similar to `ns(Monster_name_create)`.
+ */
+#undef nsc
+#define nsc(x) FLATBUFFERS_WRAP_NAMESPACE(flatbuffers, x)
+
+/* A helper to simplify creating buffers vectors from C-arrays. */
+#define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
+
+static const char zero_pad[100];
+
+int verify_empty_monster(void *buffer)
+{
+ /* Proper id given. */
+ ns(Monster_table_t) monster = ns(Monster_as_root_with_identifier)(buffer, ns(Monster_file_identifier));
+ /* Invalid id. */
+ ns(Monster_table_t) monster2 = ns(Monster_as_root_with_identifier(buffer, "1234"));
+ /* `with_id` can also mean ignore id when given a null argument. */
+ ns(Monster_table_t) monster3 = ns(Monster_as_root_with_identifier(buffer, 0));
+ /* Excessive text in identifier is ignored. */
+ ns(Monster_table_t) monster4 = ns(Monster_as_root_with_identifier(buffer, "MONSX"));
+ /* Default id should match proper id. */
+ ns(Monster_table_t) monster5 = ns(Monster_as_root(buffer));
+
+ if (!monster) {
+ printf("Monster not available\n");
+ return -1;
+ }
+ if (monster2) {
+ printf("Monster should not accept invalid identifier\n");
+ return -1;
+ }
+ if (monster3 != monster) {
+ printf("Monster should ignore identifier when given a null id\n");
+ return -1;
+ }
+ if (monster4 != monster) {
+ printf("Monster should accept a string as valid identifier");
+ return -1;
+ }
+ if (monster5 != monster) {
+ printf("Monster with default id should be accepted");
+ return -1;
+ }
+ if (ns(Monster_hp(monster)) != 100) {
+ printf("Health points are not as expected\n");
+ return -1;
+ }
+ if (ns(Monster_hp_is_present(monster))) {
+ printf("Health Points should default\n");
+ return -1;
+ }
+ if (ns(Monster_pos_is_present(monster))) {
+ printf("Position should be present\n");
+ return -1;
+ }
+ if (ns(Monster_pos(monster)) != 0) {
+ printf("Position shouldn't be available\n");
+ return -1;
+ }
+ return 0;
+}
+
+int test_enums(flatcc_builder_t *B)
+{
+ (void)B;
+
+ if (ns(neg_enum_neg1) != -12) {
+ printf("neg_enum_neg1 should be -12, was %d\n", ns(neg_enum_neg1));
+ return -1;
+ }
+ if (ns(neg_enum_neg2) != -11) {
+ printf("neg_enum_neg1 should be -11, was %d\n", ns(neg_enum_neg2));
+ return -1;
+ }
+ if (ns(int_enum_int1) != 2) {
+ printf("int_enum_int1 should be 2\n");
+ return -1;
+ }
+ if (ns(int_enum_int2) != 42) {
+ printf("int_enum_int2 should be 42\n");
+ return -1;
+ }
+ if (ns(hex_enum_hexneg) != -2) {
+ printf("enum hexneg should be -2\n");
+ return -1;
+ }
+ if (ns(hex_enum_hex1) != 3) {
+ printf("hex_enum_hex1 should be 3\n");
+ return -1;
+ }
+ if (ns(hex_enum_hex2) != INT32_C(0x7eafbeaf)) {
+ printf("hex_enum_hex2 should be 0x7eafbeaf\n");
+ return -1;
+ }
+ return 0;
+}
+
+int test_type_aliases(flatcc_builder_t *B)
+{
+ int ret = 0;
+ void *buffer = 0;
+ size_t size;
+ ns(TypeAliases_table_t) ta;
+ flatbuffers_uint8_vec_ref_t v8_ref;
+ flatbuffers_double_vec_ref_t vf64_ref;
+
+ flatcc_builder_reset(B);
+
+ v8_ref = flatbuffers_uint8_vec_create(B, 0, 0);
+ vf64_ref = flatbuffers_double_vec_create(B, 0, 0);
+ ns(TypeAliases_create_as_root(B,
+ INT8_MIN, UINT8_MAX, INT16_MIN, UINT16_MAX,
+ INT32_MIN, UINT32_MAX, INT64_MIN, UINT64_MAX, 2.3f, 2.3, v8_ref, vf64_ref));
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+ if ((ret = ns(TypeAliases_verify_as_root(buffer, size)))) {
+
+ hexdump("TypeAliases buffer", buffer, size, stderr);
+ printf("could not verify TypeAliases table, got %s\n", flatcc_verify_error_string(ret));
+ goto done;
+ }
+ ta = ns(TypeAliases_as_root(buffer));
+
+ if (ns(TypeAliases_i8(ta)) != INT8_MIN) goto failed;
+ if (ns(TypeAliases_i16(ta)) != INT16_MIN) goto failed;
+ if (ns(TypeAliases_i32(ta)) != INT32_MIN) goto failed;
+ if (ns(TypeAliases_i64(ta)) != INT64_MIN) goto failed;
+ if (ns(TypeAliases_u8(ta)) != UINT8_MAX) goto failed;
+ if (ns(TypeAliases_u16(ta)) != UINT16_MAX) goto failed;
+ if (ns(TypeAliases_u32(ta)) != UINT32_MAX) goto failed;
+ if (ns(TypeAliases_u64(ta)) != UINT64_MAX) goto failed;
+ if (!parse_float_is_equal(ns(TypeAliases_f32(ta)), 2.3f)) goto failed;
+ if (!parse_double_is_equal(ns(TypeAliases_f64(ta)), 2.3)) goto failed;
+ if (sizeof(ns(TypeAliases_i8(ta))) != 1) goto failed;
+ if (sizeof(ns(TypeAliases_i16(ta))) != 2) goto failed;
+ if (sizeof(ns(TypeAliases_i32(ta))) != 4) goto failed;
+ if (sizeof(ns(TypeAliases_i64(ta))) != 8) goto failed;
+ if (sizeof(ns(TypeAliases_u8(ta))) != 1) goto failed;
+ if (sizeof(ns(TypeAliases_u16(ta))) != 2) goto failed;
+ if (sizeof(ns(TypeAliases_u32(ta))) != 4) goto failed;
+ if (sizeof(ns(TypeAliases_u64(ta))) != 8) goto failed;
+ if (sizeof(ns(TypeAliases_f32(ta))) != 4) goto failed;
+ if (sizeof(ns(TypeAliases_f64(ta))) != 8) goto failed;
+
+done:
+ flatcc_builder_aligned_free(buffer);
+ return ret;
+
+failed:
+ ret = -1;
+ printf("Scalar type alias has unexpected value or size\n");
+ goto done;
+}
+
+int test_empty_monster(flatcc_builder_t *B)
+{
+ int ret;
+ ns(Monster_ref_t) root;
+ void *buffer;
+ size_t size;
+
+ flatcc_builder_reset(B);
+
+ flatbuffers_buffer_start(B, ns(Monster_file_identifier));
+ ns(Monster_start(B));
+ /* Cannot make monster empty as name is required. */
+ ns(Monster_name_create_str(B, "MyMonster"));
+ root = ns(Monster_end(B));
+ flatbuffers_buffer_end(B, root);
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+
+ hexdump("empty monster table", buffer, size, stderr);
+ if ((ret = verify_empty_monster(buffer))) {
+ goto done;
+ }
+
+ if ((ret = ns(Monster_verify_as_root_with_identifier(buffer, size, ns(Monster_file_identifier))))) {
+ printf("could not verify empty monster, got %s\n", flatcc_verify_error_string(ret));
+ return -1;
+ }
+
+ /*
+ * Note: this will assert if the verifier is set to assert during
+ * debugging. Also not that a buffer size - 1 is not necessarily
+ * invalid, but because we pack vtables tight at the end, we expect
+ * failure in this case.
+ */
+ if (flatcc_verify_ok == ns(Monster_verify_as_root(
+ buffer, size - 1))) {
+ printf("Monster verify failed to detect short buffer\n");
+ return -1;
+ }
+
+done:
+ flatcc_builder_aligned_free(buffer);
+ return ret;
+}
+
+int test_typed_empty_monster(flatcc_builder_t *B)
+{
+ int ret = -1;
+ ns(Monster_ref_t) root;
+ void *buffer;
+ size_t size;
+ flatbuffers_fid_t fid = { 0 };
+
+ flatcc_builder_reset(B);
+
+ flatbuffers_buffer_start(B, ns(Monster_type_identifier));
+ ns(Monster_start(B));
+ /* Cannot make monster empty as name is required. */
+ ns(Monster_name_create_str(B, "MyMonster"));
+ root = ns(Monster_end(B));
+ flatbuffers_buffer_end(B, root);
+
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+
+ hexdump("empty typed monster table", buffer, size, stderr);
+
+ if (flatbuffers_get_type_hash(buffer) != flatbuffers_type_hash_from_name("MyGame.Example.Monster")) {
+
+ printf("Monster does not have the expected type, got %lx\n", (unsigned long)flatbuffers_get_type_hash(buffer));
+ goto done;
+ }
+
+ if (!flatbuffers_has_type_hash(buffer, ns(Monster_type_hash))) {
+ printf("Monster does not have the expected type\n");
+ goto done;
+ }
+ if (!flatbuffers_has_type_hash(buffer, 0x330ef481)) {
+ printf("Monster does not have the expected type\n");
+ goto done;
+ }
+
+ if (!verify_empty_monster(buffer)) {
+ printf("typed empty monster should not verify with default identifier\n");
+ goto done;
+ }
+
+ if ((ret = ns(Monster_verify_as_root_with_identifier(buffer, size, ns(Monster_type_identifier))))) {
+ printf("could not verify typed empty monster, got %s\n", flatcc_verify_error_string(ret));
+ goto done;
+ }
+
+ if ((ret = ns(Monster_verify_as_typed_root(buffer, size)))) {
+ printf("could not verify typed empty monster, got %s\n", flatcc_verify_error_string(ret));
+ goto done;
+ }
+
+ if ((ret = ns(Monster_verify_as_root_with_type_hash(buffer, size, ns(Monster_type_hash))))) {
+ printf("could not verify empty monster with type hash, got %s\n", flatcc_verify_error_string(ret));
+ goto done;
+ }
+
+ if ((ret = ns(Monster_verify_as_root_with_type_hash(buffer, size, flatbuffers_type_hash_from_name("MyGame.Example.Monster"))))) {
+ printf("could not verify empty monster with explicit type hash, got %s\n", flatcc_verify_error_string(ret));
+ goto done;
+ }
+
+ flatbuffers_identifier_from_type_hash(0x330ef481, fid);
+ if ((ret = ns(Monster_verify_as_root_with_identifier(buffer, size, fid)))) {
+ printf("could not verify typed empty monster, got %s\n", flatcc_verify_error_string(ret));
+ goto done;
+ }
+
+ if (!ns(Monster_verify_as_root(buffer, size))) {
+ printf("should not have verified with the original identifier since we use types\n");
+ goto done;
+ }
+ ret = 0;
+
+done:
+ flatcc_builder_aligned_free(buffer);
+ return ret;
+}
+
+int verify_monster(void *buffer)
+{
+ ns(Monster_table_t) monster, mon, mon2;
+ ns(Monster_vec_t) monsters;
+ ns(Any_union_type_t) test_type;
+ ns(Any_union_t) test_union;
+ /* This is an encoded struct pointer. */
+ ns(Vec3_struct_t) vec;
+ const char *name;
+ /* This is a more precise type as there is a length field prefix. */
+ nsc(string_t) name2;
+ /* This is a native struct type. */
+ ns(Vec3_t) v;
+ ns(Test_vec_t) testvec;
+ ns(Test_t) testvec_data[] = {
+ {0x10, 0x20}, {0x30, 0x40}, {0x50, 0x60}, {0x70, (int8_t)0x80}, {0x191, (int8_t)0x91}
+ };
+ ns(Test_struct_t) test;
+ nsc(string_vec_t) strings;
+ nsc(string_t) s;
+ nsc(bool_vec_t) bools;
+ ns(Stat_table_t) stat;
+ int booldata[] = { 0, 1, 1, 0 };
+ const uint8_t *inv;
+ size_t i;
+
+ if (!nsc(has_identifier(buffer, 0))) {
+ printf("wrong monster identifier (when ignoring)\n");
+ return -1;
+ }
+ if (!nsc(has_identifier(buffer, "MONS"))) {
+ printf("wrong monster identifier (when explicit)\n");
+ return -1;
+ }
+ if (!nsc(has_identifier(buffer, "MONSTER"))) {
+ printf("extra characters in identifier should be ignored\n");
+ return -1;
+ }
+ if (nsc(has_identifier(buffer, "MON1"))) {
+ printf("accepted wrong monster identifier (when explicit)\n");
+ return -1;
+ }
+ if (!nsc(has_identifier(buffer, ns(Monster_file_identifier)))) {
+ printf("wrong monster identifier (via defined identifier)\n");
+ return -1;
+ }
+
+ if (!(monster = ns(Monster_as_root(buffer)))) {
+ printf("Monster not available\n");
+ return -1;
+ }
+ if (ns(Monster_hp(monster)) != 80) {
+ printf("Health points are not as expected\n");
+ return -1;
+ }
+ if (!(vec = ns(Monster_pos(monster)))) {
+ printf("Position is absent\n");
+ return -1;
+ }
+ if ((size_t)vec & 15) {
+ printf("Force align of Vec3 struct not correct\n");
+ }
+ /* -3.2f is actually -3.20000005 and not -3.2 due to representation loss.
+ * For 32-bit GCC compilers, -3.2f might be another value, so use lower
+ * precision portable comparison. */
+ if (!parse_float_is_equal(ns(Vec3_z(vec)), -3.2f)) {
+ printf("Position failing on z coordinate\n");
+ return -1;
+ }
+ if (nsc(is_native_pe())) {
+ if (!parse_float_is_equal(vec->x, 1.0f) ||
+ !parse_float_is_equal(vec->y, 2.0f) ||
+ !parse_float_is_equal(vec->z, -3.2f)) {
+ printf("Position is incorrect\n");
+ return -1;
+ }
+ }
+ /*
+ * NOTE: copy_from_pe and friends are provided in the builder
+ * interface, not the read only interface, but for advanced uses
+ * these may also be used for read operations.
+ * Also note that if we want the target struct fully null padded
+ * the struct must be zeroed first. The _clear operation is one way
+ * to achieve this - but it is not required for normal read access.
+ * See common_builder for more details. These operations can
+ * actually be very useful in their own right, disregarding any
+ * other flatbuffer logic when dealing with struct endian
+ * conversions in other protocols.
+ */
+ ns(Vec3_clear(&v)); /* Not strictly needed here. */
+ ns(Vec3_copy_from_pe(&v, vec));
+ if (!parse_float_is_equal(v.x, 1.0f) ||
+ !parse_float_is_equal(v.y, 2.0f) ||
+ !parse_float_is_equal(v.z, -3.2f)) {
+ printf("Position is incorrect after copy\n");
+ return -1;
+ }
+ if (vec->test1 != 0 || vec->test1 != 0 ||
+ memcmp(&vec->test3, zero_pad, sizeof(vec->test3)) != 0) {
+ printf("Zero default not correct for struct\n");
+ return -1;
+ }
+ name = ns(Monster_name(monster));
+ if (!name || strcmp(name, "MyMonster")) {
+ printf("Name is not correct\n");
+ return -1;
+ }
+ name2 = ns(Monster_name(monster));
+ if (nsc(string_len(name)) != 9 || nsc(string_len(name2)) != 9) {
+ printf("Name length is not correct\n");
+ return -1;
+ }
+ if (ns(Monster_color(monster)) != ns(Color_Green)) {
+ printf("Monster isn't a green monster\n");
+ return -1;
+ }
+ if (strcmp(ns(Color_name)(ns(Color_Green)), "Green")) {
+ printf("Enum name map does not have a green solution\n");
+ return -1;
+ }
+ /*
+ * This is bit tricky because Color is a bit flag, so we can have
+ * combinations that are expected, but that we do not know. The
+ * known value logic does not accomodate for that.
+ */
+ if (!ns(Color_is_known_value(ns(Color_Green)))) {
+ printf("Color enum does not recognize the value of the Green flag\n");
+ return -1;
+ }
+ if (!ns(Color_is_known_value(1))) {
+ printf("Color enum does not recognize the value of the Red flag\n");
+ return -1;
+ }
+ if (ns(Color_is_known_value(4))) {
+ printf("Color enum recognizes a value it shouldn't\n");
+ return -1;
+ }
+ if (!ns(Color_is_known_value(8))) {
+ printf("Color enum does not recognize the value of the Blue flag\n");
+ return -1;
+ }
+ if (ns(Color_is_known_value(9))) {
+ printf("Color enum recognizes a value it shouldn't\n");
+ return -1;
+ }
+ if (!ns(Any_is_known_type(ns(Any_Monster)))) {
+ printf("Any type does not accept Monster\n");
+ return -1;
+ }
+ if (ns(Any_is_known_type(42))) {
+ printf("Any type recognizes unexpected type\n");
+ return -1;
+ }
+ inv = ns(Monster_inventory(monster));
+ if ((nsc(uint8_vec_len(inv))) != 10) {
+ printf("Inventory length unexpected\n");
+ return -1;
+ }
+ for (i = 0; i < nsc(uint8_vec_len(inv)); ++i) {
+ if (nsc(uint8_vec_at(inv, i)) != i) {
+ printf("inventory item #%d is wrong\n", (int)i);
+ return -1;
+ }
+ }
+ if (ns(Monster_mana(monster) != 150)) {
+ printf("Mana not default\n");
+ return -1;
+ }
+ if (ns(Monster_mana_is_present(monster))) {
+ printf("Mana should default\n");
+ return -1;
+ }
+ if (!ns(Monster_hp_is_present(monster))) {
+ printf("Health points should be present\n");
+ return -1;
+ }
+ if (!ns(Monster_pos_is_present(monster))) {
+ printf("Position should be present\n");
+ return -1;
+ }
+ testvec = ns(Monster_test4(monster));
+ if (ns(Test_vec_len(testvec)) != 5) {
+ printf("Test4 vector is not the right length.\n");
+ return -1;
+ }
+ /*
+ * This particular test requires that the in-memory
+ * array layout matches the array layout in the buffer.
+ */
+ if (flatbuffers_is_native_pe()) {
+ for (i = 0; i < 5; ++i) {
+ test = ns(Test_vec_at(testvec, i));
+ if (testvec_data[i].a != ns(Test_a(test))) {
+ printf("Test4 vec failed at index %d, member a\n", (int)i);
+ return -1;
+ }
+ if (testvec_data[i].b != ns(Test_b(test))) {
+ printf("Test4 vec failed at index %d, member a\n", (int)i);
+ return -1;
+ }
+ }
+ } else {
+ printf("SKIPPING DIRECT VECTOR ACCESS WITH NON-NATIVE ENDIAN PROTOCOL\n");
+ }
+ monsters = ns(Monster_testarrayoftables(monster));
+ if (ns(Monster_vec_len(monsters)) != 8) {
+ printf("unexpected monster vector length\n");
+ return -1;
+ }
+ mon = ns(Monster_vec_at(monsters, 5));
+ assert(mon);
+ name = ns(Monster_name(mon));
+ if (strcmp(name, "TwoFace")) {
+ printf("monster 5 isn't TwoFace");
+ return -1;
+ }
+ mon2 = ns(Monster_vec_at(monsters, 1));
+ if (mon2 != mon) {
+ printf("DAG test failed, monster[5] != monster[1] as pointer\n");
+ return -1;
+ }
+ name = ns(Monster_name(mon2));
+ if (strcmp(name, "TwoFace")) {
+ printf("monster 1 isn't Joker, it is: %s\n", name);
+ return -1;
+ }
+ mon = ns(Monster_vec_at(monsters, 2));
+ name = ns(Monster_name(mon));
+ if (strcmp(name, "Joker")) {
+ printf("monster 2 isn't Joker, it is: %s\n", name);
+ return -1;
+ }
+ mon = ns(Monster_vec_at(monsters, 0));
+ name = ns(Monster_name(mon));
+ if (strcmp(name, "Gulliver")) {
+ printf("monster 0 isn't Gulliver, it is: %s\n", name);
+ return -1;
+ }
+ mon = ns(Monster_vec_at(monsters, 3));
+ name = ns(Monster_name(mon));
+ if (strcmp(name, "TwoFace")) {
+ printf("monster 3 isn't TwoFace, it is: %s\n", name);
+ return -1;
+ }
+ mon = ns(Monster_vec_at(monsters, 4));
+ name = ns(Monster_name(mon));
+ if (strcmp(name, "Joker")) {
+ printf("monster 4 isn't Joker, it is: %s\n", name);
+ return -1;
+ }
+ mon = ns(Monster_vec_at(monsters, 6));
+ name = ns(Monster_name(mon));
+ if (strcmp(name, "Gulliver")) {
+ printf("monster 6 isn't Gulliver, it is: %s\n", name);
+ return -1;
+ }
+ mon = ns(Monster_vec_at(monsters, 7));
+ name = ns(Monster_name(mon));
+ if (strcmp(name, "Joker")) {
+ printf("monster 7 isn't Gulliver, it is: %s\n", name);
+ return -1;
+ }
+ strings = ns(Monster_testarrayofstring(monster));
+ if (nsc(string_vec_len(strings) != 3)) {
+ printf("Monster array of strings has wrong length\n");
+ return -1;
+ }
+ if (strcmp(nsc(string_vec_at(strings, 0)), "Hello")) {
+ printf("string elem 0 is wrong\n");
+ return -1;
+ }
+ s = nsc(string_vec_at(strings, 1));
+ if (nsc(string_len(s)) != 2) {
+ printf("string 1 has wrong length");
+ return -1;
+ }
+ if (memcmp(s, ",\0", 2)) {
+ printf("string elem 1 has wrong content\n");
+ return -1;
+ }
+ if (strcmp(nsc(string_vec_at(strings, 2)), "world!")) {
+ printf("string elem 2 is wrong\n");
+ return -1;
+ }
+ if (!ns(Monster_testarrayofbools_is_present(monster))) {
+ printf("array of bools is missing\n");
+ return -1;
+ }
+ bools = ns(Monster_testarrayofbools(monster));
+ if (nsc(bool_vec_len(bools) != 4)) {
+ printf("bools have wrong vector length\n");
+ return -1;
+ }
+ if (sizeof(bools[0]) != 1) {
+ printf("bools have wrong element size\n");
+ return -1;
+ }
+ for (i = 0; i < 4; ++i) {
+ if (nsc(bool_vec_at(bools, i) != booldata[i])) {
+ printf("bools vector elem %d is wrong\n", (int)i);
+ return -1;
+ }
+ }
+ test_type = ns(Monster_test_type(monster));
+ if (test_type != ns(Any_Monster)) {
+ printf("the monster test type is not Any_Monster\n");
+ return -1;
+ }
+ mon = ns(Monster_test(monster));
+ if (strcmp(ns(Monster_name(mon)), "TwoFace")) {
+ printf("the test monster is not TwoFace\n");
+ return -1;
+ }
+ mon = ns(Monster_enemy(monster));
+ if (strcmp(ns(Monster_name(mon)), "the enemy")) {
+ printf("the monster is not the enemy\n");
+ return -1;
+ }
+ if (ns(Monster_test_type(mon)) != ns(Any_NONE)) {
+ printf("the enemy test type is not Any_NONE\n");
+ return -1;
+ }
+ test_union = ns(Monster_test_union(monster));
+ if (test_union.type != test_type) {
+ printf("the monster test union type is not Any_Monster\n");
+ return -1;
+ }
+ if (test_union.value != ns(Monster_test(monster))) {
+ printf("the union monster has gone awol\n");
+ return -1;
+ }
+ monsters = ns(Monster_testarrayoftables(mon));
+ i = ns(Monster_vec_len(monsters));
+ mon = ns(Monster_vec_at(monsters, i - 1));
+ if (ns(Monster_test_type)(mon) != ns(Any_Monster)) {
+ printf("The monster variant added with value, type methods is not working\n");
+ return -1;
+ }
+ mon = ns(Monster_test(mon));
+ if (strcmp(ns(Monster_name(mon)), "TwoFace")) {
+ printf("The monster variant added with value method is incorrect\n");
+ return -1;
+ }
+ if (ns(Monster_testbool(monster))) {
+ printf("testbool should not\n");
+ return -1;
+ }
+ if (!ns(Monster_testempty_is_present(monster))) {
+ printf("The empty table isn't present\n");
+ return -1;
+ }
+ stat = ns(Monster_testempty(monster));
+ if (ns(Stat_id_is_present(stat))
+ || ns(Stat_val_is_present(stat))
+ || ns(Stat_count_is_present(stat))) {
+ printf("empty table isn't empty\n");
+ return -1;
+ }
+ return 0;
+}
+
+int gen_monster(flatcc_builder_t *B, int with_size)
+{
+ uint8_t inv[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ ns(Vec3_t) *vec;
+ ns(Test_t) *test, x;
+ ns(Monster_ref_t) mon, mon2, monsters[2];
+ ns(Monster_ref_t) *aoft;
+ nsc(string_ref_t) name;
+ nsc(string_ref_t) strings[3];
+ nsc(bool_t)bools[] = { 0, 1, 1, 0 };
+ flatcc_builder_reset(B);
+
+
+
+ /*
+ * Some FlatBuffer language interfaces require a string and other
+ * non-embeddable objects to be created before the table storing it
+ * is being created. This is not necessary (but possible) here
+ * because the flatcc_builder maintains an internal stack.
+ */
+ if (with_size) {
+ ns(Monster_start_as_root_with_size(B));
+ } else {
+ ns(Monster_start_as_root(B));
+ }
+
+ ns(Monster_hp_add(B, 80));
+ vec = ns(Monster_pos_start(B));
+ vec->x = 1, vec->y = 2, vec->z = -3.2f;
+ /* _end call converts to protocol endian format. */
+ ns(Monster_pos_end(B));
+ /*
+ * NOTE: Monster_name_add requires a reference to an
+ * already created string - adding a string directly
+ * will compile with a warning but fail badly. Instead
+ * create the string first, or do it in-place with
+ * the helper function `Monster_name_create_str`, or
+ * with one of several other options.
+ *
+ * Wrong: ns(Monster_name_add(B, "MyMonster"));
+ */
+ ns(Monster_name_create_str(B, "MyMonster"));
+
+ ns(Monster_color_add)(B, ns(Color_Green));
+
+ ns(Monster_inventory_create(B, inv, c_vec_len(inv)));
+
+ /* The vector is built in native endian format. */
+ ns(Monster_test4_start(B));
+ test = ns(Monster_test4_extend(B, 1));
+ test->a = 0x10;
+ test->b = 0x20;
+ test = ns(Monster_test4_extend(B, 2));
+ test->a = 0x30;
+ test->b = 0x40;
+ test[1].a = 0x50;
+ test[1].b = 0x60;
+ ns(Monster_test4_push_create(B, 0x70, (int8_t)0x80));
+ /*
+ * Zero padding within struct
+ * - not needed when receiving a pointer like `test` in the above.
+ */
+ ns(Test_clear(&x));
+ x.a = 0x190; /* This is a short. */
+ x.b = (int8_t)0x91; /* This is a byte. */
+ /* And x also has a hidden trailing padding byte. */
+ ns(Monster_test4_push(B, &x));
+ ns(Monster_test4_push(B, &x));
+ /* We can use either field mapped push or push on the type. */
+ ns(Test_vec_push(B, &x));
+ /*
+ * `_reserved_len` is similar to the `_vec_len` function in the
+ * reader interface but `_vec_len` would not work here.
+ */
+ assert(ns(Monster_test4_reserved_len(B)) == 7);
+ ns(Monster_test4_truncate(B, 2));
+ assert(ns(Monster_test4_reserved_len(B)) == 5);
+
+ /* It is not valid to dereference old pointers unless we call edit first. */
+ test = ns(Monster_test4_edit(B));
+ test[4].a += 1; /* 0x191 */
+
+ /* Each vector element is converted to protocol endian format at end. */
+ ns(Monster_test4_end(B));
+
+ /* Test creating object before containing vector. */
+ ns(Monster_start(B));
+ name = nsc(string_create(B, "TwoFace", 7));
+ ns(Monster_name_add(B, name));
+ mon = ns(Monster_end(B));
+ /*
+ * Here we create several monsters with only a name - this also
+ * tests reuse of vtables.
+ */
+ ns(Monster_testarrayoftables_start(B));
+ aoft = ns(Monster_testarrayoftables_extend(B, 2));
+ /*
+ * It is usually not ideal to update reference vectors directly and
+ * there must not be any unassigned elements (null) when the array
+ * ends. Normally a push_start ... push_end, or a push_create
+ * operation is preferable.
+ */
+ aoft[0] = mon;
+ /*
+ * But we can do things not otherwise possible - like constructing a
+ * DAG. Note that reference values (unlike pointers) are stable as
+ * long as the buffer is open for write, also past this vector.
+ */
+ aoft[1] = mon;
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_strn(B, "Joker", 30));
+ mon2 = *ns(Monster_testarrayoftables_push_end(B));
+ aoft = ns(Monster_testarrayoftables_extend(B, 3));
+ aoft[0] = mon;
+ aoft[1] = mon2;
+ ns(Monster_testarrayoftables_truncate(B, 1));
+ assert(ns(Monster_testarrayoftables_reserved_len(B)) == 5);
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_strn(B, "Gulliver at the Big Endians", 8));
+ /* We cannot call reserved_len while a monster is still open, */
+ monsters[0] = *ns(Monster_testarrayoftables_push_end(B));
+ /* but here the vector is on top of the stack again. */
+ assert(ns(Monster_testarrayoftables_reserved_len(B)) == 6);
+ /* Swap monsters[0] and monsters[5] */
+ aoft = ns(Monster_testarrayoftables_edit(B));
+ mon2 = aoft[5];
+ monsters[1] = aoft[2];
+ aoft[5] = mon;
+ aoft[0] = mon2;
+ ns(Monster_testarrayoftables_append(B, monsters, 2));
+ /*
+ * The end call converts the reference array into an endian encoded
+ * offset vector.
+ */
+ ns(Monster_testarrayoftables_end(B));
+
+ strings[0] = nsc(string_create_str(B, "Hello"));
+ /* Test embedded null character.
+ * Note _strn is at most n, or up to 0 termination:
+ * wrong: strings[1] = nsc(string_create_strn(B, ",\0", 2));
+ */
+ strings[1] = nsc(string_create(B, ",\0", 2));
+ strings[2] = nsc(string_create_str(B, "world!"));
+ ns(Monster_testarrayofstring_create(B, strings, 3));
+
+ assert(c_vec_len(bools) == 4);
+ ns(Monster_testarrayofbools_start(B));
+ ns(Monster_testarrayofbools_append(B, bools, 1));
+ ns(Monster_testarrayofbools_append(B, bools + 1, 3));
+ ns(Monster_testarrayofbools_end(B));
+
+ /*
+ * This is using a constructor argument list where a union
+ * is a single argument, unlike the C++ interface.
+ * A union is given a type and a table reference.
+ *
+ * We are not verifying the result as this is only to stress
+ * the type system of the builder - except: the last array
+ * element is tested to ensure add_value is getting through.
+ */
+ ns(Monster_test_add)(B, ns(Any_as_Monster(mon)));
+
+ ns(Monster_enemy_start(B));
+ ns(Monster_name_create_str(B, "the enemy"));
+
+ /* Create array of monsters to test various union constructors. */
+ ns(Monster_testarrayoftables_start(B));
+
+ ns(Monster_vec_push_start(B));
+ ns(Monster_test_add)(B, ns(Any_as_Monster(mon)));
+ /* Name is required. */
+ ns(Monster_name_create_str(B, "any name"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_test_Monster_add(B, mon));
+ ns(Monster_name_create_str(B, "any name"));
+ ns(Monster_vec_push_end(B));
+ /*
+ * `push_start`: We can use the field specific method, or the type specific method
+ * that the field maps to.
+ */
+ ns(Monster_testarrayoftables_push_start(B));
+ /*
+ * This is mostly for internal use in create methods so the type
+ * can be added last and pack better in the table.
+ * `add_value` still takes union_ref because it is a NOP if
+ * the union type is NONE.
+ */
+ ns(Monster_test_add_value(B, ns(Any_as_Monster(mon))));
+ ns(Monster_name_create_str(B, "any name"));
+ ns(Monster_test_add_type(B, ns(Any_Monster)));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_end(B));
+
+ ns(Monster_enemy_end(B));
+
+ ns(Monster_testbool_add(B, 0));
+
+ ns(Monster_testempty_start(B));
+ ns(Monster_testempty_end(B));
+
+ ns(Monster_end_as_root(B));
+ return 0;
+}
+
+int test_monster(flatcc_builder_t *B)
+{
+ void *buffer;
+ size_t size;
+ int ret;
+
+ gen_monster(B, 0);
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+ hexdump("monster table", buffer, size, stderr);
+ if ((ret = ns(Monster_verify_as_root(buffer, size)))) {
+ printf("Monster buffer failed to verify, got: %s\n", flatcc_verify_error_string(ret));
+ return -1;
+ }
+ ret = verify_monster(buffer);
+
+ flatcc_builder_aligned_free(buffer);
+ return ret;
+}
+
+int test_monster_with_size(flatcc_builder_t *B)
+{
+ void *buffer, *frame;
+ size_t size, size2, esize;
+ int ret;
+
+ gen_monster(B, 1);
+
+ frame = flatcc_builder_finalize_aligned_buffer(B, &size);
+ hexdump("monster table with size", frame, size, stderr);
+ if (((size_t)frame & 15)) {
+ printf("Platform did not provide 16 byte aligned allocation and needs special attention.");
+ printf("buffer address: %x\n", (flatbuffers_uoffset_t)(size_t)frame);
+ return -1;
+ }
+
+ buffer = flatbuffers_read_size_prefix(frame, &size2);
+ esize = size - sizeof(flatbuffers_uoffset_t);
+ if (size2 != esize) {
+ printf("Size prefix has unexpected size, got %i, expected %i\n", (int)size2, (int)esize);
+ return -1;
+ }
+ if ((ret = ns(Monster_verify_as_root(buffer, size2)))) {
+ printf("Monster buffer with size prefix failed to verify, got: %s\n", flatcc_verify_error_string(ret));
+ return -1;
+ }
+ ret = verify_monster(buffer);
+
+ flatcc_builder_aligned_free(frame);
+ return ret;
+}
+
+int test_cloned_monster(flatcc_builder_t *B)
+{
+ void *buffer;
+ void *cloned_buffer;
+ size_t size;
+ int ret;
+ flatcc_refmap_t refmap, *refmap_old;
+
+ flatcc_refmap_init(&refmap);
+ gen_monster(B, 0);
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+ hexdump("monster table", buffer, size, stderr);
+ if ((ret = ns(Monster_verify_as_root(buffer, size)))) {
+ printf("Monster buffer failed to verify, got: %s\n", flatcc_verify_error_string(ret));
+ return -1;
+ }
+ if (verify_monster(buffer)) {
+ return -1;
+ }
+ flatcc_builder_reset(B);
+
+ /*
+ * Clone works without setting a refmap - but then shared references
+ * get expanded - and then the verify monster check fails on a DAG
+ * test.
+ */
+ refmap_old = flatcc_builder_set_refmap(B, &refmap);
+ if (!ns(Monster_clone_as_root(B, ns(Monster_as_root(buffer))))) {
+ printf("Cloned Monster didn't actually clone.");
+ return -1;
+ };
+ /*
+ * Restoring old refmap (or zeroing) is optional if we cleared the
+ * buffer in this scope, but we don't so we must detach and clean up
+ * the refmap manually. refmap_old is likely just null, but this
+ * way we do not interfere with caller.
+ */
+ flatcc_builder_set_refmap(B, refmap_old);
+ cloned_buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+ hexdump("cloned monster table", cloned_buffer, size, stderr);
+ if ((ret = ns(Monster_verify_as_root(cloned_buffer, size)))) {
+ printf("Cloned Monster buffer failed to verify, got: %s\n", flatcc_verify_error_string(ret));
+ return -1;
+ }
+ if (verify_monster(cloned_buffer)) {
+ printf("Cloned Monster did not have the expected content.");
+ return -1;
+ }
+
+ flatcc_refmap_clear(&refmap);
+ flatcc_builder_aligned_free(buffer);
+ flatcc_builder_aligned_free(cloned_buffer);
+ return ret;
+}
+
+int test_string(flatcc_builder_t *B)
+{
+ ns(Monster_table_t) mon;
+ void *buffer;
+ char *s;
+
+ flatcc_builder_reset(B);
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_start(B));
+ s = ns(Monster_name_extend(B, 3));
+ s[0] = '1';
+ s[1] = '2';
+ s[2] = '3';
+ ns(Monster_name_append_str(B, "4"));
+ assert(ns(Monster_name_reserved_len(B)) == 4);
+ ns(Monster_name_append_strn(B, "5678", 30));
+ assert(ns(Monster_name_reserved_len(B)) == 8);
+ ns(Monster_name_append(B, "90", 2));
+ assert(ns(Monster_name_reserved_len(B)) == 10);
+ ns(Monster_name_truncate(B, 3));
+ assert(ns(Monster_name_reserved_len(B)) == 7);
+ s = ns(Monster_name_edit(B));
+ s[4] = '.';
+ ns(Monster_name_end(B));
+ ns(Monster_end_as_root(B));
+ /* Only with small buffers and the default emitter. */
+ buffer = flatcc_builder_get_direct_buffer(B, 0);
+ assert(buffer);
+ mon = ns(Monster_as_root(buffer));
+ if (strcmp(ns(Monster_name(mon)), "1234.67")) {
+ printf("string test failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+int test_sort_find(flatcc_builder_t *B)
+{
+ size_t pos;
+ ns(Monster_table_t) mon;
+ ns(Monster_vec_t) monsters;
+ ns(Monster_mutable_vec_t) mutable_monsters;
+ void *buffer;
+ size_t size;
+ int ret = -1;
+
+ flatcc_builder_reset(B);
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "MyMonster"));
+
+ ns(Monster_testarrayoftables_start(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_str(B, "TwoFace"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_str(B, "Joker"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_str(B, "Gulliver"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_str(B, "Alice"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_str(B, "Gulliver"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_end(B));
+
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+
+ hexdump("unsorted monster buffer", buffer, size, stderr);
+ mon = ns(Monster_as_root(buffer));
+ monsters = ns(Monster_testarrayoftables(mon));
+ assert(monsters);
+ mutable_monsters = (ns(Monster_mutable_vec_t))monsters;
+ ns(Monster_vec_sort_by_name(mutable_monsters));
+
+ hexdump("sorted monster buffer", buffer, size, stderr);
+
+ if (ns(Monster_vec_len(monsters)) != 5) {
+ printf("Sorted monster vector has wrong length\n");
+ goto done;
+ }
+ if (strcmp(ns(Monster_name(ns(Monster_vec_at(monsters, 0)))), "Alice")) {
+ printf("sort isn't working at elem 0\n");
+ goto done;
+ }
+ if (strcmp(ns(Monster_name(ns(Monster_vec_at(monsters, 1)))), "Gulliver")) {
+ printf("sort isn't working at elem 1\n");
+ goto done;
+ }
+ if (strcmp(ns(Monster_name(ns(Monster_vec_at(monsters, 2)))), "Gulliver")) {
+ printf("sort isn't working at elem 2\n");
+ goto done;
+ }
+ if (strcmp(ns(Monster_name(ns(Monster_vec_at(monsters, 3)))), "Joker")) {
+ printf("sort isn't working at elem 3\n");
+ goto done;
+ }
+ if (strcmp(ns(Monster_name(ns(Monster_vec_at(monsters, 4)))), "TwoFace")) {
+ printf("sort isn't working at elem 4\n");
+ goto done;
+ }
+ /*
+ * The heap sort isn't stable, but it should keep all elements
+ * unique. Note that we could still have identical objects if we
+ * actually stored the same object twice in DAG structure.
+ */
+ if (ns(Monster_vec_at(monsters, 1)) == ns(Monster_vec_at(monsters, 2))) {
+ printf("Two identical sort keys should not be identical objects (in this case)\n");
+ goto done;
+ }
+
+ if (3 != ns(Monster_vec_find(monsters, "Joker"))) {
+ printf("find by default key did not find the Joker\n");
+ goto done;
+ }
+ if (3 != ns(Monster_vec_find_n(monsters, "Joker2", 5))) {
+ printf("find by default key did not find the Joker with n\n");
+ goto done;
+ }
+ /*
+ * We can have multiple keys on a table or struct by naming the sort
+ * and find operations.
+ */
+ if (3 != ns(Monster_vec_find_by_name(monsters, "Joker"))) {
+ printf("find did not find the Joker\n");
+ goto done;
+ }
+ if (3 != ns(Monster_vec_find_n_by_name(monsters, "Joker3", 5))) {
+ printf("find did not find the Joker with n\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_find_by_name(monsters, "Jingle"))) {
+ printf("not found not working\n");
+ goto done;
+ }
+ if (0 != ns(Monster_vec_find_by_name(monsters, "Alice"))) {
+ printf("Alice not found\n");
+ goto done;
+ }
+ /*
+ * The search, unlike sort, is stable and should return the first
+ * index of repeated keys.
+ */
+ if (1 != (pos = ns(Monster_vec_find_by_name(monsters, "Gulliver")))) {
+ printf("Gulliver not found\n");
+ printf("got %d\n", (int)pos);
+ goto done;
+ }
+ if (4 != (pos = ns(Monster_vec_find_by_name(monsters, "TwoFace")))) {
+ printf("TwoFace not found\n");
+ printf("got %d\n", (int)pos);
+ goto done;
+ }
+
+ /*
+ * Just make sure the default key has a sort method - it is the same
+ * as sort_by_name for the monster schema.
+ */
+ ns(Monster_vec_sort(mutable_monsters));
+ ret = 0;
+
+done:
+ flatcc_builder_aligned_free(buffer);
+ return ret;
+}
+
+static size_t count_monsters(ns(Monster_vec_t) monsters, const char *name)
+{
+ size_t i;
+ size_t count = 0;
+
+ for (i = ns(Monster_vec_scan)(monsters, name);
+ i != nsc(not_found);
+ i = ns(Monster_vec_scan_ex)(monsters, i + 1, nsc(end), name)) {
+ ++count;
+ }
+
+ return count;
+}
+
+int test_scan(flatcc_builder_t *B)
+{
+ size_t pos;
+ ns(Monster_table_t) mon;
+ ns(Monster_vec_t) monsters;
+ nsc(uint8_vec_t) inv;
+ nsc(string_vec_t) strings;
+ void *buffer;
+ size_t size;
+ uint8_t invdata[] = { 6, 7, 1, 3, 4, 3, 2 };
+ int ret = -1;
+
+ flatcc_builder_reset(B);
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "MyMonster"));
+ ns(Monster_inventory_create(B, invdata, c_vec_len(invdata)));
+
+ ns(Monster_testarrayofstring_start(B));
+ ns(Monster_testarrayofstring_end(B));
+
+ ns(Monster_testarrayoftables_start(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_str(B, "TwoFace"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_str(B, "Joker"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_str(B, "Gulliver"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_str(B, "Alice"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_push_start(B));
+ ns(Monster_name_create_str(B, "Gulliver"));
+ ns(Monster_testarrayoftables_push_end(B));
+
+ ns(Monster_testarrayoftables_end(B));
+
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+ mon = ns(Monster_as_root(buffer));
+ monsters = ns(Monster_testarrayoftables(mon));
+ assert(monsters);
+ inv = ns(Monster_inventory(mon));
+ assert(inv);
+ strings = ns(Monster_testarrayofstring(mon));
+ assert(strings);
+
+ if (1 != ns(Monster_vec_scan(monsters, "Joker"))) {
+ printf("scan_by did not find the Joker\n");
+ goto done;
+ }
+ if (1 != ns(Monster_vec_rscan(monsters, "Joker"))) {
+ printf("rscan_by did not find the Joker\n");
+ goto done;
+ }
+ if (1 != ns(Monster_vec_scan_n(monsters, "Joker3", 5))) {
+ printf("scan_by did not find the Joker with n\n");
+ goto done;
+ }
+ if (1 != ns(Monster_vec_rscan_n(monsters, "Joker3", 5))) {
+ printf("scan_by did not find the Joker with n\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_scan_ex(monsters, 2, nsc(end), "Joker"))) {
+ printf("scan_from found Joker past first occurence\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_scan(monsters, "Jingle"))) {
+ printf("not found not working\n");
+ goto done;
+ }
+ if (0 != ns(Monster_vec_scan(monsters, "TwoFace"))) {
+ printf("TwoFace not found\n");
+ goto done;
+ }
+ if (2 != ns(Monster_vec_scan_by_name(monsters, "Gulliver"))) {
+ printf("Gulliver not found\n");
+ goto done;
+ }
+ if (4 != ns(Monster_vec_rscan_by_name(monsters, "Gulliver"))) {
+ printf("Gulliver not found\n");
+ goto done;
+ }
+ if (4 != ns(Monster_vec_rscan_n_by_name(monsters, "Gulliver42", 8))) {
+ printf("Gulliver not found with n\n");
+ goto done;
+ }
+ if (2 != ns(Monster_vec_rscan_ex_n_by_name(monsters, 1, 3, "Gulliver42", 8))) {
+ printf("Gulliver not found with n\n");
+ goto done;
+ }
+ if (2 != ns(Monster_vec_scan_ex_by_name(monsters, 2, nsc(end), "Gulliver"))) {
+ printf("Gulliver not found starting from Gulliver\n");
+ goto done;
+ }
+ if (2 != ns(Monster_vec_scan_ex_n_by_name(monsters, 2, nsc(end), "Gulliver42", 8))) {
+ printf("Gulliver not found starting from Gulliver\n");
+ goto done;
+ }
+ if (4 != ns(Monster_vec_scan_ex_by_name(monsters, 3, nsc(end), "Gulliver"))) {
+ printf("Another Gulliver not found\n");
+ goto done;
+ }
+
+ if (nsc(not_found) != ns(Monster_vec_scan_ex(monsters, 1, 3, "Jingle"))) {
+ printf("not found in subrange not working\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_scan_ex(monsters, 1, 3, "TwoFace"))) {
+ printf("subrange doesn't limit low bound\n");
+ goto done;
+ }
+ if (1 != ns(Monster_vec_scan_ex(monsters, 1, 3, "Joker"))) {
+ printf("scan in subrange did not find Joker\n");
+ goto done;
+ }
+ if (2 != ns(Monster_vec_scan_ex_by_name(monsters, 1, 3, "Gulliver"))) {
+ printf("scan in subrange did not find Gulliver\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_scan_ex_by_name(monsters, 1, 3, "Alice"))) {
+ printf("subrange doesn't limit upper bound in scan\n");
+ goto done;
+ }
+
+ if (nsc(not_found) != ns(Monster_vec_rscan_ex(monsters, 1, 3, "Jingle"))) {
+ printf("not found in subrange not working with rscan\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_rscan_ex(monsters, 1, 3, "TwoFace"))) {
+ printf("subrange doesn't limit lower bound in rscan\n");
+ goto done;
+ }
+ if (1 != ns(Monster_vec_rscan_ex(monsters, 1, 3, "Joker"))) {
+ printf("rscan in subrange did not find Joker\n");
+ goto done;
+ }
+ if (2 != ns(Monster_vec_rscan_ex_by_name(monsters, 1, 3, "Gulliver"))) {
+ printf("rscan in subrange did not find Gulliver\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_rscan_ex_by_name(monsters, 1, 3, "Alice"))) {
+ printf("subrange doesn't limit upper bound in rscan\n");
+ goto done;
+ }
+
+ if (nsc(not_found) != ns(Monster_vec_scan_ex(monsters, 0, 0, "TwoFace"))) {
+ printf("TwoFace is found in empty range\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_scan_ex(monsters, 0, 0, "Joker"))) {
+ printf("Joker is found in empty range\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_scan_ex(monsters, 1, 1, "Joker"))) {
+ printf("Joker is found in another empty range\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_scan_ex(monsters, ns(Monster_vec_len(monsters)), nsc(end), "TwoFace"))) {
+ printf("TwoFace is found in empty range in the end\n");
+ goto done;
+ }
+
+ if (nsc(not_found) != ns(Monster_vec_rscan_ex(monsters, 0, 0, "TwoFace"))) {
+ printf("TwoFace is found in empty range\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_rscan_ex(monsters, 0, 0, "Joker"))) {
+ printf("Joker is found in empty range\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_rscan_ex(monsters, 1, 1, "Joker"))) {
+ printf("Joker is found in another empty range\n");
+ goto done;
+ }
+ if (nsc(not_found) != ns(Monster_vec_rscan_ex(monsters, ns(Monster_vec_len(monsters)), nsc(end), "TwoFace"))) {
+ printf("TwoFace is found in empty range in the end\n");
+ goto done;
+ }
+
+ if (1 != count_monsters(monsters, "Joker")) {
+ printf("number of Jokers is not 1\n");
+ goto done;
+ }
+ if (0 != count_monsters(monsters, "Jingle")) {
+ printf("number of Jingles is not 0\n");
+ goto done;
+ }
+ if (1 != count_monsters(monsters, "TwoFace")) {
+ printf("number of TwoFace is not 1\n");
+ goto done;
+ }
+ if (2 != count_monsters(monsters, "Gulliver")) {
+ printf("number of Gullivers is not 2\n");
+ goto done;
+ }
+
+
+ if (0 != (pos = nsc(uint8_vec_scan(inv, 6)))) {
+ printf("scan not working on first item of inventory\n");
+ goto done;
+ }
+ if (2 != (pos = nsc(uint8_vec_scan(inv, 1)))) {
+ printf("scan not working on middle item of inventory\n");
+ goto done;
+ }
+ if (nsc(not_found) != (pos = nsc(uint8_vec_scan_ex(inv, 3, nsc(end), 1)))) {
+ printf("scan_ex(item+1) not working on middle item of inventory\n");
+ goto done;
+ }
+ if (nsc(not_found) != (pos = nsc(uint8_vec_scan(inv, 5)))) {
+ printf("scan not working for repeating item of inventory\n");
+ goto done;
+ }
+ if (6 != (pos = nsc(uint8_vec_scan(inv, 2)))) {
+ printf("scan not working on last item of inventory\n");
+ goto done;
+ }
+ if (3 != (pos = nsc(uint8_vec_scan(inv, 3)))) {
+ printf("scan not working for repeating item of inventory\n");
+ goto done;
+ }
+ if (3 != (pos = nsc(uint8_vec_scan_ex(inv, 3, nsc(end), 3)))) {
+ printf("scan_ex(item) not working for repeating item of inventory\n");
+ goto done;
+ }
+ if (5 != (pos = nsc(uint8_vec_scan_ex(inv, 4, nsc(end), 3)))) {
+ printf("scan_ex(item+1) not working for repeating item of inventory\n");
+ goto done;
+ }
+ if (5 != (pos = nsc(uint8_vec_rscan(inv, 3)))) {
+ printf("rscan not working for repeating item of inventory\n");
+ goto done;
+ }
+ if (3 != (pos = nsc(uint8_vec_rscan_ex(inv, 1, 4, 3)))) {
+ printf("rscan_ex not working for repeating item of inventory\n");
+ goto done;
+ }
+
+ /* Test that all scan functions are generated for string arrays */
+ nsc(string_vec_scan(strings, "Hello"));
+ nsc(string_vec_scan_ex(strings, 0, nsc(end), "Hello"));
+ nsc(string_vec_scan_n(strings, "Hello", 4));
+ nsc(string_vec_scan_ex_n(strings, 0, nsc(end), "Hello", 4));
+ nsc(string_vec_rscan(strings, "Hello"));
+ nsc(string_vec_rscan_ex(strings, 0, nsc(end), "Hello"));
+ nsc(string_vec_rscan_n(strings, "Hello", 4));
+ nsc(string_vec_rscan_ex_n(strings, 0, nsc(end), "Hello", 4));
+
+#if FLATCC_ALLOW_SCAN_FOR_ALL_FIELDS
+ /* Check for presence of scan for non-key fields */
+ ns(Monster_vec_scan_by_hp(monsters, 13));
+ ns(Monster_vec_scan_ex_by_hp(monsters, 1, nsc(end), 42));
+ ns(Monster_vec_rscan_by_hp(monsters, 1));
+ ns(Monster_vec_rscan_ex_by_hp(monsters, 0, 2, 42));
+#endif
+
+ ret = 0;
+
+done:
+ flatcc_builder_aligned_free(buffer);
+ return ret;
+}
+
+int test_basic_sort(flatcc_builder_t *B)
+{
+ ns(Monster_table_t) mon;
+ nsc(uint8_vec_t) inv;
+ nsc(uint8_mutable_vec_t) minv;
+
+ void *buffer;
+ size_t size;
+ uint8_t invdata[] = { 6, 7, 1, 3, 4, 3, 2 };
+ uint8_t sortedinvdata[] = { 1, 2, 3, 3, 4, 6, 7 };
+ uint8_t v, i;
+
+ flatcc_builder_reset(B);
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "MyMonster"));
+ ns(Monster_inventory_create(B, invdata, c_vec_len(invdata)));
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_get_direct_buffer(B, &size);
+
+ mon = ns(Monster_as_root(buffer));
+ inv = ns(Monster_inventory(mon));
+ minv = (nsc(uint8_mutable_vec_t))inv;
+ nsc(uint8_vec_sort(minv));
+ assert(nsc(uint8_vec_len(inv) == c_vec_len(invdata)));
+ for (i = 0; i < nsc(uint8_vec_len(inv)); ++i) {
+ v = nsc(uint8_vec_at(inv, i));
+ if (v != sortedinvdata[i]) {
+ printf("inventory not sorted\n");
+ return -1;
+ }
+ if (nsc(uint8_vec_find(inv, v) != (i == 3 ? 2 : i))) {
+ printf("find not working on inventory\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int test_clone_slice(flatcc_builder_t *B)
+{
+ ns(Monster_table_t) mon, mon2;
+ nsc(string_vec_t) strings;
+ nsc(bool_vec_t) bools;
+ nsc(string_t) name;
+ ns(Monster_ref_t) monster_ref;
+ ns(Test_t) *t;
+ ns(Test_struct_t) test4;
+ ns(Test_struct_t) elem4;
+ void *buffer, *buf2;
+ size_t size;
+ int ret = -1;
+ uint8_t booldata[] = { 0, 1, 0, 0, 1, 0, 0 };
+
+ flatcc_builder_reset(B);
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "The Source"));
+ ns(Monster_testarrayofbools_create(B, booldata, c_vec_len(booldata)));
+
+ ns(Monster_test4_start(B));
+ t = ns(Monster_test4_extend(B, 2));
+ t[0].a = 22;
+ t[1].a = 44;
+ ns(Monster_test4_end(B));
+ ns(Monster_pos_start(B))->x = -42.3f;
+
+ ns(Monster_end_as_root(B));
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+ hexdump("clone slice source buffer", buffer, size, stderr);
+
+ mon = ns(Monster_as_root(buffer));
+
+ flatcc_builder_reset(B);
+ ns(Monster_start_as_root(B));
+
+ name = ns(Monster_name(mon));
+ assert(name);
+ bools = ns(Monster_testarrayofbools(mon));
+ assert(bools);
+ test4 = ns(Monster_test4(mon));
+ assert(test4);
+
+ ns(Monster_name_clone(B, name));
+ ns(Monster_testarrayofstring_start(B));
+ ns(Monster_testarrayofstring_push_clone(B, name));
+ ns(Monster_testarrayofstring_push_slice(B, name, 4, 20));
+ ns(Monster_testarrayofstring_push_slice(B, name, 0, 3));
+ ns(Monster_testarrayofstring_end(B));
+ ns(Monster_start(B));
+ ns(Monster_name_slice(B, name, 2, 20));
+ ns(Monster_testarrayofbools_clone(B, bools));
+
+ ns(Monster_test4_slice(B, test4, 1, 2));
+
+
+ monster_ref = ns(Monster_end(B));
+
+ ns(Monster_test_add(B, ns(Any_as_Monster(monster_ref))));
+ ns(Monster_testarrayofbools_slice(B, bools, 3, (size_t)-1));
+
+ ns(Monster_pos_clone(B, ns(Monster_pos(mon))));
+ ns(Monster_test4_clone(B, test4));
+
+ ns(Monster_end_as_root(B));
+
+ buf2 = flatcc_builder_get_direct_buffer(B, &size);
+ hexdump("target buffer of clone", buf2, size, stderr);
+ mon2 = ns(Monster_as_root(buf2));
+
+ if (strcmp(ns(Monster_name(mon2)), "The Source")) {
+ printf("The Source was not cloned\n");
+ goto done;
+ }
+
+ strings = ns(Monster_testarrayofstring(mon2));
+ if (strcmp(nsc(string_vec_at(strings, 0)), "The Source")) {
+ printf("Push clone failed The Source\n");
+ goto done;
+ }
+ if (nsc(string_len(nsc(string_vec_at(strings, 1)))) != 6) {
+ printf("Push slice failed Sourcee on length\n");
+ goto done;
+ }
+ if (strcmp(nsc(string_vec_at(strings, 1)), "Source")) {
+ printf("Push slice failed Source\n");
+ goto done;
+ }
+ if (nsc(string_len(nsc(string_vec_at(strings, 2)))) != 3) {
+ printf("Push slice failed The on length\n");
+ goto done;
+ }
+ if (strcmp(nsc(string_vec_at(strings, 2)), "The")) {
+ printf("Push slice failed The\n");
+ goto done;
+ }
+ mon = ns(Monster_test(mon2));
+ assert(mon);
+ if (strcmp(ns(Monster_name(mon)), "e Source")) {
+ printf("name_slice did not shorten The Source correctly");
+ goto done;
+ }
+ bools = ns(Monster_testarrayofbools(mon));
+ if (nsc(bool_vec_len(bools)) != 7) {
+ printf("clone bool has wrong length\n");
+ goto done;
+ }
+ if (memcmp(bools, booldata, 7)) {
+ printf("cloned bool has wrong content\n");
+ goto done;
+ }
+
+ bools = ns(Monster_testarrayofbools(mon2));
+ if (nsc(bool_vec_len(bools)) != 4) {
+ printf("slice bool has wrong length\n");
+ goto done;
+ }
+ if (memcmp(bools, booldata + 3, 4)) {
+ printf("sliced bool has wrong content\n");
+ goto done;
+ }
+ if (!parse_float_is_equal(ns(Monster_pos(mon2))->x, -42.3f)) {
+ printf("cloned pos struct failed\n");
+ goto done;
+ };
+ test4 = ns(Monster_test4(mon2));
+ if (ns(Test_vec_len(test4)) != 2) {
+ printf("struct vector test4 not cloned with correct length\n");
+ goto done;
+ }
+ elem4 = ns(Test_vec_at(test4, 0));
+ if (ns(Test_a(elem4)) != 22) {
+ printf("elem 0 of test4 not cloned\n");
+ goto done;
+ }
+ if (flatbuffers_is_native_pe() && ns(Test_vec_at(test4, 0))->a != 22) {
+ printf("elem 0 of test4 not cloned, direct access\n");
+ goto done;
+ }
+ elem4 = ns(Test_vec_at(test4, 1));
+ if (ns(Test_a(elem4)) != 44) {
+ printf("elem 1 of test4 not cloned\n");
+ goto done;
+ }
+ test4 = ns(Monster_test4(mon));
+ if (ns(Test_vec_len(test4)) != 1) {
+ printf("sliced struct vec not sliced\n");
+ goto done;
+ }
+ elem4 = ns(Test_vec_at(test4, 0));
+ if (ns(Test_a(elem4)) != 44) {
+ printf("sliced struct vec has wrong element\n");
+ goto done;
+ }
+
+ /*
+ * There is no push clone of structs because it becomes messy when
+ * the vector has to be ended using end_pe or alternative do double
+ * conversion with unclear semantics.
+ */
+
+ ret = 0;
+
+done:
+ flatcc_builder_aligned_free(buffer);
+ return ret;
+}
+
+int test_create_add_field(flatcc_builder_t *B)
+{
+ void *buffer;
+ size_t size;
+ int ret = -1;
+ ns(Monster_table_t) mon;
+ ns(Stat_table_t) stat;
+
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "MyMonster"));
+ ns(Monster_testempty_create(B, nsc(string_create_str(B, "hello")), -100, 2));
+ ns(Monster_enemy_add(B, 0));
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+ mon = ns(Monster_as_root(buffer));
+ if (ns(Monster_enemy_is_present(mon))) {
+ printf("enemy should not be present when adding null\n");
+ goto done;
+ }
+ stat = ns(Monster_testempty(mon));
+ if (!(ns(Stat_val(stat)) == -100)) {
+ printf("Stat didn't happen\n");
+ goto done;
+ }
+ ret = 0;
+done:
+ flatcc_builder_aligned_free(buffer);
+ return ret;
+}
+
+int verify_union_vector(void *buffer, size_t size)
+{
+ int ret = -1;
+ size_t n;
+ int color;
+
+ ns(Monster_table_t) mon;
+ ns(TestSimpleTableWithEnum_table_t) kermit;
+ flatbuffers_generic_vec_t anyvec;
+ ns(Any_vec_t) anyvec_type;
+ ns(Any_union_vec_t) anyvec_union;
+ ns(Any_union_t) anyelem;
+ ns(Alt_table_t) alt;
+
+ if ((ret = ns(Monster_verify_as_root(buffer, size)))) {
+ printf("Monster buffer with union vector failed to verify, got: %s\n", flatcc_verify_error_string(ret));
+ goto failed;
+ }
+
+ mon = ns(Monster_as_root(buffer));
+ if (ns(Monster_test_type(mon)) != ns(Any_Alt)) {
+ printf("test field does not have Alt type");
+ goto failed;
+ }
+ alt = ns(Monster_test(mon));
+ if (!alt || !ns(Alt_manyany_is_present(alt))) {
+ printf("manyany union vector should be present.\n");
+ goto failed;
+ }
+ anyvec_type = ns(Alt_manyany_type(alt));
+ anyvec = ns(Alt_manyany(alt));
+ n = ns(Any_vec_len(anyvec_type));
+ if (n != 1) {
+ printf("manyany union vector has wrong length.\n");
+ goto failed;
+ }
+ if (nsc(union_type_vec_at(anyvec_type, 0)) != ns(Any_TestSimpleTableWithEnum)) {
+ printf("manyany union vector has wrong element type.\n");
+ goto failed;
+ }
+ kermit = flatbuffers_generic_vec_at(anyvec, 0);
+ if (!kermit) {
+ printf("Kermit is lost.\n");
+ goto failed;
+ }
+ color = ns(TestSimpleTableWithEnum_color(kermit));
+ if (color != ns(Color_Green)) {
+ printf("Kermit has wrong color: %i.\n", (int)color);
+ goto failed;
+ }
+ anyvec_union = ns(Alt_manyany_union(alt));
+ if (ns(Any_union_vec_len(anyvec_union)) != 1) {
+ printf("manyany union vector has wrong length from a different perspective.\n");
+ goto failed;
+ }
+ anyelem = ns(Any_union_vec_at(anyvec_union, 0));
+ if (anyelem.type != nsc(union_type_vec_at(anyvec_type, 0))) {
+ printf("Kermit is now different.\n");
+ goto failed;
+ }
+ if (anyelem.value != kermit) {
+ printf("Kermit is incoherent.\n");
+ goto failed;
+ }
+ ret = 0;
+
+done:
+ return ret;
+
+failed:
+ ret = -1;
+ goto done;
+}
+
+int test_union_vector(flatcc_builder_t *B)
+{
+ void *buffer = 0, *cloned_buffer = 0;
+ size_t size;
+ int ret = -1;
+ flatcc_refmap_t refmap, *refmap_old;
+
+ ns(TestSimpleTableWithEnum_ref_t) kermit_ref;
+ ns(Any_union_vec_ref_t) anyvec_ref;
+
+
+ flatcc_refmap_init(&refmap);
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "Kermit"));
+
+ kermit_ref = ns(TestSimpleTableWithEnum_create(B,
+ ns(Color_Green), ns(Color_Green),
+ ns(Color_Green), ns(Color_Green)));
+ ns(Any_vec_start(B));
+ ns(Any_vec_push(B, ns(Any_as_TestSimpleTableWithEnum(kermit_ref))));
+ anyvec_ref = ns(Any_vec_end(B));
+ ns(Monster_test_Alt_start(B));
+ ns(Alt_manyany_add(B, anyvec_ref));
+ ns(Monster_test_Alt_end(B));
+
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+
+ if (verify_union_vector(buffer, size)) {
+ printf("Union vector Monster didn't verify.\n");
+ goto failed;
+ }
+ flatcc_builder_reset(B);
+ refmap_old = flatcc_builder_set_refmap(B, &refmap);
+ if (!ns(Monster_clone_as_root(B, ns(Monster_as_root(buffer))))) {
+ printf("Cloned union vector Monster didn't actually clone.\n");
+ goto failed;
+ };
+ flatcc_builder_set_refmap(B, refmap_old);
+ cloned_buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+
+ if (verify_union_vector(buffer, size)) {
+ printf("Cloned union vector Monster didn't verify.\n");
+ goto failed;
+ }
+
+ ret = 0;
+
+done:
+ flatcc_refmap_clear(&refmap);
+ flatcc_builder_aligned_free(buffer);
+ flatcc_builder_aligned_free(cloned_buffer);
+ return ret;
+
+failed:
+ ret = -1;
+ goto done;
+}
+
+int verify_fixed_length_array(const void *buffer, size_t size)
+{
+ const char *text;
+ ns(Monster_table_t) mon;
+ ns(Alt_table_t) alt;
+ ns(FooBar_struct_t) fa;
+ ns(FooBar_t) fa2;
+ ns(Test_struct_t) t0, t1;
+ int ret;
+
+ if ((ret = ns(Monster_verify_as_root(buffer, size)))) {
+ printf("Monster buffer with fixed length arrays failed to verify, got: %s\n", flatcc_verify_error_string(ret));
+ return -1;
+ }
+
+ mon = ns(Monster_as_root(buffer));
+ if (ns(Monster_test_type(mon)) != ns(Any_Alt)) {
+ printf("test field does not have Alt type");
+ return -1;
+ }
+
+ alt = ns(Monster_test(mon));
+ if (!alt || !ns(Alt_fixed_array_is_present(alt))) {
+ printf("fixed array should be present.\n");
+ return -1;
+ }
+
+ fa = ns(Alt_fixed_array(alt));
+
+ if (ns(FooBar_foo(fa, 0)) != 1.0f || ns(FooBar_bar(fa, 9) != 1000)) {
+ printf("Monster buffer with fixed length arrays has wrong content\n");
+ return -1;
+ }
+
+ if (ns(FooBar_foo_get(fa, 0)) != 1.0f || ns(FooBar_bar_get(fa, 9) != 1000)) {
+ printf("Monster buffer with fixed length arrays has wrong content\n");
+ return -1;
+ }
+ if (ns(FooBar_foo_get(fa, 16)) != 0.0f || ns(FooBar_bar_get(fa, 10) != 0)) {
+ printf("Monster buffer with fixed length arrays has bad bounds check\n");
+ return -1;
+ }
+ if (ns(FooBar_col_get(fa, 2)) != ns(Color_Red)) {
+ printf("Fixed length enum array content not correct\n");
+ return -1;
+ }
+ t0 = ns(FooBar_tests_get(fa, 0));
+ t1 = ns(FooBar_tests_get(fa, 1));
+ if (!t0 || !t1) {
+ printf("Monster buffer with fixed length struct arrays has missing element\n");
+ return -1;
+ }
+ if (ns(Test_a_get(t0)) != 0 || ns(Test_b_get(t0)) != 4) {
+ printf("Monster buffer with fixed length struct arrays has wrong first element member content\n");
+ return -1;
+ }
+ if (ns(Test_a_get(t1)) != 1 || ns(Test_b_get(t1)) != 2) {
+ printf("Monster buffer with fixed length struct arrays has wrong second element member content\n");
+ return -1;
+ }
+
+ /* Endian safe because char arrays are endian neutral. */
+ text = ns(FooBar_text_get_ptr(fa));
+ if (strncmp(text, "hello", ns(FooBar_text_get_len())) != 0) {
+ printf("Monster buffer with fixed length array field has wrong text\n");
+ return -1;
+ }
+
+ /*
+ * Note: use ns(FooBar_foo_get_ptr(fa) to get a raw pointer to the
+ * array is not endian safe. Since this is a struct array field,
+ * fa->foo would also provide the raw pointer.
+ */
+ if (flatbuffers_is_native_pe()) {
+ if (ns(FooBar_foo_get_ptr(fa))[1] != 2.0f) {
+ printf("Monster buffer with fixed length arrays get_ptr has wrong content\n");
+ return -1;
+ }
+ }
+
+ ns(FooBar_copy_from_pe(&fa2, fa));
+ if (fa2.foo[0] != 1.0f || fa2.foo[1] != 2.0f || fa2.foo[15] != 16.0f ||
+ fa2.bar[0] != 100 || fa2.bar[9] != 1000) {
+ printf("Monster buffer with copied fixed length arrays has wrong content\n");
+ return -1;
+ }
+ if (fa2.foo[2] != 0.0f || fa2.foo[14] != 0.0f || fa2.bar[1] != 0 || fa2.bar[8] != 0) {
+ printf("Monster buffer with copied fixed length arrays has not been zero padded\n");
+ return -1;
+ }
+
+ /*
+ * In-place conversion - a nop on little endian platforms.
+ * Cast needed to remove const
+ */
+ ns(FooBar_from_pe)((ns(FooBar_t) *)fa);
+ if (fa->foo[0] != 1.0f || fa->foo[1] != 2.0f || fa->foo[15] != 16.0f ||
+ fa->bar[0] != 100 || fa->bar[9] != 1000) {
+ printf("Monster buffer with in-place converted fixed length arrays has wrong content\n");
+ return -1;
+ }
+ if (fa->foo[2] != 0.0f || fa->foo[14] != 0.0f || fa->bar[1] != 0 || fa->bar[8] != 0) {
+ printf("Monster buffer with in-place converted fixed length arrays has not been zero padded\n");
+ return -1;
+ }
+ return 0;
+}
+
+int test_fixed_length_array(flatcc_builder_t *B)
+{
+ void *buffer = 0;
+ size_t size;
+ int ret = -1;
+ float foo_input[16] = { 1.0f, 2.0f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16.0f };
+ int bar_input[10] = { 100, 0, 0, 0, 0, 0, 0, 0, 0, 1000 };
+ ns(Color_enum_t) col_input[3] = { 0, 0, ns(Color_Red) };
+ ns(Test_t) tests_input[2] = {{ 0, 4 }, { 1, 2 }};
+
+ ns(FooBar_t) *foobar;
+
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "Monolith"));
+ ns(Monster_test_Alt_start(B));
+ foobar = ns(Alt_fixed_array_start(B));
+ foobar->foo[0] = 1.0f;
+ foobar->foo[1] = 2.0f;
+ foobar->foo[15] = 16.0f;
+ foobar->bar[0] = 100;
+ foobar->bar[9] = 1000;
+ foobar->col[2] = ns(Color_Red);
+ foobar->tests[0].b = 4;
+ foobar->tests[1].a = 1;
+ foobar->tests[1].b = 2;
+ strncpy(foobar->text, "hello, world", ns(FooBar_text_get_len()));
+ // or strncopy(foobar->text, "hello, world", sizeof(foobar->text));
+ ns(Alt_fixed_array_end(B));
+ ns(Monster_test_Alt_end(B));
+
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+ ret = verify_fixed_length_array(buffer, size);
+ flatcc_builder_aligned_free(buffer);
+ if (ret) return -1;
+
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "Monolith"));
+ ns(Monster_test_Alt_start(B));
+ foobar = ns(Alt_fixed_array_start(B));
+ ns(FooBar_assign)(foobar, foo_input, bar_input, col_input, tests_input, "hello");
+ ns(Alt_fixed_array_end(B));
+ ns(Monster_test_Alt_end(B));
+
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+ ret = verify_fixed_length_array(buffer, size);
+ flatcc_builder_aligned_free(buffer);
+ if (ret) return -1;
+
+ return 0;
+}
+
+#define STR(s) nsc(string_create_str(B, s))
+
+int test_recursive_sort(flatcc_builder_t *B)
+{
+ nsc(string_ref_t) name;
+
+ void *buffer = 0;
+ size_t size = 0;
+ int ret = -1;
+ ns(Alt_table_t) alt;
+ ns(Any_union_t) any;
+ ns(Monster_table_t) monster;
+ ns(MultipleKeys_vec_t) mkvec;
+ ns(MultipleKeys_table_t) mk;
+ size_t index;
+
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+
+ name = STR("Keyed Monster");
+ ns(Alt_start(B));
+ ns(Alt_multik_start(B));
+ ns(Alt_multik_push_create(B, STR("hi"), STR("there"), 42));
+ ns(Alt_multik_push_create(B, STR("hello"), STR("anyone"), 10));
+ ns(Alt_multik_push_create(B, STR("hello"), STR("anyone"), 4));
+ ns(Alt_multik_push_create(B, STR("good day"), STR("sir"), 1004));
+ ns(Alt_multik_end(B));
+ ns(Monster_test_add)(B, ns(Any_as_Alt(ns(Alt_end(B)))));
+ ns(Monster_name_add)(B, name);
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+ monster = ns(Monster_as_root)(buffer);
+ ns(Monster_sort)((ns(Monster_mutable_table_t))monster);
+ any = ns(Monster_test_union(monster));
+ if (any.type != ns(Any_Alt)) {
+ printf("Any type no Alt as expected\n");
+ goto done;
+ }
+ alt = any.value;
+ mkvec = ns(Alt_multik(alt));
+ index = ns(MultipleKeys_vec_len(mkvec));
+ if (index != 4) {
+ printf("unexpected multik vec len, got %d\n", (int)index);
+ goto done;
+ }
+ mk = ns(MultipleKeys_vec_at(mkvec, 0));
+ if (ns(MultipleKeys_foobar(mk) != 4)) {
+ printf("multik elem 0 not sorted, but it really should be\n");
+ }
+ mk = ns(MultipleKeys_vec_at(mkvec, 1));
+ if (ns(MultipleKeys_foobar(mk) != 10)) {
+ printf("multik elem 1 not sorted, but it really should be\n");
+ }
+ mk = ns(MultipleKeys_vec_at(mkvec, 2));
+ if (ns(MultipleKeys_foobar(mk) != 42)) {
+ printf("multik elem 2 not sorted, but it really should be\n");
+ }
+ mk = ns(MultipleKeys_vec_at(mkvec, 3));
+ if (ns(MultipleKeys_foobar(mk) != 1004)) {
+ printf("multik elem 3 not sorted, but it really should be\n");
+ }
+
+ hexdump("MultiKeyed buffer", buffer, size, stderr);
+ if ((ret = ns(Monster_verify_as_root(buffer, size)))) {
+ printf("Multikeyed Monster buffer failed to verify, got: %s\n", flatcc_verify_error_string(ret));
+ goto done;
+ }
+
+ ret = 0;
+done:
+ flatcc_builder_aligned_free(buffer);
+ return ret;
+}
+
+int test_mixed_type_union(flatcc_builder_t *B)
+{
+ void *buffer;
+ size_t size;
+ int ret = -1;
+ /* Builder */
+ nsf(Character_union_ref_t) ut;
+ nsf(Rapunzel_ref_t) cameo_ref;
+ nsf(Attacker_ref_t) attacker_ref;
+ nsf(BookReader_ref_t) br_ref;
+ nsf(BookReader_t *) pbr;
+ nsf(Movie_table_t) mov;
+
+ /* Reader */
+ nsf(Character_union_vec_t) characters;
+ nsf(Character_union_t) character;
+ nsf(Rapunzel_struct_t) rapunzel;
+ nsf(Attacker_table_t) attacker;
+ nsc(string_t) text;
+
+ flatcc_builder_reset(B);
+
+ nsf(Movie_start_as_root(B));
+ br_ref = nsf(BookReader_create(B, 10));
+ cameo_ref = nsf(Rapunzel_create(B, 22));
+ ut = nsf(Character_as_Rapunzel(cameo_ref));
+ nsf(Movie_main_character_Rapunzel_create(B, 19));
+ nsf(Movie_cameo_Rapunzel_add(B, cameo_ref));
+ attacker_ref = nsf(Attacker_create(B, 42));
+ nsf(Movie_antagonist_MuLan_add(B, attacker_ref));
+ nsf(Movie_side_kick_Other_create_str(B, "Nemo"));
+ nsf(Movie_characters_start(B));
+ nsf(Movie_characters_push(B, ut));
+ nsf(Movie_characters_MuLan_push(B, attacker_ref));
+ nsf(Movie_characters_MuLan_push_create(B, 1));
+ nsf(Character_vec_push(B, nsf(Character_as_Other(nsc(string_create_str(B, "other"))))));
+ nsf(Movie_characters_Belle_push(B, br_ref));
+ pbr = nsf(Movie_characters_Belle_push_start(B));
+ pbr->books_read = 3;
+ nsf(Movie_characters_Belle_push_end(B));
+ nsf(Movie_characters_Belle_push(B, nsf(BookReader_create(B, 1))));
+ nsf(Movie_characters_Belle_push_create(B, 2));
+ nsf(Movie_characters_Other_push(B, nsc(string_create_str(B, "another"))));
+ nsf(Movie_characters_Other_push_create_str(B, "yet another"));
+ nsf(Movie_characters_end(B));
+ nsf(Movie_end_as_root(B));
+
+ buffer = flatcc_builder_finalize_aligned_buffer(B, &size);
+
+ hexdump("Movie buffer", buffer, size, stderr);
+ if ((ret = nsf(Movie_verify_as_root(buffer, size)))) {
+ printf("Movie buffer with mixed type union and union vector failed to verify, got: %s\n", flatcc_verify_error_string(ret));
+ return -1;
+ }
+ ret = -1;
+
+ mov = nsf(Movie_as_root(buffer));
+ if (!nsf(Movie_main_character_is_present(mov))) {
+ printf("Main_charactery union should be present.\n");
+ goto done;
+ }
+ if (!nsf(Movie_characters_is_present(mov))) {
+ printf("Characters union vector should be present.\n");
+ goto done;
+ }
+ character = nsf(Movie_main_character_union(mov));
+ if (character.type != nsf(Character_Rapunzel)) {
+ printf("Unexpected main character.\n");
+ goto done;
+ };
+ /*
+ * Tables and structs can cast by void pointer assignment while
+ * strings require an explicit cast.
+ */
+ rapunzel = character.value;
+ if (!rapunzel) {
+ printf("Rapunzel has gone AWOL\n");
+ }
+ if (nsf(Rapunzel_hair_length(rapunzel)) > 19) {
+ printf("Rapunzel's hair has grown unexpectedly\n");
+ goto done;
+ }
+ if (nsf(Rapunzel_hair_length(rapunzel)) < 19) {
+ printf("Rapunzel's hair has been trimmed unexpectedly\n");
+ goto done;
+ }
+ if (nsf(Movie_cameo_type(mov)) != nsf(Character_Rapunzel)) {
+ printf("Rapunzel did was not selected for cameo appearance.\n");
+ goto done;
+ }
+ rapunzel = nsf(Movie_cameo(mov));
+ if (!rapunzel) {
+ printf("Rapunzel did not show up for cameo appearance.\n");
+ goto done;
+ }
+ if (nsf(Rapunzel_hair_length(rapunzel)) != 22) {
+ printf("Rapunzel didn't style her hair for cameo role.\n");
+ goto done;
+ }
+ if (nsf(Movie_antagonist_type(mov)) != nsf(Character_MuLan)) {
+ printf("Unexpected antagonist.\n");
+ goto done;
+ }
+ attacker = nsf(Movie_antagonist(mov));
+ if (!attacker || nsf(Attacker_sword_attack_damage(attacker)) != 42) {
+ printf("Unexpected sword attack damamage.\n");
+ goto done;
+ }
+ if (nsf(Movie_side_kick_type(mov)) != nsf(Character_Other)) {
+ printf("Unexpected side kick.\n");
+ goto done;
+ }
+ /*
+ * We need to cast because generic pointers refer to the start
+ * of the memory block which is the string length, not the first
+ * character in the string.
+ */
+ text = nsc(string_cast_from_generic(nsf(Movie_side_kick(mov))));
+ if (!text) {
+ printf("missing side kick string.\n");
+ goto done;
+ }
+ if (strcmp(text, "Nemo")) {
+ printf("unexpected side kick string: '%s'.\n", text);
+ goto done;
+ }
+ text = nsf(Movie_side_kick_as_string(mov));
+ if (!text) {
+ printf("missing side kick string.\n");
+ goto done;
+ }
+ if (strcmp(text, "Nemo")) {
+ printf("unexpected side kick string (take 2): '%s'.\n", text);
+ goto done;
+ }
+ character = nsf(Movie_side_kick_union(mov));
+ text = nsc(string_cast_from_union(character));
+ if (strcmp(text, "Nemo")) {
+ printf("unexpected side kick string (take 3): '%s'.\n", text);
+ goto done;
+ }
+ characters = nsf(Movie_characters_union(mov));
+ character = nsf(Character_union_vec_at(characters, 0));
+ if (character.type != nsf(Character_Rapunzel)) {
+ printf("The first character is not Rapunzel.");
+ goto done;
+ };
+ character = nsf(Character_union_vec_at(characters, 1));
+ if (character.type != nsf(Character_MuLan)) {
+ printf("The second character is not MuLan.");
+ goto done;
+ };
+ attacker = character.value;
+ if (nsf(Attacker_sword_attack_damage(attacker) != 42)) {
+ printf("The second character has unexpected sword damage.");
+ goto done;
+ }
+ character = nsf(Character_union_vec_at(characters, 2));
+ if (character.type != nsf(Character_MuLan)) {
+ printf("The third character is not MuLan.");
+ goto done;
+ };
+ attacker = character.value;
+ if (nsf(Attacker_sword_attack_damage(attacker) != 1)) {
+ printf("The third character has unexpected sword damage.");
+ goto done;
+ }
+ if (nsc(union_type_vec_at(nsf(Movie_characters_type(mov)), 3)) != nsf(Character_Other)) {
+ printf("The fourth character was not of type 'Other'!\n");
+ goto done;
+ }
+ text = nsf(Character_union_vec_at_as_string(characters, 3));
+ if (!text || strcmp(text, "other")) {
+ printf("The fourth character was not described as 'other'.\n");
+ goto done;
+ }
+ character = nsf(Character_union_vec_at(characters, 3));
+ if (character.type != nsf(Character_Other)) {
+ printf("The fourth character is not of type 'Other' (take two).");
+ goto done;
+ };
+ text = nsc(string_cast_from_union(character));
+ if (!text || strcmp(text, "other")) {
+ printf("The fourth character was not described as 'other' (take two).\n");
+ goto done;
+ }
+ character = nsf(Character_union_vec_at(characters, 4));
+ if (character.type != nsf(Character_Belle)) {
+ printf("The fifth character is not Belle.");
+ goto done;
+ };
+ character = nsf(Character_union_vec_at(characters, 5));
+ if (character.type != nsf(Character_Belle)) {
+ printf("The sixth character is not Belle.");
+ goto done;
+ };
+ character = nsf(Character_union_vec_at(characters, 6));
+ if (character.type != nsf(Character_Belle)) {
+ printf("The seventh character is not Belle.");
+ goto done;
+ };
+ character = nsf(Character_union_vec_at(characters, 7));
+ if (character.type != nsf(Character_Belle)) {
+ printf("The eighth character is not Belle.");
+ goto done;
+ };
+ character = nsf(Character_union_vec_at(characters, 8));
+ if (character.type != nsf(Character_Other)) {
+ printf("The ninth character is not of type 'Other'.");
+ goto done;
+ };
+ character = nsf(Character_union_vec_at(characters, 9));
+ if (character.type != nsf(Character_Other)) {
+ printf("The ninth character is not of type 'Other'.");
+ goto done;
+ };
+ if (nsf(Character_union_vec_len(characters) != 10)) {
+ printf("The 11'th character should not exist.");
+ goto done;
+ };
+
+ ret = 0;
+done:
+ flatcc_builder_aligned_free(buffer);
+ return ret;
+}
+
+int test_add_set_defaults(flatcc_builder_t *B)
+{
+ void *buffer;
+ size_t size;
+ ns(Monster_table_t) mon;
+
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "MyMonster"));
+ ns(Monster_hp_add(B, 100));
+ ns(Monster_mana_add(B, 100));
+ ns(Monster_color_add(B, ns(Color_Blue)));
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_get_direct_buffer(B, &size);
+ mon = ns(Monster_as_root(buffer));
+ if (ns(Monster_hp_is_present(mon))) {
+ printf("default should not be present for hp field\n");
+ return -1;
+ }
+ if (!ns(Monster_mana_is_present(mon))) {
+ printf("non-default should be present for mana field\n");
+ return -1;
+ }
+ if (ns(Monster_color_is_present(mon))) {
+ printf("default should not be present for color field\n");
+ return -1;
+ }
+
+ flatcc_builder_reset(B);
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "MyMonster"));
+ ns(Monster_hp_force_add(B, 100));
+ ns(Monster_mana_force_add(B, 100));
+ ns(Monster_color_force_add(B, ns(Color_Blue)));
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_get_direct_buffer(B, &size);
+ mon = ns(Monster_as_root(buffer));
+ if (!ns(Monster_hp_is_present(mon))) {
+ printf("default should be present for hp field when forced\n");
+ return -1;
+ }
+ if (!ns(Monster_mana_is_present(mon))) {
+ printf("non-default should be present for mana field, also when forced\n");
+ return -1;
+ }
+ if (!ns(Monster_color_is_present(mon))) {
+ printf("default should be present for color field when forced\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int test_nested_buffer(flatcc_builder_t *B)
+{
+ void *buffer;
+ size_t size;
+ ns(Monster_table_t) mon, nested;
+
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "MyMonster"));
+ /*
+ * Note:
+ * ns(Monster_testnestedflatbuffer_start(B));
+ * would start a raw ubyte array so we use start_as_root.
+ */
+ ns(Monster_testnestedflatbuffer_start_as_root(B));
+ ns(Monster_name_create_str(B, "MyNestedMonster"));
+ ns(Monster_testnestedflatbuffer_end_as_root(B));
+ ns(Monster_hp_add(B, 10));
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_get_direct_buffer(B, &size);
+ hexdump("nested flatbuffer", buffer, size, stderr);
+
+ mon = ns(Monster_as_root(buffer));
+ if (strcmp(ns(Monster_name(mon)), "MyMonster")) {
+ printf("got the wrong root monster\n");
+ return -1;
+ }
+ /*
+ * Note:
+ * nested = ns(Monster_testnestedflatbuffer(mon));
+ * would return a raw ubyte vector not a monster.
+ */
+ nested = ns(Monster_testnestedflatbuffer_as_root(mon));
+
+ if (ns(Monster_hp(mon)) != 10) {
+ printf("health points wrong at root monster\n");
+ return -1;
+ }
+
+ assert(ns(Monster_name(nested)));
+ if (strcmp(ns(Monster_name(nested)), "MyNestedMonster")) {
+ printf("got the wrong nested monster\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int test_nested_buffer_first(flatcc_builder_t *B)
+{
+ void *buffer;
+ size_t size;
+ ns(Monster_table_t) mon, nested;
+
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+ /*
+ * Note:
+ * ns(Monster_testnestedflatbuffer_start(B));
+ * would start a raw ubyte array so we use start_as_root.
+ *
+ * Here we create the nested buffer first, and the parent
+ * string after so the emitter sees the nested buffer first.
+ */
+ ns(Monster_testnestedflatbuffer_start_as_root(B));
+ ns(Monster_name_create_str(B, "MyNestedMonster"));
+ ns(Monster_testnestedflatbuffer_end_as_root(B));
+ ns(Monster_hp_add(B, 10));
+ ns(Monster_name_create_str(B, "MyMonster"));
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_get_direct_buffer(B, &size);
+ hexdump("nested flatbuffer", buffer, size, stderr);
+
+ mon = ns(Monster_as_root(buffer));
+ if (strcmp(ns(Monster_name(mon)), "MyMonster")) {
+ printf("got the wrong root monster\n");
+ return -1;
+ }
+ /*
+ * Note:
+ * nested = ns(Monster_testnestedflatbuffer(mon));
+ * would return a raw ubyte vector not a monster.
+ */
+ nested = ns(Monster_testnestedflatbuffer_as_root(mon));
+
+ if (ns(Monster_hp(mon)) != 10) {
+ printf("health points wrong at root monster\n");
+ return -1;
+ }
+
+ assert(ns(Monster_name(nested)));
+ if (strcmp(ns(Monster_name(nested)), "MyNestedMonster")) {
+ printf("got the wrong nested monster\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int test_nested_buffer_using_nest(flatcc_builder_t *B)
+{
+ void *buffer;
+ uint8_t nested_buffer[1024];
+ size_t size, nested_size;
+ ns(Monster_table_t) mon, nested;
+
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+ ns(Monster_name_create_str(B, "MyNestedMonster"));
+ ns(Monster_mana_add(B, 42));
+ ns(Monster_end_as_root(B));
+
+ nested_size = flatcc_builder_get_buffer_size(B);
+ if (!flatcc_builder_copy_buffer(B, nested_buffer, sizeof(nested_buffer))) {
+ printf("nested buffer copy failed\n");
+ return -1;
+ }
+
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+ ns(Monster_testnestedflatbuffer_nest(B, nested_buffer, nested_size, 0));
+ ns(Monster_hp_add(B, 10));
+ ns(Monster_name_create_str(B, "MyMonster"));
+ ns(Monster_end_as_root(B));
+
+ buffer = flatcc_builder_get_direct_buffer(B, &size);
+ hexdump("nested flatbuffer [using _nest()]", buffer, size, stderr);
+
+ mon = ns(Monster_as_root(buffer));
+ if (strcmp(ns(Monster_name(mon)), "MyMonster")) {
+ printf("got the wrong root monster\n");
+ return -1;
+ }
+ /*
+ * Note:
+ * nested = ns(Monster_testnestedflatbuffer(mon));
+ * would return a raw ubyte vector not a monster.
+ */
+ nested = ns(Monster_testnestedflatbuffer_as_root(mon));
+
+ if (ns(Monster_hp(mon)) != 10) {
+ printf("health points wrong at root monster\n");
+ return -1;
+ }
+
+ assert(ns(Monster_name(nested)));
+ if (strcmp(ns(Monster_name(nested)), "MyNestedMonster")) {
+ printf("got the wrong nested monster\n");
+ return -1;
+ }
+
+ if (ns(Monster_mana(nested)) != 42) {
+ printf("mana points wrong in nested monster\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int verify_include(void *buffer)
+{
+ (void)buffer;
+
+ if (MyGame_OtherNameSpace_FromInclude_Foo != 17) {
+ printf("Unexpected enum value `Foo` from included schema\n");
+ return -1;
+ }
+
+ if (MyGame_OtherNameSpace_FromInclude_IncludeVal != 0) {
+ printf("Unexpected enum value `IncludeVal` from included schema\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int test_struct_buffer(flatcc_builder_t *B)
+{
+ uint8_t buffer[100];
+
+ size_t size;
+ ns(Vec3_t) *v;
+ ns(Vec3_struct_t) vec3;
+
+ flatcc_builder_reset(B);
+ ns(Vec3_create_as_root(B, 1, 2, 3, 4.2, ns(Color_Blue), 2730, -17));
+ size = flatcc_builder_get_buffer_size(B);
+ assert(size == 48);
+ printf("dbg: struct buffer size: %d\n", (int)size);
+ assert(flatcc_emitter_get_buffer_size(flatcc_builder_get_emit_context(B)) == size);
+ if (!flatcc_builder_copy_buffer(B, buffer, 100)) {
+ printf("Copy failed\n");
+ return -1;
+ }
+ hexdump("Vec3 struct buffer", buffer, size, stderr);
+ if (!nsc(has_identifier(buffer, "MONS"))) {
+ printf("wrong Vec3 identifier (explicit)\n");
+ return -1;
+ }
+ if (nsc(has_identifier(buffer, "mons"))) {
+ printf("accepted wrong Vec3 identifier (explicit)\n");
+ return -1;
+ }
+ if (!nsc(has_identifier(buffer, ns(Vec3_identifier)))) {
+ printf("wrong Vec3 identifier (via define)\n");
+ return -1;
+ }
+ vec3 = ns(Vec3_as_root(buffer));
+ /* Convert buffer to native in place - a nop on native platform. */
+ v = (ns(Vec3_t) *)vec3;
+ ns(Vec3_from_pe(v));
+ if (!parse_float_is_equal(v->x, 1.0f) || !parse_float_is_equal(v->y, 2.0f) || !parse_float_is_equal(v->z, 3.0f)
+ || !parse_double_is_equal(v->test1, 4.2) || v->test2 != ns(Color_Blue)
+ || v->test3.a != 2730 || v->test3.b != -17
+ ) {
+ printf("struct buffer not valid\n");
+ return -1;
+ }
+ assert(ns(Color_Red) == 1 << 0);
+ assert(ns(Color_Green) == 1 << 1);
+ assert(ns(Color_Blue) == 1 << 3);
+ assert(sizeof(ns(Color_Blue) == 1));
+ return 0;
+}
+
+int test_typed_struct_buffer(flatcc_builder_t *B)
+{
+ uint8_t buffer[100];
+
+ size_t size;
+ ns(Vec3_t) *v;
+ ns(Vec3_struct_t) vec3;
+
+ flatcc_builder_reset(B);
+ ns(Vec3_create_as_typed_root(B, 1, 2, 3, 4.2, ns(Color_Blue), 2730, -17));
+ size = flatcc_builder_get_buffer_size(B);
+ assert(size == 48);
+ printf("dbg: struct buffer size: %d\n", (int)size);
+ assert(flatcc_emitter_get_buffer_size(flatcc_builder_get_emit_context(B)) == size);
+ if (!flatcc_builder_copy_buffer(B, buffer, 100)) {
+ printf("Copy failed\n");
+ return -1;
+ }
+ hexdump("typed Vec3 struct buffer", buffer, size, stderr);
+ if (!nsc(has_identifier(buffer, "\xd2\x3e\xf5\xa8"))) {
+ printf("wrong Vec3 identifier (explicit)\n");
+ return -1;
+ }
+ if (nsc(has_identifier(buffer, "mons"))) {
+ printf("accepted wrong Vec3 identifier (explicit)\n");
+ return -1;
+ }
+ if (!nsc(has_identifier(buffer, ns(Vec3_type_identifier)))) {
+ printf("wrong Vec3 identifier (via define)\n");
+ return -1;
+ }
+ if (!ns(Vec3_as_root_with_type_hash(buffer, ns(Vec3_type_hash)))) {
+ printf("wrong Vec3 type identifier (via define)\n");
+ return -1;
+ }
+ if (flatcc_verify_ok != ns(Vec3_verify_as_root_with_type_hash(buffer, size, ns(Vec3_type_hash)))) {
+ printf("verify failed with Vec3 type hash\n");
+ return -1;
+ }
+ vec3 = ns(Vec3_as_typed_root(buffer));
+ if (!vec3) {
+ printf("typed Vec3 could not be read\n");
+ return -1;
+ }
+ if (flatcc_verify_ok != ns(Vec3_verify_as_typed_root(buffer, size))) {
+ printf("verify failed with Vec3 as typed root\n");
+ return -1;
+ }
+ /* Convert buffer to native in place - a nop on native platform. */
+ v = (ns(Vec3_t) *)vec3;
+ ns(Vec3_from_pe(v));
+ if (!parse_float_is_equal(v->x, 1.0f) || !parse_float_is_equal(v->y, 2.0f) || !parse_float_is_equal(v->z, 3.0f)
+ || !parse_double_is_equal(v->test1, 4.2) || v->test2 != ns(Color_Blue)
+ || v->test3.a != 2730 || v->test3.b != -17
+ ) {
+ printf("struct buffer not valid\n");
+ return -1;
+ }
+ assert(ns(Color_Red) == 1 << 0);
+ assert(ns(Color_Green) == 1 << 1);
+ assert(ns(Color_Blue) == 1 << 3);
+ assert(sizeof(ns(Color_Blue) == 1));
+ return 0;
+}
+
+
+/* A stable test snapshot for reference. */
+int gen_monster_benchmark(flatcc_builder_t *B)
+{
+ uint8_t inv[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ ns(Vec3_t) *vec;
+ ns(Test_t) *test, x;
+
+ flatcc_builder_reset(B);
+
+ ns(Monster_start_as_root(B));
+ ns(Monster_hp_add(B, 80));
+ vec = ns(Monster_pos_start(B));
+ vec->x = 1, vec->y = 2, vec->z = -3.2f;
+ ns(Monster_pos_end(B));
+ ns(Monster_name_create_str(B, "MyMonster"));
+ ns(Monster_inventory_create(B, inv, c_vec_len(inv)));
+ ns(Monster_test4_start(B));
+ test = ns(Monster_test4_extend(B, 1));
+ test->a = 0x10;
+ test->b = 0x20;
+ test = ns(Monster_test4_extend(B, 2));
+ test->a = 0x30;
+ test->b = 0x40;
+ test[1].a = 0x50;
+ test[1].b = 0x60;
+ ns(Monster_test4_push_create(B, 0x70, (int8_t)0x80));
+ x.a = 0x191; /* This is a short. */
+ x.b = (int8_t)0x91; /* This is a byte. */
+ ns(Monster_test4_push(B, &x));
+ ns(Monster_test4_end(B));
+ ns(Monster_end_as_root(B));
+
+ return 0;
+}
+
+int time_monster(flatcc_builder_t *B)
+{
+ double t1, t2;
+ const int rep = 1000000;
+ size_t size;
+ int i;
+
+ printf("start timing ...\n");
+ t1 = elapsed_realtime();
+ for (i = 0; i < rep; ++i) {
+ gen_monster_benchmark(B);
+ }
+ size = flatcc_builder_get_buffer_size(B);
+ t2 = elapsed_realtime();
+ show_benchmark("encode monster buffer", t1, t2, size, rep, "million");
+ return 0;
+}
+
+int gen_struct_buffer_benchmark(flatcc_builder_t *B)
+{
+ void *buffer;
+ ns(Vec3_t) *v;
+ ns(Vec3_struct_t) vec3;
+
+ flatcc_builder_reset(B);
+
+ ns(Vec3_create_as_root(B, 1, 2, 3, 4.2, ns(Color_Blue), 2730, -17));
+
+ buffer = flatcc_builder_get_direct_buffer(B, 0);
+ if (!buffer) {
+ return -1;
+ }
+ vec3 = ns(Vec3_as_root_with_identifier(buffer, 0));
+ /* Convert buffer to native in place - a nop on native platform. */
+ v = (ns(Vec3_t) *)vec3;
+ ns(Vec3_from_pe(v));
+ if (v->x != 1.0f || v->y != 2.0f || v->z != 3.0f
+ || v->test1 != 4.2 || v->test2 != ns(Color_Blue)
+ || v->test3.a != 2730 || v->test3.b != -17
+ ) {
+ return -1;
+ }
+ return 0;
+}
+
+int time_struct_buffer(flatcc_builder_t *B)
+{
+ double t1, t2;
+ const int rep = 1000000;
+ size_t size;
+ int i;
+ int ret = 0;
+
+ printf("start timing ...\n");
+ t1 = elapsed_realtime();
+ for (i = 0; i < rep; ++i) {
+ ret |= gen_struct_buffer_benchmark(B);
+ }
+ t2 = elapsed_realtime();
+ size = flatcc_builder_get_buffer_size(B);
+ if (ret) {
+ printf("struct not valid\n");
+ }
+ show_benchmark("encode, decode and access Vec struct buffers", t1, t2, size, rep, "million");
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ flatcc_builder_t builder, *B;
+
+ (void)argc;
+ (void)argv;
+
+ B = &builder;
+ flatcc_builder_init(B);
+
+#ifdef NDEBUG
+ printf("running optimized monster test\n");
+#else
+ printf("running debug monster test\n");
+#endif
+#if 1
+ if (test_enums(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_empty_monster(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_monster(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_monster_with_size(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_string(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_struct_buffer(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_typed_empty_monster(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_typed_struct_buffer(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_clone_slice(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_add_set_defaults(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_create_add_field(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_union_vector(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_basic_sort(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_sort_find(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_scan(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_nested_buffer(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_nested_buffer_first(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_nested_buffer_using_nest(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_cloned_monster(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (verify_include(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_type_aliases(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_mixed_type_union(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_recursive_sort(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+#if 1
+ if (test_fixed_length_array(B)) {
+ printf("TEST FAILED\n");
+ return -1;
+ }
+#endif
+
+#ifdef FLATBUFFERS_BENCHMARK
+ time_monster(B);
+ time_struct_buffer(B);
+#endif
+ flatcc_builder_clear(B);
+ return 0;
+}
diff --git a/flatcc/test/monster_test/monster_test.fbs b/flatcc/test/monster_test/monster_test.fbs
new file mode 100755
index 0000000..f0f78aa
--- /dev/null
+++ b/flatcc/test/monster_test/monster_test.fbs
@@ -0,0 +1,365 @@
+// test schema file
+
+include "attributes.fbs";
+include "include_test1.fbs";
+
+struct InGlobalNamespace { unused: byte; }
+
+namespace MyGame;
+
+table InParentNamespace {}
+
+namespace MyGame.Example2;
+
+table Monster {} // Test having same name as below, but in different namespace.
+
+table Strange {}
+
+// Test schema reserved keywords as identifier
+// Can be disabled in config/config.h
+// It is still a good idea to use a namespace
+// to avoid conflicts with host language names
+// especially for enums and structs.
+// Reserved names can be important in some JSON
+// related use cases.
+table S2 {
+ namespace : int;
+ table : int;
+ struct : int;
+ union : int;
+ int : int;
+}
+
+// Enums fields can also be reserved, and these are also visible in
+// JSON, but they cannot be used as table field defaults because
+// type expressions do interpret keywords as keywords. For the same
+// reason it is of no use to allow reserved names as type names
+// such as table names.
+enum foo:int { x, y, table }
+
+namespace MyGame.Example;
+
+// Note: parent namespace resolution only works without namespace prefix
+//
+// Won't work:
+// union Foo { Example2.InParentNamespace }
+union Foo { InParentNamespace }
+
+
+namespace MyGame.Example2.SubSystem;
+
+// Note:
+//
+// parent namespace resolution only works without namespace prefix
+// or with a global namespace prefix (a fully qualified name), not
+// something in between.
+// (There is no really good reason for this, it just isn't implemented,
+// and flatc JSON enum parsing works the same way.)
+// The more local name wins any conflict.
+//
+union SubSystemA { Strange }
+union SubSystemB { MyGame.Example2.Strange }
+
+// Works in C++ flatc 1.8 but won't work with flatcc:
+// union SubSystemC { Example2.Strange }
+
+
+namespace MyGame.Example;
+
+// test case for negative enum values
+enum neg_enum:int {
+ neg1 = -12,
+ neg2 = -11,
+ neg3 = -10,
+}
+
+enum int_enum:int {
+ intneg = -02,
+ intneg2 = -1,
+ int1 = 02,
+ int2 = 42,
+}
+
+// test for hex constants
+enum hex_enum:int {
+ hexneg = -0x02,
+ hex1 = 0x3,
+ hex2 = 0x7eafbeaf,
+}
+
+attribute "priority";
+
+enum Color:byte (bit_flags) { Red = 0, Green, Blue = 3, }
+
+// Note:
+// For historic reasons C++ flatc 1.8 does permit conflicting base names
+// from different namespaces without explicitly resolving the conflict.
+//
+// flatcc does not, and cannot, support this because it needs a unique
+// base name to assign to the enumaration of union members - otherwise
+// these enumerations could not have a namespace prefix - which is used
+// in JSON and in default value assignment in the schema.
+//
+// Wont' work in flatc:
+// union Any { Monster, TestSimpleTableWithEnum, MyGame.Example2.Monster }
+
+union Any {
+ Monster,
+ TestSimpleTableWithEnum,
+ Monster2: MyGame.Example2.Monster,
+ Alt
+}
+
+struct Test { a:short; b:byte; }
+
+enum notemptyenum:int { x}
+
+table TestSimpleTableWithEnum (csharp_partial) {
+ color: Color = Green;
+ color2: Color = Color.Green;
+ uc : ubyte = MyGame.Example.Color.Green;
+ uc2 : ubyte = Color.Green;
+
+ // C++ flatc 1.8 dislikes enum values on non-enums
+ // color2: Color = Green;
+ // C++ flatc 1.8 dislikes enum values on non-enums
+ // uc : ubyte = 1;
+ // C++ flatc 1.8 dislikes enum values on non-enums, and namespace prefix
+ // uc2 : ubyte = 1;
+}
+
+table TestInclude {
+ global:InGlobalNamespace;
+ incval:MyGame.OtherNameSpace.FromInclude;
+ incval2:MyGame.OtherNameSpace.FromInclude = IncludeVal;
+ incval3 : int (included_attribute);
+ incval4:MyGame.OtherNameSpace.FromInclude = MyGame.OtherNameSpace.FromInclude.IncludeVal;
+ incval5: long = MyGame.OtherNameSpace.FromInclude.IncludeVal;
+
+ // C++ flatc 1.8 dislikes namespace prefix
+ // incval4:MyGame.OtherNameSpace.FromInclude = IncludeVal;
+ // C++ flatc 1.8 dislikes enum values on non-enums, and namespace prefix
+ // incval5: long = 0;
+}
+
+struct Vec3 (force_align: 16) {
+ x:float;
+ y:float;
+ z:float;
+ test1:double;
+ test2:Color;
+ test3:Test;
+}
+
+struct Ability {
+ id:uint(key);
+ distance:uint;
+}
+
+table Stat {
+ id:string;
+ val:long;
+ count:ushort;
+}
+
+// fixed length arrays new to flatcc 0.6.0
+struct FooBar {
+ foo:[float:0x10];
+ bar:[int:10];
+ col:[Color:3];
+ tests:[Test:2];
+ text:[char:5];
+}
+
+// `sorted` attribute new to flatcc 0.6.0, not supported by flatc 1.8.
+// tables with direct or indirect vector content marked as sorted
+// will get a mutable sort operation that recursively sorts all
+// such vectors. 'sorted` is only valid for non-union vectors.
+//
+// attribute "sorted";
+//
+table Alt {
+ prefix: TestJSONPrefix;
+ movie:Fantasy.Movie;
+ manyany: [Any];
+ multik: [MultipleKeys] (sorted);
+ rapunzels:[Fantasy.Rapunzel] (sorted);
+ names:[string] (sorted);
+ samples:[float32] (sorted);
+ fixed_array: FooBar;
+}
+
+table TestJSONPrefix {
+ testjsonprefixparsing:TestJSONPrefixParsing;
+ testjsonprefixparsing2:TestJSONPrefixParsing2;
+ testjsonprefixparsing3:TestJSONPrefixParsing3;
+}
+
+table TestJSONPrefixParsing
+{
+ aaaa: string;
+ aaaa12345: uint;
+
+ bbbb: string;
+ bbbb1234: long;
+
+ cccc: string;
+ cccc1234: long;
+ cccc12345: uint;
+
+ dddd1234: long;
+ dddd12345: uint;
+}
+
+// when there are two keys ending in same 8 character group
+table TestJSONPrefixParsing2
+{
+ aaaa_bbbb_steps: long;
+ aaaa_bbbb_start_: uint;
+}
+
+// when there are two keys ending in different 8 character group
+table TestJSONPrefixParsing3
+{
+ aaaa_bbbb_steps: long;
+ aaaa_bbbb_start_steps: uint;
+}
+
+// C++ flatc 1.8 does not yet support base64 as a built-in
+// attribute "base64";
+// attribute "base64url";
+
+table TestBase64
+{
+ data:[ubyte] (base64);
+ urldata:[ubyte] (base64url);
+ nested:[ubyte] (nested_flatbuffer: "Monster", base64);
+}
+
+// 'primary_key' attribute new to flatcc 0.6.0, not supported by flatc 1.8.
+// Allow multiple keys and allow one to be the default find and sort key
+// even if not listed first. A table with a single key field behaves the
+// same as a table with a single primary_key field, so use key for
+// compatiblity in that case.
+//
+// attribute "primary_key";
+//
+table MultipleKeys
+{
+ hello: string;
+ world: string (key);
+ foobar: int64 (primary_key);
+}
+
+table Monster {
+ pos:Vec3 (id: 0);
+ hp:short = 100 (id: 2);
+ mana:short = 150 (id: 1);
+ name:string (id: 3, required, key);
+ color:Color = Blue (id: 6);
+ inventory:[ubyte] (id: 5);
+ friendly:bool = false (deprecated, priority: 1, id: 4);
+ /// an example documentation comment: this will end up in the generated code
+ /// multiline too
+ testarrayoftables:[Monster] (id: 11);
+ testarrayofstring:[string] (id: 10);
+ testarrayofstring2:[string] (id: 28);
+ testarrayofbools:[bool] (id: 24);
+ testarrayofsortedstruct:[Ability] (id: 29);
+ enemy:MyGame.Example.Monster (id:12); // Test referring by full namespace.
+ // id 7 resever for Any_type
+ test:Any (id: 8);
+ test4:[Test] (id: 9);
+ test5:[Test] (id: 31);
+ testnestedflatbuffer:[ubyte] (id:13, nested_flatbuffer: "Monster");
+ testempty:Stat (id:14);
+ testbool:bool = 1 (id:15);
+ testhashs32_fnv1:int (id:16, hash:"fnv1_32");
+ testhashu32_fnv1:uint (id:17, hash:"fnv1_32");
+ testhashs64_fnv1:long (id:18, hash:"fnv1_64");
+ testhashu64_fnv1:ulong (id:19, hash:"fnv1_64");
+ testhashs32_fnv1a:int (id:20, hash:"fnv1a_32");
+ testhashu32_fnv1a:uint (id:21, hash:"fnv1a_32", cpp_type:"Stat");
+ testhashs64_fnv1a:long (id:22, hash:"fnv1a_64");
+ testhashu64_fnv1a:ulong (id:23, hash:"fnv1a_64");
+
+ // Googles flatc uses Pi as default be we don't because it
+ // messes up JSON tests because the numeric print format is
+ // configuration dependent.
+ //testf:float = 3.14159 (id:25);
+ testf:float = 3.14159e5 (id:25);
+ testf2:float = 3 (id:26);
+ testf3:float (id:27);
+ flex:[ubyte] (id:30, flexbuffer);
+ vector_of_longs:[long] (id:32);
+ vector_of_doubles:[double] (id:33);
+ parent_namespace_test:InParentNamespace (id:34);
+ testbase64:TestBase64 (id:35);
+}
+
+table TypeAliases {
+ i8:int8;
+ u8:uint8;
+ i16:int16;
+ u16:uint16;
+ i32:int32;
+ u32:uint32;
+ i64:int64;
+ u64:uint64;
+ f32:float32;
+ f64:float64;
+ v8:[int8];
+ vf64:[float64];
+}
+
+rpc_service MonsterStorage {
+ Store(Monster):Stat (streaming: "none");
+ Retrieve(Stat):Monster (streaming: "server", idempotent);
+}
+
+// Demonstrates the ability to have vectors of unions, and also to
+// store structs and strings in unions.
+
+namespace Fantasy;
+
+table Attacker {
+ sword_attack_damage: int;
+}
+
+struct Rapunzel {
+ hair_length: uint16 (key);
+ travel_points: int (deprecated);
+}
+
+struct BookReader {
+ books_read: int;
+}
+
+union Character {
+ MuLan: Attacker = 2, // Can have name be different from type.
+ Rapunzel = 8, // Or just both the same, as before.
+ Belle: Fantasy.BookReader,
+ BookFan: BookReader,
+ Other: string,
+ Unused: string = 255
+}
+
+table Movie {
+ main_character: Character;
+ antagonist: Character;
+ side_kick: Character;
+ cameo: Character;
+ characters: [Character];
+}
+
+root_type MyGame.Example.Monster;
+
+file_identifier "MONS";
+file_extension "mon";
+
+// Out of order enums
+
+enum ReorderedEnum: int { rx = 10, ry = 1, rz = 9 }
+
+enum ReorderedColor:byte (bit_flags) { RBlue = 3, RRed = 0, RGreen }
+
diff --git a/flatcc/test/monster_test_concat/CMakeLists.txt b/flatcc/test/monster_test_concat/CMakeLists.txt
new file mode 100644
index 0000000..dab13f5
--- /dev/null
+++ b/flatcc/test/monster_test_concat/CMakeLists.txt
@@ -0,0 +1,21 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/test/monster_test")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_monster_test_concat ALL)
+add_custom_command (
+ TARGET gen_monster_test_concat
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ # We could also use the recursive -r option, but this tests adding files manually to the output file.
+ COMMAND flatcc_cli -cwv --reader -o "${GEN_DIR}" "--outfile=monster_test.h" "${FBS_DIR}/attributes.fbs" "${FBS_DIR}/include_test2.fbs" "${FBS_DIR}/include_test1.fbs" "${FBS_DIR}/monster_test.fbs" DEPENDS flatcc_cli "${FBS_DIR}/monster_test.fbs" "${FBS_DIR}/include_test1.fbs" "${FBS_DIR}/include_test2.fbs" "${FBS_DIR}/attributes.fbs"
+)
+include_directories("${GEN_DIR}" "${INC_DIR}")
+add_executable(monster_test_concat monster_test_concat.c)
+add_dependencies(monster_test_concat gen_monster_test_concat)
+target_link_libraries(monster_test_concat flatccrt)
+
+add_test(monster_test_concat monster_test_concat${CMAKE_EXECUTABLE_SUFFIX})
diff --git a/flatcc/test/monster_test_concat/README.txt b/flatcc/test/monster_test_concat/README.txt
new file mode 100644
index 0000000..4924660
--- /dev/null
+++ b/flatcc/test/monster_test_concat/README.txt
@@ -0,0 +1,2 @@
+This test is identical to monster_test_solo, except for directing output
+to a file directly with --outfile.
diff --git a/flatcc/test/monster_test_concat/monster_test_concat.c b/flatcc/test/monster_test_concat/monster_test_concat.c
new file mode 100644
index 0000000..3005580
--- /dev/null
+++ b/flatcc/test/monster_test_concat/monster_test_concat.c
@@ -0,0 +1,24 @@
+/* Minimal test with all headers generated into a single file. */
+#include "monster_test.h"
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ void *buf;
+ size_t size;
+ flatcc_builder_t builder, *B;
+
+ (void)argc;
+ (void)argv;
+
+ B = &builder;
+ flatcc_builder_init(B);
+
+ MyGame_Example_Monster_start_as_root(B);
+ MyGame_Example_Monster_name_create_str(B, "MyMonster");
+ MyGame_Example_Monster_end_as_root(B);
+ buf = flatcc_builder_get_direct_buffer(B, &size);
+ ret = MyGame_Example_Monster_verify_as_root(buf, size);
+ flatcc_builder_clear(B);
+ return ret;
+}
diff --git a/flatcc/test/monster_test_cpp/CMakeLists.txt b/flatcc/test/monster_test_cpp/CMakeLists.txt
new file mode 100644
index 0000000..dd576ca
--- /dev/null
+++ b/flatcc/test/monster_test_cpp/CMakeLists.txt
@@ -0,0 +1,24 @@
+include(CTest)
+
+# Note: This re-uses the samples/monster fbs and .c file.
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+# We use our own separate gen dir so we don't clash with the real monster sample.
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated/monster_test_cpp")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/samples/monster")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_monster_test_cpp ALL)
+add_custom_command (
+ TARGET gen_monster_test_cpp
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND flatcc_cli -a -o "${GEN_DIR}" "${FBS_DIR}/monster.fbs"
+ DEPENDS flatcc_cli "${FBS_DIR}/monster.fbs"
+)
+
+add_executable(monster_test_cpp monster_test.cpp)
+add_dependencies(monster_test_cpp gen_monster_test_cpp)
+target_link_libraries(monster_test_cpp flatccrt)
+
+add_test(monster_test_cpp monster_test_cpp${CMAKE_EXECUTABLE_SUFFIX})
diff --git a/flatcc/test/monster_test_cpp/monster_test.cpp b/flatcc/test/monster_test_cpp/monster_test.cpp
new file mode 100644
index 0000000..9a7477a
--- /dev/null
+++ b/flatcc/test/monster_test_cpp/monster_test.cpp
@@ -0,0 +1,3 @@
+extern "C" {
+#include "../../samples/monster/monster.c"
+}
diff --git a/flatcc/test/monster_test_prefix/CMakeLists.txt b/flatcc/test/monster_test_prefix/CMakeLists.txt
new file mode 100644
index 0000000..1e26761
--- /dev/null
+++ b/flatcc/test/monster_test_prefix/CMakeLists.txt
@@ -0,0 +1,20 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/test/monster_test")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_monster_test_prefix ALL)
+add_custom_command (
+ TARGET gen_monster_test_prefix
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND flatcc_cli -a --prefix=zzz_ --stdout "${FBS_DIR}/monster_test.fbs" > "${GEN_DIR}/zzz_monster_test.h"
+ DEPENDS flatcc_cli "${FBS_DIR}/monster_test.fbs" "${FBS_DIR}/include_test1.fbs" "${FBS_DIR}/include_test2.fbs"
+)
+add_executable(monster_test_prefix monster_test_prefix.c)
+add_dependencies(monster_test_prefix gen_monster_test_prefix)
+target_link_libraries(monster_test_prefix flatccrt)
+
+add_test(monster_test_prefix monster_test_prefix${CMAKE_EXECUTABLE_SUFFIX})
diff --git a/flatcc/test/monster_test_prefix/monster_test_prefix.c b/flatcc/test/monster_test_prefix/monster_test_prefix.c
new file mode 100644
index 0000000..bb070b2
--- /dev/null
+++ b/flatcc/test/monster_test_prefix/monster_test_prefix.c
@@ -0,0 +1,24 @@
+/* Minimal test with all headers generated into a single file. */
+#include "zzz_monster_test.h"
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ void *buf;
+ size_t size;
+ flatcc_builder_t builder, *B;
+
+ (void)argc;
+ (void)argv;
+
+ B = &builder;
+ flatcc_builder_init(B);
+
+ zzz_MyGame_Example_Monster_start_as_root(B);
+ zzz_MyGame_Example_Monster_name_create_str(B, "MyMonster");
+ zzz_MyGame_Example_Monster_end_as_root(B);
+ buf = flatcc_builder_get_direct_buffer(B, &size);
+ ret = zzz_MyGame_Example_Monster_verify_as_root_with_identifier(buf, size, zzz_MyGame_Example_Monster_file_identifier);
+ flatcc_builder_clear(B);
+ return ret;
+}
diff --git a/flatcc/test/monster_test_solo/CMakeLists.txt b/flatcc/test/monster_test_solo/CMakeLists.txt
new file mode 100644
index 0000000..a974434
--- /dev/null
+++ b/flatcc/test/monster_test_solo/CMakeLists.txt
@@ -0,0 +1,21 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/test/monster_test")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_monster_test_solo ALL)
+add_custom_command (
+ TARGET gen_monster_test_solo
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND flatcc_cli -cwv --reader --stdout "${FBS_DIR}/attributes.fbs" "${FBS_DIR}/include_test2.fbs" "${FBS_DIR}/include_test1.fbs" "${FBS_DIR}/monster_test.fbs" > "${GEN_DIR}/monster_test.h" DEPENDS flatcc_cli "${FBS_DIR}/monster_test.fbs" "${FBS_DIR}/include_test1.fbs" "${FBS_DIR}/include_test2.fbs" "${FBS_DIR}/attributes.fbs"
+)
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+add_executable(monster_test_solo monster_test_solo.c)
+add_dependencies(monster_test_solo gen_monster_test_solo)
+target_link_libraries(monster_test_solo flatccrt)
+
+add_test(monster_test_solo monster_test_solo${CMAKE_EXECUTABLE_SUFFIX})
diff --git a/flatcc/test/monster_test_solo/monster_test_solo.c b/flatcc/test/monster_test_solo/monster_test_solo.c
new file mode 100644
index 0000000..3005580
--- /dev/null
+++ b/flatcc/test/monster_test_solo/monster_test_solo.c
@@ -0,0 +1,24 @@
+/* Minimal test with all headers generated into a single file. */
+#include "monster_test.h"
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ void *buf;
+ size_t size;
+ flatcc_builder_t builder, *B;
+
+ (void)argc;
+ (void)argv;
+
+ B = &builder;
+ flatcc_builder_init(B);
+
+ MyGame_Example_Monster_start_as_root(B);
+ MyGame_Example_Monster_name_create_str(B, "MyMonster");
+ MyGame_Example_Monster_end_as_root(B);
+ buf = flatcc_builder_get_direct_buffer(B, &size);
+ ret = MyGame_Example_Monster_verify_as_root(buf, size);
+ flatcc_builder_clear(B);
+ return ret;
+}
diff --git a/flatcc/test/optional_scalars_test/CMakeLists.txt b/flatcc/test/optional_scalars_test/CMakeLists.txt
new file mode 100644
index 0000000..b13c1b9
--- /dev/null
+++ b/flatcc/test/optional_scalars_test/CMakeLists.txt
@@ -0,0 +1,19 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/test/optional_scalars_test")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_optional_scalars_test ALL)
+add_custom_command (
+ TARGET gen_optional_scalars_test
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND flatcc_cli -a --json -o "${GEN_DIR}" "${FBS_DIR}/optional_scalars_test.fbs"
+)
+add_executable(optional_scalars_test optional_scalars_test.c)
+add_dependencies(optional_scalars_test gen_optional_scalars_test)
+target_link_libraries(optional_scalars_test flatccrt)
+
+add_test(optional_scalars_test optional_scalars_test${CMAKE_EXECUTABLE_SUFFIX})
diff --git a/flatcc/test/optional_scalars_test/optional_scalars_test.c b/flatcc/test/optional_scalars_test/optional_scalars_test.c
new file mode 100644
index 0000000..7566c05
--- /dev/null
+++ b/flatcc/test/optional_scalars_test/optional_scalars_test.c
@@ -0,0 +1,280 @@
+#include <assert.h>
+#include <stdio.h>
+
+#include "optional_scalars_test_builder.h"
+#include "optional_scalars_test_json_printer.h"
+#include "optional_scalars_test_json_parser.h"
+
+
+#undef ns
+#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(optional_scalars, x)
+
+// #define TEST_ASSERT
+
+#ifdef TEST_ASSERT
+#define test_assert(x) do { if (!(x)) { assert(0); return -1; }} while(0)
+#else
+#define test_assert(x) do { if (!(x)) { return -1; }} while(0)
+#endif
+
+int create_scalar_stuff(flatcc_builder_t *builder)
+{
+ ns(ScalarStuff_start_as_root(builder));
+
+ ns(ScalarStuff_just_i8_add(builder, 10));
+ ns(ScalarStuff_maybe_i8_add(builder, 11));
+ ns(ScalarStuff_default_i8_add(builder, 12));
+
+ ns(ScalarStuff_just_i16_add(builder, 42));
+ ns(ScalarStuff_maybe_i16_add(builder, 42));
+ ns(ScalarStuff_default_i16_add(builder, 42));
+
+ ns(ScalarStuff_just_u32_add(builder, 0));
+ ns(ScalarStuff_maybe_u32_add(builder, 0));
+ ns(ScalarStuff_default_u32_add(builder, 0));
+
+ ns(ScalarStuff_just_f32_add(builder, 42));
+ ns(ScalarStuff_maybe_f32_add(builder, 42));
+ ns(ScalarStuff_default_f32_add(builder, 42));
+
+ ns(ScalarStuff_just_bool_add(builder, 1));
+ ns(ScalarStuff_maybe_bool_add(builder, 1));
+ ns(ScalarStuff_default_bool_add(builder, 1));
+
+ ns(ScalarStuff_just_enum_add)(builder, ns(OptionalByte_One));
+ ns(ScalarStuff_maybe_enum_add)(builder, ns(OptionalByte_One));
+ ns(ScalarStuff_default_enum_add)(builder, ns(OptionalByte_One));
+
+ ns(ScalarStuff_just_xfactor_add)(builder, ns(OptionalFactor_Twice));
+ ns(ScalarStuff_maybe_xfactor_add)(builder, ns(OptionalFactor_Twice));
+ ns(ScalarStuff_default_xfactor_add)(builder, ns(OptionalFactor_Twice));
+
+ ns(ScalarStuff_end_as_root(builder));
+
+ return 0;
+}
+
+int access_scalar_stuff(const void *buf)
+{
+ ns(ScalarStuff_table_t) t = ns(ScalarStuff_as_root(buf));
+ flatbuffers_int8_option_t maybe_i8;
+ flatbuffers_int16_option_t maybe_i16;
+ flatbuffers_uint32_option_t maybe_u32;
+ flatbuffers_uint8_option_t maybe_u8;
+ flatbuffers_float_option_t maybe_f32;
+ flatbuffers_bool_option_t maybe_bool;
+ ns(OptionalByte_option_t) maybe_enum;
+ ns(OptionalFactor_option_t) maybe_xfactor;
+ ns(OptionalFactor_option_t) maybe_yfactor;
+
+ test_assert(10 == ns(ScalarStuff_just_i8_get(t)));
+ test_assert(11 == ns(ScalarStuff_maybe_i8_get(t)));
+ test_assert(12 == ns(ScalarStuff_default_i8_get(t)));
+ maybe_i8 = ns(ScalarStuff_maybe_i8_option(t));
+ test_assert(!maybe_i8.is_null);
+ test_assert(11 == maybe_i8.value);
+ test_assert(ns(ScalarStuff_just_i8_is_present(t)));
+ test_assert(ns(ScalarStuff_maybe_i8_is_present(t)));
+ test_assert(ns(ScalarStuff_default_i8_is_present(t)));
+
+ test_assert(0 == ns(ScalarStuff_just_u8_get(t)));
+ test_assert(0 == ns(ScalarStuff_maybe_u8_get(t)));
+ test_assert(42 == ns(ScalarStuff_default_u8_get(t)));
+ maybe_u8 = ns(ScalarStuff_maybe_u8_option(t));
+ test_assert(maybe_u8.is_null);
+ test_assert(0 == maybe_u8.value);
+ test_assert(!ns(ScalarStuff_just_u8_is_present(t)));
+ test_assert(!ns(ScalarStuff_maybe_u8_is_present(t)));
+ test_assert(!ns(ScalarStuff_default_u8_is_present(t)));
+
+ test_assert(42 == ns(ScalarStuff_just_i16_get(t)));
+ test_assert(42 == ns(ScalarStuff_maybe_i16_get(t)));
+ test_assert(42 == ns(ScalarStuff_default_i16_get(t)));
+ maybe_i16 = ns(ScalarStuff_maybe_i16_option(t));
+ test_assert(!maybe_i16.is_null);
+ test_assert(42 == maybe_i16.value);
+ test_assert(ns(ScalarStuff_just_i16_is_present(t)));
+ test_assert(ns(ScalarStuff_maybe_i16_is_present(t)));
+ test_assert(!ns(ScalarStuff_default_i16_is_present(t)));
+
+ test_assert(0 == ns(ScalarStuff_just_u32_get(t)));
+ test_assert(0 == ns(ScalarStuff_maybe_u32_get(t)));
+ test_assert(0 == ns(ScalarStuff_default_u32_get(t)));
+ maybe_u32 = ns(ScalarStuff_maybe_u32_option(t));
+ test_assert(!maybe_u32.is_null);
+ test_assert(0 == maybe_u32.value);
+ test_assert(!ns(ScalarStuff_just_u32_is_present(t)));
+ test_assert(ns(ScalarStuff_maybe_u32_is_present(t)));
+ test_assert(ns(ScalarStuff_default_u32_is_present(t)));
+
+ test_assert(42 == ns(ScalarStuff_just_f32_get(t)));
+ test_assert(42 == ns(ScalarStuff_maybe_f32_get(t)));
+ test_assert(42 == ns(ScalarStuff_default_f32_get(t)));
+ maybe_f32 = ns(ScalarStuff_maybe_f32_option(t));
+ test_assert(!maybe_f32.is_null);
+ test_assert(42 == maybe_f32.value);
+ test_assert(ns(ScalarStuff_just_f32_is_present(t)));
+ test_assert(ns(ScalarStuff_maybe_f32_is_present(t)));
+ test_assert(!ns(ScalarStuff_default_f32_is_present(t)));
+
+ test_assert(1 == ns(ScalarStuff_just_bool_get(t)));
+ test_assert(1 == ns(ScalarStuff_maybe_bool_get(t)));
+ test_assert(1 == ns(ScalarStuff_default_bool_get(t)));
+ maybe_bool = ns(ScalarStuff_maybe_bool_option(t));
+ test_assert(!maybe_bool.is_null);
+ test_assert(1 == maybe_bool.value);
+ test_assert(ns(ScalarStuff_just_bool_is_present(t)));
+ test_assert(ns(ScalarStuff_maybe_bool_is_present(t)));
+ test_assert(!ns(ScalarStuff_default_bool_is_present(t)));
+
+ test_assert(1 == ns(ScalarStuff_just_enum_get(t)));
+ test_assert(1 == ns(ScalarStuff_maybe_enum_get(t)));
+ test_assert(1 == ns(ScalarStuff_default_enum_get(t)));
+ maybe_enum = ns(ScalarStuff_maybe_enum_option(t));
+ test_assert(!maybe_enum.is_null);
+ test_assert(maybe_enum.value == 1);
+ test_assert(ns(ScalarStuff_just_enum_is_present(t)));
+ test_assert(ns(ScalarStuff_maybe_enum_is_present(t)));
+ test_assert(!ns(ScalarStuff_default_enum_is_present(t)));
+
+ test_assert(2 == ns(ScalarStuff_just_xfactor_get(t)));
+ test_assert(2 == ns(ScalarStuff_maybe_xfactor_get(t)));
+ test_assert(2 == ns(ScalarStuff_default_xfactor_get(t)));
+ maybe_xfactor = ns(ScalarStuff_maybe_xfactor_option(t));
+ test_assert(!maybe_xfactor.is_null);
+ test_assert(maybe_xfactor.value == 2);
+ test_assert(ns(ScalarStuff_just_xfactor_is_present(t)));
+ test_assert(ns(ScalarStuff_maybe_xfactor_is_present(t)));
+ test_assert(!ns(ScalarStuff_default_xfactor_is_present(t)));
+
+ test_assert(1 == ns(ScalarStuff_just_yfactor_get(t)));
+ test_assert(0 == ns(ScalarStuff_maybe_yfactor_get(t)));
+ test_assert(2 == ns(ScalarStuff_default_yfactor_get(t)));
+ maybe_yfactor = ns(ScalarStuff_maybe_yfactor_option(t));
+ test_assert(maybe_yfactor.is_null);
+ test_assert(maybe_yfactor.value == 0);
+ test_assert(!ns(ScalarStuff_just_yfactor_is_present(t)));
+ test_assert(!ns(ScalarStuff_maybe_yfactor_is_present(t)));
+ test_assert(!ns(ScalarStuff_default_yfactor_is_present(t)));
+ return 0;
+}
+
+int test(void)
+{
+ flatcc_builder_t builder;
+ void *buf;
+ size_t size;
+
+ flatcc_builder_init(&builder);
+ test_assert(0 == create_scalar_stuff(&builder));
+ buf = flatcc_builder_finalize_aligned_buffer(&builder, &size);
+ test_assert(0 == access_scalar_stuff(buf));
+ flatcc_builder_aligned_free(buf);
+ flatcc_builder_clear(&builder);
+
+ return 0;
+}
+
+const char *expected_json =
+"{\"just_i8\":10,\"maybe_i8\":11,\"default_i8\":12,\"just_i16\":42,\"maybe_i16\":42,\"maybe_u32\":0,\"default_u32\":0,\"just_f32\":42,\"maybe_f32\":42,\"just_bool\":true,\"maybe_bool\":true,\"just_enum\":\"One\",\"maybe_enum\":\"One\",\"just_xfactor\":\"Twice\",\"maybe_xfactor\":\"Twice\"}";
+
+#if 0
+int print_buffer(const void *buf, size_t size)
+{
+ flatcc_json_printer_t printer;
+ flatcc_json_printer_init(&printer, 0);
+ ns(ScalarStuff_print_json_as_root)(&printer, buf, size, NULL);
+ if (flatcc_json_printer_get_error(&printer)) {
+ printf("could not print buffer\n");
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+int test_json_printer(void)
+{
+ flatcc_builder_t builder;
+ void *buf;
+ size_t size;
+ flatcc_json_printer_t printer;
+ char *json_buf;
+ size_t json_size;
+
+ flatcc_builder_init(&builder);
+ test_assert(0 == create_scalar_stuff(&builder));
+ buf = flatcc_builder_finalize_aligned_buffer(&builder, &size);
+ test_assert(0 == access_scalar_stuff(buf));
+ flatcc_builder_clear(&builder);
+ flatcc_json_printer_init_dynamic_buffer(&printer, 0);
+ test_assert(ns(ScalarStuff_print_json_as_root)(&printer, buf, size, NULL));
+ flatcc_builder_aligned_free(buf);
+ json_buf = flatcc_json_printer_get_buffer(&printer, &json_size);
+ printf("%.*s\n", (int)json_size, json_buf);
+ test_assert(strlen(expected_json) == json_size);
+ test_assert(0 == memcmp(expected_json, json_buf, json_size));
+
+
+ flatcc_json_printer_clear(&printer);
+ return 0;
+}
+
+int test_json_parser(void)
+{
+ flatcc_builder_t builder;
+ void *buf;
+ size_t size;
+ flatcc_json_parser_t parser;
+ flatcc_json_printer_t printer;
+ char *json_buf;
+ size_t json_size;
+ int ret;
+
+ flatcc_builder_init(&builder);
+ ret = optional_scalars_ScalarStuff_parse_json_as_root(&builder,
+ &parser, expected_json, strlen(expected_json), 0, 0);
+ test_assert(ret == 0);
+
+ buf = flatcc_builder_finalize_aligned_buffer(&builder, &size);
+
+ flatcc_json_printer_init_dynamic_buffer(&printer, 0);
+ ns(ScalarStuff_print_json_as_root)(&printer, buf, size, NULL);
+ if (flatcc_json_printer_get_error(&printer)) {
+ printf("could not print buffer\n");
+ return -1;
+ }
+ test_assert(0 == access_scalar_stuff(buf));
+
+ json_buf = flatcc_json_printer_get_buffer(&printer, &json_size);
+ printf("%.*s\n", (int)json_size, json_buf);
+ test_assert(strlen(expected_json) == json_size);
+ test_assert(0 == memcmp(expected_json, json_buf, json_size));
+ flatcc_json_printer_clear(&printer);
+
+ flatcc_builder_aligned_free(buf);
+ flatcc_builder_clear(&builder);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ /* Silence warnings. */
+ (void)argc;
+ (void)argv;
+
+ if (test()) {
+ printf("optional scalars test failed");
+ return 1;
+ }
+ if (test_json_printer()) {
+ printf("optional scalars json printer test failed");
+ return 1;
+ }
+ if (test_json_parser()) {
+ printf("optional scalars json parser test failed");
+ return 1;
+ }
+ printf("optional scalars test passed");
+ return 0;
+}
+
diff --git a/flatcc/test/optional_scalars_test/optional_scalars_test.fbs b/flatcc/test/optional_scalars_test/optional_scalars_test.fbs
new file mode 100644
index 0000000..ba4c9d4
--- /dev/null
+++ b/flatcc/test/optional_scalars_test/optional_scalars_test.fbs
@@ -0,0 +1,71 @@
+namespace optional_scalars;
+
+enum OptionalByte: byte {
+ None = 0,
+ One = 1,
+}
+
+// Enums without a 0 element normally requires an initializer
+// which is a problem when = null is the default. In this case
+// the default value is forced to 0 when a reader insists on
+// getting a numerical value instead of null.
+enum OptionalFactor: byte {
+ Once = 1,
+ Twice = 2,
+}
+
+// This table tests optional scalars in tables. It should be integrated with
+// the main monster test once most languages support optional scalars.
+table ScalarStuff {
+ just_i8: int8;
+ maybe_i8: int8 = null;
+ default_i8: int8 = 42;
+ just_u8: uint8;
+ maybe_u8: uint8 = null;
+ default_u8: uint8 = 42;
+
+ just_i16: int16;
+ maybe_i16: int16 = null;
+ default_i16: int16 = 42;
+ just_u16: uint16;
+ maybe_u16: uint16 = null;
+ default_u16: uint16 = 42;
+
+ just_i32: int32;
+ maybe_i32: int32 = null;
+ default_i32: int32 = 42;
+ just_u32: uint32;
+ maybe_u32: uint32 = null;
+ default_u32: uint32 = 42;
+
+ just_i64: int64;
+ maybe_i64: int64 = null;
+ default_i64: int64 = 42;
+ just_u64: uint64;
+ maybe_u64: uint64 = null;
+ default_u64: uint64 = 42;
+
+ just_f32: float32;
+ maybe_f32: float32 = null;
+ default_f32: float32 = 42;
+ just_f64: float64;
+ maybe_f64: float64 = null;
+ default_f64: float64 = 42;
+
+ just_bool: bool;
+ maybe_bool: bool = null;
+ default_bool: bool = true;
+
+ just_enum: OptionalByte;
+ maybe_enum: OptionalByte = null;
+ default_enum: OptionalByte = One;
+
+ just_xfactor: OptionalFactor = Once;
+ maybe_xfactor: OptionalFactor = null;
+ default_xfactor: OptionalFactor = Twice;
+
+ just_yfactor: OptionalFactor = Once;
+ maybe_yfactor: OptionalFactor = null;
+ default_yfactor: OptionalFactor = Twice;
+
+}
diff --git a/flatcc/test/reflection_test/CMakeLists.txt b/flatcc/test/reflection_test/CMakeLists.txt
new file mode 100644
index 0000000..f82d1f5
--- /dev/null
+++ b/flatcc/test/reflection_test/CMakeLists.txt
@@ -0,0 +1,20 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/test/monster_test")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_reflection_test ALL)
+add_custom_command (
+ TARGET gen_reflection_test
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND flatcc_cli --schema -o "${GEN_DIR}" "${FBS_DIR}/monster_test.fbs"
+ DEPENDS flatcc_cli "${FBS_DIR}/monster_test.fbs" "${FBS_DIR}/include_test1.fbs" "${FBS_DIR}/include_test2.fbs"
+)
+add_executable(reflection_test reflection_test.c)
+add_dependencies(reflection_test gen_reflection_test)
+target_link_libraries(reflection_test flatccrt)
+
+add_test(reflection_test reflection_test${CMAKE_EXECUTABLE_SUFFIX})
diff --git a/flatcc/test/reflection_test/reflection_test.c b/flatcc/test/reflection_test/reflection_test.c
new file mode 100644
index 0000000..eef0bd1
--- /dev/null
+++ b/flatcc/test/reflection_test/reflection_test.c
@@ -0,0 +1,196 @@
+#include "flatcc/support/readfile.h"
+#include "flatcc/reflection/reflection_reader.h"
+#include "flatcc/portable/pcrt.h"
+
+/* -DFLATCC_PORTABLE may help if inttypes.h is missing. */
+#ifndef PRId64
+#include <inttypes.h>
+#endif
+
+
+/* This is not an exhaustive test. */
+int test_schema(const char *monster_bfbs)
+{
+ void *buffer;
+ size_t size;
+ int ret = -1;
+ reflection_Schema_table_t S;
+ reflection_Object_vec_t Objs;
+ reflection_Object_table_t Obj;
+ reflection_Field_vec_t Flds;
+ reflection_Field_table_t F;
+ reflection_Type_table_t T;
+ size_t k, monster_index;
+ reflection_Service_vec_t Svcs;
+ reflection_Service_table_t Svc;
+ reflection_RPCCall_vec_t Calls;
+ reflection_RPCCall_table_t Call;
+ size_t call_index;
+ const char *strval;
+
+ buffer = readfile(monster_bfbs, 100000, &size);
+ if (!buffer) {
+ printf("failed to load binary schema\n");
+ goto done;
+ }
+ S = reflection_Schema_as_root(buffer);
+ Objs = reflection_Schema_objects(S);
+ for (k = 0; k < reflection_Object_vec_len(Objs); ++k) {
+ printf("dbg: obj #%d : %s\n", (int)k,
+ reflection_Object_name(reflection_Object_vec_at(Objs, k)));
+ }
+ k = reflection_Object_vec_find(Objs, "MyGame.Example.Monster");
+ if (k == flatbuffers_not_found) {
+ printf("Could not find monster in schema\n");
+ goto done;
+ }
+ monster_index = k;
+ Obj = reflection_Object_vec_at(Objs, k);
+ if (strcmp(reflection_Object_name(Obj), "MyGame.Example.Monster")) {
+ printf("Found wrong object in schema\n");
+ goto done;
+ }
+ Flds = reflection_Object_fields(Obj);
+ k = reflection_Field_vec_find(Flds, "mana");
+ if (k == flatbuffers_not_found) {
+ printf("Did not find mana field in Monster schema\n");
+ goto done;
+ }
+ F = reflection_Field_vec_at(Flds, k);
+ if (reflection_Field_default_integer(F) != 150) {
+ printf("mana field has wrong default value\n");
+ printf("field name: %s\n", reflection_Field_name(F));
+ printf("%"PRId64"\n", (int64_t)reflection_Field_default_integer(F));
+ goto done;
+ }
+ T = reflection_Field_type(F);
+ if (reflection_Type_base_type(T) != reflection_BaseType_Short) {
+ printf("mana field has wrong type\n");
+ goto done;
+ }
+ if (reflection_Field_optional(F)) {
+ printf("mana field is not optional\n");
+ goto done;
+ }
+ k = reflection_Field_vec_find(Flds, "enemy");
+ if (k == flatbuffers_not_found) {
+ printf("enemy field not found\n");
+ goto done;
+ }
+ T = reflection_Field_type(reflection_Field_vec_at(Flds, k));
+ if (reflection_Type_base_type(T) != reflection_BaseType_Obj) {
+ printf("enemy is not an object\n");
+ goto done;
+ }
+ if (reflection_Type_index(T) != (int32_t)monster_index) {
+ printf("enemy is not a monster\n");
+ goto done;
+ }
+ k = reflection_Field_vec_find(Flds, "testarrayoftables");
+ if (k == flatbuffers_not_found) {
+ printf("array of tables not found\n");
+ goto done;
+ }
+ T = reflection_Field_type(reflection_Field_vec_at(Flds, k));
+ if (reflection_Type_base_type(T) != reflection_BaseType_Vector) {
+ printf("array of tables is not of vector type\n");
+ goto done;
+ }
+ if (reflection_Type_element(T) != reflection_BaseType_Obj) {
+ printf("array of tables is not a vector of table type\n");
+ goto done;
+ }
+ if (reflection_Type_index(T) != (int32_t)monster_index) {
+ printf("array of tables is not a monster vector\n");
+ goto done;
+ }
+ /* list services and calls */
+ Svcs = reflection_Schema_services(S);
+ for (k = 0; k < reflection_Service_vec_len(Svcs); ++k) {
+ Svc = reflection_Service_vec_at(Svcs, k);
+ printf("dbg: svc #%d : %s\n", (int)k,
+ reflection_Service_name(Svc));
+ Calls = reflection_Service_calls(Svc);
+ for (call_index = 0 ;
+ call_index < reflection_RPCCall_vec_len(Calls) ;
+ call_index++) {
+ Call = reflection_RPCCall_vec_at(Calls, call_index);
+ printf("dbg: call %d : %s\n", (int)call_index,
+ reflection_RPCCall_name(Call));
+ }
+ }
+ /* Within service MyGame.Example.MonsterStorage ... */
+ k = reflection_Service_vec_find(Svcs, "MyGame.Example.MonsterStorage");
+ if (k == flatbuffers_not_found) {
+ printf("Could not find MonsterStorage service in schema\n");
+ goto done;
+ }
+ Svc = reflection_Service_vec_at(Svcs, k);
+ /* ... search the RPC call Store */
+ Calls = reflection_Service_calls(Svc);
+ k = reflection_RPCCall_vec_find(Calls, "Store");
+ if (k == flatbuffers_not_found) {
+ printf("Could not find call Store in service\n");
+ goto done;
+ }
+ Call = reflection_RPCCall_vec_at(Calls, k);
+ /* Ensure request type is MyGame.Example.Monster */
+ Obj = reflection_Object_vec_at(Objs, monster_index);
+ if (Obj != reflection_RPCCall_request(Call)) {
+ printf("Wrong request type of rpc call\n");
+ goto done;
+ }
+ /* Ensure response type is MyGame.Example.Stat */
+ k = reflection_Object_vec_find(Objs, "MyGame.Example.Stat");
+ if (k == flatbuffers_not_found) {
+ printf("Could not find Stat in schema\n");
+ goto done;
+ }
+ Obj = reflection_Object_vec_at(Objs, k);
+ if (Obj != reflection_RPCCall_response(Call)) {
+ printf("Wrong response type of rpc call\n");
+ goto done;
+ }
+ /* check the call has an attribute "streaming" */
+ k = reflection_KeyValue_vec_scan(reflection_RPCCall_attributes(Call), "streaming");
+ if (k == flatbuffers_not_found) {
+ printf("Could not find attribute in call\n");
+ goto done;
+ }
+ /* check the attribute value is "none" */
+ strval = reflection_KeyValue_value(
+ reflection_KeyValue_vec_at(reflection_RPCCall_attributes(Call), k));
+ if (!strval || 0 != strcmp("none", strval)) {
+ printf("Wrong attribute value in call\n");
+ goto done;
+ }
+ ret = 0;
+done:
+ if (buffer) {
+ free(buffer);
+ }
+ return ret;
+}
+
+/* We take arguments so test can run without copying sources. */
+#define usage \
+"wrong number of arguments:\n" \
+"usage: <program> [<output-filename>]\n"
+
+const char *filename = "generated/monster_test.bfbs";
+
+int main(int argc, char *argv[])
+{
+ /* Avoid assert dialogs on Windows. */
+ init_headless_crt();
+
+ if (argc != 1 && argc != 2) {
+ fprintf(stderr, usage);
+ exit(1);
+ }
+ if (argc == 2) {
+ filename = argv[1];
+ }
+
+ return test_schema(filename);
+}
diff --git a/flatcc/test/reflection_test/reflection_test.sh b/flatcc/test/reflection_test/reflection_test.sh
new file mode 100755
index 0000000..f1ad69c
--- /dev/null
+++ b/flatcc/test/reflection_test/reflection_test.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../..
+ROOT=`pwd`
+TMP=${ROOT}/build/tmp/test/reflection_test
+
+CC=${CC:-cc}
+${ROOT}/scripts/build.sh
+mkdir -p ${TMP}/generated
+rm -rf ${TMP}/generated/*
+bin/flatcc --schema -o ${TMP}/generated test/monster_test/monster_test.fbs
+
+cp test/reflection_test/*.c ${TMP}
+cd ${TMP}
+
+$CC -g -I ${ROOT}/include reflection_test.c \
+ ${ROOT}/lib/libflatccrt.a -o reflection_test_d
+$CC -O3 -DNDEBUG -I ${ROOT}/include reflection_test.c \
+ ${ROOT}/lib/libflatccrt.a -o reflection_test
+echo "running reflection test debug"
+./reflection_test_d
+echo "running reflection test optimized"
+./reflection_test
diff --git a/flatcc/test/test.sh b/flatcc/test/test.sh
new file mode 100755
index 0000000..d4fae6f
--- /dev/null
+++ b/flatcc/test/test.sh
@@ -0,0 +1,103 @@
+#!/usr/bin/env bash
+
+
+echo "This is the old test script replaced by CMake's ctest"
+echo "driven by scritps/test.sh"
+echo "pausing 5 seconds - press ctrl+C to quit"
+
+sleep 5
+
+set -e
+cd `dirname $0`/..
+ROOT=`pwd`
+
+CC=${CC:-cc}
+${ROOT}/scripts/build.sh
+
+TMP=${ROOT}/build/tmp/test
+INC=${ROOT}/include
+
+echo "" 1>&2
+
+mkdir -p ${TMP}
+rm -rf ${TMP}/*
+
+echo "running generation of complex schema (cgen_test)"
+${ROOT}/test/cgen_test/cgen_test.sh
+
+mkdir -p ${TMP}/monster_test
+
+mkdir -p ${TMP}/monster_test_smoke
+mkdir -p ${TMP}/monster_test_solo
+mkdir -p ${TMP}/monster_test_hello
+mkdir -p ${TMP}/monster_test_main
+
+#
+# These first tests are to ensure the generated code can compile,
+# they don't actually run tests against the api.
+#
+echo "generating smoke test generated monster source" 1>&2
+${ROOT}/bin/flatcc -I ${ROOT}/test/monster_test -a \
+ -o ${TMP}/monster_test_smoke ${ROOT}/test/monster_test/monster_test.fbs
+echo "#include \"monster_test_builder.h\"" > ${TMP}/monster_test_smoke/smoke_monster.c
+cd ${TMP}/monster_test_smoke && $CC -c -Wall -O3 -I ${INC} smoke_monster.c
+
+echo "generating smoke test generated monster source to single file" 1>&2
+${ROOT}/bin/flatcc -I ${ROOT}/test/monster_test -a --stdout \
+ ${ROOT}/test/monster_test/monster_test.fbs > ${TMP}/monster_test_solo/solo_monster.c
+cd ${TMP}/monster_test_solo && $CC -c -Wall -O3 -I ${INC} solo_monster.c
+
+echo "generating smoke test generated monster source with --prefix zzz --common-prefix hello" 1>&2
+${ROOT}/bin/flatcc -I ${ROOT}/test/monster_test -a \
+ --common-prefix hello --prefix zzz \
+ -o ${TMP}/monster_test_hello ${ROOT}/test/monster_test/monster_test.fbs
+echo "#include \"monster_test_builder.h\"" > ${TMP}/monster_test_hello/hello_monster.c
+cd ${TMP}/monster_test_hello && $CC -c -Wall -O3 -I ${INC} hello_monster.c
+
+#
+# This test ensures the reader api understands a monster buffer generated
+# by the external `flatc` tool by Google FPL.
+#
+echo "starting compat test"
+${ROOT}/test/flatc_compat/flatc_compat.sh
+
+echo "starting emit_test for altenative emitter backend smoke test"
+${ROOT}/test/emit_test/emit_test.sh
+
+#
+# This is the main `monster_test.c` test that covers nearly all
+# functionality of the reader and builder API for C.
+#
+echo "running main monster test"
+cd ${ROOT}/test/monster_test
+${ROOT}/bin/flatcc -I ${ROOT}/test/monster_test -a \
+ -o ${TMP}/monster_test_main ${ROOT}/test/monster_test/monster_test.fbs
+cd ${TMP}/monster_test_main
+cp ${ROOT}/test/monster_test/monster_test.c .
+$CC -g -I ${ROOT}/include monster_test.c \
+ ${ROOT}/lib/libflatccrt_d.a -o monster_test_d
+$CC -O3 -DNDEBUG -DFLATBUFFERS_BENCHMARK -I ${ROOT}/include monster_test.c \
+ ${ROOT}/lib/libflatccrt.a -o monster_test
+./monster_test_d
+
+echo "running optimized version of main monster test"
+./monster_test
+
+# This may fail if reflection feature is disabled
+echo "running reflection test"
+${ROOT}/test/reflection_test/reflection_test.sh
+
+# This may fail if reflection feature is disabled
+echo "running reflection sample"
+${ROOT}/samples/reflection/build.sh
+
+echo "running monster sample"
+${ROOT}/samples/monster/build.sh
+
+echo "running json test"
+${ROOT}/test/json_test/json_test.sh
+
+echo "running load test with large buffer"
+${ROOT}/test/load_test/load_test.sh
+
+echo "TEST PASSED"
diff --git a/flatcc/test/union_vector_test/union_vector.fbs b/flatcc/test/union_vector_test/union_vector.fbs
new file mode 100644
index 0000000..73b8800
--- /dev/null
+++ b/flatcc/test/union_vector_test/union_vector.fbs
@@ -0,0 +1,26 @@
+table MuLan {
+ sword_attack_damage: int;
+}
+
+table Rapunzel {
+ hair_length: int;
+}
+
+table Belle {
+ books_read: int;
+}
+
+union Character {
+ MuLan,
+ Rapunzel,
+ Belle,
+}
+
+table Movie {
+ characters: [Character];
+ belles: [Belle];
+ character: Character;
+}
+
+root_type Movie;
+file_identifier "MOVI";