use crate::{AsArg, Env, JniType, Local, Ref, ReferenceType, ThrowableType};
use jni_sys::*;
use std::{
ffi::{CStr, CString},
marker::PhantomData,
ops::{Bound, RangeBounds},
ptr::null_mut,
};
pub trait PrimitiveArray<T>: Sized + ReferenceType
where
T: Clone + Default,
{
fn new<'env>(env: Env<'env>, size: usize) -> Local<'env, Self>;
fn len(self: &Ref<'_, Self>) -> usize;
fn is_empty(self: &Ref<'_, Self>) -> bool {
self.len() == 0
}
fn get_region(self: &Ref<'_, Self>, start: usize, elements: &mut [T]);
fn set_region(self: &Ref<'_, Self>, start: usize, elements: &[T]);
fn new_from<'env>(env: Env<'env>, elements: &[T]) -> Local<'env, Self> {
let array = Self::new(env, elements.len());
array.set_region(0, elements);
array
}
fn get_region_as_vec(self: &Ref<'_, Self>, range: impl RangeBounds<usize>) -> Vec<T> {
let len = self.len();
let start = match range.start_bound() {
Bound::Unbounded => 0,
Bound::Included(n) => *n,
Bound::Excluded(n) => *n + 1,
};
let end = match range.end_bound() {
Bound::Unbounded => len,
Bound::Included(n) => *n + 1,
Bound::Excluded(n) => *n,
};
assert!(start <= end);
assert!(end <= len);
let vec_len = end - start;
let mut vec = Vec::new();
vec.resize(vec_len, Default::default());
self.get_region(start, &mut vec[..]);
vec
}
fn as_vec(self: &Ref<'_, Self>) -> Vec<T> {
self.get_region_as_vec(0..self.len())
}
}
macro_rules! primitive_array {
($name:ident, $type_str:expr, $type:ident { $new_array:ident $set_region:ident $get_region:ident } ) => {
pub enum $name {}
unsafe impl ReferenceType for $name {}
unsafe impl JniType for $name {
fn static_with_jni_type<R>(callback: impl FnOnce(&CStr) -> R) -> R {
callback($type_str)
}
}
impl PrimitiveArray<$type> for $name {
fn new<'env>(env: Env<'env>, size: usize) -> Local<'env, Self> {
assert!(size <= i32::MAX as usize); let size = size as jsize;
let jnienv = env.as_raw();
unsafe {
let object = ((**jnienv).v1_2.$new_array)(jnienv, size);
let exception = ((**jnienv).v1_2.ExceptionOccurred)(jnienv);
assert!(exception.is_null()); Local::from_raw(env, object)
}
}
fn new_from<'env>(env: Env<'env>, elements: &[$type]) -> Local<'env, Self> {
let array = Self::new(env, elements.len());
let size = elements.len() as jsize;
let env = array.env().as_raw();
unsafe {
((**env).v1_1.$set_region)(
env,
array.as_raw(),
0,
size,
elements.as_ptr() as *const _,
);
}
array
}
fn len(self: &Ref<'_, Self>) -> usize {
let env = self.env().as_raw();
unsafe { ((**env).v1_2.GetArrayLength)(env, self.as_raw()) as usize }
}
fn get_region(self: &Ref<'_, Self>, start: usize, elements: &mut [$type]) {
assert!(start <= i32::MAX as usize); assert!(elements.len() <= i32::MAX as usize); let self_len = self.len() as jsize;
let elements_len = elements.len() as jsize;
let start = start as jsize;
let end = start + elements_len;
assert!(start <= end);
assert!(end <= self_len);
let env = self.env().as_raw();
unsafe {
((**env).v1_1.$get_region)(
env,
self.as_raw(),
start,
elements_len,
elements.as_mut_ptr() as *mut _,
)
};
}
fn set_region(self: &Ref<'_, Self>, start: usize, elements: &[$type]) {
assert!(start <= i32::MAX as usize); assert!(elements.len() <= i32::MAX as usize); let self_len = self.len() as jsize;
let elements_len = elements.len() as jsize;
let start = start as jsize;
let end = start + elements_len;
assert!(start <= end);
assert!(end <= self_len);
let env = self.env().as_raw();
unsafe {
((**env).v1_1.$set_region)(
env,
self.as_raw(),
start,
elements_len,
elements.as_ptr() as *const _,
)
};
}
}
};
}
primitive_array! { BooleanArray, c"[Z", bool { NewBooleanArray SetBooleanArrayRegion GetBooleanArrayRegion } }
primitive_array! { ByteArray, c"[B", jbyte { NewByteArray SetByteArrayRegion GetByteArrayRegion } }
primitive_array! { CharArray, c"[C", jchar { NewCharArray SetCharArrayRegion GetCharArrayRegion } }
primitive_array! { ShortArray, c"[S", jshort { NewShortArray SetShortArrayRegion GetShortArrayRegion } }
primitive_array! { IntArray, c"[I", jint { NewIntArray SetIntArrayRegion GetIntArrayRegion } }
primitive_array! { LongArray, c"[J", jlong { NewLongArray SetLongArrayRegion GetLongArrayRegion } }
primitive_array! { FloatArray, c"[F", jfloat { NewFloatArray SetFloatArrayRegion GetFloatArrayRegion } }
primitive_array! { DoubleArray, c"[D", jdouble { NewDoubleArray SetDoubleArrayRegion GetDoubleArrayRegion } }
pub struct ObjectArray<T: ReferenceType, E: ThrowableType>(
core::convert::Infallible,
PhantomData<(T, E)>,
);
unsafe impl<T: ReferenceType, E: ThrowableType> ReferenceType for ObjectArray<T, E> {}
unsafe impl<T: ReferenceType, E: ThrowableType> JniType for ObjectArray<T, E> {
fn static_with_jni_type<R>(callback: impl FnOnce(&CStr) -> R) -> R {
T::static_with_jni_type(|inner| {
let inner = inner.to_bytes();
let mut buf = Vec::with_capacity(inner.len() + 4);
buf.extend_from_slice(b"[L");
buf.extend_from_slice(inner);
buf.extend_from_slice(b";");
callback(&CString::new(buf).unwrap())
})
}
}
impl<T: ReferenceType, E: ThrowableType> ObjectArray<T, E> {
pub fn new<'env>(env: Env<'env>, size: usize) -> Local<'env, Self> {
assert!(size <= i32::MAX as usize); let class = T::static_with_jni_type(|t| unsafe { env.require_class(t) });
let size = size as jsize;
let object = unsafe {
let env = env.as_raw();
let fill = null_mut();
((**env).v1_2.NewObjectArray)(env, size, class, fill)
};
env.exception_check::<E>().map_err(|_| "OOM").unwrap();
unsafe {
let env = env.as_raw();
((**env).v1_2.DeleteLocalRef)(env, class);
}
unsafe { Local::from_raw(env, object) }
}
pub fn iter<'a, 'env>(self: &'a Ref<'env, Self>) -> ObjectArrayIter<'a, 'env, T, E> {
ObjectArrayIter {
array: self,
index: 0,
length: self.len(),
}
}
pub fn new_from<'env>(
env: Env<'env>,
elements: impl ExactSizeIterator<Item = impl AsArg<T>>,
) -> Local<'env, Self> {
let size = elements.len();
let array = Self::new(env, size);
let env = array.env().as_raw();
for (index, element) in elements.enumerate() {
assert!(index < size); unsafe {
((**env).v1_2.SetObjectArrayElement)(
env,
array.as_raw(),
index as jsize,
element.as_arg(),
)
};
}
array
}
pub fn len(self: &Ref<'_, Self>) -> usize {
let env = self.env().as_raw();
unsafe { ((**env).v1_2.GetArrayLength)(env, self.as_raw()) as usize }
}
pub fn get<'env>(
self: &Ref<'env, Self>,
index: usize,
) -> Result<Option<Local<'env, T>>, Local<'env, E>> {
assert!(index <= i32::MAX as usize); let index = index as jsize;
let env = self.env();
let result = unsafe {
let env = env.as_raw();
((**env).v1_2.GetObjectArrayElement)(env, self.as_raw(), index)
};
env.exception_check()?;
if result.is_null() {
Ok(None)
} else {
Ok(Some(unsafe { Local::from_raw(env, result) }))
}
}
pub fn set<'env>(
self: &Ref<'env, Self>,
index: usize,
value: impl AsArg<T>,
) -> Result<(), Local<'env, E>> {
assert!(index <= i32::MAX as usize); let index = index as jsize;
let env = self.env();
unsafe {
let env = env.as_raw();
((**env).v1_2.SetObjectArrayElement)(env, self.as_raw(), index, value.as_arg());
}
env.exception_check()
}
}
pub struct ObjectArrayIter<'a, 'env, T: ReferenceType, E: ThrowableType> {
array: &'a Ref<'env, ObjectArray<T, E>>,
index: usize,
length: usize,
}
impl<'a, 'env, T: ReferenceType, E: ThrowableType> Iterator for ObjectArrayIter<'a, 'env, T, E> {
type Item = Option<Local<'env, T>>;
fn next(&mut self) -> Option<Self::Item> {
let index = self.index;
if index < self.length {
self.index = index + 1;
Some(self.array.get(index).unwrap_or(None))
} else {
None
}
}
}