Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.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/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_BUFFERS_HPP
11 : #define BOOST_CAPY_BUFFERS_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <concepts>
15 : #include <cstddef>
16 : #include <iterator>
17 : #include <memory>
18 : #include <ranges>
19 : #include <type_traits>
20 :
21 : // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
22 :
23 : namespace boost {
24 :
25 : namespace asio {
26 : class const_buffer;
27 : class mutable_buffer;
28 : } // asio
29 :
30 : namespace capy {
31 :
32 : class const_buffer;
33 : class mutable_buffer;
34 :
35 : namespace detail {
36 :
37 : // satisfies Asio's buffer constructors, CANNOT be removed!
38 : template<class T, std::size_t Extent = (std::size_t)(-1)>
39 : class basic_buffer
40 : {
41 4 : constexpr auto data() const noexcept ->
42 : std::conditional_t<std::is_const_v<T>, void const*, void*>
43 : {
44 4 : return p_;
45 : }
46 :
47 4 : constexpr std::size_t size() const noexcept
48 : {
49 4 : return n_;
50 : }
51 :
52 : friend class capy::const_buffer;
53 : friend class capy::mutable_buffer;
54 : friend class asio::const_buffer;
55 : friend class asio::mutable_buffer;
56 147647 : basic_buffer() = default;
57 5320836 : constexpr basic_buffer(T* p, std::size_t n) noexcept : p_(p), n_(n) {}
58 : constexpr basic_buffer<T, (std::size_t)(-1)> subspan(
59 : std::size_t, std::size_t = (std::size_t)(-1)) const noexcept;
60 :
61 : T* p_ = nullptr;
62 : std::size_t n_ = 0;
63 : };
64 :
65 : } // detail
66 :
67 : //------------------------------------------------
68 :
69 : /** size tag for `tag_invoke`
70 :
71 : This type is used in overloads of `tag_invoke`
72 : for user-defined types to customize the `size()`
73 : algorithm.
74 : */
75 : struct size_tag {};
76 :
77 : /** slice tag for `tag_invoke`
78 :
79 : This type is used in overloads of `tag_invoke`
80 : for user-defined types to customize the slicing
81 : algorithms.
82 : */
83 : struct slice_tag {};
84 :
85 : /** slice constants for slice customization
86 :
87 : This defines the possible values passed to
88 : overloads of `tag_invoke` for user-defined
89 : types which customize the slicing algorithms.
90 : */
91 : enum class slice_how
92 : {
93 : /// Indicates that the front of the buffer sequence should be trimmed
94 : remove_prefix,
95 :
96 : /// Indicates that the front of the buffer sequence should be preserved
97 : keep_prefix
98 : };
99 :
100 : //------------------------------------------------
101 :
102 : /** Holds a contiguous range of modifiable bytes
103 : */
104 : class mutable_buffer
105 : : public detail::basic_buffer<unsigned char>
106 : {
107 : public:
108 : /** Constructor.
109 : */
110 575 : mutable_buffer() = default;
111 :
112 : /** Constructor.
113 : */
114 : mutable_buffer(
115 : mutable_buffer const&) = default;
116 :
117 : /** Assignment.
118 : */
119 : mutable_buffer& operator=(
120 : mutable_buffer const&) = default;
121 :
122 : /** Constructor.
123 : */
124 639132 : constexpr mutable_buffer(
125 : void* data, std::size_t size) noexcept
126 639132 : : basic_buffer<unsigned char>(
127 639132 : static_cast<unsigned char*>(data), size)
128 : {
129 639132 : }
130 :
131 : /** Constructor
132 : */
133 : template<class MutableBuffer>
134 : requires std::same_as<MutableBuffer, asio::mutable_buffer>
135 : constexpr mutable_buffer(
136 : MutableBuffer const& b) noexcept
137 : : basic_buffer<unsigned char>(
138 : static_cast<unsigned char*>(
139 : b.data()), b.size())
140 : {
141 : }
142 :
143 : /** Return a pointer to the beginning of the memory region
144 : */
145 800649 : constexpr void* data() const noexcept
146 : {
147 800649 : return p_;
148 : }
149 :
150 : /** Return the number of valid bytes in the referenced memory region
151 : */
152 2106908 : constexpr std::size_t size() const noexcept
153 : {
154 2106908 : return n_;
155 : }
156 :
157 : /** Remove a prefix of the memory region
158 :
159 : If the requested number of bytes is larger than the current size,
160 : the resulting buffer will have size 0.
161 :
162 : @param n The number of bytes to remove.
163 : */
164 : mutable_buffer&
165 1026737 : operator+=(std::size_t n) noexcept
166 : {
167 1026737 : if( n > n_)
168 16 : n = n_;
169 1026737 : p_ += n;
170 1026737 : n_ -= n;
171 1026737 : return *this;
172 : }
173 :
174 : /** Remove a slice from the buffer
175 : */
176 : friend
177 : void
178 1088 : tag_invoke(
179 : slice_tag const&,
180 : mutable_buffer& b,
181 : slice_how how,
182 : std::size_t n) noexcept
183 : {
184 1088 : b.do_slice(how, n);
185 1088 : }
186 :
187 : private:
188 1088 : void do_slice(
189 : slice_how how, std::size_t n) noexcept
190 : {
191 1088 : switch(how)
192 : {
193 512 : case slice_how::remove_prefix:
194 512 : *this += n;
195 512 : return;
196 :
197 576 : case slice_how::keep_prefix:
198 576 : if( n < n_)
199 476 : n_ = n;
200 576 : return;
201 : }
202 : }
203 : };
204 :
205 : //------------------------------------------------
206 :
207 : /** Holds a contiguous range of unmodifiable bytes
208 : */
209 : class const_buffer
210 : : public detail::basic_buffer<unsigned char const>
211 : {
212 : public:
213 : /** Constructor
214 : */
215 147072 : const_buffer() = default;
216 :
217 : /** Constructor
218 : */
219 : const_buffer(const_buffer const&) = default;
220 :
221 : /** Assignment
222 :
223 : @par Postconditions
224 : @code
225 : this->data() == other.data() && this->size() == other.size()
226 : @endcode
227 : */
228 : const_buffer& operator=(
229 : const_buffer const& other) = default;
230 :
231 : /** Constructor
232 : */
233 4672750 : constexpr const_buffer(
234 : void const* data, std::size_t size) noexcept
235 4672750 : : basic_buffer<unsigned char const>(
236 4672750 : static_cast<unsigned char const*>(data), size)
237 : {
238 4672750 : }
239 :
240 : /** Constructor
241 : */
242 8954 : constexpr const_buffer(
243 : mutable_buffer const& b) noexcept
244 8954 : : basic_buffer<unsigned char const>(
245 8954 : static_cast<unsigned char const*>(b.data()), b.size())
246 : {
247 8954 : }
248 :
249 : /** Constructor
250 : */
251 : template<class ConstBuffer>
252 : requires (std::same_as<ConstBuffer, asio::const_buffer> ||
253 : std::same_as<ConstBuffer, asio::mutable_buffer>)
254 : constexpr const_buffer(
255 : ConstBuffer const& b) noexcept
256 : : basic_buffer<unsigned char const>(
257 : static_cast<unsigned char const*>(
258 : b.data()), b.size())
259 : {
260 : }
261 :
262 : /** Return a pointer to the beginning of the memory region
263 : */
264 13069677 : constexpr void const* data() const noexcept
265 : {
266 13069677 : return p_;
267 : }
268 :
269 : /** Return the number of valid bytes in the referenced memory region
270 : */
271 18416594 : constexpr std::size_t size() const noexcept
272 : {
273 18416594 : return n_;
274 : }
275 :
276 : /** Remove a prefix of the memory region
277 :
278 : If the requested number of bytes is larger than the current size,
279 : the resulting buffer will have size 0.
280 :
281 : @param n The number of bytes to remove.
282 : */
283 : const_buffer&
284 1157878 : operator+=(std::size_t n) noexcept
285 : {
286 1157878 : if( n > n_)
287 4112 : n = n_;
288 1157878 : p_ += n;
289 1157878 : n_ -= n;
290 1157878 : return *this;
291 : }
292 :
293 : /** Remove a slice from the buffer
294 : */
295 : friend
296 : void
297 267349 : tag_invoke(
298 : slice_tag const&,
299 : const_buffer& b,
300 : slice_how how,
301 : std::size_t n) noexcept
302 : {
303 267349 : b.do_slice(how, n);
304 267349 : }
305 :
306 : private:
307 267349 : void do_slice(
308 : slice_how how, std::size_t n) noexcept
309 : {
310 267349 : switch(how)
311 : {
312 131653 : case slice_how::remove_prefix:
313 131653 : *this += n;
314 131653 : return;
315 :
316 135696 : case slice_how::keep_prefix:
317 135696 : if( n < n_)
318 121169 : n_ = n;
319 135696 : return;
320 : }
321 : }
322 : };
323 :
324 : //------------------------------------------------
325 :
326 : /** Concept for types that model ConstBufferSequence.
327 :
328 : A type satisfies `const_buffer_sequence` if it is convertible
329 : to `const_buffer`, or if it is a bidirectional range whose
330 : value type is convertible to `const_buffer`.
331 : */
332 : template<typename T>
333 : concept const_buffer_sequence =
334 : std::is_convertible_v<T, const_buffer> || (
335 : std::ranges::bidirectional_range<T> &&
336 : std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
337 :
338 : /** Concept for types that model MutableBufferSequence.
339 :
340 : A type satisfies `mutable_buffer_sequence` if it is convertible
341 : to `mutable_buffer`, or if it is a bidirectional range whose
342 : value type is convertible to `mutable_buffer`.
343 : */
344 : template<typename T>
345 : concept mutable_buffer_sequence =
346 : std::is_convertible_v<T, mutable_buffer> || (
347 : std::ranges::bidirectional_range<T> &&
348 : std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
349 :
350 : //------------------------------------------------------------------------------
351 :
352 : /** Return an iterator pointing to the first element of a buffer sequence
353 :
354 : This function returns an iterator to the beginning of the range denoted by
355 : `t`. It handles both ranges and single buffers uniformly.
356 :
357 : @par Constraints
358 : @code
359 : const_buffer_sequence<T>
360 : @endcode
361 :
362 : @param t The buffer sequence
363 : */
364 : constexpr struct begin_mrdocs_workaround_t
365 : {
366 : template<std::convertible_to<const_buffer> ConvertibleToBuffer>
367 593313 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
368 : {
369 593313 : return std::addressof(b);
370 : }
371 :
372 : template<const_buffer_sequence BufferSequence>
373 : requires (!std::convertible_to<BufferSequence, const_buffer>)
374 11093944 : auto operator()(BufferSequence const& bs) const noexcept
375 : {
376 11093944 : return std::ranges::begin(bs);
377 : }
378 :
379 : template<const_buffer_sequence BufferSequence>
380 : requires (!std::convertible_to<BufferSequence, const_buffer>)
381 2549260 : auto operator()(BufferSequence& bs) const noexcept
382 : {
383 2549260 : return std::ranges::begin(bs);
384 : }
385 : } begin {};
386 :
387 : //------------------------------------------------------------------------------
388 :
389 : /** Return an iterator to the end of the buffer sequence
390 :
391 : This function returns an iterator to the end of the range denoted by
392 : `t`. It handles both ranges and single buffers uniformly.
393 :
394 : @par Constraints
395 : @code
396 : const_buffer_sequence<T>
397 : @endcode
398 :
399 : @param t The buffer sequence
400 : */
401 : constexpr struct end_mrdocs_workaround_t
402 : {
403 : template<std::convertible_to<const_buffer> ConvertibleToBuffer>
404 593313 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
405 : {
406 593313 : return std::addressof(b) + 1;
407 : }
408 :
409 : template<const_buffer_sequence BufferSequence>
410 : requires (!std::convertible_to<BufferSequence, const_buffer>)
411 4319874 : auto operator()(BufferSequence const& bs) const noexcept
412 : {
413 4319874 : return std::ranges::end(bs);
414 : }
415 :
416 : template<const_buffer_sequence BufferSequence>
417 : requires (!std::convertible_to<BufferSequence, const_buffer>)
418 2549260 : auto operator()(BufferSequence& bs) const noexcept
419 : {
420 2549260 : return std::ranges::end(bs);
421 : }
422 : } end {};
423 :
424 : //------------------------------------------------------------------------------
425 :
426 : template<const_buffer_sequence ConstBufferSequence>
427 : std::size_t
428 1407618 : tag_invoke(
429 : size_tag const&,
430 : ConstBufferSequence const& bs) noexcept
431 : {
432 1407618 : std::size_t n = 0;
433 1407618 : auto const e = end(bs);
434 3888384 : for(auto it = begin(bs); it != e; ++it)
435 2480766 : n += const_buffer(*it).size();
436 1407618 : return n;
437 : }
438 :
439 : //------------------------------------------------------------------------------
440 :
441 : /** Return the total number of bytes in a buffer sequence
442 :
443 : This function returns the sum of the number of bytes in each contiguous
444 : buffer contained in the range or value. This is different from the length
445 : of the sequence returned by `std::ranges::size(t)`
446 :
447 : @par Constraints
448 : @code
449 : const_buffer_sequence<T>
450 : @endcode
451 :
452 : @par Example
453 : @code
454 : template<const_buffer_sequence ConstBufferSequence>
455 : bool is_small( ConstBufferSequence const& bs ) noexcept
456 : {
457 : return buffer_size(bs) < 100;
458 : }
459 : @endcode
460 : */
461 : constexpr struct buffer_size_mrdocs_workaround_t
462 : {
463 : template<const_buffer_sequence ConstBufferSequence>
464 1407618 : constexpr std::size_t operator()(
465 : ConstBufferSequence const& bs) const noexcept
466 : {
467 1407618 : return tag_invoke(size_tag{}, bs);
468 : }
469 : } buffer_size {};
470 :
471 : //-----------------------------------------------
472 :
473 : namespace detail {
474 :
475 : template<class It>
476 : auto
477 : length_impl(It first, It last, int)
478 : -> decltype(static_cast<std::size_t>(last - first))
479 : {
480 : return static_cast<std::size_t>(last - first);
481 : }
482 :
483 : template<class It>
484 : std::size_t
485 : length_impl(It first, It last, long)
486 : {
487 : std::size_t n = 0;
488 : while(first != last)
489 : {
490 : ++first;
491 : ++n;
492 : }
493 : return n;
494 : }
495 :
496 : } // detail
497 :
498 : /** Return the number of elements in a buffer sequence.
499 : */
500 : template<const_buffer_sequence ConstBufferSequence>
501 : std::size_t
502 : buffer_length(ConstBufferSequence const& bs)
503 : {
504 : return detail::length_impl(
505 : begin(bs), end(bs), 0);
506 : }
507 :
508 : /** Alias for const_buffer or mutable_buffer depending on sequence type.
509 : */
510 : template<typename BufferSequence>
511 : using buffer_type = std::conditional_t<
512 : mutable_buffer_sequence<BufferSequence>,
513 : mutable_buffer, const_buffer>;
514 :
515 : } // capy
516 : } // boost
517 :
518 : #endif
|