userver: userver/utils/small_string.hpp Source File
Loading...
Searching...
No Matches
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)
161 : data_(sv.begin(), sv.end())
162{}
163
164template <std::size_t N>
165SmallString<N>::operator std::string_view() const {
166 return std::string_view{data_.data(), data_.size()};
167}
168
169template <std::size_t N>
170SmallString<N>& SmallString<N>::operator=(std::string_view sv) {
171 data_ = {sv.begin(), sv.end()};
172 return *this;
173}
174
175template <std::size_t N>
176bool operator==(const SmallString<N>& str, std::string_view sv) {
177 return std::string_view{str} == sv;
178}
179
180template <std::size_t N>
181bool operator==(std::string_view sv, const SmallString<N>& str) {
182 return std::string_view{str} == sv;
183}
184
185template <std::size_t N>
186bool operator==(const SmallString<N>& str1, const SmallString<N>& str2) {
187 return std::string_view{str1} == std::string_view{str2};
188}
189
190template <std::size_t N>
191bool operator!=(const SmallString<N>& str1, const SmallString<N>& str2) {
192 return !(str1 == str2);
193}
194
195template <std::size_t N>
196const char& SmallString<N>::operator[](std::size_t pos) const {
197 return data_[pos];
198}
199
200template <std::size_t N>
201char& SmallString<N>::operator[](std::size_t pos) {
202 return data_[pos];
203}
204
205template <std::size_t N>
206const char& SmallString<N>::at(std::size_t pos) const {
207 if (size() <= pos) {
208 throw std::out_of_range("at");
209 }
210 return data_[pos];
211}
212
213template <std::size_t N>
214char& SmallString<N>::at(std::size_t pos) {
215 if (size() <= pos) {
216 throw std::out_of_range("at");
217 }
218 return data_[pos];
219}
220
221template <std::size_t N>
222typename SmallString<N>::iterator SmallString<N>::begin() noexcept {
223 return {data_.begin()};
224}
225
226template <std::size_t N>
227typename SmallString<N>::const_iterator SmallString<N>::begin() const noexcept {
228 return {data_.begin()};
229}
230
231template <std::size_t N>
232typename SmallString<N>::iterator SmallString<N>::end() noexcept {
233 return {data_.end()};
234}
235
236template <std::size_t N>
237typename SmallString<N>::const_iterator SmallString<N>::end() const noexcept {
238 return {data_.end()};
239}
240
241template <std::size_t N>
242std::size_t SmallString<N>::size() const noexcept {
243 return data_.size();
244}
245
246template <std::size_t N>
247const char* SmallString<N>::data() const noexcept {
248 return data_.data();
249}
250
251template <std::size_t N>
252char* SmallString<N>::data() noexcept {
253 return data_.data();
254}
255
256template <std::size_t N>
257bool SmallString<N>::empty() const noexcept {
258 return data_.empty();
259}
260
261template <std::size_t N>
262char& SmallString<N>::front() {
263 return data_.front();
264}
265
266template <std::size_t N>
267const char& SmallString<N>::front() const {
268 return data_.front();
269}
270
271template <std::size_t N>
272char& SmallString<N>::back() {
273 return data_.back();
274}
275
276template <std::size_t N>
277const char& SmallString<N>::back() const {
278 return data_.back();
279}
280
281template <std::size_t N>
282void SmallString<N>::push_back(char c) {
283 data_.push_back(c);
284}
285
286template <std::size_t N>
287void SmallString<N>::append(std::string_view str) {
288 const std::size_t old_size = data_.size();
289 data_.insert(data_.begin() + old_size, str.begin(), str.end());
290}
291
292template <std::size_t N>
293void SmallString<N>::append(const char* begin, const char* end) {
294 append(std::string_view{begin, static_cast<std::size_t>(end - begin)});
295}
296
297template <std::size_t N>
298template <class InputIt>
299void SmallString<N>::insert(SmallString::const_iterator pos, InputIt begin, InputIt end) {
300 data_.insert(pos, begin, end);
301}
302
303template <std::size_t N>
304void SmallString<N>::pop_back() {
305 data_.pop_back();
306}
307
308template <std::size_t N>
309void SmallString<N>::resize(std::size_t n, char c) {
310 data_.resize(n, c);
311}
312
313template <std::size_t N>
314void SmallString<N>::resize(std::size_t n) {
315 resize(n, '\0');
316}
317
318template <std::size_t N>
319template <class Operation>
320void SmallString<N>::resize_and_overwrite(std::size_t size, Operation op) {
321 data_.resize(size, boost::container::default_init);
322 data_.resize(std::move(op)(data_.data(), size), boost::container::default_init);
323 UASSERT(data_.size() <= size);
324}
325
326template <std::size_t N>
327void SmallString<N>::shrink_to_fit() {
328 data_.shrink_to_fit();
329}
330
331template <std::size_t N>
332std::size_t SmallString<N>::capacity() const noexcept {
333 return data_.capacity();
334}
335
336template <std::size_t N>
337void SmallString<N>::reserve(std::size_t n) {
338 return data_.reserve(n);
339}
340
341template <std::size_t N>
342void SmallString<N>::clear() noexcept {
343 data_.clear();
344}
345
346} // namespace utils
347
348USERVER_NAMESPACE_END
349
350template <std::size_t N>
351struct std::hash<USERVER_NAMESPACE::utils::SmallString<N>> {
352 std::size_t operator()(const USERVER_NAMESPACE::utils::SmallString<N>& s) const noexcept {
353 return std::hash<std::string_view>{}(std::string_view{s});
354 }
355};