Skip to content

Use DisplayBuffer for socket addresses. #100640

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

Merged
merged 7 commits into from
Sep 13, 2022
Merged
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
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -214,6 +214,7 @@ symbols! {
IntoIterator,
IoRead,
IoWrite,
IpAddr,
IrTyKind,
Is,
ItemContext,
Original file line number Diff line number Diff line change
@@ -3,12 +3,12 @@ use crate::mem::MaybeUninit;
use crate::str;

/// Used for slow path in `Display` implementations when alignment is required.
pub struct IpDisplayBuffer<const SIZE: usize> {
pub struct DisplayBuffer<const SIZE: usize> {
buf: [MaybeUninit<u8>; SIZE],
len: usize,
}

impl<const SIZE: usize> IpDisplayBuffer<SIZE> {
impl<const SIZE: usize> DisplayBuffer<SIZE> {
#[inline]
pub const fn new() -> Self {
Self { buf: MaybeUninit::uninit_array(), len: 0 }
@@ -25,7 +25,7 @@ impl<const SIZE: usize> IpDisplayBuffer<SIZE> {
}
}

impl<const SIZE: usize> fmt::Write for IpDisplayBuffer<SIZE> {
impl<const SIZE: usize> fmt::Write for DisplayBuffer<SIZE> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();

10 changes: 6 additions & 4 deletions library/std/src/net/ip.rs → library/std/src/net/ip_addr.rs
Original file line number Diff line number Diff line change
@@ -8,8 +8,7 @@ use crate::mem::transmute;
use crate::sys::net::netc as c;
use crate::sys_common::{FromInner, IntoInner};

mod display_buffer;
use display_buffer::IpDisplayBuffer;
use super::display_buffer::DisplayBuffer;

/// An IP address, either IPv4 or IPv6.
///
@@ -30,6 +29,7 @@ use display_buffer::IpDisplayBuffer;
/// assert_eq!(localhost_v4.is_ipv6(), false);
/// assert_eq!(localhost_v4.is_ipv4(), true);
/// ```
#[cfg_attr(not(test), rustc_diagnostic_item = "IpAddr")]
#[stable(feature = "ip_addr", since = "1.7.0")]
#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub enum IpAddr {
@@ -73,6 +73,7 @@ pub enum IpAddr {
/// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex
/// ```
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(not(test), rustc_diagnostic_item = "Ipv4Addr")]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Ipv4Addr {
octets: [u8; 4],
@@ -155,6 +156,7 @@ pub struct Ipv4Addr {
/// assert_eq!(localhost.is_loopback(), true);
/// ```
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(not(test), rustc_diagnostic_item = "Ipv6Addr")]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Ipv6Addr {
octets: [u8; 16],
@@ -997,7 +999,7 @@ impl fmt::Display for Ipv4Addr {
} else {
const LONGEST_IPV4_ADDR: &str = "255.255.255.255";

let mut buf = IpDisplayBuffer::<{ LONGEST_IPV4_ADDR.len() }>::new();
let mut buf = DisplayBuffer::<{ LONGEST_IPV4_ADDR.len() }>::new();
// Buffer is long enough for the longest possible IPv4 address, so this should never fail.
write!(buf, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap();

@@ -1844,7 +1846,7 @@ impl fmt::Display for Ipv6Addr {
} else {
const LONGEST_IPV6_ADDR: &str = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff";

let mut buf = IpDisplayBuffer::<{ LONGEST_IPV6_ADDR.len() }>::new();
let mut buf = DisplayBuffer::<{ LONGEST_IPV6_ADDR.len() }>::new();
// Buffer is long enough for the longest possible IPv6 address, so this should never fail.
write!(buf, "{}", self).unwrap();

File renamed without changes.
11 changes: 6 additions & 5 deletions library/std/src/net/mod.rs
Original file line number Diff line number Diff line change
@@ -24,21 +24,22 @@
use crate::io::{self, ErrorKind};

#[stable(feature = "rust1", since = "1.0.0")]
pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::parser::AddrParseError;
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
#[unstable(feature = "tcplistener_into_incoming", issue = "88339")]
pub use self::tcp::IntoIncoming;
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::tcp::{Incoming, TcpListener, TcpStream};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::udp::UdpSocket;

mod addr;
mod ip;
mod display_buffer;
mod ip_addr;
mod parser;
mod socket_addr;
mod tcp;
#[cfg(test)]
pub(crate) mod test;
58 changes: 22 additions & 36 deletions library/std/src/net/addr.rs → library/std/src/net/socket_addr.rs
Original file line number Diff line number Diff line change
@@ -2,9 +2,9 @@
mod tests;

use crate::cmp::Ordering;
use crate::fmt;
use crate::fmt::{self, Write};
use crate::hash;
use crate::io::{self, Write};
use crate::io;
use crate::iter;
use crate::mem;
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr};
@@ -15,6 +15,8 @@ use crate::sys_common::net::LookupHost;
use crate::sys_common::{FromInner, IntoInner};
use crate::vec;

use super::display_buffer::DisplayBuffer;

/// An internet socket address, either IPv4 or IPv6.
///
/// Internet socket addresses consist of an [IP address], a 16-bit port number, as well
@@ -616,25 +618,18 @@ impl fmt::Debug for SocketAddr {
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for SocketAddrV4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Fast path: if there's no alignment stuff, write to the output buffer
// directly
// If there are no alignment requirements, write the socket address directly to `f`.
// Otherwise, write it to a local buffer and then use `f.pad`.
if f.precision().is_none() && f.width().is_none() {
write!(f, "{}:{}", self.ip(), self.port())
} else {
const IPV4_SOCKET_BUF_LEN: usize = (3 * 4) // the segments
+ 3 // the separators
+ 1 + 5; // the port
let mut buf = [0; IPV4_SOCKET_BUF_LEN];
let mut buf_slice = &mut buf[..];

// Unwrap is fine because writing to a sufficiently-sized
// buffer is infallible
write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap();
let len = IPV4_SOCKET_BUF_LEN - buf_slice.len();

// This unsafe is OK because we know what is being written to the buffer
let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
f.pad(buf)
const LONGEST_IPV4_SOCKET_ADDR: &str = "255.255.255.255:65536";

let mut buf = DisplayBuffer::<{ LONGEST_IPV4_SOCKET_ADDR.len() }>::new();
// Buffer is long enough for the longest possible IPv4 socket address, so this should never fail.
write!(buf, "{}:{}", self.ip(), self.port()).unwrap();

f.pad(buf.as_str())
}
}
}
@@ -649,35 +644,26 @@ impl fmt::Debug for SocketAddrV4 {
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for SocketAddrV6 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Fast path: if there's no alignment stuff, write to the output
// buffer directly
// If there are no alignment requirements, write the socket address directly to `f`.
// Otherwise, write it to a local buffer and then use `f.pad`.
if f.precision().is_none() && f.width().is_none() {
match self.scope_id() {
0 => write!(f, "[{}]:{}", self.ip(), self.port()),
scope_id => write!(f, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
}
} else {
const IPV6_SOCKET_BUF_LEN: usize = (4 * 8) // The address
+ 7 // The colon separators
+ 2 // The brackets
+ 1 + 10 // The scope id
+ 1 + 5; // The port

let mut buf = [0; IPV6_SOCKET_BUF_LEN];
let mut buf_slice = &mut buf[..];
const LONGEST_IPV6_SOCKET_ADDR: &str =
"[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967296]:65536";

let mut buf = DisplayBuffer::<{ LONGEST_IPV6_SOCKET_ADDR.len() }>::new();
match self.scope_id() {
0 => write!(buf_slice, "[{}]:{}", self.ip(), self.port()),
scope_id => write!(buf_slice, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
0 => write!(buf, "[{}]:{}", self.ip(), self.port()),
scope_id => write!(buf, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
}
// Unwrap is fine because writing to a sufficiently-sized
// buffer is infallible
// Buffer is long enough for the longest possible IPv6 socket address, so this should never fail.
.unwrap();
let len = IPV6_SOCKET_BUF_LEN - buf_slice.len();

// This unsafe is OK because we know what is being written to the buffer
let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
f.pad(buf)
f.pad(buf.as_str())
}
}
}
Original file line number Diff line number Diff line change
@@ -51,6 +51,75 @@ fn to_socket_addr_string() {
// s has been moved into the tsa call
}

#[test]
fn ipv4_socket_addr_to_string() {
// Shortest possible IPv4 length.
assert_eq!(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0).to_string(), "0.0.0.0:0");

// Longest possible IPv4 length.
assert_eq!(
SocketAddrV4::new(Ipv4Addr::new(255, 255, 255, 255), u16::MAX).to_string(),
"255.255.255.255:65535"
);

// Test padding.
assert_eq!(
&format!("{:16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
"1.1.1.1:53 "
);
assert_eq!(
&format!("{:>16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
" 1.1.1.1:53"
);
}

#[test]
fn ipv6_socket_addr_to_string() {
// IPv4-mapped address.
assert_eq!(
SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280), 8080, 0, 0)
.to_string(),
"[::ffff:192.0.2.128]:8080"
);

// IPv4-compatible address.
assert_eq!(
SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(),
"[::192.0.2.128]:8080"
);

// IPv6 address with no zero segments.
assert_eq!(
SocketAddrV6::new(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15), 80, 0, 0).to_string(),
"[8:9:a:b:c:d:e:f]:80"
);

// Shortest possible IPv6 length.
assert_eq!(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0).to_string(), "[::]:0");

// Longest possible IPv6 length.
assert_eq!(
SocketAddrV6::new(
Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888),
u16::MAX,
u32::MAX,
u32::MAX,
)
.to_string(),
"[1111:2222:3333:4444:5555:6666:7777:8888%4294967295]:65535"
);

// Test padding.
assert_eq!(
&format!("{:22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)),
"[1:2:3:4:5:6:7:8]:9 "
);
assert_eq!(
&format!("{:>22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)),
" [1:2:3:4:5:6:7:8]:9"
);
}

#[test]
fn bind_udp_socket_bad() {
// rust-lang/rust#53957: This is a regression test for a parsing problem
Original file line number Diff line number Diff line change
@@ -2,17 +2,17 @@ use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::needs_ordered_drop;
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
use clippy_utils::visitors::any_temporaries_need_ordered_drop;
use clippy_utils::{higher, is_lang_ctor, is_trait_method, match_def_path, paths};
use clippy_utils::{higher, is_lang_ctor, is_trait_method};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, PollPending};
use rustc_hir::LangItem::{self, OptionSome, OptionNone, PollPending, PollReady, ResultOk, ResultErr};
use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
use rustc_span::sym;
use rustc_span::{sym, Symbol};

pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
@@ -75,9 +75,9 @@ fn find_sugg_for_if_let<'tcx>(
("is_some()", op_ty)
} else if Some(id) == lang_items.poll_ready_variant() {
("is_ready()", op_ty)
} else if match_def_path(cx, id, &paths::IPADDR_V4) {
} else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V4))) {
("is_ipv4()", op_ty)
} else if match_def_path(cx, id, &paths::IPADDR_V6) {
} else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V6))) {
("is_ipv6()", op_ty)
} else {
return;
@@ -187,8 +187,8 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
arms,
path_left,
path_right,
&paths::RESULT_OK,
&paths::RESULT_ERR,
Item::Lang(ResultOk),
Item::Lang(ResultErr),
"is_ok()",
"is_err()",
)
@@ -198,8 +198,8 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
arms,
path_left,
path_right,
&paths::IPADDR_V4,
&paths::IPADDR_V6,
Item::Diag(sym::IpAddr, sym!(V4)),
Item::Diag(sym::IpAddr, sym!(V6)),
"is_ipv4()",
"is_ipv6()",
)
@@ -213,13 +213,14 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
if patterns.len() == 1 =>
{
if let PatKind::Wild = patterns[0].kind {

find_good_method_for_match(
cx,
arms,
path_left,
path_right,
&paths::OPTION_SOME,
&paths::OPTION_NONE,
Item::Lang(OptionSome),
Item::Lang(OptionNone),
"is_some()",
"is_none()",
)
@@ -229,8 +230,8 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
arms,
path_left,
path_right,
&paths::POLL_READY,
&paths::POLL_PENDING,
Item::Lang(PollReady),
Item::Lang(PollPending),
"is_ready()",
"is_pending()",
)
@@ -266,28 +267,61 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
}
}

