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);