2021-02-09 18:35:08 +01:00
|
|
|
#include "strutils.h"
|
|
|
|
|
|
|
|
|
|
// system includes
|
|
|
|
|
#include <cctype>
|
2022-06-30 04:46:32 +02:00
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
|
|
// 3rdparty lib includes
|
|
|
|
|
#include <fmt/core.h>
|
2021-02-09 18:35:08 +01:00
|
|
|
|
|
|
|
|
namespace cpputils {
|
2022-06-30 04:46:32 +02:00
|
|
|
namespace {
|
|
|
|
|
tl::expected<char *, std::string> sodium_bin2hex(char * const hex, const size_t hex_maxlen,
|
|
|
|
|
const unsigned char * const bin, const size_t bin_len)
|
|
|
|
|
__attribute__ ((nonnull(1)));
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
std::string toHexString(std::basic_string_view<unsigned char> buf)
|
|
|
|
|
{
|
|
|
|
|
std::string hex(buf.size() * 2 + 1, {});
|
|
|
|
|
assert(hex.size() == buf.size() * 2 + 1);
|
|
|
|
|
|
|
|
|
|
if (const auto result = sodium_bin2hex(hex.data(), hex.size(), buf.data(), buf.size()); !result)
|
|
|
|
|
{
|
|
|
|
|
// auto msg = fmt::format("sodium_bin2hex() failed with {}", result.error());
|
|
|
|
|
// ESP_LOGW(TAG, "%.*s", msg.size(), msg.data());
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hex.resize(hex.size() - 1);
|
|
|
|
|
assert(hex.size() == buf.size() * 2);
|
|
|
|
|
|
|
|
|
|
return hex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tl::expected<std::basic_string<unsigned char>, std::string> fromHexString(std::string_view hex)
|
|
|
|
|
{
|
|
|
|
|
if (hex.size() % 2 != 0)
|
|
|
|
|
return tl::make_unexpected("hex length not even");
|
|
|
|
|
|
|
|
|
|
std::basic_string<unsigned char> result;
|
|
|
|
|
result.reserve(hex.size() / 2);
|
|
|
|
|
|
|
|
|
|
for (auto iter = std::cbegin(hex); iter != std::cend(hex); )
|
|
|
|
|
{
|
|
|
|
|
union {
|
|
|
|
|
unsigned char c;
|
|
|
|
|
struct {
|
|
|
|
|
unsigned char nibble1:4;
|
|
|
|
|
unsigned char nibble0:4;
|
|
|
|
|
} nibbles;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
switch (const auto c = *iter)
|
|
|
|
|
{
|
|
|
|
|
case '0'...'9': nibbles.nibble0 = c - '0'; break;
|
|
|
|
|
case 'A'...'F': nibbles.nibble0 = c - 'A' + 10; break;
|
|
|
|
|
case 'a'...'f': nibbles.nibble0 = c - 'a' + 10; break;
|
|
|
|
|
default:
|
|
|
|
|
return tl::make_unexpected(fmt::format("invalid character {} at pos {}", c, std::distance(std::begin(hex), iter)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iter++;
|
|
|
|
|
|
|
|
|
|
switch (const auto c = *iter)
|
|
|
|
|
{
|
|
|
|
|
case '0'...'9': nibbles.nibble1 = c - '0'; break;
|
|
|
|
|
case 'A'...'F': nibbles.nibble1 = c - 'A' + 10; break;
|
|
|
|
|
case 'a'...'f': nibbles.nibble1 = c - 'a' + 10; break;
|
|
|
|
|
default:
|
|
|
|
|
return tl::make_unexpected(fmt::format("invalid character {} at pos {}", c, std::distance(std::begin(hex), iter)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iter++;
|
|
|
|
|
|
|
|
|
|
result.push_back(c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-30 06:56:08 +02:00
|
|
|
std::string toBase64String(std::basic_string_view<unsigned char> buf)
|
|
|
|
|
{
|
|
|
|
|
constexpr std::string_view base64_chars{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"};
|
|
|
|
|
|
|
|
|
|
std::string out;
|
|
|
|
|
|
|
|
|
|
int val = 0;
|
|
|
|
|
int valb = -6;
|
|
|
|
|
for (unsigned char c : buf)
|
|
|
|
|
{
|
|
|
|
|
val = (val << 8) + c;
|
|
|
|
|
valb += 8;
|
|
|
|
|
|
|
|
|
|
while (valb >= 0)
|
|
|
|
|
{
|
|
|
|
|
out.push_back(base64_chars[(val>>valb)&0x3F]);
|
|
|
|
|
valb -= 6;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (valb>-6)
|
|
|
|
|
out.push_back(base64_chars[((val << 8) >> (valb + 8)) & 0x3F]);
|
|
|
|
|
|
|
|
|
|
while (out.size() % 4)
|
|
|
|
|
out.push_back('=');
|
|
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-23 15:24:44 +02:00
|
|
|
namespace {
|
|
|
|
|
constexpr std::string_view base64_chars =
|
|
|
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
|
|
|
"0123456789+/";
|
|
|
|
|
bool is_base64(char c) {
|
|
|
|
|
return (isalnum(c) || (c == '+') || (c == '/'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string base64_decode(std::string const& encoded_string)
|
|
|
|
|
{
|
|
|
|
|
int in_len = encoded_string.size();
|
|
|
|
|
int i = 0;
|
|
|
|
|
int j = 0;
|
|
|
|
|
int in_ = 0;
|
|
|
|
|
char char_array_4[4], char_array_3[3];
|
|
|
|
|
std::string ret;
|
|
|
|
|
|
|
|
|
|
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
|
|
|
|
char_array_4[i++] = encoded_string[in_]; in_++;
|
|
|
|
|
if (i ==4) {
|
|
|
|
|
for (i = 0; i <4; i++)
|
|
|
|
|
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
|
|
|
|
|
|
|
|
|
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
|
|
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
|
|
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
|
|
|
|
|
|
|
|
for (i = 0; (i < 3); i++)
|
|
|
|
|
ret.push_back(char_array_3[i]);
|
|
|
|
|
i = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
|
for (j = i; j <4; j++)
|
|
|
|
|
char_array_4[j] = 0;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j <4; j++)
|
|
|
|
|
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
|
|
|
|
|
|
|
|
|
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
|
|
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
|
|
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
|
|
|
|
|
|
|
|
for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 18:35:08 +01:00
|
|
|
bool stringEqualsIgnoreCase(std::string_view a, std::string_view b)
|
|
|
|
|
{
|
|
|
|
|
if (a.size() != b.size())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return std::equal(std::begin(a), std::end(a), std::begin(b),
|
|
|
|
|
[](char a, char b) {
|
|
|
|
|
return std::tolower(a) == std::tolower(b);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-05 19:15:58 +02:00
|
|
|
//void stringReplaceAll(char search, char replace, std::string &subject)
|
|
|
|
|
//{
|
|
|
|
|
// size_t start_pos{};
|
|
|
|
|
// while ((start_pos = subject.find(search, start_pos)) != std::string::npos)
|
|
|
|
|
// {
|
|
|
|
|
// subject.replace(start_pos, 1, replace);
|
|
|
|
|
// start_pos++; // Handles case where 'replace' is a substring of 'search'
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
void stringReplaceAll(char search, std::string_view replace, std::string &subject)
|
|
|
|
|
{
|
|
|
|
|
size_t start_pos{};
|
|
|
|
|
while ((start_pos = subject.find(search, start_pos)) != std::string::npos)
|
|
|
|
|
{
|
|
|
|
|
subject.replace(start_pos, 1, replace);
|
|
|
|
|
start_pos += replace.size(); // Handles case where 'replace' is a substring of 'search'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//void stringReplaceAll(std::string_view search, char replace, std::string &subject)
|
|
|
|
|
//{
|
|
|
|
|
// size_t start_pos{};
|
|
|
|
|
// while ((start_pos = subject.find(search, start_pos)) != std::string::npos)
|
|
|
|
|
// {
|
|
|
|
|
// subject.replace(start_pos, search.size(), replace);
|
|
|
|
|
// start_pos++; // Handles case where 'replace' is a substring of 'search'
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
void stringReplaceAll(std::string_view search, std::string_view replace, std::string &subject)
|
|
|
|
|
{
|
|
|
|
|
size_t start_pos{};
|
|
|
|
|
while ((start_pos = subject.find(search, start_pos)) != std::string::npos)
|
|
|
|
|
{
|
|
|
|
|
subject.replace(start_pos, search.size(), replace);
|
|
|
|
|
start_pos += replace.size(); // Handles case where 'replace' is a substring of 'search'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//std::string stringReplaceAll(char search, char replace, std::string_view subject)
|
|
|
|
|
//{
|
|
|
|
|
// std::string str{subject};
|
|
|
|
|
// size_t start_pos{};
|
|
|
|
|
// while ((start_pos = str.find(search, start_pos)) != std::string::npos)
|
|
|
|
|
// {
|
|
|
|
|
// str.replace(start_pos, 1, replace);
|
|
|
|
|
// start_pos++; // Handles case where 'replace' is a substring of 'search'
|
|
|
|
|
// }
|
|
|
|
|
// return str;
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
std::string stringReplaceAll(char search, std::string_view replace, std::string_view subject)
|
|
|
|
|
{
|
|
|
|
|
std::string str{subject};
|
|
|
|
|
size_t start_pos{};
|
|
|
|
|
while ((start_pos = str.find(search, start_pos)) != std::string::npos)
|
|
|
|
|
{
|
|
|
|
|
str.replace(start_pos, 1, replace);
|
|
|
|
|
start_pos += replace.size(); // Handles case where 'replace' is a substring of 'search'
|
|
|
|
|
}
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//std::string stringReplaceAll(std::string_view search, char replace, std::string_view subject)
|
|
|
|
|
//{
|
|
|
|
|
// std::string str{subject};
|
|
|
|
|
// size_t start_pos{};
|
|
|
|
|
// while ((start_pos = str.find(search, start_pos)) != std::string::npos)
|
|
|
|
|
// {
|
|
|
|
|
// str.replace(start_pos, search.size(), replace);
|
|
|
|
|
// start_pos++; // Handles case where 'replace' is a substring of 'search'
|
|
|
|
|
// }
|
|
|
|
|
// return str;
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
std::string stringReplaceAll(std::string_view search, std::string_view replace, std::string_view subject)
|
|
|
|
|
{
|
|
|
|
|
std::string str{subject};
|
|
|
|
|
size_t start_pos{};
|
|
|
|
|
while ((start_pos = str.find(search, start_pos)) != std::string::npos)
|
|
|
|
|
{
|
|
|
|
|
str.replace(start_pos, search.size(), replace);
|
|
|
|
|
start_pos += replace.size(); // Handles case where 'replace' is a substring of 'search'
|
|
|
|
|
}
|
|
|
|
|
return str;
|
|
|
|
|
}
|
2021-11-19 01:39:50 +01:00
|
|
|
|
|
|
|
|
std::optional<std::string_view> getStringBetween(std::string_view search, std::string_view prefix, std::string_view suffix)
|
|
|
|
|
{
|
|
|
|
|
auto beginIndex = search.find(prefix);
|
|
|
|
|
if (beginIndex == std::string_view::npos)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
auto restView = search.substr(beginIndex + prefix.size());
|
|
|
|
|
|
|
|
|
|
auto endIndex = restView.find(suffix);
|
|
|
|
|
if (endIndex == std::string_view::npos)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
return restView.substr(0, endIndex);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-30 04:46:32 +02:00
|
|
|
namespace {
|
|
|
|
|
/* Derived from original code by CodesInChaos */
|
|
|
|
|
tl::expected<char *, std::string>
|
|
|
|
|
sodium_bin2hex(char *const hex, const size_t hex_maxlen,
|
|
|
|
|
const unsigned char *const bin, const size_t bin_len)
|
|
|
|
|
{
|
|
|
|
|
size_t i = (size_t) 0U;
|
|
|
|
|
unsigned int x;
|
|
|
|
|
int b;
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
if (bin_len >= SIZE_MAX / 2 || hex_maxlen <= bin_len * 2U) {
|
|
|
|
|
return tl::make_unexpected("misuse because bin_len >= SIZE_MAX / 2 || hex_maxlen <= bin_len * 2U");
|
|
|
|
|
}
|
|
|
|
|
while (i < bin_len) {
|
|
|
|
|
c = bin[i] & 0xf;
|
|
|
|
|
b = bin[i] >> 4;
|
|
|
|
|
x = (unsigned char) (87U + c + (((c - 10U) >> 8) & ~38U)) << 8 |
|
|
|
|
|
(unsigned char) (87U + b + (((b - 10U) >> 8) & ~38U));
|
|
|
|
|
hex[i * 2U] = (char) x;
|
|
|
|
|
x >>= 8;
|
|
|
|
|
hex[i * 2U + 1U] = (char) x;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
hex[i * 2U] = 0U;
|
|
|
|
|
|
|
|
|
|
return hex;
|
|
|
|
|
}
|
|
|
|
|
} // namespace
|
2021-02-09 18:35:08 +01:00
|
|
|
} // namespace cpputils
|