]> granicus.if.org Git - php/commitdiff
Add 64 bit formats to pack() and unpack()
authorLeigh <leight@gmail.com>
Mon, 8 Sep 2014 20:59:32 +0000 (21:59 +0100)
committerLeigh <leight@gmail.com>
Thu, 9 Oct 2014 20:02:42 +0000 (21:02 +0100)
NEWS
ext/standard/pack.c
ext/standard/tests/strings/pack64.phpt [new file with mode: 0644]
ext/standard/tests/strings/pack64_32.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 6207983a24458a3e08bde4c297eabff01df02263..4f6ce962a90f32460ab5118c8aba45d1192d0dbc 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,7 @@ PHP                                                                        NEWS
     and passwords) (Tjerk)
   . Fixed bug #67949 (DOMNodeList elements should be accessible through
     array notation) (Florian)
+  . Implemented 64-bit format codes for pack() and unpack(). (Leigh)
 
 - FPM:
   . Fixed bug #65641 (PHP-FPM incorrectly defines the SCRIPT_NAME variable
index 16a30668dc555694a7751639b58f40d4d25c74ff..9427db90a01f81f2722272d3c8c646c758f1fdc3 100644 (file)
@@ -82,6 +82,13 @@ static int machine_endian_long_map[4];
 static int big_endian_long_map[4];
 static int little_endian_long_map[4];
 
+#if SIZEOF_LONG > 4
+/* Mappings of bytes from quads (64bit) for all endian environments */
+static int machine_endian_longlong_map[8];
+static int big_endian_longlong_map[8];
+static int little_endian_longlong_map[8];
+#endif
+
 /* {{{ php_pack
  */
 static void php_pack(zval **val, int size, int *map, char *output)
@@ -98,8 +105,8 @@ static void php_pack(zval **val, int size, int *map, char *output)
 }
 /* }}} */
 
-/* pack() idea stolen from Perl (implemented formats behave the same as there)
- * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @.
+/* pack() idea stolen from Perl (implemented formats behave the same as there except J and P)
+ * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, q, Q, J, P, f, d, x, X, @.
  */
 /* {{{ proto string pack(string format, mixed arg1 [, mixed arg2 [, mixed ...]])
    Takes one or more arguments and packs them into a binary string according to the format argument */
@@ -199,6 +206,17 @@ PHP_FUNCTION(pack)
                                break;
 
                        /* Use as many args as specified */
+                       case 'q':
+                       case 'Q':
+                       case 'J':
+                       case 'P':
+#if SIZEOF_LONG < 8
+                                       efree(argv);
+                                       efree(formatcodes);
+                                       efree(formatargs);
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "64-bit format codes are not available for 32-bit versions of PHP");
+                                       RETURN_FALSE;
+#endif
                        case 'c': 
                        case 'C': 
                        case 's': 
@@ -283,6 +301,15 @@ PHP_FUNCTION(pack)
                                INC_OUTPUTPOS(arg,4)            /* 32 bit per arg */
                                break;
 
+#if SIZEOF_LONG > 4
+                       case 'q':
+                       case 'Q':
+                       case 'J':
+                       case 'P':
+                               INC_OUTPUTPOS(arg,8)            /* 32 bit per arg */
+                               break;
+#endif
+
                        case 'f':
                                INC_OUTPUTPOS(arg,sizeof(float))
                                break;
@@ -437,6 +464,27 @@ PHP_FUNCTION(pack)
                                break;
                        }
 
+#if SIZEOF_LONG > 4
+                       case 'q':
+                       case 'Q':
+                       case 'J':
+                       case 'P': {
+                               int *map = machine_endian_longlong_map;
+
+                               if (code == 'J') {
+                                       map = big_endian_longlong_map;
+                               } else if (code == 'P') {
+                                       map = little_endian_longlong_map;
+                               }
+
+                               while (arg-- > 0) {
+                                       php_pack(argv[currentarg++], 8, map, &output[outputpos]);
+                                       outputpos += 8;
+                               }
+                               break;
+                       }
+#endif
+
                        case 'f': {
                                float v;
 
@@ -522,7 +570,7 @@ static long php_unpack(char *data, int size, int issigned, int *map)
  * chars1, chars2, and ints.
  * Numeric pack types will return numbers, a and A will return strings,
  * f and d will return doubles.
- * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @.
+ * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, q, Q, J, P, f, d, x, X, @.
  */
 /* {{{ proto array unpack(string format, string input)
    Unpack binary string into named array elements according to format argument */
@@ -637,6 +685,20 @@ PHP_FUNCTION(unpack)
                                size = 4;
                                break;
 
+                       /* Use 8 bytes of input */
+                       case 'q':
+                       case 'Q':
+                       case 'J':
+                       case 'P':
+#if SIZEOF_LONG > 4
+                               size = 8;
+                               break;
+#else
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "64-bit format codes are not available for 32-bit versions of PHP");
+                               zval_dtor(return_value);
+                               RETURN_FALSE;
+#endif
+
                        /* Use sizeof(float) bytes of input */
                        case 'f':
                                size = sizeof(float);
