Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Default owner
* @aardappel @dbaileychess derekbailey@google.com

# Prevent modification of this file
.github/CODEOWNERS @dbaileychess derekbailey@google.com
2 changes: 0 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,6 @@ if(FLATBUFFERS_BUILD_TESTS)

# Since flatsample has no sources, we have to explicitly set the linker lang.
set_target_properties(flatsample PROPERTIES LINKER_LANGUAGE CXX)

compile_schema_for_samples(samples/monster.fbs "${FLATC_OPT_COMP}")

target_link_libraries(flatsamplebinary PRIVATE $<BUILD_INTERFACE:ProjectConfig> flatsample)
target_link_libraries(flatsampletext PRIVATE $<BUILD_INTERFACE:ProjectConfig> flatsample)
Expand Down
126 changes: 126 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,132 @@
**This is a fork of the Google Flatbuffers Library with the following features added:**

- A `--preserve-case` flag to prevent IDL name mangling
- JSON Schema schema import/export (`--jsonschema`, `*.schema.json`)
- Optional lossless JSON Schema round-tripping via `--jsonschema-xflatbuffers` metadata

## Fork Features

This fork adds a few features to the `flatc` compiler intended to treat JSON Schema as a first-class schema format, while still flowing through the same FlatBuffers schema IR (see [`reflection/reflection.fbs`](reflection/reflection.fbs)).

### Preserve-case naming (`--preserve-case`)

By default, many language generators apply case conversions to schema identifiers (for example converting `snake_case` field names into `camelCase` accessors). The `--preserve-case` flag disables this name mangling for identifiers coming from the schema, and emits names “as written” instead.

Example:

```sh
flatc --cpp --preserve-case schema.fbs
```

Notes:

- This is currently supported for these generators: C++, Go, Java, Rust, Dart, Python, TypeScript, PHP, and JSON Schema (see [`src/flatc.cpp`](src/flatc.cpp)).
- Implementation: [`src/flatc.cpp`](src/flatc.cpp), [`src/util.cpp`](src/util.cpp).
- Tests: [`tests/GoTest.sh`](tests/GoTest.sh), [`tests/PHPTest.sh`](tests/PHPTest.sh), [`tests/PythonTest.sh`](tests/PythonTest.sh), [`tests/JsonSchemaTest.sh`](tests/JsonSchemaTest.sh).

### JSON Schema schema import/export

- Export a FlatBuffers schema (`.fbs`) to JSON Schema (`.schema.json`).
- Import a JSON Schema (`.schema.json`) as a schema input (as if it were an IDL), map it into FlatBuffers’ schema IR, and run the normal FlatBuffers code generators.

Implementation:

- JSON Schema generator: [`src/idl_gen_json_schema.cpp`](src/idl_gen_json_schema.cpp)
- JSON Schema importer/parser: [`src/idl_parser.cpp`](src/idl_parser.cpp) (`Parser::DoParseJsonSchema`)
- CLI wiring + `.schema.json` input detection: [`src/flatc.cpp`](src/flatc.cpp)
- Additional docs: [`docs/source/json_schema.md`](docs/source/json_schema.md), [`docs/source/flatc.md`](docs/source/flatc.md)

#### Export: FlatBuffers → JSON Schema (`--jsonschema`)

Generate `*.schema.json` from `*.fbs`:

```sh
flatc --jsonschema -o out_dir schema.fbs
```

This produces `out_dir/schema.schema.json`.

#### Import: JSON Schema → FlatBuffers IR (`*.schema.json` input)

Any file ending in `.schema.json` can be used anywhere `flatc` expects a schema file:

```sh
flatc --cpp -o out_dir schema.schema.json
```

Root selection:

- If the schema root contains a `$ref` to a definition, that definition becomes the FlatBuffers root type.
- Otherwise you can specify/override the root with `--root-type` (see [`src/flatc.cpp`](src/flatc.cpp) and [`docs/source/flatc.md`](docs/source/flatc.md)).

#### Best-effort mapping for “wild” JSON Schema + OpenAPI

