iroh_blobs/util/
sparse_mem_file.rs

1use std::io;
2
3use bao_tree::io::sync::{ReadAt, Size, WriteAt};
4use derive_more::Deref;
5use range_collections::{range_set::RangeSetRange, RangeSet2};
6
7/// A file that is sparse in memory
8///
9/// It is not actually using sparse storage to make reading faster, so it will
10/// not conserve memory. It is just a way to remember the gaps so we can
11/// write it to a file in a sparse way later.
12#[derive(derive_more::Debug)]
13pub struct SparseMemFile {
14    /// The data, with gaps filled with zeros
15    #[debug("{} bytes", data.len())]
16    data: Vec<u8>,
17    /// The ranges that are not zeros, so we can distinguish between zeros and gaps
18    ranges: RangeSet2<usize>,
19}
20
21impl Default for SparseMemFile {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl From<Vec<u8>> for SparseMemFile {
28    fn from(data: Vec<u8>) -> Self {
29        let ranges = RangeSet2::from(0..data.len());
30        Self { data, ranges }
31    }
32}
33
34impl TryInto<Vec<u8>> for SparseMemFile {
35    type Error = io::Error;
36
37    fn try_into(self) -> Result<Vec<u8>, Self::Error> {
38        let (data, ranges) = self.into_parts();
39        if ranges == RangeSet2::from(0..data.len()) {
40            Ok(data)
41        } else {
42            Err(io::Error::new(
43                io::ErrorKind::InvalidData,
44                "SparseMemFile has gaps",
45            ))
46        }
47    }
48}
49
50impl SparseMemFile {
51    /// Create a new, empty SparseMemFile
52    pub fn new() -> Self {
53        Self {
54            data: Vec::new(),
55            ranges: RangeSet2::empty(),
56        }
57    }
58
59    /// Get the data and the valid ranges
60    pub fn into_parts(self) -> (Vec<u8>, RangeSet2<usize>) {
61        (self.data, self.ranges)
62    }
63
64    /// Persist the SparseMemFile to a WriteAt
65    ///
66    /// This will not persist the gaps, only the data that was written.
67    pub fn persist(&self, mut target: impl WriteAt) -> io::Result<()> {
68        let size = self.data.len();
69        for range in self.ranges.iter() {
70            let range = match range {
71                RangeSetRange::Range(range) => *range.start..*range.end,
72                RangeSetRange::RangeFrom(range) => *range.start..size,
73            };
74            let start = range.start.try_into().unwrap();
75            let buf = &self.data[range];
76            target.write_at(start, buf)?;
77        }
78        Ok(())
79    }
80}
81
82impl AsRef<[u8]> for SparseMemFile {
83    fn as_ref(&self) -> &[u8] {
84        &self.data
85    }
86}
87
88impl Deref for SparseMemFile {
89    type Target = [u8];
90
91    fn deref(&self) -> &Self::Target {
92        &self.data
93    }
94}
95
96impl ReadAt for SparseMemFile {
97    fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
98        self.data.read_at(offset, buf)
99    }
100}
101
102impl WriteAt for SparseMemFile {
103    fn write_at(&mut self, offset: u64, buf: &[u8]) -> io::Result<usize> {
104        let start: usize = offset.try_into().map_err(|_| io::ErrorKind::InvalidInput)?;
105        let end = start
106            .checked_add(buf.len())
107            .ok_or(io::ErrorKind::InvalidInput)?;
108        let n = self.data.write_at(offset, buf)?;
109        self.ranges |= RangeSet2::from(start..end);
110        Ok(n)
111    }
112
113    fn flush(&mut self) -> io::Result<()> {
114        Ok(())
115    }
116}
117
118impl Size for SparseMemFile {
119    fn size(&self) -> io::Result<Option<u64>> {
120        Ok(Some(self.data.len() as u64))
121    }
122}