From 91c455d0773d6dc93fe2ec90465add7b94ab7bb0 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 10 Jul 2019 19:14:55 +0200 Subject: [PATCH] Fix (and then, use) the binary encoding to persist data --- moor/example_web/lib/database/database.g.dart | 29 +++++++++++++++ moor/lib/moor_web.dart | 3 +- .../lib/src/web/binary_string_conversion.dart | 37 +++++++++++-------- moor/lib/src/web/web_db.dart | 6 +-- moor/test/web/binary_string_test.dart | 18 +++++++++ 5 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 moor/test/web/binary_string_test.dart diff --git a/moor/example_web/lib/database/database.g.dart b/moor/example_web/lib/database/database.g.dart index 09d4dbd2..990db39a 100644 --- a/moor/example_web/lib/database/database.g.dart +++ b/moor/example_web/lib/database/database.g.dart @@ -187,10 +187,39 @@ class $TodoEntriesTable extends TodoEntries } } +class HiddenEntryCountResult { + final int entries; + HiddenEntryCountResult({ + this.entries, + }); +} + abstract class _$Database extends GeneratedDatabase { _$Database(QueryExecutor e) : super(const SqlTypeSystem.withDefaults(), e); $TodoEntriesTable _todoEntries; $TodoEntriesTable get todoEntries => _todoEntries ??= $TodoEntriesTable(this); + HiddenEntryCountResult _rowToHiddenEntryCountResult(QueryRow row) { + return HiddenEntryCountResult( + entries: row.readInt('entries'), + ); + } + + Future> hiddenEntryCount( + {@Deprecated('No longer needed with Moor 1.6 - see the changelog for details') + QueryEngine operateOn}) { + return (operateOn ?? this).customSelect( + 'SELECT COUNT(*) - 20 AS entries FROM todo_entries WHERE done', + variables: []).then((rows) => rows.map(_rowToHiddenEntryCountResult).toList()); + } + + Stream> watchHiddenEntryCount() { + return customSelectStream( + 'SELECT COUNT(*) - 20 AS entries FROM todo_entries WHERE done', + variables: [], + readsFrom: {todoEntries}) + .map((rows) => rows.map(_rowToHiddenEntryCountResult).toList()); + } + @override List get allTables => [todoEntries]; } diff --git a/moor/lib/moor_web.dart b/moor/lib/moor_web.dart index 83669eed..7e1912f4 100644 --- a/moor/lib/moor_web.dart +++ b/moor/lib/moor_web.dart @@ -5,7 +5,6 @@ library moor_web; import 'dart:async'; -import 'dart:convert'; import 'dart:html'; import 'package:meta/meta.dart'; @@ -13,9 +12,9 @@ import 'package:meta/dart2js.dart'; import 'package:synchronized/synchronized.dart'; import 'moor.dart'; +import 'src/web/binary_string_conversion.dart'; import 'src/web/sql_js.dart'; export 'moor.dart'; -part 'src/web/binary_string_conversion.dart'; part 'src/web/web_db.dart'; diff --git a/moor/lib/src/web/binary_string_conversion.dart b/moor/lib/src/web/binary_string_conversion.dart index 4c24413a..ac9f2c5e 100644 --- a/moor/lib/src/web/binary_string_conversion.dart +++ b/moor/lib/src/web/binary_string_conversion.dart @@ -1,18 +1,19 @@ -part of 'package:moor/moor_web.dart'; -/* -const _bin2str = _BinaryStringConversion(); +import 'dart:convert'; +import 'dart:math' as math; +import 'dart:typed_data'; -class _BinaryStringConversion extends Encoding { +/// Converts [Uint8List]s to binary strings. Used internally by moor to store +/// a database inside `window.localStorage`. +const bin2str = _BinaryStringConversion(); + +class _BinaryStringConversion extends Codec { const _BinaryStringConversion(); @override - Converter, String> get decoder => const _Bin2String(); + Converter get decoder => const _String2Bin(); @override - Converter> get encoder => const _String2Bin(); - - @override - String get name => 'bin'; + Converter get encoder => const _Bin2String(); } class _String2Bin extends Converter { @@ -24,22 +25,28 @@ class _String2Bin extends Converter { final list = Uint8List(codeUnits.length); for (var i = 0; i < codeUnits.length; i++) { - list[i] = i; + list[i] = codeUnits[i]; } return list; } } -class _Bin2String extends Converter, String> { +class _Bin2String extends Converter { const _Bin2String(); + // There is a browser limit on the amount of chars one can give to + // String.fromCharCodes https://github.com/kripken/sql.js/wiki/Persisting-a-Modified-Database#save-a-database-to-a-string + final int _chunkSize = 0xffff; + @override - String convert(List input) { + String convert(Uint8List input) { final buffer = StringBuffer(); - for (var byte in input) { - buffer.writeCharCode(byte); + + for (var pos = 0; pos < input.length; pos += _chunkSize) { + final endPos = math.min(pos + _chunkSize, input.length); + buffer.write(String.fromCharCodes(input.sublist(pos, endPos))); } + return buffer.toString(); } } -*/ diff --git a/moor/lib/src/web/web_db.dart b/moor/lib/src/web/web_db.dart index b82154d6..fd72ba53 100644 --- a/moor/lib/src/web/web_db.dart +++ b/moor/lib/src/web/web_db.dart @@ -33,19 +33,17 @@ abstract class _DatabaseUser extends QueryExecutor { return await lock.synchronized(computation); } - // todo base64 works, but is very slow. Figure out why bin2str is broken - Uint8List _restoreDb() { final raw = window.localStorage[_persistenceKey]; if (raw != null) { - return base64.decode(raw); + return bin2str.decode(raw); } return null; } void _storeDb() { final data = _db.export(); - final binStr = base64.encode(data); + final binStr = bin2str.encode(data); window.localStorage[_persistenceKey] = binStr; } diff --git a/moor/test/web/binary_string_test.dart b/moor/test/web/binary_string_test.dart new file mode 100644 index 00000000..9dc9d320 --- /dev/null +++ b/moor/test/web/binary_string_test.dart @@ -0,0 +1,18 @@ +import 'dart:typed_data'; + +import 'package:moor/src/web/binary_string_conversion.dart'; +import 'package:test_api/test_api.dart'; + +void main() { + final data = Uint8List(256 * 2); + for (var i = 0; i < 256; i++) { + data[i] = i % 256; + } + + test('converts binary data from and to strings', () { + final asStr = bin2str.encode(data); + final backToBin = bin2str.decode(asStr); + + expect(backToBin, data); + }); +}