blob: ec546ef414aaad31ead6fe3d3f0dd8937570f793 [file] [log] [blame]
import 'dart:convert';
import 'dart:typed_data';
import 'byte_order.dart';
abstract class InputStreamBase {
/// The current read position relative to the start of the buffer.
int get position;
set position(int v);
/// How many bytes are left in the stream.
int get length;
/// Is the current position at the end of the stream?
bool get isEOS;
/// Asynchronously closes the input stream.
Future<void> close() async {}
/// Reset to the beginning of the stream.
void reset();
/// Rewind the read head of the stream by the given number of bytes.
void rewind([int length = 1]);
/// Move the read position by [count] bytes.
void skip(int length);
/// Read [count] bytes from an [offset] of the current read position, without
/// moving the read position.
InputStreamBase peekBytes(int count, [int offset = 0]);
/// Read a single byte.
int readByte();
/// Read [count] bytes from the stream.
InputStreamBase readBytes(int count);
InputStreamBase subset([int? position, int? length]);
/// Read a null-terminated string, or if [len] is provided, that number of
/// bytes returned as a string.
String readString({int? size, bool utf8 = true});
/// Read a 16-bit word from the stream.
int readUint16();
/// Read a 24-bit word from the stream.
int readUint24();
/// Read a 32-bit word from the stream.
int readUint32();
/// Read a 64-bit word form the stream.
int readUint64();
Uint8List toUint8List();
}
/// A buffer that can be read as a stream of bytes
class InputStream extends InputStreamBase {
List<int> buffer;
int offset;
int start;
int byteOrder;
/// Create a InputStream for reading from a List<int>
InputStream(dynamic data,
{this.byteOrder = LITTLE_ENDIAN, this.start = 0, int? length})
: buffer = data is TypedData
? Uint8List.view(
data.buffer, data.offsetInBytes, data.lengthInBytes)
: data is List<int>
? data
: List<int>.from(data as Iterable<dynamic>),
offset = start {
_length = length ?? buffer.length;
}
/// Create a copy of [other].
InputStream.from(InputStream other)
: buffer = other.buffer,
offset = other.offset,
start = other.start,
_length = other._length,
byteOrder = other.byteOrder;
/// The current read position relative to the start of the buffer.
@override
int get position => offset - start;
@override
set position(int v) { offset = start + v; }
/// How many bytes are left in the stream.
@override
int get length => _length - (offset - start);
/// Is the current position at the end of the stream?
@override
bool get isEOS => offset >= (start + _length);
/// Reset to the beginning of the stream.
@override
void reset() {
offset = start;
}
@override
Future<void> close() async {
buffer = <int>[];
_length = 0;
}
/// Rewind the read head of the stream by the given number of bytes.
@override
void rewind([int length = 1]) {
offset -= length;
if (offset < 0) {
offset = 0;
}
}
/// Access the buffer relative from the current position.
int operator [](int index) => buffer[offset + index];
/// Return a InputStream to read a subset of this stream. It does not
/// move the read position of this stream. [position] is specified relative
/// to the start of the buffer. If [position] is not specified, the current
/// read position is used. If [length] is not specified, the remainder of this
/// stream is used.
@override
InputStreamBase subset([int? position, int? length]) {
if (position == null) {
position = offset;
} else {
position += start;
}
if (length == null || length < 0) {
length = _length - (position - start);
}
return InputStream(buffer,
byteOrder: byteOrder, start: position, length: length);
}
/// Returns the position of the given [value] within the buffer, starting
/// from the current read position with the given [offset]. The position
/// returned is relative to the start of the buffer, or -1 if the [value]
/// was not found.
int indexOf(int value, [int offset = 0]) {
for (var i = this.offset + offset, end = this.offset + length;
i < end;
++i) {
if (buffer[i] == value) {
return i - start;
}
}
return -1;
}
/// Read [count] bytes from an [offset] of the current read position, without
/// moving the read position.
@override
InputStreamBase peekBytes(int count, [int offset = 0]) {
return subset((this.offset - start) + offset, count);
}
/// Move the read position by [count] bytes.
@override
void skip(int count) {
offset += count;
}
/// Read a single byte.
@override
int readByte() {
return buffer[offset++];
}
/// Read [count] bytes from the stream.
@override
InputStreamBase readBytes(int count) {
final bytes = subset(offset - start, count);
offset += bytes.length;
return bytes as InputStream;
}
/// Read a null-terminated string, or if [len] is provided, that number of
/// bytes returned as a string.
@override
String readString({int? size, bool utf8 = true}) {
if (size == null) {
final codes = <int>[];
if (isEOS) {
return '';
}
while (!isEOS) {
final c = readByte();
if (c == 0) {
break;
}
codes.add(c);
}
return utf8
? Utf8Decoder().convert(codes)
: String.fromCharCodes(codes);
}
final s = readBytes(size);
final bytes = s.toUint8List();
try {
final str =
utf8 ? Utf8Decoder().convert(bytes) : String.fromCharCodes(bytes);
return str;
} catch (err) {
// If the string is not a valid UTF8 string, decode it as character codes.
return String.fromCharCodes(bytes);
}
}
/// Read a 16-bit word from the stream.
@override
int readUint16() {
final b1 = buffer[offset++] & 0xff;
final b2 = buffer[offset++] & 0xff;
if (byteOrder == BIG_ENDIAN) {
return (b1 << 8) | b2;
}
return (b2 << 8) | b1;
}
/// Read a 24-bit word from the stream.
@override
int readUint24() {
final b1 = buffer[offset++] & 0xff;
final b2 = buffer[offset++] & 0xff;
final b3 = buffer[offset++] & 0xff;
if (byteOrder == BIG_ENDIAN) {
return b3 | (b2 << 8) | (b1 << 16);
}
return b1 | (b2 << 8) | (b3 << 16);
}
/// Read a 32-bit word from the stream.
@override
int readUint32() {
final b1 = buffer[offset++] & 0xff;
final b2 = buffer[offset++] & 0xff;
final b3 = buffer[offset++] & 0xff;
final b4 = buffer[offset++] & 0xff;
if (byteOrder == BIG_ENDIAN) {
return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
}
return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
}
/// Read a 64-bit word form the stream.
@override
int readUint64() {
final b1 = buffer[offset++] & 0xff;
final b2 = buffer[offset++] & 0xff;
final b3 = buffer[offset++] & 0xff;
final b4 = buffer[offset++] & 0xff;
final b5 = buffer[offset++] & 0xff;
final b6 = buffer[offset++] & 0xff;
final b7 = buffer[offset++] & 0xff;
final b8 = buffer[offset++] & 0xff;
if (byteOrder == BIG_ENDIAN) {
return (b1 << 56) |
(b2 << 48) |
(b3 << 40) |
(b4 << 32) |
(b5 << 24) |
(b6 << 16) |
(b7 << 8) |
b8;
}
return (b8 << 56) |
(b7 << 48) |
(b6 << 40) |
(b5 << 32) |
(b4 << 24) |
(b3 << 16) |
(b2 << 8) |
b1;
}
@override
Uint8List toUint8List() {
var len = length;
if (buffer is Uint8List) {
final b = buffer as Uint8List;
if ((offset + len) > b.length) {
len = b.length - offset;
}
final bytes = Uint8List.view(b.buffer, b.offsetInBytes + offset, len);
return bytes;
}
var end = offset + len;
if (end > buffer.length) {
end = buffer.length;
}
return Uint8List.fromList(buffer.sublist(offset, end));
}
late int _length;
}