// license:GPL-2.0+ // copyright-holders:Couriersud #ifndef PSTREAM_H_ #define PSTREAM_H_ /// /// \file pstream.h /// #include "pconfig.h" #include "pfmtlog.h" #include "pgsl.h" #include "pstring.h" #include #include #include #include #include #include #include #include namespace plib { /// \brief wrapper around isteam read /// template static S & istream_read(S &is, T * data, size_t len) { using ct = typename S::char_type; static_assert((sizeof(T) % sizeof(ct)) == 0, "istream_read sizeof issue"); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) return is.read(reinterpret_cast(data), gsl::narrow(len * sizeof(T))); } /// \brief wrapper around osteam write /// template static S & ostream_write(S &os, const T * data, size_t len) { using ct = typename S::char_type; static_assert((sizeof(T) % sizeof(ct)) == 0, "ostream_write sizeof issue"); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) return os.write(reinterpret_cast(data), gsl::narrow(len * sizeof(T))); } /// \brief a named istream pointer container /// /// This moveable object allows to pass istream unique pointers with /// information about the origin (filename). This is useful in error /// reporting where the source of the stream has to be logged. /// struct istream_uptr { explicit istream_uptr() = default; istream_uptr(std::unique_ptr &&strm, const pstring &filename) : m_strm(std::move(strm)) , m_filename(filename) { } istream_uptr(const istream_uptr &) = delete; istream_uptr &operator=(const istream_uptr &) = delete; istream_uptr(istream_uptr &&rhs) { m_strm = std::move(rhs.m_strm); m_filename = std::move(rhs.m_filename); } istream_uptr &operator=(istream_uptr &&) /*noexcept*/ = delete; ~istream_uptr() = default; std::istream * operator ->() noexcept { return m_strm.get(); } std::istream & operator *() noexcept { return *m_strm; } pstring filename() { return m_filename; } bool empty() { return m_strm == nullptr; } // FIXME: workaround input context should accept stream_ptr std::unique_ptr release_stream() { return std::move(m_strm); } private: std::unique_ptr m_strm; pstring m_filename; }; /// /// /// putf8reader_t digests linux & dos/windows text files /// // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) class putf8_reader { public: PCOPYASSIGN(putf8_reader, delete) virtual ~putf8_reader() = default; putf8_reader(putf8_reader &&rhs) noexcept : m_strm(std::move(rhs.m_strm)) { } putf8_reader(std::unique_ptr &&rhs) noexcept : m_strm(std::move(rhs)) { // no bad surprises m_strm->imbue(std::locale::classic()); } bool eof() const { return m_strm->eof(); } /// \brief Read a line of UTF8 characters from the stream. /// /// The line will not contain a trailing linefeed /// /// \param line pstring reference to the result /// \returns Returns false if at end of file /// bool readline(putf8string &line) { putf8string::code_t c = 0; line = ""; if (!this->readcode(c)) { return false; } while (true) { if (c == 10) break; if (c != 13) // ignore CR line += putf8string(1, c); if (!this->readcode(c)) break; } return true; } /// \brief Read a line of UTF8 characters from the stream including trailing linefeed. /// /// The line will contain the trailing linefeed /// /// \param line pstring reference to the result /// \returns Returns false if at end of file /// bool readline_lf(putf8string &line) { putf8string::code_t c = 0; line = ""; if (!this->readcode(c)) { return false; } while (true) { if (c != 13) // ignore CR line += putf8string(1, c); if (c == 10) break; if (!this->readcode(c)) break; } return true; } bool readbyte(std::istream::char_type &b) { if (m_strm->eof()) return false; m_strm->read(&b, 1); return (!m_strm->eof()); } bool readcode(putf8string::traits_type::code_t &c) { std::array b{0}; if (m_strm->eof()) return false; m_strm->read(&b[0], 1); if (m_strm->eof()) return false; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) const std::size_t l = putf8string::traits_type::codelen(reinterpret_cast(&b)); for (std::size_t i = 1; i < l; i++) { m_strm->read(&b[i], 1); if (m_strm->eof()) return false; } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) c = putf8string::traits_type::code(reinterpret_cast(&b)); return true; } std::istream &stream() { return *m_strm; } private: std::unique_ptr m_strm; }; // ----------------------------------------------------------------------------- // putf8writer_t: writer on top of ostream // ----------------------------------------------------------------------------- class putf8_writer { public: explicit putf8_writer(std::ostream *strm) : m_strm(strm) {} putf8_writer(putf8_writer &&src) noexcept : m_strm(src.m_strm) {} PCOPYASSIGN(putf8_writer, delete) putf8_writer &operator=(putf8_writer &&src) = delete; virtual ~putf8_writer() = default; void writeline(const pstring &line) const { write(line); write(10); } void write(const pstring &text) const { // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) const putf8string conv_utf8(text); //m_strm->write(conv_utf8.c_str(), static_cast(plib::strlen(conv_utf8.c_str() ))); ostream_write(*m_strm, conv_utf8.c_str(), conv_utf8.size()); } void write(const pstring::value_type c) const { pstring t(1,c); write(t); } void flush() { m_strm->flush(); } private: std::ostream *m_strm; }; class putf8_fmt_writer : public pfmt_writer_t, public putf8_writer { public: explicit putf8_fmt_writer(std::ostream *strm) : putf8_writer(strm) { } PCOPYASSIGNMOVE(putf8_fmt_writer, delete) ~putf8_fmt_writer() override = default; //protected: void vdowrite(const pstring &s) const { write(s); } private: }; // ----------------------------------------------------------------------------- // pbinary_writer_t: writer on top of ostream // ----------------------------------------------------------------------------- class pbinary_writer { public: explicit pbinary_writer(std::ostream &strm) : m_strm(strm) {} pbinary_writer(pbinary_writer &&src) noexcept : m_strm(src.m_strm) {} PCOPYASSIGN(pbinary_writer, delete) pbinary_writer &operator=(pbinary_writer &&src) = delete; virtual ~pbinary_writer() = default; template void write(const T &val) { ostream_write(m_strm, &val, 1); } void write(const pstring &s) { const auto *sm = s.c_str(); //const auto sl(std::char_traits::length(sm)); const auto sl(s.size()); write(sl); ostream_write(m_strm, sm, sl); } template void write(const std::vector &val) { const auto sz(val.size()); write(sz); ostream_write(m_strm, val.data(), sz); } private: std::ostream &m_strm; }; class pbinary_reader { public: explicit pbinary_reader(std::istream &strm) : m_strm(strm) {} pbinary_reader(pbinary_reader &&src) noexcept : m_strm(src.m_strm) { } PCOPYASSIGN(pbinary_reader, delete) pbinary_reader &operator=(pbinary_reader &&src) = delete; virtual ~pbinary_reader() = default; template void read(T &val) { istream_read(m_strm, &val, 1); } void read( pstring &s) { std::size_t sz = 0; read(sz); std::vector::mem_t> buf(sz+1); m_strm.read(buf.data(), static_cast(sz)); buf[sz] = 0; s = pstring(buf.data()); } template void read(std::vector &val) { std::size_t sz = 0; read(sz); val.resize(sz); istream_read(m_strm, val.data(), sz); } private: std::istream &m_strm; }; inline void copystream(std::ostream &dest, std::istream &src) { // FIXME: optimize std::array buf; // NOLINT(cppcoreguidelines-pro-type-member-init) while (!src.eof()) { src.read(buf.data(), 1); dest.write(buf.data(), 1); } } /// /// \brief utf8 filename aware ifstream wrapper /// class ifstream : public std::ifstream { public: using filename_type = std::conditional=900), pstring_t, pstring_t>::type; template explicit ifstream(const pstring_t &name, ios_base::openmode mode = ios_base::in) : std::ifstream(filename_type(name).c_str(), mode) { } explicit ifstream(const std::string &name, ios_base::openmode mode = ios_base::in) : std::ifstream(filename_type(putf8string(name)).c_str(), mode) { } }; /// /// \brief utf8 filename aware ofstream wrapper /// class ofstream : public std::ofstream { public: using filename_type = std::conditional=900), pstring_t, pstring_t>::type; template explicit ofstream(const pstring_t &name, ios_base::openmode mode = ios_base::out | ios_base::trunc) : std::ofstream(filename_type(name).c_str(), mode) { } explicit ofstream(const std::string &name, ios_base::openmode mode = ios_base::out | ios_base::trunc) : std::ofstream(filename_type(putf8string(name)).c_str(), mode) { } }; struct perrlogger { template explicit perrlogger(Args&& ... args) { h()(std::forward(args)...); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay) } private: static putf8_fmt_writer &h() { static plib::putf8_fmt_writer perr(&std::cerr); return perr; } }; // ----------------------------------------------------------------------------- // c++17 preparation // ----------------------------------------------------------------------------- namespace filesystem { // FIXME: u8path should return a path object (c++17) template< class Source > pstring u8path( const Source& source ) { return source; } } // namespace filesystem } // namespace plib #endif // PSTREAM_H_