Brian Silverman | 84b2223 | 2019-01-25 20:29:29 -0800 | [diff] [blame^] | 1 | # pycrc -- parameterisable CRC calculation utility and C source code generator |
| 2 | # |
| 3 | # Copyright (c) 2006-2017 Thomas Pircher <tehpeh-web@tty1.net> |
| 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 |
| 7 | # deal in the Software without restriction, including without limitation the |
| 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| 9 | # sell 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 |
| 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 21 | # IN THE SOFTWARE. |
| 22 | |
| 23 | |
| 24 | """ |
| 25 | Symbol table for the macro processor used by pycrc. |
| 26 | use as follows: |
| 27 | |
| 28 | import pycrc.opt as opt |
| 29 | import pycrc.symtable as sym |
| 30 | |
| 31 | opt = opt.Options() |
| 32 | sym = sym.SymbolTable(opt) |
| 33 | |
| 34 | print(sym['crc_width']) |
| 35 | print('width: {crc_width}, poly: {crc_poly}'.format(**sym)) |
| 36 | """ |
| 37 | |
| 38 | from pycrc.algorithms import Crc |
| 39 | import collections |
| 40 | import time |
| 41 | import os |
| 42 | |
| 43 | |
| 44 | class SymbolTable(object): |
| 45 | def __init__(self, opt): |
| 46 | self.opt = opt |
| 47 | |
| 48 | |
| 49 | class SymbolTable(collections.MutableMapping): |
| 50 | """A dictionary that applies an arbitrary key-altering |
| 51 | function before accessing the keys""" |
| 52 | |
| 53 | def __init__(self, opt): |
| 54 | self.opt = opt |
| 55 | self.tbl_shift = _tbl_shift(opt) |
| 56 | self.cache = dict() |
| 57 | self.generator = dict({ |
| 58 | 'datetime': lambda: time.asctime(), |
| 59 | 'program_version': lambda: self.opt.version_str, |
| 60 | 'program_url': lambda: self.opt.web_address, |
| 61 | 'filename': lambda: 'pycrc_stdout' if self.opt.output_file is None else os.path.basename(self.opt.output_file), |
| 62 | 'header_filename': lambda: _pretty_header_filename(self.opt.output_file), |
| 63 | 'header_protection': lambda: _pretty_hdrprotection(self.opt), |
| 64 | |
| 65 | 'crc_algorithm': lambda: _pretty_algorithm(self.opt), |
| 66 | 'crc_width': lambda: _pretty_str(self.opt.width), |
| 67 | 'crc_poly': lambda: _pretty_hex(self.opt.poly, self.opt.width), |
| 68 | 'crc_reflect_in': lambda: _pretty_bool(self.opt.reflect_in), |
| 69 | 'crc_xor_in': lambda: _pretty_hex(self.opt.xor_in, self.opt.width), |
| 70 | 'crc_reflect_out': lambda: _pretty_bool(self.opt.reflect_out), |
| 71 | 'crc_xor_out': lambda: _pretty_hex(self.opt.xor_out, self.opt.width), |
| 72 | 'crc_slice_by': lambda: _pretty_str(self.opt.slice_by), |
| 73 | 'crc_table_idx_width': lambda: str(self.opt.tbl_idx_width), |
| 74 | 'crc_table_width': lambda: _pretty_str(1 << self.opt.tbl_idx_width), |
| 75 | 'crc_table_mask': lambda: _pretty_hex(self.opt.tbl_width - 1, 8), |
| 76 | 'crc_mask': lambda: _pretty_hex(self.opt.mask, self.opt.width), |
| 77 | 'crc_msb_mask': lambda: _pretty_hex(self.opt.msb_mask, self.opt.width), |
| 78 | 'crc_shift': lambda: _pretty_str(self.tbl_shift), |
| 79 | |
| 80 | 'cfg_width': lambda: self.__getitem__('crc_width') if self.opt.width is not None else 'cfg->width', |
| 81 | 'cfg_poly': lambda: self.__getitem__('crc_poly') if self.opt.poly is not None else 'cfg->poly', |
| 82 | 'cfg_reflect_in': lambda: self.__getitem__('crc_reflect_in') if self.opt.reflect_in is not None else 'cfg->reflect_in', |
| 83 | 'cfg_xor_in': lambda: self.__getitem__('crc_xor_in') if self.opt.xor_in is not None else 'cfg->xor_in', |
| 84 | 'cfg_reflect_out': lambda: self.__getitem__('crc_reflect_out') if self.opt.reflect_out is not None else 'cfg->reflect_out', |
| 85 | 'cfg_xor_out': lambda: self.__getitem__('crc_xor_out') if self.opt.xor_out is not None else 'cfg->xor_out', |
| 86 | 'cfg_table_idx_width': lambda: self.__getitem__('crc_table_idx_width') if self.opt.tbl_idx_width is not None else 'cfg->table_idx_width', |
| 87 | 'cfg_table_width': lambda: self.__getitem__('crc_table_width') if self.opt.tbl_width is not None else 'cfg->table_width', |
| 88 | 'cfg_mask': lambda: self.__getitem__('crc_mask') if self.opt.mask is not None else 'cfg->crc_mask', |
| 89 | 'cfg_msb_mask': lambda: self.__getitem__('crc_msb_mask') if self.opt.msb_mask is not None else 'cfg->msb_mask', |
| 90 | 'cfg_shift': lambda: self.__getitem__('crc_shift') if self.tbl_shift is not None else 'cfg->crc_shift', |
| 91 | 'cfg_poly_shifted': lambda: '('+self.__getitem__('cfg_poly')+' << '+self.__getitem__('cfg_shift')+')' if self.tbl_shift is None or self.tbl_shift > 0 else self.__getitem__('cfg_poly'), |
| 92 | 'cfg_mask_shifted': lambda: '('+self.__getitem__('cfg_mask')+' << '+self.__getitem__('cfg_shift')+')' if self.tbl_shift is None or self.tbl_shift > 0 else self.__getitem__('cfg_mask'), |
| 93 | 'cfg_msb_mask_shifted': lambda: '('+self.__getitem__('cfg_msb_mask')+' << '+self.__getitem__('cfg_shift')+')' if self.tbl_shift is None or self.tbl_shift > 0 else self.__getitem__('cfg_msb_mask'), |
| 94 | |
| 95 | 'c_bool': lambda: 'int' if self.opt.c_std == 'C89' else 'bool', |
| 96 | 'c_true': lambda: '1' if self.opt.c_std == 'C89' else 'true', |
| 97 | 'c_false': lambda: '0' if self.opt.c_std == 'C89' else 'false', |
| 98 | |
| 99 | 'underlying_crc_t': lambda: _get_underlying_crc_t(self.opt), |
| 100 | 'crc_t': lambda: self.opt.symbol_prefix + 't', |
| 101 | 'cfg_t': lambda: self.opt.symbol_prefix + 'cfg_t', |
| 102 | 'crc_reflect_function': lambda: self.opt.symbol_prefix + 'reflect', |
| 103 | 'crc_table_gen_function': lambda: self.opt.symbol_prefix + 'table_gen', |
| 104 | 'crc_init_function': lambda: self.opt.symbol_prefix + 'init', |
| 105 | 'crc_update_function': lambda: self.opt.symbol_prefix + 'update', |
| 106 | 'crc_finalize_function': lambda: self.opt.symbol_prefix + 'finalize', |
| 107 | |
| 108 | 'crc_init_value': lambda: _get_init_value(self.opt), |
| 109 | 'crc_table_init': lambda: _get_table_init(self.opt), |
| 110 | }) |
| 111 | |
| 112 | def __getitem__(self, key): |
| 113 | """ |
| 114 | Return the value for the requested key |
| 115 | """ |
| 116 | if key in self.cache: |
| 117 | return self.cache[key] |
| 118 | if key not in self.generator: |
| 119 | raise KeyError(key) |
| 120 | generator = self.generator[key] |
| 121 | val = self.generator[key]() |
| 122 | self.cache[key] = val |
| 123 | return val |
| 124 | |
| 125 | def __setitem__(self, key, value): |
| 126 | self.generator[key] = value |
| 127 | |
| 128 | def __delitem__(self, key): |
| 129 | del self.generator[key] |
| 130 | |
| 131 | def __iter__(self): |
| 132 | return iter(self.generator) |
| 133 | |
| 134 | def __len__(self): |
| 135 | return len(self.generator) |
| 136 | |
| 137 | |
| 138 | |
| 139 | def _pretty_str(value): |
| 140 | """ |
| 141 | Return a value of width bits as a pretty string. |
| 142 | """ |
| 143 | if value is None: |
| 144 | return 'Undefined' |
| 145 | return str(value) |
| 146 | |
| 147 | |
| 148 | def _pretty_hex(value, width=None): |
| 149 | """ |
| 150 | Return a value of width bits as a pretty hexadecimal formatted string. |
| 151 | """ |
| 152 | if value is None: |
| 153 | return 'Undefined' |
| 154 | if width is None: |
| 155 | return '{0:#x}'.format(value) |
| 156 | width = (width + 3) // 4 |
| 157 | hex_str = "{{0:#0{0:d}x}}".format(width + 2) |
| 158 | return hex_str.format(value) |
| 159 | |
| 160 | |
| 161 | def _pretty_bool(value): |
| 162 | """ |
| 163 | Return a boolen value of width bits as a pretty formatted string. |
| 164 | """ |
| 165 | if value is None: |
| 166 | return 'Undefined' |
| 167 | return 'True' if value else 'False' |
| 168 | |
| 169 | |
| 170 | def _pretty_algorithm(opt): |
| 171 | """ |
| 172 | Return the algorithm name. |
| 173 | """ |
| 174 | if opt.algorithm == opt.algo_bit_by_bit: |
| 175 | return 'bit-by-bit' |
| 176 | elif opt.algorithm == opt.algo_bit_by_bit_fast: |
| 177 | return 'bit-by-bit-fast' |
| 178 | elif opt.algorithm == opt.algo_table_driven: |
| 179 | return 'table-driven' |
| 180 | else: |
| 181 | return 'UNDEFINED' |
| 182 | |
| 183 | def _pretty_header_filename(filename): |
| 184 | """ |
| 185 | Return the sanitized filename of a header file. |
| 186 | """ |
| 187 | if filename is None: |
| 188 | return 'pycrc_stdout.h' |
| 189 | filename = os.path.basename(filename) |
| 190 | if filename[-2:] == '.c': |
| 191 | return filename[0:-1] + 'h' |
| 192 | else: |
| 193 | return filename + '.h' |
| 194 | |
| 195 | |
| 196 | def _pretty_hdrprotection(opt): |
| 197 | """ |
| 198 | Return the name of a C header protection (e.g. CRC_IMPLEMENTATION_H). |
| 199 | """ |
| 200 | if opt.output_file is None: |
| 201 | filename = 'pycrc_stdout' |
| 202 | else: |
| 203 | filename = os.path.basename(opt.output_file) |
| 204 | out_str = ''.join([s.upper() if s.isalnum() else '_' for s in filename]) |
| 205 | return out_str |
| 206 | |
| 207 | |
| 208 | def _get_underlying_crc_t(opt): |
| 209 | """ |
| 210 | Return the C type of the crc_t typedef. |
| 211 | """ |
| 212 | # pylint: disable=too-many-return-statements, too-many-branches |
| 213 | |
| 214 | if opt.crc_type is not None: |
| 215 | return opt.crc_type |
| 216 | if opt.c_std == 'C89': |
| 217 | if opt.width is None: |
| 218 | return 'unsigned long int' |
| 219 | if opt.width <= 8: |
| 220 | return 'unsigned char' |
| 221 | elif opt.width <= 16: |
| 222 | return 'unsigned int' |
| 223 | else: |
| 224 | return 'unsigned long int' |
| 225 | else: # C99 |
| 226 | if opt.width is None: |
| 227 | return 'unsigned long long int' |
| 228 | if opt.width <= 8: |
| 229 | return 'uint_fast8_t' |
| 230 | elif opt.width <= 16: |
| 231 | return 'uint_fast16_t' |
| 232 | elif opt.width <= 32: |
| 233 | return 'uint_fast32_t' |
| 234 | elif opt.width <= 64: |
| 235 | return 'uint_fast64_t' |
| 236 | elif opt.width <= 128: |
| 237 | return 'uint_fast128_t' |
| 238 | else: |
| 239 | return 'uintmax_t' |
| 240 | |
| 241 | |
| 242 | def _get_init_value(opt): |
| 243 | """ |
| 244 | Return the init value of a C implementation, according to the selected |
| 245 | algorithm and to the given options. |
| 246 | If no default option is given for a given parameter, value in the cfg_t |
| 247 | structure must be used. |
| 248 | """ |
| 249 | if opt.algorithm == opt.algo_bit_by_bit: |
| 250 | if opt.xor_in is None or opt.width is None or opt.poly is None: |
| 251 | return None |
| 252 | crc = Crc( |
| 253 | width=opt.width, poly=opt.poly, |
| 254 | reflect_in=opt.reflect_in, xor_in=opt.xor_in, |
| 255 | reflect_out=opt.reflect_out, xor_out=opt.xor_out, |
| 256 | table_idx_width=opt.tbl_idx_width) |
| 257 | init = crc.nondirect_init |
| 258 | elif opt.algorithm == opt.algo_bit_by_bit_fast: |
| 259 | if opt.xor_in is None: |
| 260 | return None |
| 261 | init = opt.xor_in |
| 262 | elif opt.algorithm == opt.algo_table_driven: |
| 263 | if opt.reflect_in is None or opt.xor_in is None or opt.width is None: |
| 264 | return None |
| 265 | if opt.poly is None: |
| 266 | poly = 0 |
| 267 | else: |
| 268 | poly = opt.poly |
| 269 | crc = Crc( |
| 270 | width=opt.width, poly=poly, |
| 271 | reflect_in=opt.reflect_in, xor_in=opt.xor_in, |
| 272 | reflect_out=opt.reflect_out, xor_out=opt.xor_out, |
| 273 | table_idx_width=opt.tbl_idx_width) |
| 274 | if opt.reflect_in: |
| 275 | init = crc.reflect(crc.direct_init, opt.width) |
| 276 | else: |
| 277 | init = crc.direct_init |
| 278 | else: |
| 279 | init = 0 |
| 280 | return _pretty_hex(init, opt.width) |
| 281 | |
| 282 | |
| 283 | def _get_simple_table(opt, crc_tbl, values_per_line, format_width, indent): |
| 284 | """ |
| 285 | Get one CRC table, formatted as string with appropriate indenting and |
| 286 | line breaks. |
| 287 | """ |
| 288 | out = "" |
| 289 | for i in range(opt.tbl_width): |
| 290 | if i % values_per_line == 0: |
| 291 | out += " " * indent |
| 292 | tbl_val = _pretty_hex(crc_tbl[i], format_width) |
| 293 | if i == (opt.tbl_width - 1): |
| 294 | out += "{0:s}".format(tbl_val) |
| 295 | elif i % values_per_line == (values_per_line - 1): |
| 296 | out += "{0:s},\n".format(tbl_val) |
| 297 | else: |
| 298 | out += "{0:s}, ".format(tbl_val) |
| 299 | return out |
| 300 | |
| 301 | |
| 302 | def _get_table_init(opt): # TODO: change to return a list |
| 303 | """ |
| 304 | Return the precalculated CRC table for the table_driven implementation. |
| 305 | """ |
| 306 | if opt.algorithm != opt.algo_table_driven: |
| 307 | return "0" |
| 308 | if opt.width is None or opt.poly is None or opt.reflect_in is None: |
| 309 | return "0" |
| 310 | crc = Crc( |
| 311 | width=opt.width, poly=opt.poly, |
| 312 | reflect_in=opt.reflect_in, |
| 313 | xor_in=0, reflect_out=False, xor_out=0, # set unimportant variables to known values |
| 314 | table_idx_width=opt.tbl_idx_width, |
| 315 | slice_by=opt.slice_by) |
| 316 | crc_tbl = crc.gen_table() |
| 317 | if opt.width > 32: |
| 318 | values_per_line = 4 |
| 319 | elif opt.width >= 16: |
| 320 | values_per_line = 8 |
| 321 | else: |
| 322 | values_per_line = 16 |
| 323 | format_width = max(opt.width, 8) |
| 324 | if opt.slice_by == 1: |
| 325 | indent = 4 |
| 326 | else: |
| 327 | indent = 8 |
| 328 | |
| 329 | out = [''] * opt.slice_by |
| 330 | for i in range(opt.slice_by): |
| 331 | out[i] = _get_simple_table(opt, crc_tbl[i], values_per_line, format_width, indent) |
| 332 | fixed_indent = ' ' * (indent - 4) |
| 333 | out = '{0:s}{{\n'.format(fixed_indent) + \ |
| 334 | '\n{0:s}}},\n{0:s}{{\n'.format(fixed_indent).join(out) + \ |
| 335 | '\n{0:s}}}'.format(fixed_indent) |
| 336 | if opt.slice_by == 1: |
| 337 | return out |
| 338 | return '{\n' + out + '\n}' |
| 339 | |
| 340 | |
| 341 | def _tbl_shift(opt): |
| 342 | """ |
| 343 | Return the table shift value |
| 344 | """ |
| 345 | if opt.algorithm == opt.algo_table_driven and (opt.width is None or opt.width < 8): |
| 346 | if opt.width is None: |
| 347 | return None |
| 348 | else: |
| 349 | return 8 - opt.width |
| 350 | else: |
| 351 | return 0 |
| 352 | |