aboutsummaryrefslogtreecommitdiffstats
path: root/src/access/screen.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/access/screen.rs')
-rw-r--r--src/access/screen.rs37
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