From 394896a3c95d845898d5e03211cc1ad47384057b Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 23 Oct 2020 15:55:44 +0100 Subject: [PATCH] Hide list of children completely in structured data * This makes it easier to enforce object ownership, as well as gives us options for e.g. generating structure data on demand lazily. --- .../Code/Interface/PersistantConfig.cpp | 4 +- qrenderdoc/Code/QRDUtils.cpp | 11 +- qrenderdoc/Code/QRDUtils.h | 3 +- qrenderdoc/Code/pyrenderdoc/renderdoc.i | 4 + .../Code/pyrenderdoc/structured_conversion.h | 18 +- qrenderdoc/Windows/APIInspector.cpp | 2 +- qrenderdoc/Windows/Dialogs/ConfigEditor.cpp | 25 +-- qrenderdoc/Windows/Dialogs/SettingsDialog.cpp | 6 +- qrenderdoc/Windows/ResourceInspector.cpp | 2 +- renderdoc/api/replay/structured_data.h | 119 ++++++++++-- renderdoc/core/settings.cpp | 34 ++-- renderdoc/serialise/codecs/xml_codec.cpp | 34 ++-- renderdoc/serialise/serialiser.cpp | 17 +- renderdoc/serialise/serialiser.h | 102 +++++----- renderdoc/serialise/serialiser_tests.cpp | 182 +++++++++--------- 15 files changed, 317 insertions(+), 246 deletions(-) diff --git a/qrenderdoc/Code/Interface/PersistantConfig.cpp b/qrenderdoc/Code/Interface/PersistantConfig.cpp index 644a096f6..00d96e0e6 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.cpp +++ b/qrenderdoc/Code/Interface/PersistantConfig.cpp @@ -268,10 +268,10 @@ void PersistantConfig::applyValues(const QVariantMap &values) SDObject *debug = RENDERDOC_SetConfigSetting("DXBC.Debug.SearchDirPaths"); debug->DeleteChildren(); - debug->data.children.resize(searchPaths.size()); + debug->ReserveChildren(searchPaths.size()); for(int i = 0; i < searchPaths.size(); i++) - debug->data.children[i] = makeSDString("$el", searchPaths[i]); + debug->AddAndOwnChild(makeSDString("$el", searchPaths[i])); } if(settings.contains(lit("d3d12ShaderDebugging"))) diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index 14cddade5..4dac52181 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -1332,17 +1332,16 @@ void CombineUsageEvents(ICaptureContext &ctx, const rdcarray &usage, callback(start, end, us); } -void addStructuredObjects(RDTreeWidgetItem *parent, const StructuredObjectList &objs, - bool parentIsArray) +void addStructuredChildren(RDTreeWidgetItem *parent, const SDObject &parentObj) { - for(const SDObject *obj : objs) + for(const SDObject *obj : parentObj) { if(obj->type.flags & SDTypeFlags::Hidden) continue; QVariant param; - if(parentIsArray) + if(parentObj.type.basetype == SDBasic::Array) param = QFormatStr("[%1]").arg(parent->childCount()); else param = obj->name; @@ -1377,11 +1376,11 @@ void addStructuredObjects(RDTreeWidgetItem *parent, const StructuredObjectList & case SDBasic::Chunk: case SDBasic::Struct: param = QFormatStr("%1()").arg(obj->type.name); - addStructuredObjects(item, obj->data.children, false); + addStructuredChildren(item, *obj); break; case SDBasic::Array: param = QFormatStr("%1[]").arg(obj->type.name); - addStructuredObjects(item, obj->data.children, true); + addStructuredChildren(item, *obj); break; case SDBasic::Null: param = lit("NULL"); break; case SDBasic::Buffer: param = lit("(%1 bytes)").arg(obj->type.byteSize); break; diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 2c792deb3..3a3b9c380 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -126,8 +126,7 @@ void CombineUsageEvents( class RDTreeWidgetItem; -void addStructuredObjects(RDTreeWidgetItem *parent, const StructuredObjectList &objs, - bool parentIsArray); +void addStructuredChildren(RDTreeWidgetItem *parent, const SDObject &parentObj); struct PointerTypeRegistry { diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index 020992ee1..e6cff1d4a 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -52,6 +52,10 @@ %rename("string") "SDObjectData::str"; +// convenience - in C++ we have both duplicating and non-duplicating adds, but in python we only +// expose the duplicating add. Rename it to be simpler +%rename("AddChild") "SDObject::DuplicateAndAddChild"; + %begin %{ #undef slots %} diff --git a/qrenderdoc/Code/pyrenderdoc/structured_conversion.h b/qrenderdoc/Code/pyrenderdoc/structured_conversion.h index 5b1058203..6fcad3f1c 100644 --- a/qrenderdoc/Code/pyrenderdoc/structured_conversion.h +++ b/qrenderdoc/Code/pyrenderdoc/structured_conversion.h @@ -130,13 +130,14 @@ struct ExtRefcount : public ActiveRefcounter { // dec ref any python-owned objects in the children array, so the default destructor doesn't // just delete them. - for(size_t i = 0; i < c->data.children.size(); i++) - if(ActiveRefcounter::HasPyObject(c->data.children[i])) - ActiveRefcounter::Dec(c->data.children[i]); + for(size_t i = 0; i < c->NumChildren(); i++) + if(ActiveRefcounter::HasPyObject(c->GetChild(i))) + ActiveRefcounter::Dec(c->GetChild(i)); // we clear the array, because anything still left is C++ owned. We're just borrowing a // reference to it, so C++ will control the lifetime. - c->data.children.clear(); + StructuredObjectList discard; + c->TakeAllChildren(discard); ActiveRefcounter::DelPyObject(py, c); } @@ -154,13 +155,14 @@ struct ExtRefcount : public ActiveRefcounter { // dec ref any python-owned objects in the children array, so the default destructor doesn't // just delete them. - for(size_t i = 0; i < o->data.children.size(); i++) - if(ActiveRefcounter::HasPyObject(o->data.children[i])) - ActiveRefcounter::Dec(o->data.children[i]); + for(size_t i = 0; i < o->NumChildren(); i++) + if(ActiveRefcounter::HasPyObject(o->GetChild(i))) + ActiveRefcounter::Dec(o->GetChild(i)); // we clear the array, because anything still left is C++ owned. We're just borrowing a // reference to it, so C++ will control the lifetime. - o->data.children.clear(); + StructuredObjectList discard; + o->TakeAllChildren(discard); ActiveRefcounter::DelPyObject(py, o); } diff --git a/qrenderdoc/Windows/APIInspector.cpp b/qrenderdoc/Windows/APIInspector.cpp index d3305aed1..73c78e70f 100644 --- a/qrenderdoc/Windows/APIInspector.cpp +++ b/qrenderdoc/Windows/APIInspector.cpp @@ -154,7 +154,7 @@ void APIInspector::fillAPIView() root->setText(1, chunk->name); - addStructuredObjects(root, chunk->data.children, false); + addStructuredChildren(root, *chunk); } else { diff --git a/qrenderdoc/Windows/Dialogs/ConfigEditor.cpp b/qrenderdoc/Windows/Dialogs/ConfigEditor.cpp index 9d80ffdf3..06d1c00b6 100644 --- a/qrenderdoc/Windows/Dialogs/ConfigEditor.cpp +++ b/qrenderdoc/Windows/Dialogs/ConfigEditor.cpp @@ -65,7 +65,7 @@ static bool anyChildChanged(const SDObject *o) if(val && def) return !val->HasEqualValue(def); - for(const SDObject *c : o->data.children) + for(const SDObject *c : *o) { if(anyChildChanged(c)) return true; @@ -95,7 +95,7 @@ public: if(row < 0 || row > rowCount(parent)) return QModelIndex(); - return createIndex(row, column, o->data.children[row]); + return createIndex(row, column, o->GetChild(row)); } QModelIndex parent(const QModelIndex &index) const override @@ -121,7 +121,7 @@ public: if(o->FindChild("value")) return 0; - return o->data.children.count(); + return (int)o->NumChildren(); } enum Columns @@ -312,7 +312,7 @@ private: return; int i = 0; - for(SDObject *c : o->GetChildren()) + for(SDObject *c : *o) { parents[c] = parent; populateParents(c, index(i++, 0, parent)); @@ -343,11 +343,14 @@ protected: SDObject *o = obj(source_parent); - return matchesAnyChild(o->data.children[source_row]); + return matchesAnyChild(o->GetChild(source_row)); } bool matchesAnyChild(SDObject *o) const { + if(!o) + return false; + if(QString(o->name).contains(m_Text, Qt::CaseInsensitive)) return true; @@ -360,7 +363,7 @@ protected: return false; } - for(SDObject *c : o->GetChildren()) + for(SDObject *c : *o) if(matchesAnyChild(c)) return true; @@ -459,8 +462,8 @@ bool SettingDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, val->DeleteChildren(); - for(size_t c = 0; c < def->data.children.size(); c++) - val->data.children.push_back(def->data.children[c]->Duplicate()); + for(size_t c = 0; c < def->NumChildren(); c++) + val->DuplicateAndAddChild(def->GetChild(c)); // call setData() to emit the dataChanged for this element and all parents model->setData(index, QVariant(), Qt::UserRole); @@ -585,7 +588,7 @@ QWidget *SettingDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt QStringList items; - for(SDObject *c : val->data.children) + for(SDObject *c : *val) items << c->data.str; list.setItems(items); @@ -597,10 +600,10 @@ QWidget *SettingDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt items = list.getItems(); val->DeleteChildren(); - val->data.children.resize(items.size()); + val->ReserveChildren(items.size()); for(int i = 0; i < items.size(); i++) - val->data.children[i] = makeSDString("$el", items[i]); + val->AddAndOwnChild(makeSDString("$el", items[i])); } // we've handled the edit synchronously, don't create an edit widget diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp index 51da926da..cb6e1d5b9 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp @@ -494,7 +494,7 @@ void SettingsDialog::on_chooseSearchPaths_clicked() QStringList items; - for(SDObject *c : getPaths->data.children) + for(SDObject *c : *getPaths) items << c->data.str; list.setItems(items); @@ -508,10 +508,10 @@ void SettingsDialog::on_chooseSearchPaths_clicked() SDObject *setPaths = RENDERDOC_SetConfigSetting("DXBC.Debug.SearchDirPaths"); setPaths->DeleteChildren(); - setPaths->data.children.resize(items.size()); + setPaths->ReserveChildren(items.size()); for(int i = 0; i < items.size(); i++) - setPaths->data.children[i] = makeSDString("$el", items[i]); + setPaths->AddAndOwnChild(makeSDString("$el", items[i])); RENDERDOC_SaveConfigSettings(); } diff --git a/qrenderdoc/Windows/ResourceInspector.cpp b/qrenderdoc/Windows/ResourceInspector.cpp index 78d7fd09d..06948e211 100644 --- a/qrenderdoc/Windows/ResourceInspector.cpp +++ b/qrenderdoc/Windows/ResourceInspector.cpp @@ -315,7 +315,7 @@ void ResourceInspector::Inspect(ResourceId id) root->setText(0, chunkObj->name); - addStructuredObjects(root, chunkObj->data.children, false); + addStructuredChildren(root, *chunkObj); } else { diff --git a/renderdoc/api/replay/structured_data.h b/renderdoc/api/replay/structured_data.h index 00fd68aa0..b1db79b4f 100644 --- a/renderdoc/api/replay/structured_data.h +++ b/renderdoc/api/replay/structured_data.h @@ -351,13 +351,22 @@ struct SDObjectData DOCUMENT("The string contents of the object."); rdcstr str; - DOCUMENT("A list of :class:`SDObject` containing the children of this :class:`SDObject`."); - StructuredObjectList children; - SDObjectData(const SDObjectData &) = delete; SDObjectData &operator=(const SDObjectData &other) = delete; private: + friend struct SDObject; + friend struct SDChunk; + + // allow serialisation functions access to the data + template + friend void DoSerialise(SerialiserType &ser, SDObjectData &el); + template + friend void DoSerialise(SerialiserType &ser, SDObject *el); + + DOCUMENT("A list of :class:`SDObject` containing the children of this :class:`SDObject`."); + StructuredObjectList children; + void *operator new(size_t count) = delete; void *operator new[](size_t count) = delete; void operator delete(void *p) = delete; @@ -366,7 +375,13 @@ private: DECLARE_REFLECTION_STRUCT(SDObjectData); -DOCUMENT("Defines a single structured object."); +DOCUMENT(R"(Defines a single structured object. Structured objects are defined recursively and one +object can either be a basic type (integer, float, etc), an array, or a struct. Arrays and structs +are defined similarly. + +Each object owns its children and they will be deleted when it is deleted. You can use +:meth:`Duplicate` to make a deep copy of an object. +)"); struct SDObject { ///////////////////////////////////////////////////////////////// @@ -400,7 +415,12 @@ struct SDObject data.basic.u = 0; } - ~SDObject() { DeleteChildren(); } + ~SDObject() + { + // we own our children, so delete them now. + DeleteChildren(); + } + DOCUMENT("Create a deep copy of this object."); SDObject *Duplicate() const { @@ -426,7 +446,13 @@ struct SDObject DOCUMENT("The :class:`SDObjectData` with the contents of this object."); SDObjectData data; - DOCUMENT("Checks if the given object has the same value as this one."); + DOCUMENT(R"(Checks if the given object has the same value as this one. This equality is defined +recursively through children. + +:param SDObject obj: The object to compare against +:return: A boolean indicating if the object is equal to this one. +:rtype: ``bool`` +)"); bool HasEqualValue(const SDObject *o) const { bool ret = true; @@ -452,10 +478,21 @@ struct SDObject return ret; } - DOCUMENT("Add a new child object by duplicating it."); - inline void AddChild(SDObject *child) { data.children.push_back(child->Duplicate()); } - DOCUMENT("Find a child object by a given name."); - inline SDObject *FindChild(const char *childName) const + // this is renamed to just AddChild in the python interface file, since we always duplicate for + // python. + DOCUMENT(R"(Add a new child object. + +:param SDObject obj: The new child to add +)"); + inline void DuplicateAndAddChild(SDObject *child) { data.children.push_back(child->Duplicate()); } + DOCUMENT(R"(Find a child object by a given name. If no matching child is found, ``None`` is +returned. + +:param str name: The name to search for. +:return: A reference to the child object if found, or ``None`` if not. +:rtype: SDObject +)"); + inline SDObject *FindChild(const rdcstr &childName) const { for(size_t i = 0; i < data.children.size(); i++) if(data.children[i]->name == childName) @@ -463,7 +500,13 @@ struct SDObject return NULL; } - DOCUMENT("Get a child object at a given index."); + DOCUMENT(R"(Find a child object by a given index. If the index is out of bounds, ``None`` is +returned. + +:param int index: The index to look up. +:return: A reference to the child object if valid, or ``None`` if not. +:rtype: SDObject +)"); inline SDObject *GetChild(size_t index) const { if(index < data.children.size()) @@ -471,6 +514,16 @@ struct SDObject return NULL; } + DOCUMENT(R"(Delete the child object at an index. If the index is out of bounds, nothing happens. + +:param int index: The index to remove. +)"); + inline void RemoveChild(size_t index) + { + if(index < data.children.size()) + delete data.children.takeAt(index); + } + DOCUMENT("Delete all child objects."); inline void DeleteChildren() { @@ -480,10 +533,12 @@ struct SDObject data.children.clear(); } - DOCUMENT("Get the number of child objects."); + DOCUMENT(R"(Get the number of child objects. + +:return: The number of children this object contains. +:rtype: ``int`` +)"); inline size_t NumChildren() const { return data.children.size(); } - DOCUMENT("Get a ``list`` of :class:`SDObject` children."); - inline StructuredObjectList &GetChildren() { return data.children; } #if !defined(SWIG) // these are for C++ iteration so not defined when SWIG is generating interfaces inline SDObject *const *begin() const { return data.children.begin(); } @@ -492,6 +547,32 @@ struct SDObject inline SDObject **end() { return data.children.end(); } #endif +#if !defined(SWIG) + // this interface is 'more advanced' and is intended for C++ code manipulating structured data. + // reserve a number of children up front, useful when constructing an array to avoid repeated + // allocations. + void ReserveChildren(size_t num) { data.children.reserve(num); } + // add a new child without duplicating it, and take ownership of it. Returns the child back + // immediately for easy chaining. + SDObject *AddAndOwnChild(SDObject *child) + { + data.children.push_back(child); + return child; + } + // similar to AddAndOwnChild, but insert at a given offset + SDObject *InsertAndOwnChild(size_t offs, SDObject *child) + { + data.children.insert(offs, child); + return child; + } + // Take ownership of the whole children array from the object. + void TakeAllChildren(StructuredObjectList &objs) + { + objs.clear(); + objs.swap(data.children); + } +#endif + // C++ gets more extensive typecasts. We'll add a couple for python in the interface file #if !defined(SWIG) // templated enum cast @@ -577,8 +658,6 @@ struct SDObject type.flags = SDTypeFlags::HasCustomString; return this; } - - void AddAndOwnChild(SDObject *child) { data.children.push_back(child); } #endif // these are common to both python and C++ @@ -720,9 +799,9 @@ inline SDObject *makeSDObject(const char *name, QVariant val) QVariantList list = val.toList(); ret->type.name = "array"_lit; ret->type.basetype = SDBasic::Array; - ret->data.children.reserve(list.size()); + ret->ReserveChildren(list.size()); for(int i = 0; i < list.size(); i++) - ret->data.children.push_back(makeSDObject("[]", list.at(i))); + ret->AddAndOwnChild(makeSDObject("[]", list.at(i))); ret->type.byteSize = list.size(); break; } @@ -731,9 +810,9 @@ inline SDObject *makeSDObject(const char *name, QVariant val) QVariantMap map = val.toMap(); ret->type.name = "struct"_lit; ret->type.basetype = SDBasic::Struct; - ret->data.children.reserve(map.size()); + ret->ReserveChildren(map.size()); for(const QString &str : map.keys()) - ret->data.children.push_back(makeSDObject(str.toUtf8().data(), map[str])); + ret->AddAndOwnChild(makeSDObject(str.toUtf8().data(), map[str])); ret->type.byteSize = map.size(); break; } diff --git a/renderdoc/core/settings.cpp b/renderdoc/core/settings.cpp index c410c9db2..f9fe13a13 100644 --- a/renderdoc/core/settings.cpp +++ b/renderdoc/core/settings.cpp @@ -119,13 +119,13 @@ static void Config2XML(pugi::xml_node &parent, SDObject &child) obj.append_attribute("type") = ToStr(value->type.basetype).c_str(); if(value->type.basetype == SDBasic::Array) { - if(!value->data.children.empty()) - obj.append_attribute("elemtype") = ToStr(value->data.children[0]->type.basetype).c_str(); + if(value->NumChildren() > 0) + obj.append_attribute("elemtype") = ToStr(value->GetChild(0)->type.basetype).c_str(); else obj.append_attribute("elemtype") = ""; - for(size_t o = 0; o < value->data.children.size(); o++) - saveSDObject(*value->data.children[o], obj.append_child("item")); + for(size_t o = 0; o < value->NumChildren(); o++) + saveSDObject(*value->GetChild(o), obj.append_child("item")); } else { @@ -149,7 +149,7 @@ static SDObject *XML2Config(pugi::xml_node &obj) SDObject *childObj = XML2Config(child); if(childObj) { - ret->data.children.push_back(childObj); + ret->AddAndOwnChild(childObj); } else { @@ -194,7 +194,7 @@ static SDObject *XML2Config(pugi::xml_node &obj) if(childObj) { - valueObj->data.children.push_back(childObj); + valueObj->AddAndOwnChild(childObj); } else { @@ -244,7 +244,7 @@ static SDObject *importXMLConfig(StreamReader &stream) { SDObject *childObj = XML2Config(child); if(childObj) - ret->data.children.push_back(XML2Config(child)); + ret->AddAndOwnChild(XML2Config(child)); } } @@ -258,8 +258,8 @@ static void exportXMLConfig(StreamWriter &stream, const SDObject *obj) pugi::xml_node xRoot = doc.append_child("config"); xRoot.append_attribute("version") = (uint32_t)1; - for(size_t o = 0; o < obj->data.children.size(); o++) - Config2XML(xRoot, *obj->data.children[o]); + for(size_t o = 0; o < obj->NumChildren(); o++) + Config2XML(xRoot, *obj->GetChild(o)); xml_stream_writer writer(stream); doc.save(writer, " ", pugi::format_default | pugi::format_no_empty_element_tags); @@ -325,8 +325,8 @@ static bool MergeConfigValues(const rdcstr &prefix, SDObject *dstConfig, const S dstVal->DeleteChildren(); - for(size_t c = 0; c < srcVal->data.children.size(); c++) - dstVal->data.children.push_back(srcVal->data.children[c]->Duplicate()); + for(size_t c = 0; c < srcVal->NumChildren(); c++) + dstVal->DuplicateAndAddChild(srcVal->GetChild(c)); } // if the description has changed from the loaded, need to write the new one @@ -344,7 +344,7 @@ static bool MergeConfigValues(const rdcstr &prefix, SDObject *dstConfig, const S ret |= true; // if we're copying nodes, do that now - dstConfig->AddChild(srcChild->Duplicate()); + dstConfig->DuplicateAndAddChild(srcChild->Duplicate()); } } @@ -385,9 +385,9 @@ rdcstr DefValString(const T &el) // this one needs a special implementation unfortunately to convert const rdcarray &ConfigVarRegistration>::value() { - tmp.resize(obj->data.children.size()); + tmp.resize(obj->NumChildren()); for(size_t i = 0; i < tmp.size(); i++) - tmp[i] = obj->data.children[i]->data.str; + tmp[i] = obj->GetChild(i)->data.str; return tmp; } @@ -410,7 +410,7 @@ inline SDObject *makeSDObject(const char *name, const rdcarray &vals) SDObject *ret = new SDObject(name, "array"_lit); ret->type.basetype = SDBasic::Array; for(const rdcstr &s : vals) - ret->data.children.push_back(makeSDObject("$el", s)); + ret->AddAndOwnChild(makeSDObject("$el", s)); return ret; } @@ -581,9 +581,9 @@ void RenderDoc::RegisterSetting(const rdcstr &settingPath, SDObject *setting) { child = new SDObject(node, "category"_lit); auto it = - std::lower_bound(cur->data.children.begin(), cur->data.children.end(), child, + std::lower_bound(cur->begin(), cur->end(), child, [](const SDObject *a, const SDObject *b) { return a->name < b->name; }); - cur->data.children.insert(it - cur->data.children.begin(), child); + cur->InsertAndOwnChild(it - cur->begin(), child); } cur = child; diff --git a/renderdoc/serialise/codecs/xml_codec.cpp b/renderdoc/serialise/codecs/xml_codec.cpp index 110d81969..2e3e563d9 100644 --- a/renderdoc/serialise/codecs/xml_codec.cpp +++ b/renderdoc/serialise/codecs/xml_codec.cpp @@ -231,12 +231,12 @@ static void Obj2XML(pugi::xml_node &parent, SDObject &child) } else if(child.type.basetype == SDBasic::Struct || child.type.basetype == SDBasic::Array) { - if(child.type.basetype == SDBasic::Array && !child.data.children.empty()) + if(child.type.basetype == SDBasic::Array && child.NumChildren() > 0) obj.remove_attribute("typename"); - for(size_t o = 0; o < child.data.children.size(); o++) + for(size_t o = 0; o < child.NumChildren(); o++) { - Obj2XML(obj, *child.data.children[o]); + Obj2XML(obj, *child.GetChild(o)); if(child.type.basetype == SDBasic::Array) obj.last_child().remove_attribute("name"); @@ -444,15 +444,15 @@ static ReplayStatus Structured2XML(const char *filename, const RDCFile &file, ui { xChunk.append_attribute("opaque") = true; - RDCASSERT(!chunk->data.children.empty()); + RDCASSERT(chunk->NumChildren() > 0); pugi::xml_node opaque = xChunk.append_child("buffer"); - opaque.append_attribute("byteLength") = chunk->data.children[0]->type.byteSize; - opaque.text() = chunk->data.children[0]->data.basic.u; + opaque.append_attribute("byteLength") = chunk->GetChild(0)->type.byteSize; + opaque.text() = chunk->GetChild(0)->data.basic.u; } else { - for(size_t o = 0; o < chunk->data.children.size(); o++) - Obj2XML(xChunk, *chunk->data.children[o]); + for(size_t o = 0; o < chunk->NumChildren(); o++) + Obj2XML(xChunk, *chunk->GetChild(o)); } if(progress) @@ -516,14 +516,14 @@ static SDObject *XML2Obj(pugi::xml_node &obj) { for(pugi::xml_node child = obj.first_child(); child; child = child.next_sibling()) { - ret->data.children.push_back(XML2Obj(child)); + SDObject *c = ret->AddAndOwnChild(XML2Obj(child)); if(ret->type.basetype == SDBasic::Array) - ret->data.children.back()->name = "$el"; + c->name = "$el"; } - if(ret->type.basetype == SDBasic::Array && !ret->data.children.empty()) - ret->type.name = ret->data.children.back()->type.name; + if(ret->type.basetype == SDBasic::Array && ret->NumChildren() > 0) + ret->type.name = ret->GetChild(0)->type.name; } else if(ret->type.basetype == SDBasic::Buffer) { @@ -813,15 +813,15 @@ static ReplayStatus XML2Structured(const char *xml, const ThumbTypeAndData &thum chunk->metadata.flags |= SDChunkFlags::OpaqueChunk; - chunk->data.children.push_back(new SDObject("Opaque chunk"_lit, "Byte Buffer"_lit)); - chunk->data.children[0]->type.basetype = SDBasic::Buffer; - chunk->data.children[0]->type.byteSize = opaque.attribute("byteLength").as_ullong(); - chunk->data.children[0]->data.basic.u = opaque.text().as_ullong(); + SDObject *buf = chunk->AddAndOwnChild(new SDObject("Opaque chunk"_lit, "Byte Buffer"_lit)); + buf->type.basetype = SDBasic::Buffer; + buf->type.byteSize = opaque.attribute("byteLength").as_ullong(); + buf->data.basic.u = opaque.text().as_ullong(); } else { for(pugi::xml_node child = xChunk.first_child(); child; child = child.next_sibling()) - chunk->data.children.push_back(XML2Obj(child)); + chunk->AddAndOwnChild(XML2Obj(child)); } chunks.push_back(chunk); diff --git a/renderdoc/serialise/serialiser.cpp b/renderdoc/serialise/serialiser.cpp index 2a8691de3..1bd4a3a9a 100644 --- a/renderdoc/serialise/serialiser.cpp +++ b/renderdoc/serialise/serialiser.cpp @@ -210,9 +210,8 @@ void Serialiser::SkipCurrentChunk() SDObject ¤t = *m_StructureStack.back(); - current.data.children.push_back(new SDObject("Opaque chunk"_lit, "Byte Buffer"_lit)); + SDObject &obj = *current.AddAndOwnChild(new SDObject("Opaque chunk"_lit, "Byte Buffer"_lit)); - SDObject &obj = *current.data.children.back(); obj.type.basetype = SDBasic::Buffer; obj.type.byteSize = m_ChunkMetadata.length; @@ -243,7 +242,7 @@ void Serialiser::SkipCurrentChunk() { SDObject ¤t = *m_StructureStack.back(); - SDObject &obj = *current.data.children.back(); + SDObject &obj = *current.GetChild(current.NumChildren() - 1); obj.data.basic.u = m_StructuredFile->buffers.size(); @@ -590,9 +589,9 @@ void Serialiser::WriteStructuredFile(const SDFile &file if(chunk.metadata.flags & SDChunkFlags::OpaqueChunk) { - RDCASSERT(chunk.data.children.size() == 1); + RDCASSERT(chunk.NumChildren() == 1); - size_t bufID = (size_t)chunk.data.children[0]->data.basic.u; + size_t bufID = (size_t)chunk.GetChild(0)->data.basic.u; byte *ptr = m_StructuredFile->buffers[bufID]->data(); size_t len = m_StructuredFile->buffers[bufID]->size(); @@ -600,10 +599,10 @@ void Serialiser::WriteStructuredFile(const SDFile &file } else { - for(size_t o = 0; o < chunk.data.children.size(); o++) + for(size_t o = 0; o < chunk.NumChildren(); o++) { // note, we don't need names because we aren't exporting structured data - ser->Serialise(""_lit, chunk.data.children[o]); + ser->Serialise(""_lit, chunk.GetChild(o)); } } @@ -753,8 +752,8 @@ void DoSerialise(SerialiserType &ser, SDObject *el) { case SDBasic::Chunk: RDCERR("Unexpected chunk inside object!"); break; case SDBasic::Struct: - for(size_t o = 0; o < el->data.children.size(); o++) - ser.Serialise(""_lit, el->data.children[o]); + for(size_t o = 0; o < el->NumChildren(); o++) + ser.Serialise(""_lit, el->GetChild(o)); break; case SDBasic::Array: ser.Serialise(""_lit, (rdcarray &)el->data.children); break; case SDBasic::Null: diff --git a/renderdoc/serialise/serialiser.h b/renderdoc/serialise/serialiser.h index 8e15c6c77..84e498923 100644 --- a/renderdoc/serialise/serialiser.h +++ b/renderdoc/serialise/serialiser.h @@ -226,10 +226,9 @@ public: SDObject ¤t = *m_StructureStack.back(); - current.data.children.push_back(new SDObject(name, TypeName())); - m_StructureStack.push_back(current.data.children.back()); + SDObject &obj = *current.AddAndOwnChild(new SDObject(name, TypeName())); + m_StructureStack.push_back(&obj); - SDObject &obj = *m_StructureStack.back(); obj.type.byteSize = sizeof(T); if(std::is_union::value) obj.type.flags |= SDTypeFlags::Union; @@ -272,10 +271,9 @@ public: SDObject ¤t = *m_StructureStack.back(); - current.data.children.push_back(new SDObject(name, "Byte Buffer"_lit)); - m_StructureStack.push_back(current.data.children.back()); + SDObject &obj = *current.AddAndOwnChild(new SDObject(name, "Byte Buffer"_lit)); + m_StructureStack.push_back(&obj); - SDObject &obj = *m_StructureStack.back(); obj.type.basetype = SDBasic::Buffer; obj.type.byteSize = byteSize; } @@ -382,10 +380,9 @@ public: SDObject ¤t = *m_StructureStack.back(); - current.data.children.push_back(new SDObject(name, "Byte Buffer"_lit)); - m_StructureStack.push_back(current.data.children.back()); + SDObject &obj = *current.AddAndOwnChild(new SDObject(name, "Byte Buffer"_lit)); + m_StructureStack.push_back(&obj); - SDObject &obj = *m_StructureStack.back(); obj.type.basetype = SDBasic::Buffer; obj.type.byteSize = count; } @@ -494,22 +491,20 @@ public: } SDObject &parent = *m_StructureStack.back(); - parent.data.children.push_back(new SDObject(name, TypeName())); - m_StructureStack.push_back(parent.data.children.back()); - SDObject &arr = *m_StructureStack.back(); + SDObject &arr = *parent.AddAndOwnChild(new SDObject(name, TypeName())); + m_StructureStack.push_back(&arr); + arr.type.basetype = SDBasic::Array; arr.type.byteSize = N; arr.type.flags |= SDTypeFlags::FixedArray; - arr.data.children.resize(N); + arr.ReserveChildren(N); for(size_t i = 0; i < N; i++) { - arr.data.children[i] = new SDObject("$el"_lit, TypeName()); - m_StructureStack.push_back(arr.data.children[i]); - - SDObject &obj = *m_StructureStack.back(); + SDObject &obj = *arr.AddAndOwnChild(new SDObject("$el"_lit, TypeName())); + m_StructureStack.push_back(&obj); // default to struct. This will be overwritten if appropriate obj.type.basetype = SDBasic::Struct; @@ -613,14 +608,14 @@ public: } SDObject &parent = *m_StructureStack.back(); - parent.data.children.push_back(new SDObject(name, TypeName())); - m_StructureStack.push_back(parent.data.children.back()); - SDObject &arr = *m_StructureStack.back(); + SDObject &arr = *parent.AddAndOwnChild(new SDObject(name, TypeName())); + m_StructureStack.push_back(&arr); + arr.type.basetype = SDBasic::Array; arr.type.byteSize = arrayCount; - arr.data.children.resize((size_t)arrayCount); + arr.ReserveChildren((size_t)arrayCount); // Coverity is unable to tie this allocation together with the automatic scoped deallocation in the // ScopedDeseralise* classes. We can verify with e.g. valgrind that there are no leaks, so to keep @@ -637,10 +632,8 @@ public: for(uint64_t i = 0; el && i < arrayCount; i++) { - arr.data.children[(size_t)i] = new SDObject("$el"_lit, TypeName()); - m_StructureStack.push_back(arr.data.children[(size_t)i]); - - SDObject &obj = *m_StructureStack.back(); + SDObject &obj = *arr.AddAndOwnChild(new SDObject("$el"_lit, TypeName())); + m_StructureStack.push_back(&obj); // default to struct. This will be overwritten if appropriate obj.type.basetype = SDBasic::Struct; @@ -704,24 +697,22 @@ public: } SDObject &parent = *m_StructureStack.back(); - parent.data.children.push_back(new SDObject(name, TypeName())); - m_StructureStack.push_back(parent.data.children.back()); - SDObject &arr = *m_StructureStack.back(); + SDObject &arr = *parent.AddAndOwnChild(new SDObject(name, TypeName())); + m_StructureStack.push_back(&arr); + arr.type.basetype = SDBasic::Array; arr.type.byteSize = size; - arr.data.children.resize((size_t)size); + arr.ReserveChildren((size_t)size); if(IsReading()) el.resize((int)size); for(size_t i = 0; i < (size_t)size; i++) { - arr.data.children[i] = new SDObject("$el"_lit, TypeName()); - m_StructureStack.push_back(arr.data.children[i]); - - SDObject &obj = *m_StructureStack.back(); + SDObject &obj = *arr.AddAndOwnChild(new SDObject("$el"_lit, TypeName())); + m_StructureStack.push_back(&obj); // default to struct. This will be overwritten if appropriate obj.type.basetype = SDBasic::Struct; @@ -759,20 +750,18 @@ public: } SDObject &parent = *m_StructureStack.back(); - parent.data.children.push_back(new SDObject(name, "pair"_lit)); - m_StructureStack.push_back(parent.data.children.back()); - SDObject &arr = *m_StructureStack.back(); + SDObject &arr = *parent.AddAndOwnChild(new SDObject(name, "pair"_lit)); + m_StructureStack.push_back(&arr); + arr.type.basetype = SDBasic::Struct; arr.type.byteSize = 2; - arr.data.children.resize(2); + arr.ReserveChildren(2); { - arr.data.children[0] = new SDObject("first"_lit, TypeName()); - m_StructureStack.push_back(arr.data.children[0]); - - SDObject &obj = *m_StructureStack.back(); + SDObject &obj = *arr.AddAndOwnChild(new SDObject("first"_lit, TypeName())); + m_StructureStack.push_back(&obj); // default to struct. This will be overwritten if appropriate obj.type.basetype = SDBasic::Struct; @@ -784,10 +773,8 @@ public: } { - arr.data.children[1] = new SDObject("second"_lit, TypeName()); - m_StructureStack.push_back(arr.data.children[1]); - - SDObject &obj = *m_StructureStack.back(); + SDObject &obj = *arr.AddAndOwnChild(new SDObject("second"_lit, TypeName())); + m_StructureStack.push_back(&obj); // default to struct. This will be overwritten if appropriate obj.type.basetype = SDBasic::Struct; @@ -866,7 +853,7 @@ public: SDObject &parent = *m_StructureStack.back(); - SDObject &nullable = *parent.data.children.back(); + SDObject &nullable = *parent.GetChild(parent.NumChildren() - 1); nullable.type.flags |= SDTypeFlags::Nullable; if(std::is_union::value) @@ -875,9 +862,9 @@ public: else { SDObject &parent = *m_StructureStack.back(); - parent.data.children.push_back(new SDObject(name, TypeName())); - SDObject &nullable = *parent.data.children.back(); + SDObject &nullable = *parent.AddAndOwnChild(new SDObject(name, TypeName())); + nullable.type.basetype = SDBasic::Null; nullable.type.byteSize = 0; nullable.type.flags |= SDTypeFlags::Nullable; @@ -962,10 +949,9 @@ public: SDObject ¤t = *m_StructureStack.back(); - current.data.children.push_back(new SDObject(name.c_str(), "Byte Buffer"_lit)); - m_StructureStack.push_back(current.data.children.back()); + SDObject &obj = *current.AddAndOwnChild(new SDObject(name, "Byte Buffer"_lit)); + m_StructureStack.push_back(&obj); - SDObject &obj = *m_StructureStack.back(); obj.type.basetype = SDBasic::Buffer; obj.type.byteSize = totalSize; @@ -1040,8 +1026,8 @@ public: { SDObject ¤t = *m_StructureStack.back(); - if(!current.data.children.empty()) - current.data.children.back()->type.flags |= SDTypeFlags::Hidden; + if(current.NumChildren() > 0) + current.GetChild(current.NumChildren() - 1)->type.flags |= SDTypeFlags::Hidden; } return *this; @@ -1053,14 +1039,14 @@ public: { SDObject ¤t = *m_StructureStack.back(); - if(!current.data.children.empty()) + if(current.NumChildren() > 0) { - SDObject *last = current.data.children.back(); + SDObject *last = current.GetChild(current.NumChildren() - 1); last->type.name = name; if(last->type.basetype == SDBasic::Array) { - for(SDObject *obj : last->data.children) + for(SDObject *obj : *last) obj->type.name = name; } } @@ -1075,8 +1061,8 @@ public: { SDObject ¤t = *m_StructureStack.back(); - if(!current.data.children.empty()) - current.data.children.back()->name = name; + if(current.NumChildren() > 0) + current.GetChild(current.NumChildren() - 1)->name = name; } return *this; diff --git a/renderdoc/serialise/serialiser_tests.cpp b/renderdoc/serialise/serialiser_tests.cpp index 957ab3ec8..fec426251 100644 --- a/renderdoc/serialise/serialiser_tests.cpp +++ b/renderdoc/serialise/serialiser_tests.cpp @@ -253,15 +253,15 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") CHECK(chunk.metadata.length < ser.GetReader()->GetSize()); CHECK(chunk.type.basetype == SDBasic::Chunk); CHECK(chunk.type.name == "Chunk"); - CHECK(chunk.data.children.size() == 16); + CHECK(chunk.NumChildren() == 16); - for(SDObject *o : chunk.data.children) + for(SDObject *o : chunk) REQUIRE(o); int childIdx = 0; { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "a"); CHECK(o.type.name == "int64_t"); @@ -273,7 +273,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "b"); CHECK(o.type.name == "uint64_t"); @@ -285,7 +285,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "c"); CHECK(o.type.name == "int32_t"); @@ -297,7 +297,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "d"); CHECK(o.type.name == "uint32_t"); @@ -309,7 +309,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "e"); CHECK(o.type.name == "int16_t"); @@ -321,7 +321,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "f"); CHECK(o.type.name == "uint16_t"); @@ -333,7 +333,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "g"); CHECK(o.type.name == "int8_t"); @@ -345,7 +345,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "h"); CHECK(o.type.name == "uint8_t"); @@ -357,7 +357,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "i"); CHECK(o.type.name == "bool"); @@ -369,7 +369,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "j"); CHECK(o.type.name == "char"); @@ -381,7 +381,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "k"); CHECK(o.type.name == "double"); @@ -393,7 +393,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "l"); CHECK(o.type.name == "float"); @@ -405,7 +405,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "m"); CHECK(o.type.name == "string"); @@ -417,7 +417,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "n"); CHECK(o.type.name == "string"); @@ -429,7 +429,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "s"); CHECK(o.type.name == "string"); @@ -441,7 +441,7 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "t"); CHECK(o.type.name == "int32_t"); @@ -449,12 +449,12 @@ TEST_CASE("Read/write via structured of basic types", "[serialiser]") CHECK(o.type.byteSize == 4); CHECK(o.type.flags == SDTypeFlags::FixedArray); - CHECK(o.data.children.size() == 4); + CHECK(o.NumChildren() == 4); - CHECK(o.data.children[0]->data.basic.i == 20); - CHECK(o.data.children[1]->data.basic.c == 20); - CHECK(o.data.children[2]->data.basic.c == 20); - CHECK(o.data.children[3]->data.basic.c == 20); + CHECK(o.GetChild(0)->data.basic.i == 20); + CHECK(o.GetChild(1)->data.basic.c == 20); + CHECK(o.GetChild(2)->data.basic.c == 20); + CHECK(o.GetChild(3)->data.basic.c == 20); } StreamWriter *rewriteBuf = new StreamWriter(StreamWriter::DefaultScratchSize); @@ -933,48 +933,48 @@ TEST_CASE("Read/write container types", "[serialiser][structured]") const SDChunk &chunk = *structData.chunks[0]; - CHECK(chunk.data.children.size() == 2); + CHECK(chunk.NumChildren() == 2); - for(SDObject *o : chunk.data.children) + for(SDObject *o : chunk) REQUIRE(o); int childIdx = 0; { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "v"); CHECK(o.type.basetype == SDBasic::Array); CHECK(o.type.byteSize == 6); CHECK(o.type.flags == SDTypeFlags::NoFlags); - CHECK(o.data.children.size() == 6); + CHECK(o.NumChildren() == 6); - for(SDObject *child : o.data.children) + for(SDObject *child : o) { CHECK(child->type.basetype == SDBasic::SignedInteger); CHECK(child->type.byteSize == 4); } - CHECK(o.data.children[0]->data.basic.i == 1); - CHECK(o.data.children[1]->data.basic.i == 1); - CHECK(o.data.children[2]->data.basic.i == 2); - CHECK(o.data.children[3]->data.basic.i == 3); - CHECK(o.data.children[4]->data.basic.i == 5); - CHECK(o.data.children[5]->data.basic.i == 8); + CHECK(o.GetChild(0)->data.basic.i == 1); + CHECK(o.GetChild(1)->data.basic.i == 1); + CHECK(o.GetChild(2)->data.basic.i == 2); + CHECK(o.GetChild(3)->data.basic.i == 3); + CHECK(o.GetChild(4)->data.basic.i == 5); + CHECK(o.GetChild(5)->data.basic.i == 8); } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "p"); CHECK(o.type.name == "pair"); CHECK(o.type.basetype == SDBasic::Struct); CHECK(o.type.byteSize == 2); CHECK(o.type.flags == SDTypeFlags::NoFlags); - CHECK(o.data.children.size() == 2); + CHECK(o.NumChildren() == 2); { - SDObject &first = *o.data.children[0]; + SDObject &first = *o.GetChild(0); CHECK(first.name == "first"); CHECK(first.type.name == "float"); @@ -986,7 +986,7 @@ TEST_CASE("Read/write container types", "[serialiser][structured]") } { - SDObject &second = *o.data.children[1]; + SDObject &second = *o.GetChild(1); CHECK(second.name == "second"); CHECK(second.type.name == "string"); @@ -1225,15 +1225,15 @@ TEST_CASE("Read/write complex types", "[serialiser][structured]") const SDChunk &chunk = *structData.chunks[0]; - CHECK(chunk.data.children.size() == 5); + CHECK(chunk.NumChildren() == 5); - for(SDObject *o : chunk.data.children) + for(SDObject *o : chunk) REQUIRE(o); int childIdx = 0; { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "enumVal"); CHECK(o.type.basetype == SDBasic::Enum); @@ -1244,62 +1244,62 @@ TEST_CASE("Read/write complex types", "[serialiser][structured]") } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "sparseStructArray"); CHECK(o.type.basetype == SDBasic::Array); CHECK(o.type.byteSize == 10); CHECK(o.type.flags == SDTypeFlags::NoFlags); - CHECK(o.data.children.size() == 10); + CHECK(o.NumChildren() == 10); - for(SDObject *child : o.data.children) + for(SDObject *child : o) { CHECK(child->type.basetype == SDBasic::Struct); CHECK(child->type.name == "struct1"); CHECK(child->type.byteSize == sizeof(struct1)); - CHECK(child->data.children.size() == 4); - CHECK(child->data.children[0]->type.basetype == SDBasic::Float); - CHECK(child->data.children[0]->type.byteSize == 4); - CHECK(child->data.children[0]->name == "x"); - CHECK(child->data.children[1]->type.basetype == SDBasic::Float); - CHECK(child->data.children[1]->type.byteSize == 4); - CHECK(child->data.children[1]->name == "y"); - CHECK(child->data.children[2]->type.basetype == SDBasic::Float); - CHECK(child->data.children[2]->type.byteSize == 4); - CHECK(child->data.children[2]->name == "width"); - CHECK(child->data.children[3]->type.basetype == SDBasic::Float); - CHECK(child->data.children[3]->type.byteSize == 4); - CHECK(child->data.children[3]->name == "height"); + CHECK(child->NumChildren() == 4); + CHECK(child->GetChild(0)->type.basetype == SDBasic::Float); + CHECK(child->GetChild(0)->type.byteSize == 4); + CHECK(child->GetChild(0)->name == "x"); + CHECK(child->GetChild(1)->type.basetype == SDBasic::Float); + CHECK(child->GetChild(1)->type.byteSize == 4); + CHECK(child->GetChild(1)->name == "y"); + CHECK(child->GetChild(2)->type.basetype == SDBasic::Float); + CHECK(child->GetChild(2)->type.byteSize == 4); + CHECK(child->GetChild(2)->name == "width"); + CHECK(child->GetChild(3)->type.basetype == SDBasic::Float); + CHECK(child->GetChild(3)->type.byteSize == 4); + CHECK(child->GetChild(3)->name == "height"); } - CHECK(o.data.children[0]->data.children[0]->data.basic.d == 0.0f); - CHECK(o.data.children[0]->data.children[1]->data.basic.d == 0.0f); - CHECK(o.data.children[0]->data.children[2]->data.basic.d == 0.0f); - CHECK(o.data.children[0]->data.children[3]->data.basic.d == 0.0f); + CHECK(o.GetChild(0)->GetChild(0)->data.basic.d == 0.0f); + CHECK(o.GetChild(0)->GetChild(1)->data.basic.d == 0.0f); + CHECK(o.GetChild(0)->GetChild(2)->data.basic.d == 0.0f); + CHECK(o.GetChild(0)->GetChild(3)->data.basic.d == 0.0f); - CHECK(o.data.children[5]->data.children[0]->data.basic.d == 1.0f); - CHECK(o.data.children[5]->data.children[1]->data.basic.d == 2.0f); - CHECK(o.data.children[5]->data.children[2]->data.basic.d == 3.0f); - CHECK(o.data.children[5]->data.children[3]->data.basic.d == 4.0f); + CHECK(o.GetChild(5)->GetChild(0)->data.basic.d == 1.0f); + CHECK(o.GetChild(5)->GetChild(1)->data.basic.d == 2.0f); + CHECK(o.GetChild(5)->GetChild(2)->data.basic.d == 3.0f); + CHECK(o.GetChild(5)->GetChild(3)->data.basic.d == 4.0f); - CHECK(o.data.children[8]->data.children[0]->data.basic.d == 10.0f); - CHECK(o.data.children[8]->data.children[1]->data.basic.d == 20.0f); - CHECK(o.data.children[8]->data.children[2]->data.basic.d == 30.0f); - CHECK(o.data.children[8]->data.children[3]->data.basic.d == 40.0f); + CHECK(o.GetChild(8)->GetChild(0)->data.basic.d == 10.0f); + CHECK(o.GetChild(8)->GetChild(1)->data.basic.d == 20.0f); + CHECK(o.GetChild(8)->GetChild(2)->data.basic.d == 30.0f); + CHECK(o.GetChild(8)->GetChild(3)->data.basic.d == 40.0f); } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "complex"); CHECK(o.type.name == "struct2"); CHECK(o.type.basetype == SDBasic::Struct); CHECK(o.type.byteSize == sizeof(struct2)); CHECK(o.type.flags == SDTypeFlags::NoFlags); - CHECK(o.data.children.size() == 3); + CHECK(o.NumChildren() == 3); { - SDObject &c = *o.data.children[0]; + SDObject &c = *o.GetChild(0); CHECK(c.name == "name"); CHECK(c.type.name == "string"); @@ -1310,58 +1310,58 @@ TEST_CASE("Read/write complex types", "[serialiser][structured]") } { - SDObject &c = *o.data.children[1]; + SDObject &c = *o.GetChild(1); CHECK(c.name == "floats"); CHECK(c.type.basetype == SDBasic::Array); CHECK(c.type.flags == SDTypeFlags::NoFlags); - CHECK(c.data.children.size() == 3); - for(SDObject *ch : c.data.children) + CHECK(c.NumChildren() == 3); + for(SDObject *ch : c) { CHECK(ch->type.basetype == SDBasic::Float); CHECK(ch->type.byteSize == 4); } - CHECK(c.data.children[0]->data.basic.d == 1.2f); - CHECK(c.data.children[1]->data.basic.d == 3.4f); - CHECK(c.data.children[2]->data.basic.d == 5.6f); + CHECK(c.GetChild(0)->data.basic.d == 1.2f); + CHECK(c.GetChild(1)->data.basic.d == 3.4f); + CHECK(c.GetChild(2)->data.basic.d == 5.6f); } { - SDObject &c = *o.data.children[2]; + SDObject &c = *o.GetChild(2); CHECK(c.name == "viewports"); CHECK(c.type.basetype == SDBasic::Array); CHECK(c.type.flags == SDTypeFlags::NoFlags); - CHECK(c.data.children.size() == 4); - for(SDObject *ch : c.data.children) + CHECK(c.NumChildren() == 4); + for(SDObject *ch : c) { CHECK(ch->type.basetype == SDBasic::Struct); CHECK(ch->type.name == "struct1"); } - CHECK(c.data.children[0]->data.children[0]->data.basic.d == 512.0f); - CHECK(c.data.children[0]->data.children[1]->data.basic.d == 0.0f); - CHECK(c.data.children[0]->data.children[2]->data.basic.d == 256.0f); - CHECK(c.data.children[0]->data.children[3]->data.basic.d == 256.0f); + CHECK(c.GetChild(0)->GetChild(0)->data.basic.d == 512.0f); + CHECK(c.GetChild(0)->GetChild(1)->data.basic.d == 0.0f); + CHECK(c.GetChild(0)->GetChild(2)->data.basic.d == 256.0f); + CHECK(c.GetChild(0)->GetChild(3)->data.basic.d == 256.0f); } } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "inputParam1"); CHECK(o.type.basetype == SDBasic::Struct); CHECK(o.type.flags == SDTypeFlags::Nullable); - CHECK(o.data.children[0]->data.basic.d == 9.0f); - CHECK(o.data.children[1]->data.basic.d == 9.9f); - CHECK(o.data.children[2]->data.basic.d == 9.99f); - CHECK(o.data.children[3]->data.basic.d == 9.999f); + CHECK(o.GetChild(0)->data.basic.d == 9.0f); + CHECK(o.GetChild(1)->data.basic.d == 9.9f); + CHECK(o.GetChild(2)->data.basic.d == 9.99f); + CHECK(o.GetChild(3)->data.basic.d == 9.999f); } { - SDObject &o = *chunk.data.children[childIdx++]; + SDObject &o = *chunk.GetChild(childIdx++); CHECK(o.name == "inputParam2"); CHECK(o.type.basetype == SDBasic::Null);