Skip to content

Commit

Permalink
Base64 improvements (#1635)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mis1eader-dev committed Jun 24, 2023
1 parent eea9163 commit 61073b4
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 31 deletions.
40 changes: 37 additions & 3 deletions lib/inc/drogon/utils/Utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
67 changes: 54 additions & 13 deletions lib/src/Utilities.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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[] =
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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];
}

Expand Down
52 changes: 37 additions & 15 deletions lib/tests/unittests/Base64Test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -19,28 +41,31 @@ 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);
}

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;
Expand All @@ -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);
}
Expand Down

0 comments on commit 61073b4

Please sign in to comment.