Skip to content

Commit 1f9946f

Browse files
authored
Switch to use zend apis for array iteration (#240)
1 parent 6b6489f commit 1f9946f

File tree

3 files changed

+110
-31
lines changed

3 files changed

+110
-31
lines changed

allowed_bindings.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,12 @@ bind! {
215215
zend_class_unserialize_deny,
216216
zend_executor_globals,
217217
zend_objects_store_del,
218+
zend_hash_move_forward_ex,
219+
zend_hash_get_current_key_type_ex,
220+
zend_hash_get_current_key_zval_ex,
221+
zend_hash_get_current_data_ex,
222+
zend_hash_move_backwards_ex,
223+
zend_array_count,
218224
gc_possible_root,
219225
ZEND_ACC_NOT_SERIALIZABLE,
220226
executor_globals,

docsrs_bindings.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,34 @@ extern "C" {
358358
extern "C" {
359359
pub fn zend_hash_index_find(ht: *const HashTable, h: zend_ulong) -> *mut zval;
360360
}
361+
extern "C" {
362+
pub fn zend_hash_move_forward_ex(ht: *mut HashTable, pos: *mut HashPosition) -> zend_result;
363+
}
364+
extern "C" {
365+
pub fn zend_hash_move_backwards_ex(ht: *mut HashTable, pos: *mut HashPosition) -> zend_result;
366+
}
367+
extern "C" {
368+
pub fn zend_hash_get_current_key_zval_ex(
369+
ht: *const HashTable,
370+
key: *mut zval,
371+
pos: *const HashPosition,
372+
);
373+
}
374+
extern "C" {
375+
pub fn zend_hash_get_current_key_type_ex(
376+
ht: *mut HashTable,
377+
pos: *mut HashPosition,
378+
) -> ::std::os::raw::c_int;
379+
}
380+
extern "C" {
381+
pub fn zend_hash_get_current_data_ex(ht: *mut HashTable, pos: *mut HashPosition) -> *mut zval;
382+
}
361383
extern "C" {
362384
pub fn _zend_new_array(size: u32) -> *mut HashTable;
363385
}
386+
extern "C" {
387+
pub fn zend_array_count(ht: *mut HashTable) -> u32;
388+
}
364389
extern "C" {
365390
pub fn zend_array_dup(source: *mut HashTable) -> *mut HashTable;
366391
}

src/types/array.rs

Lines changed: 79 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use std::{
77
ffi::CString,
88
fmt::Debug,
99
iter::FromIterator,
10-
ptr::NonNull,
1110
u64,
1211
};
1312

@@ -16,10 +15,12 @@ use crate::{
1615
convert::{FromZval, IntoZval},
1716
error::{Error, Result},
1817
ffi::{
19-
_Bucket, _zend_new_array, zend_array_destroy, zend_array_dup, zend_hash_clean,
20-
zend_hash_index_del, zend_hash_index_find, zend_hash_index_update,
18+
_zend_new_array, zend_array_count, zend_array_destroy, zend_array_dup, zend_hash_clean,
19+
zend_hash_get_current_data_ex, zend_hash_get_current_key_type_ex,
20+
zend_hash_get_current_key_zval_ex, zend_hash_index_del, zend_hash_index_find,
21+
zend_hash_index_update, zend_hash_move_backwards_ex, zend_hash_move_forward_ex,
2122
zend_hash_next_index_insert, zend_hash_str_del, zend_hash_str_find, zend_hash_str_update,
22-
HT_MIN_SIZE,
23+
HashPosition, HT_MIN_SIZE,
2324
},
2425
flags::DataType,
2526
types::Zval,
@@ -117,7 +118,7 @@ impl ZendHashTable {
117118
/// assert_eq!(ht.len(), 2);
118119
/// ```
119120
pub fn len(&self) -> usize {
120-
self.nNumOfElements as usize
121+
unsafe { zend_array_count(self as *const ZendHashTable as *mut ZendHashTable) as usize }
121122
}
122123

123124
/// Returns whether the hash table is empty.
@@ -520,8 +521,8 @@ impl ToOwned for ZendHashTable {
520521
/// Immutable iterator upon a reference to a hashtable.
521522
pub struct Iter<'a> {
522523
ht: &'a ZendHashTable,
523-
pos: Option<NonNull<_Bucket>>,
524-
end: Option<NonNull<_Bucket>>,
524+
current_num: u64,
525+
pos: HashPosition,
525526
}
526527

527528
impl<'a> Iter<'a> {
@@ -531,37 +532,57 @@ impl<'a> Iter<'a> {
531532
///
532533
/// * `ht` - The hashtable to iterate.
533534
pub fn new(ht: &'a ZendHashTable) -> Self {
534-
#[cfg(not(php82))]
535-
return Self {
535+
Self {
536536
ht,
537-
pos: NonNull::new(ht.arData),
538-
end: NonNull::new(unsafe { ht.arData.offset(ht.nNumUsed as isize) }),
539-
};
540-
#[cfg(php82)]
541-
return Self {
542-
ht,
543-
pos: NonNull::new(unsafe { ht.__bindgen_anon_1.arData }),
544-
end: NonNull::new(unsafe { ht.__bindgen_anon_1.arData.offset(ht.nNumUsed as isize) }),
545-
};
537+
current_num: 0,
538+
pos: 0,
539+
}
546540
}
547541
}
548542

549543
impl<'a> Iterator for Iter<'a> {
550544
type Item = (u64, Option<String>, &'a Zval);
551545

552546
fn next(&mut self) -> Option<Self::Item> {
553-
let pos = self.pos?;
547+
let key_type = unsafe {
548+
zend_hash_get_current_key_type_ex(
549+
self.ht as *const ZendHashTable as *mut ZendHashTable,
550+
&mut self.pos as *mut HashPosition,
551+
)
552+
};
554553

555-
if pos == self.end? {
554+
if key_type == -1 {
556555
return None;
557556
}
558557

559-
let bucket = unsafe { pos.as_ref() };
560-
let key = unsafe { bucket.key.as_ref() }.and_then(|s| s.try_into().ok());
558+
let key = Zval::new();
559+
unsafe {
560+
zend_hash_get_current_key_zval_ex(
561+
self.ht as *const ZendHashTable as *mut ZendHashTable,
562+
&key as *const Zval as *mut Zval,
563+
&mut self.pos as *mut HashPosition,
564+
);
565+
}
566+
let value = unsafe {
567+
&*zend_hash_get_current_data_ex(
568+
self.ht as *const ZendHashTable as *mut ZendHashTable,
569+
&mut self.pos as *mut HashPosition,
570+
)
571+
};
572+
let r: (u64, Option<String>, &Zval) = match key.is_long() {
573+
true => (key.long().unwrap_or(0) as u64, None, value),
574+
false => (self.current_num, key.try_into().ok(), value),
575+
};
561576

562-
self.pos = NonNull::new(unsafe { pos.as_ptr().offset(1) });
577+
unsafe {
578+
zend_hash_move_forward_ex(
579+
self.ht as *const ZendHashTable as *mut ZendHashTable,
580+
&mut self.pos as *mut HashPosition,
581+
)
582+
};
583+
self.current_num += 1;
563584

564-
Some((bucket.h, key, &bucket.val))
585+
Some(r)
565586
}
566587

567588
fn count(self) -> usize
@@ -580,18 +601,45 @@ impl<'a> ExactSizeIterator for Iter<'a> {
580601

581602
impl<'a> DoubleEndedIterator for Iter<'a> {
582603
fn next_back(&mut self) -> Option<Self::Item> {
583-
let end = self.end?;
604+
let key_type = unsafe {
605+
zend_hash_get_current_key_type_ex(
606+
self.ht as *const ZendHashTable as *mut ZendHashTable,
607+
&mut self.pos as *mut HashPosition,
608+
)
609+
};
584610

585-
if end == self.pos? {
611+
if key_type == -1 {
586612
return None;
587613
}
588614

589-
let new_end = NonNull::new(unsafe { end.as_ptr().offset(-1) })?;
590-
let bucket = unsafe { new_end.as_ref() };
591-
let key = unsafe { bucket.key.as_ref() }.and_then(|s| s.try_into().ok());
592-
self.end = Some(new_end);
615+
let key = Zval::new();
616+
unsafe {
617+
zend_hash_get_current_key_zval_ex(
618+
self.ht as *const ZendHashTable as *mut ZendHashTable,
619+
&key as *const Zval as *mut Zval,
620+
&mut self.pos as *mut HashPosition,
621+
);
622+
}
623+
let value = unsafe {
624+
&*zend_hash_get_current_data_ex(
625+
self.ht as *const ZendHashTable as *mut ZendHashTable,
626+
&mut self.pos as *mut HashPosition,
627+
)
628+
};
629+
let r: (u64, Option<String>, &Zval) = match key.is_long() {
630+
true => (key.long().unwrap_or(0) as u64, None, value),
631+
false => (self.current_num, key.try_into().ok(), value),
632+
};
633+
634+
unsafe {
635+
zend_hash_move_backwards_ex(
636+
self.ht as *const ZendHashTable as *mut ZendHashTable,
637+
&mut self.pos as *mut HashPosition,
638+
)
639+
};
640+
self.current_num -= 1;
593641

594-
Some((bucket.h, key, &bucket.val))
642+
Some(r)
595643
}
596644
}
597645

0 commit comments

Comments
 (0)