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 using an underlying string
21 +
/** A dynamic buffer backed by a `std::basic_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
22  
*/
53  
*/
23  
template<
54  
template<
24  
    class CharT,
55  
    class CharT,
25  
    class Traits = std::char_traits<CharT>,
56  
    class Traits = std::char_traits<CharT>,
26  
    class Allocator = std::allocator<CharT>>
57  
    class Allocator = std::allocator<CharT>>
27  
class basic_string_dynamic_buffer
58  
class basic_string_dynamic_buffer
28  
{
59  
{
29  
    std::basic_string<
60  
    std::basic_string<
30  
        CharT, Traits, Allocator>* s_;
61  
        CharT, Traits, Allocator>* s_;
31  
    std::size_t max_size_;
62  
    std::size_t max_size_;
32  

63  

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

66  

36  
public:
67  
public:
 
68 +
    /// Indicates this is a DynamicBuffer adapter over external storage.
37  
    using is_dynamic_buffer_adapter = void;
69  
    using is_dynamic_buffer_adapter = void;
 
70 +

 
71 +
    /// The underlying string type.
38  
    using string_type = std::basic_string<
72  
    using string_type = std::basic_string<
39  
        CharT, Traits, Allocator>;
73  
        CharT, Traits, Allocator>;
 
74 +

 
75 +
    /// The ConstBufferSequence type for readable bytes.
40  
    using const_buffers_type = const_buffer;
76  
    using const_buffers_type = const_buffer;
 
77 +

 
78 +
    /// The MutableBufferSequence type for writable bytes.
41  
    using mutable_buffers_type = mutable_buffer;
79  
    using mutable_buffers_type = mutable_buffer;
42  

80  

 
81 +
    /// Destroy the buffer.
43  
    ~basic_string_dynamic_buffer() = default;
82  
    ~basic_string_dynamic_buffer() = default;
44  

83  

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

94  

57 -
    /** Constructor.
95 +
    /** Construct from an existing string.
 
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.
58  
    */
101  
    */
59  
    explicit
102  
    explicit
60  
    basic_string_dynamic_buffer(
103  
    basic_string_dynamic_buffer(
61  
        string_type* s,
104  
        string_type* s,
62  
        std::size_t max_size =
105  
        std::size_t max_size =
63  
            std::size_t(-1)) noexcept
106  
            std::size_t(-1)) noexcept
64  
        : s_(s)
107  
        : s_(s)
65  
        , max_size_(
108  
        , max_size_(
66  
            max_size > s_->max_size()
109  
            max_size > s_->max_size()
67  
                ? s_->max_size()
110  
                ? s_->max_size()
68  
                : max_size)
111  
                : max_size)
69  
    {
112  
    {
70  
        if(s_->size() > max_size_)
113  
        if(s_->size() > max_size_)
71  
            s_->resize(max_size_);
114  
            s_->resize(max_size_);
72  
        in_size_ = s_->size();
115  
        in_size_ = s_->size();
73  
    }
116  
    }
74  

117  

75 -
    /** Assignment.
118 +
    /// Copy assignment is deleted.
76 -
    */
 
77  
    basic_string_dynamic_buffer& operator=(
119  
    basic_string_dynamic_buffer& operator=(
78  
        basic_string_dynamic_buffer const&) = delete;
120  
        basic_string_dynamic_buffer const&) = delete;
79  

121  

 
122 +
    /// Return the number of readable bytes.
80  
    std::size_t
123  
    std::size_t
81  
    size() const noexcept
124  
    size() const noexcept
82  
    {
125  
    {
83  
        return in_size_;
126  
        return in_size_;
84  
    }
127  
    }
85  

128  

 
129 +
    /// Return the maximum number of bytes the buffer can hold.
86  
    std::size_t
130  
    std::size_t
87  
    max_size() const noexcept
131  
    max_size() const noexcept
88  
    {
132  
    {
89  
        return max_size_;
133  
        return max_size_;
90  
    }
134  
    }
91  

135  

 
136 +
    /// Return the number of writable bytes without reallocation.
92  
    std::size_t
137  
    std::size_t
93  
    capacity() const noexcept
138  
    capacity() const noexcept
94  
    {
139  
    {
95  
        if(s_->capacity() <= max_size_)
140  
        if(s_->capacity() <= max_size_)
96  
            return s_->capacity() - in_size_;
141  
            return s_->capacity() - in_size_;
97  
        return max_size_ - in_size_;
142  
        return max_size_ - in_size_;
98  
    }
143  
    }
99  

144  

 
145 +
    /// Return a buffer sequence representing the readable bytes.
100  
    const_buffers_type
146  
    const_buffers_type
101  
    data() const noexcept
147  
    data() const noexcept
102  
    {
148  
    {
103  
        return const_buffers_type(
149  
        return const_buffers_type(
104  
            s_->data(), in_size_);
150  
            s_->data(), in_size_);
105  
    }
151  
    }
106  

152  

 
153 +
    /** Prepare writable space of at least `n` bytes.
 
154 +

 
155 +
        Invalidates iterators and references returned by
 
156 +
        previous calls to `data` and `prepare`.
 
157 +

 
158 +
        @throws std::invalid_argument if `n` exceeds
 
159 +
            available space.
 
160 +

 
161 +
        @param n The number of bytes to prepare.
 
162 +

 
163 +
        @return A mutable buffer of exactly `n` bytes.
 
164 +
    */
107  
    mutable_buffers_type
165  
    mutable_buffers_type
108  
    prepare(std::size_t n)
166  
    prepare(std::size_t n)
109  
    {
167  
    {
110  
        // n exceeds available space
168  
        // n exceeds available space
111  
        if(n > max_size_ - in_size_)
169  
        if(n > max_size_ - in_size_)
112  
            detail::throw_invalid_argument();
170  
            detail::throw_invalid_argument();
113  

171  

114  
        if( s_->size() < in_size_ + n)
172  
        if( s_->size() < in_size_ + n)
115  
            s_->resize(in_size_ + n);
173  
            s_->resize(in_size_ + n);
116  
        out_size_ = n;
174  
        out_size_ = n;
117  
        return mutable_buffers_type(
175  
        return mutable_buffers_type(
118  
            &(*s_)[in_size_], out_size_);
176  
            &(*s_)[in_size_], out_size_);
119  
    }
177  
    }
120  

178  

 
179 +
    /** Move bytes from the writable to the readable area.
 
180 +

 
181 +
        Invalidates iterators and references returned by
 
182 +
        previous calls to `data` and `prepare`.
 
183 +

 
184 +
        @param n The number of bytes to commit. Clamped
 
185 +
            to the size of the writable area.
 
186 +
    */
121  
    void commit(std::size_t n) noexcept
187  
    void commit(std::size_t n) noexcept
122  
    {
188  
    {
123  
        if(n < out_size_)
189  
        if(n < out_size_)
124  
            in_size_ += n;
190  
            in_size_ += n;
125  
        else
191  
        else
126  
            in_size_ += out_size_;
192  
            in_size_ += out_size_;
127  
        out_size_ = 0;
193  
        out_size_ = 0;
128  
        s_->resize(in_size_);
194  
        s_->resize(in_size_);
129  
    }
195  
    }
130  

196  

 
197 +
    /** Remove bytes from the beginning of the readable area.
 
198 +

 
199 +
        Invalidates iterators and references returned by
 
200 +
        previous calls to `data` and `prepare`.
 
201 +

 
202 +
        @param n The number of bytes to consume. Clamped
 
203 +
            to the number of readable bytes.
 
204 +
    */
131  
    void consume(std::size_t n) noexcept
205  
    void consume(std::size_t n) noexcept
132  
    {
206  
    {
133  
        if(n < in_size_)
207  
        if(n < in_size_)
134  
        {
208  
        {
135  
            s_->erase(0, n);
209  
            s_->erase(0, n);
136  
            in_size_ -= n;
210  
            in_size_ -= n;
137  
        }
211  
        }
138  
        else
212  
        else
139  
        {
213  
        {
140  
            s_->clear();
214  
            s_->clear();
141  
            in_size_ = 0;
215  
            in_size_ = 0;
142  
        }
216  
        }
143  
        out_size_ = 0;
217  
        out_size_ = 0;
144  
    }
218  
    }
145  
};
219  
};
146  

220  

 
221 +
/// A dynamic buffer using `std::string`.
147  
using string_dynamic_buffer = basic_string_dynamic_buffer<char>;
222  
using string_dynamic_buffer = basic_string_dynamic_buffer<char>;
148  

223  

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

225  

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

238  

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

241  

167  
#endif
242  
#endif