TLA 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 : /** A reference to a contiguous region of writable memory.
36 :
37 : Represents a pointer and size pair for a modifiable byte range.
38 : Does not own the memory. Satisfies `MutableBufferSequence` (as a
39 : single-element sequence) and is implicitly convertible to
40 : `const_buffer`.
41 :
42 : @see const_buffer, MutableBufferSequence
43 : */
44 : class mutable_buffer
45 : {
46 : unsigned char* p_ = nullptr;
47 : std::size_t n_ = 0;
48 :
49 : public:
50 : /// Construct an empty buffer.
51 HIT 29 : mutable_buffer() = default;
52 :
53 : /// Construct a copy.
54 : mutable_buffer(
55 : mutable_buffer const&) = default;
56 :
57 : /// Assign by copying.
58 : mutable_buffer& operator=(
59 : mutable_buffer const&) = default;
60 :
61 : /// Construct from pointer and size.
62 42582 : constexpr mutable_buffer(
63 : void* data, std::size_t size) noexcept
64 42582 : : p_(static_cast<unsigned char*>(data))
65 42582 : , n_(size)
66 : {
67 42582 : }
68 :
69 : /// Return a pointer to the memory region.
70 62337 : constexpr void* data() const noexcept
71 : {
72 62337 : return p_;
73 : }
74 :
75 : /// Return the size in bytes.
76 97996 : constexpr std::size_t size() const noexcept
77 : {
78 97996 : return n_;
79 : }
80 :
81 : /** Advance the buffer start, shrinking the region.
82 :
83 : @param n Bytes to skip. Clamped to `size()`.
84 : */
85 : mutable_buffer&
86 19809 : operator+=(std::size_t n) noexcept
87 : {
88 19809 : if( n > n_)
89 1 : n = n_;
90 19809 : p_ += n;
91 19809 : n_ -= n;
92 19809 : return *this;
93 : }
94 : };
95 :
96 : /** A reference to a contiguous region of read-only memory.
97 :
98 : Represents a pointer and size pair for a non-modifiable byte range.
99 : Does not own the memory. Satisfies `ConstBufferSequence` (as a
100 : single-element sequence). Implicitly constructible from
101 : `mutable_buffer`.
102 :
103 : @see mutable_buffer, ConstBufferSequence
104 : */
105 : class const_buffer
106 : {
107 : unsigned char const* p_ = nullptr;
108 : std::size_t n_ = 0;
109 :
110 : public:
111 : /// Construct an empty buffer.
112 57 : const_buffer() = default;
113 :
114 : /// Construct a copy.
115 : const_buffer(const_buffer const&) = default;
116 :
117 : /// Assign by copying.
118 : const_buffer& operator=(
119 : const_buffer const& other) = default;
120 :
121 : /// Construct from pointer and size.
122 43428 : constexpr const_buffer(
123 : void const* data, std::size_t size) noexcept
124 43428 : : p_(static_cast<unsigned char const*>(data))
125 43428 : , n_(size)
126 : {
127 43428 : }
128 :
129 : /// Construct from mutable_buffer.
130 12005 : constexpr const_buffer(
131 : mutable_buffer const& b) noexcept
132 12005 : : p_(static_cast<unsigned char const*>(b.data()))
133 12005 : , n_(b.size())
134 : {
135 12005 : }
136 :
137 : /// Return a pointer to the memory region.
138 59971 : constexpr void const* data() const noexcept
139 : {
140 59971 : return p_;
141 : }
142 :
143 : /// Return the size in bytes.
144 112905 : constexpr std::size_t size() const noexcept
145 : {
146 112905 : return n_;
147 : }
148 :
149 : /** Advance the buffer start, shrinking the region.
150 :
151 : @param n Bytes to skip. Clamped to `size()`.
152 : */
153 : const_buffer&
154 19810 : operator+=(std::size_t n) noexcept
155 : {
156 19810 : if( n > n_)
157 1 : n = n_;
158 19810 : p_ += n;
159 19810 : n_ -= n;
160 19810 : return *this;
161 : }
162 : };
163 :
164 : /** Concept for sequences of read-only buffer regions.
165 :
166 : A type satisfies `ConstBufferSequence` if it represents one or more
167 : contiguous memory regions that can be read. This includes single
168 : buffers (convertible to `const_buffer`) and ranges of buffers.
169 :
170 : @par Syntactic Requirements
171 : @li Convertible to `const_buffer`, OR
172 : @li A bidirectional range with value type convertible to `const_buffer`
173 :
174 : @see const_buffer, MutableBufferSequence
175 : */
176 : template<typename T>
177 : concept ConstBufferSequence =
178 : std::is_convertible_v<T, const_buffer> || (
179 : std::ranges::bidirectional_range<T> &&
180 : std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
181 :
182 : /** Concept for sequences of writable buffer regions.
183 :
184 : A type satisfies `MutableBufferSequence` if it represents one or more
185 : contiguous memory regions that can be written. This includes single
186 : buffers (convertible to `mutable_buffer`) and ranges of buffers.
187 : Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
188 :
189 : @par Syntactic Requirements
190 : @li Convertible to `mutable_buffer`, OR
191 : @li A bidirectional range with value type convertible to `mutable_buffer`
192 :
193 : @see mutable_buffer, ConstBufferSequence
194 : */
195 : template<typename T>
196 : concept MutableBufferSequence =
197 : std::is_convertible_v<T, mutable_buffer> || (
198 : std::ranges::bidirectional_range<T> &&
199 : std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
200 :
201 : /** Return an iterator to the first buffer in a sequence.
202 :
203 : @functionobject
204 : */
205 : constexpr struct
206 : {
207 : /** Return a pointer to a single buffer, forming a one-element range.
208 :
209 : @param b A single buffer.
210 :
211 : @return A pointer to `b`.
212 : */
213 : template<std::convertible_to<const_buffer> ConvertibleToBuffer>
214 13219 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
215 : {
216 13219 : return std::addressof(b);
217 : }
218 :
219 : /** Return an iterator to the first buffer of a sequence.
220 :
221 : @param bs The buffer sequence.
222 :
223 : @return An iterator to the first buffer of `bs`.
224 : */
225 : template<ConstBufferSequence BS>
226 : requires (!std::convertible_to<BS, const_buffer>)
227 46371 : auto operator()(BS const& bs) const noexcept
228 : {
229 46371 : return std::ranges::begin(bs);
230 : }
231 :
232 : /** Return an iterator to the first buffer of a sequence.
233 :
234 : @param bs The buffer sequence.
235 :
236 : @return An iterator to the first buffer of `bs`.
237 : */
238 : template<ConstBufferSequence BS>
239 : requires (!std::convertible_to<BS, const_buffer>)
240 9192 : auto operator()(BS& bs) const noexcept
241 : {
242 9192 : return std::ranges::begin(bs);
243 : }
244 : } begin {};
245 :
246 : /** Return an iterator past the last buffer in a sequence.
247 :
248 : @functionobject
249 : */
250 : constexpr struct
251 : {
252 : /** Return a pointer one past a single buffer, forming a one-element range.
253 :
254 : @param b A single buffer.
255 :
256 : @return A pointer one past `b`.
257 : */
258 : template<std::convertible_to<const_buffer> ConvertibleToBuffer>
259 12957 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
260 : {
261 12957 : return std::addressof(b) + 1;
262 : }
263 :
264 : /** Return an iterator past the last buffer of a sequence.
265 :
266 : @param bs The buffer sequence.
267 :
268 : @return An iterator one past the last buffer of `bs`.
269 : */
270 : template<ConstBufferSequence BS>
271 : requires (!std::convertible_to<BS, const_buffer>)
272 46371 : auto operator()(BS const& bs) const noexcept
273 : {
274 46371 : return std::ranges::end(bs);
275 : }
276 :
277 : /** Return an iterator past the last buffer of a sequence.
278 :
279 : @param bs The buffer sequence.
280 :
281 : @return An iterator one past the last buffer of `bs`.
282 : */
283 : template<ConstBufferSequence BS>
284 : requires (!std::convertible_to<BS, const_buffer>)
285 9192 : auto operator()(BS& bs) const noexcept
286 : {
287 9192 : return std::ranges::end(bs);
288 : }
289 : } end {};
290 :
291 : /** Return the total byte count across all buffers in a sequence.
292 :
293 : @functionobject
294 : */
295 : constexpr struct
296 : {
297 : // GCC 13 falsely flags reads of arr_[i].n_ in detail::buffer_array
298 : // when iterating here. The class uses union storage with placement
299 : // new for slots 0..n_-1, so reads inside this bounded loop are
300 : // well-defined, but the optimizer can't prove the loop bound and
301 : // warns. The runtime cost of value-initializing all N slots is
302 : // non-trivial for non-trivial value types, so we suppress instead.
303 : #if defined(__GNUC__) && !defined(__clang__)
304 : #pragma GCC diagnostic push
305 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
306 : #endif
307 : /** Return the total byte count across all buffers in a sequence.
308 :
309 : Sums the `size()` of each buffer in the sequence. This differs
310 : from `buffer_length` which counts the number of buffer elements.
311 :
312 : @param bs The buffer sequence.
313 :
314 : @return The sum of the sizes of all buffers in `bs`.
315 :
316 : @par Example
317 : @code
318 : std::array<mutable_buffer, 2> bufs = { ... };
319 : std::size_t total = buffer_size( bufs ); // sum of both sizes
320 : @endcode
321 : */
322 : template<ConstBufferSequence CB>
323 12825 : constexpr std::size_t operator()(
324 : CB const& bs) const noexcept
325 : {
326 12825 : std::size_t n = 0;
327 12825 : auto const e = capy::end(bs);
328 27177 : for(auto it = capy::begin(bs); it != e; ++it)
329 14352 : n += const_buffer(*it).size();
330 12825 : return n;
331 : }
332 : #if defined(__GNUC__) && !defined(__clang__)
333 : #pragma GCC diagnostic pop
334 : #endif
335 : } buffer_size {};
336 :
337 : /** Check if a buffer sequence contains no data.
338 :
339 : @functionobject
340 : */
341 : constexpr struct
342 : {
343 : // See note on buffer_size above — same union-storage false positive.
344 : #if defined(__GNUC__) && !defined(__clang__)
345 : #pragma GCC diagnostic push
346 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
347 : #endif
348 : /** Check if a buffer sequence contains no data.
349 :
350 : @param bs The buffer sequence.
351 :
352 : @return `true` if all buffers have size zero or the sequence
353 : is empty.
354 : */
355 : template<ConstBufferSequence CB>
356 4263 : constexpr bool operator()(
357 : CB const& bs) const noexcept
358 : {
359 4263 : auto it = begin(bs);
360 4263 : auto const end_ = end(bs);
361 4304 : while(it != end_)
362 : {
363 4264 : const_buffer b(*it++);
364 4264 : if(b.size() != 0)
365 4223 : return false;
366 : }
367 40 : return true;
368 : }
369 : #if defined(__GNUC__) && !defined(__clang__)
370 : #pragma GCC diagnostic pop
371 : #endif
372 : } buffer_empty {};
373 :
374 : namespace detail {
375 :
376 : template<class It>
377 : auto
378 263 : length_impl(It first, It last, int)
379 : -> decltype(static_cast<std::size_t>(last - first))
380 : {
381 263 : return static_cast<std::size_t>(last - first);
382 : }
383 :
384 : template<class It>
385 : std::size_t
386 : length_impl(It first, It last, long)
387 : {
388 : std::size_t n = 0;
389 : while(first != last)
390 : {
391 : ++first;
392 : ++n;
393 : }
394 : return n;
395 : }
396 :
397 : } // detail
398 :
399 : /** Return the number of buffer elements in a sequence.
400 :
401 : Counts the number of individual buffer objects, not bytes.
402 : For a single buffer, returns 1. For a range, returns the
403 : distance from `begin` to `end`.
404 :
405 : @see buffer_size
406 : */
407 : template<ConstBufferSequence CB>
408 : std::size_t
409 263 : buffer_length(CB const& bs)
410 : {
411 263 : return detail::length_impl(
412 263 : begin(bs), end(bs), 0);
413 : }
414 :
415 : /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
416 : template<typename BS>
417 : using buffer_type = std::conditional_t<
418 : MutableBufferSequence<BS>,
419 : mutable_buffer, const_buffer>;
420 :
421 : } // capy
422 : } // boost
423 :
424 : #endif
|