moqtap_client/draft08/session/subscribe_id.rs
1use moqtap_codec::varint::VarInt;
2
3/// Errors from subscribe ID allocation or validation.
4#[derive(Debug, thiserror::Error, PartialEq, Eq)]
5pub enum SubscribeIdError {
6 /// The subscribe ID reaches or exceeds the Maximum Subscribe ID in force.
7 #[error("subscribe ID {0} exceeds max {1}")]
8 ExceedsMax(u64, u64),
9 /// MAX_SUBSCRIBE_ID must only increase; it did not.
10 #[error("max subscribe ID can only increase: was {0}, got {1}")]
11 Decreased(u64, u64),
12 /// No subscribe IDs are available under the ceiling in force.
13 #[error("no subscribe IDs available (blocked)")]
14 Blocked,
15}
16
17/// Allocates the Subscribe IDs this endpoint sends.
18///
19/// Section 7.4 defines the field: "Subscribe ID is a variable length
20/// integer that MUST be unique and monotonically increasing within a session and
21/// MUST be less than the session's Maximum Subscribe ID." SUBSCRIBE and FETCH
22/// draw from the same sequence, so one allocator serves both.
23///
24/// This draft states no parity rule - the client's and the server's ids are not
25/// separated by their least significant bit, as they are from draft-11 on - so
26/// the sequence simply starts at 0 and steps by one.
27///
28/// The ceiling is exclusive. Section 7.20 gives the Maximum Subscribe ID a
29/// starting value of 0 and reads that as "the peer MUST NOT create
30/// subscriptions", which holds only if a ceiling of 0 forbids the id 0 as well.
31///
32/// The ceiling here is the one the **peer** granted this endpoint. The ceiling
33/// a peer's ids are measured against is the one this endpoint advertised, which
34/// is a different number and lives on the endpoint rather than here.
35pub struct SubscribeIdAllocator {
36 next_id: u64,
37 max_id: u64,
38}
39
40impl Default for SubscribeIdAllocator {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46impl SubscribeIdAllocator {
47 /// Create an allocator starting at subscribe ID 0, blocked until a peer
48 /// raises the maximum.
49 pub fn new() -> Self {
50 Self { next_id: 0, max_id: 0 }
51 }
52
53 /// Allocate the next Subscribe ID.
54 ///
55 /// # Errors
56 ///
57 /// [`SubscribeIdError::Blocked`] if the next id would reach the ceiling.
58 pub fn allocate(&mut self) -> Result<VarInt, SubscribeIdError> {
59 if self.is_blocked() {
60 return Err(SubscribeIdError::Blocked);
61 }
62 let id = VarInt::from_u64(self.next_id).unwrap();
63 self.next_id += 1;
64 Ok(id)
65 }
66
67 /// Raise the maximum Subscribe ID this endpoint may use.
68 ///
69 /// Section 7.20: "The Maximum Subscribe ID MUST only increase within a
70 /// session, and receipt of a MAX_SUBSCRIBE_ID message with an equal or
71 /// smaller Subscribe ID value is a 'Protocol Violation'."
72 ///
73 /// # Errors
74 ///
75 /// [`SubscribeIdError::Decreased`] if the value does not strictly increase.
76 pub fn update_max(&mut self, new_max: u64) -> Result<(), SubscribeIdError> {
77 if new_max <= self.max_id {
78 return Err(SubscribeIdError::Decreased(self.max_id, new_max));
79 }
80 self.max_id = new_max;
81 Ok(())
82 }
83
84 /// Whether the next allocation would reach the ceiling.
85 pub fn is_blocked(&self) -> bool {
86 self.next_id >= self.max_id
87 }
88
89 /// The maximum Subscribe ID the peer has granted this endpoint.
90 pub fn max_id(&self) -> u64 {
91 self.max_id
92 }
93}