1use std::collections::HashMap;
20use std::fmt;
21use std::net::SocketAddr;
22use std::time::Instant;
23
24use crate::VarInt;
25
26#[derive(Debug)]
31pub struct ContextManager {
32 local_contexts: HashMap<VarInt, ContextInfo>,
34 remote_contexts: HashMap<VarInt, ContextInfo>,
36 uncompressed_context: Option<VarInt>,
38 next_local_id: u64,
40 is_client: bool,
42}
43
44#[derive(Debug, Clone)]
46pub struct ContextInfo {
47 pub target: Option<SocketAddr>,
49 pub state: ContextState,
51 pub created_at: Instant,
53 pub last_activity: Instant,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum ContextState {
60 Pending,
62 Active,
64 Closing,
66 Closed,
68}
69
70impl fmt::Display for ContextState {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 ContextState::Pending => write!(f, "pending"),
74 ContextState::Active => write!(f, "active"),
75 ContextState::Closing => write!(f, "closing"),
76 ContextState::Closed => write!(f, "closed"),
77 }
78 }
79}
80
81impl ContextManager {
82 pub fn new(is_client: bool) -> Self {
100 Self {
101 local_contexts: HashMap::new(),
102 remote_contexts: HashMap::new(),
103 uncompressed_context: None,
104 next_local_id: if is_client { 2 } else { 1 },
106 is_client,
107 }
108 }
109
110 pub fn is_client(&self) -> bool {
112 self.is_client
113 }
114
115 pub fn allocate_local(&mut self) -> Result<VarInt, ContextError> {
124 let id = self.next_local_id;
125
126 if id > VarInt::MAX.into_inner() {
128 return Err(ContextError::IdSpaceExhausted);
129 }
130
131 self.next_local_id = self
133 .next_local_id
134 .checked_add(2)
135 .ok_or(ContextError::IdSpaceExhausted)?;
136
137 VarInt::from_u64(id).map_err(|_| ContextError::IdSpaceExhausted)
138 }
139
140 pub fn register_uncompressed(&mut self, context_id: VarInt) -> Result<(), ContextError> {
151 if self.uncompressed_context.is_some() {
152 return Err(ContextError::DuplicateUncompressed);
153 }
154
155 if context_id.into_inner() == 0 {
156 return Err(ContextError::ReservedId);
157 }
158
159 let info = ContextInfo {
160 target: None,
161 state: ContextState::Pending,
162 created_at: Instant::now(),
163 last_activity: Instant::now(),
164 };
165
166 self.local_contexts.insert(context_id, info);
167 self.uncompressed_context = Some(context_id);
168
169 Ok(())
170 }
171
172 pub fn register_compressed(
181 &mut self,
182 context_id: VarInt,
183 target: SocketAddr,
184 ) -> Result<(), ContextError> {
185 for info in self
187 .local_contexts
188 .values()
189 .chain(self.remote_contexts.values())
190 {
191 if info.target == Some(target) && info.state != ContextState::Closed {
192 return Err(ContextError::DuplicateTarget(target));
193 }
194 }
195
196 let info = ContextInfo {
197 target: Some(target),
198 state: ContextState::Pending,
199 created_at: Instant::now(),
200 last_activity: Instant::now(),
201 };
202
203 self.local_contexts.insert(context_id, info);
204
205 Ok(())
206 }
207
208 pub fn register_remote(
218 &mut self,
219 context_id: VarInt,
220 target: Option<SocketAddr>,
221 ) -> Result<(), ContextError> {
222 if target.is_none() && self.uncompressed_context.is_some() {
224 return Err(ContextError::DuplicateUncompressed);
225 }
226
227 if let Some(t) = target {
229 for info in self
230 .local_contexts
231 .values()
232 .chain(self.remote_contexts.values())
233 {
234 if info.target == Some(t) && info.state != ContextState::Closed {
235 return Err(ContextError::DuplicateTarget(t));
236 }
237 }
238 }
239
240 let info = ContextInfo {
241 target,
242 state: ContextState::Active, created_at: Instant::now(),
244 last_activity: Instant::now(),
245 };
246
247 self.remote_contexts.insert(context_id, info);
248
249 if target.is_none() {
250 self.uncompressed_context = Some(context_id);
251 }
252
253 Ok(())
254 }
255
256 pub fn handle_ack(&mut self, context_id: VarInt) -> Result<(), ContextError> {
265 let info = self
266 .local_contexts
267 .get_mut(&context_id)
268 .ok_or(ContextError::UnknownContext)?;
269
270 if info.state != ContextState::Pending {
271 return Err(ContextError::InvalidState);
272 }
273
274 info.state = ContextState::Active;
275 info.last_activity = Instant::now();
276
277 Ok(())
278 }
279
280 pub fn close(&mut self, context_id: VarInt) -> Result<(), ContextError> {
289 if let Some(info) = self.local_contexts.get_mut(&context_id) {
290 info.state = ContextState::Closed;
291 info.last_activity = Instant::now();
292 } else if let Some(info) = self.remote_contexts.get_mut(&context_id) {
293 info.state = ContextState::Closed;
294 info.last_activity = Instant::now();
295 } else {
296 return Err(ContextError::UnknownContext);
297 }
298
299 if self.uncompressed_context == Some(context_id) {
300 self.uncompressed_context = None;
301 }
302
303 Ok(())
304 }
305
306 pub fn get_by_target(&self, target: SocketAddr) -> Option<VarInt> {
311 for (id, info) in self
312 .local_contexts
313 .iter()
314 .chain(self.remote_contexts.iter())
315 {
316 if info.target == Some(target) && info.state == ContextState::Active {
317 return Some(*id);
318 }
319 }
320 None
321 }
322
323 pub fn uncompressed(&self) -> Option<VarInt> {
325 self.uncompressed_context.filter(|id| {
326 self.local_contexts
327 .get(id)
328 .or_else(|| self.remote_contexts.get(id))
329 .map(|i| i.state == ContextState::Active)
330 .unwrap_or(false)
331 })
332 }
333
334 pub fn get_context(&self, context_id: VarInt) -> Option<&ContextInfo> {
336 self.local_contexts
337 .get(&context_id)
338 .or_else(|| self.remote_contexts.get(&context_id))
339 }
340
341 pub fn get_target(&self, context_id: VarInt) -> Option<SocketAddr> {
343 self.get_context(context_id).and_then(|info| info.target)
344 }
345
346 pub fn touch(&mut self, context_id: VarInt) -> Result<(), ContextError> {
348 if let Some(info) = self.local_contexts.get_mut(&context_id) {
349 info.last_activity = Instant::now();
350 Ok(())
351 } else if let Some(info) = self.remote_contexts.get_mut(&context_id) {
352 info.last_activity = Instant::now();
353 Ok(())
354 } else {
355 Err(ContextError::UnknownContext)
356 }
357 }
358
359 pub fn active_count(&self) -> usize {
361 self.local_contexts
362 .values()
363 .chain(self.remote_contexts.values())
364 .filter(|info| info.state == ContextState::Active)
365 .count()
366 }
367
368 pub fn cleanup_closed(&mut self, max_age: std::time::Duration) {
370 let now = Instant::now();
371 self.local_contexts.retain(|_, info| {
372 info.state != ContextState::Closed || now.duration_since(info.last_activity) < max_age
373 });
374 self.remote_contexts.retain(|_, info| {
375 info.state != ContextState::Closed || now.duration_since(info.last_activity) < max_age
376 });
377 }
378
379 pub fn local_context_ids(&self) -> impl Iterator<Item = VarInt> + '_ {
381 self.local_contexts.keys().copied()
382 }
383
384 pub fn remote_context_ids(&self) -> impl Iterator<Item = VarInt> + '_ {
386 self.remote_contexts.keys().copied()
387 }
388}
389
390#[derive(Debug, Clone, PartialEq, Eq)]
392pub enum ContextError {
393 IdSpaceExhausted,
395 DuplicateUncompressed,
397 ReservedId,
399 DuplicateTarget(SocketAddr),
401 UnknownContext,
403 InvalidState,
405}
406
407impl fmt::Display for ContextError {
408 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409 match self {
410 ContextError::IdSpaceExhausted => write!(f, "context ID space exhausted"),
411 ContextError::DuplicateUncompressed => {
412 write!(f, "only one uncompressed context allowed")
413 }
414 ContextError::ReservedId => write!(f, "context ID 0 is reserved"),
415 ContextError::DuplicateTarget(addr) => {
416 write!(f, "duplicate target address: {}", addr)
417 }
418 ContextError::UnknownContext => write!(f, "unknown context ID"),
419 ContextError::InvalidState => write!(f, "invalid context state for operation"),
420 }
421 }
422}
423
424impl std::error::Error for ContextError {}
425
426#[cfg(test)]
427mod tests {
428 use super::*;
429 use std::net::{IpAddr, Ipv4Addr};
430
431 #[test]
432 fn test_context_allocation_client() {
433 let mut mgr = ContextManager::new(true);
434 assert!(mgr.is_client());
435
436 let id1 = mgr.allocate_local().unwrap();
437 assert_eq!(id1.into_inner(), 2); let id2 = mgr.allocate_local().unwrap();
440 assert_eq!(id2.into_inner(), 4);
441
442 let id3 = mgr.allocate_local().unwrap();
443 assert_eq!(id3.into_inner(), 6);
444 }
445
446 #[test]
447 fn test_context_allocation_server() {
448 let mut mgr = ContextManager::new(false);
449 assert!(!mgr.is_client());
450
451 let id1 = mgr.allocate_local().unwrap();
452 assert_eq!(id1.into_inner(), 1); let id2 = mgr.allocate_local().unwrap();
455 assert_eq!(id2.into_inner(), 3);
456 }
457
458 #[test]
459 fn test_uncompressed_context_limit() {
460 let mut mgr = ContextManager::new(true);
461 let id = mgr.allocate_local().unwrap();
462 mgr.register_uncompressed(id).unwrap();
463
464 let id2 = mgr.allocate_local().unwrap();
465 let result = mgr.register_uncompressed(id2);
466 assert_eq!(result, Err(ContextError::DuplicateUncompressed));
467 }
468
469 #[test]
470 fn test_reserved_id_zero() {
471 let mut mgr = ContextManager::new(true);
472 let result = mgr.register_uncompressed(VarInt::from_u32(0));
473 assert_eq!(result, Err(ContextError::ReservedId));
474 }
475
476 #[test]
477 fn test_compressed_context_lifecycle() {
478 let mut mgr = ContextManager::new(true);
479 let id = mgr.allocate_local().unwrap();
480 let target = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080);
481
482 mgr.register_compressed(id, target).unwrap();
483 assert_eq!(mgr.get_context(id).unwrap().state, ContextState::Pending);
484
485 mgr.handle_ack(id).unwrap();
486 assert_eq!(mgr.get_context(id).unwrap().state, ContextState::Active);
487
488 assert_eq!(mgr.get_by_target(target), Some(id));
489 assert_eq!(mgr.get_target(id), Some(target));
490
491 mgr.close(id).unwrap();
492 assert_eq!(mgr.get_context(id).unwrap().state, ContextState::Closed);
493 assert_eq!(mgr.get_by_target(target), None);
494 }
495
496 #[test]
497 fn test_duplicate_target() {
498 let mut mgr = ContextManager::new(true);
499 let target = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 9000);
500
501 let id1 = mgr.allocate_local().unwrap();
502 mgr.register_compressed(id1, target).unwrap();
503 mgr.handle_ack(id1).unwrap();
504
505 let id2 = mgr.allocate_local().unwrap();
506 let result = mgr.register_compressed(id2, target);
507 assert_eq!(result, Err(ContextError::DuplicateTarget(target)));
508 }
509
510 #[test]
511 fn test_remote_context_registration() {
512 let mut mgr = ContextManager::new(true);
513 let target = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080);
514
515 mgr.register_remote(VarInt::from_u32(1), Some(target))
517 .unwrap();
518
519 assert_eq!(
521 mgr.get_context(VarInt::from_u32(1)).unwrap().state,
522 ContextState::Active
523 );
524
525 assert_eq!(mgr.get_by_target(target), Some(VarInt::from_u32(1)));
527 }
528
529 #[test]
530 fn test_active_count() {
531 let mut mgr = ContextManager::new(true);
532
533 assert_eq!(mgr.active_count(), 0);
534
535 let id1 = mgr.allocate_local().unwrap();
536 let target1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 1000);
537 mgr.register_compressed(id1, target1).unwrap();
538 mgr.handle_ack(id1).unwrap();
539
540 assert_eq!(mgr.active_count(), 1);
541
542 let id2 = mgr.allocate_local().unwrap();
543 let target2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)), 2000);
544 mgr.register_compressed(id2, target2).unwrap();
545 mgr.handle_ack(id2).unwrap();
546
547 assert_eq!(mgr.active_count(), 2);
548
549 mgr.close(id1).unwrap();
550 assert_eq!(mgr.active_count(), 1);
551 }
552
553 #[test]
554 fn test_unknown_context_errors() {
555 let mut mgr = ContextManager::new(true);
556 let unknown_id = VarInt::from_u32(999);
557
558 assert_eq!(
559 mgr.handle_ack(unknown_id),
560 Err(ContextError::UnknownContext)
561 );
562 assert_eq!(mgr.close(unknown_id), Err(ContextError::UnknownContext));
563 assert_eq!(mgr.touch(unknown_id), Err(ContextError::UnknownContext));
564 }
565
566 #[test]
567 fn test_invalid_state_ack() {
568 let mut mgr = ContextManager::new(true);
569 let id = mgr.allocate_local().unwrap();
570 let target = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080);
571
572 mgr.register_compressed(id, target).unwrap();
573 mgr.handle_ack(id).unwrap();
574
575 assert_eq!(mgr.handle_ack(id), Err(ContextError::InvalidState));
577 }
578
579 #[test]
580 fn test_context_iterators() {
581 let mut mgr = ContextManager::new(true);
582
583 let id1 = mgr.allocate_local().unwrap();
584 let id2 = mgr.allocate_local().unwrap();
585 let target1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 1000);
586 let target2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)), 2000);
587
588 mgr.register_compressed(id1, target1).unwrap();
589 mgr.register_compressed(id2, target2).unwrap();
590
591 let local_ids: Vec<_> = mgr.local_context_ids().collect();
592 assert_eq!(local_ids.len(), 2);
593 assert!(local_ids.contains(&id1));
594 assert!(local_ids.contains(&id2));
595
596 let remote_id = VarInt::from_u32(1);
598 let remote_target = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080);
599 mgr.register_remote(remote_id, Some(remote_target)).unwrap();
600
601 let remote_ids: Vec<_> = mgr.remote_context_ids().collect();
602 assert_eq!(remote_ids.len(), 1);
603 assert!(remote_ids.contains(&remote_id));
604 }
605}