tess 1.0.0
Performance-first tile and path simulation substrate
Loading...
Searching...
No Matches
pipeline.h
1#pragma once
2
3#include <tess/block/block.h>
4
5#include <array>
6#include <cstddef>
7#include <cstdint>
8#include <functional>
9#include <ranges>
10#include <span>
11#include <type_traits>
12#include <utility>
13#include <vector>
14
15namespace tess {
16
19 public:
20 constexpr void reset() noexcept { *this = PipelineDiagnostics{}; }
21
22 constexpr void record_block() noexcept { ++blocks_read_; }
23 constexpr void record_item() noexcept { ++items_read_; }
24 constexpr void record_filtered() noexcept { ++items_filtered_; }
25 constexpr void record_emitted() noexcept { ++items_emitted_; }
26 constexpr void record_materialization() noexcept { ++materializations_; }
27 constexpr void record_capacity_failure() noexcept { ++capacity_failures_; }
28
29 [[nodiscard]] constexpr auto blocks_read() const noexcept -> std::uint64_t {
30 return blocks_read_;
31 }
32 [[nodiscard]] constexpr auto items_read() const noexcept -> std::uint64_t {
33 return items_read_;
34 }
35 [[nodiscard]] constexpr auto items_filtered() const noexcept
36 -> std::uint64_t {
37 return items_filtered_;
38 }
39 [[nodiscard]] constexpr auto items_emitted() const noexcept -> std::uint64_t {
40 return items_emitted_;
41 }
42 [[nodiscard]] constexpr auto materializations() const noexcept
43 -> std::uint64_t {
44 return materializations_;
45 }
46 [[nodiscard]] constexpr auto capacity_failures() const noexcept
47 -> std::uint64_t {
48 return capacity_failures_;
49 }
50
51 private:
52 std::uint64_t blocks_read_ = 0;
53 std::uint64_t items_read_ = 0;
54 std::uint64_t items_filtered_ = 0;
55 std::uint64_t items_emitted_ = 0;
56 std::uint64_t materializations_ = 0;
57 std::uint64_t capacity_failures_ = 0;
58};
59
62 std::size_t written = 0;
63 std::uint64_t required = 0;
64 bool capacity_exhausted = false;
65};
66
67template <typename ChunkView>
69struct BlockTile {
70 ChunkView chunk;
71 LocalTileId id{};
72 LocalCoord3 local{};
73 Coord3 world{};
74};
75
76namespace detail {
77
78template <typename Context>
79struct BlockTileSource {
80 using view_type = decltype(std::declval<const Context&>().chunk_view(
81 std::declval<ChunkKey>()));
82 using value_type = BlockTile<view_type>;
83
84 // BlockCtx is a cheap pointer/span value. Store that value, not its address:
85 // block_tiles(block_ctx(...)) must remain valid after the BlockCtx temporary
86 // dies. The world, domain keys, scratch, and diagnostics remain borrowed.
87 Context context;
88 PipelineDiagnostics* diagnostics = nullptr;
89
90 template <typename Sink>
91 void for_each(Sink&& sink) {
92 auto&& output = sink;
93 context.for_each_chunk([&](auto view) {
94 if (diagnostics != nullptr) {
95 diagnostics->record_block();
96 }
97 view.for_each_tile([&](LocalTileId id, LocalCoord3 local) {
98 if (diagnostics != nullptr) {
99 diagnostics->record_item();
100 }
101 std::invoke(output,
102 value_type{view, id, local, view.world_coord(local)});
103 });
104 });
105 }
106};
107
108template <typename Context>
109struct BlockChunkSource {
110 using value_type = decltype(std::declval<const Context&>().chunk_view(
111 std::declval<ChunkKey>()));
112
113 // See BlockTileSource: the source owns the context value while preserving
114 // the context's explicit borrows of its underlying resources.
115 Context context;
116 PipelineDiagnostics* diagnostics = nullptr;
117
118 template <typename Sink>
119 void for_each(Sink&& sink) {
120 auto&& output = sink;
121 context.for_each_chunk([&](auto view) {
122 if (diagnostics != nullptr) {
123 diagnostics->record_block();
124 diagnostics->record_item();
125 }
126 std::invoke(output, view);
127 });
128 }
129};
130
131template <typename T>
132struct SpanSource {
133 using value_type = T;
134
135 std::span<const T> values;
136 PipelineDiagnostics* diagnostics = nullptr;
137
138 template <typename Sink>
139 void for_each(Sink&& sink) {
140 auto&& output = sink;
141 for (const auto& value : values) {
142 if (diagnostics != nullptr) {
143 diagnostics->record_item();
144 }
145 std::invoke(output, value);
146 }
147 }
148};
149
150template <typename Source, typename Predicate>
151struct FilterSource {
152 using value_type = typename Source::value_type;
153
154 Source source;
155 Predicate predicate;
156 PipelineDiagnostics* diagnostics = nullptr;
157
158 template <typename Sink>
159 void for_each(Sink&& sink) {
160 auto&& output = sink;
161 source.for_each([&](auto&& value) {
162 if (std::invoke(predicate, value)) {
163 std::invoke(output, std::forward<decltype(value)>(value));
164 } else if (diagnostics != nullptr) {
165 diagnostics->record_filtered();
166 }
167 });
168 }
169};
170
171template <typename Source, typename Mapper>
172struct MapSource {
173 using value_type = std::remove_cvref_t<
174 std::invoke_result_t<Mapper&, typename Source::value_type>>;
175
176 Source source;
177 Mapper mapper;
178
179 template <typename Sink>
180 void for_each(Sink&& sink) {
181 auto&& output = sink;
182 source.for_each([&](auto&& value) {
183 std::invoke(output,
184 std::invoke(mapper, std::forward<decltype(value)>(value)));
185 });
186 }
187};
188
189template <typename Source, typename Mapper>
190struct FlatMapSource {
191 using range_type = std::remove_cvref_t<
192 std::invoke_result_t<Mapper&, typename Source::value_type>>;
193 using value_type =
194 std::remove_cvref_t<std::ranges::range_reference_t<range_type>>;
195
196 Source source;
197 Mapper mapper;
198
199 template <typename Sink>
200 void for_each(Sink&& sink) {
201 auto&& output = sink;
202 source.for_each([&](auto&& value) {
203 // Preserve lvalue range identity (including mutable references) while
204 // still extending a mapper-returned temporary through this iteration.
205 auto&& range = std::invoke(mapper, std::forward<decltype(value)>(value));
206 for (auto&& item : range) {
207 std::invoke(output, std::forward<decltype(item)>(item));
208 }
209 });
210 }
211};
212
213} // namespace detail
214
215template <typename Source>
217class Pipeline {
218 public:
219 using value_type = typename Source::value_type;
220
221 explicit Pipeline(Source source, PipelineDiagnostics* diagnostics = nullptr)
222 : source_(std::move(source)), diagnostics_(diagnostics) {}
223
224 template <typename Predicate>
225 [[nodiscard]] auto filter(Predicate predicate) && {
226 using Filter = detail::FilterSource<Source, Predicate>;
227 return Pipeline<Filter>{
228 Filter{std::move(source_), std::move(predicate), diagnostics_},
229 diagnostics_};
230 }
231
232 template <typename Mapper>
233 [[nodiscard]] auto map(Mapper mapper) && {
234 using Map = detail::MapSource<Source, Mapper>;
235 return Pipeline<Map>{Map{std::move(source_), std::move(mapper)},
236 diagnostics_};
237 }
238
239 template <typename Mapper>
240 [[nodiscard]] auto flat_map(Mapper mapper) && {
241 using FlatMap = detail::FlatMapSource<Source, Mapper>;
242 return Pipeline<FlatMap>{FlatMap{std::move(source_), std::move(mapper)},
243 diagnostics_};
244 }
245
246 template <typename Fn>
247 void for_each(Fn&& fn) {
248 auto&& output = fn;
249 source_.for_each([&](auto&& value) {
250 if (diagnostics_ != nullptr) {
251 diagnostics_->record_emitted();
252 }
253 std::invoke(output, std::forward<decltype(value)>(value));
254 });
255 }
256
257 template <typename Result, typename Reducer>
258 [[nodiscard]] auto reduce(Result initial, Reducer reducer) -> Result {
259 auto result = std::move(initial);
260 source_.for_each([&](auto&& value) {
261 if (diagnostics_ != nullptr) {
262 diagnostics_->record_emitted();
263 }
264 result = std::invoke(reducer, std::move(result),
265 std::forward<decltype(value)>(value));
266 });
267 return result;
268 }
269
270 template <typename Output, std::size_t Extent>
271 [[nodiscard]] auto collect_into(std::span<Output, Extent> output)
273 auto result = PipelineCollectResult{};
274 source_.for_each([&](auto&& value) {
275 if (result.written < output.size()) {
276 output[result.written++] = std::forward<decltype(value)>(value);
277 }
278 ++result.required;
279 if (diagnostics_ != nullptr) {
280 diagnostics_->record_emitted();
281 }
282 });
283 result.capacity_exhausted = result.required > output.size();
284 if (result.capacity_exhausted && diagnostics_ != nullptr) {
285 diagnostics_->record_capacity_failure();
286 }
287 return result;
288 }
289
290 template <typename Output, std::size_t Size>
291 [[nodiscard]] auto collect_into(std::array<Output, Size>& output)
293 return collect_into(std::span<Output, Size>{output});
294 }
295
296 [[nodiscard]] auto to_sequence_allocating() -> std::vector<value_type> {
297 if (diagnostics_ != nullptr) {
298 diagnostics_->record_materialization();
299 }
300 auto output = std::vector<value_type>{};
301 source_.for_each([&](auto&& value) {
302 output.push_back(std::forward<decltype(value)>(value));
303 if (diagnostics_ != nullptr) {
304 diagnostics_->record_emitted();
305 }
306 });
307 return output;
308 }
309
310 private:
311 Source source_;
312 PipelineDiagnostics* diagnostics_ = nullptr;
313};
314
315template <typename Context>
317[[nodiscard]] auto block_tiles(Context context,
318 PipelineDiagnostics* diagnostics = nullptr) {
319 using Source = detail::BlockTileSource<Context>;
320 return Pipeline<Source>{Source{std::move(context), diagnostics}, diagnostics};
321}
322
323template <typename Context>
325[[nodiscard]] auto block_tiles(const Context& context,
326 PipelineDiagnostics& diagnostics) {
327 return block_tiles(context, &diagnostics);
328}
329
330template <typename Context>
332[[nodiscard]] auto block_chunks(Context context,
333 PipelineDiagnostics* diagnostics = nullptr) {
334 using Source = detail::BlockChunkSource<Context>;
335 return Pipeline<Source>{Source{std::move(context), diagnostics}, diagnostics};
336}
337
338template <typename Context>
340[[nodiscard]] auto block_chunks(const Context& context,
341 PipelineDiagnostics& diagnostics) {
342 return block_chunks(context, &diagnostics);
343}
344
345template <typename T, std::size_t Extent>
347[[nodiscard]] auto pipeline_from(std::span<T, Extent> values,
348 PipelineDiagnostics* diagnostics = nullptr) {
349 using Item = std::remove_cv_t<T>;
350 using Source = detail::SpanSource<Item>;
351 return Pipeline<Source>{
352 Source{std::span<const Item>{values.data(), values.size()}, diagnostics},
353 diagnostics};
354}
355
356} // namespace tess
Definition block.h:330
Caller-owned counters for one or more lazy pipeline terminal runs.
Definition pipeline.h:18
Definition pipeline.h:217
Definition pipeline.h:69
Definition shape.h:46
Definition shape.h:68
Definition shape.h:78
Reports bounded materialization occupancy and required capacity.
Definition pipeline.h:61