]> granicus.if.org Git - php/commitdiff
Implement gmp_import() and gmp_export()
authorLeigh <leight@gmail.com>
Wed, 27 Aug 2014 20:45:05 +0000 (21:45 +0100)
committerNikita Popov <nikic@php.net>
Tue, 2 Sep 2014 12:19:18 +0000 (14:19 +0200)
NEWS
UPGRADING
ext/gmp/gmp.c
ext/gmp/php_gmp.h
ext/gmp/tests/import-export.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 30b0f8848d7a11640988c014d34ca07d7d0d553c..1034a5d93f5b82e7acff3cf2b6d67cf33e1cf731 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,7 @@ PHP                                                                        NEWS
 - GMP:
   . Fixed bug #67917 (Using GMP objects with overloaded operators can cause
     memory exhaustion). (Nikita)
+  . Implemented gmp_import() and gmp_export(). (Leigh, Nikita)
 
 - MySQLi:
   . Fixed bug #67839 (mysqli does not handle 4-byte floats correctly). (Keyur)
index 388b0d2f72d2b308c101ac1039f7e001e233adcd..d4dd30335676bcb25986b33ae6a61588454d76b4 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -267,6 +267,8 @@ PHP 5.6 UPGRADE NOTES
 
 - GMP:
   Added gmp_root($a, $nth) and gmp_rootrem($a, $nth) for calculating nth roots.
+  Added gmp_import($data, $word_size = 1, $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) in PHP 5.6.1.
+  Added gmp_export($gmpnumber, $word_size = 1, $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) in PHP 5.6.1.
 
 - Hash
   Added hash_equals($known_string, $user_string)
index 486088bbafd6fc06b4f6771a63ef469d2b6794d0..399749d3975ff5e6232499a24cb6d1ec1c822c56 100644 (file)
@@ -43,6 +43,18 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_init, 0, 0, 1)
        ZEND_ARG_INFO(0, base)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_import, 0, 0, 1)
+       ZEND_ARG_INFO(0, data)
+       ZEND_ARG_INFO(0, word_size)
+       ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_export, 0, 0, 1)
+       ZEND_ARG_INFO(0, gmpnumber)
+       ZEND_ARG_INFO(0, word_size)
+       ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmp_intval, 0, 0, 1)
        ZEND_ARG_INFO(0, gmpnumber)
 ZEND_END_ARG_INFO()
