diff --git a/UnitTests/UnitTests.vcxproj b/UnitTests/UnitTests.vcxproj
index 4570edd78..b6cd49e05 100644
--- a/UnitTests/UnitTests.vcxproj
+++ b/UnitTests/UnitTests.vcxproj
@@ -115,6 +115,7 @@
+
Create
diff --git a/UnitTests/UnitTests.vcxproj.filters b/UnitTests/UnitTests.vcxproj.filters
index a2ef46a40..05a4e21ce 100644
--- a/UnitTests/UnitTests.vcxproj.filters
+++ b/UnitTests/UnitTests.vcxproj.filters
@@ -24,6 +24,9 @@
Source Files
+
+ Source Files
+
diff --git a/UnitTests/ios.cpp b/UnitTests/ios.cpp
new file mode 100644
index 000000000..ff54c08a7
--- /dev/null
+++ b/UnitTests/ios.cpp
@@ -0,0 +1,59 @@
+/*
+ SPDX-License-Identifier: MIT
+ Copyright © 2023 Amebis
+*/
+
+#include "pch.h"
+
+using namespace std;
+using namespace stdex;
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+namespace UnitTests
+{
+ TEST_CLASS(ios)
+ {
+ public:
+ TEST_METHOD(fstream)
+ {
+ WCHAR path[MAX_PATH];
+ ExpandEnvironmentStringsW(L"%windir%\\notepad.exe", path, _countof(path));
+ stdex::fstream f(path, ios_base::in | ios_base::binary);
+ Assert::IsTrue(f.good());
+ Assert::IsTrue(f.mtime() < chrono::system_clock::now());
+ }
+
+ TEST_METHOD(isharedstrstream)
+ {
+ static const char data[] = "\xde\xad\xca\xfe";
+ stdex::isharedstrstream f(data, _countof(data));
+ Assert::IsTrue(f.good());
+
+ char val;
+ f.read(&val, 1);
+ Assert::IsTrue(f.gcount() == 1);
+ Assert::IsTrue(f.good());
+ Assert::IsTrue(val == data[0]);
+ f.read(&val, 1);
+ Assert::IsTrue(f.gcount() == 1);
+ Assert::IsTrue(f.good());
+ Assert::IsTrue(val == data[1]);
+
+ f.seekg(_countof(data) - 1);
+ f.read(&val, 1);
+ Assert::IsTrue(f.gcount() == 1);
+ Assert::IsTrue(f.good());
+ Assert::IsTrue(val == data[_countof(data) - 1]);
+
+ f.read(&val, 1);
+ Assert::IsTrue(f.eof());
+ f.clear();
+
+ f.seekg(-2, ios_base::end);
+ f.read(&val, 1);
+ Assert::IsTrue(f.gcount() == 1);
+ Assert::IsTrue(f.good());
+ Assert::IsTrue(val == data[_countof(data) - 2]);
+ }
+ };
+}
diff --git a/UnitTests/pch.h b/UnitTests/pch.h
index 2fc1d4d26..3f81b5179 100644
--- a/UnitTests/pch.h
+++ b/UnitTests/pch.h
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/stdex/internal.hpp b/include/stdex/internal.hpp
new file mode 100644
index 000000000..4e8706b96
--- /dev/null
+++ b/include/stdex/internal.hpp
@@ -0,0 +1,42 @@
+/*
+ SPDX-License-Identifier: MIT
+ Copyright © 2023 Amebis
+*/
+
+#pragma once
+
+#include
+
+namespace stdex
+{
+ ///
+ /// Helper template to allow access to internal std C++ private members
+ ///
+ /// \sa http://bloglitb.blogspot.com/2011/12/access-to-private-members-safer.html
+ ///
+ template
+ struct robber {
+ friend typename _Tag::type get(_Tag) {
+ return _Member;
+ }
+ };
+
+ ///
+ /// Helper template to allow access to internal std C++ private members
+ ///
+ /// \sa http://bloglitb.blogspot.com/2011/12/access-to-private-members-safer.html
+ ///
+ template
+ struct getter {
+ typedef _Type _Class::* type;
+ friend type get(getter<_Type, _Class>);
+ };
+}
+
+#ifdef _WIN32
+/// \cond internal
+extern "C" {
+ _ACRTIMP intptr_t __cdecl _get_osfhandle(_In_ int _FileHandle);
+}
+/// \endcond
+#endif
diff --git a/include/stdex/ios.hpp b/include/stdex/ios.hpp
new file mode 100644
index 000000000..29326f804
--- /dev/null
+++ b/include/stdex/ios.hpp
@@ -0,0 +1,504 @@
+/*
+ SPDX-License-Identifier: MIT
+ Copyright © 2023 Amebis
+*/
+
+#pragma once
+
+#ifdef _WIN32
+#include
+#endif
+#include "endian.hpp"
+#include "internal.hpp"
+#include "string.hpp"
+#include "sal.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace stdex
+{
+ ///
+ /// Binary stream writer
+ ///
+ template
+ class basic_ostreamfmt
+ {
+ public:
+ std::basic_ostream<_Elem, _Traits> &sp; // Write stream
+
+ inline basic_ostreamfmt(_Inout_ std::basic_ostream<_Elem, _Traits> &stream) : sp(stream) {}
+
+ using pos_type = typename _Traits::pos_type;
+ using off_type = typename _Traits::off_type;
+ inline pos_type tellp() { return sp.tellp(); }
+ inline basic_ostreamfmt<_Elem, _Traits>& seekp(pos_type pos) { sp.seekp(pos); return *this; }
+ inline basic_ostreamfmt<_Elem, _Traits>& seekp(off_type off, std::ios_base::seekdir dir) { sp.seekp(off, dir); return *this; }
+ inline bool good() const noexcept { return sp.good(); }
+ inline bool eof() const noexcept { return sp.eof(); }
+ inline bool fail() const noexcept { return sp.fail(); }
+ inline bool bad() const noexcept { return sp.bad(); }
+
+ inline basic_ostreamfmt<_Elem, _Traits>& write(_In_reads_bytes_(size) const void* data, _In_ std::streamsize size)
+ {
+ sp.write(reinterpret_cast(data), size/sizeof(_Elem));
+ return *this;
+ }
+
+ template
+ inline basic_ostreamfmt<_Elem, _Traits>& write(_In_ T value)
+ {
+ HE2LE(&value);
+ sp.write(reinterpret_cast(&value), sizeof(T)/sizeof(_Elem));
+ return *this;
+ }
+
+ inline basic_ostreamfmt<_Elem, _Traits>& write(_In_z_ const char* value)
+ {
+ size_t count = strlen(value);
+ if (count > UINT32_MAX)
+ throw std::invalid_argument("string too big");
+ sp.write(static_cast(count));
+ sp.write(reinterpret_cast(value), (std::streamsize)count * sizeof(char)/sizeof(_Elem));
+ return *this;
+ }
+
+ inline basic_ostreamfmt<_Elem, _Traits>& write(_In_z_ const wchar_t* value)
+ {
+ size_t count = strlen(value);
+ if (count > UINT32_MAX)
+ throw std::invalid_argument("string too big");
+ sp.write(static_cast(count));
+#ifdef BIG_ENDIAN
+ for (size_t i = 0; i < count; ++i)
+ sp.write(value[i]);
+#else
+ sp.write(reinterpret_cast(value), (std::streamsize)count * sizeof(wchar_t)/sizeof(_Elem));
+#endif
+ return *this;
+ }
+
+ ///
+ /// Formats string using `printf()` and write it to stream.
+ ///
+ /// \param[in] format String template using `printf()` style
+ /// \param[in] locale Stdlib locale used to perform formatting. Use `NULL` to use locale globally set by `setlocale()`.
+ /// \param[in] arg Arguments to `format`
+ ///
+ template
+ void vprintf(_In_z_ _Printf_format_string_ const _Elem2 *format, _In_opt_ locale_t locale, _In_ va_list arg)
+ {
+ std::basic_string<_Elem2> str;
+ vappendf(str, format, locale, arg);
+ sp.write(reinterpret_cast(str.c_str()), str.size() * sizeof(_Elem2)/sizeof(_Elem));
+ }
+
+ ///
+ /// Formats string using `printf()` and write it to stream.
+ ///
+ /// \param[in] format String template using `printf()` style
+ /// \param[in] locale Stdlib locale used to perform formatting. Use `NULL` to use locale globally set by `setlocale()`.
+ ///
+ template
+ void printf(_In_z_ _Printf_format_string_ const _Elem2 *format, _In_opt_ locale_t locale, ...)
+ {
+ va_list arg;
+ va_start(arg, locale);
+ vprintf(format, locale, arg);
+ va_end(arg);
+ }
+
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ int8_t value) { return write(value); }
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ int16_t value) { return write(value); }
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ int32_t value) { return write(value); }
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ int64_t value) { return write(value); }
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ uint8_t value) { return write(value); }
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ uint16_t value) { return write(value); }
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ uint32_t value) { return write(value); }
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ uint64_t value) { return write(value); }
+#ifdef _NATIVE_SIZE_T_DEFINED
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ size_t value) { return write(value); }
+#endif
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ float value) { return write(value); }
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ double value) { return write(value); }
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ char value) { return write(value); }
+#ifdef _NATIVE_WCHAR_T_DEFINED
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ wchar_t value) { return write(value); }
+#endif
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_z_ const char* value) { return write(value); }
+ inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_z_ const wchar_t* value) { return write(value); }
+ };
+
+ using ostreamfmt = basic_ostreamfmt>;
+ using wostreamfmt = basic_ostreamfmt>;
+
+ ///
+ /// Binary stream reader
+ ///
+ template
+ class basic_istreamfmt
+ {
+ public:
+ std::basic_istream<_Elem, _Traits> &sg; // Read stream
+
+ inline basic_istreamfmt(_Inout_ std::basic_istream<_Elem, _Traits> &stream) : sg(stream) {}
+
+ using pos_type = typename _Traits::pos_type;
+ using off_type = typename _Traits::off_type;
+ inline pos_type tellg() { return sg.tellg(); }
+ inline basic_istreamfmt<_Elem, _Traits>& seekg(pos_type pos) { sg.seekg(pos); return *this; }
+ inline basic_istreamfmt<_Elem, _Traits>& seekg(off_type off, std::ios_base::seekdir dir) { sg.seekg(off, dir); return *this; }
+ inline bool good() const noexcept { return sg.good(); }
+ inline bool eof() const noexcept { return sg.eof(); }
+ inline bool fail() const noexcept { return sg.fail(); }
+ inline bool bad() const noexcept { return sg.bad(); }
+ inline std::streamsize gcount() const noexcept { return sg.gcount(); }
+
+ inline basic_istreamfmt<_Elem, _Traits>& read(_Out_writes_bytes_(size) void* data, std::streamsize size)
+ {
+ sg.read(reinterpret_cast<_Elem*>(data), size/sizeof(_Elem));
+ return *this;
+ }
+
+ template
+ inline basic_istreamfmt<_Elem, _Traits>& read(_Out_ T& value)
+ {
+ sg.read(reinterpret_cast<_Elem*>(&value), sizeof(T)/sizeof(_Elem));
+ if (sg.good())
+ LE2HE(&value);
+ return *this;
+ }
+
+ template , class _Alloc = std::allocator>
+ inline basic_istreamfmt<_Elem, _Traits>& read(_Inout_ std::basic_string& value)
+ {
+ uint32_t count;
+ sg.read(count);
+ if (sg.good()) {
+ value.resize(count);
+ sg.read(reinterpret_cast<_Elem*>(&value[0]), (std::streamsize)count * sizeof(char)/sizeof(_Elem));
+ }
+ return *this;
+ }
+
+ template , class _Alloc = std::allocator>
+ inline basic_istreamfmt<_Elem, _Traits>& read(_Inout_ std::basic_string& value)
+ {
+ uint32_t count;
+ sg.read(count);
+ if (sg.good()) {
+ value.resize(count);
+#ifdef BIG_ENDIAN
+ for (size_t i = 0; i < count; ++i)
+ sg.read(value[i]);
+#else
+ sg.read(reinterpret_cast<_Elem*>(&value[0]), (std::streamsize)count * sizeof(wchar_t)/sizeof(_Elem));
+#endif
+ }
+ return *this;
+ }
+
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ int8_t& value) { return read(value); }
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ int16_t& value) { return read(value); }
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ int32_t& value) { return read(value); }
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ int64_t& value) { return read(value); }
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ uint8_t& value) { return read(value); }
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ uint16_t& value) { return read(value); }
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ uint32_t& value) { return read(value); }
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ uint64_t& value) { return read(value); }
+#ifdef _NATIVE_SIZE_T_DEFINED
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ size_t& value) { return read(value); }
+#endif
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ float& value) { return read(value); }
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ double& value) { return read(value); }
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ char& value) { return read(value); }
+#ifdef _NATIVE_WCHAR_T_DEFINED
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ wchar_t& value) { return read(value); }
+#endif
+ template , class _Alloc = std::allocator>
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Inout_ std::basic_string& value) { return read(value); }
+ template , class _Alloc = std::allocator>
+ inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Inout_ std::basic_string& value) { return read(value); }
+ };
+
+ using istreamfmt = basic_istreamfmt>;
+ using wistreamfmt = basic_istreamfmt>;
+
+ ///
+ /// Binary stream reader/writer
+ ///
+ template
+ class basic_iostreamfmt : public basic_ostreamfmt<_Elem, _Traits>, public basic_istreamfmt<_Elem, _Traits>
+ {
+ public:
+ inline basic_iostreamfmt(_Inout_ std::basic_iostream<_Elem, _Traits> &stream) :
+ basic_ostreamfmt<_Elem, _Traits>(stream),
+ basic_istreamfmt<_Elem, _Traits>(stream)
+ {}
+ };
+
+ using iostreamfmt = basic_iostreamfmt>;
+ using wiostreamfmt = basic_iostreamfmt>;
+
+ ///
+ /// Shared-memory string buffer
+ ///
+ template
+ class basic_sharedstrbuf : public std::basic_streambuf<_Elem, _Traits>
+ {
+ 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));
+ }
+
+ basic_sharedstrbuf(_In_ const basic_sharedstrbuf<_Elem, _Traits>& other)
+ {
+ std::basic_streambuf<_Elem, _Traits>::setg(other.eback(), other.gptr(), other.egptr());
+ }
+
+ basic_sharedstrbuf<_Elem, _Traits>& operator =(_In_ const basic_sharedstrbuf<_Elem, _Traits>& other)
+ {
+ if (this != std::addressof(other))
+ std::basic_streambuf<_Elem, _Traits>::operator =(other);
+ return *this;
+ }
+
+ private:
+ basic_sharedstrbuf(_Inout_ basic_sharedstrbuf<_Elem, _Traits>&& other) noexcept;
+ basic_sharedstrbuf<_Elem, _Traits>& operator =(_Inout_ basic_sharedstrbuf<_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) {
+ _Elem* target;
+ switch (way) {
+ case std::ios_base::beg: target = eback() + off; break;
+ case std::ios_base::cur: target = gptr() + off; break;
+ case std::ios_base::end: target = egptr() + off; break;
+ default: throw std::invalid_argument("invalid seek reference");
+ }
+ if (eback() <= target && target <= egptr()) {
+ gbump(static_cast(target - gptr()));
+ return pos_type{ off_type{ target - eback() } };
+ }
+ }
+ 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)
+ {
+ // change to specified position, according to mode
+ if (which & std::ios_base::in) {
+ _Elem* target = eback() + pos;
+ if (eback() <= target && target <= egptr()) {
+ gbump(static_cast(target - gptr()));
+ return pos_type{ off_type{ target - eback() } };
+ }
+ }
+ return pos_type{ off_type{-1} };
+ }
+ };
+
+ template
+ class basic_isharedstrstream : public std::basic_istream<_Elem, _Traits>
+ {
+ public:
+ basic_isharedstrstream(_In_reads_(size) const _Elem* data, _In_ size_t size) :
+ m_buf(data, size),
+ std::basic_istream<_Elem, _Traits>(&m_buf)
+ {}
+
+ protected:
+ basic_sharedstrbuf<_Elem, _Traits> m_buf;
+ };
+
+ using isharedstrstream = basic_isharedstrstream>;
+ using wisharedstrstream = basic_isharedstrstream>;
+
+#ifdef _WIN32
+ /// \cond internal
+ template struct robber, &std::filebuf::_Myfile>;
+ template struct robber, &std::wfilebuf::_Myfile>;
+
+ inline FILE* filebuf_fhandle(_In_ std::filebuf* rb)
+ {
+ return (*rb).*get(getter());
+ }
+
+ inline FILE* filebuf_fhandle(_In_ std::wfilebuf* rb)
+ {
+ return (*rb).*get(getter());
+ }
+ /// \endcond
+#endif
+
+ ///
+ /// File stream with additional std::filesystem features
+ ///
+ template
+ class basic_fstream : public std::basic_fstream<_Elem, _Traits>
+ {
+ public:
+ using _Mybase = std::basic_fstream<_Elem, _Traits>;
+
+ basic_fstream() {}
+
+ explicit basic_fstream(
+ _In_z_ const char* file_name,
+ _In_ ios_base::openmode mode = ios_base::in | ios_base::out,
+ _In_ int prot = ios_base::_Default_open_prot) : _Mybase(file_name, mode, prot) {}
+
+ explicit basic_fstream(
+ _In_z_ const wchar_t* file_name,
+ _In_ ios_base::openmode mode = ios_base::in | ios_base::out,
+ _In_ int prot = ios_base::_Default_open_prot) : _Mybase(file_name, mode, prot) {}
+
+ template
+ explicit basic_fstream(
+ _In_ const std::basic_string<_Elem2, _Traits2, _Ax>& str,
+ _In_ ios_base::openmode mode = ios_base::in | ios_base::out,
+ _In_ int prot = ios_base::_Default_open_prot) : basic_fstream(str.c_str(), mode, prot) {}
+
+ explicit basic_fstream(_In_ FILE* file) : _Mybase(file) {}
+
+ basic_fstream(_Inout_ basic_fstream&& other) : _Mybase(std::move(other)) {}
+
+ ///
+ /// Sets end of file at current put position
+ ///
+ void truncate()
+ {
+ flush();
+ auto h = os_fhandle();
+#ifdef _WIN32
+ if (h == INVALID_HANDLE_VALUE)
+ throw std::runtime_error("invalid handle");
+ auto pos = tellp();
+ LONG
+ pos_lo = static_cast(pos & 0xffffffff),
+ pos_hi = static_cast((pos >> 32) & 0xffffffff);
+ if (SetFilePointer(h, pos_lo, &pos_hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ throw std::runtime_error("failed to seek");
+ if (!SetEndOfFile(h))
+ throw std::runtime_error("failed to truncate");
+#else
+#error Implement!
+#endif
+ }
+
+#if _HAS_CXX20
+ using time_type = std::chrono::time_point;
+#else
+ using time_type = std::chrono::time_point;
+#endif
+
+ ///
+ /// Returns file modification time
+ ///
+ /// \returns File modification time
+ ///
+ time_type mtime() const
+ {
+ auto h = os_fhandle();
+#ifdef _WIN32
+ if (h == INVALID_HANDLE_VALUE)
+ throw std::runtime_error("invalid handle");
+ FILETIME ft;
+ if (!GetFileTime(h, NULL, NULL, &ft))
+ throw std::runtime_error("failed to get mtime");
+#if _HAS_CXX20
+ return time_type(time_type::duration(((static_cast(ft.dwHighDateTime) << 32) | ft.dwLowDateTime)));
+#else
+ // Adjust epoch to std::chrono::time_point/time_t.
+ return time_type(time_type::duration(((static_cast(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) - 116444736000000000ll));
+#endif
+#else
+#error Implement!
+#endif
+ }
+
+ protected:
+#ifdef _WIN32
+ HANDLE os_fhandle() const
+ {
+ FILE* f = filebuf_fhandle(rdbuf());
+ if (f == NULL)
+ return INVALID_HANDLE_VALUE;
+
+ int fd = _fileno(f);
+ if (fd == -1)
+ return INVALID_HANDLE_VALUE;
+
+ return (HANDLE)_get_osfhandle(fd);
+ }
+#else
+#error Implement!
+#endif
+ };
+
+ using fstream = basic_fstream>;
+ using wfstream = basic_fstream>;
+
+ ///
+ /// String stream
+ ///
+ template
+ class basic_stringstream : public std::basic_stringstream<_Elem, _Traits, _Alloc> {
+ public:
+ using _Mybase = std::basic_stringstream<_Elem, _Traits, _Alloc>;
+ using _Mystr = std::basic_string<_Elem, _Traits, _Alloc>;
+
+ basic_stringstream() {}
+ explicit basic_stringstream(_In_ std::ios_base::openmode mode) : _Mybase(mode) {}
+ explicit basic_stringstream(_In_ const _Mystr& str, _In_ std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) : _Mybase(str, mode) {}
+ basic_stringstream(_Inout_ basic_stringstream&& other) : _Mybase(std::move(other)) {}
+
+ ///
+ /// Initializes stream with content from file.
+ ///
+ /// \param[in] filename File name
+ /// \param[in] mode Mode flags to open file. The std::stringstream returned is always opened as in|out.
+ /// \param[in] prot Protection flags to open file
+ ///
+ template
+ explicit basic_stringstream(_In_z_ const T* filename, _In_ std::ios_base::openmode mode = std::ios_base::in, _In_ int prot = std::ios_base::_Default_open_prot) :
+ _Mybase(std::ios_base::in | std::ios_base::out | (mode & std::ios_base::bin | std::ios_base::app))
+ {
+ std::basic_ifstream<_Elem, _Traits> input(filename, mode & ~(std::ios_base::ate | std::ios_base::app), prot);
+ input.seekg(0, input.end);
+ auto size = input.tellg();
+ if (size > SIZE_MAX)
+ throw std::runtime_error("file too big to fit into memory");
+ str.reserve(static_cast(size));
+ input.seekg(0);
+ do {
+ _Elem buf[0x1000];
+ input.read(buf, _countof(buf));
+ write(buf, input.gcount());
+ } while (!input.eof());
+ if (!(mode & (std::ios_base::ate | std::ios_base::app)))
+ seekp(0);
+ }
+
+ ///
+ /// Initializes stream with content from file.
+ ///
+ /// \param[in] filename File name
+ /// \param[in] mode Mode flags to open file. The std::stringstream returned is always opened as in|out.
+ /// \param[in] prot Protection flags to open file
+ ///
+ template
+ explicit basic_stringstream(_In_ const std::basic_string& filename, _In_ std::ios_base::openmode mode = std::ios_base::in, _In_ int prot = std::ios_base::_Default_open_prot) :
+ basic_stringstream(filename.c_str(), mode, prot)
+ {}
+ };
+
+ using stringstream = basic_stringstream, std::allocator>;
+ using wstringstream = basic_stringstream, std::allocator>;
+}