Boost logo

Boost-Commit :

From: chris_at_[hidden]
Date: 2008-06-18 09:03:47


Author: chris_kohlhoff
Date: 2008-06-18 09:03:46 EDT (Wed, 18 Jun 2008)
New Revision: 46475
URL: http://svn.boost.org/trac/boost/changeset/46475

Log:
Add new overloads for read_until and async_read_until that invoke a
user-defined function object to determine when a match has been found.

Text files modified:
   trunk/boost/asio/impl/read_until.ipp | 374 ++++++++++++++++++++++++++++++++-------
   trunk/boost/asio/read_until.hpp | 306 ++++++++++++++++++++++++++++++++
   trunk/libs/asio/test/read_until.cpp | 179 +++++++++++++++++++
   3 files changed, 792 insertions(+), 67 deletions(-)

Modified: trunk/boost/asio/impl/read_until.ipp
==============================================================================
--- trunk/boost/asio/impl/read_until.ipp (original)
+++ trunk/boost/asio/impl/read_until.ipp 2008-06-18 09:03:46 EDT (Wed, 18 Jun 2008)
@@ -25,8 +25,8 @@
 #include <boost/asio/detail/pop_options.hpp>
 
 #include <boost/asio/buffer.hpp>
+#include <boost/asio/buffers_iterator.hpp>
 #include <boost/asio/detail/bind_handler.hpp>
-#include <boost/asio/detail/const_buffers_iterator.hpp>
 #include <boost/asio/detail/handler_alloc_helpers.hpp>
 #include <boost/asio/detail/handler_invoke_helpers.hpp>
 #include <boost/asio/detail/throw_error.hpp>
@@ -55,24 +55,24 @@
     // Determine the range of the data to be searched.
     typedef typename boost::asio::basic_streambuf<
       Allocator>::const_buffers_type const_buffers_type;
- typedef boost::asio::detail::const_buffers_iterator<
- const_buffers_type> iterator;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
     const_buffers_type buffers = b.data();
- iterator begin(buffers, next_search_start);
- iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + next_search_start;
+ iterator end = iterator::end(buffers);
 
     // Look for a match.
- iterator iter = std::find(begin, end, delim);
+ iterator iter = std::find(start, end, delim);
     if (iter != end)
     {
       // Found a match. We're done.
       ec = boost::system::error_code();
- return iter.position() + 1;
+ return iter - begin + 1;
     }
     else
     {
       // No match. Next search can start with the new data.
- next_search_start = end.position();
+ next_search_start = end - begin;
     }
 
     // Check if buffer is full.
@@ -147,33 +147,33 @@
     // Determine the range of the data to be searched.
     typedef typename boost::asio::basic_streambuf<
       Allocator>::const_buffers_type const_buffers_type;
- typedef boost::asio::detail::const_buffers_iterator<
- const_buffers_type> iterator;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
     const_buffers_type buffers = b.data();
- iterator begin(buffers, next_search_start);
- iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + next_search_start;
+ iterator end = iterator::end(buffers);
 
     // Look for a match.
     std::pair<iterator, bool> result = boost::asio::detail::partial_search(
- begin, end, delim.begin(), delim.end());
+ start, end, delim.begin(), delim.end());
     if (result.first != end)
     {
       if (result.second)
       {
         // Full match. We're done.
         ec = boost::system::error_code();
- return result.first.position() + delim.length();
+ return result.first - begin + delim.length();
       }
       else
       {
         // Partial match. Next search needs to start from beginning of match.
- next_search_start = result.first.position();
+ next_search_start = result.first - begin;
       }
     }
     else
     {
       // No match. Next search can start with the new data.
- next_search_start = end.position();
+ next_search_start = end - begin;
     }
 
     // Check if buffer is full.
@@ -213,33 +213,33 @@
     // Determine the range of the data to be searched.
     typedef typename boost::asio::basic_streambuf<
       Allocator>::const_buffers_type const_buffers_type;
- typedef boost::asio::detail::const_buffers_iterator<
- const_buffers_type> iterator;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
     const_buffers_type buffers = b.data();
- iterator begin(buffers, next_search_start);
- iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + next_search_start;
+ iterator end = iterator::end(buffers);
 
     // Look for a match.
     boost::match_results<iterator> match_results;
- if (boost::regex_search(begin, end, match_results, expr,
+ if (boost::regex_search(start, end, match_results, expr,
           boost::match_default | boost::match_partial))
     {
       if (match_results[0].matched)
       {
         // Full match. We're done.
         ec = boost::system::error_code();
- return match_results[0].second.position();
+ return match_results[0].second - begin;
       }
       else
       {
         // Partial match. Next search needs to start from beginning of match.
- next_search_start = match_results[0].first.position();
+ next_search_start = match_results[0].first - begin;
       }
     }
     else
     {
       // No match. Next search can start with the new data.
- next_search_start = end.position();
+ next_search_start = end - begin;
     }
 
     // Check if buffer is full.
@@ -258,6 +258,73 @@
   }
 }
 
