Skip to content

Instantly share code, notes, and snippets.

@arrieta
Last active January 29, 2019 17:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arrieta/668d7e9bbe82fe0060f5fc1cc168b1b3 to your computer and use it in GitHub Desktop.
Save arrieta/668d7e9bbe82fe0060f5fc1cc168b1b3 to your computer and use it in GitHub Desktop.
Implementation of Base64 encoding and decoding in C++
// -*- coding:utf-8; mode:c++; mode:auto-fill; fill-column:80; -*-
/// @file base64.hpp
/// @brief Base64 encoding and decoding.
/// @author J. Arrieta <juan.arrieta@nablazerolabs.com>
/// @date January 28, 2019
/// @copyright (C) 2019 Nabla Zero Labs
#pragma once
// C++ Standard Library
#include <cstdint>
#include <string>
namespace nzl {
namespace {
static constexpr const std::byte ENCODING_TABLE[]{
std::byte{'A'}, std::byte{'B'}, std::byte{'C'}, std::byte{'D'},
std::byte{'E'}, std::byte{'F'}, std::byte{'G'}, std::byte{'H'},
std::byte{'I'}, std::byte{'J'}, std::byte{'K'}, std::byte{'L'},
std::byte{'M'}, std::byte{'N'}, std::byte{'O'}, std::byte{'P'},
std::byte{'Q'}, std::byte{'R'}, std::byte{'S'}, std::byte{'T'},
std::byte{'U'}, std::byte{'V'}, std::byte{'W'}, std::byte{'X'},
std::byte{'Y'}, std::byte{'Z'}, std::byte{'a'}, std::byte{'b'},
std::byte{'c'}, std::byte{'d'}, std::byte{'e'}, std::byte{'f'},
std::byte{'g'}, std::byte{'h'}, std::byte{'i'}, std::byte{'j'},
std::byte{'k'}, std::byte{'l'}, std::byte{'m'}, std::byte{'n'},
std::byte{'o'}, std::byte{'p'}, std::byte{'q'}, std::byte{'r'},
std::byte{'s'}, std::byte{'t'}, std::byte{'u'}, std::byte{'v'},
std::byte{'w'}, std::byte{'x'}, std::byte{'y'}, std::byte{'z'},
std::byte{'0'}, std::byte{'1'}, std::byte{'2'}, std::byte{'3'},
std::byte{'4'}, std::byte{'5'}, std::byte{'6'}, std::byte{'7'},
std::byte{'8'}, std::byte{'9'}, std::byte{'+'}, std::byte{'/'},
};
template <std::size_t n>
inline constexpr void encode(const std::byte src[n], std::byte dst[4]) noexcept;
template <>
inline constexpr void encode<3>(const std::byte src[3],
std::byte dst[4]) noexcept {
auto I0 = src[0] >> 2;
auto I1 = src[0] << 6 >> 2 | src[1] >> 4;
auto I2 = src[1] << 4 >> 2 | src[2] >> 6;
auto I3 = src[2] << 2 >> 2;
dst[0] = ENCODING_TABLE[static_cast<int>(I0)];
dst[1] = ENCODING_TABLE[static_cast<int>(I1)];
dst[2] = ENCODING_TABLE[static_cast<int>(I2)];
dst[3] = ENCODING_TABLE[static_cast<int>(I3)];
}
template <>
inline constexpr void encode<2>(const std::byte src[2],
std::byte dst[4]) noexcept {
auto I0 = src[0] >> 2;
auto I1 = src[0] << 6 >> 2 | src[1] >> 4;
auto I2 = src[1] << 4 >> 2;
dst[0] = ENCODING_TABLE[static_cast<int>(I0)];
dst[1] = ENCODING_TABLE[static_cast<int>(I1)];
dst[2] = ENCODING_TABLE[static_cast<int>(I2)];
dst[3] = std::byte{'='};
}
template <>
inline constexpr void encode<1>(const std::byte src[1],
std::byte dst[4]) noexcept {
auto I0 = src[0] >> 2;
auto I1 = src[0] << 6 >> 2;
dst[0] = ENCODING_TABLE[static_cast<int>(I0)];
dst[1] = ENCODING_TABLE[static_cast<int>(I1)];
dst[2] = std::byte{'='};
dst[3] = std::byte{'='};
}
inline constexpr std::byte find(std::byte b) noexcept {
for (auto offset = 0u; offset < 64u; ++offset) {
if (ENCODING_TABLE[offset] == b) {
return std::byte(offset);
}
}
return std::byte(0);
}
template <std::size_t n>
inline constexpr void decode(const std::byte src[4], std::byte dst[3]) noexcept;
template <>
inline constexpr void decode<4>(const std::byte src[4],
std::byte dst[3]) noexcept {
auto O0 = find(src[0]);
auto O1 = find(src[1]);
auto O2 = find(src[2]);
auto O3 = find(src[3]);
dst[0] = O0 << 2 | O1 >> 4;
dst[1] = O1 << 4 | O2 >> 2;
dst[2] = O2 << 6 | O3;
}
} // namespace
/// @brief Encode a given string in Base64.
/// @param s String to encode in Base64.
/// @return The string encoded in Base64.
inline std::string base64_encode(const std::string& s) {
const auto len = s.size();
const auto nh = len / 3;
const auto nt = len - 3 * nh;
const auto nx = nt ? 4 : 0;
std::string encoded;
encoded.resize(4 * nh + nx);
auto src = reinterpret_cast<const std::byte*>(s.data());
auto dst = reinterpret_cast<std::byte*>(encoded.data());
for (auto chunk = 0u; chunk < nh; ++chunk, src += 3, dst += 4) {
encode<3>(src, dst);
}
switch (nt) {
case 2:
encode<2>(src, dst);
break;
case 1:
encode<1>(src, dst);
break;
default:
break;
}
return encoded;
}
/// @brief Decode a given Base64 string.
/// @param s String to decode from Base64.
/// @return The string decoded from Base64.
inline std::string base64_decode(const std::string& s) {
const auto len = s.size();
const auto nh = len / 4;
std::string decoded;
decoded.resize(3 * nh);
auto src = reinterpret_cast<const std::byte*>(s.data());
auto dst = reinterpret_cast<std::byte*>(decoded.data());
for (auto chunk = 0u; chunk < nh; ++chunk, src += 4, dst += 3) {
decode<4>(src, dst);
}
while (decoded.back() == '\0') {
decoded.pop_back();
}
return decoded;
}
} // namespace nzl
// -*- coding:utf-8; mode:c++; mode:auto-fill; fill-column:80; -*-
/// @file base64.t.cpp
/// @brief Test Base64 encoding and decoding.
/// @author J. Arrieta <juan.arrieta@nablazerolabs.com>
/// @date January 28, 2019
/// @copyright (C) 2019 Nabla Zero Labs
// Nabla Zero Labs Library
#include "base64.hpp"
// C++ Standard Library
#include <iostream>
int main() {
const std::string s =
"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.";
const auto e = nzl::base64_encode(s);
const auto d = nzl::base64_decode(e);
std::cout << "s " << s << "\n"
<< "e " << e << "\n"
<< "d " << d << "\n"
<< "s == d? " << std::boolalpha << (s == d) << "\n";
}
@arrieta
Copy link
Author

arrieta commented Jan 29, 2019

Example:

#include <iostream>
int main() {
  std::string s =
      "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.";
  std::cout << nzl::base64_encode(s) << "\n";
}

Output (newlines for clarity):

TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=

@arrieta
Copy link
Author

arrieta commented Jan 29, 2019

Test driver run:

$ clang++ base64.t.cpp -std=c++17 -Wall -Wextra -O3 -march=native
$ ./a.out 
s       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.
e       TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=
d       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.
s == d? true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment