Added a first cut of a delimiter-based receive.

This commit is contained in:
chris 2003-11-04 07:48:13 +00:00
parent 83634b0939
commit 84efc4bafc

View File

@ -17,6 +17,11 @@
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <string>
#include <utility>
#include "asio/detail/pop_options.hpp"
#include "asio/detail/bind_handler.hpp"
namespace asio {
@ -68,7 +73,7 @@ size_t recv(Stream& s, void* data, size_t max_length)
* @code template <typename Error>
* void handler(
* const Error& error, // Result of operation (the actual type is dependent
* // on the underlying stream's send operation)
* // on the underlying stream's recv operation)
* size_t bytes_received // Number of bytes received
* ); @endcode
*
@ -102,7 +107,7 @@ void async_recv(Stream& s, void* data, size_t max_length, Handler handler)
* @code template <typename Error>
* void handler(
* const Error& error, // Result of operation (the actual type is dependent
* // on the underlying stream's send operation)
* // on the underlying stream's recv operation)
* size_t bytes_received // Number of bytes received
* ); @endcode
*
@ -138,7 +143,7 @@ void async_recv(Stream& s, void* data, size_t max_length, Handler handler,
* @param total_bytes_recvd An optional output parameter that receives the
* total number of bytes actually received.
*
* @returns The number of bytes received on the last send, or 0 if end-of-file
* @returns The number of bytes received on the last recv, or 0 if end-of-file
* was reached or the connection was closed cleanly.
*
* @note Throws an exception on failure. The type of the exception depends
@ -297,6 +302,419 @@ void async_recv_n(Stream& s, void* data, size_t length, Handler handler,
length, handler, context));
}
/// Read some data from a stream and decode it.
/**
* This function is used to receive data on a stream and decode it in a single
* operation. The function call will block until the decoder function object
* indicates that it has finished.
*
* @param s The stream on which the data is to be received.
*
* @param decoder The decoder function object to be called to decode the
* received data. The function object is assumed to be stateful. That is, it
* may not be given sufficient data in a single invocation to complete
* decoding, and is expected to maintain state so that it may resume decoding
* when the next piece of data is supplied. Copies will be made of the decoder
* function object as required, however with respect to maintaining state it
* can rely on the fact that only an up-to-date copy will be used. The
* equivalent function signature of the handler must be:
* @code std::pair<bool, char*> decoder(
* char* begin, // Pointer to the beginning of the data to be decoded.
* char* end // Pointer to one-past-the-end of the data to be decoded.
* ); @endcode
* The first element of the return value is true if the decoder has finished.
* The second element is a pointer to the beginning of the unused portion of
* the data.
*
* @param total_bytes_recvd An optional output parameter that receives the
* total number of bytes actually received.
*
* @returns The number of bytes received on the last recv, or 0 if end-of-file
* was reached or the connection was closed cleanly.
*
* @note Throws an exception on failure. The type of the exception depends
* on the underlying stream's recv operation.
*/
template <typename Stream, typename Decoder>
size_t recv(Stream& s, Decoder decoder, size_t* total_bytes_recvd = 0)
{
size_t total_recvd = 0;
for (;;)
{
if (s.recv_buffer().empty() && s.fill() == 0)
{
if (total_bytes_recvd)
*total_bytes_recvd = total_recvd;
return 0;
}
std::pair<bool, char*> result =
decoder(s.recv_buffer().begin(), s.recv_buffer().end());
size_t bytes_read = result.second - s.recv_buffer().begin();
s.recv_buffer().pop(bytes_read);
total_recvd += bytes_read;
if (result.first)
{
if (total_bytes_recvd)
*total_bytes_recvd = total_recvd;
return bytes_read;
}
}
}
namespace detail
{
#if defined(_MSC_VER)
static void recv_decoder_optimiser_bug_workaround() {}
#endif // _MSC_VER
template <typename Stream, typename Decoder, typename Handler,
typename Completion_Context>
class recv_decoder_handler
{
public:
recv_decoder_handler(Stream& stream, Decoder decoder, Handler handler,
Completion_Context& context)
: stream_(stream),
decoder_(decoder),
total_recvd_(0),
handler_(handler),
context_(context)
{
}
template <typename Error>
void operator()(const Error& e, size_t bytes_recvd)
{
if (e || bytes_recvd == 0)
{
#if defined(_MSC_VER)
// Unless we put this function call here, the MSVC6 optimiser totally
// removes this function (incorrectly of course) and async_recv calls
// may not work correctly.
recv_decoder_optimiser_bug_workaround();
#endif // _MSC_VER
stream_.demuxer().operation_immediate(detail::bind_handler(handler_, e,
total_recvd_, bytes_recvd), context_, true);
}
else
{
while (!stream_.recv_buffer().empty())
{
std::pair<bool, char*> result =
decoder_(stream_.recv_buffer().begin(),
stream_.recv_buffer().end());
size_t bytes_read = result.second - stream_.recv_buffer().begin();
stream_.recv_buffer().pop(bytes_read);
total_recvd_ += bytes_read;
if (result.first)
{
stream_.demuxer().operation_immediate(detail::bind_handler(
handler_, 0, total_recvd_, bytes_read), context_, true);
return;
}
}
stream_.async_fill(*this);
}
}
private:
Stream& stream_;
Decoder decoder_;
size_t total_recvd_;
Handler handler_;
Completion_Context& context_;
};
} // namespace detail
/// Start an asynchronous receive that will not complete until some data has
/// been fully decoded.
/**
* This function is used to receive data on a stream and decode it in a single
* asynchronous operation. The function call always returns immediately. The
* asynchronous operation will complete only when the decoder indicates that it
* has finished.
*
* @param s The stream on which the data is to be received.
*
* @param decoder The decoder function object to be called to decode the
* received data. The function object is assumed to be stateful. That is, it
* may not be given sufficient data in a single invocation to complete
* decoding, and is expected to maintain state so that it may resume decoding
* when the next piece of data is supplied. Copies will be made of the decoder
* function object as required, however with respect to maintaining state it
* can rely on the fact that only an up-to-date copy will be used. The
* equivalent function signature of the handler must be:
* @code std::pair<bool, char*> decoder(
* char* begin, // Pointer to the beginning of the data to be decoded.
* char* end // Pointer to one-past-the-end of the data to be decoded.
* ); @endcode
* The first element of the return value is true if the decoder has finished.
* The second element is a pointer to the beginning of the unused portion of
* the data.
*
* @param handler The completion handler to be called when the receive
* operation completes. Copies will be made of the handler as required. The
* equivalent function signature of the handler must be:
* @code template <typename Error>
* void handler(
* const Error& error, // Result of operation (the actual type is
* // dependent on the underlying stream's recv
* // operation)
* size_t total_bytes_recvd, // Total number of bytes successfully received
* size_t last_bytes_recvd // Number of bytes received on last recv
* // operation
* ); @endcode
*/
template <typename Stream, typename Decoder, typename Handler>
void async_recv(Stream& s, Decoder decoder, Handler handler)
{
while (!s.recv_buffer().empty())
{
std::pair<bool, char*> result =
decoder(s.recv_buffer().begin(), s.recv_buffer().end());
size_t bytes_read = result.second - s.recv_buffer().begin();
s.recv_buffer().pop(bytes_read);
if (result.first)
{
s.demuxer().operation_immediate(detail::bind_handler(handler, 0,
bytes_read, bytes_read));
return;
}
}
s.async_fill(detail::recv_decoder_handler<Stream, Decoder, Handler,
null_completion_context>(s, decoder, handler,
null_completion_context::instance()));
}
/// Start an asynchronous receive that will not complete until some data has
/// been fully decoded.
/**
* This function is used to receive data on a stream and decode it in a single
* asynchronous operation. The function call always returns immediately. The
* asynchronous operation will complete only when the decoder indicates that it
* has finished.
*
* @param s The stream on which the data is to be received.
*
* @param decoder The decoder function object to be called to decode the
* received data. The function object is assumed to be stateful. That is, it
* may not be given sufficient data in a single invocation to complete
* decoding, and is expected to maintain state so that it may resume decoding
* when the next piece of data is supplied. Copies will be made of the decoder
* function object as required, however with respect to maintaining state it
* can rely on the fact that only an up-to-date copy will be used. The
* equivalent function signature of the handler must be:
* @code std::pair<bool, char*> decoder(
* char* begin, // Pointer to the beginning of the data to be decoded.
* char* end // Pointer to one-past-the-end of the data to be decoded.
* ); @endcode
* The first element of the return value is true if the decoder has finished.
* The second element is a pointer to the beginning of the unused portion of
* the data.
*
* @param handler The completion handler to be called when the receive
* operation completes. Copies will be made of the handler as required. The
* equivalent function signature of the handler must be:
* @code template <typename Error>
* void handler(
* const Error& error, // Result of operation (the actual type is
* // dependent on the underlying stream's recv
* // operation)
* size_t total_bytes_recvd, // Total number of bytes successfully received
* size_t last_bytes_recvd // Number of bytes received on last recv
* // operation
* ); @endcode
*
* @param context The completion context which controls the number of
* concurrent invocations of handlers that may be made. Ownership of the
* object is retained by the caller, which must guarantee that it is valid
* until after the handler has been called.
*/
template <typename Stream, typename Decoder, typename Handler,
typename Completion_Context>
void async_recv(Stream& s, Decoder decoder, Handler handler,
Completion_Context& context)
{
while (!s.recv_buffer().empty())
{
std::pair<bool, char*> result =
decoder(s.recv_buffer().begin(), s.recv_buffer().end());
size_t bytes_read = result.second - s.recv_buffer().begin();
s.recv_buffer().pop(bytes_read);
if (result.first)
{
s.demuxer().operation_immediate(detail::bind_handler(handler, 0,
bytes_read, bytes_read));
return;
}
}
s.async_fill(detail::recv_decoder_handler<Stream, Decoder, Handler,
Completion_Context>(s, decoder, handler, context));
}
namespace detail
{
class recv_until_decoder
{
public:
recv_until_decoder(std::string& data, const std::string& delimiter)
: data_(data),
delimiter_(delimiter),
delimiter_length_(delimiter.length()),
delimiter_pos_(0)
{
data_ = "";
}
std::pair<bool, char*> operator()(char* begin, char* end)
{
char* p = begin;
while (p < end)
{
char next_char = *p++;
if (next_char == delimiter_[delimiter_pos_])
{
if (++delimiter_pos_ == delimiter_length_)
{
data_.append(begin, p - begin);
return std::make_pair(true, p);
}
}
else
{
delimiter_pos_ = 0;
}
}
data_.append(begin, end - begin);
return std::make_pair(false, end);
}
private:
std::string& data_;
std::string delimiter_;
size_t delimiter_length_;
size_t delimiter_pos_;
};
}
/// Read data from the stream until a delimiter is reached.
/**
* This function is used to receive data from the stream into a std::string
* object until a specified delimiter is reached. The function call will block
* until the delimiter is found or an error occurs.
*
* @param s The stream on which the data is to be received.
*
* @param data The std::string object into which the received data will be
* written.
*
* @param delimiter The pattern marking the end of the data to receive.
*
* @param total_bytes_recvd An optional output parameter that receives the
* total number of bytes actually received.
*
* @returns The number of bytes received on the last recv, or 0 if end-of-file
* was reached or the connection was closed cleanly.
*
* @note Throws an exception on failure. The type of the exception depends
* on the underlying stream's recv operation.
*/
template <typename Stream>
size_t recv_until(Stream& s, std::string& data, const std::string& delimiter,
size_t* total_bytes_recvd = 0)
{
return recv(s, detail::recv_until_decoder(data, delimiter),
total_bytes_recvd);
}
/// Start an asynchronous receive that will not complete until the specified
/// delimiter is encountered.
/**
* This function is used to asynchronously receive data from a stream until a
* given delimiter is found. The function call always returns immediately.
*
* @param s The stream on which the data is to be received.
*
* @param data The std:::string object into which the received data will be
* written. Ownership of the object is retained by the caller, which must
* guarantee that it is valid until the handler is called.
*
* @param delimiter The pattern marking the end of the data to receive. Copies
* will be made of the string as required.
*
* @param handler The completion handler to be called when the receive
* operation completes. Copies will be made of the handler as required. The
* equivalent function signature of the handler must be:
* @code template <typename Error>
* void handler(
* const Error& error, // Result of operation (the actual type is
* // dependent on the underlying stream's recv
* // operation)
* size_t total_bytes_recvd, // Total number of bytes successfully received
* size_t last_bytes_recvd // Number of bytes received on last recv
* // operation
* ); @endcode
*/
template <typename Stream, typename Handler>
void async_recv_until(Stream& s, std::string& data,
const std::string& delimiter, Handler handler)
{
async_recv(s, detail::recv_until_decoder(data, delimiter), handler);
}
/// Start an asynchronous receive that will not complete until the specified
/// delimiter is encountered.
/**
* This function is used to asynchronously receive data from a stream until a
* given delimiter is found. The function call always returns immediately.
*
* @param s The stream on which the data is to be received.
*
* @param data The std:::string object into which the received data will be
* written. Ownership of the object is retained by the caller, which must
* guarantee that it is valid until the handler is called.
*
* @param delimiter The pattern marking the end of the data to receive. Copies
* will be made of the string as required.
*
* @param handler The completion handler to be called when the receive
* operation completes. Copies will be made of the handler as required. The
* equivalent function signature of the handler must be:
* @code template <typename Error>
* void handler(
* const Error& error, // Result of operation (the actual type is
* // dependent on the underlying stream's recv
* // operation)
* size_t total_bytes_recvd, // Total number of bytes successfully received
* size_t last_bytes_recvd // Number of bytes received on last recv
* // operation
* ); @endcode
*
* @param context The completion context which controls the number of
* concurrent invocations of handlers that may be made. Ownership of the
* object is retained by the caller, which must guarantee that it is valid
* until after the handler has been called.
*/
template <typename Stream, typename Handler, typename Completion_Context>
void async_recv_until(Stream& s, std::string& data,
const std::string& delimiter, Handler handler, Completion_Context& context)
{
async_recv(s, detail::recv_until_decoder(data, delimiter), handler, context);
}
} // namespace asio
#include "asio/detail/pop_options.hpp"