#![allow(unsafe_code)]
use core::mem::{size_of, MaybeUninit};
use core::ptr;
use crate::backend::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};
use crate::utils::as_ptr;
use super::{SocketAddr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
pub use crate::backend::net::addr::SocketAddrStorage;
#[cfg(unix)]
use super::SocketAddrUnix;
#[repr(C)]
pub struct SocketAddrOpaque {
_data: [u8; 0],
}
#[doc(alias = "socklen_t")]
pub type SocketAddrLen = u32;
pub unsafe trait SocketAddrArg {
unsafe fn with_sockaddr<R>(
&self,
f: impl FnOnce(*const SocketAddrOpaque, SocketAddrLen) -> R,
) -> R;
fn as_any(&self) -> SocketAddrAny {
let mut storage = MaybeUninit::<SocketAddrStorage>::uninit();
unsafe {
let len = self.write_sockaddr(storage.as_mut_ptr());
SocketAddrAny::new(storage, len)
}
}
unsafe fn write_sockaddr(&self, storage: *mut SocketAddrStorage) -> SocketAddrLen {
self.with_sockaddr(|ptr, len| {
ptr::copy_nonoverlapping(ptr.cast::<u8>(), storage.cast::<u8>(), len as usize);
len
})
}
}
pub(crate) unsafe fn call_with_sockaddr<A, R>(
addr: &A,
f: impl FnOnce(*const SocketAddrOpaque, SocketAddrLen) -> R,
) -> R {
let ptr = as_ptr(addr).cast();
let len = size_of::<A>() as SocketAddrLen;
f(ptr, len)
}
unsafe impl SocketAddrArg for SocketAddr {
unsafe fn with_sockaddr<R>(
&self,
f: impl FnOnce(*const SocketAddrOpaque, SocketAddrLen) -> R,
) -> R {
match self {
Self::V4(v4) => v4.with_sockaddr(f),
Self::V6(v6) => v6.with_sockaddr(f),
}
}
}
unsafe impl SocketAddrArg for SocketAddrV4 {
unsafe fn with_sockaddr<R>(
&self,
f: impl FnOnce(*const SocketAddrOpaque, SocketAddrLen) -> R,
) -> R {
call_with_sockaddr(&encode_sockaddr_v4(self), f)
}
}
unsafe impl SocketAddrArg for SocketAddrV6 {
unsafe fn with_sockaddr<R>(
&self,
f: impl FnOnce(*const SocketAddrOpaque, SocketAddrLen) -> R,
) -> R {
call_with_sockaddr(&encode_sockaddr_v6(self), f)
}
}
#[cfg(unix)]
unsafe impl SocketAddrArg for SocketAddrUnix {
unsafe fn with_sockaddr<R>(
&self,
f: impl FnOnce(*const SocketAddrOpaque, SocketAddrLen) -> R,
) -> R {
f(as_ptr(&self.unix).cast(), self.addr_len())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::c;
#[test]
fn test_layouts() {
assert_eq_size!(SocketAddrLen, c::socklen_t);
#[cfg(not(any(windows, target_os = "redox")))]
assert_eq!(
memoffset::span_of!(c::msghdr, msg_namelen).len(),
size_of::<SocketAddrLen>()
);
assert!(size_of::<SocketAddrLen>() <= size_of::<usize>());
}
}