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 backed by a `std::basic_string`.
22  

22  

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

28  

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

32  

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

36  

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

47  

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

51  

52  
    @see DynamicBuffer, string_dynamic_buffer, dynamic_buffer
52  
    @see DynamicBuffer, string_dynamic_buffer, dynamic_buffer
53  
*/
53  
*/
54  
template<
54  
template<
55  
    class CharT,
55  
    class CharT,
56  
    class Traits = std::char_traits<CharT>,
56  
    class Traits = std::char_traits<CharT>,
57  
    class Allocator = std::allocator<CharT>>
57  
    class Allocator = std::allocator<CharT>>
58  
class basic_string_dynamic_buffer
58  
class basic_string_dynamic_buffer
59  
{
59  
{
60  
    std::basic_string<
60  
    std::basic_string<
61  
        CharT, Traits, Allocator>* s_;
61  
        CharT, Traits, Allocator>* s_;
62  
    std::size_t max_size_;
62  
    std::size_t max_size_;
63  

63  

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

66  

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

70  

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

74  

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

77  

78  
    /// The MutableBufferSequence type for writable bytes.
78  
    /// The MutableBufferSequence type for writable bytes.
79  
    using mutable_buffers_type = mutable_buffer;
79  
    using mutable_buffers_type = mutable_buffer;
80  

80  

81  
    /// Destroy the buffer.
81  
    /// Destroy the buffer.
82  
    ~basic_string_dynamic_buffer() = default;
82  
    ~basic_string_dynamic_buffer() = default;
83  

83  

84  
    /// Construct by moving from another buffer.
84  
    /// Construct by moving from another buffer.
85  
    basic_string_dynamic_buffer(
85  
    basic_string_dynamic_buffer(
86  
        basic_string_dynamic_buffer&& other) noexcept
86  
        basic_string_dynamic_buffer&& other) noexcept
87  
        : s_(other.s_)
87  
        : s_(other.s_)
88  
        , max_size_(other.max_size_)
88  
        , max_size_(other.max_size_)
89  
        , in_size_(other.in_size_)
89  
        , in_size_(other.in_size_)
90  
        , out_size_(other.out_size_)
90  
        , out_size_(other.out_size_)
91  
    {
91  
    {
92  
        other.s_ = nullptr;
92  
        other.s_ = nullptr;
93  
    }
93  
    }
94  

94  

95  
    /** Construct from an existing string.
95  
    /** Construct from an existing string.
96  

96  

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

117  

118  
    /// Copy assignment is deleted.
118  
    /// Copy assignment is deleted.
119  
    basic_string_dynamic_buffer& operator=(
119  
    basic_string_dynamic_buffer& operator=(
120  
        basic_string_dynamic_buffer const&) = delete;
120  
        basic_string_dynamic_buffer const&) = delete;
121  

121  

122  
    /// Return the number of readable bytes.
122  
    /// Return the number of readable bytes.
123  
    std::size_t
123  
    std::size_t
124  
    size() const noexcept
124  
    size() const noexcept
125  
    {
125  
    {
126  
        return in_size_;
126  
        return in_size_;
127  
    }
127  
    }
128  

128  

129  
    /// Return the maximum number of bytes the buffer can hold.
129  
    /// Return the maximum number of bytes the buffer can hold.
130  
    std::size_t
130  
    std::size_t
131  
    max_size() const noexcept
131  
    max_size() const noexcept
132  
    {
132  
    {
133  
        return max_size_;
133  
        return max_size_;
134  
    }
134  
    }
135  

135  

136  
    /// Return the number of writable bytes without reallocation.
136  
    /// Return the number of writable bytes without reallocation.
137  
    std::size_t
137  
    std::size_t
138  
    capacity() const noexcept
138  
    capacity() const noexcept
139  
    {
139  
    {
140  
        if(s_->capacity() <= max_size_)
140  
        if(s_->capacity() <= max_size_)
141  
            return s_->capacity() - in_size_;
141  
            return s_->capacity() - in_size_;
142  
        return max_size_ - in_size_;
142  
        return max_size_ - in_size_;
143  
    }
143  
    }
144  

144  

145  
    /// Return a buffer sequence representing the readable bytes.
145  
    /// Return a buffer sequence representing the readable bytes.
146  
    const_buffers_type
146  
    const_buffers_type
147  
    data() const noexcept
147  
    data() const noexcept
148  
    {
148  
    {
149  
        return const_buffers_type(
149  
        return const_buffers_type(
150  
            s_->data(), in_size_);
150  
            s_->data(), in_size_);
151  
    }
151  
    }
152  

152  

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

154  

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

157  

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

160  

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

162  

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

171  

172  
        if( s_->size() < in_size_ + n)
172  
        if( s_->size() < in_size_ + n)
173  
            s_->resize(in_size_ + n);
173  
            s_->resize(in_size_ + n);
174  
        out_size_ = n;
174  
        out_size_ = n;
175  
        return mutable_buffers_type(
175  
        return mutable_buffers_type(
176  
            &(*s_)[in_size_], out_size_);
176  
            &(*s_)[in_size_], out_size_);
177  
    }
177  
    }
178  

178  

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

180  

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

183  

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

196  

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

198  

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

201  

202  
        @param n The number of bytes to consume. Clamped
202  
        @param n The number of bytes to consume. Clamped
203  
            to the number of readable bytes.
203  
            to the number of readable bytes.
204  
    */
204  
    */
205  
    void consume(std::size_t n) noexcept
205  
    void consume(std::size_t n) noexcept
206  
    {
206  
    {
207  
        if(n < in_size_)
207  
        if(n < in_size_)
208  
        {
208  
        {
209  
            s_->erase(0, n);
209  
            s_->erase(0, n);
210  
            in_size_ -= n;
210  
            in_size_ -= n;
211  
        }
211  
        }
212  
        else
212  
        else
213  
        {
213  
        {
214  
            s_->clear();
214  
            s_->clear();
215  
            in_size_ = 0;
215  
            in_size_ = 0;
216  
        }
216  
        }
217  
        out_size_ = 0;
217  
        out_size_ = 0;
218  
    }
218  
    }
219  
};
219  
};
220  

220  

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

223  

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

225  

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

238  

239  
} // capy
239  
} // capy
240  
} // boost
240  
} // boost
241  

241  

242  
#endif
242  
#endif