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