use bytemuck::AnyBitPattern;
use std::os::raw::c_int;
use crate::Error;
type Result<T> = std::result::Result<T, Error>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct PtrOffset<T: Copy>(pub isize, std::marker::PhantomData<T>);
unsafe impl<T: Copy> bytemuck::Zeroable for PtrOffset<T> {}
unsafe impl<T: Copy + 'static> bytemuck::Pod for PtrOffset<T> {}
pub trait IntoOffset: AnyBitPattern + Copy {
type Item: AnyBitPattern + Copy;
fn into_offset(self) -> Result<Offset<Self::Item>>;
}
impl<T: AnyBitPattern + Copy> IntoOffset for PtrOffset<T> {
type Item = T;
fn into_offset(self) -> Result<Offset<T>> {
if self.0 & 1 == 0 {
Err(Error::BadPointer(self.0))
} else {
Ok(Offset(self.0 & !1, std::marker::PhantomData))
}
}
}
impl<T: AnyBitPattern + Copy> IntoOffset for Offset<T> {
type Item = T;
fn into_offset(self) -> Result<Offset<T>> {
Ok(self)
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Offset<T: Copy>(isize, std::marker::PhantomData<T>);
pub(crate) fn offset<T: Copy>(off: isize) -> Offset<T> {
Offset(off, std::marker::PhantomData)
}
unsafe impl<T: Copy> bytemuck::Zeroable for Offset<T> {}
unsafe impl<T: Copy + 'static> bytemuck::Pod for Offset<T> {}
#[derive(Clone)]
pub struct Ptr<'buf, S> {
pub offset: isize,
pub buf: &'buf [u8],
pub(crate) marker: std::marker::PhantomData<S>,
}
impl<'buf, S> std::fmt::Debug for Ptr<'buf, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ptr").field("offset", &self.offset).finish()
}
}
#[derive(Clone, Debug)]
pub struct Array<'buf, T> {
buf: &'buf [u8],
offset: usize,
size: isize,
marker: std::marker::PhantomData<T>,
}
impl<'buf, T: AnyBitPattern> Array<'buf, T> {
fn new(buf: &'buf [u8], offset: isize, size: c_int) -> Result<Self> {
let len = std::mem::size_of::<T>();
let total_len = len
.checked_mul(size as usize)
.ok_or(Error::BadLength(size as isize))?;
if offset < 0 {
Err(Error::BadOffset(offset))
} else {
let end = (offset as usize)
.checked_add(total_len)
.ok_or(Error::BadLength(size as isize))?;
if end > buf.len() {
Err(Error::BadOffset(end as isize))
} else {
Ok(Array {
buf,
offset: offset as usize,
size: size as isize,
marker: std::marker::PhantomData,
})
}
}
}
pub fn len(&self) -> usize {
self.size as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, idx: usize) -> Option<Ptr<'buf, T>> {
if (idx as isize) < self.size {
let len = std::mem::size_of::<T>() as isize;
Some(Ptr {
buf: self.buf,
offset: self.offset as isize + (idx as isize) * len,
marker: std::marker::PhantomData,
})
} else {
None
}
}
pub fn as_slice(&self) -> Result<&'buf [T]> {
let len = std::mem::size_of::<T>() * self.size as usize;
bytemuck::try_cast_slice(&self.buf[self.offset..(self.offset + len)]).map_err(|_| {
Error::BadAlignment {
offset: self.offset,
expected_alignment: std::mem::align_of::<T>(),
}
})
}
}
impl<'buf, T: AnyBitPattern> Iterator for Array<'buf, T> {
type Item = Ptr<'buf, T>;
fn next(&mut self) -> Option<Ptr<'buf, T>> {
if self.size <= 0 {
None
} else {
let len = std::mem::size_of::<T>();
let ret = Ptr {
buf: self.buf,
offset: self.offset as isize,
marker: std::marker::PhantomData,
};
self.offset += len;
self.size -= 1;
Some(ret)
}
}
}
impl<'buf> Ptr<'buf, u8> {
pub fn str(&self) -> Result<&'buf [u8]> {
let offset = self.offset;
if offset < 0 || offset > self.buf.len() as isize {
Err(Error::BadOffset(offset))
} else {
let buf = &self.buf[(offset as usize)..];
let null_offset = buf
.iter()
.position(|&c| c == 0)
.ok_or(Error::UnterminatedString(offset))?;
Ok(&buf[..null_offset])
}
}
}
fn offset_to_usize(off: isize) -> Result<usize> {
off.try_into().map_err(|_| Error::BadOffset(off))
}
impl<'buf, S: AnyBitPattern> Ptr<'buf, S> {
pub fn relative_offset<Off: IntoOffset>(&self, offset: Off) -> Result<Ptr<'buf, Off::Item>> {
let offset = offset.into_offset()?;
Ok(Ptr {
buf: self.buf,
offset: self
.offset
.checked_add(offset.0)
.ok_or(Error::BadOffset(offset.0))?,
marker: std::marker::PhantomData,
})
}
pub fn deref(&self) -> Result<S> {
let len = std::mem::size_of::<S>() as isize;
let offset = offset_to_usize(self.offset)?;
let end = offset_to_usize(self.offset.wrapping_add(len))?;
let slice = self
.buf
.get(offset..end)
.ok_or(Error::BadOffset(self.offset))?;
Ok(bytemuck::try_pod_read_unaligned(slice).expect("but we checked the length..."))
}
pub fn array(&self, count: c_int) -> Result<Array<'buf, S>> {
Array::new(self.buf, self.offset, count)
}
}