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