1// The input consists of six character sets in the Base64 alphabet, which we
2// need to map back to the 6-bit values they represent. There are three ranges,
3// two singles, and then there's the rest.
4//
5// # From To Add Characters
6// 1 [43] [62] +19 +
7// 2 [47] [63] +16 /
8// 3 [48..57] [52..61] +4 0..9
9// 4 [65..90] [0..25] -65 A..Z
10// 5 [97..122] [26..51] -71 a..z
11// (6) Everything else => invalid input
12//
13// We will use lookup tables for character validation and offset computation.
14// Remember that 0x2X and 0x0X are the same index for _mm_shuffle_epi8, this
15// allows to mask with 0x2F instead of 0x0F and thus save one constant
16// declaration (register and/or memory access).
17//
18// For offsets:
19// Perfect hash for lut = ((src >> 4) & 0x2F) + ((src == 0x2F) ? 0xFF : 0x00)
20// 0000 = garbage
21// 0001 = /
22// 0010 = +
23// 0011 = 0-9
24// 0100 = A-Z
25// 0101 = A-Z
26// 0110 = a-z
27// 0111 = a-z
28// 1000 >= garbage
29//
30// For validation, here's the table.
31// A character is valid if and only if the AND of the 2 lookups equals 0:
32//
33// hi \ lo 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
34// LUT 0x15 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x13 0x1A 0x1B 0x1B 0x1B 0x1A
35//
36// 0000 0x10 char NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI
37// andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
38//
39// 0001 0x10 char DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US
40// andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
41//
42// 0010 0x01 char ! " # $ % & ' ( ) * + , - . /
43// andlut 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00 0x01 0x01 0x01 0x00
44//
45// 0011 0x02 char 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
46// andlut 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x02 0x02 0x02 0x02 0x02
47//
48// 0100 0x04 char @ A B C D E F G H I J K L M N O
49// andlut 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
50//
51// 0101 0x08 char P Q R S T U V W X Y Z [ \ ] ^ _
52// andlut 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08
53//
54// 0110 0x04 char ` a b c d e f g h i j k l m n o
55// andlut 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
56// 0111 0x08 char p q r s t u v w x y z { | } ~
57// andlut 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08
58//
59// 1000 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
60// 1001 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
61// 1010 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
62// 1011 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
63// 1100 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
64// 1101 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
65// 1110 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
66// 1111 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
67
68static inline int
69dec_loop_ssse3_inner (const uint8_t **s, uint8_t **o, size_t *rounds)
70{
71 const __m128i lut_lo = _mm_setr_epi8(
72 0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
73 0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A);
74
75 const __m128i lut_hi = _mm_setr_epi8(
76 0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08,
77 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10);
78
79 const __m128i lut_roll = _mm_setr_epi8(
80 0, 16, 19, 4, -65, -65, -71, -71,
81 0, 0, 0, 0, 0, 0, 0, 0);
82
83 const __m128i mask_2F = _mm_set1_epi8(0x2F);
84
85 // Load input:
86 __m128i str = _mm_loadu_si128((__m128i *) *s);
87
88 // Table lookups:
89 const __m128i hi_nibbles = _mm_and_si128(_mm_srli_epi32(str, 4), mask_2F);
90 const __m128i lo_nibbles = _mm_and_si128(str, mask_2F);
91 const __m128i hi = _mm_shuffle_epi8(lut_hi, hi_nibbles);
92 const __m128i lo = _mm_shuffle_epi8(lut_lo, lo_nibbles);
93
94 // Check for invalid input: if any "and" values from lo and hi are not
95 // zero, fall back on bytewise code to do error checking and reporting:
96 if (_mm_movemask_epi8(_mm_cmpgt_epi8(_mm_and_si128(lo, hi), _mm_setzero_si128())) != 0) {
97 return 0;
98 }
99
100 const __m128i eq_2F = _mm_cmpeq_epi8(str, mask_2F);
101 const __m128i roll = _mm_shuffle_epi8(lut_roll, _mm_add_epi8(eq_2F, hi_nibbles));
102
103 // Now simply add the delta values to the input:
104 str = _mm_add_epi8(str, roll);
105
106 // Reshuffle the input to packed 12-byte output format:
107 str = dec_reshuffle(str);
108
109 // Store the output:
110 _mm_storeu_si128((__m128i *) *o, str);
111
112 *s += 16;
113 *o += 12;
114 *rounds -= 1;
115
116 return 1;
117}
118
119static inline void
120dec_loop_ssse3 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)
121{
122 if (*slen < 24) {
123 return;
124 }
125
126 // Process blocks of 16 bytes per round. Because 4 extra zero bytes are
127 // written after the output, ensure that there will be at least 8 bytes
128 // of input data left to cover the gap. (6 data bytes and up to two
129 // end-of-string markers.)
130 size_t rounds = (*slen - 8) / 16;
131
132 *slen -= rounds * 16; // 16 bytes consumed per round
133 *olen += rounds * 12; // 12 bytes produced per round
134
135 do {
136 if (rounds >= 8) {
137 if (dec_loop_ssse3_inner(s, o, &rounds) &&
138 dec_loop_ssse3_inner(s, o, &rounds) &&
139 dec_loop_ssse3_inner(s, o, &rounds) &&
140 dec_loop_ssse3_inner(s, o, &rounds) &&
141 dec_loop_ssse3_inner(s, o, &rounds) &&
142 dec_loop_ssse3_inner(s, o, &rounds) &&
143 dec_loop_ssse3_inner(s, o, &rounds) &&
144 dec_loop_ssse3_inner(s, o, &rounds)) {
145 continue;
146 }
147 break;
148 }
149 if (rounds >= 4) {
150 if (dec_loop_ssse3_inner(s, o, &rounds) &&
151 dec_loop_ssse3_inner(s, o, &rounds) &&
152 dec_loop_ssse3_inner(s, o, &rounds) &&
153 dec_loop_ssse3_inner(s, o, &rounds)) {
154 continue;
155 }
156 break;
157 }
158 if (rounds >= 2) {
159 if (dec_loop_ssse3_inner(s, o, &rounds) &&
160 dec_loop_ssse3_inner(s, o, &rounds)) {
161 continue;
162 }
163 break;
164 }
165 dec_loop_ssse3_inner(s, o, &rounds);
166 break;
167
168 } while (rounds > 0);
169
170 // Adjust for any rounds that were skipped:
171 *slen += rounds * 16;
172 *olen -= rounds * 12;
173}
174