use std::fmt::{Debug, Formatter};
use std::io::{ErrorKind, Seek, SeekFrom, Write};
use std::marker::PhantomData;
use std::mem::size_of;
use std::slice;
use crate::spa::pod::restricted::{BasicTypePod, CloneTo, PodHeader};
use crate::spa::pod::{
PodBoolRef, PodError, PodLongRef, PodRef, PodResult, PodValue, SizedPod, WritePod, POD_ALIGN,
};
use crate::spa::type_::Type;
use crate::wrapper::RawWrapper;
type AlignedDataType = u64;
const ZEROED_ALIGNED_DATA: AlignedDataType = 0;
const DATA_ALIGN: u64 = size_of::<AlignedDataType>() as u64;
pub struct PodBuf<T> {
data: Vec<AlignedDataType>,
pos: u64,
phantom: PhantomData<T>,
}
impl<'a, T> PodBuf<T>
where
&'a T: WritePod,
T: 'a,
{
pub fn from_value(value: &<&'a T as PodValue>::Value) -> PodResult<Self> {
let mut buf = Self::new();
<&'a T>::write_pod(&mut buf, value)?;
Ok(buf)
}
}
impl<'a, T> PodBuf<T>
where
&'a T: CloneTo,
T: 'a,
{
pub fn from_pod(pod: &'a T) -> PodResult<Self> {
let mut buf = Self::new();
pod.clone_to(&mut buf)?;
Ok(buf)
}
}
impl<T> PodBuf<T>
where
T: WritePod,
{
pub fn from_primitive_value(value: T::Value) -> PodResult<Self> {
let mut buf = Self::new();
T::write_pod(&mut buf, &value)?;
Ok(buf)
}
}
impl<T> PodBuf<T> {
pub fn into_pod(self) -> AllocPod<T> {
AllocPod {
data: self.data,
phantom: PhantomData,
}
}
pub(crate) fn new() -> Self {
Self {
data: vec![ZEROED_ALIGNED_DATA],
pos: 0,
phantom: PhantomData,
}
}
pub(crate) fn with_size(size: usize) -> Self {
let mut buf = Self::new();
buf.allocate_data_if_needed(size as u64);
buf
}
pub(crate) unsafe fn append_alloc_pod(&mut self, mut alloc_pod: AllocPod<T>) -> PodResult<()> {
let aligned_size = self.pos / DATA_ALIGN;
if (aligned_size * DATA_ALIGN - self.pos) == 0 {
self.data.truncate(aligned_size as usize);
self.data.append(&mut alloc_pod.data);
self.pos = self.data_size() as u64;
Ok(())
} else {
Err(PodError::PodIsNotAligned)
}
}
unsafe fn data_bytes_mut(&mut self) -> &mut [u8] {
slice::from_raw_parts_mut(self.data.as_mut_ptr().cast(), self.data_size())
}
unsafe fn data_bytes(&self) -> &[u8] {
slice::from_raw_parts(self.data.as_ptr().cast(), self.data_size())
}
fn data_size(&self) -> usize {
self.data.len() * DATA_ALIGN as usize
}
fn allocate_data_if_needed(&mut self, pos: u64) {
let data_size = self.data_size() as u64;
if data_size < pos {
let required = (pos - data_size + DATA_ALIGN - 1) / DATA_ALIGN;
for _ in 0..required {
self.data.push(ZEROED_ALIGNED_DATA);
}
}
}
}
impl<T> Write for PodBuf<T> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let start_pos = self.pos;
let end_pos = self
.pos
.checked_add(buf.len() as u64)
.ok_or(std::io::Error::new(
ErrorKind::InvalidInput,
"buffer size is too big",
))?;
self.allocate_data_if_needed(end_pos);
let mut data_bytes = unsafe { self.data_bytes_mut() };
data_bytes[start_pos as usize..end_pos as usize].copy_from_slice(buf);
self.pos = end_pos;
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl<T> Seek for PodBuf<T> {
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
let (from, offset) = match pos {
SeekFrom::Start(pos) => {
self.allocate_data_if_needed(pos);
self.pos = pos;
return Ok(pos);
}
SeekFrom::End(pos_from_end) => (self.data_size() as u64, pos_from_end),
SeekFrom::Current(pos_from_current) => (self.pos, pos_from_current),
};
if let Some(pos) = from.checked_add_signed(offset) {
self.seek(SeekFrom::Start(pos))
} else {
Err(std::io::Error::new(
ErrorKind::InvalidInput,
"invalid seek to a negative or overflowing position",
))
}
}
}
pub struct AllocPod<T> {
data: Vec<AlignedDataType>,
phantom: PhantomData<T>,
}
impl<T> Clone for AllocPod<T> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
phantom: PhantomData,
}
}
}
impl<T> AllocPod<T> {
pub fn as_pod(&self) -> &T {
unsafe { self.as_ptr().as_ref().unwrap() }
}
pub fn as_pod_mut(&mut self) -> &mut T {
unsafe { &mut *(self.as_mut_ptr()) }
}
pub(crate) fn as_ptr(&self) -> *const T {
self.data.as_ptr() as *const _ as *const T
}
pub(crate) fn as_mut_ptr(&mut self) -> *mut T {
self.data.as_mut_ptr() as *mut _ as *mut T
}
pub fn size(&self) -> usize {
self.data.len() * size_of::<AlignedDataType>()
}
}
impl<'a, T> AllocPod<T>
where
&'a T: WritePod,
T: 'a,
{
pub fn from_value(value: &<&'a T as PodValue>::Value) -> PodResult<Self> {
Ok(PodBuf::from_value(value)?.into_pod())
}
}
impl<'a, T> AllocPod<T>
where
&'a T: CloneTo,
T: 'a,
{
pub fn from_pod(pod: &'a T) -> PodResult<Self> {
Ok(PodBuf::from_pod(pod)?.into_pod())
}
}
impl<T> AllocPod<T>
where
T: WritePod,
{
pub fn from_primitive_value(value: T::Value) -> PodResult<Self> {
Ok(PodBuf::from_primitive_value(value)?.into_pod())
}
}
impl<T: Debug> Debug for AllocPod<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AllocatedData")
.field("data", &self.data)
.finish()
}
}
impl<'a, T> TryFrom<&'a PodRef> for AllocPod<T>
where
T: 'a,
&'a T: WritePod,
T: BasicTypePod,
{
type Error = PodError;
fn try_from(value: &'a PodRef) -> Result<Self, Self::Error> {
let pod: &T = value.cast()?;
Ok(PodBuf::from_value(&pod.value()?)?.into_pod())
}
}
#[test]
fn test_buf_from_value() {
let allocated_pod = PodBuf::<PodBoolRef>::from_primitive_value(true)
.unwrap()
.into_pod();
assert_eq!(allocated_pod.data.as_ptr().align_offset(POD_ALIGN), 0);
assert_eq!(allocated_pod.as_pod().pod_size(), 12);
assert_eq!(allocated_pod.as_pod().pod_header().size, 4);
assert_eq!(allocated_pod.as_pod().pod_header().type_, Type::BOOL.raw);
assert!(allocated_pod.as_pod().value().unwrap());
assert_eq!(allocated_pod.as_pod().raw_value(), 1);
let allocated_pod = PodBuf::<PodLongRef>::from_primitive_value(123456789)
.unwrap()
.into_pod();
assert_eq!(allocated_pod.data.as_ptr().align_offset(POD_ALIGN), 0);
assert_eq!(allocated_pod.as_pod().pod_size(), 16);
assert_eq!(allocated_pod.as_pod().pod_header().size, 8);
assert_eq!(allocated_pod.as_pod().pod_header().type_, Type::LONG.raw);
assert_eq!(allocated_pod.as_pod().value().unwrap(), 123456789i64);
assert_eq!(allocated_pod.as_pod().raw_value(), 123456789i64);
}