9#include "arg_wrappers.hpp"
12#include "introspect.hpp"
13#include "numeric_traits.hpp"
14#include "processor.hpp"
15#include "type_list.hpp"
16#include "variant_event.hpp"
17#include "vector_queue.hpp"
35template <
typename EventList,
typename NumericTraits,
typename Downstream>
38 static_assert(type_list_like<EventList>);
45 is_copy_constructible_list_v<EventList>,
46 "merge requires every event in EventList to be copy-constructible "
47 "(events are copied into the reordering buffer)");
55 bool pending_on_1 =
false;
56 std::array<bool, 2> input_flushed{
false,
false};
57 bool ended_with_exception =
false;
58 vector_queue<variant_or_single_event<EventList>> pending;
59 std::size_t max_buffered;
61 Downstream downstream;
63 template <
unsigned InputChannel>
64 [[nodiscard]]
auto is_other_flushed() const noexcept ->
bool {
65 return input_flushed[1 - InputChannel];
68 template <
unsigned InputChannel>
69 [[nodiscard]]
auto is_pending_on_other() const noexcept ->
bool {
70 return pending_on_1 == (InputChannel == 0);
73 template <
unsigned InputChannel>
void set_pending_on() noexcept {
74 pending_on_1 = (InputChannel == 1);
78 template <
bool Inclusive>
79 void emit_pending_until(
typename NumericTraits::abstime_type cutoff) {
80 auto emit_if_before_cutoff = [&](
auto const &e) {
82 Inclusive ? e.abstime <= cutoff : e.abstime < cutoff;
88 emit_if_before_cutoff, pending.front()))
92 void emit_all_pending() {
93 auto emit = [&](
auto const &e) { downstream.handle(e); };
94 while (!pending.empty()) {
101 explicit merge_impl(arg::max_buffered<std::size_t> max_buffered,
102 Downstream downstream)
103 : max_buffered(max_buffered.value), downstream(std::move(downstream)) {
106 merge_impl(merge_impl
const &) =
delete;
107 auto operator=(merge_impl
const &) =
delete;
108 merge_impl(merge_impl &&) =
delete;
109 auto operator=(merge_impl &&) =
delete;
110 ~merge_impl() =
default;
112 [[nodiscard]]
auto introspect_node() const -> processor_info {
113 return processor_info(
this,
"merge_impl");
116 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
117 return downstream.introspect_graph().push_entry_point(
this);
120 template <
unsigned InputChannel,
typename Event>
121 void handle(Event
const &event) {
122 static_assert(convertible_to_type_list_member<Event, EventList>);
123 static_assert(std::is_same_v<
decltype(
event.abstime),
124 typename NumericTraits::abstime_type>);
125 if (ended_with_exception)
128 if (is_pending_on_other<InputChannel>()) {
132 emit_pending_until<InputChannel == 1>(event.abstime);
136 if (not pending.empty())
137 return downstream.handle(event);
142 set_pending_on<InputChannel>();
146 if (is_other_flushed<InputChannel>()) {
147 assert(pending.empty());
148 return downstream.handle(event);
150 if (pending.size() == max_buffered)
151 throw buffer_overflow_error(
"merge buffer capacity exceeded");
153 }
catch (std::exception
const &) {
154 ended_with_exception =
true;
159 template <
unsigned InputChannel>
void flush() {
160 input_flushed[InputChannel] =
true;
161 if (ended_with_exception)
163 if (is_other_flushed<InputChannel>()) {
169 }
else if (is_pending_on_other<InputChannel>()) {
177template <
unsigned InputChannel,
typename EventList,
typename NumericTraits,
180 std::shared_ptr<merge_impl<EventList, NumericTraits, Downstream>> impl;
183 explicit merge_input(
184 std::shared_ptr<merge_impl<EventList, NumericTraits, Downstream>> impl)
185 : impl(std::move(impl)) {}
188 merge_input(merge_input
const &) =
delete;
189 auto operator=(merge_input
const &) =
delete;
190 merge_input(merge_input &&) noexcept = default;
191 auto operator=(merge_input &&) = delete;
192 ~merge_input() = default;
194 [[nodiscard]] auto introspect_node() const -> processor_info {
195 return processor_info(
this,
"merge_input");
198 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
199 return impl->introspect_graph().push_entry_point(
this);
202 template <
typename Event>
203 requires convertible_to_type_list_member<std::remove_cvref_t<Event>,
205 void handle(Event &&event) {
206 static_assert(std::is_same_v<
decltype(
event.abstime),
207 typename NumericTraits::abstime_type>);
208 impl->template handle<InputChannel>(std::forward<Event>(event));
211 void flush() { impl->template flush<InputChannel>(); }
259 Downstream downstream) {
260 auto p = std::make_shared<
261 internal::merge_impl<EventList, NumericTraits, Downstream>>(
262 max_buffered, std::move(downstream));
264 internal::merge_input<0, EventList, NumericTraits, Downstream>(p),
265 internal::merge_input<1, EventList, NumericTraits, Downstream>(p)};
318template <std::size_t N,
typename EventList,
319 typename NumericTraits = default_numeric_traits,
typename Downstream>
321 Downstream downstream) {
322 if constexpr (N == 0) {
324 }
else if constexpr (N == 1) {
325 return std::tuple{std::move(downstream)};
328 max_buffered, std::move(downstream));
330 std::size_t
const left = N / 2;
331 std::size_t
const right = N - left;
332 if constexpr (left == 1) {
333 if constexpr (right == 1) {
334 return std::tuple{std::move(final_in0), std::move(final_in1)};
336 return std::tuple_cat(std::tuple{std::move(final_in0)},
338 max_buffered, std::move(final_in1)));
342 max_buffered, std::move(final_in0)),
344 max_buffered, std::move(final_in1)));
353template <std::
size_t N,
typename Downstream>
354 requires processor<Downstream>
355class merge_unsorted_impl {
356 Downstream downstream;
359 bool ended_with_exception =
false;
360 std::array<bool, N> input_flushed{};
363 explicit merge_unsorted_impl(Downstream downstream)
364 : downstream(std::move(downstream)) {}
366 merge_unsorted_impl(merge_unsorted_impl
const &) =
delete;
367 auto operator=(merge_unsorted_impl
const &) =
delete;
368 merge_unsorted_impl(merge_unsorted_impl &&) =
delete;
369 auto operator=(merge_unsorted_impl &&) =
delete;
370 ~merge_unsorted_impl() =
default;
372 [[nodiscard]]
auto introspect_node() const -> processor_info {
373 return processor_info(
this,
"merge_unsorted_impl");
376 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
377 return downstream.introspect_graph().push_entry_point(
this);
380 template <
typename Event>
381 requires handler_for<Downstream, std::remove_cvref_t<Event>>
382 void handle(Event &&event) {
383 if (ended_with_exception)
386 downstream.handle(std::forward<Event>(event));
387 }
catch (std::exception
const &) {
388 ended_with_exception =
true;
393 void flush(std::size_t input_channel) {
394 input_flushed[input_channel] =
true;
395 if (ended_with_exception)
397 if (std::all_of(input_flushed.begin(), input_flushed.end(),
398 [](
auto f) { return f; }))
403template <std::
size_t N,
typename Downstream>
class merge_unsorted_input {
404 std::shared_ptr<merge_unsorted_impl<N, Downstream>> impl;
410 explicit merge_unsorted_input(
411 std::shared_ptr<merge_unsorted_impl<N, Downstream>> impl,
413 : impl(std::move(impl)), chan(channel) {}
416 merge_unsorted_input(merge_unsorted_input
const &) =
delete;
417 auto operator=(merge_unsorted_input
const &) =
delete;
418 merge_unsorted_input(merge_unsorted_input &&) noexcept = default;
419 auto operator=(merge_unsorted_input &&) = delete;
420 ~merge_unsorted_input() = default;
422 [[nodiscard]] auto introspect_node() const -> processor_info {
423 return processor_info(
this,
"merge_unsorted_input");
426 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
427 return impl->introspect_graph().push_entry_point(
this);
430 template <
typename Event>
431 requires handler_for<Downstream, std::remove_cvref_t<Event>>
432 void handle(Event &&event) {
433 impl->handle(std::forward<Event>(event));
436 void flush() { impl->flush(chan); }
439template <std::size_t N,
typename Downstream, std::size_t... Indices>
440auto make_merge_unsorted_inputs(
441 std::shared_ptr<merge_unsorted_impl<N, Downstream>> impl,
442 std::index_sequence<Indices...> ) {
443 using input_type = merge_unsorted_input<N, Downstream>;
444 return std::array<input_type, N>{(input_type(impl, Indices))...};
475template <std::
size_t N = 2,
typename Downstream>
477 auto impl = std::make_shared<internal::merge_unsorted_impl<N, Downstream>>(
478 std::move(downstream));
479 return internal::make_merge_unsorted_inputs(std::move(impl),
480 std::make_index_sequence<N>());
constexpr bool is_processor_of_list_v
Trait variable to check whether a processor handles a list of event types and flush.
Definition processor.hpp:223
auto merge_n_unsorted(Downstream downstream)
Create a processor that merges a given number of event streams without sorting by abstime.
Definition merge.hpp:476
auto merge_n(arg::max_buffered< std::size_t > max_buffered, Downstream downstream)
Create a processor that merges a given number of event streams.
Definition merge.hpp:320
auto merge(arg::max_buffered< std::size_t > max_buffered, Downstream downstream)
Create a pair of processors that merge two event streams.
Definition merge.hpp:258
constexpr auto visit_variant_or_single_event(Visitor visitor, Event &&event)
Apply a visitor to an event that is not a tcspc::variant_event.
Definition variant_event.hpp:131
libtcspc namespace.
Definition acquire.hpp:30
Function argument wrapper for maximum buffered parameter.
Definition arg_wrappers.hpp:237
The default numeric traits.
Definition numeric_traits.hpp:27