use onnx_runtime_ir::DataType;
use crate::dim_expr::DimExpr;
pub const MAX_SHAPE_DATA_ELEMS: usize = 1024;
#[derive(Clone, Debug, PartialEq)]
pub struct ShapeData {
pub dtype: DataType,
pub dims: Vec<usize>,
pub elems: Vec<DimExpr>,
pub float_elems: Option<Vec<f64>>,
}
impl ShapeData {
pub fn scalar(dtype: DataType, value: DimExpr) -> Self {
Self {
dtype,
dims: Vec::new(),
elems: vec![value],
float_elems: None,
}
}
pub fn vector(dtype: DataType, elems: Vec<DimExpr>) -> Self {
Self {
dims: vec![elems.len()],
dtype,
elems,
float_elems: None,
}
}
pub fn float_scalar(dtype: DataType, value: f64) -> Self {
Self {
dtype,
dims: Vec::new(),
elems: Vec::new(),
float_elems: Some(vec![value]),
}
}
pub fn as_float_scalar(&self) -> Option<f64> {
let floats = self.float_elems.as_ref()?;
(self.is_scalar() && floats.len() == 1).then(|| floats[0])
}
pub fn is_scalar(&self) -> bool {
self.dims.is_empty()
}
pub fn within_bounds(&self) -> bool {
self.elems.len() <= MAX_SHAPE_DATA_ELEMS
}
pub fn as_shape(&self) -> Vec<DimExpr> {
self.elems.clone()
}
pub fn from_tensor(dtype: DataType, dims: &[usize], data: &[u8]) -> Option<Self> {
if dims.len() > 1 {
return None;
}
let numel: usize = if dims.is_empty() {
1
} else {
dims.iter().product()
};
if numel > MAX_SHAPE_DATA_ELEMS {
return None;
}
if dtype.is_float() {
if !dims.is_empty() {
return None;
}
let value = read_float_scalar(dtype, data)?;
return Some(Self::float_scalar(dtype, value));
}
let ints = read_ints(dtype, numel, data)?;
let elems: Vec<DimExpr> = ints.into_iter().map(DimExpr::constant).collect();
Some(Self {
dtype,
dims: dims.to_vec(),
elems,
float_elems: None,
})
}
}
fn read_float_scalar(dtype: DataType, data: &[u8]) -> Option<f64> {
match dtype {
DataType::Float32 => {
let arr: [u8; 4] = data.get(..4)?.try_into().ok()?;
Some(f32::from_le_bytes(arr) as f64)
}
DataType::Float64 => {
let arr: [u8; 8] = data.get(..8)?.try_into().ok()?;
Some(f64::from_le_bytes(arr))
}
_ => None,
}
}
fn read_ints(dtype: DataType, numel: usize, data: &[u8]) -> Option<Vec<i64>> {
macro_rules! read {
($ty:ty, $sz:expr) => {{
if data.len() < numel * $sz {
return None;
}
data.chunks_exact($sz)
.take(numel)
.map(|c| {
let arr: [u8; $sz] = c.try_into().ok()?;
Some(<$ty>::from_le_bytes(arr) as i64)
})
.collect::<Option<Vec<i64>>>()
}};
}
match dtype {
DataType::Int64 => read!(i64, 8),
DataType::Uint64 => read!(u64, 8),
DataType::Int32 => read!(i32, 4),
DataType::Uint32 => read!(u32, 4),
DataType::Int16 => read!(i16, 2),
DataType::Uint16 => read!(u16, 2),
DataType::Int8 => read!(i8, 1),
DataType::Uint8 | DataType::Bool => read!(u8, 1),
_ => None,
}
}