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