2
2
#define JWT_CPP_BASE_H
3
3
4
4
#include < algorithm>
5
- #include < array>
6
5
#include < cstdint>
7
6
#include < stdexcept>
8
7
#include < string>
9
- #include < vector>
8
+
9
+ #include " string_types.h"
10
10
11
11
#ifdef __has_cpp_attribute
12
12
#if __has_cpp_attribute(fallthrough)
18
18
#define JWT_FALLTHROUGH
19
19
#endif
20
20
21
+ #ifndef JWT_HAS_STRING_VIEW
22
+ #include < array>
23
+ #include < cstring>
24
+ #endif
25
+
21
26
namespace jwt {
22
27
/* *
23
28
* \brief character maps when encoding and decoding
@@ -30,19 +35,31 @@ namespace jwt {
30
35
* base64-encoded as per [Section 4 of RFC4648](https://datatracker.ietf.org/doc/html/rfc4648#section-4)
31
36
*/
32
37
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
33
51
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 ;
40
54
}
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 ;
44
59
}
60
+ #endif
45
61
};
62
+
46
63
/* *
47
64
* \brief valid list of character when working with [Base64URL](https://tools.ietf.org/html/rfc4648#section-5)
48
65
*
@@ -53,18 +70,24 @@ namespace jwt {
53
70
* > [Section 5 of RFC 4648 RFC4648](https://tools.ietf.org/html/rfc4648#section-5), with all trailing '=' characters omitted
54
71
*/
55
72
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
56
80
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 ;
63
83
}
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 ;
67
88
}
89
+
90
+ #endif
68
91
};
69
92
namespace helper {
70
93
/* *
@@ -74,26 +97,35 @@ namespace jwt {
74
97
* This is useful in situations outside of JWT encoding/decoding and is provided as a helper
75
98
*/
76
99
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
77
107
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 ;
84
110
}
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 ;
88
115
}
116
+ #endif
89
117
};
90
118
} // namespace helper
91
119
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" ); }
95
127
96
- return std::distance (alphabet. cbegin () , itr);
128
+ return static_cast < uint32_t >( std::distance (alphabetBeg , itr) );
97
129
}
98
130
} // namespace alphabet
99
131
@@ -108,39 +140,44 @@ namespace jwt {
108
140
size_t length = 0 ;
109
141
110
142
padding () = default ;
111
- padding (size_t count, size_t length) : count(count), length(length) {}
112
143
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) { }
114
145
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 }; }
118
147
};
119
148
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
+ }
127
161
}
128
162
}
129
163
130
164
return {};
131
165
}
132
166
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) {
135
168
size_t size = bin.size ();
136
169
std::string res;
137
170
171
+ res.reserve ((4UL * size) / 3UL );
172
+
138
173
// 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 ]);
144
181
145
182
uint32_t triple = (octet_a << 0x10 ) + (octet_b << 0x08 ) + octet_c;
146
183
@@ -152,8 +189,6 @@ namespace jwt {
152
189
153
190
if (fast_size == size) return res;
154
191
155
- size_t mod = size % 3 ;
156
-
157
192
uint32_t octet_a = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
158
193
uint32_t octet_b = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
159
194
uint32_t octet_c = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
@@ -179,9 +214,10 @@ namespace jwt {
179
214
return res;
180
215
}
181
216
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);
185
221
if (pad.count > 2 ) throw std::runtime_error (" Invalid input: too much fill" );
186
222
187
223
const size_t size = base.size () - pad.length ;
@@ -191,7 +227,9 @@ namespace jwt {
191
227
std::string res;
192
228
res.reserve (out_size);
193
229
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
+ };
195
233
196
234
size_t fast_size = size - size % 4 ;
197
235
for (size_t i = 0 ; i < fast_size;) {
@@ -225,46 +263,64 @@ namespace jwt {
225
263
return res;
226
264
}
227
265
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;
239
272
default : break ;
240
273
}
241
-
242
- return base + padding;
274
+ return res;
243
275
}
244
276
245
- inline std::string trim (const std::string& base, const std::string& fill) {
277
+ inline std::string trim (string_view base, string_view fill) {
246
278
auto pos = base.find (fill);
247
- return base.substr (0 , pos);
279
+ return static_cast <std::string>( base.substr (0 , pos) );
248
280
}
249
281
} // namespace details
250
282
283
+ #ifdef JWT_HAS_STRING_VIEW
251
284
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 ] );
254
287
}
255
288
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 ));
258
292
}
259
293
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 ] );
262
296
}
263
297
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 ] );
266
300
}
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
267
321
} // namespace base
268
322
} // namespace jwt
269
323
324
+ #undef JWT_BASE_ALPHABET
325
+
270
326
#endif
0 commit comments