Skip to content
Merged
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
97 changes: 44 additions & 53 deletions library/core/src/net/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,6 @@ impl<'a> Parser<'a> {
if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(kind))
}

/// Peek the next character from the input
fn peek_char(&self) -> Option<char> {
self.state.first().map(|&b| char::from(b))
}

/// Reads the next character from the input
fn read_char(&mut self) -> Option<char> {
self.state.split_first().map(|(&b, tail)| {
Expand Down Expand Up @@ -100,60 +95,56 @@ impl<'a> Parser<'a> {
})
}

// Read a number off the front of the input in the given radix, stopping
// at the first non-digit character or eof. Fails if the number has more
// digits than max_digits or if there is no number.
//
// INVARIANT: `max_digits` must be less than the number of digits that `u32`
// can represent.
fn read_number<T: ReadNumberHelper + TryFrom<u32>>(
/// Reads a number off the front of the input in the given radix, stopping at the first
/// non-digit character or eof. Fails if the number has more digits than `max_digits`, if there
/// is no number, if the number overflows `T`, or if there are leading zeros but
/// `allow_zero_prefix` is false.
///
/// `max_digits` must be in 1..=6.
fn read_radix_max_digits<T: ReadNumberHelper + TryFrom<u32>>(
&mut self,
radix: u32,
max_digits: Option<usize>,
max_digits: u32,
allow_zero_prefix: bool,
) -> Option<T> {
self.read_atomically(move |p| {
let mut digit_count = 0;
let has_leading_zero = p.peek_char() == Some('0');

// If max_digits.is_some(), then we are parsing a `u8` or `u16` and
// don't need to use checked arithmetic since it fits within a `u32`.
let result = if let Some(max_digits) = max_digits {
// u32::MAX = 4_294_967_295u32, which is 10 digits long.
// `max_digits` must be less than 10 to not overflow a `u32`.
debug_assert!(max_digits < 10);

let mut result = 0_u32;
while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) {
result *= radix;
result += digit;
digit_count += 1;

if digit_count > max_digits {
return None;
}
}

result.try_into().ok()
} else {
let mut result = T::ZERO;
debug_assert!(1 <= max_digits);
debug_assert!(max_digits <= 6); // Works for any radix in u32
self.read_atomically(|p| {
let first = p.read_char()?.to_digit(radix)?;
let mut result = first;
let mut digit_count = 1;

while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) {
result = result.checked_mul(radix)?;
result = result.checked_add(digit)?;
digit_count += 1;
while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) {
if digit_count >= max_digits {
return None;
}
result *= radix;
result += digit;
digit_count += 1;
}

Some(result)
};

if digit_count == 0 {
None
} else if !allow_zero_prefix && has_leading_zero && digit_count > 1 {
if !allow_zero_prefix && first == 0 && digit_count > 1 {
None
} else {
result
result.try_into().ok()
}
})
}

/// Reads a decimal number off the front of the input, stopping at the first non-digit character
/// or eof. Fails if there is no number, or if the number overflows `T`. Allows an arbitrary
/// amount of leading zeros.
fn read_decimal<T: ReadNumberHelper>(&mut self) -> Option<T> {
self.read_atomically(|p| {
let first = p.read_char()?.to_digit(10)?;
let mut result = T::ZERO.checked_add(first)?;

while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(10)) {
result = result.checked_mul(10)?;
result = result.checked_add(digit)?;
}

Some(result)
})
}

Expand All @@ -166,7 +157,7 @@ impl<'a> Parser<'a> {
*slot = p.read_separator('.', i, |p| {
// Disallow octal number in IP string.
// https://tools.ietf.org/html/rfc6943#section-3.1.1
p.read_number(10, Some(3), false)
p.read_radix_max_digits(10, 3, false)
})?;
}

Expand Down Expand Up @@ -198,7 +189,7 @@ impl<'a> Parser<'a> {
}
}

let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true));
let group = p.read_separator(':', i, |p| p.read_radix_max_digits(16, 4, true));

