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,34 @@ 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+ inline uint32_t index (const char * alphabetBeg, const char * alphabetEnd, char symbol) {
121+ if (symbol >= ' A' && symbol <= ' Z' ) { return static_cast <uint32_t >(symbol - ' A' ); }
122+ if (symbol >= ' a' && symbol <= ' z' ) { return static_cast <uint32_t >(26 + symbol - ' a' ); }
123+ if (symbol >= ' 0' && symbol <= ' 9' ) { return static_cast <uint32_t >(52 + symbol - ' 0' ); }
124+ auto itr = std::find (std::next (alphabetBeg, 62U ), alphabetEnd, symbol);
125+ if (itr == alphabetEnd) { throw std::runtime_error (" Invalid input: not within alphabet" ); }
95126
96- return std::distance (alphabet. cbegin () , itr);
127+ return static_cast < uint32_t >( std::distance (alphabetBeg , itr) );
97128 }
98129 } // namespace alphabet
99130
@@ -108,39 +139,48 @@ namespace jwt {
108139 size_t length = 0 ;
109140
110141 padding () = default ;
111- padding (size_t count, size_t length) : count(count), length(length) {}
112142
113- padding operator +(const padding& p) { return padding (count + p.count , length + p.length ); }
143+ padding (size_t c, size_t l) : count(c), length(l) {}
144+
145+ padding operator +(const padding& p) const { return padding{count + p.count , length + p.length }; }
114146
115147 friend bool operator ==(const padding& lhs, const padding& rhs) {
116148 return lhs.count == rhs.count && lhs.length == rhs.length ;
117149 }
118150 };
119151
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);
152+ inline std::size_t string_len (string_view str) { return str.size (); }
153+
154+ template <class str_input_it >
155+ padding count_padding (string_view base, str_input_it fillStart, str_input_it fillEnd) {
156+ for (str_input_it fillIt = fillStart; fillIt != fillEnd; ++fillIt) {
157+ std::size_t fillLen = string_len (*fillIt);
158+ if (base.size () >= fillLen) {
159+ std::size_t deltaLen = base.size () - fillLen;
160+ // Does the end of the input exactly match the fill pattern?
161+ if (base.substr (deltaLen) == *fillIt) {
162+ return padding{1UL , fillLen} + count_padding (base.substr (0 , deltaLen), fillStart, fillEnd);
163+ }
127164 }
128165 }
129166
130167 return {};
131168 }
132169
133- inline std::string encode (const std::string& bin, const std::array<char , 64 >& alphabet,
134- const std::string& fill) {
170+ inline std::string encode (string_view bin, const char * alphabet, string_view fill) {
135171 size_t size = bin.size ();
136172 std::string res;
137173
174+ res.reserve ((4UL * size) / 3UL );
175+
138176 // 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++]);
177+ size_t mod = size % 3 ;
178+
179+ size_t fast_size = size - mod;
180+ for (size_t i = 0 ; i < fast_size; i += 3 ) {
181+ uint32_t octet_a = static_cast <unsigned char >(bin[i]);
182+ uint32_t octet_b = static_cast <unsigned char >(bin[i + 1 ]);
183+ uint32_t octet_c = static_cast <unsigned char >(bin[i + 2 ]);
144184
145185 uint32_t triple = (octet_a << 0x10 ) + (octet_b << 0x08 ) + octet_c;
146186
@@ -152,8 +192,6 @@ namespace jwt {
152192
153193 if (fast_size == size) return res;
154194
155- size_t mod = size % 3 ;
156-
157195 uint32_t octet_a = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
158196 uint32_t octet_b = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
159197 uint32_t octet_c = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
@@ -179,9 +217,10 @@ namespace jwt {
179217 return res;
180218 }
181219
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);
220+ template <class str_input_it >
221+ inline std::string decode (string_view base, const char * alphabetBeg, const char * alphabetEnd,
222+ str_input_it fillStart, str_input_it fillEnd) {
223+ const auto pad = count_padding (base, fillStart, fillEnd);
185224 if (pad.count > 2 ) throw std::runtime_error (" Invalid input: too much fill" );
186225
187226 const size_t size = base.size () - pad.length ;
@@ -191,7 +230,9 @@ namespace jwt {
191230 std::string res;
192231 res.reserve (out_size);
193232
194- auto get_sextet = [&](size_t offset) { return alphabet::index (alphabet, base[offset]); };
233+ auto get_sextet = [&](size_t offset) {
234+ return alphabet::index (alphabetBeg, alphabetEnd, base[offset]);
235+ };
195236
196237 size_t fast_size = size - size % 4 ;
197238 for (size_t i = 0 ; i < fast_size;) {
@@ -225,46 +266,64 @@ namespace jwt {
225266 return res;
226267 }
227268
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;
269+ inline std::string pad (string_view base, string_view fill) {
270+ std::string res (base);
271+ switch (res.size () % 4 ) {
272+ case 1 : res += fill; JWT_FALLTHROUGH;
273+ case 2 : res += fill; JWT_FALLTHROUGH;
274+ case 3 : res += fill; JWT_FALLTHROUGH;
239275 default : break ;
240276 }
241-
242- return base + padding;
277+ return res;
243278 }
244279
245- inline std::string trim (const std::string& base, const std::string& fill) {
280+ inline std::string trim (string_view base, string_view fill) {
246281 auto pos = base.find (fill);
247- return base.substr (0 , pos);
282+ return static_cast <std::string>( base.substr (0 , pos) );
248283 }
249284 } // namespace details
250285
286+ #ifdef JWT_HAS_STRING_VIEW
251287 template <typename T>
252- std::string encode (const std::string& bin) {
253- return details::encode (bin, T::data () , T::fill () );
288+ std::string encode (string_view bin) {
289+ return details::encode (bin, T::kData , T::kFill [ 0 ] );
254290 }
255291 template <typename T>
256- std::string decode (const std::string& base) {
257- return details::decode (base, T::data (), T::fill ());
292+ std::string decode (string_view base) {
293+ return details::decode (base, std::begin (T::kData ), std::end (T::kData ), std::begin (T::kFill ),
294+ std::end (T::kFill ));
258295 }
259296 template <typename T>
260- std::string pad (const std::string& base) {
261- return details::pad (base, T::fill () );
297+ std::string pad (string_view base) {
298+ return details::pad (base, T::kFill [ 0 ] );
262299 }
263300 template <typename T>
264- std::string trim (const std::string& base) {
265- return details::trim (base, T::fill () );
301+ std::string trim (string_view base) {
302+ return details::trim (base, T::kFill [ 0 ] );
266303 }
304+
305+ #else
306+ template <typename T>
307+ std::string encode (string_view bin) {
308+ return details::encode (bin, T::data ().data (), T::fill ()[0 ]);
309+ }
310+ template <typename T>
311+ std::string decode (string_view base) {
312+ return details::decode (base, std::begin (T::data ()), std::end (T::data ()), std::begin (T::fill ()),
313+ std::end (T::fill ()));
314+ }
315+ template <typename T>
316+ std::string pad (string_view base) {
317+ return details::pad (base, T::fill ()[0 ]);
318+ }
319+ template <typename T>
320+ std::string trim (string_view base) {
321+ return details::trim (base, T::fill ()[0 ]);
322+ }
323+ #endif
267324 } // namespace base
268325} // namespace jwt
269326
327+ #undef JWT_BASE_ALPHABET
328+
270329#endif
0 commit comments