use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use crate::storage::dmu::get_block_size;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SparseError {
FileNotFound(u64),
DatasetNotFound(String),
InvalidRange {
offset: u64,
length: u64,
file_size: u64,
},
NotSupported,
IoError(String),
NoSpace,
PermissionDenied,
}
impl fmt::Display for SparseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FileNotFound(id) => write!(f, "File not found: {}", id),
Self::DatasetNotFound(ds) => write!(f, "Dataset not found: {}", ds),
Self::InvalidRange {
offset,
length,
file_size,
} => {
write!(
f,
"Invalid range: offset={}, length={}, file_size={}",
offset, length, file_size
)
}
Self::NotSupported => write!(f, "Operation not supported"),
Self::IoError(msg) => write!(f, "I/O error: {}", msg),
Self::NoSpace => write!(f, "No space left on device"),
Self::PermissionDenied => write!(f, "Permission denied"),
}
}
}
#[derive(Debug, Clone)]
pub struct SparseInfo {
pub object_id: u64,
pub logical_size: u64,
pub physical_size: u64,
pub is_sparse: bool,
pub hole_count: u64,
pub total_hole_size: u64,
pub data_region_count: u64,
pub block_size: u32,
}
impl Default for SparseInfo {
fn default() -> Self {
Self {
object_id: 0,
logical_size: 0,
physical_size: 0,
is_sparse: false,
hole_count: 0,
total_hole_size: 0,
data_region_count: 0,
block_size: get_block_size() as u32,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HoleRegion {
pub offset: u64,
pub length: u64,
}
impl HoleRegion {
pub fn new(offset: u64, length: u64) -> Self {
Self { offset, length }
}
pub fn end(&self) -> u64 {
self.offset + self.length
}
pub fn contains(&self, offset: u64) -> bool {
offset >= self.offset && offset < self.end()
}
pub fn overlaps(&self, offset: u64, length: u64) -> bool {
let range_end = offset + length;
self.offset < range_end && self.end() > offset
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DataRegion {
pub offset: u64,
pub length: u64,
pub physical_block: u64,
}
impl DataRegion {
pub fn new(offset: u64, length: u64, physical_block: u64) -> Self {
Self {
offset,
length,
physical_block,
}
}
pub fn end(&self) -> u64 {
self.offset + self.length
}
}
#[derive(Debug, Clone, Default)]
pub struct SparsifyResult {
pub space_saved: u64,
pub holes_created: u64,
pub time_us: u64,
pub modified: bool,
}
#[derive(Debug, Clone, Default)]
pub struct DensifyResult {
pub space_used: u64,
pub holes_filled: u64,
pub time_us: u64,
}
#[derive(Debug, Clone, Default)]
pub struct SpaceSavings {
pub logical_size: u64,
pub physical_size: u64,
pub space_saved: u64,
pub savings_percent: f32,
}
#[derive(Debug, Clone)]
pub struct SparseExtent {
pub extent_type: ExtentType,
pub logical_offset: u64,
pub physical_offset: u64,
pub length: u64,
pub flags: ExtentFlags,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExtentType {
Data,
Hole,
Preallocated,
Shared,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ExtentFlags {
pub shared: bool,
pub compressed: bool,
pub encrypted: bool,
pub inline: bool,
pub last: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum HoleDetectionPolicy {
#[default]
Explicit,
DetectZeros,
Aggressive,
}
#[derive(Debug, Clone, Default)]
pub struct SparseCopyOptions {
pub preserve_holes: bool,
pub create_holes: bool,
pub min_hole_size: u64,
pub use_reflink: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hole_region() {
let hole = HoleRegion::new(1000, 500);
assert_eq!(hole.offset, 1000);
assert_eq!(hole.length, 500);
assert_eq!(hole.end(), 1500);
assert!(hole.contains(1000));
assert!(hole.contains(1499));
assert!(!hole.contains(1500));
}
#[test]
fn test_hole_overlaps() {
let hole = HoleRegion::new(1000, 500);
assert!(hole.overlaps(900, 200)); assert!(hole.overlaps(1100, 100)); assert!(hole.overlaps(1400, 200)); assert!(hole.overlaps(800, 1000));
assert!(!hole.overlaps(0, 1000)); assert!(!hole.overlaps(1500, 100)); }
#[test]
fn test_sparse_info_default() {
let info = SparseInfo::default();
assert_eq!(
info.block_size,
crate::storage::dmu::get_block_size() as u32
);
assert!(!info.is_sparse);
}
}