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