Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
BinaryToText.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
8#include "Stroika/Foundation/Containers/Support/ReserveTweaks.h"
10#include "Stroika/Foundation/Execution/Common.h"
11#include "Stroika/Foundation/Execution/OperationNotSupportedException.h"
17
18#include "BinaryToText.h"
19
20using std::byte;
21
22using namespace Stroika::Foundation;
24using namespace Stroika::Foundation::Execution;
25using namespace Stroika::Foundation::Streams;
26using namespace Stroika::Foundation::Streams::BinaryToText;
27
31
32namespace {
33 const auto kReadPartialCharacterAtEndOfBinaryStreamException_ =
34 Execution::Exception<runtime_error>{"BinaryToText::Reader read partial character at end of binary input stream"sv};
35
36 class FromBinaryStreamBaseRep_ : public InputStream::IRep<Character> {
37 public:
38 FromBinaryStreamBaseRep_ (const InputStream::Ptr<byte>& src, const Characters::CodeCvt<>& charConverter)
39 : _fSource{src}
40 , _fCharConverter{charConverter}
41 {
42 }
43
44 protected:
45 /*
46 * In Read (Character* intoStart, Character* intoEnd) override,
47 * if we have converted at least one character, but have leftover bytes read,
48 * we must somehow backup binary read amount for next time - save 'multibyte' state
49 *
50 * Two strategies:
51 * o SEEK source back: quick/easily buy maybe impossible (if source not seekable)
52 * o OR track 'extra stuff to prepend to next read' - and use that on next read.
53 *
54 * We COULD use the 'seek' approach by creating a wrapper class that introduced seekability so
55 * we could assume it. But that would be costly - because it over-generalizes what we need. We need just very
56 * limited seekability, and that is best captured by the second approach. And little point in two implementations
57 * so just go with the second - since it will always work, and work fairly well (downside is a bit of extra code complexity here)
58 */
59 struct _ReadAheadCache {
60 SeekOffsetType fFrom; // save offset cuz cache only valid on reads from this offset (if subclass allowed seek)
61 InlineBuffer<byte, 8> fData; // @todo UPDATE DOCS - AND LOGIC - CAN BE MORE THAN ONE CHARACTERS WORTH DUE TO READ NON BLOCKING WITH nullptr args!!!
62 };
63 optional<_ReadAheadCache> _fReadAheadCache;
64
65 public:
66 virtual bool IsSeekable () const override
67 {
68 return false; // but subclasses may implement seekability
69 }
70 virtual void CloseRead () override
71 {
72 if (_fSource != nullptr) {
73 _fSource.Close ();
74 }
75 Ensure (not IsOpenRead ());
76 Assert (_fSource == nullptr);
77 }
78 virtual bool IsOpenRead () const override
79 {
80 return _fSource != nullptr;
81 }
82 virtual optional<size_t> AvailableToRead () override
83 {
84 Require (IsOpenRead ());
85 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
86 // Tricky todo. Must grab ENUF bytes (non-blocking) to assure we can create at least one character.
87 // Note we cannot SEEK the _fSource stream, which is what makes it hard...
88 // Just means read ahead a bit, and store whatever we needed into _fReadAheadCache
89 byte inByteBuf[10]; // just enuf to hold a single encoded character
90 size_t currentByteGoal = 1;
91 again:
92 if (auto o = PreReadUpstreamInto_ (span{inByteBuf}, currentByteGoal, eDontBlock)) {
93 span<const byte> binarySrcSpan{*o};
94 Character ignoredChar;
95 span<Character> targetBuf{&ignoredChar, 1};
96 Assert (_fCharConverter.ComputeTargetCharacterBufferSize (binarySrcSpan.size ()) <= targetBuf.size ()); // just because _fCharConverter will require this
97 span<Character> convertedCharacters = _fCharConverter.Bytes2Characters (&binarySrcSpan, targetBuf);
98 if (convertedCharacters.empty ()) {
99 // We have zero convertedCharacters, so apparently not enough bytes read. Read one more, and try again.
100 ++currentByteGoal;
101 Prepend2ReadAheadCache_ (binarySrcSpan);
102 goto again;
103 }
104 else {
105 Assert (_fReadAheadCache == nullopt); // may not always be true, but if not true we need todo more work here...
106 Assert (not binarySrcSpan.empty () and not convertedCharacters.empty ());
107 Assert (convertedCharacters.size () <= targetBuf.size ());
108 Prepend2ReadAheadCache_ (binarySrcSpan);
109 return 1;
110 }
111 }
112 return nullopt; // insufficient binary data known available to form a single character
113 }
114 virtual optional<SeekOffsetType> RemainingLength () override
115 {
116 Require (IsOpenRead ());
117 return nullopt; // possible, but not easy...
118 }
119 virtual optional<span<Character>> Read (span<Character> intoBuffer, NoDataAvailableHandling blockFlag) override
120 {
121 Require (not intoBuffer.empty ());
122 Require (IsOpenRead ());
123 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
124 /*
125 * Try to minimize # of calls to underlying fSource binary stream per call this this Read () - efficiency.
126 *
127 * Only need to read one character, and once we have that much, don't block on more from upstream binary stream.
128 *
129 * But always just return at least 1 char if we can, so if partial, and no full chars read, keep reading.
130 *
131 * Since number of Characters filled always <= number of bytes read, we can read up to that # of bytes from upstream binary stream.
132 */
133 size_t readAtMostCharacters = intoBuffer.size (); // must read at least one character, and no more than (intoEnd - intoStart)
134 StackBuffer<byte, 8 * 1024> inByteBuf{Memory::eUninitialized, readAtMostCharacters};
135 size_t currentByteGoal = 1;
136 again:
137 if (auto o = PreReadUpstreamInto_ (span{inByteBuf}, currentByteGoal, blockFlag)) {
138 span<const byte> binarySrcSpan{*o};
139 span<Character> targetBuf{intoBuffer};
140 Assert (_fCharConverter.ComputeTargetCharacterBufferSize (inByteBuf.size ()) <= targetBuf.size ()); // just because _fCharConverter will require this
141 span<Character> convertedCharacters = _fCharConverter.Bytes2Characters (&binarySrcSpan, targetBuf);
142 if (binarySrcSpan.empty ()) {
143 // Great case! Then we got data - already copied into intoStart (part of target span) - so just return the characters read
144 Assert (convertedCharacters.size () <= targetBuf.size ());
145 _fOffset += convertedCharacters.size ();
146 return intoBuffer.subspan (0, convertedCharacters.size ());
147 }
148 else if (convertedCharacters.empty ()) {
149 // We have zero convertedCharacters, so apparently not enough bytes read. Read one more, and try again.
150 ++currentByteGoal;
151 Prepend2ReadAheadCache_ (binarySrcSpan); // PreReadUpstreamInto_ removed from readahead cache so put back to try a second byte
152 goto again;
153 }
154 else {
155 Assert (not binarySrcSpan.empty () and not convertedCharacters.empty ());
156 Assert (convertedCharacters.size () <= targetBuf.size ());
157 _fOffset += convertedCharacters.size (); // complete the read
158 // Save the extra bytes for next time (with the offset where those bytes come from)
159 Prepend2ReadAheadCache_ (binarySrcSpan);
160 return intoBuffer.subspan (0, convertedCharacters.size ());
161 }
162 }
163 else {
164 // if we failed to meet our byte goal, any already read bytes have been squirreled away and we just return saying not enuf data
165 Assert (blockFlag == eDontBlock);
166 return nullopt;
167 }
169 return nullopt;
170 }
171 template <size_t EXTENT>
172 span<byte> ReadFromAndRemoveFromReadAheadCache_ (span<byte, EXTENT> intoSpan)
173 {
174 size_t bytes2Copy = intoSpan.size ();
175 Require (_fReadAheadCache and _fReadAheadCache->fData.size () >= bytes2Copy);
176 Require (this->_fOffset == _fReadAheadCache->fFrom);
177 Memory::CopyBytes (span{_fReadAheadCache->fData}, intoSpan);
178 if (bytes2Copy == _fReadAheadCache->fData.size ()) {
179 _fReadAheadCache.reset ();
180 }
181 else {
182 _fReadAheadCache.emplace (_ReadAheadCache{.fFrom = _fOffset + bytes2Copy, .fData = span{_fReadAheadCache->fData}.subspan (bytes2Copy)});
183 }
184 return intoSpan;
185 }
186 template <size_t EXTENT>
187 void Prepend2ReadAheadCache_ (span<const byte, EXTENT> data2Append)
188 {
189 if (_fReadAheadCache) {
190 Require (_fOffset == _fReadAheadCache->fFrom - data2Append.size ());
191 StackBuffer<byte> combined{data2Append};
192 combined.push_back (span{_fReadAheadCache->fData});
193 _fReadAheadCache.emplace (_ReadAheadCache{.fFrom = _fOffset, .fData = combined});
194 }
195 else {
196 _fReadAheadCache.emplace (_ReadAheadCache{.fFrom = _fOffset, .fData = data2Append});
197 }
198 }
199 // use (and empty) _fReadAheadCache, and read goalSizeAtLeast bytes (if possible) into intoBuf from upstream source.
200 // Return nullopt if fails due to EWouldBlock (in which case leave _fReadAheadCache unchanged)
201 // intoBuf assumed had no real data on entry, and filled in with good data on exit (except nullopt case).
202 // return span says how much good data read.
203 // If goal is met:
204 // o Return value is valid span of at least goalSizeAtLeast bytes
205 // o Any excess read data is stored in _fReadAheadCache (say because intoBuf was too small)
206 // If goal not met either:
207 // o return value is nullopt (and _fReadAheadCache saves any actually read bytes < goalsize);
208 // o return value is shorter buffer (iff EOF) - and that is known to be the end.
209 nonvirtual optional<span<const byte>> PreReadUpstreamInto_ (span<byte> intoBuf, size_t goalSizeAtLeast, NoDataAvailableHandling blockFlag)
210 {
211 Require (goalSizeAtLeast >= 1);
212 Require (goalSizeAtLeast <= intoBuf.size ());
213 span<const byte> result; // always some subset of intoBuf
214 if (_fReadAheadCache and _fReadAheadCache->fFrom == this->_fOffset) {
215 // If _fReadAheadCache present, may contain enuf bytes for a full character, so don't do another
216 // blocking read as it MAY not be necessary, and if it is, will be caught in existing logic for 'again' not enuf data
217 //
218 // NOTE - for non-seekable case, this _fReadAheadCache->fFrom == this->_fOffset will always work/match. Only way to fail
219 // is if we got seeked (subclass). But then subclass better have adjusted seek offset of _fSource magically too
220 Assert (intoBuf.size () >= _fReadAheadCache->fData.size ());
221 size_t amtToCopy = min (_fReadAheadCache->fData.size (), intoBuf.size ());
222 result = ReadFromAndRemoveFromReadAheadCache_ (intoBuf.subspan (0, amtToCopy));
223 }
224 while (result.size () < goalSizeAtLeast) {
225 switch (blockFlag) {
227 auto r = _fSource.ReadBlocking (intoBuf.subspan (result.size ()));
228 if (r.size () == 0) {
229 // we are done - got EOF, and so just return as much as we have
230 return result;
231 }
232 else {
233 // add the new data to result (its already been read into the right place of intoBuf, just adjust size of result)
234 result = intoBuf.subspan (0, result.size () + r.size ());
235 }
236 } break;
237 case eDontBlock: {
238 if (auto o = _fSource.ReadNonBlocking (intoBuf.subspan (result.size ()))) {
239 if (o->size () == 0) {
240 // we are done - got EOF, and so just return as much as we have
241 return result;
242 }
243 else {
244 // add the new data to result (its already been read into the right place of intoBuf, just adjust size of result)
245 result = intoBuf.subspan (0, result.size () + o->size ());
246 }
247 }
248 else {
249 // if we have no raw bytes to work with, cannot produce characters (yet)
250 // so must PUT BACK accumulated text for next time...
251 Prepend2ReadAheadCache_ (result);
252 return nullopt;
253 }
254 } break;
255 }
256 }
257 return result;
258 }
259 virtual SeekOffsetType GetReadOffset () const override
260 {
261 AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized_};
262 Require (IsOpenRead ());
263 return _fOffset;
264 }
265
266 protected:
267 InputStream::Ptr<byte> _fSource;
268 const Characters::CodeCvt<Character> _fCharConverter;
269 SeekOffsetType _fOffset{0};
271 };
272
273 class UnseekableBinaryStreamRep_ final : public FromBinaryStreamBaseRep_ {
274 using inherited = FromBinaryStreamBaseRep_;
275
276 public:
277 UnseekableBinaryStreamRep_ (const InputStream::Ptr<byte>& src, const Characters::CodeCvt<char32_t>& charConverter)
278 : inherited{src, charConverter}
279 {
280 }
281 };
282
283 /*
284 * Note this CAN be used with either a seekable or unseekable byte stream.
285 * But this always produces a SEEKABLE TextStream, so can be used to produce
286 * a seekable TextStream from a non-seekable binary stream.
287 *
288 * But note - it does this at the cost of KEEPING a copy of the entire stream in memory.
289 */
290 class CachingSeekableBinaryStreamRep_ final : public FromBinaryStreamBaseRep_ {
291 using inherited = FromBinaryStreamBaseRep_;
292
293 public:
294 CachingSeekableBinaryStreamRep_ (const InputStream::Ptr<byte>& src, const Characters::CodeCvt<char32_t>& charConverter, Reader::ReadAhead readAhead)
295 : FromBinaryStreamBaseRep_{src, charConverter}
296 , fReadAheadAllowed_{readAhead == Reader::ReadAhead::eReadAheadAllowed}
297 {
298 }
299
300 protected:
301 virtual bool IsSeekable () const override
302 {
303 return true;
304 }
305 virtual optional<span<Character>> Read (span<Character> intoBuffer, NoDataAvailableHandling blockFlag) override
306 {
307 Require (not intoBuffer.empty ());
308 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
309 Require (IsOpenRead ());
310
311 // if already cached, return from cache. Note - even if only one element is in the Cache, thats enough to return
312 // and not say 'eof'
313 if (_fOffset < fCache_.size ()) {
314 // return data from cache
315 size_t nToRead = intoBuffer.size ();
316 size_t nInBufAvail = fCache_.size () - static_cast<size_t> (_fOffset);
317 nToRead = min (nToRead, nInBufAvail);
318 Assert (nToRead > 0);
319 for (size_t i = 0; i < nToRead; ++i) {
320 intoBuffer[i] = fCache_[i + static_cast<size_t> (_fOffset)];
321 }
322 _fOffset += nToRead;
323 return intoBuffer.subspan (0, nToRead);
324 }
325
326 // if not already cached, add to cache, and then return the data
327 SeekOffsetType origOffset = _fOffset;
328 auto pushIntoCacheBuf = [origOffset, this] (Character* bufStart, Character* bufEnd) {
329 size_t n = bufEnd - bufStart;
330 size_t newCacheSize = static_cast<size_t> (origOffset + n);
331 Assert (fCache_.size () == static_cast<size_t> (origOffset));
332 Assert (newCacheSize > fCache_.size ());
333 Containers::Support::ReserveTweaks::Reserve4AddN (fCache_, n);
334 fCache_.resize_uninitialized (newCacheSize);
335 for (size_t i = 0; i < n; ++i) {
336 fCache_[i + static_cast<size_t> (origOffset)] = bufStart[i];
337 }
338 };
339 // if not a cache hit, use inherited Read (), and fill the cache.
340 // If the calling read big enough, re-use that buffer.
341 constexpr size_t kMinCachedReadSize_{512};
342 if (intoBuffer.size () >= kMinCachedReadSize_ or not fReadAheadAllowed_) {
343 auto result = inherited::Read (intoBuffer, blockFlag);
344 if (result == nullopt) {
345 Throw (EWouldBlock::kThe);
346 }
347 if (result->size () != 0) {
348 if (origOffset + result->size () > numeric_limits<size_t>::max ()) [[unlikely]] {
349 // size_t can be less bits than SeekOffsetType, in which case we cannot cahce all in RAM
350 Throw (range_error{"seek past max size for size_t"});
351 }
352 pushIntoCacheBuf (intoBuffer.data (), intoBuffer.data () + result->size ());
353 }
354 return result;
355 }
356 else {
357 // if argument buffer not big enough, read into a temporary buffer
358 constexpr size_t kUseCacheSize_ = 8 * kMinCachedReadSize_;
359 Character buf[kUseCacheSize_]; // use wchar_t and cast to Character* so we get this array uninitialized
360 auto result =
361 inherited::Read (span{reinterpret_cast<Character*> (std::begin (buf)), reinterpret_cast<Character*> (std::end (buf))}, blockFlag);
362 if (result == nullopt) {
363 Throw (EWouldBlock::kThe);
364 }
365 if (result->size () != 0) {
366 if (origOffset + result->size () > numeric_limits<size_t>::max ()) [[unlikely]] {
367 // size_t can be less bits than SeekOffsetType, in which case we cannot cache all in RAM
368 Throw (range_error{"seek past max size for size_t"});
369 }
370 pushIntoCacheBuf (std::begin (buf), std::begin (buf) + result->size ());
371 result = result->subspan (0, min (intoBuffer.size (), result->size ()));
372 (void)::memcpy (intoBuffer.data (), std::begin (buf), result->size () * sizeof (Character));
373 _fOffset = origOffset + result->size ();
374 }
375 return result;
376 }
377 }
378 virtual SeekOffsetType SeekRead (Whence whence, SignedSeekOffsetType offset) override
379 {
380 static const auto kException_ = range_error{"seek"};
381 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized_};
382 Require (IsOpenRead ());
383 switch (whence) {
384 case eFromStart: {
385 if (offset < 0) [[unlikely]] {
386 Throw (kException_);
387 }
388 SeekTo_ (static_cast<SeekOffsetType> (offset));
389 } break;
390 case eFromCurrent: {
391 Streams::SeekOffsetType curOffset = _fOffset;
392 Streams::SignedSeekOffsetType newOffset = curOffset + offset;
393 if (newOffset < 0) [[unlikely]] {
394 Throw (kException_);
395 }
396 SeekOffsetType uNewOffset = static_cast<SeekOffsetType> (newOffset);
397 SeekTo_ (static_cast<size_t> (uNewOffset));
398 } break;
399 case eFromEnd: {
400 // @todo DECIDE IF SeekRead needs blockFlag???
401 // and fix/simplify code here!!! for blockflag
402 Character c;
403 for (auto o = Read (span{&c, 1}, NoDataAvailableHandling::eBlockIfNoDataAvailable); o && o->size () == 1;
404 o = Read (span{&c, 1}, NoDataAvailableHandling::eBlockIfNoDataAvailable)) {
405 break; // read til EOF
406 }
407 SeekTo_ (_fOffset + offset);
408 } break;
409 }
410 return _fOffset;
411 }
412
413 private:
414 void SeekFowardTo_ (SeekOffsetType offset)
415 {
416 static const auto kException_ = range_error{"seek"};
417 // easy - keep reading
418 while (_fOffset < offset) {
419 Character c;
420 // @todo Seek may require NoDataAvailableHandling flag!!!
421 // @todo fix data missing logic... - need datanotavailhandling flag arg
422 if (auto o = Read (span{&c, 1}, NoDataAvailableHandling::eBlockIfNoDataAvailable); o && o->size () == 0) [[unlikely]] {
423 Throw (kException_);
424 }
425 }
426 Ensure (_fOffset == offset);
427 }
428 void SeekBackwardTo_ (SeekOffsetType offset)
429 {
430 _fOffset = offset;
431 // no need to adjust seekpos of base FromBinaryStreamBaseRep_ readpos, because that is always left at end of cache
432 }
433 void SeekTo_ (SeekOffsetType offset)
434 {
435 if (offset > _fOffset) {
436 SeekFowardTo_ (offset);
437 }
438 else if (offset < _fOffset) {
439 SeekBackwardTo_ (offset);
440 }
441 Ensure (_fOffset == offset);
442 }
443
444 private:
445 bool fReadAheadAllowed_{false};
446 InlineBuffer<Character> fCache_; // Cache uses wchar_t instead of Character so can use resize_uninitialized () - requires is_trivially_constructible
447 // @todo for larger streams, a different data structure than InlineBuffer would be appropriate - E.G. ChunkedArray! probably best.
448 };
449}
450
451/*
452 ********************************************************************************
453 ********************** Streams::BinaryToText::Reader::New ****************************
454 ********************************************************************************
455 */
456namespace {
457 auto New_ (const InputStream::Ptr<byte>& src, const Characters::CodeCvt<>& codeConverter, SeekableFlag seekable, Reader::ReadAhead readAhead)
459 {
461 Ptr p = (seekable == SeekableFlag::eSeekable) ? Ptr{Memory::MakeSharedPtr<CachingSeekableBinaryStreamRep_> (src, codeConverter, readAhead)}
462 : Ptr{Memory::MakeSharedPtr<UnseekableBinaryStreamRep_> (src, codeConverter)};
463 Ensure (p.IsSeekable () == (seekable == SeekableFlag::eSeekable));
464 return p;
465 }
466}
467
468namespace {
469 // as src's byte order mark says, where src can seek and has one - leaving src past it; else ifNone
470 Characters::CodeCvt<> CodeCvtFromByteOrderMarkOr_ (const InputStream::Ptr<byte>& src, const Characters::CodeCvt<>& ifNone)
471 {
472 using namespace Characters;
473 if (src.IsSeekable ()) {
474 const auto savedSeek = src.GetOffset ();
475 byte bomData[kMaxBOMSize];
476 // whatever is there, up to the longest byte order mark: a text shorter than that may still begin with a shorter one
477 if (optional<tuple<UnicodeExternalEncodings, size_t>> bomInfo = ReadByteOrderMark (src.ReadAll (span{bomData}))) {
478 src.Seek (savedSeek + get<size_t> (*bomInfo));
479 return CodeCvt<>{get<UnicodeExternalEncodings> (*bomInfo)};
480 }
481 src.Seek (savedSeek);
482 }
483 return ifNone;
484 }
485}
486
487auto BinaryToText::Reader::New (const InputStream::Ptr<byte>& src, optional<SeekableFlag> seekable, ReadAhead readAhead) -> InputStream::Ptr<Character>
488{
489 return New (src, ByteOrderMarkOr{Characters::CodeCvt<>{Characters::UnicodeExternalEncodings::eUTF8}}, seekable, readAhead);
490}
491
492auto BinaryToText::Reader::New (const InputStream::Ptr<byte>& src, const ByteOrderMarkOr& ifNoByteOrderMark,
493 optional<SeekableFlag> seekable, ReadAhead readAhead) -> InputStream::Ptr<Character>
494{
495 return New (src, CodeCvtFromByteOrderMarkOr_ (src, ifNoByteOrderMark.fIfNone), seekable, readAhead);
496}
497
499 optional<SeekableFlag> seekable, ReadAhead readAhead) -> InputStream::Ptr<Character>
500{
501 if (seekable == nullopt) {
502 seekable = src.IsSeekable () ? SeekableFlag::eSeekable : SeekableFlag::eNotSeekable;
503 }
504 return New_ (src, codeConverter, *seekable, readAhead);
505}
506
508DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
509DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
510auto BinaryToText::Reader::New (const InputStream::Ptr<byte>& src, const optional<AutomaticCodeCvtFlags> codeCvtFlags,
511 optional<SeekableFlag> seekable, ReadAhead readAhead) -> InputStream::Ptr<Character>
512{
513 using namespace Characters;
514 return New (src,
515 ByteOrderMarkOr{codeCvtFlags == AutomaticCodeCvtFlags::eReadBOMAndIfNotPresentUseCurrentLocale
516 ? CodeCvt<>{locale{}}
517 : CodeCvt<>{UnicodeExternalEncodings::eUTF8}},
518 seekable, readAhead);
519}
520DISABLE_COMPILER_MSC_WARNING_END (4996)
521DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
522DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
523
524auto BinaryToText::Reader::New (const Traversal::Iterable<Character>& src) -> InputStream::Ptr<Character>
525{
526 InputStream::Ptr<Character> p = IterableToInputStream::New<Character> (src);
527 Ensure (p.IsSeekable ());
528 return p;
529}
530
531/*
532 ********************************************************************************
533 ************************* Streams::BinaryToText::Convert ***********************
534 ********************************************************************************
535 */
536String BinaryToText::Convert (const Memory::BLOB& src)
537{
538 return BinaryToText::Reader::New (src).ReadAll ();
539}
540String BinaryToText::Convert (const Memory::BLOB& src, const ByteOrderMarkOr& ifNoByteOrderMark)
541{
542 return BinaryToText::Reader::New (src, ifNoByteOrderMark).ReadAll ();
543}
544String BinaryToText::Convert (const Memory::BLOB& src, const Characters::CodeCvt<>& codeConverter)
545{
546 return BinaryToText::Reader::New (src, codeConverter).ReadAll ();
547}
549DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
550DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
551String BinaryToText::Convert (const Memory::BLOB& src, optional<AutomaticCodeCvtFlags> codeCvtFlags)
552{
553 return BinaryToText::Reader::New (src, codeCvtFlags).ReadAll ();
554}
555DISABLE_COMPILER_MSC_WARNING_END (4996)
556DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
557DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
#define AssertNotReached()
Definition Assertions.h:358
#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
NoDataAvailableHandling
If eDontBlock passed to most Stream APIs, then when the code would do a blocking read,...
Definition Stream.h:90
CodeCvt unifies byte <-> unicode conversions, vaguely inspired by (and wraps) std::codecvt,...
Definition CodeCvt.h:118
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
Definition Exceptions.h:219
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
virtual bool IsSeekable() const =0
virtual SeekOffsetType GetReadOffset() const =0
virtual optional< span< ElementType > > Read(span< ElementType > intoBuffer, NoDataAvailableHandling blockFlag)=0
virtual optional< SeekOffsetType > RemainingLength()
returns nullopt if not known (typical, and the default) - but sometimes it is known,...
virtual optional< size_t > AvailableToRead()
returns nullopt if nothing known available, zero if known EOF, and any other number of elements (typi...
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
nonvirtual String ReadAll(size_t upTo=numeric_limits< size_t >::max()) const
nonvirtual SeekOffsetType Seek(SeekOffsetType offset) const
nonvirtual SeekOffsetType GetOffset() const
A Streams::Ptr<ELEMENT_TYPE> is a smart-pointer to a stream of elements of type T.
Definition Stream.h:170
nonvirtual bool IsSeekable() const
Returns true iff this object was constructed with a seekable input stream rep.
Definition Stream.inl:44
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:234
constexpr optional< tuple< UnicodeExternalEncodings, size_t > > ReadByteOrderMark(span< const byte > d) noexcept
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
Definition Throw.inl:54
Ptr New(const InputStream::Ptr< byte > &src, optional< SeekableFlag > seekable={}, ReadAhead readAhead=eReadAheadAllowed)
Create an InputStream::Ptr<Character> from the arguments (usually binary source) - which can be used ...
Decode by the text's byte order mark where it has one, and otherwise with fIfNone - as against a Code...