1#![no_std]
2
3extern crate alloc;
4
5mod file;
6mod unique_file;
7
8use core::fmt::Debug;
9
10pub use file::*;
11
12use alloc::{collections::btree_map::BTreeMap, vec, vec::Vec};
13use core::time::Duration;
14use file_system::{Path, PathOwned};
15use smol_str::SmolStr;
16use synchronization::{blocking_mutex::raw::CriticalSectionRawMutex, rwlock::RwLock};
17use task::TaskIdentifier;
18use task::block_on;
19use unique_file::UniqueFileIdentifier;
20use virtual_file_system::{SynchronousDirectory, SynchronousFile};
21
22pub static CONTEXT: Context = Context::new();
23
24pub fn get_instance() -> &'static Context {
25 &CONTEXT
26}
27
28struct DirectoryEntry {
29 path: SmolStr,
30 parent: Option<FileIdentifier>,
31 directory: SynchronousDirectory,
32}
33
34type FileEntry = SynchronousFile;
35
36struct Inner {
37 task: Option<TaskIdentifier>,
38 sleep_requests: BTreeMap<TaskIdentifier, Duration>,
39 directories: BTreeMap<UniqueFileIdentifier, DirectoryEntry>,
40 files: BTreeMap<UniqueFileIdentifier, FileEntry>,
41}
42
43pub struct Context(RwLock<CriticalSectionRawMutex, Inner>);
44
45impl Default for Context {
46 fn default() -> Self {
47 Self::new()
48 }
49}
50
51impl Context {
52 pub const fn new() -> Self {
53 Self(RwLock::new(Inner {
54 task: None,
55 sleep_requests: BTreeMap::new(),
56 directories: BTreeMap::new(),
57 files: BTreeMap::new(),
58 }))
59 }
60
61 pub fn get_current_task_identifier(&self) -> TaskIdentifier {
62 block_on(self.0.read()).task.expect("No current task set")
63 }
64
65 pub fn try_get_current_task_identifier(&self) -> Option<TaskIdentifier> {
66 self.0.try_read().ok().and_then(|inner| inner.task)
67 }
68
69 pub fn request_sleep(&self, task: TaskIdentifier, duration: Duration) {
70 let mut inner = self
71 .0
72 .try_write()
73 .expect("Failed to lock ABI context for sleep request");
74 inner.sleep_requests.insert(task, duration);
75 }
76
77 pub fn take_sleep_request(&self, task: TaskIdentifier) -> Option<Duration> {
78 let mut inner = self
79 .0
80 .try_write()
81 .expect("Failed to lock ABI context for sleep request");
82 inner.sleep_requests.remove(&task)
83 }
84
85 fn get_new_identifier<V>(
86 map: &BTreeMap<UniqueFileIdentifier, V>,
87 task: TaskIdentifier,
88 start: FileIdentifier,
89 end: FileIdentifier,
90 ) -> Option<UniqueFileIdentifier> {
91 let start_raw = start.into_inner();
92 let end_raw = end.into_inner();
93
94 let mut current = start_raw;
96
97 for key in map.keys() {
98 let (key_task, key_file) = key.split();
99 if key_task != task {
100 continue;
101 }
102
103 let key_raw = key_file.into_inner();
104
105 if key_raw < start_raw || key_raw > end_raw {
107 continue;
108 }
109
110 if current < key_raw {
112 return FileIdentifier::new(current).map(|id| UniqueFileIdentifier::new(task, id));
113 }
114
115 current = key_raw.checked_add(1)?;
117 if current > end_raw {
118 break;
119 }
120 }
121
122 if current <= end_raw {
124 return FileIdentifier::new(current).map(|id| UniqueFileIdentifier::new(task, id));
125 }
126
127 None
128 }
129
130 fn get_new_identifier_file(
131 map: &BTreeMap<UniqueFileIdentifier, FileEntry>,
132 task: TaskIdentifier,
133 ) -> Option<UniqueFileIdentifier> {
134 Self::get_new_identifier(
135 map,
136 task,
137 FileIdentifier::MINIMUM_FILE,
138 FileIdentifier::MAXIMUM_FILE,
139 )
140 }
141
142 fn get_new_identifier_directory(
143 map: &BTreeMap<UniqueFileIdentifier, DirectoryEntry>,
144 task: TaskIdentifier,
145 ) -> Option<UniqueFileIdentifier> {
146 Self::get_new_identifier(
147 map,
148 task,
149 FileIdentifier::MINIMUM_DIRECTORY,
150 FileIdentifier::MAXIMUM_DIRECTORY,
151 )
152 }
153
154 pub fn insert_file(
155 &self,
156 task: TaskIdentifier,
157 file: SynchronousFile,
158 custom_file_identifier: Option<FileIdentifier>,
159 ) -> Option<FileIdentifier> {
160 let mut inner = block_on(self.0.write());
161
162 let file_identifier = if let Some(custom_file_identifier) = custom_file_identifier {
163 let file_identifier = UniqueFileIdentifier::new(task, custom_file_identifier);
164 if inner.files.contains_key(&file_identifier) {
165 panic!("File identifier {:?} is already in use", file_identifier);
166 }
167 file_identifier
168 } else {
169 Self::get_new_identifier_file(&inner.files, task).unwrap()
170 };
171
172 inner.files.insert(file_identifier, file);
173
174 Some(file_identifier.get_file())
175 }
176
177 pub fn perform_operation_on_file_or_directory<FF, FD, O>(
178 &self,
179 file_identifier: FileIdentifier,
180 operation_file: FF,
181 operation_directory: FD,
182 ) -> Option<O>
183 where
184 FF: FnOnce(&mut SynchronousFile) -> O,
185 FD: FnOnce(&mut SynchronousDirectory) -> O,
186 {
187 let task = self.get_current_task_identifier();
188 let unique_file = UniqueFileIdentifier::new(task, file_identifier);
189
190 let mut inner = block_on(self.0.write());
191
192 if file_identifier.is_directory() {
193 inner
194 .directories
195 .get_mut(&unique_file)
196 .map(|entry| operation_directory(&mut entry.directory))
197 } else {
198 inner.files.get_mut(&unique_file).map(operation_file)
199 }
200 }
201
202 pub fn perform_operation_on_file<F, O>(
203 &self,
204 file_identifier: FileIdentifier,
205 operation: F,
206 ) -> Option<O>
207 where
208 F: FnOnce(&mut SynchronousFile) -> O,
209 {
210 let task = self.get_current_task_identifier();
211 let file = UniqueFileIdentifier::new(task, file_identifier);
212
213 let mut inner = block_on(self.0.write());
214 let file = inner.files.get_mut(&file)?;
215
216 Some(operation(file))
217 }
218
219 pub fn perform_operation_on_directory<F, O>(
220 &self,
221 file: FileIdentifier,
222 operation: F,
223 ) -> Option<O>
224 where
225 F: FnOnce(&mut SynchronousDirectory) -> O,
226 {
227 let task = self.get_current_task_identifier();
228 let file = UniqueFileIdentifier::new(task, file);
229
230 let mut inner = block_on(self.0.write());
231 inner
232 .directories
233 .get_mut(&file)
234 .map(|entry| operation(&mut entry.directory))
235 }
236
237 pub fn insert_directory(
238 &self,
239 task: TaskIdentifier,
240 parent: Option<FileIdentifier>,
241 path: impl AsRef<Path>,
242 directory: SynchronousDirectory,
243 ) -> Option<FileIdentifier> {
244 let mut inner = block_on(self.0.write());
245
246 let file_identifier = Self::get_new_identifier_directory(&inner.directories, task).unwrap();
247
248 inner.directories.insert(
249 file_identifier,
250 DirectoryEntry {
251 path: SmolStr::new(path.as_ref()),
252 parent,
253 directory,
254 },
255 );
256
257 Some(file_identifier.get_file())
258 }
259
260 pub fn remove_directory(&self, file: FileIdentifier) -> Option<SynchronousDirectory> {
261 let task = self.get_current_task_identifier();
262 let file = UniqueFileIdentifier::new(task, file);
263
264 let mut inner = block_on(self.0.write());
265 inner.directories.remove(&file).map(|entry| entry.directory)
266 }
267
268 pub fn remove_file(&self, file: FileIdentifier) -> Option<SynchronousFile> {
269 let task = self.get_current_task_identifier();
270 let file = UniqueFileIdentifier::new(task, file);
271
272 let mut inner = block_on(self.0.write());
273 inner.files.remove(&file)
274 }
275
276 pub fn resolve_path(
277 &self,
278 task: TaskIdentifier,
279 directory: FileIdentifier,
280 path: impl AsRef<Path>,
281 ) -> Option<PathOwned> {
282 let inner = block_on(self.0.read());
283
284 let mut stack: Vec<&SmolStr> = vec![];
285
286 let mut new_size = path.as_ref().get_length();
287
288 let mut current_file_identifier = directory.into_unique(task);
289
290 loop {
291 let DirectoryEntry { path, parent, .. } =
292 inner.directories.get(¤t_file_identifier)?;
293
294 new_size += path.len() + 1; if let Some(parent) = parent {
297 stack.push(path);
298 current_file_identifier = parent.into_unique(task);
299 } else {
300 break;
301 }
302 }
303
304 let mut new_path = PathOwned::new_with_capacity(new_size);
305
306 while let Some(path) = stack.pop() {
307 new_path = new_path.join(Path::from_str(path))?;
308 }
309
310 let new_path = new_path.join(path)?;
311
312 Some(new_path)
313 }
314
315 pub async fn set_task(&self, task: TaskIdentifier) {
316 loop {
317 let mut inner = self.0.write().await;
318
319 if inner.task.is_none() {
320 inner.task.replace(task);
321 break;
322 }
323 }
324 }
325
326 pub async fn clear_task(&self) {
327 let mut inner = self.0.write().await;
328 inner.task.take();
329 }
330
331 pub async fn call_abi<F, Fut, R>(&self, function: F) -> R
332 where
333 F: FnOnce() -> Fut,
334 Fut: Future<Output = R>,
335 {
336 let task = task::get_instance().get_current_task_identifier().await;
337 self.set_task(task).await;
338 let result = function().await;
339 self.clear_task().await;
340 result
341 }
342}
343
344impl Debug for Context {
345 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
346 let inner = block_on(self.0.read());
347 f.debug_struct("Context")
348 .field("task", &inner.task)
349 .field("directories", &inner.directories.keys())
350 .field("files", &inner.files.keys())
351 .finish()
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use core::mem::forget;
358
359 use file_system::{AccessFlags, DummyFileSystem};
360
361 use super::*;
362
363 #[test]
364 fn test_context_new() {
365 let context = Context::new();
366 assert!(block_on(context.0.read()).task.is_none());
367 assert!(block_on(context.0.read()).directories.is_empty());
368 }
369
370 #[test]
371 fn test_get_instance() {
372 let _ = get_instance();
373 }
374
375 fn new_dummy_directory() -> SynchronousDirectory {
376 SynchronousDirectory::new(
377 &DummyFileSystem,
378 AccessFlags::READ_WRITE.into(),
379 file_system::Context::new_empty(),
380 )
381 }
382
383 fn initialize() -> (TaskIdentifier, Context) {
384 let context = Context::new();
385 let task = TaskIdentifier::new(1);
386 (task, context)
387 }
388
389 fn clean_up(context: &Context) {
390 let mut inner = block_on(context.0.write());
391
392 let keys = inner.directories.keys().cloned().collect::<Vec<_>>();
393
394 for key in keys {
395 let directory = inner.directories.remove(&key).unwrap();
396 forget(directory); }
398 }
399
400 #[test]
401 fn test_insert_and_remove_opened_file_identifier_path() {
402 let (task, context) = initialize();
403 let parent_id = FileIdentifier::new_panic(10);
404 let path = Path::from_str("test.txt");
405
406 let file_identifier = context
407 .insert_directory(task, Some(parent_id), path, new_dummy_directory())
408 .unwrap();
409
410 let inner = block_on(context.0.read());
411
412 let unique_file_identifier = UniqueFileIdentifier::new(task, file_identifier);
413
414 assert!(inner.directories.contains_key(&unique_file_identifier));
415 drop(inner);
416
417 block_on(context.set_task(task));
419 forget(context.remove_directory(file_identifier));
420 block_on(context.clear_task());
421
422 let inner = block_on(context.0.read());
423 assert!(!inner.directories.contains_key(&unique_file_identifier));
424 drop(inner);
425
426 clean_up(&context);
427 }
428
429 #[test]
430 fn test_insert_with_none_parent() {
431 let (task, context) = initialize();
432 let path = Path::from_str("test.txt");
433 let directory = new_dummy_directory();
434
435 let file_identifier = context
436 .insert_directory(task, None, path, directory)
437 .unwrap();
438
439 let inner = block_on(context.0.read());
440 let unique_file_identifier = UniqueFileIdentifier::new(task, file_identifier);
441 let DirectoryEntry { parent, .. } = inner.directories.get(&unique_file_identifier).unwrap();
442 assert_eq!(*parent, None);
443 drop(inner);
444
445 clean_up(&context);
446 }
447
448 #[test]
449 fn test_get_full_path_single_level() {
450 let (task, context) = initialize();
451 let path = Path::from_str("base");
452 let directory = new_dummy_directory();
453
454 let base_id = context
455 .insert_directory(task, None, path, directory)
456 .unwrap();
457
458 let result = context.resolve_path(task, base_id, Path::from_str("file.txt"));
459 assert_eq!(result.unwrap().as_str(), "/file.txt");
461
462 clean_up(&context);
463 }
464
465 #[test]
466 fn test_get_full_path_nested() {
467 let (task, context) = initialize();
468
469 let root_id = context
470 .insert_directory(task, None, "root", new_dummy_directory())
471 .unwrap();
472 let dir_id = context
473 .insert_directory(task, Some(root_id), "dir", new_dummy_directory())
474 .unwrap();
475 let sub_dir_id = context
476 .insert_directory(task, Some(dir_id), "subdir", new_dummy_directory())
477 .unwrap();
478
479 let path = context
480 .resolve_path(task, sub_dir_id, Path::from_str("file.txt"))
481 .unwrap();
482 assert_eq!(path.as_str(), "/dir/subdir/file.txt");
485
486 clean_up(&context);
487 }
488
489 #[test]
490 fn test_get_full_path_nonexistent() {
491 let (task, context) = initialize();
492 let file_id = FileIdentifier::new_panic(999);
493
494 let result = context.resolve_path(task, file_id, Path::from_str("file.txt"));
495 assert_eq!(result, None);
496
497 clean_up(&context);
498 }
499
500 #[test]
501 fn test_remove_nonexistent_file() {
502 let (task, context) = initialize();
503 let file_id = FileIdentifier::new_panic(999);
504
505 block_on(context.set_task(task));
506 let result = context.remove_directory(file_id);
507 block_on(context.clear_task());
508
509 assert!(result.is_none());
510 clean_up(&context);
511 }
512
513 #[test]
514 fn test_multiple_tasks() {
515 let (task1, context) = initialize();
516 let task2 = TaskIdentifier::new(2);
517
518 let file_id1 = context
519 .insert_directory(
520 task1,
521 None,
522 Path::from_str("task1.txt"),
523 new_dummy_directory(),
524 )
525 .unwrap();
526 let file_id2 = context
527 .insert_directory(
528 task2,
529 None,
530 Path::from_str("task2.txt"),
531 new_dummy_directory(),
532 )
533 .unwrap();
534
535 let inner = block_on(context.0.read());
536 let unique_id1 = UniqueFileIdentifier::new(task1, file_id1);
537 let unique_id2 = UniqueFileIdentifier::new(task2, file_id2);
538
539 assert!(inner.directories.contains_key(&unique_id1));
540 assert!(inner.directories.contains_key(&unique_id2));
541 drop(inner);
542
543 clean_up(&context);
544 }
545}