diff options
Diffstat (limited to 'source/import/Importer.mc')
| -rw-r--r-- | source/import/Importer.mc | 40 |
1 files changed, 25 insertions, 15 deletions
diff --git a/source/import/Importer.mc b/source/import/Importer.mc index 9513358..825221b 100644 --- a/source/import/Importer.mc +++ b/source/import/Importer.mc @@ -12,6 +12,10 @@ // STORE one item per step: skip, report, or add to the vault. A crash // leaves only whole entries. // The report is complete when step() returns true. +// +// Memory: while JSON is parsed the pasted text is held once, as the +// tokenizer's bytes, and parsed items are kept packed (ImportItem.pack); +// the M8 numbers are in docs/PERFORMANCE.md. import Toybox.Lang; @@ -40,7 +44,8 @@ class Importer extends Jobs.Job { private var _text as String = ""; private var _uris as Array<String>? = null; private var _aegis as AegisReader? = null; - private var _items as Array<ImportItem> = [] as Array<ImportItem>; + private var _items as Array<ByteArray> = [] as Array<ByteArray>; + private var _groups as Dictionary<String, String> = {} as Dictionary<String, String>; private var _cursor as Number = 0; private var _ids as Array<String> = [] as Array<String>; private var _fingerprints as Dictionary<String, String> = {} as Dictionary<String, String>; @@ -57,7 +62,7 @@ class Importer extends Jobs.Job { _now = now; var keys = vault.keyManager.deviceKeys(); var earlier = ImportBuffer.load(vault.storage(), keys, now); - _text = earlier + text; + _text = earlier.length() > 0 ? earlier + text : text; detect(); } @@ -65,9 +70,8 @@ class Importer extends Jobs.Job { function percent() as Number { if (phase == P_PARSE) { var a = _aegis; - var size = _text.length() > 0 ? _text.length() : 1; if (a != null) { - return 40 * a.position() / (size > 0 ? size : 1); + return 40 * a.position() / (a.size() > 0 ? a.size() : 1); } var u = _uris; return u != null && u.size() > 0 ? 40 * _cursor / u.size() : 0; @@ -85,7 +89,10 @@ class Importer extends Jobs.Job { return; } if (trimmed.find("{") == 0) { - _aegis = new AegisReader(Bytes.fromUtf8(trimmed)); + // The untrimmed text: an incomplete part is saved exactly as + // received (a cut can fall on a space inside a string). + _aegis = new AegisReader(Bytes.fromUtf8(_text)); + _text = ""; return; } if (Text.findIgnoreCase(trimmed, OtpAuth.MIGRATION_PREFIX, 0) >= 0) { @@ -133,31 +140,32 @@ class Importer extends Jobs.Job { return; } if (status == AegisReader.INCOMPLETE) { - var size = Bytes.fromUtf8(_text).size(); - if (ImportBuffer.save(_vault.storage(), _vault.keyManager.deviceKeys(), _text, _now)) { - report.waitingChars = _text.length(); + var data = aegis.data(); + _aegis = null; + if (ImportBuffer.save(_vault.storage(), _vault.keyManager.deviceKeys(), data, _now)) { + var text = Bytes.toUtf8(data); + report.waitingChars = text != null ? text.length() : data.size(); phase = P_DONE; } else { - finishWith("the JSON is incomplete and too long to wait for more (" + size + " bytes, at most " + finishWith("the JSON is incomplete and too long to wait for more (" + data.size() + " bytes, at most " + ImportBuffer.MAX_TEXT_BYTES + ")"); } - _text = ""; return; } if (status == AegisReader.FAILED) { + _aegis = null; finishWith(aegis.error as String); - _text = ""; return; } _items = aegis.items; + _groups = aegis.groups; _aegis = null; - _text = ""; startReplace(); return; } var uris = _uris as Array<String>; for (var n = 0; n < URIS_PER_STEP && _cursor < uris.size(); n++) { - _items.add(OtpAuth.parse(uris[_cursor], _cursor + 1)); + _items.add(OtpAuth.parse(uris[_cursor], _cursor + 1).pack()); _cursor++; } if (_cursor >= uris.size()) { @@ -239,8 +247,10 @@ class Importer extends Jobs.Job { phase = P_DONE; return; } - var item = _items[_cursor]; - _items[_cursor] = new ImportItem(item.index); // drop the parsed secret + var packed = _items[_cursor]; + _items[_cursor] = []b; + var item = ImportItem.unpack(packed, _groups); + Bytes.wipe(packed); // the parsed secret; about 110 bytes _cursor++; var kind = _kind; var note = item.note; |
