ios: Combine and simplify i/odiagstream

We need this for a rare use-case, so maintaining three flavors of
diagstream is too expensive. Furthermore, the buffering was removed
for simplicity.

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2023-07-25 16:31:52 +02:00
parent 516b428d4b
commit 6aa63f3bd6
2 changed files with 61 additions and 241 deletions

View File

@ -59,18 +59,17 @@ namespace UnitTests
TEST_METHOD(diagstream) TEST_METHOD(diagstream)
{ {
constexpr size_t n = 3; constexpr size_t n = 3;
unique_ptr<std::fstream> f[n];
vector<std::fstream*> fv(n);
{ {
unique_ptr<ofstream> f[n]; for (size_t i = 0; i < n; ++i) {
vector<ofstream*> fv(n);
for (size_t i = 0; i < n; ++i)
{
WCHAR path[MAX_PATH]; WCHAR path[MAX_PATH];
ExpandEnvironmentStringsW(stdex::sprintf(L"%%temp%%\\file%zu.dat", NULL, i).c_str(), path, _countof(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)); f[i].reset(new std::fstream(path, ios_base::out | ios_base::binary));
fv[i] = f[i].get(); fv[i] = f[i].get();
} }
stdex::odiagstream d(fv.begin(), fv.end(), 8); stdex::diagstream d(fv.begin(), fv.end());
srand(0); srand(0);
auto write_some_random = [](_Inout_ ostream& f, _In_ size_t amount) auto write_some_random = [](_Inout_ ostream& f, _In_ size_t amount)
{ {
@ -87,16 +86,13 @@ namespace UnitTests
} }
{ {
unique_ptr<ifstream> f[n]; for (size_t i = 0; i < n; ++i) {
vector<ifstream*> fv(n);
for (size_t i = 0; i < n; ++i)
{
WCHAR path[MAX_PATH]; WCHAR path[MAX_PATH];
ExpandEnvironmentStringsW(stdex::sprintf(L"%%temp%%\\file%zu.dat", NULL, i).c_str(), path, _countof(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)); f[i].reset(new std::fstream(path, ios_base::in | ios_base::binary));
fv[i] = f[i].get(); fv[i] = f[i].get();
} }
stdex::idiagstream d(fv.begin(), fv.end(), 8); stdex::diagstream d(fv.begin(), fv.end());
do { do {
uint32_t r; uint32_t r;
d.read(reinterpret_cast<char*>(&r), sizeof(r) / sizeof(char)); d.read(reinterpret_cast<char*>(&r), sizeof(r) / sizeof(char));

View File

@ -341,270 +341,95 @@ namespace stdex
/// Verifies multiple input streams read the same data. /// Verifies multiple input streams read the same data.
/// ///
template <class _Elem, class _Traits> template <class _Elem, class _Traits>
class basic_idiagstreambuf : public std::basic_streambuf<_Elem, _Traits> class basic_diagstreambuf : public std::basic_streambuf<_Elem, _Traits>
{ {
public: public:
using guest_stream = std::basic_istream<_Elem, _Traits>; using guest_stream = std::basic_iostream<_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<typename _Iter> template<typename _Iter>
basic_idiagstreambuf(_In_ const _Iter first, _In_ const _Iter last, _In_ size_t buf_size = 2048) : basic_diagstreambuf(_In_ const _Iter first, _In_ const _Iter last) : m_streams(first, last) {}
m_streams(first, last), basic_diagstreambuf(_In_reads_(count) guest_stream* const* streams, _In_ size_t count) : basic_diagstreambuf(streams, streams + count) {}
m_buf(buf_size)
{}
private: private:
basic_idiagstreambuf(_In_ const basic_idiagstreambuf<_Elem, _Traits>& other); basic_diagstreambuf(_In_ const basic_diagstreambuf<_Elem, _Traits>& other);
basic_idiagstreambuf<_Elem, _Traits>& operator =(_In_ const basic_idiagstreambuf<_Elem, _Traits>& other); basic_diagstreambuf<_Elem, _Traits>& operator =(_In_ const basic_diagstreambuf<_Elem, _Traits>& other);
basic_idiagstreambuf(_Inout_ basic_idiagstreambuf<_Elem, _Traits>&& other) noexcept; basic_diagstreambuf(_Inout_ basic_diagstreambuf<_Elem, _Traits>&& other) noexcept;
basic_idiagstreambuf<_Elem, _Traits>& operator =(_Inout_ basic_idiagstreambuf<_Elem, _Traits>&& other) noexcept; basic_diagstreambuf<_Elem, _Traits>& operator =(_Inout_ basic_diagstreambuf<_Elem, _Traits>&& other) noexcept;
protected: 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) 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()) { if (m_streams.empty())
setg(nullptr, nullptr, nullptr); return pos_type{ off_type{-1} };
auto r = pos_type{ off_type{-1} };
if ((which & std::ios_base::in)) {
m_streams[0]->seekg(off, way); m_streams[0]->seekg(off, way);
bool errors = m_streams[0]->bad(); r = m_streams[0]->bad() ? pos_type{ off_type{-1} } : m_streams[0]->tellg();
for (size_t i = 1, n = m_streams.size(); i < n; ++i) { for (size_t i = 1, n = m_streams.size(); i < n; ++i) {
m_streams[i]->seekg(off, way); m_streams[i]->seekg(off, way);
errors |= m_streams[i]->bad(); if (m_streams[i]->bad())
r = pos_type{ off_type{-1} };
} }
return errors ? pos_type{ off_type{-1} } : m_streams[0]->tellg();
} }
return pos_type{ off_type{-1} }; if ((which & std::ios_base::out)) {
m_streams[0]->seekp(off, way);
r = m_streams[0]->bad() ? pos_type{ off_type{-1} } : m_streams[0]->tellp();
for (size_t i = 1, n = m_streams.size(); i < n; ++i) {
m_streams[i]->seekp(off, way);
if (m_streams[i]->bad())
r = pos_type{ off_type{-1} };
}
}
return r;
} }
virtual pos_type 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)
{ {
if ((which & std::ios_base::in) && !m_streams.empty()) { return seekoff(pos, std::ios_base::beg, which);
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() virtual int_type underflow()
{ {
int_type eof = _Traits::eof();
if (m_streams.empty()) if (m_streams.empty())
goto error; eof;
size_t cap = m_buf.capacity(); _Elem data;
m_buf.resize(cap); m_streams[0]->read(&data, 1);
_Elem* data = m_buf.data(); int_type r = m_streams[0]->gcount() == 1 ? _Traits::to_int_type(data) : eof;
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) { for (size_t i = 1, n = m_streams.size(); i < n; ++i) {
m_streams[i]->read(temp_data, cap); m_streams[i]->read(&data, 1);
auto temp_read = m_streams[i]->gcount(); int_type temp_r = m_streams[i]->gcount() == 1 ? _Traits::to_int_type(data) : eof;
if (read != temp_read || !std::equal(data, data + read, temp_data)) if (r != temp_r)
goto error; r = eof;
} }
setg(data, data, data + read); return r;
return _Traits::to_int_type(m_buf.front());
error:
setg(nullptr, nullptr, nullptr);
return _Traits::eof();
}
protected:
std::vector<guest_stream*> m_streams;
std::vector<_Elem> m_buf, m_temp;
};
///
/// Diagnostic input stream
///
/// Verifies multiple input streams read the same data.
///
template <class _Elem, class _Traits>
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<typename _Iter>
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<char, std::char_traits<char>>;
using widiagstream = basic_idiagstream<wchar_t, std::char_traits<wchar_t>>;
///
/// Diagnostic output stream buffer
///
/// Writes to multiple output streams the same data.
///
template <class _Elem, class _Traits>
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<typename _Iter>
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()) virtual int_type overflow(int_type ch = _Traits::eof())
{ {
if (sync() < 0)
return _Traits::eof();
if (_Traits::not_eof(ch)) { if (_Traits::not_eof(ch)) {
m_buf.front() = _Traits::to_char_type(ch); _Elem data = _Traits::to_char_type(ch);
_Elem* data = m_buf.data(); bool good = true;
setp(data, data + 1, data + m_buf.size()); for (size_t i = 0, n = m_streams.size(); i < n; ++i) {
m_streams[i]->write(&data, 1);
good &= m_streams[i]->good();
}
return good ? 0 : _Traits::eof();
} }
return 0; return 0;
} }
virtual int sync() virtual int sync()
{ {
_Elem* data = m_buf.data(); int r = 0;
size_t size = pptr() - pbase(); for (size_t i = 0, n = m_streams.size(); i < n; ++i)
if (!size) if (m_streams[i]->sync() < 0)
return 0; r = -1;
bool errors = false; return r;
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: protected:
std::vector<guest_stream*> m_streams; std::vector<guest_stream*> m_streams;
std::vector<_Elem> m_buf;
};
///
/// Diagnostic output stream
///
/// Writes to multiple output streams the same data.
///
template <class _Elem, class _Traits>
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<typename _Iter>
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<char, std::char_traits<char>>;
using wodiagstream = basic_odiagstream<wchar_t, std::char_traits<wchar_t>>;
///
/// Diagnostic input/output stream buffer
///
/// Verifies multiple input streams read the same data.
/// Writes to multiple output streams the same data.
///
template <class _Elem, class _Traits>
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<typename _Iter>
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)
{}
}; };
/// ///
@ -619,15 +444,14 @@ namespace stdex
public: public:
using guest_stream = std::basic_iostream<_Elem, _Traits>; 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) : template<typename _Iter>
m_buf(streams, count, buf_sizeg, buf_sizep), basic_diagstream(_In_ const _Iter first, _In_ const _Iter last) :
m_buf(first, last),
std::basic_iostream<_Elem, _Traits>(&m_buf) std::basic_iostream<_Elem, _Traits>(&m_buf)
{} {}
template<typename _Iter> basic_diagstream(_In_reads_(count) guest_stream* const* streams, _In_ size_t count) :
basic_diagstream(_In_ const _Iter first, _In_ const _Iter last, _In_ size_t buf_sizeg = 2048, _In_ size_t buf_sizep = 2048) : basic_diagstream(streams, streams + count)
m_buf(first, last, buf_sizeg, buf_sizep),
std::basic_iostream<_Elem, _Traits>(&m_buf)
{} {}
protected: protected: