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