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