Skip to main content

fs_core/
storage.rs

1use alloc::{boxed::Box, vec::Vec};
2
3use crate::types::BLOCK_SIZE;
4
5#[derive(Default)]
6pub struct BlockStorage {
7    blocks: Vec<Option<Box<[u8; BLOCK_SIZE]>>>,
8    size: usize, // Actual file size (may be less than blocks.len() * BLOCK_SIZE)
9}
10
11impl BlockStorage {
12    pub fn new() -> Self {
13        Self {
14            blocks: Vec::new(),
15            size: 0,
16        }
17    }
18
19    pub fn read(&self, offset: usize, buf: &mut [u8]) -> usize {
20        if offset >= self.size {
21            return 0;
22        }
23
24        let mut bytes_read = 0;
25        let mut current_offset = offset;
26
27        while bytes_read < buf.len() && current_offset < self.size {
28            let block_index = current_offset / BLOCK_SIZE;
29            let block_offset = current_offset % BLOCK_SIZE;
30
31            if block_index >= self.blocks.len() {
32                break;
33            }
34
35            if let Some(block) = &self.blocks[block_index] {
36                let bytes_in_block = BLOCK_SIZE - block_offset;
37                let bytes_to_copy = core::cmp::min(
38                    bytes_in_block,
39                    core::cmp::min(buf.len() - bytes_read, self.size - current_offset),
40                );
41
42                buf[bytes_read..bytes_read + bytes_to_copy]
43                    .copy_from_slice(&block[block_offset..block_offset + bytes_to_copy]);
44
45                bytes_read += bytes_to_copy;
46                current_offset += bytes_to_copy;
47            } else {
48                // Sparse block: fill with zeros
49                let bytes_in_block = BLOCK_SIZE - block_offset;
50                let bytes_to_copy = core::cmp::min(
51                    bytes_in_block,
52                    core::cmp::min(buf.len() - bytes_read, self.size - current_offset),
53                );
54
55                for i in 0..bytes_to_copy {
56                    buf[bytes_read + i] = 0;
57                }
58
59                bytes_read += bytes_to_copy;
60                current_offset += bytes_to_copy;
61            }
62        }
63
64        bytes_read
65    }
66
67    pub fn write(&mut self, offset: usize, data: &[u8]) -> usize {
68        if data.is_empty() {
69            return 0;
70        }
71
72        let mut bytes_written = 0;
73        let mut current_offset = offset;
74
75        while bytes_written < data.len() {
76            let block_index = current_offset / BLOCK_SIZE;
77            let block_offset = current_offset % BLOCK_SIZE;
78
79            // Ensure we have enough blocks
80            while block_index >= self.blocks.len() {
81                self.blocks.push(None);
82            }
83
84            // Allocate block if needed
85            if self.blocks[block_index].is_none() {
86                self.blocks[block_index] = Some(Box::new([0u8; BLOCK_SIZE]));
87            }
88
89            if let Some(block) = &mut self.blocks[block_index] {
90                let bytes_in_block = BLOCK_SIZE - block_offset;
91                let bytes_to_copy = core::cmp::min(bytes_in_block, data.len() - bytes_written);
92
93                block[block_offset..block_offset + bytes_to_copy]
94                    .copy_from_slice(&data[bytes_written..bytes_written + bytes_to_copy]);
95
96                bytes_written += bytes_to_copy;
97                current_offset += bytes_to_copy;
98            }
99        }
100
101        // Update file size if we wrote past the end
102        if current_offset > self.size {
103            self.size = current_offset;
104        }
105
106        bytes_written
107    }
108
109    pub fn size(&self) -> usize {
110        self.size
111    }
112
113    pub fn truncate(&mut self, new_size: usize) {
114        if new_size < self.size {
115            // Shrinking the file
116            let new_block_count = new_size.div_ceil(BLOCK_SIZE);
117
118            // Free blocks beyond the new size
119            self.blocks.truncate(new_block_count);
120
121            // Clear bytes beyond new_size in the last block (if partially filled)
122            if new_size > 0 && new_block_count > 0 {
123                let last_block_index = new_block_count - 1;
124                let offset_in_last_block = new_size % BLOCK_SIZE;
125
126                if offset_in_last_block > 0
127                    && let Some(block) = self
128                        .blocks
129                        .get_mut(last_block_index)
130                        .and_then(|opt| opt.as_mut())
131                {
132                    // Clear the remainder of the block
133                    for byte in &mut block[offset_in_last_block..] {
134                        *byte = 0;
135                    }
136                }
137            }
138
139            self.size = new_size;
140        } else if new_size > self.size {
141            // Expanding the file
142            // Note: We don't need to allocate blocks immediately for sparse files
143            // They will be allocated on write. Just update the size.
144            self.size = new_size;
145        }
146        // If new_size == self.size, do nothing
147    }
148}