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 "assert.hpp"
9#include "compat.hpp"
10#include "endian.hpp"
11#include "interval.hpp"
12#include "locale.hpp"
13#include "math.hpp"
14#include "ring.hpp"
15#include "socket.hpp"
16#include "string.hpp"
17#include "unicode.hpp"
18#include <stdint.h>
19#include <stdlib.h>
20#if defined(_WIN32)
21#include "windows.h"
22#include <asptlb.h>
23#include <objidl.h>
24#else
25#include <fcntl.h>
26#include <unistd.h>
27#include <sys/stat.h>
28#endif
29#include <chrono>
30#include <condition_variable>
31#include <list>
32#include <memory>
33#include <set>
34#include <string>
35#include <thread>
36#include <vector>
37
38#if defined(__GNUC__)
39#pragma GCC diagnostic push
40#pragma GCC diagnostic ignored "-Wunknown-pragmas"
41#endif
42
43#if !defined(SET_FILE_OP_TIMES) && defined(RDAT_BELEZI_CAS_DOSTOPA_VER)
44#define SET_FILE_OP_TIMES 1
45#pragma message("RDAT_BELEZI_CAS_DOSTOPA_VER is deprecated. Use SET_FILE_OP_TIMES instead.")
46#elif !defined(SET_FILE_OP_TIMES)
47#define SET_FILE_OP_TIMES 0
48#endif
49#if !defined(CHECK_STREAM_STATE) && defined(RDAT_NE_PREVERJAJ_STANJA_VER)
50#define CHECK_STREAM_STATE 0
51#pragma message("RDAT_NE_PREVERJAJ_EOF_VER is deprecated. Use CHECK_STREAM_STATE=0 instead.")
52#else
53#define CHECK_STREAM_STATE 1
54#endif
55
56 namespace stdex
57{
58 namespace stream
59 {
63 enum class state_t {
64 ok = 0,
65 eof,
66 fail,
67 };
68
72 using fsize_t = uint64_t;
73 constexpr fsize_t fsize_max = UINT64_MAX;
74
75 constexpr size_t iterate_count = 0x10;
76 constexpr size_t default_block_size = 0x10000;
77 constexpr utf16_t utf16_bom = u'\ufeff';
78 constexpr utf32_t utf32_bom = U'\ufeff';
79 constexpr const char utf8_bom[3] = { '\xef', '\xbb', '\xbf' };
80
84 class basic
85 {
86 public:
87 basic(_In_ state_t state = state_t::ok) : m_state(state) {}
88
89 virtual ~basic() noexcept(false) {}
90
102 virtual _Success_(return != 0 || length == 0) size_t read(
103 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
104 {
105 _Unreferenced_(data);
106 _Unreferenced_(length);
107 m_state = state_t::fail;
108 return 0;
109 }
110
120 virtual _Success_(return != 0) size_t write(
121 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
122 {
123 _Unreferenced_(data);
124 _Unreferenced_(length);
125 m_state = state_t::fail;
126 return 0;
127 }
128
132 virtual void flush()
133 {
134 m_state = state_t::ok;
135 }
136
140 virtual void close()
141 {
142 m_state = state_t::ok;
143 }
144
148 virtual void skip(_In_ fsize_t amount)
149 {
150 if (amount == 1)
151 read_byte();
152 else if (amount < iterate_count) {
153 for (size_t i = 0; i < static_cast<size_t>(amount); i++) {
154 read_byte();
155 if (!ok()) _Unlikely_
156 break;
157 }
158 }
159 else {
160 size_t block = static_cast<size_t>(std::min<fsize_t>(amount, default_block_size));
161 try {
162 std::unique_ptr<uint8_t[]> dummy(new uint8_t[block]);
163 while (amount) {
164 amount -= read_array(dummy.get(), sizeof(uint8_t), static_cast<size_t>(std::min<fsize_t>(amount, block)));
165 if (!ok()) _Unlikely_
166 break;
167 }
168 }
169 catch (const std::bad_alloc&) { m_state = state_t::fail; }
170 }
171 }
172
176 state_t state() const { return m_state; };
177
181 bool ok() const { return m_state == state_t::ok; };
182
190 virtual std::vector<uint8_t> read_remainder(_In_ size_t max_length = SIZE_MAX)
191 {
192 std::vector<uint8_t> result;
193 size_t offset, length;
194 offset = 0;
195 length = default_block_size;
196 while (offset < max_length) {
197 length = std::min(length, max_length);
198 try { result.resize(length); }
199 catch (const std::bad_alloc&) {
200 m_state = state_t::fail;
201 return result;
202 }
203 auto num_read = read_array(result.data() + offset, sizeof(uint8_t), length - offset);
204 offset += num_read;
205 if (!ok()) _Unlikely_
206 break;
207 length += default_block_size;
208 }
209 result.resize(offset);
210 return result;
211 }
212
216 uint8_t read_byte()
217 {
218 uint8_t byte;
219 if (read_array(&byte, sizeof(byte), 1) == 1)
220 return byte;
221 throw std::system_error(sys_error(), std::system_category(), "failed to read");
222 }
223
227 void write_byte(_In_ uint8_t byte, _In_ fsize_t amount = 1)
228 {
229 if (amount == 1)
230 write(&byte, sizeof(uint8_t));
231 else if (amount < iterate_count) {
232 for (size_t i = 0; i < static_cast<size_t>(amount); i++) {
233 write(&byte, sizeof(uint8_t));
234 if (!ok()) _Unlikely_
235 break;
236 }
237 }
238 else {
239 size_t block = static_cast<size_t>(std::min<fsize_t>(amount, default_block_size));
240 try {
241 std::unique_ptr<uint8_t[]> dummy(new uint8_t[block]);
242 memset(dummy.get(), byte, block);
243 while (amount) {
244 amount -= write_array(dummy.get(), sizeof(uint8_t), static_cast<size_t>(std::min<fsize_t>(amount, block)));
245 if (!ok()) _Unlikely_
246 break;
247 }
248 }
249 catch (const std::bad_alloc&) { m_state = state_t::fail; }
250 }
251 }
252
264 template <class T>
265 basic& read_data(_Out_ T& data)
266 {
267 if (!ok()) _Unlikely_ {
268 data = 0;
269 return *this;
270 }
271 if (read_array(&data, sizeof(T), 1) == 1)
272 (void)LE2HE(&data);
273 else {
274 data = 0;
275 if (ok())
276 m_state = state_t::eof;
277 }
278 return *this;
279 }
280
292 template <class T>
293 basic& write_data(_In_ const T data)
294 {
295 if (!ok()) _Unlikely_
296 return *this;
297#if BYTE_ORDER == BIG_ENDIAN
298 T data_le = HE2LE(data);
299 write(&data_le, sizeof(T));
300#else
301 write(&data, sizeof(T));
302#endif
303 return *this;
304 }
305
311 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
312 size_t readln(_Inout_ std::basic_string<T, TR, AX>& str)
313 {
314 str.clear();
315 return readln_and_attach(str);
316 }
317
323 template<class T_from, class T_to, class TR = std::char_traits<T_to>, class AX = std::allocator<T_to>>
324 size_t readln(_Inout_ std::basic_string<T_to, TR, AX>& str, _In_ charset_encoder<T_from, T_to>& encoder)
325 {
326 if (encoder.from_encoding() == encoder.to_encoding())
327 return readln(str);
328 std::basic_string<T_from> tmp;
329 readln_and_attach(tmp);
330 encoder.strcpy(str, tmp);
331 return str.size();
332 }
333
339 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
340 size_t readln_and_attach(_Inout_ std::basic_string<T, TR, AX>& str)
341 {
342 bool initial = true;
343 T chr = static_cast<T>(0), previous = static_cast<T>(0);
344 do {
345 read_array(&chr, sizeof(T), 1);
346 if (!initial && !(previous == static_cast<T>('\r') && chr == static_cast<T>('\n')))
347 str += previous;
348 else
349 initial = false;
350 previous = chr;
351 } while (ok() && chr != static_cast<T>('\n'));
352 return str.size();
353 }
354
360 template<class T_from, class T_to, class TR = std::char_traits<T_to>, class AX = std::allocator<T_to>>
361 size_t readln_and_attach(_Inout_ std::basic_string<T_to, TR, AX>& str, _In_ charset_encoder<T_from, T_to>& encoder)
362 {
363 if (encoder.from_encoding() == encoder.to_encoding())
364 return readln_and_attach(str);
365 std::basic_string<T_from> tmp;
366 readln_and_attach(tmp);
367 encoder.strcat(str, tmp);
368 return str.size();
369 }
370
376 size_t read_array(_Out_writes_bytes_(size* count) void* array, _In_ size_t size, _In_ size_t count)
377 {
378 for (size_t to_read = mul(size, count);;) {
379 size_t num_read = read(array, to_read);
380 to_read -= num_read;
381 if (!to_read)
382 return count;
383 if (!ok()) _Unlikely_
384 return count - to_read / size;
385 reinterpret_cast<uint8_t*&>(array) += num_read;
386 }
387 }
388
394 size_t write_array(_In_reads_bytes_opt_(size* count) const void* array, _In_ size_t size, _In_ size_t count)
395 {
396 return write(array, mul(size, count)) / size;
397 }
398
407 template <class T_from, class T_to>
408 size_t write_array(_In_z_ const T_from* str, _In_ charset_encoder<T_from, T_to>& encoder)
409 {
410 if (!ok()) _Unlikely_
411 return 0;
412 size_t num_chars = stdex::strlen(str);
413 if (encoder.from_encoding() == encoder.to_encoding())
414 return write_array(str, sizeof(T_from), num_chars);
415 std::basic_string<T_to> tmp(encoder.convert(str, num_chars));
416 return write_array(tmp.data(), sizeof(T_to), tmp.size());
417 }
418
428 template <class T_from, class T_to>
429 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)
430 {
431 if (!ok()) _Unlikely_
432 return 0;
433 num_chars = stdex::strnlen(str, num_chars);
434 if (encoder.from_encoding() == encoder.to_encoding())
435 return write_array(str, sizeof(T_from), num_chars);
436 std::basic_string<T_to> tmp(encoder.convert(str, num_chars));
437 return write_array(tmp.data(), sizeof(T_to), tmp.size());
438 }
439
448 template<class T_from, class T_to, class TR = std::char_traits<T_from>, class AX = std::allocator<T_from>>
449 size_t write_array(_In_ const std::basic_string<T_from, TR, AX>& str, _In_ charset_encoder<T_from, T_to>& encoder)
450 {
451 if (!ok()) _Unlikely_
452 return 0;
453 if (encoder.from_encoding() == encoder.to_encoding())
454 return write_array(str.data(), sizeof(T_from), str.size());
455 std::basic_string<T_to> tmp(encoder.convert(str));
456 return write_array(tmp.data(), sizeof(T_to), tmp.size());
457 }
458
470 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
471 basic& read_str(_Out_ std::basic_string<T, TR, AX>& data)
472 {
473 data.clear();
474 if (!ok()) _Unlikely_
475 return *this;
476 uint32_t num_chars;
477 read_data(num_chars);
478 if (!ok()) _Unlikely_
479 return *this;
480 data.reserve(num_chars);
481 for (;;) {
482 constexpr uint32_t buf_chars = 0x400;
483 T buf[buf_chars];
484 uint32_t num_read = static_cast<uint32_t>(read_array(buf, sizeof(T), std::min<uint32_t>(num_chars, buf_chars)));
485 data.append(buf, num_read);
486 num_chars -= num_read;
487 if (!num_chars || !ok())
488 return *this;
489 }
490 }
491
503 template <class T>
504 basic& write_str(_In_z_ const T* data)
505 {
506 // Stream state will be checked in write_data.
507 size_t num_chars = stdex::strlen(data);
508 if (num_chars > UINT32_MAX)
509 throw std::invalid_argument("string too long");
510 write_data(static_cast<uint32_t>(num_chars));
511 if (!ok()) _Unlikely_
512 return *this;
513 write_array(data, sizeof(T), num_chars);
514 return *this;
515 }
516
528 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
529 basic& write_str(_In_ const std::basic_string<T, TR, AX>& data)
530 {
531 // Stream state will be checked in write_data.
532 size_t num_chars = data.size();
533 if (num_chars > UINT32_MAX)
534 throw std::invalid_argument("string too long");
535 write_data(static_cast<uint32_t>(num_chars));
536 if (!ok()) _Unlikely_
537 return *this;
538 write_array(data.data(), sizeof(T), num_chars);
539 return *this;
540 }
541
542#ifdef _WIN32
548 size_t write_sa(_In_ LPSAFEARRAY sa)
549 {
550 safearray_accessor_with_size<uint8_t> a(sa);
551 return write(a.data(), a.size());
552 }
553#endif
554
560 fsize_t write_stream(_Inout_ basic& stream, _In_ fsize_t amount = fsize_max)
561 {
562 std::unique_ptr<uint8_t[]> data(new uint8_t[static_cast<size_t>(std::min<fsize_t>(amount, default_block_size))]);
563 fsize_t num_copied = 0, to_write = amount;
564 m_state = state_t::ok;
565 while (to_write) {
566 size_t num_read = stream.read(data.get(), static_cast<size_t>(std::min<fsize_t>(default_block_size, to_write)));
567 size_t num_written = write(data.get(), num_read);
568 num_copied += num_written;
569 to_write -= num_written;
570 if (stream.m_state == state_t::eof) {
571 // EOF is not an error.
572 m_state = state_t::ok;
573 break;
574 }
575 m_state = stream.m_state;
576 if (!ok())
577 break;
578 }
579 return num_copied;
580 }
581
585 void write_charset(_In_ charset_id charset)
586 {
587 if (charset == charset_id::utf32)
588 write_data(utf32_bom);
589 else if (charset == charset_id::utf16)
590 write_data(utf16_bom);
591 else if (charset == charset_id::utf8)
592 write_array(utf8_bom, sizeof(utf8_bom), 1);
593 }
594
600 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, ...)
601 {
602 va_list params;
603 va_start(params, locale);
604 size_t num_chars = write_vsprintf(format, locale, params);
605 va_end(params);
606 return num_chars;
607 }
608
614 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, ...)
615 {
616 va_list params;
617 va_start(params, locale);
618 size_t num_chars = write_vsprintf(format, locale, params);
619 va_end(params);
620 return num_chars;
621 }
622
628 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, _In_ va_list params)
629 {
630 std::string tmp;
631 tmp.reserve(default_block_size);
632 vappendf(tmp, format, locale, params);
633 return write_array(tmp.data(), sizeof(char), tmp.size());
634 }
635
641 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, _In_ va_list params)
642 {
643 std::wstring tmp;
644 tmp.reserve(default_block_size);
645 vappendf(tmp, format, locale, params);
646 return write_array(tmp.data(), sizeof(wchar_t), tmp.size());
647 }
648
649 basic& operator >>(_Out_ int8_t& data) { return read_data(data); }
650 basic& operator <<(_In_ const int8_t data) { return write_data(data); }
651 basic& operator >>(_Out_ int16_t& data) { return read_data(data); }
652 basic& operator <<(_In_ const int16_t data) { return write_data(data); }
653 basic& operator >>(_Out_ int32_t& data) { return read_data(data); }
654 basic& operator <<(_In_ const int32_t data) { return write_data(data); }
655 basic& operator >>(_Out_ int64_t& data) { return read_data(data); }
656 basic& operator <<(_In_ const int64_t data) { return write_data(data); }
657 basic& operator >>(_Out_ uint8_t& data) { return read_data(data); }
658 basic& operator <<(_In_ const uint8_t data) { return write_data(data); }
659 basic& operator >>(_Out_ uint16_t& data) { return read_data(data); }
660 basic& operator <<(_In_ const uint16_t data) { return write_data(data); }
661 basic& operator >>(_Out_ uint32_t& data) { return read_data(data); }
662 basic& operator <<(_In_ const uint32_t data) { return write_data(data); }
663 basic& operator >>(_Out_ uint64_t& data) { return read_data(data); }
664 basic& operator <<(_In_ const uint64_t data) { return write_data(data); }
665 basic& operator >>(_Out_ float& data) { return read_data(data); }
666 basic& operator <<(_In_ const float data) { return write_data(data); }
667 basic& operator >>(_Out_ double& data) { return read_data(data); }
668 basic& operator <<(_In_ const double data) { return write_data(data); }
669 basic& operator >>(_Out_ char& data) { return read_data(data); }
670 basic& operator <<(_In_ const char data) { return write_data(data); }
671#ifdef _NATIVE_WCHAR_T_DEFINED
672 basic& operator >>(_Out_ wchar_t& data) { return read_data(data); }
673 basic& operator <<(_In_ const wchar_t data) { return write_data(data); }
674#endif
675 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
676 basic& operator >>(_Out_ std::basic_string<T, TR, AX>& data) { return read_str(data); }
677 template <class T>
678 basic& operator <<(_In_ const T* data) { return write_str(data); }
679 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
680 basic& operator <<(_In_ const std::basic_string<T, TR, AX>& data) { return write_str(data); }
681
682 template <class T, class AX = std::allocator<T>>
683 basic& operator <<(_In_ const std::vector<T, AX>& data)
684 {
685 size_t num = data.size();
686 if (num > UINT32_MAX) _Unlikely_
687 throw std::invalid_argument("collection too big");
688 *this << static_cast<uint32_t>(num);
689 for (auto& el : data)
690 *this << el;
691 return *this;
692 }
693
694 template <class T, class AX = std::allocator<T>>
695 basic& operator >>(_Out_ std::vector<T, AX>& data)
696 {
697 data.clear();
698 uint32_t num;
699 *this >> num;
700 if (!ok()) _Unlikely_
701 return *this;
702 data.reserve(num);
703 for (uint32_t i = 0; i < num; ++i) {
704 T el;
705 *this >> el;
706 if (!ok()) _Unlikely_
707 return *this;
708 data.push_back(std::move(el));
709 }
710 }
711
712 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
713 basic& operator <<(_In_ const std::set<KEY, PR, AX>& data)
714 {
715 size_t num = data.size();
716 if (num > UINT32_MAX) _Unlikely_
717 throw std::invalid_argument("collection too big");
718 *this << static_cast<uint32_t>(num);
719 for (auto& el : data)
720 *this << el;
721 return *this;
722 }
723
724 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
725 basic& operator >>(_Out_ std::set<KEY, PR, AX>& data)
726 {
727 data.clear();
728 uint32_t num;
729 *this >> num;
730 if (!ok()) _Unlikely_
731 return *this;
732 for (uint32_t i = 0; i < num; ++i) {
733 KEY el;
734 *this >> el;
735 if (!ok()) _Unlikely_
736 return *this;
737 data.insert(std::move(el));
738 }
739 }
740
741 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
742 basic& operator <<(_In_ const std::multiset<KEY, PR, AX>& data)
743 {
744 size_t num = data.size();
745 if (num > UINT32_MAX) _Unlikely_
746 throw std::invalid_argument("collection too big");
747 *this << static_cast<uint32_t>(num);
748 for (auto& el : data)
749 *this << el;
750 return *this;
751 }
752
753 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
754 basic& operator >>(_Out_ std::multiset<KEY, PR, AX>& data)
755 {
756 data.clear();
757 uint32_t num;
758 *this >> num;
759 if (!ok()) _Unlikely_
760 return *this;
761 for (uint32_t i = 0; i < num; ++i) {
762 KEY el;
763 *this >> el;
764 if (!ok()) _Unlikely_
765 return *this;
766 data.insert(std::move(el));
767 }
768 return *this;
769 }
770
771 protected:
772 state_t m_state;
773 };
774
778 using fpos_t = uint64_t;
779 constexpr fpos_t fpos_max = UINT64_MAX;
780 constexpr fpos_t fpos_min = 0;
781
785 using foff_t = int64_t;
786 constexpr foff_t foff_max = INT64_MAX;
787 constexpr foff_t foff_min = INT64_MIN;
788
792 enum class seek_t {
793#ifdef _WIN32
794 beg = FILE_BEGIN,
795 cur = FILE_CURRENT,
796 end = FILE_END
797#else
798 beg = SEEK_SET,
799 cur = SEEK_CUR,
800 end = SEEK_END
801#endif
802 };
803
804#if _HAS_CXX20
805 using clock = std::chrono::file_clock;
806#else
807 using clock = std::chrono::system_clock;
808#endif
809 using time_point = std::chrono::time_point<clock>;
810
814 class basic_file : virtual public basic
815 {
816 public:
817 virtual std::vector<uint8_t> read_remainder(_In_ size_t max_length = SIZE_MAX)
818 {
819 size_t length = std::min<size_t>(max_length, static_cast<size_t>(size() - tell()));
820 std::vector<uint8_t> result;
821 try { result.resize(length); }
822 catch (const std::bad_alloc&) {
823 m_state = state_t::fail;
824 return result;
825 }
826 result.resize(read_array(result.data(), sizeof(uint8_t), length));
827 return result;
828 }
829
835 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg) = 0;
836
842 fpos_t seekbeg(_In_ fpos_t offset)
843 {
844 return seek(static_cast<foff_t>(offset), seek_t::beg);
845 }
846
852 fpos_t seekcur(_In_ foff_t offset) { return seek(offset, seek_t::cur); }
853
859 fpos_t seekend(_In_ foff_t offset) { return seek(offset, seek_t::end); }
860
861 virtual void skip(_In_ fsize_t amount)
862 {
863 if (amount > foff_max)
864 throw std::invalid_argument("file offset too big");
865 seek(static_cast<foff_t>(amount), seek_t::cur);
866 }
867
874 virtual fpos_t tell() const = 0;
875
879 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
880 {
881 _Unreferenced_(offset);
882 _Unreferenced_(length);
883 throw std::domain_error("not implemented");
884 }
885
889 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
890 {
891 _Unreferenced_(offset);
892 _Unreferenced_(length);
893 throw std::domain_error("not implemented");
894 }
895
900 virtual fsize_t size() const = 0;
901
905 virtual void truncate() = 0;
906
910 virtual time_point ctime() const
911 {
912 return time_point::min();
913 }
914
918 virtual time_point atime() const
919 {
920 return time_point::min();
921 }
922
926 virtual time_point mtime() const
927 {
928 return time_point::min();
929 }
930
934 virtual void set_ctime(time_point date)
935 {
936 _Unreferenced_(date);
937 throw std::domain_error("not implemented");
938 }
939
943 virtual void set_atime(time_point date)
944 {
945 _Unreferenced_(date);
946 throw std::domain_error("not implemented");
947 }
948
952 virtual void set_mtime(time_point date)
953 {
954 _Unreferenced_(date);
955 throw std::domain_error("not implemented");
956 }
957
958#ifdef _WIN32
962 LPSAFEARRAY read_sa()
963 {
964 stdex_assert(size() <= SIZE_MAX);
965 if (size() > ULONG_MAX)
966 throw std::range_error("data too big");
967 ULONG length = static_cast<ULONG>(size());
968 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(SafeArrayCreateVector(VT_UI1, 0, length));
969 if (!sa) _Unlikely_
970 throw std::runtime_error("SafeArrayCreateVector failed");
971 if (seek(0) != 0) _Unlikely_
972 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
973 safearray_accessor<void> a(sa.get());
974 if (read_array(a.data(), 1, length) != length)
975 throw std::system_error(sys_error(), std::system_category(), "failed to read");
976 return sa.release();
977 }
978#endif
979
985 charset_id read_charset(_In_ charset_id default_charset = charset_id::system)
986 {
987 if (seek(0) != 0) _Unlikely_
988 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
989 utf32_t id_utf32;
990 read_array(&id_utf32, sizeof(utf32_t), 1);
991 if (ok() && id_utf32 == utf32_bom)
992 return charset_id::utf32;
993
994 if (seek(0) != 0) _Unlikely_
995 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
996 utf16_t id_utf16;
997 read_array(&id_utf16, sizeof(utf16_t), 1);
998 if (ok() && id_utf16 == utf16_bom)
999 return charset_id::utf16;
1000
1001 if (seek(0) != 0) _Unlikely_
1002 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1003 char id_utf8[3] = { 0 };
1004 read_array(id_utf8, sizeof(id_utf8), 1);
1005 if (ok() && strncmp(id_utf8, _countof(id_utf8), utf8_bom, _countof(utf8_bom)) == 0)
1006 return charset_id::utf8;
1007
1008 if (seek(0) != 0) _Unlikely_
1009 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1010 return default_charset;
1011 }
1012 };
1013
1019 class converter : public basic
1020 {
1021 protected:
1023#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1024 explicit converter() : basic(state_t::fail) {}
1025
1026 void init(_Inout_ basic& source)
1027 {
1028 m_source = &source;
1029 init();
1030 }
1031
1032 void init()
1033 {
1034 m_state = m_source->state();
1035 }
1036
1037 void done()
1038 {
1039 m_source = nullptr;
1040 }
1042
1043 public:
1044 converter(_Inout_ basic& source) :
1045 basic(source.state()),
1046 m_source(&source)
1047 {}
1048
1049 virtual _Success_(return != 0 || length == 0) size_t read(
1050 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1051 {
1052 size_t num_read = m_source->read(data, length);
1053 m_state = m_source->state();
1054 return num_read;
1055 }
1056
1057 virtual _Success_(return != 0) size_t write(
1058 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1059 {
1060 size_t num_written = m_source->write(data, length);
1061 m_state = m_source->state();
1062 return num_written;
1063 }
1064
1065 virtual void close()
1066 {
1067 m_source->close();
1068 m_state = m_source->state();
1069 }
1070
1071 virtual void flush()
1072 {
1073 m_source->flush();
1074 m_state = m_source->state();
1075 }
1076
1077 protected:
1078 basic* m_source;
1079 };
1080
1084 class replicator : public basic
1085 {
1086 public:
1087 virtual ~replicator()
1088 {
1089 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1090 auto _w = w->get();
1091 {
1092 const std::lock_guard<std::mutex> lk(_w->mutex);
1093 _w->op = worker::op_t::quit;
1094 }
1095 _w->cv.notify_one();
1096 }
1097 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w)
1098 w->get()->join();
1099 }
1100
1104 void push_back(_In_ basic* source)
1105 {
1106 m_workers.push_back(std::unique_ptr<worker>(new worker(source)));
1107 }
1108
1112 void remove(basic* source)
1113 {
1114 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1115 auto _w = w->get();
1116 if (_w->source == source) {
1117 {
1118 const std::lock_guard<std::mutex> lk(_w->mutex);
1119 _w->op = worker::op_t::quit;
1120 }
1121 _w->cv.notify_one();
1122 _w->join();
1123 m_workers.erase(w);
1124 return;
1125 }
1126 }
1127 }
1128
1129 virtual _Success_(return != 0) size_t write(
1130 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1131 {
1132 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1133 auto _w = w->get();
1134 {
1135 const std::lock_guard<std::mutex> lk(_w->mutex);
1136 _w->op = worker::op_t::write;
1137 _w->data = data;
1138 _w->length = length;
1139 }
1140 _w->cv.notify_one();
1141 }
1142 size_t num_written = length;
1143 m_state = state_t::ok;
1144 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1145 auto _w = w->get();
1146 std::unique_lock<std::mutex> lk(_w->mutex);
1147 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1148 if (_w->num_written < num_written)
1149 num_written = _w->num_written;
1150 if (ok() && !_w->source->ok())
1151 m_state = _w->source->state();
1152 }
1153 return num_written;
1154 }
1155
1156 virtual void close()
1157 {
1158 foreach_worker(worker::op_t::close);
1159 }
1160
1161 virtual void flush()
1162 {
1163 foreach_worker(worker::op_t::flush);
1164 }
1165
1166 protected:
1167 class worker : public std::thread
1168 {
1169 public:
1170 worker(_In_ basic* _source) :
1171 source(_source),
1172 op(op_t::noop),
1173 data(nullptr),
1174 length(0),
1175 num_written(0)
1176 {
1177 *static_cast<std::thread*>(this) = std::thread([](_Inout_ worker& w) { w.process_op(); }, std::ref(*this));
1178 }
1179
1180 protected:
1181 void process_op()
1182 {
1183 for (;;) {
1184 std::unique_lock<std::mutex> lk(mutex);
1185 cv.wait(lk, [&] {return op != op_t::noop; });
1186 switch (op) {
1187 case op_t::quit:
1188 return;
1189 case op_t::write:
1190 num_written = source->write(data, length);
1191 break;
1192 case op_t::close:
1193 source->close();
1194 break;
1195 case op_t::flush:
1196 source->flush();
1197 break;
1198 case op_t::noop:;
1199 }
1200 op = op_t::noop;
1201 lk.unlock();
1202 cv.notify_one();
1203 }
1204 }
1205
1206 public:
1207 basic* source;
1208 enum class op_t {
1209 noop = 0,
1210 quit,
1211 write,
1212 close,
1213 flush,
1214 } op;
1215 const void* data;
1216 size_t length;
1217 size_t num_written;
1218 std::mutex mutex;
1219 std::condition_variable cv;
1220 };
1221
1222 void foreach_worker(_In_ worker::op_t op)
1223 {
1224 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1225 auto _w = w->get();
1226 {
1227 const std::lock_guard<std::mutex> lk(_w->mutex);
1228 _w->op = op;
1229 }
1230 _w->cv.notify_one();
1231 }
1232 m_state = state_t::ok;
1233 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1234 auto _w = w->get();
1235 std::unique_lock<std::mutex> lk(_w->mutex);
1236 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1237 if (ok())
1238 m_state = _w->source->state();
1239 }
1240 }
1241
1242 std::list<std::unique_ptr<worker>> m_workers;
1243 };
1244
1245 constexpr size_t default_async_limit = 0x100000;
1246
1252 template <size_t N_cap = default_async_limit>
1253 class async_reader : public converter
1254 {
1255 public:
1256 async_reader(_Inout_ basic& source) :
1257 converter(source),
1258 m_worker([](_Inout_ async_reader& w) { w.process(); }, std::ref(*this))
1259 {}
1260
1261 virtual ~async_reader()
1262 {
1263 m_ring.quit();
1264 m_worker.join();
1265 }
1266
1267#pragma warning(suppress: 6101) // See [1] below
1268 virtual _Success_(return != 0 || length == 0) size_t read(
1269 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1270 {
1271 stdex_assert(data || !length);
1272 for (size_t to_read = length;;) {
1273 uint8_t* ptr; size_t num_read;
1274 std::tie(ptr, num_read) = m_ring.front();
1275 if (!ptr) _Unlikely_ {
1276 m_state = to_read < length || !length ? state_t::ok : m_source->state();
1277 return length - to_read; // [1] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
1278 }
1279 if (to_read < num_read)
1280 num_read = to_read;
1281 memcpy(data, ptr, num_read);
1282 m_ring.pop(num_read);
1283 to_read -= num_read;
1284 if (!to_read) {
1285 m_state = state_t::ok;
1286 return length;
1287 }
1288 reinterpret_cast<uint8_t*&>(data) += num_read;
1289 }
1290 }
1291
1292 protected:
1293 void process()
1294 {
1295 for (;;) {
1296 uint8_t* ptr; size_t num_write;
1297 std::tie(ptr, num_write) = m_ring.back();
1298 if (!ptr) _Unlikely_
1299 break;
1300 num_write = m_source->read(ptr, num_write);
1301 m_ring.push(num_write);
1302 if (!m_source->ok()) {
1303 m_ring.quit();
1304 break;
1305 }
1306 }
1307 }
1308
1309 protected:
1310 ring<uint8_t, N_cap> m_ring;
1311 std::thread m_worker;
1312 };
1313
1319 template <size_t N_cap = default_async_limit>
1320 class async_writer : public converter
1321 {
1322 public:
1323 async_writer(_Inout_ basic& source) :
1324 converter(source),
1325 m_worker([](_Inout_ async_writer& w) { w.process(); }, std::ref(*this))
1326 {}
1327
1328 virtual ~async_writer()
1329 {
1330 m_ring.quit();
1331 m_worker.join();
1332 }
1333
1334 virtual _Success_(return != 0) size_t write(
1335 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1336 {
1337 stdex_assert(data || !length);
1338 for (size_t to_write = length;;) {
1339 uint8_t* ptr; size_t num_write;
1340 std::tie(ptr, num_write) = m_ring.back();
1341 if (!ptr) _Unlikely_ {
1342 m_state = state_t::fail;
1343 return length - to_write;
1344 }
1345 if (to_write < num_write)
1346 num_write = to_write;
1347 memcpy(ptr, data, num_write);
1348 m_ring.push(num_write);
1349 to_write -= num_write;
1350 if (!to_write) {
1351 m_state = state_t::ok;
1352 return length;
1353 }
1354 reinterpret_cast<const uint8_t*&>(data) += num_write;
1355 }
1356 }
1357
1358 virtual void flush()
1359 {
1360 m_ring.sync();
1361 converter::flush();
1362 }
1363
1364 protected:
1365 void process()
1366 {
1367 for (;;) {
1368 uint8_t* ptr; size_t num_read;
1369 std::tie(ptr, num_read) = m_ring.front();
1370 if (!ptr)
1371 break;
1372 num_read = m_source->write(ptr, num_read);
1373 m_ring.pop(num_read);
1374 if (!m_source->ok()) {
1375 m_ring.quit();
1376 break;
1377 }
1378 }
1379 }
1380
1381 protected:
1382 ring<uint8_t, N_cap> m_ring;
1383 std::thread m_worker;
1384 };
1385
1386 constexpr size_t default_buffer_size = 0x400;
1387
1391 class buffer : public converter
1392 {
1393 protected:
1395 explicit buffer(_In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1396 converter(),
1397 m_read_buffer(read_buffer_size),
1398 m_write_buffer(write_buffer_size)
1399 {}
1400
1401 void done()
1402 {
1403 if (m_source)
1404 flush_write();
1405 converter::done();
1406 }
1408
1409 public:
1410 buffer(_Inout_ basic& source, _In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1411 converter(source),
1412 m_read_buffer(read_buffer_size),
1413 m_write_buffer(write_buffer_size)
1414 {}
1415
1416 virtual ~buffer()
1417 {
1418 if (m_source)
1419 flush_write();
1420 }
1421
1422 virtual _Success_(return != 0 || length == 0) size_t read(
1423 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1424 {
1425 stdex_assert(data || !length);
1426 for (size_t to_read = length;;) {
1427 size_t buffer_size = m_read_buffer.tail - m_read_buffer.head;
1428 if (to_read <= buffer_size) {
1429 memcpy(data, m_read_buffer.data + m_read_buffer.head, to_read);
1430 m_read_buffer.head += to_read;
1431 m_state = state_t::ok;
1432 return length;
1433 }
1434 if (buffer_size) {
1435 memcpy(data, m_read_buffer.data + m_read_buffer.head, buffer_size);
1436 reinterpret_cast<uint8_t*&>(data) += buffer_size;
1437 to_read -= buffer_size;
1438 }
1439 m_read_buffer.head = 0;
1440 if (to_read > m_read_buffer.capacity) {
1441 // When needing to read more data than buffer capacity, bypass the buffer.
1442 m_read_buffer.tail = 0;
1443 to_read -= m_source->read(data, to_read);
1444 m_state = to_read < length ? state_t::ok : m_source->state();
1445 return length - to_read;
1446 }
1447 m_read_buffer.tail = m_source->read(m_read_buffer.data, m_read_buffer.capacity);
1448 if (m_read_buffer.tail < m_read_buffer.capacity && m_read_buffer.tail < to_read) _Unlikely_ {
1449 memcpy(data, m_read_buffer.data, m_read_buffer.tail);
1450 m_read_buffer.head = m_read_buffer.tail;
1451 to_read -= m_read_buffer.tail;
1452 m_state = to_read < length ? state_t::ok : m_source->state();
1453 return length - to_read;
1454 }
1455 }
1456 }
1457
1458 virtual _Success_(return != 0) size_t write(
1459 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1460 {
1461 stdex_assert(data || !length);
1462 if (!length) _Unlikely_ {
1463 // Pass null writes (zero-byte length). Null write operations have special meaning with with Windows pipes.
1464 flush_write();
1465 if (!ok()) _Unlikely_
1466 return 0;
1467 converter::write(nullptr, 0);
1468 return 0;
1469 }
1470
1471 for (size_t to_write = length;;) {
1472 size_t available_buffer = m_write_buffer.capacity - m_write_buffer.tail;
1473 if (to_write <= available_buffer) {
1474 memcpy(m_write_buffer.data + m_write_buffer.tail, data, to_write);
1475 m_write_buffer.tail += to_write;
1476 m_state = state_t::ok;
1477 return length;
1478 }
1479 if (available_buffer) {
1480 memcpy(m_write_buffer.data + m_write_buffer.tail, data, available_buffer);
1481 reinterpret_cast<const uint8_t*&>(data) += available_buffer;
1482 to_write -= available_buffer;
1483 m_write_buffer.tail += available_buffer;
1484 }
1485 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1486 if (buffer_size) {
1487 m_write_buffer.head += converter::write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1488 if (m_write_buffer.head == m_write_buffer.tail)
1489 m_write_buffer.head = m_write_buffer.tail = 0;
1490 else
1491 return length - to_write;
1492 }
1493 if (to_write > m_write_buffer.capacity) {
1494 // When needing to write more data than buffer capacity, bypass the buffer.
1495 to_write -= converter::write(data, to_write);
1496 return length - to_write;
1497 }
1498 }
1499 }
1500
1501 virtual void flush()
1502 {
1503 flush_write();
1504 if (ok())
1505 converter::flush();
1506 }
1507
1508 protected:
1509 void flush_write()
1510 {
1511 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1512 if (buffer_size) {
1513 m_write_buffer.head += m_source->write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1514 if (m_write_buffer.head == m_write_buffer.tail) {
1515 m_write_buffer.head = 0;
1516 m_write_buffer.tail = 0;
1517 }
1518 else {
1519 m_state = m_source->state();
1520 return;
1521 }
1522 }
1523 m_state = state_t::ok;
1524 }
1525
1526 struct buffer_t {
1527 uint8_t* data;
1528 size_t head, tail, capacity;
1529
1530 buffer_t(_In_ size_t buffer_size) :
1531 head(0),
1532 tail(0),
1533 capacity(buffer_size),
1534 data(buffer_size ? new uint8_t[buffer_size] : nullptr)
1535 {}
1536
1537 ~buffer_t()
1538 {
1539 if (data)
1540 delete[] data;
1541 }
1542 } m_read_buffer, m_write_buffer;
1543 };
1544
1548 class limiter : public converter
1549 {
1550 public:
1551 limiter(_Inout_ basic& source, _In_ fsize_t _read_limit = 0, _In_ fsize_t _write_limit = 0) :
1552 converter(source),
1553 read_limit(_read_limit),
1554 write_limit(_write_limit)
1555 {}
1556
1557 virtual _Success_(return != 0 || length == 0) size_t read(
1558 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1559 {
1560 size_t num_read;
1561 if (read_limit == fsize_max)
1562 num_read = converter::read(data, length);
1563 else if (length <= read_limit) {
1564 num_read = converter::read(data, length);
1565 read_limit -= num_read;
1566 }
1567 else if (length && !read_limit) {
1568 num_read = 0;
1569 m_state = state_t::eof;
1570 }
1571 else {
1572 num_read = converter::read(data, static_cast<size_t>(read_limit));
1573 read_limit -= num_read;
1574 }
1575 return num_read;
1576 }
1577
1578 virtual _Success_(return != 0) size_t write(
1579 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1580 {
1581 size_t num_written;
1582 if (write_limit == fsize_max)
1583 num_written = converter::write(data, length);
1584 else if (length <= write_limit) {
1585 num_written = converter::write(data, length);
1586 write_limit -= num_written;
1587 }
1588 else if (length && !write_limit) {
1589 num_written = 0;
1590 m_state = state_t::fail;
1591 }
1592 else {
1593 num_written = converter::write(data, static_cast<size_t>(write_limit));
1594 write_limit -= num_written;
1595 }
1596 return num_written;
1597 }
1598
1599 public:
1600 fsize_t
1601 read_limit,
1602 write_limit;
1603 };
1604
1608 class window : public limiter
1609 {
1610 public:
1611 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) :
1612 limiter(source, read_limit, write_limit),
1613 read_offset(_read_offset),
1614 write_offset(_write_offset)
1615 {}
1616
1617 virtual _Success_(return != 0 || length == 0) size_t read(
1618 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1619 {
1620 if (read_offset) {
1621 m_source->skip(read_offset);
1622 m_state = m_source->state();
1623 if (!ok()) _Unlikely_
1624 return 0;
1625 read_offset = 0;
1626 }
1627 size_t num_read;
1628 if (read_limit == fsize_max)
1629 num_read = converter::read(data, length);
1630 else if (length <= read_limit) {
1631 num_read = converter::read(data, length);
1632 read_limit -= num_read;
1633 }
1634 else if (length && !read_limit) {
1635 num_read = 0;
1636 m_source->skip(length);
1637 m_state = state_t::eof;
1638 }
1639 else {
1640 num_read = converter::read(data, static_cast<size_t>(read_limit));
1641 read_limit -= num_read;
1642 }
1643 return num_read;
1644 }
1645
1646 virtual _Success_(return != 0) size_t write(
1647 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1648 {
1649 size_t num_skipped, num_written;
1650 if (length <= write_offset) {
1651 write_offset -= length;
1652 m_state = state_t::ok;
1653 return length;
1654 }
1655 if (write_offset) {
1656 reinterpret_cast<const uint8_t*&>(data) += static_cast<size_t>(write_offset);
1657 length -= static_cast<size_t>(write_offset);
1658 num_skipped = static_cast<size_t>(write_offset);
1659 write_offset = 0;
1660 }
1661 else
1662 num_skipped = 0;
1663 if (write_limit == fsize_max)
1664 num_written = converter::write(data, length);
1665 else if (length <= write_limit) {
1666 num_written = converter::write(data, length);
1667 write_limit -= num_written;
1668 }
1669 else if (length && !write_limit) {
1670 num_skipped += length;
1671 num_written = 0;
1672 m_state = state_t::ok;
1673 }
1674 else {
1675 num_skipped += length - static_cast<size_t>(write_limit);
1676 num_written = converter::write(data, static_cast<size_t>(write_limit));
1677 write_limit -= num_written;
1678 }
1679 return num_skipped + num_written;
1680 }
1681
1682 public:
1683 fpos_t
1684 read_offset,
1685 write_offset;
1686 };
1687
1691 class file_window : public basic_file
1692 {
1693 public:
1694 file_window(_Inout_ basic_file& source, fpos_t offset = 0, fsize_t length = 0) :
1695 basic(source.state()),
1696 m_source(source),
1697 m_offset(source.tell()),
1698 m_region(offset, offset + length)
1699 {}
1700
1701 virtual _Success_(return != 0 || length == 0) size_t read(
1702 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1703 {
1704 stdex_assert(data || !length);
1705 if (m_region.contains(m_offset)) {
1706 size_t num_read = m_source.read(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1707 m_state = m_source.state();
1708 m_offset += num_read;
1709 return num_read;
1710 }
1711 m_state = length ? state_t::eof : state_t::ok;
1712 return 0;
1713 }
1714
1715 virtual _Success_(return != 0) size_t write(
1716 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1717 {
1718 stdex_assert(data || !length);
1719 if (m_region.contains(m_offset)) {
1720 size_t num_written = m_source.write(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1721 m_state = m_source.state();
1722 m_offset += num_written;
1723 return num_written;
1724 }
1725 m_state = state_t::fail;
1726 return 0;
1727 }
1728
1729 virtual void close()
1730 {
1731 m_source.close();
1732 m_state = m_source.state();
1733 }
1734
1735 virtual void flush()
1736 {
1737 m_source.flush();
1738 m_state = m_source.state();
1739 }
1740
1741 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
1742 {
1743 m_offset = m_source.seek(offset, how);
1744 m_state = m_source.state();
1745 return ok() ? m_offset - m_region.start : fpos_max;
1746 }
1747
1748 virtual void skip(_In_ fsize_t amount)
1749 {
1750 m_source.skip(amount);
1751 m_state = m_source.state();
1752 }
1753
1754 virtual fpos_t tell() const
1755 {
1756 fpos_t offset = m_source.tell();
1757 return m_region.contains(offset) ? offset - m_region.start : fpos_max;
1758 }
1759
1760 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
1761 {
1762 if (m_region.contains(offset)) {
1763 m_source.lock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1764 m_state = m_source.state();
1765 }
1766 else
1767 m_state = state_t::fail;
1768 }
1769
1770 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
1771 {
1772 if (m_region.contains(offset)) {
1773 m_source.unlock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1774 m_state = m_source.state();
1775 }
1776 else
1777 m_state = state_t::fail;
1778 }
1779
1780 virtual fsize_t size() const
1781 {
1782 return m_region.size();
1783 }
1784
1785 virtual void truncate()
1786 {
1787 m_state = state_t::fail;
1788 }
1789
1790 protected:
1791 basic_file& m_source;
1792 fpos_t m_offset;
1793 interval<fpos_t> m_region;
1794 };
1795
1796 constexpr size_t default_cache_size = 0x1000;
1797
1801 class cache : public basic_file
1802 {
1803 protected:
1805#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1806 explicit cache(_In_ size_t cache_size = default_cache_size) :
1807 basic(state_t::fail),
1808 m_cache(cache_size)
1809 {}
1810
1811 void init(_Inout_ basic_file& source)
1812 {
1813 m_source = &source;
1814 init();
1815 }
1816
1817 void init()
1818 {
1819 m_state = m_source->state();
1820 m_offset = m_source->tell();
1821#if SET_FILE_OP_TIMES
1822 m_atime = m_source->atime();
1823 m_mtime = m_source->mtime();
1824#endif
1825 }
1826
1827 void done()
1828 {
1829 if (m_source) {
1830 flush_cache();
1831 if (!ok()) _Unlikely_
1832 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occurred
1833 m_source->seekbeg(m_offset);
1834#if SET_FILE_OP_TIMES
1835 m_source->set_atime(m_atime);
1836 m_source->set_mtime(m_mtime);
1837#endif
1838 m_source = nullptr;
1839 }
1840 }
1842
1843 public:
1844 cache(_Inout_ basic_file& source, _In_ size_t cache_size = default_cache_size) :
1845 basic(source.state()),
1846 m_source(&source),
1847 m_cache(cache_size),
1848 m_offset(source.tell())
1849#if SET_FILE_OP_TIMES
1850 , m_atime(source.atime())
1851 , m_mtime(source.mtime())
1852#endif
1853 {}
1854
1855 virtual ~cache() noexcept(false)
1856 {
1857 if (m_source) {
1858 flush_cache();
1859 if (!ok()) _Unlikely_
1860 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occurred
1861 m_source->seekbeg(m_offset);
1862#if SET_FILE_OP_TIMES
1863 m_source->set_atime(m_atime);
1864 m_source->set_mtime(m_mtime);
1865#endif
1866 }
1867 }
1868
1869 virtual _Success_(return != 0 || length == 0) size_t read(
1870 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1871 {
1872 stdex_assert(data || !length);
1873#if SET_FILE_OP_TIMES
1874 m_atime = time_point::now();
1875#endif
1876 for (size_t to_read = length;;) {
1877 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1878 if (m_cache.region.contains(m_offset)) {
1879 size_t remaining_cache = static_cast<size_t>(m_cache.region.end - m_offset);
1880 if (to_read <= remaining_cache) {
1881 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), to_read);
1882 m_offset += to_read;
1883 m_state = state_t::ok;
1884 return length;
1885 }
1886 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), remaining_cache);
1887 reinterpret_cast<uint8_t*&>(data) += remaining_cache;
1888 to_read -= remaining_cache;
1889 m_offset += remaining_cache;
1890 }
1891 flush_cache();
1892 if (!ok()) _Unlikely_ {
1893 if (to_read < length)
1894 m_state = state_t::ok;
1895 return length - to_read;
1896 }
1897 }
1898 {
1899 fpos_t end_max = m_offset + to_read;
1900 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1901 // Read spans multiple cache blocks. Bypass cache to the last block.
1902 m_source->seekbeg(m_offset);
1903 if (!m_source->ok()) _Unlikely_ {
1904 m_state = to_read < length ? state_t::ok : state_t::fail;
1905 return length - to_read;
1906 }
1907 size_t num_read = m_source->read(data, to_read - static_cast<size_t>(end_max % m_cache.capacity));
1908 m_offset += num_read;
1909 to_read -= num_read;
1910 if (!to_read) {
1911 m_state = state_t::ok;
1912 return length;
1913 }
1914 reinterpret_cast<uint8_t*&>(data) += num_read;
1915 m_state = m_source->state();
1916 if (!ok()) {
1917 if (to_read < length)
1918 m_state = state_t::ok;
1919 return length - to_read;
1920 }
1921 }
1922 }
1923 load_cache(m_offset);
1924 if (!ok()) _Unlikely_ {
1925 m_state = to_read < length ? state_t::ok : state_t::fail;
1926 return length - to_read;
1927 }
1928 if (m_cache.region.end <= m_offset) _Unlikely_ {
1929 m_state = to_read < length ? state_t::ok : state_t::eof;
1930 return length - to_read;
1931 }
1932 }
1933 }
1934
1935 virtual _Success_(return != 0) size_t write(
1936 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1937 {
1938 stdex_assert(data || !length);
1939#if SET_FILE_OP_TIMES
1940 m_atime = m_mtime = time_point::now();
1941#endif
1942 for (size_t to_write = length;;) {
1943 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1944 fpos_t end_max = m_cache.region.start + m_cache.capacity;
1945 if (m_cache.region.start <= m_offset && m_offset < end_max) {
1946 size_t remaining_cache = static_cast<size_t>(end_max - m_offset);
1947 if (to_write <= remaining_cache) {
1948 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, to_write);
1949 m_offset += to_write;
1950 m_cache.status = cache_t::cache_t::status_t::dirty;
1951 m_cache.region.end = std::max(m_cache.region.end, m_offset);
1952 m_state = state_t::ok;
1953 return length;
1954 }
1955 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, remaining_cache);
1956 reinterpret_cast<const uint8_t*&>(data) += remaining_cache;
1957 to_write -= remaining_cache;
1958 m_offset += remaining_cache;
1959 m_cache.status = cache_t::cache_t::status_t::dirty;
1960 m_cache.region.end = end_max;
1961 }
1962 flush_cache();
1963 if (!ok()) _Unlikely_
1964 return length - to_write;
1965 }
1966 {
1967 fpos_t end_max = m_offset + to_write;
1968 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1969 // Write spans multiple cache blocks. Bypass cache to the last block.
1970 m_source->seekbeg(m_offset);
1971 if (!ok()) _Unlikely_
1972 return length - to_write;
1973 size_t num_written = m_source->write(data, to_write - static_cast<size_t>(end_max % m_cache.capacity));
1974 m_offset += num_written;
1975 m_state = m_source->state();
1976 to_write -= num_written;
1977 if (!to_write || !ok())
1978 return length - to_write;
1979 reinterpret_cast<const uint8_t*&>(data) += num_written;
1980 }
1981 }
1982 load_cache(m_offset);
1983 if (!ok()) _Unlikely_
1984 return length - to_write;
1985 }
1986 }
1987
1988 virtual void close()
1989 {
1990 invalidate_cache();
1991 if (!ok()) _Unlikely_
1992 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occurred
1993 m_source->close();
1994 m_state = m_source->state();
1995 }
1996
1997 virtual void flush()
1998 {
1999#if SET_FILE_OP_TIMES
2000 m_atime = m_mtime = time_point::min();
2001#endif
2002 flush_cache();
2003 if (!ok()) _Unlikely_
2004 return;
2005 m_source->flush();
2006 }
2007
2008 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2009 {
2010 m_state = state_t::ok;
2011 switch (how) {
2012 case seek_t::beg:
2013 break;
2014 case seek_t::cur:
2015 offset = static_cast<foff_t>(m_offset) + offset;
2016 break;
2017 case seek_t::end: {
2018 auto n = size();
2019 if (n == fsize_max) _Unlikely_{
2020 m_state = state_t::fail;
2021 return fpos_max;
2022 }
2023 offset = static_cast<foff_t>(n) + offset;
2024 break;
2025 }
2026 default:
2027 throw std::invalid_argument("unknown seek origin");
2028 }
2029 if (offset < 0) _Unlikely_
2030 throw std::invalid_argument("negative file offset");
2031 return m_offset = static_cast<fpos_t>(offset);
2032 }
2033
2034 virtual fpos_t tell() const
2035 {
2036 return m_offset;
2037 }
2038
2039 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2040 {
2041 m_source->lock(offset, length);
2042 m_state = m_source->state();
2043 }
2044
2045 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2046 {
2047 m_source->unlock(offset, length);
2048 m_state = m_source->state();
2049 }
2050
2051 virtual fsize_t size() const
2052 {
2053 return m_cache.status != cache_t::cache_t::status_t::empty ?
2054 std::max(m_source->size(), m_cache.region.end) :
2055 m_source->size();
2056 }
2057
2058 virtual void truncate()
2059 {
2060#if SET_FILE_OP_TIMES
2061 m_atime = m_mtime = time_point::now();
2062#endif
2063 m_source->seekbeg(m_offset);
2064 if (m_cache.region.end <= m_offset) {
2065 // Truncation does not affect cache.
2066 }
2067 else if (m_cache.region.start <= m_offset) {
2068 // Truncation truncates cache.
2069 m_cache.region.end = m_offset;
2070 }
2071 else {
2072 // Truncation invalidates cache.
2073 m_cache.status = cache_t::cache_t::status_t::empty;
2074 }
2075 m_source->truncate();
2076 m_state = m_source->state();
2077 }
2078
2079 virtual time_point ctime() const
2080 {
2081 return m_source->ctime();
2082 }
2083
2084 virtual time_point atime() const
2085 {
2086#if SET_FILE_OP_TIMES
2087 return std::max(m_atime, m_source->atime());
2088#else
2089 return m_source->atime();
2090#endif
2091 }
2092
2093 virtual time_point mtime() const
2094 {
2095#if SET_FILE_OP_TIMES
2096 return std::max(m_mtime, m_source->mtime());
2097#else
2098 return m_source->mtime();
2099#endif
2100 }
2101
2102 virtual void set_ctime(time_point date)
2103 {
2104 m_source->set_ctime(date);
2105 }
2106
2107 virtual void set_atime(time_point date)
2108 {
2109#if SET_FILE_OP_TIMES
2110 m_atime = date;
2111#endif
2112 m_source->set_atime(date);
2113 }
2114
2115 virtual void set_mtime(time_point date)
2116 {
2117#if SET_FILE_OP_TIMES
2118 m_mtime = date;
2119#endif
2120 m_source->set_mtime(date);
2121 }
2122
2123 protected:
2125 void flush_cache()
2126 {
2127 if (m_cache.status != cache_t::cache_t::status_t::dirty)
2128 m_state = state_t::ok;
2129 else if (!m_cache.region.empty()) {
2130 write_cache();
2131 if (ok())
2132 m_cache.status = cache_t::cache_t::status_t::loaded;
2133 }
2134 else {
2135 m_state = state_t::ok;
2136 m_cache.status = cache_t::cache_t::status_t::loaded;
2137 }
2138 }
2139
2140 void invalidate_cache()
2141 {
2142 if (m_cache.status == cache_t::cache_t::status_t::dirty && !m_cache.region.empty()) {
2143 write_cache();
2144 if (!ok()) _Unlikely_
2145 return;
2146 } else
2147 m_state = state_t::ok;
2148 m_cache.status = cache_t::cache_t::status_t::empty;
2149 }
2150
2151 void load_cache(_In_ fpos_t start)
2152 {
2153 stdex_assert(m_cache.status != cache_t::cache_t::status_t::dirty);
2154 start -= start % m_cache.capacity; // Align to cache block size.
2155 m_source->seekbeg(m_cache.region.start = start);
2156 if (m_source->ok()) {
2157 m_cache.region.end = start + m_source->read(m_cache.data, m_cache.capacity);
2158 m_cache.status = cache_t::cache_t::status_t::loaded;
2159 m_state = state_t::ok; // Regardless the read failure, we still might have cached some data.
2160 }
2161 else
2162 m_state = state_t::fail;
2163 }
2164
2165 void write_cache()
2166 {
2167 stdex_assert(m_cache.status == cache_t::cache_t::status_t::dirty);
2168 m_source->seekbeg(m_cache.region.start);
2169 m_source->write(m_cache.data, static_cast<size_t>(m_cache.region.size()));
2170 m_state = m_source->state();
2171 }
2172
2173 basic_file* m_source;
2174 struct cache_t {
2175 uint8_t* data;
2176 size_t capacity;
2177 enum class status_t {
2178 empty = 0,
2179 loaded,
2180 dirty,
2181 } status;
2182 interval<fpos_t> region;
2183
2184 cache_t(_In_ size_t _capacity) :
2185 data(new uint8_t[_capacity]),
2186 capacity(_capacity),
2187 status(status_t::empty),
2188 region(0)
2189 {}
2190
2191 ~cache_t()
2192 {
2193 delete[] data;
2194 }
2195 } m_cache;
2196 fpos_t m_offset;
2197#if SET_FILE_OP_TIMES
2198 time_point
2199 m_atime,
2200 m_mtime;
2201#endif
2203 };
2204
2208 class basic_sys : virtual public basic, public sys_object
2209 {
2210 public:
2211 basic_sys(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) :
2212 basic(state),
2213 sys_object(h)
2214 {}
2215
2216 virtual _Success_(return != 0 || length == 0) size_t read(
2217 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2218 {
2219 stdex_assert(data || !length);
2220 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2221 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2222 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2223 size_t
2224#if defined(_WIN64)
2225 block_size = 0x1F80000;
2226#elif defined(_WIN32)
2227 block_size = 0x3f00000;
2228#else
2229 block_size = SSIZE_MAX;
2230#endif
2231 for (size_t to_read = length;;) {
2232#ifdef _WIN32
2233 // ReadFile() might raise exception (e.g. STATUS_FILE_BAD_FORMAT/0xE0000002).
2234 BOOL succeeded;
2235 DWORD num_read;
2236 __try { succeeded = ReadFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_read, block_size)), &num_read, nullptr); }
2237 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_read = 0; }
2238 if (!succeeded && GetLastError() == ERROR_NO_SYSTEM_RESOURCES && block_size > default_block_size) _Unlikely_ {
2239 // Error "Insufficient system resources exist to complete the requested service." occurs
2240 // occasionally, when attempting to read too much data at once (e.g. over \\TSClient).
2241 block_size = default_block_size;
2242 continue;
2243 }
2244 if (!succeeded) _Unlikely_
2245#else
2246 auto num_read = ::read(m_h, data, std::min<size_t>(to_read, block_size));
2247 if (num_read < 0) _Unlikely_
2248#endif
2249 {
2250 m_state = to_read < length ? state_t::ok : state_t::fail;
2251 return length - to_read;
2252 }
2253 if (!num_read) _Unlikely_ {
2254 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2255 return length - to_read;
2256 }
2257 to_read -= static_cast<size_t>(num_read);
2258 if (!to_read) {
2259 m_state = state_t::ok;
2260 return length;
2261 }
2262 reinterpret_cast<uint8_t*&>(data) += num_read;
2263 }
2264 }
2265
2266 virtual _Success_(return != 0) size_t write(
2267 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2268 {
2269 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2270 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2271 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2272 constexpr size_t
2273#if defined(_WIN64)
2274 block_size = 0x1F80000;
2275#elif defined(_WIN32)
2276 block_size = 0x3f00000;
2277#else
2278 block_size = SSIZE_MAX;
2279#endif
2280 for (size_t to_write = length;;) {
2281#ifdef _WIN32
2282 // ReadFile() might raise an exception. Be cautious with WriteFile() too.
2283 BOOL succeeded;
2284 DWORD num_written;
2285 __try { succeeded = WriteFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_write, block_size)), &num_written, nullptr); }
2286 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_written = 0; }
2287 to_write -= num_written;
2288 if (!to_write) {
2289 m_state = state_t::ok;
2290 return length;
2291 }
2292 reinterpret_cast<const uint8_t*&>(data) += num_written;
2293 if (!succeeded) _Unlikely_ {
2294 m_state = state_t::fail;
2295 return length - to_write;
2296 }
2297#else
2298 auto num_written = ::write(m_h, data, std::min<size_t>(to_write, block_size));
2299 if (num_written < 0) _Unlikely_ {
2300 m_state = state_t::fail;
2301 return length - to_write;
2302 }
2303 to_write -= static_cast<size_t>(num_written);
2304 if (!to_write) {
2305 m_state = state_t::ok;
2306 return length;
2307 }
2308 reinterpret_cast<const uint8_t*&>(data) += num_written;
2309#endif
2310 }
2311 }
2312
2313 virtual void close()
2314 {
2315 try {
2316 sys_object::close();
2317 m_state = state_t::ok;
2318 }
2319 catch (...) {
2320 m_state = state_t::fail;
2321 }
2322 }
2323
2324 virtual void flush()
2325 {
2326#ifdef _WIN32
2327 m_state = FlushFileBuffers(m_h) ? state_t::ok : state_t::fail;
2328#else
2329 m_state = fsync(m_h) >= 0 ? state_t::ok : state_t::fail;
2330#endif
2331 }
2332 };
2333
2337 class buffered_sys : public buffer
2338 {
2339 public:
2340 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) :
2341 buffer(read_buffer_size, write_buffer_size),
2342 m_source(h)
2343 {
2344 init(m_source);
2345 }
2346
2347 virtual ~buffered_sys()
2348 {
2349 done();
2350 }
2351
2352 protected:
2353 basic_sys m_source;
2354 };
2355
2359 class socket : public basic
2360 {
2361 public:
2362 socket(_In_opt_ socket_t h = stdex::invalid_socket, _In_ state_t state = state_t::ok) :
2363 basic(state),
2364 m_h(h)
2365 {}
2366
2367 private:
2368 socket(_In_ const socket& other);
2369 socket& operator =(_In_ const socket& other);
2370
2371 public:
2372 socket(_Inout_ socket&& other) noexcept : m_h(other.m_h)
2373 {
2374 other.m_h = stdex::invalid_socket;
2375 }
2376
2377 socket& operator =(_Inout_ socket&& other) noexcept
2378 {
2379 if (this != std::addressof(other)) {
2380 if (m_h != stdex::invalid_socket)
2381 closesocket(m_h);
2382 m_h = other.m_h;
2383 other.m_h = stdex::invalid_socket;
2384 }
2385 return *this;
2386 }
2387
2395 socket(_In_ int af, _In_ int type, _In_ int protocol)
2396 {
2397 m_h = ::socket(af, type, protocol);
2398 if (m_h == stdex::invalid_socket) _Unlikely_
2399 m_state = state_t::fail;
2400 }
2401
2402 virtual ~socket()
2403 {
2404 if (m_h != stdex::invalid_socket)
2405 closesocket(m_h);
2406 }
2407
2411 operator bool() const noexcept { return m_h != stdex::invalid_socket; }
2412
2416 socket_t get() const noexcept { return m_h; }
2417
2418 virtual _Success_(return != 0 || length == 0) size_t read(
2419 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2420 {
2421 stdex_assert(data || !length);
2422 constexpr int block_size = 0x10000000;
2423 for (size_t to_read = length;;) {
2424 auto num_read = recv(m_h, reinterpret_cast<char*>(data),
2425#ifdef _WIN32
2426 static_cast<int>(std::min<size_t>(to_read, block_size)),
2427#else
2428 std::min<size_t>(to_read, block_size),
2429#endif
2430 0);
2431 if (num_read < 0) _Unlikely_ {
2432 m_state = to_read < length ? state_t::ok : state_t::fail;
2433 return length - to_read;
2434 }
2435 if (!num_read) {
2436 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2437 return length - to_read;
2438 }
2439 to_read -= static_cast<size_t>(num_read);
2440 if (!to_read) {
2441 m_state = state_t::ok;
2442 return length;
2443 }
2444 reinterpret_cast<uint8_t*&>(data) += num_read;
2445 }
2446 }
2447
2448 virtual _Success_(return != 0) size_t write(
2449 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2450 {
2451 stdex_assert(data || !length);
2452 constexpr int block_size = 0x10000000;
2453 for (size_t to_write = length;;) {
2454 auto num_written = send(m_h, reinterpret_cast<const char*>(data),
2455#ifdef _WIN32
2456 static_cast<int>(std::min<size_t>(to_write, block_size)),
2457#else
2458 std::min<size_t>(to_write, block_size),
2459#endif
2460 0);
2461 if (num_written < 0) _Unlikely_ {
2462 m_state = state_t::fail;
2463 return length - to_write;
2464 }
2465 to_write -= static_cast<size_t>(num_written);
2466 if (!to_write) {
2467 m_state = state_t::ok;
2468 return length;
2469 }
2470 reinterpret_cast<const uint8_t*&>(data) += num_written;
2471 }
2472 }
2473
2474 virtual void close()
2475 {
2476 if (m_h != stdex::invalid_socket) {
2477 closesocket(m_h);
2478 m_h = stdex::invalid_socket;
2479 }
2480 m_state = state_t::ok;
2481 }
2482
2483 protected:
2484 socket_t m_h;
2485 };
2486
2487#ifdef _WIN32
2491 class sequential_stream : public basic
2492 {
2493 public:
2494 sequential_stream(_In_ ISequentialStream* source) : m_source(source)
2495 {
2496 m_source->AddRef();
2497 }
2498
2499 virtual ~sequential_stream()
2500 {
2501 m_source->Release();
2502 }
2503
2504 virtual _Success_(return != 0 || length == 0) size_t read(
2505 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2506 {
2507 stdex_assert(data || !length);
2508 for (size_t to_read = length;;) {
2509 HRESULT hr;
2510 ULONG num_read = 0;
2511 __try { hr = m_source->Read(data, (ULONG)std::min<size_t>(to_read, ULONG_MAX), &num_read); }
2512 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2513 if (FAILED(hr)) _Unlikely_ {
2514 m_state = to_read < length ? state_t::ok : state_t::fail;
2515 return length - to_read;
2516 }
2517 to_read -= num_read;
2518 if (hr == S_FALSE) _Unlikely_ {
2519 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2520 return length - to_read;
2521 }
2522 if (!to_read) {
2523 m_state = state_t::ok;
2524 return length;
2525 }
2526 reinterpret_cast<uint8_t*&>(data) += num_read;
2527 }
2528 }
2529
2530 virtual _Success_(return != 0) size_t write(
2531 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2532 {
2533 stdex_assert(data || !length);
2534 for (size_t to_write = length;;) {
2535 HRESULT hr;
2536 ULONG num_written = 0;
2537 __try { hr = m_source->Write(data, static_cast<ULONG>(std::min<size_t>(to_write, ULONG_MAX)), &num_written); }
2538 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2539 // In absence of documentation whether num_written gets set when FAILED(hr) (i.e. partially successful writes),
2540 // assume write failed completely.
2541 if (FAILED(hr)) _Unlikely_ {
2542 m_state = state_t::fail;
2543 return length - to_write;
2544 }
2545 to_write -= num_written;
2546 if (!to_write) {
2547 m_state = state_t::ok;
2548 return length;
2549 }
2550 reinterpret_cast<const uint8_t*&>(data) += num_written;
2551 }
2552 }
2553
2554 protected:
2555 ISequentialStream* m_source;
2556 };
2557
2561 class asp : public basic
2562 {
2563 public:
2564 asp(_In_opt_ IRequest* request, _In_opt_ IResponse* response) :
2565 m_request(request),
2566 m_response(response)
2567 {
2568 if (m_request)
2569 m_request->AddRef();
2570 if (m_response)
2571 m_response->AddRef();
2572 }
2573
2574 virtual ~asp()
2575 {
2576 if (m_request)
2577 m_request->Release();
2578 if (m_response)
2579 m_response->Release();
2580 }
2581
2582 virtual _Success_(return != 0 || length == 0) size_t read(
2583 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2584 {
2585 stdex_assert(data || !length);
2586 if (!m_request) _Unlikely_ {
2587 m_state = state_t::fail;
2588 return 0;
2589 }
2590 for (size_t to_read = length;;) {
2591 VARIANT var_amount, var_data;
2592 V_VT(&var_amount) = VT_I4;
2593 V_I4(&var_amount) = (LONG)std::min<size_t>(to_read, LONG_MAX);
2594 V_VT(&var_data) = VT_EMPTY;
2595 HRESULT hr = [&]() {
2596 __try { return m_request->BinaryRead(&var_amount, &var_data); }
2597 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2598 }();
2599 if (FAILED(hr)) _Unlikely_ {
2600 m_state = to_read < length ? state_t::ok : state_t::fail;
2601 return length - to_read;
2602 }
2603 stdex_assert(V_VT(&var_amount) == VT_I4);
2604 stdex_assert(V_VT(&var_data) == (VT_ARRAY | VT_UI1));
2605 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(V_ARRAY(&var_data));
2606 if (!V_I4(&var_amount)) _Unlikely_ {
2607 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2608 return length - to_read;
2609 }
2610 safearray_accessor<uint8_t> a(sa.get());
2611 memcpy(data, a.data(), V_I4(&var_amount));
2612 to_read -= V_I4(&var_amount);
2613 if (!to_read) {
2614 m_state = state_t::ok;
2615 return length;
2616 }
2617 reinterpret_cast<uint8_t*&>(data) += V_I4(&var_amount);
2618 }
2619 }
2620
2621 virtual _Success_(return != 0) size_t write(
2622 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2623 {
2624 if (!m_response) {
2625 m_state = state_t::fail;
2626 return 0;
2627 }
2628 for (size_t to_write = length;;) {
2629 UINT num_written = static_cast<UINT>(std::min<size_t>(to_write, UINT_MAX));
2630 std::unique_ptr<OLECHAR, SysFreeString_delete> bstr_data(SysAllocStringByteLen(reinterpret_cast<LPCSTR>(data), num_written));
2631 VARIANT var_data;
2632 V_VT(&var_data) = VT_BSTR;
2633 V_BSTR(&var_data) = bstr_data.get();
2634 HRESULT hr = [&]() {
2635 __try { return m_response->BinaryWrite(var_data); }
2636 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2637 }();
2638 if (FAILED(hr)) _Unlikely_ {
2639 m_state = state_t::fail;
2640 return length - to_write;
2641 }
2642 to_write -= num_written;
2643 if (!to_write) {
2644 m_state = state_t::ok;
2645 return length;
2646 }
2647 reinterpret_cast<const uint8_t*&>(data) += num_written;
2648 }
2649 }
2650
2651 virtual void close()
2652 {
2653 if (m_response) {
2654 __try { m_response->End(); }
2655 __except (EXCEPTION_EXECUTE_HANDLER) {}
2656 }
2657 m_state = state_t::ok;
2658 }
2659
2660 virtual void flush()
2661 {
2662 if (m_response) {
2663 HRESULT hr;
2664 __try { hr = m_response->Flush(); }
2665 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2666 m_state = SUCCEEDED(hr) ? state_t::ok : state_t::fail;
2667 }
2668 }
2669
2670 protected:
2671 IRequest* m_request;
2672 IResponse* m_response;
2673 };
2674#endif
2675
2679 enum mode_t
2680 {
2681 mode_for_reading = 1 << 0,
2682 mode_for_writing = 1 << 1,
2683 mode_for_chmod = 1 << 2,
2684
2685 mode_open_existing = 0 << 3,
2686 mode_truncate_existing = 1 << 3,
2687 mode_preserve_existing = 2 << 3,
2688 mode_create_new = 3 << 3,
2689 mode_create = 4 << 3,
2690 mode_disposition_mask = 7 << 3,
2691
2692 mode_append = 1 << 6,
2693 mode_text = 0,
2694 mode_binary = 1 << 7,
2695
2696 share_none = 0,
2697 share_reading = 1 << 8,
2698 share_writing = 1 << 9,
2699 share_deleting = 1 << 10,
2700 share_all = share_reading | share_writing | share_deleting, // Allow others all operations on our file
2701
2702 inherit_handle = 1 << 11,
2703
2704 hint_write_thru = 1 << 12,
2705 hint_no_buffering = 1 << 13,
2706 hint_random_access = 1 << 14,
2707 hint_sequential_access = 1 << 15,
2708 };
2709
2710#pragma warning(push)
2711#pragma warning(disable: 4250)
2715 class file : virtual public basic_file, virtual public basic_sys
2716 {
2717 public:
2718 file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) : basic_sys(h, state) {}
2719
2726 file(_In_z_ const schar_t* filename, _In_ int mode)
2727 {
2728 open(filename, mode);
2729 }
2730
2737 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
2738 file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode) : file(filename.c_str(), mode) {}
2739
2746 void open(_In_z_ const schar_t* filename, _In_ int mode)
2747 {
2748 if (m_h != invalid_handle)
2749 close();
2750
2751#ifdef _WIN32
2752 DWORD dwDesiredAccess = 0;
2753 if (mode & mode_for_reading) dwDesiredAccess |= GENERIC_READ;
2754 if (mode & mode_for_writing) dwDesiredAccess |= GENERIC_WRITE;
2755 if (mode & mode_for_chmod) dwDesiredAccess |= FILE_WRITE_ATTRIBUTES;
2756
2757 DWORD dwShareMode = 0;
2758 if (mode & share_reading) dwShareMode |= FILE_SHARE_READ;
2759 if (mode & share_writing) dwShareMode |= FILE_SHARE_WRITE;
2760 if (mode & share_deleting) dwShareMode |= FILE_SHARE_DELETE;
2761
2762 SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };
2763 sa.bInheritHandle = mode & inherit_handle ? true : false;
2764
2765 DWORD dwCreationDisposition;
2766 switch (mode & mode_disposition_mask) {
2767 case mode_open_existing: dwCreationDisposition = OPEN_EXISTING; break;
2768 case mode_truncate_existing: dwCreationDisposition = TRUNCATE_EXISTING; break;
2769 case mode_preserve_existing: dwCreationDisposition = OPEN_ALWAYS; break;
2770 case mode_create_new: dwCreationDisposition = CREATE_NEW; break;
2771 case mode_create: dwCreationDisposition = CREATE_ALWAYS; break;
2772 default: throw std::invalid_argument("invalid mode");
2773 }
2774
2775 DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
2776 if (mode & hint_write_thru) dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
2777 if (mode & hint_no_buffering) dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
2778 if (mode & hint_random_access) dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
2779 if (mode & hint_sequential_access) dwFlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
2780
2781 m_h = CreateFile(filename, dwDesiredAccess, dwShareMode, &sa, dwCreationDisposition, dwFlagsAndAttributes, NULL);
2782#else
2783 int flags = 0;
2784 switch (mode & (mode_for_reading | mode_for_writing)) {
2785 case mode_for_reading: flags |= O_RDONLY; break;
2786 case mode_for_writing: flags |= O_WRONLY; break;
2787 case mode_for_reading | mode_for_writing: flags |= O_RDWR; break;
2788 }
2789 switch (mode & mode_disposition_mask) {
2790 case mode_open_existing: break;
2791 case mode_truncate_existing: flags |= O_TRUNC; break;
2792 case mode_preserve_existing: flags |= O_CREAT; break;
2793 case mode_create_new: flags |= O_CREAT | O_EXCL; break;
2794 case mode_create: flags |= O_CREAT | O_TRUNC; break;
2795 default: throw std::invalid_argument("invalid mode");
2796 }
2797 if (mode & hint_write_thru) flags |= O_DSYNC;
2798#ifndef __APPLE__
2799 if (mode & hint_no_buffering) flags |= O_RSYNC;
2800#endif
2801
2802 m_h = ::open(filename, flags, DEFFILEMODE);
2803#endif
2804 if (m_h != invalid_handle) {
2805 m_state = state_t::ok;
2806 if (mode & mode_append)
2807 seek(0, seek_t::end);
2808 }
2809 else
2810 m_state = state_t::fail;
2811 }
2812
2819 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
2820 void open(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
2821 {
2822 open(filename.c_str(), mode);
2823 }
2824
2825 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2826 {
2827#ifdef _WIN32
2828 LARGE_INTEGER li;
2829 li.QuadPart = offset;
2830 li.LowPart = SetFilePointer(m_h, li.LowPart, &li.HighPart, static_cast<DWORD>(how));
2831 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR) {
2832 m_state = state_t::ok;
2833 return li.QuadPart;
2834 }
2835#else
2836 off64_t result = lseek64(m_h, offset, static_cast<int>(how));
2837 if (result >= 0) {
2838 m_state = state_t::ok;
2839 return static_cast<fpos_t>(result);
2840 }
2841#endif
2842 m_state = state_t::fail;
2843 return fpos_max;
2844 }
2845
2846 virtual fpos_t tell() const
2847 {
2848 if (m_h != invalid_handle) {
2849#ifdef _WIN32
2850 LARGE_INTEGER li;
2851 li.QuadPart = 0;
2852 li.LowPart = SetFilePointer(m_h, 0, &li.HighPart, FILE_CURRENT);
2853 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR)
2854 return li.QuadPart;
2855#else
2856 off64_t result = lseek64(m_h, 0, SEEK_CUR);
2857 if (result >= 0)
2858 return static_cast<fpos_t>(result);
2859#endif
2860 }
2861 return fpos_max;
2862 }
2863
2864 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2865 {
2866#ifdef _WIN32
2867 LARGE_INTEGER liOffset;
2868 LARGE_INTEGER liSize;
2869 liOffset.QuadPart = offset;
2870 liSize.QuadPart = length;
2871 if (LockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2872 m_state = state_t::ok;
2873 return;
2874 }
2875#else
2876 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2877 if (orig >= 0) {
2878 if (offset > std::numeric_limits<off64_t>::max())
2879 throw std::invalid_argument("file offset too big");
2880 if (length > std::numeric_limits<off64_t>::max())
2881 throw std::invalid_argument("file section length too big");
2882 m_state = lseek64(m_h, static_cast<off64_t>(offset), SEEK_SET) >= 0 && lockf64(m_h, F_LOCK, static_cast<off64_t>(length)) >= 0 ? state_t::ok : state_t::fail;
2883 lseek64(m_h, orig, SEEK_SET);
2884 m_state = state_t::ok;
2885 return;
2886 }
2887#endif
2888 m_state = state_t::fail;
2889 }
2890
2891 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2892 {
2893#ifdef _WIN32
2894 LARGE_INTEGER liOffset;
2895 LARGE_INTEGER liSize;
2896 liOffset.QuadPart = offset;
2897 liSize.QuadPart = length;
2898 if (UnlockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2899 m_state = state_t::ok;
2900 return;
2901 }
2902#else
2903 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2904 if (orig >= 0) {
2905 if (offset > std::numeric_limits<off64_t>::max())
2906 throw std::invalid_argument("file offset too big");
2907 if (length > std::numeric_limits<off64_t>::max())
2908 throw std::invalid_argument("file section length too big");
2909 if (lseek64(m_h, static_cast<off64_t>(offset), SEEK_SET) >= 0 && lockf64(m_h, F_ULOCK, static_cast<off64_t>(length)) >= 0) {
2910 lseek64(m_h, orig, SEEK_SET);
2911 m_state = state_t::ok;
2912 return;
2913 }
2914 lseek64(m_h, orig, SEEK_SET);
2915 }
2916#endif
2917 m_state = state_t::fail;
2918 }
2919
2920 virtual fsize_t size() const
2921 {
2922#ifdef _WIN32
2923 LARGE_INTEGER li;
2924 li.LowPart = GetFileSize(m_h, (LPDWORD)&li.HighPart);
2925 if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR)
2926 li.QuadPart = -1;
2927 return li.QuadPart;
2928#else
2929 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2930 if (orig >= 0) {
2931 off64_t length = lseek64(m_h, 0, SEEK_END);
2932 lseek64(m_h, orig, SEEK_SET);
2933 if (length >= 0)
2934 return static_cast<fsize_t>(length);
2935 }
2936 return fsize_max;
2937#endif
2938 }
2939
2940 virtual void truncate()
2941 {
2942#ifdef _WIN32
2943 if (SetEndOfFile(m_h)) {
2944 m_state = state_t::ok;
2945 return;
2946 }
2947#else
2948 off64_t length = lseek64(m_h, 0, SEEK_CUR);
2949 if (length >= 0 && ftruncate64(m_h, length) >= 0) {
2950 m_state = state_t::ok;
2951 return;
2952 }
2953#endif
2954 m_state = state_t::fail;
2955 }
2956
2957#ifdef _WIN32
2958 static time_point ft2tp(_In_ const FILETIME& ft)
2959 {
2960#if _HAS_CXX20
2961 uint64_t t = (static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
2962#else
2963 uint64_t t = ((static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) - 116444736000000000ll;
2964#endif
2965 return time_point(time_point::duration(t));
2966 }
2967
2968 static void tp2ft(_In_ time_point tp, _Out_ FILETIME& ft)
2969 {
2970#if _HAS_CXX20
2971 uint64_t t = tp.time_since_epoch().count();
2972#else
2973 uint64_t t = tp.time_since_epoch().count() + 116444736000000000ll;
2974#endif
2975 ft.dwHighDateTime = static_cast<DWORD>((t >> 32) & 0xffffffff);
2976 ft.dwLowDateTime = static_cast<DWORD>(t & 0xffffffff);
2977 }
2978#endif
2979
2980 virtual time_point ctime() const
2981 {
2982#ifdef _WIN32
2983 FILETIME ft;
2984 if (GetFileTime(m_h, &ft, nullptr, nullptr))
2985 return ft2tp(ft);
2986#endif
2987 return time_point::min();
2988 }
2989
2990 virtual time_point atime() const
2991 {
2992#ifdef _WIN32
2993 FILETIME ft;
2994 if (GetFileTime(m_h, nullptr, &ft, nullptr))
2995 return ft2tp(ft);
2996#else
2997 struct stat buf;
2998 if (fstat(m_h, &buf) >= 0)
2999 return clock::from_time_t(buf.st_atime);
3000#endif
3001 return time_point::min();
3002 }
3003
3004 virtual time_point mtime() const
3005 {
3006#ifdef _WIN32
3007 FILETIME ft;
3008 if (GetFileTime(m_h, nullptr, nullptr, &ft))
3009 return ft2tp(ft);
3010#else
3011 struct stat buf;
3012 if (fstat(m_h, &buf) >= 0)
3013 return clock::from_time_t(buf.st_mtime);
3014#endif
3015 return time_point::min();
3016 }
3017
3018 virtual void set_ctime(time_point date)
3019 {
3020 stdex_assert(m_h != invalid_handle);
3021#ifdef _WIN32
3022 FILETIME ft;
3023 tp2ft(date, ft);
3024 if (SetFileTime(m_h, &ft, nullptr, nullptr))
3025 return;
3026 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3027#else
3028 _Unreferenced_(date);
3029 throw std::runtime_error("not supported");
3030#endif
3031 }
3032
3033 virtual void set_atime(time_point date)
3034 {
3035 stdex_assert(m_h != invalid_handle);
3036#ifdef _WIN32
3037 FILETIME ft;
3038 tp2ft(date, ft);
3039 if (SetFileTime(m_h, nullptr, &ft, nullptr))
3040 return;
3041 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3042#else
3043 struct timespec ts[2] = {
3044 { date.time_since_epoch().count(), 0 },
3045 { 0, UTIME_OMIT },
3046 };
3047 if (futimens(m_h, ts) >= 0)
3048 return;
3049 throw std::system_error(errno, std::system_category(), "futimens failed");
3050#endif
3051 }
3052
3053 virtual void set_mtime(time_point date)
3054 {
3055#ifdef _WIN32
3056 FILETIME ft;
3057 tp2ft(date, ft);
3058 if (SetFileTime(m_h, nullptr, nullptr, &ft))
3059 return;
3060 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3061#else
3062 struct timespec ts[2] = {
3063 { 0, UTIME_OMIT },
3064 { date.time_since_epoch().count(), 0 },
3065 };
3066 if (futimens(m_h, ts) >= 0)
3067 return;
3068 throw std::system_error(errno, std::system_category(), "futimens failed");
3069#endif
3070 }
3071
3077 static bool exists(_In_z_ const stdex::schar_t* filename)
3078 {
3079#ifdef _WIN32
3080 return GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES;
3081#else
3082 struct stat s;
3083 return stat(filename, &s) == 0;
3084#endif
3085 }
3086
3092 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3093 static bool exists(_In_ const std::basic_string<TR, AX>& filename)
3094 {
3095 return exists(filename.c_str());
3096 }
3097
3105 static bool readonly(_In_z_ const stdex::schar_t* filename)
3106 {
3107#ifdef _WIN32
3108 DWORD dwAttr = GetFileAttributes(filename);
3109 return dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_READONLY) != 0;
3110#else
3111 struct stat s;
3112 return stat(filename, &s) == 0 && (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0;
3113#endif
3114 }
3115
3123 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3124 static bool readonly(_In_ const std::basic_string<TR, AX>& filename)
3125 {
3126 return readonly(filename.c_str());
3127 }
3128 };
3129#pragma warning(pop)
3130
3134 class cached_file : public cache
3135 {
3136 public:
3137 cached_file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok, _In_ size_t cache_size = default_cache_size) :
3138 cache(cache_size),
3139 m_source(h, state)
3140 {
3141 init(m_source);
3142 }
3143
3151 cached_file(_In_z_ const schar_t* filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) :
3152 cache(cache_size),
3153 m_source(filename, mode & mode_for_writing ? mode | mode_for_reading : mode)
3154 {
3155 init(m_source);
3156 }
3157
3165 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3166 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) {}
3167
3168 virtual ~cached_file()
3169 {
3170 done();
3171 }
3172
3179 void open(_In_z_ const schar_t* filename, _In_ int mode)
3180 {
3181 invalidate_cache();
3182 if (!ok()) _Unlikely_{
3183 m_state = state_t::fail;
3184 return;
3185 }
3186 m_source.open(filename, mode & mode_for_writing ? mode | mode_for_reading : mode);
3187 if (m_source.ok()) {
3188 init();
3189 return;
3190 }
3191 m_state = state_t::fail;
3192 }
3193
3200 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3201 void open(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3202 {
3203 open(filename.c_str(), mode);
3204 }
3205
3209 operator bool() const noexcept { return m_source; }
3210
3211 protected:
3212 file m_source;
3213 };
3214
3218 class memory_file : public basic_file
3219 {
3220 public:
3221 memory_file(_In_ state_t state = state_t::ok) :
3222 basic(state),
3223 m_data(nullptr),
3224 m_offset(0),
3225 m_size(0),
3226 m_reserved(0),
3227 m_manage(true)
3228 {
3229#if SET_FILE_OP_TIMES
3230 m_ctime = m_atime = m_mtime = time_point::now();
3231#endif
3232 }
3233
3240 memory_file(_In_ size_t size, _In_ state_t state = state_t::ok) :
3241 basic(state),
3242 m_data(reinterpret_cast<uint8_t*>(malloc(size))),
3243 m_offset(0),
3244 m_size(0),
3245 m_reserved(size),
3246 m_manage(true)
3247 {
3248 if (!m_data) {
3249 m_state = state_t::fail;
3250 throw std::bad_alloc();
3251 }
3252#if SET_FILE_OP_TIMES
3253 m_ctime = m_atime = m_mtime = time_point::now();
3254#endif
3255 }
3256
3266 memory_file(_Inout_ void* data, _In_ size_t size, _In_ size_t reserved, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3267 basic(state),
3268 m_data(reinterpret_cast<uint8_t*>(data)),
3269 m_offset(0),
3270 m_size(size),
3271 m_reserved(reserved),
3272 m_manage(manage)
3273 {
3274 stdex_assert(data || !size);
3275 stdex_assert(reserved >= size);
3276#if SET_FILE_OP_TIMES
3277 m_ctime = m_atime = m_mtime = time_point::now();
3278#endif
3279 }
3280
3289 memory_file(_Inout_ void* data, _In_ size_t size, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3290 memory_file(data, size, size, manage, state)
3291 {}
3292
3299 memory_file(_In_z_ const schar_t* filename, _In_ int mode) : memory_file()
3300 {
3301 load(filename, mode);
3302 }
3303
3310 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3311 memory_file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode) : memory_file(filename.c_str(), mode) {}
3312
3318 memory_file(_In_ const memory_file& other) :
3319 basic_file(other),
3320 m_data(reinterpret_cast<uint8_t*>(malloc(other.m_size))),
3321 m_offset(other.m_offset),
3322 m_size(other.m_size),
3323 m_reserved(other.m_size),
3324 m_manage(true)
3325#if SET_FILE_OP_TIMES
3326 , m_ctime(other.m_ctime)
3327 , m_atime(other.m_atime)
3328 , m_mtime(other.m_mtime)
3329#endif
3330 {
3331 if (!m_data) {
3332 m_state = state_t::fail;
3333 throw std::bad_alloc();
3334 }
3335 memcpy(m_data, other.m_data, other.m_size);
3336 }
3337
3343 memory_file& operator=(_In_ const memory_file& other)
3344 {
3345 if (this != std::addressof(other)) {
3346 *static_cast<basic_file*>(this) = other;
3347 if (m_manage && m_data)
3348 free(m_data);
3349 m_data = reinterpret_cast<uint8_t*>(malloc(other.m_size));
3350 if (!m_data) {
3351 m_state = state_t::fail;
3352 throw std::bad_alloc();
3353 }
3354 memcpy(m_data, other.m_data, other.m_size);
3355 m_offset = other.m_offset;
3356 m_size = other.m_size;
3357 m_reserved = other.m_size;
3358 m_manage = true;
3359#if SET_FILE_OP_TIMES
3360 m_ctime = other.m_ctime;
3361 m_atime = other.m_atime;
3362 m_mtime = other.m_mtime;
3363#endif
3364 }
3365 return *this;
3366 }
3367
3373 memory_file(_Inout_ memory_file&& other) noexcept :
3374 basic_file(std::move(other)),
3375 m_data(other.m_data),
3376 m_offset(other.m_offset),
3377 m_size(other.m_size),
3378 m_reserved(other.m_reserved),
3379 m_manage(other.m_manage)
3380#if SET_FILE_OP_TIMES
3381 , m_ctime(other.m_ctime)
3382 , m_atime(other.m_atime)
3383 , m_mtime(other.m_mtime)
3384#endif
3385 {
3386 other.m_state = state_t::ok;
3387 other.m_data = nullptr;
3388 other.m_offset = 0;
3389 other.m_size = 0;
3390 other.m_reserved = 0;
3391 other.m_manage = true;
3392#if SET_FILE_OP_TIMES
3393 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3394#endif
3395 }
3396
3402 memory_file& operator=(_Inout_ memory_file&& other) noexcept
3403 {
3404 if (this != std::addressof(other)) {
3405 *static_cast<basic_file*>(this) = std::move(other);
3406 if (m_manage && m_data)
3407 free(m_data);
3408 m_data = other.m_data;
3409 other.m_data = nullptr;
3410 m_offset = other.m_offset;
3411 other.m_offset = 0;
3412 m_size = other.m_size;
3413 other.m_size = 0;
3414 m_reserved = other.m_reserved;
3415 other.m_reserved = 0;
3416 m_manage = other.m_manage;
3417 other.m_manage = true;
3418#if SET_FILE_OP_TIMES
3419 m_ctime = other.m_ctime;
3420 m_atime = other.m_atime;
3421 m_mtime = other.m_mtime;
3422 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3423#endif
3424 }
3425 return *this;
3426 }
3427
3428 virtual ~memory_file()
3429 {
3430 if (m_manage && m_data)
3431 free(m_data);
3432 }
3433
3440 void reserve(_In_ size_t required, _In_ bool tight = false) noexcept
3441 {
3442 if (required <= m_reserved && (!tight || required >= m_reserved)) {
3443 m_state = state_t::ok;
3444 return;
3445 }
3446 if (!m_manage) {
3447 m_state = state_t::fail;
3448 return;
3449 }
3450 size_t reserved = tight ? required : ((required + required / 4 + (default_block_size - 1)) / default_block_size) * default_block_size;
3451 auto data = reinterpret_cast<uint8_t*>(realloc(m_data, reserved));
3452 if (!data && reserved) _Unlikely_ {
3453 m_state = state_t::fail;
3454 return;
3455 }
3456 m_data = data;
3457 if (reserved < m_size)
3458 m_size = reserved;
3459 m_reserved = reserved;
3460 m_state = state_t::ok;
3461 }
3462
3469 void load(_In_z_ const schar_t* filename, _In_ int mode)
3470 {
3471 file f(filename, (mode & ~hint_random_access) | mode_for_reading | hint_sequential_access);
3472 if (!f.ok()) {
3473 m_state = state_t::fail;
3474 return;
3475 }
3476 fsize_t size = f.size();
3477 if (size > SIZE_MAX) {
3478 m_state = state_t::fail;
3479 return;
3480 }
3481 reserve(static_cast<size_t>(size), true);
3482 if (!ok()) _Unlikely_ {
3483 return;
3484 }
3485 m_offset = m_size = 0;
3486 write_stream(f);
3487 if (ok())
3488 m_offset = 0;
3489#if SET_FILE_OP_TIMES
3490 m_ctime = f.ctime();
3491 m_atime = f.atime();
3492 m_mtime = f.mtime();
3493#endif
3494 }
3495
3502 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3503 void load(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3504 {
3505 load(filename.c_str(), mode);
3506 }
3507
3514 void save(_In_z_ const schar_t* filename, _In_ int mode)
3515 {
3516 file f(filename, (mode & ~hint_random_access) | mode_for_writing | hint_sequential_access);
3517 if (!f.ok()) {
3518 m_state = state_t::fail;
3519 return;
3520 }
3521 f.write(m_data, m_size);
3522 if (!f.ok()) {
3523 m_state = state_t::fail;
3524 return;
3525 }
3526 f.truncate();
3527#if SET_FILE_OP_TIMES
3528 f.set_ctime(m_ctime);
3529 f.set_atime(m_atime);
3530 f.set_mtime(m_mtime);
3531#endif
3532 }
3533
3540 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3541 void save(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3542 {
3543 save(filename.c_str(), mode);
3544 }
3545
3549 const void* data() const { return m_data; }
3550
3551 virtual _Success_(return != 0 || length == 0) size_t read(
3552 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
3553 {
3554 stdex_assert(data || !length);
3555#if SET_FILE_OP_TIMES
3556 m_atime = time_point::now();
3557#endif
3558 size_t available = m_size - m_offset;
3559 if (length <= available) {
3560 memcpy(data, &m_data[m_offset], length);
3561 m_offset += length;
3562 m_state = state_t::ok;
3563 return length;
3564 }
3565 if (length && !available) {
3566 m_state = state_t::eof;
3567 return 0;
3568 }
3569 memcpy(data, &m_data[m_offset], available);
3570 m_offset += available;
3571 m_state = state_t::ok;
3572 return available;
3573 }
3574
3589 template <class T>
3590 memory_file& read_data(_Out_ T& data)
3591 {
3592#if SET_FILE_OP_TIMES
3593 m_atime = time_point::now();
3594#endif
3595 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3596 data = 0;
3597 return *this;
3598 }
3599 size_t end_offset = m_offset + sizeof(T);
3600 if (end_offset <= m_size) {
3601 data = LE2HE(*reinterpret_cast<T*>(&m_data[m_offset]));
3602 m_offset = end_offset;
3603#if !CHECK_STREAM_STATE
3604 m_state = state_t::ok;
3605#endif
3606 }
3607 else {
3608 data = 0;
3609 m_offset = m_size;
3610 m_state = state_t::eof;
3611 }
3612 return *this;
3613 }
3614
3629 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3630 memory_file& read_str(_Inout_ std::basic_string<T, TR, AX>&data)
3631 {
3632#if SET_FILE_OP_TIMES
3633 m_atime = time_point::now();
3634#endif
3635 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3636 data.clear();
3637 return *this;
3638 }
3639 size_t end_offset = m_offset + sizeof(uint32_t);
3640 if (end_offset <= m_size) {
3641 uint32_t num_chars = LE2HE(*reinterpret_cast<uint32_t*>(&m_data[m_offset]));
3642 m_offset = end_offset;
3643 end_offset = stdex::add(m_offset, stdex::mul(num_chars, sizeof(T)));
3644 T* start = reinterpret_cast<T*>(&m_data[m_offset]);
3645 if (end_offset <= m_size) {
3646 data.assign(start, start + num_chars);
3647 m_offset = end_offset;
3648#if !CHECK_STREAM_STATE
3649 m_state = state_t::ok;
3650#endif
3651 return *this;
3652 }
3653 if (end_offset <= m_size)
3654 data.assign(start, reinterpret_cast<T*>(&m_data[m_size]));
3655 }
3656 m_offset = m_size;
3657 m_state = state_t::eof;
3658 return *this;
3659 }
3660
3661 virtual _Success_(return != 0) size_t write(
3662 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3663 {
3664 stdex_assert(data || !length);
3665#if SET_FILE_OP_TIMES
3666 m_atime = m_mtime = time_point::now();
3667#endif
3668 size_t end_offset = m_offset + length;
3669 if (end_offset > m_reserved) {
3670 reserve(end_offset);
3671 if (!ok()) _Unlikely_
3672 return 0;
3673 }
3674 memcpy(&m_data[m_offset], data, length);
3675 m_offset = end_offset;
3676 if (m_offset > m_size)
3677 m_size = m_offset;
3678 m_state = state_t::ok;
3679 return length;
3680 }
3681
3685 void write_byte(_In_ uint8_t byte, _In_ size_t amount = 1)
3686 {
3687#if SET_FILE_OP_TIMES
3688 m_atime = m_mtime = time_point::now();
3689#endif
3690 size_t end_offset = m_offset + amount;
3691 if (end_offset > m_reserved) {
3692 reserve(end_offset);
3693 if (!ok()) _Unlikely_
3694 return;
3695 }
3696 memset(&m_data[m_offset], byte, amount);
3697 m_offset = end_offset;
3698 if (m_offset > m_size)
3699 m_size = m_offset;
3700 m_state = state_t::ok;
3701 }
3702
3717 template <class T>
3718 memory_file& write_data(const T data)
3719 {
3720#if SET_FILE_OP_TIMES
3721 m_atime = m_mtime = time_point::now();
3722#endif
3723 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3724 return *this;
3725 size_t end_offset = m_offset + sizeof(T);
3726 if (end_offset > m_reserved) {
3727 reserve(end_offset);
3728 if (!ok()) _Unlikely_
3729 return *this;
3730 }
3731 (*reinterpret_cast<T*>(&m_data[m_offset])) = HE2LE(data);
3732 m_offset = end_offset;
3733 if (m_offset > m_size)
3734 m_size = m_offset;
3735#if !CHECK_STREAM_STATE
3736 m_state = state_t::ok;
3737#endif
3738 return *this;
3739 }
3740
3755 template <class T>
3756 memory_file& write_str(_In_z_ const T * data)
3757 {
3758#if SET_FILE_OP_TIMES
3759 m_atime = m_mtime = time_point::now();
3760#endif
3761 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3762 return *this;
3763 size_t num_chars = stdex::strlen(data);
3764 if (num_chars > UINT32_MAX)
3765 throw std::invalid_argument("string too long");
3766 size_t size_chars = num_chars * sizeof(T);
3767 size_t size = sizeof(uint32_t) + size_chars;
3768 size_t end_offset = m_offset + size;
3769 if (end_offset > m_reserved) {
3770 reserve(end_offset);
3771 if (!ok()) _Unlikely_
3772 return *this;
3773 }
3774 auto p = &m_data[m_offset];
3775 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3776 memcpy(p + sizeof(uint32_t), data, size_chars);
3777 m_offset = end_offset;
3778 if (m_offset > m_size)
3779 m_size = m_offset;
3780#if !CHECK_STREAM_STATE
3781 m_state = state_t::ok;
3782#endif
3783 return *this;
3784 }
3785
3800 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3801 memory_file& write_str(_In_ const std::basic_string<T, TR, AX>& data)
3802 {
3803#if SET_FILE_OP_TIMES
3804 m_atime = m_mtime = time_point::now();
3805#endif
3806 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3807 return *this;
3808 size_t num_chars = data.size();
3809 if (num_chars > UINT32_MAX)
3810 throw std::invalid_argument("string too long");
3811 size_t size_chars = num_chars * sizeof(T);
3812 size_t size = sizeof(uint32_t) + size_chars;
3813 size_t end_offset = m_offset + size;
3814 if (end_offset > m_reserved) {
3815 reserve(end_offset);
3816 if (!ok()) _Unlikely_
3817 return *this;
3818 }
3819 auto p = &m_data[m_offset];
3820 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3821 memcpy(p + sizeof(uint32_t), data.data(), size_chars);
3822 m_offset = end_offset;
3823 if (m_offset > m_size)
3824 m_size = m_offset;
3825#if !CHECK_STREAM_STATE
3826 m_state = state_t::ok;
3827#endif
3828 return *this;
3829 }
3830
3836 size_t write_stream(_Inout_ basic & stream, _In_ size_t amount = SIZE_MAX)
3837 {
3838#if SET_FILE_OP_TIMES
3839 m_atime = m_mtime = time_point::now();
3840#endif
3841 size_t num_read, dst_offset = m_offset, dst_size = m_offset;
3842 size_t num_copied = 0, to_write = amount;
3843 m_state = state_t::ok;
3844 if (amount != SIZE_MAX) {
3845 dst_size = stdex::add(dst_size, amount);
3846 reserve(dst_size);
3847 if (!ok()) _Unlikely_
3848 return 0;
3849 while (to_write) {
3850 num_read = stream.read(&m_data[dst_offset], to_write);
3851 /*dst_size =*/ dst_offset += num_read;
3852 num_copied += num_read;
3853 to_write -= num_read;
3854 if (!stream.ok()) {
3855 if (stream.state() != state_t::eof)
3856 m_state = state_t::fail;
3857 break;
3858 }
3859 };
3860 }
3861 else {
3862 size_t block_size;
3863 while (to_write) {
3864 block_size = std::min(to_write, default_block_size);
3865 dst_size = stdex::add(dst_size, block_size);
3866 reserve(dst_size);
3867 if (!ok()) _Unlikely_
3868 break;
3869 num_read = stream.read(&m_data[dst_offset], block_size);
3870 dst_size = dst_offset += num_read;
3871 num_copied += num_read;
3872 to_write -= num_read;
3873 if (!stream.ok()) {
3874 if (stream.state() != state_t::eof)
3875 m_state = state_t::fail;
3876 break;
3877 }
3878 };
3879 }
3880 m_offset = dst_offset;
3881 if (m_offset > m_size)
3882 m_size = m_offset;
3883 return num_copied;
3884 }
3885
3886 virtual void close()
3887 {
3888 if (m_manage && m_data)
3889 free(m_data);
3890 m_data = nullptr;
3891 m_manage = true;
3892 m_offset = 0;
3893 m_size = m_reserved = 0;
3894#if SET_FILE_OP_TIMES
3895 m_ctime = m_atime = m_mtime = time_point::min();
3896#endif
3897 m_state = state_t::ok;
3898 }
3899
3900 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
3901 {
3902 switch (how) {
3903 case seek_t::beg: break;
3904 case seek_t::cur: offset = static_cast<foff_t>(m_offset) + offset; break;
3905 case seek_t::end: offset = static_cast<foff_t>(m_size) + offset; break;
3906 default: throw std::invalid_argument("unknown seek origin");
3907 }
3908 if (offset < 0) _Unlikely_
3909 throw std::invalid_argument("negative file offset");
3910 if (static_cast<fpos_t>(offset) > SIZE_MAX) _Unlikely_
3911 throw std::invalid_argument("file offset too big");
3912 m_state = state_t::ok;
3913 return m_offset = static_cast<size_t>(offset);
3914 }
3915
3916 virtual fpos_t tell() const
3917 {
3918 return m_offset;
3919 }
3920
3921 virtual fsize_t size() const
3922 {
3923 return m_size;
3924 }
3925
3926 virtual void truncate()
3927 {
3928#if SET_FILE_OP_TIMES
3929 m_atime = m_mtime = time_point::now();
3930#endif
3931 m_size = m_offset;
3932 reserve(m_offset);
3933 }
3934
3935#if SET_FILE_OP_TIMES
3936 virtual time_point ctime() const
3937 {
3938 return m_ctime;
3939 }
3940
3941 virtual time_point atime() const
3942 {
3943 return m_atime;
3944 }
3945
3946 virtual time_point mtime() const
3947 {
3948 return m_mtime;
3949 }
3950
3951 virtual void set_ctime(time_point date)
3952 {
3953 m_ctime = date;
3954 }
3955
3956 virtual void set_atime(time_point date)
3957 {
3958 m_atime = date;
3959 }
3960
3961 virtual void set_mtime(time_point date)
3962 {
3963 m_mtime = date;
3964 }
3965#endif
3966
3967 protected:
3975 template <class T>
3976 void set(_In_ fpos_t offset, _In_ const T data)
3977 {
3978#if SET_FILE_OP_TIMES
3979 m_atime = m_mtime = time_point::now();
3980#endif
3981 stdex_assert(offset + sizeof(T) < m_size);
3982 (*reinterpret_cast<T*>(&m_data[offset])) = HE2LE(data);
3983 }
3984
3985 public:
3986 void set(_In_ fpos_t offset, _In_ const int8_t data) { set<int8_t>(offset, data); }
3987 void set(_In_ fpos_t offset, _In_ const int16_t data) { set<int16_t>(offset, data); }
3988 void set(_In_ fpos_t offset, _In_ const int32_t data) { set<int32_t>(offset, data); }
3989 void set(_In_ fpos_t offset, _In_ const int64_t data) { set<int64_t>(offset, data); }
3990 void set(_In_ fpos_t offset, _In_ const uint8_t data) { set<uint8_t>(offset, data); }
3991 void set(_In_ fpos_t offset, _In_ const uint16_t data) { set<uint16_t>(offset, data); }
3992 void set(_In_ fpos_t offset, _In_ const uint32_t data) { set<uint32_t>(offset, data); }
3993 void set(_In_ fpos_t offset, _In_ const uint64_t data) { set<uint64_t>(offset, data); }
3994 void set(_In_ fpos_t offset, _In_ const float data) { set<float>(offset, data); }
3995 void set(_In_ fpos_t offset, _In_ const double data) { set<double>(offset, data); }
3996 void set(_In_ fpos_t offset, _In_ const char data) { set<char>(offset, data); }
3997#ifdef _NATIVE_WCHAR_T_DEFINED
3998 void set(_In_ fpos_t offset, _In_ const wchar_t data) { set<wchar_t>(offset, data); }
3999#endif
4000
4008 protected:
4009 template <class T>
4010 void get(_In_ fpos_t offset, _Out_ T & data)
4011 {
4012 stdex_assert(offset + sizeof(T) < m_size);
4013 data = LE2HE(*(T*)(&m_data[offset]));
4014#if SET_FILE_OP_TIMES
4015 m_atime = time_point::now();
4016#endif
4017 }
4018
4019 public:
4020 void get(_In_ fpos_t offset, _Out_ int8_t & data) { get<int8_t>(offset, data); }
4021 void get(_In_ fpos_t offset, _Out_ int16_t & data) { get<int16_t>(offset, data); }
4022 void get(_In_ fpos_t offset, _Out_ int32_t & data) { get<int32_t>(offset, data); }
4023 void get(_In_ fpos_t offset, _Out_ int64_t & data) { get<int64_t>(offset, data); }
4024 void get(_In_ fpos_t offset, _Out_ uint8_t & data) { get<uint8_t>(offset, data); }
4025 void get(_In_ fpos_t offset, _Out_ uint16_t & data) { get<uint16_t>(offset, data); }
4026 void get(_In_ fpos_t offset, _Out_ uint32_t & data) { get<uint32_t>(offset, data); }
4027 void get(_In_ fpos_t offset, _Out_ uint64_t & data) { get<uint64_t>(offset, data); }
4028 void get(_In_ fpos_t offset, _Out_ float& data) { get<float>(offset, data); }
4029 void get(_In_ fpos_t offset, _Out_ double& data) { get<double>(offset, data); }
4030 void get(_In_ fpos_t offset, _Out_ char& data) { get<char>(offset, data); }
4031#ifdef _NATIVE_WCHAR_T_DEFINED
4032 void get(_In_ fpos_t offset, _Out_ wchar_t& data) { get<wchar_t>(offset, data); }
4033#endif
4034
4035 memory_file& operator <<(_In_ const int8_t data) { return write_data(data); }
4036 memory_file& operator >>(_Out_ int8_t & data) { return read_data(data); }
4037 memory_file& operator <<(_In_ const int16_t data) { return write_data(data); }
4038 memory_file& operator >>(_Out_ int16_t & data) { return read_data(data); }
4039 memory_file& operator <<(_In_ const int32_t data) { return write_data(data); }
4040 memory_file& operator >>(_Out_ int32_t & data) { return read_data(data); }
4041 memory_file& operator <<(_In_ const int64_t data) { return write_data(data); }
4042 memory_file& operator >>(_Out_ int64_t & data) { return read_data(data); }
4043 memory_file& operator <<(_In_ const uint8_t data) { return write_data(data); }
4044 memory_file& operator >>(_Out_ uint8_t & data) { return read_data(data); }
4045 memory_file& operator <<(_In_ const uint16_t data) { return write_data(data); }
4046 memory_file& operator >>(_Out_ uint16_t & data) { return read_data(data); }
4047 memory_file& operator <<(_In_ const uint32_t data) { return write_data(data); }
4048 memory_file& operator >>(_Out_ uint32_t & data) { return read_data(data); }
4049 memory_file& operator <<(_In_ const uint64_t data) { return write_data(data); }
4050 memory_file& operator >>(_Out_ uint64_t & data) { return read_data(data); }
4051 memory_file& operator <<(_In_ const float data) { return write_data(data); }
4052 memory_file& operator >>(_Out_ float& data) { return read_data(data); }
4053 memory_file& operator <<(_In_ const double data) { return write_data(data); }
4054 memory_file& operator >>(_Out_ double& data) { return read_data(data); }
4055 memory_file& operator <<(_In_ const char data) { return write_data(data); }
4056 memory_file& operator >>(_Out_ char& data) { return read_data(data); }
4057#ifdef _NATIVE_WCHAR_T_DEFINED
4058 memory_file& operator <<(_In_ const wchar_t data) { return write_data(data); }
4059 memory_file& operator >>(_Out_ wchar_t& data) { return read_data(data); }
4060#endif
4061 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
4062 memory_file& operator >>(_Out_ std::basic_string<T, TR, AX>&data) { return read_str(data); }
4063 template <class T>
4064 memory_file& operator <<(_In_ const T * data) { return write_str(data); }
4065 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
4066 memory_file& operator <<(_In_ const std::basic_string<T, TR, AX>& data) { return write_str(data); }
4067
4068 protected:
4069 uint8_t* m_data;
4070 bool m_manage;
4071 size_t m_offset;
4072 size_t m_size;
4073 size_t m_reserved;
4074#if SET_FILE_OP_TIMES
4075 time_point
4076 m_ctime,
4077 m_atime,
4078 m_mtime;
4079#endif
4080 };
4081
4085 class fifo : public basic {
4086 public:
4087 fifo() :
4088 m_offset(0),
4089 m_size(0),
4090 m_head(nullptr),
4091 m_tail(nullptr)
4092 {}
4093
4094 virtual ~fifo()
4095 {
4096 while (m_head) {
4097 auto p = m_head;
4098 m_head = p->next;
4099 delete p;
4100 }
4101 }
4102
4103#pragma warning(suppress: 6101) // See [2] below
4104 virtual _Success_(return != 0 || length == 0) size_t read(
4105 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4106 {
4107 stdex_assert(data || !length);
4108 for (size_t to_read = length;;) {
4109 if (!m_head) _Unlikely_ {
4110 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
4111 return length - to_read; // [2] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
4112 }
4113 size_t remaining = m_head->size - m_offset;
4114 if (remaining > to_read) {
4115 memcpy(data, m_head->data + m_offset, to_read);
4116 m_offset += to_read;
4117 m_size -= to_read;
4118 m_state = state_t::ok;
4119 return length;
4120 }
4121 memcpy(data, m_head->data + m_offset, remaining);
4122 m_offset = 0;
4123 m_size -= remaining;
4124 reinterpret_cast<uint8_t*&>(data) += remaining;
4125 to_read -= remaining;
4126 auto p = m_head;
4127 m_head = p->next;
4128 delete p;
4129 }
4130 }
4131
4132 virtual _Success_(return != 0) size_t write(
4133 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4134 {
4135 stdex_assert(data || !length);
4136 try {
4137 std::unique_ptr<node_t> n(reinterpret_cast<node_t*>(new uint8_t[sizeof(node_t) + length]));
4138 n->next = nullptr;
4139 n->size = length;
4140 memcpy(n->data, data, length);
4141 m_size += length;
4142 if (m_head)
4143 m_tail = m_tail->next = n.release();
4144 else
4145 m_head = m_tail = n.release();
4146 m_state = state_t::ok;
4147 return length;
4148 }
4149 catch (const std::bad_alloc&) {
4150 m_state = state_t::fail;
4151 return 0;
4152 }
4153 }
4154
4155 virtual void close()
4156 {
4157 m_size = m_offset = 0;
4158 while (m_head) {
4159 auto p = m_head;
4160 m_head = p->next;
4161 delete p;
4162 }
4163 m_state = state_t::ok;
4164 }
4165
4169 size_t size() const { return m_size; };
4170
4171 protected:
4172 size_t m_offset, m_size;
4173 struct node_t {
4174 node_t* next;
4175 size_t size;
4176#pragma warning(suppress:4200)
4177 uint8_t data[0];
4178 } *m_head, * m_tail;
4179 };
4180
4184 class diag_file : public basic_file {
4185 public:
4186 diag_file(_In_count_(num_files) basic_file* const* files, _In_ size_t num_files) :
4187 basic(num_files ? files[0]->state() : state_t::fail),
4188 m_files(files, files + num_files)
4189 {}
4190
4191 virtual _Success_(return != 0 || length == 0) size_t read(
4192 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4193 {
4194 stdex_assert(data || !length);
4195 if (m_files.empty()) {
4196 m_state = state_t::fail;
4197 return 0;
4198 }
4199 size_t result = m_files[0]->read(data, length);
4200 stdex_assert(result <= length);
4201 m_state = m_files[0]->state();
4202 if (length > m_tmp.size())
4203 m_tmp.resize(length);
4204 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4205 if (m_files[i]->read(m_tmp.data(), length) != result ||
4206 memcmp(m_tmp.data(), data, result))
4207 throw std::runtime_error("read mismatch");
4208 if (m_files[i]->state() != m_state)
4209 throw std::runtime_error("state mismatch");
4210 }
4211 return result;
4212 }
4213
4214 virtual _Success_(return != 0) size_t write(
4215 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4216 {
4217 if (m_files.empty()) {
4218 m_state = state_t::fail;
4219 return 0;
4220 }
4221 size_t result = m_files[0]->write(data, length);
4222 m_state = m_files[0]->state();
4223 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4224 if (m_files[i]->write(data, length) != result)
4225 throw std::runtime_error("write mismatch");
4226 if (m_files[i]->state() != m_state)
4227 throw std::runtime_error("state mismatch");
4228 }
4229 return result;
4230 }
4231
4232 virtual void flush()
4233 {
4234 if (m_files.empty()) {
4235 m_state = state_t::ok;
4236 return;
4237 }
4238 m_files[0]->flush();
4239 m_state = m_files[0]->state();
4240 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4241 m_files[i]->flush();
4242 if (m_files[i]->state() != m_state)
4243 throw std::runtime_error("state mismatch");
4244 }
4245 }
4246
4247 virtual void close()
4248 {
4249 if (m_files.empty()) {
4250 m_state = state_t::ok;
4251 return;
4252 }
4253 m_files[0]->close();
4254 m_state = m_files[0]->state();
4255 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4256 m_files[i]->close();
4257 if (m_files[i]->state() != m_state)
4258 throw std::runtime_error("state mismatch");
4259 }
4260 m_tmp.clear();
4261 m_tmp.shrink_to_fit();
4262 }
4263
4264 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
4265 {
4266 if (m_files.empty()) {
4267 m_state = state_t::fail;
4268 return fpos_max;
4269 }
4270 fpos_t result = m_files[0]->seek(offset, how);
4271 m_state = m_files[0]->state();
4272 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4273 if (m_files[i]->seek(offset, how) != result)
4274 throw std::runtime_error("seek mismatch");
4275 if (m_files[i]->state() != m_state)
4276 throw std::runtime_error("state mismatch");
4277 }
4278 return result;
4279 }
4280
4281 virtual fpos_t tell() const
4282 {
4283 if (m_files.empty())
4284 return fpos_max;
4285 fpos_t result = m_files[0]->tell();
4286 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4287 if (m_files[i]->tell() != result)
4288 throw std::runtime_error("tell mismatch");
4289 }
4290 return result;
4291 }
4292
4293 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
4294 {
4295 if (m_files.empty())
4296 m_state = state_t::fail;
4297 m_files[0]->lock(offset, length);
4298 m_state = m_files[0]->state();
4299 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4300 m_files[i]->lock(offset, length);
4301 if (m_files[i]->state() != m_state)
4302 throw std::runtime_error("state mismatch");
4303 }
4304 }
4305
4306 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
4307 {
4308 if (m_files.empty())
4309 m_state = state_t::fail;
4310 m_files[0]->unlock(offset, length);
4311 m_state = m_files[0]->state();
4312 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4313 m_files[i]->unlock(offset, length);
4314 if (m_files[i]->state() != m_state)
4315 throw std::runtime_error("state mismatch");
4316 }
4317 }
4318
4319 virtual fsize_t size() const
4320 {
4321 if (m_files.empty())
4322 return fsize_max;
4323 fsize_t result = m_files[0]->size();
4324 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4325 if (m_files[i]->size() != result)
4326 throw std::runtime_error("size mismatch");
4327 }
4328 return result;
4329 }
4330
4331 virtual void truncate()
4332 {
4333 if (m_files.empty())
4334 m_state = state_t::fail;
4335 m_files[0]->truncate();
4336 m_state = m_files[0]->state();
4337 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4338 m_files[i]->truncate();
4339 if (m_files[i]->state() != m_state)
4340 throw std::runtime_error("state mismatch");
4341 }
4342 }
4343
4344 protected:
4345 std::vector<basic_file*> m_files;
4346 std::vector<uint8_t> m_tmp;
4347 };
4348 }
4349}
4350
4351#if defined(__GNUC__)
4352#pragma GCC diagnostic pop
4353#endif
stdex::basic_sys_object
Operating system object base class.
Definition system.hpp:142
stdex::charset_encoder
Encoding converter context.
Definition unicode.hpp:139
stdex::locale
locale_t helper class to free_locale when going out of scope.
Definition locale.hpp:74
stdex::stream::async_reader
Provides read-ahead stream capability.
Definition stream.hpp:1254
stdex::stream::async_writer
Provides write-back stream capability.
Definition stream.hpp:1321
stdex::stream::async_writer::flush
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1358
stdex::stream::basic_file
Basic seekable stream operations.
Definition stream.hpp:815
stdex::stream::basic_file::skip
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:861
stdex::stream::basic_file::ctime
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:910
stdex::stream::basic_file::lock
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:879
stdex::stream::basic_file::truncate
virtual void truncate()=0
Sets file size - truncates the remainder of file content from the current file position to the end of...
stdex::stream::basic_file::size
virtual fsize_t size() const =0
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
stdex::stream::basic_file::read_charset
charset_id read_charset(charset_id default_charset=charset_id::system)
Attempts to detect text-file charset based on UTF-32, UTF-16 or UTF-8 BOM.
Definition stream.hpp:985
stdex::stream::basic_file::seekbeg
fpos_t seekbeg(fpos_t offset)
Seeks to absolute file position.
Definition stream.hpp:842
stdex::stream::basic_file::read_remainder
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:817
stdex::stream::basic_file::set_mtime
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:952
stdex::stream::basic_file::seekcur
fpos_t seekcur(foff_t offset)
Seeks to relative from current file position.
Definition stream.hpp:852
stdex::stream::basic_file::atime
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:918
stdex::stream::basic_file::set_ctime
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:934
stdex::stream::basic_file::unlock
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:889
stdex::stream::basic_file::tell
virtual fpos_t tell() const =0
Returns absolute file position in file or fpos_max if fails. This method does not update stream state...
stdex::stream::basic_file::mtime
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:926
stdex::stream::basic_file::seekend
fpos_t seekend(foff_t offset)
Seeks to relative from end file position.
Definition stream.hpp:859
stdex::stream::basic_file::set_atime
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:943
stdex::stream::basic_file::seek
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)=0
Seeks to specified relative file position.
stdex::stream::basic_sys
OS data stream (file, pipe, socket...)
Definition stream.hpp:2209
stdex::stream::basic_sys::write
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:2266
stdex::stream::basic_sys::flush
virtual void flush()
Persists volatile element data.
Definition stream.hpp:2324
stdex::stream::basic_sys::close
virtual void close()
Closes the stream.
Definition stream.hpp:2313
stdex::stream::basic
Basic stream operations.
Definition stream.hpp:85
stdex::stream::basic::write_array
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:408
stdex::stream::basic::ok
bool ok() const
Returns true if the stream state is clean i.e. previous operation was successful.
Definition stream.hpp:181
stdex::stream::basic::readln_and_attach
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:340
stdex::stream::basic::write_vsprintf
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:628
stdex::stream::basic::state
state_t state() const
Returns stream state after last operation.
Definition stream.hpp:176
stdex::stream::basic::write_sprintf
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:614
stdex::stream::basic::write_vsprintf
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:641
stdex::stream::basic::flush
virtual void flush()
Persists volatile element data.
Definition stream.hpp:132
stdex::stream::basic::skip
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:148
stdex::stream::basic::close
virtual void close()
Closes the stream.
Definition stream.hpp:140
stdex::stream::basic::read_byte
uint8_t read_byte()
Reads one byte of data.
Definition stream.hpp:216
stdex::stream::basic::read_remainder
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:190
stdex::stream::basic::write_sprintf
size_t write_sprintf(_Printf_format_string_params_(2) const char *format, locale_t locale,...)
Writes formatted string to the stream.
Definition stream.hpp:600
stdex::stream::basic::readln
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:324
stdex::stream::basic::read_array
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:376
stdex::stream::basic::readln
size_t readln(std::basic_string< T, TR, AX > &str)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:312
stdex::stream::basic::read_str
basic & read_str(std::basic_string< T, TR, AX > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:471
stdex::stream::basic::write_str
basic & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:504
stdex::stream::basic::readln_and_attach
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:361
stdex::stream::basic::read
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:102
stdex::stream::basic::write_charset
void write_charset(charset_id charset)
Writes UTF8, UTF-16 or UTF-32 byte-order-mark.
Definition stream.hpp:585
stdex::stream::basic::write_data
basic & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:293
stdex::stream::basic::write_stream
fsize_t write_stream(basic &stream, fsize_t amount=fsize_max)
Writes content of another stream.
Definition stream.hpp:560
stdex::stream::basic::write_array
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:449
stdex::stream::basic::write
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:120
stdex::stream::basic::write_str
basic & write_str(const std::basic_string< T, TR, AX > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:529
stdex::stream::basic::write_array
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:429
stdex::stream::basic::write_array
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:394
stdex::stream::basic::write_byte
void write_byte(uint8_t byte, fsize_t amount=1)
Writes a byte of data.
Definition stream.hpp:227
stdex::stream::basic::read_data
basic & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:265
stdex::stream::buffer
Buffered read/write stream.
Definition stream.hpp:1392
stdex::stream::buffer::flush
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1501
stdex::stream::buffered_sys
Buffered OS data stream (file, pipe, socket...)
Definition stream.hpp:2338
stdex::stream::cache
Cached file.
Definition stream.hpp:1802
stdex::stream::cache::ctime
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2079
stdex::stream::cache::truncate
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2058
stdex::stream::cache::atime
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2084
stdex::stream::cache::unlock
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2045
stdex::stream::cache::mtime
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:2093
stdex::stream::cache::close
virtual void close()
Closes the stream.
Definition stream.hpp:1988
stdex::stream::cache::set_mtime
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:2115
stdex::stream::cache::lock
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2039
stdex::stream::cache::flush
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1997
stdex::stream::cache::size
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:2051
stdex::stream::cache::set_ctime
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:2102
stdex::stream::cache::tell
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:2034
stdex::stream::cache::set_atime
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:2107
stdex::stream::cache::seek
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2008
stdex::stream::cached_file
Cached file-system file.
Definition stream.hpp:3135
stdex::stream::cached_file::open
void open(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:3201
stdex::stream::cached_file::open
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:3179
stdex::stream::cached_file::cached_file
cached_file(const schar_t *filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3151
stdex::stream::cached_file::cached_file
cached_file(const std::basic_string< TR, AX > &filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3166
stdex::stream::converter
Modifies data on the fly when reading from/writing to a source stream. Could also be used to modify r...
Definition stream.hpp:1020
stdex::stream::converter::flush
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1071
stdex::stream::converter::close
virtual void close()
Closes the stream.
Definition stream.hpp:1065
stdex::stream::converter::read
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:1049
stdex::stream::converter::write
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:1057
stdex::stream::diag_file
Compares multiple files to perform the same.
Definition stream.hpp:4184
stdex::stream::diag_file::truncate
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:4331
stdex::stream::diag_file::close
virtual void close()
Closes the stream.
Definition stream.hpp:4247
stdex::stream::diag_file::size
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:4319
stdex::stream::diag_file::lock
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:4293
stdex::stream::diag_file::unlock
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:4306
stdex::stream::diag_file::seek
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:4264
stdex::stream::diag_file::tell
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:4281
stdex::stream::diag_file::flush
virtual void flush()
Persists volatile element data.
Definition stream.hpp:4232
stdex::stream::fifo
In-memory FIFO queue.
Definition stream.hpp:4085
stdex::stream::fifo::close
virtual void close()
Closes the stream.
Definition stream.hpp:4155
stdex::stream::fifo::size
size_t size() const
Returns total size of pending data in the queue.
Definition stream.hpp:4169
stdex::stream::file_window
Limits file reading/writing to a predefined window.
Definition stream.hpp:1692
stdex::stream::file_window::truncate
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:1785
stdex::stream::file_window::flush
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1735
stdex::stream::file_window::skip
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:1748
stdex::stream::file_window::seek
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:1741
stdex::stream::file_window::lock
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:1760
stdex::stream::file_window::unlock
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:1770
stdex::stream::file_window::tell
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:1754
stdex::stream::file_window::close
virtual void close()
Closes the stream.
Definition stream.hpp:1729
stdex::stream::file_window::size
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:1780
stdex::stream::file
File-system file.
Definition stream.hpp:2716
stdex::stream::file::size
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:2920
stdex::stream::file::readonly
static bool readonly(const std::basic_string< TR, AX > &filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3124
stdex::stream::file::mtime
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:3004
stdex::stream::file::unlock
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2891
stdex::stream::file::file
file(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2726
stdex::stream::file::set_ctime
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:3018
stdex::stream::file::readonly
static bool readonly(const stdex::schar_t *filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3105
stdex::stream::file::atime
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2990
stdex::stream::file::exists
static bool exists(const std::basic_string< TR, AX > &filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3093
stdex::stream::file::open
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2746
stdex::stream::file::set_mtime
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:3053
stdex::stream::file::set_atime
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:3033
stdex::stream::file::lock
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2864
stdex::stream::file::truncate
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2940
stdex::stream::file::open
void open(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:2820
stdex::stream::file::ctime
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2980
stdex::stream::file::seek
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2825
stdex::stream::file::tell
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:2846
stdex::stream::file::file
file(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:2738
stdex::stream::file::exists
static bool exists(const stdex::schar_t *filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3077
stdex::stream::limiter
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1549
stdex::stream::limiter::read_limit
fsize_t read_limit
Number of bytes left that may be read from the stream.
Definition stream.hpp:1601
stdex::stream::limiter::write_limit
fsize_t write_limit
Number of bytes left, that can be written to the stream.
Definition stream.hpp:1602
stdex::stream::memory_file
In-memory file.
Definition stream.hpp:3219
stdex::stream::memory_file::save
void save(const std::basic_string< TR, AX > &filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3541
stdex::stream::memory_file::operator=
memory_file & operator=(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3402
stdex::stream::memory_file::size
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:3921
stdex::stream::memory_file::memory_file
memory_file(const schar_t *filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3299
stdex::stream::memory_file::read_str
memory_file & read_str(std::basic_string< T, TR, AX > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:3630
stdex::stream::memory_file::memory_file
memory_file(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3318
stdex::stream::memory_file::write_str
memory_file & write_str(const std::basic_string< T, TR, AX > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3801
stdex::stream::memory_file::load
void load(const std::basic_string< TR, AX > &filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3503
stdex::stream::memory_file::m_size
size_t m_size
file size
Definition stream.hpp:4072
stdex::stream::memory_file::get
void get(fpos_t offset, T &data)
Reads data from specified file location This does not move file pointer. It checks for data size stde...
Definition stream.hpp:4010
stdex::stream::memory_file::write_stream
size_t write_stream(basic &stream, size_t amount=SIZE_MAX)
Writes content of another stream.
Definition stream.hpp:3836
stdex::stream::memory_file::m_data
uint8_t * m_data
file data
Definition stream.hpp:4069
stdex::stream::memory_file::read_data
memory_file & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:3590
stdex::stream::memory_file::close
virtual void close()
Closes the stream.
Definition stream.hpp:3886
stdex::stream::memory_file::memory_file
memory_file(const std::basic_string< TR, AX > &filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3311
stdex::stream::memory_file::tell
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:3916
stdex::stream::memory_file::m_reserved
size_t m_reserved
reserved file size
Definition stream.hpp:4073
stdex::stream::memory_file::memory_file
memory_file(size_t size, state_t state=state_t::ok)
Creates an empty file of reserved size.
Definition stream.hpp:3240
stdex::stream::memory_file::reserve
void reserve(size_t required, bool tight=false) noexcept
Reallocates memory.
Definition stream.hpp:3440
stdex::stream::memory_file::memory_file
memory_file(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3373
stdex::stream::memory_file::write_byte
void write_byte(uint8_t byte, size_t amount=1)
Writes a byte of data.
Definition stream.hpp:3685
stdex::stream::memory_file::operator=
memory_file & operator=(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3343
stdex::stream::memory_file::set
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:3976
stdex::stream::memory_file::m_offset
size_t m_offset
file pointer
Definition stream.hpp:4071
stdex::stream::memory_file::save
void save(const schar_t *filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3514
stdex::stream::memory_file::load
void load(const schar_t *filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3469
stdex::stream::memory_file::seek
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3900
stdex::stream::memory_file::truncate
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:3926
stdex::stream::memory_file::write_data
memory_file & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:3718
stdex::stream::memory_file::write_str
memory_file & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3756
stdex::stream::memory_file::m_manage
bool m_manage
may reallocate m_data?
Definition stream.hpp:4070
stdex::stream::memory_file::memory_file
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:3289
stdex::stream::memory_file::memory_file
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:3266
stdex::stream::memory_file::data
const void * data() const
Returns pointer to data.
Definition stream.hpp:3549
stdex::stream::replicator::worker
Definition stream.hpp:1168
stdex::stream::replicator::worker::num_written
size_t num_written
Number of bytes written.
Definition stream.hpp:1217
stdex::stream::replicator::worker::length
size_t length
Byte limit of data to write.
Definition stream.hpp:1216
stdex::stream::replicator::worker::data
const void * data
Data to write.
Definition stream.hpp:1215
stdex::stream::replicator
Replicates writing of the same data to multiple streams.
Definition stream.hpp:1085
stdex::stream::replicator::push_back
void push_back(basic *source)
Adds stream on the list.
Definition stream.hpp:1104
stdex::stream::replicator::flush
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1161
stdex::stream::replicator::remove
void remove(basic *source)
Removes stream from the list.
Definition stream.hpp:1112
stdex::stream::replicator::write
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:1129
stdex::stream::replicator::close
virtual void close()
Closes the stream.
Definition stream.hpp:1156
stdex::stream::socket
Socket stream.
Definition stream.hpp:2360
stdex::stream::socket::get
socket_t get() const noexcept
Returns socket handle.
Definition stream.hpp:2416
stdex::stream::socket::close
virtual void close()
Closes the stream.
Definition stream.hpp:2474
stdex::stream::socket::socket
socket(int af, int type, int protocol)
Creates a socket.
Definition stream.hpp:2395
stdex::stream::window
Limits reading from/writing to stream to a predefined window.
Definition stream.hpp:1609
stdex::stream::window::write_offset
fpos_t write_offset
Number of bytes to discard on write.
Definition stream.hpp:1685
stdex::stream::window::read_offset
fpos_t read_offset
Number of bytes to skip on read.
Definition stream.hpp:1684
stdex::interval
Numerical interval.
Definition interval.hpp:18
stdex::stream::buffer::buffer_t
Definition stream.hpp:1526
stdex::stream::fifo::node_t
Definition stream.hpp:4173