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 java.math.BigInteger; 20 import java.nio.ByteBuffer; 21 import java.nio.ByteOrder; 22 import java.nio.charset.StandardCharsets; 23 import java.util.ArrayList; 24 import java.util.Collections; 25 import java.util.Comparator; 26 import java.util.HashMap; 27 28 import static com.google.flatbuffers.FlexBuffers.*; 29 import static com.google.flatbuffers.FlexBuffers.Unsigned.byteToUnsignedInt; 30 import static com.google.flatbuffers.FlexBuffers.Unsigned.intToUnsignedLong; 31 import static com.google.flatbuffers.FlexBuffers.Unsigned.shortToUnsignedInt; 32 33 /// @file 34 /// @addtogroup flatbuffers_java_api 35 /// @{ 36 37 /** 38 * Helper class that builds FlexBuffers 39 * <p> This class presents all necessary APIs to create FlexBuffers. A `ByteBuffer` will be used to store the 40 * data. It can be created internally, or passed down in the constructor.</p> 41 * 42 * <p>There are some limitations when compared to original implementation in C++. Most notably: 43 * <ul> 44 * <li><p> No support for mutations (might change in the future).</p></li> 45 * <li><p> Buffer size limited to {@link Integer#MAX_VALUE}</p></li> 46 * <li><p> Since Java does not support unsigned type, all unsigned operations accepts an immediate higher representation 47 * of similar type.</p></li> 48 * </ul> 49 * </p> 50 */ 51 public class FlexBuffersBuilder { 52 53 /** 54 * No keys or strings will be shared 55 */ 56 public static final int BUILDER_FLAG_NONE = 0; 57 /** 58 * Keys will be shared between elements. Identical keys will only be serialized once, thus possibly saving space. 59 * But serialization performance might be slower and consumes more memory. 60 */ 61 public static final int BUILDER_FLAG_SHARE_KEYS = 1; 62 /** 63 * Strings will be shared between elements. Identical strings will only be serialized once, thus possibly saving space. 64 * But serialization performance might be slower and consumes more memory. This is ideal if you expect many repeated 65 * strings on the message. 66 */ 67 public static final int BUILDER_FLAG_SHARE_STRINGS = 2; 68 /** 69 * Strings and keys will be shared between elements. 70 */ 71 public static final int BUILDER_FLAG_SHARE_KEYS_AND_STRINGS = 3; 72 /** 73 * Reserved for the future. 74 */ 75 public static final int BUILDER_FLAG_SHARE_KEY_VECTORS = 4; 76 /** 77 * Reserved for the future. 78 */ 79 public static final int BUILDER_FLAG_SHARE_ALL = 7; 80 81 /// @cond FLATBUFFERS_INTERNAL 82 private static final int WIDTH_8 = 0; 83 private static final int WIDTH_16 = 1; 84 private static final int WIDTH_32 = 2; 85 private static final int WIDTH_64 = 3; 86 private final ReadWriteBuf bb; 87 private final ArrayList<Value> stack = new ArrayList<>(); 88 private final HashMap<String, Integer> keyPool = new HashMap<>(); 89 private final HashMap<String, Integer> stringPool = new HashMap<>(); 90 private final int flags; 91 private boolean finished = false; 92 93 // A lambda to sort map keys 94 private Comparator<Value> keyComparator = new Comparator<Value>() { 95 @Override 96 public int compare(Value o1, Value o2) { 97 int ia = o1.key; 98 int io = o2.key; 99 byte c1, c2; 100 do { 101 c1 = bb.get(ia); 102 c2 = bb.get(io); 103 if (c1 == 0) 104 return c1 - c2; 105 ia++; 106 io++; 107 } 108 while (c1 == c2); 109 return c1 - c2; 110 } 111 }; 112 /// @endcond 113 114 /** 115 * Constructs a newly allocated {@code FlexBuffersBuilder} with {@link #BUILDER_FLAG_SHARE_KEYS} set. 116 * @param bufSize size of buffer in bytes. 117 */ FlexBuffersBuilder(int bufSize)118 public FlexBuffersBuilder(int bufSize) { 119 this(new ArrayReadWriteBuf(bufSize), BUILDER_FLAG_SHARE_KEYS); 120 } 121 122 /** 123 * Constructs a newly allocated {@code FlexBuffersBuilder} with {@link #BUILDER_FLAG_SHARE_KEYS} set. 124 */ FlexBuffersBuilder()125 public FlexBuffersBuilder() { 126 this(256); 127 } 128 129 /** 130 * Constructs a newly allocated {@code FlexBuffersBuilder}. 131 * 132 * @param bb `ByteBuffer` that will hold the message 133 * @param flags Share flags 134 */ 135 @Deprecated FlexBuffersBuilder(ByteBuffer bb, int flags)136 public FlexBuffersBuilder(ByteBuffer bb, int flags) { 137 this(new ArrayReadWriteBuf(bb.array()), flags); 138 } 139 FlexBuffersBuilder(ReadWriteBuf bb, int flags)140 public FlexBuffersBuilder(ReadWriteBuf bb, int flags) { 141 this.bb = bb; 142 this.flags = flags; 143 } 144 145 /** 146 * Constructs a newly allocated {@code FlexBuffersBuilder}. 147 * By default same keys will be serialized only once 148 * @param bb `ByteBuffer` that will hold the message 149 */ FlexBuffersBuilder(ByteBuffer bb)150 public FlexBuffersBuilder(ByteBuffer bb) { 151 this(bb, BUILDER_FLAG_SHARE_KEYS); 152 } 153 154 /** 155 * Reset the FlexBuffersBuilder by purging all data that it holds. 156 */ clear()157 public void clear(){ 158 bb.clear(); 159 stack.clear(); 160 keyPool.clear(); 161 stringPool.clear(); 162 finished = false; 163 } 164 165 /** 166 * Return `ByteBuffer` containing FlexBuffer message. {@code #finish()} must be called before calling this 167 * function otherwise an assert will trigger. 168 * 169 * @return `ByteBuffer` with finished message 170 */ getBuffer()171 public ReadWriteBuf getBuffer() { 172 assert (finished); 173 return bb; 174 } 175 176 /** 177 * Insert a null value into the buffer 178 */ putNull()179 public void putNull() { 180 putNull(null); 181 } 182 183 /** 184 * Insert a null value into the buffer 185 * @param key key used to store element in map 186 */ putNull(String key)187 public void putNull(String key) { 188 stack.add(Value.nullValue(putKey(key))); 189 } 190 191 /** 192 * Insert a single boolean into the buffer 193 * @param val true or false 194 */ putBoolean(boolean val)195 public void putBoolean(boolean val) { 196 putBoolean(null, val); 197 } 198 199 /** 200 * Insert a single boolean into the buffer 201 * @param key key used to store element in map 202 * @param val true or false 203 */ putBoolean(String key, boolean val)204 public void putBoolean(String key, boolean val) { 205 stack.add(Value.bool(putKey(key), val)); 206 } 207 putKey(String key)208 private int putKey(String key) { 209 if (key == null) { 210 return -1; 211 } 212 int pos = bb.writePosition(); 213 if ((flags & BUILDER_FLAG_SHARE_KEYS) != 0) { 214 Integer keyFromPool = keyPool.get(key); 215 if (keyFromPool == null) { 216 byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); 217 bb.put(keyBytes, 0, keyBytes.length); 218 bb.put((byte) 0); 219 keyPool.put(key, pos); 220 } else { 221 pos = keyFromPool; 222 } 223 } else { 224 byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); 225 bb.put(keyBytes, 0, keyBytes.length); 226 bb.put((byte) 0); 227 keyPool.put(key, pos); 228 } 229 return pos; 230 } 231 232 /** 233 * Adds a integer into the buff 234 * @param val integer 235 */ putInt(int val)236 public void putInt(int val) { 237 putInt(null, val); 238 } 239 240 /** 241 * Adds a integer into the buff 242 * @param key key used to store element in map 243 * @param val integer 244 */ putInt(String key, int val)245 public void putInt(String key, int val) { 246 putInt(key, (long) val); 247 } 248 249 /** 250 * Adds a integer into the buff 251 * @param key key used to store element in map 252 * @param val 64-bit integer 253 */ putInt(String key, long val)254 public void putInt(String key, long val) { 255 int iKey = putKey(key); 256 if (Byte.MIN_VALUE <= val && val <= Byte.MAX_VALUE) { 257 stack.add(Value.int8(iKey, (int) val)); 258 } else if (Short.MIN_VALUE <= val && val <= Short.MAX_VALUE) { 259 stack.add(Value.int16(iKey, (int) val)); 260 } else if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) { 261 stack.add(Value.int32(iKey, (int) val)); 262 } else { 263 stack.add(Value.int64(iKey, val)); 264 } 265 } 266 267 /** 268 * Adds a 64-bit integer into the buff 269 * @param value integer 270 */ putInt(long value)271 public void putInt(long value) { 272 putInt(null, value); 273 } 274 275 /** 276 * Adds a unsigned integer into the buff. 277 * @param value integer representing unsigned value 278 */ putUInt(int value)279 public void putUInt(int value) { 280 putUInt(null, (long) value); 281 } 282 283 /** 284 * Adds a unsigned integer (stored in a signed 64-bit integer) into the buff. 285 * @param value integer representing unsigned value 286 */ putUInt(long value)287 public void putUInt(long value) { 288 putUInt(null, value); 289 } 290 291 /** 292 * Adds a 64-bit unsigned integer (stored as {@link BigInteger}) into the buff. 293 * Warning: This operation might be very slow. 294 * @param value integer representing unsigned value 295 */ putUInt64(BigInteger value)296 public void putUInt64(BigInteger value) { 297 putUInt64(null, value.longValue()); 298 } 299 putUInt64(String key, long value)300 private void putUInt64(String key, long value) { 301 stack.add(Value.uInt64(putKey(key), value)); 302 } 303 putUInt(String key, long value)304 private void putUInt(String key, long value) { 305 int iKey = putKey(key); 306 Value vVal; 307 308 int width = widthUInBits(value); 309 310 if (width == WIDTH_8) { 311 vVal = Value.uInt8(iKey, (int)value); 312 } else if (width == WIDTH_16) { 313 vVal = Value.uInt16(iKey, (int)value); 314 } else if (width == WIDTH_32) { 315 vVal = Value.uInt32(iKey, (int)value); 316 } else { 317 vVal = Value.uInt64(iKey, value); 318 } 319 stack.add(vVal); 320 } 321 322 /** 323 * Adds a 32-bit float into the buff. 324 * @param value float representing value 325 */ putFloat(float value)326 public void putFloat(float value) { 327 putFloat(null, value); 328 } 329 330 /** 331 * Adds a 32-bit float into the buff. 332 * @param key key used to store element in map 333 * @param value float representing value 334 */ putFloat(String key, float val)335 public void putFloat(String key, float val) { 336 stack.add(Value.float32(putKey(key), val)); 337 } 338 339 /** 340 * Adds a 64-bit float into the buff. 341 * @param value float representing value 342 */ putFloat(double value)343 public void putFloat(double value) { 344 putFloat(null, value); 345 } 346 347 /** 348 * Adds a 64-bit float into the buff. 349 * @param key key used to store element in map 350 * @param value float representing value 351 */ putFloat(String key, double val)352 public void putFloat(String key, double val) { 353 stack.add(Value.float64(putKey(key), val)); 354 } 355 356 /** 357 * Adds a String into the buffer 358 * @param value string 359 * @return start position of string in the buffer 360 */ putString(String value)361 public int putString(String value) { 362 return putString(null, value); 363 } 364 365 /** 366 * Adds a String into the buffer 367 * @param key key used to store element in map 368 * @param value string 369 * @return start position of string in the buffer 370 */ putString(String key, String val)371 public int putString(String key, String val) { 372 int iKey = putKey(key); 373 if ((flags & FlexBuffersBuilder.BUILDER_FLAG_SHARE_STRINGS) != 0) { 374 Integer i = stringPool.get(val); 375 if (i == null) { 376 Value value = writeString(iKey, val); 377 stringPool.put(val, (int) value.iValue); 378 stack.add(value); 379 return (int) value.iValue; 380 } else { 381 int bitWidth = widthUInBits(val.length()); 382 stack.add(Value.blob(iKey, i, FBT_STRING, bitWidth)); 383 return i; 384 } 385 } else { 386 Value value = writeString(iKey, val); 387 stack.add(value); 388 return (int) value.iValue; 389 } 390 } 391 writeString(int key, String s)392 private Value writeString(int key, String s) { 393 return writeBlob(key, s.getBytes(StandardCharsets.UTF_8), FBT_STRING, true); 394 } 395 396 // in bits to fit a unsigned int widthUInBits(long len)397 static int widthUInBits(long len) { 398 if (len <= byteToUnsignedInt((byte)0xff)) return WIDTH_8; 399 if (len <= shortToUnsignedInt((short)0xffff)) return WIDTH_16; 400 if (len <= intToUnsignedLong(0xffff_ffff)) return WIDTH_32; 401 return WIDTH_64; 402 } 403 writeBlob(int key, byte[] blob, int type, boolean trailing)404 private Value writeBlob(int key, byte[] blob, int type, boolean trailing) { 405 int bitWidth = widthUInBits(blob.length); 406 int byteWidth = align(bitWidth); 407 writeInt(blob.length, byteWidth); 408 int sloc = bb.writePosition(); 409 bb.put(blob, 0, blob.length); 410 if (trailing) { 411 bb.put((byte) 0); 412 } 413 return Value.blob(key, sloc, type, bitWidth); 414 } 415 416 // Align to prepare for writing a scalar with a certain size. align(int alignment)417 private int align(int alignment) { 418 int byteWidth = 1 << alignment; 419 int padBytes = Value.paddingBytes(bb.writePosition(), byteWidth); 420 while (padBytes-- != 0) { 421 bb.put((byte) 0); 422 } 423 return byteWidth; 424 } 425 writeInt(long value, int byteWidth)426 private void writeInt(long value, int byteWidth) { 427 switch (byteWidth) { 428 case 1: bb.put((byte) value); break; 429 case 2: bb.putShort((short) value); break; 430 case 4: bb.putInt((int) value); break; 431 case 8: bb.putLong(value); break; 432 } 433 } 434 435 /** 436 * Adds a byte array into the message 437 * @param value byte array 438 * @return position in buffer as the start of byte array 439 */ putBlob(byte[] value)440 public int putBlob(byte[] value) { 441 return putBlob(null, value); 442 } 443 444 /** 445 * Adds a byte array into the message 446 * @param key key used to store element in map 447 * @param value byte array 448 * @return position in buffer as the start of byte array 449 */ putBlob(String key, byte[] val)450 public int putBlob(String key, byte[] val) { 451 int iKey = putKey(key); 452 Value value = writeBlob(iKey, val, FBT_BLOB, false); 453 stack.add(value); 454 return (int) value.iValue; 455 } 456 457 /** 458 * Start a new vector in the buffer. 459 * @return a reference indicating position of the vector in buffer. This 460 * reference must be passed along when the vector is finished using endVector() 461 */ startVector()462 public int startVector() { 463 return stack.size(); 464 } 465 466 /** 467 * Finishes a vector, but writing the information in the buffer 468 * @param key key used to store element in map 469 * @param start reference for beginning of the vector. Returned by {@link startVector()} 470 * @param typed boolean indicating whether vector is typed 471 * @param fixed boolean indicating whether vector is fixed 472 * @return Reference to the vector 473 */ endVector(String key, int start, boolean typed, boolean fixed)474 public int endVector(String key, int start, boolean typed, boolean fixed) { 475 int iKey = putKey(key); 476 Value vec = createVector(iKey, start, stack.size() - start, typed, fixed, null); 477 // Remove temp elements and return vector. 478 while (stack.size() > start) { 479 stack.remove(stack.size() - 1); 480 } 481 stack.add(vec); 482 return (int) vec.iValue; 483 } 484 485 /** 486 * Finish writing the message into the buffer. After that no other element must 487 * be inserted into the buffer. Also, you must call this function before start using the 488 * FlexBuffer message 489 * @return `ByteBuffer` containing the FlexBuffer message 490 */ finish()491 public ByteBuffer finish() { 492 // If you hit this assert, you likely have objects that were never included 493 // in a parent. You need to have exactly one root to finish a buffer. 494 // Check your Start/End calls are matched, and all objects are inside 495 // some other object. 496 assert (stack.size() == 1); 497 // Write root value. 498 int byteWidth = align(stack.get(0).elemWidth(bb.writePosition(), 0)); 499 writeAny(stack.get(0), byteWidth); 500 // Write root type. 501 bb.put(stack.get(0).storedPackedType()); 502 // Write root size. Normally determined by parent, but root has no parent :) 503 bb.put((byte) byteWidth); 504 this.finished = true; 505 return ByteBuffer.wrap(bb.data(), 0, bb.writePosition()); 506 } 507 508 /* 509 * Create a vector based on the elements stored in the stack 510 * 511 * @param key reference to its key 512 * @param start element in the stack 513 * @param length size of the vector 514 * @param typed whether is TypedVector or not 515 * @param fixed whether is Fixed vector or not 516 * @param keys Value representing key vector 517 * @return Value representing the created vector 518 */ createVector(int key, int start, int length, boolean typed, boolean fixed, Value keys)519 private Value createVector(int key, int start, int length, boolean typed, boolean fixed, Value keys) { 520 if (fixed & !typed) 521 throw new UnsupportedOperationException("Untyped fixed vector is not supported"); 522 523 // Figure out smallest bit width we can store this vector with. 524 int bitWidth = Math.max(WIDTH_8, widthUInBits(length)); 525 int prefixElems = 1; 526 if (keys != null) { 527 // If this vector is part of a map, we will pre-fix an offset to the keys 528 // to this vector. 529 bitWidth = Math.max(bitWidth, keys.elemWidth(bb.writePosition(), 0)); 530 prefixElems += 2; 531 } 532 int vectorType = FBT_KEY; 533 // Check bit widths and types for all elements. 534 for (int i = start; i < stack.size(); i++) { 535 int elemWidth = stack.get(i).elemWidth(bb.writePosition(), i + prefixElems); 536 bitWidth = Math.max(bitWidth, elemWidth); 537 if (typed) { 538 if (i == start) { 539 vectorType = stack.get(i).type; 540 if (!FlexBuffers.isTypedVectorElementType(vectorType)) { 541 throw new FlexBufferException("TypedVector does not support this element type"); 542 } 543 } else { 544 // If you get this assert, you are writing a typed vector with 545 // elements that are not all the same type. 546 assert (vectorType == stack.get(i).type); 547 } 548 } 549 } 550 // If you get this assert, your fixed types are not one of: 551 // Int / UInt / Float / Key. 552 assert (!fixed || FlexBuffers.isTypedVectorElementType(vectorType)); 553 554 int byteWidth = align(bitWidth); 555 // Write vector. First the keys width/offset if available, and size. 556 if (keys != null) { 557 writeOffset(keys.iValue, byteWidth); 558 writeInt(1L << keys.minBitWidth, byteWidth); 559 } 560 if (!fixed) { 561 writeInt(length, byteWidth); 562 } 563 // Then the actual data. 564 int vloc = bb.writePosition(); 565 for (int i = start; i < stack.size(); i++) { 566 writeAny(stack.get(i), byteWidth); 567 } 568 // Then the types. 569 if (!typed) { 570 for (int i = start; i < stack.size(); i++) { 571 bb.put(stack.get(i).storedPackedType(bitWidth)); 572 } 573 } 574 return new Value(key, keys != null ? FBT_MAP 575 : (typed ? FlexBuffers.toTypedVector(vectorType, fixed ? length : 0) 576 : FBT_VECTOR), bitWidth, vloc); 577 } 578 writeOffset(long val, int byteWidth)579 private void writeOffset(long val, int byteWidth) { 580 int reloff = (int) (bb.writePosition() - val); 581 assert (byteWidth == 8 || reloff < 1L << (byteWidth * 8)); 582 writeInt(reloff, byteWidth); 583 } 584 writeAny(final Value val, int byteWidth)585 private void writeAny(final Value val, int byteWidth) { 586 switch (val.type) { 587 case FBT_NULL: 588 case FBT_BOOL: 589 case FBT_INT: 590 case FBT_UINT: 591 writeInt(val.iValue, byteWidth); 592 break; 593 case FBT_FLOAT: 594 writeDouble(val.dValue, byteWidth); 595 break; 596 default: 597 writeOffset(val.iValue, byteWidth); 598 break; 599 } 600 } 601 writeDouble(double val, int byteWidth)602 private void writeDouble(double val, int byteWidth) { 603 if (byteWidth == 4) { 604 bb.putFloat((float) val); 605 } else if (byteWidth == 8) { 606 bb.putDouble(val); 607 } 608 } 609 610 /** 611 * Start a new map in the buffer. 612 * @return a reference indicating position of the map in buffer. This 613 * reference must be passed along when the map is finished using endMap() 614 */ startMap()615 public int startMap() { 616 return stack.size(); 617 } 618 619 /** 620 * Finishes a map, but writing the information in the buffer 621 * @param key key used to store element in map 622 * @param start reference for beginning of the map. Returned by {@link startMap()} 623 * @return Reference to the map 624 */ endMap(String key, int start)625 public int endMap(String key, int start) { 626 int iKey = putKey(key); 627 628 Collections.sort(stack.subList(start, stack.size()), keyComparator); 629 630 Value keys = createKeyVector(start, stack.size() - start); 631 Value vec = createVector(iKey, start, stack.size() - start, false, false, keys); 632 // Remove temp elements and return map. 633 while (stack.size() > start) { 634 stack.remove(stack.size() - 1); 635 } 636 stack.add(vec); 637 return (int) vec.iValue; 638 } 639 createKeyVector(int start, int length)640 private Value createKeyVector(int start, int length) { 641 // Figure out smallest bit width we can store this vector with. 642 int bitWidth = Math.max(WIDTH_8, widthUInBits(length)); 643 int prefixElems = 1; 644 // Check bit widths and types for all elements. 645 for (int i = start; i < stack.size(); i++) { 646 int elemWidth = Value.elemWidth(FBT_KEY, WIDTH_8, stack.get(i).key, bb.writePosition(), i + prefixElems); 647 bitWidth = Math.max(bitWidth, elemWidth); 648 } 649 650 int byteWidth = align(bitWidth); 651 // Write vector. First the keys width/offset if available, and size. 652 writeInt(length, byteWidth); 653 // Then the actual data. 654 int vloc = bb.writePosition(); 655 for (int i = start; i < stack.size(); i++) { 656 int pos = stack.get(i).key; 657 assert(pos != -1); 658 writeOffset(stack.get(i).key, byteWidth); 659 } 660 // Then the types. 661 return new Value(-1, FlexBuffers.toTypedVector(FBT_KEY,0), bitWidth, vloc); 662 } 663 664 private static class Value { 665 final int type; 666 // for scalars, represents scalar size in bytes 667 // for vectors, represents the size 668 // for string, length 669 final int minBitWidth; 670 // float value 671 final double dValue; 672 // integer value 673 long iValue; 674 // position of the key associated with this value in buffer 675 int key; 676 Value(int key, int type, int bitWidth, long iValue)677 Value(int key, int type, int bitWidth, long iValue) { 678 this.key = key; 679 this.type = type; 680 this.minBitWidth = bitWidth; 681 this.iValue = iValue; 682 this.dValue = Double.MIN_VALUE; 683 } 684 Value(int key, int type, int bitWidth, double dValue)685 Value(int key, int type, int bitWidth, double dValue) { 686 this.key = key; 687 this.type = type; 688 this.minBitWidth = bitWidth; 689 this.dValue = dValue; 690 this.iValue = Long.MIN_VALUE; 691 } 692 nullValue(int key)693 static Value nullValue(int key) { 694 return new Value(key, FBT_NULL, WIDTH_8, 0); 695 } 696 bool(int key, boolean b)697 static Value bool(int key, boolean b) { 698 return new Value(key, FBT_BOOL, WIDTH_8, b ? 1 : 0); 699 } 700 blob(int key, int position, int type, int bitWidth)701 static Value blob(int key, int position, int type, int bitWidth) { 702 return new Value(key, type, bitWidth, position); 703 } 704 int8(int key, int value)705 static Value int8(int key, int value) { 706 return new Value(key, FBT_INT, WIDTH_8, value); 707 } 708 int16(int key, int value)709 static Value int16(int key, int value) { 710 return new Value(key, FBT_INT, WIDTH_16, value); 711 } 712 int32(int key, int value)713 static Value int32(int key, int value) { 714 return new Value(key, FBT_INT, WIDTH_32, value); 715 } 716 int64(int key, long value)717 static Value int64(int key, long value) { 718 return new Value(key, FBT_INT, WIDTH_64, value); 719 } 720 uInt8(int key, int value)721 static Value uInt8(int key, int value) { 722 return new Value(key, FBT_UINT, WIDTH_8, value); 723 } 724 uInt16(int key, int value)725 static Value uInt16(int key, int value) { 726 return new Value(key, FBT_UINT, WIDTH_16, value); 727 } 728 uInt32(int key, int value)729 static Value uInt32(int key, int value) { 730 return new Value(key, FBT_UINT, WIDTH_32, value); 731 } 732 uInt64(int key, long value)733 static Value uInt64(int key, long value) { 734 return new Value(key, FBT_UINT, WIDTH_64, value); 735 } 736 float32(int key, float value)737 static Value float32(int key, float value) { 738 return new Value(key, FBT_FLOAT, WIDTH_32, value); 739 } 740 float64(int key, double value)741 static Value float64(int key, double value) { 742 return new Value(key, FBT_FLOAT, WIDTH_64, value); 743 } 744 storedPackedType()745 private byte storedPackedType() { 746 return storedPackedType(WIDTH_8); 747 } 748 storedPackedType(int parentBitWidth)749 private byte storedPackedType(int parentBitWidth) { 750 return packedType(storedWidth(parentBitWidth), type); 751 } 752 packedType(int bitWidth, int type)753 private static byte packedType(int bitWidth, int type) { 754 return (byte) (bitWidth | (type << 2)); 755 } 756 storedWidth(int parentBitWidth)757 private int storedWidth(int parentBitWidth) { 758 if (FlexBuffers.isTypeInline(type)) { 759 return Math.max(minBitWidth, parentBitWidth); 760 } else { 761 return minBitWidth; 762 } 763 } 764 elemWidth(int bufSize, int elemIndex)765 private int elemWidth(int bufSize, int elemIndex) { 766 return elemWidth(type, minBitWidth, iValue, bufSize, elemIndex); 767 } 768 elemWidth(int type, int minBitWidth, long iValue, int bufSize, int elemIndex)769 private static int elemWidth(int type, int minBitWidth, long iValue, int bufSize, int elemIndex) { 770 if (FlexBuffers.isTypeInline(type)) { 771 return minBitWidth; 772 } else { 773 // We have an absolute offset, but want to store a relative offset 774 // elem_index elements beyond the current buffer end. Since whether 775 // the relative offset fits in a certain byte_width depends on 776 // the size of the elements before it (and their alignment), we have 777 // to test for each size in turn. 778 779 // Original implementation checks for largest scalar 780 // which is long unsigned int 781 for (int byteWidth = 1; byteWidth <= 32; byteWidth *= 2) { 782 // Where are we going to write this offset? 783 int offsetLoc = bufSize + paddingBytes(bufSize, byteWidth) + (elemIndex * byteWidth); 784 // Compute relative offset. 785 long offset = offsetLoc - iValue; 786 // Does it fit? 787 int bitWidth = widthUInBits(offset); 788 if (((1L) << bitWidth) == byteWidth) 789 return bitWidth; 790 } 791 assert (false); // Must match one of the sizes above. 792 return WIDTH_64; 793 } 794 } 795 paddingBytes(int bufSize, int scalarSize)796 private static int paddingBytes(int bufSize, int scalarSize) { 797 return ((~bufSize) + 1) & (scalarSize - 1); 798 } 799 } 800 } 801 802 /// @} 803