1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Stanislav Malyshev <stas@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include <config.h>
19 #endif
20
21 #include "php.h"
22 #include "php_gmp.h"
23 #include "php_gmp_int.h"
24 #include "ext/standard/info.h"
25 #include "ext/standard/php_var.h"
26 #include "zend_smart_str_public.h"
27 #include "zend_exceptions.h"
28
29 #include <gmp.h>
30
31 /* Needed for gmp_random() */
32 #include "ext/random/php_random.h"
33 #include "ext/random/php_random_csprng.h"
34
35 #define GMP_ROUND_ZERO 0
36 #define GMP_ROUND_PLUSINF 1
37 #define GMP_ROUND_MINUSINF 2
38
39 #ifdef mpir_version
40 #define GMP_MPIR_VERSION_STRING ((char *) mpir_version)
41 #endif
42 #define GMP_VERSION_STRING ((char *) gmp_version)
43
44 #define GMP_MSW_FIRST (1 << 0)
45 #define GMP_LSW_FIRST (1 << 1)
46 #define GMP_LITTLE_ENDIAN (1 << 2)
47 #define GMP_BIG_ENDIAN (1 << 3)
48 #define GMP_NATIVE_ENDIAN (1 << 4)
49
50 #include "gmp_arginfo.h"
51
52 ZEND_DECLARE_MODULE_GLOBALS(gmp)
53 static ZEND_GINIT_FUNCTION(gmp);
54
55 /* {{{ gmp_module_entry */
56 zend_module_entry gmp_module_entry = {
57 STANDARD_MODULE_HEADER,
58 "gmp",
59 ext_functions,
60 ZEND_MODULE_STARTUP_N(gmp),
61 NULL,
62 NULL,
63 ZEND_MODULE_DEACTIVATE_N(gmp),
64 ZEND_MODULE_INFO_N(gmp),
65 PHP_GMP_VERSION,
66 ZEND_MODULE_GLOBALS(gmp),
67 ZEND_GINIT(gmp),
68 NULL,
69 NULL,
70 STANDARD_MODULE_PROPERTIES_EX
71 };
72 /* }}} */
73
74 #ifdef COMPILE_DL_GMP
75 #ifdef ZTS
76 ZEND_TSRMLS_CACHE_DEFINE()
77 #endif
78 ZEND_GET_MODULE(gmp)
79 #endif
80
81 static zend_class_entry *gmp_ce;
82 static zend_object_handlers gmp_object_handlers;
83
php_gmp_class_entry(void)84 PHP_GMP_API zend_class_entry *php_gmp_class_entry(void) {
85 return gmp_ce;
86 }
87
88 typedef struct _gmp_temp {
89 mpz_t num;
90 bool is_used;
91 } gmp_temp_t;
92
93 #define GMP_MAX_BASE 62
94
95 #define GMP_51_OR_NEWER \
96 ((__GNU_MP_VERSION >= 6) || (__GNU_MP_VERSION >= 5 && __GNU_MP_VERSION_MINOR >= 1))
97
98 #define IS_GMP(zval) \
99 (Z_TYPE_P(zval) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zval), gmp_ce))
100
101 #define GET_GMP_OBJECT_FROM_OBJ(obj) \
102 php_gmp_object_from_zend_object(obj)
103 #define GET_GMP_OBJECT_FROM_ZVAL(zv) \
104 GET_GMP_OBJECT_FROM_OBJ(Z_OBJ_P(zv))
105
106 #define GET_GMP_FROM_ZVAL(zval) \
107 GET_GMP_OBJECT_FROM_OBJ(Z_OBJ_P(zval))->num
108
109 /* The FETCH_GMP_ZVAL_* family of macros is used to fetch a gmp number
110 * (mpz_ptr) from a zval. If the zval is not a GMP instance, then we
111 * try to convert the value to a temporary gmp number using convert_to_gmp.
112 * This temporary number is stored in the temp argument, which is of type
113 * gmp_temp_t. This temporary value needs to be freed lateron using the
114 * FREE_GMP_TEMP macro.
115 *
116 * If the conversion to a gmp number fails, the macros RETURN_THROWS() due to TypeError.
117 * The _DEP / _DEP_DEP variants additionally free the temporary values
118 * passed in the last / last two arguments.
119 *
120 * If one zval can sometimes be fetched as a long you have to set the
121 * is_used member of the corresponding gmp_temp_t value to 0, otherwise
122 * the FREE_GMP_TEMP and *_DEP macros will not work properly.
123 *
124 * The three FETCH_GMP_ZVAL_* macros below are mostly copy & paste code
125 * as I couldn't find a way to combine them.
126 */
127
128 #define FREE_GMP_TEMP(temp) \
129 if (temp.is_used) { \
130 mpz_clear(temp.num); \
131 }
132
133 #define FETCH_GMP_ZVAL_DEP_DEP(gmpnumber, zval, temp, dep1, dep2, arg_pos) \
134 if (IS_GMP(zval)) { \
135 gmpnumber = GET_GMP_FROM_ZVAL(zval); \
136 temp.is_used = 0; \
137 } else { \
138 mpz_init(temp.num); \
139 if (convert_to_gmp(temp.num, zval, 0, arg_pos) == FAILURE) { \
140 mpz_clear(temp.num); \
141 FREE_GMP_TEMP(dep1); \
142 FREE_GMP_TEMP(dep2); \
143 RETURN_THROWS(); \
144 } \
145 temp.is_used = 1; \
146 gmpnumber = temp.num; \
147 }
148
149 #define FETCH_GMP_ZVAL_DEP(gmpnumber, zval, temp, dep, arg_pos) \
150 if (IS_GMP(zval)) { \
151 gmpnumber = GET_GMP_FROM_ZVAL(zval); \
152 temp.is_used = 0; \
153 } else { \
154 mpz_init(temp.num); \
155 if (convert_to_gmp(temp.num, zval, 0, arg_pos) == FAILURE) { \
156 mpz_clear(temp.num); \
157 FREE_GMP_TEMP(dep); \
158 RETURN_THROWS(); \
159 } \
160 temp.is_used = 1; \
161 gmpnumber = temp.num; \
162 }
163
164 #define FETCH_GMP_ZVAL(gmpnumber, zval, temp, arg_pos) \
165 if (IS_GMP(zval)) { \
166 gmpnumber = GET_GMP_FROM_ZVAL(zval); \
167 temp.is_used = 0; \
168 } else { \
169 mpz_init(temp.num); \
170 if (convert_to_gmp(temp.num, zval, 0, arg_pos) == FAILURE) { \
171 mpz_clear(temp.num); \
172 RETURN_THROWS(); \
173 } \
174 temp.is_used = 1; \
175 gmpnumber = temp.num; \
176 }
177
178 #define INIT_GMP_RETVAL(gmpnumber) \
179 gmp_create(return_value, &gmpnumber)
180
181 static void gmp_strval(zval *result, mpz_t gmpnum, int base);
182 static zend_result convert_zstr_to_gmp(mpz_t gmp_number, const zend_string *val, zend_long base, uint32_t arg_pos);
183 static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, uint32_t arg_pos);
184 static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg, bool is_operator);
185
186 /*
187 * The gmp_*_op functions provide an implementation for several common types
188 * of GMP functions. The gmp_zval_(unary|binary)_*_op functions have to be manually
189 * passed zvals to work on, whereas the gmp_(unary|binary)_*_op macros already
190 * include parameter parsing.
191 */
192 typedef void (*gmp_unary_op_t)(mpz_ptr, mpz_srcptr);
193 typedef mp_bitcnt_t (*gmp_unary_opl_t)(mpz_srcptr);
194
195 typedef void (*gmp_unary_ui_op_t)(mpz_ptr, gmp_ulong);
196
197 typedef void (*gmp_binary_op_t)(mpz_ptr, mpz_srcptr, mpz_srcptr);
198
199 typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, gmp_ulong);
200 typedef void (*gmp_binary_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr);
201 typedef gmp_ulong (*gmp_binary_ui_op2_t)(mpz_ptr, mpz_ptr, mpz_srcptr, gmp_ulong);
202
203 static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator);
204 static inline void gmp_zval_binary_ui_op2(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op2_t gmp_op, gmp_binary_ui_op2_t gmp_ui_op, int check_b_zero);
205 static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op);
206
gmp_mpz_tdiv_q_ui(mpz_ptr a,mpz_srcptr b,gmp_ulong c)207 static void gmp_mpz_tdiv_q_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
208 mpz_tdiv_q_ui(a, b, c);
209 }
gmp_mpz_tdiv_r_ui(mpz_ptr a,mpz_srcptr b,gmp_ulong c)210 static void gmp_mpz_tdiv_r_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
211 mpz_tdiv_r_ui(a, b, c);
212 }
gmp_mpz_fdiv_q_ui(mpz_ptr a,mpz_srcptr b,gmp_ulong c)213 static void gmp_mpz_fdiv_q_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
214 mpz_fdiv_q_ui(a, b, c);
215 }
gmp_mpz_fdiv_r_ui(mpz_ptr a,mpz_srcptr b,gmp_ulong c)216 static void gmp_mpz_fdiv_r_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
217 mpz_fdiv_r_ui(a, b, c);
218 }
gmp_mpz_cdiv_r_ui(mpz_ptr a,mpz_srcptr b,gmp_ulong c)219 static void gmp_mpz_cdiv_r_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
220 mpz_cdiv_r_ui(a, b, c);
221 }
gmp_mpz_cdiv_q_ui(mpz_ptr a,mpz_srcptr b,gmp_ulong c)222 static void gmp_mpz_cdiv_q_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
223 mpz_cdiv_q_ui(a, b, c);
224 }
gmp_mpz_mod_ui(mpz_ptr a,mpz_srcptr b,gmp_ulong c)225 static void gmp_mpz_mod_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
226 mpz_mod_ui(a, b, c);
227 }
gmp_mpz_gcd_ui(mpz_ptr a,mpz_srcptr b,gmp_ulong c)228 static void gmp_mpz_gcd_ui(mpz_ptr a, mpz_srcptr b, gmp_ulong c) {
229 mpz_gcd_ui(a, b, c);
230 }
231
232 /* Binary operations */
233 #define gmp_binary_ui_op(op, uop) _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, uop, 0)
234 #define gmp_binary_op(op) _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, NULL, 0)
235 #define gmp_binary_ui_op_no_zero(op, uop) \
236 _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op, uop, 1)
237
238 /* Unary operations */
239 #define gmp_unary_op(op) _gmp_unary_op(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
240 #define gmp_unary_opl(op) _gmp_unary_opl(INTERNAL_FUNCTION_PARAM_PASSTHRU, op)
241
gmp_free_object_storage(zend_object * obj)242 static void gmp_free_object_storage(zend_object *obj) /* {{{ */
243 {
244 gmp_object *intern = GET_GMP_OBJECT_FROM_OBJ(obj);
245
246 mpz_clear(intern->num);
247 zend_object_std_dtor(&intern->std);
248 }
249 /* }}} */
250
gmp_create_object_ex(zend_class_entry * ce,mpz_ptr * gmpnum_target)251 static inline zend_object *gmp_create_object_ex(zend_class_entry *ce, mpz_ptr *gmpnum_target) /* {{{ */
252 {
253 gmp_object *intern = emalloc(sizeof(gmp_object) + zend_object_properties_size(ce));
254
255 zend_object_std_init(&intern->std, ce);
256 object_properties_init(&intern->std, ce);
257
258 mpz_init(intern->num);
259 *gmpnum_target = intern->num;
260
261 return &intern->std;
262 }
263 /* }}} */
264
gmp_create_object(zend_class_entry * ce)265 static zend_object *gmp_create_object(zend_class_entry *ce) /* {{{ */
266 {
267 mpz_ptr gmpnum_dummy;
268 return gmp_create_object_ex(ce, &gmpnum_dummy);
269 }
270 /* }}} */
271
gmp_create(zval * target,mpz_ptr * gmpnum_target)272 static inline void gmp_create(zval *target, mpz_ptr *gmpnum_target) /* {{{ */
273 {
274 ZVAL_OBJ(target, gmp_create_object_ex(gmp_ce, gmpnum_target));
275 }
276 /* }}} */
277
gmp_cast_object(zend_object * readobj,zval * writeobj,int type)278 static zend_result gmp_cast_object(zend_object *readobj, zval *writeobj, int type) /* {{{ */
279 {
280 mpz_ptr gmpnum;
281 switch (type) {
282 case IS_STRING:
283 gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
284 gmp_strval(writeobj, gmpnum, 10);
285 return SUCCESS;
286 case IS_LONG:
287 gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
288 ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
289 return SUCCESS;
290 case IS_DOUBLE:
291 gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
292 ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
293 return SUCCESS;
294 case _IS_NUMBER:
295 gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
296 if (mpz_fits_slong_p(gmpnum)) {
297 ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
298 } else {
299 ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum));
300 }
301 return SUCCESS;
302 case _IS_BOOL:
303 gmpnum = GET_GMP_OBJECT_FROM_OBJ(readobj)->num;
304 ZVAL_BOOL(writeobj, mpz_sgn(gmpnum) != 0);
305 return SUCCESS;
306 default:
307 return FAILURE;
308 }
309 }
310 /* }}} */
311
gmp_get_debug_info(zend_object * obj,int * is_temp)312 static HashTable *gmp_get_debug_info(zend_object *obj, int *is_temp) /* {{{ */
313 {
314 HashTable *ht, *props = zend_std_get_properties(obj);
315 mpz_ptr gmpnum = GET_GMP_OBJECT_FROM_OBJ(obj)->num;
316 zval zv;
317
318 *is_temp = 1;
319 ht = zend_array_dup(props);
320
321 gmp_strval(&zv, gmpnum, 10);
322 zend_hash_str_update(ht, "num", sizeof("num")-1, &zv);
323
324 return ht;
325 }
326 /* }}} */
327
gmp_clone_obj(zend_object * obj)328 static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */
329 {
330 gmp_object *old_object = GET_GMP_OBJECT_FROM_OBJ(obj);
331 gmp_object *new_object = GET_GMP_OBJECT_FROM_OBJ(gmp_create_object(obj->ce));
332
333 zend_objects_clone_members( &new_object->std, &old_object->std);
334
335 mpz_set(new_object->num, old_object->num);
336
337 return &new_object->std;
338 }
339 /* }}} */
340
shift_operator_helper(gmp_binary_ui_op_t op,zval * return_value,zval * op1,zval * op2,uint8_t opcode)341 static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, uint8_t opcode) {
342 zend_long shift = 0;
343
344 if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
345 if (UNEXPECTED(!IS_GMP(op2))) {
346 // For PHP 8.3 and up use zend_try_get_long()
347 switch (Z_TYPE_P(op2)) {
348 case IS_DOUBLE:
349 shift = zval_get_long(op2);
350 if (UNEXPECTED(EG(exception))) {
351 return FAILURE;
352 }
353 break;
354 case IS_STRING:
355 if (is_numeric_str_function(Z_STR_P(op2), &shift, NULL) != IS_LONG) {
356 goto valueof_op_failure;
357 }
358 break;
359 default:
360 goto typeof_op_failure;
361 }
362 } else {
363 // TODO We shouldn't cast the GMP object to int here
364 shift = zval_get_long(op2);
365 }
366 } else {
367 shift = Z_LVAL_P(op2);
368 }
369
370 if (shift < 0) {
371 zend_throw_error(
372 zend_ce_value_error, "%s must be greater than or equal to 0",
373 opcode == ZEND_POW ? "Exponent" : "Shift"
374 );
375 ZVAL_UNDEF(return_value);
376 return FAILURE;
377 } else {
378 mpz_ptr gmpnum_op, gmpnum_result;
379 gmp_temp_t temp;
380
381 /* We do not use FETCH_GMP_ZVAL(...); here as we don't use convert_to_gmp()
382 * as we want to handle the emitted exception ourself. */
383 if (UNEXPECTED(!IS_GMP(op1))) {
384 if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) {
385 goto typeof_op_failure;
386 }
387 mpz_init(temp.num);
388 mpz_set_si(temp.num, Z_LVAL_P(op1));
389 temp.is_used = 1;
390 gmpnum_op = temp.num;
391 } else {
392 gmpnum_op = GET_GMP_FROM_ZVAL(op1);
393 temp.is_used = 0;
394 }
395 INIT_GMP_RETVAL(gmpnum_result);
396 op(gmpnum_result, gmpnum_op, (gmp_ulong) shift);
397 FREE_GMP_TEMP(temp);
398 return SUCCESS;
399 }
400
401 typeof_op_failure: ;
402 /* Returning FAILURE without throwing an exception would emit the
403 * Unsupported operand types: GMP OP TypeOfOp2
404 * However, this leads to the engine trying to interpret the GMP object as an integer
405 * and doing the operation that way, which is not something we want. */
406 const char *op_sigil;
407 switch (opcode) {
408 case ZEND_POW:
409 op_sigil = "**";
410 break;
411 case ZEND_SL:
412 op_sigil = "<<";
413 break;
414 case ZEND_SR:
415 op_sigil = ">>";
416 break;
417 EMPTY_SWITCH_DEFAULT_CASE();
418 }
419 zend_type_error("Unsupported operand types: %s %s %s", zend_zval_type_name(op1), op_sigil, zend_zval_type_name(op2));
420 return FAILURE;
421 valueof_op_failure:
422 zend_value_error("Number is not an integer string");
423 return FAILURE;
424 }
425
426 #define DO_BINARY_UI_OP_EX(op, uop, check_b_zero) \
427 gmp_zval_binary_ui_op( \
428 result, op1, op2, op, uop, check_b_zero, /* is_operator */ true); \
429 if (UNEXPECTED(EG(exception))) { return FAILURE; } \
430 return SUCCESS;
431
432 #define DO_BINARY_UI_OP(op) DO_BINARY_UI_OP_EX(op, op ## _ui, 0)
433 #define DO_BINARY_OP(op) DO_BINARY_UI_OP_EX(op, NULL, 0)
434
435 #define DO_UNARY_OP(op) \
436 gmp_zval_unary_op(result, op1, op); \
437 if (UNEXPECTED(EG(exception))) { \
438 return FAILURE; \
439 } \
440 return SUCCESS;
441
gmp_do_operation_ex(uint8_t opcode,zval * result,zval * op1,zval * op2)442 static zend_result gmp_do_operation_ex(uint8_t opcode, zval *result, zval *op1, zval *op2) /* {{{ */
443 {
444 switch (opcode) {
445 case ZEND_ADD:
446 DO_BINARY_UI_OP(mpz_add);
447 case ZEND_SUB:
448 DO_BINARY_UI_OP(mpz_sub);
449 case ZEND_MUL:
450 DO_BINARY_UI_OP(mpz_mul);
451 case ZEND_POW:
452 return shift_operator_helper(mpz_pow_ui, result, op1, op2, opcode);
453 case ZEND_DIV:
454 DO_BINARY_UI_OP_EX(mpz_tdiv_q, gmp_mpz_tdiv_q_ui, 1);
455 case ZEND_MOD:
456 DO_BINARY_UI_OP_EX(mpz_mod, gmp_mpz_mod_ui, 1);
457 case ZEND_SL:
458 return shift_operator_helper(mpz_mul_2exp, result, op1, op2, opcode);
459 case ZEND_SR:
460 return shift_operator_helper(mpz_fdiv_q_2exp, result, op1, op2, opcode);
461 case ZEND_BW_OR:
462 DO_BINARY_OP(mpz_ior);
463 case ZEND_BW_AND:
464 DO_BINARY_OP(mpz_and);
465 case ZEND_BW_XOR:
466 DO_BINARY_OP(mpz_xor);
467 case ZEND_BW_NOT:
468 DO_UNARY_OP(mpz_com);
469
470 default:
471 return FAILURE;
472 }
473 }
474 /* }}} */
475
gmp_do_operation(uint8_t opcode,zval * result,zval * op1,zval * op2)476 static zend_result gmp_do_operation(uint8_t opcode, zval *result, zval *op1, zval *op2) /* {{{ */
477 {
478 zval op1_copy;
479
480 if (result == op1) {
481 ZVAL_COPY_VALUE(&op1_copy, op1);
482 op1 = &op1_copy;
483 }
484
485 zend_result retval = gmp_do_operation_ex(opcode, result, op1, op2);
486
487 if (retval == SUCCESS && op1 == &op1_copy) {
488 zval_ptr_dtor(op1);
489 }
490
491 return retval;
492 }
493 /* }}} */
494
gmp_compare(zval * op1,zval * op2)495 static int gmp_compare(zval *op1, zval *op2) /* {{{ */
496 {
497 zval result;
498
499 gmp_cmp(&result, op1, op2, /* is_operator */ true);
500
501 /* An error/exception occurs if one of the operands is not a numeric string
502 * or an object which is different from GMP */
503 if (EG(exception)) {
504 return 1;
505 }
506 /* result can only be a zend_long if gmp_cmp hasn't thrown an Error */
507 ZEND_ASSERT(Z_TYPE(result) == IS_LONG);
508 return Z_LVAL(result);
509 }
510 /* }}} */
511
gmp_serialize(zval * object,unsigned char ** buffer,size_t * buf_len,zend_serialize_data * data)512 static int gmp_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data) /* {{{ */
513 {
514 mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(object);
515 smart_str buf = {0};
516 zval zv;
517 php_serialize_data_t serialize_data;
518
519 PHP_VAR_SERIALIZE_INIT(serialize_data);
520
521 gmp_strval(&zv, gmpnum, 10);
522 php_var_serialize(&buf, &zv, &serialize_data);
523 zval_ptr_dtor_str(&zv);
524
525 ZVAL_ARR(&zv, zend_std_get_properties(Z_OBJ_P(object)));
526 php_var_serialize(&buf, &zv, &serialize_data);
527
528 PHP_VAR_SERIALIZE_DESTROY(serialize_data);
529 *buffer = (unsigned char *) estrndup(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
530 *buf_len = ZSTR_LEN(buf.s);
531 zend_string_release_ex(buf.s, 0);
532
533 return SUCCESS;
534 }
535 /* }}} */
536
gmp_unserialize(zval * object,zend_class_entry * ce,const unsigned char * buf,size_t buf_len,zend_unserialize_data * data)537 static int gmp_unserialize(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data) /* {{{ */
538 {
539 mpz_ptr gmpnum;
540 const unsigned char *p, *max;
541 zval *zv;
542 int retval = FAILURE;
543 php_unserialize_data_t unserialize_data;
544 zend_object *zobj;
545
546 PHP_VAR_UNSERIALIZE_INIT(unserialize_data);
547 gmp_create(object, &gmpnum);
548
549 zobj = Z_OBJ_P(object);
550
551 p = buf;
552 max = buf + buf_len;
553
554 zv = var_tmp_var(&unserialize_data);
555 if (!php_var_unserialize(zv, &p, max, &unserialize_data)
556 || Z_TYPE_P(zv) != IS_STRING
557 || convert_zstr_to_gmp(gmpnum, Z_STR_P(zv), 10, 0) == FAILURE
558 ) {
559 zend_throw_exception(NULL, "Could not unserialize number", 0);
560 goto exit;
561 }
562
563 zv = var_tmp_var(&unserialize_data);
564 if (!php_var_unserialize(zv, &p, max, &unserialize_data)
565 || Z_TYPE_P(zv) != IS_ARRAY
566 ) {
567 zend_throw_exception(NULL, "Could not unserialize properties", 0);
568 goto exit;
569 }
570
571 if (zend_hash_num_elements(Z_ARRVAL_P(zv)) != 0) {
572 zend_hash_copy(
573 zend_std_get_properties(zobj), Z_ARRVAL_P(zv),
574 (copy_ctor_func_t) zval_add_ref
575 );
576 }
577
578 retval = SUCCESS;
579 exit:
580 PHP_VAR_UNSERIALIZE_DESTROY(unserialize_data);
581 return retval;
582 }
583 /* }}} */
584
585 /* {{{ ZEND_GINIT_FUNCTION */
ZEND_GINIT_FUNCTION(gmp)586 static ZEND_GINIT_FUNCTION(gmp)
587 {
588 #if defined(COMPILE_DL_GMP) && defined(ZTS)
589 ZEND_TSRMLS_CACHE_UPDATE();
590 #endif
591 gmp_globals->rand_initialized = 0;
592 }
593 /* }}} */
594
595 /* {{{ ZEND_MINIT_FUNCTION */
ZEND_MINIT_FUNCTION(gmp)596 ZEND_MINIT_FUNCTION(gmp)
597 {
598 gmp_ce = register_class_GMP();
599 gmp_ce->create_object = gmp_create_object;
600 gmp_ce->default_object_handlers = &gmp_object_handlers;
601 gmp_ce->serialize = gmp_serialize;
602 gmp_ce->unserialize = gmp_unserialize;
603
604 memcpy(&gmp_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
605 gmp_object_handlers.offset = XtOffsetOf(gmp_object, std);
606 gmp_object_handlers.free_obj = gmp_free_object_storage;
607 gmp_object_handlers.cast_object = gmp_cast_object;
608 gmp_object_handlers.get_debug_info = gmp_get_debug_info;
609 gmp_object_handlers.clone_obj = gmp_clone_obj;
610 gmp_object_handlers.do_operation = gmp_do_operation;
611 gmp_object_handlers.compare = gmp_compare;
612
613 register_gmp_symbols(module_number);
614
615 return SUCCESS;
616 }
617 /* }}} */
618
619 /* {{{ ZEND_RSHUTDOWN_FUNCTION */
ZEND_MODULE_DEACTIVATE_D(gmp)620 ZEND_MODULE_DEACTIVATE_D(gmp)
621 {
622 if (GMPG(rand_initialized)) {
623 gmp_randclear(GMPG(rand_state));
624 GMPG(rand_initialized) = 0;
625 }
626
627 return SUCCESS;
628 }
629 /* }}} */
630
631 /* {{{ ZEND_MINFO_FUNCTION */
ZEND_MODULE_INFO_D(gmp)632 ZEND_MODULE_INFO_D(gmp)
633 {
634 php_info_print_table_start();
635 php_info_print_table_row(2, "gmp support", "enabled");
636 #ifdef mpir_version
637 php_info_print_table_row(2, "MPIR version", mpir_version);
638 #else
639 php_info_print_table_row(2, "GMP version", gmp_version);
640 #endif
641 php_info_print_table_end();
642 }
643 /* }}} */
644
convert_zstr_to_gmp(mpz_t gmp_number,const zend_string * val,zend_long base,uint32_t arg_pos)645 static zend_result convert_zstr_to_gmp(mpz_t gmp_number, const zend_string *val, zend_long base, uint32_t arg_pos)
646 {
647 const char *num_str = ZSTR_VAL(val);
648 bool skip_lead = false;
649
650 size_t num_len = ZSTR_LEN(val);
651 while (isspace(*num_str)) {
652 ++num_str;
653 --num_len;
654 }
655
656 if (num_len >= 2 && num_str[0] == '0') {
657 if ((base == 0 || base == 16) && (num_str[1] == 'x' || num_str[1] == 'X')) {
658 base = 16;
659 skip_lead = true;
660 } else if ((base == 0 || base == 8) && (num_str[1] == 'o' || num_str[1] == 'O')) {
661 base = 8;
662 skip_lead = true;
663 } else if ((base == 0 || base == 2) && (num_str[1] == 'b' || num_str[1] == 'B')) {
664 base = 2;
665 skip_lead = true;
666 }
667 }
668
669 int gmp_ret = mpz_set_str(gmp_number, (skip_lead ? &num_str[2] : num_str), (int) base);
670 if (-1 == gmp_ret) {
671 if (arg_pos == 0) {
672 zend_value_error("Number is not an integer string");
673 } else {
674 zend_argument_value_error(arg_pos, "is not an integer string");
675 }
676 return FAILURE;
677 }
678
679 return SUCCESS;
680 }
681
682 /* {{{ convert_to_gmp
683 * Convert zval to be gmp number */
convert_to_gmp(mpz_t gmpnumber,zval * val,zend_long base,uint32_t arg_pos)684 static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, uint32_t arg_pos)
685 {
686 switch (Z_TYPE_P(val)) {
687 case IS_LONG:
688 mpz_set_si(gmpnumber, Z_LVAL_P(val));
689 return SUCCESS;
690 case IS_STRING: {
691 return convert_zstr_to_gmp(gmpnumber, Z_STR_P(val), base, arg_pos);
692 }
693 case IS_NULL:
694 /* Just reject null for operator overloading */
695 if (arg_pos == 0) {
696 zend_type_error("Number must be of type GMP|string|int, %s given", zend_zval_type_name(val));
697 return FAILURE;
698 }
699 ZEND_FALLTHROUGH;
700 default: {
701 zend_long lval;
702 if (!zend_parse_arg_long_slow(val, &lval, arg_pos)) {
703 if (arg_pos == 0) {
704 zend_type_error(
705 "Number must be of type GMP|string|int, %s given", zend_zval_value_name(val));
706 } else {
707 zend_argument_type_error(arg_pos,
708 "must be of type GMP|string|int, %s given", zend_zval_value_name(val));
709 }
710 return FAILURE;
711 }
712
713 mpz_set_si(gmpnumber, lval);
714 return SUCCESS;
715 }
716 }
717 }
718 /* }}} */
719
gmp_strval(zval * result,mpz_t gmpnum,int base)720 static void gmp_strval(zval *result, mpz_t gmpnum, int base) /* {{{ */
721 {
722 size_t num_len;
723 zend_string *str;
724
725 num_len = mpz_sizeinbase(gmpnum, abs(base));
726 if (mpz_sgn(gmpnum) < 0) {
727 num_len++;
728 }
729
730 str = zend_string_alloc(num_len, 0);
731 mpz_get_str(ZSTR_VAL(str), base, gmpnum);
732
733 /*
734 * From GMP documentation for mpz_sizeinbase():
735 * The returned value will be exact or 1 too big. If base is a power of
736 * 2, the returned value will always be exact.
737 *
738 * So let's check to see if we already have a \0 byte...
739 */
740
741 if (ZSTR_VAL(str)[ZSTR_LEN(str) - 1] == '\0') {
742 ZSTR_LEN(str)--;
743 } else {
744 ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
745 }
746
747 ZVAL_NEW_STR(result, str);
748 }
749 /* }}} */
750
gmp_cmp(zval * return_value,zval * a_arg,zval * b_arg,bool is_operator)751 static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg, bool is_operator) /* {{{ */
752 {
753 mpz_ptr gmpnum_a, gmpnum_b;
754 gmp_temp_t temp_a, temp_b;
755 bool use_si = 0;
756 zend_long res;
757
758 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1);
759
760 if (Z_TYPE_P(b_arg) == IS_LONG) {
761 use_si = 1;
762 temp_b.is_used = 0;
763 } else {
764 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2);
765 }
766
767 if (use_si) {
768 res = mpz_cmp_si(gmpnum_a, Z_LVAL_P(b_arg));
769 } else {
770 res = mpz_cmp(gmpnum_a, gmpnum_b);
771 }
772
773 FREE_GMP_TEMP(temp_a);
774 FREE_GMP_TEMP(temp_b);
775
776 RETURN_LONG(res);
777 }
778 /* }}} */
779
780 /* {{{ gmp_zval_binary_ui_op
781 Execute GMP binary operation.
782 */
gmp_zval_binary_ui_op(zval * return_value,zval * a_arg,zval * b_arg,gmp_binary_op_t gmp_op,gmp_binary_ui_op_t gmp_ui_op,bool check_b_zero,bool is_operator)783 static inline void gmp_zval_binary_ui_op(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, bool check_b_zero, bool is_operator)
784 {
785 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
786 gmp_temp_t temp_a, temp_b;
787
788 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, is_operator ? 0 : 1);
789
790 if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
791 gmpnum_b = NULL;
792 temp_b.is_used = 0;
793 } else {
794 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, is_operator ? 0 : 2);
795 }
796
797 if (check_b_zero) {
798 int b_is_zero = 0;
799 if (!gmpnum_b) {
800 b_is_zero = (Z_LVAL_P(b_arg) == 0);
801 } else {
802 b_is_zero = !mpz_cmp_ui(gmpnum_b, 0);
803 }
804
805 if (b_is_zero) {
806 if ((gmp_binary_op_t) mpz_mod == gmp_op) {
807 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
808 } else {
809 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
810 }
811 FREE_GMP_TEMP(temp_a);
812 FREE_GMP_TEMP(temp_b);
813 RETURN_THROWS();
814 }
815 }
816
817 INIT_GMP_RETVAL(gmpnum_result);
818
819 if (!gmpnum_b) {
820 gmp_ui_op(gmpnum_result, gmpnum_a, (gmp_ulong) Z_LVAL_P(b_arg));
821 } else {
822 gmp_op(gmpnum_result, gmpnum_a, gmpnum_b);
823 }
824
825 FREE_GMP_TEMP(temp_a);
826 FREE_GMP_TEMP(temp_b);
827 }
828 /* }}} */
829
830 /* {{{ gmp_zval_binary_ui_op2
831 Execute GMP binary operation which returns 2 values.
832 */
gmp_zval_binary_ui_op2(zval * return_value,zval * a_arg,zval * b_arg,gmp_binary_op2_t gmp_op,gmp_binary_ui_op2_t gmp_ui_op,int check_b_zero)833 static inline void gmp_zval_binary_ui_op2(zval *return_value, zval *a_arg, zval *b_arg, gmp_binary_op2_t gmp_op, gmp_binary_ui_op2_t gmp_ui_op, int check_b_zero)
834 {
835 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result1, gmpnum_result2;
836 gmp_temp_t temp_a, temp_b;
837 zval result1, result2;
838
839 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
840
841 if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
842 gmpnum_b = NULL;
843 temp_b.is_used = 0;
844 } else {
845 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
846 }
847
848 if (check_b_zero) {
849 int b_is_zero = 0;
850 if (!gmpnum_b) {
851 b_is_zero = (Z_LVAL_P(b_arg) == 0);
852 } else {
853 b_is_zero = !mpz_cmp_ui(gmpnum_b, 0);
854 }
855
856 if (b_is_zero) {
857 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
858 FREE_GMP_TEMP(temp_a);
859 FREE_GMP_TEMP(temp_b);
860 RETURN_THROWS();
861 }
862 }
863
864 gmp_create(&result1, &gmpnum_result1);
865 gmp_create(&result2, &gmpnum_result2);
866
867 array_init(return_value);
868 add_next_index_zval(return_value, &result1);
869 add_next_index_zval(return_value, &result2);
870
871 if (!gmpnum_b) {
872 gmp_ui_op(gmpnum_result1, gmpnum_result2, gmpnum_a, (gmp_ulong) Z_LVAL_P(b_arg));
873 } else {
874 gmp_op(gmpnum_result1, gmpnum_result2, gmpnum_a, gmpnum_b);
875 }
876
877 FREE_GMP_TEMP(temp_a);
878 FREE_GMP_TEMP(temp_b);
879 }
880 /* }}} */
881
882 /* {{{ _gmp_binary_ui_op */
_gmp_binary_ui_op(INTERNAL_FUNCTION_PARAMETERS,gmp_binary_op_t gmp_op,gmp_binary_ui_op_t gmp_ui_op,int check_b_zero)883 static inline void _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAMETERS, gmp_binary_op_t gmp_op, gmp_binary_ui_op_t gmp_ui_op, int check_b_zero)
884 {
885 zval *a_arg, *b_arg;
886
887 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
888 RETURN_THROWS();
889 }
890
891 gmp_zval_binary_ui_op(
892 return_value, a_arg, b_arg, gmp_op, gmp_ui_op, check_b_zero, /* is_operator */ false);
893 }
894 /* }}} */
895
896 /* Unary operations */
897
898 /* {{{ gmp_zval_unary_op */
gmp_zval_unary_op(zval * return_value,zval * a_arg,gmp_unary_op_t gmp_op)899 static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op)
900 {
901 mpz_ptr gmpnum_a, gmpnum_result;
902 gmp_temp_t temp_a;
903
904 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
905
906 INIT_GMP_RETVAL(gmpnum_result);
907 gmp_op(gmpnum_result, gmpnum_a);
908
909 FREE_GMP_TEMP(temp_a);
910 }
911 /* }}} */
912
913 /* {{{ _gmp_unary_op */
_gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS,gmp_unary_op_t gmp_op)914 static inline void _gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_op_t gmp_op)
915 {
916 zval *a_arg;
917
918 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
919 RETURN_THROWS();
920 }
921
922 gmp_zval_unary_op(return_value, a_arg, gmp_op);
923 }
924 /* }}} */
925
926 /* {{{ _gmp_unary_opl */
_gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS,gmp_unary_opl_t gmp_op)927 static inline void _gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_opl_t gmp_op)
928 {
929 zval *a_arg;
930 mpz_ptr gmpnum_a;
931 gmp_temp_t temp_a;
932
933 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
934 RETURN_THROWS();
935 }
936
937 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
938 RETVAL_LONG(gmp_op(gmpnum_a));
939 FREE_GMP_TEMP(temp_a);
940 }
941 /* }}} */
942
gmp_verify_base(zend_long base,uint32_t arg_num)943 static bool gmp_verify_base(zend_long base, uint32_t arg_num)
944 {
945 if (base && (base < 2 || base > GMP_MAX_BASE)) {
946 zend_argument_value_error(arg_num, "must be 0 or between 2 and %d", GMP_MAX_BASE);
947 return false;
948 }
949
950 return true;
951 }
952
gmp_initialize_number(mpz_ptr gmp_number,const zend_string * arg_str,zend_long arg_l,zend_long base)953 static zend_result gmp_initialize_number(mpz_ptr gmp_number, const zend_string *arg_str, zend_long arg_l, zend_long base)
954 {
955 if (arg_str) {
956 return convert_zstr_to_gmp(gmp_number, arg_str, base, 1);
957 }
958
959 mpz_set_si(gmp_number, arg_l);
960 return SUCCESS;
961 }
962
963 /* {{{ Initializes GMP number */
ZEND_FUNCTION(gmp_init)964 ZEND_FUNCTION(gmp_init)
965 {
966 mpz_ptr gmp_number;
967 zend_string *arg_str = NULL;
968 zend_long arg_l = 0;
969 zend_long base = 0;
970
971 ZEND_PARSE_PARAMETERS_START(1, 2)
972 Z_PARAM_STR_OR_LONG(arg_str, arg_l)
973 Z_PARAM_OPTIONAL
974 Z_PARAM_LONG(base)
975 ZEND_PARSE_PARAMETERS_END();
976
977 if (!gmp_verify_base(base, 2)) {
978 RETURN_THROWS();
979 }
980
981 INIT_GMP_RETVAL(gmp_number);
982
983 if (gmp_initialize_number(gmp_number, arg_str, arg_l, base) == FAILURE) {
984 RETURN_THROWS();
985 }
986 }
987 /* }}} */
988
gmp_import_export_validate(zend_long size,zend_long options,int * order,int * endian)989 static bool gmp_import_export_validate(zend_long size, zend_long options, int *order, int *endian)
990 {
991 if (size < 1) {
992 /* size argument is in second position */
993 zend_argument_value_error(2, "must be greater than or equal to 1");
994 return false;
995 }
996
997 switch (options & (GMP_LSW_FIRST | GMP_MSW_FIRST)) {
998 case GMP_LSW_FIRST:
999 *order = -1;
1000 break;
1001 case GMP_MSW_FIRST:
1002 case 0: /* default */
1003 *order = 1;
1004 break;
1005 default:
1006 /* options argument is in third position */
1007 zend_argument_value_error(3, "cannot use multiple word order options");
1008 return false;
1009 }
1010
1011 switch (options & (GMP_LITTLE_ENDIAN | GMP_BIG_ENDIAN | GMP_NATIVE_ENDIAN)) {
1012 case GMP_LITTLE_ENDIAN:
1013 *endian = -1;
1014 break;
1015 case GMP_BIG_ENDIAN:
1016 *endian = 1;
1017 break;
1018 case GMP_NATIVE_ENDIAN:
1019 case 0: /* default */
1020 *endian = 0;
1021 break;
1022 default:
1023 /* options argument is in third position */
1024 zend_argument_value_error(3, "cannot use multiple endian options");
1025 return false;
1026 }
1027
1028 return true;
1029 }
1030
1031 /* {{{ Imports a GMP number from a binary string */
ZEND_FUNCTION(gmp_import)1032 ZEND_FUNCTION(gmp_import)
1033 {
1034 char *data;
1035 size_t data_len;
1036 zend_long size = 1;
1037 zend_long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;
1038 int order, endian;
1039 mpz_ptr gmpnumber;
1040
1041 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &data, &data_len, &size, &options) == FAILURE) {
1042 RETURN_THROWS();
1043 }
1044
1045 if (!gmp_import_export_validate(size, options, &order, &endian)) {
1046 RETURN_THROWS();
1047 }
1048
1049 if ((data_len % size) != 0) {
1050 zend_argument_value_error(1, "must be a multiple of argument #2 ($word_size)");
1051 RETURN_THROWS();
1052 }
1053
1054 INIT_GMP_RETVAL(gmpnumber);
1055
1056 mpz_import(gmpnumber, data_len / size, order, size, endian, 0, data);
1057 }
1058 /* }}} */
1059
1060 /* {{{ Exports a GMP number to a binary string */
ZEND_FUNCTION(gmp_export)1061 ZEND_FUNCTION(gmp_export)
1062 {
1063 zval *gmpnumber_arg;
1064 zend_long size = 1;
1065 zend_long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;
1066 int order, endian;
1067 mpz_ptr gmpnumber;
1068 gmp_temp_t temp_a;
1069
1070 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|ll", &gmpnumber_arg, &size, &options) == FAILURE) {
1071 RETURN_THROWS();
1072 }
1073
1074 if (!gmp_import_export_validate(size, options, &order, &endian)) {
1075 RETURN_THROWS();
1076 }
1077
1078 FETCH_GMP_ZVAL(gmpnumber, gmpnumber_arg, temp_a, 1);
1079
1080 if (mpz_sgn(gmpnumber) == 0) {
1081 RETVAL_EMPTY_STRING();
1082 } else {
1083 ZEND_ASSERT(size > 0);
1084 size_t size_in_base_2 = mpz_sizeinbase(gmpnumber, 2);
1085 if (size > ZEND_LONG_MAX / 4 || size_in_base_2 > SIZE_MAX - (size_t) size * 8 + 1) {
1086 zend_argument_value_error(2, "is too large for argument #1 ($num)");
1087 RETURN_THROWS();
1088 }
1089 size_t bits_per_word = (size_t) size * 8;
1090 size_t count = (size_in_base_2 + bits_per_word - 1) / bits_per_word;
1091
1092 zend_string *out_string = zend_string_safe_alloc(count, size, 0, 0);
1093 mpz_export(ZSTR_VAL(out_string), NULL, order, size, endian, 0, gmpnumber);
1094 ZSTR_VAL(out_string)[ZSTR_LEN(out_string)] = '\0';
1095
1096 RETVAL_NEW_STR(out_string);
1097 }
1098
1099 FREE_GMP_TEMP(temp_a);
1100 }
1101 /* }}} */
1102
1103 /* {{{ Gets signed long value of GMP number */
ZEND_FUNCTION(gmp_intval)1104 ZEND_FUNCTION(gmp_intval)
1105 {
1106 zval *gmpnumber_arg;
1107 mpz_ptr gmpnum;
1108 gmp_temp_t temp_a;
1109
1110 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &gmpnumber_arg) == FAILURE){
1111 RETURN_THROWS();
1112 }
1113
1114 FETCH_GMP_ZVAL(gmpnum, gmpnumber_arg, temp_a, 1);
1115 RETVAL_LONG(mpz_get_si(gmpnum));
1116 FREE_GMP_TEMP(temp_a);
1117 }
1118 /* }}} */
1119
1120 /* {{{ Gets string representation of GMP number */
ZEND_FUNCTION(gmp_strval)1121 ZEND_FUNCTION(gmp_strval)
1122 {
1123 zval *gmpnumber_arg;
1124 zend_long base = 10;
1125 mpz_ptr gmpnum;
1126 gmp_temp_t temp_a;
1127
1128 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &gmpnumber_arg, &base) == FAILURE) {
1129 RETURN_THROWS();
1130 }
1131
1132 /* Although the maximum base in general in GMP is 62, mpz_get_str()
1133 * is explicitly limited to -36 when dealing with negative bases. */
1134 if ((base < 2 && base > -2) || base > GMP_MAX_BASE || base < -36) {
1135 zend_argument_value_error(2, "must be between 2 and %d, or -2 and -36", GMP_MAX_BASE);
1136 RETURN_THROWS();
1137 }
1138
1139 FETCH_GMP_ZVAL(gmpnum, gmpnumber_arg, temp_a, 1);
1140
1141 gmp_strval(return_value, gmpnum, (int)base);
1142
1143 FREE_GMP_TEMP(temp_a);
1144 }
1145 /* }}} */
1146
1147 /* {{{ Add a and b */
ZEND_FUNCTION(gmp_add)1148 ZEND_FUNCTION(gmp_add)
1149 {
1150 gmp_binary_ui_op(mpz_add, mpz_add_ui);
1151 }
1152 /* }}} */
1153
1154 /* {{{ Subtract b from a */
ZEND_FUNCTION(gmp_sub)1155 ZEND_FUNCTION(gmp_sub)
1156 {
1157 gmp_binary_ui_op(mpz_sub, mpz_sub_ui);
1158 }
1159 /* }}} */
1160
1161 /* {{{ Multiply a and b */
ZEND_FUNCTION(gmp_mul)1162 ZEND_FUNCTION(gmp_mul)
1163 {
1164 gmp_binary_ui_op(mpz_mul, mpz_mul_ui);
1165 }
1166 /* }}} */
1167
1168 /* {{{ Divide a by b, returns quotient and reminder */
ZEND_FUNCTION(gmp_div_qr)1169 ZEND_FUNCTION(gmp_div_qr)
1170 {
1171 zval *a_arg, *b_arg;
1172 zend_long round = GMP_ROUND_ZERO;
1173
1174 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1175 RETURN_THROWS();
1176 }
1177
1178 switch (round) {
1179 case GMP_ROUND_ZERO:
1180 gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_tdiv_qr, mpz_tdiv_qr_ui, 1);
1181 break;
1182 case GMP_ROUND_PLUSINF:
1183 gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_cdiv_qr, mpz_cdiv_qr_ui, 1);
1184 break;
1185 case GMP_ROUND_MINUSINF:
1186 gmp_zval_binary_ui_op2(return_value, a_arg, b_arg, mpz_fdiv_qr, mpz_fdiv_qr_ui, 1);
1187 break;
1188 default:
1189 zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
1190 RETURN_THROWS();
1191 }
1192 }
1193 /* }}} */
1194
1195 /* {{{ Divide a by b, returns reminder only */
ZEND_FUNCTION(gmp_div_r)1196 ZEND_FUNCTION(gmp_div_r)
1197 {
1198 zval *a_arg, *b_arg;
1199 zend_long round = GMP_ROUND_ZERO;
1200
1201 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1202 RETURN_THROWS();
1203 }
1204
1205 switch (round) {
1206 case GMP_ROUND_ZERO:
1207 gmp_zval_binary_ui_op(
1208 return_value, a_arg, b_arg, mpz_tdiv_r, gmp_mpz_tdiv_r_ui, 1, /* is_operator */ false);
1209 break;
1210 case GMP_ROUND_PLUSINF:
1211 gmp_zval_binary_ui_op(
1212 return_value, a_arg, b_arg, mpz_cdiv_r, gmp_mpz_cdiv_r_ui, 1, /* is_operator */ false);
1213 break;
1214 case GMP_ROUND_MINUSINF:
1215 gmp_zval_binary_ui_op(
1216 return_value, a_arg, b_arg, mpz_fdiv_r, gmp_mpz_fdiv_r_ui, 1, /* is_operator */ false);
1217 break;
1218 default:
1219 zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
1220 RETURN_THROWS();
1221 }
1222 }
1223 /* }}} */
1224
1225 /* {{{ Divide a by b, returns quotient only */
ZEND_FUNCTION(gmp_div_q)1226 ZEND_FUNCTION(gmp_div_q)
1227 {
1228 zval *a_arg, *b_arg;
1229 zend_long round = GMP_ROUND_ZERO;
1230
1231 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|l", &a_arg, &b_arg, &round) == FAILURE) {
1232 RETURN_THROWS();
1233 }
1234
1235 switch (round) {
1236 case GMP_ROUND_ZERO:
1237 gmp_zval_binary_ui_op(
1238 return_value, a_arg, b_arg, mpz_tdiv_q, gmp_mpz_tdiv_q_ui, 1, /* is_operator */ false);
1239 break;
1240 case GMP_ROUND_PLUSINF:
1241 gmp_zval_binary_ui_op(
1242 return_value, a_arg, b_arg, mpz_cdiv_q, gmp_mpz_cdiv_q_ui, 1, /* is_operator */ false);
1243 break;
1244 case GMP_ROUND_MINUSINF:
1245 gmp_zval_binary_ui_op(
1246 return_value, a_arg, b_arg, mpz_fdiv_q, gmp_mpz_fdiv_q_ui, 1, /* is_operator */ false);
1247 break;
1248 default:
1249 zend_argument_value_error(3, "must be one of GMP_ROUND_ZERO, GMP_ROUND_PLUSINF, or GMP_ROUND_MINUSINF");
1250 RETURN_THROWS();
1251 }
1252
1253 }
1254 /* }}} */
1255
1256 /* {{{ Computes a modulo b */
ZEND_FUNCTION(gmp_mod)1257 ZEND_FUNCTION(gmp_mod)
1258 {
1259 gmp_binary_ui_op_no_zero(mpz_mod, gmp_mpz_mod_ui);
1260 }
1261 /* }}} */
1262
1263 /* {{{ Divide a by b using exact division algorithm */
ZEND_FUNCTION(gmp_divexact)1264 ZEND_FUNCTION(gmp_divexact)
1265 {
1266 gmp_binary_ui_op_no_zero(mpz_divexact, NULL);
1267 }
1268 /* }}} */
1269
1270 /* {{{ Negates a number */
ZEND_FUNCTION(gmp_neg)1271 ZEND_FUNCTION(gmp_neg)
1272 {
1273 gmp_unary_op(mpz_neg);
1274 }
1275 /* }}} */
1276
1277 /* {{{ Calculates absolute value */
ZEND_FUNCTION(gmp_abs)1278 ZEND_FUNCTION(gmp_abs)
1279 {
1280 gmp_unary_op(mpz_abs);
1281 }
1282 /* }}} */
1283
1284 /* {{{ Calculates factorial function */
ZEND_FUNCTION(gmp_fact)1285 ZEND_FUNCTION(gmp_fact)
1286 {
1287 zval *a_arg;
1288 mpz_ptr gmpnum_result;
1289
1290 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1291 RETURN_THROWS();
1292 }
1293
1294 if (Z_TYPE_P(a_arg) == IS_LONG) {
1295 if (Z_LVAL_P(a_arg) < 0) {
1296 zend_argument_value_error(1, "must be greater than or equal to 0");
1297 RETURN_THROWS();
1298 }
1299 } else {
1300 mpz_ptr gmpnum;
1301 gmp_temp_t temp_a;
1302
1303 FETCH_GMP_ZVAL(gmpnum, a_arg, temp_a, 1);
1304 FREE_GMP_TEMP(temp_a);
1305
1306 if (mpz_sgn(gmpnum) < 0) {
1307 zend_argument_value_error(1, "must be greater than or equal to 0");
1308 RETURN_THROWS();
1309 }
1310 }
1311
1312 INIT_GMP_RETVAL(gmpnum_result);
1313 mpz_fac_ui(gmpnum_result, zval_get_long(a_arg));
1314 }
1315 /* }}} */
1316
1317 /* {{{ Calculates binomial coefficient */
ZEND_FUNCTION(gmp_binomial)1318 ZEND_FUNCTION(gmp_binomial)
1319 {
1320 zval *n_arg;
1321 zend_long k;
1322 mpz_ptr gmpnum_result;
1323
1324 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &n_arg, &k) == FAILURE) {
1325 RETURN_THROWS();
1326 }
1327
1328 if (k < 0) {
1329 zend_argument_value_error(2, "must be greater than or equal to 0");
1330 RETURN_THROWS();
1331 }
1332
1333 INIT_GMP_RETVAL(gmpnum_result);
1334 if (Z_TYPE_P(n_arg) == IS_LONG && Z_LVAL_P(n_arg) >= 0) {
1335 mpz_bin_uiui(gmpnum_result, (gmp_ulong) Z_LVAL_P(n_arg), (gmp_ulong) k);
1336 } else {
1337 mpz_ptr gmpnum_n;
1338 gmp_temp_t temp_n;
1339 FETCH_GMP_ZVAL(gmpnum_n, n_arg, temp_n, 1);
1340 mpz_bin_ui(gmpnum_result, gmpnum_n, (gmp_ulong) k);
1341 FREE_GMP_TEMP(temp_n);
1342 }
1343 }
1344 /* }}} */
1345
1346 /* {{{ Raise base to power exp */
ZEND_FUNCTION(gmp_pow)1347 ZEND_FUNCTION(gmp_pow)
1348 {
1349 zval *base_arg;
1350 mpz_ptr gmpnum_result;
1351 gmp_temp_t temp_base;
1352 zend_long exp;
1353
1354 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &base_arg, &exp) == FAILURE) {
1355 RETURN_THROWS();
1356 }
1357
1358 if (exp < 0) {
1359 zend_argument_value_error(2, "must be greater than or equal to 0");
1360 RETURN_THROWS();
1361 }
1362
1363 double powmax = log((double)ZEND_LONG_MAX);
1364
1365 if (Z_TYPE_P(base_arg) == IS_LONG && Z_LVAL_P(base_arg) >= 0) {
1366 INIT_GMP_RETVAL(gmpnum_result);
1367 if ((log(Z_LVAL_P(base_arg)) * exp) > powmax) {
1368 zend_value_error("base and exponent overflow");
1369 RETURN_THROWS();
1370 }
1371 mpz_ui_pow_ui(gmpnum_result, Z_LVAL_P(base_arg), exp);
1372 } else {
1373 mpz_ptr gmpnum_base;
1374 zend_ulong gmpnum;
1375 FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base, 1);
1376 INIT_GMP_RETVAL(gmpnum_result);
1377 gmpnum = mpz_get_ui(gmpnum_base);
1378 if ((log(gmpnum) * exp) > powmax) {
1379 FREE_GMP_TEMP(temp_base);
1380 zend_value_error("base and exponent overflow");
1381 RETURN_THROWS();
1382 }
1383 mpz_pow_ui(gmpnum_result, gmpnum_base, exp);
1384 FREE_GMP_TEMP(temp_base);
1385 }
1386 }
1387 /* }}} */
1388
1389 /* {{{ Raise base to power exp and take result modulo mod */
ZEND_FUNCTION(gmp_powm)1390 ZEND_FUNCTION(gmp_powm)
1391 {
1392 zval *base_arg, *exp_arg, *mod_arg;
1393 mpz_ptr gmpnum_base, gmpnum_exp, gmpnum_mod, gmpnum_result;
1394 int use_ui = 0;
1395 gmp_temp_t temp_base, temp_exp, temp_mod;
1396
1397 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz", &base_arg, &exp_arg, &mod_arg) == FAILURE){
1398 RETURN_THROWS();
1399 }
1400
1401 FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base, 1);
1402
1403 if (Z_TYPE_P(exp_arg) == IS_LONG && Z_LVAL_P(exp_arg) >= 0) {
1404 use_ui = 1;
1405 temp_exp.is_used = 0;
1406 } else {
1407 FETCH_GMP_ZVAL_DEP(gmpnum_exp, exp_arg, temp_exp, temp_base, 2);
1408 if (mpz_sgn(gmpnum_exp) < 0) {
1409 zend_argument_value_error(2, "must be greater than or equal to 0");
1410 FREE_GMP_TEMP(temp_base);
1411 FREE_GMP_TEMP(temp_exp);
1412 RETURN_THROWS();
1413 }
1414 }
1415 FETCH_GMP_ZVAL_DEP_DEP(gmpnum_mod, mod_arg, temp_mod, temp_exp, temp_base, 3);
1416
1417 if (!mpz_cmp_ui(gmpnum_mod, 0)) {
1418 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
1419 FREE_GMP_TEMP(temp_base);
1420 FREE_GMP_TEMP(temp_exp);
1421 FREE_GMP_TEMP(temp_mod);
1422 RETURN_THROWS();
1423 }
1424
1425 INIT_GMP_RETVAL(gmpnum_result);
1426 if (use_ui) {
1427 mpz_powm_ui(gmpnum_result, gmpnum_base, (zend_ulong) Z_LVAL_P(exp_arg), gmpnum_mod);
1428 } else {
1429 mpz_powm(gmpnum_result, gmpnum_base, gmpnum_exp, gmpnum_mod);
1430 FREE_GMP_TEMP(temp_exp);
1431 }
1432
1433 FREE_GMP_TEMP(temp_base);
1434 FREE_GMP_TEMP(temp_mod);
1435 }
1436 /* }}} */
1437
1438 /* {{{ Takes integer part of square root of a */
ZEND_FUNCTION(gmp_sqrt)1439 ZEND_FUNCTION(gmp_sqrt)
1440 {
1441 zval *a_arg;
1442 mpz_ptr gmpnum_a, gmpnum_result;
1443 gmp_temp_t temp_a;
1444
1445 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1446 RETURN_THROWS();
1447 }
1448
1449 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1450
1451 if (mpz_sgn(gmpnum_a) < 0) {
1452 zend_argument_value_error(1, "must be greater than or equal to 0");
1453 FREE_GMP_TEMP(temp_a);
1454 RETURN_THROWS();
1455 }
1456
1457 INIT_GMP_RETVAL(gmpnum_result);
1458 mpz_sqrt(gmpnum_result, gmpnum_a);
1459 FREE_GMP_TEMP(temp_a);
1460 }
1461 /* }}} */
1462
1463 /* {{{ Square root with remainder */
ZEND_FUNCTION(gmp_sqrtrem)1464 ZEND_FUNCTION(gmp_sqrtrem)
1465 {
1466 zval *a_arg;
1467 mpz_ptr gmpnum_a, gmpnum_result1, gmpnum_result2;
1468 gmp_temp_t temp_a;
1469 zval result1, result2;
1470
1471 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1472 RETURN_THROWS();
1473 }
1474
1475 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1476
1477 if (mpz_sgn(gmpnum_a) < 0) {
1478 zend_argument_value_error(1, "must be greater than or equal to 0");
1479 FREE_GMP_TEMP(temp_a);
1480 RETURN_THROWS();
1481 }
1482
1483 gmp_create(&result1, &gmpnum_result1);
1484 gmp_create(&result2, &gmpnum_result2);
1485
1486 array_init(return_value);
1487 add_next_index_zval(return_value, &result1);
1488 add_next_index_zval(return_value, &result2);
1489
1490 mpz_sqrtrem(gmpnum_result1, gmpnum_result2, gmpnum_a);
1491 FREE_GMP_TEMP(temp_a);
1492 }
1493 /* }}} */
1494
1495 /* {{{ Takes integer part of nth root */
ZEND_FUNCTION(gmp_root)1496 ZEND_FUNCTION(gmp_root)
1497 {
1498 zval *a_arg;
1499 zend_long nth;
1500 mpz_ptr gmpnum_a, gmpnum_result;
1501 gmp_temp_t temp_a;
1502
1503 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &nth) == FAILURE) {
1504 RETURN_THROWS();
1505 }
1506
1507 if (nth <= 0) {
1508 zend_argument_value_error(2, "must be greater than 0");
1509 RETURN_THROWS();
1510 }
1511
1512 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1513
1514 if (nth % 2 == 0 && mpz_sgn(gmpnum_a) < 0) {
1515 zend_argument_value_error(2, "must be odd if argument #1 ($a) is negative");
1516 FREE_GMP_TEMP(temp_a);
1517 RETURN_THROWS();
1518 }
1519
1520 INIT_GMP_RETVAL(gmpnum_result);
1521 mpz_root(gmpnum_result, gmpnum_a, (gmp_ulong) nth);
1522 FREE_GMP_TEMP(temp_a);
1523 }
1524 /* }}} */
1525
1526 /* {{{ Calculates integer part of nth root and remainder */
ZEND_FUNCTION(gmp_rootrem)1527 ZEND_FUNCTION(gmp_rootrem)
1528 {
1529 zval *a_arg;
1530 zend_long nth;
1531 mpz_ptr gmpnum_a, gmpnum_result1, gmpnum_result2;
1532 gmp_temp_t temp_a;
1533 zval result1, result2;
1534
1535 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &nth) == FAILURE) {
1536 RETURN_THROWS();
1537 }
1538
1539 if (nth <= 0) {
1540 zend_argument_value_error(2, "must be greater than or equal to 1");
1541 RETURN_THROWS();
1542 }
1543
1544 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1545
1546 if (nth % 2 == 0 && mpz_sgn(gmpnum_a) < 0) {
1547 zend_argument_value_error(2, "must be odd if argument #1 ($a) is negative");
1548 FREE_GMP_TEMP(temp_a);
1549 RETURN_THROWS();
1550 }
1551
1552 gmp_create(&result1, &gmpnum_result1);
1553 gmp_create(&result2, &gmpnum_result2);
1554
1555 array_init(return_value);
1556 add_next_index_zval(return_value, &result1);
1557 add_next_index_zval(return_value, &result2);
1558
1559 #if GMP_51_OR_NEWER
1560 /* mpz_rootrem() is supported since GMP 4.2, but buggy wrt odd roots
1561 * of negative numbers */
1562 mpz_rootrem(gmpnum_result1, gmpnum_result2, gmpnum_a, (gmp_ulong) nth);
1563 #else
1564 mpz_root(gmpnum_result1, gmpnum_a, (gmp_ulong) nth);
1565 mpz_pow_ui(gmpnum_result2, gmpnum_result1, (gmp_ulong) nth);
1566 mpz_sub(gmpnum_result2, gmpnum_a, gmpnum_result2);
1567 #endif
1568
1569 FREE_GMP_TEMP(temp_a);
1570 }
1571 /* }}} */
1572
1573 /* {{{ Checks if a is an exact square */
ZEND_FUNCTION(gmp_perfect_square)1574 ZEND_FUNCTION(gmp_perfect_square)
1575 {
1576 zval *a_arg;
1577 mpz_ptr gmpnum_a;
1578 gmp_temp_t temp_a;
1579
1580 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1581 RETURN_THROWS();
1582 }
1583
1584 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1585
1586 RETVAL_BOOL((mpz_perfect_square_p(gmpnum_a) != 0));
1587 FREE_GMP_TEMP(temp_a);
1588 }
1589 /* }}} */
1590
1591 /* {{{ Checks if a is a perfect power */
ZEND_FUNCTION(gmp_perfect_power)1592 ZEND_FUNCTION(gmp_perfect_power)
1593 {
1594 zval *a_arg;
1595 mpz_ptr gmpnum_a;
1596 gmp_temp_t temp_a;
1597
1598 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1599 RETURN_THROWS();
1600 }
1601
1602 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1603
1604 RETVAL_BOOL((mpz_perfect_power_p(gmpnum_a) != 0));
1605 FREE_GMP_TEMP(temp_a);
1606 }
1607 /* }}} */
1608
1609 /* {{{ Checks if a is "probably prime" */
ZEND_FUNCTION(gmp_prob_prime)1610 ZEND_FUNCTION(gmp_prob_prime)
1611 {
1612 zval *gmpnumber_arg;
1613 mpz_ptr gmpnum_a;
1614 zend_long reps = 10;
1615 gmp_temp_t temp_a;
1616
1617 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &gmpnumber_arg, &reps) == FAILURE) {
1618 RETURN_THROWS();
1619 }
1620
1621 FETCH_GMP_ZVAL(gmpnum_a, gmpnumber_arg, temp_a, 1);
1622
1623 RETVAL_LONG(mpz_probab_prime_p(gmpnum_a, (int)reps));
1624 FREE_GMP_TEMP(temp_a);
1625 }
1626 /* }}} */
1627
1628 /* {{{ Computes greatest common denominator (gcd) of a and b */
ZEND_FUNCTION(gmp_gcd)1629 ZEND_FUNCTION(gmp_gcd)
1630 {
1631 gmp_binary_ui_op(mpz_gcd, gmp_mpz_gcd_ui);
1632 }
1633 /* }}} */
1634
1635 /* {{{ Computes least common multiple (lcm) of a and b */
ZEND_FUNCTION(gmp_lcm)1636 ZEND_FUNCTION(gmp_lcm)
1637 {
1638 gmp_binary_ui_op(mpz_lcm, mpz_lcm_ui);
1639 }
1640 /* }}} */
1641
1642 /* {{{ Computes G, S, and T, such that AS + BT = G = `gcd' (A, B) */
ZEND_FUNCTION(gmp_gcdext)1643 ZEND_FUNCTION(gmp_gcdext)
1644 {
1645 zval *a_arg, *b_arg;
1646 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_t, gmpnum_s, gmpnum_g;
1647 gmp_temp_t temp_a, temp_b;
1648 zval result_g, result_s, result_t;
1649
1650 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1651 RETURN_THROWS();
1652 }
1653
1654 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1655 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1656
1657 gmp_create(&result_g, &gmpnum_g);
1658 gmp_create(&result_s, &gmpnum_s);
1659 gmp_create(&result_t, &gmpnum_t);
1660
1661 array_init(return_value);
1662 add_assoc_zval(return_value, "g", &result_g);
1663 add_assoc_zval(return_value, "s", &result_s);
1664 add_assoc_zval(return_value, "t", &result_t);
1665
1666 mpz_gcdext(gmpnum_g, gmpnum_s, gmpnum_t, gmpnum_a, gmpnum_b);
1667 FREE_GMP_TEMP(temp_a);
1668 FREE_GMP_TEMP(temp_b);
1669 }
1670 /* }}} */
1671
1672 /* {{{ Computes the inverse of a modulo b */
ZEND_FUNCTION(gmp_invert)1673 ZEND_FUNCTION(gmp_invert)
1674 {
1675 zval *a_arg, *b_arg;
1676 mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
1677 gmp_temp_t temp_a, temp_b;
1678 int res;
1679
1680 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1681 RETURN_THROWS();
1682 }
1683
1684 if (Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) == 0) {
1685 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
1686 RETURN_THROWS();
1687 }
1688
1689 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1690 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1691
1692 if (!mpz_cmp_ui(gmpnum_b, 0)) {
1693 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
1694 FREE_GMP_TEMP(temp_a);
1695 FREE_GMP_TEMP(temp_b);
1696 RETURN_THROWS();
1697 }
1698
1699 INIT_GMP_RETVAL(gmpnum_result);
1700 res = mpz_invert(gmpnum_result, gmpnum_a, gmpnum_b);
1701 FREE_GMP_TEMP(temp_a);
1702 FREE_GMP_TEMP(temp_b);
1703 if (!res) {
1704 zval_ptr_dtor(return_value);
1705 RETURN_FALSE;
1706 }
1707 }
1708 /* }}} */
1709
1710 /* {{{ Computes Jacobi symbol */
ZEND_FUNCTION(gmp_jacobi)1711 ZEND_FUNCTION(gmp_jacobi)
1712 {
1713 zval *a_arg, *b_arg;
1714 mpz_ptr gmpnum_a, gmpnum_b;
1715 gmp_temp_t temp_a, temp_b;
1716
1717 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1718 RETURN_THROWS();
1719 }
1720
1721 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1722 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1723
1724 RETVAL_LONG(mpz_jacobi(gmpnum_a, gmpnum_b));
1725
1726 FREE_GMP_TEMP(temp_a);
1727 FREE_GMP_TEMP(temp_b);
1728 }
1729 /* }}} */
1730
1731 /* {{{ Computes Legendre symbol */
ZEND_FUNCTION(gmp_legendre)1732 ZEND_FUNCTION(gmp_legendre)
1733 {
1734 zval *a_arg, *b_arg;
1735 mpz_ptr gmpnum_a, gmpnum_b;
1736 gmp_temp_t temp_a, temp_b;
1737
1738 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1739 RETURN_THROWS();
1740 }
1741
1742 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1743 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1744
1745 RETVAL_LONG(mpz_legendre(gmpnum_a, gmpnum_b));
1746
1747 FREE_GMP_TEMP(temp_a);
1748 FREE_GMP_TEMP(temp_b);
1749 }
1750 /* }}} */
1751
1752 /* {{{ Computes the Kronecker symbol */
ZEND_FUNCTION(gmp_kronecker)1753 ZEND_FUNCTION(gmp_kronecker)
1754 {
1755 zval *a_arg, *b_arg;
1756 mpz_ptr gmpnum_a, gmpnum_b;
1757 gmp_temp_t temp_a, temp_b;
1758 bool use_a_si = 0, use_b_si = 0;
1759 int result;
1760
1761 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1762 RETURN_THROWS();
1763 }
1764
1765 if (Z_TYPE_P(a_arg) == IS_LONG && Z_TYPE_P(b_arg) != IS_LONG) {
1766 use_a_si = 1;
1767 temp_a.is_used = 0;
1768 } else {
1769 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1770 }
1771
1772 if (Z_TYPE_P(b_arg) == IS_LONG) {
1773 use_b_si = 1;
1774 temp_b.is_used = 0;
1775 } else {
1776 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
1777 }
1778
1779 if (use_a_si) {
1780 ZEND_ASSERT(use_b_si == 0);
1781 result = mpz_si_kronecker((gmp_long) Z_LVAL_P(a_arg), gmpnum_b);
1782 } else if (use_b_si) {
1783 result = mpz_kronecker_si(gmpnum_a, (gmp_long) Z_LVAL_P(b_arg));
1784 } else {
1785 result = mpz_kronecker(gmpnum_a, gmpnum_b);
1786 }
1787
1788 FREE_GMP_TEMP(temp_a);
1789 FREE_GMP_TEMP(temp_b);
1790
1791 RETURN_LONG(result);
1792 }
1793 /* }}} */
1794
1795 /* {{{ Compares two numbers */
ZEND_FUNCTION(gmp_cmp)1796 ZEND_FUNCTION(gmp_cmp)
1797 {
1798 zval *a_arg, *b_arg;
1799
1800 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
1801 RETURN_THROWS();
1802 }
1803
1804 gmp_cmp(return_value, a_arg, b_arg, /* is_operator */ false);
1805 }
1806 /* }}} */
1807
1808 /* {{{ Gets the sign of the number */
ZEND_FUNCTION(gmp_sign)1809 ZEND_FUNCTION(gmp_sign)
1810 {
1811 /* Can't use gmp_unary_opl here, because mpz_sgn is a macro */
1812 zval *a_arg;
1813 mpz_ptr gmpnum_a;
1814 gmp_temp_t temp_a;
1815
1816 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &a_arg) == FAILURE){
1817 RETURN_THROWS();
1818 }
1819
1820 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
1821
1822 RETVAL_LONG(mpz_sgn(gmpnum_a));
1823 FREE_GMP_TEMP(temp_a);
1824 }
1825 /* }}} */
1826
gmp_init_random(void)1827 static void gmp_init_random(void)
1828 {
1829 if (!GMPG(rand_initialized)) {
1830 /* Initialize */
1831 gmp_randinit_mt(GMPG(rand_state));
1832 /* Seed */
1833 unsigned long int seed = 0;
1834 if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
1835 seed = (unsigned long int)php_random_generate_fallback_seed();
1836 }
1837 gmp_randseed_ui(GMPG(rand_state), seed);
1838
1839 GMPG(rand_initialized) = 1;
1840 }
1841 }
1842
1843 /* {{{ Seed the RNG */
ZEND_FUNCTION(gmp_random_seed)1844 ZEND_FUNCTION(gmp_random_seed)
1845 {
1846 zval *seed;
1847 gmp_temp_t temp_a;
1848
1849 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &seed) == FAILURE) {
1850 RETURN_THROWS();
1851 }
1852
1853 gmp_init_random();
1854
1855 if (Z_TYPE_P(seed) == IS_LONG && Z_LVAL_P(seed) >= 0) {
1856 gmp_randseed_ui(GMPG(rand_state), Z_LVAL_P(seed));
1857 }
1858 else {
1859 mpz_ptr gmpnum_seed;
1860
1861 FETCH_GMP_ZVAL(gmpnum_seed, seed, temp_a, 1);
1862
1863 gmp_randseed(GMPG(rand_state), gmpnum_seed);
1864
1865 FREE_GMP_TEMP(temp_a);
1866 }
1867 }
1868 /* }}} */
1869
1870 /* {{{ Gets a random number in the range 0 to (2 ** n) - 1 */
ZEND_FUNCTION(gmp_random_bits)1871 ZEND_FUNCTION(gmp_random_bits)
1872 {
1873 zend_long bits;
1874 mpz_ptr gmpnum_result;
1875
1876 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &bits) == FAILURE) {
1877 RETURN_THROWS();
1878 }
1879
1880 #if SIZEOF_SIZE_T == 4
1881 const zend_long maxbits = ULONG_MAX / GMP_NUMB_BITS;
1882 #else
1883 const zend_long maxbits = INT_MAX;
1884 #endif
1885
1886 if (bits <= 0 || bits > maxbits) {
1887 zend_argument_value_error(1, "must be between 1 and " ZEND_LONG_FMT, maxbits);
1888 RETURN_THROWS();
1889 }
1890
1891 INIT_GMP_RETVAL(gmpnum_result);
1892 gmp_init_random();
1893
1894 mpz_urandomb(gmpnum_result, GMPG(rand_state), (mp_bitcnt_t)bits);
1895 }
1896 /* }}} */
1897
1898 /* {{{ Gets a random number in the range min to max */
ZEND_FUNCTION(gmp_random_range)1899 ZEND_FUNCTION(gmp_random_range)
1900 {
1901 zval *min_arg, *max_arg;
1902 mpz_ptr gmpnum_max, gmpnum_result;
1903 mpz_t gmpnum_range;
1904 gmp_temp_t temp_a, temp_b;
1905
1906 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &min_arg, &max_arg) == FAILURE) {
1907 RETURN_THROWS();
1908 }
1909
1910 gmp_init_random();
1911
1912 FETCH_GMP_ZVAL(gmpnum_max, max_arg, temp_a, 2);
1913
1914 if (Z_TYPE_P(min_arg) == IS_LONG && Z_LVAL_P(min_arg) >= 0) {
1915 if (mpz_cmp_ui(gmpnum_max, Z_LVAL_P(min_arg)) <= 0) {
1916 FREE_GMP_TEMP(temp_a);
1917 zend_argument_value_error(1, "must be less than argument #2 ($maximum)");
1918 RETURN_THROWS();
1919 }
1920
1921 INIT_GMP_RETVAL(gmpnum_result);
1922 mpz_init(gmpnum_range);
1923
1924 if (Z_LVAL_P(min_arg) != 0) {
1925 mpz_sub_ui(gmpnum_range, gmpnum_max, Z_LVAL_P(min_arg) - 1);
1926 } else {
1927 mpz_add_ui(gmpnum_range, gmpnum_max, 1);
1928 }
1929
1930 mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
1931
1932 if (Z_LVAL_P(min_arg) != 0) {
1933 mpz_add_ui(gmpnum_result, gmpnum_result, Z_LVAL_P(min_arg));
1934 }
1935
1936 mpz_clear(gmpnum_range);
1937 FREE_GMP_TEMP(temp_a);
1938 } else {
1939 mpz_ptr gmpnum_min;
1940
1941 FETCH_GMP_ZVAL_DEP(gmpnum_min, min_arg, temp_b, temp_a, 1);
1942
1943 if (mpz_cmp(gmpnum_max, gmpnum_min) <= 0) {
1944 FREE_GMP_TEMP(temp_b);
1945 FREE_GMP_TEMP(temp_a);
1946 zend_argument_value_error(1, "must be less than argument #2 ($maximum)");
1947 RETURN_THROWS();
1948 }
1949
1950 INIT_GMP_RETVAL(gmpnum_result);
1951 mpz_init(gmpnum_range);
1952
1953 mpz_sub(gmpnum_range, gmpnum_max, gmpnum_min);
1954 mpz_add_ui(gmpnum_range, gmpnum_range, 1);
1955 mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range);
1956 mpz_add(gmpnum_result, gmpnum_result, gmpnum_min);
1957
1958 mpz_clear(gmpnum_range);
1959 FREE_GMP_TEMP(temp_b);
1960 FREE_GMP_TEMP(temp_a);
1961 }
1962 }
1963 /* }}} */
1964
1965 /* {{{ Calculates logical AND of a and b */
ZEND_FUNCTION(gmp_and)1966 ZEND_FUNCTION(gmp_and)
1967 {
1968 gmp_binary_op(mpz_and);
1969 }
1970 /* }}} */
1971
1972 /* {{{ Calculates logical OR of a and b */
ZEND_FUNCTION(gmp_or)1973 ZEND_FUNCTION(gmp_or)
1974 {
1975 gmp_binary_op(mpz_ior);
1976 }
1977 /* }}} */
1978
1979 /* {{{ Calculates one's complement of a */
ZEND_FUNCTION(gmp_com)1980 ZEND_FUNCTION(gmp_com)
1981 {
1982 gmp_unary_op(mpz_com);
1983 }
1984 /* }}} */
1985
1986 /* {{{ Finds next prime of a */
ZEND_FUNCTION(gmp_nextprime)1987 ZEND_FUNCTION(gmp_nextprime)
1988 {
1989 gmp_unary_op(mpz_nextprime);
1990 }
1991 /* }}} */
1992
1993 /* {{{ Calculates logical exclusive OR of a and b */
ZEND_FUNCTION(gmp_xor)1994 ZEND_FUNCTION(gmp_xor)
1995 {
1996 gmp_binary_op(mpz_xor);
1997 }
1998 /* }}} */
1999
2000 /* {{{ Sets or clear bit in a */
ZEND_FUNCTION(gmp_setbit)2001 ZEND_FUNCTION(gmp_setbit)
2002 {
2003 zval *a_arg;
2004 zend_long index;
2005 bool set = 1;
2006 mpz_ptr gmpnum_a;
2007
2008 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|b", &a_arg, gmp_ce, &index, &set) == FAILURE) {
2009 RETURN_THROWS();
2010 }
2011
2012 if (index < 0) {
2013 zend_argument_value_error(2, "must be greater than or equal to 0");
2014 RETURN_THROWS();
2015 }
2016 if (index / GMP_NUMB_BITS >= INT_MAX) {
2017 zend_argument_value_error(2, "must be less than %d * %d", INT_MAX, GMP_NUMB_BITS);
2018 RETURN_THROWS();
2019 }
2020
2021 gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
2022
2023 if (set) {
2024 mpz_setbit(gmpnum_a, index);
2025 } else {
2026 mpz_clrbit(gmpnum_a, index);
2027 }
2028 }
2029 /* }}} */
2030
2031 /* {{{ Clears bit in a */
ZEND_FUNCTION(gmp_clrbit)2032 ZEND_FUNCTION(gmp_clrbit)
2033 {
2034 zval *a_arg;
2035 zend_long index;
2036 mpz_ptr gmpnum_a;
2037
2038 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &a_arg, gmp_ce, &index) == FAILURE){
2039 RETURN_THROWS();
2040 }
2041
2042 if (index < 0) {
2043 zend_argument_value_error(2, "must be greater than or equal to 0");
2044 RETURN_THROWS();
2045 }
2046
2047 gmpnum_a = GET_GMP_FROM_ZVAL(a_arg);
2048 mpz_clrbit(gmpnum_a, index);
2049 }
2050 /* }}} */
2051
2052 /* {{{ Tests if bit is set in a */
ZEND_FUNCTION(gmp_testbit)2053 ZEND_FUNCTION(gmp_testbit)
2054 {
2055 zval *a_arg;
2056 zend_long index;
2057 mpz_ptr gmpnum_a;
2058 gmp_temp_t temp_a;
2059
2060 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &index) == FAILURE){
2061 RETURN_THROWS();
2062 }
2063
2064 if (index < 0) {
2065 zend_argument_value_error(2, "must be greater than or equal to 0");
2066 RETURN_THROWS();
2067 }
2068
2069 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
2070 RETVAL_BOOL(mpz_tstbit(gmpnum_a, index));
2071 FREE_GMP_TEMP(temp_a);
2072 }
2073 /* }}} */
2074
2075 /* {{{ Calculates the population count of a */
ZEND_FUNCTION(gmp_popcount)2076 ZEND_FUNCTION(gmp_popcount)
2077 {
2078 gmp_unary_opl(mpz_popcount);
2079 }
2080 /* }}} */
2081
2082 /* {{{ Calculates hamming distance between a and b */
ZEND_FUNCTION(gmp_hamdist)2083 ZEND_FUNCTION(gmp_hamdist)
2084 {
2085 zval *a_arg, *b_arg;
2086 mpz_ptr gmpnum_a, gmpnum_b;
2087 gmp_temp_t temp_a, temp_b;
2088
2089 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a_arg, &b_arg) == FAILURE){
2090 RETURN_THROWS();
2091 }
2092
2093 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
2094 FETCH_GMP_ZVAL_DEP(gmpnum_b, b_arg, temp_b, temp_a, 2);
2095
2096 RETVAL_LONG(mpz_hamdist(gmpnum_a, gmpnum_b));
2097
2098 FREE_GMP_TEMP(temp_a);
2099 FREE_GMP_TEMP(temp_b);
2100 }
2101 /* }}} */
2102
2103 /* {{{ Finds first zero bit */
ZEND_FUNCTION(gmp_scan0)2104 ZEND_FUNCTION(gmp_scan0)
2105 {
2106 zval *a_arg;
2107 mpz_ptr gmpnum_a;
2108 gmp_temp_t temp_a;
2109 zend_long start;
2110
2111 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
2112 RETURN_THROWS();
2113 }
2114
2115 if (start < 0) {
2116 zend_argument_value_error(2, "must be greater than or equal to 0");
2117 RETURN_THROWS();
2118 }
2119
2120 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
2121
2122 RETVAL_LONG(mpz_scan0(gmpnum_a, start));
2123 FREE_GMP_TEMP(temp_a);
2124 }
2125 /* }}} */
2126
2127 /* {{{ Finds first non-zero bit */
ZEND_FUNCTION(gmp_scan1)2128 ZEND_FUNCTION(gmp_scan1)
2129 {
2130 zval *a_arg;
2131 mpz_ptr gmpnum_a;
2132 gmp_temp_t temp_a;
2133 zend_long start;
2134
2135 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &a_arg, &start) == FAILURE){
2136 RETURN_THROWS();
2137 }
2138
2139 if (start < 0) {
2140 zend_argument_value_error(2, "must be greater than or equal to 0");
2141 RETURN_THROWS();
2142 }
2143
2144 FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a, 1);
2145
2146 RETVAL_LONG(mpz_scan1(gmpnum_a, start));
2147 FREE_GMP_TEMP(temp_a);
2148 }
2149 /* }}} */
2150
ZEND_METHOD(GMP,__construct)2151 ZEND_METHOD(GMP, __construct)
2152 {
2153 zend_string *arg_str = NULL;
2154 zend_long arg_l = 0;
2155 zend_long base = 0;
2156
2157 ZEND_PARSE_PARAMETERS_START(0, 2)
2158 Z_PARAM_OPTIONAL
2159 Z_PARAM_STR_OR_LONG(arg_str, arg_l)
2160 Z_PARAM_LONG(base)
2161 ZEND_PARSE_PARAMETERS_END();
2162
2163 if (!gmp_verify_base(base, 2)) {
2164 RETURN_THROWS();
2165 }
2166
2167 return_value = ZEND_THIS;
2168 mpz_ptr gmp_number = GET_GMP_FROM_ZVAL(ZEND_THIS);
2169
2170 if (gmp_initialize_number(gmp_number, arg_str, arg_l, base) == FAILURE) {
2171 RETURN_THROWS();
2172 }
2173 }
2174
ZEND_METHOD(GMP,__serialize)2175 ZEND_METHOD(GMP, __serialize)
2176 {
2177 ZEND_PARSE_PARAMETERS_NONE();
2178
2179 zval zv;
2180 array_init(return_value);
2181
2182 mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(ZEND_THIS);
2183 gmp_strval(&zv, gmpnum, 16);
2184 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv);
2185
2186 HashTable *props = Z_OBJ_P(ZEND_THIS)->properties;
2187 if (props && zend_hash_num_elements(props) != 0) {
2188 ZVAL_ARR(&zv, zend_proptable_to_symtable(
2189 zend_std_get_properties(Z_OBJ_P(ZEND_THIS)), /* always duplicate */ 1));
2190 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv);
2191 }
2192 }
2193
ZEND_METHOD(GMP,__unserialize)2194 ZEND_METHOD(GMP, __unserialize)
2195 {
2196 HashTable *data;
2197
2198 ZEND_PARSE_PARAMETERS_START(1, 1)
2199 Z_PARAM_ARRAY_HT(data)
2200 ZEND_PARSE_PARAMETERS_END();
2201
2202 zval *num = zend_hash_index_find(data, 0);
2203 if (!num || Z_TYPE_P(num) != IS_STRING ||
2204 convert_zstr_to_gmp(GET_GMP_FROM_ZVAL(ZEND_THIS), Z_STR_P(num), 16, /* arg_pos */ 0) == FAILURE) {
2205 zend_throw_exception(NULL, "Could not unserialize number", 0);
2206 RETURN_THROWS();
2207 }
2208
2209 zval *props = zend_hash_index_find(data, 1);
2210 if (props) {
2211 if (Z_TYPE_P(props) != IS_ARRAY) {
2212 zend_throw_exception(NULL, "Could not unserialize properties", 0);
2213 RETURN_THROWS();
2214 }
2215
2216 object_properties_load(Z_OBJ_P(ZEND_THIS), Z_ARRVAL_P(props));
2217 }
2218 }
2219