+template <typename SyncReadStream, typename Allocator, typename MatchCondition>
+std::size_t read_until(SyncReadStream& s,
+ boost::asio::basic_streambuf<Allocator>& b,
+ MatchCondition match_condition, boost::system::error_code& ec,
+ typename boost::enable_if<is_match_condition<MatchCondition> >::type*)
+{
+ std::size_t next_search_start = 0;
+ for (;;)
+ {
+ // Determine the range of the data to be searched.
+ typedef typename boost::asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + next_search_start;
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ std::pair<iterator, bool> result = match_condition(start, end);
+ if (result.first != end)
+ {
+ if (result.second)
+ {
+ // Full match. We're done.
+ ec = boost::system::error_code();
+ return result.first - begin;
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ next_search_start = result.first - begin;
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ next_search_start = end - begin;
+ }
+
+ // Check if buffer is full.
+ if (b.size() == b.max_size())
+ {
+ ec = error::not_found;
+ return 0;
+ }
+
+ // Need more data.
+ std::size_t bytes_available =
+ std::min<std::size_t>(512, b.max_size() - b.size());
+ b.commit(s.read_some(b.prepare(bytes_available), ec));
+ if (ec)
+ return 0;
+ }
+}
+
+template <typename SyncReadStream, typename Allocator, typename MatchCondition>
+inline std::size_t read_until(SyncReadStream& s,
+ boost::asio::basic_streambuf<Allocator>& b, MatchCondition match_condition,
+ typename boost::enable_if<is_match_condition<MatchCondition> >::type*)
+{
+ boost::system::error_code ec;
+ std::size_t bytes_transferred = read_until(s, b, match_condition, ec);
+ boost::asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
 namespace detail
 {
   template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
@@ -292,18 +359,18 @@
       // Determine the range of the data to be searched.
       typedef typename boost::asio::basic_streambuf<
         Allocator>::const_buffers_type const_buffers_type;
- typedef boost::asio::detail::const_buffers_iterator<
- const_buffers_type> iterator;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
       const_buffers_type buffers = streambuf_.data();
- iterator begin(buffers, next_search_start_);
- iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + next_search_start_;
+ iterator end = iterator::end(buffers);
 
       // Look for a match.
- iterator iter = std::find(begin, end, delim_);
+ iterator iter = std::find(start, end, delim_);
       if (iter != end)
       {
         // Found a match. We're done.
- std::size_t bytes = iter.position() + 1;
+ std::size_t bytes = iter - begin + 1;
         handler_(ec, bytes);
         return;
       }
@@ -318,7 +385,7 @@
       }
 
       // Next search can start with the new data.
- next_search_start_ = end.position();
+ next_search_start_ = end - begin;
 
       // Start a new asynchronous read operation to obtain more data.
       std::size_t bytes_available =
@@ -370,11 +437,10 @@
   // Determine the range of the data to be searched.
   typedef typename boost::asio::basic_streambuf<
     Allocator>::const_buffers_type const_buffers_type;
- typedef boost::asio::detail::const_buffers_iterator<
- const_buffers_type> iterator;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
   const_buffers_type buffers = b.data();
- iterator begin(buffers, 0);
- iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+ iterator begin = iterator::begin(buffers);
+ iterator end = iterator::end(buffers);
 
   // Look for a match.
   iterator iter = std::find(begin, end, delim);
@@ -382,7 +448,7 @@
   {
     // Found a match. We're done.
     boost::system::error_code ec;
- std::size_t bytes = iter.position() + 1;
+ std::size_t bytes = iter - begin + 1;
     s.io_service().post(detail::bind_handler(handler, ec, bytes));
     return;
   }
@@ -400,7 +466,7 @@
     std::min<std::size_t>(512, b.max_size() - b.size());
   s.async_read_some(b.prepare(bytes_available),
       detail::read_until_delim_handler<AsyncReadStream, Allocator, ReadHandler>(
- s, b, delim, end.position(), handler));
+ s, b, delim, end - begin, handler));
 }
 
 namespace detail
@@ -438,34 +504,34 @@
       // Determine the range of the data to be searched.
       typedef typename boost::asio::basic_streambuf<
         Allocator>::const_buffers_type const_buffers_type;
- typedef boost::asio::detail::const_buffers_iterator<
- const_buffers_type> iterator;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
       const_buffers_type buffers = streambuf_.data();
