aboutsummaryrefslogtreecommitdiffstats
path: root/idatui
diff options
context:
space:
mode:
authorblasty <peter@haxx.in>2026-08-07 03:05:33 +0200
committerblasty <peter@haxx.in>2026-08-07 03:05:33 +0200
commit8e2cf13804e64c54d02aa4932ccb7ae1058edd2d (patch)
tree4e4d5d93d231129e40d289dd5ab00d22f1c49da5 /idatui
parentRe-run of #16 (keep the joined search body across a cancelled search and acro... (diff)
downloadida-tui-8e2cf13804e64c54d02aa4932ccb7ae1058edd2d.tar.gz
ida-tui-8e2cf13804e64c54d02aa4932ccb7ae1058edd2d.tar.xz
ida-tui-8e2cf13804e64c54d02aa4932ccb7ae1058edd2d.zip
Re-run of #18 (_CellRow slice assignment, one-string box borders, memoised Style sum), confirming it: 17590 -> 17501. lg_graph averages 951 over the two runs against 1136 before.
Result: {"status":"keep","total_ms":17501.4,"lg_boot_ms":711,"lg_decomp_ms":2484.8,"lg_graph_ms":1017.2,"lg_hex_ms":431.9,"lg_index_ms":98.7,"lg_listing_cold_ms":528,"lg_listing_warm_ms":409.2,"lg_nav_ms":6488.2,"lg_palette_ms":4.7,"lg_render_ms":214.8,"lg_search_ms":1473.5,"pure_graph_ms":244.3,"sm_boot_ms":451.4,"sm_decomp_ms":596,"sm_graph_ms":691.3,"sm_hex_ms":444.5,"sm_index_ms":0,"sm_listing_cold_ms":263,"sm_listing_warm_ms":284.2,"sm_nav_ms":374.7,"sm_palette_ms":0.3,"sm_render_ms":252.1,"sm_search_ms":37.5,"fails":0}
Diffstat (limited to 'idatui')
-rw-r--r--idatui/app.py56
1 files changed, 42 insertions, 14 deletions
diff --git a/idatui/app.py b/idatui/app.py
index 53dd79b..6c0a872 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -2161,6 +2161,13 @@ _GPAD = 1 # columns of padding inside a box
_MINI_W, _MINI_H = 30, 14
+#: Memo for ``Style + Style``. Rich rebuilds a whole Style on every ``+``, and
+#: the graph merges an overlay (cursor band, trail, highlight) over a run of
+#: cells that share only a handful of base styles -- so the same pair is
+#: recombined hundreds of times per frame.
+_STYLE_SUM: dict[tuple, Style] = {}
+
+
class _CellRow:
"""A row of (char, style) cells that coalesces into a Strip.
@@ -2183,13 +2190,39 @@ class _CellRow:
self.st[i] = style
def text(self, i: int, s: str, style: Style) -> None:
- for k, c in enumerate(s):
- self.put(i + k, c, style)
+ """Write ``s`` at cell ``i``, clipped to the row.
+
+ Slice assignment rather than a call per character: a graph row is drawn
+ from box borders, instruction text and the minimap, and doing it a cell
+ at a time made painting one frame thousands of bound-method calls.
+ Assigning a str to a list slice expands it to characters in C.
+ """
+ if not s:
+ return
+ a = i if i > 0 else 0
+ b = i + len(s)
+ if b > self.width:
+ b = self.width
+ if b <= a:
+ return
+ self.ch[a:b] = s[a - i:b - i]
+ self.st[a:b] = [style] * (b - a)
def restyle(self, a: int, b: int, style: Style) -> None:
"""Merge ``style`` over the cells in [a, b) (keeps the characters)."""
- for i in range(max(a, 0), min(b, self.width)):
- self.st[i] = self.st[i] + style
+ if a < 0:
+ a = 0
+ if b > self.width:
+ b = self.width
+ st = self.st
+ combine = _STYLE_SUM
+ for i in range(a, b):
+ base = st[i]
+ key = (base, style)
+ got = combine.get(key)
+ if got is None:
+ got = combine[key] = base + style
+ st[i] = got
def strip(self) -> Strip:
segs: list[Segment] = []
@@ -2796,10 +2829,8 @@ class GraphView(NavMixin, ScrollView, can_focus=True):
label = f"loc_{n.block.start:X}" if n.block else ""
if b is not None and b.rows and b.rows[0].name:
label = b.rows[0].name
- out.put(left, graph.BOX["tl"], bs)
- for i in range(1, w - 1):
- out.put(left + i, graph.BOX["h"], bs)
- out.put(left + w - 1, graph.BOX["tr"], bs)
+ out.text(left, graph.BOX["tl"] + graph.BOX["h"] * (w - 2)
+ + graph.BOX["tr"], bs)
tag = f" {label} "
if len(tag) <= w - 4:
st = _S_GENTRY if (self.fc and n.id == self.fc.entry) else (
@@ -2809,15 +2840,12 @@ class GraphView(NavMixin, ScrollView, can_focus=True):
out.put(left + w - 2, "↺", _S_EDGE[graph.E_BACK])
return
if row == n.y + n.h - 1:
- out.put(left, graph.BOX["bl"], bs)
- for i in range(1, w - 1):
- out.put(left + i, graph.BOX["h"], bs)
- out.put(left + w - 1, graph.BOX["br"], bs)
+ out.text(left, graph.BOX["bl"] + graph.BOX["h"] * (w - 2)
+ + graph.BOX["br"], bs)
return
out.put(left, graph.BOX["v"], bs)
out.put(left + w - 1, graph.BOX["v"], bs)
- for i in range(1, w - 1):
- out.put(left + i, " ", _S_INSN)
+ out.text(left + 1, " " * (w - 2), _S_INSN)
i = row - n.y - 1
rows = self._rows(n.id)
if not (0 <= i < len(rows)):