Line data Source code
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 147 : if(it == end_)
191 0 : return 0;
192 0 : for(;;)
193 : {
194 147 : mb[n++] = *it++;
195 147 : if( n < N &&
196 : it != end_)
197 0 : continue;
198 147 : span<mutable_buffer const> dest1{ mb, n };
199 147 : auto const nread = sp_->read(dest1, ec);
200 147 : BOOST_ASSERT(
201 : ec.failed() ||
202 : nread == buffers::size(dest1));
203 147 : result += nread;
204 147 : if(ec.failed())
205 147 : break;
206 0 : if(it == end_)
207 0 : break;
208 0 : 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 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 48 : if(nread_ >= size_)
288 48 : ec = error::eof;
289 : else
290 0 : 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 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 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 : 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
|