Skip to content

Commit ac2d0e1

Browse files
authored
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
1 parent f871a8e commit ac2d0e1

File tree

3 files changed

+30
-12
lines changed

3 files changed

+30
-12
lines changed

Diff for: src/re_bytes.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -496,12 +496,12 @@ impl Regex {
496496
let mut new = Vec::with_capacity(text.len());
497497
let mut last_match = 0;
498498
for (i, m) in it {
499-
if limit > 0 && i >= limit {
500-
break;
501-
}
502499
new.extend_from_slice(&text[last_match..m.start()]);
503500
new.extend_from_slice(&rep);
504501
last_match = m.end();
502+
if limit > 0 && i >= limit - 1 {
503+
break;
504+
}
505505
}
506506
new.extend_from_slice(&text[last_match..]);
507507
return Cow::Owned(new);
@@ -516,14 +516,14 @@ impl Regex {
516516
let mut new = Vec::with_capacity(text.len());
517517
let mut last_match = 0;
518518
for (i, cap) in it {
519-
if limit > 0 && i >= limit {
520-
break;
521-
}
522519
// unwrap on 0 is OK because captures only reports matches
523520
let m = cap.get(0).unwrap();
524521
new.extend_from_slice(&text[last_match..m.start()]);
525522
rep.replace_append(&cap, &mut new);
526523
last_match = m.end();
524+
if limit > 0 && i >= limit - 1 {
525+
break;
526+
}
527527
}
528528
new.extend_from_slice(&text[last_match..]);
529529
Cow::Owned(new)

Diff for: src/re_unicode.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -554,12 +554,12 @@ impl Regex {
554554
let mut new = String::with_capacity(text.len());
555555
let mut last_match = 0;
556556
for (i, m) in it {
557-
if limit > 0 && i >= limit {
558-
break;
559-
}
560557
new.push_str(&text[last_match..m.start()]);
561558
new.push_str(&rep);
562559
last_match = m.end();
560+
if limit > 0 && i >= limit - 1 {
561+
break;
562+
}
563563
}
564564
new.push_str(&text[last_match..]);
565565
return Cow::Owned(new);
@@ -574,14 +574,14 @@ impl Regex {
574574
let mut new = String::with_capacity(text.len());
575575
let mut last_match = 0;
576576
for (i, cap) in it {
577-
if limit > 0 && i >= limit {
578-
break;
579-
}
580577
// unwrap on 0 is OK because captures only reports matches
581578
let m = cap.get(0).unwrap();
582579
new.push_str(&text[last_match..m.start()]);
583580
rep.replace_append(&cap, &mut new);
584581
last_match = m.end();
582+
if limit > 0 && i >= limit - 1 {
583+
break;
584+
}
585585
}
586586
new.push_str(&text[last_match..]);
587587
Cow::Owned(new)

Diff for: tests/replace.rs

+18
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,21 @@ replace!(
228228
bytes!(&std::borrow::Cow::<'_, [u8]>::Owned(vec![b'Z'])),
229229
"age: Z6"
230230
);
231+
232+
#[test]
233+
fn replacen_no_captures() {
234+
let re = regex!(r"[0-9]");
235+
assert_eq!(
236+
re.replacen(text!("age: 1234"), 2, t!("Z")),
237+
text!("age: ZZ34")
238+
);
239+
}
240+
241+
#[test]
242+
fn replacen_with_captures() {
243+
let re = regex!(r"([0-9])");
244+
assert_eq!(
245+
re.replacen(text!("age: 1234"), 2, t!("${1}Z")),
246+
text!("age: 1Z2Z34")
247+
);
248+
}

0 commit comments

Comments
 (0)