1 use crate::sys::jsize; 2 use log::error; 3 4 use std::ptr::NonNull; 5 6 use crate::objects::release_mode::ReleaseMode; 7 use crate::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; 8 use crate::{errors::*, objects::JObject, sys, JNIEnv}; 9 10 /// Trait to define type array access/release 11 pub trait TypeArray { 12 /// getter get(env: &JNIEnv, obj: JObject, is_copy: &mut jboolean) -> Result<*mut Self>13 fn get(env: &JNIEnv, obj: JObject, is_copy: &mut jboolean) -> Result<*mut Self>; 14 15 /// releaser release(env: &JNIEnv, obj: JObject, ptr: NonNull<Self>, mode: i32) -> Result<()>16 fn release(env: &JNIEnv, obj: JObject, ptr: NonNull<Self>, mode: i32) -> Result<()>; 17 } 18 19 // TypeArray builder 20 macro_rules! type_array { 21 ( $jni_type:ty, $jni_get:tt, $jni_release:tt ) => { 22 /// $jni_type array access/release impl 23 impl TypeArray for $jni_type { 24 /// Get Java $jni_type array 25 fn get(env: &JNIEnv, obj: JObject, is_copy: &mut jboolean) -> Result<*mut Self> { 26 let internal = env.get_native_interface(); 27 // Even though this method may throw OoME, use `jni_unchecked` 28 // instead of `jni_non_null_call` to remove (a slight) overhead 29 // of exception checking. An error will still be detected as a `null` 30 // result inside AutoArray ctor. Also, modern Hotspot in case of lack 31 // of memory will return null and won't throw an exception: 32 // https://sourcegraph.com/github.com/openjdk/jdk/-/blob/src/hotspot/share/memory/allocation.hpp#L488-489 33 let res = jni_unchecked!(internal, $jni_get, *obj, is_copy); 34 Ok(res) 35 } 36 37 /// Release Java $jni_type array 38 fn release(env: &JNIEnv, obj: JObject, ptr: NonNull<Self>, mode: i32) -> Result<()> { 39 let internal = env.get_native_interface(); 40 jni_unchecked!(internal, $jni_release, *obj, ptr.as_ptr(), mode as i32); 41 Ok(()) 42 } 43 } 44 }; 45 } 46 47 type_array!(jint, GetIntArrayElements, ReleaseIntArrayElements); 48 type_array!(jlong, GetLongArrayElements, ReleaseLongArrayElements); 49 type_array!(jbyte, GetByteArrayElements, ReleaseByteArrayElements); 50 type_array!( 51 jboolean, 52 GetBooleanArrayElements, 53 ReleaseBooleanArrayElements 54 ); 55 type_array!(jchar, GetCharArrayElements, ReleaseCharArrayElements); 56 type_array!(jshort, GetShortArrayElements, ReleaseShortArrayElements); 57 type_array!(jfloat, GetFloatArrayElements, ReleaseFloatArrayElements); 58 type_array!(jdouble, GetDoubleArrayElements, ReleaseDoubleArrayElements); 59 60 /// Auto-release wrapper for pointer-based generic arrays. 61 /// 62 /// This wrapper is used to wrap pointers returned by Get<Type>ArrayElements. 63 /// While wrapped, the object can be accessed via the `From` impl. 64 /// 65 /// AutoArray provides automatic array release through a call to appropriate 66 /// Release<Type>ArrayElements when it goes out of scope. 67 pub struct AutoArray<'a: 'b, 'b, T: TypeArray> { 68 obj: JObject<'a>, 69 ptr: NonNull<T>, 70 mode: ReleaseMode, 71 is_copy: bool, 72 env: &'b JNIEnv<'a>, 73 } 74 75 impl<'a, 'b, T: TypeArray> AutoArray<'a, 'b, T> { new(env: &'b JNIEnv<'a>, obj: JObject<'a>, mode: ReleaseMode) -> Result<Self>76 pub(crate) fn new(env: &'b JNIEnv<'a>, obj: JObject<'a>, mode: ReleaseMode) -> Result<Self> { 77 let mut is_copy: jboolean = 0xff; 78 Ok(AutoArray { 79 obj, 80 ptr: { 81 let ptr = T::get(env, obj, &mut is_copy)?; 82 NonNull::new(ptr).ok_or(Error::NullPtr("Non-null ptr expected"))? 83 }, 84 mode, 85 is_copy: is_copy == sys::JNI_TRUE, 86 env, 87 }) 88 } 89 90 /// Get a reference to the wrapped pointer as_ptr(&self) -> *mut T91 pub fn as_ptr(&self) -> *mut T { 92 self.ptr.as_ptr() 93 } 94 95 /// Commits the changes to the array, if it is a copy commit(&self) -> Result<()>96 pub fn commit(&self) -> Result<()> { 97 self.release_array_elements(sys::JNI_COMMIT) 98 } 99 release_array_elements(&self, mode: i32) -> Result<()>100 fn release_array_elements(&self, mode: i32) -> Result<()> { 101 T::release(self.env, self.obj, self.ptr, mode) 102 } 103 104 /// Don't commit the changes to the array on release (if it is a copy). 105 /// This has no effect if the array is not a copy. 106 /// This method is useful to change the release mode of an array originally created 107 /// with `ReleaseMode::CopyBack`. discard(&mut self)108 pub fn discard(&mut self) { 109 self.mode = ReleaseMode::NoCopyBack; 110 } 111 112 /// Indicates if the array is a copy or not is_copy(&self) -> bool113 pub fn is_copy(&self) -> bool { 114 self.is_copy 115 } 116 117 /// Returns the array size size(&self) -> Result<jsize>118 pub fn size(&self) -> Result<jsize> { 119 self.env.get_array_length(*self.obj) 120 } 121 } 122 123 impl<'a, 'b, T: TypeArray> Drop for AutoArray<'a, 'b, T> { drop(&mut self)124 fn drop(&mut self) { 125 let res = self.release_array_elements(self.mode as i32); 126 match res { 127 Ok(()) => {} 128 Err(e) => error!("error releasing array: {:#?}", e), 129 } 130 } 131 } 132 133 impl<'a, T: TypeArray> From<&'a AutoArray<'a, '_, T>> for *mut T { from(other: &'a AutoArray<T>) -> *mut T134 fn from(other: &'a AutoArray<T>) -> *mut T { 135 other.as_ptr() 136 } 137 } 138