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