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