22#define JWT_CPP_BASE_H
33
44#include < algorithm>
5- #include < array>
65#include < cstdint>
76#include < stdexcept>
87#include < string>
9- #include < vector>
8+
9+ #include " string_types.h"
1010
1111#ifdef __has_cpp_attribute
1212#if __has_cpp_attribute(fallthrough)
1818#define JWT_FALLTHROUGH
1919#endif
2020
21+ #ifndef JWT_HAS_STRING_VIEW
22+ #include < array>
23+ #include < cstring>
24+ #endif
25+
2126namespace jwt {
2227 /* *
2328 * \brief character maps when encoding and decoding
@@ -30,19 +35,31 @@ namespace jwt {
3035 * base64-encoded as per [Section 4 of RFC4648](https://datatracker.ietf.org/doc/html/rfc4648#section-4)
3136 */
3237 struct base64 {
38+
39+ #define JWT_BASE_ALPHABET \
40+ ' A' , ' B' , ' C' , ' D' , ' E' , ' F' , ' G' , ' H' , ' I' , ' J' , ' K' , ' L' , ' M' , ' N' , ' O' , ' P' , ' Q' , ' R' , ' S' , ' T' , ' U' , ' V' , ' W' , \
41+ ' X' , ' Y' , ' Z' , ' a' , ' b' , ' c' , ' d' , ' e' , ' f' , ' g' , ' h' , ' i' , ' j' , ' k' , ' l' , ' m' , ' n' , ' o' , ' p' , ' q' , ' r' , ' s' , \
42+ ' t' , ' u' , ' v' , ' w' , ' x' , ' y' , ' z' , ' 0' , ' 1' , ' 2' , ' 3' , ' 4' , ' 5' , ' 6' , ' 7' , ' 8' , ' 9'
43+
44+ #ifdef JWT_HAS_STRING_VIEW
45+ // From C++17 it's perfectly fine to have inline static variables. No ODR violation in this case.
46+ static constexpr char kData []{JWT_BASE_ALPHABET, ' +' , ' /' };
47+
48+ static constexpr std::string_view kFill []{" =" };
49+ #else
50+ // For pre C++17 standards, we need to use a method
3351 static const std::array<char , 64 >& data () {
34- static constexpr std::array<char , 64 > data{
35- {' A' , ' B' , ' C' , ' D' , ' E' , ' F' , ' G' , ' H' , ' I' , ' J' , ' K' , ' L' , ' M' , ' N' , ' O' , ' P' ,
36- ' Q' , ' R' , ' S' , ' T' , ' U' , ' V' , ' W' , ' X' , ' Y' , ' Z' , ' a' , ' b' , ' c' , ' d' , ' e' , ' f' ,
37- ' g' , ' h' , ' i' , ' j' , ' k' , ' l' , ' m' , ' n' , ' o' , ' p' , ' q' , ' r' , ' s' , ' t' , ' u' , ' v' ,
38- ' w' , ' x' , ' y' , ' z' , ' 0' , ' 1' , ' 2' , ' 3' , ' 4' , ' 5' , ' 6' , ' 7' , ' 8' , ' 9' , ' +' , ' /' }};
39- return data;
52+ static constexpr std::array<char , 64 > kData {{JWT_BASE_ALPHABET, ' +' , ' /' }};
53+ return kData ;
4054 }
41- static const std::string& fill () {
42- static std::string fill{" =" };
43- return fill;
55+
56+ static const std::array<const char *, 1 >& fill () {
57+ static constexpr std::array<const char *, 1 > kFill {" =" };
58+ return kFill ;
4459 }
60+ #endif
4561 };
62+
4663 /* *
4764 * \brief valid list of character when working with [Base64URL](https://tools.ietf.org/html/rfc4648#section-5)
4865 *
@@ -53,18 +70,24 @@ namespace jwt {
5370 * > [Section 5 of RFC 4648 RFC4648](https://tools.ietf.org/html/rfc4648#section-5), with all trailing '=' characters omitted
5471 */
5572 struct base64url {
73+
74+ #ifdef JWT_HAS_STRING_VIEW
75+ static constexpr char kData []{JWT_BASE_ALPHABET, ' -' , ' _' };
76+
77+ static constexpr std::string_view kFill []{" %3d" };
78+ #else
79+ // For pre C++17 standards, we need to use a method
5680 static const std::array<char , 64 >& data () {
57- static constexpr std::array<char , 64 > data{
58- {' A' , ' B' , ' C' , ' D' , ' E' , ' F' , ' G' , ' H' , ' I' , ' J' , ' K' , ' L' , ' M' , ' N' , ' O' , ' P' ,
59- ' Q' , ' R' , ' S' , ' T' , ' U' , ' V' , ' W' , ' X' , ' Y' , ' Z' , ' a' , ' b' , ' c' , ' d' , ' e' , ' f' ,
60- ' g' , ' h' , ' i' , ' j' , ' k' , ' l' , ' m' , ' n' , ' o' , ' p' , ' q' , ' r' , ' s' , ' t' , ' u' , ' v' ,
61- ' w' , ' x' , ' y' , ' z' , ' 0' , ' 1' , ' 2' , ' 3' , ' 4' , ' 5' , ' 6' , ' 7' , ' 8' , ' 9' , ' -' , ' _' }};
62- return data;
81+ static constexpr std::array<char , 64 > kData {{JWT_BASE_ALPHABET, ' -' , ' _' }};
82+ return kData ;
6383 }
64- static const std::string& fill () {
65- static std::string fill{" %3d" };
66- return fill;
84+
85+ static const std::array<const char *, 1 >& fill () {
86+ static constexpr std::array<const char *, 1 > kFill {" %3d" };
87+ return kFill ;
6788 }
89+
90+ #endif
6891 };
6992 namespace helper {
7093 /* *
@@ -74,26 +97,35 @@ namespace jwt {
7497 * This is useful in situations outside of JWT encoding/decoding and is provided as a helper
7598 */
7699 struct base64url_percent_encoding {
100+
101+ #ifdef JWT_HAS_STRING_VIEW
102+ static constexpr char kData []{JWT_BASE_ALPHABET, ' -' , ' _' };
103+
104+ static constexpr std::string_view kFill []{" %3D" , " %3d" };
105+ #else
106+ // For pre C++17 standards, we need to use a method
77107 static const std::array<char , 64 >& data () {
78- static constexpr std::array<char , 64 > data{
79- {' A' , ' B' , ' C' , ' D' , ' E' , ' F' , ' G' , ' H' , ' I' , ' J' , ' K' , ' L' , ' M' , ' N' , ' O' , ' P' ,
80- ' Q' , ' R' , ' S' , ' T' , ' U' , ' V' , ' W' , ' X' , ' Y' , ' Z' , ' a' , ' b' , ' c' , ' d' , ' e' , ' f' ,
81- ' g' , ' h' , ' i' , ' j' , ' k' , ' l' , ' m' , ' n' , ' o' , ' p' , ' q' , ' r' , ' s' , ' t' , ' u' , ' v' ,
82- ' w' , ' x' , ' y' , ' z' , ' 0' , ' 1' , ' 2' , ' 3' , ' 4' , ' 5' , ' 6' , ' 7' , ' 8' , ' 9' , ' -' , ' _' }};
83- return data;
108+ static constexpr std::array<char , 64 > kData {{JWT_BASE_ALPHABET, ' -' , ' _' }};
109+ return kData ;
84110 }
85- static const std::initializer_list<std::string>& fill () {
86- static std::initializer_list<std::string> fill{" %3D" , " %3d" };
87- return fill;
111+
112+ static const std::array<const char *, 2 >& fill () {
113+ static constexpr std::array<const char *, 2 > kFill {" %3D" , " %3d" };
114+ return kFill ;
88115 }
116+ #endif
89117 };
90118 } // namespace helper
91119
92- inline uint32_t index (const std::array<char , 64 >& alphabet, char symbol) {
93- auto itr = std::find_if (alphabet.cbegin (), alphabet.cend (), [symbol](char c) { return c == symbol; });
94- if (itr == alphabet.cend ()) { throw std::runtime_error (" Invalid input: not within alphabet" ); }
120+ template <class char_it >
121+ inline uint32_t index (char_it alphabetBeg, char_it alphabetEnd, char symbol) {
122+ if (symbol >= ' A' && symbol <= ' Z' ) { return static_cast <uint32_t >(symbol - ' A' ); }
123+ if (symbol >= ' a' && symbol <= ' z' ) { return static_cast <uint32_t >(26 + symbol - ' a' ); }
124+ if (symbol >= ' 0' && symbol <= ' 9' ) { return static_cast <uint32_t >(52 + symbol - ' 0' ); }
125+ auto itr = std::find (std::next (alphabetBeg, 62U ), alphabetEnd, symbol);
126+ if (itr == alphabetEnd) { throw std::runtime_error (" Invalid input: not within alphabet" ); }
95127
96- return std::distance (alphabet. cbegin () , itr);
128+ return static_cast < uint32_t >( std::distance (alphabetBeg , itr) );
97129 }
98130 } // namespace alphabet
99131
@@ -108,39 +140,44 @@ namespace jwt {
108140 size_t length = 0 ;
109141
110142 padding () = default ;
111- padding (size_t count, size_t length) : count(count), length(length) {}
112143
113- padding operator +( const padding& p) { return padding ( count + p. count , length + p. length ); }
144+ padding ( size_t c, size_t l) : count(c) , length(l) { }
114145
115- friend bool operator ==(const padding& lhs, const padding& rhs) {
116- return lhs.count == rhs.count && lhs.length == rhs.length ;
117- }
146+ padding operator +(const padding& p) const { return padding{count + p.count , length + p.length }; }
118147 };
119148
120- inline padding count_padding (const std::string& base, const std::vector<std::string>& fills) {
121- for (const auto & fill : fills) {
122- if (base.size () < fill.size ()) continue ;
123- // Does the end of the input exactly match the fill pattern?
124- if (base.substr (base.size () - fill.size ()) == fill) {
125- return padding{1 , fill.length ()} +
126- count_padding (base.substr (0 , base.size () - fill.size ()), fills);
149+ inline std::size_t string_len (string_view str) { return str.size (); }
150+
151+ template <class str_input_it >
152+ padding count_padding (string_view base, str_input_it fillStart, str_input_it fillEnd) {
153+ for (str_input_it fillIt = fillStart; fillIt != fillEnd; ++fillIt) {
154+ std::size_t fillLen = string_len (*fillIt);
155+ if (base.size () >= fillLen) {
156+ std::size_t deltaLen = base.size () - fillLen;
157+ // Does the end of the input exactly match the fill pattern?
158+ if (base.substr (deltaLen) == *fillIt) {
159+ return padding{1UL , fillLen} + count_padding (base.substr (0 , deltaLen), fillStart, fillEnd);
160+ }
127161 }
128162 }
129163
130164 return {};
131165 }
132166
133- inline std::string encode (const std::string& bin, const std::array<char , 64 >& alphabet,
134- const std::string& fill) {
167+ inline std::string encode (string_view bin, const char * alphabet, string_view fill) {
135168 size_t size = bin.size ();
136169 std::string res;
137170
171+ res.reserve ((4UL * size) / 3UL );
172+
138173 // clear incomplete bytes
139- size_t fast_size = size - size % 3 ;
140- for (size_t i = 0 ; i < fast_size;) {
141- uint32_t octet_a = static_cast <unsigned char >(bin[i++]);
142- uint32_t octet_b = static_cast <unsigned char >(bin[i++]);
143- uint32_t octet_c = static_cast <unsigned char >(bin[i++]);
174+ size_t mod = size % 3 ;
175+
176+ size_t fast_size = size - mod;
177+ for (size_t i = 0 ; i < fast_size; i += 3 ) {
178+ uint32_t octet_a = static_cast <unsigned char >(bin[i]);
179+ uint32_t octet_b = static_cast <unsigned char >(bin[i + 1 ]);
180+ uint32_t octet_c = static_cast <unsigned char >(bin[i + 2 ]);
144181
145182 uint32_t triple = (octet_a << 0x10 ) + (octet_b << 0x08 ) + octet_c;
146183
@@ -152,8 +189,6 @@ namespace jwt {
152189
153190 if (fast_size == size) return res;
154191
155- size_t mod = size % 3 ;
156-
157192 uint32_t octet_a = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
158193 uint32_t octet_b = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
159194 uint32_t octet_c = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
@@ -179,9 +214,10 @@ namespace jwt {
179214 return res;
180215 }
181216
182- inline std::string decode (const std::string& base, const std::array<char , 64 >& alphabet,
183- const std::vector<std::string>& fill) {
184- const auto pad = count_padding (base, fill);
217+ template <class char_it , class str_input_it >
218+ inline std::string decode (string_view base, char_it alphabetBeg, char_it alphabetEnd,
219+ str_input_it fillStart, str_input_it fillEnd) {
220+ const auto pad = count_padding (base, fillStart, fillEnd);
185221 if (pad.count > 2 ) throw std::runtime_error (" Invalid input: too much fill" );
186222
187223 const size_t size = base.size () - pad.length ;
@@ -191,7 +227,9 @@ namespace jwt {
191227 std::string res;
192228 res.reserve (out_size);
193229
194- auto get_sextet = [&](size_t offset) { return alphabet::index (alphabet, base[offset]); };
230+ auto get_sextet = [&](size_t offset) {
231+ return alphabet::index (alphabetBeg, alphabetEnd, base[offset]);
232+ };
195233
196234 size_t fast_size = size - size % 4 ;
197235 for (size_t i = 0 ; i < fast_size;) {
@@ -225,46 +263,64 @@ namespace jwt {
225263 return res;
226264 }
227265
228- inline std::string decode (const std::string& base, const std::array<char , 64 >& alphabet,
229- const std::string& fill) {
230- return decode (base, alphabet, std::vector<std::string>{fill});
231- }
232-
233- inline std::string pad (const std::string& base, const std::string& fill) {
234- std::string padding;
235- switch (base.size () % 4 ) {
236- case 1 : padding += fill; JWT_FALLTHROUGH;
237- case 2 : padding += fill; JWT_FALLTHROUGH;
238- case 3 : padding += fill; JWT_FALLTHROUGH;
266+ inline std::string pad (string_view base, string_view fill) {
267+ std::string res (base);
268+ switch (res.size () % 4 ) {
269+ case 1 : res += fill; JWT_FALLTHROUGH;
270+ case 2 : res += fill; JWT_FALLTHROUGH;
271+ case 3 : res += fill; JWT_FALLTHROUGH;
239272 default : break ;
240273 }
241-
242- return base + padding;
274+ return res;
243275 }
244276
245- inline std::string trim (const std::string& base, const std::string& fill) {
277+ inline std::string trim (string_view base, string_view fill) {
246278 auto pos = base.find (fill);
247- return base.substr (0 , pos);
279+ return static_cast <std::string>( base.substr (0 , pos) );
248280 }
249281 } // namespace details
250282
283+ #ifdef JWT_HAS_STRING_VIEW
251284 template <typename T>
252- std::string encode (const std::string& bin) {
253- return details::encode (bin, T::data () , T::fill () );
285+ std::string encode (string_view bin) {
286+ return details::encode (bin, T::kData , T::kFill [ 0 ] );
254287 }
255288 template <typename T>
256- std::string decode (const std::string& base) {
257- return details::decode (base, T::data (), T::fill ());
289+ std::string decode (string_view base) {
290+ return details::decode (base, std::begin (T::kData ), std::end (T::kData ), std::begin (T::kFill ),
291+ std::end (T::kFill ));
258292 }
259293 template <typename T>
260- std::string pad (const std::string& base) {
261- return details::pad (base, T::fill () );
294+ std::string pad (string_view base) {
295+ return details::pad (base, T::kFill [ 0 ] );
262296 }
263297 template <typename T>
264- std::string trim (const std::string& base) {
265- return details::trim (base, T::fill () );
298+ std::string trim (string_view base) {
299+ return details::trim (base, T::kFill [ 0 ] );
266300 }
301+
302+ #else
303+ template <typename T>
304+ std::string encode (string_view bin) {
305+ return details::encode (bin, T::data ().data (), T::fill ()[0 ]);
306+ }
307+ template <typename T>
308+ std::string decode (string_view base) {
309+ return details::decode (base, std::begin (T::data ()), std::end (T::data ()), std::begin (T::fill ()),
310+ std::end (T::fill ()));
311+ }
312+ template <typename T>
313+ std::string pad (string_view base) {
314+ return details::pad (base, T::fill ()[0 ]);
315+ }
316+ template <typename T>
317+ std::string trim (string_view base) {
318+ return details::trim (base, T::fill ()[0 ]);
319+ }
320+ #endif
267321 } // namespace base
268322} // namespace jwt
269323
324+ #undef JWT_BASE_ALPHABET
325+
270326#endif
0 commit comments