ome-common  5.5.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 <cstdint>
44 #include <iterator>
45 #include <stdexcept>
46 #include <string>
47 
48 namespace ome
49 {
50  namespace common
51  {
52 
53  namespace detail
54  {
55 
68  inline void
69  output_base64_char(std::string& encoded,
70  uint8_t value,
71  uint64_t chars,
72  uint8_t linebreak)
73  {
74  // Static mapping using simple offsets.
75  static const char * const base64_chars =
76  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // 0-25
77  "abcdefghijklmnopqrstuvwxyz" // 26-51
78  "0123456789+/="; // 52-63
79 
80  encoded += base64_chars[value];
81  if (linebreak && (chars + 1) % linebreak == 0)
82  encoded += '\n';
83  }
84 
95  inline uint8_t
96  base64_value(char c)
97  {
98  // Static mapping; due to the nature of the encoding, it's sparse.
99  // Without C0 (-0x20) to reduce size. 98=pad, 99=invalid.
100  static const uint8_t base64_codes[] =
101  {
102  // ' ' ! " # $ % & ' ( ) * + , - . /
103  99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63,
104  // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
105  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 98, 99, 99,
106  // @ A B C D E F G H I J K L M N O
107  99, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
108  // P Q R S T U V W X Y Z [ \ ] ^ _
109  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 99, 99, 99, 99, 99,
110  // ` a b c d e f g h i j k l m n o
111  99, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
112  // p q r s t u v w x y z
113  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
114  };
115 
116  if (c < 0x20 || c > 'z')
117  throw std::runtime_error("Invalid Base64 input: character outside permitted range");
118  uint8_t v = base64_codes[c - 0x20];
119  if (v == 99)
120  throw std::runtime_error("Invalid Base64 input: character outside permitted range");
121  return v;
122  }
123 
139  inline uint8_t
140  next_base64_value(std::string::const_iterator& pos,
141  std::string::const_iterator end)
142  {
143  while (pos != end)
144  {
145  if (!std::isspace(*pos))
146  {
147  uint8_t value = base64_value(*pos++);
148  return value;
149  }
150  ++pos;
151  }
152  return 255;
153  }
154 
155  }
156 
170  template<typename Iterator>
171  std::string
172  base64_encode(Iterator begin,
173  Iterator end,
174  uint8_t linebreak = 76)
175  {
176  std::string encoded;
177  encoded.reserve((std::distance(begin, end) * 4) / 3);
178 
179  uint64_t bytes = 0U;
180  uint64_t chars = 0U;
181  uint8_t accum = 0U;
182  for (Iterator i = begin; i != end; ++bytes)
183  {
184  uint8_t byte = *i; // Byte 1
185  ++i;
186  accum = byte >> 2;
187  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 1
188  accum = (byte & 0x3) << 4;
189  if (i != end)
190  {
191  byte = *i; // Byte 2
192  ++i;
193  accum |= (byte & 0xF0) >> 4;
194  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 2
195  accum = (byte & 0x0F) << 2;
196  if (i != end)
197  {
198  byte = *i; // Byte 3
199  ++i;
200  accum |= (byte & 0xC0) >> 6;
201  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 3
202  accum = byte & 0x3F;
203  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 4
204  }
205  else
206  {
207  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 3 (+pad)
208  encoded += '='; // Char 4 (pad)
209  }
210  }
211  else
212  {
213  detail::output_base64_char(encoded, accum, chars++, linebreak); // Char 2 (+pad)
214  encoded += "=="; // Chars 3 and 4
215  }
216  }
217 
218  return encoded;
219  }
220 
231  template<typename InsertIterator>
232  void
233  base64_decode(std::string base64,
234  InsertIterator dest)
235  {
236  bool pad_seen = false;
237  uint8_t bytes[4];
238  uint8_t output;
239  for (std::string::const_iterator i = base64.begin();
240  i != base64.end();)
241  {
242  // Get next 4 bytes. If only whitespace remains, the first
243  // byte will be 255. Since the input is blocked into groups
244  // of four characters, fetch four at once.
245  for (int j = 0; j < 4; ++j)
246  {
247  bytes[j] = detail::next_base64_value(i, base64.end());
248  if(j == 0 && bytes[j] == 255)
249  break;
250  }
251 
252  if (bytes[0] == 255) // End of input (expected)
253  break;
254  if (bytes[1] == 255 || bytes[2] == 255 || bytes[3] == 255) // End of input (unexpected)
255  throw std::runtime_error("Invalid Base64 input: unexpected end of input");
256 
257  if (bytes[0] < 64 && bytes[1] < 64) // Valid input
258  {
259  if(pad_seen) // Padding only allowed at end.
260  throw std::runtime_error("Invalid Base64 input: padding only permitted at end of input");
261 
262  output = bytes[0] << 2 | bytes[1] >> 4;
263  *dest = output; // Byte 1
264  if (bytes[2] < 64) // Skip if padded
265  {
266  output = bytes[1] << 4 | bytes[2] >> 2;
267  *dest = output; // Byte 2
268  if (bytes[3] < 64) // Skip if padded
269  {
270  output = bytes[2] << 6 | bytes[3];
271  *dest = output; // Byte 3
272  }
273  else
274  {
275  pad_seen = true;
276  }
277  }
278  else
279  {
280  pad_seen = true;
281  }
282  }
283  else
284  {
285  throw(std::runtime_error("Invalid Base64 input: padding encountered unexpectedly"));
286  }
287  }
288  }
289 
300  template<typename Container>
301  Container
302  base64_decode(const std::string& base64)
303  {
304  Container decoded;
305 
306  decoded.reserve((base64.size() * 3) / 4);
307 
308  base64_decode(base64, std::back_inserter(decoded));
309 
310  return decoded;
311  }
312 
313  }
314 }
315 
316 #endif // OME_COMMON_BASE64_H
317 
318 /*
319  * Local Variables:
320  * mode:C++
321  * End:
322  */
void base64_decode(std::string base64, InsertIterator dest)
Decode a Base64-encoded string.
Definition: base64.h:233
Open Microscopy Environment C++.
Definition: base64.h:48
std::string base64_encode(Iterator begin, Iterator end, uint8_t linebreak=76)
Base64-encode a range of bytes.
Definition: base64.h:172