@@ -8,12 +8,14 @@ use std::{
8
8
fmt:: { Debug , Display } ,
9
9
iter:: FromIterator ,
10
10
ptr,
11
+ str:: FromStr ,
11
12
} ;
12
13
13
14
use crate :: {
14
15
boxed:: { ZBox , ZBoxable } ,
15
16
convert:: { FromZval , IntoZval } ,
16
17
error:: { Error , Result } ,
18
+ ffi:: zend_ulong,
17
19
ffi:: {
18
20
_zend_new_array, zend_array_count, zend_array_destroy, zend_array_dup, zend_hash_clean,
19
21
zend_hash_get_current_data_ex, zend_hash_get_current_key_type_ex,
@@ -188,9 +190,46 @@ impl ZendHashTable {
188
190
/// assert_eq!(ht.get("test").and_then(|zv| zv.str()), Some("hello world"));
189
191
/// ```
190
192
#[ must_use]
191
- pub fn get ( & self , key : & ' _ str ) -> Option < & Zval > {
192
- let str = CString :: new ( key) . ok ( ) ?;
193
- unsafe { zend_hash_str_find ( self , str. as_ptr ( ) , key. len ( ) as _ ) . as_ref ( ) }
193
+ pub fn get < ' a , K > ( & self , key : K ) -> Option < & Zval >
194
+ where
195
+ K : Into < ArrayKey < ' a > > ,
196
+ {
197
+ match key. into ( ) {
198
+ ArrayKey :: Long ( index) => unsafe {
199
+ #[ allow( clippy:: cast_sign_loss) ]
200
+ zend_hash_index_find ( self , index as zend_ulong ) . as_ref ( )
201
+ } ,
202
+ ArrayKey :: String ( key) => {
203
+ if let Ok ( index) = i64:: from_str ( key. as_str ( ) ) {
204
+ #[ allow( clippy:: cast_sign_loss) ]
205
+ unsafe {
206
+ zend_hash_index_find ( self , index as zend_ulong ) . as_ref ( )
207
+ }
208
+ } else {
209
+ unsafe {
210
+ zend_hash_str_find (
211
+ self ,
212
+ CString :: new ( key. as_str ( ) ) . ok ( ) ?. as_ptr ( ) ,
213
+ key. len ( ) as _ ,
214
+ )
215
+ . as_ref ( )
216
+ }
217
+ }
218
+ }
219
+ ArrayKey :: Str ( key) => {
220
+ if let Ok ( index) = i64:: from_str ( key) {
221
+ #[ allow( clippy:: cast_sign_loss) ]
222
+ unsafe {
223
+ zend_hash_index_find ( self , index as zend_ulong ) . as_ref ( )
224
+ }
225
+ } else {
226
+ unsafe {
227
+ zend_hash_str_find ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ )
228
+ . as_ref ( )
229
+ }
230
+ }
231
+ }
232
+ }
194
233
}
195
234
196
235
/// Attempts to retrieve a value from the hash table with a string key.
@@ -219,9 +258,46 @@ impl ZendHashTable {
219
258
// hashtable while only having a reference to it. #461
220
259
#[ allow( clippy:: mut_from_ref) ]
221
260
#[ must_use]
222
- pub fn get_mut ( & self , key : & ' _ str ) -> Option < & mut Zval > {
223
- let str = CString :: new ( key) . ok ( ) ?;
224
- unsafe { zend_hash_str_find ( self , str. as_ptr ( ) , key. len ( ) as _ ) . as_mut ( ) }
261
+ pub fn get_mut < ' a , K > ( & self , key : K ) -> Option < & mut Zval >
262
+ where
263
+ K : Into < ArrayKey < ' a > > ,
264
+ {
265
+ match key. into ( ) {
266
+ ArrayKey :: Long ( index) => unsafe {
267
+ #[ allow( clippy:: cast_sign_loss) ]
268
+ zend_hash_index_find ( self , index as zend_ulong ) . as_mut ( )
269
+ } ,
270
+ ArrayKey :: String ( key) => {
271
+ if let Ok ( index) = i64:: from_str ( key. as_str ( ) ) {
272
+ #[ allow( clippy:: cast_sign_loss) ]
273
+ unsafe {
274
+ zend_hash_index_find ( self , index as zend_ulong ) . as_mut ( )
275
+ }
276
+ } else {
277
+ unsafe {
278
+ zend_hash_str_find (
279
+ self ,
280
+ CString :: new ( key. as_str ( ) ) . ok ( ) ?. as_ptr ( ) ,
281
+ key. len ( ) as _ ,
282
+ )
283
+ . as_mut ( )
284
+ }
285
+ }
286
+ }
287
+ ArrayKey :: Str ( key) => {
288
+ if let Ok ( index) = i64:: from_str ( key) {
289
+ #[ allow( clippy:: cast_sign_loss) ]
290
+ unsafe {
291
+ zend_hash_index_find ( self , index as zend_ulong ) . as_mut ( )
292
+ }
293
+ } else {
294
+ unsafe {
295
+ zend_hash_str_find ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ )
296
+ . as_mut ( )
297
+ }
298
+ }
299
+ }
300
+ }
225
301
}
226
302
227
303
/// Attempts to retrieve a value from the hash table with an index.
@@ -247,8 +323,11 @@ impl ZendHashTable {
247
323
/// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(100));
248
324
/// ```
249
325
#[ must_use]
250
- pub fn get_index ( & self , key : u64 ) -> Option < & Zval > {
251
- unsafe { zend_hash_index_find ( self , key) . as_ref ( ) }
326
+ pub fn get_index ( & self , key : i64 ) -> Option < & Zval > {
327
+ #[ allow( clippy:: cast_sign_loss) ]
328
+ unsafe {
329
+ zend_hash_index_find ( self , key as zend_ulong ) . as_ref ( )
330
+ }
252
331
}
253
332
254
333
/// Attempts to retrieve a value from the hash table with an index.
@@ -277,8 +356,11 @@ impl ZendHashTable {
277
356
// hashtable while only having a reference to it. #461
278
357
#[ allow( clippy:: mut_from_ref) ]
279
358
#[ must_use]
280
- pub fn get_index_mut ( & self , key : u64 ) -> Option < & mut Zval > {
281
- unsafe { zend_hash_index_find ( self , key) . as_mut ( ) }
359
+ pub fn get_index_mut ( & self , key : i64 ) -> Option < & mut Zval > {
360
+ unsafe {
361
+ #[ allow( clippy:: cast_sign_loss) ]
362
+ zend_hash_index_find ( self , key as zend_ulong ) . as_mut ( )
363
+ }
282
364
}
283
365
284
366
/// Attempts to remove a value from the hash table with a string key.
@@ -305,9 +387,44 @@ impl ZendHashTable {
305
387
/// ht.remove("test");
306
388
/// assert_eq!(ht.len(), 0);
307
389
/// ```
308
- pub fn remove ( & mut self , key : & str ) -> Option < ( ) > {
309
- let result =
310
- unsafe { zend_hash_str_del ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ ) } ;
390
+ pub fn remove < ' a , K > ( & mut self , key : K ) -> Option < ( ) >
391
+ where
392
+ K : Into < ArrayKey < ' a > > ,
393
+ {
394
+ let result = match key. into ( ) {
395
+ ArrayKey :: Long ( index) => unsafe {
396
+ #[ allow( clippy:: cast_sign_loss) ]
397
+ zend_hash_index_del ( self , index as zend_ulong )
398
+ } ,
399
+ ArrayKey :: String ( key) => {
400
+ if let Ok ( index) = i64:: from_str ( key. as_str ( ) ) {
401
+ #[ allow( clippy:: cast_sign_loss) ]
402
+ unsafe {
403
+ zend_hash_index_del ( self , index as zend_ulong )
404
+ }
405
+ } else {
406
+ unsafe {
407
+ zend_hash_str_del (
408
+ self ,
409
+ CString :: new ( key. as_str ( ) ) . ok ( ) ?. as_ptr ( ) ,
410
+ key. len ( ) as _ ,
411
+ )
412
+ }
413
+ }
414
+ }
415
+ ArrayKey :: Str ( key) => {
416
+ if let Ok ( index) = i64:: from_str ( key) {
417
+ #[ allow( clippy:: cast_sign_loss) ]
418
+ unsafe {
419
+ zend_hash_index_del ( self , index as zend_ulong )
420
+ }
421
+ } else {
422
+ unsafe {
423
+ zend_hash_str_del ( self , CString :: new ( key) . ok ( ) ?. as_ptr ( ) , key. len ( ) as _ )
424
+ }
425
+ }
426
+ }
427
+ } ;
311
428
312
429
if result < 0 {
313
430
None
@@ -340,8 +457,11 @@ impl ZendHashTable {
340
457
/// ht.remove_index(0);
341
458
/// assert_eq!(ht.len(), 0);
342
459
/// ```
343
- pub fn remove_index ( & mut self , key : u64 ) -> Option < ( ) > {
344
- let result = unsafe { zend_hash_index_del ( self , key) } ;
460
+ pub fn remove_index ( & mut self , key : i64 ) -> Option < ( ) > {
461
+ let result = unsafe {
462
+ #[ allow( clippy:: cast_sign_loss) ]
463
+ zend_hash_index_del ( self , key as zend_ulong )
464
+ } ;
345
465
346
466
if result < 0 {
347
467
None
@@ -379,12 +499,54 @@ impl ZendHashTable {
379
499
/// ht.insert("c", "C");
380
500
/// assert_eq!(ht.len(), 3);
381
501
/// ```
382
- pub fn insert < V > ( & mut self , key : & str , val : V ) -> Result < ( ) >
502
+ pub fn insert < ' a , K , V > ( & mut self , key : K , val : V ) -> Result < ( ) >
383
503
where
504
+ K : Into < ArrayKey < ' a > > ,
384
505
V : IntoZval ,
385
506
{
386
507
let mut val = val. into_zval ( false ) ?;
387
- unsafe { zend_hash_str_update ( self , CString :: new ( key) ?. as_ptr ( ) , key. len ( ) , & raw mut val) } ;
508
+ match key. into ( ) {
509
+ ArrayKey :: Long ( index) => {
510
+ unsafe {
511
+ #[ allow( clippy:: cast_sign_loss) ]
512
+ zend_hash_index_update ( self , index as zend_ulong , & raw mut val)
513
+ } ;
514
+ }
515
+ ArrayKey :: String ( key) => {
516
+ if let Ok ( index) = i64:: from_str ( & key) {
517
+ unsafe {
518
+ #[ allow( clippy:: cast_sign_loss) ]
519
+ zend_hash_index_update ( self , index as zend_ulong , & raw mut val)
520
+ } ;
521
+ } else {
522
+ unsafe {
523
+ zend_hash_str_update (
524
+ self ,
525
+ CString :: new ( key. as_str ( ) ) ?. as_ptr ( ) ,
526
+ key. len ( ) ,
527
+ & raw mut val,
528
+ )
529
+ } ;
530
+ }
531
+ }
532
+ ArrayKey :: Str ( key) => {
533
+ if let Ok ( index) = i64:: from_str ( key) {
534
+ unsafe {
535
+ #[ allow( clippy:: cast_sign_loss) ]
536
+ zend_hash_index_update ( self , index as zend_ulong , & raw mut val)
537
+ } ;
538
+ } else {
539
+ unsafe {
540
+ zend_hash_str_update (
541
+ self ,
542
+ CString :: new ( key) ?. as_ptr ( ) ,
543
+ key. len ( ) ,
544
+ & raw mut val,
545
+ )
546
+ } ;
547
+ }
548
+ }
549
+ }
388
550
val. release ( ) ;
389
551
Ok ( ( ) )
390
552
}
@@ -417,12 +579,15 @@ impl ZendHashTable {
417
579
/// ht.insert_at_index(0, "C"); // notice overriding index 0
418
580
/// assert_eq!(ht.len(), 2);
419
581
/// ```
420
- pub fn insert_at_index < V > ( & mut self , key : u64 , val : V ) -> Result < ( ) >
582
+ pub fn insert_at_index < V > ( & mut self , key : i64 , val : V ) -> Result < ( ) >
421
583
where
422
584
V : IntoZval ,
423
585
{
424
586
let mut val = val. into_zval ( false ) ?;
425
- unsafe { zend_hash_index_update ( self , key, & raw mut val) } ;
587
+ unsafe {
588
+ #[ allow( clippy:: cast_sign_loss) ]
589
+ zend_hash_index_update ( self , key as zend_ulong , & raw mut val)
590
+ } ;
426
591
val. release ( ) ;
427
592
Ok ( ( ) )
428
593
}
@@ -560,6 +725,8 @@ impl ZendHashTable {
560
725
/// }
561
726
/// ArrayKey::String(key) => {
562
727
/// }
728
+ /// ArrayKey::Str(key) => {
729
+ /// }
563
730
/// }
564
731
/// dbg!(key, val);
565
732
/// }
@@ -612,15 +779,25 @@ pub struct Iter<'a> {
612
779
}
613
780
614
781
/// Represents the key of a PHP array, which can be either a long or a string.
615
- #[ derive( Debug , PartialEq ) ]
616
- pub enum ArrayKey {
782
+ #[ derive( Debug , Clone , PartialEq ) ]
783
+ pub enum ArrayKey < ' a > {
617
784
/// A numerical key.
785
+ /// In Zend API it's represented by `u64` (`zend_ulong`), so the value needs
786
+ /// to be cast to `zend_ulong` before passing into Zend functions.
618
787
Long ( i64 ) ,
619
788
/// A string key.
620
789
String ( String ) ,
790
+ /// A string key by reference.
791
+ Str ( & ' a str ) ,
621
792
}
622
793
623
- impl ArrayKey {
794
+ impl From < String > for ArrayKey < ' _ > {
795
+ fn from ( value : String ) -> Self {
796
+ Self :: String ( value)
797
+ }
798
+ }
799
+
800
+ impl ArrayKey < ' _ > {
624
801
/// Check if the key is an integer.
625
802
///
626
803
/// # Returns
@@ -630,21 +807,34 @@ impl ArrayKey {
630
807
pub fn is_long ( & self ) -> bool {
631
808
match self {
632
809
ArrayKey :: Long ( _) => true ,
633
- ArrayKey :: String ( _) => false ,
810
+ ArrayKey :: String ( _) | ArrayKey :: Str ( _ ) => false ,
634
811
}
635
812
}
636
813
}
637
814
638
- impl Display for ArrayKey {
815
+ impl Display for ArrayKey < ' _ > {
639
816
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
640
817
match self {
641
818
ArrayKey :: Long ( key) => write ! ( f, "{key}" ) ,
642
819
ArrayKey :: String ( key) => write ! ( f, "{key}" ) ,
820
+ ArrayKey :: Str ( key) => write ! ( f, "{key}" ) ,
643
821
}
644
822
}
645
823
}
646
824
647
- impl < ' a > FromZval < ' a > for ArrayKey {
825
+ impl < ' a > From < & ' a str > for ArrayKey < ' a > {
826
+ fn from ( key : & ' a str ) -> ArrayKey < ' a > {
827
+ ArrayKey :: Str ( key)
828
+ }
829
+ }
830
+
831
+ impl < ' a > From < i64 > for ArrayKey < ' a > {
832
+ fn from ( index : i64 ) -> ArrayKey < ' a > {
833
+ ArrayKey :: Long ( index)
834
+ }
835
+ }
836
+
837
+ impl < ' a > FromZval < ' a > for ArrayKey < ' _ > {
648
838
const TYPE : DataType = DataType :: String ;
649
839
650
840
fn from_zval ( zval : & ' a Zval ) -> Option < Self > {
@@ -686,7 +876,7 @@ impl<'a> Iter<'a> {
686
876
}
687
877
688
878
impl < ' a > IntoIterator for & ' a ZendHashTable {
689
- type Item = ( ArrayKey , & ' a Zval ) ;
879
+ type Item = ( ArrayKey < ' a > , & ' a Zval ) ;
690
880
type IntoIter = Iter < ' a > ;
691
881
692
882
/// Returns an iterator over the key(s) and value contained inside the
@@ -713,7 +903,7 @@ impl<'a> IntoIterator for &'a ZendHashTable {
713
903
}
714
904
715
905
impl < ' a > Iterator for Iter < ' a > {
716
- type Item = ( ArrayKey , & ' a Zval ) ;
906
+ type Item = ( ArrayKey < ' a > , & ' a Zval ) ;
717
907
718
908
fn next ( & mut self ) -> Option < Self :: Item > {
719
909
self . next_zval ( )
@@ -1052,8 +1242,8 @@ impl FromIterator<Zval> for ZBox<ZendHashTable> {
1052
1242
}
1053
1243
}
1054
1244
1055
- impl FromIterator < ( u64 , Zval ) > for ZBox < ZendHashTable > {
1056
- fn from_iter < T : IntoIterator < Item = ( u64 , Zval ) > > ( iter : T ) -> Self {
1245
+ impl FromIterator < ( i64 , Zval ) > for ZBox < ZendHashTable > {
1246
+ fn from_iter < T : IntoIterator < Item = ( i64 , Zval ) > > ( iter : T ) -> Self {
1057
1247
let mut ht = ZendHashTable :: new ( ) ;
1058
1248
for ( key, val) in iter {
1059
1249
// Inserting a zval cannot fail, as `push` only returns `Err` if converting
0 commit comments