libtcspc C++ API
Streaming TCSPC and time tag data processing
Loading...
Searching...
No Matches
histogram.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 "data_types.hpp"
14#include "errors.hpp"
15#include "histogram_events.hpp"
16#include "histogram_impl.hpp"
17#include "histogram_policy.hpp"
18#include "introspect.hpp"
19#include "processor.hpp"
20
21#include <algorithm>
22#include <cstddef>
23#include <memory>
24#include <span>
25#include <stdexcept>
26#include <type_traits>
27#include <utility>
28
29namespace tcspc {
30
31namespace internal {
32
33template <histogram_policy Policy, typename ResetEvent, typename DataTypes,
34 typename Downstream>
35class histogram {
36 static constexpr histogram_policy overflow_policy =
38 static constexpr bool emit_concluding =
41
42 static_assert(processor<Downstream, histogram_event<DataTypes>>);
43 static_assert(std::is_same_v<ResetEvent, never_event> ||
44 handler_for<Downstream, ResetEvent>);
45 static_assert(overflow_policy != histogram_policy::saturate_on_overflow ||
46 handler_for<Downstream, warning_event>);
47 static_assert(
50 handler_for<Downstream, concluding_histogram_event<DataTypes>>);
51
52 using internal_overflow_policy = std::conditional_t<
54 saturate_on_internal_overflow, stop_on_internal_overflow>;
55
56 using bin_index_type = typename DataTypes::bin_index_type;
57 using bin_type = typename DataTypes::bin_type;
58
59 std::shared_ptr<bucket_source<bin_type>> bsource;
60 bucket<bin_type> hist_bucket;
61 single_histogram<bin_index_type, bin_type, internal_overflow_policy> shist;
62 bool saturate_warning_issued = false;
63
64 Downstream downstream;
65
66 LIBTCSPC_NOINLINE void start_new_round() {
67 hist_bucket = bsource->bucket_of_size(shist.num_bins());
68 shist = decltype(shist){hist_bucket, shist};
69 shist.clear();
70 }
71
72 void reset() {
73 if (hist_bucket.empty())
74 start_new_round();
75 if constexpr (emit_concluding) {
76 downstream.handle(
77 concluding_histogram_event<DataTypes>{std::move(hist_bucket)});
78 }
79 hist_bucket = {};
80 if constexpr (overflow_policy ==
82 saturate_warning_issued = false;
83 }
84
85 [[noreturn]] LIBTCSPC_NOINLINE void overflow_error() {
86 throw histogram_overflow_error("histogram bin overflowed");
87 }
88
89 [[noreturn]] LIBTCSPC_NOINLINE void overflow_stop() {
90 reset();
91 downstream.flush();
92 throw end_of_processing("histogram bin overflowed");
93 }
94
95 LIBTCSPC_NOINLINE void saturated_warning() {
96 downstream.handle(warning_event{"histogram bin saturated"});
97 saturate_warning_issued = true;
98 }
99
100 template <typename DT>
101 LIBTCSPC_NOINLINE void
102 overflow_reset(bin_increment_event<DT> const &event) {
103 if (shist.max_per_bin() == 0)
104 overflow_error();
105 reset();
106 // Recurse at most once, because overflow on single increment (when
107 // max_per_bin == 0) is error.
108 return handle(event);
109 }
110
111 public:
112 explicit histogram(
113 arg::num_bins<std::size_t> num_bins,
114 arg::max_per_bin<bin_type> max_per_bin,
115 std::shared_ptr<bucket_source<bin_type>> buffer_provider,
116 Downstream downstream)
117 : bsource(std::move(buffer_provider)),
118 shist(hist_bucket, max_per_bin, num_bins),
119 downstream(std::move(downstream)) {
120 if (num_bins.value == 0)
121 throw std::invalid_argument("histogram must have at least 1 bin");
122 if (max_per_bin.value < 0)
123 throw std::invalid_argument(
124 "histogram max_per_bin must not be negative");
125 }
126
127 [[nodiscard]] auto introspect_node() const -> processor_info {
128 return processor_info(this, "histogram");
129 }
130
131 [[nodiscard]] auto introspect_graph() const -> processor_graph {
132 return downstream.introspect_graph().push_entry_point(this);
133 }
134
135 template <typename DT> void handle(bin_increment_event<DT> const &event) {
136 static_assert(std::is_same_v<typename DT::bin_index_type,
137 typename DataTypes::bin_index_type>);
138 if (hist_bucket.empty())
139 start_new_round();
140 if (not shist.apply_increments({&event.bin_index, 1})) {
141 if constexpr (overflow_policy ==
143 overflow_error(); // noreturn
144 } else if constexpr (overflow_policy ==
146 overflow_stop(); // noreturn
147 } else if constexpr (overflow_policy ==
149 if (not saturate_warning_issued)
150 saturated_warning();
151 } else if constexpr (overflow_policy ==
153 return overflow_reset(event);
154 }
155 }
156
157 auto const hist_event =
158 histogram_event<DataTypes>{ad_hoc_bucket(std::span(hist_bucket))};
159 downstream.handle(hist_event);
160 }
161
162 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
163 template <typename DT> void handle(bin_increment_event<DT> &&event) {
164 handle(static_cast<bin_increment_event<DT> const &>(event));
165 }
166
167 template <typename E>
168 requires handler_for<Downstream, std::remove_cvref_t<E>>
169 void handle(E &&event) {
170 if constexpr (std::is_convertible_v<std::remove_cvref_t<E>,
171 ResetEvent>)
172 reset();
173 downstream.handle(std::forward<E>(event));
174 }
175
176 void flush() { downstream.flush(); }
177};
178
179} // namespace internal
180
261 typename ResetEvent = never_event,
262 typename DataTypes = default_data_types, typename Downstream>
266 buffer_provider,
267 Downstream downstream) {
268 return internal::histogram<Policy, ResetEvent, DataTypes, Downstream>(
269 num_bins, max_per_bin, std::move(buffer_provider),
270 std::move(downstream));
271}
272
273} // namespace tcspc
auto ad_hoc_bucket(std::span< T > s) -> bucket< T >
Create a tcspc::bucket referencing a span.
Definition bucket.hpp:489
histogram_policy
Histogramming policy specifying behavior.
Definition histogram_policy.hpp:29
@ stop_on_overflow
Treat a histogram bin overflow as end of processing.
Definition histogram_policy.hpp:53
@ saturate_on_overflow
Ignore increments that would cause a bin overflow.
Definition histogram_policy.hpp:61
@ reset_on_overflow
Perform a reset when a histogram bin is about to overflow.
Definition histogram_policy.hpp:79
@ overflow_mask
Bitmask for overflow behavior.
Definition histogram_policy.hpp:88
@ emit_concluding_events
Enable generation of tcspc::concluding_histogram_array_event.
Definition histogram_policy.hpp:108
@ default_policy
Default policy with no bit set: equal to error_on_overflow.
Definition histogram_policy.hpp:33
@ error_on_overflow
Treat a histogram bin overflow as an error.
Definition histogram_policy.hpp:43
auto histogram(arg::num_bins< std::size_t > num_bins, arg::max_per_bin< typename DataTypes::bin_type > max_per_bin, std::shared_ptr< bucket_source< typename DataTypes::bin_type > > buffer_provider, Downstream downstream)
Create a processor that collects a histogram.
Definition histogram.hpp:263
libtcspc namespace.
Definition acquire.hpp:29
Function argument wrapper for maximum bin value.
Definition arg_wrappers.hpp:287
Function argument wrapper for number of bins parameter.
Definition arg_wrappers.hpp:327
Abstract base class for polymorphic bucket sources.
Definition bucket.hpp:505
The default data type set.
Definition data_types.hpp:24
An event type whose instances never occur.
Definition core.hpp:54