Program Listing for File archive.cpp¶
↰ Return to documentation for file (portal/serialization/archive.cpp)
//
// Copyright © 2025 Jonatan Nevo.
// Distributed under the MIT license (see LICENSE file).
//
#include "archive.h"
namespace portal
{
void ArchiveObject::update(const ArchiveObject& other)
{
for (auto& [name, prop] : other.property_map)
{
if (prop.type == reflection::PropertyType::object)
{
if (prop.container_type == reflection::PropertyContainerType::object)
{
auto* other_object = other.get_object(name);
auto* child = get_object(name);
if (child)
child->update(*other_object);
else
create_child(name)->update(*other_object);
}
if (prop.container_type == reflection::PropertyContainerType::array)
{
auto* other_objects = prop.value.as<ArchiveObject*>();
// Check if we already have this property as an array
auto& existing_prop = get_property_from_map(name);
if (existing_prop.type == reflection::PropertyType::object &&
existing_prop.container_type == reflection::PropertyContainerType::array &&
existing_prop.elements_number == prop.elements_number)
{
// Update each element in the existing array
auto* our_objects = existing_prop.value.as<ArchiveObject*>();
for (size_t i = 0; i < prop.elements_number; ++i)
{
our_objects[i].update(other_objects[i]);
}
}
else
{
// Create a new array by copying from other
Buffer buffer = Buffer::allocate(prop.elements_number * sizeof(ArchiveObject));
auto* new_objects = buffer.as<ArchiveObject*>();
for (size_t i = 0; i < prop.elements_number; ++i)
{
new (new_objects + i) ArchiveObject(other_objects[i]);
}
property_map[name] = reflection::Property{
.value = std::move(buffer),
.type = reflection::PropertyType::object,
.container_type = reflection::PropertyContainerType::array,
.elements_number = prop.elements_number
};
}
}
}
else
{
property_map[name] = reflection::Property{
.value = Buffer::copy(prop.value),
.type = prop.type,
.container_type = prop.container_type,
.elements_number = prop.elements_number
};
}
}
}
void ArchiveObject::add_property(const PropertyName& name, const char* t)
{
const auto len = strlen(t);
add_property_to_map(
name,
reflection::Property{
Buffer{const_cast<void*>(static_cast<const void*>(t)), (len * sizeof(char)) + 1},
reflection::PropertyType::character,
reflection::PropertyContainerType::null_term_string,
len + 1
}
);
}
void ArchiveObject::add_property(const PropertyName& name, const std::filesystem::path& t)
{
add_property(name, t.string());
}
void ArchiveObject::add_property(const PropertyName& name, const StringId& string_id)
{
add_property(name, string_id.string);
}
bool ArchiveObject::get_property(const PropertyName& name, std::filesystem::path& out)
{
std::string string;
if (!get_property<std::string>(name, string))
return false;
// TODO: make absolute?
out = std::filesystem::path(string);
return true;
}
bool ArchiveObject::get_property(const PropertyName& name, StringId& out)
{
std::string string;
if (!get_property<std::string>(name, string))
return false;
out = STRING_ID(string);
return true;
}
ArchiveObject* ArchiveObject::create_child(const PropertyName name)
{
reflection::Property child_property{
Buffer::create<ArchiveObject>(),
reflection::PropertyType::object,
reflection::PropertyContainerType::object,
1
};
const auto& new_node = add_property_to_map(name, std::move(child_property));
return new_node.value.as<ArchiveObject*>();
}
reflection::Property& ArchiveObject::add_property_to_map(PropertyName name, reflection::Property&& property)
{
auto& prop = get_property_from_map(name);
PORTAL_ASSERT(
prop.type == reflection::PropertyType::invalid || prop.type == property.type,
"Property {} already exists with a different type",
name
);
prop = std::move(property);
return prop;
}
ArchiveObject* ArchiveObject::get_object(PropertyName name) const
{
#ifdef PORTAL_DEBUG
if (!property_map.contains(std::string{name}))
return nullptr;
#else
if (!property_map.contains(name))
return nullptr;
#endif
const auto& prop = get_property_from_map(name);
if (prop.type == reflection::PropertyType::invalid || prop.container_type != reflection::PropertyContainerType::object)
{
return nullptr;
}
return prop.value.as<ArchiveObject*>();
}
reflection::Property& ArchiveObject::get_property_from_map(const PropertyName name)
{
#ifdef PORTAL_DEBUG
return property_map[std::string{name}];
#else
return property_map[name];
#endif
}
const reflection::Property& ArchiveObject::get_property_from_map(PropertyName name) const
{
#ifdef PORTAL_DEBUG
return property_map.at(std::string{name});
#else
return property_map.at(name);
#endif
}
}