aboutsummaryrefslogtreecommitdiffstats
path: root/idatui/app.py
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-07 03:28:55 +0200
committeruser <user@clank>2026-08-07 03:28:55 +0200
commit98b3b98eb1bf8f195ef4ebb1375c9f161d758c17 (patch)
tree810ec6ef2e88453a9fc076216dbc8a9b7e797a45 /idatui/app.py
parentBaseline for the v5 bench (landing polls every 2ms instead of 10ms; the poll ... (diff)
downloadida-tui-98b3b98eb1bf8f195ef4ebb1375c9f161d758c17.tar.gz
ida-tui-98b3b98eb1bf8f195ef4ebb1375c9f161d758c17.tar.xz
ida-tui-98b3b98eb1bf8f195ef4ebb1375c9f161d758c17.zip
Three targeted cuts: the graph's transposition pass counts keep and swap in one pass over the neighbour pairs (was four _pair_cross calls); the barycentre median answers degree 1 and 2 without sorting; and the search body is built from windowed model reads instead of one locked row lookup per line.
Result: {"status":"keep","total_ms":17944.4,"lg_boot_ms":703.7,"lg_decomp_ms":2453.7,"lg_graph_ms":1000,"lg_hex_ms":478,"lg_index_ms":96,"lg_listing_cold_ms":453,"lg_listing_warm_ms":411.2,"lg_nav_ms":6555.9,"lg_palette_ms":4.9,"lg_render_ms":218.4,"lg_search_ms":1308.8,"pure_graph_ms":212.3,"sm_boot_ms":432.4,"sm_decomp_ms":1267.8,"sm_graph_ms":742.7,"sm_hex_ms":440,"sm_index_ms":2.5,"sm_listing_cold_ms":264.3,"sm_listing_warm_ms":289.5,"sm_nav_ms":305.4,"sm_palette_ms":0.3,"sm_render_ms":258.6,"sm_search_ms":45,"fails":0}
Diffstat (limited to 'idatui/app.py')
-rw-r--r--idatui/app.py41
1 files changed, 34 insertions, 7 deletions
diff --git a/idatui/app.py b/idatui/app.py
index 6c0a872..c37846f 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -690,15 +690,21 @@ class SearchMixin:
key = (count, src)
if self._hay_key == key:
return self._hay
- text_of = self._search_line_text
starts: list[int] = []
parts: list[str] = []
pos = 0
- for i in range(count):
- s = text_of(i) or ""
+ chunk = 4096
+ for base in range(0, count, chunk):
+ for s in self._search_line_texts(base, min(chunk, count - base)):
+ if not s:
+ s = ""
+ starts.append(pos)
+ parts.append(s)
+ pos += len(s) + 1
+ while len(starts) < count: # a short window: keep the indices lined up
starts.append(pos)
- parts.append(s)
- pos += len(s) + 1
+ parts.append("")
+ pos += 1
blob = "\n".join(parts)
folded = blob.lower()
hay = None if len(folded) != len(blob) else (starts, blob, folded)
@@ -713,6 +719,12 @@ class SearchMixin:
def _search_line_text(self, i: int) -> str | None:
raise NotImplementedError
+ def _search_line_texts(self, start: int, count: int) -> list:
+ """``count`` line texts from ``start``. Overridable so a view whose rows
+ come from a locked model can fetch a window in one go."""
+ text_of = self._search_line_text
+ return [text_of(i) for i in range(start, start + count)]
+
def _search_ensure(self, done) -> None:
"""Ensure all line texts are available, then call ``done()`` on the UI
thread. Default: assume ready."""
@@ -1031,8 +1043,7 @@ class ListingView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=Tru
return ""
return self._op_bytes_text(h).ljust(self._op_w) + " "
- def _line_plain(self, idx: int) -> str | None:
- h = self._head(idx)
+ def _plain_of(self, h: Head | None) -> str | None:
if h is None:
return None
# Function headers and code labels sit at depth 0 (with the address);
@@ -1045,6 +1056,22 @@ class ListingView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=Tru
extra = _LST_INDENT if h.kind == "member" else ""
return base + self._op_field(h) + extra + self._name_prefix(h) + h.text
+ def _line_plain(self, idx: int) -> str | None:
+ return self._plain_of(self._head(idx))
+
+ def _search_line_texts(self, start: int, count: int) -> list:
+ """A window of plain lines in one model call.
+
+ Building the search body row by row took the model's lock and bisected
+ its row table a quarter of a million times; ``window`` does both once
+ for the whole window.
+ """
+ model = self.model
+ if model is None:
+ return []
+ plain = self._plain_of
+ return [plain(h) for h in model.window(start, count)]
+
def _insn_col(self, idx: int) -> int:
"""Column where the instruction/content text begins, past the address +
opcode-bytes gutter — the shift+home target. Mirrors ``_line_plain``'s