@@ -195,23 +195,18 @@ static HashTable* spl_fixedarray_object_get_gc(zend_object *obj, zval **table, i
195
195
return ht ;
196
196
}
197
197
198
- static HashTable * spl_fixedarray_object_get_properties (zend_object * obj )
198
+ static HashTable * spl_fixedarray_get_properties_for (zend_object * obj , zend_prop_purpose purpose )
199
199
{
200
200
spl_fixedarray_object * intern = spl_fixed_array_from_obj (obj );
201
- HashTable * ht = zend_std_get_properties (obj );
202
201
203
- if (! spl_fixedarray_empty ( & intern -> array )) {
204
- zend_long j = zend_hash_num_elements ( ht );
202
+ /* Keep the values and properties separate*/
203
+ HashTable * ht = zend_array_dup ( zend_std_get_properties ( obj ) );
205
204
205
+ if (!spl_fixedarray_empty (& intern -> array )) {
206
206
for (zend_long i = 0 ; i < intern -> array .size ; i ++ ) {
207
- zend_hash_index_update (ht , i , & intern -> array .elements [i ]);
207
+ zend_hash_index_add_new (ht , i , & intern -> array .elements [i ]);
208
208
Z_TRY_ADDREF (intern -> array .elements [i ]);
209
209
}
210
- if (j > intern -> array .size ) {
211
- for (zend_long i = intern -> array .size ; i < j ; ++ i ) {
212
- zend_hash_index_del (ht , i );
213
- }
214
- }
215
210
}
216
211
217
212
return ht ;
@@ -628,7 +623,6 @@ PHP_METHOD(SplFixedArray, fromArray)
628
623
} else if (num > 0 && !save_indexes ) {
629
624
zval * element ;
630
625
zend_long i = 0 ;
631
-
632
626
spl_fixedarray_init (& array , num );
633
627
634
628
ZEND_HASH_FOREACH_VAL (Z_ARRVAL_P (data ), element ) {
@@ -754,6 +748,182 @@ PHP_METHOD(SplFixedArray, getIterator)
754
748
zend_create_internal_iterator_zval (return_value , ZEND_THIS );
755
749
}
756
750
751
+ static zend_result spl_fixedarray_import_packed (zend_object * object , HashTable * ht )
752
+ {
753
+ ZEND_ASSERT (HT_FLAGS (ht ) & HASH_FLAG_PACKED );
754
+
755
+ spl_fixedarray_object * intern = spl_fixed_array_from_obj (object );
756
+ spl_fixedarray * fixed = & intern -> array ;
757
+
758
+ uint32_t n = zend_hash_num_elements (ht );
759
+ zend_ulong num_idx = 0 , i = 0 ;
760
+
761
+ /* This will allocate memory even if the value checks fail, but allows
762
+ * a single iteration over the input array.
763
+ * It's also manually initialized to do a single pass over the output.
764
+ * Be sure to dtor and free the correct range if there's an error during
765
+ * the construction process.
766
+ */
767
+ fixed -> size = n ;
768
+ fixed -> elements = safe_emalloc (n , sizeof (zval ), 0 );
769
+ zval * begin = fixed -> elements , * end = fixed -> elements + n ;
770
+ zval * val = NULL ;
771
+
772
+ ZEND_HASH_FOREACH_NUM_KEY_VAL (ht , num_idx , val ) {
773
+ if (UNEXPECTED (num_idx != i )) {
774
+ spl_fixedarray_dtor_range (fixed , 0 , i );
775
+ efree (fixed -> elements );
776
+
777
+ /* Zero-initialize so the object release is valid. */
778
+ spl_fixedarray_init (fixed , 0 );
779
+
780
+ zend_argument_value_error (1 ,
781
+ "did not have integer keys that start at 0 and increment sequentially" );
782
+ return FAILURE ;
783
+ }
784
+
785
+ i += 1 ;
786
+
787
+ ZEND_ASSERT (begin != end );
788
+ ZVAL_COPY (begin ++ , val );
789
+ } ZEND_HASH_FOREACH_END ();
790
+
791
+ ZEND_ASSERT (begin == end );
792
+
793
+ return SUCCESS ;
794
+ }
795
+
796
+ static zend_result spl_fixedarray_import_not_packed (zend_object * object , HashTable * ht )
797
+ {
798
+ ZEND_ASSERT (!(HT_FLAGS (ht ) & HASH_FLAG_PACKED ));
799
+
800
+ spl_fixedarray_object * intern = spl_fixed_array_from_obj (object );
801
+ spl_fixedarray * fixed = & intern -> array ;
802
+
803
+ /* The array can have string keys, but they must come prior to integers:
804
+ *
805
+ * SplFixedArray::__set_state(array(
806
+ * 'property' => 'value',
807
+ * 0 => 1,
808
+ * 1 => 2,
809
+ * 2 => 3,
810
+ * ))
811
+ */
812
+ spl_fixedarray_init (fixed , 0 );
813
+
814
+ /* For performance we do a single pass over the input array and the
815
+ * spl_fixedarray elems. This complicates the implementation, but part
816
+ * of the reason for choosing SplFixedArray is for peformance, and
817
+ * although that is primarily for memory we should still care about CPU.
818
+ *
819
+ * We need to track the number of string keys, which must come before
820
+ * all the integer keys. This way the total size of the input minus the
821
+ * number of string keys equals the number of integer keys, so we can
822
+ * allocate the spl_fixedarray.elements and do this in a single pass.
823
+ */
824
+
825
+ // The total size of the input array
826
+ const zend_long nht = zend_hash_num_elements (ht );
827
+ zend_long nstrings = 0 ; // The number of string keys
828
+ zend_long nints ; // will be nht - nstrings
829
+
830
+ zend_string * str_idx = NULL ; // current string key of input array
831
+ zend_ulong num_idx = 0 ; // current int key (valid iff str_idx == NULL)
832
+ zval * val = NULL ; // current value of input array
833
+ zend_long max_idx = -1 ; // the largest index found so far
834
+ zval * begin = NULL ; // pointer to beginning of fixedarray's storage
835
+ zval * end = NULL ; // points one element passed the last element
836
+ const char * ex_msg = NULL ; // message for the value exception
837
+
838
+ ZEND_HASH_FOREACH_KEY_VAL (ht , num_idx , str_idx , val ) {
839
+ if (str_idx != NULL ) {
840
+ if (UNEXPECTED (max_idx >= 0 )) {
841
+ ex_msg = "must have all its string keys come before all integer keys" ;
842
+ goto value_error ;
843
+ }
844
+
845
+ ++ nstrings ;
846
+ object -> handlers -> write_property (object , str_idx , val , NULL );
847
+
848
+ } else if (UNEXPECTED ((zend_long )num_idx != ++ max_idx )) {
849
+ ex_msg = "did not have integer keys that start at 0 and increment sequentially" ;
850
+ goto value_error ;
851
+
852
+ } else {
853
+ if (UNEXPECTED (max_idx == 0 )) {
854
+ nints = nht - nstrings ;
855
+ fixed -> size = nints ;
856
+ fixed -> elements =
857
+ safe_emalloc (nints , sizeof (zval ), 0 );
858
+ begin = fixed -> elements ;
859
+ end = fixed -> elements + nints ;
860
+ }
861
+
862
+ ZEND_ASSERT (num_idx == max_idx );
863
+ ZEND_ASSERT (begin != end );
864
+
865
+ ZVAL_COPY (begin ++ , val );
866
+ }
867
+ } ZEND_HASH_FOREACH_END ();
868
+
869
+ ZEND_ASSERT (begin == end );
870
+ return SUCCESS ;
871
+
872
+ value_error :
873
+ spl_fixedarray_dtor_range (fixed , 0 , max_idx );
874
+ if (fixed -> elements ) {
875
+ efree (fixed -> elements );
876
+ }
877
+
878
+ /* Zero-initialize so the object release is valid. */
879
+ spl_fixedarray_init (fixed , 0 );
880
+
881
+ zend_argument_value_error (1 , ex_msg );
882
+ return FAILURE ;
883
+ }
884
+
885
+ /* Assumes the object has been created and the spl_fixedarray_object's
886
+ * array member is uninitialized or zero-initialized.
887
+ */
888
+ static zend_result spl_fixedarray_import (zend_object * object , HashTable * ht )
889
+ {
890
+
891
+ /* if result != SUCCESS then these need to dtor their contents. */
892
+ zend_result result = (EXPECTED ((HT_FLAGS (ht ) & HASH_FLAG_PACKED )))
893
+ ? spl_fixedarray_import_packed (object , ht )
894
+ : spl_fixedarray_import_not_packed (object , ht );
895
+
896
+ if (UNEXPECTED (result != SUCCESS )) {
897
+ zend_object_release (object );
898
+ }
899
+
900
+ return result ;
901
+ }
902
+
903
+ PHP_METHOD (SplFixedArray , __set_state )
904
+ {
905
+ zval * state = NULL ;
906
+
907
+ ZEND_PARSE_PARAMETERS_START (1 , 1 )
908
+ Z_PARAM_ARRAY (state );
909
+ ZEND_PARSE_PARAMETERS_END ();
910
+
911
+ HashTable * ht = Z_ARRVAL_P (state );
912
+ zend_class_entry * called_scope = zend_get_called_scope (execute_data );
913
+
914
+ zend_result result = object_init_ex (return_value , called_scope );
915
+ if (UNEXPECTED (result != SUCCESS )) {
916
+ ZVAL_NULL (return_value );
917
+ RETURN_THROWS ();
918
+ }
919
+
920
+ zend_object * object = Z_OBJ_P (return_value );
921
+ if (UNEXPECTED (spl_fixedarray_import (object , ht ) != SUCCESS )) {
922
+ ZVAL_NULL (return_value );
923
+ RETURN_THROWS ();
924
+ }
925
+ }
926
+
757
927
static void spl_fixedarray_it_dtor (zend_object_iterator * iter )
758
928
{
759
929
zval_ptr_dtor (& iter -> data );
@@ -844,7 +1014,8 @@ PHP_MINIT_FUNCTION(spl_fixedarray)
844
1014
spl_handler_SplFixedArray .unset_dimension = spl_fixedarray_object_unset_dimension ;
845
1015
spl_handler_SplFixedArray .has_dimension = spl_fixedarray_object_has_dimension ;
846
1016
spl_handler_SplFixedArray .count_elements = spl_fixedarray_object_count_elements ;
847
- spl_handler_SplFixedArray .get_properties = spl_fixedarray_object_get_properties ;
1017
+ spl_handler_SplFixedArray .get_properties_for
1018
+ = spl_fixedarray_get_properties_for ;
848
1019
spl_handler_SplFixedArray .get_gc = spl_fixedarray_object_get_gc ;
849
1020
spl_handler_SplFixedArray .dtor_obj = zend_objects_destroy_object ;
850
1021
spl_handler_SplFixedArray .free_obj = spl_fixedarray_object_free_storage ;
0 commit comments