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