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