spectrum2/3rdparty/cpprestsdk/include/cpprest/streams.h
2015-11-19 15:19:14 +01:00

1799 lines
66 KiB
C++

/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Asynchronous I/O: streams API, used for formatted input and output, based on unformatted I/O using stream buffers
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#ifndef _CASA_STREAMS_H
#define _CASA_STREAMS_H
#include "cpprest/astreambuf.h"
#include <iosfwd>
namespace Concurrency { namespace streams
{
template<typename CharType> class basic_ostream;
template<typename CharType> class basic_istream;
namespace details {
template<typename CharType>
class basic_ostream_helper
{
public:
basic_ostream_helper(streams::streambuf<CharType> buffer) : m_buffer(buffer) { }
~basic_ostream_helper() { }
private:
template<typename CharType1> friend class streams::basic_ostream;
concurrency::streams::streambuf<CharType> m_buffer;
};
template<typename CharType>
class basic_istream_helper
{
public:
basic_istream_helper(streams::streambuf<CharType> buffer) : m_buffer(buffer) { }
~basic_istream_helper() { }
private:
template<typename CharType1> friend class streams::basic_istream;
concurrency::streams::streambuf<CharType> m_buffer;
};
template <typename CharType>
struct Value2StringFormatter
{
template <typename T>
static std::basic_string<CharType> format(const T &val)
{
std::basic_ostringstream<CharType> ss;
ss << val;
return ss.str();
}
};
template <>
struct Value2StringFormatter <uint8_t>
{
template <typename T>
static std::basic_string<uint8_t> format(const T &val)
{
std::basic_ostringstream<char> ss;
ss << val;
return reinterpret_cast<const uint8_t *>(ss.str().c_str());
}
static std::basic_string<uint8_t> format(const utf16string &val)
{
return format(utility::conversions::utf16_to_utf8(val));
}
};
static const char *_in_stream_msg = "stream not set up for input of data";
static const char *_in_streambuf_msg = "stream buffer not set up for input of data";
static const char *_out_stream_msg = "stream not set up for output of data";
static const char *_out_streambuf_msg = "stream buffer not set up for output of data";
}
/// <summary>
/// Base interface for all asynchronous output streams.
/// </summary>
template<typename CharType>
class basic_ostream
{
public:
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;
/// <summary>
/// Default constructor
/// </summary>
basic_ostream() {}
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="other">The source object</param>
basic_ostream(const basic_ostream &other) : m_helper(other.m_helper) { }
/// <summary>
/// Assignment operator
/// </summary>
/// <param name="other">The source object</param>
/// <returns>A reference to the stream object that contains the result of the assignment.</returns>
basic_ostream & operator =(const basic_ostream &other) { m_helper = other.m_helper; return *this; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="buffer">A stream buffer.</param>
basic_ostream(streams::streambuf<CharType> buffer) :
m_helper(std::make_shared<details::basic_ostream_helper<CharType>>(buffer))
{
_verify_and_throw(details::_out_streambuf_msg);
}
/// <summary>
/// Close the stream, preventing further write operations.
/// </summary>
pplx::task<void> close() const
{
return is_valid() ?
helper()->m_buffer.close(std::ios_base::out) :
pplx::task_from_result();
}
/// <summary>
/// Close the stream with exception, preventing further write operations.
/// </summary>
/// <param name="eptr">Pointer to the exception.</param>
pplx::task<void> close(std::exception_ptr eptr) const
{
return is_valid() ?
helper()->m_buffer.close(std::ios_base::out, eptr) :
pplx::task_from_result();
}
/// <summary>
/// Put a single character into the stream.
/// </summary>
/// <param name="ch">A character</param>
pplx::task<int_type> write(CharType ch) const
{
pplx::task<int_type> result;
if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result;
return helper()->m_buffer.putc(ch);
}
/// <summary>
/// Write a single value of "blittable" type T into the stream.
/// </summary>
/// <param name="value">A value of type T.</param>
/// <remarks>
/// This is not a replacement for a proper binary serialization solution, but it may
/// form the foundation for one. Writing data bit-wise to a stream is a primitive
/// operation of binary serialization.
/// Currently, no attention is paid to byte order. All data is written in the platform's
/// native byte order, which means little-endian on all platforms that have been tested.
/// This function is only available for streams using a single-byte character size.
/// </remarks>
template<typename T>
CASABLANCA_DEPRECATED("Unsafe API that will be removed in future releases, use one of the other write overloads instead.")
pplx::task<size_t> write(T value) const
{
static_assert(sizeof(CharType) == 1, "binary write is only supported for single-byte streams");
static_assert(std::is_trivial<T>::value, "unsafe to use with non-trivial types");
pplx::task<size_t> result;
if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result;
auto copy = std::make_shared<T>(std::move(value));
return helper()->m_buffer.putn_nocopy((CharType*)copy.get(), sizeof(T)).then([copy](pplx::task<size_t> op) -> size_t { return op.get(); });
}
/// <summary>
/// Write a number of characters from a given stream buffer into the stream.
/// </summary>
/// <param name="source">A source stream buffer.</param>
/// <param name="count">The number of characters to write.</param>
pplx::task<size_t> write(streams::streambuf<CharType> source, size_t count) const
{
pplx::task<size_t> result;
if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result;
if ( !source.can_read() )
return pplx::task_from_exception<size_t>(std::make_exception_ptr(std::runtime_error("source buffer not set up for input of data")));
if (count == 0)
return pplx::task_from_result((size_t)0);
auto buffer = helper()->m_buffer;
auto data = buffer.alloc(count);
if ( data != nullptr )
{
auto post_read =
[buffer](pplx::task<size_t> op)-> pplx::task<size_t>
{
auto b = buffer;
b.commit(op.get());
return op;
};
return source.getn(data, count).then(post_read);
}
else
{
size_t available = 0;
const bool acquired = source.acquire(data, available);
if (available >= count)
{
auto post_write =
[source,data](pplx::task<size_t> op)-> pplx::task<size_t>
{
auto s = source;
s.release(data,op.get());
return op;
};
return buffer.putn_nocopy(data, count).then(post_write);
}
else
{
// Always have to release if acquire returned true.
if(acquired)
{
source.release(data, 0);
}
std::shared_ptr<CharType> buf(new CharType[count], [](CharType *buf) { delete [] buf; });
auto post_write =
[buf](pplx::task<size_t> op)-> pplx::task<size_t>
{
return op;
};
auto post_read =
[buf,post_write,buffer](pplx::task<size_t> op) -> pplx::task<size_t>
{
auto b = buffer;
return b.putn_nocopy(buf.get(), op.get()).then(post_write);
};
return source.getn(buf.get(), count).then(post_read);
}
}
}
/// <summary>
/// Write the specified string to the output stream.
/// </summary>
/// <param name="str">Input string.</param>
pplx::task<size_t> print(const std::basic_string<CharType>& str) const
{
pplx::task<size_t> result;
if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result;
if (str.empty())
{
return pplx::task_from_result<size_t>(0);
}
else
{
auto sharedStr = std::make_shared<std::basic_string<CharType>>(str);
return helper()->m_buffer.putn_nocopy(sharedStr->c_str(), sharedStr->size()).then([sharedStr](size_t size) { return size; });
}
}
/// <summary>
/// Write a value of type <c>T</c> to the output stream.
/// </summary>
/// <typeparam name="T">
/// The data type of the object to be written to the stream
/// </typeparam>
/// <param name="val">Input object.</param>
template<typename T>
pplx::task<size_t> print(const T& val) const
{
pplx::task<size_t> result;
if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result;
// TODO in the future this could be improved to have Value2StringFormatter avoid another unnecessary copy
// by putting the string on the heap before calling the print string overload.
return print(details::Value2StringFormatter<CharType>::format(val));
}
/// <summary>
/// Write a value of type <c>T</c> to the output stream and append a newline character.
/// </summary>
/// <typeparam name="T">
/// The data type of the object to be written to the stream
/// </typeparam>
/// <param name="val">Input object.</param>
template<typename T>
pplx::task<size_t> print_line(const T& val) const
{
pplx::task<size_t> result;
if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result;
auto str = details::Value2StringFormatter<CharType>::format(val);
str.push_back(CharType('\n'));
return print(str);
}
/// <summary>
/// Flush any buffered output data.
/// </summary>
pplx::task<void> flush() const
{
pplx::task<void> result;
if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result;
return helper()->m_buffer.sync();
}
/// <summary>
/// Seeks to the specified write position.
/// </summary>
/// <param name="pos">An offset relative to the beginning of the stream.</param>
/// <returns>The new position in the stream.</returns>
pos_type seek(pos_type pos) const
{
_verify_and_throw(details::_out_stream_msg);
return helper()->m_buffer.seekpos(pos, std::ios_base::out);
}
/// <summary>
/// Seeks to the specified write position.
/// </summary>
/// <param name="off">An offset relative to the beginning, current write position, or the end of the stream.</param>
/// <param name="way">The starting point (beginning, current, end) for the seek.</param>
/// <returns>The new position in the stream.</returns>
pos_type seek(off_type off, std::ios_base::seekdir way) const
{
_verify_and_throw(details::_out_stream_msg);
return helper()->m_buffer.seekoff(off, way, std::ios_base::out);
}
/// <summary>
/// Get the current write position, i.e. the offset from the beginning of the stream.
/// </summary>
/// <returns>The current write position.</returns>
pos_type tell() const
{
_verify_and_throw(details::_out_stream_msg);
return helper()->m_buffer.getpos(std::ios_base::out);
}
/// <summary>
/// <c>can_seek<c/> is used to determine whether the stream supports seeking.
/// </summary>
/// <returns><c>true</c> if the stream supports seeking, <c>false</c> otherwise.</returns>
bool can_seek() const { return is_valid() && m_helper->m_buffer.can_seek(); }
/// <summary>
/// Test whether the stream has been initialized with a valid stream buffer.
/// </summary>
/// <returns><c>true</c> if the stream has been initialized with a valid stream buffer, <c>false</c> otherwise.</returns>
bool is_valid() const { return (m_helper != nullptr) && ((bool)m_helper->m_buffer); }
/// <summary>
/// Test whether the stream has been initialized or not.
/// </summary>
operator bool() const { return is_valid(); }
/// <summary>
/// Test whether the stream is open for writing.
/// </summary>
/// <returns><c>true</c> if the stream is open for writing, <c>false</c> otherwise.</returns>
bool is_open() const { return is_valid() && m_helper->m_buffer.can_write(); }
/// <summary>
/// Get the underlying stream buffer.
/// </summary>
/// <returns>The underlying stream buffer.</returns>
concurrency::streams::streambuf<CharType> streambuf() const
{
return helper()->m_buffer;
}
protected:
void set_helper(std::shared_ptr<details::basic_ostream_helper<CharType>> helper)
{
m_helper = helper;
}
private:
template<typename T>
bool _verify_and_return_task(const char *msg, pplx::task<T> &tsk) const
{
auto buffer = helper()->m_buffer;
if ( !(buffer.exception() == nullptr) )
{
tsk = pplx::task_from_exception<T>(buffer.exception());
return false;
}
if ( !buffer.can_write() )
{
tsk = pplx::task_from_exception<T>(std::make_exception_ptr(std::runtime_error(msg)));
return false;
}
return true;
}
void _verify_and_throw(const char *msg) const
{
auto buffer = helper()->m_buffer;
if ( !(buffer.exception() == nullptr) )
std::rethrow_exception(buffer.exception());
if ( !buffer.can_write() )
throw std::runtime_error(msg);
}
std::shared_ptr<details::basic_ostream_helper<CharType>> helper() const
{
if ( !m_helper )
throw std::logic_error("uninitialized stream object");
return m_helper;
}
std::shared_ptr<details::basic_ostream_helper<CharType>> m_helper;
};
template<typename int_type>
struct _type_parser_integral_traits
{
typedef std::false_type _is_integral;
typedef std::false_type _is_unsigned;
};
#ifdef _WIN32
#define _INT_TRAIT(_t,_low,_high) template<> struct _type_parser_integral_traits<_t>{typedef std::true_type _is_integral;typedef std::false_type _is_unsigned;static const int64_t _min = _low;static const int64_t _max = _high;};
#define _UINT_TRAIT(_t,_low,_high) template<> struct _type_parser_integral_traits<_t>{typedef std::true_type _is_integral;typedef std::true_type _is_unsigned;static const uint64_t _max = _high;};
_INT_TRAIT(char,INT8_MIN,INT8_MAX)
_INT_TRAIT(signed char,INT8_MIN,INT8_MAX)
_INT_TRAIT(short,INT16_MIN,INT16_MAX)
_INT_TRAIT(utf16char,INT16_MIN,INT16_MAX)
_INT_TRAIT(int,INT32_MIN,INT32_MAX)
_INT_TRAIT(long, LONG_MIN, LONG_MAX)
_INT_TRAIT(long long, LLONG_MIN, LLONG_MAX)
_UINT_TRAIT(unsigned char,UINT8_MIN,UINT8_MAX)
_UINT_TRAIT(unsigned short,UINT16_MIN,UINT16_MAX)
_UINT_TRAIT(unsigned int,UINT32_MIN,UINT32_MAX)
_UINT_TRAIT(unsigned long, ULONG_MIN, ULONG_MAX)
_UINT_TRAIT(unsigned long long, ULLONG_MIN, ULLONG_MAX)
#else
#define _INT_TRAIT(_t) template<> struct _type_parser_integral_traits<_t>{typedef std::true_type _is_integral;typedef std::false_type _is_unsigned;static const int64_t _min = std::numeric_limits<_t>::min();static const int64_t _max = (std::numeric_limits<_t>::max)();};
#define _UINT_TRAIT(_t) template<> struct _type_parser_integral_traits<_t>{typedef std::true_type _is_integral;typedef std::true_type _is_unsigned;static const uint64_t _max = (std::numeric_limits<_t>::max)();};
_INT_TRAIT(char)
_INT_TRAIT(signed char)
_INT_TRAIT(short)
_INT_TRAIT(utf16char)
_INT_TRAIT(int)
_INT_TRAIT(long)
_INT_TRAIT(long long)
_UINT_TRAIT(unsigned char)
_UINT_TRAIT(unsigned short)
_UINT_TRAIT(unsigned int)
_UINT_TRAIT(unsigned long)
_UINT_TRAIT(unsigned long long)
#endif
template<typename CharType>
class _type_parser_base
{
public:
typedef typename ::concurrency::streams::char_traits<CharType>::int_type int_type;
_type_parser_base() { }
protected:
// Aid in parsing input: skipping whitespace characters.
static pplx::task<void> _skip_whitespace(streams::streambuf<CharType> buffer);
// Aid in parsing input: peek at a character at a time, call type-specific code to examine, extract value when done.
// <remark>AcceptFunctor should model std::function<bool(std::shared_ptr<X>, int_type)></remark>
// <remark>ExtractFunctor should model std::function<pplx::task<ReturnType>(std::shared_ptr<X>)></remark>
template<typename StateType, typename ReturnType, typename AcceptFunctor, typename ExtractFunctor>
static pplx::task<ReturnType> _parse_input(streams::streambuf<CharType> buffer, AcceptFunctor accept_character, ExtractFunctor extract);
};
/// <summary>
/// Class used to handle asychronous parsing for basic_istream::extract. To support new
/// types create a new template specialization and implement the parse function.
/// </summary>
template<typename CharType, typename T>
class type_parser
{
public:
static pplx::task<T> parse(streams::streambuf<CharType> buffer)
{
typename _type_parser_integral_traits<T>::_is_integral ii;
typename _type_parser_integral_traits<T>::_is_unsigned ui;
return _parse(buffer, ii, ui);
}
private:
static pplx::task<T> _parse(streams::streambuf<CharType> buffer, std::false_type, std::false_type)
{
_parse_floating_point(buffer);
}
static pplx::task<T> _parse(streams::streambuf<CharType>, std::false_type, std::true_type)
{
#ifdef _WIN32
static_assert(false, "type is not supported for extraction from a stream");
#else
throw std::runtime_error("type is not supported for extraction from a stream");
#endif
}
static pplx::task<T> _parse(streams::streambuf<CharType> buffer, std::true_type, std::false_type)
{
return type_parser<CharType,int64_t>::parse(buffer).then(
[] (pplx::task<int64_t> op) -> T
{
int64_t val = op.get();
if ( val <= _type_parser_integral_traits<T>::_max && val >= _type_parser_integral_traits<T>::_min )
return (T)val;
else
throw std::range_error("input out of range for target type");
});
}
static pplx::task<T> _parse(streams::streambuf<CharType> buffer, std::true_type, std::true_type)
{
return type_parser<CharType,uint64_t>::parse(buffer).then(
[] (pplx::task<uint64_t> op) -> T
{
uint64_t val = op.get();
if ( val <= _type_parser_integral_traits<T>::_max )
return (T)val;
else
throw std::range_error("input out of range for target type");
});
}
};
/// <summary>
/// Base interface for all asynchronous input streams.
/// </summary>
template<typename CharType>
class basic_istream
{
public:
typedef char_traits<CharType> traits;
typedef typename char_traits<CharType>::int_type int_type;
typedef typename traits::pos_type pos_type;
typedef typename traits::off_type off_type;
/// <summary>
/// Default constructor
/// </summary>
basic_istream() {}
/// <summary>
/// Constructor
/// </summary>
/// <typeparam name="CharType">
/// The data type of the basic element of the stream.
/// </typeparam>
/// <param name="buffer">A stream buffer.</param>
basic_istream(streams::streambuf<CharType> buffer) : m_helper(std::make_shared<details::basic_istream_helper<CharType>>(buffer))
{
_verify_and_throw(details::_in_streambuf_msg);
}
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="other">The source object</param>
basic_istream(const basic_istream &other) : m_helper(other.m_helper) { }
/// <summary>
/// Assignment operator
/// </summary>
/// <param name="other">The source object</param>
/// <returns>A reference to the stream object that contains the result of the assignment.</returns>
basic_istream & operator =(const basic_istream &other)
{
m_helper = other.m_helper;
return *this;
}
/// <summary>
/// Close the stream, preventing further read operations.
/// </summary>
pplx::task<void> close() const
{
return is_valid() ?
helper()->m_buffer.close(std::ios_base::in) :
pplx::task_from_result();
}
/// <summary>
/// Close the stream with exception, preventing further read operations.
/// </summary>
/// <param name="eptr">Pointer to the exception.</param>
pplx::task<void> close(std::exception_ptr eptr) const
{
return is_valid() ?
m_helper->m_buffer.close(std::ios_base::in, eptr) :
pplx::task_from_result();
}
/// <summary>
/// Tests whether last read cause the stream reach EOF.
/// </summary>
/// <returns>True if the read head has reached the end of the stream, false otherwise.</returns>
bool is_eof() const
{
return is_valid() ? m_helper->m_buffer.is_eof() : false;
}
/// <summary>
/// Get the next character and return it as an int_type. Advance the read position.
/// </summary>
/// <returns>A <c>task</c> that holds the next character as an <c>int_type</c> on successful completion.</returns>
pplx::task<int_type> read() const
{
pplx::task<int_type> result;
if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result;
return helper()->m_buffer.bumpc();
}
/// <summary>
/// Read a single value of "blittable" type T from the stream.
/// </summary>
/// <returns>A value of type T.</returns>
/// <remarks>
/// This is not a replacement for a proper binary serialization solution, but it may
/// form the foundation for one. Reading data bit-wise to a stream is a primitive
/// operation of binary serialization.
/// Currently, no attention is paid to byte order. All data is read in the platform's
/// native byte order, which means little-endian on all platforms that have been tested.
/// This function is only available for streams using a single-byte character size.
/// </remarks>
template<typename T>
CASABLANCA_DEPRECATED("Unsafe API that will be removed in future releases, use one of the other read overloads instead.")
pplx::task<T> read() const
{
static_assert(sizeof(CharType) == 1, "binary read is only supported for single-byte streams");
static_assert(std::is_trivial<T>::value, "unsafe to use with non-trivial types");
pplx::task<T> result;
if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result;
auto copy = std::make_shared<T>();
return helper()->m_buffer.getn((CharType*)copy.get(), sizeof(T)).then([copy](pplx::task<size_t> op) -> T
{
return std::move(*copy);
});
}
/// <summary>
/// Reads up to <c>count</c> characters and place into the provided buffer.
/// </summary>
/// <param name="target">An async stream buffer supporting write operations.</param>
/// <param name="count">The maximum number of characters to read</param>
/// <returns>A <c>task</c> that holds the number of characters read. This number is 0 if the end of the stream is reached.</returns>
pplx::task<size_t> read(streams::streambuf<CharType> target, size_t count) const
{
pplx::task<size_t> result;
if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result;
if ( !target.can_write() )
return pplx::task_from_exception<size_t>(std::make_exception_ptr(std::runtime_error("target not set up for output of data")));
// Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations.
auto buffer = helper()->m_buffer;
auto data = target.alloc(count);
if ( data != nullptr )
{
auto post_read =
[target](pplx::task<size_t> op)-> pplx::task<size_t>
{
auto t = target;
t.commit(op.get());
return op;
};
return buffer.getn(data, count).then(post_read);
}
else
{
size_t available = 0;
const bool acquired = buffer.acquire(data, available);
if (available >= count)
{
auto post_write =
[buffer,data](pplx::task<size_t> op)-> pplx::task<size_t>
{
auto b = buffer;
b.release(data, op.get());
return op;
};
return target.putn_nocopy(data, count).then(post_write);
}
else
{
// Always have to release if acquire returned true.
if(acquired)
{
buffer.release(data, 0);
}
std::shared_ptr<CharType> buf(new CharType[count], [](CharType *buf) { delete [] buf; });
auto post_write =
[buf](pplx::task<size_t> op) -> pplx::task<size_t>
{
return op;
};
auto post_read =
[buf,target,post_write](pplx::task<size_t> op) -> pplx::task<size_t>
{
auto trg = target;
return trg.putn_nocopy(buf.get(), op.get()).then(post_write);
};
return helper()->m_buffer.getn(buf.get(), count).then(post_read);
}
}
}
/// <summary>
/// Get the next character and return it as an int_type. Do not advance the read position.
/// </summary>
/// <returns>A <c>task</c> that holds the character, widened to an integer. This character is EOF when the peek operation fails.</returns>
pplx::task<int_type> peek() const
{
pplx::task<int_type> result;
if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result;
return helper()->m_buffer.getc();
}
/// <summary>
/// Read characters until a delimiter or EOF is found, and place them into the target.
/// Proceed past the delimiter, but don't include it in the target buffer.
/// </summary>
/// <param name="target">An async stream buffer supporting write operations.</param>
/// <param name="delim">The delimiting character to stop the read at.</param>
/// <returns>A <c>task</c> that holds the number of characters read.</returns>
pplx::task<size_t> read_to_delim(streams::streambuf<CharType> target, int_type delim) const
{
pplx::task<size_t> result;
if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result;
if ( !target.can_write() )
return pplx::task_from_exception<size_t>(std::make_exception_ptr(std::runtime_error("target not set up for output of data")));
// Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations.
auto buffer = helper()->m_buffer;
int_type req_async = ::concurrency::streams::char_traits<CharType>::requires_async();
std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>();
auto flush = [=]() mutable
{
return target.putn_nocopy(_locals->outbuf, _locals->write_pos).then([=](size_t wrote) mutable
{
_locals->total += wrote;
_locals->write_pos = 0;
return target.sync();
});
};
auto update = [=](int_type ch) mutable
{
if (ch == ::concurrency::streams::char_traits<CharType>::eof()) return false;
if (ch == delim) return false;
_locals->outbuf[_locals->write_pos] = static_cast<CharType>(ch);
_locals->write_pos += 1;
if (_locals->is_full())
{
// Flushing synchronously because performance is terrible if we
// schedule an empty task. This isn't on a user's thread.
flush().get();
}
return true;
};
auto loop = pplx::details::do_while([=]() mutable -> pplx::task<bool>
{
while (buffer.in_avail() > 0)
{
int_type ch = buffer.sbumpc();
if (ch == req_async)
{
break;
}
if (!update(ch))
{
return pplx::task_from_result(false);
}
}
return buffer.bumpc().then(update);
});
return loop.then([=](bool) mutable
{
return flush().then([=] { return _locals->total; });
});
}
/// <summary>
/// Read until reaching a newline character. The newline is not included in the target.
/// </summary>
/// <param name="target">An asynchronous stream buffer supporting write operations.</param>
/// <returns>A <c>task</c> that holds the number of characters read. This number is 0 if the end of the stream is reached.</returns>
pplx::task<size_t> read_line(streams::streambuf<CharType> target) const
{
pplx::task<size_t> result;
if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result;
if ( !target.can_write() )
return pplx::task_from_exception<size_t>(std::make_exception_ptr(std::runtime_error("target not set up for receiving data")));
// Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations.
concurrency::streams::streambuf<CharType> buffer = helper()->m_buffer;
typename concurrency::streams::char_traits<CharType>::int_type req_async = concurrency::streams::char_traits<CharType>::requires_async();
std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>();
auto flush = [=]() mutable
{
return target.putn_nocopy(_locals->outbuf, _locals->write_pos).then([=](size_t wrote) mutable
{
_locals->total += wrote;
_locals->write_pos = 0;
return target.sync();
});
};
auto update = [=](typename concurrency::streams::char_traits<CharType>::int_type ch) mutable
{
if (ch == concurrency::streams::char_traits<CharType>::eof()) return false;
if (ch == '\n') return false;
if (ch == '\r')
{
_locals->saw_CR = true;
return true;
}
_locals->outbuf[_locals->write_pos] = static_cast<CharType>(ch);
_locals->write_pos += 1;
if (_locals->is_full())
{
// Flushing synchronously because performance is terrible if we
// schedule an empty task. This isn't on a user's thread.
flush().wait();
}
return true;
};
auto update_after_cr = [=] (typename concurrency::streams::char_traits<CharType>::int_type ch) mutable -> pplx::task<bool>
{
if (ch == concurrency::streams::char_traits<CharType>::eof()) return pplx::task_from_result(false);
if (ch == '\n')
{
return buffer.bumpc().then([](
#ifndef _WIN32 // Required by GCC
typename
#endif
concurrency::streams::char_traits<CharType>::int_type) { return false; });
}
return pplx::task_from_result(false);
};
auto loop = pplx::details::do_while([=]() mutable -> pplx::task<bool>
{
while ( buffer.in_avail() > 0 )
{
#ifndef _WIN32 // Required by GCC, because concurrency::streams::char_traits<CharType> is a dependent scope
typename
#endif
concurrency::streams::char_traits<CharType>::int_type ch;
if (_locals->saw_CR)
{
ch = buffer.sgetc();
if (ch == '\n')
buffer.sbumpc();
return pplx::task_from_result(false);
}
ch = buffer.sbumpc();
if (ch == req_async)
break;
if (!update(ch))
{
return pplx::task_from_result(false);
}
}
if (_locals->saw_CR)
{
return buffer.getc().then(update_after_cr);
}
return buffer.bumpc().then(update);
});
return loop.then([=](bool) mutable
{
return flush().then([=] { return _locals->total; });
});
}
/// <summary>
/// Read until reaching the end of the stream.
/// </summary>
/// <param name="target">An asynchronous stream buffer supporting write operations.</param>
/// <returns>The number of characters read.</returns>
pplx::task<size_t> read_to_end(streams::streambuf<CharType> target) const
{
pplx::task<size_t> result;
if ( !_verify_and_return_task("stream not set up for output of data", result) ) return result;
if ( !target.can_write() )
return pplx::task_from_exception<size_t>(std::make_exception_ptr(std::runtime_error("source buffer not set up for input of data")));
auto l_buffer = helper()->m_buffer;
auto l_buf_size = this->buf_size;
std::shared_ptr<_read_helper> l_locals = std::make_shared<_read_helper>();
auto copy_to_target = [l_locals, target, l_buffer, l_buf_size]() mutable -> pplx::task<bool>
{
// We need to capture these, because the object itself may go away
// before we're done processing the data.
//auto locs = _locals;
//auto trg = target;
return l_buffer.getn(l_locals->outbuf, l_buf_size).then([=](size_t rd) mutable -> pplx::task<bool>
{
if (rd == 0)
return pplx::task_from_result(false);
// Must be nested to capture rd
return target.putn_nocopy(l_locals->outbuf, rd).then([target, l_locals, rd](size_t wr) mutable -> pplx::task<bool>
{
l_locals->total += wr;
if (rd != wr)
// Number of bytes written is less than number of bytes received.
throw std::runtime_error("failed to write all bytes");
return target.sync().then([]() { return true; });
});
});
};
auto loop = pplx::details::do_while(copy_to_target);
return loop.then([=](bool) mutable -> size_t
{
return l_locals->total;
});
}
/// <summary>
/// Seeks to the specified write position.
/// </summary>
/// <param name="pos">An offset relative to the beginning of the stream.</param>
/// <returns>The new position in the stream.</returns>
pos_type seek(pos_type pos) const
{
_verify_and_throw(details::_in_stream_msg);
return helper()->m_buffer.seekpos(pos, std::ios_base::in);
}
/// <summary>
/// Seeks to the specified write position.
/// </summary>
/// <param name="off">An offset relative to the beginning, current write position, or the end of the stream.</param>
/// <param name="way">The starting point (beginning, current, end) for the seek.</param>
/// <returns>The new position in the stream.</returns>
pos_type seek(off_type off, std::ios_base::seekdir way) const
{
_verify_and_throw(details::_in_stream_msg);
return helper()->m_buffer.seekoff(off, way, std::ios_base::in);
}
/// <summary>
/// Get the current write position, i.e. the offset from the beginning of the stream.
/// </summary>
/// <returns>The current write position.</returns>
pos_type tell() const
{
_verify_and_throw(details::_in_stream_msg);
return helper()->m_buffer.getpos(std::ios_base::in);
}
/// <summary>
/// <c>can_seek<c/> is used to determine whether the stream supports seeking.
/// </summary>
/// <returns><c>true</c> if the stream supports seeking, <c>false</c> otherwise.</returns>
bool can_seek() const { return is_valid() && m_helper->m_buffer.can_seek(); }
/// <summary>
/// Test whether the stream has been initialized with a valid stream buffer.
/// </summary>
bool is_valid() const { return (m_helper != nullptr) && ((bool)m_helper->m_buffer); }
/// <summary>
/// Test whether the stream has been initialized or not.
/// </summary>
operator bool() const { return is_valid(); }
/// <summary>
/// Test whether the stream is open for writing.
/// </summary>
/// <returns><c>true</c> if the stream is open for writing, <c>false</c> otherwise.</returns>
bool is_open() const { return is_valid() && m_helper->m_buffer.can_read(); }
/// <summary>
/// Get the underlying stream buffer.
/// </summary>
concurrency::streams::streambuf<CharType> streambuf() const
{
return helper()->m_buffer;
}
/// <summary>
/// Read a value of type <c>T</c> from the stream.
/// </summary>
/// <remarks>
/// Supports the C++ primitive types. Can be expanded to additional types
/// by adding template specializations for <c>type_parser</c>.
/// </remarks>
/// <typeparam name="T">
/// The data type of the element to be read from the stream.
/// </typeparam>
/// <returns>A <c>task</c> that holds the element read from the stream.</returns>
template<typename T>
pplx::task<T> extract() const
{
pplx::task<T> result;
if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result;
return type_parser<CharType,T>::parse(helper()->m_buffer);
}
private:
template<typename T>
bool _verify_and_return_task(const char *msg, pplx::task<T> &tsk) const
{
auto buffer = helper()->m_buffer;
if ( !(buffer.exception() == nullptr) )
{
tsk = pplx::task_from_exception<T>(buffer.exception());
return false;
}
if ( !buffer.can_read() )
{
tsk = pplx::task_from_exception<T>(std::make_exception_ptr(std::runtime_error(msg)));
return false;
}
return true;
}
void _verify_and_throw(const char *msg) const
{
auto buffer = helper()->m_buffer;
if ( !(buffer.exception() == nullptr) )
std::rethrow_exception(buffer.exception());
if ( !buffer.can_read() )
throw std::runtime_error(msg);
}
std::shared_ptr<details::basic_istream_helper<CharType>> helper() const
{
if ( !m_helper )
throw std::logic_error("uninitialized stream object");
return m_helper;
}
static const size_t buf_size = 16*1024;
struct _read_helper
{
size_t total;
CharType outbuf[buf_size];
size_t write_pos;
bool saw_CR;
bool is_full() const
{
return write_pos == buf_size;
}
_read_helper() : total(0), write_pos(0), saw_CR(false)
{
}
};
std::shared_ptr<details::basic_istream_helper<CharType>> m_helper;
};
typedef basic_ostream<uint8_t> ostream;
typedef basic_istream<uint8_t> istream;
typedef basic_ostream<utf16char> wostream;
typedef basic_istream<utf16char> wistream;
template<typename CharType>
pplx::task<void> concurrency::streams::_type_parser_base<CharType>::_skip_whitespace(streams::streambuf<CharType> buffer)
{
int_type req_async = concurrency::streams::char_traits<CharType>::requires_async();
auto update = [=] (int_type ch) mutable
{
if (isspace(ch))
{
if (buffer.sbumpc() == req_async)
{
// Synchronously because performance is terrible if we
// schedule an empty task. This isn't on a user's thread.
buffer.nextc().wait();
}
return true;
}
return false;
};
auto loop = pplx::details::do_while([=]() mutable -> pplx::task<bool>
{
while (buffer.in_avail() > 0)
{
int_type ch = buffer.sgetc();
if (ch == req_async)
break;
if (!update(ch))
{
return pplx::task_from_result(false);
}
}
return buffer.getc().then(update);
});
return loop.then([=](pplx::task<bool> op)
{
op.wait();
});
}
template<typename CharType>
template<typename StateType, typename ReturnType, typename AcceptFunctor, typename ExtractFunctor>
pplx::task<ReturnType> concurrency::streams::_type_parser_base<CharType>::_parse_input(
concurrency::streams::streambuf<CharType> buffer,
AcceptFunctor accept_character,
ExtractFunctor extract)
{
std::shared_ptr<StateType> state = std::make_shared<StateType>();
auto update = [=] (pplx::task<int_type> op) -> pplx::task<bool>
{
int_type ch = op.get();
if (ch == concurrency::streams::char_traits<CharType>::eof()) return pplx::task_from_result(false);
bool accptd = accept_character(state, ch);
if (!accptd)
return pplx::task_from_result(false);
// We peeked earlier, so now we must advance the position.
concurrency::streams::streambuf<CharType> buf = buffer;
return buf.bumpc().then([](int_type) { return true; });
};
auto peek_char = [=]() -> pplx::task<bool>
{
concurrency::streams::streambuf<CharType> buf = buffer;
// If task results are immediately available, there's little need to use ".then(),"
// so optimize for prompt values.
auto get_op = buf.getc();
while (get_op.is_done())
{
auto condition = update(get_op);
if (!condition.is_done() || !condition.get())
return condition;
get_op = buf.getc();
}
return get_op.then(update);
};
auto finish =
[=](pplx::task<bool> op) -> pplx::task<ReturnType>
{
op.wait();
pplx::task<ReturnType> result = extract(state);
return result;
};
return _skip_whitespace(buffer).then([=](pplx::task<void> op) -> pplx::task<ReturnType>
{
op.wait();
return pplx::details::do_while(peek_char).then(finish);
});
}
template<typename CharType>
class type_parser<CharType,std::basic_string<CharType>> : public _type_parser_base<CharType>
{
typedef typename _type_parser_base<CharType>::int_type int_type;
public:
static pplx::task<std::string> parse(streams::streambuf<CharType> buffer)
{
return concurrency::streams::_type_parser_base<CharType>::template _parse_input<std::basic_string<CharType>, std::string>(buffer, _accept_char, _extract_result);
}
private:
static bool _accept_char(std::shared_ptr<std::basic_string<CharType>> state, int_type ch)
{
if ( ch == concurrency::streams::char_traits<CharType>::eof() || isspace(ch)) return false;
state->push_back(CharType(ch));
return true;
}
static pplx::task<std::basic_string<CharType>> _extract_result(std::shared_ptr<std::basic_string<CharType>> state)
{
return pplx::task_from_result(*state);
}
};
template<typename CharType>
class type_parser<CharType,int64_t> : public _type_parser_base<CharType>
{
public:
typedef typename _type_parser_base<CharType>::int_type int_type;
static pplx::task<int64_t> parse(streams::streambuf<CharType> buffer)
{
return _type_parser_base<CharType>::template _parse_input<_int64_state, int64_t>(buffer, _accept_char, _extract_result);
}
private:
struct _int64_state
{
_int64_state() : result(0), correct(false), minus(0) {}
int64_t result;
bool correct;
char minus; // 0 -- no sign, 1 -- plus, 2 -- minus
};
static bool _accept_char(std::shared_ptr<_int64_state> state, int_type ch)
{
if ( ch == concurrency::streams::char_traits<CharType>::eof()) return false;
if ( state->minus == 0 )
{
// OK to find a sign.
if ( !::isdigit(ch) && ch != int_type('+') && ch != int_type('-') )
return false;
}
else
{
if ( !::isdigit(ch) ) return false;
}
// At least one digit was found.
state->correct = true;
if ( ch == int_type('+') )
{
state->minus = 1;
}
else if ( ch == int_type('-') )
{
state->minus = 2;
}
else
{
if (state->minus == 0) state->minus = 1;
// Shift the existing value by 10, then add the new value.
bool positive = state->result >= 0;
state->result *= 10;
state->result += int64_t(ch-int_type('0'));
if ( (state->result >= 0) != positive )
{
state->correct = false;
return false;
}
}
return true;
}
static pplx::task<int64_t> _extract_result(std::shared_ptr<_int64_state> state)
{
if (!state->correct)
throw std::range_error("integer value is too large to fit in 64 bits");
int64_t result = (state->minus == 2) ? -state->result : state->result;
return pplx::task_from_result<int64_t>(result);
}
};
template <typename FloatingPoint>
struct _double_state
{
_double_state() : result(0), minus(0), after_comma(0), exponent(false), exponent_number(0), exponent_minus(0), complete(false), p_exception_string() {}
FloatingPoint result;
char minus; // 0 -- no sign, 1 -- plus, 2 -- minus
int after_comma;
bool exponent;
int exponent_number;
char exponent_minus; // 0 -- no sign, 1 -- plus, 2 -- minus
bool complete;
std::string p_exception_string;
};
template <typename FloatingPoint, typename int_type>
static std::string create_exception_message(int_type ch, bool exponent)
{
std::ostringstream os;
os << "Invalid character '" << char(ch) << "'" << (exponent ? " in exponent" : "");
return os.str();
}
template <typename FloatingPoint, typename int_type>
static bool _accept_char(std::shared_ptr<_double_state<FloatingPoint>> state, int_type ch)
{
if ( state->minus == 0 )
{
if ( !::isdigit(ch) && ch != int_type('.') && ch != int_type('+') && ch != int_type('-') )
{
if (!state->complete)
state->p_exception_string = create_exception_message<FloatingPoint, int_type>(ch, false);
return false;
}
}
else
{
if (!state->exponent && !::isdigit(ch) && ch != int_type('.') && ch != int_type('E') && ch != int_type('e'))
{
if (!state->complete)
state->p_exception_string = create_exception_message<FloatingPoint, int_type>(ch, false);
return false;
}
if (state->exponent && !::isdigit(ch) && ch != int_type('+') && ch != int_type('-'))
{
if (!state->complete)
state->p_exception_string = create_exception_message<FloatingPoint, int_type>(ch, true);
return false;
}
}
switch (ch)
{
case int_type('+') :
state->complete = false;
if (state->exponent)
{
if (state->exponent_minus != 0)
{
state->p_exception_string = "The exponent sign already set";
return false;
}
state->exponent_minus = 1;
}
else
{
state->minus = 1;
}
break;
case int_type('-') :
state->complete = false;
if (state->exponent)
{
if (state->exponent_minus != 0)
{
state->p_exception_string = "The exponent sign already set";
return false;
}
state->exponent_minus = 2;
}
else
{
state->minus = 2;
}
break;
case int_type('.') :
state->complete = false;
if (state->after_comma > 0)
return false;
state->after_comma = 1;
break;
case int_type('E') : case int_type('e') :
state->complete = false;
if (state->exponent)
return false;
state->exponent_number = 0;
state->exponent = true;
break;
default:
state->complete = true;
if (!state->exponent)
{
if (state->minus == 0)
state->minus = 1;
state->result *= 10;
state->result += int64_t(ch-int_type('0'));
if (state->after_comma > 0)
state->after_comma++;
}
else
{
if (state->exponent_minus == 0) state->exponent_minus = 1;
state->exponent_number *= 10;
state->exponent_number += int64_t(ch-int_type('0'));
}
}
return true;
}
template <typename FloatingPoint>
static pplx::task<FloatingPoint> _extract_result(std::shared_ptr<_double_state<FloatingPoint>> state)
{
if (state->p_exception_string.length() > 0)
throw std::runtime_error(state->p_exception_string.c_str());
if (!state->complete && state->exponent)
throw std::runtime_error("Incomplete exponent");
FloatingPoint result = static_cast<FloatingPoint>((state->minus == 2) ? -state->result : state->result);
if (state->exponent_minus == 2)
state->exponent_number = 0 - state->exponent_number;
if (state->after_comma > 0)
state->exponent_number -= state->after_comma-1;
if (state->exponent_number >= 0)
{
result *= pow(FloatingPoint(10.0), state->exponent_number);
#pragma push_macro ("max")
#undef max
if (result > std::numeric_limits<FloatingPoint>::max() || result < -std::numeric_limits<FloatingPoint>::max())
throw std::overflow_error("The value is too big");
#pragma pop_macro ("max")
}
else
{
bool is_zero = (result == 0);
result /= pow(FloatingPoint(10.0), -state->exponent_number);
if (!is_zero &&
result > -std::numeric_limits<FloatingPoint>::denorm_min() &&
result < std::numeric_limits<FloatingPoint>::denorm_min())
throw std::underflow_error("The value is too small");
}
return pplx::task_from_result<FloatingPoint>(result);
}
template<typename CharType>
class type_parser<CharType,double> : public _type_parser_base<CharType>
{
public:
typedef typename _type_parser_base<CharType>::int_type int_type;
static pplx::task<double> parse(streams::streambuf<CharType> buffer)
{
return _type_parser_base<CharType>::template _parse_input<_double_state<double>, double>(buffer, _accept_char<double, int_type>, _extract_result<double>);
}
protected:
};
template<typename CharType>
class type_parser<CharType,float> : public _type_parser_base<CharType>
{
public:
typedef typename _type_parser_base<CharType>::int_type int_type;
static pplx::task<float> parse(streams::streambuf<CharType> buffer)
{
return _type_parser_base<CharType>::template _parse_input<_double_state<float>, float>(buffer, _accept_char<float, int_type>, _extract_result<float>);
}
protected:
};
template<typename CharType>
class type_parser<CharType,uint64_t> : public _type_parser_base<CharType>
{
public:
typedef typename _type_parser_base<CharType>::int_type int_type;
static pplx::task<uint64_t> parse(streams::streambuf<CharType> buffer)
{
return _type_parser_base<CharType>::template _parse_input<_uint64_state,uint64_t>(buffer, _accept_char, _extract_result);
}
private:
struct _uint64_state
{
_uint64_state() : result(0), correct(false) {}
uint64_t result;
bool correct;
};
static bool _accept_char(std::shared_ptr<_uint64_state> state, int_type ch)
{
if ( !::isdigit(ch) ) return false;
// At least one digit was found.
state->correct = true;
// Shift the existing value by 10, then add the new value.
state->result *= 10;
state->result += uint64_t(ch-int_type('0'));
return true;
}
static pplx::task<uint64_t> _extract_result(std::shared_ptr<_uint64_state> state)
{
if (!state->correct)
throw std::range_error("integer value is too large to fit in 64 bits");
return pplx::task_from_result(state->result);
}
};
template<typename CharType>
class type_parser<CharType,bool> : public _type_parser_base<CharType>
{
public:
typedef typename _type_parser_base<CharType>::int_type int_type;
static pplx::task<bool> parse(streams::streambuf<CharType> buffer)
{
return _type_parser_base<CharType>::template _parse_input<_bool_state,bool>(buffer, _accept_char, _extract_result);
}
private:
struct _bool_state
{
_bool_state() : state(0) { }
// { 0 -- not started, 1 -- 't', 2 -- 'tr', 3 -- 'tru', 4 -- 'f', 5 -- 'fa', 6 -- 'fal', 7 -- 'fals', 8 -- 'true', 9 -- 'false' }
short state;
};
static bool _accept_char(std::shared_ptr<_bool_state> state, int_type ch)
{
switch (state->state)
{
case 0:
if ( ch == int_type('t') ) state->state = 1;
else if ( ch == int_type('f') ) state->state = 4;
else if ( ch == int_type('1') ) state->state = 8;
else if ( ch == int_type('0') ) state->state = 9;
else return false;
break;
case 1:
if ( ch == int_type('r') ) state->state = 2;
else return false;
break;
case 2:
if ( ch == int_type('u') ) state->state = 3;
else return false;
break;
case 3:
if ( ch == int_type('e') ) state->state = 8;
else return false;
break;
case 4:
if ( ch == int_type('a') ) state->state = 5;
else return false;
break;
case 5:
if ( ch == int_type('l') ) state->state = 6;
else return false;
break;
case 6:
if ( ch == int_type('s') ) state->state = 7;
else return false;
break;
case 7:
if ( ch == int_type('e') ) state->state = 9;
else return false;
break;
case 8:
case 9:
return false;
}
return true;
}
static pplx::task<bool> _extract_result(std::shared_ptr<_bool_state> state)
{
bool correct = (state->state == 8 || state->state == 9);
if (!correct)
{
std::runtime_error exc("cannot parse as Boolean value");
throw exc;
}
return pplx::task_from_result(state->state == 8);
}
};
template<typename CharType>
class type_parser<CharType,signed char> : public _type_parser_base<CharType>
{
typedef typename concurrency::streams::streambuf<CharType>::int_type int_type;
public:
static pplx::task<signed char> parse(streams::streambuf<CharType> buffer)
{
return _type_parser_base<CharType>::_skip_whitespace(buffer).then(
[=](pplx::task<void> op) -> pplx::task<signed char>
{
op.wait();
return type_parser<CharType,signed char>::_get_char(buffer);
});
}
private:
static pplx::task<signed char> _get_char(streams::streambuf<CharType> buffer)
{
concurrency::streams::streambuf<CharType> buf = buffer;
return buf.bumpc().then(
[=](pplx::task<typename concurrency::streams::streambuf<CharType>::int_type> op) -> signed char
{
int_type val = op.get();
if (val == concurrency::streams::char_traits<CharType>::eof())
throw std::runtime_error("reached end-of-stream while constructing a value");
return static_cast<signed char>(val);
});
}
};
template<typename CharType>
class type_parser<CharType,unsigned char> : public _type_parser_base<CharType>
{
typedef typename concurrency::streams::streambuf<CharType>::int_type int_type;
public:
static pplx::task<unsigned char> parse(streams::streambuf<CharType> buffer)
{
return _type_parser_base<CharType>::_skip_whitespace(buffer).then(
[=](pplx::task<void> op) -> pplx::task<unsigned char>
{
op.wait();
return type_parser<CharType,unsigned char>::_get_char(buffer);
});
}
private:
static pplx::task<unsigned char> _get_char(streams::streambuf<CharType> buffer)
{
concurrency::streams::streambuf<CharType> buf = buffer;
return buf.bumpc().then(
[=](pplx::task<typename concurrency::streams::streambuf<CharType>::int_type> op) -> unsigned char
{
int_type val = op.get();
if (val == concurrency::streams::char_traits<CharType>::eof())
throw std::runtime_error("reached end-of-stream while constructing a value");
return static_cast<unsigned char>(val);
});
}
};
template<typename CharType>
class type_parser<CharType,char> : public _type_parser_base<CharType>
{
typedef typename concurrency::streams::streambuf<CharType>::int_type int_type;
public:
static pplx::task<char> parse(streams::streambuf<CharType> buffer)
{
return _type_parser_base<CharType>::_skip_whitespace(buffer).then(
[=](pplx::task<void> op) -> pplx::task<char>
{
op.wait();
return _get_char(buffer);
});
}
private:
static pplx::task<char> _get_char(streams::streambuf<CharType> buffer)
{
concurrency::streams::streambuf<CharType> buf = buffer;
return buf.bumpc().then(
[=](pplx::task<typename concurrency::streams::streambuf<CharType>::int_type> op) -> char
{
int_type val = op.get();
if (val == concurrency::streams::char_traits<CharType>::eof())
throw std::runtime_error("reached end-of-stream while constructing a value");
return char(val);
});
}
};
#ifdef _WIN32
template<>
class type_parser<char,std::basic_string<wchar_t>> : public _type_parser_base<char>
{
public:
static pplx::task<std::wstring> parse(streams::streambuf<char> buffer)
{
return _parse_input<std::basic_string<char>,std::basic_string<wchar_t>>(buffer, _accept_char, _extract_result);
}
private:
static bool _accept_char(const std::shared_ptr<std::basic_string<char>> &state, int_type ch)
{
if ( ch == concurrency::streams::char_traits<char>::eof() || isspace(ch)) return false;
state->push_back(char(ch));
return true;
}
static pplx::task<std::basic_string<wchar_t>> _extract_result(std::shared_ptr<std::basic_string<char>> state)
{
return pplx::task_from_result(utility::conversions::utf8_to_utf16(*state));
}
};
template<>
class type_parser<signed char,std::basic_string<wchar_t>> : public _type_parser_base<signed char>
{
public:
static pplx::task<std::wstring> parse(streams::streambuf<signed char> buffer)
{
return _parse_input<std::basic_string<char>,std::basic_string<wchar_t>>(buffer, _accept_char, _extract_result);
}
private:
static bool _accept_char(const std::shared_ptr<std::basic_string<char>> &state, int_type ch)
{
if ( ch == concurrency::streams::char_traits<char>::eof() || isspace(ch)) return false;
state->push_back(char(ch));
return true;
}
static pplx::task<std::basic_string<wchar_t>> _extract_result(std::shared_ptr<std::basic_string<char>> state)
{
return pplx::task_from_result(utility::conversions::utf8_to_utf16(*state));
}
};
template<>
class type_parser<unsigned char,std::basic_string<wchar_t>> : public _type_parser_base<unsigned char>
{
public:
static pplx::task<std::wstring> parse(streams::streambuf<unsigned char> buffer)
{
return _parse_input<std::basic_string<char>,std::basic_string<wchar_t>>(buffer, _accept_char, _extract_result);
}
private:
static bool _accept_char(const std::shared_ptr<std::basic_string<char>> &state, int_type ch)
{
if ( ch == concurrency::streams::char_traits<char>::eof() || isspace(ch)) return false;
state->push_back(char(ch));
return true;
}
static pplx::task<std::basic_string<wchar_t>> _extract_result(std::shared_ptr<std::basic_string<char>> state)
{
return pplx::task_from_result(utility::conversions::utf8_to_utf16(*state));
}
};
#endif //_WIN32
}}
#endif