1//
2// impl/read_until.hpp
3// ~~~~~~~~~~~~~~~~~~~
4//
5// Copyright (c) 2003-2015 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/buffer.hpp>
23#include <boost/asio/buffers_iterator.hpp>
24#include <boost/asio/detail/bind_handler.hpp>
25#include <boost/asio/detail/handler_alloc_helpers.hpp>
26#include <boost/asio/detail/handler_cont_helpers.hpp>
27#include <boost/asio/detail/handler_invoke_helpers.hpp>
28#include <boost/asio/detail/handler_type_requirements.hpp>
29#include <boost/asio/detail/limits.hpp>
30#include <boost/asio/detail/throw_error.hpp>
31
32#include <boost/asio/detail/push_options.hpp>
33
34namespace boost {
35namespace asio {
36
37template <typename SyncReadStream, typename Allocator>
38inline std::size_t read_until(SyncReadStream& s,
39 boost::asio::basic_streambuf<Allocator>& b, char delim)
40{
41 boost::system::error_code ec;
42 std::size_t bytes_transferred = read_until(s, b, delim, ec);
43 boost::asio::detail::throw_error(ec, "read_until");
44 return bytes_transferred;
45}
46
47template <typename SyncReadStream, typename Allocator>
48std::size_t read_until(SyncReadStream& s,
49 boost::asio::basic_streambuf<Allocator>& b, char delim,
50 boost::system::error_code& ec)
51{
52 std::size_t search_position = 0;
53 for (;;)
54 {
55 // Determine the range of the data to be searched.
56 typedef typename boost::asio::basic_streambuf<
57 Allocator>::const_buffers_type const_buffers_type;
58 typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
59 const_buffers_type buffers = b.data();
60 iterator begin = iterator::begin(buffers);
61 iterator start_pos = begin + search_position;
62 iterator end = iterator::end(buffers);
63
64 // Look for a match.
65 iterator iter = std::find(start_pos, end, delim);
66 if (iter != end)
67 {
68 // Found a match. We're done.
69 ec = boost::system::error_code();
70 return iter - begin + 1;
71 }
72 else
73 {
74 // No match. Next search can start with the new data.
75 search_position = end - begin;
76 }
77
78 // Check if buffer is full.
79 if (b.size() == b.max_size())
80 {
81 ec = error::not_found;
82 return 0;
83 }
84
85 // Need more data.
86 std::size_t bytes_to_read = read_size_helper(b, 65536);
87 b.commit(s.read_some(b.prepare(bytes_to_read), ec));
88 if (ec)
89 return 0;
90 }
91}
92
93template <typename SyncReadStream, typename Allocator>
94inline std::size_t read_until(SyncReadStream& s,
95 boost::asio::basic_streambuf<Allocator>& b, const std::string& delim)
96{
97 boost::system::error_code ec;
98 std::size_t bytes_transferred = read_until(s, b, delim, ec);
99 boost::asio::detail::throw_error(ec, "read_until");
100 return bytes_transferred;
101}
102
103namespace detail
104{
105 // Algorithm that finds a subsequence of equal values in a sequence. Returns
106 // (iterator,true) if a full match was found, in which case the iterator
107 // points to the beginning of the match. Returns (iterator,false) if a
108 // partial match was found at the end of the first sequence, in which case
109 // the iterator points to the beginning of the partial match. Returns
110 // (last1,false) if no full or partial match was found.
111 template <typename Iterator1, typename Iterator2>
112 std::pair<Iterator1, bool> partial_search(
113 Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
114 {
115 for (Iterator1 iter1 = first1; iter1 != last1; ++iter1)
116 {
117 Iterator1 test_iter1 = iter1;
118 Iterator2 test_iter2 = first2;
119 for (;; ++test_iter1, ++test_iter2)
120 {
121 if (test_iter2 == last2)
122 return std::make_pair(iter1, true);
123 if (test_iter1 == last1)
124 {
125 if (test_iter2 != first2)
126 return std::make_pair(iter1, false);
127 else
128 break;
129 }
130 if (*test_iter1 != *test_iter2)
131 break;
132 }
133 }
134 return std::make_pair(last1, false);
135 }
136} // namespace detail
137
138template <typename SyncReadStream, typename Allocator>
139std::size_t read_until(SyncReadStream& s,
140 boost::asio::basic_streambuf<Allocator>& b, const std::string& delim,
141 boost::system::error_code& ec)
142{
143 std::size_t search_position = 0;
144 for (;;)
145 {
146 // Determine the range of the data to be searched.
147 typedef typename boost::asio::basic_streambuf<
148 Allocator>::const_buffers_type const_buffers_type;
149 typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
150 const_buffers_type buffers = b.data();
151 iterator begin = iterator::begin(buffers);
152 iterator start_pos = begin + search_position;
153 iterator end = iterator::end(buffers);
154
155 // Look for a match.
156 std::pair<iterator, bool> result = detail::partial_search(
157 start_pos, end, delim.begin(), delim.end());
158 if (result.first != end)
159 {
160 if (result.second)
161 {
162 // Full match. We're done.
163 ec = boost::system::error_code();
164 return result.first - begin + delim.length();
165 }
166 else
167 {
168 // Partial match. Next search needs to start from beginning of match.
169 search_position = result.first - begin;
170 }
171 }
172 else
173 {
174 // No match. Next search can start with the new data.
175 search_position = end - begin;
176 }
177
178 // Check if buffer is full.
179 if (b.size() == b.max_size())
180 {
181 ec = error::not_found;
182 return 0;
183 }
184
185 // Need more data.
186 std::size_t bytes_to_read = read_size_helper(b, 65536);
187 b.commit(s.read_some(b.prepare(bytes_to_read), ec));
188 if (ec)
189 return 0;
190 }
191}
192
193#if defined(BOOST_ASIO_HAS_BOOST_REGEX)
194
195template <typename SyncReadStream, typename Allocator>
196inline std::size_t read_until(SyncReadStream& s,
197 boost::asio::basic_streambuf<Allocator>& b, const boost::regex& expr)
198{
199 boost::system::error_code ec;
200 std::size_t bytes_transferred = read_until(s, b, expr, ec);
201 boost::asio::detail::throw_error(ec, "read_until");
202 return bytes_transferred;
203}
204
205template <typename SyncReadStream, typename Allocator>
206std::size_t read_until(SyncReadStream& s,
207 boost::asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
208 boost::system::error_code& ec)
209{
210 std::size_t search_position = 0;
211 for (;;)
212 {
213 // Determine the range of the data to be searched.
214 typedef typename boost::asio::basic_streambuf<
215 Allocator>::const_buffers_type const_buffers_type;
216 typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
217 const_buffers_type buffers = b.data();
218 iterator begin = iterator::begin(buffers);
219 iterator start_pos = begin + search_position;
220 iterator end = iterator::end(buffers);
221
222 // Look for a match.
223 boost::match_results<iterator,
224 typename std::vector<boost::sub_match<iterator> >::allocator_type>
225 match_results;
226 if (regex_search(start_pos, end, match_results, expr,
227 boost::match_default | boost::match_partial))
228 {
229 if (match_results[0].matched)
230 {
231 // Full match. We're done.
232 ec = boost::system::error_code();
233 return match_results[0].second - begin;
234 }
235 else
236 {
237 // Partial match. Next search needs to start from beginning of match.
238 search_position = match_results[0].first - begin;
239 }
240 }
241 else
242 {
243 // No match. Next search can start with the new data.
244 search_position = end - begin;
245 }
246
247 // Check if buffer is full.
248 if (b.size() == b.max_size())
249 {
250 ec = error::not_found;
251 return 0;
252 }
253
254 // Need more data.
255 std::size_t bytes_to_read = read_size_helper(b, 65536);
256 b.commit(s.read_some(b.prepare(bytes_to_read), ec));
257 if (ec)
258 return 0;
259 }
260}
261
262#endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
263
264template <typename SyncReadStream, typename Allocator, typename MatchCondition>
265std::size_t read_until(SyncReadStream& s,
266 boost::asio::basic_streambuf<Allocator>& b,
267 MatchCondition match_condition, boost::system::error_code& ec,
268 typename enable_if<is_match_condition<MatchCondition>::value>::type*)
269{
270 std::size_t search_position = 0;
271 for (;;)
272 {
273 // Determine the range of the data to be searched.
274 typedef typename boost::asio::basic_streambuf<
275 Allocator>::const_buffers_type const_buffers_type;
276 typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
277 const_buffers_type buffers = b.data();
278 iterator begin = iterator::begin(buffers);
279 iterator start_pos = begin + search_position;
280 iterator end = iterator::end(buffers);
281
282 // Look for a match.
283 std::pair<iterator, bool> result = match_condition(start_pos, end);
284 if (result.second)
285 {
286 // Full match. We're done.
287 ec = boost::system::error_code();
288 return result.first - begin;
289 }
290 else if (result.first != end)
291 {
292 // Partial match. Next search needs to start from beginning of match.
293 search_position = result.first - begin;
294 }
295 else
296 {
297 // No match. Next search can start with the new data.
298 search_position = end - begin;
299 }
300
301 // Check if buffer is full.
302 if (b.size() == b.max_size())
303 {
304 ec = error::not_found;
305 return 0;
306 }
307
308 // Need more data.
309 std::size_t bytes_to_read = read_size_helper(b, 65536);
310 b.commit(s.read_some(b.prepare(bytes_to_read), ec));
311 if (ec)
312 return 0;
313 }
314}
315
316template <typename SyncReadStream, typename Allocator, typename MatchCondition>
317inline std::size_t read_until(SyncReadStream& s,
318 boost::asio::basic_streambuf<Allocator>& b, MatchCondition match_condition,
319 typename enable_if<is_match_condition<MatchCondition>::value>::type*)
320{
321 boost::system::error_code ec;
322 std::size_t bytes_transferred = read_until(s, b, match_condition, ec);
323 boost::asio::detail::throw_error(ec, "read_until");
324 return bytes_transferred;
325}
326
327namespace detail
328{
329 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
330 class read_until_delim_op
331 {
332 public:
333 read_until_delim_op(AsyncReadStream& stream,
334 boost::asio::basic_streambuf<Allocator>& streambuf,
335 char delim, ReadHandler& handler)
336 : stream_(stream),
337 streambuf_(streambuf),
338 delim_(delim),
339 start_(0),
340 search_position_(0),
341 handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
342 {
343 }
344
345#if defined(BOOST_ASIO_HAS_MOVE)
346 read_until_delim_op(const read_until_delim_op& other)
347 : stream_(other.stream_),
348 streambuf_(other.streambuf_),
349 delim_(other.delim_),
350 start_(other.start_),
351 search_position_(other.search_position_),
352 handler_(other.handler_)
353 {
354 }
355
356 read_until_delim_op(read_until_delim_op&& other)
357 : stream_(other.stream_),
358 streambuf_(other.streambuf_),
359 delim_(other.delim_),
360 start_(other.start_),
361 search_position_(other.search_position_),
362 handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
363 {
364 }
365#endif // defined(BOOST_ASIO_HAS_MOVE)
366
367 void operator()(const boost::system::error_code& ec,
368 std::size_t bytes_transferred, int start = 0)
369 {
370 const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
371 std::size_t bytes_to_read;
372 switch (start_ = start)
373 {
374 case 1:
375 for (;;)
376 {
377 {
378 // Determine the range of the data to be searched.
379 typedef typename boost::asio::basic_streambuf<
380 Allocator>::const_buffers_type const_buffers_type;
381 typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
382 const_buffers_type buffers = streambuf_.data();
383 iterator begin = iterator::begin(buffers);
384 iterator start_pos = begin + search_position_;
385 iterator end = iterator::end(buffers);
386
387 // Look for a match.
388 iterator iter = std::find(start_pos, end, delim_);
389 if (iter != end)
390 {
391 // Found a match. We're done.
392 search_position_ = iter - begin + 1;
393 bytes_to_read = 0;
394 }
395
396 // No match yet. Check if buffer is full.
397 else if (streambuf_.size() == streambuf_.max_size())
398 {
399 search_position_ = not_found;
400 bytes_to_read = 0;
401 }
402
403 // Need to read some more data.
404 else
405 {
406 // Next search can start with the new data.
407 search_position_ = end - begin;
408 bytes_to_read = read_size_helper(streambuf_, 65536);
409 }
410 }
411
412 // Check if we're done.
413 if (!start && bytes_to_read == 0)
414 break;
415
416 // Start a new asynchronous read operation to obtain more data.
417 stream_.async_read_some(streambuf_.prepare(bytes_to_read),
418 BOOST_ASIO_MOVE_CAST(read_until_delim_op)(*this));
419 return; default:
420 streambuf_.commit(bytes_transferred);
421 if (ec || bytes_transferred == 0)
422 break;
423 }
424
425 const boost::system::error_code result_ec =
426 (search_position_ == not_found)
427 ? error::not_found : ec;
428
429 const std::size_t result_n =
430 (ec || search_position_ == not_found)
431 ? 0 : search_position_;
432
433 handler_(result_ec, result_n);
434 }
435 }
436
437 //private:
438 AsyncReadStream& stream_;
439 boost::asio::basic_streambuf<Allocator>& streambuf_;
440 char delim_;
441 int start_;
442 std::size_t search_position_;
443 ReadHandler handler_;
444 };
445
446 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
447 inline void* asio_handler_allocate(std::size_t size,
448 read_until_delim_op<AsyncReadStream,
449 Allocator, ReadHandler>* this_handler)
450 {
451 return boost_asio_handler_alloc_helpers::allocate(
452 size, this_handler->handler_);
453 }
454
455 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
456 inline void asio_handler_deallocate(void* pointer, std::size_t size,
457 read_until_delim_op<AsyncReadStream,
458 Allocator, ReadHandler>* this_handler)
459 {
460 boost_asio_handler_alloc_helpers::deallocate(
461 pointer, size, this_handler->handler_);
462 }
463
464 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
465 inline bool asio_handler_is_continuation(
466 read_until_delim_op<AsyncReadStream,
467 Allocator, ReadHandler>* this_handler)
468 {
469 return this_handler->start_ == 0 ? true
470 : boost_asio_handler_cont_helpers::is_continuation(
471 this_handler->handler_);
472 }
473
474 template <typename Function, typename AsyncReadStream, typename Allocator,
475 typename ReadHandler>
476 inline void asio_handler_invoke(Function& function,
477 read_until_delim_op<AsyncReadStream,
478 Allocator, ReadHandler>* this_handler)
479 {
480 boost_asio_handler_invoke_helpers::invoke(
481 function, this_handler->handler_);
482 }
483
484 template <typename Function, typename AsyncReadStream, typename Allocator,
485 typename ReadHandler>
486 inline void asio_handler_invoke(const Function& function,
487 read_until_delim_op<AsyncReadStream,
488 Allocator, ReadHandler>* this_handler)
489 {
490 boost_asio_handler_invoke_helpers::invoke(
491 function, this_handler->handler_);
492 }
493} // namespace detail
494
495template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
496BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
497 void (boost::system::error_code, std::size_t))
498async_read_until(AsyncReadStream& s,
499 boost::asio::basic_streambuf<Allocator>& b, char delim,
500 BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
501{
502 // If you get an error on the following line it means that your handler does
503 // not meet the documented type requirements for a ReadHandler.
504 BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
505
506 detail::async_result_init<
507 ReadHandler, void (boost::system::error_code, std::size_t)> init(
508 BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
509
510 detail::read_until_delim_op<AsyncReadStream,
511 Allocator, BOOST_ASIO_HANDLER_TYPE(ReadHandler,
512 void (boost::system::error_code, std::size_t))>(
513 s, b, delim, init.handler)(
514 boost::system::error_code(), 0, 1);
515
516 return init.result.get();
517}
518
519namespace detail
520{
521 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
522 class read_until_delim_string_op
523 {
524 public:
525 read_until_delim_string_op(AsyncReadStream& stream,
526 boost::asio::basic_streambuf<Allocator>& streambuf,
527 const std::string& delim, ReadHandler& handler)
528 : stream_(stream),
529 streambuf_(streambuf),
530 delim_(delim),
531 start_(0),
532 search_position_(0),
533 handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
534 {
535 }
536
537#if defined(BOOST_ASIO_HAS_MOVE)
538 read_until_delim_string_op(const read_until_delim_string_op& other)
539 : stream_(other.stream_),
540 streambuf_(other.streambuf_),
541 delim_(other.delim_),
542 start_(other.start_),
543 search_position_(other.search_position_),
544 handler_(other.handler_)
545 {
546 }
547
548 read_until_delim_string_op(read_until_delim_string_op&& other)
549 : stream_(other.stream_),
550 streambuf_(other.streambuf_),
551 delim_(BOOST_ASIO_MOVE_CAST(std::string)(other.delim_)),
552 start_(other.start_),
553 search_position_(other.search_position_),
554 handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
555 {
556 }
557#endif // defined(BOOST_ASIO_HAS_MOVE)
558
559 void operator()(const boost::system::error_code& ec,
560 std::size_t bytes_transferred, int start = 0)
561 {
562 const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
563 std::size_t bytes_to_read;
564 switch (start_ = start)
565 {
566 case 1:
567 for (;;)
568 {
569 {
570 // Determine the range of the data to be searched.
571 typedef typename boost::asio::basic_streambuf<
572 Allocator>::const_buffers_type const_buffers_type;
573 typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
574 const_buffers_type buffers = streambuf_.data();
575 iterator begin = iterator::begin(buffers);
576 iterator start_pos = begin + search_position_;
577 iterator end = iterator::end(buffers);
578
579 // Look for a match.
580 std::pair<iterator, bool> result = detail::partial_search(
581 start_pos, end, delim_.begin(), delim_.end());
582 if (result.first != end && result.second)
583 {
584 // Full match. We're done.
585 search_position_ = result.first - begin + delim_.length();
586 bytes_to_read = 0;
587 }
588
589 // No match yet. Check if buffer is full.
590 else if (streambuf_.size() == streambuf_.max_size())
591 {
592 search_position_ = not_found;
593 bytes_to_read = 0;
594 }
595
596 // Need to read some more data.
597 else
598 {
599 if (result.first != end)
600 {
601 // Partial match. Next search needs to start from beginning of
602 // match.
603 search_position_ = result.first - begin;
604 }
605 else
606 {
607 // Next search can start with the new data.
608 search_position_ = end - begin;
609 }
610
611 bytes_to_read = read_size_helper(streambuf_, 65536);
612 }
613 }
614
615 // Check if we're done.
616 if (!start && bytes_to_read == 0)
617 break;
618
619 // Start a new asynchronous read operation to obtain more data.
620 stream_.async_read_some(streambuf_.prepare(bytes_to_read),
621 BOOST_ASIO_MOVE_CAST(read_until_delim_string_op)(*this));
622 return; default:
623 streambuf_.commit(bytes_transferred);
624 if (ec || bytes_transferred == 0)
625 break;
626 }
627
628 const boost::system::error_code result_ec =
629 (search_position_ == not_found)
630 ? error::not_found : ec;
631
632 const std::size_t result_n =
633 (ec || search_position_ == not_found)
634 ? 0 : search_position_;
635
636 handler_(result_ec, result_n);
637 }
638 }
639
640 //private:
641 AsyncReadStream& stream_;
642 boost::asio::basic_streambuf<Allocator>& streambuf_;
643 std::string delim_;
644 int start_;
645 std::size_t search_position_;
646 ReadHandler handler_;
647 };
648
649 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
650 inline void* asio_handler_allocate(std::size_t size,
651 read_until_delim_string_op<AsyncReadStream,
652 Allocator, ReadHandler>* this_handler)
653 {
654 return boost_asio_handler_alloc_helpers::allocate(
655 size, this_handler->handler_);
656 }
657
658 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
659 inline void asio_handler_deallocate(void* pointer, std::size_t size,
660 read_until_delim_string_op<AsyncReadStream,
661 Allocator, ReadHandler>* this_handler)
662 {
663 boost_asio_handler_alloc_helpers::deallocate(
664 pointer, size, this_handler->handler_);
665 }
666
667 template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
668 inline bool asio_handler_is_continuation(
669 read_until_delim_string_op<AsyncReadStream,
670 Allocator, ReadHandler>* this_handler)
671 {
672 return this_handler->start_ == 0 ? true
673 : boost_asio_handler_cont_helpers::is_continuation(
674 this_handler->handler_);
675 }
676
677 template <typename Function, typename AsyncReadStream,
678 typename Allocator, typename ReadHandler>
679 inline void asio_handler_invoke(Function& function,
680 read_until_delim_string_op<AsyncReadStream,
681 Allocator, ReadHandler>* this_handler)
682 {
683 boost_asio_handler_invoke_helpers::invoke(
684 function, this_handler->handler_);
685 }
686
687 template <typename Function, typename AsyncReadStream,
688 typename Allocator, typename ReadHandler>
689 inline void asio_handler_invoke(const Function& function,
690 read_until_delim_string_op<AsyncReadStream,
691 Allocator, ReadHandler>* this_handler)
692 {
693 boost_asio_handler_invoke_helpers::invoke(
694 function, this_handler->handler_);
695 }
696} // namespace detail
697
698template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
699BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
700 void (boost::system::error_code, std::size_t))
701async_read_until(AsyncReadStream& s,
702 boost::asio::basic_streambuf<Allocator>& b, const std::string& delim,
703 BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
704{
705 // If you get an error on the following line it means that your handler does
706 // not meet the documented type requirements for a ReadHandler.
707 BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
708
709 detail::async_result_init<
710 ReadHandler, void (boost::system::error_code, std::size_t)> init(
711 BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
712
713 detail::read_until_delim_string_op<AsyncReadStream,
714 Allocator, BOOST_ASIO_HANDLER_TYPE(ReadHandler,
715 void (boost::system::error_code, std::size_t))>(
716 s, b, delim, init.handler)(
717 boost::system::error_code(), 0, 1);
718
719 return init.result.get();
720}
721
722#if defined(BOOST_ASIO_HAS_BOOST_REGEX)
723
724namespace detail
725{
726 template <typename AsyncReadStream, typename Allocator,
727 typename RegEx, typename ReadHandler>
728 class read_until_expr_op
729 {
730 public:
731 read_until_expr_op(AsyncReadStream& stream,
732 boost::asio::basic_streambuf<Allocator>& streambuf,
733 const boost::regex& expr, ReadHandler& handler)
734 : stream_(stream),
735 streambuf_(streambuf),
736 expr_(expr),
737 start_(0),
738 search_position_(0),
739 handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
740 {
741 }
742
743#if defined(BOOST_ASIO_HAS_MOVE)
744 read_until_expr_op(const read_until_expr_op& other)
745 : stream_(other.stream_),
746 streambuf_(other.streambuf_),
747 expr_(other.expr_),
748 start_(other.start_),
749 search_position_(other.search_position_),
750 handler_(other.handler_)
751 {
752 }
753
754 read_until_expr_op(read_until_expr_op&& other)
755 : stream_(other.stream_),
756 streambuf_(other.streambuf_),
757 expr_(other.expr_),
758 start_(other.start_),
759 search_position_(other.search_position_),
760 handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
761 {
762 }
763#endif // defined(BOOST_ASIO_HAS_MOVE)
764
765 void operator()(const boost::system::error_code& ec,
766 std::size_t bytes_transferred, int start = 0)
767 {
768 const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
769 std::size_t bytes_to_read;
770 switch (start_ = start)
771 {
772 case 1:
773 for (;;)
774 {
775 {
776 // Determine the range of the data to be searched.
777 typedef typename boost::asio::basic_streambuf<
778 Allocator>::const_buffers_type const_buffers_type;
779 typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
780 const_buffers_type buffers = streambuf_.data();
781 iterator begin = iterator::begin(buffers);
782 iterator start_pos = begin + search_position_;
783 iterator end = iterator::end(buffers);
784
785 // Look for a match.
786 boost::match_results<iterator,
787 typename std::vector<boost::sub_match<iterator> >::allocator_type>
788 match_results;
789 bool match = regex_search(start_pos, end, match_results, expr_,
790 boost::match_default | boost::match_partial);
791 if (match && match_results[0].matched)
792 {
793 // Full match. We're done.
794 search_position_ = match_results[0].second - begin;
795 bytes_to_read = 0;
796 }
797
798 // No match yet. Check if buffer is full.
799 else if (streambuf_.size() == streambuf_.max_size())
800 {
801 search_position_ = not_found;
802 bytes_to_read = 0;
803 }
804
805 // Need to read some more data.
806 else
807 {
808 if (match)
809 {
810 // Partial match. Next search needs to start from beginning of
811 // match.
812 search_position_ = match_results[0].first - begin;
813 }
814 else
815 {
816 // Next search can start with the new data.
817 search_position_ = end - begin;
818 }
819
820 bytes_to_read = read_size_helper(streambuf_, 65536);
821 }
822 }
823
824 // Check if we're done.
825 if (!start && bytes_to_read == 0)
826 break;
827
828 // Start a new asynchronous read operation to obtain more data.
829 stream_.async_read_some(streambuf_.prepare(bytes_to_read),
830 BOOST_ASIO_MOVE_CAST(read_until_expr_op)(*this));
831 return; default:
832 streambuf_.commit(bytes_transferred);
833 if (ec || bytes_transferred == 0)
834 break;
835 }
836
837 const boost::system::error_code result_ec =
838 (search_position_ == not_found)
839 ? error::not_found : ec;
840
841 const std::size_t result_n =
842 (ec || search_position_ == not_found)
843 ? 0 : search_position_;
844
845 handler_(result_ec, result_n);
846 }
847 }
848
849 //private:
850 AsyncReadStream& stream_;
851 boost::asio::basic_streambuf<Allocator>& streambuf_;
852 RegEx expr_;
853 int start_;
854 std::size_t search_position_;
855 ReadHandler handler_;
856 };
857
858 template <typename AsyncReadStream, typename Allocator,
859 typename RegEx, typename ReadHandler>
860 inline void* asio_handler_allocate(std::size_t size,
861 read_until_expr_op<AsyncReadStream,
862 Allocator, RegEx, ReadHandler>* this_handler)
863 {
864 return boost_asio_handler_alloc_helpers::allocate(
865 size, this_handler->handler_);
866 }
867
868 template <typename AsyncReadStream, typename Allocator,
869 typename RegEx, typename ReadHandler>
870 inline void asio_handler_deallocate(void* pointer, std::size_t size,
871 read_until_expr_op<AsyncReadStream,
872 Allocator, RegEx, ReadHandler>* this_handler)
873 {
874 boost_asio_handler_alloc_helpers::deallocate(
875 pointer, size, this_handler->handler_);
876 }
877
878 template <typename AsyncReadStream, typename Allocator,
879 typename RegEx, typename ReadHandler>
880 inline bool asio_handler_is_continuation(
881 read_until_expr_op<AsyncReadStream,
882 Allocator, RegEx, ReadHandler>* this_handler)
883 {
884 return this_handler->start_ == 0 ? true
885 : boost_asio_handler_cont_helpers::is_continuation(
886 this_handler->handler_);
887 }
888
889 template <typename Function, typename AsyncReadStream, typename Allocator,
890 typename RegEx, typename ReadHandler>
891 inline void asio_handler_invoke(Function& function,
892 read_until_expr_op<AsyncReadStream,
893 Allocator, RegEx, ReadHandler>* this_handler)
894 {
895 boost_asio_handler_invoke_helpers::invoke(
896 function, this_handler->handler_);
897 }
898
899 template <typename Function, typename AsyncReadStream, typename Allocator,
900 typename RegEx, typename ReadHandler>
901 inline void asio_handler_invoke(const Function& function,
902 read_until_expr_op<AsyncReadStream,
903 Allocator, RegEx, ReadHandler>* this_handler)
904 {
905 boost_asio_handler_invoke_helpers::invoke(
906 function, this_handler->handler_);
907 }
908} // namespace detail
909
910template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
911BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
912 void (boost::system::error_code, std::size_t))
913async_read_until(AsyncReadStream& s,
914 boost::asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
915 BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
916{
917 // If you get an error on the following line it means that your handler does
918 // not meet the documented type requirements for a ReadHandler.
919 BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
920
921 detail::async_result_init<
922 ReadHandler, void (boost::system::error_code, std::size_t)> init(
923 BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
924
925 detail::read_until_expr_op<AsyncReadStream, Allocator,
926 boost::regex, BOOST_ASIO_HANDLER_TYPE(ReadHandler,
927 void (boost::system::error_code, std::size_t))>(
928 s, b, expr, init.handler)(
929 boost::system::error_code(), 0, 1);
930
931 return init.result.get();
932}
933
934#endif // defined(BOOST_ASIO_HAS_BOOST_REGEX)
935
936namespace detail
937{
938 template <typename AsyncReadStream, typename Allocator,
939 typename MatchCondition, typename ReadHandler>
940 class read_until_match_op
941 {
942 public:
943 read_until_match_op(AsyncReadStream& stream,
944 boost::asio::basic_streambuf<Allocator>& streambuf,
945 MatchCondition match_condition, ReadHandler& handler)
946 : stream_(stream),
947 streambuf_(streambuf),
948 match_condition_(match_condition),
949 start_(0),
950 search_position_(0),
951 handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(handler))
952 {
953 }
954
955#if defined(BOOST_ASIO_HAS_MOVE)
956 read_until_match_op(const read_until_match_op& other)
957 : stream_(other.stream_),
958 streambuf_(other.streambuf_),
959 match_condition_(other.match_condition_),
960 start_(other.start_),
961 search_position_(other.search_position_),
962 handler_(other.handler_)
963 {
964 }
965
966 read_until_match_op(read_until_match_op&& other)
967 : stream_(other.stream_),
968 streambuf_(other.streambuf_),
969 match_condition_(other.match_condition_),
970 start_(other.start_),
971 search_position_(other.search_position_),
972 handler_(BOOST_ASIO_MOVE_CAST(ReadHandler)(other.handler_))
973 {
974 }
975#endif // defined(BOOST_ASIO_HAS_MOVE)
976
977 void operator()(const boost::system::error_code& ec,
978 std::size_t bytes_transferred, int start = 0)
979 {
980 const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
981 std::size_t bytes_to_read;
982 switch (start_ = start)
983 {
984 case 1:
985 for (;;)
986 {
987 {
988 // Determine the range of the data to be searched.
989 typedef typename boost::asio::basic_streambuf<
990 Allocator>::const_buffers_type const_buffers_type;
991 typedef boost::asio::buffers_iterator<const_buffers_type> iterator;
992 const_buffers_type buffers = streambuf_.data();
993 iterator begin = iterator::begin(buffers);
994 iterator start_pos = begin + search_position_;
995 iterator end = iterator::end(buffers);
996
997 // Look for a match.
998 std::pair<iterator, bool> result = match_condition_(start_pos, end);
999 if (result.second)
1000 {
1001 // Full match. We're done.
1002 search_position_ = result.first - begin;
1003 bytes_to_read = 0;
1004 }
1005
1006 // No match yet. Check if buffer is full.
1007 else if (streambuf_.size() == streambuf_.max_size())
1008 {
1009 search_position_ = not_found;
1010 bytes_to_read = 0;
1011 }
1012
1013 // Need to read some more data.
1014 else
1015 {
1016 if (result.first != end)
1017 {
1018 // Partial match. Next search needs to start from beginning of
1019 // match.
1020 search_position_ = result.first - begin;
1021 }
1022 else
1023 {
1024 // Next search can start with the new data.
1025 search_position_ = end - begin;
1026 }
1027
1028 bytes_to_read = read_size_helper(streambuf_, 65536);
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(streambuf_.prepare(bytes_to_read),
1038 BOOST_ASIO_MOVE_CAST(read_until_match_op)(*this));
1039 return; default:
1040 streambuf_.commit(bytes_transferred);
1041 if (ec || bytes_transferred == 0)
1042 break;
1043 }
1044
1045 const boost::system::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 boost::asio::basic_streambuf<Allocator>& streambuf_;
1060 MatchCondition match_condition_;
1061 int start_;
1062 std::size_t search_position_;
1063 ReadHandler handler_;
1064 };
1065
1066 template <typename AsyncReadStream, typename Allocator,
1067 typename MatchCondition, typename ReadHandler>
1068 inline void* asio_handler_allocate(std::size_t size,
1069 read_until_match_op<AsyncReadStream,
1070 Allocator, MatchCondition, ReadHandler>* this_handler)
1071 {
1072 return boost_asio_handler_alloc_helpers::allocate(
1073 size, this_handler->handler_);
1074 }
1075
1076 template <typename AsyncReadStream, typename Allocator,
1077 typename MatchCondition, typename ReadHandler>
1078 inline void asio_handler_deallocate(void* pointer, std::size_t size,
1079 read_until_match_op<AsyncReadStream,
1080 Allocator, MatchCondition, ReadHandler>* this_handler)
1081 {
1082 boost_asio_handler_alloc_helpers::deallocate(
1083 pointer, size, this_handler->handler_);
1084 }
1085
1086 template <typename AsyncReadStream, typename Allocator,
1087 typename MatchCondition, typename ReadHandler>
1088 inline bool asio_handler_is_continuation(
1089 read_until_match_op<AsyncReadStream,
1090 Allocator, MatchCondition, ReadHandler>* this_handler)
1091 {
1092 return this_handler->start_ == 0 ? true
1093 : boost_asio_handler_cont_helpers::is_continuation(
1094 this_handler->handler_);
1095 }
1096
1097 template <typename Function, typename AsyncReadStream, typename Allocator,
1098 typename MatchCondition, typename ReadHandler>
1099 inline void asio_handler_invoke(Function& function,
1100 read_until_match_op<AsyncReadStream,
1101 Allocator, MatchCondition, ReadHandler>* this_handler)
1102 {
1103 boost_asio_handler_invoke_helpers::invoke(
1104 function, this_handler->handler_);
1105 }
1106
1107 template <typename Function, typename AsyncReadStream, typename Allocator,
1108 typename MatchCondition, typename ReadHandler>
1109 inline void asio_handler_invoke(const Function& function,
1110 read_until_match_op<AsyncReadStream,
1111 Allocator, MatchCondition, ReadHandler>* this_handler)
1112 {
1113 boost_asio_handler_invoke_helpers::invoke(
1114 function, this_handler->handler_);
1115 }
1116} // namespace detail
1117
1118template <typename AsyncReadStream, typename Allocator,
1119 typename MatchCondition, typename ReadHandler>
1120BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
1121 void (boost::system::error_code, std::size_t))
1122async_read_until(AsyncReadStream& s,
1123 boost::asio::basic_streambuf<Allocator>& b,
1124 MatchCondition match_condition, BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
1125 typename enable_if<is_match_condition<MatchCondition>::value>::type*)
1126{
1127 // If you get an error on the following line it means that your handler does
1128 // not meet the documented type requirements for a ReadHandler.
1129 BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
1130
1131 detail::async_result_init<
1132 ReadHandler, void (boost::system::error_code, std::size_t)> init(
1133 BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
1134
1135 detail::read_until_match_op<AsyncReadStream, Allocator,
1136 MatchCondition, BOOST_ASIO_HANDLER_TYPE(ReadHandler,
1137 void (boost::system::error_code, std::size_t))>(
1138 s, b, match_condition, init.handler)(
1139 boost::system::error_code(), 0, 1);
1140
1141 return init.result.get();
1142}
1143
1144} // namespace asio
1145} // namespace boost
1146
1147#include <boost/asio/detail/pop_options.hpp>
1148
1149#endif // BOOST_ASIO_IMPL_READ_UNTIL_HPP
1150