use bytes::Buf;
use h3::{
error::{internal_error::InternalConnectionError, Code},
proto::varint::VarInt,
quic::StreamId,
};
#[derive(Debug, Clone)]
pub struct Datagram<B> {
stream_id: StreamId,
payload: B,
}
impl<B> Datagram<B>
where
B: Buf,
{
pub fn new(stream_id: StreamId, payload: B) -> Self {
assert!(
stream_id.into_inner() % 4 == 0,
"StreamId is not divisible by 4"
);
Self { stream_id, payload }
}
pub fn decode(mut buf: B) -> Result<Self, InternalConnectionError> {
let q_stream_id = VarInt::decode(&mut buf).map_err(|_| {
InternalConnectionError::new(Code::H3_DATAGRAM_ERROR, "invalid stream id".to_string())
})?;
let stream_id = StreamId::try_from(u64::from(q_stream_id) * 4).map_err(|_| {
InternalConnectionError::new(Code::H3_DATAGRAM_ERROR, "invalid stream id".to_string())
})?;
let payload = buf;
Ok(Self { stream_id, payload })
}
#[inline]
pub fn stream_id(&self) -> StreamId {
self.stream_id
}
#[inline]
pub fn payload(&self) -> &B {
&self.payload
}
pub fn encode(self) -> EncodedDatagram<B> {
let mut buffer = [0; VarInt::MAX_SIZE];
let varint = VarInt::from(self.stream_id) / 4;
varint.encode(&mut buffer.as_mut_slice());
EncodedDatagram {
stream_id: [0; VarInt::MAX_SIZE],
len: varint.size(),
pos: 0,
payload: self.payload,
}
}
pub fn into_payload(self) -> B {
self.payload
}
}
#[derive(Debug)]
pub struct EncodedDatagram<B: Buf> {
stream_id: [u8; VarInt::MAX_SIZE],
len: usize,
pos: usize,
payload: B,
}
impl<B> Buf for EncodedDatagram<B>
where
B: Buf,
{
fn remaining(&self) -> usize {
self.len - self.pos + self.payload.remaining()
}
fn chunk(&self) -> &[u8] {
if self.len - self.pos > 0 {
return &self.stream_id[self.pos..self.len];
} else {
self.payload.chunk()
}
}
fn advance(&mut self, mut cnt: usize) {
let remaining_header = self.len - self.pos;
if remaining_header > 0 {
let advanced = usize::min(cnt, remaining_header);
self.pos += advanced;
cnt -= advanced;
}
self.payload.advance(cnt);
}
}