Skip to content

Commit b141ae7

Browse files
authored
Merge pull request #49 from techouse/chore/optimizations
⚡ improve `encode` performance for deep object graphs
2 parents 1952f97 + 85c184d commit b141ae7

File tree

11 files changed

+716
-266
lines changed

11 files changed

+716
-266
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## 1.7.1-wip
2+
3+
* [FIX] restore robust cyclic detection when `filter` callbacks wrap values in fresh containers by tracking object identity before filter/date transformations
4+
* [FIX] improve deep path handling in encoder key materialization/dot-encoding via iterative `KeyPathNode` caching (avoids recursive overflow risk and reuses ancestor caches)
5+
* [CHORE] refactor encoder internals to share immutable frame config through new `EncodeConfig` and reduce per-frame option duplication
6+
* [CHORE] replace `weak_map` usage in encode cycle tracking with identity-based `Set<Object>` side-channel and remove `weak_map` dependency
7+
* [CHORE] expand encoder regression coverage with new tests for filter-wrapped cycles, `KeyPathNode` caching/encoding edge cases, and `EncodeConfig.copyWith` sentinel behavior
8+
* [CHORE] refine decode internals with clearer duplicate-handling branching and a small dot-decoding fast-path guard (`cleanRoot.contains('%2')`)
9+
110
## 1.7.0
211

312
* [FEAT] add `DecodeOptions.throwOnLimitExceeded` for strict limit enforcement on parameter, list, and depth overflows

lib/src/extensions/decode.dart

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,19 @@ extension _$Decode on QS {
191191

192192
// Duplicate key policy: combine/first/last (default: combine).
193193
final bool existing = obj.containsKey(key);
194-
if (existing && options.duplicates == Duplicates.combine) {
195-
obj[key] = Utils.combine(obj[key], val, listLimit: options.listLimit);
196-
} else if (!existing || options.duplicates == Duplicates.last) {
197-
obj[key] = val;
194+
switch ((existing, options.duplicates)) {
195+
case (true, Duplicates.combine):
196+
// Existing key + `combine` policy: merge old/new values.
197+
obj[key] = Utils.combine(obj[key], val, listLimit: options.listLimit);
198+
break;
199+
case (false, _):
200+
case (true, Duplicates.last):
201+
// New key, or `last` policy: store the current value.
202+
obj[key] = val;
203+
break;
204+
case (true, Duplicates.first):
205+
// Existing key + `first` policy: keep the original value.
206+
break;
198207
}
199208
}
200209

@@ -285,7 +294,7 @@ extension _$Decode on QS {
285294
final bool wasBracketed = root.startsWith('[') && root.endsWith(']');
286295
final String cleanRoot =
287296
wasBracketed ? root.slice(1, root.length - 1) : root;
288-
String decodedRoot = options.decodeDotInKeys
297+
String decodedRoot = options.decodeDotInKeys && cleanRoot.contains('%2')
289298
? cleanRoot.replaceAll('%2E', '.').replaceAll('%2e', '.')
290299
: cleanRoot;
291300

@@ -301,8 +310,8 @@ extension _$Decode on QS {
301310
int opens = 0, closes = 0;
302311
for (int k = 0; k < decodedRoot.length; k++) {
303312
final cu = decodedRoot.codeUnitAt(k);
304-
if (cu == 0x5B) opens++;
305-
if (cu == 0x5D) closes++;
313+
if (cu == 0x5B) opens++; // '['
314+
if (cu == 0x5D) closes++; // ']'
306315
}
307316
if (opens > closes) {
308317
decodedRoot = decodedRoot.substring(0, decodedRoot.length - 1);
@@ -404,9 +413,9 @@ extension _$Decode on QS {
404413
// Balance nested '[' and ']' within this group.
405414
while (i < n) {
406415
final int cu = key.codeUnitAt(i);
407-
if (cu == 0x5B) {
416+
if (cu == 0x5B /* '[' */) {
408417
level++;
409-
} else if (cu == 0x5D) {
418+
} else if (cu == 0x5D /* ']' */) {
410419
level--;
411420
if (level == 0) {
412421
close = i;

0 commit comments

Comments
 (0)