1 //
2 // impl/read_until.hpp
3 // ~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2018 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 ASIO_IMPL_READ_UNTIL_HPP
12 #define 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 "asio/associated_allocator.hpp"
23 #include "asio/associated_executor.hpp"
24 #include "asio/buffer.hpp"
25 #include "asio/buffers_iterator.hpp"
26 #include "asio/detail/bind_handler.hpp"
27 #include "asio/detail/handler_alloc_helpers.hpp"
28 #include "asio/detail/handler_cont_helpers.hpp"
29 #include "asio/detail/handler_invoke_helpers.hpp"
30 #include "asio/detail/handler_type_requirements.hpp"
31 #include "asio/detail/limits.hpp"
32 #include "asio/detail/throw_error.hpp"
33 
34 #include "asio/detail/push_options.hpp"
35 
36 namespace asio {
37 
38 template <typename SyncReadStream, typename DynamicBuffer>
read_until(SyncReadStream & s,ASIO_MOVE_ARG (DynamicBuffer)buffers,char delim)39 inline std::size_t read_until(SyncReadStream& s,
40     ASIO_MOVE_ARG(DynamicBuffer) buffers, char delim)
41 {
42   asio::error_code ec;
43   std::size_t bytes_transferred = read_until(s,
44       ASIO_MOVE_CAST(DynamicBuffer)(buffers), delim, ec);
45   asio::detail::throw_error(ec, "read_until");
46   return bytes_transferred;
47 }
48 
49 template <typename SyncReadStream, typename DynamicBuffer>
read_until(SyncReadStream & s,ASIO_MOVE_ARG (DynamicBuffer)buffers,char delim,asio::error_code & ec)50 std::size_t read_until(SyncReadStream& s,
51     ASIO_MOVE_ARG(DynamicBuffer) buffers,
52     char delim, asio::error_code& ec)
53 {
54   typename decay<DynamicBuffer>::type b(
55       ASIO_MOVE_CAST(DynamicBuffer)(buffers));
56 
57   std::size_t search_position = 0;
58   for (;;)
59   {
60     // Determine the range of the data to be searched.
61     typedef typename DynamicBuffer::const_buffers_type buffers_type;
62     typedef buffers_iterator<buffers_type> iterator;
63     buffers_type data_buffers = b.data();
64     iterator begin = iterator::begin(data_buffers);
65     iterator start_pos = begin + search_position;
66     iterator end = iterator::end(data_buffers);
67 
68     // Look for a match.
69     iterator iter = std::find(start_pos, end, delim);
70     if (iter != end)
71     {
72       // Found a match. We're done.
73       ec = asio::error_code();
74       return iter - begin + 1;
75     }
76     else
77     {
78       // No match. Next search can start with the new data.
79       search_position = end - begin;
80     }
81 
82     // Check if buffer is full.
83     if (b.size() == b.max_size())
84     {
85       ec = error::not_found;
86       return 0;
87     }
88 
89     // Need more data.
90     std::size_t bytes_to_read = std::min<std::size_t>(
91           std::max<std::size_t>(512, b.capacity() - b.size()),
92           std::min<std::size_t>(65536, b.max_size() - b.size()));
93     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
94     if (ec)
95       return 0;
96   }
97 }
98 
99 template <typename SyncReadStream, typename DynamicBuffer>
read_until(SyncReadStream & s,ASIO_MOVE_ARG (DynamicBuffer)buffers,ASIO_STRING_VIEW_PARAM delim)100 inline std::size_t read_until(SyncReadStream& s,
101     ASIO_MOVE_ARG(DynamicBuffer) buffers,
102     ASIO_STRING_VIEW_PARAM delim)
103 {
104   asio::error_code ec;
105   std::size_t bytes_transferred = read_until(s,
106       ASIO_MOVE_CAST(DynamicBuffer)(buffers), delim, ec);
107   asio::detail::throw_error(ec, "read_until");
108   return bytes_transferred;
109 }
110 
111 namespace detail
112 {
113   // Algorithm that finds a subsequence of equal values in a sequence. Returns
114   // (iterator,true) if a full match was found, in which case the iterator
115   // points to the beginning of the match. Returns (iterator,false) if a
116   // partial match was found at the end of the first sequence, in which case
117   // the iterator points to the beginning of the partial match. Returns
118   // (last1,false) if no full or partial match was found.
119   template <typename Iterator1, typename Iterator2>
partial_search(Iterator1 first1,Iterator1 last1,Iterator2 first2,Iterator2 last2)120   std::pair<Iterator1, bool> partial_search(
121       Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
122   {
123     for (Iterator1 iter1 = first1; iter1 != last1; ++iter1)
124     {
125       Iterator1 test_iter1 = iter1;
126       Iterator2 test_iter2 = first2;
127       for (;; ++test_iter1, ++test_iter2)
128       {
129         if (test_iter2 == last2)
130           return std::make_pair(iter1, true);
131         if (test_iter1 == last1)
132         {
133           if (test_iter2 != first2)
134             return std::make_pair(iter1, false);
135           else
136             break;
137         }
138         if (*test_iter1 != *test_iter2)
139           break;
140       }
141     }
142     return std::make_pair(last1, false);
143   }
144 } // namespace detail
145 
146 template <typename SyncReadStream, typename DynamicBuffer>
read_until(SyncReadStream & s,ASIO_MOVE_ARG (DynamicBuffer)buffers,ASIO_STRING_VIEW_PARAM delim,asio::error_code & ec)147 std::size_t read_until(SyncReadStream& s,
148     ASIO_MOVE_ARG(DynamicBuffer) buffers,
149     ASIO_STRING_VIEW_PARAM delim, asio::error_code& ec)
150 {
151   typename decay<DynamicBuffer>::type b(
152       ASIO_MOVE_CAST(DynamicBuffer)(buffers));
153 
154   std::size_t search_position = 0;
155   for (;;)
156   {
157     // Determine the range of the data to be searched.
158     typedef typename DynamicBuffer::const_buffers_type buffers_type;
159     typedef buffers_iterator<buffers_type> iterator;
160     buffers_type data_buffers = b.data();
161     iterator begin = iterator::begin(data_buffers);
162     iterator start_pos = begin + search_position;
163     iterator end = iterator::end(data_buffers);
164 
165     // Look for a match.
166     std::pair<iterator, bool> result = detail::partial_search(
167         start_pos, end, delim.begin(), delim.end());
168     if (result.first != end)
169     {
170       if (result.second)
171       {
172         // Full match. We're done.
173         ec = asio::error_code();
174         return result.first - begin + delim.length();
175       }
176       else
177       {
178         // Partial match. Next search needs to start from beginning of match.
179         search_position = result.first - begin;
180       }
181     }
182     else
183     {
184       // No match. Next search can start with the new data.
185       search_position = end - begin;
186     }
187 
188     // Check if buffer is full.
189     if (b.size() == b.max_size())
190     {
191       ec = error::not_found;
192       return 0;
193     }
194 
195     // Need more data.
196     std::size_t bytes_to_read = std::min<std::size_t>(
197           std::max<std::size_t>(512, b.capacity() - b.size()),
198           std::min<std::size_t>(65536, b.max_size() - b.size()));
199     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
200     if (ec)
201       return 0;
202   }
203 }
204 
205 #if !defined(ASIO_NO_EXTENSIONS)
206 #if defined(ASIO_HAS_BOOST_REGEX)
207 
208 template <typename SyncReadStream, typename DynamicBuffer>
read_until(SyncReadStream & s,ASIO_MOVE_ARG (DynamicBuffer)buffers,const boost::regex & expr)209 inline std::size_t read_until(SyncReadStream& s,
210     ASIO_MOVE_ARG(DynamicBuffer) buffers,
211     const boost::regex& expr)
212 {
213   asio::error_code ec;
214   std::size_t bytes_transferred = read_until(s,
215       ASIO_MOVE_CAST(DynamicBuffer)(buffers), expr, ec);
216   asio::detail::throw_error(ec, "read_until");
217   return bytes_transferred;
218 }
219 
220 template <typename SyncReadStream, typename DynamicBuffer>
read_until(SyncReadStream & s,ASIO_MOVE_ARG (DynamicBuffer)buffers,const boost::regex & expr,asio::error_code & ec)221 std::size_t read_until(SyncReadStream& s,
222     ASIO_MOVE_ARG(DynamicBuffer) buffers,
223     const boost::regex& expr, asio::error_code& ec)
224 {
225   typename decay<DynamicBuffer>::type b(
226       ASIO_MOVE_CAST(DynamicBuffer)(buffers));
227 
228   std::size_t search_position = 0;
229   for (;;)
230   {
231     // Determine the range of the data to be searched.
232     typedef typename DynamicBuffer::const_buffers_type buffers_type;
233     typedef buffers_iterator<buffers_type> iterator;
234     buffers_type data_buffers = b.data();
235     iterator begin = iterator::begin(data_buffers);
236     iterator start_pos = begin + search_position;
237     iterator end = iterator::end(data_buffers);
238 
239     // Look for a match.
240     boost::match_results<iterator,
241       typename std::vector<boost::sub_match<iterator> >::allocator_type>
242         match_results;
243     if (regex_search(start_pos, end, match_results, expr,
244           boost::match_default | boost::match_partial))
245     {
246       if (match_results[0].matched)
247       {
248         // Full match. We're done.
249         ec = asio::error_code();
250         return match_results[0].second - begin;
251       }
252       else
253       {
254         // Partial match. Next search needs to start from beginning of match.
255         search_position = match_results[0].first - begin;
256       }
257     }
258     else
259     {
260       // No match. Next search can start with the new data.
261       search_position = end - begin;
262     }
263 
264     // Check if buffer is full.
265     if (b.size() == b.max_size())
266     {
267       ec = error::not_found;
268       return 0;
269     }
270 
271     // Need more data.
272     std::size_t bytes_to_read = std::min<std::size_t>(
273           std::max<std::size_t>(512, b.capacity() - b.size()),
274           std::min<std::size_t>(65536, b.max_size() - b.size()));
275     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
276     if (ec)
277       return 0;
278   }
279 }
280 
281 #endif // defined(ASIO_HAS_BOOST_REGEX)
282 
283 template <typename SyncReadStream,
284     typename DynamicBuffer, typename MatchCondition>
read_until(SyncReadStream & s,ASIO_MOVE_ARG (DynamicBuffer)buffers,MatchCondition match_condition,typename enable_if<is_match_condition<MatchCondition>::value>::type *)285 inline std::size_t read_until(SyncReadStream& s,
286     ASIO_MOVE_ARG(DynamicBuffer) buffers,
287     MatchCondition match_condition,
288     typename enable_if<is_match_condition<MatchCondition>::value>::type*)
289 {
290   asio::error_code ec;
291   std::size_t bytes_transferred = read_until(s,
292       ASIO_MOVE_CAST(DynamicBuffer)(buffers),
293       match_condition, ec);
294   asio::detail::throw_error(ec, "read_until");
295   return bytes_transferred;
296 }
297 
298 template <typename SyncReadStream,
299     typename DynamicBuffer, typename MatchCondition>
read_until(SyncReadStream & s,ASIO_MOVE_ARG (DynamicBuffer)buffers,MatchCondition match_condition,asio::error_code & ec,typename enable_if<is_match_condition<MatchCondition>::value>::type *)300 std::size_t read_until(SyncReadStream& s,
301     ASIO_MOVE_ARG(DynamicBuffer) buffers,
302     MatchCondition match_condition, asio::error_code& ec,
303     typename enable_if<is_match_condition<MatchCondition>::value>::type*)
304 {
305   typename decay<DynamicBuffer>::type b(
306       ASIO_MOVE_CAST(DynamicBuffer)(buffers));
307 
308   std::size_t search_position = 0;
309   for (;;)
310   {
311     // Determine the range of the data to be searched.
312     typedef typename DynamicBuffer::const_buffers_type buffers_type;
313     typedef buffers_iterator<buffers_type> iterator;
314     buffers_type data_buffers = b.data();
315     iterator begin = iterator::begin(data_buffers);
316     iterator start_pos = begin + search_position;
317     iterator end = iterator::end(data_buffers);
318 
319     // Look for a match.
320     std::pair<iterator, bool> result = match_condition(start_pos, end);
321     if (result.second)
322     {
323       // Full match. We're done.
324       ec = asio::error_code();
325       return result.first - begin;
326     }
327     else if (result.first != end)
328     {
329       // Partial match. Next search needs to start from beginning of match.
330       search_position = result.first - begin;
331     }
332     else
333     {
334       // No match. Next search can start with the new data.
335       search_position = end - begin;
336     }
337 
338     // Check if buffer is full.
339     if (b.size() == b.max_size())
340     {
341       ec = error::not_found;
342       return 0;
343     }
344 
345     // Need more data.
346     std::size_t bytes_to_read = std::min<std::size_t>(
347           std::max<std::size_t>(512, b.capacity() - b.size()),
348           std::min<std::size_t>(65536, b.max_size() - b.size()));
349     b.commit(s.read_some(b.prepare(bytes_to_read), ec));
350     if (ec)
351       return 0;
352   }
353 }
354 
355 #if !defined(ASIO_NO_IOSTREAM)
356 
357 template <typename SyncReadStream, typename Allocator>
read_until(SyncReadStream & s,asio::basic_streambuf<Allocator> & b,char delim)358 inline std::size_t read_until(SyncReadStream& s,
359     asio::basic_streambuf<Allocator>& b, char delim)
360 {
361   return read_until(s, basic_streambuf_ref<Allocator>(b), delim);
362 }
363 
364 template <typename SyncReadStream, typename Allocator>
read_until(SyncReadStream & s,asio::basic_streambuf<Allocator> & b,char delim,asio::error_code & ec)365 inline std::size_t read_until(SyncReadStream& s,
366     asio::basic_streambuf<Allocator>& b, char delim,
367     asio::error_code& ec)
368 {
369   return read_until(s, basic_streambuf_ref<Allocator>(b), delim, ec);
370 }
371 
372 template <typename SyncReadStream, typename Allocator>
read_until(SyncReadStream & s,asio::basic_streambuf<Allocator> & b,ASIO_STRING_VIEW_PARAM delim)373 inline std::size_t read_until(SyncReadStream& s,
374     asio::basic_streambuf<Allocator>& b,
375     ASIO_STRING_VIEW_PARAM delim)
376 {
377   return read_until(s, basic_streambuf_ref<Allocator>(b), delim);
378 }
379 
380 template <typename SyncReadStream, typename Allocator>
read_until(SyncReadStream & s,asio::basic_streambuf<Allocator> & b,ASIO_STRING_VIEW_PARAM delim,asio::error_code & ec)381 inline std::size_t read_until(SyncReadStream& s,
382     asio::basic_streambuf<Allocator>& b,
383     ASIO_STRING_VIEW_PARAM delim, asio::error_code& ec)
384 {
385   return read_until(s, basic_streambuf_ref<Allocator>(b), delim, ec);
386 }
387 
388 #if defined(ASIO_HAS_BOOST_REGEX)
389 
390 template <typename SyncReadStream, typename Allocator>
read_until(SyncReadStream & s,asio::basic_streambuf<Allocator> & b,const boost::regex & expr)391 inline std::size_t read_until(SyncReadStream& s,
392     asio::basic_streambuf<Allocator>& b, const boost::regex& expr)
393 {
394   return read_until(s, basic_streambuf_ref<Allocator>(b), expr);
395 }
396 
397 template <typename SyncReadStream, typename Allocator>
read_until(SyncReadStream & s,asio::basic_streambuf<Allocator> & b,const boost::regex & expr,asio::error_code & ec)398 inline std::size_t read_until(SyncReadStream& s,
399     asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
400     asio::error_code& ec)
401 {
402   return read_until(s, basic_streambuf_ref<Allocator>(b), expr, ec);
403 }
404 
405 #endif // defined(ASIO_HAS_BOOST_REGEX)
406 
407 template <typename SyncReadStream, typename Allocator, typename MatchCondition>
read_until(SyncReadStream & s,asio::basic_streambuf<Allocator> & b,MatchCondition match_condition,typename enable_if<is_match_condition<MatchCondition>::value>::type *)408 inline std::size_t read_until(SyncReadStream& s,
409     asio::basic_streambuf<Allocator>& b, MatchCondition match_condition,
410     typename enable_if<is_match_condition<MatchCondition>::value>::type*)
411 {
412   return read_until(s, basic_streambuf_ref<Allocator>(b), match_condition);
413 }
414 
415 template <typename SyncReadStream, typename Allocator, typename MatchCondition>
read_until(SyncReadStream & s,asio::basic_streambuf<Allocator> & b,MatchCondition match_condition,asio::error_code & ec,typename enable_if<is_match_condition<MatchCondition>::value>::type *)416 inline std::size_t read_until(SyncReadStream& s,
417     asio::basic_streambuf<Allocator>& b,
418     MatchCondition match_condition, asio::error_code& ec,
419     typename enable_if<is_match_condition<MatchCondition>::value>::type*)
420 {
421   return read_until(s, basic_streambuf_ref<Allocator>(b), match_condition, ec);
422 }
423 
424 #endif // !defined(ASIO_NO_IOSTREAM)
425 #endif // !defined(ASIO_NO_EXTENSIONS)
426 
427 namespace detail
428 {
429   template <typename AsyncReadStream,
430       typename DynamicBuffer, typename ReadHandler>
431   class read_until_delim_op
432   {
433   public:
434     template <typename BufferSequence>
read_until_delim_op(AsyncReadStream & stream,ASIO_MOVE_ARG (BufferSequence)buffers,char delim,ReadHandler & handler)435     read_until_delim_op(AsyncReadStream& stream,
436         ASIO_MOVE_ARG(BufferSequence) buffers,
437         char delim, ReadHandler& handler)
438       : stream_(stream),
439         buffers_(ASIO_MOVE_CAST(BufferSequence)(buffers)),
440         delim_(delim),
441         start_(0),
442         search_position_(0),
443         handler_(ASIO_MOVE_CAST(ReadHandler)(handler))
444     {
445     }
446 
447 #if defined(ASIO_HAS_MOVE)
read_until_delim_op(const read_until_delim_op & other)448     read_until_delim_op(const read_until_delim_op& other)
449       : stream_(other.stream_),
450         buffers_(other.buffers_),
451         delim_(other.delim_),
452         start_(other.start_),
453         search_position_(other.search_position_),
454         handler_(other.handler_)
455     {
456     }
457 
read_until_delim_op(read_until_delim_op && other)458     read_until_delim_op(read_until_delim_op&& other)
459       : stream_(other.stream_),
460         buffers_(ASIO_MOVE_CAST(DynamicBuffer)(other.buffers_)),
461         delim_(other.delim_),
462         start_(other.start_),
463         search_position_(other.search_position_),
464         handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_))
465     {
466     }
467 #endif // defined(ASIO_HAS_MOVE)
468 
operator ()(const asio::error_code & ec,std::size_t bytes_transferred,int start=0)469     void operator()(const asio::error_code& ec,
470         std::size_t bytes_transferred, int start = 0)
471     {
472       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
473       std::size_t bytes_to_read;
474       switch (start_ = start)
475       {
476       case 1:
477         for (;;)
478         {
479           {
480             // Determine the range of the data to be searched.
481             typedef typename DynamicBuffer::const_buffers_type
482               buffers_type;
483             typedef buffers_iterator<buffers_type> iterator;
484             buffers_type data_buffers = buffers_.data();
485             iterator begin = iterator::begin(data_buffers);
486             iterator start_pos = begin + search_position_;
487             iterator end = iterator::end(data_buffers);
488 
489             // Look for a match.
490             iterator iter = std::find(start_pos, end, delim_);
491             if (iter != end)
492             {
493               // Found a match. We're done.
494               search_position_ = iter - begin + 1;
495               bytes_to_read = 0;
496             }
497 
498             // No match yet. Check if buffer is full.
499             else if (buffers_.size() == buffers_.max_size())
500             {
501               search_position_ = not_found;
502               bytes_to_read = 0;
503             }
504 
505             // Need to read some more data.
506             else
507             {
508               // Next search can start with the new data.
509               search_position_ = end - begin;
510               bytes_to_read = std::min<std::size_t>(
511                     std::max<std::size_t>(512,
512                       buffers_.capacity() - buffers_.size()),
513                     std::min<std::size_t>(65536,
514                       buffers_.max_size() - buffers_.size()));
515             }
516           }
517 
518           // Check if we're done.
519           if (!start && bytes_to_read == 0)
520             break;
521 
522           // Start a new asynchronous read operation to obtain more data.
523           stream_.async_read_some(buffers_.prepare(bytes_to_read),
524               ASIO_MOVE_CAST(read_until_delim_op)(*this));
525           return; default:
526           buffers_.commit(bytes_transferred);
527           if (ec || bytes_transferred == 0)
528             break;
529         }
530 
531         const asio::error_code result_ec =
532           (search_position_ == not_found)
533           ? error::not_found : ec;
534 
535         const std::size_t result_n =
536           (ec || search_position_ == not_found)
537           ? 0 : search_position_;
538 
539         handler_(result_ec, result_n);
540       }
541     }
542 
543   //private:
544     AsyncReadStream& stream_;
545     DynamicBuffer buffers_;
546     char delim_;
547     int start_;
548     std::size_t search_position_;
549     ReadHandler handler_;
550   };
551 
552   template <typename AsyncReadStream,
553       typename DynamicBuffer, typename ReadHandler>
asio_handler_allocate(std::size_t size,read_until_delim_op<AsyncReadStream,DynamicBuffer,ReadHandler> * this_handler)554   inline void* asio_handler_allocate(std::size_t size,
555       read_until_delim_op<AsyncReadStream,
556         DynamicBuffer, ReadHandler>* this_handler)
557   {
558     return asio_handler_alloc_helpers::allocate(
559         size, this_handler->handler_);
560   }
561 
562   template <typename AsyncReadStream,
563       typename DynamicBuffer, typename ReadHandler>
asio_handler_deallocate(void * pointer,std::size_t size,read_until_delim_op<AsyncReadStream,DynamicBuffer,ReadHandler> * this_handler)564   inline void asio_handler_deallocate(void* pointer, std::size_t size,
565       read_until_delim_op<AsyncReadStream,
566         DynamicBuffer, ReadHandler>* this_handler)
567   {
568     asio_handler_alloc_helpers::deallocate(
569         pointer, size, this_handler->handler_);
570   }
571 
572   template <typename AsyncReadStream,
573       typename DynamicBuffer, typename ReadHandler>
asio_handler_is_continuation(read_until_delim_op<AsyncReadStream,DynamicBuffer,ReadHandler> * this_handler)574   inline bool asio_handler_is_continuation(
575       read_until_delim_op<AsyncReadStream,
576         DynamicBuffer, ReadHandler>* this_handler)
577   {
578     return this_handler->start_ == 0 ? true
579       : asio_handler_cont_helpers::is_continuation(
580           this_handler->handler_);
581   }
582 
583   template <typename Function, typename AsyncReadStream,
584       typename DynamicBuffer, typename ReadHandler>
asio_handler_invoke(Function & function,read_until_delim_op<AsyncReadStream,DynamicBuffer,ReadHandler> * this_handler)585   inline void asio_handler_invoke(Function& function,
586       read_until_delim_op<AsyncReadStream,
587         DynamicBuffer, ReadHandler>* this_handler)
588   {
589     asio_handler_invoke_helpers::invoke(
590         function, this_handler->handler_);
591   }
592 
593   template <typename Function, typename AsyncReadStream,
594       typename DynamicBuffer, typename ReadHandler>
asio_handler_invoke(const Function & function,read_until_delim_op<AsyncReadStream,DynamicBuffer,ReadHandler> * this_handler)595   inline void asio_handler_invoke(const Function& function,
596       read_until_delim_op<AsyncReadStream,
597         DynamicBuffer, ReadHandler>* this_handler)
598   {
599     asio_handler_invoke_helpers::invoke(
600         function, this_handler->handler_);
601   }
602 } // namespace detail
603 
604 #if !defined(GENERATING_DOCUMENTATION)
605 
606 template <typename AsyncReadStream, typename DynamicBuffer,
607     typename ReadHandler, typename Allocator>
608 struct associated_allocator<
609     detail::read_until_delim_op<AsyncReadStream,
610       DynamicBuffer, ReadHandler>,
611     Allocator>
612 {
613   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
614 
getasio::associated_allocator615   static type get(
616       const detail::read_until_delim_op<AsyncReadStream,
617         DynamicBuffer, ReadHandler>& h,
618       const Allocator& a = Allocator()) ASIO_NOEXCEPT
619   {
620     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
621   }
622 };
623 
624 template <typename AsyncReadStream, typename DynamicBuffer,
625     typename ReadHandler, typename Executor>
626 struct associated_executor<
627     detail::read_until_delim_op<AsyncReadStream,
628       DynamicBuffer, ReadHandler>,
629     Executor>
630 {
631   typedef typename associated_executor<ReadHandler, Executor>::type type;
632 
getasio::associated_executor633   static type get(
634       const detail::read_until_delim_op<AsyncReadStream,
635         DynamicBuffer, ReadHandler>& h,
636       const Executor& ex = Executor()) ASIO_NOEXCEPT
637   {
638     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
639   }
640 };
641 
642 #endif // !defined(GENERATING_DOCUMENTATION)
643 
644 template <typename AsyncReadStream,
645     typename DynamicBuffer, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,void (asio::error_code,std::size_t))646 ASIO_INITFN_RESULT_TYPE(ReadHandler,
647     void (asio::error_code, std::size_t))
648 async_read_until(AsyncReadStream& s,
649     ASIO_MOVE_ARG(DynamicBuffer) buffers,
650     char delim, ASIO_MOVE_ARG(ReadHandler) handler)
651 {
652   // If you get an error on the following line it means that your handler does
653   // not meet the documented type requirements for a ReadHandler.
654   ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
655 
656   async_completion<ReadHandler,
657     void (asio::error_code, std::size_t)> init(handler);
658 
659   detail::read_until_delim_op<AsyncReadStream,
660     typename decay<DynamicBuffer>::type,
661       ASIO_HANDLER_TYPE(ReadHandler,
662         void (asio::error_code, std::size_t))>(
663           s, ASIO_MOVE_CAST(DynamicBuffer)(buffers),
664             delim, init.completion_handler)(asio::error_code(), 0, 1);
665 
666   return init.result.get();
667 }
668 
669 namespace detail
670 {
671   template <typename AsyncReadStream,
672       typename DynamicBuffer, typename ReadHandler>
673   class read_until_delim_string_op
674   {
675   public:
676     template <typename BufferSequence>
read_until_delim_string_op(AsyncReadStream & stream,ASIO_MOVE_ARG (BufferSequence)buffers,const std::string & delim,ReadHandler & handler)677     read_until_delim_string_op(AsyncReadStream& stream,
678         ASIO_MOVE_ARG(BufferSequence) buffers,
679         const std::string& delim, ReadHandler& handler)
680       : stream_(stream),
681         buffers_(ASIO_MOVE_CAST(BufferSequence)(buffers)),
682         delim_(delim),
683         start_(0),
684         search_position_(0),
685         handler_(ASIO_MOVE_CAST(ReadHandler)(handler))
686     {
687     }
688 
689 #if defined(ASIO_HAS_MOVE)
read_until_delim_string_op(const read_until_delim_string_op & other)690     read_until_delim_string_op(const read_until_delim_string_op& other)
691       : stream_(other.stream_),
692         buffers_(other.buffers_),
693         delim_(other.delim_),
694         start_(other.start_),
695         search_position_(other.search_position_),
696         handler_(other.handler_)
697     {
698     }
699 
read_until_delim_string_op(read_until_delim_string_op && other)700     read_until_delim_string_op(read_until_delim_string_op&& other)
701       : stream_(other.stream_),
702         buffers_(ASIO_MOVE_CAST(DynamicBuffer)(other.buffers_)),
703         delim_(ASIO_MOVE_CAST(std::string)(other.delim_)),
704         start_(other.start_),
705         search_position_(other.search_position_),
706         handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_))
707     {
708     }
709 #endif // defined(ASIO_HAS_MOVE)
710 
operator ()(const asio::error_code & ec,std::size_t bytes_transferred,int start=0)711     void operator()(const asio::error_code& ec,
712         std::size_t bytes_transferred, int start = 0)
713     {
714       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
715       std::size_t bytes_to_read;
716       switch (start_ = start)
717       {
718       case 1:
719         for (;;)
720         {
721           {
722             // Determine the range of the data to be searched.
723             typedef typename DynamicBuffer::const_buffers_type
724               buffers_type;
725             typedef buffers_iterator<buffers_type> iterator;
726             buffers_type data_buffers = buffers_.data();
727             iterator begin = iterator::begin(data_buffers);
728             iterator start_pos = begin + search_position_;
729             iterator end = iterator::end(data_buffers);
730 
731             // Look for a match.
732             std::pair<iterator, bool> result = detail::partial_search(
733                 start_pos, end, delim_.begin(), delim_.end());
734             if (result.first != end && result.second)
735             {
736               // Full match. We're done.
737               search_position_ = result.first - begin + delim_.length();
738               bytes_to_read = 0;
739             }
740 
741             // No match yet. Check if buffer is full.
742             else if (buffers_.size() == buffers_.max_size())
743             {
744               search_position_ = not_found;
745               bytes_to_read = 0;
746             }
747 
748             // Need to read some more data.
749             else
750             {
751               if (result.first != end)
752               {
753                 // Partial match. Next search needs to start from beginning of
754                 // match.
755                 search_position_ = result.first - begin;
756               }
757               else
758               {
759                 // Next search can start with the new data.
760                 search_position_ = end - begin;
761               }
762 
763               bytes_to_read = std::min<std::size_t>(
764                     std::max<std::size_t>(512,
765                       buffers_.capacity() - buffers_.size()),
766                     std::min<std::size_t>(65536,
767                       buffers_.max_size() - buffers_.size()));
768             }
769           }
770 
771           // Check if we're done.
772           if (!start && bytes_to_read == 0)
773             break;
774 
775           // Start a new asynchronous read operation to obtain more data.
776           stream_.async_read_some(buffers_.prepare(bytes_to_read),
777               ASIO_MOVE_CAST(read_until_delim_string_op)(*this));
778           return; default:
779           buffers_.commit(bytes_transferred);
780           if (ec || bytes_transferred == 0)
781             break;
782         }
783 
784         const asio::error_code result_ec =
785           (search_position_ == not_found)
786           ? error::not_found : ec;
787 
788         const std::size_t result_n =
789           (ec || search_position_ == not_found)
790           ? 0 : search_position_;
791 
792         handler_(result_ec, result_n);
793       }
794     }
795 
796   //private:
797     AsyncReadStream& stream_;
798     DynamicBuffer buffers_;
799     std::string delim_;
800     int start_;
801     std::size_t search_position_;
802     ReadHandler handler_;
803   };
804 
805   template <typename AsyncReadStream,
806       typename DynamicBuffer, typename ReadHandler>
asio_handler_allocate(std::size_t size,read_until_delim_string_op<AsyncReadStream,DynamicBuffer,ReadHandler> * this_handler)807   inline void* asio_handler_allocate(std::size_t size,
808       read_until_delim_string_op<AsyncReadStream,
809         DynamicBuffer, ReadHandler>* this_handler)
810   {
811     return asio_handler_alloc_helpers::allocate(
812         size, this_handler->handler_);
813   }
814 
815   template <typename AsyncReadStream,
816       typename DynamicBuffer, typename ReadHandler>
asio_handler_deallocate(void * pointer,std::size_t size,read_until_delim_string_op<AsyncReadStream,DynamicBuffer,ReadHandler> * this_handler)817   inline void asio_handler_deallocate(void* pointer, std::size_t size,
818       read_until_delim_string_op<AsyncReadStream,
819         DynamicBuffer, ReadHandler>* this_handler)
820   {
821     asio_handler_alloc_helpers::deallocate(
822         pointer, size, this_handler->handler_);
823   }
824 
825   template <typename AsyncReadStream,
826       typename DynamicBuffer, typename ReadHandler>
asio_handler_is_continuation(read_until_delim_string_op<AsyncReadStream,DynamicBuffer,ReadHandler> * this_handler)827   inline bool asio_handler_is_continuation(
828       read_until_delim_string_op<AsyncReadStream,
829         DynamicBuffer, ReadHandler>* this_handler)
830   {
831     return this_handler->start_ == 0 ? true
832       : asio_handler_cont_helpers::is_continuation(
833           this_handler->handler_);
834   }
835 
836   template <typename Function, typename AsyncReadStream,
837       typename DynamicBuffer, typename ReadHandler>
asio_handler_invoke(Function & function,read_until_delim_string_op<AsyncReadStream,DynamicBuffer,ReadHandler> * this_handler)838   inline void asio_handler_invoke(Function& function,
839       read_until_delim_string_op<AsyncReadStream,
840         DynamicBuffer, ReadHandler>* this_handler)
841   {
842     asio_handler_invoke_helpers::invoke(
843         function, this_handler->handler_);
844   }
845 
846   template <typename Function, typename AsyncReadStream,
847       typename DynamicBuffer, typename ReadHandler>
asio_handler_invoke(const Function & function,read_until_delim_string_op<AsyncReadStream,DynamicBuffer,ReadHandler> * this_handler)848   inline void asio_handler_invoke(const Function& function,
849       read_until_delim_string_op<AsyncReadStream,
850         DynamicBuffer, ReadHandler>* this_handler)
851   {
852     asio_handler_invoke_helpers::invoke(
853         function, this_handler->handler_);
854   }
855 } // namespace detail
856 
857 #if !defined(GENERATING_DOCUMENTATION)
858 
859 template <typename AsyncReadStream, typename DynamicBuffer,
860     typename ReadHandler, typename Allocator>
861 struct associated_allocator<
862     detail::read_until_delim_string_op<AsyncReadStream,
863       DynamicBuffer, ReadHandler>,
864     Allocator>
865 {
866   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
867 
getasio::associated_allocator868   static type get(
869       const detail::read_until_delim_string_op<AsyncReadStream,
870         DynamicBuffer, ReadHandler>& h,
871       const Allocator& a = Allocator()) ASIO_NOEXCEPT
872   {
873     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
874   }
875 };
876 
877 template <typename AsyncReadStream, typename DynamicBuffer,
878     typename ReadHandler, typename Executor>
879 struct associated_executor<
880     detail::read_until_delim_string_op<AsyncReadStream,
881       DynamicBuffer, ReadHandler>,
882     Executor>
883 {
884   typedef typename associated_executor<ReadHandler, Executor>::type type;
885 
getasio::associated_executor886   static type get(
887       const detail::read_until_delim_string_op<AsyncReadStream,
888         DynamicBuffer, ReadHandler>& h,
889       const Executor& ex = Executor()) ASIO_NOEXCEPT
890   {
891     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
892   }
893 };
894 
895 #endif // !defined(GENERATING_DOCUMENTATION)
896 
897 template <typename AsyncReadStream,
898     typename DynamicBuffer, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,void (asio::error_code,std::size_t))899 ASIO_INITFN_RESULT_TYPE(ReadHandler,
900     void (asio::error_code, std::size_t))
901 async_read_until(AsyncReadStream& s,
902     ASIO_MOVE_ARG(DynamicBuffer) buffers,
903     ASIO_STRING_VIEW_PARAM delim,
904     ASIO_MOVE_ARG(ReadHandler) handler)
905 {
906   // If you get an error on the following line it means that your handler does
907   // not meet the documented type requirements for a ReadHandler.
908   ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
909 
910   async_completion<ReadHandler,
911     void (asio::error_code, std::size_t)> init(handler);
912 
913   detail::read_until_delim_string_op<AsyncReadStream,
914     typename decay<DynamicBuffer>::type,
915       ASIO_HANDLER_TYPE(ReadHandler,
916         void (asio::error_code, std::size_t))>(
917           s, ASIO_MOVE_CAST(DynamicBuffer)(buffers),
918             static_cast<std::string>(delim),
919               init.completion_handler)(asio::error_code(), 0, 1);
920 
921   return init.result.get();
922 }
923 
924 #if !defined(ASIO_NO_EXTENSIONS)
925 #if defined(ASIO_HAS_BOOST_REGEX)
926 
927 namespace detail
928 {
929   template <typename AsyncReadStream, typename DynamicBuffer,
930       typename RegEx, typename ReadHandler>
931   class read_until_expr_op
932   {
933   public:
934     template <typename BufferSequence>
read_until_expr_op(AsyncReadStream & stream,ASIO_MOVE_ARG (BufferSequence)buffers,const boost::regex & expr,ReadHandler & handler)935     read_until_expr_op(AsyncReadStream& stream,
936         ASIO_MOVE_ARG(BufferSequence) buffers,
937         const boost::regex& expr, ReadHandler& handler)
938       : stream_(stream),
939         buffers_(ASIO_MOVE_CAST(BufferSequence)(buffers)),
940         expr_(expr),
941         start_(0),
942         search_position_(0),
943         handler_(ASIO_MOVE_CAST(ReadHandler)(handler))
944     {
945     }
946 
947 #if defined(ASIO_HAS_MOVE)
read_until_expr_op(const read_until_expr_op & other)948     read_until_expr_op(const read_until_expr_op& other)
949       : stream_(other.stream_),
950         buffers_(other.buffers_),
951         expr_(other.expr_),
952         start_(other.start_),
953         search_position_(other.search_position_),
954         handler_(other.handler_)
955     {
956     }
957 
read_until_expr_op(read_until_expr_op && other)958     read_until_expr_op(read_until_expr_op&& other)
959       : stream_(other.stream_),
960         buffers_(ASIO_MOVE_CAST(DynamicBuffer)(other.buffers_)),
961         expr_(other.expr_),
962         start_(other.start_),
963         search_position_(other.search_position_),
964         handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_))
965     {
966     }
967 #endif // defined(ASIO_HAS_MOVE)
968 
operator ()(const asio::error_code & ec,std::size_t bytes_transferred,int start=0)969     void operator()(const asio::error_code& ec,
970         std::size_t bytes_transferred, int start = 0)
971     {
972       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
973       std::size_t bytes_to_read;
974       switch (start_ = start)
975       {
976       case 1:
977         for (;;)
978         {
979           {
980             // Determine the range of the data to be searched.
981             typedef typename DynamicBuffer::const_buffers_type
982               buffers_type;
983             typedef buffers_iterator<buffers_type> iterator;
984             buffers_type data_buffers = buffers_.data();
985             iterator begin = iterator::begin(data_buffers);
986             iterator start_pos = begin + search_position_;
987             iterator end = iterator::end(data_buffers);
988 
989             // Look for a match.
990             boost::match_results<iterator,
991               typename std::vector<boost::sub_match<iterator> >::allocator_type>
992                 match_results;
993             bool match = regex_search(start_pos, end, match_results, expr_,
994                 boost::match_default | boost::match_partial);
995             if (match && match_results[0].matched)
996             {
997               // Full match. We're done.
998               search_position_ = match_results[0].second - begin;
999               bytes_to_read = 0;
1000             }
1001 
1002             // No match yet. Check if buffer is full.
1003             else if (buffers_.size() == buffers_.max_size())
1004             {
1005               search_position_ = not_found;
1006               bytes_to_read = 0;
1007             }
1008 
1009             // Need to read some more data.
1010             else
1011             {
1012               if (match)
1013               {
1014                 // Partial match. Next search needs to start from beginning of
1015                 // match.
1016                 search_position_ = match_results[0].first - begin;
1017               }
1018               else
1019               {
1020                 // Next search can start with the new data.
1021                 search_position_ = end - begin;
1022               }
1023 
1024               bytes_to_read = std::min<std::size_t>(
1025                     std::max<std::size_t>(512,
1026                       buffers_.capacity() - buffers_.size()),
1027                     std::min<std::size_t>(65536,
1028                       buffers_.max_size() - buffers_.size()));
1029             }
1030           }
1031 
1032           // Check if we're done.
1033           if (!start && bytes_to_read == 0)
1034             break;
1035 
1036           // Start a new asynchronous read operation to obtain more data.
1037           stream_.async_read_some(buffers_.prepare(bytes_to_read),
1038               ASIO_MOVE_CAST(read_until_expr_op)(*this));
1039           return; default:
1040           buffers_.commit(bytes_transferred);
1041           if (ec || bytes_transferred == 0)
1042             break;
1043         }
1044 
1045         const asio::error_code result_ec =
1046           (search_position_ == not_found)
1047           ? error::not_found : ec;
1048 
1049         const std::size_t result_n =
1050           (ec || search_position_ == not_found)
1051           ? 0 : search_position_;
1052 
1053         handler_(result_ec, result_n);
1054       }
1055     }
1056 
1057   //private:
1058     AsyncReadStream& stream_;
1059     DynamicBuffer buffers_;
1060     RegEx expr_;
1061     int start_;
1062     std::size_t search_position_;
1063     ReadHandler handler_;
1064   };
1065 
1066   template <typename AsyncReadStream, typename DynamicBuffer,
1067       typename RegEx, typename ReadHandler>
asio_handler_allocate(std::size_t size,read_until_expr_op<AsyncReadStream,DynamicBuffer,RegEx,ReadHandler> * this_handler)1068   inline void* asio_handler_allocate(std::size_t size,
1069       read_until_expr_op<AsyncReadStream,
1070         DynamicBuffer, RegEx, ReadHandler>* this_handler)
1071   {
1072     return asio_handler_alloc_helpers::allocate(
1073         size, this_handler->handler_);
1074   }
1075 
1076   template <typename AsyncReadStream, typename DynamicBuffer,
1077       typename RegEx, typename ReadHandler>
asio_handler_deallocate(void * pointer,std::size_t size,read_until_expr_op<AsyncReadStream,DynamicBuffer,RegEx,ReadHandler> * this_handler)1078   inline void asio_handler_deallocate(void* pointer, std::size_t size,
1079       read_until_expr_op<AsyncReadStream,
1080         DynamicBuffer, RegEx, ReadHandler>* this_handler)
1081   {
1082     asio_handler_alloc_helpers::deallocate(
1083         pointer, size, this_handler->handler_);
1084   }
1085 
1086   template <typename AsyncReadStream, typename DynamicBuffer,
1087       typename RegEx, typename ReadHandler>
asio_handler_is_continuation(read_until_expr_op<AsyncReadStream,DynamicBuffer,RegEx,ReadHandler> * this_handler)1088   inline bool asio_handler_is_continuation(
1089       read_until_expr_op<AsyncReadStream,
1090         DynamicBuffer, RegEx, ReadHandler>* this_handler)
1091   {
1092     return this_handler->start_ == 0 ? true
1093       : asio_handler_cont_helpers::is_continuation(
1094           this_handler->handler_);
1095   }
1096 
1097   template <typename Function, typename AsyncReadStream,
1098       typename DynamicBuffer, typename RegEx, typename ReadHandler>
asio_handler_invoke(Function & function,read_until_expr_op<AsyncReadStream,DynamicBuffer,RegEx,ReadHandler> * this_handler)1099   inline void asio_handler_invoke(Function& function,
1100       read_until_expr_op<AsyncReadStream,
1101         DynamicBuffer, RegEx, ReadHandler>* this_handler)
1102   {
1103     asio_handler_invoke_helpers::invoke(
1104         function, this_handler->handler_);
1105   }
1106 
1107   template <typename Function, typename AsyncReadStream,
1108       typename DynamicBuffer, typename RegEx, typename ReadHandler>
asio_handler_invoke(const Function & function,read_until_expr_op<AsyncReadStream,DynamicBuffer,RegEx,ReadHandler> * this_handler)1109   inline void asio_handler_invoke(const Function& function,
1110       read_until_expr_op<AsyncReadStream,
1111         DynamicBuffer, RegEx, ReadHandler>* this_handler)
1112   {
1113     asio_handler_invoke_helpers::invoke(
1114         function, this_handler->handler_);
1115   }
1116 } // namespace detail
1117 
1118 #if !defined(GENERATING_DOCUMENTATION)
1119 
1120 template <typename AsyncReadStream, typename DynamicBuffer,
1121     typename RegEx, typename ReadHandler, typename Allocator>
1122 struct associated_allocator<
1123     detail::read_until_expr_op<AsyncReadStream,
1124       DynamicBuffer, RegEx, ReadHandler>,
1125     Allocator>
1126 {
1127   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
1128 
getasio::associated_allocator1129   static type get(
1130       const detail::read_until_expr_op<AsyncReadStream,
1131         DynamicBuffer, RegEx, ReadHandler>& h,
1132       const Allocator& a = Allocator()) ASIO_NOEXCEPT
1133   {
1134     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
1135   }
1136 };
1137 
1138 template <typename AsyncReadStream, typename DynamicBuffer,
1139     typename RegEx, typename ReadHandler, typename Executor>
1140 struct associated_executor<
1141     detail::read_until_expr_op<AsyncReadStream,
1142       DynamicBuffer, RegEx, ReadHandler>,
1143     Executor>
1144 {
1145   typedef typename associated_executor<ReadHandler, Executor>::type type;
1146 
getasio::associated_executor1147   static type get(
1148       const detail::read_until_expr_op<AsyncReadStream,
1149         DynamicBuffer, RegEx, ReadHandler>& h,
1150       const Executor& ex = Executor()) ASIO_NOEXCEPT
1151   {
1152     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
1153   }
1154 };
1155 
1156 #endif // !defined(GENERATING_DOCUMENTATION)
1157 
1158 template <typename AsyncReadStream,
1159     typename DynamicBuffer, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,void (asio::error_code,std::size_t))1160 ASIO_INITFN_RESULT_TYPE(ReadHandler,
1161     void (asio::error_code, std::size_t))
1162 async_read_until(AsyncReadStream& s,
1163     ASIO_MOVE_ARG(DynamicBuffer) buffers,
1164     const boost::regex& expr,
1165     ASIO_MOVE_ARG(ReadHandler) handler)
1166 {
1167   // If you get an error on the following line it means that your handler does
1168   // not meet the documented type requirements for a ReadHandler.
1169   ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
1170 
1171   async_completion<ReadHandler,
1172     void (asio::error_code, std::size_t)> init(handler);
1173 
1174   detail::read_until_expr_op<AsyncReadStream,
1175     typename decay<DynamicBuffer>::type,
1176       boost::regex, ASIO_HANDLER_TYPE(ReadHandler,
1177         void (asio::error_code, std::size_t))>(
1178           s, ASIO_MOVE_CAST(DynamicBuffer)(buffers),
1179             expr, init.completion_handler)(asio::error_code(), 0, 1);
1180 
1181   return init.result.get();
1182 }
1183 
1184 #endif // defined(ASIO_HAS_BOOST_REGEX)
1185 
1186 namespace detail
1187 {
1188   template <typename AsyncReadStream, typename DynamicBuffer,
1189       typename MatchCondition, typename ReadHandler>
1190   class read_until_match_op
1191   {
1192   public:
1193     template <typename BufferSequence>
read_until_match_op(AsyncReadStream & stream,ASIO_MOVE_ARG (BufferSequence)buffers,MatchCondition match_condition,ReadHandler & handler)1194     read_until_match_op(AsyncReadStream& stream,
1195         ASIO_MOVE_ARG(BufferSequence) buffers,
1196         MatchCondition match_condition, ReadHandler& handler)
1197       : stream_(stream),
1198         buffers_(ASIO_MOVE_CAST(BufferSequence)(buffers)),
1199         match_condition_(match_condition),
1200         start_(0),
1201         search_position_(0),
1202         handler_(ASIO_MOVE_CAST(ReadHandler)(handler))
1203     {
1204     }
1205 
1206 #if defined(ASIO_HAS_MOVE)
read_until_match_op(const read_until_match_op & other)1207     read_until_match_op(const read_until_match_op& other)
1208       : stream_(other.stream_),
1209         buffers_(other.buffers_),
1210         match_condition_(other.match_condition_),
1211         start_(other.start_),
1212         search_position_(other.search_position_),
1213         handler_(other.handler_)
1214     {
1215     }
1216 
read_until_match_op(read_until_match_op && other)1217     read_until_match_op(read_until_match_op&& other)
1218       : stream_(other.stream_),
1219         buffers_(ASIO_MOVE_CAST(DynamicBuffer)(other.buffers_)),
1220         match_condition_(other.match_condition_),
1221         start_(other.start_),
1222         search_position_(other.search_position_),
1223         handler_(ASIO_MOVE_CAST(ReadHandler)(other.handler_))
1224     {
1225     }
1226 #endif // defined(ASIO_HAS_MOVE)
1227 
operator ()(const asio::error_code & ec,std::size_t bytes_transferred,int start=0)1228     void operator()(const asio::error_code& ec,
1229         std::size_t bytes_transferred, int start = 0)
1230     {
1231       const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
1232       std::size_t bytes_to_read;
1233       switch (start_ = start)
1234       {
1235       case 1:
1236         for (;;)
1237         {
1238           {
1239             // Determine the range of the data to be searched.
1240             typedef typename DynamicBuffer::const_buffers_type
1241               buffers_type;
1242             typedef buffers_iterator<buffers_type> iterator;
1243             buffers_type data_buffers = buffers_.data();
1244             iterator begin = iterator::begin(data_buffers);
1245             iterator start_pos = begin + search_position_;
1246             iterator end = iterator::end(data_buffers);
1247 
1248             // Look for a match.
1249             std::pair<iterator, bool> result = match_condition_(start_pos, end);
1250             if (result.second)
1251             {
1252               // Full match. We're done.
1253               search_position_ = result.first - begin;
1254               bytes_to_read = 0;
1255             }
1256 
1257             // No match yet. Check if buffer is full.
1258             else if (buffers_.size() == buffers_.max_size())
1259             {
1260               search_position_ = not_found;
1261               bytes_to_read = 0;
1262             }
1263 
1264             // Need to read some more data.
1265             else
1266             {
1267               if (result.first != end)
1268               {
1269                 // Partial match. Next search needs to start from beginning of
1270                 // match.
1271                 search_position_ = result.first - begin;
1272               }
1273               else
1274               {
1275                 // Next search can start with the new data.
1276                 search_position_ = end - begin;
1277               }
1278 
1279               bytes_to_read = std::min<std::size_t>(
1280                     std::max<std::size_t>(512,
1281                       buffers_.capacity() - buffers_.size()),
1282                     std::min<std::size_t>(65536,
1283                       buffers_.max_size() - buffers_.size()));
1284             }
1285           }
1286 
1287           // Check if we're done.
1288           if (!start && bytes_to_read == 0)
1289             break;
1290 
1291           // Start a new asynchronous read operation to obtain more data.
1292           stream_.async_read_some(buffers_.prepare(bytes_to_read),
1293               ASIO_MOVE_CAST(read_until_match_op)(*this));
1294           return; default:
1295           buffers_.commit(bytes_transferred);
1296           if (ec || bytes_transferred == 0)
1297             break;
1298         }
1299 
1300         const asio::error_code result_ec =
1301           (search_position_ == not_found)
1302           ? error::not_found : ec;
1303 
1304         const std::size_t result_n =
1305           (ec || search_position_ == not_found)
1306           ? 0 : search_position_;
1307 
1308         handler_(result_ec, result_n);
1309       }
1310     }
1311 
1312   //private:
1313     AsyncReadStream& stream_;
1314     DynamicBuffer buffers_;
1315     MatchCondition match_condition_;
1316     int start_;
1317     std::size_t search_position_;
1318     ReadHandler handler_;
1319   };
1320 
1321   template <typename AsyncReadStream, typename DynamicBuffer,
1322       typename MatchCondition, typename ReadHandler>
asio_handler_allocate(std::size_t size,read_until_match_op<AsyncReadStream,DynamicBuffer,MatchCondition,ReadHandler> * this_handler)1323   inline void* asio_handler_allocate(std::size_t size,
1324       read_until_match_op<AsyncReadStream, DynamicBuffer,
1325         MatchCondition, ReadHandler>* this_handler)
1326   {
1327     return asio_handler_alloc_helpers::allocate(
1328         size, this_handler->handler_);
1329   }
1330 
1331   template <typename AsyncReadStream, typename DynamicBuffer,
1332       typename MatchCondition, typename ReadHandler>
asio_handler_deallocate(void * pointer,std::size_t size,read_until_match_op<AsyncReadStream,DynamicBuffer,MatchCondition,ReadHandler> * this_handler)1333   inline void asio_handler_deallocate(void* pointer, std::size_t size,
1334       read_until_match_op<AsyncReadStream, DynamicBuffer,
1335         MatchCondition, ReadHandler>* this_handler)
1336   {
1337     asio_handler_alloc_helpers::deallocate(
1338         pointer, size, this_handler->handler_);
1339   }
1340 
1341   template <typename AsyncReadStream, typename DynamicBuffer,
1342       typename MatchCondition, typename ReadHandler>
asio_handler_is_continuation(read_until_match_op<AsyncReadStream,DynamicBuffer,MatchCondition,ReadHandler> * this_handler)1343   inline bool asio_handler_is_continuation(
1344       read_until_match_op<AsyncReadStream, DynamicBuffer,
1345         MatchCondition, ReadHandler>* this_handler)
1346   {
1347     return this_handler->start_ == 0 ? true
1348       : asio_handler_cont_helpers::is_continuation(
1349           this_handler->handler_);
1350   }
1351 
1352   template <typename Function, typename AsyncReadStream,
1353       typename DynamicBuffer, typename MatchCondition,
1354       typename ReadHandler>
asio_handler_invoke(Function & function,read_until_match_op<AsyncReadStream,DynamicBuffer,MatchCondition,ReadHandler> * this_handler)1355   inline void asio_handler_invoke(Function& function,
1356       read_until_match_op<AsyncReadStream, DynamicBuffer,
1357         MatchCondition, ReadHandler>* this_handler)
1358   {
1359     asio_handler_invoke_helpers::invoke(
1360         function, this_handler->handler_);
1361   }
1362 
1363   template <typename Function, typename AsyncReadStream,
1364       typename DynamicBuffer, typename MatchCondition,
1365       typename ReadHandler>
asio_handler_invoke(const Function & function,read_until_match_op<AsyncReadStream,DynamicBuffer,MatchCondition,ReadHandler> * this_handler)1366   inline void asio_handler_invoke(const Function& function,
1367       read_until_match_op<AsyncReadStream, DynamicBuffer,
1368       MatchCondition, ReadHandler>* this_handler)
1369   {
1370     asio_handler_invoke_helpers::invoke(
1371         function, this_handler->handler_);
1372   }
1373 } // namespace detail
1374 
1375 #if !defined(GENERATING_DOCUMENTATION)
1376 
1377 template <typename AsyncReadStream, typename DynamicBuffer,
1378     typename MatchCondition, typename ReadHandler, typename Allocator>
1379 struct associated_allocator<
1380     detail::read_until_match_op<AsyncReadStream,
1381       DynamicBuffer, MatchCondition, ReadHandler>,
1382     Allocator>
1383 {
1384   typedef typename associated_allocator<ReadHandler, Allocator>::type type;
1385 
getasio::associated_allocator1386   static type get(
1387       const detail::read_until_match_op<AsyncReadStream,
1388         DynamicBuffer, MatchCondition, ReadHandler>& h,
1389       const Allocator& a = Allocator()) ASIO_NOEXCEPT
1390   {
1391     return associated_allocator<ReadHandler, Allocator>::get(h.handler_, a);
1392   }
1393 };
1394 
1395 template <typename AsyncReadStream, typename DynamicBuffer,
1396     typename MatchCondition, typename ReadHandler, typename Executor>
1397 struct associated_executor<
1398     detail::read_until_match_op<AsyncReadStream,
1399       DynamicBuffer, MatchCondition, ReadHandler>,
1400     Executor>
1401 {
1402   typedef typename associated_executor<ReadHandler, Executor>::type type;
1403 
getasio::associated_executor1404   static type get(
1405       const detail::read_until_match_op<AsyncReadStream,
1406         DynamicBuffer, MatchCondition, ReadHandler>& h,
1407       const Executor& ex = Executor()) ASIO_NOEXCEPT
1408   {
1409     return associated_executor<ReadHandler, Executor>::get(h.handler_, ex);
1410   }
1411 };
1412 
1413 #endif // !defined(GENERATING_DOCUMENTATION)
1414 
1415 template <typename AsyncReadStream, typename DynamicBuffer,
1416     typename MatchCondition, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,void (asio::error_code,std::size_t))1417 ASIO_INITFN_RESULT_TYPE(ReadHandler,
1418     void (asio::error_code, std::size_t))
1419 async_read_until(AsyncReadStream& s,
1420     ASIO_MOVE_ARG(DynamicBuffer) buffers,
1421     MatchCondition match_condition, ASIO_MOVE_ARG(ReadHandler) handler,
1422     typename enable_if<is_match_condition<MatchCondition>::value>::type*)
1423 {
1424   // If you get an error on the following line it means that your handler does
1425   // not meet the documented type requirements for a ReadHandler.
1426   ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
1427 
1428   async_completion<ReadHandler,
1429     void (asio::error_code, std::size_t)> init(handler);
1430 
1431   detail::read_until_match_op<AsyncReadStream,
1432     typename decay<DynamicBuffer>::type,
1433       MatchCondition, ASIO_HANDLER_TYPE(ReadHandler,
1434         void (asio::error_code, std::size_t))>(
1435           s, ASIO_MOVE_CAST(DynamicBuffer)(buffers),
1436             match_condition, init.completion_handler)(
1437               asio::error_code(), 0, 1);
1438 
1439   return init.result.get();
1440 }
1441 
1442 #if !defined(ASIO_NO_IOSTREAM)
1443 
1444 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,void (asio::error_code,std::size_t))1445 inline ASIO_INITFN_RESULT_TYPE(ReadHandler,
1446     void (asio::error_code, std::size_t))
1447 async_read_until(AsyncReadStream& s,
1448     asio::basic_streambuf<Allocator>& b,
1449     char delim, ASIO_MOVE_ARG(ReadHandler) handler)
1450 {
1451   return async_read_until(s, basic_streambuf_ref<Allocator>(b),
1452       delim, ASIO_MOVE_CAST(ReadHandler)(handler));
1453 }
1454 
1455 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,void (asio::error_code,std::size_t))1456 inline ASIO_INITFN_RESULT_TYPE(ReadHandler,
1457     void (asio::error_code, std::size_t))
1458 async_read_until(AsyncReadStream& s,
1459     asio::basic_streambuf<Allocator>& b,
1460     ASIO_STRING_VIEW_PARAM delim,
1461     ASIO_MOVE_ARG(ReadHandler) handler)
1462 {
1463   return async_read_until(s, basic_streambuf_ref<Allocator>(b),
1464       delim, ASIO_MOVE_CAST(ReadHandler)(handler));
1465 }
1466 
1467 #if defined(ASIO_HAS_BOOST_REGEX)
1468 
1469 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,void (asio::error_code,std::size_t))1470 inline ASIO_INITFN_RESULT_TYPE(ReadHandler,
1471     void (asio::error_code, std::size_t))
1472 async_read_until(AsyncReadStream& s,
1473     asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
1474     ASIO_MOVE_ARG(ReadHandler) handler)
1475 {
1476   return async_read_until(s, basic_streambuf_ref<Allocator>(b),
1477       expr, ASIO_MOVE_CAST(ReadHandler)(handler));
1478 }
1479 
1480 #endif // defined(ASIO_HAS_BOOST_REGEX)
1481 
1482 template <typename AsyncReadStream, typename Allocator,
1483     typename MatchCondition, typename ReadHandler>
ASIO_INITFN_RESULT_TYPE(ReadHandler,void (asio::error_code,std::size_t))1484 inline ASIO_INITFN_RESULT_TYPE(ReadHandler,
1485     void (asio::error_code, std::size_t))
1486 async_read_until(AsyncReadStream& s,
1487     asio::basic_streambuf<Allocator>& b,
1488     MatchCondition match_condition, ASIO_MOVE_ARG(ReadHandler) handler,
1489     typename enable_if<is_match_condition<MatchCondition>::value>::type*)
1490 {
1491   return async_read_until(s, basic_streambuf_ref<Allocator>(b),
1492       match_condition, ASIO_MOVE_CAST(ReadHandler)(handler));
1493 }
1494 
1495 #endif // !defined(ASIO_NO_IOSTREAM)
1496 #endif // !defined(ASIO_NO_EXTENSIONS)
1497 
1498 } // namespace asio
1499 
1500 #include "asio/detail/pop_options.hpp"
1501 
1502 #endif // ASIO_IMPL_READ_UNTIL_HPP
1503