Source
stdlib/string/string.cpp
1
// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).2
// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.3
#include "string.hpp"5
#include <cctype>7
namespace cheatah::string {9
namespace {10
char up(char c) { return static_cast<char>(std::toupper(static_cast<unsigned char>(c))); }11
char lo(char c) { return static_cast<char>(std::tolower(static_cast<unsigned char>(c))); }12
bool in_set(char c, std::string_view set) { return set.find(c) != std::string_view::npos; }13
} // namespace15
std::string upper(std::string_view s) {16
std::string r(s);17
for (char& c : r) c = up(c);18
return r;19
}20
std::string lower(std::string_view s) {21
std::string r(s);22
for (char& c : r) c = lo(c);23
return r;24
}25
std::string capitalize(std::string_view s) {26
std::string r(s);27
if (!r.empty()) {28
r[0] = up(r[0]);29
for (std::size_t i = 1; i < r.size(); ++i) r[i] = lo(r[i]);30
}31
return r;32
}33
std::string title(std::string_view s) {34
std::string r(s);35
bool prev_alpha = false;36
for (char& c : r) {37
const bool alpha = std::isalpha(static_cast<unsigned char>(c)) != 0;38
if (alpha) c = prev_alpha ? lo(c) : up(c);39
prev_alpha = alpha;40
}41
return r;42
}43
std::string swapcase(std::string_view s) {44
std::string r(s);45
for (char& c : r) {46
const unsigned char u = static_cast<unsigned char>(c);47
if (std::islower(u)) c = up(c);48
else if (std::isupper(u)) c = lo(c);49
}50
return r;51
}53
std::string lstrip(std::string_view s, std::string_view chars) {54
std::size_t i = 0;55
while (i < s.size() && in_set(s[i], chars)) ++i;56
return std::string(s.substr(i));57
}58
std::string rstrip(std::string_view s, std::string_view chars) {59
std::size_t n = s.size();60
while (n > 0 && in_set(s[n - 1], chars)) --n;61
return std::string(s.substr(0, n));62
}63
std::string strip(std::string_view s, std::string_view chars) { return rstrip(lstrip(s, chars), chars); }65
bool startswith(std::string_view s, std::string_view prefix) {66
return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;67
}68
bool endswith(std::string_view s, std::string_view suffix) {69
return s.size() >= suffix.size() &&70
s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;71
}72
bool contains(std::string_view s, std::string_view sub) { return s.find(sub) != std::string_view::npos; }74
long find(std::string_view s, std::string_view sub) {75
const auto p = s.find(sub);76
return p == std::string_view::npos ? -1 : static_cast<long>(p);77
}79
long find(std::string_view s, std::string_view sub, long start) {80
if (start < 0) start = 0;81
if (static_cast<std::size_t>(start) > s.size()) return -1;82
const auto p = s.find(sub, static_cast<std::size_t>(start));83
return p == std::string_view::npos ? -1 : static_cast<long>(p);84
}85
long rfind(std::string_view s, std::string_view sub) {86
const auto p = s.rfind(sub);87
return p == std::string_view::npos ? -1 : static_cast<long>(p);88
}89
long count(std::string_view s, std::string_view sub) {90
if (sub.empty()) return static_cast<long>(s.size()) + 1; // Python: len+191
long n = 0;92
std::size_t pos = 0;93
while ((pos = s.find(sub, pos)) != std::string_view::npos) {94
++n;95
pos += sub.size();96
}97
return n;98
}100
std::string replace(std::string_view s, std::string_view from, std::string_view to) {101
if (from.empty()) return std::string(s);102
std::string r;103
std::size_t prev = 0, pos;104
while ((pos = s.find(from, prev)) != std::string_view::npos) {105
r.append(s.substr(prev, pos - prev));106
r.append(to);107
prev = pos + from.size();108
}109
r.append(s.substr(prev));110
return r;111
}113
std::vector<std::string> split(std::string_view s, std::string_view sep) {114
std::vector<std::string> out;115
if (sep.empty()) {116
out.emplace_back(s);117
return out;118
}119
std::size_t prev = 0, pos;120
while ((pos = s.find(sep, prev)) != std::string_view::npos) {121
out.emplace_back(s.substr(prev, pos - prev));122
prev = pos + sep.size();123
}124
out.emplace_back(s.substr(prev));125
return out;126
}127
std::vector<std::string> split(std::string_view s) {128
std::vector<std::string> out;129
const std::size_t n = s.size();130
std::size_t i = 0;131
while (i < n) {132
while (i < n && in_set(s[i], whitespace)) ++i;133
if (i >= n) break;134
const std::size_t start = i;135
while (i < n && !in_set(s[i], whitespace)) ++i;136
out.emplace_back(s.substr(start, i - start));137
}138
return out;139
}140
std::vector<std::string> splitlines(std::string_view s) {141
std::vector<std::string> out;142
const std::size_t n = s.size();143
std::size_t i = 0, start = 0;144
while (i < n) {145
if (s[i] == '\n' || s[i] == '\r') {146
out.emplace_back(s.substr(start, i - start));147
if (s[i] == '\r' && i + 1 < n && s[i + 1] == '\n') ++i;148
++i;149
start = i;150
} else {151
++i;152
}153
}154
if (start < n) out.emplace_back(s.substr(start));155
return out;156
}157
std::string capwords(std::string_view s) {158
std::vector<std::string> words = split(s);159
for (std::string& w : words) w = capitalize(w);160
return join(" ", words);161
}163
std::string ljust(std::string_view s, std::size_t width, std::string_view fill) {164
const char f = fill.empty() ? ' ' : fill[0];165
std::string r(s);166
if (r.size() < width) r.append(width - r.size(), f);167
return r;168
}169
std::string rjust(std::string_view s, std::size_t width, std::string_view fill) {170
const char f = fill.empty() ? ' ' : fill[0];171
if (s.size() >= width) return std::string(s);172
return std::string(width - s.size(), f) + std::string(s);173
}174
std::string center(std::string_view s, std::size_t width, std::string_view fill) {175
const char f = fill.empty() ? ' ' : fill[0];176
if (s.size() >= width) return std::string(s);177
const std::size_t total = width - s.size();178
const std::size_t left = total / 2;179
return std::string(left, f) + std::string(s) + std::string(total - left, f);180
}181
std::string zfill(std::string_view s, std::size_t width) {182
if (s.size() >= width) return std::string(s);183
const std::size_t pad = width - s.size();184
if (!s.empty() && (s[0] == '+' || s[0] == '-')) {185
return std::string(1, s[0]) + std::string(pad, '0') + std::string(s.substr(1));186
}187
return std::string(pad, '0') + std::string(s);188
}190
namespace {191
template <typename Pred>192
bool all_of_nonempty(std::string_view s, Pred p) {193
if (s.empty()) return false;194
for (char c : s) {195
if (!p(static_cast<unsigned char>(c))) return false;196
}197
return true;198
}199
} // namespace201
bool isdigit(std::string_view s) { return all_of_nonempty(s, [](unsigned char c) { return std::isdigit(c) != 0; }); }202
bool isalpha(std::string_view s) { return all_of_nonempty(s, [](unsigned char c) { return std::isalpha(c) != 0; }); }203
bool isalnum(std::string_view s) { return all_of_nonempty(s, [](unsigned char c) { return std::isalnum(c) != 0; }); }204
bool isspace(std::string_view s) { return all_of_nonempty(s, [](unsigned char c) { return std::isspace(c) != 0; }); }206
bool isupper(std::string_view s) {207
bool has = false;208
for (char c : s) {209
const unsigned char u = static_cast<unsigned char>(c);210
if (std::islower(u)) return false;211
if (std::isupper(u)) has = true;212
}213
return has;214
}215
bool islower(std::string_view s) {216
bool has = false;217
for (char c : s) {218
const unsigned char u = static_cast<unsigned char>(c);219
if (std::isupper(u)) return false;220
if (std::islower(u)) has = true;221
}222
return has;223
}225
} // namespace cheatah::string