ome-common  5.3.0
base64.h
1 /*
2  * #%L
3  * OME-COMMON C++ library for C++ compatibility/portability
4  * %%
5  * Copyright © 2016 Open Microscopy Environment:
6  * - Massachusetts Institute of Technology
7  * - National Institutes of Health
8  * - University of Dundee
9  * - Board of Regents of the University of Wisconsin-Madison
10  * - Glencoe Software, Inc.
11  * %%
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright notice,
16  * this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright notice,
18  * this list of conditions and the following disclaimer in the documentation
19  * and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * The views and conclusions contained in the software and documentation are
34  * those of the authors and should not be interpreted as representing official
35  * policies, either expressed or implied, of any organization.
36  * #L%
37  */
38 
39 #ifndef OME_COMMON_BASE64_H
40 #define OME_COMMON_BASE64_H
41 
42 #include <cctype>
43 #include <iterator>
44 #include <stdexcept>
45 #include <string>
46 
47 #include <ome/compat/cstdint.h>
48 
49 namespace ome
50 {
51  namespace common
52  {
53 
54  namespace detail
55  {
56 
69  inline void
70  output_base64_char(std::string& encoded,
71  uint8_t value,
72  uint64_t chars,
73  uint8_t linebreak)
74  {
75  // Static mapping using simple offsets.
76  static const char * const base64_chars =
77  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // 0-25
78  "abcdefghijklmnopqrstuvwxyz" // 26-51
79  "0123456789+/="; // 52-63
80 
81  encoded += base64_chars[value];
82  if (linebreak && (chars + 1) % linebreak == 0)
83  encoded += '\n';
84  }
85 
96  inline uint8_t
97  base64_value(char c)
98  {
99  // Static mapping; due to the nature of the encoding, it's sparse.
100  // Without C0 (-0x20) to reduce size. 98=pad, 99=invalid.
101  static const uint8_t base64_codes[] =
102  {
103  // ' ' ! " # $ % & ' ( ) * + , - . /
104  99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63,
105  // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
106  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 98, 99, 99,
107  // @ A B C D E F G H I J K L M N O
108  99, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
109  // P Q R S T U V W X Y Z [ \ ] ^ _
110  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 99, 99, 99, 99, 99,
111  // ` a b c d e f g h i j k l m n o
112  99, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
113  // p q r s t u v w x y z
114  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
115  };
116 
117  if (c < 0x20 || c > 'z')
118  throw std::runtime_error("Invalid Base64 input: character outside permitted range");
119  uint8_t v = base64_codes[c - 0x20];
120  if (v == 99)
121  throw std::runtime_error("Invalid Base64 input: character outside permitted range");
122  return v;
123  }
124 
140  inline uint8_t
141  next_base64_value(std::string::const_iterator& pos,
142  std::string::const_iterator end)
143  {
144  while (pos != end)
145  {
146  if (!std::isspace(*pos))
147  {
148  uint8_t value = base64_value(*pos++);
149  return value;
150  }
151  ++pos;
152  }
153  return 255;
154  }
155 
156  }
157 
171  template<typename Iterator>
172  std::string
173  base64_encode(Iterator begin,
174  Iterator end,
175  uint8_t linebreak = 76)
176  {
177  std::string encoded;
178  encoded.reserve((std::distance(begin, end) * 4) / 3);
179 
180  uint64_t bytes = 0U;
181  uint64_t chars = 0U;
182  uint8_t accum = 0U;
183  for (Iterator i = begin; i != end; ++bytes)
184  {
185  uint8_t byte = *i; // Byte 1
186  ++i;
187  accum = byte >> 2;
188  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 1
189  accum = (byte & 0x3) << 4;
190  if (i != end)
191  {
192  byte = *i; // Byte 2
193  ++i;
194  accum |= (byte & 0xF0) >> 4;
195  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 2
196  accum = (byte & 0x0F) << 2;
197  if (i != end)
198  {
199  byte = *i; // Byte 3
200  ++i;
201  accum |= (byte & 0xC0) >> 6;
202  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 3
203  accum = byte & 0x3F;
204  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 4
205  }
206  else
207  {
208  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 3 (+pad)
209  encoded += '='; // Char 4 (pad)
210  }
211  }
212  else
213  {
214  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 2 (+pad)
215  encoded += "=="; // Chars 3 and 4
216  }
217  }
218 
219  return encoded;
220  }
221 
232  template<typename InsertIterator>
233  void
234  base64_decode(std::string base64,
235  InsertIterator dest)
236  {
237  bool pad_seen = false;
238  uint8_t bytes[4];
239  uint8_t output;
240  for (std::string::const_iterator i = base64.begin();
241  i != base64.end();)
242  {
243  // Get next 4 bytes. If only whitespace remains, the first
244  // byte will be 255. Since the input is blocked into groups
245  // of four characters, fetch four at once.
246  for (int j = 0; j < 4; ++j)
247  {
248  bytes[j] = detail::next_base64_value(i, base64.end());
249  if(j == 0 && bytes[j] == 255)
250  break;
251  }
252 
253  if (bytes[0] == 255) // End of input (expected)
254  break;
255  if (bytes[1] == 255 || bytes[2] == 255 || bytes[3] == 255) // End of input (unexpected)
256  throw std::runtime_error("Invalid Base64 input: unexpected end of input");
257 
258  if (bytes[0] < 64 && bytes[1] < 64) // Valid input
259  {
260  if(pad_seen) // Padding only allowed at end.
261  throw std::runtime_error("Invalid Base64 input: padding only permitted at end of input");
262 
263  output = bytes[0] << 2 | bytes[1] >> 4;
264  *dest = output; // Byte 1
265  if (bytes[2] < 64) // Skip if padded
266  {
267  output = bytes[1] << 4 | bytes[2] >> 2;
268  *dest = output; // Byte 2
269  if (bytes[3] < 64) // Skip if padded
270  {
271  output = bytes[2] << 6 | bytes[3];
272  *dest = output; // Byte 3
273  }
274  else
275  {
276  pad_seen = true;
277  }
278  }
279  else
280  {
281  pad_seen = true;
282  }
283  }
284  else
285  {
286  throw(std::runtime_error("Invalid Base64 input: padding encountered unexpectedly"));
287  }
288  }
289  }
290 
301  template<typename Container>
302  Container
303  base64_decode(const std::string& base64)
304  {
305  Container decoded;
306 
307  decoded.reserve((base64.size() * 3) / 4);
308 
309  base64_decode(base64, std::back_inserter(decoded));
310 
311  return decoded;
312  }
313 
314  }
315 }
316 
317 #endif // OME_COMMON_BASE64_H
318 
319 /*
320  * Local Variables:
321  * mode:C++
322  * End:
323  */
void base64_decode(std::string base64, InsertIterator dest)
Decode a Base64-encoded string.
Definition: base64.h:234
Definition: base64.h:49
std::string base64_encode(Iterator begin, Iterator end, uint8_t linebreak=76)
Base64-encode a range of bytes.
Definition: base64.h:173
Standard integer types.