Skip to content

Commit 80fd4b3

Browse files
committed
Integrate immer-archive
1 parent 57a8c5e commit 80fd4b3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+8359
-32
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ option(ENABLE_GUILE "enable building guile module" off)
3636
option(ENABLE_BOOST_COROUTINE "run benchmarks with boost coroutine" off)
3737

3838
option(immer_BUILD_TESTS "Build tests" ON)
39+
option(immer_BUILD_ARCHIVE_TESTS "Build experimental archive tests" off)
3940
option(immer_BUILD_EXAMPLES "Build examples" ON)
4041
option(immer_BUILD_DOCS "Build docs" ON)
4142
option(immer_BUILD_EXTRAS "Build extras" ON)

extra/fuzzer/CMakeLists.txt

+20-11
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,41 @@
1+
add_custom_target(fuzzers COMMENT "Build all fuzzers.")
12

2-
add_custom_target(fuzzers
3-
COMMENT "Build all fuzzers.")
4-
5-
if (CHECK_FUZZERS)
3+
if(CHECK_FUZZERS)
64
add_dependencies(tests fuzzers)
75
endif()
86

9-
# LIB_FUZZING_ENGINE is set by the Google OSS-Fuzz
10-
# infrastructure. Otherwise we use Clang's LibFuzzer
11-
if (DEFINED ENV{LIB_FUZZING_ENGINE})
7+
# LIB_FUZZING_ENGINE is set by the Google OSS-Fuzz infrastructure. Otherwise we
8+
# use Clang's LibFuzzer
9+
if(DEFINED ENV{LIB_FUZZING_ENGINE})
1210
set(immer_fuzzing_engine $ENV{LIB_FUZZING_ENGINE})
1311
else()
1412
set(immer_fuzzing_engine "-fsanitize=fuzzer")
1513
endif()
1614

1715
file(GLOB_RECURSE immer_fuzzers "*.cpp")
16+
foreach(TMP_PATH ${immer_fuzzers})
17+
string(FIND ${TMP_PATH} immer-archive EXCLUDE_DIR_FOUND)
18+
if(NOT ${EXCLUDE_DIR_FOUND} EQUAL -1)
19+
list(REMOVE_ITEM immer_fuzzers ${TMP_PATH})
20+
endif()
21+
endforeach(TMP_PATH)
22+
1823
foreach(_file IN LISTS immer_fuzzers)
1924
immer_target_name_for(_target _output "${_file}")
2025
add_executable(${_target} EXCLUDE_FROM_ALL "${_file}")
2126
set_target_properties(${_target} PROPERTIES OUTPUT_NAME ${_output})
2227
target_compile_options(${_target} PUBLIC ${immer_fuzzing_engine})
23-
target_link_libraries(${_target} PUBLIC ${immer_fuzzing_engine}
24-
immer-dev)
28+
target_compile_definitions(${_target} PUBLIC IMMER_THROW_ON_INVALID_STATE=1)
29+
target_link_libraries(${_target} PUBLIC ${immer_fuzzing_engine} immer-dev)
2530
add_dependencies(fuzzers ${_target})
26-
if (CHECK_FUZZERS)
31+
if(CHECK_FUZZERS)
2732
add_test("fuzzer/${_output}" ${_output} -max_total_time=1)
2833
endif()
29-
if (immer_INSTALL_FUZZERS)
34+
if(immer_INSTALL_FUZZERS)
3035
install(TARGETS ${_target} DESTINATION bin)
3136
endif()
3237
endforeach()
38+
39+
if(immer_BUILD_ARCHIVE_TESTS)
40+
add_subdirectory(immer-archive)
41+
endif()

extra/fuzzer/flex-vector-st.cpp

