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 single boolean into the buffer 178 * @param val true or false 179 */ putBoolean(boolean val)180 public void putBoolean(boolean val) { 181 putBoolean(null, val); 182 } 183 184 /** 185 * Insert a single boolean into the buffer 186 * @param key key used to store element in map 187 * @param val true or false 188 */ putBoolean(String key, boolean val)189 public void putBoolean(String key, boolean val) { 190 stack.add(Value.bool(putKey(key), val)); 191 } 192 putKey(String key)193 private int putKey(String key) { 194 if (key == null) { 195 return -1; 196 } 197 int pos = bb.writePosition(); 198 if ((flags & BUILDER_FLAG_SHARE_KEYS) != 0) { 199 Integer keyFromPool = keyPool.get(key); 200 if (keyFromPool == null) { 201 byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); 202 bb.put(keyBytes, 0, keyBytes.length); 203 bb.put((byte) 0); 204 keyPool.put(key, pos); 205 } else { 206 pos = keyFromPool; 207 } 208 } else { 209 byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); 210 bb.put(keyBytes, 0, keyBytes.length); 211 bb.put((byte) 0); 212 keyPool.put(key, pos); 213 } 214 return pos; 215 } 216 217 /** 218 * Adds a integer into the buff 219 * @param val integer 220 */ putInt(int val)221 public void putInt(int val) { 222 putInt(null, val); 223 } 224 225 /** 226 * Adds a integer into the buff 227 * @param key key used to store element in map 228 * @param val integer 229 */ putInt(String key, int val)230 public void putInt(String key, int val) { 231 putInt(key, (long) val); 232 } 233 234 /** 235 * Adds a integer into the buff 236 * @param key key used to store element in map 237 * @param val 64-bit integer 238 */ putInt(String key, long val)239 public void putInt(String key, long val) { 240 int iKey = putKey(key); 241 if (Byte.MIN_VALUE <= val && val <= Byte.MAX_VALUE) { 242 stack.add(Value.int8(iKey, (int) val)); 243 } else if (Short.MIN_VALUE <= val && val <= Short.MAX_VALUE) { 244 stack.add(Value.int16(iKey, (int) val)); 245 } else if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) { 246 stack.add(Value.int32(iKey, (int) val)); 247 } else { 248 stack.add(Value.int64(iKey, val)); 249 } 250 } 251 252 /** 253 * Adds a 64-bit integer into the buff 254 * @param value integer 255 */ putInt(long value)256 public void putInt(long value) { 257 putInt(null, value); 258 } 259 260 /** 261 * Adds a unsigned integer into the buff. 262 * @param value integer representing unsigned value 263 */ putUInt(int value)264 public void putUInt(int value) { 265 putUInt(null, (long) value); 266 } 267 268 /** 269 * Adds a unsigned integer (stored in a signed 64-bit integer) into the buff. 270 * @param value integer representing unsigned value 271 */ putUInt(long value)272 public void putUInt(long value) { 273 putUInt(null, value); 274 } 275 276 /** 277 * Adds a 64-bit unsigned integer (stored as {@link BigInteger}) into the buff. 278 * Warning: This operation might be very slow. 279 * @param value integer representing unsigned value 280 */ putUInt64(BigInteger value)281 public void putUInt64(BigInteger value) { 282 putUInt64(null, value.longValue()); 283 } 284 putUInt64(String key, long value)285 private void putUInt64(String key, long value) { 286 stack.add(Value.uInt64(putKey(key), value)); 287 } 288 putUInt(String key, long value)289 private void putUInt(String key, long value) { 290 int iKey = putKey(key); 291 Value vVal; 292 293 int width = widthUInBits(value); 294 295 if (width == WIDTH_8) { 296 vVal = Value.uInt8(iKey, (int)value); 297 } else if (width == WIDTH_16) { 298 vVal = Value.uInt16(iKey, (int)value); 299 } else if (width == WIDTH_32) { 300 vVal = Value.uInt32(iKey, (int)value); 301 } else { 302 vVal = Value.uInt64(iKey, value); 303 } 304 stack.add(vVal); 305 } 306 307 /** 308 * Adds a 32-bit float into the buff. 309 * @param value float representing value 310 */ putFloat(float value)311 public void putFloat(float value) { 312 putFloat(null, value); 313 } 314 315 /** 316 * Adds a 32-bit float into the buff. 317 * @param key key used to store element in map 318 * @param value float representing value 319 */ putFloat(String key, float val)320 public void putFloat(String key, float val) { 321 stack.add(Value.float32(putKey(key), val)); 322 } 323 324 /** 325 * Adds a 64-bit float into the buff. 326 * @param value float representing value 327 */ putFloat(double value)328 public void putFloat(double value) { 329 putFloat(null, value); 330 } 331 332 /** 333 * Adds a 64-bit float into the buff. 334 * @param key key used to store element in map 335 * @param value float representing value 336 */ putFloat(String key, double val)337 public void putFloat(String key, double val) { 338 stack.add(Value.float64(putKey(key), val)); 339 } 340 341 /** 342 * Adds a String into the buffer 343 * @param value string 344 * @return start position of string in the buffer 345 */ putString(String value)346 public int putString(String value) { 347 return putString(null, value); 348 } 349 350 /** 351 * Adds a String into the buffer 352 * @param key key used to store element in map 353 * @param value string 354 * @return start position of string in the buffer 355 */ putString(String key, String val)356 public int putString(String key, String val) { 357 int iKey = putKey(key); 358 if ((flags & FlexBuffersBuilder.BUILDER_FLAG_SHARE_STRINGS) != 0) { 359 Integer i = stringPool.get(val); 360 if (i == null) { 361 Value value = writeString(iKey, val); 362 stringPool.put(val, (int) value.iValue); 363 stack.add(value); 364 return (int) value.iValue; 365 } else { 366 int bitWidth = widthUInBits(val.length()); 367 stack.add(Value.blob(iKey, i, FBT_STRING, bitWidth)); 368 return i; 369 } 370 } else { 371 Value value = writeString(iKey, val); 372 stack.add(value); 373 return (int) value.iValue; 374 } 375 } 376 writeString(int key, String s)377 private Value writeString(int key, String s) { 378 return writeBlob(key, s.getBytes(StandardCharsets.UTF_8), FBT_STRING, true); 379 } 380 381 // in bits to fit a unsigned int widthUInBits(long len)382 static int widthUInBits(long len) { 383 if (len <= byteToUnsignedInt((byte)0xff)) return WIDTH_8; 384 if (len <= shortToUnsignedInt((short)0xffff)) return WIDTH_16; 385 if (len <= intToUnsignedLong(0xffff_ffff)) return WIDTH_32; 386 return WIDTH_64; 387 } 388 writeBlob(int key, byte[] blob, int type, boolean trailing)389 private Value writeBlob(int key, byte[] blob, int type, boolean trailing) { 390 int bitWidth = widthUInBits(blob.length); 391 int byteWidth = align(bitWidth); 392 writeInt(blob.length, byteWidth); 393 int sloc = bb.writePosition(); 394 bb.put(blob, 0, blob.length); 395 if (trailing) { 396 bb.put((byte) 0); 397 } 398 return Value.blob(key, sloc, type, bitWidth); 399 } 400 401 // Align to prepare for writing a scalar with a certain size. align(int alignment)402 private int align(int alignment) { 403 int byteWidth = 1 << alignment; 404 int padBytes = Value.paddingBytes(bb.writePosition(), byteWidth); 405 while (padBytes-- != 0) { 406 bb.put((byte) 0); 407 } 408 return byteWidth; 409 } 410 writeInt(long value, int byteWidth)411 private void writeInt(long value, int byteWidth) { 412 switch (byteWidth) { 413 case 1: bb.put((byte) value); break; 414 case 2: bb.putShort((short) value); break; 415 case 4: bb.putInt((int) value); break; 416 case 8: bb.putLong(value); break; 417 } 418 } 419 420 /** 421 * Adds a byte array into the message 422 * @param value byte array 423 * @return position in buffer as the start of byte array 424 */ putBlob(byte[] value)425 public int putBlob(byte[] value) { 426 return putBlob(null, value); 427 } 428 429 /** 430 * Adds a byte array into the message 431 * @param key key used to store element in map 432 * @param value byte array 433 * @return position in buffer as the start of byte array 434 */ putBlob(String key, byte[] val)435 public int putBlob(String key, byte[] val) { 436 int iKey = putKey(key); 437 Value value = writeBlob(iKey, val, FBT_BLOB, false); 438 stack.add(value); 439 return (int) value.iValue; 440 } 441 442 /** 443 * Start a new vector in the buffer. 444 * @return a reference indicating position of the vector in buffer. This 445 * reference must be passed along when the vector is finished using endVector() 446 */ startVector()447 public int startVector() { 448 return stack.size(); 449 } 450 451 /** 452 * Finishes a vector, but writing the information in the buffer 453 * @param key key used to store element in map 454 * @param start reference for begining of the vector. Returned by {@link startVector()} 455 * @param typed boolean indicating wether vector is typed 456 * @param fixed boolean indicating wether vector is fixed 457 * @return Reference to the vector 458 */ endVector(String key, int start, boolean typed, boolean fixed)459 public int endVector(String key, int start, boolean typed, boolean fixed) { 460 int iKey = putKey(key); 461 Value vec = createVector(iKey, start, stack.size() - start, typed, fixed, null); 462 // Remove temp elements and return vector. 463 while (stack.size() > start) { 464 stack.remove(stack.size() - 1); 465 } 466 stack.add(vec); 467 return (int) vec.iValue; 468 } 469 470 /** 471 * Finish writing the message into the buffer. After that no other element must 472 * be inserted into the buffer. Also, you must call this function before start using the 473 * FlexBuffer message 474 * @return `ByteBuffer` containing the FlexBuffer message 475 */ finish()476 public ByteBuffer finish() { 477 // If you hit this assert, you likely have objects that were never included 478 // in a parent. You need to have exactly one root to finish a buffer. 479 // Check your Start/End calls are matched, and all objects are inside 480 // some other object. 481 assert (stack.size() == 1); 482 // Write root value. 483 int byteWidth = align(stack.get(0).elemWidth(bb.writePosition(), 0)); 484 writeAny(stack.get(0), byteWidth); 485 // Write root type. 486 bb.put(stack.get(0).storedPackedType()); 487 // Write root size. Normally determined by parent, but root has no parent :) 488 bb.put((byte) byteWidth); 489 this.finished = true; 490 return ByteBuffer.wrap(bb.data(), 0, bb.writePosition()); 491 } 492 493 /* 494 * Create a vector based on the elements stored in the stack 495 * 496 * @param key reference to its key 497 * @param start element in the stack 498 * @param length size of the vector 499 * @param typed whether is TypedVector or not 500 * @param fixed whether is Fixed vector or not 501 * @param keys Value representing key vector 502 * @return Value representing the created vector 503 */ createVector(int key, int start, int length, boolean typed, boolean fixed, Value keys)504 private Value createVector(int key, int start, int length, boolean typed, boolean fixed, Value keys) { 505 assert (!fixed || typed); // typed=false, fixed=true combination is not supported. 506 // Figure out smallest bit width we can store this vector with. 507 int bitWidth = Math.max(WIDTH_8, widthUInBits(length)); 508 int prefixElems = 1; 509 if (keys != null) { 510 // If this vector is part of a map, we will pre-fix an offset to the keys 511 // to this vector. 512 bitWidth = Math.max(bitWidth, keys.elemWidth(bb.writePosition(), 0)); 513 prefixElems += 2; 514 } 515 int vectorType = FBT_KEY; 516 // Check bit widths and types for all elements. 517 for (int i = start; i < stack.size(); i++) { 518 int elemWidth = stack.get(i).elemWidth(bb.writePosition(), i + prefixElems); 519 bitWidth = Math.max(bitWidth, elemWidth); 520 if (typed) { 521 if (i == start) { 522 vectorType = stack.get(i).type; 523 if (!FlexBuffers.isTypedVectorElementType(vectorType)) { 524 throw new FlexBufferException("TypedVector does not support this element type"); 525 } 526 } else { 527 // If you get this assert, you are writing a typed vector with 528 // elements that are not all the same type. 529 assert (vectorType == stack.get(i).type); 530 } 531 } 532 } 533 // If you get this assert, your fixed types are not one of: 534 // Int / UInt / Float / Key. 535 assert (!fixed || FlexBuffers.isTypedVectorElementType(vectorType)); 536 537 int byteWidth = align(bitWidth); 538 // Write vector. First the keys width/offset if available, and size. 539 if (keys != null) { 540 writeOffset(keys.iValue, byteWidth); 541 writeInt(1L << keys.minBitWidth, byteWidth); 542 } 543 if (!fixed) { 544 writeInt(length, byteWidth); 545 } 546 // Then the actual data. 547 int vloc = bb.writePosition(); 548 for (int i = start; i < stack.size(); i++) { 549 writeAny(stack.get(i), byteWidth); 550 } 551 // Then the types. 552 if (!typed) { 553 for (int i = start; i < stack.size(); i++) { 554 bb.put(stack.get(i).storedPackedType(bitWidth)); 555 } 556 } 557 return new Value(key, keys != null ? FBT_MAP 558 : (typed ? FlexBuffers.toTypedVector(vectorType, fixed ? length : 0) 559 : FBT_VECTOR), bitWidth, vloc); 560 } 561 writeOffset(long val, int byteWidth)562 private void writeOffset(long val, int byteWidth) { 563 int reloff = (int) (bb.writePosition() - val); 564 assert (byteWidth == 8 || reloff < 1L << (byteWidth * 8)); 565 writeInt(reloff, byteWidth); 566 } 567 writeAny(final Value val, int byteWidth)568 private void writeAny(final Value val, int byteWidth) { 569 switch (val.type) { 570 case FBT_NULL: 571 case FBT_BOOL: 572 case FBT_INT: 573 case FBT_UINT: 574 writeInt(val.iValue, byteWidth); 575 break; 576 case FBT_FLOAT: 577 writeDouble(val.dValue, byteWidth); 578 break; 579 default: 580 writeOffset(val.iValue, byteWidth); 581 break; 582 } 583 } 584 writeDouble(double val, int byteWidth)585 private void writeDouble(double val, int byteWidth) { 586 if (byteWidth == 4) { 587 bb.putFloat((float) val); 588 } else if (byteWidth == 8) { 589 bb.putDouble(val); 590 } 591 } 592 593 /** 594 * Start a new map in the buffer. 595 * @return a reference indicating position of the map in buffer. This 596 * reference must be passed along when the map is finished using endMap() 597 */ startMap()598 public int startMap() { 599 return stack.size(); 600 } 601 602 /** 603 * Finishes a map, but writing the information in the buffer 604 * @param key key used to store element in map 605 * @param start reference for begining of the map. Returned by {@link startMap()} 606 * @return Reference to the map 607 */ endMap(String key, int start)608 public int endMap(String key, int start) { 609 int iKey = putKey(key); 610 611 Collections.sort(stack.subList(start, stack.size()), keyComparator); 612 613 Value keys = createKeyVector(start, stack.size() - start); 614 Value vec = createVector(iKey, start, stack.size() - start, false, false, keys); 615 // Remove temp elements and return map. 616 while (stack.size() > start) { 617 stack.remove(stack.size() - 1); 618 } 619 stack.add(vec); 620 return (int) vec.iValue; 621 } 622 createKeyVector(int start, int length)623 private Value createKeyVector(int start, int length) { 624 // Figure out smallest bit width we can store this vector with. 625 int bitWidth = Math.max(WIDTH_8, widthUInBits(length)); 626 int prefixElems = 1; 627 // Check bit widths and types for all elements. 628 for (int i = start; i < stack.size(); i++) { 629 int elemWidth = Value.elemWidth(FBT_KEY, WIDTH_8, stack.get(i).key, bb.writePosition(), i + prefixElems); 630 bitWidth = Math.max(bitWidth, elemWidth); 631 } 632 633 int byteWidth = align(bitWidth); 634 // Write vector. First the keys width/offset if available, and size. 635 writeInt(length, byteWidth); 636 // Then the actual data. 637 int vloc = bb.writePosition(); 638 for (int i = start; i < stack.size(); i++) { 639 int pos = stack.get(i).key; 640 assert(pos != -1); 641 writeOffset(stack.get(i).key, byteWidth); 642 } 643 // Then the types. 644 return new Value(-1, FlexBuffers.toTypedVector(FBT_KEY,0), bitWidth, vloc); 645 } 646 647 private static class Value { 648 final int type; 649 // for scalars, represents scalar size in bytes 650 // for vectors, represents the size 651 // for string, length 652 final int minBitWidth; 653 // float value 654 final double dValue; 655 // integer value 656 long iValue; 657 // position of the key associated with this value in buffer 658 int key; 659 Value(int key, int type, int bitWidth, long iValue)660 Value(int key, int type, int bitWidth, long iValue) { 661 this.key = key; 662 this.type = type; 663 this.minBitWidth = bitWidth; 664 this.iValue = iValue; 665 this.dValue = Double.MIN_VALUE; 666 } 667 Value(int key, int type, int bitWidth, double dValue)668 Value(int key, int type, int bitWidth, double dValue) { 669 this.key = key; 670 this.type = type; 671 this.minBitWidth = bitWidth; 672 this.dValue = dValue; 673 this.iValue = Long.MIN_VALUE; 674 } 675 bool(int key, boolean b)676 static Value bool(int key, boolean b) { 677 return new Value(key, FBT_BOOL, WIDTH_8, b ? 1 : 0); 678 } 679 blob(int key, int position, int type, int bitWidth)680 static Value blob(int key, int position, int type, int bitWidth) { 681 return new Value(key, type, bitWidth, position); 682 } 683 int8(int key, int value)684 static Value int8(int key, int value) { 685 return new Value(key, FBT_INT, WIDTH_8, value); 686 } 687 int16(int key, int value)688 static Value int16(int key, int value) { 689 return new Value(key, FBT_INT, WIDTH_16, value); 690 } 691 int32(int key, int value)692 static Value int32(int key, int value) { 693 return new Value(key, FBT_INT, WIDTH_32, value); 694 } 695 int64(int key, long value)696 static Value int64(int key, long value) { 697 return new Value(key, FBT_INT, WIDTH_64, value); 698 } 699 uInt8(int key, int value)700 static Value uInt8(int key, int value) { 701 return new Value(key, FBT_UINT, WIDTH_8, value); 702 } 703 uInt16(int key, int value)704 static Value uInt16(int key, int value) { 705 return new Value(key, FBT_UINT, WIDTH_16, value); 706 } 707 uInt32(int key, int value)708 static Value uInt32(int key, int value) { 709 return new Value(key, FBT_UINT, WIDTH_32, value); 710 } 711 uInt64(int key, long value)712 static Value uInt64(int key, long value) { 713 return new Value(key, FBT_UINT, WIDTH_64, value); 714 } 715 float32(int key, float value)716 static Value float32(int key, float value) { 717 return new Value(key, FBT_FLOAT, WIDTH_32, value); 718 } 719 float64(int key, double value)720 static Value float64(int key, double value) { 721 return new Value(key, FBT_FLOAT, WIDTH_64, value); 722 } 723 storedPackedType()724 private byte storedPackedType() { 725 return storedPackedType(WIDTH_8); 726 } 727 storedPackedType(int parentBitWidth)728 private byte storedPackedType(int parentBitWidth) { 729 return packedType(storedWidth(parentBitWidth), type); 730 } 731 packedType(int bitWidth, int type)732 private static byte packedType(int bitWidth, int type) { 733 return (byte) (bitWidth | (type << 2)); 734 } 735 storedWidth(int parentBitWidth)736 private int storedWidth(int parentBitWidth) { 737 if (FlexBuffers.isTypeInline(type)) { 738 return Math.max(minBitWidth, parentBitWidth); 739 } else { 740 return minBitWidth; 741 } 742 } 743 elemWidth(int bufSize, int elemIndex)744 private int elemWidth(int bufSize, int elemIndex) { 745 return elemWidth(type, minBitWidth, iValue, bufSize, elemIndex); 746 } 747 elemWidth(int type, int minBitWidth, long iValue, int bufSize, int elemIndex)748 private static int elemWidth(int type, int minBitWidth, long iValue, int bufSize, int elemIndex) { 749 if (FlexBuffers.isTypeInline(type)) { 750 return minBitWidth; 751 } else { 752 // We have an absolute offset, but want to store a relative offset 753 // elem_index elements beyond the current buffer end. Since whether 754 // the relative offset fits in a certain byte_width depends on 755 // the size of the elements before it (and their alignment), we have 756 // to test for each size in turn. 757 758 // Original implementation checks for largest scalar 759 // which is long unsigned int 760 for (int byteWidth = 1; byteWidth <= 32; byteWidth *= 2) { 761 // Where are we going to write this offset? 762 int offsetLoc = bufSize + paddingBytes(bufSize, byteWidth) + (elemIndex * byteWidth); 763 // Compute relative offset. 764 long offset = offsetLoc - iValue; 765 // Does it fit? 766 int bitWidth = widthUInBits((int) offset); 767 if (((1L) << bitWidth) == byteWidth) 768 return bitWidth; 769 } 770 assert (false); // Must match one of the sizes above. 771 return WIDTH_64; 772 } 773 } 774 paddingBytes(int bufSize, int scalarSize)775 private static int paddingBytes(int bufSize, int scalarSize) { 776 return ((~bufSize) + 1) & (scalarSize - 1); 777 } 778 } 779 } 780 781 /// @} 782