summaryrefslogtreecommitdiff
path: root/ext/standard/pack.c
diff options
context:
space:
mode:
authorJoe Watkins <krakjoe@php.net>2017-01-03 10:48:42 +0000
committerJoe Watkins <krakjoe@php.net>2017-01-03 10:50:19 +0000
commitff4e330eae7aa2550c483d480cb98054e251da55 (patch)
treea5dd2d2d1b1b3687f2c8f075e31c9d4296b8c91a /ext/standard/pack.c
parent19b757dacd224c1e70ad3452c5f9d9b69141ddf1 (diff)
downloadphp-git-ff4e330eae7aa2550c483d480cb98054e251da55.tar.gz
Merge branch 'pull-request/1905'
* pull-request/1905: pack()/unpack() for Big Endian float/double and Little Endian float/double
Diffstat (limited to 'ext/standard/pack.c')
-rw-r--r--ext/standard/pack.c214
1 files changed, 205 insertions, 9 deletions
diff --git a/ext/standard/pack.c b/ext/standard/pack.c
index a24ee69ad2..1184717993 100644
--- a/ext/standard/pack.c
+++ b/ext/standard/pack.c
@@ -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 */
@@ -215,8 +339,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;
}
@@ -294,11 +422,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;
@@ -473,6 +605,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) {
@@ -482,6 +634,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);
@@ -542,6 +714,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 */
@@ -673,11 +846,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;
@@ -933,18 +1110,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;
}