diff --git a/crates/functions/src/lib.rs b/crates/functions/src/lib.rs index 1b422b947..ab50fe62f 100644 --- a/crates/functions/src/lib.rs +++ b/crates/functions/src/lib.rs @@ -310,6 +310,10 @@ mod tests { assert_eq!(ev("REGEXREPLACE", vec![t("a1b2"), t("[0-9]"), t("#"), n(2.0)]), Value::text("a1b#")); assert_eq!(ev("REGEXREPLACE", vec![t("a1b2"), t("[0-9]"), t("#"), n(-2.0)]), Value::text("a#b2")); assert_eq!(ev("REGEXREPLACE", vec![t("a1b2"), t("[0-9]"), t("#"), n(-3.0)]), Value::text("a1b2")); + // 2^32 is no occurrence the text has (and on 32-bit targets it must not wrap to 0). + for occurrence in [4_294_967_296.0, -4_294_967_296.0] { + assert_eq!(ev("REGEXREPLACE", vec![t("a1b2"), t("[0-9]"), t("#"), n(occurrence)]), Value::text("a1b2")); + } // One match whose replacement repeats it 10,000 times: refused before it is built. assert_eq!(ev("REGEXREPLACE", vec![t(&many), t("^.*$"), t(&"$0".repeat(10_000))]), Value::Error(CellError::Value)); assert_eq!(ev("REGEXREPLACE", vec![t(&many), t("^.*$"), t(&"$0".repeat(10_000)), n(1.0)]), Value::Error(CellError::Value)); diff --git a/crates/functions/src/text.rs b/crates/functions/src/text.rs index 87ee86cbb..4d40828ae 100644 --- a/crates/functions/src/text.rs +++ b/crates/functions/src/text.rs @@ -883,7 +883,8 @@ fn regexreplace(a: &[Arg], _c: &mut dyn Ctx) -> R { out.push_str(s.get(last..).unwrap_or_default()); return check_len(out); } - let k = occ.unsigned_abs() as usize; + // An occurrence past what `usize` holds (32-bit targets) names no match: the text is returned as it was. + let Ok(k) = usize::try_from(occ.unsigned_abs()) else { return text_val(s) }; // Keeps only the wanted match's captures: the nth from the front, or a window of the last `k`. let cap = if occ > 0 { re.captures_iter(&s).nth(k - 1)