Skip to main content

moqtap_client/draft09/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 exceeds the current MAX_SUBSCRIBE_ID.
7    #[error("subscribe ID {0} exceeds max {1}")]
8    ExceedsMax(u64, u64),
9    /// MAX_SUBSCRIBE_ID must only increase; it decreased.
10    #[error("max subscribe ID can only increase: was {0}, got {1}")]
11    Decreased(u64, u64),
12    /// No subscribe IDs are available (max is 0 or exhausted).
13    #[error("no subscribe IDs available (blocked)")]
14    Blocked,
15}
16
17/// Allocates and validates subscribe IDs per the MoQT draft-09 spec.
18///
19/// Draft-07 has no client/server parity rule for subscribe IDs (unlike
20/// the draft-14 request ID allocator). The subscriber allocates IDs
21/// monotonically starting at 0; FETCH and SUBSCRIBE share the same
22/// namespace since each carries a `subscribe_id` field.
23pub struct SubscribeIdAllocator {
24    next_id: u64,
25    max_id: u64,
26}
27
28impl Default for SubscribeIdAllocator {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl SubscribeIdAllocator {
35    /// Create a new allocator starting at subscribe_id 0 with max_id = 0
36    /// (blocked until the peer sends MAX_SUBSCRIBE_ID).
37    pub fn new() -> Self {
38        Self { next_id: 0, max_id: 0 }
39    }
40
41    /// Allocate the next subscribe ID.
42    pub fn allocate(&mut self) -> Result<VarInt, SubscribeIdError> {
43        if self.max_id == 0 || self.next_id >= self.max_id {
44            return Err(SubscribeIdError::Blocked);
45        }
46        let id = VarInt::from_u64(self.next_id).unwrap();
47        self.next_id += 1;
48        Ok(id)
49    }
50
51    /// Update the maximum allowed subscribe ID (can only increase).
52    pub fn update_max(&mut self, new_max: u64) -> Result<(), SubscribeIdError> {
53        if new_max <= self.max_id {
54            return Err(SubscribeIdError::Decreased(self.max_id, new_max));
55        }
56        self.max_id = new_max;
57        Ok(())
58    }
59
60    /// Validate a subscribe ID received from the peer.
61    pub fn validate_peer_id(&self, id: u64) -> Result<(), SubscribeIdError> {
62        if id >= self.max_id {
63            return Err(SubscribeIdError::ExceedsMax(id, self.max_id));
64        }
65        Ok(())
66    }
67
68    /// Check if we are blocked (max_id is 0 or next_id >= max_id).
69    pub fn is_blocked(&self) -> bool {
70        self.max_id == 0 || self.next_id >= self.max_id
71    }
72
73    /// Get the current maximum subscribe ID.
74    pub fn max_id(&self) -> u64 {
75        self.max_id
76    }
77}