about summary refs log tree commit diff stats
path: root/src/miasm/core/utils.py
diff options
context:
space:
mode:
authorTheofilos Augoustis <theofilos.augoustis@gmail.com>2025-10-14 09:09:29 +0000
committerTheofilos Augoustis <theofilos.augoustis@gmail.com>2025-10-14 09:09:29 +0000
commit579cf1d03fb932083e6317967d1613d5c2587fb6 (patch)
tree629f039935382a2a7391bce9253f6c9968159049 /src/miasm/core/utils.py
parent51c15d3ea2e16d4fc5f0f01a3b9befc66b1f982e (diff)
downloadfocaccia-miasm-579cf1d03fb932083e6317967d1613d5c2587fb6.tar.gz
focaccia-miasm-579cf1d03fb932083e6317967d1613d5c2587fb6.zip
Convert to src-layout ta/nix
Diffstat (limited to 'src/miasm/core/utils.py')
-rw-r--r--src/miasm/core/utils.py292
1 files changed, 292 insertions, 0 deletions
diff --git a/src/miasm/core/utils.py b/src/miasm/core/utils.py
new file mode 100644
index 00000000..291c5f4d
--- /dev/null
+++ b/src/miasm/core/utils.py
@@ -0,0 +1,292 @@
+from __future__ import print_function
+import re
+import sys
+from builtins import range
+import struct
+import inspect
+
+try:
+    from collections.abc import MutableMapping as DictMixin
+except ImportError:
+    from collections import MutableMapping as DictMixin
+
+from operator import itemgetter
+import codecs
+
+from future.utils import viewitems
+
+import collections
+
+COLOR_INT = "azure4"
+COLOR_ID = "forestgreen"#"chartreuse3"
+COLOR_MEM = "deeppink4"
+COLOR_OP_FUNC = "blue1"
+COLOR_LOC = "darkslateblue"
+COLOR_OP = "black"
+
+COLOR_MNEMO = "blue1"
+
+ESCAPE_CHARS = re.compile('[' + re.escape('{}[]') + '&|<>' + ']')
+
+
+
+def set_html_text_color(text, color):
+    return '<font color="%s">%s</font>' % (color, text)
+
+
+def _fix_chars(token):
+    return "&#%04d;" % ord(token.group())
+
+
+def fix_html_chars(text):
+    return ESCAPE_CHARS.sub(_fix_chars, str(text))
+
+BRACKET_O = fix_html_chars('[')
+BRACKET_C = fix_html_chars(']')
+
+upck8 = lambda x: struct.unpack('B', x)[0]
+upck16 = lambda x: struct.unpack('H', x)[0]
+upck32 = lambda x: struct.unpack('I', x)[0]
+upck64 = lambda x: struct.unpack('Q', x)[0]
+pck8 = lambda x: struct.pack('B', x)
+pck16 = lambda x: struct.pack('H', x)
+pck32 = lambda x: struct.pack('I', x)
+pck64 = lambda x: struct.pack('Q', x)
+
+# Little endian
+upck8le = lambda x: struct.unpack('<B', x)[0]
+upck16le = lambda x: struct.unpack('<H', x)[0]
+upck32le = lambda x: struct.unpack('<I', x)[0]
+upck64le = lambda x: struct.unpack('<Q', x)[0]
+pck8le = lambda x: struct.pack('<B', x)
+pck16le = lambda x: struct.pack('<H', x)
+pck32le = lambda x: struct.pack('<I', x)
+pck64le = lambda x: struct.pack('<Q', x)
+
+# Big endian
+upck8be = lambda x: struct.unpack('>B', x)[0]
+upck16be = lambda x: struct.unpack('>H', x)[0]
+upck32be = lambda x: struct.unpack('>I', x)[0]
+upck64be = lambda x: struct.unpack('>Q', x)[0]
+pck8be = lambda x: struct.pack('>B', x)
+pck16be = lambda x: struct.pack('>H', x)
+pck32be = lambda x: struct.pack('>I', x)
+pck64be = lambda x: struct.pack('>Q', x)
+
+
+LITTLE_ENDIAN = 1
+BIG_ENDIAN = 2
+
+
+pck = {8: pck8,
+       16: pck16,
+       32: pck32,
+       64: pck64}
+
+
+def get_caller_name(caller_num=0):
+    """Get the nth caller's name
+    @caller_num: 0 = the caller of get_caller_name, 1 = next parent, ..."""
+    pystk = inspect.stack()
+    if len(pystk) > 1 + caller_num:
+        return pystk[1 + caller_num][3]
+    else:
+        return "Bad caller num"
+
+
+def whoami():
+    """Returns the caller's name"""
+    return get_caller_name(1)
+
+
+class Disasm_Exception(Exception):
+    pass
+
+
+def printable(string):
+    if isinstance(string, bytes):
+        return "".join(
+            c.decode() if b" " <= c < b"~" else "."
+            for c in (string[i:i+1] for i in range(len(string)))
+        )
+    return string
+
+
+def force_bytes(value):
+    if isinstance(value, bytes):
+        return value
+    if not isinstance(value, str):
+        return value
+    out = []
+    for c in value:
+        c = ord(c)
+        assert c < 0x100
+        out.append(c)
+    return bytes(out)
+
+
+def force_str(value):
+    if isinstance(value, str):
+        return value
+    elif isinstance(value, bytes):
+        out = ""
+        for i in range(len(value)):
+            # For Python2/Python3 compatibility
+            c = ord(value[i:i+1])
+            out += chr(c)
+        value = out
+    else:
+        raise ValueError("Unsupported type")
+    return value
+
+
+def iterbytes(string):
+    for i in range(len(string)):
+        yield string[i:i+1]
+
+
+def int_to_byte(value):
+    return struct.pack('B', value)
+
+def cmp_elts(elt1, elt2):
+    return (elt1 > elt2) - (elt1 < elt2)
+
+
+_DECODE_HEX = codecs.getdecoder("hex_codec")
+_ENCODE_HEX = codecs.getencoder("hex_codec")
+
+def decode_hex(value):
+    return _DECODE_HEX(value)[0]
+
+def encode_hex(value):
+    return _ENCODE_HEX(value)[0]
+
+def size2mask(size):
+    """Return the bit mask of size @size"""
+    return (1 << size) - 1
+
+def hexdump(src, length=16):
+    lines = []
+    for c in range(0, len(src), length):
+        chars = src[c:c + length]
+        hexa = ' '.join("%02x" % ord(x) for x in iterbytes(chars))
+        printable = ''.join(
+            x.decode() if 32 <= ord(x) <= 126 else '.' for x in iterbytes(chars)
+        )
+        lines.append("%04x  %-*s  %s\n" % (c, length * 3, hexa, printable))
+    print(''.join(lines))
+
+
+# stackoverflow.com/questions/2912231
+class keydefaultdict(collections.defaultdict):
+
+    def __missing__(self, key):
+        if self.default_factory is None:
+            raise KeyError(key)
+        value = self[key] = self.default_factory(key)
+        return value
+
+
+class BoundedDict(DictMixin):
+
+    """Limited in size dictionary.
+
+    To reduce combinatory cost, once an upper limit @max_size is reached,
+    @max_size - @min_size elements are suppressed.
+    The targeted elements are the less accessed.
+
+    One can define a callback called when an element is removed
+    """
+
+    def __init__(self, max_size, min_size=None, initialdata=None,
+                 delete_cb=None):
+        """Create a BoundedDict
+        @max_size: maximum size of the dictionary
+        @min_size: (optional) number of most used element to keep when resizing
+        @initialdata: (optional) dict instance with initial data
+        @delete_cb: (optional) callback called when an element is removed
+        """
+        self._data = initialdata.copy() if initialdata else {}
+        self._min_size = min_size if min_size else max_size // 3
+        self._max_size = max_size
+        self._size = len(self._data)
+        # Do not use collections.Counter as it is quite slow
+        self._counter = {k: 1 for k in self._data}
+        self._delete_cb = delete_cb
+
+    def __setitem__(self, asked_key, value):
+        if asked_key not in self._data:
+            # Update internal size and use's counter
+            self._size += 1
+
+            # Bound can only be reached on a new element
+            if (self._size >= self._max_size):
+                most_common = sorted(
+                    viewitems(self._counter),
+                    key=itemgetter(1),
+                    reverse=True
+                )
+
+                # Handle callback
+                if self._delete_cb is not None:
+                    for key, _ in most_common[self._min_size - 1:]:
+                        self._delete_cb(key)
+
+                # Keep only the most @_min_size used
+                self._data = {key: self._data[key]
+                              for key, _ in most_common[:self._min_size - 1]}
+                self._size = self._min_size
+
+                # Reset use's counter
+                self._counter = {k: 1 for k in self._data}
+
+            # Avoid rechecking in dict: set to 1 here, add 1 otherwise
+            self._counter[asked_key] = 1
+        else:
+            self._counter[asked_key] += 1
+
+        self._data[asked_key] = value
+
+    def __contains__(self, key):
+        # Do not call has_key to avoid adding function call overhead
+        return key in self._data
+
+    def has_key(self, key):
+        return key in self._data
+
+    def keys(self):
+        "Return the list of dict's keys"
+        return list(self._data)
+
+    @property
+    def data(self):
+        "Return the current instance as a dictionary"
+        return self._data
+
+    def __getitem__(self, key):
+        # Retrieve data first to raise the proper exception on error
+        data = self._data[key]
+        # Should never raise, since the key is in self._data
+        self._counter[key] += 1
+        return data
+
+    def __delitem__(self, key):
+        if self._delete_cb is not None:
+            self._delete_cb(key)
+        del self._data[key]
+        self._size -= 1
+        del self._counter[key]
+
+    def __del__(self):
+        """Ensure the callback is called when last reference is lost"""
+        if self._delete_cb:
+            for key in self._data:
+                self._delete_cb(key)
+
+
+    def __len__(self):
+        return len(self._data)
+
+    def __iter__(self):
+        return iter(self._data)
+