blob: 71fcab00294fca47d5c025a28b497e210d6c9e2f [file] [log] [blame]
Austin Schuh58b9b472020-11-25 19:12:44 -08001/*
2 * Copyright 2020 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
Austin Schuh272c6132020-11-14 16:37:52 -080017import Foundation
18
19public struct FlatBufferBuilder {
Austin Schuh58b9b472020-11-25 19:12:44 -080020
21 /// Storage for the Vtables used in the buffer are stored in here, so they would be written later in EndTable
22 @usableFromInline internal var _vtableStorage = VTableStorage()
23
24 /// Reference Vtables that were already written to the buffer
25 private var _vtables: [UOffset] = []
26 /// Flatbuffer data will be written into
27 private var _bb: ByteBuffer
28 /// A check if the buffer is being written into by a different table
29 private var isNested = false
30 /// Dictonary that stores a map of all the strings that were written to the buffer
31 private var stringOffsetMap: [String: Offset<String>] = [:]
32 /// A check to see if finish(::) was ever called to retreive data object
33 private var finished = false
34 /// A check to see if the buffer should serialize Default values
35 private var serializeDefaults: Bool
36
37 /// Current alignment for the buffer
38 var _minAlignment: Int = 0 {
39 didSet {
40 _bb.alignment = _minAlignment
Austin Schuh272c6132020-11-14 16:37:52 -080041 }
Austin Schuh58b9b472020-11-25 19:12:44 -080042 }
43
44 /// Gives a read access to the buffer's size
45 public var size: UOffset { _bb.size }
46 /// Data representation of the buffer
47 public var data: Data {
48 assert(finished, "Data shouldn't be called before finish()")
49 return Data(
50 bytes: _bb.memory.advanced(by: _bb.writerIndex),
51 count: _bb.capacity &- _bb.writerIndex)
52 }
53 /// Get's the fully sized buffer stored in memory
54 public var fullSizedByteArray: [UInt8] {
55 let ptr = UnsafeBufferPointer(
56 start: _bb.memory.assumingMemoryBound(to: UInt8.self),
57 count: _bb.capacity)
58 return Array(ptr)
59 }
60 /// Returns the written size of the buffer
61 public var sizedByteArray: [UInt8] {
62 assert(finished, "Data shouldn't be called before finish()")
63 let cp = _bb.capacity &- _bb.writerIndex
64 let start = _bb.memory.advanced(by: _bb.writerIndex)
65 .bindMemory(to: UInt8.self, capacity: cp)
66
67 let ptr = UnsafeBufferPointer(start: start, count: cp)
68 return Array(ptr)
69 }
70 /// Returns the buffer
71 public var buffer: ByteBuffer { _bb }
72
73 /// Returns A sized Buffer from the readable bytes
74 public var sizedBuffer: ByteBuffer {
75 assert(finished, "Data shouldn't be called before finish()")
76 return ByteBuffer(memory: _bb.memory.advanced(by: _bb.reader), count: Int(_bb.size))
77 }
78
79 // MARK: - Init
80
81 /// initialize the buffer with a size
82 /// - Parameters:
83 /// - initialSize: Initial size for the buffer
84 /// - force: Allows default to be serialized into the buffer
85 public init(initialSize: Int32 = 1024, serializeDefaults force: Bool = false) {
86 assert(initialSize > 0, "Size should be greater than zero!")
87 guard isLitteEndian else {
88 fatalError("Reading/Writing a buffer in big endian machine is not supported on swift")
Austin Schuh272c6132020-11-14 16:37:52 -080089 }
Austin Schuh58b9b472020-11-25 19:12:44 -080090 serializeDefaults = force
91 _bb = ByteBuffer(initialSize: Int(initialSize))
92 }
93
94 /// Clears the buffer and the builder from it's data
95 mutating public func clear() {
96 _minAlignment = 0
97 isNested = false
98 stringOffsetMap = [:]
99 _vtables = []
100 _vtableStorage.clear()
101 _bb.clear()
102 }
103
104 // MARK: - Create Tables
105
106 /// Checks if the required fields were serialized into the buffer
107 /// - Parameters:
108 /// - table: offset for the table
109 /// - fields: Array of all the important fields to be serialized
110 mutating public func require(table: Offset<UOffset>, fields: [Int32]) {
111 for field in fields {
112 let start = _bb.capacity &- Int(table.o)
113 let startTable = start &- Int(_bb.read(def: Int32.self, position: start))
114 let isOkay = _bb.read(def: VOffset.self, position: startTable &+ Int(field)) != 0
115 assert(isOkay, "Flatbuffers requires the following field")
Austin Schuh272c6132020-11-14 16:37:52 -0800116 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800117 }
118
119 /// Finished the buffer by adding the file id and then calling finish
120 /// - Parameters:
121 /// - offset: Offset of the table
122 /// - fileId: Takes the fileId
123 /// - prefix: if false it wont add the size of the buffer
124 mutating public func finish<T>(offset: Offset<T>, fileId: String, addPrefix prefix: Bool = false) {
125 let size = MemoryLayout<UOffset>.size
126 preAlign(len: size &+ (prefix ? size : 0) &+ FileIdLength, alignment: _minAlignment)
127 assert(fileId.count == FileIdLength, "Flatbuffers requires file id to be 4")
128 _bb.push(string: fileId, len: 4)
129 finish(offset: offset, addPrefix: prefix)
130 }
131
132 /// Finished the buffer by adding the file id, offset, and prefix to it.
133 /// - Parameters:
134 /// - offset: Offset of the table
135 /// - prefix: if false it wont add the size of the buffer
136 mutating public func finish<T>(offset: Offset<T>, addPrefix prefix: Bool = false) {
137 notNested()
138 let size = MemoryLayout<UOffset>.size
139 preAlign(len: size &+ (prefix ? size : 0), alignment: _minAlignment)
140 push(element: refer(to: offset.o))
141 if prefix { push(element: _bb.size) }
142 _vtableStorage.clear()
143 finished = true
144 }
145
146 /// starttable will let the builder know, that a new object is being serialized.
147 ///
148 /// The function will fatalerror if called while there is another object being serialized
149 /// - Parameter numOfFields: Number of elements to be written to the buffer
150 mutating public func startTable(with numOfFields: Int) -> UOffset {
151 notNested()
152 isNested = true
153 _vtableStorage.start(count: numOfFields)
154 return _bb.size
155 }
156
157 /// Endtable will let the builder know that the object that's written to it is completed
158 ///
159 /// This would be called after all the elements are serialized, it will add the vtable into the buffer.
160 /// it will fatalError in case the object is called without starttable, or the object has exceeded the limit of
161 /// 2GB,
162 /// - Parameter startOffset:Start point of the object written
163 /// - returns: The root of the table
164 mutating public func endTable(at startOffset: UOffset) -> UOffset {
165 assert(isNested, "Calling endtable without calling starttable")
166 let sizeofVoffset = MemoryLayout<VOffset>.size
167 let vTableOffset = push(element: SOffset(0))
168
169 let tableObjectSize = vTableOffset &- startOffset
170 assert(tableObjectSize < 0x10000, "Buffer can't grow beyond 2 Gigabytes")
171 let _max = Int(_vtableStorage.maxOffset) &+ sizeofVoffset
172
173 _bb.fill(padding: _max)
174 _bb.write(
175 value: VOffset(tableObjectSize),
176 index: _bb.writerIndex &+ sizeofVoffset,
177 direct: true)
178 _bb.write(value: VOffset(_max), index: _bb.writerIndex, direct: true)
179
180 var itr = 0
181 while itr < _vtableStorage.writtenIndex {
182 let loaded = _vtableStorage.load(at: itr)
183 itr = itr &+ _vtableStorage.size
184 guard loaded.offset != 0 else { continue }
185 let _index = (_bb.writerIndex &+ Int(loaded.position))
186 _bb.write(value: VOffset(vTableOffset &- loaded.offset), index: _index, direct: true)
Austin Schuh272c6132020-11-14 16:37:52 -0800187 }
188
Austin Schuh58b9b472020-11-25 19:12:44 -0800189 _vtableStorage.clear()
190 let vt_use = _bb.size
Austin Schuh272c6132020-11-14 16:37:52 -0800191
Austin Schuh58b9b472020-11-25 19:12:44 -0800192 var isAlreadyAdded: Int?
193
194 let vt2 = _bb.memory.advanced(by: _bb.writerIndex)
195 let len2 = vt2.load(fromByteOffset: 0, as: Int16.self)
196
197 for table in _vtables {
198 let position = _bb.capacity &- Int(table)
199 let vt1 = _bb.memory.advanced(by: position)
200 let len1 = _bb.read(def: Int16.self, position: position)
201 if len2 != len1 || 0 != memcmp(vt1, vt2, Int(len2)) { continue }
202
203 isAlreadyAdded = Int(table)
204 break
Austin Schuh272c6132020-11-14 16:37:52 -0800205 }
206
Austin Schuh58b9b472020-11-25 19:12:44 -0800207 if let offset = isAlreadyAdded {
208 let vTableOff = Int(vTableOffset)
209 let space = _bb.capacity &- vTableOff
210 _bb.write(value: Int32(offset &- vTableOff), index: space, direct: true)
211 _bb.pop(_bb.capacity &- space)
212 } else {
213 _bb.write(value: Int32(vt_use &- vTableOffset), index: Int(vTableOffset))
214 _vtables.append(_bb.size)
Austin Schuh272c6132020-11-14 16:37:52 -0800215 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800216 isNested = false
217 return vTableOffset
218 }
Austin Schuh272c6132020-11-14 16:37:52 -0800219
Austin Schuh58b9b472020-11-25 19:12:44 -0800220 // MARK: - Builds Buffer
221
222 /// asserts to see if the object is not nested
223 @usableFromInline
224 mutating internal func notNested() {
225 assert(!isNested, "Object serialization must not be nested")
226 }
227
228 /// Changes the minimuim alignment of the buffer
229 /// - Parameter size: size of the current alignment
230 @usableFromInline
231 mutating internal func minAlignment(size: Int) {
232 if size > _minAlignment {
233 _minAlignment = size
Austin Schuh272c6132020-11-14 16:37:52 -0800234 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800235 }
236
237 /// Gets the padding for the current element
238 /// - Parameters:
239 /// - bufSize: Current size of the buffer + the offset of the object to be written
240 /// - elementSize: Element size
241 @usableFromInline
242 mutating internal func padding(bufSize: UInt32, elementSize: UInt32) -> UInt32 {
243 ((~bufSize) &+ 1) & (elementSize - 1)
244 }
245
246 /// Prealigns the buffer before writting a new object into the buffer
247 /// - Parameters:
248 /// - len:Length of the object
249 /// - alignment: Alignment type
250 @usableFromInline
251 mutating internal func preAlign(len: Int, alignment: Int) {
252 minAlignment(size: alignment)
253 _bb.fill(padding: Int(padding(
254 bufSize: _bb.size &+ UOffset(len),
255 elementSize: UOffset(alignment))))
256 }
257
258 /// Prealigns the buffer before writting a new object into the buffer
259 /// - Parameters:
260 /// - len: Length of the object
261 /// - type: Type of the object to be written
262 @usableFromInline
263 mutating internal func preAlign<T: Scalar>(len: Int, type: T.Type) {
264 preAlign(len: len, alignment: MemoryLayout<T>.size)
265 }
266
267 /// Refers to an object that's written in the buffer
268 /// - Parameter off: the objects index value
269 @usableFromInline
270 mutating internal func refer(to off: UOffset) -> UOffset {
271 let size = MemoryLayout<UOffset>.size
272 preAlign(len: size, alignment: size)
273 return _bb.size &- off &+ UInt32(size)
274 }
275
276 /// Tracks the elements written into the buffer
277 /// - Parameters:
278 /// - offset: The offset of the element witten
279 /// - position: The position of the element
280 @usableFromInline
281 mutating internal func track(offset: UOffset, at position: VOffset) {
282 _vtableStorage.add(loc: FieldLoc(offset: offset, position: position))
283 }
284
285 // MARK: - Vectors
286
287 /// Starts a vector of length and Element size
288 mutating public func startVector(_ len: Int, elementSize: Int) {
289 notNested()
290 isNested = true
291 preAlign(len: len &* elementSize, type: UOffset.self)
292 preAlign(len: len &* elementSize, alignment: elementSize)
293 }
294
295 /// Ends the vector of at length
296 ///
297 /// The current function will fatalError if startVector is called before serializing the vector
298 /// - Parameter len: Length of the buffer
299 mutating public func endVector(len: Int) -> UOffset {
300 assert(isNested, "Calling endVector without calling startVector")
301 isNested = false
302 return push(element: Int32(len))
303 }
304
305 /// Creates a vector of type Scalar in the buffer
306 /// - Parameter elements: elements to be written into the buffer
307 /// - returns: Offset of the vector
308 mutating public func createVector<T: Scalar>(_ elements: [T]) -> Offset<UOffset> {
309 createVector(elements, size: elements.count)
310 }
311
312 /// Creates a vector of type Scalar in the buffer
313 /// - Parameter elements: Elements to be written into the buffer
314 /// - Parameter size: Count of elements
315 /// - returns: Offset of the vector
316 mutating public func createVector<T: Scalar>(_ elements: [T], size: Int) -> Offset<UOffset> {
317 let size = size
318 startVector(size, elementSize: MemoryLayout<T>.size)
319 _bb.push(elements: elements)
320 return Offset(offset: endVector(len: size))
321 }
322
323 /// Creates a vector of type Enums in the buffer
324 /// - Parameter elements: elements to be written into the buffer
325 /// - returns: Offset of the vector
326 mutating public func createVector<T: Enum>(_ elements: [T]) -> Offset<UOffset> {
327 createVector(elements, size: elements.count)
328 }
329
330 /// Creates a vector of type Enums in the buffer
331 /// - Parameter elements: Elements to be written into the buffer
332 /// - Parameter size: Count of elements
333 /// - returns: Offset of the vector
334 mutating public func createVector<T: Enum>(_ elements: [T], size: Int) -> Offset<UOffset> {
335 let size = size
336 startVector(size, elementSize: T.byteSize)
337 for e in elements.reversed() {
338 _bb.push(value: e.value, len: T.byteSize)
Austin Schuh272c6132020-11-14 16:37:52 -0800339 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800340 return Offset(offset: endVector(len: size))
341 }
342
343 /// Creates a vector of type Offsets in the buffer
344 /// - Parameter offsets:Array of offsets of type T
345 /// - returns: Offset of the vector
346 mutating public func createVector<T>(ofOffsets offsets: [Offset<T>]) -> Offset<UOffset> {
347 createVector(ofOffsets: offsets, len: offsets.count)
348 }
349
350 /// Creates a vector of type Offsets in the buffer
351 /// - Parameter elements: Array of offsets of type T
352 /// - Parameter size: Count of elements
353 /// - returns: Offset of the vector
354 mutating public func createVector<T>(ofOffsets offsets: [Offset<T>], len: Int) -> Offset<UOffset> {
355 startVector(len, elementSize: MemoryLayout<Offset<T>>.size)
356 for o in offsets.reversed() {
357 push(element: o)
Austin Schuh272c6132020-11-14 16:37:52 -0800358 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800359 return Offset(offset: endVector(len: len))
360 }
361
362 /// Creates a vector of Strings
363 /// - Parameter str: a vector of strings that will be written into the buffer
364 /// - returns: Offset of the vector
365 mutating public func createVector(ofStrings str: [String]) -> Offset<UOffset> {
366 var offsets: [Offset<String>] = []
367 for s in str {
368 offsets.append(create(string: s))
Austin Schuh272c6132020-11-14 16:37:52 -0800369 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800370 return createVector(ofOffsets: offsets)
371 }
372
373 /// Creates a vector of Flatbuffer structs.
374 ///
375 /// The function takes a Type to know what size it is, and alignment
376 /// - Parameters:
377 /// - structs: An array of UnsafeMutableRawPointer
378 /// - type: Type of the struct being written
379 /// - returns: Offset of the vector
380 @available(
381 *,
382 deprecated,
383 message: "0.9.0 will be removing the following method. Regenerate the code")
384 mutating public func createVector<T: Readable>(
385 structs: [UnsafeMutableRawPointer],
386 type: T.Type) -> Offset<UOffset>
387 {
388 startVector(structs.count &* T.size, elementSize: T.alignment)
389 for i in structs.reversed() {
390 create(struct: i, type: T.self)
Austin Schuh272c6132020-11-14 16:37:52 -0800391 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800392 return Offset(offset: endVector(len: structs.count))
393 }
394
395 /// Starts a vector of struct that considers the size and alignment of the struct
396 /// - Parameters:
397 /// - count: number of elements to be written
398 /// - size: size of struct
399 /// - alignment: alignment of the struct
400 mutating public func startVectorOfStructs(count: Int, size: Int, alignment: Int) {
401 startVector(count &* size, elementSize: alignment)
402 }
403
404 /// Ends the vector of structs and writtens the current offset
405 /// - Parameter count: number of written elements
406 /// - Returns: Offset of type UOffset
407 mutating public func endVectorOfStructs(count: Int) -> Offset<UOffset> {
408 Offset<UOffset>(offset: endVector(len: count))
409 }
410
411 // MARK: - Inserting Structs
412
413 /// Writes a Flatbuffer struct into the buffer
414 /// - Parameters:
415 /// - s: Flatbuffer struct
416 /// - type: Type of the element to be serialized
417 /// - returns: Offset of the Object
418 @available(
419 *,
420 deprecated,
421 message: "0.9.0 will be removing the following method. Regenerate the code")
422 @discardableResult
423 mutating public func create<T: Readable>(
424 struct s: UnsafeMutableRawPointer,
425 type: T.Type) -> Offset<UOffset>
426 {
427 let size = T.size
428 preAlign(len: size, alignment: T.alignment)
429 _bb.push(struct: s, size: size)
430 return Offset(offset: _bb.size)
431 }
432
433 /// prepares the ByteBuffer to receive a struct of size and alignment
434 /// - Parameters:
435 /// - size: size of written struct
436 /// - alignment: alignment of written struct
437 mutating public func createStructOf(size: Int, alignment: Int) {
438 preAlign(len: size, alignment: alignment)
439 _bb.prepareBufferToReceiveStruct(of: size)
440 }
441
442 /// Adds scalars front to back instead of the default behavior of the normal add
443 /// - Parameters:
444 /// - v: element of type Scalar
445 /// - postion: position relative to the `writerIndex`
446 mutating public func reverseAdd<T: Scalar>(v: T, postion: Int) {
447 _bb.reversePush(
448 value: v,
449 position: postion,
450 len: MemoryLayout<T>.size)
451 }
452
453 /// Ends the struct and returns the current buffer size
454 /// - Returns: Offset of type UOffset
455 @discardableResult
456 public func endStruct() -> Offset<UOffset> {
457 Offset(offset: _bb.size)
458 }
459
460 /// Adds the offset of a struct into the vTable
461 ///
462 /// The function fatalErrors if we pass an offset that is out of range
463 /// - Parameter o: offset
464 mutating public func add(structOffset o: VOffset) {
465 _vtableStorage.add(loc: FieldLoc(offset: _bb.size, position: VOffset(o)))
466 }
467
468 // MARK: - Inserting Strings
469
470 /// Insets a string into the buffer using UTF8
471 /// - Parameter str: String to be serialized
472 /// - returns: The strings offset in the buffer
473 mutating public func create(string str: String?) -> Offset<String> {
474 guard let str = str else { return Offset() }
475 let len = str.utf8.count
476 notNested()
477 preAlign(len: len &+ 1, type: UOffset.self)
478 _bb.fill(padding: 1)
479 _bb.push(string: str, len: len)
480 push(element: UOffset(len))
481 return Offset(offset: _bb.size)
482 }
483
484 /// Inserts a shared string to the buffer
485 ///
486 /// The function checks the stringOffsetmap if it's seen a similar string before
487 /// - Parameter str: String to be serialized
488 /// - returns: The strings offset in the buffer
489 mutating public func createShared(string str: String?) -> Offset<String> {
490 guard let str = str else { return Offset() }
491 if let offset = stringOffsetMap[str] {
492 return offset
Austin Schuh272c6132020-11-14 16:37:52 -0800493 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800494 let offset = create(string: str)
495 stringOffsetMap[str] = offset
496 return offset
497 }
498
499 // MARK: - Inseting offsets
500
501 /// Adds the offset of an object into the buffer
502 /// - Parameters:
503 /// - offset: Offset of another object to be written
504 /// - position: The predefined position of the object
505 mutating public func add<T>(offset: Offset<T>, at position: VOffset) {
506 if offset.isEmpty { return }
507 add(element: refer(to: offset.o), def: 0, at: position)
508 }
509
510 /// Pushes a value of type offset into the buffer
511 /// - Parameter o: Offset
512 /// - returns: Position of the offset
513 @discardableResult
514 mutating public func push<T>(element o: Offset<T>) -> UOffset {
515 push(element: refer(to: o.o))
516 }
517
518 // MARK: - Inserting Scalars to Buffer
519
520 /// Adds a value into the buffer of type Scalar
521 ///
522 /// - Parameters:
523 /// - element: Element to insert
524 /// - def: Default value for that element
525 /// - position: The predefined position of the element
526 mutating public func add<T: Scalar>(element: T, def: T, at position: VOffset) {
527 if element == def && !serializeDefaults { return }
528 track(offset: push(element: element), at: position)
529 }
530
531 /// Adds a value into the buffer of type optional Scalar
532 /// - Parameters:
533 /// - element: Optional element of type scalar
534 /// - position: The predefined position of the element
535 mutating public func add<T: Scalar>(element: T?, at position: VOffset) {
536 guard let element = element else { return }
537 track(offset: push(element: element), at: position)
538 }
539
540 /// Pushes the values into the buffer
541 /// - Parameter element: Element to insert
542 /// - returns: Postion of the Element
543 @discardableResult
544 mutating public func push<T: Scalar>(element: T) -> UOffset {
545 let size = MemoryLayout<T>.size
546 preAlign(
547 len: size,
548 alignment: size)
549 _bb.push(value: element, len: size)
550 return _bb.size
551 }
552
Austin Schuh272c6132020-11-14 16:37:52 -0800553}
554
555extension FlatBufferBuilder: CustomDebugStringConvertible {
Austin Schuh58b9b472020-11-25 19:12:44 -0800556
557 public var debugDescription: String {
558 """
559 buffer debug:
560 \(_bb)
561 builder debug:
562 { finished: \(finished), serializeDefaults: \(serializeDefaults), isNested: \(isNested) }
563 """
564 }
565
566 /// VTableStorage is a class to contain the VTable buffer that would be serialized into buffer
567 @usableFromInline
568 internal class VTableStorage {
569 /// Memory check since deallocating each time we want to clear would be expensive
570 /// and memory leaks would happen if we dont deallocate the first allocated memory.
571 /// memory is promised to be available before adding `FieldLoc`
572 private var memoryInUse = false
573 /// Size of FieldLoc in memory
574 let size = MemoryLayout<FieldLoc>.stride
575 /// Memeory buffer
576 var memory: UnsafeMutableRawBufferPointer!
577 /// Capacity of the current buffer
578 var capacity: Int = 0
579 /// Maximuim offset written to the class
580 var maxOffset: VOffset = 0
581 /// number of fields written into the buffer
582 var numOfFields: Int = 0
583 /// Last written Index
584 var writtenIndex: Int = 0
585 /// the amount of added elements into the buffer
586 var addedElements: Int { capacity - (numOfFields &* size) }
587
588 /// Creates the memory to store the buffer in
589 init() {
590 memory = UnsafeMutableRawBufferPointer.allocate(byteCount: 0, alignment: 0)
Austin Schuh272c6132020-11-14 16:37:52 -0800591 }
592
Austin Schuh58b9b472020-11-25 19:12:44 -0800593 deinit {
594 memory.deallocate()
595 }
Austin Schuh272c6132020-11-14 16:37:52 -0800596
Austin Schuh58b9b472020-11-25 19:12:44 -0800597 /// Builds a buffer with byte count of fieldloc.size * count of field numbers
598 /// - Parameter count: number of fields to be written
599 func start(count: Int) {
600 assert(count >= 0, "number of fields should NOT be negative")
601 let capacity = count &* size
602 ensure(space: capacity)
Austin Schuh272c6132020-11-14 16:37:52 -0800603 }
Austin Schuh58b9b472020-11-25 19:12:44 -0800604
605 /// Adds a FieldLoc into the buffer, which would track how many have been written,
606 /// and max offset
607 /// - Parameter loc: Location of encoded element
608 func add(loc: FieldLoc) {
609 memory.baseAddress?.advanced(by: writtenIndex).storeBytes(of: loc, as: FieldLoc.self)
610 writtenIndex = writtenIndex &+ size
611 numOfFields = numOfFields &+ 1
612 maxOffset = max(loc.position, maxOffset)
Austin Schuh272c6132020-11-14 16:37:52 -0800613 }
614
Austin Schuh58b9b472020-11-25 19:12:44 -0800615 /// Clears the data stored related to the encoded buffer
616 func clear() {
617 maxOffset = 0
618 numOfFields = 0
619 writtenIndex = 0
620 }
621
622 /// Ensure that the buffer has enough space instead of recreating the buffer each time.
623 /// - Parameter space: space required for the new vtable
624 func ensure(space: Int) {
625 guard space &+ writtenIndex > capacity else { return }
626 memory.deallocate()
627 memory = UnsafeMutableRawBufferPointer.allocate(byteCount: space, alignment: size)
628 capacity = space
629 }
630
631 /// Loads an object of type `FieldLoc` from buffer memory
632 /// - Parameter index: index of element
633 /// - Returns: a FieldLoc at index
634 func load(at index: Int) -> FieldLoc {
635 memory.load(fromByteOffset: index, as: FieldLoc.self)
636 }
637
638 }
639
640 internal struct FieldLoc {
641 var offset: UOffset
642 var position: VOffset
643 }
644
Austin Schuh272c6132020-11-14 16:37:52 -0800645}