- iterator begin(buffers, next_search_start_);
- iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + next_search_start_;
+ iterator end = iterator::end(buffers);
 
       // Look for a match.
       std::pair<iterator, bool> result = boost::asio::detail::partial_search(
- begin, end, delim_.begin(), delim_.end());
+ start, end, delim_.begin(), delim_.end());
       if (result.first != end)
       {
         if (result.second)
         {
           // Full match. We're done.
- std::size_t bytes = result.first.position() + delim_.length();
+ std::size_t bytes = result.first - begin + delim_.length();
           handler_(ec, bytes);
           return;
         }
         else
         {
           // Partial match. Next search needs to start from beginning of match.
- next_search_start_ = result.first.position();
+ next_search_start_ = result.first - begin;
         }
       }
       else
       {
         // No match. Next search can start with the new data.
- next_search_start_ = end.position();
+ next_search_start_ = end - begin;
       }
 
       // Check if buffer is full.
@@ -528,11 +594,10 @@
   // Determine the range of the data to be searched.
   typedef typename boost::asio::basic_streambuf<
     Allocator>::const_buffers_type const_buffers_type;
- typedef boost::asio::detail::const_buffers_iterator<
- const_buffers_type> iterator;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
   const_buffers_type buffers = b.data();
- iterator begin(buffers, 0);
- iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+ iterator begin = iterator::begin(buffers);
+ iterator end = iterator::end(buffers);
 
   // Look for a match.
   std::size_t next_search_start;
@@ -544,20 +609,20 @@
     {
       // Full match. We're done.
       boost::system::error_code ec;
- std::size_t bytes = result.first.position() + delim.length();
+ std::size_t bytes = result.first - begin + delim.length();
       s.io_service().post(detail::bind_handler(handler, ec, bytes));
       return;
     }
     else
     {
       // Partial match. Next search needs to start from beginning of match.
- next_search_start = result.first.position();
+ next_search_start = result.first - begin;
     }
   }
   else
   {
     // No match. Next search can start with the new data.
- next_search_start = end.position();
+ next_search_start = end - begin;
   }
 
   // Check if buffer is full.
@@ -612,34 +677,34 @@
       // Determine the range of the data to be searched.
       typedef typename boost::asio::basic_streambuf<
         Allocator>::const_buffers_type const_buffers_type;
- typedef boost::asio::detail::const_buffers_iterator<
- const_buffers_type> iterator;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
       const_buffers_type buffers = streambuf_.data();
- iterator begin(buffers, next_search_start_);
- iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + next_search_start_;
+ iterator end = iterator::end(buffers);
 
       // Look for a match.
       boost::match_results<iterator> match_results;
- if (boost::regex_search(begin, end, match_results, expr_,
+ if (boost::regex_search(start, end, match_results, expr_,
             boost::match_default | boost::match_partial))
       {
         if (match_results[0].matched)
         {
           // Full match. We're done.
- std::size_t bytes = match_results[0].second.position();
+ std::size_t bytes = match_results[0].second - begin;
           handler_(ec, bytes);
           return;
         }
         else
         {
           // Partial match. Next search needs to start from beginning of match.
- next_search_start_ = match_results[0].first.position();
+ next_search_start_ = match_results[0].first - begin;
         }
       }
       else
       {
         // No match. Next search can start with the new data.
- next_search_start_ = end.position();
+ next_search_start_ = end - begin;
       }
 
       // Check if buffer is full.
@@ -702,11 +767,10 @@
   // Determine the range of the data to be searched.
   typedef typename boost::asio::basic_streambuf<
     Allocator>::const_buffers_type const_buffers_type;
- typedef boost::asio::detail::const_buffers_iterator<
- const_buffers_type> iterator;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
   const_buffers_type buffers = b.data();
- iterator begin(buffers, 0);
- iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+ iterator begin = iterator::begin(buffers);
+ iterator end = iterator::end(buffers);
 
   // Look for a match.
   std::size_t next_search_start;
@@ -718,20 +782,20 @@
     {
       // Full match. We're done.
       boost::system::error_code ec;
- std::size_t bytes = match_results[0].second.position();
+ std::size_t bytes = match_results[0].second - begin;
       s.io_service().post(detail::bind_handler(handler, ec, bytes));
       return;
     }
     else
     {
       // Partial match. Next search needs to start from beginning of match.
- next_search_start = match_results[0].first.position();
+ next_search_start = match_results[0].first - begin;
     }
   }
   else
   {
     // No match. Next search can start with the new data.
- next_search_start = end.position();
+ next_search_start = end - begin;
   }
 
   // Check if buffer is full.
@@ -750,6 +814,182 @@
         s, b, expr, next_search_start, handler));
 }
 