@@ -860,6 +922,38 @@ PHP_FUNCTION(unpack)
                                                break;
                                        }
 
+#if SIZEOF_LONG > 4
+                                       case 'q':
+                                       case 'Q':
+                                       case 'J':
+                                       case 'P': {
+                                               int issigned = 0;
+                                               int *map = machine_endian_longlong_map;
+                                               long v = 0;
+
+                                               if (type == 'q' || type == 'Q') {
+                                                       issigned = input[inputpos + (machine_little_endian ? 7 : 0)] & 0x80;
+                                               } else if (type == 'J') {
+                                                       issigned = input[inputpos] & 0x80;
+                                                       map = big_endian_longlong_map;
+                                               } else if (type == 'P') {
+                                                       issigned = input[inputpos + 7] & 0x80;
+                                                       map = little_endian_longlong_map;
+                                               }
+
+                                               v = php_unpack(&input[inputpos], 8, issigned, map);
+
+                                               if (type == 'q') {
+                                                       v = (signed long int) v;
+                                               } else {
+                                                       v = (unsigned long int) v;
+                                               }
+
+                                               add_assoc_long(return_value, n, v);
+                                               break;
+                                       }
+#endif
+
                                        case 'f': {
                                                float v;
 
@@ -961,6 +1055,33 @@ PHP_MINIT_FUNCTION(pack)
                little_endian_long_map[1] = 1;
                little_endian_long_map[2] = 2;
                little_endian_long_map[3] = 3;
+
+#if SIZEOF_LONG > 4
+               machine_endian_longlong_map[0] = 0;
+               machine_endian_longlong_map[1] = 1;
+               machine_endian_longlong_map[2] = 2;
+               machine_endian_longlong_map[3] = 3;
+               machine_endian_longlong_map[4] = 4;
+               machine_endian_longlong_map[5] = 5;
+               machine_endian_longlong_map[6] = 6;
+               machine_endian_longlong_map[7] = 7;
+               big_endian_longlong_map[0] = 7;
+               big_endian_longlong_map[1] = 6;
+               big_endian_longlong_map[2] = 5;
+               big_endian_longlong_map[3] = 4;
+               big_endian_longlong_map[4] = 3;
+               big_endian_longlong_map[5] = 2;
+               big_endian_longlong_map[6] = 1;
+               big_endian_longlong_map[7] = 0;
+               little_endian_longlong_map[0] = 0;
+               little_endian_longlong_map[1] = 1;
+               little_endian_longlong_map[2] = 2;
+               little_endian_longlong_map[3] = 3;
+               little_endian_longlong_map[4] = 4;
+               little_endian_longlong_map[5] = 5;
+               little_endian_longlong_map[6] = 6;
+               little_endian_longlong_map[7] = 7;
+#endif
        }
        else {
                zval val;
@@ -993,6 +1114,33 @@ PHP_MINIT_FUNCTION(pack)
                little_endian_long_map[1] = size - 2;
                little_endian_long_map[2] = size - 3;
                little_endian_long_map[3] = size - 4;
+
+#if SIZEOF_LONG > 4
+               machine_endian_longlong_map[0] = size - 8;
+               machine_endian_longlong_map[1] = size - 7;
+               machine_endian_longlong_map[2] = size - 6;
+               machine_endian_longlong_map[3] = size - 5;
+               machine_endian_longlong_map[0] = size - 4;
+               machine_endian_longlong_map[1] = size - 3;
+               machine_endian_longlong_map[2] = size - 2;
+               machine_endian_longlong_map[3] = size - 1;
+               big_endian_longlong_map[0] = size - 8;
+               big_endian_longlong_map[1] = size - 7;
+               big_endian_longlong_map[2] = size - 6;
+               big_endian_longlong_map[3] = size - 5;
+               big_endian_longlong_map[0] = size - 4;
+               big_endian_longlong_map[1] = size - 3;
+               big_endian_longlong_map[2] = size - 2;
+               big_endian_longlong_map[3] = size - 1;
+               little_endian_longlong_map[0] = size - 1;
+               little_endian_longlong_map[1] = size - 2;
+               little_endian_longlong_map[2] = size - 3;
+               little_endian_longlong_map[3] = size - 4;
+               little_endian_longlong_map[0] = size - 5;
+               little_endian_longlong_map[1] = size - 6;
+               little_endian_longlong_map[2] = size - 7;
+               little_endian_longlong_map[3] = size - 8;
+#endif
        }
 
        return SUCCESS;
