Skip to content

Commit 2b456aa

Browse files
committed
fix(array): fix double ended iterator implementation
Refs: #316
1 parent 64fbb41 commit 2b456aa

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

src/types/array.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,9 @@ impl ToOwned for ZendHashTable {
573573
pub struct Iter<'a> {
574574
ht: &'a ZendHashTable,
575575
current_num: i64,
576+
end_num: i64,
576577
pos: HashPosition,
578+
end_pos: HashPosition,
577579
}
578580

579581
#[derive(Debug, PartialEq)]
@@ -627,10 +629,19 @@ impl<'a> Iter<'a> {
627629
///
628630
/// * `ht` - The hashtable to iterate.
629631
pub fn new(ht: &'a ZendHashTable) -> Self {
632+
let end_num: i64 = ht.len().try_into().unwrap();
633+
let end_pos = if ht.nNumOfElements > 0 {
634+
ht.nNumOfElements - 1
635+
} else {
636+
0
637+
};
638+
630639
Self {
631640
ht,
632641
current_num: 0,
642+
end_num,
633643
pos: 0,
644+
end_pos,
634645
}
635646
}
636647
}
@@ -686,6 +697,10 @@ impl ExactSizeIterator for Iter<'_> {
686697

687698
impl DoubleEndedIterator for Iter<'_> {
688699
fn next_back(&mut self) -> Option<Self::Item> {
700+
if self.end_num <= self.current_num {
701+
return None;
702+
}
703+
689704
let key_type = unsafe {
690705
zend_hash_get_current_key_type_ex(
691706
self.ht as *const ZendHashTable as *mut ZendHashTable,
@@ -703,35 +718,39 @@ impl DoubleEndedIterator for Iter<'_> {
703718
zend_hash_get_current_key_zval_ex(
704719
self.ht as *const ZendHashTable as *mut ZendHashTable,
705720
&key as *const Zval as *mut Zval,
706-
&mut self.pos as *mut HashPosition,
721+
&mut self.end_pos as *mut HashPosition,
707722
);
708723
}
709724
let value = unsafe {
710725
&*zend_hash_get_current_data_ex(
711726
self.ht as *const ZendHashTable as *mut ZendHashTable,
712-
&mut self.pos as *mut HashPosition,
727+
&mut self.end_pos as *mut HashPosition,
713728
)
714729
};
715730

716731
let key = match ArrayKey::from_zval(&key) {
717732
Some(key) => key,
718-
None => ArrayKey::Long(self.current_num),
733+
None => ArrayKey::Long(self.end_num),
719734
};
720735

721736
unsafe {
722737
zend_hash_move_backwards_ex(
723738
self.ht as *const ZendHashTable as *mut ZendHashTable,
724-
&mut self.pos as *mut HashPosition,
739+
&mut self.end_pos as *mut HashPosition,
725740
)
726741
};
727-
self.current_num -= 1;
742+
self.end_num -= 1;
728743

729744
Some((key, value))
730745
}
731746
}
732747

733748
impl<'a> Iter<'a> {
734749
pub fn next_zval(&mut self) -> Option<(Zval, &'a Zval)> {
750+
if self.current_num >= self.end_num {
751+
return None;
752+
}
753+
735754
let key_type = unsafe {
736755
zend_hash_get_current_key_type_ex(
737756
self.ht as *const ZendHashTable as *mut ZendHashTable,

tests/src/integration/iterator.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
assert(iter_next([]) === []);
4+
assert(iter_next([1, 2, 3]) === [0, 1, 1, 2, 2, 3]);
5+
assert(iter_back([]) === []);
6+
assert(iter_back([1, 2, 3]) === [2, 3, 1, 2, 0, 1]);
7+
8+
assert(iter_next_back([], 2) === [null, null]);
9+
assert(iter_next_back([1, 2 ,3], 2) === [2, 3, 0, 1, 1, 2, null, null]);
10+
var_dump(iter_next_back([1, 2, 3, 4, 5], 3));
11+
assert(iter_next_back([1, 2, 3, 4, 5], 3) === [4, 5, 0, 1, 1, 2, 3, 4, 2, 3, null, null, null]);

tests/src/integration/iterator.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#[test]
2+
fn iterator_works() {
3+
assert!(crate::integration::run_php("iterator.php"));
4+
}

0 commit comments

Comments
 (0)