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 : /// Tag type for customizing `buffer_size` via `tag_invoke`.
36 : struct size_tag {};
37 :
38 : /// Tag type for customizing slice operations via `tag_invoke`.
39 : struct slice_tag {};
40 :
41 : /** Constants for slice customization.
42 :
43 : Passed to `tag_invoke` overloads to specify which portion
44 : of a buffer sequence to retain.
45 : */
46 : enum class slice_how
47 : {
48 : /// Remove bytes from the front of the sequence.
49 : remove_prefix,
50 :
51 : /// Keep only the first N bytes.
52 : keep_prefix
53 : };
54 :
55 : /** A reference to a contiguous region of writable memory.
56 :
57 : Represents a pointer and size pair for a modifiable byte range.
58 : Does not own the memory. Satisfies `MutableBufferSequence` (as a
59 : single-element sequence) and is implicitly convertible to
60 : `const_buffer`.
61 :
62 : @see const_buffer, MutableBufferSequence
63 : */
64 : class mutable_buffer
65 : {
66 : unsigned char* p_ = nullptr;
67 : std::size_t n_ = 0;
68 :
69 : public:
70 : /// Construct an empty buffer.
71 HIT 603 : mutable_buffer() = default;
72 :
73 : /// Construct a copy.
74 : mutable_buffer(
75 : mutable_buffer const&) = default;
76 :
77 : /// Assign by copying.
78 : mutable_buffer& operator=(
79 : mutable_buffer const&) = default;
80 :
81 : /// Construct from pointer and size.
82 29024 : constexpr mutable_buffer(
83 : void* data, std::size_t size) noexcept
84 29024 : : p_(static_cast<unsigned char*>(data))
85 29024 : , n_(size)
86 : {
87 29024 : }
88 :
89 : /// Return a pointer to the memory region.
90 72309 : constexpr void* data() const noexcept
91 : {
92 72309 : return p_;
93 : }
94 :
95 : /// Return the size in bytes.
96 111912 : constexpr std::size_t size() const noexcept
97 : {
98 111912 : return n_;
99 : }
100 :
101 : /** Advance the buffer start, shrinking the region.
102 :
103 : @param n Bytes to skip. Clamped to `size()`.
104 : */
105 : mutable_buffer&
106 26490 : operator+=(std::size_t n) noexcept
107 : {
108 26490 : if( n > n_)
109 17 : n = n_;
110 26490 : p_ += n;
111 26490 : n_ -= n;
112 26490 : return *this;
113 : }
114 :
115 : /// Slice customization point for `tag_invoke`.
116 : friend
117 : void
118 1335 : tag_invoke(
119 : slice_tag const&,
120 : mutable_buffer& b,
121 : slice_how how,
122 : std::size_t n) noexcept
123 : {
124 1335 : b.do_slice(how, n);
125 1335 : }
126 :
127 : private:
128 1335 : void do_slice(
129 : slice_how how, std::size_t n) noexcept
130 : {
131 1335 : switch(how)
132 : {
133 659 : case slice_how::remove_prefix:
134 659 : *this += n;
135 659 : return;
136 :
137 676 : case slice_how::keep_prefix:
138 676 : if( n < n_)
139 584 : n_ = n;
140 676 : return;
141 : }
142 : }
143 : };
144 :
145 : /** A reference to a contiguous region of read-only memory.
146 :
147 : Represents a pointer and size pair for a non-modifiable byte range.
148 : Does not own the memory. Satisfies `ConstBufferSequence` (as a
149 : single-element sequence). Implicitly constructible from
150 : `mutable_buffer`.
151 :
152 : @see mutable_buffer, ConstBufferSequence
153 : */
154 : class const_buffer
155 : {
156 : unsigned char const* p_ = nullptr;
157 : std::size_t n_ = 0;
158 :
159 : public:
160 : /// Construct an empty buffer.
161 631 : const_buffer() = default;
162 :
163 : /// Construct a copy.
164 : const_buffer(const_buffer const&) = default;
165 :
166 : /// Assign by copying.
167 : const_buffer& operator=(
168 : const_buffer const& other) = default;
169 :
170 : /// Construct from pointer and size.
171 24993 : constexpr const_buffer(
172 : void const* data, std::size_t size) noexcept
173 24993 : : p_(static_cast<unsigned char const*>(data))
174 24993 : , n_(size)
175 : {
176 24993 : }
177 :
178 : /// Construct from mutable_buffer.
179 16679 : constexpr const_buffer(
180 : mutable_buffer const& b) noexcept
181 16679 : : p_(static_cast<unsigned char const*>(b.data()))
182 16679 : , n_(b.size())
183 : {
184 16679 : }
185 :
186 : /// Return a pointer to the memory region.
187 65130 : constexpr void const* data() const noexcept
188 : {
189 65130 : return p_;
190 : }
191 :
192 : /// Return the size in bytes.
193 126623 : constexpr std::size_t size() const noexcept
194 : {
195 126623 : return n_;
196 : }
197 :
198 : /** Advance the buffer start, shrinking the region.
199 :
200 : @param n Bytes to skip. Clamped to `size()`.
201 : */
202 : const_buffer&
203 27145 : operator+=(std::size_t n) noexcept
204 : {
205 27145 : if( n > n_)
206 16 : n = n_;
207 27145 : p_ += n;
208 27145 : n_ -= n;
209 27145 : return *this;
210 : }
211 :
212 : /// Slice customization point for `tag_invoke`.
213 : friend
214 : void
215 2640 : tag_invoke(
216 : slice_tag const&,
217 : const_buffer& b,
218 : slice_how how,
219 : std::size_t n) noexcept
220 : {
221 2640 : b.do_slice(how, n);
222 2640 : }
223 :
224 : private:
225 2640 : void do_slice(
226 : slice_how how, std::size_t n) noexcept
227 : {
228 2640 : switch(how)
229 : {
230 1313 : case slice_how::remove_prefix:
231 1313 : *this += n;
232 1313 : return;
233 :
234 1327 : case slice_how::keep_prefix:
235 1327 : if( n < n_)
236 1238 : n_ = n;
237 1327 : return;
238 : }
239 : }
240 : };
241 :
242 : /** Concept for sequences of read-only buffer regions.
243 :
244 : A type satisfies `ConstBufferSequence` if it represents one or more
245 : contiguous memory regions that can be read. This includes single
246 : buffers (convertible to `const_buffer`) and ranges of buffers.
247 :
248 : @par Syntactic Requirements
249 : @li Convertible to `const_buffer`, OR
250 : @li A bidirectional range with value type convertible to `const_buffer`
251 :
252 : @see const_buffer, MutableBufferSequence
253 : */
254 : template<typename T>
255 : concept ConstBufferSequence =
256 : std::is_convertible_v<T, const_buffer> || (
257 : std::ranges::bidirectional_range<T> &&
258 : std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
259 :
260 : /** Concept for sequences of writable buffer regions.
261 :
262 : A type satisfies `MutableBufferSequence` if it represents one or more
263 : contiguous memory regions that can be written. This includes single
264 : buffers (convertible to `mutable_buffer`) and ranges of buffers.
265 : Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
266 :
267 : @par Syntactic Requirements
268 : @li Convertible to `mutable_buffer`, OR
269 : @li A bidirectional range with value type convertible to `mutable_buffer`
270 :
271 : @see mutable_buffer, ConstBufferSequence
272 : */
273 : template<typename T>
274 : concept MutableBufferSequence =
275 : std::is_convertible_v<T, mutable_buffer> || (
276 : std::ranges::bidirectional_range<T> &&
277 : std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
278 :
279 : /** Return an iterator to the first buffer in a sequence.
280 :
281 : Handles single buffers and ranges uniformly. For a single buffer,
282 : returns a pointer to it (forming a one-element range).
283 : */
284 : constexpr struct begin_mrdocs_workaround_t
285 : {
286 : template<std::convertible_to<const_buffer> ConvertibleToBuffer>
287 21798 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
288 : {
289 21798 : return std::addressof(b);
290 : }
291 :
292 : template<ConstBufferSequence BS>
293 : requires (!std::convertible_to<BS, const_buffer>)
294 56174 : auto operator()(BS const& bs) const noexcept
295 : {
296 56174 : return std::ranges::begin(bs);
297 : }
298 :
299 : template<ConstBufferSequence BS>
300 : requires (!std::convertible_to<BS, const_buffer>)
301 18574 : auto operator()(BS& bs) const noexcept
302 : {
303 18574 : return std::ranges::begin(bs);
304 : }
305 : } begin {};
306 :
307 : /** Return an iterator past the last buffer in a sequence.
308 :
309 : Handles single buffers and ranges uniformly. For a single buffer,
310 : returns a pointer one past it.
311 : */
312 : constexpr struct end_mrdocs_workaround_t
313 : {
314 : template<std::convertible_to<const_buffer> ConvertibleToBuffer>
315 21558 : auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
316 : {
317 21558 : return std::addressof(b) + 1;
318 : }
319 :
320 : template<ConstBufferSequence BS>
321 : requires (!std::convertible_to<BS, const_buffer>)
322 48645 : auto operator()(BS const& bs) const noexcept
323 : {
324 48645 : return std::ranges::end(bs);
325 : }
326 :
327 : template<ConstBufferSequence BS>
328 : requires (!std::convertible_to<BS, const_buffer>)
329 18574 : auto operator()(BS& bs) const noexcept
330 : {
331 18574 : return std::ranges::end(bs);
332 : }
333 : } end {};
334 :
335 : template<ConstBufferSequence CB>
336 : std::size_t
337 13742 : tag_invoke(
338 : size_tag const&,
339 : CB const& bs) noexcept
340 : {
341 13742 : std::size_t n = 0;
342 13742 : auto const e = end(bs);
343 34629 : for(auto it = begin(bs); it != e; ++it)
344 20887 : n += const_buffer(*it).size();
345 13742 : return n;
346 : }
347 :
348 : /** Return the total byte count across all buffers in a sequence.
349 :
350 : Sums the `size()` of each buffer in the sequence. This differs
351 : from `buffer_length` which counts the number of buffer elements.
352 :
353 : @par Example
354 : @code
355 : std::array<mutable_buffer, 2> bufs = { ... };
356 : std::size_t total = buffer_size( bufs ); // sum of both sizes
357 : @endcode
358 : */
359 : constexpr struct buffer_size_mrdocs_workaround_t
360 : {
361 : template<ConstBufferSequence CB>
362 19241 : constexpr std::size_t operator()(
363 : CB const& bs) const noexcept
364 : {
365 19241 : return tag_invoke(size_tag{}, bs);
366 : }
367 : } buffer_size {};
368 :
369 : /** Check if a buffer sequence contains no data.
370 :
371 : @return `true` if all buffers have size zero or the sequence
372 : is empty.
373 : */
374 : constexpr struct buffer_empty_mrdocs_workaround_t
375 : {
376 : template<ConstBufferSequence CB>
377 4156 : constexpr bool operator()(
378 : CB const& bs) const noexcept
379 : {
380 4156 : auto it = begin(bs);
381 4156 : auto const end_ = end(bs);
382 4211 : while(it != end_)
383 : {
384 4169 : const_buffer b(*it++);
385 4169 : if(b.size() != 0)
386 4114 : return false;
387 : }
388 42 : return true;
389 : }
390 : } buffer_empty {};
391 :
392 : namespace detail {
393 :
394 : template<class It>
395 : auto
396 240 : length_impl(It first, It last, int)
397 : -> decltype(static_cast<std::size_t>(last - first))
398 : {
399 240 : return static_cast<std::size_t>(last - first);
400 : }
401 :
402 : template<class It>
403 : std::size_t
404 : length_impl(It first, It last, long)
405 : {
406 : std::size_t n = 0;
407 : while(first != last)
408 : {
409 : ++first;
410 : ++n;
411 : }
412 : return n;
413 : }
414 :
415 : } // detail
416 :
417 : /** Return the number of buffer elements in a sequence.
418 :
419 : Counts the number of individual buffer objects, not bytes.
420 : For a single buffer, returns 1. For a range, returns the
421 : distance from `begin` to `end`.
422 :
423 : @see buffer_size
424 : */
425 : template<ConstBufferSequence CB>
426 : std::size_t
427 240 : buffer_length(CB const& bs)
428 : {
429 240 : return detail::length_impl(
430 240 : begin(bs), end(bs), 0);
431 : }
432 :
433 : /// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
434 : template<typename BS>
435 : using buffer_type = std::conditional_t<
436 : MutableBufferSequence<BS>,
437 : mutable_buffer, const_buffer>;
438 :
439 : } // capy
440 : } // boost
441 :
442 : #endif
|