Skip to content

Commit de561a9

Browse files
authored
Rollup merge of #72407 - Lucretiel:ipv6-display, r=Mark-Simulacrum
Various minor improvements to Ipv6Addr::Display Cleaned up `Ipv6Addr::Display`, especially with an eye towards simplifying and reducing duplicated logic. Also added a fast-path optimization, similar to #72399 and #72398. - Defer to `Ipv4Addr::fmt` when printing an Ipv4 address - Fast path: write directly to `f` without an intermediary buffer when there are no alignment options - Simplify finding the inner zeroes-span
2 parents e229d6e + 44ca3da commit de561a9

File tree

1 file changed

+78
-80
lines changed

1 file changed

+78
-80
lines changed

src/libstd/net/ip.rs

+78-80
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
)]
88

99
use crate::cmp::Ordering;
10-
use crate::fmt;
10+
use crate::fmt::{self, Write as FmtWrite};
1111
use crate::hash;
12-
use crate::io::Write;
12+
use crate::io::Write as IoWrite;
1313
use crate::sys::net::netc as c;
1414
use crate::sys_common::{AsInner, FromInner};
1515

@@ -1532,102 +1532,100 @@ impl Ipv6Addr {
15321532
}
15331533
}
15341534

1535+
/// Write an Ipv6Addr, conforming to the canonical style described by
1536+
/// [RFC 5952](https://tools.ietf.org/html/rfc5952).
15351537
#[stable(feature = "rust1", since = "1.0.0")]
15361538
impl fmt::Display for Ipv6Addr {
1537-
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1538-
// Note: The calls to write should never fail, hence the unwraps in the function
1539-
// Long enough for the longest possible IPv6: 39
1540-
const IPV6_BUF_LEN: usize = 39;
1541-
let mut buf = [0u8; IPV6_BUF_LEN];
1542-
let mut buf_slice = &mut buf[..];
1539+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1540+
// If there are no alignment requirements, write out the IP address to
1541+
// f. Otherwise, write it to a local buffer, then use f.pad.
1542+
if f.precision().is_none() && f.width().is_none() {
1543+
let segments = self.segments();
1544+
1545+
// Special case for :: and ::1; otherwise they get written with the
1546+
// IPv4 formatter
1547+
if self.is_unspecified() {
1548+
f.write_str("::")
1549+
} else if self.is_loopback() {
1550+
f.write_str("::1")
1551+
} else if let Some(ipv4) = self.to_ipv4() {
1552+
match segments[5] {
1553+
// IPv4 Compatible address
1554+
0 => write!(f, "::{}", ipv4),
1555+
// IPv4 Mapped address
1556+
0xffff => write!(f, "::ffff:{}", ipv4),
1557+
_ => unreachable!(),
1558+
}
1559+
} else {
1560+
#[derive(Copy, Clone, Default)]
1561+
struct Span {
1562+
start: usize,
1563+
len: usize,
1564+
}
15431565

1544-
match self.segments() {
1545-
// We need special cases for :: and ::1, otherwise they're formatted
1546-
// as ::0.0.0.[01]
1547-
[0, 0, 0, 0, 0, 0, 0, 0] => write!(buf_slice, "::").unwrap(),
1548-
[0, 0, 0, 0, 0, 0, 0, 1] => write!(buf_slice, "::1").unwrap(),
1549-
// Ipv4 Compatible address
1550-
[0, 0, 0, 0, 0, 0, g, h] => {
1551-
write!(
1552-
buf_slice,
1553-
"::{}.{}.{}.{}",
1554-
(g >> 8) as u8,
1555-
g as u8,
1556-
(h >> 8) as u8,
1557-
h as u8
1558-
)
1559-
.unwrap();
1560-
}
1561-
// Ipv4-Mapped address
1562-
[0, 0, 0, 0, 0, 0xffff, g, h] => {
1563-
write!(
1564-
buf_slice,
1565-
"::ffff:{}.{}.{}.{}",
1566-
(g >> 8) as u8,
1567-
g as u8,
1568-
(h >> 8) as u8,
1569-
h as u8
1570-
)
1571-
.unwrap();
1572-
}
1573-
_ => {
1574-
fn find_zero_slice(segments: &[u16; 8]) -> (usize, usize) {
1575-
let mut longest_span_len = 0;
1576-
let mut longest_span_at = 0;
1577-
let mut cur_span_len = 0;
1578-
let mut cur_span_at = 0;
1579-
1580-
for i in 0..8 {
1581-
if segments[i] == 0 {
1582-
if cur_span_len == 0 {
1583-
cur_span_at = i;
1566+
// Find the inner 0 span
1567+
let zeroes = {
1568+
let mut longest = Span::default();
1569+
let mut current = Span::default();
1570+
1571+
for (i, &segment) in segments.iter().enumerate() {
1572+
if segment == 0 {
1573+
if current.len == 0 {
1574+
current.start = i;
15841575
}
15851576

1586-
cur_span_len += 1;
1577+
current.len += 1;
15871578

1588-
if cur_span_len > longest_span_len {
1589-
longest_span_len = cur_span_len;
1590-
longest_span_at = cur_span_at;
1579+
if current.len > longest.len {
1580+
longest = current;
15911581
}
15921582
} else {
1593-
cur_span_len = 0;
1594-
cur_span_at = 0;
1583+
current = Span::default();
15951584
}
15961585
}
15971586

1598-
(longest_span_at, longest_span_len)
1599-
}
1600-
1601-
let (zeros_at, zeros_len) = find_zero_slice(&self.segments());
1602-
1603-
if zeros_len > 1 {
1604-
fn fmt_subslice(segments: &[u16], buf: &mut &mut [u8]) {
1605-
if !segments.is_empty() {
1606-
write!(*buf, "{:x}", segments[0]).unwrap();
1607-
for &seg in &segments[1..] {
1608-
write!(*buf, ":{:x}", seg).unwrap();
1609-
}
1587+
longest
1588+
};
1589+
1590+
/// Write a colon-separated part of the address
1591+
#[inline]
1592+
fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result {
1593+
if let Some(first) = chunk.first() {
1594+
fmt::LowerHex::fmt(first, f)?;
1595+
for segment in &chunk[1..] {
1596+
f.write_char(':')?;
1597+
fmt::LowerHex::fmt(segment, f)?;
16101598
}
16111599
}
1600+
Ok(())
1601+
}
16121602

1613-
fmt_subslice(&self.segments()[..zeros_at], &mut buf_slice);
1614-
write!(buf_slice, "::").unwrap();
1615-
fmt_subslice(&self.segments()[zeros_at + zeros_len..], &mut buf_slice);
1603+
if zeroes.len > 1 {
1604+
fmt_subslice(f, &segments[..zeroes.start])?;
1605+
f.write_str("::")?;
1606+
fmt_subslice(f, &segments[zeroes.start + zeroes.len..])
16161607
} else {
1617-
let &[a, b, c, d, e, f, g, h] = &self.segments();
1618-
write!(
1619-
buf_slice,
1620-
"{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
1621-
a, b, c, d, e, f, g, h
1622-
)
1623-
.unwrap();
1608+
fmt_subslice(f, &segments)
16241609
}
16251610
}
1611+
} else {
1612+
// Slow path: write the address to a local buffer, the use f.pad.
1613+
// Defined recursively by using the fast path to write to the
1614+
// buffer.
1615+
1616+
// This is the largest possible size of an IPv6 address
1617+
const IPV6_BUF_LEN: usize = (4 * 8) + 7;
1618+
let mut buf = [0u8; IPV6_BUF_LEN];
1619+
let mut buf_slice = &mut buf[..];
1620+
1621+
// Note: This call to write should never fail, so unwrap is okay.
1622+
write!(buf_slice, "{}", self).unwrap();
1623+
let len = IPV6_BUF_LEN - buf_slice.len();
1624+
1625+
// This is safe because we know exactly what can be in this buffer
1626+
let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
1627+
f.pad(buf)
16261628
}
1627-
let len = IPV6_BUF_LEN - buf_slice.len();
1628-
// This is safe because we know exactly what can be in this buffer
1629-
let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
1630-
fmt.pad(buf)
16311629
}
16321630
}
16331631

0 commit comments

Comments
 (0)