Skip to content

Commit 5effd65

Browse files
committed
Use Cow for replacements.
If `replace` doesn't find any matches, then it can return the original string unchanged.
1 parent 2f408a2 commit 5effd65

8 files changed

+63
-32
lines changed

examples/shootout-regex-dna-bytes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn main() {
1818
io::stdin().read_to_end(&mut seq).unwrap();
1919
let ilen = seq.len();
2020

21-
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, &b""[..]);
21+
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, &b""[..]).into_owned();
2222
let clen = seq.len();
2323
let seq_arc = Arc::new(seq.clone());
2424

@@ -56,7 +56,7 @@ fn main() {
5656
];
5757
let mut seq = seq;
5858
for (re, replacement) in substs.into_iter() {
59-
seq = re.replace_all(&seq, replacement);
59+
seq = re.replace_all(&seq, replacement).into_owned();
6060
}
6161

6262
for (variant, count) in counts {

examples/shootout-regex-dna-cheat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn main() {
2323
io::stdin().read_to_string(&mut seq).unwrap();
2424
let ilen = seq.len();
2525

26-
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "");
26+
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "").into_owned();
2727
let clen = seq.len();
2828
let seq_arc = Arc::new(seq.clone());
2929

examples/shootout-regex-dna-replace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ fn main() {
1414
io::stdin().read_to_string(&mut seq).unwrap();
1515
let ilen = seq.len();
1616

17-
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "");
17+
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "").into_owned();
1818
println!("original: {}, replaced: {}", ilen, seq.len());
1919
}

examples/shootout-regex-dna-single-cheat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn main() {
1616
io::stdin().read_to_string(&mut seq).unwrap();
1717
let ilen = seq.len();
1818

19-
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "");
19+
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "").into_owned();
2020
let clen = seq.len();
2121

2222
let variants = vec![

examples/shootout-regex-dna-single.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn main() {
1616
io::stdin().read_to_string(&mut seq).unwrap();
1717
let ilen = seq.len();
1818

19-
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "");
19+
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "").into_owned();
2020
let clen = seq.len();
2121

2222
let variants = vec![
@@ -49,7 +49,7 @@ fn main() {
4949
];
5050
let mut seq = seq;
5151
for (re, replacement) in substs.into_iter() {
52-
seq = re.replace_all(&seq, replacement);
52+
seq = re.replace_all(&seq, replacement).into_owned();
5353
}
5454
println!("\n{}\n{}\n{}", ilen, clen, seq.len());
5555
}

examples/shootout-regex-dna.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn main() {
1818
io::stdin().read_to_string(&mut seq).unwrap();
1919
let ilen = seq.len();
2020

21-
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "");
21+
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "").into_owned();
2222
let clen = seq.len();
2323
let seq_arc = Arc::new(seq.clone());
2424

@@ -56,7 +56,7 @@ fn main() {
5656
];
5757
let mut seq = seq;
5858
for (re, replacement) in substs.into_iter() {
59-
seq = re.replace_all(&seq, replacement);
59+
seq = re.replace_all(&seq, replacement).into_owned();
6060
}
6161

