blob: 5ccc7e44cb040857a7e39bc1e3a547ece40fa233 [file] [log] [blame]
Austin Schuh58b9b472020-11-25 19:12:44 -08001/*
James Kuszmaul8e62b022022-03-22 09:33:25 -07002 * Copyright 2021 Google Inc. All rights reserved.
Austin Schuh58b9b472020-11-25 19:12:44 -08003 *
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
Austin Schuh272c6132020-11-14 16:37:52 -080017import Foundation
18
James Kuszmaul8e62b022022-03-22 09:33:25 -070019/// ``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
Austin Schuh272c6132020-11-14 16:37:52 -080030public struct FlatBufferBuilder {
Austin Schuh58b9b472020-11-25 19:12:44 -080031
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()
James Kuszmaul8e62b022022-03-22 09:33:25 -070034 /// Flatbuffer data will be written into
35 @usableFromInline internal var _bb: ByteBuffer
Austin Schuh58b9b472020-11-25 19:12:44 -080036
37 /// Reference Vtables that were already written to the buffer
38 private var _vtables: [UOffset] = []
Austin Schuh58b9b472020-11-25 19:12:44 -080039 /// 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
James Kuszmaul8e62b022022-03-22 09:33:25 -070042 private var stringOffsetMap: [String: Offset] = [:]
Austin Schuh58b9b472020-11-25 19:12:44 -080043 /// 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
Austin Schuh272c6132020-11-14 16:37:52 -080052 }
Austin Schuh58b9b472020-11-25 19:12:44 -080053 }
54
55 /// Gives a read access to the buffer's size
56 public var size: UOffset { _bb.size }
James Kuszmaul8e62b022022-03-22 09:33:25 -070057
Austin Schuh58b9b472020-11-25 19:12:44 -080058 /// Data representation of the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -070059 ///
60 /// Should only be used after ``finish(offset:addPrefix:)`` is called
Austin Schuh58b9b472020-11-25 19:12:44 -080061 public var data: Data {
62 assert(finished, "Data shouldn't be called before finish()")
63 return Data(
64 bytes: _bb.memory.advanced(by: _bb.writerIndex),
65 count: _bb.capacity &- _bb.writerIndex)
66 }
James Kuszmaul8e62b022022-03-22 09:33:25 -070067
68 /// Returns the underlying bytes in the ``ByteBuffer``
69 ///
70 /// Note: This should be used with caution.
Austin Schuh58b9b472020-11-25 19:12:44 -080071 public var fullSizedByteArray: [UInt8] {
72 let ptr = UnsafeBufferPointer(
73 start: _bb.memory.assumingMemoryBound(to: UInt8.self),
74 count: _bb.capacity)
75 return Array(ptr)
76 }
James Kuszmaul8e62b022022-03-22 09:33:25 -070077
78 /// Returns the written bytes into the ``ByteBuffer``
79 ///
80 /// Should only be used after ``finish(offset:addPrefix:)`` is called
Austin Schuh58b9b472020-11-25 19:12:44 -080081 public var sizedByteArray: [UInt8] {
82 assert(finished, "Data shouldn't be called before finish()")
James Kuszmaul8e62b022022-03-22 09:33:25 -070083 return _bb.underlyingBytes
Austin Schuh58b9b472020-11-25 19:12:44 -080084 }
James Kuszmaul8e62b022022-03-22 09:33:25 -070085
86 /// Returns the original ``ByteBuffer``
87 ///
88 /// Returns the current buffer that was just created
89 /// with the offsets, and data written to it.
Austin Schuh58b9b472020-11-25 19:12:44 -080090 public var buffer: ByteBuffer { _bb }
91
James Kuszmaul8e62b022022-03-22 09:33:25 -070092 /// Returns a newly created sized ``ByteBuffer``
93 ///
94 /// returns a new buffer that is sized to the data written
95 /// to the main buffer
Austin Schuh58b9b472020-11-25 19:12:44 -080096 public var sizedBuffer: ByteBuffer {
97 assert(finished, "Data shouldn't be called before finish()")
James Kuszmaul8e62b022022-03-22 09:33:25 -070098 return ByteBuffer(
99 memory: _bb.memory.advanced(by: _bb.reader),
100 count: Int(_bb.size))
Austin Schuh58b9b472020-11-25 19:12:44 -0800101 }
102
103 // MARK: - Init
104
James Kuszmaul8e62b022022-03-22 09:33:25 -0700105 /// Initialize the buffer with a size
Austin Schuh58b9b472020-11-25 19:12:44 -0800106 /// - Parameters:
107 /// - initialSize: Initial size for the buffer
108 /// - force: Allows default to be serialized into the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700109 ///
110 /// This initializes a new builder with an initialSize that would initialize
111 /// a new ``ByteBuffer``. ``FlatBufferBuilder`` by default doesnt serialize defaults
112 /// however the builder can be force by passing true for `serializeDefaults`
113 public init(
114 initialSize: Int32 = 1024,
115 serializeDefaults force: Bool = false)
116 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800117 assert(initialSize > 0, "Size should be greater than zero!")
118 guard isLitteEndian else {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700119 fatalError(
120 "Reading/Writing a buffer in big endian machine is not supported on swift")
Austin Schuh272c6132020-11-14 16:37:52 -0800121 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800122 serializeDefaults = force
123 _bb = ByteBuffer(initialSize: Int(initialSize))
124 }
125
James Kuszmaul8e62b022022-03-22 09:33:25 -0700126 /// Clears the builder and the buffer from the written data.
Austin Schuh58b9b472020-11-25 19:12:44 -0800127 mutating public func clear() {
128 _minAlignment = 0
129 isNested = false
130 stringOffsetMap = [:]
131 _vtables = []
132 _vtableStorage.clear()
133 _bb.clear()
134 }
135
136 // MARK: - Create Tables
137
138 /// Checks if the required fields were serialized into the buffer
139 /// - Parameters:
140 /// - table: offset for the table
141 /// - fields: Array of all the important fields to be serialized
James Kuszmaul8e62b022022-03-22 09:33:25 -0700142 ///
143 /// *NOTE: Never call this function, this is only supposed to be called
144 /// by the generated code*
145 mutating public func require(table: Offset, fields: [Int32]) {
Austin Schuh58b9b472020-11-25 19:12:44 -0800146 for field in fields {
147 let start = _bb.capacity &- Int(table.o)
148 let startTable = start &- Int(_bb.read(def: Int32.self, position: start))
James Kuszmaul8e62b022022-03-22 09:33:25 -0700149 let isOkay = _bb.read(
150 def: VOffset.self,
151 position: startTable &+ Int(field)) != 0
Austin Schuh58b9b472020-11-25 19:12:44 -0800152 assert(isOkay, "Flatbuffers requires the following field")
Austin Schuh272c6132020-11-14 16:37:52 -0800153 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800154 }
155
156 /// Finished the buffer by adding the file id and then calling finish
157 /// - Parameters:
158 /// - offset: Offset of the table
159 /// - fileId: Takes the fileId
160 /// - prefix: if false it wont add the size of the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700161 ///
162 /// ``finish(offset:fileId:addPrefix:)`` should be called at the end of creating
163 /// a table
164 /// ```swift
165 /// var root = SomeObject
166 /// .createObject(&builder,
167 /// name: nameOffset)
168 /// builder.finish(
169 /// offset: root,
170 /// fileId: "ax1a",
171 /// addPrefix: true)
172 /// ```
173 /// File id would append a file id name at the end of the written bytes before,
174 /// finishing the buffer.
175 ///
176 /// Whereas, if `addPrefix` is true, the written bytes would
177 /// include the size of the current buffer.
178 mutating public func finish(
179 offset: Offset,
180 fileId: String,
181 addPrefix prefix: Bool = false)
182 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800183 let size = MemoryLayout<UOffset>.size
James Kuszmaul8e62b022022-03-22 09:33:25 -0700184 preAlign(
185 len: size &+ (prefix ? size : 0) &+ FileIdLength,
186 alignment: _minAlignment)
Austin Schuh58b9b472020-11-25 19:12:44 -0800187 assert(fileId.count == FileIdLength, "Flatbuffers requires file id to be 4")
188 _bb.push(string: fileId, len: 4)
189 finish(offset: offset, addPrefix: prefix)
190 }
191
192 /// Finished the buffer by adding the file id, offset, and prefix to it.
193 /// - Parameters:
194 /// - offset: Offset of the table
195 /// - prefix: if false it wont add the size of the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700196 ///
197 /// ``finish(offset:addPrefix:)`` should be called at the end of creating
198 /// a table
199 /// ```swift
200 /// var root = SomeObject
201 /// .createObject(&builder,
202 /// name: nameOffset)
203 /// builder.finish(
204 /// offset: root,
205 /// addPrefix: true)
206 /// ```
207 /// If `addPrefix` is true, the written bytes would
208 /// include the size of the current buffer.
209 mutating public func finish(
210 offset: Offset,
211 addPrefix prefix: Bool = false)
212 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800213 notNested()
214 let size = MemoryLayout<UOffset>.size
215 preAlign(len: size &+ (prefix ? size : 0), alignment: _minAlignment)
216 push(element: refer(to: offset.o))
217 if prefix { push(element: _bb.size) }
218 _vtableStorage.clear()
219 finished = true
220 }
221
James Kuszmaul8e62b022022-03-22 09:33:25 -0700222 /// ``startTable(with:)`` will let the builder know, that a new object is being serialized.
Austin Schuh58b9b472020-11-25 19:12:44 -0800223 ///
James Kuszmaul8e62b022022-03-22 09:33:25 -0700224 /// The function will fatalerror if called while there is another object being serialized.
225 /// ```swift
226 /// let start = Monster
227 /// .startMonster(&fbb)
228 /// ```
Austin Schuh58b9b472020-11-25 19:12:44 -0800229 /// - Parameter numOfFields: Number of elements to be written to the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700230 /// - Returns: Offset of the newly started table
Austin Schuh58b9b472020-11-25 19:12:44 -0800231 mutating public func startTable(with numOfFields: Int) -> UOffset {
232 notNested()
233 isNested = true
234 _vtableStorage.start(count: numOfFields)
235 return _bb.size
236 }
237
James Kuszmaul8e62b022022-03-22 09:33:25 -0700238 /// ``endTable(at:)`` will let the ``FlatBufferBuilder`` know that the
239 /// object that's written to it is completed
Austin Schuh58b9b472020-11-25 19:12:44 -0800240 ///
James Kuszmaul8e62b022022-03-22 09:33:25 -0700241 /// This would be called after all the elements are serialized,
242 /// it will add the current vtable into the ``ByteBuffer``.
243 /// The functions will `fatalError` in case the object is called
244 /// without ``startTable(with:)``, or the object has exceeded the limit of 2GB.
245 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800246 /// - Parameter startOffset:Start point of the object written
247 /// - returns: The root of the table
248 mutating public func endTable(at startOffset: UOffset) -> UOffset {
249 assert(isNested, "Calling endtable without calling starttable")
250 let sizeofVoffset = MemoryLayout<VOffset>.size
251 let vTableOffset = push(element: SOffset(0))
252
253 let tableObjectSize = vTableOffset &- startOffset
254 assert(tableObjectSize < 0x10000, "Buffer can't grow beyond 2 Gigabytes")
255 let _max = Int(_vtableStorage.maxOffset) &+ sizeofVoffset
256
257 _bb.fill(padding: _max)
258 _bb.write(
259 value: VOffset(tableObjectSize),
260 index: _bb.writerIndex &+ sizeofVoffset,
261 direct: true)
262 _bb.write(value: VOffset(_max), index: _bb.writerIndex, direct: true)
263
264 var itr = 0
265 while itr < _vtableStorage.writtenIndex {
266 let loaded = _vtableStorage.load(at: itr)
267 itr = itr &+ _vtableStorage.size
268 guard loaded.offset != 0 else { continue }
269 let _index = (_bb.writerIndex &+ Int(loaded.position))
James Kuszmaul8e62b022022-03-22 09:33:25 -0700270 _bb.write(
271 value: VOffset(vTableOffset &- loaded.offset),
272 index: _index,
273 direct: true)
Austin Schuh272c6132020-11-14 16:37:52 -0800274 }
275
Austin Schuh58b9b472020-11-25 19:12:44 -0800276 _vtableStorage.clear()
277 let vt_use = _bb.size
Austin Schuh272c6132020-11-14 16:37:52 -0800278
Austin Schuh58b9b472020-11-25 19:12:44 -0800279 var isAlreadyAdded: Int?
280
281 let vt2 = _bb.memory.advanced(by: _bb.writerIndex)
282 let len2 = vt2.load(fromByteOffset: 0, as: Int16.self)
283
284 for table in _vtables {
285 let position = _bb.capacity &- Int(table)
286 let vt1 = _bb.memory.advanced(by: position)
287 let len1 = _bb.read(def: Int16.self, position: position)
288 if len2 != len1 || 0 != memcmp(vt1, vt2, Int(len2)) { continue }
289
290 isAlreadyAdded = Int(table)
291 break
Austin Schuh272c6132020-11-14 16:37:52 -0800292 }
293
Austin Schuh58b9b472020-11-25 19:12:44 -0800294 if let offset = isAlreadyAdded {
295 let vTableOff = Int(vTableOffset)
296 let space = _bb.capacity &- vTableOff
297 _bb.write(value: Int32(offset &- vTableOff), index: space, direct: true)
298 _bb.pop(_bb.capacity &- space)
299 } else {
300 _bb.write(value: Int32(vt_use &- vTableOffset), index: Int(vTableOffset))
301 _vtables.append(_bb.size)
Austin Schuh272c6132020-11-14 16:37:52 -0800302 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800303 isNested = false
304 return vTableOffset
305 }
Austin Schuh272c6132020-11-14 16:37:52 -0800306
Austin Schuh58b9b472020-11-25 19:12:44 -0800307 // MARK: - Builds Buffer
308
James Kuszmaul8e62b022022-03-22 09:33:25 -0700309 /// Asserts to see if the object is not nested
Austin Schuh58b9b472020-11-25 19:12:44 -0800310 @usableFromInline
311 mutating internal func notNested() {
312 assert(!isNested, "Object serialization must not be nested")
313 }
314
315 /// Changes the minimuim alignment of the buffer
316 /// - Parameter size: size of the current alignment
James Kuszmaul8e62b022022-03-22 09:33:25 -0700317 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800318 mutating internal func minAlignment(size: Int) {
319 if size > _minAlignment {
320 _minAlignment = size
Austin Schuh272c6132020-11-14 16:37:52 -0800321 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800322 }
323
324 /// Gets the padding for the current element
325 /// - Parameters:
326 /// - bufSize: Current size of the buffer + the offset of the object to be written
327 /// - elementSize: Element size
James Kuszmaul8e62b022022-03-22 09:33:25 -0700328 @inline(__always)
329 mutating internal func padding(
330 bufSize: UInt32,
331 elementSize: UInt32) -> UInt32
332 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800333 ((~bufSize) &+ 1) & (elementSize - 1)
334 }
335
336 /// Prealigns the buffer before writting a new object into the buffer
337 /// - Parameters:
338 /// - len:Length of the object
339 /// - alignment: Alignment type
340 @usableFromInline
341 mutating internal func preAlign(len: Int, alignment: Int) {
342 minAlignment(size: alignment)
343 _bb.fill(padding: Int(padding(
344 bufSize: _bb.size &+ UOffset(len),
345 elementSize: UOffset(alignment))))
346 }
347
348 /// Prealigns the buffer before writting a new object into the buffer
349 /// - Parameters:
350 /// - len: Length of the object
351 /// - type: Type of the object to be written
352 @usableFromInline
353 mutating internal func preAlign<T: Scalar>(len: Int, type: T.Type) {
354 preAlign(len: len, alignment: MemoryLayout<T>.size)
355 }
356
357 /// Refers to an object that's written in the buffer
358 /// - Parameter off: the objects index value
359 @usableFromInline
360 mutating internal func refer(to off: UOffset) -> UOffset {
361 let size = MemoryLayout<UOffset>.size
362 preAlign(len: size, alignment: size)
363 return _bb.size &- off &+ UInt32(size)
364 }
365
366 /// Tracks the elements written into the buffer
367 /// - Parameters:
368 /// - offset: The offset of the element witten
369 /// - position: The position of the element
370 @usableFromInline
371 mutating internal func track(offset: UOffset, at position: VOffset) {
372 _vtableStorage.add(loc: FieldLoc(offset: offset, position: position))
373 }
374
James Kuszmaul8e62b022022-03-22 09:33:25 -0700375 // MARK: - Inserting Vectors
Austin Schuh58b9b472020-11-25 19:12:44 -0800376
James Kuszmaul8e62b022022-03-22 09:33:25 -0700377 /// ``startVector(_:elementSize:)`` creates a new vector within buffer
378 ///
379 /// The function checks if there is a current object being written, if
380 /// the check passes it creates a buffer alignment of `length * elementSize`
381 /// ```swift
382 /// builder.startVector(
383 /// int32Values.count, elementSize: 4)
384 /// ```
385 ///
386 /// - Parameters:
387 /// - len: Length of vector to be created
388 /// - elementSize: Size of object type to be written
Austin Schuh58b9b472020-11-25 19:12:44 -0800389 mutating public func startVector(_ len: Int, elementSize: Int) {
390 notNested()
391 isNested = true
392 preAlign(len: len &* elementSize, type: UOffset.self)
393 preAlign(len: len &* elementSize, alignment: elementSize)
394 }
395
James Kuszmaul8e62b022022-03-22 09:33:25 -0700396 /// ``endVector(len:)`` ends the currently created vector
Austin Schuh58b9b472020-11-25 19:12:44 -0800397 ///
James Kuszmaul8e62b022022-03-22 09:33:25 -0700398 /// Calling ``endVector(len:)`` requires the length, of the current
399 /// vector. The length would be pushed to indicate the count of numbers
400 /// within the vector. If ``endVector(len:)`` is called without
401 /// ``startVector(_:elementSize:)`` it asserts.
402 ///
403 /// ```swift
404 /// let vectorOffset = builder.
405 /// endVector(len: int32Values.count)
406 /// ```
407 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800408 /// - Parameter len: Length of the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700409 /// - Returns: Returns the current ``Offset`` in the ``ByteBuffer``
410 mutating public func endVector(len: Int) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800411 assert(isNested, "Calling endVector without calling startVector")
412 isNested = false
James Kuszmaul8e62b022022-03-22 09:33:25 -0700413 return Offset(offset: push(element: Int32(len)))
Austin Schuh58b9b472020-11-25 19:12:44 -0800414 }
415
James Kuszmaul8e62b022022-03-22 09:33:25 -0700416 /// Creates a vector of type ``Scalar`` into the ``ByteBuffer``
417 ///
418 /// ``createVector(_:)-4swl0`` writes a vector of type Scalars into
419 /// ``ByteBuffer``. This is a convenient method instead of calling,
420 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``
421 /// ```swift
422 /// let vectorOffset = builder.
423 /// createVector([1, 2, 3, 4])
424 /// ```
425 ///
426 /// The underlying implementation simply calls ``createVector(_:size:)-4lhrv``
427 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800428 /// - Parameter elements: elements to be written into the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700429 /// - returns: ``Offset`` of the vector
430 mutating public func createVector<T: Scalar>(_ elements: [T]) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800431 createVector(elements, size: elements.count)
432 }
433
434 /// Creates a vector of type Scalar in the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700435 ///
436 /// ``createVector(_:)-4swl0`` writes a vector of type Scalars into
437 /// ``ByteBuffer``. This is a convenient method instead of calling,
438 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``
439 /// ```swift
440 /// let vectorOffset = builder.
441 /// createVector([1, 2, 3, 4], size: 4)
442 /// ```
443 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800444 /// - Parameter elements: Elements to be written into the buffer
445 /// - Parameter size: Count of elements
James Kuszmaul8e62b022022-03-22 09:33:25 -0700446 /// - returns: ``Offset`` of the vector
447 mutating public func createVector<T: Scalar>(
448 _ elements: [T],
449 size: Int) -> Offset
450 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800451 let size = size
452 startVector(size, elementSize: MemoryLayout<T>.size)
453 _bb.push(elements: elements)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700454 return endVector(len: size)
Austin Schuh58b9b472020-11-25 19:12:44 -0800455 }
456
James Kuszmaul8e62b022022-03-22 09:33:25 -0700457 /// Creates a vector of type ``Enum`` into the ``ByteBuffer``
458 ///
459 /// ``createVector(_:)-9h189`` writes a vector of type ``Enum`` into
460 /// ``ByteBuffer``. This is a convenient method instead of calling,
461 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``
462 /// ```swift
463 /// let vectorOffset = builder.
464 /// createVector([.swift, .cpp])
465 /// ```
466 ///
467 /// The underlying implementation simply calls ``createVector(_:size:)-7cx6z``
468 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800469 /// - Parameter elements: elements to be written into the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700470 /// - returns: ``Offset`` of the vector
471 mutating public func createVector<T: Enum>(_ elements: [T]) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800472 createVector(elements, size: elements.count)
473 }
474
James Kuszmaul8e62b022022-03-22 09:33:25 -0700475 /// Creates a vector of type ``Enum`` into the ``ByteBuffer``
476 ///
477 /// ``createVector(_:)-9h189`` writes a vector of type ``Enum`` into
478 /// ``ByteBuffer``. This is a convenient method instead of calling,
479 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``
480 /// ```swift
481 /// let vectorOffset = builder.
482 /// createVector([.swift, .cpp])
483 /// ```
484 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800485 /// - Parameter elements: Elements to be written into the buffer
486 /// - Parameter size: Count of elements
James Kuszmaul8e62b022022-03-22 09:33:25 -0700487 /// - returns: ``Offset`` of the vector
488 mutating public func createVector<T: Enum>(
489 _ elements: [T],
490 size: Int) -> Offset
491 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800492 let size = size
493 startVector(size, elementSize: T.byteSize)
494 for e in elements.reversed() {
495 _bb.push(value: e.value, len: T.byteSize)
Austin Schuh272c6132020-11-14 16:37:52 -0800496 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700497 return endVector(len: size)
Austin Schuh58b9b472020-11-25 19:12:44 -0800498 }
499
James Kuszmaul8e62b022022-03-22 09:33:25 -0700500 /// Creates a vector of already written offsets
501 ///
502 /// ``createVector(ofOffsets:)`` creates a vector of ``Offset`` into
503 /// ``ByteBuffer``. This is a convenient method instead of calling,
504 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``.
505 ///
506 /// The underlying implementation simply calls ``createVector(ofOffsets:len:)``
507 ///
508 /// ```swift
509 /// let namesOffsets = builder.
510 /// createVector(ofOffsets: [name1, name2])
511 /// ```
512 /// - Parameter offsets: Array of offsets of type ``Offset``
513 /// - returns: ``Offset`` of the vector
514 mutating public func createVector(ofOffsets offsets: [Offset]) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800515 createVector(ofOffsets: offsets, len: offsets.count)
516 }
517
James Kuszmaul8e62b022022-03-22 09:33:25 -0700518 /// Creates a vector of already written offsets
519 ///
520 /// ``createVector(ofOffsets:)`` creates a vector of ``Offset`` into
521 /// ``ByteBuffer``. This is a convenient method instead of calling,
522 /// ``startVector(_:elementSize:)`` and then ``endVector(len:)``
523 ///
524 /// ```swift
525 /// let namesOffsets = builder.
526 /// createVector(ofOffsets: [name1, name2])
527 /// ```
528 ///
529 /// - Parameter offsets: Array of offsets of type ``Offset``
Austin Schuh58b9b472020-11-25 19:12:44 -0800530 /// - Parameter size: Count of elements
James Kuszmaul8e62b022022-03-22 09:33:25 -0700531 /// - returns: ``Offset`` of the vector
532 mutating public func createVector(
533 ofOffsets offsets: [Offset],
534 len: Int) -> Offset
535 {
536 startVector(len, elementSize: MemoryLayout<Offset>.size)
Austin Schuh58b9b472020-11-25 19:12:44 -0800537 for o in offsets.reversed() {
538 push(element: o)
Austin Schuh272c6132020-11-14 16:37:52 -0800539 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700540 return endVector(len: len)
Austin Schuh58b9b472020-11-25 19:12:44 -0800541 }
542
James Kuszmaul8e62b022022-03-22 09:33:25 -0700543 /// Creates a vector of strings
544 ///
545 /// ``createVector(ofStrings:)`` creates a vector of `String` into
546 /// ``ByteBuffer``. This is a convenient method instead of manually
547 /// creating the string offsets, you simply pass it to this function
548 /// and it would write the strings into the ``ByteBuffer``.
549 /// After that it calls ``createVector(ofOffsets:)``
550 ///
551 /// ```swift
552 /// let namesOffsets = builder.
553 /// createVector(ofStrings: ["Name", "surname"])
554 /// ```
555 ///
556 /// - Parameter str: Array of string
557 /// - returns: ``Offset`` of the vector
558 mutating public func createVector(ofStrings str: [String]) -> Offset {
559 var offsets: [Offset] = []
Austin Schuh58b9b472020-11-25 19:12:44 -0800560 for s in str {
561 offsets.append(create(string: s))
Austin Schuh272c6132020-11-14 16:37:52 -0800562 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800563 return createVector(ofOffsets: offsets)
564 }
565
James Kuszmaul8e62b022022-03-22 09:33:25 -0700566 /// Creates a vector of type ``NativeStruct``.
Austin Schuh58b9b472020-11-25 19:12:44 -0800567 ///
James Kuszmaul8e62b022022-03-22 09:33:25 -0700568 /// Any swift struct in the generated code, should confirm to
569 /// ``NativeStruct``. Since the generated swift structs are padded
570 /// to the `FlatBuffers` standards.
571 ///
572 /// ```swift
573 /// let offsets = builder.
574 /// createVector(ofStructs: [NativeStr(num: 1), NativeStr(num: 2)])
575 /// ```
576 ///
577 /// - Parameter structs: A vector of ``NativeStruct``
578 /// - Returns: ``Offset`` of the vector
579 mutating public func createVector<T: NativeStruct>(ofStructs structs: [T])
580 -> Offset
Austin Schuh58b9b472020-11-25 19:12:44 -0800581 {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700582 startVector(
583 structs.count * MemoryLayout<T>.size,
584 elementSize: MemoryLayout<T>.alignment)
Austin Schuh58b9b472020-11-25 19:12:44 -0800585 for i in structs.reversed() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700586 _ = create(struct: i)
Austin Schuh272c6132020-11-14 16:37:52 -0800587 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700588 return endVector(len: structs.count)
Austin Schuh58b9b472020-11-25 19:12:44 -0800589 }
590
591 // MARK: - Inserting Structs
592
James Kuszmaul8e62b022022-03-22 09:33:25 -0700593 /// Writes a ``NativeStruct`` into the ``ByteBuffer``
594 ///
595 /// Adds a native struct that's build and padded according
596 /// to `FlatBuffers` standards. with a predefined position.
597 ///
598 /// ```swift
599 /// let offset = builder.create(
600 /// struct: NativeStr(num: 1),
601 /// position: 10)
602 /// ```
603 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800604 /// - Parameters:
James Kuszmaul8e62b022022-03-22 09:33:25 -0700605 /// - s: ``NativeStruct`` to be inserted into the ``ByteBuffer``
606 /// - position: The predefined position of the object
607 /// - Returns: ``Offset`` of written struct
Austin Schuh58b9b472020-11-25 19:12:44 -0800608 @discardableResult
James Kuszmaul8e62b022022-03-22 09:33:25 -0700609 mutating public func create<T: NativeStruct>(
610 struct s: T, position: VOffset) -> Offset
Austin Schuh58b9b472020-11-25 19:12:44 -0800611 {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700612 let offset = create(struct: s)
613 _vtableStorage.add(loc: FieldLoc(
614 offset: _bb.size,
615 position: VOffset(position)))
616 return offset
617 }
618
619 /// Writes a ``NativeStruct`` into the ``ByteBuffer``
620 ///
621 /// Adds a native struct that's build and padded according
622 /// to `FlatBuffers` standards, directly into the buffer without
623 /// a predefined position.
624 ///
625 /// ```swift
626 /// let offset = builder.create(
627 /// struct: NativeStr(num: 1))
628 /// ```
629 ///
630 /// - Parameters:
631 /// - s: ``NativeStruct`` to be inserted into the ``ByteBuffer``
632 /// - Returns: ``Offset`` of written struct
633 @discardableResult
634 mutating public func create<T: NativeStruct>(
635 struct s: T) -> Offset
636 {
637 let size = MemoryLayout<T>.size
638 preAlign(len: size, alignment: MemoryLayout<T>.alignment)
Austin Schuh58b9b472020-11-25 19:12:44 -0800639 _bb.push(struct: s, size: size)
640 return Offset(offset: _bb.size)
641 }
642
Austin Schuh58b9b472020-11-25 19:12:44 -0800643 // MARK: - Inserting Strings
644
James Kuszmaul8e62b022022-03-22 09:33:25 -0700645 /// Insets a string into the buffer of type `UTF8`
646 ///
647 /// Adds a swift string into ``ByteBuffer`` by encoding it
648 /// using `UTF8`
649 ///
650 /// ```swift
651 /// let nameOffset = builder
652 /// .create(string: "welcome")
653 /// ```
654 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800655 /// - Parameter str: String to be serialized
James Kuszmaul8e62b022022-03-22 09:33:25 -0700656 /// - returns: ``Offset`` of inserted string
657 mutating public func create(string str: String?) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800658 guard let str = str else { return Offset() }
659 let len = str.utf8.count
660 notNested()
661 preAlign(len: len &+ 1, type: UOffset.self)
662 _bb.fill(padding: 1)
663 _bb.push(string: str, len: len)
664 push(element: UOffset(len))
665 return Offset(offset: _bb.size)
666 }
667
James Kuszmaul8e62b022022-03-22 09:33:25 -0700668 /// Insets a shared string into the buffer of type `UTF8`
Austin Schuh58b9b472020-11-25 19:12:44 -0800669 ///
James Kuszmaul8e62b022022-03-22 09:33:25 -0700670 /// Adds a swift string into ``ByteBuffer`` by encoding it
671 /// using `UTF8`. The function will check if the string,
672 /// is already written to the ``ByteBuffer``
673 ///
674 /// ```swift
675 /// let nameOffset = builder
676 /// .createShared(string: "welcome")
677 ///
678 ///
679 /// let secondOffset = builder
680 /// .createShared(string: "welcome")
681 ///
682 /// assert(nameOffset.o == secondOffset.o)
683 /// ```
684 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800685 /// - Parameter str: String to be serialized
James Kuszmaul8e62b022022-03-22 09:33:25 -0700686 /// - returns: ``Offset`` of inserted string
687 mutating public func createShared(string str: String?) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800688 guard let str = str else { return Offset() }
689 if let offset = stringOffsetMap[str] {
690 return offset
Austin Schuh272c6132020-11-14 16:37:52 -0800691 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800692 let offset = create(string: str)
693 stringOffsetMap[str] = offset
694 return offset
695 }
696
697 // MARK: - Inseting offsets
698
James Kuszmaul8e62b022022-03-22 09:33:25 -0700699 /// Writes the ``Offset`` of an already written table
700 ///
701 /// Writes the ``Offset`` of a table if not empty into the
702 /// ``ByteBuffer``
703 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800704 /// - Parameters:
James Kuszmaul8e62b022022-03-22 09:33:25 -0700705 /// - offset: ``Offset`` of another object to be written
706 /// - position: The predefined position of the object
707 mutating public func add(offset: Offset, at position: VOffset) {
Austin Schuh58b9b472020-11-25 19:12:44 -0800708 if offset.isEmpty { return }
709 add(element: refer(to: offset.o), def: 0, at: position)
710 }
711
James Kuszmaul8e62b022022-03-22 09:33:25 -0700712 /// Pushes a value of type ``Offset`` into the ``ByteBuffer``
713 /// - Parameter o: ``Offset``
714 /// - returns: Current position of the ``Offset``
Austin Schuh58b9b472020-11-25 19:12:44 -0800715 @discardableResult
James Kuszmaul8e62b022022-03-22 09:33:25 -0700716 mutating public func push(element o: Offset) -> UOffset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800717 push(element: refer(to: o.o))
718 }
719
720 // MARK: - Inserting Scalars to Buffer
721
James Kuszmaul8e62b022022-03-22 09:33:25 -0700722 /// Writes a ``Scalar`` value into ``ByteBuffer``
723 ///
724 /// ``add(element:def:at:)`` takes in a default value, and current value
725 /// and the position within the `VTable`. The default value would not
726 /// be serialized if the value is the same as the current value or
727 /// `serializeDefaults` is equal to false.
728 ///
729 /// If serializing defaults is important ``init(initialSize:serializeDefaults:)``,
730 /// passing true for `serializeDefaults` would do the job.
731 ///
732 /// ```swift
733 /// // Adds 10 to the buffer
734 /// builder.add(element: Int(10), def: 1, position 12)
735 /// ```
736 ///
737 /// *NOTE: Never call this manually*
Austin Schuh58b9b472020-11-25 19:12:44 -0800738 ///
739 /// - Parameters:
740 /// - element: Element to insert
741 /// - def: Default value for that element
742 /// - position: The predefined position of the element
James Kuszmaul8e62b022022-03-22 09:33:25 -0700743 mutating public func add<T: Scalar>(
744 element: T,
745 def: T,
746 at position: VOffset)
747 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800748 if element == def && !serializeDefaults { return }
749 track(offset: push(element: element), at: position)
750 }
751
James Kuszmaul8e62b022022-03-22 09:33:25 -0700752 /// Writes a optional ``Scalar`` value into ``ByteBuffer``
753 ///
754 /// Takes an optional value to be written into the ``ByteBuffer``
755 ///
756 /// *NOTE: Never call this manually*
757 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800758 /// - Parameters:
759 /// - element: Optional element of type scalar
760 /// - position: The predefined position of the element
761 mutating public func add<T: Scalar>(element: T?, at position: VOffset) {
762 guard let element = element else { return }
763 track(offset: push(element: element), at: position)
764 }
765
James Kuszmaul8e62b022022-03-22 09:33:25 -0700766 /// Pushes a values of type ``Scalar`` into the ``ByteBuffer``
767 ///
768 /// *NOTE: Never call this manually*
769 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800770 /// - Parameter element: Element to insert
771 /// - returns: Postion of the Element
772 @discardableResult
773 mutating public func push<T: Scalar>(element: T) -> UOffset {
774 let size = MemoryLayout<T>.size
775 preAlign(
776 len: size,
777 alignment: size)
778 _bb.push(value: element, len: size)
779 return _bb.size
780 }
781
Austin Schuh272c6132020-11-14 16:37:52 -0800782}
783
784extension FlatBufferBuilder: CustomDebugStringConvertible {
Austin Schuh58b9b472020-11-25 19:12:44 -0800785
786 public var debugDescription: String {
787 """
788 buffer debug:
789 \(_bb)
790 builder debug:
791 { finished: \(finished), serializeDefaults: \(serializeDefaults), isNested: \(isNested) }
792 """
793 }
794
795 /// VTableStorage is a class to contain the VTable buffer that would be serialized into buffer
796 @usableFromInline
797 internal class VTableStorage {
798 /// Memory check since deallocating each time we want to clear would be expensive
799 /// and memory leaks would happen if we dont deallocate the first allocated memory.
800 /// memory is promised to be available before adding `FieldLoc`
801 private var memoryInUse = false
802 /// Size of FieldLoc in memory
803 let size = MemoryLayout<FieldLoc>.stride
804 /// Memeory buffer
805 var memory: UnsafeMutableRawBufferPointer!
806 /// Capacity of the current buffer
807 var capacity: Int = 0
808 /// Maximuim offset written to the class
809 var maxOffset: VOffset = 0
810 /// number of fields written into the buffer
811 var numOfFields: Int = 0
812 /// Last written Index
813 var writtenIndex: Int = 0
Austin Schuh58b9b472020-11-25 19:12:44 -0800814
815 /// Creates the memory to store the buffer in
James Kuszmaul8e62b022022-03-22 09:33:25 -0700816 @usableFromInline
Austin Schuh58b9b472020-11-25 19:12:44 -0800817 init() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700818 memory = UnsafeMutableRawBufferPointer.allocate(
819 byteCount: 0,
820 alignment: 0)
Austin Schuh272c6132020-11-14 16:37:52 -0800821 }
822
Austin Schuh58b9b472020-11-25 19:12:44 -0800823 deinit {
824 memory.deallocate()
825 }
Austin Schuh272c6132020-11-14 16:37:52 -0800826
Austin Schuh58b9b472020-11-25 19:12:44 -0800827 /// Builds a buffer with byte count of fieldloc.size * count of field numbers
828 /// - Parameter count: number of fields to be written
James Kuszmaul8e62b022022-03-22 09:33:25 -0700829 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800830 func start(count: Int) {
831 assert(count >= 0, "number of fields should NOT be negative")
832 let capacity = count &* size
833 ensure(space: capacity)
Austin Schuh272c6132020-11-14 16:37:52 -0800834 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800835
836 /// Adds a FieldLoc into the buffer, which would track how many have been written,
837 /// and max offset
838 /// - Parameter loc: Location of encoded element
839 func add(loc: FieldLoc) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700840 memory.baseAddress?.advanced(by: writtenIndex).storeBytes(
841 of: loc,
842 as: FieldLoc.self)
Austin Schuh58b9b472020-11-25 19:12:44 -0800843 writtenIndex = writtenIndex &+ size
844 numOfFields = numOfFields &+ 1
845 maxOffset = max(loc.position, maxOffset)
Austin Schuh272c6132020-11-14 16:37:52 -0800846 }
847
Austin Schuh58b9b472020-11-25 19:12:44 -0800848 /// Clears the data stored related to the encoded buffer
849 func clear() {
850 maxOffset = 0
851 numOfFields = 0
852 writtenIndex = 0
853 }
854
855 /// Ensure that the buffer has enough space instead of recreating the buffer each time.
856 /// - Parameter space: space required for the new vtable
James Kuszmaul8e62b022022-03-22 09:33:25 -0700857 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800858 func ensure(space: Int) {
859 guard space &+ writtenIndex > capacity else { return }
860 memory.deallocate()
James Kuszmaul8e62b022022-03-22 09:33:25 -0700861 memory = UnsafeMutableRawBufferPointer.allocate(
862 byteCount: space,
863 alignment: size)
Austin Schuh58b9b472020-11-25 19:12:44 -0800864 capacity = space
865 }
866
867 /// Loads an object of type `FieldLoc` from buffer memory
868 /// - Parameter index: index of element
869 /// - Returns: a FieldLoc at index
James Kuszmaul8e62b022022-03-22 09:33:25 -0700870 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800871 func load(at index: Int) -> FieldLoc {
872 memory.load(fromByteOffset: index, as: FieldLoc.self)
873 }
874
875 }
876
877 internal struct FieldLoc {
878 var offset: UOffset
879 var position: VOffset
880 }
881
Austin Schuh272c6132020-11-14 16:37:52 -0800882}