xref: /aosp_15_r20/external/flatbuffers/swift/Sources/FlatBuffers/FlatBufferBuilder.swift (revision 890232f25432b36107d06881e0a25aaa6b473652)
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 #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   /// Creates a vector of type ``Enum`` into the ``ByteBuffer``
477   ///
478   /// ``createVector(_:)-9h189`` writes a vector of type ``Enum`` into
479   /// ``ByteBuffer``. This is a convenient method instead of calling,
480   /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``
481   /// ```swift
482   /// let vectorOffset = builder.
483   ///   createVector([.swift, .cpp])
484   /// ```
485   ///
486   /// The underlying implementation simply calls ``createVector(_:size:)-7cx6z``
487   ///
488   /// - Parameter elements: elements to be written into the buffer
489   /// - returns: ``Offset`` of the vector
490   @inline(__always)
createVector<T: Enum>null491   mutating public func createVector<T: Enum>(_ elements: [T]) -> Offset {
492     createVector(elements, size: elements.count)
493   }
494 
495   /// Creates a vector of type ``Enum`` into the ``ByteBuffer``
496   ///
497   /// ``createVector(_:)-9h189`` writes a vector of type ``Enum`` into
498   /// ``ByteBuffer``. This is a convenient method instead of calling,
499   /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``
500   /// ```swift
501   /// let vectorOffset = builder.
502   ///   createVector([.swift, .cpp])
503   /// ```
504   ///
505   /// - Parameter elements: Elements to be written into the buffer
506   /// - Parameter size: Count of elements
507   /// - returns: ``Offset`` of the vector
508   @inline(__always)
509   mutating public func createVector<T: Enum>(
510     _ elements: [T],
511     size: Int) -> Offset
512   {
513     let size = size
514     startVector(size, elementSize: T.byteSize)
515     for e in elements.reversed() {
516       _bb.push(value: e.value, len: T.byteSize)
517     }
518     return endVector(len: size)
519   }
520 
521   /// Creates a vector of already written offsets
522   ///
523   /// ``createVector(ofOffsets:)`` creates a vector of ``Offset`` into
524   /// ``ByteBuffer``. This is a convenient method instead of calling,
525   /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``.
526   ///
527   /// The underlying implementation simply calls ``createVector(ofOffsets:len:)``
528   ///
529   /// ```swift
530   /// let namesOffsets = builder.
531   ///   createVector(ofOffsets: [name1, name2])
532   /// ```
533   /// - Parameter offsets: Array of offsets of type ``Offset``
534   /// - returns: ``Offset`` of the vector
535   @inline(__always)
createVectornull536   mutating public func createVector(ofOffsets offsets: [Offset]) -> Offset {
537     createVector(ofOffsets: offsets, len: offsets.count)
538   }
539 
540   /// Creates a vector of already written offsets
541   ///
542   /// ``createVector(ofOffsets:)`` creates a vector of ``Offset`` into
543   /// ``ByteBuffer``. This is a convenient method instead of calling,
544   /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``
545   ///
546   /// ```swift
547   /// let namesOffsets = builder.
548   ///   createVector(ofOffsets: [name1, name2])
549   /// ```
550   ///
551   /// - Parameter offsets: Array of offsets of type ``Offset``
552   /// - Parameter size: Count of elements
553   /// - returns: ``Offset`` of the vector
554   @inline(__always)
555   mutating public func createVector(
556     ofOffsets offsets: [Offset],
557     len: Int) -> Offset
558   {
559     startVector(len, elementSize: MemoryLayout<Offset>.size)
560     for o in offsets.reversed() {
561       push(element: o)
562     }
563     return endVector(len: len)
564   }
565 
566   /// Creates a vector of strings
567   ///
568   /// ``createVector(ofStrings:)`` creates a vector of `String` into
569   /// ``ByteBuffer``. This is a convenient method instead of manually
570   /// creating the string offsets, you simply pass it to this function
571   /// and it would write the strings into the ``ByteBuffer``.
572   /// After that it calls ``createVector(ofOffsets:)``
573   ///
574   /// ```swift
575   /// let namesOffsets = builder.
576   ///   createVector(ofStrings: ["Name", "surname"])
577   /// ```
578   ///
579   /// - Parameter str: Array of string
580   /// - returns: ``Offset`` of the vector
581   @inline(__always)
createVectornull582   mutating public func createVector(ofStrings str: [String]) -> Offset {
583     var offsets: [Offset] = []
584     for s in str {
585       offsets.append(create(string: s))
586     }
587     return createVector(ofOffsets: offsets)
588   }
589 
590   /// Creates a vector of type ``NativeStruct``.
591   ///
592   /// Any swift struct in the generated code, should confirm to
593   /// ``NativeStruct``. Since the generated swift structs are padded
594   /// to the `FlatBuffers` standards.
595   ///
596   /// ```swift
597   /// let offsets = builder.
598   ///   createVector(ofStructs: [NativeStr(num: 1), NativeStr(num: 2)])
599   /// ```
600   ///
601   /// - Parameter structs: A vector of ``NativeStruct``
602   /// - Returns: ``Offset`` of the vector
603   @inline(__always)
createVector<T: NativeStruct>null604   mutating public func createVector<T: NativeStruct>(ofStructs structs: [T])
605     -> Offset
606   {
607     startVector(
608       structs.count * MemoryLayout<T>.size,
609       elementSize: MemoryLayout<T>.alignment)
610     for i in structs.reversed() {
611       _ = create(struct: i)
612     }
613     return endVector(len: structs.count)
614   }
615 
616   // MARK: - Inserting Structs
617 
618   /// Writes a ``NativeStruct`` into the ``ByteBuffer``
619   ///
620   /// Adds a native struct that's build and padded according
621   /// to `FlatBuffers` standards. with a predefined position.
622   ///
623   /// ```swift
624   /// let offset = builder.create(
625   ///   struct: NativeStr(num: 1),
626   ///   position: 10)
627   /// ```
628   ///
629   /// - Parameters:
630   ///   - s: ``NativeStruct`` to be inserted into the ``ByteBuffer``
631   ///   - position: The  predefined position of the object
632   /// - Returns: ``Offset`` of written struct
633   @inline(__always)
634   @discardableResult
635   mutating public func create<T: NativeStruct>(
636     struct s: T, position: VOffset) -> Offset
637   {
638     let offset = create(struct: s)
639     _vtableStorage.add(loc: FieldLoc(
640       offset: _bb.size,
641       position: VOffset(position)))
642     return offset
643   }
644 
645   /// Writes a ``NativeStruct`` into the ``ByteBuffer``
646   ///
647   /// Adds a native struct that's build and padded according
648   /// to `FlatBuffers` standards, directly into the buffer without
649   /// a predefined position.
650   ///
651   /// ```swift
652   /// let offset = builder.create(
653   ///   struct: NativeStr(num: 1))
654   /// ```
655   ///
656   /// - Parameters:
657   ///   - s: ``NativeStruct`` to be inserted into the ``ByteBuffer``
658   /// - Returns: ``Offset`` of written struct
659   @inline(__always)
660   @discardableResult
661   mutating public func create<T: NativeStruct>(
662     struct s: T) -> Offset
663   {
664     let size = MemoryLayout<T>.size
665     preAlign(len: size, alignment: MemoryLayout<T>.alignment)
666     _bb.push(struct: s, size: size)
667     return Offset(offset: _bb.size)
668   }
669 
670   // MARK: - Inserting Strings
671 
672   /// Insets a string into the buffer of type `UTF8`
673   ///
674   /// Adds a swift string into ``ByteBuffer`` by encoding it
675   /// using `UTF8`
676   ///
677   /// ```swift
678   /// let nameOffset = builder
679   ///   .create(string: "welcome")
680   /// ```
681   ///
682   /// - Parameter str: String to be serialized
683   /// - returns: ``Offset`` of inserted string
684   @inline(__always)
createnull685   mutating public func create(string str: String?) -> Offset {
686     guard let str = str else { return Offset() }
687     let len = str.utf8.count
688     notNested()
689     preAlign(len: len &+ 1, type: UOffset.self)
690     _bb.fill(padding: 1)
691     _bb.push(string: str, len: len)
692     push(element: UOffset(len))
693     return Offset(offset: _bb.size)
694   }
695 
696   /// Insets a shared string into the buffer of type `UTF8`
697   ///
698   /// Adds a swift string into ``ByteBuffer`` by encoding it
699   /// using `UTF8`. The function will check if the string,
700   /// is already written to the ``ByteBuffer``
701   ///
702   /// ```swift
703   /// let nameOffset = builder
704   ///   .createShared(string: "welcome")
705   ///
706   ///
707   /// let secondOffset = builder
708   ///   .createShared(string: "welcome")
709   ///
710   /// assert(nameOffset.o == secondOffset.o)
711   /// ```
712   ///
713   /// - Parameter str: String to be serialized
714   /// - returns: ``Offset`` of inserted string
715   @inline(__always)
createSharednull716   mutating public func createShared(string str: String?) -> Offset {
717     guard let str = str else { return Offset() }
718     if let offset = stringOffsetMap[str] {
719       return offset
720     }
721     let offset = create(string: str)
722     stringOffsetMap[str] = offset
723     return offset
724   }
725 
726   // MARK: - Inseting offsets
727 
728   /// Writes the ``Offset`` of an already written table
729   ///
730   /// Writes the ``Offset`` of a table if not empty into the
731   /// ``ByteBuffer``
732   ///
733   /// - Parameters:
734   ///   - offset: ``Offset`` of another object to be written
735   ///   - position: The predefined position of the object
736   @inline(__always)
addnull737   mutating public func add(offset: Offset, at position: VOffset) {
738     if offset.isEmpty { return }
739     add(element: refer(to: offset.o), def: 0, at: position)
740   }
741 
742   /// Pushes a value of type ``Offset`` into the ``ByteBuffer``
743   /// - Parameter o: ``Offset``
744   /// - returns: Current position of the ``Offset``
745   @inline(__always)
746   @discardableResult
pushnull747   mutating public func push(element o: Offset) -> UOffset {
748     push(element: refer(to: o.o))
749   }
750 
751   // MARK: - Inserting Scalars to Buffer
752 
753   /// Writes a ``Scalar`` value into ``ByteBuffer``
754   ///
755   /// ``add(element:def:at:)`` takes in a default value, and current value
756   /// and the position within the `VTable`. The default value would not
757   /// be serialized if the value is the same as the current value or
758   /// `serializeDefaults` is equal to false.
759   ///
760   /// If serializing defaults is important ``init(initialSize:serializeDefaults:)``,
761   /// passing true for `serializeDefaults` would do the job.
762   ///
763   /// ```swift
764   /// // Adds 10 to the buffer
765   /// builder.add(element: Int(10), def: 1, position 12)
766   /// ```
767   ///
768   /// *NOTE: Never call this manually*
769   ///
770   /// - Parameters:
771   ///   - element: Element to insert
772   ///   - def: Default value for that element
773   ///   - position: The predefined position of the element
774   @inline(__always)
775   mutating public func add<T: Scalar>(
776     element: T,
777     def: T,
778     at position: VOffset)
779   {
780     if element == def && !serializeDefaults { return }
781     track(offset: push(element: element), at: position)
782   }
783 
784   /// Writes a optional ``Scalar`` value into ``ByteBuffer``
785   ///
786   /// Takes an optional value to be written into the ``ByteBuffer``
787   ///
788   /// *NOTE: Never call this manually*
789   ///
790   /// - Parameters:
791   ///   - element: Optional element of type scalar
792   ///   - position: The predefined position of the element
793   @inline(__always)
add<T: Scalar>null794   mutating public func add<T: Scalar>(element: T?, at position: VOffset) {
795     guard let element = element else { return }
796     track(offset: push(element: element), at: position)
797   }
798 
799   /// Pushes a values of type ``Scalar`` into the ``ByteBuffer``
800   ///
801   /// *NOTE: Never call this manually*
802   ///
803   /// - Parameter element: Element to insert
804   /// - returns: Postion of the Element
805   @inline(__always)
806   @discardableResult
push<T: Scalar>null807   mutating public func push<T: Scalar>(element: T) -> UOffset {
808     let size = MemoryLayout<T>.size
809     preAlign(
810       len: size,
811       alignment: size)
812     _bb.push(value: element, len: size)
813     return _bb.size
814   }
815 
816 }
817 
818 extension FlatBufferBuilder: CustomDebugStringConvertible {
819 
820   public var debugDescription: String {
821     """
822     buffer debug:
823     \(_bb)
824     builder debug:
825     { finished: \(finished), serializeDefaults: \(serializeDefaults), isNested: \(isNested) }
826     """
827   }
828 
829   /// VTableStorage is a class to contain the VTable buffer that would be serialized into buffer
830   @usableFromInline
831   internal class VTableStorage {
832     /// Memory check since deallocating each time we want to clear would be expensive
833     /// and memory leaks would happen if we dont deallocate the first allocated memory.
834     /// memory is promised to be available before adding `FieldLoc`
835     private var memoryInUse = false
836     /// Size of FieldLoc in memory
837     let size = MemoryLayout<FieldLoc>.stride
838     /// Memeory buffer
839     var memory: UnsafeMutableRawBufferPointer!
840     /// Capacity of the current buffer
841     var capacity: Int = 0
842     /// Maximuim offset written to the class
843     var maxOffset: VOffset = 0
844     /// number of fields written into the buffer
845     var numOfFields: Int = 0
846     /// Last written Index
847     var writtenIndex: Int = 0
848 
849     /// Creates the memory to store the buffer in
850     @usableFromInline
851     @inline(__always)
852     init() {
853       memory = UnsafeMutableRawBufferPointer.allocate(
854         byteCount: 0,
855         alignment: 0)
856     }
857 
858     @inline(__always)
859     deinit {
860       memory.deallocate()
861     }
862 
863     /// Builds a buffer with byte count of fieldloc.size * count of field numbers
864     /// - Parameter count: number of fields to be written
865     @inline(__always)
startnull866     func start(count: Int) {
867       assert(count >= 0, "number of fields should NOT be negative")
868       let capacity = count &* size
869       ensure(space: capacity)
870     }
871 
872     /// Adds a FieldLoc into the buffer, which would track how many have been written,
873     /// and max offset
874     /// - Parameter loc: Location of encoded element
875     @inline(__always)
addnull876     func add(loc: FieldLoc) {
877       memory.baseAddress?.advanced(by: writtenIndex).storeBytes(
878         of: loc,
879         as: FieldLoc.self)
880       writtenIndex = writtenIndex &+ size
881       numOfFields = numOfFields &+ 1
882       maxOffset = max(loc.position, maxOffset)
883     }
884 
885     /// Clears the data stored related to the encoded buffer
886     @inline(__always)
clearnull887     func clear() {
888       maxOffset = 0
889       numOfFields = 0
890       writtenIndex = 0
891     }
892 
893     /// Ensure that the buffer has enough space instead of recreating the buffer each time.
894     /// - Parameter space: space required for the new vtable
895     @inline(__always)
ensurenull896     func ensure(space: Int) {
897       guard space &+ writtenIndex > capacity else { return }
898       memory.deallocate()
899       memory = UnsafeMutableRawBufferPointer.allocate(
900         byteCount: space,
901         alignment: size)
902       capacity = space
903     }
904 
905     /// Loads an object of type `FieldLoc` from buffer memory
906     /// - Parameter index: index of element
907     /// - Returns: a FieldLoc at index
908     @inline(__always)
loadnull909     func load(at index: Int) -> FieldLoc {
910       memory.load(fromByteOffset: index, as: FieldLoc.self)
911     }
912 
913   }
914 
915   internal struct FieldLoc {
916     var offset: UOffset
917     var position: VOffset
918   }
919 
920 }
921