MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
Readers

Methods and coroutines related to readers abstractions. More...

Namespaces

namespace  mtcore::io::readers
 Additional algorithms that can be done on a reader without having to extend the reader type We're encouraging this method of extending readers to avoid readers from becoming incredibly large, and to create a standardized extension mechanism to allow 3rd party extensions of readers.
 

Classes

struct  mtcore::io::Reader< Impl >
 A reader that reads data from some sort of stream or buffer Note: the data elements read should be trivially constructible, trivially destructible, and trivially copyable. More...
 

Functions

template<typename T>
Result< typename Reader< T >::ResSlice, typename Reader< T >::ErrType > mtcore::io::readers::read_until_or_eof (Reader< T > &self, typename Reader< T >::BuffSlice buff, const std::remove_const_t< typename Reader< T >::ReadElem > &delim)
 Reads data from a reader into a Slice up until there is either a delimiter or end of file If the buffer is not big enough, the error SIZE_EXCEEDED will be returned.
 
template<typename T>
Result< typename Reader< T >::BuffSlice, typename Reader< T >::ErrType > mtcore::io::readers::read_until_or_eof (Reader< T > &self, Allocator &alloc, const std::remove_const_t< typename Reader< T >::ReadElem > &delim, size_t maxSize=std::numeric_limits< size_t >::max())
 Reads data from a reader into an allocated Slice up until there is either a delimiter or end of file If the buffer is not big enough, the error SIZE_EXCEEDED will be returned Will return a Slice that the caller MUST clean up with alloc.destroy_many() Will try to shrink internal buffer before returning.
 
template<typename T>
Result< typename Reader< T >::ResSlice, typename Reader< T >::ErrType > mtcore::io::readers::read_until_no_eof (Reader< T > &self, typename Reader< T >::BuffSlice buff, const std::remove_const_t< typename Reader< T >::ReadElem > &delim)
 Reads data from a reader into a Slice up until there is either a delimiter If the buffer is not big enough, the error SIZE_EXCEEDED will be returned If the end of file is reached, the error END_OF_FILE will be returned.
 
template<typename T>
Result< typename Reader< T >::BuffSlice, typename Reader< T >::ErrType > mtcore::io::readers::read_until_no_eof (Reader< T > &self, Allocator &alloc, const std::remove_const_t< typename Reader< T >::ReadElem > &delim, size_t maxSize=std::numeric_limits< size_t >::max())
 Reads data from a reader into an allocated Slice up until there is either a delimiter If the buffer is not big enough, the error SIZE_EXCEEDED will be returned If the end of file is reached, the error END_OF_FILE will be returned Will return a Slice that the caller MUST clean up with alloc.destroy_many() Will try to shrink internal buffer before returning.
 
template<typename T>
Reader< impl::SliceReaderImpl< T > > mtcore::io::slice_reader (Slice< T > buff)
 Creates a reader to read the contents of a Slice.
 

Detailed Description

Methods and coroutines related to readers abstractions.

Function Documentation

◆ read_until_no_eof() [1/2]

template<typename T>
Result< typename Reader< T >::BuffSlice, typename Reader< T >::ErrType > mtcore::io::readers::read_until_no_eof ( Reader< T > & self,
Allocator & alloc,
const std::remove_const_t< typename Reader< T >::ReadElem > & delim,
size_t maxSize = std::numeric_limits<size_t>::max() )

Reads data from a reader into an allocated Slice up until there is either a delimiter If the buffer is not big enough, the error SIZE_EXCEEDED will be returned If the end of file is reached, the error END_OF_FILE will be returned Will return a Slice that the caller MUST clean up with alloc.destroy_many() Will try to shrink internal buffer before returning.

Template Parameters
TType of data being read
Parameters
selfReader to operate on
allocAllocator to use to create buffer
delimDelimiter to read until
maxSizeMaximum size of data to be read (anything >100MB will get smaller allocations)
Returns
Result of data read, or error

Definition at line 500 of file io/reader.hpp.

