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