blob: 007b6e16247ae7d1f3e8fd362f10db448ccb5266 [file] [log] [blame]
// Copyright (c) 2022, 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:collection';
import 'dart:convert';
import 'dart:typed_data';
abstract class Serializer {
void writeByte(int byte);
void writeBytes(List<int> bytes);
void writeSigned(int value);
void writeUnsigned(int value);
void writeF32(double value);
void writeF64(double value);
void writeName(String name);
void write(Serializable object);
void writeList(List<Serializable> objects);
void writeData(Serializer chunk, [List<int>? watchPoints]);
Uint8List get data;
}
abstract class Serializable {
void serialize(Serializer s);
}
mixin SerializerMixin implements Serializer {
static bool traceEnabled = false;
// The prefix of `_data` up to `_index` contains the data serialized so far.
Uint8List _data = Uint8List(24);
int _index = 0;
// Stack traces or other serializers attached to byte positions within the
// chunk of data produced by this serializer.
late final SplayTreeMap<int, Object> _traces = SplayTreeMap();
void _ensure(int size) {
// Ensure space for at least `size` additional bytes.
if (_data.length < _index + size) {
int newLength = _data.length * 2;
while (newLength < _index + size) newLength *= 2;
_data = Uint8List(newLength)..setRange(0, _data.length, _data);
}
}
void _debugTrace(Object data) {
_traces[_index] ??= data;
}
void writeByte(int byte) {
if (traceEnabled) _debugTrace(StackTrace.current);
assert(byte == byte & 0xFF);
_ensure(1);
_data[_index++] = byte;
}
void writeBytes(List<int> bytes) {
if (traceEnabled) _debugTrace(StackTrace.current);
_ensure(bytes.length);
_data.setRange(_index, _index += bytes.length, bytes);
}
void writeSigned(int value) {
while (value < -0x40 || value >= 0x40) {
writeByte((value & 0x7F) | 0x80);
value >>= 7;
}
writeByte(value & 0x7F);
}
void writeUnsigned(int value) {
assert(value >= 0);
while (value >= 0x80) {
writeByte((value & 0x7F) | 0x80);
value >>= 7;
}
writeByte(value);
}
void writeF32(double value) {
// Get the binary representation of the F32.
List<int> bytes = Float32List.fromList([value]).buffer.asUint8List();
assert(bytes.length == 4);
if (Endian.host == Endian.big) bytes = bytes.reversed.toList();
writeBytes(bytes);
}
void writeF64(double value) {
// Get the binary representation of the F64.
List<int> bytes = Float64List.fromList([value]).buffer.asUint8List();
assert(bytes.length == 8);
if (Endian.host == Endian.big) bytes = bytes.reversed.toList();
writeBytes(bytes);
}
void writeName(String name) {
List<int> bytes = utf8.encode(name);
writeUnsigned(bytes.length);
writeBytes(bytes);
}
void write(Serializable object) {
object.serialize(this);
}
void writeList(List<Serializable> objects) {
writeUnsigned(objects.length);
for (int i = 0; i < objects.length; i++) write(objects[i]);
}
void writeData(Serializer chunk, [List<int>? watchPoints]) {
if (traceEnabled) _debugTrace(chunk);
if (watchPoints != null) {
for (int watchPoint in watchPoints) {
if (_index <= watchPoint && watchPoint < _index + chunk.data.length) {
int byteValue = chunk.data[watchPoint - _index];
Object trace = this;
int offset = watchPoint;
while (trace is SerializerMixin) {
int keyOffset = trace._traces.containsKey(offset)
? offset
: trace._traces.lastKeyBefore(offset)!;
trace = trace._traces[keyOffset]!;
offset -= keyOffset;
}
String byte = byteValue.toRadixString(16).padLeft(2, '0');
print("Watch $watchPoint: 0x$byte\n$trace");
}
}
}
writeBytes(chunk.data);
}
Uint8List get data => Uint8List.sublistView(_data, 0, _index);
}