add util::base64_urlsafe_encode/decode

This commit is contained in:
MihailRis 2025-03-12 16:26:46 +03:00
parent 65ca46b2da
commit 4cdb1fbae2
3 changed files with 98 additions and 18 deletions

View File

@ -306,6 +306,12 @@ const char B64ABC[] =
"0123456789"
"+/";
const char URLSAFE_B64ABC[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"-_";
inline ubyte base64_decode_char(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
@ -315,16 +321,27 @@ inline ubyte base64_decode_char(char c) {
return 0;
}
inline void base64_encode_(const ubyte* segment, char* output) {
output[0] = B64ABC[(segment[0] & 0b11111100) >> 2];
output[1] =
B64ABC[((segment[0] & 0b11) << 4) | ((segment[1] & 0b11110000) >> 4)];
output[2] =
B64ABC[((segment[1] & 0b1111) << 2) | ((segment[2] & 0b11000000) >> 6)];
output[3] = B64ABC[segment[2] & 0b111111];
inline ubyte base64_urlsafe_decode_char(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '-') return 62;
if (c == '_') return 63;
return 0;
}
std::string util::base64_encode(const ubyte* data, size_t size) {
template <const char* ABC>
static void base64_encode_(const ubyte* segment, char* output) {
output[0] = ABC[(segment[0] & 0b11111100) >> 2];
output[1] =
ABC[((segment[0] & 0b11) << 4) | ((segment[1] & 0b11110000) >> 4)];
output[2] =
ABC[((segment[1] & 0b1111) << 2) | ((segment[2] & 0b11000000) >> 6)];
output[3] = ABC[segment[2] & 0b111111];
}
template <const char* ABC>
static std::string base64_encode_impl(const ubyte* data, size_t size) {
std::stringstream ss;
size_t fullsegments = (size / 3) * 3;
@ -332,7 +349,7 @@ std::string util::base64_encode(const ubyte* data, size_t size) {
size_t i = 0;
for (; i < fullsegments; i += 3) {
char output[] = "====";
base64_encode_(data + i, output);
base64_encode_<ABC>(data + i, output);
ss << output;
}
@ -343,18 +360,27 @@ std::string util::base64_encode(const ubyte* data, size_t size) {
size_t trailing = size - fullsegments;
if (trailing) {
char output[] = "====";
output[0] = B64ABC[(ending[0] & 0b11111100) >> 2];
output[0] = ABC[(ending[0] & 0b11111100) >> 2];
output[1] =
B64ABC[((ending[0] & 0b11) << 4) | ((ending[1] & 0b11110000) >> 4)];
ABC[((ending[0] & 0b11) << 4) | ((ending[1] & 0b11110000) >> 4)];
if (trailing > 1)
output[2] = B64ABC
[((ending[1] & 0b1111) << 2) | ((ending[2] & 0b11000000) >> 6)];
if (trailing > 2) output[3] = B64ABC[ending[2] & 0b111111];
output[2] =
ABC[((ending[1] & 0b1111) << 2) |
((ending[2] & 0b11000000) >> 6)];
if (trailing > 2) output[3] = ABC[ending[2] & 0b111111];
ss << output;
}
return ss.str();
}
std::string util::base64_encode(const ubyte* data, size_t size) {
return base64_encode_impl<B64ABC>(data, size);
}
std::string util::base64_urlsafe_encode(const ubyte* data, size_t size) {
return base64_encode_impl<URLSAFE_B64ABC>(data, size);
}
std::string util::tohex(uint64_t value) {
std::stringstream ss;
ss << std::hex << value;
@ -366,7 +392,8 @@ std::string util::mangleid(uint64_t value) {
return tohex(value);
}
util::Buffer<ubyte> util::base64_decode(const char* str, size_t size) {
template <ubyte(base64_decode_char)(char)>
static util::Buffer<ubyte> base64_decode_impl(const char* str, size_t size) {
util::Buffer<ubyte> bytes((size / 4) * 3);
ubyte* dst = bytes.data();
for (size_t i = 0; i < (size / 4) * 4;) {
@ -387,18 +414,35 @@ util::Buffer<ubyte> util::base64_decode(const char* str, size_t size) {
return bytes;
}
util::Buffer<ubyte> util::base64_urlsafe_decode(const char* str, size_t size) {
return base64_decode_impl<base64_urlsafe_decode_char>(str, size);
}
util::Buffer<ubyte> util::base64_decode(const char* str, size_t size) {
return base64_decode_impl<base64_decode_char>(str, size);
}
util::Buffer<ubyte> util::base64_urlsafe_decode(std::string_view str) {
return base64_urlsafe_decode(str.data(), str.size());
}
util::Buffer<ubyte> util::base64_decode(std::string_view str) {
return base64_decode(str.data(), str.size());
}
int util::replaceAll(
std::string& str, const std::string& from, const std::string& to
template <typename CharT>
static int replace_all(
std::basic_string<CharT>& str,
const std::basic_string<CharT>& from,
const std::basic_string<CharT>& to
) {
int count = 0;
size_t offset = 0;
while (true) {
size_t start_pos = str.find(from, offset);
if (start_pos == std::string::npos) break;
if (start_pos == std::basic_string<CharT>::npos) {
break;
}
str.replace(start_pos, from.length(), to);
offset = start_pos + to.length();
count++;
@ -406,6 +450,18 @@ int util::replaceAll(
return count;
}
int util::replaceAll(
std::string& str, const std::string& from, const std::string& to
) {
return replace_all(str, from, to);
}
int util::replaceAll(
std::wstring& str, const std::wstring& from, const std::wstring& to
) {
return replace_all(str, from, to);
}
// replace it with std::from_chars in the far far future
double util::parse_double(const std::string& str) {
std::istringstream ss(str);

View File

@ -74,8 +74,12 @@ namespace util {
std::wstring to_wstring(double x, int precision);
std::string base64_encode(const ubyte* data, size_t size);
std::string base64_urlsafe_encode(const ubyte* data, size_t size);
util::Buffer<ubyte> base64_decode(const char* str, size_t size);
util::Buffer<ubyte> base64_urlsafe_decode(const char* str, size_t size);
util::Buffer<ubyte> base64_decode(std::string_view str);
util::Buffer<ubyte> base64_urlsafe_decode(std::string_view str);
std::string tohex(uint64_t value);
@ -85,6 +89,10 @@ namespace util {
std::string& str, const std::string& from, const std::string& to
);
int replaceAll(
std::wstring& str, const std::wstring& from, const std::wstring& to
);
double parse_double(const std::string& str);
double parse_double(const std::string& str, size_t offset, size_t len);

View File

@ -31,3 +31,19 @@ TEST(stringutil, base64) {
}
}
}
TEST(stringutil, base64_urlsafe) {
srand(2019);
for (size_t size = 0; size < 30; size++) {
auto bytes = std::make_unique<ubyte[]>(size);
for (int i = 0; i < size; i++) {
bytes[i] = rand();
}
auto base64 = util::base64_urlsafe_encode(bytes.get(), size);
auto decoded = util::base64_urlsafe_decode(base64);
ASSERT_EQ(size, decoded.size());
for (size_t i = 0; i < size; i++) {
ASSERT_EQ(bytes[i], decoded[i]);
}
}
}