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