@@ -282,17 +282,17 @@ static zend_always_inline int php_array_key_compare_string_locale_unstable_i(Buc
282282}
283283/* }}} */
284284
285- static zend_always_inline int php_array_data_compare_unstable_i ( Bucket * f , Bucket * s ) /* {{{ */
285+ static zend_always_inline int php_array_data_compare_zval_unstable_i ( zval * f , zval * s ) /* {{{ */
286286{
287- int result = zend_compare (& f -> val , & s -> val );
287+ int result = zend_compare (f , s );
288288 /* Special enums handling for array_unique. We don't want to add this logic to zend_compare as
289289 * that would be observable via comparison operators. */
290- zval * rhs = & s -> val ;
290+ zval * rhs = s ;
291291 ZVAL_DEREF (rhs );
292292 if (UNEXPECTED (Z_TYPE_P (rhs ) == IS_OBJECT )
293293 && result == ZEND_UNCOMPARABLE
294294 && (Z_OBJCE_P (rhs )-> ce_flags & ZEND_ACC_ENUM )) {
295- zval * lhs = & f -> val ;
295+ zval * lhs = f ;
296296 ZVAL_DEREF (lhs );
297297 if (Z_TYPE_P (lhs ) == IS_OBJECT && (Z_OBJCE_P (lhs )-> ce_flags & ZEND_ACC_ENUM )) {
298298 // Order doesn't matter, we just need to group the same enum values
@@ -308,29 +308,29 @@ static zend_always_inline int php_array_data_compare_unstable_i(Bucket *f, Bucke
308308}
309309/* }}} */
310310
311- static zend_always_inline int php_array_data_compare_numeric_unstable_i ( Bucket * f , Bucket * s ) /* {{{ */
311+ static zend_always_inline int php_array_data_compare_numeric_zval_unstable_i ( zval * f , zval * s ) /* {{{ */
312312{
313- return numeric_compare_function (& f -> val , & s -> val );
313+ return numeric_compare_function (f , s );
314314}
315315/* }}} */
316316
317- static zend_always_inline int php_array_data_compare_string_case_unstable_i ( Bucket * f , Bucket * s ) /* {{{ */
317+ static zend_always_inline int php_array_data_compare_string_case_zval_unstable_i ( zval * f , zval * s ) /* {{{ */
318318{
319- return string_case_compare_function (& f -> val , & s -> val );
319+ return string_case_compare_function (f , s );
320320}
321321/* }}} */
322322
323- static zend_always_inline int php_array_data_compare_string_unstable_i ( Bucket * f , Bucket * s ) /* {{{ */
323+ static zend_always_inline int php_array_data_compare_string_zval_unstable_i ( zval * f , zval * s ) /* {{{ */
324324{
325- return string_compare_function (& f -> val , & s -> val );
325+ return string_compare_function (f , s );
326326}
327327/* }}} */
328328
329- static int php_array_natural_general_compare (Bucket * f , Bucket * s , bool fold_case ) /* {{{ */
329+ static int php_array_natural_general_compare (zval * f , zval * s , bool fold_case ) /* {{{ */
330330{
331331 zend_string * tmp_str1 , * tmp_str2 ;
332- zend_string * str1 = zval_get_tmp_string (& f -> val , & tmp_str1 );
333- zend_string * str2 = zval_get_tmp_string (& s -> val , & tmp_str2 );
332+ zend_string * str1 = zval_get_tmp_string (f , & tmp_str1 );
333+ zend_string * str2 = zval_get_tmp_string (s , & tmp_str2 );
334334
335335 int result = strnatcmp_ex (ZSTR_VAL (str1 ), ZSTR_LEN (str1 ), ZSTR_VAL (str2 ), ZSTR_LEN (str2 ), fold_case );
336336
@@ -340,36 +340,62 @@ static int php_array_natural_general_compare(Bucket *f, Bucket *s, bool fold_cas
340340}
341341/* }}} */
342342
343- static zend_always_inline int php_array_natural_compare_unstable_i ( Bucket * a , Bucket * b ) /* {{{ */
343+ static zend_always_inline int php_array_natural_compare_zval_unstable_i ( zval * a , zval * b ) /* {{{ */
344344{
345345 return php_array_natural_general_compare (a , b , false);
346346}
347347/* }}} */
348348
349- static zend_always_inline int php_array_natural_case_compare_unstable_i ( Bucket * a , Bucket * b ) /* {{{ */
349+ static zend_always_inline int php_array_natural_case_compare_zval_unstable_i ( zval * a , zval * b ) /* {{{ */
350350{
351351 return php_array_natural_general_compare (a , b , true);
352352}
353353/* }}} */
354354
355- static int php_array_data_compare_string_locale_unstable_i ( Bucket * f , Bucket * s ) /* {{{ */
355+ static int php_array_data_compare_string_locale_zval_unstable_i ( zval * f , zval * s ) /* {{{ */
356356{
357- return string_locale_compare_function (& f -> val , & s -> val );
357+ return string_locale_compare_function (f , s );
358358}
359359/* }}} */
360360
361+ static zend_never_inline ZEND_COLD int stable_zval_sort_fallback (const zval * a , const zval * b )
362+ {
363+ return ZEND_THREEWAY_COMPARE (Z_EXTRA_P (a ), Z_EXTRA_P (b ));
364+ }
365+
366+ /* Share value comparisons between Bucket sorting and packed zval sorting. */
367+ #define DEFINE_DATA_SORT_VARIANTS (name ) \
368+ static zend_always_inline int php_array_##name##_unstable_i(Bucket *a, Bucket *b) { \
369+ return php_array_##name##_zval_unstable_i(&a->val, &b->val); \
370+ } \
371+ DEFINE_SORT_VARIANTS(name) \
372+ static zend_never_inline int php_array_packed_##name(const void *a, const void *b) { \
373+ int result = php_array_##name##_zval_unstable_i((zval *) a, (zval *) b); \
374+ if (EXPECTED(result)) { \
375+ return result; \
376+ } \
377+ return stable_zval_sort_fallback(a, b); \
378+ } \
379+ static zend_never_inline int php_array_packed_reverse_##name(const void *a, const void *b) { \
380+ int result = php_array_##name##_zval_unstable_i((zval *) a, (zval *) b) * -1; \
381+ if (EXPECTED(result)) { \
382+ return result; \
383+ } \
384+ return stable_zval_sort_fallback(a, b); \
385+ }
386+
361387DEFINE_SORT_VARIANTS (key_compare );
362388DEFINE_SORT_VARIANTS (key_compare_numeric );
363389DEFINE_SORT_VARIANTS (key_compare_string_case );
364390DEFINE_SORT_VARIANTS (key_compare_string );
365391DEFINE_SORT_VARIANTS (key_compare_string_locale );
366- DEFINE_SORT_VARIANTS (data_compare );
367- DEFINE_SORT_VARIANTS (data_compare_numeric );
368- DEFINE_SORT_VARIANTS (data_compare_string_case );
369- DEFINE_SORT_VARIANTS (data_compare_string );
370- DEFINE_SORT_VARIANTS (data_compare_string_locale );
371- DEFINE_SORT_VARIANTS (natural_compare );
372- DEFINE_SORT_VARIANTS (natural_case_compare );
392+ DEFINE_DATA_SORT_VARIANTS (data_compare );
393+ DEFINE_DATA_SORT_VARIANTS (data_compare_numeric );
394+ DEFINE_DATA_SORT_VARIANTS (data_compare_string_case );
395+ DEFINE_DATA_SORT_VARIANTS (data_compare_string );
396+ DEFINE_DATA_SORT_VARIANTS (data_compare_string_locale );
397+ DEFINE_DATA_SORT_VARIANTS (natural_compare );
398+ DEFINE_DATA_SORT_VARIANTS (natural_case_compare );
373399
374400static bucket_compare_func_t php_get_key_compare_func (zend_long sort_type )
375401{
@@ -491,6 +517,39 @@ static bucket_compare_func_t php_get_data_reverse_compare_func(zend_long sort_ty
491517 return NULL ;
492518}
493519
520+ static zend_always_inline compare_func_t php_get_packed_data_compare_func_ex (zend_long sort_type , bool reverse )
521+ {
522+ switch (sort_type & ~PHP_SORT_FLAG_CASE ) {
523+ case PHP_SORT_NUMERIC :
524+ return reverse ? php_array_packed_reverse_data_compare_numeric : php_array_packed_data_compare_numeric ;
525+ case PHP_SORT_STRING :
526+ if (sort_type & PHP_SORT_FLAG_CASE ) {
527+ return reverse ? php_array_packed_reverse_data_compare_string_case : php_array_packed_data_compare_string_case ;
528+ }
529+ return reverse ? php_array_packed_reverse_data_compare_string : php_array_packed_data_compare_string ;
530+ case PHP_SORT_NATURAL :
531+ if (sort_type & PHP_SORT_FLAG_CASE ) {
532+ return reverse ? php_array_packed_reverse_natural_case_compare : php_array_packed_natural_case_compare ;
533+ }
534+ return reverse ? php_array_packed_reverse_natural_compare : php_array_packed_natural_compare ;
535+ case PHP_SORT_LOCALE_STRING :
536+ return reverse ? php_array_packed_reverse_data_compare_string_locale : php_array_packed_data_compare_string_locale ;
537+ case PHP_SORT_REGULAR :
538+ default :
539+ return reverse ? php_array_packed_reverse_data_compare : php_array_packed_data_compare ;
540+ }
541+ }
542+
543+ static compare_func_t php_get_packed_data_compare_func (zend_long sort_type )
544+ {
545+ return php_get_packed_data_compare_func_ex (sort_type , false);
546+ }
547+
548+ static compare_func_t php_get_packed_data_reverse_compare_func (zend_long sort_type )
549+ {
550+ return php_get_packed_data_compare_func_ex (sort_type , true);
551+ }
552+
494553static bucket_compare_func_t php_get_data_compare_func_unstable (zend_long sort_type , bool reverse ) /* {{{ */
495554{
496555 switch (sort_type & ~PHP_SORT_FLAG_CASE ) {
@@ -694,6 +753,7 @@ PHP_FUNCTION(natcasesort)
694753/* }}} */
695754
696755typedef bucket_compare_func_t (* get_compare_function )(zend_long );
756+ typedef compare_func_t (* get_packed_compare_function )(zend_long );
697757
698758static int php_array_packed_long_compare (const void * a , const void * b )
699759{
@@ -726,7 +786,7 @@ static bool php_array_try_packed_long_sort(HashTable *array, compare_func_t cmp)
726786}
727787
728788static zend_always_inline void php_sort (INTERNAL_FUNCTION_PARAMETERS ,
729- get_compare_function get_cmp , bool renumber , compare_func_t packed_cmp ) {
789+ get_compare_function get_cmp , bool renumber , get_packed_compare_function get_packed_cmp ) {
730790 HashTable * array ;
731791 zend_long sort_type = PHP_SORT_REGULAR ;
732792 bucket_compare_func_t cmp ;
@@ -737,15 +797,22 @@ static zend_always_inline void php_sort(INTERNAL_FUNCTION_PARAMETERS,
737797 Z_PARAM_LONG (sort_type )
738798 ZEND_PARSE_PARAMETERS_END ();
739799
740- cmp = get_cmp (sort_type );
741-
742- /* Keep arrays that do not need sorting on the existing path. */
743- if (renumber && packed_cmp && array -> nNumOfElements > 1
744- && (cmp == php_array_data_compare || cmp == php_array_reverse_data_compare )
745- && php_array_try_packed_long_sort (array , packed_cmp )) {
800+ /* Keep small arrays on the existing path. */
801+ if (renumber && get_packed_cmp && array -> nNumOfElements >= 64 && HT_IS_PACKED (array )) {
802+ compare_func_t packed_cmp = get_packed_cmp (sort_type );
803+ compare_func_t long_cmp = NULL ;
804+ if (packed_cmp == php_array_packed_data_compare ) {
805+ long_cmp = php_array_packed_long_compare ;
806+ } else if (packed_cmp == php_array_packed_reverse_data_compare ) {
807+ long_cmp = php_array_packed_long_reverse_compare ;
808+ }
809+ if (!long_cmp || !php_array_try_packed_long_sort (array , long_cmp )) {
810+ zend_array_sort_packed (array , packed_cmp );
811+ }
746812 RETURN_TRUE ;
747813 }
748814
815+ cmp = get_cmp (sort_type );
749816 zend_array_sort (array , cmp , renumber );
750817
751818 RETURN_TRUE ;
@@ -769,15 +836,15 @@ PHP_FUNCTION(arsort)
769836PHP_FUNCTION (sort )
770837{
771838 php_sort (INTERNAL_FUNCTION_PARAM_PASSTHRU , php_get_data_compare_func , true,
772- php_array_packed_long_compare );
839+ php_get_packed_data_compare_func );
773840}
774841/* }}} */
775842
776843/* {{{ Sort an array in reverse order */
777844PHP_FUNCTION (rsort )
778845{
779846 php_sort (INTERNAL_FUNCTION_PARAM_PASSTHRU , php_get_data_reverse_compare_func , true,
780- php_array_packed_long_reverse_compare );
847+ php_get_packed_data_reverse_compare_func );
781848}
782849/* }}} */
783850
0 commit comments