diff --git a/NEWS b/NEWS index 8427273c9d9a5..9e1944e1fba38 100644 --- a/NEWS +++ b/NEWS @@ -137,6 +137,7 @@ PHP NEWS . Implement GH-15387 Pdo\Pgsql::setAttribute(PDO::ATTR_PREFETCH, 0) or Pdo\Pgsql::prepare(…, [ PDO::ATTR_PREFETCH => 0 ]) make fetch() lazy instead of storing the whole result set in memory (Guillaume Outters) + . Removed unused constants Pdo\Pgsql::TRANSACTION_* (vrana). - PDO_SQLITE: . throw on null bytes / resolve GH-13952 (divinity76). @@ -190,6 +191,8 @@ PHP NEWS . Fix namespace handling of WSDL and XML schema in SOAP, fixing at least GH-16320 and bug #68576. (nielsdos) . Fixed bug #70951 (Segmentation fault on invalid WSDL cache). (nielsdos) + . Implement request #55503 (Extend __getTypes to support enumerations). + (nielsdos, datibbaw) - Sockets: . Added IPPROTO_ICMP/IPPROTO_ICMPV6 to create raw socket for ICMP usage. @@ -242,6 +245,10 @@ PHP NEWS - Tests: . Allow to shuffle tests even in non-parallell mode. (dhuang00) +- Tidy: + . tidy::__construct/parseFile/parseString methods throw an exception if + the configuration argument is invalid. (David Carlier) + - Windows: . Fixed bug GH-10992 (Improper long path support for relative paths). (cmb, nielsdos) diff --git a/UPGRADING b/UPGRADING index 46284f16ce3f4..1f55fa8ce8946 100644 --- a/UPGRADING +++ b/UPGRADING @@ -42,8 +42,8 @@ PHP 8.5 UPGRADE NOTES have run and the output handlers have been cleaned up. This is a consequence of fixing GH-18033. . Traits are now bound before the parent class. This is a subtle behavioral - change, but should closer match user expectations, demonstrated by GH-15753 - and GH-16198. + change, but should more closely match user expectations, demonstrated by + GH-15753 and GH-16198. - FileInfo: . finfo_file() and finfo::file() now throws a ValueError instead of a @@ -91,7 +91,7 @@ PHP 8.5 UPGRADE NOTES argument when fetching into an object, will now throw an Error. . The value of the constants PDO::FETCH_GROUP, PDO::FETCH_UNIQUE, PDO::FETCH_CLASSTYPE, PDO::FETCH_PROPS_LATE, and PDO::FETCH_SERIALIZE - has changed. + have changed. . A ValueError is now thrown if PDO::FETCH_PROPS_LATE is used with a fetch mode different than PDO::FETCH_CLASS, consistent with other fetch flags. . A ValueError is now thrown if PDO::FETCH_INTO is used as a fetch mode in @@ -101,6 +101,9 @@ PHP 8.5 UPGRADE NOTES . A ValueError is now thrown when trying to set a cursor name that is too long on a PDOStatement resulting from the Firebird driver. +- PDO_PGSQL: + . Removed unused constants Pdo\Pgsql::TRANSACTION_*. + - Session: . Attempting to write session data where $_SESSION has a key containing the pipe character will now emit a warning instead of silently failing. @@ -158,7 +161,7 @@ PHP 8.5 UPGRADE NOTES CURLINFO_USED_PROXY gets zero set if no proxy was used in the previous transfer or a non-zero value if a proxy was used. CURLINFO_HTTPAUTH_USED and CURLINFO_PROXYAUTH_USED get bitmasks - indicating the http and proxy authentication methods that were + indicating the HTTP and proxy authentication methods that were used in the previous request. See CURLAUTH_* constants for possible values. . Added CURLOPT_INFILESIZE_LARGE Curl option, which is a safe @@ -168,10 +171,10 @@ PHP 8.5 UPGRADE NOTES accepts the largest integer value the system can handle. . Added CURLFOLLOW_OBEYCODE, CURLFOLLOW_FIRSTONLY and CURLFOLLOW_ALL values for CURLOPT_FOLLOWLOCATION curl_easy_setopt option. - CURLFOLLOW_OBEYCODE to follow more strictly in regard of redirect + CURLFOLLOW_OBEYCODE to follow more strictly in regard to redirect if they are allowed. CURLFOLLOW_FIRSTONLY to follow only the - first redirect thus if there any follow up redirect, it won't go - any further. CURLFOLLOW_ALL is equivalent to set CURLOPT_FOLLOWLOCATION + first redirect thus if there is any follow up redirect, it won't go + any further. CURLFOLLOW_ALL is equivalent to setting CURLOPT_FOLLOWLOCATION to true. - DOM: @@ -187,13 +190,16 @@ PHP 8.5 UPGRADE NOTES number formats. . Added Locale::addLikelySubtags and Locale::minimizeSubtags to handle likely tags on a given locale. - . Added IntlListFormatter class to format, order, punctuates + . Added IntlListFormatter class to format, order, and punctuate a list of items with a given locale, IntlListFormatter::TYPE_AND, IntlListFormatter::TYPE_OR, IntlListFormatter::TYPE_UNITS operands and IntlListFormatter::WIDTH_WIDE, IntlListFormatter::WIDTH_SHORT and IntlListFormatter::WIDTH_NARROW widths. It is supported from icu 67. +- SOAP: + . Enumeration cases are now dumped in __getTypes(). + - XSL: . The $namespace argument of XSLTProcessor::getParameter(), XSLTProcessor::setParameter() and XSLTProcessor::removeParameter() @@ -247,7 +253,7 @@ PHP 8.5 UPGRADE NOTES was actually never possible. - LDAP: - . ldap_get_option() now accept a NULL connection, as ldap_set_option(), + . ldap_get_option() now accepts a NULL connection, as ldap_set_option(), to allow retrieval of global options. - libxml: @@ -260,7 +266,7 @@ PHP 8.5 UPGRADE NOTES . PDO::pgsqlCopyFromArray also supports inputs as Iterable. . Pdo\Pgsql::setAttribute and Pdo\Pgsql::prepare supports PDO::ATTR_PREFETCH sets to 0 which set to lazy fetch mode. - In this mode, statements cannot be run parallely. + In this mode, statements cannot be run in parallel. - PDO_SQLITE: . SQLite PDO::quote() will now throw an exception or emit a warning, @@ -287,7 +293,7 @@ PHP 8.5 UPGRADE NOTES are enum cases rather than normal class constants. - Session: - . session_start is stricter in regard of the option argument. + . session_start is stricter in regard to the option argument. It throws a ValueError if the whole is not a hashmap or a TypeError if read_on_close value is not a valid type compatible with int. @@ -304,7 +310,7 @@ PHP 8.5 UPGRADE NOTES ValueError if the port is lower than 0 or greater than 65535, also if any of the hints array entry is indexes numerically. . socket_addrinfo_lookup throws a TypeError if any of the hints - values cannot be cast to a int and can throw a ValueError if + values cannot be cast to int and can throw a ValueError if any of these values overflow. . socket_set_option with MCAST_LEAVE_GROUP/MCAST_LEAVE_SOURCE_GROUP options will throw an exception if the value isn't a valid object @@ -313,6 +319,12 @@ PHP 8.5 UPGRADE NOTES . socket_getsockname gets the interface index and its string representation with AF_PACKET socket. +- Tidy: + . tidy::__construct/parseFile/parseString now throws a ValueError + if the configuration contains an invalid or set a read-only + internal entry, a TypeError contains, at least, one element + when the key is not a string. + - Zlib: . The "use_include_path" argument for the gzfile, gzopen and readgzfile functions had been changed diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 82bd53db51872..574d5f0827ff7 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -46,6 +46,10 @@ PHP 8.5 INTERNALS UPGRADE NOTES without duplicate build rules. It is up to the SAPI maintainers to ensure that appropriate build rules are created. +- Linux build system changes + . libdir is properly set when --libdir (ex: /usr/lib64) and --with-libdir (ex lib64) + configure options are used to ${libdir}/php (ex: /usr/lib64/php) + ======================== 3. Module changes ======================== diff --git a/Zend/tests/e_strict-deprecated.phpt b/Zend/tests/e_strict-deprecated.phpt index 71666e75c22f4..5017d45a03a1d 100644 --- a/Zend/tests/e_strict-deprecated.phpt +++ b/Zend/tests/e_strict-deprecated.phpt @@ -10,5 +10,5 @@ var_dump(E_STRICT); --EXPECTF-- int(30719) -Deprecated: Constant E_STRICT is deprecated in %s on line %d +Deprecated: Constant E_STRICT is deprecated since 8.4, the error level was removed in %s on line %d int(2048) diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt index 5b0a9bf05f0f0..407bad6077057 100644 --- a/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt +++ b/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt @@ -10,4 +10,4 @@ class MyDateTimeZone extends DateTimeZone } ?> --EXPECTF-- -Fatal error: Declaration of MyDateTimeZone::getTransitions(): array|false must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false in %s on line %d +Fatal error: Declaration of MyDateTimeZone::getTransitions(): array|false must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = 2147483647): array|false in %s on line %d diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index b9f3bc015217c..c7fa39cc14336 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -3417,7 +3417,7 @@ ZEND_API zend_mm_storage *zend_mm_get_storage(zend_mm_heap *heap) #if ZEND_MM_STORAGE return heap->storage; #else - return NULL + return NULL; #endif } diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index eb464772c100c..a4d6b28c0094a 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -21,6 +21,7 @@ #define ZEND_ATTRIBUTES_H #include "zend_compile.h" +#include "zend_constants.h" #define ZEND_ATTRIBUTE_TARGET_CLASS (1<<0) #define ZEND_ATTRIBUTE_TARGET_FUNCTION (1<<1) @@ -126,6 +127,12 @@ static zend_always_inline zend_attribute *zend_add_class_constant_attribute(zend return zend_add_attribute(&c->attributes, name, argc, flags, 0, 0); } +static zend_always_inline zend_attribute *zend_add_global_constant_attribute(zend_constant *c, zend_string *name, uint32_t argc) +{ + uint32_t flags = ZEND_CONSTANT_MODULE_NUMBER(c) == PHP_USER_CONSTANT ? 0 : ZEND_ATTRIBUTE_PERSISTENT; + return zend_add_attribute(&c->attributes, name, argc, flags, 0, 0); +} + void zend_register_attribute_ce(void); void zend_attributes_shutdown(void); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 9a184bd931584..ffad8315ae40d 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -85,7 +85,8 @@ static void copy_zend_constant(zval *zv) c->filename = zend_string_copy(c->filename); } if (c->attributes != NULL) { - c->attributes = zend_array_dup(c->attributes); + // Use the same attributes table + GC_ADDREF(c->attributes); } if (Z_TYPE(c->value) == IS_STRING) { Z_STR(c->value) = zend_string_dup(Z_STR(c->value), 1); diff --git a/Zend/zend_constants.stub.php b/Zend/zend_constants.stub.php index ee67966c0151c..763b207641f69 100644 --- a/Zend/zend_constants.stub.php +++ b/Zend/zend_constants.stub.php @@ -71,9 +71,9 @@ /** * @var int * @cvalue E_STRICT - * @deprecated * @todo Remove in PHP 9.0 */ +#[\Deprecated(since: '8.4', message: 'the error level was removed')] const E_STRICT = UNKNOWN; /** diff --git a/Zend/zend_constants_arginfo.h b/Zend/zend_constants_arginfo.h index 8d42345ad69bc..d381bcf64ee18 100644 --- a/Zend/zend_constants_arginfo.h +++ b/Zend/zend_constants_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 65be08c1bdace83ad1fa1175fc824262e07eac2a */ + * Stub hash: 5e224893a5fb72b3f93249235c2a1634233ce505 */ static void register_zend_constants_symbols(int module_number) { @@ -26,4 +26,18 @@ static void register_zend_constants_symbols(int module_number) REGISTER_BOOL_CONSTANT("TRUE", true, CONST_PERSISTENT); REGISTER_BOOL_CONSTANT("FALSE", false, CONST_PERSISTENT); REGISTER_NULL_CONSTANT("NULL", CONST_PERSISTENT); + + zend_constant *const_E_STRICT = zend_hash_str_find_ptr(EG(zend_constants), "E_STRICT", sizeof("E_STRICT") - 1); + + zend_attribute *attribute_Deprecated_const_E_STRICT_0 = zend_add_global_constant_attribute(const_E_STRICT, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_E_STRICT_0_arg0; + zend_string *attribute_Deprecated_const_E_STRICT_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_E_STRICT_0_arg0, attribute_Deprecated_const_E_STRICT_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_E_STRICT_0->args[0].value, &attribute_Deprecated_const_E_STRICT_0_arg0); + attribute_Deprecated_const_E_STRICT_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_E_STRICT_0_arg1; + zend_string *attribute_Deprecated_const_E_STRICT_0_arg1_str = zend_string_init("the error level was removed", strlen("the error level was removed"), 1); + ZVAL_STR(&attribute_Deprecated_const_E_STRICT_0_arg1, attribute_Deprecated_const_E_STRICT_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_E_STRICT_0->args[1].value, &attribute_Deprecated_const_E_STRICT_0_arg1); + attribute_Deprecated_const_E_STRICT_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 879141d1a139e..140734d1b9602 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -378,6 +378,7 @@ static zend_always_inline zend_result zendi_try_convert_scalar_to_number(zval *o static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval *op, bool *failed) /* {{{ */ { *failed = 0; +try_again: switch (Z_TYPE_P(op)) { case IS_NULL: case IS_FALSE: @@ -448,6 +449,14 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval * case IS_ARRAY: *failed = 1; return 0; + case IS_REFERENCE: + op = Z_REFVAL_P(op); + if (Z_TYPE_P(op) == IS_LONG) { + return Z_LVAL_P(op); + } else { + goto try_again; + } + break; EMPTY_SWITCH_DEFAULT_CASE() } } diff --git a/build/gen_stub.php b/build/gen_stub.php index 5e06a53172e07..13ef9e60f334d 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2622,6 +2622,13 @@ public function __construct( ?ExposedDocComment $exposedDocComment, bool $isFileCacheAllowed ) { + foreach ($attributes as $attr) { + if ($attr->class === "Deprecated") { + $isDeprecated = true; + break; + } + } + $this->name = $name; $this->value = $value; $this->valueString = $valueString; @@ -2915,17 +2922,11 @@ protected function getFlagsByPhpVersion(): array { $flags = parent::getFlagsByPhpVersion(); + // $this->isDeprecated also accounts for any #[\Deprecated] attributes if ($this->isDeprecated) { $flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_DEPRECATED", PHP_80_VERSION_ID); } - foreach ($this->attributes as $attr) { - if ($attr->class === "Deprecated") { - $flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_DEPRECATED", PHP_80_VERSION_ID); - break; - } - } - if ($this->flags & Modifiers::FINAL) { $flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_FINAL", PHP_81_VERSION_ID); } @@ -4340,7 +4341,7 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri $cond, $this->isUndocumentable, $this->getMinimumPhpVersionIdCompatibility(), - [] + AttributeInfo::createFromGroups($stmt->attrGroups) ); } continue; @@ -5177,7 +5178,9 @@ static function (FuncInfo $funcInfo) use ($fileInfo, &$generatedFunctionDeclarat $php80MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_80_VERSION_ID; if ($fileInfo->generateClassEntries) { - if ($attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null)) { + $attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null); + $attributeInitializationCode .= generateGlobalConstantAttributeInitialization($fileInfo->constInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null); + if ($attributeInitializationCode) { if (!$php80MinimumCompatibility) { $attributeInitializationCode = "\n#if (PHP_VERSION_ID >= " . PHP_80_VERSION_ID . ")" . $attributeInitializationCode . "#endif\n"; } @@ -5289,6 +5292,51 @@ static function (FuncInfo $funcInfo) use ($allConstInfos, $phpVersionIdMinimumCo ); } +/** + * @param iterable $constInfos + * @param array $allConstInfos + */ +function generateGlobalConstantAttributeInitialization( + iterable $constInfos, + array $allConstInfos, + ?int $phpVersionIdMinimumCompatibility, + ?string $parentCond = null +): string { + $isConditional = false; + if ($phpVersionIdMinimumCompatibility !== null && $phpVersionIdMinimumCompatibility < PHP_85_VERSION_ID) { + $isConditional = true; + $phpVersionIdMinimumCompatibility = PHP_85_VERSION_ID; + } + $code = generateCodeWithConditions( + $constInfos, + "", + static function (ConstInfo $constInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility) { + if ($constInfo->attributes === []) { + return null; + } + $constName = str_replace('\\', '\\\\', $constInfo->name->__toString()); + $constVarName = 'const_' . $constName; + + $code .= "\tzend_constant *$constVarName = zend_hash_str_find_ptr(EG(zend_constants), \"" . $constName . "\", sizeof(\"" . $constName . "\") - 1);\n"; + foreach ($constInfo->attributes as $key => $attribute) { + $code .= $attribute->generateCode( + "zend_add_global_constant_attribute($constVarName", + $constVarName . "_$key", + $allConstInfos, + $phpVersionIdMinimumCompatibility + ); + } + + return $code; + }, + $parentCond + ); + if ($code && $isConditional) { + return "\n#if (PHP_VERSION_ID >= " . PHP_85_VERSION_ID . ")\n" . $code . "#endif\n"; + } + return $code; +} + /** * @param iterable $constInfos * @param array $allConstInfos @@ -6030,7 +6078,7 @@ function initPhpParser() { } $isInitialized = true; - $version = "5.3.1"; + $version = "5.5.0"; $phpParserDir = __DIR__ . "/PHP-Parser-$version"; if (!is_dir($phpParserDir)) { installPhpParser($version, $phpParserDir); diff --git a/configure.ac b/configure.ac index 61d1c350a82be..e4bd8162a2ebc 100644 --- a/configure.ac +++ b/configure.ac @@ -1324,11 +1324,15 @@ AS_VAR_IF([program_prefix], [NONE], [program_prefix=]) AS_VAR_IF([program_suffix], [NONE], [program_suffix=]) orig_libdir=$libdir + +dnl First for unexpanded (default), second for expanded (from options) AS_CASE([$libdir], - ['${exec_prefix}/lib'], [libdir=$libdir/php]) + ['${exec_prefix}/lib'], [libdir=$libdir/php], + [${exec_prefix}/${PHP_LIBDIR}], [libdir=$libdir/php]) AS_CASE([$(eval echo $datadir)], - ['${prefix}/share'], [datadir=$datadir/php]) + ['${prefix}/share'], [datadir=$datadir/php], + [${prefix}/share], [datadir=$datadir/php]) phptempdir=$(pwd)/libs diff --git a/ext/bcmath/libbcmath/src/convert.c b/ext/bcmath/libbcmath/src/convert.c index 5438b4c1c44e5..ca5b84efde1b2 100644 --- a/ext/bcmath/libbcmath/src/convert.c +++ b/ext/bcmath/libbcmath/src/convert.c @@ -17,22 +17,22 @@ #include "bcmath.h" #include "convert.h" #include "private.h" -#include "simd.h" +#include "xsse.h" char *bc_copy_and_toggle_bcd(char *restrict dest, const char *source, const char *source_end) { const size_t bulk_shift = SWAR_REPEAT('0'); -#ifdef HAVE_BC_SIMD_128 +#ifdef XSSE2 /* SIMD SSE2 or NEON bulk shift + copy */ - bc_simd_128_t shift_vector = bc_simd_set_8x16('0'); - while (source + sizeof(bc_simd_128_t) <= source_end) { - bc_simd_128_t bytes = bc_simd_load_8x16((const bc_simd_128_t *) source); - bytes = bc_simd_xor_8x16(bytes, shift_vector); - bc_simd_store_8x16((bc_simd_128_t *) dest, bytes); - - source += sizeof(bc_simd_128_t); - dest += sizeof(bc_simd_128_t); + __m128i shift_vector = _mm_set1_epi8('0'); + while (source + sizeof(__m128i) <= source_end) { + __m128i bytes = _mm_loadu_si128((const __m128i *) source); + bytes = _mm_xor_si128(bytes, shift_vector); + _mm_storeu_si128((__m128i *) dest, bytes); + + source += sizeof(__m128i); + dest += sizeof(__m128i); } #endif diff --git a/ext/bcmath/libbcmath/src/simd.h b/ext/bcmath/libbcmath/src/simd.h deleted file mode 100644 index af38f8349618c..0000000000000 --- a/ext/bcmath/libbcmath/src/simd.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Saki Takamachi | - +----------------------------------------------------------------------+ -*/ - - -#ifndef _BCMATH_SIMD_H_ -#define _BCMATH_SIMD_H_ - -#ifdef __SSE2__ -# include - typedef __m128i bc_simd_128_t; -# define HAVE_BC_SIMD_128 -# define bc_simd_set_8x16(x) _mm_set1_epi8(x) -# define bc_simd_load_8x16(ptr) _mm_loadu_si128((const __m128i *) (ptr)) -# define bc_simd_xor_8x16(a, b) _mm_xor_si128(a, b) -# define bc_simd_store_8x16(ptr, val) _mm_storeu_si128((__m128i *) (ptr), val) -# define bc_simd_add_8x16(a, b) _mm_add_epi8(a, b) -# define bc_simd_cmpeq_8x16(a, b) _mm_cmpeq_epi8(a, b) -# define bc_simd_cmplt_8x16(a, b) _mm_cmplt_epi8(a, b) -# define bc_simd_movemask_8x16(a) _mm_movemask_epi8(a) - -#elif defined(__aarch64__) || defined(_M_ARM64) -# include - typedef int8x16_t bc_simd_128_t; -# define HAVE_BC_SIMD_128 -# define bc_simd_set_8x16(x) vdupq_n_s8(x) -# define bc_simd_load_8x16(ptr) vld1q_s8((const int8_t *) (ptr)) -# define bc_simd_xor_8x16(a, b) veorq_s8(a, b) -# define bc_simd_store_8x16(ptr, val) vst1q_s8((int8_t *) (ptr), val) -# define bc_simd_add_8x16(a, b) vaddq_s8(a, b) -# define bc_simd_cmpeq_8x16(a, b) (vreinterpretq_s8_u8(vceqq_s8(a, b))) -# define bc_simd_cmplt_8x16(a, b) (vreinterpretq_s8_u8(vcltq_s8(a, b))) - static inline int bc_simd_movemask_8x16(int8x16_t vec) - { - /** - * based on code from - * https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon - */ - uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(vreinterpretq_u8_s8(vec), 7)); - uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); - uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); - uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); - return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8); - } -#endif - -#endif diff --git a/ext/bcmath/libbcmath/src/str2num.c b/ext/bcmath/libbcmath/src/str2num.c index 945de0cf60003..11d71ae492ad6 100644 --- a/ext/bcmath/libbcmath/src/str2num.c +++ b/ext/bcmath/libbcmath/src/str2num.c @@ -32,7 +32,7 @@ #include "bcmath.h" #include "convert.h" #include "private.h" -#include "simd.h" +#include "xsse.h" #include #include @@ -40,20 +40,20 @@ static inline const char *bc_count_digits(const char *str, const char *end) { /* Process in bulk */ -#ifdef HAVE_BC_SIMD_128 - const bc_simd_128_t offset = bc_simd_set_8x16((signed char) (SCHAR_MIN - '0')); +#ifdef XSSE2 + const __m128i offset = _mm_set1_epi8((signed char) (SCHAR_MIN - '0')); /* we use the less than comparator, so add 1 */ - const bc_simd_128_t threshold = bc_simd_set_8x16(SCHAR_MIN + ('9' + 1 - '0')); + const __m128i threshold = _mm_set1_epi8(SCHAR_MIN + ('9' + 1 - '0')); - while (str + sizeof(bc_simd_128_t) <= end) { - bc_simd_128_t bytes = bc_simd_load_8x16((const bc_simd_128_t *) str); + while (str + sizeof(__m128i) <= end) { + __m128i bytes = _mm_loadu_si128((const __m128i *) str); /* Wrapping-add the offset to the bytes, such that all bytes below '0' are positive and others are negative. * More specifically, '0' will be -128 and '9' will be -119. */ - bytes = bc_simd_add_8x16(bytes, offset); + bytes = _mm_add_epi8(bytes, offset); /* Now mark all bytes that are <= '9', i.e. <= -119, i.e. < -118, i.e. the threshold. */ - bytes = bc_simd_cmplt_8x16(bytes, threshold); + bytes = _mm_cmplt_epi8(bytes, threshold); - int mask = bc_simd_movemask_8x16(bytes); + int mask = _mm_movemask_epi8(bytes); if (mask != 0xffff) { /* At least one of the bytes is not within range. Move to the first offending byte. */ #ifdef PHP_HAVE_BUILTIN_CTZL @@ -63,7 +63,7 @@ static inline const char *bc_count_digits(const char *str, const char *end) #endif } - str += sizeof(bc_simd_128_t); + str += sizeof(__m128i); } #endif @@ -77,19 +77,19 @@ static inline const char *bc_count_digits(const char *str, const char *end) static inline const char *bc_skip_zero_reverse(const char *scanner, const char *stop) { /* Check in bulk */ -#ifdef HAVE_BC_SIMD_128 - const bc_simd_128_t c_zero_repeat = bc_simd_set_8x16('0'); - while (scanner - sizeof(bc_simd_128_t) >= stop) { - scanner -= sizeof(bc_simd_128_t); - bc_simd_128_t bytes = bc_simd_load_8x16((const bc_simd_128_t *) scanner); +#ifdef XSSE2 + const __m128i c_zero_repeat = _mm_set1_epi8('0'); + while (scanner - sizeof(__m128i) >= stop) { + scanner -= sizeof(__m128i); + __m128i bytes = _mm_loadu_si128((const __m128i *) scanner); /* Checks if all numeric strings are equal to '0'. */ - bytes = bc_simd_cmpeq_8x16(bytes, c_zero_repeat); + bytes = _mm_cmpeq_epi8(bytes, c_zero_repeat); - int mask = bc_simd_movemask_8x16(bytes); + int mask = _mm_movemask_epi8(bytes); /* The probability of having 16 trailing 0s in a row is very low, so we use EXPECTED. */ if (EXPECTED(mask != 0xffff)) { /* Move the pointer back and check each character in loop. */ - scanner += sizeof(bc_simd_128_t); + scanner += sizeof(__m128i); break; } } diff --git a/ext/bcmath/libbcmath/src/xsse.h b/ext/bcmath/libbcmath/src/xsse.h new file mode 100644 index 0000000000000..a0d86e335e3f7 --- /dev/null +++ b/ext/bcmath/libbcmath/src/xsse.h @@ -0,0 +1,1965 @@ +/******************************************************************************** + * MIT License + * Copyright (c) 2025 Saki Takamachi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ********************************************************************************* + * This is a wrapper library that allows you to use SSE2, SSE3, SSSE3, SSE4.1, + * and SSE4.2 APIs directly with NEON. + * Please note that APIs using `__m64`, `__m128`, or `__m128d` are not supported. + * + * In an SSE environment, if `__SSE2__` is defined, the `XSSE2` macro will be + * defined as well. + * Similarly, `XSSE3` through `XSSE4_2` will also be defined if their + * corresponding features are available.(Note that plain `SSE` is not supported.) + * + * In a NEON environment, all of the macros `XSSE2`, `XSSE3`, `XSSSE3`, + * `XSSE4_1`, and `XSSE4_2` are defined. + *********************************************************************************/ + + +#ifndef XSSE_H +#define XSSE_H + +#define XSSE_VERSION 20000 + +#ifdef _MSC_VER +# define XSSE_FORCE_INLINE __forceinline +# define XSSE_UNREACHABLE() __assume(0) +# define XSSE_EXPECTED(x) (x) +# define XSSE_UNEXPECTED(x) (x) +# define XSSE_ATTR_CONST +# define XSSE_MEMCPY(p, vp, s) memcpy((p), (vp), (s)) +#elif defined(__GNUC__) || defined(__clang__) +# define XSSE_FORCE_INLINE inline __attribute__((always_inline)) +# define XSSE_UNREACHABLE() __builtin_unreachable() +# define XSSE_EXPECTED(x) __builtin_expect(!!(x), 1) +# define XSSE_UNEXPECTED(x) __builtin_expect(!!(x), 0) +# define XSSE_ATTR_CONST __attribute__((const)) +# define XSSE_HAS_MACRO_EXTENSION +# define XSSE_MEMCPY(p, vp, s) __builtin_memcpy((p), (vp), (s)) +# ifdef __OPTIMIZE__ +# define XSSE_IS_OPTIMIZE +# endif +#else +# define XSSE_FORCE_INLINE inline +# define XSSE_UNREACHABLE() do {} while (0) +# define XSSE_EXPECTED(x) (x) +# define XSSE_UNEXPECTED(x) (x) +# define XSSE_ATTR_CONST +# define XSSE_MEMCPY(p, vp, s) memcpy((p), (vp), (s)) +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define XSSE_ATTR_OPTIMIZE_O2 __attribute__((optimize("O2"))) +#else +# define XSSE_ATTR_OPTIMIZE_O2 +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#include +#include +typedef int8x16_t __m128i; +#endif + + +/***************************************************************************** + * * + * SSE2 * + * * + *****************************************************************************/ + +#if defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) +#include +#define XSSE2 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#define XSSE2 + +/***************************************************************************** + * Load / Store * + *****************************************************************************/ + +#define _mm_set_epi8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) \ + ((int8x16_t) { \ + (int8_t) (x15), (int8_t) (x14), (int8_t) (x13), (int8_t) (x12), \ + (int8_t) (x11), (int8_t) (x10), (int8_t) (x9), (int8_t) (x8), \ + (int8_t) (x7), (int8_t) (x6), (int8_t) (x5), (int8_t) (x4), \ + (int8_t) (x3), (int8_t) (x2), (int8_t) (x1), (int8_t) (x0) }) +#define _mm_set_epi16(x0, x1, x2, x3, x4, x5, x6, x7) \ + (vreinterpretq_s8_s16((int16x8_t) { \ + (int16_t) (x7), (int16_t) (x6), (int16_t) (x5), (int16_t) (x4), \ + (int16_t) (x3), (int16_t) (x2), (int16_t) (x1), (int16_t) (x0) })) +#define _mm_set_epi32(x0, x1, x2, x3) \ + (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x3), (int32_t) (x2), (int32_t) (x1), (int32_t) (x0) })) +#define _mm_set_epi64x(x0, x1) (vreinterpretq_s8_s64((int64x2_t) { (int64_t) (x1), (int64_t) (x0) })) +#define _mm_set1_epi8(x) (vdupq_n_s8((int8_t) (x))) +#define _mm_set1_epi16(x) (vreinterpretq_s8_s16(vdupq_n_s16((int16_t) (x)))) +#define _mm_set1_epi32(x) (vreinterpretq_s8_s32(vdupq_n_s32((int32_t) (x)))) +#define _mm_set1_epi64x(x) (vreinterpretq_s8_s64(vdupq_n_s64((int64_t) (x)))) + +#define _mm_setr_epi8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) \ + ((int8x16_t) { \ + (int8_t) (x0), (int8_t) (x1), (int8_t) (x2), (int8_t) (x3), \ + (int8_t) (x4), (int8_t) (x5), (int8_t) (x6), (int8_t) (x7), \ + (int8_t) (x8), (int8_t) (x9), (int8_t) (x10), (int8_t) (x11), \ + (int8_t) (x12), (int8_t) (x13), (int8_t) (x14), (int8_t) (x15) }) +#define _mm_setr_epi16(x0, x1, x2, x3, x4, x5, x6, x7) \ + (vreinterpretq_s8_s16((int16x8_t) { \ + (int16_t) (x0), (int16_t) (x1), (int16_t) (x2), (int16_t) (x3), \ + (int16_t) (x4), (int16_t) (x5), (int16_t) (x6), (int16_t) (x7) })) +#define _mm_setr_epi32(x0, x1, x2, x3) \ + (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x0), (int32_t) (x1), (int32_t) (x2), (int32_t) (x3) })) + +#define _mm_setzero_si128() (vdupq_n_s8(0)) +#define _mm_undefined_si128() _mm_setzero_si128() + +#define _mm_load_si128(x) (vld1q_s8((const int8_t*) (x))) +static XSSE_FORCE_INLINE __m128i _mm_loadu_si16(const void *ptr) +{ + int16_t val; + XSSE_MEMCPY(&val, ptr, 2); + return vreinterpretq_s8_s16((int16x8_t) { + (int16_t) val, (int16_t) 0, (int16_t) 0, (int16_t) 0, + (int16_t) 0, (int16_t) 0, (int16_t) 0, (int16_t) 0 + }); +} +static XSSE_FORCE_INLINE __m128i _mm_loadu_si32(const void *ptr) +{ + int32_t val; + XSSE_MEMCPY(&val, ptr, 4); + return vreinterpretq_s8_s32((int32x4_t) { + (int32_t) val, (int32_t) 0, (int32_t) 0, (int32_t) 0 + }); +} +static XSSE_FORCE_INLINE __m128i _mm_loadu_si64(const void *ptr) +{ + int64_t val; + XSSE_MEMCPY(&val, ptr, 8); + return vreinterpretq_s8_s64((int64x2_t) { + (int64_t) val, (int64_t) 0 + }); +} +#define _mm_loadu_si128(x) _mm_load_si128(x) +static XSSE_FORCE_INLINE __m128i _mm_loadl_epi64(__m128i const *x) +{ + int64x1_t zero = vdup_n_s64(0); + int64x1_t x64 = vld1_s64((const int64_t*) x); + return vreinterpretq_s8_s64(vcombine_s64(x64, zero)); +} + +#define _mm_storel_epi64(to, x) (vst1_u64((uint64_t*) (to), vget_low_u64(vreinterpretq_u64_s8(x)))) +#define _mm_store_si128(to, x) (vst1q_s8((int8_t*) (to), x)) +static XSSE_FORCE_INLINE void _mm_storeu_si16(void *ptr, __m128i x) +{ + int16_t val = (int16_t) vgetq_lane_s16(vreinterpretq_s16_s8(x), 0); + XSSE_MEMCPY(ptr, &val, sizeof(val)); +} +static XSSE_FORCE_INLINE void _mm_storeu_si32(void *ptr, __m128i x) +{ + int32_t val = (int32_t) vgetq_lane_s32(vreinterpretq_s32_s8(x), 0); + XSSE_MEMCPY(ptr, &val, sizeof(val)); +} +static XSSE_FORCE_INLINE void _mm_storeu_si64(void *ptr, __m128i x) +{ + int64_t val = (int64_t) vgetq_lane_s64(vreinterpretq_s64_s8(x), 0); + XSSE_MEMCPY(ptr, &val, sizeof(val)); +} +#define _mm_storeu_si128(to, x) _mm_store_si128(to, x) +#define _mm_stream_si128(to, x) _mm_store_si128(to, x) +#define _mm_stream_si32(to, x) (*(int32_t*) (to) = (int32_t) (x)) +#define _mm_stream_si64(to, x) (*(int64_t*) (to) = (int64_t) (x)) + + +/***************************************************************************** + * Bit shift / Bit wise * + *****************************************************************************/ + +#define _mm_or_si128(a, b) (vorrq_s8((a), (b))) +#define _mm_xor_si128(a, b) (veorq_s8((a), (b))) +#define _mm_and_si128(a, b) (vandq_s8((a), (b))) +#define _mm_andnot_si128(a, b) (vbicq_s8((b), (a))) + +#define _mm_slli_epi16(x, count) (vreinterpretq_s8_u16(vshlq_n_u16(vreinterpretq_u16_s8(x), (count)))) +#define _mm_slli_epi32(x, count) (vreinterpretq_s8_u32(vshlq_n_u32(vreinterpretq_u32_s8(x), (count)))) +#define _mm_slli_epi64(x, count) (vreinterpretq_s8_u64(vshlq_n_u64(vreinterpretq_u64_s8(x), (count)))) +static XSSE_FORCE_INLINE __m128i _mm_sll_epi16(__m128i x, __m128i count) +{ + uint16x8_t x16 = vreinterpretq_u16_s8(x); + uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); + int16x8_t shifts = vdupq_n_s16((int16_t) shift); + return vreinterpretq_s8_u16(vshlq_u16(x16, shifts)); +} +static XSSE_FORCE_INLINE __m128i _mm_sll_epi32(__m128i x, __m128i count) +{ + uint32x4_t x32 = vreinterpretq_u32_s8(x); + uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); + int32x4_t shifts = vdupq_n_s32((int32_t) shift); + return vreinterpretq_s8_u32(vshlq_u32(x32, shifts)); +} +static XSSE_FORCE_INLINE __m128i _mm_sll_epi64(__m128i x, __m128i count) +{ + uint64x2_t x64 = vreinterpretq_u64_s8(x); + uint64_t shift = (uint64_t) vgetq_lane_s64(vreinterpretq_s64_s8(count), 0); + int64x2_t shifts = vdupq_n_s64((int64_t) shift); + return vreinterpretq_s8_u64(vshlq_u64(x64, shifts)); +} + +static XSSE_FORCE_INLINE __m128i _mm_slli_si128(__m128i x, int imm) +{ + switch (imm) { + case 0:; + int8x16_t x2 = x; + return x2; + case 1: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 15)); + case 2: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 14)); + case 3: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 13)); + case 4: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 12)); + case 5: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 11)); + case 6: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 10)); + case 7: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 9)); + case 8: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 8)); + case 9: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 7)); + case 10: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 6)); + case 11: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 5)); + case 12: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 4)); + case 13: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 3)); + case 14: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 2)); + case 15: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 1)); + default: return vreinterpretq_s8_u8(vdupq_n_u8(0)); + } +} +#define _mm_bslli_si128(x, imm) _mm_slli_si128(x, imm) + +#define _mm_srai_epi16(x, count) (vreinterpretq_s8_s16(vshrq_n_s16(vreinterpretq_s16_s8(x), (count)))) +#define _mm_srai_epi32(x, count) (vreinterpretq_s8_s32(vshrq_n_s32(vreinterpretq_s32_s8(x), (count)))) +static XSSE_FORCE_INLINE __m128i _mm_sra_epi16(__m128i x, __m128i count) +{ + int16x8_t x16 = vreinterpretq_s16_s8(x); + uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); + int16x8_t shifts = vdupq_n_s16(-(int16_t) shift); + return vreinterpretq_s8_s16(vshlq_s16(x16, shifts)); +} +static XSSE_FORCE_INLINE __m128i _mm_sra_epi32(__m128i x, __m128i count) +{ + int32x4_t x32 = vreinterpretq_s32_s8(x); + uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); + int32x4_t shifts = vdupq_n_s32(-(int32_t) shift); + return vreinterpretq_s8_s32(vshlq_s32(x32, shifts)); +} + +#define _mm_srli_epi16(x, count) (vreinterpretq_s8_u16(vshrq_n_u16(vreinterpretq_u16_s8(x), (count)))) +#define _mm_srli_epi32(x, count) (vreinterpretq_s8_u32(vshrq_n_u32(vreinterpretq_u32_s8(x), (count)))) +#define _mm_srli_epi64(x, count) (vreinterpretq_s8_u64(vshrq_n_u64(vreinterpretq_u64_s8(x), (count)))) +static XSSE_FORCE_INLINE __m128i _mm_srl_epi16(__m128i x, __m128i count) +{ + uint16x8_t x16 = vreinterpretq_u16_s8(x); + uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); + int16x8_t shifts = vdupq_n_s16(-(int16_t) shift); + return vreinterpretq_s8_u16(vshlq_u16(x16, shifts)); +} +static XSSE_FORCE_INLINE __m128i _mm_srl_epi32(__m128i x, __m128i count) +{ + uint32x4_t x32 = vreinterpretq_u32_s8(x); + uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); + int32x4_t shifts = vdupq_n_s32(-(int32_t) shift); + return vreinterpretq_s8_u32(vshlq_u32(x32, shifts)); +} +static XSSE_FORCE_INLINE __m128i _mm_srl_epi64(__m128i x, __m128i count) +{ + uint64x2_t x64 = vreinterpretq_u64_s8(x); + uint64_t shift = (uint64_t) vgetq_lane_s64(vreinterpretq_s64_s8(count), 0); + int64x2_t shifts = vdupq_n_s64(-(int64_t) shift); + return vreinterpretq_s8_u64(vshlq_u64(x64, shifts)); +} + +static XSSE_FORCE_INLINE __m128i _mm_srli_si128(__m128i x, int imm) +{ + switch (imm) { + case 0: return x; + case 1: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 1)); + case 2: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 2)); + case 3: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 3)); + case 4: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 4)); + case 5: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 5)); + case 6: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 6)); + case 7: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 7)); + case 8: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 8)); + case 9: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 9)); + case 10: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 10)); + case 11: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 11)); + case 12: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 12)); + case 13: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 13)); + case 14: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 14)); + case 15: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 15)); + default: return vreinterpretq_s8_u8(vdupq_n_u8(0)); + } +} +#define _mm_bsrli_si128(x, imm) _mm_srli_si128(x, imm) + + +/***************************************************************************** + * Integer Arithmetic Operations * + *****************************************************************************/ + +/** + * In practice, there is no problem, but a runtime error for signed integer overflow is triggered by UBSAN, + * so perform the calculation as unsigned. Since it is optimized at compile time, there are no unnecessary casts at runtime. + */ +#define _mm_add_epi8(a, b) (vreinterpretq_s8_u8(vaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_add_epi16(a, b) (vreinterpretq_s8_u16(vaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_add_epi32(a, b) (vreinterpretq_s8_u32(vaddq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) +#define _mm_add_epi64(a, b) (vreinterpretq_s8_u64(vaddq_u64(vreinterpretq_u64_s8(a), vreinterpretq_u64_s8(b)))) + +#define _mm_adds_epi8(a, b) (vqaddq_s8((a), (b))) +#define _mm_adds_epi16(a, b) (vreinterpretq_s8_s16(vqaddq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_adds_epu8(a, b) (vreinterpretq_s8_u8(vqaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_adds_epu16(a, b) (vreinterpretq_s8_u16(vqaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) + +#define _mm_avg_epu8(a, b) (vreinterpretq_s8_u8(vrhaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_avg_epu16(a, b) (vreinterpretq_s8_u16(vrhaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) + +static XSSE_FORCE_INLINE __m128i _mm_madd_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + + int16x4_t a_lo = vget_low_s16(a16); + int16x4_t a_hi = vget_high_s16(a16); + int16x4_t b_lo = vget_low_s16(b16); + int16x4_t b_hi = vget_high_s16(b16); + + int32x4_t mul_lo = vmull_s16(a_lo, b_lo); + int32x4_t mul_hi = vmull_s16(a_hi, b_hi); + return vreinterpretq_s8_s32(vpaddq_s32(mul_lo, mul_hi)); +} + +#define _mm_max_epu8(a, b) (vreinterpretq_s8_u8(vmaxq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_max_epi16(a, b) (vreinterpretq_s8_s16(vmaxq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_min_epu8(a, b) (vreinterpretq_s8_u8(vminq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_min_epi16(a, b) (vreinterpretq_s8_s16(vminq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) + +static XSSE_FORCE_INLINE __m128i _mm_mulhi_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + + int16x4_t a_lo = vget_low_s16(a16); + int16x4_t a_hi = vget_high_s16(a16); + int16x4_t b_lo = vget_low_s16(b16); + int16x4_t b_hi = vget_high_s16(b16); + + int32x4_t mul_lo = vmull_s16(a_lo, b_lo); + int32x4_t mul_hi = vmull_s16(a_hi, b_hi); + + int16x4_t narrowed_lo = vshrn_n_s32(mul_lo, 16); + int16x4_t narrowed_hi = vshrn_n_s32(mul_hi, 16); + + return vreinterpretq_s8_s16(vcombine_s16(narrowed_lo, narrowed_hi)); +} +static XSSE_FORCE_INLINE __m128i _mm_mulhi_epu16(__m128i a, __m128i b) +{ + uint16x8_t a16 = vreinterpretq_u16_s8(a); + uint16x8_t b16 = vreinterpretq_u16_s8(b); + + uint16x4_t a_lo = vget_low_u16(a16); + uint16x4_t a_hi = vget_high_u16(a16); + uint16x4_t b_lo = vget_low_u16(b16); + uint16x4_t b_hi = vget_high_u16(b16); + + uint32x4_t mul_lo = vmull_u16(a_lo, b_lo); + uint32x4_t mul_hi = vmull_u16(a_hi, b_hi); + + uint16x4_t narrowed_lo = vshrn_n_u32(mul_lo, 16); + uint16x4_t narrowed_hi = vshrn_n_u32(mul_hi, 16); + + return vreinterpretq_s8_u16(vcombine_u16(narrowed_lo, narrowed_hi)); +} +static XSSE_FORCE_INLINE __m128i _mm_mullo_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + return vreinterpretq_s8_s16(vmulq_s16(a16, b16)); +} +static XSSE_FORCE_INLINE __m128i _mm_mul_epu32(__m128i a, __m128i b) +{ + uint32x4_t a32 = vreinterpretq_u32_s8(a); + uint32x4_t b32 = vreinterpretq_u32_s8(b); + uint32x4_t evens = vuzp1q_u32(a32, b32); + uint32x2_t lo = vget_low_u32(evens); + uint32x2_t hi = vget_high_u32(evens); + + return vreinterpretq_s8_u64(vmull_u32(lo, hi)); +} +static XSSE_FORCE_INLINE __m128i _mm_sad_epu8(__m128i a, __m128i b) +{ + uint8x16_t ua = vreinterpretq_u8_s8(a); + uint8x16_t ub = vreinterpretq_u8_s8(b); + uint16x8_t abs_diffs_16 = vpaddlq_u8(vabdq_u8(ua, ub)); + uint32x4_t abs_diffs_32 = vpaddlq_u16(abs_diffs_16); + uint64x2_t abs_diffs_64 = vpaddlq_u32(abs_diffs_32); + + return vreinterpretq_s8_u16((uint16x8_t) { + (int16_t) vgetq_lane_u64(abs_diffs_64, 0), 0, 0, 0, + (int16_t) vgetq_lane_u64(abs_diffs_64, 1), 0, 0, 0 + }); +} + +#define _mm_sub_epi8(a, b) (vreinterpretq_s8_u8(vsubq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_sub_epi16(a, b) (vreinterpretq_s8_u16(vsubq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_sub_epi32(a, b) (vreinterpretq_s8_u32(vsubq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) +#define _mm_sub_epi64(a, b) (vreinterpretq_s8_u64(vsubq_u64(vreinterpretq_u64_s8(a), vreinterpretq_u64_s8(b)))) + +#define _mm_subs_epi8(a, b) (vqsubq_s8((a), (b))) +#define _mm_subs_epi16(a, b) (vreinterpretq_s8_s16(vqsubq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_subs_epu8(a, b) (vreinterpretq_s8_u8(vqsubq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_subs_epu16(a, b) (vreinterpretq_s8_u16(vqsubq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) + + +/***************************************************************************** + * Comparison * + *****************************************************************************/ + +#define _mm_cmpeq_epi8(a, b) (vreinterpretq_s8_u8(vceqq_s8((a), (b)))) +#define _mm_cmpeq_epi16(a, b) (vreinterpretq_s8_u16(vceqq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_cmpeq_epi32(a, b) (vreinterpretq_s8_u32(vceqq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) + +#define _mm_cmplt_epi8(a, b) (vreinterpretq_s8_u8(vcltq_s8((a), (b)))) +#define _mm_cmplt_epi16(a, b) (vreinterpretq_s8_u16(vcltq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_cmplt_epi32(a, b) (vreinterpretq_s8_u32(vcltq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) + +#define _mm_cmpgt_epi8(a, b) (vreinterpretq_s8_u8(vcgtq_s8((a), (b)))) +#define _mm_cmpgt_epi16(a, b) (vreinterpretq_s8_u16(vcgtq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_cmpgt_epi32(a, b) (vreinterpretq_s8_u32(vcgtq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) + + +/***************************************************************************** + * Convert * + *****************************************************************************/ + +#define _mm_cvtsi32_si128(x) (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x), 0, 0, 0 })) +#define _mm_cvtsi64_si128(x) (vreinterpretq_s8_s64((int64x2_t) { (int64_t) (x), 0 })) +#define _mm_cvtsi128_si32(x) (vgetq_lane_s32(vreinterpretq_s32_s8(x), 0)) +#define _mm_cvtsi128_si64(x) (vgetq_lane_s64(vreinterpretq_s64_s8(x), 0)) + + +/***************************************************************************** + * Others * + *****************************************************************************/ + +#define _mm_packs_epi16(a, b) (vcombine_s8(vqmovn_s16(vreinterpretq_s16_s8(a)), vqmovn_s16(vreinterpretq_s16_s8(b)))) +#define _mm_packs_epi32(a, b) \ + (vreinterpretq_s8_s16(vcombine_s16(vqmovn_s32(vreinterpretq_s32_s8(a)), vqmovn_s32(vreinterpretq_s32_s8(b))))) +#define _mm_packus_epi16(a, b) \ + (vreinterpretq_s8_u8(vcombine_u8(vqmovun_s16(vreinterpretq_s16_s8(a)), vqmovun_s16(vreinterpretq_s16_s8(b))))) + +#define _mm_extract_epi16(x, imm) (vgetq_lane_s16(vreinterpretq_s16_s8(x), (imm))) +#define _mm_insert_epi16(x, val, imm) (vreinterpretq_s8_s16(vsetq_lane_s16((int16_t) (val), vreinterpretq_s16_s8(x), (imm)))) + +static XSSE_FORCE_INLINE void _mm_maskmoveu_si128(__m128i x, __m128i mask, char* dest) +{ + uint8x16_t origin = vld1q_u8((uint8_t*) dest); + + uint8x16_t repeat_0x00 = vdupq_n_u8(0); + uint8x16_t repeat_0x80 = vdupq_n_u8(0x80); + uint8x16_t umask = vreinterpretq_u8_s8(mask); + uint8x16_t ux = vreinterpretq_u8_s8(x); + + uint8x16_t mask_bits = vandq_u8(umask, repeat_0x80); + uint8x16_t mask_bytes = vsubq_u8(repeat_0x00, vshrq_n_u8(mask_bits, 7)); + vst1q_u8((uint8_t*) dest, vbslq_u8(mask_bytes, ux, origin)); +} +static XSSE_FORCE_INLINE int _mm_movemask_epi8(__m128i x) +{ + /** + * based on code from + * https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon + */ + uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(vreinterpretq_u8_s8(x), 7)); + uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); + uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); + uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); + return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8); +} + +#define _MM_SHUFFLE(a, b, c, d) (((a) << 6) | ((b) << 4) | ((c) << 2) | (d)) +#ifdef XSSE_HAS_MACRO_EXTENSION +#define _mm_shuffle_epi32(x, imm) __extension__({ \ + int32x4_t __xsse_tmp = vreinterpretq_s32_s8(x); \ + vreinterpretq_s8_s32((int32x4_t) { \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 0) & 0x3), \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 2) & 0x3), \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 4) & 0x3), \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 6) & 0x3) \ + }); \ + }) +#define _mm_shufflehi_epi16(x, imm) __extension__({ \ + int16x8_t __xsse_tmp = vreinterpretq_s16_s8(x); \ + vreinterpretq_s8_s16(vcombine_s16( \ + vget_low_s16(__xsse_tmp), \ + (int16x4_t) { \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 0) & 0x3) + 4), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 2) & 0x3) + 4), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 4) & 0x3) + 4), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 6) & 0x3) + 4) \ + } \ + )); \ + }) +#define _mm_shufflelo_epi16(x, imm) __extension__({ \ + int16x8_t __xsse_tmp = vreinterpretq_s16_s8(x); \ + vreinterpretq_s8_s16(vcombine_s16( \ + (int16x4_t) { \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 0) & 0x3)), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 2) & 0x3)), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 4) & 0x3)), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 6) & 0x3)) \ + }, \ + vget_high_s16(__xsse_tmp) \ + )); \ + }) +#else +static XSSE_FORCE_INLINE __m128i _mm_shuffle_epi32(__m128i x, int imm) +{ + int32x4_t vec = vreinterpretq_s32_s8(x); + int32_t arr[4]; + vst1q_s32(arr, vec); + + return vreinterpretq_s8_s32((int32x4_t) { + arr[(imm >> 0) & 0x3], + arr[(imm >> 2) & 0x3], + arr[(imm >> 4) & 0x3], + arr[(imm >> 6) & 0x3] + }); +} +static XSSE_FORCE_INLINE __m128i _mm_shufflehi_epi16(__m128i x, int imm) +{ + int16x8_t vec = vreinterpretq_s16_s8(x); + int16_t arr[8]; + vst1q_s16(arr, vec); + + return vreinterpretq_s8_s16((int16x8_t) { + arr[0], arr[1], arr[2], arr[3], + arr[((imm >> 0) & 0x3) + 4], + arr[((imm >> 2) & 0x3) + 4], + arr[((imm >> 4) & 0x3) + 4], + arr[((imm >> 6) & 0x3) + 4] + }); +} +static XSSE_FORCE_INLINE __m128i _mm_shufflelo_epi16(__m128i x, int imm) +{ + int16x8_t vec = vreinterpretq_s16_s8(x); + int16_t arr[8]; + vst1q_s16(arr, vec); + + return vreinterpretq_s8_s16((int16x8_t) { + arr[((imm >> 0) & 0x3)], + arr[((imm >> 2) & 0x3)], + arr[((imm >> 4) & 0x3)], + arr[((imm >> 6) & 0x3)], + arr[4], arr[5], arr[6], arr[7] + }); +} +#endif + +#define _mm_unpackhi_epi8(a, b) (vzip2q_s8((a), (b))) +#define _mm_unpackhi_epi16(a, b) (vreinterpretq_s8_s16(vzip2q_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_unpackhi_epi32(a, b) (vreinterpretq_s8_s32(vzip2q_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) +#define _mm_unpackhi_epi64(a, b) (vreinterpretq_s8_s64(vzip2q_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) + +#define _mm_unpacklo_epi8(a, b) (vzip1q_s8((a), (b))) +#define _mm_unpacklo_epi16(a, b) (vreinterpretq_s8_s16(vzip1q_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_unpacklo_epi32(a, b) (vreinterpretq_s8_s32(vzip1q_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) +#define _mm_unpacklo_epi64(a, b) (vreinterpretq_s8_s64(vzip1q_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) + +#define _mm_move_epi64(x) (vreinterpretq_s8_s64((int64x2_t) { vgetq_lane_s64(vreinterpretq_s64_s8(x), 0), 0 })) + +#define _mm_clflush(x) ((void) 0) +static XSSE_FORCE_INLINE void _mm_mfence(void) +{ + __asm__ __volatile__("dsb sy" ::: "memory"); +} +static XSSE_FORCE_INLINE void _mm_lfence(void) +{ + __asm__ __volatile__("dsb ld" ::: "memory"); +} +static XSSE_FORCE_INLINE void _mm_pause(void) +{ + __asm__ __volatile__("yield"); +} + +#endif /* SSE2 */ + + +/***************************************************************************** + * * + * SSE3 * + * * + *****************************************************************************/ + +#if defined(__SSE3__) +#include +#define XSSE3 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#define XSSE3 + +/***************************************************************************** + * Load / Store * + *****************************************************************************/ + +#define _mm_lddqu_si128(x) _mm_load_si128(x) + +#endif /* SSE3 */ + + +/***************************************************************************** + * * + * SSSE3 * + * * + *****************************************************************************/ + +#if defined(__SSSE3__) +#include +#define XSSSE3 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#define XSSSE3 + +/***************************************************************************** + * Bit shift / Bit wise * + *****************************************************************************/ + +static XSSE_FORCE_INLINE __m128i _mm_alignr_epi8(__m128i a, __m128i b, int imm) +{ + switch (imm) { + case 0:; + int8x16_t b2 = b; + return b2; + case 1: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 1)); + case 2: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 2)); + case 3: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 3)); + case 4: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 4)); + case 5: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 5)); + case 6: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 6)); + case 7: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 7)); + case 8: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 8)); + case 9: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 9)); + case 10: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 10)); + case 11: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 11)); + case 12: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 12)); + case 13: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 13)); + case 14: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 14)); + case 15: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 15)); + case 16:; + int8x16_t a2 = a; + return a2; + case 17: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 1)); + case 18: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 2)); + case 19: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 3)); + case 20: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 4)); + case 21: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 5)); + case 22: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 6)); + case 23: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 7)); + case 24: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 8)); + case 25: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 9)); + case 26: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 10)); + case 27: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 11)); + case 28: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 12)); + case 29: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 13)); + case 30: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 14)); + case 31: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 15)); + default: return vdupq_n_s8(0); + } +} + + +/***************************************************************************** + * Integer Arithmetic Operations * + *****************************************************************************/ + +#define _mm_abs_epi8(x) (vabsq_s8(x)) +#define _mm_abs_epi16(x) (vreinterpretq_s8_s16(vabsq_s16(vreinterpretq_s16_s8(x)))) +#define _mm_abs_epi32(x) (vreinterpretq_s8_s32(vabsq_s32(vreinterpretq_s32_s8(x)))) + +#define _mm_hadd_epi16(a, b) (vreinterpretq_s8_u16(vpaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_hadd_epi32(a, b) (vreinterpretq_s8_u32(vpaddq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) +static XSSE_FORCE_INLINE __m128i _mm_hadds_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + int16x8_t evens = vuzp1q_s16(a16, b16); + int16x8_t odds = vuzp2q_s16(a16, b16); + return vreinterpretq_s8_s16(vqaddq_s16(evens, odds)); +} + +static XSSE_FORCE_INLINE __m128i _mm_hsub_epi16(__m128i a, __m128i b) +{ + uint16x8_t a16 = vreinterpretq_u16_s8(a); + uint16x8_t b16 = vreinterpretq_u16_s8(b); + uint16x8_t evens = vuzp1q_u16(a16, b16); + uint16x8_t odds = vuzp2q_u16(a16, b16); + return vreinterpretq_s8_u16(vsubq_u16(evens, odds)); +} +static XSSE_FORCE_INLINE __m128i _mm_hsub_epi32(__m128i a, __m128i b) +{ + uint32x4_t a32 = vreinterpretq_u32_s8(a); + uint32x4_t b32 = vreinterpretq_u32_s8(b); + uint32x4_t evens = vuzp1q_u32(a32, b32); + uint32x4_t odds = vuzp2q_u32(a32, b32); + return vreinterpretq_s8_u32(vsubq_u32(evens, odds)); +} +static XSSE_FORCE_INLINE __m128i _mm_hsubs_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + int16x8_t evens = vuzp1q_s16(a16, b16); + int16x8_t odds = vuzp2q_s16(a16, b16); + return vreinterpretq_s8_s16(vqsubq_s16(evens, odds)); +} + +static XSSE_FORCE_INLINE __m128i _mm_maddubs_epi16(__m128i a, __m128i b) +{ + uint8x16_t ua = vreinterpretq_u8_s8(a); + uint8x8_t ua_lo = vget_low_u8(ua); + uint8x8_t ua_hi = vget_high_u8(ua); + + uint16x8_t wide_ua_lo = vmovl_u8(ua_lo); + uint16x8_t wide_ua_hi = vmovl_u8(ua_hi); + + int16x8_t wide_a_lo = vreinterpretq_s16_u16(wide_ua_lo); + int16x8_t wide_a_hi = vreinterpretq_s16_u16(wide_ua_hi); + + int8x8_t b_lo = vget_low_s8(b); + int8x8_t b_hi = vget_high_s8(b); + + int16x8_t wide_b_lo = vmovl_s8(b_lo); + int16x8_t wide_b_hi = vmovl_s8(b_hi); + + int16x8_t mul_lo = vmulq_s16(wide_a_lo, wide_b_lo); + int16x8_t mul_hi = vmulq_s16(wide_a_hi, wide_b_hi); + + uint16x8_t umul_lo = vreinterpretq_u16_s16(mul_lo); + uint16x8_t umul_hi = vreinterpretq_u16_s16(mul_hi); + + return vreinterpretq_s8_u16(vpaddq_u16(umul_lo, umul_hi)); +} + +static XSSE_FORCE_INLINE __m128i _mm_mulhrs_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + + int16x4_t a_lo = vget_low_s16(a16); + int16x4_t a_hi = vget_high_s16(a16); + int16x4_t b_lo = vget_low_s16(b16); + int16x4_t b_hi = vget_high_s16(b16); + + int32x4_t mul_lo = vmull_s16(a_lo, b_lo); + int32x4_t mul_hi = vmull_s16(a_hi, b_hi); + + int32x4_t repeat_0x4000 = vdupq_n_s32(0x4000); + + int32x4_t add_lo = vaddq_s32(mul_lo, repeat_0x4000); + int32x4_t add_hi = vaddq_s32(mul_hi, repeat_0x4000); + + int32x4_t lo = vshrq_n_s32(add_lo, 15); + int32x4_t hi = vshrq_n_s32(add_hi, 15); + + int16x4_t narrowed_lo = vqmovn_s32(lo); + int16x4_t narrowed_hi = vqmovn_s32(hi); + + return vreinterpretq_s8_s16(vcombine_s16(narrowed_lo, narrowed_hi)); +} + +static XSSE_FORCE_INLINE __m128i _mm_sign_epi8(__m128i a, __m128i b) +{ + int8x16_t repeat_0x00 = vdupq_n_s8(0); + int8x16_t repeat_0x01 = vdupq_n_s8(1); + + uint8x16_t upos_cmp_ret = vcltq_s8(repeat_0x00, b); + uint8x16_t uneg_cmp_ret = vcltq_s8(b, repeat_0x00); + + int8x16_t pos_cmp_ret = vreinterpretq_s8_u8(upos_cmp_ret); + int8x16_t pos_mask = vandq_s8(pos_cmp_ret, repeat_0x01); + int8x16_t neg_mask = vreinterpretq_s8_u8(uneg_cmp_ret); + + int8x16_t mask = vorrq_s8(pos_mask, neg_mask); + + return vmulq_s8(a, mask); +} +static XSSE_FORCE_INLINE __m128i _mm_sign_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + + int16x8_t repeat_0x00 = vdupq_n_s16(0); + int16x8_t repeat_0x01 = vdupq_n_s16(1); + + uint16x8_t upos_cmp_ret = vcltq_s16(repeat_0x00, b16); + uint16x8_t uneg_cmp_ret = vcltq_s16(b16, repeat_0x00); + + int16x8_t pos_cmp_ret = vreinterpretq_s16_u16(upos_cmp_ret); + int16x8_t pos_mask = vandq_s16(pos_cmp_ret, repeat_0x01); + int16x8_t neg_mask = vreinterpretq_s16_u16(uneg_cmp_ret); + + int16x8_t mask = vorrq_s16(pos_mask, neg_mask); + + return vreinterpretq_s8_s16(vmulq_s16(a16, mask)); +} +static XSSE_FORCE_INLINE __m128i _mm_sign_epi32(__m128i a, __m128i b) +{ + int32x4_t a32 = vreinterpretq_s32_s8(a); + int32x4_t b32 = vreinterpretq_s32_s8(b); + + int32x4_t repeat_0x00 = vdupq_n_s32(0); + int32x4_t repeat_0x01 = vdupq_n_s32(1); + + uint32x4_t upos_cmp_ret = vcltq_s32(repeat_0x00, b32); + uint32x4_t uneg_cmp_ret = vcltq_s32(b32, repeat_0x00); + + int32x4_t pos_cmp_ret = vreinterpretq_s32_u32(upos_cmp_ret); + int32x4_t pos_mask = vandq_s32(pos_cmp_ret, repeat_0x01); + int32x4_t neg_mask = vreinterpretq_s32_u32(uneg_cmp_ret); + + int32x4_t mask = vorrq_s32(pos_mask, neg_mask); + + return vreinterpretq_s8_s32(vmulq_s32(a32, mask)); +} + + +/***************************************************************************** + * Others * + *****************************************************************************/ + +static XSSE_FORCE_INLINE __m128i _mm_shuffle_epi8(__m128i a, __m128i b) +{ + uint8x16_t index = vreinterpretq_u8_s8(b); + uint8x16_t repeat_0x0F = vdupq_n_u8(0x0F); + uint8x16_t repeat_0x80 = vdupq_n_u8(0x80); + + uint8x16_t and_mask = vandq_u8(index, repeat_0x0F); + uint8x16_t cmp_mask = vcgeq_u8(index, repeat_0x80); + uint8x16_t masked_index = vorrq_u8(and_mask, cmp_mask); + return vqtbl1q_s8(a, masked_index); +} + +#endif /* SSSE3 */ + + +/***************************************************************************** + * * + * SSE4.1 * + * * + *****************************************************************************/ + +#if defined(__SSE4_1__) +#include +#define XSSE4_1 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#define XSSE4_1 + +/***************************************************************************** + * Load / Store * + *****************************************************************************/ + +#define _mm_stream_load_si128(x) _mm_load_si128(x) + + +/***************************************************************************** + * Integer Arithmetic Operations * + *****************************************************************************/ + +#define _mm_max_epi8(a, b) (vmaxq_s8((a), (b))) +#define _mm_max_epi32(a, b) (vreinterpretq_s8_s32(vmaxq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) +#define _mm_max_epu16(a, b) (vreinterpretq_s8_u16(vmaxq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_max_epu32(a, b) (vreinterpretq_s8_u32(vmaxq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) + +#define _mm_min_epi8(a, b) (vminq_s8((a), (b))) +#define _mm_min_epi32(a, b) (vreinterpretq_s8_s32(vminq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) +#define _mm_min_epu16(a, b) (vreinterpretq_s8_u16(vminq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_min_epu32(a, b) (vreinterpretq_s8_u32(vminq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) + +static XSSE_FORCE_INLINE __m128i _mm_minpos_epu16(__m128i x) +{ + uint16x8_t x16 = vreinterpretq_u16_s8(x); + + uint16x4_t lo = vget_low_u16(x16); + uint16x4_t hi = vget_high_u16(x16); + uint16x4_t min4 = vmin_u16(lo, hi); + + uint16x4_t min2 = vpmin_u16(min4, min4); + uint16x4_t min1 = vpmin_u16(min2, min2); + + uint16_t min = (uint16_t) vget_lane_u16(min1, 0); + uint16x8_t repeat_min = vdupq_n_u16(min); + + uint16x8_t cmp = vceqq_u16(x16, repeat_min); + uint8x8_t narrowed_cmp = vmovn_u16(cmp); + uint64x1_t cmp_64 = vreinterpret_u64_u8(narrowed_cmp); + + uint64_t index_mask8 = (uint64_t) vget_lane_u64(cmp_64, 0); + + uint16_t index = 0; +#if (defined(__has_builtin) && __has_builtin(__builtin_ctzll)) || defined(__GNUC__) || defined(__clang__) + index = (uint16_t) __builtin_ctzll(index_mask8) / 8; +#else + for (int i = 0; i < 8; i++) { + if (index_mask8 & 1) { + index = (uint16_t) i; + break; + } + index_mask8 >>= 8; + } +#endif + return vreinterpretq_s8_u16((uint16x8_t) { min, index, 0, 0, 0, 0, 0, 0 }); +} + +static XSSE_FORCE_INLINE __m128i _mm_mul_epi32(__m128i a, __m128i b) +{ + int32x4_t a32 = vreinterpretq_s32_s8(a); + int32x4_t b32 = vreinterpretq_s32_s8(b); + int32x4_t evens = vuzp1q_s32(a32, b32); + int32x2_t lo = vget_low_s32(evens); + int32x2_t hi = vget_high_s32(evens); + return vreinterpretq_s8_s64(vmull_s32(lo, hi)); +} +static XSSE_FORCE_INLINE __m128i _mm_mullo_epi32(__m128i a, __m128i b) +{ + int32x4_t a32 = vreinterpretq_s32_s8(a); + int32x4_t b32 = vreinterpretq_s32_s8(b); + return vreinterpretq_s8_s32(vmulq_s32(a32, b32)); +} + +static XSSE_FORCE_INLINE __m128i _mm_mpsadbw_epu8(__m128i a, __m128i b, const int imm8) +{ + uint8x16_t ua = vreinterpretq_u8_s8(a); + + uint8x8_t a_sliced_0_4; + uint8x8_t a_sliced_1_5; + uint8x8_t a_sliced_2_6; + uint8x8_t a_sliced_3_7; + uint8x16_t a_pre_sliced_0_4; + uint8x16_t a_pre_sliced_1_5; + uint8x16_t a_pre_sliced_2_6; + uint8x16_t a_pre_sliced_3_7; + if ((imm8 >> 2) & 1) { + a_pre_sliced_0_4 = vextq_u8(ua, ua, 4); + a_pre_sliced_1_5 = vextq_u8(ua, ua, 5); + a_pre_sliced_2_6 = vextq_u8(ua, ua, 6); + a_pre_sliced_3_7 = vextq_u8(ua, ua, 7); + a_sliced_0_4 = vget_low_u8(a_pre_sliced_0_4); + } else { + a_pre_sliced_1_5 = vextq_u8(ua, ua, 1); + a_pre_sliced_2_6 = vextq_u8(ua, ua, 2); + a_pre_sliced_3_7 = vextq_u8(ua, ua, 3); + a_sliced_0_4 = vget_low_u8(ua); + } + a_sliced_1_5 = vget_low_u8(a_pre_sliced_1_5); + a_sliced_2_6 = vget_low_u8(a_pre_sliced_2_6); + a_sliced_3_7 = vget_low_u8(a_pre_sliced_3_7); + + uint32x4_t b32 = vreinterpretq_u32_s8(b); + uint8x8_t b_slicedx2; + switch (imm8 & 0x03) { + case 0: + b_slicedx2 = vreinterpret_u8_u32(vdup_n_u32(vgetq_lane_u32(b32, 0))); + break; + case 1: + b_slicedx2 = vreinterpret_u8_u32(vdup_n_u32(vgetq_lane_u32(b32, 1))); + break; + case 2: + b_slicedx2 = vreinterpret_u8_u32(vdup_n_u32(vgetq_lane_u32(b32, 2))); + break; + case 3: + b_slicedx2 = vreinterpret_u8_u32(vdup_n_u32(vgetq_lane_u32(b32, 3))); + break; + } + + uint16x8_t abs_diffs_0_4 = vabdl_u8(a_sliced_0_4, b_slicedx2); + uint16x8_t abs_diffs_1_5 = vabdl_u8(a_sliced_1_5, b_slicedx2); + uint16x8_t abs_diffs_2_6 = vabdl_u8(a_sliced_2_6, b_slicedx2); + uint16x8_t abs_diffs_3_7 = vabdl_u8(a_sliced_3_7, b_slicedx2); + + uint16x8_t abs_diffs_0_4_2_6 = vpaddq_u16(abs_diffs_0_4, abs_diffs_2_6); + uint16x8_t abs_diffs_1_5_3_7 = vpaddq_u16(abs_diffs_1_5, abs_diffs_3_7); + + uint32x4_t abs_diffs_0_4_2_6x32 = vreinterpretq_u32_u16(abs_diffs_0_4_2_6); + uint32x4_t abs_diffs_1_5_3_7x32 = vreinterpretq_u32_u16(abs_diffs_1_5_3_7); + uint32x4_t abs_diffs_0_1_2_3x32 = vtrn1q_u32(abs_diffs_0_4_2_6x32, abs_diffs_1_5_3_7x32); + uint32x4_t abs_diffs_4_5_6_7x32 = vtrn2q_u32(abs_diffs_0_4_2_6x32, abs_diffs_1_5_3_7x32); + + uint16x8_t abs_diffs_0_1_2_3 = vreinterpretq_u16_u32(abs_diffs_0_1_2_3x32); + uint16x8_t abs_diffs_4_5_6_7 = vreinterpretq_u16_u32(abs_diffs_4_5_6_7x32); + + return vreinterpretq_s8_u16(vpaddq_u16(abs_diffs_0_1_2_3, abs_diffs_4_5_6_7)); +} + + +/***************************************************************************** + * Comparison * + *****************************************************************************/ + +#define _mm_cmpeq_epi64(a, b) (vreinterpretq_s8_u64(vceqq_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) + + +/***************************************************************************** + * Convert * + *****************************************************************************/ + +#define _mm_cvtepi8_epi16(x) (vreinterpretq_s8_s16(vmovl_s8(vget_low_s8(x)))) +#define _mm_cvtepi8_epi32(x) (vreinterpretq_s8_s32(vmovl_s16(vget_low_s16(vmovl_s8(vget_low_s8(x)))))) +#define _mm_cvtepi8_epi64(x) \ + (vreinterpretq_s8_s64(vmovl_s32(vget_low_s32(vmovl_s16(vget_low_s16(vmovl_s8(vget_low_s8(x)))))))) +#define _mm_cvtepi16_epi32(x) (vreinterpretq_s8_s32(vmovl_s16(vget_low_s16(vreinterpretq_s16_s8(x))))) +#define _mm_cvtepi16_epi64(x) \ + (vreinterpretq_s8_s64(vmovl_s32(vget_low_s32(vmovl_s16(vget_low_s16(vreinterpretq_s16_s8(x))))))) +#define _mm_cvtepi32_epi64(x) (vreinterpretq_s8_s64(vmovl_s32(vget_low_s32(vreinterpretq_s32_s8(x))))) + +#define _mm_cvtepu8_epi16(x) (vreinterpretq_s8_u16(vmovl_u8(vget_low_u8(vreinterpretq_u8_s8(x))))) +#define _mm_cvtepu8_epi32(x) (vreinterpretq_s8_u32(vmovl_u16(vget_low_u16(vmovl_u8(vget_low_u8(vreinterpretq_u8_s8(x))))))) +#define _mm_cvtepu8_epi64(x) \ + (vreinterpretq_s8_u64(vmovl_u32(vget_low_u32(vmovl_u16(vget_low_u16(vmovl_u8(vget_low_u8(vreinterpretq_u8_s8(x))))))))) +#define _mm_cvtepu16_epi32(x) (vreinterpretq_s8_u32(vmovl_u16(vget_low_u16(vreinterpretq_u16_s8(x))))) +#define _mm_cvtepu16_epi64(x) \ + (vreinterpretq_s8_u64(vmovl_u32(vget_low_u32(vmovl_u16(vget_low_u16(vreinterpretq_u16_s8(x))))))) +#define _mm_cvtepu32_epi64(x) (vreinterpretq_s8_u64(vmovl_u32(vget_low_u32(vreinterpretq_u32_s8(x))))) + + +/***************************************************************************** + * Tests * + *****************************************************************************/ + +static XSSE_FORCE_INLINE int _mm_test_all_ones(__m128i x) +{ + uint64x2_t x64 = vreinterpretq_u64_s8(x); + return (vgetq_lane_u64(x64, 0) == ~0ULL) && (vgetq_lane_u64(x64, 1) == ~0ULL); +} +static XSSE_FORCE_INLINE int _mm_test_all_zeros(__m128i mask, __m128i x) +{ + int8x16_t masked = vandq_s8(x, mask); + uint64x2_t masked64 = vreinterpretq_u64_s8(masked); + return (vgetq_lane_u64(masked64, 0) == 0) && (vgetq_lane_u64(masked64, 1) == 0); +} +static XSSE_FORCE_INLINE int _mm_test_mix_ones_zeros(__m128i mask, __m128i x) +{ + int8x16_t and = vandq_s8(x, mask); + uint64x2_t and64 = vreinterpretq_u64_s8(and); + int has_ones = (vgetq_lane_u64(and64, 0) | vgetq_lane_u64(and64, 1)) != 0; + + int8x16_t andnot = vbicq_s8(x, mask); + uint64x2_t andnot64 = vreinterpretq_u64_s8(andnot); + int has_zeros = (vgetq_lane_u64(andnot64, 0) | vgetq_lane_u64(andnot64, 1)) != 0; + + return has_ones && has_zeros; +} +static XSSE_FORCE_INLINE int _mm_testc_si128(__m128i a, __m128i b) +{ + int8x16_t andnot = vbicq_s8(b, a); + uint64x2_t andnot64 = vreinterpretq_u64_s8(andnot); + return (vgetq_lane_u64(andnot64, 0) == 0) && (vgetq_lane_u64(andnot64, 1) == 0); +} +#define _mm_testnzc_si128(a, b) _mm_test_mix_ones_zeros(a, b) +#define _mm_testz_si128(a, b) _mm_test_all_zeros(a, b) + + +/***************************************************************************** + * Others * + *****************************************************************************/ + +#define _mm_packus_epi32(a, b) \ + (vreinterpretq_s8_u16(vcombine_u16(vqmovun_s32(vreinterpretq_s32_s8(a)), vqmovun_s32(vreinterpretq_s32_s8(b))))) + +#define _mm_extract_epi8(x, imm) (vgetq_lane_s8((x), (imm))) +#define _mm_extract_epi32(x, imm) (vgetq_lane_s32(vreinterpretq_s32_s8(x), (imm))) +#define _mm_extract_epi64(x, imm) (vgetq_lane_s64(vreinterpretq_s64_s8(x), (imm))) + +#define _mm_insert_epi8(x, val, imm) (vsetq_lane_s8((int8_t) (val), (x), (imm))) +#define _mm_insert_epi32(x, val, imm) (vreinterpretq_s8_s32(vsetq_lane_s32((int32_t) (val), vreinterpretq_s32_s8(x), (imm)))) +#define _mm_insert_epi64(x, val, imm) (vreinterpretq_s8_s64(vsetq_lane_s64((int64_t) (val), vreinterpretq_s64_s8(x), (imm)))) + +#define _mm_blend_epi16(a, b, imm8) \ + (vreinterpretq_s8_s16(vbslq_s16( \ + (uint16x8_t){ \ + (imm8 & (1 << 0)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 1)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 2)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 3)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 4)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 5)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 6)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 7)) ? 0xFFFF : 0x0000 \ + }, \ + vreinterpretq_s16_s8(b), \ + vreinterpretq_s16_s8(a) \ + ))) +static XSSE_FORCE_INLINE __m128i _mm_blendv_epi8(__m128i a, __m128i b, __m128i mask) +{ + uint8x16_t umask = vreinterpretq_u8_s8(mask); + uint8x16_t repeat_0x80 = vdupq_n_u8(0x80); + uint8x16_t mask_fill = vcgeq_u8(umask, repeat_0x80); + return vbslq_s8(mask_fill, b, a); +} + +#endif /* SSE4_1 */ + + +/***************************************************************************** + * * + * SSE4.2 * + * * + *****************************************************************************/ + +#if defined(__SSE4_2__) +#include +#define XSSE4_2 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#define XSSE4_2 + +/***************************************************************************** + * Comparison * + *****************************************************************************/ + +#define _mm_cmpgt_epi64(a, b) (vreinterpretq_s8_u64(vcgtq_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) + + +/***************************************************************************** + * Packed Compare * + *****************************************************************************/ + +/* [1:0]*/ +#define _SIDD_UBYTE_OPS 0x00 +#define _SIDD_UWORD_OPS 0x01 +#define _SIDD_SBYTE_OPS 0x02 +#define _SIDD_SWORD_OPS 0x03 + +/* [3:2] */ +#define _SIDD_CMP_EQUAL_ANY 0x00 +#define _SIDD_CMP_RANGES 0x04 +#define _SIDD_CMP_EQUAL_EACH 0x08 +#define _SIDD_CMP_EQUAL_ORDERED 0x0C + +/* [5:4] */ +#define _SIDD_POSITIVE_POLARITY 0x00 +#define _SIDD_NEGATIVE_POLARITY 0x10 +#define _SIDD_MASKED_POSITIVE_POLARITY 0x20 +#define _SIDD_MASKED_NEGATIVE_POLARITY 0x30 + +/* [6] */ +#define _SIDD_LEAST_SIGNIFICANT 0x00 +#define _SIDD_MOST_SIGNIFICANT 0x40 + +/* [6] */ +#define _SIDD_BIT_MASK 0x00 +#define _SIDD_UNIT_MASK 0x40 + +typedef struct { + int8_t cf; + int8_t zf; + int8_t sf; + uint8x16_t mask; +} _xsse_pcmp_str_result_t; + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_seach_most_significant_byte_index(uint64_t mask) +{ +#if (defined(__has_builtin) && __has_builtin(__builtin_clzll)) || defined(__GNUC__) || defined(__clang__) + return (int) 7 - (__builtin_clzll(mask) / 8); +#else + for (int i = 0; i < 8; ++i) { + uint8_t byte = (mask >> ((7 - i) * 8)) & 0xFF; + if (byte != 0) { + return 7 - i; + } + } + XSSE_UNREACHABLE(); +#endif +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_seach_least_significant_byte_index(uint64_t mask) +{ +#if (defined(__has_builtin) && __has_builtin(__builtin_ctzll)) || defined(__GNUC__) || defined(__clang__) + return (int) __builtin_ctzll(mask) / 8; +#else + for (int i = 0; i < 8; i++) { + if (mask & 0xFF) { + return i; + } + mask >>= 8; + } + XSSE_UNREACHABLE(); +#endif +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_seach_most_significant_word_index(uint64_t mask) +{ +#if (defined(__has_builtin) && __has_builtin(__builtin_clzll)) || defined(__GNUC__) || defined(__clang__) + return (int) 3 - (__builtin_clzll(mask) / 16); +#else + for (int i = 0; i < 4; ++i) { + uint16_t byte = (mask >> ((3 - i) * 16)) & 0xFFFF; + if (byte != 0) { + return 3 - i; + } + } + XSSE_UNREACHABLE(); +#endif +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_seach_least_significant_word_index(uint64_t mask) +{ +#if (defined(__has_builtin) && __has_builtin(__builtin_ctzll)) || defined(__GNUC__) || defined(__clang__) + return (int) __builtin_ctzll(mask) / 16; +#else + for (int i = 0; i < 4; i++) { + if (mask & 0xFFFF) { + return i; + } + mask >>= 16; + } + XSSE_UNREACHABLE(); +#endif +} + +#ifndef XSSE_IS_OPTIMIZE +XSSE_ATTR_OPTIMIZE_O2 +#endif +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE _xsse_pcmp_str_result_t _xsse_pcmp_str_core(const __m128i a, int la, const __m128i b, int lb, const int imm8, const int check_null) +{ + if (imm8 & 0x01) { + /* word */ +#define XSSE_PCMP_ANY_WORD(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) ? vceqq_s16(vdupq_n_s16(vgetq_lane_s16(a16, l)), b16) : vceqq_u16(vdupq_n_u16(vgetq_lane_u16(ua16, l)), ub16) +#define XSSE_PCMP_RANGES_WORD(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) \ + ? vandq_u16(vcleq_s16(vdupq_n_s16(vgetq_lane_s16(a16, (l))), b16), vcleq_s16(b16, vdupq_n_s16(vgetq_lane_s16(a16, (l + 1))))) \ + : vandq_u16(vcleq_u16(vdupq_n_u16(vgetq_lane_u16(ua16, (l))), ub16), vcleq_u16(ub16, vdupq_n_u16(vgetq_lane_u16(ua16, (l + 1))))) +#define XSSE_PCMP_EACH_WORD() cmp_ret = (imm8 & 0x02) ? vceqq_s16(a16, b16) : vceqq_u16(ua16, ub16) +#define XSSE_PCMP_ORDERED_WORD(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) \ + ? vextq_u16(vbicq_u16(vceqq_s16(vdupq_n_s16(vgetq_lane_s16(a16, l)), b16), b_invalid_mask), repeat_full_bits, l) \ + : vextq_u16(vbicq_u16(vceqq_u16(vdupq_n_u16(vgetq_lane_u16(ua16, l)), ub16), b_invalid_mask), repeat_full_bits, l) + +#define XSSE_VORR_U16(a, b) tmp_cmp_##a = vorrq_u16(tmp_cmp_##a, tmp_cmp_##b); +#define XSSE_VAND_U16(a, b) tmp_cmp_##a = vandq_u16(tmp_cmp_##a, tmp_cmp_##b); + + if (check_null) { + uint16x8_t repeat_nul = vdupq_n_u16(0); + uint16x8_t cmp_nul_a = vceqq_u16(vreinterpretq_u16_s8(a), repeat_nul); + uint16x8_t cmp_nul_b = vceqq_u16(vreinterpretq_u16_s8(b), repeat_nul); + + uint64_t low_a = vgetq_lane_u64(vreinterpretq_u64_u16(cmp_nul_a), 0); + uint64_t high_a = vgetq_lane_u64(vreinterpretq_u64_u16(cmp_nul_a), 1); + if (low_a != 0) { + la = _xsse_seach_least_significant_word_index(low_a); + } else if (high_a != 0) { + la = _xsse_seach_least_significant_word_index(high_a) + 4; + } else { + la = 8; + } + + uint64_t low_b = vgetq_lane_u64(vreinterpretq_u64_u16(cmp_nul_b), 0); + uint64_t high_b = vgetq_lane_u64(vreinterpretq_u64_u16(cmp_nul_b), 1); + if (low_b != 0) { + lb = _xsse_seach_least_significant_word_index(low_b); + } else if (high_b != 0) { + lb = _xsse_seach_least_significant_word_index(high_b) + 4; + } else { + lb = 8; + } + } + + uint16x8_t cmp_ret; + uint16x8_t lanes = { 0, 1, 2, 3, 4, 5, 6, 7 }; + uint16x8_t a_threshold = vdupq_n_u16(la); + uint16x8_t b_threshold = vdupq_n_u16(lb); + uint16x8_t a_invalid_mask = vcgeq_u16(lanes, a_threshold); + uint16x8_t b_invalid_mask = vcgeq_u16(lanes, b_threshold); + + int16x8_t a16, b16; + uint16x8_t ua16, ub16; + if (imm8 & 0x02) { + a16 = vreinterpretq_s16_s8(a); + b16 = vreinterpretq_s16_s8(b); + } else { + ua16 = vreinterpretq_u16_s8(a); + ub16 = vreinterpretq_u16_s8(b); + } + + /* mode */ + switch ((imm8 >> 2) & 0x03) { + case _SIDD_CMP_EQUAL_ANY >> 2: + { + cmp_ret = vdupq_n_u16(0); + if (la >= 8) { + uint16x8_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + XSSE_PCMP_ANY_WORD(0, 0); XSSE_PCMP_ANY_WORD(1, 1); XSSE_PCMP_ANY_WORD(2, 2); XSSE_PCMP_ANY_WORD(3, 3); + XSSE_PCMP_ANY_WORD(4, 4); XSSE_PCMP_ANY_WORD(5, 5); XSSE_PCMP_ANY_WORD(6, 6); XSSE_PCMP_ANY_WORD(7, 7); + + XSSE_VORR_U16(0, 1); XSSE_VORR_U16(2, 3); XSSE_VORR_U16(4, 5); XSSE_VORR_U16(6, 7); + XSSE_VORR_U16(0, 2); XSSE_VORR_U16(4, 6); XSSE_VORR_U16(0, 4); + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 4) { + uint16x8_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + XSSE_PCMP_ANY_WORD(0, 0); XSSE_PCMP_ANY_WORD(1, 1); XSSE_PCMP_ANY_WORD(2, 2); XSSE_PCMP_ANY_WORD(3, 3); + XSSE_VORR_U16(0, 1); XSSE_VORR_U16(2, 3); XSSE_VORR_U16(0, 2); + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + checked = 4; + la2 -= 4; + } + if (la2 >= 2) { + uint16x8_t tmp_cmp_0, tmp_cmp_1; + if (checked == 4) { + XSSE_PCMP_ANY_WORD(0, 4); XSSE_PCMP_ANY_WORD(1, 5); + } else { + XSSE_PCMP_ANY_WORD(0, 0); XSSE_PCMP_ANY_WORD(1, 1); + } + XSSE_VORR_U16(0, 1); + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + checked += 2; + la2 -= 2; + } + if (la2 >= 1) { + uint16x8_t tmp_cmp_0; + switch (checked) { + case 6: XSSE_PCMP_ANY_WORD(0, 6); break; + case 4: XSSE_PCMP_ANY_WORD(0, 4); break; + case 2: XSSE_PCMP_ANY_WORD(0, 2); break; + case 0: XSSE_PCMP_ANY_WORD(0, 0); break; + default: XSSE_UNREACHABLE(); + } + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + } + } + cmp_ret = vbicq_u16(cmp_ret, b_invalid_mask); + } + break; + + case _SIDD_CMP_RANGES >> 2: + { + cmp_ret = vdupq_n_u16(0); + if (la >= 8) { + uint16x8_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + XSSE_PCMP_RANGES_WORD(0, 0); XSSE_PCMP_RANGES_WORD(1, 2); XSSE_PCMP_RANGES_WORD(2, 4); XSSE_PCMP_RANGES_WORD(3, 6); + XSSE_VORR_U16(0, 1); XSSE_VORR_U16(2, 3); XSSE_VORR_U16(0, 2); + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 4) { + uint16x8_t tmp_cmp_0, tmp_cmp_1; + XSSE_PCMP_RANGES_WORD(0, 0); XSSE_PCMP_RANGES_WORD(1, 2); + XSSE_VORR_U16(0, 1); + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + checked = 4; + la2 -= 4; + } + if (la2 >= 2) { + uint16x8_t tmp_cmp_0; + if (checked == 4) { + XSSE_PCMP_RANGES_WORD(0, 4); + } else { + XSSE_PCMP_RANGES_WORD(0, 0); + } + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + } + } + cmp_ret = vbicq_u16(cmp_ret, b_invalid_mask); + } + break; + + case _SIDD_CMP_EQUAL_EACH >> 2: + { + uint16x8_t and_invalid_mask = vandq_u16(a_invalid_mask, b_invalid_mask); + uint16x8_t xor_invalid_mask = veorq_u16(a_invalid_mask, b_invalid_mask); + XSSE_PCMP_EACH_WORD(); + cmp_ret = vorrq_u16(cmp_ret, and_invalid_mask); + cmp_ret = vbicq_u16(cmp_ret, xor_invalid_mask); + } + break; + + case _SIDD_CMP_EQUAL_ORDERED >> 2: + { + cmp_ret = vdupq_n_u16(0xFFFF); + uint16x8_t repeat_full_bits = vdupq_n_u16(0xFFFF); + if (la >= 8) { + uint16x8_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + XSSE_PCMP_ORDERED_WORD(0, 0); XSSE_PCMP_ORDERED_WORD(1, 1); XSSE_PCMP_ORDERED_WORD(2, 2); XSSE_PCMP_ORDERED_WORD(3, 3); + XSSE_PCMP_ORDERED_WORD(4, 4); XSSE_PCMP_ORDERED_WORD(5, 5); XSSE_PCMP_ORDERED_WORD(6, 6); XSSE_PCMP_ORDERED_WORD(7, 7); + + XSSE_VAND_U16(0, 1); XSSE_VAND_U16(2, 3); XSSE_VAND_U16(4, 5); XSSE_VAND_U16(6, 7); + XSSE_VAND_U16(0, 2); XSSE_VAND_U16(4, 6); XSSE_VAND_U16(0, 4); + cmp_ret = vandq_u16(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 4) { + uint16x8_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + XSSE_PCMP_ORDERED_WORD(0, 0); XSSE_PCMP_ORDERED_WORD(1, 1); XSSE_PCMP_ORDERED_WORD(2, 2); XSSE_PCMP_ORDERED_WORD(3, 3); + XSSE_VAND_U16(0, 1); XSSE_VAND_U16(2, 3); XSSE_VAND_U16(0, 2); + cmp_ret = vandq_u16(cmp_ret, tmp_cmp_0); + checked = 4; + la2 -= 4; + } + if (la2 >= 2) { + uint16x8_t tmp_cmp_0, tmp_cmp_1; + if (checked == 4) { + XSSE_PCMP_ORDERED_WORD(0, 4); XSSE_PCMP_ORDERED_WORD(1, 5); + } else { + XSSE_PCMP_ORDERED_WORD(0, 0); XSSE_PCMP_ORDERED_WORD(1, 1); + } + XSSE_VAND_U16(0, 1); + cmp_ret = vandq_u16(cmp_ret, tmp_cmp_0); + checked += 2; + la2 -= 2; + } + if (la2 >= 1) { + uint16x8_t tmp_cmp_0; + switch (checked) { + case 6: XSSE_PCMP_ORDERED_WORD(0, 6); break; + case 4: XSSE_PCMP_ORDERED_WORD(0, 4); break; + case 2: XSSE_PCMP_ORDERED_WORD(0, 2); break; + case 0: XSSE_PCMP_ORDERED_WORD(0, 0); break; + default: XSSE_UNREACHABLE(); + } + cmp_ret = vandq_u16(cmp_ret, tmp_cmp_0); + } + } + } + break; + } + + /* negate */ + if (imm8 & _SIDD_NEGATIVE_POLARITY) { + uint16x8_t not_cmp_ret = vmvnq_u16(cmp_ret); + if (imm8 & _SIDD_MASKED_NEGATIVE_POLARITY) { + cmp_ret = vbslq_u16(cmp_ret, not_cmp_ret, b_invalid_mask); + } else { + cmp_ret = not_cmp_ret; + } + } + + uint64x2_t cmp_ret_64 = vreinterpretq_u64_u16(cmp_ret); + + _xsse_pcmp_str_result_t result; + result.cf = (vgetq_lane_u64(cmp_ret_64, 0) | vgetq_lane_u64(cmp_ret_64, 1)) != 0; + result.zf = lb < 8; + result.sf = la < 8; + result.mask = vreinterpretq_u8_u16(cmp_ret); + return result; + +#undef XSSE_VAND_U16 +#undef XSSE_VORR_U16 +#undef XSSE_PCMP_ORDERED_WORD +#undef XSSE_PCMP_EACH_WORD +#undef XSSE_PCMP_ANY_WORD +#undef XSSE_VORR_U16 +#undef XSSE_VAND_U16 + } else { + /* byte */ +#define XSSE_PCMP_ANY_BYTE(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) ? vceqq_s8(vdupq_n_s8(vgetq_lane_s8(a, l)), b) : vceqq_u8(vdupq_n_u8(vgetq_lane_u8(ua, l)), ub) +#define XSSE_PCMP_RANGES_BYTE(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) \ + ? vandq_u8(vcleq_s8(vdupq_n_s8(vgetq_lane_s8(a, (l))), b), vcleq_s8(b, vdupq_n_s8(vgetq_lane_s8(a, (l + 1))))) \ + : vandq_u8(vcleq_u8(vdupq_n_u8(vgetq_lane_u8(ua, (l))), ub), vcleq_u8(ub, vdupq_n_u8(vgetq_lane_u8(ua, (l + 1))))) +#define XSSE_PCMP_EACH_BYTE() cmp_ret = (imm8 & 0x02) ? vceqq_s8(a, b) : vceqq_u8(ua, ub) +#define XSSE_PCMP_ORDERED_BYTE(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) \ + ? vextq_u8(vbicq_u8(vceqq_s8(vdupq_n_s8(vgetq_lane_s8(a, l)), b), b_invalid_mask), repeat_full_bits, l) \ + : vextq_u8(vbicq_u8(vceqq_u8(vdupq_n_u8(vgetq_lane_u8(ua, l)), ub), b_invalid_mask), repeat_full_bits, l) + +#define XSSE_VORR_U8(a, b) tmp_cmp_##a = vorrq_u8(tmp_cmp_##a, tmp_cmp_##b); +#define XSSE_VAND_U8(a, b) tmp_cmp_##a = vandq_u8(tmp_cmp_##a, tmp_cmp_##b); + + if (check_null) { + uint8x16_t repeat_nul = vdupq_n_u8(0); + uint8x16_t cmp_nul_a = vceqq_u8(vreinterpretq_u8_s8(a), repeat_nul); + uint8x16_t cmp_nul_b = vceqq_u8(vreinterpretq_u8_s8(b), repeat_nul); + + uint64_t low_a = vgetq_lane_u64(vreinterpretq_u64_u8(cmp_nul_a), 0); + uint64_t high_a = vgetq_lane_u64(vreinterpretq_u64_u8(cmp_nul_a), 1); + if (low_a != 0) { + la = _xsse_seach_least_significant_byte_index(low_a); + } else if (high_a != 0) { + la = _xsse_seach_least_significant_byte_index(high_a) + 8; + } else { + la = 16; + } + + uint64_t low_b = vgetq_lane_u64(vreinterpretq_u64_u8(cmp_nul_b), 0); + uint64_t high_b = vgetq_lane_u64(vreinterpretq_u64_u8(cmp_nul_b), 1); + if (low_b != 0) { + lb = _xsse_seach_least_significant_byte_index(low_b); + } else if (high_b != 0) { + lb = _xsse_seach_least_significant_byte_index(high_b) + 8; + } else { + lb = 16; + } + } + + uint8x16_t cmp_ret; + uint8x16_t lanes = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + uint8x16_t a_threshold = vdupq_n_u8(la); + uint8x16_t b_threshold = vdupq_n_u8(lb); + uint8x16_t a_invalid_mask = vcgeq_u8(lanes, a_threshold); + uint8x16_t b_invalid_mask = vcgeq_u8(lanes, b_threshold); + + uint8x16_t ua, ub; + if ((imm8 & 0x02) == 0) { + ua = vreinterpretq_u8_s8(a); + ub = vreinterpretq_u8_s8(b); + } + + /* mode */ + switch ((imm8 >> 2) & 0x03) { + case _SIDD_CMP_EQUAL_ANY >> 2: + { + cmp_ret = vdupq_n_u8(0); + if (la >= 16) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + uint8x16_t tmp_cmp_8, tmp_cmp_9, tmp_cmp_10, tmp_cmp_11, tmp_cmp_12, tmp_cmp_13, tmp_cmp_14, tmp_cmp_15; + XSSE_PCMP_ANY_BYTE(0, 0); XSSE_PCMP_ANY_BYTE(1, 1); XSSE_PCMP_ANY_BYTE(2, 2); XSSE_PCMP_ANY_BYTE(3, 3); + XSSE_PCMP_ANY_BYTE(4, 4); XSSE_PCMP_ANY_BYTE(5, 5); XSSE_PCMP_ANY_BYTE(6, 6); XSSE_PCMP_ANY_BYTE(7, 7); + XSSE_PCMP_ANY_BYTE(8, 8); XSSE_PCMP_ANY_BYTE(9, 9); XSSE_PCMP_ANY_BYTE(10, 10); XSSE_PCMP_ANY_BYTE(11, 11); + XSSE_PCMP_ANY_BYTE(12, 12); XSSE_PCMP_ANY_BYTE(13, 13); XSSE_PCMP_ANY_BYTE(14, 14); XSSE_PCMP_ANY_BYTE(15, 15); + + XSSE_VORR_U8(0, 1); XSSE_VORR_U8(2, 3); XSSE_VORR_U8(4, 5); XSSE_VORR_U8(6, 7); + XSSE_VORR_U8(8, 9); XSSE_VORR_U8(10, 11); XSSE_VORR_U8(12, 13); XSSE_VORR_U8(14, 15); + XSSE_VORR_U8(0, 2); XSSE_VORR_U8(4, 6); XSSE_VORR_U8(8, 10); XSSE_VORR_U8(12, 14); + XSSE_VORR_U8(0, 4); XSSE_VORR_U8(8, 12); XSSE_VORR_U8(0, 8); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 8) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + XSSE_PCMP_ANY_BYTE(0, 0); XSSE_PCMP_ANY_BYTE(1, 1); XSSE_PCMP_ANY_BYTE(2, 2); XSSE_PCMP_ANY_BYTE(3, 3); + XSSE_PCMP_ANY_BYTE(4, 4); XSSE_PCMP_ANY_BYTE(5, 5); XSSE_PCMP_ANY_BYTE(6, 6); XSSE_PCMP_ANY_BYTE(7, 7); + + XSSE_VORR_U8(0, 1); XSSE_VORR_U8(2, 3); XSSE_VORR_U8(4, 5); XSSE_VORR_U8(6, 7); + XSSE_VORR_U8(0, 2); XSSE_VORR_U8(4, 6); XSSE_VORR_U8(0, 4); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + checked = 8; + la2 -= 8; + } + if (la2 >= 4) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + if (checked == 8) { + XSSE_PCMP_ANY_BYTE(0, 8); XSSE_PCMP_ANY_BYTE(1, 9); XSSE_PCMP_ANY_BYTE(2, 10); XSSE_PCMP_ANY_BYTE(3, 11); + } else { + XSSE_PCMP_ANY_BYTE(0, 0); XSSE_PCMP_ANY_BYTE(1, 1); XSSE_PCMP_ANY_BYTE(2, 2); XSSE_PCMP_ANY_BYTE(3, 3); + } + XSSE_VORR_U8(0, 1); XSSE_VORR_U8(2, 3); XSSE_VORR_U8(0, 2); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + checked += 4; + la2 -= 4; + } + if (la2 >= 2) { + uint8x16_t tmp_cmp_0, tmp_cmp_1; + switch (checked) { + case 12: XSSE_PCMP_ANY_BYTE(0, 12); XSSE_PCMP_ANY_BYTE(1, 13); break; + case 8: XSSE_PCMP_ANY_BYTE(0, 8); XSSE_PCMP_ANY_BYTE(1, 9); break; + case 4: XSSE_PCMP_ANY_BYTE(0, 4); XSSE_PCMP_ANY_BYTE(1, 5); break; + case 0: XSSE_PCMP_ANY_BYTE(0, 0); XSSE_PCMP_ANY_BYTE(1, 1); break; + default: XSSE_UNREACHABLE(); + } + XSSE_VORR_U8(0, 1); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + checked += 2; + la2 -= 2; + } + if (la2 == 1) { + uint8x16_t tmp_cmp_0; + switch (checked) { + case 14: XSSE_PCMP_ANY_BYTE(0, 14); break; + case 12: XSSE_PCMP_ANY_BYTE(0, 12); break; + case 10: XSSE_PCMP_ANY_BYTE(0, 10); break; + case 8: XSSE_PCMP_ANY_BYTE(0, 8); break; + case 6: XSSE_PCMP_ANY_BYTE(0, 6); break; + case 4: XSSE_PCMP_ANY_BYTE(0, 4); break; + case 2: XSSE_PCMP_ANY_BYTE(0, 2); break; + case 0: XSSE_PCMP_ANY_BYTE(0, 0); break; + default: XSSE_UNREACHABLE(); + } + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + } + } + cmp_ret = vbicq_u8(cmp_ret, b_invalid_mask); + } + break; + + case _SIDD_CMP_RANGES >> 2: + { + cmp_ret = vdupq_n_u8(0); + if (la >= 16) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + XSSE_PCMP_RANGES_BYTE(0, 0); XSSE_PCMP_RANGES_BYTE(1, 2); XSSE_PCMP_RANGES_BYTE(2, 4); XSSE_PCMP_RANGES_BYTE(3, 6); + XSSE_PCMP_RANGES_BYTE(4, 8); XSSE_PCMP_RANGES_BYTE(5, 10); XSSE_PCMP_RANGES_BYTE(6, 12); XSSE_PCMP_RANGES_BYTE(7, 14); + + XSSE_VORR_U8(0, 1); XSSE_VORR_U8(2, 3); XSSE_VORR_U8(4, 5); XSSE_VORR_U8(6, 7); + XSSE_VORR_U8(0, 2); XSSE_VORR_U8(4, 6); XSSE_VORR_U8(0, 4); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 8) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + XSSE_PCMP_RANGES_BYTE(0, 0); XSSE_PCMP_RANGES_BYTE(1, 2); XSSE_PCMP_RANGES_BYTE(2, 4); XSSE_PCMP_RANGES_BYTE(3, 6); + + XSSE_VORR_U8(0, 1); XSSE_VORR_U8(2, 3); XSSE_VORR_U8(0, 2); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + checked = 8; + la2 -= 8; + } + if (la2 >= 4) { + uint8x16_t tmp_cmp_0, tmp_cmp_1; + if (checked == 8) { + XSSE_PCMP_RANGES_BYTE(0, 8); XSSE_PCMP_RANGES_BYTE(1, 10); + } else { + XSSE_PCMP_RANGES_BYTE(0, 0); XSSE_PCMP_RANGES_BYTE(1, 2); + } + XSSE_VORR_U8(0, 1); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + checked += 4; + la2 -= 4; + } + if (la2 >= 2) { + uint8x16_t tmp_cmp_0; + switch (checked) { + case 12: XSSE_PCMP_RANGES_BYTE(0, 12); break; + case 8: XSSE_PCMP_RANGES_BYTE(0, 8); break; + case 4: XSSE_PCMP_RANGES_BYTE(0, 4); break; + case 0: XSSE_PCMP_RANGES_BYTE(0, 0); break; + default: XSSE_UNREACHABLE(); + } + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + } + } + cmp_ret = vbicq_u8(cmp_ret, b_invalid_mask); + } + break; + + case _SIDD_CMP_EQUAL_EACH >> 2: + { + uint8x16_t and_invalid_mask = vandq_u8(a_invalid_mask, b_invalid_mask); + uint8x16_t xor_invalid_mask = veorq_u8(a_invalid_mask, b_invalid_mask); + XSSE_PCMP_EACH_BYTE(); + cmp_ret = vorrq_u8(cmp_ret, and_invalid_mask); + cmp_ret = vbicq_u8(cmp_ret, xor_invalid_mask); + } + break; + + case _SIDD_CMP_EQUAL_ORDERED >> 2: + { + cmp_ret = vdupq_n_u8(0xFF); + uint8x16_t repeat_full_bits = vdupq_n_u8(0xFF); + if (la >= 16) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + uint8x16_t tmp_cmp_8, tmp_cmp_9, tmp_cmp_10, tmp_cmp_11, tmp_cmp_12, tmp_cmp_13, tmp_cmp_14, tmp_cmp_15; + XSSE_PCMP_ORDERED_BYTE(0, 0); XSSE_PCMP_ORDERED_BYTE(1, 1); XSSE_PCMP_ORDERED_BYTE(2, 2); XSSE_PCMP_ORDERED_BYTE(3, 3); + XSSE_PCMP_ORDERED_BYTE(4, 4); XSSE_PCMP_ORDERED_BYTE(5, 5); XSSE_PCMP_ORDERED_BYTE(6, 6); XSSE_PCMP_ORDERED_BYTE(7, 7); + XSSE_PCMP_ORDERED_BYTE(8, 8); XSSE_PCMP_ORDERED_BYTE(9, 9); XSSE_PCMP_ORDERED_BYTE(10, 10); XSSE_PCMP_ORDERED_BYTE(11, 11); + XSSE_PCMP_ORDERED_BYTE(12, 12); XSSE_PCMP_ORDERED_BYTE(13, 13); XSSE_PCMP_ORDERED_BYTE(14, 14); XSSE_PCMP_ORDERED_BYTE(15, 15); + + XSSE_VAND_U8(0, 1); XSSE_VAND_U8(2, 3); XSSE_VAND_U8(4, 5); XSSE_VAND_U8(6, 7); + XSSE_VAND_U8(8, 9); XSSE_VAND_U8(10, 11); XSSE_VAND_U8(12, 13); XSSE_VAND_U8(14, 15); + XSSE_VAND_U8(0, 2); XSSE_VAND_U8(4, 6); XSSE_VAND_U8(8, 10); XSSE_VAND_U8(12, 14); + XSSE_VAND_U8(0, 4); XSSE_VAND_U8(8, 12); XSSE_VAND_U8(0, 8); + cmp_ret = vandq_u8(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 8) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + XSSE_PCMP_ORDERED_BYTE(0, 0); XSSE_PCMP_ORDERED_BYTE(1, 1); XSSE_PCMP_ORDERED_BYTE(2, 2); XSSE_PCMP_ORDERED_BYTE(3, 3); + XSSE_PCMP_ORDERED_BYTE(4, 4); XSSE_PCMP_ORDERED_BYTE(5, 5); XSSE_PCMP_ORDERED_BYTE(6, 6); XSSE_PCMP_ORDERED_BYTE(7, 7); + + XSSE_VAND_U8(0, 1); XSSE_VAND_U8(2, 3); XSSE_VAND_U8(4, 5); XSSE_VAND_U8(6, 7); + XSSE_VAND_U8(0, 2); XSSE_VAND_U8(4, 6); XSSE_VAND_U8(0, 4); + cmp_ret = vandq_u8(cmp_ret, tmp_cmp_0); + checked = 8; + la2 -= 8; + } + if (la2 >= 4) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + if (checked == 8) { + XSSE_PCMP_ORDERED_BYTE(0, 8); XSSE_PCMP_ORDERED_BYTE(1, 9); XSSE_PCMP_ORDERED_BYTE(2, 10); XSSE_PCMP_ORDERED_BYTE(3, 11); + } else { + XSSE_PCMP_ORDERED_BYTE(0, 0); XSSE_PCMP_ORDERED_BYTE(1, 1); XSSE_PCMP_ORDERED_BYTE(2, 2); XSSE_PCMP_ORDERED_BYTE(3, 3); + } + XSSE_VAND_U8(0, 1); XSSE_VAND_U8(2, 3); XSSE_VAND_U8(0, 2); + cmp_ret = vandq_u8(cmp_ret, tmp_cmp_0); + checked += 4; + la2 -= 4; + } + if (la2 >= 2) { + uint8x16_t tmp_cmp_0, tmp_cmp_1; + switch (checked) { + case 12: XSSE_PCMP_ORDERED_BYTE(0, 12); XSSE_PCMP_ORDERED_BYTE(1, 13); break; + case 8: XSSE_PCMP_ORDERED_BYTE(0, 8); XSSE_PCMP_ORDERED_BYTE(1, 9); break; + case 4: XSSE_PCMP_ORDERED_BYTE(0, 4); XSSE_PCMP_ORDERED_BYTE(1, 5); break; + case 0: XSSE_PCMP_ORDERED_BYTE(0, 0); XSSE_PCMP_ORDERED_BYTE(1, 1); break; + default: XSSE_UNREACHABLE(); + } + XSSE_VAND_U8(0, 1); + cmp_ret = vandq_u8(cmp_ret, tmp_cmp_0); + checked += 2; + la2 -= 2; + } + if (la2 == 1) { + uint8x16_t tmp_cmp_0; + switch (checked) { + case 14: XSSE_PCMP_ORDERED_BYTE(0, 14); break; + case 12: XSSE_PCMP_ORDERED_BYTE(0, 12); break; + case 10: XSSE_PCMP_ORDERED_BYTE(0, 10); break; + case 8: XSSE_PCMP_ORDERED_BYTE(0, 8); break; + case 6: XSSE_PCMP_ORDERED_BYTE(0, 6); break; + case 4: XSSE_PCMP_ORDERED_BYTE(0, 4); break; + case 2: XSSE_PCMP_ORDERED_BYTE(0, 2); break; + case 0: XSSE_PCMP_ORDERED_BYTE(0, 0); break; + default: XSSE_UNREACHABLE(); + } + cmp_ret = vandq_u8(cmp_ret, tmp_cmp_0); + } + } + } + break; + } + + /* negate */ + if (imm8 & _SIDD_NEGATIVE_POLARITY) { + uint8x16_t not_cmp_ret = vmvnq_u8(cmp_ret); + if (imm8 & _SIDD_MASKED_NEGATIVE_POLARITY) { + cmp_ret = vbslq_u8(cmp_ret, not_cmp_ret, b_invalid_mask); + } else { + cmp_ret = not_cmp_ret; + } + } + + uint64x2_t cmp_ret_64 = vreinterpretq_u64_u8(cmp_ret); + + _xsse_pcmp_str_result_t result; + result.cf = (vgetq_lane_u64(cmp_ret_64, 0) | vgetq_lane_u64(cmp_ret_64, 1)) != 0; + result.zf = lb < 16; + result.sf = la < 16; + result.mask = cmp_ret; + return result; + +#undef XSSE_PCMP_ANY_BYTE +#undef XSSE_PCMP_RANGES_BYTE +#undef XSSE_PCMP_EACH_BYTE +#undef XSSE_PCMP_EACH_BYTE +#undef XSSE_PCMP_ORDERED_BYTE +#undef XSSE_VORR_U8 +#undef XSSE_VAND_U8 + } +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_a(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + return result.cf == 0 && result.zf == 0; +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_c(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + return result.cf; +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_i(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(result.mask), 0); + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(result.mask), 1); + if (imm8 & 0x01) { + if (imm8 & _SIDD_MOST_SIGNIFICANT) { + if (high != 0) { + return _xsse_seach_most_significant_word_index(high) + 4; + } else if (low != 0) { + return _xsse_seach_most_significant_word_index(low); + } else { + return 8; + } + } else { + if (low != 0) { + return _xsse_seach_least_significant_word_index(low); + } else if (high != 0) { + return _xsse_seach_least_significant_word_index(high) + 4; + } else { + return 8; + } + } + } else { + if (imm8 & _SIDD_MOST_SIGNIFICANT) { + if (high != 0) { + return _xsse_seach_most_significant_byte_index(high) + 8; + } else if (low != 0) { + return _xsse_seach_most_significant_byte_index(low); + } else { + return 16; + } + } else { + if (low != 0) { + return _xsse_seach_least_significant_byte_index(low); + } else if (high != 0) { + return _xsse_seach_least_significant_byte_index(high) + 8; + } else { + return 16; + } + } + } +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE __m128i _xsse_pcmp_str_m(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + + if (imm8 & _SIDD_UNIT_MASK) { + return vreinterpretq_s8_u8(result.mask); + } + + if (imm8 & 0x01) { + uint32x4_t high_bits = vreinterpretq_u32_u16(vshrq_n_u16(vreinterpretq_u16_u8(result.mask), 15)); + uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(high_bits, high_bits, 15)); + uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 30)); + + return vreinterpretq_s8_u8((uint8x16_t) { + (vgetq_lane_u8(paired64, 0) | (vgetq_lane_u8(paired64, 8) << 4)), 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }); + } else { + uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(result.mask, 7)); + uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); + uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); + uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); + + return vreinterpretq_s8_u8((uint8x16_t) { + vgetq_lane_u8(paired64, 0), vgetq_lane_u8(paired64, 8), 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }); + } +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_o(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(result.mask), 0); + return low & 1; +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_s(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + return result.sf; +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_z(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + return result.zf; +} + +#define _mm_cmpestra(a, la, b, lb, imm8) _xsse_pcmp_str_a(a, la, b, lb, imm8, 0) +#define _mm_cmpestrc(a, la, b, lb, imm8) _xsse_pcmp_str_c(a, la, b, lb, imm8, 0) +#define _mm_cmpestri(a, la, b, lb, imm8) _xsse_pcmp_str_i(a, la, b, lb, imm8, 0) +#define _mm_cmpestrm(a, la, b, lb, imm8) _xsse_pcmp_str_m(a, la, b, lb, imm8, 0) +#define _mm_cmpestro(a, la, b, lb, imm8) _xsse_pcmp_str_o(a, la, b, lb, imm8, 0) +#define _mm_cmpestrs(a, la, b, lb, imm8) _xsse_pcmp_str_s(a, la, b, lb, imm8, 0) +#define _mm_cmpestrz(a, la, b, lb, imm8) _xsse_pcmp_str_z(a, la, b, lb, imm8, 0) + +#define _mm_cmpistra(a, b, imm8) _xsse_pcmp_str_a(a, 0, b, 0, imm8, 1) +#define _mm_cmpistrc(a, b, imm8) _xsse_pcmp_str_c(a, 0, b, 0, imm8, 1) +#define _mm_cmpistri(a, b, imm8) _xsse_pcmp_str_i(a, 0, b, 0, imm8, 1) +#define _mm_cmpistrm(a, b, imm8) _xsse_pcmp_str_m(a, 0, b, 0, imm8, 1) +#define _mm_cmpistro(a, b, imm8) _xsse_pcmp_str_o(a, 0, b, 0, imm8, 1) +#define _mm_cmpistrs(a, b, imm8) _xsse_pcmp_str_s(a, 0, b, 0, imm8, 1) +#define _mm_cmpistrz(a, b, imm8) _xsse_pcmp_str_z(a, 0, b, 0, imm8, 1) + + +/***************************************************************************** + * CRC * + *****************************************************************************/ + +#ifdef __ARM_FEATURE_CRC32 +#include +#define _mm_crc32_u8(crc, v) (__crc32cb(crc, v)) +#define _mm_crc32_u16(crc, v) (__crc32ch(crc, v)) +#define _mm_crc32_u32(crc, v) (__crc32cw(crc, v)) +#define _mm_crc32_u64(crc, v) (__crc32cd(crc, v)) +#else +static XSSE_FORCE_INLINE int _mm_crc32_u8(unsigned int crc, unsigned char v) +{ + crc ^= v; + static const uint32_t crc32_nibble_tbl[] = { + 0x00000000, 0x105ec76f, 0x20bd8ede, 0x30e349b1, + 0x417b1dbc, 0x5125dad3, 0x61c69362, 0x7198540d, + 0x82f63b78, 0x92a8fc17, 0xa24bb5a6, 0xb21572c9, + 0xc38d26c4, 0xd3d3e1ab, 0xe330a81a, 0xf36e6f75 + }; + crc = (crc >> 4) ^ crc32_nibble_tbl[crc & 0x0F]; + crc = (crc >> 4) ^ crc32_nibble_tbl[crc & 0x0F]; + return crc; +} +static XSSE_FORCE_INLINE int _mm_crc32_u16(unsigned int crc, unsigned short v) +{ + crc = _mm_crc32_u8(crc, (unsigned char) (v & 0xFF)); + crc = _mm_crc32_u8(crc, (unsigned char) (v >> 8)); + return crc; +} +static XSSE_FORCE_INLINE int _mm_crc32_u32(unsigned int crc, unsigned int v) +{ + crc = _mm_crc32_u16(crc, (unsigned short) (v & 0xFFFF)); + crc = _mm_crc32_u16(crc, (unsigned short) (v >> 16)); + return crc; +} +static XSSE_FORCE_INLINE uint64_t _mm_crc32_u64(uint64_t crc, uint64_t v) +{ + unsigned int crc32 = (unsigned int) crc; + crc32 = _mm_crc32_u32(crc32, (unsigned int) (v & 0xFFFFFFFF)); + crc32 = _mm_crc32_u32(crc32, (unsigned int) (v >> 32)); + return crc32; +} +#endif /* __ARM_FEATURE_CRC32 */ + +#endif /* SSE4_2 */ + +#endif /* XSSE_H */ diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php index f375c60ff0a4c..7f60be40b1bc8 100644 --- a/ext/date/php_date.stub.php +++ b/ext/date/php_date.stub.php @@ -227,7 +227,7 @@ function timezone_offset_get(DateTimeZone $object, DateTimeInterface $datetime): * @refcount 1 */ function timezone_transitions_get( - DateTimeZone $object, int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false {} + DateTimeZone $object, int $timestampBegin = PHP_INT_MIN, int $timestampEnd = 2147483647): array|false {} /** * @return array|false @@ -615,7 +615,7 @@ public function getOffset(DateTimeInterface $datetime): int {} * @tentative-return-type * @alias timezone_transitions_get */ - public function getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false {} + public function getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = 2147483647): array|false {} /** * @return array|false diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 82e9f0f1718ae..2a0fd6e5ac009 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 093743b4fe7a698d1262cc1a81b60a85064fdccb */ + * Stub hash: 4e61617ca7c877aa3811d674d47850f23157074b */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) @@ -175,7 +175,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timezone_transitions_get, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampBegin, IS_LONG, 0, "PHP_INT_MIN") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampEnd, IS_LONG, 0, "PHP_INT_MAX") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampEnd, IS_LONG, 0, "2147483647") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timezone_location_get, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) @@ -436,7 +436,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DateTimeZone_getTransitions, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampBegin, IS_LONG, 0, "PHP_INT_MIN") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampEnd, IS_LONG, 0, "PHP_INT_MAX") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampEnd, IS_LONG, 0, "2147483647") ZEND_END_ARG_INFO() #define arginfo_class_DateTimeZone_getLocation arginfo_class_DateTime_getLastErrors diff --git a/ext/dom/element.c b/ext/dom/element.c index a5b0652019864..4aa56f3691b2f 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -177,10 +177,7 @@ zend_result dom_element_class_name_write(dom_object *obj, zval *newval) } /* }}} */ -/* {{{ classList TokenList -URL: https://dom.spec.whatwg.org/#dom-element-classlist -*/ -zend_result dom_element_class_list_read(dom_object *obj, zval *retval) +zval *dom_element_class_list_zval(dom_object *obj) { const uint32_t PROP_INDEX = 0; @@ -191,7 +188,15 @@ zend_result dom_element_class_list_read(dom_object *obj, zval *retval) ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX); #endif - zval *cached_token_list = OBJ_PROP_NUM(&obj->std, PROP_INDEX); + return OBJ_PROP_NUM(&obj->std, PROP_INDEX); +} + +/* {{{ classList TokenList +URL: https://dom.spec.whatwg.org/#dom-element-classlist +*/ +zend_result dom_element_class_list_read(dom_object *obj, zval *retval) +{ + zval *cached_token_list = dom_element_class_list_zval(obj); if (Z_ISUNDEF_P(cached_token_list)) { object_init_ex(cached_token_list, dom_token_list_class_entry); dom_token_list_object *intern = php_dom_token_list_from_obj(Z_OBJ_P(cached_token_list)); diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 0357252dd47f3..f2c1ec2c62b8b 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -99,6 +99,7 @@ static zend_object_handlers dom_modern_nodelist_object_handlers; static zend_object_handlers dom_html_collection_object_handlers; static zend_object_handlers dom_object_namespace_node_handlers; static zend_object_handlers dom_modern_domimplementation_object_handlers; +static zend_object_handlers dom_modern_element_object_handlers; static zend_object_handlers dom_token_list_object_handlers; #ifdef LIBXML_XPATH_ENABLED zend_object_handlers dom_xpath_object_handlers; @@ -662,6 +663,21 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ } /* }}} */ +static zend_object *dom_modern_element_clone_obj(zend_object *zobject) +{ + zend_object *clone = dom_objects_store_clone_obj(zobject); + + /* The $classList property is unique per element, and cached due to its [[SameObject]] requirement. + * Remove it from the clone so the clone will get a fresh instance upon demand. */ + zval *class_list = dom_element_class_list_zval(php_dom_obj_from_obj(clone)); + if (!Z_ISUNDEF_P(class_list)) { + zval_ptr_dtor(class_list); + ZVAL_UNDEF(class_list); + } + + return clone; +} + static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject) { dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(zobject); @@ -756,6 +772,9 @@ PHP_MINIT_FUNCTION(dom) * one instance per parent object. */ dom_modern_domimplementation_object_handlers.clone_obj = NULL; + memcpy(&dom_modern_element_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); + dom_modern_element_object_handlers.clone_obj = dom_modern_element_clone_obj; + memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_nnodemap_object_handlers.free_obj = dom_nnodemap_objects_free_storage; dom_nnodemap_object_handlers.read_dimension = dom_nodemap_read_dimension; @@ -1086,7 +1105,7 @@ PHP_MINIT_FUNCTION(dom) dom_modern_element_class_entry = register_class_Dom_Element(dom_modern_node_class_entry, dom_modern_parentnode_class_entry, dom_modern_childnode_class_entry); dom_modern_element_class_entry->create_object = dom_objects_new; - dom_modern_element_class_entry->default_object_handlers = &dom_object_handlers; + dom_modern_element_class_entry->default_object_handlers = &dom_modern_element_object_handlers; zend_hash_init(&dom_modern_element_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL); @@ -1111,7 +1130,7 @@ PHP_MINIT_FUNCTION(dom) dom_html_element_class_entry = register_class_Dom_HTMLElement(dom_modern_element_class_entry); dom_html_element_class_entry->create_object = dom_objects_new; - dom_html_element_class_entry->default_object_handlers = &dom_object_handlers; + dom_html_element_class_entry->default_object_handlers = &dom_modern_element_object_handlers; zend_hash_add_new_ptr(&classes, dom_html_element_class_entry->name, &dom_modern_element_prop_handlers); dom_text_class_entry = register_class_DOMText(dom_characterdata_class_entry); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 31fec5a7cabcb..ede77f08626cc 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -179,6 +179,7 @@ bool php_dom_create_nullable_object(xmlNodePtr obj, zval *return_value, dom_obje xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive); void dom_set_document_ref_pointers(xmlNodePtr node, php_libxml_ref_obj *document); void dom_set_document_ref_pointers_attr(xmlAttrPtr attr, php_libxml_ref_obj *document); +zval *dom_element_class_list_zval(dom_object *obj); typedef enum { DOM_LOAD_STRING = 0, diff --git a/ext/dom/tests/modern/token_list/gh18744.phpt b/ext/dom/tests/modern/token_list/gh18744.phpt new file mode 100644 index 0000000000000..a9109df789d8f --- /dev/null +++ b/ext/dom/tests/modern/token_list/gh18744.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-18744 (classList works not correctly if copy HTMLElement by clone keyword.) +--EXTENSIONS-- +dom +--FILE-- +createElement('div'); +$ele1->classList->add('foo'); +$ele2 = clone $ele1; +$ele2->classList->add('bar'); + +echo "Element1 class: " . $ele1->getAttribute('class'); +echo "\n"; +echo "Element2 class: " . $ele2->getAttribute('class'); +echo "\n"; + +var_dump($ele1->classList !== $ele2->classList); +// These comparisons are not pointless: they're getters and should not create new objects +var_dump($ele1->classList === $ele1->classList); +var_dump($ele2->classList === $ele2->classList); + +?> +--EXPECT-- +Element1 class: foo +Element2 class: foo bar +bool(true) +bool(true) +bool(true) diff --git a/ext/intl/dateformat/dateformat_parse.c b/ext/intl/dateformat/dateformat_parse.c index 129e007107db7..2bdde08bcaced 100644 --- a/ext/intl/dateformat/dateformat_parse.c +++ b/ext/intl/dateformat/dateformat_parse.c @@ -185,12 +185,10 @@ PHP_METHOD(IntlDateFormatter, parseToCalendar) DATE_FORMAT_METHOD_FETCH_OBJECT; if (z_parse_pos) { - zval *z_parse_pos_tmp = z_parse_pos; - ZVAL_DEREF(z_parse_pos_tmp); - bool failed = false; - zend_long long_parse_pos = zval_try_get_long(z_parse_pos_tmp, &failed); + bool failed; + zend_long long_parse_pos = zval_try_get_long(z_parse_pos, &failed); if (failed) { - zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos_tmp)); + zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos)); RETURN_THROWS(); } if (ZEND_LONG_INT_OVFL(long_parse_pos)) { diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 0582fbc40a534..88f9b706ed243 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -3924,7 +3924,7 @@ static uint32_t *make_conversion_map(HashTable *target_hash, size_t *conversion_ uint32_t *mapelm = convmap; ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) { - bool failed = true; + bool failed; zend_long tmp = zval_try_get_long(hash_entry, &failed); if (failed) { efree(convmap); diff --git a/ext/mbstring/tests/mb_encode_numericentity_references.phpt b/ext/mbstring/tests/mb_encode_numericentity_references.phpt new file mode 100644 index 0000000000000..682be9b2cd1e2 --- /dev/null +++ b/ext/mbstring/tests/mb_encode_numericentity_references.phpt @@ -0,0 +1,12 @@ +--TEST-- +mb_encode_numericentity() reference handling +--EXTENSIONS-- +mbstring +--FILE-- + +--EXPECT-- +string(0) "" diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 4017c72a35a67..2eb994cad3d5e 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -1068,7 +1068,6 @@ PHP_FUNCTION(odbc_execute) RETURN_FALSE; } filename = estrndup(&ZSTR_VAL(tmpstr)[1], ZSTR_LEN(tmpstr) - 2); - filename[strlen(filename)] = '\0'; /* Check the basedir */ if (php_check_open_basedir(filename)) { diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index aab240dc8506a..6518a719314fd 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -583,7 +583,7 @@ PHP_FUNCTION(openssl_spki_new) zval *zpkey = NULL; EVP_PKEY *pkey = NULL; NETSCAPE_SPKI *spki=NULL; - const EVP_MD *mdtype; + const EVP_MD *mdtype = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|l", &zpkey, php_openssl_pkey_ce, &challenge, &challenge_len, &algo) == FAILURE) { RETURN_THROWS(); @@ -647,6 +647,7 @@ PHP_FUNCTION(openssl_spki_new) goto cleanup; cleanup: + php_openssl_release_evp_md(mdtype); EVP_PKEY_free(pkey); if (spki != NULL) { NETSCAPE_SPKI_free(spki); @@ -1821,7 +1822,6 @@ PHP_FUNCTION(openssl_csr_sign) X509_free(new_cert); } - PHP_SSL_REQ_DISPOSE(&req); EVP_PKEY_free(priv_key); EVP_PKEY_free(key); if (csr_str) { @@ -1830,6 +1830,7 @@ PHP_FUNCTION(openssl_csr_sign) if (cert_str && cert && cert != new_cert) { X509_free(cert); } + PHP_SSL_REQ_DISPOSE(&req); } /* }}} */ @@ -2104,7 +2105,8 @@ PHP_FUNCTION(openssl_pkey_export_to_file) } if (!php_openssl_check_path(filename, filename_len, file_path, 2)) { - goto clean_exit_key; + EVP_PKEY_free(key); + return; } PHP_SSL_REQ_INIT(&req); @@ -2139,10 +2141,9 @@ PHP_FUNCTION(openssl_pkey_export_to_file) } clean_exit: - PHP_SSL_REQ_DISPOSE(&req); BIO_free(bio_out); -clean_exit_key: EVP_PKEY_free(key); + PHP_SSL_REQ_DISPOSE(&req); } /* }}} */ @@ -2204,9 +2205,9 @@ PHP_FUNCTION(openssl_pkey_export) php_openssl_store_errors(); } } - PHP_SSL_REQ_DISPOSE(&req); EVP_PKEY_free(key); BIO_free(bio_out); + PHP_SSL_REQ_DISPOSE(&req); } /* }}} */ @@ -2686,6 +2687,7 @@ PHP_FUNCTION(openssl_pkcs7_encrypt) if (recipcerts) { sk_X509_pop_free(recipcerts, X509_free); } + php_openssl_release_evp_cipher(cipher); } /* }}} */ @@ -3334,6 +3336,7 @@ PHP_FUNCTION(openssl_cms_encrypt) if (recipcerts) { sk_X509_pop_free(recipcerts, X509_free); } + php_openssl_release_evp_cipher(cipher); } /* }}} */ @@ -3969,7 +3972,7 @@ PHP_FUNCTION(openssl_sign) } if (method_str) { - mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str)); + mdtype = php_openssl_get_evp_md_by_name(ZSTR_VAL(method_str)); } else { mdtype = php_openssl_get_evp_md_from_algo(method_long); } @@ -3996,6 +3999,7 @@ PHP_FUNCTION(openssl_sign) RETVAL_FALSE; } EVP_MD_CTX_destroy(md_ctx); + php_openssl_release_evp_md(mdtype); EVP_PKEY_free(pkey); } /* }}} */ @@ -4027,7 +4031,7 @@ PHP_FUNCTION(openssl_verify) PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(signature_len, signature, 2); if (method_str) { - mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str)); + mdtype = php_openssl_get_evp_md_by_name(ZSTR_VAL(method_str)); } else { mdtype = php_openssl_get_evp_md_from_algo(method_long); } @@ -4041,6 +4045,7 @@ PHP_FUNCTION(openssl_verify) if (!EG(exception)) { php_error_docref(NULL, E_WARNING, "Supplied key param cannot be coerced into a public key"); } + php_openssl_release_evp_md(mdtype); RETURN_FALSE; } @@ -4051,6 +4056,7 @@ PHP_FUNCTION(openssl_verify) php_openssl_store_errors(); } EVP_MD_CTX_destroy(md_ctx); + php_openssl_release_evp_md(mdtype); EVP_PKEY_free(pkey); RETURN_LONG(err); } @@ -4323,7 +4329,7 @@ PHP_FUNCTION(openssl_digest) if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) { RETURN_THROWS(); } - mdtype = EVP_get_digestbyname(method); + mdtype = php_openssl_get_evp_md_by_name(method); if (!mdtype) { php_error_docref(NULL, E_WARNING, "Unknown digest algorithm"); RETURN_FALSE; @@ -4356,6 +4362,7 @@ PHP_FUNCTION(openssl_digest) } EVP_MD_CTX_destroy(md_ctx); + php_openssl_release_evp_md(mdtype); } /* }}} */ diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index 02210cce8b1cb..42b70c72a9cd0 100644 --- a/ext/openssl/openssl_backend_common.c +++ b/ext/openssl/openssl_backend_common.c @@ -369,11 +369,11 @@ int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args if (strcmp(req->digest_name, "null") == 0) { req->digest = req->md_alg = EVP_md_null(); } else { - req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name); + req->digest = req->md_alg = php_openssl_get_evp_md_by_name(req->digest_name); } } if (req->md_alg == NULL) { - req->md_alg = req->digest = EVP_sha1(); + req->md_alg = req->digest = php_openssl_get_evp_md_by_name("sha1"); php_openssl_store_errors(); } @@ -417,6 +417,10 @@ void php_openssl_dispose_config(struct php_x509_request * req) NCONF_free(req->req_config); req->req_config = NULL; } + if (req->md_alg != NULL && req->md_alg != EVP_md_null()) { + php_openssl_release_evp_md(req->md_alg); + } + php_openssl_release_evp_cipher(req->priv_key_encrypt_cipher); } zend_result php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded) @@ -469,92 +473,6 @@ zend_result php_openssl_write_rand_file(const char * file, int egdsocket, int se return SUCCESS; } -EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo) { - EVP_MD *mdtype; - - switch (algo) { - case OPENSSL_ALGO_SHA1: - mdtype = (EVP_MD *) EVP_sha1(); - break; - case OPENSSL_ALGO_MD5: - mdtype = (EVP_MD *) EVP_md5(); - break; -#ifndef OPENSSL_NO_MD4 - case OPENSSL_ALGO_MD4: - mdtype = (EVP_MD *) EVP_md4(); - break; -#endif -#ifndef OPENSSL_NO_MD2 - case OPENSSL_ALGO_MD2: - mdtype = (EVP_MD *) EVP_md2(); - break; -#endif - case OPENSSL_ALGO_SHA224: - mdtype = (EVP_MD *) EVP_sha224(); - break; - case OPENSSL_ALGO_SHA256: - mdtype = (EVP_MD *) EVP_sha256(); - break; - case OPENSSL_ALGO_SHA384: - mdtype = (EVP_MD *) EVP_sha384(); - break; - case OPENSSL_ALGO_SHA512: - mdtype = (EVP_MD *) EVP_sha512(); - break; -#ifndef OPENSSL_NO_RMD160 - case OPENSSL_ALGO_RMD160: - mdtype = (EVP_MD *) EVP_ripemd160(); - break; -#endif - default: - return NULL; - break; - } - return mdtype; -} - -const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo) { - switch (algo) { -#ifndef OPENSSL_NO_RC2 - case PHP_OPENSSL_CIPHER_RC2_40: - return EVP_rc2_40_cbc(); - break; - case PHP_OPENSSL_CIPHER_RC2_64: - return EVP_rc2_64_cbc(); - break; - case PHP_OPENSSL_CIPHER_RC2_128: - return EVP_rc2_cbc(); - break; -#endif - -#ifndef OPENSSL_NO_DES - case PHP_OPENSSL_CIPHER_DES: - return EVP_des_cbc(); - break; - case PHP_OPENSSL_CIPHER_3DES: - return EVP_des_ede3_cbc(); - break; -#endif - -#ifndef OPENSSL_NO_AES - case PHP_OPENSSL_CIPHER_AES_128_CBC: - return EVP_aes_128_cbc(); - break; - case PHP_OPENSSL_CIPHER_AES_192_CBC: - return EVP_aes_192_cbc(); - break; - case PHP_OPENSSL_CIPHER_AES_256_CBC: - return EVP_aes_256_cbc(); - break; -#endif - - - default: - return NULL; - break; - } -} - void php_openssl_backend_init(void) { #ifdef LIBRESSL_VERSION_NUMBER @@ -1932,7 +1850,7 @@ PHP_OPENSSL_API zend_string* php_openssl_encrypt( PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(tag_len, tag_len); - cipher_type = EVP_get_cipherbyname(method); + cipher_type = php_openssl_get_evp_cipher_by_name(method); if (!cipher_type) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); return NULL; @@ -1940,6 +1858,7 @@ PHP_OPENSSL_API zend_string* php_openssl_encrypt( cipher_ctx = EVP_CIPHER_CTX_new(); if (!cipher_ctx) { + php_openssl_release_evp_cipher(cipher_type); php_error_docref(NULL, E_WARNING, "Failed to create cipher context"); return NULL; } @@ -1998,6 +1917,7 @@ PHP_OPENSSL_API zend_string* php_openssl_encrypt( } EVP_CIPHER_CTX_reset(cipher_ctx); EVP_CIPHER_CTX_free(cipher_ctx); + php_openssl_release_evp_cipher(cipher_type); return outbuf; } @@ -2024,7 +1944,7 @@ PHP_OPENSSL_API zend_string* php_openssl_decrypt( PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(tag_len, tag); - cipher_type = EVP_get_cipherbyname(method); + cipher_type = php_openssl_get_evp_cipher_by_name(method); if (!cipher_type) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); return NULL; @@ -2032,6 +1952,7 @@ PHP_OPENSSL_API zend_string* php_openssl_decrypt( cipher_ctx = EVP_CIPHER_CTX_new(); if (!cipher_ctx) { + php_openssl_release_evp_cipher(cipher_type); php_error_docref(NULL, E_WARNING, "Failed to create cipher context"); return NULL; } @@ -2077,14 +1998,15 @@ PHP_OPENSSL_API zend_string* php_openssl_decrypt( } EVP_CIPHER_CTX_reset(cipher_ctx); EVP_CIPHER_CTX_free(cipher_ctx); + php_openssl_release_evp_cipher(cipher_type); return outbuf; } -const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *method) +const EVP_CIPHER *php_openssl_get_evp_cipher_by_name_with_warning(const char *method) { const EVP_CIPHER *cipher_type; - cipher_type = EVP_get_cipherbyname(method); + cipher_type = php_openssl_get_evp_cipher_by_name(method); if (!cipher_type) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); return NULL; @@ -2096,16 +2018,26 @@ const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *method) PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(const char *method) { - const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name(method); + const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name_with_warning(method); + if (cipher_type == NULL) { + return -1; + } + int iv_length = EVP_CIPHER_iv_length(cipher_type); + php_openssl_release_evp_cipher(cipher_type); - return cipher_type == NULL ? -1 : EVP_CIPHER_iv_length(cipher_type); + return iv_length; } PHP_OPENSSL_API zend_long php_openssl_cipher_key_length(const char *method) { - const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name(method); + const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name_with_warning(method); + if (cipher_type == NULL) { + return -1; + } + int key_length = EVP_CIPHER_key_length(cipher_type); + php_openssl_release_evp_cipher(cipher_type); - return cipher_type == NULL ? -1 : EVP_CIPHER_key_length(cipher_type); + return key_length; } PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long buffer_length) diff --git a/ext/openssl/openssl_backend_v1.c b/ext/openssl/openssl_backend_v1.c index eb94ee3fbe4bc..8b9ede38437d6 100644 --- a/ext/openssl/openssl_backend_v1.c +++ b/ext/openssl/openssl_backend_v1.c @@ -561,6 +561,114 @@ zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pu return data; } +const EVP_MD *php_openssl_get_evp_md_by_name(const char *name) +{ + return EVP_get_digestbyname(name); +} + +const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo) +{ + EVP_MD *mdtype; + + switch (algo) { + case OPENSSL_ALGO_SHA1: + mdtype = (EVP_MD *) EVP_sha1(); + break; + case OPENSSL_ALGO_MD5: + mdtype = (EVP_MD *) EVP_md5(); + break; +#ifndef OPENSSL_NO_MD4 + case OPENSSL_ALGO_MD4: + mdtype = (EVP_MD *) EVP_md4(); + break; +#endif +#ifndef OPENSSL_NO_MD2 + case OPENSSL_ALGO_MD2: + mdtype = (EVP_MD *) EVP_md2(); + break; +#endif + case OPENSSL_ALGO_SHA224: + mdtype = (EVP_MD *) EVP_sha224(); + break; + case OPENSSL_ALGO_SHA256: + mdtype = (EVP_MD *) EVP_sha256(); + break; + case OPENSSL_ALGO_SHA384: + mdtype = (EVP_MD *) EVP_sha384(); + break; + case OPENSSL_ALGO_SHA512: + mdtype = (EVP_MD *) EVP_sha512(); + break; +#ifndef OPENSSL_NO_RMD160 + case OPENSSL_ALGO_RMD160: + mdtype = (EVP_MD *) EVP_ripemd160(); + break; +#endif + default: + return NULL; + break; + } + return mdtype; +} + +void php_openssl_release_evp_md(const EVP_MD *md) +{ + // Do nothing as MD is static +} + +const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *name) +{ + return EVP_get_cipherbyname(name); +} + +const EVP_CIPHER *php_openssl_get_evp_cipher_from_algo(zend_long algo) +{ + switch (algo) { +#ifndef OPENSSL_NO_RC2 + case PHP_OPENSSL_CIPHER_RC2_40: + return EVP_rc2_40_cbc(); + break; + case PHP_OPENSSL_CIPHER_RC2_64: + return EVP_rc2_64_cbc(); + break; + case PHP_OPENSSL_CIPHER_RC2_128: + return EVP_rc2_cbc(); + break; +#endif + +#ifndef OPENSSL_NO_DES + case PHP_OPENSSL_CIPHER_DES: + return EVP_des_cbc(); + break; + case PHP_OPENSSL_CIPHER_3DES: + return EVP_des_ede3_cbc(); + break; +#endif + +#ifndef OPENSSL_NO_AES + case PHP_OPENSSL_CIPHER_AES_128_CBC: + return EVP_aes_128_cbc(); + break; + case PHP_OPENSSL_CIPHER_AES_192_CBC: + return EVP_aes_192_cbc(); + break; + case PHP_OPENSSL_CIPHER_AES_256_CBC: + return EVP_aes_256_cbc(); + break; +#endif + + + default: + return NULL; + break; + } +} + +void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher) +{ + // Do nothing as the cipher is static +} + void php_openssl_get_cipher_methods(zval *return_value, bool aliases) { array_init(return_value); diff --git a/ext/openssl/openssl_backend_v3.c b/ext/openssl/openssl_backend_v3.c index 0876690aee0ba..e3c80cf659ba1 100644 --- a/ext/openssl/openssl_backend_v3.c +++ b/ext/openssl/openssl_backend_v3.c @@ -696,6 +696,89 @@ zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pu return result; } +const EVP_MD *php_openssl_get_evp_md_by_name(const char *name) +{ + return EVP_MD_fetch(OPENSSL_G(libctx), name, OPENSSL_G(propq)); +} + +static const char *php_openssl_digest_names[] = { + [OPENSSL_ALGO_SHA1] = "SHA1", + [OPENSSL_ALGO_MD5] = "MD5", +#ifndef OPENSSL_NO_MD4 + [OPENSSL_ALGO_MD4] = "MD4", +#endif +#ifndef OPENSSL_NO_MD2 + [OPENSSL_ALGO_MD2] = "MD2", +#endif + [OPENSSL_ALGO_SHA224] = "SHA224", + [OPENSSL_ALGO_SHA256] = "SHA256", + [OPENSSL_ALGO_SHA384] = "SHA384", + [OPENSSL_ALGO_SHA512] = "SHA512", +#ifndef OPENSSL_NO_RMD160 + [OPENSSL_ALGO_RMD160] = "RIPEMD160", +#endif +}; + +const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo) +{ + if (algo < 0 || algo >= (zend_long)(sizeof(php_openssl_digest_names) / sizeof(*php_openssl_digest_names))) { + return NULL; + } + + const char *name = php_openssl_digest_names[algo]; + if (!name) { + return NULL; + } + + return php_openssl_get_evp_md_by_name(name); +} + +void php_openssl_release_evp_md(const EVP_MD *md) +{ + if (md != NULL) { + // It is fine to remove const as the md is from EVP_MD_fetch + EVP_MD_free((EVP_MD *) md); + } +} + +static const char *php_openssl_cipher_names[] = { + [PHP_OPENSSL_CIPHER_RC2_40] = "RC2-40-CBC", + [PHP_OPENSSL_CIPHER_RC2_128] = "RC2-CBC", + [PHP_OPENSSL_CIPHER_RC2_64] = "RC2-64-CBC", + [PHP_OPENSSL_CIPHER_DES] = "DES-CBC", + [PHP_OPENSSL_CIPHER_3DES] = "DES-EDE3-CBC", + [PHP_OPENSSL_CIPHER_AES_128_CBC]= "AES-128-CBC", + [PHP_OPENSSL_CIPHER_AES_192_CBC]= "AES-192-CBC", + [PHP_OPENSSL_CIPHER_AES_256_CBC]= "AES-256-CBC", +}; + +const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *name) +{ + return EVP_CIPHER_fetch(OPENSSL_G(libctx), name, OPENSSL_G(propq)); +} + +const EVP_CIPHER *php_openssl_get_evp_cipher_from_algo(zend_long algo) +{ + if (algo < 0 || algo >= (zend_long)(sizeof(php_openssl_cipher_names) / sizeof(*php_openssl_cipher_names))) { + return NULL; + } + + const char *name = php_openssl_cipher_names[algo]; + if (!name) { + return NULL; + } + + return php_openssl_get_evp_cipher_by_name(name); +} + +void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher) +{ + if (cipher != NULL) { + // It is fine to remove const as the cipher is from EVP_CIPHER_fetch + EVP_CIPHER_free((EVP_CIPHER *) cipher); + } +} + static void php_openssl_add_cipher_name(const char *name, void *arg) { size_t len = strlen(name); @@ -722,7 +805,7 @@ static int php_openssl_compare_func(Bucket *a, Bucket *b) void php_openssl_get_cipher_methods(zval *return_value, bool aliases) { array_init(return_value); - EVP_CIPHER_do_all_provided(NULL, + EVP_CIPHER_do_all_provided(OPENSSL_G(libctx), aliases ? php_openssl_add_cipher_or_alias : php_openssl_add_cipher, return_value); zend_hash_sort(Z_ARRVAL_P(return_value), php_openssl_compare_func, 1); diff --git a/ext/openssl/php_openssl_backend.h b/ext/openssl/php_openssl_backend.h index 5cfa73160b853..69317e3c7833b 100644 --- a/ext/openssl/php_openssl_backend.h +++ b/ext/openssl/php_openssl_backend.h @@ -75,14 +75,17 @@ enum php_openssl_key_type { OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, + OPENSSL_KEYTYPE_EC, + OPENSSL_KEYTYPE_X25519, + OPENSSL_KEYTYPE_ED25519, + OPENSSL_KEYTYPE_X448, + OPENSSL_KEYTYPE_ED448, + OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA, - OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1, - OPENSSL_KEYTYPE_X25519 = OPENSSL_KEYTYPE_DH +2, - OPENSSL_KEYTYPE_ED25519 = OPENSSL_KEYTYPE_DH +3, - OPENSSL_KEYTYPE_X448 = OPENSSL_KEYTYPE_DH +4, - OPENSSL_KEYTYPE_ED448 = OPENSSL_KEYTYPE_DH +5, }; +/* Cipher constants, do not forget to update php_openssl_cipher_names in + * openssl_backend_v3.c if new constant added. */ enum php_openssl_cipher_type { PHP_OPENSSL_CIPHER_RC2_40, PHP_OPENSSL_CIPHER_RC2_128, @@ -106,10 +109,10 @@ enum php_openssl_encoding { ENCODING_PEM, }; - #define MIN_KEY_LENGTH 384 -/* constants used in ext/phar/util.c, keep in sync */ +/* Constants used in ext/phar/util.c, keep in sync and do not forget to update + * php_openssl_digest_names in openssl_backend_v3.c if new constant added. */ #define OPENSSL_ALGO_SHA1 1 #define OPENSSL_ALGO_MD5 2 #ifndef OPENSSL_NO_MD4 @@ -126,6 +129,7 @@ enum php_openssl_encoding { #ifndef OPENSSL_NO_RMD160 #define OPENSSL_ALGO_RMD160 10 #endif + #define DEBUG_SMIME 0 #if !defined(OPENSSL_NO_EC) && defined(EVP_PKEY_EC) @@ -221,8 +225,12 @@ void php_openssl_dispose_config(struct php_x509_request * req); zend_result php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded); zend_result php_openssl_write_rand_file(const char * file, int egdsocket, int seeded); -EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo); +const EVP_MD *php_openssl_get_evp_md_by_name(const char *name); +const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo); +void php_openssl_release_evp_md(const EVP_MD *md); +const EVP_CIPHER * php_openssl_get_evp_cipher_by_name(const char *name); const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo); +void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher); void php_openssl_backend_init(void); void php_openssl_backend_init_common(void); diff --git a/ext/openssl/tests/bug74796.phpt b/ext/openssl/tests/bug74796.phpt new file mode 100644 index 0000000000000..b3f594d5e60f4 --- /dev/null +++ b/ext/openssl/tests/bug74796.phpt @@ -0,0 +1,178 @@ +--TEST-- +Bug #74796: TLS encryption fails behind HTTP proxy +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'SNI_server_certs' => [ + "cs.php.net" => __DIR__ . "/sni_server_cs.pem", + "uk.php.net" => __DIR__ . "/sni_server_uk.pem", + "us.php.net" => __DIR__ . "/sni_server_us.pem" + ] + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $serverFlags, $ctx); + phpt_notify_server_start($server); + + for ($i=0; $i < 3; $i++) { + $conn = stream_socket_accept($server, 3); + fwrite($conn, "HTTP/1.0 200 OK\r\n\r\nHello from server $i"); + fclose($conn); + } + + phpt_wait(); +CODE; + +$proxyCode = <<<'CODE' + function parse_sni_from_client_hello($data) { + $sni = null; + + if (strlen($data) < 5 || ord($data[0]) != 0x16) return null; + + $session_id_len = ord($data[43]); + $ptr = 44 + $session_id_len; + + // Cipher suites length + $cipher_suites_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2 + $cipher_suites_len; + + // Compression methods length + $compression_methods_len = ord($data[$ptr]); + $ptr += 1 + $compression_methods_len; + + // Extensions length + if ($ptr + 2 > strlen($data)) return null; + $extensions_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2; + + $extensions_end = $ptr + $extensions_len; + + while ($ptr + 4 <= $extensions_end) { + $ext_type = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ext_len = (ord($data[$ptr+2]) << 8) | ord($data[$ptr+3]); + $ptr += 4; + + if ($ext_type === 0x00) { // SNI extension + if ($ptr + 2 > strlen($data)) break; + $name_list_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2; + + if ($ptr + 3 > strlen($data)) break; + $name_type = ord($data[$ptr]); + $name_len = (ord($data[$ptr+1]) << 8) | ord($data[$ptr+2]); + $ptr += 3; + + if ($name_type === 0) { // host_name type + $sni = substr($data, $ptr, $name_len); + break; + } + } + + $ptr += $ext_len; + } + + return $sni; + } + + $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; + $server = stream_socket_server("tcp://127.0.0.1:0", $errornum, $errorstr, $flags); + phpt_notify_server_start($server); + + for ($i=0; $i < 3; $i++) { + $upstream = stream_socket_client("tcp://{{ ADDR }}", $errornum, $errorstr, 30, STREAM_CLIENT_CONNECT); + stream_set_blocking($upstream, false); + + $conn = stream_socket_accept($server); + stream_set_blocking($conn, true); + + // reading CONNECT request headers + while (($line = fgets($conn)) !== false) { + if (rtrim($line) === '') break; // empty line means end of headers + } + + // successful CONNECT response + fwrite($conn, "HTTP/1.0 200 Connection established\r\n\r\n"); + + // tunnel data + stream_set_blocking($conn, false); + $firstRead = true; + while (!feof($conn) && !feof($upstream)) { + $clientData = fread($conn, 8192); + if ($clientData !== false && $clientData !== '') { + if ($firstRead) { + $sni = parse_sni_from_client_hello($clientData); + if ($sni !== null) { + file_put_contents(__DIR__ . "/bug74796_proxy_sni.log", $sni . "\n", FILE_APPEND); + } + $firstRead = false; + } + fwrite($upstream, $clientData); + } + + $serverData = fread($upstream, 8192); + if ($serverData !== false && $serverData !== '') { + fwrite($conn, $serverData); + } + } + fclose($conn); + fclose($upstream); + phpt_wait(); + } +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create([ + 'ssl' => [ + 'cafile' => __DIR__ . '/sni_server_ca.pem', + 'verify_peer' => true, + 'verify_peer_name' => true, + ], + "http" => [ + "proxy" => "tcp://{{ ADDR }}" + ], + ]); + + // servers + $hosts = ["cs.php.net", "uk.php.net", "us.php.net"]; + foreach ($hosts as $host) { + var_dump(file_get_contents("https://$host/", false, $clientCtx)); + var_dump(stream_context_get_options($clientCtx)['ssl']['peer_name'] ?? null); + phpt_notify('proxy'); + } + + echo file_get_contents(__DIR__ . "/bug74796_proxy_sni.log"); + + phpt_notify('server'); +CODE; + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, [ + 'server' => $serverCode, + 'proxy' => $proxyCode, +]); +?> +--CLEAN-- + +--EXPECT-- +string(19) "Hello from server 0" +NULL +string(19) "Hello from server 1" +NULL +string(19) "Hello from server 2" +NULL +cs.php.net +uk.php.net +us.php.net diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index e6cba81d98856..0481c0966c87a 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -881,8 +881,7 @@ static bool php_pcntl_set_user_signal_infos( zval *user_signal_no; ZEND_HASH_FOREACH_VAL(user_signals, user_signal_no) { - bool failed = true; - ZVAL_DEREF(user_signal_no); + bool failed; zend_long tmp = zval_try_get_long(user_signal_no, &failed); if (failed) { diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index afd6b2a43da6a..697940d94260d 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2255,12 +2255,13 @@ static zval *row_dim_read(zend_object *object, zval *offset, int type, zval *rv) } return rv; } else { - zend_string *member = zval_try_get_string(offset); + zend_string *tmp_member; + zend_string *member = zval_try_get_tmp_string(offset, &tmp_member); if (!member) { return NULL; } zval *result = row_prop_read(object, member, type, NULL, rv); - zend_string_release_ex(member, false); + zend_tmp_string_release(tmp_member); return result; } } @@ -2330,12 +2331,13 @@ static int row_dim_exists(zend_object *object, zval *offset, int check_empty) zval_ptr_dtor_nogc(retval); return res; } else { - zend_string *member = zval_try_get_string(offset); + zend_string *tmp_member; + zend_string *member = zval_try_get_tmp_string(offset, &tmp_member); if (!member) { return 0; } int result = row_prop_exists(object, member, check_empty, NULL); - zend_string_release_ex(member, false); + zend_tmp_string_release(tmp_member); return result; } } diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index 49efa0b238484..af9dcd0ec4102 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -179,11 +179,6 @@ PHP_METHOD(Pdo_Pgsql, setNoticeCallback) PHP_MINIT_FUNCTION(pdo_pgsql) { REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_PREPARES", PDO_PGSQL_ATTR_DISABLE_PREPARES); - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_IDLE", (zend_long)PGSQL_TRANSACTION_IDLE); - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_ACTIVE", (zend_long)PGSQL_TRANSACTION_ACTIVE); - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INTRANS", (zend_long)PGSQL_TRANSACTION_INTRANS); - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INERROR", (zend_long)PGSQL_TRANSACTION_INERROR); - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_UNKNOWN", (zend_long)PGSQL_TRANSACTION_UNKNOWN); PdoPgsql_ce = register_class_Pdo_Pgsql(pdo_dbh_ce); PdoPgsql_ce->create_object = pdo_dbh_new; diff --git a/ext/pdo_pgsql/pdo_pgsql.stub.php b/ext/pdo_pgsql/pdo_pgsql.stub.php index 8e96a55266824..93415369c625d 100644 --- a/ext/pdo_pgsql/pdo_pgsql.stub.php +++ b/ext/pdo_pgsql/pdo_pgsql.stub.php @@ -18,21 +18,6 @@ class Pgsql extends \PDO public const int ATTR_RESULT_MEMORY_SIZE = UNKNOWN; #endif - /** @cvalue PGSQL_TRANSACTION_IDLE */ - public const int TRANSACTION_IDLE = UNKNOWN; - - /** @cvalue PGSQL_TRANSACTION_ACTIVE */ - public const int TRANSACTION_ACTIVE = UNKNOWN; - - /** @cvalue PGSQL_TRANSACTION_INTRANS */ - public const int TRANSACTION_INTRANS = UNKNOWN; - - /** @cvalue PGSQL_TRANSACTION_INERROR */ - public const int TRANSACTION_INERROR = UNKNOWN; - - /** @cvalue PGSQL_TRANSACTION_UNKNOWN */ - public const int TRANSACTION_UNKNOWN = UNKNOWN; - public function escapeIdentifier(string $input): string {} public function copyFromArray(string $tableName, array $rows, string $separator = "\t", string $nullAs = "\\\\N", ?string $fields = null): bool {} diff --git a/ext/pdo_pgsql/pdo_pgsql_arginfo.h b/ext/pdo_pgsql/pdo_pgsql_arginfo.h index f7f54cb600c72..8efdfe53a0d16 100644 --- a/ext/pdo_pgsql/pdo_pgsql_arginfo.h +++ b/ext/pdo_pgsql/pdo_pgsql_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 225cbb077d441f93b7c6bdb9826ab3e8f634b79d */ + * Stub hash: 81399a3d342a9327733f86f6ab733bb317a4599e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Pgsql_escapeIdentifier, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, input, IS_STRING, 0) @@ -102,35 +102,5 @@ static zend_class_entry *register_class_Pdo_Pgsql(zend_class_entry *class_entry_ zend_string_release(const_ATTR_RESULT_MEMORY_SIZE_name); #endif - zval const_TRANSACTION_IDLE_value; - ZVAL_LONG(&const_TRANSACTION_IDLE_value, PGSQL_TRANSACTION_IDLE); - zend_string *const_TRANSACTION_IDLE_name = zend_string_init_interned("TRANSACTION_IDLE", sizeof("TRANSACTION_IDLE") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_TRANSACTION_IDLE_name, &const_TRANSACTION_IDLE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(const_TRANSACTION_IDLE_name); - - zval const_TRANSACTION_ACTIVE_value; - ZVAL_LONG(&const_TRANSACTION_ACTIVE_value, PGSQL_TRANSACTION_ACTIVE); - zend_string *const_TRANSACTION_ACTIVE_name = zend_string_init_interned("TRANSACTION_ACTIVE", sizeof("TRANSACTION_ACTIVE") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_TRANSACTION_ACTIVE_name, &const_TRANSACTION_ACTIVE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(const_TRANSACTION_ACTIVE_name); - - zval const_TRANSACTION_INTRANS_value; - ZVAL_LONG(&const_TRANSACTION_INTRANS_value, PGSQL_TRANSACTION_INTRANS); - zend_string *const_TRANSACTION_INTRANS_name = zend_string_init_interned("TRANSACTION_INTRANS", sizeof("TRANSACTION_INTRANS") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INTRANS_name, &const_TRANSACTION_INTRANS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(const_TRANSACTION_INTRANS_name); - - zval const_TRANSACTION_INERROR_value; - ZVAL_LONG(&const_TRANSACTION_INERROR_value, PGSQL_TRANSACTION_INERROR); - zend_string *const_TRANSACTION_INERROR_name = zend_string_init_interned("TRANSACTION_INERROR", sizeof("TRANSACTION_INERROR") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INERROR_name, &const_TRANSACTION_INERROR_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(const_TRANSACTION_INERROR_name); - - zval const_TRANSACTION_UNKNOWN_value; - ZVAL_LONG(&const_TRANSACTION_UNKNOWN_value, PGSQL_TRANSACTION_UNKNOWN); - zend_string *const_TRANSACTION_UNKNOWN_name = zend_string_init_interned("TRANSACTION_UNKNOWN", sizeof("TRANSACTION_UNKNOWN") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_TRANSACTION_UNKNOWN_name, &const_TRANSACTION_UNKNOWN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(const_TRANSACTION_UNKNOWN_name); - return class_entry; } diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index 881b4e7046504..cb07a10de16f0 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -104,14 +104,6 @@ struct pdo_pgsql_lob_self { Oid oid; }; -enum pdo_pgsql_specific_constants { - PGSQL_TRANSACTION_IDLE = PQTRANS_IDLE, - PGSQL_TRANSACTION_ACTIVE = PQTRANS_ACTIVE, - PGSQL_TRANSACTION_INTRANS = PQTRANS_INTRANS, - PGSQL_TRANSACTION_INERROR = PQTRANS_INERROR, - PGSQL_TRANSACTION_UNKNOWN = PQTRANS_UNKNOWN -}; - php_stream *pdo_pgsql_create_lob_stream(zend_object *pdh, int lfd, Oid oid); extern const php_stream_ops pdo_pgsql_lob_stream_ops; diff --git a/ext/reflection/tests/ReflectionConstant_getAttributes_internal.phpt b/ext/reflection/tests/ReflectionConstant_getAttributes_internal.phpt new file mode 100644 index 0000000000000..ff96fa7cadb58 --- /dev/null +++ b/ext/reflection/tests/ReflectionConstant_getAttributes_internal.phpt @@ -0,0 +1,17 @@ +--TEST-- +ReflectionConstant::getAttributes() with attribute (internal constant) +--FILE-- +getAttributes()); + +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(10) "Deprecated" + } +} diff --git a/ext/reflection/tests/internal_parameter_default_value/ReflectionParameter_getDefaultValueConstantName_Internal.phpt b/ext/reflection/tests/internal_parameter_default_value/ReflectionParameter_getDefaultValueConstantName_Internal.phpt index b013c9db66725..b6473b0a3abb2 100644 --- a/ext/reflection/tests/internal_parameter_default_value/ReflectionParameter_getDefaultValueConstantName_Internal.phpt +++ b/ext/reflection/tests/internal_parameter_default_value/ReflectionParameter_getDefaultValueConstantName_Internal.phpt @@ -47,7 +47,7 @@ NULL NULL ---------- string(11) "PHP_INT_MIN" -string(11) "PHP_INT_MAX" +NULL ---------- string(17) "DateTimeZone::ALL" NULL diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 094730b88f412..e0577ac648cae 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -4404,6 +4404,22 @@ static void type_to_string(sdlTypePtr type, smart_str *buf, int level) /* {{{ */ smart_str_appendl(buf, "anyType ", sizeof("anyType ")-1); } smart_str_appendl(buf, type->name, strlen(type->name)); + + if (type->restrictions && type->restrictions->enumeration) { + zend_string *key; + bool first = true; + + smart_str_appends(buf, " {"); + ZEND_HASH_MAP_FOREACH_STR_KEY(type->restrictions->enumeration, key) { + if (first) { + first = false; + } else { + smart_str_appends(buf, ", "); + } + smart_str_append(buf, key); + } ZEND_HASH_FOREACH_END(); + smart_str_appendc(buf, '}'); + } break; case XSD_TYPEKIND_LIST: smart_str_appendl(buf, "list ", 5); diff --git a/ext/soap/tests/bugs/bug42359.phpt b/ext/soap/tests/bugs/bug42359.phpt index 0cb4f75c69be4..f717561e7caeb 100644 --- a/ext/soap/tests/bugs/bug42359.phpt +++ b/ext/soap/tests/bugs/bug42359.phpt @@ -13,7 +13,7 @@ print_r($soap->__getTypes()); Array ( [0] => list listItem {anonymous1} - [1] => string anonymous1 - [2] => string enumItem + [1] => string anonymous1 {test1, test2} + [2] => string enumItem {test1, test2} [3] => list listItem2 {enumItem} ) diff --git a/ext/soap/tests/req55503.phpt b/ext/soap/tests/req55503.phpt new file mode 100644 index 0000000000000..ac3c8627048c1 --- /dev/null +++ b/ext/soap/tests/req55503.phpt @@ -0,0 +1,16 @@ +--TEST-- +Request #55503 (Extend __getTypes to support enumerations) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--FILE-- +__getTypes()); +?> +--EXPECT-- +array(1) { + [0]=> + string(102) "anyType PersonaMemberType {NEW, LIMITED, FREE, PAID_ACTIVE, TRIAL_ACTIVE, PAID_EXPIRED, TRIAL_EXPIRED}" +} diff --git a/ext/soap/tests/req55503.wsdl b/ext/soap/tests/req55503.wsdl new file mode 100644 index 0000000000000..91ac62c8bc001 --- /dev/null +++ b/ext/soap/tests/req55503.wsdl @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index ea06e9dff61ea..9fefe153622fc 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -498,12 +498,14 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (stream && use_proxy && use_ssl) { smart_str header = {0}; + bool reset_ssl_peer_name = false; /* Set peer_name or name verification will try to use the proxy server name */ if (!context || (tmpzval = php_stream_context_get_option(context, "ssl", "peer_name")) == NULL) { ZVAL_STR_COPY(&ssl_proxy_peer_name, resource->host); php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name", &ssl_proxy_peer_name); zval_ptr_dtor(&ssl_proxy_peer_name); + reset_ssl_peer_name = true; } smart_str_appendl(&header, "CONNECT ", sizeof("CONNECT ")-1); @@ -566,6 +568,10 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, stream = NULL; } } + + if (reset_ssl_peer_name) { + php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name"); + } } php_stream_http_response_header_info_init(&header_info); diff --git a/ext/tidy/config.m4 b/ext/tidy/config.m4 index 8c3ceb38dafd7..bb85236346593 100644 --- a/ext/tidy/config.m4 +++ b/ext/tidy/config.m4 @@ -57,6 +57,12 @@ if test "$PHP_TIDY" != "no"; then [], [-L$TIDY_LIBDIR]) + PHP_CHECK_LIBRARY([$TIDY_LIB_NAME], [tidyOptGetCategory], + [AC_DEFINE([HAVE_TIDYOPTGETCATEGORY], [1], + [Define to 1 if Tidy library has the 'tidyOptGetCategory' function.])], + [], + [-L$TIDY_LIBDIR]) + PHP_ADD_LIBRARY_WITH_PATH([$TIDY_LIB_NAME], [$TIDY_LIBDIR], [TIDY_SHARED_LIBADD]) diff --git a/ext/tidy/tests/tidy_error1.phpt b/ext/tidy/tests/tidy_error1.phpt index e9ced390d7ea7..0660f37437e2b 100644 --- a/ext/tidy/tests/tidy_error1.phpt +++ b/ext/tidy/tests/tidy_error1.phpt @@ -7,12 +7,42 @@ tidy --FILE-- '; -$config = array('bogus' => 'willnotwork'); +$config = ['bogus' => 'willnotwork']; $tidy = new tidy(); -var_dump($tidy->parseString($buffer, $config)); + +try { + $tidy->parseString($buffer, $config); +} catch (\ValueError $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +$config = ['neither']; +try { + $tidy->parseString($buffer, $config); +} catch (\TypeError $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +$config = ['doctype-mode' => 'customtag']; + +try { + var_dump($tidy->parseString($buffer, $config)); +} catch (\ValueError $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +$config = ['doctype' => 'php', 0 => 'value2']; + +try { + var_dump($tidy->parseString($buffer, $config)); +} catch (\TypeError $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Warning: tidy::parseString(): Unknown Tidy configuration option "bogus" in %s on line %d -bool(true) +--EXPECT-- +ValueError: tidy::parseString(): Argument #2 ($config) Unknown Tidy configuration option "bogus" +TypeError: tidy::parseString(): Argument #2 ($config) must be of type array with keys as string +ValueError: tidy::parseString(): Argument #2 ($config) Attempting to set read-only option "doctype-mode" +TypeError: tidy::parseString(): Argument #2 ($config) must be of type array with keys as string diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index eec3fb62d60a3..18fcc1bfdce68 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -50,9 +50,6 @@ /* {{{ ext/tidy macros */ #define FIX_BUFFER(bptr) do { if ((bptr)->size) { (bptr)->bp[(bptr)->size-1] = '\0'; } } while(0) -#define TIDY_SET_CONTEXT \ - zval *object = getThis(); - #define TIDY_FETCH_OBJECT \ PHPTidyObj *obj; \ zval *object; \ @@ -65,16 +62,15 @@ TIDY_FETCH_OBJECT; \ if (!obj->ptdoc->initialized) { \ zend_throw_error(NULL, "tidy object is not initialized"); \ - return; \ + RETURN_THROWS(); \ } #define TIDY_FETCH_ONLY_OBJECT \ PHPTidyObj *obj; \ - TIDY_SET_CONTEXT; \ if (zend_parse_parameters_none() != SUCCESS) { \ RETURN_THROWS(); \ } \ - obj = Z_TIDY_P(object); \ + obj = Z_TIDY_P(ZEND_THIS); \ #define TIDY_SET_DEFAULT_CONFIG(_doc) \ if (TG(default_config) && TG(default_config)[0]) { \ @@ -99,10 +95,10 @@ typedef enum { } tidy_base_nodetypes; struct _PHPTidyDoc { - TidyDoc doc; - TidyBuffer *errbuf; - unsigned int ref_count; - unsigned int initialized:1; + TidyDoc doc; + TidyBuffer *errbuf; + uint32_t ref_count; + bool initialized; }; struct _PHPTidyObj { @@ -128,10 +124,10 @@ static zend_result tidy_doc_cast_handler(zend_object *, zval *, int); static zend_result tidy_node_cast_handler(zend_object *, zval *, int); static void tidy_doc_update_properties(PHPTidyObj *); static void tidy_add_node_default_properties(PHPTidyObj *); -static void *php_tidy_get_opt_val(PHPTidyDoc *, TidyOption, TidyOptionType *); +static void *php_tidy_get_opt_val(const PHPTidyDoc *, TidyOption, TidyOptionType *); static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetypes); -static int _php_tidy_set_tidy_opt(TidyDoc, const char *, zval *); -static int _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options); +static zend_result _php_tidy_set_tidy_opt(TidyDoc, const char *, zval *, uint32_t arg); +static zend_result _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options, uint32_t arg); static PHP_INI_MH(php_tidy_set_clean_output); static void php_tidy_clean_output_start(const char *name, size_t name_len); static php_output_handler *php_tidy_output_handler_init(const char *handler_name, size_t handler_name_len, size_t chunk_size, int flags); @@ -209,10 +205,10 @@ static void php_tidy_load_config(TidyDoc doc, const char *path) } } -static zend_result php_tidy_apply_config(TidyDoc doc, const zend_string *str_string, const HashTable *ht_options) +static zend_result php_tidy_apply_config(TidyDoc doc, const zend_string *str_string, const HashTable *ht_options, uint32_t arg) { if (ht_options) { - return _php_tidy_apply_config_array(doc, ht_options); + return _php_tidy_apply_config_array(doc, ht_options, arg); } else if (str_string) { if (php_check_open_basedir(ZSTR_VAL(str_string))) { return FAILURE; @@ -222,19 +218,23 @@ static zend_result php_tidy_apply_config(TidyDoc doc, const zend_string *str_str return SUCCESS; } -static int _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value) +static zend_result _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value, uint32_t arg) { TidyOption opt = tidyGetOptionByName(doc, optname); zend_string *str, *tmp_str; zend_long lval; if (!opt) { - php_error_docref(NULL, E_WARNING, "Unknown Tidy configuration option \"%s\"", optname); + zend_argument_value_error(arg, "Unknown Tidy configuration option \"%s\"", optname); return FAILURE; } +#if defined(HAVE_TIDYOPTGETCATEGORY) + if (tidyOptGetCategory(opt) == TidyInternalCategory) { +#else if (tidyOptIsReadOnly(opt)) { - php_error_docref(NULL, E_WARNING, "Attempting to set read-only option \"%s\"", optname); +#endif + zend_argument_value_error(arg, "Attempting to set read-only option \"%s\"", optname); return FAILURE; } @@ -341,7 +341,7 @@ static void php_tidy_quick_repair(INTERNAL_FUNCTION_PARAMETERS, bool is_file) TIDY_SET_DEFAULT_CONFIG(doc); - if (php_tidy_apply_config(doc, config_str, config_ht) != SUCCESS) { + if (php_tidy_apply_config(doc, config_str, config_ht, 2) != SUCCESS) { RETVAL_FALSE; } else if (enc_len) { if (tidySetCharEncoding(doc, enc) < 0) { @@ -408,7 +408,7 @@ static void tidy_object_free_storage(zend_object *object) if (intern->ptdoc) { intern->ptdoc->ref_count--; - if (intern->ptdoc->ref_count <= 0) { + if (intern->ptdoc->ref_count == 0) { tidyBufFree(intern->ptdoc->errbuf); efree(intern->ptdoc->errbuf); tidyRelease(intern->ptdoc->doc); @@ -417,7 +417,7 @@ static void tidy_object_free_storage(zend_object *object) } } -static zend_object *tidy_object_new(zend_class_entry *class_type, zend_object_handlers *handlers, tidy_obj_type objtype) +static zend_object *tidy_object_new(zend_class_entry *class_type, const zend_object_handlers *handlers, tidy_obj_type objtype) { PHPTidyObj *intern; @@ -433,7 +433,7 @@ static zend_object *tidy_object_new(zend_class_entry *class_type, zend_object_ha intern->ptdoc = emalloc(sizeof(PHPTidyDoc)); intern->ptdoc->doc = tidyCreate(); intern->ptdoc->ref_count = 1; - intern->ptdoc->initialized = 0; + intern->ptdoc->initialized = false; intern->ptdoc->errbuf = emalloc(sizeof(TidyBuffer)); tidyBufInit(intern->ptdoc->errbuf); @@ -666,17 +666,16 @@ static void tidy_add_node_default_properties(PHPTidyObj *obj) tempattr = tidyAttrFirst(obj->node); if (tempattr) { - const char *name, *val; array_init(&attribute); do { - name = (const char *)tidyAttrName(tempattr); - val = (const char *)tidyAttrValue(tempattr); - if (name) { + const char *attr_name = tidyAttrName(tempattr); + if (attr_name) { + const char *val = tidyAttrValue(tempattr); if (val) { - add_assoc_string(&attribute, name, val); + add_assoc_string(&attribute, attr_name, val); } else { - add_assoc_str(&attribute, name, zend_empty_string); + add_assoc_str(&attribute, attr_name, zend_empty_string); } } } while((tempattr = tidyAttrNext(tempattr))); @@ -719,13 +718,13 @@ static void tidy_add_node_default_properties(PHPTidyObj *obj) zval_ptr_dtor(&children); } -static void *php_tidy_get_opt_val(PHPTidyDoc *ptdoc, TidyOption opt, TidyOptionType *type) +static void *php_tidy_get_opt_val(const PHPTidyDoc *ptdoc, TidyOption opt, TidyOptionType *type) { *type = tidyOptGetType(opt); switch (*type) { case TidyString: { - char *val = (char *) tidyOptGetValue(ptdoc->doc, tidyOptGetId(opt)); + const char *val = tidyOptGetValue(ptdoc->doc, tidyOptGetId(opt)); if (val) { return (void *) zend_string_init(val, strlen(val), 0); } else { @@ -779,7 +778,7 @@ static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetyp tidy_create_node_object(return_value, obj->ptdoc, node); } -static int _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options) +static zend_result _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options, uint32_t arg) { zval *opt_val; zend_string *opt_name; @@ -787,15 +786,19 @@ static int _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options if (!HT_IS_PACKED(ht_options)) { ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(ht_options, opt_name, opt_val) { if (opt_name == NULL) { - continue; + zend_argument_type_error(arg, "must be of type array with keys as string"); + return FAILURE; } - _php_tidy_set_tidy_opt(doc, ZSTR_VAL(opt_name), opt_val); + _php_tidy_set_tidy_opt(doc, ZSTR_VAL(opt_name), opt_val, arg); } ZEND_HASH_FOREACH_END(); + return SUCCESS; + } else { + zend_argument_type_error(arg, "must be of type array with keys as string"); + return FAILURE; } - return SUCCESS; } -static int php_tidy_parse_string(PHPTidyObj *obj, const char *string, uint32_t len, const char *enc) +static zend_result php_tidy_parse_string(PHPTidyObj *obj, const char *string, uint32_t len, const char *enc) { TidyBuffer buf; @@ -806,7 +809,7 @@ static int php_tidy_parse_string(PHPTidyObj *obj, const char *string, uint32_t l } } - obj->ptdoc->initialized = 1; + obj->ptdoc->initialized = true; tidyBufInit(&buf); tidyBufAttach(&buf, (byte *) string, len); @@ -1014,7 +1017,7 @@ PHP_FUNCTION(tidy_parse_string) object_init_ex(return_value, tidy_ce_doc); obj = Z_TIDY_P(return_value); - if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) != SUCCESS + if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) != SUCCESS || php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) != SUCCESS) { zval_ptr_dtor(return_value); RETURN_FALSE; @@ -1082,7 +1085,7 @@ PHP_FUNCTION(tidy_parse_file) object_init_ex(return_value, tidy_ce_doc); obj = Z_TIDY_P(return_value); - if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) != SUCCESS + if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) != SUCCESS || php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc) != SUCCESS) { zval_ptr_dtor(return_value); RETVAL_FALSE; @@ -1319,19 +1322,14 @@ PHP_FUNCTION(tidy_getopt) optval = php_tidy_get_opt_val(obj->ptdoc, opt, &optt); switch (optt) { case TidyString: - RETVAL_STR((zend_string*)optval); - return; + RETURN_STR((zend_string*)optval); case TidyInteger: RETURN_LONG((zend_long)optval); break; case TidyBoolean: - if (optval) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(optval); break; default: @@ -1360,8 +1358,7 @@ PHP_METHOD(tidy, __construct) Z_PARAM_BOOL(use_include_path) ZEND_PARSE_PARAMETERS_END(); - TIDY_SET_CONTEXT; - obj = Z_TIDY_P(object); + obj = Z_TIDY_P(ZEND_THIS); if (inputfile) { if (!(contents = php_tidy_file_to_mem(ZSTR_VAL(inputfile), use_include_path))) { @@ -1377,7 +1374,7 @@ PHP_METHOD(tidy, __construct) zend_error_handling error_handling; zend_replace_error_handling(EH_THROW, NULL, &error_handling); - if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) != SUCCESS) { + if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) != SUCCESS) { zend_restore_error_handling(&error_handling); zend_string_release_ex(contents, 0); RETURN_THROWS(); @@ -1407,8 +1404,7 @@ PHP_METHOD(tidy, parseFile) Z_PARAM_BOOL(use_include_path) ZEND_PARSE_PARAMETERS_END(); - TIDY_SET_CONTEXT; - obj = Z_TIDY_P(object); + obj = Z_TIDY_P(ZEND_THIS); if (!(contents = php_tidy_file_to_mem(ZSTR_VAL(inputfile), use_include_path))) { php_error_docref(NULL, E_WARNING, "Cannot load \"%s\" into memory%s", ZSTR_VAL(inputfile), (use_include_path) ? " (using include path)" : ""); @@ -1421,7 +1417,7 @@ PHP_METHOD(tidy, parseFile) RETURN_THROWS(); } - RETVAL_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) == SUCCESS + RETVAL_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) == SUCCESS && php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc) == SUCCESS); zend_string_release_ex(contents, 0); @@ -1447,10 +1443,9 @@ PHP_METHOD(tidy, parseString) RETURN_THROWS(); } - TIDY_SET_CONTEXT; - obj = Z_TIDY_P(object); + obj = Z_TIDY_P(ZEND_THIS); - RETURN_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) == SUCCESS + RETURN_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) == SUCCESS && php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) == SUCCESS); } @@ -1488,11 +1483,7 @@ PHP_METHOD(tidyNode, hasChildren) { TIDY_FETCH_ONLY_OBJECT; - if (tidyGetChild(obj->node)) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyGetChild(obj->node)); } /* }}} */ @@ -1501,11 +1492,7 @@ PHP_METHOD(tidyNode, hasSiblings) { TIDY_FETCH_ONLY_OBJECT; - if (obj->node && tidyGetNext(obj->node)) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(obj->node && tidyGetNext(obj->node)); } /* }}} */ @@ -1514,11 +1501,7 @@ PHP_METHOD(tidyNode, isComment) { TIDY_FETCH_ONLY_OBJECT; - if (tidyNodeGetType(obj->node) == TidyNode_Comment) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Comment); } /* }}} */ @@ -1543,11 +1526,7 @@ PHP_METHOD(tidyNode, isText) { TIDY_FETCH_ONLY_OBJECT; - if (tidyNodeGetType(obj->node) == TidyNode_Text) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Text); } /* }}} */ @@ -1556,11 +1535,7 @@ PHP_METHOD(tidyNode, isJste) { TIDY_FETCH_ONLY_OBJECT; - if (tidyNodeGetType(obj->node) == TidyNode_Jste) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Jste); } /* }}} */ @@ -1569,11 +1544,7 @@ PHP_METHOD(tidyNode, isAsp) { TIDY_FETCH_ONLY_OBJECT; - if (tidyNodeGetType(obj->node) == TidyNode_Asp) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Asp); } /* }}} */ @@ -1582,11 +1553,7 @@ PHP_METHOD(tidyNode, isPhp) { TIDY_FETCH_ONLY_OBJECT; - if (tidyNodeGetType(obj->node) == TidyNode_Php) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Php); } /* }}} */ diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 9923da6dceea0..b1debcae74030 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -17,6 +17,12 @@ /** @var string */ const ZEND_CONSTANT_A = "global"; + /** + * @var int + */ + #[\Deprecated(message: "use something else", since: "version 1.5")] + const ZEND_TEST_ATTRIBUTED_CONSTANT = 42; + interface _ZendTestInterface { /** @var int */ diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index d0d12eb66bab6..c49c032013e38 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1fd4c80ed74efcc50698748b2afc89391ed69c72 */ + * Stub hash: 37ac76dddea2da24d3275cccc748b8fea4c8d09c */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -579,6 +579,7 @@ static void register_test_symbols(int module_number) { REGISTER_LONG_CONSTANT("ZEND_TEST_DEPRECATED", 42, CONST_PERSISTENT | CONST_DEPRECATED); REGISTER_STRING_CONSTANT("ZEND_CONSTANT_A", "global", CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("ZEND_TEST_ATTRIBUTED_CONSTANT", 42, CONST_PERSISTENT | CONST_DEPRECATED); REGISTER_STRING_CONSTANT("ZendTestNS2\\ZEND_CONSTANT_A", "namespaced", CONST_PERSISTENT); REGISTER_STRING_CONSTANT("ZendTestNS2\\ZendSubNS\\ZEND_CONSTANT_A", "namespaced", CONST_PERSISTENT); @@ -635,6 +636,22 @@ static void register_test_symbols(int module_number) ZVAL_STR(&attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0, attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0_str); ZVAL_COPY_VALUE(&attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0->args[0].value, &attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0); attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0->args[0].name = zend_string_init_interned("arg", sizeof("arg") - 1, 1); + +#if (PHP_VERSION_ID >= 80500) + zend_constant *const_ZEND_TEST_ATTRIBUTED_CONSTANT = zend_hash_str_find_ptr(EG(zend_constants), "ZEND_TEST_ATTRIBUTED_CONSTANT", sizeof("ZEND_TEST_ATTRIBUTED_CONSTANT") - 1); + + zend_attribute *attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0 = zend_add_global_constant_attribute(const_ZEND_TEST_ATTRIBUTED_CONSTANT, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0; + zend_string *attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0_str = zend_string_init("use something else", strlen("use something else"), 1); + ZVAL_STR(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0, attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[0].value, &attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0); + attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zval attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1; + zend_string *attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1_str = zend_string_init("version 1.5", strlen("version 1.5"), 1); + ZVAL_STR(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1, attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[1].value, &attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1); + attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[1].name = ZSTR_KNOWN(ZEND_STR_SINCE); +#endif } static zend_class_entry *register_class__ZendTestInterface(void) diff --git a/ext/zend_test/tests/attribute-deprecated.phpt b/ext/zend_test/tests/attribute-deprecated.phpt index a52d0e635c655..e076065cc91d2 100644 --- a/ext/zend_test/tests/attribute-deprecated.phpt +++ b/ext/zend_test/tests/attribute-deprecated.phpt @@ -17,6 +17,12 @@ $reflection = new ReflectionClassConstant('_ZendTestClass', 'ZEND_TEST_DEPRECATE var_dump($reflection->getAttributes()[0]->newInstance()); var_dump($reflection->isDeprecated()); +ZEND_TEST_ATTRIBUTED_CONSTANT; + +$reflection = new ReflectionConstant('ZEND_TEST_ATTRIBUTED_CONSTANT'); +var_dump($reflection->getAttributes()[0]->newInstance()); +var_dump($reflection->isDeprecated()); + ?> --EXPECTF-- Deprecated: Function zend_test_deprecated() is deprecated in %s on line %d @@ -38,3 +44,12 @@ object(Deprecated)#%d (2) { NULL } bool(true) + +Deprecated: Constant ZEND_TEST_ATTRIBUTED_CONSTANT is deprecated since version 1.5, use something else in %s on line %d +object(Deprecated)#%d (2) { + ["message"]=> + string(18) "use something else" + ["since"]=> + string(11) "version 1.5" +} +bool(true) diff --git a/ext/zend_test/tests/gh11423.phpt b/ext/zend_test/tests/gh11423.phpt index fd394126075df..7f70ee33015de 100644 --- a/ext/zend_test/tests/gh11423.phpt +++ b/ext/zend_test/tests/gh11423.phpt @@ -13,11 +13,13 @@ var_dump(get_defined_constants(true)["user"]); ?> --EXPECT-- -array(4) { +array(5) { ["ZEND_TEST_DEPRECATED"]=> int(42) ["ZEND_CONSTANT_A"]=> string(6) "global" + ["ZEND_TEST_ATTRIBUTED_CONSTANT"]=> + int(42) ["ZendTestNS2\ZEND_CONSTANT_A"]=> string(10) "namespaced" ["ZendTestNS2\ZendSubNS\ZEND_CONSTANT_A"]=> diff --git a/main/streams/php_stream_context.h b/main/streams/php_stream_context.h index a5325a94642c9..a09b61923ade2 100644 --- a/main/streams/php_stream_context.h +++ b/main/streams/php_stream_context.h @@ -63,7 +63,8 @@ PHPAPI zval *php_stream_context_get_option(php_stream_context *context, const char *wrappername, const char *optionname); PHPAPI void php_stream_context_set_option(php_stream_context *context, const char *wrappername, const char *optionname, zval *optionvalue); - +void php_stream_context_unset_option(php_stream_context *context, + const char *wrappername, const char *optionname); PHPAPI php_stream_notifier *php_stream_notification_alloc(void); PHPAPI void php_stream_notification_free(php_stream_notifier *notifier); END_EXTERN_C() diff --git a/main/streams/streams.c b/main/streams/streams.c index 661a0da9c883c..e951d455ccd3a 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -2432,6 +2432,20 @@ PHPAPI void php_stream_context_set_option(php_stream_context *context, SEPARATE_ARRAY(wrapperhash); zend_hash_str_update(Z_ARRVAL_P(wrapperhash), optionname, strlen(optionname), optionvalue); } + +void php_stream_context_unset_option(php_stream_context *context, + const char *wrappername, const char *optionname) +{ + zval *wrapperhash; + + wrapperhash = zend_hash_str_find(Z_ARRVAL(context->options), wrappername, strlen(wrappername)); + if (NULL == wrapperhash) { + return; + } + SEPARATE_ARRAY(&context->options); + SEPARATE_ARRAY(wrapperhash); + zend_hash_str_del(Z_ARRVAL_P(wrapperhash), optionname, strlen(optionname)); +} /* }}} */ /* {{{ php_stream_dirent_alphasort */