1 /* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
2 
3 #ifndef MPT_BINARY_BASE64_HPP
4 #define MPT_BINARY_BASE64_HPP
5 
6 
7 
8 #include "mpt/base/integer.hpp"
9 #include "mpt/base/memory.hpp"
10 #include "mpt/base/namespace.hpp"
11 #include "mpt/string/types.hpp"
12 
13 #include <array>
14 #include <stdexcept>
15 #include <vector>
16 
17 #include <cstddef>
18 
19 
20 
21 namespace mpt {
22 inline namespace MPT_INLINE_NS {
23 
24 
25 
26 class base64_parse_error : public std::runtime_error {
27 public:
base64_parse_error()28 	base64_parse_error()
29 		: std::runtime_error("invalid Base64 encoding") {
30 	}
31 };
32 
33 
34 inline constexpr std::array<mpt::uchar, 64> base64 = {
35 	{MPT_UCHAR('A'), MPT_UCHAR('B'), MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F'), MPT_UCHAR('G'), MPT_UCHAR('H'), MPT_UCHAR('I'), MPT_UCHAR('J'), MPT_UCHAR('K'), MPT_UCHAR('L'), MPT_UCHAR('M'), MPT_UCHAR('N'), MPT_UCHAR('O'), MPT_UCHAR('P'),
36 	 MPT_UCHAR('Q'), MPT_UCHAR('R'), MPT_UCHAR('S'), MPT_UCHAR('T'), MPT_UCHAR('U'), MPT_UCHAR('V'), MPT_UCHAR('W'), MPT_UCHAR('X'), MPT_UCHAR('Y'), MPT_UCHAR('Z'), MPT_UCHAR('a'), MPT_UCHAR('b'), MPT_UCHAR('c'), MPT_UCHAR('d'), MPT_UCHAR('e'), MPT_UCHAR('f'),
37 	 MPT_UCHAR('g'), MPT_UCHAR('h'), MPT_UCHAR('i'), MPT_UCHAR('j'), MPT_UCHAR('k'), MPT_UCHAR('l'), MPT_UCHAR('m'), MPT_UCHAR('n'), MPT_UCHAR('o'), MPT_UCHAR('p'), MPT_UCHAR('q'), MPT_UCHAR('r'), MPT_UCHAR('s'), MPT_UCHAR('t'), MPT_UCHAR('u'), MPT_UCHAR('v'),
38 	 MPT_UCHAR('w'), MPT_UCHAR('x'), MPT_UCHAR('y'), MPT_UCHAR('z'), MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'), MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'), MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('+'), MPT_UCHAR('/')}};
39 
40 
41 template <typename Tbyte>
encode_base64(mpt::span<Tbyte> src_)42 inline mpt::ustring encode_base64(mpt::span<Tbyte> src_) {
43 	mpt::const_byte_span src = mpt::byte_cast<mpt::const_byte_span>(src_);
44 	mpt::ustring result;
45 	result.reserve(4 * ((src.size() + 2) / 3));
46 	uint32 bits = 0;
47 	std::size_t bytes = 0;
48 	for (std::byte byte : src) {
49 		bits <<= 8;
50 		bits |= mpt::byte_cast<uint8>(byte);
51 		bytes++;
52 		if (bytes == 3) {
53 			result.push_back(base64[(bits >> 18) & 0x3f]);
54 			result.push_back(base64[(bits >> 12) & 0x3f]);
55 			result.push_back(base64[(bits >> 6) & 0x3f]);
56 			result.push_back(base64[(bits >> 0) & 0x3f]);
57 			bits = 0;
58 			bytes = 0;
59 		}
60 	}
61 	std::size_t padding = 0;
62 	while (bytes != 0) {
63 		bits <<= 8;
64 		padding++;
65 		bytes++;
66 		if (bytes == 3) {
67 			result.push_back(base64[(bits >> 18) & 0x3f]);
68 			result.push_back(base64[(bits >> 12) & 0x3f]);
69 			if (padding > 1) {
70 				result.push_back(MPT_UCHAR('='));
71 			} else {
72 				result.push_back(base64[(bits >> 6) & 0x3f]);
73 			}
74 			if (padding > 0) {
75 				result.push_back(MPT_UCHAR('='));
76 			} else {
77 				result.push_back(base64[(bits >> 0) & 0x3f]);
78 			}
79 			bits = 0;
80 			bytes = 0;
81 		}
82 	}
83 	return result;
84 }
85 
decode_base64_bits(mpt::uchar c)86 inline uint8 decode_base64_bits(mpt::uchar c) {
87 	for (uint8 i = 0; i < 64; ++i) {
88 		if (base64[i] == c) {
89 			return i;
90 		}
91 	}
92 	throw base64_parse_error();
93 }
94 
95 
decode_base64(const mpt::ustring & src)96 inline std::vector<std::byte> decode_base64(const mpt::ustring & src) {
97 	std::vector<std::byte> result;
98 	result.reserve(3 * (src.length() / 4));
99 	uint32 bits = 0;
100 	std::size_t chars = 0;
101 	std::size_t padding = 0;
102 	for (mpt::uchar c : src) {
103 		bits <<= 6;
104 		if (c == MPT_UCHAR('=')) {
105 			padding++;
106 		} else {
107 			bits |= decode_base64_bits(c);
108 		}
109 		chars++;
110 		if (chars == 4) {
111 			result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 16) & 0xff)));
112 			if (padding < 2) {
113 				result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 8) & 0xff)));
114 			}
115 			if (padding < 1) {
116 				result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 0) & 0xff)));
117 			}
118 			bits = 0;
119 			chars = 0;
120 			padding = 0;
121 		}
122 	}
123 	if (chars != 0) {
124 		throw base64_parse_error();
125 	}
126 	return result;
127 }
128 
129 
130 
131 } // namespace MPT_INLINE_NS
132 } // namespace mpt
133 
134 
135 
136 #endif // MPT_BINARY_BASE64_HPP
137