use jni::errors::Error as JniErrorType;
use jni::objects::{JByteArray, JString};
use jni::sys::jint;
use jni::JNIEnv;
use std::ffi::CString;
use crate::ffi::{ciphern_cleanup, ciphern_init, CiphernError};
pub type JniResult<T> = Result<T, JniError>;
#[derive(Debug)]
#[allow(dead_code)]
pub enum JniError {
Jni(()),
Ciphern(()),
InvalidString,
InvalidBuffer,
}
impl std::fmt::Display for JniError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for JniError {}
impl From<JniErrorType> for JniError {
fn from(_error: JniErrorType) -> Self {
JniError::Jni(())
}
}
impl From<CiphernError> for JniError {
fn from(_error: CiphernError) -> Self {
JniError::Ciphern(())
}
}
pub struct JniEnv<'a> {
env: JNIEnv<'a>,
}
impl<'a> JniEnv<'a> {
pub fn new(env: JNIEnv<'a>) -> Self {
Self { env }
}
pub fn get_cstring(&mut self, string: &JString) -> JniResult<CString> {
let rust_string: String = self.env.get_string(string)?.into();
CString::new(rust_string).map_err(|_| JniError::InvalidString)
}
pub fn get_bytes(&mut self, array: &JByteArray) -> JniResult<Vec<u8>> {
self.env.convert_byte_array(array).map_err(JniError::from)
}
pub fn new_string(&self, string: &str) -> JniResult<JString<'a>> {
self.env.new_string(string).map_err(JniError::from)
}
pub fn byte_array_from_slice(&self, data: &[u8]) -> JniResult<JByteArray<'a>> {
self.env.byte_array_from_slice(data).map_err(JniError::from)
}
pub fn throw_exception(&mut self, class_name: &str, message: &str) -> JniResult<()> {
self.env
.throw_new(class_name, message)
.map_err(|_e| JniError::Jni(()))
}
pub fn handle_ciphern_error(&mut self, error: CiphernError) -> JniResult<()> {
let class_name = "com/ciphern/CiphernException";
let message = format!("Ciphern operation failed: {:?}", error);
self.throw_exception(class_name, &message)
}
#[allow(dead_code)]
pub fn inner(&mut self) -> &mut JNIEnv<'a> {
&mut self.env
}
}
pub struct JniInitializer;
impl JniInitializer {
pub fn init() -> jint {
match ciphern_init() {
CiphernError::Success => 0,
_ => -1,
}
}
pub fn cleanup() {
ciphern_cleanup();
}
}
pub struct JniBuffer;
impl JniBuffer {
pub fn create_encrypt_buffer(input_len: usize) -> Vec<u8> {
vec![0u8; input_len + 256] }
pub fn create_decrypt_buffer(input_len: usize) -> Vec<u8> {
vec![0u8; input_len] }
pub fn truncate_buffer(buffer: &mut Vec<u8>, actual_len: usize) {
buffer.truncate(actual_len);
}
}
#[macro_export]
macro_rules! jni_wrap {
($env:expr, $body:expr) => {{
let mut jni_env = $crate::ffi::jni_utils::JniEnv::new($env);
match $body {
Ok(result) => result,
Err(e) => {
let _ = jni_env.handle_ciphern_error(e.into());
Default::default()
}
}
}};
}
#[macro_export]
macro_rules! jni_get_string {
($env:expr, $string:expr) => {{
let mut jni_env = $crate::ffi::jni_utils::JniEnv::new($env);
jni_env.get_cstring($string)?
}};
}
#[macro_export]
macro_rules! jni_get_bytes {
($env:expr, $array:expr) => {{
let mut jni_env = $crate::ffi::jni_utils::JniEnv::new($env);
jni_env.get_bytes($array)?
}};
}
#[cfg(test)]
mod tests {
use super::JniBuffer;
#[test]
fn test_buffer_creation() {
let encrypt_buffer = JniBuffer::create_encrypt_buffer(100);
assert_eq!(encrypt_buffer.len(), 356);
let decrypt_buffer = JniBuffer::create_decrypt_buffer(100);
assert_eq!(decrypt_buffer.len(), 100);
}
}