jni_glue/
array.rs

1use super::*;
2
3use std::marker::*;
4use std::ops::*;
5
6/// A Java Array of some POD-like type such as bool, jbyte, jchar, jshort, jint, jlong, jfloat, or jdouble.
7/// 
8/// See also [ObjectArray] for arrays of reference types.
9/// 
10/// | JNI Type      | PrimitiveArray Implementation |
11/// | ------------- | ----------------- |
12/// | [bool]\[\]    | [BooleanArray]    |
13/// | [jbyte]\[\]   | [ByteArray]       |
14/// | [jchar]\[\]   | [CharArray]       |
15/// | [jint]\[\]    | [IntArray]        |
16/// | [jlong]\[\]   | [LongArray]       |
17/// | [jfloat]\[\]  | [FloatArray]      |
18/// | [jdouble]\[\] | [DoubleArray]     |
19/// 
20/// [bool]:         https://doc.rust-lang.org/std/primitive.bool.html
21/// [jbyte]:        https://docs.rs/jni-sys/0.3.0/jni_sys/type.jbyte.html
22/// [jchar]:        struct.jchar.html
23/// [jint]:         https://docs.rs/jni-sys/0.3.0/jni_sys/type.jint.html
24/// [jlong]:        https://docs.rs/jni-sys/0.3.0/jni_sys/type.jlong.html
25/// [jfloat]:       https://docs.rs/jni-sys/0.3.0/jni_sys/type.jfloat.html
26/// [jdouble]:      https://docs.rs/jni-sys/0.3.0/jni_sys/type.jdouble.html
27/// 
28/// [BooleanArray]: struct.BooleanArray.html
29/// [ByteArray]:    struct.ByteArray.html
30/// [CharArray]:    struct.CharArray.html
31/// [IntArray]:     struct.IntArray.html
32/// [LongArray]:    struct.LongArray.html
33/// [FloatArray]:   struct.FloatArray.html
34/// [DoubleArray]:  struct.DoubleArray.html
35/// [ObjectArray]:  struct.ObjectArray.html
36/// 
37pub trait PrimitiveArray<T> where Self : Sized + AsValidJObjectAndEnv, T : Clone + Default {
38    /// Uses env.New{Type}Array to create a new java array containing "size" elements.
39    fn new<'env>(env: &'env Env, size: usize) -> Local<'env, Self>;
40
41    /// Uses env.GetArrayLength to get the length of the java array.
42    fn len(&self) -> usize;
43
44    /// Uses env.Get{Type}ArrayRegion to read the contents of the java array from \[start .. start + elements.len())
45    fn get_region(&self, start: usize, elements: &mut [T]);
46
47    /// Uses env.Set{Type}ArrayRegion to set the contents of the java array from \[start .. start + elements.len())
48    fn set_region(&self, start: usize, elements: &[T]);
49
50    /// Uses env.New{Type}Array + Set{Type}ArrayRegion to create a new java array containing a copy of "elements".
51    fn from<'env>(env: &'env Env, elements: &[T]) -> Local<'env, Self> {
52        let array = Self::new(env, elements.len());
53        array.set_region(0, elements);
54        array
55    }
56
57    /// Uses env.GetArrayLength + env.Get{Type}ArrayRegion to read the contents of the java array from range into a new Vec.
58    fn get_region_as_vec(&self, range: impl RangeBounds<usize>) -> Vec<T> {
59        let len = self.len();
60
61        let start = match range.start_bound() {
62            Bound::Unbounded => 0,
63            Bound::Included(n) => *n,
64            Bound::Excluded(n) => *n+1,
65        };
66
67        let end = match range.end_bound() {
68            Bound::Unbounded => len,
69            Bound::Included(n) => *n+1,
70            Bound::Excluded(n) => *n,
71        };
72
73        assert!(start <= end);
74        assert!(end   <= len);
75        let vec_len = end - start;
76
77        let mut vec = Vec::new();
78        vec.resize(vec_len, Default::default());
79        self.get_region(start, &mut vec[..]);
80        vec
81    }
82
83    /// Uses env.GetArrayLength + env.Get{Type}ArrayRegion to read the contents of the entire java array into a new Vec.
84    fn as_vec(&self) -> Vec<T> {
85        self.get_region_as_vec(0..self.len())
86    }
87}
88
89// I assume jboolean as used exclusively by JNI/JVM is compatible with bool.
90// This is *not* a sound/safe assumption in the general case as jboolean can be any u8 bit pattern.
91// However, I believe this *is* a sound/safe assumption when exclusively dealing with JNI/JVM APIs which *should* be
92// returning exclusively JNI_TRUE or JNI_FALSE, which are bitwise compatible with Rust's definitions of true / false.
93#[test] fn bool_ffi_assumptions_test() {
94    use std::mem::*;
95
96    // Assert that the sizes are indeed the same.
97    assert_eq!(size_of::<jboolean>(), 1); // Forever
98    assert_eq!(size_of::<bool>(),     1); // As of https://github.com/rust-lang/rust/pull/46156/commits/219ba511c824bc44149d55c570f723dcd0f0217d
99
100    // Assert that the underlying representations are indeed the same.
101    assert_eq!(unsafe { std::mem::transmute::<bool, u8>(true ) }, JNI_TRUE );
102    assert_eq!(unsafe { std::mem::transmute::<bool, u8>(false) }, JNI_FALSE);
103}
104
105macro_rules! primitive_array {
106    (#[repr(transparent)] pub struct $name:ident = $type_str:expr, $type:ident { $new_array:ident $set_region:ident $get_region:ident } ) => {
107        /// A [PrimitiveArray](trait.PrimitiveArray.html) implementation.
108        #[repr(transparent)] pub struct $name(ObjectAndEnv);
109
110        unsafe impl AsValidJObjectAndEnv for $name {}
111        unsafe impl AsJValue for $name { fn as_jvalue(&self) -> jni_sys::jvalue { jni_sys::jvalue { l: self.0.object } } }
112        unsafe impl JniType for $name { fn static_with_jni_type<R>(callback: impl FnOnce(&str) -> R) -> R { callback($type_str) } }
113
114        impl PrimitiveArray<$type> for $name {
115            fn new<'env>(env: &'env Env, size: usize) -> Local<'env, Self> {
116                assert!(size <= std::i32::MAX as usize); // jsize == jint == i32
117                let size = size as jsize;
118                let env = env.as_jni_env();
119                unsafe {
120                    let object = (**env).$new_array.unwrap()(env, size);
121                    let exception = (**env).ExceptionOccurred.unwrap()(env);
122                    assert!(exception.is_null()); // Only sane exception here is an OOM exception
123                    Local::from_env_object(env, object)
124                }
125            }
126
127            fn from<'env>(env: &'env Env, elements: &[$type]) -> Local<'env, Self> {
128                let array  = Self::new(env, elements.len());
129                let size   = elements.len() as jsize;
130                let env    = array.0.env as *mut JNIEnv;
131                let object = array.0.object;
132                unsafe {
133                    (**env).$set_region.unwrap()(env, object, 0, size, elements.as_ptr() as *const _);
134                }
135                array
136            }
137
138            fn len(&self) -> usize {
139                unsafe { (**self.0.env).GetArrayLength.unwrap()(self.0.env as *mut _, self.0.object) as usize }
140            }
141
142            fn get_region(&self, start: usize, elements: &mut [$type]) {
143                assert!(start          <= std::i32::MAX as usize); // jsize == jint == i32
144                assert!(elements.len() <= std::i32::MAX as usize); // jsize == jint == i32
145                let self_len     = self.len() as jsize;
146                let elements_len = elements.len() as jsize;
147
148                let start = start as jsize;
149                let end   = start + elements_len;
150                assert!(start <= end);
151                assert!(end   <= self_len);
152
153                unsafe { (**self.0.env).$get_region.unwrap()(self.0.env as *mut _, self.0.object, start, elements_len, elements.as_mut_ptr() as *mut _) };
154            }
155
156            fn set_region(&self, start: usize, elements: &[$type]) {
157                assert!(start          <= std::i32::MAX as usize); // jsize == jint == i32
158                assert!(elements.len() <= std::i32::MAX as usize); // jsize == jint == i32
159                let self_len     = self.len() as jsize;
160                let elements_len = elements.len() as jsize;
161
162                let start = start as jsize;
163                let end   = start + elements_len;
164                assert!(start <= end);
165                assert!(end   <= self_len);
166
167                unsafe { (**self.0.env).$set_region.unwrap()(self.0.env as *mut _, self.0.object, start, elements_len, elements.as_ptr() as *const _) };
168            }
169        }
170    };
171}
172
173primitive_array! { #[repr(transparent)] pub struct BooleanArray = "[Z\0", bool    { NewBooleanArray SetBooleanArrayRegion GetBooleanArrayRegion } }
174primitive_array! { #[repr(transparent)] pub struct ByteArray    = "[B\0", jbyte   { NewByteArray    SetByteArrayRegion    GetByteArrayRegion    } }
175primitive_array! { #[repr(transparent)] pub struct CharArray    = "[C\0", jchar   { NewCharArray    SetCharArrayRegion    GetCharArrayRegion    } }
176primitive_array! { #[repr(transparent)] pub struct ShortArray   = "[S\0", jshort  { NewShortArray   SetShortArrayRegion   GetShortArrayRegion   } }
177primitive_array! { #[repr(transparent)] pub struct IntArray     = "[I\0", jint    { NewIntArray     SetIntArrayRegion     GetIntArrayRegion     } }
178primitive_array! { #[repr(transparent)] pub struct LongArray    = "[J\0", jlong   { NewLongArray    SetLongArrayRegion    GetLongArrayRegion    } }
179primitive_array! { #[repr(transparent)] pub struct FloatArray   = "[F\0", jfloat  { NewFloatArray   SetFloatArrayRegion   GetFloatArrayRegion   } }
180primitive_array! { #[repr(transparent)] pub struct DoubleArray  = "[D\0", jdouble { NewDoubleArray  SetDoubleArrayRegion  GetDoubleArrayRegion  } }
181
182/// A Java Array of reference types (classes, interfaces, other arrays, etc.)
183/// 
184/// See also [PrimitiveArray] for arrays of reference types.
185/// 
186/// [PrimitiveArray]:   struct.PrimitiveArray.html
187/// 
188#[repr(transparent)]
189pub struct ObjectArray<T: AsValidJObjectAndEnv, E: ThrowableType>(ObjectAndEnv, PhantomData<(T,E)>);
190
191unsafe impl<T: AsValidJObjectAndEnv, E: ThrowableType> AsValidJObjectAndEnv for ObjectArray<T, E> {}
192
193unsafe impl<T: AsValidJObjectAndEnv, E: ThrowableType> JniType for ObjectArray<T, E> {
194    fn static_with_jni_type<R>(callback: impl FnOnce(&str) -> R) -> R {
195        T::static_with_jni_type(|inner| callback(format!("[{}", inner).as_str()))
196    }
197}
198
199unsafe impl<T: AsValidJObjectAndEnv, E: ThrowableType> AsJValue for ObjectArray<T, E> {
200    fn as_jvalue(&self) -> jni_sys::jvalue {
201        jni_sys::jvalue { l: self.0.object }
202    }
203}
204
205impl<T: AsValidJObjectAndEnv, E: ThrowableType> ObjectArray<T, E> {
206    pub fn new<'env>(env: &'env Env, size: usize) -> Local<'env, Self> {
207        assert!(size <= std::i32::MAX as usize); // jsize == jint == i32
208        let class = Self::static_with_jni_type(|t| unsafe { env.require_class(t) });
209        let size = size as jsize;
210        let env = env.as_jni_env();
211        unsafe {
212            let fill = null_mut();
213            let object = (**env).NewObjectArray.unwrap()(env, size, class, fill);
214            let exception = (**env).ExceptionOccurred.unwrap()(env);
215            assert!(exception.is_null()); // Only sane exception here is an OOM exception
216            Local::from_env_object(env, object)
217        }
218    }
219
220    pub fn iter<'env>(&'env self) -> ObjectArrayIter<'env, T, E> {
221        ObjectArrayIter {
222            array:  self,
223            index:  0,
224            length: self.len(),
225        }
226    }
227
228    pub fn from<'env>(env: &'env Env, elements: impl 'env + ExactSizeIterator + Iterator<Item = impl Into<Option<&'env T>>>) -> Local<'env, Self> {
229        let size    = elements.len();
230        let array   = Self::new(env, size);
231        let env     = array.0.env as *mut JNIEnv;
232        let this    = array.0.object;
233        let set     = unsafe { (**env) }.SetObjectArrayElement.unwrap();
234
235        for (index, element) in elements.enumerate() {
236            assert!(index < size); // Should only be violated by an invalid ExactSizeIterator implementation.
237            let value = element.into().map(|v| unsafe { AsJValue::as_jvalue(v.into()).l }).unwrap_or(null_mut());
238            unsafe { set(env, this, index as jsize, value) };
239        }
240        array
241    }
242
243    pub fn len(&self) -> usize {
244        unsafe { (**self.0.env).GetArrayLength.unwrap()(self.0.env as *mut _, self.0.object) as usize }
245    }
246
247    /// XXX: Expose this via std::ops::Index
248    pub fn get<'env>(&'env self, index: usize) -> Result<Option<Local<'env, T>>, Local<'env, E>> {
249        assert!(index <= std::i32::MAX as usize); // jsize == jint == i32 XXX: Should maybe be treated as an exception?
250        let index   = index as jsize;
251        let env     = self.0.env as *mut JNIEnv;
252        let this    = self.0.object;
253        unsafe {
254            let result = (**env).GetObjectArrayElement.unwrap()(env, this, index);
255            let exception = (**env).ExceptionOccurred.unwrap()(env);
256            if !exception.is_null() {
257                (**env).ExceptionClear.unwrap()(env);
258                Err(Local::from_env_object(env, exception))
259            } else if result.is_null() {
260                Ok(None)
261            } else {
262                Ok(Some(Local::from_env_object(env, result)))
263            }
264        }
265    }
266
267    /// XXX: I don't think there's a way to expose this via std::ops::IndexMut sadly?
268    pub fn set<'env>(&'env self, index: usize, value: impl Into<Option<&'env T>>) -> Result<(), Local<'env, E>> {
269        assert!(index <= std::i32::MAX as usize); // jsize == jint == i32 XXX: Should maybe be treated as an exception?
270        let value   = value.into().map(|v| unsafe { AsJValue::as_jvalue(v.into()).l }).unwrap_or(null_mut());
271        let index   = index as jsize;
272        let env     = self.0.env as *mut JNIEnv;
273        let this    = self.0.object;
274        unsafe {
275            (**env).SetObjectArrayElement.unwrap()(env, this, index, value);
276            let exception = (**env).ExceptionOccurred.unwrap()(env);
277            if !exception.is_null() {
278                (**env).ExceptionClear.unwrap()(env);
279                Err(Local::from_env_object(env, exception))
280            } else {
281                Ok(())
282            }
283        }
284    }
285}
286
287
288
289pub struct ObjectArrayIter<'env, T: AsValidJObjectAndEnv, E: ThrowableType> {
290    array:  &'env ObjectArray<T, E>,
291    index:  usize,
292    length: usize,
293}
294
295impl<'env, T: AsValidJObjectAndEnv, E: ThrowableType> Iterator for ObjectArrayIter<'env, T, E> {
296    type Item = Option<Local<'env, T>>;
297    fn next(&mut self) -> Option<Self::Item> {
298        let index = self.index;
299        if index < self.length {
300            self.index = index + 1;
301            Some(self.array.get(index).unwrap_or(None))
302        } else {
303            None
304        }
305    }
306}