blob: e46ab9c66941e7f5dd66081431524d5a8a7ccd37 [file] [log] [blame]
Brian Silverman84b22232019-01-25 20:29:29 -08001# 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"""
25Symbol table for the macro processor used by pycrc.
26use 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
38from pycrc.algorithms import Crc
39import collections
40import time
41import os
42
43
44class SymbolTable(object):
45 def __init__(self, opt):
46 self.opt = opt
47
48
49class 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
139def _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
148def _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
161def _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
170def _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
183def _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
196def _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
208def _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
242def _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
283def _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
302def _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
341def _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