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