From 4cdb1fbae28c29fa5071065bf4959f99ef69bf1f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 12 Mar 2025 16:26:46 +0300 Subject: [PATCH] add util::base64_urlsafe_encode/decode --- src/util/stringutil.cpp | 92 ++++++++++++++++++++++++++++++++-------- src/util/stringutil.hpp | 8 ++++ test/util/stringutil.cpp | 16 +++++++ 3 files changed, 98 insertions(+), 18 deletions(-) diff --git a/src/util/stringutil.cpp b/src/util/stringutil.cpp index 53f77239..79f8a1c5 100644 --- a/src/util/stringutil.cpp +++ b/src/util/stringutil.cpp @@ -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 +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 +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_(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(data, size); +} + +std::string util::base64_urlsafe_encode(const ubyte* data, size_t size) { + return base64_encode_impl(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 util::base64_decode(const char* str, size_t size) { +template +static util::Buffer base64_decode_impl(const char* str, size_t size) { util::Buffer bytes((size / 4) * 3); ubyte* dst = bytes.data(); for (size_t i = 0; i < (size / 4) * 4;) { @@ -387,18 +414,35 @@ util::Buffer util::base64_decode(const char* str, size_t size) { return bytes; } +util::Buffer util::base64_urlsafe_decode(const char* str, size_t size) { + return base64_decode_impl(str, size); +} + +util::Buffer util::base64_decode(const char* str, size_t size) { + return base64_decode_impl(str, size); +} + +util::Buffer util::base64_urlsafe_decode(std::string_view str) { + return base64_urlsafe_decode(str.data(), str.size()); +} + util::Buffer 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 +static int replace_all( + std::basic_string& str, + const std::basic_string& from, + const std::basic_string& 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::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); diff --git a/src/util/stringutil.hpp b/src/util/stringutil.hpp index 522ad2ca..d85f93cb 100644 --- a/src/util/stringutil.hpp +++ b/src/util/stringutil.hpp @@ -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 base64_decode(const char* str, size_t size); + util::Buffer base64_urlsafe_decode(const char* str, size_t size); util::Buffer base64_decode(std::string_view str); + util::Buffer 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); diff --git a/test/util/stringutil.cpp b/test/util/stringutil.cpp index 2f27a243..fd459663 100644 --- a/test/util/stringutil.cpp +++ b/test/util/stringutil.cpp @@ -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(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]); + } + } +}