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  
//------------------------------------------------
35  
//------------------------------------------------
36  

36  

37  
/// Tag type for customizing `buffer_size` via `tag_invoke`.
37  
/// Tag type for customizing `buffer_size` via `tag_invoke`.
38  
struct size_tag {};
38  
struct size_tag {};
39  

39  

40  
/// Tag type for customizing slice operations via `tag_invoke`.
40  
/// Tag type for customizing slice operations via `tag_invoke`.
41  
struct slice_tag {};
41  
struct slice_tag {};
42  

42  

43  
/** Constants for slice customization.
43  
/** Constants for slice customization.
44  

44  

45  
    Passed to `tag_invoke` overloads to specify which portion
45  
    Passed to `tag_invoke` overloads to specify which portion
46  
    of a buffer sequence to retain.
46  
    of a buffer sequence to retain.
47  
*/
47  
*/
48  
enum class slice_how
48  
enum class slice_how
49  
{
49  
{
50  
    /// Remove bytes from the front of the sequence.
50  
    /// Remove bytes from the front of the sequence.
51  
    remove_prefix,
51  
    remove_prefix,
52  

52  

53  
    /// Keep only the first N bytes.
53  
    /// Keep only the first N bytes.
54  
    keep_prefix
54  
    keep_prefix
55  
};
55  
};
56  

56  

57  
//------------------------------------------------
57  
//------------------------------------------------
58  

58  

59  
/** A reference to a contiguous region of writable memory.
59  
/** A reference to a contiguous region of writable memory.
60  

60  

61  
    Represents a pointer and size pair for a modifiable byte range.
61  
    Represents a pointer and size pair for a modifiable byte range.
62  
    Does not own the memory. Satisfies `MutableBufferSequence` (as a
62  
    Does not own the memory. Satisfies `MutableBufferSequence` (as a
63  
    single-element sequence) and is implicitly convertible to
63  
    single-element sequence) and is implicitly convertible to
64  
    `const_buffer`.
64  
    `const_buffer`.
65  

65  

66  
    @see const_buffer, MutableBufferSequence
66  
    @see const_buffer, MutableBufferSequence
67  
*/
67  
*/
68  
class mutable_buffer
68  
class mutable_buffer
69  
{
69  
{
70  
    unsigned char* p_ = nullptr;
70  
    unsigned char* p_ = nullptr;
71  
    std::size_t n_ = 0;
71  
    std::size_t n_ = 0;
72  

72  

73  
public:
73  
public:
74  
    /// Construct an empty buffer.
74  
    /// Construct an empty buffer.
75  
    mutable_buffer() = default;
75  
    mutable_buffer() = default;
76  

76  

77  
    /// Copy constructor.
77  
    /// Copy constructor.
78  
    mutable_buffer(
78  
    mutable_buffer(
79  
        mutable_buffer const&) = default;
79  
        mutable_buffer const&) = default;
80  

80  

81  
    /// Copy assignment.
81  
    /// Copy assignment.
82  
    mutable_buffer& operator=(
82  
    mutable_buffer& operator=(
83  
        mutable_buffer const&) = default;
83  
        mutable_buffer const&) = default;
84  

84  

85  
    /// Construct from pointer and size.
85  
    /// Construct from pointer and size.
86  
    constexpr mutable_buffer(
86  
    constexpr mutable_buffer(
87  
        void* data, std::size_t size) noexcept
87  
        void* data, std::size_t size) noexcept
88  
        : p_(static_cast<unsigned char*>(data))
88  
        : p_(static_cast<unsigned char*>(data))
89  
        , n_(size)
89  
        , n_(size)
90  
    {
90  
    {
91  
    }
91  
    }
92  

92  

93  
    /// Return a pointer to the memory region.
93  
    /// Return a pointer to the memory region.
94  
    constexpr void* data() const noexcept
94  
    constexpr void* data() const noexcept
95  
    {
95  
    {
96  
        return p_;
96  
        return p_;
97  
    }
97  
    }
98  

98  

99  
    /// Return the size in bytes.
99  
    /// Return the size in bytes.
100  
    constexpr std::size_t size() const noexcept
100  
    constexpr std::size_t size() const noexcept
101  
    {
101  
    {
102  
        return n_;
102  
        return n_;
103  
    }
103  
    }
104  

104  

105  
    /** Advance the buffer start, shrinking the region.
105  
    /** Advance the buffer start, shrinking the region.
106  

106  

107  
        @param n Bytes to skip. Clamped to `size()`.
107  
        @param n Bytes to skip. Clamped to `size()`.
108  
    */
108  
    */
109  
    mutable_buffer&
109  
    mutable_buffer&
110  
    operator+=(std::size_t n) noexcept
110  
    operator+=(std::size_t n) noexcept
111  
    {
111  
    {
112  
        if( n > n_)
112  
        if( n > n_)
113  
            n = n_;
113  
            n = n_;
114  
        p_ += n;
114  
        p_ += n;
115  
        n_ -= n;
115  
        n_ -= n;
116  
        return *this;
116  
        return *this;
117  
    }
117  
    }
118  

118  

119  
    /// Slice customization point for `tag_invoke`.
119  
    /// Slice customization point for `tag_invoke`.
120  
    friend
120  
    friend
121  
    void
121  
    void
122  
    tag_invoke(
122  
    tag_invoke(
123  
        slice_tag const&,
123  
        slice_tag const&,
124  
        mutable_buffer& b,
124  
        mutable_buffer& b,
125  
        slice_how how,
125  
        slice_how how,
126  
        std::size_t n) noexcept
126  
        std::size_t n) noexcept
127  
    {
127  
    {
128  
        b.do_slice(how, n);
128  
        b.do_slice(how, n);
129  
    }
129  
    }
130  

130  

131  
private:
131  
private:
132  
    void do_slice(
132  
    void do_slice(
133  
        slice_how how, std::size_t n) noexcept
133  
        slice_how how, std::size_t n) noexcept
134  
    {
134  
    {
135  
        switch(how)
135  
        switch(how)
136  
        {
136  
        {
137  
        case slice_how::remove_prefix:
137  
        case slice_how::remove_prefix:
138  
            *this += n;
138  
            *this += n;
139  
            return;
139  
            return;
140  

140  

141  
        case slice_how::keep_prefix:
141  
        case slice_how::keep_prefix:
142  
            if( n < n_)
142  
            if( n < n_)
143  
                n_ = n;
143  
                n_ = n;
144  
            return;
144  
            return;
145  
        }
145  
        }
146  
    }
146  
    }
147  
};
147  
};
148  

