1#![allow(unsafe_code)]
22
23use crate::alphabet::Symbol;
24#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
25use crate::{
26 engine::{
27 general_purpose::{
28 decode::decode_helper, encode_helper, GeneralPurpose, GeneralPurposeConfig,
29 GeneralPurposeEstimate,
30 },
31 DecodeMetadata, Engine,
32 },
33 DecodeSliceError,
34};
35
36#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
41trait SimdAlphabet {
42 const ENCODE_LUT: [i8; 16];
44 const DECODE_SHIFT_LUT: [i8; 16];
46 const DECODE_MASK_LUT: [u8; 16];
48 const DECODE_FIXUP_CHAR: i8;
50 const DECODE_FIXUP_SHIFT: i8;
52}
53
54#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
56enum Standard {}
57
58#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
60enum UrlSafe {}
61
62#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
63impl SimdAlphabet for Standard {
64 const ENCODE_LUT: [i8; 16] = [
65 65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0,
66 ];
67 const DECODE_SHIFT_LUT: [i8; 16] = [0, 0, 19, 4, -65, -65, -71, -71, 0, 0, 0, 0, 0, 0, 0, 0];
68 const DECODE_MASK_LUT: [u8; 16] = [
69 0xA8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0x54, 0x50, 0x50, 0x50,
70 0x54,
71 ];
72 const DECODE_FIXUP_CHAR: i8 = 0x2F;
73 const DECODE_FIXUP_SHIFT: i8 = 16;
74}
75
76#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
77impl SimdAlphabet for UrlSafe {
78 const ENCODE_LUT: [i8; 16] = [
79 65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -17, 32, 0, 0,
80 ];
81 const DECODE_SHIFT_LUT: [i8; 16] = [0, 0, 17, 4, -65, -65, -71, -71, 0, 0, 0, 0, 0, 0, 0, 0];
82 const DECODE_MASK_LUT: [u8; 16] = [
83 0xA8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0x50, 0x50, 0x54, 0x50,
84 0x70,
85 ];
86 const DECODE_FIXUP_CHAR: i8 = 0x5F;
87 const DECODE_FIXUP_SHIFT: i8 = -32;
88}
89
90#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
92#[derive(Clone, Copy, Debug, PartialEq, Eq)]
93enum SimdKind {
94 Standard,
95 UrlSafe,
96}
97
98#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
101const SIMD_MIN_INPUT_ENCODE: usize = 128;
102#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
103const SIMD_MIN_INPUT_DECODE: usize = 64;
104
105#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
107const BITPOS_LUT: [u8; 16] = [1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0, 0];
108
109#[cfg(target_arch = "x86_64")]
110mod avx2 {
111 use super::{SimdAlphabet, BITPOS_LUT, SIMD_MIN_INPUT_DECODE, SIMD_MIN_INPUT_ENCODE};
112 use core::arch::x86_64::*;
113
114 #[target_feature(enable = "avx2")]
120 pub(super) unsafe fn encode_bulk<A: SimdAlphabet>(
121 input: &[u8],
122 output: &mut [u8],
123 ) -> (usize, usize) {
124 if input.len() < SIMD_MIN_INPUT_ENCODE {
125 return (0, 0);
126 }
127 let lut_data = A::ENCODE_LUT;
128 let lut = _mm256_broadcastsi128_si256(_mm_loadu_si128(lut_data.as_ptr().cast()));
130 #[rustfmt::skip]
131 let shuf = _mm256_setr_epi8(
132 1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10,
133 1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10,
134 );
135 let mask_hi = _mm256_set1_epi32(0x0fc0_fc00_u32 as i32);
136 let mul_hi = _mm256_set1_epi32(0x0400_0040);
137 let mask_lo = _mm256_set1_epi32(0x003f_03f0);
138 let mul_lo = _mm256_set1_epi32(0x0100_0010);
139 let const51 = _mm256_set1_epi8(51);
140 let const25 = _mm256_set1_epi8(25);
141
142 let mut i = 0usize;
143 let mut o = 0usize;
144 while i + 32 <= input.len() && o + 32 <= output.len() {
147 let data = _mm256_loadu_si256(input.as_ptr().add(i).cast());
150 let perm = _mm256_permutevar8x32_epi32(data, _mm256_setr_epi32(0, 1, 2, 3, 3, 4, 5, 6));
154 let inb = _mm256_shuffle_epi8(perm, shuf);
155
156 let t0 = _mm256_and_si256(inb, mask_hi);
157 let t1 = _mm256_mulhi_epu16(t0, mul_hi);
158 let t2 = _mm256_and_si256(inb, mask_lo);
159 let t3 = _mm256_mullo_epi16(t2, mul_lo);
160 let indices = _mm256_or_si256(t1, t3); let reduced = _mm256_subs_epu8(indices, const51);
163 let gt25 = _mm256_cmpgt_epi8(indices, const25);
164 let reduced = _mm256_sub_epi8(reduced, gt25);
165 let ascii = _mm256_add_epi8(indices, _mm256_shuffle_epi8(lut, reduced));
166
167 _mm256_storeu_si256(output.as_mut_ptr().add(o).cast(), ascii);
170 i += 24;
171 o += 32;
172 }
173 (i, o)
174 }
175
176 #[target_feature(enable = "avx2")]
182 pub(super) unsafe fn decode_bulk<A: SimdAlphabet>(
183 input: &[u8],
184 quads_end: usize,
185 output: &mut [u8],
186 ) -> (usize, usize) {
187 if quads_end < SIMD_MIN_INPUT_DECODE {
188 return (0, 0);
189 }
190 let shift_data = A::DECODE_SHIFT_LUT;
191 let mask_data = A::DECODE_MASK_LUT;
192 let shift_lut = _mm256_broadcastsi128_si256(_mm_loadu_si128(shift_data.as_ptr().cast()));
194 let mask_lut = _mm256_broadcastsi128_si256(_mm_loadu_si128(mask_data.as_ptr().cast()));
195 let bitpos_lut = _mm256_broadcastsi128_si256(_mm_loadu_si128(BITPOS_LUT.as_ptr().cast()));
196 let low_nibble_mask = _mm256_set1_epi8(0x0f);
197 let fixup_char = _mm256_set1_epi8(A::DECODE_FIXUP_CHAR);
198 let fixup_shift = _mm256_set1_epi8(A::DECODE_FIXUP_SHIFT);
199 let zero = _mm256_setzero_si256();
200 let merge_mul1 = _mm256_set1_epi32(0x0140_0140);
201 let merge_mul2 = _mm256_set1_epi32(0x0001_1000);
202 #[rustfmt::skip]
203 let pack_shuf = _mm256_setr_epi8(
204 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1,
205 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1,
206 );
207 let lane_compact = _mm256_setr_epi32(0, 1, 2, 4, 5, 6, 6, 6);
208
209 let mut i = 0usize;
210 let mut o = 0usize;
211 while i + 32 <= quads_end && o + 24 <= output.len() {
213 let data = _mm256_loadu_si256(input.as_ptr().add(i).cast());
216
217 let hi_nibbles = _mm256_and_si256(_mm256_srli_epi32(data, 4), low_nibble_mask);
218 let lo_nibbles = _mm256_and_si256(data, low_nibble_mask);
219
220 let m = _mm256_shuffle_epi8(mask_lut, lo_nibbles);
221 let bit = _mm256_shuffle_epi8(bitpos_lut, hi_nibbles);
222 let non_match = _mm256_cmpeq_epi8(_mm256_and_si256(m, bit), zero);
223 if _mm256_movemask_epi8(non_match) != 0 {
224 break;
226 }
227
228 let sh = _mm256_shuffle_epi8(shift_lut, hi_nibbles);
229 let eq_fixup = _mm256_cmpeq_epi8(data, fixup_char);
230 let shift = _mm256_blendv_epi8(sh, fixup_shift, eq_fixup);
231 let values = _mm256_add_epi8(data, shift); let merged = _mm256_maddubs_epi16(values, merge_mul1);
234 let packed = _mm256_madd_epi16(merged, merge_mul2);
235 let shuffled = _mm256_shuffle_epi8(packed, pack_shuf);
236 let compact = _mm256_permutevar8x32_epi32(shuffled, lane_compact);
237
238 let lo = _mm256_castsi256_si128(compact);
240 let hi = _mm256_extracti128_si256(compact, 1);
241 _mm_storeu_si128(output.as_mut_ptr().add(o).cast(), lo);
244 _mm_storel_epi64(output.as_mut_ptr().add(o + 16).cast(), hi);
245 i += 32;
246 o += 24;
247 }
248 (i, o)
249 }
250}
251
252#[cfg(target_arch = "aarch64")]
253mod neon {
254 use super::{SimdAlphabet, BITPOS_LUT, SIMD_MIN_INPUT_DECODE, SIMD_MIN_INPUT_ENCODE};
255 use core::arch::aarch64::*;
256
257 #[target_feature(enable = "neon")]
263 pub(super) unsafe fn encode_bulk<A: SimdAlphabet>(
264 input: &[u8],
265 output: &mut [u8],
266 ) -> (usize, usize) {
267 if input.len() < SIMD_MIN_INPUT_ENCODE {
268 return (0, 0);
269 }
270 let lut_data = A::ENCODE_LUT;
271 let split_bytes: [u8; 16] = [1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10];
272 let split_shuf = vld1q_u8(split_bytes.as_ptr());
274 let translate = vld1q_u8(lut_data.as_ptr().cast());
275 let m1 = vdupq_n_u32(0x0000_fc00);
277 let m2 = vdupq_n_u32(0x0000_03f0);
278 let m3 = vdupq_n_u32(0x0fc0_0000);
279 let m4 = vdupq_n_u32(0x003f_0000);
280 let c51 = vdupq_n_u8(51);
281 let c25 = vdupq_n_s8(25);
282
283 let mut i = 0usize;
284 let mut o = 0usize;
285 while i + 16 <= input.len() && o + 16 <= output.len() {
288 let data = vld1q_u8(input.as_ptr().add(i));
291 let x0 = vreinterpretq_u32_u8(vqtbl1q_u8(data, split_shuf));
292 let x1 = vshrq_n_u16::<10>(vreinterpretq_u16_u32(vandq_u32(x0, m1)));
303 let x2 = vshlq_n_u16::<4>(vreinterpretq_u16_u32(vandq_u32(x0, m2)));
305 let x3 = vshrq_n_u16::<6>(vreinterpretq_u16_u32(vandq_u32(x0, m3)));
307 let x4 = vshlq_n_u16::<8>(vreinterpretq_u16_u32(vandq_u32(x0, m4)));
309 let indices = vreinterpretq_u8_u16(vorrq_u16(vorrq_u16(x1, x2), vorrq_u16(x3, x4)));
310
311 let reduced = vqsubq_u8(indices, c51);
313 let gt25 = vcgtq_s8(vreinterpretq_s8_u8(indices), c25);
314 let reduced = vsubq_u8(reduced, gt25); let ascii = vaddq_u8(indices, vqtbl1q_u8(translate, reduced));
316
317 vst1q_u8(output.as_mut_ptr().add(o), ascii);
320 i += 12;
321 o += 16;
322 }
323 (i, o)
324 }
325
326 #[target_feature(enable = "neon")]
332 pub(super) unsafe fn decode_bulk<A: SimdAlphabet>(
333 input: &[u8],
334 quads_end: usize,
335 output: &mut [u8],
336 ) -> (usize, usize) {
337 if quads_end < SIMD_MIN_INPUT_DECODE {
338 return (0, 0);
339 }
340 let shift_lut = vld1q_u8(A::DECODE_SHIFT_LUT.as_ptr().cast());
342 let mask_lut = vld1q_u8(A::DECODE_MASK_LUT.as_ptr());
343 let bitpos_lut = vld1q_u8(BITPOS_LUT.as_ptr());
344 let low_nibble_mask = vdupq_n_u8(0x0f);
345 let fixup_char_v = vdupq_n_u8(A::DECODE_FIXUP_CHAR as u8);
346 let fixup_shift_v = vdupq_n_u8(A::DECODE_FIXUP_SHIFT as u8);
347 let zero = vdupq_n_u8(0);
348 let mm1 = vdupq_n_u32(0x003f_003f);
349 let mm2 = vdupq_n_u32(0x3f00_3f00);
350 let out_mask = vdupq_n_u32(0x00ff_ffff);
351 let pack_bytes: [u8; 16] = [
352 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, 0x80, 0x80, 0x80, 0x80,
353 ];
354 let pack_shuf = vld1q_u8(pack_bytes.as_ptr());
356
357 let mut i = 0usize;
358 let mut o = 0usize;
359 while i + 16 <= quads_end && o + 12 <= output.len() {
361 let data = vld1q_u8(input.as_ptr().add(i));
363 let hi = vshrq_n_u8::<4>(data);
364 let lo = vandq_u8(data, low_nibble_mask);
365
366 let m = vqtbl1q_u8(mask_lut, lo);
367 let bit = vqtbl1q_u8(bitpos_lut, hi);
368 let non_match = vceqq_u8(vandq_u8(m, bit), zero);
369 if vmaxvq_u8(non_match) != 0 {
370 break;
372 }
373
374 let sh = vqtbl1q_u8(shift_lut, hi);
375 let eq_fixup = vceqq_u8(data, fixup_char_v);
376 let shift = vbslq_u8(eq_fixup, fixup_shift_v, sh);
377 let values = vaddq_u8(data, shift); let v = vreinterpretq_u32_u8(values);
381 let x1 = vandq_u32(v, mm1); let x2 = vandq_u32(v, mm2); let x3 = vorrq_u32(vshlq_n_u32::<18>(x1), vshrq_n_u32::<10>(x1));
384 let x4 = vorrq_u32(vshlq_n_u32::<4>(x2), vshrq_n_u32::<24>(x2));
385 let merged = vandq_u32(vorrq_u32(x3, x4), out_mask);
386 let packed = vqtbl1q_u8(vreinterpretq_u8_u32(merged), pack_shuf); vst1_u8(output.as_mut_ptr().add(o), vget_low_u8(packed));
392 let tail = vgetq_lane_u32::<2>(vreinterpretq_u32_u8(packed));
397 core::ptr::write_unaligned(output.as_mut_ptr().add(o + 8).cast::<u32>(), tail);
398
399 i += 16;
400 o += 12;
401 }
402 (i, o)
403 }
404}
405
406#[cfg(target_arch = "x86_64")]
412#[inline]
413unsafe fn avx2_encode(kind: SimdKind, input: &[u8], output: &mut [u8]) -> (usize, usize) {
414 match kind {
415 SimdKind::Standard => avx2::encode_bulk::<Standard>(input, output),
417 SimdKind::UrlSafe => avx2::encode_bulk::<UrlSafe>(input, output),
418 }
419}
420
421#[cfg(target_arch = "x86_64")]
427#[inline]
428unsafe fn avx2_decode(
429 kind: SimdKind,
430 input: &[u8],
431 quads_end: usize,
432 output: &mut [u8],
433) -> (usize, usize) {
434 match kind {
435 SimdKind::Standard => avx2::decode_bulk::<Standard>(input, quads_end, output),
437 SimdKind::UrlSafe => avx2::decode_bulk::<UrlSafe>(input, quads_end, output),
438 }
439}
440
441#[cfg(target_arch = "aarch64")]
447#[inline]
448unsafe fn neon_encode(kind: SimdKind, input: &[u8], output: &mut [u8]) -> (usize, usize) {
449 match kind {
450 SimdKind::Standard => neon::encode_bulk::<Standard>(input, output),
452 SimdKind::UrlSafe => neon::encode_bulk::<UrlSafe>(input, output),
453 }
454}
455
456#[cfg(target_arch = "aarch64")]
462#[inline]
463unsafe fn neon_decode(
464 kind: SimdKind,
465 input: &[u8],
466 quads_end: usize,
467 output: &mut [u8],
468) -> (usize, usize) {
469 match kind {
470 SimdKind::Standard => neon::decode_bulk::<Standard>(input, quads_end, output),
472 SimdKind::UrlSafe => neon::decode_bulk::<UrlSafe>(input, quads_end, output),
473 }
474}
475
476#[cfg(all(feature = "std", any(target_arch = "x86_64", target_arch = "aarch64")))]
478#[derive(Clone, Copy, Debug)]
479enum Backend {
480 Scalar,
481 #[cfg(target_arch = "x86_64")]
482 Avx2,
483 #[cfg(target_arch = "aarch64")]
484 Neon,
485}
486
487#[cfg(all(feature = "std", any(target_arch = "x86_64", target_arch = "aarch64")))]
494#[derive(Debug, Clone)]
495pub struct Simd {
496 inner: GeneralPurpose,
497 kind: SimdKind,
498 backend: Backend,
499}
500
501#[cfg(all(feature = "std", any(target_arch = "x86_64", target_arch = "aarch64")))]
502impl Simd {
503 #[must_use]
505 pub fn standard(config: GeneralPurposeConfig) -> Self {
506 Self::new(SimdKind::Standard, &crate::alphabet::STANDARD, config)
507 }
508
509 #[must_use]
511 pub fn url_safe(config: GeneralPurposeConfig) -> Self {
512 Self::new(SimdKind::UrlSafe, &crate::alphabet::URL_SAFE, config)
513 }
514
515 fn new(
516 kind: SimdKind,
517 alphabet: &crate::alphabet::Alphabet,
518 config: GeneralPurposeConfig,
519 ) -> Self {
520 #[cfg(target_arch = "x86_64")]
521 let backend = if std::is_x86_feature_detected!("avx2") {
522 Backend::Avx2
523 } else {
524 Backend::Scalar
525 };
526 #[cfg(target_arch = "aarch64")]
527 let backend = if std::arch::is_aarch64_feature_detected!("neon") {
528 Backend::Neon
529 } else {
530 Backend::Scalar
531 };
532
533 Self {
534 inner: GeneralPurpose::new(alphabet, config),
535 kind,
536 backend,
537 }
538 }
539}
540
541#[cfg(all(feature = "std", any(target_arch = "x86_64", target_arch = "aarch64")))]
542impl Engine for Simd {
543 type Config = GeneralPurposeConfig;
544 type DecodeEstimate = GeneralPurposeEstimate;
545
546 fn internal_encode(&self, input: &[u8], output: &mut [u8]) -> usize {
547 let kind = self.kind;
548 match self.backend {
549 Backend::Scalar => self.inner.internal_encode(input, output),
550 #[cfg(target_arch = "x86_64")]
551 Backend::Avx2 => encode_helper(self.inner.encode_table(), input, output, |i, o| {
552 unsafe { avx2_encode(kind, i, o) }
554 }),
555 #[cfg(target_arch = "aarch64")]
556 Backend::Neon => encode_helper(self.inner.encode_table(), input, output, |i, o| {
557 unsafe { neon_encode(kind, i, o) }
559 }),
560 }
561 }
562
563 fn internal_decoded_len_estimate(&self, input_len: usize) -> Self::DecodeEstimate {
564 self.inner.internal_decoded_len_estimate(input_len)
565 }
566
567 fn internal_decode(
568 &self,
569 input: &[u8],
570 output: &mut [u8],
571 estimate: Self::DecodeEstimate,
572 ) -> Result<DecodeMetadata, DecodeSliceError> {
573 let kind = self.kind;
574 match self.backend {
575 Backend::Scalar => self.inner.internal_decode(input, output, estimate),
576 #[cfg(target_arch = "x86_64")]
577 Backend::Avx2 => decode_helper(
578 input,
579 &estimate,
580 output,
581 self.inner.decode_table(),
582 self.inner.config().decode_allow_trailing_bits(),
583 self.inner.padding(),
584 self.inner.config().decode_padding_mode(),
585 |i, end, o| unsafe { avx2_decode(kind, i, end, o) },
587 ),
588 #[cfg(target_arch = "aarch64")]
589 Backend::Neon => decode_helper(
590 input,
591 &estimate,
592 output,
593 self.inner.decode_table(),
594 self.inner.config().decode_allow_trailing_bits(),
595 self.inner.padding(),
596 self.inner.config().decode_padding_mode(),
597 |i, end, o| unsafe { neon_decode(kind, i, end, o) },
599 ),
600 }
601 }
602
603 fn config(&self) -> &Self::Config {
604 self.inner.config()
605 }
606
607 fn padding(&self) -> Symbol {
608 self.inner.padding()
609 }
610}
611
612#[cfg(target_arch = "x86_64")]
619#[derive(Debug, Clone)]
620pub struct Avx2 {
621 inner: GeneralPurpose,
622 kind: SimdKind,
623}
624
625#[cfg(target_arch = "x86_64")]
626impl Avx2 {
627 #[cfg(feature = "std")]
632 #[must_use]
633 pub fn standard(config: GeneralPurposeConfig) -> Option<Self> {
634 if std::is_x86_feature_detected!("avx2") {
635 Some(unsafe { Self::standard_unchecked(config) })
637 } else {
638 None
639 }
640 }
641
642 #[cfg(feature = "std")]
647 #[must_use]
648 pub fn url_safe(config: GeneralPurposeConfig) -> Option<Self> {
649 if std::is_x86_feature_detected!("avx2") {
650 Some(unsafe { Self::url_safe_unchecked(config) })
652 } else {
653 None
654 }
655 }
656
657 #[must_use]
664 pub const unsafe fn standard_unchecked(config: GeneralPurposeConfig) -> Self {
665 Self {
666 inner: GeneralPurpose::new(&crate::alphabet::STANDARD, config),
667 kind: SimdKind::Standard,
668 }
669 }
670
671 #[must_use]
678 pub const unsafe fn url_safe_unchecked(config: GeneralPurposeConfig) -> Self {
679 Self {
680 inner: GeneralPurpose::new(&crate::alphabet::URL_SAFE, config),
681 kind: SimdKind::UrlSafe,
682 }
683 }
684}
685
686#[cfg(target_arch = "x86_64")]
687impl Engine for Avx2 {
688 type Config = GeneralPurposeConfig;
689 type DecodeEstimate = GeneralPurposeEstimate;
690
691 fn internal_encode(&self, input: &[u8], output: &mut [u8]) -> usize {
692 let kind = self.kind;
693 encode_helper(self.inner.encode_table(), input, output, |i, o| {
694 unsafe { avx2_encode(kind, i, o) }
696 })
697 }
698
699 fn internal_decoded_len_estimate(&self, input_len: usize) -> Self::DecodeEstimate {
700 self.inner.internal_decoded_len_estimate(input_len)
701 }
702
703 fn internal_decode(
704 &self,
705 input: &[u8],
706 output: &mut [u8],
707 estimate: Self::DecodeEstimate,
708 ) -> Result<DecodeMetadata, DecodeSliceError> {
709 let kind = self.kind;
710 decode_helper(
711 input,
712 &estimate,
713 output,
714 self.inner.decode_table(),
715 self.inner.config().decode_allow_trailing_bits(),
716 self.inner.padding(),
717 self.inner.config().decode_padding_mode(),
718 |i, end, o| unsafe { avx2_decode(kind, i, end, o) },
720 )
721 }
722
723 fn config(&self) -> &Self::Config {
724 self.inner.config()
725 }
726
727 fn padding(&self) -> Symbol {
728 self.inner.padding()
729 }
730}
731
732#[cfg(target_arch = "aarch64")]
736#[derive(Debug, Clone)]
737pub struct Neon {
738 inner: GeneralPurpose,
739 kind: SimdKind,
740}
741
742#[cfg(target_arch = "aarch64")]
743impl Neon {
744 #[must_use]
746 pub const fn standard(config: GeneralPurposeConfig) -> Self {
747 Self {
748 inner: GeneralPurpose::new(&crate::alphabet::STANDARD, config),
749 kind: SimdKind::Standard,
750 }
751 }
752
753 #[must_use]
755 pub const fn url_safe(config: GeneralPurposeConfig) -> Self {
756 Self {
757 inner: GeneralPurpose::new(&crate::alphabet::URL_SAFE, config),
758 kind: SimdKind::UrlSafe,
759 }
760 }
761}
762
763#[cfg(target_arch = "aarch64")]
764impl Engine for Neon {
765 type Config = GeneralPurposeConfig;
766 type DecodeEstimate = GeneralPurposeEstimate;
767
768 fn internal_encode(&self, input: &[u8], output: &mut [u8]) -> usize {
769 let kind = self.kind;
770 encode_helper(self.inner.encode_table(), input, output, |i, o| {
771 unsafe { neon_encode(kind, i, o) }
773 })
774 }
775
776 fn internal_decoded_len_estimate(&self, input_len: usize) -> Self::DecodeEstimate {
777 self.inner.internal_decoded_len_estimate(input_len)
778 }
779
780 fn internal_decode(
781 &self,
782 input: &[u8],
783 output: &mut [u8],
784 estimate: Self::DecodeEstimate,
785 ) -> Result<DecodeMetadata, DecodeSliceError> {
786 let kind = self.kind;
787 decode_helper(
788 input,
789 &estimate,
790 output,
791 self.inner.decode_table(),
792 self.inner.config().decode_allow_trailing_bits(),
793 self.inner.padding(),
794 self.inner.config().decode_padding_mode(),
795 |i, end, o| unsafe { neon_decode(kind, i, end, o) },
797 )
798 }
799
800 fn config(&self) -> &Self::Config {
801 self.inner.config()
802 }
803
804 fn padding(&self) -> Symbol {
805 self.inner.padding()
806 }
807}