use bytes::Bytes;
use crate::{
net::{ascii, parse_uint},
status::TransferPoll,
};
const MAX_ROOM: u64 = (1 << 63) - 1;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("{0}")]
pub struct WireError(pub String);
fn error(message: &str) -> WireError {
WireError(message.to_owned())
}
fn text(frame: &[u8], name: &str) -> Result<String, WireError> {
if !frame.is_ascii() {
return Err(WireError(format!("{name} must be ASCII")));
}
Ok(String::from_utf8_lossy(frame).into_owned())
}
#[allow(clippy::too_many_arguments)]
pub fn registration_frames(
host: &str,
port: u16,
session_id: &str,
arena_base: u64,
layout: &[u8],
tp_size: i64,
tp_rank: i64,
) -> Vec<Bytes> {
vec![
Bytes::from_static(b"None"),
ascii(host),
ascii(port),
ascii(session_id),
Bytes::copy_from_slice(&arena_base.to_ne_bytes()),
Bytes::copy_from_slice(layout),
ascii(tp_size),
ascii(tp_rank),
]
}
pub fn is_registration(frames: &[Bytes]) -> bool {
frames
.first()
.is_some_and(|header| header.as_ref() == b"None")
}
pub fn registration_route(frames: &[Bytes]) -> Result<(String, u16, String), WireError> {
if frames.len() != 8 || !is_registration(frames) {
return Err(error("CachePD slab registration requires exactly 8 frames"));
}
let endpoint = text(&frames[1], "CachePD registration endpoint")?;
let session_id = text(&frames[3], "CachePD registration session")?;
if endpoint.is_empty() || endpoint.len() > 255 {
return Err(error("CachePD registration endpoint is invalid"));
}
if session_id.is_empty() || session_id.len() > 512 {
return Err(error("CachePD registration session is invalid"));
}
let port = parse_uint(&frames[2], 65535)
.filter(|&port| port >= 1)
.ok_or_else(|| error("CachePD registration port is invalid"))?;
Ok((endpoint, port as u16, session_id))
}
pub fn preallocation_frames(room: u64, session_id: &str, manifest: Option<&[u8]>) -> Vec<Bytes> {
vec![
ascii(room),
ascii(session_id),
manifest.map_or_else(Bytes::new, Bytes::copy_from_slice),
]
}
pub fn preallocation_route(frames: &[Bytes]) -> Result<(u64, String), WireError> {
if frames.len() != 3 {
return Err(error("CachePD pre-allocation requires exactly 3 frames"));
}
let room = parse_uint(&frames[0], MAX_ROOM)
.ok_or_else(|| error("CachePD room must be canonical unsigned ASCII"))?;
let session_id = text(&frames[1], "CachePD session")?;
if session_id.is_empty() || session_id.len() > 512 {
return Err(error("CachePD request requires a Decode session"));
}
Ok((room, session_id))
}
#[derive(Debug, Clone, PartialEq)]
pub struct StatusMessage {
pub room: u64,
pub status: TransferPoll,
pub prefill_rank: i64,
pub bootstrap_token: i64,
pub spec_candidate_ids: Option<Vec<i32>>,
pub cached_tokens: i64,
pub bootstrap_logprob: Option<f64>,
}
impl StatusMessage {
pub fn bare(room: u64, status: TransferPoll, prefill_rank: i64) -> Self {
Self {
room,
status,
prefill_rank,
bootstrap_token: -1,
spec_candidate_ids: None,
cached_tokens: 0,
bootstrap_logprob: None,
}
}
pub fn to_frames(&self) -> Vec<Bytes> {
let candidates = self
.spec_candidate_ids
.as_ref()
.map_or_else(Bytes::new, |ids| {
ids.iter()
.flat_map(|id| id.to_le_bytes())
.collect::<Vec<u8>>()
.into()
});
let logprob = self.bootstrap_logprob.map_or_else(Bytes::new, |value| {
Bytes::copy_from_slice(&value.to_le_bytes())
});
vec![
ascii(self.room),
ascii(self.status as i64),
ascii(self.prefill_rank),
ascii(self.bootstrap_token),
candidates,
ascii(self.cached_tokens),
logprob,
]
}
pub fn from_frames(frames: &[Bytes]) -> Result<Self, WireError> {
fn int(frame: &[u8], name: &str) -> Result<i64, WireError> {
std::str::from_utf8(frame)
.ok()
.and_then(|value| value.parse().ok())
.ok_or_else(|| WireError(format!("{name} is not an integer")))
}
if frames.len() < 3 {
return Err(error("a status message has at least 3 frames"));
}
let room =
u64::try_from(int(&frames[0], "room")?).map_err(|_| error("room is negative"))?;
let status = TransferPoll::from_wire(int(&frames[1], "status")?)
.ok_or_else(|| error("unknown status"))?;
let prefill_rank = int(&frames[2], "prefill rank")?;
let bootstrap_token = match frames.get(3) {
Some(frame) => int(frame, "bootstrap token")?,
None => -1,
};
let spec_candidate_ids = match frames.get(4) {
Some(frame) if !frame.is_empty() => {
if frame.len() % 4 != 0 {
return Err(error("candidate ids are not int32"));
}
Some(
frame
.chunks_exact(4)
.map(|chunk| i32::from_le_bytes(chunk.try_into().expect("4 bytes")))
.collect(),
)
}
_ => None,
};
let cached_tokens = match frames.get(5) {
Some(frame) => int(frame, "cached tokens")?,
None => 0,
};
let bootstrap_logprob = match frames.get(6) {
Some(frame) if !frame.is_empty() => Some(f64::from_le_bytes(
frame
.as_ref()
.try_into()
.map_err(|_| error("the logprob is not a double"))?,
)),
_ => None,
};
Ok(Self {
room,
status,
prefill_rank,
bootstrap_token,
spec_candidate_ids,
cached_tokens,
bootstrap_logprob,
})
}
}
pub fn session_port(session_id: &str) -> Option<u64> {
session_id.rsplit(':').next()?.parse().ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_status_message_round_trips() {
let message = StatusMessage {
room: 77,
status: TransferPoll::Success,
prefill_rank: 3,
bootstrap_token: 1234,
spec_candidate_ids: Some(vec![5, -1, 7]),
cached_tokens: 64,
bootstrap_logprob: Some(-0.25),
};
assert_eq!(
StatusMessage::from_frames(&message.to_frames()),
Ok(message)
);
let bare = StatusMessage::bare(9, TransferPoll::Failed, 0);
assert_eq!(StatusMessage::from_frames(&bare.to_frames()), Ok(bare));
let short = [ascii(9), ascii(5), ascii(1)];
let parsed = StatusMessage::from_frames(&short).unwrap();
assert_eq!(parsed.bootstrap_token, -1);
assert_eq!(parsed.cached_tokens, 0);
}
#[test]
fn routes_parse_from_well_formed_frames_only() {
let frames = registration_frames("10.0.0.1", 4000, "10.0.0.1:5000", 0x1000, b"L", 4, 2);
assert_eq!(
registration_route(&frames),
Ok(("10.0.0.1".to_owned(), 4000, "10.0.0.1:5000".to_owned()))
);
assert_eq!(frames[4].as_ref(), 0x1000u64.to_ne_bytes());
assert!(registration_route(&frames[..7]).is_err());
let frames = preallocation_frames(42, "10.0.0.1:5000", None);
assert_eq!(
preallocation_route(&frames),
Ok((42, "10.0.0.1:5000".to_owned()))
);
assert!(frames[2].is_empty());
let bad = [ascii("042"), ascii("s"), Bytes::new()];
assert!(preallocation_route(&bad).is_err());
assert_eq!(session_port("[::1]:5000"), Some(5000));
}
}