libtcspc C++ API
Streaming TCSPC and time tag data processing
Loading...
Searching...
No Matches
batch_unbatch_bin_increment_clusters.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 "bin_increment_cluster_encoding.hpp"
11#include "bucket.hpp"
12#include "common.hpp"
13#include "histogram_events.hpp"
14#include "introspect.hpp"
15#include "numeric_traits.hpp"
16#include "processor.hpp"
17
18#include <cassert>
19#include <cstddef>
20#include <functional>
21#include <memory>
22#include <span>
23#include <type_traits>
24#include <utility>
25
26namespace tcspc {
27
28namespace internal {
29
30// Helper for batch_bin_increment_clusters.
31template <typename BinIndex>
32class batch_bin_increment_clusters_encoding_adapter {
33 std::reference_wrapper<bucket<BinIndex>> bkt;
34 std::size_t *siz;
35
36 public:
37 explicit batch_bin_increment_clusters_encoding_adapter(
38 bucket<BinIndex> &storage, std::size_t &usage)
39 : bkt(storage), siz(&usage) {}
40
41 [[nodiscard]] auto available_capacity() const -> std::size_t {
42 return bkt.get().size() - *siz;
43 }
44
45 [[nodiscard]] auto make_space(std::size_t size) -> std::span<BinIndex> {
46 assert(size <= available_capacity());
47 auto const old_size = *siz;
48 *siz += size;
49 return bkt.get().subspan(old_size, size);
50 }
51};
52
53template <typename NumericTraits, typename Downstream>
54 requires processor<Downstream,
55 bucket<typename NumericTraits::bin_index_type>>
56class batch_bin_increment_clusters {
57 using bin_index_type = NumericTraits::bin_index_type;
58
59 std::shared_ptr<bucket_source<bin_index_type>> bsource;
60
61 bucket<bin_index_type> cur_batch;
62 std::size_t bucket_used_size = 0;
63 std::size_t cur_batch_size = 0;
64 std::size_t bkt_siz;
65 std::size_t batch_siz;
66
67 Downstream downstream;
68
69 void emit_cur_batch() {
70 if (cur_batch_size > 0) {
71 cur_batch.shrink(0, bucket_used_size);
72 downstream.handle(std::move(cur_batch));
73 }
74 cur_batch = {};
75 bucket_used_size = 0;
76 cur_batch_size = 0;
77 }
78
79 public:
80 explicit batch_bin_increment_clusters(
81 std::shared_ptr<bucket_source<typename NumericTraits::bin_index_type>>
82 buffer_provider,
83 arg::bucket_size<std::size_t> bucket_size,
84 arg::batch_size<std::size_t> batch_size, Downstream downstream)
85 : bsource(std::move(buffer_provider)), bkt_siz(bucket_size.value),
86 batch_siz(batch_size.value), downstream(std::move(downstream)) {}
87
88 [[nodiscard]] auto introspect_node() const -> processor_info {
89 return processor_info(this, "batch_bin_increment_clusters");
90 }
91
92 [[nodiscard]] auto introspect_graph() const -> processor_graph {
93 return downstream.introspect_graph().push_entry_point(this);
94 }
95
96 template <typename NT>
97 void handle(bin_increment_cluster_event<NT> const &event) {
98 static_assert(std::is_same_v<typename NT::bin_index_type,
99 typename NumericTraits::bin_index_type>);
100 std::size_t const encoded_size = encoded_bin_increment_cluster_size<
101 typename NumericTraits::bin_index_type>(event.bin_indices.size());
102 if (encoded_size > bkt_siz - bucket_used_size) { // Won't fit.
103 emit_cur_batch();
104
105 // If the cluster will not fit in a single default-sized bucket,
106 // emit a dedicated batch. We do not attempt to minimize internal
107 // fragmentation (i.e., waste of remaining bucket capacity) under
108 // conditions where clusters take up a significant fraction of the
109 // default bucket size; users should avoid operating in a regime
110 // where that happens frequently (though the degradation is only in
111 // performance, not correctness).
112 if (encoded_size > bkt_siz) {
113 auto single_cluster_batch =
114 bsource->bucket_of_size(encoded_size);
115 std::size_t usage = 0;
116 [[maybe_unused]] bool const did_fit =
117 encode_bin_increment_cluster(
118 batch_bin_increment_clusters_encoding_adapter(
119 single_cluster_batch, usage),
120 std::span(event.bin_indices));
121 assert(did_fit);
122 assert(usage == encoded_size);
123 downstream.handle(std::move(single_cluster_batch));
124 return;
125 }
126 }
127
128 if (cur_batch.empty())
129 cur_batch = bsource->bucket_of_size(bkt_siz);
130 [[maybe_unused]] bool const did_fit = encode_bin_increment_cluster(
131 batch_bin_increment_clusters_encoding_adapter(cur_batch,
132 bucket_used_size),
133 std::span(event.bin_indices));
134 assert(did_fit);
135
136 ++cur_batch_size;
137 if (cur_batch_size == batch_siz)
138 emit_cur_batch();
139 }
140
141 // NOLINTBEGIN(cppcoreguidelines-rvalue-reference-param-not-moved)
142 template <typename NT>
143 void handle(bin_increment_cluster_event<NT> &&event) {
144 handle(static_cast<bin_increment_cluster_event<NT> const &>(event));
145 }
146 // NOLINTEND(cppcoreguidelines-rvalue-reference-param-not-moved)
147
148 template <typename Event>
149 requires handler_for<Downstream, std::remove_cvref_t<Event>>
150 void handle(Event &&event) {
151 downstream.handle(std::forward<Event>(event));
152 }
153
154 void flush() {
155 emit_cur_batch();
156 downstream.flush();
157 }
158};
159
160template <typename NumericTraits, typename Downstream>
161 requires processor<Downstream, bin_increment_cluster_event<NumericTraits>>
162class unbatch_bin_increment_clusters {
163 using bin_index_type = NumericTraits::bin_index_type;
164
165 Downstream downstream;
166
167 public:
168 explicit unbatch_bin_increment_clusters(Downstream downstream)
169 : downstream(std::move(downstream)) {}
170
171 [[nodiscard]] auto introspect_node() const -> processor_info {
172 return processor_info(this, "unbatch_bin_increment_clusters");
173 }
174
175 [[nodiscard]] auto introspect_graph() const -> processor_graph {
176 return downstream.introspect_graph().push_entry_point(this);
177 }
178
179 template <typename Event>
180 requires(std::convertible_to<
181 std::remove_cvref_t<Event>,
182 bucket<typename NumericTraits::bin_index_type>> or
183 std::convertible_to<
184 std::remove_cvref_t<Event>,
185 bucket<typename NumericTraits::bin_index_type const>>)
186 void handle(Event &&event) {
187 bin_increment_cluster_decoder<bin_index_type> const decoder(event);
188 for (auto const cluster_span : decoder) {
189 // The cluster_span is a span<T const>, but we want bucket<T>,
190 // not bucket<T const>. Casting is safe because
191 // `ad_hoc_bucket<T>` emitted as const lvalue reference does
192 // not allow mutation of the referred data.
193 auto const mut_span = std::span<bin_index_type>(
194 const_cast<bin_index_type *>(cluster_span.data()),
195 cluster_span.size());
196 bin_increment_cluster_event<NumericTraits> const e{
197 ad_hoc_bucket(mut_span)};
198 downstream.handle(e);
199 }
200 }
201
202 template <typename Event>
203 requires(not std::convertible_to<
204 std::remove_cvref_t<Event>,
205 bucket<typename NumericTraits::bin_index_type>> and
206 not std::convertible_to<
207 std::remove_cvref_t<Event>,
208 bucket<typename NumericTraits::bin_index_type const>> and
209 handler_for<Downstream, std::remove_cvref_t<Event>>)
210 void handle(Event &&event) {
211 downstream.handle(std::forward<Event>(event));
212 }
213
214 void flush() { downstream.flush(); }
215};
216
217} // namespace internal
218
259template <typename NumericTraits = default_numeric_traits, typename Downstream>
262 buffer_provider,
264 arg::batch_size<std::size_t> batch_size, Downstream downstream) {
265 return internal::batch_bin_increment_clusters<NumericTraits, Downstream>(
266 std::move(buffer_provider), bucket_size, batch_size,
267 std::move(downstream));
268}
269
296template <typename NumericTraits = default_numeric_traits, typename Downstream>
297auto unbatch_bin_increment_clusters(Downstream downstream) {
298 return internal::unbatch_bin_increment_clusters<NumericTraits, Downstream>(
299 std::move(downstream));
300}
301
302} // namespace tcspc
auto ad_hoc_bucket(std::span< T > s) -> bucket< T >
Create a tcspc::bucket referencing a span.
Definition bucket.hpp:488
auto batch_bin_increment_clusters(std::shared_ptr< bucket_source< typename NumericTraits::bin_index_type > > buffer_provider, arg::bucket_size< std::size_t > bucket_size, arg::batch_size< std::size_t > batch_size, Downstream downstream)
Create a processor that collects bin increment clusters into encoded batches.
Definition batch_unbatch_bin_increment_clusters.hpp:260
auto unbatch_bin_increment_clusters(Downstream downstream)
Create a processor that splits encoded batches of bin increment clusters into individual clusters.
Definition batch_unbatch_bin_increment_clusters.hpp:297
libtcspc namespace.
Definition acquire.hpp:30
Function argument wrapper for batch size parameter.
Definition arg_wrappers.hpp:47
Function argument wrapper for bucket size.
Definition arg_wrappers.hpp:67
Abstract base class for polymorphic bucket sources.
Definition bucket.hpp:504