Skip to content

Commit 47d630e

Browse files
ltnikic
authored andcommitted
Implement gmp_import() and gmp_export()
1 parent f54451c commit 47d630e

File tree

5 files changed

+314
-20
lines changed

5 files changed

+314
-20
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ PHP NEWS
2222
- GMP:
2323
. Fixed bug #67917 (Using GMP objects with overloaded operators can cause
2424
memory exhaustion). (Nikita)
25+
. Implemented gmp_import() and gmp_export(). (Leigh, Nikita)
2526

2627
- MySQLi:
2728
. Fixed bug #67839 (mysqli does not handle 4-byte floats correctly). (Keyur)

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ PHP 5.6 UPGRADE NOTES
267267

268268
- GMP:
269269
Added gmp_root($a, $nth) and gmp_rootrem($a, $nth) for calculating nth roots.
270+
Added gmp_import($data, $word_size = 1, $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) in PHP 5.6.1.
271+
Added gmp_export($gmpnumber, $word_size = 1, $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) in PHP 5.6.1.
270272

271273
- Hash
272274
Added hash_equals($known_string, $user_string)

ext/gmp/gmp.c

Lines changed: 158 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_init, 0, 0, 1)
4343
ZEND_ARG_INFO(0, base)
4444
ZEND_END_ARG_INFO()
4545

