Skip to content

Commit

Permalink
Use Cow for replacements.
Browse files Browse the repository at this point in the history
If `replace` doesn't find any matches, then it can return the original
string unchanged.
  • Loading branch information
BurntSushi committed May 18, 2016
1 parent 2f408a2 commit 5effd65
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 32 deletions.
4 changes: 2 additions & 2 deletions examples/shootout-regex-dna-bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn main() {
io::stdin().read_to_end(&mut seq).unwrap();
let ilen = seq.len();

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

Expand Down Expand Up @@ -56,7 +56,7 @@ fn main() {
];
let mut seq = seq;
for (re, replacement) in substs.into_iter() {
seq = re.replace_all(&seq, replacement);
seq = re.replace_all(&seq, replacement).into_owned();
}

for (variant, count) in counts {
Expand Down
2 changes: 1 addition & 1 deletion examples/shootout-regex-dna-cheat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn main() {
io::stdin().read_to_string(&mut seq).unwrap();
let ilen = seq.len();

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

Expand Down
2 changes: 1 addition & 1 deletion examples/shootout-regex-dna-replace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ fn main() {
io::stdin().read_to_string(&mut seq).unwrap();
let ilen = seq.len();

seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "");
seq = regex!(">[^\n]*\n|\n").replace_all(&seq, "").into_owned();
println!("original: {}, replaced: {}", ilen, seq.len());
}
2 changes: 1 addition & 1 deletion examples/shootout-regex-dna-single-cheat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() {
io::stdin().read_to_string(&mut seq).unwrap();
let ilen = seq.len();

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

let variants = vec![
Expand Down
4 changes: 2 additions & 2 deletions examples/shootout-regex-dna-single.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() {
io::stdin().read_to_string(&mut seq).unwrap();
let ilen = seq.len();

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

let variants = vec![
Expand Down Expand Up @@ -49,7 +49,7 @@ fn main() {
];
let mut seq = seq;
for (re, replacement) in substs.into_iter() {
seq = re.replace_all(&seq, replacement);
seq = re.replace_all(&seq, replacement).into_owned();
}
println!("\n{}\n{}\n{}", ilen, clen, seq.len());
}
4 changes: 2 additions & 2 deletions examples/shootout-regex-dna.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn main() {
io::stdin().read_to_string(&mut seq).unwrap();
let ilen = seq.len();

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

Expand Down Expand Up @@ -56,7 +56,7 @@ fn main() {
];
let mut seq = seq;
for (re, replacement) in substs.into_iter() {
seq = re.replace_all(&seq, replacement);
seq = re.replace_all(&seq, replacement).into_owned();
}

for (variant, count) in counts {
Expand Down
42 changes: 29 additions & 13 deletions src/re_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ impl Regex {
/// # extern crate regex; use regex::bytes::Regex;
/// # fn main() {
/// let re = Regex::new("[^01]+").unwrap();
/// assert_eq!(re.replace(b"1078910", &b""[..]), b"1010");
/// assert_eq!(re.replace(b"1078910", &b""[..]), &b"1010"[..]);
/// # }
/// ```
///
Expand All @@ -372,7 +372,7 @@ impl Regex {
/// replacement.extend(&caps[1]);
/// replacement
/// });
/// assert_eq!(result, b"Bruce Springsteen");
/// assert_eq!(result, &b"Bruce Springsteen"[..]);
/// # }
/// ```
///
Expand All @@ -386,7 +386,7 @@ impl Regex {
/// # fn main() {
/// let re = Regex::new(r"(?P<last>[^,\s]+),\s+(?P<first>\S+)").unwrap();
/// let result = re.replace(b"Springsteen, Bruce", &b"$first $last"[..]);
/// assert_eq!(result, b"Bruce Springsteen");
/// assert_eq!(result, &b"Bruce Springsteen"[..]);
/// # }
/// ```
///
Expand All @@ -411,10 +411,14 @@ impl Regex {
///
/// let re = Regex::new(r"(?P<last>[^,\s]+),\s+(\S+)").unwrap();
/// let result = re.replace(b"Springsteen, Bruce", NoExpand(b"$2 $last"));
/// assert_eq!(result, b"$2 $last");
/// assert_eq!(result, &b"$2 $last"[..]);
/// # }
/// ```
pub fn replace<R: Replacer>(&self, text: &[u8], rep: R) -> Vec<u8> {
pub fn replace<'t, R: Replacer>(
&self,
text: &'t [u8],
rep: R,
) -> Cow<'t, [u8]> {
self.replacen(text, 1, rep)
}

Expand All @@ -424,7 +428,11 @@ impl Regex {
///
/// See the documentation for `replace` for details on how to access
/// submatches in the replacement text.
pub fn replace_all<R: Replacer>(&self, text: &[u8], rep: R) -> Vec<u8> {
pub fn replace_all<'t, R: Replacer>(
&self,
text: &'t [u8],
rep: R,
) -> Cow<'t, [u8]> {
self.replacen(text, 0, rep)
}

Expand All @@ -434,16 +442,20 @@ impl Regex {
///
/// See the documentation for `replace` for details on how to access
/// submatches in the replacement text.
pub fn replacen<R: Replacer>(
pub fn replacen<'t, R: Replacer>(
&self,
text: &[u8],
text: &'t [u8],
limit: usize,
mut rep: R,
) -> Vec<u8> {
) -> Cow<'t, [u8]> {
if let Some(rep) = rep.no_expansion() {
let mut it = self.find_iter(text).enumerate().peekable();
if it.peek().is_none() {
return Cow::Borrowed(text);
}
let mut new = Vec::with_capacity(text.len());
let mut last_match = 0;
for (i, (s, e)) in self.find_iter(text).enumerate() {
for (i, (s, e)) in it {
if limit > 0 && i >= limit {
break
}
Expand All @@ -452,14 +464,18 @@ impl Regex {
last_match = e;
}
extend_from_slice(&mut new, &text[last_match..]);
return new;
return Cow::Owned(new);
}

// The slower path, which we use if the replacement needs access to
// capture groups.
let mut it = self.captures_iter(text).enumerate().peekable();
if it.peek().is_none() {
return Cow::Borrowed(text);
}
let mut new = Vec::with_capacity(text.len());
let mut last_match = 0;
for (i, cap) in self.captures_iter(text).enumerate() {
for (i, cap) in it {
if limit > 0 && i >= limit {
break
}
Expand All @@ -470,7 +486,7 @@ impl Regex {
last_match = e;
}
extend_from_slice(&mut new, &text[last_match..]);
new
Cow::Owned(new)
}
}

Expand Down
35 changes: 25 additions & 10 deletions src/re_unicode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,11 @@ impl Regex {
/// assert_eq!(result, "$2 $last");
/// # }
/// ```
pub fn replace<R: Replacer>(&self, text: &str, rep: R) -> String {
pub fn replace<'t, R: Replacer>(
&self,
text: &'t str,
rep: R,
) -> Cow<'t, str> {
self.replacen(text, 1, rep)
}

Expand All @@ -488,7 +492,11 @@ impl Regex {
///
/// See the documentation for `replace` for details on how to access
/// submatches in the replacement string.
pub fn replace_all<R: Replacer>(&self, text: &str, rep: R) -> String {
pub fn replace_all<'t, R: Replacer>(
&self,
text: &'t str,
rep: R,
) -> Cow<'t, str> {
self.replacen(text, 0, rep)
}

Expand All @@ -498,13 +506,12 @@ impl Regex {
///
/// See the documentation for `replace` for details on how to access
/// submatches in the replacement string.
pub fn replacen<R: Replacer>(
pub fn replacen<'t, R: Replacer>(
&self,
text: &str,
text: &'t str,
limit: usize,
mut rep: R,
) -> String {

) -> Cow<'t, str> {
// If we know that the replacement doesn't have any capture expansions,
// then we can fast path. The fast path can make a tremendous
// difference:
Expand All @@ -515,9 +522,13 @@ impl Regex {
// replacements inside the replacement string. We just push it
// at each match and be done with it.
if let Some(rep) = rep.no_expansion() {
let mut it = self.find_iter(text).enumerate().peekable();
if it.peek().is_none() {
return Cow::Borrowed(text);
}
let mut new = String::with_capacity(text.len());
let mut last_match = 0;
for (i, (s, e)) in self.find_iter(text).enumerate() {
for (i, (s, e)) in it {
if limit > 0 && i >= limit {
break
}
Expand All @@ -526,14 +537,18 @@ impl Regex {
last_match = e;
}
new.push_str(&text[last_match..]);
return new;
return Cow::Owned(new);
}

// The slower path, which we use if the replacement needs access to
// capture groups.
let mut it = self.captures_iter(text).enumerate().peekable();
if it.peek().is_none() {
return Cow::Borrowed(text);
}
let mut new = String::with_capacity(text.len());
let mut last_match = 0;
for (i, cap) in self.captures_iter(text).enumerate() {
for (i, cap) in it {
if limit > 0 && i >= limit {
break
}
Expand All @@ -544,7 +559,7 @@ impl Regex {
last_match = e;
}
new.push_str(&text[last_match..]);
new
Cow::Owned(new)
}
}

Expand Down

0 comments on commit 5effd65

Please sign in to comment.