1 import Foundation
2 
3 public final class FlatBufferBuilder {
4 
5     /// Vtables used in the buffer are stored in here, so they would be written later in EndTable
6     private var _vtable: [UInt32] = []
7     /// Reference Vtables that were already written to the buffer
8     private var _vtables: [UOffset] = []
9     /// Flatbuffer data will be written into
10     private var _bb: ByteBuffer
11     /// A check if the buffer is being written into by a different table
12     private var isNested = false
13     /// Dictonary that stores a map of all the strings that were written to the buffer
14     private var stringOffsetMap: [String: Offset<String>] = [:]
15     /// A check to see if finish(::) was ever called to retreive data object
16     private var finished = false
17     /// A check to see if the buffer should serialize Default values
18     private var serializeDefaults: Bool
19 
20     /// Current alignment for the buffer
21     var _minAlignment: Int = 0 {
22         didSet {
23             _bb.alignment = _minAlignment
24         }
25     }
26 
27     /// Gives a read access to the buffer's size
28     public var size: UOffset { return _bb.size }
29     /// Data representation of the buffer
30     public var data: Data {
31         assert(finished, "Data shouldn't be called before finish()")
32         return Data(bytes: _bb.memory.advanced(by: _bb.writerIndex),
33                     count: _bb.capacity - _bb.writerIndex)
34     }
35     /// Get's the fully sized buffer stored in memory
36     public var fullSizedByteArray: [UInt8] {
37         let ptr = UnsafeBufferPointer(start: _bb.memory.assumingMemoryBound(to: UInt8.self),
38                                       count: _bb.capacity)
39         return Array(ptr)
40     }
41     /// Returns the written size of the buffer
42     public var sizedByteArray: [UInt8] {
43         let cp = _bb.capacity - _bb.writerIndex
44         let start = _bb.memory.advanced(by: _bb.writerIndex)
45             .bindMemory(to: UInt8.self, capacity: cp)
46 
47         let ptr = UnsafeBufferPointer(start: start, count: cp)
48         return Array(ptr)
49     }
50     /// Returns the buffer
51     public var buffer: ByteBuffer { return _bb }
52 
53     /// Returns A sized Buffer from the readable bytes
54     public var sizedBuffer: ByteBuffer {
55         assert(finished, "Data shouldn't be called before finish()")
56         return ByteBuffer(memory: _bb.memory.advanced(by: _bb.reader), count: _bb.reader)
57     }
58 
59     // MARK: - Init
60 
61     /// initialize the buffer with a size
62     /// - Parameters:
63     ///   - initialSize: Initial size for the buffer
64     ///   - force: Allows default to be serialized into the buffer
65     public init(initialSize: Int32 = 1024, serializeDefaults force: Bool = false) {
66         assert(initialSize > 0, "Size should be greater than zero!")
67         guard isLitteEndian else {
68             fatalError("Reading/Writing a buffer in big endian machine is not supported on swift")
69         }
70         serializeDefaults = force
71         _bb = ByteBuffer(initialSize: Int(initialSize))
72     }
73 
74     /// Clears the buffer and the builder from it's data
clearnull75     public func clear() {
76         _minAlignment = 0
77         isNested = false
78         _bb.clear()
79         stringOffsetMap = [:]
80     }
81 
82     /// Removes all the offsets from the VTable
clearOffsetsnull83     public func clearOffsets() {
84         _vtable = []
85     }
86 
87     // MARK: - Create Tables
88 
89     /// Checks if the required fields were serialized into the buffer
90     /// - Parameters:
91     ///   - table: offset for the table
92     ///   - fields: Array of all the important fields to be serialized
requirenull93     public func require(table: Offset<UOffset>, fields: [Int32]) {
94         for field in fields {
95             let start = _bb.capacity - Int(table.o)
96             let startTable = start - Int(_bb.read(def: Int32.self, position: start))
97             let isOkay = _bb.read(def: VOffset.self, position: startTable + Int(field)) != 0
98             assert(isOkay, "Flatbuffers requires the following field")
99         }
100     }
101 
102     /// Finished the buffer by adding the file id and then calling finish
103     /// - Parameters:
104     ///   - offset: Offset of the table
105     ///   - fileId: Takes the fileId
106     ///   - prefix: if false it wont add the size of the buffer
finish<T>null107     public func finish<T>(offset: Offset<T>, fileId: String, addPrefix prefix: Bool = false) {
108         let size = MemoryLayout<UOffset>.size
109         preAlign(len: size + (prefix ? size : 0) + FileIdLength, alignment: _minAlignment)
110         assert(fileId.count == FileIdLength, "Flatbuffers requires file id to be 4")
111         _bb.push(string: fileId, len: 4)
112         finish(offset: offset, addPrefix: prefix)
113     }
114 
115     /// Finished the buffer by adding the file id, offset, and prefix to it.
116     /// - Parameters:
117     ///   - offset: Offset of the table
118     ///   - prefix: if false it wont add the size of the buffer
finish<T>null119     public func finish<T>(offset: Offset<T>, addPrefix prefix: Bool = false) {
120         notNested()
121         let size = MemoryLayout<UOffset>.size
122         preAlign(len: size + (prefix ? size : 0), alignment: _minAlignment)
123         push(element: refer(to: offset.o))
124         if prefix { push(element: _bb.size) }
125         clearOffsets()
126         finished = true
127     }
128 
129     /// starttable will let the builder know, that a new object is being serialized.
130     ///
131     /// The function will fatalerror if called while there is another object being serialized
132     /// - Parameter numOfFields: Number of elements to be written to the buffer
startTablenull133     public func startTable(with numOfFields: Int) -> UOffset {
134         notNested()
135         isNested = true
136         _vtable = [UInt32](repeating: 0, count: numOfFields)
137         return _bb.size
138     }
139 
140 
141     /// Endtable will let the builder know that the object that's written to it is completed
142     ///
143     /// This would be called after all the elements are serialized, it will add the vtable into the buffer.
144     /// it will fatalError in case the object is called without starttable, or the object has exceeded  the limit of
145     ///  2GB,
146     /// - Parameter startOffset:Start point of the object written
147     /// - returns: The root of the table
endTablenull148     public func endTable(at startOffset: UOffset)  -> UOffset {
149         assert(isNested, "Calling endtable without calling starttable")
150         let sizeofVoffset = MemoryLayout<VOffset>.size
151         let vTableOffset = push(element: SOffset(0))
152 
153         let tableObjectSize = vTableOffset - startOffset
154         assert(tableObjectSize < 0x10000, "Buffer can't grow beyond 2 Gigabytes")
155 
156         var writeIndex = 0
157         for (index,j) in _vtable.lazy.reversed().enumerated() {
158             if j != 0 {
159                 writeIndex = _vtable.count - index
160                 break
161             }
162         }
163 
164         for i in stride(from: writeIndex - 1, to: -1, by: -1) {
165             let off = _vtable[i] == 0 ? 0 : vTableOffset - _vtable[i]
166             _bb.push(value: VOffset(off), len: sizeofVoffset)
167         }
168 
169         _bb.push(value: VOffset(tableObjectSize), len: sizeofVoffset)
170         _bb.push(value: (UInt16(writeIndex + 2) * UInt16(sizeofVoffset)), len: sizeofVoffset)
171 
172         clearOffsets()
173         let vt_use = _bb.size
174 
175         var isAlreadyAdded: Int?
176 
177         let vt2 = _bb.memory.advanced(by: _bb.writerIndex)
178         let len2 = vt2.load(fromByteOffset: 0, as: Int16.self)
179 
180         for table in _vtables {
181             let position = _bb.capacity - Int(table)
182             let vt1 = _bb.memory.advanced(by: position)
183             let len1 = _bb.read(def: Int16.self, position: position)
184             if (len2 != len1 || 0 != memcmp(vt1, vt2, Int(len2))) { continue }
185 
186             isAlreadyAdded = Int(table)
187             break
188         }
189 
190         if let offset = isAlreadyAdded {
191             let vTableOff = Int(vTableOffset)
192             let space = _bb.capacity - vTableOff
193             _bb.write(value: Int32(offset - vTableOff), index: space, direct: true)
194             _bb.resize(_bb.capacity - space)
195         } else {
196             _bb.write(value: Int32(vt_use) - Int32(vTableOffset), index: Int(vTableOffset))
197             _vtables.append(_bb.size)
198         }
199         isNested = false
200         return vTableOffset
201     }
202 
203     // MARK: - Builds Buffer
204 
205     /// asserts to see if the object is not nested
notNestednull206     fileprivate func notNested()  {
207         assert(!isNested, "Object serialization must not be nested")
208     }
209 
210     /// Changes the minimuim alignment of the buffer
211     /// - Parameter size: size of the current alignment
minAlignmentnull212     fileprivate func minAlignment(size: Int) {
213         if size > _minAlignment {
214             _minAlignment = size
215         }
216     }
217 
218     /// Gets the padding for the current element
219     /// - Parameters:
220     ///   - bufSize: Current size of the buffer + the offset of the object to be written
221     ///   - elementSize: Element size
paddingnull222     fileprivate func padding(bufSize: UInt32, elementSize: UInt32) -> UInt32 {
223         ((~bufSize) &+ 1) & (elementSize - 1)
224     }
225 
226     /// Prealigns the buffer before writting a new object into the buffer
227     /// - Parameters:
228     ///   - len:Length of the object
229     ///   - alignment: Alignment type
preAlignnull230     fileprivate func preAlign(len: Int, alignment: Int) {
231         minAlignment(size: alignment)
232         _bb.fill(padding: padding(bufSize: _bb.size + UOffset(len), elementSize: UOffset(alignment)))
233     }
234 
235     /// Prealigns the buffer before writting a new object into the buffer
236     /// - Parameters:
237     ///   - len: Length of the object
238     ///   - type: Type of the object to be written
preAlign<T: Scalar>null239     fileprivate func preAlign<T: Scalar>(len: Int, type: T.Type) {
240         preAlign(len: len, alignment: MemoryLayout<T>.size)
241     }
242 
243     /// Refers to an object that's written in the buffer
244     /// - Parameter off: the objects index value
refernull245     fileprivate func refer(to off: UOffset) -> UOffset {
246         let size = MemoryLayout<UOffset>.size
247         preAlign(len: size, alignment: size)
248         return _bb.size - off + UInt32(size)
249     }
250 
251     /// Tracks the elements written into the buffer
252     /// - Parameters:
253     ///   - offset: The offset of the element witten
254     ///   - position: The position of the element
tracknull255     fileprivate func track(offset: UOffset, at position: VOffset) {
256         _vtable[Int(position)] = offset
257     }
258 
259     // MARK: - Vectors
260 
261     /// Starts a vector of length and Element size
startVectornull262     public func startVector(_ len: Int, elementSize: Int) {
263         notNested()
264         isNested = true
265         preAlign(len: len * elementSize, type: UOffset.self)
266         preAlign(len: len * elementSize, alignment: elementSize)
267     }
268 
269     /// Ends the vector of at length
270     ///
271     /// The current function will fatalError if startVector is called before serializing the vector
272     /// - Parameter len: Length of the buffer
endVectornull273     public func endVector(len: Int) -> UOffset {
274         assert(isNested, "Calling endVector without calling startVector")
275         isNested = false
276         return push(element: Int32(len))
277     }
278 
279     /// Creates a vector of type Scalar in the buffer
280     /// - Parameter elements: elements to be written into the buffer
281     /// - returns: Offset of the vector
createVector<T: Scalar>null282     public func createVector<T: Scalar>(_ elements: [T]) -> Offset<UOffset> {
283         return createVector(elements, size: elements.count)
284     }
285 
286     ///  Creates a vector of type Scalar in the buffer
287     /// - Parameter elements: Elements to be written into the buffer
288     /// - Parameter size: Count of elements
289     /// - returns: Offset of the vector
createVector<T: Scalar>null290     public func createVector<T: Scalar>(_ elements: [T], size: Int) -> Offset<UOffset> {
291         let size = size
292         startVector(size, elementSize: MemoryLayout<T>.size)
293         _bb.push(elements: elements)
294         return Offset(offset: endVector(len: size))
295     }
296 
297     /// Creates a vector of type Enums in the buffer
298     /// - Parameter elements: elements to be written into the buffer
299     /// - returns: Offset of the vector
createVector<T: Enum>null300     public func createVector<T: Enum>(_ elements: [T]) -> Offset<UOffset> {
301         return createVector(elements, size: elements.count)
302     }
303 
304     ///  Creates a vector of type Enums in the buffer
305     /// - Parameter elements: Elements to be written into the buffer
306     /// - Parameter size: Count of elements
307     /// - returns: Offset of the vector
createVector<T: Enum>null308     public func createVector<T: Enum>(_ elements: [T], size: Int) -> Offset<UOffset> {
309         let size = size
310         startVector(size, elementSize: T.byteSize)
311         for e in elements.lazy.reversed() {
312             _bb.push(value: e.value, len: T.byteSize)
313         }
314         return Offset(offset: endVector(len: size))
315     }
316 
317     /// Creates a vector of type Offsets  in the buffer
318     /// - Parameter offsets:Array of offsets of type T
319     /// - returns: Offset of the vector
createVector<T>null320     public func createVector<T>(ofOffsets offsets: [Offset<T>]) -> Offset<UOffset> {
321         createVector(ofOffsets: offsets, len: offsets.count)
322     }
323 
324     ///  Creates a vector of type Offsets  in the buffer
325     /// - Parameter elements: Array of offsets of type T
326     /// - Parameter size: Count of elements
327     /// - returns: Offset of the vector
createVector<T>null328     public func createVector<T>(ofOffsets offsets: [Offset<T>], len: Int) -> Offset<UOffset> {
329         startVector(len, elementSize: MemoryLayout<Offset<T>>.size)
330         for o in offsets.lazy.reversed() {
331             push(element: o)
332         }
333         return Offset(offset: endVector(len: len))
334     }
335 
336     /// Creates a vector of Strings
337     /// - Parameter str: a vector of strings that will be written into the buffer
338     /// - returns: Offset of the vector
createVectornull339     public func createVector(ofStrings str: [String]) -> Offset<UOffset> {
340         var offsets: [Offset<String>] = []
341         for s in str {
342             offsets.append(create(string: s))
343         }
344         return createVector(ofOffsets: offsets)
345     }
346 
347     /// Creates a vector of Flatbuffer structs.
348     ///
349     /// The function takes a Type to know what size it is, and alignment
350     /// - Parameters:
351     ///   - structs: An array of UnsafeMutableRawPointer
352     ///   - type: Type of the struct being written
353     /// - returns: Offset of the vector
354     public func createVector<T: Readable>(structs: [UnsafeMutableRawPointer],
355                                           type: T.Type) -> Offset<UOffset> {
356         startVector(structs.count * T.size, elementSize: T.alignment)
357         for i in structs.lazy.reversed() {
358             create(struct: i, type: T.self)
359         }
360         return Offset(offset: endVector(len: structs.count))
361     }
362 
363     // MARK: - Inserting Structs
364 
365     /// Writes a Flatbuffer struct into the buffer
366     /// - Parameters:
367     ///   - s: Flatbuffer struct
368     ///   - type: Type of the element to be serialized
369     /// - returns: Offset of the Object
370     @discardableResult
371     public func create<T: Readable>(struct s: UnsafeMutableRawPointer,
372                                     type: T.Type) -> Offset<UOffset> {
373         let size = T.size
374         preAlign(len: size, alignment: T.alignment)
375         _bb.push(struct: s, size: size)
376         return Offset(offset: _bb.size)
377     }
378 
379     /// Adds the offset of a struct into the vTable
380     ///
381     /// The function fatalErrors if we pass an offset that is out of range
382     /// - Parameter o: offset
addnull383     public func add(structOffset o: UOffset) {
384         guard Int(o) < _vtable.count else { fatalError("Out of the table range") }
385         _vtable[Int(o)] = _bb.size
386     }
387 
388     // MARK: - Inserting Strings
389 
390     /// Insets a string into the buffer using UTF8
391     /// - Parameter str: String to be serialized
392     /// - returns: The strings offset in the buffer
createnull393     public func create(string str: String) -> Offset<String> {
394         let len = str.count
395         notNested()
396         preAlign(len: len + 1, type: UOffset.self)
397         _bb.fill(padding: 1)
398         _bb.push(string: str, len: len)
399         push(element: UOffset(len))
400         return Offset(offset: _bb.size)
401     }
402 
403     /// Inserts a shared string to the buffer
404     ///
405     /// The function checks the stringOffsetmap if it's seen a similar string before
406     /// - Parameter str: String to be serialized
407     /// - returns: The strings offset in the buffer
createSharednull408     public func createShared(string str: String) -> Offset<String> {
409         if let offset = stringOffsetMap[str] {
410             return offset
411         }
412         let offset = create(string: str)
413         stringOffsetMap[str] = offset
414         return offset
415     }
416 
417     // MARK: - Inseting offsets
418 
419     /// Adds the offset of an object into the buffer
420     /// - Parameters:
421     ///   - offset: Offset of another object to be written
422     ///   - position: The  predefined position of the object
add<T>null423     public func add<T>(offset: Offset<T>, at position: VOffset) {
424         if offset.isEmpty {
425             track(offset: 0, at: position)
426             return
427         }
428         add(element: refer(to: offset.o), def: 0, at: position)
429     }
430 
431     /// Pushes a value of type offset into the buffer
432     /// - Parameter o: Offset
433     /// - returns: Position of the offset
434     @discardableResult
push<T>null435     public func push<T>(element o: Offset<T>) -> UOffset {
436         push(element: refer(to: o.o))
437     }
438 
439     // MARK: - Inserting Scalars to Buffer
440 
441     /// Adds a value into the buffer of type Scalar
442     ///
443     /// - Parameters:
444     ///   - element: Element to insert
445     ///   - def: Default value for that element
446     ///   - position: The predefined position of the element
add<T: Scalar>null447     public func add<T: Scalar>(element: T, def: T, at position: VOffset) {
448         if (element == def && !serializeDefaults) {
449             track(offset: 0, at: position)
450             return
451         }
452         let off = push(element: element)
453         track(offset: off, at: position)
454     }
455 
456     /// Adds Boolean values into the buffer
457     /// - Parameters:
458     ///   - condition: Condition to insert
459     ///   - def: Default condition
460     ///   - position: The predefined position of the element
addnull461     public func add(condition: Bool, def: Bool, at position: VOffset) {
462         if (condition == def && !serializeDefaults) {
463             track(offset: 0, at: position)
464             return
465         }
466         let off = push(element: Byte(condition ? 1 : 0))
467         track(offset: off, at: position)
468     }
469 
470     /// Pushes the values into the buffer
471     /// - Parameter element: Element to insert
472     /// - returns: Postion of the Element
473     @discardableResult
push<T: Scalar>null474     public func push<T: Scalar>(element: T) -> UOffset {
475         preAlign(len: MemoryLayout<T>.size,
476                  alignment: MemoryLayout<T>.size)
477         _bb.push(value: element, len: MemoryLayout<T>.size)
478         return _bb.size
479     }
480 }
481 
482 extension FlatBufferBuilder: CustomDebugStringConvertible {
483 
484     public var debugDescription: String {
485         """
486         buffer debug:
487         \(_bb)
488         builder debug:
489         { finished: \(finished), serializeDefaults: \(serializeDefaults), isNested: \(isNested) }
490         """
491     }
492 }
493