ome-common 5.3.2

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 */
ome::common::base64_decode
void base64_decode(std::string base64, InsertIterator dest)
Decode a Base64-encoded string.
Definition: base64.h:234
ome
Open Microscopy Environment C++.
Definition: base64.h:49
ome::common::base64_encode
std::string base64_encode(Iterator begin, Iterator end, uint8_t linebreak=76)
Base64-encode a range of bytes.
Definition: base64.h:173
cstdint.h
Standard integer types.