Squashed 'third_party/flatbuffers/' changes from d6a8dbd26..338393f85
338393f85 Documentation updates for Optional Scalars (#6014) (#6270)
c27bc2d76 [C++] Add ParseJson(), Parser(Parser&&), update fuzzers (#6284)
bc518a512 Fixed FlexBufferBuilder asserting on duplicate keys
100c59054 Added a few more paths for auto labeler (#6281)
e58c18244 Add --require-explicit-ids to require explicit ids (#6277)
69a8b2a57 idl_gen_json_schema.cpp: Changed generation of array element types (#6253)
25eba6f35 fix typo (#6280)
e1f0f75ba Updated Ms build Action to fix build issue (#6279)
faeb04fbe Add type annotation to unspecified array (#6264)
537212afe [Swift] Adds a format file and reformats the swift project (#6250)
6764f25d9 Adds a fix for enum generation (#6263)
Change-Id: I716bd4d2521fb0a673e50a699cef761e042052b2
git-subtree-dir: third_party/flatbuffers
git-subtree-split: 338393f854eb5ba24761a22cd9316ff5cee4eab0
diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift
index cfdb20c..71fcab0 100644
--- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift
+++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift
@@ -1,602 +1,645 @@
+/*
+ * Copyright 2020 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
import Foundation
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()
-
- /// 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
- }
+
+ /// 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()
+
+ /// 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)
+ }
+
+ /// Gives a read access to the buffer's size
+ public var size: UOffset { _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] {
+ assert(finished, "Data shouldn't be called before finish()")
+ 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 { _bb }
+
+ /// Returns A sized Buffer from the readable bytes
+ public var sizedBuffer: ByteBuffer {
+ assert(finished, "Data shouldn't be called before finish()")
+ return ByteBuffer(memory: _bb.memory.advanced(by: _bb.reader), count: Int(_bb.size))
+ }
+
+ // 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")
}
- /// 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)
+ serializeDefaults = force
+ _bb = ByteBuffer(initialSize: Int(initialSize))
+ }
+
+ /// Clears the buffer and the builder from it's data
+ mutating public func clear() {
+ _minAlignment = 0
+ isNested = false
+ stringOffsetMap = [:]
+ _vtables = []
+ _vtableStorage.clear()
+ _bb.clear()
+ }
+
+ // MARK: - Create Tables
+
+ /// 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
+ mutating 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")
}
- /// Returns the written size of the buffer
- public var sizedByteArray: [UInt8] {
- assert(finished, "Data shouldn't be called before finish()")
- 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 }
-
- /// Returns A sized Buffer from the readable bytes
- public var sizedBuffer: ByteBuffer {
- assert(finished, "Data shouldn't be called before finish()")
- return ByteBuffer(memory: _bb.memory.advanced(by: _bb.reader), count: Int(_bb.size))
- }
-
- // 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
- mutating public func clear() {
- _minAlignment = 0
- isNested = false
- stringOffsetMap = [:]
- _vtables = []
- _vtableStorage.clear()
- _bb.clear()
+ }
+
+ /// 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
+ mutating 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
+ mutating 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) }
+ _vtableStorage.clear()
+ 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
+ mutating public func startTable(with numOfFields: Int) -> UOffset {
+ notNested()
+ isNested = true
+ _vtableStorage.start(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
+ mutating 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")
+ let _max = Int(_vtableStorage.maxOffset) &+ sizeofVoffset
+
+ _bb.fill(padding: _max)
+ _bb.write(
+ value: VOffset(tableObjectSize),
+ index: _bb.writerIndex &+ sizeofVoffset,
+ direct: true)
+ _bb.write(value: VOffset(_max), index: _bb.writerIndex, direct: true)
+
+ var itr = 0
+ while itr < _vtableStorage.writtenIndex {
+ let loaded = _vtableStorage.load(at: itr)
+ itr = itr &+ _vtableStorage.size
+ guard loaded.offset != 0 else { continue }
+ let _index = (_bb.writerIndex &+ Int(loaded.position))
+ _bb.write(value: VOffset(vTableOffset &- loaded.offset), index: _index, direct: true)
}
- // MARK: - Create Tables
-
- /// 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
- mutating 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
- mutating 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
- mutating 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) }
- _vtableStorage.clear()
- 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
- mutating public func startTable(with numOfFields: Int) -> UOffset {
- notNested()
- isNested = true
- _vtableStorage.start(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
- mutating 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")
- let _max = Int(_vtableStorage.maxOffset) &+ sizeofVoffset
-
- _bb.fill(padding: _max)
- _bb.write(value: VOffset(tableObjectSize), index: _bb.writerIndex &+ sizeofVoffset, direct: true)
- _bb.write(value: VOffset(_max), index: _bb.writerIndex, direct: true)
-
- var itr = 0
- while itr < _vtableStorage.writtenIndex {
- let loaded = _vtableStorage.load(at: itr)
- itr = itr &+ _vtableStorage.size
- guard loaded.offset != 0 else { continue }
- let _index = (_bb.writerIndex &+ Int(loaded.position))
- _bb.write(value: VOffset(vTableOffset &- loaded.offset), index: _index, direct: true)
- }
-
- _vtableStorage.clear()
- let vt_use = _bb.size
-
- var isAlreadyAdded: Int?
-
- let vt2 = _bb.memory.advanced(by: _bb.writerIndex)
- let len2 = vt2.load(fromByteOffset: 0, as: Int16.self)
+ _vtableStorage.clear()
+ let vt_use = _bb.size
- for table in _vtables {
- let position = _bb.capacity &- Int(table)
- let vt1 = _bb.memory.advanced(by: position)
- let len1 = _bb.read(def: Int16.self, position: position)
- if (len2 != len1 || 0 != memcmp(vt1, vt2, Int(len2))) { continue }
-
- isAlreadyAdded = Int(table)
- break
- }
-
- if let offset = isAlreadyAdded {
- let vTableOff = Int(vTableOffset)
- let space = _bb.capacity &- vTableOff
- _bb.write(value: Int32(offset &- vTableOff), index: space, direct: true)
- _bb.pop(_bb.capacity &- space)
- } else {
- _bb.write(value: Int32(vt_use &- vTableOffset), index: Int(vTableOffset))
- _vtables.append(_bb.size)
- }
- isNested = false
- return vTableOffset
- }
-
- // MARK: - Builds Buffer
-
- /// asserts to see if the object is not nested
- @usableFromInline mutating internal func notNested() {
- assert(!isNested, "Object serialization must not be nested")
- }
-
- /// Changes the minimuim alignment of the buffer
- /// - Parameter size: size of the current alignment
- @usableFromInline mutating internal 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
- @usableFromInline mutating internal 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
- @usableFromInline mutating internal func preAlign(len: Int, alignment: Int) {
- minAlignment(size: alignment)
- _bb.fill(padding: Int(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
- @usableFromInline mutating internal 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
- @usableFromInline mutating internal 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
- @usableFromInline mutating internal func track(offset: UOffset, at position: VOffset) {
- _vtableStorage.add(loc: FieldLoc(offset: offset, position: position))
+ var isAlreadyAdded: Int?
+
+ let vt2 = _bb.memory.advanced(by: _bb.writerIndex)
+ let len2 = vt2.load(fromByteOffset: 0, as: Int16.self)
+
+ for table in _vtables {
+ let position = _bb.capacity &- Int(table)
+ let vt1 = _bb.memory.advanced(by: position)
+ let len1 = _bb.read(def: Int16.self, position: position)
+ if len2 != len1 || 0 != memcmp(vt1, vt2, Int(len2)) { continue }
+
+ isAlreadyAdded = Int(table)
+ break
}
- // MARK: - Vectors
-
- /// Starts a vector of length and Element size
- mutating public func startVector(_ len: Int, elementSize: Int) {
- notNested()
- isNested = true
- preAlign(len: len &* elementSize, type: UOffset.self)
- preAlign(len: len &* elementSize, alignment: elementSize)
+ if let offset = isAlreadyAdded {
+ let vTableOff = Int(vTableOffset)
+ let space = _bb.capacity &- vTableOff
+ _bb.write(value: Int32(offset &- vTableOff), index: space, direct: true)
+ _bb.pop(_bb.capacity &- space)
+ } else {
+ _bb.write(value: Int32(vt_use &- vTableOffset), index: Int(vTableOffset))
+ _vtables.append(_bb.size)
}
-
- /// 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
- mutating 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
- mutating 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
- mutating 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
- mutating 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
- mutating public func createVector<T: Enum>(_ elements: [T], size: Int) -> Offset<UOffset> {
- let size = size
- startVector(size, elementSize: T.byteSize)
- for e in elements.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
- mutating 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
- mutating public func createVector<T>(ofOffsets offsets: [Offset<T>], len: Int) -> Offset<UOffset> {
- startVector(len, elementSize: MemoryLayout<Offset<T>>.size)
- for o in offsets.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
- mutating 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
- @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)
- for i in structs.reversed() {
- create(struct: i, type: T.self)
- }
- 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> {
- return Offset<UOffset>(offset: endVector(len: count))
- }
+ isNested = false
+ return vTableOffset
+ }
- // 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
- @available(*, deprecated, message: "0.9.0 will be removing the following method. Regenerate the code")
- @discardableResult
- mutating 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)
+ // MARK: - Builds Buffer
+
+ /// asserts to see if the object is not nested
+ @usableFromInline
+ mutating internal func notNested() {
+ assert(!isNested, "Object serialization must not be nested")
+ }
+
+ /// Changes the minimuim alignment of the buffer
+ /// - Parameter size: size of the current alignment
+ @usableFromInline
+ mutating internal func minAlignment(size: Int) {
+ if size > _minAlignment {
+ _minAlignment = 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)
+ }
+
+ /// 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
+ @usableFromInline
+ mutating internal 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
+ @usableFromInline
+ mutating internal func preAlign(len: Int, alignment: Int) {
+ minAlignment(size: alignment)
+ _bb.fill(padding: Int(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
+ @usableFromInline
+ mutating internal 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
+ @usableFromInline
+ mutating internal 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
+ @usableFromInline
+ mutating internal func track(offset: UOffset, at position: VOffset) {
+ _vtableStorage.add(loc: FieldLoc(offset: offset, position: position))
+ }
+
+ // MARK: - Vectors
+
+ /// Starts a vector of length and Element size
+ mutating 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
+ mutating 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
+ mutating public func createVector<T: Scalar>(_ elements: [T]) -> Offset<UOffset> {
+ 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
+ mutating 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
+ mutating public func createVector<T: Enum>(_ elements: [T]) -> Offset<UOffset> {
+ 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
+ mutating public func createVector<T: Enum>(_ elements: [T], size: Int) -> Offset<UOffset> {
+ let size = size
+ startVector(size, elementSize: T.byteSize)
+ for e in elements.reversed() {
+ _bb.push(value: e.value, len: T.byteSize)
}
-
- /// 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)
+ 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
+ mutating 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
+ mutating public func createVector<T>(ofOffsets offsets: [Offset<T>], len: Int) -> Offset<UOffset> {
+ startVector(len, elementSize: MemoryLayout<Offset<T>>.size)
+ for o in offsets.reversed() {
+ push(element: o)
}
-
- /// Ends the struct and returns the current buffer size
- /// - Returns: Offset of type UOffset
- @discardableResult
- public func endStruct() -> Offset<UOffset> {
- return Offset(offset: _bb.size)
+ 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
+ mutating public func createVector(ofStrings str: [String]) -> Offset<UOffset> {
+ var offsets: [Offset<String>] = []
+ for s in str {
+ offsets.append(create(string: s))
}
-
- /// 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)))
+ 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)
+ for i in structs.reversed() {
+ create(struct: i, type: T.self)
}
-
- // MARK: - Inserting Strings
-
- /// Insets a string into the buffer using UTF8
- /// - Parameter str: String to be serialized
- /// - returns: The strings offset in the buffer
- mutating public func create(string str: String?) -> Offset<String> {
- guard let str = str else { return Offset() }
- let len = str.utf8.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)
+ 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))
+ }
+
+ // 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
+ @available(
+ *,
+ deprecated,
+ message: "0.9.0 will be removing the following method. Regenerate the code")
+ @discardableResult
+ mutating 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)
+ }
+
+ /// 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
+ /// - Parameter str: String to be serialized
+ /// - returns: The strings offset in the buffer
+ mutating public func create(string str: String?) -> Offset<String> {
+ guard let str = str else { return Offset() }
+ let len = str.utf8.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
+ mutating public func createShared(string str: String?) -> Offset<String> {
+ guard let str = str else { return Offset() }
+ if let offset = stringOffsetMap[str] {
+ return offset
}
-
- /// 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
- mutating public func createShared(string str: String?) -> Offset<String> {
- guard let str = str else { return Offset() }
- 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
- mutating public func add<T>(offset: Offset<T>, at position: VOffset) {
- if offset.isEmpty { 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
- mutating 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
- mutating public func add<T: Scalar>(element: T, def: T, at position: VOffset) {
- if (element == def && !serializeDefaults) { return }
- track(offset: push(element: element), at: position)
- }
-
- /// Adds a value into the buffer of type optional Scalar
- /// - Parameters:
- /// - element: Optional element of type scalar
- /// - position: The predefined position of the element
- mutating public func add<T: Scalar>(element: T?, at position: VOffset) {
- guard let element = element else { return }
- track(offset: push(element: element), at: position)
- }
-
- /// Pushes the values into the buffer
- /// - Parameter element: Element to insert
- /// - returns: Postion of the Element
- @discardableResult
- mutating public func push<T: Scalar>(element: T) -> UOffset {
- let size = MemoryLayout<T>.size
- preAlign(len: size,
- alignment: size)
- _bb.push(value: element, len: size)
- return _bb.size
- }
-
+ 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
+ mutating public func add<T>(offset: Offset<T>, at position: VOffset) {
+ if offset.isEmpty { 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
+ mutating 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
+ mutating public func add<T: Scalar>(element: T, def: T, at position: VOffset) {
+ if element == def && !serializeDefaults { return }
+ track(offset: push(element: element), at: position)
+ }
+
+ /// Adds a value into the buffer of type optional Scalar
+ /// - Parameters:
+ /// - element: Optional element of type scalar
+ /// - position: The predefined position of the element
+ mutating public func add<T: Scalar>(element: T?, at position: VOffset) {
+ guard let element = element else { return }
+ track(offset: push(element: element), at: position)
+ }
+
+ /// Pushes the values into the buffer
+ /// - Parameter element: Element to insert
+ /// - returns: Postion of the Element
+ @discardableResult
+ mutating public func push<T: Scalar>(element: T) -> UOffset {
+ let size = MemoryLayout<T>.size
+ preAlign(
+ len: size,
+ alignment: size)
+ _bb.push(value: element, len: size)
+ return _bb.size
+ }
+
}
extension FlatBufferBuilder: CustomDebugStringConvertible {
-
- public var debugDescription: String {
- """
- buffer debug:
- \(_bb)
- builder debug:
- { finished: \(finished), serializeDefaults: \(serializeDefaults), isNested: \(isNested) }
- """
+
+ public var debugDescription: String {
+ """
+ buffer debug:
+ \(_bb)
+ builder debug:
+ { finished: \(finished), serializeDefaults: \(serializeDefaults), isNested: \(isNested) }
+ """
+ }
+
+ /// VTableStorage is a class to contain the VTable buffer that would be serialized into buffer
+ @usableFromInline
+ internal class VTableStorage {
+ /// Memory check since deallocating each time we want to clear would be expensive
+ /// and memory leaks would happen if we dont deallocate the first allocated memory.
+ /// memory is promised to be available before adding `FieldLoc`
+ private var memoryInUse = false
+ /// Size of FieldLoc in memory
+ let size = MemoryLayout<FieldLoc>.stride
+ /// Memeory buffer
+ var memory: UnsafeMutableRawBufferPointer!
+ /// Capacity of the current buffer
+ var capacity: Int = 0
+ /// Maximuim offset written to the class
+ var maxOffset: VOffset = 0
+ /// number of fields written into the buffer
+ var numOfFields: Int = 0
+ /// Last written Index
+ var writtenIndex: Int = 0
+ /// the amount of added elements into the buffer
+ var addedElements: Int { capacity - (numOfFields &* size) }
+
+ /// Creates the memory to store the buffer in
+ init() {
+ memory = UnsafeMutableRawBufferPointer.allocate(byteCount: 0, alignment: 0)
}
- /// VTableStorage is a class to contain the VTable buffer that would be serialized into buffer
- @usableFromInline internal class VTableStorage {
- /// Memory check since deallocating each time we want to clear would be expensive
- /// and memory leaks would happen if we dont deallocate the first allocated memory.
- /// memory is promised to be available before adding `FieldLoc`
- private var memoryInUse = false
- /// Size of FieldLoc in memory
- let size = MemoryLayout<FieldLoc>.stride
- /// Memeory buffer
- var memory: UnsafeMutableRawBufferPointer!
- /// Capacity of the current buffer
- var capacity: Int = 0
- /// Maximuim offset written to the class
- var maxOffset: VOffset = 0
- /// number of fields written into the buffer
- var numOfFields: Int = 0
- /// Last written Index
- var writtenIndex: Int = 0
- /// the amount of added elements into the buffer
- var addedElements: Int { return capacity - (numOfFields &* size) }
-
- /// Creates the memory to store the buffer in
- init() {
- memory = UnsafeMutableRawBufferPointer.allocate(byteCount: 0, alignment: 0)
- }
-
- deinit {
- memory.deallocate()
- }
-
- /// Builds a buffer with byte count of fieldloc.size * count of field numbers
- /// - Parameter count: number of fields to be written
- func start(count: Int) {
- assert(count >= 0, "number of fields should NOT be negative")
- let capacity = count &* size
- ensure(space: capacity)
- }
-
- /// Adds a FieldLoc into the buffer, which would track how many have been written,
- /// and max offset
- /// - Parameter loc: Location of encoded element
- func add(loc: FieldLoc) {
- memory.baseAddress?.advanced(by: writtenIndex).storeBytes(of: loc, as: FieldLoc.self)
- writtenIndex = writtenIndex &+ size
- numOfFields = numOfFields &+ 1
- maxOffset = max(loc.position, maxOffset)
- }
-
- /// Clears the data stored related to the encoded buffer
- func clear() {
- maxOffset = 0
- numOfFields = 0
- writtenIndex = 0
- }
-
- /// Ensure that the buffer has enough space instead of recreating the buffer each time.
- /// - Parameter space: space required for the new vtable
- func ensure(space: Int) {
- guard space &+ writtenIndex > capacity else { return }
- memory.deallocate()
- memory = UnsafeMutableRawBufferPointer.allocate(byteCount: space, alignment: size)
- capacity = space
- }
+ deinit {
+ memory.deallocate()
+ }
- /// Loads an object of type `FieldLoc` from buffer memory
- /// - Parameter index: index of element
- /// - Returns: a FieldLoc at index
- func load(at index: Int) -> FieldLoc {
- return memory.load(fromByteOffset: index, as: FieldLoc.self)
- }
-
+ /// Builds a buffer with byte count of fieldloc.size * count of field numbers
+ /// - Parameter count: number of fields to be written
+ func start(count: Int) {
+ assert(count >= 0, "number of fields should NOT be negative")
+ let capacity = count &* size
+ ensure(space: capacity)
}
-
- internal struct FieldLoc {
- var offset: UOffset
- var position: VOffset
+
+ /// Adds a FieldLoc into the buffer, which would track how many have been written,
+ /// and max offset
+ /// - Parameter loc: Location of encoded element
+ func add(loc: FieldLoc) {
+ memory.baseAddress?.advanced(by: writtenIndex).storeBytes(of: loc, as: FieldLoc.self)
+ writtenIndex = writtenIndex &+ size
+ numOfFields = numOfFields &+ 1
+ maxOffset = max(loc.position, maxOffset)
}
+ /// Clears the data stored related to the encoded buffer
+ func clear() {
+ maxOffset = 0
+ numOfFields = 0
+ writtenIndex = 0
+ }
+
+ /// Ensure that the buffer has enough space instead of recreating the buffer each time.
+ /// - Parameter space: space required for the new vtable
+ func ensure(space: Int) {
+ guard space &+ writtenIndex > capacity else { return }
+ memory.deallocate()
+ memory = UnsafeMutableRawBufferPointer.allocate(byteCount: space, alignment: size)
+ capacity = space
+ }
+
+ /// Loads an object of type `FieldLoc` from buffer memory
+ /// - Parameter index: index of element
+ /// - Returns: a FieldLoc at index
+ func load(at index: Int) -> FieldLoc {
+ memory.load(fromByteOffset: index, as: FieldLoc.self)
+ }
+
+ }
+
+ internal struct FieldLoc {
+ var offset: UOffset
+ var position: VOffset
+ }
+
}