• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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