abnf_oracle/generate.rs
1//! The deterministic generator (SCOPE.md 6.8).
2//!
3//! A walk over the arena that emits a string the grammar accepts. Two things make it terminate
4//! on grammars where a naive walk would not:
5//!
6//! * **Unproductive branches are never entered**, in any mode. Once inside `bad = "x" bad`
7//! there may be no alternation left to steer by, so the ban is absolute rather than a
8//! fallback for an exhausted budget (D20).
9//! * **Witness mode.** When the depth budget runs out the walk stops choosing and starts
10//! following witnesses, which form a well-founded derivation and so reach terminals in
11//! finitely many steps (D28). Choosing "the branch with the smallest `min_len`" is not a
12//! substitute: saturated values compare meaninglessly, and the choice would turn on node
13//! numbering rather than on the grammar.
14//!
15//! Termination is not the same as practicality. `start = 1000000000*"a"` is productive and has
16//! no shorter expansion, so the walk terminates and would still produce a gigabyte;
17//! [`GenOptions::max_output_len`] is what makes that an error instead.
18//!
19//! Coverage mode ([`GenOptions::coverage`]) steers towards branches not yet taken, with a
20//! guarantee rather than a probability: while any reachable branch is untaken, each successful
21//! call takes at least one.
22
23use std::collections::{HashMap, VecDeque};
24
25use crate::ast::{CharVal, Node, NodeId, NumVal, Witness};
26use crate::check::CheckedGrammar;
27use crate::error::GenError;
28use crate::rng::SplitMix64;
29
30/// How many extra repetitions a repetition may take beyond its minimum.
31pub const DEFAULT_SPREAD: usize = 3;
32/// How deep the walk may recurse before it starts following witnesses.
33///
34/// Named apart from [`crate::DEFAULT_MAX_DEPTH`], which is the recognizer's: that one is a
35/// safety limit whose breach is an error, this one is a steering budget whose exhaustion just
36/// changes how choices are made.
37pub const DEFAULT_GEN_DEPTH: usize = 16;
38/// How long a generated string may get before generation gives up.
39pub const DEFAULT_MAX_OUTPUT_LEN: usize = 1 << 20;
40/// How many nodes the walk may visit before it gives up.
41pub const DEFAULT_MAX_STEPS: u64 = 1 << 24;
42
43/// How to generate.
44#[derive(Clone, Debug, PartialEq, Eq)]
45pub struct GenOptions {
46 /// How deep to recurse before switching to witness mode. Default [`DEFAULT_GEN_DEPTH`].
47 ///
48 /// This is a *steering* budget, not a safety limit: exhausting it does not fail, it makes
49 /// the walk take the shortest derivation it knows. The safety limits are
50 /// [`GenOptions::max_output_len`] and [`GenOptions::max_steps`].
51 pub max_depth: usize,
52
53 /// How many repetitions beyond the minimum to consider. Default [`DEFAULT_SPREAD`].
54 ///
55 /// A repetition takes a count uniformly from `min..=min(max, min + spread)`, so `*"a"`
56 /// with the default produces nought to three characters rather than an unbounded run.
57 pub spread: usize,
58
59 /// Steer towards branches that have not been taken yet, rather than choosing randomly.
60 ///
61 /// With this set, and subject to the resource limits, **each successful call covers at
62 /// least one new coverage unit while any reachable one remains uncovered** — so full
63 /// coverage takes at most `uncovered(rule)` calls. That is a guarantee rather than a
64 /// probability, and it does not depend on [`GenOptions::max_depth`] (D10, D38).
65 pub coverage: bool,
66
67 /// Emit case-insensitive strings exactly as written, rather than varying their case.
68 ///
69 /// Varying case is the more useful default for a fuzzer — `"abc"` really does match `AbC` —
70 /// but it makes output unpredictable, so a test that compares generated text against an
71 /// expected string wants this set.
72 pub preserve_case: bool,
73
74 /// Give up once the output reaches this many scalar values. Default
75 /// [`DEFAULT_MAX_OUTPUT_LEN`].
76 ///
77 /// Finite by default, because termination alone does not make generation practical: a
78 /// grammar can demand a gigabyte without being wrong (D29).
79 pub max_output_len: Option<usize>,
80
81 /// Give up after visiting this many nodes. Default [`DEFAULT_MAX_STEPS`].
82 pub max_steps: Option<u64>,
83}
84
85impl Default for GenOptions {
86 fn default() -> Self {
87 Self {
88 max_depth: DEFAULT_GEN_DEPTH,
89 spread: DEFAULT_SPREAD,
90 coverage: false,
91 preserve_case: false,
92 max_output_len: Some(DEFAULT_MAX_OUTPUT_LEN),
93 max_steps: Some(DEFAULT_MAX_STEPS),
94 }
95 }
96}
97
98/// Generates strings that a rule accepts.
99///
100/// Deterministic: the same seed, options and sequence of calls produce the same strings, within
101/// a crate version (D15). Constructible only from a [`CheckedGrammar`] (D1).
102pub struct Generator<'g> {
103 grammar: &'g CheckedGrammar,
104 rng: SplitMix64,
105 options: GenOptions,
106 coverage: Coverage,
107 /// Units taken during the call in progress.
108 ///
109 /// Held apart until the call succeeds: a call that ends in `OutputLimit` produced no
110 /// output, so counting what it touched would let the bound be satisfied by strings nobody
111 /// ever saw.
112 pending: Vec<Unit>,
113 steps: u64,
114 /// Scalars emitted so far in the current call.
115 ///
116 /// Counted rather than measured: `String::chars().count()` walks the whole buffer, so
117 /// checking the limit that way costs O(n) per character and makes filling it O(n^2). A
118 /// million-scalar default would take tens of seconds to refuse.
119 emitted: usize,
120}
121
122impl<'g> Generator<'g> {
123 /// Creates a generator seeded with `seed`.
124 #[must_use]
125 pub fn new(grammar: &'g CheckedGrammar, seed: u64) -> Self {
126 Self {
127 grammar,
128 rng: SplitMix64::new(seed),
129 options: GenOptions::default(),
130 coverage: Coverage::new(grammar),
131 pending: Vec::new(),
132 steps: 0,
133 emitted: 0,
134 }
135 }
136
137 /// Sets the generation options.
138 #[must_use]
139 pub fn with_options(mut self, options: GenOptions) -> Self {
140 self.options = options;
141 self
142 }
143
144 /// How many nodes have been visited during the most recent call.
145 ///
146 /// Reset by every [`Generator::generate`], so one call's budget is never spent by the last.
147 /// That differs from [`crate::Recognizer::steps`], which counts over the whole life of a
148 /// recognizer: a generator is asked for many independent strings, while a recognizer is
149 /// bound to one input and answers questions about it.
150 ///
151 /// The counter [`GenOptions::max_steps`] limits, and what tests assert against rather than
152 /// wall-clock time (D31).
153 #[must_use]
154 pub const fn steps(&self) -> u64 {
155 self.steps
156 }
157
158 /// How many coverage units `rule` can reach that have not been covered yet.
159 ///
160 /// Counted over the generatable graph, so a branch the generator would never take is not
161 /// counted against it (D10). Zero means every branch reachable from this rule has been
162 /// taken by some successful call.
163 ///
164 /// With [`GenOptions::coverage`] set, this is also the *bound*: each successful call
165 /// reduces it by at least one, so it reaches zero within this many calls.
166 ///
167 /// # Errors
168 ///
169 /// As [`Generator::generate`], for the same reasons: a rule that cannot be generated from
170 /// has no coverage to report.
171 pub fn uncovered(&mut self, rule: &str) -> Result<usize, GenError> {
172 let body = self.start_rule(rule)?;
173 Ok(self.coverage.uncovered_reachable(self.grammar, body))
174 }
175
176 /// Generates one string that `rule` accepts.
177 ///
178 /// # Errors
179 ///
180 /// [`GenError::UnknownRule`] if there is no such rule; the compatibility limits of
181 /// SCOPE.md 6.6 if the rule can reach a prose value or an unrepresentable terminal;
182 /// [`GenError::NoFiniteExpansion`] if it matches nothing at all; or a resource limit.
183 pub fn generate(&mut self, rule: &str) -> Result<String, GenError> {
184 let body = self.start_rule(rule)?;
185
186 let mut out = String::new();
187 // Fresh per call, so one call's limit is not spent by the last.
188 self.steps = 0;
189 self.emitted = 0;
190 self.pending.clear();
191 self.walk(body, 0, &mut out)?;
192
193 // Committed only now that the call has succeeded.
194 for unit in std::mem::take(&mut self.pending) {
195 self.coverage.cover(unit);
196 }
197 Ok(out)
198 }
199
200 /// Resolves a start rule, refusing the ones that cannot be generated from.
201 fn start_rule(&self, rule: &str) -> Result<NodeId, GenError> {
202 let found = self
203 .grammar
204 .rule(rule)
205 .ok_or_else(|| GenError::UnknownRule(rule.to_owned()))?;
206
207 // Prose and unrepresentable terminals are refused before anything is emitted, the same
208 // way the recognizer refuses them before reading input (D11, D4).
209 self.grammar.can_generate(rule)?;
210
211 // An unproductive rule is refused immediately rather than discovered by walking into
212 // it: there is no string to produce, so there is nothing to attempt (D9).
213 if !self.grammar.min_len(found.body).is_finite() {
214 return Err(GenError::NoFiniteExpansion {
215 rule: found.name.as_str().to_owned(),
216 });
217 }
218 Ok(found.body)
219 }
220
221 /// Emits one node's contribution to the output.
222 fn walk(&mut self, node: NodeId, depth: usize, out: &mut String) -> Result<(), GenError> {
223 self.step()?;
224
225 match self.grammar.node(node) {
226 Node::Alt { branches } => {
227 let branches = branches.clone();
228 let chosen = self.choose_branch(node, &branches, depth);
229 self.walk(branches[chosen], depth, out)
230 }
231 Node::Concat { items } => {
232 for item in items.clone() {
233 self.walk(item, depth, out)?;
234 }
235 Ok(())
236 }
237 Node::Repeat { repeat, body, .. } => {
238 let (repeat, body) = (*repeat, *body);
239 let count = self.choose_count(repeat.min, repeat.max, body, depth);
240 for _ in 0..count {
241 self.walk(body, depth, out)?;
242 }
243 Ok(())
244 }
245 Node::Optional { body } => {
246 let body = *body;
247 // `[x]` is `x / empty`: branch 0 takes the body, branch 1 skips it.
248 if self.choose_branch_of_optional(node, body, depth) == 0 {
249 self.walk(body, depth, out)?;
250 }
251 Ok(())
252 }
253 Node::RuleRef { .. } => {
254 let target = self
255 .grammar
256 .target(node)
257 .expect("`check` resolved every reference");
258 let body = self.grammar.rule_by_id(target).body;
259 self.walk(body, depth + 1, out)
260 }
261 Node::CharVal(value) => {
262 let value = value.clone();
263 self.emit_char_val(&value, out)
264 }
265 Node::NumVal { value, .. } => {
266 let value = value.clone();
267 self.emit_num_val(&value, out)
268 }
269 Node::ProseVal(_) => {
270 // Unreachable: the start rule was refused if it could reach prose, and the
271 // reachability that decides that walks the same nodes this does.
272 debug_assert!(false, "prose reached despite the start-rule gate");
273 Ok(())
274 }
275 }
276 }
277
278 /// Picks a branch of an alternation, and records the unit that choice covers.
279 fn choose_branch(&mut self, node: NodeId, branches: &[NodeId], depth: usize) -> usize {
280 // Never a branch that matches nothing, in any mode (D20).
281 let usable: Vec<usize> = (0..branches.len())
282 .filter(|index| self.grammar.min_len(branches[*index]).is_finite())
283 .collect();
284 debug_assert!(
285 !usable.is_empty(),
286 "a productive alternation has a productive branch"
287 );
288
289 let targets: Vec<NodeId> = usable.iter().map(|branch| branches[*branch]).collect();
290 let chosen = self.select(node, &usable, &targets, depth);
291 self.pending.push(Unit {
292 node,
293 branch: chosen as u32,
294 });
295 chosen
296 }
297
298 /// Picks between taking an optional's body and skipping it.
299 ///
300 /// `[x]` is `x / empty`, so this is an alternation with branch 0 the body and branch 1 the
301 /// empty arm. The empty arm leads nowhere, which the selection rule sees as an unreachable
302 /// target.
303 fn choose_branch_of_optional(&mut self, node: NodeId, body: NodeId, depth: usize) -> usize {
304 let (usable, targets) = if self.grammar.min_len(body).is_finite() {
305 // `node` stands in for "leads nowhere": it holds no uncovered unit of its own that
306 // taking the empty arm could reach, so its distance is never the deciding one.
307 (vec![0, 1], vec![body, node])
308 } else {
309 // Skipping is always available, and is all that is left.
310 (vec![1], vec![node])
311 };
312
313 let chosen = self.select(node, &usable, &targets, depth);
314 self.pending.push(Unit {
315 node,
316 branch: chosen as u32,
317 });
318 chosen
319 }
320
321 /// The selection rule of SCOPE.md 6.8, shared by alternations and optionals.
322 ///
323 /// `targets[i]` is the node `usable[i]` leads to.
324 fn select(
325 &mut self,
326 node: NodeId,
327 usable: &[usize],
328 targets: &[NodeId],
329 depth: usize,
330 ) -> usize {
331 if self.options.coverage {
332 // 1. A branch not yet taken. Taking it covers a unit outright, which is what makes
333 // the per-call guarantee hold.
334 let untaken = usable.iter().copied().find(|branch| {
335 let unit = Unit {
336 node,
337 branch: *branch as u32,
338 };
339 !self.coverage.is_covered(unit) && !self.pending.contains(&unit)
340 });
341 if let Some(branch) = untaken {
342 return branch;
343 }
344
345 // 2. Otherwise the branch closest to something uncovered — the *chase*. Distance
346 // strictly decreases along it, so it ends; "any branch that reaches one, ties at
347 // random" would not, which is the failure D38 exists to prevent. Ties by branch
348 // index keep it deterministic.
349 let mut nearest: Option<(u32, usize)> = None;
350 for (position, branch) in usable.iter().copied().enumerate() {
351 let target = targets[position];
352 // An optional's empty arm leads back to the node itself: nothing new lies
353 // beyond it, so it never wins the chase.
354 if target == node {
355 continue;
356 }
357 let distance = self.coverage.distance_to_uncovered(target);
358 if distance != UNREACHABLE && nearest.is_none_or(|(best, _)| distance < best) {
359 nearest = Some((distance, branch));
360 }
361 }
362 if let Some((_, branch)) = nearest {
363 return branch;
364 }
365 // 3. Nothing reachable is uncovered: fall through to the ordinary rules below.
366 }
367
368 if depth >= self.options.max_depth {
369 // Witness mode: the shortest derivation this node knows, which terminates because
370 // witnesses were recorded in the order `min_len` was settled (D28).
371 //
372 // In coverage mode this is reached only once every reachable unit is covered, so
373 // the depth budget can never cut a chase short (D38).
374 if let Some(Witness::Branch(branch)) = self.grammar.witness(node)
375 && usable.contains(&(branch as usize))
376 {
377 return branch as usize;
378 }
379 }
380 let pick = self.rng.below(usable.len() as u64) as usize;
381 usable[pick]
382 }
383
384 /// Picks how many times to repeat.
385 fn choose_count(&mut self, min: u64, max: Option<u64>, body: NodeId, depth: usize) -> u64 {
386 // In coverage mode a repetition whose body can reach something uncovered must run at
387 // least once, or a zero count would skip past units the bound is counting on (D25).
388 if self.options.coverage
389 && max.is_none_or(|max| max >= 1)
390 && self.coverage.distance_to_uncovered(body) != UNREACHABLE
391 {
392 return min.max(1);
393 }
394 if depth >= self.options.max_depth {
395 return min; // witness mode: a repetition's witness is always its minimum
396 }
397 let ceiling = max
398 .unwrap_or(u64::MAX)
399 .min(min.saturating_add(self.options.spread as u64));
400 if ceiling <= min {
401 return min;
402 }
403 min + self.rng.below(ceiling - min + 1)
404 }
405
406 fn emit_char_val(&mut self, value: &CharVal, out: &mut String) -> Result<(), GenError> {
407 for ch in value.value.chars() {
408 // A case-insensitive string matches either case, so vary it — that is the whole
409 // point of the distinction, and a generator that always emitted one case would
410 // never exercise the other.
411 let emitted = if value.case_sensitive || self.options.preserve_case {
412 ch
413 } else if self.rng.bool() {
414 ch.to_ascii_uppercase()
415 } else {
416 ch.to_ascii_lowercase()
417 };
418 self.push(emitted, out)?;
419 }
420 Ok(())
421 }
422
423 fn emit_num_val(&mut self, value: &NumVal, out: &mut String) -> Result<(), GenError> {
424 match value {
425 NumVal::Scalar(scalar) => {
426 let ch = scalar_at(*scalar).expect("`check` refused unrepresentable terminals");
427 self.push(ch, out)
428 }
429 NumVal::Range { lo, hi } => {
430 // Uniform among the scalars the range holds, which is not the same as uniform
431 // over `lo..=hi`: the surrogate block is skipped (SCOPE.md 6.1).
432 let count = NumVal::scalars_in(*lo, *hi);
433 debug_assert!(count > 0, "`check` refused empty ranges");
434 let index = self.rng.below(count);
435 let ch = NumVal::nth_scalar(*lo, *hi, index).expect("index is in range");
436 self.push(ch, out)
437 }
438 NumVal::Concat(values) => {
439 for scalar in values {
440 let ch = scalar_at(*scalar).expect("`check` refused unrepresentable terminals");
441 self.push(ch, out)?;
442 }
443 Ok(())
444 }
445 }
446 }
447
448 /// Appends one scalar, respecting the output limit.
449 fn push(&mut self, ch: char, out: &mut String) -> Result<(), GenError> {
450 if self
451 .options
452 .max_output_len
453 .is_some_and(|limit| self.emitted >= limit)
454 {
455 return Err(GenError::OutputLimit);
456 }
457 self.emitted += 1;
458 out.push(ch);
459 Ok(())
460 }
461
462 /// Counts one node visit, and stops if that exceeds the limit.
463 fn step(&mut self) -> Result<(), GenError> {
464 self.steps += 1;
465 if self
466 .options
467 .max_steps
468 .is_some_and(|limit| self.steps > limit)
469 {
470 return Err(GenError::StepLimit);
471 }
472 Ok(())
473 }
474}
475
476/// The scalar value `value` names, if it names one.
477fn scalar_at(value: u64) -> Option<char> {
478 char::from_u32(u32::try_from(value).ok()?)
479}
480
481/// Distance meaning "no uncovered unit is reachable from here".
482const UNREACHABLE: u32 = u32::MAX;
483
484/// One thing the generator can be asked to exercise: a branch of an alternation.
485///
486/// `[x]` contributes two, via its `x / empty` model, and since `*1a` canonicalizes to `[a]`
487/// the two spellings are indistinguishable here (SCOPE.md 6.8, D10).
488#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
489struct Unit {
490 node: NodeId,
491 branch: u32,
492}
493
494/// The coverage machinery: which branches exist, which have been taken, and how far away the
495/// nearest untaken one is.
496///
497/// Everything here is computed over the **generatable graph** — the arena minus every branch
498/// with infinite `min_len` and every `max == 0` repetition body — because the generator never
499/// enters either, so counting what is inside them would make the coverage bound unreachable by
500/// construction (SCOPE.md 6.8).
501struct Coverage {
502 /// Every candidate unit, in node order.
503 units: Vec<Unit>,
504 /// Where each unit sits in `units`.
505 index: HashMap<Unit, usize>,
506 /// Which units have been covered by a successful call.
507 covered: Vec<bool>,
508 /// Which units are reachable from each rule body, cached on first use.
509 ///
510 /// Keyed by the body's [`NodeId`], which is unique per rule across *both* tables. Keying by
511 /// an index into the user-rule slice instead meant every implicit core rule -- which has no
512 /// such index -- collapsed onto one shared entry, so asking about `HEXDIG` after `ALPHA`
513 /// returned `ALPHA`'s answer (D46).
514 reachable: HashMap<NodeId, Vec<usize>>,
515 /// Successors of each node over the generatable graph.
516 edges: Vec<Vec<NodeId>>,
517 /// Distance from each node to the nearest uncovered unit.
518 distance: Vec<u32>,
519 /// Whether `distance` needs recomputing before it is next read.
520 stale: bool,
521}
522
523impl Coverage {
524 /// Enumerates the units of a grammar and builds the graph they live on.
525 fn new(grammar: &CheckedGrammar) -> Self {
526 let node_count = grammar.node_count();
527 let mut units = Vec::new();
528 let mut edges = vec![Vec::new(); node_count];
529
530 for (index, outgoing) in edges.iter_mut().enumerate() {
531 let node = NodeId::from_index(index as u32);
532 match grammar.node(node) {
533 Node::Alt { branches } => {
534 for (branch, child) in branches.iter().enumerate() {
535 // A branch that matches nothing is not a unit and not an edge: the
536 // generator will never take it, so neither may the bound (D20).
537 if grammar.min_len(*child).is_finite() {
538 units.push(Unit {
539 node,
540 branch: branch as u32,
541 });
542 outgoing.push(*child);
543 }
544 }
545 }
546 Node::Optional { body } => {
547 // Two units: branch 0 takes the body, branch 1 skips it. Skipping is always
548 // available, so it is a unit even when the body is not.
549 if grammar.min_len(*body).is_finite() {
550 units.push(Unit { node, branch: 0 });
551 outgoing.push(*body);
552 }
553 units.push(Unit { node, branch: 1 });
554 }
555 Node::Concat { items } => outgoing.extend(items.iter().copied()),
556 Node::Repeat { repeat, body, .. } => {
557 // `*0(x)` can never run its body, so nothing inside it is reachable (D36).
558 if !repeat.is_never() {
559 outgoing.push(*body);
560 }
561 }
562 Node::RuleRef { .. } => {
563 if let Some(target) = grammar.target(node) {
564 outgoing.push(grammar.rule_by_id(target).body);
565 }
566 }
567 Node::CharVal(_) | Node::NumVal { .. } | Node::ProseVal(_) => {}
568 }
569 }
570
571 let index = units
572 .iter()
573 .enumerate()
574 .map(|(position, unit)| (*unit, position))
575 .collect();
576 let count = units.len();
577 Self {
578 units,
579 index,
580 covered: vec![false; count],
581 reachable: HashMap::new(),
582 edges,
583 distance: vec![UNREACHABLE; node_count],
584 stale: true,
585 }
586 }
587
588 /// How many units reachable from `body` are still uncovered.
589 fn uncovered_reachable(&mut self, grammar: &CheckedGrammar, body: NodeId) -> usize {
590 // Populate the cache first, so the borrow of `reachable` ends before `covered` is read.
591 self.reachable_from(grammar, body);
592 self.reachable[&body]
593 .iter()
594 .filter(|position| !self.covered[**position])
595 .count()
596 }
597
598 /// The units reachable from a rule `body`, cached.
599 ///
600 /// Reachability does not change as coverage grows — the generatable graph is fixed — so
601 /// this is computed once per body.
602 fn reachable_from(&mut self, grammar: &CheckedGrammar, body: NodeId) -> &[usize] {
603 if !self.reachable.contains_key(&body) {
604 let mut seen = vec![false; grammar.node_count()];
605 let mut stack = vec![body];
606 seen[body.index()] = true;
607 let mut found = Vec::new();
608
609 while let Some(node) = stack.pop() {
610 // A unit belongs to the node it is a branch of, so reaching the node is what
611 // makes its units reachable.
612 for (position, unit) in self.units.iter().enumerate() {
613 if unit.node == node {
614 found.push(position);
615 }
616 }
617 for next in &self.edges[node.index()] {
618 if !seen[next.index()] {
619 seen[next.index()] = true;
620 stack.push(*next);
621 }
622 }
623 }
624 found.sort_unstable();
625 self.reachable.insert(body, found);
626 }
627 &self.reachable[&body]
628 }
629
630 /// Marks a unit covered, if it is one.
631 fn cover(&mut self, unit: Unit) {
632 if let Some(position) = self.index.get(&unit)
633 && !self.covered[*position]
634 {
635 self.covered[*position] = true;
636 // Covering a unit can only *raise* distances, so a stale table still decreases
637 // strictly along a chase — it may merely chase something already covered. Recompute
638 // lazily rather than on every step.
639 self.stale = true;
640 }
641 }
642
643 /// Recomputes `distance` if coverage has changed since it was last read.
644 ///
645 /// Multi-source reverse breadth-first search: every node holding an uncovered unit is at
646 /// distance zero, and every other node is one more than its nearest successor.
647 fn refresh(&mut self) {
648 if !self.stale {
649 return;
650 }
651 self.stale = false;
652
653 let node_count = self.distance.len();
654 self.distance.fill(UNREACHABLE);
655
656 // Reverse edges, so the search can walk from a target back towards its predecessors.
657 let mut incoming: Vec<Vec<usize>> = vec![Vec::new(); node_count];
658 for (from, targets) in self.edges.iter().enumerate() {
659 for target in targets {
660 incoming[target.index()].push(from);
661 }
662 }
663
664 let mut queue = VecDeque::new();
665 for (position, unit) in self.units.iter().enumerate() {
666 if !self.covered[position] {
667 let index = unit.node.index();
668 if self.distance[index] != 0 {
669 self.distance[index] = 0;
670 queue.push_back(index);
671 }
672 }
673 }
674 while let Some(node) = queue.pop_front() {
675 let next = self.distance[node] + 1;
676 for previous in &incoming[node] {
677 if self.distance[*previous] > next {
678 self.distance[*previous] = next;
679 queue.push_back(*previous);
680 }
681 }
682 }
683 }
684
685 /// How far `node` is from the nearest uncovered unit.
686 fn distance_to_uncovered(&mut self, node: NodeId) -> u32 {
687 self.refresh();
688 self.distance[node.index()]
689 }
690
691 fn is_covered(&self, unit: Unit) -> bool {
692 self.index
693 .get(&unit)
694 .is_some_and(|position| self.covered[*position])
695 }
696}