Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
InputStreamFromStdIStream.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
7#include "Stroika/Foundation/Execution/Exceptions.h"
8#include "Stroika/Foundation/Execution/Throw.h"
12
13namespace Stroika::Foundation::Streams::iostream::InputStreamFromStdIStream {
14
15 namespace Private_ {
16 template <typename ELEMENT_TYPE, typename BASIC_ISTREAM_ELEMENT_TYPE, typename BASIC_ISTREAM_TRAITS_TYPE>
17 class Rep_ : public InputStream::IRep<ELEMENT_TYPE> {
18 public:
19 /**
20 * Characters from a 16-bit wchar_t - UTF-16, where a Character is one element or two: so this converts, and
21 * cannot seek, its offsets counting Characters but the istream's counting wchar_ts (New () adds seekability)
22 */
23 static constexpr bool kTranscodes = sizeof (ELEMENT_TYPE) != sizeof (BASIC_ISTREAM_ELEMENT_TYPE);
24
25 private:
26 bool fOpen_{true};
27
28 public:
29 Rep_ (basic_istream<BASIC_ISTREAM_ELEMENT_TYPE, BASIC_ISTREAM_TRAITS_TYPE>& originalStream)
30 : Rep_{originalStream, kTranscodes ? eNotSeekable : eSeekable}
31 {
32 }
33 Rep_ (basic_istream<BASIC_ISTREAM_ELEMENT_TYPE, BASIC_ISTREAM_TRAITS_TYPE>& originalStream, SeekableFlag seekable)
34 : fOriginalStreamRef_{originalStream}
35 , fSeekable_{seekable}
36 {
37 Require (not kTranscodes or seekable == eNotSeekable);
38 }
39
40 protected:
41 virtual bool IsSeekable () const override
42 {
43 return fSeekable_ == eSeekable;
44 }
45 virtual void CloseRead () override
46 {
47 fOpen_ = false;
48 Ensure (not IsOpenRead ());
49 }
50 virtual bool IsOpenRead () const override
51 {
52 return fOpen_;
53 }
54 virtual optional<size_t> AvailableToRead () override
55 {
56 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
57 streamsize sz = fOriginalStreamRef_.rdbuf ()->in_avail ();
58 // http://en.cppreference.com/w/cpp/io/basic_streambuf/in_avail
59 if (sz == 0) {
60 return nullopt;
61 }
62 else if (sz == -1) {
63 return 0;
64 }
65 if constexpr (kTranscodes) {
66 // sz counts wchar_ts: two make one Character in a surrogate pair, and a last one may be half a pair still to come
67 return sz == 1 ? nullopt : optional<size_t>{static_cast<size_t> (sz) / 2};
68 }
69 return static_cast<size_t> (sz);
70 }
71 virtual optional<SeekOffsetType> RemainingLength () override
72 {
73 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
74 Require (IsOpenRead ());
75 if constexpr (kTranscodes) {
76 return nullopt; // Characters left cannot be known without reading them
77 }
79 return nullopt; // pretty easy, but @todo
80 }
81 virtual optional<span<ELEMENT_TYPE>> Read (span<ELEMENT_TYPE> intoBuffer, NoDataAvailableHandling blockFlag) override
82 {
83 Require (not intoBuffer.empty ());
84 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
85 Require (IsOpenRead ());
86 if (fOriginalStreamRef_.eof ()) {
87 return span<ELEMENT_TYPE>{};
88 }
89 if (blockFlag == NoDataAvailableHandling::eDontBlock and AvailableToRead () == nullopt) {
90 return nullopt;
91 }
92 if constexpr (kTranscodes) {
93 // intoBuffer.size () wchar_ts make at most that many Characters - and so do they with one more, completing a
94 // surrogate pair they split
95 Memory::StackBuffer<BASIC_ISTREAM_ELEMENT_TYPE> units{Memory::eUninitialized, intoBuffer.size () + 1};
96 size_t n = ReadElements_ (span{units}.subspan (0, intoBuffer.size ()));
97 if (n != 0 and Characters::Character::IsSurrogatePair_Hi (static_cast<char16_t> (units[n - 1]))) {
98 n += ReadElements_ (span{units}.subspan (n, 1));
99 }
100 Memory::StackBuffer<ELEMENT_TYPE> characters{
101 Memory::eUninitialized, Characters::UTFConvert::ComputeTargetBufferSize<ELEMENT_TYPE, BASIC_ISTREAM_ELEMENT_TYPE> (n)};
102 span<ELEMENT_TYPE> converted =
103 Characters::UTFConvert::kThe.ConvertSpan (span<const BASIC_ISTREAM_ELEMENT_TYPE>{units.data (), n}, span{characters});
104 Assert (converted.size () <= intoBuffer.size ());
105 std::copy (converted.begin (), converted.end (), intoBuffer.begin ());
106 fOffset_ += converted.size ();
107 return intoBuffer.subspan (0, converted.size ());
108 }
109 else {
110 return intoBuffer.subspan (
111 0, ReadElements_ (span{reinterpret_cast<BASIC_ISTREAM_ELEMENT_TYPE*> (intoBuffer.data ()), intoBuffer.size ()}));
112 }
113 }
114 virtual SeekOffsetType GetReadOffset () const override
115 {
116 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized_};
117 Require (IsOpenRead ());
118 if constexpr (kTranscodes) {
119 return fOffset_;
120 }
121 else {
122 // instead of tellg () - avoids issue with EOF where fail bit set???
123 return fOriginalStreamRef_.rdbuf ()->pubseekoff (0, ios_base::cur, ios_base::in);
124 }
125 }
126 virtual SeekOffsetType SeekRead (Whence whence, SignedSeekOffsetType offset) override
127 {
128 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
129 Require (IsOpenRead ());
130 Require (IsSeekable ());
131 fOriginalStreamRef_.clear (); // in case we hit eof (causing fail) - eof gets cleared by seeking, but not failbit - it appears...--LGP 2024-02-11
132 switch (whence) {
133 case eFromStart:
134 fOriginalStreamRef_.seekg (offset, ios::beg);
135 break;
136 case eFromCurrent:
137 fOriginalStreamRef_.seekg (offset, ios::cur);
138 break;
139 case eFromEnd:
140 fOriginalStreamRef_.seekg (offset, ios::end);
141 break;
142 }
143 // instead of tellg () - avoids issue with EOF where fail bit set???
144 return fOriginalStreamRef_.rdbuf ()->pubseekoff (0, ios_base::cur, ios_base::in);
145 }
146
147 private:
148 // Up to into.size () of the istream's elements, blocking: how many were read
149 size_t ReadElements_ (span<BASIC_ISTREAM_ELEMENT_TYPE> into)
150 {
151 fOriginalStreamRef_.clear (); // clear any failures before read - it will report if a failure found
152 fOriginalStreamRef_.read (into.data (), into.size ());
153 size_t n = static_cast<size_t> (fOriginalStreamRef_.gcount ()); // cast safe cuz amount asked to read was also size_t
154 Assert (n <= into.size ());
155 // apparently based on http://www.cplusplus.com/reference/iostream/istream/read/ EOF sets the EOF bit AND the fail bit
156 if (not fOriginalStreamRef_.eof () and fOriginalStreamRef_.fail ()) [[unlikely]] {
157 static const Execution::Exception<runtime_error> kException_{"Failed to read from istream"sv};
158 Execution::Throw (kException_);
159 }
160 return n;
161 }
162
163 private:
164 basic_istream<BASIC_ISTREAM_ELEMENT_TYPE, BASIC_ISTREAM_TRAITS_TYPE>& fOriginalStreamRef_;
165 SeekableFlag fSeekable_;
166 SeekOffsetType fOffset_{0}; // Characters read - kTranscodes only, where the istream's offsets count its elements
167 Stroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE Debug::AssertExternallySynchronizedChecker fThisAssertExternallySynchronized_;
168 };
169 }
170
171 /*
172 ********************************************************************************
173 ***************** InputStreamFromStdIStream<ELEMENT_TYPE>::New *****************
174 ********************************************************************************
175 */
176 template <typename ELEMENT_TYPE, typename BASIC_ISTREAM_ELEMENT_TYPE, typename BASIC_ISTREAM_TRAITS_TYPE>
177 inline auto New (basic_istream<BASIC_ISTREAM_ELEMENT_TYPE, BASIC_ISTREAM_TRAITS_TYPE>& originalStream) -> Ptr<ELEMENT_TYPE>
178 requires ((same_as<ELEMENT_TYPE, byte> and same_as<BASIC_ISTREAM_ELEMENT_TYPE, char>) or
179 (same_as<ELEMENT_TYPE, Characters::Character> and same_as<BASIC_ISTREAM_ELEMENT_TYPE, wchar_t>))
180 {
181 return New<ELEMENT_TYPE> (originalStream, eSeekable);
182 }
183 template <typename ELEMENT_TYPE, typename BASIC_ISTREAM_ELEMENT_TYPE, typename BASIC_ISTREAM_TRAITS_TYPE>
184 inline auto New (basic_istream<BASIC_ISTREAM_ELEMENT_TYPE, BASIC_ISTREAM_TRAITS_TYPE>& originalStream, SeekableFlag seekable) -> Ptr<ELEMENT_TYPE>
185 requires ((same_as<ELEMENT_TYPE, byte> and same_as<BASIC_ISTREAM_ELEMENT_TYPE, char>) or
186 (same_as<ELEMENT_TYPE, Characters::Character> and same_as<BASIC_ISTREAM_ELEMENT_TYPE, wchar_t>))
187 {
188 using Rep = Private_::Rep_<ELEMENT_TYPE, BASIC_ISTREAM_ELEMENT_TYPE, BASIC_ISTREAM_TRAITS_TYPE>;
189 if constexpr (Rep::kTranscodes) {
190 Ptr<ELEMENT_TYPE> unseekable{Memory::MakeSharedPtr<Rep> (originalStream, eNotSeekable)};
191 if (seekable == eSeekable) {
192 return BufferedInputStream::New<ELEMENT_TYPE> (unseekable, eSeekable);
193 }
194 return unseekable;
195 }
196 else {
197 return Ptr<ELEMENT_TYPE>{Memory::MakeSharedPtr<Rep> (originalStream, seekable)};
198 }
199 }
200 template <typename ELEMENT_TYPE, typename BASIC_ISTREAM_ELEMENT_TYPE, typename BASIC_ISTREAM_TRAITS_TYPE>
201 inline auto New (Execution::InternallySynchronized internallySynchronized,
202 basic_istream<BASIC_ISTREAM_ELEMENT_TYPE, BASIC_ISTREAM_TRAITS_TYPE>& originalStream) -> Ptr<ELEMENT_TYPE>
203 requires ((same_as<ELEMENT_TYPE, byte> and same_as<BASIC_ISTREAM_ELEMENT_TYPE, char>) or
204 (same_as<ELEMENT_TYPE, Characters::Character> and same_as<BASIC_ISTREAM_ELEMENT_TYPE, wchar_t>))
205 {
206 return New<ELEMENT_TYPE> (internallySynchronized, originalStream, eSeekable);
207 }
208 template <typename ELEMENT_TYPE, typename BASIC_ISTREAM_ELEMENT_TYPE, typename BASIC_ISTREAM_TRAITS_TYPE>
209 inline auto New (Execution::InternallySynchronized internallySynchronized,
210 basic_istream<BASIC_ISTREAM_ELEMENT_TYPE, BASIC_ISTREAM_TRAITS_TYPE>& originalStream, SeekableFlag seekable) -> Ptr<ELEMENT_TYPE>
211 requires ((same_as<ELEMENT_TYPE, byte> and same_as<BASIC_ISTREAM_ELEMENT_TYPE, char>) or
212 (same_as<ELEMENT_TYPE, Characters::Character> and same_as<BASIC_ISTREAM_ELEMENT_TYPE, wchar_t>))
213 {
214 using Rep = Private_::Rep_<ELEMENT_TYPE, BASIC_ISTREAM_ELEMENT_TYPE, BASIC_ISTREAM_TRAITS_TYPE>;
215 switch (internallySynchronized) {
216 case Execution::eInternallySynchronized:
217 if constexpr (Rep::kTranscodes) {
218 if (seekable == eSeekable) {
219 return BufferedInputStream::New<ELEMENT_TYPE> (Execution::eInternallySynchronized,
220 New<ELEMENT_TYPE> (originalStream, eNotSeekable), eSeekable);
221 }
222 }
223 return InternallySynchronizedInputStream::New<Rep> ({}, originalStream, seekable);
224 case Execution::eNotKnownInternallySynchronized:
225 return New<ELEMENT_TYPE> (originalStream, seekable);
226 default:
228 return nullptr;
229 }
230 }
231
232}
#define AssertNotImplemented()
Definition Assertions.h:404
#define RequireNotReached()
Definition Assertions.h:388
#define Stroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:481
static const UTFConvert kThe
Nearly always use this default UTFConvert.
Definition UTFConvert.h:374
nonvirtual span< TRG_T > ConvertSpan(span< const SRC_T > source, span< TRG_T > target) const
Convert between UTF-N encoded (including the special case of ASCII, and Latin1) character spans (e....
unique_lock< AssertExternallySynchronizedChecker > WriteContext
Instantiate AssertExternallySynchronizedChecker::WriteContext to designate an area of code where prot...
shared_lock< const AssertExternallySynchronizedChecker > ReadContext
Instantiate AssertExternallySynchronizedChecker::ReadContext to designate an area of code where prote...
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
A Streams::Ptr<ELEMENT_TYPE> is a smart-pointer to a stream of elements of type T.
Definition Stream.h:170
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
Definition Throw.inl:54