use moqtap_codec::varint::VarInt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Role {
Client,
Server,
}
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum RequestIdError {
#[error("request ID {0} exceeds max {1}")]
ExceedsMax(u64, u64),
#[error("request ID {0} has wrong parity for {1:?}")]
WrongParity(u64, Role),
#[error("max request ID can only increase: was {0}, got {1}")]
Decreased(u64, u64),
#[error("no request IDs available (blocked)")]
Blocked,
}
pub struct RequestIdAllocator {
role: Role,
next_id: u64,
max_id: u64,
}
impl RequestIdAllocator {
pub fn new(role: Role) -> Self {
let next_id = match role {
Role::Client => 0,
Role::Server => 1,
};
Self { role, next_id, max_id: u64::MAX }
}
pub fn allocate(&mut self) -> Result<VarInt, RequestIdError> {
if self.max_id == 0 || self.next_id > self.max_id {
return Err(RequestIdError::Blocked);
}
let id = VarInt::from_u64(self.next_id).unwrap();
self.next_id += 2;
Ok(id)
}
pub fn update_max(&mut self, new_max: u64) -> Result<(), RequestIdError> {
if new_max <= self.max_id {
return Err(RequestIdError::Decreased(self.max_id, new_max));
}
self.max_id = new_max;
Ok(())
}
pub fn validate_peer_id(&self, id: u64) -> Result<(), RequestIdError> {
let expected_even = match self.role {
Role::Client => false, Role::Server => true, };
let is_even = id % 2 == 0;
if is_even != expected_even {
let peer_role = match self.role {
Role::Client => Role::Server,
Role::Server => Role::Client,
};
return Err(RequestIdError::WrongParity(id, peer_role));
}
if id > self.max_id {
return Err(RequestIdError::ExceedsMax(id, self.max_id));
}
Ok(())
}
pub fn is_blocked(&self) -> bool {
self.max_id == 0 || self.next_id > self.max_id
}
pub fn max_id(&self) -> u64 {
self.max_id
}
}