Base64 improvements (#1635)

This commit is contained in:
Muhammad 2023-06-24 13:09:08 +03:00 committed by GitHub
parent eea916315e
commit 61073b4f74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 128 additions and 31 deletions

View File

@ -60,6 +60,9 @@ namespace utils
/// Determine if the string is an integer
DROGON_EXPORT bool isInteger(const std::string &str);
/// Determine if the string is base64 encoded
DROGON_EXPORT bool isBase64(const std::string &str);
/// Generate random a string
/**
* @param length The string length
@ -98,15 +101,46 @@ DROGON_EXPORT std::set<std::string> splitStringToSet(
/// Get UUID string.
DROGON_EXPORT std::string getUuid();
/// Get the encoded length of base64.
DROGON_EXPORT size_t base64EncodedLength(unsigned int in_len,
bool padded = true);
/// Encode the string to base64 format.
DROGON_EXPORT std::string base64Encode(const unsigned char *bytes_to_encode,
unsigned int in_len,
bool url_safe = false);
bool url_safe = false,
bool padded = true);
/// Encode the string to base64 format.
inline std::string base64Encode(string_view data,
bool url_safe = false,
bool padded = true)
{
return base64Encode((unsigned char *)data.data(),
data.size(),
url_safe,
padded);
}
/// Encode the string to base64 format with no padding.
DROGON_EXPORT std::string base64EncodeUnpadded(
const unsigned char *bytes_to_encode,
unsigned int in_len,
bool url_safe = false);
/// Encode the string to base64 format with no padding.
inline std::string base64EncodeUnpadded(string_view data, bool url_safe = false)
{
return base64Encode(data, url_safe, false);
}
/// Get the decoded length of base64.
DROGON_EXPORT size_t base64DecodedLength(unsigned int in_len);
/// Decode the base64 format string.
DROGON_EXPORT std::string base64Decode(const std::string &encoded_string);
DROGON_EXPORT std::string base64Decode(string_view encoded_string);
DROGON_EXPORT std::vector<char> base64DecodeToVector(
const std::string &encoded_string);
string_view encoded_string);
/// Check if the string need decoding
DROGON_EXPORT bool needUrlDecoding(const char *begin, const char *end);

View File

@ -149,6 +149,14 @@ bool isInteger(const std::string &str)
return true;
}
bool isBase64(const std::string &str)
{
for (auto c : str)
if (!isBase64(c))
return false;
return true;
}
std::string genRandomString(int length)
{
static const char char_space[] =
@ -387,11 +395,18 @@ std::string getUuid()
#endif
}
size_t base64EncodedLength(unsigned int in_len, bool padded)
{
return padded ? ((in_len + 3 - 1) / 3) * 4 : (in_len * 8 + 6 - 1) / 6;
}
std::string base64Encode(const unsigned char *bytes_to_encode,
unsigned int in_len,
bool url_safe)
bool url_safe,
bool padded)
{
std::string ret;
ret.reserve(base64EncodedLength(in_len, padded));
int i = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
@ -428,27 +443,45 @@ std::string base64Encode(const unsigned char *bytes_to_encode,
((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (int j = 0; (j < i + 1); ++j)
for (int j = 0; (j <= i); ++j)
ret += charSet[char_array_4[j]];
while ((i++ < 3))
ret += '=';
if (padded)
while ((++i < 4))
ret += '=';
}
return ret;
}
std::vector<char> base64DecodeToVector(const std::string &encoded_string)
std::string base64EncodeUnpadded(const unsigned char *bytes_to_encode,
unsigned int in_len,
bool url_safe)
{
return base64Encode(bytes_to_encode, in_len, url_safe, false);
}
size_t base64DecodedLength(unsigned int in_len)
{
return (in_len * 3) / 4;
}
std::vector<char> base64DecodeToVector(string_view encoded_string)
{
auto in_len = encoded_string.size();
int i = 0;
int in_{0};
char char_array_4[4], char_array_3[3];
std::vector<char> ret;
ret.reserve(in_len);
ret.reserve(base64DecodedLength(in_len));
while (in_len-- && (encoded_string[in_] != '=') &&
isBase64(encoded_string[in_]))
while (in_len-- && (encoded_string[in_] != '='))
{
if (!isBase64(encoded_string[in_]))
{
++in_;
continue;
}
char_array_4[i++] = encoded_string[in_];
++in_;
if (i == 4)
@ -486,24 +519,31 @@ std::vector<char> base64DecodeToVector(const std::string &encoded_string)
((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 (int j = 0; (j < i - 1); ++j)
--i;
for (int j = 0; (j < i); ++j)
ret.push_back(char_array_3[j]);
}
return ret;
}
std::string base64Decode(const std::string &encoded_string)
std::string base64Decode(string_view encoded_string)
{
auto in_len = encoded_string.size();
int i = 0;
int in_{0};
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
ret.reserve(base64DecodedLength(in_len));
while (in_len-- && (encoded_string[in_] != '=') &&
isBase64(encoded_string[in_]))
while (in_len-- && (encoded_string[in_] != '='))
{
if (!isBase64(encoded_string[in_]))
{
++in_;
continue;
}
char_array_4[i++] = encoded_string[in_];
++in_;
if (i == 4)
@ -540,7 +580,8 @@ std::string base64Decode(const std::string &encoded_string)
((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 (int j = 0; (j < i - 1); ++j)
--i;
for (int j = 0; (j < i); ++j)
ret += char_array_3[j];
}

View File

@ -5,12 +5,34 @@
DROGON_TEST(Base64)
{
std::string in{"drogon framework"};
auto encoded = drogon::utils::base64Encode((const unsigned char *)in.data(),
(unsigned int)in.length());
auto encoded = drogon::utils::base64Encode(in);
auto decoded = drogon::utils::base64Decode(encoded);
CHECK(encoded == "ZHJvZ29uIGZyYW1ld29yaw==");
CHECK(decoded == in);
SUBSECTION(InvalidChars)
{
auto decoded =
drogon::utils::base64Decode("ZHJvZ2*9uIGZy**YW1ld2***9yaw*=*=");
CHECK(decoded == in);
}
SUBSECTION(InvalidCharsNoPadding)
{
auto decoded =
drogon::utils::base64Decode("ZHJvZ2*9uIGZy**YW1ld2***9yaw**");
CHECK(decoded == in);
}
SUBSECTION(Unpadded)
{
std::string in{"drogon framework"};
auto encoded = drogon::utils::base64EncodeUnpadded(in);
auto decoded = drogon::utils::base64Decode(encoded);
CHECK(encoded == "ZHJvZ29uIGZyYW1ld29yaw");
CHECK(decoded == in);
}
SUBSECTION(LongString)
{
std::string in;
@ -19,12 +41,9 @@ DROGON_TEST(Base64)
{
in.append(1, char(i));
}
auto out = drogon::utils::base64Encode((const unsigned char *)in.data(),
(unsigned int)in.length());
auto out = drogon::utils::base64Encode(in);
auto out2 = drogon::utils::base64Decode(out);
auto encoded =
drogon::utils::base64Encode((const unsigned char *)in.data(),
(unsigned int)in.length());
auto encoded = drogon::utils::base64Encode(in);
auto decoded = drogon::utils::base64Decode(encoded);
CHECK(decoded == in);
}
@ -32,15 +51,21 @@ DROGON_TEST(Base64)
SUBSECTION(URLSafe)
{
std::string in{"drogon framework"};
auto encoded =
drogon::utils::base64Encode((const unsigned char *)in.data(),
(unsigned int)in.length(),
true);
auto encoded = drogon::utils::base64Encode(in, true);
auto decoded = drogon::utils::base64Decode(encoded);
CHECK(encoded == "ZHJvZ29uIGZyYW1ld29yaw==");
CHECK(decoded == in);
}
SUBSECTION(UnpaddedURLSafe)
{
std::string in{"drogon framework"};
auto encoded = drogon::utils::base64EncodeUnpadded(in, true);
auto decoded = drogon::utils::base64Decode(encoded);
CHECK(encoded == "ZHJvZ29uIGZyYW1ld29yaw");
CHECK(decoded == in);
}
SUBSECTION(LongURLSafe)
{
std::string in;
@ -49,10 +74,7 @@ DROGON_TEST(Base64)
{
in.append(1, char(i));
}
auto encoded =
drogon::utils::base64Encode((const unsigned char *)in.data(),
(unsigned int)in.length(),
true);
auto encoded = drogon::utils::base64Encode(in, true);
auto decoded = drogon::utils::base64Decode(encoded);
CHECK(decoded == in);
}