Finalize library bootstrap status and stabilize async asset regressions

This commit is contained in:
2026-04-04 19:44:59 +08:00
parent 013e5a73b9
commit bcef1f145b
25 changed files with 3415 additions and 81 deletions

View File

@@ -0,0 +1,878 @@
#include <XCEngine/Resources/UI/UIDocumentCompiler.h>
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <string>
#include <unordered_set>
namespace XCEngine {
namespace Resources {
namespace fs = std::filesystem;
namespace {
Containers::String ToContainersString(const std::string& value) {
return Containers::String(value.c_str());
}
std::string ToStdString(const Containers::String& value) {
return std::string(value.CStr());
}
std::string ToLowerCopy(std::string value) {
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char ch) {
return static_cast<char>(std::tolower(ch));
});
return value;
}
Containers::String NormalizePathString(const fs::path& path) {
return Containers::String(path.lexically_normal().generic_string().c_str());
}
Containers::String FormatDiagnosticMessage(const Containers::String& path,
const UIDocumentSourceLocation& location,
const Containers::String& message) {
return path +
":" +
Containers::String(std::to_string(location.line).c_str()) +
":" +
Containers::String(std::to_string(location.column).c_str()) +
": " +
message;
}
void WriteString(std::ofstream& stream, const Containers::String& value) {
const Core::uint32 length = static_cast<Core::uint32>(value.Length());
stream.write(reinterpret_cast<const char*>(&length), sizeof(length));
if (length > 0) {
stream.write(value.CStr(), length);
}
}
bool ReadString(std::ifstream& stream, Containers::String& outValue) {
Core::uint32 length = 0;
stream.read(reinterpret_cast<char*>(&length), sizeof(length));
if (!stream) {
return false;
}
if (length == 0) {
outValue.Clear();
return true;
}
std::string buffer(length, '\0');
stream.read(buffer.data(), length);
if (!stream) {
return false;
}
outValue = ToContainersString(buffer);
return true;
}
bool WriteNode(std::ofstream& stream, const UIDocumentNode& node) {
WriteString(stream, node.tagName);
UIDocumentArtifactNodeHeader header;
header.attributeCount = static_cast<Core::uint32>(node.attributes.Size());
header.childCount = static_cast<Core::uint32>(node.children.Size());
header.line = node.location.line;
header.column = node.location.column;
header.selfClosing = node.selfClosing ? 1u : 0u;
stream.write(reinterpret_cast<const char*>(&header), sizeof(header));
if (!stream) {
return false;
}
for (const UIDocumentAttribute& attribute : node.attributes) {
WriteString(stream, attribute.name);
WriteString(stream, attribute.value);
if (!stream) {
return false;
}
}
for (const UIDocumentNode& child : node.children) {
if (!WriteNode(stream, child)) {
return false;
}
}
return static_cast<bool>(stream);
}
bool ReadNode(std::ifstream& stream, UIDocumentNode& outNode) {
if (!ReadString(stream, outNode.tagName)) {
return false;
}
UIDocumentArtifactNodeHeader header;
stream.read(reinterpret_cast<char*>(&header), sizeof(header));
if (!stream) {
return false;
}
outNode.location.line = header.line;
outNode.location.column = header.column;
outNode.selfClosing = header.selfClosing != 0;
outNode.attributes.Clear();
outNode.children.Clear();
outNode.attributes.Reserve(header.attributeCount);
outNode.children.Reserve(header.childCount);
for (Core::uint32 index = 0; index < header.attributeCount; ++index) {
UIDocumentAttribute attribute;
if (!ReadString(stream, attribute.name) ||
!ReadString(stream, attribute.value)) {
return false;
}
outNode.attributes.PushBack(std::move(attribute));
}
for (Core::uint32 index = 0; index < header.childCount; ++index) {
UIDocumentNode child;
if (!ReadNode(stream, child)) {
return false;
}
outNode.children.PushBack(std::move(child));
}
return true;
}
bool IsNameStartChar(char ch) {
return std::isalpha(static_cast<unsigned char>(ch)) != 0 ||
ch == '_' ||
ch == ':';
}
bool IsNameChar(char ch) {
return std::isalnum(static_cast<unsigned char>(ch)) != 0 ||
ch == '_' ||
ch == '-' ||
ch == ':' ||
ch == '.';
}
bool LooksLikeUIDocumentReference(const Containers::String& value) {
const std::string trimmed = ToLowerCopy(ToStdString(value.Trim()));
return trimmed.size() > 5 &&
(trimmed.size() >= 5 && trimmed.rfind(".xcui") == trimmed.size() - 5 ||
trimmed.size() >= 8 && trimmed.rfind(".xctheme") == trimmed.size() - 8 ||
trimmed.size() >= 9 && trimmed.rfind(".xcschema") == trimmed.size() - 9);
}
void AppendUniqueDependency(const Containers::String& dependencyPath,
std::unordered_set<std::string>& seenDependencies,
Containers::Array<Containers::String>& outDependencies) {
const std::string key = ToLowerCopy(ToStdString(dependencyPath));
if (seenDependencies.insert(key).second) {
outDependencies.PushBack(dependencyPath);
}
}
bool CollectUIDocumentDependencies(const fs::path& sourcePath,
const UIDocumentNode& node,
Containers::Array<Containers::String>& outDependencies,
Containers::Array<UIDocumentDiagnostic>& inOutDiagnostics,
Containers::String& outErrorMessage,
std::unordered_set<std::string>& seenDependencies) {
const fs::path sourceDirectory = sourcePath.parent_path();
for (const UIDocumentAttribute& attribute : node.attributes) {
if (!LooksLikeUIDocumentReference(attribute.value)) {
continue;
}
const Containers::String trimmedValue = attribute.value.Trim();
fs::path dependencyPath(trimmedValue.CStr());
if (!dependencyPath.is_absolute()) {
dependencyPath = sourceDirectory / dependencyPath;
}
dependencyPath = dependencyPath.lexically_normal();
if (!fs::exists(dependencyPath) || fs::is_directory(dependencyPath)) {
UIDocumentDiagnostic diagnostic;
diagnostic.severity = UIDocumentDiagnosticSeverity::Error;
diagnostic.location = node.location;
diagnostic.message =
Containers::String("Referenced UI document was not found: ") + trimmedValue;
inOutDiagnostics.PushBack(diagnostic);
outErrorMessage = FormatDiagnosticMessage(
NormalizePathString(sourcePath),
diagnostic.location,
diagnostic.message);
return false;
}
AppendUniqueDependency(
NormalizePathString(dependencyPath),
seenDependencies,
outDependencies);
}
for (const UIDocumentNode& child : node.children) {
if (!CollectUIDocumentDependencies(
sourcePath,
child,
outDependencies,
inOutDiagnostics,
outErrorMessage,
seenDependencies)) {
return false;
}
}
return true;
}
struct SourceFileReadResult {
bool succeeded = false;
fs::path resolvedPath;
std::string content;
Containers::String errorMessage;
};
bool ReadUIDocumentSourceFile(const Containers::String& path, SourceFileReadResult& outResult) {
outResult = SourceFileReadResult();
if (path.Empty()) {
outResult.errorMessage = "UI document path is empty.";
return false;
}
auto tryReadFile = [&](const fs::path& filePath) -> bool {
std::ifstream input(filePath, std::ios::binary);
if (!input.is_open()) {
return false;
}
std::ostringstream buffer;
buffer << input.rdbuf();
outResult.succeeded = static_cast<bool>(input) || input.eof();
outResult.resolvedPath = filePath.lexically_normal();
outResult.content = buffer.str();
return outResult.succeeded;
};
const fs::path requestedPath(path.CStr());
if (tryReadFile(requestedPath)) {
return true;
}
if (!requestedPath.is_absolute()) {
const Containers::String resourceRoot = ResourceManager::Get().GetResourceRoot();
if (!resourceRoot.Empty() &&
tryReadFile(fs::path(resourceRoot.CStr()) / requestedPath)) {
return true;
}
}
outResult.errorMessage = Containers::String("Unable to read UI document source file: ") + path;
return false;
}
class UIDocumentParser {
public:
UIDocumentParser(const std::string& source,
const fs::path& resolvedPath,
const Containers::String& expectedRootTag,
UIDocumentKind kind)
: m_source(source)
, m_resolvedPath(resolvedPath)
, m_expectedRootTag(expectedRootTag)
, m_kind(kind) {
}
bool Parse(UIDocumentCompileResult& outResult) {
outResult = UIDocumentCompileResult();
outResult.document.kind = m_kind;
outResult.document.sourcePath = NormalizePathString(m_resolvedPath);
if (HasUtf8Bom()) {
Advance();
Advance();
Advance();
}
SkipWhitespaceAndTrivia();
if (AtEnd()) {
AddError(CurrentLocation(), "UI document is empty.");
return Finalize(outResult, false);
}
if (Peek() != '<') {
AddError(CurrentLocation(), "Expected '<' at the beginning of the UI document.");
return Finalize(outResult, false);
}
UIDocumentNode rootNode;
if (!ParseElement(rootNode)) {
return Finalize(outResult, false);
}
SkipWhitespaceAndTrivia();
if (!AtEnd()) {
AddError(CurrentLocation(), "Unexpected trailing content after the root element.");
return Finalize(outResult, false);
}
if (!m_expectedRootTag.Empty() &&
rootNode.tagName != m_expectedRootTag) {
AddError(
rootNode.location,
Containers::String("Expected root element <") +
m_expectedRootTag +
">, found <" +
rootNode.tagName +
">.");
return Finalize(outResult, false);
}
outResult.document.rootNode = std::move(rootNode);
if (const UIDocumentAttribute* nameAttribute = outResult.document.rootNode.FindAttribute("name");
nameAttribute != nullptr && !nameAttribute->value.Empty()) {
outResult.document.displayName = nameAttribute->value;
}
std::unordered_set<std::string> seenDependencies;
if (!CollectUIDocumentDependencies(
m_resolvedPath,
outResult.document.rootNode,
outResult.document.dependencies,
outResult.document.diagnostics,
m_errorMessage,
seenDependencies)) {
return Finalize(outResult, false);
}
outResult.document.valid = true;
outResult.succeeded = true;
return Finalize(outResult, true);
}
private:
bool ParseElement(UIDocumentNode& outNode) {
const UIDocumentSourceLocation elementLocation = CurrentLocation();
if (!Consume('<', "Expected '<' to start an element.")) {
return false;
}
if (AtEnd()) {
AddError(elementLocation, "Unexpected end of file after '<'.");
return false;
}
if (Peek() == '/' || Peek() == '!' || Peek() == '?') {
AddError(elementLocation, "Unexpected token while parsing an element.");
return false;
}
Containers::String tagName;
if (!ParseName(tagName, "Expected an element tag name.")) {
return false;
}
outNode.location = elementLocation;
outNode.tagName = tagName;
while (!AtEnd()) {
SkipWhitespace();
if (StartsWith("/>")) {
Advance();
Advance();
outNode.selfClosing = true;
return true;
}
if (Peek() == '>') {
Advance();
break;
}
UIDocumentAttribute attribute;
if (!ParseAttribute(attribute)) {
return false;
}
outNode.attributes.PushBack(std::move(attribute));
}
if (AtEnd()) {
AddError(elementLocation, "Unexpected end of file before element start tag was closed.");
return false;
}
while (true) {
SkipWhitespaceAndTrivia();
if (AtEnd()) {
AddError(
elementLocation,
Containers::String("Unexpected end of file while parsing <") +
outNode.tagName +
">.");
return false;
}
if (StartsWith("</")) {
Advance();
Advance();
Containers::String closeName;
if (!ParseName(closeName, "Expected a closing element tag name.")) {
return false;
}
SkipWhitespace();
if (!Consume('>', "Expected '>' to finish the closing tag.")) {
return false;
}
if (closeName != outNode.tagName) {
AddError(
CurrentLocation(),
Containers::String("Closing tag </") +
closeName +
"> does not match <" +
outNode.tagName +
">.");
return false;
}
return true;
}
if (Peek() != '<') {
AddError(CurrentLocation(), "Text nodes are not supported in XCUI documents.");
return false;
}
UIDocumentNode childNode;
if (!ParseElement(childNode)) {
return false;
}
outNode.children.PushBack(std::move(childNode));
}
}
bool ParseAttribute(UIDocumentAttribute& outAttribute) {
if (!ParseName(outAttribute.name, "Expected an attribute name.")) {
return false;
}
SkipWhitespace();
if (!Consume('=', "Expected '=' after the attribute name.")) {
return false;
}
SkipWhitespace();
return ParseQuotedString(outAttribute.value);
}
bool ParseQuotedString(Containers::String& outValue) {
if (AtEnd()) {
AddError(CurrentLocation(), "Expected a quoted attribute value.");
return false;
}
const char quote = Peek();
if (quote != '"' && quote != '\'') {
AddError(CurrentLocation(), "Attribute values must use single or double quotes.");
return false;
}
Advance();
std::string value;
while (!AtEnd() && Peek() != quote) {
value.push_back(Peek());
Advance();
}
if (AtEnd()) {
AddError(CurrentLocation(), "Unterminated attribute value.");
return false;
}
Advance();
outValue = ToContainersString(value);
return true;
}
bool ParseName(Containers::String& outName, const char* errorMessage) {
if (AtEnd() || !IsNameStartChar(Peek())) {
AddError(CurrentLocation(), errorMessage);
return false;
}
std::string name;
name.push_back(Peek());
Advance();
while (!AtEnd() && IsNameChar(Peek())) {
name.push_back(Peek());
Advance();
}
outName = ToContainersString(name);
return true;
}
void SkipWhitespaceAndTrivia() {
while (!AtEnd()) {
SkipWhitespace();
if (StartsWith("<!--")) {
if (!SkipComment()) {
return;
}
continue;
}
if (StartsWith("<?")) {
if (!SkipProcessingInstruction()) {
return;
}
continue;
}
break;
}
}
void SkipWhitespace() {
while (!AtEnd() &&
std::isspace(static_cast<unsigned char>(Peek())) != 0) {
Advance();
}
}
bool SkipComment() {
const UIDocumentSourceLocation location = CurrentLocation();
Advance();
Advance();
Advance();
Advance();
while (!AtEnd() && !StartsWith("-->")) {
Advance();
}
if (AtEnd()) {
AddError(location, "Unterminated XML comment.");
return false;
}
Advance();
Advance();
Advance();
return true;
}
bool SkipProcessingInstruction() {
const UIDocumentSourceLocation location = CurrentLocation();
Advance();
Advance();
while (!AtEnd() && !StartsWith("?>")) {
Advance();
}
if (AtEnd()) {
AddError(location, "Unterminated processing instruction.");
return false;
}
Advance();
Advance();
return true;
}
bool Consume(char expected, const char* errorMessage) {
if (AtEnd() || Peek() != expected) {
AddError(CurrentLocation(), errorMessage);
return false;
}
Advance();
return true;
}
void AddError(const UIDocumentSourceLocation& location, const Containers::String& message) {
UIDocumentDiagnostic diagnostic;
diagnostic.severity = UIDocumentDiagnosticSeverity::Error;
diagnostic.location = location;
diagnostic.message = message;
m_diagnostics.PushBack(diagnostic);
if (m_errorMessage.Empty()) {
m_errorMessage = FormatDiagnosticMessage(NormalizePathString(m_resolvedPath), location, message);
}
}
bool Finalize(UIDocumentCompileResult& outResult, bool succeeded) {
Containers::Array<UIDocumentDiagnostic> combinedDiagnostics;
combinedDiagnostics.Reserve(
m_diagnostics.Size() +
outResult.document.diagnostics.Size());
for (const UIDocumentDiagnostic& diagnostic : m_diagnostics) {
combinedDiagnostics.PushBack(diagnostic);
}
for (const UIDocumentDiagnostic& diagnostic : outResult.document.diagnostics) {
combinedDiagnostics.PushBack(diagnostic);
}
outResult.document.diagnostics = std::move(combinedDiagnostics);
outResult.succeeded = succeeded;
outResult.errorMessage = succeeded ? Containers::String() : m_errorMessage;
if (!succeeded) {
outResult.document.valid = false;
}
return succeeded;
}
bool HasUtf8Bom() const {
return m_source.size() >= 3 &&
static_cast<unsigned char>(m_source[0]) == 0xEF &&
static_cast<unsigned char>(m_source[1]) == 0xBB &&
static_cast<unsigned char>(m_source[2]) == 0xBF;
}
bool StartsWith(const char* text) const {
const size_t length = std::strlen(text);
return m_offset + length <= m_source.size() &&
m_source.compare(m_offset, length, text) == 0;
}
bool AtEnd() const {
return m_offset >= m_source.size();
}
char Peek() const {
return AtEnd() ? '\0' : m_source[m_offset];
}
void Advance() {
if (AtEnd()) {
return;
}
if (m_source[m_offset] == '\n') {
++m_line;
m_column = 1;
} else {
++m_column;
}
++m_offset;
}
UIDocumentSourceLocation CurrentLocation() const {
UIDocumentSourceLocation location;
location.line = m_line;
location.column = m_column;
return location;
}
const std::string& m_source;
fs::path m_resolvedPath;
Containers::String m_expectedRootTag;
UIDocumentKind m_kind = UIDocumentKind::View;
size_t m_offset = 0;
Core::uint32 m_line = 1;
Core::uint32 m_column = 1;
Containers::Array<UIDocumentDiagnostic> m_diagnostics;
Containers::String m_errorMessage;
};
} // namespace
bool CompileUIDocument(const UIDocumentCompileRequest& request,
UIDocumentCompileResult& outResult) {
outResult = UIDocumentCompileResult();
SourceFileReadResult readResult;
if (!ReadUIDocumentSourceFile(request.path, readResult)) {
outResult.errorMessage = readResult.errorMessage;
outResult.succeeded = false;
outResult.document.kind = request.kind;
return false;
}
const Containers::String expectedRootTag =
request.expectedRootTag.Empty()
? GetUIDocumentDefaultRootTag(request.kind)
: request.expectedRootTag;
UIDocumentParser parser(
readResult.content,
readResult.resolvedPath,
expectedRootTag,
request.kind);
return parser.Parse(outResult);
}
bool WriteUIDocumentArtifact(const Containers::String& artifactPath,
const UIDocumentCompileResult& compileResult,
Containers::String* outErrorMessage) {
if (!compileResult.succeeded || !compileResult.document.valid) {
if (outErrorMessage != nullptr) {
*outErrorMessage = "UI document compile result is invalid.";
}
return false;
}
std::ofstream output(artifactPath.CStr(), std::ios::binary | std::ios::trunc);
if (!output.is_open()) {
if (outErrorMessage != nullptr) {
*outErrorMessage = Containers::String("Unable to write UI document artifact: ") + artifactPath;
}
return false;
}
UIDocumentArtifactFileHeader header;
header.kind = static_cast<Core::uint32>(compileResult.document.kind);
header.dependencyCount = static_cast<Core::uint32>(compileResult.document.dependencies.Size());
header.diagnosticCount = static_cast<Core::uint32>(compileResult.document.diagnostics.Size());
output.write(reinterpret_cast<const char*>(&header), sizeof(header));
if (!output) {
if (outErrorMessage != nullptr) {
*outErrorMessage = Containers::String("Failed to write UI document artifact header: ") + artifactPath;
}
return false;
}
WriteString(output, compileResult.document.sourcePath);
WriteString(output, compileResult.document.displayName);
if (!WriteNode(output, compileResult.document.rootNode)) {
if (outErrorMessage != nullptr) {
*outErrorMessage = Containers::String("Failed to write UI document node tree: ") + artifactPath;
}
return false;
}
for (const Containers::String& dependency : compileResult.document.dependencies) {
WriteString(output, dependency);
if (!output) {
if (outErrorMessage != nullptr) {
*outErrorMessage = Containers::String("Failed to write UI document dependencies: ") + artifactPath;
}
return false;
}
}
for (const UIDocumentDiagnostic& diagnostic : compileResult.document.diagnostics) {
UIDocumentArtifactDiagnosticHeader diagnosticHeader;
diagnosticHeader.severity = static_cast<Core::uint32>(diagnostic.severity);
diagnosticHeader.line = diagnostic.location.line;
diagnosticHeader.column = diagnostic.location.column;
output.write(reinterpret_cast<const char*>(&diagnosticHeader), sizeof(diagnosticHeader));
WriteString(output, diagnostic.message);
if (!output) {
if (outErrorMessage != nullptr) {
*outErrorMessage = Containers::String("Failed to write UI document diagnostics: ") + artifactPath;
}
return false;
}
}
return true;
}
bool LoadUIDocumentArtifact(const Containers::String& artifactPath,
UIDocumentKind expectedKind,
UIDocumentCompileResult& outResult) {
outResult = UIDocumentCompileResult();
std::ifstream input(artifactPath.CStr(), std::ios::binary);
if (!input.is_open()) {
outResult.errorMessage = Containers::String("Unable to open UI document artifact: ") + artifactPath;
return false;
}
UIDocumentArtifactFileHeader header;
input.read(reinterpret_cast<char*>(&header), sizeof(header));
if (!input) {
outResult.errorMessage = Containers::String("Failed to read UI document artifact header: ") + artifactPath;
return false;
}
const UIDocumentArtifactFileHeader expectedHeader;
if (std::memcmp(header.magic, expectedHeader.magic, sizeof(header.magic)) != 0) {
outResult.errorMessage = Containers::String("Invalid UI document artifact magic: ") + artifactPath;
return false;
}
if (header.schemaVersion != kUIDocumentArtifactSchemaVersion) {
outResult.errorMessage = Containers::String("Unsupported UI document artifact schema version: ") + artifactPath;
return false;
}
if (header.kind != static_cast<Core::uint32>(expectedKind)) {
outResult.errorMessage = Containers::String("UI document artifact kind mismatch: ") + artifactPath;
return false;
}
outResult.document.kind = expectedKind;
if (!ReadString(input, outResult.document.sourcePath) ||
!ReadString(input, outResult.document.displayName) ||
!ReadNode(input, outResult.document.rootNode)) {
outResult.errorMessage = Containers::String("Failed to read UI document artifact body: ") + artifactPath;
return false;
}
outResult.document.dependencies.Clear();
outResult.document.dependencies.Reserve(header.dependencyCount);
for (Core::uint32 index = 0; index < header.dependencyCount; ++index) {
Containers::String dependency;
if (!ReadString(input, dependency)) {
outResult.errorMessage = Containers::String("Failed to read UI document artifact dependencies: ") + artifactPath;
return false;
}
outResult.document.dependencies.PushBack(std::move(dependency));
}
outResult.document.diagnostics.Clear();
outResult.document.diagnostics.Reserve(header.diagnosticCount);
for (Core::uint32 index = 0; index < header.diagnosticCount; ++index) {
UIDocumentArtifactDiagnosticHeader diagnosticHeader;
input.read(reinterpret_cast<char*>(&diagnosticHeader), sizeof(diagnosticHeader));
if (!input) {
outResult.errorMessage = Containers::String("Failed to read UI document artifact diagnostic header: ") + artifactPath;
return false;
}
UIDocumentDiagnostic diagnostic;
diagnostic.severity = static_cast<UIDocumentDiagnosticSeverity>(diagnosticHeader.severity);
diagnostic.location.line = diagnosticHeader.line;
diagnostic.location.column = diagnosticHeader.column;
if (!ReadString(input, diagnostic.message)) {
outResult.errorMessage = Containers::String("Failed to read UI document artifact diagnostic message: ") + artifactPath;
return false;
}
outResult.document.diagnostics.PushBack(std::move(diagnostic));
}
outResult.document.valid = true;
outResult.succeeded = true;
return true;
}
Containers::String GetUIDocumentDefaultRootTag(UIDocumentKind kind) {
switch (kind) {
case UIDocumentKind::View:
return "View";
case UIDocumentKind::Theme:
return "Theme";
case UIDocumentKind::Schema:
return "Schema";
default:
return "View";
}
}
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,61 @@
#include <XCEngine/Resources/UI/UIDocuments.h>
namespace XCEngine {
namespace Resources {
namespace {
size_t MeasureNodeMemorySize(const UIDocumentNode& node) {
size_t size = sizeof(UIDocumentNode) + node.tagName.Length();
for (const UIDocumentAttribute& attribute : node.attributes) {
size += sizeof(UIDocumentAttribute);
size += attribute.name.Length();
size += attribute.value.Length();
}
for (const UIDocumentNode& child : node.children) {
size += MeasureNodeMemorySize(child);
}
return size;
}
size_t MeasureDiagnosticMemorySize(const UIDocumentDiagnostic& diagnostic) {
return sizeof(UIDocumentDiagnostic) + diagnostic.message.Length();
}
} // namespace
void UIDocumentResource::Release() {
m_document.Clear();
SetInvalid();
m_memorySize = 0;
}
void UIDocumentResource::SetDocumentModel(const UIDocumentModel& document) {
m_document = document;
RecalculateMemorySize();
}
void UIDocumentResource::SetDocumentModel(UIDocumentModel&& document) {
m_document = std::move(document);
RecalculateMemorySize();
}
void UIDocumentResource::RecalculateMemorySize() {
size_t size = sizeof(*this);
size += m_document.sourcePath.Length();
size += m_document.displayName.Length();
size += MeasureNodeMemorySize(m_document.rootNode);
for (const Containers::String& dependency : m_document.dependencies) {
size += sizeof(Containers::String) + dependency.Length();
}
for (const UIDocumentDiagnostic& diagnostic : m_document.diagnostics) {
size += MeasureDiagnosticMemorySize(diagnostic);
}
m_memorySize = size;
}
} // namespace Resources
} // namespace XCEngine