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