use std::error::Error;
use std::marker::{PhantomData, PhantomPinned};
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
use std::{any, fmt, mem, ptr, slice};
#[derive(Debug)]
pub struct PtrPtrVec<T> {
data: Vec<T>,
pointers: Vec<*const T>,
}
unsafe impl<T> Send for PtrPtrVec<T> where Vec<T>: Send {}
unsafe impl<T> Sync for PtrPtrVec<T> where Vec<T>: Sync {}
impl<T> PtrPtrVec<T> {
pub fn new(data: Vec<T>) -> Self {
let start = data.as_ptr();
let mut pointers = Vec::with_capacity(data.len());
pointers.push(start);
pointers.extend(data[1..].iter().map(|r| r as *const T));
Self { data, pointers }
}
pub fn into_inner(self) -> Vec<T> {
self.data
}
pub fn as_ptr<Dest>(&self) -> *const *const Dest {
Self::assert_size::<Dest>();
self.pointers.as_ptr().cast::<*const Dest>()
}
#[deprecated = "use [`Self::iter_over`] instead, unless you really need this specific version"]
#[allow(dead_code)]
pub unsafe fn iter_over_linux<'a, Src>(
ptr_ptr: *const *const Src,
count: usize,
) -> impl Iterator<Item = &'a T>
where
T: 'a,
{
Self::assert_size::<Src>();
slice::from_raw_parts(ptr_ptr.cast::<&T>(), count)
.iter()
.copied()
}
#[deprecated = "use [`Self::iter_over`] instead, unless you really need this specific version"]
#[allow(dead_code)]
pub unsafe fn iter_over_xsso<'a, Src>(
ptr_ptr: *const *const Src,
count: usize,
) -> impl Iterator<Item = &'a T>
where
T: 'a,
{
Self::assert_size::<Src>();
slice::from_raw_parts(*ptr_ptr.cast(), count).iter()
}
#[allow(deprecated)]
pub unsafe fn iter_over<'a, Src>(
ptr_ptr: *const *const Src,
count: usize,
) -> impl Iterator<Item = &'a T>
where
T: 'a,
{
#[cfg(pam_impl = "LinuxPam")]
return Self::iter_over_linux(ptr_ptr, count);
#[cfg(not(pam_impl = "LinuxPam"))]
return Self::iter_over_xsso(ptr_ptr, count);
}
fn assert_size<That>() {
assert_eq!(
mem::size_of::<T>(),
mem::size_of::<That>(),
"type {t} is not the size of {that}",
t = any::type_name::<T>(),
that = any::type_name::<That>(),
);
}
}
#[derive(Debug, PartialEq)]
pub struct TooBigError {
pub size: usize,
pub max: usize,
}
impl Error for TooBigError {}
impl fmt::Display for TooBigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"can't allocate a message of {size} bytes (max {max})",
size = self.size,
max = self.max
)
}
}
#[allow(clippy::wrong_self_convention)]
pub trait Buffer {
fn allocate(len: usize) -> Self;
fn as_ptr(this: &Self) -> *const u8;
unsafe fn as_mut_slice(this: &mut Self, len: usize) -> &mut [u8];
fn into_ptr(this: Self) -> NonNull<u8>;
unsafe fn from_ptr(ptr: NonNull<u8>, bytes: usize) -> Self;
}
impl Buffer for Vec<u8> {
fn allocate(bytes: usize) -> Self {
vec![0; bytes]
}
fn as_ptr(this: &Self) -> *const u8 {
Vec::as_ptr(this)
}
unsafe fn as_mut_slice(this: &mut Self, bytes: usize) -> &mut [u8] {
&mut this[..bytes]
}
fn into_ptr(this: Self) -> NonNull<u8> {
let mut me = ManuallyDrop::new(this);
unsafe { NonNull::new_unchecked(me.as_mut_ptr()) }
}
unsafe fn from_ptr(ptr: NonNull<u8>, bytes: usize) -> Self {
Vec::from_raw_parts(ptr.as_ptr(), bytes, bytes)
}
}
pub struct BinaryPayload {
pub total_bytes_u32be: [u8; 4],
pub data_type: u8,
pub _marker: PhantomData<PhantomPinned>,
}
impl BinaryPayload {
pub const MAX_SIZE: usize = (u32::MAX - 5) as usize;
pub fn fill(buf: &mut [u8], data: &[u8], data_type: u8) {
let ptr: *mut Self = buf.as_mut_ptr().cast();
let me = unsafe { ptr.as_mut().unwrap_unchecked() };
me.total_bytes_u32be = u32::to_be_bytes(buf.len() as u32);
me.data_type = data_type;
buf[5..].copy_from_slice(data)
}
pub unsafe fn total_bytes(this: *const Self) -> usize {
let header = this.as_ref().unwrap_unchecked();
u32::from_be_bytes(header.total_bytes_u32be) as usize
}
pub unsafe fn buffer_of<'a>(ptr: *const Self) -> &'a [u8] {
slice::from_raw_parts(ptr.cast(), Self::total_bytes(ptr).max(5))
}
pub unsafe fn contents<'a>(ptr: *const Self) -> (&'a [u8], u8) {
let header: &Self = ptr.as_ref().unwrap_unchecked();
(&Self::buffer_of(ptr)[5..], header.data_type)
}
pub unsafe fn zero(ptr: *mut Self) {
let size = Self::total_bytes(ptr);
let ptr: *mut u8 = ptr.cast();
for x in 0..size {
ptr::write_volatile(ptr.byte_add(x), mem::zeroed())
}
}
}
#[derive(Debug)]
pub struct OwnedBinaryPayload<Owner: Buffer>(Owner);
impl<O: Buffer> OwnedBinaryPayload<O> {
pub fn new(data: &[u8], type_: u8) -> Result<Self, TooBigError> {
let total_len: u32 = (data.len() + 5).try_into().map_err(|_| TooBigError {
size: data.len(),
max: BinaryPayload::MAX_SIZE,
})?;
let total_len = total_len as usize;
let mut buf = O::allocate(total_len);
BinaryPayload::fill(
unsafe { Buffer::as_mut_slice(&mut buf, total_len) },
data,
type_,
);
Ok(Self(buf))
}
pub fn contents(&self) -> (&[u8], u8) {
unsafe { BinaryPayload::contents(self.as_ptr()) }
}
pub fn total_bytes(&self) -> usize {
unsafe { BinaryPayload::buffer_of(Buffer::as_ptr(&self.0).cast()).len() }
}
pub fn into_inner(self) -> O {
self.0
}
pub fn as_ptr(&self) -> *const BinaryPayload {
Buffer::as_ptr(&self.0).cast()
}
pub fn into_ptr(self) -> NonNull<BinaryPayload> {
Buffer::into_ptr(self.0).cast()
}
pub unsafe fn from_ptr(ptr: NonNull<BinaryPayload>) -> Self {
Self(O::from_ptr(
ptr.cast(),
BinaryPayload::total_bytes(ptr.as_ptr()),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ptr;
type VecPayload = OwnedBinaryPayload<Vec<u8>>;
#[test]
fn test_binary_payload() {
let simple_message = &[0u8, 0, 0, 16, 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let empty = &[0u8; 5];
assert_eq!((&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..], 0xff), unsafe {
BinaryPayload::contents(simple_message.as_ptr().cast())
});
assert_eq!((&[][..], 0x00), unsafe {
BinaryPayload::contents(empty.as_ptr().cast())
});
}
#[test]
fn test_owned_binary_payload() {
let (data, typ) = (
&[0, 1, 1, 8, 9, 9, 9, 8, 8, 1, 9, 9, 9, 1, 1, 9, 7, 2, 5, 3][..],
112,
);
let payload = VecPayload::new(data, typ).unwrap();
assert_eq!((data, typ), payload.contents());
let ptr = payload.into_ptr();
let payload = unsafe { VecPayload::from_ptr(ptr) };
assert_eq!((data, typ), payload.contents());
}
#[test]
#[ignore]
fn test_owned_too_big() {
let data = vec![0xFFu8; 0x1_0000_0001];
assert_eq!(
TooBigError {
max: 0xffff_fffa,
size: 0x1_0000_0001
},
VecPayload::new(&data, 5).unwrap_err()
)
}
#[cfg(debug_assertions)]
#[test]
#[should_panic]
fn test_new_wrong_size() {
let bad_vec = vec![0; 19];
let msg = PtrPtrVec::new(bad_vec);
let _ = msg.as_ptr::<u64>();
}
#[allow(deprecated)]
#[test]
#[should_panic]
fn test_iter_xsso_wrong_size() {
unsafe {
let _ = PtrPtrVec::<u8>::iter_over_xsso::<f64>(ptr::null(), 1);
}
}
#[allow(deprecated)]
#[test]
#[should_panic]
fn test_iter_linux_wrong_size() {
unsafe {
let _ = PtrPtrVec::<u128>::iter_over_linux::<()>(ptr::null(), 1);
}
}
#[allow(deprecated)]
#[test]
fn test_right_size() {
let good_vec = vec![(1u64, 2u64), (3, 4), (5, 6)];
let ptr = good_vec.as_ptr();
let msg = PtrPtrVec::new(good_vec);
let msg_ref: *const *const (i64, i64) = msg.as_ptr();
assert_eq!(unsafe { *msg_ref }, ptr.cast());
let linux_result: Vec<(i64, i64)> = unsafe { PtrPtrVec::iter_over_linux(msg_ref, 3) }
.cloned()
.collect();
let xsso_result: Vec<(i64, i64)> = unsafe { PtrPtrVec::iter_over_xsso(msg_ref, 3) }
.cloned()
.collect();
assert_eq!(vec![(1, 2), (3, 4), (5, 6)], linux_result);
assert_eq!(vec![(1, 2), (3, 4), (5, 6)], xsso_result);
drop(msg)
}
#[allow(deprecated)]
#[test]
fn test_iter_ptr_ptr() {
#[repr(C)]
struct Pair(&'static str, i32);
let boxes = vec![
Box::new(Pair("a", 1)),
Box::new(Pair("b", 2)),
Box::new(Pair("c", 3)),
Box::new(Pair("D", 4)),
];
let ptr: *const *const &str = boxes.as_ptr().cast();
let got: Vec<&str> = unsafe { PtrPtrVec::iter_over_linux(ptr, 4) }
.cloned()
.collect();
assert_eq!(vec!["a", "b", "c", "D"], got);
let nums = [-1i8, 2, 3];
let ptr = nums.as_ptr();
let got: Vec<u8> = unsafe { PtrPtrVec::iter_over_xsso(&ptr, 3) }
.cloned()
.collect();
assert_eq!(vec![255, 2, 3], got);
}
}