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,34 @@ 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
+ 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" ); }
95
126
96
- return std::distance (alphabet. cbegin () , itr);
127
+ return static_cast < uint32_t >( std::distance (alphabetBeg , itr) );
97
128
}
98
129
} // namespace alphabet
99
130
@@ -108,39 +139,48 @@ namespace jwt {
108
139
size_t length = 0 ;
109
140
110
141
padding () = default ;
111
- padding (size_t count, size_t length) : count(count), length(length) {}
112
142
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 }; }
114
146
115
147
friend bool operator ==(const padding& lhs, const padding& rhs) {
116
148
return lhs.count == rhs.count && lhs.length == rhs.length ;
117
149
}
118
150
};
119
151
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
+ }
127
164
}
128
165
}
129
166
130
167
return {};
131
168
}
132
169
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) {
135
171
size_t size = bin.size ();
136
172
std::string res;
137
173
174
+ res.reserve ((4UL * size) / 3UL );
175
+
138
176
// 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 ]);
144
184
145
185
uint32_t triple = (octet_a << 0x10 ) + (octet_b << 0x08 ) + octet_c;
146
186
@@ -152,8 +192,6 @@ namespace jwt {
152
192
153
193
if (fast_size == size) return res;
154
194
155
- size_t mod = size % 3 ;
156
-
157
195
uint32_t octet_a = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
158
196
uint32_t octet_b = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
159
197
uint32_t octet_c = fast_size < size ? static_cast <unsigned char >(bin[fast_size++]) : 0 ;
@@ -179,9 +217,10 @@ namespace jwt {
179
217
return res;
180
218
}
181
219
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);
185
224
if (pad.count > 2 ) throw std::runtime_error (" Invalid input: too much fill" );
186
225
187
226
const size_t size = base.size () - pad.length ;
@@ -191,7 +230,9 @@ namespace jwt {
191
230
std::string res;
192
231
res.reserve (out_size);
193
232
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
+ };
195
236
196
237
size_t fast_size = size - size % 4 ;
197
238
for (size_t i = 0 ; i < fast_size;) {
@@ -225,46 +266,64 @@ namespace jwt {
225
266
return res;
226
267
}
227
268
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;
239
275
default : break ;
240
276
}
241
-
242
- return base + padding;
277
+ return res;
243
278
}
244
279
245
- inline std::string trim (const std::string& base, const std::string& fill) {
280
+ inline std::string trim (string_view base, string_view fill) {
246
281
auto pos = base.find (fill);
247
- return base.substr (0 , pos);
282
+ return static_cast <std::string>( base.substr (0 , pos) );
248
283
}
249
284
} // namespace details
250
285
286
+ #ifdef JWT_HAS_STRING_VIEW
251
287
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 ] );
254
290
}
255
291
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 ));
258
295
}
259
296
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 ] );
262
299
}
263
300
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 ] );
266
303
}
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
267
324
} // namespace base
268
325
} // namespace jwt
269
326
327
+ #undef JWT_BASE_ALPHABET
328
+
270
329
#endif
0 commit comments