diff options
| author | user <user@clank> | 2026-08-07 01:36:15 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-07 01:36:15 +0200 |
| commit | b6a95ec3235fb180f17199ce7e6ad7ac2e80af15 (patch) | |
| tree | 57a67d386897a24a21810f755ee9f613ca2eb248 /server/patch_server.py | |
| parent | Stop ida-pro-mcp installing a sys.setprofile hook around every tool call. Its... (diff) | |
| download | ida-tui-b6a95ec3235fb180f17199ce7e6ad7ac2e80af15.tar.gz ida-tui-b6a95ec3235fb180f17199ce7e6ad7ac2e80af15.tar.xz ida-tui-b6a95ec3235fb180f17199ce7e6ad7ac2e80af15.zip | |
_idatui_spans: one capturing re.split over the tag pairs instead of finditer+char-slicing, and collapse whitespace with ' '.join(txt.split()) instead of a regex sub. 13.15 -> 11.07 us/line (the previous finditer attempt was 14.4, i.e. SLOWER than the original char loop it replaced).
Result: {"status":"keep","total_ms":26050.2,"lg_boot_ms":727.4,"lg_decomp_ms":2618.1,"lg_graph_ms":935.1,"lg_hex_ms":954.2,"lg_index_ms":67.4,"lg_listing_cold_ms":459.3,"lg_listing_warm_ms":545.7,"lg_nav_ms":9257.8,"lg_palette_ms":4.7,"lg_render_ms":238.8,"lg_search_ms":5472.5,"pure_graph_ms":530.7,"sm_boot_ms":534.6,"sm_decomp_ms":635.8,"sm_graph_ms":748.1,"sm_hex_ms":858.7,"sm_index_ms":0,"sm_listing_cold_ms":268.7,"sm_listing_warm_ms":269.2,"sm_nav_ms":441.6,"sm_palette_ms":0.3,"sm_render_ms":281.7,"sm_search_ms":200,"fails":0}
Diffstat (limited to 'server/patch_server.py')
| -rw-r--r-- | server/patch_server.py | 102 |
1 files changed, 56 insertions, 46 deletions
diff --git a/server/patch_server.py b/server/patch_server.py index b806be2..b75b120 100644 --- a/server/patch_server.py +++ b/server/patch_server.py @@ -364,8 +364,7 @@ def _idatui_tag_map(): _IDATUI_TAGS = None _IDATUI_OPND_TAGS = None -_IDATUI_CTL = None # re: the three control characters a tagged line can hold -_IDATUI_WS = None # re: a run of whitespace, exactly what str.isspace() calls one +_IDATUI_CTL = None # re: a tag = one of three control chars plus its argument def _idatui_opnd_tag_map(): @@ -395,7 +394,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, _IDATUI_WS + global _IDATUI_TAGS, _IDATUI_OPND_TAGS, _IDATUI_CTL import ida_lines if _IDATUI_TAGS is None: _IDATUI_TAGS = _idatui_tag_map() @@ -403,48 +402,54 @@ def _idatui_spans(line): _IDATUI_OPND_TAGS = _idatui_opnd_tag_map() if _IDATUI_CTL is None: import re as _re - _IDATUI_CTL = _re.compile("[\\x01\\x02\\x03]") - # str.isspace() is true for \\x1c-\\x1f and \\x85 as well as the \\s - # class, so spell those out: this substitution has to agree with the - # plain-text collapse character for character (checked over every - # codepoint) or the row silently loses its highlighting. - _IDATUI_WS = _re.compile("[\\\\s\\x1c\\x1d\\x1e\\x1f\\x85]+") - tags, opnds, ctl = _IDATUI_TAGS, _IDATUI_OPND_TAGS, _IDATUI_CTL + # One capturing split gives [text, tag, text, tag, ..., text] in a + # single C pass. A per-character python loop over the line used to be + # the most expensive thing the `heads` tool did, and a line is ~54 + # 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\\S])") + tags, opnds = _IDATUI_TAGS, _IDATUI_OPND_TAGS 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)) - # Jump between control characters and take the text in between as one slice. - # A per-character loop here was 68% of the whole `heads` tool: a disasm line - # is ~50 characters but only ~15 tags, and everything between two tags is - # already exactly one span's worth of text. - spans, stack, buf = [], [], [] # stack entries: (kind, operand index|None) + parts = _IDATUI_CTL.split(line) + spans, stack = [], [] # stack entries: (kind, operand index|None) kind, opnd = "text", None # state the current run of text belongs to - i, n = 0, len(line) - for m in ctl.finditer(line): - j = m.start() - if j < i: # inside an address payload / after an esc - continue - if j + 1 >= n: # a trailing control char is literal text + pend = "" + skip = 0 # characters of an address payload still due + i, n = 0, len(parts) + while i < n: + txt = parts[i] + i += 1 + if skip: + if len(txt) <= skip: + skip -= len(txt) + txt = "" + else: + txt = txt[skip:] + skip = 0 + if txt: + pend += txt + if i >= n: break - ch = line[j] + pair = parts[i] + i += 1 + if skip: # a tag INSIDE an address payload: 2 chars + skip = skip - 2 if skip > 2 else 0 + continue + ch = pair[0] if ch == esc: # escaped literal: keep the char it guards - buf.append(line[i:j]) - buf.append(line[j + 1]) - i = j + 2 + pend += pair[1] continue - tag = line[j + 1] + tag = pair[1] if ch == on and tag == addr_tag: # An embedded target address, not display text: 16 hex digits that # must not reach the screen. Deliberately NOT a span boundary. - buf.append(line[i:j]) - i = j + 2 + addr_len + skip = addr_len continue - buf.append(line[i:j]) - i = j + 2 - txt = "".join(buf) - if txt: - spans.append([kind, txt, opnd]) - del buf[:] + if pend: + spans.append([kind, pend, opnd]) + pend = "" if ch == on: stack.append((kind, opnd)) kind = tags.get(tag, "text") @@ -455,24 +460,29 @@ def _idatui_spans(line): kind, opnd = stack.pop() else: kind, opnd = "text", None - if i < n: - buf.append(line[i:]) - txt = "".join(buf) - if txt: - spans.append([kind, txt, opnd]) + if pend: + spans.append([kind, pend, opnd]) # Collapse IDA's column padding EXACTLY as the plain text does. A run of # spaces can straddle two spans, so the leading space of a span is dropped # when the previous one ended in space — otherwise the spans and `text` # disagree about the line and the row silently loses its highlighting. + # ``" ".join(txt.split())`` splits on exactly what str.isspace() calls + # whitespace, which is what the character walk this replaces tested. out, prev_space = [], False - ws = _IDATUI_WS for kind, txt, opnd in spans: - acc = ws.sub(" ", txt) - if prev_space and acc[:1] == " ": - acc = acc[1:] - if acc: - prev_space = acc[-1] == " " - out.append([kind, acc, opnd]) + core = " ".join(txt.split()) + if not core: # the span is nothing but padding + if not prev_space: + prev_space = True + out.append([kind, " ", opnd]) + continue + acc = core + if txt[0].isspace() and not prev_space: + acc = " " + acc + if txt[-1].isspace(): + acc += " " + prev_space = acc[-1] == " " + out.append([kind, acc, opnd]) while out and out[0][1] == " ": out.pop(0) while out and out[-1][1] == " ": |
