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