aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--idatui/domain.py10
-rw-r--r--server/patch_server.py22
2 files changed, 26 insertions, 6 deletions
diff --git a/idatui/domain.py b/idatui/domain.py
index 1c7883f..645d111 100644
--- a/idatui/domain.py
+++ b/idatui/domain.py
@@ -90,10 +90,16 @@ class Line:
)
-@dataclass(frozen=True)
+@dataclass(frozen=True, slots=True)
class Head:
"""One flat-listing item (from the ``heads`` server tool): a code
- instruction, a data item, or an undefined byte run."""
+ instruction, a data item, or an undefined byte run.
+
+ ``slots=True`` because this is the most-constructed object in the codebase:
+ a jump to an address near the end of a big binary builds one per listing row
+ it walks past, hundreds of thousands of them, and the slotted layout is ~20%
+ cheaper to build (and smaller to hold).
+ """
ea: int
kind: str # 'code' | 'data' | 'unknown' | 'member'
diff --git a/server/patch_server.py b/server/patch_server.py
index a1ec1ef..fe16ede 100644
--- a/server/patch_server.py
+++ b/server/patch_server.py
@@ -391,6 +391,10 @@ def _idatui_tag_map():
_IDATUI_TAGS = None
_IDATUI_OPND_TAGS = None
_IDATUI_CTL = None # re: a tag = one of three control chars plus its argument
+#: {tag character: (kind, operand index or None)} -- the two maps above merged,
+#: because the span walker wants both for the same tag and a dict lookup per
+#: tag per line is one of the few things it does often enough to matter.
+_IDATUI_TAGINFO = None
def _idatui_opnd_tag_map():
@@ -420,7 +424,7 @@ def _idatui_spans(line):
Unknown tags become 'text' rather than being dropped: a processor module can
emit a colour we don't classify, and losing the characters would corrupt the
line."""
- global _IDATUI_TAGS, _IDATUI_OPND_TAGS, _IDATUI_CTL
+ global _IDATUI_TAGS, _IDATUI_OPND_TAGS, _IDATUI_CTL, _IDATUI_TAGINFO
import ida_lines
if _IDATUI_TAGS is None:
_IDATUI_TAGS = _idatui_tag_map()
@@ -434,7 +438,12 @@ def _idatui_spans(line):
# characters but only ~13 tags -- everything between two tags is already
# exactly one span's worth of text.
_IDATUI_CTL = _re.compile("([\\x01\\x02\\x03](?s:.))")
- tags, opnds = _IDATUI_TAGS, _IDATUI_OPND_TAGS
+ if _IDATUI_TAGINFO is None:
+ _IDATUI_TAGINFO = {
+ tag: (_IDATUI_TAGS.get(tag, "text"), _IDATUI_OPND_TAGS.get(tag))
+ for tag in set(_IDATUI_TAGS) | set(_IDATUI_OPND_TAGS)}
+ taginfo = _IDATUI_TAGINFO
+ plain_tag = ("text", None)
on, off, esc = "\x01", "\x02", "\x03"
addr_tag = chr(getattr(ida_lines, "COLOR_ADDR", 0x28))
addr_len = int(getattr(ida_lines, "COLOR_ADDR_SIZE", 16))
@@ -478,8 +487,7 @@ def _idatui_spans(line):
pend = ""
if ch == on:
stack.append((kind, opnd))
- kind = tags.get(tag, "text")
- o = opnds.get(tag)
+ kind, o = taginfo.get(tag, plain_tag)
if o is not None:
opnd = o # operands nest: an inner colour keeps the operand
elif stack:
@@ -497,6 +505,12 @@ def _idatui_spans(line):
out, prev_space = [], False
for kind, txt, opnd in spans:
core = " ".join(txt.split())
+ if core == txt:
+ # Nothing to collapse and no edge whitespace -- which is the common
+ # case ("mov", "rax", ", ") and skips both isspace() probes below.
+ prev_space = False
+ out.append([kind, txt, opnd])
+ continue
if not core: # the span is nothing but padding
if not prev_space:
prev_space = True