1 /*
2  *   This module provides crc32c checksum (http://www.rfc-editor.org/rfc/rfc3385.txt)
3  *   based on the Intel CRC32 instruction
4  *   provided in the Intel SSE4.2 instruction set
5  *
6  *    ICRAR - International Centre for Radio Astronomy Research
7  *    (c) UWA - The University of Western Australia, 2014
8  *    Copyright by UWA (in the framework of the ICRAR)
9  *    All rights reserved
10  *
11  *    This library is free software; you can redistribute it and/or
12  *    modify it under the terms of the GNU Lesser General Public
13  *    License as published by the Free Software Foundation; either
14  *    version 2.1 of the License, or (at your option) any later version.
15  *
16  *    This library is distributed in the hope that it will be useful,
17  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  *    Lesser General Public License for more details.
20  *
21  *    You should have received a copy of the GNU Lesser General Public
22  *    License along with this library; if not, write to the Free Software
23  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
24  *    MA 02111-1307  USA
25  *
26  */
27 
28 #include <Python.h>
29 
30 #include "checksse42.h"
31 #include "common.h"
32 #include "crc32c.h"
33 
34 /* Set at module loading time */
35 static crc_function crc_fn;
36 int is_big_endian;
37 
38 static
crc32c_crc32c(PyObject * self,PyObject * args)39 PyObject* crc32c_crc32c(PyObject *self, PyObject *args) {
40 	Py_buffer pbin;
41 	unsigned char *bin_data = NULL;
42 	uint32_t crc = 0U, result;
43 
44 	/* In python 3 we accept only bytes-like objects */
45 	const char *format =
46 #if PY_MAJOR_VERSION >= 3
47 	"y*"
48 #else
49 	"s*"
50 #endif
51 	"|I:crc32";
52 
53 	if (!PyArg_ParseTuple(args, format, &pbin, &crc) )
54 		return NULL;
55 
56 	bin_data = pbin.buf;
57 	crc ^= 0xffffffff;
58 	result = crc_fn(crc, bin_data, pbin.len);
59 	result ^= 0xffffffff;
60 
61 	PyBuffer_Release(&pbin);
62 	return PyLong_FromUnsignedLong(result);
63 }
64 
65 static
crc32c_crc32(PyObject * self,PyObject * args)66 PyObject *crc32c_crc32(PyObject *self, PyObject *args)
67 {
68 	if (PyErr_WarnEx(PyExc_DeprecationWarning,
69 	                 "crc32c.crc32 will be eventually removed, use crc32c.crc32c instead",
70 	                 1) == -1) {
71 		return NULL;
72 	}
73 	return crc32c_crc32c(self, args);
74 }
75 
76 /* The different values the SW mode preference can take */
77 enum crc32c_sw_mode {
78 	UNSPECIFIED,
79 	AUTO,
80 	FORCE,
81 	NONE
82 };
83 
get_sw_mode(void)84 static enum crc32c_sw_mode get_sw_mode(void)
85 {
86 	char *sw_mode = getenv("CRC32C_SW_MODE");
87 	if (sw_mode == NULL) {
88 		return UNSPECIFIED;
89 	}
90 	else if (!strcmp(sw_mode, "auto")) {
91 		return AUTO;
92 	}
93 	else if (!strcmp(sw_mode, "force")) {
94 		return FORCE;
95 	}
96 	else if (!strcmp(sw_mode, "none")) {
97 		return NONE;
98 	}
99 	return UNSPECIFIED;
100 }
101 
102 static PyMethodDef CRC32CMethods[] = {
103 	{"crc32",   crc32c_crc32,   METH_VARARGS, "Calculate crc32c incrementally (deprecated)"},
104 	{"crc32c",  crc32c_crc32c,  METH_VARARGS, "Calculate crc32c incrementally"},
105 	{NULL, NULL, 0, NULL}        /* Sentinel */
106 };
107 
108 static const char *import_error_msg = "\n\n"
109 "Hardware extensions providing a crc32c hardware instruction are not available in\n"
110 "your processor. This package comes with a software implementation, but this\n"
111 "support has been opted out because the CRC32C_SW_MODE environment variable is\n"
112 "set to \"none\". CRC32C_SW_MODE can take one of the following values:\n"
113 " * If unset: use the software implementation if no hardware support is found\n"
114 " * 'auto': as above, but will eventually be discontinued\n"
115 " * 'force': use software implementation regardless of hardware support.\n"
116 " * 'none': fail if no hardware support is found (this error).\n";
117 
118 /* Support for Python 2/3 */
119 #if PY_MAJOR_VERSION >= 3
120 static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "crc32c", "crc32c implementation in hardware and software", -1, CRC32CMethods};
121 	#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
122 	#define MOD_DEF(m, name, doc, methods) \
123 		m = PyModule_Create(&moduledef);
124 	#define MOD_VAL(v) v
125 #else
126 	#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
127 	#define MOD_DEF(m, name, doc, methods) \
128 		m = Py_InitModule3(name, methods, doc);
129 	#define MOD_VAL(v)
130 #endif
131 
MOD_INIT(crc32c)132 MOD_INIT(crc32c)
133 {
134 	PyObject *m;
135 	PyObject *hardware_based;
136 	enum crc32c_sw_mode sw_mode;
137 	const uint32_t n = 1;
138 
139 	sw_mode = get_sw_mode();
140 	crc_fn = NULL;
141 	if (sw_mode == FORCE) {
142 		crc_fn = _crc32c_sw_slicing_by_8;
143 		hardware_based = Py_False;
144 	}
145 #if defined(IS_INTEL)
146 	else if (_crc32c_intel_probe()) {
147 		crc_fn = _crc32c_hw_adler;
148 		crc32c_init_hw_adler();
149 		hardware_based = Py_True;
150 	}
151 #elif defined(IS_ARM) && (defined(__linux__) || defined(linux))
152 	else if (_crc32c_arm64_probe()) {
153 		crc_fn = _crc32c_hw_arm64;
154 		hardware_based = Py_True;
155 	}
156 #endif
157 	else if (sw_mode == UNSPECIFIED || sw_mode == AUTO) {
158 		crc_fn = _crc32c_sw_slicing_by_8;
159 		hardware_based = Py_False;
160 	}
161 	else if (sw_mode == NONE) {
162 		PyErr_SetString(PyExc_ImportError, import_error_msg);
163 		return MOD_VAL(NULL);
164 	}
165 
166 	is_big_endian = (*(const char *)(&n) == 0);
167 
168 	MOD_DEF(m, "crc32c", "crc32c implementation in hardware and software", CRC32CMethods);
169 	if (m == NULL) {
170 		return MOD_VAL(NULL);
171 	}
172 	Py_INCREF(hardware_based);
173 	if (PyModule_AddObject(m, "hardware_based", hardware_based) < 0) {
174 		return MOD_VAL(NULL);
175 	}
176 	if (PyModule_AddIntConstant(m, "big_endian", is_big_endian) < 0) {
177 		return MOD_VAL(NULL);
178 	}
179 	return MOD_VAL(m);
180 }
181