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