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