httproxide_h3/proto/
stream.rs1use bytes::{Buf, BufMut};
2use std::{
3 convert::TryFrom,
4 fmt::{self, Display},
5 ops::Add,
6};
7
8use super::{
9 coding::{BufExt, BufMutExt, Decode, Encode, UnexpectedEnd},
10 varint::VarInt,
11};
12
13#[derive(Debug, PartialEq, Eq, Clone)]
14pub struct StreamType(u64);
15
16macro_rules! stream_types {
17 {$($name:ident = $val:expr,)*} => {
18 impl StreamType {
19 $(pub const $name: StreamType = StreamType($val);)*
20 }
21 }
22}
23
24stream_types! {
25 CONTROL = 0x00,
26 PUSH = 0x01,
27 ENCODER = 0x02,
28 DECODER = 0x03,
29}
30
31impl StreamType {
32 pub const MAX_ENCODED_SIZE: usize = VarInt::MAX_SIZE;
33
34 pub fn value(&self) -> u64 {
35 self.0
36 }
37 pub fn grease() -> Self {
40 StreamType(fastrand::u64(0..0x210842108421083) * 0x1f + 0x21)
41 }
42}
43
44impl Decode for StreamType {
45 fn decode<B: Buf>(buf: &mut B) -> Result<Self, UnexpectedEnd> {
46 Ok(StreamType(buf.get_var()?))
47 }
48}
49
50impl Encode for StreamType {
51 fn encode<W: BufMut>(&self, buf: &mut W) {
52 buf.write_var(self.0);
53 }
54}
55
56impl fmt::Display for StreamType {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 match self {
59 &StreamType::CONTROL => write!(f, "Control"),
60 &StreamType::ENCODER => write!(f, "Encoder"),
61 &StreamType::DECODER => write!(f, "Decoder"),
62 x => write!(f, "StreamType({})", x.0),
63 }
64 }
65}
66
67#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
69pub struct StreamId(
70 #[cfg(not(any(test, feature = "test_helpers")))] u64,
71 #[cfg(any(test, feature = "test_helpers"))] pub u64,
72);
73
74impl fmt::Display for StreamId {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 let initiator = match self.initiator() {
77 Side::Client => "client",
78 Side::Server => "server",
79 };
80 let dir = match self.dir() {
81 Dir::Uni => "uni",
82 Dir::Bi => "bi",
83 };
84 write!(
85 f,
86 "{} {}directional stream {}",
87 initiator,
88 dir,
89 self.index()
90 )
91 }
92}
93
94impl StreamId {
95 pub fn first_request() -> Self {
96 Self::new(0, Dir::Bi, Side::Client)
97 }
98
99 pub fn is_request(&self) -> bool {
100 self.dir() == Dir::Bi && self.initiator() == Side::Client
101 }
102
103 pub fn is_push(&self) -> bool {
104 self.dir() == Dir::Uni && self.initiator() == Side::Server
105 }
106
107 pub(crate) fn initiator(self) -> Side {
110 if self.0 & 0x1 == 0 {
111 Side::Client
112 } else {
113 Side::Server
114 }
115 }
116
117 fn new(index: u64, dir: Dir, initiator: Side) -> Self {
118 StreamId((index as u64) << 2 | (dir as u64) << 1 | initiator as u64)
119 }
120
121 fn index(self) -> u64 {
123 self.0 >> 2
124 }
125
126 fn dir(self) -> Dir {
128 if self.0 & 0x2 == 0 {
129 Dir::Bi
130 } else {
131 Dir::Uni
132 }
133 }
134}
135
136impl TryFrom<u64> for StreamId {
137 type Error = InvalidStreamId;
138 fn try_from(v: u64) -> Result<Self, Self::Error> {
139 if v > VarInt::MAX.0 {
140 return Err(InvalidStreamId(v));
141 }
142 Ok(Self(v))
143 }
144}
145
146#[derive(Debug, PartialEq)]
147pub struct InvalidStreamId(u64);
148
149impl Display for InvalidStreamId {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 write!(f, "invalid stream id: {:x}", self.0)
152 }
153}
154
155impl Encode for StreamId {
156 fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
157 VarInt::from_u64(self.0).unwrap().encode(buf);
158 }
159}
160
161impl Add<usize> for StreamId {
162 type Output = StreamId;
163
164 #[allow(clippy::suspicious_arithmetic_impl)]
165 fn add(self, rhs: usize) -> Self::Output {
166 let index = u64::min(
167 u64::saturating_add(self.index(), rhs as u64),
168 VarInt::MAX.0 >> 2,
169 );
170 Self::new(index, self.dir(), self.initiator())
171 }
172}
173
174#[derive(Debug, Copy, Clone, Eq, PartialEq)]
175pub enum Side {
176 Client = 0,
178 Server = 1,
180}
181
182#[derive(Debug, Copy, Clone, Eq, PartialEq)]
184enum Dir {
185 Bi = 0,
187 Uni = 1,
189}