148  

149  
//------------------------------------------------
149  
//------------------------------------------------
150  

150  

151  
/** A reference to a contiguous region of read-only memory.
151  
/** A reference to a contiguous region of read-only memory.
152  

152  

153  
    Represents a pointer and size pair for a non-modifiable byte range.
153  
    Represents a pointer and size pair for a non-modifiable byte range.
154  
    Does not own the memory. Satisfies `ConstBufferSequence` (as a
154  
    Does not own the memory. Satisfies `ConstBufferSequence` (as a
155  
    single-element sequence). Implicitly constructible from
155  
    single-element sequence). Implicitly constructible from
156  
    `mutable_buffer`.
156  
    `mutable_buffer`.
157  

157  

158  
    @see mutable_buffer, ConstBufferSequence
158  
    @see mutable_buffer, ConstBufferSequence
159  
*/
159  
*/
160  
class const_buffer
160  
class const_buffer
161  
{
161  
{
162  
    unsigned char const* p_ = nullptr;
162  
    unsigned char const* p_ = nullptr;
163  
    std::size_t n_ = 0;
163  
    std::size_t n_ = 0;
164  

164  

165  
public:
165  
public:
166  
    /// Construct an empty buffer.
166  
    /// Construct an empty buffer.
167  
    const_buffer() = default;
167  
    const_buffer() = default;
168  

168  

169  
    /// Copy constructor.
169  
    /// Copy constructor.
170  
    const_buffer(const_buffer const&) = default;
170  
    const_buffer(const_buffer const&) = default;
171  

171  

172  
    /// Copy assignment.
172  
    /// Copy assignment.
173  
    const_buffer& operator=(
173  
    const_buffer& operator=(
174  
        const_buffer const& other) = default;
174  
        const_buffer const& other) = default;
175  

175  

176  
    /// Construct from pointer and size.
176  
    /// Construct from pointer and size.
177  
    constexpr const_buffer(
177  
    constexpr const_buffer(
178  
        void const* data, std::size_t size) noexcept
178  
        void const* data, std::size_t size) noexcept
179  
        : p_(static_cast<unsigned char const*>(data))
179  
        : p_(static_cast<unsigned char const*>(data))
180  
        , n_(size)
180  
        , n_(size)
181  
    {
181  
    {
182  
    }
182  
    }
183  

183  

184  
    /// Construct from mutable_buffer.
184  
    /// Construct from mutable_buffer.
185  
    constexpr const_buffer(
185  
    constexpr const_buffer(
186  
        mutable_buffer const& b) noexcept
186  
        mutable_buffer const& b) noexcept
187  
        : p_(static_cast<unsigned char const*>(b.data()))
187  
        : p_(static_cast<unsigned char const*>(b.data()))
188  
        , n_(b.size())
188  
        , n_(b.size())
189  
    {
189  
    {
190  
    }
190  
    }
191  

191  

192  
    /// Return a pointer to the memory region.
192  
    /// Return a pointer to the memory region.
193  
    constexpr void const* data() const noexcept
193  
    constexpr void const* data() const noexcept
194  
    {
194  
    {
195  
        return p_;
195  
        return p_;
196  
    }
196  
    }
197  

197  

198  
    /// Return the size in bytes.
198  
    /// Return the size in bytes.
199  
    constexpr std::size_t size() const noexcept
199  
    constexpr std::size_t size() const noexcept
200  
    {
200  
    {
201  
        return n_;
201  
        return n_;
202  
    }
202  
    }
203  

203  

204  
    /** Advance the buffer start, shrinking the region.
204  
    /** Advance the buffer start, shrinking the region.
205  

205  

206  
        @param n Bytes to skip. Clamped to `size()`.
206  
        @param n Bytes to skip. Clamped to `size()`.
207  
    */
207  
    */
208  
    const_buffer&
208  
    const_buffer&
209  
    operator+=(std::size_t n) noexcept
209  
    operator+=(std::size_t n) noexcept
210  
    {
210  
    {
211  
        if( n > n_)
211  
        if( n > n_)
212  
            n = n_;
212  
            n = n_;
213  
        p_ += n;
213  
        p_ += n;
214  
        n_ -= n;
214  
        n_ -= n;
215  
        return *this;
215  
        return *this;
216  
    }
216  
    }
217  

217  

218  
    /// Slice customization point for `tag_invoke`.
218  
    /// Slice customization point for `tag_invoke`.
219  
    friend
219  
    friend
220  
    void
220  
    void
221  
    tag_invoke(
221  
    tag_invoke(
222  
        slice_tag const&,
222  
        slice_tag const&,
223  
        const_buffer& b,
223  
        const_buffer& b,
224  
        slice_how how,
224  
        slice_how how,
225  
        std::size_t n) noexcept
225  
        std::size_t n) noexcept
226  
    {
226  
    {
227  
        b.do_slice(how, n);
227  
        b.do_slice(how, n);
228  
    }
228  
    }
229  

229  

230  
private:
230  
private:
231  
    void do_slice(
231  
    void do_slice(
232  
        slice_how how, std::size_t n) noexcept
232  
        slice_how how, std::size_t n) noexcept
233  
    {
233  
    {
234  
        switch(how)
234  
        switch(how)
235  
        {
235  
        {
236  
        case slice_how::remove_prefix:
236  
        case slice_how::remove_prefix:
237  
            *this += n;
237  
            *this += n;
238  
            return;
238  
            return;
239  

239  

240  
        case slice_how::keep_prefix:
240  
        case slice_how::keep_prefix:
241  
            if( n < n_)
241  
            if( n < n_)
242  
                n_ = n;
242  
                n_ = n;
243  
            return;
243  
            return;
244  
        }
244  
        }
245  
    }
245  
    }
246  
};
246  
};
247  