6262
for (variant, count) in counts {

src/re_bytes.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ impl Regex {
353353
/// # extern crate regex; use regex::bytes::Regex;
354354
/// # fn main() {
355355
/// let re = Regex::new("[^01]+").unwrap();
356-
/// assert_eq!(re.replace(b"1078910", &b""[..]), b"1010");
356+
/// assert_eq!(re.replace(b"1078910", &b""[..]), &b"1010"[..]);
357357
/// # }
358358
/// ```
359359
///
@@ -372,7 +372,7 @@ impl Regex {
372372
/// replacement.extend(&caps[1]);
373373
/// replacement
374374
/// });
375-
/// assert_eq!(result, b"Bruce Springsteen");
375+
/// assert_eq!(result, &b"Bruce Springsteen"[..]);
376376
/// # }
377377
/// ```
378378
///
@@ -386,7 +386,7 @@ impl Regex {
386386
/// # fn main() {
387387
/// let re = Regex::new(r"(?P<last>[^,\s]+),\s+(?P<first>\S+)").unwrap();
388388
/// let result = re.replace(b"Springsteen, Bruce", &b"$first $last"[..]);
389-
/// assert_eq!(result, b"Bruce Springsteen");
389+
/// assert_eq!(result, &b"Bruce Springsteen"[..]);
390390
/// # }
391391
/// ```
392392
///
@@ -411,10 +411,14 @@ impl Regex {
411411
///
412412
/// let re = Regex::new(r"(?P<last>[^,\s]+),\s+(\S+)").unwrap();
413413
/// let result = re.replace(b"Springsteen, Bruce", NoExpand(b"$2 $last"));
414-
/// assert_eq!(result, b"$2 $last");
414+
/// assert_eq!(result, &b"$2 $last"[..]);
415415
/// # }
416416
/// ```
417-
pub fn replace<R: Replacer>(&self, text: &[u8], rep: R) -> Vec<u8> {
417+
pub fn replace<'t, R: Replacer>(
418+
&self,
419+
text: &'t [u8],
420+
rep: R,
421+
) -> Cow<'t, [u8]> {
418422
self.replacen(text, 1, rep)
419423
}
420424

@@ -424,7 +428,11 @@ impl Regex {
424428
///
425429
/// See the documentation for `replace` for details on how to access
426430
/// submatches in the replacement text.
427-
pub fn replace_all<R: Replacer>(&self, text: &[u8], rep: R) -> Vec<u8> {
431+
pub fn replace_all<'t, R: Replacer>(
432+
&self,
433+
text: &'t [u8],
434+
rep: R,
435+
) -> Cow<'t, [u8]> {
428436
self.replacen(text, 0, rep)
429437
}
430438

@@ -434,16 +442,20 @@ impl Regex {
434442
///
435443
/// See the documentation for `replace` for details on how to access
436444
/// submatches in the replacement text.
437-
pub fn replacen<R: Replacer>(
445+
pub fn replacen<'t, R: Replacer>(
438446
&self,
439-
text: &[u8],
447+
text: &'t [u8],
440448
limit: usize,
441449
mut rep: R,
442-
) -> Vec<u8> {
450+
) -> Cow<'t, [u8]> {
443451
if let Some(rep) = rep.no_expansion() {
452+
let mut it = self.find_iter(text).enumerate().peekable();
453+
if it.peek().is_none() {
454+
return Cow::Borrowed(text);
455+
}
444456
let mut new = Vec::with_capacity(text.len());
445457
let mut last_match = 0;
446-
for (i, (s, e)) in self.find_iter(text).enumerate() {
458+
for (i, (s, e)) in it {
447459
if limit > 0 && i >= limit {
448460
break
449461
}
@@ -452,14 +464,18 @@ impl Regex {
452464
last_match = e;
453465
}
454466
extend_from_slice(&mut new, &text[last_match..]);
455-
return new;
467+
return Cow::Owned(new);
456468
}
457469

458470
// The slower path, which we use if the replacement needs access to
459471
// capture groups.
472+
let mut it = self.captures_iter(text).enumerate().peekable();
473+
if it.peek().is_none() {
474+
return Cow::Borrowed(text);
475+
}
460476
let mut new = Vec::with_capacity(text.len());
461477
let mut last_match = 0;
462-
for (i, cap) in self.captures_iter(text).enumerate() {
478+
for (i, cap) in it {
463479
if limit > 0 && i >= limit {
464480
break
465481
}
@@ -470,7 +486,7 @@ impl Regex {
470486
last_match = e;
471487
}
472488
extend_from_slice(&mut new, &text[last_match..]);
473-
new
489+
Cow::Owned(new)
474490
}
475491
}
476492

src/re_unicode.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,11 @@ impl Regex {
478478
/// assert_eq!(result, "$2 $last");
479479
/// # }
480480
/// ```
481-
pub fn replace<R: Replacer>(&self, text: &str, rep: R) -> String {
481+
pub fn replace<'t, R: Replacer>(
482+
&self,
483+
text: &'t str,
484+
rep: R,
485+
) -> Cow<'t, str> {
482486
self.replacen(text, 1, rep)
483487
}
484488

@@ -488,7 +492,11 @@ impl Regex {
488492
///
489493
/// See the documentation for `replace` for details on how to access
490494
/// submatches in the replacement string.
491-
pub fn replace_all<R: Replacer>(&self, text: &str, rep: R) -> String {
495+
pub fn replace_all<'t, R: Replacer>(
496+
&self,
497+
text: &'t str,
498+
rep: R,
499+
) -> Cow<'t, str> {
492500
self.replacen(text, 0, rep)
493501
}
494502

@@ -498,13 +506,12 @@ impl Regex {
498506
///
499507
/// See the documentation for `replace` for details on how to access
500508
/// submatches in the replacement string.
501-
pub fn replacen<R: Replacer>(
509+
pub fn replacen<'t, R: Replacer>(
502510
&self,
503-
text: &str,
511+
text: &'t str,
504512
limit: usize,
505513
mut rep: R,
506-
) -> String {
507-
514+
) -> Cow<'t, str> {
508515
// If we know that the replacement doesn't have any capture expansions,
509516
// then we can fast path. The fast path can make a tremendous
510517
// difference:
@@ -515,9 +522,13 @@ impl Regex {
515522
// replacements inside the replacement string. We just push it
516523
// at each match and be done with it.
517524
if let Some(rep) = rep.no_expansion() {
525+
let mut it = self.find_iter(text).enumerate().peekable();
526+
if it.peek().is_none() {
527+
return Cow::Borrowed(text);
528+
}
518529
let mut new = String::with_capacity(text.len());
519530
let mut last_match = 0;
520-
for (i, (s, e)) in self.find_iter(text).enumerate() {
531+
for (i, (s, e)) in it {
521532
if limit > 0 && i >= limit {
522533
break
523534
}
@@ -526,14 +537,18 @@ impl Regex {
526537
last_match = e;
527538
}
528539
new.push_str(&text[last_match..]);
529-
return new;
540+
return Cow::Owned(new);
530541
}
531542

532543
// The slower path, which we use if the replacement needs access to
533544
// capture groups.
545+
let mut it = self.captures_iter(text).enumerate().peekable();
546+
if it.peek().is_none() {
547+
return Cow::Borrowed(text);
548+
}
534549
let mut new = String::with_capacity(text.len());
535550
let mut last_match = 0;
536-
for (i, cap) in self.captures_iter(text).enumerate() {
551+
for (i, cap) in it {
537552
if limit > 0 && i >= limit {
538553
break
539554
}
@@ -544,7 +559,7 @@ impl Regex {
544559
last_match = e;
545560
}
546561
new.push_str(&text[last_match..]);
547-
new
562+
Cow::Owned(new)
548563
}
549564
}
550565

0 commit comments

Comments
 (0)