def _get_data(self, value, context): try: length = self.entry.length.evaluate(context) return encode_value(self.entry, value, length) except UndecodedReferenceError, ex: # We don't know how long this entry should be. if self.entry.format == self.entry.INTEGER: # Integers require a specific length of encoding. If one is # not specified, we'll try several lengths until we find one # that fits. # # We only consider lengths that are in the range specified by # the entry length to avoid choosing an out of bounds length. length_range = erange(self.entry.length, self.entry, self._params) def is_valid(length): if length_range.min is not None and length_range.min > length: return False if length_range.max is not None and length_range.max < length: return False return True possible_lengths = [8, 16, 32, 64] for length in (l for l in possible_lengths if is_valid(l)): try: return encode_value(self.entry, value, length) except FieldDataError: # The value didn't fit in this length... try the next # one. pass else: raise VariableIntegerTooLongError(self.entry, value) else: # All other types (eg: string, binary, hex) have an implicit # length that the encoder can use. return encode_value(self.entry, value, None)
def influence(component): reference, expression = component output = erange(expression, entry, params) result = 0 if output.min: result = max(abs(output.min), result) if output.max: result = max(abs(output.max), result) return result
def get_valid_integer_lengths(entry, params): length_range = erange(entry.length, entry, params) def is_valid(length): if length_range.min is not None and length_range.min > length: return False if length_range.max is not None and length_range.max < length: return False return True possible_lengths = [8, 16, 24, 32, 64] return (l for l in possible_lengths if is_valid(l))
def influence(component): reference, expression = component output = erange(expression, entry, params) result = 0 if output.min is not None and output.max is not None: result = max(abs(output.min), result) result = max(abs(output.max), result) else: # As we have either no minimum or maximum, our influence will # be really big. result = 1e1024 return result
def solve_expression(result_expr, expression, entry, params, input_params): """Get a list of expressions for solving the given expression. For example, for y = 2 * x + 5 'y' is the result expression, '2 * x + 5' is the expression, it would solve to a constant of 5, and x = y / 2. result_expr -- An expression for the result. expression -- The expression we want to solve. entry -- The entry where this expression is used. This is used to resolve references to entries. params -- A bdec.inspect.param.ExpressionParameters instance, used to determine ranges of references. input_params -- A list of parameters that are 'known'. Any references to these parameters will be treated as constant. return -- A (constant, [(reference, expression, inverted)]), where constant is a constant expression, and the reference / expression / inverted tuple is the reference to an unknown parameter, the portion of the expression that is made up of this entry, and the inverted expression to calculate its value given it's component of the expression. """ components, constant = _break_into_parts(entry, expression, input_params) # Sort the components in order influence on the output def influence(component): reference, expression = component output = erange(expression, entry, params) result = 0 if output.min is not None and output.max is not None: result = max(abs(output.min), result) result = max(abs(output.max), result) else: # As we have either no minimum or maximum, our influence will # be really big. result = 1e1024 return result variables = sorted(components.items(), key=influence, reverse=True) result_params = [] for i, (ref, expr) in enumerate(variables): # Get the range of the remaining references (required so we know what # way we should be rounding divisions / shifts). remaining_range = sum((erange(c[1], entry, params) for c in variables[i+1:]), Range(0, 0)) result_params.append((ref, expr, _invert(result_expr, entry, expr, params, input_params, remaining_range))) return constant, result_params
def _invert(result_expr, entry, expression, params, input_params, remainder_range): """Convert a function value=f(x) into x=f(value)""" left = result_expr right = expression # Reduce 'right' until it is just the single parameter while not isinstance(right, ReferenceExpression): if isinstance(right, ArithmeticExpression): is_left_const = _is_constant(entry, right.left, input_params) is_right_const = _is_constant(entry, right.right, input_params) if not is_left_const and not is_right_const: # The code doesn't handle the same entry being referenced # multiple times at the moment... (eg: y = x + x) raise SolverError(entry, expression, 'Unable to invert ' 'expressions where the same entry is referenced on ' 'the left and right of an expression') if right.op == operator.mul: if is_right_const: # left = right * k -> left / k = right left = ArithmeticExpression(operator.div, left, right.right) right = right.left else: # left = k * right -> left / k = right left = ArithmeticExpression(operator.div, left, right.left) right = right.right # To correctly handle solving signed integers, eg: # y = (-signed * k) + value # we have to ensure that '-signed / k' is always less than 'y', # as 'value >= 0'. Thus we need to ensure that the inverted # '/' always rounds towards negative infinity. To ensure this # we check to see if we erased any bits in the divide; if so, we # need to account for the rounding. # # To know if we need to round down or up, we need to see # whether we will be positive or negative, and likewise the # value of the remainder. For example, if our output will be # negative (eg: remainder = -signed * k), and the remainder # will be positive (eg: y = value) then we must round UP (so # that the remainder will be positive when solving 'value'). # Conversely, if both this expression and the remainder have # the same sign, there is no need for rounding... our_range = erange(expression, entry, params) should_round_up = False if (our_range.min is None or our_range.min < 0) and \ (remainder_range.max is None or remainder_range.max > 0): # We are negative, remainder is position; we need to round up. should_round_up = True elif our_range.max is None or our_range.max > 0 and \ (remainder_range.min is None or remainder_range.min < 0): # We are positive, remainder is negative; we need to round up. should_round_up = True left = RoundUpDivisionExpression(left.left, left.right, should_round_up) elif is_right_const and right.op == operator.lshift: # left = right << k -> left >> k = right left = ArithmeticExpression(operator.rshift, left, right.right) right = right.left elif is_left_const and right.op == operator.sub: # left = k - right -> k - left = right left = ArithmeticExpression(operator.sub, right.left, left) right = right.right elif is_left_const and right.op == operator.add: # left = k + right -> left - k = right left = ArithmeticExpression(operator.sub, left, right.left) right = right.right else: raise SolverError(entry, expression, 'Unable to invert ' 'expressions containing operator %s' % right.op) else: raise SolverError(entry, expression, 'Right expression is not ' 'an arithmetic expression') return left