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