Skip to content

Commit

Permalink
Add support for portable-simd as a implementation backend
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes committed Aug 28, 2023
1 parent d5adb28 commit 5d8fb1c
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 2 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,10 @@ serde = ["hex?/serde", "dep:serde"]
# This should not be needed most of the time.
hex = ["dep:hex"]

# Support for the `portable-simd` nightly feature.
# Note that `-Zbuild-std` may be necessary to unlock better performance than
# the specialized implementations.
portable-simd = []

# Nightly features for better performance.
nightly = []
3 changes: 2 additions & 1 deletion fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ path = "fuzz_targets/fuzz_const_hex.rs"
test = false
doc = false

[workspace]
[features]
portable-simd = ["const-hex/portable-simd"]
10 changes: 9 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(feature = "nightly", feature(core_intrinsics, inline_const))]
#![cfg_attr(feature = "portable-simd", feature(portable_simd))]
#![allow(
clippy::cast_lossless,
clippy::inline_always,
Expand All @@ -39,7 +40,10 @@ use alloc::{string::String, vec::Vec};

// The main encoding and decoding functions.
cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
if #[cfg(feature = "portable-simd")] {
mod portable_simd;
use portable_simd as imp;
} else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
mod x86;
use x86 as imp;
} else {
Expand Down Expand Up @@ -134,6 +138,10 @@ pub const HEX_CHARS_UPPER: &[u8; 16] = b"0123456789ABCDEF";
/// [`u8::MAX`] is used for invalid values.
pub const HEX_DECODE_LUT: &[u8; 256] = &make_decode_lut();

pub unsafe fn encode2(input: &[u8], output: *mut u8) {
imp::encode::<false>(input, output)
}

/// A correctly sized stack allocation for the formatted bytes to be written
/// into.
///
Expand Down
48 changes: 48 additions & 0 deletions src/portable_simd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use crate::default;
use core::simd::{u8x16, Swizzle};
use core::slice;

const CHUNK_SIZE: usize = core::mem::size_of::<u8x16>();

/// Hex encoding function using [`std::simd`][core::simd].
///
/// # Safety
///
/// `output` must be a valid pointer to at least `2 * input.len()` bytes.
pub(super) unsafe fn encode<const UPPER: bool>(input: &[u8], output: *mut u8) {
let mut i = 0;
let (prefix, chunks, suffix) = input.as_simd::<CHUNK_SIZE>();

default::encode::<UPPER>(prefix, output);
i += prefix.len();

let hex_table = u8x16::from_array(*crate::get_chars_table::<UPPER>());
for &chunk in chunks {
// Load input bytes and mask to nibbles.
let mut lo = chunk & u8x16::splat(15);
let mut hi = chunk >> u8x16::splat(4);

// Lookup the corresponding ASCII hex digit for each nibble.
lo = hex_table.swizzle_dyn(lo);
hi = hex_table.swizzle_dyn(hi);

// Interleave the nibbles ([hi[0], lo[0], hi[1], lo[1], ...]).
let (hex_lo, hex_hi) = u8x16::interleave(hi, lo);

// Store result into the output buffer.
hex_lo.copy_to_slice(slice::from_raw_parts_mut(output.add(i), CHUNK_SIZE));
i += CHUNK_SIZE;
hex_hi.copy_to_slice(slice::from_raw_parts_mut(output.add(i), CHUNK_SIZE));
i += CHUNK_SIZE;
}

default::encode::<UPPER>(suffix, output.add(i));
}

pub(super) use default::decode;

struct HexCharsTable<const UPPER: bool>;

impl<const UPPER: bool> Swizzle<16, 16> for HexCharsTable<UPPER> {
const INDEX: [usize; 16] = [0; 16];
}

0 comments on commit 5d8fb1c

Please sign in to comment.