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 _Unreferenced_(date);
3031 throw std::runtime_error("not supported");
3032#endif
3033 }
3034
3035 virtual void set_atime(time_point date)
3036 {
3037 _Assume_(m_h != invalid_handle);
3038#ifdef _WIN32
3039 FILETIME ft;
3040 tp2ft(date, ft);
3041 if (SetFileTime(m_h, nullptr, &ft, nullptr))
3042 return;
3043 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3044#else
3045 struct timespec ts[2] = {
3046 { date.time_since_epoch().count(), 0 },
3047 { 0, UTIME_OMIT },
3048 };
3049 if (futimens(m_h, ts) >= 0)
3050 return;
3051 throw std::system_error(errno, std::system_category(), "futimens failed");
3052#endif
3053 }
3054
3055 virtual void set_mtime(time_point date)
3056 {
3057#ifdef _WIN32
3058 FILETIME ft;
3059 tp2ft(date, ft);
3060 if (SetFileTime(m_h, nullptr, nullptr, &ft))
3061 return;
3062 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3063#else
3064 struct timespec ts[2] = {
3065 { 0, UTIME_OMIT },
3066 { date.time_since_epoch().count(), 0 },
3067 };
3068 if (futimens(m_h, ts) >= 0)
3069 return;
3070 throw std::system_error(errno, std::system_category(), "futimens failed");
3071#endif
3072 }
3073
3079 static bool exists(_In_z_ const stdex::schar_t* filename)
3080 {
3081#ifdef _WIN32
3082 return GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES;
3083#else
3084 struct stat s;
3085 return stat(filename, &s) == 0;
3086#endif
3087 }
3088
3094 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3095 static bool exists(_In_ const std::basic_string<TR, AX>& filename)
3096 {
3097 return exists(filename.c_str());
3098 }
3099
3107 static bool readonly(_In_z_ const stdex::schar_t* filename)
3108 {
3109#ifdef _WIN32
3110 DWORD dwAttr = GetFileAttributes(filename);
3111 return dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_READONLY) != 0;
3112#else
3113 struct stat s;
3114 return stat(filename, &s) == 0 && (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0;
3115#endif
3116 }
3117
3125 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3126 static bool readonly(_In_ const std::basic_string<TR, AX>& filename)
3127 {
3128 return readonly(filename.c_str());
3129 }
3130 };
3131#pragma warning(pop)
3132
3136 class cached_file : public cache
3137 {
3138 public:
3139 cached_file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok, _In_ size_t cache_size = default_cache_size) :
3140 cache(cache_size),
3141 m_source(h, state)
3142 {
3143 init(m_source);
3144 }
3145
3153 cached_file(_In_z_ const schar_t* filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) :
3154 cache(cache_size),
3155 m_source(filename, mode & mode_for_writing ? mode | mode_for_reading : mode)
3156 {
3157 init(m_source);
3158 }
3159
3167 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3168 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) {}
3169
3170 virtual ~cached_file()
3171 {
3172 done();
3173 }
3174
3181 void open(_In_z_ const schar_t* filename, _In_ int mode)
3182 {
3183 invalidate_cache();
3184 if (!ok()) _Unlikely_{
3185 m_state = state_t::fail;
3186 return;
3187 }
3188 m_source.open(filename, mode & mode_for_writing ? mode | mode_for_reading : mode);
3189 if (m_source.ok()) {
3190 init();
3191 return;
3192 }
3193 m_state = state_t::fail;
3194 }
3195
3202 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3203 void open(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3204 {
3205 open(filename.c_str(), mode);
3206 }
3207
3211 operator bool() const noexcept { return m_source; }
3212
3213 protected:
3214 file m_source;
3215 };
3216
3221 {
3222 public:
3223 memory_file(_In_ state_t state = state_t::ok) :
3224 basic(state),
3225 m_data(nullptr),
3226 m_offset(0),
3227 m_size(0),
3228 m_reserved(0),
3229 m_manage(true)
3230 {
3231#if SET_FILE_OP_TIMES
3232 m_ctime = m_atime = m_mtime = time_point::now();
3233#endif
3234 }
3235
3242 memory_file(_In_ size_t size, _In_ state_t state = state_t::ok) :
3243 basic(state),
3244 m_data(reinterpret_cast<uint8_t*>(malloc(size))),
3245 m_offset(0),
3246 m_size(0),
3248 m_manage(true)
3249 {
3250 if (!m_data) {
3251 m_state = state_t::fail;
3252 throw std::bad_alloc();
3253 }
3254#if SET_FILE_OP_TIMES
3255 m_ctime = m_atime = m_mtime = time_point::now();
3256#endif
3257 }
3258
3268 memory_file(_Inout_ void* data, _In_ size_t size, _In_ size_t reserved, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3269 basic(state),
3270 m_data(reinterpret_cast<uint8_t*>(data)),
3271 m_offset(0),
3272 m_size(size),
3273 m_reserved(reserved),
3274 m_manage(manage)
3275 {
3276 _Assume_(data || !size);
3277 _Assume_(reserved >= size);
3278#if SET_FILE_OP_TIMES
3279 m_ctime = m_atime = m_mtime = time_point::now();
3280#endif
3281 }
3282
3291 memory_file(_Inout_ void* data, _In_ size_t size, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3292 memory_file(data, size, size, manage, state)
3293 {}
3294
3301 memory_file(_In_z_ const schar_t* filename, _In_ int mode) : memory_file()
3302 {
3303 load(filename, mode);
3304 }
3305
3312 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3313 memory_file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode) : memory_file(filename.c_str(), mode) {}
3314
3320 memory_file(_In_ const memory_file& other) :
3321 basic_file(other),
3322 m_data(reinterpret_cast<uint8_t*>(malloc(other.m_size))),
3323 m_offset(other.m_offset),
3324 m_size(other.m_size),
3325 m_reserved(other.m_size),
3326 m_manage(true)
3327#if SET_FILE_OP_TIMES
3328 , m_ctime(other.m_ctime)
3329 , m_atime(other.m_atime)
3330 , m_mtime(other.m_mtime)
3331#endif
3332 {
3333 if (!m_data) {
3334 m_state = state_t::fail;
3335 throw std::bad_alloc();
3336 }
3337 memcpy(m_data, other.m_data, other.m_size);
3338 }
3339
3346 {
3347 if (this != std::addressof(other)) {
3348 *static_cast<basic_file*>(this) = other;
3349 if (m_manage && m_data)
3350 free(m_data);
3351 m_data = reinterpret_cast<uint8_t*>(malloc(other.m_size));
3352 if (!m_data) {
3353 m_state = state_t::fail;
3354 throw std::bad_alloc();
3355 }
3356 memcpy(m_data, other.m_data, other.m_size);
3357 m_offset = other.m_offset;
3358 m_size = other.m_size;
3359 m_reserved = other.m_size;
3360 m_manage = true;
3361#if SET_FILE_OP_TIMES
3362 m_ctime = other.m_ctime;
3363 m_atime = other.m_atime;
3364 m_mtime = other.m_mtime;
3365#endif
3366 }
3367 return *this;
3368 }
3369
3375 memory_file(_Inout_ memory_file&& other) noexcept :
3376 basic_file(std::move(other)),
3377 m_data(other.m_data),
3378 m_offset(other.m_offset),
3379 m_size(other.m_size),
3380 m_reserved(other.m_reserved),
3381 m_manage(other.m_manage)
3382#if SET_FILE_OP_TIMES
3383 , m_ctime(other.m_ctime)
3384 , m_atime(other.m_atime)
3385 , m_mtime(other.m_mtime)
3386#endif
3387 {
3388 other.m_state = state_t::ok;
3389 other.m_data = nullptr;
3390 other.m_offset = 0;
3391 other.m_size = 0;
3392 other.m_reserved = 0;
3393 other.m_manage = true;
3394#if SET_FILE_OP_TIMES
3395 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3396#endif
3397 }
3398
3404 memory_file& operator=(_Inout_ memory_file&& other) noexcept
3405 {
3406 if (this != std::addressof(other)) {
3407 *static_cast<basic_file*>(this) = std::move(other);
3408 if (m_manage && m_data)
3409 free(m_data);
3410 m_data = other.m_data;
3411 other.m_data = nullptr;
3412 m_offset = other.m_offset;
3413 other.m_offset = 0;
3414 m_size = other.m_size;
3415 other.m_size = 0;
3416 m_reserved = other.m_reserved;
3417 other.m_reserved = 0;
3418 m_manage = other.m_manage;
3419 other.m_manage = true;
3420#if SET_FILE_OP_TIMES
3421 m_ctime = other.m_ctime;
3422 m_atime = other.m_atime;
3423 m_mtime = other.m_mtime;
3424 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3425#endif
3426 }
3427 return *this;
3428 }
3429
3430 virtual ~memory_file()
3431 {
3432 if (m_manage && m_data)
3433 free(m_data);
3434 }
3435
3442 void reserve(_In_ size_t required, _In_ bool tight = false) noexcept
3443 {
3444 if (required <= m_reserved && (!tight || required >= m_reserved)) {
3445 m_state = state_t::ok;
3446 return;
3447 }
3448 if (!m_manage) {
3449 m_state = state_t::fail;
3450 return;
3451 }
3452 size_t reserved = tight ? required : ((required + required / 4 + (default_block_size - 1)) / default_block_size) * default_block_size;
3453 auto data = reinterpret_cast<uint8_t*>(realloc(m_data, reserved));
3454 if (!data && reserved) _Unlikely_ {
3455 m_state = state_t::fail;
3456 return;
3457 }
3458 m_data = data;
3459 if (reserved < m_size)
3460 m_size = reserved;
3461 m_reserved = reserved;
3462 m_state = state_t::ok;
3463 }
3464
3471 void load(_In_z_ const schar_t* filename, _In_ int mode)
3472 {
3473 file f(filename, (mode & ~hint_random_access) | mode_for_reading | hint_sequential_access);
3474 if (!f.ok()) {
3475 m_state = state_t::fail;
3476 return;
3477 }
3478 fsize_t size = f.size();
3479 if (size > SIZE_MAX) {
3480 m_state = state_t::fail;
3481 return;
3482 }
3483 reserve(static_cast<size_t>(size), true);
3484 if (!ok()) _Unlikely_ {
3485 return;
3486 }
3487 m_offset = m_size = 0;
3488 write_stream(f);
3489 if (ok())
3490 m_offset = 0;
3491#if SET_FILE_OP_TIMES
3492 m_ctime = f.ctime();
3493 m_atime = f.atime();
3494 m_mtime = f.mtime();
3495#endif
3496 }
3497
3504 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3505 void load(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3506 {
3507 load(filename.c_str(), mode);
3508 }
3509
3516 void save(_In_z_ const schar_t* filename, _In_ int mode)
3517 {
3518 file f(filename, (mode & ~hint_random_access) | mode_for_writing | hint_sequential_access);
3519 if (!f.ok()) {
3520 m_state = state_t::fail;
3521 return;
3522 }
3523 f.write(m_data, m_size);
3524 if (!f.ok()) {
3525 m_state = state_t::fail;
3526 return;
3527 }
3528 f.truncate();
3529#if SET_FILE_OP_TIMES
3530 f.set_ctime(m_ctime);
3531 f.set_atime(m_atime);
3532 f.set_mtime(m_mtime);
3533#endif
3534 }
3535
3542 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3543 void save(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3544 {
3545 save(filename.c_str(), mode);
3546 }
3547
3551 const void* data() const { return m_data; }
3552
3553 virtual _Success_(return != 0 || length == 0) size_t read(
3554 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
3555 {
3556 _Assume_(data || !length);
3557#if SET_FILE_OP_TIMES
3558 m_atime = time_point::now();
3559#endif
3560 size_t available = m_size - m_offset;
3561 if (length <= available) {
3562 memcpy(data, m_data + m_offset, length);
3563 m_offset += length;
3564 m_state = state_t::ok;
3565 return length;
3566 }
3567 if (length && !available) {
3568 m_state = state_t::eof;
3569 return 0;
3570 }
3571 memcpy(data, m_data + m_offset, available);
3572 m_offset += available;
3573 m_state = state_t::ok;
3574 return available;
3575 }
3576
3591 template <class T>
3593 {
3594#if SET_FILE_OP_TIMES
3595 m_atime = time_point::now();
3596#endif
3597 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3598 data = 0;
3599 return *this;
3600 }
3601 size_t end_offset = m_offset + sizeof(T);
3602 if (end_offset <= m_size) {
3603 data = LE2HE(*reinterpret_cast<T*>(m_data + m_offset));
3604 m_offset = end_offset;
3605#if !CHECK_STREAM_STATE
3606 m_state = state_t::ok;
3607#endif
3608 }
3609 else {
3610 data = 0;
3611 m_offset = m_size;
3612 m_state = state_t::eof;
3613 }
3614 return *this;
3615 }
3616
3631 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3632 memory_file& read_str(_Inout_ std::basic_string<T, TR, AX>&data)
3633 {
3634#if SET_FILE_OP_TIMES
3635 m_atime = time_point::now();
3636#endif
3637 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3638 data.clear();
3639 return *this;
3640 }
3641 size_t end_offset = m_offset + sizeof(uint32_t);
3642 if (end_offset <= m_size) {
3643 uint32_t num_chars = LE2HE(*reinterpret_cast<uint32_t*>(m_data + m_offset));
3644 m_offset = end_offset;
3645 end_offset = stdex::add(m_offset, stdex::mul(num_chars, sizeof(T)));
3646 T* start = reinterpret_cast<T*>(m_data + m_offset);
3647 if (end_offset <= m_size) {
3648 data.assign(start, start + num_chars);
3649 m_offset = end_offset;
3650#if !CHECK_STREAM_STATE
3651 m_state = state_t::ok;
3652#endif
3653 return *this;
3654 }
3655 if (end_offset <= m_size)
3656 data.assign(start, reinterpret_cast<T*>(m_data + m_size));
3657 }
3658 m_offset = m_size;
3659 m_state = state_t::eof;
3660 return *this;
3661 }
3662
3663 virtual _Success_(return != 0) size_t write(
3664 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3665 {
3666 _Assume_(data || !length);
3667#if SET_FILE_OP_TIMES
3668 m_atime = m_mtime = time_point::now();
3669#endif
3670 size_t end_offset = m_offset + length;
3671 if (end_offset > m_reserved) {
3672 reserve(end_offset);
3673 if (!ok()) _Unlikely_
3674 return 0;
3675 }
3676 memcpy(m_data + m_offset, data, length);
3677 m_offset = end_offset;
3678 if (m_offset > m_size)
3679 m_size = m_offset;
3680 m_state = state_t::ok;
3681 return length;
3682 }
3683
3687 void write_byte(_In_ uint8_t byte, _In_ size_t amount = 1)
3688 {
3689#if SET_FILE_OP_TIMES
3690 m_atime = m_mtime = time_point::now();
3691#endif
3692 size_t end_offset = m_offset + amount;
3693 if (end_offset > m_reserved) {
3694 reserve(end_offset);
3695 if (!ok()) _Unlikely_
3696 return;
3697 }
3698 memset(m_data + m_offset, byte, amount);
3699 m_offset = end_offset;
3700 if (m_offset > m_size)
3701 m_size = m_offset;
3702 m_state = state_t::ok;
3703 }
3704
3719 template <class T>
3721 {
3722#if SET_FILE_OP_TIMES
3723 m_atime = m_mtime = time_point::now();
3724#endif
3725 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3726 return *this;
3727 size_t end_offset = m_offset + sizeof(T);
3728 if (end_offset > m_reserved) {
3729 reserve(end_offset);
3730 if (!ok()) _Unlikely_
3731 return *this;
3732 }
3733 (*reinterpret_cast<T*>(m_data + m_offset)) = HE2LE(data);
3734 m_offset = end_offset;
3735 if (m_offset > m_size)
3736 m_size = m_offset;
3737#if !CHECK_STREAM_STATE
3738 m_state = state_t::ok;
3739#endif
3740 return *this;
3741 }
3742
3757 template <class T>
3758 memory_file& write_str(_In_z_ const T * data)
3759 {
3760#if SET_FILE_OP_TIMES
3761 m_atime = m_mtime = time_point::now();
3762#endif
3763 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3764 return *this;
3765 size_t num_chars = stdex::strlen(data);
3766 if (num_chars > UINT32_MAX)
3767 throw std::invalid_argument("string too long");
3768 size_t size_chars = num_chars * sizeof(T);
3769 size_t size = sizeof(uint32_t) + size_chars;
3770 size_t end_offset = m_offset + size;
3771 if (end_offset > m_reserved) {
3772 reserve(end_offset);
3773 if (!ok()) _Unlikely_
3774 return *this;
3775 }
3776 auto p = m_data + m_offset;
3777 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3778 memcpy(p + sizeof(uint32_t), data, size_chars);
3779 m_offset = end_offset;
3780 if (m_offset > m_size)
3781 m_size = m_offset;
3782#if !CHECK_STREAM_STATE
3783 m_state = state_t::ok;
3784#endif
3785 return *this;
3786 }
3787
3802 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3803 memory_file& write_str(_In_ const std::basic_string<T, TR, AX>& data)
3804 {
3805#if SET_FILE_OP_TIMES
3806 m_atime = m_mtime = time_point::now();
3807#endif
3808 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3809 return *this;
3810 size_t num_chars = data.size();
3811 if (num_chars > UINT32_MAX)
3812 throw std::invalid_argument("string too long");
3813 size_t size_chars = num_chars * sizeof(T);
3814 size_t size = sizeof(uint32_t) + size_chars;
3815 size_t end_offset = m_offset + size;
3816 if (end_offset > m_reserved) {
3817 reserve(end_offset);
3818 if (!ok()) _Unlikely_
3819 return *this;
3820 }
3821 auto p = m_data + m_offset;
3822 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3823 memcpy(p + sizeof(uint32_t), data.data(), size_chars);
3824 m_offset = end_offset;
3825 if (m_offset > m_size)
3826 m_size = m_offset;
3827#if !CHECK_STREAM_STATE
3828 m_state = state_t::ok;
3829#endif
3830 return *this;
3831 }
3832
3838 size_t write_stream(_Inout_ basic & stream, _In_ size_t amount = SIZE_MAX)
3839 {
3840#if SET_FILE_OP_TIMES
3841 m_atime = m_mtime = time_point::now();
3842#endif
3843 size_t num_read, dst_offset = m_offset, dst_size = m_offset;
3844 size_t num_copied = 0, to_write = amount;
3845 m_state = state_t::ok;
3846 if (amount != SIZE_MAX) {
3847 dst_size = stdex::add(dst_size, amount);
3848 reserve(dst_size);
3849 if (!ok()) _Unlikely_
3850 return 0;
3851 while (to_write) {
3852 num_read = stream.read(m_data + dst_offset, to_write);
3853 /*dst_size =*/ dst_offset += num_read;
3854 num_copied += num_read;
3855 to_write -= num_read;
3856 if (!stream.ok()) {
3857 if (stream.state() != state_t::eof)
3858 m_state = state_t::fail;
3859 break;
3860 }
3861 };
3862 }
3863 else {
3864 size_t block_size;
3865 while (to_write) {
3866 block_size = std::min(to_write, default_block_size);
3867 dst_size = stdex::add(dst_size, block_size);
3868 reserve(dst_size);
3869 if (!ok()) _Unlikely_
3870 break;
3871 num_read = stream.read(m_data + dst_offset, block_size);
3872 dst_size = dst_offset += num_read;
3873 num_copied += num_read;
3874 to_write -= num_read;
3875 if (!stream.ok()) {
3876 if (stream.state() != state_t::eof)
3877 m_state = state_t::fail;
3878 break;
3879 }
3880 };
3881 }
3882 m_offset = dst_offset;
3883 if (m_offset > m_size)
3884 m_size = m_offset;
3885 return num_copied;
3886 }
3887
3888 virtual void close()
3889 {
3890 if (m_manage && m_data)
3891 free(m_data);
3892 m_data = nullptr;
3893 m_manage = true;
3894 m_offset = 0;
3895 m_size = m_reserved = 0;
3896#if SET_FILE_OP_TIMES
3897 m_ctime = m_atime = m_mtime = time_point::min();
3898#endif
3899 m_state = state_t::ok;
3900 }
3901
3902 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
3903 {
3904 switch (how) {
3905 case seek_t::beg: break;
3906 case seek_t::cur: offset = static_cast<foff_t>(m_offset) + offset; break;
3907 case seek_t::end: offset = static_cast<foff_t>(m_size) + offset; break;
3908 default: throw std::invalid_argument("unknown seek origin");
3909 }
3910 if (offset < 0) _Unlikely_
3911 throw std::invalid_argument("negative file offset");
3912 if (static_cast<fpos_t>(offset) > SIZE_MAX) _Unlikely_
3913 throw std::invalid_argument("file offset too big");
3914 m_state = state_t::ok;
3915 return m_offset = static_cast<size_t>(offset);
3916 }
3917
3918 virtual fpos_t tell() const
3919 {
3920 return m_offset;
3921 }
3922
3923 virtual fsize_t size() const
3924 {
3925 return m_size;
3926 }
3927
3928 virtual void truncate()
3929 {
3930#if SET_FILE_OP_TIMES
3931 m_atime = m_mtime = time_point::now();
3932#endif
3933 m_size = m_offset;
3935 }
3936
3937#if SET_FILE_OP_TIMES
3938 virtual time_point ctime() const
3939 {
3940 return m_ctime;
3941 }
3942
3943 virtual time_point atime() const
3944 {
3945 return m_atime;
3946 }
3947
3948 virtual time_point mtime() const
3949 {
3950 return m_mtime;
3951 }
3952
3953 virtual void set_ctime(time_point date)
3954 {
3955 m_ctime = date;
3956 }
3957
3958 virtual void set_atime(time_point date)
3959 {
3960 m_atime = date;
3961 }
3962
3963 virtual void set_mtime(time_point date)
3964 {
3965 m_mtime = date;
3966 }
3967#endif
3968
3969 protected:
3977 template <class T>
3978 void set(_In_ fpos_t offset, _In_ const T data)
3979 {
3980#if SET_FILE_OP_TIMES
3981 m_atime = m_mtime = time_point::now();
3982#endif
3983 _Assume_(offset + sizeof(T) < m_size);
3984 (*reinterpret_cast<T*>(m_data + offset)) = HE2LE(data);
3985 }
3986
3987 public:
3988 void set(_In_ fpos_t offset, _In_ const int8_t data) { set<int8_t>(offset, data); }
3989 void set(_In_ fpos_t offset, _In_ const int16_t data) { set<int16_t>(offset, data); }
3990 void set(_In_ fpos_t offset, _In_ const int32_t data) { set<int32_t>(offset, data); }
3991 void set(_In_ fpos_t offset, _In_ const int64_t data) { set<int64_t>(offset, data); }
3992 void set(_In_ fpos_t offset, _In_ const uint8_t data) { set<uint8_t>(offset, data); }
3993 void set(_In_ fpos_t offset, _In_ const uint16_t data) { set<uint16_t>(offset, data); }
3994 void set(_In_ fpos_t offset, _In_ const uint32_t data) { set<uint32_t>(offset, data); }
3995 void set(_In_ fpos_t offset, _In_ const uint64_t data) { set<uint64_t>(offset, data); }
3996 void set(_In_ fpos_t offset, _In_ const float data) { set<float>(offset, data); }
3997 void set(_In_ fpos_t offset, _In_ const double data) { set<double>(offset, data); }
3998 void set(_In_ fpos_t offset, _In_ const char data) { set<char>(offset, data); }
3999#ifdef _NATIVE_WCHAR_T_DEFINED
4000 void set(_In_ fpos_t offset, _In_ const wchar_t data) { set<wchar_t>(offset, data); }
4001#endif
4002
4010 protected:
4011 template <class T>
4012 void get(_In_ fpos_t offset, _Out_ T & data)
4013 {
4014 _Assume_(offset + sizeof(T) < m_size);
4015 data = LE2HE(*(T*)(m_data + offset));
4016#if SET_FILE_OP_TIMES
4017 m_atime = time_point::now();
4018#endif
4019 }
4020
4021 public:
4022 void get(_In_ fpos_t offset, _Out_ int8_t & data) { get<int8_t>(offset, data); }
4023 void get(_In_ fpos_t offset, _Out_ int16_t & data) { get<int16_t>(offset, data); }
4024 void get(_In_ fpos_t offset, _Out_ int32_t & data) { get<int32_t>(offset, data); }
4025 void get(_In_ fpos_t offset, _Out_ int64_t & data) { get<int64_t>(offset, data); }
4026 void get(_In_ fpos_t offset, _Out_ uint8_t & data) { get<uint8_t>(offset, data); }
4027 void get(_In_ fpos_t offset, _Out_ uint16_t & data) { get<uint16_t>(offset, data); }
4028 void get(_In_ fpos_t offset, _Out_ uint32_t & data) { get<uint32_t>(offset, data); }
4029 void get(_In_ fpos_t offset, _Out_ uint64_t & data) { get<uint64_t>(offset, data); }
4030 void get(_In_ fpos_t offset, _Out_ float& data) { get<float>(offset, data); }
4031 void get(_In_ fpos_t offset, _Out_ double& data) { get<double>(offset, data); }
4032 void get(_In_ fpos_t offset, _Out_ char& data) { get<char>(offset, data); }
4033#ifdef _NATIVE_WCHAR_T_DEFINED
4034 void get(_In_ fpos_t offset, _Out_ wchar_t& data) { get<wchar_t>(offset, data); }
4035#endif
4036
4037 memory_file& operator <<(_In_ const int8_t data) { return write_data(data); }
4038 memory_file& operator >>(_Out_ int8_t & data) { return read_data(data); }
4039 memory_file& operator <<(_In_ const int16_t data) { return write_data(data); }
4040 memory_file& operator >>(_Out_ int16_t & data) { return read_data(data); }
4041 memory_file& operator <<(_In_ const int32_t data) { return write_data(data); }
4042 memory_file& operator >>(_Out_ int32_t & data) { return read_data(data); }
4043 memory_file& operator <<(_In_ const int64_t data) { return write_data(data); }
4044 memory_file& operator >>(_Out_ int64_t & data) { return read_data(data); }
4045 memory_file& operator <<(_In_ const uint8_t data) { return write_data(data); }
4046 memory_file& operator >>(_Out_ uint8_t & data) { return read_data(data); }
4047 memory_file& operator <<(_In_ const uint16_t data) { return write_data(data); }
4048 memory_file& operator >>(_Out_ uint16_t & data) { return read_data(data); }
4049 memory_file& operator <<(_In_ const uint32_t data) { return write_data(data); }
4050 memory_file& operator >>(_Out_ uint32_t & data) { return read_data(data); }
4051 memory_file& operator <<(_In_ const uint64_t data) { return write_data(data); }
4052 memory_file& operator >>(_Out_ uint64_t & data) { return read_data(data); }
4053 memory_file& operator <<(_In_ const float data) { return write_data(data); }
4054 memory_file& operator >>(_Out_ float& data) { return read_data(data); }
4055 memory_file& operator <<(_In_ const double data) { return write_data(data); }
4056 memory_file& operator >>(_Out_ double& data) { return read_data(data); }
4057 memory_file& operator <<(_In_ const char data) { return write_data(data); }
4058 memory_file& operator >>(_Out_ char& data) { return read_data(data); }
4059#ifdef _NATIVE_WCHAR_T_DEFINED
4060 memory_file& operator <<(_In_ const wchar_t data) { return write_data(data); }
4061 memory_file& operator >>(_Out_ wchar_t& data) { return read_data(data); }
4062#endif
4063 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
4064 memory_file& operator >>(_Out_ std::basic_string<T, TR, AX>&data) { return read_str(data); }
4065 template <class T>
4066 memory_file& operator <<(_In_ const T * data) { return write_str(data); }
4067 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
4068 memory_file& operator <<(_In_ const std::basic_string<T, TR, AX>& data) { return write_str(data); }
4069
4070 protected:
4071 uint8_t* m_data;
4073 size_t m_offset;
4074 size_t m_size;
4075 size_t m_reserved;
4076#if SET_FILE_OP_TIMES
4077 time_point
4078 m_ctime,
4079 m_atime,
4080 m_mtime;
4081#endif
4082 };
4083
4087 class fifo : public basic {
4088 public:
4089 fifo() :
4090 m_offset(0),
4091 m_size(0),
4092 m_head(nullptr),
4093 m_tail(nullptr)
4094 {}
4095
4096 virtual ~fifo()
4097 {
4098 while (m_head) {
4099 auto p = m_head;
4100 m_head = p->next;
4101 delete p;
4102 }
4103 }
4104
4105#pragma warning(suppress: 6101) // See [2] below
4106 virtual _Success_(return != 0 || length == 0) size_t read(
4107 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4108 {
4109 _Assume_(data || !length);
4110 for (size_t to_read = length;;) {
4111 if (!m_head) _Unlikely_ {
4112 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
4113 return length - to_read; // [2] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
4114 }
4115 size_t remaining = m_head->size - m_offset;
4116 if (remaining > to_read) {
4117 memcpy(data, m_head->data + m_offset, to_read);
4118 m_offset += to_read;
4119 m_size -= to_read;
4120 m_state = state_t::ok;
4121 return length;
4122 }
4123 memcpy(data, m_head->data + m_offset, remaining);
4124 m_offset = 0;
4125 m_size -= remaining;
4126 reinterpret_cast<uint8_t*&>(data) += remaining;
4127 to_read -= remaining;
4128 auto p = m_head;
4129 m_head = p->next;
4130 delete p;
4131 }
4132 }
4133
4134 virtual _Success_(return != 0) size_t write(
4135 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4136 {
4137 _Assume_(data || !length);
4138 try {
4139 std::unique_ptr<node_t> n(reinterpret_cast<node_t*>(new uint8_t[sizeof(node_t) + length]));
4140 n->next = nullptr;
4141 n->size = length;
4142 memcpy(n->data, data, length);
4143 m_size += length;
4144 if (m_head)
4145 m_tail = m_tail->next = n.release();
4146 else
4147 m_head = m_tail = n.release();
4148 m_state = state_t::ok;
4149 return length;
4150 }
4151 catch (const std::bad_alloc&) {
4152 m_state = state_t::fail;
4153 return 0;
4154 }
4155 }
4156
4157 virtual void close()
4158 {
4159 m_size = m_offset = 0;
4160 while (m_head) {
4161 auto p = m_head;
4162 m_head = p->next;
4163 delete p;
4164 }
4165 m_state = state_t::ok;
4166 }
4167
4171 size_t size() const { return m_size; };
4172
4173 protected:
4174 size_t m_offset, m_size;
4175 struct node_t {
4176 node_t* next;
4177 size_t size;
4178#pragma warning(suppress:4200)
4179 uint8_t data[0];
4180 } *m_head, * m_tail;
4181 };
4182
4186 class diag_file : public basic_file {
4187 public:
4188 diag_file(_In_count_(num_files) basic_file* const* files, _In_ size_t num_files) :
4189 basic(num_files ? files[0]->state() : state_t::fail),
4190 m_files(files, files + num_files)
4191 {}
4192
4193 virtual _Success_(return != 0 || length == 0) size_t read(
4194 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4195 {
4196 _Assume_(data || !length);
4197 if (m_files.empty()) {
4198 m_state = state_t::fail;
4199 return 0;
4200 }
4201 size_t result = m_files[0]->read(data, length);
4202 _Assume_(result <= length);
4203 m_state = m_files[0]->state();
4204 if (length > m_tmp.size())
4205 m_tmp.resize(length);
4206 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4207 if (m_files[i]->read(m_tmp.data(), length) != result ||
4208 memcmp(m_tmp.data(), data, result))
4209 throw std::runtime_error("read mismatch");
4210 if (m_files[i]->state() != m_state)
4211 throw std::runtime_error("state mismatch");
4212 }
4213 return result;
4214 }
4215
4216 virtual _Success_(return != 0) size_t write(
4217 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4218 {
4219 if (m_files.empty()) {
4220 m_state = state_t::fail;
4221 return 0;
4222 }
4223 size_t result = m_files[0]->write(data, length);
4224 m_state = m_files[0]->state();
4225 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4226 if (m_files[i]->write(data, length) != result)
4227 throw std::runtime_error("write mismatch");
4228 if (m_files[i]->state() != m_state)
4229 throw std::runtime_error("state mismatch");
4230 }
4231 return result;
4232 }
4233
4234 virtual void flush()
4235 {
4236 if (m_files.empty()) {
4237 m_state = state_t::ok;
4238 return;
4239 }
4240 m_files[0]->flush();
4241 m_state = m_files[0]->state();
4242 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4243 m_files[i]->flush();
4244 if (m_files[i]->state() != m_state)
4245 throw std::runtime_error("state mismatch");
4246 }
4247 }
4248
4249 virtual void close()
4250 {
4251 if (m_files.empty()) {
4252 m_state = state_t::ok;
4253 return;
4254 }
4255 m_files[0]->close();
4256 m_state = m_files[0]->state();
4257 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4258 m_files[i]->close();
4259 if (m_files[i]->state() != m_state)
4260 throw std::runtime_error("state mismatch");
4261 }
4262 m_tmp.clear();
4263 m_tmp.shrink_to_fit();
4264 }
4265
4266 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
4267 {
4268 if (m_files.empty()) {
4269 m_state = state_t::fail;
4270 return fpos_max;
4271 }
4272 fpos_t result = m_files[0]->seek(offset, how);
4273 m_state = m_files[0]->state();
4274 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4275 if (m_files[i]->seek(offset, how) != result)
4276 throw std::runtime_error("seek mismatch");
4277 if (m_files[i]->state() != m_state)
4278 throw std::runtime_error("state mismatch");
4279 }
4280 return result;
4281 }
4282
4283 virtual fpos_t tell() const
4284 {
4285 if (m_files.empty())
4286 return fpos_max;
4287 fpos_t result = m_files[0]->tell();
4288 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4289 if (m_files[i]->tell() != result)
4290 throw std::runtime_error("tell mismatch");
4291 }
4292 return result;
4293 }
4294
4295 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
4296 {
4297 if (m_files.empty())
4298 m_state = state_t::fail;
4299 m_files[0]->lock(offset, length);
4300 m_state = m_files[0]->state();
4301 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4302 m_files[i]->lock(offset, length);
4303 if (m_files[i]->state() != m_state)
4304 throw std::runtime_error("state mismatch");
4305 }
4306 }
4307
4308 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
4309 {
4310 if (m_files.empty())
4311 m_state = state_t::fail;
4312 m_files[0]->unlock(offset, length);
4313 m_state = m_files[0]->state();
4314 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4315 m_files[i]->unlock(offset, length);
4316 if (m_files[i]->state() != m_state)
4317 throw std::runtime_error("state mismatch");
4318 }
4319 }
4320
4321 virtual fsize_t size() const
4322 {
4323 if (m_files.empty())
4324 return fsize_max;
4325 fsize_t result = m_files[0]->size();
4326 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4327 if (m_files[i]->size() != result)
4328 throw std::runtime_error("size mismatch");
4329 }
4330 return result;
4331 }
4332
4333 virtual void truncate()
4334 {
4335 if (m_files.empty())
4336 m_state = state_t::fail;
4337 m_files[0]->truncate();
4338 m_state = m_files[0]->state();
4339 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4340 m_files[i]->truncate();
4341 if (m_files[i]->state() != m_state)
4342 throw std::runtime_error("state mismatch");
4343 }
4344 }
4345
4346 protected:
4347 std::vector<basic_file*> m_files;
4348 std::vector<uint8_t> m_tmp;
4349 };
4350 }
4351}
4352
4353#if defined(__GNUC__)
4354#pragma GCC diagnostic pop
4355#endif
Encoding converter context.
Definition unicode.hpp:138
locale_t helper class to free_locale when going out of scope.
Definition locale.hpp:74
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:3137
void open(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:3203
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:3181
cached_file(const schar_t *filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3153
cached_file(const std::basic_string< TR, AX > &filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3168
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:4186
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:4333
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:4216
virtual void close()
Closes the stream.
Definition stream.hpp:4249
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:4321
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:4295
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:4308
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:4266
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:4283
virtual void flush()
Persists volatile element data.
Definition stream.hpp:4234
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:4193
In-memory FIFO queue.
Definition stream.hpp:4087
virtual void close()
Closes the stream.
Definition stream.hpp:4157
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:4134
size_t size() const
Returns total size of pending data in the queue.
Definition stream.hpp:4171
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:4106
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:3126
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:3107
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:3095
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:3055
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:3035
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:3079
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:3221
void save(const std::basic_string< TR, AX > &filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3543
memory_file & operator=(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3404
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:3923
memory_file(const schar_t *filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3301
memory_file & read_str(std::basic_string< T, TR, AX > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:3632
memory_file(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3320
memory_file & write_str(const std::basic_string< T, TR, AX > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3803
void load(const std::basic_string< TR, AX > &filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3505
size_t m_size
file size
Definition stream.hpp:4074
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:4012
size_t write_stream(basic &stream, size_t amount=SIZE_MAX)
Writes content of another stream.
Definition stream.hpp:3838
uint8_t * m_data
file data
Definition stream.hpp:4071
memory_file & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:3592
virtual void close()
Closes the stream.
Definition stream.hpp:3888
memory_file(const std::basic_string< TR, AX > &filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3313
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:3553
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:3918
size_t m_reserved
reserved file size
Definition stream.hpp:4075
memory_file(size_t size, state_t state=state_t::ok)
Creates an empty file of reserved size.
Definition stream.hpp:3242
void reserve(size_t required, bool tight=false) noexcept
Reallocates memory.
Definition stream.hpp:3442
memory_file(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3375
void write_byte(uint8_t byte, size_t amount=1)
Writes a byte of data.
Definition stream.hpp:3687
memory_file & operator=(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3345
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:3978
size_t m_offset
file pointer
Definition stream.hpp:4073
void save(const schar_t *filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3516
void load(const schar_t *filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3471
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3902
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:3928
memory_file & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:3720
memory_file & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3758
bool m_manage
may reallocate m_data?
Definition stream.hpp:4072
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:3291
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:3663
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:3268
const void * data() const
Returns pointer to data.
Definition stream.hpp:3551
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:4175