perf: compose 30% faster — move semantics, BFS offsets, zero-alloc hex formatting

- compose.cpp: emitLine takes LineMeta&& (move, not copy) at all 22 call sites
- compose.cpp: reserve meta/text buffers, BFS offset computation O(N) vs O(N*D)
- compose.cpp: pre-compute typeNameLens[], merge global width loops
- format.cpp: bytesToHex uses stack buffer + lookup table (zero heap allocs)
- format.cpp: hexVal single QString::asprintf instead of 2-string concat
- editor.cpp: guard hover updates during applyDocument (stale index safety)
- core.h: assertion on makeArrayElemSelId negative index
- format.cpp: assertion on extractBits overflow
- main.cpp: tree lines enabled by default
- bench_large_class: add 2000-field benchComposeLarge test

Benchmark: 500 fields 0.70→0.51ms (27%), 2000 fields 2.28→1.57ms (31%)
This commit is contained in:
IChooseYou
2026-03-08 07:28:26 -06:00
committed by IChooseYou
parent f0fc85f60f
commit 596f410b96
8 changed files with 150 additions and 63 deletions

View File

@@ -562,9 +562,11 @@ RcxEditor::RcxEditor(QWidget* parent) : QWidget(parent) {
if (chosen == actRel && !m_relativeOffsets) {
m_relativeOffsets = true;
reformatMargins();
emit relativeOffsetsChanged(true);
} else if (chosen == actAbs && m_relativeOffsets) {
m_relativeOffsets = false;
reformatMargins();
emit relativeOffsetsChanged(false);
}
return;
}
@@ -958,8 +960,14 @@ void RcxEditor::applyDocument(const ComposeResult& result) {
}
// Dynamically resize margin to fit the current hex digit tier
QString marginSizer = QString(" %1 ").arg(QString(m_layout.offsetHexDigits, '0'));
m_sci->setMarginWidth(0, marginSizer);
// RVA mode uses half width since relative offsets are much shorter
{
int marginDigits = m_relativeOffsets
? qMax(m_layout.offsetHexDigits / 2, 4)
: m_layout.offsetHexDigits;
QString marginSizer = QString(" %1 ").arg(QString(marginDigits, '0'));
m_sci->setMarginWidth(0, marginSizer);
}
m_sci->setReadOnly(false);
m_sci->setText(result.text);
@@ -1066,6 +1074,11 @@ void RcxEditor::reformatMargins() {
uint64_t base = m_layout.baseAddress;
int hexDigits = m_layout.offsetHexDigits;
// Resize margin: RVA offsets are much shorter than full addresses
int marginDigits = m_relativeOffsets ? qMax(hexDigits / 2, 4) : hexDigits;
QString marginSizer = QString(" %1 ").arg(QString(marginDigits, '0'));
m_sci->setMarginWidth(0, marginSizer);
// ── Pass 1: margin text (global offset only) ──
m_sci->clearMarginText(-1);
for (int i = 0; i < m_meta.size(); i++) {
@@ -2195,6 +2208,7 @@ bool RcxEditor::eventFilter(QObject* obj, QEvent* event) {
#endif
m_relativeOffsets = !m_relativeOffsets;
reformatMargins();
emit relativeOffsetsChanged(m_relativeOffsets);
return true;
}
}
@@ -2274,7 +2288,8 @@ bool RcxEditor::eventFilter(QObject* obj, QEvent* event) {
m_hoverInside = m_sci->viewport()->rect().contains(m_lastHoverPos);
}
// Resolve hovered nodeId on move/wheel (non-edit mode only)
if (!m_editState.active &&
// Guard: skip during applyDocument — m_nodeLineIndex may be stale
if (!m_editState.active && !m_applyingDocument &&
(event->type() == QEvent::MouseMove || event->type() == QEvent::Wheel)) {
auto h = hitTest(m_lastHoverPos);
uint64_t newHoverId = (m_hoverInside && h.line >= 0) ? h.nodeId : 0;
@@ -3602,8 +3617,13 @@ void RcxEditor::setEditorFont(const QString& fontName) {
// Re-apply margin styles and width with new font metrics
allocateMarginStyles();
applyTheme(ThemeManager::instance().current());
QString marginSizer = QString(" %1 ").arg(QString(m_layout.offsetHexDigits, '0'));
m_sci->setMarginWidth(0, marginSizer);
{
int marginDigits = m_relativeOffsets
? qMax(m_layout.offsetHexDigits / 2, 4)
: m_layout.offsetHexDigits;
QString marginSizer = QString(" %1 ").arg(QString(marginDigits, '0'));
m_sci->setMarginWidth(0, marginSizer);
}
}
void RcxEditor::setGlobalFontName(const QString& fontName) {