From ff5dc21de0bc00a21c2bcf9853748cba50a13b63 Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Tue, 28 Apr 2026 10:45:38 +0200 Subject: [PATCH 1/8] start/stop measurements --- .../FlatbuffersBenchmarks.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/benchmarks/swift/Benchmarks/FlatbuffersBenchmarks/FlatbuffersBenchmarks.swift b/benchmarks/swift/Benchmarks/FlatbuffersBenchmarks/FlatbuffersBenchmarks.swift index 4607f58094..aa085686fb 100644 --- a/benchmarks/swift/Benchmarks/FlatbuffersBenchmarks/FlatbuffersBenchmarks.swift +++ b/benchmarks/swift/Benchmarks/FlatbuffersBenchmarks/FlatbuffersBenchmarks.swift @@ -97,6 +97,7 @@ let benchmarks = { for _ in benchmark.scaledIterations { blackHole(ByteBuffer(assumingMemoryBound: memory, capacity: Int(oneGB))) } + benchmark.stopMeasurement() } Benchmark("Clearing 1GB", configuration: singleConfiguration) { benchmark in @@ -105,6 +106,7 @@ let benchmarks = { for _ in benchmark.scaledIterations { blackHole(fb.clear()) } + benchmark.stopMeasurement() } Benchmark("Strings 10") { benchmark in @@ -113,6 +115,7 @@ let benchmarks = { for _ in benchmark.scaledIterations { blackHole(fb.create(string: str10)) } + benchmark.stopMeasurement() } Benchmark("Strings 100") { benchmark in @@ -121,6 +124,7 @@ let benchmarks = { for _ in benchmark.scaledIterations { blackHole(fb.create(string: str100)) } + benchmark.stopMeasurement() } Benchmark("Vector 1 Bytes") { benchmark in @@ -129,6 +133,7 @@ let benchmarks = { for _ in benchmark.scaledIterations { blackHole(fb.createVector(bytes: bytes)) } + benchmark.stopMeasurement() } Benchmark("Vector 1 Ints") { benchmark in @@ -137,6 +142,7 @@ let benchmarks = { for _ in benchmark.scaledIterations { blackHole(fb.createVector(ints)) } + benchmark.stopMeasurement() } Benchmark("Vector 100 Ints") { benchmark in @@ -145,6 +151,7 @@ let benchmarks = { for i in benchmark.scaledIterations { blackHole(fb.createVector(ints)) } + benchmark.stopMeasurement() } Benchmark("Vector 100 Bytes") { benchmark in @@ -153,6 +160,7 @@ let benchmarks = { for i in benchmark.scaledIterations { blackHole(fb.createVector(bytes)) } + benchmark.stopMeasurement() } Benchmark("Vector 100 ContiguousBytes") { benchmark in @@ -161,6 +169,7 @@ let benchmarks = { for i in benchmark.scaledIterations { blackHole(fb.createVector(bytes: bytes)) } + benchmark.stopMeasurement() } Benchmark( @@ -178,6 +187,7 @@ let benchmarks = { fb.add(offset: off, at: 8) blackHole(fb.endTable(at: s)) } + benchmark.stopMeasurement() } Benchmark( @@ -190,6 +200,7 @@ let benchmarks = { let s = fb.startTable(with: 4) blackHole(fb.endTable(at: s)) } + benchmark.stopMeasurement() } Benchmark("Struct") { benchmark in @@ -198,6 +209,7 @@ let benchmarks = { for _ in benchmark.scaledIterations { blackHole(fb.create(struct: array.first!)) } + benchmark.stopMeasurement() } Benchmark("Structs") { benchmark in @@ -219,6 +231,7 @@ 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 @@ -239,12 +252,15 @@ 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() } } From 07faf71e4bcce134c67b1ba5f3b93630e7da9668 Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:55:53 +0200 Subject: [PATCH 2/8] tweaks --- swift/Sources/Common/Scalar.swift | 2 +- swift/Sources/FlatBuffers/ByteBuffer.swift | 51 +++++++++++++++---- .../FlatBuffers/FlatBufferBuilder.swift | 10 +++- .../FlatBuffers/FlatBufferObject.swift | 2 +- swift/Sources/FlatBuffers/Table.swift | 2 +- swift/Sources/FlatBuffers/Verifier.swift | 2 +- .../FlatBuffers/_InternalByteBuffer.swift | 3 +- 7 files changed, 56 insertions(+), 16 deletions(-) diff --git a/swift/Sources/Common/Scalar.swift b/swift/Sources/Common/Scalar.swift index 518580a605..5c6a6c8327 100644 --- a/swift/Sources/Common/Scalar.swift +++ b/swift/Sources/Common/Scalar.swift @@ -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 { +public protocol Scalar: Equatable, BitwiseCopyable { associatedtype NumericValue var convertedEndian: NumericValue { get } } diff --git a/swift/Sources/FlatBuffers/ByteBuffer.swift b/swift/Sources/FlatBuffers/ByteBuffer.swift index ec27fd1d73..6c62b9ac3e 100644 --- a/swift/Sources/FlatBuffers/ByteBuffer.swift +++ b/swift/Sources/FlatBuffers/ByteBuffer.swift @@ -27,7 +27,7 @@ public struct ByteBuffer { @usableFromInline final class Storage { @usableFromInline - enum Blob { + @frozen enum Blob: ~Copyable { #if !os(WASI) case data(Data) case bytes(ContiguousBytes) @@ -36,6 +36,36 @@ public struct ByteBuffer { case byteBuffer(_InternalByteBuffer) case array([UInt8]) case pointer(UnsafeMutableRawPointer) + + init(_ other: borrowing Blob) { + switch other { + case .data(let data): + self = .data(data) + case .bytes(let contiguousBytes): + self = .bytes(contiguousBytes) + 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 { + case .data(let data): + "data: \(data)" + case .bytes(let contiguousBytes): + "bytes: \(contiguousBytes)" + 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. @@ -44,7 +74,7 @@ public struct ByteBuffer { private let capacity: Int /// Retained blob of data that requires the storage to retain a pointer to. @usableFromInline - var retainedBlob: Blob + let retainedBlob: Blob @usableFromInline init(count: Int) { @@ -57,9 +87,9 @@ public struct ByteBuffer { } @usableFromInline - init(blob: Blob, capacity count: Int) { + init(blob: borrowing Blob, capacity count: Int) { capacity = count - retainedBlob = blob + retainedBlob = .init(blob) isOwned = false } @@ -150,6 +180,7 @@ public struct ByteBuffer { @discardableResult @inline(__always) + @inlinable func readWithUnsafeRawPointer( position: Int, _ body: (UnsafeRawPointer) throws -> T) rethrows -> T @@ -278,7 +309,7 @@ public struct ByteBuffer { /// - removeBytes: Removes a number of bytes from the current size @inline(__always) init( - blob: Storage.Blob, + blob: borrowing Storage.Blob, count: Int, removing removeBytes: Int) { @@ -318,7 +349,8 @@ public struct ByteBuffer { /// - def: Type of the object /// - position: the index of the object in the buffer @inline(__always) - public func read(def: T.Type, position: Int) -> T { + @inlinable + public func read(def: T.Type, position: Int) -> T { _storage.readWithUnsafeRawPointer(position: position) { $0.bindMemory(to: T.self, capacity: 1) .pointee @@ -412,7 +444,7 @@ public struct ByteBuffer { /// - 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") @@ -464,8 +496,9 @@ public struct ByteBuffer { extension ByteBuffer: CustomDebugStringConvertible { public var debugDescription: String { - """ - buffer located at: \(_storage.retainedBlob), + let blobDescription = _storage.retainedBlob.description + return """ + buffer located at: \(blobDescription), with capacity of \(capacity), { writtenSize: \(_readerIndex), readerSize: \(reader), size: \(size) } diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift index 9e778d225c..388714ffc5 100644 --- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -47,7 +47,8 @@ public struct FlatBufferBuilder { /// A check to see if finish(::) was ever called to retreive data object private var finished = false /// A check to see if the buffer should serialize Default values - private var serializeDefaults: Bool + @usableFromInline + var serializeDefaults: Bool /// Current alignment for the buffer var _minAlignment: Int = 0 { @@ -756,6 +757,7 @@ 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) @@ -794,6 +796,7 @@ public struct FlatBufferBuilder { /// - def: Default value for that element /// - position: The predefined position of the element @inline(__always) + @inlinable mutating public func add( element: T, def: T, @@ -813,6 +816,7 @@ public struct FlatBufferBuilder { /// - element: Optional element of type scalar /// - position: The predefined position of the element @inline(__always) + @inlinable mutating public func add(element: T?, at position: VOffset) { guard let element = element else { return } track(offset: push(element: element), at: position) @@ -825,6 +829,7 @@ public struct FlatBufferBuilder { /// - Parameter element: Element to insert /// - returns: position of the Element @inline(__always) + @inlinable @discardableResult mutating public func push(element: T) -> UOffset { let size = MemoryLayout.size @@ -836,7 +841,8 @@ public struct FlatBufferBuilder { } @inline(__always) - public func read(def: T.Type, position: Int) -> T { + @inlinable + public func read(def: T.Type, position: Int) -> T { _bb.read(def: def, position: position) } } diff --git a/swift/Sources/FlatBuffers/FlatBufferObject.swift b/swift/Sources/FlatBuffers/FlatBufferObject.swift index 0b9f01b070..17999318f6 100644 --- a/swift/Sources/FlatBuffers/FlatBufferObject.swift +++ b/swift/Sources/FlatBuffers/FlatBufferObject.swift @@ -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 {} +public protocol NativeStruct: BitwiseCopyable {} /// FlatbuffersInitializable is a protocol that allows any object to be /// Initialized from a ByteBuffer diff --git a/swift/Sources/FlatBuffers/Table.swift b/swift/Sources/FlatBuffers/Table.swift index f29a51e5b3..de87626549 100644 --- a/swift/Sources/FlatBuffers/Table.swift +++ b/swift/Sources/FlatBuffers/Table.swift @@ -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(of type: T.Type, at o: Int32) -> T { + public func readBuffer(of type: T.Type, at o: Int32) -> T { bb.read(def: T.self, position: Int(o &+ position)) } diff --git a/swift/Sources/FlatBuffers/Verifier.swift b/swift/Sources/FlatBuffers/Verifier.swift index b0ef3968d0..525e149429 100644 --- a/swift/Sources/FlatBuffers/Verifier.swift +++ b/swift/Sources/FlatBuffers/Verifier.swift @@ -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(at position: Int) throws -> T { + internal func getValue(at position: Int) throws -> T { try inBuffer(position: position, of: T.self) return _buffer.read(def: T.self, position: position) } diff --git a/swift/Sources/FlatBuffers/_InternalByteBuffer.swift b/swift/Sources/FlatBuffers/_InternalByteBuffer.swift index 8dced85b41..4f4e02269c 100644 --- a/swift/Sources/FlatBuffers/_InternalByteBuffer.swift +++ b/swift/Sources/FlatBuffers/_InternalByteBuffer.swift @@ -30,7 +30,7 @@ struct _InternalByteBuffer { @usableFromInline final class Storage { /// pointer to the start of the buffer object in memory - private(set) var memory: UnsafeMutableRawPointer + @exclusivity(unchecked) @usableFromInline private(set) var memory: UnsafeMutableRawPointer @usableFromInline init(count: Int, alignment: Int) { @@ -333,6 +333,7 @@ struct _InternalByteBuffer { @discardableResult @inline(__always) + @inlinable func readWithUnsafeRawPointer( position: Int, _ body: (UnsafeRawPointer) throws -> T) rethrows -> T From 9de434331044c538e7c952531cfd51ccb0060750 Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Tue, 28 Apr 2026 12:33:07 +0200 Subject: [PATCH 3/8] add 6.0 annotations --- swift/Sources/Common/Scalar.swift | 7 +++ swift/Sources/FlatBuffers/ByteBuffer.swift | 62 +++++++++++++++++++ .../FlatBuffers/FlatBufferBuilder.swift | 8 +++ .../FlatBuffers/FlatBufferObject.swift | 4 ++ swift/Sources/FlatBuffers/Table.swift | 6 ++ swift/Sources/FlatBuffers/Verifier.swift | 7 +++ 6 files changed, 94 insertions(+) diff --git a/swift/Sources/Common/Scalar.swift b/swift/Sources/Common/Scalar.swift index 5c6a6c8327..ae573d235b 100644 --- a/swift/Sources/Common/Scalar.swift +++ b/swift/Sources/Common/Scalar.swift @@ -28,10 +28,17 @@ 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. +#if compiler(>=6.0) public protocol Scalar: Equatable, BitwiseCopyable { associatedtype NumericValue var convertedEndian: NumericValue { get } } +#else +public protocol Scalar: Equatable { + associatedtype NumericValue + var convertedEndian: NumericValue { get } +} +#endif extension Scalar where Self: FixedWidthInteger { /// Converts the value from BigEndian to LittleEndian diff --git a/swift/Sources/FlatBuffers/ByteBuffer.swift b/swift/Sources/FlatBuffers/ByteBuffer.swift index 6c62b9ac3e..ed5213a6ef 100644 --- a/swift/Sources/FlatBuffers/ByteBuffer.swift +++ b/swift/Sources/FlatBuffers/ByteBuffer.swift @@ -26,6 +26,7 @@ public struct ByteBuffer { /// deallocating the memory that was held by (memory: UnsafeMutableRawPointer) @usableFromInline final class Storage { + #if compiler(>=6.0) @usableFromInline @frozen enum Blob: ~Copyable { #if !os(WASI) @@ -67,6 +68,34 @@ public struct ByteBuffer { } } } + #else + @usableFromInline + @frozen enum Blob { + #if !os(WASI) + case data(Data) + case bytes(ContiguousBytes) + #endif + + case byteBuffer(_InternalByteBuffer) + case array([UInt8]) + case pointer(UnsafeMutableRawPointer) + + var description: String { + switch self { + case .data(let data): + return "data: \(data)" + case .bytes(let contiguousBytes): + return "bytes: \(contiguousBytes)" + case .byteBuffer(let internalByteBuffer): + return "byteBuffer: \(internalByteBuffer)" + case .array(let array): + return "array: \(array)" + case .pointer(let unsafeMutableRawPointer): + return "pointer: \(unsafeMutableRawPointer)" + } + } + } + #endif /// This storage doesn't own the memory, therefore, we won't deallocate on deinit. private let isOwned: Bool @@ -86,12 +115,21 @@ public struct ByteBuffer { isOwned = true } + #if compiler(>=6.0) @usableFromInline init(blob: borrowing Blob, capacity count: Int) { capacity = count retainedBlob = .init(blob) isOwned = false } + #else + @usableFromInline + init(blob: Blob, capacity count: Int) { + capacity = count + retainedBlob = blob + isOwned = false + } + #endif deinit { guard isOwned else { return } @@ -307,6 +345,7 @@ public struct ByteBuffer { /// - memory: Current memory of the buffer /// - count: count of bytes /// - removeBytes: Removes a number of bytes from the current size + #if compiler(>=6.0) @inline(__always) init( blob: borrowing Storage.Blob, @@ -317,6 +356,18 @@ public struct ByteBuffer { _readerIndex = removeBytes capacity = count } + #else + @inline(__always) + init( + blob: Storage.Blob, + count: Int, + removing removeBytes: Int) + { + _storage = Storage(blob: blob, capacity: count) + _readerIndex = removeBytes + capacity = count + } + #endif /// Write stores an object into the buffer directly or indirectly. /// @@ -348,6 +399,7 @@ public struct ByteBuffer { /// - Parameters: /// - def: Type of the object /// - position: the index of the object in the buffer + #if compiler(>=6.0) @inline(__always) @inlinable public func read(def: T.Type, position: Int) -> T { @@ -356,6 +408,16 @@ public struct ByteBuffer { .pointee } } + #else + @inline(__always) + @inlinable + public func read(def: T.Type, position: Int) -> T { + _storage.readWithUnsafeRawPointer(position: position) { + $0.bindMemory(to: T.self, capacity: 1) + .pointee + } + } + #endif /// Reads a slice from the memory assuming a type of T /// - Parameters: diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift index 388714ffc5..d2c4c99fc2 100644 --- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -840,11 +840,19 @@ public struct FlatBufferBuilder { return _bb.size } + #if compiler(>=6.0) @inline(__always) @inlinable public func read(def: T.Type, position: Int) -> T { _bb.read(def: def, position: position) } + #else + @inline(__always) + @inlinable + public func read(def: T.Type, position: Int) -> T { + _bb.read(def: def, position: position) + } + #endif } extension FlatBufferBuilder: CustomDebugStringConvertible { diff --git a/swift/Sources/FlatBuffers/FlatBufferObject.swift b/swift/Sources/FlatBuffers/FlatBufferObject.swift index 17999318f6..612d89d645 100644 --- a/swift/Sources/FlatBuffers/FlatBufferObject.swift +++ b/swift/Sources/FlatBuffers/FlatBufferObject.swift @@ -18,7 +18,11 @@ 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. +#if compiler(>=6.0) public protocol NativeStruct: BitwiseCopyable {} +#else +public protocol NativeStruct {} +#endif /// FlatbuffersInitializable is a protocol that allows any object to be /// Initialized from a ByteBuffer diff --git a/swift/Sources/FlatBuffers/Table.swift b/swift/Sources/FlatBuffers/Table.swift index de87626549..c0f0a2e68a 100644 --- a/swift/Sources/FlatBuffers/Table.swift +++ b/swift/Sources/FlatBuffers/Table.swift @@ -82,9 +82,15 @@ public struct Table { /// - Parameters: /// - type: Type of Element that needs to be read from the buffer /// - o: Offset of the Element + #if compiler(>=6.0) public func readBuffer(of type: T.Type, at o: Int32) -> T { bb.read(def: T.self, position: Int(o &+ position)) } + #else + public func readBuffer(of type: T.Type, at o: Int32) -> T { + bb.read(def: T.self, position: Int(o &+ position)) + } + #endif /// Returns that current `Union` object at a specific offset /// by adding offset to the current position of table diff --git a/swift/Sources/FlatBuffers/Verifier.swift b/swift/Sources/FlatBuffers/Verifier.swift index 525e149429..a7dbffb5ae 100644 --- a/swift/Sources/FlatBuffers/Verifier.swift +++ b/swift/Sources/FlatBuffers/Verifier.swift @@ -163,10 +163,17 @@ 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 + #if compiler(>=6.0) internal func getValue(at position: Int) throws -> T { try inBuffer(position: position, of: T.self) return _buffer.read(def: T.self, position: position) } + #else + internal func getValue(at position: Int) throws -> T { + try inBuffer(position: position, of: T.self) + return _buffer.read(def: T.self, position: position) + } + #endif /// derefrences an offset within a vtable to get the position of the field /// in the bytebuffer From 54fe999f21833a930dd777a595fb8d1811443d7e Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Tue, 28 Apr 2026 14:18:50 +0200 Subject: [PATCH 4/8] remove some inlines --- swift/Sources/FlatBuffers/_InternalByteBuffer.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/Sources/FlatBuffers/_InternalByteBuffer.swift b/swift/Sources/FlatBuffers/_InternalByteBuffer.swift index 4f4e02269c..a92fa8847b 100644 --- a/swift/Sources/FlatBuffers/_InternalByteBuffer.swift +++ b/swift/Sources/FlatBuffers/_InternalByteBuffer.swift @@ -30,7 +30,7 @@ struct _InternalByteBuffer { @usableFromInline final class Storage { /// pointer to the start of the buffer object in memory - @exclusivity(unchecked) @usableFromInline private(set) var memory: UnsafeMutableRawPointer + @exclusivity(unchecked) private(set) var memory: UnsafeMutableRawPointer @usableFromInline init(count: Int, alignment: Int) { @@ -333,7 +333,7 @@ struct _InternalByteBuffer { @discardableResult @inline(__always) - @inlinable + @usableFromInline func readWithUnsafeRawPointer( position: Int, _ body: (UnsafeRawPointer) throws -> T) rethrows -> T From 6f7dc6696d9c0cdb7f133eb3b910e0088d8ca270 Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Mon, 15 Jun 2026 15:47:05 +0200 Subject: [PATCH 5/8] address feedback --- swift/Sources/Common/Scalar.swift | 7 --- swift/Sources/FlatBuffers/ByteBuffer.swift | 62 ------------------- .../FlatBuffers/FlatBufferBuilder.swift | 8 --- .../FlatBuffers/FlatBufferObject.swift | 4 -- swift/Sources/FlatBuffers/Table.swift | 6 -- swift/Sources/FlatBuffers/Verifier.swift | 7 --- 6 files changed, 94 deletions(-) diff --git a/swift/Sources/Common/Scalar.swift b/swift/Sources/Common/Scalar.swift index ae573d235b..5c6a6c8327 100644 --- a/swift/Sources/Common/Scalar.swift +++ b/swift/Sources/Common/Scalar.swift @@ -28,17 +28,10 @@ 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. -#if compiler(>=6.0) public protocol Scalar: Equatable, BitwiseCopyable { associatedtype NumericValue var convertedEndian: NumericValue { get } } -#else -public protocol Scalar: Equatable { - associatedtype NumericValue - var convertedEndian: NumericValue { get } -} -#endif extension Scalar where Self: FixedWidthInteger { /// Converts the value from BigEndian to LittleEndian diff --git a/swift/Sources/FlatBuffers/ByteBuffer.swift b/swift/Sources/FlatBuffers/ByteBuffer.swift index 740ac81095..362167b6fc 100644 --- a/swift/Sources/FlatBuffers/ByteBuffer.swift +++ b/swift/Sources/FlatBuffers/ByteBuffer.swift @@ -26,7 +26,6 @@ public struct ByteBuffer: @unchecked Sendable { /// deallocating the memory that was held by (memory: UnsafeMutableRawPointer) @usableFromInline final class Storage { - #if compiler(>=6.0) @usableFromInline @frozen enum Blob: ~Copyable { #if !os(WASI) @@ -68,34 +67,6 @@ public struct ByteBuffer: @unchecked Sendable { } } } - #else - @usableFromInline - @frozen enum Blob { - #if !os(WASI) - case data(Data) - case bytes(ContiguousBytes) - #endif - - case byteBuffer(_InternalByteBuffer) - case array([UInt8]) - case pointer(UnsafeMutableRawPointer) - - var description: String { - switch self { - case .data(let data): - return "data: \(data)" - case .bytes(let contiguousBytes): - return "bytes: \(contiguousBytes)" - case .byteBuffer(let internalByteBuffer): - return "byteBuffer: \(internalByteBuffer)" - case .array(let array): - return "array: \(array)" - case .pointer(let unsafeMutableRawPointer): - return "pointer: \(unsafeMutableRawPointer)" - } - } - } - #endif /// This storage doesn't own the memory, therefore, we won't deallocate on deinit. private let isOwned: Bool @@ -115,21 +86,12 @@ public struct ByteBuffer: @unchecked Sendable { isOwned = true } - #if compiler(>=6.0) @usableFromInline init(blob: borrowing Blob, capacity count: Int) { capacity = count retainedBlob = .init(blob) isOwned = false } - #else - @usableFromInline - init(blob: Blob, capacity count: Int) { - capacity = count - retainedBlob = blob - isOwned = false - } - #endif deinit { guard isOwned else { return } @@ -345,7 +307,6 @@ public struct ByteBuffer: @unchecked Sendable { /// - memory: Current memory of the buffer /// - count: count of bytes /// - removeBytes: Removes a number of bytes from the current size - #if compiler(>=6.0) @inline(__always) init( blob: borrowing Storage.Blob, @@ -356,18 +317,6 @@ public struct ByteBuffer: @unchecked Sendable { _readerIndex = removeBytes capacity = count } - #else - @inline(__always) - init( - blob: Storage.Blob, - count: Int, - removing removeBytes: Int) - { - _storage = Storage(blob: blob, capacity: count) - _readerIndex = removeBytes - capacity = count - } - #endif /// Write stores an object into the buffer directly or indirectly. /// @@ -399,7 +348,6 @@ public struct ByteBuffer: @unchecked Sendable { /// - Parameters: /// - def: Type of the object /// - position: the index of the object in the buffer - #if compiler(>=6.0) @inline(__always) @inlinable public func read(def: T.Type, position: Int) -> T { @@ -408,16 +356,6 @@ public struct ByteBuffer: @unchecked Sendable { .pointee } } - #else - @inline(__always) - @inlinable - public func read(def: T.Type, position: Int) -> T { - _storage.readWithUnsafeRawPointer(position: position) { - $0.bindMemory(to: T.self, capacity: 1) - .pointee - } - } - #endif /// Reads a slice from the memory assuming a type of T /// - Parameters: diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift index 9824ec96f5..513605241b 100644 --- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -840,19 +840,11 @@ public struct FlatBufferBuilder { return _bb.size } - #if compiler(>=6.0) @inline(__always) @inlinable public func read(def: T.Type, position: Int) -> T { _bb.read(def: def, position: position) } - #else - @inline(__always) - @inlinable - public func read(def: T.Type, position: Int) -> T { - _bb.read(def: def, position: position) - } - #endif } extension FlatBufferBuilder: CustomDebugStringConvertible { diff --git a/swift/Sources/FlatBuffers/FlatBufferObject.swift b/swift/Sources/FlatBuffers/FlatBufferObject.swift index e0299fd384..dce45128e5 100644 --- a/swift/Sources/FlatBuffers/FlatBufferObject.swift +++ b/swift/Sources/FlatBuffers/FlatBufferObject.swift @@ -18,11 +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. -#if compiler(>=6.0) public protocol NativeStruct: BitwiseCopyable {} -#else -public protocol NativeStruct {} -#endif public protocol FlatBufferVerifiableNativeStruct: NativeStruct, Verifiable {} diff --git a/swift/Sources/FlatBuffers/Table.swift b/swift/Sources/FlatBuffers/Table.swift index c0f0a2e68a..de87626549 100644 --- a/swift/Sources/FlatBuffers/Table.swift +++ b/swift/Sources/FlatBuffers/Table.swift @@ -82,15 +82,9 @@ public struct Table { /// - Parameters: /// - type: Type of Element that needs to be read from the buffer /// - o: Offset of the Element - #if compiler(>=6.0) public func readBuffer(of type: T.Type, at o: Int32) -> T { bb.read(def: T.self, position: Int(o &+ position)) } - #else - public func readBuffer(of type: T.Type, at o: Int32) -> T { - bb.read(def: T.self, position: Int(o &+ position)) - } - #endif /// Returns that current `Union` object at a specific offset /// by adding offset to the current position of table diff --git a/swift/Sources/FlatBuffers/Verifier.swift b/swift/Sources/FlatBuffers/Verifier.swift index a7dbffb5ae..525e149429 100644 --- a/swift/Sources/FlatBuffers/Verifier.swift +++ b/swift/Sources/FlatBuffers/Verifier.swift @@ -163,17 +163,10 @@ 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 - #if compiler(>=6.0) internal func getValue(at position: Int) throws -> T { try inBuffer(position: position, of: T.self) return _buffer.read(def: T.self, position: position) } - #else - internal func getValue(at position: Int) throws -> T { - try inBuffer(position: position, of: T.self) - return _buffer.read(def: T.self, position: position) - } - #endif /// derefrences an offset within a vtable to get the position of the field /// in the bytebuffer From 040da7ba7bf8cb06c87404703e363823ff037685 Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:21:53 +0200 Subject: [PATCH 6/8] address wasi + let --- swift/Sources/FlatBuffers/ByteBuffer.swift | 4 ++++ swift/Sources/FlatBuffers/FlatBufferBuilder.swift | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/swift/Sources/FlatBuffers/ByteBuffer.swift b/swift/Sources/FlatBuffers/ByteBuffer.swift index 362167b6fc..f22ecff571 100644 --- a/swift/Sources/FlatBuffers/ByteBuffer.swift +++ b/swift/Sources/FlatBuffers/ByteBuffer.swift @@ -39,10 +39,12 @@ public struct ByteBuffer: @unchecked Sendable { 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): @@ -54,10 +56,12 @@ public struct ByteBuffer: @unchecked Sendable { 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): diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift index 513605241b..d9cc0a96d9 100644 --- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -48,7 +48,7 @@ public struct FlatBufferBuilder { private(set) var finished = false /// A check to see if the buffer should serialize Default values @usableFromInline - var serializeDefaults: Bool + let serializeDefaults: Bool /// Current alignment for the buffer var _minAlignment: Int = 0 { From 0d577eb660f1aaa803d6943b4aed15b216878c1c Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Mon, 15 Jun 2026 18:09:10 +0200 Subject: [PATCH 7/8] adopt flex buffers --- benchmarks/swift/Package.swift | 10 +++++ swift/Sources/FlexBuffers/ByteBuffer.swift | 37 ++++++++++++++----- .../FlexBuffers/_InternalByteBuffer.swift | 2 +- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/benchmarks/swift/Package.swift b/benchmarks/swift/Package.swift index 118732987c..41b536cc25 100644 --- a/benchmarks/swift/Package.swift +++ b/benchmarks/swift/Package.swift @@ -39,4 +39,14 @@ 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"), + ]), ]) diff --git a/swift/Sources/FlexBuffers/ByteBuffer.swift b/swift/Sources/FlexBuffers/ByteBuffer.swift index f4dd2adb77..6789618ced 100644 --- a/swift/Sources/FlexBuffers/ByteBuffer.swift +++ b/swift/Sources/FlexBuffers/ByteBuffer.swift @@ -27,7 +27,7 @@ public struct ByteBuffer { @usableFromInline final class Storage { @usableFromInline - enum Blob { + @frozen enum Blob: ~Copyable { #if !os(WASI) case data(Data) case bytes(any ContiguousBytes) @@ -36,6 +36,23 @@ 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. @@ -44,7 +61,7 @@ public struct ByteBuffer { private let capacity: Int /// Retained blob of data that requires the storage to retain a pointer to. @usableFromInline - var retainedBlob: Blob + let retainedBlob: Blob @usableFromInline init(count: Int) { @@ -57,9 +74,9 @@ public struct ByteBuffer { } @usableFromInline - init(blob: Blob, capacity count: Int) { + init(blob: borrowing Blob, capacity count: Int) { capacity = count - retainedBlob = blob + retainedBlob = .init(blob) isOwned = false } @@ -276,7 +293,7 @@ public struct ByteBuffer { /// - removeBytes: Removes a number of bytes from the current size @inline(__always) init( - blob: Storage.Blob, + blob: borrowing Storage.Blob, count: Int, removing removeBytes: Int) { @@ -316,7 +333,7 @@ public struct ByteBuffer { /// - def: Type of the object /// - position: the index of the object in the buffer @inline(__always) - public func read(def: T.Type, position: Int) -> T { + public func read(def: T.Type, position: Int) -> T { _storage.readWithUnsafeRawPointer(position: position) { $0.bindMemory(to: T.self, capacity: 1) .pointee @@ -360,10 +377,10 @@ public struct ByteBuffer { @inline(__always) func readSizedScalar< T: BinaryInteger, - T1: BinaryInteger, - T2: BinaryInteger, - T3: BinaryInteger, - T4: BinaryInteger + T1: BinaryInteger & BitwiseCopyable, + T2: BinaryInteger & BitwiseCopyable, + T3: BinaryInteger & BitwiseCopyable, + T4: BinaryInteger & BitwiseCopyable >( def: T.Type, t1: T1.Type, diff --git a/swift/Sources/FlexBuffers/_InternalByteBuffer.swift b/swift/Sources/FlexBuffers/_InternalByteBuffer.swift index 5df4ff31a0..e99f95aa15 100644 --- a/swift/Sources/FlexBuffers/_InternalByteBuffer.swift +++ b/swift/Sources/FlexBuffers/_InternalByteBuffer.swift @@ -31,7 +31,7 @@ struct _InternalByteBuffer { @usableFromInline final class Storage { /// pointer to the start of the buffer object in memory - var memory: UnsafeMutableRawPointer + @exclusivity(unchecked) private(set) var memory: UnsafeMutableRawPointer @usableFromInline init(count: Int, alignment: Int) { From e873ad1401a5ec4f333c403d6ed2548de47e2b58 Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Mon, 15 Jun 2026 18:27:18 +0200 Subject: [PATCH 8/8] flex buffers benchmarks --- .../FlexBuffersBenchmarks.swift | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 benchmarks/swift/Benchmarks/FlexBuffersBenchmarks/FlexBuffersBenchmarks.swift diff --git a/benchmarks/swift/Benchmarks/FlexBuffersBenchmarks/FlexBuffersBenchmarks.swift b/benchmarks/swift/Benchmarks/FlexBuffersBenchmarks/FlexBuffersBenchmarks.swift new file mode 100644 index 0000000000..22d0d54136 --- /dev/null +++ b/benchmarks/swift/Benchmarks/FlexBuffersBenchmarks/FlexBuffersBenchmarks.swift @@ -0,0 +1,174 @@ +/* + * 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` 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..