use crate::*;
use std::{
convert::{TryFrom, TryInto},
mem,
mem::MaybeUninit,
};
#[cfg(any(target_os = "dragonfly", target_os = "macos", target_os = "ios"))]
const ALIGN: usize = 4 - 1;
#[cfg(any(
target_os = "linux",
all(
target_os = "freebsd",
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
target_arch = "x86",
target_arch = "x86_64"
)
),
all(
target_os = "openbsd",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
),
))]
const ALIGN: usize = mem::size_of::<usize>() - 1;
const fn align(size: usize) -> usize {
(size + ALIGN) & !ALIGN
}
const HDR_SIZE: usize = mem::size_of::<c::cmsghdr>();
const HDR_SPACE: usize = align(HDR_SIZE);
#[repr(C)]
union HdrBytes {
hdr: c::cmsghdr,
bytes: [u8; HDR_SPACE],
}
pub const fn cmsg_space(data_len: usize) -> usize {
HDR_SPACE + align(data_len)
}
pub fn cmsg_read<'a>(buf: &mut &'a [u8]) -> Result<(usize, c::cmsghdr, &'a [u8])> {
if buf.len() < HDR_SPACE {
return einval();
}
let mut hdr_bytes = HdrBytes {
bytes: [0; HDR_SPACE],
};
unsafe {
hdr_bytes.bytes.copy_from_slice(&buf[..HDR_SPACE]);
}
let hdr = unsafe { hdr_bytes.hdr };
let cmsg_len = match usize::try_from(hdr.cmsg_len) {
Ok(l) => l,
_ => return einval(),
};
if cmsg_len < HDR_SPACE {
return einval();
}
if usize::max_value() - cmsg_len < ALIGN {
return einval();
}
let cmsg_space = align(cmsg_len);
if buf.len() < cmsg_space {
return einval();
}
let data = &buf[HDR_SPACE..cmsg_len];
*buf = &buf[cmsg_space..];
Ok((cmsg_space, hdr, data))
}
pub fn cmsg_write<T: ?Sized>(
buf: &mut &mut [MaybeUninit<u8>],
mut hdr: c::cmsghdr,
data: &T,
) -> Result<usize> {
let data_size = mem::size_of_val(data);
let cmsg_space = cmsg_space(data_size);
if buf.len() < cmsg_space {
return einval();
}
hdr.cmsg_len = match (HDR_SPACE + data_size).try_into() {
Ok(v) => v,
Err(_) => return einval(),
};
let ptr = buf.as_mut_ptr();
unsafe {
ptr.copy_from_nonoverlapping(&hdr as *const _ as *const _, HDR_SIZE);
ptr.add(HDR_SPACE)
.copy_from_nonoverlapping(data as *const _ as *const _, data_size);
}
*buf = &mut mem::take(buf)[cmsg_space..];
Ok(cmsg_space)
}