+namespace detail
+{
+ template <typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+ class read_until_match_handler
+ {
+ public:
+ read_until_match_handler(AsyncReadStream& stream,
+ boost::asio::basic_streambuf<Allocator>& streambuf,
+ MatchCondition match_condition, std::size_t next_search_start,
+ ReadHandler handler)
+ : stream_(stream),
+ streambuf_(streambuf),
+ match_condition_(match_condition),
+ next_search_start_(next_search_start),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const boost::system::error_code& ec,
+ std::size_t bytes_transferred)
+ {
+ // Check for errors.
+ if (ec)
+ {
+ std::size_t bytes = 0;
+ handler_(ec, bytes);
+ return;
+ }
+
+ // Commit received data to streambuf's get area.
+ streambuf_.commit(bytes_transferred);
+
+ // Determine the range of the data to be searched.
+ typedef typename boost::asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = streambuf_.data();
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + next_search_start_;
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ std::pair<iterator, bool> result = match_condition_(start, end);
+ if (result.first != end)
+ {
+ if (result.second)
+ {
+ // Full match. We're done.
+ std::size_t bytes = result.first - begin;
+ handler_(ec, bytes);
+ return;
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ next_search_start_ = result.first - begin;
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ next_search_start_ = end - begin;
+ }
+
+ // Check if buffer is full.
+ if (streambuf_.size() == streambuf_.max_size())
+ {
+ std::size_t bytes = 0;
+ boost::system::error_code ec(error::not_found);
+ handler_(ec, bytes);
+ return;
+ }
+
+ // Start a new asynchronous read operation to obtain more data.
+ std::size_t bytes_available =
+ std::min<std::size_t>(512, streambuf_.max_size() - streambuf_.size());
+ stream_.async_read_some(streambuf_.prepare(bytes_available), *this);
+ }
+
+ //private:
+ AsyncReadStream& stream_;
+ boost::asio::basic_streambuf<Allocator>& streambuf_;
+ MatchCondition match_condition_;
+ std::size_t next_search_start_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_until_match_handler<AsyncReadStream,
+ Allocator, MatchCondition, ReadHandler>* this_handler)
+ {
+ return boost_asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+ }
+
+ template <typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_until_match_handler<AsyncReadStream,
+ Allocator, MatchCondition, ReadHandler>* this_handler)
+ {
+ boost_asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+ inline void asio_handler_invoke(const Function& function,
+ read_until_match_handler<AsyncReadStream,
+ Allocator, MatchCondition, ReadHandler>* this_handler)
+ {
+ boost_asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+void async_read_until(AsyncReadStream& s,
+ boost::asio::basic_streambuf<Allocator>& b,
+ MatchCondition match_condition, ReadHandler handler,
+ typename boost::enable_if<is_match_condition<MatchCondition> >::type*)
+{
+ // Determine the range of the data to be searched.
+ typedef typename boost::asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin = iterator::begin(buffers);
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ std::size_t next_search_start;
+ std::pair<iterator, bool> result = match_condition(begin, end);
+ if (result.first != end)
+ {
+ if (result.second)
+ {
+ // Full match. We're done.
+ boost::system::error_code ec;
+ std::size_t bytes = result.first - begin;
+ s.io_service().post(detail::bind_handler(handler, ec, bytes));
+ return;
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ next_search_start = result.first - begin;
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ next_search_start = end - begin;
+ }
+
+ // Check if buffer is full.
+ if (b.size() == b.max_size())
+ {
+ boost::system::error_code ec(error::not_found);
+ s.io_service().post(detail::bind_handler(handler, ec, 0));
+ return;
+ }
+
+ // Start a new asynchronous read operation to obtain more data.
+ std::size_t bytes_available =
+ std::min<std::size_t>(512, b.max_size() - b.size());
+ s.async_read_some(b.prepare(bytes_available),
+ detail::read_until_match_handler<
+ AsyncReadStream, Allocator, MatchCondition, ReadHandler>(
+ s, b, match_condition, next_search_start, handler));
+}
+
 } // namespace asio
 } // namespace boost
 

Modified: trunk/boost/asio/read_until.hpp
==============================================================================
--- trunk/boost/asio/read_until.hpp (original)
+++ trunk/boost/asio/read_until.hpp 2008-06-18 09:03:46 EDT (Wed, 18 Jun 2008)
@@ -21,6 +21,9 @@
 #include <cstddef>
 #include <boost/config.hpp>
 #include <boost/regex.hpp>
+#include <boost/type_traits/is_function.hpp>
+#include <boost/type_traits/remove_pointer.hpp>
+#include <boost/utility/enable_if.hpp>
 #include <string>
 #include <boost/asio/detail/pop_options.hpp>
 
@@ -30,6 +33,36 @@
 namespace boost {
 namespace asio {
 
+namespace detail
+{
+ template <typename T>
+ struct has_result_type
+ {
+ struct big { char a[100]; };
+ template <typename U> static big helper(U, ...);
+ template <typename U> static char helper(U, typename U::result_type* = 0);
+ static const T& ref();
+ enum { value = (sizeof((helper)((ref)())) == 1) };
+ };
+} // namespace detail
+
+/// Type trait used to determine whether a type can be used as a match condition
+/// function with read_until and async_read_until.
+template <typename T>
+struct is_match_condition
+{
+#if defined(GENERATING_DOCUMENTATION)
+ /// The value member is true if the type may be used as a match condition.
+ static const bool value;
+#else
+ enum
+ {
+ value = boost::is_function<typename boost::remove_pointer<T>::type>::value
+ || detail::has_result_type<T>::value
+ };
+#endif
+};
+
 /**
  * @defgroup read_until boost::asio::read_until
  */
@@ -243,6 +276,158 @@
     boost::asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
     boost::system::error_code& ec);
 
+/// Read data into a streambuf until a function object indicates a match.
+/**
+ * This function is used to read data into the specified streambuf until a
+ * user-defined match condition function object, when applied to the data
+ * contained in the streambuf, indicates a successful match. The call will
+ * block until one of the following conditions is true:
+ *
+ * @li The match condition function object returns a std::pair where the second
+ * element evaluates to true.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * read_some function. If the match condition function object already indicates
+ * a match, the function returns immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the SyncReadStream concept.
+ *
+ * @param b A streambuf object into which the data will be read.
+ *
+ * @param match_condition The function object to be called to determine whether
+ * a match exists. The signature of the function object must be:
+ * @code pair<iterator, bool> match_condition(iterator begin, iterator end);
+ * @endcode
+ * where @c iterator represents the type:
+ * @code buffers_iterator<basic_streambuf<Allocator>::const_buffers_type>
+ * @endcode
+ * The iterator parameters @c begin and @c end define the range of bytes to be
+ * scanned to determine whether there is a match. The @c first member of the
+ * return value is an iterator marking one-past-the-end of the bytes that have
+ * been consumed by the match function. This iterator is used to calculate the
+ * @c begin parameter for any subsequent invocation of the match condition. The
+ * @c second member of the return value is true if a match has been found, false
+ * otherwise.
+ *
+ * @returns The number of bytes in the streambuf's get area that have been fully
+ * consumed by the match function.
+ *
+ * @throws boost::system::system_error Thrown on failure.
+ *
+ * @note The default implementation of the @c is_match_condition type trait
+ * evaluates to true for function pointers and function objects with a
+ * @c result_type typedef. It must be specialised for other user-defined
+ * function objects.
+ *
+ * @par Examples
+ * To read data into a streambuf until whitespace is encountered:
+ * @code typedef boost::asio::buffers_iterator<
+ * boost::asio::streambuf::const_buffers_type> iterator;
+ *
+ * std::pair<iterator, bool>
+ * match_whitespace(iterator begin, iterator end)
+ * {
+ * iterator i = begin;
+ * while (i != end)
+ * if (std::isspace(*i++))
+ * return std::make_pair(i, true);
+ * return std::make_pair(i, false);
+ * }
+ * ...
+ * boost::asio::streambuf b;
+ * boost::asio::read_until(s, b, match_whitespace);
+ * @endcode
+ *
+ * To read data into a streambuf until a matching character is found:
+ * @code class match_char
+ * {
+ * public:
+ * explicit match_char(char c) : c_(c) {}
+ *
+ * template <typename Iterator>
+ * std::pair<Iterator, bool> operator()(
+ * Iterator begin, Iterator end) const
+ * {
+ * Iterator i = begin;
+ * while (i != end)
+ * if (c_ == *i++)
+ * return std::make_pair(i, true);
+ * return std::make_pair(i, false);
+ * }
+ *
+ * private:
+ * char c_;
+ * };
+ *
+ * namespace asio {
+ * template <> struct is_match_condition<match_char>
+ * : public boost::true_type {};
+ * } // namespace asio
+ * ...
+ * boost::asio::streambuf b;
+ * boost::asio::read_until(s, b, match_char('a'));
+ * @endcode
+ */
+template <typename SyncReadStream, typename Allocator, typename MatchCondition>
+std::size_t read_until(SyncReadStream& s,
+ boost::asio::basic_streambuf<Allocator>& b, MatchCondition match_condition,
+ typename boost::enable_if<is_match_condition<MatchCondition> >::type* = 0);
+
+/// Read data into a streambuf until a function object indicates a match.
+/**
+ * This function is used to read data into the specified streambuf until a
+ * user-defined match condition function object, when applied to the data
+ * contained in the streambuf, indicates a successful match. The call will
+ * block until one of the following conditions is true:
+ *
+ * @li The match condition function object returns a std::pair where the second
+ * element evaluates to true.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * read_some function. If the match condition function object already indicates
+ * a match, the function returns immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the SyncReadStream concept.
+ *
+ * @param b A streambuf object into which the data will be read.
+ *
+ * @param match_condition The function object to be called to determine whether
+ * a match exists. The signature of the function object must be:
+ * @code pair<iterator, bool> match_condition(iterator begin, iterator end);
+ * @endcode
+ * where @c iterator represents the type:
+ * @code buffers_iterator<basic_streambuf<Allocator>::const_buffers_type>
+ * @endcode
+ * The iterator parameters @c begin and @c end define the range of bytes to be
+ * scanned to determine whether there is a match. The @c first member of the
+ * return value is an iterator marking one-past-the-end of the bytes that have
+ * been consumed by the match function. This iterator is used to calculate the
+ * @c begin parameter for any subsequent invocation of the match condition. The
+ * @c second member of the return value is true if a match has been found, false
+ * otherwise.
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ *
+ * @returns The number of bytes in the streambuf's get area that have been fully
+ * consumed by the match function. Returns 0 if an error occurred.
+ *
+ * @note The default implementation of the @c is_match_condition type trait
+ * evaluates to true for function pointers and function objects with a
+ * @c result_type typedef. It must be specialised for other user-defined
+ * function objects.
+ */
+template <typename SyncReadStream, typename Allocator, typename MatchCondition>
+std::size_t read_until(SyncReadStream& s,
+ boost::asio::basic_streambuf<Allocator>& b,
+ MatchCondition match_condition, boost::system::error_code& ec,
+ typename boost::enable_if<is_match_condition<MatchCondition> >::type* = 0);
+
 /*@}*/
 /**
 * @defgroup async_read_until boost::asio::async_read_until
@@ -442,6 +627,127 @@
     boost::asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
     ReadHandler handler);
 
+/// Start an asynchronous operation to read data into a streambuf until a
+/// function object indicates a match.
+/**
+ * This function is used to asynchronously read data into the specified
+ * streambuf until a user-defined match condition function object, when applied
+ * to the data contained in the streambuf, indicates a successful match. The
+ * function call always returns immediately. The asynchronous operation will
+ * continue until one of the following conditions is true:
+ *
+ * @li The match condition function object returns a std::pair where the second
+ * element evaluates to true.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * async_read_some function. If the match condition function object already
+ * indicates a match, the operation completes immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the AsyncReadStream concept.
+ *
+ * @param b A streambuf object into which the data will be read.
+ *
+ * @param match_condition The function object to be called to determine whether
+ * a match exists. The signature of the function object must be:
+ * @code pair<iterator, bool> match_condition(iterator begin, iterator end);
+ * @endcode
+ * where @c iterator represents the type:
+ * @code buffers_iterator<basic_streambuf<Allocator>::const_buffers_type>
+ * @endcode
+ * The iterator parameters @c begin and @c end define the range of bytes to be
+ * scanned to determine whether there is a match. The @c first member of the
+ * return value is an iterator marking one-past-the-end of the bytes that have
+ * been consumed by the match function. This iterator is used to calculate the
+ * @c begin parameter for any subsequent invocation of the match condition. The
+ * @c second member of the return value is true if a match has been found, false
+ * otherwise.
+ *
+ * @param handler The handler to be called when the read operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * // Result of operation.
+ * const boost::system::error_code& error,
+ *
+ * // The number of bytes in the streambuf's get
+ * // area that have been fully consumed by the
+ * // match function. O if an error occurred.
+ * std::size_t bytes_transferred
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * boost::asio::io_service::post().
+ *
+ * @note The default implementation of the @c is_match_condition type trait
+ * evaluates to true for function pointers and function objects with a
+ * @c result_type typedef. It must be specialised for other user-defined
+ * function objects.
+ *
+ * @par Examples
+ * To asynchronously read data into a streambuf until whitespace is encountered:
+ * @code typedef boost::asio::buffers_iterator<
+ * boost::asio::streambuf::const_buffers_type> iterator;
+ *
+ * std::pair<iterator, bool>
+ * match_whitespace(iterator begin, iterator end)
+ * {
+ * iterator i = begin;
+ * while (i != end)
+ * if (std::isspace(*i++))
+ * return std::make_pair(i, true);
+ * return std::make_pair(i, false);
+ * }
+ * ...
+ * void handler(const boost::system::error_code& e, std::size_t size);
+ * ...
+ * boost::asio::streambuf b;
+ * boost::asio::async_read_until(s, b, match_whitespace, handler);
+ * @endcode
+ *
+ * To asynchronously read data into a streambuf until a matching character is
+ * found:
+ * @code class match_char
+ * {
+ * public:
+ * explicit match_char(char c) : c_(c) {}
+ *
+ * template <typename Iterator>
+ * std::pair<Iterator, bool> operator()(
+ * Iterator begin, Iterator end) const
+ * {
+ * Iterator i = begin;
+ * while (i != end)
+ * if (c_ == *i++)
+ * return std::make_pair(i, true);
+ * return std::make_pair(i, false);
+ * }
+ *
+ * private:
+ * char c_;
+ * };
+ *
+ * namespace asio {
+ * template <> struct is_match_condition<match_char>
+ * : public boost::true_type {};
+ * } // namespace asio
+ * ...
+ * void handler(const boost::system::error_code& e, std::size_t size);
+ * ...
+ * boost::asio::streambuf b;
+ * boost::asio::async_read_until(s, b, match_char('a'), handler);
+ * @endcode
+ */
+template <typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+void async_read_until(AsyncReadStream& s,
+ boost::asio::basic_streambuf<Allocator>& b,
+ MatchCondition match_condition, ReadHandler handler,
+ typename boost::enable_if<is_match_condition<MatchCondition> >::type* = 0);
+
 /*@}*/
 
 } // namespace asio

Modified: trunk/libs/asio/test/read_until.cpp
==============================================================================
--- trunk/libs/asio/test/read_until.cpp (original)
+++ trunk/libs/asio/test/read_until.cpp 2008-06-18 09:03:46 EDT (Wed, 18 Jun 2008)
@@ -226,6 +226,90 @@
   BOOST_CHECK(length == 0);
 }
 
