blob: bfe36157f489d9655f083e19fc59f963c248a573 [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 Schuh2dd86a92022-09-14 21:19:23 -070017#if !os(WASI)
Austin Schuh272c6132020-11-14 16:37:52 -080018import Foundation
Austin Schuh2dd86a92022-09-14 21:19:23 -070019#else
20import SwiftOverlayShims
21#endif
Austin Schuh272c6132020-11-14 16:37:52 -080022
James Kuszmaul8e62b022022-03-22 09:33:25 -070023/// ``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
Austin Schuh272c6132020-11-14 16:37:52 -080034public struct FlatBufferBuilder {
Austin Schuh58b9b472020-11-25 19:12:44 -080035
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()
James Kuszmaul8e62b022022-03-22 09:33:25 -070038 /// Flatbuffer data will be written into
39 @usableFromInline internal var _bb: ByteBuffer
Austin Schuh58b9b472020-11-25 19:12:44 -080040
41 /// Reference Vtables that were already written to the buffer
42 private var _vtables: [UOffset] = []
Austin Schuh58b9b472020-11-25 19:12:44 -080043 /// 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
James Kuszmaul8e62b022022-03-22 09:33:25 -070046 private var stringOffsetMap: [String: Offset] = [:]
Austin Schuh58b9b472020-11-25 19:12:44 -080047 /// 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
Austin Schuh272c6132020-11-14 16:37:52 -080056 }
Austin Schuh58b9b472020-11-25 19:12:44 -080057 }
58
59 /// Gives a read access to the buffer's size
60 public var size: UOffset { _bb.size }
James Kuszmaul8e62b022022-03-22 09:33:25 -070061
Austin Schuh2dd86a92022-09-14 21:19:23 -070062 #if !os(WASI)
Austin Schuh58b9b472020-11-25 19:12:44 -080063 /// Data representation of the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -070064 ///
65 /// Should only be used after ``finish(offset:addPrefix:)`` is called
Austin Schuh58b9b472020-11-25 19:12:44 -080066 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 }
Austin Schuh2dd86a92022-09-14 21:19:23 -070072 #endif
James Kuszmaul8e62b022022-03-22 09:33:25 -070073
74 /// Returns the underlying bytes in the ``ByteBuffer``
75 ///
76 /// Note: This should be used with caution.
Austin Schuh58b9b472020-11-25 19:12:44 -080077 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 }
James Kuszmaul8e62b022022-03-22 09:33:25 -070083
84 /// Returns the written bytes into the ``ByteBuffer``
85 ///
86 /// Should only be used after ``finish(offset:addPrefix:)`` is called
Austin Schuh58b9b472020-11-25 19:12:44 -080087 public var sizedByteArray: [UInt8] {
88 assert(finished, "Data shouldn't be called before finish()")
James Kuszmaul8e62b022022-03-22 09:33:25 -070089 return _bb.underlyingBytes
Austin Schuh58b9b472020-11-25 19:12:44 -080090 }
James Kuszmaul8e62b022022-03-22 09:33:25 -070091
92 /// Returns the original ``ByteBuffer``
93 ///
94 /// Returns the current buffer that was just created
95 /// with the offsets, and data written to it.
Austin Schuh58b9b472020-11-25 19:12:44 -080096 public var buffer: ByteBuffer { _bb }
97
James Kuszmaul8e62b022022-03-22 09:33:25 -070098 /// Returns a newly created sized ``ByteBuffer``
99 ///
100 /// returns a new buffer that is sized to the data written
101 /// to the main buffer
Austin Schuh58b9b472020-11-25 19:12:44 -0800102 public var sizedBuffer: ByteBuffer {
103 assert(finished, "Data shouldn't be called before finish()")
James Kuszmaul8e62b022022-03-22 09:33:25 -0700104 return ByteBuffer(
105 memory: _bb.memory.advanced(by: _bb.reader),
106 count: Int(_bb.size))
Austin Schuh58b9b472020-11-25 19:12:44 -0800107 }
108
109 // MARK: - Init
110
James Kuszmaul8e62b022022-03-22 09:33:25 -0700111 /// Initialize the buffer with a size
Austin Schuh58b9b472020-11-25 19:12:44 -0800112 /// - Parameters:
113 /// - initialSize: Initial size for the buffer
114 /// - force: Allows default to be serialized into the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700115 ///
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 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800123 assert(initialSize > 0, "Size should be greater than zero!")
124 guard isLitteEndian else {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700125 fatalError(
126 "Reading/Writing a buffer in big endian machine is not supported on swift")
Austin Schuh272c6132020-11-14 16:37:52 -0800127 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800128 serializeDefaults = force
129 _bb = ByteBuffer(initialSize: Int(initialSize))
130 }
131
James Kuszmaul8e62b022022-03-22 09:33:25 -0700132 /// Clears the builder and the buffer from the written data.
Austin Schuh58b9b472020-11-25 19:12:44 -0800133 mutating public func clear() {
134 _minAlignment = 0
135 isNested = false
Austin Schuh2dd86a92022-09-14 21:19:23 -0700136 stringOffsetMap.removeAll(keepingCapacity: true)
137 _vtables.removeAll(keepingCapacity: true)
Austin Schuh58b9b472020-11-25 19:12:44 -0800138 _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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700148 ///
149 /// *NOTE: Never call this function, this is only supposed to be called
150 /// by the generated code*
Austin Schuh2dd86a92022-09-14 21:19:23 -0700151 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700152 mutating public func require(table: Offset, fields: [Int32]) {
Austin Schuh58b9b472020-11-25 19:12:44 -0800153 for field in fields {
154 let start = _bb.capacity &- Int(table.o)
155 let startTable = start &- Int(_bb.read(def: Int32.self, position: start))
James Kuszmaul8e62b022022-03-22 09:33:25 -0700156 let isOkay = _bb.read(
157 def: VOffset.self,
158 position: startTable &+ Int(field)) != 0
Austin Schuh58b9b472020-11-25 19:12:44 -0800159 assert(isOkay, "Flatbuffers requires the following field")
Austin Schuh272c6132020-11-14 16:37:52 -0800160 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800161 }
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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700168 ///
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 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800190 let size = MemoryLayout<UOffset>.size
James Kuszmaul8e62b022022-03-22 09:33:25 -0700191 preAlign(
192 len: size &+ (prefix ? size : 0) &+ FileIdLength,
193 alignment: _minAlignment)
Austin Schuh58b9b472020-11-25 19:12:44 -0800194 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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700203 ///
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 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800220 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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700229 /// ``startTable(with:)`` will let the builder know, that a new object is being serialized.
Austin Schuh58b9b472020-11-25 19:12:44 -0800230 ///
James Kuszmaul8e62b022022-03-22 09:33:25 -0700231 /// The function will fatalerror if called while there is another object being serialized.
232 /// ```swift
233 /// let start = Monster
234 /// .startMonster(&fbb)
235 /// ```
Austin Schuh58b9b472020-11-25 19:12:44 -0800236 /// - Parameter numOfFields: Number of elements to be written to the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700237 /// - Returns: Offset of the newly started table
Austin Schuh2dd86a92022-09-14 21:19:23 -0700238 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800239 mutating public func startTable(with numOfFields: Int) -> UOffset {
240 notNested()
241 isNested = true
242 _vtableStorage.start(count: numOfFields)
243 return _bb.size
244 }
245
James Kuszmaul8e62b022022-03-22 09:33:25 -0700246 /// ``endTable(at:)`` will let the ``FlatBufferBuilder`` know that the
247 /// object that's written to it is completed
Austin Schuh58b9b472020-11-25 19:12:44 -0800248 ///
James Kuszmaul8e62b022022-03-22 09:33:25 -0700249 /// 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 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800254 /// - Parameter startOffset:Start point of the object written
255 /// - returns: The root of the table
256 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))
James Kuszmaul8e62b022022-03-22 09:33:25 -0700278 _bb.write(
279 value: VOffset(vTableOffset &- loaded.offset),
280 index: _index,
281 direct: true)
Austin Schuh272c6132020-11-14 16:37:52 -0800282 }
283
Austin Schuh58b9b472020-11-25 19:12:44 -0800284 _vtableStorage.clear()
285 let vt_use = _bb.size
Austin Schuh272c6132020-11-14 16:37:52 -0800286
Austin Schuh58b9b472020-11-25 19:12:44 -0800287 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
Austin Schuh272c6132020-11-14 16:37:52 -0800300 }
301
Austin Schuh58b9b472020-11-25 19:12:44 -0800302 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)
Austin Schuh272c6132020-11-14 16:37:52 -0800310 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800311 isNested = false
312 return vTableOffset
313 }
Austin Schuh272c6132020-11-14 16:37:52 -0800314
Austin Schuh58b9b472020-11-25 19:12:44 -0800315 // MARK: - Builds Buffer
316
James Kuszmaul8e62b022022-03-22 09:33:25 -0700317 /// Asserts to see if the object is not nested
Austin Schuh2dd86a92022-09-14 21:19:23 -0700318 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800319 @usableFromInline
320 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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700326 @inline(__always)
Austin Schuh2dd86a92022-09-14 21:19:23 -0700327 @usableFromInline
Austin Schuh58b9b472020-11-25 19:12:44 -0800328 mutating internal func minAlignment(size: Int) {
329 if size > _minAlignment {
330 _minAlignment = size
Austin Schuh272c6132020-11-14 16:37:52 -0800331 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800332 }
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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700338 @inline(__always)
Austin Schuh2dd86a92022-09-14 21:19:23 -0700339 @usableFromInline
James Kuszmaul8e62b022022-03-22 09:33:25 -0700340 mutating internal func padding(
341 bufSize: UInt32,
342 elementSize: UInt32) -> UInt32
343 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800344 ((~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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700351 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800352 @usableFromInline
353 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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700364 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800365 @usableFromInline
366 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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700372 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800373 @usableFromInline
374 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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700384 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800385 @usableFromInline
386 mutating internal func track(offset: UOffset, at position: VOffset) {
387 _vtableStorage.add(loc: FieldLoc(offset: offset, position: position))
388 }
389
James Kuszmaul8e62b022022-03-22 09:33:25 -0700390 // MARK: - Inserting Vectors
Austin Schuh58b9b472020-11-25 19:12:44 -0800391
James Kuszmaul8e62b022022-03-22 09:33:25 -0700392 /// ``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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700404 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800405 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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700412 /// ``endVector(len:)`` ends the currently created vector
Austin Schuh58b9b472020-11-25 19:12:44 -0800413 ///
James Kuszmaul8e62b022022-03-22 09:33:25 -0700414 /// 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 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800424 /// - Parameter len: Length of the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700425 /// - Returns: Returns the current ``Offset`` in the ``ByteBuffer``
Austin Schuh2dd86a92022-09-14 21:19:23 -0700426 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700427 mutating public func endVector(len: Int) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800428 assert(isNested, "Calling endVector without calling startVector")
429 isNested = false
James Kuszmaul8e62b022022-03-22 09:33:25 -0700430 return Offset(offset: push(element: Int32(len)))
Austin Schuh58b9b472020-11-25 19:12:44 -0800431 }
432
James Kuszmaul8e62b022022-03-22 09:33:25 -0700433 /// 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 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800445 /// - Parameter elements: elements to be written into the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700446 /// - returns: ``Offset`` of the vector
Austin Schuh2dd86a92022-09-14 21:19:23 -0700447 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700448 mutating public func createVector<T: Scalar>(_ elements: [T]) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800449 createVector(elements, size: elements.count)
450 }
451
452 /// Creates a vector of type Scalar in the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700453 ///
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 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800462 /// - Parameter elements: Elements to be written into the buffer
463 /// - Parameter size: Count of elements
James Kuszmaul8e62b022022-03-22 09:33:25 -0700464 /// - returns: ``Offset`` of the vector
Austin Schuh2dd86a92022-09-14 21:19:23 -0700465 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700466 mutating public func createVector<T: Scalar>(
467 _ elements: [T],
468 size: Int) -> Offset
469 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800470 let size = size
471 startVector(size, elementSize: MemoryLayout<T>.size)
472 _bb.push(elements: elements)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700473 return endVector(len: size)
Austin Schuh58b9b472020-11-25 19:12:44 -0800474 }
475
James Kuszmaul8e62b022022-03-22 09:33:25 -0700476 /// 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 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800488 /// - Parameter elements: elements to be written into the buffer
James Kuszmaul8e62b022022-03-22 09:33:25 -0700489 /// - returns: ``Offset`` of the vector
Austin Schuh2dd86a92022-09-14 21:19:23 -0700490 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700491 mutating public func createVector<T: Enum>(_ elements: [T]) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800492 createVector(elements, size: elements.count)
493 }
494
James Kuszmaul8e62b022022-03-22 09:33:25 -0700495 /// 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 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800505 /// - Parameter elements: Elements to be written into the buffer
506 /// - Parameter size: Count of elements
James Kuszmaul8e62b022022-03-22 09:33:25 -0700507 /// - returns: ``Offset`` of the vector
Austin Schuh2dd86a92022-09-14 21:19:23 -0700508 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700509 mutating public func createVector<T: Enum>(
510 _ elements: [T],
511 size: Int) -> Offset
512 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800513 let size = size
514 startVector(size, elementSize: T.byteSize)
515 for e in elements.reversed() {
516 _bb.push(value: e.value, len: T.byteSize)
Austin Schuh272c6132020-11-14 16:37:52 -0800517 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700518 return endVector(len: size)
Austin Schuh58b9b472020-11-25 19:12:44 -0800519 }
520
James Kuszmaul8e62b022022-03-22 09:33:25 -0700521 /// 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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700535 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700536 mutating public func createVector(ofOffsets offsets: [Offset]) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800537 createVector(ofOffsets: offsets, len: offsets.count)
538 }
539
James Kuszmaul8e62b022022-03-22 09:33:25 -0700540 /// 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``
Austin Schuh58b9b472020-11-25 19:12:44 -0800552 /// - Parameter size: Count of elements
James Kuszmaul8e62b022022-03-22 09:33:25 -0700553 /// - returns: ``Offset`` of the vector
Austin Schuh2dd86a92022-09-14 21:19:23 -0700554 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700555 mutating public func createVector(
556 ofOffsets offsets: [Offset],
557 len: Int) -> Offset
558 {
559 startVector(len, elementSize: MemoryLayout<Offset>.size)
Austin Schuh58b9b472020-11-25 19:12:44 -0800560 for o in offsets.reversed() {
561 push(element: o)
Austin Schuh272c6132020-11-14 16:37:52 -0800562 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700563 return endVector(len: len)
Austin Schuh58b9b472020-11-25 19:12:44 -0800564 }
565
James Kuszmaul8e62b022022-03-22 09:33:25 -0700566 /// 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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700581 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700582 mutating public func createVector(ofStrings str: [String]) -> Offset {
583 var offsets: [Offset] = []
Austin Schuh58b9b472020-11-25 19:12:44 -0800584 for s in str {
585 offsets.append(create(string: s))
Austin Schuh272c6132020-11-14 16:37:52 -0800586 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800587 return createVector(ofOffsets: offsets)
588 }
589
James Kuszmaul8e62b022022-03-22 09:33:25 -0700590 /// Creates a vector of type ``NativeStruct``.
Austin Schuh58b9b472020-11-25 19:12:44 -0800591 ///
James Kuszmaul8e62b022022-03-22 09:33:25 -0700592 /// 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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700603 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700604 mutating public func createVector<T: NativeStruct>(ofStructs structs: [T])
605 -> Offset
Austin Schuh58b9b472020-11-25 19:12:44 -0800606 {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700607 startVector(
608 structs.count * MemoryLayout<T>.size,
609 elementSize: MemoryLayout<T>.alignment)
Austin Schuh58b9b472020-11-25 19:12:44 -0800610 for i in structs.reversed() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700611 _ = create(struct: i)
Austin Schuh272c6132020-11-14 16:37:52 -0800612 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700613 return endVector(len: structs.count)
Austin Schuh58b9b472020-11-25 19:12:44 -0800614 }
615
616 // MARK: - Inserting Structs
617
James Kuszmaul8e62b022022-03-22 09:33:25 -0700618 /// 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 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800629 /// - Parameters:
James Kuszmaul8e62b022022-03-22 09:33:25 -0700630 /// - s: ``NativeStruct`` to be inserted into the ``ByteBuffer``
631 /// - position: The predefined position of the object
632 /// - Returns: ``Offset`` of written struct
Austin Schuh2dd86a92022-09-14 21:19:23 -0700633 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800634 @discardableResult
James Kuszmaul8e62b022022-03-22 09:33:25 -0700635 mutating public func create<T: NativeStruct>(
636 struct s: T, position: VOffset) -> Offset
Austin Schuh58b9b472020-11-25 19:12:44 -0800637 {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700638 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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700659 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700660 @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)
Austin Schuh58b9b472020-11-25 19:12:44 -0800666 _bb.push(struct: s, size: size)
667 return Offset(offset: _bb.size)
668 }
669
Austin Schuh58b9b472020-11-25 19:12:44 -0800670 // MARK: - Inserting Strings
671
James Kuszmaul8e62b022022-03-22 09:33:25 -0700672 /// 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 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800682 /// - Parameter str: String to be serialized
James Kuszmaul8e62b022022-03-22 09:33:25 -0700683 /// - returns: ``Offset`` of inserted string
Austin Schuh2dd86a92022-09-14 21:19:23 -0700684 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700685 mutating public func create(string str: String?) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800686 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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700696 /// Insets a shared string into the buffer of type `UTF8`
Austin Schuh58b9b472020-11-25 19:12:44 -0800697 ///
James Kuszmaul8e62b022022-03-22 09:33:25 -0700698 /// 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 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800713 /// - Parameter str: String to be serialized
James Kuszmaul8e62b022022-03-22 09:33:25 -0700714 /// - returns: ``Offset`` of inserted string
Austin Schuh2dd86a92022-09-14 21:19:23 -0700715 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700716 mutating public func createShared(string str: String?) -> Offset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800717 guard let str = str else { return Offset() }
718 if let offset = stringOffsetMap[str] {
719 return offset
Austin Schuh272c6132020-11-14 16:37:52 -0800720 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800721 let offset = create(string: str)
722 stringOffsetMap[str] = offset
723 return offset
724 }
725
726 // MARK: - Inseting offsets
727
James Kuszmaul8e62b022022-03-22 09:33:25 -0700728 /// Writes the ``Offset`` of an already written table
729 ///
730 /// Writes the ``Offset`` of a table if not empty into the
731 /// ``ByteBuffer``
732 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800733 /// - Parameters:
James Kuszmaul8e62b022022-03-22 09:33:25 -0700734 /// - offset: ``Offset`` of another object to be written
735 /// - position: The predefined position of the object
Austin Schuh2dd86a92022-09-14 21:19:23 -0700736 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700737 mutating public func add(offset: Offset, at position: VOffset) {
Austin Schuh58b9b472020-11-25 19:12:44 -0800738 if offset.isEmpty { return }
739 add(element: refer(to: offset.o), def: 0, at: position)
740 }
741
James Kuszmaul8e62b022022-03-22 09:33:25 -0700742 /// Pushes a value of type ``Offset`` into the ``ByteBuffer``
743 /// - Parameter o: ``Offset``
744 /// - returns: Current position of the ``Offset``
Austin Schuh2dd86a92022-09-14 21:19:23 -0700745 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800746 @discardableResult
James Kuszmaul8e62b022022-03-22 09:33:25 -0700747 mutating public func push(element o: Offset) -> UOffset {
Austin Schuh58b9b472020-11-25 19:12:44 -0800748 push(element: refer(to: o.o))
749 }
750
751 // MARK: - Inserting Scalars to Buffer
752
James Kuszmaul8e62b022022-03-22 09:33:25 -0700753 /// 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*
Austin Schuh58b9b472020-11-25 19:12:44 -0800769 ///
770 /// - Parameters:
771 /// - element: Element to insert
772 /// - def: Default value for that element
773 /// - position: The predefined position of the element
Austin Schuh2dd86a92022-09-14 21:19:23 -0700774 @inline(__always)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700775 mutating public func add<T: Scalar>(
776 element: T,
777 def: T,
778 at position: VOffset)
779 {
Austin Schuh58b9b472020-11-25 19:12:44 -0800780 if element == def && !serializeDefaults { return }
781 track(offset: push(element: element), at: position)
782 }
783
James Kuszmaul8e62b022022-03-22 09:33:25 -0700784 /// 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 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800790 /// - Parameters:
791 /// - element: Optional element of type scalar
792 /// - position: The predefined position of the element
Austin Schuh2dd86a92022-09-14 21:19:23 -0700793 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800794 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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700799 /// Pushes a values of type ``Scalar`` into the ``ByteBuffer``
800 ///
801 /// *NOTE: Never call this manually*
802 ///
Austin Schuh58b9b472020-11-25 19:12:44 -0800803 /// - Parameter element: Element to insert
804 /// - returns: Postion of the Element
Austin Schuh2dd86a92022-09-14 21:19:23 -0700805 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800806 @discardableResult
807 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
Austin Schuh272c6132020-11-14 16:37:52 -0800816}
817
818extension FlatBufferBuilder: CustomDebugStringConvertible {
Austin Schuh58b9b472020-11-25 19:12:44 -0800819
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
Austin Schuh58b9b472020-11-25 19:12:44 -0800848
849 /// Creates the memory to store the buffer in
James Kuszmaul8e62b022022-03-22 09:33:25 -0700850 @usableFromInline
Austin Schuh2dd86a92022-09-14 21:19:23 -0700851 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800852 init() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700853 memory = UnsafeMutableRawBufferPointer.allocate(
854 byteCount: 0,
855 alignment: 0)
Austin Schuh272c6132020-11-14 16:37:52 -0800856 }
857
Austin Schuh2dd86a92022-09-14 21:19:23 -0700858 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800859 deinit {
860 memory.deallocate()
861 }
Austin Schuh272c6132020-11-14 16:37:52 -0800862
Austin Schuh58b9b472020-11-25 19:12:44 -0800863 /// Builds a buffer with byte count of fieldloc.size * count of field numbers
864 /// - Parameter count: number of fields to be written
James Kuszmaul8e62b022022-03-22 09:33:25 -0700865 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800866 func start(count: Int) {
867 assert(count >= 0, "number of fields should NOT be negative")
868 let capacity = count &* size
869 ensure(space: capacity)
Austin Schuh272c6132020-11-14 16:37:52 -0800870 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800871
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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700875 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800876 func add(loc: FieldLoc) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700877 memory.baseAddress?.advanced(by: writtenIndex).storeBytes(
878 of: loc,
879 as: FieldLoc.self)
Austin Schuh58b9b472020-11-25 19:12:44 -0800880 writtenIndex = writtenIndex &+ size
881 numOfFields = numOfFields &+ 1
882 maxOffset = max(loc.position, maxOffset)
Austin Schuh272c6132020-11-14 16:37:52 -0800883 }
884
Austin Schuh58b9b472020-11-25 19:12:44 -0800885 /// Clears the data stored related to the encoded buffer
Austin Schuh2dd86a92022-09-14 21:19:23 -0700886 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800887 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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700895 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800896 func ensure(space: Int) {
897 guard space &+ writtenIndex > capacity else { return }
898 memory.deallocate()
James Kuszmaul8e62b022022-03-22 09:33:25 -0700899 memory = UnsafeMutableRawBufferPointer.allocate(
900 byteCount: space,
901 alignment: size)
Austin Schuh58b9b472020-11-25 19:12:44 -0800902 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
James Kuszmaul8e62b022022-03-22 09:33:25 -0700908 @inline(__always)
Austin Schuh58b9b472020-11-25 19:12:44 -0800909 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
Austin Schuh272c6132020-11-14 16:37:52 -0800920}