Skip to content

Commit 8d43497

Browse files
committed
Fix loading a nested archive
The problem was that an intermediate archive failed to load because of the table validation failure
1 parent 6556690 commit 8d43497

8 files changed

+397
-26
lines changed

immer/extra/archive/champ/champ.hpp

+9-5
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ struct node_traits
3737
class hash_validation_failed_exception : public archive_exception
3838
{
3939
public:
40-
hash_validation_failed_exception()
40+
explicit hash_validation_failed_exception(const std::string& msg)
4141
: archive_exception{"Hash validation failed, likely different hash "
42-
"algos are used for saving and loading"}
42+
"algos are used for saving and loading, " +
43+
msg}
4344
{
4445
}
4546
};
@@ -59,7 +60,7 @@ class container_loader
5960
{
6061
const value_t* operator()(const value_t& v) const noexcept
6162
{
62-
return &v;
63+
return std::addressof(v);
6364
}
6465
};
6566

@@ -103,10 +104,13 @@ class container_loader
103104
project_value_ptr,
104105
immer::detail::constantly<const value_t*, nullptr>>(item);
105106
if (!p) {
106-
throw hash_validation_failed_exception{};
107+
throw hash_validation_failed_exception{
108+
"Couldn't find an element"};
107109
}
108110
if (!(*p == item)) {
109-
throw hash_validation_failed_exception{};
111+
throw hash_validation_failed_exception{
112+
"Found element is not equal to the one we were looking "
113+
"for"};
110114
}
111115
}
112116
}

immer/extra/archive/json/archivable.hpp

+9-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include <fmt/format.h>
1010

