import 'package:kdbx/src/internal/byte_utils.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; final _logger = Logger('kdbx_var_dictionary'); typedef Decoder = T Function(ReaderHelper reader, int length); typedef Encoder = void Function(WriterHelper writer, T value); extension on WriterHelper { LengthWriter _lengthWriter() => (int length) => writeInt32(length); } @immutable class ValueType { const ValueType(this.code, this.decoder, [this.encoder]); final int code; final Decoder decoder; final Encoder encoder; static final typeUInt32 = ValueType( 0x04, (reader, _) => reader.readUint32(), (writer, value) => writer.writeUint32(value, writer._lengthWriter()), ); static final typeUInt64 = ValueType( 0x05, (reader, _) => reader.readUint64(), (writer, value) => writer.writeUint64(value, writer._lengthWriter()), ); static final typeBool = ValueType( 0x08, (reader, _) => reader.readUint8() != 0, (writer, value) => writer.writeUint8(value ? 1 : 0, writer._lengthWriter()), ); static final typeInt32 = ValueType( 0x0C, (reader, _) => reader.readInt32(), (writer, value) => writer.writeInt32(value, writer._lengthWriter()), ); static final typeInt64 = ValueType( 0x0D, (reader, _) => reader.readInt64(), (writer, value) => writer.writeInt64(value, writer._lengthWriter()), ); static final typeString = ValueType( 0x18, (reader, length) => reader.readString(length), (writer, value) => writer.writeString(value, writer._lengthWriter()), ); static final typeBytes = ValueType( 0x42, (reader, length) => reader.readBytes(length), (writer, value) => writer.writeBytes(value, writer._lengthWriter()), ); static ValueType typeByCode(int code) => values.firstWhere((t) => t.code == code); static final values = [ typeUInt32, typeUInt64, typeBool, typeInt32, typeInt64, typeString, typeBytes, ]; } class VarDictionaryItem { VarDictionaryItem(this._key, this._valueType, this._value); final String _key; final ValueType _valueType; final T _value; String toDebugString() { return 'VarDictionaryItem{key=$_key, valueType=$_valueType, value=${_value.runtimeType}}'; } } class VarDictionary { VarDictionary(List> items) : assert(items != null), _items = items, _dict = Map.fromEntries(items.map((item) => MapEntry(item._key, item))); factory VarDictionary.read(ReaderHelper reader) { final items = []; final versionMinor = reader.readUint8(); final versionMajor = reader.readUint8(); _logger.finest('Reading VarDictionary $versionMajor.$versionMinor'); assert(versionMajor == 1); while (true) { final item = _readItem(reader); if (item == null) { break; } items.add(item); } return VarDictionary(items); } final List> _items; final Map> _dict; T get(ValueType type, String key) => _dict[key]?._value as T; static VarDictionaryItem _readItem(ReaderHelper reader) { final type = reader.readUint8(); if (type == 0) { return null; } final keyLength = reader.readUint32(); final key = reader.readString(keyLength); final valueLength = reader.readInt32(); final valueType = ValueType.typeByCode(type); return VarDictionaryItem( key, valueType, valueType.decoder(reader, valueLength)); } String toDebugString() { return 'VarDictionary{${_items.map((item) => item.toDebugString())}'; } }