mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-18 10:54:26 +00:00
[Swift] Swift implementation 🎉🎉 (#5603)
* Implemented the swift version of Flatbuffers Implemented serailzing, reading, and mutating data from object monster Fixes mis-aligned pointer issue Fixes issue when shared strings are removed from table Adds swift enum, structs code gen Fixed namespace issues + started implementing the table gen Added Mutate function to the code generator Generated linux test cases Fixed an issue with bools, and structs readers in table writer Swift docker image added Updated the test cases, and removed a method parameters in swift Fixed createVector api when called with scalars Fixed issues with scalar arrays, and fixed the code gen namespaces, added sample_binary.swift Cleaned up project Added enum vectors, and their readers Refactored code Added swift into the support document Added documentation in docs, and fixed a small issue with Data() not being returned correctly Fixes Lowercase issue, and prevents generating lookups for deprecated keys * Made all the required funcs to have const + removed unneeded code + fix lowercase func * Removed transform from lowercased and moved it to function * Fixes an issue with iOS allocation from read * Refactored cpp code to be more readable * casts position into int for position * Fix enums issue, moves scalar writer code to use memcpy * Removed c_str from struct function * Fixed script to generate new objects when ran on travis ci: fix * Handles deallocating space allocated for structs * Updated the test cases to adhere to the fileprivate lookup, no mutation for unions, and updated the names of the vector functions
This commit is contained in:
committed by
Wouter van Oortmerssen
parent
55686100aa
commit
04d80f255d
243
swift/Sources/FlatBuffers/ByteBuffer.swift
Normal file
243
swift/Sources/FlatBuffers/ByteBuffer.swift
Normal file
@@ -0,0 +1,243 @@
|
||||
import Foundation
|
||||
|
||||
public final class ByteBuffer {
|
||||
|
||||
/// pointer to the start of the buffer object in memory
|
||||
private var _memory: UnsafeMutableRawPointer
|
||||
/// The size of the elements written to the buffer + their paddings
|
||||
private var _writerSize: Int = 0
|
||||
/// Capacity of UInt8 the buffer can hold
|
||||
private var _capacity: Int
|
||||
|
||||
/// Aliginment of the current memory being written to the buffer
|
||||
internal var alignment = 1
|
||||
/// Current Index which is being used to write to the buffer, it is written from the end to the start of the buffer
|
||||
internal var writerIndex: Int { return _capacity - _writerSize }
|
||||
|
||||
/// Reader is the position of the current Writer Index (capacity - size)
|
||||
public var reader: Int { return writerIndex }
|
||||
/// Current size of the buffer
|
||||
public var size: UOffset { return UOffset(_writerSize) }
|
||||
/// Public Pointer to the buffer object in memory. This should NOT be modified for any reason
|
||||
public var memory: UnsafeMutableRawPointer { return _memory }
|
||||
/// Current capacity for the buffer
|
||||
public var capacity: Int { return _capacity }
|
||||
|
||||
/// Constructor that creates a Flatbuffer object from a UInt8
|
||||
/// - Parameter bytes: Array of UInt8
|
||||
public init(bytes: [UInt8]) {
|
||||
let ptr = UnsafePointer(bytes)
|
||||
_memory = UnsafeMutableRawPointer.allocate(byteCount: bytes.count, alignment: alignment)
|
||||
_memory.copyMemory(from: ptr, byteCount: bytes.count)
|
||||
_capacity = bytes.count
|
||||
_writerSize = _capacity
|
||||
}
|
||||
|
||||
/// Constructor that creates a Flatbuffer from the Swift Data type object
|
||||
/// - Parameter data: Swift data Object
|
||||
public init(data: Data) {
|
||||
let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
|
||||
data.copyBytes(to: pointer, count: data.count)
|
||||
_memory = UnsafeMutableRawPointer(pointer)
|
||||
_capacity = data.count
|
||||
_writerSize = _capacity
|
||||
}
|
||||
|
||||
/// Constructor that creates a Flatbuffer instance with a size
|
||||
/// - Parameter size: Length of the buffer
|
||||
init(initialSize size: Int) {
|
||||
let size = size.convertToPowerofTwo
|
||||
_memory = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: alignment)
|
||||
_memory.initializeMemory(as: UInt8.self, repeating: 0, count: size)
|
||||
_capacity = size
|
||||
}
|
||||
|
||||
/// Creates a copy of the existing flatbuffer, by copying it to a different memory.
|
||||
/// - Parameters:
|
||||
/// - memory: Current memory of the buffer
|
||||
/// - count: count of bytes
|
||||
/// - removeBytes: Removes a number of bytes from the current size
|
||||
internal init(memory: UnsafeMutableRawPointer, count: Int, removing removeBytes: Int) {
|
||||
_memory = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: alignment)
|
||||
_memory.copyMemory(from: memory, byteCount: count)
|
||||
_capacity = count
|
||||
_writerSize = removeBytes
|
||||
}
|
||||
|
||||
deinit { _memory.deallocate() }
|
||||
|
||||
/// Fills the buffer with padding by adding to the writersize
|
||||
/// - Parameter padding: Amount of padding between two to be serialized objects
|
||||
func fill(padding: UInt32) {
|
||||
ensureSpace(size: UInt8(padding))
|
||||
_writerSize += (MemoryLayout<UInt8>.size * Int(padding))
|
||||
}
|
||||
|
||||
///Adds an array of type Scalar to the buffer memory
|
||||
/// - Parameter elements: An array of Scalars
|
||||
func push<T: Scalar>(elements: [T]) {
|
||||
let size = elements.count * MemoryLayout<T>.size
|
||||
ensureSpace(size: UInt8(size))
|
||||
elements.lazy.reversed().forEach { (s) in
|
||||
push(value: s, len: MemoryLayout.size(ofValue: s))
|
||||
}
|
||||
}
|
||||
|
||||
/// A custom type of structs that are padded according to the flatbuffer padding,
|
||||
/// - Parameters:
|
||||
/// - value: Pointer to the object in memory
|
||||
/// - size: Size of Value being written to the buffer
|
||||
func push(struct value: UnsafeMutableRawPointer, size: Int) {
|
||||
ensureSpace(size: UInt8(size))
|
||||
_memory.advanced(by: writerIndex - size).copyMemory(from: value, byteCount: size)
|
||||
defer { value.deallocate() }
|
||||
_writerSize += size
|
||||
}
|
||||
|
||||
/// Adds an object of type Scalar into the buffer
|
||||
/// - Parameters:
|
||||
/// - value: Object that will be written to the buffer
|
||||
/// - len: Offset to subtract from the WriterIndex
|
||||
func push<T: Scalar>(value: T, len: Int) {
|
||||
ensureSpace(size: UInt8(len))
|
||||
var v = value.convertedEndian
|
||||
memcpy(_memory.advanced(by: writerIndex - len), &v, len)
|
||||
_writerSize += len
|
||||
}
|
||||
|
||||
/// Adds a string to the buffer using swift.utf8 object
|
||||
/// - Parameter str: String that will be added to the buffer
|
||||
/// - Parameter len: length of the string
|
||||
func push(string str: String, len: Int) {
|
||||
ensureSpace(size: UInt8(len))
|
||||
if str.utf8.withContiguousStorageIfAvailable({ self.push(bytes: $0, len: len) }) != nil {
|
||||
} else {
|
||||
let utf8View = str.utf8
|
||||
for c in utf8View.lazy.reversed() {
|
||||
push(value: c, len: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a string to Bytebuffer using UTF8View
|
||||
/// - Parameters:
|
||||
/// - bytes: Pointer to the view
|
||||
/// - len: Size of string
|
||||
private func push(bytes: UnsafeBufferPointer<String.UTF8View.Element>, len: Int) -> Bool {
|
||||
_memory.advanced(by: writerIndex - len).copyMemory(from:
|
||||
UnsafeRawPointer(bytes.baseAddress!), byteCount: len)
|
||||
_writerSize += len
|
||||
return true
|
||||
}
|
||||
|
||||
/// Write stores an object into the buffer directly or indirectly.
|
||||
///
|
||||
/// Direct: ignores the capacity of buffer which would mean we are referring to the direct point in memory
|
||||
/// indirect: takes into respect the current capacity of the buffer (capacity - index), writing to the buffer from the end
|
||||
/// - Parameters:
|
||||
/// - value: Value that needs to be written to the buffer
|
||||
/// - index: index to write to
|
||||
/// - direct: Should take into consideration the capacity of the buffer
|
||||
func write<T>(value: T, index: Int, direct: Bool = false) {
|
||||
var index = index
|
||||
if !direct {
|
||||
index = _capacity - index
|
||||
}
|
||||
_memory.storeBytes(of: value, toByteOffset: index, as: T.self)
|
||||
}
|
||||
|
||||
/// Makes sure that buffer has enouch space for each of the objects that will be written into it
|
||||
/// - Parameter size: size of object
|
||||
@discardableResult
|
||||
func ensureSpace(size: UInt8) -> UInt8 {
|
||||
if Int(size) + _writerSize > _capacity { reallocate(size) }
|
||||
assert(size < FlatBufferMaxSize, "Buffer can't grow beyond 2 Gigabytes")
|
||||
return size
|
||||
}
|
||||
|
||||
/// Reallocates the buffer incase the object to be written doesnt fit in the current buffer
|
||||
/// - Parameter size: Size of the current object
|
||||
fileprivate func reallocate(_ size: UInt8) {
|
||||
let currentWritingIndex = writerIndex
|
||||
while _capacity <= _writerSize + Int(size) {
|
||||
_capacity = _capacity << 1
|
||||
}
|
||||
|
||||
/// solution take from Apple-NIO
|
||||
_capacity = _capacity.convertToPowerofTwo
|
||||
|
||||
let newData = UnsafeMutableRawPointer.allocate(byteCount: _capacity, alignment: alignment)
|
||||
newData.initializeMemory(as: UInt8.self, repeating: 0, count: _capacity)
|
||||
newData
|
||||
.advanced(by: writerIndex)
|
||||
.copyMemory(from: _memory.advanced(by: currentWritingIndex), byteCount: _writerSize)
|
||||
_memory.deallocate()
|
||||
_memory = newData
|
||||
}
|
||||
|
||||
/// Clears the current size of the buffer
|
||||
public func clearSize() {
|
||||
_writerSize = 0
|
||||
}
|
||||
|
||||
/// Clears the current instance of the buffer, replacing it with new memory
|
||||
public func clear() {
|
||||
_writerSize = 0
|
||||
alignment = 1
|
||||
_memory.deallocate()
|
||||
_memory = UnsafeMutableRawPointer.allocate(byteCount: _capacity, alignment: alignment)
|
||||
}
|
||||
|
||||
/// Resizes the buffer size
|
||||
/// - Parameter size: new size for the buffer
|
||||
internal func resize(_ size: Int) {
|
||||
_writerSize = size
|
||||
}
|
||||
|
||||
/// Reads an object from the buffer
|
||||
/// - Parameters:
|
||||
/// - def: Type of the object
|
||||
/// - position: the index of the object in the buffer
|
||||
public func read<T>(def: T.Type, position: Int) -> T {
|
||||
return _memory.advanced(by: position).load(as: T.self)
|
||||
}
|
||||
|
||||
/// Reads a slice from the memory assuming a type of T
|
||||
/// - Parameters:
|
||||
/// - index: index of the object to be read from the buffer
|
||||
/// - count: count of bytes in memory
|
||||
public func readSlice<T>(index: Int32,
|
||||
count: Int32) -> [T] {
|
||||
let start = _memory.advanced(by: Int(index)).assumingMemoryBound(to: T.self)
|
||||
let array = UnsafeBufferPointer(start: start, count: Int(count))
|
||||
return Array(array)
|
||||
}
|
||||
|
||||
/// Reads a string from the buffer and encodes it to a swift string
|
||||
/// - Parameters:
|
||||
/// - index: index of the string in the buffer
|
||||
/// - count: length of the string
|
||||
/// - type: Encoding of the string
|
||||
public func readString(at index: Int32,
|
||||
count: Int32,
|
||||
type: String.Encoding = .utf8) -> String? {
|
||||
let start = _memory.advanced(by: Int(index)).assumingMemoryBound(to: UInt8.self)
|
||||
let bufprt = UnsafeBufferPointer(start: start, count: Int(count))
|
||||
return String(bytes: Array(bufprt), encoding: type)
|
||||
}
|
||||
|
||||
/// Creates a new Flatbuffer object that's duplicated from the current one
|
||||
/// - Parameter removeBytes: the amount of bytes to remove from the current Size
|
||||
public func duplicate(removing removeBytes: Int = 0) -> ByteBuffer {
|
||||
return ByteBuffer(memory: _memory, count: _capacity, removing: _writerSize - removeBytes)
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
func debugMemory(str: String) {
|
||||
let bufprt = UnsafeBufferPointer(start: _memory.assumingMemoryBound(to: UInt8.self),
|
||||
count: _capacity)
|
||||
let a = Array(bufprt)
|
||||
print(str, a, " \nwith buffer size: \(a.count) and writer size: \(_writerSize)")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
89
swift/Sources/FlatBuffers/Constants.swift
Normal file
89
swift/Sources/FlatBuffers/Constants.swift
Normal file
@@ -0,0 +1,89 @@
|
||||
#if os(Linux)
|
||||
import CoreFoundation
|
||||
#else
|
||||
import Foundation
|
||||
#endif
|
||||
|
||||
/// A boolean to see if the system is littleEndian
|
||||
let isLitteEndian = CFByteOrderGetCurrent() == Int(CFByteOrderLittleEndian.rawValue)
|
||||
/// Constant for the file id length
|
||||
let FileIdLength = 4
|
||||
/// Type aliases
|
||||
public typealias Byte = UInt8
|
||||
public typealias UOffset = UInt32
|
||||
public typealias SOffset = Int32
|
||||
public typealias VOffset = UInt16
|
||||
/// Maximum size for a buffer
|
||||
public let FlatBufferMaxSize = UInt32.max << ((MemoryLayout<SOffset>.size * 8 - 1) - 1)
|
||||
|
||||
/// Protocol that confirms all the numbers
|
||||
///
|
||||
/// Scalar is used to confirm all the numbers that can be represented in a FlatBuffer. It's used to write/read from the buffer.
|
||||
public protocol Scalar: Equatable {
|
||||
associatedtype NumericValue
|
||||
var convertedEndian: NumericValue { get }
|
||||
}
|
||||
|
||||
extension Scalar where Self: FixedWidthInteger {
|
||||
/// Converts the value from BigEndian to LittleEndian
|
||||
///
|
||||
/// Converts values to little endian on machines that work with BigEndian, however this is NOT TESTED yet.
|
||||
public var convertedEndian: NumericValue {
|
||||
if isLitteEndian { return self as! Self.NumericValue }
|
||||
fatalError("This is not tested! please report an issue on the offical flatbuffers repo")
|
||||
}
|
||||
}
|
||||
|
||||
extension Double: Scalar {
|
||||
public typealias NumericValue = UInt64
|
||||
|
||||
public var convertedEndian: UInt64 {
|
||||
if isLitteEndian { return self.bitPattern }
|
||||
return self.bitPattern.littleEndian
|
||||
}
|
||||
}
|
||||
|
||||
extension Float32: Scalar {
|
||||
public typealias NumericValue = UInt32
|
||||
|
||||
public var convertedEndian: UInt32 {
|
||||
if isLitteEndian { return self.bitPattern }
|
||||
return self.bitPattern.littleEndian
|
||||
}
|
||||
}
|
||||
|
||||
extension Int: Scalar {
|
||||
public typealias NumericValue = Int
|
||||
}
|
||||
|
||||
extension Int8: Scalar {
|
||||
public typealias NumericValue = Int8
|
||||
}
|
||||
|
||||
extension Int16: Scalar {
|
||||
public typealias NumericValue = Int16
|
||||
}
|
||||
|
||||
extension Int32: Scalar {
|
||||
public typealias NumericValue = Int32
|
||||
}
|
||||
|
||||
extension Int64: Scalar {
|
||||
public typealias NumericValue = Int64
|
||||
}
|
||||
|
||||
extension UInt8: Scalar {
|
||||
public typealias NumericValue = UInt8
|
||||
}
|
||||
|
||||
extension UInt16: Scalar {
|
||||
public typealias NumericValue = UInt16
|
||||
}
|
||||
|
||||
extension UInt32: Scalar {
|
||||
public typealias NumericValue = UInt32
|
||||
}
|
||||
|
||||
extension UInt64: Scalar {
|
||||
public typealias NumericValue = UInt64
|
||||
}
|
||||
486
swift/Sources/FlatBuffers/FlatBufferBuilder.swift
Normal file
486
swift/Sources/FlatBuffers/FlatBufferBuilder.swift
Normal file
@@ -0,0 +1,486 @@
|
||||
import Foundation
|
||||
|
||||
public final class FlatBufferBuilder {
|
||||
|
||||
/// Vtables used in the buffer are stored in here, so they would be written later in EndTable
|
||||
private var _vtable: [UInt32] = []
|
||||
/// 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
|
||||
private var stringOffsetMap: [String: Offset<String>] = [:]
|
||||
/// 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
|
||||
|
||||
/// Current alignment for the buffer
|
||||
var _minAlignment: Int = 0 {
|
||||
didSet {
|
||||
_bb.alignment = _minAlignment
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives a read access to the buffer's size
|
||||
public var size: UOffset { return _bb.size }
|
||||
/// Data representation of the buffer
|
||||
public var data: Data {
|
||||
assert(finished, "Data shouldn't be called before finish()")
|
||||
return Data(bytes: _bb.memory.advanced(by: _bb.writerIndex),
|
||||
count: _bb.capacity - _bb.writerIndex)
|
||||
}
|
||||
/// Get's the fully sized buffer stored in memory
|
||||
public var fullSizedByteArray: [UInt8] {
|
||||
let ptr = UnsafeBufferPointer(start: _bb.memory.assumingMemoryBound(to: UInt8.self),
|
||||
count: _bb.capacity)
|
||||
return Array(ptr)
|
||||
}
|
||||
/// Returns the written size of the buffer
|
||||
public var sizedByteArray: [UInt8] {
|
||||
let cp = _bb.capacity - _bb.writerIndex
|
||||
let start = _bb.memory.advanced(by: _bb.writerIndex)
|
||||
.bindMemory(to: UInt8.self, capacity: cp)
|
||||
|
||||
let ptr = UnsafeBufferPointer(start: start, count: cp)
|
||||
return Array(ptr)
|
||||
}
|
||||
/// Returns the buffer
|
||||
public var buffer: ByteBuffer { return _bb }
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
/// initialize the buffer with a size
|
||||
/// - Parameters:
|
||||
/// - initialSize: Initial size for the buffer
|
||||
/// - force: Allows default to be serialized into the buffer
|
||||
public init(initialSize: Int32 = 1024, serializeDefaults force: Bool = false) {
|
||||
assert(initialSize > 0, "Size should be greater than zero!")
|
||||
guard isLitteEndian else {
|
||||
fatalError("Reading/Writing a buffer in big endian machine is not supported on swift")
|
||||
}
|
||||
serializeDefaults = force
|
||||
_bb = ByteBuffer(initialSize: Int(initialSize))
|
||||
}
|
||||
|
||||
/// Clears the buffer and the builder from it's data
|
||||
public func clear() {
|
||||
_minAlignment = 0
|
||||
isNested = false
|
||||
_bb.clear()
|
||||
stringOffsetMap = [:]
|
||||
}
|
||||
|
||||
/// Removes all the offsets from the VTable
|
||||
public func clearOffsets() {
|
||||
_vtable = []
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Create Tables
|
||||
|
||||
extension FlatBufferBuilder {
|
||||
|
||||
/// Checks if the required fields were serialized into the buffer
|
||||
/// - Parameters:
|
||||
/// - table: offset for the table
|
||||
/// - fields: Array of all the important fields to be serialized
|
||||
public func require(table: Offset<UOffset>, fields: [Int32]) {
|
||||
for field in fields {
|
||||
let start = _bb.capacity - Int(table.o)
|
||||
let startTable = start - Int(_bb.read(def: Int32.self, position: start))
|
||||
let isOkay = _bb.read(def: VOffset.self, position: startTable + Int(field)) != 0
|
||||
assert(isOkay, "Flatbuffers requires the following field")
|
||||
}
|
||||
}
|
||||
|
||||
/// Finished the buffer by adding the file id and then calling finish
|
||||
/// - Parameters:
|
||||
/// - offset: Offset of the table
|
||||
/// - fileId: Takes the fileId
|
||||
/// - prefix: if false it wont add the size of the buffer
|
||||
public func finish<T>(offset: Offset<T>, fileId: String, addPrefix prefix: Bool = false) {
|
||||
let size = MemoryLayout<UOffset>.size
|
||||
preAlign(len: size + (prefix ? size : 0) + FileIdLength, alignment: _minAlignment)
|
||||
assert(fileId.count == FileIdLength, "Flatbuffers requires file id to be 4")
|
||||
_bb.push(string: fileId, len: 4)
|
||||
finish(offset: offset, addPrefix: prefix)
|
||||
}
|
||||
|
||||
/// Finished the buffer by adding the file id, offset, and prefix to it.
|
||||
/// - Parameters:
|
||||
/// - offset: Offset of the table
|
||||
/// - prefix: if false it wont add the size of the buffer
|
||||
public func finish<T>(offset: Offset<T>, addPrefix prefix: Bool = false) {
|
||||
notNested()
|
||||
let size = MemoryLayout<UOffset>.size
|
||||
preAlign(len: size + (prefix ? size : 0), alignment: _minAlignment)
|
||||
push(element: refer(to: offset.o))
|
||||
if prefix { push(element: _bb.size) }
|
||||
clearOffsets()
|
||||
finished = true
|
||||
}
|
||||
|
||||
/// starttable will let the builder know, that a new object is being serialized.
|
||||
///
|
||||
/// The function will fatalerror if called while there is another object being serialized
|
||||
/// - Parameter numOfFields: Number of elements to be written to the buffer
|
||||
public func startTable(with numOfFields: Int) -> UOffset {
|
||||
notNested()
|
||||
isNested = true
|
||||
_vtable = [UInt32](repeating: 0, count: numOfFields)
|
||||
return _bb.size
|
||||
}
|
||||
|
||||
|
||||
/// Endtable will let the builder know that the object that's written to it is completed
|
||||
///
|
||||
/// This would be called after all the elements are serialized, it will add the vtable into the buffer.
|
||||
/// it will fatalError in case the object is called without starttable, or the object has exceeded the limit of
|
||||
/// 2GB,
|
||||
/// - Parameter startOffset:Start point of the object written
|
||||
/// - returns: The root of the table
|
||||
public func endTable(at startOffset: UOffset) -> UOffset {
|
||||
assert(isNested, "Calling endtable without calling starttable")
|
||||
let sizeofVoffset = MemoryLayout<VOffset>.size
|
||||
let vTableOffset = push(element: SOffset(0))
|
||||
|
||||
let tableObjectSize = vTableOffset - startOffset
|
||||
assert(tableObjectSize < 0x10000, "Buffer can't grow beyond 2 Gigabytes")
|
||||
|
||||
var writeIndex = 0
|
||||
for (index,j) in _vtable.lazy.reversed().enumerated() {
|
||||
if j != 0 {
|
||||
writeIndex = _vtable.count - index
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for i in stride(from: writeIndex - 1, to: -1, by: -1) {
|
||||
let off = _vtable[i] == 0 ? 0 : vTableOffset - _vtable[i]
|
||||
_bb.push(value: VOffset(off), len: sizeofVoffset)
|
||||
}
|
||||
|
||||
_bb.push(value: VOffset(tableObjectSize), len: sizeofVoffset)
|
||||
_bb.push(value: (UInt16(writeIndex + 2) * UInt16(sizeofVoffset)), len: sizeofVoffset)
|
||||
|
||||
clearOffsets()
|
||||
let vt_use = _bb.size
|
||||
|
||||
var isAlreadyAdded: Int?
|
||||
|
||||
mainLoop: for table in _vtables {
|
||||
let vt1 = _bb.capacity - Int(table)
|
||||
let vt2 = _bb.writerIndex
|
||||
let len = _bb.read(def: Int16.self, position: vt1)
|
||||
guard len == _bb.read(def: Int16.self, position: vt2) else { break }
|
||||
for i in stride(from: sizeofVoffset, to: Int(len), by: sizeofVoffset) {
|
||||
let vt1ReadValue = _bb.read(def: Int16.self, position: vt1 + i)
|
||||
let vt2ReadValue = _bb.read(def: Int16.self, position: vt2 + i)
|
||||
if vt1ReadValue != vt2ReadValue {
|
||||
break mainLoop
|
||||
}
|
||||
}
|
||||
isAlreadyAdded = Int(table)
|
||||
}
|
||||
|
||||
if let offset = isAlreadyAdded {
|
||||
let vTableOff = Int(vTableOffset)
|
||||
let space = _bb.capacity - vTableOff
|
||||
_bb.write(value: Int32(offset - vTableOff), index: space, direct: true)
|
||||
_bb.resize(_bb.capacity - space)
|
||||
} else {
|
||||
_bb.write(value: Int32(vt_use) - Int32(vTableOffset), index: Int(vTableOffset))
|
||||
_vtables.append(_bb.size)
|
||||
}
|
||||
isNested = false
|
||||
return vTableOffset
|
||||
}
|
||||
|
||||
// MARK: - Builds Buffer
|
||||
|
||||
/// asserts to see if the object is not nested
|
||||
fileprivate func notNested() {
|
||||
assert(!isNested, "Object serialization must not be nested")
|
||||
}
|
||||
|
||||
/// Changes the minimuim alignment of the buffer
|
||||
/// - Parameter size: size of the current alignment
|
||||
fileprivate func minAlignment(size: Int) {
|
||||
if size > _minAlignment {
|
||||
_minAlignment = size
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the padding for the current element
|
||||
/// - Parameters:
|
||||
/// - bufSize: Current size of the buffer + the offset of the object to be written
|
||||
/// - elementSize: Element size
|
||||
fileprivate func padding(bufSize: UInt32, elementSize: UInt32) -> UInt32 {
|
||||
((~bufSize) + 1) & (elementSize - 1)
|
||||
}
|
||||
|
||||
/// Prealigns the buffer before writting a new object into the buffer
|
||||
/// - Parameters:
|
||||
/// - len:Length of the object
|
||||
/// - alignment: Alignment type
|
||||
fileprivate func preAlign(len: Int, alignment: Int) {
|
||||
minAlignment(size: alignment)
|
||||
_bb.fill(padding: padding(bufSize: _bb.size + UOffset(len), elementSize: UOffset(alignment)))
|
||||
}
|
||||
|
||||
/// Prealigns the buffer before writting a new object into the buffer
|
||||
/// - Parameters:
|
||||
/// - len: Length of the object
|
||||
/// - type: Type of the object to be written
|
||||
fileprivate func preAlign<T: Scalar>(len: Int, type: T.Type) {
|
||||
preAlign(len: len, alignment: MemoryLayout<T>.size)
|
||||
}
|
||||
|
||||
/// Refers to an object that's written in the buffer
|
||||
/// - Parameter off: the objects index value
|
||||
fileprivate func refer(to off: UOffset) -> UOffset {
|
||||
let size = MemoryLayout<UOffset>.size
|
||||
preAlign(len: size, alignment: size)
|
||||
return _bb.size - off + UInt32(size)
|
||||
}
|
||||
|
||||
/// Tracks the elements written into the buffer
|
||||
/// - Parameters:
|
||||
/// - offset: The offset of the element witten
|
||||
/// - position: The position of the element
|
||||
fileprivate func track(offset: UOffset, at position: VOffset) {
|
||||
_vtable[Int(position)] = offset
|
||||
}
|
||||
|
||||
// MARK: - Vectors
|
||||
|
||||
/// Starts a vector of length and Element size
|
||||
public func startVector(_ len: Int, elementSize: Int) {
|
||||
notNested()
|
||||
isNested = true
|
||||
preAlign(len: len * elementSize, type: UOffset.self)
|
||||
preAlign(len: len * elementSize, alignment: elementSize)
|
||||
}
|
||||
|
||||
/// Ends the vector of at length
|
||||
///
|
||||
/// The current function will fatalError if startVector is called before serializing the vector
|
||||
/// - Parameter len: Length of the buffer
|
||||
public func endVector(len: Int) -> UOffset {
|
||||
assert(isNested, "Calling endVector without calling startVector")
|
||||
isNested = false
|
||||
return push(element: Int32(len))
|
||||
}
|
||||
|
||||
/// Creates a vector of type Scalar in the buffer
|
||||
/// - Parameter elements: elements to be written into the buffer
|
||||
/// - returns: Offset of the vector
|
||||
public func createVector<T: Scalar>(_ elements: [T]) -> Offset<UOffset> {
|
||||
return createVector(elements, size: elements.count)
|
||||
}
|
||||
|
||||
/// Creates a vector of type Scalar in the buffer
|
||||
/// - Parameter elements: Elements to be written into the buffer
|
||||
/// - Parameter size: Count of elements
|
||||
/// - returns: Offset of the vector
|
||||
public func createVector<T: Scalar>(_ elements: [T], size: Int) -> Offset<UOffset> {
|
||||
let size = size
|
||||
startVector(size, elementSize: MemoryLayout<T>.size)
|
||||
_bb.push(elements: elements)
|
||||
return Offset(offset: endVector(len: size))
|
||||
}
|
||||
|
||||
/// Creates a vector of type Enums in the buffer
|
||||
/// - Parameter elements: elements to be written into the buffer
|
||||
/// - returns: Offset of the vector
|
||||
public func createVector<T: Enum>(_ elements: [T]) -> Offset<UOffset> {
|
||||
return createVector(elements, size: elements.count)
|
||||
}
|
||||
|
||||
/// Creates a vector of type Enums in the buffer
|
||||
/// - Parameter elements: Elements to be written into the buffer
|
||||
/// - Parameter size: Count of elements
|
||||
/// - returns: Offset of the vector
|
||||
public func createVector<T: Enum>(_ elements: [T], size: Int) -> Offset<UOffset> {
|
||||
let size = size
|
||||
startVector(size, elementSize: T.byteSize)
|
||||
for e in elements.lazy.reversed() {
|
||||
_bb.push(value: e.value, len: T.byteSize)
|
||||
}
|
||||
return Offset(offset: endVector(len: size))
|
||||
}
|
||||
|
||||
/// Creates a vector of type Offsets in the buffer
|
||||
/// - Parameter offsets:Array of offsets of type T
|
||||
/// - returns: Offset of the vector
|
||||
public func createVector<T>(ofOffsets offsets: [Offset<T>]) -> Offset<UOffset> {
|
||||
createVector(ofOffsets: offsets, len: offsets.count)
|
||||
}
|
||||
|
||||
/// Creates a vector of type Offsets in the buffer
|
||||
/// - Parameter elements: Array of offsets of type T
|
||||
/// - Parameter size: Count of elements
|
||||
/// - returns: Offset of the vector
|
||||
public func createVector<T>(ofOffsets offsets: [Offset<T>], len: Int) -> Offset<UOffset> {
|
||||
startVector(len, elementSize: MemoryLayout<Offset<T>>.size)
|
||||
for o in offsets.lazy.reversed() {
|
||||
push(element: o)
|
||||
}
|
||||
return Offset(offset: endVector(len: len))
|
||||
}
|
||||
|
||||
/// Creates a vector of Strings
|
||||
/// - Parameter str: a vector of strings that will be written into the buffer
|
||||
/// - returns: Offset of the vector
|
||||
public func createVector(ofStrings str: [String]) -> Offset<UOffset> {
|
||||
var offsets: [Offset<String>] = []
|
||||
for s in str {
|
||||
offsets.append(create(string: s))
|
||||
}
|
||||
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
|
||||
public func createVector<T: Readable>(structs: [UnsafeMutableRawPointer],
|
||||
type: T.Type) -> Offset<UOffset> {
|
||||
startVector(structs.count * T.size, elementSize: T.alignment)
|
||||
for i in structs.lazy.reversed() {
|
||||
create(struct: i, type: T.self)
|
||||
}
|
||||
return Offset(offset: endVector(len: structs.count))
|
||||
}
|
||||
|
||||
// MARK: - Inserting Structs
|
||||
|
||||
/// Writes a Flatbuffer struct into the buffer
|
||||
/// - Parameters:
|
||||
/// - s: Flatbuffer struct
|
||||
/// - type: Type of the element to be serialized
|
||||
/// - returns: Offset of the Object
|
||||
@discardableResult
|
||||
public func create<T: Readable>(struct s: UnsafeMutableRawPointer,
|
||||
type: T.Type) -> Offset<UOffset> {
|
||||
let size = T.size
|
||||
preAlign(len: size, alignment: T.alignment)
|
||||
_bb.push(struct: s, size: size)
|
||||
return 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
|
||||
public func add(structOffset o: UOffset) {
|
||||
guard Int(o) < _vtable.count else { fatalError("Out of the table range") }
|
||||
_vtable[Int(o)] = _bb.size
|
||||
}
|
||||
|
||||
// MARK: - Inserting Strings
|
||||
|
||||
/// Insets a string into the buffer using UTF8
|
||||
/// - Parameter str: String to be serialized
|
||||
/// - returns: The strings offset in the buffer
|
||||
public func create(string str: String) -> Offset<String> {
|
||||
let len = str.count
|
||||
notNested()
|
||||
preAlign(len: len + 1, type: UOffset.self)
|
||||
_bb.fill(padding: 1)
|
||||
_bb.push(string: str, len: len)
|
||||
push(element: UOffset(len))
|
||||
return Offset(offset: _bb.size)
|
||||
}
|
||||
|
||||
/// Inserts a shared string to the buffer
|
||||
///
|
||||
/// The function checks the stringOffsetmap if it's seen a similar string before
|
||||
/// - Parameter str: String to be serialized
|
||||
/// - returns: The strings offset in the buffer
|
||||
public func createShared(string str: String) -> Offset<String> {
|
||||
if let offset = stringOffsetMap[str] {
|
||||
return offset
|
||||
}
|
||||
let offset = create(string: str)
|
||||
stringOffsetMap[str] = offset
|
||||
return offset
|
||||
}
|
||||
|
||||
// MARK: - Inseting offsets
|
||||
|
||||
/// Adds the offset of an object into the buffer
|
||||
/// - Parameters:
|
||||
/// - offset: Offset of another object to be written
|
||||
/// - position: The predefined position of the object
|
||||
public func add<T>(offset: Offset<T>, at position: VOffset) {
|
||||
if offset.isEmpty {
|
||||
track(offset: 0, at: position)
|
||||
return
|
||||
}
|
||||
add(element: refer(to: offset.o), def: 0, at: position)
|
||||
}
|
||||
|
||||
/// Pushes a value of type offset into the buffer
|
||||
/// - Parameter o: Offset
|
||||
/// - returns: Position of the offset
|
||||
@discardableResult
|
||||
public func push<T>(element o: Offset<T>) -> UOffset {
|
||||
push(element: refer(to: o.o))
|
||||
}
|
||||
|
||||
// MARK: - Inserting Scalars to Buffer
|
||||
|
||||
/// Adds a value into the buffer of type Scalar
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - element: Element to insert
|
||||
/// - def: Default value for that element
|
||||
/// - position: The predefined position of the element
|
||||
public func add<T: Scalar>(element: T, def: T, at position: VOffset) {
|
||||
if (element == def && !serializeDefaults) {
|
||||
track(offset: 0, at: position)
|
||||
return
|
||||
}
|
||||
let off = push(element: element)
|
||||
track(offset: off, at: position)
|
||||
}
|
||||
|
||||
/// Adds Boolean values into the buffer
|
||||
/// - Parameters:
|
||||
/// - condition: Condition to insert
|
||||
/// - def: Default condition
|
||||
/// - position: The predefined position of the element
|
||||
public func add(condition: Bool, def: Bool, at position: VOffset) {
|
||||
if (condition == def && !serializeDefaults) {
|
||||
track(offset: 0, at: position)
|
||||
return
|
||||
}
|
||||
let off = push(element: Byte(condition ? 1 : 0))
|
||||
track(offset: off, at: position)
|
||||
}
|
||||
|
||||
/// Pushes the values into the buffer
|
||||
/// - Parameter element: Element to insert
|
||||
/// - returns: Postion of the Element
|
||||
@discardableResult
|
||||
public func push<T: Scalar>(element: T) -> UOffset {
|
||||
preAlign(len: MemoryLayout<T>.size,
|
||||
alignment: MemoryLayout<T>.size)
|
||||
_bb.push(value: element, len: MemoryLayout<T>.size)
|
||||
return _bb.size
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// Used to debug the buffer and the implementation
|
||||
public func debug(str: String = "normal memory: ") {
|
||||
_bb.debugMemory(str: str)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
87
swift/Sources/FlatBuffers/FlatBufferObject.swift
Normal file
87
swift/Sources/FlatBuffers/FlatBufferObject.swift
Normal file
@@ -0,0 +1,87 @@
|
||||
import Foundation
|
||||
|
||||
/// FlatbufferObject structures all the Flatbuffers objects
|
||||
public protocol FlatBufferObject {
|
||||
init(_ bb: ByteBuffer, o: Int32)
|
||||
}
|
||||
|
||||
/// 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 }
|
||||
var value: T { get }
|
||||
}
|
||||
|
||||
/// Mutable is a protocol that allows us to mutate Scalar values within the buffer
|
||||
public protocol Mutable {
|
||||
/// makes Flatbuffer accessed within the Protocol
|
||||
var bb: ByteBuffer { get }
|
||||
/// makes position of the table/struct accessed within the Protocol
|
||||
var postion: Int32 { get }
|
||||
}
|
||||
|
||||
extension Mutable {
|
||||
|
||||
/// Mutates the memory in the buffer, this is only called from the access function of table and structs
|
||||
/// - Parameters:
|
||||
/// - value: New value to be inserted to the buffer
|
||||
/// - index: index of the Element
|
||||
func mutate<T: Scalar>(value: T, o: Int32) -> Bool {
|
||||
guard o != 0 else { return false }
|
||||
bb.write(value: value, index: Int(o), direct: true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension Mutable where Self == Table {
|
||||
|
||||
/// Mutates a value by calling mutate with respect to the position in the table
|
||||
/// - Parameters:
|
||||
/// - value: New value to be inserted to the buffer
|
||||
/// - index: index of the Element
|
||||
public func mutate<T: Scalar>(_ value: T, index: Int32) -> Bool {
|
||||
guard index != 0 else { return false }
|
||||
return mutate(value: value, o: index + postion)
|
||||
}
|
||||
|
||||
/// Directly mutates the element by calling mutate
|
||||
///
|
||||
/// Mutates the Element at index ignoring the current position by calling mutate
|
||||
/// - Parameters:
|
||||
/// - value: New value to be inserted to the buffer
|
||||
/// - index: index of the Element
|
||||
public func directMutate<T: Scalar>(_ value: T, index: Int32) -> Bool {
|
||||
return mutate(value: value, o: index)
|
||||
}
|
||||
}
|
||||
|
||||
extension Mutable where Self == Struct {
|
||||
|
||||
/// Mutates a value by calling mutate with respect to the position in the struct
|
||||
/// - Parameters:
|
||||
/// - value: New value to be inserted to the buffer
|
||||
/// - index: index of the Element
|
||||
public func mutate<T: Scalar>(_ value: T, index: Int32) -> Bool {
|
||||
return mutate(value: value, o: index + postion)
|
||||
}
|
||||
|
||||
/// Directly mutates the element by calling mutate
|
||||
///
|
||||
/// Mutates the Element at index ignoring the current position by calling mutate
|
||||
/// - Parameters:
|
||||
/// - value: New value to be inserted to the buffer
|
||||
/// - index: index of the Element
|
||||
public func directMutate<T: Scalar>(_ value: T, index: Int32) -> Bool {
|
||||
return mutate(value: value, o: index)
|
||||
}
|
||||
}
|
||||
extension Struct: Mutable {}
|
||||
extension Table: Mutable {}
|
||||
16
swift/Sources/FlatBuffers/FlatBuffersUtils.swift
Normal file
16
swift/Sources/FlatBuffers/FlatBuffersUtils.swift
Normal file
@@ -0,0 +1,16 @@
|
||||
import Foundation
|
||||
|
||||
public final class FlatBuffersUtils {
|
||||
|
||||
/// Gets the size of the prefix
|
||||
/// - Parameter bb: Flatbuffer object
|
||||
public static func getSizePrefix(bb: ByteBuffer) -> Int32 {
|
||||
return bb.read(def: Int32.self, position: bb.reader)
|
||||
}
|
||||
|
||||
/// Removes the prefix by duplicating the Flatbuffer
|
||||
/// - Parameter bb: Flatbuffer object
|
||||
public static func removeSizePrefix(bb: ByteBuffer) -> ByteBuffer {
|
||||
return bb.duplicate(removing: MemoryLayout<Int32>.size)
|
||||
}
|
||||
}
|
||||
31
swift/Sources/FlatBuffers/Int+extension.swift
Normal file
31
swift/Sources/FlatBuffers/Int+extension.swift
Normal file
@@ -0,0 +1,31 @@
|
||||
import Foundation
|
||||
|
||||
extension Int {
|
||||
|
||||
/// Moves the current int into the nearest power of two
|
||||
///
|
||||
/// This is used since the UnsafeMutableRawPointer will face issues when writing/reading
|
||||
/// if the buffer alignment exceeds that actual size of the buffer
|
||||
var convertToPowerofTwo: Int {
|
||||
guard self > 0 else { return 1 }
|
||||
var n = UOffset(self)
|
||||
|
||||
#if arch(arm) || arch(i386)
|
||||
let max = UInt32(Int.max)
|
||||
#else
|
||||
let max = UInt32.max
|
||||
#endif
|
||||
|
||||
n -= 1
|
||||
n |= n >> 1
|
||||
n |= n >> 2
|
||||
n |= n >> 4
|
||||
n |= n >> 8
|
||||
n |= n >> 16
|
||||
if n != max {
|
||||
n += 1
|
||||
}
|
||||
|
||||
return Int(n)
|
||||
}
|
||||
}
|
||||
12
swift/Sources/FlatBuffers/Offset.swift
Normal file
12
swift/Sources/FlatBuffers/Offset.swift
Normal file
@@ -0,0 +1,12 @@
|
||||
import Foundation
|
||||
|
||||
/// Offset object for all the Objects that are written into the buffer
|
||||
public struct Offset<T> {
|
||||
/// Offset of the object in the buffer
|
||||
public var o: UOffset
|
||||
/// Returns false if the offset is equal to zero
|
||||
public var isEmpty: Bool { return o == 0 }
|
||||
|
||||
public init(offset: UOffset) { o = offset }
|
||||
public init() { o = 0 }
|
||||
}
|
||||
16
swift/Sources/FlatBuffers/Struct.swift
Normal file
16
swift/Sources/FlatBuffers/Struct.swift
Normal file
@@ -0,0 +1,16 @@
|
||||
import Foundation
|
||||
|
||||
public struct Struct {
|
||||
public private(set) var bb: ByteBuffer
|
||||
public private(set) var postion: Int32
|
||||
|
||||
public init(bb: ByteBuffer, position: Int32 = 0) {
|
||||
self.bb = bb
|
||||
self.postion = position
|
||||
}
|
||||
|
||||
public func readBuffer<T: Scalar>(of type: T.Type, at o: Int32) -> T {
|
||||
let r = bb.read(def: T.self, position: Int(o + postion))
|
||||
return r
|
||||
}
|
||||
}
|
||||
144
swift/Sources/FlatBuffers/Table.swift
Normal file
144
swift/Sources/FlatBuffers/Table.swift
Normal file
@@ -0,0 +1,144 @@
|
||||
import Foundation
|
||||
|
||||
public struct Table {
|
||||
public private(set) var bb: ByteBuffer
|
||||
public private(set) var postion: Int32
|
||||
|
||||
public init(bb: ByteBuffer, position: Int32 = 0) {
|
||||
guard isLitteEndian else {
|
||||
fatalError("Reading/Writing a buffer in big endian machine is not supported on swift")
|
||||
}
|
||||
self.bb = bb
|
||||
self.postion = position
|
||||
}
|
||||
|
||||
public func offset(_ o: Int32) -> Int32 {
|
||||
let vtable = postion - bb.read(def: Int32.self, position: Int(postion))
|
||||
return o < bb.read(def: VOffset.self, position: Int(vtable)) ? Int32(bb.read(def: Int16.self, position: Int(vtable + o))) : 0
|
||||
}
|
||||
|
||||
public func indirect(_ o: Int32) -> Int32 { return o + bb.read(def: Int32.self, position: Int(o)) }
|
||||
|
||||
/// String reads from the buffer with respect to position of the current table.
|
||||
/// - Parameter offset: Offset of the string
|
||||
public func string(at offset: Int32) -> String? {
|
||||
return directString(at: offset + postion)
|
||||
}
|
||||
|
||||
/// Direct string reads from the buffer disregarding the position of the table.
|
||||
/// It would be preferable to use string unless the current position of the table is not needed
|
||||
/// - Parameter offset: Offset of the string
|
||||
public func directString(at offset: Int32) -> String? {
|
||||
var offset = offset
|
||||
offset += bb.read(def: Int32.self, position: Int(offset))
|
||||
let count = bb.read(def: Int32.self, position: Int(offset))
|
||||
let position = offset + Int32(MemoryLayout<Int32>.size)
|
||||
return bb.readString(at: position, count: count)
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// - o: Offset of the Element
|
||||
public func readBuffer<T: Scalar>(of type: T.Type, at o: Int32) -> T {
|
||||
return directRead(of: T.self, offset: o + postion)
|
||||
}
|
||||
|
||||
/// Reads from the buffer disregarding the position of the table.
|
||||
/// It would be used when reading from an
|
||||
/// ```
|
||||
/// let offset = __t.offset(10)
|
||||
/// //Only used when the we already know what is the
|
||||
/// // position in the table since __t.vector(at:)
|
||||
/// // returns the index with respect to the position
|
||||
/// __t.directRead(of: Byte.self,
|
||||
/// offset: __t.vector(at: offset) + index * 1)
|
||||
/// ```
|
||||
/// - Parameters:
|
||||
/// - type: Type of Scalar 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 {
|
||||
let r = bb.read(def: T.self, position: Int(o))
|
||||
return r
|
||||
}
|
||||
|
||||
public func union<T: FlatBufferObject>(_ o: Int32) -> T {
|
||||
let o = o + postion
|
||||
return directUnion(o)
|
||||
}
|
||||
|
||||
public func directUnion<T: FlatBufferObject>(_ o: Int32) -> T {
|
||||
return T.init(bb, o: o + bb.read(def: Int32.self, position: Int(o)))
|
||||
}
|
||||
|
||||
public func getVector<T>(at off: Int32) -> [T]? {
|
||||
let o = offset(off)
|
||||
guard o != 0 else { return nil }
|
||||
return bb.readSlice(index: vector(at: o), count: vector(count: o))
|
||||
}
|
||||
|
||||
/// Vector count gets the count of Elements within the array
|
||||
/// - Parameter o: start offset of the vector
|
||||
/// - returns: Count of elements
|
||||
public func vector(count o: Int32) -> Int32 {
|
||||
var o = o
|
||||
o += postion
|
||||
o += bb.read(def: Int32.self, position: Int(o))
|
||||
return bb.read(def: Int32.self, position: Int(o))
|
||||
}
|
||||
|
||||
/// Vector start index in the buffer
|
||||
/// - Parameter o:start offset of the vector
|
||||
/// - returns: the start index of the vector
|
||||
public func vector(at o: Int32) -> Int32 {
|
||||
var o = o
|
||||
o += postion
|
||||
return o + bb.read(def: Int32.self, position: Int(o)) + 4
|
||||
}
|
||||
}
|
||||
|
||||
extension Table {
|
||||
|
||||
static public func indirect(_ o: Int32, _ fbb: ByteBuffer) -> Int32 { return o + fbb.read(def: Int32.self, position: Int(o)) }
|
||||
|
||||
static public func offset(_ o: Int32, vOffset: Int32, fbb: ByteBuffer) -> Int32 {
|
||||
let vTable = Int32(fbb.capacity) - o
|
||||
return vTable + Int32(fbb.read(def: Int16.self, position: Int(vTable + vOffset - fbb.read(def: Int32.self, position: Int(vTable)))))
|
||||
}
|
||||
|
||||
static public func compare(_ off1: Int32, _ off2: Int32, fbb: ByteBuffer) -> Int32 {
|
||||
let memorySize = Int32(MemoryLayout<Int32>.size)
|
||||
let _off1 = off1 + fbb.read(def: Int32.self, position: Int(off1))
|
||||
let _off2 = off2 + fbb.read(def: Int32.self, position: Int(off2))
|
||||
let len1 = fbb.read(def: Int32.self, position: Int(_off1))
|
||||
let len2 = fbb.read(def: Int32.self, position: Int(_off2))
|
||||
let startPos1 = _off1 + memorySize
|
||||
let startPos2 = _off2 + memorySize
|
||||
let minValue = min(len1, len2)
|
||||
for i in 0...minValue {
|
||||
let b1 = fbb.read(def: Int8.self, position: Int(i + startPos1))
|
||||
let b2 = fbb.read(def: Int8.self, position: Int(i + startPos2))
|
||||
if b1 != b2 {
|
||||
return Int32(b2 - b1)
|
||||
}
|
||||
}
|
||||
return len1 - len2
|
||||
}
|
||||
|
||||
static public func compare(_ off1: Int32, _ key: [Byte], fbb: ByteBuffer) -> Int32 {
|
||||
let memorySize = Int32(MemoryLayout<Int32>.size)
|
||||
let _off1 = off1 + fbb.read(def: Int32.self, position: Int(off1))
|
||||
let len1 = fbb.read(def: Int32.self, position: Int(_off1))
|
||||
let len2 = Int32(key.count)
|
||||
let startPos1 = _off1 + memorySize
|
||||
let minValue = min(len1, len2)
|
||||
for i in 0..<minValue {
|
||||
let b = fbb.read(def: Int8.self, position: Int(i + startPos1))
|
||||
let byte = key[Int(i)]
|
||||
if b != byte {
|
||||
return Int32(b - Int8(byte))
|
||||
}
|
||||
}
|
||||
return len1 - len2
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user