/*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Adapter classes for async and STD stream buffers, used to connect std-based and async-based APIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include "cpprest/astreambuf.h" #include "cpprest/streams.h" #include "pplx/pplxtasks.h" #if defined(_WIN32) #pragma warning(push) #pragma warning(disable : 4250) #endif namespace Concurrency { namespace streams { template class stdio_ostream; template class stdio_istream; namespace details { /// /// The basic_stdio_buffer class serves to support interoperability with STL stream buffers. /// Sitting atop a std::streambuf, which does all the I/O, instances of this class may read /// and write data to standard iostreams. The class itself should not be used in application /// code, it is used by the stream definitions farther down in the header file. /// template class basic_stdio_buffer : public streambuf_state_manager<_CharType> { typedef concurrency::streams::char_traits<_CharType> traits; typedef typename traits::int_type int_type; typedef typename traits::pos_type pos_type; typedef typename traits::off_type off_type; /// /// Private constructor /// basic_stdio_buffer(_In_ std::basic_streambuf<_CharType>* streambuf, std::ios_base::openmode mode) : streambuf_state_manager<_CharType>(mode), m_buffer(streambuf) { } public: /// /// Destructor /// virtual ~basic_stdio_buffer() { this->_close_read(); this->_close_write(); } private: // // The functions overridden below here are documented elsewhere. // See astreambuf.h for further information. // virtual bool can_seek() const { return this->is_open(); } virtual bool has_size() const { return false; } virtual size_t in_avail() const { return (size_t)m_buffer->in_avail(); } virtual size_t buffer_size(std::ios_base::openmode) const { return 0; } virtual void set_buffer_size(size_t, std::ios_base::openmode) { return; } virtual pplx::task _sync() { return pplx::task_from_result(m_buffer->pubsync() == 0); } virtual pplx::task _putc(_CharType ch) { return pplx::task_from_result(m_buffer->sputc(ch)); } virtual pplx::task _putn(const _CharType* ptr, size_t size) { return pplx::task_from_result((size_t)m_buffer->sputn(ptr, size)); } size_t _sgetn(_Out_writes_(size) _CharType* ptr, _In_ size_t size) const { return m_buffer->sgetn(ptr, size); } virtual size_t _scopy(_Out_writes_(size) _CharType*, _In_ size_t size) { (void)(size); return (size_t)-1; } virtual pplx::task _getn(_Out_writes_(size) _CharType* ptr, _In_ size_t size) { return pplx::task_from_result((size_t)m_buffer->sgetn(ptr, size)); } virtual int_type _sbumpc() { return m_buffer->sbumpc(); } virtual int_type _sgetc() { return m_buffer->sgetc(); } virtual pplx::task _bumpc() { return pplx::task_from_result(m_buffer->sbumpc()); } virtual pplx::task _getc() { return pplx::task_from_result(m_buffer->sgetc()); } virtual pplx::task _nextc() { return pplx::task_from_result(m_buffer->snextc()); } virtual pplx::task _ungetc() { return pplx::task_from_result(m_buffer->sungetc()); } virtual pos_type getpos(std::ios_base::openmode mode) const { return m_buffer->pubseekoff(0, std::ios_base::cur, mode); } virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode) { return m_buffer->pubseekpos(pos, mode); } virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode mode) { return m_buffer->pubseekoff(off, dir, mode); } virtual _CharType* _alloc(size_t) { return nullptr; } virtual void _commit(size_t) {} virtual bool acquire(_CharType*&, size_t&) { return false; } virtual void release(_CharType*, size_t) {} template friend class concurrency::streams::stdio_ostream; template friend class concurrency::streams::stdio_istream; std::basic_streambuf<_CharType>* m_buffer; }; } // namespace details /// /// stdio_ostream represents an async ostream derived from a standard synchronous stream, as /// defined by the "std" namespace. It is constructed from a reference to a standard stream, which /// must be valid for the lifetime of the asynchronous stream. /// /// /// The data type of the basic element of the stdio_ostream. /// /// /// Since std streams are not reference-counted, great care must be taken by an application to make /// sure that the std stream does not get destroyed until all uses of the asynchronous stream are /// done and have been serviced. /// template class stdio_ostream : public basic_ostream { public: /// /// Constructor /// /// /// The data type of the basic element of the source output stream. /// /// The synchronous stream that this is using for its I/O template stdio_ostream(std::basic_ostream& stream) : basic_ostream( streams::streambuf(std::shared_ptr>( new details::basic_stdio_buffer(stream.rdbuf(), std::ios_base::out)))) { } /// /// Copy constructor /// /// The source object stdio_ostream(const stdio_ostream& other) : basic_ostream(other) {} /// /// Assignment operator /// /// The source object /// A reference to the output stream object that contains the result of the assignment. stdio_ostream& operator=(const stdio_ostream& other) { basic_ostream::operator=(other); return *this; } }; /// /// stdio_istream represents an async istream derived from a standard synchronous stream, as /// defined by the "std" namespace. It is constructed from a reference to a standard stream, which /// must be valid for the lifetime of the asynchronous stream. /// /// /// The data type of the basic element of the stdio_istream. /// /// /// Since std streams are not reference-counted, great care must be taken by an application to make /// sure that the std stream does not get destroyed until all uses of the asynchronous stream are /// done and have been serviced. /// template class stdio_istream : public basic_istream { public: /// /// Constructor /// /// /// The data type of the basic element of the source istream /// /// The synchronous stream that this is using for its I/O template stdio_istream(std::basic_istream& stream) : basic_istream( streams::streambuf(std::shared_ptr>( new details::basic_stdio_buffer(stream.rdbuf(), std::ios_base::in)))) { } /// /// Copy constructor /// /// The source object stdio_istream(const stdio_istream& other) : basic_istream(other) {} /// /// Assignment operator /// /// The source object /// A reference to the input stream object that contains the result of the assignment. stdio_istream& operator=(const stdio_istream& other) { basic_istream::operator=(other); return *this; } }; namespace details { /// /// IO streams stream buffer implementation used to interface with an async streambuffer underneath. /// Used for implementing the standard synchronous streams that provide interop between std:: and concurrency::streams:: /// template class basic_async_streambuf : public std::basic_streambuf { public: typedef concurrency::streams::char_traits traits; typedef typename traits::int_type int_type; typedef typename traits::pos_type pos_type; typedef typename traits::off_type off_type; basic_async_streambuf(const streams::streambuf& async_buf) : m_buffer(async_buf) {} protected: // // The following are the functions in std::basic_streambuf that we need to override. // /// /// Writes one byte to the stream buffer. /// int_type overflow(int_type ch) { try { return m_buffer.putc(CharType(ch)).get(); } catch (...) { return traits::eof(); } } /// /// Gets one byte from the stream buffer without moving the read position. /// int_type underflow() { try { return m_buffer.getc().get(); } catch (...) { return traits::eof(); } } /// /// Gets one byte from the stream buffer and move the read position one character. /// int_type uflow() { try { return m_buffer.bumpc().get(); } catch (...) { return traits::eof(); } } /// /// Gets a number of characters from the buffer and place it into the provided memory block. /// std::streamsize xsgetn(_Out_writes_(count) CharType* ptr, _In_ std::streamsize count) { size_t cnt = size_t(count); size_t read_so_far = 0; try { while (read_so_far < cnt) { size_t rd = m_buffer.getn(ptr + read_so_far, cnt - read_so_far).get(); read_so_far += rd; if (rd == 0) break; } return read_so_far; } catch (...) { return 0; } } /// /// Writes a given number of characters from the provided block into the stream buffer. /// std::streamsize xsputn(const CharType* ptr, std::streamsize count) { try { return m_buffer.putn_nocopy(ptr, static_cast(count)).get(); } catch (...) { return 0; } } /// /// Synchronizes with the underlying medium. /// int sync() // must be int as per std::basic_streambuf { try { m_buffer.sync().wait(); } catch (...) { } return 0; } /// /// Seeks to the given offset relative to the beginning, end, or current position. /// pos_type seekoff(off_type offset, std::ios_base::seekdir dir, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { try { if (dir == std::ios_base::cur && offset == 0) // Special case for getting the current position. return m_buffer.getpos(mode); return m_buffer.seekoff(offset, dir, mode); } catch (...) { return (pos_type(-1)); } } /// /// Seeks to the given offset relative to the beginning of the stream. /// pos_type seekpos(pos_type pos, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { try { return m_buffer.seekpos(pos, mode); } catch (...) { return (pos_type(-1)); } } private: concurrency::streams::streambuf m_buffer; }; } // namespace details /// /// A concrete STL ostream which relies on an asynchronous stream for its I/O. /// /// /// The data type of the basic element of the stream. /// template class async_ostream : public std::basic_ostream { public: /// /// Constructor /// /// /// The data type of the basic element of the source ostream. /// /// The asynchronous stream whose stream buffer should be used for I/O template async_ostream(const streams::basic_ostream& astream) : std::basic_ostream(&m_strbuf), m_strbuf(astream.streambuf()) { } /// /// Constructor /// /// /// The data type of the basic element of the source streambuf. /// /// The asynchronous stream buffer to use for I/O template async_ostream(const streams::streambuf& strbuf) : std::basic_ostream(&m_strbuf), m_strbuf(strbuf) { } private: details::basic_async_streambuf m_strbuf; }; /// /// A concrete STL istream which relies on an asynchronous stream for its I/O. /// /// /// The data type of the basic element of the stream. /// template class async_istream : public std::basic_istream { public: /// /// Constructor /// /// /// The data type of the basic element of the source istream. /// /// The asynchronous stream whose stream buffer should be used for I/O template async_istream(const streams::basic_istream& astream) : std::basic_istream(&m_strbuf), m_strbuf(astream.streambuf()) { } /// /// Constructor /// /// /// The data type of the basic element of the source streambuf. /// /// The asynchronous stream buffer to use for I/O template async_istream(const streams::streambuf& strbuf) : std::basic_istream(&m_strbuf), m_strbuf(strbuf) { } private: details::basic_async_streambuf m_strbuf; }; /// /// A concrete STL istream which relies on an asynchronous stream buffer for its I/O. /// /// /// The data type of the basic element of the stream. /// template class async_iostream : public std::basic_iostream { public: /// /// Constructor /// /// The asynchronous stream buffer to use for I/O async_iostream(const streams::streambuf& strbuf) : std::basic_iostream(&m_strbuf), m_strbuf(strbuf) { } private: details::basic_async_streambuf m_strbuf; }; #if defined(__cplusplus_winrt) /// /// Static class containing factory functions for WinRT streams implemented on top of Casablanca async streams. /// /// WinRT streams are defined in terms of single-byte characters only. class winrt_stream { public: /// /// Creates a WinRT IInputStream reference from an asynchronous stream buffer. /// /// A stream buffer based on a single-byte character. /// A reference to a WinRT IInputStream. /// /// The stream buffer passed in must allow reading. /// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For /// example, using a producer_consumer_buffer, a Casablanca-based caller can pass data to a WinRT component. /// _ASYNCRTIMP static Windows::Storage::Streams::IInputStream ^ __cdecl create_input_stream(const concurrency::streams::streambuf& buffer); /// /// Creates a WinRT IOutputStream reference from an asynchronous stream buffer. /// /// A stream buffer based on a single-byte character. /// A reference to a WinRT IOutputStream. /// /// The stream buffer passed in must allow writing. /// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For /// example, using a producer_consumer_buffer, a Casablanca-based caller can retrieve data from a WinRT /// component. /// _ASYNCRTIMP static Windows::Storage::Streams::IOutputStream ^ __cdecl create_output_stream(const concurrency::streams::streambuf& buffer); /// /// Creates a WinRT IRandomAccessStream reference from an asynchronous input stream. /// /// A stream based on a single-byte character. /// A reference to a WinRT IRandomAccessStream. /// /// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For /// example, using a producer_consumer_buffer, a Casablanca-based caller can pass data to and retrieve data /// from a WinRT component. /// _ASYNCRTIMP static Windows::Storage::Streams::IRandomAccessStream ^ __cdecl create_random_access_stream(const concurrency::streams::streambuf& buffer); }; #endif } // namespace streams } // namespace Concurrency #if defined(_WIN32) #pragma warning(pop) #endif