1  
//
1  
//
2  
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2023 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_STRING_DYNAMIC_BUFFER_HPP
10  
#ifndef BOOST_CAPY_BUFFERS_STRING_DYNAMIC_BUFFER_HPP
11  
#define BOOST_CAPY_BUFFERS_STRING_DYNAMIC_BUFFER_HPP
11  
#define BOOST_CAPY_BUFFERS_STRING_DYNAMIC_BUFFER_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/buffers.hpp>
14  
#include <boost/capy/buffers.hpp>
15  
#include <boost/capy/detail/except.hpp>
15  
#include <boost/capy/detail/except.hpp>
16  
#include <string>
16  
#include <string>
17  

17  

18  
namespace boost {
18  
namespace boost {
19  
namespace capy {
19  
namespace capy {
20  

20  

21 -
/** A dynamic buffer backed by a `std::basic_string`.
21 +
/** A dynamic buffer using an underlying string
22 -

 
23 -
    This adapter wraps an externally-owned string and
 
24 -
    exposes it through the @ref DynamicBuffer interface.
 
25 -
    Readable bytes occupy the front of the string; writable
 
26 -
    bytes are appended by `prepare` and made readable by
 
27 -
    `commit`.
 
28 -

 
29 -
    @note The wrapped string must outlive this adapter.
 
30 -
        Calls to `prepare`, `commit`, and `consume`
 
31 -
        invalidate previously returned buffer views.
 
32 -

 
33 -
    @par Thread Safety
 
34 -
    Distinct objects: Safe.
 
35 -
    Shared objects: Unsafe.
 
36 -

 
37 -
    @par Example
 
38 -
    @code
 
39 -
    std::string s;
 
40 -
    auto buf = dynamic_buffer( s, 4096 );
 
41 -
    auto mb = buf.prepare( 100 );
 
42 -
    // fill mb with data...
 
43 -
    buf.commit( 100 );
 
44 -
    // buf.data() now has 100 readable bytes
 
45 -
    buf.consume( 50 );
 
46 -
    @endcode
 
47 -

 
48 -
    @tparam CharT The character type.
 
49 -
    @tparam Traits The character traits type.
 
50 -
    @tparam Allocator The allocator type.
 
51 -

 
52 -
    @see DynamicBuffer, string_dynamic_buffer, dynamic_buffer
 
53  
*/
22  
*/
54  
template<
23  
template<
55  
    class CharT,
24  
    class CharT,
56  
    class Traits = std::char_traits<CharT>,
25  
    class Traits = std::char_traits<CharT>,
57  
    class Allocator = std::allocator<CharT>>
26  
    class Allocator = std::allocator<CharT>>
58  
class basic_string_dynamic_buffer
27  
class basic_string_dynamic_buffer
59  
{
28  
{
60  
    std::basic_string<
29  
    std::basic_string<
61  
        CharT, Traits, Allocator>* s_;
30  
        CharT, Traits, Allocator>* s_;
62  
    std::size_t max_size_;
31  
    std::size_t max_size_;
63  

32  

64  
    std::size_t in_size_ = 0;
33  
    std::size_t in_size_ = 0;
65  
    std::size_t out_size_ = 0;
34  
    std::size_t out_size_ = 0;
66  

35  

67 -
    /// Indicates this is a DynamicBuffer adapter over external storage.
 
68  
public:
36  
public:
69 -

 
70 -
    /// The underlying string type.
 
71  
    using is_dynamic_buffer_adapter = void;
37  
    using is_dynamic_buffer_adapter = void;
72  
    using string_type = std::basic_string<
38  
    using string_type = std::basic_string<
73 -

 
74 -
    /// The ConstBufferSequence type for readable bytes.
 
75  
        CharT, Traits, Allocator>;
39  
        CharT, Traits, Allocator>;
76 -

 
77 -
    /// The MutableBufferSequence type for writable bytes.
 
78  
    using const_buffers_type = const_buffer;
40  
    using const_buffers_type = const_buffer;
79  
    using mutable_buffers_type = mutable_buffer;
41  
    using mutable_buffers_type = mutable_buffer;
80 -
    /// Destroy the buffer.
 
81  

42  

82  
    ~basic_string_dynamic_buffer() = default;
43  
    ~basic_string_dynamic_buffer() = default;
83  

44  

84 -
    /// Construct by moving from another buffer.
45 +
    /** Constructor.
 
46 +
    */
85  
    basic_string_dynamic_buffer(
47  
    basic_string_dynamic_buffer(
86  
        basic_string_dynamic_buffer&& other) noexcept
48  
        basic_string_dynamic_buffer&& other) noexcept
87  
        : s_(other.s_)
49  
        : s_(other.s_)
88  
        , max_size_(other.max_size_)
50  
        , max_size_(other.max_size_)
89  
        , in_size_(other.in_size_)
51  
        , in_size_(other.in_size_)
90  
        , out_size_(other.out_size_)
52  
        , out_size_(other.out_size_)
91  
    {
53  
    {
92  
        other.s_ = nullptr;
54  
        other.s_ = nullptr;
93  
    }
55  
    }
94  

56  

95 -
    /** Construct from an existing string.
57 +
    /** Constructor.
96 -

 
97 -
        @param s Pointer to the string to wrap. Must
 
98 -
            remain valid for the lifetime of this object.
 
99 -
        @param max_size Optional upper bound on the number
 
100 -
            of bytes the buffer may hold.
 
101  
    */
58  
    */
102  
    explicit
59  
    explicit
103  
    basic_string_dynamic_buffer(
60  
    basic_string_dynamic_buffer(
104  
        string_type* s,
61  
        string_type* s,
105  
        std::size_t max_size =
62  
        std::size_t max_size =
106  
            std::size_t(-1)) noexcept
63  
            std::size_t(-1)) noexcept
107  
        : s_(s)
64  
        : s_(s)
108  
        , max_size_(
65  
        , max_size_(
109  
            max_size > s_->max_size()
66  
            max_size > s_->max_size()
110  
                ? s_->max_size()
67  
                ? s_->max_size()
111  
                : max_size)
68  
                : max_size)
112  
    {
69  
    {
113  
        if(s_->size() > max_size_)
70  
        if(s_->size() > max_size_)
114  
            s_->resize(max_size_);
71  
            s_->resize(max_size_);
115  
        in_size_ = s_->size();
72  
        in_size_ = s_->size();
116  
    }
73  
    }
117  

74  

118 -
    /// Copy assignment is deleted.
75 +
    /** Assignment.
 
76 +
    */
119  
    basic_string_dynamic_buffer& operator=(
77  
    basic_string_dynamic_buffer& operator=(
120  
        basic_string_dynamic_buffer const&) = delete;
78  
        basic_string_dynamic_buffer const&) = delete;
121 -
    /// Return the number of readable bytes.
 
122  

79  

123  
    std::size_t
80  
    std::size_t
124  
    size() const noexcept
81  
    size() const noexcept
125  
    {
82  
    {
126  
        return in_size_;
83  
        return in_size_;
127  
    }
84  
    }
128 -
    /// Return the maximum number of bytes the buffer can hold.
 
129  

85  

130  
    std::size_t
86  
    std::size_t
131  
    max_size() const noexcept
87  
    max_size() const noexcept
132  
    {
88  
    {
133  
        return max_size_;
89  
        return max_size_;
134  
    }
90  
    }
135 -
    /// Return the number of writable bytes without reallocation.
 
136  

91  

137  
    std::size_t
92  
    std::size_t
138  
    capacity() const noexcept
93  
    capacity() const noexcept
139  
    {
94  
    {
140  
        if(s_->capacity() <= max_size_)
95  
        if(s_->capacity() <= max_size_)
141  
            return s_->capacity() - in_size_;
96  
            return s_->capacity() - in_size_;
142  
        return max_size_ - in_size_;
97  
        return max_size_ - in_size_;
143  
    }
98  
    }
144 -
    /// Return a buffer sequence representing the readable bytes.
 
145  

99  

146  
    const_buffers_type
100  
    const_buffers_type
147  
    data() const noexcept
101  
    data() const noexcept
148  
    {
102  
    {
149  
        return const_buffers_type(
103  
        return const_buffers_type(
150  
            s_->data(), in_size_);
104  
            s_->data(), in_size_);
151  
    }
105  
    }
152 -
    /** Prepare writable space of at least `n` bytes.
 
153 -

 
154 -
        Invalidates iterators and references returned by
 
155 -
        previous calls to `data` and `prepare`.
 
156 -

 
157 -
        @throws std::invalid_argument if `n` exceeds
 
158 -
            available space.
 
159 -

 
160 -
        @param n The number of bytes to prepare.
 
161 -

 
162 -
        @return A mutable buffer of exactly `n` bytes.
 
163 -
    */
 
164  

106  

165  
    mutable_buffers_type
107  
    mutable_buffers_type
166  
    prepare(std::size_t n)
108  
    prepare(std::size_t n)
167  
    {
109  
    {
168  
        // n exceeds available space
110  
        // n exceeds available space
169  
        if(n > max_size_ - in_size_)
111  
        if(n > max_size_ - in_size_)
170  
            detail::throw_invalid_argument();
112  
            detail::throw_invalid_argument();
171  

113  

172  
        if( s_->size() < in_size_ + n)
114  
        if( s_->size() < in_size_ + n)
173  
            s_->resize(in_size_ + n);
115  
            s_->resize(in_size_ + n);
174  
        out_size_ = n;
116  
        out_size_ = n;
175  
        return mutable_buffers_type(
117  
        return mutable_buffers_type(
176  
            &(*s_)[in_size_], out_size_);
118  
            &(*s_)[in_size_], out_size_);
177  
    }
119  
    }
178 -
    /** Move bytes from the writable to the readable area.
 
179 -

 
180 -
        Invalidates iterators and references returned by
 
181 -
        previous calls to `data` and `prepare`.
 
182 -

 
183 -
        @param n The number of bytes to commit. Clamped
 
184 -
            to the size of the writable area.
 
185 -
    */
 
186  

120  

187  
    void commit(std::size_t n) noexcept
121  
    void commit(std::size_t n) noexcept
188  
    {
122  
    {
189  
        if(n < out_size_)
123  
        if(n < out_size_)
190  
            in_size_ += n;
124  
            in_size_ += n;
191  
        else
125  
        else
192  
            in_size_ += out_size_;
126  
            in_size_ += out_size_;
193  
        out_size_ = 0;
127  
        out_size_ = 0;
194  
        s_->resize(in_size_);
128  
        s_->resize(in_size_);
195  
    }
129  
    }
196 -
    /** Remove bytes from the beginning of the readable area.
 
197 -

 
198 -
        Invalidates iterators and references returned by
 
199 -
        previous calls to `data` and `prepare`.
 
200 -

 
201 -
        @param n The number of bytes to consume. Clamped
 
202 -
            to the number of readable bytes.
 
203 -
    */
 
204  

130  

205  
    void consume(std::size_t n) noexcept
131  
    void consume(std::size_t n) noexcept
206  
    {
132  
    {
207  
        if(n < in_size_)
133  
        if(n < in_size_)
208  
        {
134  
        {
209  
            s_->erase(0, n);
135  
            s_->erase(0, n);
210  
            in_size_ -= n;
136  
            in_size_ -= n;
211  
        }
137  
        }
212  
        else
138  
        else
213  
        {
139  
        {
214  
            s_->clear();
140  
            s_->clear();
215  
            in_size_ = 0;
141  
            in_size_ = 0;
216  
        }
142  
        }
217  
        out_size_ = 0;
143  
        out_size_ = 0;
218  
    }
144  
    }
219  
};
145  
};
220 -
/// A dynamic buffer using `std::string`.
 
221  

146  

222  
using string_dynamic_buffer = basic_string_dynamic_buffer<char>;
147  
using string_dynamic_buffer = basic_string_dynamic_buffer<char>;
223  

148  

224  
/** Create a dynamic buffer from a string.
149  
/** Create a dynamic buffer from a string.
225  

150  

226  
    @param s The string to wrap.
151  
    @param s The string to wrap.
227  
    @param max_size Optional maximum size limit.
152  
    @param max_size Optional maximum size limit.
228  
    @return A string_dynamic_buffer wrapping the string.
153  
    @return A string_dynamic_buffer wrapping the string.
229  
*/
154  
*/
230  
template<class CharT, class Traits, class Allocator>
155  
template<class CharT, class Traits, class Allocator>
231  
basic_string_dynamic_buffer<CharT, Traits, Allocator>
156  
basic_string_dynamic_buffer<CharT, Traits, Allocator>
232  
dynamic_buffer(
157  
dynamic_buffer(
233  
    std::basic_string<CharT, Traits, Allocator>& s,
158  
    std::basic_string<CharT, Traits, Allocator>& s,
234  
    std::size_t max_size = std::size_t(-1))
159  
    std::size_t max_size = std::size_t(-1))
235  
{
160  
{
236  
    return basic_string_dynamic_buffer<CharT, Traits, Allocator>(&s, max_size);
161  
    return basic_string_dynamic_buffer<CharT, Traits, Allocator>(&s, max_size);
237  
}
162  
}
238  

163  

239  
} // capy
164  
} // capy
240  
} // boost
165  
} // boost
241  

166  

242  
#endif
167  
#endif