1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_CAPY_BUFFERS_HPP
10  
#ifndef BOOST_CAPY_BUFFERS_HPP
11  
#define BOOST_CAPY_BUFFERS_HPP
11  
#define BOOST_CAPY_BUFFERS_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <concepts>
14  
#include <concepts>
15  
#include <cstddef>
15  
#include <cstddef>
16  
#include <iterator>
16  
#include <iterator>
17  
#include <memory>
17  
#include <memory>
18  
#include <ranges>
18  
#include <ranges>
19  
#include <type_traits>
19  
#include <type_traits>
20  

20  

21  
// https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
21  
// https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html
22  

22  

23  
namespace boost {
23  
namespace boost {
24  

24  

25  
namespace asio {
25  
namespace asio {
26  
class const_buffer;
26  
class const_buffer;
27  
class mutable_buffer;
27  
class mutable_buffer;
28  
} // asio
28  
} // asio
29  

29  

30  
namespace capy {
30  
namespace capy {
31  

31  

32  
class const_buffer;
32  
class const_buffer;
33  
class mutable_buffer;
33  
class mutable_buffer;
34  

34  

35  
namespace detail {
35  
namespace detail {
36  

36  

37  
// satisfies Asio's buffer constructors, CANNOT be removed!
37  
// satisfies Asio's buffer constructors, CANNOT be removed!
38  
template<class T, std::size_t Extent = (std::size_t)(-1)>
38  
template<class T, std::size_t Extent = (std::size_t)(-1)>
39  
class basic_buffer
39  
class basic_buffer
40  
{
40  
{
41  
    constexpr auto data() const noexcept ->
41  
    constexpr auto data() const noexcept ->
42  
        std::conditional_t<std::is_const_v<T>, void const*, void*>
42  
        std::conditional_t<std::is_const_v<T>, void const*, void*>
43  
    {
43  
    {
44  
        return p_;
44  
        return p_;
45  
    }
45  
    }
46  

46  

47  
    constexpr std::size_t size() const noexcept
47  
    constexpr std::size_t size() const noexcept
48  
    {
48  
    {
49  
        return n_;
49  
        return n_;
50  
    }
50  
    }
51  

51  

52  
    friend class capy::const_buffer;
52  
    friend class capy::const_buffer;
53  
    friend class capy::mutable_buffer;
53  
    friend class capy::mutable_buffer;
54  
    friend class asio::const_buffer;
54  
    friend class asio::const_buffer;
55  
    friend class asio::mutable_buffer;
55  
    friend class asio::mutable_buffer;
56  
    basic_buffer() = default;
56  
    basic_buffer() = default;
57  
    constexpr basic_buffer(T* p, std::size_t n) noexcept : p_(p), n_(n) {}
57  
    constexpr basic_buffer(T* p, std::size_t n) noexcept : p_(p), n_(n) {}
58  
    constexpr basic_buffer<T, (std::size_t)(-1)> subspan(
58  
    constexpr basic_buffer<T, (std::size_t)(-1)> subspan(
59  
        std::size_t, std::size_t = (std::size_t)(-1)) const noexcept;
59  
        std::size_t, std::size_t = (std::size_t)(-1)) const noexcept;
60  

60  

61  
    T* p_ = nullptr;
61  
    T* p_ = nullptr;
62  
    std::size_t n_ = 0;
62  
    std::size_t n_ = 0;
63  
};
63  
};
64  

64  

65  
} // detail
65  
} // detail
66  

66  

67  
//------------------------------------------------
67  
//------------------------------------------------
68  

68  

69  
/// Tag type for customizing `buffer_size` via `tag_invoke`.
69  
/// Tag type for customizing `buffer_size` via `tag_invoke`.
70  
struct size_tag {};
70  
struct size_tag {};
71  

71  

72  
/// Tag type for customizing slice operations via `tag_invoke`.
72  
/// Tag type for customizing slice operations via `tag_invoke`.
73  
struct slice_tag {};
73  
struct slice_tag {};
74  

74  

75  
/** Constants for slice customization.
75  
/** Constants for slice customization.
76  

76  

77  
    Passed to `tag_invoke` overloads to specify which portion
77  
    Passed to `tag_invoke` overloads to specify which portion
78  
    of a buffer sequence to retain.
78  
    of a buffer sequence to retain.
79  
*/
79  
*/
80  
enum class slice_how
80  
enum class slice_how
81  
{
81  
{
82  
    /// Remove bytes from the front of the sequence.
82  
    /// Remove bytes from the front of the sequence.
83  
    remove_prefix,
83  
    remove_prefix,
84  

84  

85  
    /// Keep only the first N bytes.
85  
    /// Keep only the first N bytes.
86  
    keep_prefix
86  
    keep_prefix
87  
};
87  
};
88  

88  

89  
//------------------------------------------------
89  
//------------------------------------------------
90  

90  

91  
/** A reference to a contiguous region of writable memory.
91  
/** A reference to a contiguous region of writable memory.
92  

92  

93  
    Represents a pointer and size pair for a modifiable byte range.
93  
    Represents a pointer and size pair for a modifiable byte range.
94  
    Does not own the memory. Satisfies `MutableBufferSequence` (as a
94  
    Does not own the memory. Satisfies `MutableBufferSequence` (as a
95  
    single-element sequence) and is implicitly convertible to
95  
    single-element sequence) and is implicitly convertible to
96  
    `const_buffer`.
96  
    `const_buffer`.
97  

97  

98  
    @see const_buffer, MutableBufferSequence
98  
    @see const_buffer, MutableBufferSequence
99  
*/
99  
*/
100  
class mutable_buffer
100  
class mutable_buffer
101  
    : public detail::basic_buffer<unsigned char>
101  
    : public detail::basic_buffer<unsigned char>
102  
{
102  
{
103  
public:
103  
public:
104  
    /// Construct an empty buffer.
104  
    /// Construct an empty buffer.
105  
    mutable_buffer() = default;
105  
    mutable_buffer() = default;
106  

106  

107  
    /// Copy constructor.
107  
    /// Copy constructor.
108  
    mutable_buffer(
108  
    mutable_buffer(
109  
        mutable_buffer const&) = default;
109  
        mutable_buffer const&) = default;
110  

110  

111  
    /// Copy assignment.
111  
    /// Copy assignment.
112  
    mutable_buffer& operator=(
112  
    mutable_buffer& operator=(
113  
        mutable_buffer const&) = default;
113  
        mutable_buffer const&) = default;
114  

114  

115  
    /// Construct from pointer and size.
115  
    /// Construct from pointer and size.
116  
    constexpr mutable_buffer(
116  
    constexpr mutable_buffer(
117  
        void* data, std::size_t size) noexcept
117  
        void* data, std::size_t size) noexcept
118  
        : basic_buffer<unsigned char>(
118  
        : basic_buffer<unsigned char>(
119  
            static_cast<unsigned char*>(data), size)
119  
            static_cast<unsigned char*>(data), size)
120  
    {
120  
    {
121  
    }
121  
    }
122  

122  

123  
    /// Construct from Asio mutable_buffer.
123  
    /// Construct from Asio mutable_buffer.
124  
    template<class MutableBuffer>
124  
    template<class MutableBuffer>
125  
        requires std::same_as<MutableBuffer, asio::mutable_buffer>
125  
        requires std::same_as<MutableBuffer, asio::mutable_buffer>
126  
    constexpr mutable_buffer(
126  
    constexpr mutable_buffer(
127  
        MutableBuffer const& b) noexcept
127  
        MutableBuffer const& b) noexcept
128  
        : basic_buffer<unsigned char>(
128  
        : basic_buffer<unsigned char>(
129  
            static_cast<unsigned char*>(
129  
            static_cast<unsigned char*>(
130  
                b.data()), b.size())
130  
                b.data()), b.size())
131  
    {
131  
    {
132  
    }
132  
    }
133  

133  

134  
    /// Return a pointer to the memory region.
134  
    /// Return a pointer to the memory region.
135  
    constexpr void* data() const noexcept
135  
    constexpr void* data() const noexcept
136  
    {
136  
    {
137  
        return p_;
137  
        return p_;
138  
    }
138  
    }
139  

139  

140  
    /// Return the size in bytes.
140  
    /// Return the size in bytes.
141  
    constexpr std::size_t size() const noexcept
141  
    constexpr std::size_t size() const noexcept
142  
    {
142  
    {
143  
        return n_;
143  
        return n_;
144  
    }
144  
    }
145  

145  

146  
    /** Advance the buffer start, shrinking the region.
146  
    /** Advance the buffer start, shrinking the region.
147  

147  

148  
        @param n Bytes to skip. Clamped to `size()`.
148  
        @param n Bytes to skip. Clamped to `size()`.
149  
    */
149  
    */
150  
    mutable_buffer&
150  
    mutable_buffer&
151  
    operator+=(std::size_t n) noexcept
151  
    operator+=(std::size_t n) noexcept
152  
    {
152  
    {
153  
        if( n > n_)
153  
        if( n > n_)
154  
            n = n_;
154  
            n = n_;
155  
        p_ += n;
155  
        p_ += n;
156  
        n_ -= n;
156  
        n_ -= n;
157  
        return *this;
157  
        return *this;
158  
    }
158  
    }
159  

159  

160  
    /// Slice customization point for `tag_invoke`.
160  
    /// Slice customization point for `tag_invoke`.
161  
    friend
161  
    friend
162  
    void
162  
    void
163  
    tag_invoke(
163  
    tag_invoke(
164  
        slice_tag const&,
164  
        slice_tag const&,
165  
        mutable_buffer& b,
165  
        mutable_buffer& b,
166  
        slice_how how,
166  
        slice_how how,
167  
        std::size_t n) noexcept
167  
        std::size_t n) noexcept
168  
    {
168  
    {
169  
        b.do_slice(how, n);
169  
        b.do_slice(how, n);
170  
    }
170  
    }
171  

171  

172  
private:
172  
private:
173  
    void do_slice(
173  
    void do_slice(
174  
        slice_how how, std::size_t n) noexcept
174  
        slice_how how, std::size_t n) noexcept
175  
    {
175  
    {
176  
        switch(how)
176  
        switch(how)
177  
        {
177  
        {
178  
        case slice_how::remove_prefix:
178  
        case slice_how::remove_prefix:
179  
            *this += n;
179  
            *this += n;
180  
            return;
180  
            return;
181  

181  

182  
        case slice_how::keep_prefix:
182  
        case slice_how::keep_prefix:
183  
            if( n < n_)
183  
            if( n < n_)
184  
                n_ = n;
184  
                n_ = n;
185  
            return;
185  
            return;
186  
        }
186  
        }
187  
    }
187  
    }
188  
};
188  
};
189  

189  

190  
//------------------------------------------------
190  
//------------------------------------------------
191  

191  

192  
/** A reference to a contiguous region of read-only memory.
192  
/** A reference to a contiguous region of read-only memory.
193  

193  

194  
    Represents a pointer and size pair for a non-modifiable byte range.
194  
    Represents a pointer and size pair for a non-modifiable byte range.
195  
    Does not own the memory. Satisfies `ConstBufferSequence` (as a
195  
    Does not own the memory. Satisfies `ConstBufferSequence` (as a
196  
    single-element sequence). Implicitly constructible from
196  
    single-element sequence). Implicitly constructible from
197  
    `mutable_buffer`.
197  
    `mutable_buffer`.
198  

198  

199  
    @see mutable_buffer, ConstBufferSequence
199  
    @see mutable_buffer, ConstBufferSequence
200  
*/
200  
*/
201  
class const_buffer
201  
class const_buffer
202  
    : public detail::basic_buffer<unsigned char const>
202  
    : public detail::basic_buffer<unsigned char const>
203  
{
203  
{
204  
public:
204  
public:
205  
    /// Construct an empty buffer.
205  
    /// Construct an empty buffer.
206  
    const_buffer() = default;
206  
    const_buffer() = default;
207  

207  

208  
    /// Copy constructor.
208  
    /// Copy constructor.
209  
    const_buffer(const_buffer const&) = default;
209  
    const_buffer(const_buffer const&) = default;
210  

210  

211  
    /// Copy assignment.
211  
    /// Copy assignment.
212  
    const_buffer& operator=(
212  
    const_buffer& operator=(
213  
        const_buffer const& other) = default;
213  
        const_buffer const& other) = default;
214  

214  

215  
    /// Construct from pointer and size.
215  
    /// Construct from pointer and size.
216  
    constexpr const_buffer(
216  
    constexpr const_buffer(
217  
        void const* data, std::size_t size) noexcept
217  
        void const* data, std::size_t size) noexcept
218  
        : basic_buffer<unsigned char const>(
218  
        : basic_buffer<unsigned char const>(
219  
            static_cast<unsigned char const*>(data), size)
219  
            static_cast<unsigned char const*>(data), size)
220  
    {
220  
    {
221  
    }
221  
    }
222  

222  

223  
    /// Construct from mutable_buffer.
223  
    /// Construct from mutable_buffer.
224  
    constexpr const_buffer(
224  
    constexpr const_buffer(
225  
        mutable_buffer const& b) noexcept
225  
        mutable_buffer const& b) noexcept
226  
        : basic_buffer<unsigned char const>(
226  
        : basic_buffer<unsigned char const>(
227  
            static_cast<unsigned char const*>(b.data()), b.size())
227  
            static_cast<unsigned char const*>(b.data()), b.size())
228  
    {
228  
    {
229  
    }
229  
    }
230  

230  

231  
    /// Construct from Asio buffer types.
231  
    /// Construct from Asio buffer types.
232  
    template<class ConstBuffer>
232  
    template<class ConstBuffer>
233  
        requires (std::same_as<ConstBuffer, asio::const_buffer> ||
233  
        requires (std::same_as<ConstBuffer, asio::const_buffer> ||
234  
                  std::same_as<ConstBuffer, asio::mutable_buffer>)
234  
                  std::same_as<ConstBuffer, asio::mutable_buffer>)
235  
    constexpr const_buffer(
235  
    constexpr const_buffer(
236  
        ConstBuffer const& b) noexcept
236  
        ConstBuffer const& b) noexcept
237  
        : basic_buffer<unsigned char const>(
237  
        : basic_buffer<unsigned char const>(
238  
            static_cast<unsigned char const*>(
238  
            static_cast<unsigned char const*>(
239  
                b.data()), b.size())
239  
                b.data()), b.size())
240  
    {
240  
    {
241  
    }
241  
    }
242  

242  

243  
    /// Return a pointer to the memory region.
243  
    /// Return a pointer to the memory region.
244  
    constexpr void const* data() const noexcept
244  
    constexpr void const* data() const noexcept
245  
    {
245  
    {
246  
        return p_;
246  
        return p_;
247  
    }
247  
    }
248  

248  

249  
    /// Return the size in bytes.
249  
    /// Return the size in bytes.
250  
    constexpr std::size_t size() const noexcept
250  
    constexpr std::size_t size() const noexcept
251  
    {
251  
    {
252  
        return n_;
252  
        return n_;
253  
    }
253  
    }
254  

254  

255  
    /** Advance the buffer start, shrinking the region.
255  
    /** Advance the buffer start, shrinking the region.
256  

256  

257  
        @param n Bytes to skip. Clamped to `size()`.
257  
        @param n Bytes to skip. Clamped to `size()`.
258  
    */
258  
    */
259  
    const_buffer&
259  
    const_buffer&
260  
    operator+=(std::size_t n) noexcept
260  
    operator+=(std::size_t n) noexcept
261  
    {
261  
    {
262  
        if( n > n_)
262  
        if( n > n_)
263  
            n = n_;
263  
            n = n_;
264  
        p_ += n;
264  
        p_ += n;
265  
        n_ -= n;
265  
        n_ -= n;
266  
        return *this;
266  
        return *this;
267  
    }
267  
    }
268  

268  

269  
    /// Slice customization point for `tag_invoke`.
269  
    /// Slice customization point for `tag_invoke`.
270  
    friend
270  
    friend
271  
    void
271  
    void
272  
    tag_invoke(
272  
    tag_invoke(
273  
        slice_tag const&,
273  
        slice_tag const&,
274  
        const_buffer& b,
274  
        const_buffer& b,
275  
        slice_how how,
275  
        slice_how how,
276  
        std::size_t n) noexcept
276  
        std::size_t n) noexcept
277  
    {
277  
    {
278  
        b.do_slice(how, n);
278  
        b.do_slice(how, n);
279  
    }
279  
    }
280  

280  

281  
private:
281  
private:
282  
    void do_slice(
282  
    void do_slice(
283  
        slice_how how, std::size_t n) noexcept
283  
        slice_how how, std::size_t n) noexcept
284  
    {
284  
    {
285  
        switch(how)
285  
        switch(how)
286  
        {
286  
        {
287  
        case slice_how::remove_prefix:
287  
        case slice_how::remove_prefix:
288  
            *this += n;
288  
            *this += n;
289  
            return;
289  
            return;
290  

290  

291  
        case slice_how::keep_prefix:
291  
        case slice_how::keep_prefix:
292  
            if( n < n_)
292  
            if( n < n_)
293  
                n_ = n;
293  
                n_ = n;
294  
            return;
294  
            return;
295  
        }
295  
        }
296  
    }
296  
    }
297  
};
297  
};
298  

298  

299  
//------------------------------------------------
299  
//------------------------------------------------
300  

300  

301  
/** Concept for sequences of read-only buffer regions.
301  
/** Concept for sequences of read-only buffer regions.
302  

302  

303  
    A type satisfies `ConstBufferSequence` if it represents one or more
303  
    A type satisfies `ConstBufferSequence` if it represents one or more
304  
    contiguous memory regions that can be read. This includes single
304  
    contiguous memory regions that can be read. This includes single
305  
    buffers (convertible to `const_buffer`) and ranges of buffers.
305  
    buffers (convertible to `const_buffer`) and ranges of buffers.
306  

306  

307  
    @par Syntactic Requirements
307  
    @par Syntactic Requirements
308  
    @li Convertible to `const_buffer`, OR
308  
    @li Convertible to `const_buffer`, OR
309  
    @li A bidirectional range with value type convertible to `const_buffer`
309  
    @li A bidirectional range with value type convertible to `const_buffer`
310  

310  

311  
    @see const_buffer, MutableBufferSequence
311  
    @see const_buffer, MutableBufferSequence
312  
*/
312  
*/
313  
template<typename T>
313  
template<typename T>
314  
concept ConstBufferSequence =
314  
concept ConstBufferSequence =
315  
    std::is_convertible_v<T, const_buffer> || (
315  
    std::is_convertible_v<T, const_buffer> || (
316  
        std::ranges::bidirectional_range<T> &&
316  
        std::ranges::bidirectional_range<T> &&
317  
        std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
317  
        std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
318  

318  

319  
/** Concept for sequences of writable buffer regions.
319  
/** Concept for sequences of writable buffer regions.
320  

320  

321  
    A type satisfies `MutableBufferSequence` if it represents one or more
321  
    A type satisfies `MutableBufferSequence` if it represents one or more
322  
    contiguous memory regions that can be written. This includes single
322  
    contiguous memory regions that can be written. This includes single
323  
    buffers (convertible to `mutable_buffer`) and ranges of buffers.
323  
    buffers (convertible to `mutable_buffer`) and ranges of buffers.
324  
    Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
324  
    Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
325  

325  

326  
    @par Syntactic Requirements
326  
    @par Syntactic Requirements
327  
    @li Convertible to `mutable_buffer`, OR
327  
    @li Convertible to `mutable_buffer`, OR
328  
    @li A bidirectional range with value type convertible to `mutable_buffer`
328  
    @li A bidirectional range with value type convertible to `mutable_buffer`
329  

329  

330  
    @see mutable_buffer, ConstBufferSequence
330  
    @see mutable_buffer, ConstBufferSequence
331  
*/
331  
*/
332  
template<typename T>
332  
template<typename T>
333  
concept MutableBufferSequence =
333  
concept MutableBufferSequence =
334  
    std::is_convertible_v<T, mutable_buffer> || (
334  
    std::is_convertible_v<T, mutable_buffer> || (
335  
        std::ranges::bidirectional_range<T> &&
335  
        std::ranges::bidirectional_range<T> &&
336  
        std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
336  
        std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
337  

337  

338  
//------------------------------------------------------------------------------
338  
//------------------------------------------------------------------------------
339  

339  

340  
/** Return an iterator to the first buffer in a sequence.
340  
/** Return an iterator to the first buffer in a sequence.
341  

341  

342  
    Handles single buffers and ranges uniformly. For a single buffer,
342  
    Handles single buffers and ranges uniformly. For a single buffer,
343  
    returns a pointer to it (forming a one-element range).
343  
    returns a pointer to it (forming a one-element range).
344  
*/
344  
*/
345  
constexpr struct begin_mrdocs_workaround_t
345  
constexpr struct begin_mrdocs_workaround_t
346  
{
346  
{
347  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
347  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
348  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
348  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
349  
    {
349  
    {
350  
        return std::addressof(b);
350  
        return std::addressof(b);
351  
    }
351  
    }
352  

352  

353  
    template<ConstBufferSequence BS>
353  
    template<ConstBufferSequence BS>
354  
        requires (!std::convertible_to<BS, const_buffer>)
354  
        requires (!std::convertible_to<BS, const_buffer>)
355  
    auto operator()(BS const& bs) const noexcept
355  
    auto operator()(BS const& bs) const noexcept
356  
    {
356  
    {
357  
        return std::ranges::begin(bs);
357  
        return std::ranges::begin(bs);
358  
    }
358  
    }
359  

359  

360  
    template<ConstBufferSequence BS>
360  
    template<ConstBufferSequence BS>
361  
        requires (!std::convertible_to<BS, const_buffer>)
361  
        requires (!std::convertible_to<BS, const_buffer>)
362  
    auto operator()(BS& bs) const noexcept
362  
    auto operator()(BS& bs) const noexcept
363  
    {
363  
    {
364  
        return std::ranges::begin(bs);
364  
        return std::ranges::begin(bs);
365  
    }
365  
    }
366  
} begin {};
366  
} begin {};
367  

367  

368  
/** Return an iterator past the last buffer in a sequence.
368  
/** Return an iterator past the last buffer in a sequence.
369  

369  

370  
    Handles single buffers and ranges uniformly. For a single buffer,
370  
    Handles single buffers and ranges uniformly. For a single buffer,
371  
    returns a pointer one past it.
371  
    returns a pointer one past it.
372  
*/
372  
*/
373  
constexpr struct end_mrdocs_workaround_t
373  
constexpr struct end_mrdocs_workaround_t
374  
{
374  
{
375  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
375  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
376  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
376  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
377  
    {
377  
    {
378  
        return std::addressof(b) + 1;
378  
        return std::addressof(b) + 1;
379  
    }
379  
    }
380  

380  

381  
    template<ConstBufferSequence BS>
381  
    template<ConstBufferSequence BS>
382  
        requires (!std::convertible_to<BS, const_buffer>)
382  
        requires (!std::convertible_to<BS, const_buffer>)
383  
    auto operator()(BS const& bs) const noexcept
383  
    auto operator()(BS const& bs) const noexcept
384  
    {
384  
    {
385  
        return std::ranges::end(bs);
385  
        return std::ranges::end(bs);
386  
    }
386  
    }
387  

387  

388  
    template<ConstBufferSequence BS>
388  
    template<ConstBufferSequence BS>
389  
        requires (!std::convertible_to<BS, const_buffer>)
389  
        requires (!std::convertible_to<BS, const_buffer>)
390  
    auto operator()(BS& bs) const noexcept
390  
    auto operator()(BS& bs) const noexcept
391  
    {
391  
    {
392  
        return std::ranges::end(bs);
392  
        return std::ranges::end(bs);
393  
    }
393  
    }
394  
} end {};
394  
} end {};
395  

395  

396  
//------------------------------------------------------------------------------
396  
//------------------------------------------------------------------------------
397  

397  

398  
template<ConstBufferSequence CB>
398  
template<ConstBufferSequence CB>
399  
std::size_t
399  
std::size_t
400  
tag_invoke(
400  
tag_invoke(
401  
    size_tag const&,
401  
    size_tag const&,
402  
    CB const& bs) noexcept
402  
    CB const& bs) noexcept
403  
{
403  
{
404  
    std::size_t n = 0;
404  
    std::size_t n = 0;
405  
    auto const e = end(bs);
405  
    auto const e = end(bs);
406  
    for(auto it = begin(bs); it != e; ++it)
406  
    for(auto it = begin(bs); it != e; ++it)
407  
        n += const_buffer(*it).size();
407  
        n += const_buffer(*it).size();
408  
    return n;
408  
    return n;
409  
}
409  
}
410  

410  

411  
//------------------------------------------------------------------------------
411  
//------------------------------------------------------------------------------
412  

412  

413  
/** Return the total byte count across all buffers in a sequence.
413  
/** Return the total byte count across all buffers in a sequence.
414  

414  

415  
    Sums the `size()` of each buffer in the sequence. This differs
415  
    Sums the `size()` of each buffer in the sequence. This differs
416  
    from `buffer_length` which counts the number of buffer elements.
416  
    from `buffer_length` which counts the number of buffer elements.
417  

417  

418  
    @par Example
418  
    @par Example
419  
    @code
419  
    @code
420  
    std::array<mutable_buffer, 2> bufs = { ... };
420  
    std::array<mutable_buffer, 2> bufs = { ... };
421  
    std::size_t total = buffer_size( bufs );  // sum of both sizes
421  
    std::size_t total = buffer_size( bufs );  // sum of both sizes
422  
    @endcode
422  
    @endcode
423  
*/
423  
*/
424  
constexpr struct buffer_size_mrdocs_workaround_t
424  
constexpr struct buffer_size_mrdocs_workaround_t
425  
{
425  
{
426  
    template<ConstBufferSequence CB>
426  
    template<ConstBufferSequence CB>
427  
    constexpr std::size_t operator()(
427  
    constexpr std::size_t operator()(
428  
        CB const& bs) const noexcept
428  
        CB const& bs) const noexcept
429  
    {
429  
    {
430  
        return tag_invoke(size_tag{}, bs);
430  
        return tag_invoke(size_tag{}, bs);
431  
    }
431  
    }
432  
} buffer_size {};
432  
} buffer_size {};
433  

433  

434  
/** Check if a buffer sequence contains no data.
434  
/** Check if a buffer sequence contains no data.
435  

435  

436  
    @return `true` if all buffers have size zero or the sequence
436  
    @return `true` if all buffers have size zero or the sequence
437  
        is empty.
437  
        is empty.
438  
*/
438  
*/
439  
constexpr struct buffer_empty_mrdocs_workaround_t
439  
constexpr struct buffer_empty_mrdocs_workaround_t
440  
{
440  
{
441  
    template<ConstBufferSequence CB>
441  
    template<ConstBufferSequence CB>
442  
    constexpr bool operator()(
442  
    constexpr bool operator()(
443  
        CB const& bs) const noexcept
443  
        CB const& bs) const noexcept
444  
    {
444  
    {
445  
        auto it = begin(bs);
445  
        auto it = begin(bs);
446  
        auto const end_ = end(bs);
446  
        auto const end_ = end(bs);
447  
        while(it != end_)
447  
        while(it != end_)
448  
        {
448  
        {
449  
            const_buffer b(*it++);
449  
            const_buffer b(*it++);
450  
            if(b.size() != 0)
450  
            if(b.size() != 0)
451  
                return false;
451  
                return false;
452  
        }
452  
        }
453  
        return true;
453  
        return true;
454  
    }
454  
    }
455  
} buffer_empty {};
455  
} buffer_empty {};
456  

456  

457  
//-----------------------------------------------
457  
//-----------------------------------------------
458  

458  

459  
namespace detail {
459  
namespace detail {
460  

460  

461  
template<class It>
461  
template<class It>
462  
auto
462  
auto
463  
length_impl(It first, It last, int)
463  
length_impl(It first, It last, int)
464  
    -> decltype(static_cast<std::size_t>(last - first))
464  
    -> decltype(static_cast<std::size_t>(last - first))
465  
{
465  
{
466  
    return static_cast<std::size_t>(last - first);
466  
    return static_cast<std::size_t>(last - first);
467  
}
467  
}
468  

468  

469  
template<class It>
469  
template<class It>
470  
std::size_t
470  
std::size_t
471  
length_impl(It first, It last, long)
471  
length_impl(It first, It last, long)
472  
{
472  
{
473  
    std::size_t n = 0;
473  
    std::size_t n = 0;
474  
    while(first != last)
474  
    while(first != last)
475  
    {
475  
    {
476  
        ++first;
476  
        ++first;
477  
        ++n;
477  
        ++n;
478  
    }
478  
    }
479  
    return n;
479  
    return n;
480  
}
480  
}
481  

481  

482  
} // detail
482  
} // detail
483  

483  

484  
/** Return the number of buffer elements in a sequence.
484  
/** Return the number of buffer elements in a sequence.
485  

485  

486  
    Counts the number of individual buffer objects, not bytes.
486  
    Counts the number of individual buffer objects, not bytes.
487  
    For a single buffer, returns 1. For a range, returns the
487  
    For a single buffer, returns 1. For a range, returns the
488  
    distance from `begin` to `end`.
488  
    distance from `begin` to `end`.
489  

489  

490  
    @see buffer_size
490  
    @see buffer_size
491  
*/
491  
*/
492  
template<ConstBufferSequence CB>
492  
template<ConstBufferSequence CB>
493  
std::size_t
493  
std::size_t
494  
buffer_length(CB const& bs)
494  
buffer_length(CB const& bs)
495  
{
495  
{
496  
    return detail::length_impl(
496  
    return detail::length_impl(
497  
        begin(bs), end(bs), 0);
497  
        begin(bs), end(bs), 0);
498  
}
498  
}
499  

499  

500  
/// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
500  
/// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
501  
template<typename BS>
501  
template<typename BS>
502  
using buffer_type = std::conditional_t<
502  
using buffer_type = std::conditional_t<
503  
    MutableBufferSequence<BS>,
503  
    MutableBufferSequence<BS>,
504  
    mutable_buffer, const_buffer>;
504  
    mutable_buffer, const_buffer>;
505  

505  

506  
} // capy
506  
} // capy
507  
} // boost
507  
} // boost
508  

508  

509  
#endif
509  
#endif