Skip to content

Commit

Permalink
Fix loading a nested archive
Browse files Browse the repository at this point in the history
The problem was that an intermediate archive failed to load because of the table validation failure
  • Loading branch information
alex-sparus committed Apr 23, 2024
1 parent 6556690 commit 2254d22
Show file tree
Hide file tree
Showing 9 changed files with 398 additions and 26 deletions.
14 changes: 9 additions & 5 deletions immer/extra/archive/champ/champ.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ struct node_traits
class hash_validation_failed_exception : public archive_exception
{
public:
hash_validation_failed_exception()
explicit hash_validation_failed_exception(const std::string& msg)
: archive_exception{"Hash validation failed, likely different hash "
"algos are used for saving and loading"}
"algos are used for saving and loading, " +
msg}
{
}
};
Expand All @@ -59,7 +60,7 @@ class container_loader
{
const value_t* operator()(const value_t& v) const noexcept
{
return &v;
return std::addressof(v);
}
};

Expand Down Expand Up @@ -103,10 +104,13 @@ class container_loader
project_value_ptr,
immer::detail::constantly<const value_t*, nullptr>>(item);
if (!p) {
throw hash_validation_failed_exception{};
throw hash_validation_failed_exception{
"Couldn't find an element"};
}
if (!(*p == item)) {
throw hash_validation_failed_exception{};
throw hash_validation_failed_exception{
"Found element is not equal to the one we were looking "
"for"};
}
}
}
Expand Down
13 changes: 9 additions & 4 deletions immer/extra/archive/json/archivable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include <fmt/format.h>

#include <boost/core/demangle.hpp>

