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