Line | Branch | Exec | Source |
---|---|---|---|
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 | |||
11 | namespace 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 | // ------------------------------------------------ | ||
25 | /** @private */ | ||
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 | // ------------------------------------------------ | ||
32 | /** @private */ | ||
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 | 60 | constexpr ChunkedExact(TChainInput&& input) : input(std::move(input)) {} | |
44 | }; | ||
45 | |||
46 | // Implementation for contiguous memory sources | ||
47 | // ------------------------------------------------ | ||
48 | /** @private */ | ||
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 | 50 | constexpr ChunkedExact(TChainInput&& input) : input(std::move(input)) { | |
59 | 50 | remaining = (this->input.size() - CHUNK_SIZE) / STEP_SIZE + 1; | |
60 | 50 | } | |
61 | }; | ||
62 | } | ||
63 | |||
64 | |||
65 | // ------------------------------------------------------------------------------------------------ | ||
66 | /** @private */ | ||
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 | 86 | static constexpr inline IterValue<Item> next(Self& self) requires (!IS_CONTIGUOUS) { | |
97 | 137 | auto getElementFromChainInput = [&]<size_t IDX>(std::integral_constant<size_t, IDX>) -> typename ItemOwned::value_type { | |
98 |
39/78✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 1 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 1 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 2 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 2 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 2 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 1 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 1 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 1 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 1 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 1 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 1 times.
✗ Branch 59 not taken.
✓ Branch 61 taken 1 times.
✗ Branch 62 not taken.
✓ Branch 64 taken 1 times.
✗ Branch 65 not taken.
✓ Branch 67 taken 1 times.
✗ Branch 68 not taken.
✓ Branch 70 taken 1 times.
✗ Branch 71 not taken.
✓ Branch 73 taken 2 times.
✗ Branch 74 not taken.
✓ Branch 76 taken 2 times.
✗ Branch 77 not taken.
✓ Branch 79 taken 2 times.
✗ Branch 80 not taken.
✓ Branch 82 taken 1 times.
✗ Branch 83 not taken.
✓ Branch 85 taken 1 times.
✗ Branch 86 not taken.
✓ Branch 88 taken 1 times.
✗ Branch 89 not taken.
✓ Branch 91 taken 1 times.
✗ Branch 92 not taken.
✓ Branch 94 taken 1 times.
✗ Branch 95 not taken.
✓ Branch 97 taken 1 times.
✗ Branch 98 not taken.
✓ Branch 100 taken 1 times.
✗ Branch 101 not taken.
✓ Branch 103 taken 1 times.
✗ Branch 104 not taken.
✓ Branch 106 taken 1 times.
✗ Branch 107 not taken.
✓ Branch 109 taken 3 times.
✗ Branch 110 not taken.
✓ Branch 112 taken 3 times.
✗ Branch 113 not taken.
✓ Branch 115 taken 3 times.
✗ Branch 116 not taken.
|
51 | auto input = ChainInputIterator::next(self.input); |
99 |
39/78✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 1 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 1 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 1 times.
✗ Branch 31 not taken.
✓ Branch 32 taken 1 times.
✗ Branch 34 not taken.
✓ Branch 35 taken 2 times.
✗ Branch 37 not taken.
✓ Branch 38 taken 2 times.
✗ Branch 40 not taken.
✓ Branch 41 taken 2 times.
✗ Branch 43 not taken.
✓ Branch 44 taken 1 times.
✗ Branch 46 not taken.
✓ Branch 47 taken 1 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 1 times.
✗ Branch 52 not taken.
✓ Branch 53 taken 1 times.
✗ Branch 55 not taken.
✓ Branch 56 taken 1 times.
✗ Branch 58 not taken.
✓ Branch 59 taken 1 times.
✗ Branch 61 not taken.
✓ Branch 62 taken 1 times.
✗ Branch 64 not taken.
✓ Branch 65 taken 1 times.
✗ Branch 67 not taken.
✓ Branch 68 taken 1 times.
✗ Branch 70 not taken.
✓ Branch 71 taken 1 times.
✗ Branch 73 not taken.
✓ Branch 74 taken 2 times.
✗ Branch 76 not taken.
✓ Branch 77 taken 2 times.
✗ Branch 79 not taken.
✓ Branch 80 taken 2 times.
✗ Branch 82 not taken.
✓ Branch 83 taken 1 times.
✗ Branch 85 not taken.
✓ Branch 86 taken 1 times.
✗ Branch 88 not taken.
✓ Branch 89 taken 1 times.
✗ Branch 91 not taken.
✓ Branch 92 taken 1 times.
✗ Branch 94 not taken.
✓ Branch 95 taken 1 times.
✗ Branch 97 not taken.
✓ Branch 98 taken 1 times.
✗ Branch 100 not taken.
✓ Branch 101 taken 1 times.
✗ Branch 103 not taken.
✓ Branch 104 taken 1 times.
✗ Branch 106 not taken.
✓ Branch 107 taken 1 times.
✗ Branch 109 not taken.
✓ Branch 110 taken 3 times.
✗ Branch 112 not taken.
✓ Branch 113 taken 3 times.
✗ Branch 115 not taken.
✓ Branch 116 taken 3 times.
|
51 | if(!input.has_value()) [[unlikely]] { |
100 | ✗ | throw typename Self::source_ended_exception{}; | |
101 | } | ||
102 |
39/78✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 1 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 1 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 2 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 2 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 2 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 1 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 1 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 1 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 1 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 1 times.
✗ Branch 59 not taken.
✓ Branch 61 taken 1 times.
✗ Branch 62 not taken.
✓ Branch 64 taken 1 times.
✗ Branch 65 not taken.
✓ Branch 67 taken 1 times.
✗ Branch 68 not taken.
✓ Branch 70 taken 1 times.
✗ Branch 71 not taken.
✓ Branch 73 taken 1 times.
✗ Branch 74 not taken.
✓ Branch 76 taken 2 times.
✗ Branch 77 not taken.
✓ Branch 79 taken 2 times.
✗ Branch 80 not taken.
✓ Branch 82 taken 2 times.
✗ Branch 83 not taken.
✓ Branch 85 taken 1 times.
✗ Branch 86 not taken.
✓ Branch 88 taken 1 times.
✗ Branch 89 not taken.
✓ Branch 91 taken 1 times.
✗ Branch 92 not taken.
✓ Branch 94 taken 1 times.
✗ Branch 95 not taken.
✓ Branch 98 taken 1 times.
✗ Branch 99 not taken.
✓ Branch 102 taken 1 times.
✗ Branch 103 not taken.
✓ Branch 106 taken 1 times.
✗ Branch 107 not taken.
✓ Branch 110 taken 1 times.
✗ Branch 111 not taken.
✓ Branch 114 taken 1 times.
✗ Branch 115 not taken.
✓ Branch 118 taken 3 times.
✗ Branch 119 not taken.
✓ Branch 121 taken 3 times.
✗ Branch 122 not taken.
✓ Branch 124 taken 3 times.
✗ Branch 125 not taken.
|
51 | return input.value(); |
103 | }; | ||
104 | 152 | auto initializeChunk = [&]<size_t... IDX>(auto& chunkOptional, std::integer_sequence<size_t, IDX...>) { | |
105 | 15 | chunkOptional = { getElementFromChainInput(std::integral_constant<size_t, IDX>())... }; | |
106 | }; | ||
107 | |||
108 |
2/2✓ Branch 1 taken 15 times.
✓ Branch 2 taken 28 times.
|
86 | if(!self.chunk.has_value()) [[unlikely]] { |
109 | // initial loading | ||
110 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
30 | initializeChunk(self.chunk, std::make_index_sequence<CHUNK_SIZE>{}); |
111 | 30 | return *self.chunk; | |
112 | } | ||
113 | |||
114 | 56 | auto& chunk = *self.chunk; | |
115 | |||
116 | // if step-size is greater than chunk-size, we need to skip some values | ||
117 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
56 | 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 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 13 times.
|
86 | for(size_t i = 0; i < SHIFT_SIZE; ++i) { |
122 | 30 | chunk[i] = std::move(chunk[STEP_SIZE + i]); | |
123 | } | ||
124 | |||
125 | // load new items | ||
126 |
2/2✓ Branch 0 taken 63 times.
✓ Branch 1 taken 17 times.
|
160 | for(size_t i = 0; i < LOAD_SIZE; ++i) { |
127 |
1/2✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
|
126 | auto item = ChainInputIterator::next(self.input); |
128 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 2 taken 52 times.
|
126 | if(!item.has_value()) [[unlikely]] { return {}; } // reached end. Chunk needs to be full to commit! |
129 |
1/2✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
|
104 | chunk[LOAD_START + i] = item.value(); |
130 | } | ||
131 | 34 | return chunk; | |
132 | } | ||
133 | |||
134 | // contiguous | ||
135 | 102 | static constexpr inline IterValue<Item> next(Self& self) requires IS_CONTIGUOUS { | |
136 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 37 times.
|
102 | if(self.remaining == 0) [[unlikely]] { |
137 | 28 | return {}; | |
138 | } | ||
139 | |||
140 | // next ptr | ||
141 | 74 | auto itemPtr = ContiguousMemoryIterator<TChainInput>::currentPtr(self.input); | |
142 | 74 | ChainInputIterator::advanceBy(self.input, STEP_SIZE); | |
143 | 74 | self.remaining -= 1; | |
144 | 74 | return (Item)(*itemPtr); | |
145 | } | ||
146 | |||
147 | |||
148 | 48 | static constexpr inline SizeHint sizeHint(const Self& self) { | |
149 | 48 | SizeHint result = ChainInputIterator::sizeHint(self.input); | |
150 | 48 | result.lowerBound = (result.lowerBound >= CHUNK_SIZE) ? ((result.lowerBound - CHUNK_SIZE) / STEP_SIZE + 1) : 0; | |
151 | 48 | if(result.upperBound.has_value()) { | |
152 | 48 | result.upperBound.value() = (result.upperBound.value() >= CHUNK_SIZE) ? ((result.upperBound.value() - CHUNK_SIZE) / STEP_SIZE + 1) : 0; | |
153 | } | ||
154 | 48 | 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 | |||
165 | /** @private */ | ||
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 | } | ||
174 |