tlx
base64.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 * tlx/string/base64.cpp
3 *
4 * Part of tlx - http://panthema.net/tlx
5 *
6 * Copyright (C) 2007-2017 Timo Bingmann <tb@panthema.net>
7 *
8 * All rights reserved. Published under the Boost Software License, Version 1.0
9 ******************************************************************************/
10
11#include <tlx/string/base64.hpp>
12
13#include <stdexcept>
14
15namespace tlx {
16
17/*
18 * Code in this file is based on source code from http://libb64.sourceforge.net/
19 * which is in the public domain.
20 */
21
22/******************************************************************************/
23// Base64 Encoding and Decoding
24
25std::string base64_encode(const void* data, size_t size, size_t line_break) {
26 const uint8_t* in = reinterpret_cast<const uint8_t*>(data);
27 const uint8_t* in_end = in + size;
28 std::string out;
29
30 if (size == 0) return out;
31
32 // calculate output string's size in advance
33 size_t outsize = (((size - 1) / 3) + 1) * 4;
34 if (line_break > 0) outsize += outsize / line_break;
35 out.reserve(outsize);
36
37 static const char encoding64[64] = {
38 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
39 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
40 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
41 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
42 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
43 };
44
45 uint8_t result = 0;
46 size_t line_begin = 0;
47
48 while (true)
49 {
50 // step 0: if the string is finished here, no padding is needed
51 if (in == in_end) {
52 return out;
53 }
54
55 // step 0: process first byte, write first letter
56 uint8_t fragment = *in++;
57 result = (fragment & 0xFC) >> 2;
58 out += encoding64[result];
59 result = static_cast<uint8_t>((fragment & 0x03) << 4);
60
61 // step 1: if string finished here, add two padding '='s
62 if (in == in_end) {
63 out += encoding64[result];
64 out += '=';
65 out += '=';
66 return out;
67 }
68
69 // step 1: process second byte together with first, write second
70 // letter
71 fragment = *in++;
72 result |= (fragment & 0xF0) >> 4;
73 out += encoding64[result];
74 result = static_cast<uint8_t>((fragment & 0x0F) << 2);
75
76 // step 2: if string finished here, add one padding '='
77 if (in == in_end) {
78 out += encoding64[result];
79 out += '=';
80 return out;
81 }
82
83 // step 2: process third byte and write third and fourth letters.
84 fragment = *in++;
85
86 result |= (fragment & 0xC0) >> 6;
87 out += encoding64[result];
88
89 result = (fragment & 0x3F) >> 0;
90 out += encoding64[result];
91
92 // wrap base64 encoding into lines if desired, but only after whole
93 // blocks of 4 letters.
94 if (line_break > 0 && out.size() - line_begin >= line_break)
95 {
96 out += '\n';
97 line_begin = out.size();
98 }
99 }
100}
101
102std::string base64_encode(const std::string& str, size_t line_break) {
103 return base64_encode(str.data(), str.size(), line_break);
104}
105
106/******************************************************************************/
107
108std::string base64_decode(const void* data, size_t size, bool strict) {
109 const uint8_t* in = reinterpret_cast<const uint8_t*>(data);
110 const uint8_t* in_end = in + size;
111 std::string out;
112
113 // estimate the output size, assume that the whole input string is
114 // base64 encoded.
115 out.reserve(size * 3 / 4);
116
117 static constexpr uint8_t ex = 255;
118 static constexpr uint8_t ws = 254;
119 // value lookup table: -1 -> exception, -2 -> skip whitespace
120 static const uint8_t decoding64[256] = {
121 ex, ex, ex, ex, ex, ex, ex, ex, ex, ws, ws, ex, ex, ws, ex, ex,
122 ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
123 ws, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, 62, ex, ex, ex, 63,
124 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, ex, ex, ex, ws, ex, ex,
125 ex, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
126 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, ex, ex, ex, ex, ex,
127 ex, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
128 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, ex, ex, ex, ex, ex,
129 ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
130 ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
131 ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
132 ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
133 ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
134 ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
135 ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex,
136 ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex, ex
137 };
138
139 uint8_t outchar, fragment;
140
141 static const char* ex_message =
142 "Invalid character encountered during base64 decoding.";
143
144 while (true)
145 {
146 // step 0: save first valid letter. do not output a byte, yet.
147 do {
148 if (in == in_end) return out;
149
150 fragment = decoding64[*in++];
151
152 if (fragment == ex && strict)
153 throw std::runtime_error(ex_message);
154 } while (fragment >= ws);
155
156 outchar = static_cast<uint8_t>((fragment & 0x3F) << 2);
157
158 // step 1: get second valid letter. output the first byte.
159 do {
160 if (in == in_end) return out;
161
162 fragment = decoding64[*in++];
163
164 if (fragment == ex && strict)
165 throw std::runtime_error(ex_message);
166 } while (fragment >= ws);
167
168 outchar = static_cast<uint8_t>(outchar | ((fragment & 0x30) >> 4));
169 out += static_cast<char>(outchar);
170
171 outchar = static_cast<uint8_t>((fragment & 0x0F) << 4);
172
173 // step 2: get third valid letter. output the second byte.
174 do {
175 if (in == in_end) return out;
176
177 fragment = decoding64[*in++];
178
179 if (fragment == ex && strict)
180 throw std::runtime_error(ex_message);
181 } while (fragment >= ws);
182
183 outchar = static_cast<uint8_t>(outchar | ((fragment & 0x3C) >> 2));
184 out += static_cast<char>(outchar);
185
186 outchar = static_cast<uint8_t>((fragment & 0x03) << 6);
187
188 // step 3: get fourth valid letter. output the third byte.
189 do {
190 if (in == in_end) return out;
191
192 fragment = decoding64[*in++];
193
194 if (fragment == ex && strict)
195 throw std::runtime_error(ex_message);
196 } while (fragment >= ws);
197
198 outchar = static_cast<uint8_t>(outchar | ((fragment & 0x3F) >> 0));
199 out += static_cast<char>(outchar);
200 }
201}
202
203std::string base64_decode(const std::string& str, bool strict) {
204 return base64_decode(str.data(), str.size(), strict);
205}
206
207} // namespace tlx
208
209/******************************************************************************/
std::string base64_encode(const void *data, size_t size, size_t line_break)
Encode the given binary data into base64 representation as described in RFC 2045 or RFC 3548.
Definition: base64.cpp:25
std::string base64_decode(const void *data, size_t size, bool strict)
Decode a string in base64 representation as described in RFC 2045 or RFC 3548 and return the original...
Definition: base64.cpp:108