37cfae173c
Bug: https://github.com/dart-lang/sdk/issues/43304 Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-nnbd-linux-release-simarm64-try,vm-kernel-precomp-nnbd-linux-release-x64-try Change-Id: Iaf451047ce74add6520bd514bafae8b0e3c5fa59 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/161704 Commit-Queue: Tess Strickland <sstrickl@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
200 lines
6.5 KiB
Dart
200 lines
6.5 KiB
Dart
// Copyright (c) 2019, 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:io';
|
|
import 'dart:typed_data';
|
|
import 'dart:math';
|
|
|
|
String paddedHex(int value, [int bytes = 0]) {
|
|
return value.toRadixString(16).padLeft(2 * bytes, '0');
|
|
}
|
|
|
|
class Reader {
|
|
final ByteData bdata;
|
|
// These are mutable so we can update them, in case the endianness and
|
|
// wordSize are read using the reader (e.g., ELF files).
|
|
Endian? _endian;
|
|
int? _wordSize;
|
|
|
|
int _offset = 0;
|
|
|
|
Endian get endian => _endian as Endian;
|
|
void set endian(Endian value) => _endian = value;
|
|
int get wordSize => _wordSize as int;
|
|
void set wordSize(int value) => _wordSize = value;
|
|
|
|
/// Unless provided, [wordSize] and [endian] are initialized to values that
|
|
/// ensure no reads are made that depend on their value (e.g., readBytes).
|
|
Reader.fromTypedData(TypedData data, {int? wordSize, Endian? endian})
|
|
: _wordSize = wordSize,
|
|
_endian = endian,
|
|
bdata =
|
|
ByteData.view(data.buffer, data.offsetInBytes, data.lengthInBytes);
|
|
|
|
Reader.fromFile(String path, {int? wordSize, Endian? endian})
|
|
: _wordSize = wordSize,
|
|
_endian = endian,
|
|
bdata = ByteData.sublistView(File(path).readAsBytesSync());
|
|
|
|
/// Returns a reader focused on a different portion of the underlying buffer.
|
|
Reader refocusedCopy(int pos, int size) {
|
|
assert(pos >= 0 && pos < bdata.buffer.lengthInBytes);
|
|
assert(size >= 0 && (pos + size) <= bdata.buffer.lengthInBytes);
|
|
return Reader.fromTypedData(ByteData.view(bdata.buffer, pos, size),
|
|
wordSize: _wordSize, endian: _endian);
|
|
}
|
|
|
|
int get start => bdata.offsetInBytes;
|
|
int get offset => _offset;
|
|
int get length => bdata.lengthInBytes;
|
|
bool get done => _offset >= length;
|
|
|
|
void seek(int offset, {bool absolute = false}) {
|
|
final newOffset = (absolute ? 0 : _offset) + offset;
|
|
assert(newOffset >= 0 && newOffset < bdata.lengthInBytes);
|
|
_offset = newOffset;
|
|
}
|
|
|
|
int readBytes(int size, {bool signed = false}) {
|
|
if (_offset + size > length) {
|
|
throw ArgumentError("attempt to read ${size} bytes with only "
|
|
"${length - _offset} bytes remaining in the reader");
|
|
}
|
|
final start = _offset;
|
|
_offset += size;
|
|
switch (size) {
|
|
case 1:
|
|
return signed ? bdata.getInt8(start) : bdata.getUint8(start);
|
|
case 2:
|
|
return signed
|
|
? bdata.getInt16(start, endian)
|
|
: bdata.getUint16(start, endian);
|
|
case 4:
|
|
return signed
|
|
? bdata.getInt32(start, endian)
|
|
: bdata.getUint32(start, endian);
|
|
case 8:
|
|
return signed
|
|
? bdata.getInt64(start, endian)
|
|
: bdata.getUint64(start, endian);
|
|
default:
|
|
_offset -= size;
|
|
throw ArgumentError("invalid request to read $size bytes");
|
|
}
|
|
}
|
|
|
|
int readByte({bool signed = false}) => readBytes(1, signed: signed);
|
|
int readWord() => readBytes(wordSize);
|
|
String readNullTerminatedString() {
|
|
final start = bdata.offsetInBytes + _offset;
|
|
for (int i = 0; _offset + i < bdata.lengthInBytes; i++) {
|
|
if (bdata.getUint8(_offset + i) == 0) {
|
|
_offset += i + 1;
|
|
return String.fromCharCodes(bdata.buffer.asUint8List(start, i));
|
|
}
|
|
}
|
|
return String.fromCharCodes(
|
|
bdata.buffer.asUint8List(start, bdata.lengthInBytes - _offset));
|
|
}
|
|
|
|
int readLEB128EncodedInteger({bool signed = false}) {
|
|
var ret = 0;
|
|
var shift = 0;
|
|
for (var byte = readByte(); !done; byte = readByte()) {
|
|
ret |= (byte & 0x7f) << shift;
|
|
shift += 7;
|
|
if (byte & 0x80 == 0) {
|
|
if (signed && byte & 0x40 != 0) {
|
|
ret |= -(1 << shift);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// Repeatedly calls [callback] with this reader to retrieve items.
|
|
///
|
|
/// The key of the returned [MapEntry]s are the offsets of the items. If
|
|
/// absolute is false, the offsets are from the reader position when
|
|
/// [readRepeated] was invoked, otherwise they are absolute offsets from
|
|
/// the start of the reader.
|
|
///
|
|
/// Stops either when the reader is empty or when a null item is returned
|
|
/// from the callback.
|
|
Iterable<MapEntry<int, S>> readRepeatedWithOffsets<S>(
|
|
S? Function(Reader) callback,
|
|
{bool absolute = false}) sync* {
|
|
final start = offset;
|
|
while (!done) {
|
|
final itemStart = offset;
|
|
final item = callback(this);
|
|
if (item == null) break;
|
|
yield MapEntry(absolute ? itemStart : itemStart - start, item);
|
|
}
|
|
}
|
|
|
|
Iterable<S> readRepeated<S>(S? Function(Reader) callback) =>
|
|
readRepeatedWithOffsets(callback).map((kv) => kv.value);
|
|
|
|
void writeCurrentReaderPosition(StringBuffer buffer,
|
|
{int maxSize = 0, int bytesPerLine = 16}) {
|
|
var baseData = ByteData.view(bdata.buffer, 0, bdata.buffer.lengthInBytes);
|
|
var startOffset = 0;
|
|
var endOffset = baseData.lengthInBytes;
|
|
final currentOffset = start + _offset;
|
|
if (maxSize != 0 && maxSize < baseData.lengthInBytes) {
|
|
var lowerWindow = currentOffset - (maxSize >> 1);
|
|
// Adjust so that we always start at the beginning of a line.
|
|
lowerWindow -= lowerWindow % bytesPerLine;
|
|
final upperWindow = lowerWindow + maxSize;
|
|
startOffset = max(startOffset, lowerWindow);
|
|
endOffset = min(endOffset, upperWindow);
|
|
}
|
|
for (int i = startOffset; i < endOffset; i += bytesPerLine) {
|
|
buffer..write("0x")..write(paddedHex(i, 8))..write(" ");
|
|
for (int j = 0; j < bytesPerLine && i + j < endOffset; j++) {
|
|
var byte = baseData.getUint8(i + j);
|
|
buffer
|
|
..write(i + j == currentOffset ? "|" : " ")
|
|
..write(paddedHex(byte, 1));
|
|
}
|
|
buffer.writeln();
|
|
}
|
|
}
|
|
|
|
String toString() {
|
|
final buffer = StringBuffer();
|
|
buffer
|
|
..write("Word size: ")
|
|
..write(_wordSize)
|
|
..writeln();
|
|
buffer
|
|
..write("Endianness: ")
|
|
..write(_endian)
|
|
..writeln();
|
|
buffer
|
|
..write("Start: 0x")
|
|
..write(paddedHex(start, _wordSize ?? 0))
|
|
..write(" (")
|
|
..write(start)
|
|
..writeln(")");
|
|
buffer
|
|
..write("Offset: 0x")
|
|
..write(paddedHex(offset, _wordSize ?? 0))
|
|
..write(" (")
|
|
..write(offset)
|
|
..writeln(")");
|
|
buffer
|
|
..write("Length: 0x")
|
|
..write(paddedHex(length, _wordSize ?? 0))
|
|
..write(" (")
|
|
..write(length)
|
|
..writeln(")");
|
|
buffer..writeln("Bytes around current position:");
|
|
writeCurrentReaderPosition(buffer, maxSize: 256);
|
|
return buffer.toString();
|
|
}
|
|
}
|