lcpfs 2026.1.102

LCP File System - A ZFS-inspired copy-on-write filesystem for Rust
// Copyright 2025 LunaOS Contributors
// SPDX-License-Identifier: Apache-2.0

//! Sparse Files Module.
//!
//! Provides efficient storage for files with holes (regions containing only zeros).
//! Sparse files only allocate disk space for non-zero regions, making them ideal
//! for disk images, database files, and other files with large empty regions.

extern crate alloc;

mod holes;
mod types;

pub use holes::*;
pub use types::*;

use alloc::string::String;
use alloc::vec::Vec;

/// Sparse Files API
pub struct Sparse;

impl Sparse {
    /// Check if a file is sparse
    pub fn is_sparse(dataset: &str, object_id: u64) -> Result<bool, SparseError> {
        let info = get_sparse_info(dataset, object_id)?;
        Ok(info.is_sparse)
    }

    /// Get sparse file information
    pub fn info(dataset: &str, object_id: u64) -> Result<SparseInfo, SparseError> {
        get_sparse_info(dataset, object_id)
    }

    /// Get list of holes in a file
    pub fn holes(dataset: &str, object_id: u64) -> Result<Vec<HoleRegion>, SparseError> {
        get_holes(dataset, object_id)
    }

    /// Get list of data regions in a file
    pub fn data_regions(dataset: &str, object_id: u64) -> Result<Vec<DataRegion>, SparseError> {
        get_data_regions(dataset, object_id)
    }

    /// Punch a hole in a file (deallocate a region)
    pub fn punch_hole(
        dataset: &str,
        object_id: u64,
        offset: u64,
        length: u64,
    ) -> Result<(), SparseError> {
        punch_hole_impl(dataset, object_id, offset, length)
    }

    /// Zero a region (may convert to hole if beneficial)
    pub fn zero_range(
        dataset: &str,
        object_id: u64,
        offset: u64,
        length: u64,
    ) -> Result<(), SparseError> {
        zero_range_impl(dataset, object_id, offset, length)
    }

    /// Convert a file to sparse format
    pub fn make_sparse(dataset: &str, object_id: u64) -> Result<SparsifyResult, SparseError> {
        sparsify_file(dataset, object_id)
    }

    /// Convert a sparse file to non-sparse (fill holes with zeros)
    pub fn make_dense(dataset: &str, object_id: u64) -> Result<DensifyResult, SparseError> {
        densify_file(dataset, object_id)
    }

    /// Seek to next data region
    pub fn seek_data(dataset: &str, object_id: u64, offset: u64) -> Result<u64, SparseError> {
        seek_data_impl(dataset, object_id, offset)
    }

    /// Seek to next hole
    pub fn seek_hole(dataset: &str, object_id: u64, offset: u64) -> Result<u64, SparseError> {
        seek_hole_impl(dataset, object_id, offset)
    }

    /// Allocate space without writing (preallocate)
    pub fn preallocate(
        dataset: &str,
        object_id: u64,
        offset: u64,
        length: u64,
    ) -> Result<(), SparseError> {
        preallocate_impl(dataset, object_id, offset, length)
    }

    /// Get space savings from sparse storage
    pub fn space_savings(dataset: &str, object_id: u64) -> Result<SpaceSavings, SparseError> {
        calculate_space_savings(dataset, object_id)
    }

    /// Copy a sparse file preserving holes
    pub fn copy_sparse(
        src_dataset: &str,
        src_id: u64,
        dst_dataset: &str,
        dst_path: &str,
    ) -> Result<u64, SparseError> {
        copy_sparse_impl(src_dataset, src_id, dst_dataset, dst_path)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sparse_api() {
        // Basic API structure test
        // Real tests would require filesystem integration
    }
}