• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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