94.12% Lines (32/34) 88.89% Functions (8/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   // 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_TEST_BUFFER_SOURCE_HPP 10   #ifndef BOOST_CAPY_TEST_BUFFER_SOURCE_HPP
11   #define BOOST_CAPY_TEST_BUFFER_SOURCE_HPP 11   #define BOOST_CAPY_TEST_BUFFER_SOURCE_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/buffers.hpp> 14   #include <boost/capy/buffers.hpp>
15   #include <boost/capy/buffers/make_buffer.hpp> 15   #include <boost/capy/buffers/make_buffer.hpp>
16   #include <coroutine> 16   #include <coroutine>
17   #include <boost/capy/error.hpp> 17   #include <boost/capy/error.hpp>
18   #include <boost/capy/ex/io_env.hpp> 18   #include <boost/capy/ex/io_env.hpp>
19   #include <boost/capy/io_result.hpp> 19   #include <boost/capy/io_result.hpp>
20   #include <boost/capy/test/fuse.hpp> 20   #include <boost/capy/test/fuse.hpp>
21   21  
22   #include <algorithm> 22   #include <algorithm>
23   #include <string> 23   #include <string>
24   #include <string_view> 24   #include <string_view>
25   25  
26   namespace boost { 26   namespace boost {
27   namespace capy { 27   namespace capy {
28   namespace test { 28   namespace test {
29   29  
30   /** A mock buffer source for testing push operations. 30   /** A mock buffer source for testing push operations.
31   31  
32   Use this to verify code that transfers data from a buffer source to 32   Use this to verify code that transfers data from a buffer source to
33   a sink without needing real I/O. Call @ref provide to supply data, 33   a sink without needing real I/O. Call @ref provide to supply data,
34   then @ref pull to retrieve buffer descriptors. The associated 34   then @ref pull to retrieve buffer descriptors. The associated
35   @ref fuse enables error injection at controlled points. 35   @ref fuse enables error injection at controlled points.
36   36  
37   This class satisfies the @ref BufferSource concept by providing 37   This class satisfies the @ref BufferSource concept by providing
38   a pull interface that fills an array of buffer descriptors and 38   a pull interface that fills an array of buffer descriptors and
39   a consume interface to indicate bytes used. 39   a consume interface to indicate bytes used.
40   40  
41   @par Thread Safety 41   @par Thread Safety
42   Not thread-safe. 42   Not thread-safe.
43   43  
44   @par Example 44   @par Example
45   @code 45   @code
46   fuse f; 46   fuse f;
47   buffer_source bs( f ); 47   buffer_source bs( f );
48   bs.provide( "Hello, " ); 48   bs.provide( "Hello, " );
49   bs.provide( "World!" ); 49   bs.provide( "World!" );
50   50  
51   auto r = f.armed( [&]( fuse& ) -> task<void> { 51   auto r = f.armed( [&]( fuse& ) -> task<void> {
52   const_buffer arr[16]; 52   const_buffer arr[16];
53   auto [ec, bufs] = co_await bs.pull( arr ); 53   auto [ec, bufs] = co_await bs.pull( arr );
54   if( ec ) 54   if( ec )
55   co_return; 55   co_return;
56   // bufs contains buffer descriptors 56   // bufs contains buffer descriptors
57   std::size_t n = buffer_size( bufs ); 57   std::size_t n = buffer_size( bufs );
58   bs.consume( n ); 58   bs.consume( n );
59   } ); 59   } );
60   @endcode 60   @endcode
61   61  
62   @see fuse, BufferSource 62   @see fuse, BufferSource
63   */ 63   */
64   class buffer_source 64   class buffer_source
65   { 65   {
66   fuse f_; 66   fuse f_;
67   std::string data_; 67   std::string data_;
68   std::size_t pos_ = 0; 68   std::size_t pos_ = 0;
69   std::size_t max_pull_size_; 69   std::size_t max_pull_size_;
70   70  
71   public: 71   public:
72   /** Construct a buffer source. 72   /** Construct a buffer source.
73   73  
74   @param f The fuse used to inject errors during pulls. 74   @param f The fuse used to inject errors during pulls.
75   75  
76   @param max_pull_size Maximum bytes returned per pull. 76   @param max_pull_size Maximum bytes returned per pull.
77   Use to simulate chunked delivery. 77   Use to simulate chunked delivery.
78   */ 78   */
HITCBC 79   376 explicit buffer_source( 79   376 explicit buffer_source(
80   fuse f = {}, 80   fuse f = {},
81   std::size_t max_pull_size = std::size_t(-1)) noexcept 81   std::size_t max_pull_size = std::size_t(-1)) noexcept
HITCBC 82   376 : f_(std::move(f)) 82   376 : f_(std::move(f))
HITCBC 83   376 , max_pull_size_(max_pull_size) 83   376 , max_pull_size_(max_pull_size)
84   { 84   {
HITCBC 85   376 } 85   376 }
86   86  
87   /** Append data to be returned by subsequent pulls. 87   /** Append data to be returned by subsequent pulls.
88   88  
89   Multiple calls accumulate data that @ref pull returns. 89   Multiple calls accumulate data that @ref pull returns.
90   90  
91   @param sv The data to append. 91   @param sv The data to append.
92   */ 92   */
93   void 93   void
HITCBC 94   388 provide(std::string_view sv) 94   388 provide(std::string_view sv)
95   { 95   {
HITCBC 96   388 data_.append(sv); 96   388 data_.append(sv);
HITCBC 97   388 } 97   388 }
98   98  
99   /// Clear all data and reset the read position. 99   /// Clear all data and reset the read position.
100   void 100   void
HITCBC 101   6 clear() noexcept 101   6 clear() noexcept
102   { 102   {
HITCBC 103   6 data_.clear(); 103   6 data_.clear();
HITCBC 104   6 pos_ = 0; 104   6 pos_ = 0;
HITCBC 105   6 } 105   6 }
106   106  
107   /// Return the number of bytes available for pulling. 107   /// Return the number of bytes available for pulling.
108   std::size_t 108   std::size_t
HITCBC 109   18 available() const noexcept 109   18 available() const noexcept
110   { 110   {
HITCBC 111   18 return data_.size() - pos_; 111   18 return data_.size() - pos_;
112   } 112   }
113   113  
114   /** Consume bytes from the source. 114   /** Consume bytes from the source.
115   115  
116   Advances the internal read position by the specified number 116   Advances the internal read position by the specified number
117   of bytes. The next call to @ref pull returns data starting 117   of bytes. The next call to @ref pull returns data starting
118   after the consumed bytes. 118   after the consumed bytes.
119   119  
120   @param n The number of bytes to consume. Must not exceed the 120   @param n The number of bytes to consume. Must not exceed the
121   total size of buffers returned by the previous @ref pull. 121   total size of buffers returned by the previous @ref pull.
122   */ 122   */
123   void 123   void
HITCBC 124   319 consume(std::size_t n) noexcept 124   319 consume(std::size_t n) noexcept
125   { 125   {
HITCBC 126   319 pos_ += n; 126   319 pos_ += n;
HITCBC 127   319 } 127   319 }
128   128  
129   /** Pull buffer data from the source. 129   /** Pull buffer data from the source.
130   130  
131   Fills the provided span with buffer descriptors pointing to 131   Fills the provided span with buffer descriptors pointing to
132   internal data starting from the current unconsumed position. 132   internal data starting from the current unconsumed position.
133   Returns a span of filled buffers. When no data remains, 133   Returns a span of filled buffers. When no data remains,
134   returns an empty span to signal completion. 134   returns an empty span to signal completion.
135   135  
136   Calling pull multiple times without intervening @ref consume 136   Calling pull multiple times without intervening @ref consume
137   returns the same data. Use consume to advance past processed 137   returns the same data. Use consume to advance past processed
138   bytes. 138   bytes.
139   139  
140   @param dest Span of const_buffer to fill. 140   @param dest Span of const_buffer to fill.
141   141  
142   @return An awaitable that await-returns `(error_code,std::span<const_buffer>)`. 142   @return An awaitable that await-returns `(error_code,std::span<const_buffer>)`.
143   143  
144   @see consume, fuse 144   @see consume, fuse
145   */ 145   */
146   auto 146   auto
HITCBC 147   656 pull(std::span<const_buffer> dest) 147   656 pull(std::span<const_buffer> dest)
148   { 148   {
149   struct awaitable 149   struct awaitable
150   { 150   {
151   buffer_source* self_; 151   buffer_source* self_;
152   std::span<const_buffer> dest_; 152   std::span<const_buffer> dest_;
153   153  
HITCBC 154   656 bool await_ready() const noexcept { return true; } 154   656 bool await_ready() const noexcept { return true; }
155   155  
156   // This method is required to satisfy Capy's IoAwaitable concept, 156   // This method is required to satisfy Capy's IoAwaitable concept,
157   // but is never called because await_ready() returns true. 157   // but is never called because await_ready() returns true.
158   // 158   //
159   // Capy uses a two-layer awaitable system: the promise's 159   // Capy uses a two-layer awaitable system: the promise's
160   // await_transform wraps awaitables in a transform_awaiter whose 160   // await_transform wraps awaitables in a transform_awaiter whose
161   // standard await_suspend(coroutine_handle) calls this custom 161   // standard await_suspend(coroutine_handle) calls this custom
162   // 2-argument overload, passing the io_env from the coroutine's 162   // 2-argument overload, passing the io_env from the coroutine's
163   // context. For synchronous test awaitables like this one, the 163   // context. For synchronous test awaitables like this one, the
164   // coroutine never suspends, so this is not invoked. The signature 164   // coroutine never suspends, so this is not invoked. The signature
165   // exists to allow the same awaitable type to work with both 165   // exists to allow the same awaitable type to work with both
166   // synchronous (test) and asynchronous (real I/O) code. 166   // synchronous (test) and asynchronous (real I/O) code.
MISUBC 167   void await_suspend( 167   void await_suspend(
168   std::coroutine_handle<>, 168   std::coroutine_handle<>,
169   io_env const*) const noexcept 169   io_env const*) const noexcept
170   { 170   {
MISUBC 171   } 171   }
172   172  
173   io_result<std::span<const_buffer>> 173   io_result<std::span<const_buffer>>
HITCBC 174   656 await_resume() 174   656 await_resume()
175   { 175   {
HITCBC 176   656 auto ec = self_->f_.maybe_fail(); 176   656 auto ec = self_->f_.maybe_fail();
HITCBC 177   546 if(ec) 177   546 if(ec)
HITCBC 178   110 return {ec, {}}; 178   110 return {ec, {}};
179   179  
HITCBC 180   436 if(self_->pos_ >= self_->data_.size()) 180   436 if(self_->pos_ >= self_->data_.size())
HITCBC 181   72 return {error::eof, {}}; 181   72 return {error::eof, {}};
182   182  
HITCBC 183   364 std::size_t avail = self_->data_.size() - self_->pos_; 183   364 std::size_t avail = self_->data_.size() - self_->pos_;
HITCBC 184   364 std::size_t to_return = (std::min)(avail, self_->max_pull_size_); 184   364 std::size_t to_return = (std::min)(avail, self_->max_pull_size_);
185   185  
HITCBC 186   364 if(dest_.empty()) 186   364 if(dest_.empty())
HITCBC 187   2 return {{}, {}}; 187   2 return {{}, {}};
188   188  
189   // Fill a single buffer descriptor 189   // Fill a single buffer descriptor
HITCBC 190   362 dest_[0] = make_buffer( 190   362 dest_[0] = make_buffer(
HITCBC 191   362 self_->data_.data() + self_->pos_, 191   362 self_->data_.data() + self_->pos_,
192   to_return); 192   to_return);
193   193  
HITCBC 194   362 return {{}, dest_.first(1)}; 194   362 return {{}, dest_.first(1)};
195   } 195   }
196   }; 196   };
HITCBC 197   656 return awaitable{this, dest}; 197   656 return awaitable{this, dest};
198   } 198   }
199   }; 199   };
200   200  
201   } // test 201   } // test
202   } // capy 202   } // capy
203   } // boost 203   } // boost
204   204  
205   #endif 205   #endif