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