247  

248  
//------------------------------------------------
248  
//------------------------------------------------
249  

249  

250  
/** Concept for sequences of read-only buffer regions.
250  
/** Concept for sequences of read-only buffer regions.
251  

251  

252  
    A type satisfies `ConstBufferSequence` if it represents one or more
252  
    A type satisfies `ConstBufferSequence` if it represents one or more
253  
    contiguous memory regions that can be read. This includes single
253  
    contiguous memory regions that can be read. This includes single
254  
    buffers (convertible to `const_buffer`) and ranges of buffers.
254  
    buffers (convertible to `const_buffer`) and ranges of buffers.
255  

255  

256  
    @par Syntactic Requirements
256  
    @par Syntactic Requirements
257  
    @li Convertible to `const_buffer`, OR
257  
    @li Convertible to `const_buffer`, OR
258  
    @li A bidirectional range with value type convertible to `const_buffer`
258  
    @li A bidirectional range with value type convertible to `const_buffer`
259  

259  

260  
    @see const_buffer, MutableBufferSequence
260  
    @see const_buffer, MutableBufferSequence
261  
*/
261  
*/
262  
template<typename T>
262  
template<typename T>
263  
concept ConstBufferSequence =
263  
concept ConstBufferSequence =
264  
    std::is_convertible_v<T, const_buffer> || (
264  
    std::is_convertible_v<T, const_buffer> || (
265  
        std::ranges::bidirectional_range<T> &&
265  
        std::ranges::bidirectional_range<T> &&
266  
        std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
266  
        std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
267  

267  

268  
/** Concept for sequences of writable buffer regions.
268  
/** Concept for sequences of writable buffer regions.
269  

269  

270  
    A type satisfies `MutableBufferSequence` if it represents one or more
270  
    A type satisfies `MutableBufferSequence` if it represents one or more
271  
    contiguous memory regions that can be written. This includes single
271  
    contiguous memory regions that can be written. This includes single
272  
    buffers (convertible to `mutable_buffer`) and ranges of buffers.
272  
    buffers (convertible to `mutable_buffer`) and ranges of buffers.
273  
    Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
273  
    Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
274  

274  

275  
    @par Syntactic Requirements
275  
    @par Syntactic Requirements
276  
    @li Convertible to `mutable_buffer`, OR
276  
    @li Convertible to `mutable_buffer`, OR
277  
    @li A bidirectional range with value type convertible to `mutable_buffer`
277  
    @li A bidirectional range with value type convertible to `mutable_buffer`
278  

278  

279  
    @see mutable_buffer, ConstBufferSequence
279  
    @see mutable_buffer, ConstBufferSequence
280  
*/
280  
*/
281  
template<typename T>
281  
template<typename T>
282  
concept MutableBufferSequence =
282  
concept MutableBufferSequence =
283  
    std::is_convertible_v<T, mutable_buffer> || (
283  
    std::is_convertible_v<T, mutable_buffer> || (
284  
        std::ranges::bidirectional_range<T> &&
284  
        std::ranges::bidirectional_range<T> &&
285  
        std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
285  
        std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
286  

286  

287  
//------------------------------------------------------------------------------
287  
//------------------------------------------------------------------------------
288  

288  

289  
/** Return an iterator to the first buffer in a sequence.
289  
/** Return an iterator to the first buffer in a sequence.
290  

290  

291  
    Handles single buffers and ranges uniformly. For a single buffer,
291  
    Handles single buffers and ranges uniformly. For a single buffer,
292  
    returns a pointer to it (forming a one-element range).
292  
    returns a pointer to it (forming a one-element range).
293  
*/
293  
*/
294  
constexpr struct begin_mrdocs_workaround_t
294  
constexpr struct begin_mrdocs_workaround_t
295  
{
295  
{
296  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
296  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
297  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
297  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
298  
    {
298  
    {
299  
        return std::addressof(b);
299  
        return std::addressof(b);
300  
    }
300  
    }
301  

301  

302  
    template<ConstBufferSequence BS>
302  
    template<ConstBufferSequence BS>
303  
        requires (!std::convertible_to<BS, const_buffer>)
303  
        requires (!std::convertible_to<BS, const_buffer>)
304  
    auto operator()(BS const& bs) const noexcept
304  
    auto operator()(BS const& bs) const noexcept
305  
    {
305  
    {
306  
        return std::ranges::begin(bs);
306  
        return std::ranges::begin(bs);
307  
    }
307  
    }
308  

308  

309  
    template<ConstBufferSequence BS>
309  
    template<ConstBufferSequence BS>
310  
        requires (!std::convertible_to<BS, const_buffer>)
310  
        requires (!std::convertible_to<BS, const_buffer>)
311  
    auto operator()(BS& bs) const noexcept
311  
    auto operator()(BS& bs) const noexcept
312  
    {
312  
    {
313  
        return std::ranges::begin(bs);
313  
        return std::ranges::begin(bs);
314  
    }
314  
    }
315  
} begin {};
315  
} begin {};
316  

316  

317  
/** Return an iterator past the last buffer in a sequence.
317  
/** Return an iterator past the last buffer in a sequence.
318  

318  

319  
    Handles single buffers and ranges uniformly. For a single buffer,
319  
    Handles single buffers and ranges uniformly. For a single buffer,
320  
    returns a pointer one past it.
320  
    returns a pointer one past it.
321  
*/
321  
*/
322  
constexpr struct end_mrdocs_workaround_t
322  
constexpr struct end_mrdocs_workaround_t
323  
{
323  
{
324  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
324  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
325  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
325  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
326  
    {
326  
    {
327  
        return std::addressof(b) + 1;
327  
        return std::addressof(b) + 1;
328  
    }
328  
    }
329  

329  

330  
    template<ConstBufferSequence BS>
330  
    template<ConstBufferSequence BS>
331  
        requires (!std::convertible_to<BS, const_buffer>)
331  
        requires (!std::convertible_to<BS, const_buffer>)
332  
    auto operator()(BS const& bs) const noexcept
332  
    auto operator()(BS const& bs) const noexcept
333  
    {
333  
    {
334  
        return std::ranges::end(bs);
334  
        return std::ranges::end(bs);
335  
    }
335  
    }
336  

336  

337  
    template<ConstBufferSequence BS>
337  
    template<ConstBufferSequence BS>
338  
        requires (!std::convertible_to<BS, const_buffer>)
338  
        requires (!std::convertible_to<BS, const_buffer>)
339  
    auto operator()(BS& bs) const noexcept
339  
    auto operator()(BS& bs) const noexcept
340  
    {
340  
    {
341  
        return std::ranges::end(bs);
341  
        return std::ranges::end(bs);
342  
    }
342  
    }
343  
} end {};
343  
} end {};
344  

344  

345  
//------------------------------------------------------------------------------
345  
//------------------------------------------------------------------------------
346  

346  

347  
template<ConstBufferSequence CB>
347  
template<ConstBufferSequence CB>
348  
std::size_t
348  
std::size_t
349  
tag_invoke(
349  
tag_invoke(
350  
    size_tag const&,
350  
    size_tag const&,
351  
    CB const& bs) noexcept
351  
    CB const& bs) noexcept
352  
{
352  
{
353  
    std::size_t n = 0;
353  
    std::size_t n = 0;
354  
    auto const e = end(bs);
354  
    auto const e = end(bs);
355  
    for(auto it = begin(bs); it != e; ++it)
355  
    for(auto it = begin(bs); it != e; ++it)
356  
        n += const_buffer(*it).size();
356  
        n += const_buffer(*it).size();
357  
    return n;
357  
    return n;
358  
}
358  
}
359  

359  

360  
//------------------------------------------------------------------------------
360  
//------------------------------------------------------------------------------
361  

361  

362  
/** Return the total byte count across all buffers in a sequence.
362  
/** Return the total byte count across all buffers in a sequence.
363  

363  

364  
    Sums the `size()` of each buffer in the sequence. This differs
364  
    Sums the `size()` of each buffer in the sequence. This differs
365  
    from `buffer_length` which counts the number of buffer elements.
365  
    from `buffer_length` which counts the number of buffer elements.
366  

366  

367  
    @par Example
367  
    @par Example
368  
    @code
368  
    @code
369  
    std::array<mutable_buffer, 2> bufs = { ... };
369  
    std::array<mutable_buffer, 2> bufs = { ... };
370  
    std::size_t total = buffer_size( bufs );  // sum of both sizes
370  
    std::size_t total = buffer_size( bufs );  // sum of both sizes
371  
    @endcode
371  
    @endcode
372  
*/
372  
*/
373  
constexpr struct buffer_size_mrdocs_workaround_t
373  
constexpr struct buffer_size_mrdocs_workaround_t
374  
{
374  
{
375  
    template<ConstBufferSequence CB>
375  
    template<ConstBufferSequence CB>
376  
    constexpr std::size_t operator()(
376  
    constexpr std::size_t operator()(
377  
        CB const& bs) const noexcept
377  
        CB const& bs) const noexcept
378  
    {
378  
    {
379  
        return tag_invoke(size_tag{}, bs);
379  
        return tag_invoke(size_tag{}, bs);
380  
    }
380  
    }
381  
} buffer_size {};
381  
} buffer_size {};
382  

382  

383  
/** Check if a buffer sequence contains no data.
383  
/** Check if a buffer sequence contains no data.
384  

384  

385  
    @return `true` if all buffers have size zero or the sequence
385  
    @return `true` if all buffers have size zero or the sequence
386  
        is empty.
386  
        is empty.
387  
*/
387  
*/
388  
constexpr struct buffer_empty_mrdocs_workaround_t
388  
constexpr struct buffer_empty_mrdocs_workaround_t
389  
{
389  
{
390  
    template<ConstBufferSequence CB>
390  
    template<ConstBufferSequence CB>
391  
    constexpr bool operator()(
391  
    constexpr bool operator()(
392  
        CB const& bs) const noexcept
392  
        CB const& bs) const noexcept
393  
    {
393  
    {
394  
        auto it = begin(bs);
394  
        auto it = begin(bs);
395  
        auto const end_ = end(bs);
395  
        auto const end_ = end(bs);
396  
        while(it != end_)
396  
        while(it != end_)
397  
        {
397  
        {
398  
            const_buffer b(*it++);
398  
            const_buffer b(*it++);
399  
            if(b.size() != 0)
399  
            if(b.size() != 0)
400  
                return false;
400  
                return false;
401  
        }
401  
        }
402  
        return true;
402  
        return true;
403  
    }
403  
    }
404  
} buffer_empty {};
404  
} buffer_empty {};
405  

405  

406  
//-----------------------------------------------
406  
//-----------------------------------------------
407  

407  

408  
namespace detail {
408  
namespace detail {
409  

409  

410  
template<class It>
410  
template<class It>
411  
auto
411  
auto
412  
length_impl(It first, It last, int)
412  
length_impl(It first, It last, int)
413  
    -> decltype(static_cast<std::size_t>(last - first))
413  
    -> decltype(static_cast<std::size_t>(last - first))
414  
{
414  
{
415  
    return static_cast<std::size_t>(last - first);
415  
    return static_cast<std::size_t>(last - first);
416  
}
416  
}
417  

417  

418  
template<class It>
418  
template<class It>
419  
std::size_t
419  
std::size_t
420  
length_impl(It first, It last, long)
420  
length_impl(It first, It last, long)
421  
{
421  
{
422  
    std::size_t n = 0;
422  
    std::size_t n = 0;
423  
    while(first != last)
423  
    while(first != last)
424  
    {
424  
    {
425  
        ++first;
425  
        ++first;
426  
        ++n;
426  
        ++n;
427  
    }
427  
    }
428  
    return n;
428  
    return n;
429  
}
429  
}
430  

430  

431  
} // detail
431  
} // detail
432  

432  

433  
/** Return the number of buffer elements in a sequence.
433  
/** Return the number of buffer elements in a sequence.
434  

434  

435  
    Counts the number of individual buffer objects, not bytes.
435  
    Counts the number of individual buffer objects, not bytes.
436  
    For a single buffer, returns 1. For a range, returns the
436  
    For a single buffer, returns 1. For a range, returns the
437  
    distance from `begin` to `end`.
437  
    distance from `begin` to `end`.
438  

438  

439  
    @see buffer_size
439  
    @see buffer_size
440  
*/
440  
*/
441  
template<ConstBufferSequence CB>
441  
template<ConstBufferSequence CB>
442  
std::size_t
442  
std::size_t
443  
buffer_length(CB const& bs)
443  
buffer_length(CB const& bs)
444  
{
444  
{
445  
    return detail::length_impl(
445  
    return detail::length_impl(
446  
        begin(bs), end(bs), 0);
446  
        begin(bs), end(bs), 0);
447  
}
447  
}
448  

448  

449  
/// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
449  
/// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
450  
template<typename BS>
450  
template<typename BS>
451  
using buffer_type = std::conditional_t<
451  
using buffer_type = std::conditional_t<
452  
    MutableBufferSequence<BS>,
452  
    MutableBufferSequence<BS>,
453  
    mutable_buffer, const_buffer>;
453  
    mutable_buffer, const_buffer>;
454  

454  

455  
} // capy
455  
} // capy
456  
} // boost
456  
} // boost
457  

457  

458  
#endif
458  
#endif