The importer is intentionally permissive and ignores unknown JSON Schema/OpenAPI keywords while making “sane defaults” to treat the input as an IDL.

Supported input shapes:

- Schema definitions under `definitions`, `$defs`, or OpenAPI `components.schemas` (see fixtures under [`tests/jsonschema_import/inputs`](tests/jsonschema_import/inputs)).
- `$ref` resolution for `#/definitions/...`, `#/$defs/...`, and `#/components/schemas/...`.

Type/shape mapping (when `x-flatbuffers` is not present):

- `type: "object"` → FlatBuffers table by default; may infer a struct if the definition contains fixed-length arrays and is otherwise “struct-safe” (see [`src/idl_parser.cpp`](src/idl_parser.cpp)).
- `type: "array"` → FlatBuffers vector; if `minItems == maxItems` it may become a fixed-length array (and will fall back to a vector with `minItems`/`maxItems` preserved if a fixed array would be illegal in FlatBuffers).
- `type: "integer"` → a concrete FlatBuffers integer scalar inferred from numeric range (`minimum`/`maximum`) when provided; `format` of `int32`/`int64`/`uint32`/`uint64` overrides inference.
- `type: "number"` → `float` by default; `format` of `float`/`double` overrides.
- `type: "string"` → FlatBuffers `string`.
- String `enum: ["A", "B", ...]` on a field → generates a FlatBuffers enum for that field.
- `anyOf: [{ "$ref": ... }, ...]` on a field → FlatBuffers union. If the input follows the FlatBuffers JSON/JSON-Schema union convention (a value field plus a sibling `<name>_type` field), the importer will link them (see [`src/idl_parser.cpp`](src/idl_parser.cpp)).

JSON Schema/OpenAPI keyword preservation:

To keep the generated JSON Schema close to the original, the importer preserves a subset of JSON Schema/OpenAPI keywords (either as FlatBuffers doc flags, or as `jsonschema_*` attributes so they survive through the schema IR), and the JSON Schema generator re-emits them:

- Definitions + fields: `description`
- Fields: `deprecated`
- Objects: `required`, `additionalProperties`
- Arrays: `minItems`, `maxItems`, `uniqueItems`
- Strings: `format`, `minLength`, `maxLength`, `readOnly`
- Numbers/integers: `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`

#### Optional lossless semantics: `x-flatbuffers` (`--jsonschema-xflatbuffers`)

JSON Schema cannot represent some FlatBuffers semantics (for example: struct vs table, exact scalar widths, union details, field ids, and presence rules). To enable lossless round-trips, the JSON Schema generator can emit an optional vendor extension:

```sh
flatc --jsonschema --jsonschema-xflatbuffers -o out_dir schema.fbs
```

This emits `x-flatbuffers` metadata objects at the schema root, at each definition, and at each field. Because this uses the standard vendor extension mechanism (`x-...`), most JSON Schema tooling ignores it and continues to work normally (for example QuickType and similar code generators).

At a high level:

- Root metadata: `root_type`, plus optional `file_identifier` and `file_extension`.
- Definition metadata: enum/union kind + values; struct/table kind + (struct-only) `minalign`/`bytesize`.
- Field metadata: exact FlatBuffers type (including union/enum refs), plus presence and selected field attributes (for example `id`, `deprecated`, `key`).

The allowed keys/values for the `x-flatbuffers` vendor extension are described by the meta-schema:

- [`docs/source/schemas/x-flatbuffers.schema.json`](docs/source/schemas/x-flatbuffers.schema.json)

#### Tests (goldens + round-trip stability)

This fork uses golden JSON Schemas and round-trip tests to ensure stability:

- Generation + round-trip for FlatBuffers-emitted JSON Schema (with and without `x-flatbuffers`): [`tests/JsonSchemaTest.sh`](tests/JsonSchemaTest.sh)
- Goldens: [`tests/monster_test.schema.json`](tests/monster_test.schema.json), [`tests/arrays_test.schema.json`](tests/arrays_test.schema.json)
- Import “wild” JSON Schema / OpenAPI fixtures and ensure stable regeneration: [`tests/JsonSchemaImportTest.sh`](tests/JsonSchemaImportTest.sh)
- Inputs: [`tests/jsonschema_import/inputs`](tests/jsonschema_import/inputs)
- Goldens: [`tests/jsonschema_import/goldens`](tests/jsonschema_import/goldens)

