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/4] 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/4] 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/4] 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/4] 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