#![allow(clippy::not_unsafe_ptr_arg_deref)]
use axhash_core::hasher::AxHasher;
use axhash_core::hasher::api::{axhash, axhash_seeded};
use axhash_core::{RuntimeBackend, runtime_backend, runtime_has_simd};
use core::ffi::c_char;
use core::hash::Hasher;
use core::ptr::NonNull;
extern crate alloc;
use alloc::boxed::Box;
pub enum AxHashState {}
#[repr(C)]
pub struct AxHashIovec {
pub ptr: *const u8,
pub len: usize,
}
#[non_exhaustive]
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AxHashRuntimeBackend {
Scalar = 0,
Aarch64Neon = 1,
X86_64Avx2 = 2,
}
const MAX_BATCH: usize = 1 << 16;
#[inline(always)]
fn as_state_ptr(hasher: AxHasher) -> *mut AxHashState {
Box::into_raw(Box::new(hasher)).cast()
}
#[inline(always)]
unsafe fn state_mut<'a>(state: *mut AxHashState) -> Option<&'a mut AxHasher> {
NonNull::new(state.cast()).map(|p| unsafe { &mut *p.as_ptr() })
}
#[inline(always)]
unsafe fn slice_from_raw<'a>(ptr: *const u8, len: usize) -> &'a [u8] {
unsafe { core::slice::from_raw_parts(ptr, len) }
}
#[inline(always)]
fn is_invalid_input(ptr: *const u8, len: usize) -> bool {
len != 0 && ptr.is_null()
}
impl From<RuntimeBackend> for AxHashRuntimeBackend {
#[inline(always)]
fn from(b: RuntimeBackend) -> Self {
match b {
RuntimeBackend::Scalar => Self::Scalar,
RuntimeBackend::Aarch64Neon => Self::Aarch64Neon,
RuntimeBackend::X86_64Avx2 => Self::X86_64Avx2,
_ => Self::Scalar,
}
}
}
#[cold]
fn fail_u64() -> u64 {
0
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_ffi_version() -> *const c_char {
static VERSION: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
VERSION.as_ptr().cast()
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_bytes(bytes: *const u8, len: usize) -> u64 {
if is_invalid_input(bytes, len) {
return fail_u64();
}
if len == 0 {
return axhash(&[]);
}
unsafe { axhash(slice_from_raw(bytes, len)) }
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_bytes_seeded(bytes: *const u8, len: usize, seed: u64) -> u64 {
if is_invalid_input(bytes, len) {
return fail_u64();
}
if len == 0 {
return axhash_seeded(&[], seed);
}
unsafe { axhash_seeded(slice_from_raw(bytes, len), seed) }
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_batch_seeded(
iovecs: *const AxHashIovec,
count: usize,
seed: u64,
out_hashes: *mut u64,
) {
if iovecs.is_null() || out_hashes.is_null() || count == 0 {
return;
}
let count = count.min(MAX_BATCH);
let jobs = unsafe { core::slice::from_raw_parts(iovecs, count) };
let outs = unsafe { core::slice::from_raw_parts_mut(out_hashes, count) };
for (job, out) in jobs.iter().zip(outs.iter_mut()) {
*out = if is_invalid_input(job.ptr, job.len) {
0
} else if job.len == 0 {
axhash_seeded(&[], seed)
} else {
unsafe { axhash_seeded(slice_from_raw(job.ptr, job.len), seed) }
};
}
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_hasher_new() -> *mut AxHashState {
as_state_ptr(AxHasher::new())
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_hasher_new_seeded(seed: u64) -> *mut AxHashState {
as_state_ptr(AxHasher::new_with_seed(seed))
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_hasher_reset(state: *mut AxHashState, seed: u64) -> bool {
let hasher = unsafe { state_mut(state) };
let Some(hasher) = hasher else {
return false;
};
*hasher = AxHasher::new_with_seed(seed);
true
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_hasher_write(
state: *mut AxHashState,
bytes: *const u8,
len: usize,
) -> bool {
if is_invalid_input(bytes, len) {
return false;
}
if len == 0 {
return true;
}
let hasher = unsafe { state_mut(state) };
let Some(hasher) = hasher else {
return false;
};
unsafe {
hasher.write(slice_from_raw(bytes, len));
}
true
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_hasher_finish(state: *mut AxHashState) -> u64 {
let hasher = unsafe { state_mut(state) };
let Some(hasher) = hasher else {
return 0;
};
hasher.finish()
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_hasher_free(state: *mut AxHashState) {
if state.is_null() {
return;
}
unsafe {
drop(Box::from_raw(state.cast::<AxHasher>()));
}
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_runtime_backend() -> AxHashRuntimeBackend {
runtime_backend().into()
}
#[unsafe(no_mangle)]
pub extern "C" fn axhash_runtime_has_simd() -> bool {
runtime_has_simd()
}