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
17package com.google.flatbuffers;
18
19import static com.google.flatbuffers.Constants.*;
20
21import java.nio.CharBuffer;
22import java.nio.charset.CharacterCodingException;
23import java.nio.charset.CharsetEncoder;
24import java.nio.charset.CoderResult;
25import java.util.Arrays;
26import java.nio.ByteBuffer;
27import java.nio.ByteOrder;
28import 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 */
38public 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    */
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    */
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     */
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     */
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     */
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     */
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    */
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    */
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    */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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    */
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    */
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    */
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     */
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     */
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     */
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    */
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    */
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     */
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    */
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    */
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    */
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    */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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    */
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     */
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     */
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     */
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     */
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     */
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
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    */
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    */
837    public byte[] sizedByteArray() {
838        return sizedByteArray(space, bb.capacity() - space);
839    }
840}
841
842/// @}
843