The Battle for Wesnoth  1.19.16+dev
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 
16 #include "serialization/base64.hpp"
17 
18 #include <array>
19 #include <string>
20 
21 namespace
22 {
23 constexpr std::string_view base64_itoa_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
24 constexpr std::string_view crypt64_itoa_map = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
25 
26 constexpr 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 
42 constexpr std::array<int, 256> base64_atoi_map = fill_atoi_map(base64_itoa_map);
43 constexpr std::array<int, 256> crypt64_atoi_map = fill_atoi_map(crypt64_itoa_map);
44 
45 char itoa(unsigned value, std::string_view map)
46 {
47  return map[value & 0x3f];
48 }
49 
50 std::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 
87 std::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 
136 std::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 }
169 std::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 
218 namespace base64 {
219 std::vector<uint8_t> decode(std::string_view in)
220 {
221  return generic_decode_be(in, base64_atoi_map);
222 }
223 std::string encode(utils::byte_view bytes)
224 {
225  return generic_encode_be(bytes, base64_itoa_map, true);
226 }
227 }
228 namespace crypt64{
229 std::vector<uint8_t> decode(std::string_view in)
230 {
231  return generic_decode_le(in, crypt64_atoi_map);
232 }
233 std::string encode(utils::byte_view bytes)
234 {
235  return generic_encode_le(bytes, crypt64_itoa_map, false);
236 }
237 int 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 }
242 char 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.
Definition: astarsearch.cpp:70
std::size_t i
Definition: function.cpp:1032
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
mock_char c