blob: 74def6a5faaeef201dcfd3c5def45902e5ec17bb [file] [log] [blame]
// Copyright 2019 The Flutter Authors. 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:io';
import 'dart:typed_data';
typedef Reader<T> = T Function();
typedef CustomReader<T> = T Function(FileReader reader);
typedef Writer<T> = void Function(T value);
typedef CustomWriter<T> = void Function(FileWriter reader, T value);
const int _typeNullable = 0x01;
const int _typeBool = 0x02;
const int _typeInt = 0x03;
const int _typeString = 0x04;
const int _typeDateTime = 0x05;
const int _typeSet = 0x10;
const int _typeMap = 0x11;
const int _typeCustom = 0xFE;
const int _typeEnd = 0xFF;
class FileReader {
FileReader(this._buffer) : _endianness = Endian.host;
final ByteData _buffer;
final Endian _endianness;
int _position = 0;
static Future<FileReader> open(File file) async {
final Uint8List bytes = await file.readAsBytes();
return FileReader(bytes.buffer.asByteData(bytes.offsetInBytes, bytes.length));
}
void _readType(int expected) {
final int type = _buffer.getUint8(_position);
_position += 1;
if (expected != type) {
throw FormatException('expected $expected but got $type at byte ${_position - 1}');
}
}
T? readNullOr<T>(Reader<T> reader) {
_readType(_typeNullable);
final int result = _buffer.getUint8(_position);
_position += 1;
if (result == 0) {
return null;
}
return reader();
}
bool readBool() {
_readType(_typeBool);
final int result = _buffer.getUint8(_position);
_position += 1;
return result != 0x00;
}
int readInt() {
_readType(_typeInt);
final int result = _buffer.getInt64(_position, _endianness);
_position += 8;
return result;
}
String readString() {
_readType(_typeString);
final int length = readInt();
final String result = utf8.decode(_buffer.buffer.asUint8List(_buffer.offsetInBytes + _position, length));
_position += length;
return result;
}
DateTime readDateTime() {
_readType(_typeDateTime);
return DateTime.fromMicrosecondsSinceEpoch(readInt(), isUtc: true);
}
Reader<Set<T>> readerForSet<T>(Reader<T> reader) {
return () {
_readType(_typeSet);
final int count = readInt();
final Set<T> result = <T>{};
for (int index = 0; index < count; index += 1) {
result.add(reader());
}
return result;
};
}
Set<T> readSet<T>(Reader<T> reader) {
return readerForSet<T>(reader)();
}
Reader<Map<K, V>> readerForMap<K, V>(Reader<K> keyReader, Reader<V> valueReader) {
return () {
_readType(_typeMap);
final int count = readInt();
final Map<K, V> result = <K, V>{};
for (int index = 0; index < count; index += 1) {
result[keyReader()] = valueReader();
}
return result;
};
}
Map<K, V> readMap<K, V>(Reader<K> keyReader, Reader<V> valueReader) {
return readerForMap<K, V>(keyReader, valueReader)();
}
Reader<T> readerForCustom<T>(CustomReader<T> reader) {
return () {
_readType(_typeCustom);
return reader(this);
};
}
void close() {
_readType(_typeEnd);
if (_position != _buffer.lengthInBytes) {
throw StateError('read failed; position=$_position, expected ${_buffer.lengthInBytes}');
}
}
}
class FileWriter {
FileWriter() : _endianness = Endian.host;
final BytesBuilder _buffer = BytesBuilder();
final Endian _endianness;
void _writeType(int type) {
_buffer.addByte(type);
}
void writeNullOr<T>(T? value, Writer<T> writer) {
_writeType(_typeNullable);
if (value == null) {
_buffer.addByte(0x00);
} else {
_buffer.addByte(0xFF);
writer(value);
}
}
void writeBool(bool value) { // ignore: avoid_positional_boolean_parameters
_writeType(_typeBool);
_buffer.addByte(value ? 0x01 : 0x00);
}
final ByteData _intBuffer = ByteData(8);
late final Uint8List _intBytes = _intBuffer.buffer.asUint8List();
void writeInt(int value) {
_writeType(_typeInt);
_intBuffer.setInt64(0, value, _endianness);
_buffer.add(_intBytes);
}
void writeString(String value) {
_writeType(_typeString);
final List<int> stringBuffer = utf8.encode(value);
writeInt(stringBuffer.length);
_buffer.add(stringBuffer);
}
void writeDateTime(DateTime value) {
_writeType(_typeDateTime);
writeInt(value.microsecondsSinceEpoch);
}
Writer<Set<T>> writerForSet<T>(Writer<T> writer) {
return (Set<T> value) {
_writeType(_typeSet);
writeInt(value.length);
value.forEach(writer);
};
}
void writeSet<T>(Writer<T> writer, Set<T> value) {
writerForSet<T>(writer)(value);
}
Writer<Map<K, V>> writerForMap<K, V>(Writer<K> keyWriter, Writer<V> valueWriter) {
return (Map<K, V> value) {
_writeType(_typeMap);
writeInt(value.length);
value.forEach((K key, V value) {
keyWriter(key);
valueWriter(value);
});
};
}
void writeMap<K, V>(Writer<K> keyWriter, Writer<V> valueWriter, Map<K, V> value) {
writerForMap<K, V>(keyWriter, valueWriter)(value);
}
Writer<T> writerForCustom<T>(CustomWriter<T> writer) {
return (T value) {
_writeType(_typeCustom);
writer(this, value);
};
}
Future<void> write(File file) async {
_writeType(_typeEnd);
final File temp = File('${file.path}.\$\$\$');
await temp.writeAsBytes(_buffer.takeBytes());
if (file.existsSync()) {
await file.delete();
}
await temp.rename(file.path);
}
ByteData serialize() {
_writeType(_typeEnd);
final int length = _buffer.length;
return _buffer.takeBytes().buffer.asByteData(0, length);
}
}