Skip to content

Commit 59cdb43

Browse files
authored
Refactor base64 decoding to use a lookup table (#364)
* Refactor base64 decoding to use a lookup table * Refactor base64 decoding to use a lookup table - clang format adaptions
1 parent 6dd190d commit 59cdb43

File tree

2 files changed

+87
-16
lines changed

2 files changed

+87
-16
lines changed

include/jwt-cpp/base.h

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ namespace jwt {
3838
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}};
3939
return data;
4040
}
41+
static const std::array<int8_t, 256>& rdata() {
42+
static constexpr std::array<int8_t, 256> rdata{{
43+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
44+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
45+
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
46+
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
47+
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
48+
49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
49+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
50+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
51+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
52+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
53+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
54+
}};
55+
return rdata;
56+
}
4157
static const std::string& fill() {
4258
static const std::string fill{"="};
4359
return fill;
@@ -61,6 +77,22 @@ namespace jwt {
6177
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}};
6278
return data;
6379
}
80+
static const std::array<int8_t, 256>& rdata() {
81+
static constexpr std::array<int8_t, 256> rdata{{
82+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
83+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
84+
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
85+
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
86+
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
87+
49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
88+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
89+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
90+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
91+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
92+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
93+
}};
94+
return rdata;
95+
}
6496
static const std::string& fill() {
6597
static const std::string fill{"%3d"};
6698
return fill;
@@ -82,18 +114,33 @@ namespace jwt {
82114
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}};
83115
return data;
84116
}
117+
static const std::array<int8_t, 256>& rdata() {
118+
static constexpr std::array<int8_t, 256> rdata{{
119+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
120+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
121+
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
122+
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
123+
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
124+
49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
125+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
126+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
127+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
128+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
129+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
130+
}};
131+
return rdata;
132+
}
85133
static const std::vector<std::string>& fill() {
86134
static const std::vector<std::string> fill{"%3D", "%3d"};
87135
return fill;
88136
}
89137
};
90138
} // namespace helper
91139

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"); }
95-
96-
return static_cast<uint32_t>(std::distance(alphabet.cbegin(), itr));
140+
inline uint32_t index(const std::array<int8_t, 256>& rdata, char symbol) {
141+
auto index = rdata[static_cast<unsigned char>(symbol)];
142+
if (index <= -1) { throw std::runtime_error("Invalid input: not within alphabet"); }
143+
return static_cast<uint32_t>(index);
97144
}
98145
} // namespace alphabet
99146

@@ -178,7 +225,7 @@ namespace jwt {
178225
return res;
179226
}
180227

181-
inline std::string decode(const std::string& base, const std::array<char, 64>& alphabet,
228+
inline std::string decode(const std::string& base, const std::array<int8_t, 256>& rdata,
182229
const std::vector<std::string>& fill) {
183230
const auto pad = count_padding(base, fill);
184231
if (pad.count > 2) throw std::runtime_error("Invalid input: too much fill");
@@ -190,7 +237,7 @@ namespace jwt {
190237
std::string res;
191238
res.reserve(out_size);
192239

193-
auto get_sextet = [&](size_t offset) { return alphabet::index(alphabet, base[offset]); };
240+
auto get_sextet = [&](size_t offset) { return alphabet::index(rdata, base[offset]); };
194241

195242
size_t fast_size = size - size % 4;
196243
for (size_t i = 0; i < fast_size;) {
@@ -224,9 +271,9 @@ namespace jwt {
224271
return res;
225272
}
226273

227-
inline std::string decode(const std::string& base, const std::array<char, 64>& alphabet,
274+
inline std::string decode(const std::string& base, const std::array<int8_t, 256>& rdata,
228275
const std::string& fill) {
229-
return decode(base, alphabet, std::vector<std::string>{fill});
276+
return decode(base, rdata, std::vector<std::string>{fill});
230277
}
231278

232279
inline std::string pad(const std::string& base, const std::string& fill) {
@@ -273,7 +320,7 @@ namespace jwt {
273320
*/
274321
template<typename T>
275322
std::string decode(const std::string& base) {
276-
return details::decode(base, T::data(), T::fill());
323+
return details::decode(base, T::rdata(), T::fill());
277324
}
278325
/**
279326
* \brief Generic base64 padding

tests/BaseTest.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,39 @@
22
#include <gtest/gtest.h>
33

44
TEST(BaseTest, Base64Index) {
5-
ASSERT_EQ(0, jwt::alphabet::index(jwt::alphabet::base64::data(), 'A'));
6-
ASSERT_EQ(32, jwt::alphabet::index(jwt::alphabet::base64::data(), 'g'));
7-
ASSERT_EQ(62, jwt::alphabet::index(jwt::alphabet::base64::data(), '+'));
5+
ASSERT_EQ(0, jwt::alphabet::index(jwt::alphabet::base64::rdata(), 'A'));
6+
ASSERT_EQ(32, jwt::alphabet::index(jwt::alphabet::base64::rdata(), 'g'));
7+
ASSERT_EQ(62, jwt::alphabet::index(jwt::alphabet::base64::rdata(), '+'));
8+
9+
std::size_t index = 0;
10+
for (auto c : jwt::alphabet::base64::data()) {
11+
ASSERT_EQ(index, jwt::alphabet::index(jwt::alphabet::base64::rdata(), c));
12+
index++;
13+
}
14+
15+
std::size_t noBaseCharCount = 0;
16+
for (std::size_t i = 0; i < jwt::alphabet::base64::rdata().size(); i++) {
17+
if (jwt::alphabet::base64::rdata().at(i) == -1) { noBaseCharCount++; }
18+
}
19+
ASSERT_EQ(jwt::alphabet::base64::rdata().size() - jwt::alphabet::base64::data().size(), noBaseCharCount);
820
}
921

1022
TEST(BaseTest, Base64URLIndex) {
11-
ASSERT_EQ(0, jwt::alphabet::index(jwt::alphabet::base64url::data(), 'A'));
12-
ASSERT_EQ(32, jwt::alphabet::index(jwt::alphabet::base64url::data(), 'g'));
13-
ASSERT_EQ(62, jwt::alphabet::index(jwt::alphabet::base64url::data(), '-'));
23+
ASSERT_EQ(0, jwt::alphabet::index(jwt::alphabet::base64url::rdata(), 'A'));
24+
ASSERT_EQ(32, jwt::alphabet::index(jwt::alphabet::base64url::rdata(), 'g'));
25+
ASSERT_EQ(62, jwt::alphabet::index(jwt::alphabet::base64url::rdata(), '-'));
26+
27+
std::size_t index = 0;
28+
for (auto c : jwt::alphabet::base64url::data()) {
29+
ASSERT_EQ(index, jwt::alphabet::index(jwt::alphabet::base64url::rdata(), c));
30+
index++;
31+
}
32+
33+
std::size_t noBaseCharCount = 0;
34+
for (std::size_t i = 0; i < jwt::alphabet::base64url::rdata().size(); i++) {
35+
if (jwt::alphabet::base64url::rdata().at(i) == -1) { noBaseCharCount++; }
36+
}
37+
ASSERT_EQ(jwt::alphabet::base64url::rdata().size() - jwt::alphabet::base64url::data().size(), noBaseCharCount);
1438
}
1539

1640
TEST(BaseTest, BaseDetailsCountPadding) {

0 commit comments

Comments
 (0)