The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
base64.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
3 by David White <dave@whitevine.net>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
17
18#include <array>
19#include <string>
20
21namespace
22{
23constexpr std::string_view base64_itoa_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
24constexpr std::string_view crypt64_itoa_map = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
25
26constexpr auto fill_atoi_map(std::string_view itoa)
27{
28 std::array<int, 256> atoi{};
29#if __cpp_lib_array_constexpr >= 201811L
30 atoi.fill(-1);
31#else
32 for(int& elem : atoi) { elem = -1; }
33#endif
34
35 for(std::size_t i = 0; i < itoa.size(); ++i) {
36 atoi[itoa[i]] = static_cast<int>(i);
37 }
38
39 return atoi;
40}
41
42constexpr std::array<int, 256> base64_atoi_map = fill_atoi_map(base64_itoa_map);
43constexpr std::array<int, 256> crypt64_atoi_map = fill_atoi_map(crypt64_itoa_map);
44
45char itoa(unsigned value, std::string_view map)
46{
47 return map[value & 0x3f];
48}
49
50std::vector<uint8_t> generic_decode_be(std::string_view in, utils::span<const int> atoi_map)
51{
52 const std::size_t last_char = in.find_last_not_of("=");
53 if(last_char == std::string::npos) {
54 return {};
55 }
56 const std::size_t num_chars = last_char + 1;
57 const std::size_t length = num_chars * 6 / 8;
58
59 std::vector<uint8_t> out;
60 out.reserve(length);
61
62 int val = 0, bits = -8;
63 for(unsigned char c: in) {
64 if(atoi_map[c] == -1) {
65 // Non-base64 character encountered. Should be =
66 if(c != '='){
67 // If it's not a valid char, return an empty result
68 return {};
69 }
70 break;
71 }
72 val = (val<<6) + atoi_map[c];
73 bits += 6;
74 if(bits >= 0) {
75 out.push_back(static_cast<char>((val >> bits) & 0xFF));
76 bits -= 8;
77 val &= 0xFFFF; // Prevent shifting bits off the left end, which is UB
78 }
79 }
80 if(out.size() != length) {
81 return {};
82 }
83
84 return out;
85}
86
87std::vector<uint8_t> generic_decode_le(std::string_view in, utils::span<const int> atoi_map)
88{
89 const std::size_t last_char = in.find_last_not_of("=");
90 if(last_char == std::string::npos) {
91 return {};
92 }
93 const std::size_t length = last_char * 6 / 8;
94
95 std::vector<uint8_t> out;
96 out.reserve(length);
97
98 for(std::size_t i = 0; i <= last_char; i += 4) {
99 //add first char (always)
100 unsigned value = atoi_map[in[i]];
101
102 const bool second_char = i + 1 <= last_char;
103 if(!second_char) {
104 break;
105 }
106 //add second char (if present)
107 value |= atoi_map[in[i+1]] << 6;
108
109 //output first byte (if second char)
110 out.push_back(value & 0xFF);
111
112 const bool third_char = i + 2 <= last_char;
113 if(!third_char) {
114 break;
115 }
116 //add third char (if present)
117 value |= atoi_map[in[i+2]] << 12;
118
119 //output second byte (if third char)
120 out.push_back((value >> 8) & 0xFF);
121
122 const bool fourth_char = i + 3 <= last_char;
123 if(!fourth_char) {
124 break;
125 }
126 //add fourth char (if present)
127 value |= atoi_map[in[i+3]] << 18;
128
129 //output third byte (if fourth char)
130 out.push_back((value >> 16) & 0xFF);
131 }
132
133 return out;
134}
135
136std::string generic_encode_be(utils::byte_view in, std::string_view itoa_map, bool pad)
137{
138 const int in_len = in.size();
139 const int groups = (in_len + 2) / 3;
140 const int out_len = groups * 4;
141
142 std::string out;
143
144 int i = 0;
145 out.reserve(out_len);
146 unsigned value = 0;
147 unsigned bits = 0;
148 while(i < in_len) {
149 value <<= 8;
150 value |= in[i++];
151 bits += 8;
152 do {
153 bits -= 6;
154 out.push_back(itoa(value >> bits, itoa_map));
155 } while(bits >= 6);
156 }
157 if(bits > 0) {
158 out.push_back(itoa(value << (6 - bits), itoa_map));
159 }
160
161 if(pad) {
162 // If not round, append = chars
163 out.resize(out_len, '=');
164 }
165
166 return out;
167
168}
169std::string generic_encode_le(utils::byte_view in, std::string_view itoa_map, bool pad)
170{
171 const int in_len = in.size();
172 const int groups = (in_len + 2) / 3;
173 const int out_len = groups * 4;
174
175 std::string out;
176
177 int i = 0;
178 out.reserve(out_len);
179 while(i < in_len) {
180 //add first byte (always)
181 unsigned value = in[i];
182 //output first char (always)
183 out.push_back(itoa(value, itoa_map));
184 //add second byte (if present)
185 const bool second_byte = ++i < in_len;
186 if(second_byte) {
187 value |= static_cast<int>(in[i]) << 8;
188 }
189 //output second char (always, contains 2 bits from first byte)
190 out.push_back(itoa(value >> 6, itoa_map));
191 if(!second_byte) {
192 break;
193 }
194 //add third byte (if present)
195 const bool third_byte = ++i < in_len;
196 if(third_byte) {
197 value |= static_cast<int>(in[i]) << 16;
198 }
199 //output third char (if second byte)
200 out.push_back(itoa(value >> 12, itoa_map));
201 //output fourth char (if third byte)
202 if(third_byte) {
203 out.push_back(itoa(value >> 18, itoa_map));
204 ++i;
205 }
206 }
207
208 if(pad) {
209 // If not round, append = chars
210 out.resize(out_len, '=');
211 }
212
213 return out;
214
215}
216}
217
218namespace base64 {
219std::vector<uint8_t> decode(std::string_view in)
220{
221 return generic_decode_be(in, base64_atoi_map);
222}
223std::string encode(utils::byte_view bytes)
224{
225 return generic_encode_be(bytes, base64_itoa_map, true);
226}
227}
228namespace crypt64{
229std::vector<uint8_t> decode(std::string_view in)
230{
231 return generic_decode_le(in, crypt64_atoi_map);
232}
233std::string encode(utils::byte_view bytes)
234{
235 return generic_encode_le(bytes, crypt64_itoa_map, false);
236}
237int decode(char encoded_char)
238{
239 std::size_t pos = crypt64_itoa_map.find(encoded_char);
240 return pos == std::string::npos ? -1 : pos;
241}
242char encode(int value)
243{
244 return itoa(value, crypt64_itoa_map);
245}
246}
unsigned in
If equal to search_counter, the node is off the list.
constexpr size_type size() const noexcept
Definition span.hpp:355
std::string encode(utils::byte_view bytes)
Definition base64.cpp:223
std::vector< uint8_t > decode(std::string_view in)
Definition base64.cpp:219
std::string encode(utils::byte_view bytes)
Definition base64.cpp:233
std::vector< uint8_t > decode(std::string_view in)
Definition base64.cpp:229
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
#define i
#define c