Coverage Report

Created: 2026-10-28 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/base64-simd-0.8.0/src/decode.rs
Line
Count
Source
1
use crate::alsw::{STANDARD_ALSW_CHECK_X2, URL_SAFE_ALSW_CHECK_X2};
2
use crate::alsw::{STANDARD_ALSW_DECODE_X2, URL_SAFE_ALSW_DECODE_X2};
3
use crate::{Config, Error, Extra, Kind};
4
use crate::{STANDARD_CHARSET, URL_SAFE_CHARSET};
5
6
use vsimd::alsw::AlswLut;
7
use vsimd::isa::{NEON, SSSE3, WASM128};
8
use vsimd::mask::u8x32_highbit_any;
9
use vsimd::matches_isa;
10
use vsimd::tools::{read, write};
11
use vsimd::vector::V256;
12
use vsimd::SIMD256;
13
14
use core::ops::Not;
15
16
const fn decode_table(charset: &'static [u8; 64]) -> [u8; 256] {
17
    let mut table = [0xff; 256];
18
    let mut i = 0;
19
    while i < charset.len() {
20
        table[charset[i] as usize] = i as u8;
21
        i += 1;
22
    }
23
    table
24
}
25
26
pub const STANDARD_DECODE_TABLE: &[u8; 256] = &decode_table(STANDARD_CHARSET);
27
pub const URL_SAFE_DECODE_TABLE: &[u8; 256] = &decode_table(URL_SAFE_CHARSET);
28
29
#[inline(always)]
30
pub(crate) fn decoded_length(src: &[u8], config: Config) -> Result<(usize, usize), Error> {
31
    if src.is_empty() {
32
        return Ok((0, 0));
33
    }
34
35
    let n = unsafe {
36
        let len = src.len();
37
38
        let count_pad = || {
39
            let last1 = *src.get_unchecked(len - 1);
40
            let last2 = *src.get_unchecked(len - 2);
41
            if last1 == b'=' {
42
                if last2 == b'=' {
43
                    2
44
                } else {
45
                    1
46
                }
47
            } else {
48
                0
49
            }
50
        };
51
52
        match config.extra {
53
            Extra::Pad => {
54
                ensure!(len % 4 == 0);
55
                len - count_pad()
56
            }
57
            Extra::NoPad => len,
58
            Extra::Forgiving => {
59
                if len % 4 == 0 {
60
                    len - count_pad()
61
                } else {
62
                    len
63
                }
64
            }
65
        }
66
    };
67
68
    let m = match n % 4 {
69
        0 => n / 4 * 3,
70
        1 => return Err(Error::new()),
71
        2 => n / 4 * 3 + 1,
72
        3 => n / 4 * 3 + 2,
73
        _ => unsafe { core::hint::unreachable_unchecked() },
74
    };
75
76
    Ok((n, m))
77
}
78
79
#[inline(always)]
80
0
pub unsafe fn decode_ascii8<const WRITE: bool>(src: *const u8, dst: *mut u8, table: *const u8) -> Result<(), Error> {
81
0
    let mut y: u64 = 0;
82
0
    let mut flag = 0;
83
84
0
    let mut i = 0;
85
0
    while i < 8 {
86
0
        let x = read(src, i);
87
0
        let bits = read(table, x as usize);
88
0
        flag |= bits;
89
90
0
        if WRITE {
91
0
            y |= (bits as u64) << (58 - i * 6);
92
0
        }
93
94
0
        i += 1;
95
    }
96
97
0
    if WRITE {
98
0
        dst.cast::<u64>().write_unaligned(y.to_be());
99
0
    }
100
101
0
    ensure!(flag != 0xff);
102
0
    Ok(())
103
0
}
Unexecuted instantiation: base64_simd::decode::decode_ascii8::<false>
Unexecuted instantiation: base64_simd::decode::decode_ascii8::<true>
104
105
#[inline(always)]
106
0
pub unsafe fn decode_ascii4<const WRITE: bool>(src: *const u8, dst: *mut u8, table: *const u8) -> Result<(), Error> {
107
0
    let mut y: u32 = 0;
108
0
    let mut flag = 0;
109
110
0
    let mut i = 0;
111
0
    while i < 4 {
112
0
        let x = read(src, i);
113
0
        let bits = read(table, x as usize);
114
0
        flag |= bits;
115
116
0
        if WRITE {
117
0
            y |= (bits as u32) << (18 - i * 6);
118
0
        }
119
120
0
        i += 1;
121
    }
122
123
0
    if WRITE {
124
0
        let y = y.to_be_bytes();
125
0
        write(dst, 0, y[1]);
126
0
        write(dst, 1, y[2]);
127
0
        write(dst, 2, y[3]);
128
0
    }
129
130
0
    ensure!(flag != 0xff);
131
0
    Ok(())
132
0
}
Unexecuted instantiation: base64_simd::decode::decode_ascii4::<false>
Unexecuted instantiation: base64_simd::decode::decode_ascii4::<true>
133
134
#[inline(always)]
135
0
pub unsafe fn decode_extra<const WRITE: bool>(
136
0
    extra: usize,
137
0
    src: *const u8,
138
0
    dst: *mut u8,
139
0
    table: *const u8,
140
0
    forgiving: bool,
141
0
) -> Result<(), Error> {
142
0
    match extra {
143
0
        0 => {}
144
0
        1 => core::hint::unreachable_unchecked(),
145
        2 => {
146
0
            let [x1, x2] = src.cast::<[u8; 2]>().read();
147
148
0
            let y1 = read(table, x1 as usize);
149
0
            let y2 = read(table, x2 as usize);
150
0
            ensure!((y1 | y2) != 0xff && (forgiving || (y2 & 0x0f) == 0));
151
152
0
            if WRITE {
153
0
                write(dst, 0, (y1 << 2) | (y2 >> 4));
154
0
            }
155
        }
156
        3 => {
157
0
            let [x1, x2, x3] = src.cast::<[u8; 3]>().read();
158
159
0
            let y1 = read(table, x1 as usize);
160
0
            let y2 = read(table, x2 as usize);
161
0
            let y3 = read(table, x3 as usize);
162
0
            ensure!((y1 | y2 | y3) != 0xff && (forgiving || (y3 & 0x03) == 0));
163
164
0
            if WRITE {
165
0
                write(dst, 0, (y1 << 2) | (y2 >> 4));
166
0
                write(dst, 1, (y2 << 4) | (y3 >> 2));
167
0
            }
168
        }
169
0
        _ => core::hint::unreachable_unchecked(),
170
    }
171
0
    Ok(())
172
0
}
Unexecuted instantiation: base64_simd::decode::decode_extra::<false>
Unexecuted instantiation: base64_simd::decode::decode_extra::<true>
173
174
#[inline]
175
0
pub(crate) unsafe fn decode_fallback(
176
0
    mut src: *const u8,
177
0
    mut dst: *mut u8,
178
0
    mut n: usize,
179
0
    config: Config,
180
0
) -> Result<(), Error> {
181
0
    let kind = config.kind;
182
0
    let forgiving = config.extra.forgiving();
183
184
0
    let table = match kind {
185
0
        Kind::Standard => STANDARD_DECODE_TABLE.as_ptr(),
186
0
        Kind::UrlSafe => URL_SAFE_DECODE_TABLE.as_ptr(),
187
    };
188
189
    // n*3/4 >= 6+2
190
0
    while n >= 11 {
191
0
        decode_ascii8::<true>(src, dst, table)?;
192
0
        src = src.add(8);
193
0
        dst = dst.add(6);
194
0
        n -= 8;
195
    }
196
197
0
    let end = src.add(n / 4 * 4);
198
0
    while src < end {
199
0
        decode_ascii4::<true>(src, dst, table)?;
200
0
        src = src.add(4);
201
0
        dst = dst.add(3);
202
    }
203
0
    n %= 4;
204
205
0
    decode_extra::<true>(n, src, dst, table, forgiving)
206
0
}
207
208
#[inline(always)]
209
pub(crate) unsafe fn decode_simd<S: SIMD256>(
210
    s: S,
211
    mut src: *const u8,
212
    mut dst: *mut u8,
213
    mut n: usize,
214
    config: Config,
215
) -> Result<(), Error> {
216
    let kind = config.kind;
217
218
    let (check_lut, decode_lut) = match kind {
219
        Kind::Standard => (STANDARD_ALSW_CHECK_X2, STANDARD_ALSW_DECODE_X2),
220
        Kind::UrlSafe => (URL_SAFE_ALSW_CHECK_X2, URL_SAFE_ALSW_DECODE_X2),
221
    };
222
223
    // n*3/4 >= 24+4
224
    while n >= 38 {
225
        let x = s.v256_load_unaligned(src);
226
        let y = try_!(decode_ascii32(s, x, check_lut, decode_lut));
227
228
        let (y1, y2) = y.to_v128x2();
229
        s.v128_store_unaligned(dst, y1);
230
        s.v128_store_unaligned(dst.add(12), y2);
231
232
        src = src.add(32);
233
        dst = dst.add(24);
234
        n -= 32;
235
    }
236
237
    decode_fallback(src, dst, n, config)
238
}
239
240
#[inline(always)]
241
fn merge_bits_x2<S: SIMD256>(s: S, x: V256) -> V256 {
242
    // x : {00aaaaaa|00bbbbbb|00cccccc|00dddddd} x8
243
244
    let y = if matches_isa!(S, SSSE3) {
245
        let m1 = s.u16x16_splat(u16::from_le_bytes([0x40, 0x01]));
246
        let x1 = s.i16x16_maddubs(x, m1);
247
        // x1: {aabbbbbb|0000aaaa|ccdddddd|0000cccc} x8
248
249
        let m2 = s.u32x8_splat(u32::from_le_bytes([0x00, 0x10, 0x01, 0x00]));
250
        s.i16x16_madd(x1, m2)
251
        // {ccdddddd|bbbbcccc|aaaaaabb|00000000} x8
252
    } else if matches_isa!(S, NEON | WASM128) {
253
        let m1 = s.u32x8_splat(u32::from_le_bytes([0x3f, 0x00, 0x3f, 0x00]));
254
        let x1 = s.v256_and(x, m1);
255
        // x1: {00aaaaaa|00000000|00cccccc|00000000} x8
256
257
        let m2 = s.u32x8_splat(u32::from_le_bytes([0x00, 0x3f, 0x00, 0x3f]));
258
        let x2 = s.v256_and(x, m2);
259
        // x2: {00000000|00bbbbbb|00000000|00dddddd} x8
260
261
        let x3 = s.v256_or(s.u32x8_shl::<18>(x1), s.u32x8_shr::<10>(x1));
262
        // x3: {cc000000|0000cccc|aaaaaa00|00000000} x8
263
264
        let x4 = s.v256_or(s.u32x8_shl::<4>(x2), s.u32x8_shr::<24>(x2));
265
        // x4: {00dddddd|bbbb0000|000000bb|dddd0000}
266
267
        let mask = s.u32x8_splat(u32::from_le_bytes([0xff, 0xff, 0xff, 0x00]));
268
        s.v256_and(s.v256_or(x3, x4), mask)
269
        // {ccdddddd|bbbbcccc|aaaaaabb|00000000} x8
270
    } else {
271
        unreachable!()
272
    };
273
274
    const SHUFFLE: V256 = V256::double_bytes([
275
        0x02, 0x01, 0x00, 0x06, 0x05, 0x04, 0x0a, 0x09, //
276
        0x08, 0x0e, 0x0d, 0x0c, 0x80, 0x80, 0x80, 0x80, //
277
    ]);
278
    s.u8x16x2_swizzle(y, SHUFFLE)
279
    // {AAAB|BBCC|CDDD|0000|EEEF|FFGG|GHHH|0000}
280
}
281
282
#[inline(always)]
283
fn decode_ascii32<S: SIMD256>(s: S, x: V256, check: AlswLut<V256>, decode: AlswLut<V256>) -> Result<V256, Error> {
284
    let (c1, c2) = vsimd::alsw::decode_ascii_xn(s, x, check, decode);
285
    let y = merge_bits_x2(s, c2);
286
    ensure!(u8x32_highbit_any(s, c1).not());
287
    Ok(y)
288
}