• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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