diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06283d5..ea7364e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,8 +20,15 @@ jobs: strategy: fail-fast: false matrix: - target: [aarch64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-unknown-linux-gnu] + target: + - aarch64-unknown-linux-gnu + - i686-unknown-linux-gnu + - x86_64-unknown-linux-gnu + - wasm32-wasip1 rust: [nightly, stable, 1.64] + exclude: + - target: wasm32-wasip1 + rust: 1.64 timeout-minutes: 30 steps: - uses: actions/checkout@v4 diff --git a/Cargo.toml b/Cargo.toml index 95c6c33..6244d42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,9 @@ hex-literal = "0.4" serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +getrandom = { version = "*", features = ["js"] } + [features] default = ["std"] std = ["hex?/std", "serde?/std", "proptest?/std", "alloc"] diff --git a/src/arch/aarch64.rs b/src/arch/aarch64.rs index a04bad5..b381b0c 100644 --- a/src/arch/aarch64.rs +++ b/src/arch/aarch64.rs @@ -80,7 +80,7 @@ pub(crate) unsafe fn check_neon(input: &[u8]) -> bool { let ascii_la = vdupq_n_u8(b'a' - 1); let ascii_lf = vdupq_n_u8(b'f' + 1); - generic::check_unaligned_chunks(input, |chunk| { + generic::check_unaligned_chunks(input, |chunk: uint8x16_t| { let ge0 = vcgtq_u8(chunk, ascii_zero); let le9 = vcltq_u8(chunk, ascii_nine); let valid_digit = vandq_u8(ge0, le9); diff --git a/src/arch/mod.rs b/src/arch/mod.rs index e614e3c..e223620 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -13,6 +13,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "aarch64")] { pub(crate) mod aarch64; pub(crate) use aarch64 as imp; + } else if #[cfg(target_arch = "wasm32")] { + pub(crate) mod wasm32; + pub(crate) use wasm32 as imp; } else { pub(crate) use generic as imp; } diff --git a/src/arch/portable_simd.rs b/src/arch/portable_simd.rs index 64bd040..4f2b907 100644 --- a/src/arch/portable_simd.rs +++ b/src/arch/portable_simd.rs @@ -44,7 +44,7 @@ pub(crate) unsafe fn encode(input: &[u8], output: *mut u8) { } pub(crate) fn check(input: &[u8]) -> bool { - generic::check_unaligned_chunks::(input, |chunk| { + generic::check_unaligned_chunks(input, |chunk: Simd| { let valid_digit = chunk.simd_ge(Simd::splat(b'0')) & chunk.simd_le(Simd::splat(b'9')); let valid_upper = chunk.simd_ge(Simd::splat(b'A')) & chunk.simd_le(Simd::splat(b'F')); let valid_lower = chunk.simd_ge(Simd::splat(b'a')) & chunk.simd_le(Simd::splat(b'f')); diff --git a/src/arch/wasm32.rs b/src/arch/wasm32.rs new file mode 100644 index 0000000..b3107c8 --- /dev/null +++ b/src/arch/wasm32.rs @@ -0,0 +1,40 @@ +use super::generic; +use core::arch::wasm32::*; + +pub(crate) const USE_CHECK_FN: bool = false; + +pub(crate) use generic::{decode_checked, decode_unchecked, encode}; + +#[inline(always)] +fn is_available() -> bool { + cfg!(target_feature = "simd128") +} + +#[inline] +pub(crate) fn check(input: &[u8]) -> bool { + if !is_available() { + return generic::check(input); + } + unsafe { check_simd128(input) } +} + +#[target_feature(enable = "simd128")] +unsafe fn check_simd128(input: &[u8]) -> bool { + generic::check_unaligned_chunks(input, |chunk: v128| { + let ge0 = u8x16_ge(chunk, u8x16_splat(b'0')); + let le9 = u8x16_le(chunk, u8x16_splat(b'9')); + let valid_digit = v128_and(ge0, le9); + + let geua = u8x16_ge(chunk, u8x16_splat(b'A')); + let leuf = u8x16_le(chunk, u8x16_splat(b'F')); + let valid_upper = v128_and(geua, leuf); + + let gela = u8x16_ge(chunk, u8x16_splat(b'a')); + let lelf = u8x16_le(chunk, u8x16_splat(b'f')); + let valid_lower = v128_and(gela, lelf); + + let valid_letter = v128_or(valid_lower, valid_upper); + let valid = v128_or(valid_digit, valid_letter); + u8x16_all_true(valid) + }) +} diff --git a/src/arch/x86.rs b/src/arch/x86.rs index bdf5082..1029dcf 100644 --- a/src/arch/x86.rs +++ b/src/arch/x86.rs @@ -99,7 +99,7 @@ unsafe fn check_sse2(input: &[u8]) -> bool { let ascii_la = _mm_set1_epi8((b'a' - 1) as i8); let ascii_lf = _mm_set1_epi8((b'f' + 1) as i8); - generic::check_unaligned_chunks(input, |chunk| { + generic::check_unaligned_chunks(input, |chunk: __m128i| { let ge0 = _mm_cmpgt_epi8(chunk, ascii_zero); let le9 = _mm_cmplt_epi8(chunk, ascii_nine); let valid_digit = _mm_and_si128(ge0, le9);