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
21namespace LIBC_NAMESPACE {
22namespace 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.
27class string {
28private:
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
50public:
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
166LIBC_INLINE bool operator==(const string &lhs, const string &rhs) {
167 return string_view(lhs) == string_view(rhs);
168}
169LIBC_INLINE bool operator!=(const string &lhs, const string &rhs) {
170 return string_view(lhs) != string_view(rhs);
171}
172LIBC_INLINE bool operator<(const string &lhs, const string &rhs) {
173 return string_view(lhs) < string_view(rhs);
174}
175LIBC_INLINE bool operator<=(const string &lhs, const string &rhs) {
176 return string_view(lhs) <= string_view(rhs);
177}
178LIBC_INLINE bool operator>(const string &lhs, const string &rhs) {
179 return string_view(lhs) > string_view(rhs);
180}
181LIBC_INLINE bool operator>=(const string &lhs, const string &rhs) {
182 return string_view(lhs) >= string_view(rhs);
183}
184
185LIBC_INLINE string operator+(const string &lhs, const string &rhs) {
186 string Tmp(lhs);
187 return Tmp += rhs;
188}
189LIBC_INLINE string operator+(const string &lhs, const char *rhs) {
190 return lhs + string(rhs);
191}
192LIBC_INLINE string operator+(const char *lhs, const string &rhs) {
193 return string(lhs) + rhs;
194}
195
196namespace internal {
197template <typename T> string to_dec_string(T value) {
198 const IntegerToString<T> buffer(value);
199 return buffer.view();
200}
201} // namespace internal
202
203LIBC_INLINE string to_string(int value) {
204 return internal::to_dec_string<int>(value);
205}
206LIBC_INLINE string to_string(long value) {
207 return internal::to_dec_string<long>(value);
208}
209LIBC_INLINE string to_string(long long value) {
210 return internal::to_dec_string<long long>(value);
211}
212LIBC_INLINE string to_string(unsigned value) {
213 return internal::to_dec_string<unsigned>(value);
214}
215LIBC_INLINE string to_string(unsigned long value) {
216 return internal::to_dec_string<unsigned long>(value);
217}
218LIBC_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

source code of libc/src/__support/CPP/string.h