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