userver: userver/utils/small_string.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
small_string.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/small_string.hpp
4/// @brief @copybrief utils::SmallString
5
6#include <cstddef>
7#include <functional>
8#include <stdexcept>
9#include <string>
10
11#include <boost/container/small_vector.hpp>
12
13#include <userver/utils/assert.hpp>
14
15USERVER_NAMESPACE_BEGIN
16
17namespace utils {
18
19/// @ingroup userver_universal userver_containers
20///
21/// @brief An alternative to std::string with a custom SSO (small string
22/// optimization) container size. Unlike std::string, SmallString is not
23/// null-terminated thus it has no c_str(), data() returns a not null-terminated
24/// buffer.
25template <std::size_t N>
26class SmallString final {
27 using Container = boost::container::small_vector<char, N>;
28
29public:
30 using value_type = char;
31
32 /// @brief Create empty string.
33 SmallString() = default;
34
35 /// @brief Create a string from another one.
36 SmallString(const SmallString<N>&) = default;
37
38 /// @brief Create a string from another one.
39 explicit SmallString(SmallString<N>&&) noexcept = default;
40
41 /// @brief Create a string from std::string_view.
42 explicit SmallString(std::string_view sv);
43
44 /// @brief Assign the value of other string_view to this string.
45 SmallString& operator=(std::string_view sv);
46
47 /// @brief Assign the value of other string to this string.
48 SmallString& operator=(const SmallString&) = default;
49
50 /// @brief Assign the value of other string to this string.
51 SmallString& operator=(SmallString&&) noexcept = default;
52
53 SmallString& operator+=(std::string_view sv) {
54 append(sv);
55 return *this;
56 }
57
58 SmallString& operator+=(char c) {
60 return *this;
61 }
62
63 /// @brief Convert string to a std::string_view.
64 operator std::string_view() const;
65
66 /// @brief Read-only subscript access to the data contained in the string.
67 const char& operator[](std::size_t pos) const;
68
69 /// @brief Subscript access to the data contained in the string.
70 char& operator[](std::size_t pos);
71
72 /// @brief Provides access to the data contained in the string.
73 const char& at(std::size_t pos) const;
74
75 /// @brief Provides access to the data contained in the string.
76 char& at(std::size_t pos);
77
78 using iterator = typename Container::iterator;
79
80 using const_iterator = typename Container::const_iterator;
81
82 iterator begin() noexcept;
83 const_iterator begin() const noexcept;
84
85 iterator end() noexcept;
86 const_iterator end() const noexcept;
87
88 /// @brief Get string size.
89 std::size_t size() const noexcept;
90
91 /// @brief Get pointer to data.
92 /// @warning The buffer is not null-terminated.
93 const char* data() const noexcept;
94
95 /// @brief Get pointer to data.
96 /// @warning The buffer is not null-terminated.
97 char* data() noexcept;
98
99 /// @brief Resize the string. If its length is increased,
100 /// fill new chars with %c.
101 void resize(std::size_t n, char c);
102
103 /// @brief Resize the string. If its length is increased,
104 /// fill new chars with \0.
105 void resize(std::size_t n);
106
107 /// @brief Resize the string. Use op to write into the string and replace a
108 /// sequence of characters
109 template <class Operation>
110 void resize_and_overwrite(std::size_t size, Operation op);
111
112 /// @brief Shrink the string's size to fit all contents
114
115 /// @brief Get current capacity.
116 std::size_t capacity() const noexcept;
117
118 /// @brief Reserve to %n bytes.
119 void reserve(std::size_t n);
120
121 /// @brief Clear the string.
122 void clear() noexcept;
123
124 /// @brief Is the string empty?
125 bool empty() const noexcept;
126
127 /// @brief Get a reference to the first character.
128 char& front();
129
130 /// @brief Get a reference to the first character.
131 const char& front() const;
132
133 /// @brief Get a reference to the last character.
134 char& back();
135
136 /// @brief Get a reference to the last character.
137 const char& back() const;
138
139 /// @brief Append a character to the string.
140 void push_back(char c);
141
142 /// @brief Append contents of a string_view to the string.
143 void append(std::string_view str);
144
145 /// @brief Append contents of a begin...end to the string.
146 void append(const char* begin, const char* end);
147
148 /// @brief Inserts elements from range [begin, end) before pos.
149 template <class InputIt>
150 void insert(const_iterator pos, InputIt begin, InputIt end);
151
152 /// @brief Remove the last character from the string.
153 void pop_back();
154
155private:
156 boost::container::small_vector<char, N> data_;
157};
158
159template <std::size_t N>
160SmallString<N>::SmallString(std::string_view sv) : data_(sv.begin(), sv.end()) {}
161
162template <std::size_t N>
163SmallString<N>::operator std::string_view() const {
164 return std::string_view{data_.data(), data_.size()};
165}
166
167template <std::size_t N>
168SmallString<N>& SmallString<N>::operator=(std::string_view sv) {
169 data_ = {sv.begin(), sv.end()};
170 return *this;
171}
172
173template <std::size_t N>
174bool operator==(const SmallString<N>& str, std::string_view sv) {
175 return std::string_view{str} == sv;
176}
177
178template <std::size_t N>
179bool operator==(std::string_view sv, const SmallString<N>& str) {
180 return std::string_view{str} == sv;
181}
182
183template <std::size_t N>
184bool operator==(const SmallString<N>& str1, const SmallString<N>& str2) {
185 return std::string_view{str1} == std::string_view{str2};
186}
187
188template <std::size_t N>
189bool operator!=(const SmallString<N>& str1, const SmallString<N>& str2) {
190 return !(str1 == str2);
191}
192
193template <std::size_t N>
194const char& SmallString<N>::operator[](std::size_t pos) const {
195 return data_[pos];
196}
197
198template <std::size_t N>
199char& SmallString<N>::operator[](std::size_t pos) {
200 return data_[pos];
201}
202
203template <std::size_t N>
204const char& SmallString<N>::at(std::size_t pos) const {
205 if (size() <= pos) throw std::out_of_range("at");
206 return data_[pos];
207}
208
209template <std::size_t N>
210char& SmallString<N>::at(std::size_t pos) {
211 if (size() <= pos) throw std::out_of_range("at");
212 return data_[pos];
213}
214
215template <std::size_t N>
216typename SmallString<N>::iterator SmallString<N>::begin() noexcept {
217 return {data_.begin()};
218}
219
220template <std::size_t N>
221typename SmallString<N>::const_iterator SmallString<N>::begin() const noexcept {
222 return {data_.begin()};
223}
224
225template <std::size_t N>
226typename SmallString<N>::iterator SmallString<N>::end() noexcept {
227 return {data_.end()};
228}
229
230template <std::size_t N>
231typename SmallString<N>::const_iterator SmallString<N>::end() const noexcept {
232 return {data_.end()};
233}
234
235template <std::size_t N>
236std::size_t SmallString<N>::size() const noexcept {
237 return data_.size();
238}
239
240template <std::size_t N>
241const char* SmallString<N>::data() const noexcept {
242 return data_.data();
243}
244
245template <std::size_t N>
246char* SmallString<N>::data() noexcept {
247 return data_.data();
248}
249
250template <std::size_t N>
251bool SmallString<N>::empty() const noexcept {
252 return data_.empty();
253}
254
255template <std::size_t N>
256char& SmallString<N>::front() {
257 return data_.front();
258}
259
260template <std::size_t N>
261const char& SmallString<N>::front() const {
262 return data_.front();
263}
264
265template <std::size_t N>
266char& SmallString<N>::back() {
267 return data_.back();
268}
269
270template <std::size_t N>
271const char& SmallString<N>::back() const {
272 return data_.back();
273}
274
275template <std::size_t N>
276void SmallString<N>::push_back(char c) {
277 data_.push_back(c);
278}
279
280template <std::size_t N>
281void SmallString<N>::append(std::string_view str) {
282 std::size_t old_size = data_.size();
283 data_.insert(data_.begin() + old_size, str.begin(), str.end());
284}
285
286template <std::size_t N>
287void SmallString<N>::append(const char* begin, const char* end) {
288 append(std::string_view{begin, static_cast<std::size_t>(end - begin)});
289}
290
291template <std::size_t N>
292template <class InputIt>
293void SmallString<N>::insert(SmallString::const_iterator pos, InputIt begin, InputIt end) {
294 data_.insert(pos, begin, end);
295}
296
297template <std::size_t N>
298void SmallString<N>::pop_back() {
299 data_.pop_back();
300}
301
302template <std::size_t N>
303void SmallString<N>::resize(std::size_t n, char c) {
304 data_.resize(n, c);
305}
306
307template <std::size_t N>
308void SmallString<N>::resize(std::size_t n) {
309 resize(n, '\0');
310}
311
312template <std::size_t N>
313template <class Operation>
314void SmallString<N>::resize_and_overwrite(std::size_t size, Operation op) {
315 data_.resize(size, boost::container::default_init);
316 data_.resize(std::move(op)(data_.data(), size), boost::container::default_init);
317 UASSERT(data_.size() <= size);
318}
319
320template <std::size_t N>
321void SmallString<N>::shrink_to_fit() {
322 data_.shrink_to_fit();
323}
324
325template <std::size_t N>
326std::size_t SmallString<N>::capacity() const noexcept {
327 return data_.capacity();
328}
329
330template <std::size_t N>
331void SmallString<N>::reserve(std::size_t n) {
332 return data_.reserve(n);
333}
334
335template <std::size_t N>
336void SmallString<N>::clear() noexcept {
337 data_.clear();
338}
339
340} // namespace utils
341
342USERVER_NAMESPACE_END
343
344template <std::size_t N>
345struct std::hash<USERVER_NAMESPACE::utils::SmallString<N>> {
346 std::size_t operator()(const USERVER_NAMESPACE::utils::SmallString<N>& s) const noexcept {
347 return std::hash<std::string_view>{}(std::string_view{s});
348 }
349};