11+
#include <boost/core/demangle.hpp>
12+
1113
namespace immer::archive {
1214

1315
namespace detail {
@@ -135,10 +137,13 @@ void load_minimal(
135137
try {
136138
value.container = loader.load(container_id_{id});
137139
} catch (const archive_exception& ex) {
138-
throw ::cereal::Exception{
139-
fmt::format("Failed to load a container ID {} from the archive: {}",
140-
id,
141-
ex.what())};
140+
if (!ar.get_input_archives().ignore_archive_exceptions) {
141+
throw ::cereal::Exception{fmt::format(
142+
"Failed to load a container ID {} from the archive of {}: {}",
143+
id,
144+
boost::core::demangle(typeid(Container).name()),
145+
ex.what())};
146+
}
142147
}
143148
}
144149

immer/extra/archive/json/json_immer.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class json_immer_input_archive
8888
}
8989

9090
ImmerArchives& get_input_archives() { return archives; }
91+
const ImmerArchives& get_input_archives() const { return archives; }
9192

9293
private:
9394
cereal::JSONInputArchive archive;

immer/extra/archive/json/json_with_archive.hpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ struct archives_load
183183
using names_t = Names;
184184

185185
Storage storage;
186+
bool ignore_archive_exceptions = false;
186187

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

554558
constexpr auto reload_archive = [](std::istream& is, auto archives) {
555-
using Archives = std::decay_t<decltype(archives)>;
556-
auto restore = util::istream_snapshot{is};
559+
using Archives = std::decay_t<decltype(archives)>;
560+
auto restore = util::istream_snapshot{is};
561+
archives.ignore_archive_exceptions = true;
557562
auto ar = json_immer_input_archive<Archives>{std::move(archives), is};
558563
/**
559564
* NOTE: Critical to clear the archives before loading into it

immer/extra/archive/json/json_with_archive_auto.hpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,10 @@ auto load_initial_auto_archives(std::istream& is, WrapF wrap)
229229

230230
constexpr auto reload_archive_auto = [](auto wrap) {
231231
return [wrap](std::istream& is, auto archives) {
232-
using Archives = std::decay_t<decltype(archives)>;
233-
using WrapF = std::decay_t<decltype(wrap)>;
234-
auto restore = util::istream_snapshot{is};
232+
using Archives = std::decay_t<decltype(archives)>;
233+
using WrapF = std::decay_t<decltype(wrap)>;
234+
auto restore = util::istream_snapshot{is};
235+
archives.ignore_archive_exceptions = true;
235236
auto previous =
236237
json_immer_input_archive<Archives>{std::move(archives), is};
237238
auto ar = json_immer_auto_input_archive<decltype(previous), WrapF>{

test/extra/archive/test_circular_dependency_conversion.cpp

+44-10
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,21 @@ struct value_two
8282
int number = {};
8383
vector_one<value_one> ones = {};
8484
key key = {};
85+
86+
friend std::ostream& operator<<(std::ostream& s, const value_two& value)
87+
{
88+
return s << fmt::format("number = {}, ones = {}, key = '{}'",
89+
value.number,
90+
value.ones.size(),
91+
value.key.str);
92+
}
8593
};
8694

95+
std::ostream& operator<<(std::ostream& s, const two_boxed& value)
96+
{
97+
return s << value.two.get();
98+
}
99+
87100
const key& get_table_key(const two_boxed& two) { return two.two.get().key; }
88101

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

99112
} // namespace model
100113

114+
template <>
115+
struct fmt::formatter<model::two_boxed> : ostream_formatter
116+
{};
117+
101118
BOOST_HANA_ADAPT_STRUCT(model::value_two, number, ones, key);
102119

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

244+
SECTION("Try to load")
245+
{
246+
const auto loaded =
247+
immer::archive::from_json_with_auto_archive<model::value_one>(
248+
json_str, names);
249+
REQUIRE(loaded == value);
250+
}
251+
227252
/**
228253
* NOTE: There is a circular dependency between archives: to convert
229254
* value_one we need to convert value_two and vice versa.
@@ -554,16 +579,6 @@ TEST_CASE("Test circular dependency archives", "[conversion]")
554579
REQUIRE(format_twos.impl().root == format_twos_2.impl().root);
555580
}
556581
REQUIRE(test::to_json(value.twos_table) == test::to_json(format_twos));
557-
558-
// SECTION("Compare structure")
559-
// {
560-
// const auto [format_twos_json, ar] =
561-
// immer::archive::to_json_with_auto_archive(format_twos,
562-
// format_names);
563-
// const auto [model_twos_json, ar2] =
564-
// immer::archive::to_json_with_auto_archive(value, names);
565-
// REQUIRE(model_twos_json == format_twos_json);
566-
// }
567582
}
568583

569584
SECTION("map")
@@ -635,4 +650,23 @@ TEST_CASE("Test circular dependency archives", "[conversion]")
635650
REQUIRE(model_twos_json == format_twos_json);
636651
}
637652
}
653+
654+
SECTION("everything")
655+
{
656+
const auto convert = [&](const auto& value) {
657+
return immer::archive::convert_container(
658+
model_archives, format_load_archives, value);
659+
};
660+
const auto format_value = [&] {
661+
auto result = format::value_one{};
662+
hana::for_each(hana::keys(result), [&](auto key) {
663+
hana::at_key(result, key) = convert(hana::at_key(value, key));
664+
});
665+
return result;
666+
}();
667+
const auto [format_json_str, model_archives] =
668+
immer::archive::to_json_with_auto_archive(format_value,
669+
format_names);
670+
REQUIRE(format_json_str == json_str);
671+
}
638672
}

test/extra/archive/test_special_archive.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -574,8 +574,13 @@ TEST_CASE("Special archive throws cereal::Exception")
574574
REQUIRE_THROWS_MATCHES(
575575
immer::archive::from_json_with_archive<test_data>(json_archive_str),
576576
::cereal::Exception,
577-
Catch::Matchers::Message("Failed to load a container ID 99 from the "
578-
"archive: Container ID 99 is not found"));
577+
Catch::Matchers::Message(
578+
"Failed to load a container ID 99 from the "
579+
"archive of immer::vector<int, "
580+
"immer::memory_policy<immer::heap_policy<immer::debug_size_heap<"
581+
"immer::cpp_heap>>, immer::refcount_policy, "
582+
"immer::spinlock_policy, immer::no_transience_policy, false, "
583+
"true>, 5u, 1u>: Container ID 99 is not found"));
579584
}
580585

581586
namespace {

0 commit comments

Comments
 (0)