GCC Code Coverage Report


Directory: ./
File: libs/buffers/include/boost/buffers/any_source.hpp
Date: 2025-12-08 14:54:08
Exec Total Coverage
Lines: 82 86 95.3%
Functions: 26 26 100.0%
Branches: 8 10 80.0%

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