ios, stream: replace <iostream> with leaner and faster own streaming
Our test program runs 15 minutes using our streams vs. 25 minutes using std::iostream derived streams. Streams were ported from Amebis AOsn project. Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
parent
4965d1eac5
commit
8457226168
@ -23,7 +23,7 @@ Global
|
|||||||
{9AFC377D-C32D-4D42-82C2-09FC818020A2}.Release|x64.Build.0 = Release|x64
|
{9AFC377D-C32D-4D42-82C2-09FC818020A2}.Release|x64.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = false
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {BBDB843D-98C3-46EF-BDE8-0E80FD851852}
|
SolutionGuid = {BBDB843D-98C3-46EF-BDE8-0E80FD851852}
|
||||||
|
@ -111,11 +111,14 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="ios.cpp" />
|
|
||||||
<ClCompile Include="math.cpp" />
|
<ClCompile Include="math.cpp" />
|
||||||
<ClCompile Include="parser.cpp" />
|
<ClCompile Include="parser.cpp" />
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
@ -123,6 +126,7 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ring.cpp" />
|
<ClCompile Include="ring.cpp" />
|
||||||
<ClCompile Include="sgml.cpp" />
|
<ClCompile Include="sgml.cpp" />
|
||||||
|
<ClCompile Include="stream.cpp" />
|
||||||
<ClCompile Include="unicode.cpp" />
|
<ClCompile Include="unicode.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -24,15 +24,15 @@
|
|||||||
<ClCompile Include="parser.cpp">
|
<ClCompile Include="parser.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ios.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="math.cpp">
|
<ClCompile Include="math.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ring.cpp">
|
<ClCompile Include="ring.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="stream.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="unicode.cpp">
|
<ClCompile Include="unicode.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_METHOD(diagstream)
|
|
||||||
{
|
|
||||||
constexpr size_t n = 3;
|
|
||||||
unique_ptr<std::fstream> f[n];
|
|
||||||
vector<std::fstream*> 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 std::fstream(path, ios_base::out | ios_base::binary));
|
|
||||||
fv[i] = f[i].get();
|
|
||||||
}
|
|
||||||
stdex::diagstream d(fv.begin(), fv.end());
|
|
||||||
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<uint32_t>(rand());
|
|
||||||
f.write(reinterpret_cast<const char*>(&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);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
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 std::fstream(path, ios_base::in | ios_base::binary));
|
|
||||||
fv[i] = f[i].get();
|
|
||||||
}
|
|
||||||
stdex::diagstream d(fv.begin(), fv.end());
|
|
||||||
do {
|
|
||||||
uint32_t r;
|
|
||||||
d.read(reinterpret_cast<char*>(&r), sizeof(r) / sizeof(char));
|
|
||||||
} while (!d.eof());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -14,7 +14,6 @@
|
|||||||
#include <stdex/hex.hpp>
|
#include <stdex/hex.hpp>
|
||||||
#include <stdex/idrec.hpp>
|
#include <stdex/idrec.hpp>
|
||||||
#include <stdex/interval.hpp>
|
#include <stdex/interval.hpp>
|
||||||
#include <stdex/ios.hpp>
|
|
||||||
#include <stdex/mapping.hpp>
|
#include <stdex/mapping.hpp>
|
||||||
#include <stdex/math.hpp>
|
#include <stdex/math.hpp>
|
||||||
#include <stdex/parser.hpp>
|
#include <stdex/parser.hpp>
|
||||||
@ -22,11 +21,14 @@
|
|||||||
#include <stdex/ring.hpp>
|
#include <stdex/ring.hpp>
|
||||||
#include <stdex/sal.hpp>
|
#include <stdex/sal.hpp>
|
||||||
#include <stdex/sgml.hpp>
|
#include <stdex/sgml.hpp>
|
||||||
|
#include <stdex/stream.hpp>
|
||||||
#include <stdex/string.hpp>
|
#include <stdex/string.hpp>
|
||||||
|
#include <stdex/system.hpp>
|
||||||
#include <stdex/unicode.hpp>
|
#include <stdex/unicode.hpp>
|
||||||
#include <stdex/vector_queue.hpp>
|
#include <stdex/vector_queue.hpp>
|
||||||
|
|
||||||
#include <CppUnitTest.h>
|
#include <CppUnitTest.h>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
111
UnitTests/stream.cpp
Normal file
111
UnitTests/stream.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
Copyright © 2023 Amebis
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
|
|
||||||
|
namespace UnitTests
|
||||||
|
{
|
||||||
|
TEST_CLASS(stream)
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TEST_METHOD(async)
|
||||||
|
{
|
||||||
|
constexpr size_t total = 1000;
|
||||||
|
stdex::stream::memory_file source(stdex::mul(total, sizeof(size_t)));
|
||||||
|
{
|
||||||
|
stdex::stream::async_writer<70> writer(source);
|
||||||
|
for (size_t i = 0; i < total; ++i) {
|
||||||
|
Assert::IsTrue(writer.ok());
|
||||||
|
writer << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert::AreEqual<fpos_t>(0, source.seekbeg(0));
|
||||||
|
{
|
||||||
|
stdex::stream::async_reader<50> reader(source);
|
||||||
|
size_t x;
|
||||||
|
for (size_t i = 0; i < total; ++i) {
|
||||||
|
reader >> x;
|
||||||
|
Assert::IsTrue(reader.ok());
|
||||||
|
Assert::AreEqual(i, x);
|
||||||
|
}
|
||||||
|
reader >> x;
|
||||||
|
Assert::IsFalse(reader.ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(replicator)
|
||||||
|
{
|
||||||
|
constexpr size_t total = 1000;
|
||||||
|
|
||||||
|
stdex::stream::memory_file f1(stdex::mul(total, sizeof(size_t)));
|
||||||
|
|
||||||
|
std::basic_string<stdex::sys_char> filename2, filename3;
|
||||||
|
#ifdef _WIN32
|
||||||
|
{
|
||||||
|
TCHAR temp_path[MAX_PATH];
|
||||||
|
Assert::IsTrue(ExpandEnvironmentStrings(_T("%TEMP%\\"), temp_path, _countof(temp_path)) < MAX_PATH);
|
||||||
|
filename2 = filename3 = temp_path;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
filename2 = filename3 = "/tmp/";
|
||||||
|
#endif
|
||||||
|
filename2 += _T("stdex-stream-replicator-2.tmp");
|
||||||
|
stdex::stream::file f2(
|
||||||
|
filename2.c_str(),
|
||||||
|
stdex::stream::mode_for_reading | stdex::stream::mode_for_writing | stdex::stream::mode_create | stdex::stream::mode_binary);
|
||||||
|
|
||||||
|
filename3 += _T("stdex-stream-replicator-3.tmp");
|
||||||
|
stdex::stream::cached_file f3(
|
||||||
|
filename3.c_str(),
|
||||||
|
stdex::stream::mode_for_reading | stdex::stream::mode_for_writing | stdex::stream::mode_create | stdex::stream::mode_binary,
|
||||||
|
128);
|
||||||
|
|
||||||
|
{
|
||||||
|
stdex::stream::replicator writer;
|
||||||
|
stdex::stream::buffer f2_buf(f2, 0, 32);
|
||||||
|
writer.push_back(&f1);
|
||||||
|
writer.push_back(&f2_buf);
|
||||||
|
writer.push_back(&f3);
|
||||||
|
for (size_t i = 0; i < total; ++i) {
|
||||||
|
Assert::IsTrue(writer.ok());
|
||||||
|
writer << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f1.seekbeg(0);
|
||||||
|
f2.seekbeg(0);
|
||||||
|
f3.seekbeg(0);
|
||||||
|
{
|
||||||
|
stdex::stream::buffer f2_buf(f2, 64, 0);
|
||||||
|
size_t x;
|
||||||
|
for (size_t i = 0; i < total; ++i) {
|
||||||
|
f1 >> x;
|
||||||
|
Assert::IsTrue(f1.ok());
|
||||||
|
Assert::AreEqual(i, x);
|
||||||
|
f2_buf >> x;
|
||||||
|
Assert::IsTrue(f2_buf.ok());
|
||||||
|
Assert::AreEqual(i, x);
|
||||||
|
f3 >> x;
|
||||||
|
Assert::IsTrue(f3.ok());
|
||||||
|
Assert::AreEqual(i, x);
|
||||||
|
}
|
||||||
|
f1 >> x;
|
||||||
|
Assert::IsFalse(f1.ok());
|
||||||
|
f2_buf >> x;
|
||||||
|
Assert::IsFalse(f2_buf.ok());
|
||||||
|
f3 >> x;
|
||||||
|
Assert::IsFalse(f3.ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
f2.close();
|
||||||
|
std::filesystem::remove(filename2);
|
||||||
|
f3.close();
|
||||||
|
std::filesystem::remove(filename3);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
Copyright © 2023 Amebis
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
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<typename _Tag, typename _Tag::type _Member>
|
|
||||||
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<typename _Type, typename _Class>
|
|
||||||
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
|
|
@ -1,671 +0,0 @@
|
|||||||
/*
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
Copyright © 2023 Amebis
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
#include "endian.hpp"
|
|
||||||
#include "internal.hpp"
|
|
||||||
#include "string.hpp"
|
|
||||||
#include "sal.hpp"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <chrono>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iterator>
|
|
||||||
#include <istream>
|
|
||||||
#include <ostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace stdex
|
|
||||||
{
|
|
||||||
///
|
|
||||||
/// Binary stream writer
|
|
||||||
///
|
|
||||||
template <class _Elem, class _Traits>
|
|
||||||
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<const _Elem*>(data), size/sizeof(_Elem));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
inline basic_ostreamfmt<_Elem, _Traits>& write(_In_ T value)
|
|
||||||
{
|
|
||||||
HE2LE(&value);
|
|
||||||
sp.write(reinterpret_cast<const _Elem*>(&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<uint32_t>(count));
|
|
||||||
sp.write(reinterpret_cast<const _Elem*>(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<uint32_t>(count));
|
|
||||||
#ifdef BIG_ENDIAN
|
|
||||||
for (size_t i = 0; i < count; ++i)
|
|
||||||
sp.write(value[i]);
|
|
||||||
#else
|
|
||||||
sp.write(reinterpret_cast<const _Elem*>(value), (std::streamsize)count * sizeof(wchar_t)/sizeof(_Elem));
|
|
||||||
#endif
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline basic_ostreamfmt<_Elem, _Traits>& write_byte(_In_ uint8_t value)
|
|
||||||
{
|
|
||||||
return write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// 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 <class _Elem2>
|
|
||||||
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<const _Elem*>(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 <class _Elem2>
|
|
||||||
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); }
|
|
||||||
#if defined(_NATIVE_SIZE_T_DEFINED) && defined(_WIN64)
|
|
||||||
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<char, std::char_traits<char>>;
|
|
||||||
using wostreamfmt = basic_ostreamfmt<wchar_t, std::char_traits<wchar_t>>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Binary stream reader
|
|
||||||
///
|
|
||||||
template <class _Elem, class _Traits>
|
|
||||||
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 <class T>
|
|
||||||
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 _Traits = std::char_traits<char>, class _Alloc = std::allocator<char>>
|
|
||||||
inline basic_istreamfmt<_Elem, _Traits>& read(_Inout_ std::basic_string<char, _Traits, _Alloc>& 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 _Traits = std::char_traits<wchar_t>, class _Alloc = std::allocator<wchar_t>>
|
|
||||||
inline basic_istreamfmt<_Elem, _Traits>& read(_Inout_ std::basic_string<wchar_t, _Traits, _Alloc>& 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 uint8_t read_byte()
|
|
||||||
{
|
|
||||||
uint8_t value;
|
|
||||||
read(value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
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); }
|
|
||||||
#if defined(_NATIVE_SIZE_T_DEFINED) && defined(_WIN64)
|
|
||||||
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 _Traits = std::char_traits<char>, class _Alloc = std::allocator<char>>
|
|
||||||
inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Inout_ std::basic_string<char, _Traits, _Alloc>& value) { return read(value); }
|
|
||||||
template <class _Traits = std::char_traits<wchar_t>, class _Alloc = std::allocator<wchar_t>>
|
|
||||||
inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Inout_ std::basic_string<wchar_t, _Traits, _Alloc>& value) { return read(value); }
|
|
||||||
};
|
|
||||||
|
|
||||||
using istreamfmt = basic_istreamfmt<char, std::char_traits<char>>;
|
|
||||||
using wistreamfmt = basic_istreamfmt<wchar_t, std::char_traits<wchar_t>>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Binary stream reader/writer
|
|
||||||
///
|
|
||||||
template <class _Elem, class _Traits>
|
|
||||||
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<char, std::char_traits<char>>;
|
|
||||||
using wiostreamfmt = basic_iostreamfmt<wchar_t, std::char_traits<wchar_t>>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Shared-memory string stream buffer
|
|
||||||
///
|
|
||||||
template <class _Elem, class _Traits>
|
|
||||||
class basic_sharedstrbuf : public std::basic_streambuf<_Elem, _Traits>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
basic_sharedstrbuf(_In_reads_(size) const _Elem* data, _In_ size_t size)
|
|
||||||
{
|
|
||||||
setg(const_cast<_Elem*>(data), const_cast<_Elem*>(data), const_cast<_Elem*>(data + size));
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_sharedstrbuf(_In_ const basic_sharedstrbuf<_Elem, _Traits>& other)
|
|
||||||
{
|
|
||||||
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() + static_cast<ptrdiff_t>(off); break;
|
|
||||||
case std::ios_base::cur: target = gptr() + static_cast<ptrdiff_t>(off); break;
|
|
||||||
case std::ios_base::end: target = egptr() + static_cast<ptrdiff_t>(off); break;
|
|
||||||
default: throw std::invalid_argument("invalid seek reference");
|
|
||||||
}
|
|
||||||
if (eback() <= target && target <= egptr()) {
|
|
||||||
gbump(static_cast<int>(target - gptr()));
|
|
||||||
return pos_type{ off_type{ target - eback() } };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
_Elem* target = eback() + static_cast<size_t>(pos);
|
|
||||||
if (eback() <= target && target <= egptr()) {
|
|
||||||
gbump(static_cast<int>(target - gptr()));
|
|
||||||
return pos_type{ off_type{ target - eback() } };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pos_type{ off_type{-1} };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class _Elem, class _Traits>
|
|
||||||
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<char, std::char_traits<char>>;
|
|
||||||
using wisharedstrstream = basic_isharedstrstream<wchar_t, std::char_traits<wchar_t>>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Diagnostic input stream buffer
|
|
||||||
///
|
|
||||||
/// Verifies multiple input streams read the same data.
|
|
||||||
///
|
|
||||||
template <class _Elem, class _Traits>
|
|
||||||
class basic_diagstreambuf : public std::basic_streambuf<_Elem, _Traits>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using guest_stream = std::basic_iostream<_Elem, _Traits>;
|
|
||||||
|
|
||||||
template<typename _Iter>
|
|
||||||
basic_diagstreambuf(_In_ const _Iter first, _In_ const _Iter last) : m_streams(first, last) {}
|
|
||||||
basic_diagstreambuf(_In_reads_(count) guest_stream* const* streams, _In_ size_t count) : basic_diagstreambuf(streams, streams + count) {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
basic_diagstreambuf(_In_ const basic_diagstreambuf<_Elem, _Traits>& other);
|
|
||||||
basic_diagstreambuf<_Elem, _Traits>& operator =(_In_ const basic_diagstreambuf<_Elem, _Traits>& other);
|
|
||||||
basic_diagstreambuf(_Inout_ basic_diagstreambuf<_Elem, _Traits>&& other) noexcept;
|
|
||||||
basic_diagstreambuf<_Elem, _Traits>& operator =(_Inout_ basic_diagstreambuf<_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 (m_streams.empty())
|
|
||||||
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);
|
|
||||||
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) {
|
|
||||||
m_streams[i]->seekg(off, way);
|
|
||||||
if (m_streams[i]->bad())
|
|
||||||
r = 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)
|
|
||||||
{
|
|
||||||
return seekoff(pos, std::ios_base::beg, which);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int_type underflow()
|
|
||||||
{
|
|
||||||
int_type eof = _Traits::eof();
|
|
||||||
if (m_streams.empty())
|
|
||||||
eof;
|
|
||||||
_Elem data;
|
|
||||||
m_streams[0]->read(&data, 1);
|
|
||||||
int_type r = m_streams[0]->gcount() == 1 ? _Traits::to_int_type(data) : eof;
|
|
||||||
for (size_t i = 1, n = m_streams.size(); i < n; ++i) {
|
|
||||||
m_streams[i]->read(&data, 1);
|
|
||||||
int_type temp_r = m_streams[i]->gcount() == 1 ? _Traits::to_int_type(data) : eof;
|
|
||||||
if (r != temp_r)
|
|
||||||
r = eof;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int_type overflow(int_type ch = _Traits::eof())
|
|
||||||
{
|
|
||||||
if (_Traits::not_eof(ch)) {
|
|
||||||
_Elem data = _Traits::to_char_type(ch);
|
|
||||||
bool good = true;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int sync()
|
|
||||||
{
|
|
||||||
int r = 0;
|
|
||||||
for (size_t i = 0, n = m_streams.size(); i < n; ++i)
|
|
||||||
if (m_streams[i]->sync() < 0)
|
|
||||||
r = -1;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::vector<guest_stream*> m_streams;
|
|
||||||
};
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Diagnostic input/output stream
|
|
||||||
///
|
|
||||||
/// Verifies multiple input streams read the same data.
|
|
||||||
/// Writes to multiple output streams the same data.
|
|
||||||
///
|
|
||||||
template <class _Elem, class _Traits>
|
|
||||||
class basic_diagstream : public std::basic_iostream<_Elem, _Traits>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using guest_stream = std::basic_iostream<_Elem, _Traits>;
|
|
||||||
|
|
||||||
template<typename _Iter>
|
|
||||||
basic_diagstream(_In_ const _Iter first, _In_ const _Iter last) :
|
|
||||||
m_buf(first, last),
|
|
||||||
std::basic_iostream<_Elem, _Traits>(&m_buf)
|
|
||||||
{}
|
|
||||||
|
|
||||||
basic_diagstream(_In_reads_(count) guest_stream* const* streams, _In_ size_t count) :
|
|
||||||
basic_diagstream(streams, streams + count)
|
|
||||||
{}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
basic_diagstreambuf<_Elem, _Traits> m_buf;
|
|
||||||
};
|
|
||||||
|
|
||||||
using diagstream = basic_diagstream<char, std::char_traits<char>>;
|
|
||||||
using wdiagstream = basic_diagstream<wchar_t, std::char_traits<wchar_t>>;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
/// \cond internal
|
|
||||||
template struct robber<getter<FILE*, std::filebuf>, &std::filebuf::_Myfile>;
|
|
||||||
template struct robber<getter<FILE*, std::wfilebuf>, &std::wfilebuf::_Myfile>;
|
|
||||||
|
|
||||||
inline FILE* filebuf_fhandle(_In_ std::filebuf* rb)
|
|
||||||
{
|
|
||||||
return (*rb).*get(getter<FILE*, std::filebuf>());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline FILE* filebuf_fhandle(_In_ std::wfilebuf* rb)
|
|
||||||
{
|
|
||||||
return (*rb).*get(getter<FILE*, std::wfilebuf>());
|
|
||||||
}
|
|
||||||
/// \endcond
|
|
||||||
#endif
|
|
||||||
|
|
||||||
///
|
|
||||||
/// File stream with additional std::filesystem features
|
|
||||||
///
|
|
||||||
template <class _Elem, class _Traits>
|
|
||||||
class basic_fstream : public std::basic_fstream<_Elem, _Traits>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using _Mybase = std::basic_fstream<_Elem, _Traits>;
|
|
||||||
#if _HAS_CXX20
|
|
||||||
using time_point = std::chrono::time_point<std::chrono::file_clock>;
|
|
||||||
#else
|
|
||||||
using time_point = std::chrono::time_point<std::chrono::system_clock>;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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<class _Elem2, class _Traits2, class _Ax>
|
|
||||||
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<LONG>(pos & 0xffffffff),
|
|
||||||
pos_hi = static_cast<LONG>((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
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns file modification time
|
|
||||||
///
|
|
||||||
/// \returns File modification time
|
|
||||||
///
|
|
||||||
time_point 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_point(time_point::duration(((static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime)));
|
|
||||||
#else
|
|
||||||
// Adjust epoch to std::chrono::time_point<std::chrono::system_clock>/time_t.
|
|
||||||
return time_point(time_point::duration(((static_cast<int64_t>(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<char, std::char_traits<char>>;
|
|
||||||
using wfstream = basic_fstream<wchar_t, std::char_traits<wchar_t>>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// String stream
|
|
||||||
///
|
|
||||||
template <class _Elem, class _Traits, class _Alloc>
|
|
||||||
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 <class T>
|
|
||||||
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::binary | 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_t>(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 <class _Elem2, class _Traits2 = std::char_traits<_Elem2>, class _Alloc2 = std::allocator<_Elem2>>
|
|
||||||
explicit basic_stringstream(_In_ const std::basic_string<_Elem2, _Traits2, _Alloc2>& 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)
|
|
||||||
{}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Saves stream content to a file.
|
|
||||||
///
|
|
||||||
/// \param[in] filename File name
|
|
||||||
/// \param[in] mode Mode flags to open file
|
|
||||||
/// \param[in] prot Protection flags to open file
|
|
||||||
///
|
|
||||||
template <class T>
|
|
||||||
void save(_In_z_ const T* filename, _In_ std::ios_base::openmode mode = std::ios_base::out, _In_ int prot = std::ios_base::_Default_open_prot)
|
|
||||||
{
|
|
||||||
std::basic_ofstream<_Elem, _Traits> output(filename, mode, prot);
|
|
||||||
auto origin = tellg();
|
|
||||||
seekg(0, end);
|
|
||||||
auto size = tellg();
|
|
||||||
do {
|
|
||||||
_Elem buf[0x1000];
|
|
||||||
read(buf, _countof(buf));
|
|
||||||
output.write(buf, gcount());
|
|
||||||
} while (!eof());
|
|
||||||
seekg(origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class _Elem2, class _Traits2 = std::char_traits<T>, class _Alloc2 = std::allocator<T>>
|
|
||||||
void save(_In_ const std::basic_string<_Elem2, _Traits2, _Alloc2>& filename, _In_ std::ios_base::openmode mode = std::ios_base::out, _In_ int prot = std::ios_base::_Default_open_prot)
|
|
||||||
{
|
|
||||||
save(filename.data(), mode, prot);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using stringstream = basic_stringstream<char, std::char_traits<char>, std::allocator<char>>;
|
|
||||||
using wstringstream = basic_stringstream<wchar_t, std::char_traits<wchar_t>, std::allocator<char>>;
|
|
||||||
}
|
|
@ -64,3 +64,25 @@
|
|||||||
#ifndef _Success_
|
#ifndef _Success_
|
||||||
#define _Success_(p)
|
#define _Success_(p)
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef _Ret_notnull_
|
||||||
|
#define _Ret_notnull_
|
||||||
|
#endif
|
||||||
|
#ifndef _Must_inspect_result_
|
||||||
|
#define _Must_inspect_result_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _Likely_
|
||||||
|
#if _HAS_CXX20
|
||||||
|
#define _Likely_ [[likely]]
|
||||||
|
#else
|
||||||
|
#define _Likely_
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _Unlikely_
|
||||||
|
#if _HAS_CXX20
|
||||||
|
#define _Unlikely_ [[unlikely]]
|
||||||
|
#else
|
||||||
|
#define _Unlikely_
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
3664
include/stdex/stream.hpp
Normal file
3664
include/stdex/stream.hpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user