ext_php_rs/
binary_slice.rs

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