rusty_erasure_accel/lib.rs
1//! rusty_erasure-accel — the workspace's ONLY unsafe crate: hand-written SIMD
2//! twins of the scalar kernels in `rusty_erasure-core`.
3//!
4//! The named reason these exist (codec-vectorize-kernel step 0): the GF(2^8)
5//! multiply here is either the PSHUFB/TBL/swizzle nibble-table algorithm or a
6//! `GF2P8AFFINEQB` affine transform — *different algorithms* from the scalar
7//! per-byte lookup, in the polynomial/table class the compiler cannot derive.
8//! Auto-vectorization is structurally unavailable; hand-written kernels are
9//! the only route.
10//!
11//! Per-arch inventory (the per-arch-parity non-negotiable):
12//! - [`x86`] — SSSE3/AVX2 nibble + AVX2-GFNI affine (runtime-dispatched)
13//! - [`aarch64`] — NEON TBL nibble (baseline, no dispatch needed)
14//! - [`wasm`] — SIMD128 swizzle nibble (compile-time `+simd128`)
15//!
16//! Discipline: every kernel set is exposed only through checked constructors;
17//! every unsafe block carries a `// SAFETY:` invariant and the safe wrappers
18//! re-assert slice lengths; the scalar set in core stays the permanent oracle
19//! (`*_matches_scalar` gates every set); every set counts its source bytes
20//! into [`ACCEL_CENSUS_BYTES`] — the reach census is always on.
21
22#![deny(missing_docs)]
23
24use core::sync::atomic::AtomicU64;
25
26use rusty_erasure_core::kernel::Kernels;
27
28pub mod aarch64;
29#[cfg(any(
30 target_arch = "x86_64",
31 target_arch = "aarch64",
32 all(target_arch = "wasm32", target_feature = "simd128")
33))]
34mod tail;
35pub mod wasm;
36pub mod x86;
37
38/// Census counter shared by every accel kernel set: source bytes processed.
39/// One relaxed add per call, never per element.
40pub static ACCEL_CENSUS_BYTES: AtomicU64 = AtomicU64::new(0);
41
42/// The `(xor_gen, pq_gen)` function pair a RAID dispatch returns.
43pub type RaidKernels = (fn(&[&[u8]], &mut [u8]), fn(&[&[u8]], &mut [u8], &mut [u8]));
44
45/// The best kernel set for the running CPU on THIS architecture, or `None`
46/// when no SIMD set applies — callers fall back to `Kernels::scalar()`.
47pub fn kernels() -> Option<Kernels> {
48 #[cfg(target_arch = "x86_64")]
49 {
50 x86::kernels()
51 }
52 #[cfg(target_arch = "aarch64")]
53 {
54 aarch64::kernels()
55 }
56 #[cfg(target_arch = "wasm32")]
57 {
58 wasm::kernels()
59 }
60 #[cfg(not(any(
61 target_arch = "x86_64",
62 target_arch = "aarch64",
63 target_arch = "wasm32"
64 )))]
65 {
66 None
67 }
68}
69
70/// The best RAID kernel pair (xor_gen, pq_gen) for this architecture, or
71/// `None` when only the scalar core applies. Byte-identical to
72/// `rusty_erasure_core::raid` on every arch (oracle-tested).
73pub fn raid_kernels() -> Option<RaidKernels> {
74 #[cfg(target_arch = "x86_64")]
75 {
76 x86::raid_kernels()
77 }
78 #[cfg(target_arch = "aarch64")]
79 {
80 aarch64::raid_kernels()
81 }
82 #[cfg(target_arch = "wasm32")]
83 {
84 wasm::raid_kernels()
85 }
86 #[cfg(not(any(
87 target_arch = "x86_64",
88 target_arch = "aarch64",
89 target_arch = "wasm32"
90 )))]
91 {
92 None
93 }
94}
95
96/// The best kernel set consuming ISA-L NIBBLE-format tables (the compat
97/// layer's requirement — its callers hand it `ec_init_tables`-format tables
98/// by contract, and GFNI's affine tables must never be mixed in).
99pub fn kernels_nibble() -> Option<Kernels> {
100 #[cfg(target_arch = "x86_64")]
101 {
102 x86::kernels_nibble()
103 }
104 // Every non-x86 set is nibble-format already.
105 #[cfg(not(target_arch = "x86_64"))]
106 {
107 kernels()
108 }
109}