pub(crate) trait Fd {
fn as_raw(&self) -> u32;
fn from_raw(raw_fd: u32) -> Self;
}
#[derive(Debug)]
pub(crate) struct FdPool {
next_alloc: Option<u32>,
available: Vec<u32>,
}
impl FdPool {
pub fn new() -> Self {
Self {
next_alloc: Some(0),
available: Vec::new(),
}
}
pub fn allocate<T: Fd>(&mut self) -> Option<T> {
if let Some(fd) = self.available.pop() {
return Some(T::from_raw(fd));
}
let fd = self.next_alloc.take()?;
self.next_alloc = fd.checked_add(1);
Some(T::from_raw(fd))
}
pub fn deallocate<T: Fd>(&mut self, fd: T) {
let fd = fd.as_raw();
if let Some(next_alloc) = self.next_alloc {
assert!(fd < next_alloc);
}
debug_assert!(!self.available.contains(&fd));
self.available.push(fd);
}
}
#[cfg(test)]
mod test {
use super::FdPool;
use std::ops::Deref;
#[derive(Debug)]
struct Fd(u32);
impl super::Fd for Fd {
fn as_raw(&self) -> u32 {
self.0
}
fn from_raw(raw_fd: u32) -> Self {
Self(raw_fd)
}
}
impl Deref for Fd {
type Target = u32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[test]
fn basics() {
let mut fd_pool = FdPool::new();
let mut fd: Fd = fd_pool.allocate().expect("success allocating 0");
assert_eq!(*fd, 0);
fd = fd_pool.allocate().expect("success allocating 1");
assert_eq!(*fd, 1);
fd = fd_pool.allocate().expect("success allocating 2");
assert_eq!(*fd, 2);
fd_pool.deallocate(1u32);
fd_pool.deallocate(0u32);
fd = fd_pool.allocate().expect("success reallocating 0");
assert_eq!(*fd, 0);
fd = fd_pool.allocate().expect("success reallocating 1");
assert_eq!(*fd, 1);
fd = fd_pool.allocate().expect("success allocating 3");
assert_eq!(*fd, 3);
}
#[test]
#[should_panic]
fn deallocate_nonexistent() {
let mut fd_pool = FdPool::new();
fd_pool.deallocate(0u32);
}
#[test]
fn max_allocation() {
let mut fd_pool = FdPool::new();
fd_pool.next_alloc = None;
assert!(fd_pool.allocate::<Fd>().is_none());
}
}