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  
/// Tag type for customizing `buffer_size` via `tag_invoke`.
35  
/// Tag type for customizing `buffer_size` via `tag_invoke`.
36  
struct size_tag {};
36  
struct size_tag {};
37  

37  

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

40  

41  
/** Constants for slice customization.
41  
/** Constants for slice customization.
42  

42  

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

50  

51  
    /// Keep only the first N bytes.
51  
    /// Keep only the first N bytes.
52  
    keep_prefix
52  
    keep_prefix
53  
};
53  
};
54  

54  

55  
/** A reference to a contiguous region of writable memory.
55  
/** A reference to a contiguous region of writable memory.
56  

56  

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

61  

62  
    @see const_buffer, MutableBufferSequence
62  
    @see const_buffer, MutableBufferSequence
63  
*/
63  
*/
64  
class mutable_buffer
64  
class mutable_buffer
65  
{
65  
{
66  
    unsigned char* p_ = nullptr;
66  
    unsigned char* p_ = nullptr;
67  
    std::size_t n_ = 0;
67  
    std::size_t n_ = 0;
68  

68  

69  
public:
69  
public:
70  
    /// Construct an empty buffer.
70  
    /// Construct an empty buffer.
71  
    mutable_buffer() = default;
71  
    mutable_buffer() = default;
72  

72  

73  
    /// Construct a copy.
73  
    /// Construct a copy.
74  
    mutable_buffer(
74  
    mutable_buffer(
75  
        mutable_buffer const&) = default;
75  
        mutable_buffer const&) = default;
76  

76  

77  
    /// Assign by copying.
77  
    /// Assign by copying.
78  
    mutable_buffer& operator=(
78  
    mutable_buffer& operator=(
79  
        mutable_buffer const&) = default;
79  
        mutable_buffer const&) = default;
80  

80  

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

88  

89  
    /// Return a pointer to the memory region.
89  
    /// Return a pointer to the memory region.
90  
    constexpr void* data() const noexcept
90  
    constexpr void* data() const noexcept
91  
    {
91  
    {
92  
        return p_;
92  
        return p_;
93  
    }
93  
    }
94  

94  

95  
    /// Return the size in bytes.
95  
    /// Return the size in bytes.
96  
    constexpr std::size_t size() const noexcept
96  
    constexpr std::size_t size() const noexcept
97  
    {
97  
    {
98  
        return n_;
98  
        return n_;
99  
    }
99  
    }
100  

100  

101  
    /** Advance the buffer start, shrinking the region.
101  
    /** Advance the buffer start, shrinking the region.
102  

102  

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

114  

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

126  

127  
private:
127  
private:
128  
    void do_slice(
128  
    void do_slice(
129  
        slice_how how, std::size_t n) noexcept
129  
        slice_how how, std::size_t n) noexcept
130  
    {
130  
    {
131  
        switch(how)
131  
        switch(how)
132  
        {
132  
        {
133  
        case slice_how::remove_prefix:
133  
        case slice_how::remove_prefix:
134  
            *this += n;
134  
            *this += n;
135  
            return;
135  
            return;
136  

136  

137  
        case slice_how::keep_prefix:
137  
        case slice_how::keep_prefix:
138  
            if( n < n_)
138  
            if( n < n_)
139  
                n_ = n;
139  
                n_ = n;
140  
            return;
140  
            return;
141  
        }
141  
        }
142  
    }
142  
    }
143  
};
143  
};
144  

144  

145  
/** A reference to a contiguous region of read-only memory.
145  
/** A reference to a contiguous region of read-only memory.
146  

146  

147  
    Represents a pointer and size pair for a non-modifiable byte range.
147  
    Represents a pointer and size pair for a non-modifiable byte range.
148  
    Does not own the memory. Satisfies `ConstBufferSequence` (as a
148  
    Does not own the memory. Satisfies `ConstBufferSequence` (as a
149  
    single-element sequence). Implicitly constructible from
149  
    single-element sequence). Implicitly constructible from
150  
    `mutable_buffer`.
150  
    `mutable_buffer`.
151  

151  

152  
    @see mutable_buffer, ConstBufferSequence
152  
    @see mutable_buffer, ConstBufferSequence
153  
*/
153  
*/
154  
class const_buffer
154  
class const_buffer
155  
{
155  
{
156  
    unsigned char const* p_ = nullptr;
156  
    unsigned char const* p_ = nullptr;
157  
    std::size_t n_ = 0;
157  
    std::size_t n_ = 0;
158  

158  

159  
public:
159  
public:
160  
    /// Construct an empty buffer.
160  
    /// Construct an empty buffer.
161  
    const_buffer() = default;
161  
    const_buffer() = default;
162  

162  

163  
    /// Construct a copy.
163  
    /// Construct a copy.
164  
    const_buffer(const_buffer const&) = default;
164  
    const_buffer(const_buffer const&) = default;
165  

165  

166  
    /// Assign by copying.
166  
    /// Assign by copying.
167  
    const_buffer& operator=(
167  
    const_buffer& operator=(
168  
        const_buffer const& other) = default;
168  
        const_buffer const& other) = default;
169  

169  

170  
    /// Construct from pointer and size.
170  
    /// Construct from pointer and size.
171  
    constexpr const_buffer(
171  
    constexpr const_buffer(
172  
        void const* data, std::size_t size) noexcept
172  
        void const* data, std::size_t size) noexcept
173  
        : p_(static_cast<unsigned char const*>(data))
173  
        : p_(static_cast<unsigned char const*>(data))
174  
        , n_(size)
174  
        , n_(size)
175  
    {
175  
    {
176  
    }
176  
    }
177  

177  

178  
    /// Construct from mutable_buffer.
178  
    /// Construct from mutable_buffer.
179  
    constexpr const_buffer(
179  
    constexpr const_buffer(
180  
        mutable_buffer const& b) noexcept
180  
        mutable_buffer const& b) noexcept
181  
        : p_(static_cast<unsigned char const*>(b.data()))
181  
        : p_(static_cast<unsigned char const*>(b.data()))
182  
        , n_(b.size())
182  
        , n_(b.size())
183  
    {
183  
    {
184  
    }
184  
    }
185  

185  

186  
    /// Return a pointer to the memory region.
186  
    /// Return a pointer to the memory region.
187  
    constexpr void const* data() const noexcept
187  
    constexpr void const* data() const noexcept
188  
    {
188  
    {
189  
        return p_;
189  
        return p_;
190  
    }
190  
    }
191  

191  

192  
    /// Return the size in bytes.
192  
    /// Return the size in bytes.
193  
    constexpr std::size_t size() const noexcept
193  
    constexpr std::size_t size() const noexcept
194  
    {
194  
    {
195  
        return n_;
195  
        return n_;
196  
    }
196  
    }
197  

197  

198  
    /** Advance the buffer start, shrinking the region.
198  
    /** Advance the buffer start, shrinking the region.
199  

199  

200  
        @param n Bytes to skip. Clamped to `size()`.
200  
        @param n Bytes to skip. Clamped to `size()`.
201  
    */
201  
    */
202  
    const_buffer&
202  
    const_buffer&
203  
    operator+=(std::size_t n) noexcept
203  
    operator+=(std::size_t n) noexcept
204  
    {
204  
    {
205  
        if( n > n_)
205  
        if( n > n_)
206  
            n = n_;
206  
            n = n_;
207  
        p_ += n;
207  
        p_ += n;
208  
        n_ -= n;
208  
        n_ -= n;
209  
        return *this;
209  
        return *this;
210  
    }
210  
    }
211  

211  

212  
    /// Slice customization point for `tag_invoke`.
212  
    /// Slice customization point for `tag_invoke`.
213  
    friend
213  
    friend
214  
    void
214  
    void
215  
    tag_invoke(
215  
    tag_invoke(
216  
        slice_tag const&,
216  
        slice_tag const&,
217  
        const_buffer& b,
217  
        const_buffer& b,
218  
        slice_how how,
218  
        slice_how how,
219  
        std::size_t n) noexcept
219  
        std::size_t n) noexcept
220  
    {
220  
    {
221  
        b.do_slice(how, n);
221  
        b.do_slice(how, n);
222  
    }
222  
    }
223  

223  

224  
private:
224  
private:
225  
    void do_slice(
225  
    void do_slice(
226  
        slice_how how, std::size_t n) noexcept
226  
        slice_how how, std::size_t n) noexcept
227  
    {
227  
    {
228  
        switch(how)
228  
        switch(how)
229  
        {
229  
        {
230  
        case slice_how::remove_prefix:
230  
        case slice_how::remove_prefix:
231  
            *this += n;
231  
            *this += n;
232  
            return;
232  
            return;
233  

233  

234  
        case slice_how::keep_prefix:
234  
        case slice_how::keep_prefix:
235  
            if( n < n_)
235  
            if( n < n_)
236  
                n_ = n;
236  
                n_ = n;
237  
            return;
237  
            return;
238  
        }
238  
        }
239  
    }
239  
    }
240  
};
240  
};
241  

241  

242  
/** Concept for sequences of read-only buffer regions.
242  
/** Concept for sequences of read-only buffer regions.
243  

243  

244  
    A type satisfies `ConstBufferSequence` if it represents one or more
244  
    A type satisfies `ConstBufferSequence` if it represents one or more
245  
    contiguous memory regions that can be read. This includes single
245  
    contiguous memory regions that can be read. This includes single
246  
    buffers (convertible to `const_buffer`) and ranges of buffers.
246  
    buffers (convertible to `const_buffer`) and ranges of buffers.
247  

247  

248  
    @par Syntactic Requirements
248  
    @par Syntactic Requirements
249  
    @li Convertible to `const_buffer`, OR
249  
    @li Convertible to `const_buffer`, OR
250  
    @li A bidirectional range with value type convertible to `const_buffer`
250  
    @li A bidirectional range with value type convertible to `const_buffer`
251  

251  

252  
    @see const_buffer, MutableBufferSequence
252  
    @see const_buffer, MutableBufferSequence
253  
*/
253  
*/
254  
template<typename T>
254  
template<typename T>
255  
concept ConstBufferSequence =
255  
concept ConstBufferSequence =
256  
    std::is_convertible_v<T, const_buffer> || (
256  
    std::is_convertible_v<T, const_buffer> || (
257  
        std::ranges::bidirectional_range<T> &&
257  
        std::ranges::bidirectional_range<T> &&
258  
        std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
258  
        std::is_convertible_v<std::ranges::range_value_t<T>, const_buffer>);
259  

259  

260  
/** Concept for sequences of writable buffer regions.
260  
/** Concept for sequences of writable buffer regions.
261  

261  

262  
    A type satisfies `MutableBufferSequence` if it represents one or more
262  
    A type satisfies `MutableBufferSequence` if it represents one or more
263  
    contiguous memory regions that can be written. This includes single
263  
    contiguous memory regions that can be written. This includes single
264  
    buffers (convertible to `mutable_buffer`) and ranges of buffers.
264  
    buffers (convertible to `mutable_buffer`) and ranges of buffers.
265  
    Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
265  
    Every `MutableBufferSequence` also satisfies `ConstBufferSequence`.
266  

266  

267  
    @par Syntactic Requirements
267  
    @par Syntactic Requirements
268  
    @li Convertible to `mutable_buffer`, OR
268  
    @li Convertible to `mutable_buffer`, OR
269  
    @li A bidirectional range with value type convertible to `mutable_buffer`
269  
    @li A bidirectional range with value type convertible to `mutable_buffer`
270  

270  

271  
    @see mutable_buffer, ConstBufferSequence
271  
    @see mutable_buffer, ConstBufferSequence
272  
*/
272  
*/
273  
template<typename T>
273  
template<typename T>
274  
concept MutableBufferSequence =
274  
concept MutableBufferSequence =
275  
    std::is_convertible_v<T, mutable_buffer> || (
275  
    std::is_convertible_v<T, mutable_buffer> || (
276  
        std::ranges::bidirectional_range<T> &&
276  
        std::ranges::bidirectional_range<T> &&
277  
        std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
277  
        std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);
278  

278  

279  
/** Return an iterator to the first buffer in a sequence.
279  
/** Return an iterator to the first buffer in a sequence.
280  

280  

281  
    Handles single buffers and ranges uniformly. For a single buffer,
281  
    Handles single buffers and ranges uniformly. For a single buffer,
282  
    returns a pointer to it (forming a one-element range).
282  
    returns a pointer to it (forming a one-element range).
283  
*/
283  
*/
284  
constexpr struct begin_mrdocs_workaround_t
284  
constexpr struct begin_mrdocs_workaround_t
285  
{
285  
{
286  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
286  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
287  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
287  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
288  
    {
288  
    {
289  
        return std::addressof(b);
289  
        return std::addressof(b);
290  
    }
290  
    }
291  

291  

292  
    template<ConstBufferSequence BS>
292  
    template<ConstBufferSequence BS>
293  
        requires (!std::convertible_to<BS, const_buffer>)
293  
        requires (!std::convertible_to<BS, const_buffer>)
294  
    auto operator()(BS const& bs) const noexcept
294  
    auto operator()(BS const& bs) const noexcept
295  
    {
295  
    {
296  
        return std::ranges::begin(bs);
296  
        return std::ranges::begin(bs);
297  
    }
297  
    }
298  

298  

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

306  

307  
/** Return an iterator past the last buffer in a sequence.
307  
/** Return an iterator past the last buffer in a sequence.
308  

308  

309  
    Handles single buffers and ranges uniformly. For a single buffer,
309  
    Handles single buffers and ranges uniformly. For a single buffer,
310  
    returns a pointer one past it.
310  
    returns a pointer one past it.
311  
*/
311  
*/
312  
constexpr struct end_mrdocs_workaround_t
312  
constexpr struct end_mrdocs_workaround_t
313  
{
313  
{
314  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
314  
    template<std::convertible_to<const_buffer> ConvertibleToBuffer>
315  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
315  
    auto operator()(ConvertibleToBuffer const& b) const noexcept -> ConvertibleToBuffer const*
316  
    {
316  
    {
317  
        return std::addressof(b) + 1;
317  
        return std::addressof(b) + 1;
318  
    }
318  
    }
319  

319  

320  
    template<ConstBufferSequence BS>
320  
    template<ConstBufferSequence BS>
321  
        requires (!std::convertible_to<BS, const_buffer>)
321  
        requires (!std::convertible_to<BS, const_buffer>)
322  
    auto operator()(BS const& bs) const noexcept
322  
    auto operator()(BS const& bs) const noexcept
323  
    {
323  
    {
324  
        return std::ranges::end(bs);
324  
        return std::ranges::end(bs);
325  
    }
325  
    }
326  

326  

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

334  

335  
template<ConstBufferSequence CB>
335  
template<ConstBufferSequence CB>
336  
std::size_t
336  
std::size_t
337  
tag_invoke(
337  
tag_invoke(
338  
    size_tag const&,
338  
    size_tag const&,
339  
    CB const& bs) noexcept
339  
    CB const& bs) noexcept
340  
{
340  
{
341  
    std::size_t n = 0;
341  
    std::size_t n = 0;
342  
    auto const e = end(bs);
342  
    auto const e = end(bs);
343  
    for(auto it = begin(bs); it != e; ++it)
343  
    for(auto it = begin(bs); it != e; ++it)
344  
        n += const_buffer(*it).size();
344  
        n += const_buffer(*it).size();
345  
    return n;
345  
    return n;
346  
}
346  
}
347  

347  

348  
/** Return the total byte count across all buffers in a sequence.
348  
/** Return the total byte count across all buffers in a sequence.
349  

349  

350  
    Sums the `size()` of each buffer in the sequence. This differs
350  
    Sums the `size()` of each buffer in the sequence. This differs
351  
    from `buffer_length` which counts the number of buffer elements.
351  
    from `buffer_length` which counts the number of buffer elements.
352  

352  

353  
    @par Example
353  
    @par Example
354  
    @code
354  
    @code
355  
    std::array<mutable_buffer, 2> bufs = { ... };
355  
    std::array<mutable_buffer, 2> bufs = { ... };
356  
    std::size_t total = buffer_size( bufs );  // sum of both sizes
356  
    std::size_t total = buffer_size( bufs );  // sum of both sizes
357  
    @endcode
357  
    @endcode
358  
*/
358  
*/
359  
constexpr struct buffer_size_mrdocs_workaround_t
359  
constexpr struct buffer_size_mrdocs_workaround_t
360  
{
360  
{
361  
    template<ConstBufferSequence CB>
361  
    template<ConstBufferSequence CB>
362  
    constexpr std::size_t operator()(
362  
    constexpr std::size_t operator()(
363  
        CB const& bs) const noexcept
363  
        CB const& bs) const noexcept
364  
    {
364  
    {
365  
        return tag_invoke(size_tag{}, bs);
365  
        return tag_invoke(size_tag{}, bs);
366  
    }
366  
    }
367  
} buffer_size {};
367  
} buffer_size {};
368  

368  

369  
/** Check if a buffer sequence contains no data.
369  
/** Check if a buffer sequence contains no data.
370  

370  

371  
    @return `true` if all buffers have size zero or the sequence
371  
    @return `true` if all buffers have size zero or the sequence
372  
        is empty.
372  
        is empty.
373  
*/
373  
*/
374  
constexpr struct buffer_empty_mrdocs_workaround_t
374  
constexpr struct buffer_empty_mrdocs_workaround_t
375  
{
375  
{
376  
    template<ConstBufferSequence CB>
376  
    template<ConstBufferSequence CB>
377  
    constexpr bool operator()(
377  
    constexpr bool operator()(
378  
        CB const& bs) const noexcept
378  
        CB const& bs) const noexcept
379  
    {
379  
    {
380  
        auto it = begin(bs);
380  
        auto it = begin(bs);
381  
        auto const end_ = end(bs);
381  
        auto const end_ = end(bs);
382  
        while(it != end_)
382  
        while(it != end_)
383  
        {
383  
        {
384  
            const_buffer b(*it++);
384  
            const_buffer b(*it++);
385  
            if(b.size() != 0)
385  
            if(b.size() != 0)
386  
                return false;
386  
                return false;
387  
        }
387  
        }
388  
        return true;
388  
        return true;
389  
    }
389  
    }
390  
} buffer_empty {};
390  
} buffer_empty {};
391  

391  

392  
namespace detail {
392  
namespace detail {
393  

393  

394  
template<class It>
394  
template<class It>
395  
auto
395  
auto
396  
length_impl(It first, It last, int)
396  
length_impl(It first, It last, int)
397  
    -> decltype(static_cast<std::size_t>(last - first))
397  
    -> decltype(static_cast<std::size_t>(last - first))
398  
{
398  
{
399  
    return static_cast<std::size_t>(last - first);
399  
    return static_cast<std::size_t>(last - first);
400  
}
400  
}
401  

401  

402  
template<class It>
402  
template<class It>
403  
std::size_t
403  
std::size_t
404  
length_impl(It first, It last, long)
404  
length_impl(It first, It last, long)
405  
{
405  
{
406  
    std::size_t n = 0;
406  
    std::size_t n = 0;
407  
    while(first != last)
407  
    while(first != last)
408  
    {
408  
    {
409  
        ++first;
409  
        ++first;
410  
        ++n;
410  
        ++n;
411  
    }
411  
    }
412  
    return n;
412  
    return n;
413  
}
413  
}
414  

414  

415  
} // detail
415  
} // detail
416  

416  

417  
/** Return the number of buffer elements in a sequence.
417  
/** Return the number of buffer elements in a sequence.
418  

418  

419  
    Counts the number of individual buffer objects, not bytes.
419  
    Counts the number of individual buffer objects, not bytes.
420  
    For a single buffer, returns 1. For a range, returns the
420  
    For a single buffer, returns 1. For a range, returns the
421  
    distance from `begin` to `end`.
421  
    distance from `begin` to `end`.
422  

422  

423  
    @see buffer_size
423  
    @see buffer_size
424  
*/
424  
*/
425  
template<ConstBufferSequence CB>
425  
template<ConstBufferSequence CB>
426  
std::size_t
426  
std::size_t
427  
buffer_length(CB const& bs)
427  
buffer_length(CB const& bs)
428  
{
428  
{
429  
    return detail::length_impl(
429  
    return detail::length_impl(
430  
        begin(bs), end(bs), 0);
430  
        begin(bs), end(bs), 0);
431  
}
431  
}
432  

432  

433  
/// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
433  
/// Alias for `mutable_buffer` or `const_buffer` based on sequence type.
434  
template<typename BS>
434  
template<typename BS>
435  
using buffer_type = std::conditional_t<
435  
using buffer_type = std::conditional_t<
436  
    MutableBufferSequence<BS>,
436  
    MutableBufferSequence<BS>,
437  
    mutable_buffer, const_buffer>;
437  
    mutable_buffer, const_buffer>;
438  

438  

439  
} // capy
439  
} // capy
440  
} // boost
440  
} // boost
441  

441  

442  
#endif
442  
#endif