use std::{
mem::MaybeUninit,
ops::{Deref, DerefMut},
ptr,
};
use compio_buf::{IoBuf, IoBufMut, SetLen};
mod io;
pub use self::io::*;
#[cfg(feature = "bytemuck")]
pub mod bytemuck_ext;
mod sys;
pub struct AncillaryRef<'a>(sys::CMsgRef<'a>);
impl AncillaryRef<'_> {
pub fn level(&self) -> i32 {
self.0.level()
}
pub fn ty(&self) -> i32 {
self.0.ty()
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.0.len() as _
}
pub fn data<T: AncillaryData>(&self) -> Result<T, CodecError> {
self.0.decode_data()
}
}
pub struct AncillaryIter<'a> {
inner: sys::CMsgIter,
buffer: &'a [u8],
}
impl<'a> AncillaryIter<'a> {
pub unsafe fn new(buffer: &'a [u8]) -> Self {
Self {
inner: sys::CMsgIter::new(buffer.as_ptr(), buffer.len()),
buffer,
}
}
}
impl<'a> Iterator for AncillaryIter<'a> {
type Item = AncillaryRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let cmsg = self.inner.current(self.buffer.as_ptr());
self.inner.next(self.buffer.as_ptr());
cmsg.map(AncillaryRef)
}
}
}
pub struct AncillaryBuilder<'a, B: ?Sized> {
inner: sys::CMsgIter,
buffer: &'a mut B,
}
impl<'a, B: IoBufMut + ?Sized> AncillaryBuilder<'a, B> {
pub fn new(buffer: &'a mut B) -> Self {
unsafe { buffer.set_len(0) };
let slice = buffer.ensure_init();
let inner = sys::CMsgIter::new(slice.as_ptr(), slice.len());
Self { inner, buffer }
}
pub fn push<T: AncillaryData>(
&mut self,
level: i32,
ty: i32,
value: &T,
) -> Result<(), CodecError> {
if !self.inner.is_space_enough(T::SIZE) {
return Err(CodecError::BufferTooSmall);
}
let mut cmsg = unsafe { self.inner.current_mut(self.buffer.buf_mut_ptr().cast()) }
.expect("sufficient space");
cmsg.set_level(level);
cmsg.set_ty(ty);
unsafe {
self.buffer.advance(cmsg.encode_data(value)?);
}
unsafe { self.inner.next(self.buffer.buf_mut_ptr().cast()) };
Ok(())
}
}
pub struct AncillaryBuf<const N: usize> {
inner: [u8; N],
len: usize,
_align: [sys::cmsghdr; 0],
}
impl<const N: usize> AncillaryBuf<N> {
pub fn new() -> Self {
Self {
inner: [0u8; N],
len: 0,
_align: [],
}
}
pub fn builder(&mut self) -> AncillaryBuilder<'_, Self> {
AncillaryBuilder::new(self)
}
}
impl<const N: usize> Default for AncillaryBuf<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> IoBuf for AncillaryBuf<N> {
fn as_init(&self) -> &[u8] {
&self.inner[..self.len]
}
}
impl<const N: usize> SetLen for AncillaryBuf<N> {
unsafe fn set_len(&mut self, len: usize) {
debug_assert!(len <= N);
self.len = len;
}
}
impl<const N: usize> IoBufMut for AncillaryBuf<N> {
fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
self.inner.as_uninit()
}
}
impl<const N: usize> Deref for AncillaryBuf<N> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.inner[0..self.len]
}
}
impl<const N: usize> DerefMut for AncillaryBuf<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner[0..self.len]
}
}
pub const fn ancillary_space<T: AncillaryData>() -> usize {
#[allow(clippy::unnecessary_cast)]
unsafe {
sys::CMSG_SPACE(T::SIZE as _) as usize
}
}
#[derive(Debug)]
pub enum CodecError {
BufferTooSmall,
Other(Box<dyn std::error::Error + Send + Sync + 'static>),
}
impl CodecError {
pub fn other(error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
Self::Other(error.into())
}
pub fn downcast_ref<T: std::error::Error + 'static>(&self) -> Option<&T> {
match self {
Self::Other(e) => e.downcast_ref(),
_ => None,
}
}
pub fn downcast_mut<T: std::error::Error + 'static>(&mut self) -> Option<&mut T> {
match self {
Self::Other(e) => e.downcast_mut(),
_ => None,
}
}
}
impl std::fmt::Display for CodecError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BufferTooSmall => write!(f, "buffer too small for encoding/decoding"),
Self::Other(e) => write!(f, "codec error: {}", e),
}
}
}
impl std::error::Error for CodecError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Other(e) => Some(e.as_ref()),
_ => None,
}
}
}
pub trait AncillaryData: Sized {
const SIZE: usize = std::mem::size_of::<Self>();
fn encode(&self, buffer: &mut [MaybeUninit<u8>]) -> Result<(), CodecError>;
fn decode(buffer: &[u8]) -> Result<Self, CodecError>;
}
unsafe fn copy_to_bytes<T: AncillaryData>(
src: &T,
dest: &mut [MaybeUninit<u8>],
) -> Result<(), CodecError> {
if dest.len() < T::SIZE {
return Err(CodecError::BufferTooSmall);
}
unsafe {
ptr::copy_nonoverlapping::<u8>(src as *const T as _, dest.as_mut_ptr() as _, T::SIZE);
}
Ok(())
}
unsafe fn copy_from_bytes<T: AncillaryData>(src: &[u8]) -> Result<T, CodecError> {
if src.len() < T::SIZE {
return Err(CodecError::BufferTooSmall);
}
let src_ptr = src.as_ptr() as *const T;
unsafe { Ok(ptr::read_unaligned(src_ptr)) }
}