firewood_storage/linear/
mod.rs1#![expect(
8 clippy::missing_errors_doc,
9 reason = "Found 4 occurrences after enabling the lint."
10)]
11
12use std::fmt::Debug;
13use std::io::{Cursor, Read};
14use std::ops::Deref;
15use std::path::PathBuf;
16
17use crate::{CacheReadStrategy, LinearAddress, MaybePersistedNode, SharedNode};
18pub(super) mod filebacked;
19#[cfg(feature = "io-uring")]
20mod io_uring;
21pub mod memory;
22
23#[derive(Debug)]
28pub struct FileIoError {
29 inner: std::io::Error,
30 filename: Option<PathBuf>,
31 offset: u64,
32 context: Option<String>,
33}
34
35impl From<std::convert::Infallible> for FileIoError {
36 fn from(error: std::convert::Infallible) -> Self {
37 match error {}
38 }
39}
40
41impl FileIoError {
42 pub fn from_generic_no_file<T: std::error::Error>(error: T, context: &str) -> Self {
50 Self {
51 inner: std::io::Error::other(error.to_string()),
52 filename: None,
53 offset: 0,
54 context: Some(context.into()),
55 }
56 }
57
58 #[must_use]
67 pub const fn new(
68 inner: std::io::Error,
69 filename: Option<PathBuf>,
70 offset: u64,
71 context: Option<String>,
72 ) -> Self {
73 Self {
74 inner,
75 filename,
76 offset,
77 context,
78 }
79 }
80
81 #[cfg(test)]
82 pub(crate) fn context(&self) -> Option<&str> {
83 self.context.as_deref()
84 }
85}
86
87impl std::error::Error for FileIoError {
88 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
89 Some(&self.inner)
90 }
91}
92
93impl std::fmt::Display for FileIoError {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 write!(
96 f,
97 "{inner} at offset {offset} of file '{filename}' {context}",
98 inner = self.inner,
99 offset = self.offset,
100 filename = self
101 .filename
102 .as_ref()
103 .unwrap_or(&PathBuf::from("[unknown]"))
104 .display(),
105 context = self.context.as_ref().unwrap_or(&String::new())
106 )
107 }
108}
109
110impl Deref for FileIoError {
111 type Target = std::io::Error;
112
113 fn deref(&self) -> &Self::Target {
114 &self.inner
115 }
116}
117
118#[derive(Debug)]
119pub enum ReadableNodeMode {
120 Open,
121 Read,
122 ReconRead,
123 Write,
124}
125
126impl ReadableNodeMode {
127 pub const fn as_str(&self) -> &'static str {
128 match self {
129 ReadableNodeMode::Open => "open",
130 ReadableNodeMode::Read => "read",
131 ReadableNodeMode::ReconRead => "recon-read",
132 ReadableNodeMode::Write => "write",
133 }
134 }
135}
136
137pub trait ReadableStorage: Debug + Sync + Send {
139 fn node_hash_algorithm(&self) -> crate::NodeHashAlgorithm;
141
142 fn stream_from(&self, addr: u64) -> Result<impl OffsetReader, FileIoError>;
152
153 fn size(&self) -> Result<u64, FileIoError>;
155
156 fn read_cached_node(
158 &self,
159 _addr: LinearAddress,
160 _mode: ReadableNodeMode,
161 ) -> Option<SharedNode> {
162 None
163 }
164
165 fn free_list_cache(&self, _addr: LinearAddress) -> Option<Option<LinearAddress>> {
167 None
168 }
169
170 fn cache_read_strategy(&self) -> &CacheReadStrategy {
172 &CacheReadStrategy::WritesOnly
173 }
174
175 fn cache_node(&self, _addr: LinearAddress, _node: SharedNode) {}
177
178 fn filename(&self) -> Option<PathBuf> {
180 None
181 }
182
183 fn file_io_error(
185 &self,
186 error: std::io::Error,
187 offset: u64,
188 context: Option<String>,
189 ) -> FileIoError {
190 FileIoError {
191 inner: error,
192 filename: self.filename(),
193 offset,
194 context,
195 }
196 }
197}
198
199pub trait WritableStorage: ReadableStorage {
201 fn write(&self, offset: u64, object: &[u8]) -> Result<usize, FileIoError>;
212
213 fn write_batch<'a, I: IntoIterator<Item = (u64, &'a [u8])> + Clone>(
220 &self,
221 writes: I,
222 ) -> Result<usize, FileIoError> {
223 writes
224 .into_iter()
225 .try_fold(0_usize, |acc, (offset, object)| {
226 let bytes_written = self.write(offset, object)?;
227 acc.checked_add(bytes_written).ok_or_else(|| {
228 self.file_io_error(
229 std::io::Error::other("Overflow when summing bytes written"),
230 offset,
231 Some("write_batch".into()),
232 )
233 })
234 })
235 }
236
237 fn write_cached_nodes(
239 &self,
240 _nodes: impl IntoIterator<Item = MaybePersistedNode>,
241 ) -> Result<(), FileIoError> {
242 Ok(())
243 }
244
245 fn invalidate_cached_nodes<'a>(
247 &self,
248 _addresses: impl Iterator<Item = &'a MaybePersistedNode>,
249 ) {
250 }
251
252 fn add_to_free_list_cache(&self, _addr: LinearAddress, _next: Option<LinearAddress>) {}
254}
255
256pub trait OffsetReader: Read {
257 fn offset(&self) -> u64;
258}
259
260impl<T> OffsetReader for Cursor<T>
261where
262 Cursor<T>: Read,
263{
264 fn offset(&self) -> u64 {
265 self.position()
266 }
267}