Skip to content

Commit 7b72f3f

Browse files
perf(parse_regex): revert zero-copy slicing, keep pre-computed capture names
1 parent 533f5d1 commit 7b72f3f

3 files changed

Lines changed: 21 additions & 72 deletions

File tree

src/stdlib/parse_regex.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,12 @@ contains the whole match.",
2525
]
2626
});
2727

28-
fn parse_regex(
29-
bytes: &bytes::Bytes,
30-
pattern: &Regex,
31-
capture_names: &[KeyString],
32-
numeric_groups: bool,
33-
) -> Resolved {
34-
util::with_utf8_bytes(bytes, |s, utf8_bytes| {
35-
let parsed = pattern.captures(s).map(|capture| {
36-
util::capture_regex_to_map(&capture, capture_names, numeric_groups, utf8_bytes)
37-
});
38-
Ok(parsed.ok_or("could not find any pattern matches")?.into())
39-
})
28+
fn parse_regex(value: &Value, pattern: &Regex, capture_names: &[KeyString], numeric_groups: bool) -> Resolved {
29+
let value = value.try_bytes_utf8_lossy()?;
30+
let parsed = pattern
31+
.captures(value.as_ref())
32+
.map(|capture| util::capture_regex_to_map(&capture, capture_names, numeric_groups));
33+
Ok(parsed.ok_or("could not find any pattern matches")?.into())
4034
}
4135

4236
#[derive(Clone, Copy, Debug)]
@@ -183,12 +177,7 @@ impl FunctionExpression for ParseRegexFn {
183177
.numeric_groups
184178
.map_resolve_with_default(ctx, || DEFAULT_NUMERIC_GROUPS.clone())?
185179
.try_boolean()?;
186-
parse_regex(
187-
&value.try_bytes()?,
188-
&self.pattern,
189-
&self.capture_names,
190-
numeric_groups,
191-
)
180+
parse_regex(&value, &self.pattern, &self.capture_names, numeric_groups)
192181
}
193182

194183
fn type_def(&self, _: &state::TypeState) -> TypeDef {

src/stdlib/parse_regex_all.rs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,13 @@ contains the whole match.")
1717
]
1818
});
1919

20-
fn parse_regex_all(
21-
bytes: &bytes::Bytes,
22-
pattern: &Regex,
23-
capture_names: &[KeyString],
24-
numeric_groups: bool,
25-
) -> Resolved {
26-
util::with_utf8_bytes(bytes, |s, utf8_bytes| {
27-
let result: Vec<Value> = pattern
28-
.captures_iter(s)
29-
.map(|capture| {
30-
util::capture_regex_to_map(&capture, capture_names, numeric_groups, utf8_bytes)
31-
.into()
32-
})
33-
.collect();
34-
Ok(result.into())
35-
})
20+
fn parse_regex_all(value: &Value, pattern: &Regex, capture_names: &[KeyString], numeric_groups: bool) -> Resolved {
21+
let value = value.try_bytes_utf8_lossy()?;
22+
let result: Vec<Value> = pattern
23+
.captures_iter(value.as_ref())
24+
.map(|capture| util::capture_regex_to_map(&capture, capture_names, numeric_groups).into())
25+
.collect();
26+
Ok(result.into())
3627
}
3728

3829
#[derive(Clone, Copy, Debug)]
@@ -192,8 +183,6 @@ impl FunctionExpression for ParseRegexAllFn {
192183
.as_regex()
193184
.ok_or_else(|| ExpressionError::from("failed to resolve regex"))?
194185
.clone();
195-
let bytes = value.try_bytes()?;
196-
197186
let capture_names: &[KeyString] = if let Some(names) = &self.capture_names {
198187
names.as_slice()
199188
} else {
@@ -204,7 +193,7 @@ impl FunctionExpression for ParseRegexAllFn {
204193
.collect::<Vec<_>>()
205194
};
206195

207-
parse_regex_all(&bytes, &pattern, capture_names, numeric_groups)
196+
parse_regex_all(&value, &pattern, capture_names, numeric_groups)
208197
}
209198

210199
fn type_def(&self, state: &state::TypeState) -> TypeDef {

src/stdlib/util.rs

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,6 @@ where
2727
fun(num * multiplier) / multiplier
2828
}
2929

30-
/// Presents `bytes` as `(&str, &Bytes)` for regex matching and zero-copy slicing.
31-
///
32-
/// When `bytes` is valid UTF-8 both references point to the original buffer.
33-
/// Otherwise a lossy copy is allocated and both references point to that copy,
34-
/// keeping the regex byte offsets aligned with the buffer used for slicing.
35-
/// The closure receives `(s, utf8_bytes)` and its return value is forwarded.
36-
pub(crate) fn with_utf8_bytes<F, T>(bytes: &bytes::Bytes, f: F) -> T
37-
where
38-
F: FnOnce(&str, &bytes::Bytes) -> T,
39-
{
40-
let owned;
41-
let (s, utf8_bytes): (&str, &bytes::Bytes) = if let Ok(s) = std::str::from_utf8(bytes) {
42-
(s, bytes)
43-
} else {
44-
owned = bytes::Bytes::from(String::from_utf8_lossy(bytes).into_owned());
45-
(
46-
std::str::from_utf8(&owned).expect("lossy string is valid UTF-8"),
47-
&owned,
48-
)
49-
};
50-
f(s, utf8_bytes)
51-
}
52-
5330
/// Fills an [`ObjectMap`] from a regex [`Captures`](regex::Captures).
5431
///
5532
/// Named captures are inserted under their group name; numeric groups (when
@@ -59,31 +36,25 @@ where
5936
/// `capture_names` must be the pre-computed slice of named-group
6037
/// [`KeyString`]s for the regex (computed once at VRL compile time via
6138
/// `regex.capture_names().flatten().map(KeyString::from)`).
62-
///
63-
/// `utf8_bytes` must be the UTF-8 buffer the regex was run against (as produced by
64-
/// [`with_utf8_bytes`]). Each matched substring is returned as a zero-copy
65-
/// [`bytes::Bytes`] slice of that buffer.
6639
pub(crate) fn capture_regex_to_map(
6740
capture: &regex::Captures,
6841
capture_names: &[KeyString],
6942
numeric_groups: bool,
70-
utf8_bytes: &bytes::Bytes,
7143
) -> ObjectMap {
7244
let names = capture_names.iter().map(|name| {
7345
let value: Value = match capture.name(name.as_str()) {
74-
Some(m) => utf8_bytes.slice(m.start()..m.end()).into(),
46+
Some(m) => m.as_str().into(),
7547
None => Value::Null,
7648
};
7749
(name.clone(), value)
7850
});
7951

8052
if numeric_groups {
81-
let indexed = capture.iter().flatten().enumerate().map(|(idx, c)| {
82-
(
83-
KeyString::from(idx.to_string()),
84-
utf8_bytes.slice(c.start()..c.end()).into(),
85-
)
86-
});
53+
let indexed = capture
54+
.iter()
55+
.flatten()
56+
.enumerate()
57+
.map(|(idx, c)| (KeyString::from(idx.to_string()), c.as_str().into()));
8758
indexed.chain(names).collect()
8859
} else {
8960
names.collect()

0 commit comments

Comments
 (0)