aboutsummaryrefslogtreecommitdiff
path: root/source/import/Aegis.mc
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-21 03:55:01 +0200
committercirakg <cirakg@cirakg.xyz>2026-10-21 03:55:01 +0200
commita69e23fb5266dd942d125239e7dcbe8a7c8ab956 (patch)
tree164c7ed21b550ac3d5fef567060ffad4e40e4583 /source/import/Aegis.mc
parent6039571d70b1f6de1ae384e58e11879869be356c (diff)
perf(import): keep parsed items packed and the pasted text once
A parsed import item with its Entry took about 760 bytes as objects on the Forerunner 255S; packed into one Codec ByteArray it takes about 110, and the importer unpacks one item per store step (wiping the packed secret). Aegis group names are resolved when unpacking, so no step repacks all items. The JSON is parsed from the untrimmed text's bytes and the string is dropped; an incomplete part is saved from those bytes. The form hash is computed before the importer copies the text. The peak of a 100-entry import on the 255S falls from 56 % to 45 %. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'source/import/Aegis.mc')
-rw-r--r--source/import/Aegis.mc44
1 files changed, 25 insertions, 19 deletions
diff --git a/source/import/Aegis.mc b/source/import/Aegis.mc
index 05905e9..0d515cd 100644
--- a/source/import/Aegis.mc
+++ b/source/import/Aegis.mc
@@ -4,7 +4,8 @@
// README 11.3. The reader consumes the token stream of Json.Tokenizer and
// tracks the path of the current value ("/db/entries/[]/info/secret"), so it
// can stop after any token and continue in the next event. It keeps only
-// the parsed items and the group names, never the document.
+// the parsed items (packed, see ImportItem) and the group names; the
+// document's bytes stay with the tokenizer.
//
// * Encrypted exports are refused: header.slots is not null, or db is a
// string.
@@ -34,7 +35,10 @@ class AegisReader {
static const ENCRYPTED_MESSAGE = "This Aegis export is encrypted. In Aegis choose Export, then turn encryption off, and paste that file.";
- var items as Array<ImportItem> = [] as Array<ImportItem>;
+ // Parsed items, packed (ImportItem.pack); unpack with item(i).
+ var items as Array<ByteArray> = [] as Array<ByteArray>;
+ // Group UUID -> name, from db.groups.
+ var groups as Dictionary<String, String> = {} as Dictionary<String, String>;
var error as String? = null;
private var _tok as Json.Tokenizer;
@@ -44,7 +48,6 @@ class AegisReader {
private var _fields as ImportFields? = null;
private var _seen as Dictionary<String, Boolean> = {} as Dictionary<String, Boolean>;
private var _group as Dictionary<String, String>? = null;
- private var _groups as Dictionary<String, String> = {} as Dictionary<String, String>;
private var _encrypted as Boolean = false;
private var _vaultVersion as Long = -1l;
private var _sawEntries as Boolean = false;
@@ -53,11 +56,25 @@ class AegisReader {
_tok = new Json.Tokenizer(data);
}
- // Bytes read so far (for progress).
+ // Bytes read so far, and in all (for progress).
function position() as Number {
return _tok.pos;
}
+ function size() as Number {
+ return _tok.size();
+ }
+
+ // The document's bytes (saved when it is incomplete).
+ function data() as ByteArray {
+ return _tok.data();
+ }
+
+ // Parsed item i, unpacked, with its group name resolved.
+ function item(i as Number) as ImportItem {
+ return ImportItem.unpack(items[i], groups);
+ }
+
// Process up to TOKENS_PER_STEP tokens. Returns RUNNING, DONE,
// INCOMPLETE (the text ends early: wait for more) or FAILED (error set).
function step() as Number {
@@ -139,14 +156,14 @@ class AegisReader {
item.label = fields.issuer.length() > 0 ? fields.issuer : fields.account;
}
fields.secret = null;
- items.add(item);
+ items.add(item.pack());
}
_item = null;
_fields = null;
} else if (p.equals("/db/groups/[]")) {
var g = _group;
if (g != null && g.hasKey("uuid") && g.hasKey("name")) {
- _groups[g["uuid"] as String] = g["name"] as String;
+ groups[g["uuid"] as String] = g["name"] as String;
}
_group = null;
}
@@ -244,19 +261,8 @@ class AegisReader {
return fail(_vaultVersion > 1 ? "Aegis export version " + _vaultVersion + " is not supported"
: "not an Aegis export (no version 1 vault with db.entries)");
}
- // Resolve group references now that db.groups has been read.
- for (var i = 0; i < items.size(); i++) {
- var item = items[i];
- var ref = item.groupRef;
- var e = item.entry;
- if (ref == null || e == null) {
- continue;
- }
- var name = ref.find("uuid:") == 0 ? _groups[ref.substring(5, ref.length()) as String] : ref.substring(5, ref.length());
- if (name != null) {
- e.group = fitBytes(Text.trim(name as String), Entry.MAX_GROUP_BYTES);
- }
- }
+ // Group references are resolved when items are unpacked (db.groups
+ // may come after the entries).
return DONE;
}