#![allow(unsafe_code)]
#![allow(non_camel_case_types)]
use alloc::vec::Vec;
use core::ffi::c_void;
use crate::ffi::{
PITH_E_INVALID, PITH_E_REJECTED, PITH_OK, pith_text_fingerprint, pith_text_free,
pith_text_jaccard,
};
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;
type jdouble = f64;
#[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_byte_region: [*mut c_void; 23],
get_byte_array_region: unsafe extern "system" fn(
env: *mut JNIEnv,
array: JArray,
start: jint,
len: jint,
buf: *mut jbyte,
),
_gap_before_long_region: [*mut c_void; 3],
get_long_array_region: unsafe extern "system" fn(
env: *mut JNIEnv,
array: JArray,
start: jint,
len: jint,
buf: *mut jlong,
),
_gap_before_set_byte_region: [*mut c_void; 3],
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,
),
}
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 java_words(env: *mut JNIEnv, array: JArray) -> Result<Vec<u64>, 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 words: Vec<u64> = vec![0; len as usize];
unsafe {
(functions.get_long_array_region)(env, array, 0, len, words.as_mut_ptr().cast::<jlong>())
};
Ok(words)
}
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 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(no_mangle)]
unsafe extern "system" fn Java_hash_pith_text_PithText_fingerprintNative(
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 stream_ptr: *mut u8 = core::ptr::null_mut();
let mut stream_len: usize = 0;
let code = unsafe {
pith_text_fingerprint(
bytes.as_ptr(),
bytes.len(),
&mut stream_ptr,
&mut stream_len,
)
};
if code != PITH_OK {
unsafe { set_status(env, status, code) };
return core::ptr::null_mut();
}
let stream = unsafe { core::slice::from_raw_parts(stream_ptr, stream_len) };
let array = match unsafe { new_java_bytes(env, stream) } {
Some(array) => array,
None => {
unsafe { pith_text_free(stream_ptr, stream_len) };
unsafe { set_status(env, status, PITH_E_REJECTED) };
return core::ptr::null_mut();
}
};
unsafe { pith_text_free(stream_ptr, stream_len) };
unsafe { set_status(env, status, PITH_OK) };
array
}
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_hash_pith_text_PithText_jaccardNative(
env: *mut JNIEnv,
_class: JClass,
a: JArray,
b: JArray,
status: JIntArray,
) -> jdouble {
if env.is_null() || status.is_null() {
return 0.0;
}
let a_words = match unsafe { java_words(env, a) } {
Ok(words) => words,
Err(status_code) => {
unsafe { set_status(env, status, status_code) };
return 0.0;
}
};
let b_words = match unsafe { java_words(env, b) } {
Ok(words) => words,
Err(status_code) => {
unsafe { set_status(env, status, status_code) };
return 0.0;
}
};
let mut bits: u64 = 0;
let code = unsafe {
pith_text_jaccard(
a_words.as_ptr(),
a_words.len(),
b_words.as_ptr(),
b_words.len(),
&mut bits,
)
};
unsafe { set_status(env, status, code) };
if code == PITH_OK {
f64::from_bits(bits)
} else {
0.0
}
}