Commit adfb5902 authored by Dominik Charousset's avatar Dominik Charousset

Implement base64 decoding

parent 5069443f
......@@ -98,11 +98,11 @@ caf_add_component(
src/detail/abstract_worker.cpp
src/detail/abstract_worker_hub.cpp
src/detail/append_percent_encoded.cpp
src/detail/base64.cpp
src/detail/behavior_impl.cpp
src/detail/behavior_stack.cpp
src/detail/blocking_behavior.cpp
src/detail/config_consumer.cpp
src/detail/encode_base64.cpp
src/detail/get_mac_addresses.cpp
src/detail/get_process_id.cpp
src/detail/get_root_uuid.cpp
......@@ -245,9 +245,9 @@ caf_add_component(
decorator.sequencer
deep_to_string
detached_actors
detail.base64
detail.bounds_checker
detail.config_consumer
detail.encode_base64
detail.group_tunnel
detail.ieee_754
detail.json
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/byte_buffer.hpp"
#include "caf/byte_span.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/optional.hpp"
#include "caf/span.hpp"
#include "caf/string_view.hpp"
#include <string>
namespace caf::detail {
class CAF_CORE_EXPORT base64 {
public:
static void encode(string_view str, std::string& out);
static void encode(string_view str, byte_buffer& out);
static void encode(const_byte_span bytes, std::string& out);
static void encode(const_byte_span bytes, byte_buffer& out);
static std::string encode(string_view str) {
std::string result;
encode(str, result);
return result;
}
static std::string encode(const_byte_span bytes) {
std::string result;
encode(bytes, result);
return result;
}
static bool decode(string_view in, std::string& out);
static bool decode(string_view in, byte_buffer& out);
static bool decode(const_byte_span bytes, std::string& out);
static bool decode(const_byte_span bytes, byte_buffer& out);
static optional<std::string> decode(string_view in) {
std::string result;
if (decode(in, result))
return {std::move(result)};
else
return {};
}
static optional<std::string> decode(const_byte_span in) {
std::string result;
if (decode(in, result))
return {std::move(result)};
else
return {};
}
};
} // namespace caf::detail
......@@ -4,17 +4,22 @@
#pragma once
#include "caf/byte.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/span.hpp"
#include "caf/byte_span.hpp"
#include "caf/detail/base64.hpp"
#include "caf/string_view.hpp"
#include <string>
namespace caf::detail {
CAF_CORE_EXPORT std::string encode_base64(string_view str);
[[deprecated("use base64::encode instead")]] inline std::string
encode_base64(string_view str) {
return base64::encode(str);
}
CAF_CORE_EXPORT std::string encode_base64(span<const byte> bytes);
[[deprecated("use base64::encode instead")]] inline std::string
encode_base64(const_byte_span bytes) {
return base64::encode(bytes);
}
} // namespace caf::detail
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/detail/base64.hpp"
#include <cstdint>
namespace caf::detail {
namespace {
// clang-format off
constexpr uint8_t decoding_tbl[] = {
/* ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F */
/* 0.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 1.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 2.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63,
/* 3.. */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0,
/* 4.. */ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/* 5.. */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
/* 6.. */ 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
/* 7.. */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0};
// clang-format on
constexpr const char encoding_tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
template <class Storage>
void encode_impl(string_view in, Storage& out) {
using value_type = typename Storage::value_type;
// Consumes three characters from the input at once.
auto consume = [&out](const char* str) {
int buf[] = {
(str[0] & 0xfc) >> 2,
((str[0] & 0x03) << 4) + ((str[1] & 0xf0) >> 4),
((str[1] & 0x0f) << 2) + ((str[2] & 0xc0) >> 6),
str[2] & 0x3f,
};
for (auto x : buf)
out.push_back(static_cast<value_type>(encoding_tbl[x]));
};
// Iterate the input in chunks of three bytes.
auto i = in.begin();
for (; std::distance(i, in.end()) >= 3; i += 3)
consume(i);
// Deal with any leftover: pad the input with zeros and then fixup the output.
if (i != in.end()) {
char buf[] = {0, 0, 0};
std::copy(i, in.end(), buf);
consume(buf);
for (auto j = out.end() - (3 - (in.size() % 3)); j != out.end(); ++j)
*j = static_cast<value_type>('=');
}
}
template <class Storage>
bool decode_impl(string_view in, Storage& out) {
using value_type = typename Storage::value_type;
// Short-circuit empty inputs.
if (in.empty())
return true;
// Refuse invalid inputs: Base64 always produces character groups of size 4.
if (in.size() % 4 != 0)
return false;
// Consume four characters from the input at once.
auto val = [](char c) { return decoding_tbl[c & 0x7F]; };
for (auto i = in.begin(); i != in.end(); i += 4) {
// clang-format off
auto bits = (val(i[0]) << 18)
| (val(i[1]) << 12)
| (val(i[2]) << 6)
| (val(i[3]));
// clang-format on
out.push_back(static_cast<value_type>((bits & 0xFF0000) >> 16));
out.push_back(static_cast<value_type>((bits & 0x00FF00) >> 8));
out.push_back(static_cast<value_type>((bits & 0x0000FF)));
}
// Fix up the output buffer if the input contained padding.
auto s = in.size();
if (in[s - 2] == '=') {
out.pop_back();
out.pop_back();
} else if (in[s - 1] == '=') {
out.pop_back();
}
return true;
}
string_view as_string_view(const_byte_span bytes) {
return {reinterpret_cast<const char*>(bytes.data()), bytes.size()};
}
} // namespace
void base64::encode(string_view str, std::string& out) {
encode_impl(str, out);
}
void base64::encode(string_view str, byte_buffer& out) {
encode_impl(str, out);
}
void base64::encode(const_byte_span bytes, std::string& out) {
encode_impl(as_string_view(bytes), out);
}
void base64::encode(const_byte_span bytes, byte_buffer& out) {
encode_impl(as_string_view(bytes), out);
}
bool base64::decode(string_view in, std::string& out) {
return decode_impl(in, out);
}
bool base64::decode(string_view in, byte_buffer& out) {
return decode_impl(in, out);
}
bool base64::decode(const_byte_span bytes, std::string& out) {
return decode_impl(as_string_view(bytes), out);
}
bool base64::decode(const_byte_span bytes, byte_buffer& out) {
return decode_impl(as_string_view(bytes), out);
}
} // namespace caf::detail
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/detail/encode_base64.hpp"
namespace caf::detail {
namespace {
constexpr const char base64_tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
} // namespace
std::string encode_base64(string_view str) {
auto bytes = make_span(reinterpret_cast<const byte*>(str.data()), str.size());
return encode_base64(bytes);
}
std::string encode_base64(span<const byte> bytes) {
std::string result;
// Consumes three characters from input at once.
auto consume = [&result](const byte* i) {
auto at = [i](size_t index) { return to_integer<int>(i[index]); };
int buf[] = {
(at(0) & 0xfc) >> 2,
((at(0) & 0x03) << 4) + ((at(1) & 0xf0) >> 4),
((at(1) & 0x0f) << 2) + ((at(2) & 0xc0) >> 6),
at(2) & 0x3f,
};
for (auto x : buf)
result += base64_tbl[x];
};
// Iterate the input in chunks of three bytes.
auto i = bytes.begin();
for (; std::distance(i, bytes.end()) >= 3; i += 3)
consume(i);
if (i != bytes.end()) {
// Pad input with zeros.
byte buf[] = {byte{0}, byte{0}, byte{0}};
std::copy(i, bytes.end(), buf);
consume(buf);
// Override padded bytes (garbage) with '='.
for (auto j = result.end() - (3 - (bytes.size() % 3)); j != result.end();
++j)
*j = '=';
}
return result;
}
} // namespace caf::detail
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE detail.base64
#include "caf/detail/base64.hpp"
#include "core-test.hpp"
using namespace caf;
using namespace caf::literals;
using namespace std::literals::string_literals;
using caf::detail::base64;
CAF_TEST(encoding) {
CHECK_EQ(base64::encode("A"_sv), "QQ=="_sv);
CHECK_EQ(base64::encode("AB"_sv), "QUI="_sv);
CHECK_EQ(base64::encode("ABC"_sv), "QUJD"_sv);
CHECK_EQ(base64::encode("https://actor-framework.org"_sv),
"aHR0cHM6Ly9hY3Rvci1mcmFtZXdvcmsub3Jn"_sv);
}
CAF_TEST(decoding) {
CHECK_EQ(base64::decode("QQ=="_sv), "A"s);
CHECK_EQ(base64::decode("QUI="_sv), "AB"s);
CHECK_EQ(base64::decode("QUJD"_sv), "ABC"s);
CHECK_EQ(base64::decode("aHR0cHM6Ly9hY3Rvci1mcmFtZXdvcmsub3Jn"_sv),
"https://actor-framework.org"s);
}
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE detail.encode_base64
#include "caf/detail/encode_base64.hpp"
#include "caf/test/dsl.hpp"
#include <array>
using namespace caf;
namespace {
template <class... Ts>
auto encode(Ts... xs) {
std::array<byte, sizeof...(Ts)> bytes{{static_cast<byte>(xs)...}};
return detail::encode_base64(bytes);
}
} // namespace
CAF_TEST(base64 encoding converts byte sequences to strings) {
CAF_CHECK_EQUAL(encode(0xb3, 0x7a, 0x4f, 0x2c, 0xc0, 0x62, 0x4f, 0x16, 0x90,
0xf6, 0x46, 0x06, 0xcf, 0x38, 0x59, 0x45, 0xb2, 0xbe,
0xc4, 0xea),
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment