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);
79 template <
typename Pred>
void emit_pending(Pred predicate) {
80 auto emit_if_true = [&](
auto const &e) {
81 bool p = predicate(e.abstime);
86 while (!pending.empty() &&
92 explicit merge_impl(arg::max_buffered<std::size_t> max_buffered,
93 Downstream downstream)
94 : max_buffered(max_buffered.value), downstream(std::move(downstream)) {
97 merge_impl(merge_impl
const &) =
delete;
98 auto operator=(merge_impl
const &) =
delete;
99 merge_impl(merge_impl &&) =
delete;
100 auto operator=(merge_impl &&) =
delete;
101 ~merge_impl() =
default;
103 [[nodiscard]]
auto introspect_node() const -> processor_info {
104 return processor_info(
this,
"merge_impl");
107 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
108 return downstream.introspect_graph().push_entry_point(
this);
111 template <
unsigned InputChannel,
typename Event>
112 void handle(Event
const &event) {
113 static_assert(convertible_to_type_list_member<Event, EventList>);
114 static_assert(std::is_same_v<
decltype(
event.abstime),
115 typename NumericTraits::abstime_type>);
116 if (ended_with_exception)
119 if (is_pending_on_other<InputChannel>()) {
121 auto cutoff =
event.abstime;
124 if constexpr (InputChannel == 0)
126 emit_pending([=](
auto t) {
return t <= cutoff; });
130 if (not pending.empty())
131 return downstream.handle(event);
136 set_pending_on<InputChannel>();
140 if (is_other_flushed<InputChannel>()) {
141 assert(pending.empty());
142 return downstream.handle(event);
144 if (pending.size() == max_buffered)
145 throw buffer_overflow_error(
"merge buffer capacity exceeded");
147 }
catch (std::exception
const &) {
148 ended_with_exception =
true;
153 template <
unsigned InputChannel>
void flush() {
154 input_flushed[InputChannel] =
true;
155 if (ended_with_exception)
157 if (is_other_flushed<InputChannel>()) {
161 emit_pending([](
auto ) {
return true; });
163 }
else if (is_pending_on_other<InputChannel>()) {
166 emit_pending([](
auto ) {
return true; });
171template <
unsigned InputChannel,
typename EventList,
typename NumericTraits,
174 std::shared_ptr<merge_impl<EventList, NumericTraits, Downstream>> impl;
177 explicit merge_input(
178 std::shared_ptr<merge_impl<EventList, NumericTraits, Downstream>> impl)
179 : impl(std::move(impl)) {}
182 merge_input(merge_input
const &) =
delete;
183 auto operator=(merge_input
const &) =
delete;
184 merge_input(merge_input &&) noexcept = default;
185 auto operator=(merge_input &&) = delete;
186 ~merge_input() = default;
188 [[nodiscard]] auto introspect_node() const -> processor_info {
189 return processor_info(
this,
"merge_input");
192 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
193 return impl->introspect_graph().push_entry_point(
this);
196 template <
typename Event>
197 requires convertible_to_type_list_member<std::remove_cvref_t<Event>,
199 void handle(Event &&event) {
200 static_assert(std::is_same_v<
decltype(
event.abstime),
201 typename NumericTraits::abstime_type>);
202 impl->template handle<InputChannel>(std::forward<Event>(event));
205 void flush() { impl->template flush<InputChannel>(); }
253 Downstream downstream) {
254 auto p = std::make_shared<
255 internal::merge_impl<EventList, NumericTraits, Downstream>>(
256 max_buffered, std::move(downstream));
258 internal::merge_input<0, EventList, NumericTraits, Downstream>(p),
259 internal::merge_input<1, EventList, NumericTraits, Downstream>(p)};
312template <std::size_t N,
typename EventList,
313 typename NumericTraits = default_numeric_traits,
typename Downstream>
315 Downstream downstream) {
316 if constexpr (N == 0) {
318 }
else if constexpr (N == 1) {
319 return std::tuple{std::move(downstream)};
322 max_buffered, std::move(downstream));
324 std::size_t
const left = N / 2;
325 std::size_t
const right = N - left;
326 if constexpr (left == 1) {
327 if constexpr (right == 1) {
328 return std::tuple{std::move(final_in0), std::move(final_in1)};
330 return std::tuple_cat(std::tuple{std::move(final_in0)},
332 max_buffered, std::move(final_in1)));
336 max_buffered, std::move(final_in0)),
338 max_buffered, std::move(final_in1)));
347template <std::
size_t N,
typename Downstream>
348 requires processor<Downstream>
349class merge_unsorted_impl {
350 Downstream downstream;
353 bool ended_with_exception =
false;
354 std::array<bool, N> input_flushed{};
357 explicit merge_unsorted_impl(Downstream downstream)
358 : downstream(std::move(downstream)) {}
360 merge_unsorted_impl(merge_unsorted_impl
const &) =
delete;
361 auto operator=(merge_unsorted_impl
const &) =
delete;
362 merge_unsorted_impl(merge_unsorted_impl &&) =
delete;
363 auto operator=(merge_unsorted_impl &&) =
delete;
364 ~merge_unsorted_impl() =
default;
366 [[nodiscard]]
auto introspect_node() const -> processor_info {
367 return processor_info(
this,
"merge_unsorted_impl");
370 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
371 return downstream.introspect_graph().push_entry_point(
this);
374 template <
typename Event>
375 requires handler_for<Downstream, std::remove_cvref_t<Event>>
376 void handle(Event &&event) {
377 if (ended_with_exception)
380 downstream.handle(std::forward<Event>(event));
381 }
catch (std::exception
const &) {
382 ended_with_exception =
true;
387 void flush(std::size_t input_channel) {
388 input_flushed[input_channel] =
true;
389 if (ended_with_exception)
391 if (std::all_of(input_flushed.begin(), input_flushed.end(),
392 [](
auto f) { return f; }))
397template <std::
size_t N,
typename Downstream>
class merge_unsorted_input {
398 std::shared_ptr<merge_unsorted_impl<N, Downstream>> impl;
404 explicit merge_unsorted_input(
405 std::shared_ptr<merge_unsorted_impl<N, Downstream>> impl,
407 : impl(std::move(impl)), chan(channel) {}
410 merge_unsorted_input(merge_unsorted_input
const &) =
delete;
411 auto operator=(merge_unsorted_input
const &) =
delete;
412 merge_unsorted_input(merge_unsorted_input &&) noexcept = default;
413 auto operator=(merge_unsorted_input &&) = delete;
414 ~merge_unsorted_input() = default;
416 [[nodiscard]] auto introspect_node() const -> processor_info {
417 return processor_info(
this,
"merge_unsorted_input");
420 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
421 return impl->introspect_graph().push_entry_point(
this);
424 template <
typename Event>
425 requires handler_for<Downstream, std::remove_cvref_t<Event>>
426 void handle(Event &&event) {
427 impl->handle(std::forward<Event>(event));
430 void flush() { impl->flush(chan); }
433template <std::size_t N,
typename Downstream, std::size_t... Indices>
434auto make_merge_unsorted_inputs(
435 std::shared_ptr<merge_unsorted_impl<N, Downstream>> impl,
436 std::index_sequence<Indices...> ) {
437 using input_type = merge_unsorted_input<N, Downstream>;
438 return std::array<input_type, N>{(input_type(impl, Indices))...};
469template <std::
size_t N = 2,
typename Downstream>
471 auto impl = std::make_shared<internal::merge_unsorted_impl<N, Downstream>>(
472 std::move(downstream));
473 return internal::make_merge_unsorted_inputs(std::move(impl),
474 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:470
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:314
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:252
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