TLA Line data Source code
1 : //
2 : // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_BUFFERS_STRING_DYNAMIC_BUFFER_HPP
11 : #define BOOST_CAPY_BUFFERS_STRING_DYNAMIC_BUFFER_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/buffers.hpp>
15 : #include <boost/capy/detail/except.hpp>
16 : #include <string>
17 :
18 : namespace boost {
19 : namespace capy {
20 :
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
53 : */
54 : template<
55 : class CharT,
56 : class Traits = std::char_traits<CharT>,
57 : class Allocator = std::allocator<CharT>>
58 : class basic_string_dynamic_buffer
59 : {
60 : std::basic_string<
61 : CharT, Traits, Allocator>* s_;
62 : std::size_t max_size_;
63 :
64 : std::size_t in_size_ = 0;
65 : std::size_t out_size_ = 0;
66 :
67 : public:
68 : /// Indicates this is a DynamicBuffer adapter over external storage.
69 : using is_dynamic_buffer_adapter = void;
70 :
71 : /// The underlying string type.
72 : using string_type = std::basic_string<
73 : CharT, Traits, Allocator>;
74 :
75 : /// The ConstBufferSequence type for readable bytes.
76 : using const_buffers_type = const_buffer;
77 :
78 : /// The MutableBufferSequence type for writable bytes.
79 : using mutable_buffers_type = mutable_buffer;
80 :
81 : /// Destroy the buffer.
82 : ~basic_string_dynamic_buffer() = default;
83 :
84 : /// Construct by moving from another buffer.
85 HIT 266 : basic_string_dynamic_buffer(
86 : basic_string_dynamic_buffer&& other) noexcept
87 266 : : s_(other.s_)
88 266 : , max_size_(other.max_size_)
89 266 : , in_size_(other.in_size_)
90 266 : , out_size_(other.out_size_)
91 : {
92 266 : other.s_ = nullptr;
93 266 : }
94 :
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.
101 : */
102 : explicit
103 427 : basic_string_dynamic_buffer(
104 : string_type* s,
105 : std::size_t max_size =
106 : std::size_t(-1)) noexcept
107 427 : : s_(s)
108 427 : , max_size_(
109 427 : max_size > s_->max_size()
110 427 : ? s_->max_size()
111 427 : : max_size)
112 : {
113 427 : if(s_->size() > max_size_)
114 1 : s_->resize(max_size_);
115 427 : in_size_ = s_->size();
116 427 : }
117 :
118 : /// Copy assignment is deleted.
119 : basic_string_dynamic_buffer& operator=(
120 : basic_string_dynamic_buffer const&) = delete;
121 :
122 : /// Return the number of readable bytes.
123 : std::size_t
124 1280 : size() const noexcept
125 : {
126 1280 : return in_size_;
127 : }
128 :
129 : /// Return the maximum number of bytes the buffer can hold.
130 : std::size_t
131 410 : max_size() const noexcept
132 : {
133 410 : return max_size_;
134 : }
135 :
136 : /// Return the number of writable bytes without reallocation.
137 : std::size_t
138 2 : capacity() const noexcept
139 : {
140 2 : if(s_->capacity() <= max_size_)
141 1 : return s_->capacity() - in_size_;
142 1 : return max_size_ - in_size_;
143 : }
144 :
145 : /// Return a buffer sequence representing the readable bytes.
146 : const_buffers_type
147 387 : data() const noexcept
148 : {
149 387 : return const_buffers_type(
150 774 : s_->data(), in_size_);
151 : }
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 : */
165 : mutable_buffers_type
166 533 : prepare(std::size_t n)
167 : {
168 : // n exceeds available space
169 533 : if(n > max_size_ - in_size_)
170 1 : detail::throw_invalid_argument();
171 :
172 532 : if( s_->size() < in_size_ + n)
173 531 : s_->resize(in_size_ + n);
174 532 : out_size_ = n;
175 532 : return mutable_buffers_type(
176 1064 : &(*s_)[in_size_], out_size_);
177 : }
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 : */
187 434 : void commit(std::size_t n) noexcept
188 : {
189 434 : if(n < out_size_)
190 242 : in_size_ += n;
191 : else
192 192 : in_size_ += out_size_;
193 434 : out_size_ = 0;
194 434 : s_->resize(in_size_);
195 434 : }
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 : */
205 168 : void consume(std::size_t n) noexcept
206 : {
207 168 : if(n < in_size_)
208 : {
209 3 : s_->erase(0, n);
210 3 : in_size_ -= n;
211 : }
212 : else
213 : {
214 165 : s_->clear();
215 165 : in_size_ = 0;
216 : }
217 168 : out_size_ = 0;
218 168 : }
219 : };
220 :
221 : /// A dynamic buffer using `std::string`.
222 : using string_dynamic_buffer = basic_string_dynamic_buffer<char>;
223 :
224 : /** Create a dynamic buffer from a string.
225 :
226 : @param s The string to wrap.
227 : @param max_size Optional maximum size limit.
228 : @return A string_dynamic_buffer wrapping the string.
229 : */
230 : template<class CharT, class Traits, class Allocator>
231 : basic_string_dynamic_buffer<CharT, Traits, Allocator>
232 132 : dynamic_buffer(
233 : std::basic_string<CharT, Traits, Allocator>& s,
234 : std::size_t max_size = std::size_t(-1))
235 : {
236 132 : return basic_string_dynamic_buffer<CharT, Traits, Allocator>(&s, max_size);
237 : }
238 :
239 : } // capy
240 : } // boost
241 :
242 : #endif
|