Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame^] | 1 | import Foundation |
| 2 | |
| 3 | public struct FlatBufferBuilder { |
| 4 | |
| 5 | /// Storage for the Vtables used in the buffer are stored in here, so they would be written later in EndTable |
| 6 | @usableFromInline internal var _vtableStorage = VTableStorage() |
| 7 | |
| 8 | /// Reference Vtables that were already written to the buffer |
| 9 | private var _vtables: [UOffset] = [] |
| 10 | /// Flatbuffer data will be written into |
| 11 | private var _bb: ByteBuffer |
| 12 | /// A check if the buffer is being written into by a different table |
| 13 | private var isNested = false |
| 14 | /// Dictonary that stores a map of all the strings that were written to the buffer |
| 15 | private var stringOffsetMap: [String: Offset<String>] = [:] |
| 16 | /// A check to see if finish(::) was ever called to retreive data object |
| 17 | private var finished = false |
| 18 | /// A check to see if the buffer should serialize Default values |
| 19 | private var serializeDefaults: Bool |
| 20 | |
| 21 | /// Current alignment for the buffer |
| 22 | var _minAlignment: Int = 0 { |
| 23 | didSet { |
| 24 | _bb.alignment = _minAlignment |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | /// Gives a read access to the buffer's size |
| 29 | public var size: UOffset { return _bb.size } |
| 30 | /// Data representation of the buffer |
| 31 | public var data: Data { |
| 32 | assert(finished, "Data shouldn't be called before finish()") |
| 33 | return Data(bytes: _bb.memory.advanced(by: _bb.writerIndex), |
| 34 | count: _bb.capacity &- _bb.writerIndex) |
| 35 | } |
| 36 | /// Get's the fully sized buffer stored in memory |
| 37 | public var fullSizedByteArray: [UInt8] { |
| 38 | let ptr = UnsafeBufferPointer(start: _bb.memory.assumingMemoryBound(to: UInt8.self), |
| 39 | count: _bb.capacity) |
| 40 | return Array(ptr) |
| 41 | } |
| 42 | /// Returns the written size of the buffer |
| 43 | public var sizedByteArray: [UInt8] { |
| 44 | assert(finished, "Data shouldn't be called before finish()") |
| 45 | let cp = _bb.capacity &- _bb.writerIndex |
| 46 | let start = _bb.memory.advanced(by: _bb.writerIndex) |
| 47 | .bindMemory(to: UInt8.self, capacity: cp) |
| 48 | |
| 49 | let ptr = UnsafeBufferPointer(start: start, count: cp) |
| 50 | return Array(ptr) |
| 51 | } |
| 52 | /// Returns the buffer |
| 53 | public var buffer: ByteBuffer { return _bb } |
| 54 | |
| 55 | /// Returns A sized Buffer from the readable bytes |
| 56 | public var sizedBuffer: ByteBuffer { |
| 57 | assert(finished, "Data shouldn't be called before finish()") |
| 58 | return ByteBuffer(memory: _bb.memory.advanced(by: _bb.reader), count: Int(_bb.size)) |
| 59 | } |
| 60 | |
| 61 | // MARK: - Init |
| 62 | |
| 63 | /// initialize the buffer with a size |
| 64 | /// - Parameters: |
| 65 | /// - initialSize: Initial size for the buffer |
| 66 | /// - force: Allows default to be serialized into the buffer |
| 67 | public init(initialSize: Int32 = 1024, serializeDefaults force: Bool = false) { |
| 68 | assert(initialSize > 0, "Size should be greater than zero!") |
| 69 | guard isLitteEndian else { |
| 70 | fatalError("Reading/Writing a buffer in big endian machine is not supported on swift") |
| 71 | } |
| 72 | serializeDefaults = force |
| 73 | _bb = ByteBuffer(initialSize: Int(initialSize)) |
| 74 | } |
| 75 | |
| 76 | /// Clears the buffer and the builder from it's data |
| 77 | mutating public func clear() { |
| 78 | _minAlignment = 0 |
| 79 | isNested = false |
| 80 | stringOffsetMap = [:] |
| 81 | _vtables = [] |
| 82 | _vtableStorage.clear() |
| 83 | _bb.clear() |
| 84 | } |
| 85 | |
| 86 | // MARK: - Create Tables |
| 87 | |
| 88 | /// Checks if the required fields were serialized into the buffer |
| 89 | /// - Parameters: |
| 90 | /// - table: offset for the table |
| 91 | /// - fields: Array of all the important fields to be serialized |
| 92 | mutating public func require(table: Offset<UOffset>, fields: [Int32]) { |
| 93 | for field in fields { |
| 94 | let start = _bb.capacity &- Int(table.o) |
| 95 | let startTable = start &- Int(_bb.read(def: Int32.self, position: start)) |
| 96 | let isOkay = _bb.read(def: VOffset.self, position: startTable &+ Int(field)) != 0 |
| 97 | assert(isOkay, "Flatbuffers requires the following field") |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | /// Finished the buffer by adding the file id and then calling finish |
| 102 | /// - Parameters: |
| 103 | /// - offset: Offset of the table |
| 104 | /// - fileId: Takes the fileId |
| 105 | /// - prefix: if false it wont add the size of the buffer |
| 106 | mutating public func finish<T>(offset: Offset<T>, fileId: String, addPrefix prefix: Bool = false) { |
| 107 | let size = MemoryLayout<UOffset>.size |
| 108 | preAlign(len: size &+ (prefix ? size : 0) &+ FileIdLength, alignment: _minAlignment) |
| 109 | assert(fileId.count == FileIdLength, "Flatbuffers requires file id to be 4") |
| 110 | _bb.push(string: fileId, len: 4) |
| 111 | finish(offset: offset, addPrefix: prefix) |
| 112 | } |
| 113 | |
| 114 | /// Finished the buffer by adding the file id, offset, and prefix to it. |
| 115 | /// - Parameters: |
| 116 | /// - offset: Offset of the table |
| 117 | /// - prefix: if false it wont add the size of the buffer |
| 118 | mutating public func finish<T>(offset: Offset<T>, addPrefix prefix: Bool = false) { |
| 119 | notNested() |
| 120 | let size = MemoryLayout<UOffset>.size |
| 121 | preAlign(len: size &+ (prefix ? size : 0), alignment: _minAlignment) |
| 122 | push(element: refer(to: offset.o)) |
| 123 | if prefix { push(element: _bb.size) } |
| 124 | _vtableStorage.clear() |
| 125 | finished = true |
| 126 | } |
| 127 | |
| 128 | /// starttable will let the builder know, that a new object is being serialized. |
| 129 | /// |
| 130 | /// The function will fatalerror if called while there is another object being serialized |
| 131 | /// - Parameter numOfFields: Number of elements to be written to the buffer |
| 132 | mutating public func startTable(with numOfFields: Int) -> UOffset { |
| 133 | notNested() |
| 134 | isNested = true |
| 135 | _vtableStorage.start(count: numOfFields) |
| 136 | return _bb.size |
| 137 | } |
| 138 | |
| 139 | /// Endtable will let the builder know that the object that's written to it is completed |
| 140 | /// |
| 141 | /// This would be called after all the elements are serialized, it will add the vtable into the buffer. |
| 142 | /// it will fatalError in case the object is called without starttable, or the object has exceeded the limit of |
| 143 | /// 2GB, |
| 144 | /// - Parameter startOffset:Start point of the object written |
| 145 | /// - returns: The root of the table |
| 146 | mutating public func endTable(at startOffset: UOffset) -> UOffset { |
| 147 | assert(isNested, "Calling endtable without calling starttable") |
| 148 | let sizeofVoffset = MemoryLayout<VOffset>.size |
| 149 | let vTableOffset = push(element: SOffset(0)) |
| 150 | |
| 151 | let tableObjectSize = vTableOffset &- startOffset |
| 152 | assert(tableObjectSize < 0x10000, "Buffer can't grow beyond 2 Gigabytes") |
| 153 | let _max = Int(_vtableStorage.maxOffset) &+ sizeofVoffset |
| 154 | |
| 155 | _bb.fill(padding: _max) |
| 156 | _bb.write(value: VOffset(tableObjectSize), index: _bb.writerIndex &+ sizeofVoffset, direct: true) |
| 157 | _bb.write(value: VOffset(_max), index: _bb.writerIndex, direct: true) |
| 158 | |
| 159 | var itr = 0 |
| 160 | while itr < _vtableStorage.writtenIndex { |
| 161 | let loaded = _vtableStorage.load(at: itr) |
| 162 | itr = itr &+ _vtableStorage.size |
| 163 | guard loaded.offset != 0 else { continue } |
| 164 | let _index = (_bb.writerIndex &+ Int(loaded.position)) |
| 165 | _bb.write(value: VOffset(vTableOffset &- loaded.offset), index: _index, direct: true) |
| 166 | } |
| 167 | |
| 168 | _vtableStorage.clear() |
| 169 | let vt_use = _bb.size |
| 170 | |
| 171 | var isAlreadyAdded: Int? |
| 172 | |
| 173 | let vt2 = _bb.memory.advanced(by: _bb.writerIndex) |
| 174 | let len2 = vt2.load(fromByteOffset: 0, as: Int16.self) |
| 175 | |
| 176 | for table in _vtables { |
| 177 | let position = _bb.capacity &- Int(table) |
| 178 | let vt1 = _bb.memory.advanced(by: position) |
| 179 | let len1 = _bb.read(def: Int16.self, position: position) |
| 180 | if (len2 != len1 || 0 != memcmp(vt1, vt2, Int(len2))) { continue } |
| 181 | |
| 182 | isAlreadyAdded = Int(table) |
| 183 | break |
| 184 | } |
| 185 | |
| 186 | if let offset = isAlreadyAdded { |
| 187 | let vTableOff = Int(vTableOffset) |
| 188 | let space = _bb.capacity &- vTableOff |
| 189 | _bb.write(value: Int32(offset &- vTableOff), index: space, direct: true) |
| 190 | _bb.pop(_bb.capacity &- space) |
| 191 | } else { |
| 192 | _bb.write(value: Int32(vt_use &- vTableOffset), index: Int(vTableOffset)) |
| 193 | _vtables.append(_bb.size) |
| 194 | } |
| 195 | isNested = false |
| 196 | return vTableOffset |
| 197 | } |
| 198 | |
| 199 | // MARK: - Builds Buffer |
| 200 | |
| 201 | /// asserts to see if the object is not nested |
| 202 | @usableFromInline mutating internal func notNested() { |
| 203 | assert(!isNested, "Object serialization must not be nested") |
| 204 | } |
| 205 | |
| 206 | /// Changes the minimuim alignment of the buffer |
| 207 | /// - Parameter size: size of the current alignment |
| 208 | @usableFromInline mutating internal func minAlignment(size: Int) { |
| 209 | if size > _minAlignment { |
| 210 | _minAlignment = size |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | /// Gets the padding for the current element |
| 215 | /// - Parameters: |
| 216 | /// - bufSize: Current size of the buffer + the offset of the object to be written |
| 217 | /// - elementSize: Element size |
| 218 | @usableFromInline mutating internal func padding(bufSize: UInt32, elementSize: UInt32) -> UInt32 { |
| 219 | ((~bufSize) &+ 1) & (elementSize - 1) |
| 220 | } |
| 221 | |
| 222 | /// Prealigns the buffer before writting a new object into the buffer |
| 223 | /// - Parameters: |
| 224 | /// - len:Length of the object |
| 225 | /// - alignment: Alignment type |
| 226 | @usableFromInline mutating internal func preAlign(len: Int, alignment: Int) { |
| 227 | minAlignment(size: alignment) |
| 228 | _bb.fill(padding: Int(padding(bufSize: _bb.size &+ UOffset(len), elementSize: UOffset(alignment)))) |
| 229 | } |
| 230 | |
| 231 | /// Prealigns the buffer before writting a new object into the buffer |
| 232 | /// - Parameters: |
| 233 | /// - len: Length of the object |
| 234 | /// - type: Type of the object to be written |
| 235 | @usableFromInline mutating internal func preAlign<T: Scalar>(len: Int, type: T.Type) { |
| 236 | preAlign(len: len, alignment: MemoryLayout<T>.size) |
| 237 | } |
| 238 | |
| 239 | /// Refers to an object that's written in the buffer |
| 240 | /// - Parameter off: the objects index value |
| 241 | @usableFromInline mutating internal func refer(to off: UOffset) -> UOffset { |
| 242 | let size = MemoryLayout<UOffset>.size |
| 243 | preAlign(len: size, alignment: size) |
| 244 | return _bb.size &- off &+ UInt32(size) |
| 245 | } |
| 246 | |
| 247 | /// Tracks the elements written into the buffer |
| 248 | /// - Parameters: |
| 249 | /// - offset: The offset of the element witten |
| 250 | /// - position: The position of the element |
| 251 | @usableFromInline mutating internal func track(offset: UOffset, at position: VOffset) { |
| 252 | _vtableStorage.add(loc: FieldLoc(offset: offset, position: position)) |
| 253 | } |
| 254 | |
| 255 | // MARK: - Vectors |
| 256 | |
| 257 | /// Starts a vector of length and Element size |
| 258 | mutating public func startVector(_ len: Int, elementSize: Int) { |
| 259 | notNested() |
| 260 | isNested = true |
| 261 | preAlign(len: len &* elementSize, type: UOffset.self) |
| 262 | preAlign(len: len &* elementSize, alignment: elementSize) |
| 263 | } |
| 264 | |
| 265 | /// Ends the vector of at length |
| 266 | /// |
| 267 | /// The current function will fatalError if startVector is called before serializing the vector |
| 268 | /// - Parameter len: Length of the buffer |
| 269 | mutating public func endVector(len: Int) -> UOffset { |
| 270 | assert(isNested, "Calling endVector without calling startVector") |
| 271 | isNested = false |
| 272 | return push(element: Int32(len)) |
| 273 | } |
| 274 | |
| 275 | /// Creates a vector of type Scalar in the buffer |
| 276 | /// - Parameter elements: elements to be written into the buffer |
| 277 | /// - returns: Offset of the vector |
| 278 | mutating public func createVector<T: Scalar>(_ elements: [T]) -> Offset<UOffset> { |
| 279 | return createVector(elements, size: elements.count) |
| 280 | } |
| 281 | |
| 282 | /// Creates a vector of type Scalar in the buffer |
| 283 | /// - Parameter elements: Elements to be written into the buffer |
| 284 | /// - Parameter size: Count of elements |
| 285 | /// - returns: Offset of the vector |
| 286 | mutating public func createVector<T: Scalar>(_ elements: [T], size: Int) -> Offset<UOffset> { |
| 287 | let size = size |
| 288 | startVector(size, elementSize: MemoryLayout<T>.size) |
| 289 | _bb.push(elements: elements) |
| 290 | return Offset(offset: endVector(len: size)) |
| 291 | } |
| 292 | |
| 293 | /// Creates a vector of type Enums in the buffer |
| 294 | /// - Parameter elements: elements to be written into the buffer |
| 295 | /// - returns: Offset of the vector |
| 296 | mutating public func createVector<T: Enum>(_ elements: [T]) -> Offset<UOffset> { |
| 297 | return createVector(elements, size: elements.count) |
| 298 | } |
| 299 | |
| 300 | /// Creates a vector of type Enums in the buffer |
| 301 | /// - Parameter elements: Elements to be written into the buffer |
| 302 | /// - Parameter size: Count of elements |
| 303 | /// - returns: Offset of the vector |
| 304 | mutating public func createVector<T: Enum>(_ elements: [T], size: Int) -> Offset<UOffset> { |
| 305 | let size = size |
| 306 | startVector(size, elementSize: T.byteSize) |
| 307 | for e in elements.reversed() { |
| 308 | _bb.push(value: e.value, len: T.byteSize) |
| 309 | } |
| 310 | return Offset(offset: endVector(len: size)) |
| 311 | } |
| 312 | |
| 313 | /// Creates a vector of type Offsets in the buffer |
| 314 | /// - Parameter offsets:Array of offsets of type T |
| 315 | /// - returns: Offset of the vector |
| 316 | mutating public func createVector<T>(ofOffsets offsets: [Offset<T>]) -> Offset<UOffset> { |
| 317 | createVector(ofOffsets: offsets, len: offsets.count) |
| 318 | } |
| 319 | |
| 320 | /// Creates a vector of type Offsets in the buffer |
| 321 | /// - Parameter elements: Array of offsets of type T |
| 322 | /// - Parameter size: Count of elements |
| 323 | /// - returns: Offset of the vector |
| 324 | mutating public func createVector<T>(ofOffsets offsets: [Offset<T>], len: Int) -> Offset<UOffset> { |
| 325 | startVector(len, elementSize: MemoryLayout<Offset<T>>.size) |
| 326 | for o in offsets.reversed() { |
| 327 | push(element: o) |
| 328 | } |
| 329 | return Offset(offset: endVector(len: len)) |
| 330 | } |
| 331 | |
| 332 | /// Creates a vector of Strings |
| 333 | /// - Parameter str: a vector of strings that will be written into the buffer |
| 334 | /// - returns: Offset of the vector |
| 335 | mutating public func createVector(ofStrings str: [String]) -> Offset<UOffset> { |
| 336 | var offsets: [Offset<String>] = [] |
| 337 | for s in str { |
| 338 | offsets.append(create(string: s)) |
| 339 | } |
| 340 | return createVector(ofOffsets: offsets) |
| 341 | } |
| 342 | |
| 343 | /// Creates a vector of Flatbuffer structs. |
| 344 | /// |
| 345 | /// The function takes a Type to know what size it is, and alignment |
| 346 | /// - Parameters: |
| 347 | /// - structs: An array of UnsafeMutableRawPointer |
| 348 | /// - type: Type of the struct being written |
| 349 | /// - returns: Offset of the vector |
| 350 | @available(*, deprecated, message: "0.9.0 will be removing the following method. Regenerate the code") |
| 351 | mutating public func createVector<T: Readable>(structs: [UnsafeMutableRawPointer], |
| 352 | type: T.Type) -> Offset<UOffset> { |
| 353 | startVector(structs.count &* T.size, elementSize: T.alignment) |
| 354 | for i in structs.reversed() { |
| 355 | create(struct: i, type: T.self) |
| 356 | } |
| 357 | return Offset(offset: endVector(len: structs.count)) |
| 358 | } |
| 359 | |
| 360 | /// Starts a vector of struct that considers the size and alignment of the struct |
| 361 | /// - Parameters: |
| 362 | /// - count: number of elements to be written |
| 363 | /// - size: size of struct |
| 364 | /// - alignment: alignment of the struct |
| 365 | mutating public func startVectorOfStructs(count: Int, size: Int, alignment: Int) { |
| 366 | startVector(count &* size, elementSize: alignment) |
| 367 | } |
| 368 | |
| 369 | /// Ends the vector of structs and writtens the current offset |
| 370 | /// - Parameter count: number of written elements |
| 371 | /// - Returns: Offset of type UOffset |
| 372 | mutating public func endVectorOfStructs(count: Int) -> Offset<UOffset> { |
| 373 | return Offset<UOffset>(offset: endVector(len: count)) |
| 374 | } |
| 375 | |
| 376 | // MARK: - Inserting Structs |
| 377 | |
| 378 | /// Writes a Flatbuffer struct into the buffer |
| 379 | /// - Parameters: |
| 380 | /// - s: Flatbuffer struct |
| 381 | /// - type: Type of the element to be serialized |
| 382 | /// - returns: Offset of the Object |
| 383 | @available(*, deprecated, message: "0.9.0 will be removing the following method. Regenerate the code") |
| 384 | @discardableResult |
| 385 | mutating public func create<T: Readable>(struct s: UnsafeMutableRawPointer, |
| 386 | type: T.Type) -> Offset<UOffset> { |
| 387 | let size = T.size |
| 388 | preAlign(len: size, alignment: T.alignment) |
| 389 | _bb.push(struct: s, size: size) |
| 390 | return Offset(offset: _bb.size) |
| 391 | } |
| 392 | |
| 393 | /// prepares the ByteBuffer to receive a struct of size and alignment |
| 394 | /// - Parameters: |
| 395 | /// - size: size of written struct |
| 396 | /// - alignment: alignment of written struct |
| 397 | mutating public func createStructOf(size: Int, alignment: Int) { |
| 398 | preAlign(len: size, alignment: alignment) |
| 399 | _bb.prepareBufferToReceiveStruct(of: size) |
| 400 | } |
| 401 | |
| 402 | /// Adds scalars front to back instead of the default behavior of the normal add |
| 403 | /// - Parameters: |
| 404 | /// - v: element of type Scalar |
| 405 | /// - postion: position relative to the `writerIndex` |
| 406 | mutating public func reverseAdd<T: Scalar>(v: T, postion: Int) { |
| 407 | _bb.reversePush(value: v, |
| 408 | position: postion, |
| 409 | len: MemoryLayout<T>.size) |
| 410 | } |
| 411 | |
| 412 | /// Ends the struct and returns the current buffer size |
| 413 | /// - Returns: Offset of type UOffset |
| 414 | @discardableResult |
| 415 | public func endStruct() -> Offset<UOffset> { |
| 416 | return Offset(offset: _bb.size) |
| 417 | } |
| 418 | |
| 419 | /// Adds the offset of a struct into the vTable |
| 420 | /// |
| 421 | /// The function fatalErrors if we pass an offset that is out of range |
| 422 | /// - Parameter o: offset |
| 423 | mutating public func add(structOffset o: VOffset) { |
| 424 | _vtableStorage.add(loc: FieldLoc(offset: _bb.size, position: VOffset(o))) |
| 425 | } |
| 426 | |
| 427 | // MARK: - Inserting Strings |
| 428 | |
| 429 | /// Insets a string into the buffer using UTF8 |
| 430 | /// - Parameter str: String to be serialized |
| 431 | /// - returns: The strings offset in the buffer |
| 432 | mutating public func create(string str: String?) -> Offset<String> { |
| 433 | guard let str = str else { return Offset() } |
| 434 | let len = str.utf8.count |
| 435 | notNested() |
| 436 | preAlign(len: len &+ 1, type: UOffset.self) |
| 437 | _bb.fill(padding: 1) |
| 438 | _bb.push(string: str, len: len) |
| 439 | push(element: UOffset(len)) |
| 440 | return Offset(offset: _bb.size) |
| 441 | } |
| 442 | |
| 443 | /// Inserts a shared string to the buffer |
| 444 | /// |
| 445 | /// The function checks the stringOffsetmap if it's seen a similar string before |
| 446 | /// - Parameter str: String to be serialized |
| 447 | /// - returns: The strings offset in the buffer |
| 448 | mutating public func createShared(string str: String?) -> Offset<String> { |
| 449 | guard let str = str else { return Offset() } |
| 450 | if let offset = stringOffsetMap[str] { |
| 451 | return offset |
| 452 | } |
| 453 | let offset = create(string: str) |
| 454 | stringOffsetMap[str] = offset |
| 455 | return offset |
| 456 | } |
| 457 | |
| 458 | // MARK: - Inseting offsets |
| 459 | |
| 460 | /// Adds the offset of an object into the buffer |
| 461 | /// - Parameters: |
| 462 | /// - offset: Offset of another object to be written |
| 463 | /// - position: The predefined position of the object |
| 464 | mutating public func add<T>(offset: Offset<T>, at position: VOffset) { |
| 465 | if offset.isEmpty { return } |
| 466 | add(element: refer(to: offset.o), def: 0, at: position) |
| 467 | } |
| 468 | |
| 469 | /// Pushes a value of type offset into the buffer |
| 470 | /// - Parameter o: Offset |
| 471 | /// - returns: Position of the offset |
| 472 | @discardableResult |
| 473 | mutating public func push<T>(element o: Offset<T>) -> UOffset { |
| 474 | push(element: refer(to: o.o)) |
| 475 | } |
| 476 | |
| 477 | // MARK: - Inserting Scalars to Buffer |
| 478 | |
| 479 | /// Adds a value into the buffer of type Scalar |
| 480 | /// |
| 481 | /// - Parameters: |
| 482 | /// - element: Element to insert |
| 483 | /// - def: Default value for that element |
| 484 | /// - position: The predefined position of the element |
| 485 | mutating public func add<T: Scalar>(element: T, def: T, at position: VOffset) { |
| 486 | if (element == def && !serializeDefaults) { return } |
| 487 | track(offset: push(element: element), at: position) |
| 488 | } |
| 489 | |
| 490 | /// Adds a value into the buffer of type optional Scalar |
| 491 | /// - Parameters: |
| 492 | /// - element: Optional element of type scalar |
| 493 | /// - position: The predefined position of the element |
| 494 | mutating public func add<T: Scalar>(element: T?, at position: VOffset) { |
| 495 | guard let element = element else { return } |
| 496 | track(offset: push(element: element), at: position) |
| 497 | } |
| 498 | |
| 499 | /// Pushes the values into the buffer |
| 500 | /// - Parameter element: Element to insert |
| 501 | /// - returns: Postion of the Element |
| 502 | @discardableResult |
| 503 | mutating public func push<T: Scalar>(element: T) -> UOffset { |
| 504 | let size = MemoryLayout<T>.size |
| 505 | preAlign(len: size, |
| 506 | alignment: size) |
| 507 | _bb.push(value: element, len: size) |
| 508 | return _bb.size |
| 509 | } |
| 510 | |
| 511 | } |
| 512 | |
| 513 | extension FlatBufferBuilder: CustomDebugStringConvertible { |
| 514 | |
| 515 | public var debugDescription: String { |
| 516 | """ |
| 517 | buffer debug: |
| 518 | \(_bb) |
| 519 | builder debug: |
| 520 | { finished: \(finished), serializeDefaults: \(serializeDefaults), isNested: \(isNested) } |
| 521 | """ |
| 522 | } |
| 523 | |
| 524 | /// VTableStorage is a class to contain the VTable buffer that would be serialized into buffer |
| 525 | @usableFromInline internal class VTableStorage { |
| 526 | /// Memory check since deallocating each time we want to clear would be expensive |
| 527 | /// and memory leaks would happen if we dont deallocate the first allocated memory. |
| 528 | /// memory is promised to be available before adding `FieldLoc` |
| 529 | private var memoryInUse = false |
| 530 | /// Size of FieldLoc in memory |
| 531 | let size = MemoryLayout<FieldLoc>.stride |
| 532 | /// Memeory buffer |
| 533 | var memory: UnsafeMutableRawBufferPointer! |
| 534 | /// Capacity of the current buffer |
| 535 | var capacity: Int = 0 |
| 536 | /// Maximuim offset written to the class |
| 537 | var maxOffset: VOffset = 0 |
| 538 | /// number of fields written into the buffer |
| 539 | var numOfFields: Int = 0 |
| 540 | /// Last written Index |
| 541 | var writtenIndex: Int = 0 |
| 542 | /// the amount of added elements into the buffer |
| 543 | var addedElements: Int { return capacity - (numOfFields &* size) } |
| 544 | |
| 545 | /// Creates the memory to store the buffer in |
| 546 | init() { |
| 547 | memory = UnsafeMutableRawBufferPointer.allocate(byteCount: 0, alignment: 0) |
| 548 | } |
| 549 | |
| 550 | deinit { |
| 551 | memory.deallocate() |
| 552 | } |
| 553 | |
| 554 | /// Builds a buffer with byte count of fieldloc.size * count of field numbers |
| 555 | /// - Parameter count: number of fields to be written |
| 556 | func start(count: Int) { |
| 557 | assert(count >= 0, "number of fields should NOT be negative") |
| 558 | let capacity = count &* size |
| 559 | ensure(space: capacity) |
| 560 | } |
| 561 | |
| 562 | /// Adds a FieldLoc into the buffer, which would track how many have been written, |
| 563 | /// and max offset |
| 564 | /// - Parameter loc: Location of encoded element |
| 565 | func add(loc: FieldLoc) { |
| 566 | memory.baseAddress?.advanced(by: writtenIndex).storeBytes(of: loc, as: FieldLoc.self) |
| 567 | writtenIndex = writtenIndex &+ size |
| 568 | numOfFields = numOfFields &+ 1 |
| 569 | maxOffset = max(loc.position, maxOffset) |
| 570 | } |
| 571 | |
| 572 | /// Clears the data stored related to the encoded buffer |
| 573 | func clear() { |
| 574 | maxOffset = 0 |
| 575 | numOfFields = 0 |
| 576 | writtenIndex = 0 |
| 577 | } |
| 578 | |
| 579 | /// Ensure that the buffer has enough space instead of recreating the buffer each time. |
| 580 | /// - Parameter space: space required for the new vtable |
| 581 | func ensure(space: Int) { |
| 582 | guard space &+ writtenIndex > capacity else { return } |
| 583 | memory.deallocate() |
| 584 | memory = UnsafeMutableRawBufferPointer.allocate(byteCount: space, alignment: size) |
| 585 | capacity = space |
| 586 | } |
| 587 | |
| 588 | /// Loads an object of type `FieldLoc` from buffer memory |
| 589 | /// - Parameter index: index of element |
| 590 | /// - Returns: a FieldLoc at index |
| 591 | func load(at index: Int) -> FieldLoc { |
| 592 | return memory.load(fromByteOffset: index, as: FieldLoc.self) |
| 593 | } |
| 594 | |
| 595 | } |
| 596 | |
| 597 | internal struct FieldLoc { |
| 598 | var offset: UOffset |
| 599 | var position: VOffset |
| 600 | } |
| 601 | |
| 602 | } |