Skip to content

Commit

Permalink
InvertedIndexFindByKeyQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
battlmonstr committed Jan 13, 2025
1 parent 8bb5a29 commit e0485c3
Show file tree
Hide file tree
Showing 21 changed files with 1,078 additions and 10 deletions.
10 changes: 7 additions & 3 deletions cmake/compiler_settings.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,15 @@ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang$")
add_link_options(-fprofile-instr-generate -fcoverage-mapping)
endif()

# coroutines support
# configure libc++
if(NOT SILKWORM_WASM_API)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-stdlib=libc++>)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16)
add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:_LIBCPP_ENABLE_EXPERIMENTAL>)
# std::views::join is experimental on clang < 18 and Apple clang < 16
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18)
add_compile_options(-fexperimental-library)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16)
add_compile_options(-fexperimental-library)
endif()
link_libraries(c++)
link_libraries(c++abi)
Expand Down
112 changes: 112 additions & 0 deletions silkworm/db/datastore/common/owning_view.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
Copyright 2024 The Silkworm Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#pragma once

#include <ranges>

// std::ranges::owning_view is not present on GCC < 12.1
// see P2415R2 at https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.2020
#if __GNUC__ < 12 && !defined(__clang__)
#else
#define SILKWORM_HAS_BUILTIN_OWNING_VIEW
#endif

#ifdef SILKWORM_HAS_BUILTIN_OWNING_VIEW
namespace silkworm::ranges::builtin {

template <std::ranges::range TRange>
using OwningView = std::ranges::owning_view<TRange>;

} // namespace silkworm::ranges::builtin
#endif

namespace silkworm::ranges::fallback {

// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2415r2.html
template <std::ranges::range TRange>
requires std::movable<TRange>
class OwningView : public std::ranges::view_interface<OwningView<TRange>> {
public:
OwningView()
requires std::default_initializable<TRange>
= default;

explicit constexpr OwningView(TRange&& range) : range_{std::move(range)} {}

OwningView(OwningView&&) = default;
OwningView& operator=(OwningView&&) = default;

constexpr TRange& base() & noexcept { return range_; }
constexpr const TRange& base() const& noexcept { return range_; }
constexpr TRange&& base() && noexcept { return std::move(range_); }
constexpr const TRange&& base() const&& noexcept { return std::move(range_); }

constexpr std::ranges::iterator_t<TRange> begin() { return std::ranges::begin(range_); }
constexpr std::ranges::sentinel_t<TRange> end() { return std::ranges::end(range_); }

constexpr auto begin() const
requires std::ranges::range<const TRange>
{ return std::ranges::begin(range_); }

constexpr auto end() const
requires std::ranges::range<const TRange>
{ return std::ranges::end(range_); }

constexpr bool empty()
requires requires { std::ranges::empty(std::declval<TRange>()); }
{ return std::ranges::empty(range_); }

constexpr bool empty() const
requires requires { std::ranges::empty(std::declval<const TRange>()); }
{ return std::ranges::empty(range_); }

constexpr auto size()
requires std::ranges::sized_range<TRange>
{ return std::ranges::size(range_); }

constexpr auto size() const
requires std::ranges::sized_range<const TRange>
{ return std::ranges::size(range_); }

constexpr auto data()
requires std::ranges::contiguous_range<TRange>
{ return std::ranges::data(range_); }

constexpr auto data() const
requires std::ranges::contiguous_range<const TRange>
{ return std::ranges::data(range_); }

private:
TRange range_;
};

} // namespace silkworm::ranges::fallback

