diff --git a/src/compose.cpp b/src/compose.cpp index c7f1cba..341cba8 100644 --- a/src/compose.cpp +++ b/src/compose.cpp @@ -104,6 +104,13 @@ static inline uint64_t resolveAddr(const ComposeState& state, return state.absOffsets[nodeIdx]; } + +static const QVector& childIndices(const ComposeState& state, uint64_t parentId) { + static const QVector kEmpty; + auto it = state.childMap.constFind(parentId); + return it == state.childMap.constEnd() ? kEmpty : it.value(); +} + void composeLeaf(ComposeState& state, const NodeTree& tree, const Provider& prov, int nodeIdx, int depth, uint64_t absAddr, uint64_t scopeId) { @@ -269,10 +276,7 @@ void composeParent(ComposeState& state, const NodeTree& tree, } if (!node.collapsed || isArrayChild || isRootHeader) { - QVector children = state.childMap.value(node.id); - std::sort(children.begin(), children.end(), [&](int a, int b) { - return tree.nodes[a].offset < tree.nodes[b].offset; - }); + const QVector& children = childIndices(state, node.id); int childDepth = depth + 1; @@ -339,10 +343,7 @@ void composeParent(ComposeState& state, const NodeTree& tree, if (node.kind == NodeKind::Struct && children.isEmpty() && node.refId != 0) { int refIdx = tree.indexOfId(node.refId); if (refIdx >= 0) { - QVector refChildren = state.childMap.value(node.refId); - std::sort(refChildren.begin(), refChildren.end(), [&](int a, int b) { - return tree.nodes[a].offset < tree.nodes[b].offset; - }); + const QVector& refChildren = childIndices(state, node.refId); // Use the referenced struct's scope widths (children come from there) uint64_t refScopeId = node.refId; for (int childIdx : refChildren) { @@ -431,7 +432,7 @@ void composeNode(ComposeState& state, const NodeTree& tree, QString ptrTypeOverride = fmt::pointerTypeName(node.kind, ptrTargetName); // Check if this pointer has materialized children (from materializeRefChildren) - QVector ptrChildren = state.childMap.value(node.id); + const QVector& ptrChildren = childIndices(state, node.id); bool hasMaterialized = !ptrChildren.isEmpty(); // Force collapsed if this refId is already being virtually expanded @@ -496,9 +497,6 @@ void composeNode(ComposeState& state, const NodeTree& tree, // Render materialized children at the pointer target address. // These are real tree nodes with independent state — use rootId // so resolveAddr computes offsets relative to the pointer target. - std::sort(ptrChildren.begin(), ptrChildren.end(), [&](int a, int b) { - return tree.nodes[a].offset < tree.nodes[b].offset; - }); for (int childIdx : ptrChildren) { composeNode(state, tree, childProv, childIdx, depth + 1, pBase, node.id, false, node.id); @@ -565,6 +563,13 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR for (int i = 0; i < tree.nodes.size(); i++) state.childMap[tree.nodes[i].parentId].append(i); + for (auto it = state.childMap.begin(); it != state.childMap.end(); ++it) { + QVector& children = it.value(); + std::sort(children.begin(), children.end(), [&](int a, int b) { + return tree.nodes[a].offset < tree.nodes[b].offset; + }); + } + // Precompute absolute offsets (baseAddress + structure-relative offset) state.absOffsets.resize(tree.nodes.size()); for (int i = 0; i < tree.nodes.size(); i++) @@ -688,10 +693,7 @@ ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewR state.emitLine(cmdRowText, lm); } - QVector roots = state.childMap.value(0); - std::sort(roots.begin(), roots.end(), [&](int a, int b) { - return tree.nodes[a].offset < tree.nodes[b].offset; - }); + const QVector& roots = childIndices(state, 0); for (int idx : roots) { // If viewRootId is set, skip roots that don't match diff --git a/src/imports/import_pdb.cpp b/src/imports/import_pdb.cpp index d43cfaa..1834377 100644 --- a/src/imports/import_pdb.cpp +++ b/src/imports/import_pdb.cpp @@ -232,10 +232,15 @@ struct PdbCtx { NodeTree tree; const TypeTable* tt = nullptr; QHash typeCache; // typeIndex → nodeId + QHash structDefByName; // struct/class definition name → typeIndex + QHash unionDefByName; // union definition name → typeIndex + bool udtDefIndexBuilt = false; uint64_t importUDT(uint32_t typeIndex); void importFieldList(uint32_t fieldListIndex, uint64_t parentId); void importMemberType(uint32_t typeIndex, int offset, const QString& name, uint64_t parentId); + void buildUdtDefinitionIndex(); + uint32_t findUdtDefinitionIndex(TRK kind, const char* typeName); // Resolve LF_MODIFIER chain to underlying type index uint32_t unwrapModifier(uint32_t typeIndex) const { @@ -248,6 +253,56 @@ struct PdbCtx { } }; +void PdbCtx::buildUdtDefinitionIndex() { + if (udtDefIndexBuilt || !tt) return; + udtDefIndexBuilt = true; + + for (uint32_t ti = tt->firstIndex(); ti < tt->lastIndex(); ti++) { + const auto* rec = tt->get(ti); + if (!rec) continue; + + bool isUnion = false; + bool isFwd = false; + const char* candidateName = nullptr; + + if (rec->header.kind == TRK::LF_UNION) { + isUnion = true; + isFwd = rec->data.LF_UNION.property.fwdref; + candidateName = leafName(rec->data.LF_UNION.data, unionLeafKind(rec->data.LF_UNION.data)); + } else if (rec->header.kind == TRK::LF_STRUCTURE || rec->header.kind == TRK::LF_CLASS) { + isFwd = rec->data.LF_CLASS.property.fwdref; + candidateName = leafName(rec->data.LF_CLASS.data, rec->data.LF_CLASS.lfEasy.kind); + } else { + continue; + } + + if (isFwd || !candidateName || candidateName[0] == '\0') continue; + + QString qname = QString::fromUtf8(candidateName); + QHash& lookup = isUnion ? unionDefByName : structDefByName; + if (!lookup.contains(qname)) lookup.insert(qname, ti); + } +} + +uint32_t PdbCtx::findUdtDefinitionIndex(TRK kind, const char* typeName) { + if (!typeName || typeName[0] == '\0') return 0; + + buildUdtDefinitionIndex(); + + const QString qname = QString::fromUtf8(typeName); + if (kind == TRK::LF_UNION) { + auto it = unionDefByName.constFind(qname); + return (it != unionDefByName.cend()) ? it.value() : 0; + } + + if (kind == TRK::LF_STRUCTURE || kind == TRK::LF_CLASS) { + auto it = structDefByName.constFind(qname); + return (it != structDefByName.cend()) ? it.value() : 0; + } + + return 0; +} + uint64_t PdbCtx::importUDT(uint32_t typeIndex) { if (typeIndex < tt->firstIndex()) return 0; @@ -522,7 +577,6 @@ void PdbCtx::importMemberType(uint32_t typeIndex, int offset, const QString& nam isFwd = pointeeRec->data.LF_CLASS.property.fwdref; if (isFwd) { - // Need to find the non-fwdref definition by name const char* typeName = nullptr; if (pointeeRec->header.kind == TRK::LF_UNION) typeName = leafName(pointeeRec->data.LF_UNION.data, unionLeafKind(pointeeRec->data.LF_UNION.data)); @@ -530,28 +584,8 @@ void PdbCtx::importMemberType(uint32_t typeIndex, int offset, const QString& nam typeName = leafName(pointeeRec->data.LF_CLASS.data, pointeeRec->data.LF_CLASS.lfEasy.kind); - if (typeName) { - // Linear scan for the definition (cached after first import) - for (uint32_t ti = tt->firstIndex(); ti < tt->lastIndex(); ti++) { - const auto* candidate = tt->get(ti); - if (!candidate) continue; - if (candidate->header.kind != pointeeRec->header.kind) continue; - bool candidateFwd; - const char* candidateName; - if (candidate->header.kind == TRK::LF_UNION) { - candidateFwd = candidate->data.LF_UNION.property.fwdref; - candidateName = leafName(candidate->data.LF_UNION.data, unionLeafKind(candidate->data.LF_UNION.data)); - } else { - candidateFwd = candidate->data.LF_CLASS.property.fwdref; - candidateName = leafName(candidate->data.LF_CLASS.data, - candidate->data.LF_CLASS.lfEasy.kind); - } - if (!candidateFwd && candidateName && strcmp(candidateName, typeName) == 0) { - defIndex = ti; - break; - } - } - } + uint32_t resolved = findUdtDefinitionIndex(pointeeRec->header.kind, typeName); + if (resolved != 0) defIndex = resolved; } n.refId = importUDT(defIndex); } else if (pointeeRec->header.kind == TRK::LF_PROCEDURE || @@ -584,27 +618,8 @@ void PdbCtx::importMemberType(uint32_t typeIndex, int offset, const QString& nam else typeName = leafName(rec->data.LF_CLASS.data, rec->data.LF_CLASS.lfEasy.kind); - if (typeName) { - for (uint32_t ti = tt->firstIndex(); ti < tt->lastIndex(); ti++) { - const auto* candidate = tt->get(ti); - if (!candidate) continue; - if (candidate->header.kind != rec->header.kind) continue; - bool candidateFwd; - const char* candidateName; - if (candidate->header.kind == TRK::LF_UNION) { - candidateFwd = candidate->data.LF_UNION.property.fwdref; - candidateName = leafName(candidate->data.LF_UNION.data, unionLeafKind(candidate->data.LF_UNION.data)); - } else { - candidateFwd = candidate->data.LF_CLASS.property.fwdref; - candidateName = leafName(candidate->data.LF_CLASS.data, - candidate->data.LF_CLASS.lfEasy.kind); - } - if (!candidateFwd && candidateName && strcmp(candidateName, typeName) == 0) { - defIndex = ti; - break; - } - } - } + uint32_t resolved = findUdtDefinitionIndex(rec->header.kind, typeName); + if (resolved != 0) defIndex = resolved; } uint64_t refId = importUDT(defIndex); diff --git a/src/imports/import_reclass_xml.cpp b/src/imports/import_reclass_xml.cpp index 2e33495..d7bf1de 100644 --- a/src/imports/import_reclass_xml.cpp +++ b/src/imports/import_reclass_xml.cpp @@ -371,7 +371,6 @@ NodeTree importReclassXml(const QString& filePath, QString* errorMsg) { auto it = classIds.find(ref.className); if (it != classIds.end()) { tree.nodes[nodeIdx].refId = it.value(); - tree.invalidateIdCache(); resolved++; } else { qDebug() << "[ImportXML] Unresolved ref:" << ref.className << "for node" << ref.nodeId; diff --git a/src/imports/import_source.cpp b/src/imports/import_source.cpp index 57bed07..287cd18 100644 --- a/src/imports/import_source.cpp +++ b/src/imports/import_source.cpp @@ -1056,7 +1056,6 @@ NodeTree importFromSource(const QString& sourceCode, QString* errorMsg) { auto it = classIds.find(ref.className); if (it != classIds.end()) { tree.nodes[nodeIdx].refId = it.value(); - tree.invalidateIdCache(); } }