100.00% Lines (2/2) 100.00% Functions (1/1)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/capy 7   // Official repository: https://github.com/cppalliance/capy
8   // 8   //
9   9  
10   #ifndef BOOST_CAPY_WRITE_HPP 10   #ifndef BOOST_CAPY_WRITE_HPP
11   #define BOOST_CAPY_WRITE_HPP 11   #define BOOST_CAPY_WRITE_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/io_task.hpp> 14   #include <boost/capy/io_task.hpp>
15   #include <boost/capy/buffers.hpp> 15   #include <boost/capy/buffers.hpp>
16   #include <boost/capy/buffers/buffer_slice.hpp> 16   #include <boost/capy/buffers/buffer_slice.hpp>
17   #include <boost/capy/concept/write_stream.hpp> 17   #include <boost/capy/concept/write_stream.hpp>
18   #include <system_error> 18   #include <system_error>
19   19  
20   #include <cstddef> 20   #include <cstddef>
21   21  
22   namespace boost { 22   namespace boost {
23   namespace capy { 23   namespace capy {
24   24  
25   /** Write an entire buffer sequence to a stream. 25   /** Write an entire buffer sequence to a stream.
26   26  
27   @par Await-effects 27   @par Await-effects
28   28  
29   Writes the contents of `buffers` to `stream` via awaiting 29   Writes the contents of `buffers` to `stream` via awaiting
30   `stream.write_some` with consecutive portions of data from `buffers` 30   `stream.write_some` with consecutive portions of data from `buffers`
31   until: 31   until:
32   32  
33   @li either the full content of @c buffers is processed, 33   @li either the full content of @c buffers is processed,
34   @li or a contingency occurs. 34   @li or a contingency occurs.
35   35  
36   If `buffer_size(buffers) == 0` then no awaiting `stream.write_some` 36   If `buffer_size(buffers) == 0` then no awaiting `stream.write_some`
37   is performed. This is not a contingency. 37   is performed. This is not a contingency.
38   38  
39   39  
40   @par Await-returns 40   @par Await-returns
41   41  
42   An object of type `io_result<std::size_t>` destructuring as `[ec, n]`. 42   An object of type `io_result<std::size_t>` destructuring as `[ec, n]`.
43   43  
44   Upon a contingency, `n` represents the number of bytes written 44   Upon a contingency, `n` represents the number of bytes written
45   so far. 45   so far.
46   46  
47   Otherwise `n` represents the number of bytes written. 47   Otherwise `n` represents the number of bytes written.
48   48  
49   Contingencies: 49   Contingencies:
50   50  
51 - @li The first contingency reported from 51 + @li The first contingency reported from awaiting @c stream.write_some
52 - awaiting @c stream.write_some . 52 + while not all bytes have been written. A contingency that accompanies
  53 + the write which transfers the last bytes is not reported: a completed
  54 + transfer is a success.
53   55  
54   Notable conditions: 56   Notable conditions:
55   57  
56   @li @c cond::canceled — Operation was cancelled, 58   @li @c cond::canceled — Operation was cancelled,
57   @li @c std::errc::broken_pipe — Peer closed connection. 59   @li @c std::errc::broken_pipe — Peer closed connection.
58   60  
59   61  
60   @par Await-postcondition 62   @par Await-postcondition
61   63  
62 - `ec || n == buffer_size(buffers)`. 64 + If `n == buffer_size(buffers)` the transfer completed and `ec` is
  65 + success; otherwise `ec` is set.
63   66  
64   67  
65   @param stream The stream to write to. If the lifetime of `stream` ends 68   @param stream The stream to write to. If the lifetime of `stream` ends
66   before the coroutine finishes, the behavior is undefined. 69   before the coroutine finishes, the behavior is undefined.
67   70  
68   @param buffers The buffer sequence to write. If the lifetime of the buffer 71   @param buffers The buffer sequence to write. If the lifetime of the buffer
69   sequence represented by `buffers` ends 72   sequence represented by `buffers` ends
70   before the coroutine finishes, the behavior is undefined. 73   before the coroutine finishes, the behavior is undefined.
71   74  
72   @par Remarks 75   @par Remarks
73   76  
74   Supports _IoAwaitable cancellation_. 77   Supports _IoAwaitable cancellation_.
75   78  
76   @par Example 79   @par Example
77   80  
78   @code 81   @code
79   capy::task<> send_response(capy::WriteStream auto& stream, std::string_view body) 82   capy::task<> send_response(capy::WriteStream auto& stream, std::string_view body)
80   { 83   {
81   auto [ec, n] = co_await capy::write(stream, capy::make_buffer(body)); 84   auto [ec, n] = co_await capy::write(stream, capy::make_buffer(body));
82   if (ec) 85   if (ec)
83   throw std::system_error(ec); 86   throw std::system_error(ec);
84   87  
85   // All bytes written successfully 88   // All bytes written successfully
86   } 89   }
87   @endcode 90   @endcode
88   91  
89   @see WriteStream, ConstBufferSequence, IoAwaitable, io_result, cond. 92   @see WriteStream, ConstBufferSequence, IoAwaitable, io_result, cond.
90   */ 93   */
91   template <WriteStream S, ConstBufferSequence CB> 94   template <WriteStream S, ConstBufferSequence CB>
HITCBC 92   50 auto write(S& stream, CB buffers) -> io_task<std::size_t> 95   52 auto write(S& stream, CB buffers) -> io_task<std::size_t>
93   { 96   {
94   auto consuming = buffer_slice(buffers); 97   auto consuming = buffer_slice(buffers);
95   std::size_t const total_size = buffer_size(buffers); 98   std::size_t const total_size = buffer_size(buffers);
96   std::size_t total_written = 0; 99   std::size_t total_written = 0;
97   100  
98   while(total_written < total_size) 101   while(total_written < total_size)
99   { 102   {
100   auto [ec, n] = co_await stream.write_some(consuming.data()); 103   auto [ec, n] = co_await stream.write_some(consuming.data());
101   consuming.remove_prefix(n); 104   consuming.remove_prefix(n);
102   total_written += n; 105   total_written += n;
103 - if(ec) 106 + // A contingency that still completed the transfer is a success:
  107 + // report it only when not all bytes were written.
  108 + if(ec && total_written < total_size)
104   co_return {ec, total_written}; 109   co_return {ec, total_written};
105   } 110   }
106   111  
107   co_return {{}, total_written}; 112   co_return {{}, total_written};
HITCBC 108   100 } 113   104 }
109   114  
110   } // namespace capy 115   } // namespace capy
111   } // namespace boost 116   } // namespace boost
112   117  
113   #endif 118   #endif