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 : constexpr auto data() const noexcept ->
42 : std::conditional_t<std::is_const_v<T>, void const*, void*>
43 : {
44 : return p_;
45 : }
46 :
47 : constexpr std::size_t size() const noexcept
48 : {
49 : 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 1165 : basic_buffer() = default;
57 55165 : 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 : /// Tag type for customizing `buffer_size` via `tag_invoke`.
70 : struct size_tag {};
71 :
72 : /// Tag type for customizing slice operations via `tag_invoke`.
73 : struct slice_tag {};
74 :
75 : /** Constants for slice customization.
76 :
77 : Passed to `tag_invoke` overloads to specify which portion
78 : of a buffer sequence to retain.
79 : */
80 : enum class slice_how
81 : {
82 : /// Remove bytes from the front of the sequence.
83 : remove_prefix,
84 :
85 : /// Keep only the first N bytes.
86 : keep_prefix
87 : };
88 :
89 : //------------------------------------------------
90 :
91 : /** A reference to a contiguous region of writable memory.
92 :
93 : Represents a pointer and size pair for a modifiable byte range.
94 : Does not own the memory. Satisfies `MutableBufferSequence` (as a
95 : single-element sequence) and is implicitly convertible to
96 : `const_buffer`.
97 :
98 : @see const_buffer, MutableBufferSequence
99 : */
100 : class mutable_buffer
101 : : public detail::basic_buffer<unsigned char>
102 : {
103 : public:
104 : /// Construct an empty buffer.
105 580 : mutable_buffer() = default;
106 :
107 : /// Copy constructor.
108 : mutable_buffer(
109 : mutable_buffer const&) = default;
110 :
111 : /// Copy assignment.
112 : mutable_buffer& operator=(
113 : mutable_buffer const&) = default;
114 :
115 : /// Construct from pointer and size.
116 23322 : constexpr mutable_buffer(
117 : void* data, std::size_t size) noexcept
118 23322 : : basic_buffer<unsigned char>(
119 23322 : static_cast<unsigned char*>(data), size)
120 : {
121 23322 : }
122 :
123 : /// Construct from Asio mutable_buffer.
124 : template<class MutableBuffer>
125 : requires std::same_as<MutableBuffer, asio::mutable_buffer>
126 : constexpr mutable_buffer(
127 : MutableBuffer const& b) noexcept
128 : : basic_buffer<unsigned char>(
129 : static_cast<unsigned char*>(
130 : b.data()), b.size())
131 : {
132 : }
133 :
134 : /// Return a pointer to the memory region.
135 50007 : constexpr void* data() const noexcept
136 : {
137 50007 : return p_;
138 : }
139 :
140 : /// Return the size in bytes.
141 81834 : constexpr std::size_t size() const noexcept
142 : {
143 81834 : return n_;
144 : }
145 :
146 : /** Advance the buffer start, shrinking the region.
147 :
148 : @param n Bytes to skip. Clamped to `size()`.
149 : */
150 : mutable_buffer&
151 22270 : operator+=(std::size_t n) noexcept
152 : {
153 22270 : if( n > n_)
154 17 : n = n_;
155 22270 : p_ += n;
156 22270 : n_ -= n;
157 22270 : return *this;
158 : }
159 :
160 : /// Slice customization point for `tag_invoke`.
161 : friend
162 : void
163 1335 : tag_invoke(
164 : slice_tag const&,
165 : mutable_buffer& b,
166 : slice_how how,
167 : std::size_t n) noexcept
168 : {
169 1335 : b.do_slice(how, n);
170 1335 : }
171 :
172 : private:
173 1335 : void do_slice(
174 : slice_how how, std::size_t n) noexcept
175 : {
176 1335 : switch(how)
177 : {
178 659 : case slice_how::remove_prefix:
179 659 : *this += n;
180 659 : return;
181 :
182 676 : case slice_how::keep_prefix:
183 676 : if( n < n_)
184 584 : n_ = n;
185 676 : return;
186 : }
187 : }
188 : };
189 :
190 : //------------------------------------------------
191 :
192 : /** A reference to a contiguous region of read-only memory.
193 :
194 : Represents a pointer and size pair for a non-modifiable byte range.
195 : Does not own the memory. Satisfies `ConstBufferSequence` (as a
196 : single-element sequence). Implicitly constructible from
197 : `mutable_buffer`.
198 :
199 : @see mutable_buffer, ConstBufferSequence
200 : */
201 : class const_buffer
202 : : public detail::basic_buffer<unsigned char const>
203 : {
204 : public:
205 : /// Construct an empty buffer.
206 585 : const_buffer() = default;
207 :
208 : /// Copy constructor.
209 : const_buffer(const_buffer const&) = default;
210 :
211 : /// Copy assignment.
212 : const_buffer& operator=(
213 : const_buffer const& other) = default;
214 :
215 : /// Construct from pointer and size.
216 20475 : constexpr const_buffer(
217 : void const* data, std::size_t size) noexcept
218 20475 : : basic_buffer<unsigned char const>(
219 20475 : static_cast<unsigned char const*>(data), size)
220 : {
221 20475 : }
222 :
223 : /// Construct from mutable_buffer.
224 11368 : constexpr const_buffer(
225 : mutable_buffer const& b) noexcept
226 11368 : : basic_buffer<unsigned char const>(
227 11368 : static_cast<unsigned char const*>(b.data()), b.size())
228 : {
229 11368 : }
230 :
231 : /// Construct from Asio buffer types.
232 : template<class ConstBuffer>
233 : requires (std::same_as<ConstBuffer, asio::const_buffer> ||
234 : std::same_as<ConstBuffer, asio::mutable_buffer>)
235 : constexpr const_buffer(
236 : ConstBuffer const& b) noexcept
237 : : basic_buffer<unsigned char const>(
238 : static_cast<unsigned char const*>(
239 : b.data()), b.size())
240 : {
241 : }
242 :
243 : /// Return a pointer to the memory region.
244 47570 : constexpr void const* data() const noexcept
245 : {
246 47570 : return p_;
247 : }
248 :
249 : /// Return the size in bytes.
250 96226 : constexpr std::size_t size() const noexcept
251 : {
252 96226 : return n_;
253 : }
254 :
255 : /** Advance the buffer start, shrinking the region.
256 :
257 : @param n Bytes to skip. Clamped to `size()`.
258 : */
259 : const_buffer&
260 22939 : operator+=(std::size_t n) noexcept
261 : {
262 22939 : if( n > n_)
263 16 : n = n_;
264 22939 : p_ += n;
265 22939 : n_ -= n;
266 22939 : return *this;
267 : }
268 :
269 : /// Slice customization point for `tag_invoke`.
270 : friend
271 : void
272 2640 : tag_invoke(
273 : slice_tag const&,
274 : const_buffer& b,
275 : slice_how how,
276 : std::size_t n) noexcept
277 : {
278 2640 : b.do_slice(how, n);
279 2640 : }
280 :
281 : private:
282 2640 : void do_slice(
283 : slice_how how, std::size_t n) noexcept
284 : {
285 2640 : switch(how)
286 : {
287 1313 : case slice_how::remove_prefix:
288 1313 : *this += n;
289 1313 : return;
290 :
291 1327 : case slice_how::keep_prefix:
292 1327 : if( n < n_)
293 1238 : n_ = n;
294 1327 : return;
295 : }
296 : }
297 : };
298 :
299 : //------------------------------------------------
300 :
301 : /** Concept for sequences of read-only buffer regions.
302 :
303 : A type satisfies `ConstBufferSequence` if it represents one or more
304 : contiguous memory regions that can be read. This includes single
305 : buffers (convertible to `const_buffer`) and ranges of buffers.
306 :
307 : @par Syntactic Requirements
308 : @li Convertible to `const_buffer`, OR
309 : @li A bidirectional range with value type convertible to `const_buffer`
310 :
311 : @see const_buffer, MutableBufferSequence
312 : */
313 : template<typename T>
314 : concept ConstBufferSequence =
315 : std::is_convertible_v<T, const_buffer> || (
316 : std::ranges::bidirectional_range<T> &&
317 : std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
318 :
319 : /** Concept for sequences of writable buffer regions.
320 :
321 : A type satisfies `MutableBufferSequence` if it represents one or more
322 : contiguous memory regions that can be written. This includes single
323 : buffers (convertible to `mutable_buffer`) and ranges of buffers.
324 : Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
325 :
326 : @par Syntactic Requirements
327 : @li Convertible to `mutable_buffer`, OR
328 : @li A bidirectional range with value type convertible to `mutable_buffer`
329 :
330 : @see mutable_buffer, ConstBufferSequence
331 : */
332 : template<typename T>
333 : concept MutableBufferSequence =
334 : std::is_convertible_v<T, mutable_buffer> || (
335 : std::ranges::bidirectional_range<T> &&
336 : std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
337 :
338 : //------------------------------------------------------------------------------
339 :
340 : /** Return an iterator to the first buffer in a sequence.
341 :
342 : Handles single buffers and ranges uniformly. For a single buffer,
343 : returns a pointer to it (forming a one-element range).
344 : */
345 : constexpr struct begin_mrdocs_workaround_t
346 : {
347 : template<std::convertible_to<const_buffer> ConvertibleToBuffer>
348 15684 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
349 : {
350 15684 : return std::addressof(b);
351 : }
352 :
353 : template<ConstBufferSequence BS>
354 : requires (!std::convertible_to<BS, const_buffer>)
355 42825 : auto operator()(BS const& bs) const noexcept
356 : {
357 42825 : return std::ranges::begin(bs);
358 : }
359 :
360 : template<ConstBufferSequence BS>
361 : requires (!std::convertible_to<BS, const_buffer>)
362 9742 : auto operator()(BS& bs) const noexcept
363 : {
364 9742 : return std::ranges::begin(bs);
365 : }
366 : } begin {};
367 :
368 : /** Return an iterator past the last buffer in a sequence.
369 :
370 : Handles single buffers and ranges uniformly. For a single buffer,
371 : returns a pointer one past it.
372 : */
373 : constexpr struct end_mrdocs_workaround_t
374 : {
375 : template<std::convertible_to<const_buffer> ConvertibleToBuffer>
376 15444 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
377 : {
378 15444 : return std::addressof(b) + 1;
379 : }
380 :
381 : template<ConstBufferSequence BS>
382 : requires (!std::convertible_to<BS, const_buffer>)
383 35296 : auto operator()(BS const& bs) const noexcept
384 : {
385 35296 : return std::ranges::end(bs);
386 : }
387 :
388 : template<ConstBufferSequence BS>
389 : requires (!std::convertible_to<BS, const_buffer>)
390 9742 : auto operator()(BS& bs) const noexcept
391 : {
392 9742 : return std::ranges::end(bs);
393 : }
394 : } end {};
395 :
396 : //------------------------------------------------------------------------------
397 :
398 : template<ConstBufferSequence CB>
399 : std::size_t
400 13072 : tag_invoke(
401 : size_tag const&,
402 : CB const& bs) noexcept
403 : {
404 13072 : std::size_t n = 0;
405 13072 : auto const e = end(bs);
406 32961 : for(auto it = begin(bs); it != e; ++it)
407 19889 : n += const_buffer(*it).size();
408 13072 : return n;
409 : }
410 :
411 : //------------------------------------------------------------------------------
412 :
413 : /** Return the total byte count across all buffers in a sequence.
414 :
415 : Sums the `size()` of each buffer in the sequence. This differs
416 : from `buffer_length` which counts the number of buffer elements.
417 :
418 : @par Example
419 : @code
420 : std::array<mutable_buffer, 2> bufs = { ... };
421 : std::size_t total = buffer_size( bufs ); // sum of both sizes
422 : @endcode
423 : */
424 : constexpr struct buffer_size_mrdocs_workaround_t
425 : {
426 : template<ConstBufferSequence CB>
427 13072 : constexpr std::size_t operator()(
428 : CB const& bs) const noexcept
429 : {
430 13072 : return tag_invoke(size_tag{}, bs);
431 : }
432 : } buffer_size {};
433 :
434 : /** Check if a buffer sequence contains no data.
435 :
436 : @return `true` if all buffers have size zero or the sequence
437 : is empty.
438 : */
439 : constexpr struct buffer_empty_mrdocs_workaround_t
440 : {
441 : template<ConstBufferSequence CB>
442 20 : constexpr bool operator()(
443 : CB const& bs) const noexcept
444 : {
445 20 : auto it = begin(bs);
446 20 : auto const end_ = end(bs);
447 33 : while(it != end_)
448 : {
449 24 : const_buffer b(*it++);
450 24 : if(b.size() != 0)
451 11 : return false;
452 : }
453 9 : return true;
454 : }
455 : } buffer_empty {};
456 :
457 : //-----------------------------------------------
458 :
459 : namespace detail {
460 :
461 : template<class It>
462 : auto
463 240 : length_impl(It first, It last, int)
464 : -> decltype(static_cast<std::size_t>(last - first))
465 : {
466 240 : return static_cast<std::size_t>(last - first);
467 : }
468 :
469 : template<class It>
470 : std::size_t
471 : length_impl(It first, It last, long)
472 : {
473 : std::size_t n = 0;
474 : while(first != last)
475 : {
476 : ++first;
477 : ++n;
478 : }
479 : return n;
480 : }
481 :
482 : } // detail
483 :
484 : /** Return the number of buffer elements in a sequence.
485 :
486 : Counts the number of individual buffer objects, not bytes.
487 : For a single buffer, returns 1. For a range, returns the
488 : distance from `begin` to `end`.
489 :
490 : @see buffer_size
491 : */
492 : template<ConstBufferSequence CB>
493 : std::size_t
494 240 : buffer_length(CB const& bs)
495 : {
496 240 : return detail::length_impl(
497 240 : begin(bs), end(bs), 0);
498 : }
499 :
500 : /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
501 : template<typename BS>
502 : using buffer_type = std::conditional_t<
503 : MutableBufferSequence<BS>,
504 : mutable_buffer, const_buffer>;
505 :
506 : } // capy
507 : } // boost
508 :
509 : #endif
|