Nothing to see here, move along meow
1pub const MAX_HANDLES_PER_CLIENT: usize = 64;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct VfsHandleMapping {
5 pub mount_id: u8,
6 pub backend_handle: u8,
7}
8
9pub struct VfsHandleTable {
10 entries: [Option<VfsHandleMapping>; MAX_HANDLES_PER_CLIENT],
11}
12
13impl Default for VfsHandleTable {
14 fn default() -> Self {
15 Self::new()
16 }
17}
18
19impl VfsHandleTable {
20 pub const fn new() -> Self {
21 Self {
22 entries: [None; MAX_HANDLES_PER_CLIENT],
23 }
24 }
25
26 pub fn allocate(&mut self, mount_id: u8, backend_handle: u8) -> Option<u8> {
27 let slot = self.entries.iter().position(|e| e.is_none())?;
28 self.entries[slot] = Some(VfsHandleMapping {
29 mount_id,
30 backend_handle,
31 });
32 Some(slot as u8)
33 }
34
35 pub fn get(&self, handle: u8) -> Option<VfsHandleMapping> {
36 self.entries.get(handle as usize).copied().flatten()
37 }
38
39 pub fn release(&mut self, handle: u8) -> bool {
40 self.entries
41 .get_mut(handle as usize)
42 .and_then(|slot| slot.take())
43 .is_some()
44 }
45
46 pub fn resolve(&self, handle: u8) -> Option<(u8, u8)> {
47 self.get(handle).map(|m| (m.mount_id, m.backend_handle))
48 }
49
50 pub fn active_count(&self) -> usize {
51 self.entries.iter().filter(|e| e.is_some()).count()
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn allocate_returns_sequential_handles() {
61 let mut table = VfsHandleTable::new();
62 assert_eq!(table.allocate(0, 10), Some(0));
63 assert_eq!(table.allocate(0, 11), Some(1));
64 assert_eq!(table.allocate(1, 20), Some(2));
65 }
66
67 #[test]
68 fn get_returns_mapping() {
69 let mut table = VfsHandleTable::new();
70 table.allocate(3, 7);
71 let mapping = table.get(0).unwrap();
72 assert_eq!(mapping.mount_id, 3);
73 assert_eq!(mapping.backend_handle, 7);
74 }
75
76 #[test]
77 fn get_returns_none_for_unoccupied() {
78 let table = VfsHandleTable::new();
79 assert!(table.get(0).is_none());
80 }
81
82 #[test]
83 fn get_returns_none_for_out_of_bounds() {
84 let table = VfsHandleTable::new();
85 assert!(table.get(255).is_none());
86 }
87
88 #[test]
89 fn release_frees_slot_for_reuse() {
90 let mut table = VfsHandleTable::new();
91 let h = table.allocate(0, 10).unwrap();
92 assert!(table.release(h));
93 assert!(table.get(h).is_none());
94 let reused = table.allocate(1, 20).unwrap();
95 assert_eq!(reused, 0);
96 }
97
98 #[test]
99 fn release_unoccupied_returns_false() {
100 let mut table = VfsHandleTable::new();
101 assert!(!table.release(0));
102 }
103
104 #[test]
105 fn resolve_returns_mount_and_backend() {
106 let mut table = VfsHandleTable::new();
107 table.allocate(3, 7);
108 assert_eq!(table.resolve(0), Some((3, 7)));
109 }
110
111 #[test]
112 fn resolve_returns_none_for_empty() {
113 let table = VfsHandleTable::new();
114 assert_eq!(table.resolve(5), None);
115 }
116
117 #[test]
118 fn exhaustion_returns_none() {
119 let mut table = VfsHandleTable::new();
120 (0..MAX_HANDLES_PER_CLIENT).for_each(|i| {
121 assert!(table.allocate(0, i as u8).is_some());
122 });
123 assert!(table.allocate(0, 99).is_none());
124 }
125
126 #[test]
127 fn active_count_tracks_occupancy() {
128 let mut table = VfsHandleTable::new();
129 assert_eq!(table.active_count(), 0);
130 table.allocate(0, 1);
131 table.allocate(0, 2);
132 assert_eq!(table.active_count(), 2);
133 table.release(0);
134 assert_eq!(table.active_count(), 1);
135 }
136
137 #[test]
138 fn handles_across_mounts() {
139 let mut table = VfsHandleTable::new();
140 let h0 = table.allocate(0, 5).unwrap();
141 let h1 = table.allocate(1, 10).unwrap();
142 let h2 = table.allocate(0, 15).unwrap();
143
144 let (m0, b0) = table.resolve(h0).unwrap();
145 let (m1, b1) = table.resolve(h1).unwrap();
146 let (m2, b2) = table.resolve(h2).unwrap();
147
148 assert_eq!((m0, b0), (0, 5));
149 assert_eq!((m1, b1), (1, 10));
150 assert_eq!((m2, b2), (0, 15));
151 }
152}