use std::{ffi::c_void, mem, num::NonZeroUsize, ptr::NonNull, slice};
use open62541_sys::{
UA_Array_delete, UA_Array_new, UA_copy, UA_init, UA_EMPTY_ARRAY_SENTINEL, UA_STATUSCODE_GOOD,
};
use crate::DataType;
#[allow(private_bounds)]
pub struct Array<T: DataType>(State<T>);
enum State<T: DataType> {
Empty,
NonEmpty {
ptr: NonNull<T::Inner>,
size: NonZeroUsize,
},
}
#[allow(private_bounds)]
impl<T: DataType> Array<T> {
#[must_use]
pub fn new(size: usize) -> Self {
let Some(size) = NonZeroUsize::new(size) else {
return Self(State::Empty);
};
let array = NonNull::new(unsafe { UA_Array_new(size.get(), T::data_type()) })
.expect("create new UA_Array")
.cast::<T::Inner>();
debug_assert_ne!(array.as_ptr().cast::<c_void>().cast_const(), unsafe {
UA_EMPTY_ARRAY_SENTINEL
});
let slice: &mut [T::Inner] =
unsafe { slice::from_raw_parts_mut(array.as_ptr(), size.get()) };
for element in slice {
unsafe { UA_init((element as *mut T::Inner).cast::<c_void>(), T::data_type()) }
}
Self(State::NonEmpty { ptr: array, size })
}
pub(crate) fn from_iter<I: Iterator<Item = T>>(iter: I) -> Self {
Self::from_slice(&iter.collect::<Vec<_>>())
}
pub fn from_slice(slice: &[T]) -> Self {
let Some(size) = NonZeroUsize::new(slice.len()) else {
return Self(State::Empty);
};
let array = NonNull::new(unsafe { UA_Array_new(slice.len(), T::data_type()) })
.expect("create new UA_Array")
.cast::<T::Inner>();
debug_assert_ne!(array.as_ptr().cast::<c_void>().cast_const(), unsafe {
UA_EMPTY_ARRAY_SENTINEL
});
let dst: &mut [T::Inner] = unsafe { slice::from_raw_parts_mut(array.as_ptr(), size.get()) };
for (src, dst) in slice.iter().zip(dst) {
let result = unsafe {
UA_copy(
src.as_ptr().cast::<c_void>(),
(dst as *mut T::Inner).cast::<c_void>(),
T::data_type(),
)
};
if result != UA_STATUSCODE_GOOD {
unsafe {
UA_Array_delete(array.as_ptr().cast::<c_void>(), size.get(), T::data_type());
}
panic!("create new UA_Array")
}
}
Self(State::NonEmpty { ptr: array, size })
}
#[allow(private_interfaces)]
#[must_use]
pub(crate) fn from_raw_parts(ptr: *const T::Inner, size: usize) -> Option<Self> {
if size == 0 {
if ptr.is_null() {
return None;
}
debug_assert_eq!(ptr.cast::<c_void>(), unsafe { UA_EMPTY_ARRAY_SENTINEL });
return Some(Self(State::Empty));
}
debug_assert!(!ptr.is_null());
debug_assert_ne!(ptr.cast::<c_void>(), unsafe { UA_EMPTY_ARRAY_SENTINEL });
let slice = unsafe { slice::from_raw_parts(ptr.cast::<T>(), size) };
Some(Self::from_slice(slice))
}
#[must_use]
pub const fn len(&self) -> usize {
match self.0 {
State::Empty => 0,
State::NonEmpty { size, .. } => size.get(),
}
}
#[must_use]
pub const fn is_empty(&self) -> bool {
match self.0 {
State::Empty => true,
State::NonEmpty { .. } => false,
}
}
#[allow(private_interfaces)]
#[must_use]
pub const fn as_slice(&self) -> &[T] {
match self.0 {
State::Empty => &[],
State::NonEmpty { ptr, size } => {
unsafe { slice::from_raw_parts(ptr.as_ptr().cast::<T>(), size.get()) }
}
}
}
#[allow(private_interfaces)]
#[must_use]
pub fn as_slice_mut(&mut self) -> &mut [T] {
match self.0 {
State::Empty => &mut [],
State::NonEmpty { ptr, size } => {
unsafe { slice::from_raw_parts_mut(ptr.as_ptr().cast::<T>(), size.get()) }
}
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.as_slice().iter()
}
#[must_use]
pub(crate) fn into_raw_parts(self) -> (usize, *mut T::Inner) {
let (ptr, size) = match self.0 {
State::Empty => {
let ptr = unsafe { UA_EMPTY_ARRAY_SENTINEL };
(ptr.cast::<T::Inner>().cast_mut(), 0)
}
State::NonEmpty { ptr, size } => (ptr.as_ptr(), size.get()),
};
mem::forget(self);
(size, ptr)
}
pub(crate) fn move_into_raw(self, dst_size: &mut usize, dst: &mut *mut T::Inner) {
let _unused = Self::from_raw_parts(*dst, *dst_size);
let (size, ptr) = self.into_raw_parts();
*dst_size = size;
*dst = ptr;
}
}
impl<T: DataType> Drop for Array<T> {
fn drop(&mut self) {
match self.0 {
State::Empty => {
}
State::NonEmpty { ptr, size } => {
unsafe {
UA_Array_delete(ptr.as_ptr().cast::<c_void>(), size.get(), T::data_type());
}
}
}
}
}
#[cfg(test)]
mod tests {
use std::ffi::CString;
use open62541_sys::UA_NODEID_STRING_ALLOC;
use crate::ua;
use super::*;
#[test]
fn create_and_drop_array() {
const STRING: &str = "LoremIpsum";
const LEN: usize = 123;
const POS: usize = 42;
type T = ua::NodeId;
let mut array: Array<T> = Array::new(LEN);
let target = array.as_slice_mut().get_mut(POS).unwrap();
ua::NodeId::string(0, STRING).clone_into_raw(unsafe { target.as_mut() });
drop(array);
let mut array: Array<T> = Array::new(LEN);
let target = array.as_slice_mut().get_mut(POS).unwrap();
ua::NodeId::string(0, STRING).clone_into_raw(unsafe { target.as_mut() });
let (size, ptr) = array.into_raw_parts();
assert_eq!(size, LEN);
unsafe { UA_Array_delete(ptr.cast(), size, T::data_type()) }
let size = LEN;
let ptr: *mut <T as DataType>::Inner = unsafe { UA_Array_new(size, T::data_type()) }.cast();
let string = CString::new(STRING).unwrap();
*unsafe { ptr.add(POS).as_mut() }.unwrap() =
unsafe { UA_NODEID_STRING_ALLOC(0, string.as_ptr()) };
drop(string);
let array: Array<T> = Array::from_raw_parts(ptr, size).unwrap();
assert_eq!(array.len(), LEN);
drop(array);
}
}