Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions include/strutil/strutil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,140 @@ inline std::optional<double> to_double(std::string_view str) {
}
}

// ── base64_encode / base64_decode ──────────────────────
inline std::string base64_encode(std::string_view data) {
static const char kEncode[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string result;
result.reserve(((data.size() + 2) / 3) * 4);
size_t i = 0;
uint32_t buf = 0;
int n = 0;
while (i < data.size()) {
buf = (buf << 8) | static_cast<uint8_t>(data[i++]);
n++;
if (n == 3) {
result.push_back(kEncode[(buf >> 18) & 0x3F]);
result.push_back(kEncode[(buf >> 12) & 0x3F]);
result.push_back(kEncode[(buf >> 6) & 0x3F]);
result.push_back(kEncode[buf & 0x3F]);
buf = 0;
n = 0;
}
}
if (n > 0) {
buf <<= (3 - n) * 8;
result.push_back(kEncode[(buf >> 18) & 0x3F]);
result.push_back(kEncode[(buf >> 12) & 0x3F]);
result.push_back(n == 1 ? '=' : kEncode[(buf >> 6) & 0x3F]);
result.push_back('=');
}
return result;
}

inline std::string base64_decode(std::string_view encoded) {
static const int kDecode[256] = []() {
int t[256] = {};
for (int i = 0; i < 256; i++) t[i] = -1;
for (int i = 0; i < 64; i++) t["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
return *t;
}();
std::string result;
result.reserve((encoded.size() * 3) / 4);
int buf = 0, n = 0;
for (char c : encoded) {
if (c == '=') break;
int v = kDecode[static_cast<uint8_t>(c)];
if (v < 0) continue;
buf = (buf << 6) | v;
n++;
if (n == 4) {
result.push_back(static_cast<char>((buf >> 16) & 0xFF));
result.push_back(static_cast<char>((buf >> 8) & 0xFF));
result.push_back(static_cast<char>(buf & 0xFF));
buf = 0;
n = 0;
}
}
if (n >= 2) result.push_back(static_cast<char>((buf >> 10) & 0xFF));
if (n >= 3) result.push_back(static_cast<char>((buf >> 2) & 0xFF));
return result;
}

// ── url_encode / url_decode ────────────────────────────
inline std::string url_encode(std::string_view str) {
std::string result;
result.reserve(str.size() * 3);
for (unsigned char c : str) {
if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
result.push_back(static_cast<char>(c));
} else if (c == ' ') {
result.push_back('+');
} else {
result.push_back('%');
result.push_back("0123456789ABCDEF"[c >> 4]);
result.push_back("0123456789ABCDEF"[c & 0xF]);
}
}
return result;
}

inline std::string url_decode(std::string_view str) {
std::string result;
result.reserve(str.size());
for (size_t i = 0; i < str.size(); i++) {
if (str[i] == '+') {
result.push_back(' ');
} else if (str[i] == '%' && i + 2 < str.size()) {
int hi = std::isdigit(static_cast<unsigned char>(str[i + 1]))
? str[i + 1] - '0'
: std::toupper(static_cast<unsigned char>(str[i + 1])) - 'A' + 10;
int lo = std::isdigit(static_cast<unsigned char>(str[i + 2]))
? str[i + 2] - '0'
: std::toupper(static_cast<unsigned char>(str[i + 2])) - 'A' + 10;
result.push_back(static_cast<char>((hi << 4) | lo));
i += 2;
} else {
result.push_back(str[i]);
}
}
return result;
}

// ── wildcard_match — simple glob matching with * and ? ──
inline bool wildcard_match(std::string_view pattern, std::string_view text) {
size_t pi = 0, ti = 0, star = std::string_view::npos, match = 0;
while (ti < text.size()) {
if (pi < pattern.size() && (pattern[pi] == '?' ||
(pattern[pi] != '*' && pattern[pi] == text[ti]))) {
pi++;
ti++;
} else if (pi < pattern.size() && pattern[pi] == '*') {
star = pi++;
match = ti;
} else if (star != std::string_view::npos) {
pi = star + 1;
match++;
ti = match;
} else {
return false;
}
}
while (pi < pattern.size() && pattern[pi] == '*') pi++;
return pi == pattern.size();
}

// ── indent — indent each line ───────────────────────────
inline std::string indent(std::string_view str, size_t spaces = 4) {
std::string prefix(spaces, ' ');
std::string result;
result.reserve(str.size() + str.size() / 10 * spaces);
result.append(prefix);
for (char c : str) {
result.push_back(c);
if (c == '\n') result.append(prefix);
}
return result;
}

} // namespace strutil
Loading