| // 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); |
| } |