Mercurial > malikania
comparison tools/map/base64.hpp @ 86:cbdd3302998c
Tools: implement basic mlk-map, closes #620
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 31 Jan 2017 11:23:21 +0100 |
parents | |
children | 4b292c20124c |
comparison
equal
deleted
inserted
replaced
85:de77ef21a329 | 86:cbdd3302998c |
---|---|
1 /* | |
2 * base64.hpp -- base64 encoding and decoding | |
3 * | |
4 * Copyright (c) 2013-2017 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 MALIKANIA_BASE64_HPP | |
20 #define MALIKANIA_BASE64_HPP | |
21 | |
22 /** | |
23 * \file base64.hpp | |
24 * \brief Base64 encoding and decoding. | |
25 * \author David Demelier <markand@malikania.fr> | |
26 * \version 2.0.0-dev | |
27 */ | |
28 | |
29 /** | |
30 * \page Base64 Base64 | |
31 * \brief Base64 encoding and decoding. | |
32 * | |
33 * The %base64 library let you encode and decode %base64 data from std::string | |
34 * and iterators. | |
35 * | |
36 * ## Encoding | |
37 * | |
38 * You can encode like this: | |
39 * | |
40 * ````cpp | |
41 * std::string b64 = base64::encode("Hello world!"); | |
42 * ```` | |
43 * | |
44 * ## Decoding | |
45 * | |
46 * And you can decode like this: | |
47 * | |
48 * ```` | |
49 * try { | |
50 * std::string text = base64::decode(msg); | |
51 * } catch (const std::exception &ex) { | |
52 * std::cerr << ex.what() << std::endl; | |
53 * } | |
54 * ```` | |
55 */ | |
56 | |
57 #include <cassert> | |
58 #include <cctype> | |
59 #include <stdexcept> | |
60 #include <string> | |
61 | |
62 namespace mlk { | |
63 | |
64 /** | |
65 * \brief main %base64 namespace. | |
66 */ | |
67 namespace base64 { | |
68 | |
69 /** | |
70 * Check if the character is a %base64 character, A-Za-z0-9 and +/. | |
71 * | |
72 * \param ch the character to test | |
73 * \return true if valid | |
74 */ | |
75 inline bool is_base64(char ch) noexcept | |
76 { | |
77 return std::isalnum(ch) != 0 || ch == '+' || ch == '/'; | |
78 } | |
79 | |
80 /** | |
81 * Check if the given character is a valid character in %base64 string, | |
82 * including '='. | |
83 * | |
84 * \param ch the character | |
85 * \return true if the character is valid | |
86 */ | |
87 inline bool is_valid(char ch) noexcept | |
88 { | |
89 return is_base64(ch) || ch == '='; | |
90 } | |
91 | |
92 /** | |
93 * Get the %base64 character from the 6-bits value. | |
94 * | |
95 * \pre value must be valid (< 64) | |
96 * \param value the value | |
97 * \return the %base64 character for value | |
98 */ | |
99 inline char lookup(unsigned char value) noexcept | |
100 { | |
101 static const char *table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
102 | |
103 assert(value < 64); | |
104 | |
105 return table[value]; | |
106 } | |
107 | |
108 /** | |
109 * Get the integer value from the %base64 character. | |
110 * | |
111 * \pre is_base64(ch) | |
112 * \param ch the %base64 character | |
113 * \return the integer value for the %base64 character ch | |
114 * ```` | |
115 * auto b64 = base64::rlookup('D') // 3 | |
116 * ```` | |
117 */ | |
118 inline unsigned char rlookup(char ch) noexcept | |
119 { | |
120 assert(is_base64(ch)); | |
121 | |
122 if (ch >= '0' && ch <= '9') | |
123 return static_cast<unsigned char>(ch + 4); | |
124 if (ch >= 'A' && ch <= 'Z') | |
125 return static_cast<unsigned char>(ch - 65); | |
126 if (ch >= 'a' && ch <= 'z') | |
127 return static_cast<unsigned char>(ch - 71); | |
128 | |
129 return (ch == '+') ? 62U : 63U; | |
130 } | |
131 | |
132 /** | |
133 * Encode the input to the output. | |
134 * | |
135 * Requirements: | |
136 * - **InputIt** must be [InputIterator](http://en.cppreference.com/w/cpp/concept/InputIterator) | |
137 * - **OutputIt** must be [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator) | |
138 * | |
139 * \param input the beginning | |
140 * \param end the end of the data | |
141 * \param output the output destination | |
142 * \return output | |
143 */ | |
144 template <typename InputIt, typename OutputIt> | |
145 OutputIt encode(InputIt input, InputIt end, OutputIt output) | |
146 { | |
147 while (input != end) { | |
148 char inputbuf[3] = { 0, 0, 0 }; | |
149 int count; | |
150 | |
151 for (count = 0; count < 3 && input != end; ++count) | |
152 inputbuf[count] = *input++; | |
153 | |
154 *output++ = lookup(inputbuf[0] >> 2 & 0x3f); | |
155 *output++ = lookup((inputbuf[0] << 4 & 0x3f) | (inputbuf[1] >> 4 & 0x0f)); | |
156 *output++ = (count < 2) ? '=' : lookup((inputbuf[1] << 2 & 0x3c) | (inputbuf[2] >> 6 & 0x03)); | |
157 *output++ = (count < 3) ? '=' : lookup(inputbuf[2] & 0x3f); | |
158 } | |
159 | |
160 return output; | |
161 } | |
162 | |
163 /** | |
164 * Decode the input to the output. | |
165 * | |
166 * Requirements: | |
167 * - **InputIt** must be [InputIterator](http://en.cppreference.com/w/cpp/concept/InputIterator) | |
168 * - **OutputIt** must be [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator) | |
169 * | |
170 * \param input the beginning | |
171 * \param end the end of the data | |
172 * \param output the output destination | |
173 * \return output | |
174 * \throw std::invalid_argument on bad %base64 string | |
175 */ | |
176 template <typename InputIt, typename OutputIt> | |
177 OutputIt decode(InputIt input, InputIt end, OutputIt output) | |
178 { | |
179 while (input != end) { | |
180 char inputbuf[4] = { -1, -1, -1, -1 }; | |
181 int count; | |
182 | |
183 for (count = 0; count < 4 && input != end; ++count) { | |
184 // Check if the character is valid and get its value. | |
185 if ((*input == '=' && count <= 1) || !is_valid(*input)) | |
186 throw std::invalid_argument("invalid base64 string"); | |
187 if (is_base64(*input)) | |
188 inputbuf[count] = static_cast<char>(rlookup(*input)); | |
189 | |
190 input++; | |
191 } | |
192 | |
193 if (count != 4) | |
194 throw std::invalid_argument("truncated string"); | |
195 | |
196 *output++ = static_cast<char>(((inputbuf[0] << 2) & 0xfc) | ((inputbuf[1] >> 4) & 0x03)); | |
197 | |
198 if (inputbuf[2] != -1) | |
199 *output++ = static_cast<char>(((inputbuf[1] << 4) & 0xf0) | ((inputbuf[2] >> 2) & 0x0f)); | |
200 if (inputbuf[3] != -1) { | |
201 // "XY=Z" is not allowed. | |
202 if (inputbuf[2] == -1) | |
203 throw std::invalid_argument("invalid base64 string"); | |
204 | |
205 *output++ = static_cast<char>(((inputbuf[2] << 6) & 0xc0) | (inputbuf[3] & 0x3f)); | |
206 } | |
207 } | |
208 | |
209 return output; | |
210 } | |
211 | |
212 /** | |
213 * Encode a string. | |
214 * | |
215 * \param input the input string | |
216 * \return the %base64 formatted string | |
217 */ | |
218 inline std::string encode(const std::string &input) | |
219 { | |
220 std::string result; | |
221 | |
222 encode(input.begin(), input.end(), std::back_inserter(result)); | |
223 | |
224 return result; | |
225 } | |
226 | |
227 /** | |
228 * Decode a string. | |
229 * | |
230 * \param input the %base64 formatted string | |
231 * \return the original string | |
232 * \throw std::invalid_argument on bad %base64 string | |
233 */ | |
234 inline std::string decode(const std::string &input) | |
235 { | |
236 std::string result; | |
237 | |
238 decode(input.begin(), input.end(), std::back_inserter(result)); | |
239 | |
240 return result; | |
241 } | |
242 | |
243 } // !base64 | |
244 | |
245 } // !mlk | |
246 | |
247 #endif // !MALIKANIA_BASE64_HPP |