TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2026 Michael Vandeberg
4 : // Copyright (c) 2026 Steve Gerbino
5 : //
6 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 : //
9 : // Official repository: https://github.com/cppalliance/capy
10 : //
11 :
12 : #ifndef BOOST_CAPY_BUFFERS_HPP
13 : #define BOOST_CAPY_BUFFERS_HPP
14 :
15 : #include <boost/capy/detail/config.hpp>
16 : #include <concepts>
17 : #include <cstddef>
18 : #include <iterator>
19 : #include <memory>
20 : #include <ranges>
21 : #include <type_traits>
22 :
23 : // https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
24 :
25 : namespace boost {
26 :
27 : namespace asio {
28 : class const_buffer;
29 : class mutable_buffer;
30 : } // asio
31 :
32 : namespace capy {
33 :
34 : class const_buffer;
35 : class mutable_buffer;
36 :
37 : /** A reference to a contiguous region of writable memory.
38 :
39 : Represents a pointer and size pair for a modifiable byte range.
40 : Does not own the memory. Satisfies `MutableBufferSequence` (as a
41 : single-element sequence) and is implicitly convertible to
42 : `const_buffer`.
43 :
44 : @see @ref const_buffer, @ref MutableBufferSequence
45 : */
46 : class mutable_buffer
47 : {
48 : unsigned char* p_ = nullptr;
49 : std::size_t n_ = 0;
50 :
51 : public:
52 : /// Construct an empty buffer.
53 HIT 22 : mutable_buffer() = default;
54 :
55 : /** Construct a copy.
56 :
57 : @param other The buffer to copy.
58 : */
59 : mutable_buffer(
60 : mutable_buffer const& other) = default;
61 :
62 : /** Assign by copying.
63 :
64 : @param other The buffer to copy.
65 :
66 : @return A reference to `*this`.
67 : */
68 : mutable_buffer& operator=(
69 : mutable_buffer const& other) = default;
70 :
71 : /** Construct from a pointer and size.
72 :
73 : Takes `void*` so a pointer to any object type binds without a
74 : cast, since the buffer represents a raw, untyped writable
75 : region. Stored internally as `unsigned char*` for byte-wise
76 : pointer arithmetic (see `operator+=`).
77 :
78 : @param data A pointer to the first byte of the region.
79 :
80 : @param size The size of the region, in bytes.
81 : */
82 35269 : constexpr mutable_buffer(
83 : void* data, std::size_t size) noexcept
84 35269 : : p_(static_cast<unsigned char*>(data))
85 35269 : , n_(size)
86 : {
87 35269 : }
88 :
89 : /** Return a pointer to the memory region.
90 :
91 : Returns `void*`, symmetric with the constructor, so the
92 : caller can reinterpret the raw region as whatever type it needs.
93 :
94 : @return A pointer to the first byte of the region.
95 : */
96 53991 : constexpr void* data() const noexcept
97 : {
98 53991 : return p_;
99 : }
100 :
101 : /** Return the size in bytes.
102 :
103 : @return The size of the region, in bytes.
104 : */
105 80492 : constexpr std::size_t size() const noexcept
106 : {
107 80492 : return n_;
108 : }
109 :
110 : /** Advance the buffer start, shrinking the region.
111 :
112 : @param n Bytes to skip. Clamped to `size()`.
113 :
114 : @return A reference to `*this`.
115 : */
116 : mutable_buffer&
117 17716 : operator+=(std::size_t n) noexcept
118 : {
119 17716 : if( n > n_)
120 1 : n = n_;
121 17716 : p_ += n;
122 17716 : n_ -= n;
123 17716 : return *this;
124 : }
125 : };
126 :
127 : /** A reference to a contiguous region of read-only memory.
128 :
129 : Represents a pointer and size pair for a non-modifiable byte range.
130 : Does not own the memory. Satisfies `ConstBufferSequence` (as a
131 : single-element sequence). Implicitly constructible from
132 : `mutable_buffer`.
133 :
134 : @see @ref mutable_buffer, @ref ConstBufferSequence
135 : */
136 : class const_buffer
137 : {
138 : unsigned char const* p_ = nullptr;
139 : std::size_t n_ = 0;
140 :
141 : public:
142 : /// Construct an empty buffer.
143 58 : const_buffer() = default;
144 :
145 : /** Construct a copy.
146 :
147 : @param other The buffer to copy.
148 : */
149 : const_buffer(const_buffer const& other) = default;
150 :
151 : /** Assign by copying.
152 :
153 : @param other The buffer to copy.
154 :
155 : @return A reference to `*this`.
156 : */
157 : const_buffer& operator=(
158 : const_buffer const& other) = default;
159 :
160 : /** Construct from a pointer and size.
161 :
162 : Takes `void const*` so a pointer to any object type binds
163 : without a cast, since the buffer represents a raw, untyped
164 : read-only region. Stored internally as `unsigned char const*`
165 : for byte-wise pointer arithmetic (see `operator+=`).
166 :
167 : @param data A pointer to the first byte of the region.
168 :
169 : @param size The size of the region, in bytes.
170 : */
171 31999 : constexpr const_buffer(
172 : void const* data, std::size_t size) noexcept
173 31999 : : p_(static_cast<unsigned char const*>(data))
174 31999 : , n_(size)
175 : {
176 31999 : }
177 :
178 : /** Construct from mutable_buffer.
179 :
180 : @param b The writable buffer whose region is referenced.
181 : */
182 7865 : constexpr const_buffer(
183 : mutable_buffer const& b) noexcept
184 7865 : : p_(static_cast<unsigned char const*>(b.data()))
185 7865 : , n_(b.size())
186 : {
187 7865 : }
188 :
189 : /** Return a pointer to the memory region.
190 :
191 : Returns `void const*`, symmetric with the constructor, so the
192 : caller can reinterpret the raw region as whatever type it needs.
193 :
194 : @return A pointer to the first byte of the region.
195 : */
196 46540 : constexpr void const* data() const noexcept
197 : {
198 46540 : return p_;
199 : }
200 :
201 : /** Return the size in bytes.
202 :
203 : @return The size of the region, in bytes.
204 : */
205 78400 : constexpr std::size_t size() const noexcept
206 : {
207 78400 : return n_;
208 : }
209 :
210 : /** Advance the buffer start, shrinking the region.
211 :
212 : @param n Bytes to skip. Clamped to `size()`.
213 :
214 : @return A reference to `*this`.
215 : */
216 : const_buffer&
217 17361 : operator+=(std::size_t n) noexcept
218 : {
219 17361 : if( n > n_)
220 1 : n = n_;
221 17361 : p_ += n;
222 17361 : n_ -= n;
223 17361 : return *this;
224 : }
225 : };
226 :
227 : /** Requires a type to convert to `const_buffer`.
228 :
229 : This is the element requirement of @ref ConstBufferSequence.
230 :
231 : For a `T const` lvalue `x`, both `const_buffer b = x;` and
232 : `const_buffer(x)` must be valid, since algorithms convert elements
233 : through a const reference and may use either form. A conversion
234 : operator must therefore be `const`.
235 :
236 : @note Algorithms that read buffer sequences are `noexcept`, so a
237 : conversion that throws terminates the program. Conversion operators
238 : should be `noexcept`.
239 :
240 : @see @ref const_buffer, @ref MutableBuffer
241 : */
242 : template<typename T>
243 : concept ConstBuffer =
244 : std::convertible_to<T const&, const_buffer>;
245 :
246 : /** Requires a type to convert to both `mutable_buffer` and `const_buffer`.
247 :
248 : This is the element requirement of @ref MutableBufferSequence.
249 :
250 : A type that converts only to `mutable_buffer` does not qualify.
251 : Reaching `const_buffer` from it takes two user-defined conversions,
252 : and an implicit conversion sequence allows one. Such a type must
253 : also provide a conversion to `const_buffer`.
254 :
255 : As with @ref ConstBuffer, the conversion is from a `T const` lvalue,
256 : and conversion operators should be `noexcept`.
257 :
258 : @see @ref mutable_buffer, @ref ConstBuffer
259 : */
260 : template<typename T>
261 : concept MutableBuffer =
262 : ConstBuffer<T> && std::convertible_to<T const&, mutable_buffer>;
263 :
264 : /** Requires a type to be a @ref ConstBuffer, or a range of them.
265 :
266 : A type satisfies `ConstBufferSequence` if it represents one or more
267 : contiguous memory regions that can be read. This includes single
268 : buffers and ranges of buffers.
269 :
270 : @par Syntactic Requirements
271 : @li @ref ConstBuffer, OR
272 : @li A bidirectional range whose value type is a @ref ConstBuffer
273 :
274 : @see @ref const_buffer, @ref MutableBufferSequence
275 : */
276 : // tag::const_buffer_sequence_concept[]
277 : template<typename T>
278 : concept ConstBufferSequence =
279 : ConstBuffer<T> || (
280 : std::ranges::bidirectional_range<T> &&
281 : ConstBuffer<std::ranges::range_value_t<T>>);
282 : // end::const_buffer_sequence_concept[]
283 :
284 : /** Requires a type to be a @ref MutableBuffer, or a range of them.
285 :
286 : A type satisfies `MutableBufferSequence` if it represents one or more
287 : contiguous memory regions that can be written. This includes single
288 : buffers and ranges of buffers.
289 :
290 : Every `MutableBufferSequence` is also a @ref ConstBufferSequence.
291 :
292 : A @ref ConstBuffer is a single buffer, even if it is also a range,
293 : so only its own conversion to `mutable_buffer` is considered.
294 :
295 : @par Syntactic Requirements
296 : @li @ref MutableBuffer, OR
297 : @li A bidirectional range, not a @ref ConstBuffer, whose value type
298 : is a @ref MutableBuffer
299 :
300 : @see @ref mutable_buffer, @ref ConstBufferSequence
301 : */
302 : // tag::mutable_buffer_sequence_concept[]
303 : template<typename T>
304 : concept MutableBufferSequence =
305 : ConstBufferSequence<T> && (MutableBuffer<T> || (
306 : !ConstBuffer<T> &&
307 : std::ranges::bidirectional_range<T> &&
308 : MutableBuffer<std::ranges::range_value_t<T>>));
309 : // end::mutable_buffer_sequence_concept[]
310 :
311 : /** Return an iterator to the first buffer in a sequence.
312 :
313 : @functionobject
314 : */
315 : constexpr struct
316 : {
317 : /** Return a pointer to a single buffer, forming a one-element range.
318 :
319 : @param b A single buffer.
320 :
321 : @return A pointer to `b`.
322 : */
323 : template<ConstBuffer ConvertibleToBuffer>
324 6657 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
325 : {
326 6657 : return std::addressof(b);
327 : }
328 :
329 : /** Return an iterator to the first buffer of a sequence.
330 :
331 : @param bs The buffer sequence.
332 :
333 : @return An iterator to the first buffer of `bs`.
334 : */
335 : template<ConstBufferSequence BS>
336 : requires (!ConstBuffer<BS>)
337 33657 : auto operator()(BS const& bs) const noexcept
338 : {
339 33657 : return std::ranges::begin(bs);
340 : }
341 :
342 : /** Return an iterator to the first buffer of a sequence.
343 :
344 : @param bs The buffer sequence.
345 :
346 : @return An iterator to the first buffer of `bs`.
347 : */
348 : template<ConstBufferSequence BS>
349 : requires (!ConstBuffer<BS>)
350 9196 : auto operator()(BS& bs) const noexcept
351 : {
352 9196 : return std::ranges::begin(bs);
353 : }
354 : } buffer_sequence_begin {};
355 :
356 : /** Return an iterator past the last buffer in a sequence.
357 :
358 : @functionobject
359 : */
360 : constexpr struct
361 : {
362 : /** Return a pointer one past a single buffer, forming a one-element range.
363 :
364 : @param b A single buffer.
365 :
366 : @return A pointer one past `b`.
367 : */
368 : template<ConstBuffer ConvertibleToBuffer>
369 6660 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
370 : {
371 6660 : return std::addressof(b) + 1;
372 : }
373 :
374 : /** Return an iterator past the last buffer of a sequence.
375 :
376 : @param bs The buffer sequence.
377 :
378 : @return An iterator one past the last buffer of `bs`.
379 : */
380 : template<ConstBufferSequence BS>
381 : requires (!ConstBuffer<BS>)
382 33696 : auto operator()(BS const& bs) const noexcept
383 : {
384 33696 : return std::ranges::end(bs);
385 : }
386 :
387 : /** Return an iterator past the last buffer of a sequence.
388 :
389 : @param bs The buffer sequence.
390 :
391 : @return An iterator one past the last buffer of `bs`.
392 : */
393 : template<ConstBufferSequence BS>
394 : requires (!ConstBuffer<BS>)
395 9196 : auto operator()(BS& bs) const noexcept
396 : {
397 9196 : return std::ranges::end(bs);
398 : }
399 : } buffer_sequence_end {};
400 :
401 : /** Return the total byte count across all buffers in a sequence.
402 :
403 : @functionobject
404 : */
405 : constexpr struct
406 : {
407 : // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
408 : // when iterating here. The class uses union storage with placement
409 : // new for slots 0..n_-1, so reads inside this bounded loop are
410 : // well-defined, but the optimizer can't prove the loop bound and
411 : // warns. The runtime cost of value-initializing all N slots is
412 : // non-trivial for non-trivial value types, so we suppress instead.
413 : #if defined(__GNUC__) && !defined(__clang__)
414 : #pragma GCC diagnostic push
415 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
416 : #endif
417 : /** Return the total byte count across all buffers in a sequence.
418 :
419 : Sums the `size()` of each buffer in the sequence. This differs
420 : from `buffers_count` which counts the number of buffer elements.
421 :
422 : @param bs The buffer sequence.
423 :
424 : @return The sum of the sizes of all buffers in `bs`.
425 :
426 : @par Example
427 : @par !example example
428 :
429 : */
430 : template<ConstBufferSequence CB>
431 6291 : constexpr std::size_t operator()(
432 : CB const& bs) const noexcept
433 : {
434 6291 : std::size_t n = 0;
435 6291 : auto const e = capy::buffer_sequence_end(bs);
436 14581 : for(auto it = capy::buffer_sequence_begin(bs); it != e; ++it)
437 8290 : n += const_buffer(*it).size();
438 6291 : return n;
439 : }
440 : #if defined(__GNUC__) && !defined(__clang__)
441 : #pragma GCC diagnostic pop
442 : #endif
443 : } buffer_size {};
444 :
445 : /** Check if a buffer sequence contains no data.
446 :
447 : @functionobject
448 : */
449 : constexpr struct
450 : {
451 : // See note on buffer_size above — same union-storage false positive.
452 : #if defined(__GNUC__) && !defined(__clang__)
453 : #pragma GCC diagnostic push
454 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
455 : #endif
456 : /** Check if a buffer sequence contains no data.
457 :
458 : @param bs The buffer sequence.
459 :
460 : @return `true` if all buffers have size zero or the sequence
461 : is empty.
462 : */
463 : template<ConstBufferSequence CB>
464 1569 : constexpr bool operator()(
465 : CB const& bs) const noexcept
466 : {
467 1569 : auto it = buffer_sequence_begin(bs);
468 1569 : auto const end_ = buffer_sequence_end(bs);
469 1617 : while(it != end_)
470 : {
471 1581 : const_buffer b(*it++);
472 1581 : if(b.size() != 0)
473 1533 : return false;
474 : }
475 36 : return true;
476 : }
477 : #if defined(__GNUC__) && !defined(__clang__)
478 : #pragma GCC diagnostic pop
479 : #endif
480 : } buffers_empty {};
481 :
482 : namespace detail {
483 :
484 : template<class It>
485 : auto
486 8 : length_impl(It first, It last, int)
487 : -> decltype(static_cast<std::size_t>(last - first))
488 : {
489 8 : return static_cast<std::size_t>(last - first);
490 : }
491 :
492 : template<class It>
493 : std::size_t
494 : length_impl(It first, It last, long)
495 : {
496 : std::size_t n = 0;
497 : while(first != last)
498 : {
499 : ++first;
500 : ++n;
501 : }
502 : return n;
503 : }
504 :
505 : } // detail
506 :
507 : /** Return the number of buffer elements in a sequence.
508 :
509 : Counts the number of individual buffer objects, not bytes.
510 : For a single buffer, returns 1. For a range, returns the
511 : distance from `buffer_sequence_begin` to `buffer_sequence_end`.
512 :
513 : @param bs The buffer sequence.
514 :
515 : @return The number of buffers in `bs`.
516 :
517 : @see @ref buffer_size
518 : */
519 : template<ConstBufferSequence CB>
520 : std::size_t
521 8 : buffers_count(CB const& bs)
522 : {
523 8 : return detail::length_impl(
524 8 : buffer_sequence_begin(bs), buffer_sequence_end(bs), 0);
525 : }
526 :
527 : /// Names `mutable_buffer` for a mutable sequence, `const_buffer` otherwise.
528 : template<typename BS>
529 : using buffer_type = std::conditional_t<
530 : MutableBufferSequence<BS>,
531 : mutable_buffer, const_buffer>;
532 :
533 : } // capy
534 : } // boost
535 :
536 : #endif
|