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