Esempio n. 1
0
File: field.py Progetto: TauPan/bdec
 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)
Esempio n. 2
0
 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
Esempio n. 3
0
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))
Esempio n. 4
0
 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
Esempio n. 5
0
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
Esempio n. 6
0
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