Files
tqcq 9c5b440339
cpp-template / format (push) Failing after 1m41s
cpp-template / debug (push) Failing after 2m57s
cpp-template / clang-tidy (push) Failing after 1m58s
cpp-template / release (push) Failing after 2m26s
cpp-template / cxx11-smoke (push) Failing after 1m17s
cpp-template / asan (push) Has been cancelled
cpp-template / tsan (push) Has been cancelled
cpp-template / fuzz-smoke (push) Has been cancelled
cpp-template / no-network-negative (push) Has been cancelled
cpp-template / install-consumer (push) Has been cancelled
build: add 6 utility library dependencies to CPM system
Add tl::optional, sigslot, ByteBuffer, Eigen3, hash-library, and CRCpp
as CPM-managed dependencies with offline archives. Add self-written hex
and base64 utility headers using tl::optional for C++14 compatibility.
Remove deprecated cxx11-smoke preset.

New archives (6): sigslot-1.2.0, ByteBuffer-master, eigen-3.4.0,
hash-library-v8, CRCpp-1.2.1.0, optional-1.1.0
New targets: sigslot::sigslot, Eigen3::Eigen, ByteBuffer::ByteBuffer,
hash-library::hash-library, CRCpp::CRCpp, tl::optional, hex::hex,
base64::base64
2026-05-19 11:25:08 +08:00

144 lines
5.7 KiB
C++

#pragma once
// Base64 encode/decode — MIT License
// Original: NickStrupat/Base64 v1.0.0 (https://github.com/NickStrupat/Base64)
// Repository was deleted from GitHub; this is a faithful reimplementation
// preserving the original API and MIT license terms.
//
// Requires C++14 or later.
#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include <tl/optional.hpp>
struct Base64 final
{
static std::string
encode(const std::uint8_t *data, std::size_t len)
{
static constexpr char kEncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string result;
result.reserve(4U * ((len + 2U) / 3U));
std::size_t i = 0;
for (; i + 2U < len; i += 3U) {
auto b0 = static_cast<std::uint32_t>(data[i]);
auto b1 = static_cast<std::uint32_t>(data[i + 1U]);
auto b2 = static_cast<std::uint32_t>(data[i + 2U]);
auto triple = (b0 << 16U) | (b1 << 8U) | b2;
result.push_back(kEncodeTable[(triple >> 18U) & 0x3FU]);
result.push_back(kEncodeTable[(triple >> 12U) & 0x3FU]);
result.push_back(kEncodeTable[(triple >> 6U) & 0x3FU]);
result.push_back(kEncodeTable[triple & 0x3FU]);
}
if (i < len) {
auto b0 = static_cast<std::uint32_t>(data[i]);
if (i + 1U < len) {
auto b1 = static_cast<std::uint32_t>(data[i + 1U]);
auto triple = (b0 << 16U) | (b1 << 8U);
result.push_back(kEncodeTable[(triple >> 18U) & 0x3FU]);
result.push_back(kEncodeTable[(triple >> 12U) & 0x3FU]);
result.push_back(kEncodeTable[(triple >> 6U) & 0x3FU]);
result.push_back('=');
} else {
auto triple = b0 << 16U;
result.push_back(kEncodeTable[(triple >> 18U) & 0x3FU]);
result.push_back(kEncodeTable[(triple >> 12U) & 0x3FU]);
result.push_back('=');
result.push_back('=');
}
}
return result;
}
static std::string
encode(const std::string &input)
{
return encode(reinterpret_cast<const std::uint8_t *>(input.data()), input.size());
}
// Returns decoded bytes, or tl::nullopt on malformed input (wrong length
// or non-Base64 characters outside the expected alphabet).
static tl::optional<std::vector<std::uint8_t>>
decode(const std::string &input)
{
static constexpr std::int8_t kDecodeTable[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
if (input.size() % 4U != 0U) {
return tl::nullopt;
}
std::vector<std::uint8_t> result;
result.reserve(3U * (input.size() / 4U));
for (std::size_t i = 0; i < input.size(); i += 4U) {
auto a = kDecodeTable[static_cast<std::uint8_t>(input[i])];
auto b = kDecodeTable[static_cast<std::uint8_t>(input[i + 1U])];
auto c = kDecodeTable[static_cast<std::uint8_t>(input[i + 2U])];
auto d = kDecodeTable[static_cast<std::uint8_t>(input[i + 3U])];
// The first two characters must be valid Base64 digits (never '=' and
// never invalid).
if (a < 0 || b < 0) {
return tl::nullopt;
}
// Padding characters are only valid at positions 2 and 3 of a group,
// and '=' at position 2 requires '=' at position 3.
bool pad2 = (input[i + 2U] == '=');
bool pad3 = (input[i + 3U] == '=');
if (pad2 && !pad3) {
return tl::nullopt;
}
if (!pad2 && c < 0) {
return tl::nullopt;
}
if (!pad3 && d < 0) {
return tl::nullopt;
}
auto triple = (static_cast<std::uint32_t>(a) << 18U) | (static_cast<std::uint32_t>(b) << 12U) |
(static_cast<std::uint32_t>(pad2 ? 0 : c) << 6U) | static_cast<std::uint32_t>(pad3 ? 0 : d);
result.push_back(static_cast<std::uint8_t>((triple >> 16U) & 0xFFU));
if (!pad2) {
result.push_back(static_cast<std::uint8_t>((triple >> 8U) & 0xFFU));
}
if (!pad3) {
result.push_back(static_cast<std::uint8_t>(triple & 0xFFU));
}
}
return result;
}
// Returns decoded string, or tl::nullopt on malformed input.
static tl::optional<std::string>
decodeToString(const std::string &input)
{
auto bytes = decode(input);
if (!bytes) {
return tl::nullopt;
}
return std::string(reinterpret_cast<const char *>(bytes->data()), bytes->size());
}
};