]> granicus.if.org Git - php/commitdiff
Merge branch 'pull-request/1905'
authorJoe Watkins <krakjoe@php.net>
Tue, 3 Jan 2017 10:48:42 +0000 (10:48 +0000)
committerJoe Watkins <krakjoe@php.net>
Tue, 3 Jan 2017 10:49:53 +0000 (10:49 +0000)
* pull-request/1905:
   pack()/unpack() for Big Endian float/double and Little Endian float/double

ext/standard/pack.c
ext/standard/tests/strings/pack_float.phpt [new file with mode: 0644]
ext/standard/tests/strings/unpack_error.phpt

index 1252f4286f83a1c352abfaad3a4db51e295e333c..261e97ce8b7dd053090c4f2cabfb3b48b6431421 100644 (file)
@@ -104,8 +104,132 @@ static void php_pack(zval *val, size_t size, int *map, char *output)
 }
 /* }}} */
 
+/* {{{ php_pack_reverse_int32
+ */
+inline uint32_t php_pack_reverse_int32(uint32_t arg)
+{
+    uint32_t result;
+    result = ((arg & 0xFF) << 24) | ((arg & 0xFF00) << 8) | ((arg >> 8) & 0xFF00) | ((arg >> 24) & 0xFF);
+
+       return result;
+}
+/* }}} */
+
+/* {{{ php_pack
+ */
+inline uint64_t php_pack_reverse_int64(uint64_t arg)
+{
+       union Swap64 {
+               uint64_t i;
+               uint32_t ul[2];
+       } tmp, result;
+       tmp.i = arg;
+       result.ul[0] = php_pack_reverse_int32(tmp.ul[1]);
+       result.ul[1] = php_pack_reverse_int32(tmp.ul[0]);
+
+       return result.i;
+}
+/* }}} */
+
+/* {{{ php_pack_copy_float
+ */
+static void php_pack_copy_float(int is_little_endian, void * dst, float f)
+{
+       union Copy32 {
+               float f;
+               uint32_t i;
+       } m;
+       m.f = f;
+
+#ifdef WORDS_BIGENDIAN
+       if (is_little_endian) {
+               m.i = php_pack_reverse_int32(m.i);
+       }
+#else /* WORDS_BIGENDIAN */
+       if (!is_little_endian) {
+               m.i = php_pack_reverse_int32(m.i);
+       }
+#endif /* WORDS_BIGENDIAN */
+
+       memcpy(dst, &m.f, sizeof(float));
+}
+/* }}} */
+
+/* {{{ php_pack_copy_double
+ */
+static void php_pack_copy_double(int is_little_endian, void * dst, double d)
+{
+       union Copy64 {
+               double d;
+               uint64_t i;
+       } m;
+       m.d = d;
+
+#ifdef WORDS_BIGENDIAN
+       if (is_little_endian) {
+               m.i = php_pack_reverse_int64(m.i);
+       }
+#else /* WORDS_BIGENDIAN */
+       if (!is_little_endian) {
+               m.i = php_pack_reverse_int64(m.i);
+       }
+#endif /* WORDS_BIGENDIAN */
+
+       memcpy(dst, &m.d, sizeof(double));
+}
+/* }}} */
+
+/* {{{ php_pack_parse_float
+ */
+static float php_pack_parse_float(int is_little_endian, void * src)
+{
+       union Copy32 {
+               float f;
+               uint32_t i;
+       } m;
+       memcpy(&m.i, src, sizeof(float));
+
+#ifdef WORDS_BIGENDIAN
+       if (is_little_endian) {
+               m.i = php_pack_reverse_int32(m.i);
+       }
+#else /* WORDS_BIGENDIAN */
+       if (!is_little_endian) {
+               m.i = php_pack_reverse_int32(m.i);
+       }
+#endif /* WORDS_BIGENDIAN */
+
+       return m.f;
+}
+/* }}} */
+
+/* {{{ php_pack_parse_double
+ */
+static double php_pack_parse_double(int is_little_endian, void * src)
+{
+       union Copy64 {
+               double d;
+               uint64_t i;
+       } m;
+       memcpy(&m.i, src, sizeof(double));
+
+#ifdef WORDS_BIGENDIAN
+       if (is_little_endian) {
+               m.i = php_pack_reverse_int64(m.i);
+       }
+#else /* WORDS_BIGENDIAN */
+       if (!is_little_endian) {
+               m.i = php_pack_reverse_int64(m.i);
+       }
+#endif /* WORDS_BIGENDIAN */
+
+       return m.d;
+}
+/* }}} */
+
 /* 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, @.
+ * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
  */
 /* {{{ 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 */
