xref: /PHP-8.2/ext/standard/crc32.c (revision 77bd39a1)
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()42 static inline int has_crc32_insn() {
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 #  else
72 #   pragma clang attribute push(__attribute__((target("+nothing+crc"))), apply_to=function)
73 #  endif
74 # endif
crc32_aarch64(uint32_t crc,const char * p,size_t nr)75 static uint32_t crc32_aarch64(uint32_t crc, const char *p, size_t nr) {
76 	while (nr >= sizeof(uint64_t)) {
77 		crc = __crc32d(crc, *(uint64_t *)p);
78 		p += sizeof(uint64_t);
79 		nr -= sizeof(uint64_t);
80 	}
81 	if (nr >= sizeof(int32_t)) {
82 		crc = __crc32w(crc, *(uint32_t *)p);
83 		p += sizeof(uint32_t);
84 		nr -= sizeof(uint32_t);
85 	}
86 	if (nr >= sizeof(int16_t)) {
87 		crc = __crc32h(crc, *(uint16_t *)p);
88 		p += sizeof(uint16_t);
89 		nr -= sizeof(uint16_t);
90 	}
91 	if (nr) {
92 		crc = __crc32b(crc, *p);
93 	}
94 	return crc;
95 }
96 # if defined(__GNUC__)
97 #  if !defined(__clang__)
98 #   pragma GCC pop_options
99 #  else
100 #   pragma clang attribute pop
101 #  endif
102 # endif
103 #endif
104 
php_crc32_bulk_update(uint32_t crc,const char * p,size_t nr)105 PHPAPI uint32_t php_crc32_bulk_update(uint32_t crc, const char *p, size_t nr)
106 {
107 #ifdef HAVE_AARCH64_CRC32
108 	if (has_crc32_insn()) {
109 		crc = crc32_aarch64(crc, p, nr);
110 		return crc;
111 	}
112 #endif
113 
114 #if ZEND_INTRIN_SSE4_2_PCLMUL_NATIVE || ZEND_INTRIN_SSE4_2_PCLMUL_RESOLVER
115 	size_t nr_simd = crc32_x86_simd_update(X86_CRC32B, &crc, (const unsigned char *)p, nr);
116 	nr -= nr_simd;
117 	p += nr_simd;
118 #endif
119 
120 	/* The trailing part */
121 	for (; nr--; ++p) {
122 		crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[(crc ^ (*p)) & 0xFF ];
123 	}
124 
125 	return crc;
126 }
127 
php_crc32_stream_bulk_update(uint32_t * crc,php_stream * fp,size_t nr)128 PHPAPI int php_crc32_stream_bulk_update(uint32_t *crc, php_stream *fp, size_t nr)
129 {
130 	size_t handled = 0, n;
131 	char buf[1024];
132 
133 	while (handled < nr) {
134 		n = nr - handled;
135 		n = (n < sizeof(buf)) ? n : sizeof(buf); /* tweak to buf size */
136 
137 		n = php_stream_read(fp, buf, n);
138 		if (n > 0) {
139 			*crc = php_crc32_bulk_update(*crc, buf, n);
140 			handled += n;
141 		} else { /* EOF */
142 			return FAILURE;
143 		}
144 	}
145 
146 	return SUCCESS;
147 }
148 
149 /* {{{ Calculate the crc32 polynomial of a string */
PHP_FUNCTION(crc32)150 PHP_FUNCTION(crc32)
151 {
152 	char *p;
153 	size_t nr;
154 	uint32_t crc = php_crc32_bulk_init();
155 
156 	ZEND_PARSE_PARAMETERS_START(1, 1)
157 		Z_PARAM_STRING(p, nr)
158 	ZEND_PARSE_PARAMETERS_END();
159 
160 	crc = php_crc32_bulk_update(crc, p, nr);
161 
162 	RETURN_LONG(php_crc32_bulk_end(crc));
163 }
164 /* }}} */
165