mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-02 04:04:19 +00:00
[Swift] Rebuild the way swift handles structs from scratch (#6326)
* Rebuild the way swift handles structs from scratch * Updates docs, and sample binary * Replaces InMemory to Mutable * Migrates docs from inmemory * use inline for some functions * Renamed Mutable objects * Updates documentation
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'FlatBuffers'
|
||||
s.version = '0.8.1'
|
||||
s.version = '1.0.0'
|
||||
s.summary = 'FlatBuffers: Memory Efficient Serialization Library'
|
||||
|
||||
s.description = "FlatBuffers is a cross platform serialization library architected for
|
||||
|
||||
@@ -10,7 +10,7 @@ and Cocoapods
|
||||
|
||||
1- To report any error please use the main repository.
|
||||
|
||||
2- `0.6.0` deprecates `add(condition:bool)` for `add(element:bool)`. You can download the [binary here](https://github.com/google/flatbuffers/actions) and select the latest push to master
|
||||
2- `1.0.0` deprecates `MyGame_Example_Vec3.createVec3(builder: &fbb, x: 10, test2: .blue)` for `MyGame_Example_Vec3(x: 10, test2: .blue)`. This uses Swift native structs instead of workarounds that which leads to a huge performance increase when serializing structs. You can download the [binary here](https://github.com/google/flatbuffers/actions) and select the latest push to master
|
||||
|
||||
### Contribute
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ public struct ByteBuffer {
|
||||
_writerSize = _writerSize &+ (MemoryLayout<UInt8>.size &* padding)
|
||||
}
|
||||
|
||||
///Adds an array of type Scalar to the buffer memory
|
||||
/// Adds an array of type Scalar to the buffer memory
|
||||
/// - Parameter elements: An array of Scalars
|
||||
@usableFromInline
|
||||
mutating func push<T: Scalar>(elements: [T]) {
|
||||
@@ -198,41 +198,16 @@ public struct ByteBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
/// A custom type of structs that are padded according to the flatbuffer padding,
|
||||
/// Adds an object of type NativeStruct into the buffer
|
||||
/// - Parameters:
|
||||
/// - value: Pointer to the object in memory
|
||||
/// - size: Size of Value being written to the buffer
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message: "0.9.0 will be removing the following method. Regenerate the code")
|
||||
@usableFromInline
|
||||
mutating func push(struct value: UnsafeMutableRawPointer, size: Int) {
|
||||
/// - value: Object that will be written to the buffer
|
||||
/// - size: size to subtract from the WriterIndex
|
||||
@inline(__always)
|
||||
mutating func push<T: NativeStruct>(struct value: T, size: Int) {
|
||||
ensureSpace(size: size)
|
||||
memcpy(_storage.memory.advanced(by: writerIndex &- size), value, size)
|
||||
defer { value.deallocate() }
|
||||
_writerSize = _writerSize &+ size
|
||||
}
|
||||
|
||||
/// Prepares the buffer to receive a struct of certian size.
|
||||
/// The alignment of the memory is already handled since we already called preAlign
|
||||
/// - Parameter size: size of the struct
|
||||
@usableFromInline
|
||||
mutating func prepareBufferToReceiveStruct(of size: Int) {
|
||||
ensureSpace(size: size)
|
||||
_writerSize = _writerSize &+ size
|
||||
}
|
||||
|
||||
/// Reverse the input direction to the buffer, since `FlatBuffers` uses a back to front, following method will take current `writerIndex`
|
||||
/// and writes front to back into the buffer, respecting the padding & the alignment
|
||||
/// - Parameters:
|
||||
/// - value: value of type Scalar
|
||||
/// - position: position relative to the `writerIndex`
|
||||
/// - len: length of the value in terms of bytes
|
||||
@usableFromInline
|
||||
mutating func reversePush<T: Scalar>(value: T, position: Int, len: Int) {
|
||||
var v = value
|
||||
memcpy(_storage.memory.advanced(by: writerIndex &+ position), &v, len)
|
||||
memcpy(_storage.memory.advanced(by: writerIndex &- size), &v, size)
|
||||
_writerSize = _writerSize &+ size
|
||||
}
|
||||
|
||||
/// Adds an object of type Scalar into the buffer
|
||||
@@ -266,7 +241,7 @@ public struct ByteBuffer {
|
||||
/// - Parameters:
|
||||
/// - bytes: Pointer to the view
|
||||
/// - len: Size of string
|
||||
@usableFromInline
|
||||
@inline(__always)
|
||||
mutating internal func push(
|
||||
bytes: UnsafeBufferPointer<String.UTF8View.Element>,
|
||||
len: Int) -> Bool
|
||||
@@ -300,7 +275,7 @@ public struct ByteBuffer {
|
||||
/// Makes sure that buffer has enouch space for each of the objects that will be written into it
|
||||
/// - Parameter size: size of object
|
||||
@discardableResult
|
||||
@usableFromInline
|
||||
@inline(__always)
|
||||
mutating func ensureSpace(size: Int) -> Int {
|
||||
if size &+ _writerSize > _storage.capacity {
|
||||
_storage.reallocate(size, writerSize: _writerSize, alignment: alignment)
|
||||
@@ -311,7 +286,7 @@ public struct ByteBuffer {
|
||||
|
||||
/// pops the written VTable if it's already written into the buffer
|
||||
/// - Parameter size: size of the `VTable`
|
||||
@usableFromInline
|
||||
@inline(__always)
|
||||
mutating internal func pop(_ size: Int) {
|
||||
assert((_writerSize &- size) > 0, "New size should NOT be a negative number")
|
||||
memset(_storage.memory.advanced(by: writerIndex), 0, _writerSize &- size)
|
||||
@@ -319,11 +294,13 @@ public struct ByteBuffer {
|
||||
}
|
||||
|
||||
/// Clears the current size of the buffer
|
||||
@inline(__always)
|
||||
mutating public func clearSize() {
|
||||
_writerSize = 0
|
||||
}
|
||||
|
||||
/// Clears the current instance of the buffer, replacing it with new memory
|
||||
@inline(__always)
|
||||
mutating public func clear() {
|
||||
_writerSize = 0
|
||||
alignment = 1
|
||||
@@ -345,6 +322,7 @@ public struct ByteBuffer {
|
||||
/// - Parameters:
|
||||
/// - index: index of the object to be read from the buffer
|
||||
/// - count: count of bytes in memory
|
||||
@inline(__always)
|
||||
public func readSlice<T>(
|
||||
index: Int32,
|
||||
count: Int32) -> [T]
|
||||
@@ -362,6 +340,7 @@ public struct ByteBuffer {
|
||||
/// - index: index of the string in the buffer
|
||||
/// - count: length of the string
|
||||
/// - type: Encoding of the string
|
||||
@inline(__always)
|
||||
public func readString(
|
||||
at index: Int32,
|
||||
count: Int32,
|
||||
|
||||
@@ -20,11 +20,11 @@ public struct FlatBufferBuilder {
|
||||
|
||||
/// Storage for the Vtables used in the buffer are stored in here, so they would be written later in EndTable
|
||||
@usableFromInline internal var _vtableStorage = VTableStorage()
|
||||
/// Flatbuffer data will be written into
|
||||
@usableFromInline internal var _bb: ByteBuffer
|
||||
|
||||
/// Reference Vtables that were already written to the buffer
|
||||
private var _vtables: [UOffset] = []
|
||||
/// Flatbuffer data will be written into
|
||||
private var _bb: ByteBuffer
|
||||
/// A check if the buffer is being written into by a different table
|
||||
private var isNested = false
|
||||
/// Dictonary that stores a map of all the strings that were written to the buffer
|
||||
@@ -227,7 +227,7 @@ public struct FlatBufferBuilder {
|
||||
|
||||
/// Changes the minimuim alignment of the buffer
|
||||
/// - Parameter size: size of the current alignment
|
||||
@usableFromInline
|
||||
@inline(__always)
|
||||
mutating internal func minAlignment(size: Int) {
|
||||
if size > _minAlignment {
|
||||
_minAlignment = size
|
||||
@@ -238,7 +238,7 @@ public struct FlatBufferBuilder {
|
||||
/// - Parameters:
|
||||
/// - bufSize: Current size of the buffer + the offset of the object to be written
|
||||
/// - elementSize: Element size
|
||||
@usableFromInline
|
||||
@inline(__always)
|
||||
mutating internal func padding(bufSize: UInt32, elementSize: UInt32) -> UInt32 {
|
||||
((~bufSize) &+ 1) & (elementSize - 1)
|
||||
}
|
||||
@@ -282,7 +282,7 @@ public struct FlatBufferBuilder {
|
||||
_vtableStorage.add(loc: FieldLoc(offset: offset, position: position))
|
||||
}
|
||||
|
||||
// MARK: - Vectors
|
||||
// MARK: - Inserting Vectors
|
||||
|
||||
/// Starts a vector of length and Element size
|
||||
mutating public func startVector(_ len: Int, elementSize: Int) {
|
||||
@@ -296,10 +296,10 @@ public struct FlatBufferBuilder {
|
||||
///
|
||||
/// The current function will fatalError if startVector is called before serializing the vector
|
||||
/// - Parameter len: Length of the buffer
|
||||
mutating public func endVector(len: Int) -> UOffset {
|
||||
mutating public func endVector(len: Int) -> Offset<UOffset> {
|
||||
assert(isNested, "Calling endVector without calling startVector")
|
||||
isNested = false
|
||||
return push(element: Int32(len))
|
||||
return Offset(offset: push(element: Int32(len)))
|
||||
}
|
||||
|
||||
/// Creates a vector of type Scalar in the buffer
|
||||
@@ -317,7 +317,7 @@ public struct FlatBufferBuilder {
|
||||
let size = size
|
||||
startVector(size, elementSize: MemoryLayout<T>.size)
|
||||
_bb.push(elements: elements)
|
||||
return Offset(offset: endVector(len: size))
|
||||
return endVector(len: size)
|
||||
}
|
||||
|
||||
/// Creates a vector of type Enums in the buffer
|
||||
@@ -337,7 +337,7 @@ public struct FlatBufferBuilder {
|
||||
for e in elements.reversed() {
|
||||
_bb.push(value: e.value, len: T.byteSize)
|
||||
}
|
||||
return Offset(offset: endVector(len: size))
|
||||
return endVector(len: size)
|
||||
}
|
||||
|
||||
/// Creates a vector of type Offsets in the buffer
|
||||
@@ -356,7 +356,7 @@ public struct FlatBufferBuilder {
|
||||
for o in offsets.reversed() {
|
||||
push(element: o)
|
||||
}
|
||||
return Offset(offset: endVector(len: len))
|
||||
return endVector(len: len)
|
||||
}
|
||||
|
||||
/// Creates a vector of Strings
|
||||
@@ -370,101 +370,47 @@ public struct FlatBufferBuilder {
|
||||
return createVector(ofOffsets: offsets)
|
||||
}
|
||||
|
||||
/// Creates a vector of Flatbuffer structs.
|
||||
///
|
||||
/// The function takes a Type to know what size it is, and alignment
|
||||
/// - Parameters:
|
||||
/// - structs: An array of UnsafeMutableRawPointer
|
||||
/// - type: Type of the struct being written
|
||||
/// - returns: Offset of the vector
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message: "0.9.0 will be removing the following method. Regenerate the code")
|
||||
mutating public func createVector<T: Readable>(
|
||||
structs: [UnsafeMutableRawPointer],
|
||||
type: T.Type) -> Offset<UOffset>
|
||||
{
|
||||
startVector(structs.count &* T.size, elementSize: T.alignment)
|
||||
/// Creates a vector of `Native swift structs` which were padded to flatbuffers standards
|
||||
/// - Parameter structs: A vector of structs
|
||||
/// - Returns: offset of the vector
|
||||
mutating public func createVector<T: NativeStruct>(ofStructs structs: [T]) -> Offset<UOffset> {
|
||||
startVector(structs.count * MemoryLayout<T>.size, elementSize: MemoryLayout<T>.alignment)
|
||||
for i in structs.reversed() {
|
||||
create(struct: i, type: T.self)
|
||||
_ = create(struct: i)
|
||||
}
|
||||
return Offset(offset: endVector(len: structs.count))
|
||||
}
|
||||
|
||||
/// Starts a vector of struct that considers the size and alignment of the struct
|
||||
/// - Parameters:
|
||||
/// - count: number of elements to be written
|
||||
/// - size: size of struct
|
||||
/// - alignment: alignment of the struct
|
||||
mutating public func startVectorOfStructs(count: Int, size: Int, alignment: Int) {
|
||||
startVector(count &* size, elementSize: alignment)
|
||||
}
|
||||
|
||||
/// Ends the vector of structs and writtens the current offset
|
||||
/// - Parameter count: number of written elements
|
||||
/// - Returns: Offset of type UOffset
|
||||
mutating public func endVectorOfStructs(count: Int) -> Offset<UOffset> {
|
||||
Offset<UOffset>(offset: endVector(len: count))
|
||||
return endVector(len: structs.count)
|
||||
}
|
||||
|
||||
// MARK: - Inserting Structs
|
||||
|
||||
/// Writes a Flatbuffer struct into the buffer
|
||||
/// Fills the buffer with a native struct that's build and padded according to flatbuffers standards
|
||||
/// - Parameters:
|
||||
/// - s: Flatbuffer struct
|
||||
/// - type: Type of the element to be serialized
|
||||
/// - returns: Offset of the Object
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message: "0.9.0 will be removing the following method. Regenerate the code")
|
||||
/// - s: `Native swift` struct to insert
|
||||
/// - position: The predefined position of the object
|
||||
/// - Returns: offset of written struct
|
||||
@discardableResult
|
||||
mutating public func create<T: Readable>(
|
||||
struct s: UnsafeMutableRawPointer,
|
||||
type: T.Type) -> Offset<UOffset>
|
||||
mutating public func create<T: NativeStruct>(
|
||||
struct s: T, position: VOffset) -> Offset<UOffset>
|
||||
{
|
||||
let size = T.size
|
||||
preAlign(len: size, alignment: T.alignment)
|
||||
let offset = create(struct: s)
|
||||
_vtableStorage.add(loc: FieldLoc(offset: _bb.size, position: VOffset(position)))
|
||||
return offset
|
||||
}
|
||||
|
||||
/// Fills the buffer with a native struct that's build and padded according to flatbuffers standards
|
||||
/// - Parameters:
|
||||
/// - s: `Native swift` struct to insert
|
||||
/// - Returns: offset of written struct
|
||||
@discardableResult
|
||||
mutating public func create<T: NativeStruct>(
|
||||
struct s: T) -> Offset<UOffset>
|
||||
{
|
||||
let size = MemoryLayout<T>.size
|
||||
preAlign(len: size, alignment: MemoryLayout<T>.alignment)
|
||||
_bb.push(struct: s, size: size)
|
||||
return Offset(offset: _bb.size)
|
||||
}
|
||||
|
||||
/// prepares the ByteBuffer to receive a struct of size and alignment
|
||||
/// - Parameters:
|
||||
/// - size: size of written struct
|
||||
/// - alignment: alignment of written struct
|
||||
mutating public func createStructOf(size: Int, alignment: Int) {
|
||||
preAlign(len: size, alignment: alignment)
|
||||
_bb.prepareBufferToReceiveStruct(of: size)
|
||||
}
|
||||
|
||||
/// Adds scalars front to back instead of the default behavior of the normal add
|
||||
/// - Parameters:
|
||||
/// - v: element of type Scalar
|
||||
/// - postion: position relative to the `writerIndex`
|
||||
mutating public func reverseAdd<T: Scalar>(v: T, postion: Int) {
|
||||
_bb.reversePush(
|
||||
value: v,
|
||||
position: postion,
|
||||
len: MemoryLayout<T>.size)
|
||||
}
|
||||
|
||||
/// Ends the struct and returns the current buffer size
|
||||
/// - Returns: Offset of type UOffset
|
||||
@discardableResult
|
||||
public func endStruct() -> Offset<UOffset> {
|
||||
Offset(offset: _bb.size)
|
||||
}
|
||||
|
||||
/// Adds the offset of a struct into the vTable
|
||||
///
|
||||
/// The function fatalErrors if we pass an offset that is out of range
|
||||
/// - Parameter o: offset
|
||||
mutating public func add(structOffset o: VOffset) {
|
||||
_vtableStorage.add(loc: FieldLoc(offset: _bb.size, position: VOffset(o)))
|
||||
}
|
||||
|
||||
// MARK: - Inserting Strings
|
||||
|
||||
/// Insets a string into the buffer using UTF8
|
||||
@@ -596,6 +542,7 @@ extension FlatBufferBuilder: CustomDebugStringConvertible {
|
||||
|
||||
/// Builds a buffer with byte count of fieldloc.size * count of field numbers
|
||||
/// - Parameter count: number of fields to be written
|
||||
@inline(__always)
|
||||
func start(count: Int) {
|
||||
assert(count >= 0, "number of fields should NOT be negative")
|
||||
let capacity = count &* size
|
||||
@@ -621,6 +568,7 @@ extension FlatBufferBuilder: CustomDebugStringConvertible {
|
||||
|
||||
/// Ensure that the buffer has enough space instead of recreating the buffer each time.
|
||||
/// - Parameter space: space required for the new vtable
|
||||
@inline(__always)
|
||||
func ensure(space: Int) {
|
||||
guard space &+ writtenIndex > capacity else { return }
|
||||
memory.deallocate()
|
||||
@@ -631,6 +579,7 @@ extension FlatBufferBuilder: CustomDebugStringConvertible {
|
||||
/// Loads an object of type `FieldLoc` from buffer memory
|
||||
/// - Parameter index: index of element
|
||||
/// - Returns: a FieldLoc at index
|
||||
@inline(__always)
|
||||
func load(at index: Int) -> FieldLoc {
|
||||
memory.load(fromByteOffset: index, as: FieldLoc.self)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
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 {}
|
||||
|
||||
/// FlatbufferObject structures all the Flatbuffers objects
|
||||
public protocol FlatBufferObject {
|
||||
var __buffer: ByteBuffer! { get }
|
||||
@@ -28,15 +32,6 @@ public protocol ObjectAPI {
|
||||
mutating func unpack() -> T
|
||||
}
|
||||
|
||||
/// Readable is structures all the Flatbuffers structs
|
||||
///
|
||||
/// Readable is a procotol that each Flatbuffer struct should confirm to since
|
||||
/// FlatBufferBuilder would require a Type to both create(struct:) and createVector(structs:) functions
|
||||
public protocol Readable: FlatBufferObject {
|
||||
static var size: Int { get }
|
||||
static var alignment: Int { get }
|
||||
}
|
||||
|
||||
public protocol Enum {
|
||||
associatedtype T: Scalar
|
||||
static var byteSize: Int { get }
|
||||
|
||||
@@ -56,9 +56,9 @@ public struct Table {
|
||||
|
||||
/// Reads from the buffer with respect to the position in the table.
|
||||
/// - Parameters:
|
||||
/// - type: Type of Scalar that needs to be read from the buffer
|
||||
/// - type: Type of Element that needs to be read from the buffer
|
||||
/// - o: Offset of the Element
|
||||
public func readBuffer<T: Scalar>(of type: T.Type, at o: Int32) -> T {
|
||||
public func readBuffer<T>(of type: T.Type, at o: Int32) -> T {
|
||||
directRead(of: T.self, offset: o + postion)
|
||||
}
|
||||
|
||||
@@ -73,9 +73,9 @@ public struct Table {
|
||||
/// offset: __t.vector(at: offset) + index * 1)
|
||||
/// ```
|
||||
/// - Parameters:
|
||||
/// - type: Type of Scalar that needs to be read from the buffer
|
||||
/// - type: Type of Element that needs to be read from the buffer
|
||||
/// - o: Offset of the Element
|
||||
public func directRead<T: Scalar>(of type: T.Type, offset o: Int32) -> T {
|
||||
public func directRead<T>(of type: T.Type, offset o: Int32) -> T {
|
||||
let r = bb.read(def: T.self, position: Int(o))
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol NativeTable {}
|
||||
public protocol UnionObject {}
|
||||
|
||||
extension NativeTable {
|
||||
extension UnionObject {
|
||||
|
||||
/// Serialize is a helper function that serailizes the data from the Object API to a bytebuffer directly th
|
||||
/// - Parameter type: Type of the Flatbuffer object
|
||||
Reference in New Issue
Block a user