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