$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
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;
 }