@@ -13,12 +13,30 @@ namespace hermes {
13
13
namespace vm {
14
14
15
15
namespace {
16
+
16
17
constexpr const std::array<char , 64 > Base64Chars = {
17
18
' A' , ' B' , ' C' , ' D' , ' E' , ' F' , ' G' , ' H' , ' I' , ' J' , ' K' , ' L' , ' M' ,
18
19
' N' , ' O' , ' P' , ' Q' , ' R' , ' S' , ' T' , ' U' , ' V' , ' W' , ' X' , ' Y' , ' Z' ,
19
20
' a' , ' b' , ' c' , ' d' , ' e' , ' f' , ' g' , ' h' , ' i' , ' j' , ' k' , ' l' , ' m' ,
20
21
' n' , ' o' , ' p' , ' q' , ' r' , ' s' , ' t' , ' u' , ' v' , ' w' , ' x' , ' y' , ' z' ,
21
22
' 0' , ' 1' , ' 2' , ' 3' , ' 4' , ' 5' , ' 6' , ' 7' , ' 8' , ' 9' , ' +' , ' /' };
23
+
24
+ // A lookup table that map (Base64-encoded) ASCII back to binary.
25
+ constexpr const std::array<unsigned char , 128 > decMap = {
26
+ 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ,
27
+ 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ,
28
+ 64 , 64 , 64 , 64 , 64 , 62 , 64 , 64 , 64 , 63 , 52 , 53 , 54 , 55 , 56 , 57 , 58 , 59 , 60 ,
29
+ 61 , 64 , 64 , 64 , 64 , 64 , 64 , 64 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ,
30
+ 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 64 , 64 , 64 , 64 ,
31
+ 64 , 64 , 26 , 27 , 28 , 29 , 30 , 31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , 41 , 42 ,
32
+ 43 , 44 , 45 , 46 , 47 , 48 , 49 , 50 , 51 , 64 , 64 , 64 , 64 , 64 };
33
+
34
+ template <typename T>
35
+ inline bool isWhitespace (T c) {
36
+ return (
37
+ c == ' \x09 ' || c == ' \x0A ' || c == ' \x0C ' || c == ' \x0D ' || c == ' \x20 ' );
38
+ }
39
+
22
40
} // namespace
23
41
24
42
template <typename T>
@@ -90,5 +108,92 @@ template bool base64Encode(
90
108
llvh::ArrayRef<char16_t > str,
91
109
StringBuilder &builder);
92
110
111
+ template <typename T>
112
+ OptValue<uint32_t > base64DecodeOutputLength (llvh::ArrayRef<T> str) {
113
+ // Figure out the actual string length after ignoring all whitespaces.
114
+ uint64_t strLength = 0 ;
115
+ T lastChar = 0 ;
116
+ T secondLastChar = 0 ;
117
+ for (const auto c : str) {
118
+ // Only increment length if character is not a whitespace
119
+ if (!isWhitespace (c)) {
120
+ strLength++;
121
+ secondLastChar = lastChar;
122
+ lastChar = c;
123
+ }
124
+ }
125
+
126
+ uint32_t numPadding = 0 ;
127
+ if (strLength % 4 == 0 ) {
128
+ // Check to see if the last character or the last 2 characters are the
129
+ // padding character.
130
+ if (strLength > 0 && lastChar == ' =' ) {
131
+ numPadding++;
132
+ if (strLength > 1 && secondLastChar == ' =' ) {
133
+ numPadding++;
134
+ }
135
+ }
136
+ } else {
137
+ // The input string should always be divisible by 4.
138
+ return llvh::None;
139
+ }
140
+
141
+ // This shouldn't overflow because the value is guaranteed to be smaller.
142
+ uint32_t expectedLength = (strLength / 4 * 3 ) - numPadding;
143
+ if (strLength != 0 && expectedLength == 0 ) {
144
+ return llvh::None;
145
+ }
146
+ return expectedLength;
147
+ }
148
+
149
+ template OptValue<uint32_t > base64DecodeOutputLength (llvh::ArrayRef<char > str);
150
+ template OptValue<uint32_t > base64DecodeOutputLength (
151
+ llvh::ArrayRef<char16_t > str);
152
+
153
+ template <typename T>
154
+ bool base64Decode (llvh::ArrayRef<T> str, StringBuilder &builder) {
155
+ // Iterate over the trimmed \p str, decode every \c c into a sextet and store
156
+ // into a buffer \c buf of capacity 32 bits. \c bufSize is maintained to
157
+ // track how many bits are actually buffered.
158
+ uint32_t buf = 0 ;
159
+ uint32_t bufSize = 0 ;
160
+ for (const auto c : str) {
161
+ if (isWhitespace (c)) {
162
+ continue ;
163
+ }
164
+
165
+ if (LLVM_UNLIKELY (c > 127 ) || LLVM_UNLIKELY (c < 0 )) {
166
+ return false ;
167
+ }
168
+
169
+ if (c == ' =' ) {
170
+ break ;
171
+ }
172
+
173
+ unsigned char sextet = decMap[c];
174
+ if (LLVM_UNLIKELY (sextet >= 64 )) {
175
+ return false ;
176
+ }
177
+
178
+ // Making room for the new sextet.
179
+ buf = (buf << 6 ) + sextet;
180
+ bufSize += 6 ;
181
+
182
+ // Once buffer is filled over a byte, evacuate a byte to the output.
183
+ if (bufSize >= 8 ) {
184
+ char16_t decodedChar = (buf >> (bufSize - 8 )) & 0xFF ;
185
+ builder.appendCharacter (decodedChar);
186
+ bufSize -= 8 ;
187
+ }
188
+ }
189
+
190
+ return builder.currentLength () == builder.maxLength ();
191
+ }
192
+
193
+ template bool base64Decode (llvh::ArrayRef<char > str, StringBuilder &builder);
194
+ template bool base64Decode (
195
+ llvh::ArrayRef<char16_t > str,
196
+ StringBuilder &builder);
197
+
93
198
} // namespace vm
94
199
} // namespace hermes
0 commit comments