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