mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-03 08:51:30 -06:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7db8b5b746 |
@@ -339,11 +339,11 @@ fn numbervalue(a: &[Arg], c: &mut dyn Ctx) -> R<Value> {
|
||||
if body.is_empty() {
|
||||
return num_val(0.0);
|
||||
}
|
||||
let mut pct = 0;
|
||||
while let Some(b) = body.strip_suffix('%') {
|
||||
body = b.to_string();
|
||||
pct += 1;
|
||||
}
|
||||
// Count the trailing percent signs and cut them off in place: copying the rest once per sign
|
||||
// made a long run of them quadratic.
|
||||
let kept = body.trim_end_matches('%').len();
|
||||
let pct = body.len() - kept;
|
||||
body.truncate(kept);
|
||||
let mut seen_dec = false;
|
||||
let mut out = String::new();
|
||||
for c in body.chars() {
|
||||
@@ -1084,6 +1084,15 @@ mod tests {
|
||||
|
||||
use crate::util::testutil::*;
|
||||
|
||||
/// Trailing percent signs each divide by 100, and a long run of them is cut off in one pass.
|
||||
#[test]
|
||||
fn numbervalue_percent_signs() {
|
||||
close(ev("NUMBERVALUE", vec![t("5%%")]), 0.0005);
|
||||
close(ev("NUMBERVALUE", vec![t("1,5%"), t(","), t(".")]), 0.015);
|
||||
let many = format!("1{}", "%".repeat(32_000));
|
||||
close(ev("NUMBERVALUE", vec![t(&many)]), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bahttext() {
|
||||
let bt = |x: f64| ev("BAHTTEXT", vec![n(x)]);
|
||||
|
||||
+1
-10
@@ -470,14 +470,6 @@ impl Import {
|
||||
None => return Err(IoError::TooLarge("ODS row exceeds worksheet width".into())),
|
||||
};
|
||||
if kept {
|
||||
// Charge the text a row keeps before the row is done, so a row of many long cells is
|
||||
// refused as it grows; finish_row charges the row's repeats beyond the first.
|
||||
if let Value::Text(t) = &cell.value {
|
||||
self.text = self.text.saturating_add(u64::from(draft.repeat).saturating_mul(t.len() as u64));
|
||||
if self.text > MAX_TEXT {
|
||||
return Err(IoError::TooLarge("ODS expands beyond the import limit (64 MiB of text)".into()));
|
||||
}
|
||||
}
|
||||
row.cells.push(RepeatedCell { col: row.col, repeat: draft.repeat, width, height, cell });
|
||||
}
|
||||
row.col = end;
|
||||
@@ -545,8 +537,7 @@ impl Import {
|
||||
if !entry.cell.is_blank() {
|
||||
self.cells = self.cells.saturating_add(count);
|
||||
if let Value::Text(t) = &entry.cell.value {
|
||||
let repeats = count.saturating_sub(u64::from(entry.repeat));
|
||||
self.text = self.text.saturating_add(repeats.saturating_mul(t.len() as u64));
|
||||
self.text = self.text.saturating_add(count.saturating_mul(t.len() as u64));
|
||||
}
|
||||
}
|
||||
let merged = entry.width > 1 || entry.height > 1;
|
||||
|
||||
@@ -200,18 +200,6 @@ fn ods_expansion_bounds_stop_small_repeat_bombs() {
|
||||
"x".repeat(1024)
|
||||
);
|
||||
assert!(matches!(read_ods(&ods(&text)), Err(IoError::TooLarge(_))));
|
||||
// One row of cells each just under the 1 MiB cell limit, past 64 MiB in all: refused while
|
||||
// the row is read, before it holds them all. The row's end is never reached.
|
||||
let long = format!(r#"<table:table-cell office:value-type="string" office:string-value="{}"/>"#, "x".repeat(1024 * 1024 - 1));
|
||||
let cells = ods(&format!(r#"<table:table table:name="Data"><table:table-row>{}"#, long.repeat(65)));
|
||||
assert!(matches!(read_ods(&cells), Err(IoError::TooLarge(_))));
|
||||
// A row repeated once more than its cells' text allows is refused too, and one that fits reads.
|
||||
let cell = format!(r#"<table:table-cell office:value-type="string" office:string-value="{}"/>"#, "y".repeat(1024));
|
||||
let rows = |n: u32| {
|
||||
ods(&format!(r#"<table:table table:name="Data"><table:table-row table:number-rows-repeated="{n}">{cell}</table:table-row></table:table>"#))
|
||||
};
|
||||
assert!(read_ods(&rows(65_536)).is_ok());
|
||||
assert!(matches!(read_ods(&rows(65_537)), Err(IoError::TooLarge(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user