libtcspc C++ API
Streaming TCSPC and time tag data processing
Loading...
Searching...
No Matches
copy_to_buckets.hpp
1/*
2 * This file is part of libtcspc
3 * Copyright 2019-2026 Board of Regents of the University of Wisconsin System
4 * SPDX-License-Identifier: MIT
5 */
6
7#pragma once
8
9#include "arg_wrappers.hpp"
10#include "bucket.hpp"
11#include "common.hpp"
12#include "core.hpp"
13#include "errors.hpp"
14#include "introspect.hpp"
15#include "processor.hpp"
16
17#include <algorithm>
18#include <concepts>
19#include <cstddef>
20#include <exception>
21#include <memory>
22#include <span>
23#include <stdexcept>
24#include <type_traits>
25#include <utility>
26
27namespace tcspc {
28
29namespace internal {
30
31template <typename DataEvent, typename T, typename Downstream>
32 requires std::is_constructible_v<std::span<T const>, DataEvent const &> &&
33 processor<Downstream, bucket<T>>
34class copy_to_buckets {
35 std::shared_ptr<bucket_source<T>> bsource;
36
37 Downstream downstream;
38
39 public:
40 explicit copy_to_buckets(std::shared_ptr<bucket_source<T>> buffer_provider,
41 Downstream downstream)
42 : bsource(std::move(buffer_provider)),
43 downstream(std::move(downstream)) {
44 if (not bsource)
45 throw std::invalid_argument(
46 "copy_to_buckets buffer_provider must not be null");
47 }
48
49 [[nodiscard]] auto introspect_node() const -> processor_info {
50 return processor_info(this, "copy_to_buckets");
51 }
52
53 [[nodiscard]] auto introspect_graph() const -> processor_graph {
54 return downstream.introspect_graph().push_entry_point(this);
55 }
56
57 template <typename Event>
58 requires std::same_as<std::remove_cvref_t<Event>, DataEvent>
59 void handle(Event &&event) {
60 auto const event_span = std::span<T const>(event);
61 auto b = bsource->bucket_of_size(event_span.size());
62 std::copy(event_span.begin(), event_span.end(), b.begin());
63 downstream.handle(std::move(b));
64 }
65
66 template <typename Event>
67 requires(not std::same_as<std::remove_cvref_t<Event>, DataEvent> and
68 handler_for<Downstream, std::remove_cvref_t<Event>>)
69 void handle(Event &&event) {
70 downstream.handle(std::forward<Event>(event));
71 }
72
73 void flush() { downstream.flush(); }
74};
75
76template <typename DataEvent, typename T, typename LiveDownstream,
77 typename BatchDownstream>
78 requires std::is_constructible_v<std::span<T const>, DataEvent const &> &&
79 processor<LiveDownstream, bucket<T const>> &&
80 processor<BatchDownstream, bucket<T>>
81class copy_to_full_buckets {
82 std::shared_ptr<bucket_source<T>> bsource;
83 std::size_t bsize;
84
85 bucket<T> bkt;
86 std::size_t filled = 0;
87
88 LiveDownstream live_downstream;
89 BatchDownstream batch_downstream;
90
91 // Mutates 'b' only when throwing.
92 void emit_live(bucket<T> &b, std::size_t start, std::size_t count) {
93 if (count > 0) {
94 try {
95 auto v = bsource->shared_view_of(b);
96 v.shrink(start, count);
97 live_downstream.handle(std::move(v));
98 } catch (end_of_processing const &) {
99 b.shrink(0, start + count);
100 batch_downstream.handle(std::move(b));
101 batch_downstream.flush();
102 throw;
103 }
104 }
105 }
106
107 void emit_batch(bucket<T> &&b) {
108 try {
109 batch_downstream.handle(std::move(b));
110 } catch (end_of_processing const &) {
111 live_downstream.flush();
112 throw;
113 }
114 }
115
116 void flush_batch() {
117 if (not bkt.empty() && filled > 0) {
118 bkt.shrink(0, filled);
119 batch_downstream.handle(std::move(bkt));
120 }
121 batch_downstream.flush();
122 }
123
124 public:
125 explicit copy_to_full_buckets(
126 std::shared_ptr<bucket_source<T>> buffer_provider,
127 arg::batch_size<std::size_t> batch_size,
128 LiveDownstream live_downstream, BatchDownstream batch_downstream)
129 : bsource(std::move(buffer_provider)), bsize(batch_size.value),
130 live_downstream(std::move(live_downstream)),
131 batch_downstream(std::move(batch_downstream)) {
132 if (not bsource)
133 throw std::invalid_argument(
134 "copy_to_full_buckets buffer_provider must not be null");
135 if constexpr (not std::is_same_v<LiveDownstream, internal::sink_all>) {
136 if (not bsource->supports_shared_views())
137 throw std::invalid_argument(
138 "copy_to_full_buckets buffer_provider must support shared views");
139 }
140 if (bsize == 0)
141 throw std::invalid_argument(
142 "copy_to_full_buckets batch size must be positive");
143 }
144
145 [[nodiscard]] auto introspect_node() const -> processor_info {
146 return processor_info(this, "copy_to_full_buckets");
147 }
148
149 [[nodiscard]] auto introspect_graph() const -> processor_graph {
151 live_downstream.introspect_graph().push_entry_point(this),
152 batch_downstream.introspect_graph().push_entry_point(this));
153 }
154
155 template <typename Event>
156 requires std::same_as<std::remove_cvref_t<Event>, DataEvent>
157 void handle(Event &&event) {
158 auto src = std::span<T const>(event);
159 while (not src.empty()) {
160 if (filled == 0 && bkt.empty())
161 bkt = bsource->bucket_of_size(bsize);
162 auto const dest = std::span(bkt).subspan(filled);
163 auto const copy_size = std::min(src.size(), dest.size());
164 std::copy_n(src.begin(), copy_size, dest.begin());
165 if constexpr (not std::is_same_v<LiveDownstream,
166 internal::sink_all>)
167 emit_live(bkt, filled, copy_size);
168 filled += copy_size;
169 if (filled == bsize) {
170 emit_batch(std::move(bkt));
171 bkt = {};
172 filled = 0;
173 }
174 src = src.subspan(copy_size);
175 }
176 }
177
178 template <typename Event>
179 requires(not std::same_as<std::remove_cvref_t<Event>, DataEvent> and
180 handler_for<LiveDownstream, std::remove_cvref_t<Event>>)
181 void handle(Event &&event) {
182 try {
183 live_downstream.handle(std::forward<Event>(event));
184 } catch (end_of_processing const &) {
185 flush_batch();
186 throw;
187 }
188 }
189
190 void flush() {
191 std::exception_ptr end;
192 try {
193 live_downstream.flush();
194 } catch (end_of_processing const &) {
195 end = std::current_exception();
196 }
197 flush_batch();
198 if (end)
199 std::rethrow_exception(end);
200 }
201};
202
203} // namespace internal
204
239template <typename DataEvent, typename T, typename Downstream>
240auto copy_to_buckets(std::shared_ptr<bucket_source<T>> buffer_provider,
241 Downstream downstream) {
242 return internal::copy_to_buckets<DataEvent, T, Downstream>(
243 std::move(buffer_provider), std::move(downstream));
244}
245
307template <typename DataEvent, typename T, typename LiveDownstream,
308 typename BatchDownstream>
309auto copy_to_full_buckets(std::shared_ptr<bucket_source<T>> buffer_provider,
311 LiveDownstream live_downstream,
312 BatchDownstream batch_downstream) {
313 return internal::copy_to_full_buckets<DataEvent, T, LiveDownstream,
314 BatchDownstream>(
315 std::move(buffer_provider), batch_size, std::move(live_downstream),
316 std::move(batch_downstream));
317}
318
319}; // namespace tcspc
auto merge_processor_graphs(processor_graph const &a, processor_graph const &b) -> processor_graph
Create a new processor graph by merging two existing ones.
Definition introspect.hpp:380
auto copy_to_buckets(std::shared_ptr< bucket_source< T > > buffer_provider, Downstream downstream)
Create a processor that copies batches of data into buckets.
Definition copy_to_buckets.hpp:240
auto copy_to_full_buckets(std::shared_ptr< bucket_source< T > > buffer_provider, arg::batch_size< std::size_t > batch_size, LiveDownstream live_downstream, BatchDownstream batch_downstream)
Create a processor that copies data into buckets, ensuring that each bucket is filled to a fixed size...
Definition copy_to_buckets.hpp:309
auto count(access_tracker< count_accessor > &&tracker, Downstream downstream)
Create a processor that counts events of a given type.
Definition count.hpp:312
libtcspc namespace.
Definition acquire.hpp:30
Function argument wrapper for batch size parameter.
Definition arg_wrappers.hpp:47
Abstract base class for polymorphic bucket sources.
Definition bucket.hpp:504