The Battle for Wesnoth  1.19.5+dev
base64.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
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 <string>
19 
20 namespace {
21 const std::string base64_itoa_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
22 const std::string crypt64_itoa_map = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
23 
24 void fill_atoi_map(std::vector<int>& atoi, const std::string& itoa)
25 {
26  for(int i=0; i<64; ++i) {
27  atoi[itoa[i]] = i;
28  }
29 }
30 
31 const std::vector<int>& base64_atoi_map()
32 {
33  static std::vector<int> atoi64(256, -1);
34  if(atoi64['A'] == -1) {
35  fill_atoi_map(atoi64, base64_itoa_map);
36  }
37  return atoi64;
38 }
39 const std::vector<int>& crypt64_atoi_map()
40 {
41  static std::vector<int> atoi64(256, -1);
42  if(atoi64['A'] == -1) {
43  fill_atoi_map(atoi64, crypt64_itoa_map);
44  }
45  return atoi64;
46 }
47 char itoa(unsigned value, const std::string& map)
48 {
49  return map[value & 0x3f];
50 }
51 
52 std::vector<uint8_t> generic_decode_be(std::string_view in, const std::vector<int>& atoi_map)
53 {
54  const std::size_t last_char = in.find_last_not_of("=");
55  if(last_char == std::string::npos) {
56  return {};
57  }
58  const std::size_t num_chars = last_char + 1;
59  const std::size_t length = num_chars * 6 / 8;
60 
61  std::vector<uint8_t> out;
62  out.reserve(length);
63 
64  int val = 0, bits = -8;
65  for(unsigned char c: in) {
66  if(atoi_map[c] == -1) {
67  // Non-base64 character encountered. Should be =
68  if(c != '='){
69  // If it's not a valid char, return an empty result
70  return {};
71  }
72  break;
73  }
74  val = (val<<6) + atoi_map[c];
75  bits += 6;
76  if(bits >= 0) {
77  out.push_back(static_cast<char>((val >> bits) & 0xFF));
78  bits -= 8;
79  val &= 0xFFFF; // Prevent shifting bits off the left end, which is UB
80  }
81  }
82  if(out.size() != length) {
83  return {};
84  }
85 
86  return out;
87 }
88 
89 std::vector<uint8_t> generic_decode_le(std::string_view in, const std::vector<int>& atoi_map)
90 {
91  const std::size_t last_char = in.find_last_not_of("=");
92  if(last_char == std::string::npos) {
93  return {};
94  }
95  const std::size_t length = last_char * 6 / 8;
96 
97  std::vector<uint8_t> out;
98  out.reserve(length);
99 
100  for(std::size_t i = 0; i <= last_char; i += 4) {
101  //add first char (always)
102  unsigned value = atoi_map[in[i]];
103 
104  const bool second_char = i + 1 <= last_char;
105  if(!second_char) {
106  break;
107  }
108  //add second char (if present)
109  value |= atoi_map[in[i+1]] << 6;
110 
111  //output first byte (if second char)
112  out.push_back(value & 0xFF);
113 
114  const bool third_char = i + 2 <= last_char;
115  if(!third_char) {
116  break;
117  }
118  //add third char (if present)
119  value |= atoi_map[in[i+2]] << 12;
120 
121  //output second byte (if third char)
122  out.push_back((value >> 8) & 0xFF);
123 
124  const bool fourth_char = i + 3 <= last_char;
125  if(!fourth_char) {
126  break;
127  }
128  //add fourth char (if present)
129  value |= atoi_map[in[i+3]] << 18;
130 
131  //output third byte (if fourth char)
132  out.push_back((value >> 16) & 0xFF);
133  }
134 
135  return out;
136 }
137 
138 std::string generic_encode_be(utils::byte_string_view in, const std::string& itoa_map, bool pad)
139 {
140  const int in_len = in.length();
141  const int groups = (in_len + 2) / 3;
142  const int out_len = groups * 4;
143 
144  std::string out;
145 
146  int i = 0;
147  out.reserve(out_len);
148  unsigned value = 0;
149  unsigned bits = 0;
150  while(i < in_len) {
151  value <<= 8;
152  value |= in[i++];
153  bits += 8;
154  do {
155  bits -= 6;
156  out.push_back(itoa(value >> bits, itoa_map));
157  } while(bits >= 6);
158  }
159  if(bits > 0) {
160  out.push_back(itoa(value << (6 - bits), itoa_map));
161  }
162 
163  if(pad) {
164  // If not round, append = chars
165  out.resize(out_len, '=');
166  }
167 
168  return out;
169 
170 }
171 std::string generic_encode_le(utils::byte_string_view in, const std::string& itoa_map, bool pad)
172 {
173  const int in_len = in.length();
174  const int groups = (in_len + 2) / 3;
175  const int out_len = groups * 4;
176 
177  std::string out;
178 
179  int i = 0;
180  out.reserve(out_len);
181  while(i < in_len) {
182  //add first byte (always)
183  unsigned value = in[i];
184  //output first char (always)
185  out.push_back(itoa(value, itoa_map));
186  //add second byte (if present)
187  const bool second_byte = ++i < in_len;
188  if(second_byte) {
189  value |= static_cast<int>(in[i]) << 8;
190  }
191  //output second char (always, contains 2 bits from first byte)
192  out.push_back(itoa(value >> 6, itoa_map));
193  if(!second_byte) {
194  break;
195  }
196  //add third byte (if present)
197  const bool third_byte = ++i < in_len;
198  if(third_byte) {
199  value |= static_cast<int>(in[i]) << 16;
200  }
201  //output third char (if second byte)
202  out.push_back(itoa(value >> 12, itoa_map));
203  //output fourth char (if third byte)
204  if(third_byte) {
205  out.push_back(itoa(value >> 18, itoa_map));
206  ++i;
207  }
208  }
209 
210  if(pad) {
211  // If not round, append = chars
212  out.resize(out_len, '=');
213  }
214 
215  return out;
216 
217 }
218 }
219 
220 namespace base64 {
221 std::vector<uint8_t> decode(std::string_view in)
222 {
223  return generic_decode_be(in, base64_atoi_map());
224 }
225 std::string encode(utils::byte_string_view bytes)
226 {
227  return generic_encode_be(bytes, base64_itoa_map, true);
228 }
229 }
230 namespace crypt64{
231 std::vector<uint8_t> decode(std::string_view in)
232 {
233  return generic_decode_le(in, crypt64_atoi_map());
234 }
235 std::string encode(utils::byte_string_view bytes)
236 {
237  return generic_encode_le(bytes, crypt64_itoa_map, false);
238 }
239 int decode(char encoded_char)
240 {
241  std::size_t pos = crypt64_itoa_map.find(encoded_char);
242  return pos == std::string::npos ? -1 : pos;
243 }
244 char encode(int value)
245 {
246  return itoa(value, crypt64_itoa_map);
247 }
248 }
std::size_t i
Definition: function.cpp:1028
unsigned in
If equal to search_counter, the node is off the list.
std::string encode(utils::byte_string_view bytes)
Definition: base64.cpp:225
std::vector< uint8_t > decode(std::string_view in)
Definition: base64.cpp:221
std::string encode(utils::byte_string_view bytes)
Definition: base64.cpp:235
std::vector< uint8_t > decode(std::string_view in)
Definition: base64.cpp:231
std::basic_string_view< uint8_t > byte_string_view
Definition: base64.hpp:25
mock_char c