@@ -216,8 +340,12 @@ PHP_FUNCTION(pack)
                        case 'N':
                        case 'v':
                        case 'V':
-                       case 'f':
-                       case 'd':
+                       case 'f': /* float */
+                       case 'g': /* little endian float */
+                       case 'G': /* big endian float */
+                       case 'd': /* double */
+                       case 'e': /* little endian double */
+                       case 'E': /* big endian double */
                                if (arg < 0) {
                                        arg = num_args - currentarg;
                                }
@@ -295,11 +423,15 @@ PHP_FUNCTION(pack)
                                break;
 #endif
 
-                       case 'f':
+                       case 'f': /* float */
+                       case 'g': /* little endian float */
+                       case 'G': /* big endian float */
                                INC_OUTPUTPOS(arg,sizeof(float))
                                break;
 
-                       case 'd':
+                       case 'd': /* double */
+                       case 'e': /* little endian double */
+                       case 'E': /* big endian double */
                                INC_OUTPUTPOS(arg,sizeof(double))
                                break;
 
@@ -474,6 +606,26 @@ PHP_FUNCTION(pack)
                                }
                                break;
                        }
+                       
+                       case 'g': {
+                               /* pack little endian float */
+                               while (arg-- > 0) {
+                                       float v = (float) zval_get_double(&argv[currentarg++]);
+                                       php_pack_copy_float(1, &ZSTR_VAL(output)[outputpos], v);
+                                       outputpos += sizeof(v);
+                               }
+                               
+                               break;
+                       }
+                       case 'G': {
+                               /* pack big endian float */
+                               while (arg-- > 0) {
+                                       float v = (float) zval_get_double(&argv[currentarg++]);
+                                       php_pack_copy_float(0, &ZSTR_VAL(output)[outputpos], v);
+                                       outputpos += sizeof(v);
+                               }
+                               break;
+                       }
 
                        case 'd': {
                                while (arg-- > 0) {
@@ -483,6 +635,26 @@ PHP_FUNCTION(pack)
                                }
                                break;
                        }
+                       
+                       case 'e': {
+                               /* pack little endian double */
+                               while (arg-- > 0) {
+                                       double v = (double) zval_get_double(&argv[currentarg++]);
+                                       php_pack_copy_double(1, &ZSTR_VAL(output)[outputpos], v);
+                                       outputpos += sizeof(v);
+                               }
+                               break;
+                       }
+                       
+                       case 'E': {
+                               /* pack big endian double */
+                               while (arg-- > 0) {
+                                       double v = (double) zval_get_double(&argv[currentarg++]);
+                                       php_pack_copy_double(0, &ZSTR_VAL(output)[outputpos], v);
+                                       outputpos += sizeof(v);
+                               }
+                               break;
+                       }
 
                        case 'x':
                                memset(&ZSTR_VAL(output)[outputpos], '\0', arg);
@@ -543,6 +715,7 @@ static zend_long php_unpack(char *data, size_t size, int issigned, int *map)
  * 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, q, Q, J, P, f, d, x, X, @.
+ * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
  */
 /* {{{ proto array unpack(string format, string input)
    Unpack binary string into named array elements according to format argument */
@@ -683,11 +856,15 @@ PHP_FUNCTION(unpack)
 
                        /* Use sizeof(float) bytes of input */
                        case 'f':
+                       case 'g':
+                       case 'G':
                                size = sizeof(float);
                                break;
 
                        /* Use sizeof(double) bytes of input */
                        case 'd':
+                       case 'e':
+                       case 'E':
                                size = sizeof(double);
                                break;
 
@@ -943,18 +1120,37 @@ PHP_FUNCTION(unpack)
                                        }
 #endif
 
