Skip to content

Commit 57cf7b9

Browse files
authored
Re add ZArr::iter(). (#94)
1 parent d4c7814 commit 57cf7b9

File tree

3 files changed

+169
-59
lines changed

3 files changed

+169
-59
lines changed

phper-doc/doc/_05_internal_types/_02_z_arr/index.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,18 @@ let _i = arr.get("10");
4141
arr.remove("foo");
4242
```
4343

44-
`ZArr` can be iterated by `for_each()`.
44+
`ZArr` can be iterated by `iter()`.
4545

4646
```rust,no_run
4747
use phper::arrays::ZArray;
4848
use phper::values::ZVal;
4949
5050
let arr = ZArray::new();
5151
52-
53-
arr.for_each(|k, v| {
54-
dbg!(k, v);
55-
});
52+
for (k, v) in arr.iter() {
53+
}
5654
```
5755

58-
*I used to provide the `iter()` method for `ZArr`, and let `Iter` implement
59-
`Iterator`, but if using the PHP stable macro `ZEND_HASH_FOREACH_KEY_VAL`, it is a
60-
bit difficult to provide `iter`, so it is deleted.*;
61-
6256
`ZArr` implements `ToOwned`, can upgrade to `ZArray` by value copy via
6357
`zend_array_dup`.
6458

phper/src/arrays.rs

Lines changed: 116 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ use derive_more::From;
1515
use std::{
1616
borrow::Borrow,
1717
convert::TryInto,
18-
ffi::c_void,
1918
marker::PhantomData,
20-
mem::{forget, ManuallyDrop},
19+
mem::ManuallyDrop,
2120
ops::{Deref, DerefMut},
21+
ptr::null_mut,
2222
};
2323

2424
/// Key for [ZArr].
@@ -112,8 +112,9 @@ impl ZArr {
112112

113113
/// Add or update item by key.
114114
#[allow(clippy::useless_conversion)]
115-
pub fn insert<'a>(&mut self, key: impl Into<InsertKey<'a>>, mut value: ZVal) {
115+
pub fn insert<'a>(&mut self, key: impl Into<InsertKey<'a>>, value: impl Into<ZVal>) {
116116
let key = key.into();
117+
let mut value = ManuallyDrop::new(value.into());
117118
let val = value.as_mut_ptr();
118119

119120
unsafe {
@@ -150,8 +151,6 @@ impl ZArr {
150151
}
151152
}
152153
}
153-
154-
forget(value);
155154
}
156155

157156
// Get item by key.
@@ -244,21 +243,23 @@ impl ZArr {
244243
}
245244
}
246245

247-
pub fn for_each<'a>(&self, f: impl FnMut(IterKey<'a>, &'a ZVal)) {
248-
let mut f: Box<dyn FnMut(IterKey<'a>, &'a ZVal)> = Box::new(f);
249-
let f = &mut f as *mut Box<_> as *mut c_void;
250-
unsafe {
251-
phper_zend_hash_foreach_key_val(self.as_ptr() as *mut _, Some(for_each_callback), f);
252-
}
253-
}
254-
255246
pub fn entry<'a>(&'a mut self, key: impl Into<Key<'a>>) -> Entry<'a> {
256247
let key = key.into();
257248
match self.get_mut(key.clone()) {
258249
Some(val) => Entry::Occupied(val),
259250
None => Entry::Vacant { arr: self, key },
260251
}
261252
}
253+
254+
#[inline]
255+
pub fn iter(&self) -> Iter<'_> {
256+
Iter::new(self)
257+
}
258+
259+
#[inline]
260+
pub fn iter_mut(&mut self) -> IterMut<'_> {
261+
IterMut::new(self)
262+
}
262263
}
263264

264265
impl ToOwned for ZArr {
@@ -368,26 +369,114 @@ impl Drop for ZArray {
368369
}
369370
}
370371

371-
/// Iterator key for [`ZArr::for_each`].
372+
/// Iterator key for [`ZArr::iter`] and [`ZArr::iter_mut`].
372373
#[derive(Debug, Clone, PartialEq, From)]
373374
pub enum IterKey<'a> {
374375
Index(u64),
375376
ZStr(&'a ZStr),
376377
}
377378

378-
#[allow(clippy::unnecessary_cast)]
379-
unsafe extern "C" fn for_each_callback(
380-
idx: zend_ulong, key: *mut zend_string, val: *mut zval, argument: *mut c_void,
381-
) {
382-
let f = (argument as *mut Box<dyn FnMut(IterKey<'_>, &'_ ZVal)>)
383-
.as_mut()
384-
.unwrap();
385-
let iter_key = if key.is_null() {
386-
IterKey::Index(idx as u64)
387-
} else {
388-
IterKey::ZStr(ZStr::from_ptr(key))
389-
};
390-
f(iter_key, ZVal::from_ptr(val));
379+
struct RawIter<'a> {
380+
arr: *mut zend_array,
381+
pos: HashPosition,
382+
finished: bool,
383+
_p: PhantomData<&'a ()>,
384+
}
385+
386+
impl<'a> RawIter<'a> {
387+
fn new(arr: *mut zend_array) -> Self {
388+
let mut pos: HashPosition = 0;
389+
unsafe {
390+
zend_hash_internal_pointer_reset_ex(arr, &mut pos);
391+
}
392+
Self {
393+
arr,
394+
pos,
395+
finished: false,
396+
_p: PhantomData,
397+
}
398+
}
399+
}
400+
401+
impl<'a> Iterator for RawIter<'a> {
402+
type Item = (IterKey<'a>, *mut zval);
403+
404+
fn next(&mut self) -> Option<Self::Item> {
405+
unsafe {
406+
if self.finished {
407+
return None;
408+
}
409+
410+
let mut str_index: *mut zend_string = null_mut();
411+
let mut num_index: zend_ulong = 0;
412+
413+
#[allow(clippy::unnecessary_mut_passed)]
414+
let result = zend_hash_get_current_key_ex(
415+
self.arr,
416+
&mut str_index,
417+
&mut num_index,
418+
&mut self.pos,
419+
) as u32;
420+
421+
let iter_key = if result == HASH_KEY_IS_STRING {
422+
IterKey::ZStr(ZStr::from_mut_ptr(str_index))
423+
} else if result == HASH_KEY_IS_LONG {
424+
#[allow(clippy::unnecessary_cast)]
425+
IterKey::Index(num_index as u64)
426+
} else {
427+
self.finished = true;
428+
return None;
429+
};
430+
431+
let val = zend_hash_get_current_data_ex(self.arr, &mut self.pos);
432+
if val.is_null() {
433+
self.finished = true;
434+
return None;
435+
}
436+
437+
if zend_hash_move_forward_ex(self.arr, &mut self.pos) == ZEND_RESULT_CODE_FAILURE {
438+
self.finished = true;
439+
}
440+
441+
Some((iter_key, val))
442+
}
443+
}
444+
}
445+
446+
pub struct Iter<'a>(RawIter<'a>);
447+
448+
impl<'a> Iter<'a> {
449+
fn new(arr: &'a ZArr) -> Self {
450+
Self(RawIter::new(arr.as_ptr() as *mut _))
451+
}
452+
}
453+
454+
impl<'a> Iterator for Iter<'a> {
455+
type Item = (IterKey<'a>, &'a ZVal);
456+
457+
fn next(&mut self) -> Option<Self::Item> {
458+
self.0
459+
.next()
460+
.map(|(key, val)| (key, unsafe { ZVal::from_ptr(val) }))
461+
}
462+
}
463+
464+
pub struct IterMut<'a>(RawIter<'a>);
465+
466+
impl<'a> IterMut<'a> {
467+
fn new(arr: &'a mut ZArr) -> Self {
468+
Self(RawIter::new(arr.as_mut_ptr()))
469+
}
470+
}
471+
472+
impl<'a> Iterator for IterMut<'a> {
473+
type Item = (IterKey<'a>, &'a mut ZVal);
474+
475+
fn next(&mut self) -> Option<Self::Item> {
476+
self.0
477+
.next()
478+
.map(|(key, val)| (key, unsafe { ZVal::from_mut_ptr(val) }))
479+
}
391480
}
392481

393482
pub enum Entry<'a> {

tests/integration/src/arrays.rs

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -195,29 +195,56 @@ pub fn integrate(module: &mut Module) {
195195
a.insert((), ZVal::from(1));
196196
a.insert("foo", ZVal::from("bar"));
197197

198-
let mut i = 0;
199-
200-
a.for_each(|k, v| {
201-
match i {
202-
0 => {
203-
assert_eq!(k, 0.into());
204-
assert_eq!(v.as_long(), Some(0));
205-
}
206-
1 => {
207-
assert_eq!(k, 1.into());
208-
assert_eq!(v.as_long(), Some(1));
209-
}
210-
2 => {
211-
assert_eq!(k, IterKey::ZStr(&ZString::new("foo")));
212-
assert_eq!(v.as_z_str().unwrap().to_str(), Ok("bar"));
213-
}
214-
_ => unreachable!(),
215-
}
216-
217-
i += 1;
218-
});
219-
220-
assert_eq!(i, 3);
198+
let mut it = a.iter();
199+
{
200+
let (k, v) = it.next().unwrap();
201+
assert_eq!(k, 0.into());
202+
assert_eq!(v.as_long(), Some(0));
203+
}
204+
{
205+
let (k, v) = it.next().unwrap();
206+
assert_eq!(k, 1.into());
207+
assert_eq!(v.as_long(), Some(1));
208+
}
209+
{
210+
let (k, v) = it.next().unwrap();
211+
assert_eq!(k, IterKey::ZStr(&ZString::new("foo")));
212+
assert_eq!(v.as_z_str().unwrap().to_str(), Ok("bar"));
213+
}
214+
{
215+
assert!(it.next().is_none());
216+
}
217+
{
218+
assert!(it.next().is_none());
219+
}
220+
221+
let mut it = a.iter_mut();
222+
{
223+
let (k, v) = it.next().unwrap();
224+
assert_eq!(k, 0.into());
225+
assert_eq!(v.as_long(), Some(0));
226+
*v.as_mut_long().unwrap() += 100;
227+
}
228+
{
229+
let (k, v) = it.next().unwrap();
230+
assert_eq!(k, 1.into());
231+
assert_eq!(v.as_long(), Some(1));
232+
*v.as_mut_long().unwrap() += 100;
233+
}
234+
{
235+
let (k, v) = it.next().unwrap();
236+
assert_eq!(k, IterKey::ZStr(&ZString::new("foo")));
237+
assert_eq!(v.as_z_str().unwrap().to_str(), Ok("bar"));
238+
}
239+
{
240+
assert!(it.next().is_none());
241+
}
242+
{
243+
assert!(it.next().is_none());
244+
}
245+
246+
assert_eq!(a.get(0).unwrap().as_long(), Some(100));
247+
assert_eq!(a.get(1).unwrap().as_long(), Some(101));
221248

222249
Ok(())
223250
},

0 commit comments

Comments
 (0)