diff options
author | Sven Gothel <[email protected]> | 2022-08-05 12:37:43 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2022-08-05 12:37:43 +0200 |
commit | c82d4c266a80a8b41a184cdadd8ecae37bd635fd (patch) | |
tree | 6536f994720cae2d3f058d3d12bf26e597b14921 | |
parent | c1be87eb850123ac84f255eedbc28449727a98a7 (diff) |
io_util/byte_stream: Decouple from Botan, add iostate and new Byte*Stream* super iostate_func class for std::basic_ios's iostate functionality
The burden of having a compile time header dependency to ByteInStream is too high
and may cause 'confusion' if linking unit w/ different settings - to say the very least!
In case Botan::DataSource is required, user shall wrap ByteInStream into such desired types.
+++
Don't include any iostream header.
std::basic_ios's iostate functionality allows us to reuse the std spec for our streaming classes,
enforcing a well specified iostate to be defined either by just setting the iostate
at read() for non-async sources like file or
by dynamical evaluation via rdstate() override for async sources.
Use shorter method names, eg check_available() -> available()
and iostream affine names, eg bytes_read() -> tellg().
Use 'void*' buffer types for read() and peak(), similar to POSIX,
removing the burden for a nonsense cast.
Drop ByteInStream_istream.
-rw-r--r-- | include/jau/byte_stream.hpp | 516 | ||||
-rw-r--r-- | include/jau/io_util.hpp | 7 | ||||
-rw-r--r-- | src/byte_stream.cpp | 280 | ||||
-rw-r--r-- | src/io_util.cpp | 8 |
4 files changed, 349 insertions, 462 deletions
diff --git a/include/jau/byte_stream.hpp b/include/jau/byte_stream.hpp index 220af27..e67d81c 100644 --- a/include/jau/byte_stream.hpp +++ b/include/jau/byte_stream.hpp @@ -2,10 +2,6 @@ * Author: Sven Gothel <[email protected]> * Copyright (c) 2021 Gothel Software e.K. * - * ByteInStream, ByteInStream_SecMemory and ByteInStream_istream are derived from Botan under same license: - * - Copyright (c) 1999-2007 Jack Lloyd - * - Copyright (c) 2005 Matthew Gregan - * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -38,9 +34,6 @@ #include <jau/ringbuffer.hpp> #include <jau/file_util.hpp> -// Include Botan header files before this one to be integrated w/ Botan! -// #include <botan_all.h> - #include <jau/io_util.hpp> using namespace jau::fractions_i64_literals; @@ -52,49 +45,153 @@ namespace jau::io { * @{ */ -#ifdef BOTAN_VERSION_MAJOR - #define VIRTUAL_BOTAN - #define OVERRIDE_BOTAN override - #define NOEXCEPT_BOTAN -#else - #define VIRTUAL_BOTAN virtual - #define OVERRIDE_BOTAN - #define NOEXCEPT_BOTAN noexcept -#endif + /** + * Mimic std::ios_base::iostate for state functionality, see iostate_func. + * + * This `enum class` type fulfills `C++ named requirements: BitmaskType`. + */ + enum class iostate : uint32_t { + /** No error occurred nor has EOS being reached. Value is no bit set! */ + none = 0, + + /** No error occurred nor has EOS being reached. Value is no bit set! */ + goodbit = 0, + + /** Irrecoverable stream error, including loss of integrity of the underlying stream or media. */ + badbit = 1 << 0, + + /** An input operation reached the end of its stream. */ + eofbit = 1 << 1, + + /** Input or output operation failed (formatting or extraction error). */ + failbit = 1 << 2 + }; + constexpr uint32_t number(const iostate rhs) noexcept { + return static_cast<uint32_t>(rhs); + } + constexpr iostate operator ~(const iostate rhs) noexcept { + return static_cast<iostate> ( ~number(rhs) ); + } + constexpr iostate operator ^(const iostate lhs, const iostate rhs) noexcept { + return static_cast<iostate> ( number(lhs) ^ number(rhs) ); + } + constexpr iostate operator |(const iostate lhs, const iostate rhs) noexcept { + return static_cast<iostate> ( number(lhs) | number(rhs) ); + } + constexpr iostate operator &(const iostate lhs, const iostate rhs) noexcept { + return static_cast<iostate> ( number(lhs) & number(rhs) ); + } + constexpr iostate& operator |=(iostate& lhs, const iostate rhs) noexcept { + lhs = static_cast<iostate> ( number(lhs) | number(rhs) ); + return lhs; + } + constexpr iostate& operator &=(iostate& lhs, const iostate rhs) noexcept { + lhs = static_cast<iostate> ( number(lhs) & number(rhs) ); + return lhs; + } + constexpr iostate& operator ^=(iostate& lhs, const iostate rhs) noexcept { + lhs = static_cast<iostate> ( number(lhs) ^ number(rhs) ); + return lhs; + } + constexpr bool operator ==(const iostate lhs, const iostate rhs) noexcept { + return number(lhs) == number(rhs); + } + constexpr bool operator !=(const iostate lhs, const iostate rhs) noexcept { + return !( lhs == rhs ); + } + std::string to_string(const iostate mask) noexcept; /** - * This class represents an abstract byte input stream object. + * Supporting std::basic_ios's iostate functionality for all ByteInStream implementations. + */ + class iostate_func { + private: + iostate m_state; + + protected: + constexpr iostate rdstate_impl() const noexcept { return m_state; } + constexpr void setstate_impl(iostate state) const noexcept { const_cast<iostate_func*>(this)->m_state |= state; } + + public: + iostate_func() noexcept + : m_state( iostate::goodbit ) {} + + iostate_func(const iostate_func& o) noexcept = default; + iostate_func(iostate_func&& o) noexcept = default; + iostate_func& operator=(const iostate_func &o) noexcept = default; + iostate_func& operator=(iostate_func &&o) noexcept = default; + + virtual ~iostate_func() noexcept {} + + /** Clears state flags by assignment to the given value. */ + virtual void clear(const iostate state = iostate::goodbit) noexcept { m_state = state; } + + /** + * Returns the current state flags. + * + * Method is marked `virtual` to allow implementations with asynchronous resources + * to determine or update the current iostate. + * + * Method is used throughout all query members and setstate(), + * hence they all will use the updated state from a potential override implementation. + */ + virtual iostate rdstate() const noexcept { return m_state; } + + /** Sets state flags, by keeping its previous bits. */ + void setstate(const iostate state) noexcept { clear( rdstate() | state ); } + + /** Checks if no error nor eof() has occurred i.e. I/O operations are available. */ + bool good() const noexcept + { return iostate::goodbit == rdstate(); } + + /** Checks if end-of-file has been reached. */ + bool eof() const noexcept + { return iostate::none != ( rdstate() & iostate::eofbit ); } + + /** Checks if an error has occurred. */ + bool fail() const noexcept + { return iostate::none != ( rdstate() & ( iostate::badbit | iostate::failbit ) ); } + + /** Checks if an error has occurred, synonym of fail(). */ + bool operator!() const noexcept { return fail(); } + + /** Checks if no error has occurred, synonym of !fail(). */ + explicit operator bool() const noexcept { return !fail(); } + + /** Checks if a non-recoverable error has occurred. */ + bool bad() const noexcept + { return iostate::none != ( rdstate() & iostate::badbit ); } + }; + + /** + * Abstract byte input stream object. * * @anchor byte_in_stream_properties * ### ByteInStream Properties * The byte input stream can originate from a local source w/o delay, * remote URL like http connection or even from another thread feeding the input buffer.<br /> * Both latter asynchronous resources may expose blocking properties - * in check_available(). + * in available(). * * Asynchronous resources benefit from knowing their content size, - * as their check_available() implementation may avoid + * as their available() implementation may avoid * blocking and waiting for requested bytes available * if the stream is already beyond its scope. * - * All method implementations are of `noexcept` - * and declared as such if used standalone w/o Botan.<br/> - * However, if using Botan, the `noexcept` qualifier is dropped - * for compatibility with Botan::DataSource virtual function table. + * All method implementations are of `noexcept`. * - * One may use error() to detect whether an error has occurred, - * while end_of_data() not only covered the EOS case but includes error(). + * One may use fail() to detect whether an error has occurred, + * while end_of_data() not only covers the end-of-stream (EOS) case but includes fail(). * * @see @ref byte_in_stream_properties "ByteInStream Properties" */ - class ByteInStream -#ifdef BOTAN_VERSION_MAJOR - : public Botan::DataSource -#endif + class ByteInStream : public iostate_func { public: - ByteInStream() = default; - virtual ~ByteInStream() NOEXCEPT_BOTAN = default; + ByteInStream() noexcept + : iostate_func() {} + + virtual ~ByteInStream() noexcept = default; ByteInStream& operator=(const ByteInStream&) = delete; ByteInStream(const ByteInStream&) = delete; @@ -104,38 +201,56 @@ namespace jau::io { virtual void close() noexcept = 0; /** - * Check whether n bytes are available in the input stream. + * Return whether n bytes are available in the input stream, + * if has_content_size() or using an asynchronous source. * - * This method may be blocking when using an asynchronous source - * up until the requested bytes are actually available. + * If !has_content_size() and not being an asynchronous source, + * !end_of_data() is returned. + * + * Method may be blocking when using an asynchronous source + * up until the requested bytes are available. * * A subsequent call to read() shall return immediately with at least - * the requested numbers of bytes available. + * the requested numbers of bytes available, + * if has_content_size() or using an asynchronous source. + * + * See details of the implementing class. * * @param n byte count to wait for * @return true if n bytes are available, otherwise false * + * @see has_content_size() * @see read() * @see @ref byte_in_stream_properties "ByteInStream Properties" */ - VIRTUAL_BOTAN bool check_available(size_t n) NOEXCEPT_BOTAN OVERRIDE_BOTAN = 0; + virtual bool available(size_t n) noexcept = 0; /** * Read from the source. Moves the internal offset so that every * call to read will return a new portion of the source. * - * Use check_available() to wait and ensure a certain amount of bytes are available. + * Use available() to try to wait for a certain amount of bytes available. + * + * This method shall only block until `min(available, length)` bytes are transfered. * - * This method is not blocking. + * See details of the implementing class. * * @param out the byte array to write the result to * @param length the length of the byte array out * @return length in bytes that was actually read and put into out * - * @see check_available() + * @see available() * @see @ref byte_in_stream_properties "ByteInStream Properties" */ - [[nodiscard]] VIRTUAL_BOTAN size_t read(uint8_t out[], size_t length) NOEXCEPT_BOTAN OVERRIDE_BOTAN = 0; + [[nodiscard]] virtual size_t read(void* out, size_t length) noexcept = 0; + + /** + * Read one byte. + * @param out the byte to read to + * @return length in bytes that was actually read and put + * into out + */ + size_t read(uint8_t& out) noexcept; /** * Read from the source but do not modify the internal @@ -147,40 +262,7 @@ namespace jau::io { * @param peek_offset the offset into the stream to read at * @return length in bytes that was actually read and put into out */ - [[nodiscard]] VIRTUAL_BOTAN size_t peek(uint8_t out[], size_t length, size_t peek_offset) const NOEXCEPT_BOTAN OVERRIDE_BOTAN = 0; - - /** - * Test whether the source still has data that can be read. - * - * This may include a failure and/or error in the underlying implementation, see error() - * - * @return true if there is no more data to read, false otherwise - * @see error() - */ - VIRTUAL_BOTAN bool end_of_data() const NOEXCEPT_BOTAN OVERRIDE_BOTAN = 0; - - /** - * Return whether an error has occurred, excluding end_of_data(). - * - * @return true if an error has occurred, false otherwise - */ - virtual bool error() const noexcept = 0; - -#ifndef BOTAN_VERSION_MAJOR - - /** - * return the id of this data source - * @return std::string representing the id of this data source - */ - virtual std::string id() const noexcept { return ""; } - - /** - * Read one byte. - * @param out the byte to read to - * @return length in bytes that was actually read and put - * into out - */ - size_t read_byte(uint8_t& out) noexcept; + [[nodiscard]] virtual size_t peek(void* out, size_t length, size_t peek_offset) noexcept = 0; /** * Peek at one byte. @@ -188,27 +270,38 @@ namespace jau::io { * @return length in bytes that was actually read and put * into out */ - size_t peek_byte(uint8_t& out) const noexcept; + size_t peek(uint8_t& out) noexcept; /** * Discard the next N bytes of the data * @param N the number of bytes to discard * @return number of bytes actually discarded */ - size_t discard_next(size_t N) noexcept; + size_t discard(size_t N) noexcept; -#endif /* BOTAN_VERSION_MAJOR */ + /** + * Test whether the source still has data that can be read, synonym for !good(). + * + * Hence this includes errors in the underlying implementation, see fail() + * + * @return true if there is no more data to read, false otherwise + * @see good() + * @see fail() + */ + bool end_of_data() const noexcept { return !good(); } /** - * @return number of bytes read so far. + * return the id of this data source + * @return std::string representing the id of this data source */ - VIRTUAL_BOTAN size_t get_bytes_read() const NOEXCEPT_BOTAN OVERRIDE_BOTAN = 0; + virtual std::string id() const noexcept { return ""; } /** - * Variant of Botan's get_byte_read() using uint64_t for big files on a 32-bit platform. + * Returns the input position indicator, similar to std::basic_istream. + * * @return number of bytes read so far. */ - virtual uint64_t bytes_read() const noexcept = 0; + virtual uint64_t tellg() const noexcept = 0; /** * Returns true if implementation is aware of content_size(), otherwise false. @@ -226,19 +319,15 @@ namespace jau::io { }; /** - * This class represents a secure Memory-Based byte input stream + * Secure Memory-Based byte input stream */ class ByteInStream_SecMemory final : public ByteInStream { public: - size_t read(uint8_t[], size_t) NOEXCEPT_BOTAN override; - - size_t peek(uint8_t[], size_t, size_t) const NOEXCEPT_BOTAN override; + size_t read(void*, size_t) noexcept override; - bool check_available(size_t n) NOEXCEPT_BOTAN override; + size_t peek(void*, size_t, size_t) noexcept override; - bool end_of_data() const NOEXCEPT_BOTAN override; - - bool error() const noexcept override { return false; } + bool available(size_t n) noexcept override; /** * Construct a secure memory source that reads from a string @@ -270,11 +359,9 @@ namespace jau::io { void close() noexcept override; - ~ByteInStream_SecMemory() NOEXCEPT_BOTAN override { close(); } - - size_t get_bytes_read() const NOEXCEPT_BOTAN override { return m_offset; } + ~ByteInStream_SecMemory() noexcept override { close(); } - uint64_t bytes_read() const noexcept override { return m_offset; } + uint64_t tellg() const noexcept override { return m_offset; } bool has_content_size() const noexcept override { return true; } @@ -288,146 +375,42 @@ namespace jau::io { }; /** - * This class represents a std::istream based byte input stream. - */ - class ByteInStream_istream final : public ByteInStream { - public: - size_t read(uint8_t[], size_t) NOEXCEPT_BOTAN override; - size_t peek(uint8_t[], size_t, size_t) const NOEXCEPT_BOTAN override; - bool check_available(size_t n) NOEXCEPT_BOTAN override; - bool end_of_data() const NOEXCEPT_BOTAN override; - bool error() const noexcept override { return m_source.bad(); } - std::string id() const NOEXCEPT_BOTAN override; - - ByteInStream_istream(std::istream&, const std::string& id = "<std::istream>") noexcept; - - ByteInStream_istream(const ByteInStream_istream&) = delete; - - ByteInStream_istream& operator=(const ByteInStream_istream&) = delete; - - void close() noexcept override; - - ~ByteInStream_istream() NOEXCEPT_BOTAN override { close(); } - - size_t get_bytes_read() const NOEXCEPT_BOTAN override { return m_bytes_consumed; } - - uint64_t bytes_read() const noexcept override { return m_bytes_consumed; } - - bool has_content_size() const noexcept override { return false; } - - uint64_t content_size() const noexcept override { return 0; } - - std::string to_string() const noexcept override; - - private: - const std::string m_identifier; - - std::istream& m_source; - size_t m_bytes_consumed; - }; - - - /** - * Mimic std::iobase::iostate + * File based byte input stream, including named file descriptor. * - * This `enum class` type fulfills `C++ named requirements: BitmaskType`. - */ - enum class iostate : uint32_t { - /** No error occurred nor has EOS being reached. Value is no bit set! */ - none = 0, - - /** No error occurred nor has EOS being reached. Value is no bit set! */ - goodbit = 0, - - /** Irrecoverable stream error, including loss of integrity of the underlying stream or media. */ - badbit = 1 << 0, - - /** An input operation reached the end of its stream. */ - eofbit = 1 << 1, - - /** Input or output operation failed (formatting or extraction error). */ - failbit = 1 << 2 - }; - constexpr uint32_t number(const iostate rhs) noexcept { - return static_cast<uint32_t>(rhs); - } - constexpr iostate operator ~(const iostate rhs) noexcept { - return static_cast<iostate> ( ~number(rhs) ); - } - constexpr iostate operator ^(const iostate lhs, const iostate rhs) noexcept { - return static_cast<iostate> ( number(lhs) ^ number(rhs) ); - } - constexpr iostate operator |(const iostate lhs, const iostate rhs) noexcept { - return static_cast<iostate> ( number(lhs) | number(rhs) ); - } - constexpr iostate operator &(const iostate lhs, const iostate rhs) noexcept { - return static_cast<iostate> ( number(lhs) & number(rhs) ); - } - constexpr iostate& operator |=(iostate& lhs, const iostate rhs) noexcept { - lhs = static_cast<iostate> ( number(lhs) | number(rhs) ); - return lhs; - } - constexpr iostate& operator &=(iostate& lhs, const iostate rhs) noexcept { - lhs = static_cast<iostate> ( number(lhs) & number(rhs) ); - return lhs; - } - constexpr iostate& operator ^=(iostate& lhs, const iostate rhs) noexcept { - lhs = static_cast<iostate> ( number(lhs) ^ number(rhs) ); - return lhs; - } - constexpr bool operator ==(const iostate lhs, const iostate rhs) noexcept { - return number(lhs) == number(rhs); - } - constexpr bool operator !=(const iostate lhs, const iostate rhs) noexcept { - return !( lhs == rhs ); - } - std::string to_string(const iostate mask) noexcept; - - /** - * This class represents a file based byte input stream, including named file descriptor. + * Implementation mimics std::ifstream via OS level file descriptor (FD) operations, + * giving more flexibility, allowing reusing existing FD and enabling openat() operations. * * If source path denotes a named file descriptor, i.e. jau::fs::file_stats::is_fd() returns true, - * has_content_size() returns false and check_available() returns true as long the stream is open and EOS hasn't occurred. + * has_content_size() returns false and available() returns true as long the stream is open and EOS hasn't occurred. */ class ByteInStream_File final : public ByteInStream { private: jau::fs::file_stats stats; - /** - * We mimic std::ifstream via OS level file descriptor operations, - * giving us more flexibility and enabling use of openat() operations. - */ + int m_fd; - mutable iostate m_state; + bool m_has_content_length; + uint64_t m_content_size; + uint64_t m_bytes_consumed; + uint64_t get_available() const noexcept { return m_has_content_length ? m_content_size - m_bytes_consumed : 0; } - // Remember: constexpr specifier used in a function or static data member (since C++17) declaration implies inline + public: + size_t read(void*, size_t) noexcept override; + size_t peek(void*, size_t, size_t) noexcept override; + bool available(size_t n) noexcept override; + /** Checks if the stream has an associated file. */ constexpr bool is_open() const noexcept { return 0 <= m_fd; } - constexpr bool good() const noexcept - { return iostate::goodbit == m_state; } + std::string id() const noexcept override { return stats.path(); } - constexpr bool eof() const noexcept - { return iostate::none != ( m_state & iostate::eofbit ); } - - constexpr bool fail() const noexcept - { return iostate::none != ( m_state & ( iostate::badbit | iostate::failbit ) ); } - - constexpr bool bad() const noexcept - { return iostate::none != ( m_state & iostate::badbit ); } - - constexpr iostate rdstate() const noexcept { return m_state; } - void clear(iostate state = iostate::goodbit) const noexcept { m_state = state; } - void setstate(iostate state) const noexcept { m_state |= state; } - - public: - size_t read(uint8_t[], size_t) NOEXCEPT_BOTAN override; - size_t peek(uint8_t[], size_t, size_t) const NOEXCEPT_BOTAN override; - bool check_available(size_t n) NOEXCEPT_BOTAN override; - bool end_of_data() const NOEXCEPT_BOTAN override; - bool error() const noexcept override { return fail(); } - std::string id() const NOEXCEPT_BOTAN override { return stats.path(); } + /** + * Returns the file descriptor if is_open(), otherwise -1 for no file descriptor. + * + * @see is_open() + */ + int fd() const noexcept { return m_fd; } /** * Construct a stream based byte input stream from filesystem path @@ -466,23 +449,15 @@ namespace jau::io { void close() noexcept override; - ~ByteInStream_File() NOEXCEPT_BOTAN override { close(); } - - size_t get_bytes_read() const NOEXCEPT_BOTAN override { return (size_t)m_bytes_consumed; } + virtual ~ByteInStream_File() noexcept override { close(); } - uint64_t bytes_read() const noexcept override { return m_bytes_consumed; } + uint64_t tellg() const noexcept override { return m_bytes_consumed; } bool has_content_size() const noexcept override { return m_has_content_length; } uint64_t content_size() const noexcept override { return m_content_size; } std::string to_string() const noexcept override; - - private: - uint64_t get_available() const noexcept { return m_has_content_length ? m_content_size - m_bytes_consumed : 0; } - bool m_has_content_length; - uint64_t m_content_size; - uint64_t m_bytes_consumed; }; /** @@ -507,37 +482,35 @@ namespace jau::io { * @see read() * @see @ref byte_in_stream_properties "ByteInStream Properties" */ - bool check_available(size_t n) NOEXCEPT_BOTAN override; + bool available(size_t n) noexcept override; /** * Read from the source. Moves the internal offset so that every * call to read will return a new portion of the source. * - * Use check_available() to wait and ensure a certain amount of bytes are available. + * Use available() to wait and ensure a certain amount of bytes are available. * - * This method is not blocking. + * This method is not blocking beyond the transfer of `min(available, length)` bytes. * * @param out the byte array to write the result to * @param length the length of the byte array out * @return length in bytes that was actually read and put into out * - * @see check_available() + * @see available() * @see @ref byte_in_stream_properties "ByteInStream Properties" */ - size_t read(uint8_t out[], size_t length) NOEXCEPT_BOTAN override; - - size_t peek(uint8_t out[], size_t length, size_t peek_offset) const NOEXCEPT_BOTAN override; + size_t read(void* out, size_t length) noexcept override; - bool end_of_data() const NOEXCEPT_BOTAN override; + size_t peek(void* out, size_t length, size_t peek_offset) noexcept override; - bool error() const noexcept override { return async_io_result_t::FAILED == m_result; } + iostate rdstate() const noexcept override; - std::string id() const NOEXCEPT_BOTAN override { return m_url; } + std::string id() const noexcept override { return m_url; } /** * Construct a ringbuffer backed Http byte input stream * @param url the URL of the data to read - * @param timeout maximum duration in fractions of seconds to wait @ check_available() for next bytes, where fractions_i64::zero waits infinitely + * @param timeout maximum duration in fractions of seconds to wait @ available() for next bytes, where fractions_i64::zero waits infinitely */ ByteInStream_URL(const std::string& url, const jau::fraction_i64& timeout) noexcept; @@ -547,11 +520,9 @@ namespace jau::io { void close() noexcept override; - ~ByteInStream_URL() NOEXCEPT_BOTAN override { close(); } + ~ByteInStream_URL() noexcept override { close(); } - size_t get_bytes_read() const NOEXCEPT_BOTAN override { return (size_t)m_bytes_consumed; } - - uint64_t bytes_read() const noexcept override { return m_bytes_consumed; } + uint64_t tellg() const noexcept override { return m_bytes_consumed; } bool has_content_size() const noexcept override { return m_has_content_length; } @@ -580,11 +551,11 @@ namespace jau::io { * * If the above fails, ByteInStream_File is attempted. * - * If non of the above leads to a ByteInStream without ByteInStream::error(), nullptr is returned. + * If non of the above leads to a ByteInStream without ByteInStream::fail(), nullptr is returned. * * @param path_or_uri given path or uri for with a ByteInStream instance shall be established. - * @param timeout in case `path_or_uri` resolves to ByteInStream_URL, timeout is being used as maximum duration to wait for next bytes at ByteInStream_URL::check_available(), defaults to 20_s - * @return a working ByteInStream w/o ByteInStream::error() or nullptr + * @param timeout in case `path_or_uri` resolves to ByteInStream_URL, timeout is being used as maximum duration to wait for next bytes at ByteInStream_URL::available(), defaults to 20_s + * @return a working ByteInStream w/o ByteInStream::fail() or nullptr */ std::unique_ptr<ByteInStream> to_ByteInStream(const std::string& path_or_uri, jau::fraction_i64 timeout=20_s) noexcept; @@ -606,37 +577,35 @@ namespace jau::io { * @see read() * @see @ref byte_in_stream_properties "ByteInStream Properties" */ - bool check_available(size_t n) NOEXCEPT_BOTAN override; + bool available(size_t n) noexcept override; /** * Read from the source. Moves the internal offset so that every * call to read will return a new portion of the source. * - * Use check_available() to wait and ensure a certain amount of bytes are available. + * Use available() to wait and ensure a certain amount of bytes are available. * - * This method is not blocking. + * This method is not blocking beyond the transfer of `min(available, length)` bytes. * * @param out the byte array to write the result to * @param length the length of the byte array out * @return length in bytes that was actually read and put into out * - * @see check_available() + * @see available() * @see @ref byte_in_stream_properties "ByteInStream Properties" */ - size_t read(uint8_t out[], size_t length) NOEXCEPT_BOTAN override; - - size_t peek(uint8_t out[], size_t length, size_t peek_offset) const NOEXCEPT_BOTAN override; + size_t read(void* out, size_t length) noexcept override; - bool end_of_data() const NOEXCEPT_BOTAN override; + size_t peek(void* out, size_t length, size_t peek_offset) noexcept override; - bool error() const noexcept override { return async_io_result_t::FAILED == m_result; } + iostate rdstate() const noexcept override; - std::string id() const NOEXCEPT_BOTAN override { return m_id; } + std::string id() const noexcept override { return m_id; } /** * Construct a ringbuffer backed externally provisioned byte input stream * @param id_name arbitrary identifier for this instance - * @param timeout maximum duration in fractions of seconds to wait @ check_available() and write(), where fractions_i64::zero waits infinitely + * @param timeout maximum duration in fractions of seconds to wait @ available() and write(), where fractions_i64::zero waits infinitely */ ByteInStream_Feed(const std::string& id_name, const jau::fraction_i64& timeout) noexcept; @@ -646,11 +615,9 @@ namespace jau::io { void close() noexcept override; - ~ByteInStream_Feed() NOEXCEPT_BOTAN override { close(); } + ~ByteInStream_Feed() noexcept override { close(); } - size_t get_bytes_read() const NOEXCEPT_BOTAN override { return m_bytes_consumed; } - - uint64_t bytes_read() const noexcept override { return m_bytes_consumed; } + uint64_t tellg() const noexcept override { return m_bytes_consumed; } bool has_content_size() const noexcept override { return m_has_content_length; } @@ -659,7 +626,7 @@ namespace jau::io { /** * Interrupt a potentially blocked reader. * - * Call this method if intended to abort streaming and to interrupt the reader thread's potentially blocked check_available() call, + * Call this method if intended to abort streaming and to interrupt the reader thread's potentially blocked available() call, * i.e. done at set_eof() * * @see set_eof() @@ -699,7 +666,7 @@ namespace jau::io { * * @see interruptReader() */ - void set_eof(const async_io_result_t result) noexcept { m_result = result; interruptReader(); } + void set_eof(const async_io_result_t result) noexcept; std::string to_string() const noexcept override; @@ -719,27 +686,26 @@ namespace jau::io { /** * This class represents a wrapped byte input stream with the capability - * to record the byte stream read out at will. + * to record the read byte stream at will. * * Peek'ed bytes won't be recorded, only read bytes. */ class ByteInStream_Recorder final : public ByteInStream { public: - size_t read(uint8_t[], size_t) NOEXCEPT_BOTAN override; + size_t read(void*, size_t) noexcept override; - size_t peek(uint8_t out[], size_t length, size_t peek_offset) const NOEXCEPT_BOTAN override { + size_t peek(void* out, size_t length, size_t peek_offset) noexcept override { return m_parent.peek(out, length, peek_offset); } - bool check_available(size_t n) NOEXCEPT_BOTAN override { - return m_parent.check_available(n); + bool available(size_t n) noexcept override { + return m_parent.available(n); } - bool end_of_data() const NOEXCEPT_BOTAN override { return m_parent.end_of_data(); } - - bool error() const noexcept override { return m_parent.error(); } + void clear(iostate state = iostate::goodbit) noexcept override { m_parent.clear( state ); } + iostate rdstate() const noexcept override { return m_parent.rdstate(); } - std::string id() const NOEXCEPT_BOTAN override { return m_parent.id(); } + std::string id() const noexcept override { return m_parent.id(); } /** * Construct a byte input stream wrapper using the given parent ByteInStream. @@ -755,11 +721,9 @@ namespace jau::io { void close() noexcept override; - ~ByteInStream_Recorder() NOEXCEPT_BOTAN override { close(); } - - size_t get_bytes_read() const NOEXCEPT_BOTAN override { return m_parent.get_bytes_read(); } + ~ByteInStream_Recorder() noexcept override { close(); } - uint64_t bytes_read() const noexcept override { return m_bytes_consumed; } + uint64_t tellg() const noexcept override { return m_bytes_consumed; } bool has_content_size() const noexcept override { return m_parent.has_content_size(); } diff --git a/include/jau/io_util.hpp b/include/jau/io_util.hpp index ad91a42..9f28cf7 100644 --- a/include/jau/io_util.hpp +++ b/include/jau/io_util.hpp @@ -36,9 +36,6 @@ #include <jau/callocator_sec.hpp> #include <jau/ringbuffer.hpp> -// Include Botan header files before this one to be integrated w/ Botan! -// #include <botan_all.h> - namespace jau::io { /** @defgroup IOUtils IO Utilities * Input and Output (IO) types and functionality. @@ -46,11 +43,7 @@ namespace jau::io { * @{ */ -#ifdef BOTAN_VERSION_MAJOR - template<typename T> using secure_vector = std::vector<T, Botan::secure_allocator<T>>; -#else template<typename T> using secure_vector = std::vector<T, jau::callocator_sec<T>>; -#endif typedef std::basic_string<char, std::char_traits<char>, jau::callocator_sec<char>> secure_string; diff --git a/src/byte_stream.cpp b/src/byte_stream.cpp index 9bc088b..b834003 100644 --- a/src/byte_stream.cpp +++ b/src/byte_stream.cpp @@ -26,8 +26,6 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include <fstream> -#include <iostream> #include <chrono> // #include <botan_all.h> @@ -74,7 +72,7 @@ using namespace jau::fractions_i64_literals; const size_t jau::io::BEST_URLSTREAM_RINGBUFFER_SIZE = 2*16384; #endif -inline constexpr void copy_mem(uint8_t* out, const uint8_t* in, size_t n) noexcept { +inline constexpr void copy_mem(void* out, const void* in, size_t n) noexcept { if(in != nullptr && out != nullptr && n > 0) { std::memmove(out, in, sizeof(uint8_t)*n); } @@ -88,50 +86,49 @@ inline const uint8_t* cast_char_ptr_to_uint8(const char* s) noexcept { return reinterpret_cast<const uint8_t*>(s); } -#ifndef BOTAN_VERSION_MAJOR - -size_t ByteInStream::read_byte(uint8_t& out) noexcept { +size_t ByteInStream::read(uint8_t& out) noexcept { return read(&out, 1); } -size_t ByteInStream::peek_byte(uint8_t& out) const noexcept { +size_t ByteInStream::peek(uint8_t& out) noexcept { return peek(&out, 1, 0); } -size_t ByteInStream::discard_next(size_t n) noexcept { - uint8_t buf[64] = { 0 }; +size_t ByteInStream::discard(size_t n) noexcept { + uint8_t buf[1024] = { 0 }; size_t discarded = 0; while(n) { - const size_t got = this->read(buf, std::min(n, sizeof(buf))); + const size_t got = read(buf, std::min(n, sizeof(buf))); + if( 0 == got ) { + break; + } discarded += got; n -= got; - - if(got == 0) - break; } return discarded; } -#endif /* BOTAN_VERSION_MAJOR */ - -size_t ByteInStream_SecMemory::read(uint8_t out[], size_t length) NOEXCEPT_BOTAN { +size_t ByteInStream_SecMemory::read(void* out, size_t length) noexcept { if( 0 == length || end_of_data() ) { return 0; } const size_t got = std::min<size_t>(m_source.size() - m_offset, length); copy_mem(out, m_source.data() + m_offset, got); m_offset += got; + if( m_source.size() == m_offset ) { + setstate_impl( iostate::eofbit ); + } return got; } -bool ByteInStream_SecMemory::check_available(size_t n) NOEXCEPT_BOTAN { +bool ByteInStream_SecMemory::available(size_t n) noexcept { return m_source.size() - m_offset >= n; } -size_t ByteInStream_SecMemory::peek(uint8_t out[], size_t length, size_t peek_offset) const NOEXCEPT_BOTAN { +size_t ByteInStream_SecMemory::peek(void* out, size_t length, size_t peek_offset) noexcept { const size_t bytes_left = m_source.size() - m_offset; if(peek_offset >= bytes_left) { return 0; @@ -141,10 +138,6 @@ size_t ByteInStream_SecMemory::peek(uint8_t out[], size_t length, size_t peek_of return got; } -bool ByteInStream_SecMemory::end_of_data() const NOEXCEPT_BOTAN { - return m_source.size() == m_offset; -} - ByteInStream_SecMemory::ByteInStream_SecMemory(const std::string& in) : m_source(cast_char_ptr_to_uint8(in.data()), cast_char_ptr_to_uint8(in.data()) + in.length()), @@ -160,90 +153,8 @@ std::string ByteInStream_SecMemory::to_string() const noexcept { return "ByteInStream_SecMemory[content size "+jau::to_decstring(m_source.size())+ ", consumed "+jau::to_decstring(m_offset)+ ", available "+jau::to_decstring(m_source.size()-m_offset)+ - ", eod "+std::to_string( end_of_data() )+ - ", error "+std::to_string( error() )+ - "]"; -} - -size_t ByteInStream_istream::read(uint8_t out[], size_t length) NOEXCEPT_BOTAN { - if( 0 == length || end_of_data() ) { - return 0; - } - m_source.read(cast_uint8_ptr_to_char(out), length); - if( error() ) { - DBG_PRINT("ByteInStream_istream::read: Error occurred in %s", to_string().c_str()); - return 0; - } - const size_t got = static_cast<size_t>(m_source.gcount()); - m_bytes_consumed += got; - return got; -} - -bool ByteInStream_istream::check_available(size_t n) NOEXCEPT_BOTAN { - // stream size is dynamic, hence can't store size until end - const std::streampos orig_pos = m_source.tellg(); - m_source.seekg(0, std::ios::end); - uint64_t avail = static_cast<uint64_t>(m_source.tellg() - orig_pos); - m_source.seekg(orig_pos); - return avail >= n; -} - -size_t ByteInStream_istream::peek(uint8_t out[], size_t length, size_t offset) const NOEXCEPT_BOTAN { - if( 0 == length || end_of_data() ) { - return 0; - } - size_t got = 0; - - if( offset ) { - secure_vector<uint8_t> buf(offset); - m_source.read(cast_uint8_ptr_to_char(buf.data()), buf.size()); - if( error() ) { - DBG_PRINT("ByteInStream_istream::peek: Error occurred (offset) in %s", to_string().c_str()); - return 0; - } - got = static_cast<size_t>(m_source.gcount()); - } - - if(got == offset) { - m_source.read(cast_uint8_ptr_to_char(out), length); - if( error() ) { - DBG_PRINT("ByteInStream_istream::peek: Error occurred (read) in %s", to_string().c_str()); - return 0; - } - got = static_cast<size_t>(m_source.gcount()); - } - - if( m_source.eof() ) { - m_source.clear(); - } - m_source.seekg(m_bytes_consumed, std::ios::beg); - - return got; -} - -bool ByteInStream_istream::end_of_data() const NOEXCEPT_BOTAN { - return !m_source.good(); -} - -std::string ByteInStream_istream::id() const NOEXCEPT_BOTAN { - return m_identifier; -} - -ByteInStream_istream::ByteInStream_istream(std::istream& in, const std::string& name) noexcept -: m_identifier(name), m_source(in), - m_bytes_consumed(0) -{ } - -void ByteInStream_istream::close() noexcept { - // nop -} - -std::string ByteInStream_istream::to_string() const noexcept { - return "ByteInStream_Stream["+m_identifier+ - ", consumed "+jau::to_decstring(m_bytes_consumed)+ - ", eod "+std::to_string( end_of_data() )+ - ", error "+std::to_string( error() )+ - "]"; + ", iostate["+jau::io::to_string(rdstate())+ + "]]"; } template<typename T> @@ -271,26 +182,30 @@ std::string jau::io::to_string(const iostate mask) noexcept { return out; } -size_t ByteInStream_File::read(uint8_t out[], size_t length) NOEXCEPT_BOTAN { +size_t ByteInStream_File::read(void* out, size_t length) noexcept { if( 0 == length || end_of_data() ) { return 0; } + uint8_t* out_u8 = static_cast<uint8_t*>(out); size_t total = 0; while( total < length ) { ssize_t len; - while ( ( len = ::read(m_fd, out+total, length-total) ) < 0 ) { - if ( errno == EAGAIN || errno == EINTR ) { + while ( ( len = ::read(m_fd, out_u8+total, length-total) ) < 0 ) { + if ( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { // cont temp unavail or interruption + // unlikely for regular files and we open w/o O_NONBLOCK + // - EAGAIN (file) and EWOULDBLOCK (socket) if blocking + // - EINTR (signal) continue; } // Check errno == ETIMEDOUT ?? - setstate( iostate::failbit ); + setstate_impl( iostate::failbit ); DBG_PRINT("ByteInStream_File::read: Error occurred in %s, errno %d %s", to_string().c_str(), errno, strerror(errno)); return 0; } total += static_cast<size_t>(len); if( 0 == len || ( m_has_content_length && m_bytes_consumed + total >= m_content_size ) ) { - setstate( iostate::eofbit ); // Note: std::istream also sets iostate::failbit on eof, we don't. + setstate_impl( iostate::eofbit ); // Note: std::istream also sets iostate::failbit on eof, we don't. break; } } @@ -298,7 +213,7 @@ size_t ByteInStream_File::read(uint8_t out[], size_t length) NOEXCEPT_BOTAN { return total; } -size_t ByteInStream_File::peek(uint8_t out[], size_t length, size_t offset) const NOEXCEPT_BOTAN { +size_t ByteInStream_File::peek(void* out, size_t length, size_t offset) noexcept { if( 0 == length || end_of_data() || offset > std::numeric_limits<off64_t>::max() || ( m_has_content_length && m_content_size - m_bytes_consumed < offset + 1 /* min number of bytes to read */ ) ) { return 0; @@ -309,7 +224,7 @@ size_t ByteInStream_File::peek(uint8_t out[], size_t length, size_t offset) cons if( 0 < offset ) { abs_pos = __posix_lseek64(m_fd, static_cast<off64_t>(offset), SEEK_CUR); if( 0 > abs_pos ) { - setstate( iostate::failbit ); + setstate_impl( iostate::failbit ); DBG_PRINT("ByteInStream_File::peek: Error occurred (offset1 %zd) in %s, errno %d %s", offset, to_string().c_str(), errno, strerror(errno)); return 0; @@ -323,7 +238,7 @@ size_t ByteInStream_File::peek(uint8_t out[], size_t length, size_t offset) cons continue; } // Check errno == ETIMEDOUT ?? - setstate( iostate::failbit ); + setstate_impl( iostate::failbit ); DBG_PRINT("ByteInStream_File::peak: Error occurred (read) in %s, errno %d %s", to_string().c_str(), errno, strerror(errno)); return 0; } @@ -331,7 +246,7 @@ size_t ByteInStream_File::peek(uint8_t out[], size_t length, size_t offset) cons } if( __posix_lseek64(m_fd, static_cast<off64_t>(m_bytes_consumed), SEEK_SET) < 0 ) { // even though we were able to fetch the desired data above, let's fail if position reset fails - setstate( iostate::failbit ); + setstate_impl( iostate::failbit ); DBG_PRINT("ByteInStream_File::peek: Error occurred (offset2 %zd) in %s, errno %d %s", offset, to_string().c_str(), errno, strerror(errno)); return 0; @@ -339,36 +254,31 @@ size_t ByteInStream_File::peek(uint8_t out[], size_t length, size_t offset) cons return got; } -bool ByteInStream_File::check_available(size_t n) NOEXCEPT_BOTAN { +bool ByteInStream_File::available(size_t n) noexcept { return is_open() && good() && ( !m_has_content_length || m_content_size - m_bytes_consumed >= (uint64_t)n ); }; -bool ByteInStream_File::end_of_data() const NOEXCEPT_BOTAN { - return !is_open() || !good() || ( m_has_content_length && m_bytes_consumed >= m_content_size ); -} - ByteInStream_File::ByteInStream_File(const int fd) noexcept -: m_fd(-1), m_state(iostate::goodbit), - m_has_content_length(false), m_content_size(0), m_bytes_consumed(0) +: ByteInStream(), + stats(fd), m_fd(-1), m_has_content_length(false), m_content_size(0), m_bytes_consumed(0) { - stats = jau::fs::file_stats(fd); if( !stats.exists() || !stats.has_access() ) { - setstate( iostate::failbit ); // Note: conforming with std::ifstream open + setstate_impl( iostate::failbit ); // Note: conforming with std::ifstream open DBG_PRINT("ByteInStream_File::ctor: Error, not an existing or accessible file in %s, %s", stats.to_string().c_str(), to_string().c_str()); } else { - m_has_content_length = stats.is_file(); - m_content_size = stats.size(); + m_has_content_length = stats.has( jau::fs::file_stats::field_t::size ); + m_content_size = m_has_content_length ? stats.size() : 0; m_fd = ::dup(fd); if ( 0 > m_fd ) { - setstate( iostate::failbit ); // Note: conforming with std::ifstream open + setstate_impl( iostate::failbit ); // Note: conforming with std::ifstream open DBG_PRINT("ByteInStream_File::ctor: Error occurred in %s, %s", stats.to_string().c_str(), to_string().c_str()); } } } ByteInStream_File::ByteInStream_File(const int dirfd, const std::string& path) noexcept -: m_fd(-1), m_state(iostate::goodbit), - m_has_content_length(false), m_content_size(0), m_bytes_consumed(0) +: ByteInStream(), + stats(), m_fd(-1), m_has_content_length(false), m_content_size(0), m_bytes_consumed(0) { if( jau::io::uri_tk::is_local_file_protocol(path) ) { // cut of leading `file://` @@ -378,16 +288,25 @@ ByteInStream_File::ByteInStream_File(const int dirfd, const std::string& path) n stats = jau::fs::file_stats(dirfd, path); } if( !stats.exists() || !stats.has_access() ) { - setstate( iostate::failbit ); // Note: conforming with std::ifstream open + setstate_impl( iostate::failbit ); // Note: conforming with std::ifstream open DBG_PRINT("ByteInStream_File::ctor: Error, not an existing or accessible file in %s, %s", stats.to_string().c_str(), to_string().c_str()); } else { - m_has_content_length = stats.is_file(); - m_content_size = stats.size(); - // TODO: Consider O_NONBLOCK, despite being potentially useless on files? + if( stats.has( jau::fs::file_stats::field_t::size ) ) { + m_has_content_length = true; + m_content_size = stats.size(); + } else { + m_has_content_length = false; + m_content_size = 0; + } + // O_NONBLOCK, is useless on files and counter to this class logic const int src_flags = O_RDONLY|O_BINARY|O_NOCTTY; - m_fd = __posix_openat64(dirfd, stats.path().c_str(), src_flags); + if( stats.has_fd() ) { + m_fd = ::dup( stats.fd() ); + } else { + m_fd = __posix_openat64(dirfd, stats.path().c_str(), src_flags); + } if ( 0 > m_fd ) { - setstate( iostate::failbit ); // Note: conforming with std::ifstream open + setstate_impl( iostate::failbit ); // Note: conforming with std::ifstream open DBG_PRINT("ByteInStream_File::ctor: Error while opening %s, %s", stats.to_string().c_str(), to_string().c_str()); } } @@ -404,14 +323,12 @@ void ByteInStream_File::close() noexcept { } std::string ByteInStream_File::to_string() const noexcept { - return "ByteInStream_File[content_length "+jau::to_decstring(m_content_size)+ + return "ByteInStream_File[content_length "+( has_content_size() ? jau::to_decstring(m_content_size) : "n/a" )+ ", consumed "+jau::to_decstring(m_bytes_consumed)+ ", available "+jau::to_decstring(get_available())+ - ", open "+std::to_string(is_open())+ - ", iostate["+jau::io::to_string(m_state)+ - "], eod "+std::to_string( end_of_data() )+ - ", error "+std::to_string( error() )+ - ", "+stats.to_string()+ + ", fd "+std::to_string(m_fd)+ + ", iostate["+jau::io::to_string(rdstate())+ + "], "+stats.to_string()+ "]"; } @@ -446,7 +363,7 @@ void ByteInStream_URL::close() noexcept { DBG_PRINT("ByteInStream_URL: close.X %s, %s", id().c_str(), to_string_int().c_str()); } -bool ByteInStream_URL::check_available(size_t n) NOEXCEPT_BOTAN { +bool ByteInStream_URL::available(size_t n) noexcept { if( async_io_result_t::NONE != m_result ) { // url thread ended, only remaining bytes in buffer available left return m_buffer.size() >= n; @@ -458,17 +375,17 @@ bool ByteInStream_URL::check_available(size_t n) NOEXCEPT_BOTAN { return m_buffer.waitForElements(n, m_timeout) >= n; } -size_t ByteInStream_URL::read(uint8_t out[], size_t length) NOEXCEPT_BOTAN { +size_t ByteInStream_URL::read(void* out, size_t length) noexcept { if( 0 == length || end_of_data() ) { return 0; } - const size_t consumed_bytes = m_buffer.get(out, length, 1); + const size_t consumed_bytes = m_buffer.get(static_cast<uint8_t*>(out), length, 1); m_bytes_consumed += consumed_bytes; // DBG_PRINT("ByteInStream_Feed::read: size %zu/%zu bytes, %s", consumed_bytes, length, to_string_int().c_str() ); return consumed_bytes; } -size_t ByteInStream_URL::peek(uint8_t out[], size_t length, size_t peek_offset) const NOEXCEPT_BOTAN { +size_t ByteInStream_URL::peek(void* out, size_t length, size_t peek_offset) noexcept { (void)out; (void)length; (void)peek_offset; @@ -476,21 +393,26 @@ size_t ByteInStream_URL::peek(uint8_t out[], size_t length, size_t peek_offset) return 0; } -bool ByteInStream_URL::end_of_data() const NOEXCEPT_BOTAN { - return ( async_io_result_t::NONE != m_result && m_buffer.isEmpty() ) || - ( m_has_content_length && m_bytes_consumed >= m_content_size ); +iostate ByteInStream_URL::rdstate() const noexcept { + if ( ( async_io_result_t::NONE != m_result && m_buffer.isEmpty() ) || + ( m_has_content_length && m_bytes_consumed >= m_content_size ) ) + { + setstate_impl( iostate::eofbit ); + } + if( async_io_result_t::FAILED == m_result ) { + setstate_impl( iostate::failbit ); + } + return rdstate_impl(); } std::string ByteInStream_URL::to_string_int() const noexcept { - return m_url+", Url[content_length has "+std::to_string(m_has_content_length.load())+ - ", size "+jau::to_decstring(m_content_size.load())+ + return m_url+", Url[content_length "+( has_content_size() ? jau::to_decstring(m_content_size.load()) : "n/a" )+ ", xfered "+jau::to_decstring(m_total_xfered.load())+ ", result "+std::to_string((int8_t)m_result.load())+ "], consumed "+jau::to_decstring(m_bytes_consumed)+ ", available "+jau::to_decstring(get_available())+ - ", eod "+std::to_string( end_of_data() )+ - ", error "+std::to_string( error() )+ - ", "+m_buffer.toString(); + ", iostate["+jau::io::to_string(rdstate())+ + "], "+m_buffer.toString(); } std::string ByteInStream_URL::to_string() const noexcept { return "ByteInStream_URL["+to_string_int()+"]"; @@ -501,12 +423,12 @@ std::unique_ptr<ByteInStream> jau::io::to_ByteInStream(const std::string& path_o jau::io::uri_tk::protocol_supported(path_or_uri) ) { std::unique_ptr<ByteInStream> res = std::make_unique<ByteInStream_URL>(path_or_uri, timeout); - if( nullptr != res && !res->error() ) { + if( nullptr != res && !res->fail() ) { return res; } } std::unique_ptr<ByteInStream> res = std::make_unique<ByteInStream_File>(path_or_uri); - if( nullptr != res && !res->error() ) { + if( nullptr != res && !res->fail() ) { return res; } return nullptr; @@ -530,7 +452,7 @@ void ByteInStream_Feed::close() noexcept { DBG_PRINT("ByteInStream_Feed: close.X %s, %s", id().c_str(), to_string_int().c_str()); } -bool ByteInStream_Feed::check_available(size_t n) NOEXCEPT_BOTAN { +bool ByteInStream_Feed::available(size_t n) noexcept { if( async_io_result_t::NONE != m_result ) { // feeder completed, only remaining bytes in buffer available left return m_buffer.size() >= n; @@ -542,17 +464,17 @@ bool ByteInStream_Feed::check_available(size_t n) NOEXCEPT_BOTAN { return m_buffer.waitForElements(n, m_timeout) >= n; } -size_t ByteInStream_Feed::read(uint8_t out[], size_t length) NOEXCEPT_BOTAN { +size_t ByteInStream_Feed::read(void* out, size_t length) noexcept { if( 0 == length || end_of_data() ) { return 0; } - const size_t consumed_bytes = m_buffer.get(out, length, 1); - m_bytes_consumed += consumed_bytes; + const size_t got = m_buffer.get(static_cast<uint8_t*>(out), length, 1); + m_bytes_consumed += got; // DBG_PRINT("ByteInStream_Feed::read: size %zu/%zu bytes, %s", consumed_bytes, length, to_string_int().c_str() ); - return consumed_bytes; + return got; } -size_t ByteInStream_Feed::peek(uint8_t out[], size_t length, size_t peek_offset) const NOEXCEPT_BOTAN { +size_t ByteInStream_Feed::peek(void* out, size_t length, size_t peek_offset) noexcept { (void)out; (void)length; (void)peek_offset; @@ -560,9 +482,16 @@ size_t ByteInStream_Feed::peek(uint8_t out[], size_t length, size_t peek_offset) return 0; } -bool ByteInStream_Feed::end_of_data() const NOEXCEPT_BOTAN { - return ( async_io_result_t::NONE != m_result && m_buffer.isEmpty() ) || - ( m_has_content_length && m_bytes_consumed >= m_content_size ); +iostate ByteInStream_Feed::rdstate() const noexcept { + if ( ( async_io_result_t::NONE != m_result && m_buffer.isEmpty() ) || + ( m_has_content_length && m_bytes_consumed >= m_content_size ) ) + { + setstate_impl( iostate::eofbit ); + } + if( async_io_result_t::FAILED == m_result ) { + setstate_impl( iostate::failbit ); + } + return rdstate_impl(); } void ByteInStream_Feed::write(uint8_t in[], size_t length) noexcept { @@ -572,16 +501,19 @@ void ByteInStream_Feed::write(uint8_t in[], size_t length) noexcept { } } +void ByteInStream_Feed::set_eof(const async_io_result_t result) noexcept { + m_result = result; + interruptReader(); +} + std::string ByteInStream_Feed::to_string_int() const noexcept { - return m_id+", ext[content_length has "+std::to_string(m_has_content_length.load())+ - ", size "+jau::to_decstring(m_content_size.load())+ + return m_id+", ext[content_length "+( has_content_size() ? jau::to_decstring(m_content_size.load()) : "n/a" )+ ", xfered "+jau::to_decstring(m_total_xfered.load())+ ", result "+std::to_string((int8_t)m_result.load())+ "], consumed "+std::to_string(m_bytes_consumed)+ ", available "+std::to_string(get_available())+ - ", eod "+std::to_string( end_of_data() )+ - ", error "+std::to_string( error() )+ - ", "+m_buffer.toString(); + ", iostate["+jau::io::to_string(rdstate())+ + "], "+m_buffer.toString(); } std::string ByteInStream_Feed::to_string() const noexcept { @@ -610,19 +542,19 @@ void ByteInStream_Recorder::clear_recording() noexcept { m_rec_offset = 0; } -size_t ByteInStream_Recorder::read(uint8_t out[], size_t length) NOEXCEPT_BOTAN { +size_t ByteInStream_Recorder::read(void* out, size_t length) noexcept { const size_t consumed_bytes = m_parent.read(out, length); m_bytes_consumed += consumed_bytes; if( is_recording() ) { - m_buffer.insert(m_buffer.end(), out, out+consumed_bytes); + uint8_t* out_u8 = static_cast<uint8_t*>(out); + m_buffer.insert(m_buffer.end(), out_u8, out_u8+consumed_bytes); } return consumed_bytes; } std::string ByteInStream_Recorder::to_string() const noexcept { return "ByteInStream_Recorder[parent "+m_parent.id()+", recording[on "+std::to_string(m_is_recording)+ - " offset "+jau::to_decstring(m_rec_offset)+ + " offset "+jau::to_decstring(m_rec_offset)+ "], consumed "+jau::to_decstring(m_bytes_consumed)+ - ", eod "+std::to_string(end_of_data())+ - ", error "+std::to_string(error())+"]"; + ", iostate["+jau::io::to_string(rdstate())+"]]"; } diff --git a/src/io_util.cpp b/src/io_util.cpp index 7b43520..943cc5e 100644 --- a/src/io_util.cpp +++ b/src/io_util.cpp @@ -23,8 +23,6 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include <fstream> -#include <iostream> #include <chrono> // #include <botan_all.h> @@ -49,7 +47,7 @@ uint64_t jau::io::read_file(const std::string& input_file, StreamConsumerFunc consumer_fn) noexcept { if(input_file == "-") { - ByteInStream_istream in(std::cin); + ByteInStream_File in(0); // stdin return read_stream(in, buffer, consumer_fn); } else { ByteInStream_File in(input_file); @@ -63,7 +61,7 @@ uint64_t jau::io::read_stream(ByteInStream& in, uint64_t total = 0; bool has_more; do { - if( in.check_available(1) ) { // at least one byte to stream .. + if( in.available(1) ) { // at least one byte to stream .. buffer.resize(buffer.capacity()); const uint64_t got = in.read(buffer.data(), buffer.capacity()); @@ -89,7 +87,7 @@ uint64_t jau::io::read_stream(ByteInStream& in, static uint64_t _read_buffer(ByteInStream& in, secure_vector<uint8_t>& buffer) noexcept { - if( in.check_available(1) ) { // at least one byte to stream .. + if( in.available(1) ) { // at least one byte to stream .. buffer.resize(buffer.capacity()); const uint64_t got = in.read(buffer.data(), buffer.capacity()); buffer.resize(got); |