GCC Code Coverage Report


Directory: ./
File: libs/buffers/include/boost/buffers/any_source.hpp
Date: 2025-12-09 13:02:49
Exec Total Coverage
Lines: 83 90 92.2%
Functions: 24 24 100.0%
Branches: 11 21 52.4%

Line Branch Exec Source
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
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)
6 //
7 // Official repository: https://github.com/cppalliance/beast2
8 //
9
10 #ifndef BOOST_BUFFERS_ANY_SOURCE_HPP
11 #define BOOST_BUFFERS_ANY_SOURCE_HPP
12
13 #include <boost/buffers/detail/config.hpp>
14 #include <boost/buffers/any_buffers.hpp>
15 #include <boost/buffers/buffer.hpp>
16 #include <boost/buffers/copy.hpp>
17 #include <boost/buffers/data_source.hpp>
18 #include <boost/buffers/error.hpp>
19 #include <boost/buffers/read_source.hpp>
20 #include <boost/buffers/slice.hpp>
21 #include <boost/buffers/detail/except.hpp>
22 #include <boost/core/span.hpp>
23
24 #include <type_traits>
25
26 namespace boost {
27 namespace buffers {
28
29 /** A type erased source.
30
31 An object of this type represents shared ownership of a type-erased read\
32 source or data source.
33 It provides a uniform interface for reading the source data regardless of
34 how the source is implemented. Accessing the bytes is achieved by calling
35 @ref read which reads data into a caller-provided buffer. Alternatively,
36 when @ref has_buffers returns `true` the source consists of buffers in memory,
37 and they can be accessed directly by calling @ref get_buffers.
38
39 Example sources include:
40 - in-memory buffers
41 - streaming file data
42 - generated data
43
44 @note @ref any_source is copyable, and the copies share ownership of the
45 underlying source. Changes to the state of one copy, such as reading
46 or rewinding, will be visible in all copies.
47
48 Type-erased sources can always be rewound to the beginning by
49 calling @ref rewind. Therefore, a source can be read multiple times.
50
51 @par Thread Safety
52 Unsafe.
53 */
54 class any_source
55 {
56 public:
57 /** Constructor
58
59 Default-constructed sources are empty.
60 */
61 BOOST_BUFFERS_DECL
62 any_source() noexcept;
63
64 /** Constructor
65
66 After the move, the moved-from source will be empty.
67 */
68 BOOST_BUFFERS_DECL
69 any_source(any_source&&) noexcept;
70
71 /** Constructor
72
73 After the copy, both sources share ownership of the same underlying source.
74 */
75 any_source(any_source const&) = default;
76
77 /** Assignment
78
79 After the move, the moved-from source will be empty.
80 */
81 BOOST_BUFFERS_DECL
82 any_source& operator=(any_source&&) noexcept;
83
84 /** Assignment
85
86 After the copy, both sources share ownership of the same underlying source.
87 */
88 any_source& operator=(any_source const&) = default;
89
90 /** Construct a data source.
91 */
92 template<class DataSource, typename std::enable_if<
93 std::conditional<
94 std::is_same<typename std::decay<
95 DataSource>::type, any_source>::value,
96 std::false_type,
97 is_data_source<typename std::decay<DataSource>::type>
98 >::type::value, int>::type = 0>
99 any_source(DataSource&& source);
100
101 /** Construct a read source.
102 */
103 template<class ReadSource, typename std::enable_if<
104 std::conditional<
105 std::is_same<typename std::decay<
106 ReadSource>::type, any_source>::value,
107 std::false_type,
108 is_read_source<typename std::decay<ReadSource>::type>
109 >::type::value, int>::type = 0>
110 any_source(ReadSource&& source);
111
112 /** Construct a read source with a known size.
113 */
114 template<class ReadSource, typename std::enable_if<
115 std::conditional<
116 std::is_same<typename std::decay<
117 ReadSource>::type, any_source>::value,
118 std::false_type,
119 is_read_source<typename std::decay<ReadSource>::type>
120 >::type::value, int>::type = 0>
121 any_source(
122 std::size_t known_size,
123 ReadSource&& source);
124
125 /** Return `true` if the size of the source is known.
126 */
127 11 bool has_size() const noexcept
128 {
129 11 return sp_->has_size();
130 }
131
132 /** Return `true` if the source consists of buffers in memory.
133 When the source consists of buffers in memory, they can
134 also be accessed directly using @ref get_buffers.
135 */
136 11 bool has_buffers() const noexcept
137 {
138 11 return sp_->has_buffers();
139 }
140
141 /** Return the size of the source, if available.
142 @throw std::invalid_argument if @ref has_size returns `false`.
143 @return The size of the source in bytes.
144 */
145 11 auto size() const -> std::size_t
146 {
147 11 return sp_->size();
148 }
149
150 /** Return the buffers representing the source, if available.
151 @throw std::invalid_argument if @ref has_buffers returns `false`.
152 @return A buffer sequence representing the source.
153 */
154 11 auto data() const ->
155 any_const_buffers
156 {
157 11 return sp_->data();
158 }
159
160 /** Rewind the source to the beginning.
161 This allows the source to be accessed from the start when calling @read.
162 */
163 158 void rewind()
164 {
165 158 sp_->rewind();
166 158 }
167
168 /** Read from the source into a caller-provided buffer.
169
170 When the last byte of data has been read,
171 @p ec is set to @ref error::eof.
172
173 @param dest The buffer sequence to write to
174 @param ec Set to the error, if any occurred.
175 @return The number of bytes read, which may be
176 less than `size(dest)`.
177 */
178 template<class MutableBufferSequence>
179 147 auto read(
180 MutableBufferSequence const& dest,
181 system::error_code& ec) ->
182 std::size_t
183 {
184 147 std::size_t result = 0;
185 147 constexpr std::size_t N = 16;
186 147 std::size_t n = 0;
187 147 mutable_buffer mb[N];
188 147 auto it = buffers::begin(dest);
189 147 auto const end_ = buffers::end(dest);
190
1/2
✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
147 if(it == end_)
191 return 0;
192 for(;;)
193 {
194 147 mb[n++] = *it++;
195
2/4
✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 147 times.
147 if( n < N &&
196 it != end_)
197 continue;
198 147 span<mutable_buffer const> dest1{ mb, n };
199
1/1
✓ Branch 2 taken 147 times.
147 auto const nread = sp_->read(dest1, ec);
200
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 147 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
147 BOOST_ASSERT(
201 ec.failed() ||
202 nread == buffers::size(dest1));
203 147 result += nread;
204
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 if(ec.failed())
205 147 break;
206 if(it == end_)
207 break;
208 n = 0;
209 }
210 147 return result;
211 }
212
213 private:
214 struct BOOST_BUFFERS_DECL
215 any_impl
216 {
217 virtual ~any_impl() = 0;
218 virtual bool has_size() const noexcept;
219 virtual bool has_buffers() const noexcept;
220 virtual std::size_t size() const;
221 virtual auto data() const -> any_const_buffers;
222 virtual void rewind() = 0;
223 virtual std::size_t read(
224 span<mutable_buffer const> dest,
225 system::error_code& ec) = 0;
226 };
227
228 template<class> struct data_model;
229 template<class> struct read_model;
230 template<class> struct sized_read_model;
231
232 std::shared_ptr<any_impl> sp_;
233 };
234
235 //-----------------------------------------------
236
237 template<class DataSource>
238 struct any_source::
239 data_model
240 : any_source::any_impl
241 {
242 typename std::decay<DataSource>::type source_;
243 std::size_t size_ = 0;
244 std::size_t nread_ = 0;
245
246 template<class DataSource_>
247 2 explicit data_model(
248 DataSource_&& source) noexcept
249 4 : source_(std::forward<DataSource_>(source))
250 2 , size_(buffers::size(source_.data()))
251 {
252 2 }
253
254 3 bool has_size() const noexcept override
255 {
256 3 return true;
257 }
258
259 3 bool has_buffers() const noexcept override
260 {
261 3 return true;
262 }
263
264 3 std::size_t size() const override
265 {
266 3 return size_;
267 }
268
269 any_const_buffers
270 3 data() const override
271 {
272
1/1
✓ Branch 2 taken 3 times.
3 return source_.data();
273 }
274
275 51 void rewind() override
276 {
277 51 nread_ = 0;
278 51 }
279
280 48 std::size_t read(
281 span<mutable_buffer const> dest,
282 system::error_code& ec) override
283 {
284 48 std::size_t n = copy(dest,
285 48 sans_prefix(source_.data(), nread_));
286 48 nread_ += n;
287
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 if(nread_ >= size_)
288 48 ec = error::eof;
289 else
290 ec = {};
291 48 return n;
292 }
293 };
294
295 //-----------------------------------------------
296
297 template<class ReadSource>
298 struct any_source::
299 read_model : any_source::any_impl
300 {
301 ReadSource source_;
302
303 template<class ReadSource_>
304 3 explicit read_model(
305 ReadSource_&& source)
306 3 : source_(std::forward<ReadSource_>(source))
307 {
308 3 }
309
310 68 void rewind() override
311 {
312 68 source_.rewind();
313 68 }
314
315 64 std::size_t read(
316 span<mutable_buffer const> dest,
317 system::error_code& ec) override
318 {
319 64 return source_.read(dest, ec);
320 }
321 };
322
323 //-----------------------------------------------
324
325 template<class ReadSource>
326 struct any_source::
327 sized_read_model : any_source::any_impl
328 {
329 std::size_t size_;
330 ReadSource source_;
331
332 template<class ReadSource_>
333 1 explicit sized_read_model(
334 ReadSource_&& source,
335 std::size_t known_size)
336 1 : size_(known_size)
337 1 , source_(std::forward<ReadSource_>(source))
338 {
339 1 }
340
341 1 bool has_size() const noexcept override
342 {
343 1 return true;
344 }
345
346 1 std::size_t size() const override
347 {
348 1 return size_;
349 }
350
351 17 void rewind() override
352 {
353 17 source_.rewind();
354 17 }
355
356 std::size_t
357 16 read(
358 span<mutable_buffer const> dest,
359 system::error_code& ec) override
360 {
361 16 return source_.read(dest, ec);
362 }
363 };
364
365 //-----------------------------------------------
366
367 template<class DataSource, typename std::enable_if<
368 std::conditional<
369 std::is_same<typename std::decay<
370 DataSource>::type, any_source>::value,
371 std::false_type,
372 is_data_source<typename std::decay<DataSource>::type>
373 >::type::value, int>::type>
374 2 any_source::
375 any_source(
376 2 DataSource&& source)
377 {
378 // VFALCO this requires DataSource to be nothrow
379 // move constructible for strong exception safety.
380 using type = typename std::decay<DataSource>::type;
381
1/1
✓ Branch 2 taken 2 times.
2 sp_ = std::make_shared<data_model<type>>(
382 std::forward<DataSource>(source));
383 2 }
384
385 template<class ReadSource, typename std::enable_if<
386 std::conditional<
387 std::is_same<typename std::decay<
388 ReadSource>::type, any_source>::value,
389 std::false_type,
390 is_read_source<typename std::decay<ReadSource>::type>
391 >::type::value, int>::type>
392 3 any_source::
393 any_source(
394 3 ReadSource&& source)
395 {
396 using type = typename std::decay<ReadSource>::type;
397
1/1
✓ Branch 2 taken 3 times.
3 sp_ = std::make_shared<read_model<type>>(
398 std::forward<ReadSource>(source));
399 3 }
400
401 /** Construct a streaming source source with a known size.
402 */
403 template<class ReadSource, typename std::enable_if<
404 std::conditional<
405 std::is_same<typename std::decay<
406 ReadSource>::type, any_source>::value,
407 std::false_type,
408 is_read_source<typename std::decay<ReadSource>::type>
409 >::type::value, int>::type>
410 1 any_source::
411 any_source(
412 std::size_t known_size,
413 1 ReadSource&& source)
414 {
415 using type = typename std::decay<ReadSource>::type;
416
1/1
✓ Branch 2 taken 1 times.
1 sp_ = std::make_shared<sized_read_model<type>>(
417 std::forward<ReadSource>(source), known_size);
418 1 }
419
420 } // buffers
421 } // boost
422
423 #endif
424