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 | |
34 | namespace boost { |
35 | namespace asio { |
36 | |
37 | template <typename SyncReadStream, typename Allocator> |
38 | inline 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 | |
47 | template <typename SyncReadStream, typename Allocator> |
48 | std::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 | |
93 | template <typename SyncReadStream, typename Allocator> |
94 | inline 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 | |
103 | namespace 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 | |
138 | template <typename SyncReadStream, typename Allocator> |
139 | std::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 | |
195 | template <typename SyncReadStream, typename Allocator> |
196 | inline 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 | |
205 | template <typename SyncReadStream, typename Allocator> |
206 | std::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 | |
264 | template <typename SyncReadStream, typename Allocator, typename MatchCondition> |
265 | std::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 | |
316 | template <typename SyncReadStream, typename Allocator, typename MatchCondition> |
317 | inline 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 | |
327 | namespace 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 | |
495 | template <typename AsyncReadStream, typename Allocator, typename ReadHandler> |
496 | BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, |
497 | void (boost::system::error_code, std::size_t)) |
498 | async_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 | |
519 | namespace 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 | |
698 | template <typename AsyncReadStream, typename Allocator, typename ReadHandler> |
699 | BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, |
700 | void (boost::system::error_code, std::size_t)) |
701 | async_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 | |
724 | namespace 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 | |
910 | template <typename AsyncReadStream, typename Allocator, typename ReadHandler> |
911 | BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, |
912 | void (boost::system::error_code, std::size_t)) |
913 | async_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 | |
936 | namespace 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 | |
1118 | template <typename AsyncReadStream, typename Allocator, |
1119 | typename MatchCondition, typename ReadHandler> |
1120 | BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, |
1121 | void (boost::system::error_code, std::size_t)) |
1122 | async_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 | |