#pragma once // Step 565: Memory Inspector UI Model #include "MemorySnapshotModel.h" #include #include #include enum class MemoryValueKind { Integer, Floating, Boolean, String, Pointer, Container, Struct }; struct MemoryValueNode { std::string nodeId; std::string label; MemoryValueKind kind = MemoryValueKind::Integer; std::string valueText; bool expanded = false; std::vector children; }; struct MemoryInspectorRow { std::string nodeId; std::string label; std::string typeName; std::string valueText; int depth = 0; bool expandable = false; bool expanded = false; }; class MemoryInspectorUiModel { public: static bool createLeaf(const std::string& nodeId, const std::string& label, MemoryValueKind kind, const std::string& valueText, MemoryValueNode* outNode, std::string* error) { if (!outNode || !error) return false; error->clear(); if (nodeId.empty() || label.empty()) { *error = "node_id_or_label_missing"; return false; } if (isCompositeKind(kind)) { *error = "leaf_kind_must_be_primitive"; return false; } MemoryValueNode node; node.nodeId = nodeId; node.label = label; node.kind = kind; node.valueText = valueText; *outNode = node; return true; } static bool createComposite(const std::string& nodeId, const std::string& label, MemoryValueKind kind, MemoryValueNode* outNode, std::string* error) { if (!outNode || !error) return false; error->clear(); if (nodeId.empty() || label.empty()) { *error = "node_id_or_label_missing"; return false; } if (!isCompositeKind(kind)) { *error = "composite_kind_required"; return false; } MemoryValueNode node; node.nodeId = nodeId; node.label = label; node.kind = kind; node.expanded = true; *outNode = node; return true; } static bool appendChild(MemoryValueNode* parent, const MemoryValueNode& child, std::string* error) { if (!parent || !error) return false; error->clear(); if (!isCompositeKind(parent->kind)) { *error = "parent_is_not_composite"; return false; } if (child.nodeId.empty()) { *error = "child_node_id_missing"; return false; } parent->children.push_back(child); return true; } static std::vector collectVisibleRows(const MemoryValueNode& root, std::size_t maxRows) { std::vector rows; if (maxRows == 0) return rows; appendNodeRows(root, 0, maxRows, &rows); return rows; } static std::string typeNameForKind(MemoryValueKind kind) { switch (kind) { case MemoryValueKind::Integer: return "int"; case MemoryValueKind::Floating: return "float"; case MemoryValueKind::Boolean: return "bool"; case MemoryValueKind::String: return "string"; case MemoryValueKind::Pointer: return "pointer"; case MemoryValueKind::Container: return "container"; case MemoryValueKind::Struct: return "struct"; } return "unknown"; } static bool resolvePointerRegion(const MemorySnapshot& snapshot, const MemoryValueNode& pointerNode, const MemoryRegionRecord** outRegion, std::string* error) { if (!outRegion || !error) return false; *outRegion = nullptr; error->clear(); if (pointerNode.kind != MemoryValueKind::Pointer) { *error = "node_is_not_pointer"; return false; } std::uint64_t address = 0; if (!parseAddress(pointerNode.valueText, &address)) { *error = "pointer_value_parse_failed"; return false; } *outRegion = MemorySnapshotModel::findRegionByAddress(snapshot, address); return *outRegion != nullptr; } private: static bool isCompositeKind(MemoryValueKind kind) { return kind == MemoryValueKind::Container || kind == MemoryValueKind::Struct; } static bool parseAddress(const std::string& text, std::uint64_t* outAddress) { if (!outAddress || text.empty()) return false; try { size_t parsed = 0; int base = 10; if (text.size() > 2 && text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) base = 16; const auto value = std::stoull(text, &parsed, base); if (parsed != text.size() || value == 0) return false; *outAddress = static_cast(value); return true; } catch (...) { return false; } } static void appendNodeRows(const MemoryValueNode& node, int depth, std::size_t maxRows, std::vector* rows) { if (rows->size() >= maxRows) return; MemoryInspectorRow row; row.nodeId = node.nodeId; row.label = node.label; row.typeName = typeNameForKind(node.kind); row.valueText = node.valueText; row.depth = depth; row.expandable = isCompositeKind(node.kind); row.expanded = node.expanded; rows->push_back(row); if (!row.expandable || !node.expanded) return; for (const auto& child : node.children) { appendNodeRows(child, depth + 1, maxRows, rows); if (rows->size() >= maxRows) return; } } };