Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: use NonZeroU32 for query and fragment start #933

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 115 additions & 39 deletions url/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
use std::net::IpAddr;
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
use std::net::{SocketAddr, ToSocketAddrs};
use std::num::NonZeroU32;
use std::ops::{Range, RangeFrom, RangeTo};
use std::path::{Path, PathBuf};
use std::str;
Expand Down Expand Up @@ -203,9 +204,9 @@
host_end: u32,
host: HostInternal,
port: Option<u16>,
path_start: u32, // Before initial '/', if any
query_start: Option<u32>, // Before '?', unlike Position::QueryStart
fragment_start: Option<u32>, // Before '#', unlike Position::FragmentStart
path_start: u32, // Before initial '/', if any
query_start: Option<NonZeroU32>, // Before '?', unlike Position::QueryStart
fragment_start: Option<NonZeroU32>, // Before '#', unlike Position::FragmentStart
}

/// Full configuration for the URL parser.
Expand Down Expand Up @@ -732,11 +733,11 @@
}
}
if let Some(start) = self.query_start {
assert!(start >= self.path_start);
assert!(start.get() >= self.path_start);
assert_eq!(self.byte_at(start), b'?');
}
if let Some(start) = self.fragment_start {
assert!(start >= self.path_start);
assert!(start.get() >= self.path_start);

Check warning on line 740 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L740

Added line #L740 was not covered by tests
assert_eq!(self.byte_at(start), b'#');
}
if let (Some(query_start), Some(fragment_start)) = (self.query_start, self.fragment_start) {
Expand Down Expand Up @@ -1332,7 +1333,7 @@
match (self.query_start, self.fragment_start) {
(None, None) => self.slice(self.path_start..),
(Some(next_component_start), _) | (None, Some(next_component_start)) => {
self.slice(self.path_start..next_component_start)
self.slice(self.path_start..next_component_start.get())
}
}
}
Expand Down Expand Up @@ -1409,11 +1410,11 @@
(None, _) => None,
(Some(query_start), None) => {
debug_assert!(self.byte_at(query_start) == b'?');
Some(self.slice(query_start + 1..))
Some(self.slice(query_start.saturating_add(1)..))
}
(Some(query_start), Some(fragment_start)) => {
debug_assert!(self.byte_at(query_start) == b'?');
Some(self.slice(query_start + 1..fragment_start))
Some(self.slice(query_start.saturating_add(1)..fragment_start))
}
}
}
Expand Down Expand Up @@ -1482,7 +1483,7 @@
pub fn fragment(&self) -> Option<&str> {
self.fragment_start.map(|start| {
debug_assert!(self.byte_at(start) == b'#');
self.slice(start + 1..)
self.slice(start.saturating_add(1)..)
})
}

