Skip to main content

shuru_store/
backend.rs

1use std::fs::{File, OpenOptions};
2use std::os::unix::io::AsRawFd;
3
4/// Flat-file backend using pread/pwrite for thread-safe positional I/O.
5pub struct FlatFileBackend {
6    file: File,
7    path: String,
8    size: u64,
9}
10
11impl FlatFileBackend {
12    pub fn open(path: &str) -> anyhow::Result<Self> {
13        let file = OpenOptions::new().read(true).write(true).open(path)?;
14        let size = file.metadata()?.len();
15        Ok(FlatFileBackend { file, path: path.to_string(), size })
16    }
17
18    pub fn path(&self) -> &str {
19        &self.path
20    }
21
22    pub fn size(&self) -> u64 {
23        self.size
24    }
25
26    pub fn read(&self, offset: u64, buf: &mut [u8]) -> std::io::Result<usize> {
27        let fd = self.file.as_raw_fd();
28        let n = unsafe {
29            libc::pread(
30                fd,
31                buf.as_mut_ptr() as *mut libc::c_void,
32                buf.len(),
33                offset as libc::off_t,
34            )
35        };
36        if n < 0 {
37            Err(std::io::Error::last_os_error())
38        } else {
39            Ok(n as usize)
40        }
41    }
42
43    pub fn write(&self, offset: u64, buf: &[u8]) -> std::io::Result<usize> {
44        let fd = self.file.as_raw_fd();
45        let n = unsafe {
46            libc::pwrite(
47                fd,
48                buf.as_ptr() as *const libc::c_void,
49                buf.len(),
50                offset as libc::off_t,
51            )
52        };
53        if n < 0 {
54            Err(std::io::Error::last_os_error())
55        } else {
56            Ok(n as usize)
57        }
58    }
59
60    pub fn flush(&self) -> std::io::Result<()> {
61        let fd = self.file.as_raw_fd();
62        let ret = unsafe { libc::fsync(fd) };
63        if ret < 0 {
64            Err(std::io::Error::last_os_error())
65        } else {
66            Ok(())
67        }
68    }
69}