Mercurial > code
annotate modules/base64/base64.hpp @ 575:0f6501d6df34
Base64: reduce column limit
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 19 Jul 2016 22:35:42 +0200 |
parents | f48bb09bccc7 |
children |
rev | line source |
---|---|
517 | 1 /* |
2 * base64.hpp -- base64 encoding and decoding | |
3 * | |
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #ifndef BASE64_HPP | |
20 #define BASE64_HPP | |
21 | |
22 /** | |
23 * \file base64.hpp | |
24 * \brief Base64 encoding and decoding. | |
25 * \author David Demelier <markand@malikania.fr> | |
26 */ | |
27 | |
28 /** | |
29 * \page Base64 Base64 | |
30 * \brief Base64 encoding and decoding. | |
31 * | |
575
0f6501d6df34
Base64: reduce column limit
David Demelier <markand@malikania.fr>
parents:
548
diff
changeset
|
32 * The %base64 library let you encode and decode %base64 data from std::string |
0f6501d6df34
Base64: reduce column limit
David Demelier <markand@malikania.fr>
parents:
548
diff
changeset
|
33 * and iterators. |
517 | 34 * |
35 * ## Encoding | |
36 * | |
37 * You can encode like this: | |
38 * | |
39 * ````cpp | |
40 * std::string b64 = base64::encode("Hello world!"); | |
41 * ```` | |
42 * | |
43 * ## Decoding | |
44 * | |
45 * And you can decode like this: | |
46 * | |
47 * ```` | |
48 * try { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
49 * std::string text = base64::decode(msg); |
517 | 50 * } catch (const std::exception &ex) { |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
51 * std::cerr << ex.what() << std::endl; |
517 | 52 * } |
53 * ```` | |
54 */ | |
55 | |
56 #include <cassert> | |
57 #include <cctype> | |
58 #include <stdexcept> | |
59 #include <string> | |
60 | |
61 /** | |
62 * \brief main %base64 namespace. | |
63 */ | |
64 namespace base64 { | |
65 | |
66 /** | |
67 * Check if the character is a %base64 character, A-Za-z0-9 and +/. | |
68 * | |
69 * \param ch the character to test | |
70 * \return true if valid | |
71 */ | |
72 inline bool isBase64(char ch) noexcept | |
73 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
74 return std::isalnum(ch) != 0 || ch == '+' || ch == '/'; |
517 | 75 } |
76 | |
77 /** | |
575
0f6501d6df34
Base64: reduce column limit
David Demelier <markand@malikania.fr>
parents:
548
diff
changeset
|
78 * Check if the given character is a valid character in %base64 string, |
0f6501d6df34
Base64: reduce column limit
David Demelier <markand@malikania.fr>
parents:
548
diff
changeset
|
79 * including '='. |
517 | 80 * |
81 * \param ch the character | |
82 * \return true if the character is valid | |
83 */ | |
84 inline bool isValid(char ch) noexcept | |
85 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
86 return isBase64(ch) || ch == '='; |
517 | 87 } |
88 | |
89 /** | |
90 * Get the %base64 character from the 6-bits value. | |
91 * | |
92 * \pre value must be valid (< 64) | |
93 * \param value the value | |
94 * \return the %base64 character for value | |
95 */ | |
96 inline char lookup(unsigned char value) noexcept | |
97 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
98 static const char *table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
517 | 99 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
100 assert(value < 64); |
517 | 101 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
102 return table[value]; |
517 | 103 } |
104 | |
105 /** | |
106 * Get the integer value from the %base64 character. | |
107 * | |
108 * \pre isBase64(ch) | |
109 * \param ch the %base64 character | |
110 * \return the integer value for the %base64 character ch | |
111 * ```` | |
112 * auto b64 = base64::rlookup('D') // 3 | |
113 * ```` | |
114 */ | |
115 inline unsigned char rlookup(char ch) noexcept | |
116 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
117 assert(isBase64(ch)); |
517 | 118 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
119 if (ch >= '0' && ch <= '9') |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
120 return static_cast<unsigned char>(ch + 4); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
121 if (ch >= 'A' && ch <= 'Z') |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
122 return static_cast<unsigned char>(ch - 65); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
123 if (ch >= 'a' && ch <= 'z') |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
124 return static_cast<unsigned char>(ch - 71); |
517 | 125 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
126 return (ch == '+') ? 62U : 63U; |
517 | 127 } |
128 | |
129 /** | |
130 * Encode the input to the output. | |
131 * | |
132 * Requirements: | |
133 * - **InputIt** must be [InputIterator](http://en.cppreference.com/w/cpp/concept/InputIterator) | |
134 * - **OutputIt** must be [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator) | |
135 * | |
136 * \param input the beginning | |
137 * \param end the end of the data | |
138 * \param output the output destination | |
139 * \return output | |
140 */ | |
141 template <typename InputIt, typename OutputIt> | |
142 OutputIt encode(InputIt input, InputIt end, OutputIt output) | |
143 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
144 while (input != end) { |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
145 char inputbuf[3] = { 0, 0, 0 }; |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
146 int count; |
517 | 147 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
148 for (count = 0; count < 3 && input != end; ++count) |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
149 inputbuf[count] = *input++; |
517 | 150 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
151 *output++ = lookup(inputbuf[0] >> 2 & 0x3f); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
152 *output++ = lookup((inputbuf[0] << 4 & 0x3f) | (inputbuf[1] >> 4 & 0x0f)); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
153 *output++ = (count < 2) ? '=' : lookup((inputbuf[1] << 2 & 0x3c) | (inputbuf[2] >> 6 & 0x03)); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
154 *output++ = (count < 3) ? '=' : lookup(inputbuf[2] & 0x3f); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
155 } |
517 | 156 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
157 return output; |
517 | 158 } |
159 | |
160 /** | |
161 * Decode the input to the output. | |
162 * | |
163 * Requirements: | |
164 * - **InputIt** must be [InputIterator](http://en.cppreference.com/w/cpp/concept/InputIterator) | |
165 * - **OutputIt** must be [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator) | |
166 * | |
167 * \param input the beginning | |
168 * \param end the end of the data | |
169 * \param output the output destination | |
170 * \return output | |
171 * \throw std::invalid_argument on bad %base64 string | |
172 */ | |
173 template <typename InputIt, typename OutputIt> | |
174 OutputIt decode(InputIt input, InputIt end, OutputIt output) | |
175 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
176 while (input != end) { |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
177 char inputbuf[4] = { -1, -1, -1, -1 }; |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
178 int count; |
517 | 179 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
180 for (count = 0; count < 4 && input != end; ++count) { |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
181 // Check if the character is valid and get its value. |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
182 if ((*input == '=' && count <= 1) || !isValid(*input)) |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
183 throw std::invalid_argument("invalid base64 string"); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
184 if (isBase64(*input)) |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
185 inputbuf[count] = static_cast<char>(rlookup(*input)); |
517 | 186 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
187 input++; |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
188 } |
517 | 189 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
190 if (count != 4) |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
191 throw std::invalid_argument("truncated string"); |
517 | 192 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
193 *output++ = static_cast<char>(((inputbuf[0] << 2) & 0xfc) | ((inputbuf[1] >> 4) & 0x03)); |
517 | 194 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
195 if (inputbuf[2] != -1) |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
196 *output++ = static_cast<char>(((inputbuf[1] << 4) & 0xf0) | ((inputbuf[2] >> 2) & 0x0f)); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
197 if (inputbuf[3] != -1) { |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
198 // "XY=Z" is not allowed. |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
199 if (inputbuf[2] == -1) |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
200 throw std::invalid_argument("invalid base64 string"); |
517 | 201 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
202 *output++ = static_cast<char>(((inputbuf[2] << 6) & 0xc0) | (inputbuf[3] & 0x3f)); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
203 } |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
204 } |
517 | 205 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
206 return output; |
517 | 207 } |
208 | |
209 /** | |
210 * Encode a string. | |
211 * | |
212 * \param input the input string | |
213 * \return the %base64 formatted string | |
214 */ | |
215 inline std::string encode(const std::string &input) | |
216 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
217 std::string result; |
517 | 218 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
219 encode(input.begin(), input.end(), std::back_inserter(result)); |
517 | 220 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
221 return result; |
517 | 222 } |
223 | |
224 /** | |
225 * Decode a string. | |
226 * | |
227 * \param input the %base64 formatted string | |
228 * \return the original string | |
229 * \throw std::invalid_argument on bad %base64 string | |
230 */ | |
231 inline std::string decode(const std::string &input) | |
232 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
233 std::string result; |
517 | 234 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
235 decode(input.begin(), input.end(), std::back_inserter(result)); |
517 | 236 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
517
diff
changeset
|
237 return result; |
517 | 238 } |
239 | |
240 } // !base64 | |
241 | |
242 #endif // !BASE64_HPP |