use crate::file::File;
use alloc::sync::Arc;
use alloc::vec::Vec;
use spin::Mutex;
pub type FileDescriptor = usize;
pub const STDIN: FileDescriptor = 0;
pub const STDOUT: FileDescriptor = 1;
pub const STDERR: FileDescriptor = 2;
pub const FILE_DESCRIPTOR_LIMIT: usize = 1024;
pub struct FdEntry {
file: Arc<Mutex<dyn File>>,
flags: u32,
}
impl FdEntry {
pub fn new(file: Arc<Mutex<dyn File>>) -> Self {
Self { file, flags: 0 }
}
pub fn file(&self) -> Arc<Mutex<dyn File>> {
self.file.clone()
}
pub fn flags(&self) -> u32 {
self.flags
}
}
pub struct FileDescriptorTable {
entries: Vec<Option<FdEntry>>,
next_fd: FileDescriptor,
}
impl FileDescriptorTable {
pub fn new() -> Self {
let entries = alloc::vec![None, None, None];
Self {
entries,
next_fd: 3,
}
}
pub fn with_stdio(
stdin: Arc<Mutex<dyn File>>,
stdout: Arc<Mutex<dyn File>>,
stderr: Arc<Mutex<dyn File>>,
) -> Self {
let mut table = Self {
entries: Vec::with_capacity(16),
next_fd: 3,
};
table.entries.push(Some(FdEntry::new(stdin)));
table.entries.push(Some(FdEntry::new(stdout)));
table.entries.push(Some(FdEntry::new(stderr)));
table
}
pub fn alloc(&mut self, file: Arc<Mutex<dyn File>>) -> Option<FileDescriptor> {
for (i, slot) in self.entries.iter_mut().enumerate() {
if slot.is_none() && i >= 3 {
*slot = Some(FdEntry::new(file));
self.next_fd = i + 1;
return Some(i);
}
}
if self.entries.len() >= FILE_DESCRIPTOR_LIMIT {
return None;
}
let fd = self.entries.len();
self.entries.push(Some(FdEntry::new(file)));
self.next_fd = fd + 1;
Some(fd)
}
pub fn dealloc(&mut self, fd: FileDescriptor) -> bool {
if fd >= 3 && fd < self.entries.len() && self.entries[fd].is_some() {
self.entries[fd] = None;
if fd < self.next_fd {
self.next_fd = fd;
}
return true;
}
false
}
pub fn get(&self, fd: FileDescriptor) -> Option<Arc<Mutex<dyn File>>> {
self.entries.get(fd)?.as_ref().map(|entry| entry.file())
}
pub fn is_valid(&self, fd: FileDescriptor) -> bool {
self.get(fd).is_some()
}
pub fn dup2(
&mut self,
old_fd: FileDescriptor,
new_fd: FileDescriptor,
) -> Option<FileDescriptor> {
if old_fd == new_fd {
return if self.is_valid(old_fd) {
Some(new_fd)
} else {
None
};
}
if new_fd >= FILE_DESCRIPTOR_LIMIT {
return None;
}
let file = self.get(old_fd)?;
while self.entries.len() <= new_fd {
self.entries.push(None);
}
self.entries[new_fd] = Some(FdEntry { file, flags: 0 });
Some(new_fd)
}
pub fn count(&self) -> usize {
self.entries.iter().filter(|entry| entry.is_some()).count()
}
pub fn capacity(&self) -> usize {
self.entries.len()
}
}
impl Default for FileDescriptorTable {
fn default() -> Self {
Self::new()
}
}