• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 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 
17 package com.google.flatbuffers;
18 
19 import static com.google.flatbuffers.Constants.*;
20 
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.nio.*;
24 import java.util.Arrays;
25 
26 /// @file
27 /// @addtogroup flatbuffers_java_api
28 /// @{
29 
30 /**
31  * Class that helps you build a FlatBuffer.  See the section
32  * "Use in Java/C#" in the main FlatBuffers documentation.
33  */
34 public class FlatBufferBuilder {
35     /// @cond FLATBUFFERS_INTERNAL
36     ByteBuffer bb;                  // Where we construct the FlatBuffer.
37     int space;                      // Remaining space in the ByteBuffer.
38     int minalign = 1;               // Minimum alignment encountered so far.
39     int[] vtable = null;            // The vtable for the current table.
40     int vtable_in_use = 0;          // The amount of fields we're actually using.
41     boolean nested = false;         // Whether we are currently serializing a table.
42     boolean finished = false;       // Whether the buffer is finished.
43     int object_start;               // Starting offset of the current struct/table.
44     int[] vtables = new int[16];    // List of offsets of all vtables.
45     int num_vtables = 0;            // Number of entries in `vtables` in use.
46     int vector_num_elems = 0;       // For the current vector being built.
47     boolean force_defaults = false; // False omits default values from the serialized data.
48     ByteBufferFactory bb_factory;   // Factory for allocating the internal buffer
49     final Utf8 utf8;                // UTF-8 encoder to use
50     /// @endcond
51 
52     /**
53      * Start with a buffer of size `initial_size`, then grow as required.
54      *
55      * @param initial_size The initial size of the internal buffer to use.
56      * @param bb_factory The factory to be used for allocating the internal buffer
57      */
FlatBufferBuilder(int initial_size, ByteBufferFactory bb_factory)58     public FlatBufferBuilder(int initial_size, ByteBufferFactory bb_factory) {
59         this(initial_size, bb_factory, null, Utf8.getDefault());
60     }
61 
62     /**
63      * Start with a buffer of size `initial_size`, then grow as required.
64      *
65      * @param initial_size The initial size of the internal buffer to use.
66      * @param bb_factory The factory to be used for allocating the internal buffer
67      * @param existing_bb The byte buffer to reuse.
68      * @param utf8 The Utf8 codec
69      */
FlatBufferBuilder(int initial_size, ByteBufferFactory bb_factory, ByteBuffer existing_bb, Utf8 utf8)70     public FlatBufferBuilder(int initial_size, ByteBufferFactory bb_factory,
71                              ByteBuffer existing_bb, Utf8 utf8) {
72         if (initial_size <= 0) {
73           initial_size = 1;
74         }
75         space = initial_size;
76         this.bb_factory = bb_factory;
77         if (existing_bb != null) {
78           bb = existing_bb;
79           bb.clear();
80           bb.order(ByteOrder.LITTLE_ENDIAN);
81         } else {
82           bb = bb_factory.newByteBuffer(initial_size);
83         }
84         this.utf8 = utf8;
85     }
86 
87    /**
88     * Start with a buffer of size `initial_size`, then grow as required.
89     *
90     * @param initial_size The initial size of the internal buffer to use.
91     */
FlatBufferBuilder(int initial_size)92     public FlatBufferBuilder(int initial_size) {
93         this(initial_size, HeapByteBufferFactory.INSTANCE, null, Utf8.getDefault());
94     }
95 
96     /**
97      * Start with a buffer of 1KiB, then grow as required.
98      */
FlatBufferBuilder()99     public FlatBufferBuilder() {
100         this(1024);
101     }
102 
103     /**
104      * Alternative constructor allowing reuse of {@link ByteBuffer}s.  The builder
105      * can still grow the buffer as necessary.  User classes should make sure
106      * to call {@link #dataBuffer()} to obtain the resulting encoded message.
107      *
108      * @param existing_bb The byte buffer to reuse.
109      * @param bb_factory The factory to be used for allocating a new internal buffer if
110      *                   the existing buffer needs to grow
111      */
FlatBufferBuilder(ByteBuffer existing_bb, ByteBufferFactory bb_factory)112     public FlatBufferBuilder(ByteBuffer existing_bb, ByteBufferFactory bb_factory) {
113         this(existing_bb.capacity(), bb_factory, existing_bb, Utf8.getDefault());
114     }
115 
116     /**
117      * Alternative constructor allowing reuse of {@link ByteBuffer}s.  The builder
118      * can still grow the buffer as necessary.  User classes should make sure
119      * to call {@link #dataBuffer()} to obtain the resulting encoded message.
120      *
121      * @param existing_bb The byte buffer to reuse.
122      */
FlatBufferBuilder(ByteBuffer existing_bb)123     public FlatBufferBuilder(ByteBuffer existing_bb) {
124         this(existing_bb, new HeapByteBufferFactory());
125     }
126 
127     /**
128      * Alternative initializer that allows reusing this object on an existing
129      * `ByteBuffer`. This method resets the builder's internal state, but keeps
130      * objects that have been allocated for temporary storage.
131      *
132      * @param existing_bb The byte buffer to reuse.
133      * @param bb_factory The factory to be used for allocating a new internal buffer if
134      *                   the existing buffer needs to grow
135      * @return Returns `this`.
136      */
init(ByteBuffer existing_bb, ByteBufferFactory bb_factory)137     public FlatBufferBuilder init(ByteBuffer existing_bb, ByteBufferFactory bb_factory){
138         this.bb_factory = bb_factory;
139         bb = existing_bb;
140         bb.clear();
141         bb.order(ByteOrder.LITTLE_ENDIAN);
142         minalign = 1;
143         space = bb.capacity();
144         vtable_in_use = 0;
145         nested = false;
146         finished = false;
147         object_start = 0;
148         num_vtables = 0;
149         vector_num_elems = 0;
150         return this;
151     }
152 
153     /**
154      * An interface that provides a user of the FlatBufferBuilder class the ability to specify
155      * the method in which the internal buffer gets allocated. This allows for alternatives
156      * to the default behavior, which is to allocate memory for a new byte-array
157      * backed `ByteBuffer` array inside the JVM.
158      *
159      * The FlatBufferBuilder class contains the HeapByteBufferFactory class to
160      * preserve the default behavior in the event that the user does not provide
161      * their own implementation of this interface.
162      */
163     public static abstract class ByteBufferFactory {
164         /**
165          * Create a `ByteBuffer` with a given capacity.
166          * The returned ByteBuf must have a ByteOrder.LITTLE_ENDIAN ByteOrder.
167          *
168          * @param capacity The size of the `ByteBuffer` to allocate.
169          * @return Returns the new `ByteBuffer` that was allocated.
170          */
newByteBuffer(int capacity)171         public abstract ByteBuffer newByteBuffer(int capacity);
172 
173         /**
174          * Release a ByteBuffer. Current {@link FlatBufferBuilder}
175          * released any reference to it, so it is safe to dispose the buffer
176          * or return it to a pool.
177          * It is not guaranteed that the buffer has been created
178          * with {@link #newByteBuffer(int) }.
179          *
180          * @param bb the buffer to release
181          */
releaseByteBuffer(ByteBuffer bb)182         public void releaseByteBuffer(ByteBuffer bb) {
183         }
184     }
185 
186     /**
187      * An implementation of the ByteBufferFactory interface that is used when
188      * one is not provided by the user.
189      *
190      * Allocate memory for a new byte-array backed `ByteBuffer` array inside the JVM.
191      */
192     public static final class HeapByteBufferFactory extends ByteBufferFactory {
193 
194         public static final HeapByteBufferFactory INSTANCE = new HeapByteBufferFactory();
195 
196         @Override
newByteBuffer(int capacity)197         public ByteBuffer newByteBuffer(int capacity) {
198             return ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
199         }
200     }
201 
202     /**
203      * Reset the FlatBufferBuilder by purging all data that it holds.
204      */
clear()205     public void clear(){
206         space = bb.capacity();
207         bb.clear();
208         minalign = 1;
209         while(vtable_in_use > 0) vtable[--vtable_in_use] = 0;
210         vtable_in_use = 0;
211         nested = false;
212         finished = false;
213         object_start = 0;
214         num_vtables = 0;
215         vector_num_elems = 0;
216     }
217 
218     /**
219      * Doubles the size of the backing {@link ByteBuffer} and copies the old data towards the
220      * end of the new buffer (since we build the buffer backwards).
221      *
222      * @param bb The current buffer with the existing data.
223      * @param bb_factory The factory to be used for allocating the new internal buffer
224      * @return A new byte buffer with the old data copied copied to it.  The data is
225      * located at the end of the buffer.
226      */
growByteBuffer(ByteBuffer bb, ByteBufferFactory bb_factory)227     static ByteBuffer growByteBuffer(ByteBuffer bb, ByteBufferFactory bb_factory) {
228         int old_buf_size = bb.capacity();
229         if ((old_buf_size & 0xC0000000) != 0)  // Ensure we don't grow beyond what fits in an int.
230             throw new AssertionError("FlatBuffers: cannot grow buffer beyond 2 gigabytes.");
231         int new_buf_size = old_buf_size == 0 ? 1 : old_buf_size << 1;
232         bb.position(0);
233         ByteBuffer nbb = bb_factory.newByteBuffer(new_buf_size);
234         nbb.position(new_buf_size - old_buf_size);
235         nbb.put(bb);
236         return nbb;
237     }
238 
239    /**
240     * Offset relative to the end of the buffer.
241     *
242     * @return Offset relative to the end of the buffer.
243     */
offset()244     public int offset() {
245         return bb.capacity() - space;
246     }
247 
248    /**
249     * Add zero valued bytes to prepare a new entry to be added.
250     *
251     * @param byte_size Number of bytes to add.
252     */
pad(int byte_size)253     public void pad(int byte_size) {
254         for (int i = 0; i < byte_size; i++) bb.put(--space, (byte)0);
255     }
256 
257    /**
258     * Prepare to write an element of `size` after `additional_bytes`
259     * have been written, e.g. if you write a string, you need to align such
260     * the int length field is aligned to {@link com.google.flatbuffers.Constants#SIZEOF_INT}, and
261     * the string data follows it directly.  If all you need to do is alignment, `additional_bytes`
262     * will be 0.
263     *
264     * @param size This is the of the new element to write.
265     * @param additional_bytes The padding size.
266     */
prep(int size, int additional_bytes)267     public void prep(int size, int additional_bytes) {
268         // Track the biggest thing we've ever aligned to.
269         if (size > minalign) minalign = size;
270         // Find the amount of alignment needed such that `size` is properly
271         // aligned after `additional_bytes`
272         int align_size = ((~(bb.capacity() - space + additional_bytes)) + 1) & (size - 1);
273         // Reallocate the buffer if needed.
274         while (space < align_size + size + additional_bytes) {
275             int old_buf_size = bb.capacity();
276             ByteBuffer old = bb;
277             bb = growByteBuffer(old, bb_factory);
278             if (old != bb) {
279                 bb_factory.releaseByteBuffer(old);
280             }
281             space += bb.capacity() - old_buf_size;
282         }
283         pad(align_size);
284     }
285 
286     /**
287      * Add a `boolean` to the buffer, backwards from the current location. Doesn't align nor
288      * check for space.
289      *
290      * @param x A `boolean` to put into the buffer.
291      */
putBoolean(boolean x)292     public void putBoolean(boolean x) { bb.put      (space -= Constants.SIZEOF_BYTE, (byte)(x ? 1 : 0)); }
293 
294     /**
295      * Add a `byte` to the buffer, backwards from the current location. Doesn't align nor
296      * check for space.
297      *
298      * @param x A `byte` to put into the buffer.
299      */
putByte(byte x)300     public void putByte   (byte    x) { bb.put      (space -= Constants.SIZEOF_BYTE, x); }
301 
302     /**
303      * Add a `short` to the buffer, backwards from the current location. Doesn't align nor
304      * check for space.
305      *
306      * @param x A `short` to put into the buffer.
307      */
putShort(short x)308     public void putShort  (short   x) { bb.putShort (space -= Constants.SIZEOF_SHORT, x); }
309 
310     /**
311      * Add an `int` to the buffer, backwards from the current location. Doesn't align nor
312      * check for space.
313      *
314      * @param x An `int` to put into the buffer.
315      */
putInt(int x)316     public void putInt    (int     x) { bb.putInt   (space -= Constants.SIZEOF_INT, x); }
317 
318     /**
319      * Add a `long` to the buffer, backwards from the current location. Doesn't align nor
320      * check for space.
321      *
322      * @param x A `long` to put into the buffer.
323      */
putLong(long x)324     public void putLong   (long    x) { bb.putLong  (space -= Constants.SIZEOF_LONG, x); }
325 
326     /**
327      * Add a `float` to the buffer, backwards from the current location. Doesn't align nor
328      * check for space.
329      *
330      * @param x A `float` to put into the buffer.
331      */
putFloat(float x)332     public void putFloat  (float   x) { bb.putFloat (space -= Constants.SIZEOF_FLOAT, x); }
333 
334     /**
335      * Add a `double` to the buffer, backwards from the current location. Doesn't align nor
336      * check for space.
337      *
338      * @param x A `double` to put into the buffer.
339      */
putDouble(double x)340     public void putDouble (double  x) { bb.putDouble(space -= Constants.SIZEOF_DOUBLE, x); }
341     /// @endcond
342 
343     /**
344      * Add a `boolean` to the buffer, properly aligned, and grows the buffer (if necessary).
345      *
346      * @param x A `boolean` to put into the buffer.
347      */
addBoolean(boolean x)348     public void addBoolean(boolean x) { prep(Constants.SIZEOF_BYTE, 0); putBoolean(x); }
349 
350     /**
351      * Add a `byte` to the buffer, properly aligned, and grows the buffer (if necessary).
352      *
353      * @param x A `byte` to put into the buffer.
354      */
addByte(byte x)355     public void addByte   (byte    x) { prep(Constants.SIZEOF_BYTE, 0); putByte   (x); }
356 
357     /**
358      * Add a `short` to the buffer, properly aligned, and grows the buffer (if necessary).
359      *
360      * @param x A `short` to put into the buffer.
361      */
addShort(short x)362     public void addShort  (short   x) { prep(Constants.SIZEOF_SHORT, 0); putShort  (x); }
363 
364     /**
365      * Add an `int` to the buffer, properly aligned, and grows the buffer (if necessary).
366      *
367      * @param x An `int` to put into the buffer.
368      */
addInt(int x)369     public void addInt    (int     x) { prep(Constants.SIZEOF_INT, 0); putInt    (x); }
370 
371     /**
372      * Add a `long` to the buffer, properly aligned, and grows the buffer (if necessary).
373      *
374      * @param x A `long` to put into the buffer.
375      */
addLong(long x)376     public void addLong   (long    x) { prep(Constants.SIZEOF_LONG, 0); putLong   (x); }
377 
378     /**
379      * Add a `float` to the buffer, properly aligned, and grows the buffer (if necessary).
380      *
381      * @param x A `float` to put into the buffer.
382      */
addFloat(float x)383     public void addFloat  (float   x) { prep(Constants.SIZEOF_FLOAT, 0); putFloat  (x); }
384 
385     /**
386      * Add a `double` to the buffer, properly aligned, and grows the buffer (if necessary).
387      *
388      * @param x A `double` to put into the buffer.
389      */
addDouble(double x)390     public void addDouble (double  x) { prep(Constants.SIZEOF_DOUBLE, 0); putDouble (x); }
391 
392    /**
393     * Adds on offset, relative to where it will be written.
394     *
395     * @param off The offset to add.
396     */
addOffset(int off)397     public void addOffset(int off) {
398         prep(SIZEOF_INT, 0);  // Ensure alignment is already done.
399         assert off <= offset();
400         off = offset() - off + SIZEOF_INT;
401         putInt(off);
402     }
403 
404    /// @cond FLATBUFFERS_INTERNAL
405    /**
406     * Start a new array/vector of objects.  Users usually will not call
407     * this directly.  The `FlatBuffers` compiler will create a start/end
408     * method for vector types in generated code.
409     * <p>
410     * The expected sequence of calls is:
411     * <ol>
412     * <li>Start the array using this method.</li>
413     * <li>Call {@link #addOffset(int)} `num_elems` number of times to set
414     * the offset of each element in the array.</li>
415     * <li>Call {@link #endVector()} to retrieve the offset of the array.</li>
416     * </ol>
417     * <p>
418     * For example, to create an array of strings, do:
419     * <pre>{@code
420     * // Need 10 strings
421     * FlatBufferBuilder builder = new FlatBufferBuilder(existingBuffer);
422     * int[] offsets = new int[10];
423     *
424     * for (int i = 0; i < 10; i++) {
425     *   offsets[i] = fbb.createString(" " + i);
426     * }
427     *
428     * // Have the strings in the buffer, but don't have a vector.
429     * // Add a vector that references the newly created strings:
430     * builder.startVector(4, offsets.length, 4);
431     *
432     * // Add each string to the newly created vector
433     * // The strings are added in reverse order since the buffer
434     * // is filled in back to front
435     * for (int i = offsets.length - 1; i >= 0; i--) {
436     *   builder.addOffset(offsets[i]);
437     * }
438     *
439     * // Finish off the vector
440     * int offsetOfTheVector = fbb.endVector();
441     * }</pre>
442     *
443     * @param elem_size The size of each element in the array.
444     * @param num_elems The number of elements in the array.
445     * @param alignment The alignment of the array.
446     */
startVector(int elem_size, int num_elems, int alignment)447     public void startVector(int elem_size, int num_elems, int alignment) {
448         notNested();
449         vector_num_elems = num_elems;
450         prep(SIZEOF_INT, elem_size * num_elems);
451         prep(alignment, elem_size * num_elems); // Just in case alignment > int.
452         nested = true;
453     }
454 
455    /**
456     * Finish off the creation of an array and all its elements.  The array
457     * must be created with {@link #startVector(int, int, int)}.
458     *
459     * @return The offset at which the newly created array starts.
460     * @see #startVector(int, int, int)
461     */
endVector()462     public int endVector() {
463         if (!nested)
464             throw new AssertionError("FlatBuffers: endVector called without startVector");
465         nested = false;
466         putInt(vector_num_elems);
467         return offset();
468     }
469     /// @endcond
470 
471     /**
472      * Create a new array/vector and return a ByteBuffer to be filled later.
473      * Call {@link #endVector} after this method to get an offset to the beginning
474      * of vector.
475      *
476      * @param elem_size the size of each element in bytes.
477      * @param num_elems number of elements in the vector.
478      * @param alignment byte alignment.
479      * @return ByteBuffer with position and limit set to the space allocated for the array.
480      */
createUnintializedVector(int elem_size, int num_elems, int alignment)481     public ByteBuffer createUnintializedVector(int elem_size, int num_elems, int alignment) {
482         int length = elem_size * num_elems;
483         startVector(elem_size, num_elems, alignment);
484 
485         bb.position(space -= length);
486 
487         // Slice and limit the copy vector to point to the 'array'
488         ByteBuffer copy = bb.slice().order(ByteOrder.LITTLE_ENDIAN);
489         copy.limit(length);
490         return copy;
491     }
492 
493    /**
494      * Create a vector of tables.
495      *
496      * @param offsets Offsets of the tables.
497      * @return Returns offset of the vector.
498      */
createVectorOfTables(int[] offsets)499     public int createVectorOfTables(int[] offsets) {
500         notNested();
501         startVector(Constants.SIZEOF_INT, offsets.length, Constants.SIZEOF_INT);
502         for(int i = offsets.length - 1; i >= 0; i--) addOffset(offsets[i]);
503         return endVector();
504     }
505 
506     /**
507      * Create a vector of sorted by the key tables.
508      *
509      * @param obj Instance of the table subclass.
510      * @param offsets Offsets of the tables.
511      * @return Returns offset of the sorted vector.
512      */
createSortedVectorOfTables(T obj, int[] offsets)513     public <T extends Table> int createSortedVectorOfTables(T obj, int[] offsets) {
514         obj.sortTables(offsets, bb);
515         return createVectorOfTables(offsets);
516     }
517 
518    /**
519     * Encode the string `s` in the buffer using UTF-8.  If {@code s} is
520     * already a {@link CharBuffer}, this method is allocation free.
521     *
522     * @param s The string to encode.
523     * @return The offset in the buffer where the encoded string starts.
524     */
createString(CharSequence s)525     public int createString(CharSequence s) {
526         int length = utf8.encodedLength(s);
527         addByte((byte)0);
528         startVector(1, length, 1);
529         bb.position(space -= length);
530         utf8.encodeUtf8(s, bb);
531         return endVector();
532     }
533 
534    /**
535     * Create a string in the buffer from an already encoded UTF-8 string in a ByteBuffer.
536     *
537     * @param s An already encoded UTF-8 string as a `ByteBuffer`.
538     * @return The offset in the buffer where the encoded string starts.
539     */
createString(ByteBuffer s)540     public int createString(ByteBuffer s) {
541         int length = s.remaining();
542         addByte((byte)0);
543         startVector(1, length, 1);
544         bb.position(space -= length);
545         bb.put(s);
546         return endVector();
547     }
548 
549     /**
550      * Create a byte array in the buffer.
551      *
552      * @param arr A source array with data
553      * @return The offset in the buffer where the encoded array starts.
554      */
createByteVector(byte[] arr)555     public int createByteVector(byte[] arr) {
556         int length = arr.length;
557         startVector(1, length, 1);
558         bb.position(space -= length);
559         bb.put(arr);
560         return endVector();
561     }
562 
563    /// @cond FLATBUFFERS_INTERNAL
564    /**
565     * Should not be accessing the final buffer before it is finished.
566     */
finished()567     public void finished() {
568         if (!finished)
569             throw new AssertionError(
570                 "FlatBuffers: you can only access the serialized buffer after it has been" +
571                 " finished by FlatBufferBuilder.finish().");
572     }
573 
574    /**
575     * Should not be creating any other object, string or vector
576     * while an object is being constructed.
577     */
notNested()578     public void notNested() {
579         if (nested)
580             throw new AssertionError("FlatBuffers: object serialization must not be nested.");
581     }
582 
583    /**
584     * Structures are always stored inline, they need to be created right
585     * where they're used.  You'll get this assertion failure if you
586     * created it elsewhere.
587     *
588     * @param obj The offset of the created object.
589     */
Nested(int obj)590     public void Nested(int obj) {
591         if (obj != offset())
592             throw new AssertionError("FlatBuffers: struct must be serialized inline.");
593     }
594 
595    /**
596     * Start encoding a new object in the buffer.  Users will not usually need to
597     * call this directly. The `FlatBuffers` compiler will generate helper methods
598     * that call this method internally.
599     * <p>
600     * For example, using the "Monster" code found on the "landing page". An
601     * object of type `Monster` can be created using the following code:
602     *
603     * <pre>{@code
604     * int testArrayOfString = Monster.createTestarrayofstringVector(fbb, new int[] {
605     *   fbb.createString("test1"),
606     *   fbb.createString("test2")
607     * });
608     *
609     * Monster.startMonster(fbb);
610     * Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0,
611     *   Color.Green, (short)5, (byte)6));
612     * Monster.addHp(fbb, (short)80);
613     * Monster.addName(fbb, str);
614     * Monster.addInventory(fbb, inv);
615     * Monster.addTestType(fbb, (byte)Any.Monster);
616     * Monster.addTest(fbb, mon2);
617     * Monster.addTest4(fbb, test4);
618     * Monster.addTestarrayofstring(fbb, testArrayOfString);
619     * int mon = Monster.endMonster(fbb);
620     * }</pre>
621     * <p>
622     * Here:
623     * <ul>
624     * <li>The call to `Monster#startMonster(FlatBufferBuilder)` will call this
625     * method with the right number of fields set.</li>
626     * <li>`Monster#endMonster(FlatBufferBuilder)` will ensure {@link #endObject()} is called.</li>
627     * </ul>
628     * <p>
629     * It's not recommended to call this method directly.  If it's called manually, you must ensure
630     * to audit all calls to it whenever fields are added or removed from your schema.  This is
631     * automatically done by the code generated by the `FlatBuffers` compiler.
632     *
633     * @param numfields The number of fields found in this object.
634     */
startObject(int numfields)635     public void startObject(int numfields) {
636         notNested();
637         if (vtable == null || vtable.length < numfields) vtable = new int[numfields];
638         vtable_in_use = numfields;
639         Arrays.fill(vtable, 0, vtable_in_use, 0);
640         nested = true;
641         object_start = offset();
642     }
643 
644     /**
645      * Add a `boolean` to a table at `o` into its vtable, with value `x` and default `d`.
646      *
647      * @param o The index into the vtable.
648      * @param x A `boolean` to put into the buffer, depending on how defaults are handled. If
649      * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
650      * default value, it can be skipped.
651      * @param d A `boolean` default value to compare against when `force_defaults` is `false`.
652      */
addBoolean(int o, boolean x, boolean d)653     public void addBoolean(int o, boolean x, boolean d) { if(force_defaults || x != d) { addBoolean(x); slot(o); } }
654 
655     /**
656      * Add a `byte` to a table at `o` into its vtable, with value `x` and default `d`.
657      *
658      * @param o The index into the vtable.
659      * @param x A `byte` to put into the buffer, depending on how defaults are handled. If
660      * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
661      * default value, it can be skipped.
662      * @param d A `byte` default value to compare against when `force_defaults` is `false`.
663      */
addByte(int o, byte x, int d)664     public void addByte   (int o, byte    x, int     d) { if(force_defaults || x != d) { addByte   (x); slot(o); } }
665 
666     /**
667      * Add a `short` to a table at `o` into its vtable, with value `x` and default `d`.
668      *
669      * @param o The index into the vtable.
670      * @param x A `short` to put into the buffer, depending on how defaults are handled. If
671      * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
672      * default value, it can be skipped.
673      * @param d A `short` default value to compare against when `force_defaults` is `false`.
674      */
addShort(int o, short x, int d)675     public void addShort  (int o, short   x, int     d) { if(force_defaults || x != d) { addShort  (x); slot(o); } }
676 
677     /**
678      * Add an `int` to a table at `o` into its vtable, with value `x` and default `d`.
679      *
680      * @param o The index into the vtable.
681      * @param x An `int` to put into the buffer, depending on how defaults are handled. If
682      * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
683      * default value, it can be skipped.
684      * @param d An `int` default value to compare against when `force_defaults` is `false`.
685      */
addInt(int o, int x, int d)686     public void addInt    (int o, int     x, int     d) { if(force_defaults || x != d) { addInt    (x); slot(o); } }
687 
688     /**
689      * Add a `long` to a table at `o` into its vtable, with value `x` and default `d`.
690      *
691      * @param o The index into the vtable.
692      * @param x A `long` to put into the buffer, depending on how defaults are handled. If
693      * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
694      * default value, it can be skipped.
695      * @param d A `long` default value to compare against when `force_defaults` is `false`.
696      */
addLong(int o, long x, long d)697     public void addLong   (int o, long    x, long    d) { if(force_defaults || x != d) { addLong   (x); slot(o); } }
698 
699     /**
700      * Add a `float` to a table at `o` into its vtable, with value `x` and default `d`.
701      *
702      * @param o The index into the vtable.
703      * @param x A `float` to put into the buffer, depending on how defaults are handled. If
704      * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
705      * default value, it can be skipped.
706      * @param d A `float` default value to compare against when `force_defaults` is `false`.
707      */
addFloat(int o, float x, double d)708     public void addFloat  (int o, float   x, double  d) { if(force_defaults || x != d) { addFloat  (x); slot(o); } }
709 
710     /**
711      * Add a `double` to a table at `o` into its vtable, with value `x` and default `d`.
712      *
713      * @param o The index into the vtable.
714      * @param x A `double` to put into the buffer, depending on how defaults are handled. If
715      * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
716      * default value, it can be skipped.
717      * @param d A `double` default value to compare against when `force_defaults` is `false`.
718      */
addDouble(int o, double x, double d)719     public void addDouble (int o, double  x, double  d) { if(force_defaults || x != d) { addDouble (x); slot(o); } }
720 
721     /**
722      * Add an `offset` to a table at `o` into its vtable, with value `x` and default `d`.
723      *
724      * @param o The index into the vtable.
725      * @param x An `offset` to put into the buffer, depending on how defaults are handled. If
726      * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
727      * default value, it can be skipped.
728      * @param d An `offset` default value to compare against when `force_defaults` is `false`.
729      */
addOffset(int o, int x, int d)730     public void addOffset (int o, int     x, int     d) { if(force_defaults || x != d) { addOffset (x); slot(o); } }
731 
732     /**
733      * Add a struct to the table. Structs are stored inline, so nothing additional is being added.
734      *
735      * @param voffset The index into the vtable.
736      * @param x The offset of the created struct.
737      * @param d The default value is always `0`.
738      */
addStruct(int voffset, int x, int d)739     public void addStruct(int voffset, int x, int d) {
740         if(x != d) {
741             Nested(x);
742             slot(voffset);
743         }
744     }
745 
746     /**
747      * Set the current vtable at `voffset` to the current location in the buffer.
748      *
749      * @param voffset The index into the vtable to store the offset relative to the end of the
750      * buffer.
751      */
slot(int voffset)752     public void slot(int voffset) {
753         vtable[voffset] = offset();
754     }
755 
756    /**
757     * Finish off writing the object that is under construction.
758     *
759     * @return The offset to the object inside {@link #dataBuffer()}.
760     * @see #startObject(int)
761     */
endObject()762     public int endObject() {
763         if (vtable == null || !nested)
764             throw new AssertionError("FlatBuffers: endObject called without startObject");
765         addInt(0);
766         int vtableloc = offset();
767         // Write out the current vtable.
768         int i = vtable_in_use - 1;
769         // Trim trailing zeroes.
770         for (; i >= 0 && vtable[i] == 0; i--) {}
771         int trimmed_size = i + 1;
772         for (; i >= 0 ; i--) {
773             // Offset relative to the start of the table.
774             short off = (short)(vtable[i] != 0 ? vtableloc - vtable[i] : 0);
775             addShort(off);
776         }
777 
778         final int standard_fields = 2; // The fields below:
779         addShort((short)(vtableloc - object_start));
780         addShort((short)((trimmed_size + standard_fields) * SIZEOF_SHORT));
781 
782         // Search for an existing vtable that matches the current one.
783         int existing_vtable = 0;
784         outer_loop:
785         for (i = 0; i < num_vtables; i++) {
786             int vt1 = bb.capacity() - vtables[i];
787             int vt2 = space;
788             short len = bb.getShort(vt1);
789             if (len == bb.getShort(vt2)) {
790                 for (int j = SIZEOF_SHORT; j < len; j += SIZEOF_SHORT) {
791                     if (bb.getShort(vt1 + j) != bb.getShort(vt2 + j)) {
792                         continue outer_loop;
793                     }
794                 }
795                 existing_vtable = vtables[i];
796                 break outer_loop;
797             }
798         }
799 
800         if (existing_vtable != 0) {
801             // Found a match:
802             // Remove the current vtable.
803             space = bb.capacity() - vtableloc;
804             // Point table to existing vtable.
805             bb.putInt(space, existing_vtable - vtableloc);
806         } else {
807             // No match:
808             // Add the location of the current vtable to the list of vtables.
809             if (num_vtables == vtables.length) vtables = Arrays.copyOf(vtables, num_vtables * 2);
810             vtables[num_vtables++] = offset();
811             // Point table to current vtable.
812             bb.putInt(bb.capacity() - vtableloc, offset() - vtableloc);
813         }
814 
815         nested = false;
816         return vtableloc;
817     }
818 
819     /**
820      * Checks that a required field has been set in a given table that has
821      * just been constructed.
822      *
823      * @param table The offset to the start of the table from the `ByteBuffer` capacity.
824      * @param field The offset to the field in the vtable.
825      */
required(int table, int field)826     public void required(int table, int field) {
827         int table_start = bb.capacity() - table;
828         int vtable_start = table_start - bb.getInt(table_start);
829         boolean ok = bb.getShort(vtable_start + field) != 0;
830         // If this fails, the caller will show what field needs to be set.
831         if (!ok)
832             throw new AssertionError("FlatBuffers: field " + field + " must be set");
833     }
834     /// @endcond
835 
836     /**
837      * Finalize a buffer, pointing to the given `root_table`.
838      *
839      * @param root_table An offset to be added to the buffer.
840      * @param size_prefix Whether to prefix the size to the buffer.
841      */
finish(int root_table, boolean size_prefix)842     protected void finish(int root_table, boolean size_prefix) {
843         prep(minalign, SIZEOF_INT + (size_prefix ? SIZEOF_INT : 0));
844         addOffset(root_table);
845         if (size_prefix) {
846             addInt(bb.capacity() - space);
847         }
848         bb.position(space);
849         finished = true;
850     }
851 
852     /**
853      * Finalize a buffer, pointing to the given `root_table`.
854      *
855      * @param root_table An offset to be added to the buffer.
856      */
finish(int root_table)857     public void finish(int root_table) {
858         finish(root_table, false);
859     }
860 
861     /**
862      * Finalize a buffer, pointing to the given `root_table`, with the size prefixed.
863      *
864      * @param root_table An offset to be added to the buffer.
865      */
finishSizePrefixed(int root_table)866     public void finishSizePrefixed(int root_table) {
867         finish(root_table, true);
868     }
869 
870     /**
871      * Finalize a buffer, pointing to the given `root_table`.
872      *
873      * @param root_table An offset to be added to the buffer.
874      * @param file_identifier A FlatBuffer file identifier to be added to the buffer before
875      * `root_table`.
876      * @param size_prefix Whether to prefix the size to the buffer.
877      */
finish(int root_table, String file_identifier, boolean size_prefix)878     protected void finish(int root_table, String file_identifier, boolean size_prefix) {
879         prep(minalign, SIZEOF_INT + FILE_IDENTIFIER_LENGTH + (size_prefix ? SIZEOF_INT : 0));
880         if (file_identifier.length() != FILE_IDENTIFIER_LENGTH)
881             throw new AssertionError("FlatBuffers: file identifier must be length " +
882                                      FILE_IDENTIFIER_LENGTH);
883         for (int i = FILE_IDENTIFIER_LENGTH - 1; i >= 0; i--) {
884             addByte((byte)file_identifier.charAt(i));
885         }
886         finish(root_table, size_prefix);
887     }
888 
889     /**
890      * Finalize a buffer, pointing to the given `root_table`.
891      *
892      * @param root_table An offset to be added to the buffer.
893      * @param file_identifier A FlatBuffer file identifier to be added to the buffer before
894      * `root_table`.
895      */
finish(int root_table, String file_identifier)896     public void finish(int root_table, String file_identifier) {
897         finish(root_table, file_identifier, false);
898     }
899 
900     /**
901      * Finalize a buffer, pointing to the given `root_table`, with the size prefixed.
902      *
903      * @param root_table An offset to be added to the buffer.
904      * @param file_identifier A FlatBuffer file identifier to be added to the buffer before
905      * `root_table`.
906      */
finishSizePrefixed(int root_table, String file_identifier)907     public void finishSizePrefixed(int root_table, String file_identifier) {
908         finish(root_table, file_identifier, true);
909     }
910 
911     /**
912      * In order to save space, fields that are set to their default value
913      * don't get serialized into the buffer. Forcing defaults provides a
914      * way to manually disable this optimization.
915      *
916      * @param forceDefaults When set to `true`, always serializes default values.
917      * @return Returns `this`.
918      */
forceDefaults(boolean forceDefaults)919     public FlatBufferBuilder forceDefaults(boolean forceDefaults){
920         this.force_defaults = forceDefaults;
921         return this;
922     }
923 
924     /**
925      * Get the ByteBuffer representing the FlatBuffer. Only call this after you've
926      * called `finish()`. The actual data starts at the ByteBuffer's current position,
927      * not necessarily at `0`.
928      *
929      * @return The {@link ByteBuffer} representing the FlatBuffer
930      */
dataBuffer()931     public ByteBuffer dataBuffer() {
932         finished();
933         return bb;
934     }
935 
936    /**
937     * The FlatBuffer data doesn't start at offset 0 in the {@link ByteBuffer}, but
938     * now the {@code ByteBuffer}'s position is set to that location upon {@link #finish(int)}.
939     *
940     * @return The {@link ByteBuffer#position() position} the data starts in {@link #dataBuffer()}
941     * @deprecated This method should not be needed anymore, but is left
942     * here for the moment to document this API change. It will be removed in the future.
943     */
944     @Deprecated
dataStart()945     private int dataStart() {
946         finished();
947         return space;
948     }
949 
950    /**
951     * A utility function to copy and return the ByteBuffer data from `start` to
952     * `start` + `length` as a `byte[]`.
953     *
954     * @param start Start copying at this offset.
955     * @param length How many bytes to copy.
956     * @return A range copy of the {@link #dataBuffer() data buffer}.
957     * @throws IndexOutOfBoundsException If the range of bytes is ouf of bound.
958     */
sizedByteArray(int start, int length)959     public byte[] sizedByteArray(int start, int length){
960         finished();
961         byte[] array = new byte[length];
962         bb.position(start);
963         bb.get(array);
964         return array;
965     }
966 
967    /**
968     * A utility function to copy and return the ByteBuffer data as a `byte[]`.
969     *
970     * @return A full copy of the {@link #dataBuffer() data buffer}.
971     */
sizedByteArray()972     public byte[] sizedByteArray() {
973         return sizedByteArray(space, bb.capacity() - space);
974     }
975 
976     /**
977      * A utility function to return an InputStream to the ByteBuffer data
978      *
979      * @return An InputStream that starts at the beginning of the ByteBuffer data
980      *         and can read to the end of it.
981      */
sizedInputStream()982     public InputStream sizedInputStream() {
983         finished();
984         ByteBuffer duplicate = bb.duplicate();
985         duplicate.position(space);
986         duplicate.limit(bb.capacity());
987         return new ByteBufferBackedInputStream(duplicate);
988     }
989 
990     /**
991      * A class that allows a user to create an InputStream from a ByteBuffer.
992      */
993     static class ByteBufferBackedInputStream extends InputStream {
994 
995         ByteBuffer buf;
996 
ByteBufferBackedInputStream(ByteBuffer buf)997         public ByteBufferBackedInputStream(ByteBuffer buf) {
998             this.buf = buf;
999         }
1000 
read()1001         public int read() throws IOException {
1002             try {
1003                 return buf.get() & 0xFF;
1004             } catch(BufferUnderflowException e) {
1005                 return -1;
1006             }
1007         }
1008     }
1009 
1010 }
1011 
1012 /// @}
1013