From 0e34b2cdf790bd3518060d07ca986c2e6dd9da2e Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Tue, 12 Dec 2023 11:11:29 +0200 Subject: [PATCH 01/11] fix(patch): [sc-5289] use unaligned memory by default --- swift/Sources/FlatBuffers/ByteBuffer.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/swift/Sources/FlatBuffers/ByteBuffer.swift b/swift/Sources/FlatBuffers/ByteBuffer.swift index d33574d8bf..06380ccd3f 100644 --- a/swift/Sources/FlatBuffers/ByteBuffer.swift +++ b/swift/Sources/FlatBuffers/ByteBuffer.swift @@ -126,7 +126,7 @@ public struct ByteBuffer { /// - allowReadingUnalignedBuffers: allow reading from unaligned buffer public init( bytes: [UInt8], - allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = false) + allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = true) { var b = bytes _storage = Storage(count: bytes.count, alignment: alignment) @@ -144,7 +144,7 @@ public struct ByteBuffer { /// - allowReadingUnalignedBuffers: allow reading from unaligned buffer public init( data: Data, - allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = false) + allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = true) { var b = data _storage = Storage(count: data.count, alignment: alignment) @@ -176,7 +176,7 @@ public struct ByteBuffer { public init( contiguousBytes: Bytes, count: Int, - allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = false) + allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = true) { _storage = Storage(count: count, alignment: alignment) _writerSize = _storage.capacity @@ -195,7 +195,7 @@ public struct ByteBuffer { public init( assumingMemoryBound memory: UnsafeMutableRawPointer, capacity: Int, - allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = false) + allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = true) { _storage = Storage(memory: memory, capacity: capacity, unowned: true) _writerSize = capacity @@ -210,7 +210,7 @@ public struct ByteBuffer { init( memory: UnsafeMutableRawPointer, count: Int, - allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = false) + allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = true) { _storage = Storage(count: count, alignment: alignment) _storage.copy(from: memory, count: count) @@ -228,7 +228,7 @@ public struct ByteBuffer { memory: UnsafeMutableRawPointer, count: Int, removing removeBytes: Int, - allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = false) + allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = true) { _storage = Storage(count: count, alignment: alignment) _storage.copy(from: memory, count: count) From 0a214a64dd93c7761d8b5f4412ca4f932c243cf5 Mon Sep 17 00:00:00 2001 From: mr-swifter <103502437+mr-swifter@users.noreply.github.com> Date: Wed, 31 Jan 2024 10:44:45 +0200 Subject: [PATCH 02/11] Workaround https://github.com/apple/swift/issues/61455 --- swift/Sources/FlatBuffers/FlatBufferBuilder.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift index bf1978e334..d7cb7664ca 100644 --- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -50,7 +50,7 @@ public struct FlatBufferBuilder { private var serializeDefaults: Bool /// Current alignment for the buffer - var _minAlignment: Int = 0 { + public var _minAlignment: Int = 0 { didSet { _bb.alignment = _minAlignment } From aa8923a68f3ddcdf6195500726e44a2a6a3a2507 Mon Sep 17 00:00:00 2001 From: mr-swifter <103502437+mr-swifter@users.noreply.github.com> Date: Wed, 31 Jan 2024 10:46:13 +0200 Subject: [PATCH 03/11] Update FlatBufferBuilder.swift --- swift/Sources/FlatBuffers/FlatBufferBuilder.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift index d7cb7664ca..70ac823776 100644 --- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -50,6 +50,9 @@ public struct FlatBufferBuilder { private var serializeDefaults: Bool /// Current alignment for the buffer + /// + /// ONLY FOR FLATBUFFERS INTERNAL USAGE: + /// See https://github.com/apple/swift/issues/61455 public var _minAlignment: Int = 0 { didSet { _bb.alignment = _minAlignment From c231f0fd95b25c6b2dd39e3c2706632bad82b1fa Mon Sep 17 00:00:00 2001 From: mr-swifter <103502437+mr-swifter@users.noreply.github.com> Date: Mon, 26 May 2025 12:03:15 +0300 Subject: [PATCH 04/11] feat(patch): add @_specialize for Scalar types --- .../FlatBuffers/FlatBufferBuilder.swift | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift index 70ac823776..73795e16fa 100644 --- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -789,6 +789,18 @@ public struct FlatBufferBuilder { /// - def: Default value for that element /// - position: The predefined position of the element @inline(__always) + @_specialize(where T == Bool) + @_specialize(where T == Int) + @_specialize(where T == Int8) + @_specialize(where T == Int16) + @_specialize(where T == Int32) + @_specialize(where T == Int64) + @_specialize(where T == UInt8) + @_specialize(where T == UInt16) + @_specialize(where T == UInt32) + @_specialize(where T == UInt64) + @_specialize(where T == Double) + @_specialize(where T == Float32) mutating public func add( element: T, def: T, @@ -808,6 +820,18 @@ public struct FlatBufferBuilder { /// - element: Optional element of type scalar /// - position: The predefined position of the element @inline(__always) + @_specialize(where T == Bool) + @_specialize(where T == Int) + @_specialize(where T == Int8) + @_specialize(where T == Int16) + @_specialize(where T == Int32) + @_specialize(where T == Int64) + @_specialize(where T == UInt8) + @_specialize(where T == UInt16) + @_specialize(where T == UInt32) + @_specialize(where T == UInt64) + @_specialize(where T == Double) + @_specialize(where T == Float32) mutating public func add(element: T?, at position: VOffset) { guard let element = element else { return } track(offset: push(element: element), at: position) @@ -821,6 +845,18 @@ public struct FlatBufferBuilder { /// - returns: Postion of the Element @inline(__always) @discardableResult + @_specialize(where T == Bool) + @_specialize(where T == Int) + @_specialize(where T == Int8) + @_specialize(where T == Int16) + @_specialize(where T == Int32) + @_specialize(where T == Int64) + @_specialize(where T == UInt8) + @_specialize(where T == UInt16) + @_specialize(where T == UInt32) + @_specialize(where T == UInt64) + @_specialize(where T == Double) + @_specialize(where T == Float32) mutating public func push(element: T) -> UOffset { let size = MemoryLayout.size preAlign( From 48e70a6060574c2c96e2d08f62e1df46b9cee2f6 Mon Sep 17 00:00:00 2001 From: mr-swifter <103502437+mr-swifter@users.noreply.github.com> Date: Tue, 18 Nov 2025 11:48:06 +0100 Subject: [PATCH 05/11] Add specialization for Scalars in ByteBuffer --- swift/Sources/FlatBuffers/ByteBuffer.swift | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/swift/Sources/FlatBuffers/ByteBuffer.swift b/swift/Sources/FlatBuffers/ByteBuffer.swift index 06380ccd3f..475cc010ba 100644 --- a/swift/Sources/FlatBuffers/ByteBuffer.swift +++ b/swift/Sources/FlatBuffers/ByteBuffer.swift @@ -250,6 +250,18 @@ public struct ByteBuffer { /// - Parameter elements: An array of Scalars @inline(__always) @usableFromInline + @_specialize(where T == Bool) + @_specialize(where T == Int) + @_specialize(where T == Int8) + @_specialize(where T == Int16) + @_specialize(where T == Int32) + @_specialize(where T == Int64) + @_specialize(where T == UInt8) + @_specialize(where T == UInt16) + @_specialize(where T == UInt32) + @_specialize(where T == UInt64) + @_specialize(where T == Double) + @_specialize(where T == Float32) mutating func push(elements: [T]) { elements.withUnsafeBytes { ptr in ensureSpace(size: ptr.count) @@ -316,6 +328,18 @@ public struct ByteBuffer { /// - len: Offset to subtract from the WriterIndex @inline(__always) @usableFromInline + @_specialize(where T == Bool) + @_specialize(where T == Int) + @_specialize(where T == Int8) + @_specialize(where T == Int16) + @_specialize(where T == Int32) + @_specialize(where T == Int64) + @_specialize(where T == UInt8) + @_specialize(where T == UInt16) + @_specialize(where T == UInt32) + @_specialize(where T == UInt64) + @_specialize(where T == Double) + @_specialize(where T == Float32) mutating func push(value: T, len: Int) { ensureSpace(size: len) var v = value From b4694d950528535c6b03400fd14f4026112d9411 Mon Sep 17 00:00:00 2001 From: mr-swifter <103502437+mr-swifter@users.noreply.github.com> Date: Tue, 18 Nov 2025 11:53:45 +0100 Subject: [PATCH 06/11] Add specialization for Scalar --- .../FlatBuffers/FlatBufferBuilder.swift | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift index 73795e16fa..60116683a2 100644 --- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -366,6 +366,18 @@ public struct FlatBufferBuilder { /// - type: Type of the object to be written @inline(__always) @usableFromInline + @_specialize(where T == Bool) + @_specialize(where T == Int) + @_specialize(where T == Int8) + @_specialize(where T == Int16) + @_specialize(where T == Int32) + @_specialize(where T == Int64) + @_specialize(where T == UInt8) + @_specialize(where T == UInt16) + @_specialize(where T == UInt32) + @_specialize(where T == UInt64) + @_specialize(where T == Double) + @_specialize(where T == Float32) mutating internal func preAlign(len: Int, type: T.Type) { preAlign(len: len, alignment: MemoryLayout.size) } @@ -448,6 +460,18 @@ public struct FlatBufferBuilder { /// - Parameter elements: elements to be written into the buffer /// - returns: ``Offset`` of the vector @inline(__always) + @_specialize(where T == Bool) + @_specialize(where T == Int) + @_specialize(where T == Int8) + @_specialize(where T == Int16) + @_specialize(where T == Int32) + @_specialize(where T == Int64) + @_specialize(where T == UInt8) + @_specialize(where T == UInt16) + @_specialize(where T == UInt32) + @_specialize(where T == UInt64) + @_specialize(where T == Double) + @_specialize(where T == Float32) mutating public func createVector(_ elements: [T]) -> Offset { createVector(elements, size: elements.count) } @@ -466,6 +490,18 @@ public struct FlatBufferBuilder { /// - Parameter size: Count of elements /// - returns: ``Offset`` of the vector @inline(__always) + @_specialize(where T == Bool) + @_specialize(where T == Int) + @_specialize(where T == Int8) + @_specialize(where T == Int16) + @_specialize(where T == Int32) + @_specialize(where T == Int64) + @_specialize(where T == UInt8) + @_specialize(where T == UInt16) + @_specialize(where T == UInt32) + @_specialize(where T == UInt64) + @_specialize(where T == Double) + @_specialize(where T == Float32) mutating public func createVector( _ elements: [T], size: Int) -> Offset From 542bbd1427b08cfaa88bf9ddac54e1f51678b67b 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 07/11] 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 d662233f1d50ea4ebb56946cf8a9b41901747f68 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 08/11] 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 4aa08d576f..834afa599a 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 43f6ba3e4f004dbd31ef36a8927436a9a984683c 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 09/11] 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 834afa599a..f4b21dc991 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 d75aa8a7ee1ff2858deeae2621a89a3556901d70 Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Tue, 28 Apr 2026 17:43:28 +0200 Subject: [PATCH 10/11] patch flatc --- src/flatc.cpp | 2 +- src/idl_gen_swift.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/flatc.cpp b/src/flatc.cpp index ee34044fdd..d3c49f9c14 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -100,7 +100,7 @@ const static FlatCOption flatc_options[] = { "Disable generation of MIN and MAX enumerated values for scoped enums " "and prefixed enums."}, {"", "swift-implementation-only", "", - "Adds a @_implementationOnly to swift imports"}, + "Adds a internal to swift imports"}, {"", "gen-includes", "", "(deprecated), this is the default behavior. If the original behavior is " "required (no include statements) use --no-includes."}, diff --git a/src/idl_gen_swift.cpp b/src/idl_gen_swift.cpp index 0f519b2490..256103de7a 100644 --- a/src/idl_gen_swift.cpp +++ b/src/idl_gen_swift.cpp @@ -171,7 +171,7 @@ class SwiftGenerator : public BaseGenerator { if (parser_.opts.include_dependence_headers || parser_.opts.generate_all) { code_.SetValue("IMPLEMENTONLY", parser_.opts.swift_implementation_only - ? "@_implementationOnly " + ? "internal " : ""); code_ += "#if canImport(Common)"; code_ += "{{IMPLEMENTONLY}}import Common"; From 66c83a22135a5c4a033a99fe5280191bff57536b Mon Sep 17 00:00:00 2001 From: Diana Ma Date: Fri, 11 Sep 2026 21:47:24 -0500 Subject: [PATCH 11/11] add fieldIsPresent to flatc generated code --- src/idl_gen_swift.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/idl_gen_swift.cpp b/src/idl_gen_swift.cpp index 256103de7a..0bae152fe5 100644 --- a/src/idl_gen_swift.cpp +++ b/src/idl_gen_swift.cpp @@ -770,12 +770,20 @@ class SwiftGenerator : public BaseGenerator { } } + void GenIsPresent(const FieldDef& field) { + code_.SetValue("OFFSET", namer_.Constant(field.name)); + code_ += "{{ACCESS_TYPE}} var " + namer_.Variable(field) + + "IsPresent: Bool { return " + "{{ACCESS}}.offset({{TABLEOFFSET}}.{{OFFSET}}.v) != 0 }"; + } + void GenTableReader(const StructDef& struct_def) { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { const auto& field = **it; if (field.deprecated) continue; GenTableReaderFields(field); + GenIsPresent(field); } }