Expand Down Expand Up @@ -1520,11 +1521,12 @@
// Remove any previous fragment
if let Some(start) = self.fragment_start {
debug_assert!(self.byte_at(start) == b'#');
self.serialization.truncate(start as usize);
self.truncate(start);
}
// Write the new one
if let Some(input) = fragment {
self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
self.fragment_start =
Some(NonZeroU32::new(to_u32(self.serialization.len()).unwrap()).unwrap());
self.serialization.push('#');
self.mutate(|parser| parser.parse_fragment(parser::Input::new_no_trim(input)))
} else {
Expand All @@ -1536,16 +1538,17 @@
fn take_fragment(&mut self) -> Option<String> {
self.fragment_start.take().map(|start| {
debug_assert!(self.byte_at(start) == b'#');
let fragment = self.slice(start + 1..).to_owned();
self.serialization.truncate(start as usize);
let fragment = self.slice(start.saturating_add(1)..).to_owned();
self.truncate(start);
fragment
})
}

fn restore_already_parsed_fragment(&mut self, fragment: Option<String>) {
if let Some(ref fragment) = fragment {
assert!(self.fragment_start.is_none());
self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
self.fragment_start =
Some(NonZeroU32::new(to_u32(self.serialization.len()).unwrap()).unwrap());
self.serialization.push('#');
self.serialization.push_str(fragment);
}
Expand Down Expand Up @@ -1577,11 +1580,12 @@
// Remove any previous query
if let Some(start) = self.query_start.take() {
debug_assert!(self.byte_at(start) == b'?');
self.serialization.truncate(start as usize);
self.truncate(start);
}
// Write the new query, if any
if let Some(input) = query {
self.query_start = Some(to_u32(self.serialization.len()).unwrap());
self.query_start =
Some(NonZeroU32::new(to_u32(self.serialization.len()).unwrap()).unwrap());
self.serialization.push('?');
let scheme_type = SchemeType::from(self.scheme());
let scheme_end = self.scheme_end;
Expand Down Expand Up @@ -1641,10 +1645,10 @@
let query_start;
if let Some(start) = self.query_start {
debug_assert!(self.byte_at(start) == b'?');
query_start = start as usize;
query_start = start.get() as usize;
} else {
query_start = self.serialization.len();
self.query_start = Some(to_u32(query_start).unwrap());
self.query_start = Some(NonZeroU32::new(to_u32(query_start).unwrap()).unwrap());
self.serialization.push('?');
}

Expand All @@ -1659,7 +1663,7 @@
match (self.query_start, self.fragment_start) {
(Some(i), _) | (None, Some(i)) => {
let after_path = self.slice(i..).to_owned();
self.serialization.truncate(i as usize);
self.truncate(i);
after_path
}
(None, None) => String::new(),
Expand Down Expand Up @@ -1740,9 +1744,10 @@

fn restore_after_path(&mut self, old_after_path_position: u32, after_path: &str) {
let new_after_path_position = to_u32(self.serialization.len()).unwrap();
let adjust = |index: &mut u32| {
*index -= old_after_path_position;
*index += new_after_path_position;
let adjust = |index: &mut NonZeroU32| {
*index =
NonZeroU32::new(index.get() - old_after_path_position + new_after_path_position)
.unwrap();
};
if let Some(ref mut index) = self.query_start {
adjust(index)
Expand Down Expand Up @@ -1835,10 +1840,10 @@
let offset = self.path_start - self.host_end;
self.path_start = self.host_end;
if let Some(ref mut index) = self.query_start {
*index -= offset
*index = NonZeroU32::new(index.get() - offset).unwrap();

Check warning on line 1843 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L1843

Added line #L1843 was not covered by tests
}
if let Some(ref mut index) = self.fragment_start {
*index -= offset
*index = NonZeroU32::new(index.get() - offset).unwrap();

Check warning on line 1846 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L1846

Added line #L1846 was not covered by tests
}
}
(Some(old), Some(new)) if old == new => {}
Expand All @@ -1849,9 +1854,9 @@
let old_path_start = self.path_start;
let new_path_start = to_u32(self.serialization.len()).unwrap();
self.path_start = new_path_start;
let adjust = |index: &mut u32| {
*index -= old_path_start;
*index += new_path_start;
let adjust = |index: &mut NonZeroU32| {
*index =
NonZeroU32::new(index.get() - old_path_start + new_path_start).unwrap();

Check warning on line 1859 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L1858-L1859

Added lines #L1858 - L1859 were not covered by tests
};
if let Some(ref mut index) = self.query_start {
adjust(index)
Expand Down Expand Up @@ -2002,10 +2007,12 @@
self.host_end = new_path_start;
self.port = None;
if let Some(ref mut index) = self.query_start {
*index -= offset
// *index -= offset
*index = NonZeroU32::new(index.get() - offset).unwrap();

Check warning on line 2011 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2011

Added line #L2011 was not covered by tests
}
if let Some(ref mut index) = self.fragment_start {
*index -= offset
// *index -= offset
*index = NonZeroU32::new(index.get() - offset).unwrap();

Check warning on line 2015 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2015

Added line #L2015 was not covered by tests
}
}
Ok(())
Expand Down Expand Up @@ -2045,12 +2052,15 @@
*index -= old_suffix_pos;
*index += new_suffix_pos;
};
let adjust_nonzero = |index: &mut NonZeroU32| {
*index = NonZeroU32::new(index.get() - old_suffix_pos + new_suffix_pos).unwrap();

Check warning on line 2056 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2056

Added line #L2056 was not covered by tests
};
adjust(&mut self.path_start);
if let Some(ref mut index) = self.query_start {
adjust(index)
adjust_nonzero(index)

Check warning on line 2060 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2060

Added line #L2060 was not covered by tests
}
if let Some(ref mut index) = self.fragment_start {
adjust(index)
adjust_nonzero(index)

Check warning on line 2063 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2063

Added line #L2063 was not covered by tests
}
}

Expand Down Expand Up @@ -2152,14 +2162,17 @@
*index -= old_host_start;
*index += new_host_start;
};
let adjust_nonzero = |index: &mut NonZeroU32| {
*index = NonZeroU32::new(index.get() - old_host_start + new_host_start).unwrap();

Check warning on line 2166 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2166

Added line #L2166 was not covered by tests
};
self.host_start = new_host_start;
adjust(&mut self.host_end);
adjust(&mut self.path_start);
if let Some(ref mut index) = self.query_start {
adjust(index)
adjust_nonzero(index)

Check warning on line 2172 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2172

Added line #L2172 was not covered by tests
}
if let Some(ref mut index) = self.fragment_start {
adjust(index)
adjust_nonzero(index)

Check warning on line 2175 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2175

Added line #L2175 was not covered by tests
}

self.serialization.push_str(&host_and_after);
Expand All @@ -2181,10 +2194,10 @@
self.host_end -= offset;
self.path_start -= offset;
if let Some(ref mut index) = self.query_start {
*index -= offset
*index = NonZeroU32::new(index.get() - offset).unwrap();

Check warning on line 2197 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2197

Added line #L2197 was not covered by tests
}
if let Some(ref mut index) = self.fragment_start {
*index -= offset
*index = NonZeroU32::new(index.get() - offset).unwrap();

Check warning on line 2200 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2200

Added line #L2200 was not covered by tests
}
}
Ok(())
Expand Down Expand Up @@ -2265,14 +2278,17 @@
*index -= removed_bytes;
*index += added_bytes;
};
let adjust_nonzero = |index: &mut NonZeroU32| {
*index = NonZeroU32::new(index.get() - removed_bytes + added_bytes).unwrap();

Check warning on line 2282 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2282

Added line #L2282 was not covered by tests
};
adjust(&mut self.host_start);
adjust(&mut self.host_end);
adjust(&mut self.path_start);
if let Some(ref mut index) = self.query_start {
adjust(index)
adjust_nonzero(index)

Check warning on line 2288 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2288

Added line #L2288 was not covered by tests
}
if let Some(ref mut index) = self.fragment_start {
adjust(index)
adjust_nonzero(index)

Check warning on line 2291 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2291

Added line #L2291 was not covered by tests
}
Ok(())
}
Expand Down Expand Up @@ -2413,17 +2429,20 @@
*index -= old_scheme_end;
*index += new_scheme_end;
};
let adjust_nonzero = |index: &mut NonZeroU32| {
*index = NonZeroU32::new(index.get() - old_scheme_end + new_scheme_end).unwrap();

Check warning on line 2433 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2433

Added line #L2433 was not covered by tests
};

