Last active
October 28, 2020 05:35
-
-
Save Autoplay1999/0490f6526da0548eed8513e02fabb097 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
base64.cpp and base64.h | |
base64 encoding and decoding with C++. | |
More information at | |
https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp | |
Version: 2.rc.04 (release candidate) | |
Copyright (C) 2004-2017, 2020 René Nyffenegger | |
This source code is provided 'as-is', without any express or implied | |
warranty. In no event will the author be held liable for any damages | |
arising from the use of this software. | |
Permission is granted to anyone to use this software for any purpose, | |
including commercial applications, and to alter it and redistribute it | |
freely, subject to the following restrictions: | |
1. The origin of this source code must not be misrepresented; you must not | |
claim that you wrote the original source code. If you use this source code | |
in a product, an acknowledgment in the product documentation would be | |
appreciated but is not required. | |
2. Altered source versions must be plainly marked as such, and must not be | |
misrepresented as being the original source code. | |
3. This notice may not be removed or altered from any source distribution. | |
René Nyffenegger [email protected] | |
*/ | |
#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A | |
#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A | |
#include <string> | |
#if __cplusplus >= 201703L | |
#include <string_view> | |
#endif // __cplusplus >= 201703L | |
class _base64 { | |
public: | |
static const char** _base64_chars() { | |
// | |
// Depending on the url parameter in base64_chars, one of | |
// two sets of base64 characters needs to be chosen. | |
// They differ in their last two characters. | |
// | |
static const char* base64_chars[2] = { | |
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
"abcdefghijklmnopqrstuvwxyz" | |
"0123456789" | |
"+/", | |
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
"abcdefghijklmnopqrstuvwxyz" | |
"0123456789" | |
"-_" | |
}; | |
return base64_chars; | |
} | |
static unsigned int pos_of_char(const unsigned char chr) { | |
// | |
// Return the position of chr within base64_encode() | |
// | |
if (chr >= 'A' && chr <= 'Z') return chr - 'A'; | |
else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1; | |
else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2; | |
else if (chr == '+' || chr == '-') return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters ( | |
else if (chr == '/' || chr == '_') return 63; // Ditto for '/' and '_' | |
throw "If input is correct, this line should never be reached."; | |
} | |
static std::string insert_linebreaks(std::string str, size_t distance) { | |
// | |
// Provided by https://github.com/JomaCorpFX, adapted by me. | |
// | |
if (!str.length()) { | |
return ""; | |
} | |
size_t pos = distance; | |
while (pos < str.size()) { | |
str.insert(pos, "\n"); | |
pos += distance + 1; | |
} | |
return str; | |
} | |
/*template <typename String, unsigned int line_length> | |
static std::string encode_with_line_breaks(String s) { | |
return insert_linebreaks(base64_encode(s, false), line_length); | |
} | |
template <typename String> | |
static std::string encode_pem(String s) { | |
return encode_with_line_breaks<String, 64>(s); | |
} | |
template <typename String> | |
static std::string encode_mime(String s) { | |
return encode_with_line_breaks<String, 76>(s); | |
} | |
template <typename String> | |
static std::string encode(String s, bool url) { | |
return base64_encode(reinterpret_cast<const unsigned char*>(s.data()), s.length(), url); | |
}*/ | |
template <typename String> | |
static std::string decode(String encoded_string) { | |
// | |
// decode(…) is templated so that it can be used with String = const std::string& | |
// or std::string_view (requires at least C++17) | |
// | |
size_t length_of_string = encoded_string.length(); | |
if (!length_of_string) | |
return std::string(""); | |
size_t in_len = length_of_string; | |
size_t pos = 0; | |
// | |
// The approximate length (bytes) of the decoded string might be one ore | |
// two bytes smaller, depending on the amount of trailing equal signs | |
// in the encoded string. This approximation is needed to reserve | |
// enough space in the string to be returned. | |
// | |
size_t approx_length_of_decoded_string = length_of_string / 4 * 3; | |
std::string ret; | |
ret.reserve(approx_length_of_decoded_string); | |
while (pos < in_len) { | |
unsigned int pos_of_char_1 = pos_of_char(encoded_string[pos + 1]); | |
ret.push_back(static_cast<std::string::value_type>(((pos_of_char(encoded_string[pos + 0])) << 2) + ((pos_of_char_1 & 0x30) >> 4))); | |
if (encoded_string[pos + 2] != '=' && encoded_string[pos + 2] != '.') { // accept URL-safe base 64 strings, too, so check for '.' also. | |
unsigned int pos_of_char_2 = pos_of_char(encoded_string[pos + 2]); | |
ret.push_back(static_cast<std::string::value_type>(((pos_of_char_1 & 0x0f) << 4) + ((pos_of_char_2 & 0x3c) >> 2))); | |
if (encoded_string[pos + 3] != '=' && encoded_string[pos + 3] != '.') { | |
ret.push_back(static_cast<std::string::value_type>(((pos_of_char_2 & 0x03) << 6) + pos_of_char(encoded_string[pos + 3]))); | |
} | |
} | |
pos += 4; | |
} | |
return ret; | |
} | |
}; | |
static std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len, bool url = false) { | |
size_t len_encoded = (in_len + 2) / 3 * 4; | |
unsigned char trailing_char = url ? '.' : '='; | |
// | |
// Choose set of base64 characters. They differ | |
// for the last two positions, depending on the url | |
// parameter. | |
// A bool (as is the parameter url) is guaranteed | |
// to evaluate to either 0 or 1 in C++ therfore, | |
// the correct character set is chosen by subscripting | |
// base64_chars with url. | |
// | |
const char* base64_chars_ = _base64::_base64_chars()[url]; | |
std::string ret; | |
ret.reserve(len_encoded); | |
unsigned int pos = 0; | |
while (pos < in_len) { | |
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]); | |
if (pos + 1 < in_len) { | |
ret.push_back(base64_chars_[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]); | |
if (pos + 2 < in_len) { | |
ret.push_back(base64_chars_[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]); | |
ret.push_back(base64_chars_[bytes_to_encode[pos + 2] & 0x3f]); | |
} | |
else { | |
ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & 0x0f) << 2]); | |
ret.push_back(trailing_char); | |
} | |
} | |
else { | |
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0x03) << 4]); | |
ret.push_back(trailing_char); | |
ret.push_back(trailing_char); | |
} | |
pos += 3; | |
} | |
return ret; | |
} | |
static std::string base64_decode(std::string const& s, bool remove_linebreaks = false) { | |
if (remove_linebreaks) { | |
if (!s.length()) { | |
return ""; | |
} | |
std::string copy(s); | |
size_t pos = 0; | |
while ((pos = copy.find("\n", pos)) != std::string::npos) { | |
copy.erase(pos, 1); | |
} | |
return _base64::decode(copy); | |
} | |
return _base64::decode(s); | |
} | |
static std::string base64_encode(std::string const& s, bool url = false) { | |
return base64_encode(reinterpret_cast<const unsigned char*>(s.data()), s.length(), url); | |
} | |
static std::string base64_encode_pem(std::string const& s) { | |
return _base64::insert_linebreaks(base64_encode(s, false), 64); | |
} | |
static std::string base64_encode_mime(std::string const& s) { | |
return _base64::insert_linebreaks(base64_encode(s, false), 76); | |
} | |
#if __cplusplus >= 201703L | |
// | |
// Interface with std::string_view rather than const std::string& | |
// Requires C++17 | |
// Provided by Yannic Bonenberger (https://github.com/Yannic) | |
// | |
static std::string base64_encode(std::string_view s, bool url = false) { | |
return base64_encode(reinterpret_cast<const unsigned char*>(s.data()), s.length(), url); | |
} | |
static std::string base64_encode_pem(std::string_view s) { | |
return _base64::insert_linebreaks(base64_encode(s, false), 64); | |
} | |
static std::string base64_encode_mime(std::string_view s) { | |
return _base64::insert_linebreaks(base64_encode(s, false), 76); | |
} | |
static std::string base64_decode(std::string_view s, bool remove_linebreaks = false) { | |
if (remove_linebreaks) { | |
if (!s.length()) { | |
return ""; | |
} | |
std::string copy(s); | |
size_t pos = 0; | |
while ((pos = copy.find("\n", pos)) != std::string::npos) { | |
copy.erase(pos, 1); | |
} | |
return _base64::decode(copy); | |
} | |
return _base64::decode(s); | |
} | |
#endif // __cplusplus >= 201703L | |
#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment