• 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      * 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