#[cfg(all(feature = "std", not(all(target_family = "wasm", target_os = "unknown"))))]
use alloc::sync::Arc;
use alloc::{ffi::CString, format};
use core::{
ffi::c_char,
iter,
ptr::{self, NonNull},
slice
};
use smallvec::SmallVec;
#[cfg(all(feature = "std", not(all(target_family = "wasm", target_os = "unknown"))))]
use super::r#async::{AsyncInferenceContext, InferenceFut, InferenceFutInner, async_callback};
use super::{
Session, SessionInputValue, SessionInputs, SessionOutputs,
run_options::{RunOptions, SelectedOutputMarker, UntypedRunOptions}
};
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
use super::{io_binding::IoBinding, run_options::NoSelectedOutputs};
use crate::{
AsPointer,
error::{Error, ErrorCode, Result},
ortsys,
util::{STACK_SESSION_INPUTS, STACK_SESSION_OUTPUTS, with_cstr_ptr_array},
value::DynValue
};
super::mut_methods! {
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub fn run['s, 'i, 'v: 'i, const N: usize](&'s mut self, input_values: impl Into<SessionInputs<'i, 'v, N>>) -> Result<SessionOutputs<'s>> {
match input_values.into() {
SessionInputs::ValueSlice(input_values) => {
self.run_inner(self.inputs.iter().map(|input| input.name()).collect(), input_values.iter().collect(), None)
}
SessionInputs::ValueArray(input_values) => {
self.run_inner(self.inputs.iter().map(|input| input.name()).collect(), input_values.iter().collect(), None)
}
SessionInputs::ValueMap(input_values) => {
self.run_inner(input_values.iter().map(|(k, _)| k.as_ref()).collect(), input_values.iter().map(|(_, v)| v).collect(), None)
}
}
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub fn run_with_options['r, 's: 'r, 'i, 'v: 'i, O: SelectedOutputMarker, const N: usize](
&'s mut self,
input_values: impl Into<SessionInputs<'i, 'v, N>>,
run_options: &'r RunOptions<O>
) -> Result<SessionOutputs<'r>> {
match input_values.into() {
SessionInputs::ValueSlice(input_values) => {
self.run_inner(self.inputs.iter().map(|input| input.name()).collect(), input_values.iter().collect(), Some(&run_options.inner))
}
SessionInputs::ValueArray(input_values) => {
self.run_inner(self.inputs.iter().map(|input| input.name()).collect(), input_values.iter().collect(), Some(&run_options.inner))
}
SessionInputs::ValueMap(input_values) => {
self.run_inner(input_values.iter().map(|(k, _)| k.as_ref()).collect(), input_values.iter().map(|(_, v)| v).collect(), Some(&run_options.inner))
}
}
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub fn run_binding['b, 's: 'b](&'s mut self, binding: &'b IoBinding) -> Result<SessionOutputs<'b>> {
self.run_binding_inner(binding, None)
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub fn run_binding_with_options['r, 'b, 's: 'b](
&'s mut self,
binding: &'b IoBinding,
run_options: &'r RunOptions<NoSelectedOutputs>
) -> Result<SessionOutputs<'b>> {
self.run_binding_inner(binding, Some(run_options))
}
#[cfg(all(feature = "std", not(all(target_family = "wasm", target_os = "unknown"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn run_async['r, 's: 'r, 'i, O: SelectedOutputMarker, const N: usize](
&'s mut self,
input_values: impl Into<SessionInputs<'i, 'static, N>>,
run_options: &'r RunOptions<O>
) -> Result<InferenceFut<'r>> {
match input_values.into() {
SessionInputs::ValueSlice(input_values) => {
self.run_inner_async(self.inputs.iter().map(|input| input.name()).collect(), input_values.iter().collect(), &run_options.inner)
}
SessionInputs::ValueArray(input_values) => {
self.run_inner_async(self.inputs.iter().map(|input| input.name()).collect(), input_values.iter().collect(), &run_options.inner)
}
SessionInputs::ValueMap(input_values) => {
self.run_inner_async(input_values.iter().map(|(k, _)| k.as_ref()).collect(), input_values.iter().map(|(_, v)| v).collect(), &run_options.inner)
}
}
}
}
super::mut_methods! {
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
pub async fn run_async['r, 's: 'r, 'i, O: SelectedOutputMarker, const N: usize](
&'s mut self,
input_values: impl Into<SessionInputs<'i, 'static, N>>,
run_options: &'r RunOptions<O>
) -> Result<SessionOutputs<'r>> {
match input_values.into() {
SessionInputs::ValueSlice(input_values) => {
self.run_inner_async(self.inputs.iter().map(|input| input.name()).collect(), input_values.iter().collect(), &run_options.inner).await
}
SessionInputs::ValueArray(input_values) => {
self.run_inner_async(self.inputs.iter().map(|input| input.name()).collect(), input_values.iter().collect(), &run_options.inner).await
}
SessionInputs::ValueMap(input_values) => {
self.run_inner_async(input_values.iter().map(|(k, _)| k.as_ref()).collect(), input_values.iter().map(|(_, v)| v).collect(), &run_options.inner).await
}
}
}
}
impl Session {
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
fn run_inner<'i, 'r, 's: 'r, 'v: 'i>(
&'s self,
input_names: SmallVec<[&str; STACK_SESSION_INPUTS]>,
input_values: SmallVec<[&'i SessionInputValue<'v>; STACK_SESSION_INPUTS]>,
run_options: Option<&'r UntypedRunOptions>
) -> Result<SessionOutputs<'r>> {
if input_values.len() > input_names.len() {
return Err(Error::new_with_code(
ErrorCode::InvalidArgument,
format!("{} inputs were provided, but the model only accepts {}.", input_values.len(), input_names.len())
));
}
let (output_names, mut output_tensors) = match run_options {
Some(r) => r.outputs.resolve_outputs(&self.outputs),
None => (self.outputs.iter().map(|o| o.name()).collect(), iter::repeat_with(|| None).take(self.outputs.len()).collect())
};
let mut output_value_ptrs: SmallVec<[*mut ort_sys::OrtValue; STACK_SESSION_OUTPUTS]> = output_tensors
.iter_mut()
.map(|c| match c {
Some(v) => v.ptr_mut(),
None => ptr::null_mut()
})
.collect();
let input_value_ptrs: SmallVec<[*const ort_sys::OrtValue; STACK_SESSION_INPUTS]> = input_values.iter().map(|c| c.ptr()).collect();
let run_options_ptr = if let Some(run_options) = &run_options { run_options.ptr.as_ptr() } else { ptr::null() };
with_cstr_ptr_array(&input_names, &mut |input_name_ptrs| {
with_cstr_ptr_array(&output_names, &mut |output_name_ptrs| {
ortsys![
unsafe Run(
self.inner.session_ptr.as_ptr(),
run_options_ptr,
input_name_ptrs.as_ptr(),
input_value_ptrs.as_ptr(),
input_value_ptrs.len(),
output_name_ptrs.as_ptr(),
output_name_ptrs.len(),
output_value_ptrs.as_mut_ptr()
)?
];
Ok(())
})
})?;
let outputs = output_tensors
.into_iter()
.enumerate()
.map(|(i, v)| match v {
Some(value) => value,
None => unsafe {
DynValue::from_session_output_ptr(
NonNull::new(output_value_ptrs[i]).expect("OrtValue ptr returned from session Run should not be null"),
&self.inner
)
}
})
.collect();
Ok(SessionOutputs::new(output_names, outputs))
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
fn run_binding_inner<'r, 'b, 's: 'b>(
&'s self, binding: &'b IoBinding,
run_options: Option<&'r RunOptions<NoSelectedOutputs>>
) -> Result<SessionOutputs<'b>> {
use crate::util::run_on_drop;
let run_options_ptr = if let Some(run_options) = run_options { run_options.ptr() } else { ptr::null() };
ortsys![unsafe RunWithBinding(self.inner.ptr().cast_mut(), run_options_ptr, binding.ptr())?];
let mut count = binding.output_values.len();
if count > 0 {
let mut output_values_ptr: *mut *mut ort_sys::OrtValue = ptr::null_mut();
ortsys![unsafe GetBoundOutputValues(binding.ptr(), self.inner.allocator.ptr().cast_mut(), &mut output_values_ptr, &mut count)?; nonNull(output_values_ptr)];
let _guard = run_on_drop(|| unsafe {
self.inner.allocator.free(output_values_ptr.as_ptr());
});
let output_values = unsafe { slice::from_raw_parts(output_values_ptr.as_ptr(), count) }
.iter()
.map(|ptr| unsafe {
DynValue::from_session_output_ptr(
NonNull::new(*ptr).expect("OrtValue ptrs returned by GetBoundOutputValues should not be null"),
&self.inner
)
})
.collect();
Ok(SessionOutputs::new(binding.output_values.iter().map(|(k, _)| k.as_str()).collect(), output_values))
} else {
Ok(SessionOutputs::new_empty())
}
}
#[cfg(all(feature = "std", not(all(target_family = "wasm", target_os = "unknown"))))]
fn run_inner_async<'i, 'r, 's: 'r>(
&'s self, input_names: SmallVec<[&str; STACK_SESSION_INPUTS]>,
input_values: SmallVec<[&SessionInputValue<'static>; STACK_SESSION_INPUTS]>,
run_options: &'r Arc<UntypedRunOptions>
) -> Result<InferenceFut<'r>> {
if input_values.len() > input_names.len() {
return Err(Error::new_with_code(
ErrorCode::InvalidArgument,
format!("{} inputs were provided, but the model only accepts {}.", input_values.len(), input_names.len())
));
}
let input_name_ptrs = input_names
.into_iter()
.map(|name| CString::new(name.as_bytes()))
.collect::<Result<SmallVec<[CString; STACK_SESSION_INPUTS]>, _>>()?
.into_iter()
.map(|s| s.into_raw().cast_const())
.collect::<SmallVec<[*const c_char; STACK_SESSION_INPUTS]>>();
let mut input_inner_holders = SmallVec::with_capacity(input_values.len());
let mut input_ort_values = SmallVec::with_capacity(input_values.len());
for input in input_values {
input_ort_values.push(input.ptr());
input_inner_holders.push(Arc::clone(match input {
SessionInputValue::ViewMut(v) => &(**v).inner,
SessionInputValue::View(v) => &(**v).inner,
SessionInputValue::Owned(v) => &v.inner
}));
}
let (output_names, mut output_tensors) = run_options.outputs.resolve_outputs(&self.outputs);
let output_name_ptrs = output_names
.iter()
.map(|n| CString::new(*n).unwrap_or_else(|_| unreachable!()))
.map(|n| n.into_raw().cast_const())
.collect();
let output_tensor_ptrs = output_tensors
.iter_mut()
.map(|c| match c {
Some(v) => v.ptr_mut(),
None => ptr::null_mut()
})
.collect();
let async_inner = Arc::new(InferenceFutInner::new(Arc::clone(run_options)));
let mut ctx = Box::<AsyncInferenceContext>::new_uninit();
unsafe {
use core::ptr::write;
let ctx = ctx.as_mut_ptr();
write(&raw mut (*ctx).inner, Arc::clone(&async_inner));
write(&raw mut (*ctx).input_ort_values, input_ort_values);
write(&raw mut (*ctx)._input_inner_holders, input_inner_holders);
write(&raw mut (*ctx).input_name_ptrs, input_name_ptrs);
write(&raw mut (*ctx).output_name_ptrs, output_name_ptrs);
write(&raw mut (*ctx).output_names, output_names);
write(&raw mut (*ctx).output_value_ptrs, output_tensor_ptrs);
write(&raw mut (*ctx).output_values, output_tensors);
write(&raw mut (*ctx).session_inner, Arc::clone(&self.inner));
};
let ctx = Box::leak(unsafe { ctx.assume_init() });
crate::logging::create!(AsyncInferenceContext, ctx);
let res = ortsys![@ort:
unsafe RunAsync(
self.inner.session_ptr.as_ptr(),
run_options.ptr.as_ptr(),
ctx.input_name_ptrs.as_ptr(),
ctx.input_ort_values.as_ptr(),
ctx.input_ort_values.len(),
ctx.output_name_ptrs.as_ptr(),
ctx.output_name_ptrs.len(),
ctx.output_value_ptrs.as_mut_ptr(),
Some(async_callback),
ctx as *mut _ as *mut ort_sys::c_void
) as Result
];
if let Err(e) = res {
let ctx = unsafe { Box::from_raw(ctx as *mut AsyncInferenceContext<'_>) };
crate::logging::drop!(AsyncInferenceContext, &*ctx);
ctx.free_name_ptrs();
return Err(e);
}
Ok(InferenceFut::new(async_inner))
}
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
async fn run_inner_async<'i, 'r, 's: 'r>(
&'s self,
input_names: SmallVec<[&str; STACK_SESSION_INPUTS]>,
input_values: SmallVec<[&SessionInputValue<'static>; STACK_SESSION_INPUTS]>,
run_options: &'r UntypedRunOptions
) -> Result<SessionOutputs<'r>> {
if input_values.len() > input_names.len() {
return Err(Error::new_with_code(
ErrorCode::InvalidArgument,
format!("{} inputs were provided, but the model only accepts {}.", input_values.len(), input_names.len())
));
}
let (output_names, mut output_tensors) = run_options.outputs.resolve_outputs(&self.outputs);
let mut output_value_ptrs: SmallVec<[*mut ort_sys::OrtValue; STACK_SESSION_OUTPUTS]> = output_tensors
.iter_mut()
.map(|c| match c {
Some(v) => v.ptr_mut(),
None => ptr::null_mut()
})
.collect();
let input_value_ptrs: SmallVec<[*const ort_sys::OrtValue; STACK_SESSION_INPUTS]> = input_values.iter().map(|c| c.ptr()).collect();
let status = ortsys![
unsafe RunAsync(
self.inner.session_ptr.as_ptr(),
run_options.ptr.as_ptr(),
&input_names,
&input_value_ptrs,
&output_names,
&mut output_value_ptrs
)
]
.await;
unsafe { Error::result_from_status(status) }?;
let outputs = output_tensors
.into_iter()
.enumerate()
.map(|(i, v)| match v {
Some(value) => value,
None => unsafe {
DynValue::from_session_output_ptr(
NonNull::new(output_value_ptrs[i]).expect("OrtValue ptr returned from session Run should not be null"),
&self.inner
)
}
})
.collect();
Ok(SessionOutputs::new(output_names, outputs))
}
}