1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
//! Provides implementations for converting from Zend binary strings as slices,
//! commonly returned from functions such as [`pack`] and [`unpack`].
//!
//! [`pack`]: https://www.php.net/manual/en/function.pack.php
//! [`unpack`]: https://www.php.net/manual/en/function.unpack.php
use crate::ffi::zend_string;
use std::{ops::Deref, slice::from_raw_parts};
use crate::{convert::FromZval, flags::DataType, types::Zval};
/// Acts as a wrapper around `&[T]` where `T` implements [`PackSlice`].
/// Primarily used for passing read-only binary data into Rust functions.
#[derive(Debug)]
pub struct BinarySlice<'a, T>(&'a [T])
where
T: PackSlice;
impl<'a, T> BinarySlice<'a, T>
where
T: PackSlice,
{
/// Creates a new binary slice wrapper from a slice of data.
///
/// # Parameters
///
/// * `data` - Slice to store inside the binary wrapper.
pub fn new(data: &'a [T]) -> Self {
Self(data)
}
}
impl<'a, T> Deref for BinarySlice<'a, T>
where
T: PackSlice,
{
type Target = &'a [T];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a, T> FromZval<'a> for BinarySlice<'a, T>
where
T: PackSlice,
{
const TYPE: DataType = DataType::String;
fn from_zval(zval: &'a Zval) -> Option<Self> {
zval.binary_slice().map(BinarySlice)
}
}
impl<'a, T> From<BinarySlice<'a, T>> for &'a [T]
where
T: PackSlice,
{
fn from(value: BinarySlice<'a, T>) -> Self {
value.0
}
}
impl<'a, T> From<&'a [T]> for BinarySlice<'a, T>
where
T: PackSlice,
{
fn from(value: &'a [T]) -> Self {
Self::new(value)
}
}
/// Used to expose a Zend binary string as a slice. Useful in conjunction with
/// the [`pack`] and [`unpack`] functions built-in to PHP.
///
/// # Safety
///
/// The types cannot be ensured between PHP and Rust, as the data is represented
/// as a string when crossing the language boundary. Exercise caution when using
/// these functions.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub unsafe trait PackSlice: Clone {
/// Creates a Rust slice from a given Zend binary string. Can be used to
/// pass data from `pack` in PHP to Rust without encoding into another
/// format. Note that the data *must* be all one type, as this
/// implementation only unpacks one type.
///
/// # Safety
///
/// There is no way to tell if the data stored in the string is actually of
/// the given type. The results of this function can also differ from
/// platform-to-platform due to the different representation of some
/// types on different platforms. Consult the [`pack`] function
/// documentation for more details.
///
/// # Parameters
///
/// * `s` - The Zend string containing the binary data.
///
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
fn unpack_into(s: &zend_string) -> &[Self];
}
/// Implements the [`PackSlice`] trait for a given type.
macro_rules! pack_slice_impl {
($t: ty) => {
pack_slice_impl!($t, <$t>::BITS);
};
($t: ty, $d: expr) => {
unsafe impl PackSlice for $t {
fn unpack_into(s: &zend_string) -> &[Self] {
let bytes = ($d / 8) as usize;
let len = (s.len as usize) / bytes;
let ptr = s.val.as_ptr() as *const $t;
unsafe { from_raw_parts(ptr, len) }
}
}
};
}
pack_slice_impl!(u8);
pack_slice_impl!(i8);
pack_slice_impl!(u16);
pack_slice_impl!(i16);
pack_slice_impl!(u32);
pack_slice_impl!(i32);
pack_slice_impl!(u64);
pack_slice_impl!(i64);
pack_slice_impl!(isize);
pack_slice_impl!(usize);
pack_slice_impl!(f32, 32);
pack_slice_impl!(f64, 64);