Skip to content

Commit a404834

Browse files
Merge pull request #139 from TobiasBengtsson/master
Support for BinarySlice to avoid allocation
2 parents b230928 + 976bc7d commit a404834

File tree

13 files changed

+237
-13
lines changed

13 files changed

+237
-13
lines changed

build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ impl PHPInfo {
131131
fn build_wrapper(defines: &[(&str, &str)], includes: &[PathBuf]) -> Result<()> {
132132
let mut build = cc::Build::new();
133133
for (var, val) in defines {
134-
build.define(*var, *val);
134+
build.define(var, *val);
135135
}
136136
build
137137
.file("src/wrapper.c")

guide/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- [`Vec`](./types/vec.md)
1313
- [`HashMap`](./types/hashmap.md)
1414
- [`Binary`](./types/binary.md)
15+
- [`BinarySlice`](./types/binary_slice.md)
1516
- [`Option`](./types/option.md)
1617
- [Object](./types/object.md)
1718
- [Class Object](./types/class_object.md)

guide/src/types/binary_slice.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# `Binary Slices`
2+
3+
Binary data is represented as a string in PHP. The most common source of this
4+
data is from the [`pack`] and [`unpack`] functions. It allows you to use a PHP
5+
string as a read-only slice in Rust.
6+
7+
| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation |
8+
| ------------- | -------------- | --------------- | ---------------- | ------------------ |
9+
| Yes | No | No | No | `zend_string` |
10+
11+
The binary type is represented as a string in PHP. Although not encoded, the
12+
data is converted into a slice and then the pointer to the data is set as the
13+
string pointer, with the length of the array being the length of the string.
14+
15+
`BinarySlice<T>` is valid when `T` implements `PackSlice`. This is currently
16+
implemented on most primitive numbers (i8, i16, i32, i64, u8, u16, u32, u64,
17+
isize, usize, f32, f64).
18+
19+
[`pack`]: https://www.php.net/manual/en/function.pack.php
20+
[`unpack`]: https://www.php.net/manual/en/function.unpack.php
21+
22+
## Rust Usage
23+
24+
```rust,no_run
25+
# #![cfg_attr(windows, feature(abi_vectorcall))]
26+
# extern crate ext_php_rs;
27+
use ext_php_rs::prelude::*;
28+
use ext_php_rs::binary_slice::BinarySlice;
29+
30+
#[php_function]
31+
pub fn test_binary_slice(input: BinarySlice<u8>) -> u8 {
32+
let mut sum = 0;
33+
for i in input.iter() {
34+
sum += i;
35+
}
36+
37+
sum
38+
}
39+
# fn main() {}
40+
```
41+
42+
## PHP Usage
43+
44+
```php
45+
<?php
46+
47+
$data = pack('C*', 1, 2, 3, 4, 5);
48+
$output = test_binary_slice($data);
49+
var_dump($output); // 15
50+
```

guide/src/types/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ have been implemented on most regular Rust types:
1515
- `HashMap<String, T>` where T implements `IntoZval` and/or `FromZval`.
1616
- `Binary<T>` where T implements `Pack`, used for transferring binary string
1717
data.
18+
- `BinarySlice<T>` where T implements `Pack`, used for exposing PHP binary
19+
strings as read-only slices.
1820
- A PHP callable closure or function wrapped with `Callable`.
1921
- `Option<T>` where T implements `IntoZval` and/or `FromZval`, and where `None`
2022
is converted to a PHP `null`.

src/binary_slice.rs

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

src/boxed.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,14 @@ impl<T: ZBoxable> DerefMut for ZBox<T> {
9797
impl<T: ZBoxable + Debug> Debug for ZBox<T> {
9898
#[inline]
9999
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100-
(&**self).fmt(f)
100+
(**self).fmt(f)
101101
}
102102
}
103103

104104
impl<T: ZBoxable> Borrow<T> for ZBox<T> {
105105
#[inline]
106106
fn borrow(&self) -> &T {
107-
&**self
107+
self
108108
}
109109
}
110110

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
pub mod alloc;
1010
pub mod args;
1111
pub mod binary;
12+
pub mod binary_slice;
1213
pub mod builders;
1314
pub mod convert;
1415
pub mod error;

src/props.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ impl<'a, T: Clone + IntoZval + FromZval<'a>> Prop<'a> for T {
5959
}
6060
}
6161

62+
pub type PropertyGetter<'a, T> = Option<Box<dyn Fn(&T, &mut Zval) -> PhpResult + Send + Sync + 'a>>;
63+
pub type PropertySetter<'a, T> = Option<Box<dyn Fn(&mut T, &Zval) -> PhpResult + Send + Sync + 'a>>;
64+
6265
/// Represents a property added to a PHP class.
6366
///
6467
/// There are two types of properties:
@@ -69,8 +72,8 @@ impl<'a, T: Clone + IntoZval + FromZval<'a>> Prop<'a> for T {
6972
pub enum Property<'a, T> {
7073
Field(Box<dyn (Fn(&mut T) -> &mut dyn Prop) + Send + Sync>),
7174
Method {
72-
get: Option<Box<dyn Fn(&T, &mut Zval) -> PhpResult + Send + Sync + 'a>>,
73-
set: Option<Box<dyn Fn(&mut T, &Zval) -> PhpResult + Send + Sync + 'a>>,
75+
get: PropertyGetter<'a, T>,
76+
set: PropertySetter<'a, T>,
7477
},
7578
}
7679

src/types/callable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ enum OwnedZval<'a> {
160160
impl<'a> OwnedZval<'a> {
161161
fn as_ref(&self) -> &Zval {
162162
match self {
163-
OwnedZval::Reference(zv) => *zv,
163+
OwnedZval::Reference(zv) => zv,
164164
OwnedZval::Owned(zv) => zv,
165165
}
166166
}

src/types/class_object.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ impl<T: RegisteredClass + Clone> Clone for ZBox<ZendClassObject<T>> {
260260
// `ZendClassObject` pointer will contain a valid, initialized `obj`,
261261
// therefore we can dereference both safely.
262262
unsafe {
263-
let mut new = ZendClassObject::new((&***self).clone());
263+
let mut new = ZendClassObject::new((***self).clone());
264264
zend_objects_clone_members(&mut new.std, &self.std as *const _ as *mut _);
265265
new
266266
}

0 commit comments

Comments
 (0)