use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub enum ImageData {
I16_2D(Vec<Vec<i16>>),
I32_2D(Vec<Vec<i32>>),
F64_2D(Vec<Vec<f64>>),
I16_3D(Vec<Vec<Vec<i16>>>),
I32_3D(Vec<Vec<Vec<i32>>>),
F64_3D(Vec<Vec<Vec<f64>>>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ImageArrayResponse {
#[serde(rename = "Type")]
pub image_type: i32,
pub rank: i32,
pub value: serde_json::Value,
}
impl ImageData {
pub fn image_type(&self) -> i32 {
match self {
Self::I16_2D(_) | Self::I16_3D(_) => 1,
Self::I32_2D(_) | Self::I32_3D(_) => 2,
Self::F64_2D(_) | Self::F64_3D(_) => 3,
}
}
pub fn rank(&self) -> i32 {
match self {
Self::I16_2D(_) | Self::I32_2D(_) | Self::F64_2D(_) => 2,
Self::I16_3D(_) | Self::I32_3D(_) | Self::F64_3D(_) => 3,
}
}
pub fn to_json_value(&self) -> serde_json::Value {
match self {
Self::I16_2D(data) => serde_json::to_value(data).unwrap_or_default(),
Self::I32_2D(data) => serde_json::to_value(data).unwrap_or_default(),
Self::F64_2D(data) => serde_json::to_value(data).unwrap_or_default(),
Self::I16_3D(data) => serde_json::to_value(data).unwrap_or_default(),
Self::I32_3D(data) => serde_json::to_value(data).unwrap_or_default(),
Self::F64_3D(data) => serde_json::to_value(data).unwrap_or_default(),
}
}
pub fn to_response(&self) -> ImageArrayResponse {
ImageArrayResponse {
image_type: self.image_type(),
rank: self.rank(),
value: self.to_json_value(),
}
}
pub fn dimensions(&self) -> (usize, usize, usize) {
match self {
Self::I16_2D(data) => (data.len(), data.first().map_or(0, |r| r.len()), 0),
Self::I32_2D(data) => (data.len(), data.first().map_or(0, |r| r.len()), 0),
Self::F64_2D(data) => (data.len(), data.first().map_or(0, |r| r.len()), 0),
Self::I16_3D(data) => (
data.len(),
data.first().map_or(0, |p| p.len()),
data.first().and_then(|p| p.first()).map_or(0, |r| r.len()),
),
Self::I32_3D(data) => (
data.len(),
data.first().map_or(0, |p| p.len()),
data.first().and_then(|p| p.first()).map_or(0, |r| r.len()),
),
Self::F64_3D(data) => (
data.len(),
data.first().map_or(0, |p| p.len()),
data.first().and_then(|p| p.first()).map_or(0, |r| r.len()),
),
}
}
}
pub mod imagebytes {
pub const METADATA_VERSION: i32 = 1;
pub fn encode(
data: &super::ImageData,
error_number: i32,
client_transaction_id: u32,
server_transaction_id: u32,
) -> Vec<u8> {
let (dim1, dim2, dim3) = data.dimensions();
let image_type = data.image_type();
let rank = data.rank();
let data_start: i32 = 44;
let mut buf = Vec::new();
buf.extend_from_slice(&METADATA_VERSION.to_le_bytes());
buf.extend_from_slice(&error_number.to_le_bytes());
buf.extend_from_slice(&client_transaction_id.to_le_bytes());
buf.extend_from_slice(&server_transaction_id.to_le_bytes());
buf.extend_from_slice(&data_start.to_le_bytes());
buf.extend_from_slice(&image_type.to_le_bytes());
buf.extend_from_slice(&image_type.to_le_bytes()); buf.extend_from_slice(&rank.to_le_bytes());
buf.extend_from_slice(&(dim1 as i32).to_le_bytes());
buf.extend_from_slice(&(dim2 as i32).to_le_bytes());
buf.extend_from_slice(&(dim3 as i32).to_le_bytes());
match data {
super::ImageData::I16_2D(rows) => {
for row in rows {
for &pixel in row {
buf.extend_from_slice(&pixel.to_le_bytes());
}
}
}
super::ImageData::I32_2D(rows) => {
for row in rows {
for &pixel in row {
buf.extend_from_slice(&pixel.to_le_bytes());
}
}
}
super::ImageData::F64_2D(rows) => {
for row in rows {
for &pixel in row {
buf.extend_from_slice(&pixel.to_le_bytes());
}
}
}
super::ImageData::I16_3D(planes) => {
for plane in planes {
for row in plane {
for &pixel in row {
buf.extend_from_slice(&pixel.to_le_bytes());
}
}
}
}
super::ImageData::I32_3D(planes) => {
for plane in planes {
for row in plane {
for &pixel in row {
buf.extend_from_slice(&pixel.to_le_bytes());
}
}
}
}
super::ImageData::F64_3D(planes) => {
for plane in planes {
for row in plane {
for &pixel in row {
buf.extend_from_slice(&pixel.to_le_bytes());
}
}
}
}
}
buf
}
fn read_i32(bytes: &[u8], offset: usize) -> Result<i32, String> {
bytes
.get(offset..offset + 4)
.and_then(|s| s.try_into().ok())
.map(i32::from_le_bytes)
.ok_or_else(|| format!("ImageBytes: truncated at offset {offset}"))
}
fn read_u32(bytes: &[u8], offset: usize) -> Result<u32, String> {
bytes
.get(offset..offset + 4)
.and_then(|s| s.try_into().ok())
.map(u32::from_le_bytes)
.ok_or_else(|| format!("ImageBytes: truncated at offset {offset}"))
}
pub fn decode(bytes: &[u8]) -> Result<super::ImageData, String> {
if bytes.len() < 44 {
return Err("ImageBytes header too short".into());
}
let _metadata_version = read_i32(bytes, 0)?;
let _error_number = read_i32(bytes, 4)?;
let _client_tx = read_u32(bytes, 8)?;
let _server_tx = read_u32(bytes, 12)?;
let data_start = read_i32(bytes, 16)? as usize;
let image_type = read_i32(bytes, 20)?;
let _transmission_type = read_i32(bytes, 24)?;
let rank = read_i32(bytes, 28)?;
let dim1 = read_i32(bytes, 32)? as usize;
let dim2 = read_i32(bytes, 36)? as usize;
let dim3 = read_i32(bytes, 40)? as usize;
let pixel_data = bytes
.get(data_start..)
.ok_or_else(|| format!("ImageBytes: data_start {data_start} beyond buffer"))?;
match (image_type, rank) {
(1, 2) => {
let mut data = vec![vec![0i16; dim2]; dim1];
for (i, row) in data.iter_mut().enumerate() {
for (j, pixel) in row.iter_mut().enumerate() {
let offset = (i * dim2 + j) * 2;
*pixel = i16::from_le_bytes(
pixel_data
.get(offset..offset + 2)
.and_then(|s| s.try_into().ok())
.ok_or_else(|| {
format!("ImageBytes: pixel data truncated at offset {offset}")
})?,
);
}
}
Ok(super::ImageData::I16_2D(data))
}
(2, 2) => {
let mut data = vec![vec![0i32; dim2]; dim1];
for (i, row) in data.iter_mut().enumerate() {
for (j, pixel) in row.iter_mut().enumerate() {
let offset = (i * dim2 + j) * 4;
*pixel = i32::from_le_bytes(
pixel_data
.get(offset..offset + 4)
.and_then(|s| s.try_into().ok())
.ok_or_else(|| {
format!("ImageBytes: pixel data truncated at offset {offset}")
})?,
);
}
}
Ok(super::ImageData::I32_2D(data))
}
(3, 2) => {
let mut data = vec![vec![0.0f64; dim2]; dim1];
for (i, row) in data.iter_mut().enumerate() {
for (j, pixel) in row.iter_mut().enumerate() {
let offset = (i * dim2 + j) * 8;
*pixel = f64::from_le_bytes(
pixel_data
.get(offset..offset + 8)
.and_then(|s| s.try_into().ok())
.ok_or_else(|| {
format!("ImageBytes: pixel data truncated at offset {offset}")
})?,
);
}
}
Ok(super::ImageData::F64_2D(data))
}
(1, 3) => {
let mut data = vec![vec![vec![0i16; dim3]; dim2]; dim1];
for (i, plane) in data.iter_mut().enumerate() {
for (j, row) in plane.iter_mut().enumerate() {
for (k, pixel) in row.iter_mut().enumerate() {
let offset = (i * dim2 * dim3 + j * dim3 + k) * 2;
*pixel = i16::from_le_bytes(
pixel_data
.get(offset..offset + 2)
.and_then(|s| s.try_into().ok())
.ok_or_else(|| {
format!(
"ImageBytes: pixel data truncated at offset {offset}"
)
})?,
);
}
}
}
Ok(super::ImageData::I16_3D(data))
}
(2, 3) => {
let mut data = vec![vec![vec![0i32; dim3]; dim2]; dim1];
for (i, plane) in data.iter_mut().enumerate() {
for (j, row) in plane.iter_mut().enumerate() {
for (k, pixel) in row.iter_mut().enumerate() {
let offset = (i * dim2 * dim3 + j * dim3 + k) * 4;
*pixel = i32::from_le_bytes(
pixel_data
.get(offset..offset + 4)
.and_then(|s| s.try_into().ok())
.ok_or_else(|| {
format!(
"ImageBytes: pixel data truncated at offset {offset}"
)
})?,
);
}
}
}
Ok(super::ImageData::I32_3D(data))
}
(3, 3) => {
let mut data = vec![vec![vec![0.0f64; dim3]; dim2]; dim1];
for (i, plane) in data.iter_mut().enumerate() {
for (j, row) in plane.iter_mut().enumerate() {
for (k, pixel) in row.iter_mut().enumerate() {
let offset = (i * dim2 * dim3 + j * dim3 + k) * 8;
*pixel = f64::from_le_bytes(
pixel_data
.get(offset..offset + 8)
.and_then(|s| s.try_into().ok())
.ok_or_else(|| {
format!(
"ImageBytes: pixel data truncated at offset {offset}"
)
})?,
);
}
}
}
Ok(super::ImageData::F64_3D(data))
}
_ => Err(format!("unsupported image type {image_type} rank {rank}")),
}
}
}