Merge branch 'pull-request/1905'

* pull-request/1905:
   pack()/unpack() for Big Endian float/double and Little Endian float/double
This commit is contained in:
Joe Watkins 2017-01-03 10:48:42 +00:00
parent 19b757dacd
commit ff4e330eae
No known key found for this signature in database
GPG Key ID: F9BA0ADA31CBD89E
3 changed files with 519 additions and 11 deletions

View 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 */
@ -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;
}

View File

@ -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)
}

View File

@ -19,7 +19,7 @@ var_dump(unpack("I", pack("I", 65534), $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===