use crate::{Result, invalid};
const LIMIT: usize = 1024 * 1024;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PositionLayout {
Sequential,
MultiRope,
Hunyuan,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Frame {
pub view_separator: bool,
pub newline: bool,
pub width: i32,
pub height: i32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Image {
pub width: u32,
pub height: u32,
pub layout: PositionLayout,
pub image_index: u32,
pub temporal_merge: u32,
pub id: String,
pub frames: Vec<Frame>,
}
impl Image {
pub fn token_count(&self) -> Result<usize> {
if self.temporal_merge == 0 {
return Err(invalid("image temporal merge must be nonzero"));
}
let count = if self.layout == PositionLayout::Hunyuan {
(u64::from(self.width) + 1) * u64::from(self.height) + 2
} else {
let frames = u64::try_from(self.frames.len())?.div_ceil(u64::from(self.temporal_merge));
u64::from(self.width)
.checked_mul(u64::from(self.height))
.and_then(|area| area.checked_mul(frames))
.ok_or_else(|| invalid("image token count overflow"))?
};
if count > 1_048_576 {
return Err(invalid("image token count exceeds bound"));
}
Ok(usize::try_from(count)?)
}
pub fn position(&self, start: u32, index: usize) -> Result<[u64; 4]> {
let count = self.token_count()?;
if index >= count || self.width == 0 {
return Err(invalid("image position outside token grid"));
}
let sequential = u64::from(start) + u64::try_from(index)?;
Ok(match self.layout {
PositionLayout::Sequential => [sequential; 4],
PositionLayout::MultiRope => [
u64::from(start),
u64::from(start) + u64::try_from(index)? % u64::from(self.width),
u64::from(start) + u64::try_from(index)? / u64::from(self.width),
0,
],
PositionLayout::Hunyuan if index == 0 || index == count - 1 => [sequential; 4],
PositionLayout::Hunyuan => {
let offset = u64::try_from(index - 1)?;
[
sequential,
offset / (u64::from(self.width) + 1),
offset % (u64::from(self.width) + 1),
u64::from(self.image_index),
]
}
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Audio {
pub tokens: u32,
pub id: String,
pub frames: Vec<Frame>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Chunk {
Text(Vec<i32>),
Image(Image),
Audio(Audio),
}
impl Chunk {
pub fn token_count(&self) -> Result<usize> {
match self {
Self::Text(tokens) => Ok(tokens.len()),
Self::Image(image) => image.token_count(),
Self::Audio(audio) => Ok(usize::try_from(audio.tokens)?),
}
}
pub fn position_count(&self) -> Result<usize> {
match self {
Self::Image(image) if image.layout == PositionLayout::MultiRope => {
Ok(usize::try_from(image.width.max(image.height))?)
}
_ => self.token_count(),
}
}
#[must_use]
pub fn id(&self) -> Option<&str> {
match self {
Self::Text(_) => None,
Self::Image(image) => Some(&image.id),
Self::Audio(audio) => Some(&audio.id),
}
}
pub fn save(&self) -> Result<Vec<u8>> {
self.token_count()?;
let mut bytes = 1_u64.to_le_bytes().to_vec();
let kind: u32 = match self {
Self::Text(_) => 0,
Self::Image(_) => 1,
Self::Audio(_) => 2,
};
bytes.extend(kind.to_le_bytes());
let text = if let Self::Text(tokens) = self {
tokens.as_slice()
} else {
&[]
};
if text.len() > LIMIT / 4 {
return Err(invalid("text chunk exceeds cache bound"));
}
bytes.extend(u64::try_from(text.len())?.to_le_bytes());
for token in text {
bytes.extend(token.to_le_bytes());
}
bytes.push(u8::from(matches!(self, Self::Image(_))));
if let Self::Image(image) = self {
for value in [
image.width,
image.height,
match image.layout {
PositionLayout::Sequential => 0,
PositionLayout::MultiRope => 1,
PositionLayout::Hunyuan => 2,
},
image.image_index,
image.temporal_merge,
] {
bytes.extend(value.to_le_bytes());
}
string(&mut bytes, &image.id)?;
frames(&mut bytes, &image.frames, false)?;
}
bytes.push(u8::from(matches!(self, Self::Audio(_))));
if let Self::Audio(audio) = self {
bytes.extend(audio.tokens.to_le_bytes());
string(&mut bytes, &audio.id)?;
frames(&mut bytes, &audio.frames, true)?;
}
if bytes.len() > LIMIT {
return Err(invalid("chunk cache record exceeds1MiB"));
}
Ok(bytes)
}
pub fn load(bytes: &[u8]) -> Result<Self> {
if bytes.len() > LIMIT {
return Err(invalid("chunk cache record exceeds1MiB"));
}
let mut input = Reader { bytes, position: 0 };
if input.u64()? != 1 {
return Err(invalid("chunk cache version mismatch"));
}
let kind = input.u32()?;
let count = usize::try_from(input.u64()?)?;
if count > input.remaining() / 4 {
return Err(invalid("text token extent exceeds chunk cache"));
}
let mut text = Vec::with_capacity(count);
for _ in 0..count {
text.push(i32::from_le_bytes(input.take()?));
}
let image = if input.boolean()? {
Some(Image {
width: input.u32()?,
height: input.u32()?,
layout: match input.u32()? {
0 => PositionLayout::Sequential,
1 => PositionLayout::MultiRope,
2 => PositionLayout::Hunyuan,
_ => return Err(invalid("invalid image position layout")),
},
image_index: input.u32()?,
temporal_merge: input.u32()?,
id: input.string()?,
frames: input.frames()?,
})
} else {
None
};
let audio = if input.boolean()? {
Some(Audio {
tokens: input.u32()?,
id: input.string()?,
frames: input.frames()?,
})
} else {
None
};
let chunk = match kind {
0 => Self::Text(text),
1 => Self::Image(image.ok_or_else(|| invalid("image chunk has no image payload"))?),
2 => Self::Audio(audio.ok_or_else(|| invalid("audio chunk has no audio payload"))?),
_ => return Err(invalid("unknown chunk type")),
};
chunk.token_count()?;
Ok(chunk)
}
}
fn string(bytes: &mut Vec<u8>, value: &str) -> Result<()> {
if value.len() > LIMIT {
return Err(invalid("chunk cache ID exceeds bound"));
}
bytes.extend(u64::try_from(value.len())?.to_le_bytes());
bytes.extend(value.as_bytes());
Ok(())
}
fn frames(bytes: &mut Vec<u8>, values: &[Frame], audio: bool) -> Result<()> {
if values.len() > LIMIT / 10 {
return Err(invalid("chunk frame metadata exceeds cache bound"));
}
bytes.push(u8::from(audio));
bytes.extend(u64::try_from(values.len())?.to_le_bytes());
for frame in values {
bytes.extend([u8::from(frame.view_separator), u8::from(frame.newline)]);
bytes.extend(frame.width.to_le_bytes());
bytes.extend(frame.height.to_le_bytes());
}
Ok(())
}
struct Reader<'a> {
bytes: &'a [u8],
position: usize,
}
impl Reader<'_> {
const fn remaining(&self) -> usize {
self.bytes.len() - self.position
}
fn take<const N: usize>(&mut self) -> Result<[u8; N]> {
let end = self
.position
.checked_add(N)
.ok_or_else(|| invalid("chunk cursor overflow"))?;
let value = self
.bytes
.get(self.position..end)
.ok_or_else(|| invalid("truncated chunk cache"))?
.try_into()?;
self.position = end;
Ok(value)
}
fn u32(&mut self) -> Result<u32> {
Ok(u32::from_le_bytes(self.take()?))
}
fn u64(&mut self) -> Result<u64> {
Ok(u64::from_le_bytes(self.take()?))
}
fn boolean(&mut self) -> Result<bool> {
Ok(self.take::<1>()?[0] != 0)
}
fn string(&mut self) -> Result<String> {
let length = usize::try_from(self.u64()?)?;
if length > self.remaining() {
return Err(invalid("chunk ID exceeds record extent"));
}
let text =
std::str::from_utf8(&self.bytes[self.position..self.position + length])?.to_owned();
self.position += length;
Ok(text)
}
fn frames(&mut self) -> Result<Vec<Frame>> {
self.boolean()?;
let count = usize::try_from(self.u64()?)?;
if count > self.remaining() / 10 {
return Err(invalid("chunk frame count exceeds record extent"));
}
let mut frames = Vec::with_capacity(count);
for _ in 0..count {
frames.push(Frame {
view_separator: self.boolean()?,
newline: self.boolean()?,
width: i32::from_le_bytes(self.take()?),
height: i32::from_le_bytes(self.take()?),
});
}
Ok(frames)
}
}
#[must_use]
pub fn test_inputs() -> Vec<Chunk> {
vec![
Chunk::Text(vec![1, 2, 3, 4, 5]),
Chunk::Image(Image {
width: 4,
height: 4,
layout: PositionLayout::Sequential,
image_index: 0,
temporal_merge: 1,
id: "image_1".into(),
frames: vec![Frame::default(); 16],
}),
]
}