Squashed 'third_party/pycrc/' content from commit cb91196b9
Change-Id: Iaed6f7d683e3c11395f10f0724f973363aad2cdb
git-subtree-dir: third_party/pycrc
git-subtree-split: cb91196b920d1f892c05941ed470c7a80cba7596
diff --git a/pycrc/opt.py b/pycrc/opt.py
new file mode 100644
index 0000000..7868b2e
--- /dev/null
+++ b/pycrc/opt.py
@@ -0,0 +1,484 @@
+# pycrc -- parameterisable CRC calculation utility and C source code generator
+#
+# Copyright (c) 2006-2017 Thomas Pircher <tehpeh-web@tty1.net>
+#
+# 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.
+
+
+"""
+Option parsing library for pycrc.
+use as follows:
+
+ from pycrc.opt import Options
+
+ opt = Options()
+ opt.parse(sys.argv[1:])
+"""
+
+from optparse import OptionParser, Option, OptionValueError
+from copy import copy
+import sys
+from pycrc.models import CrcModels
+
+
+class Options(object):
+ """
+ The options parsing and validating class.
+ """
+ # pylint: disable=too-many-instance-attributes, too-few-public-methods
+
+ # Bitmap of the algorithms
+ algo_none = 0x00
+ algo_bit_by_bit = 0x01
+ algo_bit_by_bit_fast = 0x02
+ algo_table_driven = 0x08
+
+ action_check_str = 0x01
+ action_check_hex_str = 0x02
+ action_check_file = 0x03
+ action_generate_h = 0x04
+ action_generate_c = 0x05
+ action_generate_c_main = 0x06
+ action_generate_table = 0x07
+
+
+ def __init__(self, progname='pycrc', version=None, url=None):
+ self.program_name = progname
+ self.version = version
+ self.version_str = "{0:s} v{1:s}".format(progname, version)
+ self.web_address = url
+
+ self.width = None
+ self.poly = None
+ self.reflect_in = None
+ self.xor_in = None
+ self.reflect_out = None
+ self.xor_out = None
+ self.tbl_idx_width = 8
+ self.tbl_width = 1 << self.tbl_idx_width
+ self.slice_by = 1
+ self.verbose = False
+ self.check_string = "123456789"
+ self.msb_mask = None
+ self.mask = None
+
+ self.algorithm = self.algo_none
+ self.symbol_prefix = "crc_"
+ self.crc_type = None
+ self.include_files = []
+ self.output_file = None
+ self.action = self.action_check_str
+ self.check_file = None
+ self.c_std = None
+ self.undefined_crc_parameters = False
+
+
+ def parse(self, argv=None):
+ """
+ Parses and validates the options given as arguments
+ """
+ # pylint: disable=too-many-branches, too-many-statements
+
+ usage = """python %prog [OPTIONS]
+
+To calculate the checksum of a string or hexadecimal data:
+ python %prog [model] --check-string "123456789"
+ python %prog [model] --check-hexstring "313233343536373839"
+
+To calculate the checksum of a file:
+ python %prog [model] --check-file filename
+
+To generate the C source code and write it to filename:
+ python %prog [model] --generate c -o filename
+
+The model can be defined either with the --model switch or by specifying each
+of the following parameters:
+ --width --poly --reflect-in --xor-in --reflect-out --xor-out"""
+
+ models = CrcModels()
+ model_list = ", ".join(models.names())
+ parser = OptionParser(option_class=MyOption, usage=usage, version=self.version_str)
+ parser.add_option(
+ "-v", "--verbose",
+ action="store_true", dest="verbose", default=False,
+ help="be more verbose; print the value of the parameters "
+ "and the chosen model to stdout")
+ parser.add_option(
+ "--check-string",
+ action="store", type="string", dest="check_string",
+ help="calculate the checksum of a string (default: '123456789')",
+ metavar="STRING")
+ parser.add_option(
+ "--check-hexstring",
+ action="store", type="string", dest="check_hexstring",
+ help="calculate the checksum of a hexadecimal number string",
+ metavar="STRING")
+ parser.add_option(
+ "--check-file",
+ action="store", type="string", dest="check_file",
+ help="calculate the checksum of a file",
+ metavar="FILE")
+ parser.add_option(
+ "--generate",
+ action="store", type="string", dest="generate", default=None,
+ help="generate C source code; choose the type from {h, c, c-main, table}",
+ metavar="CODE")
+ parser.add_option(
+ "--std",
+ action="store", type="string", dest="c_std", default="C99",
+ help="choose the C dialect of the generated code from {C89, ANSI, C99}",
+ metavar="STD")
+ parser.add_option(
+ "--algorithm",
+ action="store", type="string", dest="algorithm", default="all",
+ help="choose an algorithm from "
+ "{bit-by-bit, bbb, bit-by-bit-fast, bbf, table-driven, tbl, all}",
+ metavar="ALGO")
+ parser.add_option(
+ "--model",
+ action="callback", callback=_model_cb, type="string", dest="model", default=None,
+ help="choose a parameter set from {{{0:s}}}".format(model_list),
+ metavar="MODEL")
+ parser.add_option(
+ "--width",
+ action="store", type="hex", dest="width",
+ help="use NUM bits in the polynomial",
+ metavar="NUM")
+ parser.add_option(
+ "--poly",
+ action="store", type="hex", dest="poly",
+ help="use HEX as polynomial",
+ metavar="HEX")
+ parser.add_option(
+ "--reflect-in",
+ action="store", type="bool", dest="reflect_in",
+ help="reflect the octets in the input message",
+ metavar="BOOL")
+ parser.add_option(
+ "--xor-in",
+ action="store", type="hex", dest="xor_in",
+ help="use HEX as initial value",
+ metavar="HEX")
+ parser.add_option(
+ "--reflect-out",
+ action="store", type="bool", dest="reflect_out",
+ help="reflect the resulting checksum before applying the --xor-out value",
+ metavar="BOOL")
+ parser.add_option(
+ "--xor-out",
+ action="store", type="hex", dest="xor_out",
+ help="xor the final CRC value with HEX",
+ metavar="HEX")
+ parser.add_option(
+ "--slice-by",
+ action="store", type="int", dest="slice_by",
+ help="read NUM bytes at a time from the input. NUM must be one of the values {4, 8, 16}",
+ metavar="NUM")
+ parser.add_option(
+ "--table-idx-width",
+ action="store", type="int", dest="table_idx_width",
+ help="use NUM bits to index the CRC table; NUM must be one of the values {1, 2, 4, 8}",
+ metavar="NUM")
+ parser.add_option(
+ "--force-poly",
+ action="store_true", dest="force_poly", default=False,
+ help="override any errors about possibly unsuitable polynoms")
+ parser.add_option(
+ "--symbol-prefix",
+ action="store", type="string", dest="symbol_prefix",
+ help="when generating source code, use STRING as prefix to the exported C symbols",
+ metavar="STRING")
+ parser.add_option(
+ "--crc-type",
+ action="store", type="string", dest="crc_type",
+ help="when generating source code, use STRING as crc_t type",
+ metavar="STRING")
+ parser.add_option(
+ "--include-file",
+ action="append", type="string", dest="include_files",
+ help="when generating source code, include also FILE as header file; "
+ "can be specified multiple times",
+ metavar="FILE")
+ parser.add_option(
+ "-o", "--output",
+ action="store", type="string", dest="output_file",
+ help="write the generated code to file instead to stdout",
+ metavar="FILE")
+
+ (options, args) = parser.parse_args(argv)
+
+ if options.c_std != None:
+ std = options.c_std.upper()
+ if std == "ANSI" or std == "C89":
+ self.c_std = "C89"
+ elif std == "C99":
+ self.c_std = std
+ else:
+ self.__error("unknown C standard {0:s}".format(options.c_std))
+
+ undefined_params = []
+ if options.width != None:
+ self.width = options.width
+ else:
+ undefined_params.append("--width")
+ if options.poly != None:
+ self.poly = options.poly
+ else:
+ undefined_params.append("--poly")
+ if options.reflect_in != None:
+ self.reflect_in = options.reflect_in
+ else:
+ undefined_params.append("--reflect-in")
+ if options.xor_in != None:
+ self.xor_in = options.xor_in
+ else:
+ undefined_params.append("--xor-in")
+ if options.reflect_out != None:
+ self.reflect_out = options.reflect_out
+ else:
+ undefined_params.append("--reflect-out")
+ if options.xor_out != None:
+ self.xor_out = options.xor_out
+ else:
+ undefined_params.append("--xor-out")
+
+ if options.table_idx_width != None:
+ if options.table_idx_width in set((1, 2, 4, 8)):
+ self.tbl_idx_width = options.table_idx_width
+ self.tbl_width = 1 << options.table_idx_width
+ else:
+ self.__error("unsupported table-idx-width {0:d}".format(options.table_idx_width))
+
+ if self.poly != None and self.poly % 2 == 0 and not options.force_poly:
+ self.__error("even polinomials are not allowed by default. Use --force-poly to override this.")
+
+ if self.width != None:
+ if self.width <= 0:
+ self.__error("Width must be strictly positive")
+ self.msb_mask = 0x1 << (self.width - 1)
+ self.mask = ((self.msb_mask - 1) << 1) | 1
+ if self.poly != None and self.poly >> (self.width + 1) != 0 and not options.force_poly:
+ self.__error("the polynomial is wider than the supplied Width. Use --force-poly to override this.")
+ if self.poly != None:
+ self.poly = self.poly & self.mask
+ if self.xor_in != None:
+ self.xor_in = self.xor_in & self.mask
+ if self.xor_out != None:
+ self.xor_out = self.xor_out & self.mask
+ else:
+ self.msb_mask = None
+ self.mask = None
+
+ if self.width == None or \
+ self.poly == None or \
+ self.reflect_in == None or \
+ self.xor_in == None or \
+ self.reflect_out == None or \
+ self.xor_out == None:
+ self.undefined_crc_parameters = True
+ else:
+ self.undefined_crc_parameters = False
+
+ if options.slice_by != None:
+ if options.slice_by in set((4, 8, 16)):
+ self.slice_by = options.slice_by
+ else:
+ self.__error("unsupported slice-by {0:d}".format(options.slice_by))
+ if self.undefined_crc_parameters:
+ self.__error("slice-by is only implemented for fully defined models")
+ if self.tbl_idx_width != 8:
+ self.__error("slice-by is only implemented for table-idx-width=8")
+ # FIXME tp: Fix corner cases and disable the following tests
+ if self.width < 8:
+ self.__warning("disabling slice-by for width {0}".format(self.width))
+ self.slice_by = 1
+ if self.width < 16:
+ self.__warning("disabling slice-by for width {0}".format(self.width))
+ self.slice_by = 1
+ if self.width > 32:
+ self.__warning("disabling slice-by for width {0}".format(self.width))
+ self.slice_by = 1
+ if not self.reflect_in:
+ self.__warning("disabling slice-by for non-reflected algorithm")
+ self.slice_by = 1
+# FIXME tp: reintroduce this?
+# if self.width % 8 != 0:
+# self.__error("slice-by is only implemented for width multiples of 8")
+# if options.slice_by < self.width / 8:
+# self.__error("slice-by must be greater or equal width / 8")
+ if self.c_std == "C89":
+ self.__error("--slice-by not supported for C89")
+
+ if options.algorithm != None:
+ alg = options.algorithm.lower()
+ if alg in set(["bit-by-bit", "bbb", "all"]):
+ self.algorithm |= self.algo_bit_by_bit
+ if alg in set(["bit-by-bit-fast", "bbf", "all"]):
+ self.algorithm |= self.algo_bit_by_bit_fast
+ if alg in set(["table-driven", "tbl", "all"]):
+ self.algorithm |= self.algo_table_driven
+ if self.algorithm == 0:
+ self.__error("unknown algorithm {0:s}".format(options.algorithm))
+
+ if options.symbol_prefix != None:
+ self.symbol_prefix = options.symbol_prefix
+ if options.include_files != None:
+ self.include_files = options.include_files
+ if options.crc_type != None:
+ self.crc_type = options.crc_type
+ if options.output_file != None:
+ self.output_file = options.output_file
+ op_count = 0
+ if options.check_string != None:
+ self.action = self.action_check_str
+ self.check_string = options.check_string
+ op_count += 1
+ if options.check_hexstring != None:
+ self.action = self.action_check_hex_str
+ self.check_string = options.check_hexstring
+ op_count += 1
+ if options.check_file != None:
+ self.action = self.action_check_file
+ self.check_file = options.check_file
+ op_count += 1
+ if options.generate != None:
+ arg = options.generate.lower()
+ if arg == 'h':
+ self.action = self.action_generate_h
+ elif arg == 'c':
+ self.action = self.action_generate_c
+ elif arg == 'c-main':
+ self.action = self.action_generate_c_main
+ elif arg == 'table':
+ self.action = self.action_generate_table
+ else:
+ self.__error("don't know how to generate {0:s}".format(options.generate))
+ op_count += 1
+
+ if self.action == self.action_generate_table:
+ if self.algorithm & self.algo_table_driven == 0:
+ self.__error("the --generate table option is incompatible "
+ "with the --algorithm option")
+ self.algorithm = self.algo_table_driven
+ elif self.algorithm not in set(
+ [self.algo_bit_by_bit, self.algo_bit_by_bit_fast, self.algo_table_driven]):
+ self.__error("select an algorithm to be used in the generated file")
+ else:
+ if self.tbl_idx_width != 8:
+ self.__warning("reverting to Table Index Width = 8 "
+ "for internal CRC calculation")
+ self.tbl_idx_width = 8
+ self.tbl_width = 1 << options.table_idx_width
+ if op_count == 0:
+ self.action = self.action_check_str
+ if op_count > 1:
+ self.__error("too many actions specified")
+
+ if len(args) != 0:
+ self.__error("unrecognized argument(s): {0:s}".format(" ".join(args)))
+
+ def_params_acts = (self.action_check_str, self.action_check_hex_str,
+ self.action_check_file, self.action_generate_table)
+ if self.undefined_crc_parameters and self.action in set(def_params_acts):
+ self.__error("undefined parameters: Add {0:s} or use --model"
+ .format(", ".join(undefined_params)))
+ self.verbose = options.verbose
+
+
+
+ def __warning(self, message):
+ """
+ Print a warning message to stderr.
+ """
+ sys.stderr.write(
+ "{0:s}: warning: {1:s}\n".format(self.program_name, message))
+
+
+
+ def __error(self, message):
+ """
+ Print a error message to stderr and terminate the program.
+ """
+ sys.stderr.write(
+ "{0:s}: error: {1:s}\n".format(self.program_name, message))
+ sys.exit(1)
+
+
+def _model_cb(option, opt_str, value, parser):
+ """
+ This function sets up the single parameters if the 'model' option has been selected
+ by the user.
+ """
+ model_name = value.lower()
+ models = CrcModels()
+ model = models.get_params(model_name)
+ if model != None:
+ setattr(parser.values, 'width', model['width'])
+ setattr(parser.values, 'poly', model['poly'])
+ setattr(parser.values, 'reflect_in', model['reflect_in'])
+ setattr(parser.values, 'xor_in', model['xor_in'])
+ setattr(parser.values, 'reflect_out', model['reflect_out'])
+ setattr(parser.values, 'xor_out', model['xor_out'])
+ else:
+ models = CrcModels()
+ model_list = ", ".join(models.names())
+ raise OptionValueError(
+ "unsupported model {0:s}. Supported models are: {1:s}."
+ .format(value, model_list))
+
+
+def _check_hex(dummy_option, opt, value):
+ """
+ Checks if a value is given in a decimal integer of hexadecimal reppresentation.
+ Returns the converted value or rises an exception on error.
+ """
+ try:
+ if value.lower().startswith("0x"):
+ return int(value, 16)
+ else:
+ return int(value)
+ except ValueError:
+ raise OptionValueError(
+ "option {0:s}: invalid integer or hexadecimal value: {1:s}.".format(opt, value))
+
+
+def _check_bool(dummy_option, opt, value):
+ """
+ Checks if a value is given as a boolean value (either 0 or 1 or "true" or "false")
+ Returns the converted value or rises an exception on error.
+ """
+ if value.isdigit():
+ return int(value, 10) != 0
+ elif value.lower() == "false":
+ return False
+ elif value.lower() == "true":
+ return True
+ else:
+ raise OptionValueError("option {0:s}: invalid boolean value: {1:s}.".format(opt, value))
+
+
+class MyOption(Option):
+ """
+ New option parsing class extends the Option class
+ """
+ TYPES = Option.TYPES + ("hex", "bool")
+ TYPE_CHECKER = copy(Option.TYPE_CHECKER)
+ TYPE_CHECKER["hex"] = _check_hex
+ TYPE_CHECKER["bool"] = _check_bool
+