.. _program_listing_file_src_rdf4cpp_writer_BufWriter.hpp: Program Listing for File BufWriter.hpp ====================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/rdf4cpp/writer/BufWriter.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #ifndef RDF4CPP_BUFWRITER_HPP #define RDF4CPP_BUFWRITER_HPP #include #include #include #include #include #include #include namespace rdf4cpp::writer { template concept BufWriter = requires (W &bw, size_t additional_cap) { typename W::Buffer; { bw.buffer() } -> std::same_as; { bw.write_area() } -> std::same_as; { bw.write_area_size() } -> std::same_as; { bw.finalize() } -> std::convertible_to; W::flush(&bw.buffer(), &bw.write_area(), &bw.write_area_size(), additional_cap); }; using FlushFunc = void (*)(void *buffer, char **write_area, size_t *write_area_size, size_t additional_cap); struct BufWriterParts { void *buffer; //< pointer to arbitrary buffer structure char **write_area; //< pointer to free space size_t *write_area_size; //< size of free space FlushFunc flush; //< function to flush the contents of buffer and update cursor to point to the new free space BufWriterParts() = delete; BufWriterParts(BufWriterParts const &) noexcept = default; BufWriterParts(BufWriterParts &&) noexcept = default; BufWriterParts &operator=(BufWriterParts const &) noexcept = default; BufWriterParts &operator=(BufWriterParts &&) noexcept = default; ~BufWriterParts() noexcept = default; template BufWriterParts(W &w) noexcept : buffer{&w.buffer()}, write_area{&w.write_area()}, write_area_size{&w.write_area_size()}, flush{&W::flush} { } BufWriterParts(void *buffer, char **cursor, size_t *remaining_size, FlushFunc flush) noexcept : buffer{buffer}, write_area{cursor}, write_area_size{remaining_size}, flush{flush} { } }; inline bool write_str(std::string_view str, BufWriterParts const writer) noexcept { if (str.empty()) [[unlikely]] { // An empty std::string_view may have str.data() == nullptr // Calling memcpy() with a nullptr is undefined behavior **even if size == 0** return true; } while (true) { auto const max_write = std::min(str.size(), *writer.write_area_size); memcpy(*writer.write_area, str.data(), max_write); *writer.write_area += max_write; *writer.write_area_size -= max_write; if (max_write == str.size()) [[likely]] { break; } str.remove_prefix(max_write); if (writer.flush(writer.buffer, writer.write_area, writer.write_area_size, str.size()); *writer.write_area_size == 0) [[unlikely]] { return false; } } return true; } template struct BufWriterBase { private: Buffer buffer_; char *write_area_ = nullptr; size_t write_area_size_ = 0; public: template explicit constexpr BufWriterBase(BufferArgs &&...buffer_args) : buffer_{std::forward(buffer_args)...} { } [[nodiscard]] constexpr char *&write_area() noexcept { return write_area_; } [[nodiscard]] constexpr size_t &write_area_size() noexcept { return write_area_size_; } [[nodiscard]] constexpr Buffer &buffer() noexcept { return buffer_; } [[nodiscard]] constexpr char *write_area() const noexcept { return write_area_; } [[nodiscard]] constexpr size_t write_area_size() const noexcept { return write_area_size_; } [[nodiscard]] constexpr Buffer const &buffer() const noexcept { return buffer_; } static void flush(void *buffer, char **write_area, size_t *write_area_size, size_t additional_cap) noexcept { CRTP::flush_impl(*static_cast(buffer), *write_area, *write_area_size, additional_cap); } }; struct StringBuffer { std::string *buffer_; explicit StringBuffer(std::string &buffer) noexcept : buffer_{&buffer} { } }; struct StringWriter : BufWriterBase { using Buffer = StringBuffer; constexpr StringWriter(std::string &buf) noexcept : BufWriterBase{buf} { clear(); } bool finalize() noexcept { buffer().buffer_->resize(static_cast(write_area() - buffer().buffer_->data())); return true; } [[nodiscard]] std::string_view view() const noexcept { return std::string_view{buffer().buffer_->data(), static_cast(write_area() - buffer().buffer_->data())}; } constexpr void clear() noexcept { write_area() = buffer().buffer_->data(); write_area_size() = buffer().buffer_->size(); } static void flush_impl(Buffer &buffer, char *&write_area, size_t &write_area_size, size_t additional_cap) noexcept { auto const bytes_filled = write_area - buffer.buffer_->data(); buffer.buffer_->resize(std::bit_ceil(buffer.buffer_->size() + additional_cap)); write_area = buffer.buffer_->data() + bytes_filled; write_area_size = buffer.buffer_->size() - bytes_filled; } template static std::string oneshot(F &&f) requires std::is_invocable_r_v(f)), StringWriter &> { std::string s; s.resize(32); StringWriter w{s}; if (!std::invoke(std::forward(f), w)) { throw std::runtime_error{"rdf4cpp::writer::StringWriter::oneshot failed"}; } w.finalize(); // cannot fail return s; } }; struct CFileBuffer { FILE *file_; std::array buffer_; explicit constexpr CFileBuffer(FILE *file) noexcept : file_{file} {} }; struct BufCFileWriter : BufWriterBase { using Buffer = CFileBuffer; explicit constexpr BufCFileWriter(FILE *file) noexcept : BufWriterBase{file} { write_area() = buffer().buffer_.data(); write_area_size() = buffer().buffer_.size(); } bool finalize() { auto const to_write = static_cast(write_area() - buffer().buffer_.data()); return fwrite(buffer().buffer_.data(), 1, to_write, buffer().file_) == to_write; } static void flush_impl(Buffer &buffer, char *&write_area, size_t &write_area_size, [[maybe_unused]] size_t additional_cap) noexcept { auto const bytes_flushed = fwrite(buffer.buffer_.data(), 1, static_cast(write_area - buffer.buffer_.data()), buffer.file_); write_area -= bytes_flushed; write_area_size += bytes_flushed; } }; struct OStreamBuffer { std::ostream *os_; std::array buffer_; explicit constexpr OStreamBuffer(std::ostream &os) noexcept : os_{&os} {} }; struct BufOStreamWriter : BufWriterBase { using Buffer = OStreamBuffer; explicit constexpr BufOStreamWriter(std::ostream &os) noexcept : BufWriterBase{os} { write_area() = buffer().buffer_.data(); write_area_size() = buffer().buffer_.size(); } bool finalize() { return static_cast(buffer().os_->write(buffer().buffer_.data(), static_cast(write_area() - buffer().buffer_.data()))); } static void flush_impl(Buffer &buffer, char *&write_area, size_t &write_area_size, [[maybe_unused]] size_t additional_cap) noexcept { if (!buffer.os_->write(buffer.buffer_.data(), static_cast(write_area - buffer.buffer_.data()))) { return; } write_area = buffer.buffer_.data(); write_area_size = buffer.buffer_.size(); } }; template OutIter> struct OutputIteratorBuffer { OutIter iter; std::array buffer_{}; explicit constexpr OutputIteratorBuffer(OutIter i) noexcept : iter(i) { } void write_out(char const *end) { char const *b = buffer_.data(); RDF4CPP_ASSERT(b <= end && end <= buffer_.end()); while (b != end) { *iter = *b; ++iter; ++b; } } }; template OutIter> struct BufOutputIteratorWriter : BufWriterBase, OutputIteratorBuffer> { using Buffer = OutputIteratorBuffer; explicit constexpr BufOutputIteratorWriter(OutIter i) noexcept : BufWriterBase, OutputIteratorBuffer>{i} { this->write_area() = this->buffer().buffer_.data(); this->write_area_size() = this->buffer().buffer_.size(); } bool finalize() { this->buffer().write_out(this->write_area()); return true; } static void flush_impl(Buffer &buffer, char *&write_area, size_t &write_area_size, [[maybe_unused]] size_t additional_cap) noexcept { buffer.write_out(write_area); write_area = buffer.buffer_.data(); write_area_size = buffer.buffer_.size(); } }; } // namespace rdf4cpp::writer #endif // RDF4CPP_BUFWRITER_HPP