E-MailRelay

gbase64.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gbase64.cpp
19///
20
21#include "gdef.h"
22#include "gbase64.h"
23#include "gstringview.h"
24#include "gstr.h"
25#include <algorithm>
26#include <iterator>
27
28 namespace G
29{
30 namespace Base64Imp
31 {
32 #if defined(G_WINDOWS) && defined(_MSC_VER) && _MSC_VER <= 1929
33 using uint32_type = volatile g_uint32_t ; // volatile as workround for compiler bug: MSVC 2019 16.6.2 /02 /Ob2
34 #else
35 using uint32_type = g_uint32_t ;
36 #endif
37
38 static constexpr std::string_view character_map_with_pad( "=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" , 1+26+26+10+2 ) ;
39 static constexpr std::string_view character_map( character_map_with_pad.data()+1 , character_map_with_pad.size()-1 ) ;
40 static constexpr char pad = '=' ;
41
42 using iterator_in = std::string_view::const_iterator ;
43 using iterator_out = std::back_insert_iterator<std::string> ;
44
45 std::string encode( std::string_view , std::string_view eol ) ;
46 std::string decode( std::string_view , bool do_throw , bool strict ) ;
47 bool valid( std::string_view , bool strict ) ;
48
49 void encode_imp( iterator_out , std::string_view , std::string_view , std::size_t ) ;
50 void decode_imp( iterator_out , std::string_view s , bool & error ) ;
51 void generate_6( uint32_type & n , int & i , iterator_out & ) ;
52 void accumulate_8( uint32_type & n , iterator_in & , iterator_in , int & ) ;
53 void accumulate_6( g_uint32_t & n , iterator_in & , iterator_in , std::size_t & , bool & error ) ;
54 void generate_8( g_uint32_t & n , std::size_t & i , iterator_out & , bool & error ) ;
55 std::size_t index( char c , bool & error ) noexcept ;
56 bool strictlyValid( std::string_view ) noexcept ;
57
58 constexpr char to_char( g_uint32_t n ) noexcept
59 {
60 return static_cast<char>( static_cast<unsigned char>(n) ) ;
61 }
62 constexpr g_uint32_t numeric( char c ) noexcept
63 {
64 return static_cast<g_uint32_t>( static_cast<unsigned char>(c) ) ;
65 }
66 constexpr std::size_t hi_6( g_uint32_t n ) noexcept
67 {
68 return (n >> 18U) & 0x3FU ;
69 }
70 constexpr g_uint32_t hi_8( g_uint32_t n ) noexcept
71 {
72 return (n >> 16U) & 0xFFU ;
73 }
74 }
75}
76
77// ==
78
79std::string G::Base64::encode( std::string_view s , std::string_view eol )
80{
81 return Base64Imp::encode( s , eol ) ;
82}
83
84std::string G::Base64::decode( std::string_view s , bool do_throw , bool strict )
85{
86 return Base64Imp::decode( s , do_throw , strict ) ;
87}
88
89 bool G::Base64::valid( std::string_view s , bool strict )
90{
91 return Base64Imp::valid( s , strict ) ;
92}
93
94// ==
95
96std::string G::Base64Imp::encode( std::string_view input , std::string_view eol )
97{
98 std::string result ;
99 result.reserve( input.size() + input.size()/2U ) ;
100 encode_imp( std::back_inserter(result) , input , eol , 19U ) ;
101 return result ;
102}
103
104 void G::Base64Imp::encode_imp( iterator_out result_p , std::string_view input , std::string_view eol , std::size_t blocks_per_line )
105{
106 std::size_t blocks = 0U ;
107 auto const end = input.end() ;
108 for( auto p = input.begin() ; p != end ; blocks++ )
109 {
110 if( !eol.empty() && blocks != 0U && (blocks % blocks_per_line) == 0U )
111 std::copy( eol.begin() , eol.end() , result_p ) ;
112
113 uint32_type n = 0UL ;
114 int i = 0 ;
115 accumulate_8( n , p , end , i ) ;
116 accumulate_8( n , p , end , i ) ;
117 accumulate_8( n , p , end , i ) ;
118 generate_6( n , i , result_p ) ;
119 generate_6( n , i , result_p ) ;
120 generate_6( n , i , result_p ) ;
121 generate_6( n , i , result_p ) ;
122 }
123}
124
125std::string G::Base64Imp::decode( std::string_view input , bool do_throw , bool strict )
126{
127 bool error = false ;
128 if( strict && !strictlyValid(input) )
129 error = true ;
130
131 std::string result ;
132 result.reserve( input.size() ) ;
133 decode_imp( std::back_inserter(result) , input , error ) ;
134
135 if( error )
136 result.clear() ;
137 if( error && do_throw )
138 throw Base64::Error() ;
139
140 return result ;
141}
142
143 void G::Base64Imp::decode_imp( iterator_out result_p , std::string_view s , bool & error )
144{
145 auto const end = s.end() ;
146 for( auto p = s.begin() ; p != end ; )
147 {
148 if( *p == '\r' || *p == '\n' || *p == ' ' )
149 {
150 ++p ;
151 continue ;
152 }
153
154 // four input characters encode 4*6 bits, so three output bytes
155 g_uint32_t n = 0UL ; // up to 24 bits
156 std::size_t bits = 0U ;
157 accumulate_6( n , p , end , bits , error ) ;
158 accumulate_6( n , p , end , bits , error ) ;
159 accumulate_6( n , p , end , bits , error ) ;
160 accumulate_6( n , p , end , bits , error ) ;
161 if( bits < 8U ) error = true ; // 6 bits cannot make a byte
162 generate_8( n , bits , result_p , error ) ;
163 generate_8( n , bits , result_p , error ) ;
164 generate_8( n , bits , result_p , error ) ;
165 }
166}
167
168 bool G::Base64Imp::valid( std::string_view input , bool strict )
169{
170 if( strict && !strictlyValid(input) )
171 return false ;
172
173 bool error = false ;
174 std::string result ;
175 result.reserve( input.size() ) ;
176 decode_imp( std::back_inserter(result) , input , error ) ;
177 return !error ;
178}
179
180 bool G::Base64Imp::strictlyValid( std::string_view s ) noexcept
181{
182 if( s.empty() )
183 return true ;
184
185 if( s.size() == 1 )
186 return false ; // 6 bits cannot make a byte
187
188 if( std::string::npos == s.find_first_not_of(character_map) )
189 return true ;
190
191 if( std::string::npos != s.find_first_not_of(character_map_with_pad) )
192 return false ;
193
194 std::size_t pos = s.find( pad ) ;
195 if( (pos+1U) == s.size() && s[pos] == pad && (s.size()&3U) == 0U )
196 return true ;
197
198 if( (pos+2U) == s.size() && s[pos] == pad && s[pos+1U] == pad && (s.size()&3U) == 0U )
199 return true ;
200
201 return false ;
202}
203
204 void G::Base64Imp::accumulate_8( uint32_type & n , iterator_in & p , iterator_in end , int & i )
205{
206 char c = p == end ? '\0' : *p ;
207 n <<= 8U ;
208 n |= numeric(c) ;
209 if( p != end )
210 {
211 ++p ;
212 ++i ;
213 }
214}
215
216 void G::Base64Imp::generate_6( uint32_type & n , int & i , iterator_out & result )
217{
218 size_t index = hi_6( n ) ;
219 char c = i-- >= 0 ? character_map[index] : pad ;
220 *result++ = c ;
221 n <<= 6U ;
222}
223
224 void G::Base64Imp::accumulate_6( g_uint32_t & n , iterator_in & p , iterator_in end ,
225 std::size_t & bits , bool & error )
226{
227 n <<= 6U ;
228 if( p == end )
229 {
230 }
231 else if( *p == pad )
232 {
233 ++p ;
234 }
235 else
236 {
237 n |= index( *p++ , error ) ;
238 bits += 6U ;
239 }
240}
241
242 void G::Base64Imp::generate_8( g_uint32_t & n , std::size_t & bits , iterator_out & result , bool & error )
243{
244 if( bits >= 8U )
245 {
246 bits -= 8U ;
247 *result++ = to_char(hi_8(n)) ;
248 n <<= 8U ;
249 }
250 else if( hi_8(n) != 0U )
251 {
252 error = true ;
253 }
254}
255
256std::size_t G::Base64Imp::index( char c , bool & error ) noexcept
257{
258 std::size_t pos = character_map.find( c ) ;
259 error = error || (c=='\0') || pos == std::string::npos ;
260 return pos == std::string::npos ? std::size_t(0) : pos ;
261}
262
G::Base64::encode
static std::string encode(std::string_view, std::string_view line_break={})
Encodes the given string, optionally inserting line-breaks to limit the line length.
Definition: gbase64.cpp:79
G::Base64::decode
static std::string decode(std::string_view, bool throw_on_invalid=false, bool strict=true)
Decodes the given string.
Definition: gbase64.cpp:84
G::Base64::valid
static bool valid(std::string_view, bool strict=true)
Returns true if the string is a valid base64 encoding, possibly allowing for embedded newlines,...
Definition: gbase64.cpp:89
gbase64.h
gstr.h
gstringview.h
G
Low-level classes.
Definition: garg.h:36