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