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 :
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 154 : if(ec_.failed())
215 : {
216 0 : ec = ec_;
217 0 : 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 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 24 : if(ec_.failed())
280 : {
281 0 : ec = ec_;
282 0 : 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 : 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 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 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 2 : sp_ = std::make_shared<model>(
367 : std::forward<DataSource>(source));
368 2 : }
369 :
370 : } // buffers
371 : } // boost
372 :
373 : #endif
|