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