tdb_succinct/storage/
file.rs

1use std::io::SeekFrom;
2use std::path::PathBuf;
3
4use async_trait::async_trait;
5use bytes::{Bytes, BytesMut};
6use tokio::fs::File;
7use tokio::io::{self, AsyncReadExt, AsyncSeekExt, BufWriter};
8
9use super::{FileLoad, FileStore, SyncableFile};
10
11#[derive(Clone, Debug)]
12pub struct FileBackedStore {
13    path: PathBuf,
14}
15
16#[async_trait]
17impl SyncableFile for File {
18    async fn sync_all(self) -> io::Result<()> {
19        File::sync_all(&self).await
20    }
21}
22
23#[async_trait]
24impl SyncableFile for BufWriter<File> {
25    async fn sync_all(self) -> io::Result<()> {
26        let inner = self.into_inner();
27
28        File::sync_all(&inner).await
29    }
30}
31
32impl FileBackedStore {
33    pub fn new<P: Into<PathBuf>>(path: P) -> FileBackedStore {
34        FileBackedStore { path: path.into() }
35    }
36}
37
38#[async_trait]
39impl FileLoad for FileBackedStore {
40    type Read = File;
41
42    async fn exists(&self) -> io::Result<bool> {
43        let metadata = tokio::fs::metadata(&self.path).await;
44        Ok(!(metadata.is_err() && metadata.err().unwrap().kind() == io::ErrorKind::NotFound))
45    }
46
47    async fn size(&self) -> io::Result<usize> {
48        let m = tokio::fs::metadata(&self.path).await?;
49        Ok(m.len() as usize)
50    }
51
52    async fn open_read_from(&self, offset: usize) -> io::Result<File> {
53        let mut options = tokio::fs::OpenOptions::new();
54        options.read(true);
55        let mut file = options.open(&self.path).await?;
56
57        file.seek(SeekFrom::Start(offset as u64)).await?;
58
59        Ok(file)
60    }
61
62    async fn map(&self) -> io::Result<Bytes> {
63        let size = self.size().await?;
64        if size == 0 {
65            Ok(Bytes::new())
66        } else {
67            let mut f = self.open_read().await?;
68            let mut b = BytesMut::with_capacity(size);
69
70            // unsafe justification: We are immediately
71            // overwriting the data in this BytesMut with the file
72            // contents, so it doesn't matter that it is
73            // uninitialized.
74            // Should file reading fail, an error will be
75            // returned, and the BytesMut will be freed, ensuring
76            // nobody ever looks at the initialized data.
77            unsafe { b.set_len(size) };
78            f.read_exact(&mut b[..]).await?;
79            Ok(b.freeze())
80        }
81    }
82}
83
84#[async_trait]
85impl FileStore for FileBackedStore {
86    type Write = BufWriter<File>;
87
88    async fn open_write(&self) -> io::Result<BufWriter<File>> {
89        let mut options = tokio::fs::OpenOptions::new();
90        options.read(true).write(true).create(true);
91        let file = options.open(&self.path).await?;
92
93        Ok(BufWriter::new(file))
94    }
95}