stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
stream.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2023-2024 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 T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
306 size_t readln(_Inout_ std::basic_string<T, TR, AX>& str)
307 {
308 str.clear();
309 return readln_and_attach(str);
310 }
311
317 template<class T_from, class T_to, class TR = std::char_traits<T_to>, class AX = std::allocator<T_to>>
318 size_t readln(_Inout_ std::basic_string<T_to, TR, 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 T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
334 size_t readln_and_attach(_Inout_ std::basic_string<T, TR, AX>& str)
335 {
336 bool initial = true;
337 T chr, previous = (T)0;
338 do {
339 read_array(&chr, sizeof(T), 1);
340 if (!initial && !(previous == static_cast<T>('\r') && chr == static_cast<T>('\n')))
341 str += previous;
342 else
343 initial = false;
344 previous = chr;
345 } while (ok() && chr != static_cast<T>('\n'));
346 return str.size();
347 }
348
354 template<class T_from, class T_to, class TR = std::char_traits<T_to>, class AX = std::allocator<T_to>>
355 size_t readln_and_attach(_Inout_ std::basic_string<T_to, TR, 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 TR = std::char_traits<T_from>, class AX = std::allocator<T_from>>
443 size_t write_array(_In_ const std::basic_string<T_from, TR, 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 T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
465 basic& read_str(_Out_ std::basic_string<T, TR, 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 T buf[0x400];
477 uint32_t num_read = static_cast<uint32_t>(read_array(buf, sizeof(T), 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 T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
522 basic& write_str(_In_ const std::basic_string<T, TR, 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(T), num_chars);
532 return *this;
533 }
534
535#ifdef _WIN32
541 size_t write_sa(_In_ LPSAFEARRAY sa)
542 {
543 long ubound, lbound;
544 if (FAILED(SafeArrayGetUBound(sa, 1, &ubound)) ||
545 FAILED(SafeArrayGetLBound(sa, 1, &lbound)))
546 throw std::invalid_argument("SafeArrayGet[UL]Bound failed");
547 safearray_accessor<void> a(sa);
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 T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
673 basic& operator >>(_Out_ std::basic_string<T, TR, AX>& data) { return read_str(data); }
674 template <class T>
675 basic& operator <<(_In_ const T* data) { return write_str(data); }
676 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
677 basic& operator <<(_In_ const std::basic_string<T, TR, AX>& data) { return write_str(data); }
678
679 template <class T, class AX = std::allocator<T>>
680 basic& operator <<(_In_ const std::vector<T, AX>& 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 T, class AX = std::allocator<T>>
692 basic& operator >>(_Out_ std::vector<T, AX>& 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 T el;
702 *this >> el;
703 if (!ok()) _Unlikely_
704 return *this;
705 data.push_back(std::move(el));
706 }
707 }
708
709 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
710 basic& operator <<(_In_ const std::set<KEY, PR, AX>& 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 KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
722 basic& operator >>(_Out_ std::set<KEY, PR, AX>& 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 KEY el;
731 *this >> el;
732 if (!ok()) _Unlikely_
733 return *this;
734 data.insert(std::move(el));
735 }
736 }
737
738 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
739 basic& operator <<(_In_ const std::multiset<KEY, PR, AX>& 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 KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
751 basic& operator >>(_Out_ std::multiset<KEY, PR, AX>& 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 KEY 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 if (size() > ULONG_MAX)
958 throw std::range_error("data too big");
959 ULONG length = static_cast<ULONG>(size());
960 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(SafeArrayCreateVector(VT_UI1, 0, length));
961 if (!sa) _Unlikely_
962 throw std::runtime_error("SafeArrayCreateVector failed");
963 if (seek(0) != 0) _Unlikely_
964 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
965 safearray_accessor<void> a(sa.get());
966 if (read_array(a.data(), 1, length) != length)
967 throw std::system_error(sys_error(), std::system_category(), "failed to read");
968 return sa.release();
969 }
970#endif
971
977 charset_id read_charset(_In_ charset_id default_charset = charset_id::system)
978 {
979 if (seek(0) != 0) _Unlikely_
980 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
981 char32_t id_utf32;
982 read_array(&id_utf32, sizeof(char32_t), 1);
983 if (ok() && id_utf32 == utf32_bom)
984 return charset_id::utf32;
985
986 if (seek(0) != 0) _Unlikely_
987 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
988 char16_t id_utf16;
989 read_array(&id_utf16, sizeof(char16_t), 1);
990 if (ok() && id_utf16 == utf16_bom)
991 return charset_id::utf16;
992
993 if (seek(0) != 0) _Unlikely_
994 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
995 char id_utf8[3] = { 0 };
996 read_array(id_utf8, sizeof(id_utf8), 1);
997 if (ok() && strncmp(id_utf8, _countof(id_utf8), utf8_bom, _countof(utf8_bom)) == 0)
998 return charset_id::utf8;
999
1000 if (seek(0) != 0) _Unlikely_
1001 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1002 return default_charset;
1003 }
1004 };
1005
1011 class converter : public basic
1012 {
1013 protected:
1015#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1016 explicit converter() : basic(state_t::fail) {}
1017
1018 void init(_Inout_ basic& source)
1019 {
1020 m_source = &source;
1021 init();
1022 }
1023
1024 void init()
1025 {
1026 m_state = m_source->state();
1027 }
1028
1029 void done()
1030 {
1031 m_source = nullptr;
1032 }
1034
1035 public:
1036 converter(_Inout_ basic& source) :
1037 basic(source.state()),
1038 m_source(&source)
1039 {}
1040
1041 virtual _Success_(return != 0 || length == 0) size_t read(
1042 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1043 {
1044 size_t num_read = m_source->read(data, length);
1045 m_state = m_source->state();
1046 return num_read;
1047 }
1048
1049 virtual _Success_(return != 0) size_t write(
1050 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1051 {
1052 size_t num_written = m_source->write(data, length);
1053 m_state = m_source->state();
1054 return num_written;
1055 }
1056
1057 virtual void close()
1058 {
1059 m_source->close();
1060 m_state = m_source->state();
1061 }
1062
1063 virtual void flush()
1064 {
1065 m_source->flush();
1066 m_state = m_source->state();
1067 }
1068
1069 protected:
1070 basic* m_source;
1071 };
1072
1076 class replicator : public basic
1077 {
1078 public:
1079 virtual ~replicator()
1080 {
1081 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1082 auto _w = w->get();
1083 {
1084 const std::lock_guard<std::mutex> lk(_w->mutex);
1085 _w->op = worker::op_t::quit;
1086 }
1087 _w->cv.notify_one();
1088 }
1089 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w)
1090 w->get()->join();
1091 }
1092
1096 void push_back(_In_ basic* source)
1097 {
1098 m_workers.push_back(std::unique_ptr<worker>(new worker(source)));
1099 }
1100
1104 void remove(basic* source)
1105 {
1106 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1107 auto _w = w->get();
1108 if (_w->source == source) {
1109 {
1110 const std::lock_guard<std::mutex> lk(_w->mutex);
1111 _w->op = worker::op_t::quit;
1112 }
1113 _w->cv.notify_one();
1114 _w->join();
1115 m_workers.erase(w);
1116 return;
1117 }
1118 }
1119 }
1120
1121 virtual _Success_(return != 0) size_t write(
1122 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1123 {
1124 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1125 auto _w = w->get();
1126 {
1127 const std::lock_guard<std::mutex> lk(_w->mutex);
1128 _w->op = worker::op_t::write;
1129 _w->data = data;
1130 _w->length = length;
1131 }
1132 _w->cv.notify_one();
1133 }
1134 size_t num_written = length;
1135 m_state = state_t::ok;
1136 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1137 auto _w = w->get();
1138 std::unique_lock<std::mutex> lk(_w->mutex);
1139 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1140 if (_w->num_written < num_written)
1141 num_written = _w->num_written;
1142 if (ok() && !_w->source->ok())
1143 m_state = _w->source->state();
1144 }
1145 return num_written;
1146 }
1147
1148 virtual void close()
1149 {
1150 foreach_worker(worker::op_t::close);
1151 }
1152
1153 virtual void flush()
1154 {
1155 foreach_worker(worker::op_t::flush);
1156 }
1157
1158 protected:
1159 class worker : public std::thread
1160 {
1161 public:
1162 worker(_In_ basic* _source) :
1163 source(_source),
1164 op(op_t::noop),
1165 data(nullptr),
1166 length(0),
1167 num_written(0)
1168 {
1169 *static_cast<std::thread*>(this) = std::thread([](_Inout_ worker& w) { w.process_op(); }, std::ref(*this));
1170 }
1171
1172 protected:
1173 void process_op()
1174 {
1175 for (;;) {
1176 std::unique_lock<std::mutex> lk(mutex);
1177 cv.wait(lk, [&] {return op != op_t::noop; });
1178 switch (op) {
1179 case op_t::quit:
1180 return;
1181 case op_t::write:
1182 num_written = source->write(data, length);
1183 break;
1184 case op_t::close:
1185 source->close();
1186 break;
1187 case op_t::flush:
1188 source->flush();
1189 break;
1190 case op_t::noop:;
1191 }
1192 op = op_t::noop;
1193 lk.unlock();
1194 cv.notify_one();
1195 }
1196 }
1197
1198 public:
1199 basic* source;
1200 enum class op_t {
1201 noop = 0,
1202 quit,
1203 write,
1204 close,
1205 flush,
1206 } op;
1207 const void* data;
1208 size_t length;
1210 std::mutex mutex;
1211 std::condition_variable cv;
1212 };
1213
1214 void foreach_worker(_In_ worker::op_t op)
1215 {
1216 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1217 auto _w = w->get();
1218 {
1219 const std::lock_guard<std::mutex> lk(_w->mutex);
1220 _w->op = op;
1221 }
1222 _w->cv.notify_one();
1223 }
1224 m_state = state_t::ok;
1225 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1226 auto _w = w->get();
1227 std::unique_lock<std::mutex> lk(_w->mutex);
1228 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1229 if (ok())
1230 m_state = _w->source->state();
1231 }
1232 }
1233
1234 std::list<std::unique_ptr<worker>> m_workers;
1235 };
1236
1237 constexpr size_t default_async_limit = 0x100000;
1238
1244 template <size_t N_cap = default_async_limit>
1246 {
1247 public:
1248 async_reader(_Inout_ basic& source) :
1249 converter(source),
1250 m_worker([](_Inout_ async_reader& w) { w.process(); }, std::ref(*this))
1251 {}
1252
1253 virtual ~async_reader()
1254 {
1255 m_ring.quit();
1256 m_worker.join();
1257 }
1258
1259#pragma warning(suppress: 6101) // See [1] below
1260 virtual _Success_(return != 0 || length == 0) size_t read(
1261 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1262 {
1263 _Assume_(data || !length);
1264 for (size_t to_read = length;;) {
1265 uint8_t* ptr; size_t num_read;
1266 std::tie(ptr, num_read) = m_ring.front();
1267 if (!ptr) _Unlikely_ {
1268 m_state = to_read < length || !length ? state_t::ok : m_source->state();
1269 return length - to_read; // [1] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
1270 }
1271 if (to_read < num_read)
1272 num_read = to_read;
1273 memcpy(data, ptr, num_read);
1274 m_ring.pop(num_read);
1275 to_read -= num_read;
1276 if (!to_read) {
1277 m_state = state_t::ok;
1278 return length;
1279 }
1280 reinterpret_cast<uint8_t*&>(data) += num_read;
1281 }
1282 }
1283
1284 protected:
1285 void process()
1286 {
1287 for (;;) {
1288 uint8_t* ptr; size_t num_write;
1289 std::tie(ptr, num_write) = m_ring.back();
1290 if (!ptr) _Unlikely_
1291 break;
1292 num_write = m_source->read(ptr, num_write);
1293 m_ring.push(num_write);
1294 if (!m_source->ok()) {
1295 m_ring.quit();
1296 break;
1297 }
1298 }
1299 }
1300
1301 protected:
1302 ring<uint8_t, N_cap> m_ring;
1303 std::thread m_worker;
1304 };
1305
1311 template <size_t N_cap = default_async_limit>
1313 {
1314 public:
1315 async_writer(_Inout_ basic& source) :
1316 converter(source),
1317 m_worker([](_Inout_ async_writer& w) { w.process(); }, std::ref(*this))
1318 {}
1319
1320 virtual ~async_writer()
1321 {
1322 m_ring.quit();
1323 m_worker.join();
1324 }
1325
1326 virtual _Success_(return != 0) size_t write(
1327 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1328 {
1329 _Assume_(data || !length);
1330 for (size_t to_write = length;;) {
1331 uint8_t* ptr; size_t num_write;
1332 std::tie(ptr, num_write) = m_ring.back();
1333 if (!ptr) _Unlikely_ {
1334 m_state = state_t::fail;
1335 return length - to_write;
1336 }
1337 if (to_write < num_write)
1338 num_write = to_write;
1339 memcpy(ptr, data, num_write);
1340 m_ring.push(num_write);
1341 to_write -= num_write;
1342 if (!to_write) {
1343 m_state = state_t::ok;
1344 return length;
1345 }
1346 reinterpret_cast<const uint8_t*&>(data) += num_write;
1347 }
1348 }
1349
1350 virtual void flush()
1351 {
1352 m_ring.sync();
1354 }
1355
1356 protected:
1357 void process()
1358 {
1359 for (;;) {
1360 uint8_t* ptr; size_t num_read;
1361 std::tie(ptr, num_read) = m_ring.front();
1362 if (!ptr)
1363 break;
1364 num_read = m_source->write(ptr, num_read);
1365 m_ring.pop(num_read);
1366 if (!m_source->ok()) {
1367 m_ring.quit();
1368 break;
1369 }
1370 }
1371 }
1372
1373 protected:
1374 ring<uint8_t, N_cap> m_ring;
1375 std::thread m_worker;
1376 };
1377
1378 constexpr size_t default_buffer_size = 0x400;
1379
1383 class buffer : public converter
1384 {
1385 protected:
1387 explicit buffer(_In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1388 converter(),
1389 m_read_buffer(read_buffer_size),
1390 m_write_buffer(write_buffer_size)
1391 {}
1392
1393 void done()
1394 {
1395 if (m_source)
1396 flush_write();
1397 converter::done();
1398 }
1400
1401 public:
1402 buffer(_Inout_ basic& source, _In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1403 converter(source),
1404 m_read_buffer(read_buffer_size),
1405 m_write_buffer(write_buffer_size)
1406 {}
1407
1408 virtual ~buffer()
1409 {
1410 if (m_source)
1411 flush_write();
1412 }
1413
1414 virtual _Success_(return != 0 || length == 0) size_t read(
1415 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1416 {
1417 _Assume_(data || !length);
1418 for (size_t to_read = length;;) {
1419 size_t buffer_size = m_read_buffer.tail - m_read_buffer.head;
1420 if (to_read <= buffer_size) {
1421 memcpy(data, m_read_buffer.data + m_read_buffer.head, to_read);
1422 m_read_buffer.head += to_read;
1423 m_state = state_t::ok;
1424 return length;
1425 }
1426 if (buffer_size) {
1427 memcpy(data, m_read_buffer.data + m_read_buffer.head, buffer_size);
1428 reinterpret_cast<uint8_t*&>(data) += buffer_size;
1429 to_read -= buffer_size;
1430 }
1431 m_read_buffer.head = 0;
1432 if (to_read > m_read_buffer.capacity) {
1433 // When needing to read more data than buffer capacity, bypass the buffer.
1434 m_read_buffer.tail = 0;
1435 to_read -= m_source->read(data, to_read);
1436 m_state = to_read < length ? state_t::ok : m_source->state();
1437 return length - to_read;
1438 }
1439 m_read_buffer.tail = m_source->read(m_read_buffer.data, m_read_buffer.capacity);
1440 if (m_read_buffer.tail < m_read_buffer.capacity && m_read_buffer.tail < to_read) _Unlikely_ {
1441 memcpy(data, m_read_buffer.data, m_read_buffer.tail);
1442 m_read_buffer.head = m_read_buffer.tail;
1443 to_read -= m_read_buffer.tail;
1444 m_state = to_read < length ? state_t::ok : m_source->state();
1445 return length - to_read;
1446 }
1447 }
1448 }
1449
1450 virtual _Success_(return != 0) size_t write(
1451 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1452 {
1453 _Assume_(data || !length);
1454 if (!length) _Unlikely_ {
1455 // Pass null writes (zero-byte length). Null write operations have special meaning with with Windows pipes.
1456 flush_write();
1457 if (!ok()) _Unlikely_
1458 return 0;
1459 converter::write(nullptr, 0);
1460 return 0;
1461 }
1462
1463 for (size_t to_write = length;;) {
1464 size_t available_buffer = m_write_buffer.capacity - m_write_buffer.tail;
1465 if (to_write <= available_buffer) {
1466 memcpy(m_write_buffer.data + m_write_buffer.tail, data, to_write);
1467 m_write_buffer.tail += to_write;
1468 m_state = state_t::ok;
1469 return length;
1470 }
1471 if (available_buffer) {
1472 memcpy(m_write_buffer.data + m_write_buffer.tail, data, available_buffer);
1473 reinterpret_cast<const uint8_t*&>(data) += available_buffer;
1474 to_write -= available_buffer;
1475 m_write_buffer.tail += available_buffer;
1476 }
1477 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1478 if (buffer_size) {
1479 m_write_buffer.head += converter::write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1480 if (m_write_buffer.head == m_write_buffer.tail)
1481 m_write_buffer.head = m_write_buffer.tail = 0;
1482 else
1483 return length - to_write;
1484 }
1485 if (to_write > m_write_buffer.capacity) {
1486 // When needing to write more data than buffer capacity, bypass the buffer.
1487 to_write -= converter::write(data, to_write);
1488 return length - to_write;
1489 }
1490 }
1491 }
1492
1493 virtual void flush()
1494 {
1495 flush_write();
1496 if (ok())
1498 }
1499
1500 protected:
1501 void flush_write()
1502 {
1503 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1504 if (buffer_size) {
1505 m_write_buffer.head += m_source->write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1506 if (m_write_buffer.head == m_write_buffer.tail) {
1507 m_write_buffer.head = 0;
1508 m_write_buffer.tail = 0;
1509 }
1510 else {
1511 m_state = m_source->state();
1512 return;
1513 }
1514 }
1515 m_state = state_t::ok;
1516 }
1517
1518 struct buffer_t {
1519 uint8_t* data;
1520 size_t head, tail, capacity;
1521
1522 buffer_t(_In_ size_t buffer_size) :
1523 head(0),
1524 tail(0),
1525 capacity(buffer_size),
1526 data(buffer_size ? new uint8_t[buffer_size] : nullptr)
1527 {}
1528
1529 ~buffer_t()
1530 {
1531 if (data)
1532 delete[] data;
1533 }
1534 } m_read_buffer, m_write_buffer;
1535 };
1536
1540 class limiter : public converter
1541 {
1542 public:
1543 limiter(_Inout_ basic& source, _In_ fsize_t _read_limit = 0, _In_ fsize_t _write_limit = 0) :
1544 converter(source),
1545 read_limit(_read_limit),
1546 write_limit(_write_limit)
1547 {}
1548
1549 virtual _Success_(return != 0 || length == 0) size_t read(
1550 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1551 {
1552 size_t num_read;
1553 if (read_limit == fsize_max)
1554 num_read = converter::read(data, length);
1555 else if (length <= read_limit) {
1556 num_read = converter::read(data, length);
1557 read_limit -= num_read;
1558 }
1559 else if (length && !read_limit) {
1560 num_read = 0;
1561 m_state = state_t::eof;
1562 }
1563 else {
1564 num_read = converter::read(data, static_cast<size_t>(read_limit));
1565 read_limit -= num_read;
1566 }
1567 return num_read;
1568 }
1569
1570 virtual _Success_(return != 0) size_t write(
1571 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1572 {
1573 size_t num_written;
1574 if (write_limit == fsize_max)
1575 num_written = converter::write(data, length);
1576 else if (length <= write_limit) {
1577 num_written = converter::write(data, length);
1578 write_limit -= num_written;
1579 }
1580 else if (length && !write_limit) {
1581 num_written = 0;
1582 m_state = state_t::fail;
1583 }
1584 else {
1585 num_written = converter::write(data, static_cast<size_t>(write_limit));
1586 write_limit -= num_written;
1587 }
1588 return num_written;
1589 }
1590
1591 public:
1592 fsize_t
1595 };
1596
1600 class window : public limiter
1601 {
1602 public:
1603 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) :
1604 limiter(source, read_limit, write_limit),
1605 read_offset(_read_offset),
1606 write_offset(_write_offset)
1607 {}
1608
1609 virtual _Success_(return != 0 || length == 0) size_t read(
1610 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1611 {
1612 if (read_offset) {
1613 m_source->skip(read_offset);
1614 m_state = m_source->state();
1615 if (!ok()) _Unlikely_
1616 return 0;
1617 read_offset = 0;
1618 }
1619 size_t num_read;
1620 if (read_limit == fsize_max)
1621 num_read = converter::read(data, length);
1622 else if (length <= read_limit) {
1623 num_read = converter::read(data, length);
1624 read_limit -= num_read;
1625 }
1626 else if (length && !read_limit) {
1627 num_read = 0;
1628 m_source->skip(length);
1629 m_state = state_t::eof;
1630 }
1631 else {
1632 num_read = converter::read(data, static_cast<size_t>(read_limit));
1633 read_limit -= num_read;
1634 }
1635 return num_read;
1636 }
1637
1638 virtual _Success_(return != 0) size_t write(
1639 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1640 {
1641 size_t num_skipped, num_written;
1642 if (length <= write_offset) {
1643 write_offset -= length;
1644 m_state = state_t::ok;
1645 return length;
1646 }
1647 if (write_offset) {
1648 reinterpret_cast<const uint8_t*&>(data) += static_cast<size_t>(write_offset);
1649 length -= static_cast<size_t>(write_offset);
1650 num_skipped = static_cast<size_t>(write_offset);
1651 write_offset = 0;
1652 }
1653 else
1654 num_skipped = 0;
1655 if (write_limit == fsize_max)
1656 num_written = converter::write(data, length);
1657 else if (length <= write_limit) {
1658 num_written = converter::write(data, length);
1659 write_limit -= num_written;
1660 }
1661 else if (length && !write_limit) {
1662 num_skipped += length;
1663 num_written = 0;
1664 m_state = state_t::ok;
1665 }
1666 else {
1667 num_skipped += length - static_cast<size_t>(write_limit);
1668 num_written = converter::write(data, static_cast<size_t>(write_limit));
1669 write_limit -= num_written;
1670 }
1671 return num_skipped + num_written;
1672 }
1673
1674 public:
1675 fpos_t
1678 };
1679
1684 {
1685 public:
1686 file_window(_Inout_ basic_file& source, fpos_t offset = 0, fsize_t length = 0) :
1687 basic(source.state()),
1688 m_source(source),
1689 m_offset(source.tell()),
1690 m_region(offset, offset + length)
1691 {}
1692
1693 virtual _Success_(return != 0 || length == 0) size_t read(
1694 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1695 {
1696 _Assume_(data || !length);
1697 if (m_region.contains(m_offset)) {
1698 size_t num_read = m_source.read(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1699 m_state = m_source.state();
1700 m_offset += num_read;
1701 return num_read;
1702 }
1703 m_state = length ? state_t::eof : state_t::ok;
1704 return 0;
1705 }
1706
1707 virtual _Success_(return != 0) size_t write(
1708 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1709 {
1710 _Assume_(data || !length);
1711 if (m_region.contains(m_offset)) {
1712 size_t num_written = m_source.write(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1713 m_state = m_source.state();
1714 m_offset += num_written;
1715 return num_written;
1716 }
1717 m_state = state_t::fail;
1718 return 0;
1719 }
1720
1721 virtual void close()
1722 {
1723 m_source.close();
1724 m_state = m_source.state();
1725 }
1726
1727 virtual void flush()
1728 {
1729 m_source.flush();
1730 m_state = m_source.state();
1731 }
1732
1733 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
1734 {
1735 m_offset = m_source.seek(offset, how);
1736 m_state = m_source.state();
1737 return ok() ? m_offset - m_region.start : fpos_max;
1738 }
1739
1740 virtual void skip(_In_ fsize_t amount)
1741 {
1742 m_source.skip(amount);
1743 m_state = m_source.state();
1744 }
1745
1746 virtual fpos_t tell() const
1747 {
1748 fpos_t offset = m_source.tell();
1749 return m_region.contains(offset) ? offset - m_region.start : fpos_max;
1750 }
1751
1752 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
1753 {
1754 if (m_region.contains(offset)) {
1755 m_source.lock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1756 m_state = m_source.state();
1757 }
1758 else
1759 m_state = state_t::fail;
1760 }
1761
1762 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
1763 {
1764 if (m_region.contains(offset)) {
1765 m_source.unlock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1766 m_state = m_source.state();
1767 }
1768 else
1769 m_state = state_t::fail;
1770 }
1771
1772 virtual fsize_t size() const
1773 {
1774 return m_region.size();
1775 }
1776
1777 virtual void truncate()
1778 {
1779 m_state = state_t::fail;
1780 }
1781
1782 protected:
1783 basic_file& m_source;
1784 fpos_t m_offset;
1785 interval<fpos_t> m_region;
1786 };
1787
1788 constexpr size_t default_cache_size = 0x1000;
1789
1793 class cache : public basic_file
1794 {
1795 protected:
1797#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1798 explicit cache(_In_ size_t cache_size = default_cache_size) :
1799 basic(state_t::fail),
1800 m_cache(cache_size)
1801 {}
1802
1803 void init(_Inout_ basic_file& source)
1804 {
1805 m_source = &source;
1806 init();
1807 }
1808
1809 void init()
1810 {
1811 m_state = m_source->state();
1812 m_offset = m_source->tell();
1813#if SET_FILE_OP_TIMES
1814 m_atime = m_source->atime();
1815 m_mtime = m_source->mtime();
1816#endif
1817 }
1818
1819 void done()
1820 {
1821 if (m_source) {
1822 flush_cache();
1823 if (!ok()) _Unlikely_
1824 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occured
1825 m_source->seek(m_offset);
1826#if SET_FILE_OP_TIMES
1827 m_source->set_atime(m_atime);
1828 m_source->set_mtime(m_mtime);
1829#endif
1830 m_source = nullptr;
1831 }
1832 }
1834
1835 public:
1836 cache(_Inout_ basic_file& source, _In_ size_t cache_size = default_cache_size) :
1837 basic(source.state()),
1838 m_source(&source),
1839 m_cache(cache_size),
1840 m_offset(source.tell())
1841#if SET_FILE_OP_TIMES
1842 , m_atime(source.atime())
1843 , m_mtime(source.mtime())
1844#endif
1845 {}
1846
1847 virtual ~cache() noexcept(false)
1848 {
1849 if (m_source) {
1850 flush_cache();
1851 if (!ok()) _Unlikely_
1852 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occured
1853 m_source->seek(m_offset);
1854#if SET_FILE_OP_TIMES
1855 m_source->set_atime(m_atime);
1856 m_source->set_mtime(m_mtime);
1857#endif
1858 }
1859 }
1860
1861 virtual _Success_(return != 0 || length == 0) size_t read(
1862 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1863 {
1864 _Assume_(data || !length);
1865#if SET_FILE_OP_TIMES
1866 m_atime = time_point::now();
1867#endif
1868 for (size_t to_read = length;;) {
1869 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1870 if (m_cache.region.contains(m_offset)) {
1871 size_t remaining_cache = static_cast<size_t>(m_cache.region.end - m_offset);
1872 if (to_read <= remaining_cache) {
1873 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), to_read);
1874 m_offset += to_read;
1875 m_state = state_t::ok;
1876 return length;
1877 }
1878 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), remaining_cache);
1879 reinterpret_cast<uint8_t*&>(data) += remaining_cache;
1880 to_read -= remaining_cache;
1881 m_offset += remaining_cache;
1882 }
1883 flush_cache();
1884 if (!ok()) _Unlikely_ {
1885 if (to_read < length)
1886 m_state = state_t::ok;
1887 return length - to_read;
1888 }
1889 }
1890 {
1891 fpos_t end_max = m_offset + to_read;
1892 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1893 // Read spans multiple cache blocks. Bypass cache to the last block.
1894 m_source->seek(m_offset);
1895 if (!m_source->ok()) _Unlikely_ {
1896 m_state = to_read < length ? state_t::ok : state_t::fail;
1897 return length - to_read;
1898 }
1899 size_t num_read = m_source->read(data, to_read - static_cast<size_t>(end_max % m_cache.capacity));
1900 m_offset += num_read;
1901 to_read -= num_read;
1902 if (!to_read) {
1903 m_state = state_t::ok;
1904 return length;
1905 }
1906 reinterpret_cast<uint8_t*&>(data) += num_read;
1907 m_state = m_source->state();
1908 if (!ok()) {
1909 if (to_read < length)
1910 m_state = state_t::ok;
1911 return length - to_read;
1912 }
1913 }
1914 }
1915 load_cache(m_offset);
1916 if (!ok()) _Unlikely_ {
1917 m_state = to_read < length ? state_t::ok : state_t::fail;
1918 return length - to_read;
1919 }
1920 if (m_cache.region.end <= m_offset) _Unlikely_ {
1921 m_state = to_read < length ? state_t::ok : state_t::eof;
1922 return length - to_read;
1923 }
1924 }
1925 }
1926
1927 virtual _Success_(return != 0) size_t write(
1928 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1929 {
1930 _Assume_(data || !length);
1931#if SET_FILE_OP_TIMES
1932 m_atime = m_mtime = time_point::now();
1933#endif
1934 for (size_t to_write = length;;) {
1935 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1936 fpos_t end_max = m_cache.region.start + m_cache.capacity;
1937 if (m_cache.region.start <= m_offset && m_offset < end_max) {
1938 size_t remaining_cache = static_cast<size_t>(end_max - m_offset);
1939 if (to_write <= remaining_cache) {
1940 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, to_write);
1941 m_offset += to_write;
1942 m_cache.status = cache_t::cache_t::status_t::dirty;
1943 m_cache.region.end = std::max(m_cache.region.end, m_offset);
1944 m_state = state_t::ok;
1945 return length;
1946 }
1947 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, remaining_cache);
1948 reinterpret_cast<const uint8_t*&>(data) += remaining_cache;
1949 to_write -= remaining_cache;
1950 m_offset += remaining_cache;
1951 m_cache.status = cache_t::cache_t::status_t::dirty;
1952 m_cache.region.end = end_max;
1953 }
1954 flush_cache();
1955 if (!ok()) _Unlikely_
1956 return length - to_write;
1957 }
1958 {
1959 fpos_t end_max = m_offset + to_write;
1960 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1961 // Write spans multiple cache blocks. Bypass cache to the last block.
1962 m_source->seek(m_offset);
1963 if (!ok()) _Unlikely_
1964 return length - to_write;
1965 size_t num_written = m_source->write(data, to_write - static_cast<size_t>(end_max % m_cache.capacity));
1966 m_offset += num_written;
1967 m_state = m_source->state();
1968 to_write -= num_written;
1969 if (!to_write || !ok())
1970 return length - to_write;
1971 reinterpret_cast<const uint8_t*&>(data) += num_written;
1972 }
1973 }
1974 load_cache(m_offset);
1975 if (!ok()) _Unlikely_
1976 return length - to_write;
1977 }
1978 }
1979
1980 virtual void close()
1981 {
1982 invalidate_cache();
1983 if (!ok()) _Unlikely_
1984 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occured
1985 m_source->close();
1986 m_state = m_source->state();
1987 }
1988
1989 virtual void flush()
1990 {
1991#if SET_FILE_OP_TIMES
1992 m_atime = m_mtime = time_point::min();
1993#endif
1994 flush_cache();
1995 if (!ok()) _Unlikely_
1996 return;
1997 m_source->flush();
1998 }
1999
2000 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2001 {
2002 m_state = state_t::ok;
2003 switch (how) {
2004 case seek_t::beg:
2005 return m_offset = offset;
2006 case seek_t::cur:
2007 return m_offset += offset;
2008 case seek_t::end: {
2009 auto n = size();
2010 if (n == fsize_max) _Unlikely_{
2011 m_state = state_t::fail;
2012 return fpos_max;
2013 }
2014 return m_offset = n + offset;
2015 }
2016 default:
2017 throw std::invalid_argument("unknown seek origin");
2018 }
2019 }
2020
2021 virtual fpos_t tell() const
2022 {
2023 return m_offset;
2024 }
2025
2026 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2027 {
2028 m_source->lock(offset, length);
2029 m_state = m_source->state();
2030 }
2031
2032 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2033 {
2034 m_source->unlock(offset, length);
2035 m_state = m_source->state();
2036 }
2037
2038 virtual fsize_t size() const
2039 {
2040 return m_cache.status != cache_t::cache_t::status_t::empty ?
2041 std::max(m_source->size(), m_cache.region.end) :
2042 m_source->size();
2043 }
2044
2045 virtual void truncate()
2046 {
2047#if SET_FILE_OP_TIMES
2048 m_atime = m_mtime = time_point::now();
2049#endif
2050 m_source->seek(m_offset);
2051 if (m_cache.region.end <= m_offset) {
2052 // Truncation does not affect cache.
2053 }
2054 else if (m_cache.region.start <= m_offset) {
2055 // Truncation truncates cache.
2056 m_cache.region.end = m_offset;
2057 }
2058 else {
2059 // Truncation invalidates cache.
2060 m_cache.status = cache_t::cache_t::status_t::empty;
2061 }
2062 m_source->truncate();
2063 m_state = m_source->state();
2064 }
2065
2066 virtual time_point ctime() const
2067 {
2068 return m_source->ctime();
2069 }
2070
2071 virtual time_point atime() const
2072 {
2073#if SET_FILE_OP_TIMES
2074 return std::max(m_atime, m_source->atime());
2075#else
2076 return m_source->atime();
2077#endif
2078 }
2079
2080 virtual time_point mtime() const
2081 {
2082#if SET_FILE_OP_TIMES
2083 return std::max(m_mtime, m_source->mtime());
2084#else
2085 return m_source->mtime();
2086#endif
2087 }
2088
2089 virtual void set_ctime(time_point date)
2090 {
2091 m_source->set_ctime(date);
2092 }
2093
2094 virtual void set_atime(time_point date)
2095 {
2096#if SET_FILE_OP_TIMES
2097 m_atime = date;
2098#endif
2099 m_source->set_atime(date);
2100 }
2101
2102 virtual void set_mtime(time_point date)
2103 {
2104#if SET_FILE_OP_TIMES
2105 m_mtime = date;
2106#endif
2107 m_source->set_mtime(date);
2108 }
2109
2110 protected:
2112 void flush_cache()
2113 {
2114 if (m_cache.status != cache_t::cache_t::status_t::dirty)
2115 m_state = state_t::ok;
2116 else if (!m_cache.region.empty()) {
2117 write_cache();
2118 if (ok())
2119 m_cache.status = cache_t::cache_t::status_t::loaded;
2120 }
2121 else {
2122 m_state = state_t::ok;
2123 m_cache.status = cache_t::cache_t::status_t::loaded;
2124 }
2125 }
2126
2127 void invalidate_cache()
2128 {
2129 if (m_cache.status == cache_t::cache_t::status_t::dirty && !m_cache.region.empty()) {
2130 write_cache();
2131 if (!ok()) _Unlikely_
2132 return;
2133 } else
2134 m_state = state_t::ok;
2135 m_cache.status = cache_t::cache_t::status_t::empty;
2136 }
2137
2138 void load_cache(_In_ fpos_t start)
2139 {
2140 _Assume_(m_cache.status != cache_t::cache_t::status_t::dirty);
2141 start -= start % m_cache.capacity; // Align to cache block size.
2142 m_source->seek(m_cache.region.start = start);
2143 if (m_source->ok()) {
2144 m_cache.region.end = start + m_source->read(m_cache.data, m_cache.capacity);
2145 m_cache.status = cache_t::cache_t::status_t::loaded;
2146 m_state = state_t::ok; // Regardless the read failure, we still might have cached some data.
2147 }
2148 else
2149 m_state = state_t::fail;
2150 }
2151
2152 void write_cache()
2153 {
2154 _Assume_(m_cache.status == cache_t::cache_t::status_t::dirty);
2155 m_source->seek(m_cache.region.start);
2156 m_source->write(m_cache.data, static_cast<size_t>(m_cache.region.size()));
2157 m_state = m_source->state();
2158 }
2159
2160 basic_file* m_source;
2161 struct cache_t {
2162 uint8_t* data;
2163 size_t capacity;
2164 enum class status_t {
2165 empty = 0,
2166 loaded,
2167 dirty,
2168 } status;
2169 interval<fpos_t> region;
2170
2171 cache_t(_In_ size_t _capacity) :
2172 data(new uint8_t[_capacity]),
2173 capacity(_capacity),
2174 status(status_t::empty),
2175 region(0)
2176 {}
2177
2178 ~cache_t()
2179 {
2180 delete[] data;
2181 }
2182 } m_cache;
2183 fpos_t m_offset;
2184#if SET_FILE_OP_TIMES
2185 time_point
2186 m_atime,
2187 m_mtime;
2188#endif
2190 };
2191
2195 class basic_sys : virtual public basic, public sys_object
2196 {
2197 public:
2198 basic_sys(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) :
2199 basic(state),
2200 sys_object(h)
2201 {}
2202
2203 virtual _Success_(return != 0 || length == 0) size_t read(
2204 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2205 {
2206 _Assume_(data || !length);
2207 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2208 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2209 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2210 size_t
2211#if defined(_WIN64)
2212 block_size = 0x1F80000;
2213#elif defined(_WIN32)
2214 block_size = 0x3f00000;
2215#else
2216 block_size = SSIZE_MAX;
2217#endif
2218 for (size_t to_read = length;;) {
2219#ifdef _WIN32
2220 // ReadFile() might raise exception (e.g. STATUS_FILE_BAD_FORMAT/0xE0000002).
2221 BOOL succeeded;
2222 DWORD num_read;
2223 __try { succeeded = ReadFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_read, block_size)), &num_read, nullptr); }
2224 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_read = 0; }
2225 if (!succeeded && GetLastError() == ERROR_NO_SYSTEM_RESOURCES && block_size > default_block_size) _Unlikely_ {
2226 // Error "Insufficient system resources exist to complete the requested service." occurs
2227 // ocasionally, when attempting to read too much data at once (e.g. over \\TSClient).
2228 block_size = default_block_size;
2229 continue;
2230 }
2231 if (!succeeded) _Unlikely_
2232#else
2233 ssize_t num_read = ::read(m_h, data, static_cast<ssize_t>(std::min<size_t>(to_read, block_size)));
2234 if (num_read < 0) _Unlikely_
2235#endif
2236 {
2237 m_state = to_read < length ? state_t::ok : state_t::fail;
2238 return length - to_read;
2239 }
2240 if (!num_read) _Unlikely_ {
2241 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2242 return length - to_read;
2243 }
2244 to_read -= num_read;
2245 if (!to_read) {
2246 m_state = state_t::ok;
2247 return length;
2248 }
2249 reinterpret_cast<uint8_t*&>(data) += num_read;
2250 }
2251 }
2252
2253 virtual _Success_(return != 0) size_t write(
2254 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2255 {
2256 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2257 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2258 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2259 constexpr size_t
2260#if defined(_WIN64)
2261 block_size = 0x1F80000;
2262#elif defined(_WIN32)
2263 block_size = 0x3f00000;
2264#else
2265 block_size = SSIZE_MAX;
2266#endif
2267 for (size_t to_write = length;;) {
2268#ifdef _WIN32
2269 // ReadFile() might raise an exception. Be cautious with WriteFile() too.
2270 BOOL succeeded;
2271 DWORD num_written;
2272 __try { succeeded = WriteFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_write, block_size)), &num_written, nullptr); }
2273 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_written = 0; }
2274 to_write -= num_written;
2275 if (!to_write) {
2276 m_state = state_t::ok;
2277 return length;
2278 }
2279 reinterpret_cast<const uint8_t*&>(data) += num_written;
2280 if (!succeeded) _Unlikely_ {
2281 m_state = state_t::fail;
2282 return length - to_write;
2283 }
2284#else
2285 ssize_t num_written = ::write(m_h, data, static_cast<ssize_t>(std::min<size_t>(to_write, block_size)));
2286 if (num_written < 0) _Unlikely_ {
2287 m_state = state_t::fail;
2288 return length - to_write;
2289 }
2290 to_write -= num_written;
2291 if (!to_write) {
2292 m_state = state_t::ok;
2293 return length;
2294 }
2295 reinterpret_cast<const uint8_t*&>(data) += num_written;
2296#endif
2297 }
2298 }
2299
2300 virtual void close()
2301 {
2302 try {
2304 m_state = state_t::ok;
2305 }
2306 catch (...) {
2307 m_state = state_t::fail;
2308 }
2309 }
2310
2311 virtual void flush()
2312 {
2313#ifdef _WIN32
2314 m_state = FlushFileBuffers(m_h) ? state_t::ok : state_t::fail;
2315#else
2316 m_state = fsync(m_h) >= 0 ? state_t::ok : state_t::fail;
2317#endif
2318 }
2319 };
2320
2324 class buffered_sys : public buffer
2325 {
2326 public:
2327 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) :
2328 buffer(read_buffer_size, write_buffer_size),
2329 m_source(h)
2330 {
2331 init(m_source);
2332 }
2333
2334 virtual ~buffered_sys()
2335 {
2336 done();
2337 }
2338
2339 protected:
2340 basic_sys m_source;
2341 };
2342
2346 class socket : public basic
2347 {
2348 public:
2349 socket(_In_opt_ socket_t h = invalid_socket, _In_ state_t state = state_t::ok) :
2350 basic(state),
2351 m_h(h)
2352 {}
2353
2354 private:
2355 socket(_In_ const socket& other);
2356 socket& operator =(_In_ const socket& other);
2357
2358 public:
2359 socket(_Inout_ socket&& other) noexcept : m_h(other.m_h)
2360 {
2361 other.m_h = invalid_socket;
2362 }
2363
2364 socket& operator =(_Inout_ socket&& other) noexcept
2365 {
2366 if (this != std::addressof(other)) {
2367 if (m_h != invalid_socket)
2368 closesocket(m_h);
2369 m_h = other.m_h;
2370 other.m_h = invalid_socket;
2371 }
2372 return *this;
2373 }
2374
2382 socket(_In_ int af, _In_ int type, _In_ int protocol)
2383 {
2384 m_h = ::socket(af, type, protocol);
2385 if (m_h == invalid_socket) _Unlikely_
2386 m_state = state_t::fail;
2387 }
2388
2389 virtual ~socket()
2390 {
2391 if (m_h != invalid_socket)
2392 closesocket(m_h);
2393 }
2394
2398 operator bool() const noexcept { return m_h != invalid_socket; }
2399
2403 socket_t get() const noexcept { return m_h; }
2404
2405 virtual _Success_(return != 0 || length == 0) size_t read(
2406 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2407 {
2408 _Assume_(data || !length);
2409 constexpr int block_size = 0x10000000;
2410 for (size_t to_read = length;;) {
2411 int num_read = recv(m_h, reinterpret_cast<char*>(data), static_cast<int>(std::min<size_t>(to_read, block_size)), 0);
2412 if (num_read == SOCKET_ERROR) _Unlikely_ {
2413 m_state = to_read < length ? state_t::ok : state_t::fail;
2414 return length - to_read;
2415 }
2416 if (!num_read) {
2417 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2418 return length - to_read;
2419 }
2420 to_read -= num_read;
2421 if (!to_read) {
2422 m_state = state_t::ok;
2423 return length;
2424 }
2425 reinterpret_cast<uint8_t*&>(data) += num_read;
2426 }
2427 }
2428
2429 virtual _Success_(return != 0) size_t write(
2430 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2431 {
2432 _Assume_(data || !length);
2433 constexpr int block_size = 0x10000000;
2434 for (size_t to_write = length;;) {
2435 int num_written = send(m_h, reinterpret_cast<const char*>(data), static_cast<int>(std::min<size_t>(to_write, block_size)), 0);
2436 if (num_written == SOCKET_ERROR) _Unlikely_ {
2437 m_state = state_t::fail;
2438 return length - to_write;
2439 }
2440 to_write -= num_written;
2441 if (!to_write) {
2442 m_state = state_t::ok;
2443 return length;
2444 }
2445 reinterpret_cast<const uint8_t*&>(data) += num_written;
2446 }
2447 }
2448
2449 virtual void close()
2450 {
2451 if (m_h != invalid_socket) {
2452 closesocket(m_h);
2453 m_h = invalid_socket;
2454 }
2455 m_state = state_t::ok;
2456 }
2457
2458 protected:
2459 socket_t m_h;
2460 };
2461
2462#ifdef _WIN32
2466 class sequential_stream : public basic
2467 {
2468 public:
2469 sequential_stream(_In_ ISequentialStream* source) : m_source(source)
2470 {
2471 m_source->AddRef();
2472 }
2473
2474 virtual ~sequential_stream()
2475 {
2476 m_source->Release();
2477 }
2478
2479 virtual _Success_(return != 0 || length == 0) size_t read(
2480 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2481 {
2482 _Assume_(data || !length);
2483 for (size_t to_read = length;;) {
2484 HRESULT hr;
2485 ULONG num_read = 0;
2486 __try { hr = m_source->Read(data, (ULONG)std::min<size_t>(to_read, ULONG_MAX), &num_read); }
2487 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2488 if (FAILED(hr)) _Unlikely_ {
2489 m_state = to_read < length ? state_t::ok : state_t::fail;
2490 return length - to_read;
2491 }
2492 to_read -= num_read;
2493 if (hr == S_FALSE) _Unlikely_ {
2494 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2495 return length - to_read;
2496 }
2497 if (!to_read) {
2498 m_state = state_t::ok;
2499 return length;
2500 }
2501 reinterpret_cast<uint8_t*&>(data) += num_read;
2502 }
2503 }
2504
2505 virtual _Success_(return != 0) size_t write(
2506 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2507 {
2508 _Assume_(data || !length);
2509 for (size_t to_write = length;;) {
2510 HRESULT hr;
2511 ULONG num_written = 0;
2512 __try { hr = m_source->Write(data, static_cast<ULONG>(std::min<size_t>(to_write, ULONG_MAX)), &num_written); }
2513 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2514 // In abscence of documentation whether num_written gets set when FAILED(hr) (i.e. partially succesful writes),
2515 // assume write failed completely.
2516 if (FAILED(hr)) _Unlikely_ {
2517 m_state = state_t::fail;
2518 return length - to_write;
2519 }
2520 to_write -= num_written;
2521 if (!to_write) {
2522 m_state = state_t::ok;
2523 return length;
2524 }
2525 reinterpret_cast<const uint8_t*&>(data) += num_written;
2526 }
2527 }
2528
2529 protected:
2530 ISequentialStream* m_source;
2531 };
2532
2536 class asp : public basic
2537 {
2538 public:
2539 asp(_In_opt_ IRequest* request, _In_opt_ IResponse* response) :
2540 m_request(request),
2541 m_response(response)
2542 {
2543 if (m_request)
2544 m_request->AddRef();
2545 if (m_response)
2546 m_response->AddRef();
2547 }
2548
2549 virtual ~asp()
2550 {
2551 if (m_request)
2552 m_request->Release();
2553 if (m_response)
2554 m_response->Release();
2555 }
2556
2557 virtual _Success_(return != 0 || length == 0) size_t read(
2558 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2559 {
2560 _Assume_(data || !length);
2561 if (!m_request) _Unlikely_ {
2562 m_state = state_t::fail;
2563 return 0;
2564 }
2565 for (size_t to_read = length;;) {
2566 VARIANT var_amount, var_data;
2567 V_VT(&var_amount) = VT_I4;
2568 V_I4(&var_amount) = (LONG)std::min<size_t>(to_read, LONG_MAX);
2569 V_VT(&var_data) = VT_EMPTY;
2570 HRESULT hr = [&]() {
2571 __try { return m_request->BinaryRead(&var_amount, &var_data); }
2572 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2573 }();
2574 if (FAILED(hr)) _Unlikely_ {
2575 m_state = to_read < length ? state_t::ok : state_t::fail;
2576 return length - to_read;
2577 }
2578 _Assume_(V_VT(&var_amount) == VT_I4);
2579 _Assume_(V_VT(&var_data) == (VT_ARRAY | VT_UI1));
2580 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(V_ARRAY(&var_data));
2581 if (!V_I4(&var_amount)) _Unlikely_ {
2582 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2583 return length - to_read;
2584 }
2585 safearray_accessor<uint8_t> a(sa.get());
2586 memcpy(data, a.data(), V_I4(&var_amount));
2587 to_read -= V_I4(&var_amount);
2588 if (!to_read) {
2589 m_state = state_t::ok;
2590 return length;
2591 }
2592 reinterpret_cast<uint8_t*&>(data) += V_I4(&var_amount);
2593 }
2594 }
2595
2596 virtual _Success_(return != 0) size_t write(
2597 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2598 {
2599 if (!m_response) {
2600 m_state = state_t::fail;
2601 return 0;
2602 }
2603 for (size_t to_write = length;;) {
2604 UINT num_written = static_cast<UINT>(std::min<size_t>(to_write, UINT_MAX));
2605 std::unique_ptr<OLECHAR, SysFreeString_delete> bstr_data(SysAllocStringByteLen(reinterpret_cast<LPCSTR>(data), num_written));
2606 VARIANT var_data;
2607 V_VT(&var_data) = VT_BSTR;
2608 V_BSTR(&var_data) = bstr_data.get();
2609 HRESULT hr = [&]() {
2610 __try { return m_response->BinaryWrite(var_data); }
2611 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2612 }();
2613 if (FAILED(hr)) _Unlikely_ {
2614 m_state = state_t::fail;
2615 return length - to_write;
2616 }
2617 to_write -= num_written;
2618 if (!to_write) {
2619 m_state = state_t::ok;
2620 return length;
2621 }
2622 reinterpret_cast<const uint8_t*&>(data) += num_written;
2623 }
2624 }
2625
2626 virtual void close()
2627 {
2628 if (m_response) {
2629 __try { m_response->End(); }
2630 __except (EXCEPTION_EXECUTE_HANDLER) {}
2631 }
2632 m_state = state_t::ok;
2633 }
2634
2635 virtual void flush()
2636 {
2637 if (m_response) {
2638 HRESULT hr;
2639 __try { hr = m_response->Flush(); }
2640 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2641 m_state = SUCCEEDED(hr) ? state_t::ok : state_t::fail;
2642 }
2643 }
2644
2645 protected:
2646 IRequest* m_request;
2647 IResponse* m_response;
2648 };
2649#endif
2650
2654 enum mode_t
2655 {
2656 mode_for_reading = 1 << 0,
2657 mode_for_writing = 1 << 1,
2658 mode_for_chmod = 1 << 2,
2659
2660 mode_open_existing = 0 << 3,
2661 mode_truncate_existing = 1 << 3,
2662 mode_preserve_existing = 2 << 3,
2663 mode_create_new = 3 << 3,
2664 mode_create = 4 << 3,
2665 mode_disposition_mask = 7 << 3,
2666
2667 mode_append = 1 << 6,
2668 mode_text = 0,
2669 mode_binary = 1 << 7,
2670
2671 share_none = 0,
2672 share_reading = 1 << 8,
2673 share_writing = 1 << 9,
2674 share_deleting = 1 << 10,
2675 share_all = share_reading | share_writing | share_deleting, // Allow others all operations on our file
2676
2677 inherit_handle = 1 << 11,
2678
2679 hint_write_thru = 1 << 12,
2680 hint_no_buffering = 1 << 13,
2681 hint_random_access = 1 << 14,
2682 hint_sequential_access = 1 << 15,
2683 };
2684
2685#pragma warning(push)
2686#pragma warning(disable: 4250)
2690 class file : virtual public basic_file, virtual public basic_sys
2691 {
2692 public:
2693 file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) : basic_sys(h, state) {}
2694
2701 file(_In_z_ const schar_t* filename, _In_ int mode)
2702 {
2703 open(filename, mode);
2704 }
2705
2712 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
2713 file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode) : file(filename.c_str(), mode) {}
2714
2721 void open(_In_z_ const schar_t* filename, _In_ int mode)
2722 {
2723 if (m_h != invalid_handle)
2724 close();
2725
2726#ifdef _WIN32
2727 DWORD dwDesiredAccess = 0;
2728 if (mode & mode_for_reading) dwDesiredAccess |= GENERIC_READ;
2729 if (mode & mode_for_writing) dwDesiredAccess |= GENERIC_WRITE;
2730 if (mode & mode_for_chmod) dwDesiredAccess |= FILE_WRITE_ATTRIBUTES;
2731
2732 DWORD dwShareMode = 0;
2733 if (mode & share_reading) dwShareMode |= FILE_SHARE_READ;
2734 if (mode & share_writing) dwShareMode |= FILE_SHARE_WRITE;
2735 if (mode & share_deleting) dwShareMode |= FILE_SHARE_DELETE;
2736
2737 SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };
2738 sa.bInheritHandle = mode & inherit_handle ? true : false;
2739
2740 DWORD dwCreationDisposition;
2741 switch (mode & mode_disposition_mask) {
2742 case mode_open_existing: dwCreationDisposition = OPEN_EXISTING; break;
2743 case mode_truncate_existing: dwCreationDisposition = TRUNCATE_EXISTING; break;
2744 case mode_preserve_existing: dwCreationDisposition = OPEN_ALWAYS; break;
2745 case mode_create_new: dwCreationDisposition = CREATE_NEW; break;
2746 case mode_create: dwCreationDisposition = CREATE_ALWAYS; break;
2747 default: throw std::invalid_argument("invalid mode");
2748 }
2749
2750 DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
2751 if (mode & hint_write_thru) dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
2752 if (mode & hint_no_buffering) dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
2753 if (mode & hint_random_access) dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
2754 if (mode & hint_sequential_access) dwFlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
2755
2756 m_h = CreateFile(filename, dwDesiredAccess, dwShareMode, &sa, dwCreationDisposition, dwFlagsAndAttributes, NULL);
2757#else
2758 int flags = 0;
2759 switch (mode & (mode_for_reading | mode_for_writing)) {
2760 case mode_for_reading: flags |= O_RDONLY; break;
2761 case mode_for_writing: flags |= O_WRONLY; break;
2762 case mode_for_reading | mode_for_writing: flags |= O_RDWR; break;
2763 }
2764 switch (mode & mode_disposition_mask) {
2765 case mode_open_existing: break;
2766 case mode_truncate_existing: flags |= O_TRUNC; break;
2767 case mode_preserve_existing: flags |= O_CREAT; break;
2768 case mode_create_new: flags |= O_CREAT | O_EXCL; break;
2769 case mode_create: flags |= O_CREAT | O_TRUNC; break;
2770 default: throw std::invalid_argument("invalid mode");
2771 }
2772 if (mode & hint_write_thru) flags |= O_DSYNC;
2773#ifndef __APPLE__
2774 if (mode & hint_no_buffering) flags |= O_RSYNC;
2775#endif
2776
2777 m_h = ::open(filename, flags, DEFFILEMODE);
2778#endif
2779 if (m_h != invalid_handle) {
2780 m_state = state_t::ok;
2781 if (mode & mode_append)
2782 seek(0, seek_t::end);
2783 }
2784 else
2785 m_state = state_t::fail;
2786 }
2787
2794 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
2795 void open(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
2796 {
2797 open(filename.c_str(), mode);
2798 }
2799
2800 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2801 {
2802#ifdef _WIN32
2803 LARGE_INTEGER li;
2804 li.QuadPart = offset;
2805 li.LowPart = SetFilePointer(m_h, li.LowPart, &li.HighPart, static_cast<DWORD>(how));
2806 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR) {
2807 m_state = state_t::ok;
2808 return li.QuadPart;
2809 }
2810#else
2811 off64_t result = lseek64(m_h, offset, static_cast<int>(how));
2812 if (result >= 0) {
2813 m_state = state_t::ok;
2814 return result;
2815 }
2816#endif
2817 m_state = state_t::fail;
2818 return fpos_max;
2819 }
2820
2821 virtual fpos_t tell() const
2822 {
2823 if (m_h != invalid_handle) {
2824#ifdef _WIN32
2825 LARGE_INTEGER li;
2826 li.QuadPart = 0;
2827 li.LowPart = SetFilePointer(m_h, 0, &li.HighPart, FILE_CURRENT);
2828 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR)
2829 return li.QuadPart;
2830#else
2831 off64_t result = lseek64(m_h, 0, SEEK_CUR);
2832 if (result >= 0)
2833 return result;
2834#endif
2835 }
2836 return fpos_max;
2837 }
2838
2839 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2840 {
2841#ifdef _WIN32
2842 LARGE_INTEGER liOffset;
2843 LARGE_INTEGER liSize;
2844 liOffset.QuadPart = offset;
2845 liSize.QuadPart = length;
2846 if (LockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2847 m_state = state_t::ok;
2848 return;
2849 }
2850#else
2851 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2852 if (orig >= 0) {
2853 m_state = lseek64(m_h, offset, SEEK_SET) >= 0 && lockf64(m_h, F_LOCK, length) >= 0 ? state_t::ok : state_t::fail;
2854 lseek64(m_h, orig, SEEK_SET);
2855 m_state = state_t::ok;
2856 return;
2857 }
2858#endif
2859 m_state = state_t::fail;
2860 }
2861
2862 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2863 {
2864#ifdef _WIN32
2865 LARGE_INTEGER liOffset;
2866 LARGE_INTEGER liSize;
2867 liOffset.QuadPart = offset;
2868 liSize.QuadPart = length;
2869 if (UnlockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2870 m_state = state_t::ok;
2871 return;
2872 }
2873#else
2874 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2875 if (orig >= 0) {
2876 if (lseek64(m_h, offset, SEEK_SET) >= 0 && lockf64(m_h, F_ULOCK, length) >= 0) {
2877 lseek64(m_h, orig, SEEK_SET);
2878 m_state = state_t::ok;
2879 return;
2880 }
2881 lseek64(m_h, orig, SEEK_SET);
2882 }
2883#endif
2884 m_state = state_t::fail;
2885 }
2886
2887 virtual fsize_t size() const
2888 {
2889#ifdef _WIN32
2890 LARGE_INTEGER li;
2891 li.LowPart = GetFileSize(m_h, (LPDWORD)&li.HighPart);
2892 if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR)
2893 li.QuadPart = -1;
2894 return li.QuadPart;
2895#else
2896 off64_t length = -1, orig = lseek64(m_h, 0, SEEK_CUR);
2897 if (orig >= 0) {
2898 length = lseek64(m_h, 0, SEEK_END);
2899 lseek64(m_h, orig, SEEK_SET);
2900 }
2901 return length;
2902#endif
2903 }
2904
2905 virtual void truncate()
2906 {
2907#ifdef _WIN32
2908 if (SetEndOfFile(m_h)) {
2909 m_state = state_t::ok;
2910 return;
2911 }
2912#else
2913 off64_t length = lseek64(m_h, 0, SEEK_CUR);
2914 if (length >= 0 && ftruncate64(m_h, length) >= 0) {
2915 m_state = state_t::ok;
2916 return;
2917 }
2918#endif
2919 m_state = state_t::fail;
2920 }
2921
2922#ifdef _WIN32
2923 static time_point ft2tp(_In_ const FILETIME& ft)
2924 {
2925#if _HAS_CXX20
2926 uint64_t t = (static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
2927#else
2928 uint64_t t = ((static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) - 116444736000000000ll;
2929#endif
2930 return time_point(time_point::duration(t));
2931 }
2932
2933 static void tp2ft(_In_ time_point tp, _Out_ FILETIME& ft)
2934 {
2935#if _HAS_CXX20
2936 uint64_t t = tp.time_since_epoch().count();
2937#else
2938 uint64_t t = tp.time_since_epoch().count() + 116444736000000000ll;
2939#endif
2940 ft.dwHighDateTime = static_cast<DWORD>((t >> 32) & 0xffffffff);
2941 ft.dwLowDateTime = static_cast<DWORD>(t & 0xffffffff);
2942 }
2943#endif
2944
2945 virtual time_point ctime() const
2946 {
2947#ifdef _WIN32
2948 FILETIME ft;
2949 if (GetFileTime(m_h, &ft, nullptr, nullptr))
2950 return ft2tp(ft);
2951#endif
2952 return time_point::min();
2953 }
2954
2955 virtual time_point atime() const
2956 {
2957#ifdef _WIN32
2958 FILETIME ft;
2959 if (GetFileTime(m_h, nullptr, &ft, nullptr))
2960 return ft2tp(ft);
2961#else
2962 struct stat buf;
2963 if (fstat(m_h, &buf) >= 0)
2964 return clock::from_time_t(buf.st_atime);
2965#endif
2966 return time_point::min();
2967 }
2968
2969 virtual time_point mtime() const
2970 {
2971#ifdef _WIN32
2972 FILETIME ft;
2973 if (GetFileTime(m_h, nullptr, nullptr, &ft))
2974 return ft2tp(ft);
2975#else
2976 struct stat buf;
2977 if (fstat(m_h, &buf) >= 0)
2978 return clock::from_time_t(buf.st_mtime);
2979#endif
2980 return time_point::min();
2981 }
2982
2983 virtual void set_ctime(time_point date)
2984 {
2985 _Assume_(m_h != invalid_handle);
2986#ifdef _WIN32
2987 FILETIME ft;
2988 tp2ft(date, ft);
2989 if (SetFileTime(m_h, &ft, nullptr, nullptr))
2990 return;
2991 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
2992#else
2993 throw std::runtime_error("not supported");
2994#endif
2995 }
2996
2997 virtual void set_atime(time_point date)
2998 {
2999 _Assume_(m_h != invalid_handle);
3000#ifdef _WIN32
3001 FILETIME ft;
3002 tp2ft(date, ft);
3003 if (SetFileTime(m_h, nullptr, &ft, nullptr))
3004 return;
3005 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3006#else
3007 struct timespec ts[2] = {
3008 { date.time_since_epoch().count(), 0 },
3009 { 0, UTIME_OMIT },
3010 };
3011 if (futimens(m_h, ts) >= 0)
3012 return;
3013 throw std::system_error(errno, std::system_category(), "futimens failed");
3014#endif
3015 }
3016
3017 virtual void set_mtime(time_point date)
3018 {
3019#ifdef _WIN32
3020 FILETIME ft;
3021 tp2ft(date, ft);
3022 if (SetFileTime(m_h, nullptr, nullptr, &ft))
3023 return;
3024 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3025#else
3026 struct timespec ts[2] = {
3027 { 0, UTIME_OMIT },
3028 { date.time_since_epoch().count(), 0 },
3029 };
3030 if (futimens(m_h, ts) >= 0)
3031 return;
3032 throw std::system_error(errno, std::system_category(), "futimens failed");
3033#endif
3034 }
3035
3041 static bool exists(_In_z_ const stdex::schar_t* filename)
3042 {
3043#ifdef _WIN32
3044 return GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES;
3045#else
3046 struct stat s;
3047 return stat(filename, &s) == 0;
3048#endif
3049 }
3050
3056 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3057 static bool exists(_In_ const std::basic_string<TR, AX>& filename)
3058 {
3059 return exists(filename.c_str());
3060 }
3061
3069 static bool readonly(_In_z_ const stdex::schar_t* filename)
3070 {
3071#ifdef _WIN32
3072 DWORD dwAttr = GetFileAttributes(filename);
3073 return dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_READONLY) != 0;
3074#else
3075 struct stat s;
3076 return stat(filename, &s) == 0 && (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0;
3077#endif
3078 }
3079
3087 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3088 static bool readonly(_In_ const std::basic_string<TR, AX>& filename)
3089 {
3090 return readonly(filename.c_str());
3091 }
3092 };
3093#pragma warning(pop)
3094
3098 class cached_file : public cache
3099 {
3100 public:
3101 cached_file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok, _In_ size_t cache_size = default_cache_size) :
3102 cache(cache_size),
3103 m_source(h, state)
3104 {
3105 init(m_source);
3106 }
3107
3115 cached_file(_In_z_ const schar_t* filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) :
3116 cache(cache_size),
3117 m_source(filename, mode & mode_for_writing ? mode | mode_for_reading : mode)
3118 {
3119 init(m_source);
3120 }
3121
3129 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3130 cached_file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) : cached_file(filename.c_str(), mode, cache_size) {}
3131
3132 virtual ~cached_file()
3133 {
3134 done();
3135 }
3136
3143 void open(_In_z_ const schar_t* filename, _In_ int mode)
3144 {
3145 invalidate_cache();
3146 if (!ok()) _Unlikely_{
3147 m_state = state_t::fail;
3148 return;
3149 }
3150 m_source.open(filename, mode & mode_for_writing ? mode | mode_for_reading : mode);
3151 if (m_source.ok()) {
3152 init();
3153 return;
3154 }
3155 m_state = state_t::fail;
3156 }
3157
3164 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3165 void open(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3166 {
3167 open(filename.c_str(), mode);
3168 }
3169
3173 operator bool() const noexcept { return m_source; }
3174
3175 protected:
3176 file m_source;
3177 };
3178
3183 {
3184 public:
3185 memory_file(_In_ state_t state = state_t::ok) :
3186 basic(state),
3187 m_data(nullptr),
3188 m_offset(0),
3189 m_size(0),
3190 m_reserved(0),
3191 m_manage(true)
3192 {
3193#if SET_FILE_OP_TIMES
3194 m_ctime = m_atime = m_mtime = time_point::now();
3195#endif
3196 }
3197
3204 memory_file(_In_ size_t size, _In_ state_t state = state_t::ok) :
3205 basic(state),
3206 m_data(reinterpret_cast<uint8_t*>(malloc(size))),
3207 m_offset(0),
3208 m_size(0),
3210 m_manage(true)
3211 {
3212 if (!m_data) {
3213 m_state = state_t::fail;
3214 throw std::bad_alloc();
3215 }
3216#if SET_FILE_OP_TIMES
3217 m_ctime = m_atime = m_mtime = time_point::now();
3218#endif
3219 }
3220
3230 memory_file(_Inout_ void* data, _In_ size_t size, _In_ size_t reserved, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3231 basic(state),
3232 m_data(reinterpret_cast<uint8_t*>(data)),
3233 m_offset(0),
3234 m_size(size),
3235 m_reserved(reserved),
3236 m_manage(manage)
3237 {
3238 _Assume_(data || !size);
3239 _Assume_(reserved >= size);
3240#if SET_FILE_OP_TIMES
3241 m_ctime = m_atime = m_mtime = time_point::now();
3242#endif
3243 }
3244
3253 memory_file(_Inout_ void* data, _In_ size_t size, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3254 memory_file(data, size, size, manage, state)
3255 {}
3256
3263 memory_file(_In_z_ const schar_t* filename, _In_ int mode) : memory_file()
3264 {
3265 load(filename, mode);
3266 }
3267
3274 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3275 memory_file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode) : memory_file(filename.c_str(), mode) {}
3276
3282 memory_file(_In_ const memory_file& other) :
3283 basic_file(other),
3284 m_data(reinterpret_cast<uint8_t*>(malloc(other.m_size))),
3285 m_offset(other.m_offset),
3286 m_size(other.m_size),
3287 m_reserved(other.m_size),
3288 m_manage(true)
3289#if SET_FILE_OP_TIMES
3290 , m_ctime(other.m_ctime)
3291 , m_atime(other.m_atime)
3292 , m_mtime(other.m_mtime)
3293#endif
3294 {
3295 if (!m_data) {
3296 m_state = state_t::fail;
3297 throw std::bad_alloc();
3298 }
3299 memcpy(m_data, other.m_data, other.m_size);
3300 }
3301
3308 {
3309 if (this != std::addressof(other)) {
3310 *static_cast<basic_file*>(this) = other;
3311 if (m_manage && m_data)
3312 free(m_data);
3313 m_data = reinterpret_cast<uint8_t*>(malloc(other.m_size));
3314 if (!m_data) {
3315 m_state = state_t::fail;
3316 throw std::bad_alloc();
3317 }
3318 memcpy(m_data, other.m_data, other.m_size);
3319 m_offset = other.m_offset;
3320 m_size = other.m_size;
3321 m_reserved = other.m_size;
3322 m_manage = true;
3323#if SET_FILE_OP_TIMES
3324 m_ctime = other.m_ctime;
3325 m_atime = other.m_atime;
3326 m_mtime = other.m_mtime;
3327#endif
3328 }
3329 return *this;
3330 }
3331
3337 memory_file(_Inout_ memory_file&& other) noexcept :
3338 basic_file(std::move(other)),
3339 m_data(other.m_data),
3340 m_offset(other.m_offset),
3341 m_size(other.m_size),
3342 m_reserved(other.m_reserved),
3343 m_manage(other.m_manage)
3344#if SET_FILE_OP_TIMES
3345 , m_ctime(other.m_ctime)
3346 , m_atime(other.m_atime)
3347 , m_mtime(other.m_mtime)
3348#endif
3349 {
3350 other.m_state = state_t::ok;
3351 other.m_data = nullptr;
3352 other.m_offset = 0;
3353 other.m_size = 0;
3354 other.m_reserved = 0;
3355 other.m_manage = true;
3356#if SET_FILE_OP_TIMES
3357 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3358#endif
3359 }
3360
3366 memory_file& operator=(_Inout_ memory_file&& other) noexcept
3367 {
3368 if (this != std::addressof(other)) {
3369 *static_cast<basic_file*>(this) = std::move(other);
3370 if (m_manage && m_data)
3371 free(m_data);
3372 m_data = other.m_data;
3373 other.m_data = nullptr;
3374 m_offset = other.m_offset;
3375 other.m_offset = 0;
3376 m_size = other.m_size;
3377 other.m_size = 0;
3378 m_reserved = other.m_reserved;
3379 other.m_reserved = 0;
3380 m_manage = other.m_manage;
3381 other.m_manage = true;
3382#if SET_FILE_OP_TIMES
3383 m_ctime = other.m_ctime;
3384 m_atime = other.m_atime;
3385 m_mtime = other.m_mtime;
3386 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3387#endif
3388 }
3389 return *this;
3390 }
3391
3392 virtual ~memory_file()
3393 {
3394 if (m_manage && m_data)
3395 free(m_data);
3396 }
3397
3404 void reserve(_In_ size_t required, _In_ bool tight = false) noexcept
3405 {
3406 if (required <= m_reserved && (!tight || required >= m_reserved)) {
3407 m_state = state_t::ok;
3408 return;
3409 }
3410 if (!m_manage) {
3411 m_state = state_t::fail;
3412 return;
3413 }
3414 size_t reserved = tight ? required : ((required + required / 4 + (default_block_size - 1)) / default_block_size) * default_block_size;
3415 auto data = reinterpret_cast<uint8_t*>(realloc(m_data, reserved));
3416 if (!data && reserved) _Unlikely_ {
3417 m_state = state_t::fail;
3418 return;
3419 }
3420 m_data = data;
3421 if (reserved < m_size)
3422 m_size = reserved;
3423 m_reserved = reserved;
3424 m_state = state_t::ok;
3425 }
3426
3433 void load(_In_z_ const schar_t* filename, _In_ int mode)
3434 {
3435 file f(filename, (mode & ~hint_random_access) | mode_for_reading | hint_sequential_access);
3436 if (!f.ok()) {
3437 m_state = state_t::fail;
3438 return;
3439 }
3440 fsize_t size = f.size();
3441 if (size > SIZE_MAX) {
3442 m_state = state_t::fail;
3443 return;
3444 }
3445 reserve(static_cast<size_t>(size), true);
3446 if (!ok()) _Unlikely_ {
3447 return;
3448 }
3449 m_offset = m_size = 0;
3450 write_stream(f);
3451 if (ok())
3452 m_offset = 0;
3453#if SET_FILE_OP_TIMES
3454 m_ctime = f.ctime();
3455 m_atime = f.atime();
3456 m_mtime = f.mtime();
3457#endif
3458 }
3459
3466 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3467 void load(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3468 {
3469 load(filename.c_str(), mode);
3470 }
3471
3478 void save(_In_z_ const schar_t* filename, _In_ int mode)
3479 {
3480 file f(filename, (mode & ~hint_random_access) | mode_for_writing | hint_sequential_access);
3481 if (!f.ok()) {
3482 m_state = state_t::fail;
3483 return;
3484 }
3485 f.write(m_data, m_size);
3486 if (!f.ok()) {
3487 m_state = state_t::fail;
3488 return;
3489 }
3490 f.truncate();
3491#if SET_FILE_OP_TIMES
3492 f.set_ctime(m_ctime);
3493 f.set_atime(m_atime);
3494 f.set_mtime(m_mtime);
3495#endif
3496 }
3497
3504 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3505 void save(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3506 {
3507 save(filename.c_str(), mode);
3508 }
3509
3513 const void* data() const { return m_data; }
3514
3515 virtual _Success_(return != 0 || length == 0) size_t read(
3516 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
3517 {
3518 _Assume_(data || !length);
3519#if SET_FILE_OP_TIMES
3520 m_atime = time_point::now();
3521#endif
3522 size_t available = m_size - m_offset;
3523 if (length <= available) {
3524 memcpy(data, m_data + m_offset, length);
3525 m_offset += length;
3526 m_state = state_t::ok;
3527 return length;
3528 }
3529 if (length && !available) {
3530 m_state = state_t::eof;
3531 return 0;
3532 }
3533 memcpy(data, m_data + m_offset, available);
3534 m_offset += available;
3535 m_state = state_t::ok;
3536 return available;
3537 }
3538
3553 template <class T>
3555 {
3556#if SET_FILE_OP_TIMES
3557 m_atime = time_point::now();
3558#endif
3559 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3560 data = 0;
3561 return *this;
3562 }
3563 size_t end_offset = m_offset + sizeof(T);
3564 if (end_offset <= m_size) {
3565 data = LE2HE(*reinterpret_cast<T*>(m_data + m_offset));
3566 m_offset = end_offset;
3567#if !CHECK_STREAM_STATE
3568 m_state = state_t::ok;
3569#endif
3570 }
3571 else {
3572 data = 0;
3573 m_offset = m_size;
3574 m_state = state_t::eof;
3575 }
3576 return *this;
3577 }
3578
3593 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3594 memory_file& read_str(_Inout_ std::basic_string<T, TR, AX>&data)
3595 {
3596#if SET_FILE_OP_TIMES
3597 m_atime = time_point::now();
3598#endif
3599 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3600 data.clear();
3601 return *this;
3602 }
3603 size_t end_offset = m_offset + sizeof(uint32_t);
3604 if (end_offset <= m_size) {
3605 uint32_t num_chars = LE2HE(*reinterpret_cast<uint32_t*>(m_data + m_offset));
3606 m_offset = end_offset;
3607 end_offset = stdex::add(m_offset, stdex::mul(num_chars, sizeof(T)));
3608 T* start = reinterpret_cast<T*>(m_data + m_offset);
3609 if (end_offset <= m_size) {
3610 data.assign(start, start + num_chars);
3611 m_offset = end_offset;
3612#if !CHECK_STREAM_STATE
3613 m_state = state_t::ok;
3614#endif
3615 return *this;
3616 }
3617 if (end_offset <= m_size)
3618 data.assign(start, reinterpret_cast<T*>(m_data + m_size));
3619 }
3620 m_offset = m_size;
3621 m_state = state_t::eof;
3622 return *this;
3623 }
3624
3625 virtual _Success_(return != 0) size_t write(
3626 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3627 {
3628 _Assume_(data || !length);
3629#if SET_FILE_OP_TIMES
3630 m_atime = m_mtime = time_point::now();
3631#endif
3632 size_t end_offset = m_offset + length;
3633 if (end_offset > m_reserved) {
3634 reserve(end_offset);
3635 if (!ok()) _Unlikely_
3636 return 0;
3637 }
3638 memcpy(m_data + m_offset, data, length);
3639 m_offset = end_offset;
3640 if (m_offset > m_size)
3641 m_size = m_offset;
3642 m_state = state_t::ok;
3643 return length;
3644 }
3645
3649 void write_byte(_In_ uint8_t byte, _In_ size_t amount = 1)
3650 {
3651#if SET_FILE_OP_TIMES
3652 m_atime = m_mtime = time_point::now();
3653#endif
3654 size_t end_offset = m_offset + amount;
3655 if (end_offset > m_reserved) {
3656 reserve(end_offset);
3657 if (!ok()) _Unlikely_
3658 return;
3659 }
3660 memset(m_data + m_offset, byte, amount);
3661 m_offset = end_offset;
3662 if (m_offset > m_size)
3663 m_size = m_offset;
3664 m_state = state_t::ok;
3665 }
3666
3681 template <class T>
3683 {
3684#if SET_FILE_OP_TIMES
3685 m_atime = m_mtime = time_point::now();
3686#endif
3687 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3688 return *this;
3689 size_t end_offset = m_offset + sizeof(T);
3690 if (end_offset > m_reserved) {
3691 reserve(end_offset);
3692 if (!ok()) _Unlikely_
3693 return *this;
3694 }
3695 (*reinterpret_cast<T*>(m_data + m_offset)) = HE2LE(data);
3696 m_offset = end_offset;
3697 if (m_offset > m_size)
3698 m_size = m_offset;
3699#if !CHECK_STREAM_STATE
3700 m_state = state_t::ok;
3701#endif
3702 return *this;
3703 }
3704
3719 template <class T>
3720 memory_file& write_str(_In_z_ const T * data)
3721 {
3722#if SET_FILE_OP_TIMES
3723 m_atime = m_mtime = time_point::now();
3724#endif
3725 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3726 return *this;
3727 size_t num_chars = stdex::strlen(data);
3728 if (num_chars > UINT32_MAX)
3729 throw std::invalid_argument("string too long");
3730 size_t size_chars = num_chars * sizeof(T);
3731 size_t size = sizeof(uint32_t) + size_chars;
3732 size_t end_offset = m_offset + size;
3733 if (end_offset > m_reserved) {
3734 reserve(end_offset);
3735 if (!ok()) _Unlikely_
3736 return *this;
3737 }
3738 auto p = m_data + m_offset;
3739 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3740 memcpy(p + sizeof(uint32_t), data, size_chars);
3741 m_offset = end_offset;
3742 if (m_offset > m_size)
3743 m_size = m_offset;
3744#if !CHECK_STREAM_STATE
3745 m_state = state_t::ok;
3746#endif
3747 return *this;
3748 }
3749
3764 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3765 memory_file& write_str(_In_ const std::basic_string<T, TR, AX>& data)
3766 {
3767#if SET_FILE_OP_TIMES
3768 m_atime = m_mtime = time_point::now();
3769#endif
3770 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3771 return *this;
3772 size_t num_chars = data.size();
3773 if (num_chars > UINT32_MAX)
3774 throw std::invalid_argument("string too long");
3775 size_t size_chars = num_chars * sizeof(T);
3776 size_t size = sizeof(uint32_t) + size_chars;
3777 size_t end_offset = m_offset + size;
3778 if (end_offset > m_reserved) {
3779 reserve(end_offset);
3780 if (!ok()) _Unlikely_
3781 return *this;
3782 }
3783 auto p = m_data + m_offset;
3784 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3785 memcpy(p + sizeof(uint32_t), data.data(), size_chars);
3786 m_offset = end_offset;
3787 if (m_offset > m_size)
3788 m_size = m_offset;
3789#if !CHECK_STREAM_STATE
3790 m_state = state_t::ok;
3791#endif
3792 return *this;
3793 }
3794
3800 size_t write_stream(_Inout_ basic & stream, _In_ size_t amount = SIZE_MAX)
3801 {
3802#if SET_FILE_OP_TIMES
3803 m_atime = m_mtime = time_point::now();
3804#endif
3805 size_t num_read, dst_offset = m_offset, dst_size = m_offset;
3806 size_t num_copied = 0, to_write = amount;
3807 m_state = state_t::ok;
3808 if (amount != SIZE_MAX) {
3809 dst_size = stdex::add(dst_size, amount);
3810 reserve(dst_size);
3811 if (!ok()) _Unlikely_
3812 return 0;
3813 while (to_write) {
3814 num_read = stream.read(m_data + dst_offset, to_write);
3815 dst_size = dst_offset += num_read;
3816 num_copied += num_read;
3817 to_write -= num_read;
3818 if (!stream.ok()) {
3819 if (stream.state() != state_t::eof)
3820 m_state = state_t::fail;
3821 break;
3822 }
3823 };
3824 }
3825 else {
3826 size_t block_size;
3827 while (to_write) {
3828 block_size = std::min(to_write, default_block_size);
3829 dst_size = stdex::add(dst_size, block_size);
3830 reserve(dst_size);
3831 if (!ok()) _Unlikely_
3832 break;
3833 num_read = stream.read(m_data + dst_offset, block_size);
3834 dst_size = dst_offset += num_read;
3835 num_copied += num_read;
3836 to_write -= num_read;
3837 if (!stream.ok()) {
3838 if (stream.state() != state_t::eof)
3839 m_state = state_t::fail;
3840 break;
3841 }
3842 };
3843 }
3844 m_offset = dst_offset;
3845 if (m_offset > m_size)
3846 m_size = m_offset;
3847 return num_copied;
3848 }
3849
3850 virtual void close()
3851 {
3852 if (m_manage && m_data)
3853 free(m_data);
3854 m_data = nullptr;
3855 m_manage = true;
3856 m_offset = 0;
3857 m_size = m_reserved = 0;
3858#if SET_FILE_OP_TIMES
3859 m_ctime = m_atime = m_mtime = time_point::min();
3860#endif
3861 m_state = state_t::ok;
3862 }
3863
3864 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
3865 {
3866 fpos_t target;
3867 switch (how) {
3868 case seek_t::beg: target = offset; break;
3869 case seek_t::cur: target = static_cast<fpos_t>(m_offset) + offset; break;
3870 case seek_t::end: target = static_cast<fpos_t>(m_size) + offset; break;
3871 default: throw std::invalid_argument("unknown seek origin");
3872 }
3873 if (target <= SIZE_MAX) {
3874 m_state = state_t::ok;
3875 return m_offset = static_cast<size_t>(target);
3876 }
3877 m_state = state_t::fail;
3878 return fpos_max;
3879 }
3880
3881 virtual fpos_t tell() const
3882 {
3883 return m_offset;
3884 }
3885
3886 virtual fsize_t size() const
3887 {
3888 return m_size;
3889 }
3890
3891 virtual void truncate()
3892 {
3893#if SET_FILE_OP_TIMES
3894 m_atime = m_mtime = time_point::now();
3895#endif
3896 m_size = m_offset;
3898 }
3899
3900#if SET_FILE_OP_TIMES
3901 virtual time_point ctime() const
3902 {
3903 return m_ctime;
3904 }
3905
3906 virtual time_point atime() const
3907 {
3908 return m_atime;
3909 }
3910
3911 virtual time_point mtime() const
3912 {
3913 return m_mtime;
3914 }
3915
3916 virtual void set_ctime(time_point date)
3917 {
3918 m_ctime = date;
3919 }
3920
3921 virtual void set_atime(time_point date)
3922 {
3923 m_atime = date;
3924 }
3925
3926 virtual void set_mtime(time_point date)
3927 {
3928 m_mtime = date;
3929 }
3930#endif
3931
3932 protected:
3940 template <class T>
3941 void set(_In_ fpos_t offset, _In_ const T data)
3942 {
3943#if SET_FILE_OP_TIMES
3944 m_atime = m_mtime = time_point::now();
3945#endif
3946 _Assume_(offset + sizeof(T) < m_size);
3947 (*reinterpret_cast<T*>(m_data + offset)) = HE2LE(data);
3948 }
3949
3950 public:
3951 void set(_In_ fpos_t offset, _In_ const int8_t data) { set<int8_t>(offset, data); }
3952 void set(_In_ fpos_t offset, _In_ const int16_t data) { set<int16_t>(offset, data); }
3953 void set(_In_ fpos_t offset, _In_ const int32_t data) { set<int32_t>(offset, data); }
3954 void set(_In_ fpos_t offset, _In_ const int64_t data) { set<int64_t>(offset, data); }
3955 void set(_In_ fpos_t offset, _In_ const uint8_t data) { set<uint8_t>(offset, data); }
3956 void set(_In_ fpos_t offset, _In_ const uint16_t data) { set<uint16_t>(offset, data); }
3957 void set(_In_ fpos_t offset, _In_ const uint32_t data) { set<uint32_t>(offset, data); }
3958 void set(_In_ fpos_t offset, _In_ const uint64_t data) { set<uint64_t>(offset, data); }
3959 void set(_In_ fpos_t offset, _In_ const float data) { set<float>(offset, data); }
3960 void set(_In_ fpos_t offset, _In_ const double data) { set<double>(offset, data); }
3961 void set(_In_ fpos_t offset, _In_ const char data) { set<char>(offset, data); }
3962#ifdef _NATIVE_WCHAR_T_DEFINED
3963 void set(_In_ fpos_t offset, _In_ const wchar_t data) { set<wchar_t>(offset, data); }
3964#endif
3965
3973 protected:
3974 template <class T>
3975 void get(_In_ fpos_t offset, _Out_ T & data)
3976 {
3977 _Assume_(offset + sizeof(T) < m_size);
3978 data = LE2HE(*(T*)(m_data + offset));
3979#if SET_FILE_OP_TIMES
3980 m_atime = time_point::now();
3981#endif
3982 }
3983
3984 public:
3985 void get(_In_ fpos_t offset, _Out_ int8_t & data) { get<int8_t>(offset, data); }
3986 void get(_In_ fpos_t offset, _Out_ int16_t & data) { get<int16_t>(offset, data); }
3987 void get(_In_ fpos_t offset, _Out_ int32_t & data) { get<int32_t>(offset, data); }
3988 void get(_In_ fpos_t offset, _Out_ int64_t & data) { get<int64_t>(offset, data); }
3989 void get(_In_ fpos_t offset, _Out_ uint8_t & data) { get<uint8_t>(offset, data); }
3990 void get(_In_ fpos_t offset, _Out_ uint16_t & data) { get<uint16_t>(offset, data); }
3991 void get(_In_ fpos_t offset, _Out_ uint32_t & data) { get<uint32_t>(offset, data); }
3992 void get(_In_ fpos_t offset, _Out_ uint64_t & data) { get<uint64_t>(offset, data); }
3993 void get(_In_ fpos_t offset, _Out_ float& data) { get<float>(offset, data); }
3994 void get(_In_ fpos_t offset, _Out_ double& data) { get<double>(offset, data); }
3995 void get(_In_ fpos_t offset, _Out_ char& data) { get<char>(offset, data); }
3996#ifdef _NATIVE_WCHAR_T_DEFINED
3997 void get(_In_ fpos_t offset, _Out_ wchar_t& data) { get<wchar_t>(offset, data); }
3998#endif
3999
4000 memory_file& operator <<(_In_ const int8_t data) { return write_data(data); }
4001 memory_file& operator >>(_Out_ int8_t & data) { return read_data(data); }
4002 memory_file& operator <<(_In_ const int16_t data) { return write_data(data); }
4003 memory_file& operator >>(_Out_ int16_t & data) { return read_data(data); }
4004 memory_file& operator <<(_In_ const int32_t data) { return write_data(data); }
4005 memory_file& operator >>(_Out_ int32_t & data) { return read_data(data); }
4006 memory_file& operator <<(_In_ const int64_t data) { return write_data(data); }
4007 memory_file& operator >>(_Out_ int64_t & data) { return read_data(data); }
4008 memory_file& operator <<(_In_ const uint8_t data) { return write_data(data); }
4009 memory_file& operator >>(_Out_ uint8_t & data) { return read_data(data); }
4010 memory_file& operator <<(_In_ const uint16_t data) { return write_data(data); }
4011 memory_file& operator >>(_Out_ uint16_t & data) { return read_data(data); }
4012 memory_file& operator <<(_In_ const uint32_t data) { return write_data(data); }
4013 memory_file& operator >>(_Out_ uint32_t & data) { return read_data(data); }
4014 memory_file& operator <<(_In_ const uint64_t data) { return write_data(data); }
4015 memory_file& operator >>(_Out_ uint64_t & data) { return read_data(data); }
4016 memory_file& operator <<(_In_ const float data) { return write_data(data); }
4017 memory_file& operator >>(_Out_ float& data) { return read_data(data); }
4018 memory_file& operator <<(_In_ const double data) { return write_data(data); }
4019 memory_file& operator >>(_Out_ double& data) { return read_data(data); }
4020 memory_file& operator <<(_In_ const char data) { return write_data(data); }
4021 memory_file& operator >>(_Out_ char& data) { return read_data(data); }
4022#ifdef _NATIVE_WCHAR_T_DEFINED
4023 memory_file& operator <<(_In_ const wchar_t data) { return write_data(data); }
4024 memory_file& operator >>(_Out_ wchar_t& data) { return read_data(data); }
4025#endif
4026 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
4027 memory_file& operator >>(_Out_ std::basic_string<T, TR, AX>&data) { return read_str(data); }
4028 template <class T>
4029 memory_file& operator <<(_In_ const T * data) { return write_str(data); }
4030 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
4031 memory_file& operator <<(_In_ const std::basic_string<T, TR, AX>& data) { return write_str(data); }
4032
4033 protected:
4034 uint8_t* m_data;
4036 size_t m_offset;
4037 size_t m_size;
4038 size_t m_reserved;
4039#if SET_FILE_OP_TIMES
4040 time_point
4041 m_ctime,
4042 m_atime,
4043 m_mtime;
4044#endif
4045 };
4046
4050 class fifo : public basic {
4051 public:
4052 fifo() :
4053 m_offset(0),
4054 m_size(0),
4055 m_head(nullptr),
4056 m_tail(nullptr)
4057 {}
4058
4059 virtual ~fifo()
4060 {
4061 while (m_head) {
4062 auto p = m_head;
4063 m_head = p->next;
4064 delete p;
4065 }
4066 }
4067
4068#pragma warning(suppress: 6101) // See [2] below
4069 virtual _Success_(return != 0 || length == 0) size_t read(
4070 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4071 {
4072 _Assume_(data || !length);
4073 for (size_t to_read = length;;) {
4074 if (!m_head) _Unlikely_ {
4075 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
4076 return length - to_read; // [2] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
4077 }
4078 size_t remaining = m_head->size - m_offset;
4079 if (remaining > to_read) {
4080 memcpy(data, m_head->data + m_offset, to_read);
4081 m_offset += to_read;
4082 m_size -= to_read;
4083 m_state = state_t::ok;
4084 return length;
4085 }
4086 memcpy(data, m_head->data + m_offset, remaining);
4087 m_offset = 0;
4088 m_size -= remaining;
4089 reinterpret_cast<uint8_t*&>(data) += remaining;
4090 to_read -= remaining;
4091 auto p = m_head;
4092 m_head = p->next;
4093 delete p;
4094 }
4095 }
4096
4097 virtual _Success_(return != 0) size_t write(
4098 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4099 {
4100 _Assume_(data || !length);
4101 try {
4102 std::unique_ptr<node_t> n(reinterpret_cast<node_t*>(new uint8_t[sizeof(node_t) + length]));
4103 n->next = nullptr;
4104 n->size = length;
4105 memcpy(n->data, data, length);
4106 m_size += length;
4107 if (m_head)
4108 m_tail = m_tail->next = n.release();
4109 else
4110 m_head = m_tail = n.release();
4111 m_state = state_t::ok;
4112 return length;
4113 }
4114 catch (const std::bad_alloc&) {
4115 m_state = state_t::fail;
4116 return 0;
4117 }
4118 }
4119
4120 virtual void close()
4121 {
4122 m_size = m_offset = 0;
4123 while (m_head) {
4124 auto p = m_head;
4125 m_head = p->next;
4126 delete p;
4127 }
4128 m_state = state_t::ok;
4129 }
4130
4134 size_t size() const { return m_size; };
4135
4136 protected:
4137 size_t m_offset, m_size;
4138 struct node_t {
4139 node_t* next;
4140 size_t size;
4141#pragma warning(suppress:4200)
4142 uint8_t data[0];
4143 } *m_head, * m_tail;
4144 };
4145
4149 class diag_file : public basic_file {
4150 public:
4151 diag_file(_In_count_(num_files) basic_file* const* files, _In_ size_t num_files) :
4152 basic(num_files ? files[0]->state() : state_t::fail),
4153 m_files(files, files + num_files)
4154 {}
4155
4156 virtual _Success_(return != 0 || length == 0) size_t read(
4157 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4158 {
4159 _Assume_(data || !length);
4160 if (m_files.empty()) {
4161 m_state = state_t::fail;
4162 return 0;
4163 }
4164 size_t result = m_files[0]->read(data, length);
4165 _Assume_(result <= length);
4166 m_state = m_files[0]->state();
4167 if (length > m_tmp.size())
4168 m_tmp.resize(length);
4169 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4170 if (m_files[i]->read(m_tmp.data(), length) != result ||
4171 memcmp(m_tmp.data(), data, result))
4172 throw std::runtime_error("read mismatch");
4173 if (m_files[i]->state() != m_state)
4174 throw std::runtime_error("state mismatch");
4175 }
4176 return result;
4177 }
4178
4179 virtual _Success_(return != 0) size_t write(
4180 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4181 {
4182 if (m_files.empty()) {
4183 m_state = state_t::fail;
4184 return 0;
4185 }
4186 size_t result = m_files[0]->write(data, length);
4187 m_state = m_files[0]->state();
4188 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4189 if (m_files[i]->write(data, length) != result)
4190 throw std::runtime_error("write mismatch");
4191 if (m_files[i]->state() != m_state)
4192 throw std::runtime_error("state mismatch");
4193 }
4194 return result;
4195 }
4196
4197 virtual void flush()
4198 {
4199 if (m_files.empty()) {
4200 m_state = state_t::ok;
4201 return;
4202 }
4203 m_files[0]->flush();
4204 m_state = m_files[0]->state();
4205 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4206 m_files[i]->flush();
4207 if (m_files[i]->state() != m_state)
4208 throw std::runtime_error("state mismatch");
4209 }
4210 }
4211
4212 virtual void close()
4213 {
4214 if (m_files.empty()) {
4215 m_state = state_t::ok;
4216 return;
4217 }
4218 m_files[0]->close();
4219 m_state = m_files[0]->state();
4220 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4221 m_files[i]->close();
4222 if (m_files[i]->state() != m_state)
4223 throw std::runtime_error("state mismatch");
4224 }
4225 m_tmp.clear();
4226 m_tmp.shrink_to_fit();
4227 }
4228
4229 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
4230 {
4231 if (m_files.empty()) {
4232 m_state = state_t::fail;
4233 return fpos_max;
4234 }
4235 fpos_t result = m_files[0]->seek(offset, how);
4236 m_state = m_files[0]->state();
4237 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4238 if (m_files[i]->seek(offset, how) != result)
4239 throw std::runtime_error("seek mismatch");
4240 if (m_files[i]->state() != m_state)
4241 throw std::runtime_error("state mismatch");
4242 }
4243 return result;
4244 }
4245
4246 virtual fpos_t tell() const
4247 {
4248 if (m_files.empty())
4249 return fpos_max;
4250 fpos_t result = m_files[0]->tell();
4251 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4252 if (m_files[i]->tell() != result)
4253 throw std::runtime_error("tell mismatch");
4254 }
4255 return result;
4256 }
4257
4258 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
4259 {
4260 if (m_files.empty())
4261 m_state = state_t::fail;
4262 m_files[0]->lock(offset, length);
4263 m_state = m_files[0]->state();
4264 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4265 m_files[i]->lock(offset, length);
4266 if (m_files[i]->state() != m_state)
4267 throw std::runtime_error("state mismatch");
4268 }
4269 }
4270
4271 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
4272 {
4273 if (m_files.empty())
4274 m_state = state_t::fail;
4275 m_files[0]->unlock(offset, length);
4276 m_state = m_files[0]->state();
4277 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4278 m_files[i]->unlock(offset, length);
4279 if (m_files[i]->state() != m_state)
4280 throw std::runtime_error("state mismatch");
4281 }
4282 }
4283
4284 virtual fsize_t size() const
4285 {
4286 if (m_files.empty())
4287 return fsize_max;
4288 fsize_t result = m_files[0]->size();
4289 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4290 if (m_files[i]->size() != result)
4291 throw std::runtime_error("size mismatch");
4292 }
4293 return result;
4294 }
4295
4296 virtual void truncate()
4297 {
4298 if (m_files.empty())
4299 m_state = state_t::fail;
4300 m_files[0]->truncate();
4301 m_state = m_files[0]->state();
4302 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4303 m_files[i]->truncate();
4304 if (m_files[i]->state() != m_state)
4305 throw std::runtime_error("state mismatch");
4306 }
4307 }
4308
4309 protected:
4310 std::vector<basic_file*> m_files;
4311 std::vector<uint8_t> m_tmp;
4312 };
4313 }
4314}
Encoding converter context.
Definition unicode.hpp:137
locale_t helper class to free_locale when going out of scope.
Definition locale.hpp:69
Provides read-ahead stream capability.
Definition stream.hpp:1246
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:1260
Provides write-back stream capability.
Definition stream.hpp:1313
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:1326
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1350
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:977
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:2196
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:2253
virtual void flush()
Persists volatile element data.
Definition stream.hpp:2311
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:2203
virtual void close()
Closes the stream.
Definition stream.hpp:2300
‍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 readln_and_attach(std::basic_string< T, TR, AX > &str)
Reads stream to the end-of-line or end-of-file and append to str.
Definition stream.hpp:334
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
state_t state() const
Returns stream state after last operation.
Definition stream.hpp:170
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
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(std::basic_string< T_to, TR, 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 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
size_t readln(std::basic_string< T, TR, AX > &str)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:306
basic & read_str(std::basic_string< T, TR, AX > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:465
basic & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:497
size_t readln_and_attach(std::basic_string< T_to, TR, 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
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
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
size_t write_array(const std::basic_string< T_from, TR, AX > &str, charset_encoder< T_from, T_to > &encoder)
Writes array of characters to the stream.
Definition stream.hpp:443
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
basic & write_str(const std::basic_string< T, TR, AX > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:522
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:1384
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1493
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:1414
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:1450
Buffered OS data stream (file, pipe, socket...)
Definition stream.hpp:2325
Cached file.
Definition stream.hpp:1794
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2066
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2045
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:1861
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2071
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2032
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:2080
virtual void close()
Closes the stream.
Definition stream.hpp:1980
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:2102
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2026
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1989
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:1927
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:2038
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:2089
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:2021
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:2094
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2000
Cached file-system file.
Definition stream.hpp:3099
void open(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:3165
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:3143
cached_file(const schar_t *filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3115
cached_file(const std::basic_string< TR, AX > &filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3130
Modifies data on the fly when reading from/writing to a source stream. Could also be used to modify r...
Definition stream.hpp:1012
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1063
virtual void close()
Closes the stream.
Definition stream.hpp:1057
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:1041
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:1049
Compares multiple files to perform the same.
Definition stream.hpp:4149
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:4296
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:4179
virtual void close()
Closes the stream.
Definition stream.hpp:4212
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:4284
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:4258
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:4271
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:4229
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:4246
virtual void flush()
Persists volatile element data.
Definition stream.hpp:4197
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:4156
In-memory FIFO queue.
Definition stream.hpp:4050
virtual void close()
Closes the stream.
Definition stream.hpp:4120
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:4097
size_t size() const
Returns total size of pending data in the queue.
Definition stream.hpp:4134
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:4069
Limits file reading/writing to a predefined window.
Definition stream.hpp:1684
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:1777
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1727
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:1740
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:1733
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:1707
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:1752
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:1693
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:1762
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:1746
virtual void close()
Closes the stream.
Definition stream.hpp:1721
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:1772
File-system file.
Definition stream.hpp:2691
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:2887
static bool readonly(const std::basic_string< TR, AX > &filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3088
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:2969
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2862
file(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2701
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:2983
static bool readonly(const stdex::schar_t *filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3069
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2955
static bool exists(const std::basic_string< TR, AX > &filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3057
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2721
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:3017
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:2997
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2839
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2905
void open(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:2795
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2945
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2800
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:2821
file(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:2713
static bool exists(const stdex::schar_t *filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3041
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1541
fsize_t read_limit
Number of bytes left that may be read from the stream.
Definition stream.hpp:1593
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:1549
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:1570
fsize_t write_limit
Number of bytes left, that can be written to the stream.
Definition stream.hpp:1594
In-memory file.
Definition stream.hpp:3183
void save(const std::basic_string< TR, AX > &filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3505
memory_file & operator=(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3366
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:3886
memory_file(const schar_t *filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3263
memory_file & read_str(std::basic_string< T, TR, AX > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:3594
memory_file(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3282
memory_file & write_str(const std::basic_string< T, TR, AX > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3765
void load(const std::basic_string< TR, AX > &filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3467
size_t m_size
file size
Definition stream.hpp:4037
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:3975
size_t write_stream(basic &stream, size_t amount=SIZE_MAX)
Writes content of another stream.
Definition stream.hpp:3800
uint8_t * m_data
file data
Definition stream.hpp:4034
memory_file & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:3554
virtual void close()
Closes the stream.
Definition stream.hpp:3850
memory_file(const std::basic_string< TR, AX > &filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3275
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:3515
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:3881
size_t m_reserved
reserved file size
Definition stream.hpp:4038
memory_file(size_t size, state_t state=state_t::ok)
Creates an empty file of reserved size.
Definition stream.hpp:3204
void reserve(size_t required, bool tight=false) noexcept
Reallocates memory.
Definition stream.hpp:3404
memory_file(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3337
void write_byte(uint8_t byte, size_t amount=1)
Writes a byte of data.
Definition stream.hpp:3649
memory_file & operator=(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3307
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:3941
size_t m_offset
file pointer
Definition stream.hpp:4036
void save(const schar_t *filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3478
void load(const schar_t *filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3433
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3864
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:3891
memory_file & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:3682
memory_file & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3720
bool m_manage
may reallocate m_data?
Definition stream.hpp:4035
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:3253
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:3625
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:3230
const void * data() const
Returns pointer to data.
Definition stream.hpp:3513
Definition stream.hpp:1160
enum stdex::stream::replicator::worker::op_t op
Operation to perform.
size_t num_written
Number of bytes written.
Definition stream.hpp:1209
size_t length
Byte limit of data to write.
Definition stream.hpp:1208
const void * data
Data to write.
Definition stream.hpp:1207
Replicates writing of the same data to multiple streams.
Definition stream.hpp:1077
void push_back(basic *source)
Adds stream on the list.
Definition stream.hpp:1096
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1153
void remove(basic *source)
Removes stream from the list.
Definition stream.hpp:1104
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:1121
virtual void close()
Closes the stream.
Definition stream.hpp:1148
Socket stream.
Definition stream.hpp:2347
socket_t get() const noexcept
Returns socket handle.
Definition stream.hpp:2403
virtual void close()
Closes the stream.
Definition stream.hpp:2449
socket(int af, int type, int protocol)
Creates a socket.
Definition stream.hpp:2382
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:2405
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:2429
Limits reading from/writing to stream to a predefined window.
Definition stream.hpp:1601
fpos_t write_offset
Number of bytes to discard on write.
Definition stream.hpp:1677
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:1638
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:1609
fpos_t read_offset
Number of bytes to skip on read.
Definition stream.hpp:1676
Operating system object (file, pipe, anything with an OS handle etc.)
Definition system.hpp:99
virtual void close()
Closes object.
Definition system.hpp:140
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:1518
Definition stream.hpp:4138