+19-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,25 @@ extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
4242
auto is_valid_size = [](auto& v) {
4343
return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
4444
};
45-
auto can_concat = [](auto&& v1, auto&& v2) {
46-
return v1.size() + v2.size() < vector_t::max_size();
45+
auto can_concat = [](const auto& v1, const auto& v2) {
46+
// First, check max_size
47+
if (v1.size() + v2.size() > vector_t::max_size()) {
48+
return false;
49+
}
50+
51+
// But just checking max_size is not sufficient, because there are other
52+
// conditions for the validity of the tree, like shift constraints, for
53+
// example.
54+
try {
55+
// Try to concat and catch an exception if it fails
56+
const auto v3 = v1 + v2;
57+
if (v3.size()) {
58+
return true;
59+
}
60+
} catch (const immer::detail::rbts::invalid_tree&) {
61+
return false;
62+
}
63+
return true;
4764
};
4865
auto can_compare = [](auto&& v) {
4966
// avoid comparing vectors that are too big, and hence, slow to compare
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
find_package(spdlog REQUIRED)
2+
3+
file(GLOB_RECURSE immer_fuzzers "*.cpp")
4+
foreach(_file IN LISTS immer_fuzzers)
5+
immer_target_name_for(_target _output "${_file}")
6+
add_executable(${_target} EXCLUDE_FROM_ALL "${_file}")
7+
set_target_properties(${_target} PROPERTIES OUTPUT_NAME ${_output})
8+
target_compile_options(${_target} PUBLIC ${immer_fuzzing_engine})
9+
target_include_directories(${_target} PRIVATE ../..)
10+
target_link_libraries(${_target} PUBLIC ${immer_fuzzing_engine} immer-dev
11+
spdlog::spdlog)
12+
add_dependencies(fuzzers ${_target})
13+
if(CHECK_FUZZERS)
14+
add_test("fuzzer/${_output}" ${_output} -max_total_time=1)
15+
endif()
16+
if(immer_INSTALL_FUZZERS)
17+
install(TARGETS ${_target} DESTINATION bin)
18+
endif()
19+
endforeach()
+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
//
2+
// immer: immutable data structures for C++
3+
// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4+
//
5+
// This software is distributed under the Boost Software License, Version 1.0.
6+
// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7+
//
8+
9+
#include <fuzzer/fuzzer_input.hpp>
10+
11+
#include <immer/box.hpp>
12+
#include <immer/flex_vector.hpp>
13+
14+
#include <fmt/ranges.h>
15+
#include <immer/experimental/immer-archive/rbts/load.hpp>
16+
#include <immer/experimental/immer-archive/rbts/save.hpp>
17+
18+
#include <array>
19+
20+
namespace {
21+
void require_eq(const auto& a, const auto& b)
22+
{
23+
if (a != b) {
24+
throw std::runtime_error{
25+
fmt::format("{} != {}", fmt::join(a, ", "), fmt::join(b, ", "))};
26+
}
27+
}
28+
} // namespace
29+
30+
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
31+
std::size_t size)
32+
{
33+
constexpr auto var_count = 8;
34+
constexpr auto bits = 2;
35+
36+
using vector_t =
37+
immer::flex_vector<int, immer::default_memory_policy, bits, bits>;
38+
using size_t = std::uint8_t;
39+
40+
const auto check_save_and_load = [&](const auto& vec) {
41+
auto ar = immer_archive::rbts::make_save_archive_for(vec);
42+
auto vector_id = immer_archive::container_id{};
43+
std::tie(ar, vector_id) = immer_archive::rbts::save_to_archive(vec, ar);
44+
45+
auto loader =
46+
immer_archive::rbts::make_loader_for(vec, fix_leaf_nodes(ar));
47+
auto loaded = loader.load(vector_id);
48+
require_eq(vec, loaded);
49+
};
50+
51+
auto vars = std::array<vector_t, var_count>{};
52+
53+
auto is_valid_var = [&](auto idx) { return idx >= 0 && idx < var_count; };
54+
auto is_valid_var_neq = [](auto other) {
55+
return [=](auto idx) {
56+
return idx >= 0 && idx < var_count && idx != other;
57+
};
58+
};
59+
auto is_valid_index = [](auto& v) {
60+
return [&](auto idx) { return idx >= 0 && idx < v.size(); };
61+
};
62+
auto is_valid_size = [](auto& v) {
63+
return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
64+
};
65+
auto can_concat = [](auto&& v1, auto&& v2) {
66+
return v1.size() + v2.size() < vector_t::max_size() / 4;
67+
};
68+
auto can_compare = [](auto&& v) {
69+
// avoid comparing vectors that are too big, and hence, slow to compare
70+
return v.size() < (1 << 15);
71+
};
72+
return fuzzer_input{data, size}.run([&](auto& in) {
73+
enum ops
74+
{
75+
op_push_back,
76+
op_update,
77+
op_take,
78+
op_drop,
79+
op_concat,
80+
op_push_back_move,
81+
op_update_move,
82+
op_take_move,
83+
op_drop_move,
84+
op_concat_move_l,
85+
op_concat_move_r,
86+
op_concat_move_lr,
87+
op_insert,
88+
op_erase,
89+
op_compare,
90+
};
91+
auto src = read<char>(in, is_valid_var);
92+
auto dst = read<char>(in, is_valid_var);
93+
const auto op = read<char>(in);
94+
SPDLOG_DEBUG("op = {}", static_cast<int>(op));
95+
switch (op) {
96+
case op_push_back: {
97+
vars[dst] = vars[src].push_back(42);
98+
break;
99+
}
100+
case op_update: {
101+
auto idx = read<size_t>(in, is_valid_index(vars[src]));
102+
vars[dst] = vars[src].update(idx, [](auto x) { return x + 1; });
103+
break;
104+
}
105+
case op_take: {
106+
auto idx = read<size_t>(in, is_valid_size(vars[src]));
107+
vars[dst] = vars[src].take(idx);
108+
break;
109+
}
110+
case op_drop: {
111+
auto idx = read<size_t>(in, is_valid_size(vars[src]));
112+
vars[dst] = vars[src].drop(idx);
113+
break;
114+
}
115+
case op_concat: {
116+
auto src2 = read<char>(in, is_valid_var);
117+
if (can_concat(vars[src], vars[src2]))
118+
vars[dst] = vars[src] + vars[src2];
119+
break;
120+
}
121+
case op_push_back_move: {
122+
vars[dst] = std::move(vars[src]).push_back(21);
123+
break;
124+
}
125+
case op_update_move: {
126+
auto idx = read<size_t>(in, is_valid_index(vars[src]));
127+
vars[dst] =
128+
std::move(vars[src]).update(idx, [](auto x) { return x + 1; });
129+
break;
130+
}
131+
case op_take_move: {
132+
auto idx = read<size_t>(in, is_valid_size(vars[src]));
133+
vars[dst] = std::move(vars[src]).take(idx);
134+
break;
135+
}
136+
case op_drop_move: {
137+
auto idx = read<size_t>(in, is_valid_size(vars[src]));
138+
vars[dst] = std::move(vars[src]).drop(idx);
139+
break;
140+
}
141+
case op_concat_move_l: {
142+
auto src2 = read<char>(in, is_valid_var_neq(src));
143+
if (can_concat(vars[src], vars[src2]))
144+
vars[dst] = std::move(vars[src]) + vars[src2];
145+
break;
146+
}
147+
case op_concat_move_r: {
148+
auto src2 = read<char>(in, is_valid_var_neq(src));
149+
if (can_concat(vars[src], vars[src2]))
150+
vars[dst] = vars[src] + std::move(vars[src2]);
151+
break;
152+
}
153+
case op_concat_move_lr: {
154+
auto src2 = read<char>(in, is_valid_var_neq(src));
155+
if (can_concat(vars[src], vars[src2]))
156+
vars[dst] = std::move(vars[src]) + std::move(vars[src2]);
157+
break;
158+
}
159+
case op_compare: {
160+
using std::swap;
161+
if (can_compare(vars[src]) && vars[src] == vars[dst])
162+
swap(vars[src], vars[dst]);
163+
break;
164+
}
165+
case op_erase: {
166+
auto idx = read<size_t>(in, is_valid_index(vars[src]));
167+
vars[dst] = vars[src].erase(idx);
168+
break;
169+
}
170+
case op_insert: {
171+
auto idx = read<size_t>(in, is_valid_size(vars[src]));
172+
vars[dst] = vars[src].insert(idx, immer::box<int>{42});
173+
break;
174+
}
175+
default:
176+
break;
177+
};
178+
179+
check_save_and_load(vars[src]);
180+
check_save_and_load(vars[dst]);
181+
182+
return true;
183+
});
184+
}

0 commit comments

Comments
 (0)