]>
Commit | Line | Data |
---|---|---|
6cd4f635 WB |
1 | //! Blocking `pxar` random access handling. |
2 | ||
3 | use std::io; | |
4 | use std::os::unix::fs::FileExt; | |
5 | use std::path::Path; | |
6 | use std::pin::Pin; | |
7 | use std::task::{Context, Poll}; | |
8 | ||
9 | use crate::accessor::{self, ReadAt}; | |
10 | use crate::decoder::Decoder; | |
11 | use crate::util::poll_result_once; | |
12 | use crate::Entry; | |
13 | ||
14 | /// Blocking `pxar` random-access decoder. | |
15 | /// | |
16 | /// This is the blocking I/O version of the `pxar` accessor. This will *not* work with an | |
17 | /// asynchronous I/O object. I/O must always return `Poll::Ready`. | |
18 | /// | |
19 | /// Attempting to use a `Waker` from this context *will* `panic!` | |
20 | /// | |
21 | /// If you need to use asynchronous I/O, use `aio::Accessor`. | |
22 | #[repr(transparent)] | |
23 | pub struct Accessor<T> { | |
24 | inner: accessor::AccessorImpl<T>, | |
25 | } | |
26 | ||
29c17fc0 | 27 | impl<T: FileExt> Accessor<FileReader<T>> { |
6cd4f635 WB |
28 | /// Decode a `pxar` archive from a standard file implementing `FileExt`. |
29 | #[inline] | |
29c17fc0 | 30 | pub fn from_file_and_size(input: T, size: u64) -> io::Result<Self> { |
6cd4f635 WB |
31 | Accessor::new(FileReader::new(input), size) |
32 | } | |
33 | } | |
34 | ||
35 | impl Accessor<FileReader<std::fs::File>> { | |
36 | /// Decode a `pxar` archive from a regular `std::io::File` input. | |
37 | #[inline] | |
38 | pub fn from_file(input: std::fs::File) -> io::Result<Self> { | |
39 | let size = input.metadata()?.len(); | |
40 | Accessor::from_file_and_size(input, size) | |
41 | } | |
42 | ||
43 | /// Convenience shortcut for `File::open` followed by `Accessor::from_file`. | |
44 | pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> { | |
45 | Self::from_file(std::fs::File::open(path.as_ref())?) | |
46 | } | |
47 | } | |
48 | ||
29c17fc0 WB |
49 | impl<T: Clone + std::ops::Deref<Target = std::fs::File>> Accessor<FileRefReader<T>> { |
50 | /// Open an `Arc` or `Rc` of `File`. | |
51 | pub fn from_file_ref(input: T) -> io::Result<Self> { | |
52 | let size = input.deref().metadata()?.len(); | |
53 | Accessor::from_file_ref_and_size(input, size) | |
54 | } | |
55 | } | |
56 | ||
57 | impl<T> Accessor<FileRefReader<T>> | |
58 | where | |
59 | T: Clone + std::ops::Deref, | |
60 | T::Target: FileExt, | |
61 | { | |
62 | /// Open an `Arc` or `Rc` of `File`. | |
63 | pub fn from_file_ref_and_size(input: T, size: u64) -> io::Result<Accessor<FileRefReader<T>>> { | |
64 | Accessor::new(FileRefReader::new(input), size) | |
65 | } | |
66 | } | |
67 | ||
6cd4f635 WB |
68 | impl<T: ReadAt> Accessor<T> { |
69 | /// Create a *blocking* random-access decoder from an input implementing our internal read | |
70 | /// interface. | |
71 | /// | |
72 | /// Note that the `input`'s `SeqRead` implementation must always return `Poll::Ready` and is | |
73 | /// not allowed to use the `Waker`, as this will cause a `panic!`. | |
74 | pub fn new(input: T, size: u64) -> io::Result<Self> { | |
75 | Ok(Self { | |
76 | inner: poll_result_once(accessor::AccessorImpl::new(input, size))?, | |
77 | }) | |
78 | } | |
79 | ||
80 | /// Open a directory handle to the root of the pxar archive. | |
29c17fc0 WB |
81 | pub fn open_root_ref<'a>(&'a self) -> io::Result<Directory<&'a dyn ReadAt>> { |
82 | Ok(Directory::new(poll_result_once( | |
83 | self.inner.open_root_ref(), | |
84 | )?)) | |
85 | } | |
86 | } | |
87 | ||
88 | impl<T: Clone + ReadAt> Accessor<T> { | |
89 | pub fn open_root(&self) -> io::Result<Directory<T>> { | |
6cd4f635 WB |
90 | Ok(Directory::new(poll_result_once(self.inner.open_root())?)) |
91 | } | |
92 | } | |
93 | ||
94 | /// Adapter for FileExt readers. | |
29c17fc0 | 95 | #[derive(Clone)] |
6cd4f635 WB |
96 | pub struct FileReader<T> { |
97 | inner: T, | |
98 | } | |
99 | ||
100 | impl<T: FileExt> FileReader<T> { | |
101 | pub fn new(inner: T) -> Self { | |
102 | Self { inner } | |
103 | } | |
104 | } | |
105 | ||
106 | impl<T: FileExt> ReadAt for FileReader<T> { | |
107 | fn poll_read_at( | |
108 | self: Pin<&Self>, | |
109 | _cx: &mut Context, | |
110 | buf: &mut [u8], | |
111 | offset: u64, | |
112 | ) -> Poll<io::Result<usize>> { | |
113 | Poll::Ready(self.get_ref().inner.read_at(buf, offset)) | |
114 | } | |
115 | } | |
116 | ||
29c17fc0 WB |
117 | /// Adapter for `Arc` or `Rc` to FileExt readers. |
118 | #[derive(Clone)] | |
119 | pub struct FileRefReader<T: Clone> { | |
120 | inner: T, | |
121 | } | |
122 | ||
123 | impl<T: Clone> FileRefReader<T> { | |
124 | pub fn new(inner: T) -> Self { | |
125 | Self { inner } | |
126 | } | |
127 | } | |
128 | ||
129 | impl<T> ReadAt for FileRefReader<T> | |
130 | where | |
131 | T: Clone + std::ops::Deref, | |
132 | T::Target: FileExt, | |
133 | { | |
134 | fn poll_read_at( | |
135 | self: Pin<&Self>, | |
136 | _cx: &mut Context, | |
137 | buf: &mut [u8], | |
138 | offset: u64, | |
139 | ) -> Poll<io::Result<usize>> { | |
140 | Poll::Ready(self.get_ref().inner.read_at(buf, offset)) | |
141 | } | |
142 | } | |
143 | ||
6cd4f635 WB |
144 | /// Blocking Directory variant: |
145 | #[repr(transparent)] | |
29c17fc0 WB |
146 | pub struct Directory<T> { |
147 | inner: accessor::DirectoryImpl<T>, | |
6cd4f635 WB |
148 | } |
149 | ||
29c17fc0 WB |
150 | impl<T: Clone + ReadAt> Directory<T> { |
151 | fn new(inner: accessor::DirectoryImpl<T>) -> Self { | |
6cd4f635 WB |
152 | Self { inner } |
153 | } | |
154 | ||
155 | /// Get a decoder for the directory contents. | |
29c17fc0 | 156 | pub fn decode_full(&self) -> io::Result<Decoder<accessor::SeqReadAtAdapter<T>>> { |
6cd4f635 WB |
157 | Ok(Decoder::from_impl(poll_result_once( |
158 | self.inner.decode_full(), | |
159 | )?)) | |
160 | } | |
161 | ||
162 | /// Lookup an entry in a directory. | |
29c17fc0 | 163 | pub fn lookup<P: AsRef<Path>>(&self, path: P) -> io::Result<Option<FileEntry<T>>> { |
6cd4f635 WB |
164 | if let Some(file_entry) = poll_result_once(self.inner.lookup(path.as_ref()))? { |
165 | Ok(Some(FileEntry { inner: file_entry })) | |
166 | } else { | |
167 | Ok(None) | |
168 | } | |
169 | } | |
170 | ||
171 | /// Get an iterator over the directory's contents. | |
29c17fc0 | 172 | pub fn read_dir<'a>(&'a self) -> ReadDir<'a, T> { |
6cd4f635 WB |
173 | ReadDir { |
174 | inner: self.inner.read_dir(), | |
175 | } | |
176 | } | |
177 | } | |
178 | ||
179 | /// A file entry retrieved from a `Directory` via the `lookup` method. | |
180 | #[repr(transparent)] | |
29c17fc0 WB |
181 | pub struct FileEntry<T: Clone + ReadAt> { |
182 | inner: accessor::FileEntryImpl<T>, | |
6cd4f635 WB |
183 | } |
184 | ||
29c17fc0 WB |
185 | impl<T: Clone + ReadAt> FileEntry<T> { |
186 | pub fn enter_directory(&self) -> io::Result<Directory<T>> { | |
6cd4f635 WB |
187 | Ok(Directory::new(poll_result_once( |
188 | self.inner.enter_directory(), | |
189 | )?)) | |
190 | } | |
191 | ||
98b894a9 WB |
192 | pub fn contents(&self) -> io::Result<FileContents<T>> { |
193 | Ok(FileContents { | |
194 | inner: poll_result_once(self.inner.contents())?, | |
195 | at: 0, | |
196 | }) | |
197 | } | |
198 | ||
6cd4f635 WB |
199 | #[inline] |
200 | pub fn into_entry(self) -> Entry { | |
201 | self.inner.into_entry() | |
202 | } | |
203 | ||
204 | #[inline] | |
205 | pub fn entry(&self) -> &Entry { | |
206 | &self.inner.entry() | |
207 | } | |
208 | } | |
209 | ||
29c17fc0 | 210 | impl<T: Clone + ReadAt> std::ops::Deref for FileEntry<T> { |
05005b14 WB |
211 | type Target = Entry; |
212 | ||
213 | fn deref(&self) -> &Self::Target { | |
214 | self.entry() | |
215 | } | |
216 | } | |
217 | ||
6cd4f635 WB |
218 | /// An iterator over the contents of a `Directory`. |
219 | #[repr(transparent)] | |
29c17fc0 WB |
220 | pub struct ReadDir<'a, T> { |
221 | inner: accessor::ReadDirImpl<'a, T>, | |
6cd4f635 WB |
222 | } |
223 | ||
98b894a9 WB |
224 | impl<'a, T: Clone + ReadAt> ReadDir<'a, T> { |
225 | /// Efficient alternative to `Iterator::skip`. | |
226 | #[inline] | |
227 | pub fn skip(self, n: usize) -> Self { | |
228 | Self { | |
229 | inner: self.inner.skip(n), | |
230 | } | |
231 | } | |
232 | ||
233 | /// Efficient alternative to `Iterator::count`. | |
234 | #[inline] | |
235 | pub fn count(self) -> usize { | |
236 | self.inner.count() | |
237 | } | |
238 | } | |
239 | ||
29c17fc0 WB |
240 | impl<'a, T: Clone + ReadAt> Iterator for ReadDir<'a, T> { |
241 | type Item = io::Result<DirEntry<'a, T>>; | |
6cd4f635 WB |
242 | |
243 | fn next(&mut self) -> Option<Self::Item> { | |
244 | match poll_result_once(self.inner.next()) { | |
245 | Ok(Some(inner)) => Some(Ok(DirEntry { inner })), | |
246 | Ok(None) => None, | |
247 | Err(err) => Some(Err(err)), | |
248 | } | |
249 | } | |
250 | } | |
251 | ||
29c17fc0 | 252 | impl<'a, T: Clone + ReadAt> std::iter::FusedIterator for ReadDir<'a, T> {} |
6cd4f635 WB |
253 | |
254 | /// A directory entry. When iterating through the contents of a directory we first get access to | |
255 | /// the file name. The remaining information can be decoded afterwards. | |
256 | #[repr(transparent)] | |
29c17fc0 WB |
257 | pub struct DirEntry<'a, T: Clone + ReadAt> { |
258 | inner: accessor::DirEntryImpl<'a, T>, | |
6cd4f635 WB |
259 | } |
260 | ||
29c17fc0 | 261 | impl<'a, T: Clone + ReadAt> DirEntry<'a, T> { |
6cd4f635 WB |
262 | pub fn file_name(&self) -> &Path { |
263 | self.inner.file_name() | |
264 | } | |
265 | ||
29c17fc0 | 266 | pub fn get_entry(&self) -> io::Result<FileEntry<T>> { |
6cd4f635 WB |
267 | poll_result_once(self.inner.get_entry()).map(|inner| FileEntry { inner }) |
268 | } | |
269 | } | |
98b894a9 WB |
270 | |
271 | /// A reader for file contents. | |
272 | pub struct FileContents<T> { | |
273 | inner: accessor::FileContentsImpl<T>, | |
274 | at: u64, | |
275 | } | |
276 | ||
277 | impl<T: Clone + ReadAt> io::Read for FileContents<T> { | |
278 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
279 | let got = poll_result_once(self.inner.read_at(buf, self.at))?; | |
280 | self.at += got as u64; | |
281 | Ok(got) | |
282 | } | |
283 | } | |
284 | ||
285 | impl<T: Clone + ReadAt> FileExt for FileContents<T> { | |
286 | fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { | |
287 | poll_result_once(self.inner.read_at(buf, offset)) | |
288 | } | |
289 | ||
290 | fn write_at(&self, _buf: &[u8], _offset: u64) -> io::Result<usize> { | |
291 | io_bail!("write_at on read-only file"); | |
292 | } | |
293 | } | |
294 | ||
295 | impl<T: Clone + ReadAt> ReadAt for FileContents<T> { | |
296 | fn poll_read_at( | |
297 | self: Pin<&Self>, | |
298 | _cx: &mut Context, | |
299 | buf: &mut [u8], | |
300 | offset: u64, | |
301 | ) -> Poll<io::Result<usize>> { | |
302 | Poll::Ready(poll_result_once(self.get_ref().inner.read_at(buf, offset))) | |
303 | } | |
304 | } |