Source code for ailment.utils

from typing import Union, Tuple, Optional, TYPE_CHECKING
import struct

try:
    import claripy
except ImportError:
    claripy = None

try:
    import _md5 as md5lib
except ImportError:
    import hashlib as md5lib

if TYPE_CHECKING:
    from .expression import Expression


get_bits_type_params = Union[int, "Expression"]
if claripy:
    get_bits_type_params = Union[int, claripy.ast.Bits, "Expression"]


[docs]def get_bits(expr: get_bits_type_params) -> Optional[int]: # delayed import from .expression import Expression if isinstance(expr, Expression): return expr.bits elif isinstance(expr, claripy.ast.Bits): return expr.size() elif hasattr(expr, "bits"): return expr.bits else: return None
md5_unpacker = struct.Struct("4I")
[docs]def stable_hash(t: Tuple) -> int: cnt = _dump_tuple(t) hd = md5lib.md5(cnt).digest() return md5_unpacker.unpack(hd)[0] # 32 bits
def _dump_tuple(t: Tuple) -> bytes: cnt = b"" for item in t: if item is not None: type_ = type(item) if type_ in _DUMP_BY_TYPE: cnt += _DUMP_BY_TYPE[type_](item) else: # for TaggedObjects, hash(item) is stable # other types of items may show up, such as pyvex.expr.CCall and Dirty. they will be removed some day. cnt += struct.pack("<Q", hash(item) & 0xFFFF_FFFF_FFFF_FFFF) cnt += b"\xf0" return cnt def _dump_str(t: str) -> bytes: return t.encode("ascii") def _dump_int(t: int) -> bytes: prefix = b"" if t >= 0 else b"-" t = abs(t) if t <= 0xFFFF: return prefix + struct.pack("<H", t) elif t <= 0xFFFF_FFFF: return prefix + struct.pack("<I", t) elif t <= 0xFFFF_FFFF_FFFF_FFFF: return prefix + struct.pack("<Q", t) else: cnt = b"" while t > 0: cnt += _dump_int(t & 0xFFFF_FFFF_FFFF_FFFF) t >>= 64 return prefix + cnt def _dump_type(t: type) -> bytes: return t.__name__.encode("ascii") _DUMP_BY_TYPE = { tuple: _dump_tuple, str: _dump_str, int: _dump_int, type: _dump_type, }
[docs]def is_none_or_likeable(arg1, arg2, is_list=False): """ Returns whether two things are both None or can like each other """ from .expression import Expression if arg1 is None or arg2 is None: if arg1 == arg2: return True return False if is_list: return len(arg1) == len(arg2) and all(is_none_or_likeable(a1, a2) for a1, a2 in zip(arg1, arg2)) if isinstance(arg1, Expression): return arg1.likes(arg2) return arg1 == arg2