/src/php-src/ext/standard/crc32.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Copyright © The PHP Group and Contributors. | |
4 | | +----------------------------------------------------------------------+ |
5 | | | This source file is subject to the Modified BSD License that is | |
6 | | | bundled with this package in the file LICENSE, and is available | |
7 | | | through the World Wide Web at <https://www.php.net/license/>. | |
8 | | | | |
9 | | | SPDX-License-Identifier: BSD-3-Clause | |
10 | | +----------------------------------------------------------------------+ |
11 | | | Author: Rasmus Lerdorf <rasmus@php.net> | |
12 | | +----------------------------------------------------------------------+ |
13 | | */ |
14 | | |
15 | | #include "php.h" |
16 | | #include "crc32.h" |
17 | | #include "crc32_x86.h" |
18 | | |
19 | | #ifdef HAVE_AARCH64_CRC32 |
20 | | #ifndef PHP_WIN32 |
21 | | # include <arm_acle.h> |
22 | | #endif |
23 | | # if defined(__linux__) |
24 | | # include <sys/auxv.h> |
25 | | # include <asm/hwcap.h> |
26 | | # elif defined(__APPLE__) |
27 | | # include <sys/sysctl.h> |
28 | | # elif defined(HAVE_ELF_AUX_INFO) |
29 | | # include <sys/auxv.h> |
30 | | |
31 | | static unsigned long getauxval(unsigned long key) { |
32 | | unsigned long ret = 0; |
33 | | if (elf_aux_info(key, &ret, sizeof(ret)) != 0) |
34 | | return 0; |
35 | | return ret; |
36 | | } |
37 | | # endif |
38 | | |
39 | | static inline int has_crc32_insn(void) { |
40 | | /* Only go through the runtime detection once. */ |
41 | | static int res = -1; |
42 | | if (res != -1) |
43 | | return res; |
44 | | # if defined(HWCAP_CRC32) |
45 | | res = getauxval(AT_HWCAP) & HWCAP_CRC32; |
46 | | return res; |
47 | | # elif defined(HWCAP2_CRC32) |
48 | | res = getauxval(AT_HWCAP2) & HWCAP2_CRC32; |
49 | | return res; |
50 | | # elif defined(__APPLE__) |
51 | | size_t reslen = sizeof(res); |
52 | | if (sysctlbyname("hw.optional.armv8_crc32", &res, &reslen, NULL, 0) < 0) |
53 | | res = 0; |
54 | | return res; |
55 | | # elif defined(_WIN32) |
56 | | res = (int)IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE); |
57 | | return res; |
58 | | # else |
59 | | res = 0; |
60 | | return res; |
61 | | # endif |
62 | | } |
63 | | |
64 | | # if defined(__GNUC__) |
65 | | # if!defined(__clang__) |
66 | | # pragma GCC push_options |
67 | | # pragma GCC target ("+nothing+crc") |
68 | | # elif defined(__APPLE__) |
69 | | # pragma clang attribute push(__attribute__((target("crc"))), apply_to=function) |
70 | | # else |
71 | | # pragma clang attribute push(__attribute__((target("+nothing+crc"))), apply_to=function) |
72 | | # endif |
73 | | # endif |
74 | | static uint32_t crc32_aarch64(uint32_t crc, const char *p, size_t nr) { |
75 | | while (nr >= sizeof(uint64_t)) { |
76 | | crc = __crc32d(crc, *(uint64_t *)p); |
77 | | p += sizeof(uint64_t); |
78 | | nr -= sizeof(uint64_t); |
79 | | } |
80 | | if (nr >= sizeof(int32_t)) { |
81 | | crc = __crc32w(crc, *(uint32_t *)p); |
82 | | p += sizeof(uint32_t); |
83 | | nr -= sizeof(uint32_t); |
84 | | } |
85 | | if (nr >= sizeof(int16_t)) { |
86 | | crc = __crc32h(crc, *(uint16_t *)p); |
87 | | p += sizeof(uint16_t); |
88 | | nr -= sizeof(uint16_t); |
89 | | } |
90 | | if (nr) { |
91 | | crc = __crc32b(crc, *p); |
92 | | } |
93 | | return crc; |
94 | | } |
95 | | # if defined(__GNUC__) |
96 | | # if !defined(__clang__) |
97 | | # pragma GCC pop_options |
98 | | # elif defined(__APPLE__) |
99 | | # pragma clang attribute pop |
100 | | # else |
101 | | # pragma clang attribute pop |
102 | | # endif |
103 | | # endif |
104 | | #endif |
105 | | |
106 | | PHPAPI uint32_t php_crc32_bulk_update(uint32_t crc, const char *p, size_t nr) |
107 | 0 | { |
108 | | #ifdef HAVE_AARCH64_CRC32 |
109 | | if (has_crc32_insn()) { |
110 | | crc = crc32_aarch64(crc, p, nr); |
111 | | return crc; |
112 | | } |
113 | | #endif |
114 | |
|
115 | 0 | #if defined(ZEND_INTRIN_SSE4_2_PCLMUL_NATIVE) || defined(ZEND_INTRIN_SSE4_2_PCLMUL_RESOLVER) |
116 | 0 | size_t nr_simd = crc32_x86_simd_update(X86_CRC32B, &crc, (const unsigned char *)p, nr); |
117 | 0 | nr -= nr_simd; |
118 | 0 | p += nr_simd; |
119 | 0 | #endif |
120 | | |
121 | | /* The trailing part */ |
122 | 0 | for (; nr--; ++p) { |
123 | 0 | crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[(crc ^ (*p)) & 0xFF ]; |
124 | 0 | } |
125 | |
|
126 | 0 | return crc; |
127 | 0 | } |
128 | | |
129 | | PHPAPI zend_result php_crc32_stream_bulk_update(uint32_t *crc, php_stream *fp, size_t nr) |
130 | 0 | { |
131 | 0 | size_t handled = 0, n; |
132 | 0 | char buf[1024]; |
133 | |
|
134 | 0 | while (handled < nr) { |
135 | 0 | n = nr - handled; |
136 | 0 | n = (n < sizeof(buf)) ? n : sizeof(buf); /* tweak to buf size */ |
137 | |
|
138 | 0 | n = php_stream_read(fp, buf, n); |
139 | 0 | if (n > 0) { |
140 | 0 | *crc = php_crc32_bulk_update(*crc, buf, n); |
141 | 0 | handled += n; |
142 | 0 | } else { /* EOF */ |
143 | 0 | return FAILURE; |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | 0 | return SUCCESS; |
148 | 0 | } |
149 | | |
150 | | /* {{{ Calculate the crc32 polynomial of a string */ |
151 | | PHP_FUNCTION(crc32) |
152 | 0 | { |
153 | 0 | char *p; |
154 | 0 | size_t nr; |
155 | 0 | uint32_t crc = php_crc32_bulk_init(); |
156 | |
|
157 | 0 | ZEND_PARSE_PARAMETERS_START(1, 1) |
158 | 0 | Z_PARAM_STRING(p, nr) |
159 | 0 | ZEND_PARSE_PARAMETERS_END(); |
160 | | |
161 | 0 | crc = php_crc32_bulk_update(crc, p, nr); |
162 | |
|
163 | 0 | RETURN_LONG(php_crc32_bulk_end(crc)); |
164 | 0 | } |
165 | | /* }}} */ |