Last active
January 29, 2019 17:43
-
-
Save arrieta/668d7e9bbe82fe0060f5fc1cc168b1b3 to your computer and use it in GitHub Desktop.
Implementation of Base64 encoding and decoding in C++
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
// -*- coding:utf-8; mode:c++; mode:auto-fill; fill-column:80; -*- | |
/// @file base64.hpp | |
/// @brief Base64 encoding and decoding. | |
/// @author J. Arrieta <[email protected]> | |
/// @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 |
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
// -*- coding:utf-8; mode:c++; mode:auto-fill; fill-column:80; -*- | |
/// @file base64.t.cpp | |
/// @brief Test Base64 encoding and decoding. | |
/// @author J. Arrieta <[email protected]> | |
/// @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"; | |
} |
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
Example:
Output (newlines for clarity):