use crate::{
ll::{
blocks::{BlockHeaderKind, BlockInfo, BlockOps},
objects::{ObjectHeader, ObjectIterator, ObjectState},
},
medium::StorageMedium,
StorageError,
};
pub struct Gc<'a, M: StorageMedium> {
medium: &'a mut M,
block_info: &'a mut [BlockInfo<M>],
}
impl<'a, M: StorageMedium> Gc<'a, M> {
pub fn new(medium: &'a mut M, block_info: &'a mut [BlockInfo<M>]) -> Self {
Self { medium, block_info }
}
async fn delete_invalid_objects_in_block(
medium: &mut M,
block: usize,
info: &mut BlockInfo<M>,
) -> Result<(), StorageError> {
if info.header.kind().is_unknown() {
return Err(StorageError::FsCorrupted);
}
if info.header.kind().is_empty() {
return Ok(());
}
let mut iter = ObjectIterator::new::<M>(block);
while let Some(mut object) = iter.next(medium).await? {
if object.state() == ObjectState::Allocated {
let mut delete = true;
let payload_size_based_on_block_data =
info.used_bytes() - object.location.offset - ObjectHeader::byte_count::<M>();
if let Some(payload_size) = object.header.payload_size::<M>() {
delete = payload_size != payload_size_based_on_block_data;
} else {
object
.header
.set_payload_size(medium, payload_size_based_on_block_data)
.await?;
}
if !delete {
return Ok(());
}
object
.header
.update_state(medium, ObjectState::Deleted)
.await?;
info.allow_alloc = true;
}
}
Ok(())
}
async fn delete_invalid_objects(&mut self) -> Result<(), StorageError> {
log::trace!("GC::delete_invalid_objects()");
for (block, info) in self.block_info.iter_mut().enumerate() {
Self::delete_invalid_objects_in_block(self.medium, block, info).await?;
}
Ok(())
}
async fn erase_invalid_finished_block(
medium: &mut M,
block: usize,
info: &mut BlockInfo<M>,
) -> Result<(), StorageError> {
if info.allow_alloc {
return Ok(());
}
let mut iter = ObjectIterator::new::<M>(block);
let mut erase = false;
while let Some(object) = iter.next(medium).await? {
if object.state() != ObjectState::Deleted {
erase = object.state() == ObjectState::Allocated;
break;
}
}
if !erase {
return Ok(());
}
if iter.next(medium).await?.is_some() {
return Err(StorageError::FsCorrupted);
}
let BlockHeaderKind::Known(_) = info.header.kind() else {
return Err(StorageError::FsCorrupted);
};
BlockOps::new(medium).format_block(block).await?;
info.update_stats_after_erase();
Ok(())
}
async fn erase_invalid_finished_blocks(&mut self) -> Result<(), StorageError> {
log::trace!("GC::erase_invalid_finished_blocks()");
for (block, info) in self.block_info.iter_mut().enumerate() {
Self::erase_invalid_finished_block(self.medium, block, info).await?;
}
Ok(())
}
async fn erase_full_finished_block(
medium: &mut M,
block: usize,
info: &mut BlockInfo<M>,
) -> Result<(), StorageError> {
if info.used_bytes() >= M::BLOCK_SIZE - ObjectHeader::byte_count::<M>() {
return Ok(());
}
let mut iter = ObjectIterator::new::<M>(block);
while let Some(object) = iter.next(medium).await? {
if object.state() != ObjectState::Deleted {
return Ok(());
}
}
let BlockHeaderKind::Known(_) = info.header.kind() else {
return Err(StorageError::FsCorrupted);
};
BlockOps::new(medium).format_block(block).await?;
info.update_stats_after_erase();
Ok(())
}
async fn erase_full_finished_blocks(&mut self) -> Result<(), StorageError> {
log::trace!("GC::erase_full_finished_blocks()");
for (block, info) in self.block_info.iter_mut().enumerate() {
Self::erase_full_finished_block(self.medium, block, info).await?;
}
Ok(())
}
pub async fn run(&mut self) -> Result<(), StorageError> {
self.delete_invalid_objects().await?;
self.erase_invalid_finished_blocks().await?;
self.erase_full_finished_blocks().await?;
Ok(())
}
}