1use core::mem;
4use core::ptr::NonNull;
5use super::{PAD, encode_len, decode_len, REVERSE_TABLE_SIZE, build_reverse_table, Codec};
6
7#[cold]
8#[inline(never)]
9fn unlikely_false() -> bool {
10 false
11}
12
13pub(crate) fn encode_inner(table: &[u8; 64], src: &[u8], dst: NonNull<u8>, len: &mut usize) {
14 let mut it = src.as_ptr();
15 let mut cursor = dst.as_ptr();
16 let mut remain_len = src.len();
17 macro_rules! encode_it {
18 () => {
19 (cursor as *mut u32).write_unaligned(u32::from_ne_bytes([
21 *table.as_ptr().add(
22 (*it).wrapping_shr(2) as usize
23 ),
24 *table.as_ptr().add(
25 (((*it) & 0x03).wrapping_shl(4) | (*it.add(1)).wrapping_shr(4)) as usize
26 ),
27 *table.as_ptr().add(
28 (((*it.add(1)) & 0x0f).wrapping_shl(2) | (*it.add(2)).wrapping_shr(6)) as usize
29 ),
30 *table.as_ptr().add(
31 ((*it.add(2)) & 0x3f) as usize
32 )
33 ]));
34 cursor = cursor.add(mem::size_of::<u32>());
35 it = it.add(3);
36 }
37 }
38
39 const CHUNK_SIZE: usize = 3;
40 const EIGHT_CHUNKS: usize = CHUNK_SIZE * 8;
41 const FOUR_CHUNKS: usize = CHUNK_SIZE * 4;
42
43 while remain_len >= EIGHT_CHUNKS {
44 unsafe {
45 encode_it!();
46 encode_it!();
47 encode_it!();
48 encode_it!();
49 encode_it!();
50 encode_it!();
51 encode_it!();
52 encode_it!();
53 }
54
55 remain_len -= EIGHT_CHUNKS;
56 }
57
58 while remain_len >= FOUR_CHUNKS {
59 unsafe {
60 encode_it!();
61 encode_it!();
62 encode_it!();
63 encode_it!();
64 }
65
66 remain_len -= FOUR_CHUNKS;
67 }
68
69 while remain_len >= CHUNK_SIZE {
70 unsafe {
71 encode_it!();
72 }
73
74 remain_len -= CHUNK_SIZE;
75 }
76
77 match remain_len {
78 1 => unsafe {
79 (cursor as *mut u32).write_unaligned(u32::from_ne_bytes([
80 *table.as_ptr().add(
81 (*it).wrapping_shr(2) as usize
82 ),
83 *table.as_ptr().add(
84 ((*it) & 0x03).wrapping_shl(4) as usize
85 ),
86 PAD,
87 PAD
88 ]));
89 cursor = cursor.add(mem::size_of::<u32>());
90 },
91 2 => unsafe {
92 (cursor as *mut u32).write_unaligned(u32::from_ne_bytes([
93 *table.as_ptr().add(
94 (*it).wrapping_shr(2) as usize
95 ),
96 *table.as_ptr().add(
97 (((*it) & 0x03).wrapping_shl(4) | (*it.add(1)).wrapping_shr(4)) as usize
98 ),
99 *table.as_ptr().add(
100 ((*it.add(1)) & 0x0f).wrapping_shl(2) as usize
101 ),
102 PAD
103 ]));
104 cursor = cursor.add(mem::size_of::<u32>());
105 },
106 _ => debug_assert_eq!(remain_len, 0),
107 }
108
109 *len = cursor as usize - dst.as_ptr() as usize;
110}
111
112#[inline]
113pub unsafe fn encode(table: &[u8; 64], src: &[u8], dst: NonNull<u8>, len: &mut usize) -> bool {
126 let required_len = encode_len(src.len());
127 if required_len < src.len() {
128 return unlikely_false();
130 } else if required_len > *len {
131 *len = required_len;
132 return false;
133 }
134
135 encode_inner(table, src, dst, len);
136 true
137}
138
139pub(crate) fn decode_inner_with_rev(reverse_table: &[i8; REVERSE_TABLE_SIZE], mut src: &[u8], dst: NonNull<u8>, len: &mut usize) -> bool {
140 let mut prev_ch;
141 let mut cursor = dst.as_ptr();
142
143 'main: loop {
144 macro_rules! store_ch {
145 (last: $ch:expr) => {};
146 (gogo: $ch:expr) => {
147 prev_ch = $ch;
148 }
149 }
150
151 macro_rules! transform_valid_base64 {
152 ($kind:ident => $ch:expr => 0) => {{
153 prev_ch = $ch;
154 }};
155 ($kind:ident => $ch:expr => 1) => {{
156 *cursor = (prev_ch).wrapping_shl(2) | ($ch).wrapping_shr(4);
157 store_ch!($kind: $ch);
158 }};
159 ($kind:ident => $ch:expr => 2) => {{
160 *(cursor.add(1)) = (prev_ch).wrapping_shl(4) | ($ch).wrapping_shr(2);
161 store_ch!($kind: $ch);
162 }};
163 ($kind:ident => $ch:expr => 3) => {{
164 *(cursor.add(2)) = prev_ch.wrapping_shl(6) | ($ch);
165 }}
166 }
167
168 macro_rules! get_base64_byte {
169 ($kind:ident => $src:ident[$offset:literal + $idx:tt]) => {{
170 match *$src.as_ptr().add($offset + $idx) {
171 PAD => {
172 cursor = cursor.add(usize::saturating_sub($idx, 1));
173 break 'main;
174 },
175 ch => match *reverse_table.as_ptr().add(ch as usize) {
176 -1 => return unlikely_false(),
177 pos => transform_valid_base64!($kind => pos as u8 => $idx)
178 },
179 }
180 }}
181 }
182
183 macro_rules! decode_it {
184 ($offset:literal) => {{
185 get_base64_byte!(gogo => src[$offset + 0]);
186 get_base64_byte!(gogo => src[$offset + 1]);
187 get_base64_byte!(gogo => src[$offset + 2]);
188 get_base64_byte!(gogo => src[$offset + 3]);
189 cursor = cursor.add(3);
190 }}
191 }
192
193 match src.len() {
194 0 => {
195 break;
196 },
197 1 => {
198 break;
200 },
201 2 => unsafe {
202 get_base64_byte!(gogo => src[0 + 0]);
203 get_base64_byte!(last => src[0 + 1]);
204 cursor = cursor.add(1);
205 break;
206 },
207 3 => unsafe {
208 get_base64_byte!(gogo => src[0 + 0]);
209 get_base64_byte!(gogo => src[0 + 1]);
210 get_base64_byte!(last => src[0 + 2]);
211 cursor = cursor.add(2);
212 break;
213 },
214 _ => {
215 const CHUNK_SIZE: usize = 4;
216 const EIGHT_CHUNKS: usize = CHUNK_SIZE * 8;
217 const FOUR_CHUNKS: usize = CHUNK_SIZE * 4;
218
219 while src.len() >= EIGHT_CHUNKS {
220 unsafe {
221 decode_it!(0);
222 decode_it!(4);
223 decode_it!(8);
224 decode_it!(12);
225 decode_it!(16);
226 decode_it!(20);
227 decode_it!(24);
228 decode_it!(28);
229 }
230 src = &src[EIGHT_CHUNKS..];
231 }
232
233 while src.len() >= FOUR_CHUNKS {
234 unsafe {
235 decode_it!(0);
236 decode_it!(4);
237 decode_it!(8);
238 decode_it!(12);
239 }
240 src = &src[FOUR_CHUNKS..];
241 }
242
243 while src.len() >= CHUNK_SIZE {
244 unsafe {
245 decode_it!(0);
246 }
247 src = &src[CHUNK_SIZE..];
248 }
249 }
250 }
251 }
252
253 *len = cursor as usize - dst.as_ptr() as usize;
254 true
255
256}
257
258#[inline(always)]
259pub(crate) fn decode_inner(table: &[u8; 64], src: &[u8], dst: NonNull<u8>, len: &mut usize) -> bool {
260 decode_inner_with_rev(&build_reverse_table(table), src, dst, len)
261}
262
263#[inline]
264pub unsafe fn decode(table: &[u8; 64], src: &[u8], dst: NonNull<u8>, len: &mut usize) -> bool {
276 let required_len = decode_len(src);
277
278 if required_len == 0 {
279 *len = 0;
280 return true;
281 }
282
283 decode_inner(table, src, dst, len)
284}
285
286impl<'a> Codec<'a> {
287 #[inline(always)]
288 pub unsafe fn encode_to_raw(&self, src: &[u8], dst: NonNull<u8>, len: &mut usize) -> bool {
301 encode(self.table, src, dst, len)
302 }
303
304 #[inline]
305 pub unsafe fn decode_to_raw(&self, src: &[u8], dst: NonNull<u8>, len: &mut usize) -> bool {
317 let required_len = decode_len(src);
318
319 if required_len == 0 {
320 *len = 0;
321 return true;
322 }
323
324 decode_inner_with_rev(&self.reverse, src, dst, len)
325 }
326}