501 {
502 using ErrType = typename Reader<T>::ErrType;
503 using BuffSlice = typename Reader<T>::BuffSlice;
504 using ReadElem = typename Reader<T>::ReadElem;
505 ensure(maxSize > 0, "BAD MAX SIZE");
506 // If we have no limit or a really high limit (> 100MB), we're going to allocate in chunks
507 if (maxSize == std::numeric_limits<size_t>::max() || maxSize >= 100000000) {
508 // A segmented list will be use to track the data
510 if (auto res = fullBuff.init(alloc, 4000); res.is_error()) {
511 return error(ErrType::ALLOCATION_FAILED);
512 }
513 // We will do a full copy before a successful return
514 // So we can safely set it up to always clean up the full buff
515 mtdefer { fullBuff.deinit(alloc); };
516
517 size_t count = 0;
518
519 while (true) {
520 auto readRes = self.read_one();
521 if (readRes.is_error()) {
522 if (readRes.error().code == ErrType::END_OF_FILE) {
523 return error(ErrType::END_OF_FILE);
524 }
525 return error(ErrType::ALLOCATION_FAILED);
526 }
527
528 auto cur = readRes.value();
529 if (cur == delim) {
530 break;
531 }
532 if (auto pushRes = fullBuff.push(alloc, cur); pushRes.is_error()) {
533 return error(ErrType::ALLOCATION_FAILED);
534 }
535 ++count;
536 }
537
538 if (count == 0) {
539 return BuffSlice{nullptr, 0};
540 }
541
542 // We have everything chunked in memory, but our contract is to return a contiguous set of bytes
543 // To do this, we need to do one final copy into contiguous memory
544 auto finalRes = alloc.create_many<std::remove_const_t<ReadElem>>(count);
545 if (finalRes.is_error()) {
546 return error(ErrType::ALLOCATION_FAILED);
547 }
548
549 auto finalSlice = finalRes.value();
550 // We'll use a Slice writer to make things a little simpler
551 auto finalWriter = slice_writer(finalSlice);
552 // Since we stored the chunks, we can just iterate each chunk and write the entire chunk at once
553 auto iter = fullBuff.iter();
554 std::remove_const_t<ReadElem> curElem;
555 while (iter.next().copy_if_present(curElem)) {
556 if (auto r = finalWriter.write(curElem); r.is_error()) {
557 alloc.destroy_many(finalSlice);
558 return error(ErrType::ALLOCATION_FAILED);
559 }
560 }
561
562 // Return our Result
563 return finalSlice;
564 }
565 else {
566 // If we have a smal enough max size, just read it all into one chunk of memory
567 auto outRes = alloc.create_many<std::remove_const_t<ReadElem>>(maxSize);
568 if (outRes.is_error()) {
569 return error(ErrType::ALLOCATION_FAILED);
570 }
571 auto out = outRes.value();
572 auto res = read_until_no_eof(self, out, delim);
573 if (res.is_error()) {
574 alloc.destroy_many(out);
575 return res.error();
576 }
577 auto finalRes = res.value();
578
579 // Check to see if we can save memory if we shrink things down
580 // This is more CPU time, but less memory pressure
581 if (finalRes.size() + alignof(ReadElem) < out.size()) {
582 if (finalRes.size() == 0) {
583 alloc.destroy_many(out);
584 return BuffSlice{nullptr, 0};
585 }
586 // Try to create a sub optimal buffer
587 auto smallerRes = alloc.create_many<std::remove_const_t<ReadElem>>(finalRes.size());
588 if (smallerRes.is_error()) {
589 // we have it already in memory, it's just suboptimal. Return it anyway
590 return out.sub(0, finalRes.size());
591 }
592 auto smaller = smallerRes.value();
593 auto w = slice_writer(smaller);
594 ensure(w.write_all(finalRes).is_success(), "SOMETHING IS WRONG WITH SLICE WRITER!");
595 alloc.destroy_many(out);
596 return success(smaller);
597 }
598 return success(out.sub(0, finalRes.size()));
599 }
600 }
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
#define mtdefer
Defer statement that will mtdefer execution until the scope is left, at which point the code will run...
Result< typename Reader< T >::ResSlice, typename Reader< T >::ErrType > read_until_no_eof(Reader< T > &self, typename Reader< T >::BuffSlice buff, const std::remove_const_t< typename Reader< T >::ReadElem > &delim)
Reads data from a reader into a Slice up until there is either a delimiter If the buffer is not big e...
Success< void > success()
Creates a successful void Result object.
Definition result.hpp:398
Error< Underlying > error(Underlying err)
Creates an error.
Definition result.hpp:425
io::Writer< impl::SliceWriterImpl< T > > slice_writer(Slice< T > out)
Creates a writer to write to a Slice.
void destroy_many(Slice< T > s)
Destroys objects allocated by this allocator by calling the destructors and freeing memory.
Result< Slice< T >, AllocationError > create_many(size_t count=1)
Allocates some block of memory with an allocator with a known type Will call default constructor on t...
Segmented list where each segment contains multiple nodes Allows growing the list without having to i...
Result< void, CollectionAddNoAllocationError > push(const T &elem) noexcept
Pushes a new element.
void deinit(Allocator &alloc)
Cleans up memory tied to segmented list.
Result< void, AllocationError > init(Allocator &alloc, size_t initCapacity)
Initializes a segmented list.
ConstIter iter() const noexcept
Const iterator that gives element references.
Slice< std::remove_const_t< ReadElem > > BuffSlice
Definition io/reader.hpp:46
typename Impl::ReadElem ReadElem
Definition io/reader.hpp:43
typename Impl::ErrType ErrType
Definition io/reader.hpp:44
Here is the call graph for this function:

