blob: a7da89af86e83d1b357386398d20492caaa5d2a8 [file] [log] [blame] [edit]
// 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);
});
}
}