summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoradisbladis <adisbladis@gmail.com>2023-11-24 16:10:13 +1300
committergithub-actions[bot] <github-actions[bot]@users.noreply.github.com>2023-11-27 13:45:05 +0000
commit836ef86948668e7a18c8831ed436495e42ae5350 (patch)
treedba673e5fcbdb5ea88f8956598e7b312b356bbce
parentMerge pull request #270276 from NixOS/backport-257337-to-release-23.11 (diff)
downloadnixpkgs-origin/backport-269552-to-release-23.11.tar.gz
lib.attrsets.matchAttrs: Avoid some list allocations when walking structureorigin/backport-269552-to-release-23.11
Benchmarks (`nix-instantiate ./. -A python3`): - Before: ``` json { "cpuTime": 0.29049500823020935, "envs": { "bytes": 4484216, "elements": 221443, "number": 169542 }, "gc": { "heapSize": 402915328, "totalBytes": 53086800 }, "list": { "bytes": 749424, "concats": 4242, "elements": 93678 }, "nrAvoided": 253991, "nrFunctionCalls": 149848, "nrLookups": 49612, "nrOpUpdateValuesCopied": 1587837, "nrOpUpdates": 10104, "nrPrimOpCalls": 130356, "nrThunks": 358981, "sets": { "bytes": 30423600, "elements": 1859999, "number": 41476 }, "sizes": { "Attr": 16, "Bindings": 16, "Env": 16, "Value": 24 }, "symbols": { "bytes": 236145, "number": 24453 }, "values": { "bytes": 10502520, "number": 437605 } } ``` - After: ``` json { "cpuTime": 0.2946169972419739, "envs": { "bytes": 3315224, "elements": 172735, "number": 120834 }, "gc": { "heapSize": 402915328, "totalBytes": 48718432 }, "list": { "bytes": 347568, "concats": 4242, "elements": 43446 }, "nrAvoided": 173252, "nrFunctionCalls": 101140, "nrLookups": 73595, "nrOpUpdateValuesCopied": 1587837, "nrOpUpdates": 10104, "nrPrimOpCalls": 83067, "nrThunks": 304216, "sets": { "bytes": 29704096, "elements": 1831673, "number": 24833 }, "sizes": { "Attr": 16, "Bindings": 16, "Env": 16, "Value": 24 }, "symbols": { "bytes": 236145, "number": 24453 }, "values": { "bytes": 8961552, "number": 373398 } } ``` (cherry picked from commit 013a0a1357c446d0a46b4bbd8f68512fd9223257)
-rw-r--r--lib/attrsets.nix29
-rw-r--r--lib/tests/misc.nix20
2 files changed, 40 insertions, 9 deletions
diff --git a/lib/attrsets.nix b/lib/attrsets.nix
index bf6c90bf1be6..14ce9c257731 100644
--- a/lib/attrsets.nix
+++ b/lib/attrsets.nix
@@ -883,7 +883,10 @@ rec {
recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs)) lhs rhs;
- /* Returns true if the pattern is contained in the set. False otherwise.
+ /*
+ Recurse into every attribute set of the first argument and check that:
+ - Each attribute path also exists in the second argument.
+ - If the attribute's value is not a nested attribute set, it must have the same value in the right argument.
Example:
matchAttrs { cpu = {}; } { cpu = { bits = 64; }; }
@@ -895,16 +898,24 @@ rec {
matchAttrs =
# Attribute set structure to match
pattern:
- # Attribute set to find patterns in
+ # Attribute set to check
attrs:
assert isAttrs pattern;
- all id (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
- let pat = head values; val = elemAt values 1; in
- if length values == 1 then false
- else if isAttrs pat then isAttrs val && matchAttrs pat val
- else pat == val
- ) [pattern attrs]));
-
+ all
+ ( # Compare equality between `pattern` & `attrs`.
+ attr:
+ # Missing attr, not equal.
+ attrs ? ${attr} && (
+ let
+ lhs = pattern.${attr};
+ rhs = attrs.${attr};
+ in
+ # If attrset check recursively
+ if isAttrs lhs then isAttrs rhs && matchAttrs lhs rhs
+ else lhs == rhs
+ )
+ )
+ (attrNames pattern);
/* Override only the attributes that are already present in the old set
useful for deep-overriding.
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index 8f4a37149d92..9f1fee2ba234 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -831,6 +831,26 @@ runTests {
};
};
+ testMatchAttrsMatchingExact = {
+ expr = matchAttrs { cpu = { bits = 64; }; } { cpu = { bits = 64; }; };
+ expected = true;
+ };
+
+ testMatchAttrsMismatch = {
+ expr = matchAttrs { cpu = { bits = 128; }; } { cpu = { bits = 64; }; };
+ expected = false;
+ };
+
+ testMatchAttrsMatchingImplicit = {
+ expr = matchAttrs { cpu = { }; } { cpu = { bits = 64; }; };
+ expected = true;
+ };
+
+ testMatchAttrsMissingAttrs = {
+ expr = matchAttrs { cpu = {}; } { };
+ expected = false;
+ };
+
testOverrideExistingEmpty = {
expr = overrideExisting {} { a = 1; };
expected = {};