Home | History | Annotate | Download | only in flatbuffers
      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     */
     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