1 | //===-- A simple implementation of the string class -------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_STRING_H |
10 | #define LLVM_LIBC_SRC___SUPPORT_CPP_STRING_H |
11 | |
12 | #include "src/__support/CPP/string_view.h" |
13 | #include "src/__support/integer_to_string.h" // IntegerToString |
14 | #include "src/string/memory_utils/inline_memcpy.h" |
15 | #include "src/string/memory_utils/inline_memset.h" |
16 | #include "src/string/string_utils.h" // string_length |
17 | |
18 | #include <stddef.h> // size_t |
19 | #include <stdlib.h> // malloc, free |
20 | |
21 | namespace LIBC_NAMESPACE { |
22 | namespace cpp { |
23 | |
24 | // This class mimics std::string but does not intend to be a full fledged |
25 | // implementation. Most notably it does not provide support for character traits |
26 | // nor custom allocator. |
27 | class string { |
28 | private: |
29 | static constexpr char NULL_CHARACTER = '\0'; |
30 | static constexpr char *get_empty_string() { |
31 | return const_cast<char *>(&NULL_CHARACTER); |
32 | } |
33 | |
34 | char *buffer_ = get_empty_string(); |
35 | size_t size_ = 0; |
36 | size_t capacity_ = 0; |
37 | |
38 | constexpr void reset_no_deallocate() { |
39 | buffer_ = get_empty_string(); |
40 | size_ = 0; |
41 | capacity_ = 0; |
42 | } |
43 | |
44 | void set_size_and_add_null_character(size_t size) { |
45 | size_ = size; |
46 | if (buffer_ != get_empty_string()) |
47 | buffer_[size_] = NULL_CHARACTER; |
48 | } |
49 | |
50 | public: |
51 | LIBC_INLINE constexpr string() {} |
52 | LIBC_INLINE string(const string &other) { this->operator+=(rhs: other); } |
53 | LIBC_INLINE constexpr string(string &&other) |
54 | : buffer_(other.buffer_), size_(other.size_), capacity_(other.capacity_) { |
55 | other.reset_no_deallocate(); |
56 | } |
57 | LIBC_INLINE string(const char *cstr, size_t count) { |
58 | resize(size: count); |
59 | inline_memcpy(dst: buffer_, src: cstr, count); |
60 | } |
61 | LIBC_INLINE string(const string_view &view) |
62 | : string(view.data(), view.size()) {} |
63 | LIBC_INLINE string(const char *cstr) |
64 | : string(cstr, ::LIBC_NAMESPACE::internal::string_length(src: cstr)) {} |
65 | LIBC_INLINE string(size_t size_, char value) { |
66 | resize(size: size_); |
67 | inline_memset(dst: (void *)buffer_, value, count: size_); |
68 | } |
69 | |
70 | LIBC_INLINE string &operator=(const string &other) { |
71 | resize(size: 0); |
72 | return (*this) += other; |
73 | } |
74 | |
75 | LIBC_INLINE string &operator=(string &&other) { |
76 | buffer_ = other.buffer_; |
77 | size_ = other.size_; |
78 | capacity_ = other.capacity_; |
79 | other.reset_no_deallocate(); |
80 | return *this; |
81 | } |
82 | |
83 | LIBC_INLINE string &operator=(const string_view &view) { |
84 | return *this = string(view); |
85 | } |
86 | |
87 | LIBC_INLINE ~string() { |
88 | if (buffer_ != get_empty_string()) |
89 | ::free(ptr: buffer_); |
90 | } |
91 | |
92 | LIBC_INLINE constexpr size_t capacity() const { return capacity_; } |
93 | LIBC_INLINE constexpr size_t size() const { return size_; } |
94 | LIBC_INLINE constexpr bool empty() const { return size_ == 0; } |
95 | |
96 | LIBC_INLINE constexpr const char *data() const { return buffer_; } |
97 | LIBC_INLINE char *data() { return buffer_; } |
98 | |
99 | LIBC_INLINE constexpr const char *begin() const { return data(); } |
100 | LIBC_INLINE char *begin() { return data(); } |
101 | |
102 | LIBC_INLINE constexpr const char *end() const { return data() + size_; } |
103 | LIBC_INLINE char *end() { return data() + size_; } |
104 | |
105 | LIBC_INLINE constexpr const char &front() const { return data()[0]; } |
106 | LIBC_INLINE char &front() { return data()[0]; } |
107 | |
108 | LIBC_INLINE constexpr const char &back() const { return data()[size_ - 1]; } |
109 | LIBC_INLINE char &back() { return data()[size_ - 1]; } |
110 | |
111 | LIBC_INLINE constexpr const char &operator[](size_t index) const { |
112 | return data()[index]; |
113 | } |
114 | LIBC_INLINE char &operator[](size_t index) { return data()[index]; } |
115 | |
116 | LIBC_INLINE const char *c_str() const { return data(); } |
117 | |
118 | LIBC_INLINE operator string_view() const { |
119 | return string_view(buffer_, size_); |
120 | } |
121 | |
122 | LIBC_INLINE void reserve(size_t new_capacity) { |
123 | ++new_capacity; // Accounting for the terminating '\0' |
124 | if (new_capacity <= capacity_) |
125 | return; |
126 | // We extend the capacity to amortize buffer_ reallocations. |
127 | // We choose to augment the value by 11 / 8, this is about +40% and division |
128 | // by 8 is cheap. We guard the extension so the operation doesn't overflow. |
129 | if (new_capacity < SIZE_MAX / 11) |
130 | new_capacity = new_capacity * 11 / 8; |
131 | if (void *Ptr = ::realloc(ptr: buffer_ == get_empty_string() ? nullptr : buffer_, |
132 | size: new_capacity)) { |
133 | buffer_ = static_cast<char *>(Ptr); |
134 | capacity_ = new_capacity; |
135 | } else { |
136 | __builtin_unreachable(); // out of memory |
137 | } |
138 | } |
139 | |
140 | LIBC_INLINE void resize(size_t size) { |
141 | if (size > capacity_) { |
142 | reserve(new_capacity: size); |
143 | const size_t size_extension = size - size_; |
144 | inline_memset(dst: data() + size_, value: '\0', count: size_extension); |
145 | } |
146 | set_size_and_add_null_character(size); |
147 | } |
148 | |
149 | LIBC_INLINE string &operator+=(const string &rhs) { |
150 | const size_t new_size = size_ + rhs.size(); |
151 | reserve(new_capacity: new_size); |
152 | inline_memcpy(dst: buffer_ + size_, src: rhs.data(), count: rhs.size()); |
153 | set_size_and_add_null_character(new_size); |
154 | return *this; |
155 | } |
156 | |
157 | LIBC_INLINE string &operator+=(const char c) { |
158 | const size_t new_size = size_ + 1; |
159 | reserve(new_capacity: new_size); |
160 | buffer_[size_] = c; |
161 | set_size_and_add_null_character(new_size); |
162 | return *this; |
163 | } |
164 | }; |
165 | |
166 | LIBC_INLINE bool operator==(const string &lhs, const string &rhs) { |
167 | return string_view(lhs) == string_view(rhs); |
168 | } |
169 | LIBC_INLINE bool operator!=(const string &lhs, const string &rhs) { |
170 | return string_view(lhs) != string_view(rhs); |
171 | } |
172 | LIBC_INLINE bool operator<(const string &lhs, const string &rhs) { |
173 | return string_view(lhs) < string_view(rhs); |
174 | } |
175 | LIBC_INLINE bool operator<=(const string &lhs, const string &rhs) { |
176 | return string_view(lhs) <= string_view(rhs); |
177 | } |
178 | LIBC_INLINE bool operator>(const string &lhs, const string &rhs) { |
179 | return string_view(lhs) > string_view(rhs); |
180 | } |
181 | LIBC_INLINE bool operator>=(const string &lhs, const string &rhs) { |
182 | return string_view(lhs) >= string_view(rhs); |
183 | } |
184 | |
185 | LIBC_INLINE string operator+(const string &lhs, const string &rhs) { |
186 | string Tmp(lhs); |
187 | return Tmp += rhs; |
188 | } |
189 | LIBC_INLINE string operator+(const string &lhs, const char *rhs) { |
190 | return lhs + string(rhs); |
191 | } |
192 | LIBC_INLINE string operator+(const char *lhs, const string &rhs) { |
193 | return string(lhs) + rhs; |
194 | } |
195 | |
196 | namespace internal { |
197 | template <typename T> string to_dec_string(T value) { |
198 | const IntegerToString<T> buffer(value); |
199 | return buffer.view(); |
200 | } |
201 | } // namespace internal |
202 | |
203 | LIBC_INLINE string to_string(int value) { |
204 | return internal::to_dec_string<int>(value); |
205 | } |
206 | LIBC_INLINE string to_string(long value) { |
207 | return internal::to_dec_string<long>(value); |
208 | } |
209 | LIBC_INLINE string to_string(long long value) { |
210 | return internal::to_dec_string<long long>(value); |
211 | } |
212 | LIBC_INLINE string to_string(unsigned value) { |
213 | return internal::to_dec_string<unsigned>(value); |
214 | } |
215 | LIBC_INLINE string to_string(unsigned long value) { |
216 | return internal::to_dec_string<unsigned long>(value); |
217 | } |
218 | LIBC_INLINE string to_string(unsigned long long value) { |
219 | return internal::to_dec_string<unsigned long long>(value); |
220 | } |
221 | |
222 | // TODO: Support floating point |
223 | // LIBC_INLINE string to_string(float value); |
224 | // LIBC_INLINE string to_string(double value); |
225 | // LIBC_INLINE string to_string(long double value); |
226 | |
227 | } // namespace cpp |
228 | } // namespace LIBC_NAMESPACE |
229 | |
230 | #endif // LLVM_LIBC_SRC___SUPPORT_CPP_STRING_H |
231 | |