46+
ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_import, 0, 0, 1)
47+
ZEND_ARG_INFO(0, data)
48+
ZEND_ARG_INFO(0, word_size)
49+
ZEND_ARG_INFO(0, options)
50+
ZEND_END_ARG_INFO()
51+
52+
ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_export, 0, 0, 1)
53+
ZEND_ARG_INFO(0, gmpnumber)
54+
ZEND_ARG_INFO(0, word_size)
55+
ZEND_ARG_INFO(0, options)
56+
ZEND_END_ARG_INFO()
57+
4658
ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_intval, 0, 0, 1)
4759
ZEND_ARG_INFO(0, gmpnumber)
4860
ZEND_END_ARG_INFO()
@@ -117,6 +129,8 @@ static ZEND_GINIT_FUNCTION(gmp);
117129
*/
118130
const zend_function_entry gmp_functions[] = {
119131
ZEND_FE(gmp_init, arginfo_gmp_init)
132+
ZEND_FE(gmp_import, arginfo_gmp_import)
133+
ZEND_FE(gmp_export, arginfo_gmp_export)
120134
ZEND_FE(gmp_intval, arginfo_gmp_intval)
121135
ZEND_FE(gmp_strval, arginfo_gmp_strval)
122136
ZEND_FE(gmp_add, arginfo_gmp_binary)
@@ -204,6 +218,12 @@ typedef struct _gmp_temp {
204218
#define GMP_ROUND_PLUSINF 1
205219
#define GMP_ROUND_MINUSINF 2
206220

221+
#define GMP_MSW_FIRST (1 << 0)
222+
#define GMP_LSW_FIRST (1 << 1)
223+
#define GMP_LITTLE_ENDIAN (1 << 2)
224+
#define GMP_BIG_ENDIAN (1 << 3)
225+
#define GMP_NATIVE_ENDIAN (1 << 4)
226+
207227
#define GMP_42_OR_NEWER \
208228
((__GNU_MP_VERSION >= 5) || (__GNU_MP_VERSION >= 4 && __GNU_MP_VERSION_MINOR >= 2))
209229

@@ -297,7 +317,7 @@ static void gmp_strval(zval *result, mpz_t gmpnum, long base);
297317
static int convert_to_gmp(mpz_t gmpnumber, zval *val, int base TSRMLS_DC);
298318
static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg TSRMLS_DC);
299319

300-
/*
320+
/*
301321
* The gmp_*_op functions provide an implementation for several common types
302322
* of GMP functions. The gmp_zval_(unary|binary)_*_op functions have to be manually
303323
* passed zvals to work on, whereas the gmp_(unary|binary)_*_op macros already
@@ -599,7 +619,7 @@ static int gmp_serialize(zval *object, unsigned char **buffer, zend_uint *buf_le
599619

600620
PHP_VAR_SERIALIZE_INIT(serialize_data);
601621
INIT_PZVAL(zv_ptr);
602-
622+
603623
gmp_strval(zv_ptr, gmpnum, 10);
604624
php_var_serialize(&buf, &zv_ptr, &serialize_data TSRMLS_CC);
605625
zval_dtor(zv_ptr);
@@ -697,6 +717,12 @@ ZEND_MINIT_FUNCTION(gmp)
697717
#endif
698718
REGISTER_STRING_CONSTANT("GMP_VERSION", (char *)gmp_version, CONST_CS | CONST_PERSISTENT);
699719

720+
REGISTER_LONG_CONSTANT("GMP_MSW_FIRST", GMP_MSW_FIRST, CONST_CS | CONST_PERSISTENT);
721+
REGISTER_LONG_CONSTANT("GMP_LSW_FIRST", GMP_LSW_FIRST, CONST_CS | CONST_PERSISTENT);
722+
REGISTER_LONG_CONSTANT("GMP_LITTLE_ENDIAN", GMP_LITTLE_ENDIAN, CONST_CS | CONST_PERSISTENT);
723+
REGISTER_LONG_CONSTANT("GMP_BIG_ENDIAN", GMP_BIG_ENDIAN, CONST_CS | CONST_PERSISTENT);
724+
REGISTER_LONG_CONSTANT("GMP_NATIVE_ENDIAN", GMP_NATIVE_ENDIAN, CONST_CS | CONST_PERSISTENT);
725+
700726
mp_set_memory_functions(gmp_emalloc, gmp_erealloc, gmp_efree);
701727

702728
return SUCCESS;
@@ -734,7 +760,7 @@ ZEND_MODULE_INFO_D(gmp)
734760

735761
/* {{{ convert_to_gmp
736762
* Convert zval to be gmp number */
737-
static int convert_to_gmp(mpz_t gmpnumber, zval *val, int base TSRMLS_DC)
763+
static int convert_to_gmp(mpz_t gmpnumber, zval *val, int base TSRMLS_DC)
738764
{
739765
switch (Z_TYPE_P(val)) {
740766
case IS_LONG:
@@ -788,8 +814,8 @@ static void gmp_strval(zval *result, mpz_t gmpnum, long base) /* {{{ */
788814

789815
out_string = emalloc(num_len + 1);
790816
mpz_get_str(out_string, base, gmpnum);
791-
792-
/*
817+
818+
/*
793819
* From GMP documentation for mpz_sizeinbase():
794820
* The returned value will be exact or 1 too big. If base is a power of
795821
* 2, the returned value will always be exact.
@@ -831,22 +857,22 @@ static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg TSRMLS_DC) /* {
831857

832858
FREE_GMP_TEMP(temp_a);
833859
FREE_GMP_TEMP(temp_b);
834-
860+
835861
RETURN_LONG(res);
836862
}
837863
/* }}} */
838864

839865
/* {{{ gmp_zval_binary_ui_op
840866
Execute GMP binary operation.
841867
*/
842-
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, int check_b_zero TSRMLS_DC)
868+
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, int check_b_zero TSRMLS_DC)
843869
{
844870
mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
845871
int use_ui = 0;
846872
gmp_temp_t temp_a, temp_b;
847873

848874
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
849-
875+
850876
if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
851877
use_ui = 1;
852878
temp_b.is_used = 0;
@@ -942,7 +968,7 @@ static inline void _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAMETERS, gmp_binary_op
942968
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &a_arg, &b_arg) == FAILURE){
943969
return;
944970
}
945-
971+
946972
gmp_zval_binary_ui_op(return_value, a_arg, b_arg, gmp_op, gmp_ui_op, check_b_zero TSRMLS_CC);
947973
}
948974
/* }}} */
@@ -951,11 +977,11 @@ static inline void _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAMETERS, gmp_binary_op
951977

