Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
BinaryToText.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. 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"
15
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 RuntimeErrorException{"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 AssertExternallySynchronizedMutex::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 AssertExternallySynchronizedMutex::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 AssertExternallySynchronizedMutex::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};
270 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fThisAssertExternallySynchronized_;
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 AssertExternallySynchronizedMutex::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 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wclass-memaccess\"");
373 (void)::memcpy (intoBuffer.data (), std::begin (buf), result->size () * sizeof (Character));
374 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wclass-memaccess\"");
375 _fOffset = origOffset + result->size ();
376 }
377 return result;
378 }
379 }
380 virtual SeekOffsetType SeekRead (Whence whence, SignedSeekOffsetType offset) override
381 {
382 static const auto kException_ = range_error{"seek"};
383 AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
384 Require (IsOpenRead ());
385 switch (whence) {
386 case eFromStart: {
387 if (offset < 0) [[unlikely]] {
388 Throw (kException_);
389 }
390 SeekTo_ (static_cast<SeekOffsetType> (offset));
391 } break;
392 case eFromCurrent: {
393 Streams::SeekOffsetType curOffset = _fOffset;
394 Streams::SignedSeekOffsetType newOffset = curOffset + offset;
395 if (newOffset < 0) [[unlikely]] {
396 Throw (kException_);
397 }
398 SeekOffsetType uNewOffset = static_cast<SeekOffsetType> (newOffset);
399 SeekTo_ (static_cast<size_t> (uNewOffset));
400 } break;
401 case eFromEnd: {
402 // @todo DECIDE IF SeekRead needs blockFlag???
403 // and fix/simplify code here!!! for blockflag
404 Character c;
405 for (auto o = Read (span{&c, 1}, NoDataAvailableHandling::eBlockIfNoDataAvailable); o && o->size () == 1;
406 o = Read (span{&c, 1}, NoDataAvailableHandling::eBlockIfNoDataAvailable)) {
407 break; // read til EOF
408 }
409 SeekTo_ (_fOffset + offset);
410 } break;
411 }
412 return _fOffset;
413 }
414
415 private:
416 void SeekFowardTo_ (SeekOffsetType offset)
417 {
418 static const auto kException_ = range_error{"seek"};
419 // easy - keep reading
420 while (_fOffset < offset) {
421 Character c;
422 // @todo Seek may require NoDataAvailableHandling flag!!!
423 // @todo fix data missing logic... - need datanotavailhandling flag arg
424 if (auto o = Read (span{&c, 1}, NoDataAvailableHandling::eBlockIfNoDataAvailable); o && o->size () == 0) [[unlikely]] {
425 Throw (kException_);
426 }
427 }
428 Ensure (_fOffset == offset);
429 }
430 void SeekBackwardTo_ (SeekOffsetType offset)
431 {
432 _fOffset = offset;
433 // no need to adjust seekpos of base FromBinaryStreamBaseRep_ readpos, because that is always left at end of cache
434 }
435 void SeekTo_ (SeekOffsetType offset)
436 {
437 if (offset > _fOffset) {
438 SeekFowardTo_ (offset);
439 }
440 else if (offset < _fOffset) {
441 SeekBackwardTo_ (offset);
442 }
443 Ensure (_fOffset == offset);
444 }
445
446 private:
447 bool fReadAheadAllowed_{false};
448 InlineBuffer<Character> fCache_; // Cache uses wchar_t instead of Character so can use resize_uninitialized () - requires is_trivially_constructible
449 // @todo for larger streams, a different data structure than InlineBuffer would be appropriate - E.G. ChunkedArray! probably best.
450 };
451}
452
453/*
454 ********************************************************************************
455 ********************** Streams::BinaryToText::Reader::New ****************************
456 ********************************************************************************
457 */
458namespace {
459 auto New_ (const InputStream::Ptr<byte>& src, const Characters::CodeCvt<>& codeConverter, SeekableFlag seekable, Reader::ReadAhead readAhead)
461 {
463 Ptr p = (seekable == SeekableFlag::eSeekable) ? Ptr{make_shared<CachingSeekableBinaryStreamRep_> (src, codeConverter, readAhead)}
464 : Ptr{make_shared<UnseekableBinaryStreamRep_> (src, codeConverter)};
465 Ensure (p.IsSeekable () == (seekable == SeekableFlag::eSeekable));
466 return p;
467 }
468}
469
470auto BinaryToText::Reader::New (const InputStream::Ptr<byte>& src, const optional<AutomaticCodeCvtFlags> codeCvtFlags,
471 optional<SeekableFlag> seekable, ReadAhead readAhead) -> InputStream::Ptr<Character>
472{
473 if (seekable == nullopt) {
474 seekable = src.IsSeekable () ? SeekableFlag::eSeekable : SeekableFlag::eNotSeekable;
475 }
476 using namespace Characters;
477 CodeCvt<> codeConverter = [&] () {
478 if (src.IsSeekable ()) {
479 auto savedSeek = src.GetOffset ();
480 // read possible BOM, and then chose CodeCvt according to codeCvtFlags
481 byte bomData[Characters::kMaxBOMSize];
482 optional<tuple<Characters::UnicodeExternalEncodings, size_t>> bomInfo;
483 if (src.ReadAll (span{bomData}).size () == Memory::NEltsOf (bomData) and
484 (bomInfo = Characters::ReadByteOrderMark (span{bomData})).has_value ()) {
485 // adjust amount read from input stream if we over-read
486 src.Seek (savedSeek + get<size_t> (*bomInfo)); // adjust amount read from input stream if we read anything (could be a no-op seek)
487 return CodeCvt<>{get<UnicodeExternalEncodings> (*bomInfo)};
488 }
489 else {
490 src.Seek (savedSeek); // adjust amount read from input stream if we read anything
491 }
492 }
493 // if not seekable, or no BOM, use codeCvtFlags to figure what todo for codes
494 switch (codeCvtFlags.value_or (AutomaticCodeCvtFlags::eDEFAULT)) {
495 case AutomaticCodeCvtFlags::eReadBOMAndIfNotPresentUseUTF8:
496 return CodeCvt<>{UnicodeExternalEncodings::eUTF8};
497 case AutomaticCodeCvtFlags::eReadBOMAndIfNotPresentUseCurrentLocale:
498 return CodeCvt<>{locale{}};
499 default:
501 return CodeCvt<>{};
502 }
503 }();
504 return New_ (src, codeConverter, *seekable, readAhead);
505}
506
507auto BinaryToText::Reader::New (const InputStream::Ptr<byte>& src, const Characters::CodeCvt<>& codeConverter,
508 optional<SeekableFlag> seekable, ReadAhead readAhead) -> InputStream::Ptr<Character>
509{
510 if (seekable == nullopt) {
511 seekable = src.IsSeekable () ? SeekableFlag::eSeekable : SeekableFlag::eNotSeekable;
512 }
513 return New_ (src, codeConverter, *seekable, readAhead);
514}
515
517{
518 InputStream::Ptr<Character> p = IterableToInputStream::New<Character> (src);
519 Ensure (p.IsSeekable ());
520 return p;
521}
522
523/*
524 ********************************************************************************
525 ************************* Streams::BinaryToText::Convert ***********************
526 ********************************************************************************
527 */
528String BinaryToText::Convert (const Memory::BLOB& src, optional<AutomaticCodeCvtFlags> codeCvtFlags)
529{
530 return BinaryToText::Reader::New (src, codeCvtFlags).ReadAll ();
531}
532String BinaryToText::Convert (const Memory::BLOB& src, const Characters::CodeCvt<>& codeConverter)
533{
534 return BinaryToText::Reader::New (src, codeConverter).ReadAll ();
535}
#define AssertNotReached()
Definition Assertions.h:355
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...
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
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:237
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
Ptr New(const InputStream::Ptr< byte > &src, optional< AutomaticCodeCvtFlags > codeCvtFlags={}, optional< SeekableFlag > seekable={}, ReadAhead readAhead=eReadAheadAllowed)
Create an InputStream::Ptr<Character> from the arguments (usually binary source) - which can be used ...