@@ -117,6 +129,8 @@ static ZEND_GINIT_FUNCTION(gmp);
  */
 const zend_function_entry gmp_functions[] = {
        ZEND_FE(gmp_init,               arginfo_gmp_init)
+       ZEND_FE(gmp_import,             arginfo_gmp_import)
+       ZEND_FE(gmp_export,             arginfo_gmp_export)
        ZEND_FE(gmp_intval,             arginfo_gmp_intval)
        ZEND_FE(gmp_strval,             arginfo_gmp_strval)
        ZEND_FE(gmp_add,                arginfo_gmp_binary)
@@ -204,6 +218,12 @@ typedef struct _gmp_temp {
 #define GMP_ROUND_PLUSINF   1
 #define GMP_ROUND_MINUSINF  2
 
+#define GMP_MSW_FIRST     (1 << 0)
+#define GMP_LSW_FIRST     (1 << 1)
+#define GMP_LITTLE_ENDIAN (1 << 2)
+#define GMP_BIG_ENDIAN    (1 << 3)
+#define GMP_NATIVE_ENDIAN (1 << 4)
+
 #define GMP_42_OR_NEWER \
        ((__GNU_MP_VERSION >= 5) || (__GNU_MP_VERSION >= 4 && __GNU_MP_VERSION_MINOR >= 2))
 
@@ -297,7 +317,7 @@ static void gmp_strval(zval *result, mpz_t gmpnum, long base);
 static int convert_to_gmp(mpz_t gmpnumber, zval *val, int base TSRMLS_DC);
 static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg TSRMLS_DC);
 
-/* 
+/*
  * The gmp_*_op functions provide an implementation for several common types
  * of GMP functions. The gmp_zval_(unary|binary)_*_op functions have to be manually
  * 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
 
        PHP_VAR_SERIALIZE_INIT(serialize_data);
        INIT_PZVAL(zv_ptr);
-    
+
        gmp_strval(zv_ptr, gmpnum, 10);
        php_var_serialize(&buf, &zv_ptr, &serialize_data TSRMLS_CC);
        zval_dtor(zv_ptr);
@@ -697,6 +717,12 @@ ZEND_MINIT_FUNCTION(gmp)
 #endif
        REGISTER_STRING_CONSTANT("GMP_VERSION", (char *)gmp_version, CONST_CS | CONST_PERSISTENT);
 
+       REGISTER_LONG_CONSTANT("GMP_MSW_FIRST", GMP_MSW_FIRST, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("GMP_LSW_FIRST", GMP_LSW_FIRST, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("GMP_LITTLE_ENDIAN", GMP_LITTLE_ENDIAN, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("GMP_BIG_ENDIAN", GMP_BIG_ENDIAN, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("GMP_NATIVE_ENDIAN", GMP_NATIVE_ENDIAN, CONST_CS | CONST_PERSISTENT);
+
        mp_set_memory_functions(gmp_emalloc, gmp_erealloc, gmp_efree);
 
        return SUCCESS;
@@ -734,7 +760,7 @@ ZEND_MODULE_INFO_D(gmp)
 
 /* {{{ convert_to_gmp
  * Convert zval to be gmp number */
-static int convert_to_gmp(mpz_t gmpnumber, zval *val, int base TSRMLS_DC) 
+static int convert_to_gmp(mpz_t gmpnumber, zval *val, int base TSRMLS_DC)
 {
        switch (Z_TYPE_P(val)) {
        case IS_LONG:
@@ -788,8 +814,8 @@ static void gmp_strval(zval *result, mpz_t gmpnum, long base) /* {{{ */
 
        out_string = emalloc(num_len + 1);
        mpz_get_str(out_string, base, gmpnum);
-       
-       /* 
+
+       /*
         * From GMP documentation for mpz_sizeinbase():
         * The returned value will be exact or 1 too big.  If base is a power of
         * 2, the returned value will always be exact.
@@ -831,7 +857,7 @@ static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg TSRMLS_DC) /* {
 
        FREE_GMP_TEMP(temp_a);
        FREE_GMP_TEMP(temp_b);
-       
+
        RETURN_LONG(res);
 }
 /* }}} */
@@ -839,14 +865,14 @@ static void gmp_cmp(zval *return_value, zval *a_arg, zval *b_arg TSRMLS_DC) /* {
 /* {{{ gmp_zval_binary_ui_op
    Execute GMP binary operation.
 */
-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) 
+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)
 {
        mpz_ptr gmpnum_a, gmpnum_b, gmpnum_result;
        int use_ui = 0;
        gmp_temp_t temp_a, temp_b;
 
        FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
-       
+
        if (gmp_ui_op && Z_TYPE_P(b_arg) == IS_LONG && Z_LVAL_P(b_arg) >= 0) {
                use_ui = 1;
                temp_b.is_used = 0;
@@ -942,7 +968,7 @@ static inline void _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAMETERS, gmp_binary_op
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &a_arg, &b_arg) == FAILURE){
                return;
        }
-       
+
        gmp_zval_binary_ui_op(return_value, a_arg, b_arg, gmp_op, gmp_ui_op, check_b_zero TSRMLS_CC);
 }
 /* }}} */
@@ -951,11 +977,11 @@ static inline void _gmp_binary_ui_op(INTERNAL_FUNCTION_PARAMETERS, gmp_binary_op
 
 /* {{{ gmp_zval_unary_op
  */
-static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op TSRMLS_DC) 
+static inline void gmp_zval_unary_op(zval *return_value, zval *a_arg, gmp_unary_op_t gmp_op TSRMLS_DC)
 {
        mpz_ptr gmpnum_a, gmpnum_result;
        gmp_temp_t temp_a;
-       
+
        FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
 
        INIT_GMP_RETVAL(gmpnum_result);
@@ -1000,7 +1026,7 @@ static inline void _gmp_unary_op(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_op_t gm
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &a_arg) == FAILURE){
                return;
        }