+class match_char
+{
+public:
+ explicit match_char(char c) : c_(c) {}
+
+ template <typename Iterator>
+ std::pair<Iterator, bool> operator()(
+ Iterator begin, Iterator end) const
+ {
+ Iterator i = begin;
+ while (i != end)
+ if (c_ == *i++)
+ return std::make_pair(i, true);
+ return std::make_pair(i, false);
+ }
+
+private:
+ char c_;
+};
+
+namespace boost {
+namespace asio {
+ template <> struct is_match_condition<match_char>
+ : public boost::true_type {};
+} // namespace asio
+} // namespace boost
+
+void test_match_condition_read_until()
+{
+ boost::asio::io_service ios;
+ test_stream s(ios);
+ boost::asio::streambuf sb1;
+ boost::asio::streambuf sb2(25);
+ boost::system::error_code ec;
+
+ s.reset(read_data, sizeof(read_data));
+ std::size_t length = boost::asio::read_until(s, sb1, match_char('Z'));
+ BOOST_CHECK(length == 26);
+
+ s.reset(read_data, sizeof(read_data));
+ s.next_read_length(1);
+ length = boost::asio::read_until(s, sb1, match_char('Z'));
+ BOOST_CHECK(length == 26);
+
+ s.reset(read_data, sizeof(read_data));
+ s.next_read_length(10);
+ length = boost::asio::read_until(s, sb1, match_char('Z'));
+ BOOST_CHECK(length == 26);
+
+ s.reset(read_data, sizeof(read_data));
+ length = boost::asio::read_until(s, sb1, match_char('Z'), ec);
+ BOOST_CHECK(!ec);
+ BOOST_CHECK(length == 26);
+
+ s.reset(read_data, sizeof(read_data));
+ s.next_read_length(1);
+ length = boost::asio::read_until(s, sb1, match_char('Z'), ec);
+ BOOST_CHECK(!ec);
+ BOOST_CHECK(length == 26);
+
+ s.reset(read_data, sizeof(read_data));
+ s.next_read_length(10);
+ length = boost::asio::read_until(s, sb1, match_char('Z'), ec);
+ BOOST_CHECK(!ec);
+ BOOST_CHECK(length == 26);
+
+ s.reset(read_data, sizeof(read_data));
+ length = boost::asio::read_until(s, sb2, match_char('Z'), ec);
+ BOOST_CHECK(ec == boost::asio::error::not_found);
+ BOOST_CHECK(length == 0);
+
+ s.reset(read_data, sizeof(read_data));
+ s.next_read_length(1);
+ length = boost::asio::read_until(s, sb2, match_char('Z'), ec);
+ BOOST_CHECK(ec == boost::asio::error::not_found);
+ BOOST_CHECK(length == 0);
+
+ s.reset(read_data, sizeof(read_data));
+ s.next_read_length(10);
+ length = boost::asio::read_until(s, sb2, match_char('Z'), ec);
+ BOOST_CHECK(ec == boost::asio::error::not_found);
+ BOOST_CHECK(length == 0);
+}
+
 void async_read_handler(
     const boost::system::error_code& err, boost::system::error_code* err_out,
     std::size_t bytes_transferred, std::size_t* bytes_out, bool* called)
