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