http3_datagram/
datagram.rs1use bytes::Buf;
2use http3::{
3 error::{Code, internal_error::InternalConnectionError},
4 proto::varint::VarInt,
5 quic::StreamId,
6};
7
8#[derive(Debug, Clone)]
11pub struct Datagram<B> {
12 stream_id: StreamId,
14 payload: B,
16}
17
18impl<B> Datagram<B>
19where
20 B: Buf,
21{
22 #[allow(unknown_lints, clippy::manual_is_multiple_of)]
25 pub fn new(stream_id: StreamId, payload: B) -> Self {
26 assert!(
27 stream_id.into_inner() % 4 == 0,
28 "StreamId is not divisible by 4"
29 );
30 Self { stream_id, payload }
32 }
33
34 pub fn decode(mut buf: B) -> Result<Self, InternalConnectionError> {
36 let q_stream_id = VarInt::decode(&mut buf).map_err(|_| {
37 InternalConnectionError::new(Code::H3_DATAGRAM_ERROR, "invalid stream id".to_string())
38 })?;
39
40 let stream_id = StreamId::try_from(u64::from(q_stream_id) * 4).map_err(|_| {
50 InternalConnectionError::new(Code::H3_DATAGRAM_ERROR, "invalid stream id".to_string())
51 })?;
52
53 let payload = buf;
54
55 Ok(Self { stream_id, payload })
56 }
57
58 #[inline]
59 pub fn stream_id(&self) -> StreamId {
61 self.stream_id
62 }
63
64 #[inline]
65 pub fn payload(&self) -> &B {
67 &self.payload
68 }
69
70 pub fn encode(self) -> EncodedDatagram<B> {
72 let mut buffer = [0; VarInt::MAX_SIZE];
73 let varint = VarInt::from(self.stream_id) / 4;
74 varint.encode(&mut buffer.as_mut_slice());
75 EncodedDatagram {
76 stream_id: buffer,
77 len: varint.size(),
78 pos: 0,
79 payload: self.payload,
80 }
81 }
82
83 pub fn into_payload(self) -> B {
85 self.payload
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use bytes::{Buf, Bytes};
92 use http3::quic::StreamId;
93
94 use super::Datagram;
95
96 #[test]
97 fn encode_preserves_quarter_stream_id() {
98 let stream_id = StreamId::try_from(4).unwrap();
99 let mut encoded = Datagram::new(stream_id, Bytes::from_static(b"payload")).encode();
100
101 let decoded = Datagram::decode(encoded.copy_to_bytes(encoded.remaining())).unwrap();
102
103 assert_eq!(decoded.stream_id(), stream_id);
104 assert_eq!(decoded.into_payload(), Bytes::from_static(b"payload"));
105 }
106}
107
108#[derive(Debug)]
109pub struct EncodedDatagram<B: Buf> {
110 stream_id: [u8; VarInt::MAX_SIZE],
112 len: usize,
114 pos: usize,
116 payload: B,
118}
119
120impl<B> Buf for EncodedDatagram<B>
122where
123 B: Buf,
124{
125 fn remaining(&self) -> usize {
126 self.len - self.pos + self.payload.remaining()
127 }
128
129 fn chunk(&self) -> &[u8] {
130 if self.len - self.pos > 0 {
131 &self.stream_id[self.pos..self.len]
132 } else {
133 self.payload.chunk()
134 }
135 }
136
137 fn advance(&mut self, mut cnt: usize) {
138 let remaining_header = self.len - self.pos;
139 if remaining_header > 0 {
140 let advanced = usize::min(cnt, remaining_header);
141 self.pos += advanced;
142 cnt -= advanced;
143 }
144 self.payload.advance(cnt);
145 }
146}