1 //
2 // impl/write.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 ASIO_IMPL_WRITE_HPP
12 #define ASIO_IMPL_WRITE_HPP
13 
14 
15 #include "asio/buffer.hpp"
16 #include "asio/completion_condition.hpp"
17 #include "asio/detail/array_fwd.hpp"
18 #include "asio/detail/base_from_completion_cond.hpp"
19 #include "asio/detail/bind_handler.hpp"
20 #include "asio/detail/consuming_buffers.hpp"
21 #include "asio/detail/dependent_type.hpp"
22 #include "asio/detail/handler_alloc_helpers.hpp"
23 #include "asio/detail/handler_cont_helpers.hpp"
24 #include "asio/detail/handler_invoke_helpers.hpp"
25 #include "asio/detail/handler_type_requirements.hpp"
26 #include "asio/detail/throw_error.hpp"
27 
28 #include "asio/detail/push_options.hpp"
29 
30 namespace asio {
31 
32 template <typename SyncWriteStream, typename ConstBufferSequence,
33     typename CompletionCondition>
34 std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers,
35     CompletionCondition completion_condition, asio::error_code& ec)
36 {
37   ec = asio::error_code();
38   asio::detail::consuming_buffers<
39     const_buffer, ConstBufferSequence> tmp(buffers);
40   std::size_t total_transferred = 0;
41   tmp.prepare(detail::adapt_completion_condition_result(
42         completion_condition(ec, total_transferred)));
43   while (tmp.begin() != tmp.end())
44   {
45     std::size_t bytes_transferred = s.write_some(tmp, ec);
46     tmp.consume(bytes_transferred);
47     total_transferred += bytes_transferred;
48     tmp.prepare(detail::adapt_completion_condition_result(
49           completion_condition(ec, total_transferred)));
50   }
51   return total_transferred;
52 }
53 
54 template <typename SyncWriteStream, typename ConstBufferSequence>
55 inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers)
56 {
57   asio::error_code ec;
58   std::size_t bytes_transferred = write(s, buffers, transfer_all(), ec);
59   asio::detail::throw_error(ec, "write");
60   return bytes_transferred;
61 }
62 
63 template <typename SyncWriteStream, typename ConstBufferSequence>
64 inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers,
65     asio::error_code& ec)
66 {
67   return write(s, buffers, transfer_all(), ec);
68 }
69 
70 template <typename SyncWriteStream, typename ConstBufferSequence,
71     typename CompletionCondition>
72 inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers,
73     CompletionCondition completion_condition)
74 {
75   asio::error_code ec;
76   std::size_t bytes_transferred = write(s, buffers, completion_condition, ec);
77   asio::detail::throw_error(ec, "write");
78   return bytes_transferred;
79 }
80 
81 
82 namespace detail
83 {
84   template <typename AsyncWriteStream, typename ConstBufferSequence,
85       typename CompletionCondition, typename WriteHandler>
86   class write_op
87     : detail::base_from_completion_cond<CompletionCondition>
88   {
89   public:
90     write_op(AsyncWriteStream& stream, const ConstBufferSequence& buffers,
91         CompletionCondition completion_condition, WriteHandler& handler)
92       : detail::base_from_completion_cond<
93           CompletionCondition>(completion_condition),
94         stream_(stream),
95         buffers_(buffers),
96         start_(0),
97         total_transferred_(0),
98         handler_(ASIO_MOVE_CAST(WriteHandler)(handler))
99     {
100     }
101 
102     write_op(const write_op& other)
103       : detail::base_from_completion_cond<CompletionCondition>(other),
104         stream_(other.stream_),
105         buffers_(other.buffers_),
106         start_(other.start_),
107         total_transferred_(other.total_transferred_),
108         handler_(other.handler_)
109     {
110     }
111 
112     write_op(write_op&& other)
113       : detail::base_from_completion_cond<CompletionCondition>(other),
114         stream_(other.stream_),
115         buffers_(other.buffers_),
116         start_(other.start_),
117         total_transferred_(other.total_transferred_),
118         handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_))
119     {
120     }
121 
122     void operator()(const asio::error_code& ec,
123         std::size_t bytes_transferred, int start = 0)
124     {
125       switch (start_ = start)
126       {
127         case 1:
128         buffers_.prepare(this->check_for_completion(ec, total_transferred_));
129         for (;;)
130         {
131           stream_.async_write_some(buffers_,
132               ASIO_MOVE_CAST(write_op)(*this));
133           return; default:
134           total_transferred_ += bytes_transferred;
135           buffers_.consume(bytes_transferred);
136           buffers_.prepare(this->check_for_completion(ec, total_transferred_));
137           if ((!ec && bytes_transferred == 0)
138               || buffers_.begin() == buffers_.end())
139             break;
140         }
141 
142         handler_(ec, static_cast<const std::size_t&>(total_transferred_));
143       }
144     }
145 
146   //private:
147     AsyncWriteStream& stream_;
148     asio::detail::consuming_buffers<
149       const_buffer, ConstBufferSequence> buffers_;
150     int start_;
151     std::size_t total_transferred_;
152     WriteHandler handler_;
153   };
154 
155   template <typename AsyncWriteStream,
156       typename CompletionCondition, typename WriteHandler>
157   class write_op<AsyncWriteStream, asio::mutable_buffers_1,
158       CompletionCondition, WriteHandler>
159     : detail::base_from_completion_cond<CompletionCondition>
160   {
161   public:
162     write_op(AsyncWriteStream& stream,
163         const asio::mutable_buffers_1& buffers,
164         CompletionCondition completion_condition,
165         WriteHandler& handler)
166       : detail::base_from_completion_cond<
167           CompletionCondition>(completion_condition),
168         stream_(stream),
169         buffer_(buffers),
170         start_(0),
171         total_transferred_(0),
172         handler_(ASIO_MOVE_CAST(WriteHandler)(handler))
173     {
174     }
175 
176     write_op(const write_op& other)
177       : detail::base_from_completion_cond<CompletionCondition>(other),
178         stream_(other.stream_),
179         buffer_(other.buffer_),
180         start_(other.start_),
181         total_transferred_(other.total_transferred_),
182         handler_(other.handler_)
183     {
184     }
185 
186     write_op(write_op&& other)
187       : detail::base_from_completion_cond<CompletionCondition>(other),
188         stream_(other.stream_),
189         buffer_(other.buffer_),
190         start_(other.start_),
191         total_transferred_(other.total_transferred_),
192         handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_))
193     {
194     }
195 
196     void operator()(const asio::error_code& ec,
197         std::size_t bytes_transferred, int start = 0)
198     {
199       std::size_t n = 0;
200       switch (start_ = start)
201       {
202         case 1:
203         n = this->check_for_completion(ec, total_transferred_);
204         for (;;)
205         {
206           stream_.async_write_some(
207               asio::buffer(buffer_ + total_transferred_, n),
208               ASIO_MOVE_CAST(write_op)(*this));
209           return; default:
210           total_transferred_ += bytes_transferred;
211           if ((!ec && bytes_transferred == 0)
212               || (n = this->check_for_completion(ec, total_transferred_)) == 0
213               || total_transferred_ == asio::buffer_size(buffer_))
214             break;
215         }
216 
217         handler_(ec, static_cast<const std::size_t&>(total_transferred_));
218       }
219     }
220 
221   //private:
222     AsyncWriteStream& stream_;
223     asio::mutable_buffer buffer_;
224     int start_;
225     std::size_t total_transferred_;
226     WriteHandler handler_;
227   };
228 
229   template <typename AsyncWriteStream,
230       typename CompletionCondition, typename WriteHandler>
231   class write_op<AsyncWriteStream, asio::const_buffers_1,
232       CompletionCondition, WriteHandler>
233     : detail::base_from_completion_cond<CompletionCondition>
234   {
235   public:
236     write_op(AsyncWriteStream& stream,
237         const asio::const_buffers_1& buffers,
238         CompletionCondition completion_condition,
239         WriteHandler& handler)
240       : detail::base_from_completion_cond<
241           CompletionCondition>(completion_condition),
242         stream_(stream),
243         buffer_(buffers),
244         start_(0),
245         total_transferred_(0),
246         handler_(ASIO_MOVE_CAST(WriteHandler)(handler))
247     {
248     }
249 
250     write_op(const write_op& other)
251       : detail::base_from_completion_cond<CompletionCondition>(other),
252         stream_(other.stream_),
253         buffer_(other.buffer_),
254         start_(other.start_),
255         total_transferred_(other.total_transferred_),
256         handler_(other.handler_)
257     {
258     }
259 
260     write_op(write_op&& other)
261       : detail::base_from_completion_cond<CompletionCondition>(other),
262         stream_(other.stream_),
263         buffer_(other.buffer_),
264         start_(other.start_),
265         total_transferred_(other.total_transferred_),
266         handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_))
267     {
268     }
269 
270     void operator()(const asio::error_code& ec,
271         std::size_t bytes_transferred, int start = 0)
272     {
273       std::size_t n = 0;
274       switch (start_ = start)
275       {
276         case 1:
277         n = this->check_for_completion(ec, total_transferred_);
278         for (;;)
279         {
280           stream_.async_write_some(
281               asio::buffer(buffer_ + total_transferred_, n),
282               ASIO_MOVE_CAST(write_op)(*this));
283           return; default:
284           total_transferred_ += bytes_transferred;
285           if ((!ec && bytes_transferred == 0)
286               || (n = this->check_for_completion(ec, total_transferred_)) == 0
287               || total_transferred_ == asio::buffer_size(buffer_))
288             break;
289         }
290 
291         handler_(ec, static_cast<const std::size_t&>(total_transferred_));
292       }
293     }
294 
295   //private:
296     AsyncWriteStream& stream_;
297     asio::const_buffer buffer_;
298     int start_;
299     std::size_t total_transferred_;
300     WriteHandler handler_;
301   };
302 
303   template <typename AsyncWriteStream, typename Elem,
304       typename CompletionCondition, typename WriteHandler>
305   class write_op<AsyncWriteStream, boost::array<Elem, 2>,
306       CompletionCondition, WriteHandler>
307     : detail::base_from_completion_cond<CompletionCondition>
308   {
309   public:
310     write_op(AsyncWriteStream& stream, const boost::array<Elem, 2>& buffers,
311         CompletionCondition completion_condition, WriteHandler& handler)
312       : detail::base_from_completion_cond<
313           CompletionCondition>(completion_condition),
314         stream_(stream),
315         buffers_(buffers),
316         start_(0),
317         total_transferred_(0),
318         handler_(ASIO_MOVE_CAST(WriteHandler)(handler))
319     {
320     }
321 
322     write_op(const write_op& other)
323       : detail::base_from_completion_cond<CompletionCondition>(other),
324         stream_(other.stream_),
325         buffers_(other.buffers_),
326         start_(other.start_),
327         total_transferred_(other.total_transferred_),
328         handler_(other.handler_)
329     {
330     }
331 
332     write_op(write_op&& other)
333       : detail::base_from_completion_cond<CompletionCondition>(other),
334         stream_(other.stream_),
335         buffers_(other.buffers_),
336         start_(other.start_),
337         total_transferred_(other.total_transferred_),
338         handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_))
339     {
340     }
341 
342     void operator()(const asio::error_code& ec,
343         std::size_t bytes_transferred, int start = 0)
344     {
345       typename asio::detail::dependent_type<Elem,
346           boost::array<asio::const_buffer, 2> >::type bufs = {{
347         asio::const_buffer(buffers_[0]),
348         asio::const_buffer(buffers_[1]) }};
349       std::size_t buffer_size0 = asio::buffer_size(bufs[0]);
350       std::size_t buffer_size1 = asio::buffer_size(bufs[1]);
351       std::size_t n = 0;
352       switch (start_ = start)
353       {
354         case 1:
355         n = this->check_for_completion(ec, total_transferred_);
356         for (;;)
357         {
358           bufs[0] = asio::buffer(bufs[0] + total_transferred_, n);
359           bufs[1] = asio::buffer(
360               bufs[1] + (total_transferred_ < buffer_size0
361                 ? 0 : total_transferred_ - buffer_size0),
362               n - asio::buffer_size(bufs[0]));
363           stream_.async_write_some(bufs, ASIO_MOVE_CAST(write_op)(*this));
364           return; default:
365           total_transferred_ += bytes_transferred;
366           if ((!ec && bytes_transferred == 0)
367               || (n = this->check_for_completion(ec, total_transferred_)) == 0
368               || total_transferred_ == buffer_size0 + buffer_size1)
369             break;
370         }
371 
372         handler_(ec, static_cast<const std::size_t&>(total_transferred_));
373       }
374     }
375 
376   //private:
377     AsyncWriteStream& stream_;
378     boost::array<Elem, 2> buffers_;
379     int start_;
380     std::size_t total_transferred_;
381     WriteHandler handler_;
382   };
383 
384 
385   template <typename AsyncWriteStream, typename Elem,
386       typename CompletionCondition, typename WriteHandler>
387   class write_op<AsyncWriteStream, std::array<Elem, 2>,
388       CompletionCondition, WriteHandler>
389     : detail::base_from_completion_cond<CompletionCondition>
390   {
391   public:
392     write_op(AsyncWriteStream& stream, const std::array<Elem, 2>& buffers,
393         CompletionCondition completion_condition, WriteHandler& handler)
394       : detail::base_from_completion_cond<
395           CompletionCondition>(completion_condition),
396         stream_(stream),
397         buffers_(buffers),
398         start_(0),
399         total_transferred_(0),
400         handler_(ASIO_MOVE_CAST(WriteHandler)(handler))
401     {
402     }
403 
404     write_op(const write_op& other)
405       : detail::base_from_completion_cond<CompletionCondition>(other),
406         stream_(other.stream_),
407         buffers_(other.buffers_),
408         start_(other.start_),
409         total_transferred_(other.total_transferred_),
410         handler_(other.handler_)
411     {
412     }
413 
414     write_op(write_op&& other)
415       : detail::base_from_completion_cond<CompletionCondition>(other),
416         stream_(other.stream_),
417         buffers_(other.buffers_),
418         start_(other.start_),
419         total_transferred_(other.total_transferred_),
420         handler_(ASIO_MOVE_CAST(WriteHandler)(other.handler_))
421     {
422     }
423 
424     void operator()(const asio::error_code& ec,
425         std::size_t bytes_transferred, int start = 0)
426     {
427       typename asio::detail::dependent_type<Elem,
428           std::array<asio::const_buffer, 2> >::type bufs = {{
429         asio::const_buffer(buffers_[0]),
430         asio::const_buffer(buffers_[1]) }};
431       std::size_t buffer_size0 = asio::buffer_size(bufs[0]);
432       std::size_t buffer_size1 = asio::buffer_size(bufs[1]);
433       std::size_t n = 0;
434       switch (start_ = start)
435       {
436         case 1:
437         n = this->check_for_completion(ec, total_transferred_);
438         for (;;)
439         {
440           bufs[0] = asio::buffer(bufs[0] + total_transferred_, n);
441           bufs[1] = asio::buffer(
442               bufs[1] + (total_transferred_ < buffer_size0
443                 ? 0 : total_transferred_ - buffer_size0),
444               n - asio::buffer_size(bufs[0]));
445           stream_.async_write_some(bufs, ASIO_MOVE_CAST(write_op)(*this));
446           return; default:
447           total_transferred_ += bytes_transferred;
448           if ((!ec && bytes_transferred == 0)
449               || (n = this->check_for_completion(ec, total_transferred_)) == 0
450               || total_transferred_ == buffer_size0 + buffer_size1)
451             break;
452         }
453 
454         handler_(ec, static_cast<const std::size_t&>(total_transferred_));
455       }
456     }
457 
458   //private:
459     AsyncWriteStream& stream_;
460     std::array<Elem, 2> buffers_;
461     int start_;
462     std::size_t total_transferred_;
463     WriteHandler handler_;
464   };
465 
466 
467   template <typename AsyncWriteStream, typename ConstBufferSequence,
468       typename CompletionCondition, typename WriteHandler>
469   inline void* asio_handler_allocate(std::size_t size,
470       write_op<AsyncWriteStream, ConstBufferSequence,
471         CompletionCondition, WriteHandler>* this_handler)
472   {
473     return asio_handler_alloc_helpers::allocate(
474         size, this_handler->handler_);
475   }
476 
477   template <typename AsyncWriteStream, typename ConstBufferSequence,
478       typename CompletionCondition, typename WriteHandler>
479   inline void asio_handler_deallocate(void* pointer, std::size_t size,
480       write_op<AsyncWriteStream, ConstBufferSequence,
481         CompletionCondition, WriteHandler>* this_handler)
482   {
483     asio_handler_alloc_helpers::deallocate(
484         pointer, size, this_handler->handler_);
485   }
486 
487   template <typename AsyncWriteStream, typename ConstBufferSequence,
488       typename CompletionCondition, typename WriteHandler>
489   inline bool asio_handler_is_continuation(
490       write_op<AsyncWriteStream, ConstBufferSequence,
491         CompletionCondition, WriteHandler>* this_handler)
492   {
493     return this_handler->start_ == 0 ? true
494       : asio_handler_cont_helpers::is_continuation(
495           this_handler->handler_);
496   }
497 
498   template <typename Function, typename AsyncWriteStream,
499       typename ConstBufferSequence, typename CompletionCondition,
500       typename WriteHandler>
501   inline void asio_handler_invoke(Function& function,
502       write_op<AsyncWriteStream, ConstBufferSequence,
503         CompletionCondition, WriteHandler>* this_handler)
504   {
505     asio_handler_invoke_helpers::invoke(
506         function, this_handler->handler_);
507   }
508 
509   template <typename Function, typename AsyncWriteStream,
510       typename ConstBufferSequence, typename CompletionCondition,
511       typename WriteHandler>
512   inline void asio_handler_invoke(const Function& function,
513       write_op<AsyncWriteStream, ConstBufferSequence,
514         CompletionCondition, WriteHandler>* this_handler)
515   {
516     asio_handler_invoke_helpers::invoke(
517         function, this_handler->handler_);
518   }
519 } // namespace detail
520 
521 template <typename AsyncWriteStream, typename ConstBufferSequence,
522   typename CompletionCondition, typename WriteHandler>
523 inline ASIO_INITFN_RESULT_TYPE(WriteHandler,
524     void (asio::error_code, std::size_t))
525 async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers,
526     CompletionCondition completion_condition,
527     ASIO_MOVE_ARG(WriteHandler) handler)
528 {
529   // If you get an error on the following line it means that your handler does
530   // not meet the documented type requirements for a WriteHandler.
531   ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
532 
533   detail::async_result_init<
534     WriteHandler, void (asio::error_code, std::size_t)> init(
535       ASIO_MOVE_CAST(WriteHandler)(handler));
536 
537   detail::write_op<AsyncWriteStream, ConstBufferSequence,
538     CompletionCondition, ASIO_HANDLER_TYPE(
539       WriteHandler, void (asio::error_code, std::size_t))>(
540         s, buffers, completion_condition, init.handler)(
541           asio::error_code(), 0, 1);
542 
543   return init.result.get();
544 }
545 
546 template <typename AsyncWriteStream, typename ConstBufferSequence,
547     typename WriteHandler>
548 inline ASIO_INITFN_RESULT_TYPE(WriteHandler,
549     void (asio::error_code, std::size_t))
550 async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers,
551     ASIO_MOVE_ARG(WriteHandler) handler)
552 {
553   // If you get an error on the following line it means that your handler does
554   // not meet the documented type requirements for a WriteHandler.
555   ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
556 
557   detail::async_result_init<
558     WriteHandler, void (asio::error_code, std::size_t)> init(
559       ASIO_MOVE_CAST(WriteHandler)(handler));
560 
561   detail::write_op<AsyncWriteStream, ConstBufferSequence,
562     detail::transfer_all_t, ASIO_HANDLER_TYPE(
563       WriteHandler, void (asio::error_code, std::size_t))>(
564         s, buffers, transfer_all(), init.handler)(
565           asio::error_code(), 0, 1);
566 
567   return init.result.get();
568 }
569 
570 
571 } // namespace asio
572 
573 #include "asio/detail/pop_options.hpp"
574 
575 #endif // ASIO_IMPL_WRITE_HPP
576