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