Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 08:27:29

0001 //
0002 // impl/read_until.hpp
0003 // ~~~~~~~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
0006 //
0007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0009 //
0010 
0011 #ifndef BOOST_ASIO_IMPL_READ_UNTIL_HPP
0012 #define BOOST_ASIO_IMPL_READ_UNTIL_HPP
0013 
0014 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0015 # pragma once
0016 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
0017 
0018 #include <algorithm>
0019 #include <string>
0020 #include <vector>
0021 #include <utility>
0022 #include <boost/asio/associator.hpp>
0023 #include <boost/asio/buffer.hpp>
0024 #include <boost/asio/buffers_iterator.hpp>
0025 #include <boost/asio/detail/base_from_cancellation_state.hpp>
0026 #include <boost/asio/detail/bind_handler.hpp>
0027 #include <boost/asio/detail/handler_cont_helpers.hpp>
0028 #include <boost/asio/detail/handler_tracking.hpp>
0029 #include <boost/asio/detail/handler_type_requirements.hpp>
0030 #include <boost/asio/detail/limits.hpp>
0031 #include <boost/asio/detail/non_const_lvalue.hpp>
0032 #include <boost/asio/detail/throw_error.hpp>
0033 
0034 #include <boost/asio/detail/push_options.hpp>
0035 
0036 namespace boost {
0037 namespace asio {
0038 
0039 namespace detail
0040 {
0041   // Algorithm that finds a subsequence of equal values in a sequence. Returns
0042   // (iterator,true) if a full match was found, in which case the iterator
0043   // points to the beginning of the match. Returns (iterator,false) if a
0044   // partial match was found at the end of the first sequence, in which case
0045   // the iterator points to the beginning of the partial match. Returns
0046   // (last1,false) if no full or partial match was found.
0047   template <typename Iterator1, typename Iterator2>
0048   std::pair<Iterator1, bool> partial_search(
0049       Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
0050   {
0051     for (Iterator1 iter1 = first1; iter1 != last1; ++iter1)
0052     {
0053       Iterator1 test_iter1 = iter1;
0054       Iterator2 test_iter2 = first2;
0055       for (;; ++test_iter1, ++test_iter2)
0056       {
0057         if (test_iter2 == last2)
0058           return std::make_pair(iter1, true);
0059         if (test_iter1 == last1)
0060         {
0061           if (test_iter2 != first2)
0062             return std::make_pair(iter1, false);
0063           else
0064             break;
0065         }
0066         if (*test_iter1 != *test_iter2)
0067           break;
0068       }
0069     }
0070     return std::make_pair(last1, false);
0071   }
0072 
0073 #if !defined(BOOST_ASIO_NO_EXTENSIONS)
0074 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
0075   struct regex_match_flags
0076   {
0077     template <typename T>
0078     operator T() const
0079     {
0080       return T::match_default | T::match_partial;
0081     }
0082   };
0083 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
0084 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
0085 } // namespace detail
0086 
0087 #if !defined(BOOST_ASIO_NO_DYNAMIC_BUFFER_V1)
0088 
0089 template <typename SyncReadStream, typename DynamicBuffer_v1>
0090 inline std::size_t read_until(SyncReadStream& s,
0091     DynamicBuffer_v1&& buffers, char delim,
0092     constraint_t<
0093       is_dynamic_buffer_v1<decay_t<DynamicBuffer_v1>>::value
0094     >,
0095     constraint_t<
0096       !is_dynamic_buffer_v2<decay_t<DynamicBuffer_v1>>::value
0097     >)
0098 {
0099   boost::system::error_code ec;
0100   std::size_t bytes_transferred = read_until(s,
0101       static_cast<DynamicBuffer_v1&&>(buffers), delim, ec);
0102   boost::asio::detail::throw_error(ec, "read_until");
0103   return bytes_transferred;
0104 }
0105 
0106 template <typename SyncReadStream, typename DynamicBuffer_v1>
0107 std::size_t read_until(SyncReadStream& s,
0108     DynamicBuffer_v1&& buffers,
0109     char delim, boost::system::error_code& ec,
0110     constraint_t<
0111       is_dynamic_buffer_v1<decay_t<DynamicBuffer_v1>>::value
0112     >,
0113     constraint_t<
0114       !is_dynamic_buffer_v2<decay_t<DynamicBuffer_v1>>::value
0115     >)
0116 {
0117   decay_t<DynamicBuffer_v1> b(
0118       static_cast<DynamicBuffer_v1&&>(buffers));
0119 
0120   std::size_t search_position = 0;
0121   for (;;)
0122   {
0123     // Determine the range of the data to be searched.
0124     typedef typename DynamicBuffer_v1::const_buffers_type buffers_type;
0125     typedef buffers_iterator<buffers_type> iterator;
0126     buffers_type data_buffers = b.data();
0127     iterator begin = iterator::begin(data_buffers);
0128     iterator start_pos = begin + search_position;
0129     iterator end = iterator::end(data_buffers);
0130 
0131     // Look for a match.
0132     iterator iter = std::find(start_pos, end, delim);
0133     if (iter != end)
0134     {
0135       // Found a match. We're done.
0136       ec = boost::system::error_code();
0137       return iter - begin + 1;
0138     }
0139     else
0140     {
0141       // No match. Next search can start with the new data.
0142       search_position = end - begin;
0143     }
0144 
0145     // Check if buffer is full.
0146     if (b.size() == b.max_size())
0147     {
0148       ec = error::not_found;
0149       return 0;
0150     }
0151 
0152     // Need more data.
0153     std::size_t bytes_to_read = std::min<std::size_t>(
0154           std::max<std::size_t>(512, b.capacity() - b.size()),
0155           std::min<std::size_t>(65536, b.max_size() - b.size()));
0156     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
0157     if (ec)
0158       return 0;
0159   }
0160 }
0161 
0162 template <typename SyncReadStream, typename DynamicBuffer_v1>
0163 inline std::size_t read_until(SyncReadStream& s,
0164     DynamicBuffer_v1&& buffers,
0165     BOOST_ASIO_STRING_VIEW_PARAM delim,
0166     constraint_t<
0167       is_dynamic_buffer_v1<decay_t<DynamicBuffer_v1>>::value
0168     >,
0169     constraint_t<
0170       !is_dynamic_buffer_v2<decay_t<DynamicBuffer_v1>>::value
0171     >)
0172 {
0173   boost::system::error_code ec;
0174   std::size_t bytes_transferred = read_until(s,
0175       static_cast<DynamicBuffer_v1&&>(buffers), delim, ec);
0176   boost::asio::detail::throw_error(ec, "read_until");
0177   return bytes_transferred;
0178 }
0179 
0180 template <typename SyncReadStream, typename DynamicBuffer_v1>
0181 std::size_t read_until(SyncReadStream& s,
0182     DynamicBuffer_v1&& buffers,
0183     BOOST_ASIO_STRING_VIEW_PARAM delim, boost::system::error_code& ec,
0184     constraint_t<
0185       is_dynamic_buffer_v1<decay_t<DynamicBuffer_v1>>::value
0186     >,
0187     constraint_t<
0188       !is_dynamic_buffer_v2<decay_t<DynamicBuffer_v1>>::value
0189     >)
0190 {
0191   decay_t<DynamicBuffer_v1> b(
0192       static_cast<DynamicBuffer_v1&&>(buffers));
0193 
0194   std::size_t search_position = 0;
0195   for (;;)
0196   {
0197     // Determine the range of the data to be searched.
0198     typedef typename DynamicBuffer_v1::const_buffers_type buffers_type;
0199     typedef buffers_iterator<buffers_type> iterator;
0200     buffers_type data_buffers = b.data();
0201     iterator begin = iterator::begin(data_buffers);
0202     iterator start_pos = begin + search_position;
0203     iterator end = iterator::end(data_buffers);
0204 
0205     // Look for a match.
0206     std::pair<iterator, bool> result = detail::partial_search(
0207         start_pos, end, delim.begin(), delim.end());
0208     if (result.first != end)
0209     {
0210       if (result.second)
0211       {
0212         // Full match. We're done.
0213         ec = boost::system::error_code();
0214         return result.first - begin + delim.length();
0215       }
0216       else
0217       {
0218         // Partial match. Next search needs to start from beginning of match.
0219         search_position = result.first - begin;
0220       }
0221     }
0222     else
0223     {
0224       // No match. Next search can start with the new data.
0225       search_position = end - begin;
0226     }
0227 
0228     // Check if buffer is full.
0229     if (b.size() == b.max_size())
0230     {
0231       ec = error::not_found;
0232       return 0;
0233     }
0234 
0235     // Need more data.
0236     std::size_t bytes_to_read = std::min<std::size_t>(
0237           std::max<std::size_t>(512, b.capacity() - b.size()),
0238           std::min<std::size_t>(65536, b.max_size() - b.size()));
0239     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
0240     if (ec)
0241       return 0;
0242   }
0243 }
0244 
0245 #if !defined(BOOST_ASIO_NO_EXTENSIONS)
0246 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
0247 
0248 template <typename SyncReadStream, typename DynamicBuffer_v1, typename Traits>
0249 inline std::size_t read_until(SyncReadStream& s, DynamicBuffer_v1&& buffers,
0250     const boost::basic_regex<char, Traits>& expr,
0251     constraint_t<
0252       is_dynamic_buffer_v1<decay_t<DynamicBuffer_v1>>::value
0253     >,
0254     constraint_t<
0255       !is_dynamic_buffer_v2<decay_t<DynamicBuffer_v1>>::value
0256     >)
0257 {
0258   boost::system::error_code ec;
0259   std::size_t bytes_transferred = read_until(s,
0260       static_cast<DynamicBuffer_v1&&>(buffers), expr, ec);
0261   boost::asio::detail::throw_error(ec, "read_until");
0262   return bytes_transferred;
0263 }
0264 
0265 template <typename SyncReadStream, typename DynamicBuffer_v1, typename Traits>
0266 std::size_t read_until(SyncReadStream& s, DynamicBuffer_v1&& buffers,
0267     const boost::basic_regex<char, Traits>& expr, boost::system::error_code& ec,
0268     constraint_t<
0269       is_dynamic_buffer_v1<decay_t<DynamicBuffer_v1>>::value
0270     >,
0271     constraint_t<
0272       !is_dynamic_buffer_v2<decay_t<DynamicBuffer_v1>>::value
0273     >)
0274 {
0275   decay_t<DynamicBuffer_v1> b(
0276       static_cast<DynamicBuffer_v1&&>(buffers));
0277 
0278   std::size_t search_position = 0;
0279   for (;;)
0280   {
0281     // Determine the range of the data to be searched.
0282     typedef typename DynamicBuffer_v1::const_buffers_type buffers_type;
0283     typedef buffers_iterator<buffers_type> iterator;
0284     buffers_type data_buffers = b.data();
0285     iterator begin = iterator::begin(data_buffers);
0286     iterator start_pos = begin + search_position;
0287     iterator end = iterator::end(data_buffers);
0288 
0289     // Look for a match.
0290     boost::match_results<iterator,
0291       typename std::vector<boost::sub_match<iterator>>::allocator_type>
0292         match_results;
0293     if (regex_search(start_pos, end, match_results,
0294           expr, detail::regex_match_flags()))
0295     {
0296       if (match_results[0].matched)
0297       {
0298         // Full match. We're done.
0299         ec = boost::system::error_code();
0300         return match_results[0].second - begin;
0301       }
0302       else
0303       {
0304         // Partial match. Next search needs to start from beginning of match.
0305         search_position = match_results[0].first - begin;
0306       }
0307     }
0308     else
0309     {
0310       // No match. Next search can start with the new data.
0311       search_position = end - begin;
0312     }
0313 
0314     // Check if buffer is full.
0315     if (b.size() == b.max_size())
0316     {
0317       ec = error::not_found;
0318       return 0;
0319     }
0320 
0321     // Need more data.
0322     std::size_t bytes_to_read = std::min<std::size_t>(
0323           std::max<std::size_t>(512, b.capacity() - b.size()),
0324           std::min<std::size_t>(65536, b.max_size() - b.size()));
0325     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
0326     if (ec)
0327       return 0;
0328   }
0329 }
0330 
0331 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
0332 
0333 template <typename SyncReadStream,
0334     typename DynamicBuffer_v1, typename MatchCondition>
0335 inline std::size_t read_until(SyncReadStream& s,
0336     DynamicBuffer_v1&& buffers,
0337     MatchCondition match_condition,
0338     constraint_t<
0339       is_match_condition<MatchCondition>::value
0340     >,
0341     constraint_t<
0342       is_dynamic_buffer_v1<decay_t<DynamicBuffer_v1>>::value
0343     >,
0344     constraint_t<
0345       !is_dynamic_buffer_v2<decay_t<DynamicBuffer_v1>>::value
0346     >)
0347 {
0348   boost::system::error_code ec;
0349   std::size_t bytes_transferred = read_until(s,
0350       static_cast<DynamicBuffer_v1&&>(buffers),
0351       match_condition, ec);
0352   boost::asio::detail::throw_error(ec, "read_until");
0353   return bytes_transferred;
0354 }
0355 
0356 template <typename SyncReadStream,
0357     typename DynamicBuffer_v1, typename MatchCondition>
0358 std::size_t read_until(SyncReadStream& s,
0359     DynamicBuffer_v1&& buffers,
0360     MatchCondition match_condition, boost::system::error_code& ec,
0361     constraint_t<
0362       is_match_condition<MatchCondition>::value
0363     >,
0364     constraint_t<
0365       is_dynamic_buffer_v1<decay_t<DynamicBuffer_v1>>::value
0366     >,
0367     constraint_t<
0368       !is_dynamic_buffer_v2<decay_t<DynamicBuffer_v1>>::value
0369     >)
0370 {
0371   decay_t<DynamicBuffer_v1> b(
0372       static_cast<DynamicBuffer_v1&&>(buffers));
0373 
0374   std::size_t search_position = 0;
0375   for (;;)
0376   {
0377     // Determine the range of the data to be searched.
0378     typedef typename DynamicBuffer_v1::const_buffers_type buffers_type;
0379     typedef buffers_iterator<buffers_type> iterator;
0380     buffers_type data_buffers = b.data();
0381     iterator begin = iterator::begin(data_buffers);
0382     iterator start_pos = begin + search_position;
0383     iterator end = iterator::end(data_buffers);
0384 
0385     // Look for a match.
0386     std::pair<iterator, bool> result = match_condition(start_pos, end);
0387     if (result.second)
0388     {
0389       // Full match. We're done.
0390       ec = boost::system::error_code();
0391       return result.first - begin;
0392     }
0393     else if (result.first != end)
0394     {
0395       // Partial match. Next search needs to start from beginning of match.
0396       search_position = result.first - begin;
0397     }
0398     else
0399     {
0400       // No match. Next search can start with the new data.
0401       search_position = end - begin;
0402     }
0403 
0404     // Check if buffer is full.
0405     if (b.size() == b.max_size())
0406     {
0407       ec = error::not_found;
0408       return 0;
0409     }
0410 
0411     // Need more data.
0412     std::size_t bytes_to_read = std::min<std::size_t>(
0413           std::max<std::size_t>(512, b.capacity() - b.size()),
0414           std::min<std::size_t>(65536, b.max_size() - b.size()));
0415     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
0416     if (ec)
0417       return 0;
0418   }
0419 }
0420 
0421 #if !defined(BOOST_ASIO_NO_IOSTREAM)
0422 
0423 template <typename SyncReadStream, typename Allocator>
0424 inline std::size_t read_until(SyncReadStream& s,
0425     boost::asio::basic_streambuf<Allocator>& b, char delim)
0426 {
0427   return read_until(s, basic_streambuf_ref<Allocator>(b), delim);
0428 }
0429 
0430 template <typename SyncReadStream, typename Allocator>
0431 inline std::size_t read_until(SyncReadStream& s,
0432     boost::asio::basic_streambuf<Allocator>& b, char delim,
0433     boost::system::error_code& ec)
0434 {
0435   return read_until(s, basic_streambuf_ref<Allocator>(b), delim, ec);
0436 }
0437 
0438 template <typename SyncReadStream, typename Allocator>
0439 inline std::size_t read_until(SyncReadStream& s,
0440     boost::asio::basic_streambuf<Allocator>& b,
0441     BOOST_ASIO_STRING_VIEW_PARAM delim)
0442 {
0443   return read_until(s, basic_streambuf_ref<Allocator>(b), delim);
0444 }
0445 
0446 template <typename SyncReadStream, typename Allocator>
0447 inline std::size_t read_until(SyncReadStream& s,
0448     boost::asio::basic_streambuf<Allocator>& b,
0449     BOOST_ASIO_STRING_VIEW_PARAM delim, boost::system::error_code& ec)
0450 {
0451   return read_until(s, basic_streambuf_ref<Allocator>(b), delim, ec);
0452 }
0453 
0454 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
0455 
0456 template <typename SyncReadStream, typename Allocator, typename Traits>
0457 inline std::size_t read_until(SyncReadStream& s,
0458     boost::asio::basic_streambuf<Allocator>& b,
0459     const boost::basic_regex<char, Traits>& expr)
0460 {
0461   return read_until(s, basic_streambuf_ref<Allocator>(b), expr);
0462 }
0463 
0464 template <typename SyncReadStream, typename Allocator, typename Traits>
0465 inline std::size_t read_until(SyncReadStream& s,
0466     boost::asio::basic_streambuf<Allocator>& b,
0467     const boost::basic_regex<char, Traits>& expr,
0468     boost::system::error_code& ec)
0469 {
0470   return read_until(s, basic_streambuf_ref<Allocator>(b), expr, ec);
0471 }
0472 
0473 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
0474 
0475 template <typename SyncReadStream, typename Allocator, typename MatchCondition>
0476 inline std::size_t read_until(SyncReadStream& s,
0477     boost::asio::basic_streambuf<Allocator>& b, MatchCondition match_condition,
0478     constraint_t<is_match_condition<MatchCondition>::value>)
0479 {
0480   return read_until(s, basic_streambuf_ref<Allocator>(b), match_condition);
0481 }
0482 
0483 template <typename SyncReadStream, typename Allocator, typename MatchCondition>
0484 inline std::size_t read_until(SyncReadStream& s,
0485     boost::asio::basic_streambuf<Allocator>& b,
0486     MatchCondition match_condition, boost::system::error_code& ec,
0487     constraint_t<is_match_condition<MatchCondition>::value>)
0488 {
0489   return read_until(s, basic_streambuf_ref<Allocator>(b), match_condition, ec);
0490 }
0491 
0492 #endif // !defined(BOOST_ASIO_NO_IOSTREAM)
0493 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
0494 #endif // !defined(BOOST_ASIO_NO_DYNAMIC_BUFFER_V1)
0495 
0496 template <typename SyncReadStream, typename DynamicBuffer_v2>
0497 inline std::size_t read_until(SyncReadStream& s,
0498     DynamicBuffer_v2 buffers, char delim,
0499     constraint_t<
0500       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
0501     >)
0502 {
0503   boost::system::error_code ec;
0504   std::size_t bytes_transferred = read_until(s,
0505       static_cast<DynamicBuffer_v2&&>(buffers), delim, ec);
0506   boost::asio::detail::throw_error(ec, "read_until");
0507   return bytes_transferred;
0508 }
0509 
0510 template <typename SyncReadStream, typename DynamicBuffer_v2>
0511 std::size_t read_until(SyncReadStream& s, DynamicBuffer_v2 buffers,
0512     char delim, boost::system::error_code& ec,
0513     constraint_t<
0514       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
0515     >)
0516 {
0517   DynamicBuffer_v2& b = buffers;
0518 
0519   std::size_t search_position = 0;
0520   for (;;)
0521   {
0522     // Determine the range of the data to be searched.
0523     typedef typename DynamicBuffer_v2::const_buffers_type buffers_type;
0524     typedef buffers_iterator<buffers_type> iterator;
0525     buffers_type data_buffers =
0526       const_cast<const DynamicBuffer_v2&>(b).data(0, b.size());
0527     iterator begin = iterator::begin(data_buffers);
0528     iterator start_pos = begin + search_position;
0529     iterator end = iterator::end(data_buffers);
0530 
0531     // Look for a match.
0532     iterator iter = std::find(start_pos, end, delim);
0533     if (iter != end)
0534     {
0535       // Found a match. We're done.
0536       ec = boost::system::error_code();
0537       return iter - begin + 1;
0538     }
0539     else
0540     {
0541       // No match. Next search can start with the new data.
0542       search_position = end - begin;
0543     }
0544 
0545     // Check if buffer is full.
0546     if (b.size() == b.max_size())
0547     {
0548       ec = error::not_found;
0549       return 0;
0550     }
0551 
0552     // Need more data.
0553     std::size_t bytes_to_read = std::min<std::size_t>(
0554           std::max<std::size_t>(512, b.capacity() - b.size()),
0555           std::min<std::size_t>(65536, b.max_size() - b.size()));
0556     std::size_t pos = b.size();
0557     b.grow(bytes_to_read);
0558     std::size_t bytes_transferred = s.read_some(b.data(pos, bytes_to_read), ec);
0559     b.shrink(bytes_to_read - bytes_transferred);
0560     if (ec)
0561       return 0;
0562   }
0563 }
0564 
0565 template <typename SyncReadStream, typename DynamicBuffer_v2>
0566 inline std::size_t read_until(SyncReadStream& s,
0567     DynamicBuffer_v2 buffers, BOOST_ASIO_STRING_VIEW_PARAM delim,
0568     constraint_t<
0569       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
0570     >)
0571 {
0572   boost::system::error_code ec;
0573   std::size_t bytes_transferred = read_until(s,
0574       static_cast<DynamicBuffer_v2&&>(buffers), delim, ec);
0575   boost::asio::detail::throw_error(ec, "read_until");
0576   return bytes_transferred;
0577 }
0578 
0579 template <typename SyncReadStream, typename DynamicBuffer_v2>
0580 std::size_t read_until(SyncReadStream& s, DynamicBuffer_v2 buffers,
0581     BOOST_ASIO_STRING_VIEW_PARAM delim, boost::system::error_code& ec,
0582     constraint_t<
0583       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
0584     >)
0585 {
0586   DynamicBuffer_v2& b = buffers;
0587 
0588   std::size_t search_position = 0;
0589   for (;;)
0590   {
0591     // Determine the range of the data to be searched.
0592     typedef typename DynamicBuffer_v2::const_buffers_type buffers_type;
0593     typedef buffers_iterator<buffers_type> iterator;
0594     buffers_type data_buffers =
0595       const_cast<const DynamicBuffer_v2&>(b).data(0, b.size());
0596     iterator begin = iterator::begin(data_buffers);
0597     iterator start_pos = begin + search_position;
0598     iterator end = iterator::end(data_buffers);
0599 
0600     // Look for a match.
0601     std::pair<iterator, bool> result = detail::partial_search(
0602         start_pos, end, delim.begin(), delim.end());
0603     if (result.first != end)
0604     {
0605       if (result.second)
0606       {
0607         // Full match. We're done.
0608         ec = boost::system::error_code();
0609         return result.first - begin + delim.length();
0610       }
0611       else
0612       {
0613         // Partial match. Next search needs to start from beginning of match.
0614         search_position = result.first - begin;
0615       }
0616     }
0617     else
0618     {
0619       // No match. Next search can start with the new data.
0620       search_position = end - begin;
0621     }
0622 
0623     // Check if buffer is full.
0624     if (b.size() == b.max_size())
0625     {
0626       ec = error::not_found;
0627       return 0;
0628     }
0629 
0630     // Need more data.
0631     std::size_t bytes_to_read = std::min<std::size_t>(
0632           std::max<std::size_t>(512, b.capacity() - b.size()),
0633           std::min<std::size_t>(65536, b.max_size() - b.size()));
0634     std::size_t pos = b.size();
0635     b.grow(bytes_to_read);
0636     std::size_t bytes_transferred = s.read_some(b.data(pos, bytes_to_read), ec);
0637     b.shrink(bytes_to_read - bytes_transferred);
0638     if (ec)
0639       return 0;
0640   }
0641 }
0642 
0643 #if !defined(BOOST_ASIO_NO_EXTENSIONS)
0644 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
0645 
0646 template <typename SyncReadStream, typename DynamicBuffer_v2, typename Traits>
0647 inline std::size_t read_until(SyncReadStream& s, DynamicBuffer_v2 buffers,
0648     const boost::basic_regex<char, Traits>& expr,
0649     constraint_t<
0650       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
0651     >)
0652 {
0653   boost::system::error_code ec;
0654   std::size_t bytes_transferred = read_until(s,
0655       static_cast<DynamicBuffer_v2&&>(buffers), expr, ec);
0656   boost::asio::detail::throw_error(ec, "read_until");
0657   return bytes_transferred;
0658 }
0659 
0660 template <typename SyncReadStream, typename DynamicBuffer_v2, typename Traits>
0661 std::size_t read_until(SyncReadStream& s, DynamicBuffer_v2 buffers,
0662     const boost::basic_regex<char, Traits>& expr, boost::system::error_code& ec,
0663     constraint_t<
0664       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
0665     >)
0666 {
0667   DynamicBuffer_v2& b = buffers;
0668 
0669   std::size_t search_position = 0;
0670   for (;;)
0671   {
0672     // Determine the range of the data to be searched.
0673     typedef typename DynamicBuffer_v2::const_buffers_type buffers_type;
0674     typedef buffers_iterator<buffers_type> iterator;
0675     buffers_type data_buffers =
0676       const_cast<const DynamicBuffer_v2&>(b).data(0, b.size());
0677     iterator begin = iterator::begin(data_buffers);
0678     iterator start_pos = begin + search_position;
0679     iterator end = iterator::end(data_buffers);
0680 
0681     // Look for a match.
0682     boost::match_results<iterator,
0683       typename std::vector<boost::sub_match<iterator>>::allocator_type>
0684         match_results;
0685     if (regex_search(start_pos, end, match_results,
0686           expr, detail::regex_match_flags()))
0687     {
0688       if (match_results[0].matched)
0689       {
0690         // Full match. We're done.
0691         ec = boost::system::error_code();
0692         return match_results[0].second - begin;
0693       }
0694       else
0695       {
0696         // Partial match. Next search needs to start from beginning of match.
0697         search_position = match_results[0].first - begin;
0698       }
0699     }
0700     else
0701     {
0702       // No match. Next search can start with the new data.
0703       search_position = end - begin;
0704     }
0705 
0706     // Check if buffer is full.
0707     if (b.size() == b.max_size())
0708     {
0709       ec = error::not_found;
0710       return 0;
0711     }
0712 
0713     // Need more data.
0714     std::size_t bytes_to_read = std::min<std::size_t>(
0715           std::max<std::size_t>(512, b.capacity() - b.size()),
0716           std::min<std::size_t>(65536, b.max_size() - b.size()));
0717     std::size_t pos = b.size();
0718     b.grow(bytes_to_read);
0719     std::size_t bytes_transferred = s.read_some(b.data(pos, bytes_to_read), ec);
0720     b.shrink(bytes_to_read - bytes_transferred);
0721     if (ec)
0722       return 0;
0723   }
0724 }
0725 
0726 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
0727 
0728 template <typename SyncReadStream,
0729     typename DynamicBuffer_v2, typename MatchCondition>
0730 inline std::size_t read_until(SyncReadStream& s,
0731     DynamicBuffer_v2 buffers, MatchCondition match_condition,
0732     constraint_t<
0733       is_match_condition<MatchCondition>::value
0734     >,
0735     constraint_t<
0736       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
0737     >)
0738 {
0739   boost::system::error_code ec;
0740   std::size_t bytes_transferred = read_until(s,
0741       static_cast<DynamicBuffer_v2&&>(buffers),
0742       match_condition, ec);
0743   boost::asio::detail::throw_error(ec, "read_until");
0744   return bytes_transferred;
0745 }
0746 
0747 template <typename SyncReadStream,
0748     typename DynamicBuffer_v2, typename MatchCondition>
0749 std::size_t read_until(SyncReadStream& s, DynamicBuffer_v2 buffers,
0750     MatchCondition match_condition, boost::system::error_code& ec,
0751     constraint_t<
0752       is_match_condition<MatchCondition>::value
0753     >,
0754     constraint_t<
0755       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
0756     >)
0757 {
0758   DynamicBuffer_v2& b = buffers;
0759 
0760   std::size_t search_position = 0;
0761   for (;;)
0762   {
0763     // Determine the range of the data to be searched.
0764     typedef typename DynamicBuffer_v2::const_buffers_type buffers_type;
0765     typedef buffers_iterator<buffers_type> iterator;
0766     buffers_type data_buffers =
0767       const_cast<const DynamicBuffer_v2&>(b).data(0, b.size());
0768     iterator begin = iterator::begin(data_buffers);
0769     iterator start_pos = begin + search_position;
0770     iterator end = iterator::end(data_buffers);
0771 
0772     // Look for a match.
0773     std::pair<iterator, bool> result = match_condition(start_pos, end);
0774     if (result.second)
0775     {
0776       // Full match. We're done.
0777       ec = boost::system::error_code();
0778       return result.first - begin;
0779     }
0780     else if (result.first != end)
0781     {
0782       // Partial match. Next search needs to start from beginning of match.
0783       search_position = result.first - begin;
0784     }
0785     else
0786     {
0787       // No match. Next search can start with the new data.
0788       search_position = end - begin;
0789     }
0790 
0791     // Check if buffer is full.
0792     if (b.size() == b.max_size())
0793     {
0794       ec = error::not_found;
0795       return 0;
0796     }
0797 
0798     // Need more data.
0799     std::size_t bytes_to_read = std::min<std::size_t>(
0800           std::max<std::size_t>(512, b.capacity() - b.size()),
0801           std::min<std::size_t>(65536, b.max_size() - b.size()));
0802     std::size_t pos = b.size();
0803     b.grow(bytes_to_read);
0804     std::size_t bytes_transferred = s.read_some(b.data(pos, bytes_to_read), ec);
0805     b.shrink(bytes_to_read - bytes_transferred);
0806     if (ec)
0807       return 0;
0808   }
0809 }
0810 
0811 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
0812 
0813 #if !defined(BOOST_ASIO_NO_DYNAMIC_BUFFER_V1)
0814 
0815 namespace detail
0816 {
0817   template <typename AsyncReadStream,
0818       typename DynamicBuffer_v1, typename ReadHandler>
0819   class read_until_delim_op_v1
0820     : public base_from_cancellation_state<ReadHandler>
0821   {
0822   public:
0823     template <typename BufferSequence>
0824     read_until_delim_op_v1(AsyncReadStream& stream,
0825         BufferSequence&& buffers,
0826         char delim, ReadHandler& handler)
0827       : base_from_cancellation_state<ReadHandler>(
0828           handler, enable_partial_cancellation()),
0829         stream_(stream),
0830         buffers_(static_cast<BufferSequence&&>(buffers)),
0831         delim_(delim),
0832         start_(0),
0833         search_position_(0),
0834         handler_(static_cast<ReadHandler&&>(handler))
0835     {
0836     }
0837 
0838     read_until_delim_op_v1(const read_until_delim_op_v1& other)
0839       : base_from_cancellation_state<ReadHandler>(other),
0840         stream_(other.stream_),
0841         buffers_(other.buffers_),
0842         delim_(other.delim_),
0843         start_(other.start_),
0844         search_position_(other.search_position_),
0845         handler_(other.handler_)
0846     {
0847     }
0848 
0849     read_until_delim_op_v1(read_until_delim_op_v1&& other)
0850       : base_from_cancellation_state<ReadHandler>(
0851           static_cast<base_from_cancellation_state<ReadHandler>&&>(other)),
0852         stream_(other.stream_),
0853         buffers_(static_cast<DynamicBuffer_v1&&>(other.buffers_)),
0854         delim_(other.delim_),
0855         start_(other.start_),
0856         search_position_(other.search_position_),
0857         handler_(static_cast<ReadHandler&&>(other.handler_))
0858     {
0859     }
0860 
0861     void operator()(boost::system::error_code ec,
0862         std::size_t bytes_transferred, int start = 0)
0863     {
0864       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
0865       std::size_t bytes_to_read;
0866       switch (start_ = start)
0867       {
0868       case 1:
0869         for (;;)
0870         {
0871           {
0872             // Determine the range of the data to be searched.
0873             typedef typename DynamicBuffer_v1::const_buffers_type
0874               buffers_type;
0875             typedef buffers_iterator<buffers_type> iterator;
0876             buffers_type data_buffers = buffers_.data();
0877             iterator begin = iterator::begin(data_buffers);
0878             iterator start_pos = begin + search_position_;
0879             iterator end = iterator::end(data_buffers);
0880 
0881             // Look for a match.
0882             iterator iter = std::find(start_pos, end, delim_);
0883             if (iter != end)
0884             {
0885               // Found a match. We're done.
0886               search_position_ = iter - begin + 1;
0887               bytes_to_read = 0;
0888             }
0889 
0890             // No match yet. Check if buffer is full.
0891             else if (buffers_.size() == buffers_.max_size())
0892             {
0893               search_position_ = not_found;
0894               bytes_to_read = 0;
0895             }
0896 
0897             // Need to read some more data.
0898             else
0899             {
0900               // Next search can start with the new data.
0901               search_position_ = end - begin;
0902               bytes_to_read = std::min<std::size_t>(
0903                     std::max<std::size_t>(512,
0904                       buffers_.capacity() - buffers_.size()),
0905                     std::min<std::size_t>(65536,
0906                       buffers_.max_size() - buffers_.size()));
0907             }
0908           }
0909 
0910           // Check if we're done.
0911           if (!start && bytes_to_read == 0)
0912             break;
0913 
0914           // Start a new asynchronous read operation to obtain more data.
0915           {
0916             BOOST_ASIO_HANDLER_LOCATION((
0917                   __FILE__, __LINE__, "async_read_until"));
0918             stream_.async_read_some(buffers_.prepare(bytes_to_read),
0919                 static_cast<read_until_delim_op_v1&&>(*this));
0920           }
0921           return; default:
0922           buffers_.commit(bytes_transferred);
0923           if (ec || bytes_transferred == 0)
0924             break;
0925           if (this->cancelled() != cancellation_type::none)
0926           {
0927             ec = error::operation_aborted;
0928             break;
0929           }
0930         }
0931 
0932         const boost::system::error_code result_ec =
0933           (search_position_ == not_found)
0934           ? error::not_found : ec;
0935 
0936         const std::size_t result_n =
0937           (ec || search_position_ == not_found)
0938           ? 0 : search_position_;
0939 
0940         static_cast<ReadHandler&&>(handler_)(result_ec, result_n);
0941       }
0942     }
0943 
0944   //private:
0945     AsyncReadStream& stream_;
0946     DynamicBuffer_v1 buffers_;
0947     char delim_;
0948     int start_;
0949     std::size_t search_position_;
0950     ReadHandler handler_;
0951   };
0952 
0953   template <typename AsyncReadStream,
0954       typename DynamicBuffer_v1, typename ReadHandler>
0955   inline bool asio_handler_is_continuation(
0956       read_until_delim_op_v1<AsyncReadStream,
0957         DynamicBuffer_v1, ReadHandler>* this_handler)
0958   {
0959     return this_handler->start_ == 0 ? true
0960       : boost_asio_handler_cont_helpers::is_continuation(
0961           this_handler->handler_);
0962   }
0963 
0964   template <typename AsyncReadStream>
0965   class initiate_async_read_until_delim_v1
0966   {
0967   public:
0968     typedef typename AsyncReadStream::executor_type executor_type;
0969 
0970     explicit initiate_async_read_until_delim_v1(AsyncReadStream& stream)
0971       : stream_(stream)
0972     {
0973     }
0974 
0975     executor_type get_executor() const noexcept
0976     {
0977       return stream_.get_executor();
0978     }
0979 
0980     template <typename ReadHandler, typename DynamicBuffer_v1>
0981     void operator()(ReadHandler&& handler,
0982         DynamicBuffer_v1&& buffers,
0983         char delim) const
0984     {
0985       // If you get an error on the following line it means that your handler
0986       // does not meet the documented type requirements for a ReadHandler.
0987       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
0988 
0989       non_const_lvalue<ReadHandler> handler2(handler);
0990       read_until_delim_op_v1<AsyncReadStream,
0991         decay_t<DynamicBuffer_v1>,
0992           decay_t<ReadHandler>>(
0993             stream_, static_cast<DynamicBuffer_v1&&>(buffers),
0994             delim, handler2.value)(boost::system::error_code(), 0, 1);
0995     }
0996 
0997   private:
0998     AsyncReadStream& stream_;
0999   };
1000 } // namespace detail
1001 
1002 #if !defined(GENERATING_DOCUMENTATION)
1003 
1004 template <template <typename, typename> class Associator,
1005     typename AsyncReadStream, typename DynamicBuffer_v1,
1006     typename ReadHandler, typename DefaultCandidate>
1007 struct associator<Associator,
1008     detail::read_until_delim_op_v1<AsyncReadStream,
1009       DynamicBuffer_v1, ReadHandler>,
1010     DefaultCandidate>
1011   : Associator<ReadHandler, DefaultCandidate>
1012 {
1013   static typename Associator<ReadHandler, DefaultCandidate>::type get(
1014       const detail::read_until_delim_op_v1<AsyncReadStream,
1015         DynamicBuffer_v1, ReadHandler>& h) noexcept
1016   {
1017     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_);
1018   }
1019 
1020   static auto get(
1021       const detail::read_until_delim_op_v1<AsyncReadStream,
1022         DynamicBuffer_v1, ReadHandler>& h,
1023       const DefaultCandidate& c) noexcept
1024     -> decltype(Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c))
1025   {
1026     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c);
1027   }
1028 };
1029 
1030 #endif // !defined(GENERATING_DOCUMENTATION)
1031 
1032 namespace detail
1033 {
1034   template <typename AsyncReadStream,
1035       typename DynamicBuffer_v1, typename ReadHandler>
1036   class read_until_delim_string_op_v1
1037     : public base_from_cancellation_state<ReadHandler>
1038   {
1039   public:
1040     template <typename BufferSequence>
1041     read_until_delim_string_op_v1(AsyncReadStream& stream,
1042         BufferSequence&& buffers,
1043         const std::string& delim, ReadHandler& handler)
1044       : base_from_cancellation_state<ReadHandler>(
1045           handler, enable_partial_cancellation()),
1046         stream_(stream),
1047         buffers_(static_cast<BufferSequence&&>(buffers)),
1048         delim_(delim),
1049         start_(0),
1050         search_position_(0),
1051         handler_(static_cast<ReadHandler&&>(handler))
1052     {
1053     }
1054 
1055     read_until_delim_string_op_v1(const read_until_delim_string_op_v1& other)
1056       : base_from_cancellation_state<ReadHandler>(other),
1057         stream_(other.stream_),
1058         buffers_(other.buffers_),
1059         delim_(other.delim_),
1060         start_(other.start_),
1061         search_position_(other.search_position_),
1062         handler_(other.handler_)
1063     {
1064     }
1065 
1066     read_until_delim_string_op_v1(read_until_delim_string_op_v1&& other)
1067       : base_from_cancellation_state<ReadHandler>(
1068           static_cast<base_from_cancellation_state<ReadHandler>&&>(other)),
1069         stream_(other.stream_),
1070         buffers_(static_cast<DynamicBuffer_v1&&>(other.buffers_)),
1071         delim_(static_cast<std::string&&>(other.delim_)),
1072         start_(other.start_),
1073         search_position_(other.search_position_),
1074         handler_(static_cast<ReadHandler&&>(other.handler_))
1075     {
1076     }
1077 
1078     void operator()(boost::system::error_code ec,
1079         std::size_t bytes_transferred, int start = 0)
1080     {
1081       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
1082       std::size_t bytes_to_read;
1083       switch (start_ = start)
1084       {
1085       case 1:
1086         for (;;)
1087         {
1088           {
1089             // Determine the range of the data to be searched.
1090             typedef typename DynamicBuffer_v1::const_buffers_type
1091               buffers_type;
1092             typedef buffers_iterator<buffers_type> iterator;
1093             buffers_type data_buffers = buffers_.data();
1094             iterator begin = iterator::begin(data_buffers);
1095             iterator start_pos = begin + search_position_;
1096             iterator end = iterator::end(data_buffers);
1097 
1098             // Look for a match.
1099             std::pair<iterator, bool> result = detail::partial_search(
1100                 start_pos, end, delim_.begin(), delim_.end());
1101             if (result.first != end && result.second)
1102             {
1103               // Full match. We're done.
1104               search_position_ = result.first - begin + delim_.length();
1105               bytes_to_read = 0;
1106             }
1107 
1108             // No match yet. Check if buffer is full.
1109             else if (buffers_.size() == buffers_.max_size())
1110             {
1111               search_position_ = not_found;
1112               bytes_to_read = 0;
1113             }
1114 
1115             // Need to read some more data.
1116             else
1117             {
1118               if (result.first != end)
1119               {
1120                 // Partial match. Next search needs to start from beginning of
1121                 // match.
1122                 search_position_ = result.first - begin;
1123               }
1124               else
1125               {
1126                 // Next search can start with the new data.
1127                 search_position_ = end - begin;
1128               }
1129 
1130               bytes_to_read = std::min<std::size_t>(
1131                     std::max<std::size_t>(512,
1132                       buffers_.capacity() - buffers_.size()),
1133                     std::min<std::size_t>(65536,
1134                       buffers_.max_size() - buffers_.size()));
1135             }
1136           }
1137 
1138           // Check if we're done.
1139           if (!start && bytes_to_read == 0)
1140             break;
1141 
1142           // Start a new asynchronous read operation to obtain more data.
1143           {
1144             BOOST_ASIO_HANDLER_LOCATION((
1145                   __FILE__, __LINE__, "async_read_until"));
1146             stream_.async_read_some(buffers_.prepare(bytes_to_read),
1147                 static_cast<read_until_delim_string_op_v1&&>(*this));
1148           }
1149           return; default:
1150           buffers_.commit(bytes_transferred);
1151           if (ec || bytes_transferred == 0)
1152             break;
1153           if (this->cancelled() != cancellation_type::none)
1154           {
1155             ec = error::operation_aborted;
1156             break;
1157           }
1158         }
1159 
1160         const boost::system::error_code result_ec =
1161           (search_position_ == not_found)
1162           ? error::not_found : ec;
1163 
1164         const std::size_t result_n =
1165           (ec || search_position_ == not_found)
1166           ? 0 : search_position_;
1167 
1168         static_cast<ReadHandler&&>(handler_)(result_ec, result_n);
1169       }
1170     }
1171 
1172   //private:
1173     AsyncReadStream& stream_;
1174     DynamicBuffer_v1 buffers_;
1175     std::string delim_;
1176     int start_;
1177     std::size_t search_position_;
1178     ReadHandler handler_;
1179   };
1180 
1181   template <typename AsyncReadStream,
1182       typename DynamicBuffer_v1, typename ReadHandler>
1183   inline bool asio_handler_is_continuation(
1184       read_until_delim_string_op_v1<AsyncReadStream,
1185         DynamicBuffer_v1, ReadHandler>* this_handler)
1186   {
1187     return this_handler->start_ == 0 ? true
1188       : boost_asio_handler_cont_helpers::is_continuation(
1189           this_handler->handler_);
1190   }
1191 
1192   template <typename AsyncReadStream>
1193   class initiate_async_read_until_delim_string_v1
1194   {
1195   public:
1196     typedef typename AsyncReadStream::executor_type executor_type;
1197 
1198     explicit initiate_async_read_until_delim_string_v1(AsyncReadStream& stream)
1199       : stream_(stream)
1200     {
1201     }
1202 
1203     executor_type get_executor() const noexcept
1204     {
1205       return stream_.get_executor();
1206     }
1207 
1208     template <typename ReadHandler, typename DynamicBuffer_v1>
1209     void operator()(ReadHandler&& handler,
1210         DynamicBuffer_v1&& buffers,
1211         const std::string& delim) const
1212     {
1213       // If you get an error on the following line it means that your handler
1214       // does not meet the documented type requirements for a ReadHandler.
1215       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
1216 
1217       non_const_lvalue<ReadHandler> handler2(handler);
1218       read_until_delim_string_op_v1<AsyncReadStream,
1219         decay_t<DynamicBuffer_v1>,
1220           decay_t<ReadHandler>>(
1221             stream_, static_cast<DynamicBuffer_v1&&>(buffers),
1222             delim, handler2.value)(boost::system::error_code(), 0, 1);
1223     }
1224 
1225   private:
1226     AsyncReadStream& stream_;
1227   };
1228 } // namespace detail
1229 
1230 #if !defined(GENERATING_DOCUMENTATION)
1231 
1232 template <template <typename, typename> class Associator,
1233     typename AsyncReadStream, typename DynamicBuffer_v1,
1234     typename ReadHandler, typename DefaultCandidate>
1235 struct associator<Associator,
1236     detail::read_until_delim_string_op_v1<AsyncReadStream,
1237       DynamicBuffer_v1, ReadHandler>,
1238     DefaultCandidate>
1239   : Associator<ReadHandler, DefaultCandidate>
1240 {
1241   static typename Associator<ReadHandler, DefaultCandidate>::type get(
1242       const detail::read_until_delim_string_op_v1<
1243         AsyncReadStream, DynamicBuffer_v1, ReadHandler>& h) noexcept
1244   {
1245     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_);
1246   }
1247 
1248   static auto get(
1249       const detail::read_until_delim_string_op_v1<
1250         AsyncReadStream, DynamicBuffer_v1, ReadHandler>& h,
1251       const DefaultCandidate& c) noexcept
1252     -> decltype(Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c))
1253   {
1254     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c);
1255   }
1256 };
1257 
1258 #endif // !defined(GENERATING_DOCUMENTATION)
1259 
1260 #if !defined(BOOST_ASIO_NO_EXTENSIONS)
1261 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
1262 
1263 namespace detail
1264 {
1265   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1266       typename RegEx, typename ReadHandler>
1267   class read_until_expr_op_v1
1268     : public base_from_cancellation_state<ReadHandler>
1269   {
1270   public:
1271     template <typename BufferSequence, typename Traits>
1272     read_until_expr_op_v1(AsyncReadStream& stream, BufferSequence&& buffers,
1273         const boost::basic_regex<char, Traits>& expr, ReadHandler& handler)
1274       : base_from_cancellation_state<ReadHandler>(
1275           handler, enable_partial_cancellation()),
1276         stream_(stream),
1277         buffers_(static_cast<BufferSequence&&>(buffers)),
1278         expr_(expr),
1279         start_(0),
1280         search_position_(0),
1281         handler_(static_cast<ReadHandler&&>(handler))
1282     {
1283     }
1284 
1285     read_until_expr_op_v1(const read_until_expr_op_v1& other)
1286       : base_from_cancellation_state<ReadHandler>(other),
1287         stream_(other.stream_),
1288         buffers_(other.buffers_),
1289         expr_(other.expr_),
1290         start_(other.start_),
1291         search_position_(other.search_position_),
1292         handler_(other.handler_)
1293     {
1294     }
1295 
1296     read_until_expr_op_v1(read_until_expr_op_v1&& other)
1297       : base_from_cancellation_state<ReadHandler>(
1298           static_cast<base_from_cancellation_state<ReadHandler>&&>(other)),
1299         stream_(other.stream_),
1300         buffers_(static_cast<DynamicBuffer_v1&&>(other.buffers_)),
1301         expr_(other.expr_),
1302         start_(other.start_),
1303         search_position_(other.search_position_),
1304         handler_(static_cast<ReadHandler&&>(other.handler_))
1305     {
1306     }
1307 
1308     void operator()(boost::system::error_code ec,
1309         std::size_t bytes_transferred, int start = 0)
1310     {
1311       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
1312       std::size_t bytes_to_read;
1313       switch (start_ = start)
1314       {
1315       case 1:
1316         for (;;)
1317         {
1318           {
1319             // Determine the range of the data to be searched.
1320             typedef typename DynamicBuffer_v1::const_buffers_type
1321               buffers_type;
1322             typedef buffers_iterator<buffers_type> iterator;
1323             buffers_type data_buffers = buffers_.data();
1324             iterator begin = iterator::begin(data_buffers);
1325             iterator start_pos = begin + search_position_;
1326             iterator end = iterator::end(data_buffers);
1327 
1328             // Look for a match.
1329             boost::match_results<iterator,
1330               typename std::vector<boost::sub_match<iterator>>::allocator_type>
1331                 match_results;
1332             bool match = regex_search(start_pos, end,
1333                 match_results, expr_, regex_match_flags());
1334             if (match && match_results[0].matched)
1335             {
1336               // Full match. We're done.
1337               search_position_ = match_results[0].second - begin;
1338               bytes_to_read = 0;
1339             }
1340 
1341             // No match yet. Check if buffer is full.
1342             else if (buffers_.size() == buffers_.max_size())
1343             {
1344               search_position_ = not_found;
1345               bytes_to_read = 0;
1346             }
1347 
1348             // Need to read some more data.
1349             else
1350             {
1351               if (match)
1352               {
1353                 // Partial match. Next search needs to start from beginning of
1354                 // match.
1355                 search_position_ = match_results[0].first - begin;
1356               }
1357               else
1358               {
1359                 // Next search can start with the new data.
1360                 search_position_ = end - begin;
1361               }
1362 
1363               bytes_to_read = std::min<std::size_t>(
1364                     std::max<std::size_t>(512,
1365                       buffers_.capacity() - buffers_.size()),
1366                     std::min<std::size_t>(65536,
1367                       buffers_.max_size() - buffers_.size()));
1368             }
1369           }
1370 
1371           // Check if we're done.
1372           if (!start && bytes_to_read == 0)
1373             break;
1374 
1375           // Start a new asynchronous read operation to obtain more data.
1376           {
1377             BOOST_ASIO_HANDLER_LOCATION((
1378                   __FILE__, __LINE__, "async_read_until"));
1379             stream_.async_read_some(buffers_.prepare(bytes_to_read),
1380                 static_cast<read_until_expr_op_v1&&>(*this));
1381           }
1382           return; default:
1383           buffers_.commit(bytes_transferred);
1384           if (ec || bytes_transferred == 0)
1385             break;
1386           if (this->cancelled() != cancellation_type::none)
1387           {
1388             ec = error::operation_aborted;
1389             break;
1390           }
1391         }
1392 
1393         const boost::system::error_code result_ec =
1394           (search_position_ == not_found)
1395           ? error::not_found : ec;
1396 
1397         const std::size_t result_n =
1398           (ec || search_position_ == not_found)
1399           ? 0 : search_position_;
1400 
1401         static_cast<ReadHandler&&>(handler_)(result_ec, result_n);
1402       }
1403     }
1404 
1405   //private:
1406     AsyncReadStream& stream_;
1407     DynamicBuffer_v1 buffers_;
1408     RegEx expr_;
1409     int start_;
1410     std::size_t search_position_;
1411     ReadHandler handler_;
1412   };
1413 
1414   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1415       typename RegEx, typename ReadHandler>
1416   inline bool asio_handler_is_continuation(
1417       read_until_expr_op_v1<AsyncReadStream,
1418         DynamicBuffer_v1, RegEx, ReadHandler>* this_handler)
1419   {
1420     return this_handler->start_ == 0 ? true
1421       : boost_asio_handler_cont_helpers::is_continuation(
1422           this_handler->handler_);
1423   }
1424 
1425   template <typename AsyncReadStream>
1426   class initiate_async_read_until_expr_v1
1427   {
1428   public:
1429     typedef typename AsyncReadStream::executor_type executor_type;
1430 
1431     explicit initiate_async_read_until_expr_v1(AsyncReadStream& stream)
1432       : stream_(stream)
1433     {
1434     }
1435 
1436     executor_type get_executor() const noexcept
1437     {
1438       return stream_.get_executor();
1439     }
1440 
1441     template <typename ReadHandler, typename DynamicBuffer_v1, typename RegEx>
1442     void operator()(ReadHandler&& handler,
1443         DynamicBuffer_v1&& buffers, const RegEx& expr) const
1444     {
1445       // If you get an error on the following line it means that your handler
1446       // does not meet the documented type requirements for a ReadHandler.
1447       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
1448 
1449       non_const_lvalue<ReadHandler> handler2(handler);
1450       read_until_expr_op_v1<AsyncReadStream,
1451         decay_t<DynamicBuffer_v1>,
1452           RegEx, decay_t<ReadHandler>>(
1453             stream_, static_cast<DynamicBuffer_v1&&>(buffers),
1454             expr, handler2.value)(boost::system::error_code(), 0, 1);
1455     }
1456 
1457   private:
1458     AsyncReadStream& stream_;
1459   };
1460 } // namespace detail
1461 
1462 #if !defined(GENERATING_DOCUMENTATION)
1463 
1464 template <template <typename, typename> class Associator,
1465     typename AsyncReadStream, typename DynamicBuffer_v1,
1466     typename RegEx, typename ReadHandler, typename DefaultCandidate>
1467 struct associator<Associator,
1468     detail::read_until_expr_op_v1<AsyncReadStream,
1469       DynamicBuffer_v1, RegEx, ReadHandler>,
1470     DefaultCandidate>
1471   : Associator<ReadHandler, DefaultCandidate>
1472 {
1473   static typename Associator<ReadHandler, DefaultCandidate>::type get(
1474       const detail::read_until_expr_op_v1<AsyncReadStream,
1475         DynamicBuffer_v1, RegEx, ReadHandler>& h) noexcept
1476   {
1477     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_);
1478   }
1479 
1480   static auto get(
1481       const detail::read_until_expr_op_v1<AsyncReadStream,
1482         DynamicBuffer_v1, RegEx, ReadHandler>& h,
1483       const DefaultCandidate& c) noexcept
1484     -> decltype(Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c))
1485   {
1486     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c);
1487   }
1488 };
1489 
1490 #endif // !defined(GENERATING_DOCUMENTATION)
1491 
1492 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
1493 
1494 namespace detail
1495 {
1496   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1497       typename MatchCondition, typename ReadHandler>
1498   class read_until_match_op_v1
1499     : public base_from_cancellation_state<ReadHandler>
1500   {
1501   public:
1502     template <typename BufferSequence>
1503     read_until_match_op_v1(AsyncReadStream& stream,
1504         BufferSequence&& buffers,
1505         MatchCondition match_condition, ReadHandler& handler)
1506       : base_from_cancellation_state<ReadHandler>(
1507           handler, enable_partial_cancellation()),
1508         stream_(stream),
1509         buffers_(static_cast<BufferSequence&&>(buffers)),
1510         match_condition_(match_condition),
1511         start_(0),
1512         search_position_(0),
1513         handler_(static_cast<ReadHandler&&>(handler))
1514     {
1515     }
1516 
1517     read_until_match_op_v1(const read_until_match_op_v1& other)
1518       : base_from_cancellation_state<ReadHandler>(other),
1519         stream_(other.stream_),
1520         buffers_(other.buffers_),
1521         match_condition_(other.match_condition_),
1522         start_(other.start_),
1523         search_position_(other.search_position_),
1524         handler_(other.handler_)
1525     {
1526     }
1527 
1528     read_until_match_op_v1(read_until_match_op_v1&& other)
1529       : base_from_cancellation_state<ReadHandler>(
1530           static_cast<base_from_cancellation_state<ReadHandler>&&>(other)),
1531         stream_(other.stream_),
1532         buffers_(static_cast<DynamicBuffer_v1&&>(other.buffers_)),
1533         match_condition_(other.match_condition_),
1534         start_(other.start_),
1535         search_position_(other.search_position_),
1536         handler_(static_cast<ReadHandler&&>(other.handler_))
1537     {
1538     }
1539 
1540     void operator()(boost::system::error_code ec,
1541         std::size_t bytes_transferred, int start = 0)
1542     {
1543       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
1544       std::size_t bytes_to_read;
1545       switch (start_ = start)
1546       {
1547       case 1:
1548         for (;;)
1549         {
1550           {
1551             // Determine the range of the data to be searched.
1552             typedef typename DynamicBuffer_v1::const_buffers_type
1553               buffers_type;
1554             typedef buffers_iterator<buffers_type> iterator;
1555             buffers_type data_buffers = buffers_.data();
1556             iterator begin = iterator::begin(data_buffers);
1557             iterator start_pos = begin + search_position_;
1558             iterator end = iterator::end(data_buffers);
1559 
1560             // Look for a match.
1561             std::pair<iterator, bool> result = match_condition_(start_pos, end);
1562             if (result.second)
1563             {
1564               // Full match. We're done.
1565               search_position_ = result.first - begin;
1566               bytes_to_read = 0;
1567             }
1568 
1569             // No match yet. Check if buffer is full.
1570             else if (buffers_.size() == buffers_.max_size())
1571             {
1572               search_position_ = not_found;
1573               bytes_to_read = 0;
1574             }
1575 
1576             // Need to read some more data.
1577             else
1578             {
1579               if (result.first != end)
1580               {
1581                 // Partial match. Next search needs to start from beginning of
1582                 // match.
1583                 search_position_ = result.first - begin;
1584               }
1585               else
1586               {
1587                 // Next search can start with the new data.
1588                 search_position_ = end - begin;
1589               }
1590 
1591               bytes_to_read = std::min<std::size_t>(
1592                     std::max<std::size_t>(512,
1593                       buffers_.capacity() - buffers_.size()),
1594                     std::min<std::size_t>(65536,
1595                       buffers_.max_size() - buffers_.size()));
1596             }
1597           }
1598 
1599           // Check if we're done.
1600           if (!start && bytes_to_read == 0)
1601             break;
1602 
1603           // Start a new asynchronous read operation to obtain more data.
1604           {
1605             BOOST_ASIO_HANDLER_LOCATION((
1606                   __FILE__, __LINE__, "async_read_until"));
1607             stream_.async_read_some(buffers_.prepare(bytes_to_read),
1608                 static_cast<read_until_match_op_v1&&>(*this));
1609           }
1610           return; default:
1611           buffers_.commit(bytes_transferred);
1612           if (ec || bytes_transferred == 0)
1613             break;
1614           if (this->cancelled() != cancellation_type::none)
1615           {
1616             ec = error::operation_aborted;
1617             break;
1618           }
1619         }
1620 
1621         const boost::system::error_code result_ec =
1622           (search_position_ == not_found)
1623           ? error::not_found : ec;
1624 
1625         const std::size_t result_n =
1626           (ec || search_position_ == not_found)
1627           ? 0 : search_position_;
1628 
1629         static_cast<ReadHandler&&>(handler_)(result_ec, result_n);
1630       }
1631     }
1632 
1633   //private:
1634     AsyncReadStream& stream_;
1635     DynamicBuffer_v1 buffers_;
1636     MatchCondition match_condition_;
1637     int start_;
1638     std::size_t search_position_;
1639     ReadHandler handler_;
1640   };
1641 
1642   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1643       typename MatchCondition, typename ReadHandler>
1644   inline bool asio_handler_is_continuation(
1645       read_until_match_op_v1<AsyncReadStream, DynamicBuffer_v1,
1646         MatchCondition, ReadHandler>* this_handler)
1647   {
1648     return this_handler->start_ == 0 ? true
1649       : boost_asio_handler_cont_helpers::is_continuation(
1650           this_handler->handler_);
1651   }
1652 
1653   template <typename AsyncReadStream>
1654   class initiate_async_read_until_match_v1
1655   {
1656   public:
1657     typedef typename AsyncReadStream::executor_type executor_type;
1658 
1659     explicit initiate_async_read_until_match_v1(AsyncReadStream& stream)
1660       : stream_(stream)
1661     {
1662     }
1663 
1664     executor_type get_executor() const noexcept
1665     {
1666       return stream_.get_executor();
1667     }
1668 
1669     template <typename ReadHandler,
1670         typename DynamicBuffer_v1, typename MatchCondition>
1671     void operator()(ReadHandler&& handler,
1672         DynamicBuffer_v1&& buffers,
1673         MatchCondition match_condition) const
1674     {
1675       // If you get an error on the following line it means that your handler
1676       // does not meet the documented type requirements for a ReadHandler.
1677       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
1678 
1679       non_const_lvalue<ReadHandler> handler2(handler);
1680       read_until_match_op_v1<AsyncReadStream,
1681         decay_t<DynamicBuffer_v1>,
1682           MatchCondition, decay_t<ReadHandler>>(
1683             stream_, static_cast<DynamicBuffer_v1&&>(buffers),
1684             match_condition, handler2.value)(boost::system::error_code(), 0, 1);
1685     }
1686 
1687   private:
1688     AsyncReadStream& stream_;
1689   };
1690 } // namespace detail
1691 
1692 #if !defined(GENERATING_DOCUMENTATION)
1693 
1694 template <template <typename, typename> class Associator,
1695     typename AsyncReadStream, typename DynamicBuffer_v1,
1696     typename MatchCondition, typename ReadHandler, typename DefaultCandidate>
1697 struct associator<Associator,
1698     detail::read_until_match_op_v1<AsyncReadStream,
1699       DynamicBuffer_v1, MatchCondition, ReadHandler>,
1700     DefaultCandidate>
1701   : Associator<ReadHandler, DefaultCandidate>
1702 {
1703   static typename Associator<ReadHandler, DefaultCandidate>::type get(
1704       const detail::read_until_match_op_v1<AsyncReadStream,
1705         DynamicBuffer_v1, MatchCondition, ReadHandler>& h) noexcept
1706   {
1707     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_);
1708   }
1709 
1710   static auto get(
1711       const detail::read_until_match_op_v1<AsyncReadStream,
1712         DynamicBuffer_v1, MatchCondition, ReadHandler>& h,
1713       const DefaultCandidate& c) noexcept
1714     -> decltype(Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c))
1715   {
1716     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c);
1717   }
1718 };
1719 
1720 #endif // !defined(GENERATING_DOCUMENTATION)
1721 
1722 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
1723 #endif // !defined(BOOST_ASIO_NO_DYNAMIC_BUFFER_V1)
1724 
1725 namespace detail
1726 {
1727   template <typename AsyncReadStream,
1728       typename DynamicBuffer_v2, typename ReadHandler>
1729   class read_until_delim_op_v2
1730     : public base_from_cancellation_state<ReadHandler>
1731   {
1732   public:
1733     template <typename BufferSequence>
1734     read_until_delim_op_v2(AsyncReadStream& stream,
1735         BufferSequence&& buffers,
1736         char delim, ReadHandler& handler)
1737       : base_from_cancellation_state<ReadHandler>(
1738           handler, enable_partial_cancellation()),
1739         stream_(stream),
1740         buffers_(static_cast<BufferSequence&&>(buffers)),
1741         delim_(delim),
1742         start_(0),
1743         search_position_(0),
1744         bytes_to_read_(0),
1745         handler_(static_cast<ReadHandler&&>(handler))
1746     {
1747     }
1748 
1749     read_until_delim_op_v2(const read_until_delim_op_v2& other)
1750       : base_from_cancellation_state<ReadHandler>(other),
1751         stream_(other.stream_),
1752         buffers_(other.buffers_),
1753         delim_(other.delim_),
1754         start_(other.start_),
1755         search_position_(other.search_position_),
1756         bytes_to_read_(other.bytes_to_read_),
1757         handler_(other.handler_)
1758     {
1759     }
1760 
1761     read_until_delim_op_v2(read_until_delim_op_v2&& other)
1762       : base_from_cancellation_state<ReadHandler>(
1763           static_cast<base_from_cancellation_state<ReadHandler>&&>(other)),
1764         stream_(other.stream_),
1765         buffers_(static_cast<DynamicBuffer_v2&&>(other.buffers_)),
1766         delim_(other.delim_),
1767         start_(other.start_),
1768         search_position_(other.search_position_),
1769         bytes_to_read_(other.bytes_to_read_),
1770         handler_(static_cast<ReadHandler&&>(other.handler_))
1771     {
1772     }
1773 
1774     void operator()(boost::system::error_code ec,
1775         std::size_t bytes_transferred, int start = 0)
1776     {
1777       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
1778       std::size_t pos;
1779       switch (start_ = start)
1780       {
1781       case 1:
1782         for (;;)
1783         {
1784           {
1785             // Determine the range of the data to be searched.
1786             typedef typename DynamicBuffer_v2::const_buffers_type
1787               buffers_type;
1788             typedef buffers_iterator<buffers_type> iterator;
1789             buffers_type data_buffers =
1790               const_cast<const DynamicBuffer_v2&>(buffers_).data(
1791                   0, buffers_.size());
1792             iterator begin = iterator::begin(data_buffers);
1793             iterator start_pos = begin + search_position_;
1794             iterator end = iterator::end(data_buffers);
1795 
1796             // Look for a match.
1797             iterator iter = std::find(start_pos, end, delim_);
1798             if (iter != end)
1799             {
1800               // Found a match. We're done.
1801               search_position_ = iter - begin + 1;
1802               bytes_to_read_ = 0;
1803             }
1804 
1805             // No match yet. Check if buffer is full.
1806             else if (buffers_.size() == buffers_.max_size())
1807             {
1808               search_position_ = not_found;
1809               bytes_to_read_ = 0;
1810             }
1811 
1812             // Need to read some more data.
1813             else
1814             {
1815               // Next search can start with the new data.
1816               search_position_ = end - begin;
1817               bytes_to_read_ = std::min<std::size_t>(
1818                     std::max<std::size_t>(512,
1819                       buffers_.capacity() - buffers_.size()),
1820                     std::min<std::size_t>(65536,
1821                       buffers_.max_size() - buffers_.size()));
1822             }
1823           }
1824 
1825           // Check if we're done.
1826           if (!start && bytes_to_read_ == 0)
1827             break;
1828 
1829           // Start a new asynchronous read operation to obtain more data.
1830           pos = buffers_.size();
1831           buffers_.grow(bytes_to_read_);
1832           {
1833             BOOST_ASIO_HANDLER_LOCATION((
1834                   __FILE__, __LINE__, "async_read_until"));
1835             stream_.async_read_some(buffers_.data(pos, bytes_to_read_),
1836                 static_cast<read_until_delim_op_v2&&>(*this));
1837           }
1838           return; default:
1839           buffers_.shrink(bytes_to_read_ - bytes_transferred);
1840           if (ec || bytes_transferred == 0)
1841             break;
1842           if (this->cancelled() != cancellation_type::none)
1843           {
1844             ec = error::operation_aborted;
1845             break;
1846           }
1847         }
1848 
1849         const boost::system::error_code result_ec =
1850           (search_position_ == not_found)
1851           ? error::not_found : ec;
1852 
1853         const std::size_t result_n =
1854           (ec || search_position_ == not_found)
1855           ? 0 : search_position_;
1856 
1857         static_cast<ReadHandler&&>(handler_)(result_ec, result_n);
1858       }
1859     }
1860 
1861   //private:
1862     AsyncReadStream& stream_;
1863     DynamicBuffer_v2 buffers_;
1864     char delim_;
1865     int start_;
1866     std::size_t search_position_;
1867     std::size_t bytes_to_read_;
1868     ReadHandler handler_;
1869   };
1870 
1871   template <typename AsyncReadStream,
1872       typename DynamicBuffer_v2, typename ReadHandler>
1873   inline bool asio_handler_is_continuation(
1874       read_until_delim_op_v2<AsyncReadStream,
1875         DynamicBuffer_v2, ReadHandler>* this_handler)
1876   {
1877     return this_handler->start_ == 0 ? true
1878       : boost_asio_handler_cont_helpers::is_continuation(
1879           this_handler->handler_);
1880   }
1881 
1882   template <typename AsyncReadStream>
1883   class initiate_async_read_until_delim_v2
1884   {
1885   public:
1886     typedef typename AsyncReadStream::executor_type executor_type;
1887 
1888     explicit initiate_async_read_until_delim_v2(AsyncReadStream& stream)
1889       : stream_(stream)
1890     {
1891     }
1892 
1893     executor_type get_executor() const noexcept
1894     {
1895       return stream_.get_executor();
1896     }
1897 
1898     template <typename ReadHandler, typename DynamicBuffer_v2>
1899     void operator()(ReadHandler&& handler,
1900         DynamicBuffer_v2&& buffers, char delim) const
1901     {
1902       // If you get an error on the following line it means that your handler
1903       // does not meet the documented type requirements for a ReadHandler.
1904       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
1905 
1906       non_const_lvalue<ReadHandler> handler2(handler);
1907       read_until_delim_op_v2<AsyncReadStream,
1908         decay_t<DynamicBuffer_v2>,
1909           decay_t<ReadHandler>>(
1910             stream_, static_cast<DynamicBuffer_v2&&>(buffers),
1911             delim, handler2.value)(boost::system::error_code(), 0, 1);
1912     }
1913 
1914   private:
1915     AsyncReadStream& stream_;
1916   };
1917 } // namespace detail
1918 
1919 #if !defined(GENERATING_DOCUMENTATION)
1920 
1921 template <template <typename, typename> class Associator,
1922     typename AsyncReadStream, typename DynamicBuffer_v2,
1923     typename ReadHandler, typename DefaultCandidate>
1924 struct associator<Associator,
1925     detail::read_until_delim_op_v2<AsyncReadStream,
1926       DynamicBuffer_v2, ReadHandler>,
1927     DefaultCandidate>
1928   : Associator<ReadHandler, DefaultCandidate>
1929 {
1930   static typename Associator<ReadHandler, DefaultCandidate>::type get(
1931       const detail::read_until_delim_op_v2<AsyncReadStream,
1932         DynamicBuffer_v2, ReadHandler>& h) noexcept
1933   {
1934     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_);
1935   }
1936 
1937   static auto get(
1938       const detail::read_until_delim_op_v2<AsyncReadStream,
1939         DynamicBuffer_v2, ReadHandler>& h,
1940       const DefaultCandidate& c) noexcept
1941     -> decltype(Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c))
1942   {
1943     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c);
1944   }
1945 };
1946 
1947 #endif // !defined(GENERATING_DOCUMENTATION)
1948 
1949 namespace detail
1950 {
1951   template <typename AsyncReadStream,
1952       typename DynamicBuffer_v2, typename ReadHandler>
1953   class read_until_delim_string_op_v2
1954     : public base_from_cancellation_state<ReadHandler>
1955   {
1956   public:
1957     template <typename BufferSequence>
1958     read_until_delim_string_op_v2(AsyncReadStream& stream,
1959         BufferSequence&& buffers,
1960         const std::string& delim, ReadHandler& handler)
1961       : base_from_cancellation_state<ReadHandler>(
1962           handler, enable_partial_cancellation()),
1963         stream_(stream),
1964         buffers_(static_cast<BufferSequence&&>(buffers)),
1965         delim_(delim),
1966         start_(0),
1967         search_position_(0),
1968         bytes_to_read_(0),
1969         handler_(static_cast<ReadHandler&&>(handler))
1970     {
1971     }
1972 
1973     read_until_delim_string_op_v2(const read_until_delim_string_op_v2& other)
1974       : base_from_cancellation_state<ReadHandler>(other),
1975         stream_(other.stream_),
1976         buffers_(other.buffers_),
1977         delim_(other.delim_),
1978         start_(other.start_),
1979         search_position_(other.search_position_),
1980         bytes_to_read_(other.bytes_to_read_),
1981         handler_(other.handler_)
1982     {
1983     }
1984 
1985     read_until_delim_string_op_v2(read_until_delim_string_op_v2&& other)
1986       : base_from_cancellation_state<ReadHandler>(
1987           static_cast<base_from_cancellation_state<ReadHandler>&&>(other)),
1988         stream_(other.stream_),
1989         buffers_(static_cast<DynamicBuffer_v2&&>(other.buffers_)),
1990         delim_(static_cast<std::string&&>(other.delim_)),
1991         start_(other.start_),
1992         search_position_(other.search_position_),
1993         bytes_to_read_(other.bytes_to_read_),
1994         handler_(static_cast<ReadHandler&&>(other.handler_))
1995     {
1996     }
1997 
1998     void operator()(boost::system::error_code ec,
1999         std::size_t bytes_transferred, int start = 0)
2000     {
2001       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
2002       std::size_t pos;
2003       switch (start_ = start)
2004       {
2005       case 1:
2006         for (;;)
2007         {
2008           {
2009             // Determine the range of the data to be searched.
2010             typedef typename DynamicBuffer_v2::const_buffers_type
2011               buffers_type;
2012             typedef buffers_iterator<buffers_type> iterator;
2013             buffers_type data_buffers =
2014               const_cast<const DynamicBuffer_v2&>(buffers_).data(
2015                   0, buffers_.size());
2016             iterator begin = iterator::begin(data_buffers);
2017             iterator start_pos = begin + search_position_;
2018             iterator end = iterator::end(data_buffers);
2019 
2020             // Look for a match.
2021             std::pair<iterator, bool> result = detail::partial_search(
2022                 start_pos, end, delim_.begin(), delim_.end());
2023             if (result.first != end && result.second)
2024             {
2025               // Full match. We're done.
2026               search_position_ = result.first - begin + delim_.length();
2027               bytes_to_read_ = 0;
2028             }
2029 
2030             // No match yet. Check if buffer is full.
2031             else if (buffers_.size() == buffers_.max_size())
2032             {
2033               search_position_ = not_found;
2034               bytes_to_read_ = 0;
2035             }
2036 
2037             // Need to read some more data.
2038             else
2039             {
2040               if (result.first != end)
2041               {
2042                 // Partial match. Next search needs to start from beginning of
2043                 // match.
2044                 search_position_ = result.first - begin;
2045               }
2046               else
2047               {
2048                 // Next search can start with the new data.
2049                 search_position_ = end - begin;
2050               }
2051 
2052               bytes_to_read_ = std::min<std::size_t>(
2053                     std::max<std::size_t>(512,
2054                       buffers_.capacity() - buffers_.size()),
2055                     std::min<std::size_t>(65536,
2056                       buffers_.max_size() - buffers_.size()));
2057             }
2058           }
2059 
2060           // Check if we're done.
2061           if (!start && bytes_to_read_ == 0)
2062             break;
2063 
2064           // Start a new asynchronous read operation to obtain more data.
2065           pos = buffers_.size();
2066           buffers_.grow(bytes_to_read_);
2067           {
2068             BOOST_ASIO_HANDLER_LOCATION((
2069                   __FILE__, __LINE__, "async_read_until"));
2070             stream_.async_read_some(buffers_.data(pos, bytes_to_read_),
2071                 static_cast<read_until_delim_string_op_v2&&>(*this));
2072           }
2073           return; default:
2074           buffers_.shrink(bytes_to_read_ - bytes_transferred);
2075           if (ec || bytes_transferred == 0)
2076             break;
2077           if (this->cancelled() != cancellation_type::none)
2078           {
2079             ec = error::operation_aborted;
2080             break;
2081           }
2082         }
2083 
2084         const boost::system::error_code result_ec =
2085           (search_position_ == not_found)
2086           ? error::not_found : ec;
2087 
2088         const std::size_t result_n =
2089           (ec || search_position_ == not_found)
2090           ? 0 : search_position_;
2091 
2092         static_cast<ReadHandler&&>(handler_)(result_ec, result_n);
2093       }
2094     }
2095 
2096   //private:
2097     AsyncReadStream& stream_;
2098     DynamicBuffer_v2 buffers_;
2099     std::string delim_;
2100     int start_;
2101     std::size_t search_position_;
2102     std::size_t bytes_to_read_;
2103     ReadHandler handler_;
2104   };
2105 
2106   template <typename AsyncReadStream,
2107       typename DynamicBuffer_v2, typename ReadHandler>
2108   inline bool asio_handler_is_continuation(
2109       read_until_delim_string_op_v2<AsyncReadStream,
2110         DynamicBuffer_v2, ReadHandler>* this_handler)
2111   {
2112     return this_handler->start_ == 0 ? true
2113       : boost_asio_handler_cont_helpers::is_continuation(
2114           this_handler->handler_);
2115   }
2116 
2117   template <typename AsyncReadStream>
2118   class initiate_async_read_until_delim_string_v2
2119   {
2120   public:
2121     typedef typename AsyncReadStream::executor_type executor_type;
2122 
2123     explicit initiate_async_read_until_delim_string_v2(AsyncReadStream& stream)
2124       : stream_(stream)
2125     {
2126     }
2127 
2128     executor_type get_executor() const noexcept
2129     {
2130       return stream_.get_executor();
2131     }
2132 
2133     template <typename ReadHandler, typename DynamicBuffer_v2>
2134     void operator()(ReadHandler&& handler,
2135         DynamicBuffer_v2&& buffers,
2136         const std::string& delim) const
2137     {
2138       // If you get an error on the following line it means that your handler
2139       // does not meet the documented type requirements for a ReadHandler.
2140       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
2141 
2142       non_const_lvalue<ReadHandler> handler2(handler);
2143       read_until_delim_string_op_v2<AsyncReadStream,
2144         decay_t<DynamicBuffer_v2>,
2145           decay_t<ReadHandler>>(
2146             stream_, static_cast<DynamicBuffer_v2&&>(buffers),
2147             delim, handler2.value)(boost::system::error_code(), 0, 1);
2148     }
2149 
2150   private:
2151     AsyncReadStream& stream_;
2152   };
2153 } // namespace detail
2154 
2155 #if !defined(GENERATING_DOCUMENTATION)
2156 
2157 template <template <typename, typename> class Associator,
2158     typename AsyncReadStream, typename DynamicBuffer_v2,
2159     typename ReadHandler, typename DefaultCandidate>
2160 struct associator<Associator,
2161     detail::read_until_delim_string_op_v2<AsyncReadStream,
2162       DynamicBuffer_v2, ReadHandler>,
2163     DefaultCandidate>
2164   : Associator<ReadHandler, DefaultCandidate>
2165 {
2166   static typename Associator<ReadHandler, DefaultCandidate>::type get(
2167       const detail::read_until_delim_string_op_v2<
2168         AsyncReadStream, DynamicBuffer_v2, ReadHandler>& h) noexcept
2169   {
2170     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_);
2171   }
2172 
2173   static auto get(
2174       const detail::read_until_delim_string_op_v2<
2175         AsyncReadStream, DynamicBuffer_v2, ReadHandler>& h,
2176       const DefaultCandidate& c) noexcept
2177     -> decltype(Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c))
2178   {
2179     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c);
2180   }
2181 };
2182 
2183 #endif // !defined(GENERATING_DOCUMENTATION)
2184 
2185 #if !defined(BOOST_ASIO_NO_EXTENSIONS)
2186 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
2187 
2188 namespace detail
2189 {
2190   template <typename AsyncReadStream, typename DynamicBuffer_v2,
2191       typename RegEx, typename ReadHandler>
2192   class read_until_expr_op_v2
2193     : public base_from_cancellation_state<ReadHandler>
2194   {
2195   public:
2196     template <typename BufferSequence, typename Traits>
2197     read_until_expr_op_v2(AsyncReadStream& stream, BufferSequence&& buffers,
2198         const boost::basic_regex<char, Traits>& expr, ReadHandler& handler)
2199       : base_from_cancellation_state<ReadHandler>(
2200           handler, enable_partial_cancellation()),
2201         stream_(stream),
2202         buffers_(static_cast<BufferSequence&&>(buffers)),
2203         expr_(expr),
2204         start_(0),
2205         search_position_(0),
2206         bytes_to_read_(0),
2207         handler_(static_cast<ReadHandler&&>(handler))
2208     {
2209     }
2210 
2211     read_until_expr_op_v2(const read_until_expr_op_v2& other)
2212       : base_from_cancellation_state<ReadHandler>(other),
2213         stream_(other.stream_),
2214         buffers_(other.buffers_),
2215         expr_(other.expr_),
2216         start_(other.start_),
2217         search_position_(other.search_position_),
2218         bytes_to_read_(other.bytes_to_read_),
2219         handler_(other.handler_)
2220     {
2221     }
2222 
2223     read_until_expr_op_v2(read_until_expr_op_v2&& other)
2224       : base_from_cancellation_state<ReadHandler>(
2225           static_cast<base_from_cancellation_state<ReadHandler>&&>(other)),
2226         stream_(other.stream_),
2227         buffers_(static_cast<DynamicBuffer_v2&&>(other.buffers_)),
2228         expr_(other.expr_),
2229         start_(other.start_),
2230         search_position_(other.search_position_),
2231         bytes_to_read_(other.bytes_to_read_),
2232         handler_(static_cast<ReadHandler&&>(other.handler_))
2233     {
2234     }
2235 
2236     void operator()(boost::system::error_code ec,
2237         std::size_t bytes_transferred, int start = 0)
2238     {
2239       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
2240       std::size_t pos;
2241       switch (start_ = start)
2242       {
2243       case 1:
2244         for (;;)
2245         {
2246           {
2247             // Determine the range of the data to be searched.
2248             typedef typename DynamicBuffer_v2::const_buffers_type
2249               buffers_type;
2250             typedef buffers_iterator<buffers_type> iterator;
2251             buffers_type data_buffers =
2252               const_cast<const DynamicBuffer_v2&>(buffers_).data(
2253                   0, buffers_.size());
2254             iterator begin = iterator::begin(data_buffers);
2255             iterator start_pos = begin + search_position_;
2256             iterator end = iterator::end(data_buffers);
2257 
2258             // Look for a match.
2259             boost::match_results<iterator,
2260               typename std::vector<boost::sub_match<iterator>>::allocator_type>
2261                 match_results;
2262             bool match = regex_search(start_pos, end,
2263                 match_results, expr_, regex_match_flags());
2264             if (match && match_results[0].matched)
2265             {
2266               // Full match. We're done.
2267               search_position_ = match_results[0].second - begin;
2268               bytes_to_read_ = 0;
2269             }
2270 
2271             // No match yet. Check if buffer is full.
2272             else if (buffers_.size() == buffers_.max_size())
2273             {
2274               search_position_ = not_found;
2275               bytes_to_read_ = 0;
2276             }
2277 
2278             // Need to read some more data.
2279             else
2280             {
2281               if (match)
2282               {
2283                 // Partial match. Next search needs to start from beginning of
2284                 // match.
2285                 search_position_ = match_results[0].first - begin;
2286               }
2287               else
2288               {
2289                 // Next search can start with the new data.
2290                 search_position_ = end - begin;
2291               }
2292 
2293               bytes_to_read_ = std::min<std::size_t>(
2294                     std::max<std::size_t>(512,
2295                       buffers_.capacity() - buffers_.size()),
2296                     std::min<std::size_t>(65536,
2297                       buffers_.max_size() - buffers_.size()));
2298             }
2299           }
2300 
2301           // Check if we're done.
2302           if (!start && bytes_to_read_ == 0)
2303             break;
2304 
2305           // Start a new asynchronous read operation to obtain more data.
2306           pos = buffers_.size();
2307           buffers_.grow(bytes_to_read_);
2308           {
2309             BOOST_ASIO_HANDLER_LOCATION((
2310                   __FILE__, __LINE__, "async_read_until"));
2311             stream_.async_read_some(buffers_.data(pos, bytes_to_read_),
2312                 static_cast<read_until_expr_op_v2&&>(*this));
2313           }
2314           return; default:
2315           buffers_.shrink(bytes_to_read_ - bytes_transferred);
2316           if (ec || bytes_transferred == 0)
2317             break;
2318           if (this->cancelled() != cancellation_type::none)
2319           {
2320             ec = error::operation_aborted;
2321             break;
2322           }
2323         }
2324 
2325         const boost::system::error_code result_ec =
2326           (search_position_ == not_found)
2327           ? error::not_found : ec;
2328 
2329         const std::size_t result_n =
2330           (ec || search_position_ == not_found)
2331           ? 0 : search_position_;
2332 
2333         static_cast<ReadHandler&&>(handler_)(result_ec, result_n);
2334       }
2335     }
2336 
2337   //private:
2338     AsyncReadStream& stream_;
2339     DynamicBuffer_v2 buffers_;
2340     RegEx expr_;
2341     int start_;
2342     std::size_t search_position_;
2343     std::size_t bytes_to_read_;
2344     ReadHandler handler_;
2345   };
2346 
2347   template <typename AsyncReadStream, typename DynamicBuffer_v2,
2348       typename RegEx, typename ReadHandler>
2349   inline bool asio_handler_is_continuation(
2350       read_until_expr_op_v2<AsyncReadStream,
2351         DynamicBuffer_v2, RegEx, ReadHandler>* this_handler)
2352   {
2353     return this_handler->start_ == 0 ? true
2354       : boost_asio_handler_cont_helpers::is_continuation(
2355           this_handler->handler_);
2356   }
2357 
2358   template <typename AsyncReadStream>
2359   class initiate_async_read_until_expr_v2
2360   {
2361   public:
2362     typedef typename AsyncReadStream::executor_type executor_type;
2363 
2364     explicit initiate_async_read_until_expr_v2(AsyncReadStream& stream)
2365       : stream_(stream)
2366     {
2367     }
2368 
2369     executor_type get_executor() const noexcept
2370     {
2371       return stream_.get_executor();
2372     }
2373 
2374     template <typename ReadHandler, typename DynamicBuffer_v2, typename RegEx>
2375     void operator()(ReadHandler&& handler,
2376         DynamicBuffer_v2&& buffers,
2377         const RegEx& expr) const
2378     {
2379       // If you get an error on the following line it means that your handler
2380       // does not meet the documented type requirements for a ReadHandler.
2381       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
2382 
2383       non_const_lvalue<ReadHandler> handler2(handler);
2384       read_until_expr_op_v2<AsyncReadStream,
2385         decay_t<DynamicBuffer_v2>,
2386           RegEx, decay_t<ReadHandler>>(
2387             stream_, static_cast<DynamicBuffer_v2&&>(buffers),
2388             expr, handler2.value)(boost::system::error_code(), 0, 1);
2389     }
2390 
2391   private:
2392     AsyncReadStream& stream_;
2393   };
2394 } // namespace detail
2395 
2396 #if !defined(GENERATING_DOCUMENTATION)
2397 
2398 template <template <typename, typename> class Associator,
2399     typename AsyncReadStream, typename DynamicBuffer_v2,
2400     typename RegEx, typename ReadHandler, typename DefaultCandidate>
2401 struct associator<Associator,
2402     detail::read_until_expr_op_v2<AsyncReadStream,
2403       DynamicBuffer_v2, RegEx, ReadHandler>,
2404     DefaultCandidate>
2405   : Associator<ReadHandler, DefaultCandidate>
2406 {
2407   static typename Associator<ReadHandler, DefaultCandidate>::type get(
2408       const detail::read_until_expr_op_v2<AsyncReadStream,
2409         DynamicBuffer_v2, RegEx, ReadHandler>& h) noexcept
2410   {
2411     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_);
2412   }
2413 
2414   static auto get(
2415       const detail::read_until_expr_op_v2<AsyncReadStream,
2416         DynamicBuffer_v2, RegEx, ReadHandler>& h,
2417       const DefaultCandidate& c) noexcept
2418     -> decltype(Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c))
2419   {
2420     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c);
2421   }
2422 };
2423 
2424 #endif // !defined(GENERATING_DOCUMENTATION)
2425 
2426 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
2427 
2428 namespace detail
2429 {
2430   template <typename AsyncReadStream, typename DynamicBuffer_v2,
2431       typename MatchCondition, typename ReadHandler>
2432   class read_until_match_op_v2
2433     : public base_from_cancellation_state<ReadHandler>
2434   {
2435   public:
2436     template <typename BufferSequence>
2437     read_until_match_op_v2(AsyncReadStream& stream,
2438         BufferSequence&& buffers,
2439         MatchCondition match_condition, ReadHandler& handler)
2440       : base_from_cancellation_state<ReadHandler>(
2441           handler, enable_partial_cancellation()),
2442         stream_(stream),
2443         buffers_(static_cast<BufferSequence&&>(buffers)),
2444         match_condition_(match_condition),
2445         start_(0),
2446         search_position_(0),
2447         bytes_to_read_(0),
2448         handler_(static_cast<ReadHandler&&>(handler))
2449     {
2450     }
2451 
2452     read_until_match_op_v2(const read_until_match_op_v2& other)
2453       : base_from_cancellation_state<ReadHandler>(other),
2454         stream_(other.stream_),
2455         buffers_(other.buffers_),
2456         match_condition_(other.match_condition_),
2457         start_(other.start_),
2458         search_position_(other.search_position_),
2459         bytes_to_read_(other.bytes_to_read_),
2460         handler_(other.handler_)
2461     {
2462     }
2463 
2464     read_until_match_op_v2(read_until_match_op_v2&& other)
2465       : base_from_cancellation_state<ReadHandler>(
2466           static_cast<base_from_cancellation_state<ReadHandler>&&>(other)),
2467         stream_(other.stream_),
2468         buffers_(static_cast<DynamicBuffer_v2&&>(other.buffers_)),
2469         match_condition_(other.match_condition_),
2470         start_(other.start_),
2471         search_position_(other.search_position_),
2472         bytes_to_read_(other.bytes_to_read_),
2473         handler_(static_cast<ReadHandler&&>(other.handler_))
2474     {
2475     }
2476 
2477     void operator()(boost::system::error_code ec,
2478         std::size_t bytes_transferred, int start = 0)
2479     {
2480       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
2481       std::size_t pos;
2482       switch (start_ = start)
2483       {
2484       case 1:
2485         for (;;)
2486         {
2487           {
2488             // Determine the range of the data to be searched.
2489             typedef typename DynamicBuffer_v2::const_buffers_type
2490               buffers_type;
2491             typedef buffers_iterator<buffers_type> iterator;
2492             buffers_type data_buffers =
2493               const_cast<const DynamicBuffer_v2&>(buffers_).data(
2494                   0, buffers_.size());
2495             iterator begin = iterator::begin(data_buffers);
2496             iterator start_pos = begin + search_position_;
2497             iterator end = iterator::end(data_buffers);
2498 
2499             // Look for a match.
2500             std::pair<iterator, bool> result = match_condition_(start_pos, end);
2501             if (result.second)
2502             {
2503               // Full match. We're done.
2504               search_position_ = result.first - begin;
2505               bytes_to_read_ = 0;
2506             }
2507 
2508             // No match yet. Check if buffer is full.
2509             else if (buffers_.size() == buffers_.max_size())
2510             {
2511               search_position_ = not_found;
2512               bytes_to_read_ = 0;
2513             }
2514 
2515             // Need to read some more data.
2516             else
2517             {
2518               if (result.first != end)
2519               {
2520                 // Partial match. Next search needs to start from beginning of
2521                 // match.
2522                 search_position_ = result.first - begin;
2523               }
2524               else
2525               {
2526                 // Next search can start with the new data.
2527                 search_position_ = end - begin;
2528               }
2529 
2530               bytes_to_read_ = std::min<std::size_t>(
2531                     std::max<std::size_t>(512,
2532                       buffers_.capacity() - buffers_.size()),
2533                     std::min<std::size_t>(65536,
2534                       buffers_.max_size() - buffers_.size()));
2535             }
2536           }
2537 
2538           // Check if we're done.
2539           if (!start && bytes_to_read_ == 0)
2540             break;
2541 
2542           // Start a new asynchronous read operation to obtain more data.
2543           pos = buffers_.size();
2544           buffers_.grow(bytes_to_read_);
2545           {
2546             BOOST_ASIO_HANDLER_LOCATION((
2547                   __FILE__, __LINE__, "async_read_until"));
2548             stream_.async_read_some(buffers_.data(pos, bytes_to_read_),
2549                 static_cast<read_until_match_op_v2&&>(*this));
2550           }
2551           return; default:
2552           buffers_.shrink(bytes_to_read_ - bytes_transferred);
2553           if (ec || bytes_transferred == 0)
2554             break;
2555           if (this->cancelled() != cancellation_type::none)
2556           {
2557             ec = error::operation_aborted;
2558             break;
2559           }
2560         }
2561 
2562         const boost::system::error_code result_ec =
2563           (search_position_ == not_found)
2564           ? error::not_found : ec;
2565 
2566         const std::size_t result_n =
2567           (ec || search_position_ == not_found)
2568           ? 0 : search_position_;
2569 
2570         static_cast<ReadHandler&&>(handler_)(result_ec, result_n);
2571       }
2572     }
2573 
2574   //private:
2575     AsyncReadStream& stream_;
2576     DynamicBuffer_v2 buffers_;
2577     MatchCondition match_condition_;
2578     int start_;
2579     std::size_t search_position_;
2580     std::size_t bytes_to_read_;
2581     ReadHandler handler_;
2582   };
2583 
2584   template <typename AsyncReadStream, typename DynamicBuffer_v2,
2585       typename MatchCondition, typename ReadHandler>
2586   inline bool asio_handler_is_continuation(
2587       read_until_match_op_v2<AsyncReadStream, DynamicBuffer_v2,
2588         MatchCondition, ReadHandler>* this_handler)
2589   {
2590     return this_handler->start_ == 0 ? true
2591       : boost_asio_handler_cont_helpers::is_continuation(
2592           this_handler->handler_);
2593   }
2594 
2595   template <typename AsyncReadStream>
2596   class initiate_async_read_until_match_v2
2597   {
2598   public:
2599     typedef typename AsyncReadStream::executor_type executor_type;
2600 
2601     explicit initiate_async_read_until_match_v2(AsyncReadStream& stream)
2602       : stream_(stream)
2603     {
2604     }
2605 
2606     executor_type get_executor() const noexcept
2607     {
2608       return stream_.get_executor();
2609     }
2610 
2611     template <typename ReadHandler,
2612         typename DynamicBuffer_v2, typename MatchCondition>
2613     void operator()(ReadHandler&& handler,
2614         DynamicBuffer_v2&& buffers,
2615         MatchCondition match_condition) const
2616     {
2617       // If you get an error on the following line it means that your handler
2618       // does not meet the documented type requirements for a ReadHandler.
2619       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
2620 
2621       non_const_lvalue<ReadHandler> handler2(handler);
2622       read_until_match_op_v2<AsyncReadStream, decay_t<DynamicBuffer_v2>,
2623         MatchCondition, decay_t<ReadHandler>>(
2624           stream_, static_cast<DynamicBuffer_v2&&>(buffers),
2625           match_condition, handler2.value)(boost::system::error_code(), 0, 1);
2626     }
2627 
2628   private:
2629     AsyncReadStream& stream_;
2630   };
2631 } // namespace detail
2632 
2633 #if !defined(GENERATING_DOCUMENTATION)
2634 
2635 template <template <typename, typename> class Associator,
2636     typename AsyncReadStream, typename DynamicBuffer_v2,
2637     typename MatchCondition, typename ReadHandler, typename DefaultCandidate>
2638 struct associator<Associator,
2639     detail::read_until_match_op_v2<AsyncReadStream,
2640       DynamicBuffer_v2, MatchCondition, ReadHandler>,
2641     DefaultCandidate>
2642   : Associator<ReadHandler, DefaultCandidate>
2643 {
2644   static typename Associator<ReadHandler, DefaultCandidate>::type get(
2645       const detail::read_until_match_op_v2<AsyncReadStream,
2646         DynamicBuffer_v2, MatchCondition, ReadHandler>& h) noexcept
2647   {
2648     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_);
2649   }
2650 
2651   static auto get(
2652       const detail::read_until_match_op_v2<AsyncReadStream,
2653         DynamicBuffer_v2, MatchCondition, ReadHandler>& h,
2654       const DefaultCandidate& c) noexcept
2655     -> decltype(Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c))
2656   {
2657     return Associator<ReadHandler, DefaultCandidate>::get(h.handler_, c);
2658   }
2659 };
2660 
2661 #endif // !defined(GENERATING_DOCUMENTATION)
2662 
2663 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
2664 
2665 } // namespace asio
2666 } // namespace boost
2667 
2668 #include <boost/asio/detail/pop_options.hpp>
2669 
2670 #endif // BOOST_ASIO_IMPL_READ_UNTIL_HPP