use crate::ffi::{ext_php_rs_zend_string_init, zend_string};
use std::{
convert::TryFrom,
iter::FromIterator,
ops::{Deref, DerefMut},
};
use crate::{
convert::{FromZval, IntoZval},
error::{Error, Result},
flags::DataType,
types::Zval,
};
#[derive(Debug)]
pub struct Binary<T: Pack>(Vec<T>);
impl<T: Pack> Binary<T> {
pub fn new(data: impl Into<Vec<T>>) -> Self {
Self(data.into())
}
}
impl<T: Pack> Deref for Binary<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: Pack> DerefMut for Binary<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: Pack> FromZval<'_> for Binary<T> {
const TYPE: DataType = DataType::String;
fn from_zval(zval: &Zval) -> Option<Self> {
zval.binary().map(Binary)
}
}
impl<T: Pack> TryFrom<Zval> for Binary<T> {
type Error = Error;
fn try_from(value: Zval) -> Result<Self> {
Self::from_zval(&value).ok_or_else(|| Error::ZvalConversion(value.get_type()))
}
}
impl<T: Pack> IntoZval for Binary<T> {
const TYPE: DataType = DataType::String;
fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
zv.set_binary(self.0);
Ok(())
}
}
impl<T: Pack> From<Binary<T>> for Vec<T> {
fn from(value: Binary<T>) -> Self {
value.0
}
}
impl<T: Pack> From<Vec<T>> for Binary<T> {
fn from(value: Vec<T>) -> Self {
Self::new(value)
}
}
impl<T: Pack> FromIterator<T> for Binary<T> {
fn from_iter<U: IntoIterator<Item = T>>(iter: U) -> Self {
Self(iter.into_iter().collect::<Vec<_>>())
}
}
pub unsafe trait Pack: Clone {
fn pack_into(vec: Vec<Self>) -> *mut zend_string;
fn unpack_into(s: &zend_string) -> Vec<Self>;
}
macro_rules! pack_impl {
($t: ty) => {
pack_impl!($t, <$t>::BITS);
};
($t: ty, $d: expr) => {
unsafe impl Pack for $t {
fn pack_into(vec: Vec<Self>) -> *mut zend_string {
let len = vec.len() * ($d as usize / 8);
let ptr = Box::into_raw(vec.into_boxed_slice());
unsafe { ext_php_rs_zend_string_init(ptr.cast(), len as _, false) }
}
fn unpack_into(s: &zend_string) -> Vec<Self> {
let bytes = ($d / 8) as u64;
let len = (s.len as u64) / bytes;
let mut result = Vec::with_capacity(len as _);
let ptr = s.val.as_ptr() as *const $t;
for i in 0..len {
result.push(unsafe { *ptr.offset(i as _) });
}
result
}
}
};
}
pack_impl!(u8);
pack_impl!(i8);
pack_impl!(u16);
pack_impl!(i16);
pack_impl!(u32);
pack_impl!(i32);
pack_impl!(u64);
pack_impl!(i64);
pack_impl!(isize);
pack_impl!(usize);
pack_impl!(f32, 32);
pack_impl!(f64, 64);