1 /* 2 * Copyright 2023 Google Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #if !os(WASI) 18 import Foundation 19 #else 20 import SwiftOverlayShims 21 #endif 22 23 /// ``FlatBufferBuilder`` builds a `FlatBuffer` through manipulating its internal state. 24 /// 25 /// This is done by creating a ``ByteBuffer`` that hosts the incoming data and 26 /// has a hardcoded growth limit of `2GiB` which is set by the Flatbuffers standards. 27 /// 28 /// ```swift 29 /// var builder = FlatBufferBuilder() 30 /// ``` 31 /// The builder should be always created as a variable, since it would be passed into the writers 32 /// 33 @frozen 34 public struct FlatBufferBuilder { 35 36 /// Storage for the Vtables used in the buffer are stored in here, so they would be written later in EndTable 37 @usableFromInline internal var _vtableStorage = VTableStorage() 38 /// Flatbuffer data will be written into 39 @usableFromInline internal var _bb: ByteBuffer 40 41 /// Reference Vtables that were already written to the buffer 42 private var _vtables: [UOffset] = [] 43 /// A check if the buffer is being written into by a different table 44 private var isNested = false 45 /// Dictonary that stores a map of all the strings that were written to the buffer 46 private var stringOffsetMap: [String: Offset] = [:] 47 /// A check to see if finish(::) was ever called to retreive data object 48 private var finished = false 49 /// A check to see if the buffer should serialize Default values 50 private var serializeDefaults: Bool 51 52 /// Current alignment for the buffer 53 var _minAlignment: Int = 0 { 54 didSet { 55 _bb.alignment = _minAlignment 56 } 57 } 58 59 /// Gives a read access to the buffer's size 60 public var size: UOffset { _bb.size } 61 62 #if !os(WASI) 63 /// Data representation of the buffer 64 /// 65 /// Should only be used after ``finish(offset:addPrefix:)`` is called 66 public var data: Data { 67 assert(finished, "Data shouldn't be called before finish()") 68 return Data( 69 bytes: _bb.memory.advanced(by: _bb.writerIndex), 70 count: _bb.capacity &- _bb.writerIndex) 71 } 72 #endif 73 74 /// Returns the underlying bytes in the ``ByteBuffer`` 75 /// 76 /// Note: This should be used with caution. 77 public var fullSizedByteArray: [UInt8] { 78 let ptr = UnsafeBufferPointer( 79 start: _bb.memory.assumingMemoryBound(to: UInt8.self), 80 count: _bb.capacity) 81 return Array(ptr) 82 } 83 84 /// Returns the written bytes into the ``ByteBuffer`` 85 /// 86 /// Should only be used after ``finish(offset:addPrefix:)`` is called 87 public var sizedByteArray: [UInt8] { 88 assert(finished, "Data shouldn't be called before finish()") 89 return _bb.underlyingBytes 90 } 91 92 /// Returns the original ``ByteBuffer`` 93 /// 94 /// Returns the current buffer that was just created 95 /// with the offsets, and data written to it. 96 public var buffer: ByteBuffer { _bb } 97 98 /// Returns a newly created sized ``ByteBuffer`` 99 /// 100 /// returns a new buffer that is sized to the data written 101 /// to the main buffer 102 public var sizedBuffer: ByteBuffer { 103 assert(finished, "Data shouldn't be called before finish()") 104 return ByteBuffer( 105 memory: _bb.memory.advanced(by: _bb.reader), 106 count: Int(_bb.size)) 107 } 108 109 // MARK: - Init 110 111 /// Initialize the buffer with a size 112 /// - Parameters: 113 /// - initialSize: Initial size for the buffer 114 /// - force: Allows default to be serialized into the buffer 115 /// 116 /// This initializes a new builder with an initialSize that would initialize 117 /// a new ``ByteBuffer``. ``FlatBufferBuilder`` by default doesnt serialize defaults 118 /// however the builder can be force by passing true for `serializeDefaults` 119 public init( 120 initialSize: Int32 = 1024, 121 serializeDefaults force: Bool = false) 122 { 123 assert(initialSize > 0, "Size should be greater than zero!") 124 guard isLitteEndian else { 125 fatalError( 126 "Reading/Writing a buffer in big endian machine is not supported on swift") 127 } 128 serializeDefaults = force 129 _bb = ByteBuffer(initialSize: Int(initialSize)) 130 } 131 132 /// Clears the builder and the buffer from the written data. clearnull133 mutating public func clear() { 134 _minAlignment = 0 135 isNested = false 136 stringOffsetMap.removeAll(keepingCapacity: true) 137 _vtables.removeAll(keepingCapacity: true) 138 _vtableStorage.clear() 139 _bb.clear() 140 } 141 142 // MARK: - Create Tables 143 144 /// Checks if the required fields were serialized into the buffer 145 /// - Parameters: 146 /// - table: offset for the table 147 /// - fields: Array of all the important fields to be serialized 148 /// 149 /// *NOTE: Never call this function, this is only supposed to be called 150 /// by the generated code* 151 @inline(__always) requirenull152 mutating public func require(table: Offset, fields: [Int32]) { 153 for field in fields { 154 let start = _bb.capacity &- Int(table.o) 155 let startTable = start &- Int(_bb.read(def: Int32.self, position: start)) 156 let isOkay = _bb.read( 157 def: VOffset.self, 158 position: startTable &+ Int(field)) != 0 159 assert(isOkay, "Flatbuffers requires the following field") 160 } 161 } 162 163 /// Finished the buffer by adding the file id and then calling finish 164 /// - Parameters: 165 /// - offset: Offset of the table 166 /// - fileId: Takes the fileId 167 /// - prefix: if false it wont add the size of the buffer 168 /// 169 /// ``finish(offset:fileId:addPrefix:)`` should be called at the end of creating 170 /// a table 171 /// ```swift 172 /// var root = SomeObject 173 /// .createObject(&builder, 174 /// name: nameOffset) 175 /// builder.finish( 176 /// offset: root, 177 /// fileId: "ax1a", 178 /// addPrefix: true) 179 /// ``` 180 /// File id would append a file id name at the end of the written bytes before, 181 /// finishing the buffer. 182 /// 183 /// Whereas, if `addPrefix` is true, the written bytes would 184 /// include the size of the current buffer. 185 mutating public func finish( 186 offset: Offset, 187 fileId: String, 188 addPrefix prefix: Bool = false) 189 { 190 let size = MemoryLayout<UOffset>.size 191 preAlign( 192 len: size &+ (prefix ? size : 0) &+ FileIdLength, 193 alignment: _minAlignment) 194 assert(fileId.count == FileIdLength, "Flatbuffers requires file id to be 4") 195 _bb.push(string: fileId, len: 4) 196 finish(offset: offset, addPrefix: prefix) 197 } 198 199 /// Finished the buffer by adding the file id, offset, and prefix to it. 200 /// - Parameters: 201 /// - offset: Offset of the table 202 /// - prefix: if false it wont add the size of the buffer 203 /// 204 /// ``finish(offset:addPrefix:)`` should be called at the end of creating 205 /// a table 206 /// ```swift 207 /// var root = SomeObject 208 /// .createObject(&builder, 209 /// name: nameOffset) 210 /// builder.finish( 211 /// offset: root, 212 /// addPrefix: true) 213 /// ``` 214 /// If `addPrefix` is true, the written bytes would 215 /// include the size of the current buffer. 216 mutating public func finish( 217 offset: Offset, 218 addPrefix prefix: Bool = false) 219 { 220 notNested() 221 let size = MemoryLayout<UOffset>.size 222 preAlign(len: size &+ (prefix ? size : 0), alignment: _minAlignment) 223 push(element: refer(to: offset.o)) 224 if prefix { push(element: _bb.size) } 225 _vtableStorage.clear() 226 finished = true 227 } 228 229 /// ``startTable(with:)`` will let the builder know, that a new object is being serialized. 230 /// 231 /// The function will fatalerror if called while there is another object being serialized. 232 /// ```swift 233 /// let start = Monster 234 /// .startMonster(&fbb) 235 /// ``` 236 /// - Parameter numOfFields: Number of elements to be written to the buffer 237 /// - Returns: Offset of the newly started table 238 @inline(__always) startTablenull239 mutating public func startTable(with numOfFields: Int) -> UOffset { 240 notNested() 241 isNested = true 242 _vtableStorage.start(count: numOfFields) 243 return _bb.size 244 } 245 246 /// ``endTable(at:)`` will let the ``FlatBufferBuilder`` know that the 247 /// object that's written to it is completed 248 /// 249 /// This would be called after all the elements are serialized, 250 /// it will add the current vtable into the ``ByteBuffer``. 251 /// The functions will `fatalError` in case the object is called 252 /// without ``startTable(with:)``, or the object has exceeded the limit of 2GB. 253 /// 254 /// - Parameter startOffset:Start point of the object written 255 /// - returns: The root of the table endTablenull256 mutating public func endTable(at startOffset: UOffset) -> UOffset { 257 assert(isNested, "Calling endtable without calling starttable") 258 let sizeofVoffset = MemoryLayout<VOffset>.size 259 let vTableOffset = push(element: SOffset(0)) 260 261 let tableObjectSize = vTableOffset &- startOffset 262 assert(tableObjectSize < 0x10000, "Buffer can't grow beyond 2 Gigabytes") 263 let _max = Int(_vtableStorage.maxOffset) &+ sizeofVoffset 264 265 _bb.fill(padding: _max) 266 _bb.write( 267 value: VOffset(tableObjectSize), 268 index: _bb.writerIndex &+ sizeofVoffset, 269 direct: true) 270 _bb.write(value: VOffset(_max), index: _bb.writerIndex, direct: true) 271 272 var itr = 0 273 while itr < _vtableStorage.writtenIndex { 274 let loaded = _vtableStorage.load(at: itr) 275 itr = itr &+ _vtableStorage.size 276 guard loaded.offset != 0 else { continue } 277 let _index = (_bb.writerIndex &+ Int(loaded.position)) 278 _bb.write( 279 value: VOffset(vTableOffset &- loaded.offset), 280 index: _index, 281 direct: true) 282 } 283 284 _vtableStorage.clear() 285 let vt_use = _bb.size 286 287 var isAlreadyAdded: Int? 288 289 let vt2 = _bb.memory.advanced(by: _bb.writerIndex) 290 let len2 = vt2.load(fromByteOffset: 0, as: Int16.self) 291 292 for table in _vtables { 293 let position = _bb.capacity &- Int(table) 294 let vt1 = _bb.memory.advanced(by: position) 295 let len1 = _bb.read(def: Int16.self, position: position) 296 if len2 != len1 || 0 != memcmp(vt1, vt2, Int(len2)) { continue } 297 298 isAlreadyAdded = Int(table) 299 break 300 } 301 302 if let offset = isAlreadyAdded { 303 let vTableOff = Int(vTableOffset) 304 let space = _bb.capacity &- vTableOff 305 _bb.write(value: Int32(offset &- vTableOff), index: space, direct: true) 306 _bb.pop(_bb.capacity &- space) 307 } else { 308 _bb.write(value: Int32(vt_use &- vTableOffset), index: Int(vTableOffset)) 309 _vtables.append(_bb.size) 310 } 311 isNested = false 312 return vTableOffset 313 } 314 315 // MARK: - Builds Buffer 316 317 /// Asserts to see if the object is not nested 318 @inline(__always) 319 @usableFromInline notNestednull320 mutating internal func notNested() { 321 assert(!isNested, "Object serialization must not be nested") 322 } 323 324 /// Changes the minimuim alignment of the buffer 325 /// - Parameter size: size of the current alignment 326 @inline(__always) 327 @usableFromInline minAlignmentnull328 mutating internal func minAlignment(size: Int) { 329 if size > _minAlignment { 330 _minAlignment = size 331 } 332 } 333 334 /// Gets the padding for the current element 335 /// - Parameters: 336 /// - bufSize: Current size of the buffer + the offset of the object to be written 337 /// - elementSize: Element size 338 @inline(__always) 339 @usableFromInline 340 mutating internal func padding( 341 bufSize: UInt32, 342 elementSize: UInt32) -> UInt32 343 { 344 ((~bufSize) &+ 1) & (elementSize - 1) 345 } 346 347 /// Prealigns the buffer before writting a new object into the buffer 348 /// - Parameters: 349 /// - len:Length of the object 350 /// - alignment: Alignment type 351 @inline(__always) 352 @usableFromInline preAlignnull353 mutating internal func preAlign(len: Int, alignment: Int) { 354 minAlignment(size: alignment) 355 _bb.fill(padding: Int(padding( 356 bufSize: _bb.size &+ UOffset(len), 357 elementSize: UOffset(alignment)))) 358 } 359 360 /// Prealigns the buffer before writting a new object into the buffer 361 /// - Parameters: 362 /// - len: Length of the object 363 /// - type: Type of the object to be written 364 @inline(__always) 365 @usableFromInline preAlign<T: Scalar>null366 mutating internal func preAlign<T: Scalar>(len: Int, type: T.Type) { 367 preAlign(len: len, alignment: MemoryLayout<T>.size) 368 } 369 370 /// Refers to an object that's written in the buffer 371 /// - Parameter off: the objects index value 372 @inline(__always) 373 @usableFromInline refernull374 mutating internal func refer(to off: UOffset) -> UOffset { 375 let size = MemoryLayout<UOffset>.size 376 preAlign(len: size, alignment: size) 377 return _bb.size &- off &+ UInt32(size) 378 } 379 380 /// Tracks the elements written into the buffer 381 /// - Parameters: 382 /// - offset: The offset of the element witten 383 /// - position: The position of the element 384 @inline(__always) 385 @usableFromInline tracknull386 mutating internal func track(offset: UOffset, at position: VOffset) { 387 _vtableStorage.add(loc: FieldLoc(offset: offset, position: position)) 388 } 389 390 // MARK: - Inserting Vectors 391 392 /// ``startVector(_:elementSize:)`` creates a new vector within buffer 393 /// 394 /// The function checks if there is a current object being written, if 395 /// the check passes it creates a buffer alignment of `length * elementSize` 396 /// ```swift 397 /// builder.startVector( 398 /// int32Values.count, elementSize: 4) 399 /// ``` 400 /// 401 /// - Parameters: 402 /// - len: Length of vector to be created 403 /// - elementSize: Size of object type to be written 404 @inline(__always) startVectornull405 mutating public func startVector(_ len: Int, elementSize: Int) { 406 notNested() 407 isNested = true 408 preAlign(len: len &* elementSize, type: UOffset.self) 409 preAlign(len: len &* elementSize, alignment: elementSize) 410 } 411 412 /// ``endVector(len:)`` ends the currently created vector 413 /// 414 /// Calling ``endVector(len:)`` requires the length, of the current 415 /// vector. The length would be pushed to indicate the count of numbers 416 /// within the vector. If ``endVector(len:)`` is called without 417 /// ``startVector(_:elementSize:)`` it asserts. 418 /// 419 /// ```swift 420 /// let vectorOffset = builder. 421 /// endVector(len: int32Values.count) 422 /// ``` 423 /// 424 /// - Parameter len: Length of the buffer 425 /// - Returns: Returns the current ``Offset`` in the ``ByteBuffer`` 426 @inline(__always) endVectornull427 mutating public func endVector(len: Int) -> Offset { 428 assert(isNested, "Calling endVector without calling startVector") 429 isNested = false 430 return Offset(offset: push(element: Int32(len))) 431 } 432 433 /// Creates a vector of type ``Scalar`` into the ``ByteBuffer`` 434 /// 435 /// ``createVector(_:)-4swl0`` writes a vector of type Scalars into 436 /// ``ByteBuffer``. This is a convenient method instead of calling, 437 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)`` 438 /// ```swift 439 /// let vectorOffset = builder. 440 /// createVector([1, 2, 3, 4]) 441 /// ``` 442 /// 443 /// The underlying implementation simply calls ``createVector(_:size:)-4lhrv`` 444 /// 445 /// - Parameter elements: elements to be written into the buffer 446 /// - returns: ``Offset`` of the vector 447 @inline(__always) createVector<T: Scalar>null448 mutating public func createVector<T: Scalar>(_ elements: [T]) -> Offset { 449 createVector(elements, size: elements.count) 450 } 451 452 /// Creates a vector of type Scalar in the buffer 453 /// 454 /// ``createVector(_:)-4swl0`` writes a vector of type Scalars into 455 /// ``ByteBuffer``. This is a convenient method instead of calling, 456 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)`` 457 /// ```swift 458 /// let vectorOffset = builder. 459 /// createVector([1, 2, 3, 4], size: 4) 460 /// ``` 461 /// 462 /// - Parameter elements: Elements to be written into the buffer 463 /// - Parameter size: Count of elements 464 /// - returns: ``Offset`` of the vector 465 @inline(__always) 466 mutating public func createVector<T: Scalar>( 467 _ elements: [T], 468 size: Int) -> Offset 469 { 470 let size = size 471 startVector(size, elementSize: MemoryLayout<T>.size) 472 _bb.push(elements: elements) 473 return endVector(len: size) 474 } 475 476 #if swift(>=5.0) && !os(WASI) 477 @inline(__always) 478 /// Creates a vector of bytes in the buffer. 479 /// 480 /// Allows creating a vector from `Data` without copying to a `[UInt8]` 481 /// 482 /// - Parameter bytes: bytes to be written into the buffer 483 /// - Returns: ``Offset`` of the vector createVectornull484 mutating public func createVector(bytes: ContiguousBytes) -> Offset { 485 let size = bytes.withUnsafeBytes { ptr in ptr.count } 486 startVector(size, elementSize: MemoryLayout<UInt8>.size) 487 _bb.push(bytes: bytes) 488 return endVector(len: size) 489 } 490 #endif 491 492 /// Creates a vector of type ``Enum`` into the ``ByteBuffer`` 493 /// 494 /// ``createVector(_:)-9h189`` writes a vector of type ``Enum`` into 495 /// ``ByteBuffer``. This is a convenient method instead of calling, 496 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)`` 497 /// ```swift 498 /// let vectorOffset = builder. 499 /// createVector([.swift, .cpp]) 500 /// ``` 501 /// 502 /// The underlying implementation simply calls ``createVector(_:size:)-7cx6z`` 503 /// 504 /// - Parameter elements: elements to be written into the buffer 505 /// - returns: ``Offset`` of the vector 506 @inline(__always) createVector<T: Enum>null507 mutating public func createVector<T: Enum>(_ elements: [T]) -> Offset { 508 createVector(elements, size: elements.count) 509 } 510 511 /// Creates a vector of type ``Enum`` into the ``ByteBuffer`` 512 /// 513 /// ``createVector(_:)-9h189`` writes a vector of type ``Enum`` into 514 /// ``ByteBuffer``. This is a convenient method instead of calling, 515 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)`` 516 /// ```swift 517 /// let vectorOffset = builder. 518 /// createVector([.swift, .cpp]) 519 /// ``` 520 /// 521 /// - Parameter elements: Elements to be written into the buffer 522 /// - Parameter size: Count of elements 523 /// - returns: ``Offset`` of the vector 524 @inline(__always) 525 mutating public func createVector<T: Enum>( 526 _ elements: [T], 527 size: Int) -> Offset 528 { 529 let size = size 530 startVector(size, elementSize: T.byteSize) 531 for e in elements.reversed() { 532 _bb.push(value: e.value, len: T.byteSize) 533 } 534 return endVector(len: size) 535 } 536 537 /// Creates a vector of already written offsets 538 /// 539 /// ``createVector(ofOffsets:)`` creates a vector of ``Offset`` into 540 /// ``ByteBuffer``. This is a convenient method instead of calling, 541 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``. 542 /// 543 /// The underlying implementation simply calls ``createVector(ofOffsets:len:)`` 544 /// 545 /// ```swift 546 /// let namesOffsets = builder. 547 /// createVector(ofOffsets: [name1, name2]) 548 /// ``` 549 /// - Parameter offsets: Array of offsets of type ``Offset`` 550 /// - returns: ``Offset`` of the vector 551 @inline(__always) createVectornull552 mutating public func createVector(ofOffsets offsets: [Offset]) -> Offset { 553 createVector(ofOffsets: offsets, len: offsets.count) 554 } 555 556 /// Creates a vector of already written offsets 557 /// 558 /// ``createVector(ofOffsets:)`` creates a vector of ``Offset`` into 559 /// ``ByteBuffer``. This is a convenient method instead of calling, 560 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)`` 561 /// 562 /// ```swift 563 /// let namesOffsets = builder. 564 /// createVector(ofOffsets: [name1, name2]) 565 /// ``` 566 /// 567 /// - Parameter offsets: Array of offsets of type ``Offset`` 568 /// - Parameter size: Count of elements 569 /// - returns: ``Offset`` of the vector 570 @inline(__always) 571 mutating public func createVector( 572 ofOffsets offsets: [Offset], 573 len: Int) -> Offset 574 { 575 startVector(len, elementSize: MemoryLayout<Offset>.size) 576 for o in offsets.reversed() { 577 push(element: o) 578 } 579 return endVector(len: len) 580 } 581 582 /// Creates a vector of strings 583 /// 584 /// ``createVector(ofStrings:)`` creates a vector of `String` into 585 /// ``ByteBuffer``. This is a convenient method instead of manually 586 /// creating the string offsets, you simply pass it to this function 587 /// and it would write the strings into the ``ByteBuffer``. 588 /// After that it calls ``createVector(ofOffsets:)`` 589 /// 590 /// ```swift 591 /// let namesOffsets = builder. 592 /// createVector(ofStrings: ["Name", "surname"]) 593 /// ``` 594 /// 595 /// - Parameter str: Array of string 596 /// - returns: ``Offset`` of the vector 597 @inline(__always) createVectornull598 mutating public func createVector(ofStrings str: [String]) -> Offset { 599 var offsets: [Offset] = [] 600 for s in str { 601 offsets.append(create(string: s)) 602 } 603 return createVector(ofOffsets: offsets) 604 } 605 606 /// Creates a vector of type ``NativeStruct``. 607 /// 608 /// Any swift struct in the generated code, should confirm to 609 /// ``NativeStruct``. Since the generated swift structs are padded 610 /// to the `FlatBuffers` standards. 611 /// 612 /// ```swift 613 /// let offsets = builder. 614 /// createVector(ofStructs: [NativeStr(num: 1), NativeStr(num: 2)]) 615 /// ``` 616 /// 617 /// - Parameter structs: A vector of ``NativeStruct`` 618 /// - Returns: ``Offset`` of the vector 619 @inline(__always) createVector<T: NativeStruct>null620 mutating public func createVector<T: NativeStruct>(ofStructs structs: [T]) 621 -> Offset 622 { 623 startVector( 624 structs.count * MemoryLayout<T>.size, 625 elementSize: MemoryLayout<T>.alignment) 626 _bb.push(elements: structs) 627 return endVector(len: structs.count) 628 } 629 630 // MARK: - Inserting Structs 631 632 /// Writes a ``NativeStruct`` into the ``ByteBuffer`` 633 /// 634 /// Adds a native struct that's build and padded according 635 /// to `FlatBuffers` standards. with a predefined position. 636 /// 637 /// ```swift 638 /// let offset = builder.create( 639 /// struct: NativeStr(num: 1), 640 /// position: 10) 641 /// ``` 642 /// 643 /// - Parameters: 644 /// - s: ``NativeStruct`` to be inserted into the ``ByteBuffer`` 645 /// - position: The predefined position of the object 646 /// - Returns: ``Offset`` of written struct 647 @inline(__always) 648 @discardableResult 649 mutating public func create<T: NativeStruct>( 650 struct s: T, position: VOffset) -> Offset 651 { 652 let offset = create(struct: s) 653 _vtableStorage.add(loc: FieldLoc( 654 offset: _bb.size, 655 position: VOffset(position))) 656 return offset 657 } 658 659 /// Writes a ``NativeStruct`` into the ``ByteBuffer`` 660 /// 661 /// Adds a native struct that's build and padded according 662 /// to `FlatBuffers` standards, directly into the buffer without 663 /// a predefined position. 664 /// 665 /// ```swift 666 /// let offset = builder.create( 667 /// struct: NativeStr(num: 1)) 668 /// ``` 669 /// 670 /// - Parameters: 671 /// - s: ``NativeStruct`` to be inserted into the ``ByteBuffer`` 672 /// - Returns: ``Offset`` of written struct 673 @inline(__always) 674 @discardableResult 675 mutating public func create<T: NativeStruct>( 676 struct s: T) -> Offset 677 { 678 let size = MemoryLayout<T>.size 679 preAlign(len: size, alignment: MemoryLayout<T>.alignment) 680 _bb.push(struct: s, size: size) 681 return Offset(offset: _bb.size) 682 } 683 684 // MARK: - Inserting Strings 685 686 /// Insets a string into the buffer of type `UTF8` 687 /// 688 /// Adds a swift string into ``ByteBuffer`` by encoding it 689 /// using `UTF8` 690 /// 691 /// ```swift 692 /// let nameOffset = builder 693 /// .create(string: "welcome") 694 /// ``` 695 /// 696 /// - Parameter str: String to be serialized 697 /// - returns: ``Offset`` of inserted string 698 @inline(__always) createnull699 mutating public func create(string str: String?) -> Offset { 700 guard let str = str else { return Offset() } 701 let len = str.utf8.count 702 notNested() 703 preAlign(len: len &+ 1, type: UOffset.self) 704 _bb.fill(padding: 1) 705 _bb.push(string: str, len: len) 706 push(element: UOffset(len)) 707 return Offset(offset: _bb.size) 708 } 709 710 /// Insets a shared string into the buffer of type `UTF8` 711 /// 712 /// Adds a swift string into ``ByteBuffer`` by encoding it 713 /// using `UTF8`. The function will check if the string, 714 /// is already written to the ``ByteBuffer`` 715 /// 716 /// ```swift 717 /// let nameOffset = builder 718 /// .createShared(string: "welcome") 719 /// 720 /// 721 /// let secondOffset = builder 722 /// .createShared(string: "welcome") 723 /// 724 /// assert(nameOffset.o == secondOffset.o) 725 /// ``` 726 /// 727 /// - Parameter str: String to be serialized 728 /// - returns: ``Offset`` of inserted string 729 @inline(__always) createSharednull730 mutating public func createShared(string str: String?) -> Offset { 731 guard let str = str else { return Offset() } 732 if let offset = stringOffsetMap[str] { 733 return offset 734 } 735 let offset = create(string: str) 736 stringOffsetMap[str] = offset 737 return offset 738 } 739 740 // MARK: - Inseting offsets 741 742 /// Writes the ``Offset`` of an already written table 743 /// 744 /// Writes the ``Offset`` of a table if not empty into the 745 /// ``ByteBuffer`` 746 /// 747 /// - Parameters: 748 /// - offset: ``Offset`` of another object to be written 749 /// - position: The predefined position of the object 750 @inline(__always) addnull751 mutating public func add(offset: Offset, at position: VOffset) { 752 if offset.isEmpty { return } 753 add(element: refer(to: offset.o), def: 0, at: position) 754 } 755 756 /// Pushes a value of type ``Offset`` into the ``ByteBuffer`` 757 /// - Parameter o: ``Offset`` 758 /// - returns: Current position of the ``Offset`` 759 @inline(__always) 760 @discardableResult pushnull761 mutating public func push(element o: Offset) -> UOffset { 762 push(element: refer(to: o.o)) 763 } 764 765 // MARK: - Inserting Scalars to Buffer 766 767 /// Writes a ``Scalar`` value into ``ByteBuffer`` 768 /// 769 /// ``add(element:def:at:)`` takes in a default value, and current value 770 /// and the position within the `VTable`. The default value would not 771 /// be serialized if the value is the same as the current value or 772 /// `serializeDefaults` is equal to false. 773 /// 774 /// If serializing defaults is important ``init(initialSize:serializeDefaults:)``, 775 /// passing true for `serializeDefaults` would do the job. 776 /// 777 /// ```swift 778 /// // Adds 10 to the buffer 779 /// builder.add(element: Int(10), def: 1, position 12) 780 /// ``` 781 /// 782 /// *NOTE: Never call this manually* 783 /// 784 /// - Parameters: 785 /// - element: Element to insert 786 /// - def: Default value for that element 787 /// - position: The predefined position of the element 788 @inline(__always) 789 mutating public func add<T: Scalar>( 790 element: T, 791 def: T, 792 at position: VOffset) 793 { 794 if element == def && !serializeDefaults { return } 795 track(offset: push(element: element), at: position) 796 } 797 798 /// Writes a optional ``Scalar`` value into ``ByteBuffer`` 799 /// 800 /// Takes an optional value to be written into the ``ByteBuffer`` 801 /// 802 /// *NOTE: Never call this manually* 803 /// 804 /// - Parameters: 805 /// - element: Optional element of type scalar 806 /// - position: The predefined position of the element 807 @inline(__always) add<T: Scalar>null808 mutating public func add<T: Scalar>(element: T?, at position: VOffset) { 809 guard let element = element else { return } 810 track(offset: push(element: element), at: position) 811 } 812 813 /// Pushes a values of type ``Scalar`` into the ``ByteBuffer`` 814 /// 815 /// *NOTE: Never call this manually* 816 /// 817 /// - Parameter element: Element to insert 818 /// - returns: Postion of the Element 819 @inline(__always) 820 @discardableResult push<T: Scalar>null821 mutating public func push<T: Scalar>(element: T) -> UOffset { 822 let size = MemoryLayout<T>.size 823 preAlign( 824 len: size, 825 alignment: size) 826 _bb.push(value: element, len: size) 827 return _bb.size 828 } 829 830 } 831 832 extension FlatBufferBuilder: CustomDebugStringConvertible { 833 834 public var debugDescription: String { 835 """ 836 buffer debug: 837 \(_bb) 838 builder debug: 839 { finished: \(finished), serializeDefaults: \(serializeDefaults), isNested: \(isNested) } 840 """ 841 } 842 843 /// VTableStorage is a class to contain the VTable buffer that would be serialized into buffer 844 @usableFromInline 845 internal class VTableStorage { 846 /// Memory check since deallocating each time we want to clear would be expensive 847 /// and memory leaks would happen if we dont deallocate the first allocated memory. 848 /// memory is promised to be available before adding `FieldLoc` 849 private var memoryInUse = false 850 /// Size of FieldLoc in memory 851 let size = MemoryLayout<FieldLoc>.stride 852 /// Memeory buffer 853 var memory: UnsafeMutableRawBufferPointer! 854 /// Capacity of the current buffer 855 var capacity: Int = 0 856 /// Maximuim offset written to the class 857 var maxOffset: VOffset = 0 858 /// number of fields written into the buffer 859 var numOfFields: Int = 0 860 /// Last written Index 861 var writtenIndex: Int = 0 862 863 /// Creates the memory to store the buffer in 864 @usableFromInline 865 @inline(__always) 866 init() { 867 memory = UnsafeMutableRawBufferPointer.allocate( 868 byteCount: 0, 869 alignment: 0) 870 } 871 872 @inline(__always) 873 deinit { 874 memory.deallocate() 875 } 876 877 /// Builds a buffer with byte count of fieldloc.size * count of field numbers 878 /// - Parameter count: number of fields to be written 879 @inline(__always) startnull880 func start(count: Int) { 881 assert(count >= 0, "number of fields should NOT be negative") 882 let capacity = count &* size 883 ensure(space: capacity) 884 } 885 886 /// Adds a FieldLoc into the buffer, which would track how many have been written, 887 /// and max offset 888 /// - Parameter loc: Location of encoded element 889 @inline(__always) addnull890 func add(loc: FieldLoc) { 891 memory.baseAddress?.advanced(by: writtenIndex).storeBytes( 892 of: loc, 893 as: FieldLoc.self) 894 writtenIndex = writtenIndex &+ size 895 numOfFields = numOfFields &+ 1 896 maxOffset = max(loc.position, maxOffset) 897 } 898 899 /// Clears the data stored related to the encoded buffer 900 @inline(__always) clearnull901 func clear() { 902 maxOffset = 0 903 numOfFields = 0 904 writtenIndex = 0 905 } 906 907 /// Ensure that the buffer has enough space instead of recreating the buffer each time. 908 /// - Parameter space: space required for the new vtable 909 @inline(__always) ensurenull910 func ensure(space: Int) { 911 guard space &+ writtenIndex > capacity else { return } 912 memory.deallocate() 913 memory = UnsafeMutableRawBufferPointer.allocate( 914 byteCount: space, 915 alignment: size) 916 capacity = space 917 } 918 919 /// Loads an object of type `FieldLoc` from buffer memory 920 /// - Parameter index: index of element 921 /// - Returns: a FieldLoc at index 922 @inline(__always) loadnull923 func load(at index: Int) -> FieldLoc { 924 memory.load(fromByteOffset: index, as: FieldLoc.self) 925 } 926 927 } 928 929 internal struct FieldLoc { 930 var offset: UOffset 931 var position: VOffset 932 } 933 934 } 935