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