namespace silkworm::ranges {

#ifdef SILKWORM_HAS_BUILTIN_OWNING_VIEW
using silkworm::ranges::builtin::OwningView;
#else
using silkworm::ranges::fallback::OwningView;
#endif

template <class TRange>
auto owning_view(TRange&& range) {
return OwningView<TRange>{std::forward<TRange>(range)};
}

} // namespace silkworm::ranges
3 changes: 3 additions & 0 deletions silkworm/db/datastore/common/timestamp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ struct TimestampRange {
TimestampRange(Timestamp start1, Timestamp end1) : start(start1), end(end1) {}
friend bool operator==(const TimestampRange&, const TimestampRange&) = default;
bool contains(Timestamp value) const { return (start <= value) && (value < end); }
auto contains_predicate() {
return [range = *this](Timestamp t) { return range.contains(t); };
};
bool contains_range(TimestampRange range) const { return (start <= range.start) && (range.end <= end); }
Timestamp size() const { return end - start; }
std::string to_string() const { return std::string("[") + std::to_string(start) + ", " + std::to_string(end) + ")"; }
Expand Down
165 changes: 165 additions & 0 deletions silkworm/db/datastore/concat_view.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
Copyright 2024 The Silkworm Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#pragma once

#include <optional>
#include <ranges>
#include <utility>

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

// std::views::concat is present on C++26
#if __cplusplus >= 202601L
#define SILKWORM_HAS_BUILTIN_CONCAT_VIEW
#endif

#ifdef SILKWORM_HAS_BUILTIN_CONCAT_VIEW
namespace silkworm::views::concat_view::builtin {

template <std::ranges::input_range... Views>
using ConcatView = std::ranges::concat_view<Views...>;

} // namespace silkworm::views::concat_view::builtin
#endif

namespace silkworm::views::concat_view::fallback {

template <std::ranges::input_range Range1, std::ranges::input_range Range2>
class ConcatView : public std::ranges::view_interface<ConcatView<Range1, Range2>> {
public:
class Iterator {
public:
using Range1Iterator = std::ranges::iterator_t<Range1>;
using Range1Sentinel = std::ranges::sentinel_t<Range1>;
using Range1ReferenceType = std::iter_reference_t<Range1Iterator>;
using Range2Iterator = std::ranges::iterator_t<Range2>;
using Range2Sentinel = std::ranges::sentinel_t<Range2>;
using Range2ReferenceType = std::iter_reference_t<Range2Iterator>;
using DereferenceType = std::conditional_t<!std::is_reference_v<Range1ReferenceType>, Range1ReferenceType, Range2ReferenceType>;

using value_type = std::iter_value_t<Range1Iterator>;
using iterator_category [[maybe_unused]] = std::input_iterator_tag;
using difference_type = std::iter_difference_t<Range1Iterator>;
using reference = DereferenceType;
using pointer = std::remove_reference_t<reference>*;

Iterator() = default;
Iterator(Range1* range1, Range2* range2)
: range1_{range1},
range2_{range2} {
it1_ = std::ranges::begin(*range1_);
sentinel1_ = std::ranges::end(*range1_);
if (*it1_ == *sentinel1_) {
it1_ = std::nullopt;
sentinel1_ = std::nullopt;
it2_ = std::ranges::begin(*range2_);
sentinel2_ = std::ranges::end(*range2_);
if (*it2_ == *sentinel2_) {
it2_ = std::nullopt;
sentinel2_ = std::nullopt;
}
}
}

reference operator*() const {
if (it1_) return **it1_;
if (it2_) return **it2_;
SILKWORM_ASSERT(false);
std::abort();
}

Iterator operator++(int) { return std::exchange(*this, ++Iterator{*this}); }
Iterator& operator++() {
if (it1_) {
++(*it1_);
if (*it1_ == *sentinel1_) {
it1_ = std::nullopt;
sentinel1_ = std::nullopt;
it2_ = std::ranges::begin(*range2_);
sentinel2_ = std::ranges::end(*range2_);
if (*it2_ == *sentinel2_) {
it2_ = std::nullopt;
sentinel2_ = std::nullopt;
}
}
} else if (it2_) {
++(*it2_);
#if __GNUC__ < 12 && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
if (*it2_ == *sentinel2_) {
#if __GNUC__ < 12 && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
it2_ = std::nullopt;
sentinel2_ = std::nullopt;
}
}
return *this;
}

friend bool operator==(const Iterator& it, const std::default_sentinel_t&) {
return !it.it1_ && !it.it2_;
}
friend bool operator!=(const Iterator& it, const std::default_sentinel_t&) {
return it.it1_ || it.it2_;
}

private:
Range1* range1_{nullptr};
Range2* range2_{nullptr};
std::optional<Range1Iterator> it1_;
std::optional<Range2Iterator> it2_;
std::optional<Range1Sentinel> sentinel1_;
std::optional<Range2Sentinel> sentinel2_;
};

static_assert(std::input_iterator<Iterator>);

ConcatView(Range1 range1, Range2 range2)
: range1_{std::move(range1)},
range2_{std::move(range2)} {}
ConcatView() = default;

ConcatView(ConcatView&&) = default;
ConcatView& operator=(ConcatView&&) noexcept = default;

Iterator begin() { return Iterator{&range1_, &range2_}; }
std::default_sentinel_t end() const { return std::default_sentinel; }

private:
Range1 range1_;
Range2 range2_;
};

} // namespace silkworm::views::concat_view::fallback

namespace silkworm::views {

#ifdef SILKWORM_HAS_BUILTIN_CONCAT_VIEW
using silkworm::views::concat_view::builtin::ConcatView;
#else
using silkworm::views::concat_view::fallback::ConcatView;
#endif

template <class Range1, class Range2>
auto concat(Range1&& v1, Range2&& v2) {
return ConcatView<Range1, Range2>{std::forward<Range1>(v1), std::forward<Range2>(v2)};
}

} // namespace silkworm::views
61 changes: 61 additions & 0 deletions silkworm/db/datastore/concat_view_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright 2024 The Silkworm Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "concat_view.hpp"

#include <algorithm>
#include <iterator>
#include <vector>

#include <catch2/catch_test_macros.hpp>

#include "common/owning_view.hpp"

namespace silkworm::views::concat_view {

static_assert(std::ranges::input_range<ConcatView<std::vector<int>, std::vector<int>>>);

template <std::ranges::input_range Range, typename Value = std::iter_value_t<std::ranges::iterator_t<Range>>>
std::vector<Value> vector_from_range(Range range) {
std::vector<Value> results;
std::ranges::copy(range, std::back_inserter(results));
return results;
}

TEST_CASE("ConcatView") {
CHECK(vector_from_range(concat(
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}),
silkworm::ranges::owning_view(std::vector<int>{4, 5, 6}))) == std::vector<int>{1, 2, 3, 4, 5, 6});

auto even = [](int x) { return x % 2 == 0; };
auto odd = [](int x) { return x % 2 == 1; };
CHECK(vector_from_range(concat(
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}) | std::views::filter(even),
silkworm::ranges::owning_view(std::vector<int>{4, 5, 6}) | std::views::filter(odd))) == std::vector<int>{2, 5});
CHECK(vector_from_range(concat(
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}) | std::views::filter(odd),
silkworm::ranges::owning_view(std::vector<int>{4, 5, 6}) | std::views::filter(even))) == std::vector<int>{1, 3, 4, 6});

CHECK(vector_from_range(concat(std::ranges::empty_view<int>{}, std::ranges::empty_view<int>{})).empty());
CHECK(vector_from_range(concat(silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}), std::ranges::empty_view<int>{})) == std::vector<int>{1, 2, 3});
CHECK(vector_from_range(concat(std::ranges::empty_view<int>{}, silkworm::ranges::owning_view(std::vector<int>{4, 5, 6}))) == std::vector<int>{4, 5, 6});

CHECK(vector_from_range(concat(
silkworm::ranges::owning_view(std::vector<int>{1, 2, 3}) | std::views::transform([](int v) { return std::vector<int>{v, v, v}; }) | std::views::join,
silkworm::ranges::owning_view(std::vector<int>{4, 4, 4}))) == std::vector<int>{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4});
}

} // namespace silkworm::views::concat_view
Loading

0 comments on commit e0485c3

Please sign in to comment.