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