-                                       case 'f': {
+                                       case 'f': /* float */ 
+                                       case 'g': /* little endian float*/
+                                       case 'G': /* big endian float*/
+                                       {
                                                float v;
 
-                                               memcpy(&v, &input[inputpos], sizeof(float));
+                                               if (type == 'g') {
+                                                       v = php_pack_parse_float(1, &input[inputpos]);
+                                               } else if (type == 'G') {
+                                                       v = php_pack_parse_float(0, &input[inputpos]);
+                                               } else {
+                                                       memcpy(&v, &input[inputpos], sizeof(float));
+                                               }
+                                               
                                                add_assoc_double(return_value, n, (double)v);
                                                break;
                                        }
+                                       
 
-                                       case 'd': {
+                                       case 'd': /* double */
+                                       case 'e': /* little endian float */
+                                       case 'E': /* big endian float */ 
+                                       {
                                                double v;
-
-                                               memcpy(&v, &input[inputpos], sizeof(double));
+                                               if (type == 'e') {
+                                                       v = php_pack_parse_double(1, &input[inputpos]);
+                                               } else if (type == 'E') {
+                                                       v = php_pack_parse_double(0, &input[inputpos]);
+                                               } else {
+                                                       memcpy(&v, &input[inputpos], sizeof(double));
+                                               }
                                                add_assoc_double(return_value, n, v);
                                                break;
                                        }
diff --git a/ext/standard/tests/strings/pack_float.phpt b/ext/standard/tests/strings/pack_float.phpt
new file mode 100644 (file)
index 0000000..088b508
--- /dev/null
@@ -0,0 +1,312 @@
+--TEST--
+pack()/unpack(): float/double tests
+--FILE--
+<?php
+var_dump(
+    'pack e',
+    bin2hex(pack("e", "")),
+    bin2hex(pack("e", "a")),
+    bin2hex(pack("e", " ")),
+    bin2hex(pack("e", NULL)),
+    bin2hex(pack("e", 0)),
+    bin2hex(pack("e", 1)),
+    bin2hex(pack("e", 1.0)),
+    bin2hex(pack("e", 10000000000000000)),
+    bin2hex(pack("e", 0.591234709823149)),
+    bin2hex(pack("e", 12345678901234567890.1234567898765432123456789)),
+    bin2hex(pack("e", -1)),
+    bin2hex(pack("e", -1.0)),
+    bin2hex(pack("e", -10000000000000000)),
+    bin2hex(pack("e", -0.591234709823149)),
+    bin2hex(pack("e", -12345678901234567890.1234567898765432123456789)),
+    
+    'pack E',
+    bin2hex(pack("E", "")),
+    bin2hex(pack("E", "a")),
+    bin2hex(pack("E", " ")),
+    bin2hex(pack("E", NULL)),
+    bin2hex(pack("E", 0)),
+    bin2hex(pack("E", 1)),
+    bin2hex(pack("E", 1.0)),
+    bin2hex(pack("E", 10000000000000000)),
+    bin2hex(pack("E", 0.591234709823149)),
+    bin2hex(pack("E", 12345678901234567890.1234567898765432123456789)),
+    bin2hex(pack("E", -1)),
+    bin2hex(pack("E", -1.0)),
+    bin2hex(pack("E", -10000000000000000)),
+    bin2hex(pack("E", -0.591234709823149)),
+    bin2hex(pack("E", -12345678901234567890.1234567898765432123456789)),
+    
+    'pack g',
+    bin2hex(pack("g", "")),
+    bin2hex(pack("g", "a")),
+    bin2hex(pack("g", " ")),
+    bin2hex(pack("g", NULL)),
+    bin2hex(pack("g", 0)),
+    bin2hex(pack("g", 1)),
+    bin2hex(pack("g", 1.0)),
+    bin2hex(pack("g", 10000000000000000)),
+    bin2hex(pack("g", 0.591234709823149)),
+    bin2hex(pack("g", 12345678901234567890.1234567898765432123456789)),
+    bin2hex(pack("g", -1)),
+    bin2hex(pack("g", -1.0)),
+    bin2hex(pack("g", -10000000000000000)),
+    bin2hex(pack("g", -0.591234709823149)),
+    bin2hex(pack("g", -12345678901234567890.1234567898765432123456789)),
+    
+    'pack G',
+    bin2hex(pack("G", "")),
+    bin2hex(pack("G", "a")),
+    bin2hex(pack("G", " ")),
+    bin2hex(pack("G", NULL)),
+    bin2hex(pack("G", 0)),
+    bin2hex(pack("G", 1)),
+    bin2hex(pack("G", 1.0)),
+    bin2hex(pack("G", 10000000000000000)),
+    bin2hex(pack("G", 0.591234709823149)),
+    bin2hex(pack("G", 12345678901234567890.1234567898765432123456789)),
+    bin2hex(pack("G", -1)),
+    bin2hex(pack("G", -1.0)),
+    bin2hex(pack("G", -10000000000000000)),
+    bin2hex(pack("G", -0.591234709823149)),
+    bin2hex(pack("G", -12345678901234567890.1234567898765432123456789)),
+    
+    'unpack e',
+    unpack('e', hex2bin('0000000000000000')),
+    unpack('e', hex2bin('000000000000f03f')),
+    unpack('e', hex2bin('0080e03779c34143')),
+    unpack('e', hex2bin('4a6ade0d65ebe23f')),
+    unpack('e', hex2bin('000000000000f0bf')),
+    unpack('e', hex2bin('0080e03779c341c3')),
+    unpack('e', hex2bin('4a6ade0d65ebe2bf')),
+    unpack('e', hex2bin('e1639d31956ae5c3')),
+    
+    'unpack E',
+    unpack('E', hex2bin('3ff0000000000000')),
+    unpack('E', hex2bin('4341c37937e08000')),
+    unpack('E', hex2bin('3fe2eb650dde6a4a')),
+    unpack('E', hex2bin('43e56a95319d63e1')),
+    unpack('E', hex2bin('bff0000000000000')),
+    unpack('E', hex2bin('c341c37937e08000')),
+    unpack('E', hex2bin('bfe2eb650dde6a4a')),
+    unpack('E', hex2bin('c3e56a95319d63e1')),
+    
+    'unpack g',
+    unpack('g', hex2bin('0000803f')),
+    unpack('g', hex2bin('ca1b0e5a')),
+    unpack('g', hex2bin('285b173f')),
+    unpack('g', hex2bin('aa542b5f')),
+    unpack('g', hex2bin('000080bf')),
+    unpack('g', hex2bin('ca1b0eda')),
+    unpack('g', hex2bin('285b17bf')),
+    unpack('g', hex2bin('aa542bdf')),
+    
+    'unpack G',
+    unpack('G', hex2bin('3f800000')),
+    unpack('G', hex2bin('5a0e1bca')),
+    unpack('G', hex2bin('3f175b28')),
+    unpack('G', hex2bin('5f2b54aa')),
+    unpack('G', hex2bin('bf800000')),
+    unpack('G', hex2bin('da0e1bca')),
+    unpack('G', hex2bin('bf175b28')),
+    unpack('G', hex2bin('df2b54aa'))
+    
+);
+?>
+--EXPECTF--
+string(6) "pack e"
+string(16) "0000000000000000"
+string(16) "0000000000000000"
+string(16) "0000000000000000"
+string(16) "0000000000000000"
+string(16) "0000000000000000"
+string(16) "000000000000f03f"
+string(16) "000000000000f03f"
+string(16) "0080e03779c34143"
+string(16) "4a6ade0d65ebe23f"
+string(16) "e1639d31956ae543"
+string(16) "000000000000f0bf"
+string(16) "000000000000f0bf"
+string(16) "0080e03779c341c3"
+string(16) "4a6ade0d65ebe2bf"
+string(16) "e1639d31956ae5c3"
+string(6) "pack E"
+string(16) "0000000000000000"
+string(16) "0000000000000000"
+string(16) "0000000000000000"
+string(16) "0000000000000000"
+string(16) "0000000000000000"
+string(16) "3ff0000000000000"
+string(16) "3ff0000000000000"
+string(16) "4341c37937e08000"
+string(16) "3fe2eb650dde6a4a"
+string(16) "43e56a95319d63e1"
+string(16) "bff0000000000000"
+string(16) "bff0000000000000"
+string(16) "c341c37937e08000"
+string(16) "bfe2eb650dde6a4a"
+string(16) "c3e56a95319d63e1"
+string(6) "pack g"
+string(8) "00000000"
+string(8) "00000000"
+string(8) "00000000"
+string(8) "00000000"
+string(8) "00000000"
+string(8) "0000803f"
+string(8) "0000803f"
+string(8) "ca1b0e5a"
+string(8) "285b173f"
+string(8) "aa542b5f"
+string(8) "000080bf"
+string(8) "000080bf"
+string(8) "ca1b0eda"
+string(8) "285b17bf"
+string(8) "aa542bdf"
+string(6) "pack G"
+string(8) "00000000"
+string(8) "00000000"
+string(8) "00000000"
+string(8) "00000000"
+string(8) "00000000"
+string(8) "3f800000"
+string(8) "3f800000"
+string(8) "5a0e1bca"
+string(8) "3f175b28"
+string(8) "5f2b54aa"
+string(8) "bf800000"
+string(8) "bf800000"
+string(8) "da0e1bca"
+string(8) "bf175b28"
+string(8) "df2b54aa"
+string(8) "unpack e"
+array(1) {
+  [1]=>
+  float(0)
+}
+array(1) {
+  [1]=>
+  float(1)
+}
+array(1) {
+  [1]=>
+  float(1.0E+16)
+}
+array(1) {
+  [1]=>
+  float(0.59123470982315)
+}
+array(1) {
+  [1]=>
+  float(-1)
+}
+array(1) {
+  [1]=>
+  float(-1.0E+16)
+}
+array(1) {
+  [1]=>
+  float(-0.59123470982315)
+}
+array(1) {
+  [1]=>
+  float(-1.2345678901235E+19)
+}
+string(8) "unpack E"
+array(1) {
+  [1]=>
+  float(1)
+}
+array(1) {
+  [1]=>
+  float(1.0E+16)
+}
+array(1) {
+  [1]=>
+  float(0.59123470982315)
+}
+array(1) {
+  [1]=>
+  float(1.2345678901235E+19)
+}
+array(1) {
+  [1]=>
+  float(-1)
+}
+array(1) {
+  [1]=>
+  float(-1.0E+16)
+}
+array(1) {
+  [1]=>
+  float(-0.59123470982315)
+}
+array(1) {
+  [1]=>
+  float(-1.2345678901235E+19)
+}
+string(8) "unpack g"
+array(1) {
+  [1]=>
+  float(1)
+}
+array(1) {
+  [1]=>
+  float(1.0000000272564E+16)
+}
+array(1) {
+  [1]=>
+  float(0.59123468399048)
+}
+array(1) {
+  [1]=>
+  float(1.2345679395506E+19)
+}
+array(1) {
+  [1]=>
+  float(-1)
+}
+array(1) {
+  [1]=>
+  float(-1.0000000272564E+16)
+}
+array(1) {
+  [1]=>
+  float(-0.59123468399048)
+}
+array(1) {
+  [1]=>
+  float(-1.2345679395506E+19)
+}
+string(8) "unpack G"
+array(1) {
+  [1]=>
+  float(1)
+}
+array(1) {
+  [1]=>
+  float(1.0000000272564E+16)
+}
+array(1) {
+  [1]=>
+  float(0.59123468399048)
+}
+array(1) {
+  [1]=>
+  float(1.2345679395506E+19)
+}
+array(1) {
+  [1]=>
+  float(-1)
+}
+array(1) {
+  [1]=>
+  float(-1.0000000272564E+16)
+}
+array(1) {
+  [1]=>
+  float(-0.59123468399048)
+}
+array(1) {
+  [1]=>
+  float(-1.2345679395506E+19)
+}
index 3a4f334c3b211b2510ab654a7f758499d4bdc939..484366293b4bcff45f9c3b5f198712eb93fead67 100644 (file)
@@ -19,7 +19,7 @@ var_dump(unpack("I", pack("I", 65534), 0, $extra_arg));
 
 echo "\n-- Testing unpack() function with invalid format character --\n";
 $extra_arg = 10;
-var_dump(unpack("G", pack("I", 65534)));
+var_dump(unpack("B", pack("I", 65534)));
 ?>
 ===DONE===
 --EXPECTF--
@@ -37,6 +37,6 @@ NULL
 
 -- Testing unpack() function with invalid format character --
 
-Warning: unpack(): Invalid format type G in %s on line %d
+Warning: unpack(): Invalid format type B in %s on line %d
 bool(false)
 ===DONE===