xref: /aosp_15_r20/external/flatbuffers/dart/lib/src/reference.dart (revision 890232f25432b36107d06881e0a25aaa6b473652)
1*890232f2SAndroid Build Coastguard Workerimport 'dart:collection';
2*890232f2SAndroid Build Coastguard Workerimport 'dart:convert';
3*890232f2SAndroid Build Coastguard Workerimport 'dart:typed_data';
4*890232f2SAndroid Build Coastguard Workerimport 'types.dart';
5*890232f2SAndroid Build Coastguard Worker
6*890232f2SAndroid Build Coastguard Worker/// Main class to read a value out of a FlexBuffer.
7*890232f2SAndroid Build Coastguard Worker///
8*890232f2SAndroid Build Coastguard Worker/// This class let you access values stored in the buffer in a lazy fashion.
9*890232f2SAndroid Build Coastguard Workerclass Reference {
10*890232f2SAndroid Build Coastguard Worker  final ByteData _buffer;
11*890232f2SAndroid Build Coastguard Worker  final int _offset;
12*890232f2SAndroid Build Coastguard Worker  final BitWidth _parentWidth;
13*890232f2SAndroid Build Coastguard Worker  final String _path;
14*890232f2SAndroid Build Coastguard Worker  final int _byteWidth;
15*890232f2SAndroid Build Coastguard Worker  final ValueType _valueType;
16*890232f2SAndroid Build Coastguard Worker  int? _length;
17*890232f2SAndroid Build Coastguard Worker
18*890232f2SAndroid Build Coastguard Worker  Reference._(
19*890232f2SAndroid Build Coastguard Worker      this._buffer, this._offset, this._parentWidth, int packedType, this._path,
20*890232f2SAndroid Build Coastguard Worker      [int? byteWidth, ValueType? valueType])
21*890232f2SAndroid Build Coastguard Worker      : _byteWidth = byteWidth ?? 1 << (packedType & 3),
22*890232f2SAndroid Build Coastguard Worker        _valueType = valueType ?? ValueTypeUtils.fromInt(packedType >> 2);
23*890232f2SAndroid Build Coastguard Worker
24*890232f2SAndroid Build Coastguard Worker  /// Use this method to access the root value of a FlexBuffer.
25*890232f2SAndroid Build Coastguard Worker  static Reference fromBuffer(ByteBuffer buffer) {
26*890232f2SAndroid Build Coastguard Worker    final len = buffer.lengthInBytes;
27*890232f2SAndroid Build Coastguard Worker    if (len < 3) {
28*890232f2SAndroid Build Coastguard Worker      throw UnsupportedError('Buffer needs to be bigger than 3');
29*890232f2SAndroid Build Coastguard Worker    }
30*890232f2SAndroid Build Coastguard Worker    final byteData = ByteData.view(buffer);
31*890232f2SAndroid Build Coastguard Worker    final byteWidth = byteData.getUint8(len - 1);
32*890232f2SAndroid Build Coastguard Worker    final packedType = byteData.getUint8(len - 2);
33*890232f2SAndroid Build Coastguard Worker    final offset = len - byteWidth - 2;
34*890232f2SAndroid Build Coastguard Worker    return Reference._(ByteData.view(buffer), offset,
35*890232f2SAndroid Build Coastguard Worker        BitWidthUtil.fromByteWidth(byteWidth), packedType, "/");
36*890232f2SAndroid Build Coastguard Worker  }
37*890232f2SAndroid Build Coastguard Worker
38*890232f2SAndroid Build Coastguard Worker  /// Returns true if the underlying value is null.
39*890232f2SAndroid Build Coastguard Worker  bool get isNull => _valueType == ValueType.Null;
40*890232f2SAndroid Build Coastguard Worker
41*890232f2SAndroid Build Coastguard Worker  /// Returns true if the underlying value can be represented as [num].
42*890232f2SAndroid Build Coastguard Worker  bool get isNum =>
43*890232f2SAndroid Build Coastguard Worker      ValueTypeUtils.isNumber(_valueType) ||
44*890232f2SAndroid Build Coastguard Worker      ValueTypeUtils.isIndirectNumber(_valueType);
45*890232f2SAndroid Build Coastguard Worker
46*890232f2SAndroid Build Coastguard Worker  /// Returns true if the underlying value was encoded as a float (direct or indirect).
47*890232f2SAndroid Build Coastguard Worker  bool get isDouble =>
48*890232f2SAndroid Build Coastguard Worker      _valueType == ValueType.Float || _valueType == ValueType.IndirectFloat;
49*890232f2SAndroid Build Coastguard Worker
50*890232f2SAndroid Build Coastguard Worker  /// Returns true if the underlying value was encoded as an int or uint (direct or indirect).
51*890232f2SAndroid Build Coastguard Worker  bool get isInt => isNum && !isDouble;
52*890232f2SAndroid Build Coastguard Worker
53*890232f2SAndroid Build Coastguard Worker  /// Returns true if the underlying value was encoded as a string or a key.
54*890232f2SAndroid Build Coastguard Worker  bool get isString =>
55*890232f2SAndroid Build Coastguard Worker      _valueType == ValueType.String || _valueType == ValueType.Key;
56*890232f2SAndroid Build Coastguard Worker
57*890232f2SAndroid Build Coastguard Worker  /// Returns true if the underlying value was encoded as a bool.
58*890232f2SAndroid Build Coastguard Worker  bool get isBool => _valueType == ValueType.Bool;
59*890232f2SAndroid Build Coastguard Worker
60*890232f2SAndroid Build Coastguard Worker  /// Returns true if the underlying value was encoded as a blob.
61*890232f2SAndroid Build Coastguard Worker  bool get isBlob => _valueType == ValueType.Blob;
62*890232f2SAndroid Build Coastguard Worker
63*890232f2SAndroid Build Coastguard Worker  /// Returns true if the underlying value points to a vector.
64*890232f2SAndroid Build Coastguard Worker  bool get isVector => ValueTypeUtils.isAVector(_valueType);
65*890232f2SAndroid Build Coastguard Worker
66*890232f2SAndroid Build Coastguard Worker  /// Returns true if the underlying value points to a map.
67*890232f2SAndroid Build Coastguard Worker  bool get isMap => _valueType == ValueType.Map;
68*890232f2SAndroid Build Coastguard Worker
69*890232f2SAndroid Build Coastguard Worker  /// If this [isBool], returns the bool value. Otherwise, returns null.
70*890232f2SAndroid Build Coastguard Worker  bool? get boolValue {
71*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.Bool) {
72*890232f2SAndroid Build Coastguard Worker      return _readInt(_offset, _parentWidth) != 0;
73*890232f2SAndroid Build Coastguard Worker    }
74*890232f2SAndroid Build Coastguard Worker    return null;
75*890232f2SAndroid Build Coastguard Worker  }
76*890232f2SAndroid Build Coastguard Worker
77*890232f2SAndroid Build Coastguard Worker  /// Returns an [int], if the underlying value can be represented as an int.
78*890232f2SAndroid Build Coastguard Worker  ///
79*890232f2SAndroid Build Coastguard Worker  /// Otherwise returns [null].
80*890232f2SAndroid Build Coastguard Worker  int? get intValue {
81*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.Int) {
82*890232f2SAndroid Build Coastguard Worker      return _readInt(_offset, _parentWidth);
83*890232f2SAndroid Build Coastguard Worker    }
84*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.UInt) {
85*890232f2SAndroid Build Coastguard Worker      return _readUInt(_offset, _parentWidth);
86*890232f2SAndroid Build Coastguard Worker    }
87*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.IndirectInt) {
88*890232f2SAndroid Build Coastguard Worker      return _readInt(_indirect, BitWidthUtil.fromByteWidth(_byteWidth));
89*890232f2SAndroid Build Coastguard Worker    }
90*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.IndirectUInt) {
91*890232f2SAndroid Build Coastguard Worker      return _readUInt(_indirect, BitWidthUtil.fromByteWidth(_byteWidth));
92*890232f2SAndroid Build Coastguard Worker    }
93*890232f2SAndroid Build Coastguard Worker    return null;
94*890232f2SAndroid Build Coastguard Worker  }
95*890232f2SAndroid Build Coastguard Worker
96*890232f2SAndroid Build Coastguard Worker  /// Returns [double], if the underlying value [isDouble].
97*890232f2SAndroid Build Coastguard Worker  ///
98*890232f2SAndroid Build Coastguard Worker  /// Otherwise returns [null].
99*890232f2SAndroid Build Coastguard Worker  double? get doubleValue {
100*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.Float) {
101*890232f2SAndroid Build Coastguard Worker      return _readFloat(_offset, _parentWidth);
102*890232f2SAndroid Build Coastguard Worker    }
103*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.IndirectFloat) {
104*890232f2SAndroid Build Coastguard Worker      return _readFloat(_indirect, BitWidthUtil.fromByteWidth(_byteWidth));
105*890232f2SAndroid Build Coastguard Worker    }
106*890232f2SAndroid Build Coastguard Worker    return null;
107*890232f2SAndroid Build Coastguard Worker  }
108*890232f2SAndroid Build Coastguard Worker
109*890232f2SAndroid Build Coastguard Worker  /// Returns [num], if the underlying value is numeric, be it int uint, or float (direct or indirect).
110*890232f2SAndroid Build Coastguard Worker  ///
111*890232f2SAndroid Build Coastguard Worker  /// Otherwise returns [null].
112*890232f2SAndroid Build Coastguard Worker  num? get numValue => doubleValue ?? intValue;
113*890232f2SAndroid Build Coastguard Worker
114*890232f2SAndroid Build Coastguard Worker  /// Returns [String] value or null otherwise.
115*890232f2SAndroid Build Coastguard Worker  ///
116*890232f2SAndroid Build Coastguard Worker  /// This method performers a utf8 decoding, as FlexBuffers format stores strings in utf8 encoding.
117*890232f2SAndroid Build Coastguard Worker  String? get stringValue {
118*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.String || _valueType == ValueType.Key) {
119*890232f2SAndroid Build Coastguard Worker      return utf8.decode(_buffer.buffer.asUint8List(_indirect, length));
120*890232f2SAndroid Build Coastguard Worker    }
121*890232f2SAndroid Build Coastguard Worker    return null;
122*890232f2SAndroid Build Coastguard Worker  }
123*890232f2SAndroid Build Coastguard Worker
124*890232f2SAndroid Build Coastguard Worker  /// Returns [Uint8List] value or null otherwise.
125*890232f2SAndroid Build Coastguard Worker  Uint8List? get blobValue {
126*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.Blob) {
127*890232f2SAndroid Build Coastguard Worker      return _buffer.buffer.asUint8List(_indirect, length);
128*890232f2SAndroid Build Coastguard Worker    }
129*890232f2SAndroid Build Coastguard Worker    return null;
130*890232f2SAndroid Build Coastguard Worker  }
131*890232f2SAndroid Build Coastguard Worker
132*890232f2SAndroid Build Coastguard Worker  /// Can be used with an [int] or a [String] value for key.
133*890232f2SAndroid Build Coastguard Worker  /// If the underlying value in FlexBuffer is a vector, then use [int] for access.
134*890232f2SAndroid Build Coastguard Worker  /// If the underlying value in FlexBuffer is a map, then use [String] for access.
135*890232f2SAndroid Build Coastguard Worker  /// Returns [Reference] value. Throws an exception when [key] is not applicable.
136*890232f2SAndroid Build Coastguard Worker  Reference operator [](Object key) {
137*890232f2SAndroid Build Coastguard Worker    if (key is int && ValueTypeUtils.isAVector(_valueType)) {
138*890232f2SAndroid Build Coastguard Worker      final index = key;
139*890232f2SAndroid Build Coastguard Worker      if (index >= length || index < 0) {
140*890232f2SAndroid Build Coastguard Worker        throw ArgumentError(
141*890232f2SAndroid Build Coastguard Worker            'Key: [$key] is not applicable on: $_path of: $_valueType length: $length');
142*890232f2SAndroid Build Coastguard Worker      }
143*890232f2SAndroid Build Coastguard Worker      final elementOffset = _indirect + index * _byteWidth;
144*890232f2SAndroid Build Coastguard Worker      int packedType = 0;
145*890232f2SAndroid Build Coastguard Worker      int? byteWidth;
146*890232f2SAndroid Build Coastguard Worker      ValueType? valueType;
147*890232f2SAndroid Build Coastguard Worker      if (ValueTypeUtils.isTypedVector(_valueType)) {
148*890232f2SAndroid Build Coastguard Worker        byteWidth = 1;
149*890232f2SAndroid Build Coastguard Worker        valueType = ValueTypeUtils.typedVectorElementType(_valueType);
150*890232f2SAndroid Build Coastguard Worker      } else if (ValueTypeUtils.isFixedTypedVector(_valueType)) {
151*890232f2SAndroid Build Coastguard Worker        byteWidth = 1;
152*890232f2SAndroid Build Coastguard Worker        valueType = ValueTypeUtils.fixedTypedVectorElementType(_valueType);
153*890232f2SAndroid Build Coastguard Worker      } else {
154*890232f2SAndroid Build Coastguard Worker        packedType = _buffer.getUint8(_indirect + length * _byteWidth + index);
155*890232f2SAndroid Build Coastguard Worker      }
156*890232f2SAndroid Build Coastguard Worker      return Reference._(
157*890232f2SAndroid Build Coastguard Worker          _buffer,
158*890232f2SAndroid Build Coastguard Worker          elementOffset,
159*890232f2SAndroid Build Coastguard Worker          BitWidthUtil.fromByteWidth(_byteWidth),
160*890232f2SAndroid Build Coastguard Worker          packedType,
161*890232f2SAndroid Build Coastguard Worker          "$_path[$index]",
162*890232f2SAndroid Build Coastguard Worker          byteWidth,
163*890232f2SAndroid Build Coastguard Worker          valueType);
164*890232f2SAndroid Build Coastguard Worker    }
165*890232f2SAndroid Build Coastguard Worker    if (key is String && _valueType == ValueType.Map) {
166*890232f2SAndroid Build Coastguard Worker      final index = _keyIndex(key);
167*890232f2SAndroid Build Coastguard Worker      if (index != null) {
168*890232f2SAndroid Build Coastguard Worker        return _valueForIndexWithKey(index, key);
169*890232f2SAndroid Build Coastguard Worker      }
170*890232f2SAndroid Build Coastguard Worker    }
171*890232f2SAndroid Build Coastguard Worker    throw ArgumentError(
172*890232f2SAndroid Build Coastguard Worker        'Key: [$key] is not applicable on: $_path of: $_valueType');
173*890232f2SAndroid Build Coastguard Worker  }
174*890232f2SAndroid Build Coastguard Worker
175*890232f2SAndroid Build Coastguard Worker  /// Get an iterable if the underlying flexBuffer value is a vector.
176*890232f2SAndroid Build Coastguard Worker  /// Otherwise throws an exception.
177*890232f2SAndroid Build Coastguard Worker  Iterable<Reference> get vectorIterable {
178*890232f2SAndroid Build Coastguard Worker    if (isVector == false) {
179*890232f2SAndroid Build Coastguard Worker      throw UnsupportedError('Value is not a vector. It is: $_valueType');
180*890232f2SAndroid Build Coastguard Worker    }
181*890232f2SAndroid Build Coastguard Worker    return _VectorIterator(this);
182*890232f2SAndroid Build Coastguard Worker  }
183*890232f2SAndroid Build Coastguard Worker
184*890232f2SAndroid Build Coastguard Worker  /// Get an iterable for keys if the underlying flexBuffer value is a map.
185*890232f2SAndroid Build Coastguard Worker  /// Otherwise throws an exception.
186*890232f2SAndroid Build Coastguard Worker  Iterable<String> get mapKeyIterable {
187*890232f2SAndroid Build Coastguard Worker    if (isMap == false) {
188*890232f2SAndroid Build Coastguard Worker      throw UnsupportedError('Value is not a map. It is: $_valueType');
189*890232f2SAndroid Build Coastguard Worker    }
190*890232f2SAndroid Build Coastguard Worker    return _MapKeyIterator(this);
191*890232f2SAndroid Build Coastguard Worker  }
192*890232f2SAndroid Build Coastguard Worker
193*890232f2SAndroid Build Coastguard Worker  /// Get an iterable for values if the underlying flexBuffer value is a map.
194*890232f2SAndroid Build Coastguard Worker  /// Otherwise throws an exception.
195*890232f2SAndroid Build Coastguard Worker  Iterable<Reference> get mapValueIterable {
196*890232f2SAndroid Build Coastguard Worker    if (isMap == false) {
197*890232f2SAndroid Build Coastguard Worker      throw UnsupportedError('Value is not a map. It is: $_valueType');
198*890232f2SAndroid Build Coastguard Worker    }
199*890232f2SAndroid Build Coastguard Worker    return _MapValueIterator(this);
200*890232f2SAndroid Build Coastguard Worker  }
201*890232f2SAndroid Build Coastguard Worker
202*890232f2SAndroid Build Coastguard Worker  /// Returns the length of the the underlying FlexBuffer value.
203*890232f2SAndroid Build Coastguard Worker  /// If the underlying value is [null] the length is 0.
204*890232f2SAndroid Build Coastguard Worker  /// If the underlying value is a number, or a bool, the length is 1.
205*890232f2SAndroid Build Coastguard Worker  /// If the underlying value is a vector, or map, the length reflects number of elements / element pairs.
206*890232f2SAndroid Build Coastguard Worker  /// If the values is a string or a blob, the length reflects a number of bytes the value occupies (strings are encoded in utf8 format).
207*890232f2SAndroid Build Coastguard Worker  int get length {
208*890232f2SAndroid Build Coastguard Worker    if (_length == null) {
209*890232f2SAndroid Build Coastguard Worker      // needs to be checked before more generic isAVector
210*890232f2SAndroid Build Coastguard Worker      if (ValueTypeUtils.isFixedTypedVector(_valueType)) {
211*890232f2SAndroid Build Coastguard Worker        _length = ValueTypeUtils.fixedTypedVectorElementSize(_valueType);
212*890232f2SAndroid Build Coastguard Worker      } else if (_valueType == ValueType.Blob ||
213*890232f2SAndroid Build Coastguard Worker          ValueTypeUtils.isAVector(_valueType) ||
214*890232f2SAndroid Build Coastguard Worker          _valueType == ValueType.Map) {
215*890232f2SAndroid Build Coastguard Worker        _length = _readUInt(
216*890232f2SAndroid Build Coastguard Worker            _indirect - _byteWidth, BitWidthUtil.fromByteWidth(_byteWidth));
217*890232f2SAndroid Build Coastguard Worker      } else if (_valueType == ValueType.Null) {
218*890232f2SAndroid Build Coastguard Worker        _length = 0;
219*890232f2SAndroid Build Coastguard Worker      } else if (_valueType == ValueType.String) {
220*890232f2SAndroid Build Coastguard Worker        final indirect = _indirect;
221*890232f2SAndroid Build Coastguard Worker        var sizeByteWidth = _byteWidth;
222*890232f2SAndroid Build Coastguard Worker        var size = _readUInt(indirect - sizeByteWidth,
223*890232f2SAndroid Build Coastguard Worker            BitWidthUtil.fromByteWidth(sizeByteWidth));
224*890232f2SAndroid Build Coastguard Worker        while (_buffer.getInt8(indirect + size) != 0) {
225*890232f2SAndroid Build Coastguard Worker          sizeByteWidth <<= 1;
226*890232f2SAndroid Build Coastguard Worker          size = _readUInt(indirect - sizeByteWidth,
227*890232f2SAndroid Build Coastguard Worker              BitWidthUtil.fromByteWidth(sizeByteWidth));
228*890232f2SAndroid Build Coastguard Worker        }
229*890232f2SAndroid Build Coastguard Worker        _length = size;
230*890232f2SAndroid Build Coastguard Worker      } else if (_valueType == ValueType.Key) {
231*890232f2SAndroid Build Coastguard Worker        final indirect = _indirect;
232*890232f2SAndroid Build Coastguard Worker        var size = 1;
233*890232f2SAndroid Build Coastguard Worker        while (_buffer.getInt8(indirect + size) != 0) {
234*890232f2SAndroid Build Coastguard Worker          size += 1;
235*890232f2SAndroid Build Coastguard Worker        }
236*890232f2SAndroid Build Coastguard Worker        _length = size;
237*890232f2SAndroid Build Coastguard Worker      } else {
238*890232f2SAndroid Build Coastguard Worker        _length = 1;
239*890232f2SAndroid Build Coastguard Worker      }
240*890232f2SAndroid Build Coastguard Worker    }
241*890232f2SAndroid Build Coastguard Worker    return _length!;
242*890232f2SAndroid Build Coastguard Worker  }
243*890232f2SAndroid Build Coastguard Worker
244*890232f2SAndroid Build Coastguard Worker  /// Returns a minified JSON representation of the underlying FlexBuffer value.
245*890232f2SAndroid Build Coastguard Worker  ///
246*890232f2SAndroid Build Coastguard Worker  /// This method involves materializing the entire object tree, which may be
247*890232f2SAndroid Build Coastguard Worker  /// expensive. It is more efficient to work with [Reference] and access only the needed data.
248*890232f2SAndroid Build Coastguard Worker  /// Blob values are represented as base64 encoded string.
249*890232f2SAndroid Build Coastguard Worker  String get json {
250*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.Bool) {
251*890232f2SAndroid Build Coastguard Worker      return boolValue! ? 'true' : 'false';
252*890232f2SAndroid Build Coastguard Worker    }
253*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.Null) {
254*890232f2SAndroid Build Coastguard Worker      return 'null';
255*890232f2SAndroid Build Coastguard Worker    }
256*890232f2SAndroid Build Coastguard Worker    if (ValueTypeUtils.isNumber(_valueType)) {
257*890232f2SAndroid Build Coastguard Worker      return jsonEncode(numValue);
258*890232f2SAndroid Build Coastguard Worker    }
259*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.String) {
260*890232f2SAndroid Build Coastguard Worker      return jsonEncode(stringValue);
261*890232f2SAndroid Build Coastguard Worker    }
262*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.Blob) {
263*890232f2SAndroid Build Coastguard Worker      return jsonEncode(base64Encode(blobValue!));
264*890232f2SAndroid Build Coastguard Worker    }
265*890232f2SAndroid Build Coastguard Worker    if (ValueTypeUtils.isAVector(_valueType)) {
266*890232f2SAndroid Build Coastguard Worker      final result = StringBuffer();
267*890232f2SAndroid Build Coastguard Worker      result.write('[');
268*890232f2SAndroid Build Coastguard Worker      for (var i = 0; i < length; i++) {
269*890232f2SAndroid Build Coastguard Worker        result.write(this[i].json);
270*890232f2SAndroid Build Coastguard Worker        if (i < length - 1) {
271*890232f2SAndroid Build Coastguard Worker          result.write(',');
272*890232f2SAndroid Build Coastguard Worker        }
273*890232f2SAndroid Build Coastguard Worker      }
274*890232f2SAndroid Build Coastguard Worker      result.write(']');
275*890232f2SAndroid Build Coastguard Worker      return result.toString();
276*890232f2SAndroid Build Coastguard Worker    }
277*890232f2SAndroid Build Coastguard Worker    if (_valueType == ValueType.Map) {
278*890232f2SAndroid Build Coastguard Worker      final result = StringBuffer();
279*890232f2SAndroid Build Coastguard Worker      result.write('{');
280*890232f2SAndroid Build Coastguard Worker      for (var i = 0; i < length; i++) {
281*890232f2SAndroid Build Coastguard Worker        result.write(jsonEncode(_keyForIndex(i)));
282*890232f2SAndroid Build Coastguard Worker        result.write(':');
283*890232f2SAndroid Build Coastguard Worker        result.write(_valueForIndex(i).json);
284*890232f2SAndroid Build Coastguard Worker        if (i < length - 1) {
285*890232f2SAndroid Build Coastguard Worker          result.write(',');
286*890232f2SAndroid Build Coastguard Worker        }
287*890232f2SAndroid Build Coastguard Worker      }
288*890232f2SAndroid Build Coastguard Worker      result.write('}');
289*890232f2SAndroid Build Coastguard Worker      return result.toString();
290*890232f2SAndroid Build Coastguard Worker    }
291*890232f2SAndroid Build Coastguard Worker    throw UnsupportedError(
292*890232f2SAndroid Build Coastguard Worker        'Type: $_valueType is not supported for JSON conversion');
293*890232f2SAndroid Build Coastguard Worker  }
294*890232f2SAndroid Build Coastguard Worker
295*890232f2SAndroid Build Coastguard Worker  /// Computes the indirect offset of the value.
296*890232f2SAndroid Build Coastguard Worker  ///
297*890232f2SAndroid Build Coastguard Worker  /// To optimize for the more common case of being called only once, this
298*890232f2SAndroid Build Coastguard Worker  /// value is not cached. Callers that need to use it more than once should
299*890232f2SAndroid Build Coastguard Worker  /// cache the return value in a local variable.
300*890232f2SAndroid Build Coastguard Worker  int get _indirect {
301*890232f2SAndroid Build Coastguard Worker    final step = _readUInt(_offset, _parentWidth);
302*890232f2SAndroid Build Coastguard Worker    return _offset - step;
303*890232f2SAndroid Build Coastguard Worker  }
304*890232f2SAndroid Build Coastguard Worker
305*890232f2SAndroid Build Coastguard Worker  int _readInt(int offset, BitWidth width) {
306*890232f2SAndroid Build Coastguard Worker    _validateOffset(offset, width);
307*890232f2SAndroid Build Coastguard Worker    if (width == BitWidth.width8) {
308*890232f2SAndroid Build Coastguard Worker      return _buffer.getInt8(offset);
309*890232f2SAndroid Build Coastguard Worker    }
310*890232f2SAndroid Build Coastguard Worker    if (width == BitWidth.width16) {
311*890232f2SAndroid Build Coastguard Worker      return _buffer.getInt16(offset, Endian.little);
312*890232f2SAndroid Build Coastguard Worker    }
313*890232f2SAndroid Build Coastguard Worker    if (width == BitWidth.width32) {
314*890232f2SAndroid Build Coastguard Worker      return _buffer.getInt32(offset, Endian.little);
315*890232f2SAndroid Build Coastguard Worker    }
316*890232f2SAndroid Build Coastguard Worker    return _buffer.getInt64(offset, Endian.little);
317*890232f2SAndroid Build Coastguard Worker  }
318*890232f2SAndroid Build Coastguard Worker
319*890232f2SAndroid Build Coastguard Worker  int _readUInt(int offset, BitWidth width) {
320*890232f2SAndroid Build Coastguard Worker    _validateOffset(offset, width);
321*890232f2SAndroid Build Coastguard Worker    if (width == BitWidth.width8) {
322*890232f2SAndroid Build Coastguard Worker      return _buffer.getUint8(offset);
323*890232f2SAndroid Build Coastguard Worker    }
324*890232f2SAndroid Build Coastguard Worker    if (width == BitWidth.width16) {
325*890232f2SAndroid Build Coastguard Worker      return _buffer.getUint16(offset, Endian.little);
326*890232f2SAndroid Build Coastguard Worker    }
327*890232f2SAndroid Build Coastguard Worker    if (width == BitWidth.width32) {
328*890232f2SAndroid Build Coastguard Worker      return _buffer.getUint32(offset, Endian.little);
329*890232f2SAndroid Build Coastguard Worker    }
330*890232f2SAndroid Build Coastguard Worker    return _buffer.getUint64(offset, Endian.little);
331*890232f2SAndroid Build Coastguard Worker  }
332*890232f2SAndroid Build Coastguard Worker
333*890232f2SAndroid Build Coastguard Worker  double _readFloat(int offset, BitWidth width) {
334*890232f2SAndroid Build Coastguard Worker    _validateOffset(offset, width);
335*890232f2SAndroid Build Coastguard Worker    if (width.index < BitWidth.width32.index) {
336*890232f2SAndroid Build Coastguard Worker      throw StateError('Bad width: $width');
337*890232f2SAndroid Build Coastguard Worker    }
338*890232f2SAndroid Build Coastguard Worker
339*890232f2SAndroid Build Coastguard Worker    if (width == BitWidth.width32) {
340*890232f2SAndroid Build Coastguard Worker      return _buffer.getFloat32(offset, Endian.little);
341*890232f2SAndroid Build Coastguard Worker    }
342*890232f2SAndroid Build Coastguard Worker
343*890232f2SAndroid Build Coastguard Worker    return _buffer.getFloat64(offset, Endian.little);
344*890232f2SAndroid Build Coastguard Worker  }
345*890232f2SAndroid Build Coastguard Worker
346*890232f2SAndroid Build Coastguard Worker  void _validateOffset(int offset, BitWidth width) {
347*890232f2SAndroid Build Coastguard Worker    if (_offset < 0 ||
348*890232f2SAndroid Build Coastguard Worker        _buffer.lengthInBytes <= offset + width.index ||
349*890232f2SAndroid Build Coastguard Worker        offset & (BitWidthUtil.toByteWidth(width) - 1) != 0) {
350*890232f2SAndroid Build Coastguard Worker      throw StateError('Bad offset: $offset, width: $width');
351*890232f2SAndroid Build Coastguard Worker    }
352*890232f2SAndroid Build Coastguard Worker  }
353*890232f2SAndroid Build Coastguard Worker
354*890232f2SAndroid Build Coastguard Worker  int? _keyIndex(String key) {
355*890232f2SAndroid Build Coastguard Worker    final input = utf8.encode(key);
356*890232f2SAndroid Build Coastguard Worker    final keysVectorOffset = _indirect - _byteWidth * 3;
357*890232f2SAndroid Build Coastguard Worker    final indirectOffset = keysVectorOffset -
358*890232f2SAndroid Build Coastguard Worker        _readUInt(keysVectorOffset, BitWidthUtil.fromByteWidth(_byteWidth));
359*890232f2SAndroid Build Coastguard Worker    final byteWidth = _readUInt(
360*890232f2SAndroid Build Coastguard Worker        keysVectorOffset + _byteWidth, BitWidthUtil.fromByteWidth(_byteWidth));
361*890232f2SAndroid Build Coastguard Worker    var low = 0;
362*890232f2SAndroid Build Coastguard Worker    var high = length - 1;
363*890232f2SAndroid Build Coastguard Worker    while (low <= high) {
364*890232f2SAndroid Build Coastguard Worker      final mid = (high + low) >> 1;
365*890232f2SAndroid Build Coastguard Worker      final dif = _diffKeys(input, mid, indirectOffset, byteWidth);
366*890232f2SAndroid Build Coastguard Worker      if (dif == 0) return mid;
367*890232f2SAndroid Build Coastguard Worker      if (dif < 0) {
368*890232f2SAndroid Build Coastguard Worker        high = mid - 1;
369*890232f2SAndroid Build Coastguard Worker      } else {
370*890232f2SAndroid Build Coastguard Worker        low = mid + 1;
371*890232f2SAndroid Build Coastguard Worker      }
372*890232f2SAndroid Build Coastguard Worker    }
373*890232f2SAndroid Build Coastguard Worker    return null;
374*890232f2SAndroid Build Coastguard Worker  }
375*890232f2SAndroid Build Coastguard Worker
376*890232f2SAndroid Build Coastguard Worker  int _diffKeys(List<int> input, int index, int indirectOffset, int byteWidth) {
377*890232f2SAndroid Build Coastguard Worker    final keyOffset = indirectOffset + index * byteWidth;
378*890232f2SAndroid Build Coastguard Worker    final keyIndirectOffset =
379*890232f2SAndroid Build Coastguard Worker        keyOffset - _readUInt(keyOffset, BitWidthUtil.fromByteWidth(byteWidth));
380*890232f2SAndroid Build Coastguard Worker    for (var i = 0; i < input.length; i++) {
381*890232f2SAndroid Build Coastguard Worker      final dif = input[i] - _buffer.getUint8(keyIndirectOffset + i);
382*890232f2SAndroid Build Coastguard Worker      if (dif != 0) {
383*890232f2SAndroid Build Coastguard Worker        return dif;
384*890232f2SAndroid Build Coastguard Worker      }
385*890232f2SAndroid Build Coastguard Worker    }
386*890232f2SAndroid Build Coastguard Worker    return (_buffer.getUint8(keyIndirectOffset + input.length) == 0) ? 0 : -1;
387*890232f2SAndroid Build Coastguard Worker  }
388*890232f2SAndroid Build Coastguard Worker
389*890232f2SAndroid Build Coastguard Worker  Reference _valueForIndexWithKey(int index, String key) {
390*890232f2SAndroid Build Coastguard Worker    final indirect = _indirect;
391*890232f2SAndroid Build Coastguard Worker    final elementOffset = indirect + index * _byteWidth;
392*890232f2SAndroid Build Coastguard Worker    final packedType = _buffer.getUint8(indirect + length * _byteWidth + index);
393*890232f2SAndroid Build Coastguard Worker    return Reference._(_buffer, elementOffset,
394*890232f2SAndroid Build Coastguard Worker        BitWidthUtil.fromByteWidth(_byteWidth), packedType, "$_path/$key");
395*890232f2SAndroid Build Coastguard Worker  }
396*890232f2SAndroid Build Coastguard Worker
397*890232f2SAndroid Build Coastguard Worker  Reference _valueForIndex(int index) {
398*890232f2SAndroid Build Coastguard Worker    final indirect = _indirect;
399*890232f2SAndroid Build Coastguard Worker    final elementOffset = indirect + index * _byteWidth;
400*890232f2SAndroid Build Coastguard Worker    final packedType = _buffer.getUint8(indirect + length * _byteWidth + index);
401*890232f2SAndroid Build Coastguard Worker    return Reference._(_buffer, elementOffset,
402*890232f2SAndroid Build Coastguard Worker        BitWidthUtil.fromByteWidth(_byteWidth), packedType, "$_path/[$index]");
403*890232f2SAndroid Build Coastguard Worker  }
404*890232f2SAndroid Build Coastguard Worker
405*890232f2SAndroid Build Coastguard Worker  String _keyForIndex(int index) {
406*890232f2SAndroid Build Coastguard Worker    final keysVectorOffset = _indirect - _byteWidth * 3;
407*890232f2SAndroid Build Coastguard Worker    final indirectOffset = keysVectorOffset -
408*890232f2SAndroid Build Coastguard Worker        _readUInt(keysVectorOffset, BitWidthUtil.fromByteWidth(_byteWidth));
409*890232f2SAndroid Build Coastguard Worker    final byteWidth = _readUInt(
410*890232f2SAndroid Build Coastguard Worker        keysVectorOffset + _byteWidth, BitWidthUtil.fromByteWidth(_byteWidth));
411*890232f2SAndroid Build Coastguard Worker    final keyOffset = indirectOffset + index * byteWidth;
412*890232f2SAndroid Build Coastguard Worker    final keyIndirectOffset =
413*890232f2SAndroid Build Coastguard Worker        keyOffset - _readUInt(keyOffset, BitWidthUtil.fromByteWidth(byteWidth));
414*890232f2SAndroid Build Coastguard Worker    var length = 0;
415*890232f2SAndroid Build Coastguard Worker    while (_buffer.getUint8(keyIndirectOffset + length) != 0) {
416*890232f2SAndroid Build Coastguard Worker      length += 1;
417*890232f2SAndroid Build Coastguard Worker    }
418*890232f2SAndroid Build Coastguard Worker    return utf8.decode(_buffer.buffer.asUint8List(keyIndirectOffset, length));
419*890232f2SAndroid Build Coastguard Worker  }
420*890232f2SAndroid Build Coastguard Worker}
421*890232f2SAndroid Build Coastguard Worker
422*890232f2SAndroid Build Coastguard Workerclass _VectorIterator
423*890232f2SAndroid Build Coastguard Worker    with IterableMixin<Reference>
424*890232f2SAndroid Build Coastguard Worker    implements Iterator<Reference> {
425*890232f2SAndroid Build Coastguard Worker  final Reference _vector;
426*890232f2SAndroid Build Coastguard Worker  int index = -1;
427*890232f2SAndroid Build Coastguard Worker
428*890232f2SAndroid Build Coastguard Worker  _VectorIterator(this._vector);
429*890232f2SAndroid Build Coastguard Worker
430*890232f2SAndroid Build Coastguard Worker  @override
431*890232f2SAndroid Build Coastguard Worker  Reference get current => _vector[index];
432*890232f2SAndroid Build Coastguard Worker
433*890232f2SAndroid Build Coastguard Worker  @override
434*890232f2SAndroid Build Coastguard Worker  bool moveNext() {
435*890232f2SAndroid Build Coastguard Worker    index++;
436*890232f2SAndroid Build Coastguard Worker    return index < _vector.length;
437*890232f2SAndroid Build Coastguard Worker  }
438*890232f2SAndroid Build Coastguard Worker
439*890232f2SAndroid Build Coastguard Worker  @override
440*890232f2SAndroid Build Coastguard Worker  Iterator<Reference> get iterator => this;
441*890232f2SAndroid Build Coastguard Worker}
442*890232f2SAndroid Build Coastguard Worker
443*890232f2SAndroid Build Coastguard Workerclass _MapKeyIterator with IterableMixin<String> implements Iterator<String> {
444*890232f2SAndroid Build Coastguard Worker  final Reference _map;
445*890232f2SAndroid Build Coastguard Worker  int index = -1;
446*890232f2SAndroid Build Coastguard Worker
447*890232f2SAndroid Build Coastguard Worker  _MapKeyIterator(this._map);
448*890232f2SAndroid Build Coastguard Worker
449*890232f2SAndroid Build Coastguard Worker  @override
450*890232f2SAndroid Build Coastguard Worker  String get current => _map._keyForIndex(index);
451*890232f2SAndroid Build Coastguard Worker
452*890232f2SAndroid Build Coastguard Worker  @override
453*890232f2SAndroid Build Coastguard Worker  bool moveNext() {
454*890232f2SAndroid Build Coastguard Worker    index++;
455*890232f2SAndroid Build Coastguard Worker    return index < _map.length;
456*890232f2SAndroid Build Coastguard Worker  }
457*890232f2SAndroid Build Coastguard Worker
458*890232f2SAndroid Build Coastguard Worker  @override
459*890232f2SAndroid Build Coastguard Worker  Iterator<String> get iterator => this;
460*890232f2SAndroid Build Coastguard Worker}
461*890232f2SAndroid Build Coastguard Worker
462*890232f2SAndroid Build Coastguard Workerclass _MapValueIterator
463*890232f2SAndroid Build Coastguard Worker    with IterableMixin<Reference>
464*890232f2SAndroid Build Coastguard Worker    implements Iterator<Reference> {
465*890232f2SAndroid Build Coastguard Worker  final Reference _map;
466*890232f2SAndroid Build Coastguard Worker  int index = -1;
467*890232f2SAndroid Build Coastguard Worker
468*890232f2SAndroid Build Coastguard Worker  _MapValueIterator(this._map);
469*890232f2SAndroid Build Coastguard Worker
470*890232f2SAndroid Build Coastguard Worker  @override
471*890232f2SAndroid Build Coastguard Worker  Reference get current => _map._valueForIndex(index);
472*890232f2SAndroid Build Coastguard Worker
473*890232f2SAndroid Build Coastguard Worker  @override
474*890232f2SAndroid Build Coastguard Worker  bool moveNext() {
475*890232f2SAndroid Build Coastguard Worker    index++;
476*890232f2SAndroid Build Coastguard Worker    return index < _map.length;
477*890232f2SAndroid Build Coastguard Worker  }
478*890232f2SAndroid Build Coastguard Worker
479*890232f2SAndroid Build Coastguard Worker  @override
480*890232f2SAndroid Build Coastguard Worker  Iterator<Reference> get iterator => this;
481*890232f2SAndroid Build Coastguard Worker}
482