-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8bb5a29
commit e0485c3
Showing
21 changed files
with
1,078 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.