diff options
| author | user <user@clank> | 2026-08-07 02:49:28 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-07 02:49:28 +0200 |
| commit | 625b067ed89f773bf1601d4dcb01484826db34c0 (patch) | |
| tree | db3e0a1158144c42b5e999a2028f35777c578c19 /idatui | |
| parent | Search the whole segment as ONE joined string. Every line is concatenated onc... (diff) | |
| download | ida-tui-625b067ed89f773bf1601d4dcb01484826db34c0.tar.gz ida-tui-625b067ed89f773bf1601d4dcb01484826db34c0.tar.xz ida-tui-625b067ed89f773bf1601d4dcb01484826db34c0.zip | |
HexView.render_line emits style RUNS instead of one Segment per byte cell (35 -> 7 segments per row), and Head is a NamedTuple rather than a frozen dataclass (tuple.__new__ 1.9us vs a dataclass __init__ 2.9us, and it is built once per listing row walked).
Result: {"status":"keep","total_ms":17784.1,"lg_boot_ms":680.8,"lg_decomp_ms":2344,"lg_graph_ms":1106.5,"lg_hex_ms":576.4,"lg_index_ms":96.5,"lg_listing_cold_ms":552.9,"lg_listing_warm_ms":431.3,"lg_nav_ms":6256.4,"lg_palette_ms":4.8,"lg_render_ms":224.1,"lg_search_ms":1916.5,"pure_graph_ms":241.3,"sm_boot_ms":442.6,"sm_decomp_ms":591.3,"sm_graph_ms":663.9,"sm_hex_ms":419.2,"sm_index_ms":0,"sm_listing_cold_ms":258.2,"sm_listing_warm_ms":281.8,"sm_nav_ms":374.4,"sm_palette_ms":0.3,"sm_render_ms":251.1,"sm_search_ms":69.8,"fails":0}
Diffstat (limited to 'idatui')
| -rw-r--r-- | idatui/app.py | 34 | ||||
| -rw-r--r-- | idatui/domain.py | 17 |
2 files changed, 39 insertions, 12 deletions
diff --git a/idatui/app.py b/idatui/app.py index abdaa1e..20aed88 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -2052,10 +2052,31 @@ class HexView(ScrollView, can_focus=True): if data is None: segs.append(Segment("… fetching", _S_DIM)) else: + # Emit RUNS, not one segment per byte. A row is 32 cells whose style + # almost never changes (one cursor cell, or a trace boundary), and a + # segment per cell made every hex frame 1540 segments for the + # compositor to cut and merge again. n = len(data) + run: list[str] = [] + run_st = None + + def flush(st=None, _segs=segs) -> None: + nonlocal run, run_st + if run: + _segs.append(Segment("".join(run), run_st)) + run = [] + run_st = st + + def put(text: str, st) -> None: + nonlocal run_st + if st is not run_st: + flush(st) + run.append(text) + + run_st = _S_HEX for i in range(16): if i == 8: - segs.append(Segment(" ", _S_HEX)) + put(" ", _S_HEX) if i < n: live = tknown is not None and tknown[i] val = tmem[i] if live else data[i] @@ -2065,10 +2086,10 @@ class HexView(ScrollView, can_focus=True): st = _S_HEX else: st = _S_HEX_LIVE if live else _S_HEX_STALE - segs.append(Segment(f"{val:02X} ", st)) + put(f"{val:02X} ", st) else: - segs.append(Segment(" ", _S_HEX)) - segs.append(Segment(" |", _S_DIM)) + put(" ", _S_HEX) + put(" |", _S_DIM) for i in range(16): if i < n: live = tknown is not None and tknown[i] @@ -2082,8 +2103,9 @@ class HexView(ScrollView, can_focus=True): st = _S_HEX_LIVE if live else _S_HEX_STALE else: ch, st = " ", _S_ASCII - segs.append(Segment(ch, st)) - segs.append(Segment("|", _S_DIM)) + put(ch, st) + put("|", _S_DIM) + flush() return Strip(segs).adjust_cell_length(width, _S_HEX) diff --git a/idatui/domain.py b/idatui/domain.py index 9dea353..e107786 100644 --- a/idatui/domain.py +++ b/idatui/domain.py @@ -28,6 +28,7 @@ import threading import urllib.request from concurrent.futures import ThreadPoolExecutor from dataclasses import dataclass, field, replace +from typing import NamedTuple from typing import Callable, TYPE_CHECKING from . import diag @@ -90,15 +91,19 @@ class Line: ) -@dataclass(frozen=True, slots=True) -class Head: +class Head(NamedTuple): """One flat-listing item (from the ``heads`` server tool): a code 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). + A ``NamedTuple`` rather than a dataclass because this is by far 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, a quarter of a + million of them -- and ``tuple.__new__`` costs 1.9us where a frozen + dataclass's ``__init__`` costs 2.9us. Attribute reads are marginally slower + (10ns vs 20ns), which is the right trade: rows are built far more often than + they are read, and a viewport only ever reads forty of them. + + Immutable, like the frozen dataclass it replaced. """ ea: int |
