| 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 |