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