diff options
Diffstat (limited to 'src/access/screen.rs')
| -rw-r--r-- | src/access/screen.rs | 37 |
1 files changed, 29 insertions, 8 deletions
diff --git a/src/access/screen.rs b/src/access/screen.rs index 793e8bb..3fbc7ab 100644 --- a/src/access/screen.rs +++ b/src/access/screen.rs @@ -386,6 +386,9 @@ pub(crate) fn apply( let mut unapplied: BTreeSet<Path> = BTreeSet::new(); // What the rules read at the heads judged at, kept while they stay the same. let mut reads = rules::Reads::default(); + // The values their users sign, under a strict policy, as the document holds them: + // read again once the document takes the accepted (`reaches_signed`). + let mut signed: Option<Option<Vec<Vec<String>>>> = None; for (index, change) in ready.iter().enumerate() { let hash = change.hash(); let committer = committers[index].as_deref(); @@ -453,6 +456,7 @@ pub(crate) fn apply( apply_taken(doc, std::mem::take(&mut accepted), log)?; frontier.clear(); unapplied.clear(); + signed = None; } let mut seen_at: BTreeSet<ChangeHash> = BTreeSet::new(); for dep in change.deps() { @@ -490,12 +494,16 @@ pub(crate) fn apply( screened.taken_from.insert(committer.to_owned()); } // A root too: whether another may be one is read in the document. - if root || relevant(&paths[index]) || reaches_signed(doc, &paths[index]) { + if root + || relevant(&paths[index]) + || reaches_signed(doc, &mut signed, &paths[index]) + { // What rules read changed: in before the next is judged. screened.applied += accepted.len(); apply_taken(doc, std::mem::take(&mut accepted), log)?; frontier.clear(); unapplied.clear(); + signed = None; } else { frontier.insert(hash, seen_at); unapplied.extend(paths[index].iter().cloned()); @@ -748,15 +756,28 @@ fn relevant(paths: &BTreeSet<Path>) -> bool { /// Whether a change touching `paths` changes values their users sign, under a strict /// policy, at, within or above one (`envelope::reaches_signed`): the rules read the /// value at a signed place (`prev`) and what holds one, so it goes in before the next -/// is judged. -fn reaches_signed(doc: &Document, paths: &BTreeSet<Path>) -> bool { - if !super::policy::of(doc).strict() { +/// is judged. Where they are, None if the policy isn't strict, is read into `signed` +/// once: a read costs more than the rest of screening a change, and what it reads +/// changes only as the document takes changes (and one changing the policy, or what +/// users sign, is `relevant`, taken at once). +fn reaches_signed( + doc: &Document, + signed: &mut Option<Option<Vec<Vec<String>>>>, + paths: &BTreeSet<Path>, +) -> bool { + if paths.is_empty() { return false; } - let signed = super::envelope::signed_paths(doc); - paths - .iter() - .any(|path| super::envelope::reaches_signed(&signed, path)) + let signed = signed.get_or_insert_with(|| { + super::policy::of(doc) + .strict() + .then(|| super::envelope::signed_paths(doc)) + }); + signed.as_ref().is_some_and(|signed| { + paths + .iter() + .any(|path| super::envelope::reaches_signed(signed, path)) + }) } /// `candidates` in an order where every change comes after its dependencies among them |
