use std::{
convert::{TryFrom, TryInto},
ptr,
};
use ndarray::ArrayD;
use widestring::U16CString;
use winapi::{
shared::{
minwindef::UINT,
winerror::NOERROR,
wtypes::{
VARIANT_BOOL, VARTYPE, VT_BOOL, VT_BSTR, VT_I2, VT_I4, VT_I8, VT_INT, VT_R4, VT_R8,
VT_UI2, VT_UI4, VT_UI8, VT_UINT,
},
},
um::{
oaidl::{SAFEARRAY, SAFEARRAYBOUND},
oleauto::SysAllocStringLen,
winnt::PVOID,
},
};
use crate::{errors::SafeArrayError, SafeArray, BOOL_FALSE, BOOL_TRUE};
use super::{SafeArrayGetElement, SafeArrayPutElement};
extern "system" {
pub fn SafeArrayCreate(
vt: VARTYPE,
cDims: UINT,
rgsabound: *const SAFEARRAYBOUND,
) -> *mut SAFEARRAY;
}
impl SafeArray {
fn from_arrayd<T>(src: &ArrayD<T>, vt: VARTYPE) -> Result<SafeArray, SafeArrayError> {
let dims = src.shape();
let bounds: Vec<SAFEARRAYBOUND> = dims
.iter()
.rev()
.map(|count| SAFEARRAYBOUND {
lLbound: 0,
cElements: *count as u32,
})
.collect();
let sa = unsafe { SafeArrayCreate(vt, bounds.len() as u32, &bounds[0]) };
if sa.is_null() {
return Err(SafeArrayError::NullPointer);
}
let mut indice: Vec<usize> = dims.iter().map(|_| 0).collect();
loop {
let psa_indices = to_psa_indice(&indice);
let v: *const T = &src[indice.as_slice()];
let result = unsafe { SafeArrayPutElement(sa, &psa_indices[0], v as PVOID) };
if result != NOERROR {
return Err(SafeArrayError::from(result));
}
if !next_indice(dims, &mut indice) {
break;
}
}
Ok(SafeArray {
psa: sa,
prevent_destroy: false,
})
}
fn to_arrayd<R>(&self, expected_type: VARTYPE) -> Result<ArrayD<R>, SafeArrayError>
where
R: Default,
{
let vt = self.get_vartype()?;
if vt != expected_type {
return Err(SafeArrayError::IncorrectDataType(vt));
}
let bounds = self.dimensions()?;
for b in &bounds {
if b.lLbound != 0 {
panic!("lower bounds other than 0 not supported");
}
}
let bounds: Vec<usize> = bounds.iter().rev().map(|b| b.cElements as usize).collect();
let mut dest = ArrayD::default(bounds.as_slice());
let mut indice: Vec<usize> = bounds.iter().map(|_| 0).collect();
loop {
let psa_indices = to_psa_indice(&indice);
let mut v: R = R::default();
let v_ref: *mut R = &mut v;
let result =
unsafe { SafeArrayGetElement(self.psa, psa_indices.as_ptr(), v_ref as PVOID) };
if result != NOERROR {
return Err(SafeArrayError::from(result));
}
dest[indice.as_slice()] = v;
if !next_indice(&bounds, &mut indice) {
break;
}
}
Ok(dest)
}
fn from_arrayd_str<S>(src: &ArrayD<S>) -> Result<SafeArray, SafeArrayError>
where
S: AsRef<str>,
{
let dims = src.shape();
let bounds: Vec<SAFEARRAYBOUND> = dims
.iter()
.rev()
.map(|count| SAFEARRAYBOUND {
lLbound: 0,
cElements: *count as u32,
})
.collect();
let sa = unsafe { SafeArrayCreate(VT_BSTR as u16, bounds.len() as u32, &bounds[0]) };
if sa.is_null() {
return Err(SafeArrayError::NullPointer);
}
let mut indice: Vec<usize> = dims.iter().map(|_| 0).collect();
loop {
let psa_indices = to_psa_indice(&indice);
let bstr = U16CString::from_str(src[indice.as_slice()].as_ref())?;
let result = unsafe {
let bstr_ptr = SysAllocStringLen(bstr.as_ptr(), bstr.len() as UINT);
SafeArrayPutElement(sa, &psa_indices[0], bstr_ptr as PVOID)
};
if result != NOERROR {
return Err(SafeArrayError::from(result));
}
if !next_indice(dims, &mut indice) {
break;
}
}
Ok(SafeArray {
psa: sa,
prevent_destroy: false,
})
}
fn to_string_arrayd(&self) -> Result<ArrayD<String>, SafeArrayError> {
let vt = self.get_vartype()?;
if vt != VT_BSTR as u16 {
return Err(SafeArrayError::IncorrectDataType(vt));
}
let bounds = self.dimensions()?;
for b in &bounds {
if b.lLbound != 0 {
panic!("lower bounds other than 0 not supported");
}
}
let bounds: Vec<usize> = bounds.iter().rev().map(|b| b.cElements as usize).collect();
let mut dest = ArrayD::default(bounds.as_slice());
let mut indice: Vec<usize> = bounds.iter().map(|_| 0).collect();
loop {
let psa_indices = to_psa_indice(&indice);
let v = unsafe {
let mut v_ptr = ptr::null();
let v_ptr_ptr: *mut *const u16 = &mut v_ptr;
match SafeArrayGetElement(self.psa, psa_indices.as_ptr(), v_ptr_ptr as PVOID) {
NOERROR => (),
hr => return Err(SafeArrayError::from(hr)),
};
let u16_str = U16CString::from_ptr_str(v_ptr);
u16_str.to_string()?
};
dest[indice.as_slice()] = v;
if !next_indice(&bounds, &mut indice) {
break;
}
}
Ok(dest)
}
}
fn next_indice(bounds: &[usize], indice: &mut [usize]) -> bool {
for pos in (0..indice.len()).rev() {
if indice[pos] + 1 < bounds[pos] {
indice[pos] += 1;
return true;
}
indice[pos] = 0;
}
false
}
#[inline]
fn to_psa_indice(src: &[usize]) -> Vec<i32> {
src.to_owned().iter().rev().map(|v| *v as i32).collect()
}
impl TryFrom<&ArrayD<bool>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<bool>) -> Result<Self, Self::Error> {
let mapped = value.map(|v| if *v { BOOL_TRUE } else { BOOL_FALSE });
SafeArray::from_arrayd(&mapped, VT_BOOL as u16)
}
}
impl TryInto<ArrayD<bool>> for &SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<bool>, Self::Error> {
let result: ArrayD<VARIANT_BOOL> = self.to_arrayd(VT_BOOL as u16)?;
Ok(result.map(|v| *v == BOOL_TRUE))
}
}
impl TryInto<ArrayD<bool>> for SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<bool>, Self::Error> {
let result: ArrayD<VARIANT_BOOL> = self.to_arrayd(VT_BOOL as u16)?;
Ok(result.map(|v| *v == BOOL_TRUE))
}
}
impl TryFrom<&ArrayD<i16>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<i16>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd(value, VT_I2 as u16)
}
}
impl TryInto<ArrayD<i16>> for SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<i16>, Self::Error> {
self.to_arrayd(VT_I2 as u16)
}
}
impl TryInto<ArrayD<i16>> for &SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<i16>, Self::Error> {
self.to_arrayd(VT_I2 as u16)
}
}
impl TryFrom<&ArrayD<i32>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<i32>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd(value, VT_I4 as u16)
}
}
impl TryInto<ArrayD<i32>> for SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<i32>, Self::Error> {
match self.get_vartype()? as u32 {
VT_I4 => self.to_arrayd(VT_I4 as u16),
VT_INT => self.to_arrayd(VT_INT as u16),
vt => Err(SafeArrayError::IncorrectDataType(vt as u16)),
}
}
}
impl TryInto<ArrayD<i32>> for &SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<i32>, Self::Error> {
match self.get_vartype()? as u32 {
VT_I4 => self.to_arrayd(VT_I4 as u16),
VT_INT => self.to_arrayd(VT_INT as u16),
vt => Err(SafeArrayError::IncorrectDataType(vt as u16)),
}
}
}
impl TryFrom<&ArrayD<i64>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<i64>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd(value, VT_I8 as u16)
}
}
impl TryInto<ArrayD<i64>> for SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<i64>, Self::Error> {
self.to_arrayd(VT_I8 as u16)
}
}
impl TryInto<ArrayD<i64>> for &SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<i64>, Self::Error> {
self.to_arrayd(VT_I8 as u16)
}
}
impl TryFrom<&ArrayD<f32>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<f32>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd(value, VT_R4 as u16)
}
}
impl TryInto<ArrayD<f32>> for SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<f32>, Self::Error> {
self.to_arrayd(VT_R4 as u16)
}
}
impl TryInto<ArrayD<f32>> for &SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<f32>, Self::Error> {
self.to_arrayd(VT_R4 as u16)
}
}
impl TryFrom<&ArrayD<f64>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<f64>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd(value, VT_R8 as u16)
}
}
impl TryInto<ArrayD<f64>> for SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<f64>, Self::Error> {
self.to_arrayd(VT_R8 as u16)
}
}
impl TryInto<ArrayD<f64>> for &SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<f64>, Self::Error> {
self.to_arrayd(VT_R8 as u16)
}
}
impl TryFrom<&ArrayD<u16>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<u16>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd(value, VT_UI2 as u16)
}
}
impl TryInto<ArrayD<u16>> for SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<u16>, Self::Error> {
self.to_arrayd(VT_UI2 as u16)
}
}
impl TryInto<ArrayD<u16>> for &SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<u16>, Self::Error> {
self.to_arrayd(VT_UI2 as u16)
}
}
impl TryFrom<&ArrayD<u32>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<u32>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd(value, VT_UI4 as u16)
}
}
impl TryInto<ArrayD<u32>> for SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<u32>, Self::Error> {
match self.get_vartype()? as u32 {
VT_UI4 => self.to_arrayd(VT_UI4 as u16),
VT_UINT => self.to_arrayd(VT_UINT as u16),
vt => Err(SafeArrayError::IncorrectDataType(vt as u16)),
}
}
}
impl TryInto<ArrayD<u32>> for &SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<u32>, Self::Error> {
match self.get_vartype()? as u32 {
VT_UI4 => self.to_arrayd(VT_UI4 as u16),
VT_UINT => self.to_arrayd(VT_UINT as u16),
vt => Err(SafeArrayError::IncorrectDataType(vt as u16)),
}
}
}
impl TryFrom<&ArrayD<u64>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<u64>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd(value, VT_UI8 as u16)
}
}
impl TryInto<ArrayD<u64>> for SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<u64>, Self::Error> {
self.to_arrayd(VT_UI8 as u16)
}
}
impl TryInto<ArrayD<u64>> for &SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<u64>, Self::Error> {
self.to_arrayd(VT_UI8 as u16)
}
}
impl TryFrom<&ArrayD<String>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<String>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd_str(value)
}
}
impl TryFrom<&ArrayD<&String>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<&String>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd_str(value)
}
}
impl TryFrom<&ArrayD<&str>> for SafeArray {
type Error = SafeArrayError;
fn try_from(value: &ArrayD<&str>) -> Result<Self, Self::Error> {
SafeArray::from_arrayd_str(value)
}
}
impl TryInto<ArrayD<String>> for SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<String>, Self::Error> {
self.to_string_arrayd()
}
}
impl TryInto<ArrayD<String>> for &SafeArray {
type Error = SafeArrayError;
fn try_into(self) -> Result<ArrayD<String>, Self::Error> {
self.to_string_arrayd()
}
}