stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
stream.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2023-2024 Amebis
4*/
5
6#pragma once
7
8#include "assert.hpp"
9#include "compat.hpp"
10#include "endian.hpp"
11#include "interval.hpp"
12#include "locale.hpp"
13#include "math.hpp"
14#include "ring.hpp"
15#include "socket.hpp"
16#include "string.hpp"
17#include "unicode.hpp"
18#include <stdint.h>
19#include <stdlib.h>
20#if defined(_WIN32)
21#include "windows.h"
22#include <asptlb.h>
23#include <objidl.h>
24#else
25#include <fcntl.h>
26#include <unistd.h>
27#include <sys/stat.h>
28#endif
29#include <chrono>
30#include <condition_variable>
31#include <list>
32#include <memory>
33#include <set>
34#include <string>
35#include <thread>
36#include <vector>
37
38#if defined(__GNUC__)
39#pragma GCC diagnostic push
40#pragma GCC diagnostic ignored "-Wunknown-pragmas"
41#endif
42
43#if !defined(SET_FILE_OP_TIMES) && defined(RDAT_BELEZI_CAS_DOSTOPA_VER)
44#define SET_FILE_OP_TIMES 1
45#pragma message("RDAT_BELEZI_CAS_DOSTOPA_VER is deprecated. Use SET_FILE_OP_TIMES instead.")
46#elif !defined(SET_FILE_OP_TIMES)
47#define SET_FILE_OP_TIMES 0
48#endif
49#if !defined(CHECK_STREAM_STATE) && defined(RDAT_NE_PREVERJAJ_STANJA_VER)
50#define CHECK_STREAM_STATE 0
51#pragma message("RDAT_NE_PREVERJAJ_EOF_VER is deprecated. Use CHECK_STREAM_STATE=0 instead.")
52#else
53#define CHECK_STREAM_STATE 1
54#endif
55
56namespace stdex
57{
58 namespace stream
59 {
63 enum class state_t {
64 ok = 0,
65 eof,
66 fail,
67 };
68
72 using fsize_t = uint64_t;
73 constexpr fsize_t fsize_max = UINT64_MAX;
74
75 constexpr size_t iterate_count = 0x10;
76 constexpr size_t default_block_size = 0x10000;
77 constexpr utf16_t utf16_bom = u'\ufeff';
78 constexpr utf32_t utf32_bom = U'\ufeff';
79 constexpr const char utf8_bom[3] = { '\xef', '\xbb', '\xbf' };
80
84 class basic
85 {
86 public:
87 basic(_In_ state_t state = state_t::ok) : m_state(state) {}
88
89 virtual ~basic() noexcept(false) {}
90
102 virtual _Success_(return != 0 || length == 0) size_t read(
103 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
104 {
105 _Unreferenced_(data);
106 _Unreferenced_(length);
107 m_state = state_t::fail;
108 return 0;
109 }
110
120 virtual _Success_(return != 0) size_t write(
121 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
122 {
123 _Unreferenced_(data);
124 _Unreferenced_(length);
125 m_state = state_t::fail;
126 return 0;
127 }
128
132 virtual void flush()
133 {
134 m_state = state_t::ok;
135 }
136
140 virtual void close()
141 {
142 m_state = state_t::ok;
143 }
144
148 virtual void skip(_In_ fsize_t amount)
149 {
150 if (amount == 1)
151 read_byte();
152 else if (amount < iterate_count) {
153 for (size_t i = 0; i < static_cast<size_t>(amount); i++) {
154 read_byte();
155 if (!ok()) _Unlikely_
156 break;
157 }
158 }
159 else {
160 size_t block = static_cast<size_t>(std::min<fsize_t>(amount, default_block_size));
161 try {
162 std::unique_ptr<uint8_t[]> dummy(new uint8_t[block]);
163 while (amount) {
164 amount -= read_array(dummy.get(), sizeof(uint8_t), static_cast<size_t>(std::min<fsize_t>(amount, block)));
165 if (!ok()) _Unlikely_
166 break;
167 }
168 }
169 catch (const std::bad_alloc&) { m_state = state_t::fail; }
170 }
171 }
172
176 state_t state() const { return m_state; };
177
181 bool ok() const { return m_state == state_t::ok; };
182
190 virtual std::vector<uint8_t> read_remainder(_In_ size_t max_length = SIZE_MAX)
191 {
192 std::vector<uint8_t> result;
193 size_t offset, length;
194 offset = 0;
195 length = default_block_size;
196 while (offset < max_length) {
197 length = std::min(length, max_length);
198 try { result.resize(length); }
199 catch (const std::bad_alloc&) {
200 m_state = state_t::fail;
201 return result;
202 }
203 auto num_read = read_array(result.data() + offset, sizeof(uint8_t), length - offset);
204 offset += num_read;
205 if (!ok()) _Unlikely_
206 break;
207 length += default_block_size;
208 }
209 result.resize(offset);
210 return result;
211 }
212
216 uint8_t read_byte()
217 {
218 uint8_t byte;
219 if (read_array(&byte, sizeof(byte), 1) == 1)
220 return byte;
221 throw std::system_error(sys_error(), std::system_category(), "failed to read");
222 }
223
227 void write_byte(_In_ uint8_t byte, _In_ fsize_t amount = 1)
228 {
229 if (amount == 1)
230 write(&byte, sizeof(uint8_t));
231 else if (amount < iterate_count) {
232 for (size_t i = 0; i < static_cast<size_t>(amount); i++) {
233 write(&byte, sizeof(uint8_t));
234 if (!ok()) _Unlikely_
235 break;
236 }
237 }
238 else {
239 size_t block = static_cast<size_t>(std::min<fsize_t>(amount, default_block_size));
240 try {
241 std::unique_ptr<uint8_t[]> dummy(new uint8_t[block]);
242 memset(dummy.get(), byte, block);
243 while (amount) {
244 amount -= write_array(dummy.get(), sizeof(uint8_t), static_cast<size_t>(std::min<fsize_t>(amount, block)));
245 if (!ok()) _Unlikely_
246 break;
247 }
248 }
249 catch (const std::bad_alloc&) { m_state = state_t::fail; }
250 }
251 }
252
264 template <class T>
265 basic& read_data(_Out_ T& data)
266 {
267 if (!ok()) _Unlikely_ {
268 data = 0;
269 return *this;
270 }
271 if (read_array(&data, sizeof(T), 1) == 1)
272 (void)LE2HE(&data);
273 else {
274 data = 0;
275 if (ok())
276 m_state = state_t::eof;
277 }
278 return *this;
279 }
280
292 template <class T>
293 basic& write_data(_In_ const T data)
294 {
295 if (!ok()) _Unlikely_
296 return *this;
297#if BYTE_ORDER == BIG_ENDIAN
298 T data_le = HE2LE(data);
299 write(&data_le, sizeof(T));
300#else
301 write(&data, sizeof(T));
302#endif
303 return *this;
304 }
305
311 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
312 size_t readln(_Inout_ std::basic_string<T, TR, AX>& str)
313 {
314 str.clear();
315 return readln_and_attach(str);
316 }
317
323 template<class T_from, class T_to, class TR = std::char_traits<T_to>, class AX = std::allocator<T_to>>
324 size_t readln(_Inout_ std::basic_string<T_to, TR, AX>& str, _In_ charset_encoder<T_from, T_to>& encoder)
325 {
326 if (encoder.from_encoding() == encoder.to_encoding())
327 return readln(str);
328 std::basic_string<T_from> tmp;
330 encoder.strcpy(str, tmp);
331 return str.size();
332 }
333
339 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
340 size_t readln_and_attach(_Inout_ std::basic_string<T, TR, AX>& str)
341 {
342 bool initial = true;
343 T chr, previous = (T)0;
344 do {
345 read_array(&chr, sizeof(T), 1);
346 if (!initial && !(previous == static_cast<T>('\r') && chr == static_cast<T>('\n')))
347 str += previous;
348 else
349 initial = false;
350 previous = chr;
351 } while (ok() && chr != static_cast<T>('\n'));
352 return str.size();
353 }
354
360 template<class T_from, class T_to, class TR = std::char_traits<T_to>, class AX = std::allocator<T_to>>
361 size_t readln_and_attach(_Inout_ std::basic_string<T_to, TR, AX>& str, _In_ charset_encoder<T_from, T_to>& encoder)
362 {
363 if (encoder.from_encoding() == encoder.to_encoding())
364 return readln_and_attach(str);
365 std::basic_string<T_from> tmp;
367 encoder.strcat(str, tmp);
368 return str.size();
369 }
370
376 size_t read_array(_Out_writes_bytes_(size* count) void* array, _In_ size_t size, _In_ size_t count)
377 {
378 for (size_t to_read = mul(size, count);;) {
379 size_t num_read = read(array, to_read);
380 to_read -= num_read;
381 if (!to_read)
382 return count;
383 if (!ok()) _Unlikely_
384 return count - to_read / size;
385 reinterpret_cast<uint8_t*&>(array) += num_read;
386 }
387 }
388
394 size_t write_array(_In_reads_bytes_opt_(size* count) const void* array, _In_ size_t size, _In_ size_t count)
395 {
396 return write(array, mul(size, count)) / size;
397 }
398
407 template <class T_from, class T_to>
408 size_t write_array(_In_z_ const T_from* str, _In_ charset_encoder<T_from, T_to>& encoder)
409 {
410 if (!ok()) _Unlikely_
411 return 0;
412 size_t num_chars = stdex::strlen(str);
413 if (encoder.from_encoding() == encoder.to_encoding())
414 return write_array(str, sizeof(T_from), num_chars);
415 std::basic_string<T_to> tmp(encoder.convert(str, num_chars));
416 return write_array(tmp.data(), sizeof(T_to), tmp.size());
417 }
418
428 template <class T_from, class T_to>
429 size_t write_array(_In_reads_or_z_opt_(num_chars) const T_from* str, _In_ size_t num_chars, _In_ charset_encoder<T_from, T_to>& encoder)
430 {
431 if (!ok()) _Unlikely_
432 return 0;
433 num_chars = stdex::strnlen(str, num_chars);
434 if (encoder.from_encoding() == encoder.to_encoding())
435 return write_array(str, sizeof(T_from), num_chars);
436 std::basic_string<T_to> tmp(encoder.convert(str, num_chars));
437 return write_array(tmp.data(), sizeof(T_to), tmp.size());
438 }
439
448 template<class T_from, class T_to, class TR = std::char_traits<T_from>, class AX = std::allocator<T_from>>
449 size_t write_array(_In_ const std::basic_string<T_from, TR, AX>& str, _In_ charset_encoder<T_from, T_to>& encoder)
450 {
451 if (!ok()) _Unlikely_
452 return 0;
453 if (encoder.from_encoding() == encoder.to_encoding())
454 return write_array(str.data(), sizeof(T_from), str.size());
455 std::basic_string<T_to> tmp(encoder.convert(str));
456 return write_array(tmp.data(), sizeof(T_to), tmp.size());
457 }
458
470 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
471 basic& read_str(_Out_ std::basic_string<T, TR, AX>& data)
472 {
473 data.clear();
474 if (!ok()) _Unlikely_
475 return *this;
476 uint32_t num_chars;
477 read_data(num_chars);
478 if (!ok()) _Unlikely_
479 return *this;
480 data.reserve(num_chars);
481 for (;;) {
482 T buf[0x400];
483 uint32_t num_read = static_cast<uint32_t>(read_array(buf, sizeof(T), std::min<uint32_t>(num_chars, _countof(buf))));
484 data.append(buf, num_read);
485 num_chars -= num_read;
486 if (!num_chars || !ok())
487 return *this;
488 }
489 }
490
502 template <class T>
503 basic& write_str(_In_z_ const T* data)
504 {
505 // Stream state will be checked in write_data.
506 size_t num_chars = stdex::strlen(data);
507 if (num_chars > UINT32_MAX)
508 throw std::invalid_argument("string too long");
509 write_data(static_cast<uint32_t>(num_chars));
510 if (!ok()) _Unlikely_
511 return *this;
512 write_array(data, sizeof(T), num_chars);
513 return *this;
514 }
515
527 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
528 basic& write_str(_In_ const std::basic_string<T, TR, AX>& data)
529 {
530 // Stream state will be checked in write_data.
531 size_t num_chars = data.size();
532 if (num_chars > UINT32_MAX)
533 throw std::invalid_argument("string too long");
534 write_data(static_cast<uint32_t>(num_chars));
535 if (!ok()) _Unlikely_
536 return *this;
537 write_array(data.data(), sizeof(T), num_chars);
538 return *this;
539 }
540
541#ifdef _WIN32
547 size_t write_sa(_In_ LPSAFEARRAY sa)
548 {
549 long ubound, lbound;
550 if (FAILED(SafeArrayGetUBound(sa, 1, &ubound)) ||
551 FAILED(SafeArrayGetLBound(sa, 1, &lbound)))
552 throw std::invalid_argument("SafeArrayGet[UL]Bound failed");
553 safearray_accessor<void> a(sa);
554 return write(a.data(), static_cast<size_t>(ubound) - lbound + 1);
555 }
556#endif
557
563 fsize_t write_stream(_Inout_ basic& stream, _In_ fsize_t amount = fsize_max)
564 {
565 std::unique_ptr<uint8_t[]> data(new uint8_t[static_cast<size_t>(std::min<fsize_t>(amount, default_block_size))]);
566 fsize_t num_copied = 0, to_write = amount;
567 m_state = state_t::ok;
568 while (to_write) {
569 size_t num_read = stream.read(data.get(), static_cast<size_t>(std::min<fsize_t>(default_block_size, to_write)));
570 size_t num_written = write(data.get(), num_read);
571 num_copied += num_written;
572 to_write -= num_written;
573 if (stream.m_state == state_t::eof) {
574 // EOF is not an error.
575 m_state = state_t::ok;
576 break;
577 }
578 m_state = stream.m_state;
579 if (!ok())
580 break;
581 }
582 return num_copied;
583 }
584
588 void write_charset(_In_ charset_id charset)
589 {
590 if (charset == charset_id::utf32)
591 write_data(utf32_bom);
592 else if (charset == charset_id::utf16)
593 write_data(utf16_bom);
594 else if (charset == charset_id::utf8)
595 write_array(utf8_bom, sizeof(utf8_bom), 1);
596 }
597
603 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, ...)
604 {
605 va_list params;
606 va_start(params, locale);
607 size_t num_chars = write_vsprintf(format, locale, params);
608 va_end(params);
609 return num_chars;
610 }
611
617 size_t write_sprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, ...)
618 {
619 va_list params;
620 va_start(params, locale);
621 size_t num_chars = write_vsprintf(format, locale, params);
622 va_end(params);
623 return num_chars;
624 }
625
631 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, _In_ va_list params)
632 {
633 std::string tmp;
634 tmp.reserve(default_block_size);
635 vappendf(tmp, format, locale, params);
636 return write_array(tmp.data(), sizeof(char), tmp.size());
637 }
638
644 size_t write_vsprintf(_In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, _In_ va_list params)
645 {
646 std::wstring tmp;
647 tmp.reserve(default_block_size);
648 vappendf(tmp, format, locale, params);
649 return write_array(tmp.data(), sizeof(wchar_t), tmp.size());
650 }
651
652 basic& operator >>(_Out_ int8_t& data) { return read_data(data); }
653 basic& operator <<(_In_ const int8_t data) { return write_data(data); }
654 basic& operator >>(_Out_ int16_t& data) { return read_data(data); }
655 basic& operator <<(_In_ const int16_t data) { return write_data(data); }
656 basic& operator >>(_Out_ int32_t& data) { return read_data(data); }
657 basic& operator <<(_In_ const int32_t data) { return write_data(data); }
658 basic& operator >>(_Out_ int64_t& data) { return read_data(data); }
659 basic& operator <<(_In_ const int64_t data) { return write_data(data); }
660 basic& operator >>(_Out_ uint8_t& data) { return read_data(data); }
661 basic& operator <<(_In_ const uint8_t data) { return write_data(data); }
662 basic& operator >>(_Out_ uint16_t& data) { return read_data(data); }
663 basic& operator <<(_In_ const uint16_t data) { return write_data(data); }
664 basic& operator >>(_Out_ uint32_t& data) { return read_data(data); }
665 basic& operator <<(_In_ const uint32_t data) { return write_data(data); }
666 basic& operator >>(_Out_ uint64_t& data) { return read_data(data); }
667 basic& operator <<(_In_ const uint64_t data) { return write_data(data); }
668 basic& operator >>(_Out_ float& data) { return read_data(data); }
669 basic& operator <<(_In_ const float data) { return write_data(data); }
670 basic& operator >>(_Out_ double& data) { return read_data(data); }
671 basic& operator <<(_In_ const double data) { return write_data(data); }
672 basic& operator >>(_Out_ char& data) { return read_data(data); }
673 basic& operator <<(_In_ const char data) { return write_data(data); }
674#ifdef _NATIVE_WCHAR_T_DEFINED
675 basic& operator >>(_Out_ wchar_t& data) { return read_data(data); }
676 basic& operator <<(_In_ const wchar_t data) { return write_data(data); }
677#endif
678 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
679 basic& operator >>(_Out_ std::basic_string<T, TR, AX>& data) { return read_str(data); }
680 template <class T>
681 basic& operator <<(_In_ const T* data) { return write_str(data); }
682 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
683 basic& operator <<(_In_ const std::basic_string<T, TR, AX>& data) { return write_str(data); }
684
685 template <class T, class AX = std::allocator<T>>
686 basic& operator <<(_In_ const std::vector<T, AX>& data)
687 {
688 size_t num = data.size();
689 if (num > UINT32_MAX) _Unlikely_
690 throw std::invalid_argument("collection too big");
691 *this << static_cast<uint32_t>(num);
692 for (auto& el : data)
693 *this << el;
694 return *this;
695 }
696
697 template <class T, class AX = std::allocator<T>>
698 basic& operator >>(_Out_ std::vector<T, AX>& data)
699 {
700 data.clear();
701 uint32_t num;
702 *this >> num;
703 if (!ok()) _Unlikely_
704 return *this;
705 data.reserve(num);
706 for (uint32_t i = 0; i < num; ++i) {
707 T el;
708 *this >> el;
709 if (!ok()) _Unlikely_
710 return *this;
711 data.push_back(std::move(el));
712 }
713 }
714
715 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
716 basic& operator <<(_In_ const std::set<KEY, PR, AX>& data)
717 {
718 size_t num = data.size();
719 if (num > UINT32_MAX) _Unlikely_
720 throw std::invalid_argument("collection too big");
721 *this << static_cast<uint32_t>(num);
722 for (auto& el : data)
723 *this << el;
724 return *this;
725 }
726
727 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
728 basic& operator >>(_Out_ std::set<KEY, PR, AX>& data)
729 {
730 data.clear();
731 uint32_t num;
732 *this >> num;
733 if (!ok()) _Unlikely_
734 return *this;
735 for (uint32_t i = 0; i < num; ++i) {
736 KEY el;
737 *this >> el;
738 if (!ok()) _Unlikely_
739 return *this;
740 data.insert(std::move(el));
741 }
742 }
743
744 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
745 basic& operator <<(_In_ const std::multiset<KEY, PR, AX>& data)
746 {
747 size_t num = data.size();
748 if (num > UINT32_MAX) _Unlikely_
749 throw std::invalid_argument("collection too big");
750 *this << static_cast<uint32_t>(num);
751 for (auto& el : data)
752 *this << el;
753 return *this;
754 }
755
756 template <class KEY, class PR = std::less<KEY>, class AX = std::allocator<KEY>>
757 basic& operator >>(_Out_ std::multiset<KEY, PR, AX>& data)
758 {
759 data.clear();
760 uint32_t num;
761 *this >> num;
762 if (!ok()) _Unlikely_
763 return *this;
764 for (uint32_t i = 0; i < num; ++i) {
765 KEY el;
766 *this >> el;
767 if (!ok()) _Unlikely_
768 return *this;
769 data.insert(std::move(el));
770 }
771 return *this;
772 }
773
774 protected:
775 state_t m_state;
776 };
777
781 using fpos_t = uint64_t;
782 constexpr fpos_t fpos_max = UINT64_MAX;
783 constexpr fpos_t fpos_min = 0;
784
788 using foff_t = int64_t;
789 constexpr foff_t foff_max = INT64_MAX;
790 constexpr foff_t foff_min = INT64_MIN;
791
795 enum class seek_t {
796#ifdef _WIN32
797 beg = FILE_BEGIN,
798 cur = FILE_CURRENT,
799 end = FILE_END
800#else
801 beg = SEEK_SET,
802 cur = SEEK_CUR,
803 end = SEEK_END
804#endif
805 };
806
807#if _HAS_CXX20
808 using clock = std::chrono::file_clock;
809#else
810 using clock = std::chrono::system_clock;
811#endif
812 using time_point = std::chrono::time_point<clock>;
813
817 class basic_file : virtual public basic
818 {
819 public:
820 virtual std::vector<uint8_t> read_remainder(_In_ size_t max_length = SIZE_MAX)
821 {
822 size_t length = std::min<size_t>(max_length, static_cast<size_t>(size() - tell()));
823 std::vector<uint8_t> result;
824 try { result.resize(length); }
825 catch (const std::bad_alloc&) {
826 m_state = state_t::fail;
827 return result;
828 }
829 result.resize(read_array(result.data(), sizeof(uint8_t), length));
830 return result;
831 }
832
838 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg) = 0;
839
845 fpos_t seekbeg(_In_ fpos_t offset)
846 {
847 return seek(static_cast<foff_t>(offset), seek_t::beg);
848 }
849
855 fpos_t seekcur(_In_ foff_t offset) { return seek(offset, seek_t::cur); }
856
862 fpos_t seekend(_In_ foff_t offset) { return seek(offset, seek_t::end); }
863
864 virtual void skip(_In_ fsize_t amount)
865 {
866 if (amount > foff_max)
867 throw std::invalid_argument("file offset too big");
868 seek(static_cast<foff_t>(amount), seek_t::cur);
869 }
870
877 virtual fpos_t tell() const = 0;
878
882 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
883 {
884 _Unreferenced_(offset);
885 _Unreferenced_(length);
886 throw std::domain_error("not implemented");
887 }
888
892 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
893 {
894 _Unreferenced_(offset);
895 _Unreferenced_(length);
896 throw std::domain_error("not implemented");
897 }
898
903 virtual fsize_t size() const = 0;
904
908 virtual void truncate() = 0;
909
913 virtual time_point ctime() const
914 {
915 return time_point::min();
916 }
917
921 virtual time_point atime() const
922 {
923 return time_point::min();
924 }
925
929 virtual time_point mtime() const
930 {
931 return time_point::min();
932 }
933
937 virtual void set_ctime(time_point date)
938 {
939 _Unreferenced_(date);
940 throw std::domain_error("not implemented");
941 }
942
946 virtual void set_atime(time_point date)
947 {
948 _Unreferenced_(date);
949 throw std::domain_error("not implemented");
950 }
951
955 virtual void set_mtime(time_point date)
956 {
957 _Unreferenced_(date);
958 throw std::domain_error("not implemented");
959 }
960
961#ifdef _WIN32
965 LPSAFEARRAY read_sa()
966 {
967 stdex_assert(size() <= SIZE_MAX);
968 if (size() > ULONG_MAX)
969 throw std::range_error("data too big");
970 ULONG length = static_cast<ULONG>(size());
971 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(SafeArrayCreateVector(VT_UI1, 0, length));
972 if (!sa) _Unlikely_
973 throw std::runtime_error("SafeArrayCreateVector failed");
974 if (seek(0) != 0) _Unlikely_
975 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
976 safearray_accessor<void> a(sa.get());
977 if (read_array(a.data(), 1, length) != length)
978 throw std::system_error(sys_error(), std::system_category(), "failed to read");
979 return sa.release();
980 }
981#endif
982
988 charset_id read_charset(_In_ charset_id default_charset = charset_id::system)
989 {
990 if (seek(0) != 0) _Unlikely_
991 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
992 utf32_t id_utf32;
993 read_array(&id_utf32, sizeof(utf32_t), 1);
994 if (ok() && id_utf32 == utf32_bom)
995 return charset_id::utf32;
996
997 if (seek(0) != 0) _Unlikely_
998 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
999 utf16_t id_utf16;
1000 read_array(&id_utf16, sizeof(utf16_t), 1);
1001 if (ok() && id_utf16 == utf16_bom)
1002 return charset_id::utf16;
1003
1004 if (seek(0) != 0) _Unlikely_
1005 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1006 char id_utf8[3] = { 0 };
1007 read_array(id_utf8, sizeof(id_utf8), 1);
1008 if (ok() && strncmp(id_utf8, _countof(id_utf8), utf8_bom, _countof(utf8_bom)) == 0)
1009 return charset_id::utf8;
1010
1011 if (seek(0) != 0) _Unlikely_
1012 throw std::system_error(sys_error(), std::system_category(), "failed to seek");
1013 return default_charset;
1014 }
1015 };
1016
1022 class converter : public basic
1023 {
1024 protected:
1026#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1027 explicit converter() : basic(state_t::fail) {}
1028
1029 void init(_Inout_ basic& source)
1030 {
1031 m_source = &source;
1032 init();
1033 }
1034
1035 void init()
1036 {
1037 m_state = m_source->state();
1038 }
1039
1040 void done()
1041 {
1042 m_source = nullptr;
1043 }
1045
1046 public:
1047 converter(_Inout_ basic& source) :
1048 basic(source.state()),
1049 m_source(&source)
1050 {}
1051
1052 virtual _Success_(return != 0 || length == 0) size_t read(
1053 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1054 {
1055 size_t num_read = m_source->read(data, length);
1056 m_state = m_source->state();
1057 return num_read;
1058 }
1059
1060 virtual _Success_(return != 0) size_t write(
1061 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1062 {
1063 size_t num_written = m_source->write(data, length);
1064 m_state = m_source->state();
1065 return num_written;
1066 }
1067
1068 virtual void close()
1069 {
1070 m_source->close();
1071 m_state = m_source->state();
1072 }
1073
1074 virtual void flush()
1075 {
1076 m_source->flush();
1077 m_state = m_source->state();
1078 }
1079
1080 protected:
1081 basic* m_source;
1082 };
1083
1087 class replicator : public basic
1088 {
1089 public:
1090 virtual ~replicator()
1091 {
1092 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1093 auto _w = w->get();
1094 {
1095 const std::lock_guard<std::mutex> lk(_w->mutex);
1096 _w->op = worker::op_t::quit;
1097 }
1098 _w->cv.notify_one();
1099 }
1100 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w)
1101 w->get()->join();
1102 }
1103
1107 void push_back(_In_ basic* source)
1108 {
1109 m_workers.push_back(std::unique_ptr<worker>(new worker(source)));
1110 }
1111
1115 void remove(basic* source)
1116 {
1117 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1118 auto _w = w->get();
1119 if (_w->source == source) {
1120 {
1121 const std::lock_guard<std::mutex> lk(_w->mutex);
1122 _w->op = worker::op_t::quit;
1123 }
1124 _w->cv.notify_one();
1125 _w->join();
1126 m_workers.erase(w);
1127 return;
1128 }
1129 }
1130 }
1131
1132 virtual _Success_(return != 0) size_t write(
1133 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1134 {
1135 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1136 auto _w = w->get();
1137 {
1138 const std::lock_guard<std::mutex> lk(_w->mutex);
1139 _w->op = worker::op_t::write;
1140 _w->data = data;
1141 _w->length = length;
1142 }
1143 _w->cv.notify_one();
1144 }
1145 size_t num_written = length;
1146 m_state = state_t::ok;
1147 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1148 auto _w = w->get();
1149 std::unique_lock<std::mutex> lk(_w->mutex);
1150 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1151 if (_w->num_written < num_written)
1152 num_written = _w->num_written;
1153 if (ok() && !_w->source->ok())
1154 m_state = _w->source->state();
1155 }
1156 return num_written;
1157 }
1158
1159 virtual void close()
1160 {
1161 foreach_worker(worker::op_t::close);
1162 }
1163
1164 virtual void flush()
1165 {
1166 foreach_worker(worker::op_t::flush);
1167 }
1168
1169 protected:
1170 class worker : public std::thread
1171 {
1172 public:
1173 worker(_In_ basic* _source) :
1174 source(_source),
1175 op(op_t::noop),
1176 data(nullptr),
1177 length(0),
1178 num_written(0)
1179 {
1180 *static_cast<std::thread*>(this) = std::thread([](_Inout_ worker& w) { w.process_op(); }, std::ref(*this));
1181 }
1182
1183 protected:
1184 void process_op()
1185 {
1186 for (;;) {
1187 std::unique_lock<std::mutex> lk(mutex);
1188 cv.wait(lk, [&] {return op != op_t::noop; });
1189 switch (op) {
1190 case op_t::quit:
1191 return;
1192 case op_t::write:
1193 num_written = source->write(data, length);
1194 break;
1195 case op_t::close:
1196 source->close();
1197 break;
1198 case op_t::flush:
1199 source->flush();
1200 break;
1201 case op_t::noop:;
1202 }
1203 op = op_t::noop;
1204 lk.unlock();
1205 cv.notify_one();
1206 }
1207 }
1208
1209 public:
1210 basic* source;
1211 enum class op_t {
1212 noop = 0,
1213 quit,
1214 write,
1215 close,
1216 flush,
1217 } op;
1218 const void* data;
1219 size_t length;
1221 std::mutex mutex;
1222 std::condition_variable cv;
1223 };
1224
1225 void foreach_worker(_In_ worker::op_t op)
1226 {
1227 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1228 auto _w = w->get();
1229 {
1230 const std::lock_guard<std::mutex> lk(_w->mutex);
1231 _w->op = op;
1232 }
1233 _w->cv.notify_one();
1234 }
1235 m_state = state_t::ok;
1236 for (auto w = m_workers.begin(), w_end = m_workers.end(); w != w_end; ++w) {
1237 auto _w = w->get();
1238 std::unique_lock<std::mutex> lk(_w->mutex);
1239 _w->cv.wait(lk, [&] {return _w->op == worker::op_t::noop; });
1240 if (ok())
1241 m_state = _w->source->state();
1242 }
1243 }
1244
1245 std::list<std::unique_ptr<worker>> m_workers;
1246 };
1247
1248 constexpr size_t default_async_limit = 0x100000;
1249
1255 template <size_t N_cap = default_async_limit>
1257 {
1258 public:
1259 async_reader(_Inout_ basic& source) :
1260 converter(source),
1261 m_worker([](_Inout_ async_reader& w) { w.process(); }, std::ref(*this))
1262 {}
1263
1264 virtual ~async_reader()
1265 {
1266 m_ring.quit();
1267 m_worker.join();
1268 }
1269
1270#pragma warning(suppress: 6101) // See [1] below
1271 virtual _Success_(return != 0 || length == 0) size_t read(
1272 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1273 {
1274 stdex_assert(data || !length);
1275 for (size_t to_read = length;;) {
1276 uint8_t* ptr; size_t num_read;
1277 std::tie(ptr, num_read) = m_ring.front();
1278 if (!ptr) _Unlikely_ {
1279 m_state = to_read < length || !length ? state_t::ok : m_source->state();
1280 return length - to_read; // [1] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
1281 }
1282 if (to_read < num_read)
1283 num_read = to_read;
1284 memcpy(data, ptr, num_read);
1285 m_ring.pop(num_read);
1286 to_read -= num_read;
1287 if (!to_read) {
1288 m_state = state_t::ok;
1289 return length;
1290 }
1291 reinterpret_cast<uint8_t*&>(data) += num_read;
1292 }
1293 }
1294
1295 protected:
1296 void process()
1297 {
1298 for (;;) {
1299 uint8_t* ptr; size_t num_write;
1300 std::tie(ptr, num_write) = m_ring.back();
1301 if (!ptr) _Unlikely_
1302 break;
1303 num_write = m_source->read(ptr, num_write);
1304 m_ring.push(num_write);
1305 if (!m_source->ok()) {
1306 m_ring.quit();
1307 break;
1308 }
1309 }
1310 }
1311
1312 protected:
1313 ring<uint8_t, N_cap> m_ring;
1314 std::thread m_worker;
1315 };
1316
1322 template <size_t N_cap = default_async_limit>
1324 {
1325 public:
1326 async_writer(_Inout_ basic& source) :
1327 converter(source),
1328 m_worker([](_Inout_ async_writer& w) { w.process(); }, std::ref(*this))
1329 {}
1330
1331 virtual ~async_writer()
1332 {
1333 m_ring.quit();
1334 m_worker.join();
1335 }
1336
1337 virtual _Success_(return != 0) size_t write(
1338 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1339 {
1340 stdex_assert(data || !length);
1341 for (size_t to_write = length;;) {
1342 uint8_t* ptr; size_t num_write;
1343 std::tie(ptr, num_write) = m_ring.back();
1344 if (!ptr) _Unlikely_ {
1345 m_state = state_t::fail;
1346 return length - to_write;
1347 }
1348 if (to_write < num_write)
1349 num_write = to_write;
1350 memcpy(ptr, data, num_write);
1351 m_ring.push(num_write);
1352 to_write -= num_write;
1353 if (!to_write) {
1354 m_state = state_t::ok;
1355 return length;
1356 }
1357 reinterpret_cast<const uint8_t*&>(data) += num_write;
1358 }
1359 }
1360
1361 virtual void flush()
1362 {
1363 m_ring.sync();
1365 }
1366
1367 protected:
1368 void process()
1369 {
1370 for (;;) {
1371 uint8_t* ptr; size_t num_read;
1372 std::tie(ptr, num_read) = m_ring.front();
1373 if (!ptr)
1374 break;
1375 num_read = m_source->write(ptr, num_read);
1376 m_ring.pop(num_read);
1377 if (!m_source->ok()) {
1378 m_ring.quit();
1379 break;
1380 }
1381 }
1382 }
1383
1384 protected:
1385 ring<uint8_t, N_cap> m_ring;
1386 std::thread m_worker;
1387 };
1388
1389 constexpr size_t default_buffer_size = 0x400;
1390
1394 class buffer : public converter
1395 {
1396 protected:
1398 explicit buffer(_In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1399 converter(),
1400 m_read_buffer(read_buffer_size),
1401 m_write_buffer(write_buffer_size)
1402 {}
1403
1404 void done()
1405 {
1406 if (m_source)
1407 flush_write();
1408 converter::done();
1409 }
1411
1412 public:
1413 buffer(_Inout_ basic& source, _In_ size_t read_buffer_size = default_buffer_size, _In_ size_t write_buffer_size = default_buffer_size) :
1414 converter(source),
1415 m_read_buffer(read_buffer_size),
1416 m_write_buffer(write_buffer_size)
1417 {}
1418
1419 virtual ~buffer()
1420 {
1421 if (m_source)
1422 flush_write();
1423 }
1424
1425 virtual _Success_(return != 0 || length == 0) size_t read(
1426 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1427 {
1428 stdex_assert(data || !length);
1429 for (size_t to_read = length;;) {
1430 size_t buffer_size = m_read_buffer.tail - m_read_buffer.head;
1431 if (to_read <= buffer_size) {
1432 memcpy(data, m_read_buffer.data + m_read_buffer.head, to_read);
1433 m_read_buffer.head += to_read;
1434 m_state = state_t::ok;
1435 return length;
1436 }
1437 if (buffer_size) {
1438 memcpy(data, m_read_buffer.data + m_read_buffer.head, buffer_size);
1439 reinterpret_cast<uint8_t*&>(data) += buffer_size;
1440 to_read -= buffer_size;
1441 }
1442 m_read_buffer.head = 0;
1443 if (to_read > m_read_buffer.capacity) {
1444 // When needing to read more data than buffer capacity, bypass the buffer.
1445 m_read_buffer.tail = 0;
1446 to_read -= m_source->read(data, to_read);
1447 m_state = to_read < length ? state_t::ok : m_source->state();
1448 return length - to_read;
1449 }
1450 m_read_buffer.tail = m_source->read(m_read_buffer.data, m_read_buffer.capacity);
1451 if (m_read_buffer.tail < m_read_buffer.capacity && m_read_buffer.tail < to_read) _Unlikely_ {
1452 memcpy(data, m_read_buffer.data, m_read_buffer.tail);
1453 m_read_buffer.head = m_read_buffer.tail;
1454 to_read -= m_read_buffer.tail;
1455 m_state = to_read < length ? state_t::ok : m_source->state();
1456 return length - to_read;
1457 }
1458 }
1459 }
1460
1461 virtual _Success_(return != 0) size_t write(
1462 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1463 {
1464 stdex_assert(data || !length);
1465 if (!length) _Unlikely_ {
1466 // Pass null writes (zero-byte length). Null write operations have special meaning with with Windows pipes.
1467 flush_write();
1468 if (!ok()) _Unlikely_
1469 return 0;
1470 converter::write(nullptr, 0);
1471 return 0;
1472 }
1473
1474 for (size_t to_write = length;;) {
1475 size_t available_buffer = m_write_buffer.capacity - m_write_buffer.tail;
1476 if (to_write <= available_buffer) {
1477 memcpy(m_write_buffer.data + m_write_buffer.tail, data, to_write);
1478 m_write_buffer.tail += to_write;
1479 m_state = state_t::ok;
1480 return length;
1481 }
1482 if (available_buffer) {
1483 memcpy(m_write_buffer.data + m_write_buffer.tail, data, available_buffer);
1484 reinterpret_cast<const uint8_t*&>(data) += available_buffer;
1485 to_write -= available_buffer;
1486 m_write_buffer.tail += available_buffer;
1487 }
1488 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1489 if (buffer_size) {
1490 m_write_buffer.head += converter::write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1491 if (m_write_buffer.head == m_write_buffer.tail)
1492 m_write_buffer.head = m_write_buffer.tail = 0;
1493 else
1494 return length - to_write;
1495 }
1496 if (to_write > m_write_buffer.capacity) {
1497 // When needing to write more data than buffer capacity, bypass the buffer.
1498 to_write -= converter::write(data, to_write);
1499 return length - to_write;
1500 }
1501 }
1502 }
1503
1504 virtual void flush()
1505 {
1506 flush_write();
1507 if (ok())
1509 }
1510
1511 protected:
1512 void flush_write()
1513 {
1514 size_t buffer_size = m_write_buffer.tail - m_write_buffer.head;
1515 if (buffer_size) {
1516 m_write_buffer.head += m_source->write(m_write_buffer.data + m_write_buffer.head, buffer_size);
1517 if (m_write_buffer.head == m_write_buffer.tail) {
1518 m_write_buffer.head = 0;
1519 m_write_buffer.tail = 0;
1520 }
1521 else {
1522 m_state = m_source->state();
1523 return;
1524 }
1525 }
1526 m_state = state_t::ok;
1527 }
1528
1529 struct buffer_t {
1530 uint8_t* data;
1531 size_t head, tail, capacity;
1532
1533 buffer_t(_In_ size_t buffer_size) :
1534 head(0),
1535 tail(0),
1536 capacity(buffer_size),
1537 data(buffer_size ? new uint8_t[buffer_size] : nullptr)
1538 {}
1539
1540 ~buffer_t()
1541 {
1542 if (data)
1543 delete[] data;
1544 }
1545 } m_read_buffer, m_write_buffer;
1546 };
1547
1551 class limiter : public converter
1552 {
1553 public:
1554 limiter(_Inout_ basic& source, _In_ fsize_t _read_limit = 0, _In_ fsize_t _write_limit = 0) :
1555 converter(source),
1556 read_limit(_read_limit),
1557 write_limit(_write_limit)
1558 {}
1559
1560 virtual _Success_(return != 0 || length == 0) size_t read(
1561 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1562 {
1563 size_t num_read;
1564 if (read_limit == fsize_max)
1565 num_read = converter::read(data, length);
1566 else if (length <= read_limit) {
1567 num_read = converter::read(data, length);
1568 read_limit -= num_read;
1569 }
1570 else if (length && !read_limit) {
1571 num_read = 0;
1572 m_state = state_t::eof;
1573 }
1574 else {
1575 num_read = converter::read(data, static_cast<size_t>(read_limit));
1576 read_limit -= num_read;
1577 }
1578 return num_read;
1579 }
1580
1581 virtual _Success_(return != 0) size_t write(
1582 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1583 {
1584 size_t num_written;
1585 if (write_limit == fsize_max)
1586 num_written = converter::write(data, length);
1587 else if (length <= write_limit) {
1588 num_written = converter::write(data, length);
1589 write_limit -= num_written;
1590 }
1591 else if (length && !write_limit) {
1592 num_written = 0;
1593 m_state = state_t::fail;
1594 }
1595 else {
1596 num_written = converter::write(data, static_cast<size_t>(write_limit));
1597 write_limit -= num_written;
1598 }
1599 return num_written;
1600 }
1601
1602 public:
1603 fsize_t
1606 };
1607
1611 class window : public limiter
1612 {
1613 public:
1614 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) :
1615 limiter(source, read_limit, write_limit),
1616 read_offset(_read_offset),
1617 write_offset(_write_offset)
1618 {}
1619
1620 virtual _Success_(return != 0 || length == 0) size_t read(
1621 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1622 {
1623 if (read_offset) {
1624 m_source->skip(read_offset);
1625 m_state = m_source->state();
1626 if (!ok()) _Unlikely_
1627 return 0;
1628 read_offset = 0;
1629 }
1630 size_t num_read;
1631 if (read_limit == fsize_max)
1632 num_read = converter::read(data, length);
1633 else if (length <= read_limit) {
1634 num_read = converter::read(data, length);
1635 read_limit -= num_read;
1636 }
1637 else if (length && !read_limit) {
1638 num_read = 0;
1639 m_source->skip(length);
1640 m_state = state_t::eof;
1641 }
1642 else {
1643 num_read = converter::read(data, static_cast<size_t>(read_limit));
1644 read_limit -= num_read;
1645 }
1646 return num_read;
1647 }
1648
1649 virtual _Success_(return != 0) size_t write(
1650 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1651 {
1652 size_t num_skipped, num_written;
1653 if (length <= write_offset) {
1654 write_offset -= length;
1655 m_state = state_t::ok;
1656 return length;
1657 }
1658 if (write_offset) {
1659 reinterpret_cast<const uint8_t*&>(data) += static_cast<size_t>(write_offset);
1660 length -= static_cast<size_t>(write_offset);
1661 num_skipped = static_cast<size_t>(write_offset);
1662 write_offset = 0;
1663 }
1664 else
1665 num_skipped = 0;
1666 if (write_limit == fsize_max)
1667 num_written = converter::write(data, length);
1668 else if (length <= write_limit) {
1669 num_written = converter::write(data, length);
1670 write_limit -= num_written;
1671 }
1672 else if (length && !write_limit) {
1673 num_skipped += length;
1674 num_written = 0;
1675 m_state = state_t::ok;
1676 }
1677 else {
1678 num_skipped += length - static_cast<size_t>(write_limit);
1679 num_written = converter::write(data, static_cast<size_t>(write_limit));
1680 write_limit -= num_written;
1681 }
1682 return num_skipped + num_written;
1683 }
1684
1685 public:
1686 fpos_t
1689 };
1690
1695 {
1696 public:
1697 file_window(_Inout_ basic_file& source, fpos_t offset = 0, fsize_t length = 0) :
1698 basic(source.state()),
1699 m_source(source),
1700 m_offset(source.tell()),
1701 m_region(offset, offset + length)
1702 {}
1703
1704 virtual _Success_(return != 0 || length == 0) size_t read(
1705 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1706 {
1707 stdex_assert(data || !length);
1708 if (m_region.contains(m_offset)) {
1709 size_t num_read = m_source.read(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1710 m_state = m_source.state();
1711 m_offset += num_read;
1712 return num_read;
1713 }
1714 m_state = length ? state_t::eof : state_t::ok;
1715 return 0;
1716 }
1717
1718 virtual _Success_(return != 0) size_t write(
1719 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1720 {
1721 stdex_assert(data || !length);
1722 if (m_region.contains(m_offset)) {
1723 size_t num_written = m_source.write(data, static_cast<size_t>(std::min<fpos_t>(length, m_region.end - m_offset)));
1724 m_state = m_source.state();
1725 m_offset += num_written;
1726 return num_written;
1727 }
1728 m_state = state_t::fail;
1729 return 0;
1730 }
1731
1732 virtual void close()
1733 {
1734 m_source.close();
1735 m_state = m_source.state();
1736 }
1737
1738 virtual void flush()
1739 {
1740 m_source.flush();
1741 m_state = m_source.state();
1742 }
1743
1744 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
1745 {
1746 m_offset = m_source.seek(offset, how);
1747 m_state = m_source.state();
1748 return ok() ? m_offset - m_region.start : fpos_max;
1749 }
1750
1751 virtual void skip(_In_ fsize_t amount)
1752 {
1753 m_source.skip(amount);
1754 m_state = m_source.state();
1755 }
1756
1757 virtual fpos_t tell() const
1758 {
1759 fpos_t offset = m_source.tell();
1760 return m_region.contains(offset) ? offset - m_region.start : fpos_max;
1761 }
1762
1763 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
1764 {
1765 if (m_region.contains(offset)) {
1766 m_source.lock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1767 m_state = m_source.state();
1768 }
1769 else
1770 m_state = state_t::fail;
1771 }
1772
1773 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
1774 {
1775 if (m_region.contains(offset)) {
1776 m_source.unlock(m_region.start + offset, std::min<fsize_t>(length, m_region.end - offset));
1777 m_state = m_source.state();
1778 }
1779 else
1780 m_state = state_t::fail;
1781 }
1782
1783 virtual fsize_t size() const
1784 {
1785 return m_region.size();
1786 }
1787
1788 virtual void truncate()
1789 {
1790 m_state = state_t::fail;
1791 }
1792
1793 protected:
1794 basic_file& m_source;
1795 fpos_t m_offset;
1796 interval<fpos_t> m_region;
1797 };
1798
1799 constexpr size_t default_cache_size = 0x1000;
1800
1804 class cache : public basic_file
1805 {
1806 protected:
1808#pragma warning(suppress: 26495) // The delayed init call will finish initializing the class.
1809 explicit cache(_In_ size_t cache_size = default_cache_size) :
1810 basic(state_t::fail),
1811 m_cache(cache_size)
1812 {}
1813
1814 void init(_Inout_ basic_file& source)
1815 {
1816 m_source = &source;
1817 init();
1818 }
1819
1820 void init()
1821 {
1822 m_state = m_source->state();
1823 m_offset = m_source->tell();
1824#if SET_FILE_OP_TIMES
1825 m_atime = m_source->atime();
1826 m_mtime = m_source->mtime();
1827#endif
1828 }
1829
1830 void done()
1831 {
1832 if (m_source) {
1833 flush_cache();
1834 if (!ok()) _Unlikely_
1835 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occurred
1836 m_source->seekbeg(m_offset);
1837#if SET_FILE_OP_TIMES
1838 m_source->set_atime(m_atime);
1839 m_source->set_mtime(m_mtime);
1840#endif
1841 m_source = nullptr;
1842 }
1843 }
1845
1846 public:
1847 cache(_Inout_ basic_file& source, _In_ size_t cache_size = default_cache_size) :
1848 basic(source.state()),
1849 m_source(&source),
1850 m_cache(cache_size),
1851 m_offset(source.tell())
1852#if SET_FILE_OP_TIMES
1853 , m_atime(source.atime())
1854 , m_mtime(source.mtime())
1855#endif
1856 {}
1857
1858 virtual ~cache() noexcept(false)
1859 {
1860 if (m_source) {
1861 flush_cache();
1862 if (!ok()) _Unlikely_
1863 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occurred
1864 m_source->seekbeg(m_offset);
1865#if SET_FILE_OP_TIMES
1866 m_source->set_atime(m_atime);
1867 m_source->set_mtime(m_mtime);
1868#endif
1869 }
1870 }
1871
1872 virtual _Success_(return != 0 || length == 0) size_t read(
1873 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
1874 {
1875 stdex_assert(data || !length);
1876#if SET_FILE_OP_TIMES
1877 m_atime = time_point::now();
1878#endif
1879 for (size_t to_read = length;;) {
1880 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1881 if (m_cache.region.contains(m_offset)) {
1882 size_t remaining_cache = static_cast<size_t>(m_cache.region.end - m_offset);
1883 if (to_read <= remaining_cache) {
1884 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), to_read);
1885 m_offset += to_read;
1886 m_state = state_t::ok;
1887 return length;
1888 }
1889 memcpy(data, m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), remaining_cache);
1890 reinterpret_cast<uint8_t*&>(data) += remaining_cache;
1891 to_read -= remaining_cache;
1892 m_offset += remaining_cache;
1893 }
1894 flush_cache();
1895 if (!ok()) _Unlikely_ {
1896 if (to_read < length)
1897 m_state = state_t::ok;
1898 return length - to_read;
1899 }
1900 }
1901 {
1902 fpos_t end_max = m_offset + to_read;
1903 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1904 // Read spans multiple cache blocks. Bypass cache to the last block.
1905 m_source->seekbeg(m_offset);
1906 if (!m_source->ok()) _Unlikely_ {
1907 m_state = to_read < length ? state_t::ok : state_t::fail;
1908 return length - to_read;
1909 }
1910 size_t num_read = m_source->read(data, to_read - static_cast<size_t>(end_max % m_cache.capacity));
1911 m_offset += num_read;
1912 to_read -= num_read;
1913 if (!to_read) {
1914 m_state = state_t::ok;
1915 return length;
1916 }
1917 reinterpret_cast<uint8_t*&>(data) += num_read;
1918 m_state = m_source->state();
1919 if (!ok()) {
1920 if (to_read < length)
1921 m_state = state_t::ok;
1922 return length - to_read;
1923 }
1924 }
1925 }
1926 load_cache(m_offset);
1927 if (!ok()) _Unlikely_ {
1928 m_state = to_read < length ? state_t::ok : state_t::fail;
1929 return length - to_read;
1930 }
1931 if (m_cache.region.end <= m_offset) _Unlikely_ {
1932 m_state = to_read < length ? state_t::ok : state_t::eof;
1933 return length - to_read;
1934 }
1935 }
1936 }
1937
1938 virtual _Success_(return != 0) size_t write(
1939 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
1940 {
1941 stdex_assert(data || !length);
1942#if SET_FILE_OP_TIMES
1943 m_atime = m_mtime = time_point::now();
1944#endif
1945 for (size_t to_write = length;;) {
1946 if (m_cache.status != cache_t::cache_t::status_t::empty) {
1947 fpos_t end_max = m_cache.region.start + m_cache.capacity;
1948 if (m_cache.region.start <= m_offset && m_offset < end_max) {
1949 size_t remaining_cache = static_cast<size_t>(end_max - m_offset);
1950 if (to_write <= remaining_cache) {
1951 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, to_write);
1952 m_offset += to_write;
1953 m_cache.status = cache_t::cache_t::status_t::dirty;
1954 m_cache.region.end = std::max(m_cache.region.end, m_offset);
1955 m_state = state_t::ok;
1956 return length;
1957 }
1958 memcpy(m_cache.data + static_cast<size_t>(m_offset - m_cache.region.start), data, remaining_cache);
1959 reinterpret_cast<const uint8_t*&>(data) += remaining_cache;
1960 to_write -= remaining_cache;
1961 m_offset += remaining_cache;
1962 m_cache.status = cache_t::cache_t::status_t::dirty;
1963 m_cache.region.end = end_max;
1964 }
1965 flush_cache();
1966 if (!ok()) _Unlikely_
1967 return length - to_write;
1968 }
1969 {
1970 fpos_t end_max = m_offset + to_write;
1971 if (m_offset / m_cache.capacity < end_max / m_cache.capacity) {
1972 // Write spans multiple cache blocks. Bypass cache to the last block.
1973 m_source->seekbeg(m_offset);
1974 if (!ok()) _Unlikely_
1975 return length - to_write;
1976 size_t num_written = m_source->write(data, to_write - static_cast<size_t>(end_max % m_cache.capacity));
1977 m_offset += num_written;
1978 m_state = m_source->state();
1979 to_write -= num_written;
1980 if (!to_write || !ok())
1981 return length - to_write;
1982 reinterpret_cast<const uint8_t*&>(data) += num_written;
1983 }
1984 }
1985 load_cache(m_offset);
1986 if (!ok()) _Unlikely_
1987 return length - to_write;
1988 }
1989 }
1990
1991 virtual void close()
1992 {
1993 invalidate_cache();
1994 if (!ok()) _Unlikely_
1995 throw std::system_error(sys_error(), std::system_category(), "failed to flush cache"); // Data loss occurred
1996 m_source->close();
1997 m_state = m_source->state();
1998 }
1999
2000 virtual void flush()
2001 {
2002#if SET_FILE_OP_TIMES
2003 m_atime = m_mtime = time_point::min();
2004#endif
2005 flush_cache();
2006 if (!ok()) _Unlikely_
2007 return;
2008 m_source->flush();
2009 }
2010
2011 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2012 {
2013 m_state = state_t::ok;
2014 switch (how) {
2015 case seek_t::beg:
2016 break;
2017 case seek_t::cur:
2018 offset = static_cast<foff_t>(m_offset) + offset;
2019 break;
2020 case seek_t::end: {
2021 auto n = size();
2022 if (n == fsize_max) _Unlikely_{
2023 m_state = state_t::fail;
2024 return fpos_max;
2025 }
2026 offset = static_cast<foff_t>(n) + offset;
2027 break;
2028 }
2029 default:
2030 throw std::invalid_argument("unknown seek origin");
2031 }
2032 if (offset < 0) _Unlikely_
2033 throw std::invalid_argument("negative file offset");
2034 return m_offset = static_cast<fpos_t>(offset);
2035 }
2036
2037 virtual fpos_t tell() const
2038 {
2039 return m_offset;
2040 }
2041
2042 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2043 {
2044 m_source->lock(offset, length);
2045 m_state = m_source->state();
2046 }
2047
2048 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2049 {
2050 m_source->unlock(offset, length);
2051 m_state = m_source->state();
2052 }
2053
2054 virtual fsize_t size() const
2055 {
2056 return m_cache.status != cache_t::cache_t::status_t::empty ?
2057 std::max(m_source->size(), m_cache.region.end) :
2058 m_source->size();
2059 }
2060
2061 virtual void truncate()
2062 {
2063#if SET_FILE_OP_TIMES
2064 m_atime = m_mtime = time_point::now();
2065#endif
2066 m_source->seekbeg(m_offset);
2067 if (m_cache.region.end <= m_offset) {
2068 // Truncation does not affect cache.
2069 }
2070 else if (m_cache.region.start <= m_offset) {
2071 // Truncation truncates cache.
2072 m_cache.region.end = m_offset;
2073 }
2074 else {
2075 // Truncation invalidates cache.
2076 m_cache.status = cache_t::cache_t::status_t::empty;
2077 }
2078 m_source->truncate();
2079 m_state = m_source->state();
2080 }
2081
2082 virtual time_point ctime() const
2083 {
2084 return m_source->ctime();
2085 }
2086
2087 virtual time_point atime() const
2088 {
2089#if SET_FILE_OP_TIMES
2090 return std::max(m_atime, m_source->atime());
2091#else
2092 return m_source->atime();
2093#endif
2094 }
2095
2096 virtual time_point mtime() const
2097 {
2098#if SET_FILE_OP_TIMES
2099 return std::max(m_mtime, m_source->mtime());
2100#else
2101 return m_source->mtime();
2102#endif
2103 }
2104
2105 virtual void set_ctime(time_point date)
2106 {
2107 m_source->set_ctime(date);
2108 }
2109
2110 virtual void set_atime(time_point date)
2111 {
2112#if SET_FILE_OP_TIMES
2113 m_atime = date;
2114#endif
2115 m_source->set_atime(date);
2116 }
2117
2118 virtual void set_mtime(time_point date)
2119 {
2120#if SET_FILE_OP_TIMES
2121 m_mtime = date;
2122#endif
2123 m_source->set_mtime(date);
2124 }
2125
2126 protected:
2128 void flush_cache()
2129 {
2130 if (m_cache.status != cache_t::cache_t::status_t::dirty)
2131 m_state = state_t::ok;
2132 else if (!m_cache.region.empty()) {
2133 write_cache();
2134 if (ok())
2135 m_cache.status = cache_t::cache_t::status_t::loaded;
2136 }
2137 else {
2138 m_state = state_t::ok;
2139 m_cache.status = cache_t::cache_t::status_t::loaded;
2140 }
2141 }
2142
2143 void invalidate_cache()
2144 {
2145 if (m_cache.status == cache_t::cache_t::status_t::dirty && !m_cache.region.empty()) {
2146 write_cache();
2147 if (!ok()) _Unlikely_
2148 return;
2149 } else
2150 m_state = state_t::ok;
2151 m_cache.status = cache_t::cache_t::status_t::empty;
2152 }
2153
2154 void load_cache(_In_ fpos_t start)
2155 {
2156 stdex_assert(m_cache.status != cache_t::cache_t::status_t::dirty);
2157 start -= start % m_cache.capacity; // Align to cache block size.
2158 m_source->seekbeg(m_cache.region.start = start);
2159 if (m_source->ok()) {
2160 m_cache.region.end = start + m_source->read(m_cache.data, m_cache.capacity);
2161 m_cache.status = cache_t::cache_t::status_t::loaded;
2162 m_state = state_t::ok; // Regardless the read failure, we still might have cached some data.
2163 }
2164 else
2165 m_state = state_t::fail;
2166 }
2167
2168 void write_cache()
2169 {
2170 stdex_assert(m_cache.status == cache_t::cache_t::status_t::dirty);
2171 m_source->seekbeg(m_cache.region.start);
2172 m_source->write(m_cache.data, static_cast<size_t>(m_cache.region.size()));
2173 m_state = m_source->state();
2174 }
2175
2176 basic_file* m_source;
2177 struct cache_t {
2178 uint8_t* data;
2179 size_t capacity;
2180 enum class status_t {
2181 empty = 0,
2182 loaded,
2183 dirty,
2184 } status;
2185 interval<fpos_t> region;
2186
2187 cache_t(_In_ size_t _capacity) :
2188 data(new uint8_t[_capacity]),
2189 capacity(_capacity),
2190 status(status_t::empty),
2191 region(0)
2192 {}
2193
2194 ~cache_t()
2195 {
2196 delete[] data;
2197 }
2198 } m_cache;
2199 fpos_t m_offset;
2200#if SET_FILE_OP_TIMES
2201 time_point
2202 m_atime,
2203 m_mtime;
2204#endif
2206 };
2207
2211 class basic_sys : virtual public basic, public sys_object
2212 {
2213 public:
2214 basic_sys(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) :
2215 basic(state),
2216 sys_object(h)
2217 {}
2218
2219 virtual _Success_(return != 0 || length == 0) size_t read(
2220 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2221 {
2222 stdex_assert(data || !length);
2223 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2224 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2225 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2226 size_t
2227#if defined(_WIN64)
2228 block_size = 0x1F80000;
2229#elif defined(_WIN32)
2230 block_size = 0x3f00000;
2231#else
2232 block_size = SSIZE_MAX;
2233#endif
2234 for (size_t to_read = length;;) {
2235#ifdef _WIN32
2236 // ReadFile() might raise exception (e.g. STATUS_FILE_BAD_FORMAT/0xE0000002).
2237 BOOL succeeded;
2238 DWORD num_read;
2239 __try { succeeded = ReadFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_read, block_size)), &num_read, nullptr); }
2240 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_read = 0; }
2241 if (!succeeded && GetLastError() == ERROR_NO_SYSTEM_RESOURCES && block_size > default_block_size) _Unlikely_ {
2242 // Error "Insufficient system resources exist to complete the requested service." occurs
2243 // occasionally, when attempting to read too much data at once (e.g. over \\TSClient).
2244 block_size = default_block_size;
2245 continue;
2246 }
2247 if (!succeeded) _Unlikely_
2248#else
2249 auto num_read = ::read(m_h, data, std::min<size_t>(to_read, block_size));
2250 if (num_read < 0) _Unlikely_
2251#endif
2252 {
2253 m_state = to_read < length ? state_t::ok : state_t::fail;
2254 return length - to_read;
2255 }
2256 if (!num_read) _Unlikely_ {
2257 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2258 return length - to_read;
2259 }
2260 to_read -= static_cast<size_t>(num_read);
2261 if (!to_read) {
2262 m_state = state_t::ok;
2263 return length;
2264 }
2265 reinterpret_cast<uint8_t*&>(data) += num_read;
2266 }
2267 }
2268
2269 virtual _Success_(return != 0) size_t write(
2270 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2271 {
2272 // Windows Server 2003 and Windows XP: Pipe write operations across a network are limited in size per write.
2273 // The amount varies per platform. For x86 platforms it's 63.97 MB. For x64 platforms it's 31.97 MB. For Itanium
2274 // it's 63.95 MB. For more information regarding pipes, see the Remarks section.
2275 constexpr size_t
2276#if defined(_WIN64)
2277 block_size = 0x1F80000;
2278#elif defined(_WIN32)
2279 block_size = 0x3f00000;
2280#else
2281 block_size = SSIZE_MAX;
2282#endif
2283 for (size_t to_write = length;;) {
2284#ifdef _WIN32
2285 // ReadFile() might raise an exception. Be cautious with WriteFile() too.
2286 BOOL succeeded;
2287 DWORD num_written;
2288 __try { succeeded = WriteFile(m_h, data, static_cast<DWORD>(std::min<size_t>(to_write, block_size)), &num_written, nullptr); }
2289 __except (EXCEPTION_EXECUTE_HANDLER) { succeeded = FALSE; SetLastError(ERROR_UNHANDLED_EXCEPTION); num_written = 0; }
2290 to_write -= num_written;
2291 if (!to_write) {
2292 m_state = state_t::ok;
2293 return length;
2294 }
2295 reinterpret_cast<const uint8_t*&>(data) += num_written;
2296 if (!succeeded) _Unlikely_ {
2297 m_state = state_t::fail;
2298 return length - to_write;
2299 }
2300#else
2301 auto num_written = ::write(m_h, data, std::min<size_t>(to_write, block_size));
2302 if (num_written < 0) _Unlikely_ {
2303 m_state = state_t::fail;
2304 return length - to_write;
2305 }
2306 to_write -= static_cast<size_t>(num_written);
2307 if (!to_write) {
2308 m_state = state_t::ok;
2309 return length;
2310 }
2311 reinterpret_cast<const uint8_t*&>(data) += num_written;
2312#endif
2313 }
2314 }
2315
2316 virtual void close()
2317 {
2318 try {
2320 m_state = state_t::ok;
2321 }
2322 catch (...) {
2323 m_state = state_t::fail;
2324 }
2325 }
2326
2327 virtual void flush()
2328 {
2329#ifdef _WIN32
2330 m_state = FlushFileBuffers(m_h) ? state_t::ok : state_t::fail;
2331#else
2332 m_state = fsync(m_h) >= 0 ? state_t::ok : state_t::fail;
2333#endif
2334 }
2335 };
2336
2340 class buffered_sys : public buffer
2341 {
2342 public:
2343 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) :
2344 buffer(read_buffer_size, write_buffer_size),
2345 m_source(h)
2346 {
2347 init(m_source);
2348 }
2349
2350 virtual ~buffered_sys()
2351 {
2352 done();
2353 }
2354
2355 protected:
2356 basic_sys m_source;
2357 };
2358
2362 class socket : public basic
2363 {
2364 public:
2365 socket(_In_opt_ socket_t h = stdex::invalid_socket, _In_ state_t state = state_t::ok) :
2366 basic(state),
2367 m_h(h)
2368 {}
2369
2370 private:
2371 socket(_In_ const socket& other);
2372 socket& operator =(_In_ const socket& other);
2373
2374 public:
2375 socket(_Inout_ socket&& other) noexcept : m_h(other.m_h)
2376 {
2377 other.m_h = stdex::invalid_socket;
2378 }
2379
2380 socket& operator =(_Inout_ socket&& other) noexcept
2381 {
2382 if (this != std::addressof(other)) {
2383 if (m_h != stdex::invalid_socket)
2384 closesocket(m_h);
2385 m_h = other.m_h;
2386 other.m_h = stdex::invalid_socket;
2387 }
2388 return *this;
2389 }
2390
2398 socket(_In_ int af, _In_ int type, _In_ int protocol)
2399 {
2400 m_h = ::socket(af, type, protocol);
2401 if (m_h == stdex::invalid_socket) _Unlikely_
2402 m_state = state_t::fail;
2403 }
2404
2405 virtual ~socket()
2406 {
2407 if (m_h != stdex::invalid_socket)
2408 closesocket(m_h);
2409 }
2410
2414 operator bool() const noexcept { return m_h != stdex::invalid_socket; }
2415
2419 socket_t get() const noexcept { return m_h; }
2420
2421 virtual _Success_(return != 0 || length == 0) size_t read(
2422 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2423 {
2424 stdex_assert(data || !length);
2425 constexpr int block_size = 0x10000000;
2426 for (size_t to_read = length;;) {
2427 auto num_read = recv(m_h, reinterpret_cast<char*>(data),
2428#ifdef _WIN32
2429 static_cast<int>(std::min<size_t>(to_read, block_size)),
2430#else
2431 std::min<size_t>(to_read, block_size),
2432#endif
2433 0);
2434 if (num_read < 0) _Unlikely_ {
2435 m_state = to_read < length ? state_t::ok : state_t::fail;
2436 return length - to_read;
2437 }
2438 if (!num_read) {
2439 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2440 return length - to_read;
2441 }
2442 to_read -= static_cast<size_t>(num_read);
2443 if (!to_read) {
2444 m_state = state_t::ok;
2445 return length;
2446 }
2447 reinterpret_cast<uint8_t*&>(data) += num_read;
2448 }
2449 }
2450
2451 virtual _Success_(return != 0) size_t write(
2452 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2453 {
2454 stdex_assert(data || !length);
2455 constexpr int block_size = 0x10000000;
2456 for (size_t to_write = length;;) {
2457 auto num_written = send(m_h, reinterpret_cast<const char*>(data),
2458#ifdef _WIN32
2459 static_cast<int>(std::min<size_t>(to_write, block_size)),
2460#else
2461 std::min<size_t>(to_write, block_size),
2462#endif
2463 0);
2464 if (num_written < 0) _Unlikely_ {
2465 m_state = state_t::fail;
2466 return length - to_write;
2467 }
2468 to_write -= static_cast<size_t>(num_written);
2469 if (!to_write) {
2470 m_state = state_t::ok;
2471 return length;
2472 }
2473 reinterpret_cast<const uint8_t*&>(data) += num_written;
2474 }
2475 }
2476
2477 virtual void close()
2478 {
2479 if (m_h != stdex::invalid_socket) {
2480 closesocket(m_h);
2481 m_h = stdex::invalid_socket;
2482 }
2483 m_state = state_t::ok;
2484 }
2485
2486 protected:
2487 socket_t m_h;
2488 };
2489
2490#ifdef _WIN32
2494 class sequential_stream : public basic
2495 {
2496 public:
2497 sequential_stream(_In_ ISequentialStream* source) : m_source(source)
2498 {
2499 m_source->AddRef();
2500 }
2501
2502 virtual ~sequential_stream()
2503 {
2504 m_source->Release();
2505 }
2506
2507 virtual _Success_(return != 0 || length == 0) size_t read(
2508 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2509 {
2510 stdex_assert(data || !length);
2511 for (size_t to_read = length;;) {
2512 HRESULT hr;
2513 ULONG num_read = 0;
2514 __try { hr = m_source->Read(data, (ULONG)std::min<size_t>(to_read, ULONG_MAX), &num_read); }
2515 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2516 if (FAILED(hr)) _Unlikely_ {
2517 m_state = to_read < length ? state_t::ok : state_t::fail;
2518 return length - to_read;
2519 }
2520 to_read -= num_read;
2521 if (hr == S_FALSE) _Unlikely_ {
2522 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2523 return length - to_read;
2524 }
2525 if (!to_read) {
2526 m_state = state_t::ok;
2527 return length;
2528 }
2529 reinterpret_cast<uint8_t*&>(data) += num_read;
2530 }
2531 }
2532
2533 virtual _Success_(return != 0) size_t write(
2534 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2535 {
2536 stdex_assert(data || !length);
2537 for (size_t to_write = length;;) {
2538 HRESULT hr;
2539 ULONG num_written = 0;
2540 __try { hr = m_source->Write(data, static_cast<ULONG>(std::min<size_t>(to_write, ULONG_MAX)), &num_written); }
2541 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2542 // In absence of documentation whether num_written gets set when FAILED(hr) (i.e. partially successful writes),
2543 // assume write failed completely.
2544 if (FAILED(hr)) _Unlikely_ {
2545 m_state = state_t::fail;
2546 return length - to_write;
2547 }
2548 to_write -= num_written;
2549 if (!to_write) {
2550 m_state = state_t::ok;
2551 return length;
2552 }
2553 reinterpret_cast<const uint8_t*&>(data) += num_written;
2554 }
2555 }
2556
2557 protected:
2558 ISequentialStream* m_source;
2559 };
2560
2564 class asp : public basic
2565 {
2566 public:
2567 asp(_In_opt_ IRequest* request, _In_opt_ IResponse* response) :
2568 m_request(request),
2569 m_response(response)
2570 {
2571 if (m_request)
2572 m_request->AddRef();
2573 if (m_response)
2574 m_response->AddRef();
2575 }
2576
2577 virtual ~asp()
2578 {
2579 if (m_request)
2580 m_request->Release();
2581 if (m_response)
2582 m_response->Release();
2583 }
2584
2585 virtual _Success_(return != 0 || length == 0) size_t read(
2586 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
2587 {
2588 stdex_assert(data || !length);
2589 if (!m_request) _Unlikely_ {
2590 m_state = state_t::fail;
2591 return 0;
2592 }
2593 for (size_t to_read = length;;) {
2594 VARIANT var_amount, var_data;
2595 V_VT(&var_amount) = VT_I4;
2596 V_I4(&var_amount) = (LONG)std::min<size_t>(to_read, LONG_MAX);
2597 V_VT(&var_data) = VT_EMPTY;
2598 HRESULT hr = [&]() {
2599 __try { return m_request->BinaryRead(&var_amount, &var_data); }
2600 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2601 }();
2602 if (FAILED(hr)) _Unlikely_ {
2603 m_state = to_read < length ? state_t::ok : state_t::fail;
2604 return length - to_read;
2605 }
2606 stdex_assert(V_VT(&var_amount) == VT_I4);
2607 stdex_assert(V_VT(&var_data) == (VT_ARRAY | VT_UI1));
2608 std::unique_ptr<SAFEARRAY, SafeArrayDestroy_delete> sa(V_ARRAY(&var_data));
2609 if (!V_I4(&var_amount)) _Unlikely_ {
2610 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
2611 return length - to_read;
2612 }
2613 safearray_accessor<uint8_t> a(sa.get());
2614 memcpy(data, a.data(), V_I4(&var_amount));
2615 to_read -= V_I4(&var_amount);
2616 if (!to_read) {
2617 m_state = state_t::ok;
2618 return length;
2619 }
2620 reinterpret_cast<uint8_t*&>(data) += V_I4(&var_amount);
2621 }
2622 }
2623
2624 virtual _Success_(return != 0) size_t write(
2625 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
2626 {
2627 if (!m_response) {
2628 m_state = state_t::fail;
2629 return 0;
2630 }
2631 for (size_t to_write = length;;) {
2632 UINT num_written = static_cast<UINT>(std::min<size_t>(to_write, UINT_MAX));
2633 std::unique_ptr<OLECHAR, SysFreeString_delete> bstr_data(SysAllocStringByteLen(reinterpret_cast<LPCSTR>(data), num_written));
2634 VARIANT var_data;
2635 V_VT(&var_data) = VT_BSTR;
2636 V_BSTR(&var_data) = bstr_data.get();
2637 HRESULT hr = [&]() {
2638 __try { return m_response->BinaryWrite(var_data); }
2639 __except (EXCEPTION_EXECUTE_HANDLER) { return E_FAIL; }
2640 }();
2641 if (FAILED(hr)) _Unlikely_ {
2642 m_state = state_t::fail;
2643 return length - to_write;
2644 }
2645 to_write -= num_written;
2646 if (!to_write) {
2647 m_state = state_t::ok;
2648 return length;
2649 }
2650 reinterpret_cast<const uint8_t*&>(data) += num_written;
2651 }
2652 }
2653
2654 virtual void close()
2655 {
2656 if (m_response) {
2657 __try { m_response->End(); }
2658 __except (EXCEPTION_EXECUTE_HANDLER) {}
2659 }
2660 m_state = state_t::ok;
2661 }
2662
2663 virtual void flush()
2664 {
2665 if (m_response) {
2666 HRESULT hr;
2667 __try { hr = m_response->Flush(); }
2668 __except (EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
2669 m_state = SUCCEEDED(hr) ? state_t::ok : state_t::fail;
2670 }
2671 }
2672
2673 protected:
2674 IRequest* m_request;
2675 IResponse* m_response;
2676 };
2677#endif
2678
2682 enum mode_t
2683 {
2684 mode_for_reading = 1 << 0,
2685 mode_for_writing = 1 << 1,
2686 mode_for_chmod = 1 << 2,
2687
2688 mode_open_existing = 0 << 3,
2689 mode_truncate_existing = 1 << 3,
2690 mode_preserve_existing = 2 << 3,
2691 mode_create_new = 3 << 3,
2692 mode_create = 4 << 3,
2693 mode_disposition_mask = 7 << 3,
2694
2695 mode_append = 1 << 6,
2696 mode_text = 0,
2697 mode_binary = 1 << 7,
2698
2699 share_none = 0,
2700 share_reading = 1 << 8,
2701 share_writing = 1 << 9,
2702 share_deleting = 1 << 10,
2703 share_all = share_reading | share_writing | share_deleting, // Allow others all operations on our file
2704
2705 inherit_handle = 1 << 11,
2706
2707 hint_write_thru = 1 << 12,
2708 hint_no_buffering = 1 << 13,
2709 hint_random_access = 1 << 14,
2710 hint_sequential_access = 1 << 15,
2711 };
2712
2713#pragma warning(push)
2714#pragma warning(disable: 4250)
2718 class file : virtual public basic_file, virtual public basic_sys
2719 {
2720 public:
2721 file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok) : basic_sys(h, state) {}
2722
2729 file(_In_z_ const schar_t* filename, _In_ int mode)
2730 {
2731 open(filename, mode);
2732 }
2733
2740 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
2741 file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode) : file(filename.c_str(), mode) {}
2742
2749 void open(_In_z_ const schar_t* filename, _In_ int mode)
2750 {
2751 if (m_h != invalid_handle)
2752 close();
2753
2754#ifdef _WIN32
2755 DWORD dwDesiredAccess = 0;
2756 if (mode & mode_for_reading) dwDesiredAccess |= GENERIC_READ;
2757 if (mode & mode_for_writing) dwDesiredAccess |= GENERIC_WRITE;
2758 if (mode & mode_for_chmod) dwDesiredAccess |= FILE_WRITE_ATTRIBUTES;
2759
2760 DWORD dwShareMode = 0;
2761 if (mode & share_reading) dwShareMode |= FILE_SHARE_READ;
2762 if (mode & share_writing) dwShareMode |= FILE_SHARE_WRITE;
2763 if (mode & share_deleting) dwShareMode |= FILE_SHARE_DELETE;
2764
2765 SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };
2766 sa.bInheritHandle = mode & inherit_handle ? true : false;
2767
2768 DWORD dwCreationDisposition;
2769 switch (mode & mode_disposition_mask) {
2770 case mode_open_existing: dwCreationDisposition = OPEN_EXISTING; break;
2771 case mode_truncate_existing: dwCreationDisposition = TRUNCATE_EXISTING; break;
2772 case mode_preserve_existing: dwCreationDisposition = OPEN_ALWAYS; break;
2773 case mode_create_new: dwCreationDisposition = CREATE_NEW; break;
2774 case mode_create: dwCreationDisposition = CREATE_ALWAYS; break;
2775 default: throw std::invalid_argument("invalid mode");
2776 }
2777
2778 DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
2779 if (mode & hint_write_thru) dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
2780 if (mode & hint_no_buffering) dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
2781 if (mode & hint_random_access) dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
2782 if (mode & hint_sequential_access) dwFlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
2783
2784 m_h = CreateFile(filename, dwDesiredAccess, dwShareMode, &sa, dwCreationDisposition, dwFlagsAndAttributes, NULL);
2785#else
2786 int flags = 0;
2787 switch (mode & (mode_for_reading | mode_for_writing)) {
2788 case mode_for_reading: flags |= O_RDONLY; break;
2789 case mode_for_writing: flags |= O_WRONLY; break;
2790 case mode_for_reading | mode_for_writing: flags |= O_RDWR; break;
2791 }
2792 switch (mode & mode_disposition_mask) {
2793 case mode_open_existing: break;
2794 case mode_truncate_existing: flags |= O_TRUNC; break;
2795 case mode_preserve_existing: flags |= O_CREAT; break;
2796 case mode_create_new: flags |= O_CREAT | O_EXCL; break;
2797 case mode_create: flags |= O_CREAT | O_TRUNC; break;
2798 default: throw std::invalid_argument("invalid mode");
2799 }
2800 if (mode & hint_write_thru) flags |= O_DSYNC;
2801#ifndef __APPLE__
2802 if (mode & hint_no_buffering) flags |= O_RSYNC;
2803#endif
2804
2805 m_h = ::open(filename, flags, DEFFILEMODE);
2806#endif
2807 if (m_h != invalid_handle) {
2808 m_state = state_t::ok;
2809 if (mode & mode_append)
2810 seek(0, seek_t::end);
2811 }
2812 else
2813 m_state = state_t::fail;
2814 }
2815
2822 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
2823 void open(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
2824 {
2825 open(filename.c_str(), mode);
2826 }
2827
2828 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
2829 {
2830#ifdef _WIN32
2831 LARGE_INTEGER li;
2832 li.QuadPart = offset;
2833 li.LowPart = SetFilePointer(m_h, li.LowPart, &li.HighPart, static_cast<DWORD>(how));
2834 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR) {
2835 m_state = state_t::ok;
2836 return li.QuadPart;
2837 }
2838#else
2839 off64_t result = lseek64(m_h, offset, static_cast<int>(how));
2840 if (result >= 0) {
2841 m_state = state_t::ok;
2842 return static_cast<fpos_t>(result);
2843 }
2844#endif
2845 m_state = state_t::fail;
2846 return fpos_max;
2847 }
2848
2849 virtual fpos_t tell() const
2850 {
2851 if (m_h != invalid_handle) {
2852#ifdef _WIN32
2853 LARGE_INTEGER li;
2854 li.QuadPart = 0;
2855 li.LowPart = SetFilePointer(m_h, 0, &li.HighPart, FILE_CURRENT);
2856 if (li.LowPart != 0xFFFFFFFF || GetLastError() == NO_ERROR)
2857 return li.QuadPart;
2858#else
2859 off64_t result = lseek64(m_h, 0, SEEK_CUR);
2860 if (result >= 0)
2861 return static_cast<fpos_t>(result);
2862#endif
2863 }
2864 return fpos_max;
2865 }
2866
2867 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
2868 {
2869#ifdef _WIN32
2870 LARGE_INTEGER liOffset;
2871 LARGE_INTEGER liSize;
2872 liOffset.QuadPart = offset;
2873 liSize.QuadPart = length;
2874 if (LockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2875 m_state = state_t::ok;
2876 return;
2877 }
2878#else
2879 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2880 if (orig >= 0) {
2881 if (offset > std::numeric_limits<off64_t>::max())
2882 throw std::invalid_argument("file offset too big");
2883 if (length > std::numeric_limits<off64_t>::max())
2884 throw std::invalid_argument("file section length too big");
2885 m_state = lseek64(m_h, static_cast<off64_t>(offset), SEEK_SET) >= 0 && lockf64(m_h, F_LOCK, static_cast<off64_t>(length)) >= 0 ? state_t::ok : state_t::fail;
2886 lseek64(m_h, orig, SEEK_SET);
2887 m_state = state_t::ok;
2888 return;
2889 }
2890#endif
2891 m_state = state_t::fail;
2892 }
2893
2894 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
2895 {
2896#ifdef _WIN32
2897 LARGE_INTEGER liOffset;
2898 LARGE_INTEGER liSize;
2899 liOffset.QuadPart = offset;
2900 liSize.QuadPart = length;
2901 if (UnlockFile(m_h, liOffset.LowPart, liOffset.HighPart, liSize.LowPart, liSize.HighPart)) {
2902 m_state = state_t::ok;
2903 return;
2904 }
2905#else
2906 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2907 if (orig >= 0) {
2908 if (offset > std::numeric_limits<off64_t>::max())
2909 throw std::invalid_argument("file offset too big");
2910 if (length > std::numeric_limits<off64_t>::max())
2911 throw std::invalid_argument("file section length too big");
2912 if (lseek64(m_h, static_cast<off64_t>(offset), SEEK_SET) >= 0 && lockf64(m_h, F_ULOCK, static_cast<off64_t>(length)) >= 0) {
2913 lseek64(m_h, orig, SEEK_SET);
2914 m_state = state_t::ok;
2915 return;
2916 }
2917 lseek64(m_h, orig, SEEK_SET);
2918 }
2919#endif
2920 m_state = state_t::fail;
2921 }
2922
2923 virtual fsize_t size() const
2924 {
2925#ifdef _WIN32
2926 LARGE_INTEGER li;
2927 li.LowPart = GetFileSize(m_h, (LPDWORD)&li.HighPart);
2928 if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR)
2929 li.QuadPart = -1;
2930 return li.QuadPart;
2931#else
2932 off64_t orig = lseek64(m_h, 0, SEEK_CUR);
2933 if (orig >= 0) {
2934 off64_t length = lseek64(m_h, 0, SEEK_END);
2935 lseek64(m_h, orig, SEEK_SET);
2936 if (length >= 0)
2937 return static_cast<fsize_t>(length);
2938 }
2939 return fsize_max;
2940#endif
2941 }
2942
2943 virtual void truncate()
2944 {
2945#ifdef _WIN32
2946 if (SetEndOfFile(m_h)) {
2947 m_state = state_t::ok;
2948 return;
2949 }
2950#else
2951 off64_t length = lseek64(m_h, 0, SEEK_CUR);
2952 if (length >= 0 && ftruncate64(m_h, length) >= 0) {
2953 m_state = state_t::ok;
2954 return;
2955 }
2956#endif
2957 m_state = state_t::fail;
2958 }
2959
2960#ifdef _WIN32
2961 static time_point ft2tp(_In_ const FILETIME& ft)
2962 {
2963#if _HAS_CXX20
2964 uint64_t t = (static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
2965#else
2966 uint64_t t = ((static_cast<int64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) - 116444736000000000ll;
2967#endif
2968 return time_point(time_point::duration(t));
2969 }
2970
2971 static void tp2ft(_In_ time_point tp, _Out_ FILETIME& ft)
2972 {
2973#if _HAS_CXX20
2974 uint64_t t = tp.time_since_epoch().count();
2975#else
2976 uint64_t t = tp.time_since_epoch().count() + 116444736000000000ll;
2977#endif
2978 ft.dwHighDateTime = static_cast<DWORD>((t >> 32) & 0xffffffff);
2979 ft.dwLowDateTime = static_cast<DWORD>(t & 0xffffffff);
2980 }
2981#endif
2982
2983 virtual time_point ctime() const
2984 {
2985#ifdef _WIN32
2986 FILETIME ft;
2987 if (GetFileTime(m_h, &ft, nullptr, nullptr))
2988 return ft2tp(ft);
2989#endif
2990 return time_point::min();
2991 }
2992
2993 virtual time_point atime() const
2994 {
2995#ifdef _WIN32
2996 FILETIME ft;
2997 if (GetFileTime(m_h, nullptr, &ft, nullptr))
2998 return ft2tp(ft);
2999#else
3000 struct stat buf;
3001 if (fstat(m_h, &buf) >= 0)
3002 return clock::from_time_t(buf.st_atime);
3003#endif
3004 return time_point::min();
3005 }
3006
3007 virtual time_point mtime() const
3008 {
3009#ifdef _WIN32
3010 FILETIME ft;
3011 if (GetFileTime(m_h, nullptr, nullptr, &ft))
3012 return ft2tp(ft);
3013#else
3014 struct stat buf;
3015 if (fstat(m_h, &buf) >= 0)
3016 return clock::from_time_t(buf.st_mtime);
3017#endif
3018 return time_point::min();
3019 }
3020
3021 virtual void set_ctime(time_point date)
3022 {
3023 stdex_assert(m_h != invalid_handle);
3024#ifdef _WIN32
3025 FILETIME ft;
3026 tp2ft(date, ft);
3027 if (SetFileTime(m_h, &ft, nullptr, nullptr))
3028 return;
3029 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3030#else
3031 _Unreferenced_(date);
3032 throw std::runtime_error("not supported");
3033#endif
3034 }
3035
3036 virtual void set_atime(time_point date)
3037 {
3038 stdex_assert(m_h != invalid_handle);
3039#ifdef _WIN32
3040 FILETIME ft;
3041 tp2ft(date, ft);
3042 if (SetFileTime(m_h, nullptr, &ft, nullptr))
3043 return;
3044 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3045#else
3046 struct timespec ts[2] = {
3047 { date.time_since_epoch().count(), 0 },
3048 { 0, UTIME_OMIT },
3049 };
3050 if (futimens(m_h, ts) >= 0)
3051 return;
3052 throw std::system_error(errno, std::system_category(), "futimens failed");
3053#endif
3054 }
3055
3056 virtual void set_mtime(time_point date)
3057 {
3058#ifdef _WIN32
3059 FILETIME ft;
3060 tp2ft(date, ft);
3061 if (SetFileTime(m_h, nullptr, nullptr, &ft))
3062 return;
3063 throw std::system_error(GetLastError(), std::system_category(), "SetFileTime failed");
3064#else
3065 struct timespec ts[2] = {
3066 { 0, UTIME_OMIT },
3067 { date.time_since_epoch().count(), 0 },
3068 };
3069 if (futimens(m_h, ts) >= 0)
3070 return;
3071 throw std::system_error(errno, std::system_category(), "futimens failed");
3072#endif
3073 }
3074
3080 static bool exists(_In_z_ const stdex::schar_t* filename)
3081 {
3082#ifdef _WIN32
3083 return GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES;
3084#else
3085 struct stat s;
3086 return stat(filename, &s) == 0;
3087#endif
3088 }
3089
3095 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3096 static bool exists(_In_ const std::basic_string<TR, AX>& filename)
3097 {
3098 return exists(filename.c_str());
3099 }
3100
3108 static bool readonly(_In_z_ const stdex::schar_t* filename)
3109 {
3110#ifdef _WIN32
3111 DWORD dwAttr = GetFileAttributes(filename);
3112 return dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_READONLY) != 0;
3113#else
3114 struct stat s;
3115 return stat(filename, &s) == 0 && (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0;
3116#endif
3117 }
3118
3126 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3127 static bool readonly(_In_ const std::basic_string<TR, AX>& filename)
3128 {
3129 return readonly(filename.c_str());
3130 }
3131 };
3132#pragma warning(pop)
3133
3137 class cached_file : public cache
3138 {
3139 public:
3140 cached_file(_In_opt_ sys_handle h = invalid_handle, _In_ state_t state = state_t::ok, _In_ size_t cache_size = default_cache_size) :
3141 cache(cache_size),
3142 m_source(h, state)
3143 {
3144 init(m_source);
3145 }
3146
3154 cached_file(_In_z_ const schar_t* filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) :
3155 cache(cache_size),
3156 m_source(filename, mode & mode_for_writing ? mode | mode_for_reading : mode)
3157 {
3158 init(m_source);
3159 }
3160
3168 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3169 cached_file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode, _In_ size_t cache_size = default_cache_size) : cached_file(filename.c_str(), mode, cache_size) {}
3170
3171 virtual ~cached_file()
3172 {
3173 done();
3174 }
3175
3182 void open(_In_z_ const schar_t* filename, _In_ int mode)
3183 {
3184 invalidate_cache();
3185 if (!ok()) _Unlikely_{
3186 m_state = state_t::fail;
3187 return;
3188 }
3189 m_source.open(filename, mode & mode_for_writing ? mode | mode_for_reading : mode);
3190 if (m_source.ok()) {
3191 init();
3192 return;
3193 }
3194 m_state = state_t::fail;
3195 }
3196
3203 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3204 void open(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3205 {
3206 open(filename.c_str(), mode);
3207 }
3208
3212 operator bool() const noexcept { return m_source; }
3213
3214 protected:
3215 file m_source;
3216 };
3217
3222 {
3223 public:
3224 memory_file(_In_ state_t state = state_t::ok) :
3225 basic(state),
3226 m_data(nullptr),
3227 m_offset(0),
3228 m_size(0),
3229 m_reserved(0),
3230 m_manage(true)
3231 {
3232#if SET_FILE_OP_TIMES
3233 m_ctime = m_atime = m_mtime = time_point::now();
3234#endif
3235 }
3236
3243 memory_file(_In_ size_t size, _In_ state_t state = state_t::ok) :
3244 basic(state),
3245 m_data(reinterpret_cast<uint8_t*>(malloc(size))),
3246 m_offset(0),
3247 m_size(0),
3249 m_manage(true)
3250 {
3251 if (!m_data) {
3252 m_state = state_t::fail;
3253 throw std::bad_alloc();
3254 }
3255#if SET_FILE_OP_TIMES
3256 m_ctime = m_atime = m_mtime = time_point::now();
3257#endif
3258 }
3259
3269 memory_file(_Inout_ void* data, _In_ size_t size, _In_ size_t reserved, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3270 basic(state),
3271 m_data(reinterpret_cast<uint8_t*>(data)),
3272 m_offset(0),
3273 m_size(size),
3274 m_reserved(reserved),
3275 m_manage(manage)
3276 {
3277 stdex_assert(data || !size);
3278 stdex_assert(reserved >= size);
3279#if SET_FILE_OP_TIMES
3280 m_ctime = m_atime = m_mtime = time_point::now();
3281#endif
3282 }
3283
3292 memory_file(_Inout_ void* data, _In_ size_t size, _In_ bool manage = false, _In_ state_t state = state_t::ok) :
3293 memory_file(data, size, size, manage, state)
3294 {}
3295
3302 memory_file(_In_z_ const schar_t* filename, _In_ int mode) : memory_file()
3303 {
3304 load(filename, mode);
3305 }
3306
3313 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3314 memory_file(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode) : memory_file(filename.c_str(), mode) {}
3315
3321 memory_file(_In_ const memory_file& other) :
3322 basic_file(other),
3323 m_data(reinterpret_cast<uint8_t*>(malloc(other.m_size))),
3324 m_offset(other.m_offset),
3325 m_size(other.m_size),
3326 m_reserved(other.m_size),
3327 m_manage(true)
3328#if SET_FILE_OP_TIMES
3329 , m_ctime(other.m_ctime)
3330 , m_atime(other.m_atime)
3331 , m_mtime(other.m_mtime)
3332#endif
3333 {
3334 if (!m_data) {
3335 m_state = state_t::fail;
3336 throw std::bad_alloc();
3337 }
3338 memcpy(m_data, other.m_data, other.m_size);
3339 }
3340
3347 {
3348 if (this != std::addressof(other)) {
3349 *static_cast<basic_file*>(this) = other;
3350 if (m_manage && m_data)
3351 free(m_data);
3352 m_data = reinterpret_cast<uint8_t*>(malloc(other.m_size));
3353 if (!m_data) {
3354 m_state = state_t::fail;
3355 throw std::bad_alloc();
3356 }
3357 memcpy(m_data, other.m_data, other.m_size);
3358 m_offset = other.m_offset;
3359 m_size = other.m_size;
3360 m_reserved = other.m_size;
3361 m_manage = true;
3362#if SET_FILE_OP_TIMES
3363 m_ctime = other.m_ctime;
3364 m_atime = other.m_atime;
3365 m_mtime = other.m_mtime;
3366#endif
3367 }
3368 return *this;
3369 }
3370
3376 memory_file(_Inout_ memory_file&& other) noexcept :
3377 basic_file(std::move(other)),
3378 m_data(other.m_data),
3379 m_offset(other.m_offset),
3380 m_size(other.m_size),
3381 m_reserved(other.m_reserved),
3382 m_manage(other.m_manage)
3383#if SET_FILE_OP_TIMES
3384 , m_ctime(other.m_ctime)
3385 , m_atime(other.m_atime)
3386 , m_mtime(other.m_mtime)
3387#endif
3388 {
3389 other.m_state = state_t::ok;
3390 other.m_data = nullptr;
3391 other.m_offset = 0;
3392 other.m_size = 0;
3393 other.m_reserved = 0;
3394 other.m_manage = true;
3395#if SET_FILE_OP_TIMES
3396 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3397#endif
3398 }
3399
3405 memory_file& operator=(_Inout_ memory_file&& other) noexcept
3406 {
3407 if (this != std::addressof(other)) {
3408 *static_cast<basic_file*>(this) = std::move(other);
3409 if (m_manage && m_data)
3410 free(m_data);
3411 m_data = other.m_data;
3412 other.m_data = nullptr;
3413 m_offset = other.m_offset;
3414 other.m_offset = 0;
3415 m_size = other.m_size;
3416 other.m_size = 0;
3417 m_reserved = other.m_reserved;
3418 other.m_reserved = 0;
3419 m_manage = other.m_manage;
3420 other.m_manage = true;
3421#if SET_FILE_OP_TIMES
3422 m_ctime = other.m_ctime;
3423 m_atime = other.m_atime;
3424 m_mtime = other.m_mtime;
3425 other.m_ctime = other.m_atime = other.m_mtime = time_point::now();
3426#endif
3427 }
3428 return *this;
3429 }
3430
3431 virtual ~memory_file()
3432 {
3433 if (m_manage && m_data)
3434 free(m_data);
3435 }
3436
3443 void reserve(_In_ size_t required, _In_ bool tight = false) noexcept
3444 {
3445 if (required <= m_reserved && (!tight || required >= m_reserved)) {
3446 m_state = state_t::ok;
3447 return;
3448 }
3449 if (!m_manage) {
3450 m_state = state_t::fail;
3451 return;
3452 }
3453 size_t reserved = tight ? required : ((required + required / 4 + (default_block_size - 1)) / default_block_size) * default_block_size;
3454 auto data = reinterpret_cast<uint8_t*>(realloc(m_data, reserved));
3455 if (!data && reserved) _Unlikely_ {
3456 m_state = state_t::fail;
3457 return;
3458 }
3459 m_data = data;
3460 if (reserved < m_size)
3461 m_size = reserved;
3462 m_reserved = reserved;
3463 m_state = state_t::ok;
3464 }
3465
3472 void load(_In_z_ const schar_t* filename, _In_ int mode)
3473 {
3474 file f(filename, (mode & ~hint_random_access) | mode_for_reading | hint_sequential_access);
3475 if (!f.ok()) {
3476 m_state = state_t::fail;
3477 return;
3478 }
3479 fsize_t size = f.size();
3480 if (size > SIZE_MAX) {
3481 m_state = state_t::fail;
3482 return;
3483 }
3484 reserve(static_cast<size_t>(size), true);
3485 if (!ok()) _Unlikely_ {
3486 return;
3487 }
3488 m_offset = m_size = 0;
3489 write_stream(f);
3490 if (ok())
3491 m_offset = 0;
3492#if SET_FILE_OP_TIMES
3493 m_ctime = f.ctime();
3494 m_atime = f.atime();
3495 m_mtime = f.mtime();
3496#endif
3497 }
3498
3505 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3506 void load(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3507 {
3508 load(filename.c_str(), mode);
3509 }
3510
3517 void save(_In_z_ const schar_t* filename, _In_ int mode)
3518 {
3519 file f(filename, (mode & ~hint_random_access) | mode_for_writing | hint_sequential_access);
3520 if (!f.ok()) {
3521 m_state = state_t::fail;
3522 return;
3523 }
3524 f.write(m_data, m_size);
3525 if (!f.ok()) {
3526 m_state = state_t::fail;
3527 return;
3528 }
3529 f.truncate();
3530#if SET_FILE_OP_TIMES
3531 f.set_ctime(m_ctime);
3532 f.set_atime(m_atime);
3533 f.set_mtime(m_mtime);
3534#endif
3535 }
3536
3543 template <class TR = std::char_traits<schar_t>, class AX = std::allocator<schar_t>>
3544 void save(_In_ const std::basic_string<TR, AX>& filename, _In_ int mode)
3545 {
3546 save(filename.c_str(), mode);
3547 }
3548
3552 const void* data() const { return m_data; }
3553
3554 virtual _Success_(return != 0 || length == 0) size_t read(
3555 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
3556 {
3557 stdex_assert(data || !length);
3558#if SET_FILE_OP_TIMES
3559 m_atime = time_point::now();
3560#endif
3561 size_t available = m_size - m_offset;
3562 if (length <= available) {
3563 memcpy(data, m_data + m_offset, length);
3564 m_offset += length;
3565 m_state = state_t::ok;
3566 return length;
3567 }
3568 if (length && !available) {
3569 m_state = state_t::eof;
3570 return 0;
3571 }
3572 memcpy(data, m_data + m_offset, available);
3573 m_offset += available;
3574 m_state = state_t::ok;
3575 return available;
3576 }
3577
3592 template <class T>
3594 {
3595#if SET_FILE_OP_TIMES
3596 m_atime = time_point::now();
3597#endif
3598 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3599 data = 0;
3600 return *this;
3601 }
3602 size_t end_offset = m_offset + sizeof(T);
3603 if (end_offset <= m_size) {
3604 data = LE2HE(*reinterpret_cast<T*>(m_data + m_offset));
3605 m_offset = end_offset;
3606#if !CHECK_STREAM_STATE
3607 m_state = state_t::ok;
3608#endif
3609 }
3610 else {
3611 data = 0;
3612 m_offset = m_size;
3613 m_state = state_t::eof;
3614 }
3615 return *this;
3616 }
3617
3632 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3633 memory_file& read_str(_Inout_ std::basic_string<T, TR, AX>&data)
3634 {
3635#if SET_FILE_OP_TIMES
3636 m_atime = time_point::now();
3637#endif
3638 if (CHECK_STREAM_STATE && !ok()) _Unlikely_ {
3639 data.clear();
3640 return *this;
3641 }
3642 size_t end_offset = m_offset + sizeof(uint32_t);
3643 if (end_offset <= m_size) {
3644 uint32_t num_chars = LE2HE(*reinterpret_cast<uint32_t*>(m_data + m_offset));
3645 m_offset = end_offset;
3646 end_offset = stdex::add(m_offset, stdex::mul(num_chars, sizeof(T)));
3647 T* start = reinterpret_cast<T*>(m_data + m_offset);
3648 if (end_offset <= m_size) {
3649 data.assign(start, start + num_chars);
3650 m_offset = end_offset;
3651#if !CHECK_STREAM_STATE
3652 m_state = state_t::ok;
3653#endif
3654 return *this;
3655 }
3656 if (end_offset <= m_size)
3657 data.assign(start, reinterpret_cast<T*>(m_data + m_size));
3658 }
3659 m_offset = m_size;
3660 m_state = state_t::eof;
3661 return *this;
3662 }
3663
3664 virtual _Success_(return != 0) size_t write(
3665 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
3666 {
3667 stdex_assert(data || !length);
3668#if SET_FILE_OP_TIMES
3669 m_atime = m_mtime = time_point::now();
3670#endif
3671 size_t end_offset = m_offset + length;
3672 if (end_offset > m_reserved) {
3673 reserve(end_offset);
3674 if (!ok()) _Unlikely_
3675 return 0;
3676 }
3677 memcpy(m_data + m_offset, data, length);
3678 m_offset = end_offset;
3679 if (m_offset > m_size)
3680 m_size = m_offset;
3681 m_state = state_t::ok;
3682 return length;
3683 }
3684
3688 void write_byte(_In_ uint8_t byte, _In_ size_t amount = 1)
3689 {
3690#if SET_FILE_OP_TIMES
3691 m_atime = m_mtime = time_point::now();
3692#endif
3693 size_t end_offset = m_offset + amount;
3694 if (end_offset > m_reserved) {
3695 reserve(end_offset);
3696 if (!ok()) _Unlikely_
3697 return;
3698 }
3699 memset(m_data + m_offset, byte, amount);
3700 m_offset = end_offset;
3701 if (m_offset > m_size)
3702 m_size = m_offset;
3703 m_state = state_t::ok;
3704 }
3705
3720 template <class T>
3722 {
3723#if SET_FILE_OP_TIMES
3724 m_atime = m_mtime = time_point::now();
3725#endif
3726 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3727 return *this;
3728 size_t end_offset = m_offset + sizeof(T);
3729 if (end_offset > m_reserved) {
3730 reserve(end_offset);
3731 if (!ok()) _Unlikely_
3732 return *this;
3733 }
3734 (*reinterpret_cast<T*>(m_data + m_offset)) = HE2LE(data);
3735 m_offset = end_offset;
3736 if (m_offset > m_size)
3737 m_size = m_offset;
3738#if !CHECK_STREAM_STATE
3739 m_state = state_t::ok;
3740#endif
3741 return *this;
3742 }
3743
3758 template <class T>
3759 memory_file& write_str(_In_z_ const T * data)
3760 {
3761#if SET_FILE_OP_TIMES
3762 m_atime = m_mtime = time_point::now();
3763#endif
3764 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3765 return *this;
3766 size_t num_chars = stdex::strlen(data);
3767 if (num_chars > UINT32_MAX)
3768 throw std::invalid_argument("string too long");
3769 size_t size_chars = num_chars * sizeof(T);
3770 size_t size = sizeof(uint32_t) + size_chars;
3771 size_t end_offset = m_offset + size;
3772 if (end_offset > m_reserved) {
3773 reserve(end_offset);
3774 if (!ok()) _Unlikely_
3775 return *this;
3776 }
3777 auto p = m_data + m_offset;
3778 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3779 memcpy(p + sizeof(uint32_t), data, size_chars);
3780 m_offset = end_offset;
3781 if (m_offset > m_size)
3782 m_size = m_offset;
3783#if !CHECK_STREAM_STATE
3784 m_state = state_t::ok;
3785#endif
3786 return *this;
3787 }
3788
3803 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3804 memory_file& write_str(_In_ const std::basic_string<T, TR, AX>& data)
3805 {
3806#if SET_FILE_OP_TIMES
3807 m_atime = m_mtime = time_point::now();
3808#endif
3809 if (CHECK_STREAM_STATE && !ok()) _Unlikely_
3810 return *this;
3811 size_t num_chars = data.size();
3812 if (num_chars > UINT32_MAX)
3813 throw std::invalid_argument("string too long");
3814 size_t size_chars = num_chars * sizeof(T);
3815 size_t size = sizeof(uint32_t) + size_chars;
3816 size_t end_offset = m_offset + size;
3817 if (end_offset > m_reserved) {
3818 reserve(end_offset);
3819 if (!ok()) _Unlikely_
3820 return *this;
3821 }
3822 auto p = m_data + m_offset;
3823 *reinterpret_cast<uint32_t*>(p) = HE2LE((uint32_t)num_chars);
3824 memcpy(p + sizeof(uint32_t), data.data(), size_chars);
3825 m_offset = end_offset;
3826 if (m_offset > m_size)
3827 m_size = m_offset;
3828#if !CHECK_STREAM_STATE
3829 m_state = state_t::ok;
3830#endif
3831 return *this;
3832 }
3833
3839 size_t write_stream(_Inout_ basic & stream, _In_ size_t amount = SIZE_MAX)
3840 {
3841#if SET_FILE_OP_TIMES
3842 m_atime = m_mtime = time_point::now();
3843#endif
3844 size_t num_read, dst_offset = m_offset, dst_size = m_offset;
3845 size_t num_copied = 0, to_write = amount;
3846 m_state = state_t::ok;
3847 if (amount != SIZE_MAX) {
3848 dst_size = stdex::add(dst_size, amount);
3849 reserve(dst_size);
3850 if (!ok()) _Unlikely_
3851 return 0;
3852 while (to_write) {
3853 num_read = stream.read(m_data + dst_offset, to_write);
3854 /*dst_size =*/ dst_offset += num_read;
3855 num_copied += num_read;
3856 to_write -= num_read;
3857 if (!stream.ok()) {
3858 if (stream.state() != state_t::eof)
3859 m_state = state_t::fail;
3860 break;
3861 }
3862 };
3863 }
3864 else {
3865 size_t block_size;
3866 while (to_write) {
3867 block_size = std::min(to_write, default_block_size);
3868 dst_size = stdex::add(dst_size, block_size);
3869 reserve(dst_size);
3870 if (!ok()) _Unlikely_
3871 break;
3872 num_read = stream.read(m_data + dst_offset, block_size);
3873 dst_size = dst_offset += num_read;
3874 num_copied += num_read;
3875 to_write -= num_read;
3876 if (!stream.ok()) {
3877 if (stream.state() != state_t::eof)
3878 m_state = state_t::fail;
3879 break;
3880 }
3881 };
3882 }
3883 m_offset = dst_offset;
3884 if (m_offset > m_size)
3885 m_size = m_offset;
3886 return num_copied;
3887 }
3888
3889 virtual void close()
3890 {
3891 if (m_manage && m_data)
3892 free(m_data);
3893 m_data = nullptr;
3894 m_manage = true;
3895 m_offset = 0;
3896 m_size = m_reserved = 0;
3897#if SET_FILE_OP_TIMES
3898 m_ctime = m_atime = m_mtime = time_point::min();
3899#endif
3900 m_state = state_t::ok;
3901 }
3902
3903 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
3904 {
3905 switch (how) {
3906 case seek_t::beg: break;
3907 case seek_t::cur: offset = static_cast<foff_t>(m_offset) + offset; break;
3908 case seek_t::end: offset = static_cast<foff_t>(m_size) + offset; break;
3909 default: throw std::invalid_argument("unknown seek origin");
3910 }
3911 if (offset < 0) _Unlikely_
3912 throw std::invalid_argument("negative file offset");
3913 if (static_cast<fpos_t>(offset) > SIZE_MAX) _Unlikely_
3914 throw std::invalid_argument("file offset too big");
3915 m_state = state_t::ok;
3916 return m_offset = static_cast<size_t>(offset);
3917 }
3918
3919 virtual fpos_t tell() const
3920 {
3921 return m_offset;
3922 }
3923
3924 virtual fsize_t size() const
3925 {
3926 return m_size;
3927 }
3928
3929 virtual void truncate()
3930 {
3931#if SET_FILE_OP_TIMES
3932 m_atime = m_mtime = time_point::now();
3933#endif
3934 m_size = m_offset;
3936 }
3937
3938#if SET_FILE_OP_TIMES
3939 virtual time_point ctime() const
3940 {
3941 return m_ctime;
3942 }
3943
3944 virtual time_point atime() const
3945 {
3946 return m_atime;
3947 }
3948
3949 virtual time_point mtime() const
3950 {
3951 return m_mtime;
3952 }
3953
3954 virtual void set_ctime(time_point date)
3955 {
3956 m_ctime = date;
3957 }
3958
3959 virtual void set_atime(time_point date)
3960 {
3961 m_atime = date;
3962 }
3963
3964 virtual void set_mtime(time_point date)
3965 {
3966 m_mtime = date;
3967 }
3968#endif
3969
3970 protected:
3978 template <class T>
3979 void set(_In_ fpos_t offset, _In_ const T data)
3980 {
3981#if SET_FILE_OP_TIMES
3982 m_atime = m_mtime = time_point::now();
3983#endif
3984 stdex_assert(offset + sizeof(T) < m_size);
3985 (*reinterpret_cast<T*>(m_data + offset)) = HE2LE(data);
3986 }
3987
3988 public:
3989 void set(_In_ fpos_t offset, _In_ const int8_t data) { set<int8_t>(offset, data); }
3990 void set(_In_ fpos_t offset, _In_ const int16_t data) { set<int16_t>(offset, data); }
3991 void set(_In_ fpos_t offset, _In_ const int32_t data) { set<int32_t>(offset, data); }
3992 void set(_In_ fpos_t offset, _In_ const int64_t data) { set<int64_t>(offset, data); }
3993 void set(_In_ fpos_t offset, _In_ const uint8_t data) { set<uint8_t>(offset, data); }
3994 void set(_In_ fpos_t offset, _In_ const uint16_t data) { set<uint16_t>(offset, data); }
3995 void set(_In_ fpos_t offset, _In_ const uint32_t data) { set<uint32_t>(offset, data); }
3996 void set(_In_ fpos_t offset, _In_ const uint64_t data) { set<uint64_t>(offset, data); }
3997 void set(_In_ fpos_t offset, _In_ const float data) { set<float>(offset, data); }
3998 void set(_In_ fpos_t offset, _In_ const double data) { set<double>(offset, data); }
3999 void set(_In_ fpos_t offset, _In_ const char data) { set<char>(offset, data); }
4000#ifdef _NATIVE_WCHAR_T_DEFINED
4001 void set(_In_ fpos_t offset, _In_ const wchar_t data) { set<wchar_t>(offset, data); }
4002#endif
4003
4011 protected:
4012 template <class T>
4013 void get(_In_ fpos_t offset, _Out_ T & data)
4014 {
4015 stdex_assert(offset + sizeof(T) < m_size);
4016 data = LE2HE(*(T*)(m_data + offset));
4017#if SET_FILE_OP_TIMES
4018 m_atime = time_point::now();
4019#endif
4020 }
4021
4022 public:
4023 void get(_In_ fpos_t offset, _Out_ int8_t & data) { get<int8_t>(offset, data); }
4024 void get(_In_ fpos_t offset, _Out_ int16_t & data) { get<int16_t>(offset, data); }
4025 void get(_In_ fpos_t offset, _Out_ int32_t & data) { get<int32_t>(offset, data); }
4026 void get(_In_ fpos_t offset, _Out_ int64_t & data) { get<int64_t>(offset, data); }
4027 void get(_In_ fpos_t offset, _Out_ uint8_t & data) { get<uint8_t>(offset, data); }
4028 void get(_In_ fpos_t offset, _Out_ uint16_t & data) { get<uint16_t>(offset, data); }
4029 void get(_In_ fpos_t offset, _Out_ uint32_t & data) { get<uint32_t>(offset, data); }
4030 void get(_In_ fpos_t offset, _Out_ uint64_t & data) { get<uint64_t>(offset, data); }
4031 void get(_In_ fpos_t offset, _Out_ float& data) { get<float>(offset, data); }
4032 void get(_In_ fpos_t offset, _Out_ double& data) { get<double>(offset, data); }
4033 void get(_In_ fpos_t offset, _Out_ char& data) { get<char>(offset, data); }
4034#ifdef _NATIVE_WCHAR_T_DEFINED
4035 void get(_In_ fpos_t offset, _Out_ wchar_t& data) { get<wchar_t>(offset, data); }
4036#endif
4037
4038 memory_file& operator <<(_In_ const int8_t data) { return write_data(data); }
4039 memory_file& operator >>(_Out_ int8_t & data) { return read_data(data); }
4040 memory_file& operator <<(_In_ const int16_t data) { return write_data(data); }
4041 memory_file& operator >>(_Out_ int16_t & data) { return read_data(data); }
4042 memory_file& operator <<(_In_ const int32_t data) { return write_data(data); }
4043 memory_file& operator >>(_Out_ int32_t & data) { return read_data(data); }
4044 memory_file& operator <<(_In_ const int64_t data) { return write_data(data); }
4045 memory_file& operator >>(_Out_ int64_t & data) { return read_data(data); }
4046 memory_file& operator <<(_In_ const uint8_t data) { return write_data(data); }
4047 memory_file& operator >>(_Out_ uint8_t & data) { return read_data(data); }
4048 memory_file& operator <<(_In_ const uint16_t data) { return write_data(data); }
4049 memory_file& operator >>(_Out_ uint16_t & data) { return read_data(data); }
4050 memory_file& operator <<(_In_ const uint32_t data) { return write_data(data); }
4051 memory_file& operator >>(_Out_ uint32_t & data) { return read_data(data); }
4052 memory_file& operator <<(_In_ const uint64_t data) { return write_data(data); }
4053 memory_file& operator >>(_Out_ uint64_t & data) { return read_data(data); }
4054 memory_file& operator <<(_In_ const float data) { return write_data(data); }
4055 memory_file& operator >>(_Out_ float& data) { return read_data(data); }
4056 memory_file& operator <<(_In_ const double data) { return write_data(data); }
4057 memory_file& operator >>(_Out_ double& data) { return read_data(data); }
4058 memory_file& operator <<(_In_ const char data) { return write_data(data); }
4059 memory_file& operator >>(_Out_ char& data) { return read_data(data); }
4060#ifdef _NATIVE_WCHAR_T_DEFINED
4061 memory_file& operator <<(_In_ const wchar_t data) { return write_data(data); }
4062 memory_file& operator >>(_Out_ wchar_t& data) { return read_data(data); }
4063#endif
4064 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
4065 memory_file& operator >>(_Out_ std::basic_string<T, TR, AX>&data) { return read_str(data); }
4066 template <class T>
4067 memory_file& operator <<(_In_ const T * data) { return write_str(data); }
4068 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
4069 memory_file& operator <<(_In_ const std::basic_string<T, TR, AX>& data) { return write_str(data); }
4070
4071 protected:
4072 uint8_t* m_data;
4074 size_t m_offset;
4075 size_t m_size;
4076 size_t m_reserved;
4077#if SET_FILE_OP_TIMES
4078 time_point
4079 m_ctime,
4080 m_atime,
4081 m_mtime;
4082#endif
4083 };
4084
4088 class fifo : public basic {
4089 public:
4090 fifo() :
4091 m_offset(0),
4092 m_size(0),
4093 m_head(nullptr),
4094 m_tail(nullptr)
4095 {}
4096
4097 virtual ~fifo()
4098 {
4099 while (m_head) {
4100 auto p = m_head;
4101 m_head = p->next;
4102 delete p;
4103 }
4104 }
4105
4106#pragma warning(suppress: 6101) // See [2] below
4107 virtual _Success_(return != 0 || length == 0) size_t read(
4108 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4109 {
4110 stdex_assert(data || !length);
4111 for (size_t to_read = length;;) {
4112 if (!m_head) _Unlikely_ {
4113 m_state = to_read < length || !length ? state_t::ok : state_t::eof;
4114 return length - to_read; // [2] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
4115 }
4116 size_t remaining = m_head->size - m_offset;
4117 if (remaining > to_read) {
4118 memcpy(data, m_head->data + m_offset, to_read);
4119 m_offset += to_read;
4120 m_size -= to_read;
4121 m_state = state_t::ok;
4122 return length;
4123 }
4124 memcpy(data, m_head->data + m_offset, remaining);
4125 m_offset = 0;
4126 m_size -= remaining;
4127 reinterpret_cast<uint8_t*&>(data) += remaining;
4128 to_read -= remaining;
4129 auto p = m_head;
4130 m_head = p->next;
4131 delete p;
4132 }
4133 }
4134
4135 virtual _Success_(return != 0) size_t write(
4136 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4137 {
4138 stdex_assert(data || !length);
4139 try {
4140 std::unique_ptr<node_t> n(reinterpret_cast<node_t*>(new uint8_t[sizeof(node_t) + length]));
4141 n->next = nullptr;
4142 n->size = length;
4143 memcpy(n->data, data, length);
4144 m_size += length;
4145 if (m_head)
4146 m_tail = m_tail->next = n.release();
4147 else
4148 m_head = m_tail = n.release();
4149 m_state = state_t::ok;
4150 return length;
4151 }
4152 catch (const std::bad_alloc&) {
4153 m_state = state_t::fail;
4154 return 0;
4155 }
4156 }
4157
4158 virtual void close()
4159 {
4160 m_size = m_offset = 0;
4161 while (m_head) {
4162 auto p = m_head;
4163 m_head = p->next;
4164 delete p;
4165 }
4166 m_state = state_t::ok;
4167 }
4168
4172 size_t size() const { return m_size; };
4173
4174 protected:
4175 size_t m_offset, m_size;
4176 struct node_t {
4177 node_t* next;
4178 size_t size;
4179#pragma warning(suppress:4200)
4180 uint8_t data[0];
4181 } *m_head, * m_tail;
4182 };
4183
4187 class diag_file : public basic_file {
4188 public:
4189 diag_file(_In_count_(num_files) basic_file* const* files, _In_ size_t num_files) :
4190 basic(num_files ? files[0]->state() : state_t::fail),
4191 m_files(files, files + num_files)
4192 {}
4193
4194 virtual _Success_(return != 0 || length == 0) size_t read(
4195 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
4196 {
4197 stdex_assert(data || !length);
4198 if (m_files.empty()) {
4199 m_state = state_t::fail;
4200 return 0;
4201 }
4202 size_t result = m_files[0]->read(data, length);
4203 stdex_assert(result <= length);
4204 m_state = m_files[0]->state();
4205 if (length > m_tmp.size())
4206 m_tmp.resize(length);
4207 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4208 if (m_files[i]->read(m_tmp.data(), length) != result ||
4209 memcmp(m_tmp.data(), data, result))
4210 throw std::runtime_error("read mismatch");
4211 if (m_files[i]->state() != m_state)
4212 throw std::runtime_error("state mismatch");
4213 }
4214 return result;
4215 }
4216
4217 virtual _Success_(return != 0) size_t write(
4218 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
4219 {
4220 if (m_files.empty()) {
4221 m_state = state_t::fail;
4222 return 0;
4223 }
4224 size_t result = m_files[0]->write(data, length);
4225 m_state = m_files[0]->state();
4226 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4227 if (m_files[i]->write(data, length) != result)
4228 throw std::runtime_error("write mismatch");
4229 if (m_files[i]->state() != m_state)
4230 throw std::runtime_error("state mismatch");
4231 }
4232 return result;
4233 }
4234
4235 virtual void flush()
4236 {
4237 if (m_files.empty()) {
4238 m_state = state_t::ok;
4239 return;
4240 }
4241 m_files[0]->flush();
4242 m_state = m_files[0]->state();
4243 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4244 m_files[i]->flush();
4245 if (m_files[i]->state() != m_state)
4246 throw std::runtime_error("state mismatch");
4247 }
4248 }
4249
4250 virtual void close()
4251 {
4252 if (m_files.empty()) {
4253 m_state = state_t::ok;
4254 return;
4255 }
4256 m_files[0]->close();
4257 m_state = m_files[0]->state();
4258 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4259 m_files[i]->close();
4260 if (m_files[i]->state() != m_state)
4261 throw std::runtime_error("state mismatch");
4262 }
4263 m_tmp.clear();
4264 m_tmp.shrink_to_fit();
4265 }
4266
4267 virtual fpos_t seek(_In_ foff_t offset, _In_ seek_t how = seek_t::beg)
4268 {
4269 if (m_files.empty()) {
4270 m_state = state_t::fail;
4271 return fpos_max;
4272 }
4273 fpos_t result = m_files[0]->seek(offset, how);
4274 m_state = m_files[0]->state();
4275 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4276 if (m_files[i]->seek(offset, how) != result)
4277 throw std::runtime_error("seek mismatch");
4278 if (m_files[i]->state() != m_state)
4279 throw std::runtime_error("state mismatch");
4280 }
4281 return result;
4282 }
4283
4284 virtual fpos_t tell() const
4285 {
4286 if (m_files.empty())
4287 return fpos_max;
4288 fpos_t result = m_files[0]->tell();
4289 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4290 if (m_files[i]->tell() != result)
4291 throw std::runtime_error("tell mismatch");
4292 }
4293 return result;
4294 }
4295
4296 virtual void lock(_In_ fpos_t offset, _In_ fsize_t length)
4297 {
4298 if (m_files.empty())
4299 m_state = state_t::fail;
4300 m_files[0]->lock(offset, length);
4301 m_state = m_files[0]->state();
4302 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4303 m_files[i]->lock(offset, length);
4304 if (m_files[i]->state() != m_state)
4305 throw std::runtime_error("state mismatch");
4306 }
4307 }
4308
4309 virtual void unlock(_In_ fpos_t offset, _In_ fsize_t length)
4310 {
4311 if (m_files.empty())
4312 m_state = state_t::fail;
4313 m_files[0]->unlock(offset, length);
4314 m_state = m_files[0]->state();
4315 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4316 m_files[i]->unlock(offset, length);
4317 if (m_files[i]->state() != m_state)
4318 throw std::runtime_error("state mismatch");
4319 }
4320 }
4321
4322 virtual fsize_t size() const
4323 {
4324 if (m_files.empty())
4325 return fsize_max;
4326 fsize_t result = m_files[0]->size();
4327 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4328 if (m_files[i]->size() != result)
4329 throw std::runtime_error("size mismatch");
4330 }
4331 return result;
4332 }
4333
4334 virtual void truncate()
4335 {
4336 if (m_files.empty())
4337 m_state = state_t::fail;
4338 m_files[0]->truncate();
4339 m_state = m_files[0]->state();
4340 for (size_t i = 1, n = m_files.size(); i < n; ++i) {
4341 m_files[i]->truncate();
4342 if (m_files[i]->state() != m_state)
4343 throw std::runtime_error("state mismatch");
4344 }
4345 }
4346
4347 protected:
4348 std::vector<basic_file*> m_files;
4349 std::vector<uint8_t> m_tmp;
4350 };
4351 }
4352}
4353
4354#if defined(__GNUC__)
4355#pragma GCC diagnostic pop
4356#endif
Operating system object base class.
Definition system.hpp:142
virtual void close()
Closes object.
Definition system.hpp:183
Encoding converter context.
Definition unicode.hpp:139
locale_t helper class to free_locale when going out of scope.
Definition locale.hpp:74
Provides read-ahead stream capability.
Definition stream.hpp:1257
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:1271
Provides write-back stream capability.
Definition stream.hpp:1324
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:1337
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1361
Basic seekable stream operations.
Definition stream.hpp:818
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:864
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:913
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:882
virtual void truncate()=0
Sets file size - truncates the remainder of file content from the current file position to the end of...
virtual fsize_t size() const =0
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
charset_id read_charset(charset_id default_charset=charset_id::system)
Attempts to detect text-file charset based on UTF-32, UTF-16 or UTF-8 BOM.
Definition stream.hpp:988
fpos_t seekbeg(fpos_t offset)
Seeks to absolute file position.
Definition stream.hpp:845
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:820
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:955
fpos_t seekcur(foff_t offset)
Seeks to relative from current file position.
Definition stream.hpp:855
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:921
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:937
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:892
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:929
fpos_t seekend(foff_t offset)
Seeks to relative from end file position.
Definition stream.hpp:862
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:946
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:2212
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:2269
virtual void flush()
Persists volatile element data.
Definition stream.hpp:2327
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:2219
virtual void close()
Closes the stream.
Definition stream.hpp:2316
‍UTF-8 byte-order-mark
Definition stream.hpp:85
size_t write_array(const T_from *str, charset_encoder< T_from, T_to > &encoder)
Writes array of characters to the stream.
Definition stream.hpp:408
bool ok() const
Returns true if the stream state is clean i.e. previous operation was successful.
Definition stream.hpp:181
size_t readln_and_attach(std::basic_string< T, TR, AX > &str)
Reads stream to the end-of-line or end-of-file and append to str.
Definition stream.hpp:340
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:631
state_t state() const
Returns stream state after last operation.
Definition stream.hpp:176
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:617
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:644
virtual void flush()
Persists volatile element data.
Definition stream.hpp:132
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:148
virtual void close()
Closes the stream.
Definition stream.hpp:140
uint8_t read_byte()
Reads one byte of data.
Definition stream.hpp:216
virtual std::vector< uint8_t > read_remainder(size_t max_length=SIZE_MAX)
Reads and returns remainder of the stream.
Definition stream.hpp:190
size_t write_sprintf(_Printf_format_string_params_(2) const char *format, locale_t locale,...)
Writes formatted string to the stream.
Definition stream.hpp:603
size_t readln(std::basic_string< T_to, TR, AX > &str, charset_encoder< T_from, T_to > &encoder)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:324
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:376
size_t readln(std::basic_string< T, TR, AX > &str)
Reads stream to the end-of-line or end-of-file.
Definition stream.hpp:312
basic & read_str(std::basic_string< T, TR, AX > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:471
basic & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:503
size_t readln_and_attach(std::basic_string< T_to, TR, AX > &str, charset_encoder< T_from, T_to > &encoder)
Reads stream to the end-of-line or end-of-file and append to str.
Definition stream.hpp:361
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:102
void write_charset(charset_id charset)
Writes UTF8, UTF-16 or UTF-32 byte-order-mark.
Definition stream.hpp:588
basic & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:293
fsize_t write_stream(basic &stream, fsize_t amount=fsize_max)
Writes content of another stream.
Definition stream.hpp:563
size_t write_array(const std::basic_string< T_from, TR, AX > &str, charset_encoder< T_from, T_to > &encoder)
Writes array of characters to the stream.
Definition stream.hpp:449
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:120
basic & write_str(const std::basic_string< T, TR, AX > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:528
size_t write_array(_In_reads_or_z_opt_(num_chars) const T_from *str, size_t num_chars, charset_encoder< T_from, T_to > &encoder)
Writes array of characters to the stream.
Definition stream.hpp:429
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:394
void write_byte(uint8_t byte, fsize_t amount=1)
Writes a byte of data.
Definition stream.hpp:227
basic & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:265
Buffered read/write stream.
Definition stream.hpp:1395
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1504
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:1425
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:1461
Buffered OS data stream (file, pipe, socket...)
Definition stream.hpp:2341
Cached file.
Definition stream.hpp:1805
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2082
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2061
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:1872
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2087
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2048
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:2096
virtual void close()
Closes the stream.
Definition stream.hpp:1991
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:2118
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2042
virtual void flush()
Persists volatile element data.
Definition stream.hpp:2000
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:1938
virtual fsize_t size() const
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:2054
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:2105
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:2037
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:2110
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2011
Cached file-system file.
Definition stream.hpp:3138
void open(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:3204
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:3182
cached_file(const schar_t *filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3154
cached_file(const std::basic_string< TR, AX > &filename, int mode, size_t cache_size=default_cache_size)
Opens file.
Definition stream.hpp:3169
Modifies data on the fly when reading from/writing to a source stream. Could also be used to modify r...
Definition stream.hpp:1023
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1074
virtual void close()
Closes the stream.
Definition stream.hpp:1068
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:1052
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:1060
Compares multiple files to perform the same.
Definition stream.hpp:4187
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:4334
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:4217
virtual void close()
Closes the stream.
Definition stream.hpp:4250
virtual fsize_t size() const
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:4322
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:4296
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:4309
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:4267
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:4284
virtual void flush()
Persists volatile element data.
Definition stream.hpp:4235
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:4194
In-memory FIFO queue.
Definition stream.hpp:4088
virtual void close()
Closes the stream.
Definition stream.hpp:4158
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:4135
size_t size() const
Returns total size of pending data in the queue.
Definition stream.hpp:4172
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:4107
Limits file reading/writing to a predefined window.
Definition stream.hpp:1695
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:1788
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1738
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:1751
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:1744
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:1718
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:1763
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:1704
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:1773
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:1757
virtual void close()
Closes the stream.
Definition stream.hpp:1732
virtual fsize_t size() const
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:1783
File-system file.
Definition stream.hpp:2719
virtual fsize_t size() const
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:2923
static bool readonly(const std::basic_string< TR, AX > &filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3127
virtual time_point mtime() const
Returns file modification time.
Definition stream.hpp:3007
virtual void unlock(fpos_t offset, fsize_t length)
Unlocks file section for exclusive access.
Definition stream.hpp:2894
file(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2729
virtual void set_ctime(time_point date)
Sets file create time.
Definition stream.hpp:3021
static bool readonly(const stdex::schar_t *filename)
Checks if file/folder/symlink is read-only.
Definition stream.hpp:3108
virtual time_point atime() const
Returns file access time.
Definition stream.hpp:2993
static bool exists(const std::basic_string< TR, AX > &filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3096
void open(const schar_t *filename, int mode)
Opens file.
Definition stream.hpp:2749
virtual void set_mtime(time_point date)
Sets file modification time.
Definition stream.hpp:3056
virtual void set_atime(time_point date)
Sets file access time.
Definition stream.hpp:3036
virtual void lock(fpos_t offset, fsize_t length)
Locks file section for exclusive access.
Definition stream.hpp:2867
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:2943
void open(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:2823
virtual time_point ctime() const
Returns file creation time.
Definition stream.hpp:2983
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:2828
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:2849
file(const std::basic_string< TR, AX > &filename, int mode)
Opens file.
Definition stream.hpp:2741
static bool exists(const stdex::schar_t *filename)
Checks if file/folder/symlink likely exists.
Definition stream.hpp:3080
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1552
fsize_t read_limit
Number of bytes left that may be read from the stream.
Definition stream.hpp:1604
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:1560
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:1581
fsize_t write_limit
Number of bytes left, that can be written to the stream.
Definition stream.hpp:1605
In-memory file.
Definition stream.hpp:3222
void save(const std::basic_string< TR, AX > &filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3544
memory_file & operator=(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3405
virtual fsize_t size() const
Returns file size Should the file size cannot be determined, the method returns fsize_max and it does...
Definition stream.hpp:3924
memory_file(const schar_t *filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3302
memory_file & read_str(std::basic_string< T, TR, AX > &data)
Reads length-prefixed string from the stream.
Definition stream.hpp:3633
memory_file(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3321
memory_file & write_str(const std::basic_string< T, TR, AX > &data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3804
void load(const std::basic_string< TR, AX > &filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3506
size_t m_size
file size
Definition stream.hpp:4075
void get(fpos_t offset, T &data)
Reads data from specified file location This does not move file pointer. It checks for data size stde...
Definition stream.hpp:4013
size_t write_stream(basic &stream, size_t amount=SIZE_MAX)
Writes content of another stream.
Definition stream.hpp:3839
uint8_t * m_data
file data
Definition stream.hpp:4072
memory_file & read_data(T &data)
Reads one primitive data type.
Definition stream.hpp:3593
virtual void close()
Closes the stream.
Definition stream.hpp:3889
memory_file(const std::basic_string< TR, AX > &filename, int mode)
Loads content from file-system file.
Definition stream.hpp:3314
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:3554
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:3919
size_t m_reserved
reserved file size
Definition stream.hpp:4076
memory_file(size_t size, state_t state=state_t::ok)
Creates an empty file of reserved size.
Definition stream.hpp:3243
void reserve(size_t required, bool tight=false) noexcept
Reallocates memory.
Definition stream.hpp:3443
memory_file(memory_file &&other) noexcept
Moves content from another file.
Definition stream.hpp:3376
void write_byte(uint8_t byte, size_t amount=1)
Writes a byte of data.
Definition stream.hpp:3688
memory_file & operator=(const memory_file &other)
Copies content from another file.
Definition stream.hpp:3346
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:3979
size_t m_offset
file pointer
Definition stream.hpp:4074
void save(const schar_t *filename, int mode)
Saves content to a file-system file.
Definition stream.hpp:3517
void load(const schar_t *filename, int mode)
Loads content from a file-system file.
Definition stream.hpp:3472
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3903
virtual void truncate()
Sets file size - truncates the remainder of file content from the current file position to the end of...
Definition stream.hpp:3929
memory_file & write_data(const T data)
Writes one primitive data type.
Definition stream.hpp:3721
memory_file & write_str(const T *data)
Writes string to the stream length-prefixed.
Definition stream.hpp:3759
bool m_manage
may reallocate m_data?
Definition stream.hpp:4073
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:3292
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:3664
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:3269
const void * data() const
Returns pointer to data.
Definition stream.hpp:3552
Definition stream.hpp:1171
enum stdex::stream::replicator::worker::op_t op
Operation to perform.
size_t num_written
Number of bytes written.
Definition stream.hpp:1220
size_t length
Byte limit of data to write.
Definition stream.hpp:1219
const void * data
Data to write.
Definition stream.hpp:1218
Replicates writing of the same data to multiple streams.
Definition stream.hpp:1088
void push_back(basic *source)
Adds stream on the list.
Definition stream.hpp:1107
virtual void flush()
Persists volatile element data.
Definition stream.hpp:1164
void remove(basic *source)
Removes stream from the list.
Definition stream.hpp:1115
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:1132
virtual void close()
Closes the stream.
Definition stream.hpp:1159
Socket stream.
Definition stream.hpp:2363
socket_t get() const noexcept
Returns socket handle.
Definition stream.hpp:2419
virtual void close()
Closes the stream.
Definition stream.hpp:2477
socket(int af, int type, int protocol)
Creates a socket.
Definition stream.hpp:2398
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:2421
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:2451
Limits reading from/writing to stream to a predefined window.
Definition stream.hpp:1612
fpos_t write_offset
Number of bytes to discard on write.
Definition stream.hpp:1688
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:1649
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:1620
fpos_t read_offset
Number of bytes to skip on read.
Definition stream.hpp:1687
Numerical interval.
Definition interval.hpp:18
bool contains(T x) const
Is value in interval?
Definition interval.hpp:79
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:1529
Definition stream.hpp:4176