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
56namespace 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;
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, previous = (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;
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 long ubound, lbound;
551 if (FAILED(SafeArrayGetUBound(sa, 1, &ubound)) ||
552 FAILED(SafeArrayGetLBound(sa, 1, &lbound)))
553 throw std::invalid_argument("SafeArrayGet[UL]Bound failed");
554 safearray_accessor<void> a(sa);
555 return write(a.data(), static_cast<size_t>(ubound) - lbound + 1);
556 }
557#endif
558
564 fsize_t write_stream(_Inout_ basic& stream, _In_ fsize_t amount = fsize_max)
565 {
566 std::unique_ptr<uint8_t[]> data(new uint8_t[static_cast<size_t>(std::min<fsize_t>(amount, default_block_size))]);
567 fsize_t num_copied = 0, to_write = amount;
568 m_state = state_t::ok;
569 while (to_write) {
570 size_t num_read = stream.read(data.get(), static_cast<size_t>(std::min<fsize_t>(default_block_size, to_write)));
571 size_t num_written = write(data.get(), num_read);
572 num_copied += num_written;
573 to_write -= num_written;
574 if (stream.m_state == state_t::eof) {
575 // EOF is not an error.
576 m_state = state_t::ok;
577 break;
578 }
579 m_state = stream.m_state;
580 if (!ok())
581 break;
582 }
583 return num_copied;
584 }
585
589 void write_charset(_In_ charset_id charset)
590 {
591 if (charset == charset_id::utf32)
592 write_data(utf32_bom);
593 else if (charset == charset_id::utf16)
594 write_data(utf16_bom);
595 else if (charset == charset_id::utf8)
596 write_array(utf8_bom, sizeof(utf8_bom), 1);
597 }
598
604 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, ...)
605 {
606 va_list params;
607 va_start(params, locale);
608 size_t num_chars = write_vsprintf(format, locale, params);
609 va_end(params);
610 return num_chars;
611 }
612
618 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, ...)
619 {
620 va_list params;
621 va_start(params, locale);
622 size_t num_chars = write_vsprintf(format, locale, params);
623 va_end(params);
624 return num_chars;
625 }
626
632 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, _In_ va_list params)
633 {
634 std::string tmp;
635 tmp.reserve(default_block_size);
636 vappendf(tmp, format, locale, params);
637 return write_array(tmp.data(), sizeof(char), tmp.size());
638 }
639
645 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, _In_ va_list params)
646 {
647 std::wstring tmp;
648 tmp.reserve(default_block_size);
649 vappendf(tmp, format, locale, params);
650 return write_array(tmp.data(), sizeof(wchar_t), tmp.size());
651 }
652
653 basic& operator >>(_Out_ int8_t& data) { return read_data(data); }
654 basic& operator <<(_In_ const int8_t data) { return write_data(data); }
655 basic& operator >>(_Out_ int16_t& data) { return read_data(data); }
656 basic& operator <<(_In_ const int16_t data) { return write_data(data); }
657 basic& operator >>(_Out_ int32_t& data) { return read_data(data); }
658 basic& operator <<(_In_ const int32_t data) { return write_data(data); }
659 basic& operator >>(_Out_ int64_t& data) { return read_data(data); }
660 basic& operator <<(_In_ const int64_t data) { return write_data(data); }
661 basic& operator >>(_Out_ uint8_t& data) { return read_data(data); }
662 basic& operator <<(_In_ const uint8_t data) { return write_data(data); }
663 basic& operator >>(_Out_ uint16_t& data) { return read_data(data); }
664 basic& operator <<(_In_ const uint16_t data) { return write_data(data); }
665 basic& operator >>(_Out_ uint32_t& data) { return read_data(data); }
666 basic& operator <<(_In_ const uint32_t data) { return write_data(data); }
667 basic& operator >>(_Out_ uint64_t& data) { return read_data(data); }
668 basic& operator <<(_In_ const uint64_t data) { return write_data(data); }
669 basic& operator >>(_Out_ float& data) { return read_data(data); }
670 basic& operator <<(_In_ const float data) { return write_data(data); }
671 basic& operator >>(_Out_ double& data) { return read_data(data); }
672 basic& operator <<(_In_ const double data) { return write_data(data); }
673 basic& operator >>(_Out_ char& data) { return read_data(data); }
674 basic& operator <<(_In_ const char data) { return write_data(data); }
675#ifdef _NATIVE_WCHAR_T_DEFINED
676 basic& operator >>(_Out_ wchar_t& data) { return read_data(data); }
677 basic& operator <<(_In_ const wchar_t data) { return write_data(data); }
678#endif
679 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
680 basic& operator >>(_Out_ std::basic_string<T, TR, AX>& data) { return read_str(data); }
681 template <class T>
682 basic& operator <<(_In_ const T* data) { return write_str(data); }
683 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
684 basic& operator <<(_In_ const std::basic_string<T, TR, AX>& data) { return write_str(data); }
685
686 template <class T, class AX = std::allocator<T>>
687 basic& operator <<(_In_ const std::vector<T, AX>& data)
688 {
689 size_t num = data.size();
690 if (num > UINT32_MAX) _Unlikely_
691 throw std::invalid_argument("collection too big");
692 *this << static_cast<uint32_t>(num);
693 for (auto& el : data)
694 *this << el;
695 return *this;
696 }
697
698 template <class T, class AX = std::allocator<T>>
699 basic& operator >>(_Out_ std::vector<T, AX>& data)
700 {
701 data.clear();
702 uint32_t num;
703 *this >> num;
704 if (!ok()) _Unlikely_
705 return *this;
706 data.reserve(num);
707 for (uint32_t i = 0; i < num; ++i) {
708 T el;
709 *this >> el;
710 if (!ok()) _Unlikely_
711 return *this;
712 data.push_back(std::move(el));
713 }
714 }
715
716 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
717 basic& operator <<(_In_ const std::set<KEY, PR, AX>& data)
718 {
719 size_t num = data.size();
720 if (num > UINT32_MAX) _Unlikely_
721 throw std::invalid_argument("collection too big");
722 *this << static_cast<uint32_t>(num);
723 for (auto& el : data)
724 *this << el;
725 return *this;
726 }
727
728 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
729 basic& operator >>(_Out_ std::set<KEY, PR, AX>& data)
730 {
731 data.clear();
732 uint32_t num;
733 *this >> num;
734 if (!ok()) _Unlikely_
735 return *this;
736 for (uint32_t i = 0; i < num; ++i) {
737 KEY el;
738 *this >> el;
739 if (!ok()) _Unlikely_
740 return *this;
741 data.insert(std::move(el));
742 }
743 }
744
745 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
746 basic& operator <<(_In_ const std::multiset<KEY, PR, AX>& data)
747 {
748 size_t num = data.size();
749 if (num > UINT32_MAX) _Unlikely_
750 throw std::invalid_argument("collection too big");
751 *this << static_cast<uint32_t>(num);
752 for (auto& el : data)
753 *this << el;
754 return *this;
755 }
756
757 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
758 basic& operator >>(_Out_ std::multiset<KEY, PR, AX>& data)
759 {
760 data.clear();
761 uint32_t num;
762 *this >> num;
763 if (!ok()) _Unlikely_
764 return *this;
765 for (uint32_t i = 0; i < num; ++i) {
766 KEY el;
767 *this >> el;
768 if (!ok()) _Unlikely_
769 return *this;
770 data.insert(std::move(el));
771 }
772 return *this;
773 }
774
775 protected:
776 state_t m_state;
777 };
778
782 using fpos_t = uint64_t;
783 constexpr fpos_t fpos_max = UINT64_MAX;
784 constexpr fpos_t fpos_min = 0;
785
789 using foff_t = int64_t;
790 constexpr foff_t foff_max = INT64_MAX;
791 constexpr foff_t foff_min = INT64_MIN;
792
796 enum class seek_t {
797#ifdef _WIN32
798 beg = FILE_BEGIN,
799 cur = FILE_CURRENT,
800 end = FILE_END
801#else
802 beg = SEEK_SET,
803 cur = SEEK_CUR,
804 end = SEEK_END
805#endif
806 };
807
808#if _HAS_CXX20
809 using clock = std::chrono::file_clock;
810#else
811 using clock = std::chrono::system_clock;
812#endif
813 using time_point = std::chrono::time_point<clock>;
814
818 class basic_file : virtual public basic
819 {
820 public:
821 virtual std::vector<uint8_t> read_remainder(_In_ size_t max_length = SIZE_MAX)
822 {
823 size_t length = std::min<size_t>(max_length, static_cast<size_t>(size() - tell()));
824 std::vector<uint8_t> result;
825 try { result.resize(length); }
826 catch (const std::bad_alloc&) {
827 m_state = state_t::fail;
828 return result;
829 }
830 result.resize(read_array(result.data(), sizeof(uint8_t), length));
831 return result;
832 }
833
839 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg) = 0;
840
846 fpos_t seekbeg(_In_ fpos_t offset)
847 {
848 return seek(static_cast<foff_t>(offset), seek_t::beg);
849 }
850
856 fpos_t seekcur(_In_ foff_t offset) { return seek(offset, seek_t::cur); }
857
863 fpos_t seekend(_In_ foff_t offset) { return seek(offset, seek_t::end); }
864
865 virtual void skip(_In_ fsize_t amount)
866 {
867 if (amount > foff_max)
868 throw std::invalid_argument("file offset too big");
869 seek(static_cast<foff_t>(amount), seek_t::cur);
870 }
871
878 virtual fpos_t tell() const = 0;
879
883 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
884 {
885 _Unreferenced_(offset);
886 _Unreferenced_(length);
887 throw std::domain_error("not implemented");
888 }
889
893 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
894 {
895 _Unreferenced_(offset);
896 _Unreferenced_(length);
897 throw std::domain_error("not implemented");
898 }
899
904 virtual fsize_t size() const = 0;
905
909 virtual void truncate() = 0;
910
914 virtual time_point ctime() const
915 {
916 return time_point::min();
917 }
918
922 virtual time_point atime() const
923 {
924 return time_point::min();
925 }
926
930 virtual time_point mtime() const
931 {
932 return time_point::min();
933 }
934
938 virtual void set_ctime(time_point date)
939 {
940 _Unreferenced_(date);
941 throw std::domain_error("not implemented");
942 }
943
947 virtual void set_atime(time_point date)
948 {
949 _Unreferenced_(date);
950 throw std::domain_error("not implemented");
951 }
952
956 virtual void set_mtime(time_point date)
957 {
958 _Unreferenced_(date);
959 throw std::domain_error("not implemented");
960 }
961
962#ifdef _WIN32
966 LPSAFEARRAY read_sa()
967 {
968 stdex_assert(size() <= SIZE_MAX);
969 if (size() > ULONG_MAX)
970 throw std::range_error("data too big");
971 ULONG length = static_cast<ULONG>(size());
972 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(SafeArrayCreateVector(VT_UI1, 0, length));
973 if (!sa) _Unlikely_
974 throw std::runtime_error("SafeArrayCreateVector failed");
975 if (seek(0) != 0) _Unlikely_
976 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
977 safearray_accessor<void> a(sa.get());
978 if (read_array(a.data(), 1, length) != length)
979 throw std::system_error(sys_error(), std::system_category(), "failed to read");
980 return sa.release();
981 }
982#endif
983
989 charset_id read_charset(_In_ charset_id default_charset = charset_id::system)
990 {
991 if (seek(0) != 0) _Unlikely_
992 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
993 utf32_t id_utf32;
994 read_array(&id_utf32, sizeof(utf32_t), 1);
995 if (ok() && id_utf32 == utf32_bom)
996 return charset_id::utf32;
997
998 if (seek(0) != 0) _Unlikely_
999 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1000 utf16_t id_utf16;
1001 read_array(&id_utf16, sizeof(utf16_t), 1);
1002 if (ok() && id_utf16 == utf16_bom)
1003 return charset_id::utf16;
1004
1005 if (seek(0) != 0) _Unlikely_
1006 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1007 char id_utf8[3] = { 0 };
1008 read_array(id_utf8, sizeof(id_utf8), 1);
1009 if (ok() && strncmp(id_utf8, _countof(id_utf8), utf8_bom, _countof(utf8_bom)) == 0)
1010 return charset_id::utf8;
1011
1012 if (seek(0) != 0) _Unlikely_
1013 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1014 return default_charset;
1015 }
1016 };
1017
1023 class converter : public basic
1024 {
1025 protected:
1027#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1028 explicit converter() : basic(state_t::fail) {}
1029
1030 void init(_Inout_ basic& source)
1031 {
1032 m_source = &source;
1033 init();
1034 }
1035
1036 void init()
1037 {
1038 m_state = m_source->state();
1039 }
1040
1041 void done()
1042 {
1043 m_source = nullptr;
1044 }
1046
1047 public:
1048 converter(_Inout_ basic& source) :
1049 basic(source.state()),
1050 m_source(&source)
1051 {}
1052
1053 virtual _Success_(return != 0 || length == 0) size_t read(
1054 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1055 {
1056 size_t num_read = m_source->read(data, length);
1057 m_state = m_source->state();
1058 return num_read;
1059 }
1060
1061 virtual _Success_(return != 0) size_t write(
1062 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1063 {
1064 size_t num_written = m_source->write(data, length);
1065 m_state = m_source->state();
1066 return num_written;
1067 }
1068
1069 virtual void close()
1070 {
1071 m_source->close();
1072 m_state = m_source->state();
1073 }
1074
1075 virtual void flush()
1076 {
1077 m_source->flush();
1078 m_state = m_source->state();
1079 }
1080
1081 protected:
1082 basic* m_source;
1083 };
1084
1088 class replicator : public basic
1089 {
1090 public:
1091 virtual ~replicator()
1092 {
1093 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1094 auto _w = w->get();
1095 {
1096 const std::lock_guard<std::mutex> lk(_w->mutex);
1097 _w->op = worker::op_t::quit;
1098 }
1099 _w->cv.notify_one();
1100 }
1101 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w)
1102 w->get()->join();
1103 }
1104
1108 void push_back(_In_ basic* source)
1109 {
1110 m_workers.push_back(std::unique_ptr<worker>(new worker(source)));
1111 }
1112
1116 void remove(basic* source)
1117 {
1118 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1119 auto _w = w->get();
1120 if (_w->source == source) {
1121 {
1122 const std::lock_guard<std::mutex> lk(_w->mutex);
1123 _w->op = worker::op_t::quit;
1124 }
1125 _w->cv.notify_one();
1126 _w->join();
1127 m_workers.erase(w);
1128 return;
1129 }
1130 }
1131 }
1132
1133 virtual _Success_(return != 0) size_t write(
1134 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1135 {
1136 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1137 auto _w = w->get();
1138 {
1139 const std::lock_guard<std::mutex> lk(_w->mutex);
1140 _w->op = worker::op_t::write;
1141 _w->data = data;
1142 _w->length = length;
1143 }
1144 _w->cv.notify_one();
1145 }
1146 size_t num_written = length;
1147 m_state = state_t::ok;
1148 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1149 auto _w = w->get();
1150 std::unique_lock<std::mutex> lk(_w->mutex);
1151 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1152 if (_w->num_written < num_written)
1153 num_written = _w->num_written;
1154 if (ok() && !_w->source->ok())
1155 m_state = _w->source->state();
1156 }
1157 return num_written;
1158 }
1159
1160 virtual void close()
1161 {
1162 foreach_worker(worker::op_t::close);
1163 }
1164
1165 virtual void flush()
1166 {
1167 foreach_worker(worker::op_t::flush);
1168 }
1169
1170 protected:
1171 class worker : public std::thread
1172 {
1173 public:
1174 worker(_In_ basic* _source) :
1175 source(_source),
1176 op(op_t::noop),
1177 data(nullptr),
1178 length(0),
1179 num_written(0)
1180 {
1181 *static_cast<std::thread*>(this) = std::thread([](_Inout_ worker& w) { w.process_op(); }, std::ref(*this));
1182 }
1183
1184 protected:
1185 void process_op()
1186 {
1187 for (;;) {
1188 std::unique_lock<std::mutex> lk(mutex);
1189 cv.wait(lk, [&] {return op != op_t::noop; });
1190 switch (op) {
1191 case op_t::quit:
1192 return;
1193 case op_t::write:
1194 num_written = source->write(data, length);
1195 break;
1196 case op_t::close:
1197 source->close();
1198 break;
1199 case op_t::flush:
1200 source->flush();
1201 break;
1202 case op_t::noop:;
1203 }
1204 op = op_t::noop;
1205 lk.unlock();
1206 cv.notify_one();
1207 }
1208 }
1209
1210 public:
1211 basic* source;
1212 enum class op_t {
1213 noop = 0,
1214 quit,
1215 write,
1216 close,
1217 flush,
1218 } op;
1219 const void* data;
1220 size_t length;
1222 std::mutex mutex;
1223 std::condition_variable cv;
1224 };
1225
1226 void foreach_worker(_In_ worker::op_t op)
1227 {
1228 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1229 auto _w = w->get();
1230 {
1231 const std::lock_guard<std::mutex> lk(_w->mutex);
1232 _w->op = op;
1233 }
1234 _w->cv.notify_one();
1235 }
1236 m_state = state_t::ok;
1237 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1238 auto _w = w->get();
1239 std::unique_lock<std::mutex> lk(_w->mutex);
1240 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1241 if (ok())
1242 m_state = _w->source->state();
1243 }
1244 }
1245
1246 std::list<std::unique_ptr<worker>> m_workers;
1247 };
1248
1249 constexpr size_t default_async_limit = 0x100000;
1250
1256 template <size_t N_cap = default_async_limit>
1258 {
1259 public:
1260 async_reader(_Inout_ basic& source) :
1261 converter(source),
1262 m_worker([](_Inout_ async_reader& w) { w.process(); }, std::ref(*this))
1263 {}
1264
1265 virtual ~async_reader()
1266 {
1267 m_ring.quit();
1268 m_worker.join();
1269 }
1270
1271#pragma warning(suppress: 6101) // See [1] below
1272 virtual _Success_(return != 0 || length == 0) size_t read(
1273 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1274 {
1275 stdex_assert(data || !length);
1276 for (size_t to_read = length;;) {
1277 uint8_t* ptr; size_t num_read;
1278 std::tie(ptr, num_read) = m_ring.front();
1279 if (!ptr) _Unlikely_ {
1280 m_state = to_read < length || !length ? state_t::ok : m_source->state();
1281 return length - to_read; // [1] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
1282 }
1283 if (to_read < num_read)
1284 num_read = to_read;
1285 memcpy(data, ptr, num_read);
1286 m_ring.pop(num_read);
1287 to_read -= num_read;
1288 if (!to_read) {
1289 m_state = state_t::ok;
1290 return length;
1291 }
1292 reinterpret_cast<uint8_t*&>(data) += num_read;
1293 }
1294 }
1295
1296 protected:
1297 void process()
1298 {
1299 for (;;) {
1300 uint8_t* ptr; size_t num_write;
1301 std::tie(ptr, num_write) = m_ring.back();
1302 if (!ptr) _Unlikely_
1303 break;
1304 num_write = m_source->read(ptr, num_write);
1305 m_ring.push(num_write);
1306 if (!m_source->ok()) {
1307 m_ring.quit();
1308 break;
1309 }
1310 }
1311 }
1312
1313 protected:
1314 ring<uint8_t, N_cap> m_ring;
1315 std::thread m_worker;
1316 };
1317
1323 template <size_t N_cap = default_async_limit>
1325 {
1326 public:
1327 async_writer(_Inout_ basic& source) :
1328 converter(source),
1329 m_worker([](_Inout_ async_writer& w) { w.process(); }, std::ref(*this))
1330 {}
1331
1332 virtual ~async_writer()
1333 {
1334 m_ring.quit();
1335 m_worker.join();
1336 }
1337
1338 virtual _Success_(return != 0) size_t write(
1339 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1340 {
1341 stdex_assert(data || !length);
1342 for (size_t to_write = length;;) {
1343 uint8_t* ptr; size_t num_write;
1344 std::tie(ptr, num_write) = m_ring.back();
1345 if (!ptr) _Unlikely_ {
1346 m_state = state_t::fail;
1347 return length - to_write;
1348 }
1349 if (to_write < num_write)
1350 num_write = to_write;
1351 memcpy(ptr, data, num_write);
1352 m_ring.push(num_write);
1353 to_write -= num_write;
1354 if (!to_write) {
1355 m_state = state_t::ok;
1356 return length;
1357 }
1358 reinterpret_cast<const uint8_t*&>(data) += num_write;
1359 }
1360 }
1361
1362 virtual void flush()
1363 {
1364 m_ring.sync();
1365 converter::flush();
1366 }
1367
1368 protected:
1369 void process()
1370 {
1371 for (;;) {
1372 uint8_t* ptr; size_t num_read;
1373 std::tie(ptr, num_read) = m_ring.front();
1374 if (!ptr)
1375 break;
1376 num_read = m_source->write(ptr, num_read);
1377 m_ring.pop(num_read);
1378 if (!m_source->ok()) {
1379 m_ring.quit();
1380 break;
1381 }
1382 }
1383 }
1384
1385 protected:
1386 ring<uint8_t, N_cap> m_ring;
1387 std::thread m_worker;
1388 };
1389
1390 constexpr size_t default_buffer_size = 0x400;
1391
1395 class buffer : public converter
1396 {
1397 protected:
1399 explicit buffer(_In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1400 converter(),
1401 m_read_buffer(read_buffer_size),
1402 m_write_buffer(write_buffer_size)
1403 {}
1404
1405 void done()
1406 {
1407 if (m_source)
1408 flush_write();
1409 converter::done();
1410 }
1412
1413 public:
1414 buffer(_Inout_ basic& source, _In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1415 converter(source),
1416 m_read_buffer(read_buffer_size),
1417 m_write_buffer(write_buffer_size)
1418 {}
1419
1420 virtual ~buffer()
1421 {
1422 if (m_source)
1423 flush_write();
1424 }
1425
1426 virtual _Success_(return != 0 || length == 0) size_t read(
1427 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1428 {
1429 stdex_assert(data || !length);
1430 for (size_t to_read = length;;) {
1431 size_t buffer_size = m_read_buffer.tail - m_read_buffer.head;
1432 if (to_read <= buffer_size) {
1433 memcpy(data, m_read_buffer.data + m_read_buffer.head, to_read);
1434 m_read_buffer.head += to_read;
1435 m_state = state_t::ok;
1436 return length;
1437 }
1438 if (buffer_size) {
1439 memcpy(data, m_read_buffer.data + m_read_buffer.head, buffer_size);
1440 reinterpret_cast<uint8_t*&>(data) += buffer_size;
1441 to_read -= buffer_size;
1442 }
1443 m_read_buffer.head = 0;
1444 if (to_read > m_read_buffer.capacity) {
1445 // When needing to read more data than buffer capacity, bypass the buffer.
1446 m_read_buffer.tail = 0;
1447 to_read -= m_source->read(data, to_read);
1448 m_state = to_read < length ? state_t::ok : m_source->state();
1449 return length - to_read;
1450 }
1451 m_read_buffer.tail = m_source->read(m_read_buffer.data, m_read_buffer.capacity);
1452 if (m_read_buffer.tail < m_read_buffer.capacity && m_read_buffer.tail < to_read) _Unlikely_ {
1453 memcpy(data, m_read_buffer.data, m_read_buffer.tail);
1454 m_read_buffer.head = m_read_buffer.tail;
1455 to_read -= m_read_buffer.tail;
1456 m_state = to_read < length ? state_t::ok : m_source->state();
1457 return length - to_read;
1458 }
1459 }
1460 }
1461
1462 virtual _Success_(return != 0) size_t write(
1463 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1464 {
1465 stdex_assert(data || !length);
1466 if (!length) _Unlikely_ {
1467 // Pass null writes (zero-byte length). Null write operations have special meaning with with Windows pipes.
1468 flush_write();
1469 if (!ok()) _Unlikely_
1470 return 0;
1471 converter::write(nullptr, 0);
1472 return 0;
1473 }
1474
1475 for (size_t to_write = length;;) {
1476 size_t available_buffer = m_write_buffer.capacity - m_write_buffer.tail;
1477 if (to_write <= available_buffer) {
1478 memcpy(m_write_buffer.data + m_write_buffer.tail, data, to_write);
1479 m_write_buffer.tail += to_write;
1480 m_state = state_t::ok;
1481 return length;
1482 }
1483 if (available_buffer) {
1484 memcpy(m_write_buffer.data + m_write_buffer.tail, data, available_buffer);
1485 reinterpret_cast<const uint8_t*&>(data) += available_buffer;
1486 to_write -= available_buffer;
1487 m_write_buffer.tail += available_buffer;
1488 }
1489 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1490 if (buffer_size) {
1491 m_write_buffer.head += converter::write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1492 if (m_write_buffer.head == m_write_buffer.tail)
1493 m_write_buffer.head = m_write_buffer.tail = 0;
1494 else
1495 return length - to_write;
1496 }
1497 if (to_write > m_write_buffer.capacity) {
1498 // When needing to write more data than buffer capacity, bypass the buffer.
1499 to_write -= converter::write(data, to_write);
1500 return length - to_write;
1501 }
1502 }
1503 }
1504
1505 virtual void flush()
1506 {
1507 flush_write();
1508 if (ok())
1509 converter::flush();
1510 }
1511
1512 protected:
1513 void flush_write()
1514 {
1515 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1516 if (buffer_size) {
1517 m_write_buffer.head += m_source->write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1518 if (m_write_buffer.head == m_write_buffer.tail) {
1519 m_write_buffer.head = 0;
1520 m_write_buffer.tail = 0;
1521 }
1522 else {
1523 m_state = m_source->state();
1524 return;
1525 }
1526 }
1527 m_state = state_t::ok;
1528 }
1529
1530 struct buffer_t {
1531 uint8_t* data;
1532 size_t head, tail, capacity;
1533
1534 buffer_t(_In_ size_t buffer_size) :
1535 head(0),
1536 tail(0),
1537 capacity(buffer_size),
1538 data(buffer_size ? new uint8_t[buffer_size] : nullptr)
1539 {}
1540
1541 ~buffer_t()
1542 {
1543 if (data)
1544 delete[] data;
1545 }
1546 } m_read_buffer, m_write_buffer;
1547 };
1548
1552 class limiter : public converter
1553 {
1554 public:
1555 limiter(_Inout_ basic& source, _In_ fsize_t _read_limit = 0, _In_ fsize_t _write_limit = 0) :
1556 converter(source),
1557 read_limit(_read_limit),
1558 write_limit(_write_limit)
1559 {}
1560
1561 virtual _Success_(return != 0 || length == 0) size_t read(
1562 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1563 {
1564 size_t num_read;
1565 if (read_limit == fsize_max)
1566 num_read = converter::read(data, length);
1567 else if (length <= read_limit) {
1568 num_read = converter::read(data, length);
1569 read_limit -= num_read;
1570 }
1571 else if (length && !read_limit) {
1572 num_read = 0;
1573 m_state = state_t::eof;
1574 }
1575 else {
1576 num_read = converter::read(data, static_cast<size_t>(read_limit));
1577 read_limit -= num_read;
1578 }
1579 return num_read;
1580 }
1581
1582 virtual _Success_(return != 0) size_t write(
1583 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1584 {
1585 size_t num_written;
1586 if (write_limit == fsize_max)
1587 num_written = converter::write(data, length);
1588 else if (length <= write_limit) {
1589 num_written = converter::write(data, length);
1590 write_limit -= num_written;
1591 }
1592 else if (length && !write_limit) {
1593 num_written = 0;
1594 m_state = state_t::fail;
1595 }
1596 else {
1597 num_written = converter::write(data, static_cast<size_t>(write_limit));
1598 write_limit -= num_written;
1599 }
1600 return num_written;
1601 }
1602
1603 public:
1604 fsize_t
1607 };
1608
1612 class window : public limiter
1613 {
1614 public:
1615 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) :
1616 limiter(source, read_limit, write_limit),
1617 read_offset(_read_offset),
1618 write_offset(_write_offset)
1619 {}
1620
1621 virtual _Success_(return != 0 || length == 0) size_t read(
1622 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1623 {
1624 if (read_offset) {
1625 m_source->skip(read_offset);
1626 m_state = m_source->state();
1627 if (!ok()) _Unlikely_
1628 return 0;
1629 read_offset = 0;
1630 }
1631 size_t num_read;
1632 if (read_limit == fsize_max)
1633 num_read = converter::read(data, length);
1634 else if (length <= read_limit) {
1635 num_read = converter::read(data, length);
1636 read_limit -= num_read;
1637 }
1638 else if (length && !read_limit) {
1639 num_read = 0;
1640 m_source->skip(length);
1641 m_state = state_t::eof;
1642 }
1643 else {
1644 num_read = converter::read(data, static_cast<size_t>(read_limit));
1645 read_limit -= num_read;
1646 }
1647 return num_read;
1648 }
1649
1650 virtual _Success_(return != 0) size_t write(
1651 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1652 {
1653 size_t num_skipped, num_written;
1654 if (length <= write_offset) {
1655 write_offset -= length;
1656 m_state = state_t::ok;
1657 return length;
1658 }
1659 if (write_offset) {
1660 reinterpret_cast<const uint8_t*&>(data) += static_cast<size_t>(write_offset);
1661 length -= static_cast<size_t>(write_offset);
1662 num_skipped = static_cast<size_t>(write_offset);
1663 write_offset = 0;
1664 }
1665 else
1666 num_skipped = 0;
1667 if (write_limit == fsize_max)
1668 num_written = converter::write(data, length);
1669 else if (length <= write_limit) {
1670 num_written = converter::write(data, length);
1671 write_limit -= num_written;
1672 }
1673 else if (length && !write_limit) {
1674 num_skipped += length;
1675 num_written = 0;
1676 m_state = state_t::ok;
1677 }
1678 else {
1679 num_skipped += length - static_cast<size_t>(write_limit);
1680 num_written = converter::write(data, static_cast<size_t>(write_limit));
1681 write_limit -= num_written;
1682 }
1683 return num_skipped + num_written;
1684 }
1685
1686 public:
1687 fpos_t
1690 };
1691
1696 {
1697 public:
1698 file_window(_Inout_ basic_file& source, fpos_t offset = 0, fsize_t length = 0) :
1699 basic(source.state()),
1700 m_source(source),
1701 m_offset(source.tell()),
1702 m_region(offset, offset + length)
1703 {}
1704
1705 virtual _Success_(return != 0 || length == 0) size_t read(
1706 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1707 {
1708 stdex_assert(data || !length);
1709 if (m_region.contains(m_offset)) {
1710 size_t num_read = m_source.read(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1711 m_state = m_source.state();
1712 m_offset += num_read;
1713 return num_read;
1714 }
1715 m_state = length ? state_t::eof : state_t::ok;
1716 return 0;
1717 }
1718
1719 virtual _Success_(return != 0) size_t write(
1720 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1721 {
1722 stdex_assert(data || !length);
1723 if (m_region.contains(m_offset)) {
1724 size_t num_written = m_source.write(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1725 m_state = m_source.state();
1726 m_offset += num_written;
1727 return num_written;
1728 }
1729 m_state = state_t::fail;
1730 return 0;
1731 }
1732
1733 virtual void close()
1734 {
1735 m_source.close();
1736 m_state = m_source.state();
1737 }
1738
1739 virtual void flush()
1740 {
1741 m_source.flush();
1742 m_state = m_source.state();
1743 }
1744
1745 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
1746 {
1747 m_offset = m_source.seek(offset, how);
1748 m_state = m_source.state();
1749 return ok() ? m_offset - m_region.start : fpos_max;
1750 }
1751
1752 virtual void skip(_In_ fsize_t amount)
1753 {
1754 m_source.skip(amount);
1755 m_state = m_source.state();
1756 }
1757
1758 virtual fpos_t tell() const
1759 {
1760 fpos_t offset = m_source.tell();
1761 return m_region.contains(offset) ? offset - m_region.start : fpos_max;
1762 }
1763
1764 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
1765 {
1766 if (m_region.contains(offset)) {
1767 m_source.lock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1768 m_state = m_source.state();
1769 }
1770 else
1771 m_state = state_t::fail;
1772 }
1773
1774 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
1775 {
1776 if (m_region.contains(offset)) {
1777 m_source.unlock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1778 m_state = m_source.state();
1779 }
1780 else
1781 m_state = state_t::fail;
1782 }
1783
1784 virtual fsize_t size() const
1785 {
1786 return m_region.size();
1787 }
1788
1789 virtual void truncate()
1790 {
1791 m_state = state_t::fail;
1792 }
1793
1794 protected:
1795 basic_file& m_source;
1796 fpos_t m_offset;
1797 interval<fpos_t> m_region;
1798 };
1799
1800 constexpr size_t default_cache_size = 0x1000;
1801
1805 class cache : public basic_file
1806 {
1807 protected:
1809#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1810 explicit cache(_In_ size_t cache_size = default_cache_size) :
1811 basic(state_t::fail),
1812 m_cache(cache_size)
1813 {}
1814
1815 void init(_Inout_ basic_file& source)
1816 {
1817 m_source = &source;
1818 init();
1819 }
1820
1821 void init()
1822 {
1823 m_state = m_source->state();
1824 m_offset = m_source->tell();
1825#if SET_FILE_OP_TIMES
1826 m_atime = m_source->atime();
1827 m_mtime = m_source->mtime();
1828#endif
1829 }
1830
1831 void done()
1832 {
1833 if (m_source) {
1834 flush_cache();
1835 if (!ok()) _Unlikely_
1836 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occurred
1837 m_source->seekbeg(m_offset);
1838#if SET_FILE_OP_TIMES
1839 m_source->set_atime(m_atime);
1840 m_source->set_mtime(m_mtime);
1841#endif
1842 m_source = nullptr;
1843 }
1844 }
1846
1847 public:
1848 cache(_Inout_ basic_file& source, _In_ size_t cache_size = default_cache_size) :
1849 basic(source.state()),
1850 m_source(&source),
1851 m_cache(cache_size),
1852 m_offset(source.tell())
1853#if SET_FILE_OP_TIMES
1854 , m_atime(source.atime())
1855 , m_mtime(source.mtime())
1856#endif
1857 {}
1858
1859 virtual ~cache() noexcept(false)
1860 {
1861 if (m_source) {
1862 flush_cache();
1863 if (!ok()) _Unlikely_
1864 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occurred
1865 m_source->seekbeg(m_offset);
1866#if SET_FILE_OP_TIMES
1867 m_source->set_atime(m_atime);
1868 m_source->set_mtime(m_mtime);
1869#endif
1870 }
1871 }
1872
1873 virtual _Success_(return != 0 || length == 0) size_t read(
1874 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1875 {
1876 stdex_assert(data || !length);
1877#if SET_FILE_OP_TIMES
1878 m_atime = time_point::now();
1879#endif
1880 for (size_t to_read = length;;) {
1881 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1882 if (m_cache.region.contains(m_offset)) {
1883 size_t remaining_cache = static_cast<size_t>(m_cache.region.end - m_offset);
1884 if (to_read <= remaining_cache) {
1885 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), to_read);
1886 m_offset += to_read;
1887 m_state = state_t::ok;
1888 return length;
1889 }
1890 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), remaining_cache);
1891 reinterpret_cast<uint8_t*&>(data) += remaining_cache;
1892 to_read -= remaining_cache;
1893 m_offset += remaining_cache;
1894 }
1895 flush_cache();
1896 if (!ok()) _Unlikely_ {
1897 if (to_read < length)
1898 m_state = state_t::ok;
1899 return length - to_read;
1900 }
1901 }
1902 {
1903 fpos_t end_max = m_offset + to_read;
1904 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1905 // Read spans multiple cache blocks. Bypass cache to the last block.
1906 m_source->seekbeg(m_offset);
1907 if (!m_source->ok()) _Unlikely_ {
1908 m_state = to_read < length ? state_t::ok : state_t::fail;
1909 return length - to_read;
1910 }
1911 size_t num_read = m_source->read(data, to_read - static_cast<size_t>(end_max % m_cache.capacity));
1912 m_offset += num_read;
1913 to_read -= num_read;
1914 if (!to_read) {
1915 m_state = state_t::ok;
1916 return length;
1917 }
1918 reinterpret_cast<uint8_t*&>(data) += num_read;
1919 m_state = m_source->state();
1920 if (!ok()) {
1921 if (to_read < length)
1922 m_state = state_t::ok;
1923 return length - to_read;
1924 }
1925 }
1926 }
1927 load_cache(m_offset);
1928 if (!ok()) _Unlikely_ {
1929 m_state = to_read < length ? state_t::ok : state_t::fail;
1930 return length - to_read;
1931 }
1932 if (m_cache.region.end <= m_offset) _Unlikely_ {
1933 m_state = to_read < length ? state_t::ok : state_t::eof;
1934 return length - to_read;
1935 }
1936 }
1937 }
1938
1939 virtual _Success_(return != 0) size_t write(
1940 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1941 {
1942 stdex_assert(data || !length);
1943#if SET_FILE_OP_TIMES
1944 m_atime = m_mtime = time_point::now();
1945#endif
1946 for (size_t to_write = length;;) {
1947 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1948 fpos_t end_max = m_cache.region.start + m_cache.capacity;
1949 if (m_cache.region.start <= m_offset && m_offset < end_max) {
1950 size_t remaining_cache = static_cast<size_t>(end_max - m_offset);
1951 if (to_write <= remaining_cache) {
1952 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, to_write);
1953 m_offset += to_write;
1954 m_cache.status = cache_t::cache_t::status_t::dirty;
1955 m_cache.region.end = std::max(m_cache.region.end, m_offset);
1956 m_state = state_t::ok;
1957 return length;
1958 }
1959 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, remaining_cache);
1960 reinterpret_cast<const uint8_t*&>(data) += remaining_cache;
1961 to_write -= remaining_cache;
1962 m_offset += remaining_cache;
1963 m_cache.status = cache_t::cache_t::status_t::dirty;
1964 m_cache.region.end = end_max;
1965 }
1966 flush_cache();
1967 if (!ok()) _Unlikely_
1968 return length - to_write;
1969 }
1970 {
1971 fpos_t end_max = m_offset + to_write;
1972 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1973 // Write spans multiple cache blocks. Bypass cache to the last block.
1974 m_source->seekbeg(m_offset);
1975 if (!ok()) _Unlikely_
1976 return length - to_write;
1977 size_t num_written = m_source->write(data, to_write - static_cast<size_t>(end_max % m_cache.capacity));
1978 m_offset += num_written;
1979 m_state = m_source->state();
1980 to_write -= num_written;
1981 if (!to_write || !ok())
1982 return length - to_write;
1983 reinterpret_cast<const uint8_t*&>(data) += num_written;
1984 }
1985 }
1986 load_cache(m_offset);
1987 if (!ok()) _Unlikely_
1988 return length - to_write;
1989 }
1990 }
1991
1992 virtual void close()
1993 {
1994 invalidate_cache();
1995 if (!ok()) _Unlikely_
1996 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occurred
1997 m_source->close();
1998 m_state = m_source->state();
1999 }
2000
2001 virtual void flush()
2002 {
2003#if SET_FILE_OP_TIMES
2004 m_atime = m_mtime = time_point::min();
2005#endif
2006 flush_cache();
2007 if (!ok()) _Unlikely_
2008 return;
2009 m_source->flush();
2010 }
2011
2012 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2013 {
2014 m_state = state_t::ok;
2015 switch (how) {
2016 case seek_t::beg:
2017 break;
2018 case seek_t::cur:
2019 offset = static_cast<foff_t>(m_offset) + offset;
2020 break;
2021 case seek_t::end: {
2022 auto n = size();
2023 if (n == fsize_max) _Unlikely_{
2024 m_state = state_t::fail;
2025 return fpos_max;
2026 }
2027 offset = static_cast<foff_t>(n) + offset;
2028 break;
2029 }
2030 default:
2031 throw std::invalid_argument("unknown seek origin");
2032 }
2033 if (offset < 0) _Unlikely_
2034 throw std::invalid_argument("negative file offset");
2035 return m_offset = static_cast<fpos_t>(offset);
2036 }
2037
2038 virtual fpos_t tell() const
2039 {
2040 return m_offset;
2041 }
2042
2043 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2044 {
2045 m_source->lock(offset, length);
2046 m_state = m_source->state();
2047 }
2048
2049 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2050 {
2051 m_source->unlock(offset, length);
2052 m_state = m_source->state();
2053 }
2054
2055 virtual fsize_t size() const
2056 {
2057 return m_cache.status != cache_t::cache_t::status_t::empty ?
2058 std::max(m_source->size(), m_cache.region.end) :
2059 m_source->size();
2060 }
2061
2062 virtual void truncate()
2063 {
2064#if SET_FILE_OP_TIMES
2065 m_atime = m_mtime = time_point::now();
2066#endif
2067 m_source->seekbeg(m_offset);
2068 if (m_cache.region.end <= m_offset) {
2069 // Truncation does not affect cache.
2070 }
2071 else if (m_cache.region.start <= m_offset) {
2072 // Truncation truncates cache.
2073 m_cache.region.end = m_offset;
2074 }
2075 else {
2076 // Truncation invalidates cache.
2077 m_cache.status = cache_t::cache_t::status_t::empty;
2078 }
2079 m_source->truncate();
2080 m_state = m_source->state();
2081 }
2082
2083 virtual time_point ctime() const
2084 {
2085 return m_source->ctime();
2086 }
2087
2088 virtual time_point atime() const
2089 {
2090#if SET_FILE_OP_TIMES
2091 return std::max(m_atime, m_source->atime());
2092#else
2093 return m_source->atime();
2094#endif
2095 }
2096
2097 virtual time_point mtime() const
2098 {
2099#if SET_FILE_OP_TIMES
2100 return std::max(m_mtime, m_source->mtime());
2101#else
2102 return m_source->mtime();
2103#endif
2104 }
2105
2106 virtual void set_ctime(time_point date)
2107 {
2108 m_source->set_ctime(date);
2109 }
2110
2111 virtual void set_atime(time_point date)
2112 {
2113#if SET_FILE_OP_TIMES
2114 m_atime = date;
2115#endif
2116 m_source->set_atime(date);
2117 }
2118
2119 virtual void set_mtime(time_point date)
2120 {
2121#if SET_FILE_OP_TIMES
2122 m_mtime = date;
2123#endif
2124 m_source->set_mtime(date);
2125 }
2126
2127 protected:
2129 void flush_cache()
2130 {
2131 if (m_cache.status != cache_t::cache_t::status_t::dirty)
2132 m_state = state_t::ok;
2133 else if (!m_cache.region.empty()) {
2134 write_cache();
2135 if (ok())
2136 m_cache.status = cache_t::cache_t::status_t::loaded;
2137 }
2138 else {
2139 m_state = state_t::ok;
2140 m_cache.status = cache_t::cache_t::status_t::loaded;
2141 }
2142 }
2143
2144 void invalidate_cache()
2145 {
2146 if (m_cache.status == cache_t::cache_t::status_t::dirty && !m_cache.region.empty()) {
2147 write_cache();
2148 if (!ok()) _Unlikely_
2149 return;
2150 } else
2151 m_state = state_t::ok;
2152 m_cache.status = cache_t::cache_t::status_t::empty;
2153 }
2154
2155 void load_cache(_In_ fpos_t start)
2156 {
2157 stdex_assert(m_cache.status != cache_t::cache_t::status_t::dirty);
2158 start -= start % m_cache.capacity; // Align to cache block size.
2159 m_source->seekbeg(m_cache.region.start = start);
2160 if (m_source->ok()) {
2161 m_cache.region.end = start + m_source->read(m_cache.data, m_cache.capacity);
2162 m_cache.status = cache_t::cache_t::status_t::loaded;
2163 m_state = state_t::ok; // Regardless the read failure, we still might have cached some data.
2164 }
2165 else
2166 m_state = state_t::fail;
2167 }
2168
2169 void write_cache()
2170 {
2171 stdex_assert(m_cache.status == cache_t::cache_t::status_t::dirty);
2172 m_source->seekbeg(m_cache.region.start);
2173 m_source->write(m_cache.data, static_cast<size_t>(m_cache.region.size()));
2174 m_state = m_source->state();
2175 }
2176
2177 basic_file* m_source;
2178 struct cache_t {
2179 uint8_t* data;
2180 size_t capacity;
2181 enum class status_t {
2182 empty = 0,
2183 loaded,
2184 dirty,
2185 } status;
2186 interval<fpos_t> region;
2187
2188 cache_t(_In_ size_t _capacity) :
2189 data(new uint8_t[_capacity]),
2190 capacity(_capacity),
2191 status(status_t::empty),
2192 region(0)
2193 {}
2194
2195 ~cache_t()
2196 {
2197 delete[] data;
2198 }
2199 } m_cache;
2200 fpos_t m_offset;
2201#if SET_FILE_OP_TIMES
2202 time_point
2203 m_atime,
2204 m_mtime;
2205#endif
2207 };
2208
2212 class basic_sys : virtual public basic, public sys_object
2213 {
2214 public:
2215 basic_sys(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) :
2216 basic(state),
2217 sys_object(h)
2218 {}
2219
2220 virtual _Success_(return != 0 || length == 0) size_t read(
2221 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2222 {
2223 stdex_assert(data || !length);
2224 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2225 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2226 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2227 size_t
2228#if defined(_WIN64)
2229 block_size = 0x1F80000;
2230#elif defined(_WIN32)
2231 block_size = 0x3f00000;
2232#else
2233 block_size = SSIZE_MAX;
2234#endif
2235 for (size_t to_read = length;;) {
2236#ifdef _WIN32
2237 // ReadFile() might raise exception (e.g. STATUS_FILE_BAD_FORMAT/0xE0000002).
2238 BOOL succeeded;
2239 DWORD num_read;
2240 __try { succeeded = ReadFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_read, block_size)), &num_read, nullptr); }
2241 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_read = 0; }
2242 if (!succeeded && GetLastError() == ERROR_NO_SYSTEM_RESOURCES && block_size > default_block_size) _Unlikely_ {
2243 // Error "Insufficient system resources exist to complete the requested service." occurs
2244 // occasionally, when attempting to read too much data at once (e.g. over \\TSClient).
2245 block_size = default_block_size;
2246 continue;
2247 }
2248 if (!succeeded) _Unlikely_
2249#else
2250 auto num_read = ::read(m_h, data, std::min<size_t>(to_read, block_size));
2251 if (num_read < 0) _Unlikely_
2252#endif
2253 {
2254 m_state = to_read < length ? state_t::ok : state_t::fail;
2255 return length - to_read;
2256 }
2257 if (!num_read) _Unlikely_ {
2258 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2259 return length - to_read;
2260 }
2261 to_read -= static_cast<size_t>(num_read);
2262 if (!to_read) {
2263 m_state = state_t::ok;
2264 return length;
2265 }
2266 reinterpret_cast<uint8_t*&>(data) += num_read;
2267 }
2268 }
2269
2270 virtual _Success_(return != 0) size_t write(
2271 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2272 {
2273 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2274 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2275 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2276 constexpr size_t
2277#if defined(_WIN64)
2278 block_size = 0x1F80000;
2279#elif defined(_WIN32)
2280 block_size = 0x3f00000;
2281#else
2282 block_size = SSIZE_MAX;
2283#endif
2284 for (size_t to_write = length;;) {
2285#ifdef _WIN32
2286 // ReadFile() might raise an exception. Be cautious with WriteFile() too.
2287 BOOL succeeded;
2288 DWORD num_written;
2289 __try { succeeded = WriteFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_write, block_size)), &num_written, nullptr); }
2290 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_written = 0; }
2291 to_write -= num_written;
2292 if (!to_write) {
2293 m_state = state_t::ok;
2294 return length;
2295 }
2296 reinterpret_cast<const uint8_t*&>(data) += num_written;
2297 if (!succeeded) _Unlikely_ {
2298 m_state = state_t::fail;
2299 return length - to_write;
2300 }
2301#else
2302 auto num_written = ::write(m_h, data, std::min<size_t>(to_write, block_size));
2303 if (num_written < 0) _Unlikely_ {
2304 m_state = state_t::fail;
2305 return length - to_write;
2306 }
2307 to_write -= static_cast<size_t>(num_written);
2308 if (!to_write) {
2309 m_state = state_t::ok;
2310 return length;
2311 }
2312 reinterpret_cast<const uint8_t*&>(data) += num_written;
2313#endif
2314 }
2315 }
2316
2317 virtual void close()
2318 {
2319 try {
2320 sys_object::close();
2321 m_state = state_t::ok;
2322 }
2323 catch (...) {
2324 m_state = state_t::fail;
2325 }
2326 }
2327
2328 virtual void flush()
2329 {
2330#ifdef _WIN32
2331 m_state = FlushFileBuffers(m_h) ? state_t::ok : state_t::fail;
2332#else
2333 m_state = fsync(m_h) >= 0 ? state_t::ok : state_t::fail;
2334#endif
2335 }
2336 };
2337
2341 class buffered_sys : public buffer
2342 {
2343 public:
2344 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) :
2345 buffer(read_buffer_size, write_buffer_size),
2346 m_source(h)
2347 {
2348 init(m_source);
2349 }
2350
2351 virtual ~buffered_sys()
2352 {
2353 done();
2354 }
2355
2356 protected:
2357 basic_sys m_source;
2358 };
2359
2363 class socket : public basic
2364 {
2365 public:
2366 socket(_In_opt_ socket_t h = stdex::invalid_socket, _In_ state_t state = state_t::ok) :
2367 basic(state),
2368 m_h(h)
2369 {}
2370
2371 private:
2372 socket(_In_ const socket& other);
2373 socket& operator =(_In_ const socket& other);
2374
2375 public:
2376 socket(_Inout_ socket&& other) noexcept : m_h(other.m_h)
2377 {
2378 other.m_h = stdex::invalid_socket;
2379 }
2380
2381 socket& operator =(_Inout_ socket&& other) noexcept
2382 {
2383 if (this != std::addressof(other)) {
2384 if (m_h != stdex::invalid_socket)
2385 closesocket(m_h);
2386 m_h = other.m_h;
2387 other.m_h = stdex::invalid_socket;
2388 }
2389 return *this;
2390 }
2391
2399 socket(_In_ int af, _In_ int type, _In_ int protocol)
2400 {
2401 m_h = ::socket(af, type, protocol);
2402 if (m_h == stdex::invalid_socket) _Unlikely_
2403 m_state = state_t::fail;
2404 }
2405
2406 virtual ~socket()
2407 {
2408 if (m_h != stdex::invalid_socket)
2409 closesocket(m_h);
2410 }
2411
2415 operator bool() const noexcept { return m_h != stdex::invalid_socket; }
2416
2420 socket_t get() const noexcept { return m_h; }
2421
2422 virtual _Success_(return != 0 || length == 0) size_t read(
2423 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2424 {
2425 stdex_assert(data || !length);
2426 constexpr int block_size = 0x10000000;
2427 for (size_t to_read = length;;) {
2428 auto num_read = recv(m_h, reinterpret_cast<char*>(data),
2429#ifdef _WIN32
2430 static_cast<int>(std::min<size_t>(to_read, block_size)),
2431#else
2432 std::min<size_t>(to_read, block_size),
2433#endif
2434 0);
2435 if (num_read < 0) _Unlikely_ {
2436 m_state = to_read < length ? state_t::ok : state_t::fail;
2437 return length - to_read;
2438 }
2439 if (!num_read) {
2440 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2441 return length - to_read;
2442 }
2443 to_read -= static_cast<size_t>(num_read);
2444 if (!to_read) {
2445 m_state = state_t::ok;
2446 return length;
2447 }
2448 reinterpret_cast<uint8_t*&>(data) += num_read;
2449 }
2450 }
2451
2452 virtual _Success_(return != 0) size_t write(
2453 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2454 {
2455 stdex_assert(data || !length);
2456 constexpr int block_size = 0x10000000;
2457 for (size_t to_write = length;;) {
2458 auto num_written = send(m_h, reinterpret_cast<const char*>(data),
2459#ifdef _WIN32
2460 static_cast<int>(std::min<size_t>(to_write, block_size)),
2461#else
2462 std::min<size_t>(to_write, block_size),
2463#endif
2464 0);
2465 if (num_written < 0) _Unlikely_ {
2466 m_state = state_t::fail;
2467 return length - to_write;
2468 }
2469 to_write -= static_cast<size_t>(num_written);
2470 if (!to_write) {
2471 m_state = state_t::ok;
2472 return length;
2473 }
2474 reinterpret_cast<const uint8_t*&>(data) += num_written;
2475 }
2476 }
2477
2478 virtual void close()
2479 {
2480 if (m_h != stdex::invalid_socket) {
2481 closesocket(m_h);
2482 m_h = stdex::invalid_socket;
2483 }
2484 m_state = state_t::ok;
2485 }
2486
2487 protected:
2488 socket_t m_h;
2489 };
2490
2491#ifdef _WIN32
2495 class sequential_stream : public basic
2496 {
2497 public:
2498 sequential_stream(_In_ ISequentialStream* source) : m_source(source)
2499 {
2500 m_source->AddRef();
2501 }
2502
2503 virtual ~sequential_stream()
2504 {
2505 m_source->Release();
2506 }
2507
2508 virtual _Success_(return != 0 || length == 0) size_t read(
2509 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2510 {
2511 stdex_assert(data || !length);
2512 for (size_t to_read = length;;) {
2513 HRESULT hr;
2514 ULONG num_read = 0;
2515 __try { hr = m_source->Read(data, (ULONG)std::min<size_t>(to_read, ULONG_MAX), &num_read); }
2516 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2517 if (FAILED(hr)) _Unlikely_ {
2518 m_state = to_read < length ? state_t::ok : state_t::fail;
2519 return length - to_read;
2520 }
2521 to_read -= num_read;
2522 if (hr == S_FALSE) _Unlikely_ {
2523 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2524 return length - to_read;
2525 }
2526 if (!to_read) {
2527 m_state = state_t::ok;
2528 return length;
2529 }
2530 reinterpret_cast<uint8_t*&>(data) += num_read;
2531 }
2532 }
2533
2534 virtual _Success_(return != 0) size_t write(
2535 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2536 {
2537 stdex_assert(data || !length);
2538 for (size_t to_write = length;;) {
2539 HRESULT hr;
2540 ULONG num_written = 0;
2541 __try { hr = m_source->Write(data, static_cast<ULONG>(std::min<size_t>(to_write, ULONG_MAX)), &num_written); }
2542 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2543 // In absence of documentation whether num_written gets set when FAILED(hr) (i.e. partially successful writes),
2544 // assume write failed completely.
2545 if (FAILED(hr)) _Unlikely_ {
2546 m_state = state_t::fail;
2547 return length - to_write;
2548 }
2549 to_write -= num_written;
2550 if (!to_write) {
2551 m_state = state_t::ok;
2552 return length;
2553 }
2554 reinterpret_cast<const uint8_t*&>(data) += num_written;
2555 }
2556 }
2557
2558 protected:
2559 ISequentialStream* m_source;
2560 };
2561
2565 class asp : public basic
2566 {
2567 public:
2568 asp(_In_opt_ IRequest* request, _In_opt_ IResponse* response) :
2569 m_request(request),
2570 m_response(response)
2571 {
2572 if (m_request)
2573 m_request->AddRef();
2574 if (m_response)
2575 m_response->AddRef();
2576 }
2577
2578 virtual ~asp()
2579 {
2580 if (m_request)
2581 m_request->Release();
2582 if (m_response)
2583 m_response->Release();
2584 }
2585
2586 virtual _Success_(return != 0 || length == 0) size_t read(
2587 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2588 {
2589 stdex_assert(data || !length);
2590 if (!m_request) _Unlikely_ {
2591 m_state = state_t::fail;
2592 return 0;
2593 }
2594 for (size_t to_read = length;;) {
2595 VARIANT var_amount, var_data;
2596 V_VT(&var_amount) = VT_I4;
2597 V_I4(&var_amount) = (LONG)std::min<size_t>(to_read, LONG_MAX);
2598 V_VT(&var_data) = VT_EMPTY;
2599 HRESULT hr = [&]() {
2600 __try { return m_request->BinaryRead(&var_amount, &var_data); }
2601 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2602 }();
2603 if (FAILED(hr)) _Unlikely_ {
2604 m_state = to_read < length ? state_t::ok : state_t::fail;
2605 return length - to_read;
2606 }
2607 stdex_assert(V_VT(&var_amount) == VT_I4);
2608 stdex_assert(V_VT(&var_data) == (VT_ARRAY | VT_UI1));
2609 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(V_ARRAY(&var_data));
2610 if (!V_I4(&var_amount)) _Unlikely_ {
2611 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2612 return length - to_read;
2613 }
2614 safearray_accessor<uint8_t> a(sa.get());
2615 memcpy(data, a.data(), V_I4(&var_amount));
2616 to_read -= V_I4(&var_amount);
2617 if (!to_read) {
2618 m_state = state_t::ok;
2619 return length;
2620 }
2621 reinterpret_cast<uint8_t*&>(data) += V_I4(&var_amount);
2622 }
2623 }
2624
2625 virtual _Success_(return != 0) size_t write(
2626 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2627 {
2628 if (!m_response) {
2629 m_state = state_t::fail;
2630 return 0;
2631 }
2632 for (size_t to_write = length;;) {
2633 UINT num_written = static_cast<UINT>(std::min<size_t>(to_write, UINT_MAX));
2634 std::unique_ptr<OLECHAR, SysFreeString_delete> bstr_data(SysAllocStringByteLen(reinterpret_cast<LPCSTR>(data), num_written));
2635 VARIANT var_data;
2636 V_VT(&var_data) = VT_BSTR;
2637 V_BSTR(&var_data) = bstr_data.get();
2638 HRESULT hr = [&]() {
2639 __try { return m_response->BinaryWrite(var_data); }
2640 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2641 }();
2642 if (FAILED(hr)) _Unlikely_ {
2643 m_state = state_t::fail;
2644 return length - to_write;
2645 }
2646 to_write -= num_written;
2647 if (!to_write) {
2648 m_state = state_t::ok;
2649 return length;
2650 }
2651 reinterpret_cast<const uint8_t*&>(data) += num_written;
2652 }
2653 }
2654
2655 virtual void close()
2656 {
2657 if (m_response) {
2658 __try { m_response->End(); }
2659 __except (EXCEPTION_EXECUTE_HANDLER) {}
2660 }
2661 m_state = state_t::ok;
2662 }
2663
2664 virtual void flush()
2665 {
2666 if (m_response) {
2667 HRESULT hr;
2668 __try { hr = m_response->Flush(); }
2669 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2670 m_state = SUCCEEDED(hr) ? state_t::ok : state_t::fail;
2671 }
2672 }
2673
2674 protected:
2675 IRequest* m_request;
2676 IResponse* m_response;
2677 };
2678#endif
2679
2683 enum mode_t
2684 {
2685 mode_for_reading = 1 << 0,
2686 mode_for_writing = 1 << 1,
2687 mode_for_chmod = 1 << 2,
2688
2689 mode_open_existing = 0 << 3,
2690 mode_truncate_existing = 1 << 3,
2691 mode_preserve_existing = 2 << 3,
2692 mode_create_new = 3 << 3,
2693 mode_create = 4 << 3,
2694 mode_disposition_mask = 7 << 3,
2695
2696 mode_append = 1 << 6,
2697 mode_text = 0,
2698 mode_binary = 1 << 7,
2699
2700 share_none = 0,
2701 share_reading = 1 << 8,
2702 share_writing = 1 << 9,
2703 share_deleting = 1 << 10,
2704 share_all = share_reading | share_writing | share_deleting, // Allow others all operations on our file
2705
2706 inherit_handle = 1 << 11,
2707
2708 hint_write_thru = 1 << 12,
2709 hint_no_buffering = 1 << 13,
2710 hint_random_access = 1 << 14,
2711 hint_sequential_access = 1 << 15,
2712 };
2713
2714#pragma warning(push)
2715#pragma warning(disable: 4250)
2719 class file : virtual public basic_file, virtual public basic_sys
2720 {
2721 public:
2722 file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) : basic_sys(h, state) {}
2723
2730 file(_In_z_ const schar_t* filename, _In_ int mode)
2731 {
2732 open(filename, mode);
2733 }
2734
2741 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
2742 file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode) : file(filename.c_str(), mode) {}
2743
2750 void open(_In_z_ const schar_t* filename, _In_ int mode)
2751 {
2752 if (m_h != invalid_handle)
2753 close();
2754
2755#ifdef _WIN32
2756 DWORD dwDesiredAccess = 0;
2757 if (mode & mode_for_reading) dwDesiredAccess |= GENERIC_READ;
2758 if (mode & mode_for_writing) dwDesiredAccess |= GENERIC_WRITE;
2759 if (mode & mode_for_chmod) dwDesiredAccess |= FILE_WRITE_ATTRIBUTES;
2760
2761 DWORD dwShareMode = 0;
2762 if (mode & share_reading) dwShareMode |= FILE_SHARE_READ;
2763 if (mode & share_writing) dwShareMode |= FILE_SHARE_WRITE;
2764 if (mode & share_deleting) dwShareMode |= FILE_SHARE_DELETE;
2765
2766 SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };
2767 sa.bInheritHandle = mode & inherit_handle ? true : false;
2768
2769 DWORD dwCreationDisposition;
2770 switch (mode & mode_disposition_mask) {
2771 case mode_open_existing: dwCreationDisposition = OPEN_EXISTING; break;
2772 case mode_truncate_existing: dwCreationDisposition = TRUNCATE_EXISTING; break;
2773 case mode_preserve_existing: dwCreationDisposition = OPEN_ALWAYS; break;
2774 case mode_create_new: dwCreationDisposition = CREATE_NEW; break;
2775 case mode_create: dwCreationDisposition = CREATE_ALWAYS; break;
2776 default: throw std::invalid_argument("invalid mode");
2777 }
2778
2779 DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
2780 if (mode & hint_write_thru) dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
2781 if (mode & hint_no_buffering) dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
2782 if (mode & hint_random_access) dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
2783 if (mode & hint_sequential_access) dwFlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
2784
2785 m_h = CreateFile(filename, dwDesiredAccess, dwShareMode, &sa, dwCreationDisposition, dwFlagsAndAttributes, NULL);
2786#else
2787 int flags = 0;
2788 switch (mode & (mode_for_reading | mode_for_writing)) {
2789 case mode_for_reading: flags |= O_RDONLY; break;
2790 case mode_for_writing: flags |= O_WRONLY; break;
2791 case mode_for_reading | mode_for_writing: flags |= O_RDWR; break;
2792 }
2793 switch (mode & mode_disposition_mask) {
2794 case mode_open_existing: break;
2795 case mode_truncate_existing: flags |= O_TRUNC; break;
2796 case mode_preserve_existing: flags |= O_CREAT; break;
2797 case mode_create_new: flags |= O_CREAT | O_EXCL; break;
2798 case mode_create: flags |= O_CREAT | O_TRUNC; break;
2799 default: throw std::invalid_argument("invalid mode");
2800 }
2801 if (mode & hint_write_thru) flags |= O_DSYNC;
2802#ifndef __APPLE__
2803 if (mode & hint_no_buffering) flags |= O_RSYNC;
2804#endif
2805
2806 m_h = ::open(filename, flags, DEFFILEMODE);
2807#endif
2808 if (m_h != invalid_handle) {
2809 m_state = state_t::ok;
2810 if (mode & mode_append)
2811 seek(0, seek_t::end);
2812 }
2813 else
2814 m_state = state_t::fail;
2815 }
2816
2823 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
2824 void open(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
2825 {
2826 open(filename.c_str(), mode);
2827 }
2828
2829 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2830 {
2831#ifdef _WIN32
2832 LARGE_INTEGER li;
2833 li.QuadPart = offset;
2834 li.LowPart = SetFilePointer(m_h, li.LowPart, &li.HighPart, static_cast<DWORD>(how));
2835 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR) {
2836 m_state = state_t::ok;
2837 return li.QuadPart;
2838 }
2839#else
2840 off64_t result = lseek64(m_h, offset, static_cast<int>(how));
2841 if (result >= 0) {
2842 m_state = state_t::ok;
2843 return static_cast<fpos_t>(result);
2844 }
2845#endif
2846 m_state = state_t::fail;
2847 return fpos_max;
2848 }
2849
2850 virtual fpos_t tell() const
2851 {
2852 if (m_h != invalid_handle) {
2853#ifdef _WIN32
2854 LARGE_INTEGER li;
2855 li.QuadPart = 0;
2856 li.LowPart = SetFilePointer(m_h, 0, &li.HighPart, FILE_CURRENT);
2857 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR)
2858 return li.QuadPart;
2859#else
2860 off64_t result = lseek64(m_h, 0, SEEK_CUR);
2861 if (result >= 0)
2862 return static_cast<fpos_t>(result);
2863#endif
2864 }
2865 return fpos_max;
2866 }
2867
2868 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2869 {
2870#ifdef _WIN32
2871 LARGE_INTEGER liOffset;
2872 LARGE_INTEGER liSize;
2873 liOffset.QuadPart = offset;
2874 liSize.QuadPart = length;
2875 if (LockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2876 m_state = state_t::ok;
2877 return;
2878 }
2879#else
2880 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2881 if (orig >= 0) {
2882 if (offset > std::numeric_limits<off64_t>::max())
2883 throw std::invalid_argument("file offset too big");
2884 if (length > std::numeric_limits<off64_t>::max())
2885 throw std::invalid_argument("file section length too big");
2886 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;
2887 lseek64(m_h, orig, SEEK_SET);
2888 m_state = state_t::ok;
2889 return;
2890 }
2891#endif
2892 m_state = state_t::fail;
2893 }
2894
2895 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2896 {
2897#ifdef _WIN32
2898 LARGE_INTEGER liOffset;
2899 LARGE_INTEGER liSize;
2900 liOffset.QuadPart = offset;
2901 liSize.QuadPart = length;
2902 if (UnlockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2903 m_state = state_t::ok;
2904 return;
2905 }
2906#else
2907 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2908 if (orig >= 0) {
2909 if (offset > std::numeric_limits<off64_t>::max())
2910 throw std::invalid_argument("file offset too big");
2911 if (length > std::numeric_limits<off64_t>::max())
2912 throw std::invalid_argument("file section length too big");
2913 if (lseek64(m_h, static_cast<off64_t>(offset), SEEK_SET) >= 0 && lockf64(m_h, F_ULOCK, static_cast<off64_t>(length)) >= 0) {
2914 lseek64(m_h, orig, SEEK_SET);
2915 m_state = state_t::ok;
2916 return;
2917 }
2918 lseek64(m_h, orig, SEEK_SET);
2919 }
2920#endif
2921 m_state = state_t::fail;
2922 }
2923
2924 virtual fsize_t size() const
2925 {
2926#ifdef _WIN32
2927 LARGE_INTEGER li;
2928 li.LowPart = GetFileSize(m_h, (LPDWORD)&li.HighPart);
2929 if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR)
2930 li.QuadPart = -1;
2931 return li.QuadPart;
2932#else
2933 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2934 if (orig >= 0) {
2935 off64_t length = lseek64(m_h, 0, SEEK_END);
2936 lseek64(m_h, orig, SEEK_SET);
2937 if (length >= 0)
2938 return static_cast<fsize_t>(length);
2939 }
2940 return fsize_max;
2941#endif
2942 }
2943
2944 virtual void truncate()
2945 {
2946#ifdef _WIN32
2947 if (SetEndOfFile(m_h)) {
2948 m_state = state_t::ok;
2949 return;
2950 }
2951#else
2952 off64_t length = lseek64(m_h, 0, SEEK_CUR);
2953 if (length >= 0 && ftruncate64(m_h, length) >= 0) {
2954 m_state = state_t::ok;
2955 return;
2956 }
2957#endif
2958 m_state = state_t::fail;
2959 }
2960
2961#ifdef _WIN32
2962 static time_point ft2tp(_In_ const FILETIME& ft)
2963 {
2964#if _HAS_CXX20
2965 uint64_t t = (static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
2966#else
2967 uint64_t t = ((static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) - 116444736000000000ll;
2968#endif
2969 return time_point(time_point::duration(t));
2970 }
2971
2972 static void tp2ft(_In_ time_point tp, _Out_ FILETIME& ft)
2973 {
2974#if _HAS_CXX20
2975 uint64_t t = tp.time_since_epoch().count();
2976#else
2977 uint64_t t = tp.time_since_epoch().count() + 116444736000000000ll;
2978#endif
2979 ft.dwHighDateTime = static_cast<DWORD>((t >> 32) & 0xffffffff);
2980 ft.dwLowDateTime = static_cast<DWORD>(t & 0xffffffff);
2981 }
2982#endif
2983
2984 virtual time_point ctime() const
2985 {
2986#ifdef _WIN32
2987 FILETIME ft;
2988 if (GetFileTime(m_h, &ft, nullptr, nullptr))
2989 return ft2tp(ft);
2990#endif
2991 return time_point::min();
2992 }
2993
2994 virtual time_point atime() const
2995 {
2996#ifdef _WIN32
2997 FILETIME ft;
2998 if (GetFileTime(m_h, nullptr, &ft, nullptr))
2999 return ft2tp(ft);
3000#else
3001 struct stat buf;
3002 if (fstat(m_h, &buf) >= 0)
3003 return clock::from_time_t(buf.st_atime);
3004#endif
3005 return time_point::min();
3006 }
3007
3008 virtual time_point mtime() const
3009 {
3010#ifdef _WIN32
3011 FILETIME ft;
3012 if (GetFileTime(m_h, nullptr, nullptr, &ft))
3013 return ft2tp(ft);
3014#else
3015 struct stat buf;
3016 if (fstat(m_h, &buf) >= 0)
3017 return clock::from_time_t(buf.st_mtime);
3018#endif
3019 return time_point::min();
3020 }
3021
3022 virtual void set_ctime(time_point date)
3023 {
3024 stdex_assert(m_h != invalid_handle);
3025#ifdef _WIN32
3026 FILETIME ft;
3027 tp2ft(date, ft);
3028 if (SetFileTime(m_h, &ft, nullptr, nullptr))
3029 return;
3030 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3031#else
3032 _Unreferenced_(date);
3033 throw std::runtime_error("not supported");
3034#endif
3035 }
3036
3037 virtual void set_atime(time_point date)
3038 {
3039 stdex_assert(m_h != invalid_handle);
3040#ifdef _WIN32
3041 FILETIME ft;
3042 tp2ft(date, ft);
3043 if (SetFileTime(m_h, nullptr, &ft, nullptr))
3044 return;
3045 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3046#else
3047 struct timespec ts[2] = {
3048 { date.time_since_epoch().count(), 0 },
3049 { 0, UTIME_OMIT },
3050 };
3051 if (futimens(m_h, ts) >= 0)
3052 return;
3053 throw std::system_error(errno, std::system_category(), "futimens failed");
3054#endif
3055 }
3056
3057 virtual void set_mtime(time_point date)
3058 {
3059#ifdef _WIN32
3060 FILETIME ft;
3061 tp2ft(date, ft);
3062 if (SetFileTime(m_h, nullptr, nullptr, &ft))
3063 return;
3064 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3065#else
3066 struct timespec ts[2] = {
3067 { 0, UTIME_OMIT },
3068 { date.time_since_epoch().count(), 0 },
3069 };
3070 if (futimens(m_h, ts) >= 0)
3071 return;
3072 throw std::system_error(errno, std::system_category(), "futimens failed");
3073#endif
3074 }
3075
3081 static bool exists(_In_z_ const stdex::schar_t* filename)
3082 {
3083#ifdef _WIN32
3084 return GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES;
3085#else
3086 struct stat s;
3087 return stat(filename, &s) == 0;
3088#endif
3089 }
3090
3096 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3097 static bool exists(_In_ const std::basic_string<TR, AX>& filename)
3098 {
3099 return exists(filename.c_str());
3100 }
3101
3109 static bool readonly(_In_z_ const stdex::schar_t* filename)
3110 {
3111#ifdef _WIN32
3112 DWORD dwAttr = GetFileAttributes(filename);
3113 return dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_READONLY) != 0;
3114#else
3115 struct stat s;
3116 return stat(filename, &s) == 0 && (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0;
3117#endif
3118 }
3119
3127 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3128 static bool readonly(_In_ const std::basic_string<TR, AX>& filename)
3129 {
3130 return readonly(filename.c_str());
3131 }
3132 };
3133#pragma warning(pop)
3134
3138 class cached_file : public cache
3139 {
3140 public:
3141 cached_file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok, _In_ size_t cache_size = default_cache_size) :
3142 cache(cache_size),
3143 m_source(h, state)
3144 {
3145 init(m_source);
3146 }
3147
3155 cached_file(_In_z_ const schar_t* filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) :
3156 cache(cache_size),
3157 m_source(filename, mode & mode_for_writing ? mode | mode_for_reading : mode)
3158 {
3159 init(m_source);
3160 }
3161
3169 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3170 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) {}
3171
3172 virtual ~cached_file()
3173 {
3174 done();
3175 }
3176
3183 void open(_In_z_ const schar_t* filename, _In_ int mode)
3184 {
3185 invalidate_cache();
3186 if (!ok()) _Unlikely_{
3187 m_state = state_t::fail;
3188 return;
3189 }
3190 m_source.open(filename, mode & mode_for_writing ? mode | mode_for_reading : mode);
3191 if (m_source.ok()) {
3192 init();
3193 return;
3194 }
3195 m_state = state_t::fail;
3196 }
3197
3204 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3205 void open(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3206 {
3207 open(filename.c_str(), mode);
3208 }
3209
3213 operator bool() const noexcept { return m_source; }
3214
3215 protected:
3216 file m_source;
3217 };
3218
3223 {
3224 public:
3225 memory_file(_In_ state_t state = state_t::ok) :
3226 basic(state),
3227 m_data(nullptr),
3228 m_offset(0),
3229 m_size(0),
3230 m_reserved(0),
3231 m_manage(true)
3232 {
3233#if SET_FILE_OP_TIMES
3234 m_ctime = m_atime = m_mtime = time_point::now();
3235#endif
3236 }
3237
3244 memory_file(_In_ size_t size, _In_ state_t state = state_t::ok) :
3245 basic(state),
3246 m_data(reinterpret_cast<uint8_t*>(malloc(size))),
3247 m_offset(0),
3248 m_size(0),
3249 m_reserved(size),
3250 m_manage(true)
3251 {
3252 if (!m_data) {
3253 m_state = state_t::fail;
3254 throw std::bad_alloc();
3255 }
3256#if SET_FILE_OP_TIMES
3257 m_ctime = m_atime = m_mtime = time_point::now();
3258#endif
3259 }
3260
3270 memory_file(_Inout_ void* data, _In_ size_t size, _In_ size_t reserved, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3271 basic(state),
3272 m_data(reinterpret_cast<uint8_t*>(data)),
3273 m_offset(0),
3274 m_size(size),
3275 m_reserved(reserved),
3276 m_manage(manage)
3277 {
3278 stdex_assert(data || !size);
3279 stdex_assert(reserved >= size);
3280#if SET_FILE_OP_TIMES
3281 m_ctime = m_atime = m_mtime = time_point::now();
3282#endif
3283 }
3284
3293 memory_file(_Inout_ void* data, _In_ size_t size, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3294 memory_file(data, size, size, manage, state)
3295 {}
3296
3303 memory_file(_In_z_ const schar_t* filename, _In_ int mode) : memory_file()
3304 {
3305 load(filename, mode);
3306 }
3307
3314 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3315 memory_file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode) : memory_file(filename.c_str(), mode) {}
3316
3322 memory_file(_In_ const memory_file& other) :
3323 basic_file(other),
3324 m_data(reinterpret_cast<uint8_t*>(malloc(other.m_size))),
3325 m_offset(other.m_offset),
3326 m_size(other.m_size),
3327 m_reserved(other.m_size),
3328 m_manage(true)
3329#if SET_FILE_OP_TIMES
3330 , m_ctime(other.m_ctime)
3331 , m_atime(other.m_atime)
3332 , m_mtime(other.m_mtime)
3333#endif
3334 {
3335 if (!m_data) {
3336 m_state = state_t::fail;
3337 throw std::bad_alloc();
3338 }
3339 memcpy(m_data, other.m_data, other.m_size);
3340 }
3341
3348 {
3349 if (this != std::addressof(other)) {
3350 *static_cast<basic_file*>(this) = other;
3351 if (m_manage && m_data)
3352 free(m_data);
3353 m_data = reinterpret_cast<uint8_t*>(malloc(other.m_size));
3354 if (!m_data) {
3355 m_state = state_t::fail;
3356 throw std::bad_alloc();
3357 }
3358 memcpy(m_data, other.m_data, other.m_size);
3359 m_offset = other.m_offset;
3360 m_size = other.m_size;
3361 m_reserved = other.m_size;
3362 m_manage = true;
3363#if SET_FILE_OP_TIMES
3364 m_ctime = other.m_ctime;
3365 m_atime = other.m_atime;
3366 m_mtime = other.m_mtime;
3367#endif
3368 }
3369 return *this;
3370 }
3371
3377 memory_file(_Inout_ memory_file&& other) noexcept :
3378 basic_file(std::move(other)),
3379 m_data(other.m_data),
3380 m_offset(other.m_offset),
3381 m_size(other.m_size),
3382 m_reserved(other.m_reserved),
3383 m_manage(other.m_manage)
3384#if SET_FILE_OP_TIMES
3385 , m_ctime(other.m_ctime)
3386 , m_atime(other.m_atime)
3387 , m_mtime(other.m_mtime)
3388#endif
3389 {
3390 other.m_state = state_t::ok;
3391 other.m_data = nullptr;
3392 other.m_offset = 0;
3393 other.m_size = 0;
3394 other.m_reserved = 0;
3395 other.m_manage = true;
3396#if SET_FILE_OP_TIMES
3397 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3398#endif
3399 }
3400
3406 memory_file& operator=(_Inout_ memory_file&& other) noexcept
3407 {
3408 if (this != std::addressof(other)) {
3409 *static_cast<basic_file*>(this) = std::move(other);
3410 if (m_manage && m_data)
3411 free(m_data);
3412 m_data = other.m_data;
3413 other.m_data = nullptr;
3414 m_offset = other.m_offset;
3415 other.m_offset = 0;
3416 m_size = other.m_size;
3417 other.m_size = 0;
3418 m_reserved = other.m_reserved;
3419 other.m_reserved = 0;
3420 m_manage = other.m_manage;
3421 other.m_manage = true;
3422#if SET_FILE_OP_TIMES
3423 m_ctime = other.m_ctime;
3424 m_atime = other.m_atime;
3425 m_mtime = other.m_mtime;
3426 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3427#endif
3428 }
3429 return *this;
3430 }
3431
3432 virtual ~memory_file()
3433 {
3434 if (m_manage && m_data)
3435 free(m_data);
3436 }
3437
3444 void reserve(_In_ size_t required, _In_ bool tight = false) noexcept
3445 {
3446 if (required <= m_reserved && (!tight || required >= m_reserved)) {
3447 m_state = state_t::ok;
3448 return;
3449 }
3450 if (!m_manage) {
3451 m_state = state_t::fail;
3452 return;
3453 }
3454 size_t reserved = tight ? required : ((required + required / 4 + (default_block_size - 1)) / default_block_size) * default_block_size;
3455 auto data = reinterpret_cast<uint8_t*>(realloc(m_data, reserved));
3456 if (!data && reserved) _Unlikely_ {
3457 m_state = state_t::fail;
3458 return;
3459 }
3460 m_data = data;
3461 if (reserved < m_size)
3462 m_size = reserved;
3463 m_reserved = reserved;
3464 m_state = state_t::ok;
3465 }
3466
3473 void load(_In_z_ const schar_t* filename, _In_ int mode)
3474 {
3475 file f(filename, (mode & ~hint_random_access) | mode_for_reading | hint_sequential_access);
3476 if (!f.ok()) {
3477 m_state = state_t::fail;
3478 return;
3479 }
3480 fsize_t size = f.size();
3481 if (size > SIZE_MAX) {
3482 m_state = state_t::fail;
3483 return;
3484 }
3485 reserve(static_cast<size_t>(size), true);
3486 if (!ok()) _Unlikely_ {
3487 return;
3488 }
3489 m_offset = m_size = 0;
3490 write_stream(f);
3491 if (ok())
3492 m_offset = 0;
3493#if SET_FILE_OP_TIMES
3494 m_ctime = f.ctime();
3495 m_atime = f.atime();
3496 m_mtime = f.mtime();
3497#endif
3498 }
3499
3506 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3507 void load(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3508 {
3509 load(filename.c_str(), mode);
3510 }
3511
3518 void save(_In_z_ const schar_t* filename, _In_ int mode)
3519 {
3520 file f(filename, (mode & ~hint_random_access) | mode_for_writing | hint_sequential_access);
3521 if (!f.ok()) {
3522 m_state = state_t::fail;
3523 return;
3524 }
3525 f.write(m_data, m_size);
3526 if (!f.ok()) {
3527 m_state = state_t::fail;
3528 return;
3529 }
3530 f.truncate();
3531#if SET_FILE_OP_TIMES
3532 f.set_ctime(m_ctime);
3533 f.set_atime(m_atime);
3534 f.set_mtime(m_mtime);
3535#endif
3536 }
3537
3544 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3545 void save(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3546 {
3547 save(filename.c_str(), mode);
3548 }
3549
3553 const void* data() const { return m_data; }
3554
3555 virtual _Success_(return != 0 || length == 0) size_t read(
3556 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
3557 {
3558 stdex_assert(data || !length);
3559#if SET_FILE_OP_TIMES
3560 m_atime = time_point::now();
3561#endif
3562 size_t available = m_size - m_offset;
3563 if (length <= available) {
3564 memcpy(data, m_data + m_offset, length);
3565 m_offset += length;
3566 m_state = state_t::ok;
3567 return length;
3568 }
3569 if (length && !available) {
3570 m_state = state_t::eof;
3571 return 0;
3572 }
3573 memcpy(data, m_data + m_offset, available);
3574 m_offset += available;
3575 m_state = state_t::ok;
3576 return available;
3577 }
3578
3593 template <class T>
3594 memory_file& read_data(_Out_ T& data)
3595 {
3596#if SET_FILE_OP_TIMES
3597 m_atime = time_point::now();
3598#endif
3599 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3600 data = 0;
3601 return *this;
3602 }
3603 size_t end_offset = m_offset + sizeof(T);
3604 if (end_offset <= m_size) {
3605 data = LE2HE(*reinterpret_cast<T*>(m_data + m_offset));
3606 m_offset = end_offset;
3607#if !CHECK_STREAM_STATE
3608 m_state = state_t::ok;
3609#endif
3610 }
3611 else {
3612 data = 0;
3613 m_offset = m_size;
3614 m_state = state_t::eof;
3615 }
3616 return *this;
3617 }
3618
3633 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3634 memory_file& read_str(_Inout_ std::basic_string<T, TR, AX>&data)
3635 {
3636#if SET_FILE_OP_TIMES
3637 m_atime = time_point::now();
3638#endif
3639 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3640 data.clear();
3641 return *this;
3642 }
3643 size_t end_offset = m_offset + sizeof(uint32_t);
3644 if (end_offset <= m_size) {
3645 uint32_t num_chars = LE2HE(*reinterpret_cast<uint32_t*>(m_data + m_offset));
3646 m_offset = end_offset;
3647 end_offset = stdex::add(m_offset, stdex::mul(num_chars, sizeof(T)));
3648 T* start = reinterpret_cast<T*>(m_data + m_offset);
3649 if (end_offset <= m_size) {
3650 data.assign(start, start + num_chars);
3651 m_offset = end_offset;
3652#if !CHECK_STREAM_STATE
3653 m_state = state_t::ok;
3654#endif
3655 return *this;
3656 }
3657 if (end_offset <= m_size)
3658 data.assign(start, reinterpret_cast<T*>(m_data + m_size));
3659 }
3660 m_offset = m_size;
3661 m_state = state_t::eof;
3662 return *this;
3663 }
3664
3665 virtual _Success_(return != 0) size_t write(
3666 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3667 {
3668 stdex_assert(data || !length);
3669#if SET_FILE_OP_TIMES
3670 m_atime = m_mtime = time_point::now();
3671#endif
3672 size_t end_offset = m_offset + length;
3673 if (end_offset > m_reserved) {
3674 reserve(end_offset);
3675 if (!ok()) _Unlikely_
3676 return 0;
3677 }
3678 memcpy(m_data + m_offset, data, length);
3679 m_offset = end_offset;
3680 if (m_offset > m_size)
3681 m_size = m_offset;
3682 m_state = state_t::ok;
3683 return length;
3684 }
3685
3689 void write_byte(_In_ uint8_t byte, _In_ size_t amount = 1)
3690 {
3691#if SET_FILE_OP_TIMES
3692 m_atime = m_mtime = time_point::now();
3693#endif
3694 size_t end_offset = m_offset + amount;
3695 if (end_offset > m_reserved) {
3696 reserve(end_offset);
3697 if (!ok()) _Unlikely_
3698 return;
3699 }
3700 memset(m_data + m_offset, byte, amount);
3701 m_offset = end_offset;
3702 if (m_offset > m_size)
3703 m_size = m_offset;
3704 m_state = state_t::ok;
3705 }
3706
3721 template <class T>
3723 {
3724#if SET_FILE_OP_TIMES
3725 m_atime = m_mtime = time_point::now();
3726#endif
3727 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3728 return *this;
3729 size_t end_offset = m_offset + sizeof(T);
3730 if (end_offset > m_reserved) {
3731 reserve(end_offset);
3732 if (!ok()) _Unlikely_
3733 return *this;
3734 }
3735 (*reinterpret_cast<T*>(m_data + m_offset)) = HE2LE(data);
3736 m_offset = end_offset;
3737 if (m_offset > m_size)
3738 m_size = m_offset;
3739#if !CHECK_STREAM_STATE
3740 m_state = state_t::ok;
3741#endif
3742 return *this;
3743 }
3744
3759 template <class T>
3760 memory_file& write_str(_In_z_ const T * data)
3761 {
3762#if SET_FILE_OP_TIMES
3763 m_atime = m_mtime = time_point::now();
3764#endif
3765 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3766 return *this;
3767 size_t num_chars = stdex::strlen(data);
3768 if (num_chars > UINT32_MAX)
3769 throw std::invalid_argument("string too long");
3770 size_t size_chars = num_chars * sizeof(T);
3771 size_t size = sizeof(uint32_t) + size_chars;
3772 size_t end_offset = m_offset + size;
3773 if (end_offset > m_reserved) {
3774 reserve(end_offset);
3775 if (!ok()) _Unlikely_
3776 return *this;
3777 }
3778 auto p = m_data + m_offset;
3779 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3780 memcpy(p + sizeof(uint32_t), data, size_chars);
3781 m_offset = end_offset;
3782 if (m_offset > m_size)
3783 m_size = m_offset;
3784#if !CHECK_STREAM_STATE
3785 m_state = state_t::ok;
3786#endif
3787 return *this;
3788 }
3789
3804 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3805 memory_file& write_str(_In_ const std::basic_string<T, TR, AX>& data)
3806 {
3807#if SET_FILE_OP_TIMES
3808 m_atime = m_mtime = time_point::now();
3809#endif
3810 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3811 return *this;
3812 size_t num_chars = data.size();
3813 if (num_chars > UINT32_MAX)
3814 throw std::invalid_argument("string too long");
3815 size_t size_chars = num_chars * sizeof(T);
3816 size_t size = sizeof(uint32_t) + size_chars;
3817 size_t end_offset = m_offset + size;
3818 if (end_offset > m_reserved) {
3819 reserve(end_offset);
3820 if (!ok()) _Unlikely_
3821 return *this;
3822 }
3823 auto p = m_data + m_offset;
3824 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3825 memcpy(p + sizeof(uint32_t), data.data(), size_chars);
3826 m_offset = end_offset;
3827 if (m_offset > m_size)
3828 m_size = m_offset;
3829#if !CHECK_STREAM_STATE
3830 m_state = state_t::ok;
3831#endif
3832 return *this;
3833 }
3834
3840 size_t write_stream(_Inout_ basic & stream, _In_ size_t amount = SIZE_MAX)
3841 {
3842#if SET_FILE_OP_TIMES
3843 m_atime = m_mtime = time_point::now();
3844#endif
3845 size_t num_read, dst_offset = m_offset, dst_size = m_offset;
3846 size_t num_copied = 0, to_write = amount;
3847 m_state = state_t::ok;
3848 if (amount != SIZE_MAX) {
3849 dst_size = stdex::add(dst_size, amount);
3850 reserve(dst_size);
3851 if (!ok()) _Unlikely_
3852 return 0;
3853 while (to_write) {
3854 num_read = stream.read(m_data + dst_offset, to_write);
3855 /*dst_size =*/ dst_offset += num_read;
3856 num_copied += num_read;
3857 to_write -= num_read;
3858 if (!stream.ok()) {
3859 if (stream.state() != state_t::eof)
3860 m_state = state_t::fail;
3861 break;
3862 }
3863 };
3864 }
3865 else {
3866 size_t block_size;
3867 while (to_write) {
3868 block_size = std::min(to_write, default_block_size);
3869 dst_size = stdex::add(dst_size, block_size);
3870 reserve(dst_size);
3871 if (!ok()) _Unlikely_
3872 break;
3873 num_read = stream.read(m_data + dst_offset, block_size);
3874 dst_size = dst_offset += num_read;
3875 num_copied += num_read;
3876 to_write -= num_read;
3877 if (!stream.ok()) {
3878 if (stream.state() != state_t::eof)
3879 m_state = state_t::fail;
3880 break;
3881 }
3882 };
3883 }
3884 m_offset = dst_offset;
3885 if (m_offset > m_size)
3886 m_size = m_offset;
3887 return num_copied;
3888 }
3889
3890 virtual void close()
3891 {
3892 if (m_manage && m_data)
3893 free(m_data);
3894 m_data = nullptr;
3895 m_manage = true;
3896 m_offset = 0;
3897 m_size = m_reserved = 0;
3898#if SET_FILE_OP_TIMES
3899 m_ctime = m_atime = m_mtime = time_point::min();
3900#endif
3901 m_state = state_t::ok;
3902 }
3903
3904 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
3905 {
3906 switch (how) {
3907 case seek_t::beg: break;
3908 case seek_t::cur: offset = static_cast<foff_t>(m_offset) + offset; break;
3909 case seek_t::end: offset = static_cast<foff_t>(m_size) + offset; break;
3910 default: throw std::invalid_argument("unknown seek origin");
3911 }
3912 if (offset < 0) _Unlikely_
3913 throw std::invalid_argument("negative file offset");
3914 if (static_cast<fpos_t>(offset) > SIZE_MAX) _Unlikely_
3915 throw std::invalid_argument("file offset too big");
3916 m_state = state_t::ok;
3917 return m_offset = static_cast<size_t>(offset);
3918 }
3919
3920 virtual fpos_t tell() const
3921 {
3922 return m_offset;
3923 }
3924
3925 virtual fsize_t size() const
3926 {
3927 return m_size;
3928 }
3929
3930 virtual void truncate()
3931 {
3932#if SET_FILE_OP_TIMES
3933 m_atime = m_mtime = time_point::now();
3934#endif
3935 m_size = m_offset;
3936 reserve(m_offset);
3937 }
3938
3939#if SET_FILE_OP_TIMES
3940 virtual time_point ctime() const
3941 {
3942 return m_ctime;
3943 }
3944
3945 virtual time_point atime() const
3946 {
3947 return m_atime;
3948 }
3949
3950 virtual time_point mtime() const
3951 {
3952 return m_mtime;
3953 }
3954
3955 virtual void set_ctime(time_point date)
3956 {
3957 m_ctime = date;
3958 }
3959
3960 virtual void set_atime(time_point date)
3961 {
3962 m_atime = date;
3963 }
3964
3965 virtual void set_mtime(time_point date)
3966 {
3967 m_mtime = date;
3968 }
3969#endif
3970
3971 protected:
3979 template <class T>
3980 void set(_In_ fpos_t offset, _In_ const T data)
3981 {
3982#if SET_FILE_OP_TIMES
3983 m_atime = m_mtime = time_point::now();
3984#endif
3985 stdex_assert(offset + sizeof(T) < m_size);
3986 (*reinterpret_cast<T*>(m_data + offset)) = HE2LE(data);
3987 }
3988
3989 public:
3990 void set(_In_ fpos_t offset, _In_ const int8_t data) { set<int8_t>(offset, data); }
3991 void set(_In_ fpos_t offset, _In_ const int16_t data) { set<int16_t>(offset, data); }
3992 void set(_In_ fpos_t offset, _In_ const int32_t data) { set<int32_t>(offset, data); }
3993 void set(_In_ fpos_t offset, _In_ const int64_t data) { set<int64_t>(offset, data); }
3994 void set(_In_ fpos_t offset, _In_ const uint8_t data) { set<uint8_t>(offset, data); }
3995 void set(_In_ fpos_t offset, _In_ const uint16_t data) { set<uint16_t>(offset, data); }
3996 void set(_In_ fpos_t offset, _In_ const uint32_t data) { set<uint32_t>(offset, data); }
3997 void set(_In_ fpos_t offset, _In_ const uint64_t data) { set<uint64_t>(offset, data); }
3998 void set(_In_ fpos_t offset, _In_ const float data) { set<float>(offset, data); }
3999 void set(_In_ fpos_t offset, _In_ const double data) { set<double>(offset, data); }
4000 void set(_In_ fpos_t offset, _In_ const char data) { set<char>(offset, data); }
4001#ifdef _NATIVE_WCHAR_T_DEFINED
4002 void set(_In_ fpos_t offset, _In_ const wchar_t data) { set<wchar_t>(offset, data); }
4003#endif
4004
4012 protected:
4013 template <class T>
4014 void get(_In_ fpos_t offset, _Out_ T & data)
4015 {
4016 stdex_assert(offset + sizeof(T) < m_size);
4017 data = LE2HE(*(T*)(m_data + offset));
4018#if SET_FILE_OP_TIMES
4019 m_atime = time_point::now();
4020#endif
4021 }
4022
4023 public:
4024 void get(_In_ fpos_t offset, _Out_ int8_t & data) { get<int8_t>(offset, data); }
4025 void get(_In_ fpos_t offset, _Out_ int16_t & data) { get<int16_t>(offset, data); }
4026 void get(_In_ fpos_t offset, _Out_ int32_t & data) { get<int32_t>(offset, data); }
4027 void get(_In_ fpos_t offset, _Out_ int64_t & data) { get<int64_t>(offset, data); }
4028 void get(_In_ fpos_t offset, _Out_ uint8_t & data) { get<uint8_t>(offset, data); }
4029 void get(_In_ fpos_t offset, _Out_ uint16_t & data) { get<uint16_t>(offset, data); }
4030 void get(_In_ fpos_t offset, _Out_ uint32_t & data) { get<uint32_t>(offset, data); }
4031 void get(_In_ fpos_t offset, _Out_ uint64_t & data) { get<uint64_t>(offset, data); }
4032 void get(_In_ fpos_t offset, _Out_ float& data) { get<float>(offset, data); }
4033 void get(_In_ fpos_t offset, _Out_ double& data) { get<double>(offset, data); }
4034 void get(_In_ fpos_t offset, _Out_ char& data) { get<char>(offset, data); }
4035#ifdef _NATIVE_WCHAR_T_DEFINED
4036 void get(_In_ fpos_t offset, _Out_ wchar_t& data) { get<wchar_t>(offset, data); }
4037#endif
4038
4039 memory_file& operator <<(_In_ const int8_t data) { return write_data(data); }
4040 memory_file& operator >>(_Out_ int8_t & data) { return read_data(data); }
4041 memory_file& operator <<(_In_ const int16_t data) { return write_data(data); }
4042 memory_file& operator >>(_Out_ int16_t & data) { return read_data(data); }
4043 memory_file& operator <<(_In_ const int32_t data) { return write_data(data); }
4044 memory_file& operator >>(_Out_ int32_t & data) { return read_data(data); }
4045 memory_file& operator <<(_In_ const int64_t data) { return write_data(data); }
4046 memory_file& operator >>(_Out_ int64_t & data) { return read_data(data); }
4047 memory_file& operator <<(_In_ const uint8_t data) { return write_data(data); }
4048 memory_file& operator >>(_Out_ uint8_t & data) { return read_data(data); }
4049 memory_file& operator <<(_In_ const uint16_t data) { return write_data(data); }
4050 memory_file& operator >>(_Out_ uint16_t & data) { return read_data(data); }
4051 memory_file& operator <<(_In_ const uint32_t data) { return write_data(data); }
4052 memory_file& operator >>(_Out_ uint32_t & data) { return read_data(data); }
4053 memory_file& operator <<(_In_ const uint64_t data) { return write_data(data); }
4054 memory_file& operator >>(_Out_ uint64_t & data) { return read_data(data); }
4055 memory_file& operator <<(_In_ const float data) { return write_data(data); }
4056 memory_file& operator >>(_Out_ float& data) { return read_data(data); }
4057 memory_file& operator <<(_In_ const double data) { return write_data(data); }
4058 memory_file& operator >>(_Out_ double& data) { return read_data(data); }
4059 memory_file& operator <<(_In_ const char data) { return write_data(data); }
4060 memory_file& operator >>(_Out_ char& data) { return read_data(data); }
4061#ifdef _NATIVE_WCHAR_T_DEFINED
4062 memory_file& operator <<(_In_ const wchar_t data) { return write_data(data); }
4063 memory_file& operator >>(_Out_ wchar_t& data) { return read_data(data); }
4064#endif
4065 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
4066 memory_file& operator >>(_Out_ std::basic_string<T, TR, AX>&data) { return read_str(data); }
4067 template <class T>
4068 memory_file& operator <<(_In_ const T * data) { return write_str(data); }
4069 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
4070 memory_file& operator <<(_In_ const std::basic_string<T, TR, AX>& data) { return write_str(data); }
4071
4072 protected:
4073 uint8_t* m_data;
4075 size_t m_offset;
4076 size_t m_size;
4077 size_t m_reserved;
4078#if SET_FILE_OP_TIMES
4079 time_point
4080 m_ctime,
4081 m_atime,
4082 m_mtime;
4083#endif
4084 };
4085
4089 class fifo : public basic {
4090 public:
4091 fifo() :
4092 m_offset(0),
4093 m_size(0),
4094 m_head(nullptr),
4095 m_tail(nullptr)
4096 {}
4097
4098 virtual ~fifo()
4099 {
4100 while (m_head) {
4101 auto p = m_head;
4102 m_head = p->next;
4103 delete p;
4104 }
4105 }
4106
4107#pragma warning(suppress: 6101) // See [2] below
4108 virtual _Success_(return != 0 || length == 0) size_t read(
4109 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4110 {
4111 stdex_assert(data || !length);
4112 for (size_t to_read = length;;) {
4113 if (!m_head) _Unlikely_ {
4114 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
4115 return length - to_read; // [2] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
4116 }
4117 size_t remaining = m_head->size - m_offset;
4118 if (remaining > to_read) {
4119 memcpy(data, m_head->data + m_offset, to_read);
4120 m_offset += to_read;
4121 m_size -= to_read;
4122 m_state = state_t::ok;
4123 return length;
4124 }
4125 memcpy(data, m_head->data + m_offset, remaining);
4126 m_offset = 0;
4127 m_size -= remaining;
4128 reinterpret_cast<uint8_t*&>(data) += remaining;
4129 to_read -= remaining;
4130 auto p = m_head;
4131 m_head = p->next;
4132 delete p;
4133 }
4134 }
4135
4136 virtual _Success_(return != 0) size_t write(
4137 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4138 {
4139 stdex_assert(data || !length);
4140 try {
4141 std::unique_ptr<node_t> n(reinterpret_cast<node_t*>(new uint8_t[sizeof(node_t) + length]));
4142 n->next = nullptr;
4143 n->size = length;
4144 memcpy(n->data, data, length);
4145 m_size += length;
4146 if (m_head)
4147 m_tail = m_tail->next = n.release();
4148 else
4149 m_head = m_tail = n.release();
4150 m_state = state_t::ok;
4151 return length;
4152 }
4153 catch (const std::bad_alloc&) {
4154 m_state = state_t::fail;
4155 return 0;
4156 }
4157 }
4158
4159 virtual void close()
4160 {
4161 m_size = m_offset = 0;
4162 while (m_head) {
4163 auto p = m_head;
4164 m_head = p->next;
4165 delete p;
4166 }
4167 m_state = state_t::ok;
4168 }
4169
4173 size_t size() const { return m_size; };
4174
4175 protected:
4176 size_t m_offset, m_size;
4177 struct node_t {
4178 node_t* next;
4179 size_t size;
4180#pragma warning(suppress:4200)
4181 uint8_t data[0];
4182 } *m_head, * m_tail;
4183 };
4184
4188 class diag_file : public basic_file {
4189 public:
4190 diag_file(_In_count_(num_files) basic_file* const* files, _In_ size_t num_files) :
4191 basic(num_files ? files[0]->state() : state_t::fail),
4192 m_files(files, files + num_files)
4193 {}
4194
4195 virtual _Success_(return != 0 || length == 0) size_t read(
4196 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4197 {
4198 stdex_assert(data || !length);
4199 if (m_files.empty()) {
4200 m_state = state_t::fail;
4201 return 0;
4202 }
4203 size_t result = m_files[0]->read(data, length);
4204 stdex_assert(result <= length);
4205 m_state = m_files[0]->state();
4206 if (length > m_tmp.size())
4207 m_tmp.resize(length);
4208 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4209 if (m_files[i]->read(m_tmp.data(), length) != result ||
4210 memcmp(m_tmp.data(), data, result))
4211 throw std::runtime_error("read mismatch");
4212 if (m_files[i]->state() != m_state)
4213 throw std::runtime_error("state mismatch");
4214 }
4215 return result;
4216 }
4217
4218 virtual _Success_(return != 0) size_t write(
4219 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4220 {
4221 if (m_files.empty()) {
4222 m_state = state_t::fail;
4223 return 0;
4224 }
4225 size_t result = m_files[0]->write(data, length);
4226 m_state = m_files[0]->state();
4227 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4228 if (m_files[i]->write(data, length) != result)
4229 throw std::runtime_error("write mismatch");
4230 if (m_files[i]->state() != m_state)
4231 throw std::runtime_error("state mismatch");
4232 }
4233 return result;
4234 }
4235
4236 virtual void flush()
4237 {
4238 if (m_files.empty()) {
4239 m_state = state_t::ok;
4240 return;
4241 }
4242 m_files[0]->flush();
4243 m_state = m_files[0]->state();
4244 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4245 m_files[i]->flush();
4246 if (m_files[i]->state() != m_state)
4247 throw std::runtime_error("state mismatch");
4248 }
4249 }
4250
4251 virtual void close()
4252 {
4253 if (m_files.empty()) {
4254 m_state = state_t::ok;
4255 return;
4256 }
4257 m_files[0]->close();
4258 m_state = m_files[0]->state();
4259 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4260 m_files[i]->close();
4261 if (m_files[i]->state() != m_state)
4262 throw std::runtime_error("state mismatch");
4263 }
4264 m_tmp.clear();
4265 m_tmp.shrink_to_fit();
4266 }
4267
4268 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
4269 {
4270 if (m_files.empty()) {
4271 m_state = state_t::fail;
4272 return fpos_max;
4273 }
4274 fpos_t result = m_files[0]->seek(offset, how);
4275 m_state = m_files[0]->state();
4276 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4277 if (m_files[i]->seek(offset, how) != result)
4278 throw std::runtime_error("seek mismatch");
4279 if (m_files[i]->state() != m_state)
4280 throw std::runtime_error("state mismatch");
4281 }
4282 return result;
4283 }
4284
4285 virtual fpos_t tell() const
4286 {
4287 if (m_files.empty())
4288 return fpos_max;
4289 fpos_t result = m_files[0]->tell();
4290 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4291 if (m_files[i]->tell() != result)
4292 throw std::runtime_error("tell mismatch");
4293 }
4294 return result;
4295 }
4296
4297 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
4298 {
4299 if (m_files.empty())
4300 m_state = state_t::fail;
4301 m_files[0]->lock(offset, length);
4302 m_state = m_files[0]->state();
4303 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4304 m_files[i]->lock(offset, length);
4305 if (m_files[i]->state() != m_state)
4306 throw std::runtime_error("state mismatch");
4307 }
4308 }
4309
4310 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
4311 {
4312 if (m_files.empty())
4313 m_state = state_t::fail;
4314 m_files[0]->unlock(offset, length);
4315 m_state = m_files[0]->state();
4316 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4317 m_files[i]->unlock(offset, length);
4318 if (m_files[i]->state() != m_state)
4319 throw std::runtime_error("state mismatch");
4320 }
4321 }
4322
4323 virtual fsize_t size() const
4324 {
4325 if (m_files.empty())
4326 return fsize_max;
4327 fsize_t result = m_files[0]->size();
4328 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4329 if (m_files[i]->size() != result)
4330 throw std::runtime_error("size mismatch");
4331 }
4332 return result;
4333 }
4334
4335 virtual void truncate()
4336 {
4337 if (m_files.empty())
4338 m_state = state_t::fail;
4339 m_files[0]->truncate();
4340 m_state = m_files[0]->state();
4341 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4342 m_files[i]->truncate();
4343 if (m_files[i]->state() != m_state)
4344 throw std::runtime_error("state mismatch");
4345 }
4346 }
4347
4348 protected:
4349 std::vector<basic_file*> m_files;
4350 std::vector<uint8_t> m_tmp;
4351 };
4352 }
4353}
4354
4355#if defined(__GNUC__)
4356#pragma GCC diagnostic pop
4357#endif
Operating system object base class.
Definition system.hpp:142
Encoding converter context.
Definition unicode.hpp:139
locale_t helper class to free_locale when going out of scope.
Definition locale.hpp:74
Provides read-ahead stream capability.
Definition stream.hpp:1258
Provides write-back stream capability.
Definition stream.hpp:1325
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1362
Basic seekable stream operations.
Definition stream.hpp:819
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:865
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:914
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:883
virtual void truncate()=0
Sets file size - truncates the remainder of file content from the current file position to the end of...
virtual fsize_t size() const =0
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
charset_id read_charset(charset_id default_charset=charset_id::system)
Attempts to detect text-file charset based on UTF-32, UTF-16 or UTF-8 BOM.
Definition stream.hpp:989
fpos_t seekbeg(fpos_t offset)
Seeks to absolute file position.
Definition stream.hpp:846
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:821
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:956
fpos_t seekcur(foff_t offset)
Seeks to relative from current file position.
Definition stream.hpp:856
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:922
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:938
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:893
virtual fpos_t tell() const =0
Returns absolute file position in file or fpos_max if fails. This method does not update stream state...
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:930
fpos_t seekend(foff_t offset)
Seeks to relative from end file position.
Definition stream.hpp:863
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:947
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)=0
Seeks to specified relative file position.
OS data stream (file, pipe, socket...)
Definition stream.hpp:2213
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:2270
virtual void flush()
Persists volatile element data.
Definition stream.hpp:2328
virtual void close()
Closes the stream.
Definition stream.hpp:2317
Basic stream operations.
Definition stream.hpp:85
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
bool ok() const
Returns true if the stream state is clean i.e. previous operation was successful.
Definition stream.hpp:181
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
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:632
state_t state() const
Returns stream state after last operation.
Definition stream.hpp:176
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:618
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:645
virtual void flush()
Persists volatile element data.
Definition stream.hpp:132
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:148
virtual void close()
Closes the stream.
Definition stream.hpp:140
uint8_t read_byte()
Reads one byte of data.
Definition stream.hpp:216
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:190
size_t write_sprintf(_Printf_format_string_params_(2) const char *format, locale_t locale,...)
Writes formatted string to the stream.
Definition stream.hpp:604
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
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
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
basic & read_str(std::basic_string< T, TR, AX > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:471
basic & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:504
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
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
void write_charset(charset_id charset)
Writes UTF8, UTF-16 or UTF-32 byte-order-mark.
Definition stream.hpp:589
basic & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:293
fsize_t write_stream(basic &stream, fsize_t amount=fsize_max)
Writes content of another stream.
Definition stream.hpp:564
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
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
basic & write_str(const std::basic_string< T, TR, AX > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:529
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
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
void write_byte(uint8_t byte, fsize_t amount=1)
Writes a byte of data.
Definition stream.hpp:227
basic & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:265
Buffered read/write stream.
Definition stream.hpp:1396
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1505
Buffered OS data stream (file, pipe, socket...)
Definition stream.hpp:2342
Cached file.
Definition stream.hpp:1806
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2083
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2062
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2088
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2049
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:2097
virtual void close()
Closes the stream.
Definition stream.hpp:1992
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:2119
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2043
virtual void flush()
Persists volatile element data.
Definition stream.hpp:2001
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:2055
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:2106
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:2038
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:2111
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2012
Cached file-system file.
Definition stream.hpp:3139
void open(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:3205
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:3183
cached_file(const schar_t *filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3155
cached_file(const std::basic_string< TR, AX > &filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3170
Modifies data on the fly when reading from/writing to a source stream. Could also be used to modify r...
Definition stream.hpp:1024
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1075
virtual void close()
Closes the stream.
Definition stream.hpp:1069
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:1053
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:1061
Compares multiple files to perform the same.
Definition stream.hpp:4188
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:4335
virtual void close()
Closes the stream.
Definition stream.hpp:4251
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:4323
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:4297
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:4310
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:4268
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:4285
virtual void flush()
Persists volatile element data.
Definition stream.hpp:4236
In-memory FIFO queue.
Definition stream.hpp:4089
virtual void close()
Closes the stream.
Definition stream.hpp:4159
size_t size() const
Returns total size of pending data in the queue.
Definition stream.hpp:4173
Limits file reading/writing to a predefined window.
Definition stream.hpp:1696
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:1789
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1739
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:1752
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:1745
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:1764
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:1774
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:1758
virtual void close()
Closes the stream.
Definition stream.hpp:1733
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:1784
File-system file.
Definition stream.hpp:2720
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:2924
static bool readonly(const std::basic_string< TR, AX > &filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3128
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:3008
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2895
file(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2730
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:3022
static bool readonly(const stdex::schar_t *filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3109
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2994
static bool exists(const std::basic_string< TR, AX > &filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3097
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2750
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:3057
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:3037
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2868
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2944
void open(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:2824
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2984
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2829
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:2850
file(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:2742
static bool exists(const stdex::schar_t *filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3081
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1553
fsize_t read_limit
Number of bytes left that may be read from the stream.
Definition stream.hpp:1605
fsize_t write_limit
Number of bytes left, that can be written to the stream.
Definition stream.hpp:1606
In-memory file.
Definition stream.hpp:3223
void save(const std::basic_string< TR, AX > &filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3545
memory_file & operator=(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3406
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:3925
memory_file(const schar_t *filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3303
memory_file & read_str(std::basic_string< T, TR, AX > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:3634
memory_file(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3322
memory_file & write_str(const std::basic_string< T, TR, AX > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3805
void load(const std::basic_string< TR, AX > &filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3507
size_t m_size
file size
Definition stream.hpp:4076
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:4014
size_t write_stream(basic &stream, size_t amount=SIZE_MAX)
Writes content of another stream.
Definition stream.hpp:3840
uint8_t * m_data
file data
Definition stream.hpp:4073
memory_file & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:3594
virtual void close()
Closes the stream.
Definition stream.hpp:3890
memory_file(const std::basic_string< TR, AX > &filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3315
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:3920
size_t m_reserved
reserved file size
Definition stream.hpp:4077
memory_file(size_t size, state_t state=state_t::ok)
Creates an empty file of reserved size.
Definition stream.hpp:3244
void reserve(size_t required, bool tight=false) noexcept
Reallocates memory.
Definition stream.hpp:3444
memory_file(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3377
void write_byte(uint8_t byte, size_t amount=1)
Writes a byte of data.
Definition stream.hpp:3689
memory_file & operator=(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3347
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:3980
size_t m_offset
file pointer
Definition stream.hpp:4075
void save(const schar_t *filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3518
void load(const schar_t *filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3473
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3904
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:3930
memory_file & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:3722
memory_file & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3760
bool m_manage
may reallocate m_data?
Definition stream.hpp:4074
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:3293
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:3270
const void * data() const
Returns pointer to data.
Definition stream.hpp:3553
Definition stream.hpp:1172
size_t num_written
Number of bytes written.
Definition stream.hpp:1221
size_t length
Byte limit of data to write.
Definition stream.hpp:1220
const void * data
Data to write.
Definition stream.hpp:1219
Replicates writing of the same data to multiple streams.
Definition stream.hpp:1089
void push_back(basic *source)
Adds stream on the list.
Definition stream.hpp:1108
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1165
void remove(basic *source)
Removes stream from the list.
Definition stream.hpp:1116
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:1133
virtual void close()
Closes the stream.
Definition stream.hpp:1160
Socket stream.
Definition stream.hpp:2364
socket_t get() const noexcept
Returns socket handle.
Definition stream.hpp:2420
virtual void close()
Closes the stream.
Definition stream.hpp:2478
socket(int af, int type, int protocol)
Creates a socket.
Definition stream.hpp:2399
Limits reading from/writing to stream to a predefined window.
Definition stream.hpp:1613
fpos_t write_offset
Number of bytes to discard on write.
Definition stream.hpp:1689
fpos_t read_offset
Number of bytes to skip on read.
Definition stream.hpp:1688
Numerical interval.
Definition interval.hpp:18
Definition stream.hpp:1530
Definition stream.hpp:4177