1use crate::{alloc::EBox, strings::ZStr, sys::*, values::ZVal};
14use cfg_if::cfg_if;
15use derive_more::From;
16use phper_alloc::ToRefOwned;
17use std::{
18 fmt::{self, Debug},
19 marker::PhantomData,
20 mem::{ManuallyDrop, MaybeUninit},
21 ops::Deref,
22 ptr::null_mut,
23};
24
25#[derive(Debug, Clone, PartialEq, From)]
27pub enum Key<'a> {
28 Index(u64),
30 Str(&'a str),
32 Bytes(&'a [u8]),
34 ZStr(&'a ZStr),
36}
37
38#[derive(Debug, Clone, PartialEq, From)]
40pub enum InsertKey<'a> {
41 NextIndex,
43 Index(u64),
45 Str(&'a str),
47 Bytes(&'a [u8]),
49 ZStr(&'a ZStr),
51}
52
53impl From<()> for InsertKey<'_> {
54 fn from(_: ()) -> Self {
55 Self::NextIndex
56 }
57}
58
59impl<'a> From<Key<'a>> for InsertKey<'a> {
60 fn from(k: Key<'a>) -> Self {
61 match k {
62 Key::Index(i) => InsertKey::Index(i),
63 Key::Str(s) => InsertKey::Str(s),
64 Key::Bytes(b) => InsertKey::Bytes(b),
65 Key::ZStr(s) => InsertKey::ZStr(s),
66 }
67 }
68}
69
70#[repr(transparent)]
72pub struct ZArr {
73 inner: zend_array,
74 _p: PhantomData<*mut ()>,
75}
76
77impl ZArr {
78 #[inline]
88 pub unsafe fn from_ptr<'a>(ptr: *const zend_array) -> &'a Self {
89 unsafe { (ptr as *const Self).as_ref().expect("ptr should't be null") }
90 }
91
92 #[inline]
98 pub unsafe fn try_from_ptr<'a>(ptr: *const zend_array) -> Option<&'a Self> {
99 unsafe { (ptr as *const Self).as_ref() }
100 }
101
102 #[inline]
112 pub unsafe fn from_mut_ptr<'a>(ptr: *mut zend_array) -> &'a mut Self {
113 unsafe { (ptr as *mut Self).as_mut().expect("ptr should't be null") }
114 }
115
116 #[inline]
122 pub unsafe fn try_from_mut_ptr<'a>(ptr: *mut zend_array) -> Option<&'a mut Self> {
123 unsafe { (ptr as *mut Self).as_mut() }
124 }
125
126 pub const fn as_ptr(&self) -> *const zend_array {
128 &self.inner
129 }
130
131 #[inline]
133 pub fn as_mut_ptr(&mut self) -> *mut zend_array {
134 &mut self.inner
135 }
136
137 #[inline]
139 pub fn is_empty(&mut self) -> bool {
140 self.len() == 0
141 }
142
143 #[inline]
145 pub fn len(&mut self) -> usize {
146 unsafe { zend_array_count(self.as_mut_ptr()).try_into().unwrap() }
147 }
148
149 #[allow(clippy::useless_conversion)]
154 pub fn insert<'a>(&mut self, key: impl Into<InsertKey<'a>>, value: impl Into<ZVal>) {
155 let key = key.into();
156 let mut value = ManuallyDrop::new(value.into());
157 let val = value.as_mut_ptr();
158
159 unsafe {
160 match key {
161 InsertKey::NextIndex => {
162 phper_zend_hash_next_index_insert(self.as_mut_ptr(), val);
163 }
164 InsertKey::Index(i) => {
165 phper_zend_hash_index_update(self.as_mut_ptr(), i, val);
166 }
167 InsertKey::Str(s) => {
168 phper_zend_symtable_str_update(
169 self.as_mut_ptr(),
170 s.as_ptr().cast(),
171 s.len().try_into().unwrap(),
172 val,
173 );
174 }
175 InsertKey::Bytes(b) => {
176 phper_zend_symtable_str_update(
177 self.as_mut_ptr(),
178 b.as_ptr().cast(),
179 b.len().try_into().unwrap(),
180 val,
181 );
182 }
183 InsertKey::ZStr(s) => {
184 phper_zend_symtable_str_update(
185 self.as_mut_ptr(),
186 s.as_c_str_ptr().cast(),
187 s.len().try_into().unwrap(),
188 val,
189 );
190 }
191 }
192 }
193 }
194
195 pub fn get<'a>(&self, key: impl Into<Key<'a>>) -> Option<&'a ZVal> {
200 self.inner_get(key).map(|v| &*v)
201 }
202
203 pub fn get_mut<'a>(&mut self, key: impl Into<Key<'a>>) -> Option<&'a mut ZVal> {
208 self.inner_get(key)
209 }
210
211 #[allow(clippy::useless_conversion)]
212 fn inner_get<'a>(&self, key: impl Into<Key<'a>>) -> Option<&'a mut ZVal> {
213 let key = key.into();
214 let ptr = self.as_ptr() as *mut _;
215 unsafe {
216 let value = match key {
217 Key::Index(i) => phper_zend_hash_index_find(ptr, i),
218 Key::Str(s) => phper_zend_symtable_str_find(
219 ptr,
220 s.as_ptr().cast(),
221 s.len().try_into().unwrap(),
222 ),
223 Key::Bytes(b) => phper_zend_symtable_str_find(
224 ptr,
225 b.as_ptr().cast(),
226 b.len().try_into().unwrap(),
227 ),
228 Key::ZStr(s) => {
229 phper_zend_symtable_str_find(ptr, s.as_c_str_ptr(), s.len().try_into().unwrap())
230 }
231 };
232 if value.is_null() {
233 None
234 } else {
235 Some(ZVal::from_mut_ptr(value))
236 }
237 }
238 }
239
240 #[allow(clippy::useless_conversion)]
245 pub fn exists<'a>(&self, key: impl Into<Key<'a>>) -> bool {
246 let key = key.into();
247 let ptr = self.as_ptr() as *mut _;
248 unsafe {
249 match key {
250 Key::Index(i) => phper_zend_hash_index_exists(ptr, i),
251 Key::Str(s) => phper_zend_symtable_str_exists(
252 ptr,
253 s.as_ptr().cast(),
254 s.len().try_into().unwrap(),
255 ),
256 Key::Bytes(b) => phper_zend_symtable_str_exists(
257 ptr,
258 b.as_ptr().cast(),
259 b.len().try_into().unwrap(),
260 ),
261 Key::ZStr(s) => phper_zend_symtable_str_exists(
262 ptr,
263 s.to_bytes().as_ptr().cast(),
264 s.len().try_into().unwrap(),
265 ),
266 }
267 }
268 }
269
270 #[allow(clippy::useless_conversion)]
275 pub fn remove<'a>(&mut self, key: impl Into<Key<'a>>) -> bool {
276 let key = key.into();
277 unsafe {
278 match key {
279 Key::Index(i) => phper_zend_hash_index_del(&mut self.inner, i),
280 Key::Str(s) => phper_zend_symtable_str_del(
281 &mut self.inner,
282 s.as_ptr().cast(),
283 s.len().try_into().unwrap(),
284 ),
285 Key::Bytes(b) => phper_zend_symtable_str_del(
286 &mut self.inner,
287 b.as_ptr().cast(),
288 b.len().try_into().unwrap(),
289 ),
290 Key::ZStr(s) => phper_zend_symtable_str_del(
291 &mut self.inner,
292 s.as_c_str_ptr().cast(),
293 s.len().try_into().unwrap(),
294 ),
295 }
296 }
297 }
298
299 pub fn entry<'a>(&'a mut self, key: impl Into<Key<'a>>) -> Entry<'a> {
317 let key = key.into();
318 match self.get_mut(key.clone()) {
319 Some(val) => Entry::Occupied(OccupiedEntry(val)),
320 None => Entry::Vacant(VacantEntry { arr: self, key }),
321 }
322 }
323
324 #[inline]
326 pub fn iter(&self) -> Iter<'_> {
327 Iter::new(self)
328 }
329
330 #[inline]
332 pub fn iter_mut(&mut self) -> IterMut<'_> {
333 IterMut::new(self)
334 }
335}
336
337impl Drop for ZArr {
338 fn drop(&mut self) {
339 let mut val = MaybeUninit::<zval>::uninit();
340 unsafe {
341 phper_zval_arr(val.as_mut_ptr().cast(), self.as_mut_ptr());
342 phper_zval_ptr_dtor(val.as_mut_ptr());
343 }
344 }
345}
346
347impl Debug for ZArr {
348 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349 common_fmt(self, f, "ZArr")
350 }
351}
352
353impl ToOwned for ZArr {
354 type Owned = ZArray;
355
356 fn to_owned(&self) -> Self::Owned {
357 unsafe {
358 let dest = phper_zend_array_dup(self.as_ptr() as *mut _);
360 ZArray::from_raw_cast(dest)
361 }
362 }
363}
364
365impl ToRefOwned for ZArr {
366 type Owned = ZArray;
367
368 fn to_ref_owned(&mut self) -> Self::Owned {
369 let mut val = ManuallyDrop::new(ZVal::default());
370 unsafe {
371 phper_zval_arr(val.as_mut_ptr(), self.as_mut_ptr());
372 phper_z_addref_p(val.as_mut_ptr());
373 ZArray::from_raw_cast(val.as_mut_z_arr().unwrap().as_mut_ptr())
374 }
375 }
376}
377
378pub type ZArray = EBox<ZArr>;
384
385impl ZArray {
386 #[inline]
388 pub fn new() -> Self {
389 Self::with_capacity(0)
390 }
391
392 pub fn with_capacity(n: usize) -> Self {
397 unsafe {
398 let ptr = phper_zend_new_array(n.try_into().unwrap());
399 Self::from_raw_cast(ptr)
400 }
401 }
402}
403
404impl Default for ZArray {
405 fn default() -> Self {
406 Self::new()
407 }
408}
409
410impl Clone for ZArray {
411 fn clone(&self) -> Self {
412 self.deref().to_owned()
413 }
414}
415
416#[derive(Debug, Clone, PartialEq, From)]
418pub enum IterKey<'a> {
419 Index(u64),
421 ZStr(&'a ZStr),
423}
424
425struct RawIter<'a> {
426 arr: *mut zend_array,
427 pos: HashPosition,
428 finished: bool,
429 _p: PhantomData<&'a ()>,
430}
431
432impl RawIter<'_> {
433 fn new(arr: *mut zend_array) -> Self {
434 let mut pos: HashPosition = 0;
435 unsafe {
436 zend_hash_internal_pointer_reset_ex(arr, &mut pos);
437 }
438 Self {
439 arr,
440 pos,
441 finished: false,
442 _p: PhantomData,
443 }
444 }
445}
446
447impl<'a> Iterator for RawIter<'a> {
448 type Item = (IterKey<'a>, *mut zval);
449
450 fn next(&mut self) -> Option<Self::Item> {
451 unsafe {
452 if self.finished {
453 return None;
454 }
455
456 let mut str_index: *mut zend_string = null_mut();
457 let mut num_index: zend_ulong = 0;
458
459 #[allow(clippy::unnecessary_mut_passed)]
460 let result = zend_hash_get_current_key_ex(
461 self.arr,
462 &mut str_index,
463 &mut num_index,
464 &mut self.pos,
465 ) as u32;
466
467 const IS_STRING: u32 = {
468 cfg_if! {
469 if #[cfg(all(phper_major_version = "8", phper_minor_version = "5"))] {
470 zend_hash_key_type_HASH_KEY_IS_STRING
471 } else {
472 HASH_KEY_IS_STRING
473 }
474 }
475 };
476
477 const IS_LONG: u32 = {
478 cfg_if! {
479 if #[cfg(all(phper_major_version = "8", phper_minor_version = "5"))] {
480 zend_hash_key_type_HASH_KEY_IS_LONG
481 } else {
482 HASH_KEY_IS_LONG
483 }
484 }
485 };
486
487 let iter_key = if result == IS_STRING {
488 IterKey::ZStr(ZStr::from_mut_ptr(str_index))
489 } else if result == IS_LONG {
490 #[allow(clippy::unnecessary_cast)]
491 IterKey::Index(num_index as u64)
492 } else {
493 self.finished = true;
494 return None;
495 };
496
497 #[allow(clippy::unnecessary_mut_passed)]
498 let val = zend_hash_get_current_data_ex(self.arr, &mut self.pos);
499 if val.is_null() {
500 self.finished = true;
501 return None;
502 }
503
504 if zend_hash_move_forward_ex(self.arr, &mut self.pos) == ZEND_RESULT_CODE_FAILURE {
505 self.finished = true;
506 }
507
508 Some((iter_key, val))
509 }
510 }
511}
512
513pub struct Iter<'a>(RawIter<'a>);
519
520impl<'a> Iter<'a> {
521 fn new(arr: &'a ZArr) -> Self {
522 Self(RawIter::new(arr.as_ptr() as *mut _))
523 }
524}
525
526impl<'a> Iterator for Iter<'a> {
527 type Item = (IterKey<'a>, &'a ZVal);
528
529 fn next(&mut self) -> Option<Self::Item> {
530 self.0
531 .next()
532 .map(|(key, val)| (key, unsafe { ZVal::from_ptr(val) }))
533 }
534}
535
536pub struct IterMut<'a>(RawIter<'a>);
542
543impl<'a> IterMut<'a> {
544 fn new(arr: &'a mut ZArr) -> Self {
545 Self(RawIter::new(arr.as_mut_ptr()))
546 }
547}
548
549impl<'a> Iterator for IterMut<'a> {
550 type Item = (IterKey<'a>, &'a mut ZVal);
551
552 fn next(&mut self) -> Option<Self::Item> {
553 self.0
554 .next()
555 .map(|(key, val)| (key, unsafe { ZVal::from_mut_ptr(val) }))
556 }
557}
558
559pub enum Entry<'a> {
566 Occupied(OccupiedEntry<'a>),
568 Vacant(VacantEntry<'a>),
570}
571
572pub struct OccupiedEntry<'a>(&'a mut ZVal);
575
576pub struct VacantEntry<'a> {
579 arr: &'a mut ZArr,
580 key: Key<'a>,
581}
582
583impl<'a> Entry<'a> {
584 pub fn and_modify<F>(self, f: F) -> Self
587 where
588 F: FnOnce(&mut ZVal),
589 {
590 match self {
591 Entry::Occupied(entry) => {
592 f(entry.0);
593 Entry::Occupied(entry)
594 }
595 entry => entry,
596 }
597 }
598
599 pub fn or_insert(self, val: impl Into<ZVal>) -> &'a mut ZVal {
602 match self {
603 Entry::Occupied(entry) => entry.0,
604 Entry::Vacant(entry) => {
605 let insert_key: InsertKey<'_> = entry.key.clone().into();
606 entry.arr.insert(insert_key, val);
607 entry.arr.get_mut(entry.key).unwrap()
608 }
609 }
610 }
611}
612
613fn common_fmt(this: &ZArr, f: &mut fmt::Formatter<'_>, name: &str) -> fmt::Result {
614 struct Debugger<'a>(&'a ZArr);
615
616 impl Debug for Debugger<'_> {
617 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618 f.debug_map().entries(self.0.iter()).finish()
619 }
620 }
621
622 let zd = Debugger(this);
623
624 f.debug_tuple(name).field(&zd).finish()
625}