@@ -195,23 +195,18 @@ static HashTable* spl_fixedarray_object_get_gc(zend_object *obj, zval **table, i
195195 return ht ;
196196}
197197
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 )
199199{
200200 spl_fixedarray_object * intern = spl_fixed_array_from_obj (obj );
201- HashTable * ht = zend_std_get_properties (obj );
202201
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 ) );
205204
205+ if (!spl_fixedarray_empty (& intern -> array )) {
206206 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 ]);
208208 Z_TRY_ADDREF (intern -> array .elements [i ]);
209209 }
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- }
215210 }
216211
217212 return ht ;
@@ -628,7 +623,6 @@ PHP_METHOD(SplFixedArray, fromArray)
628623 } else if (num > 0 && !save_indexes ) {
629624 zval * element ;
630625 zend_long i = 0 ;
631-
632626 spl_fixedarray_init (& array , num );
633627
634628 ZEND_HASH_FOREACH_VAL (Z_ARRVAL_P (data ), element ) {
@@ -754,6 +748,182 @@ PHP_METHOD(SplFixedArray, getIterator)
754748 zend_create_internal_iterator_zval (return_value , ZEND_THIS );
755749}
756750
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+
757927static void spl_fixedarray_it_dtor (zend_object_iterator * iter )
758928{
759929 zval_ptr_dtor (& iter -> data );
@@ -844,7 +1014,8 @@ PHP_MINIT_FUNCTION(spl_fixedarray)
8441014 spl_handler_SplFixedArray .unset_dimension = spl_fixedarray_object_unset_dimension ;
8451015 spl_handler_SplFixedArray .has_dimension = spl_fixedarray_object_has_dimension ;
8461016 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 ;
8481019 spl_handler_SplFixedArray .get_gc = spl_fixedarray_object_get_gc ;
8491020 spl_handler_SplFixedArray .dtor_obj = zend_objects_destroy_object ;
8501021 spl_handler_SplFixedArray .free_obj = spl_fixedarray_object_free_storage ;
0 commit comments