@@ -91,6 +91,11 @@ PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */
91
91
}
92
92
/* }}} */
93
93
94
+ static zend_always_inline bool zend_may_modify_array_in_place (const zval * arg )
95
+ {
96
+ return !(GC_FLAGS (Z_ARRVAL_P (arg )) & (IS_ARRAY_IMMUTABLE | IS_ARRAY_PERSISTENT )) && Z_REFCOUNT_P (arg ) == 1 ;
97
+ }
98
+
94
99
static zend_never_inline ZEND_COLD int stable_sort_fallback (Bucket * a , Bucket * b ) {
95
100
if (Z_EXTRA (a -> val ) > Z_EXTRA (b -> val )) {
96
101
return 1 ;
@@ -901,8 +906,12 @@ static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compar
901
906
RETURN_TRUE ;
902
907
}
903
908
904
- /* Copy array, so the in-place modifications will not be visible to the callback function */
905
- arr = zend_array_dup (arr );
909
+ /* Copy array, so the in-place modifications will not be visible to the callback function.
910
+ * Unless there are no other references since we know for sure it won't be visible. */
911
+ bool in_place = zend_may_modify_array_in_place (array );
912
+ if (!in_place ) {
913
+ arr = zend_array_dup (arr );
914
+ }
906
915
907
916
zend_hash_sort (arr , compare_func , renumber );
908
917
@@ -911,6 +920,10 @@ static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compar
911
920
ZVAL_ARR (array , arr );
912
921
zval_ptr_dtor (& garbage );
913
922
923
+ if (in_place ) {
924
+ GC_ADDREF (arr );
925
+ }
926
+
914
927
PHP_ARRAY_CMP_FUNC_RESTORE ();
915
928
RETURN_TRUE ;
916
929
}
@@ -3866,10 +3879,17 @@ static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM
3866
3879
}
3867
3880
}
3868
3881
3869
- /* copy first array */
3882
+ /* copy first array if necessary */
3870
3883
arg = args ;
3871
- dest = zend_array_dup (Z_ARRVAL_P (arg ));
3884
+ bool in_place = zend_may_modify_array_in_place (arg );
3885
+ if (in_place ) {
3886
+ dest = Z_ARRVAL_P (arg );
3887
+ } else {
3888
+ dest = zend_array_dup (Z_ARRVAL_P (arg ));
3889
+ }
3890
+
3872
3891
ZVAL_ARR (return_value , dest );
3892
+
3873
3893
if (recursive ) {
3874
3894
for (i = 1 ; i < argc ; i ++ ) {
3875
3895
arg = args + i ;
@@ -3881,6 +3901,10 @@ static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM
3881
3901
zend_hash_merge (dest , Z_ARRVAL_P (arg ), zval_add_ref , 1 );
3882
3902
}
3883
3903
}
3904
+
3905
+ if (in_place ) {
3906
+ GC_ADDREF (dest );
3907
+ }
3884
3908
}
3885
3909
/* }}} */
3886
3910
@@ -3949,7 +3973,7 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET
3949
3973
/* copy first array if necessary */
3950
3974
if (HT_IS_PACKED (src )) {
3951
3975
/* Note: If it has holes, it might get sequentialized */
3952
- if (HT_IS_WITHOUT_HOLES (src ) && !( GC_FLAGS ( Z_ARRVAL_P ( arg )) & ( IS_ARRAY_IMMUTABLE | IS_ARRAY_PERSISTENT )) && Z_REFCOUNT_P ( arg ) == 1 ) {
3976
+ if (HT_IS_WITHOUT_HOLES (src ) && zend_may_modify_array_in_place ( arg )) {
3953
3977
dest = src ;
3954
3978
in_place = true;
3955
3979
ZVAL_ARR (return_value , dest );
@@ -4001,7 +4025,7 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET
4001
4025
}
4002
4026
4003
4027
if (in_place ) {
4004
- GC_ADDREF (Z_ARRVAL_P ( return_value ) );
4028
+ GC_ADDREF (dest );
4005
4029
}
4006
4030
//fprintf(stderr, "after, in-place %d %d\n", in_place, GC_REFCOUNT(Z_ARRVAL_P(return_value)));
4007
4031
}
@@ -4611,7 +4635,12 @@ PHP_FUNCTION(array_unique)
4611
4635
4612
4636
cmp = php_get_data_compare_func_unstable (sort_type , 0 );
4613
4637
4614
- RETVAL_ARR (zend_array_dup (Z_ARRVAL_P (array )));
4638
+ bool in_place = zend_may_modify_array_in_place (array );
4639
+ if (in_place ) {
4640
+ RETVAL_ARR (Z_ARRVAL_P (array ));
4641
+ } else {
4642
+ RETVAL_ARR (zend_array_dup (Z_ARRVAL_P (array )));
4643
+ }
4615
4644
4616
4645
/* create and sort array with pointers to the target_hash buckets */
4617
4646
arTmp = pemalloc ((Z_ARRVAL_P (array )-> nNumOfElements + 1 ) * sizeof (struct bucketindex ), GC_FLAGS (Z_ARRVAL_P (array )) & IS_ARRAY_PERSISTENT );
@@ -4657,6 +4686,10 @@ PHP_FUNCTION(array_unique)
4657
4686
}
4658
4687
}
4659
4688
pefree (arTmp , GC_FLAGS (Z_ARRVAL_P (array )) & IS_ARRAY_PERSISTENT );
4689
+
4690
+ if (in_place ) {
4691
+ Z_ADDREF_P (return_value );
4692
+ }
4660
4693
}
4661
4694
/* }}} */
4662
4695
@@ -4781,6 +4814,7 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int
4781
4814
zend_fcall_info * fci_key = NULL , * fci_data ;
4782
4815
zend_fcall_info_cache * fci_key_cache = NULL , * fci_data_cache ;
4783
4816
PHP_ARRAY_CMP_FUNC_VARS ;
4817
+ bool in_place = false;
4784
4818
4785
4819
bucket_compare_func_t intersect_key_compare_func ;
4786
4820
bucket_compare_func_t intersect_data_compare_func ;
@@ -4907,8 +4941,13 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int
4907
4941
}
4908
4942
}
4909
4943
4910
- /* copy the argument array */
4911
- RETVAL_ARR (zend_array_dup (Z_ARRVAL (args [0 ])));
4944
+ /* copy the argument array if necessary */
4945
+ in_place = zend_may_modify_array_in_place (& args [0 ]);
4946
+ if (in_place ) {
4947
+ RETVAL_ARR (Z_ARRVAL_P (& args [0 ]));
4948
+ } else {
4949
+ RETVAL_ARR (zend_array_dup (Z_ARRVAL_P (& args [0 ])));
4950
+ }
4912
4951
4913
4952
/* go through the lists and look for common values */
4914
4953
while (Z_TYPE (ptrs [0 ]-> val ) != IS_UNDEF ) {
@@ -5019,6 +5058,10 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int
5019
5058
5020
5059
efree (ptrs );
5021
5060
efree (lists );
5061
+
5062
+ if (in_place ) {
5063
+ Z_ADDREF_P (return_value );
5064
+ }
5022
5065
}
5023
5066
/* }}} */
5024
5067
0 commit comments