mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 03:27:21 -06:00
fix(forms): merge a calculation's read names with a set
calculation_order merged the names a Calculate script reads into the field's other reads by searching the list so far for each name, so a script naming many fields took quadratic time before the link limits applied. The merge looks names up in a set.
This commit is contained in:
@@ -122,6 +122,12 @@ pub(crate) fn run_order(doc: &Document, fields: &[Field]) -> (Vec<(usize, bool)>
|
||||
(run, extra)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
/// Read names compared while merging a field's reads.
|
||||
pub(crate) static NAMES_COMPARED: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
|
||||
}
|
||||
|
||||
/// The form's calculation order (`fields` as [`fields`] reads them), with each calculated field's
|
||||
/// reads and issues, and Fix order's order.
|
||||
pub fn calculation_order(doc: &Document, fields: &[Field]) -> CalculationOrder {
|
||||
@@ -132,8 +138,11 @@ pub fn calculation_order(doc: &Document, fields: &[Field]) -> CalculationOrder {
|
||||
.map(|(f, _)| {
|
||||
let mut names = f.actions.calculate.reads();
|
||||
if let Some(js) = &f.actions.scripts.calculate {
|
||||
let mut known: HashSet<String> = names.iter().cloned().collect();
|
||||
for name in crate::af::calculation_reads(js) {
|
||||
if !names.contains(&name) {
|
||||
#[cfg(test)]
|
||||
NAMES_COMPARED.with(|n| n.set(n.get() + 1));
|
||||
if known.insert(name.clone()) {
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2981,3 +2981,20 @@ fn page_walk_skips_repeated_and_cyclic_kids_without_queueing_them() {
|
||||
assert_eq!(walk.next(), None);
|
||||
assert_eq!(page_refs(&doc), [page]);
|
||||
}
|
||||
|
||||
/// A Calculate script naming thousands of fields is merged with the field's other reads in one
|
||||
/// pass, not by searching the names so far for each one.
|
||||
#[test]
|
||||
fn a_script_reading_many_fields_merges_its_reads_in_one_pass() {
|
||||
use crate::calc_order::NAMES_COMPARED;
|
||||
let mut doc = one_page();
|
||||
add_field(&mut doc, 0, [50.0, 700.0, 250.0, 720.0], &NewField::Text { multiline: false }, Some("total")).unwrap();
|
||||
let n = 2_000;
|
||||
let script: String = (0..n).map(|i| format!("getField(\"f{i}\").value + ")).collect::<String>() + "0";
|
||||
set_field_script(&mut doc, "total", "calculate", Some(&format!("event.value = {script};"))).unwrap();
|
||||
NAMES_COMPARED.with(|c| c.set(0));
|
||||
let order = calculation_order(&doc, &fields(&doc));
|
||||
assert_eq!(order.fields[0].reads.len(), n);
|
||||
let compared = NAMES_COMPARED.with(std::cell::Cell::get);
|
||||
assert!(compared <= 4 * n, "{compared} names compared for {n} reads");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user