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 : // tag::const_buffer_sequence_concept[]
239 : template<typename T>
240 : concept ConstBufferSequence =
241 : std::is_convertible_v<T, const_buffer> || (
242 : std::ranges::bidirectional_range<T> &&
243 : std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
244 : // end::const_buffer_sequence_concept[]
245 :
246 : /** Requires a type to convert to `mutable_buffer`, or be a range of such buffers.
247 :
248 : A type satisfies `MutableBufferSequence` if it represents one or more
249 : contiguous memory regions that can be written. This includes single
250 : buffers (convertible to `mutable_buffer`) and ranges of buffers.
251 :
252 : This does not imply `ConstBufferSequence`. A type reaching
253 : `mutable_buffer` through its own conversion operator would need a
254 : second conversion, to `const_buffer`. An implicit conversion
255 : sequence allows only one user-defined step.
256 :
257 : @par Syntactic Requirements
258 : @li Convertible to `mutable_buffer`, OR
259 : @li A bidirectional range with value type convertible to `mutable_buffer`
260 :
261 : @see mutable_buffer, ConstBufferSequence
262 : */
263 : // tag::mutable_buffer_sequence_concept[]
264 : template<typename T>
265 : concept MutableBufferSequence =
266 : std::is_convertible_v<T, mutable_buffer> || (
267 : std::ranges::bidirectional_range<T> &&
268 : std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
269 : // end::mutable_buffer_sequence_concept[]
270 :
271 : /** Return an iterator to the first buffer in a sequence.
272 :
273 : @functionobject
274 : */
275 : constexpr struct
276 : {
277 : /** Return a pointer to a single buffer, forming a one-element range.
278 :
279 : @param b A single buffer.
280 :
281 : @return A pointer to `b`.
282 : */
283 : template<std::convertible_to<const_buffer> ConvertibleToBuffer>
284 6664 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
285 : {
286 6664 : return std::addressof(b);
287 : }
288 :
289 : /** Return an iterator to the first buffer of a sequence.
290 :
291 : @param bs The buffer sequence.
292 :
293 : @return An iterator to the first buffer of `bs`.
294 : */
295 : template<ConstBufferSequence BS>
296 : requires (!std::convertible_to<BS, const_buffer>)
297 33709 : auto operator()(BS const& bs) const noexcept
298 : {
299 33709 : return std::ranges::begin(bs);
300 : }
301 :
302 : /** Return an iterator to the first buffer of a sequence.
303 :
304 : @param bs The buffer sequence.
305 :
306 : @return An iterator to the first buffer of `bs`.
307 : */
308 : template<ConstBufferSequence BS>
309 : requires (!std::convertible_to<BS, const_buffer>)
310 9193 : auto operator()(BS& bs) const noexcept
311 : {
312 9193 : return std::ranges::begin(bs);
313 : }
314 : } begin {};
315 :
316 : /** Return an iterator past the last buffer in a sequence.
317 :
318 : @functionobject
319 : */
320 : constexpr struct
321 : {
322 : /** Return a pointer one past a single buffer, forming a one-element range.
323 :
324 : @param b A single buffer.
325 :
326 : @return A pointer one past `b`.
327 : */
328 : template<std::convertible_to<const_buffer> ConvertibleToBuffer>
329 6666 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
330 : {
331 6666 : return std::addressof(b) + 1;
332 : }
333 :
334 : /** Return an iterator past the last buffer of a sequence.
335 :
336 : @param bs The buffer sequence.
337 :
338 : @return An iterator one past the last buffer of `bs`.
339 : */
340 : template<ConstBufferSequence BS>
341 : requires (!std::convertible_to<BS, const_buffer>)
342 33731 : auto operator()(BS const& bs) const noexcept
343 : {
344 33731 : return std::ranges::end(bs);
345 : }
346 :
347 : /** Return an iterator past the last buffer of a sequence.
348 :
349 : @param bs The buffer sequence.
350 :
351 : @return An iterator one past the last buffer of `bs`.
352 : */
353 : template<ConstBufferSequence BS>
354 : requires (!std::convertible_to<BS, const_buffer>)
355 9193 : auto operator()(BS& bs) const noexcept
356 : {
357 9193 : return std::ranges::end(bs);
358 : }
359 : } end {};
360 :
361 : /** Return the total byte count across all buffers in a sequence.
362 :
363 : @functionobject
364 : */
365 : constexpr struct
366 : {
367 : // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
368 : // when iterating here. The class uses union storage with placement
369 : // new for slots 0..n_-1, so reads inside this bounded loop are
370 : // well-defined, but the optimizer can't prove the loop bound and
371 : // warns. The runtime cost of value-initializing all N slots is
372 : // non-trivial for non-trivial value types, so we suppress instead.
373 : #if defined(__GNUC__) && !defined(__clang__)
374 : #pragma GCC diagnostic push
375 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
376 : #endif
377 : /** Return the total byte count across all buffers in a sequence.
378 :
379 : Sums the `size()` of each buffer in the sequence. This differs
380 : from `buffer_length` which counts the number of buffer elements.
381 :
382 : @param bs The buffer sequence.
383 :
384 : @return The sum of the sizes of all buffers in `bs`.
385 :
386 : @par Example
387 : @par !example example
388 :
389 : */
390 : template<ConstBufferSequence CB>
391 6296 : constexpr std::size_t operator()(
392 : CB const& bs) const noexcept
393 : {
394 6296 : std::size_t n = 0;
395 6296 : auto const e = capy::end(bs);
396 14520 : for(auto it = capy::begin(bs); it != e; ++it)
397 8224 : n += const_buffer(*it).size();
398 6296 : return n;
399 : }
400 : #if defined(__GNUC__) && !defined(__clang__)
401 : #pragma GCC diagnostic pop
402 : #endif
403 : } buffer_size {};
404 :
405 : /** Check if a buffer sequence contains no data.
406 :
407 : @functionobject
408 : */
409 : constexpr struct
410 : {
411 : // See note on buffer_size above — same union-storage false positive.
412 : #if defined(__GNUC__) && !defined(__clang__)
413 : #pragma GCC diagnostic push
414 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
415 : #endif
416 : /** Check if a buffer sequence contains no data.
417 :
418 : @param bs The buffer sequence.
419 :
420 : @return `true` if all buffers have size zero or the sequence
421 : is empty.
422 : */
423 : template<ConstBufferSequence CB>
424 1584 : constexpr bool operator()(
425 : CB const& bs) const noexcept
426 : {
427 1584 : auto it = begin(bs);
428 1584 : auto const end_ = end(bs);
429 1632 : while(it != end_)
430 : {
431 1596 : const_buffer b(*it++);
432 1596 : if(b.size() != 0)
433 1548 : return false;
434 : }
435 36 : return true;
436 : }
437 : #if defined(__GNUC__) && !defined(__clang__)
438 : #pragma GCC diagnostic pop
439 : #endif
440 : } buffer_empty {};
441 :
442 : namespace detail {
443 :
444 : template<class It>
445 : auto
446 11 : length_impl(It first, It last, int)
447 : -> decltype(static_cast<std::size_t>(last - first))
448 : {
449 11 : return static_cast<std::size_t>(last - first);
450 : }
451 :
452 : template<class It>
453 : std::size_t
454 : length_impl(It first, It last, long)
455 : {
456 : std::size_t n = 0;
457 : while(first != last)
458 : {
459 : ++first;
460 : ++n;
461 : }
462 : return n;
463 : }
464 :
465 : } // detail
466 :
467 : /** Return the number of buffer elements in a sequence.
468 :
469 : Counts the number of individual buffer objects, not bytes.
470 : For a single buffer, returns 1. For a range, returns the
471 : distance from `begin` to `end`.
472 :
473 : @param bs The buffer sequence.
474 :
475 : @return The number of buffers in `bs`.
476 :
477 : @see buffer_size
478 : */
479 : template<ConstBufferSequence CB>
480 : std::size_t
481 11 : buffer_length(CB const& bs)
482 : {
483 11 : return detail::length_impl(
484 11 : begin(bs), end(bs), 0);
485 : }
486 :
487 : /// Names `mutable_buffer` for a mutable sequence, `const_buffer` otherwise.
488 : template<typename BS>
489 : using buffer_type = std::conditional_t<
490 : MutableBufferSequence<BS>,
491 : mutable_buffer, const_buffer>;
492 :
493 : } // capy
494 : } // boost
495 :
496 : #endif
|