◆ read_until_no_eof() [2/2]

template<typename T>
Result< typename Reader< T >::ResSlice, typename Reader< T >::ErrType > mtcore::io::readers::read_until_no_eof ( Reader< T > & self,
typename Reader< T >::BuffSlice buff,
const std::remove_const_t< typename Reader< T >::ReadElem > & delim )

Reads data from a reader into a Slice up until there is either a delimiter If the buffer is not big enough, the error SIZE_EXCEEDED will be returned If the end of file is reached, the error END_OF_FILE will be returned.

Template Parameters
TType of data being read
Parameters
selfReader to operate on
buffBuff to write read data to
delimDelimiter to read until
Returns
Result of data read, or error

Definition at line 457 of file io/reader.hpp.

458 {
459 using ErrType = typename Reader<T>::ErrType;
460 for (size_t i = 0; i < buff.size(); ++i) {
461 auto chRes = self.read_one();
462 if (chRes.is_error()) {
463 if (chRes.error().code == ErrType::END_OF_FILE) {
464 return error(ErrType::END_OF_FILE);
465 }
466 return chRes.error();
467 }
468 auto ch = chRes.value();
469 if (ch == delim) {
470 return buff.sub(0, i).to_const();
471 }
472 buff[i] = ch;
473 }
474 auto nxt = self.read_one();
475 if (nxt.is_error()) {
476 return nxt.error();
477 }
478 if (nxt.value() != delim) {
479 return error(ErrType::SIZE_EXCEEDED);
480 }
481 return buff.to_const();
482 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ read_until_or_eof() [1/2]

template<typename T>
Result< typename Reader< T >::BuffSlice, typename Reader< T >::ErrType > mtcore::io::readers::read_until_or_eof ( Reader< T > & self,
Allocator & alloc,
const std::remove_const_t< typename Reader< T >::ReadElem > & delim,
size_t maxSize = std::numeric_limits<size_t>::max() )

Reads data from a reader into an allocated Slice up until there is either a delimiter or end of file If the buffer is not big enough, the error SIZE_EXCEEDED will be returned Will return a Slice that the caller MUST clean up with alloc.destroy_many() Will try to shrink internal buffer before returning.

Template Parameters
TType of data being read
Parameters
selfReader to operate on
allocAllocator to use to create buffer
delimDelimiter to read until
maxSizeMaximum size of data to be read (anything >100MB will get smaller allocations)
Returns
Result of data read, or error

Definition at line 342 of file io/reader.hpp.

343 {
344 using ErrType = typename Reader<T>::ErrType;
345 using BuffSlice = typename Reader<T>::BuffSlice;
346 using ReadElem = typename Reader<T>::ReadElem;
347 ensure(maxSize > 0, "BAD MAX SIZE");
348 // If we have no limit or a really high limit (> 100MB), we're going to allocate in chunks
349 if (maxSize == std::numeric_limits<size_t>::max() || maxSize >= 100000000) {
350 // A segmented list will be use to track the data
352 if (auto res = fullBuff.init(alloc, 4000); res.is_error()) {
353 return error(ErrType::ALLOCATION_FAILED);
354 }
355 // We will do a full copy before a successful return
356 // So we can safely set it up to always clean up the full buff
357 mtdefer { fullBuff.deinit(alloc); };
358
359 size_t count = 0;
360
361 while (true) {
362 auto readRes = self.read_one();
363 if (readRes.is_error()) {
364 if (readRes.error().code == ErrType::END_OF_FILE) {
365 break;
366 }
367 return error(ErrType::ALLOCATION_FAILED);
368 }
369
370 auto cur = readRes.value();
371 if (cur == delim) {
372 break;
373 }
374 if (auto pushRes = fullBuff.push(alloc, cur); pushRes.is_error()) {
375 return error(ErrType::ALLOCATION_FAILED);
376 }
377 ++count;
378 }
379
380 if (count == 0) {
381 return BuffSlice{nullptr, 0};
382 }
383
384 // We have everything chunked in memory, but our contract is to return a contiguous set of bytes
385 // To do this, we need to do one final copy into contiguous memory
386 auto finalRes = alloc.create_many<std::remove_const_t<ReadElem>>(count);
387 if (finalRes.is_error()) {
388 return error(ErrType::ALLOCATION_FAILED);
389 }
390
391 auto finalSlice = finalRes.value();
392 // We'll use a Slice writer to make things a little simpler
393 auto finalWriter = slice_writer(finalSlice);
394 // Since we stored the chunks, we can just iterate each chunk and write the entire chunk at once
395 auto iter = fullBuff.iter();
396 std::remove_const_t<ReadElem> curElem;
397 while (iter.next().copy_if_present(curElem)) {
398 if (auto r = finalWriter.write(curElem); r.is_error()) {
399 alloc.destroy_many(finalSlice);
400 return error(ErrType::ALLOCATION_FAILED);
401 }
402 }
403
404 // Return our Result
405 return finalSlice;
406 }
407 else {
408 // If we have a smal enough max size, just read it all into one chunk of memory
409 auto outRes = alloc.create_many<std::remove_const_t<ReadElem>>(maxSize);
410 if (outRes.is_error()) {
411 return error(ErrType::ALLOCATION_FAILED);
412 }
413 auto out = outRes.value();
414 auto res = read_until_or_eof(self, out, delim);
415 if (res.is_error()) {
416 alloc.destroy_many(out);
417 return res.error();
418 }
419 auto finalRes = res.value();
420
421 // Check to see if we can save memory if we shrink things down
422 // This is more CPU time, but less memory pressure
423 if (finalRes.size() + alignof(ReadElem) < out.size()) {
424 if (finalRes.size() == 0) {
425 alloc.destroy_many(out);
426 return BuffSlice{nullptr, 0};
427 }
428 // Try to create a sub optimal buffer
429 auto smallerRes = alloc.create_many<std::remove_const_t<ReadElem>>(finalRes.size());
430 if (smallerRes.is_error()) {
431 // we have it already in memory, it's just suboptimal. Return it anyway
432 return out.sub(0, finalRes.size());
433 }
434 auto smaller = smallerRes.value();
435 auto w = slice_writer(smaller);
436 ensure(w.write_all(finalRes).is_success(), "SOMETHING IS WRONG WITH SLICE WRITER!");
437 alloc.destroy_many(out);
438 return success(smaller);
439 }
440 return success(out.sub(0, finalRes.size()));
441 }
442 }
Result< typename Reader< T >::ResSlice, typename Reader< T >::ErrType > read_until_or_eof(Reader< T > &self, typename Reader< T >::BuffSlice buff, const std::remove_const_t< typename Reader< T >::ReadElem > &delim)
Reads data from a reader into a Slice up until there is either a delimiter or end of file If the buff...
Here is the call graph for this function:

◆ read_until_or_eof() [2/2]

template<typename T>
Result< typename Reader< T >::ResSlice, typename Reader< T >::ErrType > mtcore::io::readers::read_until_or_eof ( Reader< T > & self,
typename Reader< T >::BuffSlice buff,
const std::remove_const_t< typename Reader< T >::ReadElem > & delim )

Reads data from a reader into a Slice up until there is either a delimiter or end of file If the buffer is not big enough, the error SIZE_EXCEEDED will be returned.

Template Parameters
TType of data being read
Parameters
selfReader to operate on
buffBuff to write read data to
delimDelimiter to read until
Returns
Result of data read, or error

Definition at line 297 of file io/reader.hpp.

298 {
299 using ErrType = typename Reader<T>::ErrType;
300 for (size_t i = 0; i < buff.size(); ++i) {
301 auto chRes = self.read_one();
302 if (chRes.is_error()) {
303 if (chRes.error().code == ErrType::END_OF_FILE) {
304 return buff.sub(0, i).to_const();
305 }
306 return chRes.error();
307 }
308 auto ch = chRes.value();
309 if (ch == delim) {
310 return buff.sub(0, i).to_const();
311 }
312 buff[i] = ch;
313 }
314 auto nxt = self.read_one();
315 if (nxt.is_error()) {
316 if (nxt.error().code == ErrType::END_OF_FILE) {
317 return buff.to_const();
318 }
319 return nxt.error();
320 }
321 if (nxt.value() != delim) {
322 return error(ErrType::SIZE_EXCEEDED);
323 }
324 return buff.to_const();
325 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ slice_reader()

template<typename T>
Reader< impl::SliceReaderImpl< T > > mtcore::io::slice_reader ( Slice< T > buff)

Creates a reader to read the contents of a Slice.

Template Parameters
TData type to read (should be trivially copyable, constructible, and destructible)
Parameters
buffBuffer to read from
Returns
A reader that reads from the Slice buffer

Definition at line 646 of file io/reader.hpp.

646 {
647 return Reader<impl::SliceReaderImpl<T>>{.underlying = impl::SliceReaderImpl<T>{.buff = buff}};
648 }
A reader that reads data from some sort of stream or buffer Note: the data elements read should be tr...
Definition io/reader.hpp:42