| // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:convert'; |
| import 'dart:typed_data'; |
| |
| import 'package:_fe_analyzer_shared/src/scanner/string_canonicalizer.dart'; |
| import 'package:analyzer/src/binary/binary_writer.dart'; |
| import 'package:analyzer/src/binary/string_table.dart'; |
| import 'package:analyzer/src/fine/manifest_id.dart'; |
| import 'package:analyzer/src/utilities/uri_cache.dart'; |
| |
| /// Reader for binary formats. |
| class BinaryReader { |
| final Uint8List bytes; |
| int offset = 0; |
| |
| final Int64List _int64Buffer = Int64List(1); |
| late final Uint8List _int64BufferUint8 = _int64Buffer.buffer.asUint8List(); |
| |
| final Float64List _doubleBuffer = Float64List(1); |
| late final Uint8List _doubleBufferUint8 = _doubleBuffer.buffer.asUint8List(); |
| |
| List<ManifestItemId>? _manifestIdTable; |
| StringTable? _stringTable; |
| |
| BinaryReader(this.bytes); |
| |
| /// Create a new instance with the given [offset]. |
| /// It shares the same bytes and string reader. |
| BinaryReader fork(int offset) { |
| var result = BinaryReader(bytes); |
| result.offset = offset; |
| result._manifestIdTable = _manifestIdTable; |
| result._stringTable = _stringTable; |
| return result; |
| } |
| |
| /// Initializes the manifest-ID and string tables by reading their start |
| /// offsets from two `uint32` values trailer at the end of the buffer. |
| /// The reader's current offset is preserved. |
| /// |
| /// Layout (BOF -> EOF): |
| /// ```text |
| /// <payload> |
| /// <manifest_id_table> |
| /// <string_table> |
| /// <manifestIdTableOffset:u32> |
| /// <stringTableOffset:u32> |
| /// ``` |
| /// |
| /// This is the counterpart to [BinaryWriter.writeTableTrailer]. |
| void initFromTableTrailer() { |
| runAtOffset(bytes.length - 4 * 2, () { |
| var manifestIdTableOffset = readUint32(); |
| var stringTableOffset = readUint32(); |
| _initManifestIdTableAt(manifestIdTableOffset); |
| initStringTableAt(stringTableOffset); |
| }); |
| } |
| |
| void initStringTableAt(int offset) { |
| _stringTable = StringTable(bytes: bytes, startOffset: offset); |
| } |
| |
| @pragma("vm:prefer-inline") |
| bool readBool() { |
| return readByte() != 0; |
| } |
| |
| @pragma("vm:prefer-inline") |
| int readByte() { |
| return bytes[offset++]; |
| } |
| |
| double readDouble() { |
| _doubleBufferUint8[0] = readByte(); |
| _doubleBufferUint8[1] = readByte(); |
| _doubleBufferUint8[2] = readByte(); |
| _doubleBufferUint8[3] = readByte(); |
| _doubleBufferUint8[4] = readByte(); |
| _doubleBufferUint8[5] = readByte(); |
| _doubleBufferUint8[6] = readByte(); |
| _doubleBufferUint8[7] = readByte(); |
| return _doubleBuffer[0]; |
| } |
| |
| T readEnum<T extends Enum>(List<T> values) { |
| var index = readByte(); |
| return values[index]; |
| } |
| |
| int readInt64() { |
| _int64BufferUint8[0] = readByte(); |
| _int64BufferUint8[1] = readByte(); |
| _int64BufferUint8[2] = readByte(); |
| _int64BufferUint8[3] = readByte(); |
| _int64BufferUint8[4] = readByte(); |
| _int64BufferUint8[5] = readByte(); |
| _int64BufferUint8[6] = readByte(); |
| _int64BufferUint8[7] = readByte(); |
| return _int64Buffer[0]; |
| } |
| |
| ManifestItemId readManifestItemId() { |
| var table = _manifestIdTable; |
| if (table == null) { |
| throw StateError('Manifest ID table not initialized.'); |
| } |
| var index = readUint30(); |
| return table[index]; |
| } |
| |
| Map<K, V> readMap<K, V>({ |
| required K Function() readKey, |
| required V Function() readValue, |
| }) { |
| var length = readUint30(); |
| if (length == 0) { |
| return const {}; |
| } |
| |
| return {for (var i = 0; i < length; i++) readKey(): readValue()}; |
| } |
| |
| int? readOptionalInt64() { |
| if (readBool()) { |
| return readInt64(); |
| } else { |
| return null; |
| } |
| } |
| |
| T? readOptionalObject<T>(T Function() read) { |
| if (readBool()) { |
| return read(); |
| } else { |
| return null; |
| } |
| } |
| |
| String? readOptionalStringReference() { |
| if (readBool()) { |
| return readStringReference(); |
| } else { |
| return null; |
| } |
| } |
| |
| String? readOptionalStringUtf8() { |
| if (readBool()) { |
| return readStringUtf8(); |
| } else { |
| return null; |
| } |
| } |
| |
| int? readOptionalUint30() { |
| if (readBool()) { |
| return readUint30(); |
| } else { |
| return null; |
| } |
| } |
| |
| Uint8List? readOptionalUint8List() { |
| if (readBool()) { |
| return readUint8List(); |
| } else { |
| return null; |
| } |
| } |
| |
| List<Uri>? readOptionalUriList() { |
| if (readBool()) { |
| return readUriList(); |
| } else { |
| return null; |
| } |
| } |
| |
| String readStringReference() { |
| var index = readUint30(); |
| return stringOfIndex(index); |
| } |
| |
| List<String> readStringReferenceList() { |
| return readTypedList(readStringReference); |
| } |
| |
| String readStringUtf8() { |
| var bytes = readUint8List(); |
| return considerCanonicalizeString(utf8.decode(bytes)); |
| } |
| |
| List<String> readStringUtf8List() { |
| return readTypedList(readStringUtf8); |
| } |
| |
| Set<String> readStringUtf8Set() { |
| var length = readUint30(); |
| var result = <String>{}; |
| for (var i = 0; i < length; i++) { |
| var item = readStringUtf8(); |
| result.add(item); |
| } |
| return result; |
| } |
| |
| List<T> readTypedList<T>(T Function() read) { |
| var length = readUint30(); |
| if (length == 0) { |
| return const <Never>[]; |
| } |
| return List<T>.generate(length, (_) { |
| return read(); |
| }, growable: false); |
| } |
| |
| List<T> readTypedListCast<T>(Object? Function() read) { |
| var length = readUint30(); |
| if (length == 0) { |
| return const <Never>[]; |
| } |
| return List<T>.generate(length, (_) { |
| return read() as T; |
| }, growable: false); |
| } |
| |
| int readUint30() { |
| var byte = readByte(); |
| if (byte & 0x80 == 0) { |
| // 0xxxxxxx |
| return byte; |
| } else if (byte & 0x40 == 0) { |
| // 10xxxxxx |
| return ((byte & 0x3F) << 8) | readByte(); |
| } else { |
| // 11xxxxxx |
| return ((byte & 0x3F) << 24) | |
| (readByte() << 16) | |
| (readByte() << 8) | |
| readByte(); |
| } |
| } |
| |
| Uint32List readUint30List() { |
| var length = readUint30(); |
| var result = Uint32List(length); |
| for (var i = 0; i < length; ++i) { |
| result[i] = readUint30(); |
| } |
| return result; |
| } |
| |
| int readUint32() { |
| return (readByte() << 24) | |
| (readByte() << 16) | |
| (readByte() << 8) | |
| readByte(); |
| } |
| |
| Uint32List readUint32List() { |
| var length = readUint32(); |
| var result = Uint32List(length); |
| for (var i = 0; i < length; ++i) { |
| result[i] = readUint32(); |
| } |
| return result; |
| } |
| |
| Uint8List readUint8List() { |
| var length = readUint30(); |
| var result = Uint8List.sublistView(bytes, offset, offset + length); |
| offset += length; |
| return result; |
| } |
| |
| Uri readUri() { |
| var uriStr = readStringReference(); |
| return uriCache.parse(uriStr); |
| } |
| |
| List<Uri> readUriList() { |
| return readTypedList(readUri); |
| } |
| |
| /// Temporary move to [offset] and run [operation]. |
| void runAtOffset(int offset, void Function() operation) { |
| var oldOffset = this.offset; |
| this.offset = offset; |
| try { |
| operation(); |
| } finally { |
| this.offset = oldOffset; |
| } |
| } |
| |
| String stringOfIndex(int index) { |
| var table = _stringTable; |
| if (table == null) { |
| throw StateError('String table not initialized.'); |
| } |
| return table[index]; |
| } |
| |
| void _initManifestIdTableAt(int offset) { |
| runAtOffset(offset, () { |
| _manifestIdTable = ManifestIdTableBuilder.readTable(this); |
| }); |
| } |
| } |