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 35286 : constexpr mutable_buffer(
82 : void* data, std::size_t size) noexcept
83 35286 : : p_(static_cast<unsigned char*>(data))
84 35286 : , n_(size)
85 : {
86 35286 : }
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 54041 : constexpr void* data() const noexcept
96 : {
97 54041 : return p_;
98 : }
99 :
100 : /** Return the size in bytes.
101 :
102 : @return The size of the region, in bytes.
103 : */
104 80569 : constexpr std::size_t size() const noexcept
105 : {
106 80569 : 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 32089 : constexpr const_buffer(
171 : void const* data, std::size_t size) noexcept
172 32089 : : p_(static_cast<unsigned char const*>(data))
173 32089 : , n_(size)
174 : {
175 32089 : }
176 :
177 : /** Construct from mutable_buffer.
178 :
179 : @param b The writable buffer whose region is referenced.
180 : */
181 7891 : constexpr const_buffer(
182 : mutable_buffer const& b) noexcept
183 7891 : : p_(static_cast<unsigned char const*>(b.data()))
184 7891 : , n_(b.size())
185 : {
186 7891 : }
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 77667 : constexpr std::size_t size() const noexcept
205 : {
206 77667 : 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 33711 : auto operator()(BS const& bs) const noexcept
294 : {
295 33711 : 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 33733 : auto operator()(BS const& bs) const noexcept
339 : {
340 33733 : 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 : @par !example example
384 :
385 : */
386 : template<ConstBufferSequence CB>
387 6298 : constexpr std::size_t operator()(
388 : CB const& bs) const noexcept
389 : {
390 6298 : std::size_t n = 0;
391 6298 : auto const e = capy::end(bs);
392 14526 : for(auto it = capy::begin(bs); it != e; ++it)
393 8228 : n += const_buffer(*it).size();
394 6298 : return n;
395 : }
396 : #if defined(__GNUC__) && !defined(__clang__)
397 : #pragma GCC diagnostic pop
398 : #endif
399 : } buffer_size {};
400 :
401 : /** Check if a buffer sequence contains no data.
402 :
403 : @functionobject
404 : */
405 : constexpr struct
406 : {
407 : // See note on buffer_size above — same union-storage false positive.
408 : #if defined(__GNUC__) && !defined(__clang__)
409 : #pragma GCC diagnostic push
410 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
411 : #endif
412 : /** Check if a buffer sequence contains no data.
413 :
414 : @param bs The buffer sequence.
415 :
416 : @return `true` if all buffers have size zero or the sequence
417 : is empty.
418 : */
419 : template<ConstBufferSequence CB>
420 1584 : constexpr bool operator()(
421 : CB const& bs) const noexcept
422 : {
423 1584 : auto it = begin(bs);
424 1584 : auto const end_ = end(bs);
425 1632 : while(it != end_)
426 : {
427 1596 : const_buffer b(*it++);
428 1596 : if(b.size() != 0)
429 1548 : return false;
430 : }
431 36 : return true;
432 : }
433 : #if defined(__GNUC__) && !defined(__clang__)
434 : #pragma GCC diagnostic pop
435 : #endif
436 : } buffer_empty {};
437 :
438 : namespace detail {
439 :
440 : template<class It>
441 : auto
442 11 : length_impl(It first, It last, int)
443 : -> decltype(static_cast<std::size_t>(last - first))
444 : {
445 11 : return static_cast<std::size_t>(last - first);
446 : }
447 :
448 : template<class It>
449 : std::size_t
450 : length_impl(It first, It last, long)
451 : {
452 : std::size_t n = 0;
453 : while(first != last)
454 : {
455 : ++first;
456 : ++n;
457 : }
458 : return n;
459 : }
460 :
461 : } // detail
462 :
463 : /** Return the number of buffer elements in a sequence.
464 :
465 : Counts the number of individual buffer objects, not bytes.
466 : For a single buffer, returns 1. For a range, returns the
467 : distance from `begin` to `end`.
468 :
469 : @param bs The buffer sequence.
470 :
471 : @return The number of buffers in `bs`.
472 :
473 : @see buffer_size
474 : */
475 : template<ConstBufferSequence CB>
476 : std::size_t
477 11 : buffer_length(CB const& bs)
478 : {
479 11 : return detail::length_impl(
480 11 : begin(bs), end(bs), 0);
481 : }
482 :
483 : /// Names `mutable_buffer` for a mutable sequence, `const_buffer` otherwise.
484 : template<typename BS>
485 : using buffer_type = std::conditional_t<
486 : MutableBufferSequence<BS>,
487 : mutable_buffer, const_buffer>;
488 :
489 : } // capy
490 : } // boost
491 :
492 : #endif
|