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