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 state_t state() const { return m_state; };
171
175 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 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 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 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 _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
306 size_t readln(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
307 {
308 str.clear();
309 return readln_and_attach(str);
310 }
311
317 template<class T_from, class T_to, class _Traits = std::char_traits<T_to>, class _Ax = std::allocator<T_to>>
318 size_t readln(_Inout_ std::basic_string<T_to, _Traits, _Ax>& str, _In_ charset_encoder<T_from, T_to>& encoder)
319 {
320 if (encoder.from_encoding() == encoder.to_encoding())
321 return readln(str);
322 std::basic_string<T_from> tmp;
324 encoder.strcpy(str, tmp);
325 return str.size();
326 }
327
333 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
334 size_t readln_and_attach(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
335 {
336 bool initial = true;
337 _Elem chr, previous = (_Elem)0;
338 do {
339 read_array(&chr, sizeof(_Elem), 1);
340 if (!initial && !(previous == static_cast<_Elem>('\r') && chr == static_cast<_Elem>('\n')))
341 str += previous;
342 else
343 initial = false;
344 previous = chr;
345 } while (ok() && chr != static_cast<_Elem>('\n'));
346 return str.size();
347 }
348
354 template<class T_from, class T_to, class _Traits = std::char_traits<T_to>, class _Ax = std::allocator<T_to>>
355 size_t readln_and_attach(_Inout_ std::basic_string<T_to, _Traits, _Ax>& str, _In_ charset_encoder<T_from, T_to>& encoder)
356 {
357 if (encoder.from_encoding() == encoder.to_encoding())
358 return readln_and_attach(str);
359 std::basic_string<T_from> tmp;
361 encoder.strcat(str, tmp);
362 return str.size();
363 }
364
370 size_t read_array(_Out_writes_bytes_(size* count) void* array, _In_ size_t size, _In_ size_t count)
371 {
372 for (size_t to_read = mul(size, count);;) {
373 size_t num_read = read(array, to_read);
374 to_read -= num_read;
375 if (!to_read)
376 return count;
377 if (!ok()) _Unlikely_
378 return count - to_read / size;
379 reinterpret_cast<uint8_t*&>(array) += num_read;
380 }
381 }
382
388 size_t write_array(_In_reads_bytes_opt_(size* count) const void* array, _In_ size_t size, _In_ size_t count)
389 {
390 return write(array, mul(size, count)) / size;
391 }
392
401 template <class T_from, class T_to>
402 size_t write_array(_In_z_ const T_from* str, _In_ charset_encoder<T_from, T_to>& encoder)
403 {
404 if (!ok()) _Unlikely_
405 return 0;
406 size_t num_chars = stdex::strlen(str);
407 if (encoder.from_encoding() == encoder.to_encoding())
408 return write_array(str, sizeof(T_from), num_chars);
409 std::basic_string<T_to> tmp(encoder.convert(str, num_chars));
410 return write_array(tmp.data(), sizeof(T_to), tmp.size());
411 }
412
422 template <class T_from, class T_to>
423 size_t write_array(_In_reads_or_z_opt_(num_chars) const T_from* str, _In_ size_t num_chars, _In_ charset_encoder<T_from, T_to>& encoder)
424 {
425 if (!ok()) _Unlikely_
426 return 0;
427 num_chars = stdex::strnlen(str, num_chars);
428 if (encoder.from_encoding() == encoder.to_encoding())
429 return write_array(str, sizeof(T_from), num_chars);
430 std::basic_string<T_to> tmp(encoder.convert(str, num_chars));
431 return write_array(tmp.data(), sizeof(T_to), tmp.size());
432 }
433
442 template<class T_from, class T_to, class _Traits = std::char_traits<T_from>, class _Ax = std::allocator<T_from>>
443 size_t write_array(_In_ const std::basic_string<T_from, _Traits, _Ax>& str, _In_ charset_encoder<T_from, T_to>& encoder)
444 {
445 if (!ok()) _Unlikely_
446 return 0;
447 if (encoder.from_encoding() == encoder.to_encoding())
448 return write_array(str.data(), sizeof(T_from), str.size());
449 std::basic_string<T_to> tmp(encoder.convert(str));
450 return write_array(tmp.data(), sizeof(T_to), tmp.size());
451 }
452
464 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
465 basic& read_str(_Out_ std::basic_string<_Elem, _Traits, _Ax>& data)
466 {
467 data.clear();
468 if (!ok()) _Unlikely_
469 return *this;
470 uint32_t num_chars;
471 read_data(num_chars);
472 if (!ok()) _Unlikely_
473 return *this;
474 data.reserve(num_chars);
475 for (;;) {
476 _Elem buf[0x400];
477 uint32_t num_read = static_cast<uint32_t>(read_array(buf, sizeof(_Elem), std::min<uint32_t>(num_chars, _countof(buf))));
478 data.append(buf, num_read);
479 num_chars -= num_read;
480 if (!num_chars || !ok())
481 return *this;
482 }
483 }
484
496 template <class T>
497 basic& write_str(_In_z_ const T* data)
498 {
499 // Stream state will be checked in write_data.
500 size_t num_chars = stdex::strlen(data);
501 if (num_chars > UINT32_MAX)
502 throw std::invalid_argument("string too long");
503 write_data(static_cast<uint32_t>(num_chars));
504 if (!ok()) _Unlikely_
505 return *this;
506 write_array(data, sizeof(T), num_chars);
507 return *this;
508 }
509
521 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
522 basic& write_str(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data)
523 {
524 // Stream state will be checked in write_data.
525 size_t num_chars = data.size();
526 if (num_chars > UINT32_MAX)
527 throw std::invalid_argument("string too long");
528 write_data(static_cast<uint32_t>(num_chars));
529 if (!ok()) _Unlikely_
530 return *this;
531 write_array(data.data(), sizeof(_Elem), num_chars);
532 return *this;
533 }
534
535#ifdef _WIN32
541 size_t write_sa(_In_ LPSAFEARRAY sa)
542 {
543 safearray_accessor<void> a(sa);
544 long ubound, lbound;
545 if (FAILED(SafeArrayGetUBound(sa, 1, &ubound)) ||
546 FAILED(SafeArrayGetLBound(sa, 1, &lbound)))
547 throw std::invalid_argument("SafeArrayGet[UL]Bound failed");
548 return write(a.data(), static_cast<size_t>(ubound) - lbound + 1);
549 }
550#endif
551
557 fsize_t write_stream(_Inout_ basic& stream, _In_ fsize_t amount = fsize_max)
558 {
559 std::unique_ptr<uint8_t[]> data(new uint8_t[static_cast<size_t>(std::min<fsize_t>(amount, default_block_size))]);
560 fsize_t num_copied = 0, to_write = amount;
561 m_state = state_t::ok;
562 while (to_write) {
563 size_t num_read = stream.read(data.get(), static_cast<size_t>(std::min<fsize_t>(default_block_size, to_write)));
564 size_t num_written = write(data.get(), num_read);
565 num_copied += num_written;
566 to_write -= num_written;
567 if (stream.m_state == state_t::eof) {
568 // EOF is not an error.
569 m_state = state_t::ok;
570 break;
571 }
572 m_state = stream.m_state;
573 if (!ok())
574 break;
575 }
576 return num_copied;
577 }
578
582 void write_charset(_In_ charset_id charset)
583 {
584 if (charset == charset_id::utf32)
585 write_data(utf32_bom);
586 else if (charset == charset_id::utf16)
587 write_data(utf16_bom);
588 else if (charset == charset_id::utf8)
589 write_array(utf8_bom, sizeof(utf8_bom), 1);
590 }
591
597 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, ...)
598 {
599 va_list params;
600 va_start(params, locale);
601 size_t num_chars = write_vsprintf(format, locale, params);
602 va_end(params);
603 return num_chars;
604 }
605
611 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, ...)
612 {
613 va_list params;
614 va_start(params, locale);
615 size_t num_chars = write_vsprintf(format, locale, params);
616 va_end(params);
617 return num_chars;
618 }
619
625 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, _In_ va_list params)
626 {
627 std::string tmp;
628 tmp.reserve(default_block_size);
629 vappendf(tmp, format, locale, params);
630 return write_array(tmp.data(), sizeof(char), tmp.size());
631 }
632
638 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, _In_ va_list params)
639 {
640 std::wstring tmp;
641 tmp.reserve(default_block_size);
642 vappendf(tmp, format, locale, params);
643 return write_array(tmp.data(), sizeof(wchar_t), tmp.size());
644 }
645
646 basic& operator >>(_Out_ int8_t& data) { return read_data(data); }
647 basic& operator <<(_In_ const int8_t data) { return write_data(data); }
648 basic& operator >>(_Out_ int16_t& data) { return read_data(data); }
649 basic& operator <<(_In_ const int16_t data) { return write_data(data); }
650 basic& operator >>(_Out_ int32_t& data) { return read_data(data); }
651 basic& operator <<(_In_ const int32_t data) { return write_data(data); }
652 basic& operator >>(_Out_ int64_t& data) { return read_data(data); }
653 basic& operator <<(_In_ const int64_t data) { return write_data(data); }
654 basic& operator >>(_Out_ uint8_t& data) { return read_data(data); }
655 basic& operator <<(_In_ const uint8_t data) { return write_data(data); }
656 basic& operator >>(_Out_ uint16_t& data) { return read_data(data); }
657 basic& operator <<(_In_ const uint16_t data) { return write_data(data); }
658 basic& operator >>(_Out_ uint32_t& data) { return read_data(data); }
659 basic& operator <<(_In_ const uint32_t data) { return write_data(data); }
660 basic& operator >>(_Out_ uint64_t& data) { return read_data(data); }
661 basic& operator <<(_In_ const uint64_t data) { return write_data(data); }
662 basic& operator >>(_Out_ float& data) { return read_data(data); }
663 basic& operator <<(_In_ const float data) { return write_data(data); }
664 basic& operator >>(_Out_ double& data) { return read_data(data); }
665 basic& operator <<(_In_ const double data) { return write_data(data); }
666 basic& operator >>(_Out_ char& data) { return read_data(data); }
667 basic& operator <<(_In_ const char data) { return write_data(data); }
668#ifdef _NATIVE_WCHAR_T_DEFINED
669 basic& operator >>(_Out_ wchar_t& data) { return read_data(data); }
670 basic& operator <<(_In_ const wchar_t data) { return write_data(data); }
671#endif
672 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
673 basic& operator >>(_Out_ std::basic_string<_Elem, _Traits, _Ax>& data) { return read_str(data); }
674 template <class T>
675 basic& operator <<(_In_ const T* data) { return write_str(data); }
676 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
677 basic& operator <<(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data) { return write_str(data); }
678
679 template <class _Ty, class _Alloc = std::allocator<_Ty>>
680 basic& operator <<(_In_ const std::vector<_Ty, _Alloc>& data)
681 {
682 size_t num = data.size();
683 if (num > UINT32_MAX) _Unlikely_
684 throw std::invalid_argument("collection too big");
685 *this << static_cast<uint32_t>(num);
686 for (auto& el : data)
687 *this << el;
688 return *this;
689 }
690
691 template <class _Ty, class _Alloc = std::allocator<_Ty>>
692 basic& operator >>(_Out_ std::vector<_Ty, _Alloc>& data)
693 {
694 data.clear();
695 uint32_t num;
696 *this >> num;
697 if (!ok()) _Unlikely_
698 return *this;
699 data.reserve(num);
700 for (uint32_t i = 0; i < num; ++i) {
701 _Ty el;
702 *this >> el;
703 if (!ok()) _Unlikely_
704 return *this;
705 data.push_back(std::move(el));
706 }
707 }
708
709 template <class _Kty, class _Pr = std::less<_Kty>, class _Alloc = std::allocator<_Kty>>
710 basic& operator <<(_In_ const std::set<_Kty, _Pr, _Alloc>& data)
711 {
712 size_t num = data.size();
713 if (num > UINT32_MAX) _Unlikely_
714 throw std::invalid_argument("collection too big");
715 *this << static_cast<uint32_t>(num);
716 for (auto& el : data)
717 *this << el;
718 return *this;
719 }
720
721 template <class _Kty, class _Pr = std::less<_Kty>, class _Alloc = std::allocator<_Kty>>
722 basic& operator >>(_Out_ std::set<_Kty, _Pr, _Alloc>& data)
723 {
724 data.clear();
725 uint32_t num;
726 *this >> num;
727 if (!ok()) _Unlikely_
728 return *this;
729 for (uint32_t i = 0; i < num; ++i) {
730 _Kty el;
731 *this >> el;
732 if (!ok()) _Unlikely_
733 return *this;
734 data.insert(std::move(el));
735 }
736 }
737
738 template <class _Kty, class _Pr = std::less<_Kty>, class _Alloc = std::allocator<_Kty>>
739 basic& operator <<(_In_ const std::multiset<_Kty, _Pr, _Alloc>& data)
740 {
741 size_t num = data.size();
742 if (num > UINT32_MAX) _Unlikely_
743 throw std::invalid_argument("collection too big");
744 *this << static_cast<uint32_t>(num);
745 for (auto& el : data)
746 *this << el;
747 return *this;
748 }
749
750 template <class _Kty, class _Pr = std::less<_Kty>, class _Alloc = std::allocator<_Kty>>
751 basic& operator >>(_Out_ std::multiset<_Kty, _Pr, _Alloc>& data)
752 {
753 data.clear();
754 uint32_t num;
755 *this >> num;
756 if (!ok()) _Unlikely_
757 return *this;
758 for (uint32_t i = 0; i < num; ++i) {
759 _Kty el;
760 *this >> el;
761 if (!ok()) _Unlikely_
762 return *this;
763 data.insert(std::move(el));
764 }
765 return *this;
766 }
767
768 protected:
769 state_t m_state;
770 };
771
775 using fpos_t = uint64_t;
776 constexpr fpos_t fpos_max = UINT64_MAX;
777 constexpr fpos_t fpos_min = 0;
778
782 using foff_t = int64_t;
783 constexpr foff_t foff_max = INT64_MAX;
784 constexpr foff_t foff_min = INT64_MIN;
785
789 enum class seek_t {
790#ifdef _WIN32
791 beg = FILE_BEGIN,
792 cur = FILE_CURRENT,
793 end = FILE_END
794#else
795 beg = SEEK_SET,
796 cur = SEEK_CUR,
797 end = SEEK_END
798#endif
799 };
800
801#if _HAS_CXX20
802 using clock = std::chrono::file_clock;
803#else
804 using clock = std::chrono::system_clock;
805#endif
806 using time_point = std::chrono::time_point<clock>;
807
811 class basic_file : virtual public basic
812 {
813 public:
814 virtual std::vector<uint8_t> read_remainder(_In_ size_t max_length = SIZE_MAX)
815 {
816 size_t length = std::min<size_t>(max_length, static_cast<size_t>(size() - tell()));
817 std::vector<uint8_t> result;
818 try { result.resize(length); }
819 catch (const std::bad_alloc&) {
820 m_state = state_t::fail;
821 return result;
822 }
823 result.resize(read_array(result.data(), sizeof(uint8_t), length));
824 return result;
825 }
826
832 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg) = 0;
833
839 fpos_t seekbeg(_In_ fpos_t offset) { return seek(offset, seek_t::beg); }
840
846 fpos_t seekcur(_In_ foff_t offset) { return seek(offset, seek_t::cur); }
847
853 fpos_t seekend(_In_ foff_t offset) { return seek(offset, seek_t::end); }
854
855 virtual void skip(_In_ fsize_t amount)
856 {
857 seek(amount, seek_t::cur);
858 }
859
866 virtual fpos_t tell() const = 0;
867
871 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
872 {
873 _Unreferenced_(offset);
874 _Unreferenced_(length);
875 throw std::domain_error("not implemented");
876 }
877
881 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
882 {
883 _Unreferenced_(offset);
884 _Unreferenced_(length);
885 throw std::domain_error("not implemented");
886 }
887
892 virtual fsize_t size() const = 0;
893
897 virtual void truncate() = 0;
898
902 virtual time_point ctime() const
903 {
904 return time_point::min();
905 }
906
910 virtual time_point atime() const
911 {
912 return time_point::min();
913 }
914
918 virtual time_point mtime() const
919 {
920 return time_point::min();
921 }
922
926 virtual void set_ctime(time_point date)
927 {
928 _Unreferenced_(date);
929 throw std::domain_error("not implemented");
930 }
931
935 virtual void set_atime(time_point date)
936 {
937 _Unreferenced_(date);
938 throw std::domain_error("not implemented");
939 }
940
944 virtual void set_mtime(time_point date)
945 {
946 _Unreferenced_(date);
947 throw std::domain_error("not implemented");
948 }
949
950#ifdef _WIN32
954 LPSAFEARRAY read_sa()
955 {
956 _Assume_(size() <= SIZE_MAX);
957 size_t length = static_cast<size_t>(size());
958 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(SafeArrayCreateVector(VT_UI1, 0, (ULONG)length));
959 if (!sa) _Unlikely_
960 throw std::runtime_error("SafeArrayCreateVector failed");
961 safearray_accessor<void> a(sa.get());
962 if (seek(0) != 0) _Unlikely_
963 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
964 if (read_array(a.data(), 1, length) != length)
965 throw std::system_error(sys_error(), std::system_category(), "failed to read");
966 return sa.release();
967 }
968#endif
969
975 charset_id read_charset(_In_ charset_id default_charset = charset_id::system)
976 {
977 if (seek(0) != 0) _Unlikely_
978 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
979 char32_t id_utf32;
980 read_array(&id_utf32, sizeof(char32_t), 1);
981 if (ok() && id_utf32 == utf32_bom)
982 return charset_id::utf32;
983
984 if (seek(0) != 0) _Unlikely_
985 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
986 char16_t id_utf16;
987 read_array(&id_utf16, sizeof(char16_t), 1);
988 if (ok() && id_utf16 == utf16_bom)
989 return charset_id::utf16;
990
991 if (seek(0) != 0) _Unlikely_
992 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
993 char id_utf8[3] = { 0 };
994 read_array(id_utf8, sizeof(id_utf8), 1);
995 if (ok() && strncmp(id_utf8, _countof(id_utf8), utf8_bom, _countof(utf8_bom)) == 0)
996 return charset_id::utf8;
997
998 if (seek(0) != 0) _Unlikely_
999 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1000 return default_charset;
1001 }
1002 };
1003
1009 class converter : public basic
1010 {
1011 protected:
1013#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1014 explicit converter() : basic(state_t::fail) {}
1015
1016 void init(_Inout_ basic& source)
1017 {
1018 m_source = &source;
1019 init();
1020 }
1021
1022 void init()
1023 {
1024 m_state = m_source->state();
1025 }
1026
1027 void done()
1028 {
1029 m_source = nullptr;
1030 }
1032
1033 public:
1034 converter(_Inout_ basic& source) :
1035 basic(source.state()),
1036 m_source(&source)
1037 {}
1038
1039 virtual _Success_(return != 0 || length == 0) size_t read(
1040 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1041 {
1042 size_t num_read = m_source->read(data, length);
1043 m_state = m_source->state();
1044 return num_read;
1045 }
1046
1047 virtual _Success_(return != 0) size_t write(
1048 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1049 {
1050 size_t num_written = m_source->write(data, length);
1051 m_state = m_source->state();
1052 return num_written;
1053 }
1054
1055 virtual void close()
1056 {
1057 m_source->close();
1058 m_state = m_source->state();
1059 }
1060
1061 virtual void flush()
1062 {
1063 m_source->flush();
1064 m_state = m_source->state();
1065 }
1066
1067 protected:
1068 basic* m_source;
1069 };
1070
1074 class replicator : public basic
1075 {
1076 public:
1077 virtual ~replicator()
1078 {
1079 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1080 auto _w = w->get();
1081 {
1082 const std::lock_guard<std::mutex> lk(_w->mutex);
1083 _w->op = worker::op_t::quit;
1084 }
1085 _w->cv.notify_one();
1086 }
1087 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w)
1088 w->get()->join();
1089 }
1090
1094 void push_back(_In_ basic* source)
1095 {
1096 m_workers.push_back(std::unique_ptr<worker>(new worker(source)));
1097 }
1098
1102 void remove(basic* source)
1103 {
1104 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1105 auto _w = w->get();
1106 if (_w->source == source) {
1107 {
1108 const std::lock_guard<std::mutex> lk(_w->mutex);
1109 _w->op = worker::op_t::quit;
1110 }
1111 _w->cv.notify_one();
1112 _w->join();
1113 m_workers.erase(w);
1114 return;
1115 }
1116 }
1117 }
1118
1119 virtual _Success_(return != 0) size_t write(
1120 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1121 {
1122 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1123 auto _w = w->get();
1124 {
1125 const std::lock_guard<std::mutex> lk(_w->mutex);
1126 _w->op = worker::op_t::write;
1127 _w->data = data;
1128 _w->length = length;
1129 }
1130 _w->cv.notify_one();
1131 }
1132 size_t num_written = length;
1133 m_state = state_t::ok;
1134 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1135 auto _w = w->get();
1136 std::unique_lock<std::mutex> lk(_w->mutex);
1137 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1138 if (_w->num_written < num_written)
1139 num_written = _w->num_written;
1140 if (ok() && !_w->source->ok())
1141 m_state = _w->source->state();
1142 }
1143 return num_written;
1144 }
1145
1146 virtual void close()
1147 {
1148 foreach_worker(worker::op_t::close);
1149 }
1150
1151 virtual void flush()
1152 {
1153 foreach_worker(worker::op_t::flush);
1154 }
1155
1156 protected:
1157 class worker : public std::thread
1158 {
1159 public:
1160 worker(_In_ basic* _source) :
1161 source(_source),
1162 op(op_t::noop),
1163 data(nullptr),
1164 length(0),
1165 num_written(0)
1166 {
1167 *static_cast<std::thread*>(this) = std::thread([](_Inout_ worker& w) { w.process_op(); }, std::ref(*this));
1168 }
1169
1170 protected:
1171 void process_op()
1172 {
1173 for (;;) {
1174 std::unique_lock<std::mutex> lk(mutex);
1175 cv.wait(lk, [&] {return op != op_t::noop; });
1176 switch (op) {
1177 case op_t::quit:
1178 return;
1179 case op_t::write:
1180 num_written = source->write(data, length);
1181 break;
1182 case op_t::close:
1183 source->close();
1184 break;
1185 case op_t::flush:
1186 source->flush();
1187 break;
1188 case op_t::noop:;
1189 }
1190 op = op_t::noop;
1191 lk.unlock();
1192 cv.notify_one();
1193 }
1194 }
1195
1196 public:
1197 basic* source;
1198 enum class op_t {
1199 noop = 0,
1200 quit,
1201 write,
1202 close,
1203 flush,
1204 } op;
1205 const void* data;
1206 size_t length;
1208 std::mutex mutex;
1209 std::condition_variable cv;
1210 };
1211
1212 void foreach_worker(_In_ worker::op_t op)
1213 {
1214 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1215 auto _w = w->get();
1216 {
1217 const std::lock_guard<std::mutex> lk(_w->mutex);
1218 _w->op = op;
1219 }
1220 _w->cv.notify_one();
1221 }
1222 m_state = state_t::ok;
1223 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1224 auto _w = w->get();
1225 std::unique_lock<std::mutex> lk(_w->mutex);
1226 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1227 if (ok())
1228 m_state = _w->source->state();
1229 }
1230 }
1231
1232 std::list<std::unique_ptr<worker>> m_workers;
1233 };
1234
1235 constexpr size_t default_async_limit = 0x100000;
1236
1242 template <size_t CAPACITY = default_async_limit>
1244 {
1245 public:
1246 async_reader(_Inout_ basic& source) :
1247 converter(source),
1248 m_worker([](_Inout_ async_reader& w) { w.process(); }, std::ref(*this))
1249 {}
1250
1251 virtual ~async_reader()
1252 {
1253 m_ring.quit();
1254 m_worker.join();
1255 }
1256
1257#pragma warning(suppress: 6101) // See [1] below
1258 virtual _Success_(return != 0 || length == 0) size_t read(
1259 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1260 {
1261 _Assume_(data || !length);
1262 for (size_t to_read = length;;) {
1263 uint8_t* ptr; size_t num_read;
1264 std::tie(ptr, num_read) = m_ring.front();
1265 if (!ptr) _Unlikely_ {
1266 m_state = to_read < length || !length ? state_t::ok : m_source->state();
1267 return length - to_read; // [1] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
1268 }
1269 if (to_read < num_read)
1270 num_read = to_read;
1271 memcpy(data, ptr, num_read);
1272 m_ring.pop(num_read);
1273 to_read -= num_read;
1274 if (!to_read) {
1275 m_state = state_t::ok;
1276 return length;
1277 }
1278 reinterpret_cast<uint8_t*&>(data) += num_read;
1279 }
1280 }
1281
1282 protected:
1283 void process()
1284 {
1285 for (;;) {
1286 uint8_t* ptr; size_t num_write;
1287 std::tie(ptr, num_write) = m_ring.back();
1288 if (!ptr) _Unlikely_
1289 break;
1290 num_write = m_source->read(ptr, num_write);
1291 m_ring.push(num_write);
1292 if (!m_source->ok()) {
1293 m_ring.quit();
1294 break;
1295 }
1296 }
1297 }
1298
1299 protected:
1300 ring<uint8_t, CAPACITY> m_ring;
1301 std::thread m_worker;
1302 };
1303
1309 template <size_t CAPACITY = default_async_limit>
1311 {
1312 public:
1313 async_writer(_Inout_ basic& source) :
1314 converter(source),
1315 m_worker([](_Inout_ async_writer& w) { w.process(); }, std::ref(*this))
1316 {}
1317
1318 virtual ~async_writer()
1319 {
1320 m_ring.quit();
1321 m_worker.join();
1322 }
1323
1324 virtual _Success_(return != 0) size_t write(
1325 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1326 {
1327 _Assume_(data || !length);
1328 for (size_t to_write = length;;) {
1329 uint8_t* ptr; size_t num_write;
1330 std::tie(ptr, num_write) = m_ring.back();
1331 if (!ptr) _Unlikely_ {
1332 m_state = state_t::fail;
1333 return length - to_write;
1334 }
1335 if (to_write < num_write)
1336 num_write = to_write;
1337 memcpy(ptr, data, num_write);
1338 m_ring.push(num_write);
1339 to_write -= num_write;
1340 if (!to_write) {
1341 m_state = state_t::ok;
1342 return length;
1343 }
1344 reinterpret_cast<const uint8_t*&>(data) += num_write;
1345 }
1346 }
1347
1348 virtual void flush()
1349 {
1350 m_ring.sync();
1352 }
1353
1354 protected:
1355 void process()
1356 {
1357 for (;;) {
1358 uint8_t* ptr; size_t num_read;
1359 std::tie(ptr, num_read) = m_ring.front();
1360 if (!ptr)
1361 break;
1362 num_read = m_source->write(ptr, num_read);
1363 m_ring.pop(num_read);
1364 if (!m_source->ok()) {
1365 m_ring.quit();
1366 break;
1367 }
1368 }
1369 }
1370
1371 protected:
1372 ring<uint8_t, CAPACITY> m_ring;
1373 std::thread m_worker;
1374 };
1375
1376 constexpr size_t default_buffer_size = 0x400;
1377
1381 class buffer : public converter
1382 {
1383 protected:
1385 explicit buffer(_In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1386 converter(),
1387 m_read_buffer(read_buffer_size),
1388 m_write_buffer(write_buffer_size)
1389 {}
1390
1391 void done()
1392 {
1393 if (m_source)
1394 flush_write();
1395 converter::done();
1396 }
1398
1399 public:
1400 buffer(_Inout_ basic& source, _In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1401 converter(source),
1402 m_read_buffer(read_buffer_size),
1403 m_write_buffer(write_buffer_size)
1404 {}
1405
1406 virtual ~buffer()
1407 {
1408 if (m_source)
1409 flush_write();
1410 }
1411
1412 virtual _Success_(return != 0 || length == 0) size_t read(
1413 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1414 {
1415 _Assume_(data || !length);
1416 for (size_t to_read = length;;) {
1417 size_t buffer_size = m_read_buffer.tail - m_read_buffer.head;
1418 if (to_read <= buffer_size) {
1419 memcpy(data, m_read_buffer.data + m_read_buffer.head, to_read);
1420 m_read_buffer.head += to_read;
1421 m_state = state_t::ok;
1422 return length;
1423 }
1424 if (buffer_size) {
1425 memcpy(data, m_read_buffer.data + m_read_buffer.head, buffer_size);
1426 reinterpret_cast<uint8_t*&>(data) += buffer_size;
1427 to_read -= buffer_size;
1428 }
1429 m_read_buffer.head = 0;
1430 if (to_read > m_read_buffer.capacity) {
1431 // When needing to read more data than buffer capacity, bypass the buffer.
1432 m_read_buffer.tail = 0;
1433 to_read -= m_source->read(data, to_read);
1434 m_state = to_read < length ? state_t::ok : m_source->state();
1435 return length - to_read;
1436 }
1437 m_read_buffer.tail = m_source->read(m_read_buffer.data, m_read_buffer.capacity);
1438 if (m_read_buffer.tail < m_read_buffer.capacity && m_read_buffer.tail < to_read) _Unlikely_ {
1439 memcpy(data, m_read_buffer.data, m_read_buffer.tail);
1440 m_read_buffer.head = m_read_buffer.tail;
1441 to_read -= m_read_buffer.tail;
1442 m_state = to_read < length ? state_t::ok : m_source->state();
1443 return length - to_read;
1444 }
1445 }
1446 }
1447
1448 virtual _Success_(return != 0) size_t write(
1449 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1450 {
1451 _Assume_(data || !length);
1452 if (!length) _Unlikely_ {
1453 // Pass null writes (zero-byte length). Null write operations have special meaning with with Windows pipes.
1454 flush_write();
1455 if (!ok()) _Unlikely_
1456 return 0;
1457 converter::write(nullptr, 0);
1458 return 0;
1459 }
1460
1461 for (size_t to_write = length;;) {
1462 size_t available_buffer = m_write_buffer.capacity - m_write_buffer.tail;
1463 if (to_write <= available_buffer) {
1464 memcpy(m_write_buffer.data + m_write_buffer.tail, data, to_write);
1465 m_write_buffer.tail += to_write;
1466 m_state = state_t::ok;
1467 return length;
1468 }
1469 if (available_buffer) {
1470 memcpy(m_write_buffer.data + m_write_buffer.tail, data, available_buffer);
1471 reinterpret_cast<const uint8_t*&>(data) += available_buffer;
1472 to_write -= available_buffer;
1473 m_write_buffer.tail += available_buffer;
1474 }
1475 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1476 if (buffer_size) {
1477 m_write_buffer.head += converter::write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1478 if (m_write_buffer.head == m_write_buffer.tail)
1479 m_write_buffer.head = m_write_buffer.tail = 0;
1480 else
1481 return length - to_write;
1482 }
1483 if (to_write > m_write_buffer.capacity) {
1484 // When needing to write more data than buffer capacity, bypass the buffer.
1485 to_write -= converter::write(data, to_write);
1486 return length - to_write;
1487 }
1488 }
1489 }
1490
1491 virtual void flush()
1492 {
1493 flush_write();
1494 if (ok())
1496 }
1497
1498 protected:
1499 void flush_write()
1500 {
1501 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1502 if (buffer_size) {
1503 m_write_buffer.head += m_source->write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1504 if (m_write_buffer.head == m_write_buffer.tail) {
1505 m_write_buffer.head = 0;
1506 m_write_buffer.tail = 0;
1507 }
1508 else {
1509 m_state = m_source->state();
1510 return;
1511 }
1512 }
1513 m_state = state_t::ok;
1514 }
1515
1516 struct buffer_t {
1517 uint8_t* data;
1518 size_t head, tail, capacity;
1519
1520 buffer_t(_In_ size_t buffer_size) :
1521 head(0),
1522 tail(0),
1523 capacity(buffer_size),
1524 data(buffer_size ? new uint8_t[buffer_size] : nullptr)
1525 {}
1526
1527 ~buffer_t()
1528 {
1529 if (data)
1530 delete[] data;
1531 }
1532 } m_read_buffer, m_write_buffer;
1533 };
1534
1538 class limiter : public converter
1539 {
1540 public:
1541 limiter(_Inout_ basic& source, _In_ fsize_t _read_limit = 0, _In_ fsize_t _write_limit = 0) :
1542 converter(source),
1543 read_limit(_read_limit),
1544 write_limit(_write_limit)
1545 {}
1546
1547 virtual _Success_(return != 0 || length == 0) size_t read(
1548 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1549 {
1550 size_t num_read;
1551 if (read_limit == fsize_max)
1552 num_read = converter::read(data, length);
1553 else if (length <= read_limit) {
1554 num_read = converter::read(data, length);
1555 read_limit -= num_read;
1556 }
1557 else if (length && !read_limit) {
1558 num_read = 0;
1559 m_state = state_t::eof;
1560 }
1561 else {
1562 num_read = converter::read(data, static_cast<size_t>(read_limit));
1563 read_limit -= num_read;
1564 }
1565 return num_read;
1566 }
1567
1568 virtual _Success_(return != 0) size_t write(
1569 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1570 {
1571 size_t num_written;
1572 if (write_limit == fsize_max)
1573 num_written = converter::write(data, length);
1574 else if (length <= write_limit) {
1575 num_written = converter::write(data, length);
1576 write_limit -= num_written;
1577 }
1578 else if (length && !write_limit) {
1579 num_written = 0;
1580 m_state = state_t::fail;
1581 }
1582 else {
1583 num_written = converter::write(data, static_cast<size_t>(write_limit));
1584 write_limit -= num_written;
1585 }
1586 return num_written;
1587 }
1588
1589 public:
1590 fsize_t
1593 };
1594
1598 class window : public limiter
1599 {
1600 public:
1601 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) :
1602 limiter(source, read_limit, write_limit),
1603 read_offset(_read_offset),
1604 write_offset(_write_offset)
1605 {}
1606
1607 virtual _Success_(return != 0 || length == 0) size_t read(
1608 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1609 {
1610 if (read_offset) {
1611 m_source->skip(read_offset);
1612 m_state = m_source->state();
1613 if (!ok()) _Unlikely_
1614 return 0;
1615 read_offset = 0;
1616 }
1617 size_t num_read;
1618 if (read_limit == fsize_max)
1619 num_read = converter::read(data, length);
1620 else if (length <= read_limit) {
1621 num_read = converter::read(data, length);
1622 read_limit -= num_read;
1623 }
1624 else if (length && !read_limit) {
1625 num_read = 0;
1626 m_source->skip(length);
1627 m_state = state_t::eof;
1628 }
1629 else {
1630 num_read = converter::read(data, static_cast<size_t>(read_limit));
1631 read_limit -= num_read;
1632 }
1633 return num_read;
1634 }
1635
1636 virtual _Success_(return != 0) size_t write(
1637 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1638 {
1639 size_t num_skipped, num_written;
1640 if (length <= write_offset) {
1641 write_offset -= length;
1642 m_state = state_t::ok;
1643 return length;
1644 }
1645 if (write_offset) {
1646 reinterpret_cast<const uint8_t*&>(data) += static_cast<size_t>(write_offset);
1647 length -= static_cast<size_t>(write_offset);
1648 num_skipped = static_cast<size_t>(write_offset);
1649 write_offset = 0;
1650 }
1651 else
1652 num_skipped = 0;
1653 if (write_limit == fsize_max)
1654 num_written = converter::write(data, length);
1655 else if (length <= write_limit) {
1656 num_written = converter::write(data, length);
1657 write_limit -= num_written;
1658 }
1659 else if (length && !write_limit) {
1660 num_skipped += length;
1661 num_written = 0;
1662 m_state = state_t::ok;
1663 }
1664 else {
1665 num_skipped += length - static_cast<size_t>(write_limit);
1666 num_written = converter::write(data, static_cast<size_t>(write_limit));
1667 write_limit -= num_written;
1668 }
1669 return num_skipped + num_written;
1670 }
1671
1672 public:
1673 fpos_t
1676 };
1677
1682 {
1683 public:
1684 file_window(_Inout_ basic_file& source, fpos_t offset = 0, fsize_t length = 0) :
1685 basic(source.state()),
1686 m_source(source),
1687 m_offset(source.tell()),
1688 m_region(offset, offset + length)
1689 {}
1690
1691 virtual _Success_(return != 0 || length == 0) size_t read(
1692 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1693 {
1694 _Assume_(data || !length);
1695 if (m_region.contains(m_offset)) {
1696 size_t num_read = m_source.read(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1697 m_state = m_source.state();
1698 m_offset += num_read;
1699 return num_read;
1700 }
1701 m_state = length ? state_t::eof : state_t::ok;
1702 return 0;
1703 }
1704
1705 virtual _Success_(return != 0) size_t write(
1706 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1707 {
1708 _Assume_(data || !length);
1709 if (m_region.contains(m_offset)) {
1710 size_t num_written = m_source.write(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1711 m_state = m_source.state();
1712 m_offset += num_written;
1713 return num_written;
1714 }
1715 m_state = state_t::fail;
1716 return 0;
1717 }
1718
1719 virtual void close()
1720 {
1721 m_source.close();
1722 m_state = m_source.state();
1723 }
1724
1725 virtual void flush()
1726 {
1727 m_source.flush();
1728 m_state = m_source.state();
1729 }
1730
1731 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
1732 {
1733 m_offset = m_source.seek(offset, how);
1734 m_state = m_source.state();
1735 return ok() ? m_offset - m_region.start : fpos_max;
1736 }
1737
1738 virtual void skip(_In_ fsize_t amount)
1739 {
1740 m_source.skip(amount);
1741 m_state = m_source.state();
1742 }
1743
1744 virtual fpos_t tell() const
1745 {
1746 fpos_t offset = m_source.tell();
1747 return m_region.contains(offset) ? offset - m_region.start : fpos_max;
1748 }
1749
1750 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
1751 {
1752 if (m_region.contains(offset)) {
1753 m_source.lock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1754 m_state = m_source.state();
1755 }
1756 else
1757 m_state = state_t::fail;
1758 }
1759
1760 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
1761 {
1762 if (m_region.contains(offset)) {
1763 m_source.unlock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1764 m_state = m_source.state();
1765 }
1766 else
1767 m_state = state_t::fail;
1768 }
1769
1770 virtual fsize_t size() const
1771 {
1772 return m_region.size();
1773 }
1774
1775 virtual void truncate()
1776 {
1777 m_state = state_t::fail;
1778 }
1779
1780 protected:
1781 basic_file& m_source;
1782 fpos_t m_offset;
1783 interval<fpos_t> m_region;
1784 };
1785
1786 constexpr size_t default_cache_size = 0x1000;
1787
1791 class cache : public basic_file
1792 {
1793 protected:
1795#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1796 explicit cache(_In_ size_t cache_size = default_cache_size) :
1797 basic(state_t::fail),
1798 m_cache(cache_size)
1799 {}
1800
1801 void init(_Inout_ basic_file& source)
1802 {
1803 m_source = &source;
1804 init();
1805 }
1806
1807 void init()
1808 {
1809 m_state = m_source->state();
1810 m_offset = m_source->tell();
1811#if SET_FILE_OP_TIMES
1812 m_atime = m_source->atime();
1813 m_mtime = m_source->mtime();
1814#endif
1815 }
1816
1817 void done()
1818 {
1819 if (m_source) {
1820 flush_cache();
1821 if (!ok()) _Unlikely_
1822 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occured
1823 m_source->seek(m_offset);
1824#if SET_FILE_OP_TIMES
1825 m_source->set_atime(m_atime);
1826 m_source->set_mtime(m_mtime);
1827#endif
1828 m_source = nullptr;
1829 }
1830 }
1832
1833 public:
1834 cache(_Inout_ basic_file& source, _In_ size_t cache_size = default_cache_size) :
1835 basic(source.state()),
1836 m_source(&source),
1837 m_cache(cache_size),
1838 m_offset(source.tell())
1839#if SET_FILE_OP_TIMES
1840 , m_atime(source.atime())
1841 , m_mtime(source.mtime())
1842#endif
1843 {}
1844
1845 virtual ~cache() noexcept(false)
1846 {
1847 if (m_source) {
1848 flush_cache();
1849 if (!ok()) _Unlikely_
1850 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occured
1851 m_source->seek(m_offset);
1852#if SET_FILE_OP_TIMES
1853 m_source->set_atime(m_atime);
1854 m_source->set_mtime(m_mtime);
1855#endif
1856 }
1857 }
1858
1859 virtual _Success_(return != 0 || length == 0) size_t read(
1860 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1861 {
1862 _Assume_(data || !length);
1863#if SET_FILE_OP_TIMES
1864 m_atime = time_point::now();
1865#endif
1866 for (size_t to_read = length;;) {
1867 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1868 if (m_cache.region.contains(m_offset)) {
1869 size_t remaining_cache = static_cast<size_t>(m_cache.region.end - m_offset);
1870 if (to_read <= remaining_cache) {
1871 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), to_read);
1872 m_offset += to_read;
1873 m_state = state_t::ok;
1874 return length;
1875 }
1876 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), remaining_cache);
1877 reinterpret_cast<uint8_t*&>(data) += remaining_cache;
1878 to_read -= remaining_cache;
1879 m_offset += remaining_cache;
1880 }
1881 flush_cache();
1882 if (!ok()) _Unlikely_ {
1883 if (to_read < length)
1884 m_state = state_t::ok;
1885 return length - to_read;
1886 }
1887 }
1888 {
1889 fpos_t end_max = m_offset + to_read;
1890 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1891 // Read spans multiple cache blocks. Bypass cache to the last block.
1892 m_source->seek(m_offset);
1893 if (!m_source->ok()) _Unlikely_ {
1894 m_state = to_read < length ? state_t::ok : state_t::fail;
1895 return length - to_read;
1896 }
1897 size_t num_read = m_source->read(data, to_read - static_cast<size_t>(end_max % m_cache.capacity));
1898 m_offset += num_read;
1899 to_read -= num_read;
1900 if (!to_read) {
1901 m_state = state_t::ok;
1902 return length;
1903 }
1904 reinterpret_cast<uint8_t*&>(data) += num_read;
1905 m_state = m_source->state();
1906 if (!ok()) {
1907 if (to_read < length)
1908 m_state = state_t::ok;
1909 return length - to_read;
1910 }
1911 }
1912 }
1913 load_cache(m_offset);
1914 if (!ok()) _Unlikely_ {
1915 m_state = to_read < length ? state_t::ok : state_t::fail;
1916 return length - to_read;
1917 }
1918 if (m_cache.region.end <= m_offset) _Unlikely_ {
1919 m_state = to_read < length ? state_t::ok : state_t::eof;
1920 return length - to_read;
1921 }
1922 }
1923 }
1924
1925 virtual _Success_(return != 0) size_t write(
1926 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1927 {
1928 _Assume_(data || !length);
1929#if SET_FILE_OP_TIMES
1930 m_atime = m_mtime = time_point::now();
1931#endif
1932 for (size_t to_write = length;;) {
1933 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1934 fpos_t end_max = m_cache.region.start + m_cache.capacity;
1935 if (m_cache.region.start <= m_offset && m_offset < end_max) {
1936 size_t remaining_cache = static_cast<size_t>(end_max - m_offset);
1937 if (to_write <= remaining_cache) {
1938 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, to_write);
1939 m_offset += to_write;
1940 m_cache.status = cache_t::cache_t::status_t::dirty;
1941 m_cache.region.end = std::max(m_cache.region.end, m_offset);
1942 m_state = state_t::ok;
1943 return length;
1944 }
1945 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, remaining_cache);
1946 reinterpret_cast<const uint8_t*&>(data) += remaining_cache;
1947 to_write -= remaining_cache;
1948 m_offset += remaining_cache;
1949 m_cache.status = cache_t::cache_t::status_t::dirty;
1950 m_cache.region.end = end_max;
1951 }
1952 flush_cache();
1953 if (!ok()) _Unlikely_
1954 return length - to_write;
1955 }
1956 {
1957 fpos_t end_max = m_offset + to_write;
1958 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1959 // Write spans multiple cache blocks. Bypass cache to the last block.
1960 m_source->seek(m_offset);
1961 if (!ok()) _Unlikely_
1962 return length - to_write;
1963 size_t num_written = m_source->write(data, to_write - static_cast<size_t>(end_max % m_cache.capacity));
1964 m_offset += num_written;
1965 m_state = m_source->state();
1966 to_write -= num_written;
1967 if (!to_write || !ok())
1968 return length - to_write;
1969 reinterpret_cast<const uint8_t*&>(data) += num_written;
1970 }
1971 }
1972 load_cache(m_offset);
1973 if (!ok()) _Unlikely_
1974 return length - to_write;
1975 }
1976 }
1977
1978 virtual void close()
1979 {
1980 invalidate_cache();
1981 if (!ok()) _Unlikely_
1982 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occured
1983 m_source->close();
1984 m_state = m_source->state();
1985 }
1986
1987 virtual void flush()
1988 {
1989#if SET_FILE_OP_TIMES
1990 m_atime = m_mtime = time_point::min();
1991#endif
1992 flush_cache();
1993 if (!ok()) _Unlikely_
1994 return;
1995 m_source->flush();
1996 }
1997
1998 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
1999 {
2000 m_state = state_t::ok;
2001 switch (how) {
2002 case seek_t::beg:
2003 return m_offset = offset;
2004 case seek_t::cur:
2005 return m_offset += offset;
2006 case seek_t::end: {
2007 auto n = size();
2008 if (n == fsize_max) _Unlikely_{
2009 m_state = state_t::fail;
2010 return fpos_max;
2011 }
2012 return m_offset = n + offset;
2013 }
2014 default:
2015 throw std::invalid_argument("unknown seek origin");
2016 }
2017 }
2018
2019 virtual fpos_t tell() const
2020 {
2021 return m_offset;
2022 }
2023
2024 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2025 {
2026 m_source->lock(offset, length);
2027 m_state = m_source->state();
2028 }
2029
2030 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2031 {
2032 m_source->unlock(offset, length);
2033 m_state = m_source->state();
2034 }
2035
2036 virtual fsize_t size() const
2037 {
2038 return m_cache.status != cache_t::cache_t::status_t::empty ?
2039 std::max(m_source->size(), m_cache.region.end) :
2040 m_source->size();
2041 }
2042
2043 virtual void truncate()
2044 {
2045#if SET_FILE_OP_TIMES
2046 m_atime = m_mtime = time_point::now();
2047#endif
2048 m_source->seek(m_offset);
2049 if (m_cache.region.end <= m_offset) {
2050 // Truncation does not affect cache.
2051 }
2052 else if (m_cache.region.start <= m_offset) {
2053 // Truncation truncates cache.
2054 m_cache.region.end = m_offset;
2055 }
2056 else {
2057 // Truncation invalidates cache.
2058 m_cache.status = cache_t::cache_t::status_t::empty;
2059 }
2060 m_source->truncate();
2061 m_state = m_source->state();
2062 }
2063
2064 virtual time_point ctime() const
2065 {
2066 return m_source->ctime();
2067 }
2068
2069 virtual time_point atime() const
2070 {
2071#if SET_FILE_OP_TIMES
2072 return std::max(m_atime, m_source->atime());
2073#else
2074 return m_source->atime();
2075#endif
2076 }
2077
2078 virtual time_point mtime() const
2079 {
2080#if SET_FILE_OP_TIMES
2081 return std::max(m_mtime, m_source->mtime());
2082#else
2083 return m_source->mtime();
2084#endif
2085 }
2086
2087 virtual void set_ctime(time_point date)
2088 {
2089 m_source->set_ctime(date);
2090 }
2091
2092 virtual void set_atime(time_point date)
2093 {
2094#if SET_FILE_OP_TIMES
2095 m_atime = date;
2096#endif
2097 m_source->set_atime(date);
2098 }
2099
2100 virtual void set_mtime(time_point date)
2101 {
2102#if SET_FILE_OP_TIMES
2103 m_mtime = date;
2104#endif
2105 m_source->set_mtime(date);
2106 }
2107
2108 protected:
2110 void flush_cache()
2111 {
2112 if (m_cache.status != cache_t::cache_t::status_t::dirty)
2113 m_state = state_t::ok;
2114 else if (!m_cache.region.empty()) {
2115 write_cache();
2116 if (ok())
2117 m_cache.status = cache_t::cache_t::status_t::loaded;
2118 }
2119 else {
2120 m_state = state_t::ok;
2121 m_cache.status = cache_t::cache_t::status_t::loaded;
2122 }
2123 }
2124
2125 void invalidate_cache()
2126 {
2127 if (m_cache.status == cache_t::cache_t::status_t::dirty && !m_cache.region.empty()) {
2128 write_cache();
2129 if (!ok()) _Unlikely_
2130 return;
2131 } else
2132 m_state = state_t::ok;
2133 m_cache.status = cache_t::cache_t::status_t::empty;
2134 }
2135
2136 void load_cache(_In_ fpos_t start)
2137 {
2138 _Assume_(m_cache.status != cache_t::cache_t::status_t::dirty);
2139 start -= start % m_cache.capacity; // Align to cache block size.
2140 m_source->seek(m_cache.region.start = start);
2141 if (m_source->ok()) {
2142 m_cache.region.end = start + m_source->read(m_cache.data, m_cache.capacity);
2143 m_cache.status = cache_t::cache_t::status_t::loaded;
2144 m_state = state_t::ok; // Regardless the read failure, we still might have cached some data.
2145 }
2146 else
2147 m_state = state_t::fail;
2148 }
2149
2150 void write_cache()
2151 {
2152 _Assume_(m_cache.status == cache_t::cache_t::status_t::dirty);
2153 m_source->seek(m_cache.region.start);
2154 m_source->write(m_cache.data, static_cast<size_t>(m_cache.region.size()));
2155 m_state = m_source->state();
2156 }
2157
2158 basic_file* m_source;
2159 struct cache_t {
2160 uint8_t* data;
2161 size_t capacity;
2162 enum class status_t {
2163 empty = 0,
2164 loaded,
2165 dirty,
2166 } status;
2167 interval<fpos_t> region;
2168
2169 cache_t(_In_ size_t _capacity) :
2170 data(new uint8_t[_capacity]),
2171 capacity(_capacity),
2172 status(status_t::empty),
2173 region(0)
2174 {}
2175
2176 ~cache_t()
2177 {
2178 delete[] data;
2179 }
2180 } m_cache;
2181 fpos_t m_offset;
2182#if SET_FILE_OP_TIMES
2183 time_point
2184 m_atime,
2185 m_mtime;
2186#endif
2188 };
2189
2193 class basic_sys : virtual public basic, public sys_object
2194 {
2195 public:
2196 basic_sys(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) :
2197 basic(state),
2198 sys_object(h)
2199 {}
2200
2201 virtual _Success_(return != 0 || length == 0) size_t read(
2202 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2203 {
2204 _Assume_(data || !length);
2205 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2206 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2207 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2208 size_t
2209#if defined(_WIN64)
2210 block_size = 0x1F80000;
2211#elif defined(_WIN32)
2212 block_size = 0x3f00000;
2213#else
2214 block_size = SSIZE_MAX;
2215#endif
2216 for (size_t to_read = length;;) {
2217#ifdef _WIN32
2218 // ReadFile() might raise exception (e.g. STATUS_FILE_BAD_FORMAT/0xE0000002).
2219 BOOL succeeded;
2220 DWORD num_read;
2221 __try { succeeded = ReadFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_read, block_size)), &num_read, nullptr); }
2222 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_read = 0; }
2223 if (!succeeded && GetLastError() == ERROR_NO_SYSTEM_RESOURCES && block_size > default_block_size) _Unlikely_ {
2224 // Error "Insufficient system resources exist to complete the requested service." occurs
2225 // ocasionally, when attempting to read too much data at once (e.g. over \\TSClient).
2226 block_size = default_block_size;
2227 continue;
2228 }
2229 if (!succeeded) _Unlikely_
2230#else
2231 ssize_t num_read = ::read(m_h, data, static_cast<ssize_t>(std::min<size_t>(to_read, block_size)));
2232 if (num_read < 0) _Unlikely_
2233#endif
2234 {
2235 m_state = to_read < length ? state_t::ok : state_t::fail;
2236 return length - to_read;
2237 }
2238 if (!num_read) _Unlikely_ {
2239 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2240 return length - to_read;
2241 }
2242 to_read -= num_read;
2243 if (!to_read) {
2244 m_state = state_t::ok;
2245 return length;
2246 }
2247 reinterpret_cast<uint8_t*&>(data) += num_read;
2248 }
2249 }
2250
2251 virtual _Success_(return != 0) size_t write(
2252 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2253 {
2254 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2255 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2256 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2257 constexpr size_t
2258#if defined(_WIN64)
2259 block_size = 0x1F80000;
2260#elif defined(_WIN32)
2261 block_size = 0x3f00000;
2262#else
2263 block_size = SSIZE_MAX;
2264#endif
2265 for (size_t to_write = length;;) {
2266#ifdef _WIN32
2267 // ReadFile() might raise an exception. Be cautious with WriteFile() too.
2268 BOOL succeeded;
2269 DWORD num_written;
2270 __try { succeeded = WriteFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_write, block_size)), &num_written, nullptr); }
2271 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_written = 0; }
2272 to_write -= num_written;
2273 if (!to_write) {
2274 m_state = state_t::ok;
2275 return length;
2276 }
2277 reinterpret_cast<const uint8_t*&>(data) += num_written;
2278 if (!succeeded) _Unlikely_ {
2279 m_state = state_t::fail;
2280 return length - to_write;
2281 }
2282#else
2283 ssize_t num_written = ::write(m_h, data, static_cast<ssize_t>(std::min<size_t>(to_write, block_size)));
2284 if (num_written < 0) _Unlikely_ {
2285 m_state = state_t::fail;
2286 return length - to_write;
2287 }
2288 to_write -= num_written;
2289 if (!to_write) {
2290 m_state = state_t::ok;
2291 return length;
2292 }
2293 reinterpret_cast<const uint8_t*&>(data) += num_written;
2294#endif
2295 }
2296 }
2297
2298 virtual void close()
2299 {
2300 try {
2302 m_state = state_t::ok;
2303 }
2304 catch (...) {
2305 m_state = state_t::fail;
2306 }
2307 }
2308
2309 virtual void flush()
2310 {
2311#ifdef _WIN32
2312 m_state = FlushFileBuffers(m_h) ? state_t::ok : state_t::fail;
2313#else
2314 m_state = fsync(m_h) >= 0 ? state_t::ok : state_t::fail;
2315#endif
2316 }
2317 };
2318
2322 class buffered_sys : public buffer
2323 {
2324 public:
2325 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) :
2326 buffer(read_buffer_size, write_buffer_size),
2327 m_source(h)
2328 {
2329 init(m_source);
2330 }
2331
2332 virtual ~buffered_sys()
2333 {
2334 done();
2335 }
2336
2337 protected:
2338 basic_sys m_source;
2339 };
2340
2344 class socket : public basic
2345 {
2346 public:
2347 socket(_In_opt_ socket_t h = invalid_socket, _In_ state_t state = state_t::ok) :
2348 basic(state),
2349 m_h(h)
2350 {}
2351
2352 private:
2353 socket(_In_ const socket& other);
2354 socket& operator =(_In_ const socket& other);
2355
2356 public:
2357 socket(_Inout_ socket&& other) noexcept : m_h(other.m_h)
2358 {
2359 other.m_h = invalid_socket;
2360 }
2361
2362 socket& operator =(_Inout_ socket&& other) noexcept
2363 {
2364 if (this != std::addressof(other)) {
2365 if (m_h != invalid_socket)
2366 closesocket(m_h);
2367 m_h = other.m_h;
2368 other.m_h = invalid_socket;
2369 }
2370 return *this;
2371 }
2372
2380 socket(_In_ int af, _In_ int type, _In_ int protocol)
2381 {
2382 m_h = ::socket(af, type, protocol);
2383 if (m_h == invalid_socket) _Unlikely_
2384 m_state = state_t::fail;
2385 }
2386
2387 virtual ~socket()
2388 {
2389 if (m_h != invalid_socket)
2390 closesocket(m_h);
2391 }
2392
2396 operator bool() const noexcept { return m_h != invalid_socket; }
2397
2401 socket_t get() const noexcept { return m_h; }
2402
2403 virtual _Success_(return != 0 || length == 0) size_t read(
2404 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2405 {
2406 _Assume_(data || !length);
2407 constexpr int block_size = 0x10000000;
2408 for (size_t to_read = length;;) {
2409 int num_read = recv(m_h, reinterpret_cast<char*>(data), static_cast<int>(std::min<size_t>(to_read, block_size)), 0);
2410 if (num_read == SOCKET_ERROR) _Unlikely_ {
2411 m_state = to_read < length ? state_t::ok : state_t::fail;
2412 return length - to_read;
2413 }
2414 if (!num_read) {
2415 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2416 return length - to_read;
2417 }
2418 to_read -= num_read;
2419 if (!to_read) {
2420 m_state = state_t::ok;
2421 return length;
2422 }
2423 reinterpret_cast<uint8_t*&>(data) += num_read;
2424 }
2425 }
2426
2427 virtual _Success_(return != 0) size_t write(
2428 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2429 {
2430 _Assume_(data || !length);
2431 constexpr int block_size = 0x10000000;
2432 for (size_t to_write = length;;) {
2433 int num_written = send(m_h, reinterpret_cast<const char*>(data), static_cast<int>(std::min<size_t>(to_write, block_size)), 0);
2434 if (num_written == SOCKET_ERROR) _Unlikely_ {
2435 m_state = state_t::fail;
2436 return length - to_write;
2437 }
2438 to_write -= num_written;
2439 if (!to_write) {
2440 m_state = state_t::ok;
2441 return length;
2442 }
2443 reinterpret_cast<const uint8_t*&>(data) += num_written;
2444 }
2445 }
2446
2447 virtual void close()
2448 {
2449 if (m_h != invalid_socket) {
2450 closesocket(m_h);
2451 m_h = invalid_socket;
2452 }
2453 m_state = state_t::ok;
2454 }
2455
2456 protected:
2457 socket_t m_h;
2458 };
2459
2460#ifdef _WIN32
2464 class sequential_stream : public basic
2465 {
2466 public:
2467 sequential_stream(_In_ ISequentialStream* source) : m_source(source)
2468 {
2469 m_source->AddRef();
2470 }
2471
2472 virtual ~sequential_stream()
2473 {
2474 m_source->Release();
2475 }
2476
2477 virtual _Success_(return != 0 || length == 0) size_t read(
2478 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2479 {
2480 _Assume_(data || !length);
2481 for (size_t to_read = length;;) {
2482 HRESULT hr;
2483 ULONG num_read = 0;
2484 __try { hr = m_source->Read(data, (ULONG)std::min<size_t>(to_read, ULONG_MAX), &num_read); }
2485 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2486 if (FAILED(hr)) _Unlikely_ {
2487 m_state = to_read < length ? state_t::ok : state_t::fail;
2488 return length - to_read;
2489 }
2490 to_read -= num_read;
2491 if (hr == S_FALSE) _Unlikely_ {
2492 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2493 return length - to_read;
2494 }
2495 if (!to_read) {
2496 m_state = state_t::ok;
2497 return length;
2498 }
2499 reinterpret_cast<uint8_t*&>(data) += num_read;
2500 }
2501 }
2502
2503 virtual _Success_(return != 0) size_t write(
2504 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2505 {
2506 _Assume_(data || !length);
2507 for (size_t to_write = length;;) {
2508 HRESULT hr;
2509 ULONG num_written = 0;
2510 __try { hr = m_source->Write(data, static_cast<ULONG>(std::min<size_t>(to_write, ULONG_MAX)), &num_written); }
2511 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2512 // In abscence of documentation whether num_written gets set when FAILED(hr) (i.e. partially succesful writes),
2513 // assume write failed completely.
2514 if (FAILED(hr)) _Unlikely_ {
2515 m_state = state_t::fail;
2516 return length - to_write;
2517 }
2518 to_write -= num_written;
2519 if (!to_write) {
2520 m_state = state_t::ok;
2521 return length;
2522 }
2523 reinterpret_cast<const uint8_t*&>(data) += num_written;
2524 }
2525 }
2526
2527 protected:
2528 ISequentialStream* m_source;
2529 };
2530
2534 class asp : public basic
2535 {
2536 public:
2537 asp(_In_opt_ IRequest* request, _In_opt_ IResponse* response) :
2538 m_request(request),
2539 m_response(response)
2540 {
2541 if (m_request)
2542 m_request->AddRef();
2543 if (m_response)
2544 m_response->AddRef();
2545 }
2546
2547 virtual ~asp()
2548 {
2549 if (m_request)
2550 m_request->Release();
2551 if (m_response)
2552 m_response->Release();
2553 }
2554
2555 virtual _Success_(return != 0 || length == 0) size_t read(
2556 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2557 {
2558 _Assume_(data || !length);
2559 if (!m_request) _Unlikely_ {
2560 m_state = state_t::fail;
2561 return 0;
2562 }
2563 for (size_t to_read = length;;) {
2564 VARIANT var_amount, var_data;
2565 V_VT(&var_amount) = VT_I4;
2566 V_I4(&var_amount) = (LONG)std::min<size_t>(to_read, LONG_MAX);
2567 V_VT(&var_data) = VT_EMPTY;
2568 HRESULT hr = [&]() {
2569 __try { return m_request->BinaryRead(&var_amount, &var_data); }
2570 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2571 }();
2572 if (FAILED(hr)) _Unlikely_ {
2573 m_state = to_read < length ? state_t::ok : state_t::fail;
2574 return length - to_read;
2575 }
2576 _Assume_(V_VT(&var_amount) == VT_I4);
2577 _Assume_(V_VT(&var_data) == (VT_ARRAY | VT_UI1));
2578 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(V_ARRAY(&var_data));
2579 if (!V_I4(&var_amount)) _Unlikely_ {
2580 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2581 return length - to_read;
2582 }
2583 safearray_accessor<uint8_t> a(sa.get());
2584 memcpy(data, a.data(), V_I4(&var_amount));
2585 to_read -= V_I4(&var_amount);
2586 if (!to_read) {
2587 m_state = state_t::ok;
2588 return length;
2589 }
2590 reinterpret_cast<uint8_t*&>(data) += V_I4(&var_amount);
2591 }
2592 }
2593
2594 virtual _Success_(return != 0) size_t write(
2595 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2596 {
2597 if (!m_response) {
2598 m_state = state_t::fail;
2599 return 0;
2600 }
2601 for (size_t to_write = length;;) {
2602 UINT num_written = static_cast<UINT>(std::min<size_t>(to_write, UINT_MAX));
2603 std::unique_ptr<OLECHAR, SysFreeString_delete> bstr_data(SysAllocStringByteLen(reinterpret_cast<LPCSTR>(data), num_written));
2604 VARIANT var_data;
2605 V_VT(&var_data) = VT_BSTR;
2606 V_BSTR(&var_data) = bstr_data.get();
2607 HRESULT hr = [&]() {
2608 __try { return m_response->BinaryWrite(var_data); }
2609 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2610 }();
2611 if (FAILED(hr)) _Unlikely_ {
2612 m_state = state_t::fail;
2613 return length - to_write;
2614 }
2615 to_write -= num_written;
2616 if (!to_write) {
2617 m_state = state_t::ok;
2618 return length;
2619 }
2620 reinterpret_cast<const uint8_t*&>(data) += num_written;
2621 }
2622 }
2623
2624 virtual void close()
2625 {
2626 if (m_response) {
2627 __try { m_response->End(); }
2628 __except (EXCEPTION_EXECUTE_HANDLER) {}
2629 }
2630 m_state = state_t::ok;
2631 }
2632
2633 virtual void flush()
2634 {
2635 if (m_response) {
2636 HRESULT hr;
2637 __try { hr = m_response->Flush(); }
2638 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2639 m_state = SUCCEEDED(hr) ? state_t::ok : state_t::fail;
2640 }
2641 }
2642
2643 protected:
2644 IRequest* m_request;
2645 IResponse* m_response;
2646 };
2647#endif
2648
2652 enum mode_t
2653 {
2654 mode_for_reading = 1 << 0,
2655 mode_for_writing = 1 << 1,
2656 mode_for_chmod = 1 << 2,
2657
2658 mode_open_existing = 0 << 3,
2659 mode_truncate_existing = 1 << 3,
2660 mode_preserve_existing = 2 << 3,
2661 mode_create_new = 3 << 3,
2662 mode_create = 4 << 3,
2663 mode_disposition_mask = 7 << 3,
2664
2665 mode_append = 1 << 6,
2666 mode_text = 0,
2667 mode_binary = 1 << 7,
2668
2669 share_none = 0,
2670 share_reading = 1 << 8,
2671 share_writing = 1 << 9,
2672 share_deleting = 1 << 10,
2673 share_all = share_reading | share_writing | share_deleting, // Allow others all operations on our file
2674
2675 inherit_handle = 1 << 11,
2676
2677 hint_write_thru = 1 << 12,
2678 hint_no_buffering = 1 << 13,
2679 hint_random_access = 1 << 14,
2680 hint_sequential_access = 1 << 15,
2681 };
2682
2683#pragma warning(push)
2684#pragma warning(disable: 4250)
2688 class file : virtual public basic_file, virtual public basic_sys
2689 {
2690 public:
2691 file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) : basic_sys(h, state) {}
2692
2699 file(_In_z_ const schar_t* filename, _In_ int mode)
2700 {
2701 open(filename, mode);
2702 }
2703
2710 file(_In_ const stdex::sstring& filename, _In_ int mode) : file(filename.c_str(), mode) {}
2711
2718 void open(_In_z_ const schar_t* filename, _In_ int mode)
2719 {
2720 if (m_h != invalid_handle)
2721 close();
2722
2723#ifdef _WIN32
2724 DWORD dwDesiredAccess = 0;
2725 if (mode & mode_for_reading) dwDesiredAccess |= GENERIC_READ;
2726 if (mode & mode_for_writing) dwDesiredAccess |= GENERIC_WRITE;
2727 if (mode & mode_for_chmod) dwDesiredAccess |= FILE_WRITE_ATTRIBUTES;
2728
2729 DWORD dwShareMode = 0;
2730 if (mode & share_reading) dwShareMode |= FILE_SHARE_READ;
2731 if (mode & share_writing) dwShareMode |= FILE_SHARE_WRITE;
2732 if (mode & share_deleting) dwShareMode |= FILE_SHARE_DELETE;
2733
2734 SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };
2735 sa.bInheritHandle = mode & inherit_handle ? true : false;
2736
2737 DWORD dwCreationDisposition;
2738 switch (mode & mode_disposition_mask) {
2739 case mode_open_existing: dwCreationDisposition = OPEN_EXISTING; break;
2740 case mode_truncate_existing: dwCreationDisposition = TRUNCATE_EXISTING; break;
2741 case mode_preserve_existing: dwCreationDisposition = OPEN_ALWAYS; break;
2742 case mode_create_new: dwCreationDisposition = CREATE_NEW; break;
2743 case mode_create: dwCreationDisposition = CREATE_ALWAYS; break;
2744 default: throw std::invalid_argument("invalid mode");
2745 }
2746
2747 DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
2748 if (mode & hint_write_thru) dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
2749 if (mode & hint_no_buffering) dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
2750 if (mode & hint_random_access) dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
2751 if (mode & hint_sequential_access) dwFlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
2752
2753 m_h = CreateFile(filename, dwDesiredAccess, dwShareMode, &sa, dwCreationDisposition, dwFlagsAndAttributes, NULL);
2754#else
2755 int flags = 0;
2756 switch (mode & (mode_for_reading | mode_for_writing)) {
2757 case mode_for_reading: flags |= O_RDONLY; break;
2758 case mode_for_writing: flags |= O_WRONLY; break;
2759 case mode_for_reading | mode_for_writing: flags |= O_RDWR; break;
2760 }
2761 switch (mode & mode_disposition_mask) {
2762 case mode_open_existing: break;
2763 case mode_truncate_existing: flags |= O_TRUNC; break;
2764 case mode_preserve_existing: flags |= O_CREAT; break;
2765 case mode_create_new: flags |= O_CREAT | O_EXCL; break;
2766 case mode_create: flags |= O_CREAT | O_TRUNC; break;
2767 default: throw std::invalid_argument("invalid mode");
2768 }
2769 if (mode & hint_write_thru) flags |= O_DSYNC;
2770#ifndef __APPLE__
2771 if (mode & hint_no_buffering) flags |= O_RSYNC;
2772#endif
2773
2774 m_h = ::open(filename, flags, DEFFILEMODE);
2775#endif
2776 if (m_h != invalid_handle) {
2777 m_state = state_t::ok;
2778 if (mode & mode_append)
2779 seek(0, seek_t::end);
2780 }
2781 else
2782 m_state = state_t::fail;
2783 }
2784
2791 void open(_In_ const stdex::sstring& filename, _In_ int mode)
2792 {
2793 open(filename.c_str(), mode);
2794 }
2795
2796 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2797 {
2798#ifdef _WIN32
2799 LARGE_INTEGER li;
2800 li.QuadPart = offset;
2801 li.LowPart = SetFilePointer(m_h, li.LowPart, &li.HighPart, static_cast<DWORD>(how));
2802 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR) {
2803 m_state = state_t::ok;
2804 return li.QuadPart;
2805 }
2806#else
2807 off64_t result = lseek64(m_h, offset, static_cast<int>(how));
2808 if (result >= 0) {
2809 m_state = state_t::ok;
2810 return result;
2811 }
2812#endif
2813 m_state = state_t::fail;
2814 return fpos_max;
2815 }
2816
2817 virtual fpos_t tell() const
2818 {
2819 if (m_h != invalid_handle) {
2820#ifdef _WIN32
2821 LARGE_INTEGER li;
2822 li.QuadPart = 0;
2823 li.LowPart = SetFilePointer(m_h, 0, &li.HighPart, FILE_CURRENT);
2824 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR)
2825 return li.QuadPart;
2826#else
2827 off64_t result = lseek64(m_h, 0, SEEK_CUR);
2828 if (result >= 0)
2829 return result;
2830#endif
2831 }
2832 return fpos_max;
2833 }
2834
2835 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2836 {
2837#ifdef _WIN32
2838 LARGE_INTEGER liOffset;
2839 LARGE_INTEGER liSize;
2840 liOffset.QuadPart = offset;
2841 liSize.QuadPart = length;
2842 if (LockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2843 m_state = state_t::ok;
2844 return;
2845 }
2846#else
2847 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2848 if (orig >= 0) {
2849 m_state = lseek64(m_h, offset, SEEK_SET) >= 0 && lockf64(m_h, F_LOCK, length) >= 0 ? state_t::ok : state_t::fail;
2850 lseek64(m_h, orig, SEEK_SET);
2851 m_state = state_t::ok;
2852 return;
2853 }
2854#endif
2855 m_state = state_t::fail;
2856 }
2857
2858 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2859 {
2860#ifdef _WIN32
2861 LARGE_INTEGER liOffset;
2862 LARGE_INTEGER liSize;
2863 liOffset.QuadPart = offset;
2864 liSize.QuadPart = length;
2865 if (UnlockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2866 m_state = state_t::ok;
2867 return;
2868 }
2869#else
2870 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2871 if (orig >= 0) {
2872 if (lseek64(m_h, offset, SEEK_SET) >= 0 && lockf64(m_h, F_ULOCK, length) >= 0) {
2873 lseek64(m_h, orig, SEEK_SET);
2874 m_state = state_t::ok;
2875 return;
2876 }
2877 lseek64(m_h, orig, SEEK_SET);
2878 }
2879#endif
2880 m_state = state_t::fail;
2881 }
2882
2883 virtual fsize_t size() const
2884 {
2885#ifdef _WIN32
2886 LARGE_INTEGER li;
2887 li.LowPart = GetFileSize(m_h, (LPDWORD)&li.HighPart);
2888 if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR)
2889 li.QuadPart = -1;
2890 return li.QuadPart;
2891#else
2892 off64_t length = -1, orig = lseek64(m_h, 0, SEEK_CUR);
2893 if (orig >= 0) {
2894 length = lseek64(m_h, 0, SEEK_END);
2895 lseek64(m_h, orig, SEEK_SET);
2896 }
2897 return length;
2898#endif
2899 }
2900
2901 virtual void truncate()
2902 {
2903#ifdef _WIN32
2904 if (SetEndOfFile(m_h)) {
2905 m_state = state_t::ok;
2906 return;
2907 }
2908#else
2909 off64_t length = lseek64(m_h, 0, SEEK_CUR);
2910 if (length >= 0 && ftruncate64(m_h, length) >= 0) {
2911 m_state = state_t::ok;
2912 return;
2913 }
2914#endif
2915 m_state = state_t::fail;
2916 }
2917
2918#ifdef _WIN32
2919 static time_point ft2tp(_In_ const FILETIME& ft)
2920 {
2921#if _HAS_CXX20
2922 uint64_t t = (static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
2923#else
2924 uint64_t t = ((static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) - 116444736000000000ll;
2925#endif
2926 return time_point(time_point::duration(t));
2927 }
2928
2929 static void tp2ft(_In_ time_point tp, _Out_ FILETIME& ft)
2930 {
2931#if _HAS_CXX20
2932 uint64_t t = tp.time_since_epoch().count();
2933#else
2934 uint64_t t = tp.time_since_epoch().count() + 116444736000000000ll;
2935#endif
2936 ft.dwHighDateTime = static_cast<DWORD>((t >> 32) & 0xffffffff);
2937 ft.dwLowDateTime = static_cast<DWORD>(t & 0xffffffff);
2938 }
2939#endif
2940
2941 virtual time_point ctime() const
2942 {
2943#ifdef _WIN32
2944 FILETIME ft;
2945 if (GetFileTime(m_h, &ft, nullptr, nullptr))
2946 return ft2tp(ft);
2947#endif
2948 return time_point::min();
2949 }
2950
2951 virtual time_point atime() const
2952 {
2953#ifdef _WIN32
2954 FILETIME ft;
2955 if (GetFileTime(m_h, nullptr, &ft, nullptr))
2956 return ft2tp(ft);
2957#else
2958 struct stat buf;
2959 if (fstat(m_h, &buf) >= 0)
2960 return clock::from_time_t(buf.st_atime);
2961#endif
2962 return time_point::min();
2963 }
2964
2965 virtual time_point mtime() const
2966 {
2967#ifdef _WIN32
2968 FILETIME ft;
2969 if (GetFileTime(m_h, nullptr, nullptr, &ft))
2970 return ft2tp(ft);
2971#else
2972 struct stat buf;
2973 if (fstat(m_h, &buf) >= 0)
2974 return clock::from_time_t(buf.st_mtime);
2975#endif
2976 return time_point::min();
2977 }
2978
2979 virtual void set_ctime(time_point date)
2980 {
2981 _Assume_(m_h != invalid_handle);
2982#ifdef _WIN32
2983 FILETIME ft;
2984 tp2ft(date, ft);
2985 if (SetFileTime(m_h, &ft, nullptr, nullptr))
2986 return;
2987 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
2988#else
2989 throw std::runtime_error("not supported");
2990#endif
2991 }
2992
2993 virtual void set_atime(time_point date)
2994 {
2995 _Assume_(m_h != invalid_handle);
2996#ifdef _WIN32
2997 FILETIME ft;
2998 tp2ft(date, ft);
2999 if (SetFileTime(m_h, nullptr, &ft, nullptr))
3000 return;
3001 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3002#else
3003 struct timespec ts[2] = {
3004 { date.time_since_epoch().count(), 0 },
3005 { 0, UTIME_OMIT },
3006 };
3007 if (futimens(m_h, ts) >= 0)
3008 return;
3009 throw std::system_error(errno, std::system_category(), "futimens failed");
3010#endif
3011 }
3012
3013 virtual void set_mtime(time_point date)
3014 {
3015#ifdef _WIN32
3016 FILETIME ft;
3017 tp2ft(date, ft);
3018 if (SetFileTime(m_h, nullptr, nullptr, &ft))
3019 return;
3020 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3021#else
3022 struct timespec ts[2] = {
3023 { 0, UTIME_OMIT },
3024 { date.time_since_epoch().count(), 0 },
3025 };
3026 if (futimens(m_h, ts) >= 0)
3027 return;
3028 throw std::system_error(errno, std::system_category(), "futimens failed");
3029#endif
3030 }
3031
3037 static bool exists(_In_z_ const stdex::schar_t* filename)
3038 {
3039#ifdef _WIN32
3040 return GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES;
3041#else
3042 struct stat s;
3043 return stat(filename, &s) == 0;
3044#endif
3045 }
3046
3052 static bool exists(_In_ const stdex::sstring& filename)
3053 {
3054 return exists(filename.c_str());
3055 }
3056
3064 static bool readonly(_In_z_ const stdex::schar_t* filename)
3065 {
3066#ifdef _WIN32
3067 DWORD dwAttr = GetFileAttributes(filename);
3068 return dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_READONLY) != 0;
3069#else
3070 struct stat s;
3071 return stat(filename, &s) == 0 && (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0;
3072#endif
3073 }
3074
3082 static bool readonly(_In_ const stdex::sstring& filename)
3083 {
3084 return readonly(filename.c_str());
3085 }
3086 };
3087#pragma warning(pop)
3088
3092 class cached_file : public cache
3093 {
3094 public:
3095 cached_file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok, _In_ size_t cache_size = default_cache_size) :
3096 cache(cache_size),
3097 m_source(h, state)
3098 {
3099 init(m_source);
3100 }
3101
3109 cached_file(_In_z_ const schar_t* filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) :
3110 cache(cache_size),
3111 m_source(filename, mode & mode_for_writing ? mode | mode_for_reading : mode)
3112 {
3113 init(m_source);
3114 }
3115
3123 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) {}
3124
3125 virtual ~cached_file()
3126 {
3127 done();
3128 }
3129
3136 void open(_In_z_ const schar_t* filename, _In_ int mode)
3137 {
3138 invalidate_cache();
3139 if (!ok()) _Unlikely_{
3140 m_state = state_t::fail;
3141 return;
3142 }
3143 m_source.open(filename, mode & mode_for_writing ? mode | mode_for_reading : mode);
3144 if (m_source.ok()) {
3145 init();
3146 return;
3147 }
3148 m_state = state_t::fail;
3149 }
3150
3157 void open(_In_ const stdex::sstring& filename, _In_ int mode)
3158 {
3159 open(filename.c_str(), mode);
3160 }
3161
3165 operator bool() const noexcept { return m_source; }
3166
3167 protected:
3168 file m_source;
3169 };
3170
3175 {
3176 public:
3177 memory_file(_In_ state_t state = state_t::ok) :
3178 basic(state),
3179 m_data(nullptr),
3180 m_offset(0),
3181 m_size(0),
3182 m_reserved(0),
3183 m_manage(true)
3184 {
3185#if SET_FILE_OP_TIMES
3186 m_ctime = m_atime = m_mtime = time_point::now();
3187#endif
3188 }
3189
3196 memory_file(_In_ size_t size, _In_ state_t state = state_t::ok) :
3197 basic(state),
3198 m_data(reinterpret_cast<uint8_t*>(malloc(size))),
3199 m_offset(0),
3200 m_size(0),
3202 m_manage(true)
3203 {
3204 if (!m_data) {
3205 m_state = state_t::fail;
3206 throw std::bad_alloc();
3207 }
3208#if SET_FILE_OP_TIMES
3209 m_ctime = m_atime = m_mtime = time_point::now();
3210#endif
3211 }
3212
3222 memory_file(_Inout_ void* data, _In_ size_t size, _In_ size_t reserved, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3223 basic(state),
3224 m_data(reinterpret_cast<uint8_t*>(data)),
3225 m_offset(0),
3226 m_size(size),
3227 m_reserved(reserved),
3228 m_manage(manage)
3229 {
3230 _Assume_(data || !size);
3231 _Assume_(reserved >= size);
3232#if SET_FILE_OP_TIMES
3233 m_ctime = m_atime = m_mtime = time_point::now();
3234#endif
3235 }
3236
3245 memory_file(_Inout_ void* data, _In_ size_t size, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3246 memory_file(data, size, size, manage, state)
3247 {}
3248
3255 memory_file(_In_z_ const schar_t* filename, _In_ int mode) : memory_file()
3256 {
3257 load(filename, mode);
3258 }
3259
3266 memory_file(_In_ const stdex::sstring& filename, _In_ int mode) : memory_file(filename.c_str(), mode) {}
3267
3273 memory_file(_In_ const memory_file& other) :
3274 basic_file(other),
3275 m_data(reinterpret_cast<uint8_t*>(malloc(other.m_size))),
3276 m_offset(other.m_offset),
3277 m_size(other.m_size),
3278 m_reserved(other.m_size),
3279 m_manage(true)
3280#if SET_FILE_OP_TIMES
3281 , m_ctime(other.m_ctime)
3282 , m_atime(other.m_atime)
3283 , m_mtime(other.m_mtime)
3284#endif
3285 {
3286 if (!m_data) {
3287 m_state = state_t::fail;
3288 throw std::bad_alloc();
3289 }
3290 memcpy(m_data, other.m_data, other.m_size);
3291 }
3292
3299 {
3300 if (this != std::addressof(other)) {
3301 *static_cast<basic_file*>(this) = other;
3302 if (m_manage && m_data)
3303 free(m_data);
3304 m_data = reinterpret_cast<uint8_t*>(malloc(other.m_size));
3305 if (!m_data) {
3306 m_state = state_t::fail;
3307 throw std::bad_alloc();
3308 }
3309 memcpy(m_data, other.m_data, other.m_size);
3310 m_offset = other.m_offset;
3311 m_size = other.m_size;
3312 m_reserved = other.m_size;
3313 m_manage = true;
3314#if SET_FILE_OP_TIMES
3315 m_ctime = other.m_ctime;
3316 m_atime = other.m_atime;
3317 m_mtime = other.m_mtime;
3318#endif
3319 }
3320 return *this;
3321 }
3322
3328 memory_file(_Inout_ memory_file&& other) noexcept :
3329 basic_file(std::move(other)),
3330 m_data(other.m_data),
3331 m_offset(other.m_offset),
3332 m_size(other.m_size),
3333 m_reserved(other.m_reserved),
3334 m_manage(other.m_manage)
3335#if SET_FILE_OP_TIMES
3336 , m_ctime(other.m_ctime)
3337 , m_atime(other.m_atime)
3338 , m_mtime(other.m_mtime)
3339#endif
3340 {
3341 other.m_state = state_t::ok;
3342 other.m_data = nullptr;
3343 other.m_offset = 0;
3344 other.m_size = 0;
3345 other.m_reserved = 0;
3346 other.m_manage = true;
3347#if SET_FILE_OP_TIMES
3348 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3349#endif
3350 }
3351
3357 memory_file& operator=(_Inout_ memory_file&& other) noexcept
3358 {
3359 if (this != std::addressof(other)) {
3360 *static_cast<basic_file*>(this) = std::move(other);
3361 if (m_manage && m_data)
3362 free(m_data);
3363 m_data = other.m_data;
3364 other.m_data = nullptr;
3365 m_offset = other.m_offset;
3366 other.m_offset = 0;
3367 m_size = other.m_size;
3368 other.m_size = 0;
3369 m_reserved = other.m_reserved;
3370 other.m_reserved = 0;
3371 m_manage = other.m_manage;
3372 other.m_manage = true;
3373#if SET_FILE_OP_TIMES
3374 m_ctime = other.m_ctime;
3375 m_atime = other.m_atime;
3376 m_mtime = other.m_mtime;
3377 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3378#endif
3379 }
3380 return *this;
3381 }
3382
3383 virtual ~memory_file()
3384 {
3385 if (m_manage && m_data)
3386 free(m_data);
3387 }
3388
3395 void reserve(_In_ size_t required, _In_ bool tight = false) noexcept
3396 {
3397 if (required <= m_reserved && (!tight || required >= m_reserved)) {
3398 m_state = state_t::ok;
3399 return;
3400 }
3401 if (!m_manage) {
3402 m_state = state_t::fail;
3403 return;
3404 }
3405 size_t reserved = tight ? required : ((required + required / 4 + (default_block_size - 1)) / default_block_size) * default_block_size;
3406 auto data = reinterpret_cast<uint8_t*>(realloc(m_data, reserved));
3407 if (!data && reserved) _Unlikely_ {
3408 m_state = state_t::fail;
3409 return;
3410 }
3411 m_data = data;
3412 if (reserved < m_size)
3413 m_size = reserved;
3414 m_reserved = reserved;
3415 m_state = state_t::ok;
3416 }
3417
3424 void load(_In_z_ const schar_t* filename, _In_ int mode)
3425 {
3426 file f(filename, (mode & ~hint_random_access) | mode_for_reading | hint_sequential_access);
3427 if (!f.ok()) {
3428 m_state = state_t::fail;
3429 return;
3430 }
3431 fsize_t size = f.size();
3432 if (size > SIZE_MAX) {
3433 m_state = state_t::fail;
3434 return;
3435 }
3436 reserve(static_cast<size_t>(size), true);
3437 if (!ok()) _Unlikely_ {
3438 return;
3439 }
3440 m_offset = m_size = 0;
3441 write_stream(f);
3442 if (ok())
3443 m_offset = 0;
3444#if SET_FILE_OP_TIMES
3445 m_ctime = f.ctime();
3446 m_atime = f.atime();
3447 m_mtime = f.mtime();
3448#endif
3449 }
3450
3457 void load(_In_ const stdex::sstring& filename, _In_ int mode)
3458 {
3459 load(filename.c_str(), mode);
3460 }
3461
3468 void save(_In_z_ const schar_t* filename, _In_ int mode)
3469 {
3470 file f(filename, (mode & ~hint_random_access) | mode_for_writing | hint_sequential_access);
3471 if (!f.ok()) {
3472 m_state = state_t::fail;
3473 return;
3474 }
3475 f.write(m_data, m_size);
3476 if (!f.ok()) {
3477 m_state = state_t::fail;
3478 return;
3479 }
3480 f.truncate();
3481#if SET_FILE_OP_TIMES
3482 f.set_ctime(m_ctime);
3483 f.set_atime(m_atime);
3484 f.set_mtime(m_mtime);
3485#endif
3486 }
3487
3494 void save(_In_ const stdex::sstring& filename, _In_ int mode)
3495 {
3496 save(filename.c_str(), mode);
3497 }
3498
3502 const void* data() const { return m_data; }
3503
3504 virtual _Success_(return != 0 || length == 0) size_t read(
3505 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
3506 {
3507 _Assume_(data || !length);
3508#if SET_FILE_OP_TIMES
3509 m_atime = time_point::now();
3510#endif
3511 size_t available = m_size - m_offset;
3512 if (length <= available) {
3513 memcpy(data, m_data + m_offset, length);
3514 m_offset += length;
3515 m_state = state_t::ok;
3516 return length;
3517 }
3518 if (length && !available) {
3519 m_state = state_t::eof;
3520 return 0;
3521 }
3522 memcpy(data, m_data + m_offset, available);
3523 m_offset += available;
3524 m_state = state_t::ok;
3525 return available;
3526 }
3527
3542 template <class T>
3544 {
3545#if SET_FILE_OP_TIMES
3546 m_atime = time_point::now();
3547#endif
3548 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3549 data = 0;
3550 return *this;
3551 }
3552 size_t end_offset = m_offset + sizeof(T);
3553 if (end_offset <= m_size) {
3554 data = LE2HE(*reinterpret_cast<T*>(m_data + m_offset));
3555 m_offset = end_offset;
3556#if !CHECK_STREAM_STATE
3557 m_state = state_t::ok;
3558#endif
3559 }
3560 else {
3561 data = 0;
3562 m_offset = m_size;
3563 m_state = state_t::eof;
3564 }
3565 return *this;
3566 }
3567
3582 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
3583 memory_file& read_str(_Inout_ std::basic_string<_Elem, _Traits, _Ax>&data)
3584 {
3585#if SET_FILE_OP_TIMES
3586 m_atime = time_point::now();
3587#endif
3588 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3589 data.clear();
3590 return *this;
3591 }
3592 size_t end_offset = m_offset + sizeof(uint32_t);
3593 if (end_offset <= m_size) {
3594 uint32_t num_chars = LE2HE(*reinterpret_cast<uint32_t*>(m_data + m_offset));
3595 m_offset = end_offset;
3596 end_offset = stdex::add(m_offset, stdex::mul(num_chars, sizeof(_Elem)));
3597 _Elem* start = reinterpret_cast<_Elem*>(m_data + m_offset);
3598 if (end_offset <= m_size) {
3599 data.assign(start, start + num_chars);
3600 m_offset = end_offset;
3601#if !CHECK_STREAM_STATE
3602 m_state = state_t::ok;
3603#endif
3604 return *this;
3605 }
3606 if (end_offset <= m_size)
3607 data.assign(start, reinterpret_cast<_Elem*>(m_data + m_size));
3608 }
3609 m_offset = m_size;
3610 m_state = state_t::eof;
3611 return *this;
3612 }
3613
3614 virtual _Success_(return != 0) size_t write(
3615 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3616 {
3617 _Assume_(data || !length);
3618#if SET_FILE_OP_TIMES
3619 m_atime = m_mtime = time_point::now();
3620#endif
3621 size_t end_offset = m_offset + length;
3622 if (end_offset > m_reserved) {
3623 reserve(end_offset);
3624 if (!ok()) _Unlikely_
3625 return 0;
3626 }
3627 memcpy(m_data + m_offset, data, length);
3628 m_offset = end_offset;
3629 if (m_offset > m_size)
3630 m_size = m_offset;
3631 m_state = state_t::ok;
3632 return length;
3633 }
3634
3638 void write_byte(_In_ uint8_t byte, _In_ size_t amount = 1)
3639 {
3640#if SET_FILE_OP_TIMES
3641 m_atime = m_mtime = time_point::now();
3642#endif
3643 size_t end_offset = m_offset + amount;
3644 if (end_offset > m_reserved) {
3645 reserve(end_offset);
3646 if (!ok()) _Unlikely_
3647 return;
3648 }
3649 memset(m_data + m_offset, byte, amount);
3650 m_offset = end_offset;
3651 if (m_offset > m_size)
3652 m_size = m_offset;
3653 m_state = state_t::ok;
3654 }
3655
3670 template <class T>
3672 {
3673#if SET_FILE_OP_TIMES
3674 m_atime = m_mtime = time_point::now();
3675#endif
3676 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3677 return *this;
3678 size_t end_offset = m_offset + sizeof(T);
3679 if (end_offset > m_reserved) {
3680 reserve(end_offset);
3681 if (!ok()) _Unlikely_
3682 return *this;
3683 }
3684 (*reinterpret_cast<T*>(m_data + m_offset)) = HE2LE(data);
3685 m_offset = end_offset;
3686 if (m_offset > m_size)
3687 m_size = m_offset;
3688#if !CHECK_STREAM_STATE
3689 m_state = state_t::ok;
3690#endif
3691 return *this;
3692 }
3693
3708 template <class T>
3709 memory_file& write_str(_In_z_ const T * data)
3710 {
3711#if SET_FILE_OP_TIMES
3712 m_atime = m_mtime = time_point::now();
3713#endif
3714 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3715 return *this;
3716 size_t num_chars = stdex::strlen(data);
3717 if (num_chars > UINT32_MAX)
3718 throw std::invalid_argument("string too long");
3719 size_t size_chars = num_chars * sizeof(T);
3720 size_t size = sizeof(uint32_t) + size_chars;
3721 size_t end_offset = m_offset + size;
3722 if (end_offset > m_reserved) {
3723 reserve(end_offset);
3724 if (!ok()) _Unlikely_
3725 return *this;
3726 }
3727 auto p = m_data + m_offset;
3728 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3729 memcpy(p + sizeof(uint32_t), data, size_chars);
3730 m_offset = end_offset;
3731 if (m_offset > m_size)
3732 m_size = m_offset;
3733#if !CHECK_STREAM_STATE
3734 m_state = state_t::ok;
3735#endif
3736 return *this;
3737 }
3738
3753 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
3754 memory_file& write_str(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data)
3755 {
3756#if SET_FILE_OP_TIMES
3757 m_atime = m_mtime = time_point::now();
3758#endif
3759 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3760 return *this;
3761 size_t num_chars = data.size();
3762 if (num_chars > UINT32_MAX)
3763 throw std::invalid_argument("string too long");
3764 size_t size_chars = num_chars * sizeof(_Elem);
3765 size_t size = sizeof(uint32_t) + size_chars;
3766 size_t end_offset = m_offset + size;
3767 if (end_offset > m_reserved) {
3768 reserve(end_offset);
3769 if (!ok()) _Unlikely_
3770 return *this;
3771 }
3772 auto p = m_data + m_offset;
3773 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3774 memcpy(p + sizeof(uint32_t), data.data(), size_chars);
3775 m_offset = end_offset;
3776 if (m_offset > m_size)
3777 m_size = m_offset;
3778#if !CHECK_STREAM_STATE
3779 m_state = state_t::ok;
3780#endif
3781 return *this;
3782 }
3783
3789 size_t write_stream(_Inout_ basic & stream, _In_ size_t amount = SIZE_MAX)
3790 {
3791#if SET_FILE_OP_TIMES
3792 m_atime = m_mtime = time_point::now();
3793#endif
3794 size_t num_read, dst_offset = m_offset, dst_size = m_offset;
3795 size_t num_copied = 0, to_write = amount;
3796 m_state = state_t::ok;
3797 if (amount != SIZE_MAX) {
3798 dst_size = stdex::add(dst_size, amount);
3799 reserve(dst_size);
3800 if (!ok()) _Unlikely_
3801 return 0;
3802 while (to_write) {
3803 num_read = stream.read(m_data + dst_offset, to_write);
3804 dst_size = dst_offset += num_read;
3805 num_copied += num_read;
3806 to_write -= num_read;
3807 if (!stream.ok()) {
3808 if (stream.state() != state_t::eof)
3809 m_state = state_t::fail;
3810 break;
3811 }
3812 };
3813 }
3814 else {
3815 size_t block_size;
3816 while (to_write) {
3817 block_size = std::min(to_write, default_block_size);
3818 dst_size = stdex::add(dst_size, block_size);
3819 reserve(dst_size);
3820 if (!ok()) _Unlikely_
3821 break;
3822 num_read = stream.read(m_data + dst_offset, block_size);
3823 dst_size = dst_offset += num_read;
3824 num_copied += num_read;
3825 to_write -= num_read;
3826 if (!stream.ok()) {
3827 if (stream.state() != state_t::eof)
3828 m_state = state_t::fail;
3829 break;
3830 }
3831 };
3832 }
3833 m_offset = dst_offset;
3834 if (m_offset > m_size)
3835 m_size = m_offset;
3836 return num_copied;
3837 }
3838
3839 virtual void close()
3840 {
3841 if (m_manage && m_data)
3842 free(m_data);
3843 m_data = nullptr;
3844 m_manage = true;
3845 m_offset = 0;
3846 m_size = m_reserved = 0;
3847#if SET_FILE_OP_TIMES
3848 m_ctime = m_atime = m_mtime = time_point::min();
3849#endif
3850 m_state = state_t::ok;
3851 }
3852
3853 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
3854 {
3855 fpos_t target;
3856 switch (how) {
3857 case seek_t::beg: target = offset; break;
3858 case seek_t::cur: target = static_cast<fpos_t>(m_offset) + offset; break;
3859 case seek_t::end: target = static_cast<fpos_t>(m_size) + offset; break;
3860 default: throw std::invalid_argument("unknown seek origin");
3861 }
3862 if (target <= SIZE_MAX) {
3863 m_state = state_t::ok;
3864 return m_offset = static_cast<size_t>(target);
3865 }
3866 m_state = state_t::fail;
3867 return fpos_max;
3868 }
3869
3870 virtual fpos_t tell() const
3871 {
3872 return m_offset;
3873 }
3874
3875 virtual fsize_t size() const
3876 {
3877 return m_size;
3878 }
3879
3880 virtual void truncate()
3881 {
3882#if SET_FILE_OP_TIMES
3883 m_atime = m_mtime = time_point::now();
3884#endif
3885 m_size = m_offset;
3887 }
3888
3889#if SET_FILE_OP_TIMES
3890 virtual time_point ctime() const
3891 {
3892 return m_ctime;
3893 }
3894
3895 virtual time_point atime() const
3896 {
3897 return m_atime;
3898 }
3899
3900 virtual time_point mtime() const
3901 {
3902 return m_mtime;
3903 }
3904
3905 virtual void set_ctime(time_point date)
3906 {
3907 m_ctime = date;
3908 }
3909
3910 virtual void set_atime(time_point date)
3911 {
3912 m_atime = date;
3913 }
3914
3915 virtual void set_mtime(time_point date)
3916 {
3917 m_mtime = date;
3918 }
3919#endif
3920
3921 protected:
3929 template <class T>
3930 void set(_In_ fpos_t offset, _In_ const T data)
3931 {
3932#if SET_FILE_OP_TIMES
3933 m_atime = m_mtime = time_point::now();
3934#endif
3935 _Assume_(offset + sizeof(T) < m_size);
3936 (*reinterpret_cast<T*>(m_data + offset)) = HE2LE(data);
3937 }
3938
3939 public:
3940 void set(_In_ fpos_t offset, _In_ const int8_t data) { set<int8_t>(offset, data); }
3941 void set(_In_ fpos_t offset, _In_ const int16_t data) { set<int16_t>(offset, data); }
3942 void set(_In_ fpos_t offset, _In_ const int32_t data) { set<int32_t>(offset, data); }
3943 void set(_In_ fpos_t offset, _In_ const int64_t data) { set<int64_t>(offset, data); }
3944 void set(_In_ fpos_t offset, _In_ const uint8_t data) { set<uint8_t>(offset, data); }
3945 void set(_In_ fpos_t offset, _In_ const uint16_t data) { set<uint16_t>(offset, data); }
3946 void set(_In_ fpos_t offset, _In_ const uint32_t data) { set<uint32_t>(offset, data); }
3947 void set(_In_ fpos_t offset, _In_ const uint64_t data) { set<uint64_t>(offset, data); }
3948 void set(_In_ fpos_t offset, _In_ const float data) { set<float>(offset, data); }
3949 void set(_In_ fpos_t offset, _In_ const double data) { set<double>(offset, data); }
3950 void set(_In_ fpos_t offset, _In_ const char data) { set<char>(offset, data); }
3951#ifdef _NATIVE_WCHAR_T_DEFINED
3952 void set(_In_ fpos_t offset, _In_ const wchar_t data) { set<wchar_t>(offset, data); }
3953#endif
3954
3962 protected:
3963 template <class T>
3964 void get(_In_ fpos_t offset, _Out_ T & data)
3965 {
3966 _Assume_(offset + sizeof(T) < m_size);
3967 data = LE2HE(*(T*)(m_data + offset));
3968#if SET_FILE_OP_TIMES
3969 m_atime = time_point::now();
3970#endif
3971 }
3972
3973 public:
3974 void get(_In_ fpos_t offset, _Out_ int8_t & data) { get<int8_t>(offset, data); }
3975 void get(_In_ fpos_t offset, _Out_ int16_t & data) { get<int16_t>(offset, data); }
3976 void get(_In_ fpos_t offset, _Out_ int32_t & data) { get<int32_t>(offset, data); }
3977 void get(_In_ fpos_t offset, _Out_ int64_t & data) { get<int64_t>(offset, data); }
3978 void get(_In_ fpos_t offset, _Out_ uint8_t & data) { get<uint8_t>(offset, data); }
3979 void get(_In_ fpos_t offset, _Out_ uint16_t & data) { get<uint16_t>(offset, data); }
3980 void get(_In_ fpos_t offset, _Out_ uint32_t & data) { get<uint32_t>(offset, data); }
3981 void get(_In_ fpos_t offset, _Out_ uint64_t & data) { get<uint64_t>(offset, data); }
3982 void get(_In_ fpos_t offset, _Out_ float& data) { get<float>(offset, data); }
3983 void get(_In_ fpos_t offset, _Out_ double& data) { get<double>(offset, data); }
3984 void get(_In_ fpos_t offset, _Out_ char& data) { get<char>(offset, data); }
3985#ifdef _NATIVE_WCHAR_T_DEFINED
3986 void get(_In_ fpos_t offset, _Out_ wchar_t& data) { get<wchar_t>(offset, data); }
3987#endif
3988
3989 memory_file& operator <<(_In_ const int8_t data) { return write_data(data); }
3990 memory_file& operator >>(_Out_ int8_t & data) { return read_data(data); }
3991 memory_file& operator <<(_In_ const int16_t data) { return write_data(data); }
3992 memory_file& operator >>(_Out_ int16_t & data) { return read_data(data); }
3993 memory_file& operator <<(_In_ const int32_t data) { return write_data(data); }
3994 memory_file& operator >>(_Out_ int32_t & data) { return read_data(data); }
3995 memory_file& operator <<(_In_ const int64_t data) { return write_data(data); }
3996 memory_file& operator >>(_Out_ int64_t & data) { return read_data(data); }
3997 memory_file& operator <<(_In_ const uint8_t data) { return write_data(data); }
3998 memory_file& operator >>(_Out_ uint8_t & data) { return read_data(data); }
3999 memory_file& operator <<(_In_ const uint16_t data) { return write_data(data); }
4000 memory_file& operator >>(_Out_ uint16_t & data) { return read_data(data); }
4001 memory_file& operator <<(_In_ const uint32_t data) { return write_data(data); }
4002 memory_file& operator >>(_Out_ uint32_t & data) { return read_data(data); }
4003 memory_file& operator <<(_In_ const uint64_t data) { return write_data(data); }
4004 memory_file& operator >>(_Out_ uint64_t & data) { return read_data(data); }
4005 memory_file& operator <<(_In_ const float data) { return write_data(data); }
4006 memory_file& operator >>(_Out_ float& data) { return read_data(data); }
4007 memory_file& operator <<(_In_ const double data) { return write_data(data); }
4008 memory_file& operator >>(_Out_ double& data) { return read_data(data); }
4009 memory_file& operator <<(_In_ const char data) { return write_data(data); }
4010 memory_file& operator >>(_Out_ char& data) { return read_data(data); }
4011#ifdef _NATIVE_WCHAR_T_DEFINED
4012 memory_file& operator <<(_In_ const wchar_t data) { return write_data(data); }
4013 memory_file& operator >>(_Out_ wchar_t& data) { return read_data(data); }
4014#endif
4015 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
4016 memory_file& operator >>(_Out_ std::basic_string<_Elem, _Traits, _Ax>&data) { return read_str(data); }
4017 template <class T>
4018 memory_file& operator <<(_In_ const T * data) { return write_str(data); }
4019 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
4020 memory_file& operator <<(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data) { return write_str(data); }
4021
4022 protected:
4023 uint8_t* m_data;
4025 size_t m_offset;
4026 size_t m_size;
4027 size_t m_reserved;
4028#if SET_FILE_OP_TIMES
4029 time_point
4030 m_ctime,
4031 m_atime,
4032 m_mtime;
4033#endif
4034 };
4035
4039 class fifo : public basic {
4040 public:
4041 fifo() :
4042 m_offset(0),
4043 m_size(0),
4044 m_head(nullptr),
4045 m_tail(nullptr)
4046 {}
4047
4048 virtual ~fifo()
4049 {
4050 while (m_head) {
4051 auto p = m_head;
4052 m_head = p->next;
4053 delete p;
4054 }
4055 }
4056
4057#pragma warning(suppress: 6101) // See [2] below
4058 virtual _Success_(return != 0 || length == 0) size_t read(
4059 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4060 {
4061 _Assume_(data || !length);
4062 for (size_t to_read = length;;) {
4063 if (!m_head) _Unlikely_ {
4064 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
4065 return length - to_read; // [2] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
4066 }
4067 size_t remaining = m_head->size - m_offset;
4068 if (remaining > to_read) {
4069 memcpy(data, m_head->data + m_offset, to_read);
4070 m_offset += to_read;
4071 m_size -= to_read;
4072 m_state = state_t::ok;
4073 return length;
4074 }
4075 memcpy(data, m_head->data + m_offset, remaining);
4076 m_offset = 0;
4077 m_size -= remaining;
4078 reinterpret_cast<uint8_t*&>(data) += remaining;
4079 to_read -= remaining;
4080 auto p = m_head;
4081 m_head = p->next;
4082 delete p;
4083 }
4084 }
4085
4086 virtual _Success_(return != 0) size_t write(
4087 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4088 {
4089 _Assume_(data || !length);
4090 try {
4091 std::unique_ptr<node_t> n(reinterpret_cast<node_t*>(new uint8_t[sizeof(node_t) + length]));
4092 n->next = nullptr;
4093 n->size = length;
4094 memcpy(n->data, data, length);
4095 m_size += length;
4096 if (m_head)
4097 m_tail = m_tail->next = n.release();
4098 else
4099 m_head = m_tail = n.release();
4100 m_state = state_t::ok;
4101 return length;
4102 }
4103 catch (const std::bad_alloc&) {
4104 m_state = state_t::fail;
4105 return 0;
4106 }
4107 }
4108
4109 virtual void close()
4110 {
4111 m_size = m_offset = 0;
4112 while (m_head) {
4113 auto p = m_head;
4114 m_head = p->next;
4115 delete p;
4116 }
4117 m_state = state_t::ok;
4118 }
4119
4123 size_t size() const { return m_size; };
4124
4125 protected:
4126 size_t m_offset, m_size;
4127 struct node_t {
4128 node_t* next;
4129 size_t size;
4130#pragma warning(suppress:4200)
4131 uint8_t data[0];
4132 } *m_head, * m_tail;
4133 };
4134
4138 class diag_file : public basic_file {
4139 public:
4140 diag_file(_In_count_(num_files) basic_file* const* files, _In_ size_t num_files) :
4141 basic(num_files ? files[0]->state() : state_t::fail),
4142 m_files(files, files + num_files)
4143 {}
4144
4145 virtual _Success_(return != 0 || length == 0) size_t read(
4146 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4147 {
4148 _Assume_(data || !length);
4149 if (m_files.empty()) {
4150 m_state = state_t::fail;
4151 return 0;
4152 }
4153 size_t result = m_files[0]->read(data, length);
4154 _Assume_(result <= length);
4155 m_state = m_files[0]->state();
4156 if (length > m_tmp.size())
4157 m_tmp.resize(length);
4158 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4159 if (m_files[i]->read(m_tmp.data(), length) != result ||
4160 memcmp(m_tmp.data(), data, result))
4161 throw std::runtime_error("read mismatch");
4162 if (m_files[i]->state() != m_state)
4163 throw std::runtime_error("state mismatch");
4164 }
4165 return result;
4166 }
4167
4168 virtual _Success_(return != 0) size_t write(
4169 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4170 {
4171 if (m_files.empty()) {
4172 m_state = state_t::fail;
4173 return 0;
4174 }
4175 size_t result = m_files[0]->write(data, length);
4176 m_state = m_files[0]->state();
4177 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4178 if (m_files[i]->write(data, length) != result)
4179 throw std::runtime_error("write mismatch");
4180 if (m_files[i]->state() != m_state)
4181 throw std::runtime_error("state mismatch");
4182 }
4183 return result;
4184 }
4185
4186 virtual void flush()
4187 {
4188 if (m_files.empty()) {
4189 m_state = state_t::ok;
4190 return;
4191 }
4192 m_files[0]->flush();
4193 m_state = m_files[0]->state();
4194 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4195 m_files[i]->flush();
4196 if (m_files[i]->state() != m_state)
4197 throw std::runtime_error("state mismatch");
4198 }
4199 }
4200
4201 virtual void close()
4202 {
4203 if (m_files.empty()) {
4204 m_state = state_t::ok;
4205 return;
4206 }
4207 m_files[0]->close();
4208 m_state = m_files[0]->state();
4209 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4210 m_files[i]->close();
4211 if (m_files[i]->state() != m_state)
4212 throw std::runtime_error("state mismatch");
4213 }
4214 m_tmp.clear();
4215 m_tmp.shrink_to_fit();
4216 }
4217
4218 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
4219 {
4220 if (m_files.empty()) {
4221 m_state = state_t::fail;
4222 return fpos_max;
4223 }
4224 fpos_t result = m_files[0]->seek(offset, how);
4225 m_state = m_files[0]->state();
4226 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4227 if (m_files[i]->seek(offset, how) != result)
4228 throw std::runtime_error("seek mismatch");
4229 if (m_files[i]->state() != m_state)
4230 throw std::runtime_error("state mismatch");
4231 }
4232 return result;
4233 }
4234
4235 virtual fpos_t tell() const
4236 {
4237 if (m_files.empty())
4238 return fpos_max;
4239 fpos_t result = m_files[0]->tell();
4240 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4241 if (m_files[i]->tell() != result)
4242 throw std::runtime_error("tell mismatch");
4243 }
4244 return result;
4245 }
4246
4247 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
4248 {
4249 if (m_files.empty())
4250 m_state = state_t::fail;
4251 m_files[0]->lock(offset, length);
4252 m_state = m_files[0]->state();
4253 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4254 m_files[i]->lock(offset, length);
4255 if (m_files[i]->state() != m_state)
4256 throw std::runtime_error("state mismatch");
4257 }
4258 }
4259
4260 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
4261 {
4262 if (m_files.empty())
4263 m_state = state_t::fail;
4264 m_files[0]->unlock(offset, length);
4265 m_state = m_files[0]->state();
4266 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4267 m_files[i]->unlock(offset, length);
4268 if (m_files[i]->state() != m_state)
4269 throw std::runtime_error("state mismatch");
4270 }
4271 }
4272
4273 virtual fsize_t size() const
4274 {
4275 if (m_files.empty())
4276 return fsize_max;
4277 fsize_t result = m_files[0]->size();
4278 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4279 if (m_files[i]->size() != result)
4280 throw std::runtime_error("size mismatch");
4281 }
4282 return result;
4283 }
4284
4285 virtual void truncate()
4286 {
4287 if (m_files.empty())
4288 m_state = state_t::fail;
4289 m_files[0]->truncate();
4290 m_state = m_files[0]->state();
4291 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4292 m_files[i]->truncate();
4293 if (m_files[i]->state() != m_state)
4294 throw std::runtime_error("state mismatch");
4295 }
4296 }
4297
4298 protected:
4299 std::vector<basic_file*> m_files;
4300 std::vector<uint8_t> m_tmp;
4301 };
4302 }
4303}
Encoding converter context.
Definition unicode.hpp:133
Provides read-ahead stream capability.
Definition stream.hpp:1244
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:1258
Provides write-back stream capability.
Definition stream.hpp:1311
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1348
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:1324
Basic seekable stream operations.
Definition stream.hpp:812
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:855
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:902
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:871
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:975
fpos_t seekbeg(fpos_t offset)
Seeks to absolute file position.
Definition stream.hpp:839
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:814
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:944
fpos_t seekcur(foff_t offset)
Seeks to relative from current file position.
Definition stream.hpp:846
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:910
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:926
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:881
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:918
fpos_t seekend(foff_t offset)
Seeks to relative from end file position.
Definition stream.hpp:853
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:935
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:2194
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:2251
virtual void flush()
Persists volatile element data.
Definition stream.hpp:2309
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:2201
virtual void close()
Closes the stream.
Definition stream.hpp:2298
‍UTF-8 byte-order-mark
Definition stream.hpp:79
size_t write_array(const T_from *str, charset_encoder< T_from, T_to > &encoder)
Writes array of characters to the stream.
Definition stream.hpp:402
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:625
size_t readln(std::basic_string< _Elem, _Traits, _Ax > &str)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:306
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:465
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:611
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:638
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
size_t readln(std::basic_string< T_to, _Traits, _Ax > &str, charset_encoder< T_from, T_to > &encoder)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:318
size_t readln_and_attach(std::basic_string< T_to, _Traits, _Ax > &str, 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:355
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:597
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:334
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:370
basic & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:497
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
void write_charset(charset_id charset)
Writes UTF8, UTF-16 or UTF-32 byte-order-mark.
Definition stream.hpp:582
size_t write_array(const std::basic_string< T_from, _Traits, _Ax > &str, charset_encoder< T_from, T_to > &encoder)
Writes array of characters to the stream.
Definition stream.hpp:443
basic & write_str(const std::basic_string< _Elem, _Traits, _Ax > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:522
basic & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:287
fsize_t write_stream(basic &stream, fsize_t amount=fsize_max)
Writes content of another stream.
Definition stream.hpp:557
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 *str, size_t num_chars, charset_encoder< T_from, T_to > &encoder)
Writes array of characters to the stream.
Definition stream.hpp:423
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:388
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:1382
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1491
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:1412
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:1448
Buffered OS data stream (file, pipe, socket...)
Definition stream.hpp:2323
Cached file.
Definition stream.hpp:1792
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2064
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2043
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:1859
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2069
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2030
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:2078
virtual void close()
Closes the stream.
Definition stream.hpp:1978
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:2100
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2024
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1987
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:1925
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:2036
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:2087
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:2019
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:2092
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:1998
Cached file-system file.
Definition stream.hpp:3093
cached_file(const stdex::sstring &filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3123
void open(const stdex::sstring &filename, int mode)
Opens file.
Definition stream.hpp:3157
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:3136
cached_file(const schar_t *filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3109
Modifies data on the fly when reading from/writing to a source stream. Could also be used to modify r...
Definition stream.hpp:1010
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1061
virtual void close()
Closes the stream.
Definition stream.hpp:1055
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:1039
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:1047
Compares multiple files to perform the same.
Definition stream.hpp:4138
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:4285
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:4168
virtual void close()
Closes the stream.
Definition stream.hpp:4201
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:4273
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:4247
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:4260
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:4218
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:4235
virtual void flush()
Persists volatile element data.
Definition stream.hpp:4186
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:4145
In-memory FIFO queue.
Definition stream.hpp:4039
virtual void close()
Closes the stream.
Definition stream.hpp:4109
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:4086
size_t size() const
Returns total size of pending data in the queue.
Definition stream.hpp:4123
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:4058
Limits file reading/writing to a predefined window.
Definition stream.hpp:1682
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:1775
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1725
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:1738
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:1731
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:1705
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:1750
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:1691
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:1760
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:1744
virtual void close()
Closes the stream.
Definition stream.hpp:1719
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:1770
File-system file.
Definition stream.hpp:2689
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:2883
file(const stdex::sstring &filename, int mode)
Opens file.
Definition stream.hpp:2710
static bool readonly(const stdex::sstring &filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3082
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:2965
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2858
file(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2699
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:2979
static bool readonly(const stdex::schar_t *filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3064
static bool exists(const stdex::sstring &filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3052
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2951
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2718
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:3013
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:2993
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2835
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2901
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2941
void open(const stdex::sstring &filename, int mode)
Opens file.
Definition stream.hpp:2791
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2796
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:2817
static bool exists(const stdex::schar_t *filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3037
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1539
fsize_t read_limit
Number of bytes left that may be read from the stream.
Definition stream.hpp:1591
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:1547
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:1568
fsize_t write_limit
Number of bytes left, that can be written to the stream.
Definition stream.hpp:1592
In-memory file.
Definition stream.hpp:3175
memory_file & operator=(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3357
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:3875
memory_file(const schar_t *filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3255
memory_file & write_str(const std::basic_string< _Elem, _Traits, _Ax > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3754
memory_file(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3273
size_t m_size
file size
Definition stream.hpp:4026
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:3964
size_t write_stream(basic &stream, size_t amount=SIZE_MAX)
Writes content of another stream.
Definition stream.hpp:3789
uint8_t * m_data
file data
Definition stream.hpp:4023
memory_file & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:3543
virtual void close()
Closes the stream.
Definition stream.hpp:3839
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:3504
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:3870
size_t m_reserved
reserved file size
Definition stream.hpp:4027
memory_file(size_t size, state_t state=state_t::ok)
Creates an empty file of reserved size.
Definition stream.hpp:3196
void reserve(size_t required, bool tight=false) noexcept
Reallocates memory.
Definition stream.hpp:3395
memory_file(const stdex::sstring &filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3266
memory_file & read_str(std::basic_string< _Elem, _Traits, _Ax > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:3583
memory_file(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3328
void write_byte(uint8_t byte, size_t amount=1)
Writes a byte of data.
Definition stream.hpp:3638
memory_file & operator=(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3298
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:3930
void load(const stdex::sstring &filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3457
size_t m_offset
file pointer
Definition stream.hpp:4025
void save(const schar_t *filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3468
void load(const schar_t *filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3424
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3853
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:3880
memory_file & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:3671
memory_file & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3709
void save(const stdex::sstring &filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3494
bool m_manage
may reallocate m_data?
Definition stream.hpp:4024
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:3245
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:3614
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:3222
const void * data() const
Returns pointer to data.
Definition stream.hpp:3502
Definition stream.hpp:1158
enum stdex::stream::replicator::worker::op_t op
Operation to perform.
size_t num_written
Number of bytes written.
Definition stream.hpp:1207
size_t length
Byte limit of data to write.
Definition stream.hpp:1206
const void * data
Data to write.
Definition stream.hpp:1205
Replicates writing of the same data to multiple streams.
Definition stream.hpp:1075
void push_back(basic *source)
Adds stream on the list.
Definition stream.hpp:1094
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1151
void remove(basic *source)
Removes stream from the list.
Definition stream.hpp:1102
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:1119
virtual void close()
Closes the stream.
Definition stream.hpp:1146
Socket stream.
Definition stream.hpp:2345
socket_t get() const noexcept
Returns socket handle.
Definition stream.hpp:2401
virtual void close()
Closes the stream.
Definition stream.hpp:2447
socket(int af, int type, int protocol)
Creates a socket.
Definition stream.hpp:2380
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:2403
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:2427
Limits reading from/writing to stream to a predefined window.
Definition stream.hpp:1599
fpos_t write_offset
Number of bytes to discard on write.
Definition stream.hpp:1675
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:1636
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:1607
fpos_t read_offset
Number of bytes to skip on read.
Definition stream.hpp:1674
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:1516
Definition stream.hpp:4127