Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / asio / impl / read_until.hpp
1 //
2 // impl/read_until.hpp
3 // ~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10
11 #ifndef BOOST_ASIO_IMPL_READ_UNTIL_HPP
12 #define BOOST_ASIO_IMPL_READ_UNTIL_HPP
13
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
15 # pragma once
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17
18 #include <algorithm>
19 #include <string>
20 #include <vector>
21 #include <utility>
22 #include <boost/asio/associated_allocator.hpp>
23 #include <boost/asio/associated_executor.hpp>
24 #include <boost/asio/buffer.hpp>
25 #include <boost/asio/buffers_iterator.hpp>
26 #include <boost/asio/detail/bind_handler.hpp>
27 #include <boost/asio/detail/handler_alloc_helpers.hpp>
28 #include <boost/asio/detail/handler_cont_helpers.hpp>
29 #include <boost/asio/detail/handler_invoke_helpers.hpp>
30 #include <boost/asio/detail/handler_type_requirements.hpp>
31 #include <boost/asio/detail/limits.hpp>
32 #include <boost/asio/detail/non_const_lvalue.hpp>
33 #include <boost/asio/detail/throw_error.hpp>
34
35 #include <boost/asio/detail/push_options.hpp>
36
37 namespace boost {
38 namespace asio {
39
40 namespace detail
41 {
42   // Algorithm that finds a subsequence of equal values in a sequence. Returns
43   // (iterator,true) if a full match was found, in which case the iterator
44   // points to the beginning of the match. Returns (iterator,false) if a
45   // partial match was found at the end of the first sequence, in which case
46   // the iterator points to the beginning of the partial match. Returns
47   // (last1,false) if no full or partial match was found.
48   template <typename Iterator1, typename Iterator2>
49   std::pair<Iterator1, bool> partial_search(
50       Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
51   {
52     for (Iterator1 iter1 = first1; iter1 != last1; ++iter1)
53     {
54       Iterator1 test_iter1 = iter1;
55       Iterator2 test_iter2 = first2;
56       for (;; ++test_iter1, ++test_iter2)
57       {
58         if (test_iter2 == last2)
59           return std::make_pair(iter1, true);
60         if (test_iter1 == last1)
61         {
62           if (test_iter2 != first2)
63             return std::make_pair(iter1, false);
64           else
65             break;
66         }
67         if (*test_iter1 != *test_iter2)
68           break;
69       }
70     }
71     return std::make_pair(last1, false);
72   }
73 } // namespace detail
74
75 #if !defined(BOOST_ASIO_NO_DYNAMIC_BUFFER_V1)
76
77 template <typename SyncReadStream, typename DynamicBuffer_v1>
78 inline std::size_t read_until(SyncReadStream& s,
79     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers, char delim,
80     typename enable_if<
81       is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
82         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
83     >::type*)
84 {
85   boost::system::error_code ec;
86   std::size_t bytes_transferred = read_until(s,
87       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers), delim, ec);
88   boost::asio::detail::throw_error(ec, "read_until");
89   return bytes_transferred;
90 }
91
92 template <typename SyncReadStream, typename DynamicBuffer_v1>
93 std::size_t read_until(SyncReadStream& s,
94     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
95     char delim, boost::system::error_code& ec,
96     typename enable_if<
97       is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
98         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
99     >::type*)
100 {
101   typename decay<DynamicBuffer_v1>::type b(
102       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers));
103
104   std::size_t search_position = 0;
105   for (;;)
106   {
107     // Determine the range of the data to be searched.
108     typedef typename DynamicBuffer_v1::const_buffers_type buffers_type;
109     typedef buffers_iterator<buffers_type> iterator;
110     buffers_type data_buffers = b.data();
111     iterator begin = iterator::begin(data_buffers);
112     iterator start_pos = begin + search_position;
113     iterator end = iterator::end(data_buffers);
114
115     // Look for a match.
116     iterator iter = std::find(start_pos, end, delim);
117     if (iter != end)
118     {
119       // Found a match. We're done.
120       ec = boost::system::error_code();
121       return iter - begin + 1;
122     }
123     else
124     {
125       // No match. Next search can start with the new data.
126       search_position = end - begin;
127     }
128
129     // Check if buffer is full.
130     if (b.size() == b.max_size())
131     {
132       ec = error::not_found;
133       return 0;
134     }
135
136     // Need more data.
137     std::size_t bytes_to_read = std::min<std::size_t>(
138           std::max<std::size_t>(512, b.capacity() - b.size()),
139           std::min<std::size_t>(65536, b.max_size() - b.size()));
140     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
141     if (ec)
142       return 0;
143   }
144 }
145
146 template <typename SyncReadStream, typename DynamicBuffer_v1>
147 inline std::size_t read_until(SyncReadStream& s,
148     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
149     BOOST_ASIO_STRING_VIEW_PARAM delim,
150     typename enable_if<
151       is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
152         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
153     >::type*)
154 {
155   boost::system::error_code ec;
156   std::size_t bytes_transferred = read_until(s,
157       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers), delim, ec);
158   boost::asio::detail::throw_error(ec, "read_until");
159   return bytes_transferred;
160 }
161
162 template <typename SyncReadStream, typename DynamicBuffer_v1>
163 std::size_t read_until(SyncReadStream& s,
164     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
165     BOOST_ASIO_STRING_VIEW_PARAM delim, boost::system::error_code& ec,
166     typename enable_if<
167       is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
168         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
169     >::type*)
170 {
171   typename decay<DynamicBuffer_v1>::type b(
172       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers));
173
174   std::size_t search_position = 0;
175   for (;;)
176   {
177     // Determine the range of the data to be searched.
178     typedef typename DynamicBuffer_v1::const_buffers_type buffers_type;
179     typedef buffers_iterator<buffers_type> iterator;
180     buffers_type data_buffers = b.data();
181     iterator begin = iterator::begin(data_buffers);
182     iterator start_pos = begin + search_position;
183     iterator end = iterator::end(data_buffers);
184
185     // Look for a match.
186     std::pair<iterator, bool> result = detail::partial_search(
187         start_pos, end, delim.begin(), delim.end());
188     if (result.first != end)
189     {
190       if (result.second)
191       {
192         // Full match. We're done.
193         ec = boost::system::error_code();
194         return result.first - begin + delim.length();
195       }
196       else
197       {
198         // Partial match. Next search needs to start from beginning of match.
199         search_position = result.first - begin;
200       }
201     }
202     else
203     {
204       // No match. Next search can start with the new data.
205       search_position = end - begin;
206     }
207
208     // Check if buffer is full.
209     if (b.size() == b.max_size())
210     {
211       ec = error::not_found;
212       return 0;
213     }
214
215     // Need more data.
216     std::size_t bytes_to_read = std::min<std::size_t>(
217           std::max<std::size_t>(512, b.capacity() - b.size()),
218           std::min<std::size_t>(65536, b.max_size() - b.size()));
219     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
220     if (ec)
221       return 0;
222   }
223 }
224
225 #if !defined(BOOST_ASIO_NO_EXTENSIONS)
226 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
227
228 template <typename SyncReadStream, typename DynamicBuffer_v1>
229 inline std::size_t read_until(SyncReadStream& s,
230     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
231     const boost::regex& expr,
232     typename enable_if<
233       is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
234         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
235     >::type*)
236 {
237   boost::system::error_code ec;
238   std::size_t bytes_transferred = read_until(s,
239       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers), expr, ec);
240   boost::asio::detail::throw_error(ec, "read_until");
241   return bytes_transferred;
242 }
243
244 template <typename SyncReadStream, typename DynamicBuffer_v1>
245 std::size_t read_until(SyncReadStream& s,
246     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
247     const boost::regex& expr, boost::system::error_code& ec,
248     typename enable_if<
249       is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
250         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
251     >::type*)
252 {
253   typename decay<DynamicBuffer_v1>::type b(
254       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers));
255
256   std::size_t search_position = 0;
257   for (;;)
258   {
259     // Determine the range of the data to be searched.
260     typedef typename DynamicBuffer_v1::const_buffers_type buffers_type;
261     typedef buffers_iterator<buffers_type> iterator;
262     buffers_type data_buffers = b.data();
263     iterator begin = iterator::begin(data_buffers);
264     iterator start_pos = begin + search_position;
265     iterator end = iterator::end(data_buffers);
266
267     // Look for a match.
268     boost::match_results<iterator,
269       typename std::vector<boost::sub_match<iterator> >::allocator_type>
270         match_results;
271     if (regex_search(start_pos, end, match_results, expr,
272           boost::match_default | boost::match_partial))
273     {
274       if (match_results[0].matched)
275       {
276         // Full match. We're done.
277         ec = boost::system::error_code();
278         return match_results[0].second - begin;
279       }
280       else
281       {
282         // Partial match. Next search needs to start from beginning of match.
283         search_position = match_results[0].first - begin;
284       }
285     }
286     else
287     {
288       // No match. Next search can start with the new data.
289       search_position = end - begin;
290     }
291
292     // Check if buffer is full.
293     if (b.size() == b.max_size())
294     {
295       ec = error::not_found;
296       return 0;
297     }
298
299     // Need more data.
300     std::size_t bytes_to_read = std::min<std::size_t>(
301           std::max<std::size_t>(512, b.capacity() - b.size()),
302           std::min<std::size_t>(65536, b.max_size() - b.size()));
303     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
304     if (ec)
305       return 0;
306   }
307 }
308
309 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
310
311 template <typename SyncReadStream,
312     typename DynamicBuffer_v1, typename MatchCondition>
313 inline std::size_t read_until(SyncReadStream& s,
314     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
315     MatchCondition match_condition,
316     typename enable_if<
317       is_match_condition<MatchCondition>::value
318         && is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
319         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
320     >::type*)
321 {
322   boost::system::error_code ec;
323   std::size_t bytes_transferred = read_until(s,
324       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers),
325       match_condition, ec);
326   boost::asio::detail::throw_error(ec, "read_until");
327   return bytes_transferred;
328 }
329
330 template <typename SyncReadStream,
331     typename DynamicBuffer_v1, typename MatchCondition>
332 std::size_t read_until(SyncReadStream& s,
333     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
334     MatchCondition match_condition, boost::system::error_code& ec,
335     typename enable_if<
336       is_match_condition<MatchCondition>::value
337         && is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
338         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
339     >::type*)
340 {
341   typename decay<DynamicBuffer_v1>::type b(
342       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers));
343
344   std::size_t search_position = 0;
345   for (;;)
346   {
347     // Determine the range of the data to be searched.
348     typedef typename DynamicBuffer_v1::const_buffers_type buffers_type;
349     typedef buffers_iterator<buffers_type> iterator;
350     buffers_type data_buffers = b.data();
351     iterator begin = iterator::begin(data_buffers);
352     iterator start_pos = begin + search_position;
353     iterator end = iterator::end(data_buffers);
354
355     // Look for a match.
356     std::pair<iterator, bool> result = match_condition(start_pos, end);
357     if (result.second)
358     {
359       // Full match. We're done.
360       ec = boost::system::error_code();
361       return result.first - begin;
362     }
363     else if (result.first != end)
364     {
365       // Partial match. Next search needs to start from beginning of match.
366       search_position = result.first - begin;
367     }
368     else
369     {
370       // No match. Next search can start with the new data.
371       search_position = end - begin;
372     }
373
374     // Check if buffer is full.
375     if (b.size() == b.max_size())
376     {
377       ec = error::not_found;
378       return 0;
379     }
380
381     // Need more data.
382     std::size_t bytes_to_read = std::min<std::size_t>(
383           std::max<std::size_t>(512, b.capacity() - b.size()),
384           std::min<std::size_t>(65536, b.max_size() - b.size()));
385     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
386     if (ec)
387       return 0;
388   }
389 }
390
391 #if !defined(BOOST_ASIO_NO_IOSTREAM)
392
393 template <typename SyncReadStream, typename Allocator>
394 inline std::size_t read_until(SyncReadStream& s,
395     boost::asio::basic_streambuf<Allocator>& b, char delim)
396 {
397   return read_until(s, basic_streambuf_ref<Allocator>(b), delim);
398 }
399
400 template <typename SyncReadStream, typename Allocator>
401 inline std::size_t read_until(SyncReadStream& s,
402     boost::asio::basic_streambuf<Allocator>& b, char delim,
403     boost::system::error_code& ec)
404 {
405   return read_until(s, basic_streambuf_ref<Allocator>(b), delim, ec);
406 }
407
408 template <typename SyncReadStream, typename Allocator>
409 inline std::size_t read_until(SyncReadStream& s,
410     boost::asio::basic_streambuf<Allocator>& b,
411     BOOST_ASIO_STRING_VIEW_PARAM delim)
412 {
413   return read_until(s, basic_streambuf_ref<Allocator>(b), delim);
414 }
415
416 template <typename SyncReadStream, typename Allocator>
417 inline std::size_t read_until(SyncReadStream& s,
418     boost::asio::basic_streambuf<Allocator>& b,
419     BOOST_ASIO_STRING_VIEW_PARAM delim, boost::system::error_code& ec)
420 {
421   return read_until(s, basic_streambuf_ref<Allocator>(b), delim, ec);
422 }
423
424 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
425
426 template <typename SyncReadStream, typename Allocator>
427 inline std::size_t read_until(SyncReadStream& s,
428     boost::asio::basic_streambuf<Allocator>& b, const boost::regex& expr)
429 {
430   return read_until(s, basic_streambuf_ref<Allocator>(b), expr);
431 }
432
433 template <typename SyncReadStream, typename Allocator>
434 inline std::size_t read_until(SyncReadStream& s,
435     boost::asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
436     boost::system::error_code& ec)
437 {
438   return read_until(s, basic_streambuf_ref<Allocator>(b), expr, ec);
439 }
440
441 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
442
443 template <typename SyncReadStream, typename Allocator, typename MatchCondition>
444 inline std::size_t read_until(SyncReadStream& s,
445     boost::asio::basic_streambuf<Allocator>& b, MatchCondition match_condition,
446     typename enable_if<is_match_condition<MatchCondition>::value>::type*)
447 {
448   return read_until(s, basic_streambuf_ref<Allocator>(b), match_condition);
449 }
450
451 template <typename SyncReadStream, typename Allocator, typename MatchCondition>
452 inline std::size_t read_until(SyncReadStream& s,
453     boost::asio::basic_streambuf<Allocator>& b,
454     MatchCondition match_condition, boost::system::error_code& ec,
455     typename enable_if<is_match_condition<MatchCondition>::value>::type*)
456 {
457   return read_until(s, basic_streambuf_ref<Allocator>(b), match_condition, ec);
458 }
459
460 #endif // !defined(BOOST_ASIO_NO_IOSTREAM)
461 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
462 #endif // !defined(BOOST_ASIO_NO_DYNAMIC_BUFFER_V1)
463
464 template <typename SyncReadStream, typename DynamicBuffer_v2>
465 inline std::size_t read_until(SyncReadStream& s,
466     DynamicBuffer_v2 buffers, char delim,
467     typename enable_if<
468       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
469     >::type*)
470 {
471   boost::system::error_code ec;
472   std::size_t bytes_transferred = read_until(s,
473       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers), delim, ec);
474   boost::asio::detail::throw_error(ec, "read_until");
475   return bytes_transferred;
476 }
477
478 template <typename SyncReadStream, typename DynamicBuffer_v2>
479 std::size_t read_until(SyncReadStream& s, DynamicBuffer_v2 buffers,
480     char delim, boost::system::error_code& ec,
481     typename enable_if<
482       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
483     >::type*)
484 {
485   DynamicBuffer_v2& b = buffers;
486
487   std::size_t search_position = 0;
488   for (;;)
489   {
490     // Determine the range of the data to be searched.
491     typedef typename DynamicBuffer_v2::const_buffers_type buffers_type;
492     typedef buffers_iterator<buffers_type> iterator;
493     buffers_type data_buffers =
494       const_cast<const DynamicBuffer_v2&>(b).data(0, b.size());
495     iterator begin = iterator::begin(data_buffers);
496     iterator start_pos = begin + search_position;
497     iterator end = iterator::end(data_buffers);
498
499     // Look for a match.
500     iterator iter = std::find(start_pos, end, delim);
501     if (iter != end)
502     {
503       // Found a match. We're done.
504       ec = boost::system::error_code();
505       return iter - begin + 1;
506     }
507     else
508     {
509       // No match. Next search can start with the new data.
510       search_position = end - begin;
511     }
512
513     // Check if buffer is full.
514     if (b.size() == b.max_size())
515     {
516       ec = error::not_found;
517       return 0;
518     }
519
520     // Need more data.
521     std::size_t bytes_to_read = std::min<std::size_t>(
522           std::max<std::size_t>(512, b.capacity() - b.size()),
523           std::min<std::size_t>(65536, b.max_size() - b.size()));
524     std::size_t pos = b.size();
525     b.grow(bytes_to_read);
526     std::size_t bytes_transferred = s.read_some(b.data(pos, bytes_to_read), ec);
527     b.shrink(bytes_to_read - bytes_transferred);
528     if (ec)
529       return 0;
530   }
531 }
532
533 template <typename SyncReadStream, typename DynamicBuffer_v2>
534 inline std::size_t read_until(SyncReadStream& s,
535     DynamicBuffer_v2 buffers, BOOST_ASIO_STRING_VIEW_PARAM delim,
536     typename enable_if<
537       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
538     >::type*)
539 {
540   boost::system::error_code ec;
541   std::size_t bytes_transferred = read_until(s,
542       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers), delim, ec);
543   boost::asio::detail::throw_error(ec, "read_until");
544   return bytes_transferred;
545 }
546
547 template <typename SyncReadStream, typename DynamicBuffer_v2>
548 std::size_t read_until(SyncReadStream& s, DynamicBuffer_v2 buffers,
549     BOOST_ASIO_STRING_VIEW_PARAM delim, boost::system::error_code& ec,
550     typename enable_if<
551       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
552     >::type*)
553 {
554   DynamicBuffer_v2& b = buffers;
555
556   std::size_t search_position = 0;
557   for (;;)
558   {
559     // Determine the range of the data to be searched.
560     typedef typename DynamicBuffer_v2::const_buffers_type buffers_type;
561     typedef buffers_iterator<buffers_type> iterator;
562     buffers_type data_buffers =
563       const_cast<const DynamicBuffer_v2&>(b).data(0, b.size());
564     iterator begin = iterator::begin(data_buffers);
565     iterator start_pos = begin + search_position;
566     iterator end = iterator::end(data_buffers);
567
568     // Look for a match.
569     std::pair<iterator, bool> result = detail::partial_search(
570         start_pos, end, delim.begin(), delim.end());
571     if (result.first != end)
572     {
573       if (result.second)
574       {
575         // Full match. We're done.
576         ec = boost::system::error_code();
577         return result.first - begin + delim.length();
578       }
579       else
580       {
581         // Partial match. Next search needs to start from beginning of match.
582         search_position = result.first - begin;
583       }
584     }
585     else
586     {
587       // No match. Next search can start with the new data.
588       search_position = end - begin;
589     }
590
591     // Check if buffer is full.
592     if (b.size() == b.max_size())
593     {
594       ec = error::not_found;
595       return 0;
596     }
597
598     // Need more data.
599     std::size_t bytes_to_read = std::min<std::size_t>(
600           std::max<std::size_t>(512, b.capacity() - b.size()),
601           std::min<std::size_t>(65536, b.max_size() - b.size()));
602     std::size_t pos = b.size();
603     b.grow(bytes_to_read);
604     std::size_t bytes_transferred = s.read_some(b.data(pos, bytes_to_read), ec);
605     b.shrink(bytes_to_read - bytes_transferred);
606     if (ec)
607       return 0;
608   }
609 }
610
611 #if !defined(BOOST_ASIO_NO_EXTENSIONS)
612 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
613
614 template <typename SyncReadStream, typename DynamicBuffer_v2>
615 inline std::size_t read_until(SyncReadStream& s,
616     DynamicBuffer_v2 buffers, const boost::regex& expr,
617     typename enable_if<
618       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
619     >::type*)
620 {
621   boost::system::error_code ec;
622   std::size_t bytes_transferred = read_until(s,
623       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers), expr, ec);
624   boost::asio::detail::throw_error(ec, "read_until");
625   return bytes_transferred;
626 }
627
628 template <typename SyncReadStream, typename DynamicBuffer_v2>
629 std::size_t read_until(SyncReadStream& s, DynamicBuffer_v2 buffers,
630     const boost::regex& expr, boost::system::error_code& ec,
631     typename enable_if<
632       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
633     >::type*)
634 {
635   DynamicBuffer_v2& b = buffers;
636
637   std::size_t search_position = 0;
638   for (;;)
639   {
640     // Determine the range of the data to be searched.
641     typedef typename DynamicBuffer_v2::const_buffers_type buffers_type;
642     typedef buffers_iterator<buffers_type> iterator;
643     buffers_type data_buffers =
644       const_cast<const DynamicBuffer_v2&>(b).data(0, b.size());
645     iterator begin = iterator::begin(data_buffers);
646     iterator start_pos = begin + search_position;
647     iterator end = iterator::end(data_buffers);
648
649     // Look for a match.
650     boost::match_results<iterator,
651       typename std::vector<boost::sub_match<iterator> >::allocator_type>
652         match_results;
653     if (regex_search(start_pos, end, match_results, expr,
654           boost::match_default | boost::match_partial))
655     {
656       if (match_results[0].matched)
657       {
658         // Full match. We're done.
659         ec = boost::system::error_code();
660         return match_results[0].second - begin;
661       }
662       else
663       {
664         // Partial match. Next search needs to start from beginning of match.
665         search_position = match_results[0].first - begin;
666       }
667     }
668     else
669     {
670       // No match. Next search can start with the new data.
671       search_position = end - begin;
672     }
673
674     // Check if buffer is full.
675     if (b.size() == b.max_size())
676     {
677       ec = error::not_found;
678       return 0;
679     }
680
681     // Need more data.
682     std::size_t bytes_to_read = std::min<std::size_t>(
683           std::max<std::size_t>(512, b.capacity() - b.size()),
684           std::min<std::size_t>(65536, b.max_size() - b.size()));
685     std::size_t pos = b.size();
686     b.grow(bytes_to_read);
687     std::size_t bytes_transferred = s.read_some(b.data(pos, bytes_to_read), ec);
688     b.shrink(bytes_to_read - bytes_transferred);
689     if (ec)
690       return 0;
691   }
692 }
693
694 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
695
696 template <typename SyncReadStream,
697     typename DynamicBuffer_v2, typename MatchCondition>
698 inline std::size_t read_until(SyncReadStream& s,
699     DynamicBuffer_v2 buffers, MatchCondition match_condition,
700     typename enable_if<
701       is_match_condition<MatchCondition>::value
702         && is_dynamic_buffer_v2<DynamicBuffer_v2>::value
703     >::type*)
704 {
705   boost::system::error_code ec;
706   std::size_t bytes_transferred = read_until(s,
707       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers),
708       match_condition, ec);
709   boost::asio::detail::throw_error(ec, "read_until");
710   return bytes_transferred;
711 }
712
713 template <typename SyncReadStream,
714     typename DynamicBuffer_v2, typename MatchCondition>
715 std::size_t read_until(SyncReadStream& s, DynamicBuffer_v2 buffers,
716     MatchCondition match_condition, boost::system::error_code& ec,
717     typename enable_if<
718       is_match_condition<MatchCondition>::value
719         && is_dynamic_buffer_v2<DynamicBuffer_v2>::value
720     >::type*)
721 {
722   DynamicBuffer_v2& b = buffers;
723
724   std::size_t search_position = 0;
725   for (;;)
726   {
727     // Determine the range of the data to be searched.
728     typedef typename DynamicBuffer_v2::const_buffers_type buffers_type;
729     typedef buffers_iterator<buffers_type> iterator;
730     buffers_type data_buffers =
731       const_cast<const DynamicBuffer_v2&>(b).data(0, b.size());
732     iterator begin = iterator::begin(data_buffers);
733     iterator start_pos = begin + search_position;
734     iterator end = iterator::end(data_buffers);
735
736     // Look for a match.
737     std::pair<iterator, bool> result = match_condition(start_pos, end);
738     if (result.second)
739     {
740       // Full match. We're done.
741       ec = boost::system::error_code();
742       return result.first - begin;
743     }
744     else if (result.first != end)
745     {
746       // Partial match. Next search needs to start from beginning of match.
747       search_position = result.first - begin;
748     }
749     else
750     {
751       // No match. Next search can start with the new data.
752       search_position = end - begin;
753     }
754
755     // Check if buffer is full.
756     if (b.size() == b.max_size())
757     {
758       ec = error::not_found;
759       return 0;
760     }
761
762     // Need more data.
763     std::size_t bytes_to_read = std::min<std::size_t>(
764           std::max<std::size_t>(512, b.capacity() - b.size()),
765           std::min<std::size_t>(65536, b.max_size() - b.size()));
766     std::size_t pos = b.size();
767     b.grow(bytes_to_read);
768     std::size_t bytes_transferred = s.read_some(b.data(pos, bytes_to_read), ec);
769     b.shrink(bytes_to_read - bytes_transferred);
770     if (ec)
771       return 0;
772   }
773 }
774
775 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
776
777 #if !defined(BOOST_ASIO_NO_DYNAMIC_BUFFER_V1)
778
779 namespace detail
780 {
781   template <typename AsyncReadStream,
782       typename DynamicBuffer_v1, typename ReadHandler>
783   class read_until_delim_op_v1
784   {
785   public:
786     template <typename BufferSequence>
787     read_until_delim_op_v1(AsyncReadStream& stream,
788         BOOST_ASIO_MOVE_ARG(BufferSequence) buffers,
789         char delim, ReadHandler& handler)
790       : stream_(stream),
791         buffers_(BOOST_ASIO_MOVE_CAST(BufferSequence)(buffers)),
792         delim_(delim),
793         start_(0),
794         search_position_(0),
795         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
796     {
797     }
798
799 #if defined(BOOST_ASIO_HAS_MOVE)
800     read_until_delim_op_v1(const read_until_delim_op_v1& other)
801       : stream_(other.stream_),
802         buffers_(other.buffers_),
803         delim_(other.delim_),
804         start_(other.start_),
805         search_position_(other.search_position_),
806         handler_(other.handler_)
807     {
808     }
809
810     read_until_delim_op_v1(read_until_delim_op_v1&& other)
811       : stream_(other.stream_),
812         buffers_(BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(other.buffers_)),
813         delim_(other.delim_),
814         start_(other.start_),
815         search_position_(other.search_position_),
816         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
817     {
818     }
819 #endif // defined(BOOST_ASIO_HAS_MOVE)
820
821     void operator()(const boost::system::error_code& ec,
822         std::size_t bytes_transferred, int start = 0)
823     {
824       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
825       std::size_t bytes_to_read;
826       switch (start_ = start)
827       {
828       case 1:
829         for (;;)
830         {
831           {
832             // Determine the range of the data to be searched.
833             typedef typename DynamicBuffer_v1::const_buffers_type
834               buffers_type;
835             typedef buffers_iterator<buffers_type> iterator;
836             buffers_type data_buffers = buffers_.data();
837             iterator begin = iterator::begin(data_buffers);
838             iterator start_pos = begin + search_position_;
839             iterator end = iterator::end(data_buffers);
840
841             // Look for a match.
842             iterator iter = std::find(start_pos, end, delim_);
843             if (iter != end)
844             {
845               // Found a match. We're done.
846               search_position_ = iter - begin + 1;
847               bytes_to_read = 0;
848             }
849
850             // No match yet. Check if buffer is full.
851             else if (buffers_.size() == buffers_.max_size())
852             {
853               search_position_ = not_found;
854               bytes_to_read = 0;
855             }
856
857             // Need to read some more data.
858             else
859             {
860               // Next search can start with the new data.
861               search_position_ = end - begin;
862               bytes_to_read = std::min<std::size_t>(
863                     std::max<std::size_t>(512,
864                       buffers_.capacity() - buffers_.size()),
865                     std::min<std::size_t>(65536,
866                       buffers_.max_size() - buffers_.size()));
867             }
868           }
869
870           // Check if we're done.
871           if (!start && bytes_to_read == 0)
872             break;
873
874           // Start a new asynchronous read op_v1eration to obtain more data.
875           stream_.async_read_some(buffers_.prepare(bytes_to_read),
876               BOOST_ASIO_MOVE_CAST(read_until_delim_op_v1)(*this));
877           return; default:
878           buffers_.commit(bytes_transferred);
879           if (ec || bytes_transferred == 0)
880             break;
881         }
882
883         const boost::system::error_code result_ec =
884           (search_position_ == not_found)
885           ? error::not_found : ec;
886
887         const std::size_t result_n =
888           (ec || search_position_ == not_found)
889           ? 0 : search_position_;
890
891         handler_(result_ec, result_n);
892       }
893     }
894
895   //private:
896     AsyncReadStream& stream_;
897     DynamicBuffer_v1 buffers_;
898     char delim_;
899     int start_;
900     std::size_t search_position_;
901     ReadHandler handler_;
902   };
903
904   template <typename AsyncReadStream,
905       typename DynamicBuffer_v1, typename ReadHandler>
906   inline void* asio_handler_allocate(std::size_t size,
907       read_until_delim_op_v1<AsyncReadStream,
908         DynamicBuffer_v1, ReadHandler>* this_handler)
909   {
910     return boost_asio_handler_alloc_helpers::allocate(
911         size, this_handler->handler_);
912   }
913
914   template <typename AsyncReadStream,
915       typename DynamicBuffer_v1, typename ReadHandler>
916   inline void asio_handler_deallocate(void* pointer, std::size_t size,
917       read_until_delim_op_v1<AsyncReadStream,
918         DynamicBuffer_v1, ReadHandler>* this_handler)
919   {
920     boost_asio_handler_alloc_helpers::deallocate(
921         pointer, size, this_handler->handler_);
922   }
923
924   template <typename AsyncReadStream,
925       typename DynamicBuffer_v1, typename ReadHandler>
926   inline bool asio_handler_is_continuation(
927       read_until_delim_op_v1<AsyncReadStream,
928         DynamicBuffer_v1, ReadHandler>* this_handler)
929   {
930     return this_handler->start_ == 0 ? true
931       : boost_asio_handler_cont_helpers::is_continuation(
932           this_handler->handler_);
933   }
934
935   template <typename Function, typename AsyncReadStream,
936       typename DynamicBuffer_v1, typename ReadHandler>
937   inline void asio_handler_invoke(Function& function,
938       read_until_delim_op_v1<AsyncReadStream,
939         DynamicBuffer_v1, ReadHandler>* this_handler)
940   {
941     boost_asio_handler_invoke_helpers::invoke(
942         function, this_handler->handler_);
943   }
944
945   template <typename Function, typename AsyncReadStream,
946       typename DynamicBuffer_v1, typename ReadHandler>
947   inline void asio_handler_invoke(const Function& function,
948       read_until_delim_op_v1<AsyncReadStream,
949         DynamicBuffer_v1, ReadHandler>* this_handler)
950   {
951     boost_asio_handler_invoke_helpers::invoke(
952         function, this_handler->handler_);
953   }
954
955   template <typename AsyncReadStream>
956   class initiate_async_read_until_delim_v1
957   {
958   public:
959     typedef typename AsyncReadStream::executor_type executor_type;
960
961     explicit initiate_async_read_until_delim_v1(AsyncReadStream& stream)
962       : stream_(stream)
963     {
964     }
965
966     executor_type get_executor() const BOOST_ASIO_NOEXCEPT
967     {
968       return stream_.get_executor();
969     }
970
971     template <typename ReadHandler, typename DynamicBuffer_v1>
972     void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
973         BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
974         char delim) const
975     {
976       // If you get an error on the following line it means that your handler
977       // does not meet the documented type requirements for a ReadHandler.
978       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
979
980       non_const_lvalue<ReadHandler> handler2(handler);
981       read_until_delim_op_v1<AsyncReadStream,
982         typename decay<DynamicBuffer_v1>::type,
983           typename decay<ReadHandler>::type>(
984             stream_, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers),
985             delim, handler2.value)(boost::system::error_code(), 0, 1);
986     }
987
988   private:
989     AsyncReadStream& stream_;
990   };
991 } // namespace detail
992
993 #if !defined(GENERATING_DOCUMENTATION)
994
995 template <typename AsyncReadStream, typename DynamicBuffer_v1,
996     typename ReadHandler, typename Allocator>
997 struct associated_allocator<
998     detail::read_until_delim_op_v1<AsyncReadStream,
999       DynamicBuffer_v1, ReadHandler>,
1000     Allocator>
1001 {
1002   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
1003
1004   static type get(
1005       const detail::read_until_delim_op_v1<AsyncReadStream,
1006         DynamicBuffer_v1, ReadHandler>& h,
1007       const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
1008   {
1009     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
1010   }
1011 };
1012
1013 template <typename AsyncReadStream, typename DynamicBuffer_v1,
1014     typename ReadHandler, typename Executor>
1015 struct associated_executor<
1016     detail::read_until_delim_op_v1<AsyncReadStream,
1017       DynamicBuffer_v1, ReadHandler>,
1018     Executor>
1019 {
1020   typedef typename associated_executor<ReadHandler, Executor>::type type;
1021
1022   static type get(
1023       const detail::read_until_delim_op_v1<AsyncReadStream,
1024         DynamicBuffer_v1, ReadHandler>& h,
1025       const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
1026   {
1027     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
1028   }
1029 };
1030
1031 #endif // !defined(GENERATING_DOCUMENTATION)
1032
1033 template <typename AsyncReadStream, typename DynamicBuffer_v1,
1034     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
1035       std::size_t)) ReadHandler>
1036 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
1037     void (boost::system::error_code, std::size_t))
1038 async_read_until(AsyncReadStream& s,
1039     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
1040     char delim, BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
1041     typename enable_if<
1042       is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
1043         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
1044     >::type*)
1045 {
1046   return async_initiate<ReadHandler,
1047     void (boost::system::error_code, std::size_t)>(
1048       detail::initiate_async_read_until_delim_v1<AsyncReadStream>(s),
1049       handler, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers), delim);
1050 }
1051
1052 namespace detail
1053 {
1054   template <typename AsyncReadStream,
1055       typename DynamicBuffer_v1, typename ReadHandler>
1056   class read_until_delim_string_op_v1
1057   {
1058   public:
1059     template <typename BufferSequence>
1060     read_until_delim_string_op_v1(AsyncReadStream& stream,
1061         BOOST_ASIO_MOVE_ARG(BufferSequence) buffers,
1062         const std::string& delim, ReadHandler& handler)
1063       : stream_(stream),
1064         buffers_(BOOST_ASIO_MOVE_CAST(BufferSequence)(buffers)),
1065         delim_(delim),
1066         start_(0),
1067         search_position_(0),
1068         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
1069     {
1070     }
1071
1072 #if defined(BOOST_ASIO_HAS_MOVE)
1073     read_until_delim_string_op_v1(const read_until_delim_string_op_v1& other)
1074       : stream_(other.stream_),
1075         buffers_(other.buffers_),
1076         delim_(other.delim_),
1077         start_(other.start_),
1078         search_position_(other.search_position_),
1079         handler_(other.handler_)
1080     {
1081     }
1082
1083     read_until_delim_string_op_v1(read_until_delim_string_op_v1&& other)
1084       : stream_(other.stream_),
1085         buffers_(BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(other.buffers_)),
1086         delim_(BOOST_ASIO_MOVE_CAST(std::string)(other.delim_)),
1087         start_(other.start_),
1088         search_position_(other.search_position_),
1089         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
1090     {
1091     }
1092 #endif // defined(BOOST_ASIO_HAS_MOVE)
1093
1094     void operator()(const boost::system::error_code& ec,
1095         std::size_t bytes_transferred, int start = 0)
1096     {
1097       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
1098       std::size_t bytes_to_read;
1099       switch (start_ = start)
1100       {
1101       case 1:
1102         for (;;)
1103         {
1104           {
1105             // Determine the range of the data to be searched.
1106             typedef typename DynamicBuffer_v1::const_buffers_type
1107               buffers_type;
1108             typedef buffers_iterator<buffers_type> iterator;
1109             buffers_type data_buffers = buffers_.data();
1110             iterator begin = iterator::begin(data_buffers);
1111             iterator start_pos = begin + search_position_;
1112             iterator end = iterator::end(data_buffers);
1113
1114             // Look for a match.
1115             std::pair<iterator, bool> result = detail::partial_search(
1116                 start_pos, end, delim_.begin(), delim_.end());
1117             if (result.first != end && result.second)
1118             {
1119               // Full match. We're done.
1120               search_position_ = result.first - begin + delim_.length();
1121               bytes_to_read = 0;
1122             }
1123
1124             // No match yet. Check if buffer is full.
1125             else if (buffers_.size() == buffers_.max_size())
1126             {
1127               search_position_ = not_found;
1128               bytes_to_read = 0;
1129             }
1130
1131             // Need to read some more data.
1132             else
1133             {
1134               if (result.first != end)
1135               {
1136                 // Partial match. Next search needs to start from beginning of
1137                 // match.
1138                 search_position_ = result.first - begin;
1139               }
1140               else
1141               {
1142                 // Next search can start with the new data.
1143                 search_position_ = end - begin;
1144               }
1145
1146               bytes_to_read = std::min<std::size_t>(
1147                     std::max<std::size_t>(512,
1148                       buffers_.capacity() - buffers_.size()),
1149                     std::min<std::size_t>(65536,
1150                       buffers_.max_size() - buffers_.size()));
1151             }
1152           }
1153
1154           // Check if we're done.
1155           if (!start && bytes_to_read == 0)
1156             break;
1157
1158           // Start a new asynchronous read op_v1eration to obtain more data.
1159           stream_.async_read_some(buffers_.prepare(bytes_to_read),
1160               BOOST_ASIO_MOVE_CAST(read_until_delim_string_op_v1)(*this));
1161           return; default:
1162           buffers_.commit(bytes_transferred);
1163           if (ec || bytes_transferred == 0)
1164             break;
1165         }
1166
1167         const boost::system::error_code result_ec =
1168           (search_position_ == not_found)
1169           ? error::not_found : ec;
1170
1171         const std::size_t result_n =
1172           (ec || search_position_ == not_found)
1173           ? 0 : search_position_;
1174
1175         handler_(result_ec, result_n);
1176       }
1177     }
1178
1179   //private:
1180     AsyncReadStream& stream_;
1181     DynamicBuffer_v1 buffers_;
1182     std::string delim_;
1183     int start_;
1184     std::size_t search_position_;
1185     ReadHandler handler_;
1186   };
1187
1188   template <typename AsyncReadStream,
1189       typename DynamicBuffer_v1, typename ReadHandler>
1190   inline void* asio_handler_allocate(std::size_t size,
1191       read_until_delim_string_op_v1<AsyncReadStream,
1192         DynamicBuffer_v1, ReadHandler>* this_handler)
1193   {
1194     return boost_asio_handler_alloc_helpers::allocate(
1195         size, this_handler->handler_);
1196   }
1197
1198   template <typename AsyncReadStream,
1199       typename DynamicBuffer_v1, typename ReadHandler>
1200   inline void asio_handler_deallocate(void* pointer, std::size_t size,
1201       read_until_delim_string_op_v1<AsyncReadStream,
1202         DynamicBuffer_v1, ReadHandler>* this_handler)
1203   {
1204     boost_asio_handler_alloc_helpers::deallocate(
1205         pointer, size, this_handler->handler_);
1206   }
1207
1208   template <typename AsyncReadStream,
1209       typename DynamicBuffer_v1, typename ReadHandler>
1210   inline bool asio_handler_is_continuation(
1211       read_until_delim_string_op_v1<AsyncReadStream,
1212         DynamicBuffer_v1, ReadHandler>* this_handler)
1213   {
1214     return this_handler->start_ == 0 ? true
1215       : boost_asio_handler_cont_helpers::is_continuation(
1216           this_handler->handler_);
1217   }
1218
1219   template <typename Function, typename AsyncReadStream,
1220       typename DynamicBuffer_v1, typename ReadHandler>
1221   inline void asio_handler_invoke(Function& function,
1222       read_until_delim_string_op_v1<AsyncReadStream,
1223         DynamicBuffer_v1, ReadHandler>* this_handler)
1224   {
1225     boost_asio_handler_invoke_helpers::invoke(
1226         function, this_handler->handler_);
1227   }
1228
1229   template <typename Function, typename AsyncReadStream,
1230       typename DynamicBuffer_v1, typename ReadHandler>
1231   inline void asio_handler_invoke(const Function& function,
1232       read_until_delim_string_op_v1<AsyncReadStream,
1233         DynamicBuffer_v1, ReadHandler>* this_handler)
1234   {
1235     boost_asio_handler_invoke_helpers::invoke(
1236         function, this_handler->handler_);
1237   }
1238
1239   template <typename AsyncReadStream>
1240   class initiate_async_read_until_delim_string_v1
1241   {
1242   public:
1243     typedef typename AsyncReadStream::executor_type executor_type;
1244
1245     explicit initiate_async_read_until_delim_string_v1(AsyncReadStream& stream)
1246       : stream_(stream)
1247     {
1248     }
1249
1250     executor_type get_executor() const BOOST_ASIO_NOEXCEPT
1251     {
1252       return stream_.get_executor();
1253     }
1254
1255     template <typename ReadHandler, typename DynamicBuffer_v1>
1256     void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
1257         BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
1258         const std::string& delim) const
1259     {
1260       // If you get an error on the following line it means that your handler
1261       // does not meet the documented type requirements for a ReadHandler.
1262       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
1263
1264       non_const_lvalue<ReadHandler> handler2(handler);
1265       read_until_delim_string_op_v1<AsyncReadStream,
1266         typename decay<DynamicBuffer_v1>::type,
1267           typename decay<ReadHandler>::type>(
1268             stream_, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers),
1269             delim, handler2.value)(boost::system::error_code(), 0, 1);
1270     }
1271
1272   private:
1273     AsyncReadStream& stream_;
1274   };
1275 } // namespace detail
1276
1277 #if !defined(GENERATING_DOCUMENTATION)
1278
1279 template <typename AsyncReadStream, typename DynamicBuffer_v1,
1280     typename ReadHandler, typename Allocator>
1281 struct associated_allocator<
1282     detail::read_until_delim_string_op_v1<AsyncReadStream,
1283       DynamicBuffer_v1, ReadHandler>,
1284     Allocator>
1285 {
1286   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
1287
1288   static type get(
1289       const detail::read_until_delim_string_op_v1<AsyncReadStream,
1290         DynamicBuffer_v1, ReadHandler>& h,
1291       const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
1292   {
1293     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
1294   }
1295 };
1296
1297 template <typename AsyncReadStream, typename DynamicBuffer_v1,
1298     typename ReadHandler, typename Executor>
1299 struct associated_executor<
1300     detail::read_until_delim_string_op_v1<AsyncReadStream,
1301       DynamicBuffer_v1, ReadHandler>,
1302     Executor>
1303 {
1304   typedef typename associated_executor<ReadHandler, Executor>::type type;
1305
1306   static type get(
1307       const detail::read_until_delim_string_op_v1<AsyncReadStream,
1308         DynamicBuffer_v1, ReadHandler>& h,
1309       const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
1310   {
1311     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
1312   }
1313 };
1314
1315 #endif // !defined(GENERATING_DOCUMENTATION)
1316
1317 template <typename AsyncReadStream, typename DynamicBuffer_v1,
1318     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
1319       std::size_t)) ReadHandler>
1320 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
1321     void (boost::system::error_code, std::size_t))
1322 async_read_until(AsyncReadStream& s,
1323     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
1324     BOOST_ASIO_STRING_VIEW_PARAM delim,
1325     BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
1326     typename enable_if<
1327       is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
1328         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
1329     >::type*)
1330 {
1331   return async_initiate<ReadHandler,
1332     void (boost::system::error_code, std::size_t)>(
1333       detail::initiate_async_read_until_delim_string_v1<AsyncReadStream>(s),
1334       handler, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers),
1335       static_cast<std::string>(delim));
1336 }
1337
1338 #if !defined(BOOST_ASIO_NO_EXTENSIONS)
1339 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
1340
1341 namespace detail
1342 {
1343   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1344       typename RegEx, typename ReadHandler>
1345   class read_until_expr_op_v1
1346   {
1347   public:
1348     template <typename BufferSequence>
1349     read_until_expr_op_v1(AsyncReadStream& stream,
1350         BOOST_ASIO_MOVE_ARG(BufferSequence) buffers,
1351         const boost::regex& expr, ReadHandler& handler)
1352       : stream_(stream),
1353         buffers_(BOOST_ASIO_MOVE_CAST(BufferSequence)(buffers)),
1354         expr_(expr),
1355         start_(0),
1356         search_position_(0),
1357         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
1358     {
1359     }
1360
1361 #if defined(BOOST_ASIO_HAS_MOVE)
1362     read_until_expr_op_v1(const read_until_expr_op_v1& other)
1363       : stream_(other.stream_),
1364         buffers_(other.buffers_),
1365         expr_(other.expr_),
1366         start_(other.start_),
1367         search_position_(other.search_position_),
1368         handler_(other.handler_)
1369     {
1370     }
1371
1372     read_until_expr_op_v1(read_until_expr_op_v1&& other)
1373       : stream_(other.stream_),
1374         buffers_(BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(other.buffers_)),
1375         expr_(other.expr_),
1376         start_(other.start_),
1377         search_position_(other.search_position_),
1378         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
1379     {
1380     }
1381 #endif // defined(BOOST_ASIO_HAS_MOVE)
1382
1383     void operator()(const boost::system::error_code& ec,
1384         std::size_t bytes_transferred, int start = 0)
1385     {
1386       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
1387       std::size_t bytes_to_read;
1388       switch (start_ = start)
1389       {
1390       case 1:
1391         for (;;)
1392         {
1393           {
1394             // Determine the range of the data to be searched.
1395             typedef typename DynamicBuffer_v1::const_buffers_type
1396               buffers_type;
1397             typedef buffers_iterator<buffers_type> iterator;
1398             buffers_type data_buffers = buffers_.data();
1399             iterator begin = iterator::begin(data_buffers);
1400             iterator start_pos = begin + search_position_;
1401             iterator end = iterator::end(data_buffers);
1402
1403             // Look for a match.
1404             boost::match_results<iterator,
1405               typename std::vector<boost::sub_match<iterator> >::allocator_type>
1406                 match_results;
1407             bool match = regex_search(start_pos, end, match_results, expr_,
1408                 boost::match_default | boost::match_partial);
1409             if (match && match_results[0].matched)
1410             {
1411               // Full match. We're done.
1412               search_position_ = match_results[0].second - begin;
1413               bytes_to_read = 0;
1414             }
1415
1416             // No match yet. Check if buffer is full.
1417             else if (buffers_.size() == buffers_.max_size())
1418             {
1419               search_position_ = not_found;
1420               bytes_to_read = 0;
1421             }
1422
1423             // Need to read some more data.
1424             else
1425             {
1426               if (match)
1427               {
1428                 // Partial match. Next search needs to start from beginning of
1429                 // match.
1430                 search_position_ = match_results[0].first - begin;
1431               }
1432               else
1433               {
1434                 // Next search can start with the new data.
1435                 search_position_ = end - begin;
1436               }
1437
1438               bytes_to_read = std::min<std::size_t>(
1439                     std::max<std::size_t>(512,
1440                       buffers_.capacity() - buffers_.size()),
1441                     std::min<std::size_t>(65536,
1442                       buffers_.max_size() - buffers_.size()));
1443             }
1444           }
1445
1446           // Check if we're done.
1447           if (!start && bytes_to_read == 0)
1448             break;
1449
1450           // Start a new asynchronous read op_v1eration to obtain more data.
1451           stream_.async_read_some(buffers_.prepare(bytes_to_read),
1452               BOOST_ASIO_MOVE_CAST(read_until_expr_op_v1)(*this));
1453           return; default:
1454           buffers_.commit(bytes_transferred);
1455           if (ec || bytes_transferred == 0)
1456             break;
1457         }
1458
1459         const boost::system::error_code result_ec =
1460           (search_position_ == not_found)
1461           ? error::not_found : ec;
1462
1463         const std::size_t result_n =
1464           (ec || search_position_ == not_found)
1465           ? 0 : search_position_;
1466
1467         handler_(result_ec, result_n);
1468       }
1469     }
1470
1471   //private:
1472     AsyncReadStream& stream_;
1473     DynamicBuffer_v1 buffers_;
1474     RegEx expr_;
1475     int start_;
1476     std::size_t search_position_;
1477     ReadHandler handler_;
1478   };
1479
1480   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1481       typename RegEx, typename ReadHandler>
1482   inline void* asio_handler_allocate(std::size_t size,
1483       read_until_expr_op_v1<AsyncReadStream,
1484         DynamicBuffer_v1, RegEx, ReadHandler>* this_handler)
1485   {
1486     return boost_asio_handler_alloc_helpers::allocate(
1487         size, this_handler->handler_);
1488   }
1489
1490   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1491       typename RegEx, typename ReadHandler>
1492   inline void asio_handler_deallocate(void* pointer, std::size_t size,
1493       read_until_expr_op_v1<AsyncReadStream,
1494         DynamicBuffer_v1, RegEx, ReadHandler>* this_handler)
1495   {
1496     boost_asio_handler_alloc_helpers::deallocate(
1497         pointer, size, this_handler->handler_);
1498   }
1499
1500   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1501       typename RegEx, typename ReadHandler>
1502   inline bool asio_handler_is_continuation(
1503       read_until_expr_op_v1<AsyncReadStream,
1504         DynamicBuffer_v1, RegEx, ReadHandler>* this_handler)
1505   {
1506     return this_handler->start_ == 0 ? true
1507       : boost_asio_handler_cont_helpers::is_continuation(
1508           this_handler->handler_);
1509   }
1510
1511   template <typename Function, typename AsyncReadStream,
1512       typename DynamicBuffer_v1, typename RegEx, typename ReadHandler>
1513   inline void asio_handler_invoke(Function& function,
1514       read_until_expr_op_v1<AsyncReadStream,
1515         DynamicBuffer_v1, RegEx, ReadHandler>* this_handler)
1516   {
1517     boost_asio_handler_invoke_helpers::invoke(
1518         function, this_handler->handler_);
1519   }
1520
1521   template <typename Function, typename AsyncReadStream,
1522       typename DynamicBuffer_v1, typename RegEx, typename ReadHandler>
1523   inline void asio_handler_invoke(const Function& function,
1524       read_until_expr_op_v1<AsyncReadStream,
1525         DynamicBuffer_v1, RegEx, ReadHandler>* this_handler)
1526   {
1527     boost_asio_handler_invoke_helpers::invoke(
1528         function, this_handler->handler_);
1529   }
1530
1531   template <typename AsyncReadStream>
1532   class initiate_async_read_until_expr_v1
1533   {
1534   public:
1535     typedef typename AsyncReadStream::executor_type executor_type;
1536
1537     explicit initiate_async_read_until_expr_v1(AsyncReadStream& stream)
1538       : stream_(stream)
1539     {
1540     }
1541
1542     executor_type get_executor() const BOOST_ASIO_NOEXCEPT
1543     {
1544       return stream_.get_executor();
1545     }
1546
1547     template <typename ReadHandler, typename DynamicBuffer_v1, typename RegEx>
1548     void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
1549         BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers, const RegEx& expr) const
1550     {
1551       // If you get an error on the following line it means that your handler
1552       // does not meet the documented type requirements for a ReadHandler.
1553       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
1554
1555       non_const_lvalue<ReadHandler> handler2(handler);
1556       read_until_expr_op_v1<AsyncReadStream,
1557         typename decay<DynamicBuffer_v1>::type,
1558           RegEx, typename decay<ReadHandler>::type>(
1559             stream_, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers),
1560             expr, handler2.value)(boost::system::error_code(), 0, 1);
1561     }
1562
1563   private:
1564     AsyncReadStream& stream_;
1565   };
1566 } // namespace detail
1567
1568 #if !defined(GENERATING_DOCUMENTATION)
1569
1570 template <typename AsyncReadStream, typename DynamicBuffer_v1,
1571     typename RegEx, typename ReadHandler, typename Allocator>
1572 struct associated_allocator<
1573     detail::read_until_expr_op_v1<AsyncReadStream,
1574       DynamicBuffer_v1, RegEx, ReadHandler>,
1575     Allocator>
1576 {
1577   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
1578
1579   static type get(
1580       const detail::read_until_expr_op_v1<AsyncReadStream,
1581         DynamicBuffer_v1, RegEx, ReadHandler>& h,
1582       const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
1583   {
1584     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
1585   }
1586 };
1587
1588 template <typename AsyncReadStream, typename DynamicBuffer_v1,
1589     typename RegEx, typename ReadHandler, typename Executor>
1590 struct associated_executor<
1591     detail::read_until_expr_op_v1<AsyncReadStream,
1592       DynamicBuffer_v1, RegEx, ReadHandler>,
1593     Executor>
1594 {
1595   typedef typename associated_executor<ReadHandler, Executor>::type type;
1596
1597   static type get(
1598       const detail::read_until_expr_op_v1<AsyncReadStream,
1599         DynamicBuffer_v1, RegEx, ReadHandler>& h,
1600       const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
1601   {
1602     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
1603   }
1604 };
1605
1606 #endif // !defined(GENERATING_DOCUMENTATION)
1607
1608 template <typename AsyncReadStream, typename DynamicBuffer_v1,
1609     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
1610       std::size_t)) ReadHandler>
1611 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
1612     void (boost::system::error_code, std::size_t))
1613 async_read_until(AsyncReadStream& s,
1614     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
1615     const boost::regex& expr,
1616     BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
1617     typename enable_if<
1618       is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
1619         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
1620     >::type*)
1621 {
1622   return async_initiate<ReadHandler,
1623     void (boost::system::error_code, std::size_t)>(
1624       detail::initiate_async_read_until_expr_v1<AsyncReadStream>(s),
1625       handler, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers), expr);
1626 }
1627
1628 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
1629
1630 namespace detail
1631 {
1632   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1633       typename MatchCondition, typename ReadHandler>
1634   class read_until_match_op_v1
1635   {
1636   public:
1637     template <typename BufferSequence>
1638     read_until_match_op_v1(AsyncReadStream& stream,
1639         BOOST_ASIO_MOVE_ARG(BufferSequence) buffers,
1640         MatchCondition match_condition, ReadHandler& handler)
1641       : stream_(stream),
1642         buffers_(BOOST_ASIO_MOVE_CAST(BufferSequence)(buffers)),
1643         match_condition_(match_condition),
1644         start_(0),
1645         search_position_(0),
1646         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
1647     {
1648     }
1649
1650 #if defined(BOOST_ASIO_HAS_MOVE)
1651     read_until_match_op_v1(const read_until_match_op_v1& other)
1652       : stream_(other.stream_),
1653         buffers_(other.buffers_),
1654         match_condition_(other.match_condition_),
1655         start_(other.start_),
1656         search_position_(other.search_position_),
1657         handler_(other.handler_)
1658     {
1659     }
1660
1661     read_until_match_op_v1(read_until_match_op_v1&& other)
1662       : stream_(other.stream_),
1663         buffers_(BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(other.buffers_)),
1664         match_condition_(other.match_condition_),
1665         start_(other.start_),
1666         search_position_(other.search_position_),
1667         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
1668     {
1669     }
1670 #endif // defined(BOOST_ASIO_HAS_MOVE)
1671
1672     void operator()(const boost::system::error_code& ec,
1673         std::size_t bytes_transferred, int start = 0)
1674     {
1675       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
1676       std::size_t bytes_to_read;
1677       switch (start_ = start)
1678       {
1679       case 1:
1680         for (;;)
1681         {
1682           {
1683             // Determine the range of the data to be searched.
1684             typedef typename DynamicBuffer_v1::const_buffers_type
1685               buffers_type;
1686             typedef buffers_iterator<buffers_type> iterator;
1687             buffers_type data_buffers = buffers_.data();
1688             iterator begin = iterator::begin(data_buffers);
1689             iterator start_pos = begin + search_position_;
1690             iterator end = iterator::end(data_buffers);
1691
1692             // Look for a match.
1693             std::pair<iterator, bool> result = match_condition_(start_pos, end);
1694             if (result.second)
1695             {
1696               // Full match. We're done.
1697               search_position_ = result.first - begin;
1698               bytes_to_read = 0;
1699             }
1700
1701             // No match yet. Check if buffer is full.
1702             else if (buffers_.size() == buffers_.max_size())
1703             {
1704               search_position_ = not_found;
1705               bytes_to_read = 0;
1706             }
1707
1708             // Need to read some more data.
1709             else
1710             {
1711               if (result.first != end)
1712               {
1713                 // Partial match. Next search needs to start from beginning of
1714                 // match.
1715                 search_position_ = result.first - begin;
1716               }
1717               else
1718               {
1719                 // Next search can start with the new data.
1720                 search_position_ = end - begin;
1721               }
1722
1723               bytes_to_read = std::min<std::size_t>(
1724                     std::max<std::size_t>(512,
1725                       buffers_.capacity() - buffers_.size()),
1726                     std::min<std::size_t>(65536,
1727                       buffers_.max_size() - buffers_.size()));
1728             }
1729           }
1730
1731           // Check if we're done.
1732           if (!start && bytes_to_read == 0)
1733             break;
1734
1735           // Start a new asynchronous read op_v1eration to obtain more data.
1736           stream_.async_read_some(buffers_.prepare(bytes_to_read),
1737               BOOST_ASIO_MOVE_CAST(read_until_match_op_v1)(*this));
1738           return; default:
1739           buffers_.commit(bytes_transferred);
1740           if (ec || bytes_transferred == 0)
1741             break;
1742         }
1743
1744         const boost::system::error_code result_ec =
1745           (search_position_ == not_found)
1746           ? error::not_found : ec;
1747
1748         const std::size_t result_n =
1749           (ec || search_position_ == not_found)
1750           ? 0 : search_position_;
1751
1752         handler_(result_ec, result_n);
1753       }
1754     }
1755
1756   //private:
1757     AsyncReadStream& stream_;
1758     DynamicBuffer_v1 buffers_;
1759     MatchCondition match_condition_;
1760     int start_;
1761     std::size_t search_position_;
1762     ReadHandler handler_;
1763   };
1764
1765   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1766       typename MatchCondition, typename ReadHandler>
1767   inline void* asio_handler_allocate(std::size_t size,
1768       read_until_match_op_v1<AsyncReadStream, DynamicBuffer_v1,
1769         MatchCondition, ReadHandler>* this_handler)
1770   {
1771     return boost_asio_handler_alloc_helpers::allocate(
1772         size, this_handler->handler_);
1773   }
1774
1775   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1776       typename MatchCondition, typename ReadHandler>
1777   inline void asio_handler_deallocate(void* pointer, std::size_t size,
1778       read_until_match_op_v1<AsyncReadStream, DynamicBuffer_v1,
1779         MatchCondition, ReadHandler>* this_handler)
1780   {
1781     boost_asio_handler_alloc_helpers::deallocate(
1782         pointer, size, this_handler->handler_);
1783   }
1784
1785   template <typename AsyncReadStream, typename DynamicBuffer_v1,
1786       typename MatchCondition, typename ReadHandler>
1787   inline bool asio_handler_is_continuation(
1788       read_until_match_op_v1<AsyncReadStream, DynamicBuffer_v1,
1789         MatchCondition, ReadHandler>* this_handler)
1790   {
1791     return this_handler->start_ == 0 ? true
1792       : boost_asio_handler_cont_helpers::is_continuation(
1793           this_handler->handler_);
1794   }
1795
1796   template <typename Function, typename AsyncReadStream,
1797       typename DynamicBuffer_v1, typename MatchCondition,
1798       typename ReadHandler>
1799   inline void asio_handler_invoke(Function& function,
1800       read_until_match_op_v1<AsyncReadStream, DynamicBuffer_v1,
1801         MatchCondition, ReadHandler>* this_handler)
1802   {
1803     boost_asio_handler_invoke_helpers::invoke(
1804         function, this_handler->handler_);
1805   }
1806
1807   template <typename Function, typename AsyncReadStream,
1808       typename DynamicBuffer_v1, typename MatchCondition,
1809       typename ReadHandler>
1810   inline void asio_handler_invoke(const Function& function,
1811       read_until_match_op_v1<AsyncReadStream, DynamicBuffer_v1,
1812       MatchCondition, ReadHandler>* this_handler)
1813   {
1814     boost_asio_handler_invoke_helpers::invoke(
1815         function, this_handler->handler_);
1816   }
1817
1818   template <typename AsyncReadStream>
1819   class initiate_async_read_until_match_v1
1820   {
1821   public:
1822     typedef typename AsyncReadStream::executor_type executor_type;
1823
1824     explicit initiate_async_read_until_match_v1(AsyncReadStream& stream)
1825       : stream_(stream)
1826     {
1827     }
1828
1829     executor_type get_executor() const BOOST_ASIO_NOEXCEPT
1830     {
1831       return stream_.get_executor();
1832     }
1833
1834     template <typename ReadHandler,
1835         typename DynamicBuffer_v1, typename MatchCondition>
1836     void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
1837         BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
1838         MatchCondition match_condition) const
1839     {
1840       // If you get an error on the following line it means that your handler
1841       // does not meet the documented type requirements for a ReadHandler.
1842       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
1843
1844       non_const_lvalue<ReadHandler> handler2(handler);
1845       read_until_match_op_v1<AsyncReadStream,
1846         typename decay<DynamicBuffer_v1>::type,
1847           MatchCondition, typename decay<ReadHandler>::type>(
1848             stream_, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers),
1849             match_condition, handler2.value)(boost::system::error_code(), 0, 1);
1850     }
1851
1852   private:
1853     AsyncReadStream& stream_;
1854   };
1855 } // namespace detail
1856
1857 #if !defined(GENERATING_DOCUMENTATION)
1858
1859 template <typename AsyncReadStream, typename DynamicBuffer_v1,
1860     typename MatchCondition, typename ReadHandler, typename Allocator>
1861 struct associated_allocator<
1862     detail::read_until_match_op_v1<AsyncReadStream,
1863       DynamicBuffer_v1, MatchCondition, ReadHandler>,
1864     Allocator>
1865 {
1866   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
1867
1868   static type get(
1869       const detail::read_until_match_op_v1<AsyncReadStream,
1870         DynamicBuffer_v1, MatchCondition, ReadHandler>& h,
1871       const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
1872   {
1873     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
1874   }
1875 };
1876
1877 template <typename AsyncReadStream, typename DynamicBuffer_v1,
1878     typename MatchCondition, typename ReadHandler, typename Executor>
1879 struct associated_executor<
1880     detail::read_until_match_op_v1<AsyncReadStream,
1881       DynamicBuffer_v1, MatchCondition, ReadHandler>,
1882     Executor>
1883 {
1884   typedef typename associated_executor<ReadHandler, Executor>::type type;
1885
1886   static type get(
1887       const detail::read_until_match_op_v1<AsyncReadStream,
1888         DynamicBuffer_v1, MatchCondition, ReadHandler>& h,
1889       const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
1890   {
1891     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
1892   }
1893 };
1894
1895 #endif // !defined(GENERATING_DOCUMENTATION)
1896
1897 template <typename AsyncReadStream,
1898     typename DynamicBuffer_v1, typename MatchCondition,
1899     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
1900       std::size_t)) ReadHandler>
1901 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
1902     void (boost::system::error_code, std::size_t))
1903 async_read_until(AsyncReadStream& s,
1904     BOOST_ASIO_MOVE_ARG(DynamicBuffer_v1) buffers,
1905     MatchCondition match_condition, BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
1906     typename enable_if<
1907       is_match_condition<MatchCondition>::value
1908         && is_dynamic_buffer_v1<typename decay<DynamicBuffer_v1>::type>::value
1909         && !is_dynamic_buffer_v2<typename decay<DynamicBuffer_v1>::type>::value
1910     >::type*)
1911 {
1912   return async_initiate<ReadHandler,
1913     void (boost::system::error_code, std::size_t)>(
1914       detail::initiate_async_read_until_match_v1<AsyncReadStream>(s), handler,
1915       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v1)(buffers), match_condition);
1916 }
1917
1918 #if !defined(BOOST_ASIO_NO_IOSTREAM)
1919
1920 template <typename AsyncReadStream, typename Allocator,
1921     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
1922       std::size_t)) ReadHandler>
1923 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
1924     void (boost::system::error_code, std::size_t))
1925 async_read_until(AsyncReadStream& s,
1926     boost::asio::basic_streambuf<Allocator>& b,
1927     char delim, BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
1928 {
1929   return async_read_until(s, basic_streambuf_ref<Allocator>(b),
1930       delim, BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
1931 }
1932
1933 template <typename AsyncReadStream, typename Allocator,
1934     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
1935       std::size_t)) ReadHandler>
1936 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
1937     void (boost::system::error_code, std::size_t))
1938 async_read_until(AsyncReadStream& s,
1939     boost::asio::basic_streambuf<Allocator>& b,
1940     BOOST_ASIO_STRING_VIEW_PARAM delim,
1941     BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
1942 {
1943   return async_read_until(s, basic_streambuf_ref<Allocator>(b),
1944       delim, BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
1945 }
1946
1947 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
1948
1949 template <typename AsyncReadStream, typename Allocator,
1950     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
1951       std::size_t)) ReadHandler>
1952 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
1953     void (boost::system::error_code, std::size_t))
1954 async_read_until(AsyncReadStream& s,
1955     boost::asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
1956     BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
1957 {
1958   return async_read_until(s, basic_streambuf_ref<Allocator>(b),
1959       expr, BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
1960 }
1961
1962 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
1963
1964 template <typename AsyncReadStream, typename Allocator, typename MatchCondition,
1965     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
1966       std::size_t)) ReadHandler>
1967 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
1968     void (boost::system::error_code, std::size_t))
1969 async_read_until(AsyncReadStream& s,
1970     boost::asio::basic_streambuf<Allocator>& b,
1971     MatchCondition match_condition, BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
1972     typename enable_if<is_match_condition<MatchCondition>::value>::type*)
1973 {
1974   return async_read_until(s, basic_streambuf_ref<Allocator>(b),
1975       match_condition, BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
1976 }
1977
1978 #endif // !defined(BOOST_ASIO_NO_IOSTREAM)
1979 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
1980 #endif // !defined(BOOST_ASIO_NO_DYNAMIC_BUFFER_V1)
1981
1982 namespace detail
1983 {
1984   template <typename AsyncReadStream,
1985       typename DynamicBuffer_v2, typename ReadHandler>
1986   class read_until_delim_op_v2
1987   {
1988   public:
1989     template <typename BufferSequence>
1990     read_until_delim_op_v2(AsyncReadStream& stream,
1991         BOOST_ASIO_MOVE_ARG(BufferSequence) buffers,
1992         char delim, ReadHandler& handler)
1993       : stream_(stream),
1994         buffers_(BOOST_ASIO_MOVE_CAST(BufferSequence)(buffers)),
1995         delim_(delim),
1996         start_(0),
1997         search_position_(0),
1998         bytes_to_read_(0),
1999         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
2000     {
2001     }
2002
2003 #if defined(BOOST_ASIO_HAS_MOVE)
2004     read_until_delim_op_v2(const read_until_delim_op_v2& other)
2005       : stream_(other.stream_),
2006         buffers_(other.buffers_),
2007         delim_(other.delim_),
2008         start_(other.start_),
2009         search_position_(other.search_position_),
2010         bytes_to_read_(other.bytes_to_read_),
2011         handler_(other.handler_)
2012     {
2013     }
2014
2015     read_until_delim_op_v2(read_until_delim_op_v2&& other)
2016       : stream_(other.stream_),
2017         buffers_(BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(other.buffers_)),
2018         delim_(other.delim_),
2019         start_(other.start_),
2020         search_position_(other.search_position_),
2021         bytes_to_read_(other.bytes_to_read_),
2022         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
2023     {
2024     }
2025 #endif // defined(BOOST_ASIO_HAS_MOVE)
2026
2027     void operator()(const boost::system::error_code& ec,
2028         std::size_t bytes_transferred, int start = 0)
2029     {
2030       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
2031       std::size_t pos;
2032       switch (start_ = start)
2033       {
2034       case 1:
2035         for (;;)
2036         {
2037           {
2038             // Determine the range of the data to be searched.
2039             typedef typename DynamicBuffer_v2::const_buffers_type
2040               buffers_type;
2041             typedef buffers_iterator<buffers_type> iterator;
2042             buffers_type data_buffers =
2043               const_cast<const DynamicBuffer_v2&>(buffers_).data(
2044                   0, buffers_.size());
2045             iterator begin = iterator::begin(data_buffers);
2046             iterator start_pos = begin + search_position_;
2047             iterator end = iterator::end(data_buffers);
2048
2049             // Look for a match.
2050             iterator iter = std::find(start_pos, end, delim_);
2051             if (iter != end)
2052             {
2053               // Found a match. We're done.
2054               search_position_ = iter - begin + 1;
2055               bytes_to_read_ = 0;
2056             }
2057
2058             // No match yet. Check if buffer is full.
2059             else if (buffers_.size() == buffers_.max_size())
2060             {
2061               search_position_ = not_found;
2062               bytes_to_read_ = 0;
2063             }
2064
2065             // Need to read some more data.
2066             else
2067             {
2068               // Next search can start with the new data.
2069               search_position_ = end - begin;
2070               bytes_to_read_ = std::min<std::size_t>(
2071                     std::max<std::size_t>(512,
2072                       buffers_.capacity() - buffers_.size()),
2073                     std::min<std::size_t>(65536,
2074                       buffers_.max_size() - buffers_.size()));
2075             }
2076           }
2077
2078           // Check if we're done.
2079           if (!start && bytes_to_read_ == 0)
2080             break;
2081
2082           // Start a new asynchronous read op_v2eration to obtain more data.
2083           pos = buffers_.size();
2084           buffers_.grow(bytes_to_read_);
2085           stream_.async_read_some(buffers_.data(pos, bytes_to_read_),
2086               BOOST_ASIO_MOVE_CAST(read_until_delim_op_v2)(*this));
2087           return; default:
2088           buffers_.shrink(bytes_to_read_ - bytes_transferred);
2089           if (ec || bytes_transferred == 0)
2090             break;
2091         }
2092
2093         const boost::system::error_code result_ec =
2094           (search_position_ == not_found)
2095           ? error::not_found : ec;
2096
2097         const std::size_t result_n =
2098           (ec || search_position_ == not_found)
2099           ? 0 : search_position_;
2100
2101         handler_(result_ec, result_n);
2102       }
2103     }
2104
2105   //private:
2106     AsyncReadStream& stream_;
2107     DynamicBuffer_v2 buffers_;
2108     char delim_;
2109     int start_;
2110     std::size_t search_position_;
2111     std::size_t bytes_to_read_;
2112     ReadHandler handler_;
2113   };
2114
2115   template <typename AsyncReadStream,
2116       typename DynamicBuffer_v2, typename ReadHandler>
2117   inline void* asio_handler_allocate(std::size_t size,
2118       read_until_delim_op_v2<AsyncReadStream,
2119         DynamicBuffer_v2, ReadHandler>* this_handler)
2120   {
2121     return boost_asio_handler_alloc_helpers::allocate(
2122         size, this_handler->handler_);
2123   }
2124
2125   template <typename AsyncReadStream,
2126       typename DynamicBuffer_v2, typename ReadHandler>
2127   inline void asio_handler_deallocate(void* pointer, std::size_t size,
2128       read_until_delim_op_v2<AsyncReadStream,
2129         DynamicBuffer_v2, ReadHandler>* this_handler)
2130   {
2131     boost_asio_handler_alloc_helpers::deallocate(
2132         pointer, size, this_handler->handler_);
2133   }
2134
2135   template <typename AsyncReadStream,
2136       typename DynamicBuffer_v2, typename ReadHandler>
2137   inline bool asio_handler_is_continuation(
2138       read_until_delim_op_v2<AsyncReadStream,
2139         DynamicBuffer_v2, ReadHandler>* this_handler)
2140   {
2141     return this_handler->start_ == 0 ? true
2142       : boost_asio_handler_cont_helpers::is_continuation(
2143           this_handler->handler_);
2144   }
2145
2146   template <typename Function, typename AsyncReadStream,
2147       typename DynamicBuffer_v2, typename ReadHandler>
2148   inline void asio_handler_invoke(Function& function,
2149       read_until_delim_op_v2<AsyncReadStream,
2150         DynamicBuffer_v2, ReadHandler>* this_handler)
2151   {
2152     boost_asio_handler_invoke_helpers::invoke(
2153         function, this_handler->handler_);
2154   }
2155
2156   template <typename Function, typename AsyncReadStream,
2157       typename DynamicBuffer_v2, typename ReadHandler>
2158   inline void asio_handler_invoke(const Function& function,
2159       read_until_delim_op_v2<AsyncReadStream,
2160         DynamicBuffer_v2, ReadHandler>* this_handler)
2161   {
2162     boost_asio_handler_invoke_helpers::invoke(
2163         function, this_handler->handler_);
2164   }
2165
2166   template <typename AsyncReadStream>
2167   class initiate_async_read_until_delim_v2
2168   {
2169   public:
2170     typedef typename AsyncReadStream::executor_type executor_type;
2171
2172     explicit initiate_async_read_until_delim_v2(AsyncReadStream& stream)
2173       : stream_(stream)
2174     {
2175     }
2176
2177     executor_type get_executor() const BOOST_ASIO_NOEXCEPT
2178     {
2179       return stream_.get_executor();
2180     }
2181
2182     template <typename ReadHandler, typename DynamicBuffer_v2>
2183     void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
2184         BOOST_ASIO_MOVE_ARG(DynamicBuffer_v2) buffers, char delim) const
2185     {
2186       // If you get an error on the following line it means that your handler
2187       // does not meet the documented type requirements for a ReadHandler.
2188       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
2189
2190       non_const_lvalue<ReadHandler> handler2(handler);
2191       read_until_delim_op_v2<AsyncReadStream,
2192         typename decay<DynamicBuffer_v2>::type,
2193           typename decay<ReadHandler>::type>(
2194             stream_, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers),
2195             delim, handler2.value)(boost::system::error_code(), 0, 1);
2196     }
2197
2198   private:
2199     AsyncReadStream& stream_;
2200   };
2201 } // namespace detail
2202
2203 #if !defined(GENERATING_DOCUMENTATION)
2204
2205 template <typename AsyncReadStream, typename DynamicBuffer_v2,
2206     typename ReadHandler, typename Allocator>
2207 struct associated_allocator<
2208     detail::read_until_delim_op_v2<AsyncReadStream,
2209       DynamicBuffer_v2, ReadHandler>,
2210     Allocator>
2211 {
2212   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
2213
2214   static type get(
2215       const detail::read_until_delim_op_v2<AsyncReadStream,
2216         DynamicBuffer_v2, ReadHandler>& h,
2217       const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
2218   {
2219     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
2220   }
2221 };
2222
2223 template <typename AsyncReadStream, typename DynamicBuffer_v2,
2224     typename ReadHandler, typename Executor>
2225 struct associated_executor<
2226     detail::read_until_delim_op_v2<AsyncReadStream,
2227       DynamicBuffer_v2, ReadHandler>,
2228     Executor>
2229 {
2230   typedef typename associated_executor<ReadHandler, Executor>::type type;
2231
2232   static type get(
2233       const detail::read_until_delim_op_v2<AsyncReadStream,
2234         DynamicBuffer_v2, ReadHandler>& h,
2235       const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
2236   {
2237     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
2238   }
2239 };
2240
2241 #endif // !defined(GENERATING_DOCUMENTATION)
2242
2243 template <typename AsyncReadStream, typename DynamicBuffer_v2,
2244     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
2245       std::size_t)) ReadHandler>
2246 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
2247     void (boost::system::error_code, std::size_t))
2248 async_read_until(AsyncReadStream& s, DynamicBuffer_v2 buffers,
2249     char delim, BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
2250     typename enable_if<
2251       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
2252     >::type*)
2253 {
2254   return async_initiate<ReadHandler,
2255     void (boost::system::error_code, std::size_t)>(
2256       detail::initiate_async_read_until_delim_v2<AsyncReadStream>(s),
2257       handler, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers), delim);
2258 }
2259
2260 namespace detail
2261 {
2262   template <typename AsyncReadStream,
2263       typename DynamicBuffer_v2, typename ReadHandler>
2264   class read_until_delim_string_op_v2
2265   {
2266   public:
2267     template <typename BufferSequence>
2268     read_until_delim_string_op_v2(AsyncReadStream& stream,
2269         BOOST_ASIO_MOVE_ARG(BufferSequence) buffers,
2270         const std::string& delim, ReadHandler& handler)
2271       : stream_(stream),
2272         buffers_(BOOST_ASIO_MOVE_CAST(BufferSequence)(buffers)),
2273         delim_(delim),
2274         start_(0),
2275         search_position_(0),
2276         bytes_to_read_(0),
2277         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
2278     {
2279     }
2280
2281 #if defined(BOOST_ASIO_HAS_MOVE)
2282     read_until_delim_string_op_v2(const read_until_delim_string_op_v2& other)
2283       : stream_(other.stream_),
2284         buffers_(other.buffers_),
2285         delim_(other.delim_),
2286         start_(other.start_),
2287         search_position_(other.search_position_),
2288         bytes_to_read_(other.bytes_to_read_),
2289         handler_(other.handler_)
2290     {
2291     }
2292
2293     read_until_delim_string_op_v2(read_until_delim_string_op_v2&& other)
2294       : stream_(other.stream_),
2295         buffers_(BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(other.buffers_)),
2296         delim_(BOOST_ASIO_MOVE_CAST(std::string)(other.delim_)),
2297         start_(other.start_),
2298         search_position_(other.search_position_),
2299         bytes_to_read_(other.bytes_to_read_),
2300         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
2301     {
2302     }
2303 #endif // defined(BOOST_ASIO_HAS_MOVE)
2304
2305     void operator()(const boost::system::error_code& ec,
2306         std::size_t bytes_transferred, int start = 0)
2307     {
2308       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
2309       std::size_t pos;
2310       switch (start_ = start)
2311       {
2312       case 1:
2313         for (;;)
2314         {
2315           {
2316             // Determine the range of the data to be searched.
2317             typedef typename DynamicBuffer_v2::const_buffers_type
2318               buffers_type;
2319             typedef buffers_iterator<buffers_type> iterator;
2320             buffers_type data_buffers =
2321               const_cast<const DynamicBuffer_v2&>(buffers_).data(
2322                   0, buffers_.size());
2323             iterator begin = iterator::begin(data_buffers);
2324             iterator start_pos = begin + search_position_;
2325             iterator end = iterator::end(data_buffers);
2326
2327             // Look for a match.
2328             std::pair<iterator, bool> result = detail::partial_search(
2329                 start_pos, end, delim_.begin(), delim_.end());
2330             if (result.first != end && result.second)
2331             {
2332               // Full match. We're done.
2333               search_position_ = result.first - begin + delim_.length();
2334               bytes_to_read_ = 0;
2335             }
2336
2337             // No match yet. Check if buffer is full.
2338             else if (buffers_.size() == buffers_.max_size())
2339             {
2340               search_position_ = not_found;
2341               bytes_to_read_ = 0;
2342             }
2343
2344             // Need to read some more data.
2345             else
2346             {
2347               if (result.first != end)
2348               {
2349                 // Partial match. Next search needs to start from beginning of
2350                 // match.
2351                 search_position_ = result.first - begin;
2352               }
2353               else
2354               {
2355                 // Next search can start with the new data.
2356                 search_position_ = end - begin;
2357               }
2358
2359               bytes_to_read_ = std::min<std::size_t>(
2360                     std::max<std::size_t>(512,
2361                       buffers_.capacity() - buffers_.size()),
2362                     std::min<std::size_t>(65536,
2363                       buffers_.max_size() - buffers_.size()));
2364             }
2365           }
2366
2367           // Check if we're done.
2368           if (!start && bytes_to_read_ == 0)
2369             break;
2370
2371           // Start a new asynchronous read op_v2eration to obtain more data.
2372           pos = buffers_.size();
2373           buffers_.grow(bytes_to_read_);
2374           stream_.async_read_some(buffers_.data(pos, bytes_to_read_),
2375               BOOST_ASIO_MOVE_CAST(read_until_delim_string_op_v2)(*this));
2376           return; default:
2377           buffers_.shrink(bytes_to_read_ - bytes_transferred);
2378           if (ec || bytes_transferred == 0)
2379             break;
2380         }
2381
2382         const boost::system::error_code result_ec =
2383           (search_position_ == not_found)
2384           ? error::not_found : ec;
2385
2386         const std::size_t result_n =
2387           (ec || search_position_ == not_found)
2388           ? 0 : search_position_;
2389
2390         handler_(result_ec, result_n);
2391       }
2392     }
2393
2394   //private:
2395     AsyncReadStream& stream_;
2396     DynamicBuffer_v2 buffers_;
2397     std::string delim_;
2398     int start_;
2399     std::size_t search_position_;
2400     std::size_t bytes_to_read_;
2401     ReadHandler handler_;
2402   };
2403
2404   template <typename AsyncReadStream,
2405       typename DynamicBuffer_v2, typename ReadHandler>
2406   inline void* asio_handler_allocate(std::size_t size,
2407       read_until_delim_string_op_v2<AsyncReadStream,
2408         DynamicBuffer_v2, ReadHandler>* this_handler)
2409   {
2410     return boost_asio_handler_alloc_helpers::allocate(
2411         size, this_handler->handler_);
2412   }
2413
2414   template <typename AsyncReadStream,
2415       typename DynamicBuffer_v2, typename ReadHandler>
2416   inline void asio_handler_deallocate(void* pointer, std::size_t size,
2417       read_until_delim_string_op_v2<AsyncReadStream,
2418         DynamicBuffer_v2, ReadHandler>* this_handler)
2419   {
2420     boost_asio_handler_alloc_helpers::deallocate(
2421         pointer, size, this_handler->handler_);
2422   }
2423
2424   template <typename AsyncReadStream,
2425       typename DynamicBuffer_v2, typename ReadHandler>
2426   inline bool asio_handler_is_continuation(
2427       read_until_delim_string_op_v2<AsyncReadStream,
2428         DynamicBuffer_v2, ReadHandler>* this_handler)
2429   {
2430     return this_handler->start_ == 0 ? true
2431       : boost_asio_handler_cont_helpers::is_continuation(
2432           this_handler->handler_);
2433   }
2434
2435   template <typename Function, typename AsyncReadStream,
2436       typename DynamicBuffer_v2, typename ReadHandler>
2437   inline void asio_handler_invoke(Function& function,
2438       read_until_delim_string_op_v2<AsyncReadStream,
2439         DynamicBuffer_v2, ReadHandler>* this_handler)
2440   {
2441     boost_asio_handler_invoke_helpers::invoke(
2442         function, this_handler->handler_);
2443   }
2444
2445   template <typename Function, typename AsyncReadStream,
2446       typename DynamicBuffer_v2, typename ReadHandler>
2447   inline void asio_handler_invoke(const Function& function,
2448       read_until_delim_string_op_v2<AsyncReadStream,
2449         DynamicBuffer_v2, ReadHandler>* this_handler)
2450   {
2451     boost_asio_handler_invoke_helpers::invoke(
2452         function, this_handler->handler_);
2453   }
2454
2455   template <typename AsyncReadStream>
2456   class initiate_async_read_until_delim_string_v2
2457   {
2458   public:
2459     typedef typename AsyncReadStream::executor_type executor_type;
2460
2461     explicit initiate_async_read_until_delim_string_v2(AsyncReadStream& stream)
2462       : stream_(stream)
2463     {
2464     }
2465
2466     executor_type get_executor() const BOOST_ASIO_NOEXCEPT
2467     {
2468       return stream_.get_executor();
2469     }
2470
2471     template <typename ReadHandler, typename DynamicBuffer_v2>
2472     void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
2473         BOOST_ASIO_MOVE_ARG(DynamicBuffer_v2) buffers,
2474         const std::string& delim) const
2475     {
2476       // If you get an error on the following line it means that your handler
2477       // does not meet the documented type requirements for a ReadHandler.
2478       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
2479
2480       non_const_lvalue<ReadHandler> handler2(handler);
2481       read_until_delim_string_op_v2<AsyncReadStream,
2482         typename decay<DynamicBuffer_v2>::type,
2483           typename decay<ReadHandler>::type>(
2484             stream_, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers),
2485             delim, handler2.value)(boost::system::error_code(), 0, 1);
2486     }
2487
2488   private:
2489     AsyncReadStream& stream_;
2490   };
2491 } // namespace detail
2492
2493 #if !defined(GENERATING_DOCUMENTATION)
2494
2495 template <typename AsyncReadStream, typename DynamicBuffer_v2,
2496     typename ReadHandler, typename Allocator>
2497 struct associated_allocator<
2498     detail::read_until_delim_string_op_v2<AsyncReadStream,
2499       DynamicBuffer_v2, ReadHandler>,
2500     Allocator>
2501 {
2502   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
2503
2504   static type get(
2505       const detail::read_until_delim_string_op_v2<AsyncReadStream,
2506         DynamicBuffer_v2, ReadHandler>& h,
2507       const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
2508   {
2509     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
2510   }
2511 };
2512
2513 template <typename AsyncReadStream, typename DynamicBuffer_v2,
2514     typename ReadHandler, typename Executor>
2515 struct associated_executor<
2516     detail::read_until_delim_string_op_v2<AsyncReadStream,
2517       DynamicBuffer_v2, ReadHandler>,
2518     Executor>
2519 {
2520   typedef typename associated_executor<ReadHandler, Executor>::type type;
2521
2522   static type get(
2523       const detail::read_until_delim_string_op_v2<AsyncReadStream,
2524         DynamicBuffer_v2, ReadHandler>& h,
2525       const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
2526   {
2527     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
2528   }
2529 };
2530
2531 #endif // !defined(GENERATING_DOCUMENTATION)
2532
2533 template <typename AsyncReadStream,
2534     typename DynamicBuffer_v2,
2535     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
2536       std::size_t)) ReadHandler>
2537 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
2538     void (boost::system::error_code, std::size_t))
2539 async_read_until(AsyncReadStream& s,
2540     DynamicBuffer_v2 buffers, BOOST_ASIO_STRING_VIEW_PARAM delim,
2541     BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
2542     typename enable_if<
2543       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
2544     >::type*)
2545 {
2546   return async_initiate<ReadHandler,
2547     void (boost::system::error_code, std::size_t)>(
2548       detail::initiate_async_read_until_delim_string_v2<AsyncReadStream>(s),
2549       handler, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers),
2550       static_cast<std::string>(delim));
2551 }
2552
2553 #if !defined(BOOST_ASIO_NO_EXTENSIONS)
2554 #if defined(BOOST_ASIO_HAS_BOOST_REGEX)
2555
2556 namespace detail
2557 {
2558   template <typename AsyncReadStream, typename DynamicBuffer_v2,
2559       typename RegEx, typename ReadHandler>
2560   class read_until_expr_op_v2
2561   {
2562   public:
2563     template <typename BufferSequence>
2564     read_until_expr_op_v2(AsyncReadStream& stream,
2565         BOOST_ASIO_MOVE_ARG(BufferSequence) buffers,
2566         const boost::regex& expr, ReadHandler& handler)
2567       : stream_(stream),
2568         buffers_(BOOST_ASIO_MOVE_CAST(BufferSequence)(buffers)),
2569         expr_(expr),
2570         start_(0),
2571         search_position_(0),
2572         bytes_to_read_(0),
2573         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
2574     {
2575     }
2576
2577 #if defined(BOOST_ASIO_HAS_MOVE)
2578     read_until_expr_op_v2(const read_until_expr_op_v2& other)
2579       : stream_(other.stream_),
2580         buffers_(other.buffers_),
2581         expr_(other.expr_),
2582         start_(other.start_),
2583         search_position_(other.search_position_),
2584         bytes_to_read_(other.bytes_to_read_),
2585         handler_(other.handler_)
2586     {
2587     }
2588
2589     read_until_expr_op_v2(read_until_expr_op_v2&& other)
2590       : stream_(other.stream_),
2591         buffers_(BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(other.buffers_)),
2592         expr_(other.expr_),
2593         start_(other.start_),
2594         search_position_(other.search_position_),
2595         bytes_to_read_(other.bytes_to_read_),
2596         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
2597     {
2598     }
2599 #endif // defined(BOOST_ASIO_HAS_MOVE)
2600
2601     void operator()(const boost::system::error_code& ec,
2602         std::size_t bytes_transferred, int start = 0)
2603     {
2604       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
2605       std::size_t pos;
2606       switch (start_ = start)
2607       {
2608       case 1:
2609         for (;;)
2610         {
2611           {
2612             // Determine the range of the data to be searched.
2613             typedef typename DynamicBuffer_v2::const_buffers_type
2614               buffers_type;
2615             typedef buffers_iterator<buffers_type> iterator;
2616             buffers_type data_buffers =
2617               const_cast<const DynamicBuffer_v2&>(buffers_).data(
2618                   0, buffers_.size());
2619             iterator begin = iterator::begin(data_buffers);
2620             iterator start_pos = begin + search_position_;
2621             iterator end = iterator::end(data_buffers);
2622
2623             // Look for a match.
2624             boost::match_results<iterator,
2625               typename std::vector<boost::sub_match<iterator> >::allocator_type>
2626                 match_results;
2627             bool match = regex_search(start_pos, end, match_results, expr_,
2628                 boost::match_default | boost::match_partial);
2629             if (match && match_results[0].matched)
2630             {
2631               // Full match. We're done.
2632               search_position_ = match_results[0].second - begin;
2633               bytes_to_read_ = 0;
2634             }
2635
2636             // No match yet. Check if buffer is full.
2637             else if (buffers_.size() == buffers_.max_size())
2638             {
2639               search_position_ = not_found;
2640               bytes_to_read_ = 0;
2641             }
2642
2643             // Need to read some more data.
2644             else
2645             {
2646               if (match)
2647               {
2648                 // Partial match. Next search needs to start from beginning of
2649                 // match.
2650                 search_position_ = match_results[0].first - begin;
2651               }
2652               else
2653               {
2654                 // Next search can start with the new data.
2655                 search_position_ = end - begin;
2656               }
2657
2658               bytes_to_read_ = std::min<std::size_t>(
2659                     std::max<std::size_t>(512,
2660                       buffers_.capacity() - buffers_.size()),
2661                     std::min<std::size_t>(65536,
2662                       buffers_.max_size() - buffers_.size()));
2663             }
2664           }
2665
2666           // Check if we're done.
2667           if (!start && bytes_to_read_ == 0)
2668             break;
2669
2670           // Start a new asynchronous read op_v2eration to obtain more data.
2671           pos = buffers_.size();
2672           buffers_.grow(bytes_to_read_);
2673           stream_.async_read_some(buffers_.data(pos, bytes_to_read_),
2674               BOOST_ASIO_MOVE_CAST(read_until_expr_op_v2)(*this));
2675           return; default:
2676           buffers_.shrink(bytes_to_read_ - bytes_transferred);
2677           if (ec || bytes_transferred == 0)
2678             break;
2679         }
2680
2681         const boost::system::error_code result_ec =
2682           (search_position_ == not_found)
2683           ? error::not_found : ec;
2684
2685         const std::size_t result_n =
2686           (ec || search_position_ == not_found)
2687           ? 0 : search_position_;
2688
2689         handler_(result_ec, result_n);
2690       }
2691     }
2692
2693   //private:
2694     AsyncReadStream& stream_;
2695     DynamicBuffer_v2 buffers_;
2696     RegEx expr_;
2697     int start_;
2698     std::size_t search_position_;
2699     std::size_t bytes_to_read_;
2700     ReadHandler handler_;
2701   };
2702
2703   template <typename AsyncReadStream, typename DynamicBuffer_v2,
2704       typename RegEx, typename ReadHandler>
2705   inline void* asio_handler_allocate(std::size_t size,
2706       read_until_expr_op_v2<AsyncReadStream,
2707         DynamicBuffer_v2, RegEx, ReadHandler>* this_handler)
2708   {
2709     return boost_asio_handler_alloc_helpers::allocate(
2710         size, this_handler->handler_);
2711   }
2712
2713   template <typename AsyncReadStream, typename DynamicBuffer_v2,
2714       typename RegEx, typename ReadHandler>
2715   inline void asio_handler_deallocate(void* pointer, std::size_t size,
2716       read_until_expr_op_v2<AsyncReadStream,
2717         DynamicBuffer_v2, RegEx, ReadHandler>* this_handler)
2718   {
2719     boost_asio_handler_alloc_helpers::deallocate(
2720         pointer, size, this_handler->handler_);
2721   }
2722
2723   template <typename AsyncReadStream, typename DynamicBuffer_v2,
2724       typename RegEx, typename ReadHandler>
2725   inline bool asio_handler_is_continuation(
2726       read_until_expr_op_v2<AsyncReadStream,
2727         DynamicBuffer_v2, RegEx, ReadHandler>* this_handler)
2728   {
2729     return this_handler->start_ == 0 ? true
2730       : boost_asio_handler_cont_helpers::is_continuation(
2731           this_handler->handler_);
2732   }
2733
2734   template <typename Function, typename AsyncReadStream,
2735       typename DynamicBuffer_v2, typename RegEx, typename ReadHandler>
2736   inline void asio_handler_invoke(Function& function,
2737       read_until_expr_op_v2<AsyncReadStream,
2738         DynamicBuffer_v2, RegEx, ReadHandler>* this_handler)
2739   {
2740     boost_asio_handler_invoke_helpers::invoke(
2741         function, this_handler->handler_);
2742   }
2743
2744   template <typename Function, typename AsyncReadStream,
2745       typename DynamicBuffer_v2, typename RegEx, typename ReadHandler>
2746   inline void asio_handler_invoke(const Function& function,
2747       read_until_expr_op_v2<AsyncReadStream,
2748         DynamicBuffer_v2, RegEx, ReadHandler>* this_handler)
2749   {
2750     boost_asio_handler_invoke_helpers::invoke(
2751         function, this_handler->handler_);
2752   }
2753
2754   template <typename AsyncReadStream>
2755   class initiate_async_read_until_expr_v2
2756   {
2757   public:
2758     typedef typename AsyncReadStream::executor_type executor_type;
2759
2760     explicit initiate_async_read_until_expr_v2(AsyncReadStream& stream)
2761       : stream_(stream)
2762     {
2763     }
2764
2765     executor_type get_executor() const BOOST_ASIO_NOEXCEPT
2766     {
2767       return stream_.get_executor();
2768     }
2769
2770     template <typename ReadHandler, typename DynamicBuffer_v2, typename RegEx>
2771     void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
2772         BOOST_ASIO_MOVE_ARG(DynamicBuffer_v2) buffers,
2773         const RegEx& expr) const
2774     {
2775       // If you get an error on the following line it means that your handler
2776       // does not meet the documented type requirements for a ReadHandler.
2777       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
2778
2779       non_const_lvalue<ReadHandler> handler2(handler);
2780       read_until_expr_op_v2<AsyncReadStream,
2781         typename decay<DynamicBuffer_v2>::type,
2782           RegEx, typename decay<ReadHandler>::type>(
2783             stream_, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers),
2784             expr, handler2.value)(boost::system::error_code(), 0, 1);
2785     }
2786
2787   private:
2788     AsyncReadStream& stream_;
2789   };
2790 } // namespace detail
2791
2792 #if !defined(GENERATING_DOCUMENTATION)
2793
2794 template <typename AsyncReadStream, typename DynamicBuffer_v2,
2795     typename RegEx, typename ReadHandler, typename Allocator>
2796 struct associated_allocator<
2797     detail::read_until_expr_op_v2<AsyncReadStream,
2798       DynamicBuffer_v2, RegEx, ReadHandler>,
2799     Allocator>
2800 {
2801   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
2802
2803   static type get(
2804       const detail::read_until_expr_op_v2<AsyncReadStream,
2805         DynamicBuffer_v2, RegEx, ReadHandler>& h,
2806       const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
2807   {
2808     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
2809   }
2810 };
2811
2812 template <typename AsyncReadStream, typename DynamicBuffer_v2,
2813     typename RegEx, typename ReadHandler, typename Executor>
2814 struct associated_executor<
2815     detail::read_until_expr_op_v2<AsyncReadStream,
2816       DynamicBuffer_v2, RegEx, ReadHandler>,
2817     Executor>
2818 {
2819   typedef typename associated_executor<ReadHandler, Executor>::type type;
2820
2821   static type get(
2822       const detail::read_until_expr_op_v2<AsyncReadStream,
2823         DynamicBuffer_v2, RegEx, ReadHandler>& h,
2824       const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
2825   {
2826     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
2827   }
2828 };
2829
2830 #endif // !defined(GENERATING_DOCUMENTATION)
2831
2832 template <typename AsyncReadStream, typename DynamicBuffer_v2,
2833     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
2834       std::size_t)) ReadHandler>
2835 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
2836     void (boost::system::error_code, std::size_t))
2837 async_read_until(AsyncReadStream& s, DynamicBuffer_v2 buffers,
2838     const boost::regex& expr, BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
2839     typename enable_if<
2840       is_dynamic_buffer_v2<DynamicBuffer_v2>::value
2841     >::type*)
2842 {
2843   return async_initiate<ReadHandler,
2844     void (boost::system::error_code, std::size_t)>(
2845       detail::initiate_async_read_until_expr_v2<AsyncReadStream>(s),
2846       handler, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers), expr);
2847 }
2848
2849 #endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
2850
2851 namespace detail
2852 {
2853   template <typename AsyncReadStream, typename DynamicBuffer_v2,
2854       typename MatchCondition, typename ReadHandler>
2855   class read_until_match_op_v2
2856   {
2857   public:
2858     template <typename BufferSequence>
2859     read_until_match_op_v2(AsyncReadStream& stream,
2860         BOOST_ASIO_MOVE_ARG(BufferSequence) buffers,
2861         MatchCondition match_condition, ReadHandler& handler)
2862       : stream_(stream),
2863         buffers_(BOOST_ASIO_MOVE_CAST(BufferSequence)(buffers)),
2864         match_condition_(match_condition),
2865         start_(0),
2866         search_position_(0),
2867         bytes_to_read_(0),
2868         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
2869     {
2870     }
2871
2872 #if defined(BOOST_ASIO_HAS_MOVE)
2873     read_until_match_op_v2(const read_until_match_op_v2& other)
2874       : stream_(other.stream_),
2875         buffers_(other.buffers_),
2876         match_condition_(other.match_condition_),
2877         start_(other.start_),
2878         search_position_(other.search_position_),
2879         bytes_to_read_(other.bytes_to_read_),
2880         handler_(other.handler_)
2881     {
2882     }
2883
2884     read_until_match_op_v2(read_until_match_op_v2&& other)
2885       : stream_(other.stream_),
2886         buffers_(BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(other.buffers_)),
2887         match_condition_(other.match_condition_),
2888         start_(other.start_),
2889         search_position_(other.search_position_),
2890         bytes_to_read_(other.bytes_to_read_),
2891         handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
2892     {
2893     }
2894 #endif // defined(BOOST_ASIO_HAS_MOVE)
2895
2896     void operator()(const boost::system::error_code& ec,
2897         std::size_t bytes_transferred, int start = 0)
2898     {
2899       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
2900       std::size_t pos;
2901       switch (start_ = start)
2902       {
2903       case 1:
2904         for (;;)
2905         {
2906           {
2907             // Determine the range of the data to be searched.
2908             typedef typename DynamicBuffer_v2::const_buffers_type
2909               buffers_type;
2910             typedef buffers_iterator<buffers_type> iterator;
2911             buffers_type data_buffers =
2912               const_cast<const DynamicBuffer_v2&>(buffers_).data(
2913                   0, buffers_.size());
2914             iterator begin = iterator::begin(data_buffers);
2915             iterator start_pos = begin + search_position_;
2916             iterator end = iterator::end(data_buffers);
2917
2918             // Look for a match.
2919             std::pair<iterator, bool> result = match_condition_(start_pos, end);
2920             if (result.second)
2921             {
2922               // Full match. We're done.
2923               search_position_ = result.first - begin;
2924               bytes_to_read_ = 0;
2925             }
2926
2927             // No match yet. Check if buffer is full.
2928             else if (buffers_.size() == buffers_.max_size())
2929             {
2930               search_position_ = not_found;
2931               bytes_to_read_ = 0;
2932             }
2933
2934             // Need to read some more data.
2935             else
2936             {
2937               if (result.first != end)
2938               {
2939                 // Partial match. Next search needs to start from beginning of
2940                 // match.
2941                 search_position_ = result.first - begin;
2942               }
2943               else
2944               {
2945                 // Next search can start with the new data.
2946                 search_position_ = end - begin;
2947               }
2948
2949               bytes_to_read_ = std::min<std::size_t>(
2950                     std::max<std::size_t>(512,
2951                       buffers_.capacity() - buffers_.size()),
2952                     std::min<std::size_t>(65536,
2953                       buffers_.max_size() - buffers_.size()));
2954             }
2955           }
2956
2957           // Check if we're done.
2958           if (!start && bytes_to_read_ == 0)
2959             break;
2960
2961           // Start a new asynchronous read op_v2eration to obtain more data.
2962           pos = buffers_.size();
2963           buffers_.grow(bytes_to_read_);
2964           stream_.async_read_some(buffers_.data(pos, bytes_to_read_),
2965               BOOST_ASIO_MOVE_CAST(read_until_match_op_v2)(*this));
2966           return; default:
2967           buffers_.shrink(bytes_to_read_ - bytes_transferred);
2968           if (ec || bytes_transferred == 0)
2969             break;
2970         }
2971
2972         const boost::system::error_code result_ec =
2973           (search_position_ == not_found)
2974           ? error::not_found : ec;
2975
2976         const std::size_t result_n =
2977           (ec || search_position_ == not_found)
2978           ? 0 : search_position_;
2979
2980         handler_(result_ec, result_n);
2981       }
2982     }
2983
2984   //private:
2985     AsyncReadStream& stream_;
2986     DynamicBuffer_v2 buffers_;
2987     MatchCondition match_condition_;
2988     int start_;
2989     std::size_t search_position_;
2990     std::size_t bytes_to_read_;
2991     ReadHandler handler_;
2992   };
2993
2994   template <typename AsyncReadStream, typename DynamicBuffer_v2,
2995       typename MatchCondition, typename ReadHandler>
2996   inline void* asio_handler_allocate(std::size_t size,
2997       read_until_match_op_v2<AsyncReadStream, DynamicBuffer_v2,
2998         MatchCondition, ReadHandler>* this_handler)
2999   {
3000     return boost_asio_handler_alloc_helpers::allocate(
3001         size, this_handler->handler_);
3002   }
3003
3004   template <typename AsyncReadStream, typename DynamicBuffer_v2,
3005       typename MatchCondition, typename ReadHandler>
3006   inline void asio_handler_deallocate(void* pointer, std::size_t size,
3007       read_until_match_op_v2<AsyncReadStream, DynamicBuffer_v2,
3008         MatchCondition, ReadHandler>* this_handler)
3009   {
3010     boost_asio_handler_alloc_helpers::deallocate(
3011         pointer, size, this_handler->handler_);
3012   }
3013
3014   template <typename AsyncReadStream, typename DynamicBuffer_v2,
3015       typename MatchCondition, typename ReadHandler>
3016   inline bool asio_handler_is_continuation(
3017       read_until_match_op_v2<AsyncReadStream, DynamicBuffer_v2,
3018         MatchCondition, ReadHandler>* this_handler)
3019   {
3020     return this_handler->start_ == 0 ? true
3021       : boost_asio_handler_cont_helpers::is_continuation(
3022           this_handler->handler_);
3023   }
3024
3025   template <typename Function, typename AsyncReadStream,
3026       typename DynamicBuffer_v2, typename MatchCondition,
3027       typename ReadHandler>
3028   inline void asio_handler_invoke(Function& function,
3029       read_until_match_op_v2<AsyncReadStream, DynamicBuffer_v2,
3030         MatchCondition, ReadHandler>* this_handler)
3031   {
3032     boost_asio_handler_invoke_helpers::invoke(
3033         function, this_handler->handler_);
3034   }
3035
3036   template <typename Function, typename AsyncReadStream,
3037       typename DynamicBuffer_v2, typename MatchCondition,
3038       typename ReadHandler>
3039   inline void asio_handler_invoke(const Function& function,
3040       read_until_match_op_v2<AsyncReadStream, DynamicBuffer_v2,
3041       MatchCondition, ReadHandler>* this_handler)
3042   {
3043     boost_asio_handler_invoke_helpers::invoke(
3044         function, this_handler->handler_);
3045   }
3046
3047   template <typename AsyncReadStream>
3048   class initiate_async_read_until_match_v2
3049   {
3050   public:
3051     typedef typename AsyncReadStream::executor_type executor_type;
3052
3053     explicit initiate_async_read_until_match_v2(AsyncReadStream& stream)
3054       : stream_(stream)
3055     {
3056     }
3057
3058     executor_type get_executor() const BOOST_ASIO_NOEXCEPT
3059     {
3060       return stream_.get_executor();
3061     }
3062
3063     template <typename ReadHandler,
3064         typename DynamicBuffer_v2, typename MatchCondition>
3065     void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
3066         BOOST_ASIO_MOVE_ARG(DynamicBuffer_v2) buffers,
3067         MatchCondition match_condition) const
3068     {
3069       // If you get an error on the following line it means that your handler
3070       // does not meet the documented type requirements for a ReadHandler.
3071       BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
3072
3073       non_const_lvalue<ReadHandler> handler2(handler);
3074       read_until_match_op_v2<AsyncReadStream,
3075         typename decay<DynamicBuffer_v2>::type,
3076           MatchCondition, typename decay<ReadHandler>::type>(
3077             stream_, BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers),
3078             match_condition, handler2.value)(boost::system::error_code(), 0, 1);
3079     }
3080
3081   private:
3082     AsyncReadStream& stream_;
3083   };
3084 } // namespace detail
3085
3086 #if !defined(GENERATING_DOCUMENTATION)
3087
3088 template <typename AsyncReadStream, typename DynamicBuffer_v2,
3089     typename MatchCondition, typename ReadHandler, typename Allocator>
3090 struct associated_allocator<
3091     detail::read_until_match_op_v2<AsyncReadStream,
3092       DynamicBuffer_v2, MatchCondition, ReadHandler>,
3093     Allocator>
3094 {
3095   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
3096
3097   static type get(
3098       const detail::read_until_match_op_v2<AsyncReadStream,
3099         DynamicBuffer_v2, MatchCondition, ReadHandler>& h,
3100       const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
3101   {
3102     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
3103   }
3104 };
3105
3106 template <typename AsyncReadStream, typename DynamicBuffer_v2,
3107     typename MatchCondition, typename ReadHandler, typename Executor>
3108 struct associated_executor<
3109     detail::read_until_match_op_v2<AsyncReadStream,
3110       DynamicBuffer_v2, MatchCondition, ReadHandler>,
3111     Executor>
3112 {
3113   typedef typename associated_executor<ReadHandler, Executor>::type type;
3114
3115   static type get(
3116       const detail::read_until_match_op_v2<AsyncReadStream,
3117         DynamicBuffer_v2, MatchCondition, ReadHandler>& h,
3118       const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
3119   {
3120     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
3121   }
3122 };
3123
3124 #endif // !defined(GENERATING_DOCUMENTATION)
3125
3126 template <typename AsyncReadStream,
3127     typename DynamicBuffer_v2, typename MatchCondition,
3128     BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
3129       std::size_t)) ReadHandler>
3130 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadHandler,
3131     void (boost::system::error_code, std::size_t))
3132 async_read_until(AsyncReadStream& s, DynamicBuffer_v2 buffers,
3133     MatchCondition match_condition, BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
3134     typename enable_if<
3135       is_match_condition<MatchCondition>::value
3136         && is_dynamic_buffer_v2<DynamicBuffer_v2>::value
3137     >::type*)
3138 {
3139   return async_initiate<ReadHandler,
3140     void (boost::system::error_code, std::size_t)>(
3141       detail::initiate_async_read_until_match_v2<AsyncReadStream>(s), handler,
3142       BOOST_ASIO_MOVE_CAST(DynamicBuffer_v2)(buffers), match_condition);
3143 }
3144
3145 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
3146
3147 } // namespace asio
3148 } // namespace boost
3149
3150 #include <boost/asio/detail/pop_options.hpp>
3151
3152 #endif // BOOST_ASIO_IMPL_READ_UNTIL_HPP