Mercurial > hgrepos > Python > apps > py-cutils
changeset 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 | 53614a724bf0 |
| children | c4f737ec368c |
| files | cutils/crcmod/__init__.py cutils/crcmod/predefined.py cutils/crcmod/python2/__init__.py cutils/crcmod/python2/_crcfunpy.py cutils/crcmod/python2/crcmod.py cutils/crcmod/python2/predefined.py cutils/crcmod/python2/test.py cutils/crcmod/python3/__init__.py cutils/crcmod/python3/_crcfunpy.py cutils/crcmod/python3/crcmod.py cutils/crcmod/python3/predefined.py cutils/crcmod/python3/test.py cutils/crcmod/test.py |
| diffstat | 13 files changed, 2534 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/__init__.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,17 @@ +# -*- coding: utf-8; -*- +from __future__ import absolute_import + +import sys + +if sys.version_info[0] < 3: + from .python2.crcmod import * + from .python2.crcmod import __doc__ +else: + from .python3.crcmod import * + from .python3.crcmod import __doc__ + + +__version__ = "1.7" + + +del sys
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/predefined.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,13 @@ +# -*- coding: utf-8; -*- +from __future__ import absolute_import + +import sys + +if sys.version_info[0] < 3: + from .python2.predefined import * + from .python2.predefined import __doc__ +else: + from .python3.predefined import * + from .python3.predefined import __doc__ + +del sys
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/python2/__init__.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,1 @@ +# -*- coding: utf-8 -*-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/python2/_crcfunpy.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,87 @@ +#----------------------------------------------------------------------------- +# Low level CRC functions for use by crcmod. This version is implemented in +# Python for a couple of reasons. 1) Provide a reference implememtation. +# 2) Provide a version that can be used on systems where a C compiler is not +# available for building extension modules. +# +# Copyright (c) 2004 Raymond L. Buvel +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#----------------------------------------------------------------------------- + +def _crc8(data, crc, table): + crc = crc & 0xFF + for x in data: + crc = table[ord(x) ^ crc] + return crc + +def _crc8r(data, crc, table): + crc = crc & 0xFF + for x in data: + crc = table[ord(x) ^ crc] + return crc + +def _crc16(data, crc, table): + crc = crc & 0xFFFF + for x in data: + crc = table[ord(x) ^ ((crc>>8) & 0xFF)] ^ ((crc << 8) & 0xFF00) + return crc + +def _crc16r(data, crc, table): + crc = crc & 0xFFFF + for x in data: + crc = table[ord(x) ^ (crc & 0xFF)] ^ (crc >> 8) + return crc + +def _crc24(data, crc, table): + crc = crc & 0xFFFFFF + for x in data: + crc = table[ord(x) ^ (int(crc>>16) & 0xFF)] ^ ((crc << 8) & 0xFFFF00) + return crc + +def _crc24r(data, crc, table): + crc = crc & 0xFFFFFF + for x in data: + crc = table[ord(x) ^ int(crc & 0xFF)] ^ (crc >> 8) + return crc + +def _crc32(data, crc, table): + crc = crc & 0xFFFFFFFFL + for x in data: + crc = table[ord(x) ^ (int(crc>>24) & 0xFF)] ^ ((crc << 8) & 0xFFFFFF00L) + return crc + +def _crc32r(data, crc, table): + crc = crc & 0xFFFFFFFFL + for x in data: + crc = table[ord(x) ^ int(crc & 0xFFL)] ^ (crc >> 8) + return crc + +def _crc64(data, crc, table): + crc = crc & 0xFFFFFFFFFFFFFFFFL + for x in data: + crc = table[ord(x) ^ (int(crc>>56) & 0xFF)] ^ ((crc << 8) & 0xFFFFFFFFFFFFFF00L) + return crc + +def _crc64r(data, crc, table): + crc = crc & 0xFFFFFFFFFFFFFFFFL + for x in data: + crc = table[ord(x) ^ int(crc & 0xFFL)] ^ (crc >> 8) + return crc +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/python2/crcmod.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,475 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2010 Raymond L. Buvel +# Copyright (c) 2010 Craig McQueen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#----------------------------------------------------------------------------- +'''crcmod is a Python module for gererating objects that compute the Cyclic +Redundancy Check. Any 8, 16, 24, 32, or 64 bit polynomial can be used. + +The following are the public components of this module. + +Crc -- a class that creates instances providing the same interface as the +md5 and sha modules in the Python standard library. These instances also +provide a method for generating a C/C++ function to compute the CRC. + +mkCrcFun -- create a Python function to compute the CRC using the specified +polynomial and initial value. This provides a much simpler interface if +all you need is a function for CRC calculation. +''' + +from __future__ import absolute_import + + +__all__ = '''mkCrcFun Crc'''.split() + + +# Select the appropriate set of low-level CRC functions for this installation. +# If the extension module was not built, drop back to the Python implementation +# even though it is significantly slower. +try: + from . import _crcfunext as _crcfun + _usingExtension = True +except ImportError: + from . import _crcfunpy as _crcfun + _usingExtension = False + +import sys, struct + +#----------------------------------------------------------------------------- +class Crc: + '''Compute a Cyclic Redundancy Check (CRC) using the specified polynomial. + + Instances of this class have the same interface as the md5 and sha modules + in the Python standard library. See the documentation for these modules + for examples of how to use a Crc instance. + + The string representation of a Crc instance identifies the polynomial, + initial value, XOR out value, and the current CRC value. The print + statement can be used to output this information. + + If you need to generate a C/C++ function for use in another application, + use the generateCode method. If you need to generate code for another + language, subclass Crc and override the generateCode method. + + The following are the parameters supplied to the constructor. + + poly -- The generator polynomial to use in calculating the CRC. The value + is specified as a Python integer or long integer. The bits in this integer + are the coefficients of the polynomial. The only polynomials allowed are + those that generate 8, 16, 24, 32, or 64 bit CRCs. + + initCrc -- Initial value used to start the CRC calculation. This initial + value should be the initial shift register value XORed with the final XOR + value. That is equivalent to the CRC result the algorithm should return for + a zero-length string. Defaults to all bits set because that starting value + will take leading zero bytes into account. Starting with zero will ignore + all leading zero bytes. + + rev -- A flag that selects a bit reversed algorithm when True. Defaults to + True because the bit reversed algorithms are more efficient. + + xorOut -- Final value to XOR with the calculated CRC value. Used by some + CRC algorithms. Defaults to zero. + ''' + def __init__(self, poly, initCrc=~0L, rev=True, xorOut=0, initialize=True): + if not initialize: + # Don't want to perform the initialization when using new or copy + # to create a new instance. + return + + (sizeBits, initCrc, xorOut) = _verifyParams(poly, initCrc, xorOut) + self.digest_size = sizeBits//8 + self.initCrc = initCrc + self.xorOut = xorOut + + self.poly = poly + self.reverse = rev + + (crcfun, table) = _mkCrcFun(poly, sizeBits, initCrc, rev, xorOut) + self._crc = crcfun + self.table = table + + self.crcValue = self.initCrc + + def __str__(self): + lst = [] + lst.append('poly = 0x%X' % self.poly) + lst.append('reverse = %s' % self.reverse) + fmt = '0x%%0%dX' % (self.digest_size*2) + lst.append('initCrc = %s' % (fmt % self.initCrc)) + lst.append('xorOut = %s' % (fmt % self.xorOut)) + lst.append('crcValue = %s' % (fmt % self.crcValue)) + return '\n'.join(lst) + + def new(self, arg=None): + '''Create a new instance of the Crc class initialized to the same + values as the original instance. The current CRC is set to the initial + value. If a string is provided in the optional arg parameter, it is + passed to the update method. + ''' + n = Crc(poly=None, initialize=False) + n._crc = self._crc + n.digest_size = self.digest_size + n.initCrc = self.initCrc + n.xorOut = self.xorOut + n.table = self.table + n.crcValue = self.initCrc + n.reverse = self.reverse + n.poly = self.poly + if arg is not None: + n.update(arg) + return n + + def copy(self): + '''Create a new instance of the Crc class initialized to the same + values as the original instance. The current CRC is set to the current + value. This allows multiple CRC calculations using a common initial + string. + ''' + c = self.new() + c.crcValue = self.crcValue + return c + + def update(self, data): + '''Update the current CRC value using the string specified as the data + parameter. + ''' + self.crcValue = self._crc(data, self.crcValue) + + def digest(self): + '''Return the current CRC value as a string of bytes. The length of + this string is specified in the digest_size attribute. + ''' + n = self.digest_size + crc = self.crcValue + lst = [] + while n > 0: + lst.append(chr(crc & 0xFF)) + crc = crc >> 8 + n -= 1 + lst.reverse() + return ''.join(lst) + + def hexdigest(self): + '''Return the current CRC value as a string of hex digits. The length + of this string is twice the digest_size attribute. + ''' + n = self.digest_size + crc = self.crcValue + lst = [] + while n > 0: + lst.append('%02X' % (crc & 0xFF)) + crc = crc >> 8 + n -= 1 + lst.reverse() + return ''.join(lst) + + def generateCode(self, functionName, out, dataType=None, crcType=None): + '''Generate a C/C++ function. + + functionName -- String specifying the name of the function. + + out -- An open file-like object with a write method. This specifies + where the generated code is written. + + dataType -- An optional parameter specifying the data type of the input + data to the function. Defaults to UINT8. + + crcType -- An optional parameter specifying the data type of the CRC + value. Defaults to one of UINT8, UINT16, UINT32, or UINT64 depending + on the size of the CRC value. + ''' + if dataType is None: + dataType = 'UINT8' + + if crcType is None: + size = 8*self.digest_size + if size == 24: + size = 32 + crcType = 'UINT%d' % size + + if self.digest_size == 1: + # Both 8-bit CRC algorithms are the same + crcAlgor = 'table[*data ^ (%s)crc]' + elif self.reverse: + # The bit reverse algorithms are all the same except for the data + # type of the crc variable which is specified elsewhere. + crcAlgor = 'table[*data ^ (%s)crc] ^ (crc >> 8)' + else: + # The forward CRC algorithms larger than 8 bits have an extra shift + # operation to get the high byte. + shift = 8*(self.digest_size - 1) + crcAlgor = 'table[*data ^ (%%s)(crc >> %d)] ^ (crc << 8)' % shift + + fmt = '0x%%0%dX' % (2*self.digest_size) + if self.digest_size <= 4: + fmt = fmt + 'U,' + else: + # Need the long long type identifier to keep gcc from complaining. + fmt = fmt + 'ULL,' + + # Select the number of entries per row in the output code. + n = {1:8, 2:8, 3:4, 4:4, 8:2}[self.digest_size] + + lst = [] + for i, val in enumerate(self.table): + if (i % n) == 0: + lst.append('\n ') + lst.append(fmt % val) + + poly = 'polynomial: 0x%X' % self.poly + if self.reverse: + poly = poly + ', bit reverse algorithm' + + if self.xorOut: + # Need to remove the comma from the format. + preCondition = '\n crc = crc ^ %s;' % (fmt[:-1] % self.xorOut) + postCondition = preCondition + else: + preCondition = '' + postCondition = '' + + if self.digest_size == 3: + # The 24-bit CRC needs to be conditioned so that only 24-bits are + # used from the 32-bit variable. + if self.reverse: + preCondition += '\n crc = crc & 0xFFFFFFU;' + else: + postCondition += '\n crc = crc & 0xFFFFFFU;' + + + parms = { + 'dataType' : dataType, + 'crcType' : crcType, + 'name' : functionName, + 'crcAlgor' : crcAlgor % dataType, + 'crcTable' : ''.join(lst), + 'poly' : poly, + 'preCondition' : preCondition, + 'postCondition' : postCondition, + } + out.write(_codeTemplate % parms) + +#----------------------------------------------------------------------------- +def mkCrcFun(poly, initCrc=~0L, rev=True, xorOut=0): + '''Return a function that computes the CRC using the specified polynomial. + + poly -- integer representation of the generator polynomial + initCrc -- default initial CRC value + rev -- when true, indicates that the data is processed bit reversed. + xorOut -- the final XOR value + + The returned function has the following user interface + def crcfun(data, crc=initCrc): + ''' + + # First we must verify the params + (sizeBits, initCrc, xorOut) = _verifyParams(poly, initCrc, xorOut) + # Make the function (and table), return the function + return _mkCrcFun(poly, sizeBits, initCrc, rev, xorOut)[0] + +#----------------------------------------------------------------------------- +# Naming convention: +# All function names ending with r are bit reverse variants of the ones +# without the r. + +#----------------------------------------------------------------------------- +# Check the polynomial to make sure that it is acceptable and return the number +# of bits in the CRC. + +def _verifyPoly(poly): + msg = 'The degree of the polynomial must be 8, 16, 24, 32 or 64' + poly = long(poly) # Use a common representation for all operations + for n in (8,16,24,32,64): + low = 1L<<n + high = low*2 + if low <= poly < high: + return n + raise ValueError(msg) + +#----------------------------------------------------------------------------- +# Bit reverse the input value. + +def _bitrev(x, n): + x = long(x) + y = 0L + for i in xrange(n): + y = (y << 1) | (x & 1L) + x = x >> 1 + if ((1L<<n)-1) <= sys.maxint: + return int(y) + return y + +#----------------------------------------------------------------------------- +# The following functions compute the CRC for a single byte. These are used +# to build up the tables needed in the CRC algorithm. Assumes the high order +# bit of the polynomial has been stripped off. + +def _bytecrc(crc, poly, n): + crc = long(crc) + poly = long(poly) + mask = 1L<<(n-1) + for i in xrange(8): + if crc & mask: + crc = (crc << 1) ^ poly + else: + crc = crc << 1 + mask = (1L<<n) - 1 + crc = crc & mask + if mask <= sys.maxint: + return int(crc) + return crc + +def _bytecrc_r(crc, poly, n): + crc = long(crc) + poly = long(poly) + for i in xrange(8): + if crc & 1L: + crc = (crc >> 1) ^ poly + else: + crc = crc >> 1 + mask = (1L<<n) - 1 + crc = crc & mask + if mask <= sys.maxint: + return int(crc) + return crc + +#----------------------------------------------------------------------------- +# The following functions compute the table needed to compute the CRC. The +# table is returned as a list. Note that the array module does not support +# 64-bit integers on a 32-bit architecture as of Python 2.3. +# +# These routines assume that the polynomial and the number of bits in the CRC +# have been checked for validity by the caller. + +def _mkTable(poly, n): + mask = (1L<<n) - 1 + poly = long(poly) & mask + table = [_bytecrc(long(i)<<(n-8),poly,n) for i in xrange(256)] + return table + +def _mkTable_r(poly, n): + mask = (1L<<n) - 1 + poly = _bitrev(long(poly) & mask, n) + table = [_bytecrc_r(long(i),poly,n) for i in xrange(256)] + return table + +#----------------------------------------------------------------------------- +# Map the CRC size onto the functions that handle these sizes. + +_sizeMap = { + 8 : [_crcfun._crc8, _crcfun._crc8r], + 16 : [_crcfun._crc16, _crcfun._crc16r], + 24 : [_crcfun._crc24, _crcfun._crc24r], + 32 : [_crcfun._crc32, _crcfun._crc32r], + 64 : [_crcfun._crc64, _crcfun._crc64r], +} + +#----------------------------------------------------------------------------- +# Build a mapping of size to struct module type code. This table is +# constructed dynamically so that it has the best chance of picking the best +# code to use for the platform we are running on. This should properly adapt +# to 32 and 64 bit machines. + +_sizeToTypeCode = {} + +for typeCode in 'B H I L Q'.split(): + size = {1:8, 2:16, 4:32, 8:64}.get(struct.calcsize(typeCode),None) + if size is not None and size not in _sizeToTypeCode: + _sizeToTypeCode[size] = '256%s' % typeCode + +_sizeToTypeCode[24] = _sizeToTypeCode[32] + +del typeCode, size + +#----------------------------------------------------------------------------- +# The following function validates the parameters of the CRC, namely, +# poly, and initial/final XOR values. +# It returns the size of the CRC (in bits), and "sanitized" initial/final XOR values. + +def _verifyParams(poly, initCrc, xorOut): + sizeBits = _verifyPoly(poly) + + mask = (1L<<sizeBits) - 1 + + # Adjust the initial CRC to the correct data type (unsigned value). + initCrc = long(initCrc) & mask + if mask <= sys.maxint: + initCrc = int(initCrc) + + # Similar for XOR-out value. + xorOut = long(xorOut) & mask + if mask <= sys.maxint: + xorOut = int(xorOut) + + return (sizeBits, initCrc, xorOut) + +#----------------------------------------------------------------------------- +# The following function returns a Python function to compute the CRC. +# +# It must be passed parameters that are already verified & sanitized by +# _verifyParams(). +# +# The returned function calls a low level function that is written in C if the +# extension module could be loaded. Otherwise, a Python implementation is +# used. +# +# In addition to this function, a list containing the CRC table is returned. + +def _mkCrcFun(poly, sizeBits, initCrc, rev, xorOut): + if rev: + tableList = _mkTable_r(poly, sizeBits) + _fun = _sizeMap[sizeBits][1] + else: + tableList = _mkTable(poly, sizeBits) + _fun = _sizeMap[sizeBits][0] + + _table = tableList + if _usingExtension: + _table = struct.pack(_sizeToTypeCode[sizeBits], *tableList) + + if xorOut == 0: + def crcfun(data, crc=initCrc, table=_table, fun=_fun): + return fun(data, crc, table) + else: + def crcfun(data, crc=initCrc, table=_table, fun=_fun): + return xorOut ^ fun(data, xorOut ^ crc, table) + + return crcfun, tableList + +#----------------------------------------------------------------------------- +_codeTemplate = '''// Automatically generated CRC function +// %(poly)s +%(crcType)s +%(name)s(%(dataType)s *data, int len, %(crcType)s crc) +{ + static const %(crcType)s table[256] = {%(crcTable)s + }; + %(preCondition)s + while (len > 0) + { + crc = %(crcAlgor)s; + data++; + len--; + }%(postCondition)s + return crc; +} +''' +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/python2/predefined.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,164 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2010 Craig McQueen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#----------------------------------------------------------------------------- +''' +crcmod.predefined defines some well-known CRC algorithms. + +To use it, e.g.: + import crcmod.predefined + + crc32func = crcmod.predefined.mkPredefinedCrcFun("crc-32") + crc32class = crcmod.predefined.PredefinedCrc("crc-32") + +crcmod.predefined.Crc is an alias for crcmod.predefined.PredefinedCrc +But if doing 'from crc.predefined import *', only PredefinedCrc is imported. +''' + +from __future__ import absolute_import + +# local imports +from . import crcmod + +__all__ = [ + 'PredefinedCrc', + 'mkPredefinedCrcFun', +] + +REVERSE = True +NON_REVERSE = False + +# The following table defines the parameters of well-known CRC algorithms. +# The "Check" value is the CRC for the ASCII byte sequence "123456789". It +# can be used for unit tests. +_crc_definitions_table = [ +# Name Identifier-name, Poly Reverse Init-value XOR-out Check + [ 'crc-8', 'Crc8', 0x107, NON_REVERSE, 0x00, 0x00, 0xF4, ], + [ 'crc-8-darc', 'Crc8Darc', 0x139, REVERSE, 0x00, 0x00, 0x15, ], + [ 'crc-8-i-code', 'Crc8ICode', 0x11D, NON_REVERSE, 0xFD, 0x00, 0x7E, ], + [ 'crc-8-itu', 'Crc8Itu', 0x107, NON_REVERSE, 0x55, 0x55, 0xA1, ], + [ 'crc-8-maxim', 'Crc8Maxim', 0x131, REVERSE, 0x00, 0x00, 0xA1, ], + [ 'crc-8-rohc', 'Crc8Rohc', 0x107, REVERSE, 0xFF, 0x00, 0xD0, ], + [ 'crc-8-wcdma', 'Crc8Wcdma', 0x19B, REVERSE, 0x00, 0x00, 0x25, ], + + [ 'crc-16', 'Crc16', 0x18005, REVERSE, 0x0000, 0x0000, 0xBB3D, ], + [ 'crc-16-buypass', 'Crc16Buypass', 0x18005, NON_REVERSE, 0x0000, 0x0000, 0xFEE8, ], + [ 'crc-16-dds-110', 'Crc16Dds110', 0x18005, NON_REVERSE, 0x800D, 0x0000, 0x9ECF, ], + [ 'crc-16-dect', 'Crc16Dect', 0x10589, NON_REVERSE, 0x0001, 0x0001, 0x007E, ], + [ 'crc-16-dnp', 'Crc16Dnp', 0x13D65, REVERSE, 0xFFFF, 0xFFFF, 0xEA82, ], + [ 'crc-16-en-13757', 'Crc16En13757', 0x13D65, NON_REVERSE, 0xFFFF, 0xFFFF, 0xC2B7, ], + [ 'crc-16-genibus', 'Crc16Genibus', 0x11021, NON_REVERSE, 0x0000, 0xFFFF, 0xD64E, ], + [ 'crc-16-maxim', 'Crc16Maxim', 0x18005, REVERSE, 0xFFFF, 0xFFFF, 0x44C2, ], + [ 'crc-16-mcrf4xx', 'Crc16Mcrf4xx', 0x11021, REVERSE, 0xFFFF, 0x0000, 0x6F91, ], + [ 'crc-16-riello', 'Crc16Riello', 0x11021, REVERSE, 0x554D, 0x0000, 0x63D0, ], + [ 'crc-16-t10-dif', 'Crc16T10Dif', 0x18BB7, NON_REVERSE, 0x0000, 0x0000, 0xD0DB, ], + [ 'crc-16-teledisk', 'Crc16Teledisk', 0x1A097, NON_REVERSE, 0x0000, 0x0000, 0x0FB3, ], + [ 'crc-16-usb', 'Crc16Usb', 0x18005, REVERSE, 0x0000, 0xFFFF, 0xB4C8, ], + [ 'x-25', 'CrcX25', 0x11021, REVERSE, 0x0000, 0xFFFF, 0x906E, ], + [ 'xmodem', 'CrcXmodem', 0x11021, NON_REVERSE, 0x0000, 0x0000, 0x31C3, ], + [ 'modbus', 'CrcModbus', 0x18005, REVERSE, 0xFFFF, 0x0000, 0x4B37, ], + + # Note definitions of CCITT are disputable. See: + # http://homepages.tesco.net/~rainstorm/crc-catalogue.htm + # http://web.archive.org/web/20071229021252/http://www.joegeluso.com/software/articles/ccitt.htm + [ 'kermit', 'CrcKermit', 0x11021, REVERSE, 0x0000, 0x0000, 0x2189, ], + [ 'crc-ccitt-false', 'CrcCcittFalse', 0x11021, NON_REVERSE, 0xFFFF, 0x0000, 0x29B1, ], + [ 'crc-aug-ccitt', 'CrcAugCcitt', 0x11021, NON_REVERSE, 0x1D0F, 0x0000, 0xE5CC, ], + + [ 'crc-24', 'Crc24', 0x1864CFB, NON_REVERSE, 0xB704CE, 0x000000, 0x21CF02, ], + [ 'crc-24-flexray-a', 'Crc24FlexrayA', 0x15D6DCB, NON_REVERSE, 0xFEDCBA, 0x000000, 0x7979BD, ], + [ 'crc-24-flexray-b', 'Crc24FlexrayB', 0x15D6DCB, NON_REVERSE, 0xABCDEF, 0x000000, 0x1F23B8, ], + + [ 'crc-32', 'Crc32', 0x104C11DB7, REVERSE, 0x00000000, 0xFFFFFFFF, 0xCBF43926, ], + [ 'crc-32-bzip2', 'Crc32Bzip2', 0x104C11DB7, NON_REVERSE, 0x00000000, 0xFFFFFFFF, 0xFC891918, ], + [ 'crc-32c', 'Crc32C', 0x11EDC6F41, REVERSE, 0x00000000, 0xFFFFFFFF, 0xE3069283, ], + [ 'crc-32d', 'Crc32D', 0x1A833982B, REVERSE, 0x00000000, 0xFFFFFFFF, 0x87315576, ], + [ 'crc-32-mpeg', 'Crc32Mpeg', 0x104C11DB7, NON_REVERSE, 0xFFFFFFFF, 0x00000000, 0x0376E6E7, ], + [ 'posix', 'CrcPosix', 0x104C11DB7, NON_REVERSE, 0xFFFFFFFF, 0xFFFFFFFF, 0x765E7680, ], + [ 'crc-32q', 'Crc32Q', 0x1814141AB, NON_REVERSE, 0x00000000, 0x00000000, 0x3010BF7F, ], + [ 'jamcrc', 'CrcJamCrc', 0x104C11DB7, REVERSE, 0xFFFFFFFF, 0x00000000, 0x340BC6D9, ], + [ 'xfer', 'CrcXfer', 0x1000000AF, NON_REVERSE, 0x00000000, 0x00000000, 0xBD0BE338, ], + +# 64-bit +# Name Identifier-name, Poly Reverse Init-value XOR-out Check + [ 'crc-64', 'Crc64', 0x1000000000000001B, REVERSE, 0x0000000000000000, 0x0000000000000000, 0x46A5A9388A5BEFFE, ], + [ 'crc-64-we', 'Crc64We', 0x142F0E1EBA9EA3693, NON_REVERSE, 0x0000000000000000, 0xFFFFFFFFFFFFFFFF, 0x62EC59E3F1A4F00A, ], + [ 'crc-64-jones', 'Crc64Jones', 0x1AD93D23594C935A9, REVERSE, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000, 0xCAA717168609F281, ], +] + + +def _simplify_name(name): + """ + Reduce CRC definition name to a simplified form: + * lowercase + * dashes removed + * spaces removed + * any initial "CRC" string removed + """ + name = name.lower() + name = name.replace('-', '') + name = name.replace(' ', '') + if name.startswith('crc'): + name = name[len('crc'):] + return name + + +_crc_definitions_by_name = {} +_crc_definitions_by_identifier = {} +_crc_definitions = [] + +_crc_table_headings = [ 'name', 'identifier', 'poly', 'reverse', 'init', 'xor_out', 'check' ] + +for table_entry in _crc_definitions_table: + crc_definition = dict(zip(_crc_table_headings, table_entry)) + _crc_definitions.append(crc_definition) + name = _simplify_name(table_entry[0]) + if name in _crc_definitions_by_name: + raise Exception("Duplicate entry for '%s' in CRC table" % name) + _crc_definitions_by_name[name] = crc_definition + _crc_definitions_by_identifier[table_entry[1]] = crc_definition + + +def _get_definition_by_name(crc_name): + definition = _crc_definitions_by_name.get(_simplify_name(crc_name), None) + if not definition: + definition = _crc_definitions_by_identifier.get(crc_name, None) + if not definition: + raise KeyError("Unkown CRC name '%s'" % crc_name) + return definition + + +class PredefinedCrc(crcmod.Crc): + def __init__(self, crc_name): + definition = _get_definition_by_name(crc_name) + crcmod.Crc.__init__(self, poly=definition['poly'], initCrc=definition['init'], rev=definition['reverse'], xorOut=definition['xor_out']) + + +# crcmod.predefined.Crc is an alias for crcmod.predefined.PredefinedCrc +Crc = PredefinedCrc + + +def mkPredefinedCrcFun(crc_name): + definition = _get_definition_by_name(crc_name) + return crcmod.mkCrcFun(poly=definition['poly'], initCrc=definition['init'], rev=definition['reverse'], xorOut=definition['xor_out']) + + +# crcmod.predefined.mkCrcFun is an alias for crcmod.predefined.mkPredefinedCrcFun +mkCrcFun = mkPredefinedCrcFun
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/python2/test.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,495 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2010 Raymond L. Buvel +# Copyright (c) 2010 Craig McQueen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#----------------------------------------------------------------------------- +'''Unit tests for crcmod functionality''' + + +from __future__ import absolute_import + +import unittest +import binascii + +from .crcmod import mkCrcFun, Crc +from .crcmod import _usingExtension +from .predefined import PredefinedCrc +from .predefined import mkPredefinedCrcFun +from .predefined import _crc_definitions as _predefined_crc_definitions + + +#----------------------------------------------------------------------------- +# This polynomial was chosen because it is the product of two irreducible +# polynomials. +# g8 = (x^7+x+1)*(x+1) +g8 = 0x185 + +#----------------------------------------------------------------------------- +# The following reproduces all of the entries in the Numerical Recipes table. +# This is the standard CCITT polynomial. +g16 = 0x11021 + +#----------------------------------------------------------------------------- +g24 = 0x15D6DCB + +#----------------------------------------------------------------------------- +# This is the standard AUTODIN-II polynomial which appears to be used in a +# wide variety of standards and applications. +g32 = 0x104C11DB7 + + +#----------------------------------------------------------------------------- +# I was able to locate a couple of 64-bit polynomials on the web. To make it +# easier to input the representation, define a function that builds a +# polynomial from a list of the bits that need to be turned on. + +def polyFromBits(bits): + p = 0L + for n in bits: + p = p | (1L << n) + return p + +# The following is from the paper "An Improved 64-bit Cyclic Redundancy Check +# for Protein Sequences" by David T. Jones + +g64a = polyFromBits([64, 63, 61, 59, 58, 56, 55, 52, 49, 48, 47, 46, 44, 41, + 37, 36, 34, 32, 31, 28, 26, 23, 22, 19, 16, 13, 12, 10, 9, 6, 4, + 3, 0]) + +# The following is from Standard ECMA-182 "Data Interchange on 12,7 mm 48-Track +# Magnetic Tape Cartridges -DLT1 Format-", December 1992. + +g64b = polyFromBits([64, 62, 57, 55, 54, 53, 52, 47, 46, 45, 40, 39, 38, 37, + 35, 33, 32, 31, 29, 27, 24, 23, 22, 21, 19, 17, 13, 12, 10, 9, 7, + 4, 1, 0]) + +#----------------------------------------------------------------------------- +# This class is used to check the CRC calculations against a direct +# implementation using polynomial division. + +class poly: + '''Class implementing polynomials over the field of integers mod 2''' + def __init__(self,p): + p = long(p) + if p < 0: raise ValueError('invalid polynomial') + self.p = p + + def __long__(self): + return self.p + + def __eq__(self,other): + return self.p == other.p + + def __ne__(self,other): + return self.p != other.p + + # To allow sorting of polynomials, use their long integer form for + # comparison + def __cmp__(self,other): + return cmp(self.p, other.p) + + def __nonzero__(self): + return self.p != 0L + + def __neg__(self): + return self # These polynomials are their own inverse under addition + + def __invert__(self): + n = max(self.deg() + 1, 1) + x = (1L << n) - 1 + return poly(self.p ^ x) + + def __add__(self,other): + return poly(self.p ^ other.p) + + def __sub__(self,other): + return poly(self.p ^ other.p) + + def __mul__(self,other): + a = self.p + b = other.p + if a == 0 or b == 0: return poly(0) + x = 0L + while b: + if b&1: + x = x ^ a + a = a<<1 + b = b>>1 + return poly(x) + + def __divmod__(self,other): + u = self.p + m = self.deg() + v = other.p + n = other.deg() + if v == 0: raise ZeroDivisionError('polynomial division by zero') + if n == 0: return (self,poly(0)) + if m < n: return (poly(0),self) + k = m-n + a = 1L << m + v = v << k + q = 0L + while k > 0: + if a & u: + u = u ^ v + q = q | 1L + q = q << 1 + a = a >> 1 + v = v >> 1 + k -= 1 + if a & u: + u = u ^ v + q = q | 1L + return (poly(q),poly(u)) + + def __div__(self,other): + return self.__divmod__(other)[0] + + def __mod__(self,other): + return self.__divmod__(other)[1] + + def __repr__(self): + return 'poly(0x%XL)' % self.p + + def __str__(self): + p = self.p + if p == 0: return '0' + lst = { 0:[], 1:['1'], 2:['x'], 3:['1','x'] }[p&3] + p = p>>2 + n = 2 + while p: + if p&1: lst.append('x^%d' % n) + p = p>>1 + n += 1 + lst.reverse() + return '+'.join(lst) + + def deg(self): + '''return the degree of the polynomial''' + a = self.p + if a == 0: return -1 + n = 0 + while a >= 0x10000L: + n += 16 + a = a >> 16 + a = int(a) + while a > 1: + n += 1 + a = a >> 1 + return n + +#----------------------------------------------------------------------------- +# The following functions compute the CRC using direct polynomial division. +# These functions are checked against the result of the table driven +# algorithms. + +g8p = poly(g8) +x8p = poly(1L<<8) +def crc8p(d): + d = map(ord, d) + p = 0L + for i in d: + p = p*256L + i + p = poly(p) + return long(p*x8p%g8p) + +g16p = poly(g16) +x16p = poly(1L<<16) +def crc16p(d): + d = map(ord, d) + p = 0L + for i in d: + p = p*256L + i + p = poly(p) + return long(p*x16p%g16p) + +g24p = poly(g24) +x24p = poly(1L<<24) +def crc24p(d): + d = map(ord, d) + p = 0L + for i in d: + p = p*256L + i + p = poly(p) + return long(p*x24p%g24p) + +g32p = poly(g32) +x32p = poly(1L<<32) +def crc32p(d): + d = map(ord, d) + p = 0L + for i in d: + p = p*256L + i + p = poly(p) + return long(p*x32p%g32p) + +g64ap = poly(g64a) +x64p = poly(1L<<64) +def crc64ap(d): + d = map(ord, d) + p = 0L + for i in d: + p = p*256L + i + p = poly(p) + return long(p*x64p%g64ap) + +g64bp = poly(g64b) +def crc64bp(d): + d = map(ord, d) + p = 0L + for i in d: + p = p*256L + i + p = poly(p) + return long(p*x64p%g64bp) + + +class KnownAnswerTests(unittest.TestCase): + test_messages = [ + 'T', + 'CatMouse987654321', + ] + + known_answers = [ + [ (g8,0,0), (0xFE, 0x9D) ], + [ (g8,-1,1), (0x4F, 0x9B) ], + [ (g8,0,1), (0xFE, 0x62) ], + [ (g16,0,0), (0x1A71, 0xE556) ], + [ (g16,-1,1), (0x1B26, 0xF56E) ], + [ (g16,0,1), (0x14A1, 0xC28D) ], + [ (g24,0,0), (0xBCC49D, 0xC4B507) ], + [ (g24,-1,1), (0x59BD0E, 0x0AAA37) ], + [ (g24,0,1), (0xD52B0F, 0x1523AB) ], + [ (g32,0,0), (0x6B93DDDB, 0x12DCA0F4) ], + [ (g32,0xFFFFFFFFL,1), (0x41FB859FL, 0xF7B400A7L) ], + [ (g32,0,1), (0x6C0695EDL, 0xC1A40EE5L) ], + [ (g32,0,1,0xFFFFFFFF), (0xBE047A60L, 0x084BFF58L) ], + ] + + def test_known_answers(self): + for crcfun_params, v in self.known_answers: + crcfun = mkCrcFun(*crcfun_params) + self.assertEqual(crcfun('',0), 0, "Wrong answer for CRC parameters %s, input ''" % (crcfun_params,)) + for i, msg in enumerate(self.test_messages): + self.assertEqual(crcfun(msg), v[i], "Wrong answer for CRC parameters %s, input '%s'" % (crcfun_params,msg)) + self.assertEqual(crcfun(msg[4:], crcfun(msg[:4])), v[i], "Wrong answer for CRC parameters %s, input '%s'" % (crcfun_params,msg)) + self.assertEqual(crcfun(msg[-1:], crcfun(msg[:-1])), v[i], "Wrong answer for CRC parameters %s, input '%s'" % (crcfun_params,msg)) + + +class CompareReferenceCrcTest(unittest.TestCase): + test_messages = [ + '', + 'T', + '123456789', + 'CatMouse987654321', + ] + + test_poly_crcs = [ + [ (g8,0,0), crc8p ], + [ (g16,0,0), crc16p ], + [ (g24,0,0), crc24p ], + [ (g32,0,0), crc32p ], + [ (g64a,0,0), crc64ap ], + [ (g64b,0,0), crc64bp ], + ] + + @staticmethod + def reference_crc32(d, crc=0): + """This function modifies the return value of binascii.crc32 + to be an unsigned 32-bit value. I.e. in the range 0 to 2**32-1.""" + # Work around the future warning on constants. + if crc > 0x7FFFFFFFL: + x = int(crc & 0x7FFFFFFFL) + crc = x | -2147483648 + x = binascii.crc32(d,crc) + return long(x) & 0xFFFFFFFFL + + def test_compare_crc32(self): + """The binascii module has a 32-bit CRC function that is used in a wide range + of applications including the checksum used in the ZIP file format. + This test compares the CRC-32 implementation of this crcmod module to + that of binascii.crc32.""" + # The following function should produce the same result as + # self.reference_crc32 which is derived from binascii.crc32. + crc32 = mkCrcFun(g32,0,1,0xFFFFFFFF) + + for msg in self.test_messages: + self.assertEqual(crc32(msg), self.reference_crc32(msg)) + + def test_compare_poly(self): + """Compare various CRCs of this crcmod module to a pure + polynomial-based implementation.""" + for crcfun_params, crc_poly_fun in self.test_poly_crcs: + # The following function should produce the same result as + # the associated polynomial CRC function. + crcfun = mkCrcFun(*crcfun_params) + + for msg in self.test_messages: + self.assertEqual(crcfun(msg), crc_poly_fun(msg)) + + +class CrcClassTest(unittest.TestCase): + """Verify the Crc class""" + + msg = 'CatMouse987654321' + + def test_simple_crc32_class(self): + """Verify the CRC class when not using xorOut""" + crc = Crc(g32) + + str_rep = \ +'''poly = 0x104C11DB7 +reverse = True +initCrc = 0xFFFFFFFF +xorOut = 0x00000000 +crcValue = 0xFFFFFFFF''' + self.assertEqual(str(crc), str_rep) + self.assertEqual(crc.digest(), '\xff\xff\xff\xff') + self.assertEqual(crc.hexdigest(), 'FFFFFFFF') + + crc.update(self.msg) + self.assertEqual(crc.crcValue, 0xF7B400A7L) + self.assertEqual(crc.digest(), '\xf7\xb4\x00\xa7') + self.assertEqual(crc.hexdigest(), 'F7B400A7') + + # Verify the .copy() method + x = crc.copy() + self.assertTrue(x is not crc) + str_rep = \ +'''poly = 0x104C11DB7 +reverse = True +initCrc = 0xFFFFFFFF +xorOut = 0x00000000 +crcValue = 0xF7B400A7''' + self.assertEqual(str(crc), str_rep) + self.assertEqual(str(x), str_rep) + + def test_full_crc32_class(self): + """Verify the CRC class when using xorOut""" + + crc = Crc(g32, initCrc=0, xorOut= ~0L) + + str_rep = \ +'''poly = 0x104C11DB7 +reverse = True +initCrc = 0x00000000 +xorOut = 0xFFFFFFFF +crcValue = 0x00000000''' + self.assertEqual(str(crc), str_rep) + self.assertEqual(crc.digest(), '\x00\x00\x00\x00') + self.assertEqual(crc.hexdigest(), '00000000') + + crc.update(self.msg) + self.assertEqual(crc.crcValue, 0x84BFF58L) + self.assertEqual(crc.digest(), '\x08\x4b\xff\x58') + self.assertEqual(crc.hexdigest(), '084BFF58') + + # Verify the .copy() method + x = crc.copy() + self.assertTrue(x is not crc) + str_rep = \ +'''poly = 0x104C11DB7 +reverse = True +initCrc = 0x00000000 +xorOut = 0xFFFFFFFF +crcValue = 0x084BFF58''' + self.assertEqual(str(crc), str_rep) + self.assertEqual(str(x), str_rep) + + # Verify the .new() method + y = crc.new() + self.assertTrue(y is not crc) + self.assertTrue(y is not x) + str_rep = \ +'''poly = 0x104C11DB7 +reverse = True +initCrc = 0x00000000 +xorOut = 0xFFFFFFFF +crcValue = 0x00000000''' + self.assertEqual(str(y), str_rep) + + +class PredefinedCrcTest(unittest.TestCase): + """Verify the predefined CRCs""" + + test_messages_for_known_answers = [ + '', # Test cases below depend on this first entry being the empty string. + 'T', + 'CatMouse987654321', + ] + + known_answers = [ + [ 'crc-aug-ccitt', (0x1D0F, 0xD6ED, 0x5637) ], + [ 'x-25', (0x0000, 0xE4D9, 0x0A91) ], + [ 'crc-32', (0x00000000, 0xBE047A60, 0x084BFF58) ], + ] + + def test_known_answers(self): + for crcfun_name, v in self.known_answers: + crcfun = mkPredefinedCrcFun(crcfun_name) + self.assertEqual(crcfun('',0), 0, "Wrong answer for CRC '%s', input ''" % crcfun_name) + for i, msg in enumerate(self.test_messages_for_known_answers): + self.assertEqual(crcfun(msg), v[i], "Wrong answer for CRC %s, input '%s'" % (crcfun_name,msg)) + self.assertEqual(crcfun(msg[4:], crcfun(msg[:4])), v[i], "Wrong answer for CRC %s, input '%s'" % (crcfun_name,msg)) + self.assertEqual(crcfun(msg[-1:], crcfun(msg[:-1])), v[i], "Wrong answer for CRC %s, input '%s'" % (crcfun_name,msg)) + + def test_class_with_known_answers(self): + for crcfun_name, v in self.known_answers: + for i, msg in enumerate(self.test_messages_for_known_answers): + crc1 = PredefinedCrc(crcfun_name) + crc1.update(msg) + self.assertEqual(crc1.crcValue, v[i], "Wrong answer for crc1 %s, input '%s'" % (crcfun_name,msg)) + + crc2 = crc1.new() + # Check that crc1 maintains its same value, after .new() call. + self.assertEqual(crc1.crcValue, v[i], "Wrong state for crc1 %s, input '%s'" % (crcfun_name,msg)) + # Check that the new class instance created by .new() contains the initialisation value. + # This depends on the first string in self.test_messages_for_known_answers being + # the empty string. + self.assertEqual(crc2.crcValue, v[0], "Wrong state for crc2 %s, input '%s'" % (crcfun_name,msg)) + + crc2.update(msg) + # Check that crc1 maintains its same value, after crc2 has called .update() + self.assertEqual(crc1.crcValue, v[i], "Wrong state for crc1 %s, input '%s'" % (crcfun_name,msg)) + # Check that crc2 contains the right value after calling .update() + self.assertEqual(crc2.crcValue, v[i], "Wrong state for crc2 %s, input '%s'" % (crcfun_name,msg)) + + def test_function_predefined_table(self): + for table_entry in _predefined_crc_definitions: + # Check predefined function + crc_func = mkPredefinedCrcFun(table_entry['name']) + calc_value = crc_func("123456789") + self.assertEqual(calc_value, table_entry['check'], "Wrong answer for CRC '%s'" % table_entry['name']) + + def test_class_predefined_table(self): + for table_entry in _predefined_crc_definitions: + # Check predefined class + crc1 = PredefinedCrc(table_entry['name']) + crc1.update("123456789") + self.assertEqual(crc1.crcValue, table_entry['check'], "Wrong answer for CRC '%s'" % table_entry['name']) + + +def runtests(): + print "Using extension:", _usingExtension + print + unittest.main() + + +if __name__ == '__main__': + runtests()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/python3/__init__.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,1 @@ +# -*- coding: utf-8 -*-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/python3/_crcfunpy.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,107 @@ +#----------------------------------------------------------------------------- +# Low level CRC functions for use by crcmod. This version is implemented in +# Python for a couple of reasons. 1) Provide a reference implememtation. +# 2) Provide a version that can be used on systems where a C compiler is not +# available for building extension modules. +# +# Copyright (c) 2009 Raymond L. Buvel +# Copyright (c) 2010 Craig McQueen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#----------------------------------------------------------------------------- + +def _get_buffer_view(in_obj): + if isinstance(in_obj, str): + raise TypeError('Unicode-objects must be encoded before calculating a CRC') + mv = memoryview(in_obj) + if mv.ndim > 1: + raise BufferError('Buffer must be single dimension') + return mv + + +def _crc8(data, crc, table): + mv = _get_buffer_view(data) + crc = crc & 0xFF + for x in mv.tobytes(): + crc = table[x ^ crc] + return crc + +def _crc8r(data, crc, table): + mv = _get_buffer_view(data) + crc = crc & 0xFF + for x in mv.tobytes(): + crc = table[x ^ crc] + return crc + +def _crc16(data, crc, table): + mv = _get_buffer_view(data) + crc = crc & 0xFFFF + for x in mv.tobytes(): + crc = table[x ^ ((crc>>8) & 0xFF)] ^ ((crc << 8) & 0xFF00) + return crc + +def _crc16r(data, crc, table): + mv = _get_buffer_view(data) + crc = crc & 0xFFFF + for x in mv.tobytes(): + crc = table[x ^ (crc & 0xFF)] ^ (crc >> 8) + return crc + +def _crc24(data, crc, table): + mv = _get_buffer_view(data) + crc = crc & 0xFFFFFF + for x in mv.tobytes(): + crc = table[x ^ (crc>>16 & 0xFF)] ^ ((crc << 8) & 0xFFFF00) + return crc + +def _crc24r(data, crc, table): + mv = _get_buffer_view(data) + crc = crc & 0xFFFFFF + for x in mv.tobytes(): + crc = table[x ^ (crc & 0xFF)] ^ (crc >> 8) + return crc + +def _crc32(data, crc, table): + mv = _get_buffer_view(data) + crc = crc & 0xFFFFFFFF + for x in mv.tobytes(): + crc = table[x ^ ((crc>>24) & 0xFF)] ^ ((crc << 8) & 0xFFFFFF00) + return crc + +def _crc32r(data, crc, table): + mv = _get_buffer_view(data) + crc = crc & 0xFFFFFFFF + for x in mv.tobytes(): + crc = table[x ^ (crc & 0xFF)] ^ (crc >> 8) + return crc + +def _crc64(data, crc, table): + mv = _get_buffer_view(data) + crc = crc & 0xFFFFFFFFFFFFFFFF + for x in mv.tobytes(): + crc = table[x ^ ((crc>>56) & 0xFF)] ^ ((crc << 8) & 0xFFFFFFFFFFFFFF00) + return crc + +def _crc64r(data, crc, table): + mv = _get_buffer_view(data) + crc = crc & 0xFFFFFFFFFFFFFFFF + for x in mv.tobytes(): + crc = table[x ^ (crc & 0xFF)] ^ (crc >> 8) + return crc +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/python3/crcmod.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,456 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2010 Raymond L. Buvel +# Copyright (c) 2010 Craig McQueen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#----------------------------------------------------------------------------- +'''crcmod is a Python module for gererating objects that compute the Cyclic +Redundancy Check. Any 8, 16, 24, 32, or 64 bit polynomial can be used. + +The following are the public components of this module. + +Crc -- a class that creates instances providing the same interface as the +algorithms in the hashlib module in the Python standard library. These +instances also provide a method for generating a C/C++ function to compute +the CRC. + +mkCrcFun -- create a Python function to compute the CRC using the specified +polynomial and initial value. This provides a much simpler interface if +all you need is a function for CRC calculation. +''' + +__all__ = '''mkCrcFun Crc'''.split() + +# Select the appropriate set of low-level CRC functions for this installation. +# If the extension module was not built, drop back to the Python implementation +# even though it is significantly slower. +try: + from . import _crcfunext as _crcfun + _usingExtension = True +except ImportError: + from . import _crcfunpy as _crcfun + _usingExtension = False + +import sys, struct + +#----------------------------------------------------------------------------- +class Crc: + '''Compute a Cyclic Redundancy Check (CRC) using the specified polynomial. + + Instances of this class have the same interface as the algorithms in the + hashlib module in the Python standard library. See the documentation of + this module for examples of how to use a Crc instance. + + The string representation of a Crc instance identifies the polynomial, + initial value, XOR out value, and the current CRC value. The print + statement can be used to output this information. + + If you need to generate a C/C++ function for use in another application, + use the generateCode method. If you need to generate code for another + language, subclass Crc and override the generateCode method. + + The following are the parameters supplied to the constructor. + + poly -- The generator polynomial to use in calculating the CRC. The value + is specified as a Python integer. The bits in this integer are the + coefficients of the polynomial. The only polynomials allowed are those + that generate 8, 16, 24, 32, or 64 bit CRCs. + + initCrc -- Initial value used to start the CRC calculation. This initial + value should be the initial shift register value XORed with the final XOR + value. That is equivalent to the CRC result the algorithm should return for + a zero-length string. Defaults to all bits set because that starting value + will take leading zero bytes into account. Starting with zero will ignore + all leading zero bytes. + + rev -- A flag that selects a bit reversed algorithm when True. Defaults to + True because the bit reversed algorithms are more efficient. + + xorOut -- Final value to XOR with the calculated CRC value. Used by some + CRC algorithms. Defaults to zero. + ''' + def __init__(self, poly, initCrc=~0, rev=True, xorOut=0, initialize=True): + if not initialize: + # Don't want to perform the initialization when using new or copy + # to create a new instance. + return + + (sizeBits, initCrc, xorOut) = _verifyParams(poly, initCrc, xorOut) + self.digest_size = sizeBits//8 + self.initCrc = initCrc + self.xorOut = xorOut + + self.poly = poly + self.reverse = rev + + (crcfun, table) = _mkCrcFun(poly, sizeBits, initCrc, rev, xorOut) + self._crc = crcfun + self.table = table + + self.crcValue = self.initCrc + + def __str__(self): + lst = [] + lst.append('poly = 0x%X' % self.poly) + lst.append('reverse = %s' % self.reverse) + fmt = '0x%%0%dX' % (self.digest_size*2) + lst.append('initCrc = %s' % (fmt % self.initCrc)) + lst.append('xorOut = %s' % (fmt % self.xorOut)) + lst.append('crcValue = %s' % (fmt % self.crcValue)) + return '\n'.join(lst) + + def new(self, arg=None): + '''Create a new instance of the Crc class initialized to the same + values as the original instance. The current CRC is set to the initial + value. If a string is provided in the optional arg parameter, it is + passed to the update method. + ''' + n = Crc(poly=None, initialize=False) + n._crc = self._crc + n.digest_size = self.digest_size + n.initCrc = self.initCrc + n.xorOut = self.xorOut + n.table = self.table + n.crcValue = self.initCrc + n.reverse = self.reverse + n.poly = self.poly + if arg is not None: + n.update(arg) + return n + + def copy(self): + '''Create a new instance of the Crc class initialized to the same + values as the original instance. The current CRC is set to the current + value. This allows multiple CRC calculations using a common initial + string. + ''' + c = self.new() + c.crcValue = self.crcValue + return c + + def update(self, data): + '''Update the current CRC value using the string specified as the data + parameter. + ''' + self.crcValue = self._crc(data, self.crcValue) + + def digest(self): + '''Return the current CRC value as a string of bytes. The length of + this string is specified in the digest_size attribute. + ''' + n = self.digest_size + crc = self.crcValue + lst = [] + while n > 0: + lst.append(crc & 0xFF) + crc = crc >> 8 + n -= 1 + lst.reverse() + return bytes(lst) + + def hexdigest(self): + '''Return the current CRC value as a string of hex digits. The length + of this string is twice the digest_size attribute. + ''' + n = self.digest_size + crc = self.crcValue + lst = [] + while n > 0: + lst.append('%02X' % (crc & 0xFF)) + crc = crc >> 8 + n -= 1 + lst.reverse() + return ''.join(lst) + + def generateCode(self, functionName, out, dataType=None, crcType=None): + '''Generate a C/C++ function. + + functionName -- String specifying the name of the function. + + out -- An open file-like object with a write method. This specifies + where the generated code is written. + + dataType -- An optional parameter specifying the data type of the input + data to the function. Defaults to UINT8. + + crcType -- An optional parameter specifying the data type of the CRC + value. Defaults to one of UINT8, UINT16, UINT32, or UINT64 depending + on the size of the CRC value. + ''' + if dataType is None: + dataType = 'UINT8' + + if crcType is None: + size = 8*self.digest_size + if size == 24: + size = 32 + crcType = 'UINT%d' % size + + if self.digest_size == 1: + # Both 8-bit CRC algorithms are the same + crcAlgor = 'table[*data ^ (%s)crc]' + elif self.reverse: + # The bit reverse algorithms are all the same except for the data + # type of the crc variable which is specified elsewhere. + crcAlgor = 'table[*data ^ (%s)crc] ^ (crc >> 8)' + else: + # The forward CRC algorithms larger than 8 bits have an extra shift + # operation to get the high byte. + shift = 8*(self.digest_size - 1) + crcAlgor = 'table[*data ^ (%%s)(crc >> %d)] ^ (crc << 8)' % shift + + fmt = '0x%%0%dX' % (2*self.digest_size) + if self.digest_size <= 4: + fmt = fmt + 'U,' + else: + # Need the long long type identifier to keep gcc from complaining. + fmt = fmt + 'ULL,' + + # Select the number of entries per row in the output code. + n = {1:8, 2:8, 3:4, 4:4, 8:2}[self.digest_size] + + lst = [] + for i, val in enumerate(self.table): + if (i % n) == 0: + lst.append('\n ') + lst.append(fmt % val) + + poly = 'polynomial: 0x%X' % self.poly + if self.reverse: + poly = poly + ', bit reverse algorithm' + + if self.xorOut: + # Need to remove the comma from the format. + preCondition = '\n crc = crc ^ %s;' % (fmt[:-1] % self.xorOut) + postCondition = preCondition + else: + preCondition = '' + postCondition = '' + + if self.digest_size == 3: + # The 24-bit CRC needs to be conditioned so that only 24-bits are + # used from the 32-bit variable. + if self.reverse: + preCondition += '\n crc = crc & 0xFFFFFFU;' + else: + postCondition += '\n crc = crc & 0xFFFFFFU;' + + + parms = { + 'dataType' : dataType, + 'crcType' : crcType, + 'name' : functionName, + 'crcAlgor' : crcAlgor % dataType, + 'crcTable' : ''.join(lst), + 'poly' : poly, + 'preCondition' : preCondition, + 'postCondition' : postCondition, + } + out.write(_codeTemplate % parms) + +#----------------------------------------------------------------------------- +def mkCrcFun(poly, initCrc=~0, rev=True, xorOut=0): + '''Return a function that computes the CRC using the specified polynomial. + + poly -- integer representation of the generator polynomial + initCrc -- default initial CRC value + rev -- when true, indicates that the data is processed bit reversed. + xorOut -- the final XOR value + + The returned function has the following user interface + def crcfun(data, crc=initCrc): + ''' + + # First we must verify the params + (sizeBits, initCrc, xorOut) = _verifyParams(poly, initCrc, xorOut) + # Make the function (and table), return the function + return _mkCrcFun(poly, sizeBits, initCrc, rev, xorOut)[0] + +#----------------------------------------------------------------------------- +# Naming convention: +# All function names ending with r are bit reverse variants of the ones +# without the r. + +#----------------------------------------------------------------------------- +# Check the polynomial to make sure that it is acceptable and return the number +# of bits in the CRC. + +def _verifyPoly(poly): + msg = 'The degree of the polynomial must be 8, 16, 24, 32 or 64' + for n in (8,16,24,32,64): + low = 1<<n + high = low*2 + if low <= poly < high: + return n + raise ValueError(msg) + +#----------------------------------------------------------------------------- +# Bit reverse the input value. + +def _bitrev(x, n): + y = 0 + for i in range(n): + y = (y << 1) | (x & 1) + x = x >> 1 + return y + +#----------------------------------------------------------------------------- +# The following functions compute the CRC for a single byte. These are used +# to build up the tables needed in the CRC algorithm. Assumes the high order +# bit of the polynomial has been stripped off. + +def _bytecrc(crc, poly, n): + mask = 1<<(n-1) + for i in range(8): + if crc & mask: + crc = (crc << 1) ^ poly + else: + crc = crc << 1 + mask = (1<<n) - 1 + crc = crc & mask + return crc + +def _bytecrc_r(crc, poly, n): + for i in range(8): + if crc & 1: + crc = (crc >> 1) ^ poly + else: + crc = crc >> 1 + mask = (1<<n) - 1 + crc = crc & mask + return crc + +#----------------------------------------------------------------------------- +# The following functions compute the table needed to compute the CRC. The +# table is returned as a list. Note that the array module does not support +# 64-bit integers on a 32-bit architecture as of Python 2.3. +# +# These routines assume that the polynomial and the number of bits in the CRC +# have been checked for validity by the caller. + +def _mkTable(poly, n): + mask = (1<<n) - 1 + poly = poly & mask + table = [_bytecrc(i<<(n-8),poly,n) for i in range(256)] + return table + +def _mkTable_r(poly, n): + mask = (1<<n) - 1 + poly = _bitrev(poly & mask, n) + table = [_bytecrc_r(i,poly,n) for i in range(256)] + return table + +#----------------------------------------------------------------------------- +# Map the CRC size onto the functions that handle these sizes. + +_sizeMap = { + 8 : [_crcfun._crc8, _crcfun._crc8r], + 16 : [_crcfun._crc16, _crcfun._crc16r], + 24 : [_crcfun._crc24, _crcfun._crc24r], + 32 : [_crcfun._crc32, _crcfun._crc32r], + 64 : [_crcfun._crc64, _crcfun._crc64r], +} + +#----------------------------------------------------------------------------- +# Build a mapping of size to struct module type code. This table is +# constructed dynamically so that it has the best chance of picking the best +# code to use for the platform we are running on. This should properly adapt +# to 32 and 64 bit machines. + +_sizeToTypeCode = {} + +for typeCode in 'B H I L Q'.split(): + size = {1:8, 2:16, 4:32, 8:64}.get(struct.calcsize(typeCode),None) + if size is not None and size not in _sizeToTypeCode: + _sizeToTypeCode[size] = '256%s' % typeCode + +_sizeToTypeCode[24] = _sizeToTypeCode[32] + +del typeCode, size + +#----------------------------------------------------------------------------- +# The following function validates the parameters of the CRC, namely, +# poly, and initial/final XOR values. +# It returns the size of the CRC (in bits), and "sanitized" initial/final XOR values. + +def _verifyParams(poly, initCrc, xorOut): + sizeBits = _verifyPoly(poly) + + mask = (1<<sizeBits) - 1 + + # Adjust the initial CRC to the correct data type (unsigned value). + initCrc = initCrc & mask + + # Similar for XOR-out value. + xorOut = xorOut & mask + + return (sizeBits, initCrc, xorOut) + +#----------------------------------------------------------------------------- +# The following function returns a Python function to compute the CRC. +# +# It must be passed parameters that are already verified & sanitized by +# _verifyParams(). +# +# The returned function calls a low level function that is written in C if the +# extension module could be loaded. Otherwise, a Python implementation is +# used. +# +# In addition to this function, a list containing the CRC table is returned. + +def _mkCrcFun(poly, sizeBits, initCrc, rev, xorOut): + if rev: + tableList = _mkTable_r(poly, sizeBits) + _fun = _sizeMap[sizeBits][1] + else: + tableList = _mkTable(poly, sizeBits) + _fun = _sizeMap[sizeBits][0] + + _table = tableList + if _usingExtension: + _table = struct.pack(_sizeToTypeCode[sizeBits], *tableList) + + if xorOut == 0: + def crcfun(data, crc=initCrc, table=_table, fun=_fun): + return fun(data, crc, table) + else: + def crcfun(data, crc=initCrc, table=_table, fun=_fun): + return xorOut ^ fun(data, xorOut ^ crc, table) + + return crcfun, tableList + +#----------------------------------------------------------------------------- +_codeTemplate = '''// Automatically generated CRC function +// %(poly)s +%(crcType)s +%(name)s(%(dataType)s *data, int len, %(crcType)s crc) +{ + static const %(crcType)s table[256] = {%(crcTable)s + }; + %(preCondition)s + while (len > 0) + { + crc = %(crcAlgor)s; + data++; + len--; + }%(postCondition)s + return crc; +} +''' +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/python3/predefined.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,162 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2010 Craig McQueen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#----------------------------------------------------------------------------- +''' +crcmod.predefined defines some well-known CRC algorithms. + +To use it, e.g.: + import crcmod.predefined + + crc32func = crcmod.predefined.mkPredefinedCrcFun("crc-32") + crc32class = crcmod.predefined.PredefinedCrc("crc-32") + +crcmod.predefined.Crc is an alias for crcmod.predefined.PredefinedCrc +But if doing 'from crc.predefined import *', only PredefinedCrc is imported. +''' + +# local imports +from . import crcmod + +__all__ = [ + 'PredefinedCrc', + 'mkPredefinedCrcFun', +] + +REVERSE = True +NON_REVERSE = False + +# The following table defines the parameters of well-known CRC algorithms. +# The "Check" value is the CRC for the ASCII byte sequence b"123456789". It +# can be used for unit tests. +_crc_definitions_table = [ +# Name Identifier-name, Poly Reverse Init-value XOR-out Check + [ 'crc-8', 'Crc8', 0x107, NON_REVERSE, 0x00, 0x00, 0xF4, ], + [ 'crc-8-darc', 'Crc8Darc', 0x139, REVERSE, 0x00, 0x00, 0x15, ], + [ 'crc-8-i-code', 'Crc8ICode', 0x11D, NON_REVERSE, 0xFD, 0x00, 0x7E, ], + [ 'crc-8-itu', 'Crc8Itu', 0x107, NON_REVERSE, 0x55, 0x55, 0xA1, ], + [ 'crc-8-maxim', 'Crc8Maxim', 0x131, REVERSE, 0x00, 0x00, 0xA1, ], + [ 'crc-8-rohc', 'Crc8Rohc', 0x107, REVERSE, 0xFF, 0x00, 0xD0, ], + [ 'crc-8-wcdma', 'Crc8Wcdma', 0x19B, REVERSE, 0x00, 0x00, 0x25, ], + + [ 'crc-16', 'Crc16', 0x18005, REVERSE, 0x0000, 0x0000, 0xBB3D, ], + [ 'crc-16-buypass', 'Crc16Buypass', 0x18005, NON_REVERSE, 0x0000, 0x0000, 0xFEE8, ], + [ 'crc-16-dds-110', 'Crc16Dds110', 0x18005, NON_REVERSE, 0x800D, 0x0000, 0x9ECF, ], + [ 'crc-16-dect', 'Crc16Dect', 0x10589, NON_REVERSE, 0x0001, 0x0001, 0x007E, ], + [ 'crc-16-dnp', 'Crc16Dnp', 0x13D65, REVERSE, 0xFFFF, 0xFFFF, 0xEA82, ], + [ 'crc-16-en-13757', 'Crc16En13757', 0x13D65, NON_REVERSE, 0xFFFF, 0xFFFF, 0xC2B7, ], + [ 'crc-16-genibus', 'Crc16Genibus', 0x11021, NON_REVERSE, 0x0000, 0xFFFF, 0xD64E, ], + [ 'crc-16-maxim', 'Crc16Maxim', 0x18005, REVERSE, 0xFFFF, 0xFFFF, 0x44C2, ], + [ 'crc-16-mcrf4xx', 'Crc16Mcrf4xx', 0x11021, REVERSE, 0xFFFF, 0x0000, 0x6F91, ], + [ 'crc-16-riello', 'Crc16Riello', 0x11021, REVERSE, 0x554D, 0x0000, 0x63D0, ], + [ 'crc-16-t10-dif', 'Crc16T10Dif', 0x18BB7, NON_REVERSE, 0x0000, 0x0000, 0xD0DB, ], + [ 'crc-16-teledisk', 'Crc16Teledisk', 0x1A097, NON_REVERSE, 0x0000, 0x0000, 0x0FB3, ], + [ 'crc-16-usb', 'Crc16Usb', 0x18005, REVERSE, 0x0000, 0xFFFF, 0xB4C8, ], + [ 'x-25', 'CrcX25', 0x11021, REVERSE, 0x0000, 0xFFFF, 0x906E, ], + [ 'xmodem', 'CrcXmodem', 0x11021, NON_REVERSE, 0x0000, 0x0000, 0x31C3, ], + [ 'modbus', 'CrcModbus', 0x18005, REVERSE, 0xFFFF, 0x0000, 0x4B37, ], + + # Note definitions of CCITT are disputable. See: + # http://homepages.tesco.net/~rainstorm/crc-catalogue.htm + # http://web.archive.org/web/20071229021252/http://www.joegeluso.com/software/articles/ccitt.htm + [ 'kermit', 'CrcKermit', 0x11021, REVERSE, 0x0000, 0x0000, 0x2189, ], + [ 'crc-ccitt-false', 'CrcCcittFalse', 0x11021, NON_REVERSE, 0xFFFF, 0x0000, 0x29B1, ], + [ 'crc-aug-ccitt', 'CrcAugCcitt', 0x11021, NON_REVERSE, 0x1D0F, 0x0000, 0xE5CC, ], + + [ 'crc-24', 'Crc24', 0x1864CFB, NON_REVERSE, 0xB704CE, 0x000000, 0x21CF02, ], + [ 'crc-24-flexray-a', 'Crc24FlexrayA', 0x15D6DCB, NON_REVERSE, 0xFEDCBA, 0x000000, 0x7979BD, ], + [ 'crc-24-flexray-b', 'Crc24FlexrayB', 0x15D6DCB, NON_REVERSE, 0xABCDEF, 0x000000, 0x1F23B8, ], + + [ 'crc-32', 'Crc32', 0x104C11DB7, REVERSE, 0x00000000, 0xFFFFFFFF, 0xCBF43926, ], + [ 'crc-32-bzip2', 'Crc32Bzip2', 0x104C11DB7, NON_REVERSE, 0x00000000, 0xFFFFFFFF, 0xFC891918, ], + [ 'crc-32c', 'Crc32C', 0x11EDC6F41, REVERSE, 0x00000000, 0xFFFFFFFF, 0xE3069283, ], + [ 'crc-32d', 'Crc32D', 0x1A833982B, REVERSE, 0x00000000, 0xFFFFFFFF, 0x87315576, ], + [ 'crc-32-mpeg', 'Crc32Mpeg', 0x104C11DB7, NON_REVERSE, 0xFFFFFFFF, 0x00000000, 0x0376E6E7, ], + [ 'posix', 'CrcPosix', 0x104C11DB7, NON_REVERSE, 0xFFFFFFFF, 0xFFFFFFFF, 0x765E7680, ], + [ 'crc-32q', 'Crc32Q', 0x1814141AB, NON_REVERSE, 0x00000000, 0x00000000, 0x3010BF7F, ], + [ 'jamcrc', 'CrcJamCrc', 0x104C11DB7, REVERSE, 0xFFFFFFFF, 0x00000000, 0x340BC6D9, ], + [ 'xfer', 'CrcXfer', 0x1000000AF, NON_REVERSE, 0x00000000, 0x00000000, 0xBD0BE338, ], + +# 64-bit +# Name Identifier-name, Poly Reverse Init-value XOR-out Check + [ 'crc-64', 'Crc64', 0x1000000000000001B, REVERSE, 0x0000000000000000, 0x0000000000000000, 0x46A5A9388A5BEFFE, ], + [ 'crc-64-we', 'Crc64We', 0x142F0E1EBA9EA3693, NON_REVERSE, 0x0000000000000000, 0xFFFFFFFFFFFFFFFF, 0x62EC59E3F1A4F00A, ], + [ 'crc-64-jones', 'Crc64Jones', 0x1AD93D23594C935A9, REVERSE, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000, 0xCAA717168609F281, ], +] + + +def _simplify_name(name): + """ + Reduce CRC definition name to a simplified form: + * lowercase + * dashes removed + * spaces removed + * any initial "CRC" string removed + """ + name = name.lower() + name = name.replace('-', '') + name = name.replace(' ', '') + if name.startswith('crc'): + name = name[len('crc'):] + return name + + +_crc_definitions_by_name = {} +_crc_definitions_by_identifier = {} +_crc_definitions = [] + +_crc_table_headings = [ 'name', 'identifier', 'poly', 'reverse', 'init', 'xor_out', 'check' ] + +for table_entry in _crc_definitions_table: + crc_definition = dict(zip(_crc_table_headings, table_entry)) + _crc_definitions.append(crc_definition) + name = _simplify_name(table_entry[0]) + if name in _crc_definitions_by_name: + raise Exception("Duplicate entry for '{0}' in CRC table".format(name)) + _crc_definitions_by_name[name] = crc_definition + _crc_definitions_by_identifier[table_entry[1]] = crc_definition + + +def _get_definition_by_name(crc_name): + definition = _crc_definitions_by_name.get(_simplify_name(crc_name), None) + if not definition: + definition = _crc_definitions_by_identifier.get(crc_name, None) + if not definition: + raise KeyError("Unkown CRC name '{0}'".format(crc_name)) + return definition + + +class PredefinedCrc(crcmod.Crc): + def __init__(self, crc_name): + definition = _get_definition_by_name(crc_name) + super().__init__(poly=definition['poly'], initCrc=definition['init'], rev=definition['reverse'], xorOut=definition['xor_out']) + + +# crcmod.predefined.Crc is an alias for crcmod.predefined.PredefinedCrc +Crc = PredefinedCrc + + +def mkPredefinedCrcFun(crc_name): + definition = _get_definition_by_name(crc_name) + return crcmod.mkCrcFun(poly=definition['poly'], initCrc=definition['init'], rev=definition['reverse'], xorOut=definition['xor_out']) + + +# crcmod.predefined.mkCrcFun is an alias for crcmod.predefined.mkPredefinedCrcFun +mkCrcFun = mkPredefinedCrcFun
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/python3/test.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,540 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2010 Raymond L. Buvel +# Copyright (c) 2010 Craig McQueen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#----------------------------------------------------------------------------- +'''Unit tests for crcmod functionality''' + + +import unittest + +from array import array +import binascii + +from .crcmod import mkCrcFun, Crc +from .crcmod import _usingExtension +from .predefined import PredefinedCrc +from .predefined import mkPredefinedCrcFun +from .predefined import _crc_definitions as _predefined_crc_definitions + + +#----------------------------------------------------------------------------- +# This polynomial was chosen because it is the product of two irreducible +# polynomials. +# g8 = (x^7+x+1)*(x+1) +g8 = 0x185 + +#----------------------------------------------------------------------------- +# The following reproduces all of the entries in the Numerical Recipes table. +# This is the standard CCITT polynomial. +g16 = 0x11021 + +#----------------------------------------------------------------------------- +g24 = 0x15D6DCB + +#----------------------------------------------------------------------------- +# This is the standard AUTODIN-II polynomial which appears to be used in a +# wide variety of standards and applications. +g32 = 0x104C11DB7 + + +#----------------------------------------------------------------------------- +# I was able to locate a couple of 64-bit polynomials on the web. To make it +# easier to input the representation, define a function that builds a +# polynomial from a list of the bits that need to be turned on. + +def polyFromBits(bits): + p = 0 + for n in bits: + p = p | (1 << n) + return p + +# The following is from the paper "An Improved 64-bit Cyclic Redundancy Check +# for Protein Sequences" by David T. Jones + +g64a = polyFromBits([64, 63, 61, 59, 58, 56, 55, 52, 49, 48, 47, 46, 44, 41, + 37, 36, 34, 32, 31, 28, 26, 23, 22, 19, 16, 13, 12, 10, 9, 6, 4, + 3, 0]) + +# The following is from Standard ECMA-182 "Data Interchange on 12,7 mm 48-Track +# Magnetic Tape Cartridges -DLT1 Format-", December 1992. + +g64b = polyFromBits([64, 62, 57, 55, 54, 53, 52, 47, 46, 45, 40, 39, 38, 37, + 35, 33, 32, 31, 29, 27, 24, 23, 22, 21, 19, 17, 13, 12, 10, 9, 7, + 4, 1, 0]) + +#----------------------------------------------------------------------------- +# This class is used to check the CRC calculations against a direct +# implementation using polynomial division. + +class poly: + '''Class implementing polynomials over the field of integers mod 2''' + def __init__(self,p): + p = int(p) + if p < 0: raise ValueError('invalid polynomial') + self.p = p + + def __int__(self): + return self.p + + def __eq__(self,other): + return self.p == other.p + + def __ne__(self,other): + return self.p != other.p + + # To allow sorting of polynomials, use their long integer form for + # comparison + def __cmp__(self,other): + return cmp(self.p, other.p) + + def __bool__(self): + return self.p != 0 + + def __neg__(self): + return self # These polynomials are their own inverse under addition + + def __invert__(self): + n = max(self.deg() + 1, 1) + x = (1 << n) - 1 + return poly(self.p ^ x) + + def __add__(self,other): + return poly(self.p ^ other.p) + + def __sub__(self,other): + return poly(self.p ^ other.p) + + def __mul__(self,other): + a = self.p + b = other.p + if a == 0 or b == 0: return poly(0) + x = 0 + while b: + if b&1: + x = x ^ a + a = a<<1 + b = b>>1 + return poly(x) + + def __divmod__(self,other): + u = self.p + m = self.deg() + v = other.p + n = other.deg() + if v == 0: raise ZeroDivisionError('polynomial division by zero') + if n == 0: return (self,poly(0)) + if m < n: return (poly(0),self) + k = m-n + a = 1 << m + v = v << k + q = 0 + while k > 0: + if a & u: + u = u ^ v + q = q | 1 + q = q << 1 + a = a >> 1 + v = v >> 1 + k -= 1 + if a & u: + u = u ^ v + q = q | 1 + return (poly(q),poly(u)) + + def __div__(self,other): + return self.__divmod__(other)[0] + + def __mod__(self,other): + return self.__divmod__(other)[1] + + def __repr__(self): + return 'poly(0x%XL)' % self.p + + def __str__(self): + p = self.p + if p == 0: return '0' + lst = { 0:[], 1:['1'], 2:['x'], 3:['1','x'] }[p&3] + p = p>>2 + n = 2 + while p: + if p&1: lst.append('x^%d' % n) + p = p>>1 + n += 1 + lst.reverse() + return '+'.join(lst) + + def deg(self): + '''return the degree of the polynomial''' + a = self.p + if a == 0: return -1 + n = 0 + while a >= 0x10000: + n += 16 + a = a >> 16 + a = int(a) + while a > 1: + n += 1 + a = a >> 1 + return n + +#----------------------------------------------------------------------------- +# The following functions compute the CRC using direct polynomial division. +# These functions are checked against the result of the table driven +# algorithms. + +g8p = poly(g8) +x8p = poly(1<<8) +def crc8p(d): + p = 0 + for i in d: + p = p*256 + i + p = poly(p) + return int(p*x8p%g8p) + +g16p = poly(g16) +x16p = poly(1<<16) +def crc16p(d): + p = 0 + for i in d: + p = p*256 + i + p = poly(p) + return int(p*x16p%g16p) + +g24p = poly(g24) +x24p = poly(1<<24) +def crc24p(d): + p = 0 + for i in d: + p = p*256 + i + p = poly(p) + return int(p*x24p%g24p) + +g32p = poly(g32) +x32p = poly(1<<32) +def crc32p(d): + p = 0 + for i in d: + p = p*256 + i + p = poly(p) + return int(p*x32p%g32p) + +g64ap = poly(g64a) +x64p = poly(1<<64) +def crc64ap(d): + p = 0 + for i in d: + p = p*256 + i + p = poly(p) + return int(p*x64p%g64ap) + +g64bp = poly(g64b) +def crc64bp(d): + p = 0 + for i in d: + p = p*256 + i + p = poly(p) + return int(p*x64p%g64bp) + + +class KnownAnswerTests(unittest.TestCase): + test_messages = [ + b'T', + b'CatMouse987654321', + ] + + known_answers = [ + [ (g8,0,0), (0xFE, 0x9D) ], + [ (g8,-1,1), (0x4F, 0x9B) ], + [ (g8,0,1), (0xFE, 0x62) ], + [ (g16,0,0), (0x1A71, 0xE556) ], + [ (g16,-1,1), (0x1B26, 0xF56E) ], + [ (g16,0,1), (0x14A1, 0xC28D) ], + [ (g24,0,0), (0xBCC49D, 0xC4B507) ], + [ (g24,-1,1), (0x59BD0E, 0x0AAA37) ], + [ (g24,0,1), (0xD52B0F, 0x1523AB) ], + [ (g32,0,0), (0x6B93DDDB, 0x12DCA0F4) ], + [ (g32,0xFFFFFFFF,1), (0x41FB859F, 0xF7B400A7) ], + [ (g32,0,1), (0x6C0695ED, 0xC1A40EE5) ], + [ (g32,0,1,0xFFFFFFFF), (0xBE047A60, 0x084BFF58) ], + ] + + def test_known_answers(self): + for crcfun_params, v in self.known_answers: + crcfun = mkCrcFun(*crcfun_params) + self.assertEqual(crcfun(b'',0), 0, "Wrong answer for CRC parameters %s, input ''" % (crcfun_params,)) + for i, msg in enumerate(self.test_messages): + self.assertEqual(crcfun(msg), v[i], "Wrong answer for CRC parameters %s, input '%s'" % (crcfun_params,msg)) + self.assertEqual(crcfun(msg[4:], crcfun(msg[:4])), v[i], "Wrong answer for CRC parameters %s, input '%s'" % (crcfun_params,msg)) + self.assertEqual(crcfun(msg[-1:], crcfun(msg[:-1])), v[i], "Wrong answer for CRC parameters %s, input '%s'" % (crcfun_params,msg)) + + +class CompareReferenceCrcTest(unittest.TestCase): + test_messages = [ + b'', + b'T', + b'123456789', + b'CatMouse987654321', + ] + + test_poly_crcs = [ + [ (g8,0,0), crc8p ], + [ (g16,0,0), crc16p ], + [ (g24,0,0), crc24p ], + [ (g32,0,0), crc32p ], + [ (g64a,0,0), crc64ap ], + [ (g64b,0,0), crc64bp ], + ] + + @staticmethod + def reference_crc32(d, crc=0): + """This function modifies the return value of binascii.crc32 + to be an unsigned 32-bit value. I.e. in the range 0 to 2**32-1.""" + # Work around the future warning on constants. + if crc > 0x7FFFFFFF: + x = int(crc & 0x7FFFFFFF) + crc = x | -2147483648 + x = binascii.crc32(d,crc) + return int(x) & 0xFFFFFFFF + + def test_compare_crc32(self): + """The binascii module has a 32-bit CRC function that is used in a wide range + of applications including the checksum used in the ZIP file format. + This test compares the CRC-32 implementation of this crcmod module to + that of binascii.crc32.""" + # The following function should produce the same result as + # self.reference_crc32 which is derived from binascii.crc32. + crc32 = mkCrcFun(g32,0,1,0xFFFFFFFF) + + for msg in self.test_messages: + self.assertEqual(crc32(msg), self.reference_crc32(msg)) + + def test_compare_poly(self): + """Compare various CRCs of this crcmod module to a pure + polynomial-based implementation.""" + for crcfun_params, crc_poly_fun in self.test_poly_crcs: + # The following function should produce the same result as + # the associated polynomial CRC function. + crcfun = mkCrcFun(*crcfun_params) + + for msg in self.test_messages: + self.assertEqual(crcfun(msg), crc_poly_fun(msg)) + + +class CrcClassTest(unittest.TestCase): + """Verify the Crc class""" + + msg = b'CatMouse987654321' + + def test_simple_crc32_class(self): + """Verify the CRC class when not using xorOut""" + crc = Crc(g32) + + str_rep = \ +'''poly = 0x104C11DB7 +reverse = True +initCrc = 0xFFFFFFFF +xorOut = 0x00000000 +crcValue = 0xFFFFFFFF''' + self.assertEqual(str(crc), str_rep) + self.assertEqual(crc.digest(), b'\xff\xff\xff\xff') + self.assertEqual(crc.hexdigest(), 'FFFFFFFF') + + crc.update(self.msg) + self.assertEqual(crc.crcValue, 0xF7B400A7) + self.assertEqual(crc.digest(), b'\xf7\xb4\x00\xa7') + self.assertEqual(crc.hexdigest(), 'F7B400A7') + + # Verify the .copy() method + x = crc.copy() + self.assertTrue(x is not crc) + str_rep = \ +'''poly = 0x104C11DB7 +reverse = True +initCrc = 0xFFFFFFFF +xorOut = 0x00000000 +crcValue = 0xF7B400A7''' + self.assertEqual(str(crc), str_rep) + self.assertEqual(str(x), str_rep) + + def test_full_crc32_class(self): + """Verify the CRC class when using xorOut""" + + crc = Crc(g32, initCrc=0, xorOut= ~0) + + str_rep = \ +'''poly = 0x104C11DB7 +reverse = True +initCrc = 0x00000000 +xorOut = 0xFFFFFFFF +crcValue = 0x00000000''' + self.assertEqual(str(crc), str_rep) + self.assertEqual(crc.digest(), b'\x00\x00\x00\x00') + self.assertEqual(crc.hexdigest(), '00000000') + + crc.update(self.msg) + self.assertEqual(crc.crcValue, 0x84BFF58) + self.assertEqual(crc.digest(), b'\x08\x4b\xff\x58') + self.assertEqual(crc.hexdigest(), '084BFF58') + + # Verify the .copy() method + x = crc.copy() + self.assertTrue(x is not crc) + str_rep = \ +'''poly = 0x104C11DB7 +reverse = True +initCrc = 0x00000000 +xorOut = 0xFFFFFFFF +crcValue = 0x084BFF58''' + self.assertEqual(str(crc), str_rep) + self.assertEqual(str(x), str_rep) + + # Verify the .new() method + y = crc.new() + self.assertTrue(y is not crc) + self.assertTrue(y is not x) + str_rep = \ +'''poly = 0x104C11DB7 +reverse = True +initCrc = 0x00000000 +xorOut = 0xFFFFFFFF +crcValue = 0x00000000''' + self.assertEqual(str(y), str_rep) + + +class PredefinedCrcTest(unittest.TestCase): + """Verify the predefined CRCs""" + + test_messages_for_known_answers = [ + b'', # Test cases below depend on this first entry being the empty string. + b'T', + b'CatMouse987654321', + ] + + known_answers = [ + [ 'crc-aug-ccitt', (0x1D0F, 0xD6ED, 0x5637) ], + [ 'x-25', (0x0000, 0xE4D9, 0x0A91) ], + [ 'crc-32', (0x00000000, 0xBE047A60, 0x084BFF58) ], + ] + + def test_known_answers(self): + for crcfun_name, v in self.known_answers: + crcfun = mkPredefinedCrcFun(crcfun_name) + self.assertEqual(crcfun(b'',0), 0, "Wrong answer for CRC '%s', input ''" % crcfun_name) + for i, msg in enumerate(self.test_messages_for_known_answers): + self.assertEqual(crcfun(msg), v[i], "Wrong answer for CRC %s, input '%s'" % (crcfun_name,msg)) + self.assertEqual(crcfun(msg[4:], crcfun(msg[:4])), v[i], "Wrong answer for CRC %s, input '%s'" % (crcfun_name,msg)) + self.assertEqual(crcfun(msg[-1:], crcfun(msg[:-1])), v[i], "Wrong answer for CRC %s, input '%s'" % (crcfun_name,msg)) + + def test_class_with_known_answers(self): + for crcfun_name, v in self.known_answers: + for i, msg in enumerate(self.test_messages_for_known_answers): + crc1 = PredefinedCrc(crcfun_name) + crc1.update(msg) + self.assertEqual(crc1.crcValue, v[i], "Wrong answer for crc1 %s, input '%s'" % (crcfun_name,msg)) + + crc2 = crc1.new() + # Check that crc1 maintains its same value, after .new() call. + self.assertEqual(crc1.crcValue, v[i], "Wrong state for crc1 %s, input '%s'" % (crcfun_name,msg)) + # Check that the new class instance created by .new() contains the initialisation value. + # This depends on the first string in self.test_messages_for_known_answers being + # the empty string. + self.assertEqual(crc2.crcValue, v[0], "Wrong state for crc2 %s, input '%s'" % (crcfun_name,msg)) + + crc2.update(msg) + # Check that crc1 maintains its same value, after crc2 has called .update() + self.assertEqual(crc1.crcValue, v[i], "Wrong state for crc1 %s, input '%s'" % (crcfun_name,msg)) + # Check that crc2 contains the right value after calling .update() + self.assertEqual(crc2.crcValue, v[i], "Wrong state for crc2 %s, input '%s'" % (crcfun_name,msg)) + + def test_function_predefined_table(self): + for table_entry in _predefined_crc_definitions: + # Check predefined function + crc_func = mkPredefinedCrcFun(table_entry['name']) + calc_value = crc_func(b"123456789") + self.assertEqual(calc_value, table_entry['check'], "Wrong answer for CRC '%s'" % table_entry['name']) + + def test_class_predefined_table(self): + for table_entry in _predefined_crc_definitions: + # Check predefined class + crc1 = PredefinedCrc(table_entry['name']) + crc1.update(b"123456789") + self.assertEqual(crc1.crcValue, table_entry['check'], "Wrong answer for CRC '%s'" % table_entry['name']) + + +class InputTypesTest(unittest.TestCase): + """Check the various input types that CRC functions can accept.""" + + msg = b'CatMouse987654321' + + check_crc_names = [ + 'crc-aug-ccitt', + 'x-25', + 'crc-32', + ] + + array_check_types = [ + 'B', + 'H', + 'I', + 'L', + ] + + def test_bytearray_input(self): + """Test that bytearray inputs are accepted, as an example + of a type that implements the buffer protocol.""" + for crc_name in self.check_crc_names: + crcfun = mkPredefinedCrcFun(crc_name) + for i in range(len(self.msg) + 1): + test_msg = self.msg[:i] + bytes_answer = crcfun(test_msg) + bytearray_answer = crcfun(bytearray(test_msg)) + self.assertEqual(bytes_answer, bytearray_answer) + + def test_array_input(self): + """Test that array inputs are accepted, as an example + of a type that implements the buffer protocol.""" + for crc_name in self.check_crc_names: + crcfun = mkPredefinedCrcFun(crc_name) + for i in range(len(self.msg) + 1): + test_msg = self.msg[:i] + bytes_answer = crcfun(test_msg) + for array_type in self.array_check_types: + if i % array(array_type).itemsize == 0: + test_array = array(array_type, test_msg) + array_answer = crcfun(test_array) + self.assertEqual(bytes_answer, array_answer) + + def test_unicode_input(self): + """Test that Unicode input raises TypeError""" + for crc_name in self.check_crc_names: + crcfun = mkPredefinedCrcFun(crc_name) + with self.assertRaises(TypeError): + crcfun("123456789") + + +def runtests(): + print("Using extension:", _usingExtension) + print() + unittest.main() + + +if __name__ == '__main__': + runtests()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cutils/crcmod/test.py Mon Jan 13 04:09:35 2025 +0100 @@ -0,0 +1,16 @@ +# -*- coding: utf-8; -*- +from __future__ import absolute_import + +import sys + +if sys.version_info[0] < 3: + from .python2.test import * +else: + from .python3.test import * + + +del sys + + +if __name__ == '__main__': + runtests()
