diff --git a/NEWS b/NEWS index ca6b431d70cd8..fdff2158abae1 100644 --- a/NEWS +++ b/NEWS @@ -124,6 +124,8 @@ PHP NEWS . Fix NUL byte truncation in sqlite3 TEXT column handling. (ndossche) - Standard: + . Added endianness modifiers (<, >) for integer and floating-point format + codes in pack() and unpack(). (alexandre-daubois) . Fixed bug GH-19926 (reset internal pointer earlier while splicing array while COW violation flag is still set). (alexandre-daubois) . Added form feed (\f) in the default trimmed characters of trim(), rtrim() diff --git a/ext/standard/pack.c b/ext/standard/pack.c index 55da64897a2e7..4c1e6786cd860 100644 --- a/ext/standard/pack.c +++ b/ext/standard/pack.c @@ -24,6 +24,7 @@ if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \ efree(formatcodes); \ efree(formatargs); \ + efree(formatendian); \ zend_value_error("Type %c: integer overflow in format string", code); \ RETURN_THROWS(); \ } \ @@ -32,6 +33,7 @@ typedef enum { PHP_LITTLE_ENDIAN, PHP_BIG_ENDIAN, + PHP_NO_ENDIAN_MODIFIER, } php_pack_endianness; #ifdef WORDS_BIGENDIAN @@ -220,12 +222,27 @@ PHP_FUNCTION(pack) /* We have a maximum of format codes to deal with */ formatcodes = safe_emalloc(formatlen, sizeof(*formatcodes), 0); formatargs = safe_emalloc(formatlen, sizeof(*formatargs), 0); + php_pack_endianness *formatendian = safe_emalloc(formatlen, sizeof(*formatendian), 0); currentarg = 0; /* Preprocess format into formatcodes and formatargs */ for (i = 0; i < formatlen; formatcount++) { char code = format[i++]; int arg = 1; + php_pack_endianness endian = PHP_NO_ENDIAN_MODIFIER; + + /* Handle endianness modifier if any */ + if (i < formatlen) { + char c = format[i]; + + if (c == '<') { + endian = PHP_LITTLE_ENDIAN; + i++; + } else if (c == '>') { + endian = PHP_BIG_ENDIAN; + i++; + } + } /* Handle format arguments if any */ if (i < formatlen) { @@ -250,6 +267,13 @@ PHP_FUNCTION(pack) case 'x': case 'X': case '@': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + efree(formatcodes); + efree(formatargs); + efree(formatendian); + zend_value_error("Endianness modifier is not supported for format code '%c'", code); + RETURN_THROWS(); + } if (arg < 0) { php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", code); arg = 1; @@ -262,9 +286,17 @@ PHP_FUNCTION(pack) case 'Z': case 'h': case 'H': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + efree(formatcodes); + efree(formatargs); + efree(formatendian); + zend_value_error("Endianness modifier is not supported for format code '%c'", code); + RETURN_THROWS(); + } if (currentarg >= num_args) { efree(formatcodes); efree(formatargs); + efree(formatendian); zend_value_error("Type %c: not enough arguments", code); RETURN_THROWS(); } @@ -273,6 +305,7 @@ PHP_FUNCTION(pack) if (!try_convert_to_string(&argv[currentarg])) { efree(formatcodes); efree(formatargs); + efree(formatendian); RETURN_THROWS(); } @@ -288,35 +321,156 @@ PHP_FUNCTION(pack) currentarg++; break; - /* Use as many args as specified */ - case 'q': - case 'Q': + /* 64-bit codes with explicit endianness, endianness modifiers not allowed */ case 'J': case 'P': #if SIZEOF_ZEND_LONG < 8 + efree(formatcodes); + efree(formatargs); + efree(formatendian); + zend_value_error("64-bit format codes are not available for 32-bit versions of PHP"); + RETURN_THROWS(); +#else + if (endian != PHP_NO_ENDIAN_MODIFIER) { efree(formatcodes); efree(formatargs); - zend_value_error("64-bit format codes are not available for 32-bit versions of PHP"); + efree(formatendian); + zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', code); RETURN_THROWS(); + } + if (arg < 0) { + arg = num_args - currentarg; + } + if (currentarg > INT_MAX - arg) { + goto too_few_args; + } + currentarg += arg; + + if (currentarg > num_args) { + goto too_few_args; + } + break; #endif - case 'c': - case 'C': - case 's': - case 'S': - case 'i': - case 'I': - case 'l': - case 'L': + + /* 64-bit codes that support endianness modifiers */ + case 'q': + case 'Q': +#if SIZEOF_ZEND_LONG < 8 + efree(formatcodes); + efree(formatargs); + efree(formatendian); + zend_value_error("64-bit format codes are not available for 32-bit versions of PHP"); + RETURN_THROWS(); +#else + if (arg < 0) { + arg = num_args - currentarg; + } + if (currentarg > INT_MAX - arg) { + goto too_few_args; + } + currentarg += arg; + + if (currentarg > num_args) { + goto too_few_args; + } + break; +#endif + + /* Codes with explicit endianness, endianness modifiers not allowed */ case 'n': case 'N': case 'v': case 'V': - case 'f': /* float */ + if (endian != PHP_NO_ENDIAN_MODIFIER) { + efree(formatcodes); + efree(formatargs); + efree(formatendian); + zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', code); + RETURN_THROWS(); + } + if (arg < 0) { + arg = num_args - currentarg; + } + if (currentarg > INT_MAX - arg) { + goto too_few_args; + } + currentarg += arg; + + if (currentarg > num_args) { + goto too_few_args; + } + break; + + /* Codes that support endianness modifiers */ + case 's': + case 'S': + case 'l': + case 'L': + if (arg < 0) { + arg = num_args - currentarg; + } + if (currentarg > INT_MAX - arg) { + goto too_few_args; + } + currentarg += arg; + + if (currentarg > num_args) { + goto too_few_args; + } + break; + + case 'c': + case 'C': + case 'i': + case 'I': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + efree(formatcodes); + efree(formatargs); + efree(formatendian); + zend_value_error("Endianness modifier is not supported for format code '%c'", code); + RETURN_THROWS(); + } + if (arg < 0) { + arg = num_args - currentarg; + } + if (currentarg > INT_MAX - arg) { + goto too_few_args; + } + currentarg += arg; + + if (currentarg > num_args) { + goto too_few_args; + } + break; + + /* Codes with explicit endianness, endianness modifiers not allowed */ case 'g': /* little endian float */ case 'G': /* big endian float */ - case 'd': /* double */ case 'e': /* little endian double */ case 'E': /* big endian double */ + if (endian != PHP_NO_ENDIAN_MODIFIER) { + efree(formatcodes); + efree(formatargs); + efree(formatendian); + zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', code); + RETURN_THROWS(); + } + if (arg < 0) { + arg = num_args - currentarg; + } + if (currentarg > INT_MAX - arg) { + goto too_few_args; + } + currentarg += arg; + + if (currentarg > num_args) { + goto too_few_args; + } + break; + + /* Codes that support endianness modifiers */ + case 'f': /* float */ + case 'd': /* double */ if (arg < 0) { arg = num_args - currentarg; } @@ -329,6 +483,7 @@ PHP_FUNCTION(pack) too_few_args: efree(formatcodes); efree(formatargs); + efree(formatendian); zend_value_error("Type %c: too few arguments", code); RETURN_THROWS(); } @@ -337,12 +492,14 @@ PHP_FUNCTION(pack) default: efree(formatcodes); efree(formatargs); + efree(formatendian); zend_value_error("Type %c: unknown format code", code); RETURN_THROWS(); } formatcodes[formatcount] = code; formatargs[formatcount] = arg; + formatendian[formatcount] = endian; } if (currentarg < num_args) { @@ -509,12 +666,16 @@ PHP_FUNCTION(pack) case 'S': case 'n': case 'v': { - php_pack_endianness endianness = PHP_MACHINE_ENDIAN; + php_pack_endianness endianness; if (code == 'n') { endianness = PHP_BIG_ENDIAN; } else if (code == 'v') { endianness = PHP_LITTLE_ENDIAN; + } else if (formatendian[i] != PHP_NO_ENDIAN_MODIFIER) { + endianness = formatendian[i]; + } else { + endianness = PHP_MACHINE_ENDIAN; } while (arg-- > 0) { @@ -536,12 +697,16 @@ PHP_FUNCTION(pack) case 'L': case 'N': case 'V': { - php_pack_endianness endianness = PHP_MACHINE_ENDIAN; + php_pack_endianness endianness; if (code == 'N') { endianness = PHP_BIG_ENDIAN; } else if (code == 'V') { endianness = PHP_LITTLE_ENDIAN; + } else if (formatendian[i] != PHP_NO_ENDIAN_MODIFIER) { + endianness = formatendian[i]; + } else { + endianness = PHP_MACHINE_ENDIAN; } while (arg-- > 0) { @@ -556,12 +721,16 @@ PHP_FUNCTION(pack) case 'Q': case 'J': case 'P': { - php_pack_endianness endianness = PHP_MACHINE_ENDIAN; + php_pack_endianness endianness; if (code == 'J') { endianness = PHP_BIG_ENDIAN; } else if (code == 'P') { endianness = PHP_LITTLE_ENDIAN; + } else if (formatendian[i] != PHP_NO_ENDIAN_MODIFIER) { + endianness = formatendian[i]; + } else { + endianness = PHP_MACHINE_ENDIAN; } while (arg-- > 0) { @@ -572,59 +741,35 @@ PHP_FUNCTION(pack) } #endif - case 'f': { - while (arg-- > 0) { - float v = (float) zval_get_double(&argv[currentarg++]); - memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v)); - outputpos += sizeof(v); - } - 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 'f': + case 'g': 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) { - double v = zval_get_double(&argv[currentarg++]); - memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v)); - outputpos += sizeof(v); - } - break; - } - - case 'e': { - /* pack little endian double */ - while (arg-- > 0) { - double v = zval_get_double(&argv[currentarg++]); - php_pack_copy_double(1, &ZSTR_VAL(output)[outputpos], v); + if (code == 'g' || formatendian[i] == PHP_LITTLE_ENDIAN) { + php_pack_copy_float(1, &ZSTR_VAL(output)[outputpos], v); + } else if (code == 'G' || formatendian[i] == PHP_BIG_ENDIAN) { + php_pack_copy_float(0, &ZSTR_VAL(output)[outputpos], v); + } else { + memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v)); + } outputpos += sizeof(v); } break; } + case 'd': + case 'e': case 'E': { - /* pack big endian double */ while (arg-- > 0) { double v = zval_get_double(&argv[currentarg++]); - php_pack_copy_double(0, &ZSTR_VAL(output)[outputpos], v); + if (code == 'e' || formatendian[i] == PHP_LITTLE_ENDIAN) { + php_pack_copy_double(1, &ZSTR_VAL(output)[outputpos], v); + } else if (code == 'E' || formatendian[i] == PHP_BIG_ENDIAN) { + php_pack_copy_double(0, &ZSTR_VAL(output)[outputpos], v); + } else { + memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v)); + } outputpos += sizeof(v); } break; @@ -654,6 +799,7 @@ PHP_FUNCTION(pack) efree(formatcodes); efree(formatargs); + efree(formatendian); ZSTR_VAL(output)[outputpos] = '\0'; ZSTR_LEN(output) = outputpos; RETURN_NEW_STR(output); @@ -712,6 +858,21 @@ PHP_FUNCTION(unpack) char *name; int namelen; int size = 0; + php_pack_endianness endian = PHP_NO_ENDIAN_MODIFIER; + + if (formatlen > 0) { + char c = *format; + + if (c == '<') { + endian = PHP_LITTLE_ENDIAN; + format++; + formatlen--; + } else if (c == '>') { + endian = PHP_BIG_ENDIAN; + format++; + formatlen--; + } + } /* Handle format arguments if any */ if (formatlen > 0) { @@ -757,6 +918,10 @@ PHP_FUNCTION(unpack) switch (type) { /* Never use any input */ case 'X': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier is not supported for format code '%c'", type); + RETURN_THROWS(); + } size = -1; if (repetitions < 0) { php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", type); @@ -765,18 +930,30 @@ PHP_FUNCTION(unpack) break; case '@': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier is not supported for format code '%c'", type); + RETURN_THROWS(); + } size = 0; break; case 'a': case 'A': case 'Z': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier is not supported for format code '%c'", type); + RETURN_THROWS(); + } size = repetitions; repetitions = 1; break; case 'h': case 'H': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier is not supported for format code '%c'", type); + RETURN_THROWS(); + } size = (repetitions > 0) ? ((unsigned int) repetitions + 1) / 2 : repetitions; repetitions = 1; break; @@ -785,37 +962,74 @@ PHP_FUNCTION(unpack) case 'c': case 'C': case 'x': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier is not supported for format code '%c'", type); + RETURN_THROWS(); + } size = 1; break; - /* Use 2 bytes of input */ + /* Use 2 bytes of input, endianness modifiers allowed */ case 's': case 'S': + size = 2; + break; + + /* Use 2 bytes of input with inherent endianness */ case 'n': case 'v': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', type); + RETURN_THROWS(); + } size = 2; break; /* Use sizeof(int) bytes of input */ case 'i': case 'I': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier is not supported for format code '%c'", type); + RETURN_THROWS(); + } size = sizeof(int); break; - /* Use 4 bytes of input */ + /* Use 4 bytes of input, endianness modifiers allowed */ case 'l': case 'L': + size = 4; + break; + + /* Use 4 bytes of input with inherent endianness */ case 'N': case 'V': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', type); + RETURN_THROWS(); + } size = 4; break; - /* Use 8 bytes of input */ + /* Use 8 bytes of input, endianness modifiers allowed */ case 'q': case 'Q': +#if SIZEOF_ZEND_LONG > 4 + size = 8; + break; +#else + zend_value_error("64-bit format codes are not available for 32-bit versions of PHP"); + RETURN_THROWS(); +#endif + + /* Use 8 bytes of input with inherent endianness */ case 'J': case 'P': #if SIZEOF_ZEND_LONG > 4 + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', type); + RETURN_THROWS(); + } size = 8; break; #else @@ -823,17 +1037,33 @@ PHP_FUNCTION(unpack) RETURN_THROWS(); #endif - /* Use sizeof(float) bytes of input */ + /* Use sizeof(float) bytes of input, endianness modifiers allowed */ case 'f': + size = sizeof(float); + break; + + /* Use sizeof(float) bytes of input with inherent endianness */ case 'g': case 'G': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', type); + RETURN_THROWS(); + } size = sizeof(float); break; - /* Use sizeof(double) bytes of input */ + /* Use sizeof(double) bytes of input, endianness modifiers allowed */ case 'd': + size = sizeof(double); + break; + + /* Use sizeof(double) bytes of input with inherent endianness */ case 'e': case 'E': + if (endian != PHP_NO_ENDIAN_MODIFIER) { + zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', type); + RETURN_THROWS(); + } size = sizeof(double); break; @@ -998,17 +1228,30 @@ PHP_FUNCTION(unpack) break; } - case 's': /* signed machine endian */ - case 'S': /* unsigned machine endian */ + case 's': /* signed, machine endian or explicit */ + case 'S': /* unsigned, machine endian or explicit */ case 'n': /* unsigned big endian */ case 'v': { /* unsigned little endian */ zend_long v = 0; uint16_t x = *((unaligned_uint16_t*) &input[inputpos]); + bool need_swap = false; + if (type == 'n') { + need_swap = MACHINE_LITTLE_ENDIAN; + } else if (type == 'v') { + need_swap = !MACHINE_LITTLE_ENDIAN; + } else if (endian == PHP_LITTLE_ENDIAN) { + need_swap = !MACHINE_LITTLE_ENDIAN; + } else if (endian == PHP_BIG_ENDIAN) { + need_swap = MACHINE_LITTLE_ENDIAN; + } + + if (need_swap) { + x = php_pack_reverse_int16(x); + } + if (type == 's') { v = (int16_t) x; - } else if ((type == 'n' && MACHINE_LITTLE_ENDIAN) || (type == 'v' && !MACHINE_LITTLE_ENDIAN)) { - v = php_pack_reverse_int16(x); } else { v = x; } @@ -1032,17 +1275,30 @@ PHP_FUNCTION(unpack) break; } - case 'l': /* signed machine endian */ - case 'L': /* unsigned machine endian */ + case 'l': /* signed, machine endian or explicit */ + case 'L': /* unsigned, machine endian or explicit */ case 'N': /* unsigned big endian */ case 'V': { /* unsigned little endian */ zend_long v = 0; uint32_t x = *((unaligned_uint32_t*) &input[inputpos]); + bool need_swap = false; + if (type == 'N') { + need_swap = MACHINE_LITTLE_ENDIAN; + } else if (type == 'V') { + need_swap = !MACHINE_LITTLE_ENDIAN; + } else if (endian == PHP_LITTLE_ENDIAN) { + need_swap = !MACHINE_LITTLE_ENDIAN; + } else if (endian == PHP_BIG_ENDIAN) { + need_swap = MACHINE_LITTLE_ENDIAN; + } + + if (need_swap) { + x = php_pack_reverse_int32(x); + } + if (type == 'l') { v = (int32_t) x; - } else if ((type == 'N' && MACHINE_LITTLE_ENDIAN) || (type == 'V' && !MACHINE_LITTLE_ENDIAN)) { - v = php_pack_reverse_int32(x); } else { v = x; } @@ -1052,17 +1308,30 @@ PHP_FUNCTION(unpack) } #if SIZEOF_ZEND_LONG > 4 - case 'q': /* signed machine endian */ - case 'Q': /* unsigned machine endian */ + case 'q': /* signed, machine endian or explicit */ + case 'Q': /* unsigned, machine endian or explicit */ case 'J': /* unsigned big endian */ case 'P': { /* unsigned little endian */ zend_long v = 0; uint64_t x = *((unaligned_uint64_t*) &input[inputpos]); + bool need_swap = false; + if (type == 'J') { + need_swap = MACHINE_LITTLE_ENDIAN; + } else if (type == 'P') { + need_swap = !MACHINE_LITTLE_ENDIAN; + } else if (endian == PHP_LITTLE_ENDIAN) { + need_swap = !MACHINE_LITTLE_ENDIAN; + } else if (endian == PHP_BIG_ENDIAN) { + need_swap = MACHINE_LITTLE_ENDIAN; + } + + if (need_swap) { + x = php_pack_reverse_int64(x); + } + if (type == 'q') { v = (int64_t) x; - } else if ((type == 'J' && MACHINE_LITTLE_ENDIAN) || (type == 'P' && !MACHINE_LITTLE_ENDIAN)) { - v = php_pack_reverse_int64(x); } else { v = x; } @@ -1078,9 +1347,9 @@ PHP_FUNCTION(unpack) { float v; - if (type == 'g') { + if (type == 'g' || endian == PHP_LITTLE_ENDIAN) { v = php_pack_parse_float(1, &input[inputpos]); - } else if (type == 'G') { + } else if (type == 'G' || endian == PHP_BIG_ENDIAN) { v = php_pack_parse_float(0, &input[inputpos]); } else { memcpy(&v, &input[inputpos], sizeof(float)); @@ -1096,9 +1365,9 @@ PHP_FUNCTION(unpack) case 'E': /* big endian float */ { double v; - if (type == 'e') { + if (type == 'e' || endian == PHP_LITTLE_ENDIAN) { v = php_pack_parse_double(1, &input[inputpos]); - } else if (type == 'E') { + } else if (type == 'E' || endian == PHP_BIG_ENDIAN) { v = php_pack_parse_double(0, &input[inputpos]); } else { memcpy(&v, &input[inputpos], sizeof(double)); diff --git a/ext/standard/tests/strings/pack_endian_modifiers.phpt b/ext/standard/tests/strings/pack_endian_modifiers.phpt new file mode 100644 index 0000000000000..667da077b8241 --- /dev/null +++ b/ext/standard/tests/strings/pack_endian_modifiers.phpt @@ -0,0 +1,335 @@ +--TEST-- +pack()/unpack() endianness modifiers +--SKIPIF-- + +--FILE-- +", 0x0102))); +var_dump(bin2hex(pack("S>", 0x0102))); + +var_dump(pack("s<", 0x0102) === pack("v", 0x0102)); +var_dump(pack("S<", 0x0102) === pack("v", 0x0102)); + +var_dump(pack("s>", 0x0102) === pack("n", 0x0102)); +var_dump(pack("S>", 0x0102) === pack("n", 0x0102)); + +var_dump(bin2hex(pack("l<", 0x01020304))); +var_dump(bin2hex(pack("L<", 0x01020304))); + +var_dump(bin2hex(pack("l>", 0x01020304))); +var_dump(bin2hex(pack("L>", 0x01020304))); + +var_dump(pack("l<", 0x01020304) === pack("V", 0x01020304)); +var_dump(pack("L<", 0x01020304) === pack("V", 0x01020304)); + +var_dump(pack("l>", 0x01020304) === pack("N", 0x01020304)); +var_dump(pack("L>", 0x01020304) === pack("N", 0x01020304)); + +var_dump(bin2hex(pack("q<", 0x0102030405060708))); +var_dump(bin2hex(pack("Q<", 0x0102030405060708))); + +var_dump(bin2hex(pack("q>", 0x0102030405060708))); +var_dump(bin2hex(pack("Q>", 0x0102030405060708))); + +var_dump(pack("q<", 0x0102030405060708) === pack("P", 0x0102030405060708)); +var_dump(pack("Q<", 0x0102030405060708) === pack("P", 0x0102030405060708)); + +var_dump(pack("q>", 0x0102030405060708) === pack("J", 0x0102030405060708)); +var_dump(pack("Q>", 0x0102030405060708) === pack("J", 0x0102030405060708)); + +// === Integer unpack with endianness modifiers === + +print_r(unpack("s<", "\x02\x01")); +print_r(unpack("S<", "\x02\x01")); + +print_r(unpack("s>", "\x01\x02")); +print_r(unpack("S>", "\x01\x02")); + +print_r(unpack("s<", "\xfe\xff")); // -2 in little-endian +print_r(unpack("s>", "\xff\xfe")); // -2 in big-endian + +print_r(unpack("l<", "\x04\x03\x02\x01")); +print_r(unpack("L<", "\x04\x03\x02\x01")); + +print_r(unpack("l>", "\x01\x02\x03\x04")); +print_r(unpack("L>", "\x01\x02\x03\x04")); + +print_r(unpack("l<", "\xfe\xff\xff\xff")); // -2 in little-endian +print_r(unpack("l>", "\xff\xff\xff\xfe")); // -2 in big-endian + +print_r(unpack("q<", "\x08\x07\x06\x05\x04\x03\x02\x01")); +print_r(unpack("Q<", "\x08\x07\x06\x05\x04\x03\x02\x01")); + +print_r(unpack("q>", "\x01\x02\x03\x04\x05\x06\x07\x08")); +print_r(unpack("Q>", "\x01\x02\x03\x04\x05\x06\x07\x08")); + +print_r(unpack("q<", "\xfe\xff\xff\xff\xff\xff\xff\xff")); // -2 in little-endian +print_r(unpack("q>", "\xff\xff\xff\xff\xff\xff\xff\xfe")); // -2 in big-endian + +var_dump(bin2hex(pack("s<2", 0x0102, 0x0304))); +var_dump(bin2hex(pack("s>2", 0x0102, 0x0304))); + +print_r(unpack("s<2", "\x02\x01\x04\x03")); +print_r(unpack("s>2", "\x01\x02\x03\x04")); + +print_r(unpack("scount", "\x02\x01\x00\x00\x00\x05")); + +var_dump(pack("f<", 3.14) === pack("g", 3.14)); +var_dump(pack("f>", 3.14) === pack("G", 3.14)); + +var_dump(pack("d<", 3.14) === pack("e", 3.14)); +var_dump(pack("d>", 3.14) === pack("E", 3.14)); + +$packed_le = pack("g", 3.14); +$packed_be = pack("G", 3.14); +$unpacked_le = unpack("f<", $packed_le); +$unpacked_be = unpack("f>", $packed_be); +$unpacked_g = unpack("g", $packed_le); +$unpacked_G = unpack("G", $packed_be); +var_dump($unpacked_le[1] === $unpacked_g[1]); +var_dump($unpacked_be[1] === $unpacked_G[1]); + +$packed_le = pack("e", 3.14); +$packed_be = pack("E", 3.14); +$unpacked_le = unpack("d<", $packed_le); +$unpacked_be = unpack("d>", $packed_be); +$unpacked_e = unpack("e", $packed_le); +$unpacked_E = unpack("E", $packed_be); +var_dump($unpacked_le[1] === $unpacked_e[1]); +var_dump($unpacked_be[1] === $unpacked_E[1]); + +$machine_float = pack("f", 1.5); +var_dump(unpack("f", $machine_float)[1] === 1.5); + +$machine_double = pack("d", 1.5); +var_dump(unpack("d", $machine_double)[1] === 1.5); + +$inherent_formats = ['n<', 'v>', 'N<', 'V>', 'J<', 'P>']; +foreach ($inherent_formats as $fmt) { + try { + pack($fmt, 1); + echo "FAIL: Expected ValueError for pack('$fmt', 1)\n"; + } catch (ValueError $e) { + echo "pack('$fmt'): " . $e->getMessage() . "\n"; + } +} + +$inherent_float_formats = ['g<', 'G>', 'e<', 'E>']; +foreach ($inherent_float_formats as $fmt) { + try { + pack($fmt, 1.0); + echo "FAIL: Expected ValueError for pack('$fmt', 1.0)\n"; + } catch (ValueError $e) { + echo "pack('$fmt'): " . $e->getMessage() . "\n"; + } +} + +$unsupported_formats = ['c<', 'C>', 'a<', 'A>', 'h<', 'H>', 'i<', 'I>', 'x<', 'X>', '@<']; +foreach ($unsupported_formats as $fmt) { + try { + pack($fmt, 1); + echo "FAIL: Expected ValueError for pack('$fmt', 1)\n"; + } catch (ValueError $e) { + echo "pack('$fmt'): " . $e->getMessage() . "\n"; + } +} + +foreach (['n<', 'v>', 'N<', 'V>', 'J<', 'P>'] as $fmt) { + try { + unpack($fmt, "\x00\x00\x00\x00\x00\x00\x00\x00"); + echo "FAIL: Expected ValueError for unpack('$fmt', ...)\n"; + } catch (ValueError $e) { + echo "unpack('$fmt'): " . $e->getMessage() . "\n"; + } +} + +foreach (['g<', 'G>', 'e<', 'E>'] as $fmt) { + try { + unpack($fmt, "\x00\x00\x00\x00\x00\x00\x00\x00"); + echo "FAIL: Expected ValueError for unpack('$fmt', ...)\n"; + } catch (ValueError $e) { + echo "unpack('$fmt'): " . $e->getMessage() . "\n"; + } +} + +foreach (['c<', 'C>', 'a<', 'A>', 'h<', 'H>', 'i<', 'I>', 'x<', 'X>', '@<'] as $fmt) { + try { + unpack($fmt, "\x00\x00\x00\x00\x00\x00\x00\x00"); + echo "FAIL: Expected ValueError for unpack('$fmt', ...)\n"; + } catch (ValueError $e) { + echo "unpack('$fmt'): " . $e->getMessage() . "\n"; + } +} +?> +--EXPECT-- +string(4) "0201" +string(4) "0201" +string(4) "0102" +string(4) "0102" +bool(true) +bool(true) +bool(true) +bool(true) +string(8) "04030201" +string(8) "04030201" +string(8) "01020304" +string(8) "01020304" +bool(true) +bool(true) +bool(true) +bool(true) +string(16) "0807060504030201" +string(16) "0807060504030201" +string(16) "0102030405060708" +string(16) "0102030405060708" +bool(true) +bool(true) +bool(true) +bool(true) +Array +( + [1] => 258 +) +Array +( + [1] => 258 +) +Array +( + [1] => 258 +) +Array +( + [1] => 258 +) +Array +( + [1] => -2 +) +Array +( + [1] => -2 +) +Array +( + [1] => 16909060 +) +Array +( + [1] => 16909060 +) +Array +( + [1] => 16909060 +) +Array +( + [1] => 16909060 +) +Array +( + [1] => -2 +) +Array +( + [1] => -2 +) +Array +( + [1] => 72623859790382856 +) +Array +( + [1] => 72623859790382856 +) +Array +( + [1] => 72623859790382856 +) +Array +( + [1] => 72623859790382856 +) +Array +( + [1] => -2 +) +Array +( + [1] => -2 +) +string(8) "02010403" +string(8) "01020304" +Array +( + [1] => 258 + [2] => 772 +) +Array +( + [1] => 258 + [2] => 772 +) +Array +( + [value] => 258 + [count] => 5 +) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +pack('n<'): Endianness modifier '<' cannot be applied to format code 'n' which already has inherent endianness +pack('v>'): Endianness modifier '>' cannot be applied to format code 'v' which already has inherent endianness +pack('N<'): Endianness modifier '<' cannot be applied to format code 'N' which already has inherent endianness +pack('V>'): Endianness modifier '>' cannot be applied to format code 'V' which already has inherent endianness +pack('J<'): Endianness modifier '<' cannot be applied to format code 'J' which already has inherent endianness +pack('P>'): Endianness modifier '>' cannot be applied to format code 'P' which already has inherent endianness +pack('g<'): Endianness modifier '<' cannot be applied to format code 'g' which already has inherent endianness +pack('G>'): Endianness modifier '>' cannot be applied to format code 'G' which already has inherent endianness +pack('e<'): Endianness modifier '<' cannot be applied to format code 'e' which already has inherent endianness +pack('E>'): Endianness modifier '>' cannot be applied to format code 'E' which already has inherent endianness +pack('c<'): Endianness modifier is not supported for format code 'c' +pack('C>'): Endianness modifier is not supported for format code 'C' +pack('a<'): Endianness modifier is not supported for format code 'a' +pack('A>'): Endianness modifier is not supported for format code 'A' +pack('h<'): Endianness modifier is not supported for format code 'h' +pack('H>'): Endianness modifier is not supported for format code 'H' +pack('i<'): Endianness modifier is not supported for format code 'i' +pack('I>'): Endianness modifier is not supported for format code 'I' +pack('x<'): Endianness modifier is not supported for format code 'x' +pack('X>'): Endianness modifier is not supported for format code 'X' +pack('@<'): Endianness modifier is not supported for format code '@' +unpack('n<'): Endianness modifier '<' cannot be applied to format code 'n' which already has inherent endianness +unpack('v>'): Endianness modifier '>' cannot be applied to format code 'v' which already has inherent endianness +unpack('N<'): Endianness modifier '<' cannot be applied to format code 'N' which already has inherent endianness +unpack('V>'): Endianness modifier '>' cannot be applied to format code 'V' which already has inherent endianness +unpack('J<'): Endianness modifier '<' cannot be applied to format code 'J' which already has inherent endianness +unpack('P>'): Endianness modifier '>' cannot be applied to format code 'P' which already has inherent endianness +unpack('g<'): Endianness modifier '<' cannot be applied to format code 'g' which already has inherent endianness +unpack('G>'): Endianness modifier '>' cannot be applied to format code 'G' which already has inherent endianness +unpack('e<'): Endianness modifier '<' cannot be applied to format code 'e' which already has inherent endianness +unpack('E>'): Endianness modifier '>' cannot be applied to format code 'E' which already has inherent endianness +unpack('c<'): Endianness modifier is not supported for format code 'c' +unpack('C>'): Endianness modifier is not supported for format code 'C' +unpack('a<'): Endianness modifier is not supported for format code 'a' +unpack('A>'): Endianness modifier is not supported for format code 'A' +unpack('h<'): Endianness modifier is not supported for format code 'h' +unpack('H>'): Endianness modifier is not supported for format code 'H' +unpack('i<'): Endianness modifier is not supported for format code 'i' +unpack('I>'): Endianness modifier is not supported for format code 'I' +unpack('x<'): Endianness modifier is not supported for format code 'x' +unpack('X>'): Endianness modifier is not supported for format code 'X' +unpack('@<'): Endianness modifier is not supported for format code '@' diff --git a/ext/standard/tests/strings/pack_endian_modifiers_32.phpt b/ext/standard/tests/strings/pack_endian_modifiers_32.phpt new file mode 100644 index 0000000000000..cf67409ded65c --- /dev/null +++ b/ext/standard/tests/strings/pack_endian_modifiers_32.phpt @@ -0,0 +1,77 @@ +--TEST-- +pack()/unpack() endianness modifiers on 32-bit systems +--SKIPIF-- + 4) die("skip 32bit test only"); +?> +--FILE-- +", 0x0102))); +var_dump(bin2hex(pack("S>", 0x0102))); + +print_r(unpack("s<", "\x02\x01")); +print_r(unpack("S>", "\x01\x02")); + +var_dump(bin2hex(pack("l<", 0x01020304))); +var_dump(bin2hex(pack("L<", 0x01020304))); +var_dump(bin2hex(pack("l>", 0x01020304))); +var_dump(bin2hex(pack("L>", 0x01020304))); + +print_r(unpack("l<", "\x04\x03\x02\x01")); +print_r(unpack("L>", "\x01\x02\x03\x04")); + +$formats = ['q<', 'q>', 'Q<', 'Q>']; +foreach ($formats as $fmt) { + try { + pack($fmt, 0); + echo "FAIL: Expected ValueError for pack('$fmt', 0)\n"; + } catch (ValueError $e) { + echo "pack('$fmt'): " . $e->getMessage() . "\n"; + } +} + +foreach ($formats as $fmt) { + try { + unpack($fmt, "\x00\x00\x00\x00\x00\x00\x00\x00"); + echo "FAIL: Expected ValueError for unpack('$fmt', ...)\n"; + } catch (ValueError $e) { + echo "unpack('$fmt'): " . $e->getMessage() . "\n"; + } +} +?> +--EXPECT-- +string(4) "0201" +string(4) "0201" +string(4) "0102" +string(4) "0102" +Array +( + [1] => 258 +) +Array +( + [1] => 258 +) +string(8) "04030201" +string(8) "04030201" +string(8) "01020304" +string(8) "01020304" +Array +( + [1] => 16909060 +) +Array +( + [1] => 16909060 +) +pack('q<'): 64-bit format codes are not available for 32-bit versions of PHP +pack('q>'): 64-bit format codes are not available for 32-bit versions of PHP +pack('Q<'): 64-bit format codes are not available for 32-bit versions of PHP +pack('Q>'): 64-bit format codes are not available for 32-bit versions of PHP +unpack('q<'): 64-bit format codes are not available for 32-bit versions of PHP +unpack('q>'): 64-bit format codes are not available for 32-bit versions of PHP +unpack('Q<'): 64-bit format codes are not available for 32-bit versions of PHP +unpack('Q>'): 64-bit format codes are not available for 32-bit versions of PHP