CXXIter 0.2
Loading...
Searching...
No Matches
ChunkedExact.h
1#pragma once
2
3#include <array>
4#include <cstdlib>
5#include <utility>
6#include <type_traits>
7
8#include "../Common.h"
9#include "../util/TraitImpl.h"
10
11namespace CXXIter {
12
13 template<typename TItem, const size_t CHUNK_SIZE>
14 using ExactChunk = std::conditional_t<
15 std::is_reference_v<TItem>,
16 std::array<std::reference_wrapper<std::remove_reference_t<TItem>>, CHUNK_SIZE>,
17 std::array<TItem, CHUNK_SIZE>>;
18
19 // ################################################################################################
20 // CHUNKED EXACT
21 // ################################################################################################
22 namespace op {
23 // Empty definition
24 // ------------------------------------------------
26 template<typename TChainInput, const size_t CHUNK_SIZE, const size_t STEP_SIZE>
27 class ChunkedExact {};
28
29
30 // Implementation for non-contiguous memory sources
31 // ------------------------------------------------
33 template<typename TChainInput, const size_t CHUNK_SIZE, const size_t STEP_SIZE>
34 requires (!CXXIterContiguousMemoryIterator<TChainInput>)
35 class [[nodiscard(CXXITER_CHAINER_NODISCARD_WARNING)]] ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE> : public IterApi<ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>> {
36 friend struct trait::Iterator<ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>>;
37 friend struct trait::ExactSizeIterator<ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>>;
38 private:
39 struct source_ended_exception {};
40 TChainInput input;
41 std::optional<ExactChunk<typename TChainInput::Item, CHUNK_SIZE>> chunk;
42 public:
43 constexpr ChunkedExact(TChainInput&& input) : input(std::move(input)) {}
44 };
45
46 // Implementation for contiguous memory sources
47 // ------------------------------------------------
49 template<typename TChainInput, const size_t CHUNK_SIZE, const size_t STEP_SIZE>
50 requires CXXIterContiguousMemoryIterator<TChainInput>
51 class [[nodiscard(CXXITER_CHAINER_NODISCARD_WARNING)]] ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE> : public IterApi<ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>> {
52 friend struct trait::Iterator<ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>>;
53 friend struct trait::ExactSizeIterator<ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>>;
54 private:
55 TChainInput input;
56 size_t remaining = 0;
57 public:
58 constexpr ChunkedExact(TChainInput&& input) : input(std::move(input)) {
59 remaining = (this->input.size() - CHUNK_SIZE) / STEP_SIZE + 1;
60 }
61 };
62 }
63
64
65 // ------------------------------------------------------------------------------------------------
67 template<typename TChainInput, const size_t CHUNK_SIZE, const size_t STEP_SIZE>
68 struct trait::Iterator<op::ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>> {
69
70 static_assert(STEP_SIZE > 0, "STEP_SIZE has to be at least 1");
71
72 private:
73 static constexpr size_t LOAD_SIZE = std::min(CHUNK_SIZE, STEP_SIZE);
74 static constexpr size_t SHIFT_SIZE = (STEP_SIZE < CHUNK_SIZE) ? (CHUNK_SIZE - STEP_SIZE) : 0;
75 static constexpr size_t LOAD_START = SHIFT_SIZE;
76 static constexpr size_t SKIP_SIZE = (STEP_SIZE > CHUNK_SIZE) ? (STEP_SIZE - CHUNK_SIZE) : 0;
77
78 using ChainInputIterator = trait::Iterator<TChainInput>;
79 using InputItem = typename TChainInput::Item;
80 using InputItemOwned = typename TChainInput::ItemOwned;
81
82 static constexpr bool IS_CONTIGUOUS = CXXIterContiguousMemoryIterator<TChainInput>;
83
84 public:
85 // CXXIter Interface
86 using Self = op::ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>;
87 // If the source is contiguous, copy the const specifier from the InputItem
88 // If the source is non-contiguous, always add a const specifier, since we pass a reference
89 // to our internal chunk buffer, and changing that doesn't make much sense.
90 using ItemOwned = std::conditional_t<IS_CONTIGUOUS,
91 copy_const_from<InputItem, ExactChunk<InputItemOwned, CHUNK_SIZE>>,
92 const ExactChunk<InputItem, CHUNK_SIZE>>;
93 using Item = ItemOwned&;
94
95 // non-contiguous
96 static constexpr inline IterValue<Item> next(Self& self) requires (!IS_CONTIGUOUS) {
97 auto getElementFromChainInput = [&]<size_t IDX>(std::integral_constant<size_t, IDX>) -> typename ItemOwned::value_type {
98 auto input = ChainInputIterator::next(self.input);
99 if(!input.has_value()) [[unlikely]] {
100 throw typename Self::source_ended_exception{};
101 }
102 return input.value();
103 };
104 auto initializeChunk = [&]<size_t... IDX>(auto& chunkOptional, std::integer_sequence<size_t, IDX...>) {
105 chunkOptional = { getElementFromChainInput(std::integral_constant<size_t, IDX>())... };
106 };
107
108 if(!self.chunk.has_value()) [[unlikely]] {
109 // initial loading
110 initializeChunk(self.chunk, std::make_index_sequence<CHUNK_SIZE>{});
111 return *self.chunk;
112 }
113
114 auto& chunk = *self.chunk;
115
116 // if step-size is greater than chunk-size, we need to skip some values
117 ChainInputIterator::advanceBy(self.input, SKIP_SIZE);
118
119 // if step-size is smaller than chunk-size, we have to shift out the first couple of items
120 // so we can push new ones in the back
121 for(size_t i = 0; i < SHIFT_SIZE; ++i) {
122 chunk[i] = std::move(chunk[STEP_SIZE + i]);
123 }
124
125 // load new items
126 for(size_t i = 0; i < LOAD_SIZE; ++i) {
127 auto item = ChainInputIterator::next(self.input);
128 if(!item.has_value()) [[unlikely]] { return {}; } // reached end. Chunk needs to be full to commit!
129 chunk[LOAD_START + i] = item.value();
130 }
131 return chunk;
132 }
133
134 // contiguous
135 static constexpr inline IterValue<Item> next(Self& self) requires IS_CONTIGUOUS {
136 if(self.remaining == 0) [[unlikely]] {
137 return {};
138 }
139
140 // next ptr
141 auto itemPtr = ContiguousMemoryIterator<TChainInput>::currentPtr(self.input);
142 ChainInputIterator::advanceBy(self.input, STEP_SIZE);
143 self.remaining -= 1;
144 return (Item)(*itemPtr);
145 }
146
147
148 static constexpr inline SizeHint sizeHint(const Self& self) {
149 SizeHint result = ChainInputIterator::sizeHint(self.input);
150 result.lowerBound = (result.lowerBound >= CHUNK_SIZE) ? ((result.lowerBound - CHUNK_SIZE) / STEP_SIZE + 1) : 0;
151 if(result.upperBound.has_value()) {
152 result.upperBound.value() = (result.upperBound.value() >= CHUNK_SIZE) ? ((result.upperBound.value() - CHUNK_SIZE) / STEP_SIZE + 1) : 0;
153 }
154 return result;
155 }
156 static constexpr inline size_t advanceBy(Self& self, size_t n) {
157 if constexpr(IS_CONTIGUOUS) {
158 ChainInputIterator::advanceBy(self.input, n * STEP_SIZE);
159 } else {
160 return util::advanceByPull(self, n);
161 }
162 }
163 };
164
166 template<CXXIterExactSizeIterator TChainInput, const size_t CHUNK_SIZE, const size_t STEP_SIZE>
167 struct trait::ExactSizeIterator<op::ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>> {
168 static constexpr inline size_t size(const op::ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>& self) {
169 return trait::Iterator<op::ChunkedExact<TChainInput, CHUNK_SIZE, STEP_SIZE>>::sizeHint(self).lowerBound;
170 }
171 };
172
173}
CXXIter.
Definition: CXXIter.h:48