-       
+
        gmp_zval_unary_op(return_value, a_arg, gmp_op TSRMLS_CC);
 }
 /* }}} */
@@ -1016,7 +1042,7 @@ static inline void _gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_opl_t
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &a_arg) == FAILURE){
                return;
        }
-       
+
        FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
        RETVAL_LONG(gmp_op(gmpnum_a));
        FREE_GMP_TEMP(temp_a);
@@ -1070,6 +1096,118 @@ ZEND_FUNCTION(gmp_init)
 }
 /* }}} */
 
+int gmp_import_export_validate(long size, long options, int *order, int *endian TSRMLS_DC)
+{
+       if (size < 1) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                       "Word size must be positive, %ld given", size);
+               return FAILURE;
+       }
+
+       switch (options & (GMP_LSW_FIRST | GMP_MSW_FIRST)) {
+               case GMP_LSW_FIRST:
+                       *order = -1;
+                       break;
+               case GMP_MSW_FIRST:
+               case 0: /* default */
+                       *order = 1;
+                       break;
+               default:
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                               "Invalid options: Conflicting word orders");
+                       return FAILURE;
+       }
+
+       switch (options & (GMP_LITTLE_ENDIAN | GMP_BIG_ENDIAN | GMP_NATIVE_ENDIAN)) {
+               case GMP_LITTLE_ENDIAN:
+                       *endian = -1;
+                       break;
+               case GMP_BIG_ENDIAN:
+                       *endian = 1;
+                       break;
+               case GMP_NATIVE_ENDIAN:
+               case 0: /* default */
+                       *endian = 0;
+                       break;
+               default:
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                               "Invalid options: Conflicting word endianness");
+                       return FAILURE;
+       }
+
+       return SUCCESS;
+}
+
+/* {{{ proto GMP gmp_import(string data [, int word_size = 1, int options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN])
+   Imports a GMP number from a binary string */
+ZEND_FUNCTION(gmp_import)
+{
+       char *data;
+       int data_len;
+       long size = 1;
+       long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;;
+       int order, endian;
+       mpz_ptr gmpnumber;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &data, &data_len, &size, &options) == FAILURE) {
+               return;
+       }
+
+       if (gmp_import_export_validate(size, options, &order, &endian TSRMLS_CC) == FAILURE) {
+               RETURN_FALSE;
+       }
+
+       if ((data_len % size) != 0) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                       "Input length must be a multiple of word size");
+               RETURN_FALSE;
+       }
+
+       INIT_GMP_RETVAL(gmpnumber);
+
+       mpz_import(gmpnumber, data_len / size, order, size, endian, 0, data);
+}
+/* }}} */
+
+/* {{{ proto string gmp_export(GMP gmpnumber [, int word_size = 1, int options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN])
+   Exports a GMP number to a binary string */
+ZEND_FUNCTION(gmp_export)
+{
+       zval *gmpnumber_arg;
+       long size = 1;
+       long options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN;;
+       int order, endian;
+       mpz_ptr gmpnumber;
+       gmp_temp_t temp_a;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &gmpnumber_arg, &size, &options) == FAILURE) {
+               return;
+       }
+
+       if (gmp_import_export_validate(size, options, &order, &endian TSRMLS_CC) == FAILURE) {
+               RETURN_FALSE;
+       }
+
+       FETCH_GMP_ZVAL(gmpnumber, gmpnumber_arg, temp_a);
+
+       if (mpz_sgn(gmpnumber) == 0) {
+               ZVAL_STRING(return_value, "", 1);
+       } else {
+               size_t bits_per_word = size * 8;
+               size_t count = (mpz_sizeinbase(gmpnumber, 2) + bits_per_word - 1) / bits_per_word;
+               size_t out_len = count * size;
+
+               char *out_string = emalloc(out_len + 1);
+               mpz_export(out_string, NULL, order, size, endian, 0, gmpnumber);
+               out_string[out_len] = '\0';
+
+               ZVAL_STRINGL(return_value, out_string, out_len, 0);
+       }
+
+       FREE_GMP_TEMP(temp_a);
+}
+/* }}} */
+
 /* {{{ proto int gmp_intval(mixed gmpnumber)
    Gets signed long value of GMP number */
 ZEND_FUNCTION(gmp_intval)
