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