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() } } 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..0bae152fe5 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"; @@ -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); } } diff --git a/swift/Sources/Common/Scalar.swift b/swift/Sources/Common/Scalar.swift index 518580a605..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 ec27fd1d73..ed5213a6ef 100644 --- a/swift/Sources/FlatBuffers/ByteBuffer.swift +++ b/swift/Sources/FlatBuffers/ByteBuffer.swift @@ -26,8 +26,9 @@ public struct ByteBuffer { /// deallocating the memory that was held by (memory: UnsafeMutableRawPointer) @usableFromInline final class Storage { + #if compiler(>=6.0) @usableFromInline - enum Blob { + @frozen enum Blob: ~Copyable { #if !os(WASI) case data(Data) case bytes(ContiguousBytes) @@ -36,7 +37,65 @@ 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)" + } + } + } + #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 @@ -44,7 +103,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) { @@ -56,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 } @@ -150,6 +218,7 @@ public struct ByteBuffer { @discardableResult @inline(__always) + @inlinable func readWithUnsafeRawPointer( position: Int, _ body: (UnsafeRawPointer) throws -> T) rethrows -> T @@ -276,6 +345,18 @@ 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, + count: Int, + removing removeBytes: Int) + { + _storage = Storage(blob: blob, capacity: count) + _readerIndex = removeBytes + capacity = count + } + #else @inline(__always) init( blob: Storage.Blob, @@ -286,6 +367,7 @@ public struct ByteBuffer { _readerIndex = removeBytes capacity = count } + #endif /// Write stores an object into the buffer directly or indirectly. /// @@ -317,13 +399,25 @@ 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 { + _storage.readWithUnsafeRawPointer(position: position) { + $0.bindMemory(to: T.self, capacity: 1) + .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: @@ -412,7 +506,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 +558,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..4730100ab5 100644 --- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -47,10 +47,14 @@ 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 { + /// + /// ONLY FOR FLATBUFFERS INTERNAL USAGE: + /// See https://github.com/apple/swift/issues/61455 + public var _minAlignment: Int = 0 { didSet { _bb.alignment = _minAlignment } @@ -370,6 +374,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) } @@ -452,6 +468,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) } @@ -470,6 +498,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 @@ -756,6 +796,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 +835,19 @@ public struct FlatBufferBuilder { /// - def: Default value for that element /// - position: The predefined position of the element @inline(__always) + @inlinable + @_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, @@ -813,6 +867,19 @@ public struct FlatBufferBuilder { /// - element: Optional element of type scalar /// - position: The predefined position of the element @inline(__always) + @inlinable + @_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) @@ -825,7 +892,20 @@ public struct FlatBufferBuilder { /// - Parameter element: Element to insert /// - returns: position of the Element @inline(__always) + @inlinable @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( @@ -835,10 +915,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 0b9f01b070..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 4aa08d576f..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 b0ef3968d0..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 diff --git a/swift/Sources/FlatBuffers/_InternalByteBuffer.swift b/swift/Sources/FlatBuffers/_InternalByteBuffer.swift index 8dced85b41..1d59673d2e 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) { @@ -115,6 +115,18 @@ struct _InternalByteBuffer { /// - 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) @@ -181,6 +193,18 @@ struct _InternalByteBuffer { /// - 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) withUnsafePointer(to: value) { @@ -333,6 +357,7 @@ struct _InternalByteBuffer { @discardableResult @inline(__always) + @inlinable func readWithUnsafeRawPointer( position: Int, _ body: (UnsafeRawPointer) throws -> T) rethrows -> T