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

Generated by: LCOV version 2.0-1