use bytes::Bytes;
use crate::{
codec::Wire,
error::{Error, Result},
msgpack::{Decoder, Encoder, Kind},
};
pub const RAW_VIEW_EXT: i8 = 3;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Tensor {
pub dtype: String,
pub shape: Vec<i64>,
pub data: Bytes,
}
impl Wire for Tensor {
fn encode(&self, encoder: &mut Encoder) {
encoder.array_len(3);
encoder.str(&self.dtype);
self.shape.encode(encoder);
encoder.buffer(RAW_VIEW_EXT, &self.data);
}
fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
let len = decoder.array_len()?;
if len != 3 {
return Err(Error::Invalid(format!(
"a tensor is [dtype, shape, data], found {len} elements"
)));
}
let dtype = String::decode(decoder)?;
let shape = Vec::decode(decoder)?;
let data = match decoder.peek_kind()? {
Kind::Ext => {
let (code, data) = decoder.ext()?;
if code != RAW_VIEW_EXT {
return Err(Error::Invalid(format!(
"tensor data has ext code {code}, expected {RAW_VIEW_EXT}"
)));
}
data
}
Kind::Int => {
let index = decoder.integer()?;
decoder.frame(index)?
}
_ => {
return Err(Error::Invalid(
"tensor data must be an inline ext buffer or a frame index".to_owned(),
))
}
};
Ok(Self { dtype, shape, data })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codec::{decode_frames, encode_frames};
fn tensor(len: usize) -> Tensor {
Tensor {
dtype: "uint8".to_owned(),
shape: vec![len as i64],
data: Bytes::from(vec![7u8; len]),
}
}
#[test]
fn small_tensors_inline_and_large_ones_get_a_frame() {
let small = encode_frames(&tensor(255));
assert_eq!(small.len(), 1);
assert_eq!(decode_frames::<Tensor>(&small).unwrap(), tensor(255));
let large = encode_frames(&tensor(256));
assert_eq!(large.len(), 2);
assert_eq!(large[1].len(), 256);
assert_eq!(decode_frames::<Tensor>(&large).unwrap(), tensor(256));
}
#[test]
fn a_frame_index_past_the_message_fails() {
let mut frames = encode_frames(&tensor(300));
frames.truncate(1);
assert!(decode_frames::<Tensor>(&frames).is_err());
}
}