100.00% Lines (37/37) 100.00% Functions (9/9)
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   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/capy 8   // Official repository: https://github.com/cppalliance/capy
9   // 9   //
10   10  
11   #ifndef BOOST_CAPY_TEST_BUFFER_SOURCE_HPP 11   #ifndef BOOST_CAPY_TEST_BUFFER_SOURCE_HPP
12   #define BOOST_CAPY_TEST_BUFFER_SOURCE_HPP 12   #define BOOST_CAPY_TEST_BUFFER_SOURCE_HPP
13   13  
14   #include <boost/capy/detail/config.hpp> 14   #include <boost/capy/detail/config.hpp>
15   #include <boost/capy/buffers.hpp> 15   #include <boost/capy/buffers.hpp>
16   #include <boost/capy/buffers/make_buffer.hpp> 16   #include <boost/capy/buffers/make_buffer.hpp>
17   #include <coroutine> 17   #include <coroutine>
18   #include <boost/capy/error.hpp> 18   #include <boost/capy/error.hpp>
19   #include <boost/capy/ex/io_env.hpp> 19   #include <boost/capy/ex/io_env.hpp>
20   #include <boost/capy/io_result.hpp> 20   #include <boost/capy/io_result.hpp>
21   #include <boost/capy/test/fuse.hpp> 21   #include <boost/capy/test/fuse.hpp>
22   22  
23   #include <algorithm> 23   #include <algorithm>
24   #include <span> 24   #include <span>
25   #include <string> 25   #include <string>
26   #include <string_view> 26   #include <string_view>
27   27  
28   namespace boost { 28   namespace boost {
29   namespace capy { 29   namespace capy {
30   namespace test { 30   namespace test {
31   31  
32   /** A mock buffer source for testing pull (BufferSource) operations. 32   /** A mock buffer source for testing pull (BufferSource) operations.
33   33  
34   Use this to verify code that transfers data from a buffer source to 34   Use this to verify code that transfers data from a buffer source to
35   a sink without needing real I/O. Call @ref provide to supply data, 35   a sink without needing real I/O. Call @ref provide to supply data,
36   then @ref pull to retrieve buffer descriptors. The associated 36   then @ref pull to retrieve buffer descriptors. The associated
37   @ref fuse enables error injection at controlled points. 37   @ref fuse enables error injection at controlled points.
38   38  
39   This class satisfies the @ref BufferSource concept by providing 39   This class satisfies the @ref BufferSource concept by providing
40   a pull interface that fills an array of buffer descriptors and 40   a pull interface that fills an array of buffer descriptors and
41   a consume interface to indicate bytes used. 41   a consume interface to indicate bytes used.
42   42  
43   @par Thread Safety 43   @par Thread Safety
44   Not thread-safe. 44   Not thread-safe.
45   45  
46   @par Example 46   @par Example
47   @code 47   @code
48   fuse f; 48   fuse f;
49   buffer_source bs( f ); 49   buffer_source bs( f );
50   bs.provide( "Hello, " ); 50   bs.provide( "Hello, " );
51   bs.provide( "World!" ); 51   bs.provide( "World!" );
52   52  
53   auto r = f.armed( [&]( fuse& ) -> task<void> { 53   auto r = f.armed( [&]( fuse& ) -> task<void> {
54   const_buffer arr[16]; 54   const_buffer arr[16];
55   auto [ec, bufs] = co_await bs.pull( arr ); 55   auto [ec, bufs] = co_await bs.pull( arr );
56   if( ec ) 56   if( ec )
57   co_return; 57   co_return;
58   // bufs contains buffer descriptors 58   // bufs contains buffer descriptors
59   std::size_t n = buffer_size( bufs ); 59   std::size_t n = buffer_size( bufs );
60   bs.consume( n ); 60   bs.consume( n );
61   } ); 61   } );
62   @endcode 62   @endcode
63   63  
64   @see fuse, BufferSource 64   @see fuse, BufferSource
65   */ 65   */
66   class buffer_source 66   class buffer_source
67   { 67   {
68   fuse f_; 68   fuse f_;
69   std::string data_; 69   std::string data_;
70   std::size_t pos_ = 0; 70   std::size_t pos_ = 0;
71   std::size_t max_pull_size_; 71   std::size_t max_pull_size_;
72   72  
73   public: 73   public:
74   /** Construct a buffer source. 74   /** Construct a buffer source.
75   75  
76   @param f The fuse used to inject errors during pulls. 76   @param f The fuse used to inject errors during pulls.
77   77  
78   @param max_pull_size Maximum bytes returned per pull. 78   @param max_pull_size Maximum bytes returned per pull.
79   Use to simulate chunked delivery. 79   Use to simulate chunked delivery.
80   */ 80   */
HITCBC 81   377 explicit buffer_source( 81   377 explicit buffer_source(
82   fuse f = {}, 82   fuse f = {},
83   std::size_t max_pull_size = std::size_t(-1)) noexcept 83   std::size_t max_pull_size = std::size_t(-1)) noexcept
HITCBC 84   377 : f_(std::move(f)) 84   377 : f_(std::move(f))
HITCBC 85   377 , max_pull_size_(max_pull_size) 85   377 , max_pull_size_(max_pull_size)
86   { 86   {
HITCBC 87   377 } 87   377 }
88   88  
89   /** Append data to be returned by subsequent pulls. 89   /** Append data to be returned by subsequent pulls.
90   90  
91   Multiple calls accumulate data that @ref pull returns. 91   Multiple calls accumulate data that @ref pull returns.
92   92  
93   @param sv The data to append. 93   @param sv The data to append.
94   */ 94   */
95   void 95   void
HITCBC 96   389 provide(std::string_view sv) 96   389 provide(std::string_view sv)
97   { 97   {
HITCBC 98   389 data_.append(sv); 98   389 data_.append(sv);
HITCBC 99   389 } 99   389 }
100   100  
101   /// Clear all data and reset the read position. 101   /// Clear all data and reset the read position.
102   void 102   void
HITCBC 103   6 clear() noexcept 103   6 clear() noexcept
104   { 104   {
HITCBC 105   6 data_.clear(); 105   6 data_.clear();
HITCBC 106   6 pos_ = 0; 106   6 pos_ = 0;
HITCBC 107   6 } 107   6 }
108   108  
109   /// Return the number of bytes available for pulling. 109   /// Return the number of bytes available for pulling.
110   std::size_t 110   std::size_t
HITCBC 111   18 available() const noexcept 111   18 available() const noexcept
112   { 112   {
HITCBC 113   18 return data_.size() - pos_; 113   18 return data_.size() - pos_;
114   } 114   }
115   115  
116   /** Consume bytes from the source. 116   /** Consume bytes from the source.
117   117  
118   Advances the internal read position by the specified number 118   Advances the internal read position by the specified number
119   of bytes. The next call to @ref pull returns data starting 119   of bytes. The next call to @ref pull returns data starting
120   after the consumed bytes. 120   after the consumed bytes.
121   121  
122   @param n The number of bytes to consume. Must not exceed the 122   @param n The number of bytes to consume. Must not exceed the
123   total size of buffers returned by the previous @ref pull. 123   total size of buffers returned by the previous @ref pull.
124   */ 124   */
125   void 125   void
HITCBC 126   319 consume(std::size_t n) noexcept 126   319 consume(std::size_t n) noexcept
127   { 127   {
HITCBC 128   319 pos_ += n; 128   319 pos_ += n;
HITCBC 129   319 } 129   319 }
130   130  
131   /** Pull buffer data from the source. 131   /** Pull buffer data from the source.
132   132  
133   Fills the provided span with buffer descriptors pointing to 133   Fills the provided span with buffer descriptors pointing to
134   internal data starting from the current unconsumed position. 134   internal data starting from the current unconsumed position.
135   Returns a span of filled buffers. When no data remains, 135   Returns a span of filled buffers. When no data remains,
136   returns an empty span to signal completion. 136   returns an empty span to signal completion.
137   137  
138   Calling pull multiple times without intervening @ref consume 138   Calling pull multiple times without intervening @ref consume
139   returns the same data. Use consume to advance past processed 139   returns the same data. Use consume to advance past processed
140   bytes. 140   bytes.
141   141  
142   @param dest Span of const_buffer to fill. 142   @param dest Span of const_buffer to fill.
143   143  
144   @return An awaitable that await-returns `(error_code,std::span<const_buffer>)`. 144   @return An awaitable that await-returns `(error_code,std::span<const_buffer>)`.
145   145  
146   @par Cancellation 146   @par Cancellation
147   If the environment's stop token has been requested, the pull 147   If the environment's stop token has been requested, the pull
148   completes immediately with `error::canceled` and an empty span. 148   completes immediately with `error::canceled` and an empty span.
149   149  
150   @see consume, fuse 150   @see consume, fuse
151   */ 151   */
152   auto 152   auto
HITCBC 153   657 pull(std::span<const_buffer> dest) 153   657 pull(std::span<const_buffer> dest)
154   { 154   {
155   struct awaitable 155   struct awaitable
156   { 156   {
157   buffer_source* self_; 157   buffer_source* self_;
158   std::span<const_buffer> dest_; 158   std::span<const_buffer> dest_;
159   bool canceled_ = false; 159   bool canceled_ = false;
160   160  
HITCBC 161   657 bool await_ready() const noexcept { return false; } 161   657 bool await_ready() const noexcept { return false; }
162   162  
163   // The operation completes synchronously, but await_suspend is 163   // The operation completes synchronously, but await_suspend is
164   // the only place io_env is delivered (the promise's 164   // the only place io_env is delivered (the promise's
165   // transform_awaiter forwards it here). Returning false means 165   // transform_awaiter forwards it here). Returning false means
166   // the coroutine does not actually suspend; it resumes 166   // the coroutine does not actually suspend; it resumes
167   // immediately, having observed the stop token. See io_env, 167   // immediately, having observed the stop token. See io_env,
168   // IoAwaitable. 168   // IoAwaitable.
169   bool 169   bool
HITCBC 170   657 await_suspend( 170   657 await_suspend(
171   std::coroutine_handle<>, 171   std::coroutine_handle<>,
172   io_env const* env) noexcept 172   io_env const* env) noexcept
173   { 173   {
HITCBC 174   657 canceled_ = env->stop_token.stop_requested(); 174   657 canceled_ = env->stop_token.stop_requested();
HITCBC 175   657 return false; 175   657 return false;
176   } 176   }
177   177  
178   io_result<std::span<const_buffer>> 178   io_result<std::span<const_buffer>>
HITCBC 179   657 await_resume() 179   657 await_resume()
180   { 180   {
HITCBC 181   657 if(canceled_) 181   657 if(canceled_)
HITCBC 182   1 return {error::canceled, {}}; 182   1 return {error::canceled, {}};
183   183  
HITCBC 184   656 auto ec = self_->f_.maybe_fail(); 184   656 auto ec = self_->f_.maybe_fail();
HITCBC 185   546 if(ec) 185   546 if(ec)
HITCBC 186   110 return {ec, {}}; 186   110 return {ec, {}};
187   187  
HITCBC 188   436 if(self_->pos_ >= self_->data_.size()) 188   436 if(self_->pos_ >= self_->data_.size())
HITCBC 189   72 return {error::eof, {}}; 189   72 return {error::eof, {}};
190   190  
HITCBC 191   364 std::size_t avail = self_->data_.size() - self_->pos_; 191   364 std::size_t avail = self_->data_.size() - self_->pos_;
HITCBC 192   364 std::size_t to_return = (std::min)(avail, self_->max_pull_size_); 192   364 std::size_t to_return = (std::min)(avail, self_->max_pull_size_);
193   193  
HITCBC 194   364 if(dest_.empty()) 194   364 if(dest_.empty())
HITCBC 195   2 return {{}, {}}; 195   2 return {{}, {}};
196   196  
197   // Fill a single buffer descriptor 197   // Fill a single buffer descriptor
HITCBC 198   362 dest_[0] = make_buffer( 198   362 dest_[0] = make_buffer(
HITCBC 199   362 self_->data_.data() + self_->pos_, 199   362 self_->data_.data() + self_->pos_,
200   to_return); 200   to_return);
201   201  
HITCBC 202   362 return {{}, dest_.first(1)}; 202   362 return {{}, dest_.first(1)};
203   } 203   }
204   }; 204   };
HITCBC 205   657 return awaitable{this, dest}; 205   657 return awaitable{this, dest};
206   } 206   }
207   }; 207   };
208   208  
209   } // test 209   } // test
210   } // capy 210   } // capy
211   } // boost 211   } // boost
212   212  
213   #endif 213   #endif