diff --git a/ext/standard/tests/strings/pack64.phpt b/ext/standard/tests/strings/pack64.phpt
new file mode 100644 (file)
index 0000000..9bc2492
--- /dev/null
@@ -0,0 +1,115 @@
+--TEST--
+64bit pack()/unpack() tests
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE < 8) {
+       die("skip 64bit test only");
+}
+?>
+--FILE--
+<?php
+print_r(unpack("Q", pack("Q", 0xfffffffffffe)));
+print_r(unpack("Q", pack("Q", 0)));
+print_r(unpack("Q", pack("Q", 0x8000000000000002)));
+print_r(unpack("Q", pack("Q", -1)));
+print_r(unpack("Q", pack("Q", 0x8000000000000000)));
+
+print_r(unpack("J", pack("J", 0xfffffffffffe)));
+print_r(unpack("J", pack("J", 0)));
+print_r(unpack("J", pack("J", 0x8000000000000002)));
+print_r(unpack("J", pack("J", -1)));
+print_r(unpack("J", pack("J", 0x8000000000000000)));
+
+print_r(unpack("P", pack("P", 0xfffffffffffe)));
+print_r(unpack("P", pack("P", 0)));
+print_r(unpack("P", pack("P", 0x8000000000000002)));
+print_r(unpack("P", pack("P", -1)));
+print_r(unpack("P", pack("P", 0x8000000000000000)));
+
+print_r(unpack("q", pack("q", 0xfffffffffffe)));
+print_r(unpack("q", pack("q", 0)));
+print_r(unpack("q", pack("q", 0x8000000000000002)));
+print_r(unpack("q", pack("q", -1)));
+print_r(unpack("q", pack("q", 0x8000000000000000)));
+?>
+--EXPECTF--
+Array
+(
+    [1] => 281474976710654
+)
+Array
+(
+    [1] => 0
+)
+Array
+(
+    [1] => -9223372036854775808
+)
+Array
+(
+    [1] => -1
+)
+Array
+(
+    [1] => -9223372036854775808
+)
+Array
+(
+    [1] => 281474976710654
+)
+Array
+(
+    [1] => 0
+)
+Array
+(
+    [1] => -9223372036854775808
+)
+Array
+(
+    [1] => -1
+)
+Array
+(
+    [1] => -9223372036854775808
+)
+Array
+(
+    [1] => 281474976710654
+)
+Array
+(
+    [1] => 0
+)
+Array
+(
+    [1] => -9223372036854775808
+)
+Array
+(
+    [1] => -1
+)
+Array
+(
+    [1] => -9223372036854775808
+)
+Array
+(
+    [1] => 281474976710654
+)
+Array
+(
+    [1] => 0
+)
+Array
+(
+    [1] => -9223372036854775808
+)
+Array
+(
+    [1] => -1
+)
+Array
+(
+    [1] => -9223372036854775808
+)
diff --git a/ext/standard/tests/strings/pack64_32.phpt b/ext/standard/tests/strings/pack64_32.phpt
new file mode 100644 (file)
index 0000000..f52de63
--- /dev/null
@@ -0,0 +1,44 @@
+--TEST--
+64bit pack()/unpack() tests
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE > 4) {
+       die("skip 32bit test only");
+}
+?>
+--FILE--
+<?php
+var_dump(pack("Q", 0));
+var_dump(pack("J", 0));
+var_dump(pack("P", 0));
+var_dump(pack("q", 0));
+
+var_dump(unpack("Q", ''));
+var_dump(unpack("J", ''));
+var_dump(unpack("P", ''));
+var_dump(unpack("q", ''));
+?>
+--EXPECTF--
+Warning: pack(): 64-bit format codes are not available for 32-bit versions of PHP in %s on line %d
+bool(false)
+
+Warning: pack(): 64-bit format codes are not available for 32-bit versions of PHP in %s on line %d
+bool(false)
+
+Warning: pack(): 64-bit format codes are not available for 32-bit versions of PHP in %s on line %d
+bool(false)
+
+Warning: pack(): 64-bit format codes are not available for 32-bit versions of PHP in %s on line %d
+bool(false)
+
+Warning: unpack(): 64-bit format codes are not available for 32-bit versions of PHP in %s on line %d
+bool(false)
+
+Warning: unpack(): 64-bit format codes are not available for 32-bit versions of PHP in %s on line %d
+bool(false)
+
+Warning: unpack(): 64-bit format codes are not available for 32-bit versions of PHP in %s on line %d
+bool(false)
+
+Warning: unpack(): 64-bit format codes are not available for 32-bit versions of PHP in %s on line %d
+bool(false)