Compare commits

..

24 Commits

Author SHA1 Message Date
Derek Bailey
203241ed32 FlatBuffers Version 22.10.26 (#7607) 2022-10-26 16:02:38 -07:00
Derek Bailey
ac485609c4 setup.py: Define version directly
Define the version directly instead of loading from an environment variable and writing to a file.
2022-10-26 15:31:02 -07:00
Derek Bailey
de5b85aa66 release.yml: Switch to python directory 2022-10-26 15:29:02 -07:00
Derek Bailey
de3df2d88b release.yml: Add publishing to PyPi 2022-10-26 15:17:21 -07:00
Joshua Smith
043a24f2e4 [Python] Fixed the issue with nested unions relying on InitFromBuf. (#7576)
* feat: Fixed the issue with nested unions relying on InitFromBuf.
Problem: Issue #7569
Nested Unions were broken with the introduction of parsing buffers with an initial encoding offset.

Fix:
Revert the InitFromBuf method to the previous version and introduction of InitFromPackedBuf that allows
users to read types from packed buffers applying the offset automatically.

Test:
Added in TestNestedUnionTables to test the encoding and decoding ability using a nested table with a
union field.

* fix: Uncommented generate code command
2022-10-26 14:56:52 -07:00
Derek Bailey
5a48b0d7d6 release.yml: Typo 2022-10-26 00:40:43 -07:00
Derek Bailey
ce307556fb release.yml: Remove npm ci
This command [was suggested](https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages#publishing-packages-to-the-npm-registry) in the github docs, but not in our release steps.
2022-10-26 00:35:11 -07:00
Derek Bailey
cb616e27cb Create release.yml (#7605)
Create a new `release.yml` for defining automatic publishing to package managers on releases.

Adds the NPM publishing steps as it is the most straightforward.
2022-10-26 00:30:16 -07:00
Derek Bailey
a54ca1e759 FlatBuffers Version 22.10.25 (#7604) 2022-10-26 00:03:49 -07:00
ArnaudD-FR
5b3fadcc16 [vector] Allow to iterate with mutables (#7586)
Co-authored-by: ArnaudD-FR <arnaud.desmier@gmail.com>
Co-authored-by: Derek Bailey <derekbailey@google.com>
2022-10-21 12:10:18 -07:00
Dan Lapid
872a497464 [Nim] Bfbs Nim Generator (#7534)
* Bfbs Nim Generator

* Remove commented out tests

* add missing line to idl.h

* Commit python reflection changes

* Commit python reflection changes and move tests

* Remove default string addition

* Move tests to python file

* Fix element size check when element is table

* remove whitespace changes

* add element_type docs and commit further to namer and remove kkeep

* Bfbs Nim Generator

* Remove commented out tests

* add missing line to idl.h

* Commit python reflection changes

* Commit python reflection changes and move tests

* Remove default string addition

* Move tests to python file

* Fix element size check when element is table

* remove whitespace changes

* add element_type docs and commit further to namer and remove kkeep

* remove unused variables

* added tests to ci

* added tests to ci

* fixes

* Added reflection type Field, Variable to namer

* Moved reflection namer impl to bfbsnamer

* Remove whitespace at end of line

* Added nim to generated code

* Revert whitespace removal

Co-authored-by: Derek Bailey <derekbailey@google.com>
2022-10-21 14:30:04 -04:00
Dominic Battre
e301702964 Make type conversions explicit. (#7595)
As Java does not support unsigned integer types, the value types
are "rounded up" (an uint32 is represented as a long) but persisted
correctly (an uint32 is persisted as 4 bytes).

This CL makes a cast operation explicit so that the compiler
does not throw warning messages.

Co-authored-by: Dominic Battre <battre@chromium.org>
Co-authored-by: Derek Bailey <derekbailey@google.com>
2022-10-21 09:09:28 -07:00
Dominic Battre
f7b734438d Fix LongEnum definitions (#7596)
The MyGame/Example/LongEnum.java class did not compile because
Java expects an "L" suffix for literals of type long.

This CL fixes the code generation to include such a suffix.

Co-authored-by: Dominic Battre <battre@chromium.org>
2022-10-20 18:15:00 -07:00
Dan Lapid
5792623df4 Rust fix compilation for no_std targets #2 (#7553)
* Fix nightly no_std

* Fix nightly no_std
2022-10-19 09:14:41 -04:00
Casper
0edb275285 Update Rust version (#7574)
Co-authored-by: Casper Neo <cneo@google.com>
2022-10-18 20:37:12 +00:00
Dominic Battre
acc6a20d39 tests/test.cpp contains a couple of tests that are only executed (#7571)
on a subset of platforms. The test calls are guarded via #ifndef
FLATBUFFERS_NO_FILE_TEST.

Embedders of flatbuffers that rely on -Werror,-Wunused-function
compiler flags (like chromium) complain about the exsitence of these
tests in the anonymous namespace.

This CL guards the test definitions as well (not just the test
calls).

Co-authored-by: Dominic Battre <battre@chromium.org>
Co-authored-by: Derek Bailey <derekbailey@google.com>
2022-10-18 12:37:06 -07:00
Casper
04cd037ba2 Fix #7580 by documenting union schema evolution rules (#7585)
Co-authored-by: Casper Neo <cneo@google.com>
2022-10-17 17:49:28 -04:00
Casper
e1c5db988a Turn on clippy for Rust and fix lints for non-generated code (#7575)
Co-authored-by: Casper Neo <cneo@google.com>
2022-10-10 21:26:35 -04:00
Piotr Dziwiński
b80142b901 Update documentation to mention enum value attributes (#7570) 2022-10-08 19:52:38 -04:00
Piotr Dziwiński
54418f371b Add support for metadata attributes for enum values (#7567) (#7568)
* Add support for metadata attributes for enum values (#7567)

* Fix path lookup in flatc test

* Try a fix for Windows paths

* Convert path to string to fix Windows error
2022-10-06 20:18:00 -04:00
Derek Bailey
c92e78a9f8 FlatBuffers Version 22.9.29 (#7557) 2022-09-29 22:12:07 -07:00
Björn Harrtell
d243b904cc [TS] Make strict compliant and improve typings (#7549)
* [TS] Make strict compliant and improve typings

* clang-format

* Code gen harmonize

Co-authored-by: Derek Bailey <derekbailey@google.com>
2022-09-29 15:03:35 -07:00
Raphael Taylor-Davies
374f8fb5fb Rust soundness fixes (#7518)
* Rust soundness fixes

* Second pass

* Make init_from_table unsafe

* Remove SafeSliceAccess

* Clippy

* Remove create_vector_of_strings

* More clippy

* Remove deprecated root type accessors

* More soundness fixes

* Fix EndianScalar for bool

* Add TriviallyTransmutable

* Add debug assertions

* Review comments

* Review feedback
2022-09-29 09:58:49 -04:00
mustiikhalil
dadbff5714 Moves swift package to root of repository so it can be used directly … (#7548)
* Moves swift package to root of repository so it can be used directly from the main repo

Fixing paths for swift directory

* Update swift readme
2022-09-27 14:32:05 -07:00
395 changed files with 7945 additions and 2820 deletions

10
.github/labeler.yml vendored
View File

@@ -18,6 +18,12 @@ swift:
- tests/swift/**
- src/idl_gen_swift.cpp
nim:
- '**/*.nim'
- nim/**/*
- src/idl_gen_nim.cpp
- src/bfbs_gen_nim.cpp
javascript:
- '**/*.js'
- src/idl_gen_ts.cpp
@@ -61,7 +67,7 @@ rust:
- '**/*.rs'
- rust/**/*
- src/idl_gen_rust.cpp
dart:
- '**/*.dart'
- src/idl_gen_dart.cpp
@@ -88,4 +94,4 @@ CI:
grpc:
- grpc/**/*
- src/idl_gen_grpc.cpp
- src/idl_gen_grpc.cpp

View File

@@ -477,6 +477,22 @@ jobs:
working-directory: tests
run: bash DartTest.sh
build-nim:
name: Build Nim
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: flatc
# FIXME: make test script not rely on flatc
run: cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_INSTALL=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF . && make -j
- uses: jiro4989/setup-nim-action@v1
- name: install library
working-directory: nim
run: nimble -y develop
- name: test
working-directory: tests/nim
run: python3 testnim.py
release-digests:
if: startsWith(github.ref, 'refs/tags/')
needs: [build-linux, build-windows, build-mac-intel, build-mac-universal]

54
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: Release
permissions: read-all
on:
# For manual tests.
workflow_dispatch:
release:
types: [created]
jobs:
publish-npm:
name: Publish NPM
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16.x'
registry-url: 'https://registry.npmjs.org'
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
publish-pypi:
name: Publish PyPi
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./python
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install Dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install setuptools wheel twine
- name: Build
run: |
python3 setup.py sdist bdist_wheel
- name: Upload to PyPi
run: |
python3 -m twine upload dist/*
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TWINE_TOKEN }}

View File

@@ -4,6 +4,14 @@ All major or breaking changes will be documented in this file, as well as any
new features that should be highlighted. Minor fixes or improvements are not
necessarily listed.
## 22.10.25 (Oct 25 2002)
* Added Nim language support with generator and runtime libraries (#7534).
## 22.9.29 (Sept 29 2022)
* Rust soundness fixes to avoid the crate from bing labelled unsafe (#7518).
## 22.9.24 (Sept 24 2022)
* 20 Major releases in a row? Nope, we switched to a new
@@ -23,8 +31,8 @@ necessarily listed.
`AlignOf()` for structs (#7520).
* C# has an
[offical Nuget package](https://www.nuget.org/packages/Google.FlatBuffers) now
(#7496).
[official Nuget package](https://www.nuget.org/packages/Google.FlatBuffers)
now (#7496).
## 2.0.8 (Aug 29 2022)

View File

@@ -1,6 +1,6 @@
set(VERSION_MAJOR 22)
set(VERSION_MINOR 9)
set(VERSION_PATCH 24)
set(VERSION_MINOR 10)
set(VERSION_PATCH 26)
set(VERSION_COMMIT 0)
find_program(GIT git)

View File

@@ -182,6 +182,7 @@ set(FlatBuffers_Compiler_SRCS
src/flatc_main.cpp
src/bfbs_gen.h
src/bfbs_gen_lua.h
src/bfbs_gen_nim.h
src/bfbs_namer.h
include/flatbuffers/code_generators.h
src/binary_annotator.h
@@ -189,6 +190,7 @@ set(FlatBuffers_Compiler_SRCS
src/annotated_binary_text_gen.h
src/annotated_binary_text_gen.cpp
src/bfbs_gen_lua.cpp
src/bfbs_gen_nim.cpp
src/code_generators.cpp
grpc/src/compiler/schema_interface.h
grpc/src/compiler/cpp_generator.h

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'FlatBuffers'
s.version = '22.9.24'
s.version = '22.10.26'
s.summary = 'FlatBuffers: Memory Efficient Serialization Library'
s.description = "FlatBuffers is a cross platform serialization library architected for
@@ -11,11 +11,11 @@ Pod::Spec.new do |s|
s.homepage = 'https://github.com/google/flatbuffers'
s.license = { :type => 'Apache2.0', :file => 'LICENSE' }
s.author = { 'mustii' => 'mustii@mmk.one' }
s.source = { :git => 'https://github.com/mustiikhalil/flatbuffers.git', :tag => s.version.to_s, :submodules => true }
s.source = { :git => 'https://github.com/google/flatbuffers.git', :tag => s.version.to_s, :submodules => true }
s.ios.deployment_target = '11.0'
s.osx.deployment_target = '10.14'
s.swift_version = '5.0'
s.source_files = 'Sources/Flatbuffers/*.swift'
s.source_files = 'swift/Sources/Flatbuffers/*.swift'
end

View File

@@ -32,5 +32,6 @@ let package = Package(
.target(
name: "FlatBuffers",
dependencies: [],
path: "swift/Sources",
exclude: ["Documentation.docc/Resources/code/swift"]),
])

View File

@@ -31,6 +31,7 @@ let package = Package(
targets: [
.target(
name: "FlatBuffers",
dependencies: []),
dependencies: [],
path: "swift/Sources")
])

View File

@@ -36,7 +36,7 @@ class Animal : Table() {
return if(o != 0) bb.getShort(o + bb_pos).toUShort() else 0u
}
companion object {
fun validateVersion() = Constants.FLATBUFFERS_22_9_24()
fun validateVersion() = Constants.FLATBUFFERS_22_10_26()
fun getRootAsAnimal(_bb: ByteBuffer): Animal = getRootAsAnimal(_bb, Animal())
fun getRootAsAnimal(_bb: ByteBuffer, obj: Animal): Animal {
_bb.order(ByteOrder.LITTLE_ENDIAN)

View File

@@ -1,5 +1,5 @@
name: flat_buffers
version: 22.9.24
version: 22.10.26
description: FlatBuffers reading and writing library for Dart. Based on original work by Konstantin Scheglov and Paul Berry of the Dart SDK team.
homepage: https://github.com/google/flatbuffers
documentation: https://google.github.io/flatbuffers/index.html

View File

@@ -10,7 +10,7 @@ include = `include` string\_constant `;`
namespace\_decl = `namespace` ident ( `.` ident )* `;`
attribute\_decl = `attribute` ident | `"`ident`"` `;`
attribute\_decl = `attribute` ident | `"` ident `"` `;`
type\_decl = ( `table` | `struct` ) ident metadata `{` field\_decl+ `}`
@@ -31,7 +31,7 @@ type = `bool` | `byte` | `ubyte` | `short` | `ushort` | `int` | `uint` |
`float32` | `float64` |
`string` | `[` type `]` | ident
enumval\_decl = ident [ `=` integer\_constant ]
enumval\_decl = ident [ `=` integer\_constant ] metadata
metadata = [ `(` commasep( ident [ `:` single\_value ] ) `)` ]

View File

@@ -309,11 +309,11 @@ in the corresponding C++ code. Multiple such lines per item are allowed.
### Attributes
Attributes may be attached to a declaration, behind a field, or after
the name of a table/struct/enum/union. These may either have a value or
not. Some attributes like `deprecated` are understood by the compiler;
user defined ones need to be declared with the attribute declaration
(like `priority` in the example above), and are
Attributes may be attached to a declaration, behind a field/enum value,
or after the name of a table/struct/enum/union. These may either have
a value or not. Some attributes like `deprecated` are understood by
the compiler; user defined ones need to be declared with the attribute
declaration (like `priority` in the example above), and are
available to query if you parse the schema at runtime.
This is useful if you write your own code generators/editors etc., and
you wish to add additional information specific to your tool (such as a
@@ -552,7 +552,7 @@ the world. If this is not practical for you, use explicit field ids, which
should always generate a merge conflict if two people try to allocate the same
id.
### Schema evolution examples
### Schema evolution examples (tables)
Some examples to clarify what happens as you change a schema:
@@ -614,6 +614,41 @@ Occasionally ok. You've renamed fields, which will break all code (and JSON
files!) that use this schema, but as long as the change is obvious, this is not
incompatible with the actual binary buffers, since those only ever address
fields by id/offset.
#### Schema evolution examples (unions)
Suppose we have the following schema:
```
union Foo { A, B }
```
We can add another variant at the end.
```
union Foo { A, B, another_a: A }
```
and this will be okay. Old code will not recognize `another_a`.
However if we add `another_a` anywhere but the end, e.g.
```
union Foo { A, another_a: A, B }
```
this is not okay. When new code writes `another_a`, old code will
misinterpret it as `B` (and vice versa). However you can explicitly
set the union's "discriminant" value like so:
```
union Foo { A = 1, another_a: A = 3, B = 2 }
```
This is okay.
```
union Foo { original_a: A = 1, another_a: A = 3, B = 2 }
```
Renaming fields will break code and any saved human readable representations,
such as json files, but the binary buffers will be the same.
<br>
### Testing whether a field is present in a table

View File

@@ -6,7 +6,7 @@ import FlatBuffers
public struct models_HelloReply: FlatBufferObject, Verifiable {
static func validateVersion() { FlatBuffersVersion_22_9_24() }
static func validateVersion() { FlatBuffersVersion_22_10_26() }
public var __buffer: ByteBuffer! { return _accessor.bb }
private var _accessor: Table
@@ -55,7 +55,7 @@ extension models_HelloReply: Encodable {
public struct models_HelloRequest: FlatBufferObject, Verifiable {
static func validateVersion() { FlatBuffersVersion_22_9_24() }
static func validateVersion() { FlatBuffersVersion_22_10_26() }
public var __buffer: ByteBuffer! { return _accessor.bb }
private var _accessor: Table

View File

@@ -35,7 +35,7 @@ template<typename T, uint16_t length> class Array {
public:
typedef uint16_t size_type;
typedef typename IndirectHelper<IndirectHelperType>::return_type return_type;
typedef VectorIterator<T, return_type> const_iterator;
typedef VectorConstIterator<T, return_type> const_iterator;
typedef VectorReverseIterator<const_iterator> const_reverse_iterator;
// If T is a LE-scalar or a struct (!scalar_tag::value).

View File

@@ -139,8 +139,8 @@
#endif // !defined(FLATBUFFERS_LITTLEENDIAN)
#define FLATBUFFERS_VERSION_MAJOR 22
#define FLATBUFFERS_VERSION_MINOR 9
#define FLATBUFFERS_VERSION_REVISION 24
#define FLATBUFFERS_VERSION_MINOR 10
#define FLATBUFFERS_VERSION_REVISION 26
#define FLATBUFFERS_STRING_EXPAND(X) #X
#define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X)
namespace flatbuffers {

View File

@@ -76,6 +76,9 @@ template<typename T> struct IndirectHelper {
static return_type Read(const uint8_t *p, uoffset_t i) {
return EndianScalar((reinterpret_cast<const T *>(p))[i]);
}
static return_type Read(uint8_t *p, uoffset_t i) {
return Read(const_cast<const uint8_t *>(p), i);
}
};
template<typename T> struct IndirectHelper<Offset<T>> {
typedef const T *return_type;
@@ -85,13 +88,20 @@ template<typename T> struct IndirectHelper<Offset<T>> {
p += i * sizeof(uoffset_t);
return reinterpret_cast<return_type>(p + ReadScalar<uoffset_t>(p));
}
static mutable_return_type Read(uint8_t *p, uoffset_t i) {
p += i * sizeof(uoffset_t);
return reinterpret_cast<mutable_return_type>(p + ReadScalar<uoffset_t>(p));
}
};
template<typename T> struct IndirectHelper<const T *> {
typedef const T *return_type;
typedef T *mutable_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));
return reinterpret_cast<return_type>(p + i * sizeof(T));
}
static mutable_return_type Read(uint8_t *p, uoffset_t i) {
return reinterpret_cast<mutable_return_type>(p + i * sizeof(T));
}
};

View File

@@ -382,7 +382,14 @@ struct EnumVal {
Offset<reflection::EnumVal> Serialize(FlatBufferBuilder *builder,
const Parser &parser) const;
bool Deserialize(const Parser &parser, const reflection::EnumVal *val);
bool Deserialize(Parser &parser, const reflection::EnumVal *val);
flatbuffers::Offset<
flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>>
SerializeAttributes(FlatBufferBuilder *builder, const Parser &parser) const;
bool DeserializeAttributes(Parser &parser,
const Vector<Offset<reflection::KeyValue>> *attrs);
uint64_t GetAsUInt64() const { return static_cast<uint64_t>(value); }
int64_t GetAsInt64() const { return value; }
@@ -392,6 +399,7 @@ struct EnumVal {
std::string name;
std::vector<std::string> doc_comment;
Type union_type;
SymbolTable<Value> attributes;
private:
friend EnumDef;
@@ -653,6 +661,7 @@ struct IDLOptions {
kRust = 1 << 14,
kKotlin = 1 << 15,
kSwift = 1 << 16,
kNim = 1 << 17,
kMAX
};

View File

@@ -9,8 +9,8 @@
// Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible.
static_assert(FLATBUFFERS_VERSION_MAJOR == 22 &&
FLATBUFFERS_VERSION_MINOR == 9 &&
FLATBUFFERS_VERSION_REVISION == 24,
FLATBUFFERS_VERSION_MINOR == 10 &&
FLATBUFFERS_VERSION_REVISION == 26,
"Non-compatible flatbuffers version included");
namespace reflection {
@@ -334,7 +334,8 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
VT_NAME = 4,
VT_VALUE = 6,
VT_UNION_TYPE = 10,
VT_DOCUMENTATION = 12
VT_DOCUMENTATION = 12,
VT_ATTRIBUTES = 14
};
const flatbuffers::String *name() const {
return GetPointer<const flatbuffers::String *>(VT_NAME);
@@ -354,6 +355,9 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *documentation() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(VT_DOCUMENTATION);
}
const flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>> *attributes() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>> *>(VT_ATTRIBUTES);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffsetRequired(verifier, VT_NAME) &&
@@ -364,6 +368,9 @@ struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
VerifyOffset(verifier, VT_DOCUMENTATION) &&
verifier.VerifyVector(documentation()) &&
verifier.VerifyVectorOfStrings(documentation()) &&
VerifyOffset(verifier, VT_ATTRIBUTES) &&
verifier.VerifyVector(attributes()) &&
verifier.VerifyVectorOfTables(attributes()) &&
verifier.EndTable();
}
};
@@ -384,6 +391,9 @@ struct EnumValBuilder {
void add_documentation(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> documentation) {
fbb_.AddOffset(EnumVal::VT_DOCUMENTATION, documentation);
}
void add_attributes(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>> attributes) {
fbb_.AddOffset(EnumVal::VT_ATTRIBUTES, attributes);
}
explicit EnumValBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
@@ -401,9 +411,11 @@ inline flatbuffers::Offset<EnumVal> CreateEnumVal(
flatbuffers::Offset<flatbuffers::String> name = 0,
int64_t value = 0,
flatbuffers::Offset<reflection::Type> union_type = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> documentation = 0) {
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> documentation = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>> attributes = 0) {
EnumValBuilder builder_(_fbb);
builder_.add_value(value);
builder_.add_attributes(attributes);
builder_.add_documentation(documentation);
builder_.add_union_type(union_type);
builder_.add_name(name);
@@ -415,15 +427,18 @@ inline flatbuffers::Offset<EnumVal> CreateEnumValDirect(
const char *name = nullptr,
int64_t value = 0,
flatbuffers::Offset<reflection::Type> union_type = 0,
const std::vector<flatbuffers::Offset<flatbuffers::String>> *documentation = nullptr) {
const std::vector<flatbuffers::Offset<flatbuffers::String>> *documentation = nullptr,
std::vector<flatbuffers::Offset<reflection::KeyValue>> *attributes = nullptr) {
auto name__ = name ? _fbb.CreateString(name) : 0;
auto documentation__ = documentation ? _fbb.CreateVector<flatbuffers::Offset<flatbuffers::String>>(*documentation) : 0;
auto attributes__ = attributes ? _fbb.CreateVectorOfSortedTables<reflection::KeyValue>(attributes) : 0;
return reflection::CreateEnumVal(
_fbb,
name__,
value,
union_type,
documentation__);
documentation__,
attributes__);
}
struct Enum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {

View File

@@ -27,14 +27,15 @@ struct String;
// An STL compatible iterator implementation for Vector below, effectively
// calling Get() for every element.
template<typename T, typename IT> struct VectorIterator {
template<typename T, typename IT, typename Data = uint8_t *>
struct VectorIterator {
typedef std::random_access_iterator_tag iterator_category;
typedef IT value_type;
typedef ptrdiff_t difference_type;
typedef IT *pointer;
typedef IT &reference;
VectorIterator(const uint8_t *data, uoffset_t i)
VectorIterator(Data data, uoffset_t i)
: data_(data + IndirectHelper<T>::element_stride * i) {}
VectorIterator(const VectorIterator &other) : data_(other.data_) {}
VectorIterator() : data_(nullptr) {}
@@ -116,9 +117,12 @@ template<typename T, typename IT> struct VectorIterator {
}
private:
const uint8_t *data_;
Data data_;
};
template<typename T, typename IT>
using VectorConstIterator = VectorIterator<T, IT, const uint8_t *>;
template<typename Iterator>
struct VectorReverseIterator : public std::reverse_iterator<Iterator> {
explicit VectorReverseIterator(Iterator iter)
@@ -145,7 +149,7 @@ template<typename T> class Vector {
public:
typedef VectorIterator<T, typename IndirectHelper<T>::mutable_return_type>
iterator;
typedef VectorIterator<T, typename IndirectHelper<T>::return_type>
typedef VectorConstIterator<T, typename IndirectHelper<T>::return_type>
const_iterator;
typedef VectorReverseIterator<iterator> reverse_iterator;
typedef VectorReverseIterator<const_iterator> const_reverse_iterator;

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.flatbuffers</groupId>
<artifactId>flatbuffers-java</artifactId>
<version>22.9.24</version>
<version>22.10.26</version>
<packaging>bundle</packaging>
<name>FlatBuffers Java API</name>
<description>
@@ -58,19 +58,17 @@
</distributionManagement>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>8</release>
<testExcludes>
<testExclude>**/LongEnum.java</testExclude>
<testExclude>MyGame/Example/MonsterStorageGrpc.java</testExclude>
<testExclude>MyGame/Example/StructOfStructs**</testExclude>
<testExclude>MyGame/OtherNameSpace/TableBT.java</testExclude>
</testExcludes>
</configuration>
<version>3.8.1</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>8</release>
<testExcludes>
<testExclude>MyGame/Example/MonsterStorageGrpc.java</testExclude>
<testExclude>MyGame/OtherNameSpace/TableBT.java</testExclude>
</testExcludes>
</configuration>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
<profiles>
@@ -149,10 +147,10 @@
<goal>sign</goal>
</goals>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</execution>
</executions>
@@ -173,4 +171,3 @@
</profile>
</profiles>
</project>

View File

@@ -46,7 +46,7 @@ public class Constants {
Changes to the Java implementation need to be sure to change
the version here and in the code generator on every possible
incompatible change */
public static void FLATBUFFERS_22_9_24() {}
public static void FLATBUFFERS_22_10_26() {}
}
/// @endcond

View File

@@ -32,6 +32,6 @@ namespace Google.FlatBuffers
Changes to the C# implementation need to be sure to change
the version here and in the code generator on every possible
incompatible change */
public static void FLATBUFFERS_22_9_24() {}
public static void FLATBUFFERS_22_10_26() {}
}
}

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net46</TargetFrameworks>
<Description>A cross-platform memory efficient serialization library</Description>
<PackageVersion>22.9.24</PackageVersion>
<PackageVersion>22.10.26</PackageVersion>
<Authors>Google LLC</Authors>
<PackageProjectUrl>https://github.com/google/flatbuffers</PackageProjectUrl>
<RepositoryUrl>https://github.com/google/flatbuffers</RepositoryUrl>

7
nim/flatbuffers.nimble Normal file
View File

@@ -0,0 +1,7 @@
version = "2.0.8"
author = "flatbuffers"
description = "Flatbuffers"
license = "Apache 2.0"
srcDir = "flatbuffers"
requires "nim >= 1.4.0"

View File

@@ -0,0 +1,7 @@
import
src/[
builder,
struct,
table
]
export flatbuffers.builder, flatbuffers.table, flatbuffers.struct

View File

@@ -0,0 +1,262 @@
import math
import table
const MAX_BUFFER_SIZE* = 2^31
type Builder* = ref object of RootObj
bytes*: seq[byte]
minalign*: int
current_vtable*: seq[uoffset]
objectEnd*: uoffset
vtables*: seq[uoffset] #?
head*: uoffset
nested*: bool
finished*: bool
vectorNumElems*: uoffset
using this: var Builder
func newBuilder*(size: int): Builder =
result = new Builder
result.bytes.setLen(size)
result.minalign = 1
result.head = size.uoffset
result.nested = false
result.finished = false
result.vectorNumElems = 0
proc FinishedBytes*(this): seq[byte] =
if not this.finished:
quit("Builder not finished, Incorrect use of FinishedBytes(): must call 'Finish' first.")
result = this.bytes[this.head..^1]
proc Output*(this): seq[byte] =
if not this.finished:
quit("Builder not finished, Incorrect use of Output(): must call 'Finish' first.")
result = this.bytes[this.head..^1]
func Offset*(this): uoffset =
result = this.bytes.len.uoffset - this.head
proc StartObject*(this; numfields: int) =
if this.nested:
quit("builder is nested")
this.current_vtable.setLen(numfields)
for i in this.current_vtable.mitems():
i = 0
this.objectEnd = this.Offset()
this.nested = true
proc GrowByteBuffer*(this) =
if this.bytes.len == MAX_BUFFER_SIZE:
quit("flatbuffers: cannot grow buffer beyond 2 gigabytes")
let oldLen = this.bytes.len
var newLen = min(this.bytes.len * 2, MAX_BUFFER_SIZE)
if newLen == 0:
newLen = 1
this.bytes.setLen(newLen)
var j = this.bytes.len - 1
while j >= 0:
if j >= newLen - oldLen:
this.bytes[j] = this.bytes[j - (newLen - oldLen)]
else:
this.bytes[j] = 0
dec(j)
proc Place*[T](this; x: T) =
this.head -= uoffset x.sizeof
WriteVal(this.bytes, this.head, x)
func Pad*(this; n: int) =
for i in 0..<n:
this.Place(0.byte)
proc Prep*(this; size: int; additionalBytes: int) =
if size > this.minalign:
this.minalign = size
var alignsize = (not (this.bytes.len - this.head.int + additionalBytes)) + 1
alignsize = alignsize and (size - 1)
while this.head.int < alignsize + size + additionalBytes:
let oldbufSize = this.bytes.len
this.GrowByteBuffer()
this.head = (this.head.int + this.bytes.len - oldbufSize).uoffset
this.Pad(alignsize)
proc PrependOffsetRelative*[T: Offsets](this; off: T) =
when T is voffset:
this.Prep(T.sizeof, 0)
if not off.uoffset <= this.Offset:
quit("flatbuffers: Offset arithmetic error.")
this.Place(off)
else:
this.Prep(T.sizeof, 0)
if not off.uoffset <= this.Offset:
quit("flatbuffers: Offset arithmetic error.")
let off2: T = this.Offset.T - off + sizeof(T).T
this.Place(off2)
proc Prepend*[T](this; x: T) =
this.Prep(x.sizeof, 0)
this.Place(x)
proc Slot*(this; slotnum: int) =
this.current_vtable[slotnum] = this.Offset
proc PrependSlot*[T](this; o: int; x, d: T) =
if x != d:
when T is uoffset or T is soffset or T is voffset:
this.PrependOffsetRelative(x)
else:
this.Prepend(x)
this.Slot(o)
proc AssertStuctInline(this; obj: uoffset) =
if obj != this.Offset:
quit("flatbuffers: Tried to write a Struct at an Offset that is different from the current Offset of the Builder.")
proc PrependStructSlot*(this; o: int; x: uoffset; d: uoffset) =
if x != d:
this.AssertStuctInline(x)
this.Slot(o)
proc Add*[T](this; n: T) =
this.Prep(T.sizeof, 0)
WriteVal(this.bytes, this.head, n)
proc VtableEqual*(a: seq[uoffset]; objectStart: uoffset; b: seq[byte]): bool =
if a.len * voffset.sizeof != b.len:
return false
var i = 0
while i < a.len:
var seq = b[i * voffset.sizeof..<(i + 1) * voffset.sizeof]
let x = GetVal[voffset](addr seq)
if x == 0 and a[i] == 0:
inc i
continue
let y = objectStart.soffset - a[i].soffset
if x.soffset != y:
return false
inc i
return true
proc WriteVtable*(this): uoffset =
this.PrependOffsetRelative(0.soffset)
let objectOffset = this.Offset
var existingVtable = uoffset 0
var i = this.current_vtable.len - 1
while i >= 0 and this.current_vtable[i] == 0: dec i
this.current_vtable = this.current_vtable[0..i]
for i in countdown(this.vtables.len - 1, 0):
let
vt2Offset: uoffset = this.vtables[i]
vt2Start: int = this.bytes.len - int vt2Offset
var seq = this.bytes[vt2Start..<this.bytes.len]
let
vt2Len = GetVal[voffset](addr seq)
metadata = 2 * voffset.sizeof # VtableMetadataFields * SizeVOffsetT
vt2End = vt2Start + vt2Len.int
vt2 = this.bytes[this.bytes.len - vt2Offset.int + metadata..<vt2End]
if VtableEqual(this.current_vtable, objectOffset, vt2):
existingVtable = vt2Offset
break
if existingVtable == 0:
for i in countdown(this.current_vtable.len - 1, 0):
var off: uoffset
if this.current_vtable[i] != 0:
off = objectOffset - this.current_vtable[i]
this.PrependOffsetRelative(off.voffset)
let objectSize = objectOffset - this.objectEnd
this.PrependOffsetRelative(objectSize.voffset)
let vBytes = (this.current_vtable.len + 2) * voffset.sizeof
this.PrependOffsetRelative(vBytes.voffset)
let objectStart: uoffset = (this.bytes.len.uoffset - objectOffset)
WriteVal(this.bytes, objectStart, (this.Offset - objectOffset).soffset)
this.vtables.add this.Offset
else:
let objectStart: uoffset = this.bytes.len.uoffset - objectOffset
this.head = objectStart
WriteVal(this.bytes, this.head,
(existingVtable.soffset - objectOffset.soffset))
this.current_vtable = @[]
result = objectOffset
proc EndObject*(this): uoffset =
if not this.nested:
quit("builder is not nested")
result = this.WriteVtable()
this.nested = false
proc End*(this: var Builder): uoffset =
result = this.EndObject()
proc StartVector*(this; elemSize: int; numElems: uoffset;
alignment: int) =
if this.nested:
quit("builder is nested")
this.nested = true
this.vectorNumElems = numElems
this.Prep(sizeof(uint32), elemSize * numElems.int)
this.Prep(alignment, elemSize * numElems.int)
proc EndVector*(this): uoffset =
if not this.nested:
quit("builder is not nested")
this.nested = false
this.Place(this.vectorNumElems)
this.vectorNumElems = 0
result = this.Offset
proc getChars*(str: seq[byte]): string =
var bytes = str
result = GetVal[string](addr bytes)
proc getBytes*(str: string | cstring): seq[byte] =
for chr in str:
result.add byte chr
result.add byte 0
proc Create*[T](this; s: T): uoffset = # Both CreateString and CreateByteVector functionality
if this.nested:
quit("builder is nested")
this.nested = true
when T is cstring or T is string:
let x = s.getBytes()
let l = x.len.uoffset
this.vectorNumElems = l-1
else:
let x = s
let l = x.len.uoffset
this.vectorNumElems = l
this.Prep(uoffset.sizeof, l.int * byte.sizeof)
this.head -= l
this.bytes[this.head..<this.head+l] = x
result = this.EndVector()
proc Finish*(this; rootTable: uoffset) =
if this.nested:
quit("builder is nested")
this.nested = true
this.Prep(this.minalign, uoffset.sizeof)
this.PrependOffsetRelative(rootTable)
this.finished = true

View File

@@ -0,0 +1,12 @@
template swapEndian*(outp, inp: pointer, size: int) =
var i = cast[cstring](inp)
var o = cast[cstring](outp)
for x in 0..<size:
o[x] = i[(0..<size).len - x - 1]
when system.cpuEndian == bigEndian:
func littleEndianX*(outp, inp: pointer, size: int) {.inline.} = swapEndian(outp, inp, size)
func bigEndianX*(outp, inp: pointer, size: int) {.inline.} = copyMem(outp, inp, size)
else:
func littleEndianX*(outp, inp: pointer, size: int) {.inline.} = copyMem(outp, inp, size)
func bigEndianX*(outp, inp: pointer, size: int) {.inline.} = swapEndian(outp, inp, size)

View File

@@ -0,0 +1,24 @@
import table
type FlatObj* {.inheritable.} = object
tab*: Vtable
func Table*(this: var FlatObj): Vtable = this.tab
func Init*(this: var FlatObj; buf: seq[byte]; i: uoffset) =
this.tab.Bytes = buf
this.tab.Pos = i
# Cant define it in table.nim since it needs FlatObj and Init
func GetUnion*[T: FlatObj](this: var Vtable; off: uoffset): T =
result.Init(this.Bytes, this.Indirect(off))
func GetRootAs*(result: var FlatObj; buf: seq[byte]; offset: uoffset) =
var
vtable = Vtable(Bytes: buf[offset..^1], Pos: offset)
n = Get[uoffset](vtable, offset)
result.Init(buf, n+offset)
func GetRootAs*(result: var FlatObj; buf: string; offset: uoffset) =
result.GetRootAs(cast[seq[byte]](buf), offset)

View File

@@ -0,0 +1,149 @@
import endian
type
uoffset* = uint32 ## offset in to the buffer
soffset* = int32 ## offset from start of table, to a vtable
voffset* = uint16 ## offset from start of table to value
type Offsets* = uoffset | soffset | voffset
type Vtable* = object
Bytes*: seq[byte]
Pos*: uoffset
using this: Vtable
func GetVal*[T](b: ptr seq[byte]): T {.inline.} =
when T is float64:
result = cast[T](GetVal[uint64](b))
elif T is float32:
result = cast[T](GetVal[uint32](b))
elif T is string:
result = cast[T](b[])
else:
if b[].len < T.sizeof:
b[].setLen T.sizeof
result = cast[ptr T](unsafeAddr b[][0])[]
template Get*[T](this; off: uoffset): T =
var seq = this.Bytes[off..^1]
GetVal[T](addr seq)
template Get*[T](this; off: soffset): T =
var seq = this.Bytes[off..^1]
GetVal[T](addr seq)
template Get*[T](this; off: voffset): T =
var seq = this.Bytes[off..^1]
GetVal[T](addr seq)
func WriteVal*[T: not SomeFloat](b: var openArray[byte]; off: uoffset;
n: T) {.inline.} =
when sizeof(T) == 8:
littleEndianX(addr b[off], unsafeAddr n, T.sizeof)
elif sizeof(T) == 4:
littleEndianX(addr b[off], unsafeAddr n, T.sizeof)
elif sizeof(T) == 2:
littleEndianX(addr b[off], unsafeAddr n, T.sizeof)
elif sizeof(T) == 1:
b[off] = n.uint8
else:
discard
#littleEndianX(addr b[off], unsafeAddr n, T.sizeof)
#{.error:"shouldnt appear".}
func WriteVal*[T: not SomeFloat](b: var seq[byte]; off: uoffset;
n: T) {.inline.} =
when sizeof(T) == 8:
littleEndianX(addr b[off], unsafeAddr n, T.sizeof)
elif sizeof(T) == 4:
littleEndianX(addr b[off], unsafeAddr n, T.sizeof)
elif sizeof(T) == 2:
littleEndianX(addr b[off], unsafeAddr n, T.sizeof)
elif sizeof(T) == 1:
b[off] = n.uint8
else:
discard
#littleEndianX(addr b[off], unsafeAddr n, T.sizeof)
#{.error:"shouldnt appear".}
func WriteVal*[T: SomeFloat](b: var openArray[byte]; off: uoffset;
n: T) {.inline.} =
when T is float64:
WriteVal(b, off, cast[uint64](n))
elif T is float32:
WriteVal(b, off, cast[uint32](n))
func WriteVal*[T: SomeFloat](b: var seq[byte]; off: uoffset; n: T) {.inline.} =
when T is float64:
WriteVal(b, off, cast[uint64](n))
elif T is float32:
WriteVal(b, off, cast[uint32](n))
func Offset*(this; off: voffset): voffset =
let vtable = (this.Pos - this.Get[:uoffset](this.Pos)).voffset
let vtableEnd = this.Get[:voffset](vtable)
if off < vtableEnd:
return this.Get[:voffset](vtable + off)
return 0
func Indirect*(this; off: uoffset): uoffset =
result = off + this.Get[:uoffset](off)
func VectorLen*(this; off: uoffset): int =
var newoff: uoffset = off + this.Pos
newoff += this.Get[:uoffset](newoff)
return this.Get[:uoffset](newoff).int
func Vector*(this; off: uoffset): uoffset =
let newoff: uoffset = off + this.Pos
var x: uoffset = newoff + this.Get[:uoffset](newoff)
x += (uoffset.sizeof).uoffset
result = x
func Union*(this; off: uoffset): Vtable =
let newoff: uoffset = off + this.Pos
result.Pos = newoff + this.Get[:uoffset](newoff)
result.Bytes = this.Bytes
func GetSlot*[T](this; slot: voffset; d: T): T =
let off = this.Offset(slot)
if off == 0:
return d
return this.Get[T](this.Pos + off)
func GetOffsetSlot*[T: Offsets](this; slot: voffset; d: T): T =
let off = this.Offset(slot)
if off == 0:
return d
return off
func ByteVector*(this; off: uoffset): seq[byte] =
let
newoff: uoffset = off + this.Get[:uoffset](off)
start = newoff + (uoffset.sizeof).uoffset
var newseq = this.Bytes[newoff..^1]
let
length = GetVal[uoffset](addr newseq)
result = this.Bytes[start..<start+length]
func String*(this; off: uoffset): string =
var byte_seq = this.ByteVector(off)
result = GetVal[string](addr byte_seq)
using this: var Vtable
proc Mutate*[T](this; off: uoffset; n: T): bool =
WriteVal(this.Bytes, off, n)
return true
func MutateSlot*[T](this; slot: voffset; n: T): bool =
let off: voffset = this.Offset(slot)
if off != 0:
return this.Mutate(this.Pos + off.uoffset, n)
return false

View File

@@ -1,6 +1,6 @@
{
"name": "flatbuffers",
"version": "22.9.24",
"version": "22.10.26",
"description": "Memory Efficient Serialization Library",
"files": [
"js/**/*.js",
@@ -17,6 +17,7 @@
},
"scripts": {
"test": "npm run compile && cd tests/ts && python3 ./TypeScriptTest.py",
"lint": "eslint ts",
"compile": "tsc && tsc -p tsconfig.mjs.json && rollup -c",
"prepublishOnly": "npm install --only=dev && npm run compile"
},

View File

@@ -14,4 +14,4 @@
# Placeholder, to be updated during the release process
# by the setup.py
__version__ = u"22.9.24"
__version__ = u"22.10.26"

View File

@@ -73,7 +73,32 @@ class EnumVal(object):
o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(12))
return o == 0
def EnumValStart(builder): builder.StartObject(5)
# EnumVal
def Attributes(self, j):
o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(14))
if o != 0:
x = self._tab.Vector(o)
x += flatbuffers.number_types.UOffsetTFlags.py_type(j) * 4
x = self._tab.Indirect(x)
from reflection.KeyValue import KeyValue
obj = KeyValue()
obj.Init(self._tab.Bytes, x)
return obj
return None
# EnumVal
def AttributesLength(self):
o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(14))
if o != 0:
return self._tab.VectorLen(o)
return 0
# EnumVal
def AttributesIsNone(self):
o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(14))
return o == 0
def EnumValStart(builder): builder.StartObject(6)
def Start(builder):
return EnumValStart(builder)
def EnumValAddName(builder, name): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(name), 0)
@@ -91,6 +116,12 @@ def AddDocumentation(builder, documentation):
def EnumValStartDocumentationVector(builder, numElems): return builder.StartVector(4, numElems, 4)
def StartDocumentationVector(builder, numElems):
return EnumValStartDocumentationVector(builder, numElems)
def EnumValAddAttributes(builder, attributes): builder.PrependUOffsetTRelativeSlot(5, flatbuffers.number_types.UOffsetTFlags.py_type(attributes), 0)
def AddAttributes(builder, attributes):
return EnumValAddAttributes(builder, attributes)
def EnumValStartAttributesVector(builder, numElems): return builder.StartVector(4, numElems, 4)
def StartAttributesVector(builder, numElems):
return EnumValStartAttributesVector(builder, numElems)
def EnumValEnd(builder): return builder.EndObject()
def End(builder):
return EnumValEnd(builder)

View File

@@ -12,54 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import fileinput
import os
import re
import sys
from datetime import datetime
from setuptools import setup
def _update_version_attr(new_version):
for line in fileinput.input('flatbuffers/_version.py', inplace=True):
if line.startswith('__version__'):
line = re.sub(r'".*"', '"{}"'.format(new_version), line)
sys.stdout.write(line)
def version():
version = os.getenv('VERSION', None)
if version:
# Most git tags are prefixed with 'v' (example: v1.2.3) this is
# never desirable for artifact repositories, so we strip the
# leading 'v' if it's present.
version = version[1:] if version.startswith('v') else version
else:
# Default version is an ISO8601 compiliant datetime. PyPI doesn't allow
# the colon ':' character in its versions, and time is required to allow
# for multiple publications to master in one day. This datetime string
# uses the "basic" ISO8601 format for both its date and time components
# to avoid issues with the colon character (ISO requires that date and
# time components of a date-time string must be uniformly basic or
# extended, which is why the date component does not have dashes.
#
# Publications using datetime versions should only be made from master
# to represent the HEAD moving forward.
version = datetime.utcnow().strftime('%Y%m%d%H%M%S')
print("VERSION environment variable not set, using datetime instead: {}"
.format(version))
_update_version_attr(version)
return version
setup(
name='flatbuffers',
version=version(),
version='22.10.26',
license='Apache 2.0',
author='FlatBuffers Contributors',
author_email='me@rwinslow.com',
author='Derek Bailey',
author_email='derekbailey@google.com',
url='https://google.github.io/flatbuffers/',
long_description=('Python runtime library for use with the '
'`Flatbuffers <https://google.github.io/flatbuffers/>`_ '
@@ -81,4 +41,4 @@ setup(
'Documentation': 'https://google.github.io/flatbuffers/',
'Source': 'https://github.com/google/flatbuffers',
},
)
)

View File

@@ -56,6 +56,7 @@ table EnumVal {
object:Object (deprecated);
union_type:Type;
documentation:[string];
attributes:[KeyValue];
}
table Enum {

View File

@@ -1,6 +1,6 @@
[package]
name = "flatbuffers"
version = "2.1.2"
version = "22.9.29"
edition = "2018"
authors = ["Robert Winslow <hello@rwinslow.com>", "FlatBuffers Maintainers"]
license = "Apache-2.0"
@@ -12,15 +12,13 @@ categories = ["encoding", "data-structures", "memory-management"]
rust = "1.51"
[features]
default = ["thiserror"]
no_std = ["core2", "thiserror_core2"]
default = ["std"]
std = []
serialize = ["serde"]
[dependencies]
smallvec = "1.6.1"
bitflags = "1.2.1"
serde = { version = "1.0", optional = true }
thiserror = { version = "1.0.30", optional = true }
core2 = { version = "0.4.0", optional = true }
# This version is compliant with mainline 1.0.30
thiserror_core2 = { version = "2.0.0", default-features = false, optional = true }
[build-dependencies]
rustc_version = "0.4.0"

12
rust/flatbuffers/build.rs Normal file
View File

@@ -0,0 +1,12 @@
use rustc_version::{version_meta, Channel};
fn main() {
let version_meta = version_meta().unwrap();
// To use nightly features we declare this and then we can use
// #[cfg(nightly)]
// for nightly only features
if version_meta.channel == Channel::Nightly {
println!("cargo:rustc-cfg=nightly")
}
}

View File

@@ -37,14 +37,18 @@ where
#[allow(clippy::len_without_is_empty)]
#[allow(clippy::from_over_into)] // TODO(caspern): Go from From to Into.
impl<'a, T: 'a, const N: usize> Array<'a, T, N> {
/// # Safety
///
/// buf must be a contiguous array of `T`
///
/// # Panics
///
/// Panics if `buf.len()` is not `size_of::<T>() * N`
#[inline(always)]
pub fn new(buf: &'a [u8]) -> Self {
assert!(size_of::<T>() * N == buf.len());
pub unsafe fn new(buf: &'a [u8]) -> Self {
assert_eq!(size_of::<T>() * N, buf.len());
Array {
0: buf,
1: PhantomData,
}
Array(buf, PhantomData)
}
#[inline(always)]
@@ -61,34 +65,39 @@ impl<'a, T: Follow<'a> + 'a, const N: usize> Array<'a, T, N> {
pub fn get(&self, idx: usize) -> T::Inner {
assert!(idx < N);
let sz = size_of::<T>();
T::follow(self.0, sz * idx)
// Safety:
// self.0 was valid for length `N` on construction and have verified `idx < N`
unsafe { T::follow(self.0, sz * idx) }
}
#[inline(always)]
pub fn iter(&self) -> VectorIter<'a, T> {
VectorIter::from_slice(self.0, self.len())
// Safety:
// self.0 was valid for length N on construction
unsafe { VectorIter::from_slice(self.0, self.len()) }
}
}
impl<'a, T: Follow<'a> + Debug, const N: usize> Into<[T::Inner; N]> for Array<'a, T, N> {
#[inline(always)]
fn into(self) -> [T::Inner; N] {
array_init(|i| self.get(i))
impl<'a, T: Follow<'a> + Debug, const N: usize> From<Array<'a, T, N>> for [T::Inner; N] {
fn from(array: Array<'a, T, N>) -> Self {
array_init(|i| array.get(i))
}
}
// TODO(caspern): Implement some future safe version of SafeSliceAccess.
/// Implement Follow for all possible Arrays that have Follow-able elements.
impl<'a, T: Follow<'a> + 'a, const N: usize> Follow<'a> for Array<'a, T, N> {
type Inner = Array<'a, T, N>;
#[inline(always)]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
Array::new(&buf[loc..loc + N * size_of::<T>()])
}
}
pub fn emplace_scalar_array<T: EndianScalar, const N: usize>(
/// Place an array of EndianScalar into the provided mutable byte slice. Performs
/// endian conversion, if necessary.
/// # Safety
/// Caller must ensure `s.len() >= size_of::<[T; N]>()`
pub unsafe fn emplace_scalar_array<T: EndianScalar, const N: usize>(
buf: &mut [u8],
loc: usize,
src: &[T; N],
@@ -96,14 +105,12 @@ pub fn emplace_scalar_array<T: EndianScalar, const N: usize>(
let mut buf_ptr = buf[loc..].as_mut_ptr();
for item in src.iter() {
let item_le = item.to_little_endian();
unsafe {
core::ptr::copy_nonoverlapping(
&item_le as *const T as *const u8,
buf_ptr,
size_of::<T>(),
);
buf_ptr = buf_ptr.add(size_of::<T>());
}
core::ptr::copy_nonoverlapping(
&item_le as *const T::Scalar as *const u8,
buf_ptr,
size_of::<T::Scalar>(),
);
buf_ptr = buf_ptr.add(size_of::<T::Scalar>());
}
}
@@ -124,6 +131,8 @@ where
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
let mut ptr_i = array.as_mut_ptr() as *mut T;
// Safety:
// array is aligned by T, and has length N
unsafe {
for i in 0..N {
let value_i = initializer(i);
@@ -134,7 +143,7 @@ where
}
}
#[cfg(feature="serialize")]
#[cfg(feature = "serialize")]
impl<'a, T: 'a, const N: usize> serde::ser::Serialize for Array<'a, T, N>
where
T: 'a + Follow<'a>,

View File

@@ -14,26 +14,22 @@
* limitations under the License.
*/
extern crate smallvec;
#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
use core::cmp::max;
use core::iter::{DoubleEndedIterator, ExactSizeIterator};
use core::marker::PhantomData;
use core::ptr::write_bytes;
use core::slice::from_raw_parts;
#[cfg(feature = "no_std")]
use alloc::{vec, vec::Vec};
use crate::endian_scalar::{emplace_scalar, read_scalar_at};
use crate::endian_scalar::emplace_scalar;
use crate::primitives::*;
use crate::push::{Push, PushAlignment};
use crate::read_scalar;
use crate::table::Table;
use crate::vector::{SafeSliceAccess, Vector};
use crate::vector::Vector;
use crate::vtable::{field_index_to_field_offset, VTable};
use crate::vtable_writer::VTableWriter;
pub const N_SMALLVEC_STRING_VECTOR_CAPACITY: usize = 16;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct FieldLoc {
off: UOffsetT,
@@ -120,7 +116,9 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
// memset only the part of the buffer that could be dirty:
{
let to_clear = self.owned_buf.len() - self.head;
let ptr = (&mut self.owned_buf[self.head..]).as_mut_ptr();
let ptr = self.owned_buf[self.head..].as_mut_ptr();
// Safety:
// Verified ptr is valid for `to_clear` above
unsafe {
write_bytes(ptr, 0, to_clear);
}
@@ -152,8 +150,10 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
self.align(sz, P::alignment());
self.make_space(sz);
{
let (dst, rest) = (&mut self.owned_buf[self.head..]).split_at_mut(sz);
x.push(dst, rest);
let (dst, rest) = self.owned_buf[self.head..].split_at_mut(sz);
// Safety:
// Called make_space above
unsafe { x.push(dst, rest.len()) };
}
WIPOffset::new(self.used_space() as UOffsetT)
}
@@ -309,73 +309,32 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
WIPOffset::new(self.used_space() as UOffsetT)
}
/// Create a vector by memcpy'ing. This is much faster than calling
/// `create_vector`, but the underlying type must be represented as
/// little-endian on the host machine. This property is encoded in the
/// type system through the SafeSliceAccess trait. The following types are
/// always safe, on any platform: bool, u8, i8, and any
/// FlatBuffers-generated struct.
#[inline]
pub fn create_vector_direct<'a: 'b, 'b, T: SafeSliceAccess + Push + Sized + 'b>(
&'a mut self,
items: &'b [T],
) -> WIPOffset<Vector<'fbb, T>> {
self.assert_not_nested(
"create_vector_direct can not be called when a table or vector is under construction",
);
let elem_size = T::size();
self.align(items.len() * elem_size, T::alignment().max_of(SIZE_UOFFSET));
let bytes = {
let ptr = items.as_ptr() as *const T as *const u8;
unsafe { from_raw_parts(ptr, items.len() * elem_size) }
};
self.push_bytes_unprefixed(bytes);
self.push(items.len() as UOffsetT);
WIPOffset::new(self.used_space() as UOffsetT)
}
/// Create a vector of strings.
///
/// Speed-sensitive users may wish to reduce memory usage by creating the
/// vector manually: use `start_vector`, `push`, and `end_vector`.
#[inline]
pub fn create_vector_of_strings<'a, 'b>(
&'a mut self,
xs: &'b [&'b str],
) -> WIPOffset<Vector<'fbb, ForwardsUOffset<&'fbb str>>> {
self.assert_not_nested("create_vector_of_strings can not be called when a table or vector is under construction");
// internally, smallvec can be a stack-allocated or heap-allocated vector:
// if xs.len() > N_SMALLVEC_STRING_VECTOR_CAPACITY then it will overflow to the heap.
let mut offsets: smallvec::SmallVec<[WIPOffset<&str>; N_SMALLVEC_STRING_VECTOR_CAPACITY]> =
smallvec::SmallVec::with_capacity(xs.len());
unsafe {
offsets.set_len(xs.len());
}
// note that this happens in reverse, because the buffer is built back-to-front:
for (i, &s) in xs.iter().enumerate().rev() {
let o = self.create_string(s);
offsets[i] = o;
}
self.create_vector(&offsets[..])
}
/// Create a vector of Push-able objects.
///
/// Speed-sensitive users may wish to reduce memory usage by creating the
/// vector manually: use `start_vector`, `push`, and `end_vector`.
#[inline]
pub fn create_vector<'a: 'b, 'b, T: Push + Copy + 'b>(
pub fn create_vector<'a: 'b, 'b, T: Push + 'b>(
&'a mut self,
items: &'b [T],
) -> WIPOffset<Vector<'fbb, T::Output>> {
let elem_size = T::size();
self.align(items.len() * elem_size, T::alignment().max_of(SIZE_UOFFSET));
for i in (0..items.len()).rev() {
self.push(items[i]);
let slice_size = items.len() * elem_size;
self.align(slice_size, T::alignment().max_of(SIZE_UOFFSET));
self.ensure_capacity(slice_size + UOffsetT::size());
self.head -= slice_size;
let mut written_len = self.owned_buf.len() - self.head;
let buf = &mut self.owned_buf[self.head..self.head + slice_size];
for (item, out) in items.iter().zip(buf.chunks_exact_mut(elem_size)) {
written_len -= elem_size;
// Safety:
// Called ensure_capacity and aligned to T above
unsafe { item.push(out, written_len) };
}
WIPOffset::new(self.push::<UOffsetT>(items.len() as UOffsetT).value())
}
@@ -384,17 +343,18 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
/// Speed-sensitive users may wish to reduce memory usage by creating the
/// vector manually: use `start_vector`, `push`, and `end_vector`.
#[inline]
pub fn create_vector_from_iter<T: Push + Copy>(
pub fn create_vector_from_iter<T: Push>(
&mut self,
items: impl ExactSizeIterator<Item = T> + DoubleEndedIterator,
) -> WIPOffset<Vector<'fbb, T::Output>> {
let elem_size = T::size();
let len = items.len();
self.align(len * elem_size, T::alignment().max_of(SIZE_UOFFSET));
self.align(items.len() * elem_size, T::alignment().max_of(SIZE_UOFFSET));
let mut actual = 0;
for item in items.rev() {
self.push(item);
actual += 1;
}
WIPOffset::new(self.push::<UOffsetT>(len as UOffsetT).value())
WIPOffset::new(self.push::<UOffsetT>(actual).value())
}
/// Set whether default values are stored.
@@ -443,7 +403,15 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
assert_msg_name: &'static str,
) {
let idx = self.used_space() - tab_revloc.value() as usize;
let tab = Table::new(&self.owned_buf[self.head..], idx);
// Safety:
// The value of TableFinishedWIPOffset is the offset from the end of owned_buf
// to an SOffsetT pointing to a valid VTable
//
// `self.owned_buf.len() = self.used_space() + self.head`
// `self.owned_buf.len() - tab_revloc = self.used_space() - tab_revloc + self.head`
// `self.owned_buf.len() - tab_revloc = idx + self.head`
let tab = unsafe { Table::new(&self.owned_buf[self.head..], idx) };
let o = tab.vtable().get(slot_byte_loc) as usize;
assert!(o != 0, "missing required field {}", assert_msg_name);
}
@@ -560,11 +528,15 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
}
}
let new_vt_bytes = &self.owned_buf[vt_start_pos..vt_end_pos];
let found = self.written_vtable_revpos.binary_search_by(|old_vtable_revpos: &UOffsetT| {
let old_vtable_pos = self.owned_buf.len() - *old_vtable_revpos as usize;
let old_vtable = VTable::init(&self.owned_buf, old_vtable_pos);
new_vt_bytes.cmp(old_vtable.as_bytes())
});
let found = self
.written_vtable_revpos
.binary_search_by(|old_vtable_revpos: &UOffsetT| {
let old_vtable_pos = self.owned_buf.len() - *old_vtable_revpos as usize;
// Safety:
// Already written vtables are valid by construction
let old_vtable = unsafe { VTable::init(&self.owned_buf, old_vtable_pos) };
new_vt_bytes.cmp(old_vtable.as_bytes())
});
let final_vtable_revpos = match found {
Ok(i) => {
// The new vtable is a duplicate so clear it.
@@ -581,12 +553,22 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
};
// Write signed offset from table to its vtable.
let table_pos = self.owned_buf.len() - object_revloc_to_vtable.value() as usize;
let tmp_soffset_to_vt = unsafe { read_scalar_at::<UOffsetT>(&self.owned_buf, table_pos) };
debug_assert_eq!(tmp_soffset_to_vt, 0xF0F0_F0F0);
if cfg!(debug_assertions) {
// Safety:
// Verified slice length
let tmp_soffset_to_vt = unsafe {
read_scalar::<UOffsetT>(&self.owned_buf[table_pos..table_pos + SIZE_UOFFSET])
};
assert_eq!(tmp_soffset_to_vt, 0xF0F0_F0F0);
}
let buf = &mut self.owned_buf[table_pos..table_pos + SIZE_SOFFSET];
// Safety:
// Verified length of buf above
unsafe {
emplace_scalar::<SOffsetT>(
&mut self.owned_buf[table_pos..table_pos + SIZE_SOFFSET],
final_vtable_revpos as SOffsetT - object_revloc_to_vtable.value() as SOffsetT
buf,
final_vtable_revpos as SOffsetT - object_revloc_to_vtable.value() as SOffsetT,
);
}
@@ -623,7 +605,9 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
}
// finally, zero out the old end data.
{
let ptr = (&mut self.owned_buf[..middle]).as_mut_ptr();
let ptr = self.owned_buf[..middle].as_mut_ptr();
// Safety:
// ptr is byte aligned and of length middle
unsafe {
write_bytes(ptr, 0, middle);
}

View File

@@ -17,6 +17,24 @@
use core::mem::size_of;
mod private {
/// Types that are trivially transmutable are those where any combination of bits
/// represents a valid value of that type
///
/// For example integral types are TriviallyTransmutable as all bit patterns are valid,
/// however, `bool` is not trivially transmutable as only `0` and `1` are valid
pub trait TriviallyTransmutable {}
impl TriviallyTransmutable for i8 {}
impl TriviallyTransmutable for i16 {}
impl TriviallyTransmutable for i32 {}
impl TriviallyTransmutable for i64 {}
impl TriviallyTransmutable for u8 {}
impl TriviallyTransmutable for u16 {}
impl TriviallyTransmutable for u32 {}
impl TriviallyTransmutable for u64 {}
}
/// Trait for values that must be stored in little-endian byte order, but
/// might be represented in memory as big-endian. Every type that implements
/// EndianScalar is a valid FlatBuffers scalar value.
@@ -28,144 +46,118 @@ use core::mem::size_of;
/// "too much". For example, num-traits provides i128 support, but that is an
/// invalid FlatBuffers type.
pub trait EndianScalar: Sized + PartialEq + Copy + Clone {
fn to_little_endian(self) -> Self;
fn from_little_endian(self) -> Self;
}
type Scalar: private::TriviallyTransmutable;
/// Macro for implementing a no-op endian conversion. This is used for types
/// that are one byte wide.
macro_rules! impl_endian_scalar_noop {
($ty:ident) => {
impl EndianScalar for $ty {
#[inline]
fn to_little_endian(self) -> Self {
self
}
#[inline]
fn from_little_endian(self) -> Self {
self
}
}
};
fn to_little_endian(self) -> Self::Scalar;
fn from_little_endian(v: Self::Scalar) -> Self;
}
/// Macro for implementing an endian conversion using the stdlib `to_le` and
/// `from_le` functions. This is used for integer types. It is not used for
/// floats, because the `to_le` and `from_le` are not implemented for them in
/// the stdlib.
macro_rules! impl_endian_scalar_stdlib_le_conversion {
macro_rules! impl_endian_scalar {
($ty:ident) => {
impl EndianScalar for $ty {
type Scalar = Self;
#[inline]
fn to_little_endian(self) -> Self {
fn to_little_endian(self) -> Self::Scalar {
Self::to_le(self)
}
#[inline]
fn from_little_endian(self) -> Self {
Self::from_le(self)
fn from_little_endian(v: Self::Scalar) -> Self {
Self::from_le(v)
}
}
};
}
impl_endian_scalar_noop!(bool);
impl_endian_scalar_noop!(u8);
impl_endian_scalar_noop!(i8);
impl_endian_scalar!(u8);
impl_endian_scalar!(i8);
impl_endian_scalar!(u16);
impl_endian_scalar!(u32);
impl_endian_scalar!(u64);
impl_endian_scalar!(i16);
impl_endian_scalar!(i32);
impl_endian_scalar!(i64);
impl_endian_scalar_stdlib_le_conversion!(u16);
impl_endian_scalar_stdlib_le_conversion!(u32);
impl_endian_scalar_stdlib_le_conversion!(u64);
impl_endian_scalar_stdlib_le_conversion!(i16);
impl_endian_scalar_stdlib_le_conversion!(i32);
impl_endian_scalar_stdlib_le_conversion!(i64);
impl EndianScalar for bool {
type Scalar = u8;
fn to_little_endian(self) -> Self::Scalar {
self as u8
}
fn from_little_endian(v: Self::Scalar) -> Self {
v != 0
}
}
impl EndianScalar for f32 {
type Scalar = u32;
/// Convert f32 from host endian-ness to little-endian.
#[inline]
fn to_little_endian(self) -> Self {
#[cfg(target_endian = "little")]
{
self
}
#[cfg(not(target_endian = "little"))]
{
byte_swap_f32(self)
}
fn to_little_endian(self) -> u32 {
// Floats and Ints have the same endianness on all supported platforms.
// <https://doc.rust-lang.org/std/primitive.f32.html#method.from_bits>
self.to_bits().to_le()
}
/// Convert f32 from little-endian to host endian-ness.
#[inline]
fn from_little_endian(self) -> Self {
#[cfg(target_endian = "little")]
{
self
}
#[cfg(not(target_endian = "little"))]
{
byte_swap_f32(self)
}
fn from_little_endian(v: u32) -> Self {
// Floats and Ints have the same endianness on all supported platforms.
// <https://doc.rust-lang.org/std/primitive.f32.html#method.from_bits>
f32::from_bits(u32::from_le(v))
}
}
impl EndianScalar for f64 {
type Scalar = u64;
/// Convert f64 from host endian-ness to little-endian.
#[inline]
fn to_little_endian(self) -> Self {
#[cfg(target_endian = "little")]
{
self
}
#[cfg(not(target_endian = "little"))]
{
byte_swap_f64(self)
}
fn to_little_endian(self) -> u64 {
// Floats and Ints have the same endianness on all supported platforms.
// <https://doc.rust-lang.org/std/primitive.f64.html#method.from_bits>
self.to_bits().to_le()
}
/// Convert f64 from little-endian to host endian-ness.
#[inline]
fn from_little_endian(self) -> Self {
#[cfg(target_endian = "little")]
{
self
}
#[cfg(not(target_endian = "little"))]
{
byte_swap_f64(self)
}
fn from_little_endian(v: u64) -> Self {
// Floats and Ints have the same endianness on all supported platforms.
// <https://doc.rust-lang.org/std/primitive.f64.html#method.from_bits>
f64::from_bits(u64::from_le(v))
}
}
/// Swaps the bytes of an f32.
#[allow(dead_code)]
#[inline]
pub fn byte_swap_f32(x: f32) -> f32 {
f32::from_bits(x.to_bits().swap_bytes())
}
/// Swaps the bytes of an f64.
#[allow(dead_code)]
#[inline]
pub fn byte_swap_f64(x: f64) -> f64 {
f64::from_bits(x.to_bits().swap_bytes())
}
/// Place an EndianScalar into the provided mutable byte slice. Performs
/// endian conversion, if necessary.
/// # Safety
/// Caller must ensure `s.len() > size_of::<T>()`
/// and `x` does not overlap with `s`.
/// Caller must ensure `s.len() >= size_of::<T>()`
#[inline]
pub unsafe fn emplace_scalar<T: EndianScalar>(s: &mut [u8], x: T) {
let size = size_of::<T::Scalar>();
debug_assert!(
s.len() >= size,
"insufficient capacity for emplace_scalar, needed {} got {}",
size,
s.len()
);
let x_le = x.to_little_endian();
core::ptr::copy_nonoverlapping(
&x_le as *const T as *const u8,
&x_le as *const T::Scalar as *const u8,
s.as_mut_ptr() as *mut u8,
size_of::<T>(),
size,
);
}
/// Read an EndianScalar from the provided byte slice at the specified location.
/// Performs endian conversion, if necessary.
/// # Safety
/// Caller must ensure `s.len() > loc + size_of::<T>()`.
/// Caller must ensure `s.len() >= loc + size_of::<T>()`.
#[inline]
pub unsafe fn read_scalar_at<T: EndianScalar>(s: &[u8], loc: usize) -> T {
read_scalar(&s[loc..])
@@ -177,8 +169,16 @@ pub unsafe fn read_scalar_at<T: EndianScalar>(s: &[u8], loc: usize) -> T {
/// Caller must ensure `s.len() > size_of::<T>()`.
#[inline]
pub unsafe fn read_scalar<T: EndianScalar>(s: &[u8]) -> T {
let mut mem = core::mem::MaybeUninit::<T>::uninit();
let size = size_of::<T::Scalar>();
debug_assert!(
s.len() >= size,
"insufficient capacity for emplace_scalar, needed {} got {}",
size,
s.len()
);
let mut mem = core::mem::MaybeUninit::<T::Scalar>::uninit();
// Since [u8] has alignment 1, we copy it into T which may have higher alignment.
core::ptr::copy_nonoverlapping(s.as_ptr(), mem.as_mut_ptr() as *mut u8, size_of::<T>());
mem.assume_init().from_little_endian()
core::ptr::copy_nonoverlapping(s.as_ptr(), mem.as_mut_ptr() as *mut u8, size);
T::from_little_endian(mem.assume_init())
}

View File

@@ -29,7 +29,11 @@ use core::marker::PhantomData;
/// continue traversing the FlatBuffer.
pub trait Follow<'buf> {
type Inner;
fn follow(buf: &'buf [u8], loc: usize) -> Self::Inner;
/// # Safety
///
/// `buf[loc..]` must contain a valid value of `Self` and anything it
/// transitively refers to by offset must also be valid
unsafe fn follow(buf: &'buf [u8], loc: usize) -> Self::Inner;
}
/// FollowStart wraps a Follow impl in a struct type. This can make certain
@@ -39,17 +43,21 @@ pub struct FollowStart<T>(PhantomData<T>);
impl<'a, T: Follow<'a> + 'a> FollowStart<T> {
#[inline]
pub fn new() -> Self {
Self { 0: PhantomData }
Self(PhantomData)
}
/// # Safety
///
/// `buf[loc..]` must contain a valid value of `T`
#[inline]
pub fn self_follow(&'a self, buf: &'a [u8], loc: usize) -> T::Inner {
pub unsafe fn self_follow(&'a self, buf: &'a [u8], loc: usize) -> T::Inner {
T::follow(buf, loc)
}
}
impl<'a, T: Follow<'a>> Follow<'a> for FollowStart<T> {
type Inner = T::Inner;
#[inline]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
T::follow(buf, loc)
}
}

View File

@@ -45,6 +45,8 @@ where
{
let mut v = Verifier::new(opts, data);
<ForwardsUOffset<T>>::run_verifier(&mut v, 0)?;
// Safety:
// Run verifier above
Ok(unsafe { root_unchecked::<T>(data) })
}
@@ -75,6 +77,8 @@ where
{
let mut v = Verifier::new(opts, data);
<SkipSizePrefix<ForwardsUOffset<T>>>::run_verifier(&mut v, 0)?;
// Safety:
// Run verifier above
Ok(unsafe { size_prefixed_root_unchecked::<T>(data) })
}

View File

@@ -28,9 +28,10 @@
//! At this time, to generate Rust code, you will need the latest `master` version of `flatc`, available from here: <https://github.com/google/flatbuffers>
//! (On OSX, you can install FlatBuffers from `HEAD` with the Homebrew package manager.)
#![cfg_attr(feature = "no_std", no_std)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(all(nightly, not(feature = "std")), feature(error_in_core))]
#[cfg(feature = "no_std")]
#[cfg(not(feature = "std"))]
extern crate alloc;
mod array;
@@ -48,14 +49,12 @@ mod vtable_writer;
pub use crate::array::{array_init, emplace_scalar_array, Array};
pub use crate::builder::FlatBufferBuilder;
pub use crate::endian_scalar::{
byte_swap_f32, byte_swap_f64, emplace_scalar, read_scalar, read_scalar_at, EndianScalar,
};
pub use crate::endian_scalar::{emplace_scalar, read_scalar, read_scalar_at, EndianScalar};
pub use crate::follow::{Follow, FollowStart};
pub use crate::primitives::*;
pub use crate::push::Push;
pub use crate::table::{buffer_has_identifier, Table};
pub use crate::vector::{follow_cast_ref, SafeSliceAccess, Vector, VectorIter};
pub use crate::vector::{follow_cast_ref, Vector, VectorIter};
pub use crate::verifier::{
ErrorTraceDetail, InvalidFlatbuffer, SimpleToVerifyInSlice, Verifiable, Verifier,
VerifierOptions,
@@ -64,6 +63,4 @@ pub use crate::vtable::field_index_to_field_offset;
pub use bitflags;
pub use get_root::*;
// TODO(rw): Unify `create_vector` and `create_vector_direct` by using
// `Into<Vector<...>>`.
// TODO(rw): Split fill ops in builder into fill_small, fill_big like in C++.

View File

@@ -112,10 +112,7 @@ impl<'a, T: 'a> WIPOffset<T> {
/// Create a new WIPOffset.
#[inline]
pub fn new(o: UOffsetT) -> WIPOffset<T> {
WIPOffset {
0: o,
1: PhantomData,
}
WIPOffset(o, PhantomData)
}
/// Return a wrapped value that brings its meaning as a union WIPOffset
@@ -135,11 +132,9 @@ impl<T> Push for WIPOffset<T> {
type Output = ForwardsUOffset<T>;
#[inline(always)]
fn push(&self, dst: &mut [u8], rest: &[u8]) {
let n = (SIZE_UOFFSET + rest.len() - self.value() as usize) as UOffsetT;
unsafe {
emplace_scalar::<UOffsetT>(dst, n);
}
unsafe fn push(&self, dst: &mut [u8], written_len: usize) {
let n = (SIZE_UOFFSET + written_len - self.value() as usize) as UOffsetT;
emplace_scalar::<UOffsetT>(dst, n);
}
}
@@ -147,8 +142,8 @@ impl<T> Push for ForwardsUOffset<T> {
type Output = Self;
#[inline(always)]
fn push(&self, dst: &mut [u8], rest: &[u8]) {
self.value().push(dst, rest);
unsafe fn push(&self, dst: &mut [u8], written_len: usize) {
self.value().push(dst, written_len);
}
}
@@ -179,9 +174,9 @@ impl<T> ForwardsUOffset<T> {
impl<'a, T: Follow<'a>> Follow<'a> for ForwardsUOffset<T> {
type Inner = T::Inner;
#[inline(always)]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
let slice = &buf[loc..loc + SIZE_UOFFSET];
let off = unsafe { read_scalar::<u32>(slice) as usize };
let off = read_scalar::<u32>(slice) as usize;
T::follow(buf, loc + off)
}
}
@@ -200,9 +195,9 @@ impl<T> ForwardsVOffset<T> {
impl<'a, T: Follow<'a>> Follow<'a> for ForwardsVOffset<T> {
type Inner = T::Inner;
#[inline(always)]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
let slice = &buf[loc..loc + SIZE_VOFFSET];
let off = unsafe { read_scalar::<VOffsetT>(slice) as usize };
let off = read_scalar::<VOffsetT>(slice) as usize;
T::follow(buf, loc + off)
}
}
@@ -211,8 +206,8 @@ impl<T> Push for ForwardsVOffset<T> {
type Output = Self;
#[inline]
fn push(&self, dst: &mut [u8], rest: &[u8]) {
self.value().push(dst, rest);
unsafe fn push(&self, dst: &mut [u8], written_len: usize) {
self.value().push(dst, written_len);
}
}
@@ -230,9 +225,9 @@ impl<T> BackwardsSOffset<T> {
impl<'a, T: Follow<'a>> Follow<'a> for BackwardsSOffset<T> {
type Inner = T::Inner;
#[inline(always)]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
let slice = &buf[loc..loc + SIZE_SOFFSET];
let off = unsafe { read_scalar::<SOffsetT>(slice) };
let off = read_scalar::<SOffsetT>(slice);
T::follow(buf, (loc as SOffsetT - off) as usize)
}
}
@@ -241,8 +236,8 @@ impl<T> Push for BackwardsSOffset<T> {
type Output = Self;
#[inline]
fn push(&self, dst: &mut [u8], rest: &[u8]) {
self.value().push(dst, rest);
unsafe fn push(&self, dst: &mut [u8], written_len: usize) {
self.value().push(dst, written_len);
}
}
@@ -252,7 +247,7 @@ pub struct SkipSizePrefix<T>(PhantomData<T>);
impl<'a, T: Follow<'a> + 'a> Follow<'a> for SkipSizePrefix<T> {
type Inner = T::Inner;
#[inline(always)]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
T::follow(buf, loc + SIZE_SIZEPREFIX)
}
}
@@ -263,7 +258,7 @@ pub struct SkipRootOffset<T>(PhantomData<T>);
impl<'a, T: Follow<'a> + 'a> Follow<'a> for SkipRootOffset<T> {
type Inner = T::Inner;
#[inline(always)]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
T::follow(buf, loc + SIZE_UOFFSET)
}
}
@@ -274,7 +269,7 @@ pub struct FileIdentifier;
impl<'a> Follow<'a> for FileIdentifier {
type Inner = &'a [u8];
#[inline(always)]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
&buf[loc..loc + FILE_IDENTIFIER_LENGTH]
}
}
@@ -286,7 +281,7 @@ pub struct SkipFileIdentifier<T>(PhantomData<T>);
impl<'a, T: Follow<'a> + 'a> Follow<'a> for SkipFileIdentifier<T> {
type Inner = T::Inner;
#[inline(always)]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
T::follow(buf, loc + FILE_IDENTIFIER_LENGTH)
}
}
@@ -294,8 +289,8 @@ impl<'a, T: Follow<'a> + 'a> Follow<'a> for SkipFileIdentifier<T> {
impl<'a> Follow<'a> for bool {
type Inner = bool;
#[inline(always)]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe { read_scalar_at::<u8>(buf, loc) != 0 }
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
read_scalar_at::<u8>(buf, loc) != 0
}
}
@@ -309,8 +304,8 @@ macro_rules! impl_follow_for_endian_scalar {
impl<'a> Follow<'a> for $ty {
type Inner = $ty;
#[inline(always)]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe { read_scalar_at::<$ty>(buf, loc) }
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
read_scalar_at::<$ty>(buf, loc)
}
}
};

View File

@@ -24,7 +24,11 @@ use crate::endian_scalar::emplace_scalar;
/// types.
pub trait Push: Sized {
type Output;
fn push(&self, dst: &mut [u8], _rest: &[u8]);
/// # Safety
///
/// dst is aligned to [`Self::alignment`] and has length greater than or equal to [`Self::size`]
unsafe fn push(&self, dst: &mut [u8], written_len: usize);
#[inline]
fn size() -> usize {
size_of::<Self::Output>()
@@ -35,13 +39,29 @@ pub trait Push: Sized {
}
}
impl<'a, T: Push> Push for &'a T {
type Output = T::Output;
unsafe fn push(&self, dst: &mut [u8], written_len: usize) {
T::push(self, dst, written_len)
}
fn size() -> usize {
T::size()
}
fn alignment() -> PushAlignment {
T::alignment()
}
}
/// Ensure Push alignment calculations are typesafe (because this helps reduce
/// implementation issues when using FlatBufferBuilder::align).
pub struct PushAlignment(usize);
impl PushAlignment {
#[inline]
pub fn new(x: usize) -> Self {
PushAlignment { 0: x }
PushAlignment(x)
}
#[inline]
pub fn value(&self) -> usize {
@@ -60,10 +80,8 @@ macro_rules! impl_push_for_endian_scalar {
type Output = $ty;
#[inline]
fn push(&self, dst: &mut [u8], _rest: &[u8]) {
unsafe {
emplace_scalar::<$ty>(dst, *self);
}
unsafe fn push(&self, dst: &mut [u8], _written_len: usize) {
emplace_scalar::<$ty>(dst, *self);
}
}
};

View File

@@ -18,23 +18,36 @@ use crate::follow::Follow;
use crate::primitives::*;
use crate::vtable::VTable;
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Table<'a> {
pub buf: &'a [u8],
pub loc: usize,
buf: &'a [u8],
loc: usize,
}
impl<'a> Table<'a> {
/// # Safety
///
/// `buf` must contain a `soffset_t` at `loc`, which points to a valid vtable
#[inline]
pub fn new(buf: &'a [u8], loc: usize) -> Self {
pub unsafe fn new(buf: &'a [u8], loc: usize) -> Self {
Table { buf, loc }
}
#[inline]
pub fn vtable(&self) -> VTable<'a> {
<BackwardsSOffset<VTable<'a>>>::follow(self.buf, self.loc)
// Safety:
// Table::new is created with a valid buf and location
unsafe { <BackwardsSOffset<VTable<'a>>>::follow(self.buf, self.loc) }
}
/// Retrieves the value at the provided `slot_byte_loc` returning `default`
/// if no value present
///
/// # Safety
///
/// The value of the corresponding slot must have type T
#[inline]
pub fn get<T: Follow<'a> + 'a>(
pub unsafe fn get<T: Follow<'a> + 'a>(
&self,
slot_byte_loc: VOffsetT,
default: Option<T::Inner>,
@@ -50,19 +63,26 @@ impl<'a> Table<'a> {
impl<'a> Follow<'a> for Table<'a> {
type Inner = Table<'a>;
#[inline]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
Table { buf, loc }
}
}
/// Returns true if data contains a prefix of `ident`
#[inline]
pub fn buffer_has_identifier(data: &[u8], ident: &str, size_prefixed: bool) -> bool {
assert_eq!(ident.len(), FILE_IDENTIFIER_LENGTH);
let got = if size_prefixed {
<SkipSizePrefix<SkipRootOffset<FileIdentifier>>>::follow(data, 0)
assert!(data.len() >= SIZE_SIZEPREFIX + SIZE_UOFFSET + FILE_IDENTIFIER_LENGTH);
// Safety:
// Verified data has sufficient bytes
unsafe { <SkipSizePrefix<SkipRootOffset<FileIdentifier>>>::follow(data, 0) }
} else {
<SkipRootOffset<FileIdentifier>>::follow(data, 0)
assert!(data.len() >= SIZE_UOFFSET + FILE_IDENTIFIER_LENGTH);
// Safety:
// Verified data has sufficient bytes
unsafe { <SkipRootOffset<FileIdentifier>>::follow(data, 0) }
};
ident.as_bytes() == got

View File

@@ -17,13 +17,10 @@
use core::fmt::{Debug, Formatter, Result};
use core::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator};
use core::marker::PhantomData;
use core::mem::size_of;
use core::slice::from_raw_parts;
use core::mem::{align_of, size_of};
use core::str::from_utf8_unchecked;
use crate::endian_scalar::read_scalar_at;
#[cfg(target_endian = "little")]
use crate::endian_scalar::EndianScalar;
use crate::follow::Follow;
use crate::primitives::*;
@@ -55,6 +52,7 @@ where
// and Clone for `T: Copy` and `T: Clone` respectively. However `Vector<'a, T>`
// can always be copied, no matter that `T` you have.
impl<'a, T> Copy for Vector<'a, T> {}
impl<'a, T> Clone for Vector<'a, T> {
fn clone(&self) -> Self {
*self
@@ -62,32 +60,46 @@ impl<'a, T> Clone for Vector<'a, T> {
}
impl<'a, T: 'a> Vector<'a, T> {
/// # Safety
///
/// `buf` contains a valid vector at `loc` consisting of
///
/// - UOffsetT element count
/// - Consecutive list of `T` elements
#[inline(always)]
pub fn new(buf: &'a [u8], loc: usize) -> Self {
Vector {
0: buf,
1: loc,
2: PhantomData,
}
pub unsafe fn new(buf: &'a [u8], loc: usize) -> Self {
Vector(buf, loc, PhantomData)
}
#[inline(always)]
pub fn len(&self) -> usize {
// Safety:
// Valid vector at time of construction starting with UOffsetT element count
unsafe { read_scalar_at::<UOffsetT>(self.0, self.1) as usize }
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline(always)]
pub fn bytes(&self) -> &'a [u8] {
let sz = size_of::<T>();
let len = self.len();
&self.0[self.1 + SIZE_UOFFSET..self.1 + SIZE_UOFFSET + sz * len]
}
}
impl<'a, T: Follow<'a> + 'a> Vector<'a, T> {
#[inline(always)]
pub fn get(&self, idx: usize) -> T::Inner {
assert!(idx < self.len() as usize);
assert!(idx < self.len());
let sz = size_of::<T>();
debug_assert!(sz > 0);
T::follow(self.0, self.1 as usize + SIZE_UOFFSET + sz * idx)
// Safety:
// Valid vector at time of construction, verified that idx < element count
unsafe { T::follow(self.0, self.1 as usize + SIZE_UOFFSET + sz * idx) }
}
#[inline(always)]
@@ -96,84 +108,40 @@ impl<'a, T: Follow<'a> + 'a> Vector<'a, T> {
}
}
pub trait SafeSliceAccess {}
impl<'a, T: SafeSliceAccess + 'a> Vector<'a, T> {
pub fn safe_slice(self) -> &'a [T] {
let buf = self.0;
let loc = self.1;
let sz = size_of::<T>();
debug_assert!(sz > 0);
let len = unsafe { read_scalar_at::<UOffsetT>(buf, loc) } as usize;
let data_buf = &buf[loc + SIZE_UOFFSET..loc + SIZE_UOFFSET + len * sz];
let ptr = data_buf.as_ptr() as *const T;
let s: &'a [T] = unsafe { from_raw_parts(ptr, len) };
s
}
}
impl SafeSliceAccess for u8 {}
impl SafeSliceAccess for i8 {}
impl SafeSliceAccess for bool {}
// TODO(caspern): Get rid of this. Conditional compliation is unnecessary complexity.
// Vectors of primitives just don't work on big endian machines!!!
#[cfg(target_endian = "little")]
mod le_safe_slice_impls {
impl super::SafeSliceAccess for u16 {}
impl super::SafeSliceAccess for u32 {}
impl super::SafeSliceAccess for u64 {}
impl super::SafeSliceAccess for i16 {}
impl super::SafeSliceAccess for i32 {}
impl super::SafeSliceAccess for i64 {}
impl super::SafeSliceAccess for f32 {}
impl super::SafeSliceAccess for f64 {}
}
#[cfg(target_endian = "little")]
pub use self::le_safe_slice_impls::*;
pub fn follow_cast_ref<'a, T: Sized + 'a>(buf: &'a [u8], loc: usize) -> &'a T {
/// # Safety
///
/// `buf` must contain a value of T at `loc` and have alignment of 1
pub unsafe fn follow_cast_ref<'a, T: Sized + 'a>(buf: &'a [u8], loc: usize) -> &'a T {
assert_eq!(align_of::<T>(), 1);
let sz = size_of::<T>();
let buf = &buf[loc..loc + sz];
let ptr = buf.as_ptr() as *const T;
unsafe { &*ptr }
// SAFETY
// buf contains a value at loc of type T and T has no alignment requirements
&*ptr
}
impl<'a> Follow<'a> for &'a str {
type Inner = &'a str;
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
let len = unsafe { read_scalar_at::<UOffsetT>(buf, loc) } as usize;
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
let len = read_scalar_at::<UOffsetT>(buf, loc) as usize;
let slice = &buf[loc + SIZE_UOFFSET..loc + SIZE_UOFFSET + len];
unsafe { from_utf8_unchecked(slice) }
from_utf8_unchecked(slice)
}
}
#[cfg(target_endian = "little")]
fn follow_slice_helper<T>(buf: &[u8], loc: usize) -> &[T] {
let sz = size_of::<T>();
debug_assert!(sz > 0);
let len = unsafe { read_scalar_at::<UOffsetT>(buf, loc) as usize };
let data_buf = &buf[loc + SIZE_UOFFSET..loc + SIZE_UOFFSET + len * sz];
let ptr = data_buf.as_ptr() as *const T;
let s: &[T] = unsafe { from_raw_parts(ptr, len) };
s
}
/// Implement direct slice access if the host is little-endian.
#[cfg(target_endian = "little")]
impl<'a, T: EndianScalar> Follow<'a> for &'a [T] {
type Inner = &'a [T];
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
follow_slice_helper::<T>(buf, loc)
impl<'a> Follow<'a> for &'a [u8] {
type Inner = &'a [u8];
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
let len = read_scalar_at::<UOffsetT>(buf, loc) as usize;
&buf[loc + SIZE_UOFFSET..loc + SIZE_UOFFSET + len]
}
}
/// Implement Follow for all possible Vectors that have Follow-able elements.
impl<'a, T: Follow<'a> + 'a> Follow<'a> for Vector<'a, T> {
type Inner = Vector<'a, T>;
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
Vector::new(buf, loc)
}
}
@@ -201,8 +169,14 @@ impl<'a, T: 'a> VectorIter<'a, T> {
}
}
/// Creates a new `VectorIter` from the provided slice
///
/// # Safety
///
/// buf must contain a contiguous sequence of `items_num` values of `T`
///
#[inline]
pub fn from_slice(buf: &'a [u8], items_num: usize) -> Self {
pub unsafe fn from_slice(buf: &'a [u8], items_num: usize) -> Self {
VectorIter {
buf,
loc: 0,
@@ -235,7 +209,10 @@ impl<'a, T: Follow<'a> + 'a> Iterator for VectorIter<'a, T> {
if self.remaining == 0 {
None
} else {
let result = T::follow(self.buf, self.loc);
// Safety:
// VectorIter can only be created from a contiguous sequence of `items_num`
// And remaining is initialized to `items_num`
let result = unsafe { T::follow(self.buf, self.loc) };
self.loc += sz;
self.remaining -= 1;
Some(result)
@@ -272,7 +249,10 @@ impl<'a, T: Follow<'a> + 'a> DoubleEndedIterator for VectorIter<'a, T> {
None
} else {
self.remaining -= 1;
Some(T::follow(self.buf, self.loc + sz * self.remaining))
// Safety:
// VectorIter can only be created from a contiguous sequence of `items_num`
// And remaining is initialized to `items_num`
Some(unsafe { T::follow(self.buf, self.loc + sz * self.remaining) })
}
}
@@ -309,7 +289,7 @@ impl<'a, 'b, T: Follow<'a> + 'a> IntoIterator for &'b Vector<'a, T> {
}
}
#[cfg(feature="serialize")]
#[cfg(feature = "serialize")]
impl<'a, T> serde::ser::Serialize for Vector<'a, T>
where
T: 'a + Follow<'a>,

View File

@@ -1,14 +1,14 @@
#[cfg(feature = "no_std")]
use crate::follow::Follow;
use crate::{ForwardsUOffset, SOffsetT, SkipSizePrefix, UOffsetT, VOffsetT, Vector, SIZE_UOFFSET};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::ops::Range;
use core::option::Option;
use crate::follow::Follow;
use crate::{ForwardsUOffset, SOffsetT, SkipSizePrefix, UOffsetT, VOffsetT, Vector, SIZE_UOFFSET};
#[cfg(feature="no_std")]
use thiserror_core2::Error;
#[cfg(not(feature="no_std"))]
use thiserror::Error;
#[cfg(all(nightly, not(feature = "std")))]
use core::error::Error;
#[cfg(feature = "std")]
use std::error::Error;
/// Traces the location of data errors. Not populated for Dos detecting errors.
/// Useful for MissingRequiredField and Utf8Error in particular, though
@@ -28,8 +28,10 @@ pub enum ErrorTraceDetail {
position: usize,
},
}
#[derive(PartialEq, Eq, Default, Debug, Clone)]
pub struct ErrorTrace(Vec<ErrorTraceDetail>);
impl core::convert::AsRef<[ErrorTraceDetail]> for ErrorTrace {
#[inline]
fn as_ref(&self) -> &[ErrorTraceDetail] {
@@ -39,64 +41,138 @@ impl core::convert::AsRef<[ErrorTraceDetail]> for ErrorTrace {
/// Describes how a flatuffer is invalid and, for data errors, roughly where. No extra tracing
/// information is given for DoS detecting errors since it will probably be a lot.
#[derive(Clone, Error, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InvalidFlatbuffer {
#[error("Missing required field `{required}`.\n{error_trace}")]
MissingRequiredField {
required: &'static str,
error_trace: ErrorTrace,
},
#[error(
"Union exactly one of union discriminant (`{field_type}`) and value \
(`{field}`) are present.\n{error_trace}"
)]
InconsistentUnion {
field: &'static str,
field_type: &'static str,
error_trace: ErrorTrace,
},
#[error("Utf8 error for string in {range:?}: {error}\n{error_trace}")]
Utf8Error {
#[source]
error: core::str::Utf8Error,
range: Range<usize>,
error_trace: ErrorTrace,
},
#[error("String in range [{}, {}) is missing its null terminator.\n{error_trace}",
range.start, range.end)]
MissingNullTerminator {
range: Range<usize>,
error_trace: ErrorTrace,
},
#[error("Type `{unaligned_type}` at position {position} is unaligned.\n{error_trace}")]
Unaligned {
position: usize,
unaligned_type: &'static str,
error_trace: ErrorTrace,
},
#[error("Range [{}, {}) is out of bounds.\n{error_trace}", range.start, range.end)]
RangeOutOfBounds {
range: Range<usize>,
error_trace: ErrorTrace,
},
#[error(
"Signed offset at position {position} has value {soffset} which points out of bounds.\
\n{error_trace}"
)]
SignedOffsetOutOfBounds {
soffset: SOffsetT,
position: usize,
error_trace: ErrorTrace,
},
// Dos detecting errors. These do not get error traces since it will probably be very large.
#[error("Too many tables.")]
TooManyTables,
#[error("Apparent size too large.")]
ApparentSizeTooLarge,
#[error("Nested table depth limit reached.")]
DepthLimitReached,
}
#[cfg(any(nightly, feature = "std"))]
impl Error for InvalidFlatbuffer {
fn source(&self) -> Option<&(dyn Error + 'static)> {
if let InvalidFlatbuffer::Utf8Error { error: source, .. } = self {
Some(source)
} else {
None
}
}
}
impl core::fmt::Display for InvalidFlatbuffer {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
InvalidFlatbuffer::MissingRequiredField {
required,
error_trace,
} => {
writeln!(f, "Missing required field `{}`.\n{}", required, error_trace)?;
}
InvalidFlatbuffer::InconsistentUnion {
field,
field_type,
error_trace,
} => {
writeln!(
f,
"Exactly one of union discriminant (`{}`) and value (`{}`) are present.\n{}",
field_type, field, error_trace
)?;
}
InvalidFlatbuffer::Utf8Error {
error,
range,
error_trace,
} => {
writeln!(
f,
"Utf8 error for string in {:?}: {}\n{}",
range, error, error_trace
)?;
}
InvalidFlatbuffer::MissingNullTerminator { range, error_trace } => {
writeln!(
f,
"String in range [{}, {}) is missing its null terminator.\n{}",
range.start, range.end, error_trace
)?;
}
InvalidFlatbuffer::Unaligned {
position,
unaligned_type,
error_trace,
} => {
writeln!(
f,
"Type `{}` at position {} is unaligned.\n{}",
unaligned_type, position, error_trace
)?;
}
InvalidFlatbuffer::RangeOutOfBounds { range, error_trace } => {
writeln!(
f,
"Range [{}, {}) is out of bounds.\n{}",
range.start, range.end, error_trace
)?;
}
InvalidFlatbuffer::SignedOffsetOutOfBounds {
soffset,
position,
error_trace,
} => {
writeln!(
f,
"Signed offset at position {} has value {} which points out of bounds.\n{}",
position, soffset, error_trace
)?;
}
InvalidFlatbuffer::TooManyTables {} => {
writeln!(f, "Too many tables.")?;
}
InvalidFlatbuffer::ApparentSizeTooLarge {} => {
writeln!(f, "Apparent size too large.")?;
}
InvalidFlatbuffer::DepthLimitReached {} => {
writeln!(f, "Nested table depth limit reached.")?;
}
}
Ok(())
}
}
impl core::fmt::Display for ErrorTrace {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use ErrorTraceDetail::*;
@@ -184,6 +260,7 @@ fn trace_field<T>(res: Result<T>, field_name: &'static str, position: usize) ->
},
)
}
/// Adds a TableField trace detail if `res` is a data error.
fn trace_elem<T>(res: Result<T>, index: usize, position: usize) -> Result<T> {
append_trace(res, ErrorTraceDetail::VectorElement { index, position })
@@ -205,6 +282,7 @@ pub struct VerifierOptions {
// options to error un-recognized enums and unions? possible footgun.
// Ignore nested flatbuffers, etc?
}
impl Default for VerifierOptions {
fn default() -> Self {
Self {
@@ -226,6 +304,7 @@ pub struct Verifier<'opts, 'buf> {
num_tables: usize,
apparent_size: usize,
}
impl<'opts, 'buf> Verifier<'opts, 'buf> {
pub fn new(opts: &'opts VerifierOptions, buffer: &'buf [u8]) -> Self {
Self {
@@ -247,9 +326,12 @@ impl<'opts, 'buf> Verifier<'opts, 'buf> {
/// memory since `buffer: &[u8]` has alignment 1.
///
/// ### WARNING
///
/// This does not work for flatbuffers-structs as they have alignment 1 according to
/// `core::mem::align_of` but are meant to have higher alignment within a Flatbuffer w.r.t.
/// `buffer[0]`. TODO(caspern).
///
/// Note this does not impact soundness as this crate does not assume alignment of structs
#[inline]
fn is_aligned<T>(&self, pos: usize) -> Result<()> {
if pos % core::mem::align_of::<T>() == 0 {
@@ -307,9 +389,9 @@ impl<'opts, 'buf> Verifier<'opts, 'buf> {
// signed offsets are subtracted.
let derefed = if offset > 0 {
pos.checked_sub(offset.abs() as usize)
pos.checked_sub(offset.unsigned_abs() as usize)
} else {
pos.checked_add(offset.abs() as usize)
pos.checked_add(offset.unsigned_abs() as usize)
};
if let Some(x) = derefed {
if x < self.buffer.len() {
@@ -372,6 +454,7 @@ pub struct TableVerifier<'ver, 'opts, 'buf> {
// Verifier struct which holds the surrounding state and options.
verifier: &'ver mut Verifier<'opts, 'buf>,
}
impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> {
fn deref(&mut self, field: VOffsetT) -> Result<Option<usize>> {
let field = field as usize;
@@ -439,7 +522,9 @@ impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> {
}
(Some(k), Some(v)) => {
trace_field(Key::run_verifier(self.verifier, k), key_field_name, k)?;
let discriminant = Key::follow(self.verifier.buffer, k);
// Safety:
// Run verifier on `k` above
let discriminant = unsafe { Key::follow(self.verifier.buffer, k) };
trace_field(
verify_union(discriminant, self.verifier, v),
val_field_name,
@@ -486,16 +571,27 @@ fn verify_vector_range<T>(v: &mut Verifier, pos: usize) -> Result<core::ops::Ran
}
pub trait SimpleToVerifyInSlice {}
impl SimpleToVerifyInSlice for bool {}
impl SimpleToVerifyInSlice for i8 {}
impl SimpleToVerifyInSlice for u8 {}
impl SimpleToVerifyInSlice for i16 {}
impl SimpleToVerifyInSlice for u16 {}
impl SimpleToVerifyInSlice for i32 {}
impl SimpleToVerifyInSlice for u32 {}
impl SimpleToVerifyInSlice for f32 {}
impl SimpleToVerifyInSlice for i64 {}
impl SimpleToVerifyInSlice for u64 {}
impl SimpleToVerifyInSlice for f64 {}
impl<T: SimpleToVerifyInSlice> Verifiable for Vector<'_, T> {

View File

@@ -33,24 +33,42 @@ impl<'a> PartialEq for VTable<'a> {
}
impl<'a> VTable<'a> {
pub fn init(buf: &'a [u8], loc: usize) -> Self {
/// SAFETY
/// `buf` must contain a valid vtable at `loc`
///
/// This consists of a number of `VOffsetT`
/// - size of vtable in bytes including size element
/// - size of object in bytes including the vtable offset
/// - n fields where n is the number of fields in the table's schema when the code was compiled
pub unsafe fn init(buf: &'a [u8], loc: usize) -> Self {
VTable { buf, loc }
}
pub fn num_fields(&self) -> usize {
(self.num_bytes() / SIZE_VOFFSET) - 2
}
pub fn num_bytes(&self) -> usize {
// Safety:
// Valid VTable at time of construction
unsafe { read_scalar_at::<VOffsetT>(self.buf, self.loc) as usize }
}
pub fn object_inline_num_bytes(&self) -> usize {
// Safety:
// Valid VTable at time of construction
let n = unsafe { read_scalar_at::<VOffsetT>(self.buf, self.loc + SIZE_VOFFSET) };
n as usize
}
pub fn get_field(&self, idx: usize) -> VOffsetT {
// TODO(rw): distinguish between None and 0?
if idx > self.num_fields() {
return 0;
}
// Safety:
// Valid VTable at time of construction
unsafe {
read_scalar_at::<VOffsetT>(
self.buf,
@@ -58,13 +76,17 @@ impl<'a> VTable<'a> {
)
}
}
pub fn get(&self, byte_loc: VOffsetT) -> VOffsetT {
// TODO(rw): distinguish between None and 0?
if byte_loc as usize >= self.num_bytes() {
if byte_loc as usize + 2 > self.num_bytes() {
return 0;
}
// Safety:
// byte_loc is within bounds of vtable, which was valid at time of construction
unsafe { read_scalar_at::<VOffsetT>(self.buf, self.loc + byte_loc as usize) }
}
pub fn as_bytes(&self) -> &[u8] {
let len = self.num_bytes();
&self.buf[self.loc..self.loc + len]
@@ -87,7 +109,7 @@ pub fn field_offset_to_field_index(field_o: VOffsetT) -> VOffsetT {
impl<'a> Follow<'a> for VTable<'a> {
type Inner = VTable<'a>;
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
VTable::init(buf, loc)
}
}

View File

@@ -40,8 +40,11 @@ impl<'a> VTableWriter<'a> {
/// to the provided value.
#[inline(always)]
pub fn write_vtable_byte_length(&mut self, n: VOffsetT) {
let buf = &mut self.buf[..SIZE_VOFFSET];
// Safety:
// Validated range above
unsafe {
emplace_scalar::<VOffsetT>(&mut self.buf[..SIZE_VOFFSET], n);
emplace_scalar::<VOffsetT>(buf, n);
}
debug_assert_eq!(n as usize, self.buf.len());
}
@@ -49,8 +52,11 @@ impl<'a> VTableWriter<'a> {
/// Writes an object length (in bytes) into the vtable.
#[inline(always)]
pub fn write_object_inline_size(&mut self, n: VOffsetT) {
let buf = &mut self.buf[SIZE_VOFFSET..2 * SIZE_VOFFSET];
// Safety:
// Validated range above
unsafe {
emplace_scalar::<VOffsetT>(&mut self.buf[SIZE_VOFFSET..2 * SIZE_VOFFSET], n);
emplace_scalar::<VOffsetT>(buf, n);
}
}
@@ -61,8 +67,11 @@ impl<'a> VTableWriter<'a> {
#[inline(always)]
pub fn write_field_offset(&mut self, vtable_offset: VOffsetT, object_data_offset: VOffsetT) {
let idx = vtable_offset as usize;
let buf = &mut self.buf[idx..idx + SIZE_VOFFSET];
// Safety:
// Validated range above
unsafe {
emplace_scalar::<VOffsetT>(&mut self.buf[idx..idx + SIZE_VOFFSET], object_data_offset);
emplace_scalar::<VOffsetT>(buf, object_data_offset);
}
}
@@ -73,6 +82,9 @@ impl<'a> VTableWriter<'a> {
// This is the closest thing to memset in Rust right now.
let len = self.buf.len();
let p = self.buf.as_mut_ptr() as *mut u8;
// Safety:
// p is byte aligned and of length `len`
unsafe {
write_bytes(p, 0, len);
}

View File

@@ -70,7 +70,7 @@ impl<'de> Buffer for &'de [u8] {
/// Based off of the `empty` function, allows override for optimization purposes.
#[inline]
fn empty_str() -> Self::BufferString {
&""
""
}
#[inline]

View File

@@ -49,7 +49,7 @@ impl<'a> MapBuilder<'a> {
// Nested vector.
let start = Some(self.builder.values.len());
VectorBuilder {
builder: &mut self.builder,
builder: self.builder,
start,
}
}
@@ -64,7 +64,7 @@ impl<'a> MapBuilder<'a> {
// Nested map.
let start = Some(self.builder.values.len());
MapBuilder {
builder: &mut self.builder,
builder: self.builder,
start,
}
}

View File

@@ -98,7 +98,7 @@ impl ser::Error for Error {
Self::Serde(format!("{}", msg))
}
}
impl<'a> ser::SerializeSeq for &mut FlexbufferSerializer {
impl ser::SerializeSeq for &mut FlexbufferSerializer {
type Ok = ();
type Error = Error;
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
@@ -113,14 +113,14 @@ impl<'a> ser::SerializeSeq for &mut FlexbufferSerializer {
}
// This is unlike a flexbuffers map which requires CString like keys.
// Its implemented as alternating keys and values (hopefully).
impl<'a> ser::SerializeMap for &'a mut FlexbufferSerializer {
impl ser::SerializeMap for &mut FlexbufferSerializer {
type Ok = ();
type Error = Error;
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
key.serialize(MapKeySerializer(&mut **self))
key.serialize(MapKeySerializer(self))
}
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
@@ -132,7 +132,7 @@ impl<'a> ser::SerializeMap for &'a mut FlexbufferSerializer {
self.end_map()
}
}
impl<'a> ser::SerializeTuple for &mut FlexbufferSerializer {
impl ser::SerializeTuple for &mut FlexbufferSerializer {
type Ok = ();
type Error = Error;
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
@@ -145,7 +145,7 @@ impl<'a> ser::SerializeTuple for &mut FlexbufferSerializer {
self.end_vector()
}
}
impl<'a> ser::SerializeTupleStruct for &mut FlexbufferSerializer {
impl ser::SerializeTupleStruct for &mut FlexbufferSerializer {
type Ok = ();
type Error = Error;
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
@@ -158,7 +158,7 @@ impl<'a> ser::SerializeTupleStruct for &mut FlexbufferSerializer {
self.end_vector()
}
}
impl<'a> ser::SerializeStruct for &mut FlexbufferSerializer {
impl ser::SerializeStruct for &mut FlexbufferSerializer {
type Ok = ();
type Error = Error;
fn serialize_field<T: ?Sized>(
@@ -176,7 +176,7 @@ impl<'a> ser::SerializeStruct for &mut FlexbufferSerializer {
self.end_map()
}
}
impl<'a> ser::SerializeTupleVariant for &mut FlexbufferSerializer {
impl ser::SerializeTupleVariant for &mut FlexbufferSerializer {
type Ok = ();
type Error = Error;
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
@@ -190,7 +190,7 @@ impl<'a> ser::SerializeTupleVariant for &mut FlexbufferSerializer {
self.end_map()
}
}
impl<'a> ser::SerializeStructVariant for &mut FlexbufferSerializer {
impl ser::SerializeStructVariant for &mut FlexbufferSerializer {
type Ok = ();
type Error = Error;
fn serialize_field<T: ?Sized>(

View File

@@ -39,7 +39,7 @@ impl<'a> VectorBuilder<'a> {
pub fn start_vector(&mut self) -> VectorBuilder {
let start = Some(self.builder.values.len());
VectorBuilder {
builder: &mut self.builder,
builder: self.builder,
start,
}
}
@@ -48,7 +48,7 @@ impl<'a> VectorBuilder<'a> {
pub fn start_map(&mut self) -> MapBuilder {
let start = Some(self.builder.values.len());
MapBuilder {
builder: &mut self.builder,
builder: self.builder,
start,
}
}

View File

@@ -29,7 +29,7 @@
// Serde with maps - field names and type names.
// Until flat/flexbuffers is on Rust v1.42, we cannot use the previously unstable matches! macro.
#![allow(clippy::unknown_clippy_lints)]
#![allow(unknown_lints)]
#![allow(clippy::match_like_matches_macro)]
#[macro_use]

View File

@@ -380,7 +380,7 @@ impl<B: Buffer> Reader<B> {
}
pub fn as_blob(&self) -> Blob<B> {
self.get_blob().unwrap_or(Blob(B::empty()))
self.get_blob().unwrap_or_else(|_| Blob(B::empty()))
}
/// Retrieves str pointer, errors if invalid UTF-8, or the provided index
@@ -580,8 +580,8 @@ impl<B: Buffer> Reader<B> {
/// Returns empty string if you're not trying to read a string.
pub fn as_str(&self) -> B::BufferString {
match self.fxb_type {
FlexBufferType::String => self.get_str().unwrap_or(B::empty_str()),
FlexBufferType::Key => self.get_key().unwrap_or(B::empty_str()),
FlexBufferType::String => self.get_str().unwrap_or_else(|_| B::empty_str()),
FlexBufferType::Key => self.get_key().unwrap_or_else(|_| B::empty_str()),
_ => B::empty_str(),
}
}

Binary file not shown.

View File

@@ -9,8 +9,8 @@
// Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible.
static_assert(FLATBUFFERS_VERSION_MAJOR == 22 &&
FLATBUFFERS_VERSION_MINOR == 9 &&
FLATBUFFERS_VERSION_REVISION == 24,
FLATBUFFERS_VERSION_MINOR == 10 &&
FLATBUFFERS_VERSION_REVISION == 26,
"Non-compatible flatbuffers version included");
namespace MyGame {

View File

@@ -31,7 +31,7 @@ public enum MyGame_Sample_Equipment: UInt8, Enum {
public struct MyGame_Sample_Vec3: NativeStruct {
static func validateVersion() { FlatBuffersVersion_22_9_24() }
static func validateVersion() { FlatBuffersVersion_22_10_26() }
private var _x: Float32
private var _y: Float32
@@ -56,7 +56,7 @@ public struct MyGame_Sample_Vec3: NativeStruct {
public struct MyGame_Sample_Vec3_Mutable: FlatBufferObject {
static func validateVersion() { FlatBuffersVersion_22_9_24() }
static func validateVersion() { FlatBuffersVersion_22_10_26() }
public var __buffer: ByteBuffer! { return _accessor.bb }
private var _accessor: Struct
@@ -72,7 +72,7 @@ public struct MyGame_Sample_Vec3_Mutable: FlatBufferObject {
public struct MyGame_Sample_Monster: FlatBufferObject {
static func validateVersion() { FlatBuffersVersion_22_9_24() }
static func validateVersion() { FlatBuffersVersion_22_10_26() }
public var __buffer: ByteBuffer! { return _accessor.bb }
private var _accessor: Table
@@ -162,7 +162,7 @@ public struct MyGame_Sample_Monster: FlatBufferObject {
public struct MyGame_Sample_Weapon: FlatBufferObject {
static func validateVersion() { FlatBuffersVersion_22_9_24() }
static func validateVersion() { FlatBuffersVersion_22_10_26() }
public var __buffer: ByteBuffer! { return _accessor.bb }
private var _accessor: Table

View File

@@ -59,10 +59,8 @@ impl core::fmt::Debug for Color {
impl<'a> flatbuffers::Follow<'a> for Color {
type Inner = Self;
#[inline]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
let b = unsafe {
flatbuffers::read_scalar_at::<i8>(buf, loc)
};
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
let b = flatbuffers::read_scalar_at::<i8>(buf, loc);
Self(b)
}
}
@@ -70,21 +68,21 @@ impl<'a> flatbuffers::Follow<'a> for Color {
impl flatbuffers::Push for Color {
type Output = Color;
#[inline]
fn push(&self, dst: &mut [u8], _rest: &[u8]) {
unsafe { flatbuffers::emplace_scalar::<i8>(dst, self.0); }
unsafe fn push(&self, dst: &mut [u8], _written_len: usize) {
flatbuffers::emplace_scalar::<i8>(dst, self.0);
}
}
impl flatbuffers::EndianScalar for Color {
type Scalar = i8;
#[inline]
fn to_little_endian(self) -> Self {
let b = i8::to_le(self.0);
Self(b)
fn to_little_endian(self) -> i8 {
self.0.to_le()
}
#[inline]
#[allow(clippy::wrong_self_convention)]
fn from_little_endian(self) -> Self {
let b = i8::from_le(self.0);
fn from_little_endian(v: i8) -> Self {
let b = i8::from_le(v);
Self(b)
}
}

View File

@@ -55,10 +55,8 @@ impl core::fmt::Debug for Equipment {
impl<'a> flatbuffers::Follow<'a> for Equipment {
type Inner = Self;
#[inline]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
let b = unsafe {
flatbuffers::read_scalar_at::<u8>(buf, loc)
};
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
let b = flatbuffers::read_scalar_at::<u8>(buf, loc);
Self(b)
}
}
@@ -66,21 +64,21 @@ impl<'a> flatbuffers::Follow<'a> for Equipment {
impl flatbuffers::Push for Equipment {
type Output = Equipment;
#[inline]
fn push(&self, dst: &mut [u8], _rest: &[u8]) {
unsafe { flatbuffers::emplace_scalar::<u8>(dst, self.0); }
unsafe fn push(&self, dst: &mut [u8], _written_len: usize) {
flatbuffers::emplace_scalar::<u8>(dst, self.0);
}
}
impl flatbuffers::EndianScalar for Equipment {
type Scalar = u8;
#[inline]
fn to_little_endian(self) -> Self {
let b = u8::to_le(self.0);
Self(b)
fn to_little_endian(self) -> u8 {
self.0.to_le()
}
#[inline]
#[allow(clippy::wrong_self_convention)]
fn from_little_endian(self) -> Self {
let b = u8::from_le(self.0);
fn from_little_endian(v: u8) -> Self {
let b = u8::from_le(v);
Self(b)
}
}

View File

@@ -19,8 +19,8 @@ pub struct Monster<'a> {
impl<'a> flatbuffers::Follow<'a> for Monster<'a> {
type Inner = Monster<'a>;
#[inline]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
Self { _tab: flatbuffers::Table { buf, loc } }
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
Self { _tab: flatbuffers::Table::new(buf, loc) }
}
}
@@ -41,7 +41,7 @@ impl<'a> Monster<'a> {
}
#[inline]
pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
Monster { _tab: table }
}
#[allow(unused_mut)]
@@ -73,7 +73,7 @@ impl<'a> Monster<'a> {
x.to_string()
});
let inventory = self.inventory().map(|x| {
x.to_vec()
x.into_iter().collect()
});
let color = self.color();
let weapons = self.weapons().map(|x| {
@@ -106,49 +106,84 @@ impl<'a> Monster<'a> {
#[inline]
pub fn pos(&self) -> Option<&'a Vec3> {
self._tab.get::<Vec3>(Monster::VT_POS, None)
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<Vec3>(Monster::VT_POS, None)}
}
#[inline]
pub fn mana(&self) -> i16 {
self._tab.get::<i16>(Monster::VT_MANA, Some(150)).unwrap()
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<i16>(Monster::VT_MANA, Some(150)).unwrap()}
}
#[inline]
pub fn hp(&self) -> i16 {
self._tab.get::<i16>(Monster::VT_HP, Some(100)).unwrap()
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<i16>(Monster::VT_HP, Some(100)).unwrap()}
}
#[inline]
pub fn name(&self) -> Option<&'a str> {
self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(Monster::VT_NAME, None)
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(Monster::VT_NAME, None)}
}
#[inline]
pub fn inventory(&self) -> Option<&'a [u8]> {
self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, u8>>>(Monster::VT_INVENTORY, None).map(|v| v.safe_slice())
pub fn inventory(&self) -> Option<flatbuffers::Vector<'a, u8>> {
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, u8>>>(Monster::VT_INVENTORY, None)}
}
#[inline]
pub fn color(&self) -> Color {
self._tab.get::<Color>(Monster::VT_COLOR, Some(Color::Blue)).unwrap()
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<Color>(Monster::VT_COLOR, Some(Color::Blue)).unwrap()}
}
#[inline]
pub fn weapons(&self) -> Option<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Weapon<'a>>>> {
self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Weapon>>>>(Monster::VT_WEAPONS, None)
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<Weapon>>>>(Monster::VT_WEAPONS, None)}
}
#[inline]
pub fn equipped_type(&self) -> Equipment {
self._tab.get::<Equipment>(Monster::VT_EQUIPPED_TYPE, Some(Equipment::NONE)).unwrap()
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<Equipment>(Monster::VT_EQUIPPED_TYPE, Some(Equipment::NONE)).unwrap()}
}
#[inline]
pub fn equipped(&self) -> Option<flatbuffers::Table<'a>> {
self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Table<'a>>>(Monster::VT_EQUIPPED, None)
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Table<'a>>>(Monster::VT_EQUIPPED, None)}
}
#[inline]
pub fn path(&self) -> Option<&'a [Vec3]> {
self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, Vec3>>>(Monster::VT_PATH, None).map(|v| v.safe_slice())
pub fn path(&self) -> Option<flatbuffers::Vector<'a, Vec3>> {
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<flatbuffers::Vector<'a, Vec3>>>(Monster::VT_PATH, None)}
}
#[inline]
#[allow(non_snake_case)]
pub fn equipped_as_weapon(&self) -> Option<Weapon<'a>> {
if self.equipped_type() == Equipment::Weapon {
self.equipped().map(Weapon::init_from_table)
self.equipped().map(|t| {
// Safety:
// Created from a valid Table for this object
// Which contains a valid union in this slot
unsafe { Weapon::init_from_table(t) }
})
} else {
None
}
@@ -365,18 +400,6 @@ impl MonsterT {
})
}
}
#[inline]
#[deprecated(since="2.0.0", note="Deprecated in favor of `root_as...` methods.")]
pub fn get_root_as_monster<'a>(buf: &'a [u8]) -> Monster<'a> {
unsafe { flatbuffers::root_unchecked::<Monster<'a>>(buf) }
}
#[inline]
#[deprecated(since="2.0.0", note="Deprecated in favor of `root_as...` methods.")]
pub fn get_size_prefixed_root_as_monster<'a>(buf: &'a [u8]) -> Monster<'a> {
unsafe { flatbuffers::size_prefixed_root_unchecked::<Monster<'a>>(buf) }
}
#[inline]
/// Verifies that a buffer of bytes contains a `Monster`
/// and returns it.

View File

@@ -29,39 +29,25 @@ impl core::fmt::Debug for Vec3 {
}
impl flatbuffers::SimpleToVerifyInSlice for Vec3 {}
impl flatbuffers::SafeSliceAccess for Vec3 {}
impl<'a> flatbuffers::Follow<'a> for Vec3 {
type Inner = &'a Vec3;
#[inline]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
<&'a Vec3>::follow(buf, loc)
}
}
impl<'a> flatbuffers::Follow<'a> for &'a Vec3 {
type Inner = &'a Vec3;
#[inline]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
flatbuffers::follow_cast_ref::<Vec3>(buf, loc)
}
}
impl<'b> flatbuffers::Push for Vec3 {
type Output = Vec3;
#[inline]
fn push(&self, dst: &mut [u8], _rest: &[u8]) {
let src = unsafe {
::core::slice::from_raw_parts(self as *const Vec3 as *const u8, Self::size())
};
dst.copy_from_slice(src);
}
}
impl<'b> flatbuffers::Push for &'b Vec3 {
type Output = Vec3;
#[inline]
fn push(&self, dst: &mut [u8], _rest: &[u8]) {
let src = unsafe {
::core::slice::from_raw_parts(*self as *const Vec3 as *const u8, Self::size())
};
unsafe fn push(&self, dst: &mut [u8], _written_len: usize) {
let src = ::core::slice::from_raw_parts(self as *const Vec3 as *const u8, Self::size());
dst.copy_from_slice(src);
}
}
@@ -95,70 +81,88 @@ impl<'a> Vec3 {
}
pub fn x(&self) -> f32 {
let mut mem = core::mem::MaybeUninit::<f32>::uninit();
unsafe {
let mut mem = core::mem::MaybeUninit::<<f32 as EndianScalar>::Scalar>::uninit();
// Safety:
// Created from a valid Table for this object
// Which contains a valid value in this slot
EndianScalar::from_little_endian(unsafe {
core::ptr::copy_nonoverlapping(
self.0[0..].as_ptr(),
mem.as_mut_ptr() as *mut u8,
core::mem::size_of::<f32>(),
core::mem::size_of::<<f32 as EndianScalar>::Scalar>(),
);
mem.assume_init()
}.from_little_endian()
})
}
pub fn set_x(&mut self, x: f32) {
let x_le = x.to_little_endian();
// Safety:
// Created from a valid Table for this object
// Which contains a valid value in this slot
unsafe {
core::ptr::copy_nonoverlapping(
&x_le as *const f32 as *const u8,
&x_le as *const _ as *const u8,
self.0[0..].as_mut_ptr(),
core::mem::size_of::<f32>(),
core::mem::size_of::<<f32 as EndianScalar>::Scalar>(),
);
}
}
pub fn y(&self) -> f32 {
let mut mem = core::mem::MaybeUninit::<f32>::uninit();
unsafe {
let mut mem = core::mem::MaybeUninit::<<f32 as EndianScalar>::Scalar>::uninit();
// Safety:
// Created from a valid Table for this object
// Which contains a valid value in this slot
EndianScalar::from_little_endian(unsafe {
core::ptr::copy_nonoverlapping(
self.0[4..].as_ptr(),
mem.as_mut_ptr() as *mut u8,
core::mem::size_of::<f32>(),
core::mem::size_of::<<f32 as EndianScalar>::Scalar>(),
);
mem.assume_init()
}.from_little_endian()
})
}
pub fn set_y(&mut self, x: f32) {
let x_le = x.to_little_endian();
// Safety:
// Created from a valid Table for this object
// Which contains a valid value in this slot
unsafe {
core::ptr::copy_nonoverlapping(
&x_le as *const f32 as *const u8,
&x_le as *const _ as *const u8,
self.0[4..].as_mut_ptr(),
core::mem::size_of::<f32>(),
core::mem::size_of::<<f32 as EndianScalar>::Scalar>(),
);
}
}
pub fn z(&self) -> f32 {
let mut mem = core::mem::MaybeUninit::<f32>::uninit();
unsafe {
let mut mem = core::mem::MaybeUninit::<<f32 as EndianScalar>::Scalar>::uninit();
// Safety:
// Created from a valid Table for this object
// Which contains a valid value in this slot
EndianScalar::from_little_endian(unsafe {
core::ptr::copy_nonoverlapping(
self.0[8..].as_ptr(),
mem.as_mut_ptr() as *mut u8,
core::mem::size_of::<f32>(),
core::mem::size_of::<<f32 as EndianScalar>::Scalar>(),
);
mem.assume_init()
}.from_little_endian()
})
}
pub fn set_z(&mut self, x: f32) {
let x_le = x.to_little_endian();
// Safety:
// Created from a valid Table for this object
// Which contains a valid value in this slot
unsafe {
core::ptr::copy_nonoverlapping(
&x_le as *const f32 as *const u8,
&x_le as *const _ as *const u8,
self.0[8..].as_mut_ptr(),
core::mem::size_of::<f32>(),
core::mem::size_of::<<f32 as EndianScalar>::Scalar>(),
);
}
}

View File

@@ -19,8 +19,8 @@ pub struct Weapon<'a> {
impl<'a> flatbuffers::Follow<'a> for Weapon<'a> {
type Inner = Weapon<'a>;
#[inline]
fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
Self { _tab: flatbuffers::Table { buf, loc } }
unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
Self { _tab: flatbuffers::Table::new(buf, loc) }
}
}
@@ -33,7 +33,7 @@ impl<'a> Weapon<'a> {
}
#[inline]
pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
Weapon { _tab: table }
}
#[allow(unused_mut)]
@@ -60,11 +60,17 @@ impl<'a> Weapon<'a> {
#[inline]
pub fn name(&self) -> Option<&'a str> {
self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(Weapon::VT_NAME, None)
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(Weapon::VT_NAME, None)}
}
#[inline]
pub fn damage(&self) -> i16 {
self._tab.get::<i16>(Weapon::VT_DAMAGE, Some(0)).unwrap()
// Safety:
// Created from valid Table for this object
// which contains a valid value in this slot
unsafe { self._tab.get::<i16>(Weapon::VT_DAMAGE, Some(0)).unwrap()}
}
}

View File

@@ -19,7 +19,7 @@ extern crate flatbuffers;
// import the generated code
#[allow(dead_code, unused_imports)]
#[allow(clippy::approx_constant)] // We use low precision PI as a default value.
#[allow(clippy::all)]
mod rust_generated;
pub use rust_generated::my_game::sample::{Color, Equipment,
Monster, MonsterArgs,
@@ -122,10 +122,7 @@ fn main() {
// Get an element from the `inventory` FlatBuffer's `vector`.
assert!(monster.inventory().is_some());
let inv = monster.inventory().unwrap();
// Note that this vector is returned as a slice, because direct access for
// this type, a u8 vector, is safe on all platforms:
let third_item = inv[2];
let third_item = inv.get(2);
assert_eq!(third_item, 2);
// Get and test the `weapons` FlatBuffers's `vector`.

View File

@@ -147,7 +147,7 @@ TS_OPTS = ["--ts", "--gen-name-strings"]
LOBSTER_OPTS = ["--lobster"]
SWIFT_OPTS = ["--swift", "--gen-json-emit", "--bfbs-filenames", str(tests_path)]
SWIFT_OPTS_CODE_GEN = [
"--swift",
"--swift",
"--gen-json-emit",
"--bfbs-filenames",
swift_code_gen
@@ -305,14 +305,14 @@ flatc(
# Generate the annotated binary of the monster_test binary schema.
flatc_annotate(
schema="../reflection/reflection.fbs",
file="monster_test.bfbs",
schema="../reflection/reflection.fbs",
file="monster_test.bfbs",
include="include_test"
)
flatc_annotate(
schema="monster_test.fbs",
file="monsterdata_test.mon",
schema="monster_test.fbs",
file="monsterdata_test.mon",
include="include_test"
)
@@ -371,6 +371,12 @@ flatc(
)
flatc(
BASE_OPTS + PYTHON_OPTS,
schema="nested_union_test.fbs",
)
# Optional Scalars
optional_scalars_schema = "optional_scalars.fbs"
flatc(["--java", "--kotlin", "--lobster"], schema=optional_scalars_schema)
@@ -449,6 +455,13 @@ flatc(
cwd=swift_code_gen
)
# Nim Tests
NIM_OPTS = BASE_OPTS + ["--nim"]
flatc(NIM_OPTS, schema="monster_test.fbs", include="include_test")
flatc(NIM_OPTS, schema="optional_scalars.fbs")
flatc(NIM_OPTS, schema="more_defaults.fbs")
flatc(NIM_OPTS, schema="MutatingBool.fbs")
# --filename-suffix and --filename-ext tests
flatc(
CPP_OPTS + NO_INCL_OPTS + ["--grpc", "--filename-ext", "hpp"],

81
scripts/release.sh Executable file
View File

@@ -0,0 +1,81 @@
printf -v year '%(%y)T' -1
printf -v month '%(%m)T' -1
printf -v day '%(%d)T' -1
version="$year.$month.$day"
version_underscore="$year\_$month\_$day"
echo "Setting Flatbuffers Version to: $version"
echo "Updating include/flatbuffers/base.h..."
sed -i \
-e "s/\(#define FLATBUFFERS_VERSION_MAJOR \).*/\1$year/" \
-e "s/\(#define FLATBUFFERS_VERSION_MINOR \).*/\1$month/" \
-e "s/\(#define FLATBUFFERS_VERSION_REVISION \).*/\1$day/" \
include/flatbuffers/base.h
echo "Updating CMake\Version.cmake..."
sed -i \
-e "s/\(set(VERSION_MAJOR \).*/\1$year)/" \
-e "s/\(set(VERSION_MINOR \).*/\1$month)/" \
-e "s/\(set(VERSION_PATCH \).*/\1$day)/" \
CMake/Version.cmake
echo "Updating include/flatbuffers/reflection_generated.h..."
echo "Updating tests/evolution_test/evolution_v1_generated.h..."
echo "Updating tests/evolution_test/evolution_v1_generated.h..."
sed -i \
-e "s/\(FLATBUFFERS_VERSION_MAJOR == \)[0-9]*\(.*\)/\1$year\2/" \
-e "s/\(FLATBUFFERS_VERSION_MINOR == \)[0-9]*\(.*\)/\1$month\2/" \
-e "s/\(FLATBUFFERS_VERSION_REVISION == \)[0-9]*\(.*\)/\1$day\2/" \
include/flatbuffers/reflection_generated.h \
tests/evolution_test/evolution_v1_generated.h \
tests/evolution_test/evolution_v2_generated.h
echo "Updating java/pom.xml..."
xmlstarlet edit --inplace -N s=http://maven.apache.org/POM/4.0.0 \
--update '//s:project/s:version' --value $version \
java/pom.xml
echo "Updating package.json..."
sed -i \
-e "s/\(\"version\": \).*/\1\"$version\",/" \
package.json
echo "Updating net/FlatBuffers/Google.FlatBuffers.csproj..."
sed -i \
-e "s/\(<PackageVersion>\).*\(<\/PackageVersion>\)/\1$version\2/" \
net/FlatBuffers/Google.FlatBuffers.csproj
echo "Updating dart/pubspec.yaml..."
sed -i \
-e "s/\(version: \).*/\1$version/" \
dart/pubspec.yaml
echo "Updating python/flatbuffers/_version.py..."
sed -i \
-e "s/\(__version__ = u\).*/\1\"$version\"/" \
python/flatbuffers/_version.py
echo "Updating python/setup.py..."
sed -i \
-e "s/\(version='\).*/\1$version',/" \
python/setup.py
echo "Updating FlatBuffers.podspec..."
sed -i \
-e "s/\(s.version\s*= \).*/\1'$version'/" \
FlatBuffers.podspec
echo "Updating FlatBuffersVersion_X_X_X() version check...."
grep -rl 'FlatBuffersVersion_' * --exclude=release.sh | xargs -i@ \
sed -i \
-e "s/\(FlatBuffersVersion_\).*()/\1$version_underscore()/g" \
@
echo "Updating FLATBUFFERS_X_X_X() version check...."
grep -rl 'FLATBUFFERS_\d*' * --exclude=release.sh | xargs -i@ \
sed -i \
-e "s/\(FLATBUFFERS_\)[0-9]\{2\}.*()/\1$version_underscore()/g" \
@

View File

@@ -39,6 +39,8 @@ cc_library(
"bfbs_gen.h",
"bfbs_gen_lua.cpp",
"bfbs_gen_lua.h",
"bfbs_gen_nim.cpp",
"bfbs_gen_nim.h",
"bfbs_namer.h",
"binary_annotator.cpp",
"binary_annotator.h",
@@ -62,6 +64,8 @@ cc_library(
"bfbs_gen.h",
"bfbs_gen_lua.cpp",
"bfbs_gen_lua.h",
"bfbs_gen_nim.cpp",
"bfbs_gen_nim.h",
"bfbs_namer.h",
"flatc_main.cpp",
"idl_gen_cpp.cpp",

View File

@@ -38,8 +38,9 @@ static void ForAllObjects(
for (auto it = objects->cbegin(); it != objects->cend(); ++it) { func(*it); }
}
static void ForAllEnumValues(const reflection::Enum *enum_def,
std::function<void(const reflection::EnumVal *)> func) {
static void ForAllEnumValues(
const reflection::Enum *enum_def,
std::function<void(const reflection::EnumVal *)> func) {
for (auto it = enum_def->values()->cbegin(); it != enum_def->values()->cend();
++it) {
func(*it);
@@ -91,7 +92,7 @@ static bool IsVector(const reflection::BaseType base_type) {
return base_type == reflection::Vector;
}
} // namespace
} // namespace
// A concrete base Flatbuffer Generator that specific language generators can
// derive from.
@@ -130,17 +131,29 @@ class BaseBfbsGenerator : public BfbsGenerator {
}
protected:
const reflection::Object *GetObject(const reflection::Type *type) const {
if (type->index() >= 0 && IsStructOrTable(type->base_type())) {
// GetObject returns the underlying object struct of the given type
// if element_type is true and GetObject is a list of objects then
// GetObject will correctly return the object struct of the vector's elements
const reflection::Object *GetObject(const reflection::Type *type,
bool element_type = false) const {
const reflection::BaseType base_type =
element_type ? type->element() : type->base_type();
if (type->index() >= 0 && IsStructOrTable(base_type)) {
return GetObjectByIndex(type->index());
}
return nullptr;
}
const reflection::Enum *GetEnum(const reflection::Type *type) const {
// GetEnum returns the underlying enum struct of the given type
// if element_type is true and GetEnum is a list of enums then
// GetEnum will correctly return the enum struct of the vector's elements
const reflection::Enum *GetEnum(const reflection::Type *type,
bool element_type = false) const {
const reflection::BaseType base_type =
element_type ? type->element() : type->base_type();
// TODO(derekbailey): it would be better to have a explicit list of allowed
// base types, instead of negating Obj types.
if (type->index() >= 0 && !IsStructOrTable(type->base_type())) {
if (type->index() >= 0 && !IsStructOrTable(base_type)) {
return GetEnumByIndex(type->index());
}
return nullptr;

View File

@@ -175,7 +175,7 @@ class LuaBfbsGenerator : public BaseBfbsGenerator {
// Skip writing deprecated fields altogether.
if (field->deprecated()) { return; }
const std::string field_name = namer_.Field(field->name()->str());
const std::string field_name = namer_.Field(*field);
const r::BaseType base_type = field->type()->base_type();
// Generate some fixed strings so we don't repeat outselves later.
@@ -367,9 +367,8 @@ class LuaBfbsGenerator : public BaseBfbsGenerator {
ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
if (field->deprecated()) { return; }
const std::string field_name = namer_.Field(field->name()->str());
const std::string variable_name =
namer_.Variable(field->name()->str());
const std::string field_name = namer_.Field(*field);
const std::string variable_name = namer_.Variable(*field);
code += "function " + object_name + ".Add" + field_name +
"(builder, " + variable_name + ")\n";
@@ -428,9 +427,9 @@ class LuaBfbsGenerator : public BaseBfbsGenerator {
if (IsStructOrTable(field->type()->base_type())) {
const r::Object *field_object = GetObject(field->type());
signature += GenerateStructBuilderArgs(
field_object, prefix + namer_.Variable(field->name()->str()) + "_");
field_object, prefix + namer_.Variable(*field) + "_");
} else {
signature += ", " + prefix + namer_.Variable(field->name()->str());
signature += ", " + prefix + namer_.Variable(*field);
}
});
return signature;
@@ -451,11 +450,11 @@ class LuaBfbsGenerator : public BaseBfbsGenerator {
}
if (IsStructOrTable(field->type()->base_type())) {
const r::Object *field_object = GetObject(field->type());
code += AppendStructBuilderBody(
field_object, prefix + namer_.Variable(field->name()->str()) + "_");
code += AppendStructBuilderBody(field_object,
prefix + namer_.Variable(*field) + "_");
} else {
code += " builder:Prepend" + GenerateMethod(field) + "(" + prefix +
namer_.Variable(field->name()->str()) + ")\n";
namer_.Variable(*field) + ")\n";
}
});

640
src/bfbs_gen_nim.cpp Normal file
View File

@@ -0,0 +1,640 @@
/*
* Copyright 2021 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.
*/
#include "bfbs_gen_nim.h"
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>
// Ensure no includes to flatc internals. bfbs_gen.h and generator.h are OK.
#include "bfbs_gen.h"
#include "bfbs_namer.h"
#include "flatbuffers/bfbs_generator.h"
// The intermediate representation schema.
#include "flatbuffers/reflection.h"
#include "flatbuffers/reflection_generated.h"
namespace flatbuffers {
namespace {
// To reduce typing
namespace r = ::reflection;
std::set<std::string> NimKeywords() {
return {
"addr", "and", "as", "asm", "bind", "block",
"break", "case", "cast", "concept", "const", "continue",
"converter", "defer", "discard", "distinct", "div", "do",
"elif", "else", "end", "enum", "except", "export",
"finally", "for", "from", "func", "if", "import",
"in", "include", "interface", "is", "isnot", "iterator",
"let", "macro", "method", "mixin", "mod", "nil",
"not", "notin", "object", "of", "or", "out",
"proc", "ptr", "raise", "ref", "return", "shl",
"shr", "static", "template", "try", "tuple", "type",
"using", "var", "when", "while", "xor", "yield",
};
}
Namer::Config NimDefaultConfig() {
return { /*types=*/Case::kUpperCamel,
/*constants=*/Case::kUpperCamel,
/*methods=*/Case::kLowerCamel,
/*functions=*/Case::kUpperCamel,
/*fields=*/Case::kLowerCamel,
/*variable=*/Case::kLowerCamel,
/*variants=*/Case::kUpperCamel,
/*enum_variant_seperator=*/".",
/*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
/*namespaces=*/Case::kKeep,
/*namespace_seperator=*/"/",
/*object_prefix=*/"",
/*object_suffix=*/"T",
/*keyword_prefix=*/"",
/*keyword_suffix=*/"_",
/*filenames=*/Case::kKeep,
/*directories=*/Case::kKeep,
/*output_path=*/"",
/*filename_suffix=*/"",
/*filename_extension=*/".nim" };
}
const std::string Indent = " ";
const std::string Export = "*";
const std::set<std::string> builtin_types = {
"uint8", "uint8", "bool", "int8", "uint8", "int16",
"uint16", "int32", "uint32", "int64", "uint64", "float32",
"float64", "string", "int", "uint", "uoffset", "Builder"
};
class NimBfbsGenerator : public BaseBfbsGenerator {
public:
explicit NimBfbsGenerator(const std::string &flatc_version)
: BaseBfbsGenerator(),
keywords_(),
imports_(),
current_obj_(nullptr),
current_enum_(nullptr),
flatc_version_(flatc_version),
namer_(NimDefaultConfig(), NimKeywords()) {}
GeneratorStatus GenerateFromSchema(const r::Schema *schema)
FLATBUFFERS_OVERRIDE {
ForAllEnums(schema->enums(), [&](const r::Enum *enum_def) {
StartCodeBlock(enum_def);
GenerateEnum(enum_def);
});
ForAllObjects(schema->objects(), [&](const r::Object *object) {
StartCodeBlock(object);
GenerateObject(object);
});
return OK;
}
uint64_t SupportedAdvancedFeatures() const FLATBUFFERS_OVERRIDE {
return r::AdvancedArrayFeatures | r::AdvancedUnionFeatures |
r::OptionalScalars | r::DefaultVectorsAndStrings;
}
protected:
void GenerateEnum(const r::Enum *enum_def) {
std::string code;
std::string ns;
const std::string enum_name = namer_.Type(namer_.Denamespace(enum_def, ns));
const std::string enum_type =
GenerateTypeBasic(enum_def->underlying_type());
GenerateDocumentation(enum_def->documentation(), "", code);
code += "type " + enum_name + Export + "{.pure.} = enum\n";
ForAllEnumValues(enum_def, [&](const reflection::EnumVal *enum_val) {
GenerateDocumentation(enum_val->documentation(), " ", code);
code += " " + namer_.Variant(enum_val->name()->str()) + " = " +
NumToString(enum_val->value()) + "." + enum_type + ",\n";
});
EmitCodeBlock(code, enum_name, ns, enum_def->declaration_file()->str());
}
void GenerateObject(const r::Object *object) {
// Register the main flatbuffers module.
RegisterImports("flatbuffers", "");
std::string code;
std::string ns;
const std::string object_name = namer_.Type(namer_.Denamespace(object, ns));
GenerateDocumentation(object->documentation(), "", code);
code += "type " + object_name + "* = object of FlatObj\n";
// Create all the field accessors.
ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
// Skip writing deprecated fields altogether.
if (field->deprecated()) { return; }
const std::string field_name = namer_.Field(*field);
const r::BaseType base_type = field->type()->base_type();
std::string field_type = GenerateType(field->type());
if (field->optional() && !object->is_struct()) {
RegisterImports("std/options", "");
field_type = "Option[" + field_type + "]";
}
const std::string offset_prefix =
"let o = self.tab.Offset(" + NumToString(field->offset()) + ")\n";
const std::string offset_prefix_2 = "if o != 0:\n";
if (IsScalar(base_type) || base_type == r::String ||
base_type == r::Obj || base_type == r::Union) {
GenerateDocumentation(field->documentation(), "", code);
std::string getter_signature = "func " + namer_.Method(field_name) +
"*(self: " + object_name +
"): " + field_type + " =\n";
std::string getter_code;
std::string setter_signature =
"func `" + namer_.Method(field_name + "=") + "`*(self: var " +
object_name + ", n: " + field_type + "): bool =\n";
std::string setter_code;
if (base_type == r::Obj || base_type == r::Union ||
field->type()->index() >= 0) {
RegisterImports(object, field);
}
if (object->is_struct()) {
std::string field_getter =
GenerateGetter(field->type(), NumToString(field->offset()));
getter_code += " return " + field_getter + "\n";
if (IsScalar(base_type)) {
setter_code += " return self.tab.Mutate(self.tab.Pos + " +
NumToString(field->offset()) + ", n)\n";
}
} else {
// Table accessors
getter_code += " " + offset_prefix;
getter_code += " " + offset_prefix_2;
std::string field_getter = GenerateGetter(field->type(), "o");
if (field->optional()) {
field_getter = "some(" + field_getter + ")";
}
getter_code += " return " + field_getter + "\n";
if (!field->optional()) {
getter_code += " return " + DefaultValue(field) + "\n";
}
if (IsScalar(base_type)) {
setter_code += " return self.tab.MutateSlot(" +
NumToString(field->offset()) + ", n)\n";
}
}
code += getter_signature + getter_code;
if (IsScalar(base_type)) { code += setter_signature + setter_code; }
} else if (base_type == r::Array || base_type == r::Vector) {
const r::BaseType vector_base_type = field->type()->element();
uint32_t element_size = field->type()->element_size();
if (vector_base_type == r::Obj || vector_base_type == r::Union ||
field->type()->index() >= 0) {
RegisterImports(object, field, true);
}
// Get vector length:
code += "func " + namer_.Method(field_name + "Length") +
"*(self: " + object_name + "): int = \n";
code += " " + offset_prefix;
code += " " + offset_prefix_2;
code += " return self.tab.VectorLen(o)\n";
// Get single vector field:
code += "func " + namer_.Method(field_name) + "*(self: " + object_name +
", j: int): " + GenerateType(field->type(), true) + " = \n";
code += " " + offset_prefix;
code += " " + offset_prefix_2;
code += " var x = self.tab.Vector(o)\n";
code +=
" x += j.uoffset * " + NumToString(element_size) + ".uoffset\n";
code += " return " + GenerateGetter(field->type(), "x", true) + "\n";
// Get entire vector:
code += "func " + namer_.Method(field_name) + "*(self: " + object_name +
"): " + GenerateType(field->type()) + " = \n";
code += " let len = self." + field_name + "Length\n";
code += " for i in countup(0, len - 1):\n";
code += " result.add(self." + field_name + "(i))\n";
(void)IsSingleByte(vector_base_type); // unnused function warning
}
});
// Create all the builders
if (object->is_struct()) {
code += "proc " + namer_.Function(object_name + "Create") +
"*(self: var Builder";
code += GenerateStructBuilderArgs(object);
code += "): uoffset =\n";
code += AppendStructBuilderBody(object);
code += " return self.Offset()\n";
} else {
// Table builders
code += "proc " + namer_.Function(object_name + "Start") +
"*(builder: var Builder) =\n";
code += " builder.StartObject(" + NumToString(object->fields()->size()) +
")\n";
ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
if (field->deprecated()) { return; }
const std::string field_name = namer_.Field(*field);
const std::string variable_name = namer_.Variable(*field);
const std::string variable_type = GenerateTypeBasic(field->type());
code += "proc " + namer_.Function(object_name + "Add" + field_name) +
"*(builder: var Builder, " + variable_name + ": " +
variable_type + ") =\n";
code += " builder.Prepend" + GenerateMethod(field) + "Slot(" +
NumToString(field->id()) + ", " + variable_name + ", default(" +
variable_type + "))\n";
if (IsVector(field->type()->base_type())) {
code += "proc " +
namer_.Function(object_name + "Start" + field_name) +
"Vector*(builder: var Builder, numElems: uoffset) =\n";
const int32_t element_size = field->type()->element_size();
int32_t alignment = element_size;
if (IsStruct(field->type(), /*use_element=*/true)) {
alignment = GetObjectByIndex(field->type()->index())->minalign();
}
code += " builder.StartVector(" + NumToString(element_size) +
", numElems, " + NumToString(alignment) + ")\n";
}
});
code += "proc " + namer_.Function(object_name + "End") +
"*(builder: var Builder): uoffset =\n";
code += " return builder.EndObject()\n";
}
EmitCodeBlock(code, object_name, ns, object->declaration_file()->str());
}
private:
void GenerateDocumentation(
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>
*documentation,
std::string indent, std::string &code) const {
flatbuffers::ForAllDocumentation(
documentation, [&](const flatbuffers::String *str) {
code += indent + "# " + str->str() + "\n";
});
}
std::string GenerateStructBuilderArgs(const r::Object *object,
std::string prefix = "") const {
std::string signature;
ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
if (IsStructOrTable(field->type()->base_type())) {
const r::Object *field_object = GetObject(field->type());
signature += GenerateStructBuilderArgs(
field_object, prefix + namer_.Variable(*field) + "_");
} else {
signature += ", " + prefix + namer_.Variable(*field) + ": " +
GenerateType(field->type());
}
});
return signature;
}
std::string AppendStructBuilderBody(const r::Object *object,
std::string prefix = "") const {
std::string code;
code += " self.Prep(" + NumToString(object->minalign()) + ", " +
NumToString(object->bytesize()) + ")\n";
// We need to reverse the order we iterate over, since we build the
// buffer backwards.
ForAllFields(object, /*reverse=*/true, [&](const r::Field *field) {
const int32_t num_padding_bytes = field->padding();
if (num_padding_bytes) {
code += " self.Pad(" + NumToString(num_padding_bytes) + ")\n";
}
if (IsStructOrTable(field->type()->base_type())) {
const r::Object *field_object = GetObject(field->type());
code += AppendStructBuilderBody(field_object,
prefix + namer_.Variable(*field) + "_");
} else {
code += " self.Prepend(" + prefix + namer_.Variable(*field) + ")\n";
}
});
return code;
}
std::string GenerateMethod(const r::Field *field) const {
const r::BaseType base_type = field->type()->base_type();
if (IsStructOrTable(base_type)) { return "Struct"; }
return "";
}
std::string GenerateGetter(const r::Type *type, const std::string &offsetval,
bool element_type = false) const {
const r::BaseType base_type =
element_type ? type->element() : type->base_type();
std::string offset = offsetval;
if (!element_type) { offset = "self.tab.Pos + " + offset; }
switch (base_type) {
case r::String: return "self.tab.String(" + offset + ")";
case r::Union: return "self.tab.Union(" + offsetval + ")";
case r::Obj: {
return GenerateType(type, element_type) +
"(tab: Vtable(Bytes: self.tab.Bytes, Pos: " + offset + "))";
}
case r::Vector: return GenerateGetter(type, offsetval, true);
default:
const r::Enum *type_enum = GetEnum(type, element_type);
if (type_enum != nullptr) {
return GenerateType(type, element_type) + "(" + "Get[" +
GenerateType(base_type) + "](self.tab, " + offset + ")" + ")";
} else {
return "Get[" + GenerateType(base_type) + "](self.tab, " + offset +
")";
}
}
}
std::string Denamespace(const std::string &s, std::string &importns,
std::string &ns) const {
if (builtin_types.find(s) != builtin_types.end()) { return s; }
std::string type = namer_.Type(namer_.Denamespace(s, ns));
importns = ns.empty() ? type : ns + "." + type;
std::replace(importns.begin(), importns.end(), '.', '_');
return type;
}
std::string Denamespace(const std::string &s, std::string &importns) const {
std::string ns;
return Denamespace(s, importns, ns);
}
std::string Denamespace(const std::string &s) const {
std::string importns;
return Denamespace(s, importns);
}
std::string GenerateType(const r::Type *type, bool element_type = false,
bool enum_inner = false) const {
const r::BaseType base_type =
element_type ? type->element() : type->base_type();
if (IsScalar(base_type) && !enum_inner) {
const r::Enum *type_enum = GetEnum(type, element_type);
if (type_enum != nullptr) {
std::string importns;
std::string type_name = Denamespace(type_enum->name()->str(), importns);
return importns + "." + type_name;
}
}
if (IsScalar(base_type)) { return Denamespace(GenerateType(base_type)); }
switch (base_type) {
case r::String: return "string";
case r::Vector: {
return "seq[" + GenerateType(type, true) + "]";
}
case r::Union: return "Vtable";
case r::Obj: {
const r::Object *type_obj = GetObject(type, element_type);
std::string importns;
std::string type_name = Denamespace(type_obj->name()->str(), importns);
if (type_obj == current_obj_) {
return type_name;
} else {
return importns + "." + type_name;
}
}
default: return "uoffset";
}
}
std::string GenerateTypeBasic(const r::Type *type,
bool element_type = false) const {
const r::BaseType base_type =
element_type ? type->element() : type->base_type();
if (IsScalar(base_type)) {
return GenerateType(base_type);
} else {
return "uoffset";
}
}
std::string GenerateType(const r::BaseType base_type) const {
switch (base_type) {
case r::None: return "uint8";
case r::UType: return "uint8";
case r::Bool: return "bool";
case r::Byte: return "int8";
case r::UByte: return "uint8";
case r::Short: return "int16";
case r::UShort: return "uint16";
case r::Int: return "int32";
case r::UInt: return "uint32";
case r::Long: return "int64";
case r::ULong: return "uint64";
case r::Float: return "float32";
case r::Double: return "float64";
case r::String: return "string";
default: return r::EnumNameBaseType(base_type);
}
}
std::string DefaultValue(const r::Field *field) const {
const r::BaseType base_type = field->type()->base_type();
if (IsFloatingPoint(base_type)) {
return NumToString(field->default_real());
}
if (IsBool(base_type)) {
return field->default_integer() ? "true" : "false";
}
if (IsScalar(base_type)) {
const r::Enum *type_enum = GetEnum(field->type());
if (type_enum != nullptr) {
return "type(result)(" + NumToString((field->default_integer())) + ")";
}
return NumToString((field->default_integer()));
}
if (base_type == r::String) { return "\"\""; }
// represents offsets
return "0";
}
void StartCodeBlock(const reflection::Enum *enum_def) {
current_enum_ = enum_def;
current_obj_ = nullptr;
imports_.clear();
}
void StartCodeBlock(const reflection::Object *object) {
current_enum_ = nullptr;
current_obj_ = object;
imports_.clear();
}
std::vector<std::string> StringSplit(const std::string orig_str,
const std::string token) {
std::vector<std::string> result;
std::string str = orig_str;
while (str.size()) {
size_t index = str.find(token);
if (index != std::string::npos) {
result.push_back(str.substr(0, index));
str = str.substr(index + token.size());
if (str.size() == 0) result.push_back(str);
} else {
result.push_back(str);
str = "";
}
}
return result;
}
std::string GetRelativePathFromNamespace(const std::string &relative_to,
const std::string &str2) {
std::vector<std::string> relative_to_vec = StringSplit(relative_to, ".");
std::vector<std::string> str2_vec = StringSplit(str2, ".");
while (relative_to_vec.size() > 0 && str2_vec.size() > 0) {
if (relative_to_vec[0] == str2_vec[0]) {
relative_to_vec.erase(relative_to_vec.begin());
str2_vec.erase(str2_vec.begin());
} else {
break;
}
}
relative_to_vec.pop_back();
for (size_t i = 0; i < relative_to_vec.size(); ++i) {
str2_vec.insert(str2_vec.begin(), std::string(".."));
}
std::string new_path;
for (size_t i = 0; i < str2_vec.size(); ++i) {
new_path += str2_vec[i];
if (i != str2_vec.size() - 1) { new_path += "/"; }
}
return new_path;
}
void RegisterImports(const r::Object *object, const r::Field *field,
bool use_element = false) {
std::string importns;
std::string type_name;
const r::BaseType type =
use_element ? field->type()->element() : field->type()->base_type();
if (IsStructOrTable(type)) {
const r::Object *object_def = GetObjectByIndex(field->type()->index());
if (object_def == current_obj_) { return; }
std::string ns;
type_name = Denamespace(object_def->name()->str(), importns, ns);
type_name = ns.empty() ? type_name : ns + "." + type_name;
} else {
const r::Enum *enum_def = GetEnumByIndex(field->type()->index());
if (enum_def == current_enum_) { return; }
std::string ns;
type_name = Denamespace(enum_def->name()->str(), importns, ns);
type_name = ns.empty() ? type_name : ns + "." + type_name;
}
std::string import_path =
GetRelativePathFromNamespace(object->name()->str(), type_name);
std::replace(type_name.begin(), type_name.end(), '.', '_');
RegisterImports(import_path, importns);
}
void RegisterImports(const std::string &local_name,
const std::string &imports_name) {
imports_[local_name] = imports_name;
}
void EmitCodeBlock(const std::string &code_block, const std::string &name,
const std::string &ns, const std::string &declaring_file) {
const std::string full_qualified_name = ns.empty() ? name : ns + "." + name;
std::string code = "#[ " + full_qualified_name + "\n";
code +=
" Automatically generated by the FlatBuffers compiler, do not "
"modify.\n";
code += " Or modify. I'm a message, not a cop.\n";
code += "\n";
code += " flatc version: " + flatc_version_ + "\n";
code += "\n";
code += " Declared by : " + declaring_file + "\n";
if (schema_->root_table() != nullptr) {
const std::string root_type = schema_->root_table()->name()->str();
const std::string root_file =
schema_->root_table()->declaration_file()->str();
code += " Rooting type : " + root_type + " (" + root_file + ")\n";
}
code += "]#\n\n";
if (!imports_.empty()) {
for (auto it = imports_.cbegin(); it != imports_.cend(); ++it) {
if (it->second.empty()) {
code += "import " + it->first + "\n";
} else {
code += "import " + it->first + " as " + it->second + "\n";
}
}
code += "\n";
}
code += code_block;
// Namespaces are '.' deliminted, so replace it with the path separator.
std::string path = ns;
if (ns.empty()) {
path = ".";
} else {
std::replace(path.begin(), path.end(), '.', '/');
}
// TODO(derekbailey): figure out a save file without depending on util.h
EnsureDirExists(path);
const std::string file_name = path + "/" + namer_.File(name);
SaveFile(file_name.c_str(), code, false);
}
std::unordered_set<std::string> keywords_;
std::map<std::string, std::string> imports_;
const r::Object *current_obj_;
const r::Enum *current_enum_;
const std::string flatc_version_;
const BfbsNamer namer_;
};
} // namespace
std::unique_ptr<BfbsGenerator> NewNimBfbsGenerator(
const std::string &flatc_version) {
return std::unique_ptr<NimBfbsGenerator>(new NimBfbsGenerator(flatc_version));
}
} // namespace flatbuffers

33
src/bfbs_gen_nim.h Normal file
View File

@@ -0,0 +1,33 @@
/*
* Copyright 2021 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_BFBS_GEN_NIM_H_
#define FLATBUFFERS_BFBS_GEN_NIM_H_
#include <memory>
#include <string>
#include "flatbuffers/bfbs_generator.h"
namespace flatbuffers {
// Constructs a new Nim Code generator.
std::unique_ptr<BfbsGenerator> NewNimBfbsGenerator(
const std::string &flatc_version);
} // namespace flatbuffers
#endif // FLATBUFFERS_BFBS_GEN_NIM_H_

View File

@@ -36,6 +36,14 @@ class BfbsNamer : public Namer {
std::string Denamespace(T t, const char delimiter = '.') const {
return Namer::Denamespace(t->name()->c_str(), delimiter);
}
virtual std::string Field(const ::reflection::Field &f) const {
return Field(f.name()->str());
}
virtual std::string Variable(const ::reflection::Field &f) const {
return Variable(f.name()->str());
}
};
} // namespace flatbuffers

View File

@@ -18,6 +18,7 @@
#include <memory>
#include "bfbs_gen_lua.h"
#include "bfbs_gen_nim.h"
#include "flatbuffers/base.h"
#include "flatbuffers/flatc.h"
#include "flatbuffers/util.h"
@@ -56,6 +57,8 @@ int main(int argc, const char *argv[]) {
std::unique_ptr<flatbuffers::BfbsGenerator> bfbs_gen_lua =
flatbuffers::NewLuaBfbsGenerator(flatbuffers_version);
std::unique_ptr<flatbuffers::BfbsGenerator> bfbs_gen_nim =
flatbuffers::NewNimBfbsGenerator(flatbuffers_version);
g_program_name = argv[0];
@@ -142,6 +145,10 @@ int main(int argc, const char *argv[]) {
flatbuffers::FlatCOption{ "", "swift", "",
"Generate Swift files for tables/structs" },
nullptr, nullptr, nullptr },
{ nullptr, "Nim", true, nullptr, flatbuffers::IDLOptions::kNim,
flatbuffers::FlatCOption{ "", "nim", "",
"Generate Nim files for tables/structs" },
nullptr, bfbs_gen_nim.get(), nullptr },
};
flatbuffers::FlatCompiler::InitParams params;

View File

@@ -655,7 +655,7 @@ class CSharpGenerator : public BaseGenerator {
// Force compile time error if not using the same version runtime.
code += " public static void ValidateVersion() {";
code += " FlatBufferConstants.";
code += "FLATBUFFERS_22_9_24(); ";
code += "FLATBUFFERS_22_10_26(); ";
code += "}\n";
// Generate a special accessor for the table that when used as the root

View File

@@ -397,6 +397,10 @@ class JavaGenerator : public BaseGenerator {
code += " ";
code += namer_.Variant(ev) + " = ";
code += enum_def.ToString(ev);
if (enum_def.underlying_type.base_type == BASE_TYPE_LONG ||
enum_def.underlying_type.base_type == BASE_TYPE_ULONG) {
code += "L";
}
code += ";\n";
}
@@ -665,7 +669,7 @@ class JavaGenerator : public BaseGenerator {
// Force compile time error if not using the same version runtime.
code += " public static void ValidateVersion() {";
code += " Constants.";
code += "FLATBUFFERS_22_9_24(); ";
code += "FLATBUFFERS_22_10_26(); ";
code += "}\n";
// Generate a special accessor for the table that when used as the root
@@ -1889,6 +1893,7 @@ class JavaGenerator : public BaseGenerator {
}
} else {
code += " " + name + " = ";
code += SourceCast(field_type);
code += "_o";
for (size_t i = 0; i < array_lengths.size(); ++i) {
code += "." + namer_.Method("get", array_lengths[i].name) + "()";

View File

@@ -505,7 +505,7 @@ class KotlinGenerator : public BaseGenerator {
// runtime.
GenerateFunOneLine(
writer, "validateVersion", "", "",
[&]() { writer += "Constants.FLATBUFFERS_22_9_24()"; },
[&]() { writer += "Constants.FLATBUFFERS_22_10_26()"; },
options.gen_jvmstatic);
GenerateGetRootAsAccessors(namer_.Type(struct_def), writer, options);

View File

@@ -1108,13 +1108,25 @@ class PythonGenerator : public BaseGenerator {
code += GenIndents(1) + "@classmethod";
code += GenIndents(1) + "def InitFromBuf(cls, buf, pos):";
code += GenIndents(2) + "n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, 0)";
code += GenIndents(2) + struct_var + " = " + struct_type + "()";
code += GenIndents(2) + struct_var + ".Init(buf, pos+n)";
code += GenIndents(2) + struct_var + ".Init(buf, pos)";
code += GenIndents(2) + "return cls.InitFromObj(" + struct_var + ")";
code += "\n";
}
void InitializeFromPackedBuf(const StructDef &struct_def,
std::string *code_ptr) const {
auto &code = *code_ptr;
const auto struct_var = namer_.Variable(struct_def);
const auto struct_type = namer_.Type(struct_def);
code += GenIndents(1) + "@classmethod";
code += GenIndents(1) + "def InitFromPackedBuf(cls, buf, pos=0):";
code += GenIndents(2) + "n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, pos)";
code += GenIndents(2) + "return cls.InitFromBuf(buf, pos+n)";
code += "\n";
}
void InitializeFromObjForObject(const StructDef &struct_def,
std::string *code_ptr) const {
auto &code = *code_ptr;
@@ -1607,6 +1619,8 @@ class PythonGenerator : public BaseGenerator {
InitializeFromBuf(struct_def, &code);
InitializeFromPackedBuf(struct_def, &code);
InitializeFromObjForObject(struct_def, &code);
GenUnPack(struct_def, &code);

View File

@@ -737,7 +737,6 @@ class RustGenerator : public BaseGenerator {
code_ += "pub use self::bitflags_{{ENUM_NAMESPACE}}::{{ENUM_TY}};";
code_ += "";
code_.SetValue("FROM_BASE", "unsafe { Self::from_bits_unchecked(b) }");
code_.SetValue("INTO_BASE", "self.bits()");
} else {
// Normal, c-modelled enums.
@@ -810,7 +809,6 @@ class RustGenerator : public BaseGenerator {
code_ += " }";
code_ += "}";
code_.SetValue("FROM_BASE", "Self(b)");
code_.SetValue("INTO_BASE", "self.0");
}
@@ -839,35 +837,55 @@ class RustGenerator : public BaseGenerator {
code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_TY}} {";
code_ += " type Inner = Self;";
code_ += " #[inline]";
code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
code_ += " let b = unsafe {";
code_ += " flatbuffers::read_scalar_at::<{{BASE_TYPE}}>(buf, loc)";
code_ += " };";
code_ += " {{FROM_BASE}}";
code_ += " unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
code_ += " let b = flatbuffers::read_scalar_at::<{{BASE_TYPE}}>(buf, loc);";
if (IsBitFlagsEnum(enum_def)) {
// Safety:
// This is safe because we know bitflags is implemented with a repr transparent uint of the correct size.
// from_bits_unchecked will be replaced by an equivalent but safe from_bits_retain in bitflags 2.0
// https://github.com/bitflags/bitflags/issues/262
code_ += " // Safety:";
code_ += " // This is safe because we know bitflags is implemented with a repr transparent uint of the correct size.";
code_ += " // from_bits_unchecked will be replaced by an equivalent but safe from_bits_retain in bitflags 2.0";
code_ += " // https://github.com/bitflags/bitflags/issues/262";
code_ += " Self::from_bits_unchecked(b)";
} else {
code_ += " Self(b)";
}
code_ += " }";
code_ += "}";
code_ += "";
code_ += "impl flatbuffers::Push for {{ENUM_TY}} {";
code_ += " type Output = {{ENUM_TY}};";
code_ += " #[inline]";
code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
code_ +=
" unsafe { flatbuffers::emplace_scalar::<{{BASE_TYPE}}>"
"(dst, {{INTO_BASE}}); }";
code_ += " unsafe fn push(&self, dst: &mut [u8], _written_len: usize) {";
code_ += " flatbuffers::emplace_scalar::<{{BASE_TYPE}}>(dst, {{INTO_BASE}});";
code_ += " }";
code_ += "}";
code_ += "";
code_ += "impl flatbuffers::EndianScalar for {{ENUM_TY}} {";
code_ += " type Scalar = {{BASE_TYPE}};";
code_ += " #[inline]";
code_ += " fn to_little_endian(self) -> Self {";
code_ += " let b = {{BASE_TYPE}}::to_le({{INTO_BASE}});";
code_ += " {{FROM_BASE}}";
code_ += " fn to_little_endian(self) -> {{BASE_TYPE}} {";
code_ += " {{INTO_BASE}}.to_le()";
code_ += " }";
code_ += " #[inline]";
code_ += " #[allow(clippy::wrong_self_convention)]";
code_ += " fn from_little_endian(self) -> Self {";
code_ += " let b = {{BASE_TYPE}}::from_le({{INTO_BASE}});";
code_ += " {{FROM_BASE}}";
code_ += " fn from_little_endian(v: {{BASE_TYPE}}) -> Self {";
code_ += " let b = {{BASE_TYPE}}::from_le(v);";
if (IsBitFlagsEnum(enum_def)) {
// Safety:
// This is safe because we know bitflags is implemented with a repr transparent uint of the correct size.
// from_bits_unchecked will be replaced by an equivalent but safe from_bits_retain in bitflags 2.0
// https://github.com/bitflags/bitflags/issues/262
code_ += " // Safety:";
code_ += " // This is safe because we know bitflags is implemented with a repr transparent uint of the correct size.";
code_ += " // from_bits_unchecked will be replaced by an equivalent but safe from_bits_retain in bitflags 2.0";
code_ += " // https://github.com/bitflags/bitflags/issues/262";
code_ += " unsafe { Self::from_bits_unchecked(b) }";
} else {
code_ += " Self(b)";
}
code_ += " }";
code_ += "}";
code_ += "";
@@ -1425,11 +1443,7 @@ class RustGenerator : public BaseGenerator {
case ftVectorOfBool:
case ftVectorOfFloat: {
const auto typname = GetTypeBasic(type.VectorType());
const auto vector_type =
IsOneByte(type.VectorType().base_type)
? "&" + lifetime + " [" + typname + "]"
: "flatbuffers::Vector<" + lifetime + ", " + typname + ">";
return WrapOption(vector_type);
return WrapOption("flatbuffers::Vector<" + lifetime + ", " + typname + ">");
}
case ftVectorOfEnumKey: {
const auto typname = WrapInNameSpace(*type.enum_def);
@@ -1438,7 +1452,7 @@ class RustGenerator : public BaseGenerator {
}
case ftVectorOfStruct: {
const auto typname = WrapInNameSpace(*type.struct_def);
return WrapOption("&" + lifetime + " [" + typname + "]");
return WrapOption("flatbuffers::Vector<" + lifetime + ", " + typname + ">");
}
case ftVectorOfTable: {
const auto typname = WrapInNameSpace(*type.struct_def);
@@ -1556,19 +1570,8 @@ class RustGenerator : public BaseGenerator {
: "None";
const std::string unwrap = field.IsOptional() ? "" : ".unwrap()";
const auto t = GetFullType(field.value.type);
// TODO(caspern): Shouldn't 1byte VectorOfEnumKey be slice too?
const std::string safe_slice =
(t == ftVectorOfStruct ||
((t == ftVectorOfBool || t == ftVectorOfFloat ||
t == ftVectorOfInteger) &&
IsOneByte(field.value.type.VectorType().base_type)))
? ".map(|v| v.safe_slice())"
: "";
return "self._tab.get::<" + typname + ">({{STRUCT_TY}}::" + vt_offset +
", " + default_value + ")" + safe_slice + unwrap;
return "unsafe { self._tab.get::<" + typname + ">({{STRUCT_TY}}::" + vt_offset +
", " + default_value + ")" + unwrap + "}";
}
// Generates a fully-qualified name getter for use with --gen-name-strings
@@ -1650,8 +1653,8 @@ class RustGenerator : public BaseGenerator {
code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_TY}}<'a> {";
code_ += " type Inner = {{STRUCT_TY}}<'a>;";
code_ += " #[inline]";
code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
code_ += " Self { _tab: flatbuffers::Table { buf, loc } }";
code_ += " unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
code_ += " Self { _tab: flatbuffers::Table::new(buf, loc) }";
code_ += " }";
code_ += "}";
code_ += "";
@@ -1672,7 +1675,7 @@ class RustGenerator : public BaseGenerator {
code_ += " #[inline]";
code_ +=
" pub fn init_from_table(table: flatbuffers::Table<'a>) -> "
" pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> "
"Self {";
code_ += " {{STRUCT_TY}} { _tab: table }";
code_ += " }";
@@ -1764,16 +1767,7 @@ class RustGenerator : public BaseGenerator {
break;
}
case ftVectorOfInteger:
case ftVectorOfBool: {
if (IsOneByte(type.VectorType().base_type)) {
// 1 byte stuff is viewed w/ slice instead of flatbuffer::Vector
// and thus needs to be cloned out of the slice.
code_.SetValue("EXPR", "x.to_vec()");
break;
}
code_.SetValue("EXPR", "x.into_iter().collect()");
break;
}
case ftVectorOfBool:
case ftVectorOfFloat:
case ftVectorOfEnumKey: {
code_.SetValue("EXPR", "x.into_iter().collect()");
@@ -1840,6 +1834,9 @@ class RustGenerator : public BaseGenerator {
this->GenComment(field.doc_comment);
code_ += "#[inline]";
code_ += "pub fn {{FIELD}}(&self) -> {{RETURN_TYPE}} {";
code_ += " // Safety:";
code_ += " // Created from valid Table for this object";
code_ += " // which contains a valid value in this slot";
code_ += " " + GenTableAccessorFuncBody(field, "'a");
code_ += "}";
@@ -1864,16 +1861,22 @@ class RustGenerator : public BaseGenerator {
code_ += "{{NESTED}}<'a> {";
code_ += " let data = self.{{FIELD}}();";
code_ += " use flatbuffers::Follow;";
code_ += " // Safety:";
code_ += " // Created from a valid Table for this object";
code_ += " // Which contains a valid flatbuffer in this slot";
code_ +=
" <flatbuffers::ForwardsUOffset<{{NESTED}}<'a>>>"
"::follow(data, 0)";
" unsafe { <flatbuffers::ForwardsUOffset<{{NESTED}}<'a>>>"
"::follow(data.bytes(), 0) }";
} else {
code_ += "Option<{{NESTED}}<'a>> {";
code_ += " self.{{FIELD}}().map(|data| {";
code_ += " use flatbuffers::Follow;";
code_ += " // Safety:";
code_ += " // Created from a valid Table for this object";
code_ += " // Which contains a valid flatbuffer in this slot";
code_ +=
" <flatbuffers::ForwardsUOffset<{{NESTED}}<'a>>>"
"::follow(data, 0)";
" unsafe { <flatbuffers::ForwardsUOffset<{{NESTED}}<'a>>>"
"::follow(data.bytes(), 0) }";
code_ += " })";
}
code_ += "}";
@@ -1909,11 +1912,17 @@ class RustGenerator : public BaseGenerator {
// as of April 10, 2020
if (field.IsRequired()) {
code_ += " let u = self.{{FIELD}}();";
code_ += " Some({{U_ELEMENT_TABLE_TYPE}}::init_from_table(u))";
code_ += " // Safety:";
code_ += " // Created from a valid Table for this object";
code_ += " // Which contains a valid union in this slot";
code_ += " Some(unsafe { {{U_ELEMENT_TABLE_TYPE}}::init_from_table(u) })";
} else {
code_ +=
" self.{{FIELD}}().map("
"{{U_ELEMENT_TABLE_TYPE}}::init_from_table)";
code_ +=" self.{{FIELD}}().map(|t| {";
code_ += " // Safety:";
code_ += " // Created from a valid Table for this object";
code_ += " // Which contains a valid union in this slot";
code_ += " unsafe { {{U_ELEMENT_TABLE_TYPE}}::init_from_table(t) }";
code_ += " })";
}
code_ += " } else {";
code_ += " None";
@@ -2282,8 +2291,8 @@ class RustGenerator : public BaseGenerator {
MapNativeTableField(
field,
"let w: Vec<_> = x.iter().map(|s| s.as_ref()).collect();"
"_fbb.create_vector_of_strings(&w)");
"let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();"
"_fbb.create_vector(&w)");
return;
}
case ftVectorOfTable: {
@@ -2374,32 +2383,6 @@ class RustGenerator : public BaseGenerator {
code_.SetValue("STRUCT_FN", namer_.Function(struct_def));
code_.SetValue("STRUCT_CONST", namer_.Constant(struct_def.name));
// The root datatype accessors:
code_ += "#[inline]";
code_ +=
"#[deprecated(since=\"2.0.0\", "
"note=\"Deprecated in favor of `root_as...` methods.\")]";
code_ +=
"pub fn get_root_as_{{STRUCT_FN}}<'a>(buf: &'a [u8])"
" -> {{STRUCT_TY}}<'a> {";
code_ +=
" unsafe { flatbuffers::root_unchecked::<{{STRUCT_TY}}"
"<'a>>(buf) }";
code_ += "}";
code_ += "";
code_ += "#[inline]";
code_ +=
"#[deprecated(since=\"2.0.0\", "
"note=\"Deprecated in favor of `root_as...` methods.\")]";
code_ +=
"pub fn get_size_prefixed_root_as_{{STRUCT_FN}}"
"<'a>(buf: &'a [u8]) -> {{STRUCT_TY}}<'a> {";
code_ +=
" unsafe { flatbuffers::size_prefixed_root_unchecked::<{{STRUCT_TY}}"
"<'a>>(buf) }";
code_ += "}";
code_ += "";
// Default verifier root fns.
code_ += "#[inline]";
code_ += "/// Verifies that a buffer of bytes contains a `{{STRUCT_TY}}`";
@@ -2641,43 +2624,25 @@ class RustGenerator : public BaseGenerator {
// Follow for the value type, Follow for the reference type, Push for the
// value type, and Push for the reference type.
code_ += "impl flatbuffers::SimpleToVerifyInSlice for {{STRUCT_TY}} {}";
code_ += "impl flatbuffers::SafeSliceAccess for {{STRUCT_TY}} {}";
code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_TY}} {";
code_ += " type Inner = &'a {{STRUCT_TY}};";
code_ += " #[inline]";
code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
code_ += " unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
code_ += " <&'a {{STRUCT_TY}}>::follow(buf, loc)";
code_ += " }";
code_ += "}";
code_ += "impl<'a> flatbuffers::Follow<'a> for &'a {{STRUCT_TY}} {";
code_ += " type Inner = &'a {{STRUCT_TY}};";
code_ += " #[inline]";
code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
code_ += " unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
code_ += " flatbuffers::follow_cast_ref::<{{STRUCT_TY}}>(buf, loc)";
code_ += " }";
code_ += "}";
code_ += "impl<'b> flatbuffers::Push for {{STRUCT_TY}} {";
code_ += " type Output = {{STRUCT_TY}};";
code_ += " #[inline]";
code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
code_ += " let src = unsafe {";
code_ +=
" ::core::slice::from_raw_parts("
"self as *const {{STRUCT_TY}} as *const u8, Self::size())";
code_ += " };";
code_ += " dst.copy_from_slice(src);";
code_ += " }";
code_ += "}";
code_ += "impl<'b> flatbuffers::Push for &'b {{STRUCT_TY}} {";
code_ += " type Output = {{STRUCT_TY}};";
code_ += "";
code_ += " #[inline]";
code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
code_ += " let src = unsafe {";
code_ +=
" ::core::slice::from_raw_parts("
"*self as *const {{STRUCT_TY}} as *const u8, Self::size())";
code_ += " };";
code_ += " unsafe fn push(&self, dst: &mut [u8], _written_len: usize) {";
code_ += " let src = ::core::slice::from_raw_parts(self as *const {{STRUCT_TY}} as *const u8, Self::size());";
code_ += " dst.copy_from_slice(src);";
code_ += " }";
code_ += "}";
@@ -2754,6 +2719,9 @@ class RustGenerator : public BaseGenerator {
// Getter.
if (IsStruct(field.value.type)) {
code_ += "pub fn {{FIELD}}(&self) -> &{{FIELD_TYPE}} {";
code_ += " // Safety:";
code_ += " // Created from a valid Table for this object";
code_ += " // Which contains a valid struct in this slot";
code_ +=
" unsafe {"
" &*(self.0[{{FIELD_OFFSET}}..].as_ptr() as *const"
@@ -2765,20 +2733,26 @@ class RustGenerator : public BaseGenerator {
code_ +=
"pub fn {{FIELD}}(&'a self) -> "
"flatbuffers::Array<'a, {{ARRAY_ITEM}}, {{ARRAY_SIZE}}> {";
code_ += " flatbuffers::Array::follow(&self.0, {{FIELD_OFFSET}})";
code_ += " // Safety:";
code_ += " // Created from a valid Table for this object";
code_ += " // Which contains a valid array in this slot";
code_ += " unsafe { flatbuffers::Array::follow(&self.0, {{FIELD_OFFSET}}) }";
} else {
code_ += "pub fn {{FIELD}}(&self) -> {{FIELD_TYPE}} {";
code_ +=
" let mut mem = core::mem::MaybeUninit::"
"<{{FIELD_TYPE}}>::uninit();";
code_ += " unsafe {";
"<<{{FIELD_TYPE}} as EndianScalar>::Scalar>::uninit();";
code_ += " // Safety:";
code_ += " // Created from a valid Table for this object";
code_ += " // Which contains a valid value in this slot";
code_ += " EndianScalar::from_little_endian(unsafe {";
code_ += " core::ptr::copy_nonoverlapping(";
code_ += " self.0[{{FIELD_OFFSET}}..].as_ptr(),";
code_ += " mem.as_mut_ptr() as *mut u8,";
code_ += " core::mem::size_of::<{{FIELD_TYPE}}>(),";
code_ += " core::mem::size_of::<<{{FIELD_TYPE}} as EndianScalar>::Scalar>(),";
code_ += " );";
code_ += " mem.assume_init()";
code_ += " }.from_little_endian()";
code_ += " })";
}
code_ += "}\n";
// Setter.
@@ -2799,13 +2773,19 @@ class RustGenerator : public BaseGenerator {
code_ +=
"pub fn set_{{FIELD}}(&mut self, items: &{{FIELD_TYPE}}) "
"{";
code_ += " // Safety:";
code_ += " // Created from a valid Table for this object";
code_ += " // Which contains a valid array in this slot";
code_ +=
" flatbuffers::emplace_scalar_array(&mut self.0, "
"{{FIELD_OFFSET}}, items);";
" unsafe { flatbuffers::emplace_scalar_array(&mut self.0, "
"{{FIELD_OFFSET}}, items) };";
} else {
code_.SetValue("FIELD_SIZE",
NumToString(InlineSize(field.value.type)));
code_ += "pub fn set_{{FIELD}}(&mut self, x: &{{FIELD_TYPE}}) {";
code_ += " // Safety:";
code_ += " // Created from a valid Table for this object";
code_ += " // Which contains a valid array in this slot";
code_ += " unsafe {";
code_ += " core::ptr::copy(";
code_ += " x.as_ptr() as *const u8,";
@@ -2817,11 +2797,14 @@ class RustGenerator : public BaseGenerator {
} else {
code_ += "pub fn set_{{FIELD}}(&mut self, x: {{FIELD_TYPE}}) {";
code_ += " let x_le = x.to_little_endian();";
code_ += " // Safety:";
code_ += " // Created from a valid Table for this object";
code_ += " // Which contains a valid value in this slot";
code_ += " unsafe {";
code_ += " core::ptr::copy_nonoverlapping(";
code_ += " &x_le as *const {{FIELD_TYPE}} as *const u8,";
code_ += " &x_le as *const _ as *const u8,";
code_ += " self.0[{{FIELD_OFFSET}}..].as_mut_ptr(),";
code_ += " core::mem::size_of::<{{FIELD_TYPE}}>(),";
code_ += " core::mem::size_of::<<{{FIELD_TYPE}} as EndianScalar>::Scalar>(),";
code_ += " );";
code_ += " }";
}

View File

@@ -1874,7 +1874,7 @@ class SwiftGenerator : public BaseGenerator {
}
std::string ValidateFunc() {
return "static func validateVersion() { FlatBuffersVersion_22_9_24() }";
return "static func validateVersion() { FlatBuffersVersion_22_10_26() }";
}
std::string GenType(const Type &type,

View File

@@ -759,10 +759,10 @@ class TsGenerator : public BaseGenerator {
import.object_name = object_name;
import.bare_file_path = bare_file_path;
import.rel_file_path = rel_file_path;
import.import_statement =
"import { " + symbols_expression + " } from '" + rel_file_path + ".js';";
import.export_statement =
"export { " + symbols_expression + " } from '." + bare_file_path + ".js';";
import.import_statement = "import { " + symbols_expression + " } from '" +
rel_file_path + ".js';";
import.export_statement = "export { " + symbols_expression + " } from '." +
bare_file_path + ".js';";
import.dependency = &dependency;
import.dependent = &dependent;
@@ -903,7 +903,7 @@ class TsGenerator : public BaseGenerator {
const auto conversion_function = GenUnionConvFuncName(enum_def);
ret = "(() => {\n";
ret += " let temp = " + conversion_function + "(this." +
ret += " const temp = " + conversion_function + "(this." +
namer_.Method(field_name, "Type") + "(), " +
field_binded_method + ");\n";
ret += " if(temp === null) { return null; }\n";
@@ -916,17 +916,17 @@ class TsGenerator : public BaseGenerator {
const auto conversion_function = GenUnionListConvFuncName(enum_def);
ret = "(() => {\n";
ret += " let ret = [];\n";
ret += " const ret = [];\n";
ret += " for(let targetEnumIndex = 0; targetEnumIndex < this." +
namer_.Method(field_name, "TypeLength") + "()" +
"; "
"++targetEnumIndex) {\n";
ret += " let targetEnum = this." +
ret += " const targetEnum = this." +
namer_.Method(field_name, "Type") + "(targetEnumIndex);\n";
ret += " if(targetEnum === null || " + enum_type +
"[targetEnum!] === 'NONE') { "
"continue; }\n\n";
ret += " let temp = " + conversion_function + "(targetEnum, " +
ret += " const temp = " + conversion_function + "(targetEnum, " +
field_binded_method + ", targetEnumIndex);\n";
ret += " if(temp === null) { continue; }\n";
ret += union_has_string ? " if(typeof temp === 'string') { "
@@ -1102,11 +1102,13 @@ class TsGenerator : public BaseGenerator {
switch (vectortype.base_type) {
case BASE_TYPE_STRUCT: {
const auto &sd = *field.value.type.struct_def;
field_type += GetTypeName(sd, /*object_api=*/true);
;
const auto field_type_name =
GetTypeName(sd, /*object_api=*/true);
field_type += field_type_name;
field_type += ")[]";
field_val = GenBBAccess() + ".createObjList(" +
field_val = GenBBAccess() + ".createObjList<" + vectortypename +
", " + field_type_name + ">(" +
field_binded_method + ", this." +
namer_.Method(field, "Length") + "())";
@@ -1128,7 +1130,7 @@ class TsGenerator : public BaseGenerator {
case BASE_TYPE_STRING: {
field_type += "string)[]";
field_val = GenBBAccess() + ".createScalarList(" +
field_val = GenBBAccess() + ".createScalarList<string>(" +
field_binded_method + ", this." +
namer_.Field(field, "Length") + "())";
field_offset_decl =
@@ -1162,9 +1164,9 @@ class TsGenerator : public BaseGenerator {
field_type += vectortypename;
}
field_type += ")[]";
field_val = GenBBAccess() + ".createScalarList(" +
field_binded_method + ", this." +
namer_.Method(field, "Length") + "())";
field_val = GenBBAccess() + ".createScalarList<" +
vectortypename + ">(" + field_binded_method +
", this." + namer_.Method(field, "Length") + "())";
field_offset_decl =
AddImport(imports, struct_def, struct_def).name + "." +
@@ -1260,7 +1262,7 @@ class TsGenerator : public BaseGenerator {
obj_api_class = "\n";
obj_api_class += "export class ";
obj_api_class += GetTypeName(struct_def, /*object_api=*/true);
obj_api_class += " {\n";
obj_api_class += " implements flatbuffers.IGeneratedObject {\n";
obj_api_class += constructor_func;
obj_api_class += pack_func_prototype + pack_func_offset_decl +
pack_func_create_call + "\n}";
@@ -1298,12 +1300,17 @@ class TsGenerator : public BaseGenerator {
}
const std::string object_name = GetTypeName(struct_def);
const std::string object_api_name = GetTypeName(struct_def, true);
// Emit constructor
GenDocComment(struct_def.doc_comment, code_ptr);
code += "export class ";
code += object_name;
code += " {\n";
if (parser.opts.generate_object_based_api)
code += " implements flatbuffers.IUnpackableObject<" + object_api_name +
"> {\n";
else
code += " {\n";
code += " bb: flatbuffers.ByteBuffer|null = null;\n";
code += " bb_pos = 0;\n";

View File

@@ -1584,7 +1584,6 @@ CheckedError Parser::ParseVectorDelimiters(uoffset_t &count, F body) {
return NoError();
}
CheckedError Parser::ParseAlignAttribute(const std::string &align_constant,
size_t min_align, size_t *align) {
// Use uint8_t to avoid problems with size_t==`unsigned long` on LP64.
@@ -2450,14 +2449,17 @@ CheckedError Parser::ParseEnum(const bool is_union, EnumDef **dest,
EXPECT(kTokenIntegerConstant);
}
ECHECK(evb.AcceptEnumerator());
if (opts.proto_mode && Is('[')) {
NEXT();
// ignore attributes on enums.
while (token_ != ']') NEXT();
NEXT();
} else {
// parse attributes in fbs schema
ECHECK(ParseMetaData(&ev.attributes));
}
ECHECK(evb.AcceptEnumerator());
}
if (!Is(opts.proto_mode ? ';' : ',')) break;
NEXT();
@@ -2552,7 +2554,8 @@ bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions &opts) {
IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster |
IDLOptions::kKotlin | IDLOptions::kCpp | IDLOptions::kJava |
IDLOptions::kCSharp | IDLOptions::kTs | IDLOptions::kBinary |
IDLOptions::kGo | IDLOptions::kPython | IDLOptions::kJson;
IDLOptions::kGo | IDLOptions::kPython | IDLOptions::kJson |
IDLOptions::kNim;
unsigned long langs = opts.lang_to_generate;
return (langs > 0 && langs < IDLOptions::kMAX) && !(langs & ~supported_langs);
}
@@ -2563,7 +2566,7 @@ bool Parser::SupportsOptionalScalars() const {
bool Parser::SupportsDefaultVectorsAndStrings() const {
static FLATBUFFERS_CONSTEXPR unsigned long supported_langs =
IDLOptions::kRust | IDLOptions::kSwift;
IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim;
return !(opts.lang_to_generate & ~supported_langs);
}
@@ -2571,7 +2574,7 @@ bool Parser::SupportsAdvancedUnionFeatures() const {
return (opts.lang_to_generate &
~(IDLOptions::kCpp | IDLOptions::kTs | IDLOptions::kPhp |
IDLOptions::kJava | IDLOptions::kCSharp | IDLOptions::kKotlin |
IDLOptions::kBinary | IDLOptions::kSwift)) == 0;
IDLOptions::kBinary | IDLOptions::kSwift | IDLOptions::kNim)) == 0;
}
bool Parser::SupportsAdvancedArrayFeatures() const {
@@ -3415,7 +3418,6 @@ CheckedError Parser::CheckPrivatelyLeakedFields(const Definition &def,
return NoError();
}
CheckedError Parser::DoParse(const char *source, const char **include_paths,
const char *source_filename,
const char *include_filename) {
@@ -3633,6 +3635,44 @@ std::set<std::string> Parser::GetIncludedFilesRecursive(
// Schema serialization functionality:
static flatbuffers::Offset<
flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>>
SerializeAttributesCommon(const SymbolTable<Value> &attributes,
FlatBufferBuilder *builder, const Parser &parser) {
std::vector<flatbuffers::Offset<reflection::KeyValue>> attrs;
for (auto kv = attributes.dict.begin(); kv != attributes.dict.end(); ++kv) {
auto it = parser.known_attributes_.find(kv->first);
FLATBUFFERS_ASSERT(it != parser.known_attributes_.end());
if (parser.opts.binary_schema_builtins || !it->second) {
auto key = builder->CreateString(kv->first);
auto val = builder->CreateString(kv->second->constant);
attrs.push_back(reflection::CreateKeyValue(*builder, key, val));
}
}
if (attrs.size()) {
return builder->CreateVectorOfSortedTables(&attrs);
} else {
return 0;
}
}
static bool DeserializeAttributesCommon(
SymbolTable<Value> &attributes, Parser &parser,
const Vector<Offset<reflection::KeyValue>> *attrs) {
if (attrs == nullptr) return true;
for (uoffset_t i = 0; i < attrs->size(); ++i) {
auto kv = attrs->Get(i);
auto value = new Value();
if (kv->value()) { value->constant = kv->value()->str(); }
if (attributes.Add(kv->key()->str(), value)) {
delete value;
return false;
}
parser.known_attributes_[kv->key()->str()];
}
return true;
}
void Parser::Serialize() {
builder_.Clear();
AssignIndices(structs_.vec);
@@ -3918,32 +3958,52 @@ bool EnumDef::Deserialize(Parser &parser, const reflection::Enum *_enum) {
return true;
}
Offset<reflection::EnumVal> EnumVal::Serialize(FlatBufferBuilder *builder,
const Parser &parser) const {
auto name__ = builder->CreateString(name);
auto type__ = union_type.Serialize(builder);
auto docs__ = parser.opts.binary_schema_comments
? builder->CreateVectorOfStrings(doc_comment)
: 0;
return reflection::CreateEnumVal(*builder, name__, value, type__, docs__);
flatbuffers::Offset<
flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>>
EnumVal::SerializeAttributes(FlatBufferBuilder *builder,
const Parser &parser) const {
return SerializeAttributesCommon(attributes, builder, parser);
}
bool EnumVal::Deserialize(const Parser &parser,
const reflection::EnumVal *val) {
bool EnumVal::DeserializeAttributes(
Parser &parser, const Vector<Offset<reflection::KeyValue>> *attrs) {
return DeserializeAttributesCommon(attributes, parser, attrs);
}
Offset<reflection::EnumVal> EnumVal::Serialize(FlatBufferBuilder *builder,
const Parser &parser) const {
const auto name__ = builder->CreateString(name);
const auto type__ = union_type.Serialize(builder);
const auto attr__ = SerializeAttributes(builder, parser);
const auto docs__ = parser.opts.binary_schema_comments
? builder->CreateVectorOfStrings(doc_comment)
: 0;
return reflection::CreateEnumVal(*builder, name__, value, type__, docs__,
attr__);
}
bool EnumVal::Deserialize(Parser &parser, const reflection::EnumVal *val) {
name = val->name()->str();
value = val->value();
if (!union_type.Deserialize(parser, val->union_type())) return false;
if (!DeserializeAttributes(parser, val->attributes())) return false;
DeserializeDoc(doc_comment, val->documentation());
return true;
}
Offset<reflection::Type> Type::Serialize(FlatBufferBuilder *builder) const {
size_t element_size = SizeOf(element);
if (base_type == BASE_TYPE_VECTOR && element == BASE_TYPE_STRUCT &&
struct_def->bytesize != 0) {
// struct_def->bytesize==0 means struct is table
element_size = struct_def->bytesize;
}
return reflection::CreateType(
*builder, static_cast<reflection::BaseType>(base_type),
static_cast<reflection::BaseType>(element),
struct_def ? struct_def->index : (enum_def ? enum_def->index : -1),
fixed_length, static_cast<uint32_t>(SizeOf(base_type)),
static_cast<uint32_t>(SizeOf(element)));
static_cast<uint32_t>(element_size));
}
bool Type::Deserialize(const Parser &parser, const reflection::Type *type) {
@@ -3977,37 +4037,12 @@ flatbuffers::Offset<
flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>>
Definition::SerializeAttributes(FlatBufferBuilder *builder,
const Parser &parser) const {
std::vector<flatbuffers::Offset<reflection::KeyValue>> attrs;
for (auto kv = attributes.dict.begin(); kv != attributes.dict.end(); ++kv) {
auto it = parser.known_attributes_.find(kv->first);
FLATBUFFERS_ASSERT(it != parser.known_attributes_.end());
if (parser.opts.binary_schema_builtins || !it->second) {
auto key = builder->CreateString(kv->first);
auto val = builder->CreateString(kv->second->constant);
attrs.push_back(reflection::CreateKeyValue(*builder, key, val));
}
}
if (attrs.size()) {
return builder->CreateVectorOfSortedTables(&attrs);
} else {
return 0;
}
return SerializeAttributesCommon(attributes, builder, parser);
}
bool Definition::DeserializeAttributes(
Parser &parser, const Vector<Offset<reflection::KeyValue>> *attrs) {
if (attrs == nullptr) return true;
for (uoffset_t i = 0; i < attrs->size(); ++i) {
auto kv = attrs->Get(i);
auto value = new Value();
if (kv->value()) { value->constant = kv->value()->str(); }
if (attributes.Add(kv->key()->str(), value)) {
delete value;
return false;
}
parser.known_attributes_[kv->key()->str()];
}
return true;
return DeserializeAttributesCommon(attributes, parser, attrs);
}
/************************************************************************/
@@ -4122,7 +4157,7 @@ std::string Parser::ConformTo(const Parser &base) {
struct_def.defined_namespace->GetFullyQualifiedName(struct_def.name);
auto struct_def_base = base.LookupStruct(qualified_name);
if (!struct_def_base) continue;
std::set<FieldDef*> renamed_fields;
std::set<FieldDef *> renamed_fields;
for (auto fit = struct_def.fields.vec.begin();
fit != struct_def.fields.vec.end(); ++fit) {
auto &field = **fit;
@@ -4151,15 +4186,15 @@ std::string Parser::ConformTo(const Parser &base) {
}
}
}
//deletion of trailing fields are not allowed
// deletion of trailing fields are not allowed
for (auto fit = struct_def_base->fields.vec.begin();
fit != struct_def_base->fields.vec.end(); ++fit ) {
fit != struct_def_base->fields.vec.end(); ++fit) {
auto &field_base = **fit;
//not a renamed field
// not a renamed field
if (renamed_fields.find(&field_base) == renamed_fields.end()) {
auto field = struct_def.fields.Lookup(field_base.name);
if (!field) {
return "field deleted: " + qualified_name + "." + field_base.name;
if (!field) {
return "field deleted: " + qualified_name + "." + field_base.name;
}
}
}

View File

@@ -1,15 +1,11 @@
FlatBuffers swift can be found in both SPM
`.package(url: "https://github.com/mustiikhalil/flatbuffers.git", from: "X.Y.Z"),`
`.package(url: "https://github.com/google/flatbuffers.git", from: "X.Y.Z"),`
and Cocoapods
`pod 'FlatBuffers'`
### Notes
1- To report any error please use the main repository.
### Contribute
1- Always run `swift test --generate-linuxmain` whenever new test functions are added or removed

View File

@@ -119,4 +119,4 @@ extension UInt64: Scalar, Verifiable {
public typealias NumericValue = UInt64
}
public func FlatBuffersVersion_22_9_24() {}
public func FlatBuffersVersion_22_10_26() {}

13
tests/Abc.nim Normal file
View File

@@ -0,0 +1,13 @@
#[ Abc
Automatically generated by the FlatBuffers compiler, do not modify.
Or modify. I'm a message, not a cop.
flatc version: 22.10.26
Declared by :
]#
type Abc*{.pure.} = enum
A = 0.int32,
B = 1.int32,
C = 2.int32,

View File

@@ -9,7 +9,7 @@ import com.google.flatbuffers.*;
@SuppressWarnings("unused")
public final class LongFloatEntry extends Table {
public static void ValidateVersion() { Constants.FLATBUFFERS_22_9_24(); }
public static void ValidateVersion() { Constants.FLATBUFFERS_22_10_26(); }
public static LongFloatEntry getRootAsLongFloatEntry(ByteBuffer _bb) { return getRootAsLongFloatEntry(_bb, new LongFloatEntry()); }
public static LongFloatEntry getRootAsLongFloatEntry(ByteBuffer _bb, LongFloatEntry obj) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__assign(_bb.getInt(_bb.position()) + _bb.position(), _bb)); }
public void __init(int _i, ByteBuffer _bb) { __reset(_i, _bb); }

View File

@@ -32,7 +32,7 @@ class LongFloatEntry : Table() {
return (val_1 - val_2).sign
}
companion object {
fun validateVersion() = Constants.FLATBUFFERS_22_9_24()
fun validateVersion() = Constants.FLATBUFFERS_22_10_26()
fun getRootAsLongFloatEntry(_bb: ByteBuffer): LongFloatEntry = getRootAsLongFloatEntry(_bb, LongFloatEntry())
fun getRootAsLongFloatEntry(_bb: ByteBuffer, obj: LongFloatEntry): LongFloatEntry {
_bb.order(ByteOrder.LITTLE_ENDIAN)

View File

@@ -9,7 +9,7 @@ import com.google.flatbuffers.*;
@SuppressWarnings("unused")
public final class LongFloatMap extends Table {
public static void ValidateVersion() { Constants.FLATBUFFERS_22_9_24(); }
public static void ValidateVersion() { Constants.FLATBUFFERS_22_10_26(); }
public static LongFloatMap getRootAsLongFloatMap(ByteBuffer _bb) { return getRootAsLongFloatMap(_bb, new LongFloatMap()); }
public static LongFloatMap getRootAsLongFloatMap(ByteBuffer _bb, LongFloatMap obj) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__assign(_bb.getInt(_bb.position()) + _bb.position(), _bb)); }
public void __init(int _i, ByteBuffer _bb) { __reset(_i, _bb); }

View File

@@ -46,7 +46,7 @@ class LongFloatMap : Table() {
}
}
companion object {
fun validateVersion() = Constants.FLATBUFFERS_22_9_24()
fun validateVersion() = Constants.FLATBUFFERS_22_10_26()
fun getRootAsLongFloatMap(_bb: ByteBuffer): LongFloatMap = getRootAsLongFloatMap(_bb, LongFloatMap())
fun getRootAsLongFloatMap(_bb: ByteBuffer, obj: LongFloatMap): LongFloatMap {
_bb.order(ByteOrder.LITTLE_ENDIAN)

View File

@@ -13,7 +13,7 @@ public struct KeywordsInTable : IFlatbufferObject
{
private Table __p;
public ByteBuffer ByteBuffer { get { return __p.bb; } }
public static void ValidateVersion() { FlatBufferConstants.FLATBUFFERS_22_9_24(); }
public static void ValidateVersion() { FlatBufferConstants.FLATBUFFERS_22_10_26(); }
public static KeywordsInTable GetRootAsKeywordsInTable(ByteBuffer _bb) { return GetRootAsKeywordsInTable(_bb, new KeywordsInTable()); }
public static KeywordsInTable GetRootAsKeywordsInTable(ByteBuffer _bb, KeywordsInTable obj) { return (obj.__assign(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
public void __init(int _i, ByteBuffer _bb) { __p = new Table(_i, _bb); }

103
tests/MoreDefaults.nim Normal file
View File

@@ -0,0 +1,103 @@
#[ MoreDefaults
Automatically generated by the FlatBuffers compiler, do not modify.
Or modify. I'm a message, not a cop.
flatc version: 22.10.26
Declared by :
]#
import Abc as Abc
import flatbuffers
type MoreDefaults* = object of FlatObj
func intsLength*(self: MoreDefaults): int =
let o = self.tab.Offset(4)
if o != 0:
return self.tab.VectorLen(o)
func ints*(self: MoreDefaults, j: int): int32 =
let o = self.tab.Offset(4)
if o != 0:
var x = self.tab.Vector(o)
x += j.uoffset * 4.uoffset
return Get[int32](self.tab, x)
func ints*(self: MoreDefaults): seq[int32] =
let len = self.intsLength
for i in countup(0, len - 1):
result.add(self.ints(i))
func floatsLength*(self: MoreDefaults): int =
let o = self.tab.Offset(6)
if o != 0:
return self.tab.VectorLen(o)
func floats*(self: MoreDefaults, j: int): float32 =
let o = self.tab.Offset(6)
if o != 0:
var x = self.tab.Vector(o)
x += j.uoffset * 4.uoffset
return Get[float32](self.tab, x)
func floats*(self: MoreDefaults): seq[float32] =
let len = self.floatsLength
for i in countup(0, len - 1):
result.add(self.floats(i))
func emptyString*(self: MoreDefaults): string =
let o = self.tab.Offset(8)
if o != 0:
return self.tab.String(self.tab.Pos + o)
return ""
func someString*(self: MoreDefaults): string =
let o = self.tab.Offset(10)
if o != 0:
return self.tab.String(self.tab.Pos + o)
return ""
func abcsLength*(self: MoreDefaults): int =
let o = self.tab.Offset(12)
if o != 0:
return self.tab.VectorLen(o)
func abcs*(self: MoreDefaults, j: int): Abc.Abc =
let o = self.tab.Offset(12)
if o != 0:
var x = self.tab.Vector(o)
x += j.uoffset * 4.uoffset
return Abc.Abc(Get[int32](self.tab, x))
func abcs*(self: MoreDefaults): seq[Abc.Abc] =
let len = self.abcsLength
for i in countup(0, len - 1):
result.add(self.abcs(i))
func boolsLength*(self: MoreDefaults): int =
let o = self.tab.Offset(14)
if o != 0:
return self.tab.VectorLen(o)
func bools*(self: MoreDefaults, j: int): bool =
let o = self.tab.Offset(14)
if o != 0:
var x = self.tab.Vector(o)
x += j.uoffset * 1.uoffset
return Get[bool](self.tab, x)
func bools*(self: MoreDefaults): seq[bool] =
let len = self.boolsLength
for i in countup(0, len - 1):
result.add(self.bools(i))
proc MoreDefaultsStart*(builder: var Builder) =
builder.StartObject(6)
proc MoreDefaultsAddints*(builder: var Builder, ints: uoffset) =
builder.PrependSlot(0, ints, default(uoffset))
proc MoreDefaultsStartintsVector*(builder: var Builder, numElems: uoffset) =
builder.StartVector(4, numElems, 4)
proc MoreDefaultsAddfloats*(builder: var Builder, floats: uoffset) =
builder.PrependSlot(1, floats, default(uoffset))
proc MoreDefaultsStartfloatsVector*(builder: var Builder, numElems: uoffset) =
builder.StartVector(4, numElems, 4)
proc MoreDefaultsAddemptyString*(builder: var Builder, emptyString: uoffset) =
builder.PrependSlot(2, emptyString, default(uoffset))
proc MoreDefaultsAddsomeString*(builder: var Builder, someString: uoffset) =
builder.PrependSlot(3, someString, default(uoffset))
proc MoreDefaultsAddabcs*(builder: var Builder, abcs: uoffset) =
builder.PrependSlot(4, abcs, default(uoffset))
proc MoreDefaultsStartabcsVector*(builder: var Builder, numElems: uoffset) =
builder.StartVector(4, numElems, 4)
proc MoreDefaultsAddbools*(builder: var Builder, bools: uoffset) =
builder.PrependSlot(5, bools, default(uoffset))
proc MoreDefaultsStartboolsVector*(builder: var Builder, numElems: uoffset) =
builder.StartVector(1, numElems, 1)
proc MoreDefaultsEnd*(builder: var Builder): uoffset =
return builder.EndObject()

View File

@@ -3,7 +3,7 @@
Automatically generated by the FlatBuffers compiler, do not modify.
Or modify. I'm a message, not a cop.
flatc version: 22.9.24
flatc version: 22.10.26
Declared by : //monster_test.fbs
Rooting type : MyGame.Example.Monster (//monster_test.fbs)

View File

@@ -0,0 +1,26 @@
#[ MyGame.Example.Ability
Automatically generated by the FlatBuffers compiler, do not modify.
Or modify. I'm a message, not a cop.
flatc version: 22.10.26
Declared by :
Rooting type : MyGame.Example.Monster ()
]#
import flatbuffers
type Ability* = object of FlatObj
func id*(self: Ability): uint32 =
return Get[uint32](self.tab, self.tab.Pos + 0)
func `id=`*(self: var Ability, n: uint32): bool =
return self.tab.Mutate(self.tab.Pos + 0, n)
func distance*(self: Ability): uint32 =
return Get[uint32](self.tab, self.tab.Pos + 4)
func `distance=`*(self: var Ability, n: uint32): bool =
return self.tab.Mutate(self.tab.Pos + 4, n)
proc AbilityCreate*(self: var Builder, id: uint32, distance: uint32): uoffset =
self.Prep(4, 8)
self.Prepend(distance)
self.Prepend(id)
return self.Offset()

View File

@@ -38,11 +38,15 @@ class AbilityT(object):
@classmethod
def InitFromBuf(cls, buf, pos):
n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, 0)
ability = Ability()
ability.Init(buf, pos+n)
ability.Init(buf, pos)
return cls.InitFromObj(ability)
@classmethod
def InitFromPackedBuf(cls, buf, pos=0):
n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, pos)
return cls.InitFromBuf(buf, pos+n)
@classmethod
def InitFromObj(cls, ability):
x = AbilityT()

View File

@@ -3,7 +3,7 @@
Automatically generated by the FlatBuffers compiler, do not modify.
Or modify. I'm a message, not a cop.
flatc version: 22.9.24
flatc version: 22.10.26
Declared by : //monster_test.fbs
Rooting type : MyGame.Example.Monster (//monster_test.fbs)

View File

@@ -0,0 +1,15 @@
#[ MyGame.Example.Any
Automatically generated by the FlatBuffers compiler, do not modify.
Or modify. I'm a message, not a cop.
flatc version: 22.10.26
Declared by :
Rooting type : MyGame.Example.Monster ()
]#
type Any*{.pure.} = enum
None = 0.uint8,
Monster = 1.uint8,
TestSimpleTableWithEnum = 2.uint8,
MyGameExample2Monster = 3.uint8,

View File

@@ -3,7 +3,7 @@
Automatically generated by the FlatBuffers compiler, do not modify.
Or modify. I'm a message, not a cop.
flatc version: 22.9.24
flatc version: 22.10.26
Declared by : //monster_test.fbs
Rooting type : MyGame.Example.Monster (//monster_test.fbs)

View File

@@ -0,0 +1,15 @@
#[ MyGame.Example.AnyAmbiguousAliases
Automatically generated by the FlatBuffers compiler, do not modify.
Or modify. I'm a message, not a cop.
flatc version: 22.10.26
Declared by :
Rooting type : MyGame.Example.Monster ()
]#
type AnyAmbiguousAliases*{.pure.} = enum
None = 0.uint8,
M1 = 1.uint8,
M2 = 2.uint8,
M3 = 3.uint8,

View File

@@ -3,7 +3,7 @@
Automatically generated by the FlatBuffers compiler, do not modify.
Or modify. I'm a message, not a cop.
flatc version: 22.9.24
flatc version: 22.10.26
Declared by : //monster_test.fbs
Rooting type : MyGame.Example.Monster (//monster_test.fbs)

View File

@@ -0,0 +1,15 @@
#[ MyGame.Example.AnyUniqueAliases
Automatically generated by the FlatBuffers compiler, do not modify.
Or modify. I'm a message, not a cop.
flatc version: 22.10.26
Declared by :
Rooting type : MyGame.Example.Monster ()
]#
type AnyUniqueAliases*{.pure.} = enum
None = 0.uint8,
M = 1.uint8,
Ts = 2.uint8,
M2 = 3.uint8,

Some files were not shown because too many files have changed in this diff Show More