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