use crate::{Error, Result};
use alloc::vec;
use alloc::vec::Vec;
#[derive(Debug, Clone)]
pub struct Alloc {
bits: Vec<u64>,
count: u32,
next: u32,
}
impl Alloc {
pub fn new(count: u32) -> Self {
Self {
bits: vec![0; (count as usize).div_ceil(64)],
count,
next: 0,
}
}
pub fn mark(&mut self, block: u32) {
if block < self.count {
self.bits[block as usize / 64] |= 1u64 << (block % 64);
}
}
pub fn free(&mut self, block: u32) {
if block < self.count {
self.bits[block as usize / 64] &= !(1u64 << (block % 64));
}
}
pub fn is_used(&self, block: u32) -> bool {
block < self.count && self.bits[block as usize / 64] & (1u64 << (block % 64)) != 0
}
pub fn used(&self) -> u32 {
self.bits
.iter()
.map(|w| w.count_ones())
.sum::<u32>()
.min(self.count)
}
pub fn take(&mut self) -> Result<u32> {
for i in 0..self.count {
let b = (self.next + i) % self.count;
if !self.is_used(b) {
self.mark(b);
self.next = (b + 1) % self.count;
return Ok(b);
}
}
Err(Error::InvalidArgument(
"littlefs: no free blocks left on the volume".into(),
))
}
pub fn take_pair(&mut self) -> Result<[u32; 2]> {
let a = self.take()?;
let b = self.take().inspect_err(|_| self.free(a))?;
Ok([a, b])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn allocations_are_exact_and_reusable() {
let mut a = Alloc::new(8);
let p = a.take_pair().unwrap();
assert_ne!(p[0], p[1]);
assert_eq!(a.used(), 2);
a.free(p[0]);
assert_eq!(a.used(), 1);
assert!(!a.is_used(p[0]));
}
#[test]
fn a_full_volume_reports_out_of_space() {
let mut a = Alloc::new(2);
a.take().unwrap();
a.take().unwrap();
assert!(a.take().is_err());
let mut b = Alloc::new(3);
b.take().unwrap();
b.take().unwrap();
assert!(b.take_pair().is_err());
assert_eq!(b.used(), 2);
}
}