Skip to content

Commit c95cb97

Browse files
committed
Make StringOrInit use COW for string variant to avoid a copy when calling
quirks::process_match_input.
1 parent 8365964 commit c95cb97

4 files changed

Lines changed: 41 additions & 34 deletions

File tree

benches/parse_patterns.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ fn bench_parse_shipping_groups_summary(c: &mut Criterion) {
66
b.iter(|| {
77
let input = quirks::process_construct_pattern_input(
88
black_box(quirks::StringOrInit::String(
9-
"component-ShippingGroupsSummary.*.js".to_owned()
9+
"component-ShippingGroupsSummary.*.js".into()
1010
)),
1111
black_box(Some("https://example.test/web/")),
1212
);

src/lib.rs

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -659,10 +659,11 @@ mod tests {
659659

660660
#[derive(Debug, Deserialize)]
661661
#[serde(untagged)]
662+
#[serde(bound(deserialize = "'de: 'a"))]
662663
#[allow(clippy::large_enum_variant)]
663-
enum ExpectedMatch {
664+
enum ExpectedMatch<'a> {
664665
String(String),
665-
MatchResult(MatchResult),
666+
MatchResult(MatchResult<'a>),
666667
}
667668

668669
#[derive(Debug, Deserialize)]
@@ -674,28 +675,30 @@ mod tests {
674675
#[allow(clippy::large_enum_variant)]
675676
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
676677
#[serde(untagged)]
677-
pub enum StringOrInitOrOptions {
678+
pub enum StringOrInitOrOptions<'a> {
678679
Options(UrlPatternOptions),
679-
StringOrInit(quirks::StringOrInit),
680+
StringOrInit(quirks::StringOrInit<'a>),
680681
}
681682

682683
#[derive(Debug, Deserialize)]
683-
struct TestCase {
684+
#[serde(bound(deserialize = "'de: 'a"))]
685+
struct TestCase<'a> {
684686
skip: Option<String>,
685-
pattern: Vec<StringOrInitOrOptions>,
687+
pattern: Vec<StringOrInitOrOptions<'a>>,
686688
#[serde(default)]
687-
inputs: Vec<quirks::StringOrInit>,
688-
expected_obj: Option<quirks::StringOrInit>,
689-
expected_match: Option<ExpectedMatch>,
689+
inputs: Vec<quirks::StringOrInit<'a>>,
690+
expected_obj: Option<quirks::StringOrInit<'a>>,
691+
expected_match: Option<ExpectedMatch<'a>>,
690692
#[serde(default)]
691693
exactly_empty_components: Vec<String>,
692694
}
693695

694696
#[derive(Debug, Deserialize)]
695-
struct MatchResult {
697+
#[serde(bound(deserialize = "'de: 'a"))]
698+
struct MatchResult<'a> {
696699
#[serde(deserialize_with = "deserialize_match_result_inputs")]
697700
#[serde(default)]
698-
inputs: Option<(quirks::StringOrInit, Option<String>)>,
701+
inputs: Option<(quirks::StringOrInit<'a>, Option<String>)>,
699702

700703
protocol: Option<ComponentResult>,
701704
username: Option<ComponentResult>,
@@ -707,17 +710,17 @@ mod tests {
707710
hash: Option<ComponentResult>,
708711
}
709712

710-
fn deserialize_match_result_inputs<'de, D>(
713+
fn deserialize_match_result_inputs<'a, D>(
711714
deserializer: D,
712-
) -> Result<Option<(quirks::StringOrInit, Option<String>)>, D::Error>
715+
) -> Result<Option<(quirks::StringOrInit<'a>, Option<String>)>, D::Error>
713716
where
714-
D: serde::Deserializer<'de>,
717+
D: serde::Deserializer<'a>,
715718
{
716719
#[derive(Debug, Deserialize)]
717720
#[serde(untagged)]
718-
enum MatchResultInputs {
719-
OneArgument((quirks::StringOrInit,)),
720-
TwoArguments(quirks::StringOrInit, String),
721+
enum MatchResultInputs<'a> {
722+
OneArgument((quirks::StringOrInit<'a>,)),
723+
TwoArguments(quirks::StringOrInit<'a>, String),
721724
}
722725

723726
let res = Option::<MatchResultInputs>::deserialize(deserializer)?;
@@ -811,7 +814,7 @@ mod tests {
811814
..
812815
}) = &input
813816
{
814-
base_url = Some(url.clone())
817+
base_url = Some(url.clone().into())
815818
}
816819

817820
macro_rules! assert_field {
@@ -921,7 +924,8 @@ mod tests {
921924

922925
let input = input.unwrap_or_else(|| StringOrInit::Init(Default::default()));
923926

924-
let expected_input = (input.clone(), base_url.clone());
927+
let expected_input =
928+
(input.clone(), base_url.clone().map(|s| s.to_string()));
925929

926930
let match_input = quirks::process_match_input(input, base_url.as_deref());
927931

@@ -1058,7 +1062,7 @@ mod tests {
10581062
#[test]
10591063
fn issue46() {
10601064
quirks::process_construct_pattern_input(
1061-
quirks::StringOrInit::String(":café://:foo".to_owned()),
1065+
quirks::StringOrInit::String(":café://:foo".to_owned().into()),
10621066
None,
10631067
)
10641068
.unwrap();

src/quirks.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
44
use serde::Deserialize;
55
use serde::Serialize;
6+
use std::borrow::Cow;
67
use url::Url;
78

89
pub use crate::Error;
@@ -36,8 +37,8 @@ pub struct UrlPatternInit {
3637
#[allow(clippy::large_enum_variant)]
3738
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3839
#[serde(untagged)]
39-
pub enum StringOrInit {
40-
String(String),
40+
pub enum StringOrInit<'a> {
41+
String(Cow<'a, str>),
4142
Init(UrlPatternInit),
4243
}
4344

@@ -106,7 +107,7 @@ impl<R: RegExp> From<Component<R>> for UrlPatternComponent {
106107
fn from(component: Component<R>) -> Self {
107108
let regexp_string = component
108109
.regexp
109-
.map(|r| r.pattern_string())
110+
.map(|r| r.pattern_string().to_owned())
110111
.unwrap_or_default();
111112
Self {
112113
pattern_string: component.pattern_string,
@@ -166,7 +167,9 @@ impl<R: RegExp> From<crate::matcher::InnerMatcher<R>> for InnerMatcher {
166167
allow_empty,
167168
},
168169
crate::matcher::InnerMatcher::RegExp { regexp } => Self::RegExp {
169-
regexp: regexp.map(|r| r.pattern_string()).unwrap_or_default(),
170+
regexp: regexp
171+
.map(|r| r.pattern_string().to_owned())
172+
.unwrap_or_default(),
170173
},
171174
}
172175
}
@@ -196,8 +199,8 @@ impl RegExp for EcmaRegexp {
196199
regexp.matches(text)
197200
}
198201

199-
fn pattern_string(&self) -> String {
200-
self.0.clone()
202+
fn pattern_string(&self) -> &str {
203+
self.0.as_ref()
201204
}
202205
}
203206

@@ -230,12 +233,12 @@ pub fn parse_pattern_as_lib<R: RegExp>(
230233
Ok(pattern)
231234
}
232235

233-
pub type Inputs = (StringOrInit, Option<String>);
236+
pub type Inputs<'a> = (StringOrInit<'a>, Option<String>);
234237

235-
pub fn process_match_input(
236-
input: StringOrInit,
238+
pub fn process_match_input<'a>(
239+
input: StringOrInit<'a>,
237240
base_url_str: Option<&str>,
238-
) -> Result<Option<(crate::UrlPatternMatchInput, Inputs)>, Error> {
241+
) -> Result<Option<(crate::UrlPatternMatchInput, Inputs<'a>)>, Error> {
239242
let mut inputs = (input.clone(), None);
240243
let init = match input {
241244
StringOrInit::String(url) => {

src/regexp.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub trait RegExp: Sized {
1717
/// Returns `None` if the text does not match the regular expression.
1818
fn matches<'a>(&self, text: &'a str) -> Option<Vec<Option<&'a str>>>;
1919

20-
fn pattern_string(&self) -> String;
20+
fn pattern_string(&self) -> &str;
2121
}
2222

2323
impl RegExp for regex::Regex {
@@ -41,7 +41,7 @@ impl RegExp for regex::Regex {
4141
Some(captures)
4242
}
4343

44-
fn pattern_string(&self) -> String {
45-
self.as_str().to_string()
44+
fn pattern_string(&self) -> &str {
45+
self.as_str()
4646
}
4747
}

0 commit comments

Comments
 (0)