base64_simd/
forgiving.rs
1use crate::ascii::*;
2use crate::STANDARD_FORGIVING;
3use crate::{Error, Out};
4
5use vsimd::tools::slice_mut;
6
7use core::ptr::copy_nonoverlapping;
8
9#[cfg(feature = "alloc")]
10use alloc::vec::Vec;
11
12#[inline]
21pub fn forgiving_decode_inplace(data: &mut [u8]) -> Result<&mut [u8], Error> {
22 let data = remove_ascii_whitespace_inplace(data);
23 STANDARD_FORGIVING.decode_inplace(data)
24}
25
26#[inline]
38pub fn forgiving_decode<'d>(src: &[u8], mut dst: Out<'d, [u8]>) -> Result<&'d mut [u8], Error> {
39 assert!(src.len() <= dst.len());
40
41 let pos = find_non_ascii_whitespace(src);
42 debug_assert!(pos <= src.len());
43
44 if pos == src.len() {
45 return STANDARD_FORGIVING.decode(src, dst);
46 }
47
48 unsafe {
49 let len = src.len();
50 let src = src.as_ptr();
51 let dst = dst.as_mut_ptr();
52
53 copy_nonoverlapping(src, dst, pos);
54
55 let rem = remove_ascii_whitespace_fallback(src.add(pos), len - pos, dst.add(pos));
56 debug_assert!(rem <= len - pos);
57
58 let data = slice_mut(dst, pos + rem);
59 STANDARD_FORGIVING.decode_inplace(data)
60 }
61}
62
63#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
72#[cfg(feature = "alloc")]
73#[inline]
74pub fn forgiving_decode_to_vec(data: &[u8]) -> Result<Vec<u8>, Error> {
75 let pos = find_non_ascii_whitespace(data);
76 debug_assert!(pos <= data.len());
77
78 if pos == data.len() {
79 return STANDARD_FORGIVING.decode_type::<Vec<u8>>(data);
80 }
81
82 let mut vec = Vec::with_capacity(data.len());
83
84 unsafe {
85 let len = data.len();
86 let src = data.as_ptr();
87 let dst = vec.as_mut_ptr();
88
89 copy_nonoverlapping(src, dst, pos);
90
91 let rem = remove_ascii_whitespace_fallback(src.add(pos), len - pos, dst.add(pos));
92 debug_assert!(rem <= len - pos);
93
94 let data = slice_mut(dst, pos + rem);
95 let ans_len = STANDARD_FORGIVING.decode_inplace(data)?.len();
96
97 vec.set_len(ans_len);
98 };
99
100 Ok(vec)
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 use crate::AsOut;
108
109 #[cfg_attr(not(target_arch = "wasm32"), test)]
110 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
111 fn test_forgiving() {
112 use const_str::hex;
113
114 let mut inputs: Vec<&str> = Vec::new();
115 let mut outputs: Vec<&[u8]> = Vec::new();
116
117 {
118 let mut i = |i| inputs.push(i);
119 let mut o = |o| outputs.push(o);
120
121 i("ab");
122 o(&[0x69]);
123
124 i("abc");
125 o(&[0x69, 0xB7]);
126
127 i("abcd");
128 o(&[0x69, 0xB7, 0x1D]);
129
130 i("helloworld");
131 o(&hex!("85 E9 65 A3 0A 2B 95"));
132
133 i(" h e l l o w o r\nl\rd\t");
134 o(&hex!("85 E9 65 A3 0A 2B 95"));
135 }
136
137 for i in 0..inputs.len() {
138 let (src, expected) = (inputs[i], outputs[i]);
139
140 let mut buf = src.to_owned().into_bytes();
141
142 let ans = forgiving_decode_inplace(&mut buf).unwrap();
143 assert_eq!(ans, expected);
144
145 let ans = crate::forgiving_decode(src.as_bytes(), buf.as_out()).unwrap();
146 assert_eq!(ans, expected);
147
148 #[cfg(feature = "alloc")]
149 {
150 let ans = crate::forgiving_decode_to_vec(src.as_bytes()).unwrap();
151 assert_eq!(ans, expected);
152 }
153 }
154 }
155}