stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
stream.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2023 Amebis
4*/
5
6#pragma once
7
8#include "compat.hpp"
9#include "endian.hpp"
10#include "interval.hpp"
11#include "math.hpp"
12#include "ring.hpp"
13#include "string.hpp"
14#include "unicode.hpp"
15#include <stdint.h>
16#include <stdlib.h>
17#if defined(_WIN32)
18#include "windows.h"
19#include <asptlb.h>
20#include <objidl.h>
21#include <WinSock2.h>
22#else
23#include <fcntl.h>
24#include <unistd.h>
25#include <sys/stat.h>
26#endif
27#include <chrono>
28#include <condition_variable>
29#include <list>
30#include <memory>
31#include <set>
32#include <string>
33#include <thread>
34#include <vector>
35
36#if !defined(SET_FILE_OP_TIMES) && defined(RDAT_BELEZI_CAS_DOSTOPA_VER)
37#define SET_FILE_OP_TIMES 1
38#pragma message("RDAT_BELEZI_CAS_DOSTOPA_VER is deprecated. Use SET_FILE_OP_TIMES instead.")
39#elif !defined(SET_FILE_OP_TIMES)
40#define SET_FILE_OP_TIMES 0
41#endif
42#if !defined(CHECK_STREAM_STATE) && defined(RDAT_NE_PREVERJAJ_STANJA_VER)
43#define CHECK_STREAM_STATE 0
44#pragma message("RDAT_NE_PREVERJAJ_EOF_VER is deprecated. Use CHECK_STREAM_STATE=0 instead.")
45#else
46#define CHECK_STREAM_STATE 1
47#endif
48
49namespace stdex
50{
51 namespace stream
52 {
56 enum class state_t {
57 ok = 0,
58 eof,
59 fail,
60 };
61
65 using fsize_t = uint64_t;
66 constexpr fsize_t fsize_max = UINT64_MAX;
67
68 constexpr size_t iterate_count = 0x10;
69 constexpr size_t default_block_size = 0x10000;
70 constexpr char16_t utf16_bom = u'\ufeff';
71 constexpr char32_t utf32_bom = U'\ufeff';
72 constexpr const char utf8_bom[3] = { '\xef', '\xbb', '\xbf' };
73
77 class basic
78 {
79 public:
80 basic(_In_ state_t state = state_t::ok) : m_state(state) {}
81
82 virtual ~basic() noexcept(false) {}
83
95 virtual _Success_(return != 0 || length == 0) size_t read(
96 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
97 {
98 _Unreferenced_(data);
99 _Unreferenced_(length);
100 m_state = state_t::fail;
101 return 0;
102 }
103
113 virtual _Success_(return != 0) size_t write(
114 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
115 {
116 _Unreferenced_(data);
117 _Unreferenced_(length);
118 m_state = state_t::fail;
119 return 0;
120 }
121
125 virtual void flush()
126 {
127 m_state = state_t::ok;
128 }
129
133 virtual void close()
134 {
135 m_state = state_t::ok;
136 }
137
141 virtual void skip(_In_ fsize_t amount)
142 {
143 if (amount == 1)
144 read_byte();
145 else if (amount < iterate_count) {
146 for (size_t i = 0; i < static_cast<size_t>(amount); i++) {
147 read_byte();
148 if (!ok()) _Unlikely_
149 break;
150 }
151 }
152 else {
153 size_t block = static_cast<size_t>(std::min<fsize_t>(amount, default_block_size));
154 try {
155 std::unique_ptr<uint8_t[]> dummy(new uint8_t[block]);
156 while (amount) {
157 amount -= read_array(dummy.get(), sizeof(uint8_t), static_cast<size_t>(std::min<fsize_t>(amount, block)));
158 if (!ok()) _Unlikely_
159 break;
160 }
161 }
162 catch (const std::bad_alloc&) { m_state = state_t::fail; }
163 }
164 }
165
169 inline state_t state() const { return m_state; };
170
174 inline bool ok() const { return m_state == state_t::ok; };
175
183 virtual std::vector<uint8_t> read_remainder(_In_ size_t max_length = SIZE_MAX)
184 {
185 std::vector<uint8_t> result;
186 size_t offset, length;
187 offset = 0;
188 length = default_block_size;
189 while (offset < max_length) {
190 length = std::min(length, max_length);
191 try { result.resize(length); }
192 catch (const std::bad_alloc&) {
193 m_state = state_t::fail;
194 return result;
195 }
196 auto num_read = read_array(result.data() + offset, sizeof(uint8_t), length - offset);
197 offset += num_read;
198 if (!ok()) _Unlikely_
199 break;
200 length += default_block_size;
201 }
202 result.resize(offset);
203 return result;
204 }
205
209 inline uint8_t read_byte()
210 {
211 uint8_t byte;
212 if (read_array(&byte, sizeof(byte), 1) == 1)
213 return byte;
214 throw std::system_error(sys_error(), std::system_category(), "failed to read");
215 }
216
220 void write_byte(_In_ uint8_t byte, _In_ fsize_t amount = 1)
221 {
222 if (amount == 1)
223 write(&byte, sizeof(uint8_t));
224 else if (amount < iterate_count) {
225 for (size_t i = 0; i < static_cast<size_t>(amount); i++) {
226 write(&byte, sizeof(uint8_t));
227 if (!ok()) _Unlikely_
228 break;
229 }
230 }
231 else {
232 size_t block = static_cast<size_t>(std::min<fsize_t>(amount, default_block_size));
233 try {
234 std::unique_ptr<uint8_t[]> dummy(new uint8_t[block]);
235 memset(dummy.get(), byte, block);
236 while (amount) {
237 amount -= write_array(dummy.get(), sizeof(uint8_t), static_cast<size_t>(std::min<fsize_t>(amount, block)));
238 if (!ok()) _Unlikely_
239 break;
240 }
241 }
242 catch (const std::bad_alloc&) { m_state = state_t::fail; }
243 }
244 }
245
257 template <class T>
258 inline basic& read_data(_Out_ T& data)
259 {
260 if (!ok()) _Unlikely_ {
261 data = 0;
262 return *this;
263 }
264 if (read_array(&data, sizeof(T), 1) == 1)
265 (void)LE2HE(&data);
266 else {
267 data = 0;
268 if (ok())
269 m_state = state_t::eof;
270 }
271 return *this;
272 }
273
285 template <class T>
286 inline basic& write_data(_In_ const T data)
287 {
288 if (!ok()) _Unlikely_
289 return *this;
290#if BYTE_ORDER == BIG_ENDIAN
291 T data_le = HE2LE(data);
292 write(&data_le, sizeof(T));
293#else
294 write(&data, sizeof(T));
295#endif
296 return *this;
297 }
298
304 template<class _Traits = std::char_traits<char>, class _Ax = std::allocator<char>>
305 inline size_t readln(_Inout_ std::basic_string<char, _Traits, _Ax>& str)
306 {
307 str.clear();
308 return readln_and_attach(str);
309 }
310
316 template<class _Traits = std::char_traits<wchar_t>, class _Ax = std::allocator<wchar_t>>
317 inline size_t readln(_Inout_ std::basic_string<wchar_t, _Traits, _Ax>& wstr)
318 {
319 wstr.clear();
320 return readln_and_attach(wstr);
321 }
322
328 template<class T_from, class T_to, class _Traits = std::char_traits<T_to>, class _Ax = std::allocator<T_to>>
329 size_t readln(_Inout_ std::basic_string<T_to, _Traits, _Ax>& wstr, _In_ charset_encoder<T_from, T_to>& encoder)
330 {
331 if (encoder.from_encoding() == encoder.to_encoding())
332 return readln(wstr);
333 std::string str;
335 encoder.strcpy(wstr, str);
336 return wstr.size();
337 }
338
344 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
345 size_t readln_and_attach(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
346 {
347 bool initial = true;
348 _Elem chr, previous = (_Elem)0;
349 do {
350 read_array(&chr, sizeof(_Elem), 1);
351 if (!initial && !(previous == static_cast<_Elem>('\r') && chr == static_cast<_Elem>('\n')))
352 str += previous;
353 else
354 initial = false;
355 previous = chr;
356 } while (ok() && chr != static_cast<_Elem>('\n'));
357 return str.size();
358 }
359
365 template<class T_from, class T_to, class _Traits = std::char_traits<T_to>, class _Ax = std::allocator<T_to>>
366 size_t readln_and_attach(_Inout_ std::basic_string<T_to, _Traits, _Ax>& wstr, _In_ charset_encoder<T_from, T_to>& encoder)
367 {
368 if (encoder.from_encoding() == encoder.to_encoding())
369 return readln_and_attach(wstr);
370 std::string str;
372 encoder.strcat(wstr, str);
373 return wstr.size();
374 }
375
381 size_t read_array(_Out_writes_bytes_(size* count) void* array, _In_ size_t size, _In_ size_t count)
382 {
383 for (size_t to_read = mul(size, count);;) {
384 size_t num_read = read(array, to_read);
385 to_read -= num_read;
386 if (!to_read)
387 return count;
388 if (!ok()) _Unlikely_
389 return count - to_read / size;
390 reinterpret_cast<uint8_t*&>(array) += num_read;
391 }
392 }
393
399 inline size_t write_array(_In_reads_bytes_opt_(size* count) const void* array, _In_ size_t size, _In_ size_t count)
400 {
401 return write(array, mul(size, count)) / size;
402 }
403
412 template <class T_from, class T_to>
413 size_t write_array(_In_z_ const T_from* wstr, _In_ charset_encoder<T_from, T_to>& encoder)
414 {
415 if (!ok()) _Unlikely_
416 return 0;
417 size_t num_chars = stdex::strlen(wstr);
418 if (encoder.from_encoding() == encoder.to_encoding())
419 return write_array(wstr, sizeof(T_from), num_chars);
420 std::basic_string<T_to> str(encoder.convert(wstr, num_chars));
421 return write_array(str.data(), sizeof(T_to), str.size());
422 }
423
433 template <class T_from, class T_to>
434 size_t write_array(_In_reads_or_z_opt_(num_chars) const T_from* wstr, _In_ size_t num_chars, _In_ charset_encoder<T_from, T_to>& encoder)
435 {
436 if (!ok()) _Unlikely_
437 return 0;
438 num_chars = stdex::strnlen(wstr, num_chars);
439 if (encoder.from_encoding() == encoder.to_encoding())
440 return write_array(wstr, sizeof(T_from), num_chars);
441 std::basic_string<T_to> str(encoder.convert(wstr, num_chars));
442 return write_array(str.data(), sizeof(T_to), str.size());
443 }
444
453 template<class T_from, class T_to, class _Traits = std::char_traits<T_from>, class _Ax = std::allocator<T_from>>
454 size_t write_array(_In_ const std::basic_string<T_from, _Traits, _Ax>& wstr, _In_ charset_encoder<T_from, T_to>& encoder)
455 {
456 if (!ok()) _Unlikely_
457 return 0;
458 if (encoder.from_encoding() == encoder.to_encoding())
459 return write_array(wstr.data(), sizeof(T_from), wstr.size());
460 std::basic_string<T_to> str(encoder.convert(wstr));
461 return write_array(str.data(), sizeof(T_to), str.size());
462 }
463
475 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
476 inline basic& read_str(_Out_ std::basic_string<_Elem, _Traits, _Ax>& data)
477 {
478 data.clear();
479 if (!ok()) _Unlikely_
480 return *this;
481 uint32_t num_chars;
482 read_data(num_chars);
483 if (!ok()) _Unlikely_
484 return *this;
485 data.reserve(num_chars);
486 for (;;) {
487 _Elem buf[0x400];
488 uint32_t num_read = static_cast<uint32_t>(read_array(buf, sizeof(_Elem), std::min<uint32_t>(num_chars, _countof(buf))));
489 data.append(buf, buf + num_read);
490 num_chars -= num_read;
491 if (!num_chars || !ok())
492 return *this;
493 }
494 }
495
507 template <class T>
508 inline basic& write_str(_In_z_ const T* data)
509 {
510 // Stream state will be checked in write_data.
511 size_t num_chars = stdex::strlen(data);
512 if (num_chars > UINT32_MAX)
513 throw std::invalid_argument("string too long");
514 write_data(static_cast<uint32_t>(num_chars));
515 if (!ok()) _Unlikely_
516 return *this;
517 write_array(data, sizeof(T), num_chars);
518 return *this;
519 }
520
532 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
533 inline basic& write_str(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data)
534 {
535 // Stream state will be checked in write_data.
536 size_t num_chars = data.size();
537 if (num_chars > UINT32_MAX)
538 throw std::invalid_argument("string too long");
539 write_data(static_cast<uint32_t>(num_chars));
540 if (!ok()) _Unlikely_
541 return *this;
542 write_array(data.data(), sizeof(_Elem), num_chars);
543 return *this;
544 }
545
546#ifdef _WIN32
552 size_t write_sa(_In_ LPSAFEARRAY sa)
553 {
554 safearray_accessor<void> a(sa);
555 long ubound, lbound;
556 if (FAILED(SafeArrayGetUBound(sa, 1, &ubound)) ||
557 FAILED(SafeArrayGetLBound(sa, 1, &lbound)))
558 throw std::invalid_argument("SafeArrayGet[UL]Bound failed");
559 return write(a.data(), static_cast<size_t>(ubound) - lbound + 1);
560 }
561#endif
562
568 fsize_t write_stream(_Inout_ basic& stream, _In_ fsize_t amount = fsize_max)
569 {
570 std::unique_ptr<uint8_t[]> data(new uint8_t[static_cast<size_t>(std::min<fsize_t>(amount, default_block_size))]);
571 fsize_t num_copied = 0, to_write = amount;
572 m_state = state_t::ok;
573 while (to_write) {
574 size_t num_read = stream.read(data.get(), static_cast<size_t>(std::min<fsize_t>(default_block_size, to_write)));
575 size_t num_written = write(data.get(), num_read);
576 num_copied += num_written;
577 to_write -= num_written;
578 if (stream.m_state == state_t::eof) {
579 // EOF is not an error.
580 m_state = state_t::ok;
581 break;
582 }
583 m_state = stream.m_state;
584 if (!ok())
585 break;
586 }
587 return num_copied;
588 }
589
593 void write_charset(_In_ charset_id charset)
594 {
595 if (charset == charset_id::utf32)
596 write_data(utf32_bom);
597 else if (charset == charset_id::utf16)
598 write_data(utf16_bom);
599 else if (charset == charset_id::utf8)
600 write_array(utf8_bom, sizeof(utf8_bom), 1);
601 }
602
608 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, ...)
609 {
610 va_list params;
611 va_start(params, locale);
612 size_t num_chars = write_vsprintf(format, locale, params);
613 va_end(params);
614 return num_chars;
615 }
616
622 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, ...)
623 {
624 va_list params;
625 va_start(params, locale);
626 size_t num_chars = write_vsprintf(format, locale, params);
627 va_end(params);
628 return num_chars;
629 }
630
636 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, _In_ va_list params)
637 {
638 std::string str;
639 str.reserve(default_block_size);
640 vappendf(str, format, locale, params);
641 return write_array(str.data(), sizeof(char), str.size());
642 }
643
649 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, _In_ va_list params)
650 {
651 std::wstring str;
652 str.reserve(default_block_size);
653 vappendf(str, format, locale, params);
654 return write_array(str.data(), sizeof(wchar_t), str.size());
655 }
656
657 inline basic& operator >>(_Out_ int8_t& data) { return read_data(data); }
658 inline basic& operator <<(_In_ const int8_t data) { return write_data(data); }
659 inline basic& operator >>(_Out_ int16_t& data) { return read_data(data); }
660 inline basic& operator <<(_In_ const int16_t data) { return write_data(data); }
661 inline basic& operator >>(_Out_ int32_t& data) { return read_data(data); }
662 inline basic& operator <<(_In_ const int32_t data) { return write_data(data); }
663 inline basic& operator >>(_Out_ int64_t& data) { return read_data(data); }
664 inline basic& operator <<(_In_ const int64_t data) { return write_data(data); }
665 inline basic& operator >>(_Out_ uint8_t& data) { return read_data(data); }
666 inline basic& operator <<(_In_ const uint8_t data) { return write_data(data); }
667 inline basic& operator >>(_Out_ uint16_t& data) { return read_data(data); }
668 inline basic& operator <<(_In_ const uint16_t data) { return write_data(data); }
669 inline basic& operator >>(_Out_ uint32_t& data) { return read_data(data); }
670 inline basic& operator <<(_In_ const uint32_t data) { return write_data(data); }
671 inline basic& operator >>(_Out_ uint64_t& data) { return read_data(data); }
672 inline basic& operator <<(_In_ const uint64_t data) { return write_data(data); }
673 inline basic& operator >>(_Out_ float& data) { return read_data(data); }
674 inline basic& operator <<(_In_ const float data) { return write_data(data); }
675 inline basic& operator >>(_Out_ double& data) { return read_data(data); }
676 inline basic& operator <<(_In_ const double data) { return write_data(data); }
677 inline basic& operator >>(_Out_ char& data) { return read_data(data); }
678 inline basic& operator <<(_In_ const char data) { return write_data(data); }
679#ifdef _NATIVE_WCHAR_T_DEFINED
680 inline basic& operator >>(_Out_ wchar_t& data) { return read_data(data); }
681 inline basic& operator <<(_In_ const wchar_t data) { return write_data(data); }
682#endif
683 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
684 inline basic& operator >>(_Out_ std::basic_string<_Elem, _Traits, _Ax>& data) { return read_str(data); }
685 template <class T>
686 inline basic& operator <<(_In_ const T* data) { return write_str(data); }
687 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
688 inline basic& operator <<(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data) { return write_str(data); }
689
690 template <class _Ty, class _Alloc = std::allocator<_Ty>>
691 basic& operator <<(_In_ const std::vector<_Ty, _Alloc>& data)
692 {
693 size_t num = data.size();
694 if (num > UINT32_MAX) _Unlikely_
695 throw std::invalid_argument("collection too big");
696 *this << static_cast<uint32_t>(num);
697 for (auto& el : data)
698 *this << el;
699 return *this;
700 }
701
702 template <class _Ty, class _Alloc = std::allocator<_Ty>>
703 basic& operator >>(_Out_ std::vector<_Ty, _Alloc>& data)
704 {
705 data.clear();
706 uint32_t num;
707 *this >> num;
708 if (!ok()) _Unlikely_
709 return *this;
710 data.reserve(num);
711 for (uint32_t i = 0; i < num; ++i) {
712 _Ty el;
713 *this >> el;
714 if (!ok()) _Unlikely_
715 return *this;
716 data.push_back(std::move(el));
717 }
718 }
719
720 template <class _Kty, class _Pr = std::less<_Kty>, class _Alloc = std::allocator<_Kty>>
721 basic& operator <<(_In_ const std::set<_Kty, _Pr, _Alloc>& data)
722 {
723 size_t num = data.size();
724 if (num > UINT32_MAX) _Unlikely_
725 throw std::invalid_argument("collection too big");
726 *this << static_cast<uint32_t>(num);
727 for (auto& el : data)
728 *this << el;
729 return *this;
730 }
731
732 template <class _Kty, class _Pr = std::less<_Kty>, class _Alloc = std::allocator<_Kty>>
733 basic& operator >>(_Out_ std::set<_Kty, _Pr, _Alloc>& data)
734 {
735 data.clear();
736 uint32_t num;
737 *this >> num;
738 if (!ok()) _Unlikely_
739 return *this;
740 for (uint32_t i = 0; i < num; ++i) {
741 _Kty el;
742 *this >> el;
743 if (!ok()) _Unlikely_
744 return *this;
745 data.insert(std::move(el));
746 }
747 }
748
749 template <class _Kty, class _Pr = std::less<_Kty>, class _Alloc = std::allocator<_Kty>>
750 basic& operator <<(_In_ const std::multiset<_Kty, _Pr, _Alloc>& data)
751 {
752 size_t num = data.size();
753 if (num > UINT32_MAX) _Unlikely_
754 throw std::invalid_argument("collection too big");
755 *this << static_cast<uint32_t>(num);
756 for (auto& el : data)
757 *this << el;
758 return *this;
759 }
760
761 template <class _Kty, class _Pr = std::less<_Kty>, class _Alloc = std::allocator<_Kty>>
762 basic& operator >>(_Out_ std::multiset<_Kty, _Pr, _Alloc>& data)
763 {
764 data.clear();
765 uint32_t num;
766 *this >> num;
767 if (!ok()) _Unlikely_
768 return *this;
769 for (uint32_t i = 0; i < num; ++i) {
770 _Kty el;
771 *this >> el;
772 if (!ok()) _Unlikely_
773 return *this;
774 data.insert(std::move(el));
775 }
776 return *this;
777 }
778
779 protected:
780 state_t m_state;
781 };
782
786 using fpos_t = uint64_t;
787 constexpr fpos_t fpos_max = UINT64_MAX;
788 constexpr fpos_t fpos_min = 0;
789
793 using foff_t = int64_t;
794 constexpr foff_t foff_max = INT64_MAX;
795 constexpr foff_t foff_min = INT64_MIN;
796
800 enum class seek_t {
801#ifdef _WIN32
802 beg = FILE_BEGIN,
803 cur = FILE_CURRENT,
804 end = FILE_END
805#else
806 beg = SEEK_SET,
807 cur = SEEK_CUR,
808 end = SEEK_END
809#endif
810 };
811
812#if _HAS_CXX20
813 using clock = std::chrono::file_clock;
814#else
815 using clock = std::chrono::system_clock;
816#endif
817 using time_point = std::chrono::time_point<clock>;
818
822 class basic_file : virtual public basic
823 {
824 public:
825 virtual std::vector<uint8_t> read_remainder(_In_ size_t max_length = SIZE_MAX)
826 {
827 size_t length = std::min<size_t>(max_length, static_cast<size_t>(size() - tell()));
828 std::vector<uint8_t> result;
829 try { result.resize(length); }
830 catch (const std::bad_alloc&) {
831 m_state = state_t::fail;
832 return result;
833 }
834 result.resize(read_array(result.data(), sizeof(uint8_t), length));
835 return result;
836 }
837
843 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg) = 0;
844
850 inline fpos_t seekbeg(_In_ fpos_t offset) { return seek(offset, seek_t::beg); }
851
857 inline fpos_t seekcur(_In_ foff_t offset) { return seek(offset, seek_t::cur); }
858
864 inline fpos_t seekend(_In_ foff_t offset) { return seek(offset, seek_t::end); }
865
866 virtual void skip(_In_ fsize_t amount)
867 {
868 seek(amount, seek_t::cur);
869 }
870
877 virtual fpos_t tell() const = 0;
878
882 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
883 {
884 _Unreferenced_(offset);
885 _Unreferenced_(length);
886 throw std::domain_error("not implemented");
887 }
888
892 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
893 {
894 _Unreferenced_(offset);
895 _Unreferenced_(length);
896 throw std::domain_error("not implemented");
897 }
898
903 virtual fsize_t size() = 0;
904
908 virtual void truncate() = 0;
909
913 virtual time_point ctime() const
914 {
915 return time_point::min();
916 }
917
921 virtual time_point atime() const
922 {
923 return time_point::min();
924 }
925
929 virtual time_point mtime() const
930 {
931 return time_point::min();
932 }
933
937 virtual void set_ctime(time_point date)
938 {
939 _Unreferenced_(date);
940 throw std::domain_error("not implemented");
941 }
942
946 virtual void set_atime(time_point date)
947 {
948 _Unreferenced_(date);
949 throw std::domain_error("not implemented");
950 }
951
955 virtual void set_mtime(time_point date)
956 {
957 _Unreferenced_(date);
958 throw std::domain_error("not implemented");
959 }
960
961#ifdef _WIN32
965 LPSAFEARRAY read_sa()
966 {
967 _Assume_(size() <= SIZE_MAX);
968 size_t length = static_cast<size_t>(size());
969 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(SafeArrayCreateVector(VT_UI1, 0, (ULONG)length));
970 if (!sa) _Unlikely_
971 throw std::runtime_error("SafeArrayCreateVector failed");
972 safearray_accessor<void> a(sa.get());
973 if (seek(0) != 0) _Unlikely_
974 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
975 if (read_array(a.data(), 1, length) != length)
976 throw std::system_error(sys_error(), std::system_category(), "failed to read");
977 return sa.release();
978 }
979#endif
980
986 charset_id read_charset(_In_ charset_id default_charset = charset_id::system)
987 {
988 if (seek(0) != 0) _Unlikely_
989 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
990 char32_t id_utf32;
991 read_array(&id_utf32, sizeof(char32_t), 1);
992 if (ok() && id_utf32 == utf32_bom)
993 return charset_id::utf32;
994
995 if (seek(0) != 0) _Unlikely_
996 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
997 char16_t id_utf16;
998 read_array(&id_utf16, sizeof(char16_t), 1);
999 if (ok() && id_utf16 == utf16_bom)
1000 return charset_id::utf16;
1001
1002 if (seek(0) != 0) _Unlikely_
1003 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1004 char id_utf8[3] = { 0 };
1005 read_array(id_utf8, sizeof(id_utf8), 1);
1006 if (ok() && strncmp(id_utf8, _countof(id_utf8), utf8_bom, _countof(utf8_bom)) == 0)
1007 return charset_id::utf8;
1008
1009 if (seek(0) != 0) _Unlikely_
1010 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1011 return default_charset;
1012 }
1013 };
1014
1020 class converter : public basic
1021 {
1022 protected:
1024#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1025 explicit converter() : basic(state_t::fail) {}
1026
1027 void init(_Inout_ basic& source)
1028 {
1029 m_source = &source;
1030 init();
1031 }
1032
1033 void init()
1034 {
1035 m_state = m_source->state();
1036 }
1037
1038 void done()
1039 {
1040 m_source = nullptr;
1041 }
1043
1044 public:
1045 converter(_Inout_ basic& source) :
1046 basic(source.state()),
1047 m_source(&source)
1048 {}
1049
1050 virtual _Success_(return != 0 || length == 0) size_t read(
1051 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1052 {
1053 size_t num_read = m_source->read(data, length);
1054 m_state = m_source->state();
1055 return num_read;
1056 }
1057
1058 virtual _Success_(return != 0) size_t write(
1059 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1060 {
1061 size_t num_written = m_source->write(data, length);
1062 m_state = m_source->state();
1063 return num_written;
1064 }
1065
1066 virtual void close()
1067 {
1068 m_source->close();
1069 m_state = m_source->state();
1070 }
1071
1072 virtual void flush()
1073 {
1074 m_source->flush();
1075 m_state = m_source->state();
1076 }
1077
1078 protected:
1079 basic* m_source;
1080 };
1081
1085 class replicator : public basic
1086 {
1087 public:
1088 virtual ~replicator()
1089 {
1090 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1091 auto _w = w->get();
1092 {
1093 const std::lock_guard<std::mutex> lk(_w->mutex);
1094 _w->op = worker::op_t::quit;
1095 }
1096 _w->cv.notify_one();
1097 }
1098 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w)
1099 w->get()->join();
1100 }
1101
1105 void push_back(_In_ basic* source)
1106 {
1107 m_workers.push_back(std::unique_ptr<worker>(new worker(source)));
1108 }
1109
1113 void remove(basic* source)
1114 {
1115 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1116 auto _w = w->get();
1117 if (_w->source == source) {
1118 {
1119 const std::lock_guard<std::mutex> lk(_w->mutex);
1120 _w->op = worker::op_t::quit;
1121 }
1122 _w->cv.notify_one();
1123 _w->join();
1124 m_workers.erase(w);
1125 return;
1126 }
1127 }
1128 }
1129
1130 virtual _Success_(return != 0) size_t write(
1131 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1132 {
1133 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1134 auto _w = w->get();
1135 {
1136 const std::lock_guard<std::mutex> lk(_w->mutex);
1137 _w->op = worker::op_t::write;
1138 _w->data = data;
1139 _w->length = length;
1140 }
1141 _w->cv.notify_one();
1142 }
1143 size_t num_written = length;
1144 m_state = state_t::ok;
1145 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1146 auto _w = w->get();
1147 std::unique_lock<std::mutex> lk(_w->mutex);
1148 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1149 if (_w->num_written < num_written)
1150 num_written = _w->num_written;
1151 if (ok() && !_w->source->ok())
1152 m_state = _w->source->state();
1153 }
1154 return num_written;
1155 }
1156
1157 virtual void close()
1158 {
1159 foreach_worker(worker::op_t::close);
1160 }
1161
1162 virtual void flush()
1163 {
1164 foreach_worker(worker::op_t::flush);
1165 }
1166
1167 protected:
1168 class worker : public std::thread
1169 {
1170 public:
1171 worker(_In_ basic* _source) :
1172 source(_source),
1173 op(op_t::noop),
1174 data(nullptr),
1175 length(0),
1176 num_written(0)
1177 {
1178 *static_cast<std::thread*>(this) = std::thread([](_Inout_ worker& w) { w.process_op(); }, std::ref(*this));
1179 }
1180
1181 protected:
1182 void process_op()
1183 {
1184 for (;;) {
1185 std::unique_lock<std::mutex> lk(mutex);
1186 cv.wait(lk, [&] {return op != op_t::noop; });
1187 switch (op) {
1188 case op_t::quit:
1189 return;
1190 case op_t::write:
1191 num_written = source->write(data, length);
1192 break;
1193 case op_t::close:
1194 source->close();
1195 break;
1196 case op_t::flush:
1197 source->flush();
1198 break;
1199 case op_t::noop:;
1200 }
1201 op = op_t::noop;
1202 lk.unlock();
1203 cv.notify_one();
1204 }
1205 }
1206
1207 public:
1208 basic* source;
1209 enum class op_t {
1210 noop = 0,
1211 quit,
1212 write,
1213 close,
1214 flush,
1215 } op;
1216 const void* data;
1217 size_t length;
1219 std::mutex mutex;
1220 std::condition_variable cv;
1221 };
1222
1223 void foreach_worker(_In_ worker::op_t op)
1224 {
1225 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1226 auto _w = w->get();
1227 {
1228 const std::lock_guard<std::mutex> lk(_w->mutex);
1229 _w->op = op;
1230 }
1231 _w->cv.notify_one();
1232 }
1233 m_state = state_t::ok;
1234 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1235 auto _w = w->get();
1236 std::unique_lock<std::mutex> lk(_w->mutex);
1237 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1238 if (ok())
1239 m_state = _w->source->state();
1240 }
1241 }
1242
1243 std::list<std::unique_ptr<worker>> m_workers;
1244 };
1245
1246 constexpr size_t default_async_limit = 0x100000;
1247
1253 template <size_t CAPACITY = default_async_limit>
1255 {
1256 public:
1257 async_reader(_Inout_ basic& source) :
1258 converter(source),
1259 m_worker([](_Inout_ async_reader& w) { w.process(); }, std::ref(*this))
1260 {}
1261
1262 virtual ~async_reader()
1263 {
1264 m_ring.quit();
1265 m_worker.join();
1266 }
1267
1268#pragma warning(suppress: 6101) // See [1] below
1269 virtual _Success_(return != 0 || length == 0) size_t read(
1270 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1271 {
1272 _Assume_(data || !length);
1273 for (size_t to_read = length;;) {
1274 uint8_t* ptr; size_t num_read;
1275 std::tie(ptr, num_read) = m_ring.front();
1276 if (!ptr) _Unlikely_ {
1277 m_state = to_read < length || !length ? state_t::ok : m_source->state();
1278 return length - to_read; // [1] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
1279 }
1280 if (to_read < num_read)
1281 num_read = to_read;
1282 memcpy(data, ptr, num_read);
1283 m_ring.pop(num_read);
1284 to_read -= num_read;
1285 if (!to_read) {
1286 m_state = state_t::ok;
1287 return length;
1288 }
1289 reinterpret_cast<uint8_t*&>(data) += num_read;
1290 }
1291 }
1292
1293 protected:
1294 void process()
1295 {
1296 for (;;) {
1297 uint8_t* ptr; size_t num_write;
1298 std::tie(ptr, num_write) = m_ring.back();
1299 if (!ptr) _Unlikely_
1300 break;
1301 num_write = m_source->read(ptr, num_write);
1302 m_ring.push(num_write);
1303 if (!m_source->ok()) {
1304 m_ring.quit();
1305 break;
1306 }
1307 }
1308 }
1309
1310 protected:
1311 ring<uint8_t, CAPACITY> m_ring;
1312 std::thread m_worker;
1313 };
1314
1320 template <size_t CAPACITY = default_async_limit>
1322 {
1323 public:
1324 async_writer(_Inout_ basic& source) :
1325 converter(source),
1326 m_worker([](_Inout_ async_writer& w) { w.process(); }, std::ref(*this))
1327 {}
1328
1329 virtual ~async_writer()
1330 {
1331 m_ring.quit();
1332 m_worker.join();
1333 }
1334
1335 virtual _Success_(return != 0) size_t write(
1336 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1337 {
1338 _Assume_(data || !length);
1339 for (size_t to_write = length;;) {
1340 uint8_t* ptr; size_t num_write;
1341 std::tie(ptr, num_write) = m_ring.back();
1342 if (!ptr) _Unlikely_ {
1343 m_state = state_t::fail;
1344 return length - to_write;
1345 }
1346 if (to_write < num_write)
1347 num_write = to_write;
1348 memcpy(ptr, data, num_write);
1349 m_ring.push(num_write);
1350 to_write -= num_write;
1351 if (!to_write) {
1352 m_state = state_t::ok;
1353 return length;
1354 }
1355 reinterpret_cast<const uint8_t*&>(data) += num_write;
1356 }
1357 }
1358
1359 virtual void flush()
1360 {
1361 m_ring.sync();
1363 }
1364
1365 protected:
1366 void process()
1367 {
1368 for (;;) {
1369 uint8_t* ptr; size_t num_read;
1370 std::tie(ptr, num_read) = m_ring.front();
1371 if (!ptr)
1372 break;
1373 num_read = m_source->write(ptr, num_read);
1374 m_ring.pop(num_read);
1375 if (!m_source->ok()) {
1376 m_ring.quit();
1377 break;
1378 }
1379 }
1380 }
1381
1382 protected:
1383 ring<uint8_t, CAPACITY> m_ring;
1384 std::thread m_worker;
1385 };
1386
1387 constexpr size_t default_buffer_size = 0x400;
1388
1392 class buffer : public converter
1393 {
1394 protected:
1396 explicit buffer(_In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1397 converter(),
1398 m_read_buffer(read_buffer_size),
1399 m_write_buffer(write_buffer_size)
1400 {}
1401
1402 void done()
1403 {
1404 if (m_source)
1405 flush_write();
1406 converter::done();
1407 }
1409
1410 public:
1411 buffer(_Inout_ basic& source, _In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1412 converter(source),
1413 m_read_buffer(read_buffer_size),
1414 m_write_buffer(write_buffer_size)
1415 {}
1416
1417 virtual ~buffer()
1418 {
1419 if (m_source)
1420 flush_write();
1421 }
1422
1423 virtual _Success_(return != 0 || length == 0) size_t read(
1424 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1425 {
1426 _Assume_(data || !length);
1427 for (size_t to_read = length;;) {
1428 size_t buffer_size = m_read_buffer.tail - m_read_buffer.head;
1429 if (to_read <= buffer_size) {
1430 memcpy(data, m_read_buffer.data + m_read_buffer.head, to_read);
1431 m_read_buffer.head += to_read;
1432 m_state = state_t::ok;
1433 return length;
1434 }
1435 if (buffer_size) {
1436 memcpy(data, m_read_buffer.data + m_read_buffer.head, buffer_size);
1437 reinterpret_cast<uint8_t*&>(data) += buffer_size;
1438 to_read -= buffer_size;
1439 }
1440 m_read_buffer.head = 0;
1441 if (to_read > m_read_buffer.capacity) {
1442 // When needing to read more data than buffer capacity, bypass the buffer.
1443 m_read_buffer.tail = 0;
1444 to_read -= m_source->read(data, to_read);
1445 m_state = to_read < length ? state_t::ok : m_source->state();
1446 return length - to_read;
1447 }
1448 m_read_buffer.tail = m_source->read(m_read_buffer.data, m_read_buffer.capacity);
1449 if (m_read_buffer.tail < m_read_buffer.capacity && m_read_buffer.tail < to_read) _Unlikely_ {
1450 memcpy(data, m_read_buffer.data, m_read_buffer.tail);
1451 m_read_buffer.head = m_read_buffer.tail;
1452 to_read -= m_read_buffer.tail;
1453 m_state = to_read < length ? state_t::ok : m_source->state();
1454 return length - to_read;
1455 }
1456 }
1457 }
1458
1459 virtual _Success_(return != 0) size_t write(
1460 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1461 {
1462 _Assume_(data || !length);
1463 if (!length) _Unlikely_ {
1464 // Pass null writes (zero-byte length). Null write operations have special meaning with with Windows pipes.
1465 flush_write();
1466 if (!ok()) _Unlikely_
1467 return 0;
1468 converter::write(nullptr, 0);
1469 return 0;
1470 }
1471
1472 for (size_t to_write = length;;) {
1473 size_t available_buffer = m_write_buffer.capacity - m_write_buffer.tail;
1474 if (to_write <= available_buffer) {
1475 memcpy(m_write_buffer.data + m_write_buffer.tail, data, to_write);
1476 m_write_buffer.tail += to_write;
1477 m_state = state_t::ok;
1478 return length;
1479 }
1480 if (available_buffer) {
1481 memcpy(m_write_buffer.data + m_write_buffer.tail, data, available_buffer);
1482 reinterpret_cast<const uint8_t*&>(data) += available_buffer;
1483 to_write -= available_buffer;
1484 m_write_buffer.tail += available_buffer;
1485 }
1486 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1487 if (buffer_size) {
1488 m_write_buffer.head += converter::write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1489 if (m_write_buffer.head == m_write_buffer.tail)
1490 m_write_buffer.head = m_write_buffer.tail = 0;
1491 else
1492 return length - to_write;
1493 }
1494 if (to_write > m_write_buffer.capacity) {
1495 // When needing to write more data than buffer capacity, bypass the buffer.
1496 to_write -= converter::write(data, to_write);
1497 return length - to_write;
1498 }
1499 }
1500 }
1501
1502 virtual void flush()
1503 {
1504 flush_write();
1505 if (ok())
1507 }
1508
1509 protected:
1510 void flush_write()
1511 {
1512 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1513 if (buffer_size) {
1514 m_write_buffer.head += m_source->write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1515 if (m_write_buffer.head == m_write_buffer.tail) {
1516 m_write_buffer.head = 0;
1517 m_write_buffer.tail = 0;
1518 }
1519 else {
1520 m_state = m_source->state();
1521 return;
1522 }
1523 }
1524 m_state = state_t::ok;
1525 }
1526
1527 struct buffer_t {
1528 uint8_t* data;
1529 size_t head, tail, capacity;
1530
1531 buffer_t(_In_ size_t buffer_size) :
1532 head(0),
1533 tail(0),
1534 capacity(buffer_size),
1535 data(buffer_size ? new uint8_t[buffer_size] : nullptr)
1536 {}
1537
1538 ~buffer_t()
1539 {
1540 if (data)
1541 delete[] data;
1542 }
1543 } m_read_buffer, m_write_buffer;
1544 };
1545
1549 class limiter : public converter
1550 {
1551 public:
1552 limiter(_Inout_ basic& source, _In_ fsize_t _read_limit = 0, _In_ fsize_t _write_limit = 0) :
1553 converter(source),
1554 read_limit(_read_limit),
1555 write_limit(_write_limit)
1556 {}
1557
1558 virtual _Success_(return != 0 || length == 0) size_t read(
1559 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1560 {
1561 size_t num_read;
1562 if (read_limit == fsize_max)
1563 num_read = converter::read(data, length);
1564 else if (length <= read_limit) {
1565 num_read = converter::read(data, length);
1566 read_limit -= num_read;
1567 }
1568 else if (length && !read_limit) {
1569 num_read = 0;
1570 m_state = state_t::eof;
1571 }
1572 else {
1573 num_read = converter::read(data, static_cast<size_t>(read_limit));
1574 read_limit -= num_read;
1575 }
1576 return num_read;
1577 }
1578
1579 virtual _Success_(return != 0) size_t write(
1580 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1581 {
1582 size_t num_written;
1583 if (write_limit == fsize_max)
1584 num_written = converter::write(data, length);
1585 else if (length <= write_limit) {
1586 num_written = converter::write(data, length);
1587 write_limit -= num_written;
1588 }
1589 else if (length && !write_limit) {
1590 num_written = 0;
1591 m_state = state_t::fail;
1592 }
1593 else {
1594 num_written = converter::write(data, static_cast<size_t>(write_limit));
1595 write_limit -= num_written;
1596 }
1597 return num_written;
1598 }
1599
1600 public:
1601 fsize_t
1604 };
1605
1609 class window : public limiter
1610 {
1611 public:
1612 window(_Inout_ basic& source, _In_ fpos_t _read_offset = 0, _In_ fsize_t read_limit = fsize_max, _In_ fpos_t _write_offset = 0, _In_ fsize_t write_limit = fsize_max) :
1613 limiter(source, read_limit, write_limit),
1614 read_offset(_read_offset),
1615 write_offset(_write_offset)
1616 {}
1617
1618 virtual _Success_(return != 0 || length == 0) size_t read(
1619 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1620 {
1621 if (read_offset) {
1622 m_source->skip(read_offset);
1623 m_state = m_source->state();
1624 if (!ok()) _Unlikely_
1625 return 0;
1626 read_offset = 0;
1627 }
1628 size_t num_read;
1629 if (read_limit == fsize_max)
1630 num_read = converter::read(data, length);
1631 else if (length <= read_limit) {
1632 num_read = converter::read(data, length);
1633 read_limit -= num_read;
1634 }
1635 else if (length && !read_limit) {
1636 num_read = 0;
1637 m_source->skip(length);
1638 m_state = state_t::eof;
1639 }
1640 else {
1641 num_read = converter::read(data, static_cast<size_t>(read_limit));
1642 read_limit -= num_read;
1643 }
1644 return num_read;
1645 }
1646
1647 virtual _Success_(return != 0) size_t write(
1648 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1649 {
1650 size_t num_skipped, num_written;
1651 if (length <= write_offset) {
1652 write_offset -= length;
1653 m_state = state_t::ok;
1654 return length;
1655 }
1656 if (write_offset) {
1657 reinterpret_cast<const uint8_t*&>(data) += static_cast<size_t>(write_offset);
1658 length -= static_cast<size_t>(write_offset);
1659 num_skipped = static_cast<size_t>(write_offset);
1660 write_offset = 0;
1661 }
1662 else
1663 num_skipped = 0;
1664 if (write_limit == fsize_max)
1665 num_written = converter::write(data, length);
1666 else if (length <= write_limit) {
1667 num_written = converter::write(data, length);
1668 write_limit -= num_written;
1669 }
1670 else if (length && !write_limit) {
1671 num_skipped += length;
1672 num_written = 0;
1673 m_state = state_t::ok;
1674 }
1675 else {
1676 num_skipped += length - static_cast<size_t>(write_limit);
1677 num_written = converter::write(data, static_cast<size_t>(write_limit));
1678 write_limit -= num_written;
1679 }
1680 return num_skipped + num_written;
1681 }
1682
1683 public:
1684 fpos_t
1687 };
1688
1693 {
1694 public:
1695 file_window(_Inout_ basic_file& source, fpos_t offset = 0, fsize_t length = 0) :
1696 basic(source.state()),
1697 m_source(source),
1698 m_offset(source.tell()),
1699 m_region(offset, offset + length)
1700 {}
1701
1702 virtual _Success_(return != 0 || length == 0) size_t read(
1703 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1704 {
1705 _Assume_(data || !length);
1706 if (m_region.contains(m_offset)) {
1707 size_t num_read = m_source.read(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1708 m_state = m_source.state();
1709 m_offset += num_read;
1710 return num_read;
1711 }
1712 m_state = length ? state_t::eof : state_t::ok;
1713 return 0;
1714 }
1715
1716 virtual _Success_(return != 0) size_t write(
1717 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1718 {
1719 _Assume_(data || !length);
1720 if (m_region.contains(m_offset)) {
1721 size_t num_written = m_source.write(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1722 m_state = m_source.state();
1723 m_offset += num_written;
1724 return num_written;
1725 }
1726 m_state = state_t::fail;
1727 return 0;
1728 }
1729
1730 virtual void close()
1731 {
1732 m_source.close();
1733 m_state = m_source.state();
1734 }
1735
1736 virtual void flush()
1737 {
1738 m_source.flush();
1739 m_state = m_source.state();
1740 }
1741
1742 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
1743 {
1744 m_offset = m_source.seek(offset, how);
1745 m_state = m_source.state();
1746 return ok() ? m_offset - m_region.start : fpos_max;
1747 }
1748
1749 virtual void skip(_In_ fsize_t amount)
1750 {
1751 m_source.skip(amount);
1752 m_state = m_source.state();
1753 }
1754
1755 virtual fpos_t tell() const
1756 {
1757 fpos_t offset = m_source.tell();
1758 return m_region.contains(offset) ? offset - m_region.start : fpos_max;
1759 }
1760
1761 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
1762 {
1763 if (m_region.contains(offset)) {
1764 m_source.lock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1765 m_state = m_source.state();
1766 }
1767 else
1768 m_state = state_t::fail;
1769 }
1770
1771 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
1772 {
1773 if (m_region.contains(offset)) {
1774 m_source.unlock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1775 m_state = m_source.state();
1776 }
1777 else
1778 m_state = state_t::fail;
1779 }
1780
1781 virtual fsize_t size()
1782 {
1783 return m_region.size();
1784 }
1785
1786 virtual void truncate()
1787 {
1788 m_state = state_t::fail;
1789 }
1790
1791 protected:
1792 basic_file& m_source;
1793 fpos_t m_offset;
1794 interval<fpos_t> m_region;
1795 };
1796
1797 constexpr size_t default_cache_size = 0x1000;
1798
1802 class cache : public basic_file
1803 {
1804 protected:
1806#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1807 explicit cache(_In_ size_t cache_size = default_cache_size) :
1808 basic(state_t::fail),
1809 m_cache(cache_size)
1810 {}
1811
1812 void init(_Inout_ basic_file& source)
1813 {
1814 m_source = &source;
1815 init();
1816 }
1817
1818 void init()
1819 {
1820 m_state = m_source->state();
1821 m_offset = m_source->tell();
1822#if SET_FILE_OP_TIMES
1823 m_atime = m_source->atime();
1824 m_mtime = m_source->mtime();
1825#endif
1826 }
1827
1828 void done()
1829 {
1830 if (m_source) {
1831 flush_cache();
1832 if (!ok()) _Unlikely_
1833 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occured
1834 m_source->seek(m_offset);
1835#if SET_FILE_OP_TIMES
1836 m_source->set_atime(m_atime);
1837 m_source->set_mtime(m_mtime);
1838#endif
1839 m_source = nullptr;
1840 }
1841 }
1843
1844 public:
1845 cache(_Inout_ basic_file& source, _In_ size_t cache_size = default_cache_size) :
1846 basic(source.state()),
1847 m_source(&source),
1848 m_cache(cache_size),
1849 m_offset(source.tell())
1850#if SET_FILE_OP_TIMES
1851 , m_atime(source.atime())
1852 , m_mtime(source.mtime())
1853#endif
1854 {}
1855
1856 virtual ~cache() noexcept(false)
1857 {
1858 if (m_source) {
1859 flush_cache();
1860 if (!ok()) _Unlikely_
1861 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occured
1862 m_source->seek(m_offset);
1863#if SET_FILE_OP_TIMES
1864 m_source->set_atime(m_atime);
1865 m_source->set_mtime(m_mtime);
1866#endif
1867 }
1868 }
1869
1870 virtual _Success_(return != 0 || length == 0) size_t read(
1871 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1872 {
1873 _Assume_(data || !length);
1874#if SET_FILE_OP_TIMES
1875 m_atime = time_point::now();
1876#endif
1877 for (size_t to_read = length;;) {
1878 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1879 if (m_cache.region.contains(m_offset)) {
1880 size_t remaining_cache = static_cast<size_t>(m_cache.region.end - m_offset);
1881 if (to_read <= remaining_cache) {
1882 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), to_read);
1883 m_offset += to_read;
1884 m_state = state_t::ok;
1885 return length;
1886 }
1887 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), remaining_cache);
1888 reinterpret_cast<uint8_t*&>(data) += remaining_cache;
1889 to_read -= remaining_cache;
1890 m_offset += remaining_cache;
1891 }
1892 flush_cache();
1893 if (!ok()) _Unlikely_ {
1894 if (to_read < length)
1895 m_state = state_t::ok;
1896 return length - to_read;
1897 }
1898 }
1899 {
1900 fpos_t end_max = m_offset + to_read;
1901 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1902 // Read spans multiple cache blocks. Bypass cache to the last block.
1903 m_source->seek(m_offset);
1904 if (!m_source->ok()) _Unlikely_ {
1905 m_state = to_read < length ? state_t::ok : state_t::fail;
1906 return length - to_read;
1907 }
1908 size_t num_read = m_source->read(data, to_read - static_cast<size_t>(end_max % m_cache.capacity));
1909 m_offset += num_read;
1910 to_read -= num_read;
1911 if (!to_read) {
1912 m_state = state_t::ok;
1913 return length;
1914 }
1915 reinterpret_cast<uint8_t*&>(data) += num_read;
1916 m_state = m_source->state();
1917 if (!ok()) {
1918 if (to_read < length)
1919 m_state = state_t::ok;
1920 return length - to_read;
1921 }
1922 }
1923 }
1924 load_cache(m_offset);
1925 if (!ok() || m_cache.region.end <= m_offset) _Unlikely_ {
1926 m_state = to_read < length ? state_t::ok : state_t::fail;
1927 return length - to_read;
1928 }
1929 }
1930 }
1931
1932 virtual _Success_(return != 0) size_t write(
1933 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1934 {
1935 _Assume_(data || !length);
1936#if SET_FILE_OP_TIMES
1937 m_atime = m_mtime = time_point::now();
1938#endif
1939 for (size_t to_write = length;;) {
1940 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1941 fpos_t end_max = m_cache.region.start + m_cache.capacity;
1942 if (m_cache.region.start <= m_offset && m_offset < end_max) {
1943 size_t remaining_cache = static_cast<size_t>(end_max - m_offset);
1944 if (to_write <= remaining_cache) {
1945 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, to_write);
1946 m_offset += to_write;
1947 m_cache.status = cache_t::cache_t::status_t::dirty;
1948 m_cache.region.end = std::max(m_cache.region.end, m_offset);
1949 m_state = state_t::ok;
1950 return length;
1951 }
1952 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, remaining_cache);
1953 reinterpret_cast<const uint8_t*&>(data) += remaining_cache;
1954 to_write -= remaining_cache;
1955 m_offset += remaining_cache;
1956 m_cache.status = cache_t::cache_t::status_t::dirty;
1957 m_cache.region.end = end_max;
1958 }
1959 flush_cache();
1960 if (!ok()) _Unlikely_
1961 return length - to_write;
1962 }
1963 {
1964 fpos_t end_max = m_offset + to_write;
1965 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1966 // Write spans multiple cache blocks. Bypass cache to the last block.
1967 m_source->seek(m_offset);
1968 if (!ok()) _Unlikely_
1969 return length - to_write;
1970 size_t num_written = m_source->write(data, to_write - static_cast<size_t>(end_max % m_cache.capacity));
1971 m_offset += num_written;
1972 m_state = m_source->state();
1973 to_write -= num_written;
1974 if (!to_write || !ok())
1975 return length - to_write;
1976 reinterpret_cast<const uint8_t*&>(data) += num_written;
1977 }
1978 }
1979 load_cache(m_offset);
1980 if (!ok()) _Unlikely_
1981 return length - to_write;
1982 }
1983 }
1984
1985 virtual void close()
1986 {
1987 invalidate_cache();
1988 if (!ok()) _Unlikely_
1989 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occured
1990 m_source->close();
1991 m_state = m_source->state();
1992 }
1993
1994 virtual void flush()
1995 {
1996#if SET_FILE_OP_TIMES
1997 m_atime = m_mtime = time_point::min();
1998#endif
1999 flush_cache();
2000 if (!ok()) _Unlikely_
2001 return;
2002 m_source->flush();
2003 }
2004
2005 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2006 {
2007 m_state = state_t::ok;
2008 switch (how) {
2009 case seek_t::beg:
2010 return m_offset = offset;
2011 case seek_t::cur:
2012 return m_offset += offset;
2013 case seek_t::end:
2014 return m_offset = size() + offset;
2015 default:
2016 throw std::invalid_argument("unknown seek origin");
2017 }
2018 }
2019
2020 virtual fpos_t tell() const
2021 {
2022 return m_offset;
2023 }
2024
2025 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2026 {
2027 m_source->lock(offset, length);
2028 m_state = m_source->state();
2029 }
2030
2031 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2032 {
2033 m_source->unlock(offset, length);
2034 m_state = m_source->state();
2035 }
2036
2037 virtual fsize_t size()
2038 {
2039 return m_cache.status != cache_t::cache_t::status_t::empty ?
2040 std::max(m_source->size(), m_cache.region.end) :
2041 m_source->size();
2042 }
2043
2044 virtual void truncate()
2045 {
2046#if SET_FILE_OP_TIMES
2047 m_atime = m_mtime = time_point::now();
2048#endif
2049 m_source->seek(m_offset);
2050 if (m_cache.region.end <= m_offset) {
2051 // Truncation does not affect cache.
2052 }
2053 else if (m_cache.region.start <= m_offset) {
2054 // Truncation truncates cache.
2055 m_cache.region.end = m_offset;
2056 }
2057 else {
2058 // Truncation invalidates cache.
2059 m_cache.status = cache_t::cache_t::status_t::empty;
2060 }
2061 m_source->truncate();
2062 m_state = m_source->state();
2063 }
2064
2065 virtual time_point ctime() const
2066 {
2067 return m_source->ctime();
2068 }
2069
2070 virtual time_point atime() const
2071 {
2072#if SET_FILE_OP_TIMES
2073 return std::max(m_atime, m_source->atime());
2074#else
2075 return m_source->atime();
2076#endif
2077 }
2078
2079 virtual time_point mtime() const
2080 {
2081#if SET_FILE_OP_TIMES
2082 return std::max(m_mtime, m_source->mtime());
2083#else
2084 return m_source->mtime();
2085#endif
2086 }
2087
2088 virtual void set_ctime(time_point date)
2089 {
2090 m_source->set_ctime(date);
2091 }
2092
2093 virtual void set_atime(time_point date)
2094 {
2095#if SET_FILE_OP_TIMES
2096 m_atime = date;
2097#endif
2098 m_source->set_atime(date);
2099 }
2100
2101 virtual void set_mtime(time_point date)
2102 {
2103#if SET_FILE_OP_TIMES
2104 m_mtime = date;
2105#endif
2106 m_source->set_mtime(date);
2107 }
2108
2109 protected:
2111 void flush_cache()
2112 {
2113 if (m_cache.status != cache_t::cache_t::status_t::dirty)
2114 m_state = state_t::ok;
2115 else if (!m_cache.region.empty()) {
2116 write_cache();
2117 if (ok())
2118 m_cache.status = cache_t::cache_t::status_t::loaded;
2119 }
2120 else {
2121 m_state = state_t::ok;
2122 m_cache.status = cache_t::cache_t::status_t::loaded;
2123 }
2124 }
2125
2126 void invalidate_cache()
2127 {
2128 if (m_cache.status == cache_t::cache_t::status_t::dirty && !m_cache.region.empty()) {
2129 write_cache();
2130 if (!ok()) _Unlikely_
2131 return;
2132 } else
2133 m_state = state_t::ok;
2134 m_cache.status = cache_t::cache_t::status_t::empty;
2135 }
2136
2137 void load_cache(_In_ fpos_t start)
2138 {
2139 _Assume_(m_cache.status != cache_t::cache_t::status_t::dirty);
2140 start -= start % m_cache.capacity; // Align to cache block size.
2141 m_source->seek(m_cache.region.start = start);
2142 if (m_source->ok()) {
2143 m_cache.region.end = start + m_source->read(m_cache.data, m_cache.capacity);
2144 m_cache.status = cache_t::cache_t::status_t::loaded;
2145 m_state = state_t::ok; // Regardless the read failure, we still might have cached some data.
2146 }
2147 else
2148 m_state = state_t::fail;
2149 }
2150
2151 void write_cache()
2152 {
2153 _Assume_(m_cache.status == cache_t::cache_t::status_t::dirty);
2154 m_source->seek(m_cache.region.start);
2155 m_source->write(m_cache.data, static_cast<size_t>(m_cache.region.size()));
2156 m_state = m_source->state();
2157 }
2158
2159 basic_file* m_source;
2160 struct cache_t {
2161 uint8_t* data;
2162 size_t capacity;
2163 enum class status_t {
2164 empty = 0,
2165 loaded,
2166 dirty,
2167 } status;
2168 interval<fpos_t> region;
2169
2170 cache_t(_In_ size_t _capacity) :
2171 data(new uint8_t[_capacity]),
2172 capacity(_capacity),
2173 status(status_t::empty),
2174 region(0)
2175 {}
2176
2177 ~cache_t()
2178 {
2179 delete[] data;
2180 }
2181 } m_cache;
2182 fpos_t m_offset;
2183#if SET_FILE_OP_TIMES
2184 time_point
2185 m_atime,
2186 m_mtime;
2187#endif
2189 };
2190
2194 class basic_sys : virtual public basic, public sys_object
2195 {
2196 public:
2197 basic_sys(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) :
2198 basic(state),
2199 sys_object(h)
2200 {}
2201
2202 virtual _Success_(return != 0 || length == 0) size_t read(
2203 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2204 {
2205 _Assume_(data || !length);
2206 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2207 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2208 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2209 size_t
2210#if defined(_WIN64)
2211 block_size = 0x1F80000;
2212#elif defined(_WIN32)
2213 block_size = 0x3f00000;
2214#else
2215 block_size = SSIZE_MAX;
2216#endif
2217 for (size_t to_read = length;;) {
2218#ifdef _WIN32
2219 // ReadFile() might raise exception (e.g. STATUS_FILE_BAD_FORMAT/0xE0000002).
2220 BOOL succeeded;
2221 DWORD num_read;
2222 __try { succeeded = ReadFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_read, block_size)), &num_read, nullptr); }
2223 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_read = 0; }
2224 if (!succeeded && GetLastError() == ERROR_NO_SYSTEM_RESOURCES && block_size > default_block_size) _Unlikely_ {
2225 // Error "Insufficient system resources exist to complete the requested service." occurs
2226 // ocasionally, when attempting to read too much data at once (e.g. over \\TSClient).
2227 block_size = default_block_size;
2228 continue;
2229 }
2230 if (!succeeded) _Unlikely_
2231#else
2232 ssize_t num_read = ::read(m_h, data, static_cast<ssize_t>(std::min<size_t>(to_read, block_size)));
2233 if (num_read < 0) _Unlikely_
2234#endif
2235 {
2236 m_state = to_read < length ? state_t::ok : state_t::fail;
2237 return length - to_read;
2238 }
2239 if (!num_read) _Unlikely_ {
2240 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2241 return length - to_read;
2242 }
2243 to_read -= num_read;
2244 if (!to_read) {
2245 m_state = state_t::ok;
2246 return length;
2247 }
2248 reinterpret_cast<uint8_t*&>(data) += num_read;
2249 }
2250 }
2251
2252 virtual _Success_(return != 0) size_t write(
2253 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2254 {
2255 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2256 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2257 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2258 constexpr size_t
2259#if defined(_WIN64)
2260 block_size = 0x1F80000;
2261#elif defined(_WIN32)
2262 block_size = 0x3f00000;
2263#else
2264 block_size = SSIZE_MAX;
2265#endif
2266 for (size_t to_write = length;;) {
2267#ifdef _WIN32
2268 // ReadFile() might raise an exception. Be cautious with WriteFile() too.
2269 BOOL succeeded;
2270 DWORD num_written;
2271 __try { succeeded = WriteFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_write, block_size)), &num_written, nullptr); }
2272 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_written = 0; }
2273 to_write -= num_written;
2274 if (!to_write) {
2275 m_state = state_t::ok;
2276 return length;
2277 }
2278 reinterpret_cast<const uint8_t*&>(data) += num_written;
2279 if (!succeeded) _Unlikely_ {
2280 m_state = state_t::fail;
2281 return length - to_write;
2282 }
2283#else
2284 ssize_t num_written = ::write(m_h, data, static_cast<ssize_t>(std::min<size_t>(to_write, block_size)));
2285 if (num_written < 0) _Unlikely_ {
2286 m_state = state_t::fail;
2287 return length - to_write;
2288 }
2289 to_write -= num_written;
2290 if (!to_write) {
2291 m_state = state_t::ok;
2292 return length;
2293 }
2294 reinterpret_cast<const uint8_t*&>(data) += num_written;
2295#endif
2296 }
2297 }
2298
2299 virtual void close()
2300 {
2301 try {
2303 m_state = state_t::ok;
2304 }
2305 catch (...) {
2306 m_state = state_t::fail;
2307 }
2308 }
2309
2310 virtual void flush()
2311 {
2312#ifdef _WIN32
2313 m_state = FlushFileBuffers(m_h) ? state_t::ok : state_t::fail;
2314#else
2315 m_state = fsync(m_h) >= 0 ? state_t::ok : state_t::fail;
2316#endif
2317 }
2318 };
2319
2323 class buffered_sys : public buffer
2324 {
2325 public:
2326 buffered_sys(_In_opt_ sys_handle h = invalid_handle, size_t read_buffer_size = default_buffer_size, size_t write_buffer_size = default_buffer_size) :
2327 buffer(read_buffer_size, write_buffer_size),
2328 m_source(h)
2329 {
2330 init(m_source);
2331 }
2332
2333 virtual ~buffered_sys()
2334 {
2335 done();
2336 }
2337
2338 protected:
2339 basic_sys m_source;
2340 };
2341
2345 class socket : public basic
2346 {
2347 public:
2348 socket(_In_opt_ SOCKET h = INVALID_SOCKET, _In_ state_t state = state_t::ok) :
2349 basic(state),
2350 m_h(h)
2351 {}
2352
2353 private:
2354 socket(_In_ const socket& other);
2355 socket& operator =(_In_ const socket& other);
2356
2357 public:
2358 socket(_Inout_ socket&& other) noexcept : m_h(other.m_h)
2359 {
2360 other.m_h = INVALID_SOCKET;
2361 }
2362
2363 socket& operator =(_Inout_ socket&& other) noexcept
2364 {
2365 if (this != std::addressof(other)) {
2366 if (m_h != INVALID_SOCKET)
2367 closesocket(m_h);
2368 m_h = other.m_h;
2369 other.m_h = INVALID_SOCKET;
2370 }
2371 return *this;
2372 }
2373
2381 socket(_In_ int af, _In_ int type, _In_ int protocol)
2382 {
2383 m_h = ::socket(af, type, protocol);
2384 if (m_h == INVALID_SOCKET) _Unlikely_
2385 m_state = state_t::fail;
2386 }
2387
2388 virtual ~socket()
2389 {
2390 if (m_h != INVALID_SOCKET)
2391 closesocket(m_h);
2392 }
2393
2397 inline operator bool() const noexcept { return m_h != INVALID_SOCKET; }
2398
2402 inline SOCKET get() const noexcept { return m_h; }
2403
2404 virtual _Success_(return != 0 || length == 0) size_t read(
2405 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2406 {
2407 _Assume_(data || !length);
2408 constexpr int block_size = 0x10000000;
2409 for (size_t to_read = length;;) {
2410 int num_read = recv(m_h, reinterpret_cast<char*>(data), static_cast<int>(std::min<size_t>(to_read, block_size)), 0);
2411 if (num_read == SOCKET_ERROR) _Unlikely_ {
2412 m_state = to_read < length ? state_t::ok : state_t::fail;
2413 return length - to_read;
2414 }
2415 if (!num_read) {
2416 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2417 return length - to_read;
2418 }
2419 to_read -= num_read;
2420 if (!to_read) {
2421 m_state = state_t::ok;
2422 return length;
2423 }
2424 reinterpret_cast<uint8_t*&>(data) += num_read;
2425 }
2426 }
2427
2428 virtual _Success_(return != 0) size_t write(
2429 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2430 {
2431 _Assume_(data || !length);
2432 constexpr int block_size = 0x10000000;
2433 for (size_t to_write = length;;) {
2434 int num_written = send(m_h, reinterpret_cast<const char*>(data), static_cast<int>(std::min<size_t>(to_write, block_size)), 0);
2435 if (num_written == SOCKET_ERROR) _Unlikely_ {
2436 m_state = state_t::fail;
2437 return length - to_write;
2438 }
2439 to_write -= num_written;
2440 if (!to_write) {
2441 m_state = state_t::ok;
2442 return length;
2443 }
2444 reinterpret_cast<const uint8_t*&>(data) += num_written;
2445 }
2446 }
2447
2448 virtual void close()
2449 {
2450 if (m_h != INVALID_SOCKET) {
2451 closesocket(m_h);
2452 m_h = INVALID_SOCKET;
2453 }
2454 m_state = state_t::ok;
2455 }
2456
2457 protected:
2458 SOCKET m_h;
2459 };
2460
2461#ifdef _WIN32
2465 class sequential_stream : public basic
2466 {
2467 public:
2468 sequential_stream(_In_ ISequentialStream* source) : m_source(source)
2469 {
2470 m_source->AddRef();
2471 }
2472
2473 virtual ~sequential_stream()
2474 {
2475 m_source->Release();
2476 }
2477
2478 virtual _Success_(return != 0 || length == 0) size_t read(
2479 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2480 {
2481 _Assume_(data || !length);
2482 for (size_t to_read = length;;) {
2483 HRESULT hr;
2484 ULONG num_read = 0;
2485 __try { hr = m_source->Read(data, (ULONG)std::min<size_t>(to_read, ULONG_MAX), &num_read); }
2486 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2487 if (FAILED(hr)) _Unlikely_ {
2488 m_state = to_read < length ? state_t::ok : state_t::fail;
2489 return length - to_read;
2490 }
2491 to_read -= num_read;
2492 if (hr == S_FALSE) _Unlikely_ {
2493 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2494 return length - to_read;
2495 }
2496 if (!to_read) {
2497 m_state = state_t::ok;
2498 return length;
2499 }
2500 reinterpret_cast<uint8_t*&>(data) += num_read;
2501 }
2502 }
2503
2504 virtual _Success_(return != 0) size_t write(
2505 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2506 {
2507 _Assume_(data || !length);
2508 for (size_t to_write = length;;) {
2509 HRESULT hr;
2510 ULONG num_written = 0;
2511 __try { hr = m_source->Write(data, static_cast<ULONG>(std::min<size_t>(to_write, ULONG_MAX)), &num_written); }
2512 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2513 // In abscence of documentation whether num_written gets set when FAILED(hr) (i.e. partially succesful writes),
2514 // assume write failed completely.
2515 if (FAILED(hr)) _Unlikely_ {
2516 m_state = state_t::fail;
2517 return length - to_write;
2518 }
2519 to_write -= num_written;
2520 if (!to_write) {
2521 m_state = state_t::ok;
2522 return length;
2523 }
2524 reinterpret_cast<const uint8_t*&>(data) += num_written;
2525 }
2526 }
2527
2528 protected:
2529 ISequentialStream* m_source;
2530 };
2531
2535 class asp : public basic
2536 {
2537 public:
2538 asp(_In_opt_ IRequest* request, _In_opt_ IResponse* response) :
2539 m_request(request),
2540 m_response(response)
2541 {
2542 if (m_request)
2543 m_request->AddRef();
2544 if (m_response)
2545 m_response->AddRef();
2546 }
2547
2548 virtual ~asp()
2549 {
2550 if (m_request)
2551 m_request->Release();
2552 if (m_response)
2553 m_response->Release();
2554 }
2555
2556 virtual _Success_(return != 0 || length == 0) size_t read(
2557 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2558 {
2559 _Assume_(data || !length);
2560 if (!m_request) _Unlikely_ {
2561 m_state = state_t::fail;
2562 return 0;
2563 }
2564 for (size_t to_read = length;;) {
2565 VARIANT var_amount, var_data;
2566 V_VT(&var_amount) = VT_I4;
2567 V_I4(&var_amount) = (LONG)std::min<size_t>(to_read, LONG_MAX);
2568 V_VT(&var_data) = VT_EMPTY;
2569 HRESULT hr = [&]() {
2570 __try { return m_request->BinaryRead(&var_amount, &var_data); }
2571 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2572 }();
2573 if (FAILED(hr)) _Unlikely_ {
2574 m_state = to_read < length ? state_t::ok : state_t::fail;
2575 return length - to_read;
2576 }
2577 _Assume_(V_VT(&var_amount) == VT_I4);
2578 _Assume_(V_VT(&var_data) == (VT_ARRAY | VT_UI1));
2579 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(V_ARRAY(&var_data));
2580 if (!V_I4(&var_amount)) _Unlikely_ {
2581 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2582 return length - to_read;
2583 }
2584 safearray_accessor<uint8_t> a(sa.get());
2585 memcpy(data, a.data(), V_I4(&var_amount));
2586 to_read -= V_I4(&var_amount);
2587 if (!to_read) {
2588 m_state = state_t::ok;
2589 return length;
2590 }
2591 reinterpret_cast<uint8_t*&>(data) += V_I4(&var_amount);
2592 }
2593 }
2594
2595 virtual _Success_(return != 0) size_t write(
2596 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2597 {
2598 if (!m_response) {
2599 m_state = state_t::fail;
2600 return 0;
2601 }
2602 for (size_t to_write = length;;) {
2603 UINT num_written = static_cast<UINT>(std::min<size_t>(to_write, UINT_MAX));
2604 std::unique_ptr<OLECHAR, SysFreeString_delete> bstr_data(SysAllocStringByteLen(reinterpret_cast<LPCSTR>(data), num_written));
2605 VARIANT var_data;
2606 V_VT(&var_data) = VT_BSTR;
2607 V_BSTR(&var_data) = bstr_data.get();
2608 HRESULT hr = [&]() {
2609 __try { return m_response->BinaryWrite(var_data); }
2610 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2611 }();
2612 if (FAILED(hr)) _Unlikely_ {
2613 m_state = state_t::fail;
2614 return length - to_write;
2615 }
2616 to_write -= num_written;
2617 if (!to_write) {
2618 m_state = state_t::ok;
2619 return length;
2620 }
2621 reinterpret_cast<const uint8_t*&>(data) += num_written;
2622 }
2623 }
2624
2625 virtual void close()
2626 {
2627 if (m_response) {
2628 __try { m_response->End(); }
2629 __except (EXCEPTION_EXECUTE_HANDLER) {}
2630 }
2631 m_state = state_t::ok;
2632 }
2633
2634 virtual void flush()
2635 {
2636 if (m_response) {
2637 HRESULT hr;
2638 __try { hr = m_response->Flush(); }
2639 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2640 m_state = SUCCEEDED(hr) ? state_t::ok : state_t::fail;
2641 }
2642 }
2643
2644 protected:
2645 IRequest* m_request;
2646 IResponse* m_response;
2647 };
2648#endif
2649
2653 enum mode_t
2654 {
2655 mode_for_reading = 1 << 0,
2656 mode_for_writing = 1 << 1,
2657 mode_for_chmod = 1 << 2,
2658
2659 mode_open_existing = 0 << 3,
2660 mode_truncate_existing = 1 << 3,
2661 mode_preserve_existing = 2 << 3,
2662 mode_create_new = 3 << 3,
2663 mode_create = 4 << 3,
2664 mode_disposition_mask = 7 << 3,
2665
2666 mode_append = 1 << 6,
2667 mode_text = 0,
2668 mode_binary = 1 << 7,
2669
2670 share_none = 0,
2671 share_reading = 1 << 8,
2672 share_writing = 1 << 9,
2673 share_deleting = 1 << 10,
2674 share_all = share_reading | share_writing | share_deleting, // Allow others all operations on our file
2675
2676 inherit_handle = 1 << 11,
2677
2678 hint_write_thru = 1 << 12,
2679 hint_no_buffering = 1 << 13,
2680 hint_random_access = 1 << 14,
2681 hint_sequential_access = 1 << 15,
2682 };
2683
2684#pragma warning(push)
2685#pragma warning(disable: 4250)
2689 class file : virtual public basic_file, virtual public basic_sys
2690 {
2691 public:
2692 file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) : basic_sys(h, state) {}
2693
2700 file(_In_z_ const schar_t* filename, _In_ int mode)
2701 {
2702 open(filename, mode);
2703 }
2704
2711 inline file(_In_ const stdex::sstring& filename, _In_ int mode) : file(filename.c_str(), mode) {}
2712
2719 void open(_In_z_ const schar_t* filename, _In_ int mode)
2720 {
2721 if (m_h != invalid_handle)
2722 close();
2723
2724#ifdef _WIN32
2725 DWORD dwDesiredAccess = 0;
2726 if (mode & mode_for_reading) dwDesiredAccess |= GENERIC_READ;
2727 if (mode & mode_for_writing) dwDesiredAccess |= GENERIC_WRITE;
2728 if (mode & mode_for_chmod) dwDesiredAccess |= FILE_WRITE_ATTRIBUTES;
2729
2730 DWORD dwShareMode = 0;
2731 if (mode & share_reading) dwShareMode |= FILE_SHARE_READ;
2732 if (mode & share_writing) dwShareMode |= FILE_SHARE_WRITE;
2733 if (mode & share_deleting) dwShareMode |= FILE_SHARE_DELETE;
2734
2735 SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };
2736 sa.bInheritHandle = mode & inherit_handle ? true : false;
2737
2738 DWORD dwCreationDisposition;
2739 switch (mode & mode_disposition_mask) {
2740 case mode_open_existing: dwCreationDisposition = OPEN_EXISTING; break;
2741 case mode_truncate_existing: dwCreationDisposition = TRUNCATE_EXISTING; break;
2742 case mode_preserve_existing: dwCreationDisposition = OPEN_ALWAYS; break;
2743 case mode_create_new: dwCreationDisposition = CREATE_NEW; break;
2744 case mode_create: dwCreationDisposition = CREATE_ALWAYS; break;
2745 default: throw std::invalid_argument("invalid mode");
2746 }
2747
2748 DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
2749 if (mode & hint_write_thru) dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
2750 if (mode & hint_no_buffering) dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
2751 if (mode & hint_random_access) dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
2752 if (mode & hint_sequential_access) dwFlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
2753
2754 m_h = CreateFile(filename, dwDesiredAccess, dwShareMode, &sa, dwCreationDisposition, dwFlagsAndAttributes, NULL);
2755#else
2756 int flags = 0;
2757 switch (mode & (mode_for_reading | mode_for_writing)) {
2758 case mode_for_reading: flags |= O_RDONLY; break;
2759 case mode_for_writing: flags |= O_WRONLY; break;
2760 case mode_for_reading | mode_for_writing: flags |= O_RDWR; break;
2761 }
2762 switch (mode & mode_disposition_mask) {
2763 case mode_open_existing: break;
2764 case mode_truncate_existing: flags |= O_TRUNC; break;
2765 case mode_preserve_existing: flags |= O_CREAT; break;
2766 case mode_create_new: flags |= O_CREAT | O_EXCL; break;
2767 case mode_create: flags |= O_CREAT | O_TRUNC; break;
2768 default: throw std::invalid_argument("invalid mode");
2769 }
2770 if (mode & hint_write_thru) flags |= O_DSYNC;
2771#ifndef __APPLE__
2772 if (mode & hint_no_buffering) flags |= O_RSYNC;
2773#endif
2774
2775 m_h = ::open(filename, flags, DEFFILEMODE);
2776#endif
2777 if (m_h != invalid_handle) {
2778 m_state = state_t::ok;
2779 if (mode & mode_append)
2780 seek(0, seek_t::end);
2781 }
2782 else
2783 m_state = state_t::fail;
2784 }
2785
2792 inline void open(_In_ const stdex::sstring& filename, _In_ int mode)
2793 {
2794 open(filename.c_str(), mode);
2795 }
2796
2797 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2798 {
2799#ifdef _WIN32
2800 LARGE_INTEGER li;
2801 li.QuadPart = offset;
2802 li.LowPart = SetFilePointer(m_h, li.LowPart, &li.HighPart, static_cast<DWORD>(how));
2803 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR) {
2804 m_state = state_t::ok;
2805 return li.QuadPart;
2806 }
2807#else
2808 off64_t result = lseek64(m_h, offset, static_cast<int>(how));
2809 if (result >= 0) {
2810 m_state = state_t::ok;
2811 return result;
2812 }
2813#endif
2814 m_state = state_t::fail;
2815 return fpos_max;
2816 }
2817
2818 virtual fpos_t tell() const
2819 {
2820 if (m_h != invalid_handle) {
2821#ifdef _WIN32
2822 LARGE_INTEGER li;
2823 li.QuadPart = 0;
2824 li.LowPart = SetFilePointer(m_h, 0, &li.HighPart, FILE_CURRENT);
2825 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR)
2826 return li.QuadPart;
2827#else
2828 off64_t result = lseek64(m_h, 0, SEEK_CUR);
2829 if (result >= 0)
2830 return result;
2831#endif
2832 }
2833 return fpos_max;
2834 }
2835
2836 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2837 {
2838#ifdef _WIN32
2839 LARGE_INTEGER liOffset;
2840 LARGE_INTEGER liSize;
2841 liOffset.QuadPart = offset;
2842 liSize.QuadPart = length;
2843 if (LockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2844 m_state = state_t::ok;
2845 return;
2846 }
2847#else
2848 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2849 if (orig >= 0) {
2850 m_state = lseek64(m_h, offset, SEEK_SET) >= 0 && lockf64(m_h, F_LOCK, length) >= 0 ? state_t::ok : state_t::fail;
2851 lseek64(m_h, orig, SEEK_SET);
2852 m_state = state_t::ok;
2853 return;
2854 }
2855#endif
2856 m_state = state_t::fail;
2857 }
2858
2859 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2860 {
2861#ifdef _WIN32
2862 LARGE_INTEGER liOffset;
2863 LARGE_INTEGER liSize;
2864 liOffset.QuadPart = offset;
2865 liSize.QuadPart = length;
2866 if (UnlockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2867 m_state = state_t::ok;
2868 return;
2869 }
2870#else
2871 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2872 if (orig >= 0) {
2873 if (lseek64(m_h, offset, SEEK_SET) >= 0 && lockf64(m_h, F_ULOCK, length) >= 0) {
2874 lseek64(m_h, orig, SEEK_SET);
2875 m_state = state_t::ok;
2876 return;
2877 }
2878 lseek64(m_h, orig, SEEK_SET);
2879 }
2880#endif
2881 m_state = state_t::fail;
2882 }
2883
2884 virtual fsize_t size()
2885 {
2886#ifdef _WIN32
2887 LARGE_INTEGER li;
2888 li.LowPart = GetFileSize(m_h, (LPDWORD)&li.HighPart);
2889 if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR)
2890 li.QuadPart = -1;
2891 return li.QuadPart;
2892#else
2893 off64_t length = -1, orig = lseek64(m_h, 0, SEEK_CUR);
2894 if (orig >= 0) {
2895 length = lseek64(m_h, 0, SEEK_END);
2896 lseek64(m_h, orig, SEEK_SET);
2897 }
2898 return length;
2899#endif
2900 }
2901
2902 virtual void truncate()
2903 {
2904#ifdef _WIN32
2905 if (SetEndOfFile(m_h)) {
2906 m_state = state_t::ok;
2907 return;
2908 }
2909#else
2910 off64_t length = lseek64(m_h, 0, SEEK_CUR);
2911 if (length >= 0 && ftruncate64(m_h, length) >= 0) {
2912 m_state = state_t::ok;
2913 return;
2914 }
2915#endif
2916 m_state = state_t::fail;
2917 }
2918
2919#ifdef _WIN32
2920 static inline time_point ft2tp(_In_ const FILETIME& ft)
2921 {
2922#if _HAS_CXX20
2923 uint64_t t = (static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
2924#else
2925 uint64_t t = ((static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) - 116444736000000000ll;
2926#endif
2927 return time_point(time_point::duration(t));
2928 }
2929
2930 static inline void tp2ft(_In_ time_point tp, _Out_ FILETIME& ft)
2931 {
2932#if _HAS_CXX20
2933 uint64_t t = tp.time_since_epoch().count();
2934#else
2935 uint64_t t = tp.time_since_epoch().count() + 116444736000000000ll;
2936#endif
2937 ft.dwHighDateTime = static_cast<DWORD>((t >> 32) & 0xffffffff);
2938 ft.dwLowDateTime = static_cast<DWORD>(t & 0xffffffff);
2939 }
2940#endif
2941
2942 virtual time_point ctime() const
2943 {
2944#ifdef _WIN32
2945 FILETIME ft;
2946 if (GetFileTime(m_h, &ft, nullptr, nullptr))
2947 return ft2tp(ft);
2948#endif
2949 return time_point::min();
2950 }
2951
2952 virtual time_point atime() const
2953 {
2954#ifdef _WIN32
2955 FILETIME ft;
2956 if (GetFileTime(m_h, nullptr, &ft, nullptr))
2957 return ft2tp(ft);
2958#else
2959 struct stat buf;
2960 if (fstat(m_h, &buf) >= 0)
2961 return clock::from_time_t(buf.st_atime);
2962#endif
2963 return time_point::min();
2964 }
2965
2966 virtual time_point mtime() const
2967 {
2968#ifdef _WIN32
2969 FILETIME ft;
2970 if (GetFileTime(m_h, nullptr, nullptr, &ft))
2971 return ft2tp(ft);
2972#else
2973 struct stat buf;
2974 if (fstat(m_h, &buf) >= 0)
2975 return clock::from_time_t(buf.st_mtime);
2976#endif
2977 return time_point::min();
2978 }
2979
2980 virtual void set_ctime(time_point date)
2981 {
2982 _Assume_(m_h != invalid_handle);
2983#ifdef _WIN32
2984 FILETIME ft;
2985 tp2ft(date, ft);
2986 if (SetFileTime(m_h, &ft, nullptr, nullptr))
2987 return;
2988 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
2989#else
2990 throw std::runtime_error("not supported");
2991#endif
2992 }
2993
2994 virtual void set_atime(time_point date)
2995 {
2996 _Assume_(m_h != invalid_handle);
2997#ifdef _WIN32
2998 FILETIME ft;
2999 tp2ft(date, ft);
3000 if (SetFileTime(m_h, nullptr, &ft, nullptr))
3001 return;
3002 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3003#else
3004 struct timespec ts[2] = {
3005 { date.time_since_epoch().count(), 0 },
3006 { 0, UTIME_OMIT },
3007 };
3008 if (futimens(m_h, ts) >= 0)
3009 return;
3010 throw std::system_error(errno, std::system_category(), "futimens failed");
3011#endif
3012 }
3013
3014 virtual void set_mtime(time_point date)
3015 {
3016#ifdef _WIN32
3017 FILETIME ft;
3018 tp2ft(date, ft);
3019 if (SetFileTime(m_h, nullptr, nullptr, &ft))
3020 return;
3021 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3022#else
3023 struct timespec ts[2] = {
3024 { 0, UTIME_OMIT },
3025 { date.time_since_epoch().count(), 0 },
3026 };
3027 if (futimens(m_h, ts) >= 0)
3028 return;
3029 throw std::system_error(errno, std::system_category(), "futimens failed");
3030#endif
3031 }
3032
3038 static bool exists(_In_z_ const stdex::schar_t* filename)
3039 {
3040#ifdef _WIN32
3041 return GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES;
3042#else
3043 struct stat s;
3044 return stat(filename, &s) == 0;
3045#endif
3046 }
3047
3053 static inline bool exists(_In_ const stdex::sstring& filename)
3054 {
3055 return exists(filename.c_str());
3056 }
3057
3065 static bool readonly(_In_z_ const stdex::schar_t* filename)
3066 {
3067#ifdef _WIN32
3068 DWORD dwAttr = GetFileAttributes(filename);
3069 return dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_READONLY) != 0;
3070#else
3071 struct stat s;
3072 return stat(filename, &s) == 0 && (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0;
3073#endif
3074 }
3075
3083 static inline bool readonly(_In_ const stdex::sstring& filename)
3084 {
3085 return readonly(filename.c_str());
3086 }
3087 };
3088#pragma warning(pop)
3089
3093 class cached_file : public cache
3094 {
3095 public:
3096 cached_file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok, _In_ size_t cache_size = default_cache_size) :
3097 cache(cache_size),
3098 m_source(h, state)
3099 {
3100 init(m_source);
3101 }
3102
3110 cached_file(_In_z_ const schar_t* filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) :
3111 cache(cache_size),
3112 m_source(filename, mode & mode_for_writing ? mode | mode_for_reading : mode)
3113 {
3114 init(m_source);
3115 }
3116
3124 inline cached_file(_In_ const stdex::sstring& filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) : cached_file(filename.c_str(), mode, cache_size) {}
3125
3126 virtual ~cached_file()
3127 {
3128 done();
3129 }
3130
3137 void open(_In_z_ const schar_t* filename, _In_ int mode)
3138 {
3139 invalidate_cache();
3140 if (!ok()) _Unlikely_{
3141 m_state = state_t::fail;
3142 return;
3143 }
3144 m_source.open(filename, mode & mode_for_writing ? mode | mode_for_reading : mode);
3145 if (m_source.ok()) {
3146 init();
3147 return;
3148 }
3149 m_state = state_t::fail;
3150 }
3151
3158 inline void open(_In_ const stdex::sstring& filename, _In_ int mode)
3159 {
3160 open(filename.c_str(), mode);
3161 }
3162
3163 protected:
3164 file m_source;
3165 };
3166
3171 {
3172 public:
3173 memory_file(_In_ state_t state = state_t::ok) :
3174 basic(state),
3175 m_data(nullptr),
3176 m_offset(0),
3177 m_size(0),
3178 m_reserved(0),
3179 m_manage(true)
3180 {
3181#if SET_FILE_OP_TIMES
3182 m_ctime = m_atime = m_mtime = time_point::now();
3183#endif
3184 }
3185
3192 memory_file(_In_ size_t size, _In_ state_t state = state_t::ok) :
3193 basic(state),
3194 m_data(reinterpret_cast<uint8_t*>(malloc(size))),
3195 m_offset(0),
3196 m_size(0),
3198 m_manage(true)
3199 {
3200 if (!m_data)
3201 throw std::bad_alloc();
3202#if SET_FILE_OP_TIMES
3203 m_ctime = m_atime = m_mtime = time_point::now();
3204#endif
3205 }
3206
3216 memory_file(_Inout_ void* data, _In_ size_t size, _In_ size_t reserved, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3217 basic(state),
3218 m_data(reinterpret_cast<uint8_t*>(data)),
3219 m_offset(0),
3220 m_size(size),
3221 m_reserved(reserved),
3222 m_manage(manage)
3223 {
3224 _Assume_(data || !size);
3225 _Assume_(reserved >= size);
3226#if SET_FILE_OP_TIMES
3227 m_ctime = m_atime = m_mtime = time_point::now();
3228#endif
3229 }
3230
3239 memory_file(_Inout_ void* data, _In_ size_t size, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3240 memory_file(data, size, size, manage, state)
3241 {}
3242
3249 memory_file(_In_z_ const schar_t* filename, _In_ int mode) : memory_file()
3250 {
3251 load(filename, mode);
3252 }
3253
3260 inline memory_file(_In_ const stdex::sstring& filename, _In_ int mode) : memory_file(filename.c_str(), mode) {}
3261
3262 virtual ~memory_file()
3263 {
3264 if (m_manage && m_data)
3265 free(m_data);
3266 }
3267
3274 void reserve(_In_ size_t required, _In_ bool tight = false) noexcept
3275 {
3276 if (required <= m_reserved && (!tight || required >= m_reserved)) {
3277 m_state = state_t::ok;
3278 return;
3279 }
3280 if (!m_manage) {
3281 m_state = state_t::fail;
3282 return;
3283 }
3284 size_t reserved = tight ? required : ((required + required / 4 + (default_block_size - 1)) / default_block_size) * default_block_size;
3285 auto data = reinterpret_cast<uint8_t*>(realloc(m_data, reserved));
3286 if (!data && reserved) _Unlikely_ {
3287 m_state = state_t::fail;
3288 return;
3289 }
3290 m_data = data;
3291 if (reserved < m_size)
3292 m_size = reserved;
3293 m_reserved = reserved;
3294 m_state = state_t::ok;
3295 }
3296
3303 void load(_In_z_ const schar_t* filename, _In_ int mode)
3304 {
3305 file f(filename, (mode & ~hint_random_access) | mode_for_reading | hint_sequential_access);
3306 if (!f.ok()) {
3307 m_state = state_t::fail;
3308 return;
3309 }
3310 fsize_t size = f.size();
3311 if (size > SIZE_MAX) {
3312 m_state = state_t::fail;
3313 return;
3314 }
3315 reserve(static_cast<size_t>(size), true);
3316 if (!ok()) _Unlikely_ {
3317 return;
3318 }
3319 m_offset = m_size = 0;
3320 write_stream(f);
3321 if (ok())
3322 m_offset = 0;
3323#if SET_FILE_OP_TIMES
3324 m_ctime = f.ctime();
3325 m_atime = f.atime();
3326 m_mtime = f.mtime();
3327#endif
3328 }
3329
3336 inline void load(_In_ const stdex::sstring& filename, _In_ int mode)
3337 {
3338 load(filename.c_str(), mode);
3339 }
3340
3347 void save(_In_z_ const schar_t* filename, _In_ int mode)
3348 {
3349 file f(filename, (mode & ~hint_random_access) | mode_for_writing | hint_sequential_access);
3350 if (!f.ok()) {
3351 m_state = state_t::fail;
3352 return;
3353 }
3354 f.write(m_data, m_size);
3355 if (!f.ok()) {
3356 m_state = state_t::fail;
3357 return;
3358 }
3359 f.truncate();
3360#if SET_FILE_OP_TIMES
3361 f.set_ctime(m_ctime);
3362 f.set_atime(m_atime);
3363 f.set_mtime(m_mtime);
3364#endif
3365 }
3366
3373 inline void save(_In_ const stdex::sstring& filename, _In_ int mode)
3374 {
3375 save(filename.c_str(), mode);
3376 }
3377
3381 inline const void* data() const { return m_data; }
3382
3383 virtual _Success_(return != 0 || length == 0) size_t read(
3384 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
3385 {
3386 _Assume_(data || !length);
3387#if SET_FILE_OP_TIMES
3388 m_atime = time_point::now();
3389#endif
3390 size_t available = m_size - m_offset;
3391 if (length <= available) {
3392 memcpy(data, m_data + m_offset, length);
3393 m_offset += length;
3394 m_state = state_t::ok;
3395 return length;
3396 }
3397 if (length && !available) {
3398 m_state = state_t::eof;
3399 return 0;
3400 }
3401 memcpy(data, m_data + m_offset, available);
3402 m_offset += available;
3403 m_state = state_t::ok;
3404 return available;
3405 }
3406
3421 template <class T>
3422 inline memory_file& read_data(_Out_ T& data)
3423 {
3424#if SET_FILE_OP_TIMES
3425 m_atime = time_point::now();
3426#endif
3427 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3428 data = 0;
3429 return *this;
3430 }
3431 size_t end_offset = m_offset + sizeof(T);
3432 if (end_offset <= m_size) {
3433 data = LE2HE(*reinterpret_cast<T*>(m_data + m_offset));
3434 m_offset = end_offset;
3435#if !CHECK_STREAM_STATE
3436 m_state = state_t::ok;
3437#endif
3438 }
3439 else {
3440 data = 0;
3441 m_offset = m_size;
3442 m_state = state_t::eof;
3443 }
3444 return *this;
3445 }
3446
3461 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
3462 memory_file& read_str(_Inout_ std::basic_string<_Elem, _Traits, _Ax>&data)
3463 {
3464#if SET_FILE_OP_TIMES
3465 m_atime = time_point::now();
3466#endif
3467 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3468 data.clear();
3469 return *this;
3470 }
3471 size_t end_offset = m_offset + sizeof(uint32_t);
3472 if (end_offset <= m_size) {
3473 uint32_t num_chars = LE2HE(*reinterpret_cast<uint32_t*>(m_data + m_offset));
3474 m_offset = end_offset;
3475 end_offset = stdex::add(m_offset, stdex::mul(num_chars, sizeof(_Elem)));
3476 _Elem* start = reinterpret_cast<_Elem*>(m_data + m_offset);
3477 if (end_offset <= m_size) {
3478 data.assign(start, start + num_chars);
3479 m_offset = end_offset;
3480#if !CHECK_STREAM_STATE
3481 m_state = state_t::ok;
3482#endif
3483 return *this;
3484 }
3485 if (end_offset <= m_size)
3486 data.assign(start, reinterpret_cast<_Elem*>(m_data + m_size));
3487 }
3488 m_offset = m_size;
3489 m_state = state_t::eof;
3490 return *this;
3491 }
3492
3493 virtual _Success_(return != 0) size_t write(
3494 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3495 {
3496 _Assume_(data || !length);
3497#if SET_FILE_OP_TIMES
3498 m_atime = m_mtime = time_point::now();
3499#endif
3500 size_t end_offset = m_offset + length;
3501 if (end_offset > m_reserved) {
3502 reserve(end_offset);
3503 if (!ok()) _Unlikely_
3504 return 0;
3505 }
3506 memcpy(m_data + m_offset, data, length);
3507 m_offset = end_offset;
3508 if (m_offset > m_size)
3509 m_size = m_offset;
3510 m_state = state_t::ok;
3511 return length;
3512 }
3513
3517 void write_byte(_In_ uint8_t byte, _In_ size_t amount = 1)
3518 {
3519#if SET_FILE_OP_TIMES
3520 m_atime = m_mtime = time_point::now();
3521#endif
3522 size_t end_offset = m_offset + amount;
3523 if (end_offset > m_reserved) {
3524 reserve(end_offset);
3525 if (!ok()) _Unlikely_
3526 return;
3527 }
3528 memset(m_data + m_offset, byte, amount);
3529 m_offset = end_offset;
3530 if (m_offset > m_size)
3531 m_size = m_offset;
3532 m_state = state_t::ok;
3533 }
3534
3549 template <class T>
3550 inline memory_file& write_data(const T data)
3551 {
3552#if SET_FILE_OP_TIMES
3553 m_atime = m_mtime = time_point::now();
3554#endif
3555 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3556 return *this;
3557 size_t end_offset = m_offset + sizeof(T);
3558 if (end_offset > m_reserved) {
3559 reserve(end_offset);
3560 if (!ok()) _Unlikely_
3561 return *this;
3562 }
3563 (*reinterpret_cast<T*>(m_data + m_offset)) = HE2LE(data);
3564 m_offset = end_offset;
3565 if (m_offset > m_size)
3566 m_size = m_offset;
3567#if !CHECK_STREAM_STATE
3568 m_state = state_t::ok;
3569#endif
3570 return *this;
3571 }
3572
3587 template <class T>
3588 inline memory_file& write_str(_In_z_ const T * data)
3589 {
3590#if SET_FILE_OP_TIMES
3591 m_atime = m_mtime = time_point::now();
3592#endif
3593 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3594 return *this;
3595 size_t num_chars = stdex::strlen(data);
3596 if (num_chars > UINT32_MAX)
3597 throw std::invalid_argument("string too long");
3598 size_t size_chars = num_chars * sizeof(T);
3599 size_t size = sizeof(uint32_t) + size_chars;
3600 size_t end_offset = m_offset + size;
3601 if (end_offset > m_reserved) {
3602 reserve(end_offset);
3603 if (!ok()) _Unlikely_
3604 return *this;
3605 }
3606 auto p = m_data + m_offset;
3607 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3608 memcpy(p + sizeof(uint32_t), data, size_chars);
3609 m_offset = end_offset;
3610 if (m_offset > m_size)
3611 m_size = m_offset;
3612#if !CHECK_STREAM_STATE
3613 m_state = state_t::ok;
3614#endif
3615 return *this;
3616 }
3617
3632 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
3633 inline memory_file& write_str(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data)
3634 {
3635#if SET_FILE_OP_TIMES
3636 m_atime = m_mtime = time_point::now();
3637#endif
3638 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3639 return *this;
3640 size_t num_chars = data.size();
3641 if (num_chars > UINT32_MAX)
3642 throw std::invalid_argument("string too long");
3643 size_t size_chars = num_chars * sizeof(_Elem);
3644 size_t size = sizeof(uint32_t) + size_chars;
3645 size_t end_offset = m_offset + size;
3646 if (end_offset > m_reserved) {
3647 reserve(end_offset);
3648 if (!ok()) _Unlikely_
3649 return *this;
3650 }
3651 auto p = m_data + m_offset;
3652 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3653 memcpy(p + sizeof(uint32_t), data.data(), size_chars);
3654 m_offset = end_offset;
3655 if (m_offset > m_size)
3656 m_size = m_offset;
3657#if !CHECK_STREAM_STATE
3658 m_state = state_t::ok;
3659#endif
3660 return *this;
3661 }
3662
3668 size_t write_stream(_Inout_ basic & stream, _In_ size_t amount = SIZE_MAX)
3669 {
3670#if SET_FILE_OP_TIMES
3671 m_atime = m_mtime = time_point::now();
3672#endif
3673 size_t num_read, dst_offset = m_offset, dst_size = m_offset;
3674 size_t num_copied = 0, to_write = amount;
3675 m_state = state_t::ok;
3676 if (amount != SIZE_MAX) {
3677 dst_size = stdex::add(dst_size, amount);
3678 reserve(dst_size);
3679 if (!ok()) _Unlikely_
3680 return 0;
3681 while (to_write) {
3682 num_read = stream.read(m_data + dst_offset, to_write);
3683 dst_size = dst_offset += num_read;
3684 num_copied += num_read;
3685 to_write -= num_read;
3686 if (!stream.ok()) {
3687 if (stream.state() != state_t::eof)
3688 m_state = state_t::fail;
3689 break;
3690 }
3691 };
3692 }
3693 else {
3694 size_t block_size;
3695 while (to_write) {
3696 block_size = std::min(to_write, default_block_size);
3697 dst_size = stdex::add(dst_size, block_size);
3698 reserve(dst_size);
3699 if (!ok()) _Unlikely_
3700 break;
3701 num_read = stream.read(m_data + dst_offset, block_size);
3702 dst_size = dst_offset += num_read;
3703 num_copied += num_read;
3704 to_write -= num_read;
3705 if (!stream.ok()) {
3706 if (stream.state() != state_t::eof)
3707 m_state = state_t::fail;
3708 break;
3709 }
3710 };
3711 }
3712 m_offset = dst_offset;
3713 if (m_offset > m_size)
3714 m_size = m_offset;
3715 return num_copied;
3716 }
3717
3718 virtual void close()
3719 {
3720 if (m_manage && m_data)
3721 free(m_data);
3722 m_data = nullptr;
3723 m_manage = true;
3724 m_offset = 0;
3725 m_size = m_reserved = 0;
3726#if SET_FILE_OP_TIMES
3727 m_ctime = m_atime = m_mtime = time_point::min();
3728#endif
3729 m_state = state_t::ok;
3730 }
3731
3732 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
3733 {
3734 fpos_t target;
3735 switch (how) {
3736 case seek_t::beg: target = offset; break;
3737 case seek_t::cur: target = static_cast<fpos_t>(m_offset) + offset; break;
3738 case seek_t::end: target = static_cast<fpos_t>(m_size) + offset; break;
3739 default: throw std::invalid_argument("unknown seek origin");
3740 }
3741 if (target <= SIZE_MAX) {
3742 m_state = state_t::ok;
3743 return m_offset = static_cast<size_t>(target);
3744 }
3745 m_state = state_t::fail;
3746 return fpos_max;
3747 }
3748
3749 virtual fpos_t tell() const
3750 {
3751 return m_offset;
3752 }
3753
3754 virtual fsize_t size()
3755 {
3756 return m_size;
3757 }
3758
3759 virtual void truncate()
3760 {
3761#if SET_FILE_OP_TIMES
3762 m_atime = m_mtime = time_point::now();
3763#endif
3764 m_size = m_offset;
3766 }
3767
3768#if SET_FILE_OP_TIMES
3769 virtual time_point ctime() const
3770 {
3771 return m_ctime;
3772 }
3773
3774 virtual time_point atime() const
3775 {
3776 return m_atime;
3777 }
3778
3779 virtual time_point mtime() const
3780 {
3781 return m_mtime;
3782 }
3783
3784 virtual void set_ctime(time_point date)
3785 {
3786 m_ctime = date;
3787 }
3788
3789 virtual void set_atime(time_point date)
3790 {
3791 m_atime = date;
3792 }
3793
3794 virtual void set_mtime(time_point date)
3795 {
3796 m_mtime = date;
3797 }
3798#endif
3799
3800 protected:
3808 template <class T>
3809 inline void set(_In_ fpos_t offset, _In_ const T data)
3810 {
3811#if SET_FILE_OP_TIMES
3812 m_atime = m_mtime = time_point::now();
3813#endif
3814 _Assume_(offset + sizeof(T) < m_size);
3815 (*reinterpret_cast<T*>(m_data + offset)) = HE2LE(data);
3816 }
3817
3818 public:
3819 inline void set(_In_ fpos_t offset, _In_ const int8_t data) { set<int8_t>(offset, data); }
3820 inline void set(_In_ fpos_t offset, _In_ const int16_t data) { set<int16_t>(offset, data); }
3821 inline void set(_In_ fpos_t offset, _In_ const int32_t data) { set<int32_t>(offset, data); }
3822 inline void set(_In_ fpos_t offset, _In_ const int64_t data) { set<int64_t>(offset, data); }
3823 inline void set(_In_ fpos_t offset, _In_ const uint8_t data) { set<uint8_t>(offset, data); }
3824 inline void set(_In_ fpos_t offset, _In_ const uint16_t data) { set<uint16_t>(offset, data); }
3825 inline void set(_In_ fpos_t offset, _In_ const uint32_t data) { set<uint32_t>(offset, data); }
3826 inline void set(_In_ fpos_t offset, _In_ const uint64_t data) { set<uint64_t>(offset, data); }
3827 inline void set(_In_ fpos_t offset, _In_ const float data) { set<float>(offset, data); }
3828 inline void set(_In_ fpos_t offset, _In_ const double data) { set<double>(offset, data); }
3829 inline void set(_In_ fpos_t offset, _In_ const char data) { set<char>(offset, data); }
3830#ifdef _NATIVE_WCHAR_T_DEFINED
3831 inline void set(_In_ fpos_t offset, _In_ const wchar_t data) { set<wchar_t>(offset, data); }
3832#endif
3833
3841 protected:
3842 template <class T>
3843 inline void get(_In_ fpos_t offset, _Out_ T & data)
3844 {
3845 _Assume_(offset + sizeof(T) < m_size);
3846 data = LE2HE(*(T*)(m_data + offset));
3847#if SET_FILE_OP_TIMES
3848 m_atime = time_point::now();
3849#endif
3850 }
3851
3852 public:
3853 inline void get(_In_ fpos_t offset, _Out_ int8_t & data) { get<int8_t>(offset, data); }
3854 inline void get(_In_ fpos_t offset, _Out_ int16_t & data) { get<int16_t>(offset, data); }
3855 inline void get(_In_ fpos_t offset, _Out_ int32_t & data) { get<int32_t>(offset, data); }
3856 inline void get(_In_ fpos_t offset, _Out_ int64_t & data) { get<int64_t>(offset, data); }
3857 inline void get(_In_ fpos_t offset, _Out_ uint8_t & data) { get<uint8_t>(offset, data); }
3858 inline void get(_In_ fpos_t offset, _Out_ uint16_t & data) { get<uint16_t>(offset, data); }
3859 inline void get(_In_ fpos_t offset, _Out_ uint32_t & data) { get<uint32_t>(offset, data); }
3860 inline void get(_In_ fpos_t offset, _Out_ uint64_t & data) { get<uint64_t>(offset, data); }
3861 inline void get(_In_ fpos_t offset, _Out_ float& data) { get<float>(offset, data); }
3862 inline void get(_In_ fpos_t offset, _Out_ double& data) { get<double>(offset, data); }
3863 inline void get(_In_ fpos_t offset, _Out_ char& data) { get<char>(offset, data); }
3864#ifdef _NATIVE_WCHAR_T_DEFINED
3865 inline void get(_In_ fpos_t offset, _Out_ wchar_t& data) { get<wchar_t>(offset, data); }
3866#endif
3867
3868 inline memory_file& operator <<(_In_ const int8_t data) { return write_data(data); }
3869 inline memory_file& operator >>(_Out_ int8_t & data) { return read_data(data); }
3870 inline memory_file& operator <<(_In_ const int16_t data) { return write_data(data); }
3871 inline memory_file& operator >>(_Out_ int16_t & data) { return read_data(data); }
3872 inline memory_file& operator <<(_In_ const int32_t data) { return write_data(data); }
3873 inline memory_file& operator >>(_Out_ int32_t & data) { return read_data(data); }
3874 inline memory_file& operator <<(_In_ const int64_t data) { return write_data(data); }
3875 inline memory_file& operator >>(_Out_ int64_t & data) { return read_data(data); }
3876 inline memory_file& operator <<(_In_ const uint8_t data) { return write_data(data); }
3877 inline memory_file& operator >>(_Out_ uint8_t & data) { return read_data(data); }
3878 inline memory_file& operator <<(_In_ const uint16_t data) { return write_data(data); }
3879 inline memory_file& operator >>(_Out_ uint16_t & data) { return read_data(data); }
3880 inline memory_file& operator <<(_In_ const uint32_t data) { return write_data(data); }
3881 inline memory_file& operator >>(_Out_ uint32_t & data) { return read_data(data); }
3882 inline memory_file& operator <<(_In_ const uint64_t data) { return write_data(data); }
3883 inline memory_file& operator >>(_Out_ uint64_t & data) { return read_data(data); }
3884 inline memory_file& operator <<(_In_ const float data) { return write_data(data); }
3885 inline memory_file& operator >>(_Out_ float& data) { return read_data(data); }
3886 inline memory_file& operator <<(_In_ const double data) { return write_data(data); }
3887 inline memory_file& operator >>(_Out_ double& data) { return read_data(data); }
3888 inline memory_file& operator <<(_In_ const char data) { return write_data(data); }
3889 inline memory_file& operator >>(_Out_ char& data) { return read_data(data); }
3890#ifdef _NATIVE_WCHAR_T_DEFINED
3891 inline memory_file& operator <<(_In_ const wchar_t data) { return write_data(data); }
3892 inline memory_file& operator >>(_Out_ wchar_t& data) { return read_data(data); }
3893#endif
3894 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
3895 inline memory_file& operator >>(_Out_ std::basic_string<_Elem, _Traits, _Ax>&data) { return read_str(data); }
3896 template <class T>
3897 inline memory_file& operator <<(_In_ const T * data) { return write_str(data); }
3898 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
3899 inline memory_file& operator <<(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data) { return write_str(data); }
3900
3901 protected:
3902 uint8_t* m_data;
3904 size_t m_offset;
3905 size_t m_size;
3906 size_t m_reserved;
3907#if SET_FILE_OP_TIMES
3908 time_point
3909 m_ctime,
3910 m_atime,
3911 m_mtime;
3912#endif
3913 };
3914
3918 class fifo : public basic {
3919 public:
3920 fifo() :
3921 m_offset(0),
3922 m_size(0),
3923 m_head(nullptr),
3924 m_tail(nullptr)
3925 {}
3926
3927 virtual ~fifo()
3928 {
3929 while (m_head) {
3930 auto p = m_head;
3931 m_head = p->next;
3932 delete p;
3933 }
3934 }
3935
3936#pragma warning(suppress: 6101) // See [2] below
3937 virtual _Success_(return != 0 || length == 0) size_t read(
3938 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
3939 {
3940 _Assume_(data || !length);
3941 for (size_t to_read = length;;) {
3942 if (!m_head) _Unlikely_ {
3943 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
3944 return length - to_read; // [2] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
3945 }
3946 size_t remaining = m_head->size - m_offset;
3947 if (remaining > to_read) {
3948 memcpy(data, m_head->data + m_offset, to_read);
3949 m_offset += to_read;
3950 m_size -= to_read;
3951 m_state = state_t::ok;
3952 return length;
3953 }
3954 memcpy(data, m_head->data + m_offset, remaining);
3955 m_offset = 0;
3956 m_size -= remaining;
3957 reinterpret_cast<uint8_t*&>(data) += remaining;
3958 to_read -= remaining;
3959 auto p = m_head;
3960 m_head = p->next;
3961 delete p;
3962 }
3963 }
3964
3965 virtual _Success_(return != 0) size_t write(
3966 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3967 {
3968 _Assume_(data || !length);
3969 try {
3970 std::unique_ptr<node_t> n(reinterpret_cast<node_t*>(new uint8_t[sizeof(node_t) + length]));
3971 n->next = nullptr;
3972 n->size = length;
3973 memcpy(n->data, data, length);
3974 m_size += length;
3975 if (m_head)
3976 m_tail = m_tail->next = n.release();
3977 else
3978 m_head = m_tail = n.release();
3979 m_state = state_t::ok;
3980 return length;
3981 }
3982 catch (const std::bad_alloc&) {
3983 m_state = state_t::fail;
3984 return 0;
3985 }
3986 }
3987
3988 virtual void close()
3989 {
3990 m_size = m_offset = 0;
3991 while (m_head) {
3992 auto p = m_head;
3993 m_head = p->next;
3994 delete p;
3995 }
3996 m_state = state_t::ok;
3997 }
3998
4002 inline size_t size() const { return m_size; };
4003
4004 protected:
4005 size_t m_offset, m_size;
4006 struct node_t {
4007 node_t* next;
4008 size_t size;
4009#pragma warning(suppress:4200)
4010 uint8_t data[0];
4011 } *m_head, * m_tail;
4012 };
4013
4017 class diag_file : public basic_file {
4018 public:
4019 diag_file(_In_count_(num_files) basic_file* const* files, _In_ size_t num_files) :
4020 basic(num_files ? files[0]->state() : state_t::fail),
4021 m_files(files, files + num_files)
4022 {}
4023
4024 virtual _Success_(return != 0 || length == 0) size_t read(
4025 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4026 {
4027 _Assume_(data || !length);
4028 if (m_files.empty()) {
4029 m_state = state_t::fail;
4030 return 0;
4031 }
4032 size_t result = m_files[0]->read(data, length);
4033 _Assume_(result <= length);
4034 m_state = m_files[0]->state();
4035 if (length > m_tmp.size())
4036 m_tmp.resize(length);
4037 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4038 if (m_files[i]->read(m_tmp.data(), length) != result ||
4039 memcmp(m_tmp.data(), data, result))
4040 throw std::runtime_error("read mismatch");
4041 if (m_files[i]->state() != m_state)
4042 throw std::runtime_error("state mismatch");
4043 }
4044 return result;
4045 }
4046
4047 virtual _Success_(return != 0) size_t write(
4048 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4049 {
4050 if (m_files.empty()) {
4051 m_state = state_t::fail;
4052 return 0;
4053 }
4054 size_t result = m_files[0]->write(data, length);
4055 m_state = m_files[0]->state();
4056 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4057 if (m_files[i]->write(data, length) != result)
4058 throw std::runtime_error("write mismatch");
4059 if (m_files[i]->state() != m_state)
4060 throw std::runtime_error("state mismatch");
4061 }
4062 return result;
4063 }
4064
4065 virtual void flush()
4066 {
4067 if (m_files.empty()) {
4068 m_state = state_t::ok;
4069 return;
4070 }
4071 m_files[0]->flush();
4072 m_state = m_files[0]->state();
4073 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4074 m_files[i]->flush();
4075 if (m_files[i]->state() != m_state)
4076 throw std::runtime_error("state mismatch");
4077 }
4078 }
4079
4080 virtual void close()
4081 {
4082 if (m_files.empty()) {
4083 m_state = state_t::ok;
4084 return;
4085 }
4086 m_files[0]->close();
4087 m_state = m_files[0]->state();
4088 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4089 m_files[i]->close();
4090 if (m_files[i]->state() != m_state)
4091 throw std::runtime_error("state mismatch");
4092 }
4093 m_tmp.clear();
4094 m_tmp.shrink_to_fit();
4095 }
4096
4097 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
4098 {
4099 if (m_files.empty()) {
4100 m_state = state_t::fail;
4101 return fpos_max;
4102 }
4103 fpos_t result = m_files[0]->seek(offset, how);
4104 m_state = m_files[0]->state();
4105 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4106 if (m_files[i]->seek(offset, how) != result)
4107 throw std::runtime_error("seek mismatch");
4108 if (m_files[i]->state() != m_state)
4109 throw std::runtime_error("state mismatch");
4110 }
4111 return result;
4112 }
4113
4114 virtual fpos_t tell() const
4115 {
4116 if (m_files.empty())
4117 return fpos_max;
4118 fpos_t result = m_files[0]->tell();
4119 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4120 if (m_files[i]->tell() != result)
4121 throw std::runtime_error("tell mismatch");
4122 }
4123 return result;
4124 }
4125
4126 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
4127 {
4128 if (m_files.empty())
4129 m_state = state_t::fail;
4130 m_files[0]->lock(offset, length);
4131 m_state = m_files[0]->state();
4132 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4133 m_files[i]->lock(offset, length);
4134 if (m_files[i]->state() != m_state)
4135 throw std::runtime_error("state mismatch");
4136 }
4137 }
4138
4139 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
4140 {
4141 if (m_files.empty())
4142 m_state = state_t::fail;
4143 m_files[0]->unlock(offset, length);
4144 m_state = m_files[0]->state();
4145 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4146 m_files[i]->unlock(offset, length);
4147 if (m_files[i]->state() != m_state)
4148 throw std::runtime_error("state mismatch");
4149 }
4150 }
4151
4152 virtual fsize_t size()
4153 {
4154 if (m_files.empty()) {
4155 m_state = state_t::fail;
4156 return 0;
4157 }
4158 fsize_t result = m_files[0]->size();
4159 m_state = m_files[0]->state();
4160 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4161 if (m_files[i]->size() != result)
4162 throw std::runtime_error("size mismatch");
4163 if (m_files[i]->state() != m_state)
4164 throw std::runtime_error("state mismatch");
4165 }
4166 return result;
4167 }
4168
4169 virtual void truncate()
4170 {
4171 if (m_files.empty())
4172 m_state = state_t::fail;
4173 m_files[0]->truncate();
4174 m_state = m_files[0]->state();
4175 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4176 m_files[i]->truncate();
4177 if (m_files[i]->state() != m_state)
4178 throw std::runtime_error("state mismatch");
4179 }
4180 }
4181
4182 protected:
4183 std::vector<basic_file*> m_files;
4184 std::vector<uint8_t> m_tmp;
4185 };
4186 }
4187}
Encoding converter context.
Definition unicode.hpp:64
Provides read-ahead stream capability.
Definition stream.hpp:1255
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:1269
Provides write-back stream capability.
Definition stream.hpp:1322
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1359
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:1335
Basic seekable stream operations.
Definition stream.hpp:823
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:866
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:913
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:882
virtual void truncate()=0
Sets file size - truncates the remainder of file content from the current file position to the end of...
charset_id read_charset(charset_id default_charset=charset_id::system)
Attempts to detect textfile charset based on UTF-32, UTF-16 or UTF-8 BOM.
Definition stream.hpp:986
fpos_t seekbeg(fpos_t offset)
Seeks to absolute file position.
Definition stream.hpp:850
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:825
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:955
fpos_t seekcur(foff_t offset)
Seeks to relative from current file position.
Definition stream.hpp:857
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:921
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:937
virtual fsize_t size()=0
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:892
virtual fpos_t tell() const =0
Returns absolute file position in file or fpos_max if fails. This method does not update stream state...
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:929
fpos_t seekend(foff_t offset)
Seeks to relative from end file position.
Definition stream.hpp:864
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:946
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)=0
Seeks to specified relative file position.
OS data stream (file, pipe, socket...)
Definition stream.hpp:2195
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:2252
virtual void flush()
Persists volatile element data.
Definition stream.hpp:2310
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:2202
virtual void close()
Closes the stream.
Definition stream.hpp:2299
‍UTF-8 byte-order-mark
Definition stream.hpp:78
bool ok() const
Returns true if the stream state is clean i.e. previous operation was succesful.
Definition stream.hpp:174
size_t write_vsprintf(_Printf_format_string_params_(2) const char *format, locale_t locale, va_list params)
Writes formatted string to the stream.
Definition stream.hpp:636
size_t write_array(const std::basic_string< T_from, _Traits, _Ax > &wstr, charset_encoder< T_from, T_to > &encoder)
Writes array of characters to the stream.
Definition stream.hpp:454
state_t state() const
Returns stream state after last operation.
Definition stream.hpp:169
basic & read_str(std::basic_string< _Elem, _Traits, _Ax > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:476
size_t write_sprintf(_Printf_format_string_params_(2) const wchar_t *format, locale_t locale,...)
Writes formatted string to the stream.
Definition stream.hpp:622
size_t write_vsprintf(_Printf_format_string_params_(2) const wchar_t *format, locale_t locale, va_list params)
Writes formatted string to the stream.
Definition stream.hpp:649
virtual void flush()
Persists volatile element data.
Definition stream.hpp:125
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:141
virtual void close()
Closes the stream.
Definition stream.hpp:133
uint8_t read_byte()
Reads one byte of data.
Definition stream.hpp:209
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:183
size_t write_sprintf(_Printf_format_string_params_(2) const char *format, locale_t locale,...)
Writes formatted string to the stream.
Definition stream.hpp:608
size_t readln(std::basic_string< char, _Traits, _Ax > &str)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:305
size_t readln_and_attach(std::basic_string< _Elem, _Traits, _Ax > &str)
Reads stream to the end-of-line or end-of-file and append to str.
Definition stream.hpp:345
size_t read_array(_Out_writes_bytes_(size *count) void *array, size_t size, size_t count)
Reads an array of data from the stream.
Definition stream.hpp:381
basic & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:508
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:95
size_t readln(std::basic_string< wchar_t, _Traits, _Ax > &wstr)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:317
void write_charset(charset_id charset)
Writes UTF8, UTF-16 or UTF-32 byte-order-mark.
Definition stream.hpp:593
basic & write_str(const std::basic_string< _Elem, _Traits, _Ax > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:533
basic & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:286
size_t write_array(const T_from *wstr, charset_encoder< T_from, T_to > &encoder)
Writes array of characters to the stream.
Definition stream.hpp:413
size_t readln_and_attach(std::basic_string< T_to, _Traits, _Ax > &wstr, charset_encoder< T_from, T_to > &encoder)
Reads stream to the end-of-line or end-of-file and append to str.
Definition stream.hpp:366
fsize_t write_stream(basic &stream, fsize_t amount=fsize_max)
Writes content of another stream.
Definition stream.hpp:568
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:113
size_t write_array(_In_reads_or_z_opt_(num_chars) const T_from *wstr, size_t num_chars, charset_encoder< T_from, T_to > &encoder)
Writes array of characters to the stream.
Definition stream.hpp:434
size_t readln(std::basic_string< T_to, _Traits, _Ax > &wstr, charset_encoder< T_from, T_to > &encoder)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:329
size_t write_array(_In_reads_bytes_opt_(size *count) const void *array, size_t size, size_t count)
Writes an array of data to the stream.
Definition stream.hpp:399
void write_byte(uint8_t byte, fsize_t amount=1)
Writes a byte of data.
Definition stream.hpp:220
basic & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:258
Buffered read/write stream.
Definition stream.hpp:1393
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1502
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:1423
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:1459
Buffered OS data stream (file, pipe, socket...)
Definition stream.hpp:2324
Cached file.
Definition stream.hpp:1803
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2065
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2044
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:1870
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2070
virtual fsize_t size()
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:2037
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2031
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:2079
virtual void close()
Closes the stream.
Definition stream.hpp:1985
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:2101
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2025
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1994
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:1932
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:2088
virtual fpos_t tell() const
Returns absolute file position in file or fpos_max if fails. This method does not update stream state...
Definition stream.hpp:2020
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:2093
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2005
Cached file-system file.
Definition stream.hpp:3094
cached_file(const stdex::sstring &filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3124
void open(const stdex::sstring &filename, int mode)
Opens file.
Definition stream.hpp:3158
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:3137
cached_file(const schar_t *filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3110
Modifies data on the fly when reading from/writing to a source stream. Could also be used to modify r...
Definition stream.hpp:1021
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1072
virtual void close()
Closes the stream.
Definition stream.hpp:1066
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:1050
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:1058
Compares multiple files to perform the same.
Definition stream.hpp:4017
virtual fsize_t size()
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:4152
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:4169
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:4047
virtual void close()
Closes the stream.
Definition stream.hpp:4080
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:4126
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:4139
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:4097
virtual fpos_t tell() const
Returns absolute file position in file or fpos_max if fails. This method does not update stream state...
Definition stream.hpp:4114
virtual void flush()
Persists volatile element data.
Definition stream.hpp:4065
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:4024
In-memory FIFO queue.
Definition stream.hpp:3918
virtual void close()
Closes the stream.
Definition stream.hpp:3988
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:3965
size_t size() const
Returns total size of pending data in the queue.
Definition stream.hpp:4002
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:3937
Limits file reading/writing to a predefined window.
Definition stream.hpp:1693
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:1786
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1736
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:1749
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:1742
virtual fsize_t size()
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:1781
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:1716
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:1761
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:1702
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:1771
virtual fpos_t tell() const
Returns absolute file position in file or fpos_max if fails. This method does not update stream state...
Definition stream.hpp:1755
virtual void close()
Closes the stream.
Definition stream.hpp:1730
File-system file.
Definition stream.hpp:2690
file(const stdex::sstring &filename, int mode)
Opens file.
Definition stream.hpp:2711
static bool readonly(const stdex::sstring &filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3083
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:2966
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2859
file(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2700
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:2980
static bool readonly(const stdex::schar_t *filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3065
static bool exists(const stdex::sstring &filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3053
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2952
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2719
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:3014
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:2994
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2836
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2902
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2942
void open(const stdex::sstring &filename, int mode)
Opens file.
Definition stream.hpp:2792
virtual fsize_t size()
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:2884
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2797
virtual fpos_t tell() const
Returns absolute file position in file or fpos_max if fails. This method does not update stream state...
Definition stream.hpp:2818
static bool exists(const stdex::schar_t *filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3038
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1550
fsize_t read_limit
Number of bytes left that may be read from the stream.
Definition stream.hpp:1602
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:1558
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:1579
fsize_t write_limit
Number of bytes left, that can be written to the stream.
Definition stream.hpp:1603
In-memory file.
Definition stream.hpp:3171
memory_file(const schar_t *filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3249
memory_file & write_str(const std::basic_string< _Elem, _Traits, _Ax > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3633
size_t m_size
file size
Definition stream.hpp:3905
void get(fpos_t offset, T &data)
Reads data from specified file location This does not move file pointer. It checks for data size Assu...
Definition stream.hpp:3843
size_t write_stream(basic &stream, size_t amount=SIZE_MAX)
Writes content of another stream.
Definition stream.hpp:3668
uint8_t * m_data
file data
Definition stream.hpp:3902
memory_file & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:3422
virtual void close()
Closes the stream.
Definition stream.hpp:3718
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:3383
virtual fpos_t tell() const
Returns absolute file position in file or fpos_max if fails. This method does not update stream state...
Definition stream.hpp:3749
size_t m_reserved
reserved file size
Definition stream.hpp:3906
memory_file(size_t size, state_t state=state_t::ok)
Creates an empty file of reserved size.
Definition stream.hpp:3192
void reserve(size_t required, bool tight=false) noexcept
Reallocates memory.
Definition stream.hpp:3274
memory_file(const stdex::sstring &filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3260
memory_file & read_str(std::basic_string< _Elem, _Traits, _Ax > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:3462
void write_byte(uint8_t byte, size_t amount=1)
Writes a byte of data.
Definition stream.hpp:3517
void set(fpos_t offset, const T data)
Writes data to specified file location This does not move file pointer nor update file size....
Definition stream.hpp:3809
void load(const stdex::sstring &filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3336
size_t m_offset
file pointer
Definition stream.hpp:3904
void save(const schar_t *filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3347
void load(const schar_t *filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3303
virtual fsize_t size()
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:3754
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3732
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:3759
memory_file & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:3550
memory_file & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3588
void save(const stdex::sstring &filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3373
bool m_manage
may reallocate m_data?
Definition stream.hpp:3903
memory_file(void *data, size_t size, bool manage=false, state_t state=state_t::ok)
Creates a file based on available data.
Definition stream.hpp:3239
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:3493
memory_file(void *data, size_t size, size_t reserved, bool manage=false, state_t state=state_t::ok)
Creates a file based on available data.
Definition stream.hpp:3216
const void * data() const
Returns pointer to data.
Definition stream.hpp:3381
Definition stream.hpp:1169
enum stdex::stream::replicator::worker::op_t op
Operation to perform.
size_t num_written
Number of bytes written.
Definition stream.hpp:1218
size_t length
Byte limit of data to write.
Definition stream.hpp:1217
const void * data
Data to write.
Definition stream.hpp:1216
Replicates writing of the same data to multiple streams.
Definition stream.hpp:1086
void push_back(basic *source)
Adds stream on the list.
Definition stream.hpp:1105
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1162
void remove(basic *source)
Removes stream from the list.
Definition stream.hpp:1113
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:1130
virtual void close()
Closes the stream.
Definition stream.hpp:1157
Socket stream.
Definition stream.hpp:2346
virtual void close()
Closes the stream.
Definition stream.hpp:2448
socket(int af, int type, int protocol)
Creates a socket.
Definition stream.hpp:2381
SOCKET get() const noexcept
Returns socket handle.
Definition stream.hpp:2402
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:2404
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:2428
Limits reading from/writing to stream to a predefined window.
Definition stream.hpp:1610
fpos_t write_offset
Number of bytes to discard on write.
Definition stream.hpp:1686
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:1647
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:1618
fpos_t read_offset
Number of bytes to skip on read.
Definition stream.hpp:1685
Operating system object (file, pipe, anything with an OS handle etc.)
Definition system.hpp:91
virtual void close()
Closes object.
Definition system.hpp:132
Numerical interval.
Definition interval.hpp:18
bool contains(T x) const
Is value in interval?
Definition interval.hpp:70
T size() const
Returns interval size.
Definition interval.hpp:47
T end
interval end
Definition interval.hpp:20
T start
interval start
Definition interval.hpp:19
Definition stream.hpp:1527
Definition stream.hpp:4006