RESTinio
base64.hpp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
9#pragma once
10
12#include <restinio/expected.hpp>
13
15
17
18#include <string>
19#include <array>
20#include <exception>
21#include <iostream> // std::cout, debug
22
23namespace restinio
24{
25
26namespace utils
27{
28
29namespace base64
30{
31
32#include "base64_lut.ipp"
33
34using uint_type_t = std::uint_fast32_t;
35
36inline bool
37is_base64_char( char c ) noexcept
38{
39 return 1 == is_base64_char_lut< unsigned char >()[
40 static_cast<unsigned char>(c) ];
41}
42
43inline bool
45{
46 enum class expected_type { b64ch, b64ch_or_padding, padding };
47
48 if( str.size() < 4u )
49 return false;
50
51 expected_type expects = expected_type::b64ch;
52 std::uint_fast8_t b64chars_found = 0u; // Can't be greater than 2.
53 std::uint_fast8_t paddings_found = 0u; // Can't be greater than 3.
54 for( const auto ch : str )
55 {
56 switch( expects )
57 {
58 case expected_type::b64ch:
59 {
60 // Because '=' is a part of base64_chars, it should be checked
61 // individually.
62 if( '=' == ch )
63 return false;
64 else if( is_base64_char( ch ) )
65 {
66 ++b64chars_found;
67 if( b64chars_found >= 2u )
68 expects = expected_type::b64ch_or_padding;
69 }
70 else
71 return false;
72 break;
73 }
74 case expected_type::b64ch_or_padding:
75 {
76 if( '=' == ch )
77 {
78 expects = expected_type::padding;
79 ++paddings_found;
80 }
81 else if( is_base64_char( ch ) )
82 {
83 /* Nothing to do */
84 }
85 else
86 return false;
87
88 break;
89 }
90 case expected_type::padding:
91 {
92 if( '=' == ch )
93 {
94 ++paddings_found;
95 if( paddings_found > 2u )
96 return false;
97 }
98 else
99 return false;
100
101 break;
102 }
103 }
104 }
105
106 return true;
107}
108
109inline uint_type_t
110uch( char ch )
111{
112 return static_cast<uint_type_t>(static_cast<unsigned char>(ch));
113}
114
115template<unsigned int Shift>
116char
118{
119 return ::restinio::utils::impl::bitops::n_bits_from< char, Shift, 6 >(bs);
120}
121
122inline std::string
124{
125 std::string result;
126
127 const auto at = [&str](auto index) { return uch(str[index]); };
128
129 const auto alphabet_char = [](auto ch) {
130 return static_cast<char>(
131 base64_alphabet< unsigned char >()[
132 static_cast<unsigned char>(ch) ]);
133 };
134
135 constexpr std::size_t group_size = 3u;
136 const auto remaining = str.size() % group_size;
137
138 result.reserve( (str.size()/group_size + (remaining ? 1:0)) * 4 );
139
140 std::size_t i = 0;
141 for(; i < str.size() - remaining; i += group_size )
142 {
143 uint_type_t bs = (at(i) << 16) | (at(i+1) << 8) | at(i+2);
144
145 result.push_back( alphabet_char( sixbits_char<18>(bs) ) );
146 result.push_back( alphabet_char( sixbits_char<12>(bs) ) );
147 result.push_back( alphabet_char( sixbits_char<6>(bs) ) );
148 result.push_back( alphabet_char( sixbits_char<0>(bs) ) );
149 }
150
151 if( remaining )
152 {
153 // Some code duplication to avoid additional IFs.
154 if( 1u == remaining )
155 {
156 uint_type_t bs = (at(i) << 16);
157 result.push_back( alphabet_char( sixbits_char<18>(bs) ) );
158 result.push_back( alphabet_char( sixbits_char<12>(bs) ) );
159
160 result.push_back('=');
161 }
162 else
163 {
164 uint_type_t bs = (at(i) << 16) | (at(i+1) << 8);
165
166 result.push_back( alphabet_char( sixbits_char<18>(bs) ) );
167 result.push_back( alphabet_char( sixbits_char<12>(bs) ) );
168 result.push_back( alphabet_char( sixbits_char<6>(bs) ) );
169 }
170
171 result.push_back('=');
172 }
173
174 return result;
175}
176
179{
181};
182
185{
186 if( !is_valid_base64_string( str ) )
187 return make_unexpected( decoding_error_t::invalid_base64_sequence );
188
189 constexpr std::size_t group_size = 4;
190
191 std::string result;
192 result.reserve( (str.size() / group_size) * 3 );
193
194 const unsigned char * const decode_table = base64_decode_lut< unsigned char >();
195
196 const auto at = [&str](auto index) {
197 return static_cast<unsigned char>(str[index]);
198 };
199
200 for( size_t i = 0 ; i < str.size(); i += group_size)
201 {
202
203 uint_type_t bs{};
204 int paddings_found = 0u;
205
206 bs |= decode_table[ at(i) ];
207
208 bs <<= 6;
209 bs |= decode_table[ at(i+1) ];
210
211 bs <<= 6;
212 if( '=' == str[i+2] )
213 {
214 ++paddings_found;
215 }
216 else
217 {
218 bs |= decode_table[ at(i+2) ];
219 }
220
221 bs <<= 6;
222 if( '=' == str[i+3] )
223 {
224 ++paddings_found;
225 }
226 else
227 {
228 bs |= decode_table[ at(i+3) ];
229 }
230
232
233 result.push_back( n_bits_from< char, 16 >(bs) );
234 if( paddings_found < 2 )
235 {
236 result.push_back( n_bits_from< char, 8 >(bs) );
237 }
238 if( paddings_found < 1 )
239 {
240 result.push_back( n_bits_from< char, 0 >(bs) );
241 }
242 }
243
244 return result;
245}
246
247namespace impl
248{
249
250inline void
252{
253 constexpr size_t max_allowed_len = 32u;
254 // If str is too long only a part of it will be included
255 // in the error message.
256 if( str.size() > max_allowed_len )
257 throw exception_t{
258 fmt::format( "invalid base64 string that starts with '{}'",
259 str.substr( 0u, max_allowed_len ) )
260 };
261 else
262 throw exception_t{
263 fmt::format( "invalid base64 string '{}'", str ) };
264}
265
266} /* namespace impl */
267
268inline std::string
270{
271 auto result = try_decode( str );
272 if( !result )
274
275 return std::move( *result );
276}
277
278} /* namespace base64 */
279
280} /* namespace utils */
281
282} /* namespace restinio */
283
Exception class for all exceptions thrown by RESTinio.
Definition: exception.hpp:26
A special wrapper around fmtlib include files.
void throw_exception_on_invalid_base64_string(string_view_t str)
Definition: base64.hpp:251
char sixbits_char(uint_type_t bs)
Definition: base64.hpp:117
std::uint_fast32_t uint_type_t
Definition: base64.hpp:34
bool is_base64_char(char c) noexcept
Definition: base64.hpp:37
std::string decode(string_view_t str)
Definition: base64.hpp:269
bool is_valid_base64_string(string_view_t str) noexcept
Definition: base64.hpp:44
expected_t< std::string, decoding_error_t > try_decode(string_view_t str)
Definition: base64.hpp:184
decoding_error_t
Description of base64 decode error.
Definition: base64.hpp:179
uint_type_t uch(char ch)
Definition: base64.hpp:110
std::string encode(string_view_t str)
Definition: base64.hpp:123
T n_bits_from(F value)
Extract N bits from a bigger integer value.
Definition: bitops.hpp:86
nonstd::string_view string_view_t
Definition: string_view.hpp:19
nonstd::expected< T, E > expected_t
Definition: expected.hpp:22