SIGN IN SIGN UP

feat: add SIMD engines (Simd, Avx2, Neon) behind a simd-unsafe feature

Add SIMD-accelerated base64 for the STANDARD and URL_SAFE alphabets, controlled
by the `simd-unsafe` feature (off by default). It is the only feature that uses
`unsafe`; without it the crate is `#![forbid(unsafe_code)]`. When enabled,
`unsafe` is confined to the `engine::simd` module and the crate uses
`#![deny(unsafe_code)]`.

Three engines are provided, all reusing the scalar `GeneralPurpose` kernels for
the tail:

- `Simd`: detects the best instruction set at runtime (AVX2 on x86_64, NEON on
  aarch64) and falls back to the scalar engine. Needs `std` for the detection.

- `Avx2` / `Neon`: target one instruction set with no runtime detection, so they
  work in `no_std`. `Avx2` has checked `standard` / `url_safe` constructors (std)
  plus `unsafe` `*_unchecked` variants; NEON is mandatory on aarch64 so `Neon`'s
  constructors are safe.

Only the STANDARD and URL_SAFE alphabets are accelerated, so the engines have
dedicated `::standard` / `::url_safe` constructors (no `&Alphabet` parameter); the
per-alphabet lookup tables are the associated constants of a sealed `SimdAlphabet`
trait so the generic kernels can inline them. `GeneralPurpose` stays a pure-scalar
engine (also aliased as `Scalar`); the encode/decode helpers take an optional
SIMD-prefix step that is a no-op for it, and the scalar encode tail is factored
out so it is not monomorphized per engine.

Instruction counts below were gathered with gungraun (Callgrind); wall-clock
times with the criterion benchmarks (AVX2, x86-64-v3, this machine):

| Operation    | Input | Instructions (scalar -> SIMD)  | Wall-clock (scalar -> SIMD) |
| ------------ | ----- | ------------------------------ | --------------------------- |
| encode_slice | 3 KiB | 22,245 -> 4,741        (4.7x)  | 1.73 us -> 468 ns   (3.7x)  |
| encode_slice | 3 MiB | 22,282,621 -> 3,802,013 (5.9x) | 1.74 ms -> 456 us   (3.8x)  |
| decode_slice | 3 KiB | 28,620 -> 4,817        (5.9x)  | 1.49 us -> 230 ns   (6.5x)  |
| decode_slice | 3 MiB | 28,705,264 -> 4,063,989 (7.1x) | 1.54 ms -> 216 us   (7.1x)  |
M
Martin Taillefer committed
ce2281bb31073bf0e4b8aca47990239574bd3fe1
Parent: 81fa4f1