#[derive(Clone, Copy)]
enum Item {
Lang(LangItem),
Diag(Symbol, Symbol),
}

fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expected_item: Item) -> bool {
let Some(id) = cx.typeck_results().qpath_res(path, pat.hir_id).opt_def_id() else { return false };

match expected_item {
Item::Lang(expected_lang_item) => {
let expected_id = cx.tcx.lang_items().require(expected_lang_item).unwrap();
cx.tcx.parent(id) == expected_id
},
Item::Diag(expected_ty, expected_variant) => {
let ty = cx.typeck_results().pat_ty(pat);

if is_type_diagnostic_item(cx, ty, expected_ty) {
let variant = ty.ty_adt_def()
.expect("struct pattern type is not an ADT")
.variant_of_res(cx.qpath_res(path, pat.hir_id));

return variant.name == expected_variant
}

false
}
}
}

#[expect(clippy::too_many_arguments)]
fn find_good_method_for_match<'a>(
cx: &LateContext<'_>,
arms: &[Arm<'_>],
path_left: &QPath<'_>,
path_right: &QPath<'_>,
expected_left: &[&str],
expected_right: &[&str],
expected_item_left: Item,
expected_item_right: Item,
should_be_left: &'a str,
should_be_right: &'a str,
) -> Option<&'a str> {
let left_id = cx
.typeck_results()
.qpath_res(path_left, arms[0].pat.hir_id)
.opt_def_id()?;
let right_id = cx
.typeck_results()
.qpath_res(path_right, arms[1].pat.hir_id)
.opt_def_id()?;
let body_node_pair = if match_def_path(cx, left_id, expected_left) && match_def_path(cx, right_id, expected_right) {
let pat_left = arms[0].pat;
let pat_right = arms[1].pat;

let body_node_pair = if (
is_pat_variant(cx, pat_left, path_left, expected_item_left)
) && (
is_pat_variant(cx, pat_right, path_right, expected_item_right)
) {
(&arms[0].body.kind, &arms[1].body.kind)
} else if match_def_path(cx, right_id, expected_left) && match_def_path(cx, right_id, expected_right) {
} else if (
is_pat_variant(cx, pat_left, path_left, expected_item_right)
) && (
is_pat_variant(cx, pat_right, path_right, expected_item_left)
) {
(&arms[1].body.kind, &arms[0].body.kind)
} else {
return None;
2 changes: 0 additions & 2 deletions src/tools/clippy/clippy_utils/src/paths.rs
Original file line number Diff line number Diff line change
@@ -66,8 +66,6 @@ pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];