952978
/* {{{ gmp_zval_unary_op
953979
*/
954-
static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op TSRMLS_DC)
980+
static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op TSRMLS_DC)
955981
{
956982
mpz_ptr gmpnum_a, gmpnum_result;
957983
gmp_temp_t temp_a;
958-
984+
959985
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
960986

961987
INIT_GMP_RETVAL(gmpnum_result);
@@ -1000,7 +1026,7 @@ static inline void _gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_op_t gm
10001026
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &a_arg) == FAILURE){
10011027
return;
10021028
}
1003-
1029+
10041030
gmp_zval_unary_op(return_value, a_arg, gmp_op TSRMLS_CC);
10051031
}
10061032
/* }}} */
@@ -1016,7 +1042,7 @@ static inline void _gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_opl_t
10161042
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &a_arg) == FAILURE){
10171043
return;
10181044
}
1019-
1045+
10201046
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
10211047
RETVAL_LONG(gmp_op(gmpnum_a));
10221048
FREE_GMP_TEMP(temp_a);
@@ -1070,6 +1096,118 @@ ZEND_FUNCTION(gmp_init)
10701096
}
10711097
/* }}} */
10721098

1099+
int gmp_import_export_validate(long size, long options, int *order, int *endian TSRMLS_DC)
1100+
{
1101+
if (size < 1) {
1102+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
1103+
"Word size must be positive, %ld given", size);
1104+
return FAILURE;
1105+
}
1106+
1107+
switch (options & (GMP_LSW_FIRST | GMP_MSW_FIRST)) {
1108+
case GMP_LSW_FIRST:
1109+
*order = -1;
1110+
break;
1111+
case GMP_MSW_FIRST:
1112+
case 0: /* default */
1113+
*order = 1;
1114+
break;
1115+
default:
1116+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
1117+
"Invalid options: Conflicting word orders");
1118+
return FAILURE;
1119+
}
1120+
1121+
switch (options & (GMP_LITTLE_ENDIAN | GMP_BIG_ENDIAN | GMP_NATIVE_ENDIAN)) {
1122+
case GMP_LITTLE_ENDIAN:
1123+
*endian = -1;
1124+
break;
1125+
case GMP_BIG_ENDIAN:
1126+
*endian = 1;
1127+
break;
1128+
case GMP_NATIVE_ENDIAN:
1129+
case 0: /* default */
1130+
*endian = 0;
1131+
break;
1132+
default:
1133+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
1134+
"Invalid options: Conflicting word endianness");
1135+
return FAILURE;
1136+
}
1137+
1138+
return SUCCESS;
1139+
}
1140+
1141+
/* {{{ proto GMP gmp_import(string data [, int word_size = 1, int options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN])
1142+
Imports a GMP number from a binary string */
1143+
ZEND_FUNCTION(gmp_import)
1144+
{
1145+
char *data;
1146+
int data_len;
1147+
long size = 1;
1148+
long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;;
1149+
int order, endian;
1150+
mpz_ptr gmpnumber;
1151+
1152+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &data, &data_len, &size, &options) == FAILURE) {
1153+
return;
1154+
}
1155+
1156+
if (gmp_import_export_validate(size, options, &order, &endian TSRMLS_CC) == FAILURE) {
1157+
RETURN_FALSE;
1158+
}
1159+
1160+
if ((data_len % size) != 0) {
1161+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
1162+
"Input length must be a multiple of word size");
1163+
RETURN_FALSE;
1164+
}
1165+
1166+
INIT_GMP_RETVAL(gmpnumber);
1167+
1168+
mpz_import(gmpnumber, data_len / size, order, size, endian, 0, data);
1169+
}
1170+
/* }}} */
1171+
1172+
/* {{{ proto string gmp_export(GMP gmpnumber [, int word_size = 1, int options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN])
1173+
Exports a GMP number to a binary string */
1174+
ZEND_FUNCTION(gmp_export)
1175+
{
1176+
zval *gmpnumber_arg;
1177+
long size = 1;
1178+
long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;;
1179+
int order, endian;
1180+
mpz_ptr gmpnumber;
1181+
gmp_temp_t temp_a;
1182+
1183+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &gmpnumber_arg, &size, &options) == FAILURE) {
1184+
return;
1185+
}
1186+
1187+
if (gmp_import_export_validate(size, options, &order, &endian TSRMLS_CC) == FAILURE) {
1188+
RETURN_FALSE;
1189+
}
1190+
1191+
FETCH_GMP_ZVAL(gmpnumber, gmpnumber_arg, temp_a);
1192+
1193+
if (mpz_sgn(gmpnumber) == 0) {
1194+
ZVAL_STRING(return_value, "", 1);
1195+
} else {
1196+
size_t bits_per_word = size * 8;
1197+
size_t count = (mpz_sizeinbase(gmpnumber, 2) + bits_per_word - 1) / bits_per_word;
1198+
size_t out_len = count * size;
1199+
1200+
char *out_string = emalloc(out_len + 1);
1201+
mpz_export(out_string, NULL, order, size, endian, 0, gmpnumber);
1202+
out_string[out_len] = '\0';
1203+
1204+
ZVAL_STRINGL(return_value, out_string, out_len, 0);
1205+
}
1206+
1207+
FREE_GMP_TEMP(temp_a);
1208+
}
1209+
/* }}} */
1210+
10731211
/* {{{ proto int gmp_intval(mixed gmpnumber)
10741212
Gets signed long value of GMP number */
10751213
ZEND_FUNCTION(gmp_intval)
@@ -1079,7 +1217,7 @@ ZEND_FUNCTION(gmp_intval)
10791217
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &gmpnumber_arg) == FAILURE){
10801218
return;
10811219
}
1082-
1220+
10831221
if (IS_GMP(gmpnumber_arg)) {
10841222
RETVAL_LONG(mpz_get_si(GET_GMP_FROM_ZVAL(gmpnumber_arg)));
10851223
} else {
@@ -1226,7 +1364,7 @@ ZEND_FUNCTION(gmp_div_q)
12261364
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid rounding mode");
12271365
RETURN_FALSE;
12281366
}
1229-
1367+
12301368
}
12311369
/* }}} */
12321370

