LCOV - code coverage report
Current view: top level - /ext - colopl_bc_php74.c (source / functions) Coverage Total Hit
Test: Coverage Report Lines: 55.9 % 746 417
Test Date: 2025-09-19 03:13:10 Functions: - 0 0

            Line data    Source code
       1              : /*
       2              :   +----------------------------------------------------------------------+
       3              :   | COLOPL PHP Backwards Compatibility Extension.                        |
       4              :   +----------------------------------------------------------------------+
       5              :   | Copyright (c) COLOPL, Inc.                                           |
       6              :   | Copyright (c) The PHP Group                                          |
       7              :   +----------------------------------------------------------------------+
       8              :   | This source file is subject to version 3.01 of the PHP license,      |
       9              :   | that is bundled with this package in the file LICENSE, and is        |
      10              :   | available through the world-wide-web at the following url:           |
      11              :   | http://www.php.net/license/3_01.txt                                  |
      12              :   | If you did not receive a copy of the PHP license and are unable to   |
      13              :   | obtain it through the world-wide-web, please send a note to          |
      14              :   | license@php.net so we can mail you a copy immediately.               |
      15              :   +----------------------------------------------------------------------+
      16              :   | Author: Go Kudo <g-kudo@colopl.co.jp>                                |
      17              :   +----------------------------------------------------------------------+
      18              : */
      19              : 
      20              : #ifdef HAVE_CONFIG_H
      21              : # include "config.h"
      22              : #endif
      23              : 
      24              : #include "php_colopl_bc.h"
      25              : 
      26              : #include "ext/standard/php_array.h"
      27              : #include "ext/standard/php_string.h"
      28              : #include "zend_operators.h"
      29              : 
      30              : /* zend_operators.c */
      31              : 
      32              : #define COLOPL_BC_TYPE_PAIR(t1,t2) (((t1) << 4) | (t2))
      33              : 
      34              : #define php_colopl_bc_convert_object_to_type(op, dst, ctype)                                                                    \
      35              :         ZVAL_UNDEF(dst);                                                                                                                                                        \
      36              :         if (Z_OBJ_HT_P(op)->cast_object(Z_OBJ_P(op), dst, ctype) == FAILURE) {                                               \
      37              :                 zend_error(E_WARNING,                                                                                                                                   \
      38              :                         "Object of class %s could not be converted to %s", ZSTR_VAL(Z_OBJCE_P(op)->name),  \
      39              :                 zend_get_type_by_const(ctype));                                                                                                                 \
      40              :         }                                                                                                                                                                                       \
      41              : 
      42         7704 : static zend_never_inline zval* ZEND_FASTCALL _php_colopl_bc_zendi_convert_scalar_to_number_silent(zval *op, zval *holder)
      43              : {
      44         7704 :         switch (Z_TYPE_P(op)) {
      45            0 :                 case IS_NULL:
      46              :                 case IS_FALSE:
      47            0 :                         ZVAL_LONG(holder, 0);
      48            0 :                         return holder;
      49            0 :                 case IS_TRUE:
      50            0 :                         ZVAL_LONG(holder, 1);
      51            0 :                         return holder;
      52         3532 :                 case IS_STRING:
      53         3532 :                         if ((Z_TYPE_INFO_P(holder) = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL_P(holder), &Z_DVAL_P(holder), 1)) == 0) {
      54         1096 :                                 ZVAL_LONG(holder, 0);
      55              :                         }
      56         3532 :                         return holder;
      57            0 :                 case IS_RESOURCE:
      58            0 :                         ZVAL_LONG(holder, Z_RES_HANDLE_P(op));
      59            0 :                         return holder;
      60            0 :                 case IS_OBJECT:
      61            0 :                         php_colopl_bc_convert_object_to_type(op, holder, _IS_NUMBER);
      62            0 :                         if (UNEXPECTED(EG(exception)) ||
      63            0 :                                 UNEXPECTED(Z_TYPE_P(holder) != IS_LONG && Z_TYPE_P(holder) != IS_DOUBLE)) {
      64            0 :                                 ZVAL_LONG(holder, 1);
      65              :                         }
      66            0 :                         return holder;
      67         4172 :                 case IS_LONG:
      68              :                 case IS_DOUBLE:
      69              :                 default:
      70         4172 :                         return op;
      71              :         }
      72              : }
      73              : 
      74        15084 : static int legacy_compare_fast(zval *op1, zval *op2)
      75              : {
      76        15084 :         int converted = 0;
      77              :         zval op1_copy, op2_copy;
      78              : 
      79              :         while (1) {
      80        18936 :                 switch (COLOPL_BC_TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2))) {
      81         8880 :                         case COLOPL_BC_TYPE_PAIR(IS_LONG, IS_LONG):
      82         8880 :                                 return Z_LVAL_P(op1)>Z_LVAL_P(op2)?1:(Z_LVAL_P(op1)<Z_LVAL_P(op2)?-1:0);
      83              : 
      84          200 :                         case COLOPL_BC_TYPE_PAIR(IS_DOUBLE, IS_LONG):
      85          200 :                                 return ZEND_NORMALIZE_BOOL(Z_DVAL_P(op1) - (double)Z_LVAL_P(op2));
      86              : 
      87          200 :                         case COLOPL_BC_TYPE_PAIR(IS_LONG, IS_DOUBLE):
      88          200 :                                 return ZEND_NORMALIZE_BOOL((double)Z_LVAL_P(op1) - Z_DVAL_P(op2));
      89              : 
      90           60 :                         case COLOPL_BC_TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):
      91           60 :                                 if (Z_DVAL_P(op1) == Z_DVAL_P(op2)) {
      92           60 :                                         return 0;
      93              :                                 } else {
      94            0 :                                         return ZEND_NORMALIZE_BOOL(Z_DVAL_P(op1) - Z_DVAL_P(op2));
      95              :                                 }
      96              : 
      97           80 :                         case COLOPL_BC_TYPE_PAIR(IS_ARRAY, IS_ARRAY):
      98           80 :                                 return zend_compare_arrays(op1, op2);
      99              : 
     100          100 :                         case COLOPL_BC_TYPE_PAIR(IS_NULL, IS_NULL):
     101              :                         case COLOPL_BC_TYPE_PAIR(IS_NULL, IS_FALSE):
     102              :                         case COLOPL_BC_TYPE_PAIR(IS_FALSE, IS_NULL):
     103              :                         case COLOPL_BC_TYPE_PAIR(IS_FALSE, IS_FALSE):
     104              :                         case COLOPL_BC_TYPE_PAIR(IS_TRUE, IS_TRUE):
     105          100 :                                 return 0;
     106              : 
     107           20 :                         case COLOPL_BC_TYPE_PAIR(IS_NULL, IS_TRUE):
     108           20 :                                 return -1;
     109              : 
     110           20 :                         case COLOPL_BC_TYPE_PAIR(IS_TRUE, IS_NULL):
     111           20 :                                 return 1;
     112              : 
     113         1584 :                         case COLOPL_BC_TYPE_PAIR(IS_STRING, IS_STRING):
     114         1584 :                                 if (Z_STR_P(op1) == Z_STR_P(op2)) {
     115          772 :                                         return 0;
     116              :                                 }
     117          812 :                                 return zendi_smart_strcmp(Z_STR_P(op1), Z_STR_P(op2));
     118              : 
     119          100 :                         case COLOPL_BC_TYPE_PAIR(IS_NULL, IS_STRING):
     120          100 :                                 return Z_STRLEN_P(op2) == 0 ? 0 : -1;
     121              : 
     122          100 :                         case COLOPL_BC_TYPE_PAIR(IS_STRING, IS_NULL):
     123          100 :                                 return Z_STRLEN_P(op1) == 0 ? 0 : 1;
     124              : 
     125           60 :                         case COLOPL_BC_TYPE_PAIR(IS_OBJECT, IS_NULL):
     126           60 :                                 return 1;
     127              : 
     128           60 :                         case COLOPL_BC_TYPE_PAIR(IS_NULL, IS_OBJECT):
     129           60 :                                 return -1;
     130              : 
     131         7472 :                         default:
     132         7472 :                                 if (Z_ISREF_P(op1)) {
     133            0 :                                         op1 = Z_REFVAL_P(op1);
     134            0 :                                         continue;
     135         7472 :                                 } else if (Z_ISREF_P(op2)) {
     136            0 :                                         op2 = Z_REFVAL_P(op2);
     137            0 :                                         continue;
     138              :                                 }
     139              : 
     140         7472 :                                 if (Z_TYPE_P(op1) == IS_OBJECT
     141          960 :                                  && Z_TYPE_P(op2) == IS_OBJECT
     142          180 :                                  && Z_OBJ_P(op1) == Z_OBJ_P(op2)) {
     143           60 :                                         return 0;
     144         7412 :                                 } else if (Z_TYPE_P(op1) == IS_OBJECT) {
     145          900 :                                         return Z_OBJ_HANDLER_P(op1, compare)(op1, op2);
     146         6512 :                                 } else if (Z_TYPE_P(op2) == IS_OBJECT) {
     147          780 :                                         return Z_OBJ_HANDLER_P(op2, compare)(op1, op2);
     148              :                                 }
     149              : 
     150         5732 :                                 if (!converted) {
     151         5012 :                                         if (Z_TYPE_P(op1) < IS_TRUE) {
     152          360 :                                                 return zval_is_true(op2) ? -1 : 0;
     153         4652 :                                         } else if (Z_TYPE_P(op1) == IS_TRUE) {
     154          240 :                                                 return zval_is_true(op2) ? 0 : 1;
     155         4412 :                                         } else if (Z_TYPE_P(op2) < IS_TRUE) {
     156          340 :                                                 return zval_is_true(op1) ? 1 : 0;
     157         4072 :                                         } else if (Z_TYPE_P(op2) == IS_TRUE) {
     158          220 :                                                 return zval_is_true(op1) ? 0 : -1;
     159              :                                         } else {
     160         3852 :                                                 op1 = _php_colopl_bc_zendi_convert_scalar_to_number_silent(op1, &op1_copy);
     161         3852 :                                                 op2 = _php_colopl_bc_zendi_convert_scalar_to_number_silent(op2, &op2_copy);
     162         3852 :                                                 if (EG(exception)) {
     163            0 :                                                         return 1; /* to stop comparison of arrays */
     164              :                                                 }
     165         3852 :                                                 converted = 1;
     166              :                                         }
     167          720 :                                 } else if (Z_TYPE_P(op1)==IS_ARRAY) {
     168          360 :                                         return 1;
     169          360 :                                 } else if (Z_TYPE_P(op2)==IS_ARRAY) {
     170          360 :                                         return -1;
     171              :                                 } else {
     172            0 :                                         ZEND_UNREACHABLE();
     173              :                                         zend_throw_error(NULL, "Unsupported operand types");
     174              :                                         return 1;
     175              :                                 }
     176              :                 }
     177              :         }
     178              : }
     179              : 
     180        11880 : static int legacy_compare_slow(zval *op1, zval *op2)
     181              : {
     182        11880 :         int bc = legacy_compare_fast(op1, op2);
     183        11880 :         int native = zend_compare(op1, op2);
     184              :         
     185        11880 :         if (native != bc) {
     186          812 :                 if (COLOPL_BC_G(php74_compare_mode) & COLOPL_BC_PHP74_COMPARE_MODE_LOG) {
     187          808 :                         php_log_err_with_severity("Incompatible compare detected", LOG_NOTICE);
     188              :                 }
     189          812 :                 if (COLOPL_BC_G(php74_compare_mode) & COLOPL_BC_PHP74_COMPARE_MODE_DEPRECATED) {
     190          808 :                         php_error_docref(NULL, E_DEPRECATED, "Incompatible compare detected");
     191              :                 }
     192              :         }
     193              : 
     194        11880 :         return bc;
     195              : }
     196              : 
     197          224 : PHP_INI_MH(OnUpdateCompareMode)
     198              : {
     199          224 :         if (OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage) == FAILURE) {
     200            0 :                 return FAILURE;
     201              :         }
     202              : 
     203          224 :         if (COLOPL_BC_G(php74_compare_mode) <= COLOPL_BC_PHP74_COMPARE_MODE_SILENT) {
     204          136 :                 COLOPL_BC_G(php74_compare_func) = legacy_compare_fast;
     205              :         } else {
     206           88 :                 COLOPL_BC_G(php74_compare_func) = legacy_compare_slow;
     207              :         }
     208              : 
     209          224 :         return SUCCESS;
     210              : }
     211              : 
     212        15084 : int php_colopl_bc_compare(zval *op1, zval *op2)
     213              : {
     214        15084 :         return COLOPL_BC_G(php74_compare_func)(op1, op2);
     215              : }
     216              : 
     217            0 : static zend_always_inline bool php_colopl_bc_fast_equal_check_long(zval *op1, zval *op2)
     218              : {
     219            0 :         if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
     220            0 :                 return Z_LVAL_P(op1) == Z_LVAL_P(op2);
     221              :         }
     222            0 :         return php_colopl_bc_compare(op1, op2) == 0;
     223              : }
     224              : 
     225            8 : static zend_always_inline bool php_colopl_bc_fast_equal_check_string(zval *op1, zval *op2)
     226              : {
     227            8 :         if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
     228            0 :                 return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2));
     229              :         }
     230            8 :         return php_colopl_bc_compare(op1, op2) == 0;
     231              : }
     232              : 
     233            8 : static zend_always_inline bool php_colopl_bc_fast_equal_check_function(zval *op1, zval *op2)
     234              : {
     235            8 :         if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
     236            0 :                 if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
     237            0 :                         return Z_LVAL_P(op1) == Z_LVAL_P(op2);
     238            0 :                 } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
     239            0 :                         return ((double)Z_LVAL_P(op1)) == Z_DVAL_P(op2);
     240              :                 }
     241            8 :         } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) {
     242            0 :                 if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
     243            0 :                         return Z_DVAL_P(op1) == Z_DVAL_P(op2);
     244            0 :                 } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
     245            0 :                         return Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2));
     246              :                 }
     247            8 :         } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
     248            8 :                 if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
     249            4 :                         return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2));
     250              :                 }
     251              :         }
     252            4 :         return php_colopl_bc_compare(op1, op2) == 0;
     253              : }
     254              : 
     255              : /* array.c */
     256              : 
     257         2792 : static int php_colopl_bc_array_key_compare(Bucket *f, Bucket *s)
     258              : {
     259              :         zend_uchar t;
     260              :         zend_long l1, l2;
     261              :         double d;
     262              : 
     263         2792 :         if (f->key == NULL) {
     264           40 :                 if (s->key == NULL) {
     265            0 :                         return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
     266              :                 } else {
     267           40 :                         l1 = (zend_long)f->h;
     268           40 :                         t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1);
     269           40 :                         if (t == IS_LONG) {
     270              :                                 /* pass */
     271           40 :                         } else if (t == IS_DOUBLE) {
     272            0 :                                 return ZEND_NORMALIZE_BOOL((double)l1 - d);
     273              :                         } else {
     274           40 :                                 l2 = 0;
     275              :                         }
     276              :                 }
     277              :         } else {
     278         2752 :                 if (s->key) {
     279         2704 :                         return zendi_smart_strcmp(f->key, s->key);
     280              :                 } else {
     281           48 :                         l2 = (zend_long)s->h;
     282           48 :                         t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1);
     283           48 :                         if (t == IS_LONG) {
     284              :                                 /* pass */
     285           48 :                         } else if (t == IS_DOUBLE) {
     286            0 :                                 return ZEND_NORMALIZE_BOOL(d - (double)l2);
     287              :                         } else {
     288           48 :                                 l1 = 0;
     289              :                         }
     290              :                 }
     291              :         }
     292           88 :         return ZEND_NORMALIZE_BOOL(l1 - l2);
     293              : }
     294              : 
     295         1412 : static int php_colopl_bc_array_reverse_key_compare(Bucket *a, Bucket *b)
     296              : {
     297         1412 :         return php_colopl_bc_array_key_compare(b, a);
     298              : }
     299              : 
     300            0 : static int php_colopl_bc_array_key_compare_numeric(Bucket *f, Bucket *s)
     301              : {
     302            0 :         if (f->key == NULL && s->key == NULL) {
     303            0 :                 return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
     304              :         } else {
     305              :                 double d1, d2;
     306            0 :                 if (f->key) {
     307            0 :                         d1 = zend_strtod(f->key->val, NULL);
     308              :                 } else {
     309            0 :                         d1 = (double)(zend_long)f->h;
     310              :                 }
     311            0 :                 if (s->key) {
     312            0 :                         d2 = zend_strtod(s->key->val, NULL);
     313              :                 } else {
     314            0 :                         d2 = (double)(zend_long)s->h;
     315              :                 }
     316            0 :                 return ZEND_NORMALIZE_BOOL(d1 - d2);
     317              :         }
     318              : }
     319              : 
     320            0 : static int php_colopl_bc_array_reverse_key_compare_numeric(Bucket *a, Bucket *b)
     321              : {
     322            0 :         return php_colopl_bc_array_key_compare_numeric(b, a);
     323              : }
     324              : 
     325            0 : static int php_colopl_bc_array_key_compare_string_case(Bucket *f, Bucket *s)
     326              : {
     327              :         const char *s1, *s2;
     328              :         size_t l1, l2;
     329              :         char buf1[MAX_LENGTH_OF_LONG + 1];
     330              :         char buf2[MAX_LENGTH_OF_LONG + 1];
     331              : 
     332            0 :         if (f->key) {
     333            0 :                 s1 = f->key->val;
     334            0 :                 l1 = f->key->len;
     335              :         } else {
     336            0 :                 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
     337            0 :                 l1 = buf1 + sizeof(buf1) - 1 - s1;
     338              :         }
     339            0 :         if (s->key) {
     340            0 :                 s2 = s->key->val;
     341            0 :                 l2 = s->key->len;
     342              :         } else {
     343            0 :                 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
     344            0 :                 l2 = buf2 + sizeof(buf2) - 1 - s1;
     345              :         }
     346            0 :         return zend_binary_strcasecmp_l(s1, l1, s2, l2);
     347              : }
     348              : 
     349            0 : static int php_colopl_bc_array_reverse_key_compare_string_case(Bucket *a, Bucket *b)
     350              : {
     351            0 :         return php_colopl_bc_array_key_compare_string_case(b, a);
     352              : }
     353              : 
     354            0 : static int php_colopl_bc_array_key_compare_string(Bucket *f, Bucket *s)
     355              : {
     356              :         const char *s1, *s2;
     357              :         size_t l1, l2;
     358              :         char buf1[MAX_LENGTH_OF_LONG + 1];
     359              :         char buf2[MAX_LENGTH_OF_LONG + 1];
     360              : 
     361            0 :         if (f->key) {
     362            0 :                 s1 = f->key->val;
     363            0 :                 l1 = f->key->len;
     364              :         } else {
     365            0 :                 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
     366            0 :                 l1 = buf1 + sizeof(buf1) - 1 - s1;
     367              :         }
     368            0 :         if (s->key) {
     369            0 :                 s2 = s->key->val;
     370            0 :                 l2 = s->key->len;
     371              :         } else {
     372            0 :                 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
     373            0 :                 l2 = buf2 + sizeof(buf2) - 1 - s2;
     374              :         }
     375            0 :         return zend_binary_strcmp(s1, l1, s2, l2);
     376              : }
     377              : 
     378            0 : static int php_colopl_bc_array_reverse_key_compare_string(Bucket *a, Bucket *b)
     379              : {
     380            0 :         return php_colopl_bc_array_key_compare_string(b, a);
     381              : }
     382              : 
     383            0 : static int php_colopl_bc_array_key_compare_string_natural_general(Bucket *f, Bucket *s, int fold_case)
     384              : {
     385              :         const char *s1, *s2;
     386              :         size_t l1, l2;
     387              :         char buf1[MAX_LENGTH_OF_LONG + 1];
     388              :         char buf2[MAX_LENGTH_OF_LONG + 1];
     389              : 
     390            0 :         if (f->key) {
     391            0 :                 s1 = f->key->val;
     392            0 :                 l1 = f->key->len;
     393              :         } else {
     394            0 :                 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
     395            0 :                 l1 = buf1 + sizeof(buf1) - 1 - s1;
     396              :         }
     397            0 :         if (s->key) {
     398            0 :                 s2 = s->key->val;
     399            0 :                 l2 = s->key->len;
     400              :         } else {
     401            0 :                 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
     402            0 :                 l2 = buf2 + sizeof(buf2) - 1 - s1;
     403              :         }
     404            0 :         return strnatcmp_ex(s1, l1, s2, l2, fold_case);
     405              : }
     406              : 
     407            0 : static int php_colopl_bc_array_key_compare_string_natural_case(Bucket *a, Bucket *b)
     408              : {
     409            0 :         return php_colopl_bc_array_key_compare_string_natural_general(a, b, 1);
     410              : }
     411              : 
     412            0 : static int php_colopl_bc_array_reverse_key_compare_string_natural_case(Bucket *a, Bucket *b)
     413              : {
     414            0 :         return php_colopl_bc_array_key_compare_string_natural_general(b, a, 1);
     415              : }
     416              : 
     417            0 : static int php_colopl_bc_array_key_compare_string_natural(Bucket *a, Bucket *b)
     418              : {
     419            0 :         return php_colopl_bc_array_key_compare_string_natural_general(a, b, 0);
     420              : }
     421              : 
     422            0 : static int php_colopl_bc_array_reverse_key_compare_string_natural(Bucket *a, Bucket *b)
     423              : {
     424            0 :         return php_colopl_bc_array_key_compare_string_natural_general(b, a, 0);
     425              : }
     426              : 
     427            0 : static int php_colopl_bc_array_key_compare_string_locale(Bucket *f, Bucket *s)
     428              : {
     429              :         const char *s1, *s2;
     430              :         char buf1[MAX_LENGTH_OF_LONG + 1];
     431              :         char buf2[MAX_LENGTH_OF_LONG + 1];
     432              : 
     433            0 :         if (f->key) {
     434            0 :                 s1 = f->key->val;
     435              :         } else {
     436            0 :                 s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
     437              :         }
     438            0 :         if (s->key) {
     439            0 :                 s2 = s->key->val;
     440              :         } else {
     441            0 :                 s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
     442              :         }
     443            0 :         return strcoll(s1, s2);
     444              : }
     445              : 
     446            0 : static int php_colopl_bc_array_reverse_key_compare_string_locale(Bucket *a, Bucket *b)
     447              : {
     448            0 :         return php_colopl_bc_array_key_compare_string_locale(b, a);
     449              : }
     450              : 
     451         9276 : static int php_colopl_bc_array_data_compare(Bucket *f, Bucket *s)
     452              : {
     453         9276 :         zval *first = &f->val;
     454         9276 :         zval *second = &s->val;
     455              : 
     456         9276 :         if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
     457            0 :                 first = Z_INDIRECT_P(first);
     458              :         }
     459         9276 :         if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
     460            0 :                 second = Z_INDIRECT_P(second);
     461              :         }
     462              : 
     463         9276 :         return php_colopl_bc_compare(first, second);
     464              : }
     465              : 
     466         3120 : static int php_colopl_bc_array_reverse_data_compare(Bucket *a, Bucket *b)
     467              : {
     468         3120 :         return php_colopl_bc_array_data_compare(a, b) * -1;
     469              : }
     470              : 
     471            0 : static int php_colopl_bc_array_data_compare_numeric(Bucket *f, Bucket *s)
     472              : {
     473            0 :         zval *first = &f->val;
     474            0 :         zval *second = &s->val;
     475              : 
     476            0 :         if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
     477            0 :                 first = Z_INDIRECT_P(first);
     478              :         }
     479            0 :         if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
     480            0 :                 second = Z_INDIRECT_P(second);
     481              :         }
     482              : 
     483            0 :         return numeric_compare_function(first, second);
     484              : }
     485              : 
     486            0 : static int php_colopl_bc_array_reverse_data_compare_numeric(Bucket *a, Bucket *b)
     487              : {
     488            0 :         return php_colopl_bc_array_data_compare_numeric(b, a);
     489              : }
     490              : 
     491            0 : static int php_colopl_bc_array_data_compare_string_case(Bucket *f, Bucket *s)
     492              : {
     493            0 :         zval *first = &f->val;
     494            0 :         zval *second = &s->val;
     495              : 
     496            0 :         if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
     497            0 :                 first = Z_INDIRECT_P(first);
     498              :         }
     499            0 :         if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
     500            0 :                 second = Z_INDIRECT_P(second);
     501              :         }
     502              : 
     503            0 :         return string_case_compare_function(first, second);
     504              : }
     505              : 
     506            0 : static int php_colopl_bc_array_reverse_data_compare_string_case(Bucket *a, Bucket *b)
     507              : {
     508            0 :         return php_colopl_bc_array_data_compare_string_case(b, a);
     509              : }
     510              : 
     511            0 : static int php_colopl_bc_array_data_compare_string(Bucket *f, Bucket *s)
     512              : {
     513            0 :         zval *first = &f->val;
     514            0 :         zval *second = &s->val;
     515              : 
     516            0 :         if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
     517            0 :                 first = Z_INDIRECT_P(first);
     518              :         }
     519            0 :         if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
     520            0 :                 second = Z_INDIRECT_P(second);
     521              :         }
     522              : 
     523            0 :         return string_compare_function(first, second);
     524              : }
     525              : 
     526            0 : static int php_colopl_bc_array_reverse_data_compare_string(Bucket *a, Bucket *b)
     527              : {
     528            0 :         return php_colopl_bc_array_data_compare_string(b, a);
     529              : }
     530              : 
     531            0 : static int php_colopl_bc_array_natural_general_compare(Bucket *f, Bucket *s, int fold_case)
     532              : {
     533              :         zend_string *tmp_str1, *tmp_str2;
     534            0 :         zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1);
     535            0 :         zend_string *str2 = zval_get_tmp_string(&s->val, &tmp_str2);
     536              : 
     537            0 :         int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
     538              : 
     539            0 :         zend_tmp_string_release(tmp_str1);
     540            0 :         zend_tmp_string_release(tmp_str2);
     541            0 :         return result;
     542              : }
     543              : 
     544            0 : static int php_colopl_bc_array_natural_compare(Bucket *a, Bucket *b)
     545              : {
     546            0 :         return php_colopl_bc_array_natural_general_compare(a, b, 0);
     547              : }
     548              : 
     549            0 : static int php_colopl_bc_array_reverse_natural_compare(Bucket *a, Bucket *b)
     550              : {
     551            0 :         return php_colopl_bc_array_natural_general_compare(b, a, 0);
     552              : }
     553              : 
     554            0 : static int php_colopl_bc_array_natural_case_compare(Bucket *a, Bucket *b)
     555              : {
     556            0 :         return php_colopl_bc_array_natural_general_compare(a, b, 1);
     557              : }
     558              : 
     559            0 : static int php_colopl_bc_array_reverse_natural_case_compare(Bucket *a, Bucket *b)
     560              : {
     561            0 :         return php_colopl_bc_array_natural_general_compare(b, a, 1);
     562              : }
     563              : 
     564            0 : static int php_colopl_bc_array_data_compare_string_locale(Bucket *f, Bucket *s)
     565              : {
     566            0 :         zval *first = &f->val;
     567            0 :         zval *second = &s->val;
     568              : 
     569            0 :         if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
     570            0 :                 first = Z_INDIRECT_P(first);
     571              :         }
     572            0 :         if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
     573            0 :                 second = Z_INDIRECT_P(second);
     574              :         }
     575              : 
     576            0 :         return string_locale_compare_function(first, second);
     577              : }
     578              : 
     579            0 : static int php_colopl_bc_array_reverse_data_compare_string_locale(Bucket *a, Bucket *b)
     580              : {
     581            0 :         return php_colopl_bc_array_data_compare_string_locale(b, a);
     582              : }
     583              : 
     584         3048 : static int php_colopl_bc_array_user_compare(Bucket *f, Bucket *s)
     585              : {
     586              :         zval args[2];
     587              :         zval retval;
     588              : 
     589         3048 :         ZVAL_COPY(&args[0], &f->val);
     590         3048 :         ZVAL_COPY(&args[1], &s->val);
     591              : 
     592         3048 :         COLOPL_BC_G(user_compare_fci).param_count = 2;
     593         3048 :         COLOPL_BC_G(user_compare_fci).params = args;
     594         3048 :         COLOPL_BC_G(user_compare_fci).retval = &retval;
     595         3048 :         if (zend_call_function(&COLOPL_BC_G(user_compare_fci), &COLOPL_BC_G(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
     596         3048 :                 zend_long ret = zval_get_long(&retval);
     597         3048 :                 zval_ptr_dtor(&retval);
     598         3048 :                 zval_ptr_dtor(&args[1]);
     599         3048 :                 zval_ptr_dtor(&args[0]);
     600         3048 :                 return ZEND_NORMALIZE_BOOL(ret);
     601              :         } else {
     602            0 :                 zval_ptr_dtor(&args[1]);
     603            0 :                 zval_ptr_dtor(&args[0]);
     604            0 :                 return 0;
     605              :         }
     606              : }
     607              : 
     608         1524 : static int php_colopl_bc_array_user_key_compare(Bucket *f, Bucket *s)
     609              : {
     610              :         zval args[2];
     611              :         zval retval;
     612              :         zend_long result;
     613              : 
     614         1524 :         if (f->key == NULL) {
     615          740 :                 ZVAL_LONG(&args[0], f->h);
     616              :         } else {
     617          784 :                 ZVAL_STR_COPY(&args[0], f->key);
     618              :         }
     619         1524 :         if (s->key == NULL) {
     620          740 :                 ZVAL_LONG(&args[1], s->h);
     621              :         } else {
     622          784 :                 ZVAL_STR_COPY(&args[1], s->key);
     623              :         }
     624              : 
     625         1524 :         COLOPL_BC_G(user_compare_fci).param_count = 2;
     626         1524 :         COLOPL_BC_G(user_compare_fci).params = args;
     627         1524 :         COLOPL_BC_G(user_compare_fci).retval = &retval;;
     628         1524 :         if (zend_call_function(&COLOPL_BC_G(user_compare_fci), &COLOPL_BC_G(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
     629         1524 :                 result = zval_get_long(&retval);
     630         1524 :                 zval_ptr_dtor(&retval);
     631              :         } else {
     632            0 :                 result = 0;
     633              :         }
     634              : 
     635         1524 :         zval_ptr_dtor(&args[0]);
     636         1524 :         zval_ptr_dtor(&args[1]);
     637              : 
     638         1524 :         return ZEND_NORMALIZE_BOOL(result);
     639              : }
     640              : 
     641           44 : static bucket_compare_func_t php_colopl_bc_get_key_compare_func(zend_long sort_type, int reverse)
     642              : {
     643           44 :         switch (sort_type & ~PHP_SORT_FLAG_CASE) {
     644            0 :                 case PHP_SORT_NUMERIC:
     645            0 :                         if (reverse) {
     646            0 :                                 return php_colopl_bc_array_reverse_key_compare_numeric;
     647              :                         } else {
     648            0 :                                 return php_colopl_bc_array_key_compare_numeric;
     649              :                         }
     650              :                         break;
     651              : 
     652            0 :                 case PHP_SORT_STRING:
     653            0 :                         if (sort_type & PHP_SORT_FLAG_CASE) {
     654            0 :                                 if (reverse) {
     655            0 :                                         return php_colopl_bc_array_reverse_key_compare_string_case;
     656              :                                 } else {
     657            0 :                                         return php_colopl_bc_array_key_compare_string_case;
     658              :                                 }
     659              :                         } else {
     660            0 :                                 if (reverse) {
     661            0 :                                         return php_colopl_bc_array_reverse_key_compare_string;
     662              :                                 } else {
     663            0 :                                         return php_colopl_bc_array_key_compare_string;
     664              :                                 }
     665              :                         }
     666              :                         break;
     667              : 
     668            0 :                 case PHP_SORT_NATURAL:
     669            0 :                         if (sort_type & PHP_SORT_FLAG_CASE) {
     670            0 :                                 if (reverse) {
     671            0 :                                         return php_colopl_bc_array_reverse_key_compare_string_natural_case;
     672              :                                 } else {
     673            0 :                                         return php_colopl_bc_array_key_compare_string_natural_case;
     674              :                                 }
     675              :                         } else {
     676            0 :                                 if (reverse) {
     677            0 :                                         return php_colopl_bc_array_reverse_key_compare_string_natural;
     678              :                                 } else {
     679            0 :                                         return php_colopl_bc_array_key_compare_string_natural;
     680              :                                 }
     681              :                         }
     682              :                         break;
     683              : 
     684            0 :                 case PHP_SORT_LOCALE_STRING:
     685            0 :                         if (reverse) {
     686            0 :                                 return php_colopl_bc_array_reverse_key_compare_string_locale;
     687              :                         } else {
     688            0 :                                 return php_colopl_bc_array_key_compare_string_locale;
     689              :                         }
     690              :                         break;
     691              : 
     692           44 :                 case PHP_SORT_REGULAR:
     693              :                 default:
     694           44 :                         if (reverse) {
     695           24 :                                 return php_colopl_bc_array_reverse_key_compare;
     696              :                         } else {
     697           20 :                                 return php_colopl_bc_array_key_compare;
     698              :                         }
     699              :                         break;
     700              :         }
     701              :         return NULL;
     702              : }
     703              : 
     704           88 : static bucket_compare_func_t php_colopl_bc_get_data_compare_func(zend_long sort_type, int reverse)
     705              : {
     706           88 :         switch (sort_type & ~PHP_SORT_FLAG_CASE) {
     707            0 :                 case PHP_SORT_NUMERIC:
     708            0 :                         if (reverse) {
     709            0 :                                 return php_colopl_bc_array_reverse_data_compare_numeric;
     710              :                         } else {
     711            0 :                                 return php_colopl_bc_array_data_compare_numeric;
     712              :                         }
     713              :                         break;
     714              : 
     715            0 :                 case PHP_SORT_STRING:
     716            0 :                         if (sort_type & PHP_SORT_FLAG_CASE) {
     717            0 :                                 if (reverse) {
     718            0 :                                         return php_colopl_bc_array_reverse_data_compare_string_case;
     719              :                                 } else {
     720            0 :                                         return php_colopl_bc_array_data_compare_string_case;
     721              :                                 }
     722              :                         } else {
     723            0 :                                 if (reverse) {
     724            0 :                                         return php_colopl_bc_array_reverse_data_compare_string;
     725              :                                 } else {
     726            0 :                                         return php_colopl_bc_array_data_compare_string;
     727              :                                 }
     728              :                         }
     729              :                         break;
     730              : 
     731            0 :                 case PHP_SORT_NATURAL:
     732            0 :                         if (sort_type & PHP_SORT_FLAG_CASE) {
     733            0 :                                 if (reverse) {
     734            0 :                                         return php_colopl_bc_array_reverse_natural_case_compare;
     735              :                                 } else {
     736            0 :                                         return php_colopl_bc_array_natural_case_compare;
     737              :                                 }
     738              :                         } else {
     739            0 :                                 if (reverse) {
     740            0 :                                         return php_colopl_bc_array_reverse_natural_compare;
     741              :                                 } else {
     742            0 :                                         return php_colopl_bc_array_natural_compare;
     743              :                                 }
     744              :                         }
     745              :                         break;
     746              : 
     747            0 :                 case PHP_SORT_LOCALE_STRING:
     748            0 :                         if (reverse) {
     749            0 :                                 return php_colopl_bc_array_reverse_data_compare_string_locale;
     750              :                         } else {
     751            0 :                                 return php_colopl_bc_array_data_compare_string_locale;
     752              :                         }
     753              :                         break;
     754              : 
     755           88 :                 case PHP_SORT_REGULAR:
     756              :                 default:
     757           88 :                         if (reverse) {
     758           40 :                                 return php_colopl_bc_array_reverse_data_compare;
     759              :                         } else {
     760           48 :                                 return php_colopl_bc_array_data_compare;
     761              :                         }
     762              :                         break;
     763              :         }
     764              :         return NULL;
     765              : }
     766              : 
     767              : #define COLOPL_BC_MULTISORT_ORDER       0
     768              : #define COLOPL_BC_MULTISORT_TYPE        1
     769              : #define COLOPL_BC_MULTISORT_LAST        2
     770              : 
     771         1496 : int php_colopl_bc_multisort_compare(const void *a, const void *b)
     772              : {
     773         1496 :         Bucket *ab = *(Bucket **)a;
     774         1496 :         Bucket *bb = *(Bucket **)b;
     775              :         int r;
     776              :         zend_long result;
     777              : 
     778         1496 :         r = 0;
     779              :         do {
     780         2992 :                 result = COLOPL_BC_G(multisort_func)[r](&ab[r], &bb[r]);
     781         2992 :                 if (result != 0) {
     782            0 :                         return result > 0 ? 1 : -1;
     783              :                 }
     784         2992 :                 r++;
     785         2992 :         } while (Z_TYPE(ab[r].val) != IS_UNDEF);
     786              : 
     787         1496 :         return 0;
     788              : }
     789              : 
     790              : #define COLOPL_BC_MULTISORT_ABORT       \
     791              :         efree(func);    \
     792              :         efree(arrays);  \
     793              :         return;
     794              : 
     795          596 : static void colopl_bc_array_bucket_p_swap(void *p, void *q)
     796              : {
     797              :         Bucket *t;
     798          596 :         Bucket **f = (Bucket **)p;
     799          596 :         Bucket **g = (Bucket **)q;
     800              : 
     801          596 :         t = *f;
     802          596 :         *f = *g;
     803          596 :         *g = t;
     804          596 : }
     805              : 
     806            8 : static inline void php_colopl_bc_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
     807              : {
     808              :         zval *value,                            /* value to check for */
     809              :                  *array,                                /* array to check in */
     810              :                  *entry;                                /* pointer to array entry */
     811              :         zend_ulong num_idx;
     812              :         zend_string *str_idx;
     813            8 :         bool strict = 0;                        /* strict comparison or not */
     814              : 
     815            8 :         ZEND_PARSE_PARAMETERS_START(2, 3)
     816            8 :                 Z_PARAM_ZVAL(value)
     817            8 :                 Z_PARAM_ARRAY(array)
     818            8 :                 Z_PARAM_OPTIONAL
     819            8 :                 Z_PARAM_BOOL(strict)
     820            8 :         ZEND_PARSE_PARAMETERS_END();
     821              : 
     822            8 :         if (strict) {
     823            0 :                 if (Z_TYPE_P(value) == IS_LONG) {
     824            0 :                         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
     825            0 :                                 ZVAL_DEREF(entry);
     826            0 :                                 if (Z_TYPE_P(entry) == IS_LONG && Z_LVAL_P(entry) == Z_LVAL_P(value)) {
     827            0 :                                         if (behavior == 0) {
     828            0 :                                                 RETURN_TRUE;
     829              :                                         } else {
     830            0 :                                                 if (str_idx) {
     831            0 :                                                         RETURN_STR_COPY(str_idx);
     832              :                                                 } else {
     833            0 :                                                         RETURN_LONG(num_idx);
     834              :                                                 }
     835              :                                         }
     836              :                                 }
     837              :                         } ZEND_HASH_FOREACH_END();
     838              :                 } else {
     839            0 :                         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
     840            0 :                                 ZVAL_DEREF(entry);
     841            0 :                                 if (fast_is_identical_function(value, entry)) {
     842            0 :                                         if (behavior == 0) {
     843            0 :                                                 RETURN_TRUE;
     844              :                                         } else {
     845            0 :                                                 if (str_idx) {
     846            0 :                                                         RETURN_STR_COPY(str_idx);
     847              :                                                 } else {
     848            0 :                                                         RETURN_LONG(num_idx);
     849              :                                                 }
     850              :                                         }
     851              :                                 }
     852              :                         } ZEND_HASH_FOREACH_END();
     853              :                 }
     854              :         } else {
     855            8 :                 if (Z_TYPE_P(value) == IS_LONG) {
     856            0 :                         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
     857            0 :                                 if (php_colopl_bc_fast_equal_check_long(value, entry)) {
     858            0 :                                         if (behavior == 0) {
     859            0 :                                                 RETURN_TRUE;
     860              :                                         } else {
     861            0 :                                                 if (str_idx) {
     862            0 :                                                         RETURN_STR_COPY(str_idx);
     863              :                                                 } else {
     864            0 :                                                         RETURN_LONG(num_idx);
     865              :                                                 }
     866              :                                         }
     867              :                                 }
     868              :                         } ZEND_HASH_FOREACH_END();
     869            8 :                 } else if (Z_TYPE_P(value) == IS_STRING) {
     870            8 :                         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
     871            8 :                                 if (php_colopl_bc_fast_equal_check_string(value, entry)) {
     872            8 :                                         if (behavior == 0) {
     873            4 :                                                 RETURN_TRUE;
     874              :                                         } else {
     875            4 :                                                 if (str_idx) {
     876            4 :                                                         RETURN_STR_COPY(str_idx);
     877              :                                                 } else {
     878            0 :                                                         RETURN_LONG(num_idx);
     879              :                                                 }
     880              :                                         }
     881              :                                 }
     882              :                         } ZEND_HASH_FOREACH_END();
     883              :                 } else {
     884            0 :                         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
     885            0 :                                 if (php_colopl_bc_fast_equal_check_function(value, entry)) {
     886            0 :                                         if (behavior == 0) {
     887            0 :                                                 RETURN_TRUE;
     888              :                                         } else {
     889            0 :                                                 if (str_idx) {
     890            0 :                                                         RETURN_STR_COPY(str_idx);
     891              :                                                 } else {
     892            0 :                                                         RETURN_LONG(num_idx);
     893              :                                                 }
     894              :                                         }
     895              :                                 }
     896              :                         } ZEND_HASH_FOREACH_END();
     897              :                 }
     898              :         }
     899              : 
     900            0 :         RETURN_FALSE;
     901              : }
     902              : 
     903              : #define PHP_COLOPL_BC_ARRAY_CMP_FUNC_VARS \
     904              :         zend_fcall_info old_user_compare_fci; \
     905              :         zend_fcall_info_cache old_user_compare_fci_cache \
     906              : 
     907              : #define PHP_COLOPL_BC_ARRAY_CMP_FUNC_BACKUP() \
     908              :         old_user_compare_fci = COLOPL_BC_G(user_compare_fci); \
     909              :         old_user_compare_fci_cache = COLOPL_BC_G(user_compare_fci_cache); \
     910              :         COLOPL_BC_G(user_compare_fci_cache) = empty_fcall_info_cache; \
     911              : 
     912              : #define PHP_COLOPL_BC_ARRAY_CMP_FUNC_RESTORE() \
     913              :         zend_release_fcall_info_cache(&COLOPL_BC_G(user_compare_fci_cache)); \
     914              :         COLOPL_BC_G(user_compare_fci) = old_user_compare_fci; \
     915              :         COLOPL_BC_G(user_compare_fci_cache) = old_user_compare_fci_cache; \
     916              : 
     917          184 : static void legacy_hash_sort_fast(INTERNAL_FUNCTION_PARAMETERS, zval *array, bucket_compare_func_t comapre_func, bool renumber)
     918              : {
     919          184 :         zend_hash_sort(Z_ARRVAL_P(array), comapre_func, renumber);
     920          184 : }
     921              : 
     922          148 : static void legacy_hash_sort_slow(INTERNAL_FUNCTION_PARAMETERS, zval *array, bucket_compare_func_t compare_func, bool renumber)
     923              : {
     924              :         zval legacy;
     925              :         zend_internal_function *fn;
     926              :         char *fnname_ptr;
     927              : 
     928          148 :         ZVAL_DUP(&legacy, array);
     929              : 
     930          148 :         legacy_hash_sort_fast(INTERNAL_FUNCTION_PARAM_PASSTHRU, &legacy, compare_func, renumber);
     931              : 
     932              :         /* Get native PHP bundled (internal) function ptr */
     933          148 :         fnname_ptr = execute_data->func->internal_function.function_name->val;
     934              :         /* 
     935              :          * Extracting last piece of namespaces, they're always identical a native function name
     936              :          * 
     937              :          * e.g.)
     938              :          * Colopl\ColoplBc\Php74\*****
     939              :          *                                     ^^^^^
     940              :          */
     941         3404 :         while (strchr(fnname_ptr, '\\')) {
     942         3256 :                 ++fnname_ptr;
     943              :         }
     944          148 :         fn = zend_hash_str_find_ptr(EG(function_table), fnname_ptr, strlen(fnname_ptr));
     945          148 :         if (EXPECTED(fn != NULL)) {
     946              :                 /* Passthrough PHP bundled (internal) function */
     947          148 :                 fn->handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
     948              : 
     949          148 :                 if (!zend_is_identical(&legacy, array)) {
     950          120 :                         if (COLOPL_BC_G(php74_sort_mode) & COLOPL_BC_PHP74_SORT_MODE_LOG) {
     951           92 :                                 php_log_err_with_severity("Incompatible sort detected", LOG_NOTICE);
     952              :                         }
     953          120 :                         if (COLOPL_BC_G(php74_sort_mode) & COLOPL_BC_PHP74_SORT_MODE_DEPRECATED) {
     954           92 :                                 php_error_docref(NULL, E_DEPRECATED, "Incompatible sort detected");
     955              :                         }
     956              :                 }
     957              :         }
     958              : 
     959          148 :         zval_ptr_dtor(array);
     960          148 :         ZVAL_ARR(array, Z_ARRVAL(legacy));
     961          148 : }
     962              : 
     963          224 : PHP_INI_MH(OnUpdateSortMode)
     964              : {
     965          224 :         if (OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage) == FAILURE) {
     966            0 :                 return FAILURE;
     967              :         }
     968              : 
     969          224 :         if (COLOPL_BC_G(php74_sort_mode) <= 0) {
     970          136 :                 COLOPL_BC_G(php74_hash_sort_func) = legacy_hash_sort_fast;
     971              :         } else {
     972           88 :                 COLOPL_BC_G(php74_hash_sort_func) = legacy_hash_sort_slow;
     973              :         }
     974              : 
     975          224 :         return SUCCESS;
     976              : }
     977              : 
     978           60 : static void php_colopl_bc_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compare_func, zend_bool renumber)
     979              : {
     980              :         zval *array, arr;
     981              :         PHP_COLOPL_BC_ARRAY_CMP_FUNC_VARS;
     982              : 
     983           60 :         PHP_COLOPL_BC_ARRAY_CMP_FUNC_BACKUP();
     984              : 
     985           60 :         ZEND_PARSE_PARAMETERS_START(2, 2)
     986           60 :                 Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
     987           60 :                 Z_PARAM_FUNC(COLOPL_BC_G(user_compare_fci), COLOPL_BC_G(user_compare_fci_cache))
     988           60 :         ZEND_PARSE_PARAMETERS_END_EX( PHP_COLOPL_BC_ARRAY_CMP_FUNC_RESTORE(); return );
     989              : 
     990              :         /* Copy array, so the in-place modifications will not be visible to the callback function */
     991           60 :         ZVAL_DUP(&arr, array);
     992           60 :         if (zend_hash_num_elements(Z_ARRVAL(arr)) == 0) {
     993            0 :                 PHP_COLOPL_BC_ARRAY_CMP_FUNC_RESTORE();
     994            0 :                 RETURN_TRUE;
     995              :         }
     996              :         
     997           60 :         COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, &arr, compare_func, renumber);
     998              : 
     999           60 :         zval_ptr_dtor(array);
    1000           60 :         ZVAL_ARR(array, Z_ARRVAL(arr));
    1001              : 
    1002           60 :         PHP_COLOPL_BC_ARRAY_CMP_FUNC_RESTORE();
    1003           60 :         RETURN_TRUE;
    1004              : }
    1005              : 
    1006           20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_ksort)
    1007              : {
    1008              :         zval *array;
    1009           20 :         zend_long sort_type = PHP_SORT_REGULAR;
    1010              :         bucket_compare_func_t cmp;
    1011              : 
    1012           20 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1013           20 :                 Z_PARAM_ARRAY_EX(array, 0, 1)
    1014           20 :                 Z_PARAM_OPTIONAL
    1015           20 :                 Z_PARAM_LONG(sort_type)
    1016           20 :         ZEND_PARSE_PARAMETERS_END();
    1017              : 
    1018           20 :         cmp = php_colopl_bc_get_key_compare_func(sort_type, 0);
    1019              : 
    1020           20 :         COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 0);
    1021              : 
    1022           20 :         RETURN_TRUE;
    1023              : }
    1024              : 
    1025           24 : PHP_FUNCTION(Colopl_ColoplBc_Php74_krsort)
    1026              : {
    1027              :         zval *array;
    1028           24 :         zend_long sort_type = PHP_SORT_REGULAR;
    1029              :         bucket_compare_func_t cmp;
    1030              : 
    1031           24 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1032           24 :                 Z_PARAM_ARRAY_EX(array, 0, 1)
    1033           24 :                 Z_PARAM_OPTIONAL
    1034           24 :                 Z_PARAM_LONG(sort_type)
    1035           24 :         ZEND_PARSE_PARAMETERS_END();
    1036              : 
    1037           24 :         cmp = php_colopl_bc_get_key_compare_func(sort_type, 1);
    1038              : 
    1039           24 :         COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 0);
    1040              : 
    1041           24 :         RETURN_TRUE;
    1042              : }
    1043              : 
    1044           20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_asort)
    1045              : {
    1046              :         zval *array;
    1047           20 :         zend_long sort_type = PHP_SORT_REGULAR;
    1048              :         bucket_compare_func_t cmp;
    1049              : 
    1050           20 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1051           20 :                 Z_PARAM_ARRAY_EX(array, 0, 1)
    1052           20 :                 Z_PARAM_OPTIONAL
    1053           20 :                 Z_PARAM_LONG(sort_type)
    1054           20 :         ZEND_PARSE_PARAMETERS_END();
    1055              : 
    1056           20 :         cmp = php_colopl_bc_get_data_compare_func(sort_type, 0);
    1057              : 
    1058           20 :         COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 0);
    1059              : 
    1060           20 :         RETURN_TRUE;
    1061              : }
    1062              : 
    1063           20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_arsort)
    1064              : {
    1065              :         zval *array;
    1066           20 :         zend_long sort_type = PHP_SORT_REGULAR;
    1067              :         bucket_compare_func_t cmp;
    1068              : 
    1069           20 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1070           20 :                 Z_PARAM_ARRAY_EX(array, 0, 1)
    1071           20 :                 Z_PARAM_OPTIONAL
    1072           20 :                 Z_PARAM_LONG(sort_type)
    1073           20 :         ZEND_PARSE_PARAMETERS_END();
    1074              : 
    1075           20 :         cmp = php_colopl_bc_get_data_compare_func(sort_type, 1);
    1076              : 
    1077           20 :         COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 0);
    1078              : 
    1079           20 :         RETURN_TRUE;
    1080              : }
    1081              : 
    1082           20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_sort)
    1083              : {
    1084              :         zval *array;
    1085           20 :         zend_long sort_type = PHP_SORT_REGULAR;
    1086              :         bucket_compare_func_t cmp;
    1087              : 
    1088           20 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1089           20 :                 Z_PARAM_ARRAY_EX(array, 0, 1)
    1090           20 :                 Z_PARAM_OPTIONAL
    1091           20 :                 Z_PARAM_LONG(sort_type)
    1092           20 :         ZEND_PARSE_PARAMETERS_END();
    1093              : 
    1094           20 :         cmp = php_colopl_bc_get_data_compare_func(sort_type, 0);
    1095              : 
    1096           20 :         COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 1);
    1097              : 
    1098           20 :         RETURN_TRUE;
    1099              : }
    1100              : 
    1101           20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_rsort)
    1102              : {
    1103              :         zval *array;
    1104           20 :         zend_long sort_type = PHP_SORT_REGULAR;
    1105              :         bucket_compare_func_t cmp;
    1106              : 
    1107           20 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1108           20 :                 Z_PARAM_ARRAY_EX(array, 0, 1)
    1109           20 :                 Z_PARAM_OPTIONAL
    1110           20 :                 Z_PARAM_LONG(sort_type)
    1111           20 :         ZEND_PARSE_PARAMETERS_END();
    1112              : 
    1113           20 :         cmp = php_colopl_bc_get_data_compare_func(sort_type, 1);
    1114              : 
    1115           20 :         COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 1);
    1116              : 
    1117           20 :         RETURN_TRUE;
    1118              : }
    1119              : 
    1120           20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_usort)
    1121              : {
    1122           20 :         php_colopl_bc_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_colopl_bc_array_user_compare, 1);
    1123           20 : }
    1124              : 
    1125           20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_uasort)
    1126              : {
    1127           20 :         php_colopl_bc_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_colopl_bc_array_user_compare, 0);
    1128           20 : }
    1129              : 
    1130           20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_uksort)
    1131              : {
    1132           20 :         php_colopl_bc_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_colopl_bc_array_user_key_compare, 0);
    1133           20 : }
    1134              : 
    1135            4 : PHP_FUNCTION(Colopl_ColoplBc_Php74_in_array)
    1136              : {
    1137            4 :         php_colopl_bc_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
    1138            4 : }
    1139              : 
    1140            4 : PHP_FUNCTION(Colopl_ColoplBc_Php74_array_search)
    1141              : {
    1142            4 :         php_colopl_bc_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
    1143            4 : }
    1144              : 
    1145            4 : PHP_FUNCTION(Colopl_ColoplBc_Php74_array_keys)
    1146              : {
    1147              :         zval *input,                            /* Input array */
    1148            4 :                  *search_value = NULL,  /* Value to search for */
    1149              :                  *entry,                                /* An entry in the input array */
    1150              :                  new_val;                               /* New value */
    1151            4 :         bool strict = 0;                        /* do strict comparison */
    1152              :         zend_ulong num_idx;
    1153              :         zend_string *str_idx;
    1154              :         zend_array *arrval;
    1155              :         zend_ulong elem_count;
    1156              : 
    1157            4 :         ZEND_PARSE_PARAMETERS_START(1, 3)
    1158            4 :                 Z_PARAM_ARRAY(input)
    1159            4 :                 Z_PARAM_OPTIONAL
    1160            4 :                 Z_PARAM_ZVAL(search_value)
    1161            4 :                 Z_PARAM_BOOL(strict)
    1162            4 :         ZEND_PARSE_PARAMETERS_END();
    1163            4 :         arrval = Z_ARRVAL_P(input);
    1164            4 :         elem_count = zend_hash_num_elements(arrval);
    1165              : 
    1166              :         /* Base case: empty input */
    1167            4 :         if (!elem_count) {
    1168            0 :                 RETURN_COPY(input);
    1169              :         }
    1170              : 
    1171              :         /* Initialize return array */
    1172            4 :         if (search_value != NULL) {
    1173            4 :                 array_init(return_value);
    1174              : 
    1175            4 :                 if (strict) {
    1176            0 :                         ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
    1177            0 :                                 ZVAL_DEREF(entry);
    1178            0 :                                 if (fast_is_identical_function(search_value, entry)) {
    1179            0 :                                         if (str_idx) {
    1180            0 :                                                 ZVAL_STR_COPY(&new_val, str_idx);
    1181              :                                         } else {
    1182            0 :                                                 ZVAL_LONG(&new_val, num_idx);
    1183              :                                         }
    1184            0 :                                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
    1185              :                                 }
    1186              :                         } ZEND_HASH_FOREACH_END();
    1187              :                 } else {
    1188           12 :                         ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
    1189            8 :                                 if (php_colopl_bc_fast_equal_check_function(search_value, entry)) {
    1190            8 :                                         if (str_idx) {
    1191            8 :                                                 ZVAL_STR_COPY(&new_val, str_idx);
    1192              :                                         } else {
    1193            0 :                                                 ZVAL_LONG(&new_val, num_idx);
    1194              :                                         }
    1195            8 :                                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
    1196              :                                 }
    1197              :                         } ZEND_HASH_FOREACH_END();
    1198              :                 }
    1199              :         } else {
    1200            0 :                 array_init_size(return_value, elem_count);
    1201            0 :                 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
    1202            0 :                 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
    1203            0 :                         if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
    1204              :                                 /* Optimistic case: range(0..n-1) for vector-like packed array */
    1205            0 :                                 zend_ulong lval = 0;
    1206              : 
    1207            0 :                                 for (; lval < elem_count; ++lval) {
    1208            0 :                                         ZEND_HASH_FILL_SET_LONG(lval);
    1209            0 :                                         ZEND_HASH_FILL_NEXT();
    1210              :                                 }
    1211              :                         } else {
    1212              :                                 /* Go through input array and add keys to the return array */
    1213            0 :                                 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
    1214            0 :                                         if (str_idx) {
    1215            0 :                                                 ZEND_HASH_FILL_SET_STR_COPY(str_idx);
    1216              :                                         } else {
    1217            0 :                                                 ZEND_HASH_FILL_SET_LONG(num_idx);
    1218              :                                         }
    1219            0 :                                         ZEND_HASH_FILL_NEXT();
    1220              :                                 } ZEND_HASH_FOREACH_END();
    1221              :                         }
    1222            0 :                 } ZEND_HASH_FILL_END();
    1223              :         }
    1224              : }
    1225              : 
    1226              : #if PHP_VERSION_ID < 80200
    1227              : PHP_FUNCTION(Colopl_ColoplBc_Php74_array_multisort)
    1228              : {
    1229              :         zval*                   args;
    1230              :         zval**                  arrays;
    1231              :         Bucket**                indirect;
    1232              :         uint32_t                idx;
    1233              :         Bucket*                 p;
    1234              :         HashTable*              hash;
    1235              :         int                             argc;
    1236              :         int                             array_size;
    1237              :         int                             num_arrays = 0;
    1238              :         int                             parse_state[COLOPL_BC_MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */
    1239              :         int                             sort_order = PHP_SORT_ASC;
    1240              :         int                             sort_type = PHP_SORT_REGULAR;
    1241              :         int                             i, k, n;
    1242              :         bucket_compare_func_t *func;
    1243              : 
    1244              :         ZEND_PARSE_PARAMETERS_START(1, -1)
    1245              :                 Z_PARAM_VARIADIC('+', args, argc)
    1246              :         ZEND_PARSE_PARAMETERS_END();
    1247              : 
    1248              :         /* Allocate space for storing pointers to input arrays and sort flags. */
    1249              :         arrays = (zval **)ecalloc(argc, sizeof(zval *));
    1250              :         for (i = 0; i < COLOPL_BC_MULTISORT_LAST; i++) {
    1251              :                 parse_state[i] = 0;
    1252              :         }
    1253              :         func = COLOPL_BC_G(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
    1254              : 
    1255              :         /* Here we go through the input arguments and parse them. Each one can
    1256              :          * be either an array or a sort flag which follows an array. If not
    1257              :          * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
    1258              :          * accordingly. There can't be two sort flags of the same type after an
    1259              :          * array, and the very first argument has to be an array. */
    1260              :         for (i = 0; i < argc; i++) {
    1261              :                 zval *arg = &args[i];
    1262              : 
    1263              :                 ZVAL_DEREF(arg);
    1264              :                 if (Z_TYPE_P(arg) == IS_ARRAY) {
    1265              :                         SEPARATE_ARRAY(arg);
    1266              :                         /* We see the next array, so we update the sort flags of
    1267              :                          * the previous array and reset the sort flags. */
    1268              :                         if (i > 0) {
    1269              :                                 COLOPL_BC_G(multisort_func)[num_arrays - 1] = php_colopl_bc_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
    1270              :                                 sort_order = PHP_SORT_ASC;
    1271              :                                 sort_type = PHP_SORT_REGULAR;
    1272              :                         }
    1273              :                         arrays[num_arrays++] = arg;
    1274              : 
    1275              :                         /* Next one may be an array or a list of sort flags. */
    1276              :                         for (k = 0; k < COLOPL_BC_MULTISORT_LAST; k++) {
    1277              :                                 parse_state[k] = 1;
    1278              :                         }
    1279              :                 } else if (Z_TYPE_P(arg) == IS_LONG) {
    1280              :                         switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
    1281              :                                 case PHP_SORT_ASC:
    1282              :                                 case PHP_SORT_DESC:
    1283              :                                         /* flag allowed here */
    1284              :                                         if (parse_state[COLOPL_BC_MULTISORT_ORDER] == 1) {
    1285              :                                                 /* Save the flag and make sure then next arg is not the current flag. */
    1286              :                                                 sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
    1287              :                                                 parse_state[COLOPL_BC_MULTISORT_ORDER] = 0;
    1288              :                                         } else {
    1289              :                                                 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
    1290              :                                                 COLOPL_BC_MULTISORT_ABORT;
    1291              :                                         }
    1292              :                                         break;
    1293              : 
    1294              :                                 case PHP_SORT_REGULAR:
    1295              :                                 case PHP_SORT_NUMERIC:
    1296              :                                 case PHP_SORT_STRING:
    1297              :                                 case PHP_SORT_NATURAL:
    1298              :                                 case PHP_SORT_LOCALE_STRING:
    1299              :                                         /* flag allowed here */
    1300              :                                         if (parse_state[COLOPL_BC_MULTISORT_TYPE] == 1) {
    1301              :                                                 /* Save the flag and make sure then next arg is not the current flag. */
    1302              :                                                 sort_type = (int)Z_LVAL_P(arg);
    1303              :                                                 parse_state[COLOPL_BC_MULTISORT_TYPE] = 0;
    1304              :                                         } else {
    1305              :                                                 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
    1306              :                                                 COLOPL_BC_MULTISORT_ABORT;
    1307              :                                         }
    1308              :                                         break;
    1309              : 
    1310              :                                 default:
    1311              :                                         zend_argument_value_error(i + 1, "must be a valid sort flag");
    1312              :                                         COLOPL_BC_MULTISORT_ABORT;
    1313              :                                         break;
    1314              : 
    1315              :                         }
    1316              :                 } else {
    1317              :                         zend_argument_type_error(i + 1, "must be an array or a sort flag");
    1318              :                         COLOPL_BC_MULTISORT_ABORT;
    1319              :                 }
    1320              :         }
    1321              :         /* Take care of the last array sort flags. */
    1322              :         COLOPL_BC_G(multisort_func)[num_arrays - 1] = php_colopl_bc_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
    1323              : 
    1324              :         /* Make sure the arrays are of the same size. */
    1325              :         array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
    1326              :         for (i = 0; i < num_arrays; i++) {
    1327              :                 if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
    1328              :                         zend_value_error("Array sizes are inconsistent");
    1329              :                         COLOPL_BC_MULTISORT_ABORT;
    1330              :                 }
    1331              :         }
    1332              : 
    1333              :         /* If all arrays are empty we don't need to do anything. */
    1334              :         if (array_size < 1) {
    1335              :                 efree(func);
    1336              :                 efree(arrays);
    1337              :                 RETURN_TRUE;
    1338              :         }
    1339              : 
    1340              :         /* Create the indirection array. This array is of size MxN, where
    1341              :          * M is the number of entries in each input array and N is the number
    1342              :          * of the input arrays + 1. The last column is NULL to indicate the end
    1343              :          * of the row. */
    1344              :         indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
    1345              :         for (i = 0; i < array_size; i++) {
    1346              :                 indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
    1347              :         }
    1348              :         for (i = 0; i < num_arrays; i++) {
    1349              :                 k = 0;
    1350              :                 for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++) {
    1351              :                         p = Z_ARRVAL_P(arrays[i])->arData + idx;
    1352              :                         if (Z_TYPE(p->val) == IS_UNDEF) continue;
    1353              :                         indirect[k][i] = *p;
    1354              :                         k++;
    1355              :                 }
    1356              :         }
    1357              :         for (k = 0; k < array_size; k++) {
    1358              :                 ZVAL_UNDEF(&indirect[k][num_arrays].val);
    1359              :         }
    1360              : 
    1361              :         /* Do the actual sort magic - bada-bim, bada-boom. */
    1362              :         zend_sort(indirect, array_size, sizeof(Bucket *), php_colopl_bc_multisort_compare, (swap_func_t)colopl_bc_array_bucket_p_swap);
    1363              : 
    1364              :         /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
    1365              :         for (i = 0; i < num_arrays; i++) {
    1366              :                 int repack;
    1367              : 
    1368              :                 hash = Z_ARRVAL_P(arrays[i]);
    1369              :                 hash->nNumUsed = array_size;
    1370              :                 hash->nInternalPointer = 0;
    1371              :                 repack = !(HT_FLAGS(hash) & HASH_FLAG_PACKED);
    1372              : 
    1373              :                 for (n = 0, k = 0; k < array_size; k++) {
    1374              :                         hash->arData[k] = indirect[k][i];
    1375              :                         if (hash->arData[k].key == NULL) {
    1376              :                                 hash->arData[k].h = n++;
    1377              :                         } else {
    1378              :                                 repack = 0;
    1379              :                         }
    1380              :                 }
    1381              :                 hash->nNextFreeElement = array_size;
    1382              :                 if (repack) {
    1383              :                         zend_hash_to_packed(hash);
    1384              :                 } else if (!(HT_FLAGS(hash) & HASH_FLAG_PACKED)) {
    1385              :                         zend_hash_rehash(hash);
    1386              :                 }
    1387              :         }
    1388              : 
    1389              :         /* Clean up. */
    1390              :         for (i = 0; i < array_size; i++) {
    1391              :                 efree(indirect[i]);
    1392              :         }
    1393              :         efree(indirect);
    1394              :         efree(func);
    1395              :         efree(arrays);
    1396              :         RETURN_TRUE;
    1397              : }
    1398              : #else
    1399            4 : PHP_FUNCTION(Colopl_ColoplBc_Php74_array_multisort)
    1400              : {
    1401              :         zval*                   args;
    1402              :         zval**                  arrays;
    1403              :         Bucket**                indirect;
    1404              :         uint32_t                idx;
    1405              :         HashTable*              hash;
    1406              :         int                             argc;
    1407              :         int                             array_size;
    1408            4 :         int                             num_arrays = 0;
    1409              :         int                             parse_state[COLOPL_BC_MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
    1410            4 :         int                             sort_order = PHP_SORT_ASC;
    1411            4 :         int                             sort_type  = PHP_SORT_REGULAR;
    1412              :         int                             i, k, n;
    1413              :         bucket_compare_func_t *func;
    1414              : 
    1415            4 :         ZEND_PARSE_PARAMETERS_START(1, -1)
    1416            4 :                 Z_PARAM_VARIADIC('+', args, argc)
    1417            4 :         ZEND_PARSE_PARAMETERS_END();
    1418              : 
    1419              :         /* Allocate space for storing pointers to input arrays and sort flags. */
    1420            4 :         arrays = (zval **)ecalloc(argc, sizeof(zval *));
    1421           12 :         for (i = 0; i < COLOPL_BC_MULTISORT_LAST; i++) {
    1422            8 :                 parse_state[i] = 0;
    1423              :         }
    1424            4 :         func = COLOPL_BC_G(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
    1425              : 
    1426              :         /* Here we go through the input arguments and parse them. Each one can
    1427              :          * be either an array or a sort flag which follows an array. If not
    1428              :          * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
    1429              :          * accordingly. There can't be two sort flags of the same type after an
    1430              :          * array, and the very first argument has to be an array. */
    1431           12 :         for (i = 0; i < argc; i++) {
    1432            8 :                 zval *arg = &args[i];
    1433              : 
    1434            8 :                 ZVAL_DEREF(arg);
    1435            8 :                 if (Z_TYPE_P(arg) == IS_ARRAY) {
    1436            8 :                         SEPARATE_ARRAY(arg);
    1437              :                         /* We see the next array, so we update the sort flags of
    1438              :                          * the previous array and reset the sort flags. */
    1439            8 :                         if (i > 0) {
    1440            4 :                                 COLOPL_BC_G(multisort_func)[num_arrays - 1] = php_colopl_bc_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
    1441            4 :                                 sort_order = PHP_SORT_ASC;
    1442            4 :                                 sort_type = PHP_SORT_REGULAR;
    1443              :                         }
    1444            8 :                         arrays[num_arrays++] = arg;
    1445              : 
    1446              :                         /* Next one may be an array or a list of sort flags. */
    1447           24 :                         for (k = 0; k < COLOPL_BC_MULTISORT_LAST; k++) {
    1448           16 :                                 parse_state[k] = 1;
    1449              :                         }
    1450            0 :                 } else if (Z_TYPE_P(arg) == IS_LONG) {
    1451            0 :                         switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
    1452            0 :                                 case PHP_SORT_ASC:
    1453              :                                 case PHP_SORT_DESC:
    1454              :                                         /* flag allowed here */
    1455            0 :                                         if (parse_state[COLOPL_BC_MULTISORT_ORDER] == 1) {
    1456              :                                                 /* Save the flag and make sure then next arg is not the current flag. */
    1457            0 :                                                 sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
    1458            0 :                                                 parse_state[COLOPL_BC_MULTISORT_ORDER] = 0;
    1459              :                                         } else {
    1460            0 :                                                 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
    1461            0 :                                                 COLOPL_BC_MULTISORT_ABORT;
    1462              :                                         }
    1463            0 :                                         break;
    1464              : 
    1465            0 :                                 case PHP_SORT_REGULAR:
    1466              :                                 case PHP_SORT_NUMERIC:
    1467              :                                 case PHP_SORT_STRING:
    1468              :                                 case PHP_SORT_NATURAL:
    1469              :                                 case PHP_SORT_LOCALE_STRING:
    1470              :                                         /* flag allowed here */
    1471            0 :                                         if (parse_state[COLOPL_BC_MULTISORT_TYPE] == 1) {
    1472              :                                                 /* Save the flag and make sure then next arg is not the current flag. */
    1473            0 :                                                 sort_type = (int)Z_LVAL_P(arg);
    1474            0 :                                                 parse_state[COLOPL_BC_MULTISORT_TYPE] = 0;
    1475              :                                         } else {
    1476            0 :                                                 zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
    1477            0 :                                                 COLOPL_BC_MULTISORT_ABORT;
    1478              :                                         }
    1479            0 :                                         break;
    1480              : 
    1481            0 :                                 default:
    1482            0 :                                         zend_argument_value_error(i + 1, "must be a valid sort flag");
    1483            0 :                                         COLOPL_BC_MULTISORT_ABORT;
    1484              :                                         break;
    1485              : 
    1486              :                         }
    1487              :                 } else {
    1488            0 :                         zend_argument_type_error(i + 1, "must be an array or a sort flag");
    1489            0 :                         COLOPL_BC_MULTISORT_ABORT;
    1490              :                 }
    1491              :         }
    1492              :         /* Take care of the last array sort flags. */
    1493            4 :         COLOPL_BC_G(multisort_func)[num_arrays - 1] = php_colopl_bc_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
    1494              : 
    1495              :         /* Make sure the arrays are of the same size. */
    1496            4 :         array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
    1497           12 :         for (i = 0; i < num_arrays; i++) {
    1498            8 :                 if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
    1499            0 :                         zend_value_error("Array sizes are inconsistent");
    1500            0 :                         COLOPL_BC_MULTISORT_ABORT;
    1501              :                 }
    1502              :         }
    1503              : 
    1504              :         /* If all arrays are empty we don't need to do anything. */
    1505            4 :         if (array_size < 1) {
    1506            0 :                 efree(func);
    1507            0 :                 efree(arrays);
    1508            0 :                 RETURN_TRUE;
    1509              :         }
    1510              : 
    1511              :         /* Create the indirection array. This array is of size MxN, where
    1512              :          * M is the number of entries in each input array and N is the number
    1513              :          * of the input arrays + 1. The last column is UNDEF to indicate the end
    1514              :          * of the row. It also stores the original position for stable sorting. */
    1515            4 :         indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
    1516          404 :         for (i = 0; i < array_size; i++) {
    1517          400 :                 indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
    1518              :         }
    1519           12 :         for (i = 0; i < num_arrays; i++) {
    1520            8 :                 k = 0;
    1521            8 :                 if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) {
    1522            8 :                         zval *zv = Z_ARRVAL_P(arrays[i])->arPacked;
    1523          808 :                         for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) {
    1524          800 :                                 if (Z_TYPE_P(zv) == IS_UNDEF) continue;
    1525          800 :                                 ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
    1526          800 :                                 indirect[k][i].h = idx;
    1527          800 :                                 indirect[k][i].key = NULL;
    1528          800 :                                 k++;
    1529              :                         }
    1530              :                 } else {
    1531            0 :                         Bucket *p = Z_ARRVAL_P(arrays[i])->arData;
    1532            0 :                         for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) {
    1533            0 :                                 if (Z_TYPE(p->val) == IS_UNDEF) continue;
    1534            0 :                                 indirect[k][i] = *p;
    1535            0 :                                 k++;
    1536              :                         }
    1537              :                 }
    1538              :         }
    1539          404 :         for (k = 0; k < array_size; k++) {
    1540          400 :                 ZVAL_UNDEF(&indirect[k][num_arrays].val);
    1541          400 :                 Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
    1542              :         }
    1543              : 
    1544              :         /* Do the actual sort magic - bada-bim, bada-boom. */
    1545            4 :         zend_sort(indirect, array_size, sizeof(Bucket *), php_colopl_bc_multisort_compare, (swap_func_t)colopl_bc_array_bucket_p_swap);
    1546              : 
    1547              :         /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
    1548           12 :         for (i = 0; i < num_arrays; i++) {
    1549            8 :                 hash = Z_ARRVAL_P(arrays[i]);
    1550            8 :                 hash->nNumUsed = array_size;
    1551            8 :                 hash->nNextFreeElement = array_size;
    1552            8 :                 hash->nInternalPointer = 0;
    1553            8 :                 if (HT_IS_PACKED(hash)) {
    1554          808 :                         for (k = 0; k < array_size; k++) {
    1555          800 :                                 ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
    1556              :                         }
    1557              :                 } else {
    1558            0 :                         int repack = 1;
    1559              : 
    1560            0 :                         for (n = 0, k = 0; k < array_size; k++) {
    1561            0 :                                 hash->arData[k] = indirect[k][i];
    1562            0 :                                 if (hash->arData[k].key == NULL) {
    1563            0 :                                         hash->arData[k].h = n++;
    1564              :                                 } else {
    1565            0 :                                         repack = 0;
    1566              :                                 }
    1567              :                         }
    1568            0 :                         if (repack) {
    1569            0 :                                 zend_hash_to_packed(hash);
    1570              :                         }
    1571              :                 }
    1572              :         }
    1573              : 
    1574              :         /* Clean up. */
    1575          404 :         for (i = 0; i < array_size; i++) {
    1576          400 :                 efree(indirect[i]);
    1577              :         }
    1578            4 :         efree(indirect);
    1579            4 :         efree(func);
    1580            4 :         efree(arrays);
    1581            4 :         RETURN_TRUE;
    1582              : }
    1583              : #endif
    1584              : 
    1585              : /* BinaryOps */
    1586              : 
    1587         1172 : PHP_FUNCTION(Colopl_ColoplBc_Php74_eq)
    1588              : {
    1589              :         zval *op1, *op2;
    1590              : 
    1591         1172 :         ZEND_PARSE_PARAMETERS_START(2, 2)
    1592         1172 :                 Z_PARAM_ZVAL(op1)
    1593         1172 :                 Z_PARAM_ZVAL(op2)
    1594         1172 :         ZEND_PARSE_PARAMETERS_END();
    1595              : 
    1596         1172 :         RETURN_BOOL(php_colopl_bc_compare(op1, op2) == 0);
    1597              : }
    1598              : 
    1599            0 : PHP_FUNCTION(Colopl_ColoplBc_Php74_neq)
    1600              : {
    1601              :         zval *op1, *op2;
    1602              : 
    1603            0 :         ZEND_PARSE_PARAMETERS_START(2, 2)
    1604            0 :                 Z_PARAM_ZVAL(op1)
    1605            0 :                 Z_PARAM_ZVAL(op2)
    1606            0 :         ZEND_PARSE_PARAMETERS_END();
    1607              : 
    1608            0 :         RETURN_BOOL(php_colopl_bc_compare(op1, op2) != 0);
    1609              : }
    1610              : 
    1611         1156 : PHP_FUNCTION(Colopl_ColoplBc_Php74_lt)
    1612              : {
    1613              :         zval *op1, *op2;
    1614              : 
    1615         1156 :         ZEND_PARSE_PARAMETERS_START(2, 2)
    1616         1156 :                 Z_PARAM_ZVAL(op1)
    1617         1156 :                 Z_PARAM_ZVAL(op2)
    1618         1156 :         ZEND_PARSE_PARAMETERS_END();
    1619              : 
    1620         1156 :         RETURN_BOOL(php_colopl_bc_compare(op1, op2) < 0);
    1621              : }
    1622              : 
    1623         1156 : PHP_FUNCTION(Colopl_ColoplBc_Php74_lte)
    1624              : {
    1625              :         zval *op1, *op2;
    1626              : 
    1627         1156 :         ZEND_PARSE_PARAMETERS_START(2, 2)
    1628         1156 :                 Z_PARAM_ZVAL(op1)
    1629         1156 :                 Z_PARAM_ZVAL(op2)
    1630         1156 :         ZEND_PARSE_PARAMETERS_END();
    1631              : 
    1632         1156 :         RETURN_BOOL(php_colopl_bc_compare(op1, op2) <= 0);
    1633              : }
    1634              : 
    1635         1156 : PHP_FUNCTION(Colopl_ColoplBc_Php74_gt)
    1636              : {
    1637              :         zval *op1, *op2;
    1638              : 
    1639         1156 :         ZEND_PARSE_PARAMETERS_START(2, 2)
    1640         1156 :                 Z_PARAM_ZVAL(op1)
    1641         1156 :                 Z_PARAM_ZVAL(op2)
    1642         1156 :         ZEND_PARSE_PARAMETERS_END();
    1643              : 
    1644         1156 :         RETURN_BOOL(php_colopl_bc_compare(op2, op1) < 0);
    1645              : }
    1646              : 
    1647         1156 : PHP_FUNCTION(Colopl_ColoplBc_Php74_gte)
    1648              : {
    1649              :         zval *op1, *op2;
    1650              : 
    1651         1156 :         ZEND_PARSE_PARAMETERS_START(2, 2)
    1652         1156 :                 Z_PARAM_ZVAL(op1)
    1653         1156 :                 Z_PARAM_ZVAL(op2)
    1654         1156 :         ZEND_PARSE_PARAMETERS_END();
    1655              : 
    1656         1156 :         RETURN_BOOL(php_colopl_bc_compare(op2, op1) <= 0);
    1657              : }
    1658              : 
    1659            0 : PHP_FUNCTION(Colopl_ColoplBc_Php74_spaceship)
    1660              : {
    1661              :         zval *op1, *op2;
    1662              : 
    1663            0 :         ZEND_PARSE_PARAMETERS_START(2, 2)
    1664            0 :                 Z_PARAM_ZVAL(op1)
    1665            0 :                 Z_PARAM_ZVAL(op2)
    1666            0 :         ZEND_PARSE_PARAMETERS_END();
    1667              : 
    1668            0 :         RETURN_LONG(php_colopl_bc_compare(op1, op2));
    1669              : }
        

Generated by: LCOV version 2.0-1