Typical local run:

```sh
cmake -B build -S .
cmake --build build --target flatc -j
tests/JsonSchemaTest.sh
tests/JsonSchemaImportTest.sh
```


![logo](https://flatbuffers.dev/assets/flatbuffers_logo.svg) FlatBuffers
Expand Down
21 changes: 0 additions & 21 deletions android/app/proguard-rules.pro

This file was deleted.

28 changes: 15 additions & 13 deletions android/app/src/main/cpp/animals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,27 @@
*/

#include <jni.h>
#include <string>
#include <search.h>

#include <string>

#include "generated/animal_generated.h"

using namespace com::fbs::app;
using namespace flatbuffers;

extern "C" JNIEXPORT jbyteArray JNICALL Java_com_flatbuffers_app_MainActivity_createAnimalFromJNI(
JNIEnv* env,
jobject /* this */) {
// create a new animal flatbuffers
auto fb = FlatBufferBuilder(1024);
auto tiger = CreateAnimalDirect(fb, "Tiger", "Roar", 300);
fb.Finish(tiger);
extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_flatbuffers_app_MainActivity_createAnimalFromJNI(JNIEnv* env,
jobject /* this */) {
// create a new animal flatbuffers
auto fb = FlatBufferBuilder(1024);
auto tiger = CreateAnimalDirect(fb, "Tiger", "Roar", 300);
fb.Finish(tiger);

// copies it to a Java byte array.
auto buf = reinterpret_cast<jbyte*>(fb.GetBufferPointer());
int size = fb.GetSize();
auto ret = env->NewByteArray(size);
env->SetByteArrayRegion (ret, 0, fb.GetSize(), buf);
// copies it to a Java byte array.
auto buf = reinterpret_cast<jbyte*>(fb.GetBufferPointer());
int size = fb.GetSize();
auto ret = env->NewByteArray(size);
env->SetByteArrayRegion(ret, 0, fb.GetSize(), buf);
return ret;
}
36 changes: 19 additions & 17 deletions benchmarks/cpp/benchmark_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,42 @@
#include "benchmarks/cpp/flatbuffers/fb_bench.h"
#include "benchmarks/cpp/raw/raw_bench.h"

static inline void Encode(benchmark::State &state,
std::unique_ptr<Bench> &bench, uint8_t *buffer) {
static inline void Encode(benchmark::State& state,
std::unique_ptr<Bench>& bench, uint8_t* buffer) {
int64_t length;
for (auto _ : state) {
bench->Encode(buffer, length);
benchmark::DoNotOptimize(length);
}
}

static inline void Decode(benchmark::State &state,
std::unique_ptr<Bench> &bench, uint8_t *buffer) {
static inline void Decode(benchmark::State& state,
std::unique_ptr<Bench>& bench, uint8_t* buffer) {
int64_t length;
uint8_t *encoded = bench->Encode(buffer, length);
uint8_t* encoded = bench->Encode(buffer, length);

for (auto _ : state) {
void *decoded = bench->Decode(encoded, length);
void* decoded = bench->Decode(encoded, length);
benchmark::DoNotOptimize(decoded);
}
}

static inline void Use(benchmark::State &state, std::unique_ptr<Bench> &bench,
uint8_t *buffer, int64_t check_sum) {
static inline void Use(benchmark::State& state, std::unique_ptr<Bench>& bench,
uint8_t* buffer, int64_t check_sum) {
int64_t length;
uint8_t *encoded = bench->Encode(buffer, length);
void *decoded = bench->Decode(encoded, length);
uint8_t* encoded = bench->Encode(buffer, length);
void* decoded = bench->Decode(encoded, length);

int64_t sum = 0;

for (auto _ : state) { sum = bench->Use(decoded); }
for (auto _ : state) {
sum = bench->Use(decoded);
}

EXPECT_EQ(sum, check_sum);
}

static void BM_Flatbuffers_Encode(benchmark::State &state) {
static void BM_Flatbuffers_Encode(benchmark::State& state) {
const int64_t kBufferLength = 1024;
uint8_t buffer[kBufferLength];

Expand All @@ -48,7 +50,7 @@ static void BM_Flatbuffers_Encode(benchmark::State &state) {
}
BENCHMARK(BM_Flatbuffers_Encode);

static void BM_Flatbuffers_Decode(benchmark::State &state) {
static void BM_Flatbuffers_Decode(benchmark::State& state) {
const int64_t kBufferLength = 1024;
uint8_t buffer[kBufferLength];

Expand All @@ -58,7 +60,7 @@ static void BM_Flatbuffers_Decode(benchmark::State &state) {
}
BENCHMARK(BM_Flatbuffers_Decode);

static void BM_Flatbuffers_Use(benchmark::State &state) {
static void BM_Flatbuffers_Use(benchmark::State& state) {
const int64_t kBufferLength = 1024;
uint8_t buffer[kBufferLength];

Expand All @@ -68,7 +70,7 @@ static void BM_Flatbuffers_Use(benchmark::State &state) {
}
BENCHMARK(BM_Flatbuffers_Use);

static void BM_Raw_Encode(benchmark::State &state) {
static void BM_Raw_Encode(benchmark::State& state) {
const int64_t kBufferLength = 1024;
uint8_t buffer[kBufferLength];

Expand All @@ -77,7 +79,7 @@ static void BM_Raw_Encode(benchmark::State &state) {
}
BENCHMARK(BM_Raw_Encode);

static void BM_Raw_Decode(benchmark::State &state) {
static void BM_Raw_Decode(benchmark::State& state) {
const int64_t kBufferLength = 1024;
uint8_t buffer[kBufferLength];

Expand All @@ -86,7 +88,7 @@ static void BM_Raw_Decode(benchmark::State &state) {
}
BENCHMARK(BM_Raw_Decode);

static void BM_Raw_Use(benchmark::State &state) {
static void BM_Raw_Use(benchmark::State& state) {
const int64_t kBufferLength = 1024;
uint8_t buffer[kBufferLength];

Expand Down
14 changes: 7 additions & 7 deletions benchmarks/cpp/flatbuffers/fb_bench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ using namespace benchmarks_flatbuffers;
namespace {

struct FlatBufferBench : Bench {
explicit FlatBufferBench(int64_t initial_size, Allocator *allocator)
explicit FlatBufferBench(int64_t initial_size, Allocator* allocator)
: fbb(initial_size, allocator, false) {}

uint8_t *Encode(void *, int64_t &len) override {
uint8_t* Encode(void*, int64_t& len) override {
fbb.Clear();

const int kVectorLength = 3;
Expand All @@ -40,7 +40,7 @@ struct FlatBufferBench : Bench {
return fbb.GetBufferPointer();
}

int64_t Use(void *decoded) override {
int64_t Use(void* decoded) override {
sum = 0;
auto foobarcontainer = GetFooBarContainer(decoded);
sum = 0;
Expand All @@ -56,7 +56,7 @@ struct FlatBufferBench : Bench {
Add(static_cast<int64_t>(bar->ratio()));
Add(bar->size());
Add(bar->time());
auto &foo = bar->parent();
auto& foo = bar->parent();
Add(foo.count());
Add(foo.id());
Add(foo.length());
Expand All @@ -65,16 +65,16 @@ struct FlatBufferBench : Bench {
return sum;
}

void *Decode(void *buffer, int64_t) override { return buffer; }
void Dealloc(void *) override {};
void* Decode(void* buffer, int64_t) override { return buffer; }
void Dealloc(void*) override {};

FlatBufferBuilder fbb;
};

} // namespace

std::unique_ptr<Bench> NewFlatBuffersBench(int64_t initial_size,
Allocator *allocator) {
Allocator* allocator) {
return std::unique_ptr<FlatBufferBench>(
new FlatBufferBench(initial_size, allocator));
}
Loading