@@ -421,12 +505,107 @@
   BOOST_CHECK(length == 0);
 }
 
+void test_match_condition_async_read_until()
+{
+ boost::asio::io_service ios;
+ test_stream s(ios);
+ boost::asio::streambuf sb1;
+ boost::asio::streambuf sb2(25);
+ boost::system::error_code ec;
+ std::size_t length;
+ bool called;
+
+ s.reset(read_data, sizeof(read_data));
+ ec = boost::system::error_code();
+ length = 0;
+ called = false;
+ boost::asio::async_read_until(s, sb1, match_char('Z'),
+ boost::bind(async_read_handler, boost::asio::placeholders::error, &ec,
+ boost::asio::placeholders::bytes_transferred, &length, &called));
+ ios.reset();
+ ios.run();
+ BOOST_CHECK(called);
+ BOOST_CHECK(!ec);
+ BOOST_CHECK(length == 26);
+
+ s.reset(read_data, sizeof(read_data));
+ s.next_read_length(1);
+ ec = boost::system::error_code();
+ length = 0;
+ called = false;
+ boost::asio::async_read_until(s, sb1, match_char('Z'),
+ boost::bind(async_read_handler, boost::asio::placeholders::error, &ec,
+ boost::asio::placeholders::bytes_transferred, &length, &called));
+ ios.reset();
+ ios.run();
+ BOOST_CHECK(called);
+ BOOST_CHECK(!ec);
+ BOOST_CHECK(length == 26);
+
+ s.reset(read_data, sizeof(read_data));
+ s.next_read_length(10);
+ ec = boost::system::error_code();
+ length = 0;
+ called = false;
+ boost::asio::async_read_until(s, sb1, match_char('Z'),
+ boost::bind(async_read_handler, boost::asio::placeholders::error, &ec,
+ boost::asio::placeholders::bytes_transferred, &length, &called));
+ ios.reset();
+ ios.run();
+ BOOST_CHECK(called);
+ BOOST_CHECK(!ec);
+ BOOST_CHECK(length == 26);
+
+ s.reset(read_data, sizeof(read_data));
+ ec = boost::system::error_code();
+ length = 0;
+ called = false;
+ boost::asio::async_read_until(s, sb2, match_char('Z'),
+ boost::bind(async_read_handler, boost::asio::placeholders::error, &ec,
+ boost::asio::placeholders::bytes_transferred, &length, &called));
+ ios.reset();
+ ios.run();
+ BOOST_CHECK(called);
+ BOOST_CHECK(ec == boost::asio::error::not_found);
+ BOOST_CHECK(length == 0);
+
+ s.reset(read_data, sizeof(read_data));
+ s.next_read_length(1);
+ ec = boost::system::error_code();
+ length = 0;
+ called = false;
+ boost::asio::async_read_until(s, sb2, match_char('Z'),
+ boost::bind(async_read_handler, boost::asio::placeholders::error, &ec,
+ boost::asio::placeholders::bytes_transferred, &length, &called));
+ ios.reset();
+ ios.run();
+ BOOST_CHECK(called);
+ BOOST_CHECK(ec == boost::asio::error::not_found);
+ BOOST_CHECK(length == 0);
+
+ s.reset(read_data, sizeof(read_data));
+ s.next_read_length(10);
+ ec = boost::system::error_code();
+ length = 0;
+ called = false;
+ boost::asio::async_read_until(s, sb2, match_char('Z'),
+ boost::bind(async_read_handler, boost::asio::placeholders::error, &ec,
+ boost::asio::placeholders::bytes_transferred, &length, &called));
+ ios.reset();
+ ios.run();
+ BOOST_CHECK(called);
+ BOOST_CHECK(ec == boost::asio::error::not_found);
+ BOOST_CHECK(length == 0);
+}
+
 test_suite* init_unit_test_suite(int, char*[])
 {
   test_suite* test = BOOST_TEST_SUITE("read_until");
   test->add(BOOST_TEST_CASE(&test_char_read_until));
   test->add(BOOST_TEST_CASE(&test_string_read_until));
+ test->add(BOOST_TEST_CASE(&test_match_condition_read_until));
   test->add(BOOST_TEST_CASE(&test_char_async_read_until));
   test->add(BOOST_TEST_CASE(&test_string_async_read_until));
+ test->add(BOOST_TEST_CASE(&test_match_condition_async_read_until));
   return test;
 }


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk