Skip to main content

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}