1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Jim Winstead <jimw@php.net> |
16 | Xinchen Hui <laruence@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include <string.h>
21
22 #include "php.h"
23 #include "base64.h"
24
25 /* {{{ base64 tables */
26 static const char base64_table[] = {
27 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
28 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
29 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
30 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
31 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0'
32 };
33
34 static const char base64_pad = '=';
35
36 static const short base64_reverse_table[256] = {
37 -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2,
38 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
39 -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
40 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2,
41 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
42 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
43 -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
44 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
45 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
46 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
47 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
48 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
49 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
50 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
51 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
52 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
53 };
54 /* }}} */
55
php_base64_encode_impl(const unsigned char * in,size_t inl,unsigned char * out)56 static zend_always_inline unsigned char *php_base64_encode_impl(const unsigned char *in, size_t inl, unsigned char *out) /* {{{ */
57 {
58
59 while (inl > 2) { /* keep going until we have less than 24 bits */
60 *out++ = base64_table[in[0] >> 2];
61 *out++ = base64_table[((in[0] & 0x03) << 4) + (in[1] >> 4)];
62 *out++ = base64_table[((in[1] & 0x0f) << 2) + (in[2] >> 6)];
63 *out++ = base64_table[in[2] & 0x3f];
64
65 in += 3;
66 inl -= 3; /* we just handle 3 octets of data */
67 }
68
69 /* now deal with the tail end of things */
70 if (inl != 0) {
71 *out++ = base64_table[in[0] >> 2];
72 if (inl > 1) {
73 *out++ = base64_table[((in[0] & 0x03) << 4) + (in[1] >> 4)];
74 *out++ = base64_table[(in[1] & 0x0f) << 2];
75 *out++ = base64_pad;
76 } else {
77 *out++ = base64_table[(in[0] & 0x03) << 4];
78 *out++ = base64_pad;
79 *out++ = base64_pad;
80 }
81 }
82
83 *out = '\0';
84
85 return out;
86 }
87 /* }}} */
88
php_base64_decode_impl(const unsigned char * in,size_t inl,unsigned char * out,size_t * outl,zend_bool strict)89 static zend_always_inline int php_base64_decode_impl(const unsigned char *in, size_t inl, unsigned char *out, size_t *outl, zend_bool strict) /* {{{ */
90 {
91 int ch;
92 size_t i = 0, padding = 0, j = *outl;
93
94 /* run through the whole string, converting as we go */
95 while (inl-- > 0) {
96 ch = *in++;
97 if (ch == base64_pad) {
98 padding++;
99 continue;
100 }
101
102 ch = base64_reverse_table[ch];
103 if (!strict) {
104 /* skip unknown characters and whitespace */
105 if (ch < 0) {
106 continue;
107 }
108 } else {
109 /* skip whitespace */
110 if (ch == -1) {
111 continue;
112 }
113 /* fail on bad characters or if any data follows padding */
114 if (ch == -2 || padding) {
115 goto fail;
116 }
117 }
118
119 switch (i % 4) {
120 case 0:
121 out[j] = ch << 2;
122 break;
123 case 1:
124 out[j++] |= ch >> 4;
125 out[j] = (ch & 0x0f) << 4;
126 break;
127 case 2:
128 out[j++] |= ch >>2;
129 out[j] = (ch & 0x03) << 6;
130 break;
131 case 3:
132 out[j++] |= ch;
133 break;
134 }
135 i++;
136 }
137
138 /* fail if the input is truncated (only one char in last group) */
139 if (strict && i % 4 == 1) {
140 goto fail;
141 }
142
143 /* fail if the padding length is wrong (not VV==, VVV=), but accept zero padding
144 * RFC 4648: "In some circumstances, the use of padding [--] is not required" */
145 if (strict && padding && (padding > 2 || (i + padding) % 4 != 0)) {
146 goto fail;
147 }
148
149 *outl = j;
150 out[j] = '\0';
151
152 return 1;
153
154 fail:
155 return 0;
156 }
157 /* }}} */
158
159 /* {{{ php_base64_encode */
160
161 #if ZEND_INTRIN_AVX2_NATIVE
162 # undef ZEND_INTRIN_SSSE3_NATIVE
163 # undef ZEND_INTRIN_SSSE3_RESOLVER
164 # undef ZEND_INTRIN_SSSE3_FUNC_PROTO
165 # undef ZEND_INTRIN_SSSE3_FUNC_PTR
166 #elif ZEND_INTRIN_AVX2_FUNC_PROTO && ZEND_INTRIN_SSSE3_NATIVE
167 # undef ZEND_INTRIN_SSSE3_NATIVE
168 # undef ZEND_INTRIN_SSSE3_RESOLVER
169 # define ZEND_INTRIN_SSSE3_RESOLVER 1
170 # define ZEND_INTRIN_SSSE3_FUNC_PROTO 1
171 # undef ZEND_INTRIN_SSSE3_FUNC_DECL
172 # ifdef HAVE_FUNC_ATTRIBUTE_TARGET
173 # define ZEND_INTRIN_SSSE3_FUNC_DECL(func) ZEND_API func __attribute__((target("ssse3")))
174 # else
175 # define ZEND_INTRIN_SSSE3_FUNC_DECL(func) ZEND_API func
176 # endif
177 #elif ZEND_INTRIN_AVX2_FUNC_PTR && ZEND_INTRIN_SSSE3_NATIVE
178 # undef ZEND_INTRIN_SSSE3_NATIVE
179 # undef ZEND_INTRIN_SSSE3_RESOLVER
180 # define ZEND_INTRIN_SSSE3_RESOLVER 1
181 # define ZEND_INTRIN_SSSE3_FUNC_PTR 1
182 # undef ZEND_INTRIN_SSSE3_FUNC_DECL
183 # ifdef HAVE_FUNC_ATTRIBUTE_TARGET
184 # define ZEND_INTRIN_SSSE3_FUNC_DECL(func) ZEND_API func __attribute__((target("ssse3")))
185 # else
186 # define ZEND_INTRIN_SSSE3_FUNC_DECL(func) ZEND_API func
187 # endif
188 #endif
189
190 #if ZEND_INTRIN_AVX2_NATIVE
191 # include <immintrin.h>
192 #elif ZEND_INTRIN_SSSE3_NATIVE
193 # include <tmmintrin.h>
194 #elif (ZEND_INTRIN_SSSE3_RESOLVER || ZEND_INTRIN_AVX2_RESOLVER)
195 # if ZEND_INTRIN_AVX2_RESOLVER
196 # include <immintrin.h>
197 # else
198 # include <tmmintrin.h>
199 # endif /* (ZEND_INTRIN_SSSE3_RESOLVER || ZEND_INTRIN_AVX2_RESOLVER) */
200 # include "Zend/zend_cpuinfo.h"
201
202 # if ZEND_INTRIN_AVX2_RESOLVER
203 ZEND_INTRIN_AVX2_FUNC_DECL(zend_string *php_base64_encode_avx2(const unsigned char *str, size_t length));
204 ZEND_INTRIN_AVX2_FUNC_DECL(zend_string *php_base64_decode_ex_avx2(const unsigned char *str, size_t length, zend_bool strict));
205 # endif
206
207 # if ZEND_INTRIN_SSSE3_RESOLVER
208 ZEND_INTRIN_SSSE3_FUNC_DECL(zend_string *php_base64_encode_ssse3(const unsigned char *str, size_t length));
209 ZEND_INTRIN_SSSE3_FUNC_DECL(zend_string *php_base64_decode_ex_ssse3(const unsigned char *str, size_t length, zend_bool strict));
210 # endif
211
212 zend_string *php_base64_encode_default(const unsigned char *str, size_t length);
213 zend_string *php_base64_decode_ex_default(const unsigned char *str, size_t length, zend_bool strict);
214
215 # if (ZEND_INTRIN_AVX2_FUNC_PROTO || ZEND_INTRIN_SSSE3_FUNC_PROTO)
216 PHPAPI zend_string *php_base64_encode(const unsigned char *str, size_t length) __attribute__((ifunc("resolve_base64_encode")));
217 PHPAPI zend_string *php_base64_decode_ex(const unsigned char *str, size_t length, zend_bool strict) __attribute__((ifunc("resolve_base64_decode")));
218
219 ZEND_NO_SANITIZE_ADDRESS
resolve_base64_encode()220 static void *resolve_base64_encode() {
221 # if ZEND_INTRIN_AVX2_FUNC_PROTO
222 if (zend_cpu_supports_avx2()) {
223 return php_base64_encode_avx2;
224 } else
225 # endif
226 #if ZEND_INTRIN_SSSE3_FUNC_PROTO
227 if (zend_cpu_supports_ssse3()) {
228 return php_base64_encode_ssse3;
229 }
230 #endif
231 return php_base64_encode_default;
232 }
233
234 ZEND_NO_SANITIZE_ADDRESS
resolve_base64_decode()235 static void *resolve_base64_decode() {
236 # if ZEND_INTRIN_AVX2_FUNC_PROTO
237 if (zend_cpu_supports_avx2()) {
238 return php_base64_decode_ex_avx2;
239 } else
240 # endif
241 #if ZEND_INTRIN_SSSE3_FUNC_PROTO
242 if (zend_cpu_supports_ssse3()) {
243 return php_base64_decode_ex_ssse3;
244 }
245 #endif
246 return php_base64_decode_ex_default;
247 }
248 # else /* (ZEND_INTRIN_AVX2_FUNC_PROTO || ZEND_INTRIN_SSSE3_FUNC_PROTO) */
249
250 PHPAPI zend_string *(*php_base64_encode)(const unsigned char *str, size_t length) = NULL;
251 PHPAPI zend_string *(*php_base64_decode_ex)(const unsigned char *str, size_t length, zend_bool strict) = NULL;
252
PHP_MINIT_FUNCTION(base64_intrin)253 PHP_MINIT_FUNCTION(base64_intrin)
254 {
255 # if ZEND_INTRIN_AVX2_FUNC_PTR
256 if (zend_cpu_supports_avx2()) {
257 php_base64_encode = php_base64_encode_avx2;
258 php_base64_decode_ex = php_base64_decode_ex_avx2;
259 } else
260 # endif
261 #if ZEND_INTRIN_SSSE3_FUNC_PTR
262 if (zend_cpu_supports_ssse3()) {
263 php_base64_encode = php_base64_encode_ssse3;
264 php_base64_decode_ex = php_base64_decode_ex_ssse3;
265 } else
266 #endif
267 {
268 php_base64_encode = php_base64_encode_default;
269 php_base64_decode_ex = php_base64_decode_ex_default;
270 }
271 return SUCCESS;
272 }
273 # endif /* (ZEND_INTRIN_AVX2_FUNC_PROTO || ZEND_INTRIN_SSSE3_FUNC_PROTO) */
274 #endif /* ZEND_INTRIN_AVX2_NATIVE */
275
276 #if ZEND_INTRIN_AVX2_NATIVE || ZEND_INTRIN_AVX2_RESOLVER
277 # if ZEND_INTRIN_AVX2_RESOLVER && defined(HAVE_FUNC_ATTRIBUTE_TARGET)
278 static __m256i php_base64_encode_avx2_reshuffle(__m256i in) __attribute__((target("avx2")));
279 static __m256i php_base64_encode_avx2_translate(__m256i in) __attribute__((target("avx2")));
280 # endif
php_base64_encode_avx2_reshuffle(__m256i in)281 static __m256i php_base64_encode_avx2_reshuffle(__m256i in)
282 {
283 /* This one works with shifted (4 bytes) input in order to
284 * be able to work efficiently in the 2 128-bit lanes */
285 __m256i t0, t1, t2, t3;
286
287 /* input, bytes MSB to LSB:
288 * 0 0 0 0 x w v u t s r q p o n m
289 * l k j i h g f e d c b a 0 0 0 0 */
290 in = _mm256_shuffle_epi8(in, _mm256_set_epi8(
291 10, 11, 9, 10,
292 7, 8, 6, 7,
293 4, 5, 3, 4,
294 1, 2, 0, 1,
295
296 14, 15, 13, 14,
297 11, 12, 10, 11,
298 8, 9, 7, 8,
299 5, 6, 4, 5));
300
301 t0 = _mm256_and_si256(in, _mm256_set1_epi32(0x0fc0fc00));
302
303 t1 = _mm256_mulhi_epu16(t0, _mm256_set1_epi32(0x04000040));
304
305 t2 = _mm256_and_si256(in, _mm256_set1_epi32(0x003f03f0));
306
307 t3 = _mm256_mullo_epi16(t2, _mm256_set1_epi32(0x01000010));
308
309 return _mm256_or_si256(t1, t3);
310 /* 00xxxxxx 00wwwwXX 00vvWWWW 00VVVVVV
311 * 00uuuuuu 00ttttUU 00ssTTTT 00SSSSSS
312 * 00rrrrrr 00qqqqRR 00ppQQQQ 00PPPPPP
313 * 00oooooo 00nnnnOO 00mmNNNN 00MMMMMM
314 * 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
315 * 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
316 * 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
317 * 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA */
318 }
319
php_base64_encode_avx2_translate(__m256i in)320 static __m256i php_base64_encode_avx2_translate(__m256i in)
321 {
322 __m256i lut, indices, mask;
323
324 lut = _mm256_setr_epi8(
325 65, 71, -4, -4, -4, -4, -4, -4,
326 -4, -4, -4, -4, -19, -16, 0, 0,
327 65, 71, -4, -4, -4, -4, -4, -4,
328 -4, -4, -4, -4, -19, -16, 0, 0);
329
330 indices = _mm256_subs_epu8(in, _mm256_set1_epi8(51));
331
332 mask = _mm256_cmpgt_epi8(in, _mm256_set1_epi8(25));
333
334 indices = _mm256_sub_epi8(indices, mask);
335
336 return _mm256_add_epi8(in, _mm256_shuffle_epi8(lut, indices));
337
338 }
339 #endif /* ZEND_INTRIN_AVX2_NATIVE || (ZEND_INTRIN_AVX2_RESOLVER && !ZEND_INTRIN_SSSE3_NATIVE) */
340
341 #if ZEND_INTRIN_SSSE3_NATIVE || ZEND_INTRIN_SSSE3_RESOLVER
342
343 # if ZEND_INTRIN_SSSE3_RESOLVER && defined(HAVE_FUNC_ATTRIBUTE_TARGET)
344 static __m128i php_base64_encode_ssse3_reshuffle(__m128i in) __attribute__((target("ssse3")));
345 static __m128i php_base64_encode_ssse3_translate(__m128i in) __attribute__((target("ssse3")));
346 # endif
347
php_base64_encode_ssse3_reshuffle(__m128i in)348 static __m128i php_base64_encode_ssse3_reshuffle(__m128i in)
349 {
350 __m128i t0, t1, t2, t3;
351
352 /* input, bytes MSB to LSB:
353 * 0 0 0 0 l k j i h g f e d c b a */
354 in = _mm_shuffle_epi8(in, _mm_set_epi8(
355 10, 11, 9, 10,
356 7, 8, 6, 7,
357 4, 5, 3, 4,
358 1, 2, 0, 1));
359
360 t0 = _mm_and_si128(in, _mm_set1_epi32(0x0fc0fc00));
361
362 t1 = _mm_mulhi_epu16(t0, _mm_set1_epi32(0x04000040));
363
364 t2 = _mm_and_si128(in, _mm_set1_epi32(0x003f03f0));
365
366 t3 = _mm_mullo_epi16(t2, _mm_set1_epi32(0x01000010));
367
368 /* output (upper case are MSB, lower case are LSB):
369 * 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
370 * 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
371 * 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
372 * 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA */
373 return _mm_or_si128(t1, t3);
374 }
375
php_base64_encode_ssse3_translate(__m128i in)376 static __m128i php_base64_encode_ssse3_translate(__m128i in)
377 {
378 __m128i mask, indices;
379 __m128i lut = _mm_setr_epi8(
380 65, 71, -4, -4,
381 -4, -4, -4, -4,
382 -4, -4, -4, -4,
383 -19, -16, 0, 0
384 );
385
386 /* Translate values 0..63 to the Base64 alphabet. There are five sets:
387 * # From To Abs Index Characters
388 * 0 [0..25] [65..90] +65 0 ABCDEFGHIJKLMNOPQRSTUVWXYZ
389 * 1 [26..51] [97..122] +71 1 abcdefghijklmnopqrstuvwxyz
390 * 2 [52..61] [48..57] -4 [2..11] 0123456789
391 * 3 [62] [43] -19 12 +
392 * 4 [63] [47] -16 13 / */
393
394 /* Create LUT indices from input:
395 * the index for range #0 is right, others are 1 less than expected: */
396 indices = _mm_subs_epu8(in, _mm_set1_epi8(51));
397
398 /* mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0: */
399 mask = _mm_cmpgt_epi8(in, _mm_set1_epi8(25));
400
401 /* subtract -1, so add 1 to indices for range #[1..4], All indices are now correct: */
402 indices = _mm_sub_epi8(indices, mask);
403
404 /* Add offsets to input values: */
405 return _mm_add_epi8(in, _mm_shuffle_epi8(lut, indices));
406 }
407
408 #define PHP_BASE64_ENCODE_SSSE3_LOOP \
409 while (length > 15) { \
410 __m128i s = _mm_loadu_si128((__m128i *)c); \
411 \
412 s = php_base64_encode_ssse3_reshuffle(s); \
413 \
414 s = php_base64_encode_ssse3_translate(s); \
415 \
416 _mm_storeu_si128((__m128i *)o, s); \
417 c += 12; \
418 o += 16; \
419 length -= 12; \
420 }
421
422 #endif /* ZEND_INTRIN_SSSE3_NATIVE || (ZEND_INTRIN_SSSE3_RESOLVER && !ZEND_INTRIN_AVX2_NATIVE) */
423
424 #if ZEND_INTRIN_AVX2_NATIVE || ZEND_INTRIN_AVX2_RESOLVER || ZEND_INTRIN_SSSE3_NATIVE || ZEND_INTRIN_SSSE3_RESOLVER
425 # if ZEND_INTRIN_AVX2_NATIVE || ZEND_INTRIN_SSSE3_NATIVE
php_base64_encode(const unsigned char * str,size_t length)426 PHPAPI zend_string *php_base64_encode(const unsigned char *str, size_t length)
427 # elif ZEND_INTRIN_AVX2_RESOLVER
428 zend_string *php_base64_encode_avx2(const unsigned char *str, size_t length)
429 # else /* ZEND_INTRIN_SSSE3_RESOLVER */
430 zend_string *php_base64_encode_ssse3(const unsigned char *str, size_t length)
431 # endif
432 {
433 const unsigned char *c = str;
434 unsigned char *o;
435 zend_string *result;
436
437 result = zend_string_safe_alloc(((length + 2) / 3), 4 * sizeof(char), 0, 0);
438 o = (unsigned char *)ZSTR_VAL(result);
439 # if ZEND_INTRIN_AVX2_NATIVE || ZEND_INTRIN_AVX2_RESOLVER
440 if (length > 31) {
441 __m256i s = _mm256_loadu_si256((__m256i *)c);
442
443 s = _mm256_permutevar8x32_epi32(s, _mm256_setr_epi32(0, 0, 1, 2, 3, 4, 5, 6));
444
445 for (;;) {
446 s = php_base64_encode_avx2_reshuffle(s);
447
448 s = php_base64_encode_avx2_translate(s);
449
450 _mm256_storeu_si256((__m256i *)o, s);
451 c += 24;
452 o += 32;
453 length -= 24;
454 if (length < 28) {
455 break;
456 }
457 s = _mm256_loadu_si256((__m256i *)(c - 4));
458 }
459 }
460 # else
461 PHP_BASE64_ENCODE_SSSE3_LOOP;
462 # endif
463
464 o = php_base64_encode_impl(c, length, o);
465
466 ZSTR_LEN(result) = (o - (unsigned char *)ZSTR_VAL(result));
467
468 return result;
469 }
470
471 # if ZEND_INTRIN_SSSE3_RESOLVER && ZEND_INTRIN_AVX2_RESOLVER
php_base64_encode_ssse3(const unsigned char * str,size_t length)472 zend_string *php_base64_encode_ssse3(const unsigned char *str, size_t length)
473 {
474 const unsigned char *c = str;
475 unsigned char *o;
476 zend_string *result;
477
478 result = zend_string_safe_alloc(((length + 2) / 3), 4 * sizeof(char), 0, 0);
479 o = (unsigned char *)ZSTR_VAL(result);
480
481 PHP_BASE64_ENCODE_SSSE3_LOOP;
482
483 o = php_base64_encode_impl(c, length, o);
484
485 ZSTR_LEN(result) = (o - (unsigned char *)ZSTR_VAL(result));
486
487 return result;
488 }
489 # endif
490 #endif /* ZEND_INTRIN_AVX2_NATIVE || ZEND_INTRIN_AVX2_RESOLVER || ZEND_INTRIN_SSSE3_NATIVE || ZEND_INTRIN_SSSE3_RESOLVER */
491
492 /* }}} */
493
494 /* {{{ php_base64_decode_ex */
495 /* generate reverse table (do not set index 0 to 64)
496 static unsigned short base64_reverse_table[256];
497 #define rt base64_reverse_table
498 void php_base64_init(void)
499 {
500 char *s = emalloc(10240), *sp;
501 char *chp;
502 short idx;
503
504 for(ch = 0; ch < 256; ch++) {
505 chp = strchr(base64_table, ch);
506 if(ch && chp) {
507 idx = chp - base64_table;
508 if (idx >= 64) idx = -1;
509 rt[ch] = idx;
510 } else {
511 rt[ch] = -1;
512 }
513 }
514 sp = s;
515 sprintf(sp, "static const short base64_reverse_table[256] = {\n");
516 for(ch =0; ch < 256;) {
517 sp = s+strlen(s);
518 sprintf(sp, "\t% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,% 3d,\n", rt[ch+0], rt[ch+1], rt[ch+2], rt[ch+3], rt[ch+4], rt[ch+5], rt[ch+6], rt[ch+7], rt[ch+8], rt[ch+9], rt[ch+10], rt[ch+11], rt[ch+12], rt[ch+13], rt[ch+14], rt[ch+15]);
519 ch += 16;
520 }
521 sprintf(sp, "};");
522 php_error_docref(NULL, E_NOTICE, "Reverse_table:\n%s", s);
523 efree(s);
524 }
525 */
526
527 #if ZEND_INTRIN_AVX2_NATIVE || ZEND_INTRIN_AVX2_RESOLVER
528 # if ZEND_INTRIN_AVX2_RESOLVER && defined(HAVE_FUNC_ATTRIBUTE_TARGET)
529 static __m256i php_base64_decode_avx2_reshuffle(__m256i in) __attribute__((target("avx2")));
530 # endif
531
php_base64_decode_avx2_reshuffle(__m256i in)532 static __m256i php_base64_decode_avx2_reshuffle(__m256i in)
533 {
534 __m256i merge_ab_and_bc, out;
535
536 merge_ab_and_bc = _mm256_maddubs_epi16(in, _mm256_set1_epi32(0x01400140));
537
538 out = _mm256_madd_epi16(merge_ab_and_bc, _mm256_set1_epi32(0x00011000));
539
540 out = _mm256_shuffle_epi8(out, _mm256_setr_epi8(
541 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1,
542 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1));
543
544 return _mm256_permutevar8x32_epi32(out, _mm256_setr_epi32(0, 1, 2, 4, 5, 6, -1, -1));
545 }
546 #endif
547
548 #if ZEND_INTRIN_SSSE3_NATIVE || ZEND_INTRIN_SSSE3_RESOLVER
549 # if ZEND_INTRIN_SSSE3_RESOLVER && defined(HAVE_FUNC_ATTRIBUTE_TARGET)
550 static __m128i php_base64_decode_ssse3_reshuffle(__m128i in) __attribute__((target("ssse3")));
551 # endif
552
php_base64_decode_ssse3_reshuffle(__m128i in)553 static __m128i php_base64_decode_ssse3_reshuffle(__m128i in)
554 {
555 __m128i merge_ab_and_bc, out;
556
557 merge_ab_and_bc = _mm_maddubs_epi16(in, _mm_set1_epi32(0x01400140));
558 /* 0000kkkk LLllllll 0000JJJJ JJjjKKKK
559 * 0000hhhh IIiiiiii 0000GGGG GGggHHHH
560 * 0000eeee FFffffff 0000DDDD DDddEEEE
561 * 0000bbbb CCcccccc 0000AAAA AAaaBBBB */
562
563 out = _mm_madd_epi16(merge_ab_and_bc, _mm_set1_epi32(0x00011000));
564 /* 00000000 JJJJJJjj KKKKkkkk LLllllll
565 * 00000000 GGGGGGgg HHHHhhhh IIiiiiii
566 * 00000000 DDDDDDdd EEEEeeee FFffffff
567 * 00000000 AAAAAAaa BBBBbbbb CCcccccc */
568
569 return _mm_shuffle_epi8(out, _mm_setr_epi8(
570 2, 1, 0,
571 6, 5, 4,
572 10, 9, 8,
573 14, 13, 12,
574 -1, -1, -1, -1));
575 /* 00000000 00000000 00000000 00000000
576 * LLllllll KKKKkkkk JJJJJJjj IIiiiiii
577 * HHHHhhhh GGGGGGgg FFffffff EEEEeeee
578 * DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa */
579 }
580
581 #define PHP_BASE64_DECODE_SSSE3_LOOP \
582 while (length > 15 + 6 + 2) { \
583 __m128i lut_lo, lut_hi, lut_roll; \
584 __m128i hi_nibbles, lo_nibbles, hi, lo; \
585 __m128i s = _mm_loadu_si128((__m128i *)c); \
586 \
587 lut_lo = _mm_setr_epi8( \
588 0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, \
589 0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A); \
590 lut_hi = _mm_setr_epi8( \
591 0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08, \
592 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10); \
593 lut_roll = _mm_setr_epi8( \
594 0, 16, 19, 4, -65, -65, -71, -71, \
595 0, 0, 0, 0, 0, 0, 0, 0); \
596 \
597 hi_nibbles = _mm_and_si128( \
598 _mm_srli_epi32(s, 4), _mm_set1_epi8(0x2f)); \
599 lo_nibbles = _mm_and_si128(s, _mm_set1_epi8(0x2f)); \
600 hi = _mm_shuffle_epi8(lut_hi, hi_nibbles); \
601 lo = _mm_shuffle_epi8(lut_lo, lo_nibbles); \
602 \
603 \
604 if (UNEXPECTED( \
605 _mm_movemask_epi8( \
606 _mm_cmpgt_epi8( \
607 _mm_and_si128(lo, hi), _mm_set1_epi8(0))))) { \
608 break; \
609 } else { \
610 __m128i eq_2f, roll; \
611 \
612 eq_2f = _mm_cmpeq_epi8(s, _mm_set1_epi8(0x2f)); \
613 roll = _mm_shuffle_epi8( \
614 lut_roll, _mm_add_epi8(eq_2f, hi_nibbles)); \
615 \
616 s = _mm_add_epi8(s, roll); \
617 s = php_base64_decode_ssse3_reshuffle(s); \
618 \
619 _mm_storeu_si128((__m128i *)o, s); \
620 \
621 c += 16; \
622 o += 12; \
623 outl += 12; \
624 length -= 16; \
625 } \
626 }
627
628 #endif
629
630 #if ZEND_INTRIN_AVX2_NATIVE || ZEND_INTRIN_AVX2_RESOLVER || ZEND_INTRIN_SSSE3_NATIVE || ZEND_INTRIN_SSSE3_RESOLVER
631 # if ZEND_INTRIN_AVX2_NATIVE || ZEND_INTRIN_SSSE3_NATIVE
php_base64_decode_ex(const unsigned char * str,size_t length,zend_bool strict)632 PHPAPI zend_string *php_base64_decode_ex(const unsigned char *str, size_t length, zend_bool strict)
633 # elif ZEND_INTRIN_AVX2_RESOLVER
634 zend_string *php_base64_decode_ex_avx2(const unsigned char *str, size_t length, zend_bool strict)
635 # else
636 zend_string *php_base64_decode_ex_ssse3(const unsigned char *str, size_t length, zend_bool strict)
637 # endif
638 {
639 const unsigned char *c = str;
640 unsigned char *o;
641 size_t outl = 0;
642 zend_string *result;
643
644 result = zend_string_alloc(length, 0);
645 o = (unsigned char *)ZSTR_VAL(result);
646
647 /* See: "Faster Base64 Encoding and Decoding using AVX2 Instructions"
648 * https://arxiv.org/pdf/1704.00605.pdf */
649 # if ZEND_INTRIN_AVX2_NATIVE || ZEND_INTRIN_AVX2_RESOLVER
650 while (length > 31 + 11 + 2) {
651 __m256i lut_lo, lut_hi, lut_roll;
652 __m256i hi_nibbles, lo_nibbles, hi, lo;
653 __m256i str = _mm256_loadu_si256((__m256i *)c);
654
655 lut_lo = _mm256_setr_epi8(
656 0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
657 0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A,
658 0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
659 0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A);
660
661 lut_hi = _mm256_setr_epi8(
662 0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08,
663 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
664 0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08,
665 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10);
666
667 lut_roll = _mm256_setr_epi8(
668 0, 16, 19, 4, -65, -65, -71, -71,
669 0, 0, 0, 0, 0, 0, 0, 0,
670 0, 16, 19, 4, -65, -65, -71, -71,
671 0, 0, 0, 0, 0, 0, 0, 0);
672
673 hi_nibbles = _mm256_and_si256(_mm256_srli_epi32(str, 4), _mm256_set1_epi8(0x2f));
674 lo_nibbles = _mm256_and_si256(str, _mm256_set1_epi8(0x2f));
675 hi = _mm256_shuffle_epi8(lut_hi, hi_nibbles);
676 lo = _mm256_shuffle_epi8(lut_lo, lo_nibbles);
677
678 if (!_mm256_testz_si256(lo, hi)) {
679 break;
680 } else {
681 __m256i eq_2f, roll;
682 eq_2f = _mm256_cmpeq_epi8(str, _mm256_set1_epi8(0x2f));
683 roll = _mm256_shuffle_epi8(lut_roll, _mm256_add_epi8(eq_2f, hi_nibbles));
684
685
686 str = _mm256_add_epi8(str, roll);
687
688 str = php_base64_decode_avx2_reshuffle(str);
689
690 _mm256_storeu_si256((__m256i *)o, str);
691
692 c += 32;
693 o += 24;
694 outl += 24;
695 length -= 32;
696 }
697 }
698 # else
699 PHP_BASE64_DECODE_SSSE3_LOOP;
700 # endif
701
702 if (!php_base64_decode_impl(c, length, (unsigned char*)ZSTR_VAL(result), &outl, strict)) {
703 zend_string_efree(result);
704 return NULL;
705 }
706
707 ZSTR_LEN(result) = outl;
708
709 return result;
710 }
711
712 # if ZEND_INTRIN_SSSE3_RESOLVER && ZEND_INTRIN_AVX2_RESOLVER
php_base64_decode_ex_ssse3(const unsigned char * str,size_t length,zend_bool strict)713 zend_string *php_base64_decode_ex_ssse3(const unsigned char *str, size_t length, zend_bool strict)
714 {
715 const unsigned char *c = str;
716 unsigned char *o;
717 size_t outl = 0;
718 zend_string *result;
719
720 result = zend_string_alloc(length, 0);
721 o = (unsigned char *)ZSTR_VAL(result);
722
723 PHP_BASE64_DECODE_SSSE3_LOOP;
724
725 if (!php_base64_decode_impl(c, length, (unsigned char*)ZSTR_VAL(result), &outl, strict)) {
726 zend_string_efree(result);
727 return NULL;
728 }
729
730 ZSTR_LEN(result) = outl;
731
732 return result;
733 }
734 # endif
735 #endif /* ZEND_INTRIN_AVX2_NATIVE || ZEND_INTRIN_AVX2_RESOLVER || ZEND_INTRIN_SSSE3_NATIVE || ZEND_INTRIN_SSSE3_RESOLVER */
736
737 #if !ZEND_INTRIN_AVX2_NATIVE && !ZEND_INTRIN_SSSE3_NATIVE
738 #if ZEND_INTRIN_AVX2_RESOLVER || ZEND_INTRIN_SSSE3_RESOLVER
php_base64_encode_default(const unsigned char * str,size_t length)739 zend_string *php_base64_encode_default(const unsigned char *str, size_t length)
740 #else
741 PHPAPI zend_string *php_base64_encode(const unsigned char *str, size_t length)
742 #endif
743 {
744 unsigned char *p;
745 zend_string *result;
746
747 result = zend_string_safe_alloc(((length + 2) / 3), 4 * sizeof(char), 0, 0);
748 p = (unsigned char *)ZSTR_VAL(result);
749
750 p = php_base64_encode_impl(str, length, p);
751
752 ZSTR_LEN(result) = (p - (unsigned char *)ZSTR_VAL(result));
753
754 return result;
755 }
756 #endif
757
758 #if !ZEND_INTRIN_AVX2_NATIVE && !ZEND_INTRIN_SSSE3_NATIVE
759 #if ZEND_INTRIN_AVX2_RESOLVER || ZEND_INTRIN_SSSE3_RESOLVER
php_base64_decode_ex_default(const unsigned char * str,size_t length,zend_bool strict)760 zend_string *php_base64_decode_ex_default(const unsigned char *str, size_t length, zend_bool strict)
761 #else
762 PHPAPI zend_string *php_base64_decode_ex(const unsigned char *str, size_t length, zend_bool strict)
763 #endif
764 {
765 zend_string *result;
766 size_t outl = 0;
767
768 result = zend_string_alloc(length, 0);
769
770 if (!php_base64_decode_impl(str, length, (unsigned char*)ZSTR_VAL(result), &outl, strict)) {
771 zend_string_efree(result);
772 return NULL;
773 }
774
775 ZSTR_LEN(result) = outl;
776
777 return result;
778 }
779 #endif
780 /* }}} */
781
782 /* {{{ proto string base64_encode(string str)
783 Encodes string using MIME base64 algorithm */
PHP_FUNCTION(base64_encode)784 PHP_FUNCTION(base64_encode)
785 {
786 char *str;
787 size_t str_len;
788 zend_string *result;
789
790 ZEND_PARSE_PARAMETERS_START(1, 1)
791 Z_PARAM_STRING(str, str_len)
792 ZEND_PARSE_PARAMETERS_END();
793
794 result = php_base64_encode((unsigned char*)str, str_len);
795 if (result != NULL) {
796 RETURN_STR(result);
797 } else {
798 RETURN_FALSE;
799 }
800 }
801 /* }}} */
802
803 /* {{{ proto string base64_decode(string str[, bool strict])
804 Decodes string using MIME base64 algorithm */
PHP_FUNCTION(base64_decode)805 PHP_FUNCTION(base64_decode)
806 {
807 char *str;
808 zend_bool strict = 0;
809 size_t str_len;
810 zend_string *result;
811
812 ZEND_PARSE_PARAMETERS_START(1, 2)
813 Z_PARAM_STRING(str, str_len)
814 Z_PARAM_OPTIONAL
815 Z_PARAM_BOOL(strict)
816 ZEND_PARSE_PARAMETERS_END();
817
818 result = php_base64_decode_ex((unsigned char*)str, str_len, strict);
819 if (result != NULL) {
820 RETURN_STR(result);
821 } else {
822 RETURN_FALSE;
823 }
824 }
825 /* }}} */
826
827 /*
828 * Local variables:
829 * tab-width: 4
830 * c-basic-offset: 4
831 * End:
832 * vim600: sw=4 ts=4 fdm=marker
833 * vim<600: sw=4 ts=4
834 */
835