use super::{encode, size_of_cmsg};
use core::{
mem::{align_of, size_of},
ops::{Deref, DerefMut},
};
use libc::cmsghdr;
#[repr(align(8))] #[derive(Clone, Debug)]
pub struct Storage<const L: usize>([u8; L]);
impl<const L: usize> Storage<L> {
#[inline]
pub fn encoder(&mut self) -> Encoder<'_, L> {
Encoder {
storage: self,
cursor: 0,
}
}
#[inline]
pub fn iter(&self) -> super::decode::Iter<'_> {
super::decode::Iter::new(self)
}
}
impl<const L: usize> Default for Storage<L> {
#[inline]
fn default() -> Self {
Self([0; L])
}
}
impl<const L: usize> Deref for Storage<L> {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
&self.0
}
}
impl<const L: usize> DerefMut for Storage<L> {
#[inline]
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
pub struct Encoder<'a, const L: usize> {
storage: &'a mut Storage<L>,
cursor: usize,
}
impl<'a, const L: usize> Encoder<'a, L> {
#[inline]
pub fn new(storage: &'a mut Storage<L>) -> Self {
Self { storage, cursor: 0 }
}
#[inline]
pub fn len(&self) -> usize {
self.cursor
}
#[inline]
pub fn is_empty(&self) -> bool {
self.cursor == 0
}
#[inline]
pub fn seek(&mut self, len: usize) {
self.cursor += len;
debug_assert!(self.cursor <= L);
}
#[inline]
pub fn iter(&self) -> super::decode::Iter<'_> {
unsafe {
super::decode::Iter::from_bytes(self)
}
}
}
impl<const L: usize> Deref for Encoder<'_, L> {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
&self.storage[..self.cursor]
}
}
impl<const L: usize> DerefMut for Encoder<'_, L> {
#[inline]
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.storage[..self.cursor]
}
}
impl<const L: usize> super::Encoder for Encoder<'_, L> {
#[inline]
fn encode_cmsg<T: Copy>(
&mut self,
level: libc::c_int,
ty: libc::c_int,
value: T,
) -> Result<usize, encode::Error> {
unsafe {
debug_assert!(
align_of::<T>() <= align_of::<cmsghdr>(),
"alignment of T should be less than or equal to cmsghdr"
);
let element_len = size_of_cmsg::<T>();
debug_assert_ne!(element_len, 0);
debug_assert_eq!(libc::CMSG_SPACE(size_of::<T>() as _) as usize, element_len);
let new_cursor = self.cursor.checked_add(element_len).ok_or(encode::Error)?;
self.storage
.len()
.checked_sub(new_cursor)
.ok_or(encode::Error)?;
let cmsg_ptr = {
let msg_controllen = self.cursor;
let msg_control = self.storage.as_mut_ptr().add(msg_controllen as _);
msg_control as *mut cmsghdr
};
{
let cmsg = &mut *cmsg_ptr;
cmsg.cmsg_level = level;
cmsg.cmsg_type = ty;
cmsg.cmsg_len = libc::CMSG_LEN(size_of::<T>() as _) as _;
}
{
let data_ptr = cmsg_ptr.add(1);
debug_assert_eq!(data_ptr as *mut u8, libc::CMSG_DATA(cmsg_ptr) as *mut u8);
core::ptr::copy_nonoverlapping(
&value as *const T as *const u8,
data_ptr as *mut u8,
size_of::<T>(),
);
}
self.cursor = new_cursor;
debug_assert!(
self.cursor <= self.storage.len(),
"msg should not exceed max allocated"
);
Ok(self.cursor)
}
}
}