stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
stream.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2023 Amebis
4*/
5
6#pragma once
7
8#include "compat.hpp"
9#include "endian.hpp"
10#include "interval.hpp"
11#include "math.hpp"
12#include "ring.hpp"
13#include "string.hpp"
14#include "system.hpp"
15#include "unicode.hpp"
16#include <assert.h>
17#include <stdint.h>
18#include <stdlib.h>
19#if defined(_WIN32)
20#include <asptlb.h>
21#include <objidl.h>
22#else
23#include <fcntl.h>
24#include <unistd.h>
25#include <sys/stat.h>
26#endif
27#include <chrono>
28#include <condition_variable>
29#include <list>
30#include <memory>
31#include <string>
32#include <thread>
33#include <vector>
34
35#if !defined(SET_FILE_OP_TIMES) && defined(RDAT_BELEZI_CAS_DOSTOPA_VER)
36#define SET_FILE_OP_TIMES 1
37#pragma message("RDAT_BELEZI_CAS_DOSTOPA_VER is deprecated. Use SET_FILE_OP_TIMES instead.")
38#elif !defined(SET_FILE_OP_TIMES)
39#define SET_FILE_OP_TIMES 0
40#endif
41#if !defined(CHECK_STREAM_STATE) && defined(RDAT_NE_PREVERJAJ_STANJA_VER)
42#define CHECK_STREAM_STATE 0
43#pragma message("RDAT_NE_PREVERJAJ_EOF_VER is deprecated. Use CHECK_STREAM_STATE=0 instead.")
44#else
45#define CHECK_STREAM_STATE 1
46#endif
47
48namespace stdex
49{
50 namespace stream
51 {
55 enum class state_t {
56 ok = 0,
57 eof,
58 fail,
59 };
60
64 using fsize_t = uint64_t;
65 constexpr fsize_t fsize_max = UINT64_MAX;
66
67 constexpr size_t iterate_count = 0x10;
68 constexpr size_t default_block_size = 0x10000;
69 constexpr wchar_t utf16_bom = L'\ufeff';
70 constexpr const char utf8_bom[3] = { '\xef', '\xbb', '\xbf' };
71
75 class basic
76 {
77 public:
78 basic(_In_ state_t state = state_t::ok) : m_state(state) {}
79
91 virtual _Success_(return != 0 || length == 0) size_t read(
92 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
93 {
94 _Unreferenced_(data);
95 _Unreferenced_(length);
96 m_state = state_t::fail;
97 return 0;
98 }
99
109 virtual _Success_(return != 0) size_t write(
110 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
111 {
112 _Unreferenced_(data);
113 _Unreferenced_(length);
114 m_state = state_t::fail;
115 return 0;
116 }
117
121 virtual void flush()
122 {
123 m_state = state_t::ok;
124 }
125
129 virtual void close()
130 {
131 m_state = state_t::ok;
132 }
133
137 virtual void skip(_In_ fsize_t amount)
138 {
139 if (amount == 1)
140 read_byte();
141 else if (amount < iterate_count) {
142 for (size_t i = 0; i < static_cast<size_t>(amount); i++) {
143 read_byte();
144 if (!ok()) _Unlikely_
145 break;
146 }
147 }
148 else {
149 size_t block = static_cast<size_t>(std::min<fsize_t>(amount, default_block_size));
150 try {
151 std::unique_ptr<uint8_t[]> dummy(new uint8_t[block]);
152 while (amount) {
153 amount -= read_array(dummy.get(), sizeof(uint8_t), static_cast<size_t>(std::min<fsize_t>(amount, block)));
154 if (!ok()) _Unlikely_
155 break;
156 }
157 }
158 catch (const std::bad_alloc&) { m_state = state_t::fail; }
159 }
160 }
161
165 inline state_t state() const { return m_state; };
166
170 inline bool ok() const { return m_state == state_t::ok; };
171
179 virtual std::vector<uint8_t> read_remainder(_In_ size_t max_length = SIZE_MAX)
180 {
181 std::vector<uint8_t> result;
182 size_t offset, length;
183 offset = 0;
184 length = default_block_size;
185 while (offset < max_length) {
186 length = std::min(length, max_length);
187 try { result.resize(length); }
188 catch (const std::bad_alloc&) {
189 m_state = state_t::fail;
190 return result;
191 }
192 auto num_read = read_array(result.data() + offset, sizeof(uint8_t), length - offset);
193 offset += num_read;
194 if (!ok()) _Unlikely_
195 break;
196 length += default_block_size;
197 }
198 result.resize(offset);
199 return result;
200 }
201
205 inline uint8_t read_byte()
206 {
207 uint8_t byte;
208 if (read_array(&byte, sizeof(byte), 1) == 1)
209 return byte;
210 throw std::runtime_error("failed to read");
211 }
212
216 void write_byte(_In_ uint8_t byte, _In_ fsize_t amount = 1)
217 {
218 if (amount == 1)
219 write(&byte, sizeof(uint8_t));
220 else if (amount < iterate_count) {
221 for (size_t i = 0; i < static_cast<size_t>(amount); i++) {
222 write(&byte, sizeof(uint8_t));
223 if (!ok()) _Unlikely_
224 break;
225 }
226 }
227 else {
228 size_t block = static_cast<size_t>(std::min<fsize_t>(amount, default_block_size));
229 try {
230 std::unique_ptr<uint8_t[]> dummy(new uint8_t[block]);
231 memset(dummy.get(), byte, block);
232 while (amount) {
233 amount -= write_array(dummy.get(), sizeof(uint8_t), static_cast<size_t>(std::min<fsize_t>(amount, block)));
234 if (!ok()) _Unlikely_
235 break;
236 }
237 }
238 catch (const std::bad_alloc&) { m_state = state_t::fail; }
239 }
240 }
241
253 template <class T>
254 inline basic& read_data(_Out_ T& data)
255 {
256 if (!ok()) _Unlikely_ {
257 data = 0;
258 return *this;
259 }
260 if (read_array(&data, sizeof(T), 1) == 1)
261 (void)LE2HE(&data);
262 else {
263 data = 0;
264 if (ok())
265 m_state = state_t::eof;
266 }
267 return *this;
268 }
269
281 template <class T>
282 inline basic& write_data(_In_ const T data)
283 {
284 if (!ok()) _Unlikely_
285 return *this;
286#if BYTE_ORDER == BIG_ENDIAN
287 T data_le = HE2LE(data);
288 write(&data_le, sizeof(T));
289#else
290 write(&data, sizeof(T));
291#endif
292 return *this;
293 }
294
300 template<class _Traits = std::char_traits<char>, class _Ax = std::allocator<char>>
301 inline size_t readln(_Inout_ std::basic_string<char, _Traits, _Ax>& str)
302 {
303 str.clear();
304 return readln_and_attach(str);
305 }
306
312 template<class _Traits = std::char_traits<wchar_t>, class _Ax = std::allocator<wchar_t>>
313 inline size_t readln(_Inout_ std::basic_string<wchar_t, _Traits, _Ax>& wstr)
314 {
315 wstr.clear();
316 return readln_and_attach(wstr);
317 }
318
324 template<class _Traits = std::char_traits<wchar_t>, class _Ax = std::allocator<wchar_t>>
325 size_t readln(_Inout_ std::basic_string<wchar_t, _Traits, _Ax>& wstr, _In_ charset_id charset)
326 {
327 if (charset == charset_id::utf16)
328 return readln(wstr);
329 std::string str;
331 wstr.clear();
332 str2wstr(wstr, str, charset);
333 return wstr.size();
334 }
335
341 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
342 size_t readln_and_attach(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
343 {
344 bool initial = true;
345 _Elem chr, previous = (_Elem)0;
346 do {
347 read_array(&chr, sizeof(_Elem), 1);
348 if (!initial && !(previous == static_cast<_Elem>('\r') && chr == static_cast<_Elem>('\n')))
349 str += previous;
350 else
351 initial = false;
352 previous = chr;
353 } while (ok() && chr != static_cast<_Elem>('\n'));
354 return str.size();
355 }
356
362 template<class _Traits = std::char_traits<wchar_t>, class _Ax = std::allocator<wchar_t>>
363 size_t readln_and_attach(_Inout_ std::basic_string<wchar_t, _Traits, _Ax>& wstr, _In_ charset_id charset)
364 {
365 if (charset == charset_id::utf16)
366 return readln_and_attach(wstr);
367 std::string str;
369 str2wstr(wstr, str, charset);
370 return wstr.size();
371 }
372
378 size_t read_array(_Out_writes_bytes_(size* count) void* array, _In_ size_t size, _In_ size_t count)
379 {
380 for (size_t to_read = mul(size, count);;) {
381 size_t num_read = read(array, to_read);
382 to_read -= num_read;
383 if (!to_read)
384 return count;
385 if (!ok()) _Unlikely_
386 return count - to_read / size;
387 reinterpret_cast<uint8_t*&>(array) += num_read;
388 }
389 }
390
396 inline size_t write_array(_In_reads_bytes_opt_(size* count) const void* array, _In_ size_t size, _In_ size_t count)
397 {
398 return write(array, mul(size, count)) / size;
399 }
400
409 size_t write_array(_In_z_ const wchar_t* wstr, _In_ charset_id charset)
410 {
411 if (!ok()) _Unlikely_
412 return 0;
413 size_t num_chars = stdex::strlen(wstr);
414 if (charset != charset_id::utf16) {
415 std::string str(wstr2str(wstr, num_chars, charset));
416 return write_array(str.data(), sizeof(char), str.size());
417 }
418 return write_array(wstr, sizeof(wchar_t), num_chars);
419 }
420
430 size_t write_array(_In_reads_or_z_opt_(num_chars) const wchar_t* wstr, _In_ size_t num_chars, _In_ charset_id charset)
431 {
432 if (!ok()) _Unlikely_
433 return 0;
434 num_chars = stdex::strnlen(wstr, num_chars);
435 if (charset != charset_id::utf16) {
436 std::string str(wstr2str(wstr, num_chars, charset));
437 return write_array(str.data(), sizeof(char), str.size());
438 }
439 return write_array(wstr, sizeof(wchar_t), num_chars);
440 }
441
450 template<class _Traits = std::char_traits<wchar_t>, class _Ax = std::allocator<wchar_t>>
451 size_t write_array(_In_ const std::basic_string<wchar_t, _Traits, _Ax>& wstr, _In_ charset_id charset)
452 {
453 if (!ok()) _Unlikely_
454 return 0;
455 if (charset != charset_id::utf16) {
456 std::string str(wstr2str(wstr, charset));
457 return write_array(str.data(), sizeof(char), str.size());
458 }
459 return write_array(wstr.data(), sizeof(wchar_t), wstr.size());
460 }
461
473 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
474 inline basic& read_str(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& data)
475 {
476 uint32_t num_chars;
477 read_data(num_chars);
478 if (!ok()) _Unlikely_ {
479 data.clear();
480 return *this;
481 }
482 data.resize(num_chars);
483 data.resize(read_array(data.data(), sizeof(_Elem), num_chars));
484 return *this;
485 }
486
498 template <class T>
499 inline basic& write_str(_In_z_ const T* data)
500 {
501 // Stream state will be checked in write_data.
502 size_t num_chars = stdex::strlen(data);
503 if (num_chars > UINT32_MAX)
504 throw std::invalid_argument("string too long");
505 write_data((uint32_t)num_chars);
506 if (!ok()) _Unlikely_
507 return *this;
508 write_array(data, sizeof(T), num_chars);
509 return *this;
510 }
511
512#ifdef _WIN32
518 size_t write_sa(_In_ LPSAFEARRAY sa)
519 {
520 safearray_accessor<void> a(sa);
521 long ubound, lbound;
522 if (FAILED(SafeArrayGetUBound(sa, 1, &ubound)) ||
523 FAILED(SafeArrayGetLBound(sa, 1, &lbound)))
524 throw std::invalid_argument("SafeArrayGet[UL]Bound failed");
525 return write(a.data(), static_cast<size_t>(ubound) - lbound + 1);
526 }
527#endif
528
534 fsize_t write_stream(_Inout_ basic& stream, _In_ fsize_t amount = fsize_max)
535 {
536 std::unique_ptr<uint8_t[]> data(new uint8_t[static_cast<size_t>(std::min<fsize_t>(amount, default_block_size))]);
537 fsize_t num_copied = 0, to_write = amount;
538 m_state = state_t::ok;
539 while (to_write) {
540 size_t num_read = stream.read(data.get(), static_cast<size_t>(std::min<fsize_t>(default_block_size, to_write)));
541 size_t num_written = write(data.get(), num_read);
542 num_copied += num_written;
543 to_write -= num_written;
544 if (stream.m_state == state_t::eof) {
545 // EOF is not an error.
546 m_state = state_t::ok;
547 break;
548 }
549 m_state = stream.m_state;
550 if (!ok())
551 break;
552 }
553 return num_copied;
554 }
555
559 void write_charset(_In_ charset_id charset)
560 {
561 if (charset == charset_id::utf16)
562 write_data(utf16_bom);
563 else if (charset == charset_id::utf8)
564 write_array(utf8_bom, sizeof(utf8_bom), 1);
565 }
566
572 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, ...)
573 {
574 va_list params;
575 va_start(params, locale);
576 size_t num_chars = write_vsprintf(format, locale, params);
577 va_end(params);
578 return num_chars;
579 }
580
586 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, ...)
587 {
588 va_list params;
589 va_start(params, locale);
590 size_t num_chars = write_vsprintf(format, locale, params);
591 va_end(params);
592 return num_chars;
593 }
594
600 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, _In_ va_list params)
601 {
602 std::string str;
603 str.reserve(default_block_size);
604 vappendf(str, format, locale, params);
605 return write_array(str.data(), sizeof(char), str.size());
606 }
607
613 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, _In_ va_list params)
614 {
615 std::wstring str;
616 str.reserve(default_block_size);
617 vappendf(str, format, locale, params);
618 return write_array(str.data(), sizeof(wchar_t), str.size());
619 }
620
621 inline basic& operator >>(_Out_ int8_t& data) { return read_data(data); }
622 inline basic& operator <<(_In_ const int8_t data) { return write_data(data); }
623 inline basic& operator >>(_Out_ int16_t& data) { return read_data(data); }
624 inline basic& operator <<(_In_ const int16_t data) { return write_data(data); }
625 inline basic& operator >>(_Out_ int32_t& data) { return read_data(data); }
626 inline basic& operator <<(_In_ const int32_t data) { return write_data(data); }
627 inline basic& operator >>(_Out_ int64_t& data) { return read_data(data); }
628 inline basic& operator <<(_In_ const int64_t data) { return write_data(data); }
629 inline basic& operator >>(_Out_ uint8_t& data) { return read_data(data); }
630 inline basic& operator <<(_In_ const uint8_t data) { return write_data(data); }
631 inline basic& operator >>(_Out_ uint16_t& data) { return read_data(data); }
632 inline basic& operator <<(_In_ const uint16_t data) { return write_data(data); }
633 inline basic& operator >>(_Out_ uint32_t& data) { return read_data(data); }
634 inline basic& operator <<(_In_ const uint32_t data) { return write_data(data); }
635 inline basic& operator >>(_Out_ uint64_t& data) { return read_data(data); }
636 inline basic& operator <<(_In_ const uint64_t data) { return write_data(data); }
637 inline basic& operator >>(_Out_ float& data) { return read_data(data); }
638 inline basic& operator <<(_In_ const float data) { return write_data(data); }
639 inline basic& operator >>(_Out_ double& data) { return read_data(data); }
640 inline basic& operator <<(_In_ const double data) { return write_data(data); }
641 inline basic& operator >>(_Out_ char& data) { return read_data(data); }
642 inline basic& operator <<(_In_ const char data) { return write_data(data); }
643#ifdef _NATIVE_WCHAR_T_DEFINED
644 inline basic& operator >>(_Out_ wchar_t& data) { return read_data(data); }
645 inline basic& operator <<(_In_ const wchar_t data) { return write_data(data); }
646#endif
647 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
648 inline basic& operator >>(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& data) { return read_str(data); }
649 template <class T>
650 inline basic& operator <<(_In_ const T* data) { return write_str(data); }
651
652 protected:
653 state_t m_state;
654 };
655
659 using fpos_t = uint64_t;
660 constexpr fpos_t fpos_max = UINT64_MAX;
661 constexpr fpos_t fpos_min = 0;
662
666 using foff_t = int64_t;
667 constexpr foff_t foff_max = INT64_MAX;
668 constexpr foff_t foff_min = INT64_MIN;
669
673 enum class seek_t {
674#ifdef _WIN32
675 beg = FILE_BEGIN,
676 cur = FILE_CURRENT,
677 end = FILE_END
678#else
679 beg = SEEK_SET,
680 cur = SEEK_CUR,
681 end = SEEK_END
682#endif
683 };
684
685#if _HAS_CXX20
686 using clock = std::chrono::file_clock;
687#else
688 using clock = std::chrono::system_clock;
689#endif
690 using time_point = std::chrono::time_point<clock>;
691
695 class basic_file : virtual public basic
696 {
697 public:
698 virtual std::vector<uint8_t> read_remainder(_In_ size_t max_length = SIZE_MAX)
699 {
700 size_t length = std::min<size_t>(max_length, static_cast<size_t>(size() - tell()));
701 std::vector<uint8_t> result;
702 try { result.resize(length); }
703 catch (const std::bad_alloc&) {
704 m_state = state_t::fail;
705 return result;
706 }
707 result.resize(read_array(result.data(), sizeof(uint8_t), length));
708 return result;
709 }
710
716 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg) = 0;
717
723 inline fpos_t seekbeg(_In_ fpos_t offset) { return seek(offset, seek_t::beg); }
724
730 inline fpos_t seekcur(_In_ foff_t offset) { return seek(offset, seek_t::cur); }
731
737 inline fpos_t seekend(_In_ foff_t offset) { return seek(offset, seek_t::end); }
738
739 virtual void skip(_In_ fsize_t amount)
740 {
741 seek(amount, seek_t::cur);
742 }
743
750 virtual fpos_t tell() const = 0;
751
755 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
756 {
757 _Unreferenced_(offset);
758 _Unreferenced_(length);
759 throw std::domain_error("not implemented");
760 }
761
765 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
766 {
767 _Unreferenced_(offset);
768 _Unreferenced_(length);
769 throw std::domain_error("not implemented");
770 }
771
776 virtual fsize_t size() = 0;
777
781 virtual void truncate() = 0;
782
786 virtual time_point ctime() const
787 {
788 return time_point::min();
789 }
790
794 virtual time_point atime() const
795 {
796 return time_point::min();
797 }
798
802 virtual time_point mtime() const
803 {
804 return time_point::min();
805 }
806
810 virtual void set_ctime(time_point date)
811 {
812 _Unreferenced_(date);
813 throw std::domain_error("not implemented");
814 }
815
819 virtual void set_atime(time_point date)
820 {
821 _Unreferenced_(date);
822 throw std::domain_error("not implemented");
823 }
824
828 virtual void set_mtime(time_point date)
829 {
830 _Unreferenced_(date);
831 throw std::domain_error("not implemented");
832 }
833
834#ifdef _WIN32
838 LPSAFEARRAY read_sa()
839 {
840 assert(size() <= SIZE_MAX);
841 size_t length = static_cast<size_t>(size());
842 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(SafeArrayCreateVector(VT_UI1, 0, (ULONG)length));
843 if (!sa)
844 throw std::runtime_error("SafeArrayCreateVector failed");
845 safearray_accessor<void> a(sa.get());
846 if (seek(0) != 0)
847 throw std::runtime_error("failed to seek");
848 if (read_array(a.data(), 1, length) != length)
849 throw std::runtime_error("failed to read");
850 return sa.release();
851 }
852#endif
853
859 charset_id read_charset(_In_ charset_id default_charset = charset_id::system)
860 {
861 if (seek(0) != 0)
862 throw std::runtime_error("failed to seek");
863 wchar_t id_utf16;
864 read_array(&id_utf16, sizeof(wchar_t), 1);
865 if (!ok()) _Unlikely_
866 return default_charset;
867 if (id_utf16 == utf16_bom)
868 return charset_id::utf16;
869
870 if (seek(0) != 0)
871 throw std::runtime_error("failed to seek");
872 char id_utf8[3] = { 0 };
873 read_array(id_utf8, sizeof(id_utf8), 1);
874 if (!ok()) _Unlikely_
875 return default_charset;
876 if (strncmp(id_utf8, _countof(id_utf8), utf8_bom, _countof(utf8_bom)) == 0)
877 return charset_id::utf8;
878
879 if (seek(0) != 0)
880 throw std::runtime_error("failed to seek");
881 return default_charset;
882 }
883 };
884
888 class converter : public basic
889 {
890 protected:
891 explicit converter() :
892 basic(state_t::fail),
893 m_source(nullptr)
894 {}
895
896 void init(_Inout_ basic& source)
897 {
898 m_state = source.state();
899 m_source = &source;
900 }
901
902 void done()
903 {
904 m_source = nullptr;
905 }
906
907 public:
908 converter(_Inout_ basic& source) :
909 basic(source.state()),
910 m_source(&source)
911 {}
912
913 virtual _Success_(return != 0 || length == 0) size_t read(
914 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
915 {
916 size_t num_read = m_source->read(data, length);
917 m_state = m_source->state();
918 return num_read;
919 }
920
921 virtual _Success_(return != 0) size_t write(
922 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
923 {
924 size_t num_written = m_source->write(data, length);
925 m_state = m_source->state();
926 return num_written;
927 }
928
929 virtual void close()
930 {
931 m_source->close();
932 m_state = m_source->state();
933 }
934
935 virtual void flush()
936 {
937 m_source->flush();
938 m_state = m_source->state();
939 }
940
941 protected:
942 basic* m_source;
943 };
944
948 class replicator : public basic
949 {
950 public:
951 virtual ~replicator()
952 {
953 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
954 auto _w = w->get();
955 {
956 const std::lock_guard<std::mutex> lk(_w->mutex);
957 _w->op = worker::op_t::quit;
958 }
959 _w->cv.notify_one();
960 }
961 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w)
962 w->get()->thread.join();
963 }
964
968 void push_back(_In_ basic* source)
969 {
970 m_workers.push_back(std::unique_ptr<worker>(new worker(source)));
971 }
972
976 void remove(basic* source)
977 {
978 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
979 auto _w = w->get();
980 if (_w->source == source) {
981 {
982 const std::lock_guard<std::mutex> lk(_w->mutex);
983 _w->op = worker::op_t::quit;
984 }
985 _w->cv.notify_one();
986 _w->thread.join();
987 m_workers.erase(w);
988 return;
989 }
990 }
991 }
992
993 virtual _Success_(return != 0) size_t write(
994 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
995 {
996 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
997 auto _w = w->get();
998 {
999 const std::lock_guard<std::mutex> lk(_w->mutex);
1000 _w->op = worker::op_t::write;
1001 _w->data = data;
1002 _w->length = length;
1003 }
1004 _w->cv.notify_one();
1005 }
1006 size_t num_written = length;
1007 m_state = state_t::ok;
1008 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1009 auto _w = w->get();
1010 std::unique_lock<std::mutex> lk(_w->mutex);
1011 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1012 if (_w->num_written < num_written)
1013 num_written = _w->num_written;
1014 if (ok() && !_w->source->ok())
1015 m_state = _w->source->state();
1016 }
1017 return num_written;
1018 }
1019
1020 virtual void close()
1021 {
1022 foreach_worker(worker::op_t::close);
1023 }
1024
1025 virtual void flush()
1026 {
1027 foreach_worker(worker::op_t::flush);
1028 }
1029
1030 protected:
1032 {
1033 public:
1034 worker(_In_ basic* _source) :
1035 source(_source),
1036 op(op_t::noop),
1037 data(nullptr),
1038 length(0),
1039 num_written(0),
1040 thread(process_op, std::ref(*this))
1041 {}
1042
1043 protected:
1044 static void process_op(_Inout_ worker& w)
1045 {
1046 for (;;) {
1047 std::unique_lock<std::mutex> lk(w.mutex);
1048 w.cv.wait(lk, [&] {return w.op != op_t::noop; });
1049 switch (w.op) {
1050 case op_t::quit:
1051 return;
1052 case op_t::write:
1053 w.num_written = w.source->write(w.data, w.length);
1054 break;
1055 case op_t::close:
1056 w.source->close();
1057 break;
1058 case op_t::flush:
1059 w.source->flush();
1060 break;
1061 case op_t::noop:;
1062 }
1063 w.op = op_t::noop;
1064 lk.unlock();
1065 w.cv.notify_one();
1066 }
1067 }
1068
1069 public:
1070 basic* source;
1071 enum class op_t {
1072 noop = 0,
1073 quit,
1074 write,
1075 close,
1076 flush,
1077 } op;
1078 const void* data;
1079 size_t length;
1081 std::mutex mutex;
1082 std::condition_variable cv;
1083 std::thread thread;
1084 };
1085
1086 void foreach_worker(_In_ worker::op_t op)
1087 {
1088 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1089 auto _w = w->get();
1090 {
1091 const std::lock_guard<std::mutex> lk(_w->mutex);
1092 _w->op = op;
1093 }
1094 _w->cv.notify_one();
1095 }
1096 m_state = state_t::ok;
1097 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1098 auto _w = w->get();
1099 std::unique_lock<std::mutex> lk(_w->mutex);
1100 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1101 if (ok())
1102 m_state = _w->source->state();
1103 }
1104 }
1105
1106 std::list<std::unique_ptr<worker>> m_workers;
1107 };
1108
1109 constexpr size_t default_async_limit = 0x100000;
1110
1116 template <size_t CAPACITY = default_async_limit>
1118 {
1119 public:
1120 async_reader(_Inout_ basic& source) :
1121 converter(source),
1122 m_worker(process, std::ref(*this))
1123 {}
1124
1125 virtual ~async_reader()
1126 {
1127 m_ring.quit();
1128 m_worker.join();
1129 }
1130
1131#pragma warning(suppress: 6101) // See [1] below
1132 virtual _Success_(return != 0 || length == 0) size_t read(
1133 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1134 {
1135 assert(data || !length);
1136 for (size_t to_read = length;;) {
1137 uint8_t* ptr; size_t num_read;
1138 std::tie(ptr, num_read) = m_ring.front();
1139 if (!ptr) _Unlikely_ {
1140 // [1] Code analysis misses length - to_read bytes were written to data in previous loop iterations.
1141 m_state = to_read < length || !length ? state_t::ok : m_source->state();
1142 return length - to_read;
1143 }
1144 if (to_read < num_read)
1145 num_read = to_read;
1146 memcpy(data, ptr, num_read);
1147 m_ring.pop(num_read);
1148 to_read -= num_read;
1149 if (!to_read) {
1150 m_state = state_t::ok;
1151 return length;
1152 }
1153 reinterpret_cast<uint8_t*&>(data) += num_read;
1154 }
1155 }
1156
1157 protected:
1158 static void process(_Inout_ async_reader& w)
1159 {
1160 for (;;) {
1161 uint8_t* ptr; size_t num_write;
1162 std::tie(ptr, num_write) = w.m_ring.back();
1163 if (!ptr) _Unlikely_
1164 break;
1165 num_write = w.m_source->read(ptr, num_write);
1166 w.m_ring.push(num_write);
1167 if (!w.m_source->ok()) {
1168 w.m_ring.quit();
1169 break;
1170 }
1171 }
1172 }
1173
1174 protected:
1175 ring<uint8_t, CAPACITY> m_ring;
1176 std::thread m_worker;
1177 };
1178
1184 template <size_t CAPACITY = default_async_limit>
1186 {
1187 public:
1188 async_writer(_Inout_ basic& source) :
1189 converter(source),
1190 m_worker(process, std::ref(*this))
1191 {}
1192
1193 virtual ~async_writer()
1194 {
1195 m_ring.quit();
1196 m_worker.join();
1197 }
1198
1199 virtual _Success_(return != 0) size_t write(
1200 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1201 {
1202 assert(data || !length);
1203 for (size_t to_write = length;;) {
1204 uint8_t* ptr; size_t num_write;
1205 std::tie(ptr, num_write) = m_ring.back();
1206 if (!ptr) _Unlikely_ {
1207 m_state = state_t::fail;
1208 return length - to_write;
1209 }
1210 if (to_write < num_write)
1211 num_write = to_write;
1212 memcpy(ptr, data, num_write);
1213 m_ring.push(num_write);
1214 to_write -= num_write;
1215 if (!to_write) {
1216 m_state = state_t::ok;
1217 return length;
1218 }
1219 reinterpret_cast<const uint8_t*&>(data) += num_write;
1220 }
1221 }
1222
1223 virtual void flush()
1224 {
1225 m_ring.sync();
1227 }
1228
1229 protected:
1230 static void process(_Inout_ async_writer& w)
1231 {
1232 for (;;) {
1233 uint8_t* ptr; size_t num_read;
1234 std::tie(ptr, num_read) = w.m_ring.front();
1235 if (!ptr)
1236 break;
1237 num_read = w.m_source->write(ptr, num_read);
1238 w.m_ring.pop(num_read);
1239 if (!w.m_source->ok()) {
1240 w.m_ring.quit();
1241 break;
1242 }
1243 }
1244 }
1245
1246 protected:
1247 ring<uint8_t, CAPACITY> m_ring;
1248 std::thread m_worker;
1249 };
1250
1251 constexpr size_t default_buffer_size = 0x400;
1252
1256 class buffer : public converter
1257 {
1258 protected:
1259 explicit buffer(_In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1260 converter(),
1261 m_read_buffer(read_buffer_size),
1262 m_write_buffer(write_buffer_size)
1263 {}
1264
1265 void done()
1266 {
1267 if (m_source)
1268 flush_write();
1269 converter::done();
1270 }
1271
1272 public:
1273 buffer(_Inout_ basic& source, _In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1274 converter(source),
1275 m_read_buffer(read_buffer_size),
1276 m_write_buffer(write_buffer_size)
1277 {}
1278
1279 virtual ~buffer()
1280 {
1281 if (m_source)
1282 flush_write();
1283 }
1284
1285 virtual _Success_(return != 0 || length == 0) size_t read(
1286 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1287 {
1288 assert(data || !length);
1289 for (size_t to_read = length;;) {
1290 size_t buffer_size = m_read_buffer.tail - m_read_buffer.head;
1291 if (to_read <= buffer_size) {
1292 memcpy(data, m_read_buffer.data + m_read_buffer.head, to_read);
1293 m_read_buffer.head += to_read;
1294 m_state = state_t::ok;
1295 return length;
1296 }
1297 if (buffer_size) {
1298 memcpy(data, m_read_buffer.data + m_read_buffer.head, buffer_size);
1299 reinterpret_cast<uint8_t*&>(data) += buffer_size;
1300 to_read -= buffer_size;
1301 }
1302 m_read_buffer.head = 0;
1303 if (to_read > m_read_buffer.capacity) {
1304 // When needing to read more data than buffer capacity, bypass the buffer.
1305 m_read_buffer.tail = 0;
1306 to_read -= m_source->read(data, to_read);
1307 m_state = to_read < length ? state_t::ok : m_source->state();
1308 return length - to_read;
1309 }
1310 m_read_buffer.tail = m_source->read(m_read_buffer.data, m_read_buffer.capacity);
1311 if (m_read_buffer.tail < m_read_buffer.capacity && m_read_buffer.tail < to_read) _Unlikely_ {
1312 memcpy(data, m_read_buffer.data, m_read_buffer.tail);
1313 m_read_buffer.head = m_read_buffer.tail;
1314 to_read -= m_read_buffer.tail;
1315 m_state = to_read < length ? state_t::ok : m_source->state();
1316 return length - to_read;
1317 }
1318 }
1319 }
1320
1321 virtual _Success_(return != 0) size_t write(
1322 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1323 {
1324 assert(data || !length);
1325 if (!length) _Unlikely_ {
1326 // Pass null writes (zero-byte length). Null write operations have special meaning with with Windows pipes.
1327 flush_write();
1328 if (!ok()) _Unlikely_
1329 return 0;
1330 m_source->write(nullptr, 0);
1331 m_state = m_source->state();
1332 return 0;
1333 }
1334
1335 for (size_t to_write = length;;) {
1336 size_t available_buffer = m_write_buffer.capacity - m_write_buffer.tail;
1337 if (to_write <= available_buffer) {
1338 memcpy(m_write_buffer.data + m_write_buffer.tail, data, to_write);
1339 m_write_buffer.tail += to_write;
1340 m_state = state_t::ok;
1341 return length;
1342 }
1343 if (available_buffer) {
1344 memcpy(m_write_buffer.data + m_write_buffer.tail, data, available_buffer);
1345 reinterpret_cast<const uint8_t*&>(data) += available_buffer;
1346 to_write -= available_buffer;
1347 m_write_buffer.tail += available_buffer;
1348 }
1349 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1350 if (buffer_size) {
1351 m_write_buffer.head += m_source->write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1352 m_state = m_source->state();
1353 if (m_write_buffer.head == m_write_buffer.tail)
1354 m_write_buffer.head = m_write_buffer.tail = 0;
1355 else
1356 return length - to_write;
1357 }
1358 if (to_write > m_write_buffer.capacity) {
1359 // When needing to write more data than buffer capacity, bypass the buffer.
1360 to_write -= m_source->write(data, to_write);
1361 m_state = m_source->state();
1362 return length - to_write;
1363 }
1364 }
1365 }
1366
1367 virtual void flush()
1368 {
1369 flush_write();
1370 if (ok())
1372 }
1373
1374 protected:
1375 void flush_write()
1376 {
1377 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1378 if (buffer_size) {
1379 m_write_buffer.head += m_source->write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1380 if (m_write_buffer.head == m_write_buffer.tail) {
1381 m_write_buffer.head = 0;
1382 m_write_buffer.tail = 0;
1383 }
1384 else {
1385 m_state = m_source->state();
1386 return;
1387 }
1388 }
1389 m_state = state_t::ok;
1390 }
1391
1392 struct buffer_t {
1393 uint8_t* data;
1394 size_t head, tail, capacity;
1395
1396 buffer_t(_In_ size_t buffer_size) :
1397 head(0),
1398 tail(0),
1399 capacity(buffer_size),
1400 data(buffer_size ? new uint8_t[buffer_size] : nullptr)
1401 {}
1402
1403 ~buffer_t()
1404 {
1405 if (data)
1406 delete[] data;
1407 }
1408 } m_read_buffer, m_write_buffer;
1409 };
1410
1414 class limiter : public converter
1415 {
1416 public:
1417 limiter(_Inout_ basic& source, _In_ fsize_t _read_limit = 0, _In_ fsize_t _write_limit = 0) :
1418 converter(source),
1419 read_limit(_read_limit),
1420 write_limit(_write_limit)
1421 {}
1422
1423 virtual _Success_(return != 0 || length == 0) size_t read(
1424 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1425 {
1426 size_t num_read;
1427 if (read_limit == fsize_max) {
1428 num_read = m_source->read(data, length);
1429 m_state = m_source->state();
1430 }
1431 else if (length <= read_limit) {
1432 num_read = m_source->read(data, length);
1433 m_state = m_source->state();
1434 read_limit -= num_read;
1435 }
1436 else if (length && !read_limit) {
1437 num_read = 0;
1438 m_state = state_t::eof;
1439 }
1440 else {
1441 num_read = m_source->read(data, static_cast<size_t>(read_limit));
1442 m_state = m_source->state();
1443 read_limit -= num_read;
1444 }
1445 return num_read;
1446 }
1447
1448 virtual _Success_(return != 0) size_t write(
1449 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1450 {
1451 size_t num_written;
1452 if (write_limit == fsize_max) {
1453 num_written = m_source->write(data, length);
1454 m_state = m_source->state();
1455 }
1456 else if (length <= write_limit) {
1457 num_written = m_source->write(data, length);
1458 m_state = m_source->state();
1459 write_limit -= num_written;
1460 }
1461 else if (length && !write_limit) {
1462 num_written = 0;
1463 m_state = state_t::fail;
1464 }
1465 else {
1466 num_written = m_source->write(data, static_cast<size_t>(write_limit));
1467 m_state = m_source->state();
1468 write_limit -= num_written;
1469 }
1470 return num_written;
1471 }
1472
1473 public:
1474 fsize_t
1477 };
1478
1482 class window : public limiter
1483 {
1484 public:
1485 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) :
1486 limiter(source, read_limit, write_limit),
1487 read_offset(_read_offset),
1488 write_offset(_write_offset)
1489 {}
1490
1491 virtual _Success_(return != 0 || length == 0) size_t read(
1492 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1493 {
1494 if (read_offset) {
1495 m_source->skip(read_offset);
1496 m_state = m_source->state();
1497 if (!ok()) _Unlikely_
1498 return 0;
1499 read_offset = 0;
1500 }
1501 size_t num_read;
1502 if (read_limit == fsize_max) {
1503 num_read = m_source->read(data, length);
1504 m_state = m_source->state();
1505 }
1506 else if (length <= read_limit) {
1507 num_read = m_source->read(data, length);
1508 m_state = m_source->state();
1509 read_limit -= num_read;
1510 }
1511 else if (length && !read_limit) {
1512 num_read = 0;
1513 m_source->skip(length);
1514 m_state = state_t::eof;
1515 }
1516 else {
1517 num_read = m_source->read(data, static_cast<size_t>(read_limit));
1518 m_state = m_source->state();
1519 read_limit -= num_read;
1520 }
1521 return num_read;
1522 }
1523
1524 virtual _Success_(return != 0) size_t write(
1525 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1526 {
1527 size_t num_skipped, num_written;
1528 if (length <= write_offset) {
1529 write_offset -= length;
1530 m_state = state_t::ok;
1531 return length;
1532 }
1533 if (write_offset) {
1534 reinterpret_cast<const uint8_t*&>(data) += static_cast<size_t>(write_offset);
1535 length -= static_cast<size_t>(write_offset);
1536 num_skipped = static_cast<size_t>(write_offset);
1537 write_offset = 0;
1538 }
1539 else
1540 num_skipped = 0;
1541 if (write_limit == fsize_max) {
1542 num_written = m_source->write(data, length);
1543 m_state = m_source->state();
1544 }
1545 else if (length <= write_limit) {
1546 num_written = m_source->write(data, length);
1547 m_state = m_source->state();
1548 write_limit -= num_written;
1549 }
1550 else if (length && !write_limit) {
1551 num_skipped += length;
1552 num_written = 0;
1553 m_state = state_t::ok;
1554 }
1555 else {
1556 num_skipped += length - static_cast<size_t>(write_limit);
1557 num_written = m_source->write(data, static_cast<size_t>(write_limit));
1558 m_state = m_source->state();
1559 write_limit -= num_written;
1560 }
1561 return num_skipped + num_written;
1562 }
1563
1564 public:
1565 fpos_t
1568 };
1569
1574 {
1575 public:
1576 file_window(_Inout_ basic_file& source, fpos_t offset = 0, fsize_t length = 0) :
1577 basic(source.state()),
1578 m_source(source),
1579 m_offset(source.tell()),
1580 m_region(offset, offset + length)
1581 {}
1582
1583 virtual _Success_(return != 0 || length == 0) size_t read(
1584 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1585 {
1586 assert(data || !length);
1587 if (m_region.contains(m_offset)) {
1588 size_t num_read = m_source.read(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1589 m_state = m_source.state();
1590 m_offset += num_read;
1591 return num_read;
1592 }
1593 m_state = length ? state_t::eof : state_t::ok;
1594 return 0;
1595 }
1596
1597 virtual _Success_(return != 0) size_t write(
1598 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1599 {
1600 assert(data || !length);
1601 if (m_region.contains(m_offset)) {
1602 size_t num_written = m_source.write(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1603 m_state = m_source.state();
1604 m_offset += num_written;
1605 return num_written;
1606 }
1607 m_state = state_t::fail;
1608 return 0;
1609 }
1610
1611 virtual void close()
1612 {
1613 m_source.close();
1614 m_state = m_source.state();
1615 }
1616
1617 virtual void flush()
1618 {
1619 m_source.flush();
1620 m_state = m_source.state();
1621 }
1622
1623 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
1624 {
1625 m_offset = m_source.seek(offset, how);
1626 m_state = m_source.state();
1627 return ok() ? m_offset - m_region.start : fpos_max;
1628 }
1629
1630 virtual void skip(_In_ fsize_t amount)
1631 {
1632 m_source.skip(amount);
1633 m_state = m_source.state();
1634 }
1635
1636 virtual fpos_t tell() const
1637 {
1638 fpos_t offset = m_source.tell();
1639 return m_region.contains(offset) ? offset - m_region.start : fpos_max;
1640 }
1641
1642 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
1643 {
1644 if (m_region.contains(offset)) {
1645 m_source.lock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1646 m_state = m_source.state();
1647 }
1648 else
1649 m_state = state_t::fail;
1650 }
1651
1652 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
1653 {
1654 if (m_region.contains(offset)) {
1655 m_source.unlock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1656 m_state = m_source.state();
1657 }
1658 else
1659 m_state = state_t::fail;
1660 }
1661
1662 virtual fsize_t size()
1663 {
1664 return m_region.size();
1665 }
1666
1667 virtual void truncate()
1668 {
1669 m_state = state_t::fail;
1670 }
1671
1672 protected:
1673 basic_file& m_source;
1674 fpos_t m_offset;
1675 interval<fpos_t> m_region;
1676 };
1677
1678 constexpr size_t default_cache_size = 0x1000;
1679
1683 class cache : public basic_file
1684 {
1685 protected:
1686 explicit cache(_In_ size_t cache_size = default_cache_size) :
1687 basic(state_t::fail),
1688 m_source(nullptr),
1689 m_cache(cache_size),
1690 m_offset(0)
1691#if SET_FILE_OP_TIMES
1692 , m_atime(time_point::min()),
1693 m_mtime(time_point::min())
1694#endif
1695 {}
1696
1697 void init(_Inout_ basic_file& source)
1698 {
1699 m_state = source.state();
1700 m_source = &source;
1701 m_offset = source.tell();
1702#if SET_FILE_OP_TIMES
1703 m_atime = source.atime();
1704 m_mtime = source.mtime();
1705#endif
1706 }
1707
1708 void done()
1709 {
1710 if (m_source) {
1711 flush_cache();
1712 if (!ok()) _Unlikely_
1713 throw std::runtime_error("cache flush failed"); // Data loss occured
1714 m_source->seek(m_offset);
1715 m_source = nullptr;
1716 }
1717 }
1718
1719 public:
1720 cache(_Inout_ basic_file& source, _In_ size_t cache_size = default_cache_size) :
1721 basic(source.state()),
1722 m_source(&source),
1723 m_cache(cache_size),
1724 m_offset(source.tell())
1725#if SET_FILE_OP_TIMES
1726 , m_atime(source.atime()),
1727 m_mtime(source.mtime())
1728#endif
1729 {}
1730
1731 virtual ~cache() noexcept(false)
1732 {
1733 if (m_source) {
1734 flush_cache();
1735 if (!ok()) _Unlikely_
1736 throw std::runtime_error("cache flush failed"); // Data loss occured
1737 m_source->seek(m_offset);
1738 }
1739 }
1740
1741 virtual _Success_(return != 0 || length == 0) size_t read(
1742 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1743 {
1744 assert(data || !length);
1745#if SET_FILE_OP_TIMES
1746 m_atime = time_point::now();
1747#endif
1748 for (size_t to_read = length;;) {
1749 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1750 if (m_cache.region.contains(m_offset)) {
1751 size_t remaining_cache = static_cast<size_t>(m_cache.region.end - m_offset);
1752 if (to_read <= remaining_cache) {
1753 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), to_read);
1754 m_offset += to_read;
1755 m_state = state_t::ok;
1756 return length;
1757 }
1758 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), remaining_cache);
1759 reinterpret_cast<uint8_t*&>(data) += remaining_cache;
1760 to_read -= remaining_cache;
1761 m_offset += remaining_cache;
1762 }
1763 flush_cache();
1764 if (!ok()) _Unlikely_ {
1765 if (to_read < length)
1766 m_state = state_t::ok;
1767 return length - to_read;
1768 }
1769 }
1770 {
1771 fpos_t end_max = m_offset + to_read;
1772 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1773 // Read spans multiple cache blocks. Bypass cache to the last block.
1774 m_source->seek(m_offset);
1775 if (!m_source->ok()) _Unlikely_ {
1776 m_state = to_read < length ? state_t::ok : state_t::fail;
1777 return length - to_read;
1778 }
1779 size_t num_read = m_source->read(data, to_read - static_cast<size_t>(end_max % m_cache.capacity));
1780 m_offset += num_read;
1781 to_read -= num_read;
1782 if (!to_read) {
1783 m_state = state_t::ok;
1784 return length;
1785 }
1786 reinterpret_cast<uint8_t*&>(data) += num_read;
1787 m_state = m_source->state();
1788 if (!ok()) {
1789 if (to_read < length)
1790 m_state = state_t::ok;
1791 return length - to_read;
1792 }
1793 }
1794 }
1795 load_cache(m_offset);
1796 if (!ok() || m_cache.region.end <= m_offset) _Unlikely_ {
1797 m_state = to_read < length ? state_t::ok : state_t::fail;
1798 return length - to_read;
1799 }
1800 }
1801 }
1802
1803 virtual _Success_(return != 0) size_t write(
1804 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1805 {
1806 assert(data || !length);
1807#if SET_FILE_OP_TIMES
1808 m_atime = m_mtime = time_point::now();
1809#endif
1810 for (size_t to_write = length;;) {
1811 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1812 fpos_t end_max = m_cache.region.start + m_cache.capacity;
1813 if (m_cache.region.start <= m_offset && m_offset < end_max) {
1814 size_t remaining_cache = static_cast<size_t>(end_max - m_offset);
1815 if (to_write <= remaining_cache) {
1816 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, to_write);
1817 m_offset += to_write;
1818 m_cache.status = cache_t::cache_t::status_t::dirty;
1819 m_cache.region.end = std::max(m_cache.region.end, m_offset);
1820 m_state = state_t::ok;
1821 return length;
1822 }
1823 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, remaining_cache);
1824 reinterpret_cast<const uint8_t*&>(data) += remaining_cache;
1825 to_write -= remaining_cache;
1826 m_offset += remaining_cache;
1827 m_cache.status = cache_t::cache_t::status_t::dirty;
1828 m_cache.region.end = end_max;
1829 }
1830 flush_cache();
1831 if (!ok()) _Unlikely_
1832 return length - to_write;
1833 }
1834 {
1835 fpos_t end_max = m_offset + to_write;
1836 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1837 // Write spans multiple cache blocks. Bypass cache to the last block.
1838 m_source->seek(m_offset);
1839 if (!ok()) _Unlikely_
1840 return length - to_write;
1841 size_t num_written = m_source->write(data, to_write - static_cast<size_t>(end_max % m_cache.capacity));
1842 m_offset += num_written;
1843 m_state = m_source->state();
1844 to_write -= num_written;
1845 if (!to_write || !ok())
1846 return length - to_write;
1847 reinterpret_cast<const uint8_t*&>(data) += num_written;
1848 }
1849 }
1850 load_cache(m_offset);
1851 if (!ok()) _Unlikely_
1852 return length - to_write;
1853 }
1854 }
1855
1856 virtual void close()
1857 {
1858 invalidate_cache();
1859 if (!ok()) _Unlikely_
1860 throw std::runtime_error("cache flush failed"); // Data loss occured
1861 m_source->close();
1862 m_state = m_source->state();
1863 }
1864
1865 virtual void flush()
1866 {
1867#if SET_FILE_OP_TIMES
1868 m_atime = m_mtime = time_point::min();
1869#endif
1870 flush_cache();
1871 if (!ok()) _Unlikely_
1872 return;
1873 m_source->flush();
1874 }
1875
1876 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
1877 {
1878 m_state = state_t::ok;
1879 switch (how) {
1880 case seek_t::beg:
1881 return m_offset = offset;
1882 case seek_t::cur:
1883 return m_offset += offset;
1884 case seek_t::end:
1885 return m_offset = size() + offset;
1886 default:
1887 throw std::invalid_argument("unknown seek origin");
1888 }
1889 }
1890
1891 virtual fpos_t tell() const
1892 {
1893 return m_offset;
1894 }
1895
1896 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
1897 {
1898 m_source->lock(offset, length);
1899 m_state = m_source->state();
1900 }
1901
1902 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
1903 {
1904 m_source->unlock(offset, length);
1905 m_state = m_source->state();
1906 }
1907
1908 virtual fsize_t size()
1909 {
1910 return m_cache.status != cache_t::cache_t::status_t::empty ?
1911 std::max(m_source->size(), m_cache.region.end) :
1912 m_source->size();
1913 }
1914
1915 virtual void truncate()
1916 {
1917#if SET_FILE_OP_TIMES
1918 m_atime = m_mtime = time_point::now();
1919#endif
1920 m_source->seek(m_offset);
1921 if (m_cache.region.end <= m_offset) {
1922 // Truncation does not affect cache.
1923 }
1924 else if (m_cache.region.start <= m_offset) {
1925 // Truncation truncates cache.
1926 m_cache.region.end = m_offset;
1927 }
1928 else {
1929 // Truncation invalidates cache.
1930 m_cache.status = cache_t::cache_t::status_t::empty;
1931 }
1932 m_source->truncate();
1933 m_state = m_source->state();
1934 }
1935
1936 virtual time_point ctime() const
1937 {
1938 return m_source->ctime();
1939 }
1940
1941 virtual time_point atime() const
1942 {
1943#if SET_FILE_OP_TIMES
1944 return std::max(m_atime, m_source->atime());
1945#else
1946 return m_source->atime();
1947#endif
1948 }
1949
1950 virtual time_point mtime() const
1951 {
1952#if SET_FILE_OP_TIMES
1953 return std::max(m_mtime, m_source->mtime());
1954#else
1955 return m_source->mtime();
1956#endif
1957 }
1958
1959 virtual void set_ctime(time_point date)
1960 {
1961 m_source->set_ctime(date);
1962 }
1963
1964 virtual void set_atime(time_point date)
1965 {
1966#if SET_FILE_OP_TIMES
1967 m_atime = date;
1968#endif
1969 m_source->set_atime(date);
1970 }
1971
1972 virtual void set_mtime(time_point date)
1973 {
1974#if SET_FILE_OP_TIMES
1975 m_mtime = date;
1976#endif
1977 m_source->set_mtime(date);
1978 }
1979
1980 protected:
1981 void flush_cache()
1982 {
1983 if (m_cache.status != cache_t::cache_t::status_t::dirty)
1984 m_state = state_t::ok;
1985 else if (!m_cache.region.empty()) {
1986 write_cache();
1987 if (ok())
1988 m_cache.status = cache_t::cache_t::status_t::loaded;
1989 }
1990 else {
1991 m_state = state_t::ok;
1992 m_cache.status = cache_t::cache_t::status_t::loaded;
1993 }
1994 }
1995
1996 void invalidate_cache()
1997 {
1998 if (m_cache.status == cache_t::cache_t::status_t::dirty && !m_cache.region.empty()) {
1999 write_cache();
2000 if (!ok()) _Unlikely_
2001 return;
2002 } else
2003 m_state = state_t::ok;
2004 m_cache.status = cache_t::cache_t::status_t::empty;
2005 }
2006
2007 void load_cache(_In_ fpos_t start)
2008 {
2009 assert(m_cache.status != cache_t::cache_t::status_t::dirty);
2010 start -= start % m_cache.capacity; // Align to cache block size.
2011 m_source->seek(m_cache.region.start = start);
2012 if (m_source->ok()) {
2013 m_cache.region.end = start + m_source->read(m_cache.data, m_cache.capacity);
2014 m_cache.status = cache_t::cache_t::status_t::loaded;
2015 m_state = state_t::ok; // Regardless the read failure, we still might have cached some data.
2016 }
2017 else
2018 m_state = state_t::fail;
2019 }
2020
2021 void write_cache()
2022 {
2023 assert(m_cache.status == cache_t::cache_t::status_t::dirty);
2024 m_source->seek(m_cache.region.start);
2025 m_source->write(m_cache.data, static_cast<size_t>(m_cache.region.size()));
2026 m_state = m_source->state();
2027 }
2028
2029 basic_file* m_source;
2030 struct cache_t {
2031 uint8_t* data;
2032 size_t capacity;
2033 enum class status_t {
2034 empty = 0,
2035 loaded,
2036 dirty,
2037 } status;
2039
2040 cache_t(_In_ size_t _capacity) :
2041 data(new uint8_t[_capacity]),
2042 capacity(_capacity),
2043 status(status_t::empty),
2044 region(0)
2045 {}
2046
2047 ~cache_t()
2048 {
2049 delete[] data;
2050 }
2051 } m_cache;
2052 fpos_t m_offset;
2053#if SET_FILE_OP_TIMES
2054 time_point
2055 m_atime,
2056 m_mtime;
2057#endif
2058 };
2059
2063 class basic_sys : virtual public basic, public sys_object
2064 {
2065 public:
2066 basic_sys(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) :
2067 basic(state),
2068 sys_object(h)
2069 {}
2070
2071 virtual _Success_(return != 0 || length == 0) size_t read(
2072 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2073 {
2074 assert(data || !length);
2075 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2076 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2077 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2078 size_t
2079#if defined(_WIN64)
2080 block_size = 0x1F80000;
2081#elif defined(_WIN32)
2082 block_size = 0x3f00000;
2083#else
2084 block_size = SSIZE_MAX;
2085#endif
2086 for (size_t to_read = length;;) {
2087#ifdef _WIN32
2088 // ReadFile() might raise exception (e.g. STATUS_FILE_BAD_FORMAT/0xE0000002).
2089 BOOL succeeded;
2090 DWORD num_read;
2091 __try { succeeded = ReadFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_read, block_size)), &num_read, nullptr); }
2092 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_read = 0; }
2093 if (!succeeded && GetLastError() == ERROR_NO_SYSTEM_RESOURCES && block_size > default_block_size) _Unlikely_ {
2094 // Error "Insufficient system resources exist to complete the requested service." occurs
2095 // ocasionally, when attempting to read too much data at once (e.g. over \\TSClient).
2096 block_size = default_block_size;
2097 continue;
2098 }
2099 if (!succeeded) _Unlikely_
2100#else
2101 ssize_t num_read = ::read(m_h, data, static_cast<ssize_t>(std::min<size_t>(to_read, block_size)));
2102 if (num_read < 0) _Unlikely_
2103#endif
2104 {
2105 m_state = to_read < length ? state_t::ok : state_t::fail;
2106 return length - to_read;
2107 }
2108 if (!num_read) _Unlikely_ {
2109 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2110 return length - to_read;
2111 }
2112 to_read -= num_read;
2113 if (!to_read) {
2114 m_state = state_t::ok;
2115 return length;
2116 }
2117 reinterpret_cast<uint8_t*&>(data) += num_read;
2118 }
2119 }
2120
2121 virtual _Success_(return != 0) size_t write(
2122 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2123 {
2124 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2125 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2126 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2127 constexpr size_t
2128#if defined(_WIN64)
2129 block_size = 0x1F80000;
2130#elif defined(_WIN32)
2131 block_size = 0x3f00000;
2132#else
2133 block_size = SSIZE_MAX;
2134#endif
2135 for (size_t to_write = length;;) {
2136#ifdef _WIN32
2137 // ReadFile() might raise an exception. Be cautious with WriteFile() too.
2138 BOOL succeeded;
2139 DWORD num_written;
2140 __try { succeeded = WriteFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_write, block_size)), &num_written, nullptr); }
2141 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_written = 0; }
2142 to_write -= num_written;
2143 if (!to_write) {
2144 m_state = state_t::ok;
2145 return length;
2146 }
2147 reinterpret_cast<const uint8_t*&>(data) += num_written;
2148 if (!succeeded) _Unlikely_ {
2149 m_state = state_t::fail;
2150 return length - to_write;
2151 }
2152#else
2153 ssize_t num_written = ::write(m_h, data, static_cast<ssize_t>(std::min<size_t>(to_write, block_size)));
2154 if (num_written < 0) _Unlikely_ {
2155 m_state = state_t::fail;
2156 return length - to_write;
2157 }
2158 to_write -= num_written;
2159 if (!to_write) {
2160 m_state = state_t::ok;
2161 return length;
2162 }
2163 reinterpret_cast<const uint8_t*&>(data) += num_written;
2164#endif
2165 }
2166 }
2167
2168 virtual void close()
2169 {
2170 try {
2172 m_state = state_t::ok;
2173 }
2174 catch (...) {
2175 m_state = state_t::fail;
2176 }
2177 }
2178
2179 virtual void flush()
2180 {
2181#ifdef _WIN32
2182 m_state = FlushFileBuffers(m_h) ? state_t::ok : state_t::fail;
2183#else
2184 m_state = fsync(m_h) >= 0 ? state_t::ok : state_t::fail;
2185#endif
2186 }
2187 };
2188
2192 class buffered_sys : public buffer
2193 {
2194 public:
2195 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) :
2196 buffer(read_buffer_size, write_buffer_size),
2197 m_source(h)
2198 {
2199 init(m_source);
2200 }
2201
2202 virtual ~buffered_sys()
2203 {
2204 done();
2205 }
2206
2207 protected:
2208 basic_sys m_source;
2209 };
2210
2211#ifdef _WIN32
2215 class sequential_stream : public basic
2216 {
2217 public:
2218 sequential_stream(_In_ ISequentialStream* source) : m_source(source)
2219 {
2220 m_source->AddRef();
2221 }
2222
2223 virtual ~sequential_stream()
2224 {
2225 m_source->Release();
2226 }
2227
2228 virtual _Success_(return != 0 || length == 0) size_t read(
2229 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2230 {
2231 assert(data || !length);
2232 for (size_t to_read = length;;) {
2233 HRESULT hr;
2234 ULONG num_read = 0;
2235 __try { hr = m_source->Read(data, (ULONG)std::min<size_t>(to_read, ULONG_MAX), &num_read); }
2236 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2237 if (FAILED(hr)) _Unlikely_ {
2238 m_state = to_read < length ? state_t::ok : state_t::fail;
2239 return length - to_read;
2240 }
2241 to_read -= num_read;
2242 if (hr == S_FALSE) _Unlikely_ {
2243 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2244 return length - to_read;
2245 }
2246 if (!to_read) {
2247 m_state = state_t::ok;
2248 return length;
2249 }
2250 reinterpret_cast<uint8_t*&>(data) += num_read;
2251 }
2252 }
2253
2254 virtual _Success_(return != 0) size_t write(
2255 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2256 {
2257 assert(data || !length);
2258 for (size_t to_write = length;;) {
2259 HRESULT hr;
2260 ULONG num_written = 0;
2261 __try { hr = m_source->Write(data, static_cast<ULONG>(std::min<size_t>(to_write, ULONG_MAX)), &num_written); }
2262 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2263 // In abscence of documentation whether num_written gets set when FAILED(hr) (i.e. partially succesful writes),
2264 // assume write failed completely.
2265 if (FAILED(hr)) _Unlikely_ {
2266 m_state = state_t::fail;
2267 return length - to_write;
2268 }
2269 to_write -= num_written;
2270 if (!to_write) {
2271 m_state = state_t::ok;
2272 return length;
2273 }
2274 reinterpret_cast<const uint8_t*&>(data) += num_written;
2275 }
2276 }
2277
2278 protected:
2279 ISequentialStream* m_source;
2280 };
2281
2285 class asp : public basic
2286 {
2287 public:
2288 asp(_In_opt_ IRequest* request, _In_opt_ IResponse* response) :
2289 m_request(request),
2290 m_response(response)
2291 {
2292 if (m_request)
2293 m_request->AddRef();
2294 if (m_response)
2295 m_response->AddRef();
2296 }
2297
2298 virtual ~asp()
2299 {
2300 if (m_request)
2301 m_request->Release();
2302 if (m_response)
2303 m_response->Release();
2304 }
2305
2306 virtual _Success_(return != 0 || length == 0) size_t read(
2307 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2308 {
2309 assert(data || !length);
2310 if (!m_request) _Unlikely_ {
2311 m_state = state_t::fail;
2312 return 0;
2313 }
2314 for (size_t to_read = length;;) {
2315 VARIANT var_amount, var_data;
2316 V_VT(&var_amount) = VT_I4;
2317 V_I4(&var_amount) = (LONG)std::min<size_t>(to_read, LONG_MAX);
2318 V_VT(&var_data) = VT_EMPTY;
2319 HRESULT hr = [&]() {
2320 __try { return m_request->BinaryRead(&var_amount, &var_data); }
2321 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2322 }();
2323 if (FAILED(hr)) _Unlikely_ {
2324 m_state = to_read < length ? state_t::ok : state_t::fail;
2325 return length - to_read;
2326 }
2327 assert(V_VT(&var_amount) == VT_I4);
2328 assert(V_VT(&var_data) == (VT_ARRAY | VT_UI1));
2329 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(V_ARRAY(&var_data));
2330 if (!V_I4(&var_amount)) _Unlikely_ {
2331 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2332 return length - to_read;
2333 }
2334 safearray_accessor<uint8_t> a(sa.get());
2335 memcpy(data, a.data(), V_I4(&var_amount));
2336 to_read -= V_I4(&var_amount);
2337 if (!to_read) {
2338 m_state = state_t::ok;
2339 return length;
2340 }
2341 reinterpret_cast<uint8_t*&>(data) += V_I4(&var_amount);
2342 }
2343 }
2344
2345 virtual _Success_(return != 0) size_t write(
2346 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2347 {
2348 if (!m_response) {
2349 m_state = state_t::fail;
2350 return 0;
2351 }
2352 for (size_t to_write = length;;) {
2353 UINT num_written = static_cast<UINT>(std::min<size_t>(to_write, UINT_MAX));
2354 std::unique_ptr<OLECHAR, SysFreeString_delete> bstr_data(SysAllocStringByteLen(reinterpret_cast<LPCSTR>(data), num_written));
2355 VARIANT var_data;
2356 V_VT(&var_data) = VT_BSTR;
2357 V_BSTR(&var_data) = bstr_data.get();
2358 HRESULT hr = [&]() {
2359 __try { return m_response->BinaryWrite(var_data); }
2360 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2361 }();
2362 if (FAILED(hr)) _Unlikely_ {
2363 m_state = state_t::fail;
2364 return length - to_write;
2365 }
2366 to_write -= num_written;
2367 if (!to_write) {
2368 m_state = state_t::ok;
2369 return length;
2370 }
2371 reinterpret_cast<const uint8_t*&>(data) += num_written;
2372 }
2373 }
2374
2375 virtual void close()
2376 {
2377 if (m_response) {
2378 __try { m_response->End(); }
2379 __except (EXCEPTION_EXECUTE_HANDLER) {}
2380 }
2381 m_state = state_t::ok;
2382 }
2383
2384 virtual void flush()
2385 {
2386 if (m_response) {
2387 HRESULT hr;
2388 __try { hr = m_response->Flush(); }
2389 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2390 m_state = SUCCEEDED(hr) ? state_t::ok : state_t::fail;
2391 }
2392 }
2393
2394 protected:
2395 IRequest* m_request;
2396 IResponse* m_response;
2397 };
2398#endif
2399
2403 enum mode_t
2404 {
2405 mode_for_reading = 1 << 0,
2406 mode_for_writing = 1 << 1,
2407 mode_for_chmod = 1 << 2,
2408 mode_create = 1 << 3,
2409 mode_preserve_existing = mode_create | (1 << 4),
2410 mode_append = 1 << 5,
2411 mode_text = 0,
2412 mode_binary = 1 << 6,
2413
2414 share_none = 0,
2415 share_reading = 1 << 7,
2416 share_writing = 1 << 8,
2417 share_deleting = 1 << 9,
2418 share_all = share_reading | share_writing | share_deleting, // Allow others all operations on our file
2419
2420 inherit_handle = 1 << 10,
2421
2422 hint_write_thru = 1 << 11,
2423 hint_no_buffering = 1 << 12,
2424 hint_random_access = 1 << 13,
2425 hint_sequential_access = 1 << 14,
2426 };
2427
2428#pragma warning(push)
2429#pragma warning(disable: 4250)
2433 class file : virtual public basic_file, virtual public basic_sys
2434 {
2435 public:
2436 file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) : basic_sys(h, state) {}
2437
2444 file(_In_z_ const schar_t* filename, _In_ int mode)
2445 {
2446 open(filename, mode);
2447 }
2448
2455 void open(_In_z_ const schar_t* filename, _In_ int mode)
2456 {
2457 if (m_h != invalid_handle)
2458 close();
2459
2460#ifdef _WIN32
2461 DWORD dwDesiredAccess = 0;
2462 if (mode & mode_for_reading) dwDesiredAccess |= GENERIC_READ;
2463 if (mode & mode_for_writing) dwDesiredAccess |= GENERIC_WRITE;
2464 if (mode & mode_for_chmod) dwDesiredAccess |= FILE_WRITE_ATTRIBUTES;
2465
2466 DWORD dwShareMode = 0;
2467 if (mode & share_reading) dwShareMode |= FILE_SHARE_READ;
2468 if (mode & share_writing) dwShareMode |= FILE_SHARE_WRITE;
2469 if (mode & share_deleting) dwShareMode |= FILE_SHARE_DELETE;
2470
2471 SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };
2472 sa.bInheritHandle = mode & inherit_handle ? true : false;
2473
2474 DWORD dwCreationDisposition;
2475 switch (mode & mode_preserve_existing) {
2476 case mode_create: dwCreationDisposition = CREATE_ALWAYS; break;
2477 case mode_preserve_existing: dwCreationDisposition = OPEN_ALWAYS; break;
2478 case 0: dwCreationDisposition = OPEN_EXISTING; break;
2479 default: throw std::invalid_argument("invalid mode");
2480 }
2481
2482 DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
2483 if (mode & hint_write_thru) dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
2484 if (mode & hint_no_buffering) dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
2485 if (mode & hint_random_access) dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
2486 if (mode & hint_sequential_access) dwFlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
2487
2488 m_h = CreateFile(filename, dwDesiredAccess, dwShareMode, &sa, dwCreationDisposition, dwFlagsAndAttributes, nullptr);
2489#else
2490 int flags = 0;
2491 switch (mode & (mode_for_reading | mode_for_writing)) {
2492 case mode_for_reading: flags |= O_RDONLY; break;
2493 case mode_for_writing: flags |= O_WRONLY; break;
2494 case mode_for_reading | mode_for_writing: flags |= O_RDWR; break;
2495 }
2496 if (mode & mode_create) flags |= mode & mode_preserve_existing ? O_CREAT : (O_CREAT | O_EXCL);
2497 if (mode & hint_write_thru) flags |= O_DSYNC;
2498#ifndef __APPLE__
2499 if (mode & hint_no_buffering) flags |= O_RSYNC;
2500#endif
2501
2502 m_h = ::open(filename, flags, DEFFILEMODE);
2503#endif
2504 if (m_h != invalid_handle) {
2505 m_state = state_t::ok;
2506 if (mode & mode_append)
2507 seek(0, seek_t::end);
2508 }
2509 else
2510 m_state = state_t::fail;
2511 }
2512
2513 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2514 {
2515#ifdef _WIN32
2516 LARGE_INTEGER li;
2517 li.QuadPart = offset;
2518 li.LowPart = SetFilePointer(m_h, li.LowPart, &li.HighPart, static_cast<DWORD>(how));
2519 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR) {
2520 m_state = state_t::ok;
2521 return li.QuadPart;
2522 }
2523#else
2524 off64_t result = lseek64(m_h, offset, static_cast<int>(how));
2525 if (result >= 0) {
2526 m_state = state_t::ok;
2527 return result;
2528 }
2529#endif
2530 m_state = state_t::fail;
2531 return fpos_max;
2532 }
2533
2534 virtual fpos_t tell() const
2535 {
2536 if (m_h != invalid_handle) {
2537#ifdef _WIN32
2538 LARGE_INTEGER li;
2539 li.QuadPart = 0;
2540 li.LowPart = SetFilePointer(m_h, 0, &li.HighPart, FILE_CURRENT);
2541 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR)
2542 return li.QuadPart;
2543#else
2544 off64_t result = lseek64(m_h, 0, SEEK_CUR);
2545 if (result >= 0)
2546 return result;
2547#endif
2548 }
2549 return fpos_max;
2550 }
2551
2552 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2553 {
2554#ifdef _WIN32
2555 LARGE_INTEGER liOffset;
2556 LARGE_INTEGER liSize;
2557 liOffset.QuadPart = offset;
2558 liSize.QuadPart = length;
2559 if (LockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2560 m_state = state_t::ok;
2561 return;
2562 }
2563#else
2564 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2565 if (orig >= 0) {
2566 m_state = lseek64(m_h, offset, SEEK_SET) >= 0 && lockf64(m_h, F_LOCK, length) >= 0 ? state_t::ok : state_t::fail;
2567 lseek64(m_h, orig, SEEK_SET);
2568 m_state = state_t::ok;
2569 return;
2570 }
2571#endif
2572 m_state = state_t::fail;
2573 }
2574
2575 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2576 {
2577#ifdef _WIN32
2578 LARGE_INTEGER liOffset;
2579 LARGE_INTEGER liSize;
2580 liOffset.QuadPart = offset;
2581 liSize.QuadPart = length;
2582 if (UnlockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2583 m_state = state_t::ok;
2584 return;
2585 }
2586#else
2587 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2588 if (orig >= 0) {
2589 if (lseek64(m_h, offset, SEEK_SET) >= 0 && lockf64(m_h, F_ULOCK, length) >= 0) {
2590 lseek64(m_h, orig, SEEK_SET);
2591 m_state = state_t::ok;
2592 return;
2593 }
2594 lseek64(m_h, orig, SEEK_SET);
2595 }
2596#endif
2597 m_state = state_t::fail;
2598 }
2599
2600 virtual fsize_t size()
2601 {
2602#ifdef _WIN32
2603 LARGE_INTEGER li;
2604 li.LowPart = GetFileSize(m_h, (LPDWORD)&li.HighPart);
2605 if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR)
2606 li.QuadPart = -1;
2607 return li.QuadPart;
2608#else
2609 off64_t length = -1, orig = lseek64(m_h, 0, SEEK_CUR);
2610 if (orig >= 0) {
2611 length = lseek64(m_h, 0, SEEK_END);
2612 lseek64(m_h, orig, SEEK_SET);
2613 }
2614 return length;
2615#endif
2616 }
2617
2618 virtual void truncate()
2619 {
2620#ifdef _WIN32
2621 if (SetEndOfFile(m_h)) {
2622 m_state = state_t::ok;
2623 return;
2624 }
2625#else
2626 off64_t length = lseek64(m_h, 0, SEEK_CUR);
2627 if (length >= 0 && ftruncate64(m_h, length) >= 0) {
2628 m_state = state_t::ok;
2629 return;
2630 }
2631#endif
2632 m_state = state_t::fail;
2633 }
2634
2635#ifdef _WIN32
2636 static inline time_point ft2tp(_In_ const FILETIME& ft)
2637 {
2638#if _HAS_CXX20
2639 uint64_t t = (static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
2640#else
2641 uint64_t t = ((static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) - 116444736000000000ll;
2642#endif
2643 return time_point(time_point::duration(t));
2644 }
2645
2646 static inline void tp2ft(_In_ time_point tp, _Out_ FILETIME& ft)
2647 {
2648#if _HAS_CXX20
2649 uint64_t t = tp.time_since_epoch().count();
2650#else
2651 uint64_t t = tp.time_since_epoch().count() + 116444736000000000ll;
2652#endif
2653 ft.dwHighDateTime = static_cast<DWORD>((t >> 32) & 0xffffffff);
2654 ft.dwLowDateTime = static_cast<DWORD>(t & 0xffffffff);
2655 }
2656#endif
2657
2658 virtual time_point ctime() const
2659 {
2660#ifdef _WIN32
2661 FILETIME ft;
2662 if (GetFileTime(m_h, &ft, nullptr, nullptr))
2663 return ft2tp(ft);
2664#endif
2665 return time_point::min();
2666 }
2667
2668 virtual time_point atime() const
2669 {
2670#ifdef _WIN32
2671 FILETIME ft;
2672 if (GetFileTime(m_h, nullptr, &ft, nullptr))
2673 return ft2tp(ft);
2674#else
2675 struct stat buf;
2676 if (fstat(m_h, &buf) >= 0)
2677 return clock::from_time_t(buf.st_atime);
2678#endif
2679 return time_point::min();
2680 }
2681
2682 virtual time_point mtime() const
2683 {
2684#ifdef _WIN32
2685 FILETIME ft;
2686 if (GetFileTime(m_h, nullptr, nullptr, &ft))
2687 return ft2tp(ft);
2688#else
2689 struct stat buf;
2690 if (fstat(m_h, &buf) >= 0)
2691 return clock::from_time_t(buf.st_mtime);
2692#endif
2693 return time_point::min();
2694 }
2695
2696 virtual void set_ctime(time_point date)
2697 {
2698 assert(m_h != invalid_handle);
2699#ifdef _WIN32
2700 FILETIME ft;
2701 tp2ft(date, ft);
2702 if (SetFileTime(m_h, &ft, nullptr, nullptr))
2703 return;
2704#endif
2705 throw std::runtime_error("failed to set file ctime");
2706 }
2707
2708 virtual void set_atime(time_point date)
2709 {
2710 assert(m_h != invalid_handle);
2711#ifdef _WIN32
2712 FILETIME ft;
2713 tp2ft(date, ft);
2714 if (SetFileTime(m_h, nullptr, &ft, nullptr))
2715 return;
2716#else
2717 struct timespec ts[2] = {
2718 { date.time_since_epoch().count(), 0 },
2719 { 0, UTIME_OMIT },
2720 };
2721 if (futimens(m_h, ts) >= 0)
2722 return;
2723#endif
2724 throw std::runtime_error("failed to set file atime");
2725 }
2726
2727 virtual void set_mtime(time_point date)
2728 {
2729#ifdef _WIN32
2730 FILETIME ft;
2731 tp2ft(date, ft);
2732 if (SetFileTime(m_h, nullptr, nullptr, &ft))
2733 return;
2734#else
2735 struct timespec ts[2] = {
2736 { 0, UTIME_OMIT },
2737 { date.time_since_epoch().count(), 0 },
2738 };
2739 if (futimens(m_h, ts) >= 0)
2740 return;
2741#endif
2742 throw std::runtime_error("failed to set file mtime");
2743 }
2744 };
2745#pragma warning(pop)
2746
2750 class cached_file : public cache
2751 {
2752 public:
2753 cached_file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok, _In_ size_t cache_size = default_cache_size) :
2754 cache(cache_size),
2755 m_source(h, state)
2756 {
2757 init(m_source);
2758 }
2759
2767 cached_file(_In_z_ const schar_t* filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) :
2768 cache(cache_size),
2769 m_source(filename, mode & mode_for_writing ? mode | mode_for_reading : mode)
2770 {
2771 init(m_source);
2772 }
2773
2774 virtual ~cached_file()
2775 {
2776 done();
2777 }
2778
2785 void open(_In_z_ const schar_t* filename, _In_ int mode)
2786 {
2787 invalidate_cache();
2788 if (!ok()) _Unlikely_{
2789 m_state = state_t::fail;
2790 return;
2791 }
2792 m_source.open(filename, mode & mode_for_writing ? mode | mode_for_reading : mode);
2793 if (m_source.ok()) {
2794#if SET_FILE_OP_TIMES
2795 m_atime = m_source.atime();
2796 m_mtime = m_source.mtime();
2797#endif
2798 m_offset = m_source.tell();
2799 m_state = state_t::ok;
2800 return;
2801 }
2802 m_state = state_t::fail;
2803 }
2804
2805 protected:
2806 file m_source;
2807 };
2808
2813 {
2814 public:
2815 memory_file(_In_ state_t state = state_t::ok) :
2816 basic(state),
2817 m_data(nullptr),
2818 m_offset(0),
2819 m_size(0),
2820 m_reserved(0),
2821 m_manage(true)
2822 {
2823#if SET_FILE_OP_TIMES
2824 m_ctime = m_atime = m_mtime = time_point::now();
2825#endif
2826 }
2827
2834 memory_file(_In_ size_t size, _In_ state_t state = state_t::ok) :
2835 basic(state),
2836 m_data(reinterpret_cast<uint8_t*>(malloc(size))),
2837 m_offset(0),
2838 m_size(0),
2840 m_manage(true)
2841 {
2842 if (!m_data)
2843 throw std::bad_alloc();
2844#if SET_FILE_OP_TIMES
2845 m_ctime = m_atime = m_mtime = time_point::now();
2846#endif
2847 }
2848
2858 memory_file(_Inout_ void* data, _In_ size_t size, _In_ size_t reserved, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
2859 basic(state),
2860 m_data(reinterpret_cast<uint8_t*>(data)),
2861 m_offset(0),
2862 m_size(size),
2863 m_reserved(reserved),
2864 m_manage(manage)
2865 {
2866 assert(data || !size);
2867 assert(reserved >= size);
2868#if SET_FILE_OP_TIMES
2869 m_ctime = m_atime = m_mtime = time_point::now();
2870#endif
2871 }
2872
2881 memory_file(_Inout_ void* data, _In_ size_t size, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
2882 memory_file(data, size, size, manage, state)
2883 {}
2884
2891 memory_file(_In_z_ const schar_t* filename, _In_ int mode) : memory_file()
2892 {
2893 load(filename, mode);
2894 }
2895
2896 virtual ~memory_file()
2897 {
2898 if (m_manage && m_data)
2899 free(m_data);
2900 }
2901
2908 void reserve(_In_ size_t required, _In_ bool tight = false) noexcept
2909 {
2910 if (required <= m_reserved && (!tight || required >= m_reserved)) {
2911 m_state = state_t::ok;
2912 return;
2913 }
2914 if (!m_manage) {
2915 m_state = state_t::fail;
2916 return;
2917 }
2918 size_t reserved = tight ? required : ((required + required / 4 + (default_block_size - 1)) / default_block_size) * default_block_size;
2919 auto data = reinterpret_cast<uint8_t*>(realloc(m_data, reserved));
2920 if (!data && reserved) _Unlikely_ {
2921 m_state = state_t::fail;
2922 return;
2923 }
2924 m_data = data;
2925 if (reserved < m_size)
2926 m_size = reserved;
2927 m_reserved = reserved;
2928 m_state = state_t::ok;
2929 }
2930
2937 void load(_In_z_ const schar_t* filename, _In_ int mode)
2938 {
2939 file f(filename, (mode & ~hint_random_access) | mode_for_reading | hint_sequential_access);
2940 if (!f.ok()) {
2941 m_state = state_t::fail;
2942 return;
2943 }
2944 fsize_t size = f.size();
2945 if (size > SIZE_MAX) {
2946 m_state = state_t::fail;
2947 return;
2948 }
2949 reserve(static_cast<size_t>(size), true);
2950 if (!ok()) _Unlikely_ {
2951 return;
2952 }
2953 m_offset = m_size = 0;
2954 write_stream(f);
2955 if (ok())
2956 m_offset = 0;
2957#if SET_FILE_OP_TIMES
2958 m_ctime = f.ctime();
2959 m_atime = f.atime();
2960 m_mtime = f.mtime();
2961#endif
2962 }
2963
2970 void save(_In_z_ const schar_t* filename, _In_ int mode)
2971 {
2972 file f(filename, (mode & ~hint_random_access) | mode_for_writing | hint_sequential_access);
2973 if (!f.ok()) {
2974 m_state = state_t::fail;
2975 return;
2976 }
2977 f.write(m_data, m_size);
2978 if (!f.ok()) {
2979 m_state = state_t::fail;
2980 return;
2981 }
2982 f.truncate();
2983#if SET_FILE_OP_TIMES
2984 f.set_ctime(m_ctime);
2985 f.set_atime(m_atime);
2986 f.set_mtime(m_mtime);
2987#endif
2988 }
2989
2993 inline const void* data() const { return m_data; }
2994
2995 virtual _Success_(return != 0 || length == 0) size_t read(
2996 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2997 {
2998 assert(data || !length);
2999#if SET_FILE_OP_TIMES
3000 m_atime = time_point::now();
3001#endif
3002 size_t available = m_size - m_offset;
3003 if (length <= available) {
3004 memcpy(data, m_data + m_offset, length);
3005 m_offset += length;
3006 m_state = state_t::ok;
3007 return length;
3008 }
3009 if (length && !available) {
3010 m_state = state_t::eof;
3011 return 0;
3012 }
3013 memcpy(data, m_data + m_offset, available);
3014 m_offset += available;
3015 m_state = state_t::ok;
3016 return available;
3017 }
3018
3033 template <class T>
3035 {
3036#if SET_FILE_OP_TIMES
3037 m_atime = time_point::now();
3038#endif
3039 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3040 data = 0;
3041 return *this;
3042 }
3043 size_t end_offset = m_offset + sizeof(T);
3044 if (end_offset <= m_size) {
3045 data = LE2HE(*reinterpret_cast<T*>(m_data + m_offset));
3046 m_offset = end_offset;
3047#if !CHECK_STREAM_STATE
3048 m_state = state_t::ok;
3049#endif
3050 }
3051 else {
3052 data = 0;
3053 m_offset = m_size;
3054 m_state = state_t::eof;
3055 }
3056 return *this;
3057 }
3058
3073 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
3074 memory_file& read_str(_Inout_ std::basic_string<_Elem, _Traits, _Ax>&data)
3075 {
3076#if SET_FILE_OP_TIMES
3077 m_atime = time_point::now();
3078#endif
3079 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3080 data.clear();
3081 return *this;
3082 }
3083 size_t end_offset = m_offset + sizeof(uint32_t);
3084 if (end_offset <= m_size) {
3085 uint32_t num_chars = LE2HE(*reinterpret_cast<uint32_t*>(m_data + m_offset));
3086 m_offset = end_offset;
3087 end_offset = stdex::add(m_offset, stdex::mul(num_chars, sizeof(_Elem)));
3088 _Elem* start = reinterpret_cast<_Elem*>(m_data + m_offset);
3089 if (end_offset <= m_size) {
3090 data.assign(start, start + num_chars);
3091 m_offset = end_offset;
3092#if !CHECK_STREAM_STATE
3093 m_state = state_t::ok;
3094#endif
3095 return *this;
3096 }
3097 if (end_offset <= m_size)
3098 data.assign(start, reinterpret_cast<_Elem*>(m_data + m_size));
3099 }
3100 m_offset = m_size;
3101 m_state = state_t::eof;
3102 return *this;
3103 }
3104
3105 virtual _Success_(return != 0) size_t write(
3106 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3107 {
3108 assert(data || !length);
3109#if SET_FILE_OP_TIMES
3110 m_atime = m_mtime = time_point::now();
3111#endif
3112 size_t end_offset = m_offset + length;
3113 if (end_offset > m_reserved) {
3114 reserve(end_offset);
3115 if (!ok()) _Unlikely_
3116 return 0;
3117 }
3118 memcpy(m_data + m_offset, data, length);
3119 m_offset = end_offset;
3120 if (m_offset > m_size)
3121 m_size = m_offset;
3122 m_state = state_t::ok;
3123 return length;
3124 }
3125
3129 void write_byte(_In_ uint8_t byte, _In_ size_t amount = 1)
3130 {
3131#if SET_FILE_OP_TIMES
3132 m_atime = m_mtime = time_point::now();
3133#endif
3134 size_t end_offset = m_offset + amount;
3135 if (end_offset > m_reserved) {
3136 reserve(end_offset);
3137 if (!ok()) _Unlikely_
3138 return;
3139 }
3140 memset(m_data + m_offset, byte, amount);
3141 m_offset = end_offset;
3142 if (m_offset > m_size)
3143 m_size = m_offset;
3144 m_state = state_t::ok;
3145 }
3146
3161 template <class T>
3162 inline memory_file& write_data(const T data)
3163 {
3164#if SET_FILE_OP_TIMES
3165 m_atime = m_mtime = time_point::now();
3166#endif
3167 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3168 return *this;
3169 size_t end_offset = m_offset + sizeof(T);
3170 if (end_offset > m_reserved) {
3171 reserve(end_offset);
3172 if (!ok()) _Unlikely_
3173 return *this;
3174 }
3175 (*reinterpret_cast<T*>(m_data + m_offset)) = HE2LE(data);
3176 m_offset = end_offset;
3177 if (m_offset > m_size)
3178 m_size = m_offset;
3179#if !CHECK_STREAM_STATE
3180 m_state = state_t::ok;
3181#endif
3182 return *this;
3183 }
3184
3199 template <class T>
3200 inline memory_file& write_str(_In_z_ const T * data)
3201 {
3202#if SET_FILE_OP_TIMES
3203 m_atime = m_mtime = time_point::now();
3204#endif
3205 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3206 return *this;
3207 size_t num_chars = stdex::strlen(data);
3208 if (num_chars > UINT32_MAX)
3209 throw std::invalid_argument("string too long");
3210 size_t size_chars = num_chars * sizeof(T);
3211 size_t size = sizeof(uint32_t) + size_chars;
3212 size_t end_offset = m_offset + size;
3213 if (end_offset > m_reserved) {
3214 reserve(end_offset);
3215 if (!ok()) _Unlikely_
3216 return *this;
3217 }
3218 auto p = m_data + m_offset;
3219 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3220 memcpy(p + sizeof(uint32_t), data, size_chars);
3221 m_offset = end_offset;
3222 if (m_offset > m_size)
3223 m_size = m_offset;
3224#if !CHECK_STREAM_STATE
3225 m_state = state_t::ok;
3226#endif
3227 return *this;
3228 }
3229
3235 size_t write_stream(_Inout_ basic & stream, _In_ size_t amount = SIZE_MAX)
3236 {
3237#if SET_FILE_OP_TIMES
3238 m_atime = m_mtime = time_point::now();
3239#endif
3240 size_t num_read, dst_offset = m_offset, dst_size = m_offset;
3241 size_t num_copied = 0, to_write = amount;
3242 m_state = state_t::ok;
3243 if (amount != SIZE_MAX) {
3244 dst_size = stdex::add(dst_size, amount);
3245 reserve(dst_size);
3246 if (!ok()) _Unlikely_
3247 return 0;
3248 while (to_write) {
3249 num_read = stream.read(m_data + dst_offset, to_write);
3250 dst_size = dst_offset += num_read;
3251 num_copied += num_read;
3252 to_write -= num_read;
3253 if (!stream.ok()) {
3254 if (stream.state() != state_t::eof)
3255 m_state = state_t::fail;
3256 break;
3257 }
3258 };
3259 }
3260 else {
3261 size_t block_size;
3262 while (to_write) {
3263 block_size = std::min(to_write, default_block_size);
3264 dst_size = stdex::add(dst_size, block_size);
3265 reserve(dst_size);
3266 if (!ok()) _Unlikely_
3267 break;
3268 num_read = stream.read(m_data + dst_offset, block_size);
3269 dst_size = dst_offset += num_read;
3270 num_copied += num_read;
3271 to_write -= num_read;
3272 if (!stream.ok()) {
3273 if (stream.state() != state_t::eof)
3274 m_state = state_t::fail;
3275 break;
3276 }
3277 };
3278 }
3279 m_offset = dst_offset;
3280 if (m_offset > m_size)
3281 m_size = m_offset;
3282 return num_copied;
3283 }
3284
3285 virtual void close()
3286 {
3287 if (m_manage && m_data)
3288 free(m_data);
3289 m_data = nullptr;
3290 m_manage = true;
3291 m_offset = 0;
3292 m_size = m_reserved = 0;
3293#if SET_FILE_OP_TIMES
3294 m_ctime = m_atime = m_mtime = time_point::min();
3295#endif
3296 m_state = state_t::ok;
3297 }
3298
3299 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
3300 {
3301 fpos_t target;
3302 switch (how) {
3303 case seek_t::beg: target = offset; break;
3304 case seek_t::cur: target = static_cast<fpos_t>(m_offset) + offset; break;
3305 case seek_t::end: target = static_cast<fpos_t>(m_size) + offset; break;
3306 default: throw std::invalid_argument("unknown seek origin");
3307 }
3308 if (target <= SIZE_MAX) {
3309 m_state = state_t::ok;
3310 return m_offset = static_cast<size_t>(target);
3311 }
3312 m_state = state_t::fail;
3313 return fpos_max;
3314 }
3315
3316 virtual fpos_t tell() const
3317 {
3318 return m_offset;
3319 }
3320
3321 virtual fsize_t size()
3322 {
3323 return m_size;
3324 }
3325
3326 virtual void truncate()
3327 {
3328#if SET_FILE_OP_TIMES
3329 m_atime = m_mtime = time_point::now();
3330#endif
3331 m_size = m_offset;
3333 }
3334
3335#if SET_FILE_OP_TIMES
3336 virtual time_point ctime() const
3337 {
3338 return m_ctime;
3339 }
3340
3341 virtual time_point atime() const
3342 {
3343 return m_atime;
3344 }
3345
3346 virtual time_point mtime() const
3347 {
3348 return m_mtime;
3349 }
3350
3351 virtual void set_ctime(time_point date)
3352 {
3353 m_ctime = date;
3354 }
3355
3356 virtual void set_atime(time_point date)
3357 {
3358 m_atime = date;
3359 }
3360
3361 virtual void set_mtime(time_point date)
3362 {
3363 m_mtime = date;
3364 }
3365#endif
3366
3367 protected:
3375 template <class T>
3376 inline void set(_In_ fpos_t offset, _In_ const T data)
3377 {
3378#if SET_FILE_OP_TIMES
3379 m_atime = m_mtime = time_point::now();
3380#endif
3381 assert(offset + sizeof(T) < m_size);
3382 (*reinterpret_cast<T*>(m_data + offset)) = HE2LE(data);
3383 }
3384
3385 public:
3386 inline void set(_In_ fpos_t offset, _In_ const int8_t data) { set<int8_t>(offset, data); }
3387 inline void set(_In_ fpos_t offset, _In_ const int16_t data) { set<int16_t>(offset, data); }
3388 inline void set(_In_ fpos_t offset, _In_ const int32_t data) { set<int32_t>(offset, data); }
3389 inline void set(_In_ fpos_t offset, _In_ const int64_t data) { set<int64_t>(offset, data); }
3390 inline void set(_In_ fpos_t offset, _In_ const uint8_t data) { set<uint8_t>(offset, data); }
3391 inline void set(_In_ fpos_t offset, _In_ const uint16_t data) { set<uint16_t>(offset, data); }
3392 inline void set(_In_ fpos_t offset, _In_ const uint32_t data) { set<uint32_t>(offset, data); }
3393 inline void set(_In_ fpos_t offset, _In_ const uint64_t data) { set<uint64_t>(offset, data); }
3394 inline void set(_In_ fpos_t offset, _In_ const float data) { set<float>(offset, data); }
3395 inline void set(_In_ fpos_t offset, _In_ const double data) { set<double>(offset, data); }
3396 inline void set(_In_ fpos_t offset, _In_ const char data) { set<char>(offset, data); }
3397#ifdef _NATIVE_WCHAR_T_DEFINED
3398 inline void set(_In_ fpos_t offset, _In_ const wchar_t data) { set<wchar_t>(offset, data); }
3399#endif
3400
3408 protected:
3409 template <class T>
3410 inline void get(_In_ fpos_t offset, _Out_ T & data)
3411 {
3412 assert(offset + sizeof(T) < m_size);
3413 data = LE2HE(*(T*)(m_data + offset));
3414#if SET_FILE_OP_TIMES
3415 m_atime = time_point::now();
3416#endif
3417 }
3418
3419 public:
3420 inline void get(_In_ fpos_t offset, _Out_ int8_t & data) { get<int8_t>(offset, data); }
3421 inline void get(_In_ fpos_t offset, _Out_ int16_t & data) { get<int16_t>(offset, data); }
3422 inline void get(_In_ fpos_t offset, _Out_ int32_t & data) { get<int32_t>(offset, data); }
3423 inline void get(_In_ fpos_t offset, _Out_ int64_t & data) { get<int64_t>(offset, data); }
3424 inline void get(_In_ fpos_t offset, _Out_ uint8_t & data) { get<uint8_t>(offset, data); }
3425 inline void get(_In_ fpos_t offset, _Out_ uint16_t & data) { get<uint16_t>(offset, data); }
3426 inline void get(_In_ fpos_t offset, _Out_ uint32_t & data) { get<uint32_t>(offset, data); }
3427 inline void get(_In_ fpos_t offset, _Out_ uint64_t & data) { get<uint64_t>(offset, data); }
3428 inline void get(_In_ fpos_t offset, _Out_ float& data) { get<float>(offset, data); }
3429 inline void get(_In_ fpos_t offset, _Out_ double& data) { get<double>(offset, data); }
3430 inline void get(_In_ fpos_t offset, _Out_ char& data) { get<char>(offset, data); }
3431#ifdef _NATIVE_WCHAR_T_DEFINED
3432 inline void get(_In_ fpos_t offset, _Out_ wchar_t& data) { get<wchar_t>(offset, data); }
3433#endif
3434
3435 inline memory_file& operator <<(_In_ const int8_t data) { return write_data(data); }
3436 inline memory_file& operator >>(_Out_ int8_t & data) { return read_data(data); }
3437 inline memory_file& operator <<(_In_ const int16_t data) { return write_data(data); }
3438 inline memory_file& operator >>(_Out_ int16_t & data) { return read_data(data); }
3439 inline memory_file& operator <<(_In_ const int32_t data) { return write_data(data); }
3440 inline memory_file& operator >>(_Out_ int32_t & data) { return read_data(data); }
3441 inline memory_file& operator <<(_In_ const int64_t data) { return write_data(data); }
3442 inline memory_file& operator >>(_Out_ int64_t & data) { return read_data(data); }
3443 inline memory_file& operator <<(_In_ const uint8_t data) { return write_data(data); }
3444 inline memory_file& operator >>(_Out_ uint8_t & data) { return read_data(data); }
3445 inline memory_file& operator <<(_In_ const uint16_t data) { return write_data(data); }
3446 inline memory_file& operator >>(_Out_ uint16_t & data) { return read_data(data); }
3447 inline memory_file& operator <<(_In_ const uint32_t data) { return write_data(data); }
3448 inline memory_file& operator >>(_Out_ uint32_t & data) { return read_data(data); }
3449 inline memory_file& operator <<(_In_ const uint64_t data) { return write_data(data); }
3450 inline memory_file& operator >>(_Out_ uint64_t & data) { return read_data(data); }
3451 inline memory_file& operator <<(_In_ const float data) { return write_data(data); }
3452 inline memory_file& operator >>(_Out_ float& data) { return read_data(data); }
3453 inline memory_file& operator <<(_In_ const double data) { return write_data(data); }
3454 inline memory_file& operator >>(_Out_ double& data) { return read_data(data); }
3455 inline memory_file& operator <<(_In_ const char data) { return write_data(data); }
3456 inline memory_file& operator >>(_Out_ char& data) { return read_data(data); }
3457#ifdef _NATIVE_WCHAR_T_DEFINED
3458 inline memory_file& operator <<(_In_ const wchar_t data) { return write_data(data); }
3459 inline memory_file& operator >>(_Out_ wchar_t& data) { return read_data(data); }
3460#endif
3461 template <class T>
3462 inline memory_file& operator <<(_In_ const T * data) { return write_str(data); }
3463 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
3464 inline memory_file& operator >>(_Inout_ std::basic_string<_Elem, _Traits, _Ax>&data) { return read_str(data); }
3465
3466 protected:
3467 uint8_t* m_data;
3469 size_t m_offset;
3470 size_t m_size;
3471 size_t m_reserved;
3472#if SET_FILE_OP_TIMES
3473 time_point
3474 m_ctime,
3475 m_atime,
3476 m_mtime;
3477#endif
3478 };
3479
3483 class fifo : public basic {
3484 public:
3485 fifo() :
3486 m_offset(0),
3487 m_size(0),
3488 m_head(nullptr),
3489 m_tail(nullptr)
3490 {}
3491
3492 virtual ~fifo()
3493 {
3494 while (m_head) {
3495 auto p = m_head;
3496 m_head = p->next;
3497 delete p;
3498 }
3499 }
3500
3501#pragma warning(suppress: 6101) // See [2] below
3502 virtual _Success_(return != 0 || length == 0) size_t read(
3503 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
3504 {
3505 assert(data || !length);
3506 for (size_t to_read = length;;) {
3507 if (!m_head) _Unlikely_ {
3508 // [1] Code analysis misses length - to_read bytes were written to data in previous loop iterations.
3509 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
3510 return length - to_read;
3511 }
3512 size_t remaining = m_head->size - m_offset;
3513 if (remaining > to_read) {
3514 memcpy(data, m_head->data + m_offset, to_read);
3515 m_offset += to_read;
3516 m_size -= to_read;
3517 m_state = state_t::ok;
3518 return length;
3519 }
3520 memcpy(data, m_head->data + m_offset, remaining);
3521 m_offset = 0;
3522 m_size -= remaining;
3523 reinterpret_cast<uint8_t*&>(data) += remaining;
3524 to_read -= remaining;
3525 auto p = m_head;
3526 m_head = p->next;
3527 delete p;
3528 }
3529 }
3530
3531 virtual _Success_(return != 0) size_t write(
3532 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3533 {
3534 assert(data || !length);
3535 try {
3536 std::unique_ptr<node_t> n(reinterpret_cast<node_t*>(new uint8_t[sizeof(node_t) + length]));
3537 n->next = nullptr;
3538 n->size = length;
3539 memcpy(n->data, data, length);
3540 m_size += length;
3541 if (m_head)
3542 m_tail = m_tail->next = n.release();
3543 else
3544 m_head = m_tail = n.release();
3545 m_state = state_t::ok;
3546 return length;
3547 }
3548 catch (const std::bad_alloc&) {
3549 m_state = state_t::fail;
3550 return 0;
3551 }
3552 }
3553
3554 virtual void close()
3555 {
3556 m_size = m_offset = 0;
3557 while (m_head) {
3558 auto p = m_head;
3559 m_head = p->next;
3560 delete p;
3561 }
3562 m_state = state_t::ok;
3563 }
3564
3568 inline size_t size() const { return m_size; };
3569
3570 protected:
3571 size_t m_offset, m_size;
3572 struct node_t {
3573 node_t* next;
3574 size_t size;
3575#pragma warning(suppress:4200)
3576 uint8_t data[0];
3577 } *m_head, * m_tail;
3578 };
3579
3583 class diag_file : public basic_file {
3584 public:
3585 diag_file(_In_count_(num_files) basic_file* const* files, _In_ size_t num_files) :
3586 basic(num_files ? files[0]->state() : state_t::fail),
3587 m_files(files, files + num_files)
3588 {
3589 }
3590
3591 virtual _Success_(return != 0 || length == 0) size_t read(
3592 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
3593 {
3594 assert(data || !length);
3595 if (m_files.empty()) {
3596 m_state = state_t::fail;
3597 return 0;
3598 }
3599 size_t result = m_files[0]->read(data, length);
3600 _Analysis_assume_(result <= length);
3601 m_state = m_files[0]->state();
3602 if (length > m_tmp.size())
3603 m_tmp.resize(length);
3604 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
3605 if (m_files[i]->read(m_tmp.data(), length) != result ||
3606 memcmp(m_tmp.data(), data, result))
3607 throw std::runtime_error("read mismatch");
3608 if (m_files[i]->state() != m_state)
3609 throw std::runtime_error("state mismatch");
3610 }
3611 return result;
3612 }
3613
3614 virtual _Success_(return != 0) size_t write(
3615 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3616 {
3617 if (m_files.empty()) {
3618 m_state = state_t::fail;
3619 return 0;
3620 }
3621 size_t result = m_files[0]->write(data, length);
3622 m_state = m_files[0]->state();
3623 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
3624 if (m_files[i]->write(data, length) != result)
3625 throw std::runtime_error("write mismatch");
3626 if (m_files[i]->state() != m_state)
3627 throw std::runtime_error("state mismatch");
3628 }
3629 return result;
3630 }
3631
3632 virtual void flush()
3633 {
3634 if (m_files.empty()) {
3635 m_state = state_t::ok;
3636 return;
3637 }
3638 m_files[0]->flush();
3639 m_state = m_files[0]->state();
3640 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
3641 m_files[i]->flush();
3642 if (m_files[i]->state() != m_state)
3643 throw std::runtime_error("state mismatch");
3644 }
3645 }
3646
3647 virtual void close()
3648 {
3649 if (m_files.empty()) {
3650 m_state = state_t::ok;
3651 return;
3652 }
3653 m_files[0]->close();
3654 m_state = m_files[0]->state();
3655 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
3656 m_files[i]->close();
3657 if (m_files[i]->state() != m_state)
3658 throw std::runtime_error("state mismatch");
3659 }
3660 m_tmp.clear();
3661 m_tmp.shrink_to_fit();
3662 }
3663
3664 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
3665 {
3666 if (m_files.empty()) {
3667 m_state = state_t::fail;
3668 return fpos_max;
3669 }
3670 fpos_t result = m_files[0]->seek(offset, how);
3671 m_state = m_files[0]->state();
3672 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
3673 if (m_files[i]->seek(offset, how) != result)
3674 throw std::runtime_error("seek mismatch");
3675 if (m_files[i]->state() != m_state)
3676 throw std::runtime_error("state mismatch");
3677 }
3678 return result;
3679 }
3680
3681 virtual fpos_t tell() const
3682 {
3683 if (m_files.empty())
3684 return fpos_max;
3685 fpos_t result = m_files[0]->tell();
3686 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
3687 if (m_files[i]->tell() != result)
3688 throw std::runtime_error("tell mismatch");
3689 }
3690 return result;
3691 }
3692
3693 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
3694 {
3695 if (m_files.empty())
3696 m_state = state_t::fail;
3697 m_files[0]->lock(offset, length);
3698 m_state = m_files[0]->state();
3699 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
3700 m_files[i]->lock(offset, length);
3701 if (m_files[i]->state() != m_state)
3702 throw std::runtime_error("state mismatch");
3703 }
3704 }
3705
3706 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
3707 {
3708 if (m_files.empty())
3709 m_state = state_t::fail;
3710 m_files[0]->unlock(offset, length);
3711 m_state = m_files[0]->state();
3712 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
3713 m_files[i]->unlock(offset, length);
3714 if (m_files[i]->state() != m_state)
3715 throw std::runtime_error("state mismatch");
3716 }
3717 }
3718
3719 virtual fsize_t size()
3720 {
3721 if (m_files.empty()) {
3722 m_state = state_t::fail;
3723 return 0;
3724 }
3725 fsize_t result = m_files[0]->size();
3726 m_state = m_files[0]->state();
3727 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
3728 if (m_files[i]->size() != result)
3729 throw std::runtime_error("size mismatch");
3730 if (m_files[i]->state() != m_state)
3731 throw std::runtime_error("state mismatch");
3732 }
3733 return result;
3734 }
3735
3736 virtual void truncate()
3737 {
3738 if (m_files.empty())
3739 m_state = state_t::fail;
3740 m_files[0]->truncate();
3741 m_state = m_files[0]->state();
3742 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
3743 m_files[i]->truncate();
3744 if (m_files[i]->state() != m_state)
3745 throw std::runtime_error("state mismatch");
3746 }
3747 }
3748
3749 protected:
3750 std::vector<basic_file*> m_files;
3751 std::vector<uint8_t> m_tmp;
3752 };
3753 }
3754}
Provides read-ahead stream capability.
Definition stream.hpp:1118
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:1132
Provides write-back stream capability.
Definition stream.hpp:1186
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1223
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:1199
Basic seekable stream operations.
Definition stream.hpp:696
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:739
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:786
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:755
virtual void truncate()=0
Sets file size - truncates the remainder of file content from the current file position to the end of...
charset_id read_charset(charset_id default_charset=charset_id::system)
Attempts to detect textfile charset based on UTF16 or UTF8 BOM.
Definition stream.hpp:859
fpos_t seekbeg(fpos_t offset)
Seeks to absolute file position.
Definition stream.hpp:723
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:698
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:828
fpos_t seekcur(foff_t offset)
Seeks to relative from current file position.
Definition stream.hpp:730
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:794
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:810
virtual fsize_t size()=0
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:765
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:802
fpos_t seekend(foff_t offset)
Seeks to relative from end file position.
Definition stream.hpp:737
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:819
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:2064
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:2121
virtual void flush()
Persists volatile element data.
Definition stream.hpp:2179
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:2071
virtual void close()
Closes the stream.
Definition stream.hpp:2168
‍UTF-8 byte-order-mark
Definition stream.hpp:76
bool ok() const
Returns true if the stream state is clean i.e. previous operation was succesful.
Definition stream.hpp:170
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:600
size_t write_array(_In_reads_or_z_opt_(num_chars) const wchar_t *wstr, size_t num_chars, charset_id charset)
Writes array of characters to the stream.
Definition stream.hpp:430
state_t state() const
Returns stream state after last operation.
Definition stream.hpp:165
basic & read_str(std::basic_string< _Elem, _Traits, _Ax > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:474
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:586
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:613
virtual void flush()
Persists volatile element data.
Definition stream.hpp:121
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:137
virtual void close()
Closes the stream.
Definition stream.hpp:129
uint8_t read_byte()
Reads one byte of data.
Definition stream.hpp:205
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:179
size_t write_sprintf(_Printf_format_string_params_(2) const char *format, locale_t locale,...)
Writes formatted string to the stream.
Definition stream.hpp:572
size_t readln_and_attach(std::basic_string< wchar_t, _Traits, _Ax > &wstr, charset_id charset)
Reads stream to the end-of-line or end-of-file and append to str.
Definition stream.hpp:363
size_t readln(std::basic_string< char, _Traits, _Ax > &str)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:301
size_t readln_and_attach(std::basic_string< _Elem, _Traits, _Ax > &str)
Reads stream to the end-of-line or end-of-file and append to str.
Definition stream.hpp:342
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:378
basic & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:499
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:91
size_t readln(std::basic_string< wchar_t, _Traits, _Ax > &wstr)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:313
void write_charset(charset_id charset)
Writes UTF8 or UTF-16 byte-order-mark.
Definition stream.hpp:559
size_t readln(std::basic_string< wchar_t, _Traits, _Ax > &wstr, charset_id charset)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:325
size_t write_array(const wchar_t *wstr, charset_id charset)
Writes array of characters to the stream.
Definition stream.hpp:409
basic & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:282
fsize_t write_stream(basic &stream, fsize_t amount=fsize_max)
Writes content of another stream.
Definition stream.hpp:534
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:109
size_t write_array(const std::basic_string< wchar_t, _Traits, _Ax > &wstr, charset_id charset)
Writes array of characters to the stream.
Definition stream.hpp:451
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:396
void write_byte(uint8_t byte, fsize_t amount=1)
Writes a byte of data.
Definition stream.hpp:216
basic & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:254
Buffered read/write stream.
Definition stream.hpp:1257
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1367
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:1285
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:1321
Buffered OS data stream (file, pipe, socket...)
Definition stream.hpp:2193
Cached file.
Definition stream.hpp:1684
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:1936
fpos_t m_offset
Logical absolute file position.
Definition stream.hpp:2052
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:1915
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:1741
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:1941
virtual fsize_t size()
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:1908
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:1902
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:1950
virtual void close()
Closes the stream.
Definition stream.hpp:1856
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:1972
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:1896
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1865
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:1803
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:1959
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:1891
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:1964
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:1876
Cached file-system file.
Definition stream.hpp:2751
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2785
cached_file(const schar_t *filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:2767
Modifies data on the fly when reading from/writing to a source stream.
Definition stream.hpp:889
virtual void flush()
Persists volatile element data.
Definition stream.hpp:935
virtual void close()
Closes the stream.
Definition stream.hpp:929
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:913
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:921
Compares multiple files to perform the same.
Definition stream.hpp:3583
virtual fsize_t size()
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:3719
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:3736
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:3614
virtual void close()
Closes the stream.
Definition stream.hpp:3647
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:3693
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:3706
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3664
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:3681
virtual void flush()
Persists volatile element data.
Definition stream.hpp:3632
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:3591
In-memory FIFO queue.
Definition stream.hpp:3483
virtual void close()
Closes the stream.
Definition stream.hpp:3554
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:3531
size_t size() const
Returns total size of pending data in the queue.
Definition stream.hpp:3568
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:3502
Limits file reading/writing to a predefined window.
Definition stream.hpp:1574
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:1667
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1617
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:1630
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:1623
virtual fsize_t size()
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:1662
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:1597
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:1642
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:1583
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:1652
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:1636
virtual void close()
Closes the stream.
Definition stream.hpp:1611
File-system file.
Definition stream.hpp:2434
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:2682
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2575
file(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2444
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:2696
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2668
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2455
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:2727
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:2708
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2552
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2618
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2658
virtual fsize_t size()
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:2600
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2513
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:2534
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1415
fsize_t read_limit
Number of bytes left that may be read from the stream.
Definition stream.hpp:1475
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:1423
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:1448
fsize_t write_limit
Number of bytes left, that can be written to the stream.
Definition stream.hpp:1476
In-memory file.
Definition stream.hpp:2813
memory_file(const schar_t *filename, int mode)
Loads content from file-system file.
Definition stream.hpp:2891
size_t m_size
file size
Definition stream.hpp:3470
void get(fpos_t offset, T &data)
Reads data from specified file location This does not move file pointer. It checks for data size asse...
Definition stream.hpp:3410
size_t write_stream(basic &stream, size_t amount=SIZE_MAX)
Writes content of another stream.
Definition stream.hpp:3235
uint8_t * m_data
file data
Definition stream.hpp:3467
memory_file & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:3034
virtual void close()
Closes the stream.
Definition stream.hpp:3285
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:2995
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:3316
size_t m_reserved
reserved file size
Definition stream.hpp:3471
memory_file(size_t size, state_t state=state_t::ok)
Creates an empty file of reserved size.
Definition stream.hpp:2834
void reserve(size_t required, bool tight=false) noexcept
Reallocates memory.
Definition stream.hpp:2908
memory_file & read_str(std::basic_string< _Elem, _Traits, _Ax > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:3074
void write_byte(uint8_t byte, size_t amount=1)
Writes a byte of data.
Definition stream.hpp:3129
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:3376
size_t m_offset
file pointer
Definition stream.hpp:3469
void save(const schar_t *filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:2970
void load(const schar_t *filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:2937
virtual fsize_t size()
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:3321
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3299
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:3326
memory_file & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:3162
memory_file & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3200
bool m_manage
may reallocate m_data?
Definition stream.hpp:3468
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:2881
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:3105
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:2858
const void * data() const
Returns pointer to data.
Definition stream.hpp:2993
Definition stream.hpp:1032
enum stdex::stream::replicator::worker::op_t op
Operation to perform.
size_t num_written
Number of bytes written.
Definition stream.hpp:1080
size_t length
Byte limit of data to write.
Definition stream.hpp:1079
const void * data
Data to write.
Definition stream.hpp:1078
Replicates writing of the same data to multiple streams.
Definition stream.hpp:949
void push_back(basic *source)
Adds stream on the list.
Definition stream.hpp:968
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1025
void remove(basic *source)
Removes stream from the list.
Definition stream.hpp:976
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:993
virtual void close()
Closes the stream.
Definition stream.hpp:1020
Limits reading from/writing to stream to a predefined window.
Definition stream.hpp:1483
fpos_t write_offset
Number of bytes to discard on write.
Definition stream.hpp:1567
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:1524
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:1491
fpos_t read_offset
Number of bytes to skip on read.
Definition stream.hpp:1566
Operating system object (file, pipe, anything with an OS handle etc.)
Definition system.hpp:78
virtual void close()
Closes object.
Definition system.hpp:119
Numerical interval.
Definition interval.hpp:18
bool contains(T x) const
Is value in interval?
Definition interval.hpp:70
bool empty() const
Is interval empty?
Definition interval.hpp:54
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:1392
Definition stream.hpp:2030
interval< fpos_t > region
valid data region
Definition stream.hpp:2038
Definition stream.hpp:3572