Base64 encoding involves turning binary data into ASCII text for the purpose of saving it to text files like XML, or transmitting it over protocols like HTTP, or embedding it into web page files, and many other purposes. That’s the basic idea behind it. For every 3 bytes of input, you get 4 bytes of output, so it’s not crazy inflated.
I was looking for a library that has built in, easy to use and clean base64
encoding and decoding functions but didn’t really find anything to my liking. So I looked for a reference implementation and found one at wikibooks.org. Their C++ implementation (released to public domain so I could freely use and modify it) was my starting point. I beautified the code and brought it closer to modern C++ . So now, you have a header only, clean base64 encode and decode functions you can use in your projects: base64.hpp.
I wrote a program that encodes and decodes an input string, checks the original against the decoded one, and also checks the encoded base64 text against reference base64 string taken from wiki. The implementation checks out and produces correct encoded string
s and decoded data . Below is the test program.
base64.cpp:
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include "base64.hpp"
#include "ascii_escape_code.hpp"
using namespace std;
using namespace ascii_escape_code;
int main(int argc, char** argv)
{
string input
{
"Man is distinguished, not only by his reason,
but by this singular passion from other animals, "
"which is a lust of the mind, that by a perseverance of delight
in the continued and indefatigable "
"generation of knowledge, exceeds the short vehemence of any carnal pleasure."
};
string reference
{
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="
};
try
{
vector<base64::byte> data(begin(input), end(input));
cout << bold << bright_red << "Input: " << reset << italic << input << reset << endl << endl;
cout << bold << bright_red << "Reference: " << reset << reference << endl << endl;
auto encoded = base64::encode(data);
cout << bold << bright_red << "Encoded: " << reset << encoded << endl << endl;
if(encoded != reference) throw runtime_error("Oh snap! Encoded data does not match reference!");
else cout << bright_red << "Encoded data matches reference :o)" << reset << endl << endl;
auto decoded = base64::decode(encoded);
string s2(begin(decoded), end(decoded));
cout << bold << bright_red << "Decoded: " << reset << italic << s2 << reset << endl << endl;
if(data != decoded) throw runtime_error("Oh snap! Input data does not match decoded!");
else cout << bright_red << "Decoded data matches original :o)" << reset << endl << endl;
}
catch(exception& e)
{
cerr << slow_blink << bright_red << e.what() << reset << endl << endl;
}
return 1;
}
Program output:
Input:
Man is distinguished, not only by his reason, but by this singular passion from other animals,
which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable
generation of knowledge, exceeds the short vehemence of any carnal pleasure.
Reference:
TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ 1dCBieSB0aGlzIHNpbmd1bGFyIHB
hc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3 aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZX
JzZXZlcmF uY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWd hYmxlIGdlbmVyYXRpb24g
b2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydC B2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=
Encoded:
TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ 1dCBieSB0aGlzIHNpbmd1bGFyIHB
hc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3 aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZX
JzZXZlcmF uY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWd hYmxlIGdlbmVyYXRpb24g
b2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydC B2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=
Encoded data matches reference :o)
Decoded:
Man is distinguished, not only by his reason, but by this singular passion from other animals,
which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable
generation of knowledge, exceeds the short vehemence of any carnal pleasure.
Decoded data matches original :o)
And here is the encoder and decoder function implementation.
base64.hpp:
#pragma once
#include <string>
#include <vector>
#include <stdexcept>
#include <cstdint>
namespace base64
{
inline static const char kEncodeLookup[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
inline static const char kPadCharacter = '=';
using byte = std::uint8_t;
inline std::string encode(const std::vector<byte>& input)
{
std::string encoded;
encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4);
std::uint32_t temp{};
auto it = input.begin();
for(std::size_t i = 0; i < input.size() / 3; ++i)
{
temp = (*it++) << 16;
temp += (*it++) << 8;
temp += (*it++);
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
encoded.append(1, kEncodeLookup[(temp & 0x0000003F) ]);
}
switch(input.size() % 3)
{
case 1:
temp = (*it++) << 16;
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(2, kPadCharacter);
break;
case 2:
temp = (*it++) << 16;
temp += (*it++) << 8;
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
encoded.append(1, kPadCharacter);
break;
}
return encoded;
}
std::vector<byte> decode(const std::string& input)
{
if(input.length() % 4)
throw std::runtime_error("Invalid base64 length!");
std::size_t padding{};
if(input.length())
{
if(input[input.length() - 1] == kPadCharacter) padding++;
if(input[input.length() - 2] == kPadCharacter) padding++;
}
std::vector<byte> decoded;
decoded.reserve(((input.length() / 4) * 3) - padding);
std::uint32_t temp{};
auto it = input.begin();
while(it < input.end())
{
for(std::size_t i = 0; i < 4; ++i)
{
temp <<= 6;
if (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41;
else if(*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47;
else if(*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04;
else if(*it == 0x2B) temp |= 0x3E;
else if(*it == 0x2F) temp |= 0x3F;
else if(*it == kPadCharacter)
{
switch(input.end() - it)
{
case 1:
decoded.push_back((temp >> 16) & 0x000000FF);
decoded.push_back((temp >> 8 ) & 0x000000FF);
return decoded;
case 2:
decoded.push_back((temp >> 10) & 0x000000FF);
return decoded;
default:
throw std::runtime_error("Invalid padding in base64!");
}
}
else throw std::runtime_error("Invalid character in base64!");
++it;
}
decoded.push_back((temp >> 16) & 0x000000FF);
decoded.push_back((temp >> 8 ) & 0x000000FF);
decoded.push_back((temp ) & 0x000000FF);
}
return decoded;
}
}