self.scheme_end = new_scheme_end;
adjust(&mut self.username_end);
adjust(&mut self.host_start);
adjust(&mut self.host_end);
adjust(&mut self.path_start);
if let Some(ref mut index) = self.query_start {
adjust(index)
adjust_nonzero(index)

Check warning on line 2442 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2442

Added line #L2442 was not covered by tests
}
if let Some(ref mut index) = self.fragment_start {
adjust(index)
adjust_nonzero(index)

Check warning on line 2445 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2445

Added line #L2445 was not covered by tests
}

parser.serialization.push_str(self.slice(old_scheme_end..));
Expand Down Expand Up @@ -2646,13 +2665,49 @@
{
range.slice_of(&self.serialization)
}
}

trait ByteAt<T> {
fn byte_at(&self, i: T) -> u8;
}

impl ByteAt<u32> for Url {
#[inline]
fn byte_at(&self, i: u32) -> u8 {
self.serialization.as_bytes()[i as usize]
}
}

impl ByteAt<NonZeroU32> for Url {
#[inline]
fn byte_at(&self, i: NonZeroU32) -> u8 {
self.serialization.as_bytes()[i.get() as usize]
}
}

trait Truncate<T> {
/// Truncate the URL's serialization to the given length.
fn truncate(&mut self, len: T);
}
impl Truncate<u32> for Url {
#[inline]
fn truncate(&mut self, len: u32) {
self.serialization.truncate(len as usize)

Check warning on line 2695 in url/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

url/src/lib.rs#L2694-L2695

Added lines #L2694 - L2695 were not covered by tests
}
}
impl Truncate<NonZeroU32> for Url {
#[inline]
fn truncate(&mut self, len: NonZeroU32) {
self.serialization.truncate(len.get() as usize)
}
}
impl Truncate<usize> for Url {
#[inline]
fn truncate(&mut self, len: usize) {
self.serialization.truncate(len)
}
}

/// Parse a string as an URL, without a base URL or encoding override.
impl str::FromStr for Url {
type Err = ParseError;
Expand Down Expand Up @@ -2776,6 +2831,27 @@
}
}

impl RangeArg for Range<NonZeroU32> {
#[inline]
fn slice_of<'a>(&self, s: &'a str) -> &'a str {
&s[self.start.get() as usize..self.end.get() as usize]
}
}

impl RangeArg for RangeFrom<NonZeroU32> {
#[inline]
fn slice_of<'a>(&self, s: &'a str) -> &'a str {
&s[self.start.get() as usize..]
}
}

impl RangeArg for RangeTo<NonZeroU32> {
#[inline]
fn slice_of<'a>(&self, s: &'a str) -> &'a str {
&s[..self.end.get() as usize]
}
}

/// Serializes this URL into a `serde` stream.
///
/// This implementation is only available if the `serde` Cargo feature is enabled.
Expand Down
Loading
Loading