My Base64 encoder class converts a string with ascii characters to a base64 encoded string with padding if required. Note that I'm not using istream
as the input because my goal was only to convert a std::string
with ascii characters to another std::string
with base64 encoded characters.
Only resource I used was this wikipedia article.
base64encoder.h:
#ifndef BASE64_BASE64_H
#define BASE64_BASE64_H
#include <array>
class Base64Encoder {
public:
Base64Encoder() = default;
~Base64Encoder() = default;
const std::string encode(const std::string s) const;
private:
constexpr static std::array<const unsigned char, 64> base64_table = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', '+', '/'};
constexpr static unsigned char get_base_64_char(ulong number_of_char) {
return base64_table.at(number_of_char);
}
const static unsigned char next_ascii(size_t current_index, const std::string s, size_t length_of_s);
const static size_t MINIMAL_B64_STRING_LENGTH = 4;
};
#endif //BASE64_BASE64_H
base64encoder.cpp:
#include <iostream>
#include <sstream>
#include "base64encoder.h"
// Takes a string and encodes it into a base64 string.
const std::string Base64Encoder::encode(const std::string s) const {
std::stringstream encoding;
const size_t length_of_s = s.length();
size_t i = 0;
// Base64 uses 6 bits for encoding. ASCII characters have a size of 8 bits.
// We sometimes have to use bits of the next character a.k.a right character.
size_t bit_offset = 0; // offset of used bits in current ascii character
while (i < length_of_s) {
// left_ascii is the current ASCII character which needs to be encoded.
auto left_ascii = static_cast<unsigned char>(s.at(i));
// b64 will contain the encoded b64 character.
unsigned char b64_encoded = '\0';
if (bit_offset == 0) {
left_ascii = left_ascii >> 2;
b64_encoded = get_base_64_char(static_cast<ulong>(left_ascii));
bit_offset = 6;
// no need to go to the next index, we didn't use any other information except the current char.
} else if (bit_offset == 6) {
left_ascii = left_ascii << 6;
left_ascii = left_ascii >> 2;
auto right_ascii = Base64Encoder::next_ascii(i, s, length_of_s);
right_ascii = right_ascii >> 4;
left_ascii = right_ascii | left_ascii;
b64_encoded = get_base_64_char(static_cast<ulong>(left_ascii));
bit_offset = 4;
++i;
} else if (bit_offset == 4) {
left_ascii = left_ascii << 4;
left_ascii = left_ascii >> 2;
auto right_ascii = Base64Encoder::next_ascii(i, s, length_of_s);
right_ascii = right_ascii >> 6;
left_ascii = left_ascii | right_ascii;
b64_encoded = get_base_64_char(static_cast<ulong>(left_ascii));
bit_offset = 2;
++i;
} else { // offset is 2
left_ascii = left_ascii << 2;
left_ascii = left_ascii >> 2;
b64_encoded = get_base_64_char(static_cast<ulong>(left_ascii));
bit_offset = 0;
++i;
}
encoding << b64_encoded;
}
// Checking if padding in form of appending '=' is required.
if (bit_offset || length_of_s < 2) {
encoding << '=';
if (length_of_s == 1) {
encoding << '=';
}
}
return encoding.str();
}
// returns next ascii character if index is in string or a zero byte which happens if we are at the end of string
// and there is no next ascii character filling up our 6 bits which we need for a base64 encoded char - in this case
// we also need padding.
const unsigned char Base64Encoder::next_ascii(size_t current_index, const std::string s, size_t length_of_s) {
size_t next_index = ++current_index;
if (next_index < length_of_s) {
return static_cast<unsigned char>(s.at(next_index));
}
unsigned char zero_byte = 0;
return zero_byte;
}
Example usage main.cpp:
#include <iostream>
#include "base64encoder.h"
int main() {
Base64Encoder b;
const std::string s("Hello Base64!");
std::cout << b.encode(s);
}
A-Za-z0-9+/
characters into 3 bytes. It does this by using 6 bits to encode each character and thus has a limited character set. Messages are always a multiple of 3 in length and the '=' character is used to indicate terminator padding. \$\endgroup\$