FlatBufferBuilder.swift (30006B)
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. 133 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) 152 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) 239 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 256 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 320 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 328 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 353 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 366 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 374 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 386 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) 405 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) 427 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) 448 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 /// Creates a vector of type ``Enum`` into the ``ByteBuffer`` 477 /// 478 /// ``createVector(_:)-9h189`` writes a vector of type ``Enum`` into 479 /// ``ByteBuffer``. This is a convenient method instead of calling, 480 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)`` 481 /// ```swift 482 /// let vectorOffset = builder. 483 /// createVector([.swift, .cpp]) 484 /// ``` 485 /// 486 /// The underlying implementation simply calls ``createVector(_:size:)-7cx6z`` 487 /// 488 /// - Parameter elements: elements to be written into the buffer 489 /// - returns: ``Offset`` of the vector 490 @inline(__always) 491 mutating public func createVector<T: Enum>(_ elements: [T]) -> Offset { 492 createVector(elements, size: elements.count) 493 } 494 495 /// Creates a vector of type ``Enum`` into the ``ByteBuffer`` 496 /// 497 /// ``createVector(_:)-9h189`` writes a vector of type ``Enum`` into 498 /// ``ByteBuffer``. This is a convenient method instead of calling, 499 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)`` 500 /// ```swift 501 /// let vectorOffset = builder. 502 /// createVector([.swift, .cpp]) 503 /// ``` 504 /// 505 /// - Parameter elements: Elements to be written into the buffer 506 /// - Parameter size: Count of elements 507 /// - returns: ``Offset`` of the vector 508 @inline(__always) 509 mutating public func createVector<T: Enum>( 510 _ elements: [T], 511 size: Int) -> Offset 512 { 513 let size = size 514 startVector(size, elementSize: T.byteSize) 515 for e in elements.reversed() { 516 _bb.push(value: e.value, len: T.byteSize) 517 } 518 return endVector(len: size) 519 } 520 521 /// Creates a vector of already written offsets 522 /// 523 /// ``createVector(ofOffsets:)`` creates a vector of ``Offset`` into 524 /// ``ByteBuffer``. This is a convenient method instead of calling, 525 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``. 526 /// 527 /// The underlying implementation simply calls ``createVector(ofOffsets:len:)`` 528 /// 529 /// ```swift 530 /// let namesOffsets = builder. 531 /// createVector(ofOffsets: [name1, name2]) 532 /// ``` 533 /// - Parameter offsets: Array of offsets of type ``Offset`` 534 /// - returns: ``Offset`` of the vector 535 @inline(__always) 536 mutating public func createVector(ofOffsets offsets: [Offset]) -> Offset { 537 createVector(ofOffsets: offsets, len: offsets.count) 538 } 539 540 /// Creates a vector of already written offsets 541 /// 542 /// ``createVector(ofOffsets:)`` creates a vector of ``Offset`` into 543 /// ``ByteBuffer``. This is a convenient method instead of calling, 544 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)`` 545 /// 546 /// ```swift 547 /// let namesOffsets = builder. 548 /// createVector(ofOffsets: [name1, name2]) 549 /// ``` 550 /// 551 /// - Parameter offsets: Array of offsets of type ``Offset`` 552 /// - Parameter size: Count of elements 553 /// - returns: ``Offset`` of the vector 554 @inline(__always) 555 mutating public func createVector( 556 ofOffsets offsets: [Offset], 557 len: Int) -> Offset 558 { 559 startVector(len, elementSize: MemoryLayout<Offset>.size) 560 for o in offsets.reversed() { 561 push(element: o) 562 } 563 return endVector(len: len) 564 } 565 566 /// Creates a vector of strings 567 /// 568 /// ``createVector(ofStrings:)`` creates a vector of `String` into 569 /// ``ByteBuffer``. This is a convenient method instead of manually 570 /// creating the string offsets, you simply pass it to this function 571 /// and it would write the strings into the ``ByteBuffer``. 572 /// After that it calls ``createVector(ofOffsets:)`` 573 /// 574 /// ```swift 575 /// let namesOffsets = builder. 576 /// createVector(ofStrings: ["Name", "surname"]) 577 /// ``` 578 /// 579 /// - Parameter str: Array of string 580 /// - returns: ``Offset`` of the vector 581 @inline(__always) 582 mutating public func createVector(ofStrings str: [String]) -> Offset { 583 var offsets: [Offset] = [] 584 for s in str { 585 offsets.append(create(string: s)) 586 } 587 return createVector(ofOffsets: offsets) 588 } 589 590 /// Creates a vector of type ``NativeStruct``. 591 /// 592 /// Any swift struct in the generated code, should confirm to 593 /// ``NativeStruct``. Since the generated swift structs are padded 594 /// to the `FlatBuffers` standards. 595 /// 596 /// ```swift 597 /// let offsets = builder. 598 /// createVector(ofStructs: [NativeStr(num: 1), NativeStr(num: 2)]) 599 /// ``` 600 /// 601 /// - Parameter structs: A vector of ``NativeStruct`` 602 /// - Returns: ``Offset`` of the vector 603 @inline(__always) 604 mutating public func createVector<T: NativeStruct>(ofStructs structs: [T]) 605 -> Offset 606 { 607 startVector( 608 structs.count * MemoryLayout<T>.size, 609 elementSize: MemoryLayout<T>.alignment) 610 for i in structs.reversed() { 611 _ = create(struct: i) 612 } 613 return endVector(len: structs.count) 614 } 615 616 // MARK: - Inserting Structs 617 618 /// Writes a ``NativeStruct`` into the ``ByteBuffer`` 619 /// 620 /// Adds a native struct that's build and padded according 621 /// to `FlatBuffers` standards. with a predefined position. 622 /// 623 /// ```swift 624 /// let offset = builder.create( 625 /// struct: NativeStr(num: 1), 626 /// position: 10) 627 /// ``` 628 /// 629 /// - Parameters: 630 /// - s: ``NativeStruct`` to be inserted into the ``ByteBuffer`` 631 /// - position: The predefined position of the object 632 /// - Returns: ``Offset`` of written struct 633 @inline(__always) 634 @discardableResult 635 mutating public func create<T: NativeStruct>( 636 struct s: T, position: VOffset) -> Offset 637 { 638 let offset = create(struct: s) 639 _vtableStorage.add(loc: FieldLoc( 640 offset: _bb.size, 641 position: VOffset(position))) 642 return offset 643 } 644 645 /// Writes a ``NativeStruct`` into the ``ByteBuffer`` 646 /// 647 /// Adds a native struct that's build and padded according 648 /// to `FlatBuffers` standards, directly into the buffer without 649 /// a predefined position. 650 /// 651 /// ```swift 652 /// let offset = builder.create( 653 /// struct: NativeStr(num: 1)) 654 /// ``` 655 /// 656 /// - Parameters: 657 /// - s: ``NativeStruct`` to be inserted into the ``ByteBuffer`` 658 /// - Returns: ``Offset`` of written struct 659 @inline(__always) 660 @discardableResult 661 mutating public func create<T: NativeStruct>( 662 struct s: T) -> Offset 663 { 664 let size = MemoryLayout<T>.size 665 preAlign(len: size, alignment: MemoryLayout<T>.alignment) 666 _bb.push(struct: s, size: size) 667 return Offset(offset: _bb.size) 668 } 669 670 // MARK: - Inserting Strings 671 672 /// Insets a string into the buffer of type `UTF8` 673 /// 674 /// Adds a swift string into ``ByteBuffer`` by encoding it 675 /// using `UTF8` 676 /// 677 /// ```swift 678 /// let nameOffset = builder 679 /// .create(string: "welcome") 680 /// ``` 681 /// 682 /// - Parameter str: String to be serialized 683 /// - returns: ``Offset`` of inserted string 684 @inline(__always) 685 mutating public func create(string str: String?) -> Offset { 686 guard let str = str else { return Offset() } 687 let len = str.utf8.count 688 notNested() 689 preAlign(len: len &+ 1, type: UOffset.self) 690 _bb.fill(padding: 1) 691 _bb.push(string: str, len: len) 692 push(element: UOffset(len)) 693 return Offset(offset: _bb.size) 694 } 695 696 /// Insets a shared string into the buffer of type `UTF8` 697 /// 698 /// Adds a swift string into ``ByteBuffer`` by encoding it 699 /// using `UTF8`. The function will check if the string, 700 /// is already written to the ``ByteBuffer`` 701 /// 702 /// ```swift 703 /// let nameOffset = builder 704 /// .createShared(string: "welcome") 705 /// 706 /// 707 /// let secondOffset = builder 708 /// .createShared(string: "welcome") 709 /// 710 /// assert(nameOffset.o == secondOffset.o) 711 /// ``` 712 /// 713 /// - Parameter str: String to be serialized 714 /// - returns: ``Offset`` of inserted string 715 @inline(__always) 716 mutating public func createShared(string str: String?) -> Offset { 717 guard let str = str else { return Offset() } 718 if let offset = stringOffsetMap[str] { 719 return offset 720 } 721 let offset = create(string: str) 722 stringOffsetMap[str] = offset 723 return offset 724 } 725 726 // MARK: - Inseting offsets 727 728 /// Writes the ``Offset`` of an already written table 729 /// 730 /// Writes the ``Offset`` of a table if not empty into the 731 /// ``ByteBuffer`` 732 /// 733 /// - Parameters: 734 /// - offset: ``Offset`` of another object to be written 735 /// - position: The predefined position of the object 736 @inline(__always) 737 mutating public func add(offset: Offset, at position: VOffset) { 738 if offset.isEmpty { return } 739 add(element: refer(to: offset.o), def: 0, at: position) 740 } 741 742 /// Pushes a value of type ``Offset`` into the ``ByteBuffer`` 743 /// - Parameter o: ``Offset`` 744 /// - returns: Current position of the ``Offset`` 745 @inline(__always) 746 @discardableResult 747 mutating public func push(element o: Offset) -> UOffset { 748 push(element: refer(to: o.o)) 749 } 750 751 // MARK: - Inserting Scalars to Buffer 752 753 /// Writes a ``Scalar`` value into ``ByteBuffer`` 754 /// 755 /// ``add(element:def:at:)`` takes in a default value, and current value 756 /// and the position within the `VTable`. The default value would not 757 /// be serialized if the value is the same as the current value or 758 /// `serializeDefaults` is equal to false. 759 /// 760 /// If serializing defaults is important ``init(initialSize:serializeDefaults:)``, 761 /// passing true for `serializeDefaults` would do the job. 762 /// 763 /// ```swift 764 /// // Adds 10 to the buffer 765 /// builder.add(element: Int(10), def: 1, position 12) 766 /// ``` 767 /// 768 /// *NOTE: Never call this manually* 769 /// 770 /// - Parameters: 771 /// - element: Element to insert 772 /// - def: Default value for that element 773 /// - position: The predefined position of the element 774 @inline(__always) 775 mutating public func add<T: Scalar>( 776 element: T, 777 def: T, 778 at position: VOffset) 779 { 780 if element == def && !serializeDefaults { return } 781 track(offset: push(element: element), at: position) 782 } 783 784 /// Writes a optional ``Scalar`` value into ``ByteBuffer`` 785 /// 786 /// Takes an optional value to be written into the ``ByteBuffer`` 787 /// 788 /// *NOTE: Never call this manually* 789 /// 790 /// - Parameters: 791 /// - element: Optional element of type scalar 792 /// - position: The predefined position of the element 793 @inline(__always) 794 mutating public func add<T: Scalar>(element: T?, at position: VOffset) { 795 guard let element = element else { return } 796 track(offset: push(element: element), at: position) 797 } 798 799 /// Pushes a values of type ``Scalar`` into the ``ByteBuffer`` 800 /// 801 /// *NOTE: Never call this manually* 802 /// 803 /// - Parameter element: Element to insert 804 /// - returns: Postion of the Element 805 @inline(__always) 806 @discardableResult 807 mutating public func push<T: Scalar>(element: T) -> UOffset { 808 let size = MemoryLayout<T>.size 809 preAlign( 810 len: size, 811 alignment: size) 812 _bb.push(value: element, len: size) 813 return _bb.size 814 } 815 816 } 817 818 extension FlatBufferBuilder: CustomDebugStringConvertible { 819 820 public var debugDescription: String { 821 """ 822 buffer debug: 823 \(_bb) 824 builder debug: 825 { finished: \(finished), serializeDefaults: \(serializeDefaults), isNested: \(isNested) } 826 """ 827 } 828 829 /// VTableStorage is a class to contain the VTable buffer that would be serialized into buffer 830 @usableFromInline 831 internal class VTableStorage { 832 /// Memory check since deallocating each time we want to clear would be expensive 833 /// and memory leaks would happen if we dont deallocate the first allocated memory. 834 /// memory is promised to be available before adding `FieldLoc` 835 private var memoryInUse = false 836 /// Size of FieldLoc in memory 837 let size = MemoryLayout<FieldLoc>.stride 838 /// Memeory buffer 839 var memory: UnsafeMutableRawBufferPointer! 840 /// Capacity of the current buffer 841 var capacity: Int = 0 842 /// Maximuim offset written to the class 843 var maxOffset: VOffset = 0 844 /// number of fields written into the buffer 845 var numOfFields: Int = 0 846 /// Last written Index 847 var writtenIndex: Int = 0 848 849 /// Creates the memory to store the buffer in 850 @usableFromInline 851 @inline(__always) 852 init() { 853 memory = UnsafeMutableRawBufferPointer.allocate( 854 byteCount: 0, 855 alignment: 0) 856 } 857 858 @inline(__always) 859 deinit { 860 memory.deallocate() 861 } 862 863 /// Builds a buffer with byte count of fieldloc.size * count of field numbers 864 /// - Parameter count: number of fields to be written 865 @inline(__always) 866 func start(count: Int) { 867 assert(count >= 0, "number of fields should NOT be negative") 868 let capacity = count &* size 869 ensure(space: capacity) 870 } 871 872 /// Adds a FieldLoc into the buffer, which would track how many have been written, 873 /// and max offset 874 /// - Parameter loc: Location of encoded element 875 @inline(__always) 876 func add(loc: FieldLoc) { 877 memory.baseAddress?.advanced(by: writtenIndex).storeBytes( 878 of: loc, 879 as: FieldLoc.self) 880 writtenIndex = writtenIndex &+ size 881 numOfFields = numOfFields &+ 1 882 maxOffset = max(loc.position, maxOffset) 883 } 884 885 /// Clears the data stored related to the encoded buffer 886 @inline(__always) 887 func clear() { 888 maxOffset = 0 889 numOfFields = 0 890 writtenIndex = 0 891 } 892 893 /// Ensure that the buffer has enough space instead of recreating the buffer each time. 894 /// - Parameter space: space required for the new vtable 895 @inline(__always) 896 func ensure(space: Int) { 897 guard space &+ writtenIndex > capacity else { return } 898 memory.deallocate() 899 memory = UnsafeMutableRawBufferPointer.allocate( 900 byteCount: space, 901 alignment: size) 902 capacity = space 903 } 904 905 /// Loads an object of type `FieldLoc` from buffer memory 906 /// - Parameter index: index of element 907 /// - Returns: a FieldLoc at index 908 @inline(__always) 909 func load(at index: Int) -> FieldLoc { 910 memory.load(fromByteOffset: index, as: FieldLoc.self) 911 } 912 913 } 914 915 internal struct FieldLoc { 916 var offset: UOffset 917 var position: VOffset 918 } 919 920 }