@@ -1079,7 +1217,7 @@ ZEND_FUNCTION(gmp_intval)
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &gmpnumber_arg) == FAILURE){
                return;
        }
-       
+
        if (IS_GMP(gmpnumber_arg)) {
                RETVAL_LONG(mpz_get_si(GET_GMP_FROM_ZVAL(gmpnumber_arg)));
        } else {
@@ -1226,7 +1364,7 @@ ZEND_FUNCTION(gmp_div_q)
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid rounding mode");
                RETURN_FALSE;
        }
-                                                          
+
 }
 /* }}} */
 
@@ -1306,7 +1444,7 @@ ZEND_FUNCTION(gmp_pow)
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative exponent not supported");
                RETURN_FALSE;
        }
-       
+
        INIT_GMP_RETVAL(gmpnum_result);
        if (Z_TYPE_P(base_arg) == IS_LONG && Z_LVAL_P(base_arg) >= 0) {
                mpz_ui_pow_ui(gmpnum_result, Z_LVAL_P(base_arg), exp);
@@ -1380,14 +1518,14 @@ ZEND_FUNCTION(gmp_sqrt)
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &a_arg) == FAILURE){
                return;
        }
-       
+
        FETCH_GMP_ZVAL(gmpnum_a, a_arg, temp_a);
 
        if (mpz_sgn(gmpnum_a) < 0) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number has to be greater than or equal to 0");
                FREE_GMP_TEMP(temp_a);
                RETURN_FALSE;
-       }       
+       }
 
        INIT_GMP_RETVAL(gmpnum_result);
        mpz_sqrt(gmpnum_result, gmpnum_a);
@@ -1414,7 +1552,7 @@ ZEND_FUNCTION(gmp_sqrtrem)
                FREE_GMP_TEMP(temp_a);
                RETURN_FALSE;
        }
-       
+
        array_init(return_value);
        add_index_zval(return_value, 0, gmp_create(&gmpnum_result1 TSRMLS_CC));
        add_index_zval(return_value, 1, gmp_create(&gmpnum_result2 TSRMLS_CC));
@@ -1494,7 +1632,7 @@ ZEND_FUNCTION(gmp_rootrem)
        mpz_sub(gmpnum_result2, gmpnum_a, gmpnum_result2);
        mpz_abs(gmpnum_result2, gmpnum_result2);
 #endif
-       
+
        FREE_GMP_TEMP(temp_a);
 }
 /* }}} */
index 05bd56fa913d5e0ed96ec311e39bd675069982a7..b3706c534d50916ef68ac47875fb637e9b5720b7 100644 (file)
@@ -31,6 +31,8 @@ ZEND_MODULE_DEACTIVATE_D(gmp);
 ZEND_MODULE_INFO_D(gmp);
 
 ZEND_FUNCTION(gmp_init);
+ZEND_FUNCTION(gmp_import);
+ZEND_FUNCTION(gmp_export);
 ZEND_FUNCTION(gmp_intval);
 ZEND_FUNCTION(gmp_strval);
 ZEND_FUNCTION(gmp_add);
