From 516b428d4b8c75711cc08e4fb28d2eb7b126bc04 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Tue, 25 Jul 2023 12:38:20 +0200 Subject: [PATCH] ios: Add diagstreambuf Signed-off-by: Simon Rozman --- UnitTests/ios.cpp | 48 +++++++ UnitTests/pch.h | 2 + include/stdex/ios.hpp | 314 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 359 insertions(+), 5 deletions(-) diff --git a/UnitTests/ios.cpp b/UnitTests/ios.cpp index ff54c08a7..53bc1ad90 100644 --- a/UnitTests/ios.cpp +++ b/UnitTests/ios.cpp @@ -55,5 +55,53 @@ namespace UnitTests Assert::IsTrue(f.good()); Assert::IsTrue(val == data[_countof(data) - 2]); } + + TEST_METHOD(diagstream) + { + constexpr size_t n = 3; + + { + unique_ptr f[n]; + vector fv(n); + for (size_t i = 0; i < n; ++i) + { + WCHAR path[MAX_PATH]; + ExpandEnvironmentStringsW(stdex::sprintf(L"%%temp%%\\file%zu.dat", NULL, i).c_str(), path, _countof(path)); + f[i].reset(new ofstream(path, ios_base::out | ios_base::binary)); + fv[i] = f[i].get(); + } + stdex::odiagstream d(fv.begin(), fv.end(), 8); + srand(0); + auto write_some_random = [](_Inout_ ostream& f, _In_ size_t amount) + { + for (size_t i = 0; i < amount; ++i) { + auto r = static_cast(rand()); + f.write(reinterpret_cast(&r), sizeof(r) / sizeof(char)); + } + }; + write_some_random(d, 15); + d.seekp(3); + write_some_random(d, 2); + d.seekp(28); + write_some_random(d, 7); + } + + { + unique_ptr f[n]; + vector fv(n); + for (size_t i = 0; i < n; ++i) + { + WCHAR path[MAX_PATH]; + ExpandEnvironmentStringsW(stdex::sprintf(L"%%temp%%\\file%zu.dat", NULL, i).c_str(), path, _countof(path)); + f[i].reset(new ifstream(path, ios_base::in | ios_base::binary)); + fv[i] = f[i].get(); + } + stdex::idiagstream d(fv.begin(), fv.end(), 8); + do { + uint32_t r; + d.read(reinterpret_cast(&r), sizeof(r) / sizeof(char)); + } while (!d.eof()); + } + } }; } diff --git a/UnitTests/pch.h b/UnitTests/pch.h index 16c07ad16..6016f700e 100644 --- a/UnitTests/pch.h +++ b/UnitTests/pch.h @@ -24,3 +24,5 @@ #include #include + +#include diff --git a/include/stdex/ios.hpp b/include/stdex/ios.hpp index 88e2fb6a5..a6f2bfaea 100644 --- a/include/stdex/ios.hpp +++ b/include/stdex/ios.hpp @@ -15,10 +15,13 @@ #include #include #include +#include #include #include +#include #include #include +#include namespace stdex { @@ -257,7 +260,7 @@ namespace stdex using wiostreamfmt = basic_iostreamfmt>; /// - /// Shared-memory string buffer + /// Shared-memory string stream buffer /// template class basic_sharedstrbuf : public std::basic_streambuf<_Elem, _Traits> @@ -265,12 +268,12 @@ namespace stdex public: basic_sharedstrbuf(_In_reads_(size) const _Elem* data, _In_ size_t size) { - std::basic_streambuf<_Elem, _Traits>::setg(const_cast<_Elem*>(data), const_cast<_Elem*>(data), const_cast<_Elem*>(data + size)); + setg(const_cast<_Elem*>(data), const_cast<_Elem*>(data), const_cast<_Elem*>(data + size)); } basic_sharedstrbuf(_In_ const basic_sharedstrbuf<_Elem, _Traits>& other) { - std::basic_streambuf<_Elem, _Traits>::setg(other.eback(), other.gptr(), other.egptr()); + setg(other.eback(), other.gptr(), other.egptr()); } basic_sharedstrbuf<_Elem, _Traits>& operator =(_In_ const basic_sharedstrbuf<_Elem, _Traits>& other) @@ -303,9 +306,8 @@ namespace stdex return pos_type{ off_type{-1} }; } - virtual pos_type __CLR_OR_THIS_CALL seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) + virtual pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) { - // change to specified position, according to mode if (which & std::ios_base::in) { _Elem* target = eback() + static_cast(pos); if (eback() <= target && target <= egptr()) { @@ -333,6 +335,308 @@ namespace stdex using isharedstrstream = basic_isharedstrstream>; using wisharedstrstream = basic_isharedstrstream>; + /// + /// Diagnostic input stream buffer + /// + /// Verifies multiple input streams read the same data. + /// + template + class basic_idiagstreambuf : public std::basic_streambuf<_Elem, _Traits> + { + public: + using guest_stream = std::basic_istream<_Elem, _Traits>; + + basic_idiagstreambuf(_In_reads_(count) const guest_stream** streams, _In_ size_t count, _In_ size_t buf_size = 2048) : + m_streams(stream, count), + m_buf(buf_size) + {} + + template + basic_idiagstreambuf(_In_ const _Iter first, _In_ const _Iter last, _In_ size_t buf_size = 2048) : + m_streams(first, last), + m_buf(buf_size) + {} + + private: + basic_idiagstreambuf(_In_ const basic_idiagstreambuf<_Elem, _Traits>& other); + basic_idiagstreambuf<_Elem, _Traits>& operator =(_In_ const basic_idiagstreambuf<_Elem, _Traits>& other); + basic_idiagstreambuf(_Inout_ basic_idiagstreambuf<_Elem, _Traits>&& other) noexcept; + basic_idiagstreambuf<_Elem, _Traits>& operator =(_Inout_ basic_idiagstreambuf<_Elem, _Traits>&& other) noexcept; + + protected: + virtual pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) + { + if ((which & std::ios_base::in) && !m_streams.empty()) { + setg(nullptr, nullptr, nullptr); + m_streams[0]->seekg(off, way); + bool errors = m_streams[0]->bad(); + for (size_t i = 1, n = m_streams.size(); i < n; ++i) { + m_streams[i]->seekg(off, way); + errors |= m_streams[i]->bad(); + } + return errors ? pos_type{ off_type{-1} } : m_streams[0]->tellg(); + } + return pos_type{ off_type{-1} }; + } + + virtual pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) + { + if ((which & std::ios_base::in) && !m_streams.empty()) { + setg(nullptr, nullptr, nullptr); + m_streams[0]->seekg(pos); + bool errors = m_streams[0]->bad(); + for (size_t i = 1, n = m_streams.size(); i < n; ++i) { + m_streams[i]->seekg(pos); + errors |= m_streams[i]->bad(); + } + return errors ? pos_type{ off_type{-1} } : m_streams[0]->tellg(); + } + return pos_type{ off_type{-1} }; + } + + virtual int_type underflow() + { + if (m_streams.empty()) + goto error; + size_t cap = m_buf.capacity(); + m_buf.resize(cap); + _Elem* data = m_buf.data(); + m_streams[0]->read(data, cap); + auto read = m_streams[0]->gcount(); + if (!read) + goto error; + m_temp.resize(cap); + _Elem* temp_data = m_temp.data(); + for (size_t i = 1, n = m_streams.size(); i < n; ++i) { + m_streams[i]->read(temp_data, cap); + auto temp_read = m_streams[i]->gcount(); + if (read != temp_read || !std::equal(data, data + read, temp_data)) + goto error; + } + setg(data, data, data + read); + return _Traits::to_int_type(m_buf.front()); + + error: + setg(nullptr, nullptr, nullptr); + return _Traits::eof(); + } + + protected: + std::vector m_streams; + std::vector<_Elem> m_buf, m_temp; + }; + + /// + /// Diagnostic input stream + /// + /// Verifies multiple input streams read the same data. + /// + template + class basic_idiagstream : public std::basic_istream<_Elem, _Traits> + { + public: + using guest_stream = std::basic_istream<_Elem, _Traits>; + + basic_idiagstream(_In_reads_(count) const guest_stream** streams, _In_ size_t count, _In_ size_t buf_size = 2048) : + m_buf(streams, count, buf_size), + std::basic_istream<_Elem, _Traits>(&m_buf) + {} + + template + basic_idiagstream(_In_ const _Iter first, _In_ const _Iter last, _In_ size_t buf_size = 2048) : + m_buf(first, last, buf_size), + std::basic_istream<_Elem, _Traits>(&m_buf) + {} + + protected: + basic_idiagstreambuf<_Elem, _Traits> m_buf; + }; + + using idiagstream = basic_idiagstream>; + using widiagstream = basic_idiagstream>; + + /// + /// Diagnostic output stream buffer + /// + /// Writes to multiple output streams the same data. + /// + template + class basic_odiagstreambuf : public std::basic_streambuf<_Elem, _Traits> + { + public: + using guest_stream = std::basic_ostream<_Elem, _Traits>; + + basic_odiagstreambuf(_In_reads_(count) const guest_stream** streams, _In_ size_t count, _In_ size_t buf_size = 2048) : + m_streams(stream, count), + m_buf(buf_size) + { + setp(m_buf.data(), m_buf.data(), m_buf.data() + m_buf.size()); + } + + template + basic_odiagstreambuf(_In_ const _Iter first, _In_ const _Iter last, _In_ size_t buf_size = 2048) : + m_streams(first, last), + m_buf(buf_size) + {} + + private: + basic_odiagstreambuf(_In_ const basic_odiagstreambuf<_Elem, _Traits>& other); + basic_odiagstreambuf<_Elem, _Traits>& operator =(_In_ const basic_odiagstreambuf<_Elem, _Traits>& other); + basic_odiagstreambuf(_Inout_ basic_odiagstreambuf<_Elem, _Traits>&& other) noexcept; + basic_odiagstreambuf<_Elem, _Traits>& operator =(_Inout_ basic_odiagstreambuf<_Elem, _Traits>&& other) noexcept; + + protected: + virtual pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) + { + if ((which & std::ios_base::out) && !m_streams.empty()) { + if (sync() < 0) + return pos_type{ off_type{-1} }; + m_streams[0]->seekp(off, way); + bool errors = m_streams[0]->bad(); + for (size_t i = 1, n = m_streams.size(); i < n; ++i) { + m_streams[i]->seekp(off, way); + errors |= m_streams[i]->bad(); + } + return errors ? pos_type{ off_type{-1} } : m_streams[0]->tellp(); + } + return pos_type{ off_type{-1} }; + } + + virtual pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) + { + if ((which & std::ios_base::out) && !m_streams.empty()) { + if (sync() < 0) + return pos_type{ off_type{-1} }; + m_streams[0]->seekp(pos); + bool errors = m_streams[0]->bad(); + for (size_t i = 1, n = m_streams.size(); i < n; ++i) { + m_streams[i]->seekp(pos); + errors |= m_streams[i]->bad(); + } + return errors ? pos_type{ off_type{-1} } : m_streams[0]->tellp(); + } + return pos_type{ off_type{-1} }; + } + + virtual int_type overflow(int_type ch = _Traits::eof()) + { + if (sync() < 0) + return _Traits::eof(); + if (_Traits::not_eof(ch)) { + m_buf.front() = _Traits::to_char_type(ch); + _Elem* data = m_buf.data(); + setp(data, data + 1, data + m_buf.size()); + } + return 0; + } + + virtual int sync() + { + _Elem* data = m_buf.data(); + size_t size = pptr() - pbase(); + if (!size) + return 0; + bool errors = false; + for (size_t i = 0, n = m_streams.size(); i < n; ++i) { + try { m_streams[i]->write(data, size); } + catch (std::exception) { errors = true; } + if (!m_streams[i]->good()) + errors = true; + } + setp(data, data, data + m_buf.size()); + return errors ? -1 : 0; + } + + protected: + std::vector m_streams; + std::vector<_Elem> m_buf; + }; + + /// + /// Diagnostic output stream + /// + /// Writes to multiple output streams the same data. + /// + template + class basic_odiagstream : public std::basic_ostream<_Elem, _Traits> + { + public: + using guest_stream = std::basic_ostream<_Elem, _Traits>; + + basic_odiagstream(_In_reads_(count) const guest_stream** streams, _In_ size_t count, _In_ size_t buf_size = 2048) : + m_buf(streams, count, buf_size), + std::basic_ostream<_Elem, _Traits>(&m_buf) + {} + + template + basic_odiagstream(_In_ const _Iter first, _In_ const _Iter last, _In_ size_t buf_size = 2048) : + m_buf(first, last, buf_size), + std::basic_ostream<_Elem, _Traits>(&m_buf) + {} + + protected: + basic_odiagstreambuf<_Elem, _Traits> m_buf; + }; + + using odiagstream = basic_odiagstream>; + using wodiagstream = basic_odiagstream>; + + /// + /// Diagnostic input/output stream buffer + /// + /// Verifies multiple input streams read the same data. + /// Writes to multiple output streams the same data. + /// + template + class basic_diagstreambuf : + public basic_idiagstreambuf<_Elem, _Traits>, + public basic_odiagstreambuf<_Elem, _Traits> + { + public: + using guest_stream = std::basic_iostream<_Elem, _Traits>; + + basic_diagstreambuf(_In_reads_(count) const guest_stream** streams, _In_ size_t count, _In_ size_t buf_sizeg = 2048, _In_ size_t buf_sizep = 2048) : + basic_idiagstreambuf<_Elem, _Traits>(streams, count, buf_sizeg), + basic_odiagstreambuf<_Elem, _Traits>(streams, count, buf_sizep) + {} + + template + basic_diagstreambuf(_In_ const _Iter first, _In_ const _Iter last, _In_ size_t buf_sizeg = 2048, _In_ size_t buf_sizep = 2048) : + basic_idiagstreambuf<_Elem, _Traits>(first, last, buf_sizeg), + basic_odiagstreambuf<_Elem, _Traits>(first, last, buf_sizep) + {} + }; + + /// + /// Diagnostic input/output stream + /// + /// Verifies multiple input streams read the same data. + /// Writes to multiple output streams the same data. + /// + template + class basic_diagstream : public std::basic_iostream<_Elem, _Traits> + { + public: + using guest_stream = std::basic_iostream<_Elem, _Traits>; + + basic_diagstream(_In_reads_(count) const guest_stream** streams, _In_ size_t count, _In_ size_t buf_sizeg = 2048, _In_ size_t buf_sizep = 2048) : + m_buf(streams, count, buf_sizeg, buf_sizep), + std::basic_iostream<_Elem, _Traits>(&m_buf) + {} + + template + basic_diagstream(_In_ const _Iter first, _In_ const _Iter last, _In_ size_t buf_sizeg = 2048, _In_ size_t buf_sizep = 2048) : + m_buf(first, last, buf_sizeg, buf_sizep), + std::basic_iostream<_Elem, _Traits>(&m_buf) + {} + + protected: + basic_diagstreambuf<_Elem, _Traits> m_buf; + }; + + using diagstream = basic_diagstream>; + using wdiagstream = basic_diagstream>; + #ifdef _WIN32 /// \cond internal template struct robber, &std::filebuf::_Myfile>;