Skip to content

Commit 94edb27

Browse files
authored
Optimize socket address handling to avoid temporary Vecs. (#669)
Use `transmute` to convert between `&[u8]` and `&[c_char]` instead of copying the data into a temporary `Vec`.
1 parent eac93e7 commit 94edb27

File tree

5 files changed

+110
-116
lines changed

5 files changed

+110
-116
lines changed

src/backend/libc/net/addr.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl SocketAddrUnix {
6363
if 1 + name.len() > unix.sun_path.len() {
6464
return Err(io::Errno::NAMETOOLONG);
6565
}
66-
unix.sun_path[0] = b'\0' as c::c_char;
66+
unix.sun_path[0] = 0;
6767
for (i, b) in name.iter().enumerate() {
6868
unix.sun_path[1 + i] = *b as c::c_char;
6969
}
@@ -94,7 +94,7 @@ impl SocketAddrUnix {
9494
#[inline]
9595
pub fn path(&self) -> Option<&CStr> {
9696
let len = self.len();
97-
if len != 0 && self.unix.sun_path[0] != b'\0' as c::c_char {
97+
if len != 0 && self.unix.sun_path[0] != 0 {
9898
let end = len as usize - offsetof_sun_path();
9999
let bytes = &self.unix.sun_path[..end];
100100
// SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
@@ -116,7 +116,7 @@ impl SocketAddrUnix {
116116
#[inline]
117117
pub fn abstract_name(&self) -> Option<&[u8]> {
118118
let len = self.len();
119-
if len != 0 && self.unix.sun_path[0] == b'\0' as c::c_char {
119+
if len != 0 && self.unix.sun_path[0] == 0 {
120120
let end = len as usize - offsetof_sun_path();
121121
let bytes = &self.unix.sun_path[1..end];
122122
// SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.

src/backend/libc/net/ext.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,15 @@ pub(crate) fn in6_addr_new(s6_addr: [u8; 16]) -> c::in6_addr {
105105

106106
#[cfg(not(windows))]
107107
#[inline]
108-
pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: c::sockaddr_in6) -> u32 {
108+
pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: &c::sockaddr_in6) -> u32 {
109109
addr.sin6_scope_id
110110
}
111111

112112
#[cfg(not(feature = "std"))]
113113
#[cfg(windows)]
114114
#[inline]
115-
pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: c::sockaddr_in6) -> u32 {
116-
let addr: sockaddr_in6 = unsafe { core::mem::transmute(addr) };
115+
pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: &c::sockaddr_in6) -> u32 {
116+
let addr: &sockaddr_in6 = unsafe { core::mem::transmute(addr) };
117117
addr.sin6_scope_id
118118
}
119119

@@ -122,8 +122,8 @@ pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: c::sockaddr_in6) -> u32 {
122122
#[cfg(feature = "std")]
123123
#[cfg(windows)]
124124
#[inline]
125-
pub(crate) fn sockaddr_in6_sin6_scope_id(addr: c::sockaddr_in6) -> u32 {
126-
let addr: sockaddr_in6 = unsafe { core::mem::transmute(addr) };
125+
pub(crate) fn sockaddr_in6_sin6_scope_id(addr: &c::sockaddr_in6) -> u32 {
126+
let addr: &sockaddr_in6 = unsafe { core::mem::transmute(addr) };
127127
addr.sin6_scope_id
128128
}
129129

src/backend/libc/net/read_sockaddr.rs

Lines changed: 58 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ use super::ext::{in6_addr_s6_addr, in_addr_s_addr, sockaddr_in6_sin6_scope_id};
66
use crate::ffi::CStr;
77
use crate::io;
88
use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
9-
#[cfg(not(windows))]
10-
use alloc::vec::Vec;
119
use core::mem::size_of;
1210

1311
// This must match the header of `sockaddr`.
@@ -61,7 +59,7 @@ pub(crate) unsafe fn read_sockaddr(
6159
if len < size_of::<c::sockaddr_in>() {
6260
return Err(io::Errno::INVAL);
6361
}
64-
let decode = *storage.cast::<c::sockaddr_in>();
62+
let decode = &*storage.cast::<c::sockaddr_in>();
6563
Ok(SocketAddrAny::V4(SocketAddrV4::new(
6664
Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
6765
u16::from_be(decode.sin_port),
@@ -71,7 +69,7 @@ pub(crate) unsafe fn read_sockaddr(
7169
if len < size_of::<c::sockaddr_in6>() {
7270
return Err(io::Errno::INVAL);
7371
}
74-
let decode = *storage.cast::<c::sockaddr_in6>();
72+
let decode = &*storage.cast::<c::sockaddr_in6>();
7573
#[cfg(not(windows))]
7674
let s6_addr = decode.sin6_addr.s6_addr;
7775
#[cfg(windows)]
@@ -93,20 +91,40 @@ pub(crate) unsafe fn read_sockaddr(
9391
return Err(io::Errno::INVAL);
9492
}
9593
if len == offsetof_sun_path {
96-
Ok(SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap()))
94+
SocketAddrUnix::new(&[][..]).map(SocketAddrAny::Unix)
9795
} else {
98-
let decode = *storage.cast::<c::sockaddr_un>();
96+
let decode = &*storage.cast::<c::sockaddr_un>();
97+
98+
// On Linux check for Linux's [abstract namespace].
99+
//
100+
// [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
101+
#[cfg(any(target_os = "android", target_os = "linux"))]
102+
if decode.sun_path[0] == 0 {
103+
return SocketAddrUnix::new_abstract_name(core::mem::transmute::<
104+
&[c::c_char],
105+
&[u8],
106+
>(
107+
&decode.sun_path[1..len - offsetof_sun_path],
108+
))
109+
.map(SocketAddrAny::Unix);
110+
}
111+
112+
// Otherwise we expect a NUL-terminated filesystem path.
99113

100114
// Trim off unused bytes from the end of `path_bytes`.
101115
let path_bytes = if cfg!(target_os = "freebsd") {
102116
// FreeBSD sometimes sets the length to longer than the length
103117
// of the NUL-terminated string. Find the NUL and truncate the
104118
// string accordingly.
105-
&decode.sun_path[..decode.sun_path.iter().position(|b| *b == 0).unwrap()]
119+
&decode.sun_path[..decode
120+
.sun_path
121+
.iter()
122+
.position(|b| *b == 0)
123+
.ok_or(io::Errno::INVAL)?]
106124
} else {
107125
// Otherwise, use the provided length.
108126
let provided_len = len - 1 - offsetof_sun_path;
109-
if decode.sun_path[provided_len] != b'\0' as c::c_char {
127+
if decode.sun_path[provided_len] != 0 {
110128
return Err(io::Errno::INVAL);
111129
}
112130
debug_assert_eq!(
@@ -116,10 +134,8 @@ pub(crate) unsafe fn read_sockaddr(
116134
&decode.sun_path[..provided_len]
117135
};
118136

119-
Ok(SocketAddrAny::Unix(
120-
SocketAddrUnix::new(path_bytes.iter().map(|c| *c as u8).collect::<Vec<u8>>())
121-
.unwrap(),
122-
))
137+
SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
138+
.map(SocketAddrAny::Unix)
123139
}
124140
}
125141
_ => Err(io::Errno::INVAL),
@@ -164,15 +180,15 @@ unsafe fn inner_read_sockaddr_os(
164180
match family {
165181
c::AF_INET => {
166182
assert!(len >= size_of::<c::sockaddr_in>());
167-
let decode = *storage.cast::<c::sockaddr_in>();
183+
let decode = &*storage.cast::<c::sockaddr_in>();
168184
SocketAddrAny::V4(SocketAddrV4::new(
169185
Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
170186
u16::from_be(decode.sin_port),
171187
))
172188
}
173189
c::AF_INET6 => {
174190
assert!(len >= size_of::<c::sockaddr_in6>());
175-
let decode = *storage.cast::<c::sockaddr_in6>();
191+
let decode = &*storage.cast::<c::sockaddr_in6>();
176192
SocketAddrAny::V6(SocketAddrV6::new(
177193
Ipv6Addr::from(in6_addr_s6_addr(decode.sin6_addr)),
178194
u16::from_be(decode.sin6_port),
@@ -186,51 +202,38 @@ unsafe fn inner_read_sockaddr_os(
186202
if len == offsetof_sun_path {
187203
SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap())
188204
} else {
189-
#[cfg(not(any(target_os = "android", target_os = "linux")))]
190-
fn try_decode_abstract_socket(
191-
_sockaddr: &c::sockaddr_un,
192-
_len: usize,
193-
) -> Option<SocketAddrUnix> {
194-
None
195-
}
205+
let decode = &*storage.cast::<c::sockaddr_un>();
206+
207+
// On Linux check for Linux's [abstract namespace].
208+
//
209+
// [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
196210
#[cfg(any(target_os = "android", target_os = "linux"))]
197-
fn try_decode_abstract_socket(
198-
decode: &c::sockaddr_un,
199-
len: usize,
200-
) -> Option<SocketAddrUnix> {
201-
if decode.sun_path[0] != 0 {
202-
None
203-
} else {
204-
let offsetof_sun_path = super::addr::offsetof_sun_path();
205-
let address_bytes = &decode.sun_path[1..len - offsetof_sun_path];
206-
Some(
207-
SocketAddrUnix::new_abstract_name(
208-
&address_bytes.iter().map(|c| *c as u8).collect::<Vec<u8>>(),
209-
)
210-
.unwrap(),
211-
)
212-
}
211+
if decode.sun_path[0] == 0 {
212+
return SocketAddrAny::Unix(
213+
SocketAddrUnix::new_abstract_name(core::mem::transmute::<
214+
&[c::c_char],
215+
&[u8],
216+
>(
217+
&decode.sun_path[1..len - offsetof_sun_path],
218+
))
219+
.unwrap(),
220+
);
213221
}
214222

215-
let decode = *storage.cast::<c::sockaddr_un>();
216-
let result = try_decode_abstract_socket(&decode, len).unwrap_or_else(|| {
217-
assert_eq!(
218-
decode.sun_path[len - 1 - offsetof_sun_path],
219-
b'\0' as c::c_char
220-
);
221-
let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path];
223+
// Otherwise we expect a NUL-terminated filesystem path.
224+
assert_eq!(decode.sun_path[len - 1 - offsetof_sun_path], 0);
225+
let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path];
222226

223-
// FreeBSD sometimes sets the length to longer than the length
224-
// of the NUL-terminated string. Find the NUL and truncate the
225-
// string accordingly.
226-
#[cfg(target_os = "freebsd")]
227-
let path_bytes =
228-
&path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()];
229-
230-
SocketAddrUnix::new(path_bytes.iter().map(|c| *c as u8).collect::<Vec<u8>>())
231-
.unwrap()
232-
});
233-
SocketAddrAny::Unix(result)
227+
// FreeBSD sometimes sets the length to longer than the length
228+
// of the NUL-terminated string. Find the NUL and truncate the
229+
// string accordingly.
230+
#[cfg(target_os = "freebsd")]
231+
let path_bytes = &path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()];
232+
233+
SocketAddrAny::Unix(
234+
SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
235+
.unwrap(),
236+
)
234237
}
235238
}
236239
other => unimplemented!("{:?}", other),

src/backend/linux_raw/net/addr.rs

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
//!
33
//! # Safety
44
//!
5-
//! This file casts between `&[c_char]` used in C APIs and `&[u8]` used in Rust
6-
//! APIs.
5+
//! This file uses `CStr::from_bytes_with_nul_unchecked` on a string it knows
6+
//! to be NUL-terminated.
77
#![allow(unsafe_code)]
88

99
use super::super::c;
1010
use crate::ffi::CStr;
1111
use crate::{io, path};
1212
use core::cmp::Ordering;
1313
use core::convert::TryInto;
14+
use core::fmt;
1415
use core::hash::{Hash, Hasher};
15-
use core::{fmt, slice};
1616

1717
/// `struct sockaddr_un`
1818
#[derive(Clone)]
@@ -37,7 +37,7 @@ impl SocketAddrUnix {
3737
return Err(io::Errno::NAMETOOLONG);
3838
}
3939
for (i, b) in bytes.iter().enumerate() {
40-
unix.sun_path[i] = *b as c::c_char;
40+
unix.sun_path[i] = *b;
4141
}
4242
let len = offsetof_sun_path() + bytes.len();
4343
let len = len.try_into().unwrap();
@@ -48,16 +48,15 @@ impl SocketAddrUnix {
4848
#[inline]
4949
pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> {
5050
let mut unix = Self::init();
51-
if 1 + name.len() > unix.sun_path.len() {
51+
let id = &mut unix.sun_path[1..];
52+
if let Some(id) = id.get_mut(..name.len()) {
53+
id.copy_from_slice(name);
54+
let len = offsetof_sun_path() + 1 + name.len();
55+
let len = len.try_into().unwrap();
56+
Ok(Self { unix, len })
57+
} else {
5258
return Err(io::Errno::NAMETOOLONG);
5359
}
54-
unix.sun_path[0] = b'\0' as c::c_char;
55-
for (i, b) in name.iter().enumerate() {
56-
unix.sun_path[1 + i] = *b as c::c_char;
57-
}
58-
let len = offsetof_sun_path() + 1 + name.len();
59-
let len = len.try_into().unwrap();
60-
Ok(Self { unix, len })
6160
}
6261

6362
fn init() -> c::sockaddr_un {
@@ -71,18 +70,12 @@ impl SocketAddrUnix {
7170
#[inline]
7271
pub fn path(&self) -> Option<&CStr> {
7372
let len = self.len();
74-
if len != 0 && self.unix.sun_path[0] != b'\0' as c::c_char {
73+
if len != 0 && self.unix.sun_path[0] != b'\0' {
7574
let end = len as usize - offsetof_sun_path();
7675
let bytes = &self.unix.sun_path[..end];
77-
// SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
78-
// And `from_bytes_with_nul_unchecked` since the string is
76+
// SAFETY: `from_bytes_with_nul_unchecked` since the string is
7977
// NUL-terminated.
80-
unsafe {
81-
Some(CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
82-
bytes.as_ptr().cast(),
83-
bytes.len(),
84-
)))
85-
}
78+
unsafe { Some(CStr::from_bytes_with_nul_unchecked(bytes)) }
8679
} else {
8780
None
8881
}
@@ -92,11 +85,9 @@ impl SocketAddrUnix {
9285
#[inline]
9386
pub fn abstract_name(&self) -> Option<&[u8]> {
9487
let len = self.len();
95-
if len != 0 && self.unix.sun_path[0] == b'\0' as c::c_char {
88+
if len != 0 && self.unix.sun_path[0] == b'\0' {
9689
let end = len as usize - offsetof_sun_path();
97-
let bytes = &self.unix.sun_path[1..end];
98-
// SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
99-
unsafe { Some(slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len())) }
90+
Some(&self.unix.sun_path[1..end])
10091
} else {
10192
None
10293
}

0 commit comments

Comments
 (0)