match group {
Some(g) => *slot = g,
Expand Down Expand Up @@ -250,15 +241,15 @@ impl<'a> Parser<'a> {
fn read_port(&mut self) -> Option<u16> {
self.read_atomically(|p| {
p.read_given_char(':')?;
p.read_number(10, None, true)
p.read_decimal()
})
}

/// Reads a `%` followed by a scope ID in base 10.
fn read_scope_id(&mut self) -> Option<u32> {
self.read_atomically(|p| {
p.read_given_char('%')?;
p.read_number(10, None, true)
p.read_decimal()
})
}

Expand Down
176 changes: 155 additions & 21 deletions library/coretests/benches/net/addr_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,76 +3,210 @@ use core::str::FromStr;

use test::{Bencher, black_box};

const IPV4_STR: &str = "192.168.0.1";
const IPV4_STR_PORT: &str = "192.168.0.1:8080";

const IPV6_STR_FULL: &str = "2001:db8:0:0:0:0:c0a8:1";
const IPV6_STR_COMPRESS: &str = "2001:db8::c0a8:1";
const IPV6_STR_V4: &str = "2001:db8::192.168.0.1";
const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080";
const IPV6_STR_PORT_SCOPE_ID: &str = "[2001:db8::c0a8:1%1337]:8080";
const IPV4: &[&str] = &[
"192.168.0.1",
"8.8.8.8",
"127.0.0.1",
"255.255.255.255",
"0.0.0.0",
"10.0.0.1",
"203.0.113.42",
"172.16.254.1",
"100.64.0.1",
"1.2.3.4",
];

const IPV4_PORT: &[&str] = &[
"192.168.0.1:8080",
"8.8.8.8:53",
"127.0.0.1:65535",
"255.255.255.255:1",
"0.0.0.0:80",
"10.0.0.1:443",
"203.0.113.42:22",
"172.16.254.1:3306",
"100.64.0.1:8443",
"1.2.3.4:0",
];

const IPV6_FULL: &[&str] = &[
"2001:db8:0:0:0:0:c0a8:1",
"2001:db8:85a3:8d3:1319:8a2e:370:7348",
"fe80:0:0:0:0:0:0:1",
"ff02:0:0:0:0:0:0:101",
"2001:4860:4860:0:0:0:0:8888",
"2606:4700:4700:0:0:0:0:1111",
"fd00:0:0:0:0:0:0:1",
"fec0:0:0:0:0:0:0:abcd",
"1:2:3:4:5:6:7:8",
"abcd:ef01:2345:6789:abcd:ef01:2345:6789",
];

const IPV6_COMPRESS: &[&str] = &[
"2001:db8::c0a8:1",
"::1",
"fe80::1",
"2001:db8::",
"ff02::1:2",
"64:ff9b::",
"::",
"2001:4860:4860::8888",
"2606:4700:4700::1111",
"fe80::1ff:fe23:4567:890a",
];

const IPV6_V4: &[&str] = &[
"2001:db8::192.168.0.1",
"::ffff:192.168.0.1",
"64:ff9b::192.0.2.33",
"::ffff:8.8.8.8",
"::192.168.0.1",
"::ffff:255.255.255.255",
"2001:db8:0:0:0:0:192.168.0.1",
"64:ff9b::10.0.0.1",
"::ffff:1.2.3.4",
"::ffff:127.0.0.1",
];

const IPV6_PORT: &[&str] = &[
"[2001:db8::c0a8:1]:8080",
"[::1]:443",
"[fe80::1]:53",
"[2001:db8:85a3:8d3:1319:8a2e:370:7348]:22",
"[::]:80",
"[2001:4860:4860::8888]:443",
"[2606:4700:4700::1111]:53",
"[fe80::1ff:fe23:4567:890a]:8080",
"[64:ff9b::192.0.2.33]:443",
"[::ffff:8.8.8.8]:53",
];

const IPV6_PORT_SCOPE_ID: &[&str] = &[
"[2001:db8::c0a8:1%1337]:8080",
"[fe80::1%1]:53",
"[fe80::1%999999]:443",
"[fe80::1%0]:80",
"[fe80::1ff:fe23:4567:890a%2]:8080",
"[::1%1]:443",
"[fe80::abcd%15]:22",
"[ff02::1%42]:5353",
"[fe80::1%4294967295]:443",
"[2001:db8::1%100]:8080",
];

#[bench]
fn bench_parse_ipv4(b: &mut Bencher) {
b.iter(|| Ipv4Addr::from_str(black_box(IPV4_STR)));
b.iter(|| {
for s in IPV4 {
let _ = black_box(Ipv4Addr::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_ipv6_full(b: &mut Bencher) {
b.iter(|| Ipv6Addr::from_str(black_box(IPV6_STR_FULL)));
b.iter(|| {
for s in IPV6_FULL {
let _ = black_box(Ipv6Addr::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_ipv6_compress(b: &mut Bencher) {
b.iter(|| Ipv6Addr::from_str(black_box(IPV6_STR_COMPRESS)));
b.iter(|| {
for s in IPV6_COMPRESS {
let _ = black_box(Ipv6Addr::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_ipv6_v4(b: &mut Bencher) {
b.iter(|| Ipv6Addr::from_str(black_box(IPV6_STR_V4)));
b.iter(|| {
for s in IPV6_V4 {
let _ = black_box(Ipv6Addr::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_ipaddr_v4(b: &mut Bencher) {
b.iter(|| IpAddr::from_str(black_box(IPV4_STR)));
b.iter(|| {
for s in IPV4 {
let _ = black_box(IpAddr::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_ipaddr_v6_full(b: &mut Bencher) {
b.iter(|| IpAddr::from_str(black_box(IPV6_STR_FULL)));
b.iter(|| {
for s in IPV6_FULL {
let _ = black_box(IpAddr::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_ipaddr_v6_compress(b: &mut Bencher) {
b.iter(|| IpAddr::from_str(black_box(IPV6_STR_COMPRESS)));
b.iter(|| {
for s in IPV6_COMPRESS {
let _ = black_box(IpAddr::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_ipaddr_v6_v4(b: &mut Bencher) {
b.iter(|| IpAddr::from_str(black_box(IPV6_STR_V4)));
b.iter(|| {
for s in IPV6_V4 {
let _ = black_box(IpAddr::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_socket_v4(b: &mut Bencher) {
b.iter(|| SocketAddrV4::from_str(black_box(IPV4_STR_PORT)));
b.iter(|| {
for s in IPV4_PORT {
let _ = black_box(SocketAddrV4::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_socket_v6(b: &mut Bencher) {
b.iter(|| SocketAddrV6::from_str(black_box(IPV6_STR_PORT)));
b.iter(|| {
for s in IPV6_PORT {
let _ = black_box(SocketAddrV6::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_socket_v6_scope_id(b: &mut Bencher) {
b.iter(|| SocketAddrV6::from_str(black_box(IPV6_STR_PORT_SCOPE_ID)));
b.iter(|| {
for s in IPV6_PORT_SCOPE_ID {
let _ = black_box(SocketAddrV6::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_socketaddr_v4(b: &mut Bencher) {
b.iter(|| SocketAddr::from_str(black_box(IPV4_STR_PORT)));
b.iter(|| {
for s in IPV4_PORT {
let _ = black_box(SocketAddr::from_str(black_box(s)));
}
});
}

#[bench]
fn bench_parse_socketaddr_v6(b: &mut Bencher) {
b.iter(|| SocketAddr::from_str(black_box(IPV6_STR_PORT)));
b.iter(|| {
for s in IPV6_PORT {
let _ = black_box(SocketAddr::from_str(black_box(s)));
}
});
}
Loading