pub const MAX_SESSIONS: usize = 8; pub const SESSION_LABEL_BYTES: usize = 38; pub struct SessionList { labels: [[u8; SESSION_LABEL_BYTES]; MAX_SESSIONS], lengths: [usize; MAX_SESSIONS], count: usize, } impl SessionList { pub const fn new() -> Self { Self { labels: [[0; SESSION_LABEL_BYTES]; MAX_SESSIONS], lengths: [0; MAX_SESSIONS], count: 0, } } pub const fn count(&self) -> usize { self.count } pub const fn is_empty(&self) -> bool { self.count == 0 } pub fn add(&mut self, index: usize, label: &[u8]) -> bool { if index != self.count || self.count == MAX_SESSIONS { return false; } let length = label.len().min(SESSION_LABEL_BYTES); self.labels[index][..length].copy_from_slice(&label[..length]); self.lengths[index] = length; self.count += 1; true } pub fn label(&self, index: usize) -> Option<&[u8]> { if index >= self.count { return None; } Some(&self.labels[index][..self.lengths[index]]) } } #[cfg(test)] mod tests { use super::{SessionList, SESSION_LABEL_BYTES}; #[test] fn stores_ordered_session_labels() { let mut sessions = SessionList::new(); assert!(sessions.add(0, b"ONE")); assert!(sessions.add(1, b"TWO")); assert_eq!(sessions.count(), 2); assert_eq!(sessions.label(0), Some(b"ONE".as_slice())); assert_eq!(sessions.label(1), Some(b"TWO".as_slice())); } #[test] fn rejects_gaps_and_truncates_long_labels() { let mut sessions = SessionList::new(); assert!(!sessions.add(1, b"SKIP")); assert!(sessions.add(0, &[b'X'; SESSION_LABEL_BYTES + 1])); assert_eq!(sessions.label(0).unwrap().len(), SESSION_LABEL_BYTES); } }