Skip to content

Commit 6a1a406

Browse files
0x676e67Copilot
andauthored
perf(uri): avoid double copying during URI percent encoding (#977)
* perf(uri): avoid double copying during URI percent encoding * Update src/into_uri.rs Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent e54df35 commit 6a1a406

File tree

1 file changed

+56
-52
lines changed

1 file changed

+56
-52
lines changed

src/into_uri.rs

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ impl IntoUri for &[u8] {}
2626
mod sealed {
2727
use std::borrow::Cow;
2828

29+
use bytes::Bytes;
2930
use http::{
3031
Uri,
3132
uri::{Authority, Parts, PathAndQuery, Scheme},
@@ -43,88 +44,91 @@ mod sealed {
4344

4445
impl IntoUriSealed for &[u8] {
4546
fn into_uri(self) -> Result<Uri> {
46-
// 1. try to parse directly
47+
// try to parse directly
4748
let uri = match Uri::try_from(self) {
4849
Ok(uri) => uri,
4950
Err(err) => {
5051
let mut parts = Parts::default();
5152

52-
// 2. parse scheme and rest directly with "://"
53+
// parse scheme and rest directly with "://"
5354
let pos = self
5455
.windows(3)
5556
.position(|window| window == b"://")
5657
.ok_or_else(|| Error::builder(err))?;
5758
let (scheme, rest) = self.split_at(pos);
5859
let rest = &rest[3..];
5960

60-
// 3. parse scheme
61+
// parse scheme
6162
parts.scheme = Scheme::try_from(scheme).map(Some).map_err(Error::builder)?;
6263

63-
// 4. split authority and path_and_query
64+
// split authority and path_and_query
6465
let (authority, path_and_query) = match rest.iter().position(|&b| b == b'/') {
6566
Some(pos) => rest.split_at(pos),
6667
None => (rest, b"" as &[u8]),
6768
};
6869

69-
// 5. parse authority
70+
// parse authority
7071
parts.authority = {
7172
let authority = percent_encoding::percent_encode(authority, USERINFO);
72-
Authority::try_from(Cow::from(authority).as_ref())
73+
Authority::from_maybe_shared(Bytes::from(Cow::from(authority).into_owned()))
7374
.map(Some)
7475
.map_err(Error::builder)?
7576
};
7677

77-
// 6. parse and percent-encode path_and_query
78+
// parse and percent-encode path_and_query
7879
if !path_and_query.is_empty() {
79-
parts.path_and_query = match path_and_query.iter().position(|&b| b == b'?')
80-
{
81-
Some(pos) => {
82-
let (path, query) = path_and_query.split_at(pos);
83-
let encoded_path =
84-
Cow::from(percent_encoding::percent_encode(path, PATH));
85-
let encoded_query =
86-
Cow::from(percent_encoding::percent_encode(&query[1..], QUERY));
87-
88-
let path_and_query = match (encoded_path, encoded_query) {
89-
(Cow::Owned(mut path), query) => {
90-
path.push('?');
91-
path.extend(query.chars());
92-
path
93-
}
94-
(path, Cow::Owned(mut query)) => {
95-
query.reserve(path.len() + 1);
96-
query.insert(0, '?');
97-
query.insert_str(0, &path);
98-
query
99-
}
100-
(Cow::Borrowed(path), Cow::Borrowed(query)) => {
101-
let mut path_and_query =
102-
String::with_capacity(path.len() + query.len() + 1);
103-
path_and_query.push_str(path);
104-
path_and_query.push('?');
105-
path_and_query.push_str(query);
106-
path_and_query
107-
}
108-
};
109-
110-
PathAndQuery::from_maybe_shared(path_and_query)
80+
const PQ_SPLIT: char = '?';
81+
82+
parts.path_and_query =
83+
match path_and_query.iter().position(|&b| b == (PQ_SPLIT as u8)) {
84+
Some(pos) => {
85+
let (path, query) = path_and_query.split_at(pos);
86+
let encoded_path =
87+
Cow::from(percent_encoding::percent_encode(path, PATH));
88+
let encoded_query = Cow::from(
89+
percent_encoding::percent_encode(&query[1..], QUERY),
90+
);
91+
92+
let path_and_query = match (encoded_path, encoded_query) {
93+
(Cow::Owned(mut path), query) => {
94+
path.push(PQ_SPLIT);
95+
path.extend(query.chars());
96+
path
97+
}
98+
(path, Cow::Owned(mut query)) => {
99+
query.reserve(path.len() + 1);
100+
query.insert(0, PQ_SPLIT);
101+
query.insert_str(0, &path);
102+
query
103+
}
104+
(Cow::Borrowed(path), Cow::Borrowed(query)) => {
105+
let mut path_and_query =
106+
String::with_capacity(path.len() + query.len() + 1);
107+
path_and_query.push_str(path);
108+
path_and_query.push(PQ_SPLIT);
109+
path_and_query.push_str(query);
110+
path_and_query
111+
}
112+
};
113+
114+
PathAndQuery::from_maybe_shared(Bytes::from(path_and_query))
115+
.map(Some)
116+
.map_err(Error::builder)?
117+
}
118+
None => {
119+
let encoded_path =
120+
percent_encoding::percent_encode(path_and_query, PATH);
121+
122+
PathAndQuery::from_maybe_shared(Bytes::from(
123+
Cow::from(encoded_path).into_owned(),
124+
))
111125
.map(Some)
112126
.map_err(Error::builder)?
113-
}
114-
None => {
115-
let encoded_path =
116-
percent_encoding::percent_encode(path_and_query, PATH);
117-
118-
PathAndQuery::from_maybe_shared(
119-
Cow::from(encoded_path).into_owned(),
120-
)
121-
.map(Some)
122-
.map_err(Error::builder)?
123-
}
124-
};
127+
}
128+
};
125129
}
126130

127-
// 7. Reconstruct Uri
131+
// Reconstruct Uri
128132
Uri::from_parts(parts).map_err(Error::builder)?
129133
}
130134
};

0 commit comments

Comments
 (0)