Skip to content

Commit c12dd00

Browse files
authoredMar 6, 2025··
datastore: HistoryRangeByKeys and DomainRangeAsOf (#2758)
extras: * rename HistoryRangeQuery to HistoryRangeInPeriodQuery * fix HistoryRangeInPeriodQuery: merge by key instead of join
2 parents 9912e31 + a9bd934 commit c12dd00

29 files changed

+1163
-150
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
Copyright 2025 The Silkworm Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <cstdlib>
20+
#include <iterator>
21+
#include <optional>
22+
#include <ranges>
23+
#include <type_traits>
24+
#include <utility>
25+
26+
#include <silkworm/core/common/assert.hpp>
27+
28+
namespace silkworm::views {
29+
30+
template <std::ranges::input_range Range1, std::ranges::input_range Range2>
31+
class IfView : public std::ranges::view_interface<IfView<Range1, Range2>> {
32+
public:
33+
class Iterator {
34+
public:
35+
using Range1Iterator = std::ranges::iterator_t<Range1>;
36+
using Range1Sentinel = std::ranges::sentinel_t<Range1>;
37+
using Range1ReferenceType = std::iter_reference_t<Range1Iterator>;
38+
using Range2Iterator = std::ranges::iterator_t<Range2>;
39+
using Range2Sentinel = std::ranges::sentinel_t<Range2>;
40+
using Range2ReferenceType = std::iter_reference_t<Range2Iterator>;
41+
using DereferenceType = std::conditional_t<!std::is_reference_v<Range1ReferenceType>, Range1ReferenceType, Range2ReferenceType>;
42+
43+
using value_type = std::iter_value_t<Range1Iterator>;
44+
using iterator_category [[maybe_unused]] = std::input_iterator_tag;
45+
using difference_type = std::iter_difference_t<Range1Iterator>;
46+
using reference = DereferenceType;
47+
using pointer = std::remove_reference_t<reference>*;
48+
49+
Iterator() = default;
50+
Iterator(
51+
std::optional<Range1Iterator> it1,
52+
std::optional<Range1Sentinel> sentinel1,
53+
std::optional<Range2Iterator> it2,
54+
std::optional<Range2Sentinel> sentinel2)
55+
: it1_{std::move(it1)},
56+
sentinel1_{std::move(sentinel1)},
57+
it2_{std::move(it2)},
58+
sentinel2_{std::move(sentinel2)} {}
59+
60+
reference operator*() const {
61+
if (it1_) return **it1_;
62+
if (it2_) return **it2_;
63+
SILKWORM_ASSERT(false);
64+
std::abort();
65+
}
66+
67+
Iterator operator++(int) { return std::exchange(*this, ++Iterator{*this}); }
68+
Iterator& operator++() {
69+
if (it1_) ++(*it1_);
70+
if (it2_) ++(*it2_);
71+
return *this;
72+
}
73+
74+
friend bool operator==(const Iterator& it, const std::default_sentinel_t&) {
75+
return (it.it1_ && (*it.it1_ == *it.sentinel1_)) ||
76+
(it.it2_ && (*it.it2_ == *it.sentinel2_));
77+
}
78+
friend bool operator!=(const Iterator& it, const std::default_sentinel_t& s) {
79+
return !(it == s);
80+
}
81+
friend bool operator==(const std::default_sentinel_t& s, const Iterator& it) {
82+
return it == s;
83+
}
84+
friend bool operator!=(const std::default_sentinel_t& s, const Iterator& it) {
85+
return !(it == s);
86+
}
87+
88+
private:
89+
std::optional<Range1Iterator> it1_;
90+
std::optional<Range1Sentinel> sentinel1_;
91+
std::optional<Range2Iterator> it2_;
92+
std::optional<Range2Sentinel> sentinel2_;
93+
};
94+
95+
static_assert(std::input_iterator<Iterator>);
96+
97+
IfView(bool cond, Range1 range1, Range2 range2)
98+
: cond_{cond},
99+
range1_{std::move(range1)},
100+
range2_{std::move(range2)} {}
101+
IfView() = default;
102+
103+
IfView(IfView&&) = default;
104+
IfView& operator=(IfView&&) noexcept = default;
105+
106+
Iterator begin() {
107+
if (cond_) {
108+
return Iterator{
109+
std::ranges::begin(range1_),
110+
std::ranges::end(range1_),
111+
std::nullopt,
112+
std::nullopt,
113+
};
114+
} else {
115+
return Iterator{
116+
std::nullopt,
117+
std::nullopt,
118+
std::ranges::begin(range2_),
119+
std::ranges::end(range2_),
120+
};
121+
}
122+
}
123+
124+
std::default_sentinel_t end() const { return std::default_sentinel; }
125+
126+
private:
127+
bool cond_{false};
128+
Range1 range1_;
129+
Range2 range2_;
130+
};
131+
132+
template <class Range1, class Range2>
133+
IfView<Range1, Range2> if_view(bool cond, Range1&& v1, Range2&& v2) {
134+
return IfView<Range1, Range2>{cond, std::forward<Range1>(v1), std::forward<Range2>(v2)};
135+
}
136+
137+
} // namespace silkworm::views

‎silkworm/db/datastore/common/ranges/merge_unique_compare_func.hpp ‎silkworm/db/datastore/common/ranges/merge_compare_func.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
namespace silkworm::views {
2222

23-
struct MergeUniqueCompareFunc {
23+
struct MergeCompareFunc {
2424
template <typename T>
2525
constexpr std::strong_ordering operator()(const T& lhs, const T& rhs) const noexcept {
2626
return std::compare_strong_order_fallback(lhs, rhs);

‎silkworm/db/datastore/common/ranges/merge_unique_many_view.hpp ‎silkworm/db/datastore/common/ranges/merge_many_view.hpp

+47-21
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,17 @@
2828

2929
#include <silkworm/core/common/assert.hpp>
3030

31-
#include "merge_unique_compare_func.hpp"
31+
#include "merge_compare_func.hpp"
3232

3333
namespace silkworm::views {
3434

3535
template <
3636
std::ranges::input_range Ranges,
3737
std::ranges::input_range Range = std::iter_value_t<std::ranges::iterator_t<Ranges>>,
38-
class Comp = MergeUniqueCompareFunc,
39-
class Proj = std::identity>
40-
class MergeUniqueManyView : public std::ranges::view_interface<MergeUniqueManyView<Range, Ranges, Comp, Proj>> {
38+
class Comp = MergeCompareFunc,
39+
class Proj = std::identity,
40+
bool kUnique = false>
41+
class MergeManyView : public std::ranges::view_interface<MergeManyView<Range, Ranges, Comp, Proj, kUnique>> {
4142
public:
4243
class Iterator {
4344
public:
@@ -82,22 +83,18 @@ class MergeUniqueManyView : public std::ranges::view_interface<MergeUniqueManyVi
8283

8384
Iterator operator++(int) { return std::exchange(*this, ++Iterator{*this}); }
8485
Iterator& operator++() {
86+
if constexpr (!kUnique) {
87+
next();
88+
return *this;
89+
}
90+
8591
// backup the current key for duplicate detection
8692
auto current_key = std::invoke(proj_, **this);
8793

8894
// first iteration: increment the current iterator once and restore the order
8995
// next iterations: skip duplicate keys and restore the order
9096
do {
91-
size_t current = order_.front();
92-
++iterators_[current];
93-
94-
std::ranges::pop_heap(order_, order_compare_func());
95-
order_.pop_back();
96-
97-
if (!it_ended(current)) {
98-
order_.push_back(current);
99-
std::ranges::push_heap(order_, order_compare_func());
100-
}
97+
next();
10198
} while (
10299
!order_.empty() &&
103100
std::is_eq(std::invoke(*comp_, std::invoke(proj_, *iterators_[order_.front()]), current_key)));
@@ -119,6 +116,20 @@ class MergeUniqueManyView : public std::ranges::view_interface<MergeUniqueManyVi
119116
}
120117

121118
private:
119+
//! Increment the current iterator once and restore the order
120+
void next() {
121+
size_t current = order_.front();
122+
++iterators_[current];
123+
124+
std::ranges::pop_heap(order_, order_compare_func());
125+
order_.pop_back();
126+
127+
if (!it_ended(current)) {
128+
order_.push_back(current);
129+
std::ranges::push_heap(order_, order_compare_func());
130+
}
131+
}
132+
122133
bool it_ended(size_t i) const {
123134
return iterators_[i] == sentinels_[i];
124135
}
@@ -152,16 +163,16 @@ class MergeUniqueManyView : public std::ranges::view_interface<MergeUniqueManyVi
152163

153164
static_assert(std::input_iterator<Iterator>);
154165

155-
MergeUniqueManyView(
166+
MergeManyView(
156167
Ranges ranges,
157168
Comp comp, Proj proj)
158169
: ranges_{std::move(ranges)},
159170
comp_{std::move(comp)},
160171
proj_{std::move(proj)} {}
161-
MergeUniqueManyView() = default;
172+
MergeManyView() = default;
162173

163-
MergeUniqueManyView(MergeUniqueManyView&&) = default;
164-
MergeUniqueManyView& operator=(MergeUniqueManyView&&) noexcept = default;
174+
MergeManyView(MergeManyView&&) = default;
175+
MergeManyView& operator=(MergeManyView&&) noexcept = default;
165176

166177
Iterator begin() { return Iterator{ranges_, &comp_, proj_}; }
167178
std::default_sentinel_t end() const { return std::default_sentinel; }
@@ -175,12 +186,27 @@ class MergeUniqueManyView : public std::ranges::view_interface<MergeUniqueManyVi
175186
template <
176187
class Ranges,
177188
class Range = std::iter_value_t<std::ranges::iterator_t<Ranges>>,
178-
class Comp = MergeUniqueCompareFunc,
189+
class Comp = MergeCompareFunc,
190+
class Proj = std::identity>
191+
MergeManyView<Ranges, Range, Comp, Proj> merge_many(
192+
Ranges&& ranges,
193+
Comp comp = {}, Proj proj = {}) {
194+
return MergeManyView<Ranges, Range, Comp, Proj>{
195+
std::forward<Ranges>(ranges),
196+
std::move(comp),
197+
std::move(proj),
198+
};
199+
}
200+
201+
template <
202+
class Ranges,
203+
class Range = std::iter_value_t<std::ranges::iterator_t<Ranges>>,
204+
class Comp = MergeCompareFunc,
179205
class Proj = std::identity>
180-
MergeUniqueManyView<Ranges, Range, Comp, Proj> merge_unique_many(
206+
MergeManyView<Ranges, Range, Comp, Proj, /* kUnique = */ true> merge_unique_many(
181207
Ranges&& ranges,
182208
Comp comp = {}, Proj proj = {}) {
183-
return MergeUniqueManyView<Ranges, Range, Comp, Proj>{
209+
return MergeManyView<Ranges, Range, Comp, Proj, /* kUnique = */ true>{
184210
std::forward<Ranges>(ranges),
185211
std::move(comp),
186212
std::move(proj),

‎silkworm/db/datastore/common/ranges/merge_unique_many_view_test.cpp ‎silkworm/db/datastore/common/ranges/merge_many_view_test.cpp

+47-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
limitations under the License.
1515
*/
1616

17-
#include "merge_unique_many_view.hpp"
17+
#include "merge_many_view.hpp"
1818

1919
#include <catch2/catch_test_macros.hpp>
2020

@@ -23,8 +23,8 @@
2323

2424
namespace silkworm::views {
2525

26-
static_assert(std::ranges::input_range<MergeUniqueManyView<std::vector<std::vector<int>>>>);
27-
static_assert(std::ranges::view<MergeUniqueManyView<std::vector<std::vector<int>>>>);
26+
static_assert(std::ranges::input_range<MergeManyView<std::vector<std::vector<int>>>>);
27+
static_assert(std::ranges::view<MergeManyView<std::vector<std::vector<int>>>>);
2828

2929
template <std::ranges::input_range TRange>
3030
std::vector<TRange> ranges(TRange r1, TRange r2) {
@@ -37,6 +37,50 @@ std::vector<TRange> ranges(TRange r1, TRange r2) {
3737
// Skip to avoid Windows error C3889: call to object of class type 'std::ranges::_Begin::_Cpo': no matching call operator found
3838
// Unable to reproduce: https://godbolt.org/z/3jd5brKMj
3939
#ifndef _WIN32
40+
TEST_CASE("MergeManyView") {
41+
CHECK(vector_from_range(merge_many(ranges(
42+
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}),
43+
silkworm::ranges::owning_view(std::vector<int>{2, 3, 4})))) ==
44+
std::vector<int>{1, 2, 2, 3, 3, 4});
45+
46+
CHECK(vector_from_range(merge_many(ranges(
47+
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3, 4, 5}),
48+
silkworm::ranges::owning_view(std::vector<int>{3, 4, 5, 6, 7})))) ==
49+
std::vector<int>{1, 2, 3, 3, 4, 4, 5, 5, 6, 7});
50+
51+
CHECK(vector_from_range(merge_many(ranges(
52+
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3, 4, 5, 5, 5}),
53+
silkworm::ranges::owning_view(std::vector<int>{3, 4, 5, 6, 7})))) ==
54+
std::vector<int>{1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 7});
55+
56+
CHECK(vector_from_range(merge_many(ranges(
57+
silkworm::ranges::owning_view(std::vector<int>{0, 2, 2, 2, 4, 5, 6, 6, 7, 7}),
58+
silkworm::ranges::owning_view(std::vector<int>{0, 0, 1, 2, 3, 5, 5, 6, 8, 9})))) ==
59+
std::vector<int>{0, 0, 0, 1, 2, 2, 2, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8, 9});
60+
61+
using IntPredicate = std::function<bool(int)>;
62+
IntPredicate even = [](int x) { return x % 2 == 0; };
63+
IntPredicate odd = [](int x) { return x % 2 == 1; };
64+
CHECK(vector_from_range(merge_many(ranges(
65+
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}) | std::views::filter(even),
66+
silkworm::ranges::owning_view(std::vector<int>{2, 3, 4}) | std::views::filter(odd)))) ==
67+
std::vector<int>{2, 3});
68+
CHECK(vector_from_range(merge_many(ranges(
69+
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}) | std::views::filter(odd),
70+
silkworm::ranges::owning_view(std::vector<int>{2, 3, 4}) | std::views::filter(even)))) ==
71+
std::vector<int>{1, 2, 3, 4});
72+
73+
CHECK(vector_from_range(merge_many(ranges(std::vector<int>{}, std::vector<int>{}))).empty());
74+
CHECK(vector_from_range(merge_many(ranges(silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}), silkworm::ranges::owning_view(std::vector<int>{})))) == std::vector<int>{1, 2, 3});
75+
CHECK(vector_from_range(merge_many(ranges(silkworm::ranges::owning_view(std::vector<int>{}), silkworm::ranges::owning_view(std::vector<int>{2, 3, 4})))) == std::vector<int>{2, 3, 4});
76+
77+
using IntToVectorFunc = std::function<std::vector<int>(int)>;
78+
CHECK(vector_from_range(merge_many(ranges(
79+
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}) | std::views::transform(IntToVectorFunc{[](int v) { return std::vector<int>{v, v, v}; }}) | std::views::join,
80+
silkworm::ranges::owning_view(std::vector<int>{4, 4, 4}) | std::views::transform(IntToVectorFunc{[](int v) { return std::vector<int>{v}; }}) | std::views::join))) ==
81+
std::vector<int>{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4});
82+
}
83+
4084
TEST_CASE("MergeUniqueManyView") {
4185
CHECK(vector_from_range(merge_unique_many(ranges(
4286
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}),

‎silkworm/db/datastore/common/ranges/merge_unique_view.hpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626

2727
#include <silkworm/core/common/assert.hpp>
2828

29-
#include "merge_unique_compare_func.hpp"
29+
#include "merge_compare_func.hpp"
3030

3131
namespace silkworm::views {
3232

3333
template <
3434
std::ranges::input_range Range1, std::ranges::input_range Range2,
35-
class Comp = MergeUniqueCompareFunc,
35+
class Comp = MergeCompareFunc,
3636
class Proj1 = std::identity, class Proj2 = std::identity>
3737
class MergeUniqueView : public std::ranges::view_interface<MergeUniqueView<Range1, Range2, Comp, Proj1, Proj2>> {
3838
public:
@@ -176,7 +176,7 @@ class MergeUniqueView : public std::ranges::view_interface<MergeUniqueView<Range
176176

177177
template <
178178
class Range1, class Range2,
179-
class Comp = MergeUniqueCompareFunc,
179+
class Comp = MergeCompareFunc,
180180
class Proj1 = std::identity, class Proj2 = std::identity>
181181
MergeUniqueView<Range1, Range2, Comp, Proj1, Proj2> merge_unique(
182182
Range1&& v1, Range2&& v2,

‎silkworm/db/datastore/common/ranges/unique_view.hpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ namespace silkworm::views {
2222

2323
template <
2424
std::ranges::input_range TRange,
25-
class Comp = MergeUniqueCompareFunc,
25+
class Comp = MergeCompareFunc,
2626
class Proj = std::identity>
2727
using UniqueView = MergeUniqueView<TRange, std::ranges::empty_view<std::iter_value_t<std::ranges::iterator_t<TRange>>>, Comp, Proj, Proj>;
2828

29-
template <class Comp = MergeUniqueCompareFunc, class Proj = std::identity>
29+
template <class Comp = MergeCompareFunc, class Proj = std::identity>
3030
struct UniqueViewFactory {
3131
template <class TRange>
3232
constexpr UniqueView<TRange, Comp, Proj> operator()(
@@ -41,7 +41,7 @@ struct UniqueViewFactory {
4141
}
4242
};
4343

44-
template <class Comp = MergeUniqueCompareFunc, class Proj = std::identity>
44+
template <class Comp = MergeCompareFunc, class Proj = std::identity>
4545
inline constexpr UniqueViewFactory<Comp, Proj> unique;
4646

4747
} // namespace silkworm::views

0 commit comments

Comments
 (0)
Please sign in to comment.