namespace immer::archive {

namespace detail {
Expand Down Expand Up @@ -135,10 +137,13 @@ void load_minimal(
try {
value.container = loader.load(container_id_{id});
} catch (const archive_exception& ex) {
throw ::cereal::Exception{
fmt::format("Failed to load a container ID {} from the archive: {}",
id,
ex.what())};
if (!ar.get_input_archives().ignore_archive_exceptions) {
throw ::cereal::Exception{fmt::format(
"Failed to load a container ID {} from the archive of {}: {}",
id,
boost::core::demangle(typeid(Container).name()),
ex.what())};
}
}
}

Expand Down
1 change: 1 addition & 0 deletions immer/extra/archive/json/json_immer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class json_immer_input_archive
}

ImmerArchives& get_input_archives() { return archives; }
const ImmerArchives& get_input_archives() const { return archives; }

private:
cereal::JSONInputArchive archive;
Expand Down
9 changes: 7 additions & 2 deletions immer/extra/archive/json/json_with_archive.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ struct archives_load
using names_t = Names;

Storage storage;
bool ignore_archive_exceptions = false;

template <class Container>
auto& get_loader()
Expand Down Expand Up @@ -541,6 +542,9 @@ auto load_archives(std::istream& is,
auto prev = archives;
while (true) {
// Keep reloading until everything is loaded.
// Reloading of the archive might trigger validation of some containers
// (hash-based, for example) because the elements actually come from
// other archives that are not yet loaded.
archives = reload_archive(is, std::move(archives));
if (prev == archives) {
break;
Expand All @@ -552,8 +556,9 @@ auto load_archives(std::istream& is,
}

constexpr auto reload_archive = [](std::istream& is, auto archives) {
using Archives = std::decay_t<decltype(archives)>;
auto restore = util::istream_snapshot{is};
using Archives = std::decay_t<decltype(archives)>;
auto restore = util::istream_snapshot{is};
archives.ignore_archive_exceptions = true;
auto ar = json_immer_input_archive<Archives>{std::move(archives), is};
/**
* NOTE: Critical to clear the archives before loading into it
Expand Down
7 changes: 4 additions & 3 deletions immer/extra/archive/json/json_with_archive_auto.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,10 @@ auto load_initial_auto_archives(std::istream& is, WrapF wrap)

constexpr auto reload_archive_auto = [](auto wrap) {
return [wrap](std::istream& is, auto archives) {
using Archives = std::decay_t<decltype(archives)>;
using WrapF = std::decay_t<decltype(wrap)>;
auto restore = util::istream_snapshot{is};
using Archives = std::decay_t<decltype(archives)>;
using WrapF = std::decay_t<decltype(wrap)>;
auto restore = util::istream_snapshot{is};
archives.ignore_archive_exceptions = true;
auto previous =
json_immer_input_archive<Archives>{std::move(archives), is};
auto ar = json_immer_auto_input_archive<decltype(previous), WrapF>{
Expand Down
2 changes: 2 additions & 0 deletions test/extra/archive/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ add_executable(
test_conversion.cpp
test_circular_dependency_conversion.cpp
${PROJECT_SOURCE_DIR}/immer/extra/archive/xxhash/xxhash_64.cpp)
target_precompile_headers(archive-tests PRIVATE
<immer/extra/archive/json/json_with_archive_auto.hpp>)
add_dependencies(tests archive-tests)
add_test("test/archive-tests" archive-tests)
target_include_directories(archive-tests PRIVATE ${CMAKE_SOURCE_DIR})
Expand Down
54 changes: 44 additions & 10 deletions test/extra/archive/test_circular_dependency_conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,21 @@ struct value_two
int number = {};
vector_one<value_one> ones = {};
key key = {};

friend std::ostream& operator<<(std::ostream& s, const value_two& value)
{
return s << fmt::format("number = {}, ones = {}, key = '{}'",
value.number,
value.ones.size(),
value.key.str);
}
};

std::ostream& operator<<(std::ostream& s, const two_boxed& value)
{
return s << value.two.get();
}

const key& get_table_key(const two_boxed& two) { return two.two.get().key; }

std::size_t xx_hash_value(const two_boxed& value)
Expand All @@ -98,6 +111,10 @@ two_boxed::two_boxed(value_two val)

} // namespace model

template <>
struct fmt::formatter<model::two_boxed> : ostream_formatter
{};

BOOST_HANA_ADAPT_STRUCT(model::value_two, number, ones, key);

namespace model {
Expand Down Expand Up @@ -224,6 +241,14 @@ TEST_CASE("Test exception while circular converting")
immer::archive::to_json_with_auto_archive(value, names);
// REQUIRE(json_str == "");

SECTION("Try to load")
{
const auto loaded =
immer::archive::from_json_with_auto_archive<model::value_one>(
json_str, names);
REQUIRE(loaded == value);
}

/**
* NOTE: There is a circular dependency between archives: to convert
* value_one we need to convert value_two and vice versa.
Expand Down Expand Up @@ -554,16 +579,6 @@ TEST_CASE("Test circular dependency archives", "[conversion]")
REQUIRE(format_twos.impl().root == format_twos_2.impl().root);
}
REQUIRE(test::to_json(value.twos_table) == test::to_json(format_twos));

// SECTION("Compare structure")
// {
// const auto [format_twos_json, ar] =
// immer::archive::to_json_with_auto_archive(format_twos,
// format_names);
// const auto [model_twos_json, ar2] =
// immer::archive::to_json_with_auto_archive(value, names);
// REQUIRE(model_twos_json == format_twos_json);
// }
}

SECTION("map")
Expand Down Expand Up @@ -635,4 +650,23 @@ TEST_CASE("Test circular dependency archives", "[conversion]")
REQUIRE(model_twos_json == format_twos_json);
}
}

SECTION("everything")
{
const auto convert = [&](const auto& value) {
return immer::archive::convert_container(
model_archives, format_load_archives, value);
};
const auto format_value = [&] {
auto result = format::value_one{};
hana::for_each(hana::keys(result), [&](auto key) {
hana::at_key(result, key) = convert(hana::at_key(value, key));
});
return result;
}();
const auto [format_json_str, model_archives] =
immer::archive::to_json_with_auto_archive(format_value,
format_names);
REQUIRE(format_json_str == json_str);
}
}
8 changes: 6 additions & 2 deletions test/extra/archive/test_special_archive.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_exception.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>

#include "utils.hpp"

Expand Down Expand Up @@ -574,8 +575,11 @@ TEST_CASE("Special archive throws cereal::Exception")
REQUIRE_THROWS_MATCHES(
immer::archive::from_json_with_archive<test_data>(json_archive_str),
::cereal::Exception,
Catch::Matchers::Message("Failed to load a container ID 99 from the "
"archive: Container ID 99 is not found"));
MessageMatches(Catch::Matchers::ContainsSubstring(
"Failed to load a container ID 99 from the "
"archive of immer::vector<int") &&
Catch::Matchers::ContainsSubstring(
"Container ID 99 is not found")));
}

namespace {
Expand Down
Loading

0 comments on commit 2254d22

Please sign in to comment.