tlx
base64.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * tlx/string/base64.cpp
3  *
4  * Part of tlx - http://panthema.net/tlx
5  *
6  * Copyright (C) 2007-2017 Timo Bingmann <tb@panthema.net>
7  *
8  * All rights reserved. Published under the Boost Software License, Version 1.0
9  ******************************************************************************/
10 
11 #include <tlx/string/base64.hpp>
12 
13 #include <stdexcept>
14 
15 namespace tlx {
16 
17 /*
18  * Code in this file is based on source code from http://libb64.sourceforge.net/
19  * which is in the public domain.
20  */
21 
22 /******************************************************************************/
23 // Base64 Encoding and Decoding
24 
25 std::string base64_encode(const void* data, size_t size, size_t line_break) {
26  const uint8_t* in = reinterpret_cast<const uint8_t*>(data);
27  const uint8_t* in_end = in + size;
28  std::string out;
29 
30  if (size == 0) return out;
31 
32  // calculate output string's size in advance
33  size_t outsize = (((size - 1) / 3) + 1) * 4;
34  if (line_break > 0) outsize += outsize / line_break;
35  out.reserve(outsize);
36 
37  static const char encoding64[64] = {
38  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
39  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
40  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
41  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
42  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
43  };
44 
45  uint8_t result = 0;
46  size_t line_begin = 0;
47 
48  while (true)
49  {
50  // step 0: if the string is finished here, no padding is needed
51  if (in == in_end) {
52  return out;
53  }
54 
55  // step 0: process first byte, write first letter
56  uint8_t fragment = *in++;
57  result = (fragment & 0xFC) >> 2;
58  out += encoding64[result];
59  result = static_cast<uint8_t>((fragment & 0x03) << 4);
60 
61  // step 1: if string finished here, add two padding '='s
62  if (in == in_end) {
63  out += encoding64[result];
64  out += '=';
65  out += '=';
66  return out;
67  }
68 
69  // step 1: process second byte together with first, write second
70  // letter
71  fragment = *in++;
72  result |= (fragment & 0xF0) >> 4;
73  out += encoding64[result];
74  result = static_cast<uint8_t>((fragment & 0x0F) << 2);
75 
76  // step 2: if string finished here, add one padding '='
77  if (in == in_end) {
78  out += encoding64[result];
79  out += '=';
80  return out;
81  }
82 
83  // step 2: process third byte and write third and fourth letters.
84  fragment = *in++;
85 
86  result |= (fragment & 0xC0) >> 6;
87  out += encoding64[result];
88 
89  result = (fragment & 0x3F) >> 0;
90  out += encoding64[result];
91 
92  // wrap base64 encoding into lines if desired, but only after whole
93  // blocks of 4 letters.
94  if (line_break > 0 && out.size() - line_begin >= line_break)
95  {
96  out += '\n';
97  line_begin = out.size();
98  }
99  }
100 }
101 
102 std::string base64_encode(const std::string& str, size_t line_break) {
103  return base64_encode(str.data(), str.size(), line_break);
104 }
105 
106 /******************************************************************************/
107 
108 std::string base64_decode(const void* data, size_t size, bool strict) {
109  const uint8_t* in = reinterpret_cast<const uint8_t*>(data);
110  const uint8_t* in_end = in + size;
111  std::string out;
112 
113  // estimate the output size, assume that the whole input string is
114  // base64 encoded.
115  out.reserve(size * 3 / 4);
116 
117  static constexpr uint8_t ex = 255;
118  static constexpr uint8_t ws = 254;
119  // value lookup table: -1 -> exception, -2 -> skip whitespace
120  static const uint8_t decoding64[256] = {
121  ex, ex, ex, ex, ex, ex, ex, ex, ex, ws, ws, ex, ex, ws, ex, ex,
122  ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
123  ws, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, 62, ex, ex, ex, 63,
124  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, ex, ex, ex, ws, ex, ex,
125  ex, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
126  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, ex, ex, ex, ex, ex,
127  ex, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
128  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, ex, ex, ex, ex, ex,
129  ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
130  ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
131  ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
132  ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
133  ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
134  ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
135  ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
136  ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex
137  };
138 
139  uint8_t outchar, fragment;
140 
141  static const char* ex_message =
142  "Invalid character encountered during base64 decoding.";
143 
144  while (true)
145  {
146  // step 0: save first valid letter. do not output a byte, yet.
147  do {
148  if (in == in_end) return out;
149 
150  fragment = decoding64[*in++];
151 
152  if (fragment == ex && strict)
153  throw std::runtime_error(ex_message);
154  } while (fragment >= ws);
155 
156  outchar = static_cast<uint8_t>((fragment & 0x3F) << 2);
157 
158  // step 1: get second valid letter. output the first byte.
159  do {
160  if (in == in_end) return out;
161 
162  fragment = decoding64[*in++];
163 
164  if (fragment == ex && strict)
165  throw std::runtime_error(ex_message);
166  } while (fragment >= ws);
167 
168  outchar = static_cast<uint8_t>(outchar | ((fragment & 0x30) >> 4));
169  out += static_cast<char>(outchar);
170 
171  outchar = static_cast<uint8_t>((fragment & 0x0F) << 4);
172 
173  // step 2: get third valid letter. output the second byte.
174  do {
175  if (in == in_end) return out;
176 
177  fragment = decoding64[*in++];
178 
179  if (fragment == ex && strict)
180  throw std::runtime_error(ex_message);
181  } while (fragment >= ws);
182 
183  outchar = static_cast<uint8_t>(outchar | ((fragment & 0x3C) >> 2));
184  out += static_cast<char>(outchar);
185 
186  outchar = static_cast<uint8_t>((fragment & 0x03) << 6);
187 
188  // step 3: get fourth valid letter. output the third byte.
189  do {
190  if (in == in_end) return out;
191 
192  fragment = decoding64[*in++];
193 
194  if (fragment == ex && strict)
195  throw std::runtime_error(ex_message);
196  } while (fragment >= ws);
197 
198  outchar = static_cast<uint8_t>(outchar | ((fragment & 0x3F) >> 0));
199  out += static_cast<char>(outchar);
200  }
201 }
202 
203 std::string base64_decode(const std::string& str, bool strict) {
204  return base64_decode(str.data(), str.size(), strict);
205 }
206 
207 } // namespace tlx
208 
209 /******************************************************************************/
std::string base64_decode(const void *data, size_t size, bool strict)
Decode a string in base64 representation as described in RFC 2045 or RFC 3548 and return the original...
Definition: base64.cpp:108
std::string base64_encode(const void *data, size_t size, size_t line_break)
Encode the given binary data into base64 representation as described in RFC 2045 or RFC 3548...
Definition: base64.cpp:25