#![allow(unsafe_code)]
#![allow(non_camel_case_types)]
use core::ffi::c_void;
use crate::ffi::{
PITH_E_INVALID, PITH_E_REJECTED, PITH_OK, pith_digest_adler32, pith_digest_base64_decode,
pith_digest_base64_encode, pith_digest_crc32, pith_digest_crc32c, pith_digest_fnv1a64,
pith_digest_hmac_sha1, pith_digest_hmac_sha256, pith_digest_hmac_sha512,
pith_digest_murmur3_x64_128, pith_digest_sha1, pith_digest_sha256, pith_digest_sha512,
pith_digest_splitmix64_fill, pith_digest_xoshiro256_fill, pith_digest_xxh64,
};
type JNIEnv = *const JniTable;
type JArray = *mut c_void;
type JIntArray = *mut c_void;
type JClass = *mut c_void;
type jbyte = i8;
type jint = i32;
type jlong = i64;
#[repr(C)]
struct JniTable {
_prefix: [*mut c_void; 171],
get_array_length: unsafe extern "system" fn(env: *mut JNIEnv, array: JArray) -> jint,
_gap_before_new_byte_array: [*mut c_void; 4],
new_byte_array: unsafe extern "system" fn(env: *mut JNIEnv, len: jint) -> JArray,
_gap_before_new_long_array: [*mut c_void; 3],
new_long_array: unsafe extern "system" fn(env: *mut JNIEnv, len: jint) -> JArray,
_gap_before_byte_region: [*mut c_void; 19],
get_byte_array_region: unsafe extern "system" fn(
env: *mut JNIEnv,
array: JArray,
start: jint,
len: jint,
buf: *mut jbyte,
),
_gap_before_set_byte_region: [*mut c_void; 7],
set_byte_array_region: unsafe extern "system" fn(
env: *mut JNIEnv,
array: JArray,
start: jint,
len: jint,
buf: *const jbyte,
),
_gap_before_set_int_region: [*mut c_void; 2],
set_int_array_region: unsafe extern "system" fn(
env: *mut JNIEnv,
array: JIntArray,
start: jint,
len: jint,
buf: *const jint,
),
set_long_array_region: unsafe extern "system" fn(
env: *mut JNIEnv,
array: JArray,
start: jint,
len: jint,
buf: *const jlong,
),
}
unsafe fn table<'a>(env: *mut JNIEnv) -> &'a JniTable {
unsafe { &**env }
}
unsafe fn java_bytes(env: *mut JNIEnv, array: JArray) -> Result<Vec<u8>, i32> {
if array.is_null() {
return Err(PITH_E_INVALID);
}
let functions = unsafe { table(env) };
let len = unsafe { (functions.get_array_length)(env, array) };
if len < 0 {
return Err(PITH_E_INVALID);
}
let mut bytes = vec![0u8; len as usize];
unsafe { (functions.get_byte_array_region)(env, array, 0, len, bytes.as_mut_ptr().cast()) };
Ok(bytes)
}
unsafe fn new_java_bytes(env: *mut JNIEnv, bytes: &[u8]) -> Option<JArray> {
let functions = unsafe { table(env) };
let array = unsafe { (functions.new_byte_array)(env, bytes.len() as jint) };
if array.is_null() {
return None;
}
unsafe {
(functions.set_byte_array_region)(env, array, 0, bytes.len() as jint, bytes.as_ptr().cast())
};
Some(array)
}
unsafe fn new_java_longs(env: *mut JNIEnv, values: &[u64]) -> Option<JArray> {
let functions = unsafe { table(env) };
let array = unsafe { (functions.new_long_array)(env, values.len() as jint) };
if array.is_null() {
return None;
}
let longs: Vec<i64> = values.iter().map(|&value| value as i64).collect();
unsafe {
(functions.set_long_array_region)(env, array, 0, longs.len() as jint, longs.as_ptr())
};
Some(array)
}
unsafe fn set_status(env: *mut JNIEnv, status: JIntArray, value: jint) {
let functions = unsafe { table(env) };
unsafe { (functions.set_int_array_region)(env, status, 0, 1, &value) };
}
unsafe fn digest_in<const LEN: usize>(
env: *mut JNIEnv,
status: JIntArray,
data: JArray,
produce: impl FnOnce(&[u8], *mut u8) -> i32,
) -> JArray {
if env.is_null() || status.is_null() {
return core::ptr::null_mut();
}
let bytes = match unsafe { java_bytes(env, data) } {
Ok(bytes) => bytes,
Err(status_code) => {
unsafe { set_status(env, status, status_code) };
return core::ptr::null_mut();
}
};
let mut out = [0u8; LEN];
let code = produce(&bytes, out.as_mut_ptr());
if code != PITH_OK {
unsafe { set_status(env, status, code) };
return core::ptr::null_mut();
}
match unsafe { new_java_bytes(env, &out) } {
Some(array) => {
unsafe { set_status(env, status, PITH_OK) };
array
}
None => {
unsafe { set_status(env, status, PITH_E_REJECTED) };
core::ptr::null_mut()
}
}
}
unsafe fn with_data(
env: *mut JNIEnv,
status: JIntArray,
data: JArray,
produce: impl FnOnce(&[u8]) -> i32,
) -> i32 {
if env.is_null() || status.is_null() {
return PITH_E_INVALID;
}
let bytes = match unsafe { java_bytes(env, data) } {
Ok(bytes) => bytes,
Err(status_code) => {
unsafe { set_status(env, status, status_code) };
return PITH_E_INVALID;
}
};
produce(&bytes)
}
unsafe fn longs_out(
env: *mut JNIEnv,
status: JIntArray,
seed: jlong,
count: jint,
fill: unsafe extern "C" fn(u64, *mut u64, usize) -> i32,
) -> JArray {
if env.is_null() || status.is_null() {
return core::ptr::null_mut();
}
if count < 0 {
unsafe { set_status(env, status, PITH_E_INVALID) };
return core::ptr::null_mut();
}
let mut out = vec![0u64; count as usize];
let code = unsafe { fill(seed as u64, out.as_mut_ptr(), out.len()) };
if code != PITH_OK {
unsafe { set_status(env, status, code) };
return core::ptr::null_mut();
}
match unsafe { new_java_longs(env, &out) } {
Some(array) => {
unsafe { set_status(env, status, PITH_OK) };
array
}
None => {
unsafe { set_status(env, status, PITH_E_REJECTED) };
core::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_sha256Native(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
status: JIntArray,
) -> JArray {
unsafe {
digest_in::<32>(env, status, data, |bytes, out| {
pith_digest_sha256(bytes.as_ptr(), bytes.len(), out)
})
}
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_sha1Native(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
status: JIntArray,
) -> JArray {
unsafe {
digest_in::<20>(env, status, data, |bytes, out| {
pith_digest_sha1(bytes.as_ptr(), bytes.len(), out)
})
}
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_sha512Native(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
status: JIntArray,
) -> JArray {
unsafe {
digest_in::<64>(env, status, data, |bytes, out| {
pith_digest_sha512(bytes.as_ptr(), bytes.len(), out)
})
}
}
unsafe fn hmac_out<const LEN: usize>(
env: *mut JNIEnv,
status: JIntArray,
key: JArray,
data: JArray,
hmac: unsafe extern "C" fn(*const u8, usize, *const u8, usize, *mut u8) -> i32,
) -> JArray {
if env.is_null() || status.is_null() {
return core::ptr::null_mut();
}
let key_bytes = match unsafe { java_bytes(env, key) } {
Ok(bytes) => bytes,
Err(status_code) => {
unsafe { set_status(env, status, status_code) };
return core::ptr::null_mut();
}
};
let data_bytes = match unsafe { java_bytes(env, data) } {
Ok(bytes) => bytes,
Err(status_code) => {
unsafe { set_status(env, status, status_code) };
return core::ptr::null_mut();
}
};
let mut out = [0u8; LEN];
let code = unsafe {
hmac(
key_bytes.as_ptr(),
key_bytes.len(),
data_bytes.as_ptr(),
data_bytes.len(),
out.as_mut_ptr(),
)
};
if code != PITH_OK {
unsafe { set_status(env, status, code) };
return core::ptr::null_mut();
}
match unsafe { new_java_bytes(env, &out) } {
Some(array) => {
unsafe { set_status(env, status, PITH_OK) };
array
}
None => {
unsafe { set_status(env, status, PITH_E_REJECTED) };
core::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_hmacSha1Native(
env: *mut JNIEnv,
_class: JClass,
key: JArray,
data: JArray,
status: JIntArray,
) -> JArray {
unsafe { hmac_out::<20>(env, status, key, data, pith_digest_hmac_sha1) }
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_hmacSha256Native(
env: *mut JNIEnv,
_class: JClass,
key: JArray,
data: JArray,
status: JIntArray,
) -> JArray {
unsafe { hmac_out::<32>(env, status, key, data, pith_digest_hmac_sha256) }
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_hmacSha512Native(
env: *mut JNIEnv,
_class: JClass,
key: JArray,
data: JArray,
status: JIntArray,
) -> JArray {
unsafe { hmac_out::<64>(env, status, key, data, pith_digest_hmac_sha512) }
}
unsafe fn checksum32_out(
env: *mut JNIEnv,
status: JIntArray,
data: JArray,
sum: unsafe extern "C" fn(*const u8, usize, *mut u32) -> i32,
) -> jint {
let mut out: u32 = 0;
if unsafe {
with_data(env, status, data, |bytes| {
sum(bytes.as_ptr(), bytes.len(), &mut out)
})
} != PITH_OK
{
return 0;
}
out as jint
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_crc32Native(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
status: JIntArray,
) -> jint {
unsafe { checksum32_out(env, status, data, pith_digest_crc32) }
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_adler32Native(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
status: JIntArray,
) -> jint {
unsafe { checksum32_out(env, status, data, pith_digest_adler32) }
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_crc32cNative(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
status: JIntArray,
) -> jint {
unsafe { checksum32_out(env, status, data, pith_digest_crc32c) }
}
unsafe fn hash64_out(
env: *mut JNIEnv,
status: JIntArray,
data: JArray,
hash: unsafe extern "C" fn(*const u8, usize, *mut u64) -> i32,
) -> jlong {
let mut out: u64 = 0;
if unsafe {
with_data(env, status, data, |bytes| {
hash(bytes.as_ptr(), bytes.len(), &mut out)
})
} != PITH_OK
{
return 0;
}
out as jlong
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_fnv1a64Native(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
status: JIntArray,
) -> jlong {
unsafe { hash64_out(env, status, data, pith_digest_fnv1a64) }
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_xxh64Native(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
seed: jlong,
status: JIntArray,
) -> jlong {
let mut out: u64 = 0;
if unsafe {
with_data(env, status, data, |bytes| {
pith_digest_xxh64(bytes.as_ptr(), bytes.len(), seed as u64, &mut out)
})
} != PITH_OK
{
return 0;
}
out as jlong
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_murmur3X64128Native(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
seed: jint,
status: JIntArray,
) -> JArray {
unsafe {
digest_in::<16>(env, status, data, |bytes, out| {
pith_digest_murmur3_x64_128(bytes.as_ptr(), bytes.len(), seed as u32, out)
})
}
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_splitmix64FillNative(
env: *mut JNIEnv,
_class: JClass,
seed: jlong,
count: jint,
status: JIntArray,
) -> JArray {
unsafe { longs_out(env, status, seed, count, pith_digest_splitmix64_fill) }
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_xoshiro256FillNative(
env: *mut JNIEnv,
_class: JClass,
seed: jlong,
count: jint,
status: JIntArray,
) -> JArray {
unsafe { longs_out(env, status, seed, count, pith_digest_xoshiro256_fill) }
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_base64EncodeNative(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
status: JIntArray,
) -> JArray {
if env.is_null() || status.is_null() {
return core::ptr::null_mut();
}
let bytes = match unsafe { java_bytes(env, data) } {
Ok(bytes) => bytes,
Err(status_code) => {
unsafe { set_status(env, status, status_code) };
return core::ptr::null_mut();
}
};
let mut out = vec![0u8; crate::base64::encoded_len(bytes.len())];
let mut out_len: usize = 0;
let code = unsafe {
pith_digest_base64_encode(
bytes.as_ptr(),
bytes.len(),
out.as_mut_ptr(),
out.len(),
&mut out_len,
)
};
if code != PITH_OK {
unsafe { set_status(env, status, code) };
return core::ptr::null_mut();
}
match unsafe { new_java_bytes(env, &out[..out_len]) } {
Some(array) => {
unsafe { set_status(env, status, PITH_OK) };
array
}
None => {
unsafe { set_status(env, status, PITH_E_REJECTED) };
core::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_digest_PithDigest_base64DecodeNative(
env: *mut JNIEnv,
_class: JClass,
data: JArray,
status: JIntArray,
) -> JArray {
if env.is_null() || status.is_null() {
return core::ptr::null_mut();
}
let bytes = match unsafe { java_bytes(env, data) } {
Ok(bytes) => bytes,
Err(status_code) => {
unsafe { set_status(env, status, status_code) };
return core::ptr::null_mut();
}
};
let mut out = vec![0u8; bytes.len() / 4 * 3];
let mut out_len: usize = 0;
let code = unsafe {
pith_digest_base64_decode(
bytes.as_ptr(),
bytes.len(),
out.as_mut_ptr(),
out.len(),
&mut out_len,
)
};
if code != PITH_OK {
unsafe { set_status(env, status, code) };
return core::ptr::null_mut();
}
match unsafe { new_java_bytes(env, &out[..out_len]) } {
Some(array) => {
unsafe { set_status(env, status, PITH_OK) };
array
}
None => {
unsafe { set_status(env, status, PITH_E_REJECTED) };
core::ptr::null_mut()
}
}
}