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