stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
ios.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2023 Amebis
4*/
5
6#pragma once
7
8#ifdef _WIN32
9#include <windows.h>
10#endif
11#include "endian.hpp"
12#include "internal.hpp"
13#include "string.hpp"
14#include "sal.hpp"
15#include <assert.h>
16#include <stdint.h>
17#include <stdio.h>
18#include <chrono>
19#include <fstream>
20#include <istream>
21#include <ostream>
22
23namespace stdex
24{
28 template <class _Elem, class _Traits>
30 {
31 public:
32 std::basic_ostream<_Elem, _Traits> &sp; // Write stream
33
34 inline basic_ostreamfmt(_Inout_ std::basic_ostream<_Elem, _Traits> &stream) : sp(stream) {}
35
36 using pos_type = typename _Traits::pos_type;
37 using off_type = typename _Traits::off_type;
38 inline pos_type tellp() { return sp.tellp(); }
39 inline basic_ostreamfmt<_Elem, _Traits>& seekp(pos_type pos) { sp.seekp(pos); return *this; }
40 inline basic_ostreamfmt<_Elem, _Traits>& seekp(off_type off, std::ios_base::seekdir dir) { sp.seekp(off, dir); return *this; }
41 inline bool good() const noexcept { return sp.good(); }
42 inline bool eof() const noexcept { return sp.eof(); }
43 inline bool fail() const noexcept { return sp.fail(); }
44 inline bool bad() const noexcept { return sp.bad(); }
45
46 inline basic_ostreamfmt<_Elem, _Traits>& write(_In_reads_bytes_(size) const void* data, _In_ std::streamsize size)
47 {
48 sp.write(reinterpret_cast<const _Elem*>(data), size/sizeof(_Elem));
49 return *this;
50 }
51
52 template <class T>
53 inline basic_ostreamfmt<_Elem, _Traits>& write(_In_ T value)
54 {
55 HE2LE(&value);
56 sp.write(reinterpret_cast<const _Elem*>(&value), sizeof(T)/sizeof(_Elem));
57 return *this;
58 }
59
60 inline basic_ostreamfmt<_Elem, _Traits>& write(_In_z_ const char* value)
61 {
62 size_t count = strlen(value);
63 if (count > UINT32_MAX)
64 throw std::invalid_argument("string too big");
65 sp.write(static_cast<uint32_t>(count));
66 sp.write(reinterpret_cast<const _Elem*>(value), (std::streamsize)count * sizeof(char)/sizeof(_Elem));
67 return *this;
68 }
69
70 inline basic_ostreamfmt<_Elem, _Traits>& write(_In_z_ const wchar_t* value)
71 {
72 size_t count = strlen(value);
73 if (count > UINT32_MAX)
74 throw std::invalid_argument("string too big");
75 sp.write(static_cast<uint32_t>(count));
76#ifdef BIG_ENDIAN
77 for (size_t i = 0; i < count; ++i)
78 sp.write(value[i]);
79#else
80 sp.write(reinterpret_cast<const _Elem*>(value), (std::streamsize)count * sizeof(wchar_t)/sizeof(_Elem));
81#endif
82 return *this;
83 }
84
85 inline basic_ostreamfmt<_Elem, _Traits>& write_byte(_In_ uint8_t value)
86 {
87 return write(value);
88 }
89
97 template <class _Elem2>
98 void vprintf(_In_z_ _Printf_format_string_ const _Elem2 *format, _In_opt_ locale_t locale, _In_ va_list arg)
99 {
100 std::basic_string<_Elem2> str;
101 vappendf(str, format, locale, arg);
102 sp.write(reinterpret_cast<const _Elem*>(str.c_str()), str.size() * sizeof(_Elem2)/sizeof(_Elem));
103 }
104
111 template <class _Elem2>
112 void printf(_In_z_ _Printf_format_string_ const _Elem2 *format, _In_opt_ locale_t locale, ...)
113 {
114 va_list arg;
115 va_start(arg, locale);
116 vprintf(format, locale, arg);
117 va_end(arg);
118 }
119
120 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ int8_t value) { return write(value); }
121 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ int16_t value) { return write(value); }
122 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ int32_t value) { return write(value); }
123 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ int64_t value) { return write(value); }
124 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ uint8_t value) { return write(value); }
125 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ uint16_t value) { return write(value); }
126 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ uint32_t value) { return write(value); }
127 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ uint64_t value) { return write(value); }
128#if defined(_NATIVE_SIZE_T_DEFINED) && defined(_WIN64)
129 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ size_t value) { return write(value); }
130#endif
131 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ float value) { return write(value); }
132 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ double value) { return write(value); }
133 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ char value) { return write(value); }
134#ifdef _NATIVE_WCHAR_T_DEFINED
135 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_ wchar_t value) { return write(value); }
136#endif
137 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_z_ const char* value) { return write(value); }
138 inline basic_ostreamfmt<_Elem, _Traits>& operator <<(_In_z_ const wchar_t* value) { return write(value); }
139 };
140
141 using ostreamfmt = basic_ostreamfmt<char, std::char_traits<char>>;
142 using wostreamfmt = basic_ostreamfmt<wchar_t, std::char_traits<wchar_t>>;
143
147 template <class _Elem, class _Traits>
149 {
150 public:
151 std::basic_istream<_Elem, _Traits> &sg; // Read stream
152
153 inline basic_istreamfmt(_Inout_ std::basic_istream<_Elem, _Traits> &stream) : sg(stream) {}
154
155 using pos_type = typename _Traits::pos_type;
156 using off_type = typename _Traits::off_type;
157 inline pos_type tellg() { return sg.tellg(); }
158 inline basic_istreamfmt<_Elem, _Traits>& seekg(pos_type pos) { sg.seekg(pos); return *this; }
159 inline basic_istreamfmt<_Elem, _Traits>& seekg(off_type off, std::ios_base::seekdir dir) { sg.seekg(off, dir); return *this; }
160 inline bool good() const noexcept { return sg.good(); }
161 inline bool eof() const noexcept { return sg.eof(); }
162 inline bool fail() const noexcept { return sg.fail(); }
163 inline bool bad() const noexcept { return sg.bad(); }
164 inline std::streamsize gcount() const noexcept { return sg.gcount(); }
165
166 inline basic_istreamfmt<_Elem, _Traits>& read(_Out_writes_bytes_(size) void* data, std::streamsize size)
167 {
168 sg.read(reinterpret_cast<_Elem*>(data), size/sizeof(_Elem));
169 return *this;
170 }
171
172 template <class T>
173 inline basic_istreamfmt<_Elem, _Traits>& read(_Out_ T& value)
174 {
175 sg.read(reinterpret_cast<_Elem*>(&value), sizeof(T)/sizeof(_Elem));
176 if (sg.good())
177 LE2HE(&value);
178 return *this;
179 }
180
181 template <class _Traits = std::char_traits<char>, class _Alloc = std::allocator<char>>
182 inline basic_istreamfmt<_Elem, _Traits>& read(_Inout_ std::basic_string<char, _Traits, _Alloc>& value)
183 {
184 uint32_t count;
185 sg.read(count);
186 if (sg.good()) {
187 value.resize(count);
188 sg.read(reinterpret_cast<_Elem*>(&value[0]), (std::streamsize)count * sizeof(char)/sizeof(_Elem));
189 }
190 return *this;
191 }
192
193 template <class _Traits = std::char_traits<wchar_t>, class _Alloc = std::allocator<wchar_t>>
194 inline basic_istreamfmt<_Elem, _Traits>& read(_Inout_ std::basic_string<wchar_t, _Traits, _Alloc>& value)
195 {
196 uint32_t count;
197 sg.read(count);
198 if (sg.good()) {
199 value.resize(count);
200#ifdef BIG_ENDIAN
201 for (size_t i = 0; i < count; ++i)
202 sg.read(value[i]);
203#else
204 sg.read(reinterpret_cast<_Elem*>(&value[0]), (std::streamsize)count * sizeof(wchar_t)/sizeof(_Elem));
205#endif
206 }
207 return *this;
208 }
209
210 inline uint8_t read_byte()
211 {
212 uint8_t value;
213 read(value);
214 return value;
215 }
216
217 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ int8_t& value) { return read(value); }
218 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ int16_t& value) { return read(value); }
219 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ int32_t& value) { return read(value); }
220 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ int64_t& value) { return read(value); }
221 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ uint8_t& value) { return read(value); }
222 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ uint16_t& value) { return read(value); }
223 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ uint32_t& value) { return read(value); }
224 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ uint64_t& value) { return read(value); }
225#if defined(_NATIVE_SIZE_T_DEFINED) && defined(_WIN64)
226 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ size_t& value) { return read(value); }
227#endif
228 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ float& value) { return read(value); }
229 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ double& value) { return read(value); }
230 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ char& value) { return read(value); }
231#ifdef _NATIVE_WCHAR_T_DEFINED
232 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Out_ wchar_t& value) { return read(value); }
233#endif
234 template <class _Traits = std::char_traits<char>, class _Alloc = std::allocator<char>>
235 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Inout_ std::basic_string<char, _Traits, _Alloc>& value) { return read(value); }
236 template <class _Traits = std::char_traits<wchar_t>, class _Alloc = std::allocator<wchar_t>>
237 inline basic_istreamfmt<_Elem, _Traits>& operator >>(_Inout_ std::basic_string<wchar_t, _Traits, _Alloc>& value) { return read(value); }
238 };
239
242
246 template <class _Elem, class _Traits>
247 class basic_iostreamfmt : public basic_ostreamfmt<_Elem, _Traits>, public basic_istreamfmt<_Elem, _Traits>
248 {
249 public:
250 inline basic_iostreamfmt(_Inout_ std::basic_iostream<_Elem, _Traits> &stream) :
253 {}
254 };
255
258
262 template <class _Elem, class _Traits>
263 class basic_sharedstrbuf : public std::basic_streambuf<_Elem, _Traits>
264 {
265 public:
266 basic_sharedstrbuf(_In_reads_(size) const _Elem* data, _In_ size_t size)
267 {
268 std::basic_streambuf<_Elem, _Traits>::setg(const_cast<_Elem*>(data), const_cast<_Elem*>(data), const_cast<_Elem*>(data + size));
269 }
270
272 {
273 std::basic_streambuf<_Elem, _Traits>::setg(other.eback(), other.gptr(), other.egptr());
274 }
275
277 {
278 if (this != std::addressof(other))
279 std::basic_streambuf<_Elem, _Traits>::operator =(other);
280 return *this;
281 }
282
283 private:
286
287 protected:
288 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)
289 {
290 if (which & std::ios_base::in) {
291 _Elem* target;
292 switch (way) {
293 case std::ios_base::beg: target = eback() + static_cast<ptrdiff_t>(off); break;
294 case std::ios_base::cur: target = gptr() + static_cast<ptrdiff_t>(off); break;
295 case std::ios_base::end: target = egptr() + static_cast<ptrdiff_t>(off); break;
296 default: throw std::invalid_argument("invalid seek reference");
297 }
298 if (eback() <= target && target <= egptr()) {
299 gbump(static_cast<int>(target - gptr()));
300 return pos_type{ off_type{ target - eback() } };
301 }
302 }
303 return pos_type{ off_type{-1} };
304 }
305
306 virtual pos_type __CLR_OR_THIS_CALL seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
307 {
308 // change to specified position, according to mode
309 if (which & std::ios_base::in) {
310 _Elem* target = eback() + static_cast<size_t>(pos);
311 if (eback() <= target && target <= egptr()) {
312 gbump(static_cast<int>(target - gptr()));
313 return pos_type{ off_type{ target - eback() } };
314 }
315 }
316 return pos_type{ off_type{-1} };
317 }
318 };
319
320 template <class _Elem, class _Traits>
321 class basic_isharedstrstream : public std::basic_istream<_Elem, _Traits>
322 {
323 public:
324 basic_isharedstrstream(_In_reads_(size) const _Elem* data, _In_ size_t size) :
325 m_buf(data, size),
326 std::basic_istream<_Elem, _Traits>(&m_buf)
327 {}
328
329 protected:
331 };
332
335
336#ifdef _WIN32
338 template struct robber<getter<FILE*, std::filebuf>, &std::filebuf::_Myfile>;
339 template struct robber<getter<FILE*, std::wfilebuf>, &std::wfilebuf::_Myfile>;
340
341 inline FILE* filebuf_fhandle(_In_ std::filebuf* rb)
342 {
343 return (*rb).*get(getter<FILE*, std::filebuf>());
344 }
345
346 inline FILE* filebuf_fhandle(_In_ std::wfilebuf* rb)
347 {
348 return (*rb).*get(getter<FILE*, std::wfilebuf>());
349 }
351#endif
352
356 template <class _Elem, class _Traits>
357 class basic_fstream : public std::basic_fstream<_Elem, _Traits>
358 {
359 public:
360 using _Mybase = std::basic_fstream<_Elem, _Traits>;
361#if _HAS_CXX20
362 using time_point = std::chrono::time_point<std::chrono::file_clock>;
363#else
364 using time_point = std::chrono::time_point<std::chrono::system_clock>;
365#endif
366
367 basic_fstream() {}
368
369 explicit basic_fstream(
370 _In_z_ const char* file_name,
371 _In_ ios_base::openmode mode = ios_base::in | ios_base::out,
372 _In_ int prot = ios_base::_Default_open_prot) : _Mybase(file_name, mode, prot) {}
373
374 explicit basic_fstream(
375 _In_z_ const wchar_t* file_name,
376 _In_ ios_base::openmode mode = ios_base::in | ios_base::out,
377 _In_ int prot = ios_base::_Default_open_prot) : _Mybase(file_name, mode, prot) {}
378
379 template<class _Elem2, class _Traits2, class _Ax>
380 explicit basic_fstream(
381 _In_ const std::basic_string<_Elem2, _Traits2, _Ax>& str,
382 _In_ ios_base::openmode mode = ios_base::in | ios_base::out,
383 _In_ int prot = ios_base::_Default_open_prot) : basic_fstream(str.c_str(), mode, prot) {}
384
385 explicit basic_fstream(_In_ FILE* file) : _Mybase(file) {}
386
387 basic_fstream(_Inout_ basic_fstream&& other) : _Mybase(std::move(other)) {}
388
392 void truncate()
393 {
394 flush();
395 auto h = os_fhandle();
396#ifdef _WIN32
397 if (h == INVALID_HANDLE_VALUE)
398 throw std::runtime_error("invalid handle");
399 auto pos = tellp();
400 LONG
401 pos_lo = static_cast<LONG>(pos & 0xffffffff),
402 pos_hi = static_cast<LONG>((pos >> 32) & 0xffffffff);
403 if (SetFilePointer(h, pos_lo, &pos_hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
404 throw std::runtime_error("failed to seek");
405 if (!SetEndOfFile(h))
406 throw std::runtime_error("failed to truncate");
407#else
408#error Implement!
409#endif
410 }
411
417 time_point mtime() const
418 {
419 auto h = os_fhandle();
420#ifdef _WIN32
421 if (h == INVALID_HANDLE_VALUE)
422 throw std::runtime_error("invalid handle");
423 FILETIME ft;
424 if (!GetFileTime(h, NULL, NULL, &ft))
425 throw std::runtime_error("failed to get mtime");
426#if _HAS_CXX20
427 return time_point(time_point::duration(((static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime)));
428#else
429 // Adjust epoch to std::chrono::time_point<std::chrono::system_clock>/time_t.
430 return time_point(time_point::duration(((static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) - 116444736000000000ll));
431#endif
432#else
433#error Implement!
434#endif
435 }
436
437 protected:
438#ifdef _WIN32
439 HANDLE os_fhandle() const
440 {
441 FILE* f = filebuf_fhandle(rdbuf());
442 if (f == NULL)
443 return INVALID_HANDLE_VALUE;
444
445 int fd = _fileno(f);
446 if (fd == -1)
447 return INVALID_HANDLE_VALUE;
448
449 return (HANDLE)_get_osfhandle(fd);
450 }
451#else
452#error Implement!
453#endif
454 };
455
456 using fstream = basic_fstream<char, std::char_traits<char>>;
457 using wfstream = basic_fstream<wchar_t, std::char_traits<wchar_t>>;
458
462 template <class _Elem, class _Traits, class _Alloc>
463 class basic_stringstream : public std::basic_stringstream<_Elem, _Traits, _Alloc> {
464 public:
465 using _Mybase = std::basic_stringstream<_Elem, _Traits, _Alloc>;
466 using _Mystr = std::basic_string<_Elem, _Traits, _Alloc>;
467
469 explicit basic_stringstream(_In_ std::ios_base::openmode mode) : _Mybase(mode) {}
470 explicit basic_stringstream(_In_ const _Mystr& str, _In_ std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) : _Mybase(str, mode) {}
471 basic_stringstream(_Inout_ basic_stringstream&& other) : _Mybase(std::move(other)) {}
472
480 template <class T>
481 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) :
482 _Mybase(std::ios_base::in | std::ios_base::out | (mode & std::ios_base::binary | std::ios_base::app))
483 {
484 std::basic_ifstream<_Elem, _Traits> input(filename, mode & ~(std::ios_base::ate | std::ios_base::app), prot);
485 input.seekg(0, input.end);
486 auto size = input.tellg();
487 if (size > SIZE_MAX)
488 throw std::runtime_error("file too big to fit into memory");
489 str().reserve(static_cast<size_t>(size));
490 input.seekg(0);
491 do {
492 _Elem buf[0x1000];
493 input.read(buf, _countof(buf));
494 write(buf, input.gcount());
495 } while (!input.eof());
496 if (!(mode & (std::ios_base::ate | std::ios_base::app)))
497 seekp(0);
498 }
499
507 template <class _Elem2, class _Traits2 = std::char_traits<_Elem2>, class _Alloc2 = std::allocator<_Elem2>>
508 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) :
509 basic_stringstream(filename.c_str(), mode, prot)
510 {}
511
519 template <class T>
520 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)
521 {
522 std::basic_ofstream<_Elem, _Traits> output(filename, mode, prot);
523 auto origin = tellg();
524 seekg(0, end);
525 auto size = tellg();
526 do {
527 _Elem buf[0x1000];
528 read(buf, _countof(buf));
529 output.write(buf, gcount());
530 } while (!eof());
531 seekg(origin);
532 }
533
534 template <class _Elem2, class _Traits2 = std::char_traits<T>, class _Alloc2 = std::allocator<T>>
535 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)
536 {
537 save(filename.data(), mode, prot);
538 }
539 };
540
541 using stringstream = basic_stringstream<char, std::char_traits<char>, std::allocator<char>>;
542 using wstringstream = basic_stringstream<wchar_t, std::char_traits<wchar_t>, std::allocator<char>>;
543}
File stream with additional std::filesystem features.
Definition ios.hpp:358
time_point mtime() const
Returns file modification time.
Definition ios.hpp:417
void truncate()
Sets end of file at current put position.
Definition ios.hpp:392
Binary stream reader/writer.
Definition ios.hpp:248
Definition ios.hpp:322
Binary stream reader.
Definition ios.hpp:149
Binary stream writer.
Definition ios.hpp:30
void vprintf(const _Elem2 *format, locale_t locale, va_list arg)
Formats string using printf() and write it to stream.
Definition ios.hpp:98
void printf(const _Elem2 *format, locale_t locale,...)
Formats string using printf() and write it to stream.
Definition ios.hpp:112
Shared-memory string buffer.
Definition ios.hpp:264
String stream.
Definition ios.hpp:463
basic_stringstream(const T *filename, std::ios_base::openmode mode=std::ios_base::in, int prot=std::ios_base::_Default_open_prot)
Initializes stream with content from file.
Definition ios.hpp:481
void save(const T *filename, std::ios_base::openmode mode=std::ios_base::out, int prot=std::ios_base::_Default_open_prot)
Saves stream content to a file.
Definition ios.hpp:520
basic_stringstream(const std::basic_string< _Elem2, _Traits2, _Alloc2 > &filename, std::ios_base::openmode mode=std::ios_base::in, int prot=std::ios_base::_Default_open_prot)
Initializes stream with content from file.
Definition ios.hpp:508
Helper template to allow access to internal std C++ private members.
Definition internal.hpp:30
Helper template to allow access to internal std C++ private members.
Definition internal.hpp:18