@@ -1306,7 +1444,7 @@ ZEND_FUNCTION(gmp_pow)
13061444
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative exponent not supported");
13071445
RETURN_FALSE;
13081446
}
1309-
1447+
13101448
INIT_GMP_RETVAL(gmpnum_result);
13111449
if (Z_TYPE_P(base_arg) == IS_LONG && Z_LVAL_P(base_arg) >= 0) {
13121450
mpz_ui_pow_ui(gmpnum_result, Z_LVAL_P(base_arg), exp);
@@ -1380,14 +1518,14 @@ ZEND_FUNCTION(gmp_sqrt)
13801518
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &a_arg) == FAILURE){
13811519
return;
13821520
}
1383-
1521+
13841522
FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
13851523

13861524
if (mpz_sgn(gmpnum_a) < 0) {
13871525
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number has to be greater than or equal to 0");
13881526
FREE_GMP_TEMP(temp_a);
13891527
RETURN_FALSE;
1390-
}
1528+
}
13911529

13921530
INIT_GMP_RETVAL(gmpnum_result);
13931531
mpz_sqrt(gmpnum_result, gmpnum_a);
@@ -1414,7 +1552,7 @@ ZEND_FUNCTION(gmp_sqrtrem)
14141552
FREE_GMP_TEMP(temp_a);
14151553
RETURN_FALSE;
14161554
}
1417-
1555+
14181556
array_init(return_value);
14191557
add_index_zval(return_value, 0, gmp_create(&gmpnum_result1 TSRMLS_CC));
14201558
add_index_zval(return_value, 1, gmp_create(&gmpnum_result2 TSRMLS_CC));
@@ -1494,7 +1632,7 @@ ZEND_FUNCTION(gmp_rootrem)
14941632
mpz_sub(gmpnum_result2, gmpnum_a, gmpnum_result2);
14951633
mpz_abs(gmpnum_result2, gmpnum_result2);
14961634
#endif
1497-
1635+
14981636
FREE_GMP_TEMP(temp_a);
14991637
}
15001638
/* }}} */

ext/gmp/php_gmp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ ZEND_MODULE_DEACTIVATE_D(gmp);
3131
ZEND_MODULE_INFO_D(gmp);
3232

3333
ZEND_FUNCTION(gmp_init);
34+
ZEND_FUNCTION(gmp_import);
35+
ZEND_FUNCTION(gmp_export);
3436
ZEND_FUNCTION(gmp_intval);
3537
ZEND_FUNCTION(gmp_strval);
3638
ZEND_FUNCTION(gmp_add);

0 commit comments

Comments
 (0)