|
| 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/memcpy_implementations.h" |
| 15 | +#include "src/string/memory_utils/memset_implementations.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 __llvm_libc { |
| 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+=(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(count); |
| 59 | + inline_memcpy((void *)buffer_, (const void *)cstr, count); |
| 60 | + } |
| 61 | + LIBC_INLINE string(const char *cstr) |
| 62 | + : string(cstr, ::__llvm_libc::internal::string_length(cstr)) {} |
| 63 | + LIBC_INLINE string(size_t size_, char value) { |
| 64 | + resize(size_); |
| 65 | + inline_memset((void *)buffer_, value, size_); |
| 66 | + } |
| 67 | + |
| 68 | + LIBC_INLINE string &operator=(const string &other) { |
| 69 | + resize(0); |
| 70 | + return (*this) += other; |
| 71 | + } |
| 72 | + |
| 73 | + LIBC_INLINE string &operator=(string &&other) { |
| 74 | + buffer_ = other.buffer_; |
| 75 | + size_ = other.size_; |
| 76 | + capacity_ = other.capacity_; |
| 77 | + other.reset_no_deallocate(); |
| 78 | + return *this; |
| 79 | + } |
| 80 | + |
| 81 | + LIBC_INLINE ~string() { |
| 82 | + if (buffer_ != get_empty_string()) |
| 83 | + ::free(buffer_); |
| 84 | + } |
| 85 | + |
| 86 | + LIBC_INLINE constexpr size_t capacity() const { return capacity_; } |
| 87 | + LIBC_INLINE constexpr size_t size() const { return size_; } |
| 88 | + LIBC_INLINE constexpr bool empty() const { return size_ == 0; } |
| 89 | + |
| 90 | + LIBC_INLINE constexpr const char *data() const { return buffer_; } |
| 91 | + LIBC_INLINE char *data() { return buffer_; } |
| 92 | + |
| 93 | + LIBC_INLINE constexpr const char *begin() const { return data(); } |
| 94 | + LIBC_INLINE char *begin() { return data(); } |
| 95 | + |
| 96 | + LIBC_INLINE constexpr const char *end() const { return data() + size_; } |
| 97 | + LIBC_INLINE char *end() { return data() + size_; } |
| 98 | + |
| 99 | + LIBC_INLINE constexpr const char &front() const { return data()[0]; } |
| 100 | + LIBC_INLINE char &front() { return data()[0]; } |
| 101 | + |
| 102 | + LIBC_INLINE constexpr const char &back() const { return data()[size_ - 1]; } |
| 103 | + LIBC_INLINE char &back() { return data()[size_ - 1]; } |
| 104 | + |
| 105 | + LIBC_INLINE constexpr const char &operator[](size_t index) const { |
| 106 | + return data()[index]; |
| 107 | + } |
| 108 | + LIBC_INLINE char &operator[](size_t index) { return data()[index]; } |
| 109 | + |
| 110 | + LIBC_INLINE const char *c_str() const { return data(); } |
| 111 | + |
| 112 | + LIBC_INLINE operator string_view() const { |
| 113 | + return string_view(buffer_, size_); |
| 114 | + } |
| 115 | + |
| 116 | + LIBC_INLINE void reserve(size_t new_capacity) { |
| 117 | + ++new_capacity; // Accounting for the terminating '\0' |
| 118 | + if (new_capacity <= capacity_) |
| 119 | + return; |
| 120 | + // We extend the capacity to amortize buffer_ reallocations. |
| 121 | + // We choose to augment the value by 11 / 8, this is about +40% and division |
| 122 | + // by 8 is cheap. We guard the extension so the operation doesn't overflow. |
| 123 | + if (new_capacity < SIZE_MAX / 11) |
| 124 | + new_capacity = new_capacity * 11 / 8; |
| 125 | + if (void *Ptr = ::realloc(buffer_ == get_empty_string() ? nullptr : buffer_, |
| 126 | + new_capacity)) { |
| 127 | + buffer_ = static_cast<char *>(Ptr); |
| 128 | + capacity_ = new_capacity; |
| 129 | + } else { |
| 130 | + __builtin_unreachable(); // out of memory |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + LIBC_INLINE void resize(size_t size) { |
| 135 | + if (size > capacity_) { |
| 136 | + reserve(size); |
| 137 | + const size_t size_extension = size - size_; |
| 138 | + inline_memset(data() + size_, '\0', size_extension); |
| 139 | + } |
| 140 | + set_size_and_add_null_character(size); |
| 141 | + } |
| 142 | + |
| 143 | + LIBC_INLINE string &operator+=(const string &rhs) { |
| 144 | + const size_t new_size = size_ + rhs.size(); |
| 145 | + reserve(new_size); |
| 146 | + inline_memcpy(buffer_ + size_, rhs.data(), rhs.size()); |
| 147 | + set_size_and_add_null_character(new_size); |
| 148 | + return *this; |
| 149 | + } |
| 150 | + |
| 151 | + LIBC_INLINE string &operator+=(const char c) { |
| 152 | + const size_t new_size = size_ + 1; |
| 153 | + reserve(new_size); |
| 154 | + buffer_[size_] = c; |
| 155 | + set_size_and_add_null_character(new_size); |
| 156 | + return *this; |
| 157 | + } |
| 158 | +}; |
| 159 | + |
| 160 | +LIBC_INLINE bool operator==(const string &lhs, const string &rhs) { |
| 161 | + return string_view(lhs) == string_view(rhs); |
| 162 | +} |
| 163 | +LIBC_INLINE bool operator!=(const string &lhs, const string &rhs) { |
| 164 | + return string_view(lhs) != string_view(rhs); |
| 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 | + |
| 179 | +LIBC_INLINE string operator+(const string &lhs, const string &rhs) { |
| 180 | + string Tmp(lhs); |
| 181 | + return Tmp += rhs; |
| 182 | +} |
| 183 | +LIBC_INLINE string operator+(const string &lhs, const char *rhs) { |
| 184 | + return lhs + string(rhs); |
| 185 | +} |
| 186 | +LIBC_INLINE string operator+(const char *lhs, const string &rhs) { |
| 187 | + return string(lhs) + rhs; |
| 188 | +} |
| 189 | + |
| 190 | +namespace internal { |
| 191 | +template <typename T> string to_dec_string(T value) { |
| 192 | + char dec_buf[IntegerToString::dec_bufsize<T>()]; |
| 193 | + auto maybe_string_view = IntegerToString::dec(value, dec_buf); |
| 194 | + const auto &string_view = *maybe_string_view; |
| 195 | + return string(string_view.data(), string_view.size()); |
| 196 | +} |
| 197 | +} // namespace internal |
| 198 | + |
| 199 | +LIBC_INLINE string to_string(int value) { |
| 200 | + return internal::to_dec_string<int>(value); |
| 201 | +} |
| 202 | +LIBC_INLINE string to_string(long value) { |
| 203 | + return internal::to_dec_string<long>(value); |
| 204 | +} |
| 205 | +LIBC_INLINE string to_string(long long value) { |
| 206 | + return internal::to_dec_string<long long>(value); |
| 207 | +} |
| 208 | +LIBC_INLINE string to_string(unsigned value) { |
| 209 | + return internal::to_dec_string<unsigned>(value); |
| 210 | +} |
| 211 | +LIBC_INLINE string to_string(unsigned long value) { |
| 212 | + return internal::to_dec_string<unsigned long>(value); |
| 213 | +} |
| 214 | +LIBC_INLINE string to_string(unsigned long long value) { |
| 215 | + return internal::to_dec_string<unsigned long long>(value); |
| 216 | +} |
| 217 | + |
| 218 | +// TODO: Support floating point |
| 219 | +// LIBC_INLINE string to_string(float value); |
| 220 | +// LIBC_INLINE string to_string(double value); |
| 221 | +// LIBC_INLINE string to_string(long double value); |
| 222 | + |
| 223 | +} // namespace cpp |
| 224 | +} // namespace __llvm_libc |
| 225 | + |
| 226 | +#endif // LLVM_LIBC_SRC_SUPPORT_CPP_STRING_H |
0 commit comments