Compare commits

..
Author SHA1 Message Date
dependabot[bot] 7ffc8d8346 Bump softprops/action-gh-release from 2 to 3
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-08 01:50:55 +00:00
29 changed files with 104 additions and 447 deletions
+5 -5
View File
@@ -51,7 +51,7 @@ jobs:
if: startsWith(github.ref, 'refs/tags/')
run: zip Linux.flatc.binary.${{ matrix.cxx }}.zip flatc
- name: Release zip file
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
if: startsWith(github.ref, 'refs/tags/')
with:
files: Linux.flatc.binary.${{ matrix.cxx }}.zip
@@ -179,7 +179,7 @@ jobs:
if: startsWith(github.ref, 'refs/tags/')
run: move Release/flatc.exe . && Compress-Archive flatc.exe Windows.flatc.binary.zip
- name: Release binary
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
if: startsWith(github.ref, 'refs/tags/')
with:
files: Windows.flatc.binary.zip
@@ -255,7 +255,7 @@ jobs:
if: startsWith(github.ref, 'refs/tags/')
run: mv Release/flatc . && zip MacIntel.flatc.binary.zip flatc
- name: Release binary
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
if: startsWith(github.ref, 'refs/tags/')
with:
files: MacIntel.flatc.binary.zip
@@ -298,7 +298,7 @@ jobs:
if: startsWith(github.ref, 'refs/tags/')
run: mv Release/flatc . && zip Mac.flatc.binary.zip flatc
- name: Release binary
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
if: startsWith(github.ref, 'refs/tags/')
with:
files: Mac.flatc.binary.zip
@@ -545,7 +545,7 @@ jobs:
# 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
- name: pnpm
run: npm install -g pnpm@10
run: npm install -g pnpm
- name: deps
run: pnpm i
- name: compile
-11
View File
@@ -75,17 +75,6 @@ extension Array where Element == Package.Dependency {
.package(
url: "https://github.com/grpc/grpc-swift-nio-transport.git",
from: "2.0.0"),
// swift-crypto arrives through grpc-swift-nio-transport -> swift-nio-ssl
// and is otherwise resolved afresh on every build, because this package
// ships no Package.resolved. 5.x asks for swift-tools-version 6.2, so only
// the 6.2 CI job selects it - and then fails to compile it, because 5.x
// uses `@nonexhaustive`, which the 6.2.x toolchains do not accept yet.
// Stay on the last 4.x release that still builds with every toolchain in
// the Swift CI matrix (4.3.0 declares swift-tools-version 6.0; 4.4+ asks
// for 6.1 and would drop the 6.0 job).
.package(
url: "https://github.com/apple/swift-crypto.git",
"4.0.0" ..< "4.4.0"),
]
#endif
}
@@ -97,7 +97,6 @@ let benchmarks = {
for _ in benchmark.scaledIterations {
blackHole(ByteBuffer(assumingMemoryBound: memory, capacity: Int(oneGB)))
}
benchmark.stopMeasurement()
}
Benchmark("Clearing 1GB", configuration: singleConfiguration) { benchmark in
@@ -106,7 +105,6 @@ let benchmarks = {
for _ in benchmark.scaledIterations {
blackHole(fb.clear())
}
benchmark.stopMeasurement()
}
Benchmark("Strings 10") { benchmark in
@@ -115,7 +113,6 @@ let benchmarks = {
for _ in benchmark.scaledIterations {
blackHole(fb.create(string: str10))
}
benchmark.stopMeasurement()
}
Benchmark("Strings 100") { benchmark in
@@ -124,7 +121,6 @@ let benchmarks = {
for _ in benchmark.scaledIterations {
blackHole(fb.create(string: str100))
}
benchmark.stopMeasurement()
}
Benchmark("Vector 1 Bytes") { benchmark in
@@ -133,7 +129,6 @@ let benchmarks = {
for _ in benchmark.scaledIterations {
blackHole(fb.createVector(bytes: bytes))
}
benchmark.stopMeasurement()
}
Benchmark("Vector 1 Ints") { benchmark in
@@ -142,7 +137,6 @@ let benchmarks = {
for _ in benchmark.scaledIterations {
blackHole(fb.createVector(ints))
}
benchmark.stopMeasurement()
}
Benchmark("Vector 100 Ints") { benchmark in
@@ -151,7 +145,6 @@ let benchmarks = {
for i in benchmark.scaledIterations {
blackHole(fb.createVector(ints))
}
benchmark.stopMeasurement()
}
Benchmark("Vector 100 Bytes") { benchmark in
@@ -160,7 +153,6 @@ let benchmarks = {
for i in benchmark.scaledIterations {
blackHole(fb.createVector(bytes))
}
benchmark.stopMeasurement()
}
Benchmark("Vector 100 ContiguousBytes") { benchmark in
@@ -169,7 +161,6 @@ let benchmarks = {
for i in benchmark.scaledIterations {
blackHole(fb.createVector(bytes: bytes))
}
benchmark.stopMeasurement()
}
Benchmark(
@@ -187,7 +178,6 @@ let benchmarks = {
fb.add(offset: off, at: 8)
blackHole(fb.endTable(at: s))
}
benchmark.stopMeasurement()
}
Benchmark(
@@ -200,7 +190,6 @@ let benchmarks = {
let s = fb.startTable(with: 4)
blackHole(fb.endTable(at: s))
}
benchmark.stopMeasurement()
}
Benchmark("Struct") { benchmark in
@@ -209,7 +198,6 @@ let benchmarks = {
for _ in benchmark.scaledIterations {
blackHole(fb.create(struct: array.first!))
}
benchmark.stopMeasurement()
}
Benchmark("Structs") { benchmark in
@@ -231,7 +219,6 @@ let benchmarks = {
fb.add(offset: vector, at: 4)
let root = Offset(offset: fb.endTable(at: start))
blackHole(fb.finish(offset: root))
benchmark.stopMeasurement()
}
Benchmark("Vector of Offsets") { benchmark in
@@ -252,15 +239,12 @@ let benchmarks = {
fb.add(offset: off, at: 2)
blackHole(fb.endTable(at: s))
}
benchmark.stopMeasurement()
}
Benchmark("Reading Doubles") { benchmark in
let byteBuffer = ByteBuffer(data: data)
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
blackHole(byteBuffer.read(def: Double.self, position: 0))
}
benchmark.stopMeasurement()
}
}
@@ -1,174 +0,0 @@
/*
* Copyright 2024 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.
*/
import Benchmark
import FlexBuffers
import Foundation
let benchmarks = {
let data = {
var array = [8888.88, 8888.88]
var data = Data()
array.withUnsafeBytes { ptr in
data.append(contentsOf: ptr)
}
return data
}()
let ints: [Int32] = Array(repeating: 42, count: 100)
let str10 = (0...9).map { _ -> String in "x" }.joined()
let str100 = (0...99).map { _ -> String in "x" }.joined()
// A representative map: 50 keyed scalars, a keyed string and a keyed vector
// of 100 scalars. Used for the realistic decode benchmarks.
let mapBuffer: ByteBuffer = {
var fbx = FlexBuffersWriter(initialSize: 1 << 16)
fbx.map {
for i in 0..<50 { $0.add(int: i, key: "i\(i)") }
$0.add(string: "hello world", key: "s")
$0.vector(key: "v") { v in
for x in 0..<100 { v.add(int: x) }
}
}
fbx.finish()
return fbx.sizedByteBuffer
}()
let metrics: [BenchmarkMetric] = [
.cpuTotal,
.wallClock,
.mallocCountTotal,
.releaseCount,
.peakMemoryResident,
]
let maxIterations = 1_000_000
let maxDuration: Duration = .seconds(3)
let megaConfiguration: Benchmark.Configuration = .init(
metrics: metrics,
warmupIterations: 1,
scalingFactor: .mega,
maxDuration: maxDuration,
maxIterations: maxIterations)
Benchmark.defaultConfiguration = megaConfiguration
// Decode (read path)
// Raw scalar read: isolates `read<T: BitwiseCopyable>` and the `let` blob.
Benchmark("Reading Doubles") { benchmark in
let byteBuffer = ByteBuffer(data: data)
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
blackHole(byteBuffer.read(def: Double.self, position: 0))
}
benchmark.stopMeasurement()
}
// Realistic decode: resolve root map and read a keyed scalar.
Benchmark("Decode Map Scalar") { benchmark in
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
let map = try! getRoot(buffer: mapBuffer)!.map!
blackHole(map["i25"]?.int)
}
benchmark.stopMeasurement()
}
// Realistic decode: resolve root map and read a keyed string.
Benchmark("Decode Map String") { benchmark in
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
let map = try! getRoot(buffer: mapBuffer)!.map!
blackHole(map["s"]?.string())
}
benchmark.stopMeasurement()
}
// Realistic decode: resolve a nested vector and sum its scalars.
Benchmark("Decode Vector") { benchmark in
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
let map = try! getRoot(buffer: mapBuffer)!.map!
let vector = map["v"]!.vector!
var sum: Int64 = 0
for i in 0..<vector.count {
sum &+= vector[i]?.int ?? 0
}
blackHole(sum)
}
benchmark.stopMeasurement()
}
// Encode (write path)
// Writers are reused with `reset(keepingCapacity:)` so per-iteration
// allocation does not dominate and mask the write-path cost (which is what
// the `@exclusivity(unchecked)` storage pointer affects).
Benchmark("Strings 10") { benchmark in
var fbx = FlexBuffersWriter(initialSize: 1 << 20)
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
fbx.add(string: str10)
fbx.finish()
blackHole(fbx.sizedByteBuffer)
fbx.reset(keepingCapacity: true)
}
benchmark.stopMeasurement()
}
Benchmark("Strings 100") { benchmark in
var fbx = FlexBuffersWriter(initialSize: 1 << 20)
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
fbx.add(string: str100)
fbx.finish()
blackHole(fbx.sizedByteBuffer)
fbx.reset(keepingCapacity: true)
}
benchmark.stopMeasurement()
}
Benchmark("Encoding Vector Of Ints") { benchmark in
var fbx = FlexBuffersWriter(initialSize: 1 << 20)
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
fbx.vector {
$0.create(vector: ints)
}
fbx.finish()
blackHole(fbx.sizedByteBuffer)
fbx.reset(keepingCapacity: true)
}
benchmark.stopMeasurement()
}
Benchmark("Encoding Map") { benchmark in
var fbx = FlexBuffersWriter(initialSize: 1 << 20)
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
fbx.map {
for i in 0..<50 { $0.add(int: i, key: "i\(i)") }
$0.add(string: "hello world", key: "s")
$0.vector(key: "v") { v in
for x in 0..<100 { v.add(int: x) }
}
}
fbx.finish()
blackHole(fbx.sizedByteBuffer)
fbx.reset(keepingCapacity: true)
}
benchmark.stopMeasurement()
}
}
-10
View File
@@ -39,14 +39,4 @@ let package = Package(
plugins: [
.plugin(name: "BenchmarkPlugin", package: "package-benchmark"),
]),
.executableTarget(
name: "FlexBuffersBenchmarks",
dependencies: [
.product(name: "FlexBuffers", package: "flatbuffers"),
.product(name: "Benchmark", package: "package-benchmark"),
],
path: "Benchmarks/FlexBuffersBenchmarks",
plugins: [
.plugin(name: "BenchmarkPlugin", package: "package-benchmark"),
]),
])
+1
View File
@@ -504,6 +504,7 @@ grpc::string GenerateInterface(grpc_generator::File* file,
const grpc::string& filename) {
grpc::string output;
std::set<grpc::string> generated_functions;
std::map<grpc::string, grpc::string> vars;
vars["PATH"] = file->package();
+1 -1
View File
@@ -271,7 +271,7 @@ namespace flatbuffers {
#if (defined(_MSC_VER) && _MSC_VER >= 1800) || \
(defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__>= 26) || \
(defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 700)) && \
!defined(__Fuchsia__) && (!defined(__linux__) || defined(__GLIBC__))
!defined(__Fuchsia__)
#define FLATBUFFERS_LOCALE_INDEPENDENT 1
#else
#define FLATBUFFERS_LOCALE_INDEPENDENT 0
+9 -8
View File
@@ -24,14 +24,15 @@ class handle:
struct offset:
o:int
let sz_8 = 1
let sz_16 = 2
let sz_32 = 4
let sz_64 = 8
let sz_voffset = 2
let sz_uoffset = 4
let sz_soffset = 4
let sz_metadata_fields = 2
enum sizeof:
sz_8 = 1
sz_16 = 2
sz_32 = 4
sz_64 = 8
sz_voffset = 2
sz_uoffset = 4
sz_soffset = 4
sz_metadata_fields = 2
class builder:
buf = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+2
View File
@@ -722,6 +722,8 @@ void BinaryAnnotator::BuildTable(const uint64_t table_offset,
BinaryRegionComment offset_field_comment;
offset_field_comment.type = BinaryRegionCommentType::TableOffsetField;
offset_field_comment.name = field->name()->c_str();
const std::string offset_prefix =
"offset to field `" + std::string(field->name()->c_str()) + "`";
// Validate any field that isn't inline (i.e., non-structs).
if (!IsInlineField(field)) {
+3 -1
View File
@@ -404,7 +404,7 @@ class CppGenerator : public BaseGenerator {
code_ += "struct {{STRUCT_NAME}}BinarySchema {";
code_ += " static const uint8_t *data() {";
code_ += " // Buffer containing the binary schema.";
code_ += " alignas(uint64_t) static const uint8_t bfbsData[" +
code_ += " static const uint8_t bfbsData[" +
NumToString(parser_.builder_.GetSize()) + "] = {";
code_ += binary_schema_hex_text;
code_ += " };";
@@ -2737,6 +2737,7 @@ class CppGenerator : public BaseGenerator {
if (!field.IsScalarOptional()) {
const bool is_scalar = IsScalar(type.base_type);
std::string accessor;
std::string offset_size = "";
if (is_scalar) {
accessor = "GetField<";
} else if (IsStruct(type)) {
@@ -2814,6 +2815,7 @@ class CppGenerator : public BaseGenerator {
void GenTableFieldType(const FieldDef& field) {
const auto& type = field.value.type;
const auto offset_str = GenFieldOffsetName(field);
if (!field.IsScalarOptional()) {
std::string afterptr = " *" + NullableExtension();
code_.SetValue("FIELD_TYPE",
+27 -6
View File
@@ -654,15 +654,34 @@ class DartGenerator : public BaseGenerator {
std::string NamespaceAliasFromUnionType(Namespace* root_namespace,
const Type& type) {
const Namespace& type_namespace = *type.struct_def->defined_namespace;
if (root_namespace->components == type_namespace.components) {
const std::vector<std::string> qualified_name_parts =
type.struct_def->defined_namespace->components;
if (std::equal(root_namespace->components.begin(),
root_namespace->components.end(),
qualified_name_parts.begin())) {
return namer_.Type(*type.struct_def);
}
const std::string ns = namer_.Namespace(type_namespace);
return ns.empty()
? namer_.Type(*type.struct_def)
: ImportAliasName(ns) + "." + namer_.Type(*type.struct_def);
std::string ns;
for (auto it = qualified_name_parts.begin();
it != qualified_name_parts.end(); ++it) {
auto& part = *it;
for (size_t i = 0; i < part.length(); i++) {
if (i && !isdigit(part[i]) && part[i] == CharToUpper(part[i])) {
ns += "_";
ns += CharToLower(part[i]);
} else {
ns += CharToLower(part[i]);
}
}
if (it != qualified_name_parts.end() - 1) {
ns += "_";
}
}
return ns + "." + namer_.Type(*type.struct_def);
}
void GenImplementationGetters(
@@ -1149,6 +1168,8 @@ static bool GenerateDart(const Parser& parser, const std::string& path,
static std::string DartMakeRule(const Parser& parser, const std::string& path,
const std::string& file_name) {
auto filebase =
flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
dart::DartGenerator generator(parser, path, file_name);
auto make_rule = generator.Filename("") + ": ";
+2
View File
@@ -966,6 +966,8 @@ class KotlinGenerator : public BaseGenerator {
auto bbgetter = ByteBufferGetter(field.value.type, "bb");
auto ucast = CastToUsigned(field);
auto offset_val = NumToString(field.value.offset);
auto offset_prefix =
"val o = __offset(" + offset_val + "); return o != 0 ? ";
auto value_base_type = field.value.type.base_type;
// Most field accessors need to retrieve and test the field offset
// first, this is the offset value for that:
+5
View File
@@ -146,6 +146,7 @@ class KotlinKMPGenerator : public BaseGenerator {
std::string code =
"// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
auto qualified = ns.GetFullyQualifiedName("");
std::string namespace_name = namer_.Namespace(ns);
if (!namespace_name.empty()) {
code += "package " + namespace_name;
@@ -340,6 +341,7 @@ class KotlinKMPGenerator : public BaseGenerator {
std::string GenDefaultBufferValue(const FieldDef& field) const {
auto& value = field.value;
auto base_type = value.type.base_type;
auto field_name = field.name;
std::string suffix = IsScalar(base_type) ? LiteralSuffix(value.type) : "";
if (field.IsScalarOptional()) {
return "null";
@@ -371,6 +373,7 @@ class KotlinKMPGenerator : public BaseGenerator {
std::string GenDefaultValue(const FieldDef& field) const {
auto& value = field.value;
auto base_type = value.type.base_type;
auto field_name = field.name;
std::string suffix = LiteralSuffix(value.type);
if (field.IsScalarOptional()) {
return "null";
@@ -1079,6 +1082,8 @@ class KotlinKMPGenerator : public BaseGenerator {
auto return_type = GetterReturnType(field);
auto bbgetter = ByteBufferGetter(field.value.type, "bb");
auto offset_val = NumToString(field.value.offset);
auto offset_prefix =
"val o = offset(" + offset_val + "); return o != 0 ? ";
auto value_base_type = field.value.type.base_type;
// Most field accessors need to retrieve and test the field offset
// first, this is the offset value for that:
+2
View File
@@ -2010,6 +2010,8 @@ class PythonGenerator : public BaseGenerator {
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):";
+2
View File
@@ -787,6 +787,7 @@ class SwiftGenerator : public BaseGenerator {
}
void GenTableReaderFields(const FieldDef& field) {
const auto offset = NumToString(field.value.offset);
const auto field_field = namer_.Field(field);
const auto type = GenType(field.value.type);
code_.SetValue("FIELDVAR", namer_.Variable(field));
@@ -1381,6 +1382,7 @@ class SwiftGenerator : public BaseGenerator {
const auto& field = **it;
if (field.deprecated) continue;
const auto type = GenType(field.value.type);
code_.SetValue("FIELDVAR", namer_.Variable(field));
if (IsArray(field.value.type)) {
code_.SetValue(
+5
View File
@@ -196,6 +196,7 @@ class TsGenerator : public BaseGenerator {
SkipDir::OutputPathAndTrailingPathSeparator);
symbolic_name = definition.defined_namespace->components.back();
} else {
auto def_mod_name = namer_.File(definition, SkipFile::SuffixAndExtension);
symbolic_name = file_name_;
filepath = path_ + symbolic_name + ".ts";
}
@@ -285,10 +286,12 @@ class TsGenerator : public BaseGenerator {
auto base_name =
namer_.Directories(it.second.ns->components, SkipDir::OutputPath) +
base_file_name;
auto ts_file_path = base_name + ".ts";
auto base_name_rel = std::string("./");
base_name_rel +=
namer_.Directories(rel_components, SkipDir::OutputPath);
base_name_rel += base_file_name;
auto ts_file_path_rel = base_name_rel + ".ts";
auto type_name = def.first;
auto fully_qualified_type_name =
it.second.ns->GetFullyQualifiedName(type_name);
@@ -308,6 +311,7 @@ class TsGenerator : public BaseGenerator {
const auto child_ns_level = it.second.ns->components.size() + 1;
for (const auto& it2 : ns_defs_) {
if (it2.second.ns->components.size() != child_ns_level) continue;
auto ts_file_path = it2.second.path + ".ts";
code += "export * as " + it2.second.symbolic_name + " from './";
int count = it2.second.ns->components.size() > 1 ? 2 : 1;
std::vector<std::string> rel_path;
@@ -1172,6 +1176,7 @@ class TsGenerator : public BaseGenerator {
if (union_type.enum_def) {
const auto& enum_def = *union_type.enum_def;
const auto enum_type = AddImport(imports, dependent, enum_def).name;
const std::string union_accessor = "this." + field_name;
const auto union_has_string = UnionHasStringType(enum_def);
const auto field_binded_method = "this." + field_name + ".bind(this)";
+1 -1
View File
@@ -28,7 +28,7 @@ public let FileIdLength = 4
/// Protocol that All Scalars should conform to
///
/// Scalar is used to conform all the numbers that can be represented in a FlatBuffer. It's used to write/read from the buffer.
public protocol Scalar: Equatable, BitwiseCopyable {
public protocol Scalar: Equatable {
associatedtype NumericValue
var convertedEndian: NumericValue { get }
}
+9 -46
View File
@@ -27,7 +27,7 @@ public struct ByteBuffer: @unchecked Sendable {
@usableFromInline
final class Storage {
@usableFromInline
@frozen enum Blob: ~Copyable {
enum Blob {
#if !os(WASI)
case data(Data)
case bytes(any ContiguousBytes)
@@ -36,40 +36,6 @@ public struct ByteBuffer: @unchecked Sendable {
case byteBuffer(_InternalByteBuffer)
case array([UInt8])
case pointer(UnsafeMutableRawPointer)
init(_ other: borrowing Blob) {
switch other {
#if !os(WASI)
case .data(let data):
self = .data(data)
case .bytes(let contiguousBytes):
self = .bytes(contiguousBytes)
#endif
case .byteBuffer(let internalByteBuffer):
self = .byteBuffer(internalByteBuffer)
case .array(let array):
self = .array(array)
case .pointer(let unsafeMutableRawPointer):
self = .pointer(unsafeMutableRawPointer)
}
}
var description: String {
switch self {
#if !os(WASI)
case .data(let data):
"data: \(data)"
case .bytes(let contiguousBytes):
"bytes: \(contiguousBytes)"
#endif
case .byteBuffer(let internalByteBuffer):
"byteBuffer: \(internalByteBuffer)"
case .array(let array):
"array: \(array)"
case .pointer(let unsafeMutableRawPointer):
"pointer: \(unsafeMutableRawPointer)"
}
}
}
/// This storage doesn't own the memory, therefore, we won't deallocate on deinit.
@@ -78,7 +44,7 @@ public struct ByteBuffer: @unchecked Sendable {
private let capacity: Int
/// Retained blob of data that requires the storage to retain a pointer to.
@usableFromInline
let retainedBlob: Blob
var retainedBlob: Blob
@usableFromInline
init(count: Int) {
@@ -91,9 +57,9 @@ public struct ByteBuffer: @unchecked Sendable {
}
@usableFromInline
init(blob: borrowing Blob, capacity count: Int) {
init(blob: Blob, capacity count: Int) {
capacity = count
retainedBlob = .init(blob)
retainedBlob = blob
isOwned = false
}
@@ -184,7 +150,6 @@ public struct ByteBuffer: @unchecked Sendable {
@discardableResult
@inline(__always)
@inlinable
func readWithUnsafeRawPointer<T>(
position: Int,
_ body: (UnsafeRawPointer) throws -> T) rethrows -> T
@@ -313,7 +278,7 @@ public struct ByteBuffer: @unchecked Sendable {
/// - removeBytes: Removes a number of bytes from the current size
@inline(__always)
init(
blob: borrowing Storage.Blob,
blob: Storage.Blob,
count: Int,
removing removeBytes: Int)
{
@@ -353,8 +318,7 @@ public struct ByteBuffer: @unchecked Sendable {
/// - def: Type of the object
/// - position: the index of the object in the buffer
@inline(__always)
@inlinable
public func read<T: BitwiseCopyable>(def: T.Type, position: Int) -> T {
public func read<T>(def: T.Type, position: Int) -> T {
_storage.readWithUnsafeRawPointer(position: position) {
$0.bindMemory(to: T.self, capacity: 1)
.pointee
@@ -448,7 +412,7 @@ public struct ByteBuffer: @unchecked Sendable {
/// - Parameter removeBytes: the amount of bytes to remove from the current Size
@inline(__always)
public func duplicate(removing removeBytes: Int = 0) -> ByteBuffer {
assert(removeBytes >= 0, "Can NOT remove negative bytes")
assert(removeBytes > 0, "Can NOT remove negative bytes")
assert(
removeBytes < capacity,
"Can NOT remove more bytes than the ones allocated")
@@ -500,9 +464,8 @@ public struct ByteBuffer: @unchecked Sendable {
extension ByteBuffer: CustomDebugStringConvertible {
public var debugDescription: String {
let blobDescription = _storage.retainedBlob.description
return """
buffer located at: \(blobDescription),
"""
buffer located at: \(_storage.retainedBlob),
with capacity of \(capacity),
{ writtenSize: \(_readerIndex), readerSize: \(reader),
size: \(size) }
@@ -47,8 +47,7 @@ public struct FlatBufferBuilder {
/// A check to see if finish(::) was ever called to retreive data object
private(set) var finished = false
/// A check to see if the buffer should serialize Default values
@usableFromInline
let serializeDefaults: Bool
private var serializeDefaults: Bool
/// Current alignment for the buffer
var _minAlignment: Int = 0 {
@@ -757,7 +756,6 @@ public struct FlatBufferBuilder {
/// - offset: ``Offset`` of another object to be written
/// - position: The predefined position of the object
@inline(__always)
@inlinable
mutating public func add(offset: Offset, at position: VOffset) {
if offset.isEmpty { return }
add(element: refer(to: offset.o), def: 0, at: position)
@@ -796,7 +794,6 @@ public struct FlatBufferBuilder {
/// - def: Default value for that element
/// - position: The predefined position of the element
@inline(__always)
@inlinable
mutating public func add<T: Scalar>(
element: T,
def: T,
@@ -816,7 +813,6 @@ public struct FlatBufferBuilder {
/// - element: Optional element of type scalar
/// - position: The predefined position of the element
@inline(__always)
@inlinable
mutating public func add<T: Scalar>(element: T?, at position: VOffset) {
guard let element = element else { return }
track(offset: push(element: element), at: position)
@@ -829,7 +825,6 @@ public struct FlatBufferBuilder {
/// - Parameter element: Element to insert
/// - returns: position of the Element
@inline(__always)
@inlinable
@discardableResult
mutating public func push<T: Scalar>(element: T) -> UOffset {
let size = MemoryLayout<T>.size
@@ -841,8 +836,7 @@ public struct FlatBufferBuilder {
}
@inline(__always)
@inlinable
public func read<T: BitwiseCopyable>(def: T.Type, position: Int) -> T {
public func read<T>(def: T.Type, position: Int) -> T {
_bb.read(def: def, position: position)
}
}
@@ -18,7 +18,7 @@ import Foundation
/// NativeStruct is a protocol that indicates if the struct is a native `swift` struct
/// since now we will be serializing native structs into the buffer.
public protocol NativeStruct: BitwiseCopyable {}
public protocol NativeStruct {}
public protocol FlatBufferVerifiableNativeStruct: NativeStruct, Verifiable {}
+8 -38
View File
@@ -16,30 +16,6 @@
import Foundation
@inline(__always)
private func verifySizePrefix(
byteBuffer: inout ByteBuffer,
requireExactSize: Bool,
options: VerifierOptions) throws
{
let verifier = try Verifier(buffer: &byteBuffer, options: options)
let prefixPosition = byteBuffer.reader
let prefix: UOffset = try verifier.getValue(at: prefixPosition)
let availableSize = byteBuffer.size &- UOffset(MemoryLayout<UOffset>.size)
if requireExactSize {
guard prefix == availableSize else {
throw FlatbuffersErrors.prefixedSizeNotEqualToBufferSize
}
} else if prefix > availableSize {
throw FlatbuffersErrors.outOfBounds(
position: UInt(prefixPosition)
&+ UInt(MemoryLayout<UOffset>.size)
&+ UInt(prefix),
end: byteBuffer.capacity)
}
}
/// Takes in a prefixed sized buffer, where the prefixed size would be skipped.
/// And would verify that the buffer passed is a valid `Flatbuffers` Object.
/// - Parameters:
@@ -55,10 +31,6 @@ public func getPrefixedSizeCheckedRoot<T: FlatBufferTable & Verifiable>(
fileId: String? = nil,
options: VerifierOptions = .init()) throws -> T
{
try verifySizePrefix(
byteBuffer: &byteBuffer,
requireExactSize: false,
options: options)
byteBuffer.skipPrefix()
return try getCheckedRoot(
byteBuffer: &byteBuffer,
@@ -81,11 +53,10 @@ public func getCheckedPrefixedSizeRoot<T: FlatBufferTable & Verifiable>(
fileId: String? = nil,
options: VerifierOptions = .init()) throws -> T
{
try verifySizePrefix(
byteBuffer: &byteBuffer,
requireExactSize: true,
options: options)
byteBuffer.skipPrefix()
let prefix = byteBuffer.skipPrefix()
if prefix != byteBuffer.size {
throw FlatbuffersErrors.prefixedSizeNotEqualToBufferSize
}
return try getCheckedRoot(
byteBuffer: &byteBuffer,
fileId: fileId,
@@ -124,15 +95,14 @@ public func getCheckedRoot<T: FlatBufferTable & Verifiable>(
options: VerifierOptions = .init()) throws -> T
{
var verifier = try Verifier(buffer: &byteBuffer, options: options)
let rootPosition = byteBuffer.reader
if let fileId = fileId {
try verifier.verify(id: fileId, at: rootPosition)
try verifier.verify(id: fileId)
}
try ForwardOffset<T>.verify(&verifier, at: rootPosition, of: T.self)
try ForwardOffset<T>.verify(&verifier, at: 0, of: T.self)
return T.init(
byteBuffer,
o: Int32(byteBuffer.read(def: UOffset.self, position: rootPosition))
&+ Int32(rootPosition))
o: Int32(byteBuffer.read(def: UOffset.self, position: byteBuffer.reader))
&+ Int32(byteBuffer.reader))
}
/// Returns a `NON-Checked` flatbuffers object
+1 -1
View File
@@ -82,7 +82,7 @@ public struct Table {
/// - Parameters:
/// - type: Type of Element that needs to be read from the buffer
/// - o: Offset of the Element
public func readBuffer<T: BitwiseCopyable>(of type: T.Type, at o: Int32) -> T {
public func readBuffer<T>(of type: T.Type, at o: Int32) -> T {
bb.read(def: T.self, position: Int(o &+ position))
}
+1 -8
View File
@@ -56,15 +56,8 @@ extension Verifiable {
let len: UOffset = try verifier.getValue(at: position)
let intLen = Int(len)
let start = Int(clamping: (position &+ MemoryLayout<Int32>.size).magnitude)
let byteCount = intLen.multipliedReportingOverflow(
by: MemoryLayout<T>.size)
guard !byteCount.overflow else {
throw FlatbuffersErrors.outOfBounds(
position: UInt.max,
end: verifier.capacity)
}
try verifier.isAligned(position: start, type: type.self)
try verifier.rangeInBuffer(position: start, size: byteCount.partialValue)
try verifier.rangeInBuffer(position: start, size: intLen)
return (start, intLen)
}
}
+4 -8
View File
@@ -163,7 +163,7 @@ public struct Verifier {
/// - Parameter position: Current position to be read
/// - Throws: `inBuffer` errors
/// - Returns: a value of type `T` usually a `VTable` or a table offset
internal func getValue<T: BitwiseCopyable>(at position: Int) throws -> T {
internal func getValue<T>(at position: Int) throws -> T {
try inBuffer(position: position, of: T.self)
return _buffer.read(def: T.self, position: position)
}
@@ -215,16 +215,12 @@ public struct Verifier {
}
@inline(__always)
func verify(id: String, at position: Int) throws {
func verify(id: String) throws {
let size = MemoryLayout<Int32>.size
guard
position >= 0,
position <= storage.capacity,
storage.capacity - position >= size &* 2
else {
guard storage.capacity >= (size &* 2) else {
throw FlatbuffersErrors.bufferDoesntContainID
}
let str = _buffer.readString(at: position &+ size, count: size)
let str = _buffer.readString(at: size, count: size)
if id == str {
return
}
@@ -30,7 +30,7 @@ struct _InternalByteBuffer {
@usableFromInline
final class Storage {
/// pointer to the start of the buffer object in memory
@exclusivity(unchecked) private(set) var memory: UnsafeMutableRawPointer
private(set) var memory: UnsafeMutableRawPointer
@usableFromInline
init(count: Int, alignment: Int) {
@@ -333,7 +333,6 @@ struct _InternalByteBuffer {
@discardableResult
@inline(__always)
@usableFromInline
func readWithUnsafeRawPointer<T>(
position: Int,
_ body: (UnsafeRawPointer) throws -> T) rethrows -> T
+10 -27
View File
@@ -27,7 +27,7 @@ public struct ByteBuffer {
@usableFromInline
final class Storage {
@usableFromInline
@frozen enum Blob: ~Copyable {
enum Blob {
#if !os(WASI)
case data(Data)
case bytes(any ContiguousBytes)
@@ -36,23 +36,6 @@ public struct ByteBuffer {
case byteBuffer(_InternalByteBuffer)
case array([UInt8])
case pointer(UnsafeMutableRawPointer)
init(_ other: borrowing Blob) {
switch other {
#if !os(WASI)
case .data(let data):
self = .data(data)
case .bytes(let contiguousBytes):
self = .bytes(contiguousBytes)
#endif
case .byteBuffer(let internalByteBuffer):
self = .byteBuffer(internalByteBuffer)
case .array(let array):
self = .array(array)
case .pointer(let unsafeMutableRawPointer):
self = .pointer(unsafeMutableRawPointer)
}
}
}
/// This storage doesn't own the memory, therefore, we won't deallocate on deinit.
@@ -61,7 +44,7 @@ public struct ByteBuffer {
private let capacity: Int
/// Retained blob of data that requires the storage to retain a pointer to.
@usableFromInline
let retainedBlob: Blob
var retainedBlob: Blob
@usableFromInline
init(count: Int) {
@@ -74,9 +57,9 @@ public struct ByteBuffer {
}
@usableFromInline
init(blob: borrowing Blob, capacity count: Int) {
init(blob: Blob, capacity count: Int) {
capacity = count
retainedBlob = .init(blob)
retainedBlob = blob
isOwned = false
}
@@ -293,7 +276,7 @@ public struct ByteBuffer {
/// - removeBytes: Removes a number of bytes from the current size
@inline(__always)
init(
blob: borrowing Storage.Blob,
blob: Storage.Blob,
count: Int,
removing removeBytes: Int)
{
@@ -333,7 +316,7 @@ public struct ByteBuffer {
/// - def: Type of the object
/// - position: the index of the object in the buffer
@inline(__always)
public func read<T: BitwiseCopyable>(def: T.Type, position: Int) -> T {
public func read<T>(def: T.Type, position: Int) -> T {
_storage.readWithUnsafeRawPointer(position: position) {
$0.bindMemory(to: T.self, capacity: 1)
.pointee
@@ -377,10 +360,10 @@ public struct ByteBuffer {
@inline(__always)
func readSizedScalar<
T: BinaryInteger,
T1: BinaryInteger & BitwiseCopyable,
T2: BinaryInteger & BitwiseCopyable,
T3: BinaryInteger & BitwiseCopyable,
T4: BinaryInteger & BitwiseCopyable
T1: BinaryInteger,
T2: BinaryInteger,
T3: BinaryInteger,
T4: BinaryInteger
>(
def: T.Type,
t1: T1.Type,
@@ -31,7 +31,7 @@ struct _InternalByteBuffer {
@usableFromInline
final class Storage {
/// pointer to the start of the buffer object in memory
@exclusivity(unchecked) private(set) var memory: UnsafeMutableRawPointer
var memory: UnsafeMutableRawPointer
@usableFromInline
init(count: Int, alignment: Int) {
+1 -1
View File
@@ -12,7 +12,7 @@ namespace Example {
struct MonsterBinarySchema {
static const uint8_t *data() {
// Buffer containing the binary schema.
alignas(uint64_t) static const uint8_t bfbsData[14520] = {
static const uint8_t bfbsData[14520] = {
0x20,0x00,0x00,0x00,0x42,0x46,0x42,0x53,0x00,0x00,0x00,0x00,0x14,0x00,0x20,0x00,0x04,0x00,0x08,0x00,
0x0C,0x00,0x10,0x00,0x14,0x00,0x18,0x00,0x00,0x00,0x1C,0x00,0x14,0x00,0x00,0x00,0x58,0x00,0x00,0x00,
0x34,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x70,0x0D,0x00,0x00,0x08,0x00,0x00,0x00,
@@ -373,58 +373,6 @@ final class FlatbuffersVerifierTests {
}
}
@Test
func testSizePrefixedVerifierUsesPostPrefixRoot() throws {
// The size prefix points to a valid empty decoy table at byte 44. The real
// root at byte 4 points to a truncated UInt64 vector that must be rejected.
let bytes: [UInt8] = [
44, 0, 0, 0,
16, 0, 0, 0,
6, 0, 8, 0, 4, 0,
0, 0, 0, 0, 0, 0,
12, 0, 0, 0,
8, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 65, 66,
0, 0,
4, 0, 4, 0,
4, 0, 0, 0,
]
var strictBuffer = ByteBuffer(bytes: bytes)
#expect(throws: FlatbuffersErrors.self) {
try getCheckedPrefixedSizeRoot(
byteBuffer: &strictBuffer) as Swift_Tests_Vectors
}
var prefixedBuffer = ByteBuffer(bytes: bytes)
#expect(throws: FlatbuffersErrors.self) {
try getPrefixedSizeCheckedRoot(
byteBuffer: &prefixedBuffer) as Swift_Tests_Vectors
}
var builder = FlatBufferBuilder()
let movie = Movie.createMovie(&builder)
Movie.finish(&builder, end: movie, prefix: true)
var validBuffer = builder.sizedBuffer
let _: Movie = try getCheckedPrefixedSizeRoot(
byteBuffer: &validBuffer,
fileId: Movie.id)
var shortBuffer = ByteBuffer(bytes: [0, 0, 0, 0])
#expect(throws: FlatbuffersErrors.bufferDoesntContainID) {
try getCheckedRoot(
byteBuffer: &shortBuffer,
fileId: Movie.id) as Movie
}
var oversizedPrefix = ByteBuffer(bytes: [5, 0, 0, 0, 0, 0, 0, 0])
#expect(throws: FlatbuffersErrors.outOfBounds(position: 9, end: 8)) {
try getPrefixedSizeCheckedRoot(
byteBuffer: &oversizedPrefix) as Movie
}
#expect(oversizedPrefix.reader == 0)
}
@Test
func testFullVerifier() throws {
_ =
@@ -463,27 +411,6 @@ final class FlatbuffersVerifierTests {
}
}
@Test(.bug("https://github.com/google/flatbuffers/issues/9082"))
func testRejectsTruncatedScalarVector() {
// swiftformat:disable all
var byteBuffer = ByteBuffer(bytes: [
16, 0, 0, 0,
6, 0, 8, 0,
4, 0, 0, 0,
0, 0, 0, 0,
12, 0, 0, 0,
8, 0, 0, 0,
0, 0, 0, 0,
2, 0, 0, 0,
65, 66,
])
// swiftformat:enable all
#expect(throws: FlatbuffersErrors.self) {
try getCheckedRoot(byteBuffer: &byteBuffer) as Swift_Tests_Vectors
}
}
@Test
func testValidUnionBuffer() {
let string = "Awesome \\\\t\t\nstring!"