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