diff --git a/ext/gmp/tests/import-export.phpt b/ext/gmp/tests/import-export.phpt
new file mode 100644 (file)
index 0000000..0063251
--- /dev/null
@@ -0,0 +1,151 @@
+--TEST--
+Check gmp_import and gmp_export behave as intended
+--SKIPIF--
+<?php if (!extension_loaded("gmp")) echo "skip"; ?>
+--FILE--
+<?php
+
+// Tests taken from GMPs own test suite.
+
+// format is [expected, size, options, input]
+$import = [
+    ['0',1,GMP_BIG_ENDIAN,''],
+    ['12345678',1,GMP_BIG_ENDIAN,'12345678'],
+    ['12345678',4,GMP_BIG_ENDIAN,'12345678'],
+    ['12345678',4,GMP_LSW_FIRST | GMP_BIG_ENDIAN,'12345678'],
+    ['12345678',1,GMP_LSW_FIRST | GMP_LITTLE_ENDIAN,'78563412'],
+    ['12345678',4,GMP_LITTLE_ENDIAN,'78563412'],
+    ['12345678',4,GMP_LSW_FIRST | GMP_LITTLE_ENDIAN,'78563412'],
+    ['123456789abc',2,GMP_BIG_ENDIAN,'123456789abc'],
+    ['123456789abc',2,GMP_LSW_FIRST | GMP_BIG_ENDIAN,'9abc56781234'],
+    ['123456789abc',2,GMP_LITTLE_ENDIAN,'34127856bc9a'],
+    ['123456789abc',2,GMP_LSW_FIRST | GMP_LITTLE_ENDIAN,'bc9a78563412'],
+    ['112233445566778899aabbcc',4,GMP_BIG_ENDIAN,'112233445566778899aabbcc'],
+    ['112233445566778899aabbcc',4,GMP_LSW_FIRST | GMP_BIG_ENDIAN,'99aabbcc5566778811223344'],
+    ['112233445566778899aabbcc',4,GMP_LITTLE_ENDIAN,'4433221188776655ccbbaa99'],
+    ['112233445566778899aabbcc',4,GMP_LSW_FIRST | GMP_LITTLE_ENDIAN,'ccbbaa998877665544332211'],
+    ['100120023003400450056006700780089009a00ab00bc00c',8,GMP_BIG_ENDIAN,'100120023003400450056006700780089009a00ab00bc00c'],
+    ['100120023003400450056006700780089009a00ab00bc00c',8,GMP_LSW_FIRST | GMP_BIG_ENDIAN,'9009a00ab00bc00c50056006700780081001200230034004'],
+    ['100120023003400450056006700780089009a00ab00bc00c',8,GMP_LITTLE_ENDIAN,'044003300220011008800770066005500cc00bb00aa00990'],
+    ['100120023003400450056006700780089009a00ab00bc00c',8,GMP_LSW_FIRST | GMP_LITTLE_ENDIAN,'0cc00bb00aa0099008800770066005500440033002200110']
+];
+
+// format is [output, size, options, expected]
+$export = [
+    ['0',1,GMP_BIG_ENDIAN,''],
+    ['0',2,GMP_BIG_ENDIAN,''],
+    ['0',3,GMP_BIG_ENDIAN,''],
+    ['12345678',1,GMP_BIG_ENDIAN,'12345678'],
+    ['12345678',4,GMP_BIG_ENDIAN,'12345678'],
+    ['12345678',4,GMP_LSW_FIRST | GMP_BIG_ENDIAN,'12345678'],
+    ['12345678',1,GMP_LSW_FIRST | GMP_LITTLE_ENDIAN,'78563412'],
+    ['12345678',4,GMP_LITTLE_ENDIAN,'78563412'],
+    ['12345678',4,GMP_LSW_FIRST | GMP_LITTLE_ENDIAN,'78563412'],
+    ['123456789ABC',2,GMP_BIG_ENDIAN,'123456789abc'],
+    ['123456789ABC',2,GMP_LSW_FIRST | GMP_BIG_ENDIAN,'9abc56781234'],
+    ['123456789ABC',2,GMP_LITTLE_ENDIAN,'34127856bc9a'],
+    ['123456789ABC',2,GMP_LSW_FIRST | GMP_LITTLE_ENDIAN,'bc9a78563412'],
+    ['112233445566778899AABBCC',4,GMP_BIG_ENDIAN,'112233445566778899aabbcc'],
+    ['112233445566778899AABBCC',4,GMP_LSW_FIRST | GMP_BIG_ENDIAN,'99aabbcc5566778811223344'],
+    ['112233445566778899AABBCC',4,GMP_LITTLE_ENDIAN,'4433221188776655ccbbaa99'],
+    ['112233445566778899AABBCC',4,GMP_LSW_FIRST | GMP_LITTLE_ENDIAN,'ccbbaa998877665544332211'],
+    ['100120023003400450056006700780089009A00AB00BC00C',8,GMP_BIG_ENDIAN,'100120023003400450056006700780089009a00ab00bc00c'],
+    ['100120023003400450056006700780089009A00AB00BC00C',8,GMP_LSW_FIRST | GMP_BIG_ENDIAN,'9009a00ab00bc00c50056006700780081001200230034004'],
+    ['100120023003400450056006700780089009A00AB00BC00C',8,GMP_LITTLE_ENDIAN,'044003300220011008800770066005500cc00bb00aa00990'],
+    ['100120023003400450056006700780089009A00AB00BC00C',8,GMP_LSW_FIRST | GMP_LITTLE_ENDIAN,'0cc00bb00aa0099008800770066005500440033002200110']
+];
+
+echo "Import:\n";
+$passed = true;
+foreach ($import as $k => $test) {
+    $gmp = gmp_import(hex2bin($test[3]), $test[1], $test[2]);
+    if ($gmp instanceof GMP) {
+        $result = gmp_strval($gmp, 16);
+        if ($result !== $test[0]) {
+            echo "$k: '$result' !== '{$test[0]}'\n";
+            $passed = false;
+        }
+    } else {
+        $type = gettype($gmp);
+        echo "$k: $type !== '{$test[0]}'\n";
+    }
+}
+
+var_dump($passed);
+
+echo "\nExport:\n";
+$passed = true;
+foreach ($export as $k => $test) {
+    $gmp = gmp_init($test[0], 16);
+    $str = gmp_export($gmp, $test[1], $test[2]);
+    if (is_string($str)) {
+        $result = bin2hex($str);
+        if ($result !== $test[3]) {
+            echo "$k: '$result' !== '{$test[3]}'\n";
+            $passed = false;
+        }
+    } else {
+        $type = gettype($str);
+        echo "$k: $type !== '{$test[3]}'\n";
+    }
+}
+
+var_dump($passed);
+
+// Invalid word sizes
+var_dump(gmp_import('a', -1));
+var_dump(gmp_import('a', 0));
+var_dump(gmp_export(123, -1));
+var_dump(gmp_export(123, 0));
+
+// Invalid data lengths
+var_dump(gmp_import('a', 2));
+var_dump(gmp_import('aa', 3));
+var_dump(gmp_import(str_repeat('a', 100), 64));
+
+// Invalid options
+var_dump(gmp_import('a', 1, GMP_MSW_FIRST | GMP_LSW_FIRST));
+var_dump(gmp_import('a', 1, GMP_BIG_ENDIAN | GMP_LITTLE_ENDIAN));
+
+var_dump(gmp_export(123, 1, GMP_MSW_FIRST | GMP_LSW_FIRST));
+var_dump(gmp_export(123, 1, GMP_BIG_ENDIAN | GMP_LITTLE_ENDIAN));
+
+--EXPECTF--
+Import:
+bool(true)
+
+Export:
+bool(true)
+
+Warning: gmp_import(): Word size must be positive, -1 given in %s on line %d
+bool(false)
+
+Warning: gmp_import(): Word size must be positive, 0 given in %s on line %d
+bool(false)
+
+Warning: gmp_export(): Word size must be positive, -1 given in %s on line %d
+bool(false)
+
+Warning: gmp_export(): Word size must be positive, 0 given in %s on line %d
+bool(false)
+
+Warning: gmp_import(): Input length must be a multiple of word size in %s on line %d
+bool(false)
+
+Warning: gmp_import(): Input length must be a multiple of word size in %s on line %d
+bool(false)
+
+Warning: gmp_import(): Input length must be a multiple of word size in %s on line %d
+bool(false)
+
+Warning: gmp_import(): Invalid options: Conflicting word orders in %s on line %d
+bool(false)
+
+Warning: gmp_import(): Invalid options: Conflicting word endianness in %s on line %d
+bool(false)
+
+Warning: gmp_export(): Invalid options: Conflicting word orders in %s on line %d
+bool(false)
+
+Warning: gmp_export(): Invalid options: Conflicting word endianness in %s on line %d
+bool(false)