From ac2d0e1b33b4674ad9b26266ef4b828d7200ec0f Mon Sep 17 00:00:00 2001 From: Jonas Berlin Date: Mon, 28 Nov 2022 20:37:22 +0200 Subject: [PATCH] impl: optimize replacen loop The previous implementation didn't bail out of the replace loop when the limit was reached until one more than the total number of 'find' operations had completed. By moving the limit check to the end of the loop body, we execute only the number of 'find' operations that is necessary, instead of one extra. This optimization only applies to 'replacen' calls with a limit not equal to '0'. That includes 'replace' but not 'replace_all'. PR #930 --- src/re_bytes.rs | 12 ++++++------ src/re_unicode.rs | 12 ++++++------ tests/replace.rs | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/re_bytes.rs b/src/re_bytes.rs index d71969257..07e9f98ac 100644 --- a/src/re_bytes.rs +++ b/src/re_bytes.rs @@ -496,12 +496,12 @@ impl Regex { let mut new = Vec::with_capacity(text.len()); let mut last_match = 0; for (i, m) in it { - if limit > 0 && i >= limit { - break; - } new.extend_from_slice(&text[last_match..m.start()]); new.extend_from_slice(&rep); last_match = m.end(); + if limit > 0 && i >= limit - 1 { + break; + } } new.extend_from_slice(&text[last_match..]); return Cow::Owned(new); @@ -516,14 +516,14 @@ impl Regex { let mut new = Vec::with_capacity(text.len()); let mut last_match = 0; for (i, cap) in it { - if limit > 0 && i >= limit { - break; - } // unwrap on 0 is OK because captures only reports matches let m = cap.get(0).unwrap(); new.extend_from_slice(&text[last_match..m.start()]); rep.replace_append(&cap, &mut new); last_match = m.end(); + if limit > 0 && i >= limit - 1 { + break; + } } new.extend_from_slice(&text[last_match..]); Cow::Owned(new) diff --git a/src/re_unicode.rs b/src/re_unicode.rs index 60d81a7d9..197510ea0 100644 --- a/src/re_unicode.rs +++ b/src/re_unicode.rs @@ -554,12 +554,12 @@ impl Regex { let mut new = String::with_capacity(text.len()); let mut last_match = 0; for (i, m) in it { - if limit > 0 && i >= limit { - break; - } new.push_str(&text[last_match..m.start()]); new.push_str(&rep); last_match = m.end(); + if limit > 0 && i >= limit - 1 { + break; + } } new.push_str(&text[last_match..]); return Cow::Owned(new); @@ -574,14 +574,14 @@ impl Regex { let mut new = String::with_capacity(text.len()); let mut last_match = 0; for (i, cap) in it { - if limit > 0 && i >= limit { - break; - } // unwrap on 0 is OK because captures only reports matches let m = cap.get(0).unwrap(); new.push_str(&text[last_match..m.start()]); rep.replace_append(&cap, &mut new); last_match = m.end(); + if limit > 0 && i >= limit - 1 { + break; + } } new.push_str(&text[last_match..]); Cow::Owned(new) diff --git a/tests/replace.rs b/tests/replace.rs index 1dc610635..d65be072f 100644 --- a/tests/replace.rs +++ b/tests/replace.rs @@ -228,3 +228,21 @@ replace!( bytes!(&std::borrow::Cow::<'_, [u8]>::Owned(vec![b'Z'])), "age: Z6" ); + +#[test] +fn replacen_no_captures() { + let re = regex!(r"[0-9]"); + assert_eq!( + re.replacen(text!("age: 1234"), 2, t!("Z")), + text!("age: ZZ34") + ); +} + +#[test] +fn replacen_with_captures() { + let re = regex!(r"([0-9])"); + assert_eq!( + re.replacen(text!("age: 1234"), 2, t!("${1}Z")), + text!("age: 1Z2Z34") + ); +}