1 /*
2 * Copyright 2021 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.google.flatbuffers.kotlin
17
18 import kotlin.jvm.JvmOverloads
19
20
21 /**
22 * Class that helps you build a FlatBuffer. See the section
23 * "Use in Kotlin" in the main FlatBuffers documentation.
24 */
25 public class FlatBufferBuilder @JvmOverloads constructor(
26 private val initialSize: Int = 1024,
27 private var buffer: ReadWriteBuffer = ArrayReadWriteBuffer(initialSize)
28 ) {
29 // Remaining space in the ByteBuffer.
30 private var space: Int = buffer.capacity
31
32 // Minimum alignment encountered so far.
33 private var minalign: Int = 1
34
35 // The vtable for the current table.
36 private var vtable: IntArray = IntArray(16)
37
38 // The amount of fields we're actually using.
39 private var vtableInUse: Int = 0
40
41 // Whether we are currently serializing a table.
42 private var nested: Boolean = false
43
44 // Whether the buffer is finished.
45 private var finished: Boolean = false
46
47 // Starting offset of the current struct/table.
48 private var objectStart: Int = 0
49
50 // List of offsets of all vtables.
51 private var vtables = IntArray(16)
52
53 // Number of entries in `vtables` in use.
54 private var numVtables = 0
55
56 // For the current vector being built.
57 private var vectorNumElems = 0
58
59 // False omits default values from the serialized data.
60 private var forceDefaults = false
61
62 // map used to cache shared strings.
63 private var stringPool: MutableMap<CharSequence, Offset<String>>? = null
64
65 /**
66 * Reset the FlatBufferBuilder by purging all data that it holds.
67 */
clearnull68 public fun clear() {
69 space = buffer.capacity
70 buffer.clear()
71 minalign = 1
72 vtable.fill(0, 0, vtableInUse)
73 vtableInUse = 0
74 nested = false
75 finished = false
76 objectStart = 0
77 numVtables = 0
78 vectorNumElems = 0
79 stringPool?.clear()
80 }
81
82 /**
83 * Offset relative to the end of the buffer.
84 *
85 * @return Offset relative to the end of the buffer.
86 */
offsetnull87 public fun offset(): Int = buffer.capacity - space
88
89 /**
90 * Add zero valued bytes to prepare a new entry to be added.
91 *
92 * @param byteSize Number of bytes to add.
93 */
94 public fun pad(byteSize: Int) {
95 for (i in 0 until byteSize) buffer[--space] = 0.toByte()
96 }
97
98 /**
99 * Prepare to write an element of `size` after `additional_bytes`
100 * have been written, e.g. if you write a string, you need to align such
101 * the int length field is aligned to [com.google.flatbuffers.Int.SIZE_BYTES], and
102 * the string data follows it directly. If all you need to do is alignment, `additional_bytes`
103 * will be 0.
104 *
105 * @param size This is the of the new element to write.
106 * @param additionalBytes The padding size.
107 */
prepnull108 public fun prep(size: Int, additionalBytes: Int) {
109 // Track the biggest thing we've ever aligned to.
110 if (size > minalign) minalign = size
111 // Find the amount of alignment needed such that `size` is properly
112 // aligned after `additional_bytes`
113
114 val alignSize: Int = ((buffer.capacity - space + additionalBytes).inv() + 1).and(size - 1)
115 // Reallocate the buffer if needed.
116 while (space < alignSize + size + additionalBytes) {
117 val oldBufSize: Int = buffer.capacity
118 val newBufSize = buffer.moveWrittenDataToEnd(oldBufSize + alignSize + size + additionalBytes)
119 space += newBufSize - oldBufSize
120 }
121 if (alignSize > 0) {
122 pad(alignSize)
123 }
124 }
125
126 /**
127 * Add a `boolean` to the buffer, backwards from the current location. Doesn't align nor
128 * check for space.
129 *
130 * @param x A `boolean` to put into the buffer.
131 */
putnull132 public fun put(x: Boolean) {
133 space -= Byte.SIZE_BYTES
134 buffer[space] = (if (x) 1 else 0).toByte()
135 }
136
137 /**
138 * Add a [UByte] to the buffer, backwards from the current location. Doesn't align nor
139 * check for space.
140 *
141 * @param x A [UByte] to put into the buffer.
142 */
putnull143 public fun put(x: UByte): Unit = put(x.toByte())
144
145 /**
146 * Add a [Byte] to the buffer, backwards from the current location. Doesn't align nor
147 * check for space.
148 *
149 * @param x A [Byte] to put into the buffer.
150 */
151 public fun put(x: Byte) {
152 space -= Byte.SIZE_BYTES
153 buffer[space] = x
154 }
155
156 /**
157 * Add a [UShort] to the buffer, backwards from the current location. Doesn't align nor
158 * check for space.
159 *
160 * @param x A [UShort] to put into the buffer.
161 */
putnull162 public fun put(x: UShort): Unit = put(x.toShort())
163
164 /**
165 * Add a [Short] to the buffer, backwards from the current location. Doesn't align nor
166 * check for space.
167 *
168 * @param x A [Short] to put into the buffer.
169 */
170 public fun put(x: Short) {
171 space -= Short.SIZE_BYTES
172 buffer.set(space, x)
173 }
174
175 /**
176 * Add an [UInt] to the buffer, backwards from the current location. Doesn't align nor
177 * check for space.
178 *
179 * @param x An [UInt] to put into the buffer.
180 */
putnull181 public fun put(x: UInt): Unit = put(x.toInt())
182
183 /**
184 * Add an [Int] to the buffer, backwards from the current location. Doesn't align nor
185 * check for space.
186 *
187 * @param x An [Int] to put into the buffer.
188 */
189 public fun put(x: Int){
190 space -= Int.SIZE_BYTES
191 buffer.set(space, x)
192 }
193
194 /**
195 * Add a [ULong] to the buffer, backwards from the current location. Doesn't align nor
196 * check for space.
197 *
198 * @param x A [ULong] to put into the buffer.
199 */
putnull200 public fun put(x: ULong): Unit = put(x.toLong())
201
202 /**
203 * Add a [Long] to the buffer, backwards from the current location. Doesn't align nor
204 * check for space.
205 *
206 * @param x A [Long] to put into the buffer.
207 */
208 public fun put(x: Long) {
209 space -= Long.SIZE_BYTES
210 buffer.set(space, x)
211 }
212
213 /**
214 * Add a [Float] to the buffer, backwards from the current location. Doesn't align nor
215 * check for space.
216 *
217 * @param x A [Float] to put into the buffer.
218 */
putnull219 public fun put(x: Float) {
220 space -= Float.SIZE_BYTES
221 buffer.set(space, x)
222 }
223
224 /**
225 * Add a [Double] to the buffer, backwards from the current location. Doesn't align nor
226 * check for space.
227 *
228 * @param x A [Double] to put into the buffer.
229 */
putnull230 public fun put(x: Double) {
231 space -= Double.SIZE_BYTES
232 buffer.set(space, x)
233 }
234
235 /**
236 * Add a [Boolean] to the buffer, properly aligned, and grows the buffer (if necessary).
237 *
238 * @param x A [Boolean] to put into the buffer.
239 */
addnull240 public fun add(x: Boolean) {
241 prep(Byte.SIZE_BYTES, 0)
242 put(x)
243 }
244
245 /**
246 * Add a [UByte] to the buffer, properly aligned, and grows the buffer (if necessary).
247 *
248 * @param x A [UByte] to put into the buffer.
249 */
addnull250 public fun add(x: UByte): Unit = add(x.toByte())
251
252 /**
253 * Add a [Byte] to the buffer, properly aligned, and grows the buffer (if necessary).
254 *
255 * @param x A [Byte] to put into the buffer.
256 */
257 public fun add(x: Byte) {
258 prep(Byte.SIZE_BYTES, 0)
259 put(x)
260 }
261
262 /**
263 * Add a [UShort] to the buffer, properly aligned, and grows the buffer (if necessary).
264 *
265 * @param x A [UShort] to put into the buffer.
266 */
addnull267 public fun add(x: UShort): Unit = add(x.toShort())
268
269 /**
270 * Add a [Short] to the buffer, properly aligned, and grows the buffer (if necessary).
271 *
272 * @param x A [Short] to put into the buffer.
273 */
274 public fun add(x: Short) {
275 prep(Short.SIZE_BYTES, 0)
276 put(x)
277 }
278
279 /**
280 * Add an [Unit] to the buffer, properly aligned, and grows the buffer (if necessary).
281 *
282 * @param x An [Unit] to put into the buffer.
283 */
addnull284 public fun add(x: UInt): Unit = add(x.toInt())
285
286 /**
287 * Add an [Int] to the buffer, properly aligned, and grows the buffer (if necessary).
288 *
289 * @param x An [Int] to put into the buffer.
290 */
291 public fun add(x: Int) {
292 prep(Int.SIZE_BYTES, 0)
293 put(x)
294 }
295
296 /**
297 * Add a [ULong] to the buffer, properly aligned, and grows the buffer (if necessary).
298 *
299 * @param x A [ULong] to put into the buffer.
300 */
addnull301 public fun add(x: ULong): Unit = add(x.toLong())
302
303 /**
304 * Add a `long` to the buffer, properly aligned, and grows the buffer (if necessary).
305 *
306 * @param x A `long` to put into the buffer.
307 */
308 public fun add(x: Long) {
309 prep(Long.SIZE_BYTES, 0)
310 put(x)
311 }
312
313 /**
314 * Add a [Float] to the buffer, properly aligned, and grows the buffer (if necessary).
315 *
316 * @param x A [Float] to put into the buffer.
317 */
addnull318 public fun add(x: Float) {
319 prep(Float.SIZE_BYTES, 0)
320 put(x)
321 }
322
323 /**
324 * Add a [Double] to the buffer, properly aligned, and grows the buffer (if necessary).
325 *
326 * @param x A [Double] to put into the buffer.
327 */
addnull328 public fun add(x: Double) {
329 prep(Double.SIZE_BYTES, 0)
330 put(x)
331 }
332
333 /**
334 * Adds on offset, relative to where it will be written.
335 *
336 * @param off The offset to add.
337 */
addnull338 public fun add(off: Offset<*>): Unit = addOffset(off.value)
339 public fun add(off: VectorOffset<*>): Unit = addOffset(off.value)
340 private fun addOffset(off: Int) {
341 prep(Int.SIZE_BYTES, 0) // Ensure alignment is already done.
342 put(buffer.capacity - space - off + Int.SIZE_BYTES)
343 }
344
345 /**
346 * Start a new array/vector of objects. Users usually will not call
347 * this directly. The `FlatBuffers` compiler will create a start/end
348 * method for vector types in generated code.
349 *
350 *
351 * The expected sequence of calls is:
352 *
353 * 1. Start the array using this method.
354 * 1. Call [.addOffset] `num_elems` number of times to set
355 * the offset of each element in the array.
356 * 1. Call [.endVector] to retrieve the offset of the array.
357 *
358 *
359 *
360 * For example, to create an array of strings, do:
361 * <pre>`// Need 10 strings
362 * FlatBufferBuilder builder = new FlatBufferBuilder(existingBuffer);
363 * int[] offsets = new int[10];
364 *
365 * for (int i = 0; i < 10; i++) {
366 * offsets[i] = fbb.createString(" " + i);
367 * }
368 *
369 * // Have the strings in the buffer, but don't have a vector.
370 * // Add a vector that references the newly created strings:
371 * builder.startVector(4, offsets.length, 4);
372 *
373 * // Add each string to the newly created vector
374 * // The strings are added in reverse order since the buffer
375 * // is filled in back to front
376 * for (int i = offsets.length - 1; i >= 0; i--) {
377 * builder.addOffset(offsets[i]);
378 * }
379 *
380 * // Finish off the vector
381 * int offsetOfTheVector = fbb.endVector();
382 `</pre> *
383 *
384 * @param elemSize The size of each element in the array.
385 * @param numElems The number of elements in the array.
386 * @param alignment The alignment of the array.
387 */
startVectornull388 public fun startVector(elemSize: Int, numElems: Int, alignment: Int) {
389 notNested()
390 vectorNumElems = numElems
391 prep(Int.SIZE_BYTES, elemSize * numElems)
392 prep(alignment, elemSize * numElems) // Just in case alignment > int.
393 nested = true
394 }
startStringnull395 public fun startString(numElems: Int): Unit = startVector(1, numElems, 1)
396
397 /**
398 * Finish off the creation of an array and all its elements. The array
399 * must be created with [.startVector].
400 *
401 * @return The offset at which the newly created array starts.
402 * @see .startVector
403 */
404 public fun <T> endVector(): VectorOffset<T> {
405 if (!nested) throw AssertionError("FlatBuffers: endVector called without startVector")
406 nested = false
407 put(vectorNumElems)
408 return VectorOffset(offset())
409 }
410
endStringnull411 public fun endString(): Offset<String> {
412 if (!nested) throw AssertionError("FlatBuffers: endString called without startString")
413 nested = false
414 put(vectorNumElems)
415 return Offset(offset())
416 }
417
endVectornull418 private fun endVector(): Int {
419 if (!nested) throw AssertionError("FlatBuffers: endVector called without startVector")
420 nested = false
421 put(vectorNumElems)
422 return offset()
423 }
424
425 /**
426 * Create a new array/vector and return a ByteBuffer to be filled later.
427 * Call [endVector] after this method to get an offset to the beginning
428 * of vector.
429 *
430 * @param elemSize the size of each element in bytes.
431 * @param numElems number of elements in the vector.
432 * @param alignment byte alignment.
433 * @return ByteBuffer with position and limit set to the space allocated for the array.
434 */
createUnintializedVectornull435 public fun createUnintializedVector(elemSize: Int, numElems: Int, alignment: Int): ReadWriteBuffer {
436 val length = elemSize * numElems
437 startVector(elemSize, numElems, alignment)
438 space -= length
439 buffer.writePosition = space
440 return buffer.writeSlice(buffer.writePosition, length)
441 }
442
443 /**
444 * Create a vector of tables.
445 *
446 * @param offsets Offsets of the tables.
447 * @return Returns offset of the vector.
448 */
createVectorOfTablesnull449 public fun <T> createVectorOfTables(offsets: Array<Offset<T>>): VectorOffset<T> {
450 notNested()
451 startVector(Int.SIZE_BYTES, offsets.size, Int.SIZE_BYTES)
452 for (i in offsets.indices.reversed()) add(offsets[i])
453 return VectorOffset(endVector())
454 }
455
456 /**
457 * Create a vector of sorted by the key tables.
458 *
459 * @param obj Instance of the table subclass.
460 * @param offsets Offsets of the tables.
461 * @return Returns offset of the sorted vector.
462 */
createSortedVectorOfTablesnull463 public fun <T : Table> createSortedVectorOfTables(obj: T, offsets: Array<Offset<T>>): VectorOffset<T> {
464 obj.sortTables(offsets, buffer)
465 return createVectorOfTables(offsets)
466 }
467
468 /**
469 * Encode the String `s` in the buffer using UTF-8. If a String with
470 * this exact contents has already been serialized using this method,
471 * instead simply returns the offset of the existing String.
472 *
473 * Usage of the method will incur into additional allocations,
474 * so it is advisable to use it only when it is known upfront that
475 * your message will have several repeated strings.
476 *
477 * @param s The String to encode.
478 * @return The offset in the buffer where the encoded String starts.
479 */
createSharedStringnull480 public fun createSharedString(s: CharSequence): Offset<String> {
481 if (stringPool == null) {
482 stringPool = HashMap()
483 val offset = createString(s)
484 stringPool!![s] = offset
485 return offset
486 }
487 var offset = stringPool!![s]
488 if (offset == null) {
489 offset = createString(s)
490 stringPool?.put(s, offset)
491 }
492 return offset
493 }
494
495 /**
496 * Encode the [CharSequence] `s` in the buffer using UTF-8.
497 * @param s The [CharSequence] to encode.
498 * @return The offset in the buffer where the encoded string starts.
499 */
createStringnull500 public fun createString(s: CharSequence): Offset<String> {
501 val length: Int = Utf8.encodedLength(s)
502 add(0.toByte())
503 startString(length)
504 space -= length
505 buffer.writePosition = space
506 buffer.put(s, length)
507 return endString()
508 }
509
510 /**
511 * Create a string in the buffer from an already encoded UTF-8 string in a ByteBuffer.
512 *
513 * @param s An already encoded UTF-8 string as a `ByteBuffer`.
514 * @return The offset in the buffer where the encoded string starts.
515 */
createStringnull516 public fun createString(s: ReadBuffer): Offset<String> {
517 val length: Int = s.limit
518 add(0.toByte())
519 startVector(1, length, 1)
520 space -= length
521 buffer.writePosition = space
522 buffer.put(s)
523 return endString()
524 }
525
526 /**
527 * Create a byte array in the buffer.
528 *
529 * @param arr A source array with data
530 * @return The offset in the buffer where the encoded array starts.
531 */
createByteVectornull532 public fun createByteVector(arr: ByteArray): VectorOffset<Byte> {
533 val length = arr.size
534 startVector(1, length, 1)
535 space -= length
536 buffer.writePosition = space
537 buffer.put(arr)
538 return VectorOffset(endVector())
539 }
540
541 /**
542 * Create a byte array in the buffer.
543 *
544 * @param arr a source array with data.
545 * @param offset the offset in the source array to start copying from.
546 * @param length the number of bytes to copy from the source array.
547 * @return The offset in the buffer where the encoded array starts.
548 */
createByteVectornull549 public fun createByteVector(arr: ByteArray, offset: Int, length: Int): VectorOffset<Byte> {
550 startVector(1, length, 1)
551 space -= length
552 buffer.writePosition = space
553 buffer.put(arr, offset, length)
554 return VectorOffset(endVector())
555 }
556
557 /**
558 * Create a byte array in the buffer.
559 *
560 * The source [ReadBuffer] position is advanced until [ReadBuffer.limit]
561 * after this call.
562 *
563 * @param data A source [ReadBuffer] with data.
564 * @return The offset in the buffer where the encoded array starts.
565 */
createByteVectornull566 public fun createByteVector(data: ReadBuffer, from: Int = 0, until: Int = data.limit): VectorOffset<Byte> {
567 val length: Int = until - from
568 startVector(1, length, 1)
569 space -= length
570 buffer.writePosition = space
571 buffer.put(data, from, until)
572 return VectorOffset(endVector())
573 }
574
575 /**
576 * Should not be accessing the final buffer before it is finished.
577 */
finishednull578 public fun finished() {
579 if (!finished) throw AssertionError(
580 "FlatBuffers: you can only access the serialized buffer after it has been" +
581 " finished by FlatBufferBuilder.finish()."
582 )
583 }
584
585 /**
586 * Should not be creating any other object, string or vector
587 * while an object is being constructed.
588 */
notNestednull589 public fun notNested() {
590 if (nested) throw AssertionError("FlatBuffers: object serialization must not be nested.")
591 }
592
593 /**
594 * Structures are always stored inline, they need to be created right
595 * where they're used. You'll get this assertion failure if you
596 * created it elsewhere.
597 *
598 * @param obj The offset of the created object.
599 */
nestednull600 public fun nested(obj: Int) {
601 if (obj != offset()) throw AssertionError("FlatBuffers: struct must be serialized inline.")
602 }
603
604 /**
605 * Start encoding a new object in the buffer. Users will not usually need to
606 * call this directly. The `FlatBuffers` compiler will generate helper methods
607 * that call this method internally.
608 *
609 *
610 * For example, using the "Monster" code found on the "landing page". An
611 * object of type `Monster` can be created using the following code:
612 *
613 * <pre>`int testArrayOfString = Monster.createTestarrayofstringVector(fbb, new int[] {
614 * fbb.createString("test1"),
615 * fbb.createString("test2")
616 * });
617 *
618 * Monster.startMonster(fbb);
619 * Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0,
620 * Color.Green, (short)5, (byte)6));
621 * Monster.addHp(fbb, (short)80);
622 * Monster.addName(fbb, str);
623 * Monster.addInventory(fbb, inv);
624 * Monster.addTestType(fbb, (byte)Any.Monster);
625 * Monster.addTest(fbb, mon2);
626 * Monster.addTest4(fbb, test4);
627 * Monster.addTestarrayofstring(fbb, testArrayOfString);
628 * int mon = Monster.endMonster(fbb);
629 `</pre> *
630 *
631 *
632 * Here:
633 *
634 * * The call to `Monster#startMonster(FlatBufferBuilder)` will call this
635 * method with the right number of fields set.
636 * * `Monster#endMonster(FlatBufferBuilder)` will ensure [.endObject] is called.
637 *
638 *
639 *
640 * It's not recommended to call this method directly. If it's called manually, you must ensure
641 * to audit all calls to it whenever fields are added or removed from your schema. This is
642 * automatically done by the code generated by the `FlatBuffers` compiler.
643 *
644 * @param numFields The number of fields found in this object.
645 */
startTablenull646 public fun startTable(numFields: Int) {
647 notNested()
648 if (vtable.size < numFields) {
649 vtable = IntArray(numFields)
650 }
651 vtableInUse = numFields
652 for (i in 0 until vtableInUse)
653 vtable[i] = 0
654 nested = true
655 objectStart = offset()
656 }
657
658 /**
659 * Add a [Boolean] to a table at `o` into its vtable, with value `x` and default `d`.
660 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
661 * default value, it can be skipped.
662 */
addnull663 public fun add(o: Int, x: Boolean, d: Boolean?) {
664 if (forceDefaults || x != d) {
665 add(x)
666 slot(o)
667 }
668 }
669 // unboxed specialization
addnull670 public fun add(o: Int, x: Boolean, d: Boolean) {
671 if (forceDefaults || x != d) {
672 add(x)
673 slot(o)
674 }
675 }
676
677 /**
678 * Add a [UByte] to a table at `o` into its vtable, with value `x` and default `d`.
679 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
680 * default value, it can be skipped.
681 */
addnull682 public fun add(o: Int, x: UByte, d: UByte?): Unit = add(o, x.toByte(), d?.toByte())
683 // unboxed specialization
684 public fun add(o: Int, x: UByte, d: UByte): Unit = add(o, x.toByte(), d.toByte())
685
686 /**
687 * Add a [Byte] to a table at `o` into its vtable, with value `x` and default `d`.
688 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
689 * default value, it can be skipped.
690 */
691 public fun add(o: Int, x: Byte, d: Byte?) {
692 if (forceDefaults || x != d) {
693 add(x)
694 slot(o)
695 }
696 }
697 // unboxed specialization
addnull698 public fun add(o: Int, x: Byte, d: Byte) {
699 if (forceDefaults || x != d) {
700 add(x)
701 slot(o)
702 }
703 }
704 /**
705 * Add a [UShort] to a table at `o` into its vtable, with value `x` and default `d`.
706 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
707 * default value, it can be skipped.
708 */
addnull709 public fun add(o: Int, x: UShort, d: UShort?): Unit = add(o, x.toShort(), d?.toShort())
710 // unboxed specialization
711 public fun add(o: Int, x: UShort, d: UShort): Unit = add(o, x.toShort(), d.toShort())
712
713
714 /**
715 * Add a [Short] to a table at `o` into its vtable, with value `x` and default `d`.
716 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
717 * default value, it can be skipped.
718 */
719 public fun add(o: Int, x: Short, d: Short?) {
720 if (forceDefaults || x != d) {
721 add(x)
722 slot(o)
723 }
724 }
725 // unboxed specialization
addnull726 public fun add(o: Int, x: Short, d: Short) {
727 if (forceDefaults || x != d) {
728 add(x)
729 slot(o)
730 }
731 }
732
733 /**
734 * Add a [UInt] to a table at `o` into its vtable, with value `x` and default `d`.
735 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
736 * default value, it can be skipped.
737 */
addnull738 public fun add(o: Int, x: UInt, d: UInt?): Unit = add(o, x.toInt(), d?.toInt())
739 // unboxed specialization
740 public fun add(o: Int, x: UInt, d: UInt): Unit = add(o, x.toInt(), d.toInt())
741
742 /**
743 * Add a [Int] to a table at `o` into its vtable, with value `x` and default `d`.
744 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
745 * default value, it can be skipped.
746 */
747 public fun add(o: Int, x: Int, d: Int?) {
748 if (forceDefaults || x != d) {
749 add(x)
750 slot(o)
751 }
752 }
753 // unboxed specialization
addnull754 public fun add(o: Int, x: Int, d: Int) {
755 if (forceDefaults || x != d) {
756 add(x)
757 slot(o)
758 }
759 }
760 /**
761 * Add a [ULong] to a table at `o` into its vtable, with value `x` and default `d`.
762 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
763 * default value, it can be skipped.
764 */
addnull765 public fun add(o: Int, x: ULong, d: ULong?): Unit = add(o, x.toLong(), d?.toLong())
766 // unboxed specialization
767 public fun add(o: Int, x: ULong, d: ULong): Unit = add(o, x.toLong(), d.toLong())
768 /**
769 * Add a [Long] to a table at `o` into its vtable, with value `x` and default `d`.
770 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
771 * default value, it can be skipped.
772 */
773 public fun add(o: Int, x: Long, d: Long?) {
774 if (forceDefaults || x != d) {
775 add(x)
776 slot(o)
777 }
778 }
779 // unboxed specialization
addnull780 public fun add(o: Int, x: Long, d: Long) {
781 if (forceDefaults || x != d) {
782 add(x)
783 slot(o)
784 }
785 }
786
787 /**
788 * Add a [Float] to a table at `o` into its vtable, with value `x` and default `d`.
789 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
790 * default value, it can be skipped.
791 */
addnull792 public fun add(o: Int, x: Float, d: Float?) {
793 if (forceDefaults || x != d) {
794 add(x)
795 slot(o)
796 }
797 }
798 // unboxed specialization
addnull799 public fun add(o: Int, x: Float, d: Float) {
800 if (forceDefaults || x != d) {
801 add(x)
802 slot(o)
803 }
804 }
805
806 /**
807 * Add a [Double] to a table at `o` into its vtable, with value `x` and default `d`.
808 * If `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
809 * default value, it can be skipped.
810 */
addnull811 public fun add(o: Int, x: Double, d: Double?) {
812 if (forceDefaults || x != d) {
813 add(x)
814 slot(o)
815 }
816 }
817 // unboxed specialization
addnull818 public fun add(o: Int, x: Double, d: Double) {
819 if (forceDefaults || x != d) {
820 add(x)
821 slot(o)
822 }
823 }
824
825 /**
826 * Add an `offset` to a table at `o` into its vtable, with value `x` and default `d`.
827 *
828 * @param o The index into the vtable.
829 * @param x An `offset` to put into the buffer, depending on how defaults are handled. If
830 * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
831 * default value, it can be skipped.
832 * @param d An `offset` default value to compare against when `force_defaults` is `false`.
833 */
addnull834 public fun add(o: Int, x: Offset<*>, d: Int) {
835 if (forceDefaults || x.value != d) {
836 add(x)
837 slot(o)
838 }
839 }
addnull840 public fun add(o: Int, x: VectorOffset<*>, d: Int) {
841 if (forceDefaults || x.value != d) {
842 add(x)
843 slot(o)
844 }
845 }
846
847 /**
848 * Add a struct to the table. Structs are stored inline, so nothing additional is being added.
849 *
850 * @param vOffset The index into the vtable.
851 * @param x The offset of the created struct.
852 * @param d The default value is always `0`.
853 */
addStructnull854 public fun addStruct(vOffset: Int, x: Offset<*>, d: Offset<*>?): Unit = addStruct(vOffset, x.value, d?.value)
855 // unboxed specialization
856 public fun addStruct(vOffset: Int, x: Offset<*>, d: Offset<*>): Unit = addStruct(vOffset, x.value, d.value)
857 public fun addStruct(vOffset: Int, x: Int, d: Int?) {
858 if (x != d) {
859 nested(x)
860 slot(vOffset)
861 }
862 }
863 // unboxed specialization
addStructnull864 public fun addStruct(vOffset: Int, x: Int, d: Int) {
865 if (x != d) {
866 nested(x)
867 slot(vOffset)
868 }
869 }
870
871 /**
872 * Set the current vtable at `voffset` to the current location in the buffer.
873 *
874 * @param vOffset The index into the vtable to store the offset relative to the end of the
875 * buffer.
876 */
slotnull877 public fun slot(vOffset: Int) {
878 vtable[vOffset] = offset()
879 }
880
881 /**
882 * Finish off writing the object that is under construction.
883 *
884 * @return The offset to the object inside [.dataBuffer].
885 * @see .startTable
886 */
endTablenull887 public fun <T> endTable(): Offset<T> {
888 if (!nested) throw AssertionError("FlatBuffers: endTable called without startTable")
889
890 val vtable = this.vtable
891
892 add(0)
893 val vtableloc = offset()
894 // Write out the current vtable.
895 var i: Int = vtableInUse - 1
896 // Trim trailing zeroes.
897 while (i >= 0 && vtable[i] == 0) {
898 i--
899 }
900 val trimmedSize = i + 1
901 while (i >= 0) {
902 // Offset relative to the start of the table.
903 add((if (vtable[i] != 0) vtableloc - vtable[i] else 0).toShort())
904 i--
905 }
906
907 add((vtableloc - objectStart).toShort())
908 add(((trimmedSize + 2) * Short.SIZE_BYTES).toShort())
909
910 // Search for an existing vtable that matches the current one.
911 var existingVtable = 0
912 i = 0
913 outer_loop@ while (i < numVtables) {
914 val vt1: Int = buffer.capacity - vtables[i]
915 val vt2 = space
916 val len: Short = buffer.getShort(vt1)
917 if (len == buffer.getShort(vt2)) {
918 var j: Int = Short.SIZE_BYTES
919 while (j < len) {
920 if (buffer.getShort(vt1 + j) != buffer.getShort(vt2 + j)) {
921 i++
922 continue@outer_loop
923 }
924 j += Short.SIZE_BYTES
925 }
926 existingVtable = vtables[i]
927 break@outer_loop
928 }
929 i++
930 }
931 if (existingVtable != 0) {
932 // Found a match:
933 // Remove the current vtable.
934 space = buffer.capacity - vtableloc
935 // Point table to existing vtable.
936 buffer.set(space, existingVtable - vtableloc)
937 } else {
938 // No match:
939 // Add the location of the current vtable to the list of vtables.
940 if (numVtables == vtables.size) vtables = vtables.copyOf(numVtables * 2)
941 vtables[numVtables++] = offset()
942 // Point table to current vtable.
943 buffer.set(buffer.capacity - vtableloc, offset() - vtableloc)
944 }
945 nested = false
946 return Offset(vtableloc)
947 }
948
949 /**
950 * Checks that a required field has been set in a given table that has
951 * just been constructed.
952 *
953 * @param table The offset to the start of the table from the `ByteBuffer` capacity.
954 * @param field The offset to the field in the vtable.
955 */
requirednull956 public fun required(table: Offset<*>, field: Int, fileName: String? = null) {
957 val tableStart: Int = buffer.capacity - table
958 val vtableStart: Int = tableStart - buffer.getInt(tableStart)
959 val ok = buffer.getShort(vtableStart + field).toInt() != 0
960 // If this fails, the caller will show what field needs to be set.
961 if (!ok) throw AssertionError("FlatBuffers: field ${fileName ?: field} must be set")
962 }
963
964 /**
965 * Finalize a buffer, pointing to the given `root_table`.
966 *
967 * @param rootTable An offset to be added to the buffer.
968 * @param sizePrefix Whether to prefix the size to the buffer.
969 */
finishnull970 protected fun finish(rootTable: Offset<*>, sizePrefix: Boolean) {
971 prep(minalign, Int.SIZE_BYTES + if (sizePrefix) Int.SIZE_BYTES else 0)
972 add(rootTable)
973 if (sizePrefix) {
974 add(buffer.capacity - space)
975 }
976 buffer.writePosition = space
977 finished = true
978 }
979
980 /**
981 * Finalize a buffer, pointing to the given `root_table`.
982 *
983 * @param rootTable An offset to be added to the buffer.
984 */
finishnull985 public fun finish(rootTable: Offset<*>) {
986 finish(rootTable, false)
987 }
988
989 /**
990 * Finalize a buffer, pointing to the given `root_table`, with the size prefixed.
991 *
992 * @param rootTable An offset to be added to the buffer.
993 */
finishSizePrefixednull994 public fun finishSizePrefixed(rootTable: Offset<*>) {
995 finish(rootTable, true)
996 }
997
998 /**
999 * Finalize a buffer, pointing to the given `root_table`.
1000 *
1001 * @param rootTable An offset to be added to the buffer.
1002 * @param fileIdentifier A FlatBuffer file identifier to be added to the buffer before
1003 * `root_table`.
1004 * @param sizePrefix Whether to prefix the size to the buffer.
1005 */
finishnull1006 protected fun finish(rootTable: Offset<*>, fileIdentifier: String, sizePrefix: Boolean) {
1007 val identifierSize = 4
1008 prep(minalign, Int.SIZE_BYTES + identifierSize + if (sizePrefix) Int.SIZE_BYTES else 0)
1009 if (fileIdentifier.length != identifierSize) throw AssertionError(
1010 "FlatBuffers: file identifier must be length " +
1011 identifierSize
1012 )
1013 for (i in identifierSize - 1 downTo 0) {
1014 add(fileIdentifier[i].code.toByte())
1015 }
1016 finish(rootTable, sizePrefix)
1017 }
1018
1019 /**
1020 * Finalize a buffer, pointing to the given `root_table`.
1021 *
1022 * @param rootTable An offset to be added to the buffer.
1023 * @param fileIdentifier A FlatBuffer file identifier to be added to the buffer before
1024 * `root_table`.
1025 */
finishnull1026 public fun finish(rootTable: Offset<*>, fileIdentifier: String) {
1027 finish(rootTable, fileIdentifier, false)
1028 }
1029
1030 /**
1031 * Finalize a buffer, pointing to the given `root_table`, with the size prefixed.
1032 *
1033 * @param rootTable An offset to be added to the buffer.
1034 * @param fileIdentifier A FlatBuffer file identifier to be added to the buffer before
1035 * `root_table`.
1036 */
finishSizePrefixednull1037 public fun finishSizePrefixed(rootTable: Offset<*>, fileIdentifier: String) {
1038 finish(rootTable, fileIdentifier, true)
1039 }
1040
1041 /**
1042 * In order to save space, fields that are set to their default value
1043 * don't get serialized into the buffer. Forcing defaults provides a
1044 * way to manually disable this optimization.
1045 *
1046 * @param forceDefaults When set to `true`, always serializes default values.
1047 * @return Returns `this`.
1048 */
forceDefaultsnull1049 public fun forceDefaults(forceDefaults: Boolean): FlatBufferBuilder {
1050 this.forceDefaults = forceDefaults
1051 return this
1052 }
1053
1054 /**
1055 * Get the ByteBuffer representing the FlatBuffer. Only call this after you've
1056 * called `finish()`. The actual data starts at the ByteBuffer's current position,
1057 * not necessarily at `0`.
1058 *
1059 * @return The [ReadBuffer] representing the FlatBuffer
1060 */
dataBuffernull1061 public fun dataBuffer(): ReadWriteBuffer {
1062 finished()
1063 return buffer
1064 }
1065
1066 /**
1067 * A utility function to copy and return the ByteBuffer data as a `byte[]`.
1068 *
1069 * @return A full copy of the [data buffer][.dataBuffer].
1070 */
sizedByteArraynull1071 public fun sizedByteArray(start: Int = space, length: Int = buffer.capacity - space): ByteArray {
1072 finished()
1073 val array = ByteArray(length)
1074 buffer.getBytes(array, start)
1075 return array
1076 }
1077
1078 /**
1079 * Helper function to test if a field is present in the table
1080 *
1081 * @param offset virtual table offset
1082 * @return true if the filed is present
1083 */
isFieldPresentnull1084 public fun Table.isFieldPresent(offset: Int): Boolean = this.offset(offset) != 0
1085 }
1086
1087 public fun Double.sign(): Double = when {
1088 this.isNaN() -> Double.NaN
1089 this > 0 -> 1.0
1090 this < 0 -> -1.0
1091 else -> this
1092 }
1093
signnull1094 public fun Float.sign(): Float = when {
1095 this.isNaN() -> Float.NaN
1096 this > 0 -> 1.0f
1097 this < 0 -> -1.0f
1098 else -> this
1099 }
1100
signnull1101 public fun Int.sign(): Int = when {
1102 this > 0 -> 1
1103 this < 0 -> -1
1104 else -> this
1105 }
1106