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