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 import java.util.HashMap; 26 import java.util.Map; 27 import java.lang.Integer; 28 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 */ 37 public class FlatBufferBuilder { 38 /// @cond FLATBUFFERS_INTERNAL 39 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. 54 /// @endcond 55 56 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 69 /** 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 */ FlatBufferBuilder(int initial_size, ByteBufferFactory bb_factory)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 */ FlatBufferBuilder(int initial_size, ByteBufferFactory bb_factory, ByteBuffer existing_bb, Utf8 utf8)87 public FlatBufferBuilder(int initial_size, ByteBufferFactory bb_factory, 88 ByteBuffer existing_bb, Utf8 utf8) { 89 if (initial_size <= 0) { 90 initial_size = DEFAULT_BUFFER_SIZE; 91 } 92 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; 101 space = bb.capacity(); 102 } 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 */ FlatBufferBuilder(int initial_size)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 */ FlatBufferBuilder()116 public FlatBufferBuilder() { 117 this(DEFAULT_BUFFER_SIZE); 118 } 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 */ FlatBufferBuilder(ByteBuffer existing_bb, ByteBufferFactory bb_factory)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 */ FlatBufferBuilder(ByteBuffer existing_bb)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 */ init(ByteBuffer existing_bb, ByteBufferFactory bb_factory)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; 167 if (string_pool != null) { 168 string_pool.clear(); 169 } 170 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 */ newByteBuffer(int capacity)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 */ releaseByteBuffer(ByteBuffer bb)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 newByteBuffer(int capacity)217 public ByteBuffer newByteBuffer(int capacity) { 218 return ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN); 219 } 220 } 221 222 /** 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 */ isFieldPresent(Table table, int offset)229 public static boolean isFieldPresent(Table table, int offset) { 230 return table.__offset(offset) != 0; 231 } 232 233 /** 234 * Reset the FlatBufferBuilder by purging all data that it holds. 235 */ clear()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; 247 if (string_pool != null) { 248 string_pool.clear(); 249 } 250 } 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 */ growByteBuffer(ByteBuffer bb, ByteBufferFactory bb_factory)261 static ByteBuffer growByteBuffer(ByteBuffer bb, ByteBufferFactory bb_factory) { 262 int old_buf_size = bb.capacity(); 263 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 276 bb.position(0); 277 ByteBuffer nbb = bb_factory.newByteBuffer(new_buf_size); 278 new_buf_size = nbb.clear().capacity(); // Ensure the returned buffer is treated as empty 279 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 */ offset()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 */ pad(int byte_size)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 */ prep(int size, int additional_bytes)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 */ putBoolean(boolean x)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 */ putByte(byte x)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 */ putShort(short x)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 */ putInt(int x)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 */ putLong(long x)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 */ putFloat(float x)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 */ putDouble(double x)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 */ addBoolean(boolean x)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 */ addByte(byte x)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 */ addShort(short x)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 */ addInt(int x)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 */ addLong(long x)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 */ addFloat(float x)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 */ addDouble(double x)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 */ addOffset(int off)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 */ startVector(int elem_size, int num_elems, int alignment)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 */ endVector()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 */ createUnintializedVector(int elem_size, int num_elems, int alignment)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 */ createVectorOfTables(int[] offsets)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 */ createSortedVectorOfTables(T obj, int[] offsets)558 public <T extends Table> int createSortedVectorOfTables(T obj, int[] offsets) { 559 obj.sortTables(offsets, bb); 560 return createVectorOfTables(offsets); 561 } 562 563 /** 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 */ createSharedString(String s)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 594 /** 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 */ createString(CharSequence s)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 */ createString(ByteBuffer s)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 */ createByteVector(byte[] arr)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 639 /** 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 */ createByteVector(byte[] arr, int offset, int length)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 */ createByteVector(ByteBuffer byteBuffer)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 671 /// @cond FLATBUFFERS_INTERNAL 672 /** 673 * Should not be accessing the final buffer before it is finished. 674 */ finished()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 */ notNested()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 */ Nested(int obj)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 */ startTable(int numfields)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 */ addBoolean(int o, boolean x, boolean d)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 */ addByte(int o, byte x, int d)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 */ addShort(int o, short x, int d)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 */ addInt(int o, int x, int d)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 */ addLong(int o, long x, long d)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 */ addFloat(int o, float x, double d)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 */ addDouble(int o, double x, double d)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 */ addOffset(int o, int x, int d)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 */ addStruct(int voffset, int x, int d)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 */ slot(int voffset)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 */ endTable()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 */ required(int table, int field)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 */ finish(int root_table, boolean size_prefix)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 */ finish(int root_table)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 */ finishSizePrefixed(int root_table)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 */ finish(int root_table, String file_identifier, boolean size_prefix)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 */ finish(int root_table, String file_identifier)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 */ finishSizePrefixed(int root_table, String file_identifier)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 */ forceDefaults(boolean forceDefaults)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 */ dataBuffer()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 dataStart()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 */ sizedByteArray(int start, int length)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 */ sizedByteArray()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 */ sizedInputStream()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 ByteBufferBackedInputStream(ByteBuffer buf)1105 public ByteBufferBackedInputStream(ByteBuffer buf) { 1106 this.buf = buf; 1107 } 1108 read()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 /// @} 1121