11#include "introspect.hpp"
12#include "processor.hpp"
28template <
typename Event,
typename Downstream>
29 requires processor<Downstream, bucket<Event>>
30class batch_from_bytes {
31 static_assert(std::is_trivial_v<Event>,
32 "batch_from_bytes requires Event to be a trivial type "
33 "(events are constructed by copying raw bytes)");
35 std::shared_ptr<bucket_source<Event>> bsource;
37 std::size_t bytes_buffered = 0;
38 std::array<std::byte,
sizeof(Event)> buf;
40 Downstream downstream;
43 explicit batch_from_bytes(
44 std::shared_ptr<bucket_source<Event>> buffer_provider,
45 Downstream downstream)
46 : bsource(std::move(buffer_provider)),
47 downstream(std::move(downstream)) {}
49 [[nodiscard]]
auto introspect_node() const -> processor_info {
50 return processor_info(
this,
"batch_from_bytes");
53 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
54 return downstream.introspect_graph().push_entry_point(
this);
57 template <
typename ByteSpan>
58 requires std::constructible_from<std::span<std::byte const>,
60 void handle(ByteSpan
const &event) {
61 auto input_span = std::span<std::byte const>(event);
62 auto const bytes_available = bytes_buffered + input_span.size();
63 if (bytes_available <
sizeof(Event)) {
64 std::copy(input_span.begin(), input_span.end(),
65 std::span(buf).subspan(bytes_buffered).begin());
66 bytes_buffered = bytes_available;
70 auto const batch_size = bytes_available /
sizeof(Event);
71 auto bucket = bsource->bucket_of_size(batch_size);
72 auto const output_span = std::as_writable_bytes(std::span(bucket));
73 auto const input_bulk =
74 input_span.first(output_span.size() - bytes_buffered);
75 auto const remainder = input_span.subspan(input_bulk.size());
76 auto const output_bulk = output_span.subspan(bytes_buffered);
78 std::copy_n(buf.begin(), bytes_buffered, output_span.begin());
79 std::copy(input_bulk.begin(), input_bulk.end(), output_bulk.begin());
80 std::copy(remainder.begin(), remainder.end(), buf.begin());
81 bytes_buffered = remainder.size();
83 downstream.handle(std::move(bucket));
87 if (bytes_buffered > 0)
88 throw std::runtime_error(
"excess bytes at end of stream");
93template <
typename Event,
typename Downstream>
94 requires processor<Downstream, Event>
95class unbatch_from_bytes {
96 static_assert(std::is_trivial_v<Event>,
97 "unbatch_from_bytes requires Event to be a trivial type "
98 "(events are constructed by copying raw bytes)");
100 std::size_t bytes_buffered = 0;
101 std::array<std::byte,
sizeof(Event)> buf;
103 Downstream downstream;
106 explicit unbatch_from_bytes(Downstream downstream)
107 : downstream(std::move(downstream)) {}
109 [[nodiscard]]
auto introspect_node() const -> processor_info {
110 return processor_info(
this,
"unbatch_from_bytes");
113 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
114 return downstream.introspect_graph().push_entry_point(
this);
117 template <
typename ByteSpan>
118 requires std::constructible_from<std::span<std::byte const>,
120 void handle(ByteSpan
const &event) {
121 auto input_span = std::span<std::byte const>(event);
122 if (bytes_buffered > 0) {
123 auto const available_bytes = bytes_buffered + input_span.size();
124 if (available_bytes <
sizeof(Event)) {
125 std::copy(input_span.begin(), input_span.end(),
126 std::span(buf).subspan(bytes_buffered).begin());
127 bytes_buffered = available_bytes;
131 auto const output_bytes = std::as_writable_bytes(std::span(&e, 1));
132 auto const bytes_to_fill =
sizeof(Event) - bytes_buffered;
133 std::copy_n(buf.begin(), bytes_buffered, output_bytes.begin());
134 std::copy_n(input_span.begin(), bytes_to_fill,
135 output_bytes.subspan(bytes_buffered).begin());
136 downstream.handle(std::as_const(e));
137 input_span = input_span.subspan(bytes_to_fill);
140 auto const n_whole = input_span.size() /
sizeof(Event);
141 auto const whole_event_bytes =
142 input_span.first(n_whole *
sizeof(Event));
143 auto const remainder = input_span.subspan(whole_event_bytes.size());
145 if (is_aligned<Event>(input_span.data())) {
148 reinterpret_cast<Event
const *
>(input_span.data());
149 for (Event
const &e : std::span(ptr, n_whole))
150 downstream.handle(e);
152 for (std::size_t i = 0; i < whole_event_bytes.size();
153 i +=
sizeof(Event)) {
155 std::copy_n(whole_event_bytes.subspan(i).begin(),
157 std::as_writable_bytes(std::span(&e, 1)).begin());
158 downstream.handle(std::as_const(e));
162 std::copy(remainder.begin(), remainder.end(), buf.begin());
163 bytes_buffered = remainder.size();
167 if (bytes_buffered > 0)
168 throw std::runtime_error(
"excess bytes at end of stream");
210template <
typename Event,
typename Downstream>
212 Downstream downstream) {
213 return internal::batch_from_bytes<Event, Downstream>(
214 std::move(buffer_provider), std::move(downstream));
247template <
typename Event,
typename Downstream>
249 return internal::unbatch_from_bytes<Event, Downstream>(
250 std::move(downstream));
auto batch_from_bytes(std::shared_ptr< bucket_source< Event > > buffer_provider, Downstream downstream)
Create a processor that converts batches of bytes into batches of events.
Definition batch_unbatch_from_bytes.hpp:211
auto unbatch_from_bytes(Downstream downstream)
Create a processor that converts batches of bytes into individual events.
Definition batch_unbatch_from_bytes.hpp:248
libtcspc namespace.
Definition acquire.hpp:30
Abstract base class for polymorphic bucket sources.
Definition bucket.hpp:504