9#include "arg_wrappers.hpp"
12#include "introspect.hpp"
29#include <system_error>
50 std::move_constructible<T> &&
51 requires(T &s, std::span<std::byte const> buf) {
52 { s.is_error() }
noexcept -> std::same_as<bool>;
53 { s.tell() }
noexcept -> std::same_as<std::optional<std::uint64_t>>;
54 { s.write(buf) }
noexcept -> std::same_as<void>;
60 std::uint64_t bytes_written = 0;
63 static auto is_error() noexcept ->
bool {
return false; }
64 [[nodiscard]]
auto tell() const noexcept -> std::optional<std::uint64_t> {
67 void write(std::span<std::byte const>
buffer)
noexcept {
68 bytes_written +=
buffer.size();
74template <
typename OStream>
class ostream_output_stream {
75 static_assert(std::is_base_of_v<std::ostream, OStream>);
79 explicit ostream_output_stream(OStream stream)
80 : stream(std::move(stream)) {
81 this->stream.exceptions(std::ios::goodbit);
84 auto is_error() noexcept ->
bool {
return stream.fail(); }
86 [[nodiscard]]
auto tell() noexcept -> std::optional<std::uint64_t> {
89 std::int64_t
const pos = stream.tellp();
91 return std::uint64_t(pos);
96 void write(std::span<std::byte const>
buffer)
noexcept {
98 stream.write(
reinterpret_cast<char const *
>(
buffer.data()),
99 static_cast<std::streamsize
>(
buffer.size()));
104class cfile_output_stream {
109 explicit cfile_output_stream(std::FILE *stream,
bool close_on_destruction)
110 : fp(stream), should_close(close_on_destruction && fp != nullptr) {}
112 cfile_output_stream(cfile_output_stream
const &) =
delete;
113 auto operator=(cfile_output_stream
const &) =
delete;
115 cfile_output_stream(cfile_output_stream &&other) noexcept
116 : fp(std::exchange(other.fp,
nullptr)),
117 should_close(std::exchange(other.should_close,
false)) {}
119 auto operator=(cfile_output_stream &&) =
delete;
121 ~cfile_output_stream() {
123 (void)std::fclose(fp);
126 auto is_error() noexcept ->
bool {
127 return fp ==
nullptr || std::ferror(fp) != 0;
130 [[nodiscard]]
auto tell() noexcept -> std::optional<std::uint64_t> {
140 return std::uint64_t(pos);
144 void write(std::span<std::byte const>
buffer)
noexcept {
154unbuffered_binary_ofstream_output_stream(std::string
const &filename,
155 arg::truncate<bool> truncate,
156 arg::append<bool> append) {
157 std::ofstream stream;
160 stream.rdbuf()->pubsetbuf(
nullptr, 0);
162 stream.open(filename,
164 (truncate.value ? std::ios::trunc : std::ios::openmode{}) |
165 (append.value ? std::ios::ate : std::ios::openmode{}));
167 throw input_output_error(
"failed to open output file: " + filename);
168 return internal::ostream_output_stream(std::move(stream));
172inline auto binary_ofstream_output_stream(std::string
const &filename,
173 arg::truncate<bool> truncate,
174 arg::append<bool> append) {
175 std::ofstream stream;
176 stream.open(filename,
178 (truncate.value ? std::ios::trunc : std::ios::openmode{}) |
179 (append.value ? std::ios::ate : std::ios::openmode{}));
181 throw input_output_error(
"failed to open output file: " + filename);
182 return internal::ostream_output_stream(std::move(stream));
185inline auto unbuffered_binary_cfile_output_stream(std::string
const &filename,
186 arg::truncate<bool> truncate,
187 arg::append<bool> append) {
188 char const *mode = std::invoke([&] {
197 (void)fopen_s(&fp, filename.c_str(), mode);
201 std::FILE *fp = std::fopen(filename.c_str(), mode);
205 throw std::system_error(errno, std::generic_category());
206 throw input_output_error(
"failed to open output file: " + filename);
208 if (std::setvbuf(fp,
nullptr, _IONBF, 0) != 0)
209 throw input_output_error(
210 "failed to disable buffering for output file: " + filename);
211 return internal::cfile_output_stream(fp,
true);
215inline auto binary_cfile_output_stream(std::string
const &filename,
216 arg::truncate<bool> truncate,
217 arg::append<bool> append) {
218 char const *mode = std::invoke([&] {
227 (void)fopen_s(&fp, filename.c_str(), mode);
231 std::FILE *fp = std::fopen(filename.c_str(), mode);
235 throw std::system_error(errno, std::generic_category());
236 throw input_output_error(
"failed to open output file: " + filename);
238 return internal::cfile_output_stream(fp,
true);
273 arg::append<bool>
append = arg::append{
false}) {
274 return internal::unbuffered_binary_cfile_output_stream(filename, truncate,
293 static_assert(std::is_base_of_v<std::ostream, OStream>);
294 return internal::ostream_output_stream(std::move(stream));
316 return internal::cfile_output_stream(fp,
true);
342 return internal::cfile_output_stream(fp,
false);
347template <
typename OutputStream>
348 requires output_stream<OutputStream>
351 std::shared_ptr<bucket_source<std::byte>> bsource;
352 std::size_t write_granularity;
354 std::uint64_t total_bytes_written = 0;
357 bucket<std::byte> buffer;
358 std::size_t bytes_buffered = 0;
360 void handle_span(std::span<std::byte const> event_span) {
361 auto first_block_size = write_granularity;
362 if (total_bytes_written == 0) {
367 std::optional<std::uint64_t> pos = strm.tell();
368 if (pos.has_value()) {
370 write_granularity - *pos % write_granularity;
374 if (bytes_buffered > 0 || first_block_size < write_granularity) {
375 auto const bytes_available =
376 std::min(bytes_buffered + event_span.size(), first_block_size);
378 buffer = bsource->bucket_of_size(write_granularity);
379 auto const dest_span =
380 buffer.first(bytes_available).subspan(bytes_buffered);
381 auto const src_span = event_span.first(
382 std::min(event_span.size(), dest_span.size()));
383 std::copy(src_span.begin(), src_span.end(), dest_span.begin());
384 if (bytes_available == first_block_size) {
385 strm.write(buffer.first(bytes_available));
389 throw input_output_error(
"failed to write output");
390 total_bytes_written += bytes_available;
392 bytes_buffered = bytes_available;
394 event_span = event_span.subspan(src_span.size());
397 auto const direct_write_size =
398 event_span.size() / write_granularity * write_granularity;
399 if (direct_write_size > 0) {
400 strm.write(event_span.first(direct_write_size));
402 throw input_output_error(
"failed to write output");
403 total_bytes_written += direct_write_size;
404 event_span = event_span.subspan(direct_write_size);
407 if (not event_span.empty()) {
408 buffer = bsource->bucket_of_size(write_granularity);
409 bytes_buffered = event_span.size();
410 std::copy(event_span.begin(), event_span.end(), buffer.begin());
415 explicit write_binary_stream(
417 std::shared_ptr<bucket_source<std::byte>> buffer_provider,
418 arg::granularity<std::size_t> granularity)
419 : strm(std::move(stream)), bsource(std::move(buffer_provider)),
420 write_granularity(granularity.value) {
422 throw std::invalid_argument(
423 "write_binary_stream buffer_provider must not be null");
424 if (write_granularity <= 0)
425 throw std::invalid_argument(
426 "write_binary_stream granularity must be positive");
429 [[nodiscard]]
auto introspect_node() const -> processor_info {
430 return processor_info(
this,
"write_binary_stream");
433 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
434 return processor_graph().push_entry_point(
this);
437 template <
typename Span,
438 typename = std::void_t<
439 decltype(std::span<std::byte const>(std::declval<Span>()))>>
440 void handle(Span
const &event) {
441 handle_span(std::span<std::byte const>(event));
445 if (bytes_buffered > 0) {
446 strm.write(std::span(buffer).first(bytes_buffered));
449 throw input_output_error(
"failed to write output");
508template <
typename OutputStream>
514 if constexpr (std::is_base_of_v<std::ostream, OutputStream>) {
516 return internal::write_binary_stream<decltype(wrapped)>(
517 std::move(wrapped), std::move(buffer_provider), granularity);
519 return internal::write_binary_stream<OutputStream>(
520 std::move(stream), std::move(buffer_provider), granularity);
Concept that is satisfied when T conforms to the libtcspc output stream interface.
Definition write_binary_stream.hpp:49
auto buffer(arg::threshold< std::size_t > threshold, access_tracker< buffer_accessor > &&tracker, Downstream downstream)
Create a processor that buffers events and emits them on a different thread.
Definition buffer.hpp:366
auto append(Event event, Downstream downstream)
Create a processor that inserts an event at the end of the stream.
Definition prepend_append.hpp:154
auto write_binary_stream(OutputStream stream, std::shared_ptr< bucket_source< std::byte > > buffer_provider, arg::granularity< std::size_t > granularity)
Create a sink that writes bytes to a binary stream, such as a file.
Definition write_binary_stream.hpp:509
auto binary_file_output_stream(std::string const &filename, arg::truncate< bool > truncate=arg::truncate{false}, arg::append< bool > append=arg::append{false})
Create a binary output stream for the given file.
Definition write_binary_stream.hpp:271
auto borrowed_cfile_output_stream(std::FILE *fp)
Create an output stream from a non-owned C file pointer.
Definition write_binary_stream.hpp:341
auto owning_cfile_output_stream(std::FILE *fp)
Create an output stream from a C file pointer, taking ownership.
Definition write_binary_stream.hpp:315
auto null_output_stream()
Create an output stream that discards all written bytes.
Definition write_binary_stream.hpp:250
auto ostream_output_stream(OStream stream)
Create an output stream from an std::ostream instance.
Definition write_binary_stream.hpp:292
libtcspc namespace.
Definition acquire.hpp:30
Function argument wrapper for granularity parameter.
Definition arg_wrappers.hpp:147
Function argument wrapper for truncate parameter.
Definition arg_wrappers.hpp:427
Abstract base class for polymorphic bucket sources.
Definition bucket.hpp:504