comparison cutils/crcmod/python3/crcmod.py @ 180:d038f0a9ba49

Vendored crcmod2 into sub-package "cutils.crcmod" as pure-Python implementation of additional "digests": CRC sums. Running python -m cutils.crcmod.test works successfully.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 13 Jan 2025 04:09:35 +0100
parents
children 5c5c0c5a7402
comparison
equal deleted inserted replaced
179:53614a724bf0 180:d038f0a9ba49
1 #-----------------------------------------------------------------------------
2 # Copyright (c) 2010 Raymond L. Buvel
3 # Copyright (c) 2010 Craig McQueen
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a copy
6 # of this software and associated documentation files (the "Software"), to deal
7 # in the Software without restriction, including without limitation the rights
8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 # copies of the Software, and to permit persons to whom the Software is
10 # furnished to do so, subject to the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 # SOFTWARE.
22 #-----------------------------------------------------------------------------
23 '''crcmod is a Python module for gererating objects that compute the Cyclic
24 Redundancy Check. Any 8, 16, 24, 32, or 64 bit polynomial can be used.
25
26 The following are the public components of this module.
27
28 Crc -- a class that creates instances providing the same interface as the
29 algorithms in the hashlib module in the Python standard library. These
30 instances also provide a method for generating a C/C++ function to compute
31 the CRC.
32
33 mkCrcFun -- create a Python function to compute the CRC using the specified
34 polynomial and initial value. This provides a much simpler interface if
35 all you need is a function for CRC calculation.
36 '''
37
38 __all__ = '''mkCrcFun Crc'''.split()
39
40 # Select the appropriate set of low-level CRC functions for this installation.
41 # If the extension module was not built, drop back to the Python implementation
42 # even though it is significantly slower.
43 try:
44 from . import _crcfunext as _crcfun
45 _usingExtension = True
46 except ImportError:
47 from . import _crcfunpy as _crcfun
48 _usingExtension = False
49
50 import sys, struct
51
52 #-----------------------------------------------------------------------------
53 class Crc:
54 '''Compute a Cyclic Redundancy Check (CRC) using the specified polynomial.
55
56 Instances of this class have the same interface as the algorithms in the
57 hashlib module in the Python standard library. See the documentation of
58 this module for examples of how to use a Crc instance.
59
60 The string representation of a Crc instance identifies the polynomial,
61 initial value, XOR out value, and the current CRC value. The print
62 statement can be used to output this information.
63
64 If you need to generate a C/C++ function for use in another application,
65 use the generateCode method. If you need to generate code for another
66 language, subclass Crc and override the generateCode method.
67
68 The following are the parameters supplied to the constructor.
69
70 poly -- The generator polynomial to use in calculating the CRC. The value
71 is specified as a Python integer. The bits in this integer are the
72 coefficients of the polynomial. The only polynomials allowed are those
73 that generate 8, 16, 24, 32, or 64 bit CRCs.
74
75 initCrc -- Initial value used to start the CRC calculation. This initial
76 value should be the initial shift register value XORed with the final XOR
77 value. That is equivalent to the CRC result the algorithm should return for
78 a zero-length string. Defaults to all bits set because that starting value
79 will take leading zero bytes into account. Starting with zero will ignore
80 all leading zero bytes.
81
82 rev -- A flag that selects a bit reversed algorithm when True. Defaults to
83 True because the bit reversed algorithms are more efficient.
84
85 xorOut -- Final value to XOR with the calculated CRC value. Used by some
86 CRC algorithms. Defaults to zero.
87 '''
88 def __init__(self, poly, initCrc=~0, rev=True, xorOut=0, initialize=True):
89 if not initialize:
90 # Don't want to perform the initialization when using new or copy
91 # to create a new instance.
92 return
93
94 (sizeBits, initCrc, xorOut) = _verifyParams(poly, initCrc, xorOut)
95 self.digest_size = sizeBits//8
96 self.initCrc = initCrc
97 self.xorOut = xorOut
98
99 self.poly = poly
100 self.reverse = rev
101
102 (crcfun, table) = _mkCrcFun(poly, sizeBits, initCrc, rev, xorOut)
103 self._crc = crcfun
104 self.table = table
105
106 self.crcValue = self.initCrc
107
108 def __str__(self):
109 lst = []
110 lst.append('poly = 0x%X' % self.poly)
111 lst.append('reverse = %s' % self.reverse)
112 fmt = '0x%%0%dX' % (self.digest_size*2)
113 lst.append('initCrc = %s' % (fmt % self.initCrc))
114 lst.append('xorOut = %s' % (fmt % self.xorOut))
115 lst.append('crcValue = %s' % (fmt % self.crcValue))
116 return '\n'.join(lst)
117
118 def new(self, arg=None):
119 '''Create a new instance of the Crc class initialized to the same
120 values as the original instance. The current CRC is set to the initial
121 value. If a string is provided in the optional arg parameter, it is
122 passed to the update method.
123 '''
124 n = Crc(poly=None, initialize=False)
125 n._crc = self._crc
126 n.digest_size = self.digest_size
127 n.initCrc = self.initCrc
128 n.xorOut = self.xorOut
129 n.table = self.table
130 n.crcValue = self.initCrc
131 n.reverse = self.reverse
132 n.poly = self.poly
133 if arg is not None:
134 n.update(arg)
135 return n
136
137 def copy(self):
138 '''Create a new instance of the Crc class initialized to the same
139 values as the original instance. The current CRC is set to the current
140 value. This allows multiple CRC calculations using a common initial
141 string.
142 '''
143 c = self.new()
144 c.crcValue = self.crcValue
145 return c
146
147 def update(self, data):
148 '''Update the current CRC value using the string specified as the data
149 parameter.
150 '''
151 self.crcValue = self._crc(data, self.crcValue)
152
153 def digest(self):
154 '''Return the current CRC value as a string of bytes. The length of
155 this string is specified in the digest_size attribute.
156 '''
157 n = self.digest_size
158 crc = self.crcValue
159 lst = []
160 while n > 0:
161 lst.append(crc & 0xFF)
162 crc = crc >> 8
163 n -= 1
164 lst.reverse()
165 return bytes(lst)
166
167 def hexdigest(self):
168 '''Return the current CRC value as a string of hex digits. The length
169 of this string is twice the digest_size attribute.
170 '''
171 n = self.digest_size
172 crc = self.crcValue
173 lst = []
174 while n > 0:
175 lst.append('%02X' % (crc & 0xFF))
176 crc = crc >> 8
177 n -= 1
178 lst.reverse()
179 return ''.join(lst)
180
181 def generateCode(self, functionName, out, dataType=None, crcType=None):
182 '''Generate a C/C++ function.
183
184 functionName -- String specifying the name of the function.
185
186 out -- An open file-like object with a write method. This specifies
187 where the generated code is written.
188
189 dataType -- An optional parameter specifying the data type of the input
190 data to the function. Defaults to UINT8.
191
192 crcType -- An optional parameter specifying the data type of the CRC
193 value. Defaults to one of UINT8, UINT16, UINT32, or UINT64 depending
194 on the size of the CRC value.
195 '''
196 if dataType is None:
197 dataType = 'UINT8'
198
199 if crcType is None:
200 size = 8*self.digest_size
201 if size == 24:
202 size = 32
203 crcType = 'UINT%d' % size
204
205 if self.digest_size == 1:
206 # Both 8-bit CRC algorithms are the same
207 crcAlgor = 'table[*data ^ (%s)crc]'
208 elif self.reverse:
209 # The bit reverse algorithms are all the same except for the data
210 # type of the crc variable which is specified elsewhere.
211 crcAlgor = 'table[*data ^ (%s)crc] ^ (crc >> 8)'
212 else:
213 # The forward CRC algorithms larger than 8 bits have an extra shift
214 # operation to get the high byte.
215 shift = 8*(self.digest_size - 1)
216 crcAlgor = 'table[*data ^ (%%s)(crc >> %d)] ^ (crc << 8)' % shift
217
218 fmt = '0x%%0%dX' % (2*self.digest_size)
219 if self.digest_size <= 4:
220 fmt = fmt + 'U,'
221 else:
222 # Need the long long type identifier to keep gcc from complaining.
223 fmt = fmt + 'ULL,'
224
225 # Select the number of entries per row in the output code.
226 n = {1:8, 2:8, 3:4, 4:4, 8:2}[self.digest_size]
227
228 lst = []
229 for i, val in enumerate(self.table):
230 if (i % n) == 0:
231 lst.append('\n ')
232 lst.append(fmt % val)
233
234 poly = 'polynomial: 0x%X' % self.poly
235 if self.reverse:
236 poly = poly + ', bit reverse algorithm'
237
238 if self.xorOut:
239 # Need to remove the comma from the format.
240 preCondition = '\n crc = crc ^ %s;' % (fmt[:-1] % self.xorOut)
241 postCondition = preCondition
242 else:
243 preCondition = ''
244 postCondition = ''
245
246 if self.digest_size == 3:
247 # The 24-bit CRC needs to be conditioned so that only 24-bits are
248 # used from the 32-bit variable.
249 if self.reverse:
250 preCondition += '\n crc = crc & 0xFFFFFFU;'
251 else:
252 postCondition += '\n crc = crc & 0xFFFFFFU;'
253
254
255 parms = {
256 'dataType' : dataType,
257 'crcType' : crcType,
258 'name' : functionName,
259 'crcAlgor' : crcAlgor % dataType,
260 'crcTable' : ''.join(lst),
261 'poly' : poly,
262 'preCondition' : preCondition,
263 'postCondition' : postCondition,
264 }
265 out.write(_codeTemplate % parms)
266
267 #-----------------------------------------------------------------------------
268 def mkCrcFun(poly, initCrc=~0, rev=True, xorOut=0):
269 '''Return a function that computes the CRC using the specified polynomial.
270
271 poly -- integer representation of the generator polynomial
272 initCrc -- default initial CRC value
273 rev -- when true, indicates that the data is processed bit reversed.
274 xorOut -- the final XOR value
275
276 The returned function has the following user interface
277 def crcfun(data, crc=initCrc):
278 '''
279
280 # First we must verify the params
281 (sizeBits, initCrc, xorOut) = _verifyParams(poly, initCrc, xorOut)
282 # Make the function (and table), return the function
283 return _mkCrcFun(poly, sizeBits, initCrc, rev, xorOut)[0]
284
285 #-----------------------------------------------------------------------------
286 # Naming convention:
287 # All function names ending with r are bit reverse variants of the ones
288 # without the r.
289
290 #-----------------------------------------------------------------------------
291 # Check the polynomial to make sure that it is acceptable and return the number
292 # of bits in the CRC.
293
294 def _verifyPoly(poly):
295 msg = 'The degree of the polynomial must be 8, 16, 24, 32 or 64'
296 for n in (8,16,24,32,64):
297 low = 1<<n
298 high = low*2
299 if low <= poly < high:
300 return n
301 raise ValueError(msg)
302
303 #-----------------------------------------------------------------------------
304 # Bit reverse the input value.
305
306 def _bitrev(x, n):
307 y = 0
308 for i in range(n):
309 y = (y << 1) | (x & 1)
310 x = x >> 1
311 return y
312
313 #-----------------------------------------------------------------------------
314 # The following functions compute the CRC for a single byte. These are used
315 # to build up the tables needed in the CRC algorithm. Assumes the high order
316 # bit of the polynomial has been stripped off.
317
318 def _bytecrc(crc, poly, n):
319 mask = 1<<(n-1)
320 for i in range(8):
321 if crc & mask:
322 crc = (crc << 1) ^ poly
323 else:
324 crc = crc << 1
325 mask = (1<<n) - 1
326 crc = crc & mask
327 return crc
328
329 def _bytecrc_r(crc, poly, n):
330 for i in range(8):
331 if crc & 1:
332 crc = (crc >> 1) ^ poly
333 else:
334 crc = crc >> 1
335 mask = (1<<n) - 1
336 crc = crc & mask
337 return crc
338
339 #-----------------------------------------------------------------------------
340 # The following functions compute the table needed to compute the CRC. The
341 # table is returned as a list. Note that the array module does not support
342 # 64-bit integers on a 32-bit architecture as of Python 2.3.
343 #
344 # These routines assume that the polynomial and the number of bits in the CRC
345 # have been checked for validity by the caller.
346
347 def _mkTable(poly, n):
348 mask = (1<<n) - 1
349 poly = poly & mask
350 table = [_bytecrc(i<<(n-8),poly,n) for i in range(256)]
351 return table
352
353 def _mkTable_r(poly, n):
354 mask = (1<<n) - 1
355 poly = _bitrev(poly & mask, n)
356 table = [_bytecrc_r(i,poly,n) for i in range(256)]
357 return table
358
359 #-----------------------------------------------------------------------------
360 # Map the CRC size onto the functions that handle these sizes.
361
362 _sizeMap = {
363 8 : [_crcfun._crc8, _crcfun._crc8r],
364 16 : [_crcfun._crc16, _crcfun._crc16r],
365 24 : [_crcfun._crc24, _crcfun._crc24r],
366 32 : [_crcfun._crc32, _crcfun._crc32r],
367 64 : [_crcfun._crc64, _crcfun._crc64r],
368 }
369
370 #-----------------------------------------------------------------------------
371 # Build a mapping of size to struct module type code. This table is
372 # constructed dynamically so that it has the best chance of picking the best
373 # code to use for the platform we are running on. This should properly adapt
374 # to 32 and 64 bit machines.
375
376 _sizeToTypeCode = {}
377
378 for typeCode in 'B H I L Q'.split():
379 size = {1:8, 2:16, 4:32, 8:64}.get(struct.calcsize(typeCode),None)
380 if size is not None and size not in _sizeToTypeCode:
381 _sizeToTypeCode[size] = '256%s' % typeCode
382
383 _sizeToTypeCode[24] = _sizeToTypeCode[32]
384
385 del typeCode, size
386
387 #-----------------------------------------------------------------------------
388 # The following function validates the parameters of the CRC, namely,
389 # poly, and initial/final XOR values.
390 # It returns the size of the CRC (in bits), and "sanitized" initial/final XOR values.
391
392 def _verifyParams(poly, initCrc, xorOut):
393 sizeBits = _verifyPoly(poly)
394
395 mask = (1<<sizeBits) - 1
396
397 # Adjust the initial CRC to the correct data type (unsigned value).
398 initCrc = initCrc & mask
399
400 # Similar for XOR-out value.
401 xorOut = xorOut & mask
402
403 return (sizeBits, initCrc, xorOut)
404
405 #-----------------------------------------------------------------------------
406 # The following function returns a Python function to compute the CRC.
407 #
408 # It must be passed parameters that are already verified & sanitized by
409 # _verifyParams().
410 #
411 # The returned function calls a low level function that is written in C if the
412 # extension module could be loaded. Otherwise, a Python implementation is
413 # used.
414 #
415 # In addition to this function, a list containing the CRC table is returned.
416
417 def _mkCrcFun(poly, sizeBits, initCrc, rev, xorOut):
418 if rev:
419 tableList = _mkTable_r(poly, sizeBits)
420 _fun = _sizeMap[sizeBits][1]
421 else:
422 tableList = _mkTable(poly, sizeBits)
423 _fun = _sizeMap[sizeBits][0]
424
425 _table = tableList
426 if _usingExtension:
427 _table = struct.pack(_sizeToTypeCode[sizeBits], *tableList)
428
429 if xorOut == 0:
430 def crcfun(data, crc=initCrc, table=_table, fun=_fun):
431 return fun(data, crc, table)
432 else:
433 def crcfun(data, crc=initCrc, table=_table, fun=_fun):
434 return xorOut ^ fun(data, xorOut ^ crc, table)
435
436 return crcfun, tableList
437
438 #-----------------------------------------------------------------------------
439 _codeTemplate = '''// Automatically generated CRC function
440 // %(poly)s
441 %(crcType)s
442 %(name)s(%(dataType)s *data, int len, %(crcType)s crc)
443 {
444 static const %(crcType)s table[256] = {%(crcTable)s
445 };
446 %(preCondition)s
447 while (len > 0)
448 {
449 crc = %(crcAlgor)s;
450 data++;
451 len--;
452 }%(postCondition)s
453 return crc;
454 }
455 '''
456