]> git.proxmox.com Git - pxar.git/blame - src/accessor.rs
implement accessor::aio
[pxar.git] / src / accessor.rs
CommitLineData
6cd4f635
WB
1//! Random access for PXAR files.
2
dc4a2854 3use std::ffi::{OsStr, OsString};
6cd4f635 4use std::io;
dc4a2854 5use std::mem::{self, size_of, size_of_val, MaybeUninit};
6cd4f635
WB
6use std::ops::Range;
7use std::os::unix::ffi::{OsStrExt, OsStringExt};
8use std::path::{Path, PathBuf};
9use std::pin::Pin;
9d8af6f2 10use std::sync::Arc;
6cd4f635
WB
11use std::task::{Context, Poll};
12
13use endian_trait::Endian;
14
fbddffdc 15use crate::binary_tree_array;
6cd4f635
WB
16use crate::decoder::{self, DecoderImpl};
17use crate::format::{self, GoodbyeItem};
18use crate::poll_fn::poll_fn;
19use crate::util;
98b894a9 20use crate::{Entry, EntryKind};
6cd4f635
WB
21
22pub mod aio;
9d8af6f2 23pub mod cache;
6cd4f635
WB
24pub mod sync;
25
26#[doc(inline)]
2c23bd09 27pub use sync::{Accessor, DirEntry, Directory, FileEntry, ReadDir};
6cd4f635 28
9d8af6f2
WB
29use cache::Cache;
30
6cd4f635
WB
31/// Random access read implementation.
32pub trait ReadAt {
33 fn poll_read_at(
34 self: Pin<&Self>,
35 cx: &mut Context,
36 buf: &mut [u8],
37 offset: u64,
38 ) -> Poll<io::Result<usize>>;
39}
40
41/// We do not want to bother with actual polling, so we implement `async fn` variants of the above
42/// on `dyn ReadAt`.
43///
44/// The reason why this is not an internal `ReadAtExt` trait like `AsyncReadExt` is simply that
45/// we'd then need to define all the `Future` types they return manually and explicitly. Since we
46/// have no use for them, all we want is the ability to use `async fn`...
47///
48/// The downside is that we need some `(&mut self.input as &mut dyn ReadAt)` casts in the
49/// decoder's code, but that's fine.
50impl<'a> dyn ReadAt + 'a {
51 /// awaitable version of `poll_read_at`.
52 async fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
53 poll_fn(|cx| unsafe { Pin::new_unchecked(self).poll_read_at(cx, buf, offset) }).await
54 }
55
56 /// `read_exact_at` - since that's what we _actually_ want most of the time.
57 async fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
58 while !buf.is_empty() {
59 match self.read_at(buf, offset).await? {
60 0 => io_bail!("unexpected EOF"),
61 got => {
62 buf = &mut buf[got..];
63 offset += got as u64;
64 }
65 }
66 }
67 Ok(())
68 }
69
70 /// Helper to read into an `Endian`-implementing `struct`.
71 async fn read_entry_at<T: Endian>(&self, offset: u64) -> io::Result<T> {
72 let mut data = MaybeUninit::<T>::uninit();
73 let buf =
74 unsafe { std::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut u8, size_of::<T>()) };
75 self.read_exact_at(buf, offset).await?;
76 Ok(unsafe { data.assume_init().from_le() })
77 }
78
79 /// Helper to read into an allocated byte vector.
80 async fn read_exact_data_at(&self, size: usize, offset: u64) -> io::Result<Vec<u8>> {
81 let mut data = util::vec_new(size);
82 self.read_exact_at(&mut data[..], offset).await?;
83 Ok(data)
84 }
85}
86
29c17fc0
WB
87/// Allow using trait objects for `T: ReadAt`
88impl<'a> ReadAt for &(dyn ReadAt + 'a) {
89 fn poll_read_at(
90 self: Pin<&Self>,
91 cx: &mut Context,
92 buf: &mut [u8],
93 offset: u64,
94 ) -> Poll<io::Result<usize>> {
d3a83ee3 95 unsafe { Pin::new_unchecked(&**self).poll_read_at(cx, buf, offset) }
29c17fc0
WB
96 }
97}
98
b764a2b1 99#[derive(Clone)]
9d8af6f2
WB
100struct Caches {
101 /// The goodbye table cache maps goodbye table offsets to cache entries.
102 gbt_cache: Option<Arc<dyn Cache<u64, [GoodbyeItem]> + Send + Sync>>,
103}
104
105impl Default for Caches {
106 fn default() -> Self {
107 Self { gbt_cache: None }
108 }
109}
110
6cd4f635 111/// The random access state machine implementation.
5cf335be 112pub(crate) struct AccessorImpl<T> {
6cd4f635
WB
113 input: T,
114 size: u64,
9d8af6f2 115 caches: Arc<Caches>,
6cd4f635
WB
116}
117
118impl<T: ReadAt> AccessorImpl<T> {
119 pub async fn new(input: T, size: u64) -> io::Result<Self> {
120 if size < (size_of::<GoodbyeItem>() as u64) {
121 io_bail!("too small to contain a pxar archive");
122 }
9d8af6f2
WB
123
124 Ok(Self {
125 input,
126 size,
127 caches: Arc::new(Caches::default()),
128 })
6cd4f635
WB
129 }
130
a2530fb7
WB
131 pub fn size(&self) -> u64 {
132 self.size
133 }
134
29c17fc0 135 pub async fn open_root_ref<'a>(&'a self) -> io::Result<DirectoryImpl<&'a dyn ReadAt>> {
9d8af6f2
WB
136 DirectoryImpl::open_at_end(
137 &self.input as &dyn ReadAt,
138 self.size,
139 "/".into(),
140 Arc::clone(&self.caches),
141 )
142 .await
29c17fc0 143 }
b764a2b1
WB
144
145 pub fn set_goodbye_table_cache(
146 &mut self,
147 cache: Option<Arc<dyn Cache<u64, [GoodbyeItem]> + Send + Sync>>,
148 ) {
149 let new_caches = Arc::new(Caches {
150 gbt_cache: cache,
151 ..*self.caches
152 });
153 self.caches = new_caches;
154 }
29c17fc0
WB
155}
156
6b9e2478
WB
157async fn get_decoder<T: ReadAt>(
158 input: T,
159 entry_range: Range<u64>,
160 path: PathBuf,
161) -> io::Result<DecoderImpl<SeqReadAtAdapter<T>>> {
d3a83ee3 162 Ok(DecoderImpl::new_full(SeqReadAtAdapter::new(input, entry_range), path).await?)
6b9e2478
WB
163}
164
29c17fc0
WB
165impl<T: Clone + ReadAt> AccessorImpl<T> {
166 pub async fn open_root(&self) -> io::Result<DirectoryImpl<T>> {
9d8af6f2
WB
167 DirectoryImpl::open_at_end(
168 self.input.clone(),
169 self.size,
170 "/".into(),
171 Arc::clone(&self.caches),
172 )
173 .await
6cd4f635 174 }
ceb83806
WB
175
176 /// Allow opening a directory at a specified offset.
177 pub async unsafe fn open_dir_at_end(&self, offset: u64) -> io::Result<DirectoryImpl<T>> {
178 DirectoryImpl::open_at_end(
179 self.input.clone(),
180 offset,
181 "/".into(),
182 Arc::clone(&self.caches),
183 )
184 .await
185 }
6b9e2478
WB
186
187 /// Allow opening a regular file from a specified range.
188 pub async unsafe fn open_file_at_range(
189 &self,
190 range: Range<u64>,
191 ) -> io::Result<FileEntryImpl<T>> {
192 let mut decoder = get_decoder(self.input.clone(), range.clone(), PathBuf::new()).await?;
193 let entry = decoder
194 .next()
195 .await
196 .ok_or_else(|| io_format_err!("unexpected EOF while decoding file entry"))??;
197 Ok(FileEntryImpl {
198 input: self.input.clone(),
199 entry,
200 entry_range: range,
201 caches: Arc::clone(&self.caches),
202 })
203 }
204
205 /// Allow opening arbitrary contents from a specific range.
206 pub unsafe fn open_contents_at_range(&self, range: Range<u64>) -> FileContentsImpl<T> {
207 FileContentsImpl::new(self.input.clone(), range)
208 }
6cd4f635
WB
209}
210
211/// The directory random-access state machine implementation.
5cf335be 212pub(crate) struct DirectoryImpl<T> {
29c17fc0 213 input: T,
6cd4f635
WB
214 entry_ofs: u64,
215 goodbye_ofs: u64,
216 size: u64,
9d8af6f2 217 table: Arc<[GoodbyeItem]>,
6cd4f635 218 path: PathBuf,
9d8af6f2 219 caches: Arc<Caches>,
6cd4f635
WB
220}
221
29c17fc0 222impl<T: Clone + ReadAt> DirectoryImpl<T> {
6cd4f635 223 /// Open a directory ending at the specified position.
9d8af6f2 224 async fn open_at_end(
29c17fc0 225 input: T,
6cd4f635
WB
226 end_offset: u64,
227 path: PathBuf,
9d8af6f2 228 caches: Arc<Caches>,
29c17fc0
WB
229 ) -> io::Result<DirectoryImpl<T>> {
230 let tail = Self::read_tail_entry(&input, end_offset).await?;
6cd4f635
WB
231
232 if end_offset < tail.size {
233 io_bail!("goodbye tail size out of range");
234 }
235
236 let goodbye_ofs = end_offset - tail.size;
237
238 if goodbye_ofs < tail.offset {
239 io_bail!("goodbye offset out of range");
240 }
241
242 let entry_ofs = goodbye_ofs - tail.offset;
243 let size = end_offset - entry_ofs;
244
9d8af6f2
WB
245 let table: Option<Arc<[GoodbyeItem]>> = caches
246 .gbt_cache
247 .as_ref()
248 .and_then(|cache| cache.fetch(goodbye_ofs));
249
6cd4f635
WB
250 let mut this = Self {
251 input,
252 entry_ofs,
253 goodbye_ofs,
254 size,
9d8af6f2 255 table: table.as_ref().map_or_else(|| Arc::new([]), Arc::clone),
6cd4f635 256 path,
9d8af6f2 257 caches,
6cd4f635
WB
258 };
259
260 // sanity check:
261 if this.table_size() % (size_of::<GoodbyeItem>() as u64) != 0 {
262 io_bail!("invalid goodbye table size: {}", this.table_size());
263 }
264
9d8af6f2
WB
265 if table.is_none() {
266 this.table = this.load_table().await?;
267 if let Some(ref cache) = this.caches.gbt_cache {
268 cache.insert(goodbye_ofs, Arc::clone(&this.table));
269 }
270 }
6cd4f635
WB
271
272 Ok(this)
273 }
274
275 /// Load the entire goodbye table:
9d8af6f2 276 async fn load_table(&self) -> io::Result<Arc<[GoodbyeItem]>> {
6cd4f635
WB
277 let len = self.len();
278 let mut data = Vec::with_capacity(self.len());
279 unsafe {
280 data.set_len(len);
281 let slice = std::slice::from_raw_parts_mut(
282 data.as_mut_ptr() as *mut u8,
2c23bd09 283 len * size_of::<GoodbyeItem>(),
6cd4f635 284 );
29c17fc0
WB
285 (&self.input as &dyn ReadAt)
286 .read_exact_at(slice, self.table_offset())
287 .await?;
6cd4f635
WB
288 drop(slice);
289 }
9d8af6f2 290 Ok(Arc::from(data))
6cd4f635
WB
291 }
292
293 #[inline]
294 fn end_offset(&self) -> u64 {
295 self.entry_ofs + self.size
296 }
297
dc4a2854
WB
298 #[inline]
299 fn entry_range(&self) -> Range<u64> {
300 self.entry_ofs..self.end_offset()
301 }
302
6cd4f635
WB
303 #[inline]
304 fn table_size(&self) -> u64 {
305 (self.end_offset() - self.goodbye_ofs) - (size_of::<format::Header>() as u64)
306 }
307
308 #[inline]
309 fn table_offset(&self) -> u64 {
310 self.goodbye_ofs + (size_of::<format::Header>() as u64)
311 }
312
313 /// Length *excluding* the tail marker!
314 #[inline]
315 fn len(&self) -> usize {
316 (self.table_size() / (size_of::<GoodbyeItem>() as u64)) as usize - 1
317 }
318
319 /// Read the goodbye tail and perform some sanity checks.
29c17fc0 320 async fn read_tail_entry(input: &'_ dyn ReadAt, end_offset: u64) -> io::Result<GoodbyeItem> {
6cd4f635
WB
321 if end_offset < (size_of::<GoodbyeItem>() as u64) {
322 io_bail!("goodbye tail does not fit");
323 }
324
325 let tail_offset = end_offset - (size_of::<GoodbyeItem>() as u64);
326 let tail: GoodbyeItem = input.read_entry_at(tail_offset).await?;
327
328 if tail.hash != format::PXAR_GOODBYE_TAIL_MARKER {
329 io_bail!("no goodbye tail marker found");
330 }
331
332 Ok(tail)
333 }
334
335 /// Get a decoder for the directory contents.
29c17fc0 336 pub(crate) async fn decode_full(&self) -> io::Result<DecoderImpl<SeqReadAtAdapter<T>>> {
dc4a2854 337 let (dir, decoder) = self.decode_one_entry(self.entry_range(), None).await?;
6cd4f635
WB
338 if !dir.is_dir() {
339 io_bail!("directory does not seem to be a directory");
340 }
341 Ok(decoder)
342 }
343
344 async fn get_decoder(
345 &self,
346 entry_range: Range<u64>,
347 file_name: Option<&Path>,
29c17fc0 348 ) -> io::Result<DecoderImpl<SeqReadAtAdapter<T>>> {
6b9e2478
WB
349 get_decoder(
350 self.input.clone(),
351 entry_range,
6cd4f635
WB
352 match file_name {
353 None => self.path.clone(),
354 Some(file) => self.path.join(file),
355 },
d3a83ee3
WB
356 )
357 .await
6cd4f635
WB
358 }
359
360 async fn decode_one_entry(
361 &self,
362 entry_range: Range<u64>,
363 file_name: Option<&Path>,
29c17fc0 364 ) -> io::Result<(Entry, DecoderImpl<SeqReadAtAdapter<T>>)> {
6cd4f635
WB
365 let mut decoder = self.get_decoder(entry_range, file_name).await?;
366 let entry = decoder
367 .next()
368 .await
369 .ok_or_else(|| io_format_err!("unexpected EOF while decoding directory entry"))??;
370 Ok((entry, decoder))
371 }
372
fbddffdc
WB
373 fn lookup_hash_position(&self, hash: u64, start: usize, skip: usize) -> Option<usize> {
374 binary_tree_array::search_by(&self.table, start, skip, |i| hash.cmp(&i.hash))
6cd4f635
WB
375 }
376
a5922fbc 377 pub async fn lookup_self(&self) -> io::Result<FileEntryImpl<T>> {
c76d3f98 378 let (entry, _decoder) = self.decode_one_entry(self.entry_range(), None).await?;
dc4a2854
WB
379 Ok(FileEntryImpl {
380 input: self.input.clone(),
381 entry,
ceb83806 382 entry_range: self.entry_range(),
9d8af6f2 383 caches: Arc::clone(&self.caches),
dc4a2854
WB
384 })
385 }
386
6cd4f635 387 /// Lookup a directory entry.
29c17fc0 388 pub async fn lookup(&self, path: &Path) -> io::Result<Option<FileEntryImpl<T>>> {
dc4a2854
WB
389 let mut cur: Option<FileEntryImpl<T>> = None;
390
391 let mut first = true;
392 for component in path.components() {
393 use std::path::Component;
394
395 let first = mem::replace(&mut first, false);
396
397 let component = match component {
398 Component::Normal(path) => path,
399 Component::ParentDir => io_bail!("cannot enter parent directory in archive"),
400 Component::RootDir | Component::CurDir if first => {
401 cur = Some(self.lookup_self().await?);
402 continue;
403 }
404 Component::CurDir => continue,
405 _ => io_bail!("invalid component in path"),
406 };
407
408 let next = match cur {
409 Some(entry) => {
410 entry
411 .enter_directory()
412 .await?
413 .lookup_component(component)
414 .await?
415 }
416 None => self.lookup_component(component).await?,
417 };
418
419 if next.is_none() {
420 return Ok(None);
421 }
422
423 cur = next;
424 }
425
426 Ok(cur)
427 }
428
429 /// Lookup a single directory entry component (does not handle multiple components in path)
430 pub async fn lookup_component(&self, path: &OsStr) -> io::Result<Option<FileEntryImpl<T>>> {
431 let hash = format::hash_filename(path.as_bytes());
fbddffdc 432 let first_index = match self.lookup_hash_position(hash, 0, 0) {
6cd4f635
WB
433 Some(index) => index,
434 None => return Ok(None),
435 };
436
fbddffdc
WB
437 // Lookup FILENAME, if the hash matches but the filename doesn't, check for a duplicate
438 // hash once found, use the GoodbyeItem's offset+size as well as the file's Entry to return
439 // a DirEntry::Dir or Dir::Entry.
440 //
441 let mut dup = 0;
442 loop {
443 let index = match self.lookup_hash_position(hash, first_index, dup) {
444 Some(index) => index,
445 None => return Ok(None),
446 };
6cd4f635 447
6cd4f635
WB
448 let cursor = self.get_cursor(index).await?;
449 if cursor.file_name == path {
aabb78a4 450 return Ok(Some(cursor.decode_entry().await?));
6cd4f635 451 }
6cd4f635 452
fbddffdc
WB
453 dup += 1;
454 }
6cd4f635
WB
455 }
456
29c17fc0 457 async fn get_cursor<'a>(&'a self, index: usize) -> io::Result<DirEntryImpl<'a, T>> {
6cd4f635
WB
458 let entry = &self.table[index];
459 let file_goodbye_ofs = entry.offset;
460 if self.goodbye_ofs < file_goodbye_ofs {
461 io_bail!("invalid file offset");
462 }
463
464 let file_ofs = self.goodbye_ofs - file_goodbye_ofs;
465 let (file_name, entry_ofs) = self.read_filename_entry(file_ofs).await?;
466
70acf637
WB
467 let entry_range = Range {
468 start: entry_ofs,
469 end: file_ofs + entry.size,
470 };
471 if entry_range.end < entry_range.start {
472 io_bail!(
473 "bad file: invalid entry ranges for {:?}: \
474 start=0x{:x}, file_ofs=0x{:x}, size=0x{:x}",
475 file_name,
476 entry_ofs,
477 file_ofs,
478 entry.size,
479 );
480 }
481
6cd4f635
WB
482 Ok(DirEntryImpl {
483 dir: self,
484 file_name,
70acf637 485 entry_range,
9d8af6f2 486 caches: Arc::clone(&self.caches),
6cd4f635
WB
487 })
488 }
489
490 async fn read_filename_entry(&self, file_ofs: u64) -> io::Result<(PathBuf, u64)> {
29c17fc0 491 let head: format::Header = (&self.input as &dyn ReadAt).read_entry_at(file_ofs).await?;
6cd4f635
WB
492 if head.htype != format::PXAR_FILENAME {
493 io_bail!("expected PXAR_FILENAME header, found: {:x}", head.htype);
494 }
495
29c17fc0 496 let mut path = (&self.input as &dyn ReadAt)
6cd4f635
WB
497 .read_exact_data_at(
498 head.content_size() as usize,
499 file_ofs + (size_of_val(&head) as u64),
500 )
501 .await?;
502
503 if path.pop() != Some(0) {
504 io_bail!("invalid file name (missing terminating zero)");
505 }
506
507 if path.is_empty() {
508 io_bail!("invalid empty file name");
509 }
510
511 let file_name = PathBuf::from(OsString::from_vec(path));
512 format::check_file_name(&file_name)?;
513
514 Ok((file_name, file_ofs + head.full_size()))
515 }
516
29c17fc0 517 pub fn read_dir(&self) -> ReadDirImpl<T> {
6cd4f635
WB
518 ReadDirImpl::new(self, 0)
519 }
d3a83ee3
WB
520
521 pub fn entry_count(&self) -> usize {
522 self.table.len()
523 }
6cd4f635
WB
524}
525
526/// A file entry retrieved from a Directory.
5cf335be 527pub(crate) struct FileEntryImpl<T: Clone + ReadAt> {
29c17fc0 528 input: T,
6cd4f635 529 entry: Entry,
ceb83806 530 entry_range: Range<u64>,
9d8af6f2 531 caches: Arc<Caches>,
6cd4f635
WB
532}
533
29c17fc0
WB
534impl<T: Clone + ReadAt> FileEntryImpl<T> {
535 pub async fn enter_directory(&self) -> io::Result<DirectoryImpl<T>> {
6cd4f635
WB
536 if !self.entry.is_dir() {
537 io_bail!("enter_directory() on a non-directory");
538 }
539
9d8af6f2
WB
540 DirectoryImpl::open_at_end(
541 self.input.clone(),
ceb83806 542 self.entry_range.end,
9d8af6f2
WB
543 self.entry.path.clone(),
544 Arc::clone(&self.caches),
545 )
546 .await
6cd4f635
WB
547 }
548
6b9e2478
WB
549 /// For use with unsafe accessor methods.
550 pub fn content_range(&self) -> io::Result<Option<Range<u64>>> {
98b894a9 551 match self.entry.kind {
c76d3f98
WB
552 EntryKind::File { offset: None, .. } => {
553 io_bail!("cannot open file, reader provided no offset")
554 }
555 EntryKind::File {
556 size,
557 offset: Some(offset),
6b9e2478
WB
558 } => Ok(Some(offset..(offset + size))),
559 _ => Ok(None),
560 }
561 }
562
563 pub async fn contents(&self) -> io::Result<FileContentsImpl<T>> {
564 match self.content_range()? {
565 Some(range) => Ok(FileContentsImpl::new(self.input.clone(), range)),
566 None => io_bail!("not a file"),
98b894a9
WB
567 }
568 }
569
6cd4f635
WB
570 #[inline]
571 pub fn into_entry(self) -> Entry {
572 self.entry
573 }
574
575 #[inline]
576 pub fn entry(&self) -> &Entry {
577 &self.entry
578 }
ceb83806
WB
579
580 /// Exposed for raw by-offset access methods (use with `open_dir_at_end`).
581 #[inline]
582 pub fn entry_range(&self) -> Range<u64> {
583 self.entry_range.clone()
584 }
6cd4f635
WB
585}
586
587/// An iterator over the contents of a directory.
5cf335be 588pub(crate) struct ReadDirImpl<'a, T> {
29c17fc0 589 dir: &'a DirectoryImpl<T>,
6cd4f635
WB
590 at: usize,
591}
592
29c17fc0 593impl<'a, T: Clone + ReadAt> ReadDirImpl<'a, T> {
5cf335be 594 fn new(dir: &'a DirectoryImpl<T>, at: usize) -> Self {
6cd4f635
WB
595 Self { dir, at }
596 }
597
98b894a9 598 /// Get the next entry.
29c17fc0 599 pub async fn next(&mut self) -> io::Result<Option<DirEntryImpl<'a, T>>> {
6cd4f635
WB
600 if self.at == self.dir.table.len() {
601 Ok(None)
602 } else {
603 let cursor = self.dir.get_cursor(self.at).await?;
604 self.at += 1;
605 Ok(Some(cursor))
606 }
607 }
98b894a9
WB
608
609 /// Efficient alternative to `Iterator::skip`.
610 #[inline]
611 pub fn skip(self, n: usize) -> Self {
612 Self {
613 at: (self.at + n).min(self.dir.table.len()),
614 dir: self.dir,
615 }
616 }
617
618 /// Efficient alternative to `Iterator::count`.
619 #[inline]
620 pub fn count(self) -> usize {
621 self.dir.table.len()
622 }
6cd4f635
WB
623}
624
625/// A cursor pointing to a file in a directory.
626///
627/// At this point only the file name has been read and we remembered the position for finding the
628/// actual data. This can be upgraded into a FileEntryImpl.
5cf335be 629pub(crate) struct DirEntryImpl<'a, T: Clone + ReadAt> {
29c17fc0 630 dir: &'a DirectoryImpl<T>,
6cd4f635
WB
631 file_name: PathBuf,
632 entry_range: Range<u64>,
9d8af6f2 633 caches: Arc<Caches>,
6cd4f635
WB
634}
635
29c17fc0 636impl<'a, T: Clone + ReadAt> DirEntryImpl<'a, T> {
6cd4f635
WB
637 pub fn file_name(&self) -> &Path {
638 &self.file_name
639 }
640
aabb78a4 641 async fn decode_entry(&self) -> io::Result<FileEntryImpl<T>> {
c76d3f98 642 let (entry, _decoder) = self
6cd4f635
WB
643 .dir
644 .decode_one_entry(self.entry_range.clone(), Some(&self.file_name))
645 .await?;
6cd4f635
WB
646
647 Ok(FileEntryImpl {
29c17fc0 648 input: self.dir.input.clone(),
6cd4f635 649 entry,
ceb83806 650 entry_range: self.entry_range(),
9d8af6f2 651 caches: Arc::clone(&self.caches),
6cd4f635
WB
652 })
653 }
ceb83806
WB
654
655 /// Exposed for raw by-offset access methods.
656 #[inline]
657 pub fn entry_range(&self) -> Range<u64> {
658 self.entry_range.clone()
659 }
6cd4f635
WB
660}
661
98b894a9 662/// A reader for file contents.
5cf335be 663pub(crate) struct FileContentsImpl<T> {
98b894a9
WB
664 input: T,
665
666 /// Absolute offset inside the `input`.
667 range: Range<u64>,
668}
669
670impl<T: Clone + ReadAt> FileContentsImpl<T> {
671 pub fn new(input: T, range: Range<u64>) -> Self {
672 Self { input, range }
673 }
674
675 #[inline]
676 pub fn file_size(&self) -> u64 {
677 self.range.end - self.range.start
678 }
679
680 async fn read_at(&self, mut buf: &mut [u8], offset: u64) -> io::Result<usize> {
681 let size = self.file_size();
682 if offset >= size {
683 return Ok(0);
684 }
685 let remaining = size - offset;
686
687 if remaining < buf.len() as u64 {
688 buf = &mut buf[..(remaining as usize)];
689 }
690
c76d3f98
WB
691 (&self.input as &dyn ReadAt)
692 .read_at(buf, self.range.start + offset)
693 .await
98b894a9
WB
694 }
695}
696
d3a83ee3
WB
697impl<T: Clone + ReadAt> ReadAt for FileContentsImpl<T> {
698 fn poll_read_at(
699 self: Pin<&Self>,
700 cx: &mut Context,
701 mut buf: &mut [u8],
702 offset: u64,
703 ) -> Poll<io::Result<usize>> {
704 let size = self.file_size();
705 if offset >= size {
706 return Poll::Ready(Ok(0));
707 }
708 let remaining = size - offset;
709
710 if remaining < buf.len() as u64 {
711 buf = &mut buf[..(remaining as usize)];
712 }
713
714 let offset = self.range.start + offset;
715 unsafe { self.map_unchecked(|this| &this.input) }.poll_read_at(cx, buf, offset)
716 }
717}
718
6cd4f635 719#[doc(hidden)]
29c17fc0
WB
720pub struct SeqReadAtAdapter<T> {
721 input: T,
6cd4f635
WB
722 range: Range<u64>,
723}
724
29c17fc0
WB
725impl<T: ReadAt> SeqReadAtAdapter<T> {
726 pub fn new(input: T, range: Range<u64>) -> Self {
70acf637
WB
727 if range.end < range.start {
728 panic!("BAD SEQ READ AT ADAPTER");
729 }
6cd4f635
WB
730 Self { input, range }
731 }
732
733 #[inline]
734 fn remaining(&self) -> usize {
735 (self.range.end - self.range.start) as usize
736 }
737}
738
29c17fc0 739impl<T: ReadAt> decoder::SeqRead for SeqReadAtAdapter<T> {
6cd4f635
WB
740 fn poll_seq_read(
741 self: Pin<&mut Self>,
742 cx: &mut Context,
743 buf: &mut [u8],
744 ) -> Poll<io::Result<usize>> {
745 let len = buf.len().min(self.remaining());
746 let buf = &mut buf[..len];
747
29c17fc0 748 let this = unsafe { self.get_unchecked_mut() };
6cd4f635
WB
749
750 let got = ready!(unsafe {
29c17fc0 751 Pin::new_unchecked(&this.input).poll_read_at(cx, buf, this.range.start)
6cd4f635
WB
752 })?;
753 this.range.start += got as u64;
754 Poll::Ready(Ok(got))
755 }
756
757 fn poll_position(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Option<io::Result<u64>>> {
758 Poll::Ready(Some(Ok(self.range.start)))
759 }
760}