peridot_archive/
read.rs

1use core::mem::MaybeUninit;
2use std::{
3    convert::TryFrom,
4    io::{Error as IOError, IoSliceMut, Read, Result as IOResult},
5};
6
7use crate::{
8    AssetEntryHeadingPair, CompressionMethod, ContentFlags, entry::AssetNameRef,
9    entry_tree::EntryTreePointer,
10};
11use crc::crc32;
12use libflate::deflate as zlib;
13use peridot_native_io::{
14    MemoryMapBlob, PlatformNativeFileReader, PlatformNativeFileReaderAsync, RandomReadBlob,
15    RandomReadBlobAsync,
16};
17use peridot_serialization_utils::{VariableUInt, VariableULong};
18use pin_project::pin_project;
19
20#[non_exhaustive]
21#[derive(Debug)]
22pub enum ArchiveReadError {
23    IO(IOError),
24    IntegrityCheckFailed,
25    SignatureMismatch,
26    Lz4DecompressError(lz4_compression::decompress::Error),
27}
28impl From<IOError> for ArchiveReadError {
29    fn from(e: IOError) -> Self {
30        Self::IO(e)
31    }
32}
33impl From<lz4_compression::decompress::Error> for ArchiveReadError {
34    fn from(e: lz4_compression::decompress::Error) -> Self {
35        Self::Lz4DecompressError(e)
36    }
37}
38pub type ArchiveReadResult<T> = Result<T, ArchiveReadError>;
39impl From<ArchiveReadError> for IOError {
40    fn from(e: ArchiveReadError) -> Self {
41        match e {
42            ArchiveReadError::IO(e) => e,
43            ArchiveReadError::IntegrityCheckFailed => {
44                IOError::other("Archive Integrity check failed")
45            }
46            ArchiveReadError::SignatureMismatch => IOError::other("Archive Signature Mismatch"),
47            ArchiveReadError::Lz4DecompressError(e) => {
48                IOError::other(format!("Lz4DecompressError: {e:?}"))
49            }
50        }
51    }
52}
53
54fn list_entry(
55    head_size: usize,
56    hash_tree_root_exact: bool,
57    hash_tree_block: &[u8],
58    exact_match_block: &[u8],
59    mut callback: impl FnMut(AssetNameRef),
60) {
61    fn enumerate_exact_block_content(
62        block: &[u8],
63        pointer: usize,
64        callback: &mut impl FnMut(AssetNameRef),
65    ) {
66        let (VariableUInt(entry_count), entry_count_len) =
67            VariableUInt::from_bytes_head(&block[pointer..]);
68        let mut read_ptr = pointer + entry_count_len;
69        for _ in 0..entry_count {
70            let (VariableULong(n), name_bytes) = VariableULong::from_bytes_head(&block[read_ptr..]);
71            read_ptr += name_bytes;
72            let name_ext = &block[read_ptr..read_ptr + n as usize];
73            read_ptr += n as usize;
74            let (_, hlen) = AssetEntryHeadingPair::from_bytes_head(&block[read_ptr..]);
75            read_ptr += hlen;
76
77            let name_split = name_ext
78                .iter()
79                .position(|&x| x == 0)
80                .unwrap_or(name_ext.len());
81            let n = AssetNameRef {
82                name: unsafe { core::str::from_utf8_unchecked(&name_ext[..name_split]) },
83                ext: if name_split >= name_ext.len() - 1 {
84                    // no ext
85                    ""
86                } else {
87                    unsafe { core::str::from_utf8_unchecked(&name_ext[name_split + 1..]) }
88                },
89            };
90
91            callback(n);
92        }
93    }
94
95    fn enumerate_exact_tree<'b>(
96        block_view: &(impl crate::entry_tree::ExactBlockViewOps<'b> + ?Sized),
97        exact_match_block: &[u8],
98        callback: &mut impl FnMut(AssetNameRef),
99    ) {
100        for ptr in 0..block_view.entry_count() {
101            enumerate_exact_block_content(
102                exact_match_block,
103                block_view.entry(ptr).exact_block_offset() as _,
104                callback,
105            );
106        }
107    }
108
109    fn enumerate_tree(
110        block_view: &crate::entry_tree::BlockView,
111        hash_tree_block: &[u8],
112        exact_match_block: &[u8],
113        callback: &mut impl FnMut(AssetNameRef),
114    ) {
115        for ptr in 0..block_view.entry_count() {
116            let e = block_view.entry(ptr);
117            enumerate_subtree(
118                hash_tree_block,
119                exact_match_block,
120                callback,
121                e.smaller_tree_pointer(),
122            );
123            enumerate_exact_block_content(exact_match_block, e.exact_block_offset() as _, callback);
124        }
125
126        enumerate_subtree(
127            hash_tree_block,
128            exact_match_block,
129            callback,
130            block_view.larger_tree_pointer(),
131        );
132    }
133
134    fn enumerate_subtree(
135        hash_tree_block: &[u8],
136        exact_match_block: &[u8],
137        callback: &mut impl FnMut(AssetNameRef),
138        tree_pointer: EntryTreePointer,
139    ) {
140        if tree_pointer.is_exact_tree() {
141            // Exact Tree
142            enumerate_exact_tree(
143                &crate::entry_tree::ExactBlockView(
144                    &hash_tree_block[tree_pointer.pointer_value() as usize..],
145                ),
146                exact_match_block,
147                callback,
148            );
149
150            return;
151        }
152
153        // normal tree
154        enumerate_tree(
155            &crate::entry_tree::BlockView::from_offset_and_element_count(
156                hash_tree_block,
157                tree_pointer.pointer_value() as _,
158                crate::entry_tree::MAX_ENTRY_COUNT,
159            ),
160            hash_tree_block,
161            exact_match_block,
162            callback,
163        );
164    }
165
166    if hash_tree_root_exact {
167        // Exact tree only
168        enumerate_exact_tree(
169            &crate::entry_tree::ExactRootBlockView(hash_tree_block),
170            exact_match_block,
171            &mut callback,
172        );
173
174        return;
175    }
176
177    enumerate_tree(
178        &crate::entry_tree::BlockView(
179            &hash_tree_block[..crate::entry_tree::trim_normal_tree_block_size(
180                crate::entry_tree::first_hash_tree_block_size(head_size),
181            )],
182        ),
183        hash_tree_block,
184        exact_match_block,
185        &mut callback,
186    );
187}
188
189fn find_entry(
190    head_size: usize,
191    name: &str,
192    ext: &str,
193    hash_tree_root_exact: bool,
194    hash_tree_block: &[u8],
195    exact_match_block: &[u8],
196) -> Option<AssetEntryHeadingPair> {
197    let name = AssetNameRef { name, ext };
198    let name_hash = name.hash();
199
200    fn find_exact<'b>(
201        block_view: &(impl crate::entry_tree::ExactBlockViewOps<'b> + ?Sized),
202        target: u64,
203    ) -> Option<u64> {
204        let (mut top, mut bottom) = (0, block_view.entry_count());
205        loop {
206            let ptr = (top + bottom) / 2;
207            let e = block_view.entry(ptr);
208
209            match target.cmp(&e.name_hash()) {
210                // match
211                core::cmp::Ordering::Equal => return Some(e.exact_block_offset()),
212                core::cmp::Ordering::Less => {
213                    // bottom: exclusive
214                    bottom = ptr;
215                }
216                core::cmp::Ordering::Greater => {
217                    top = ptr + 1;
218                }
219            }
220
221            if bottom <= top {
222                return None;
223            }
224        }
225    }
226
227    fn find(
228        block_view: &crate::entry_tree::BlockView,
229        target: u64,
230    ) -> Result<u64, EntryTreePointer> {
231        // edge check
232        let e = block_view.largest_entry();
233        match target.cmp(&e.name_hash()) {
234            // exact largest
235            core::cmp::Ordering::Equal => return Ok(e.exact_block_offset()),
236            // more greater
237            core::cmp::Ordering::Greater => return Err(block_view.larger_tree_pointer()),
238            core::cmp::Ordering::Less => (/* nop */),
239        }
240
241        let e = block_view.smallest_entry();
242        match target.cmp(&e.name_hash()) {
243            // exact smallest
244            core::cmp::Ordering::Equal => return Ok(e.exact_block_offset()),
245            // more smaller
246            core::cmp::Ordering::Less => return Err(e.smaller_tree_pointer()),
247            core::cmp::Ordering::Greater => (/* nop */),
248        }
249
250        // binary search
251        let (mut top, mut bottom) = (0, block_view.entry_count());
252        loop {
253            let ptr = (top + bottom) / 2;
254            let e = block_view.entry(ptr);
255
256            match target.cmp(&e.name_hash()) {
257                // match
258                core::cmp::Ordering::Equal => return Ok(e.exact_block_offset()),
259                core::cmp::Ordering::Less => {
260                    // bottom: exclusive
261                    bottom = ptr;
262                }
263                core::cmp::Ordering::Greater => {
264                    top = ptr + 1;
265                }
266            }
267
268            if bottom <= top {
269                // search under here
270                return Err(e.smaller_tree_pointer());
271            }
272        }
273    }
274
275    let exact_block_offset = 'hash_tree_finder: {
276        if hash_tree_root_exact {
277            // Exact Root Tree
278            break 'hash_tree_finder find_exact(
279                &crate::entry_tree::ExactRootBlockView(hash_tree_block),
280                name_hash,
281            );
282        }
283
284        fn find_subtree(
285            name_hash: u64,
286            hash_tree_block: &[u8],
287            tree_pointer: EntryTreePointer,
288        ) -> Option<u64> {
289            if tree_pointer.is_exact_tree() {
290                // Exact Tree
291                return find_exact(
292                    &crate::entry_tree::ExactBlockView(
293                        &hash_tree_block[tree_pointer.pointer_value() as usize..],
294                    ),
295                    name_hash,
296                );
297            }
298
299            // normal tree
300            let block_view = crate::entry_tree::BlockView::from_offset_and_element_count(
301                hash_tree_block,
302                tree_pointer.pointer_value() as _,
303                crate::entry_tree::MAX_ENTRY_COUNT,
304            );
305
306            match find(&block_view, name_hash) {
307                Ok(x) => Some(x),
308                Err(p) => find_subtree(name_hash, hash_tree_block, p),
309            }
310        }
311
312        let block_view = crate::entry_tree::BlockView(
313            &hash_tree_block[..crate::entry_tree::trim_normal_tree_block_size(
314                crate::entry_tree::first_hash_tree_block_size(head_size),
315            )],
316        );
317
318        match find(&block_view, name_hash) {
319            Ok(x) => Some(x),
320            Err(p) => find_subtree(name_hash, hash_tree_block, p),
321        }
322    }?;
323
324    // linear search conflicting hash bucket
325    let (VariableUInt(exact_entry_count), exact_entry_offset) =
326        VariableUInt::from_bytes_head(&exact_match_block[exact_block_offset as usize..]);
327    let mut read_ptr = exact_block_offset as usize + exact_entry_offset;
328    for _ in 0..exact_entry_count {
329        let (VariableULong(n), name_bytes) =
330            VariableULong::from_bytes_head(&exact_match_block[read_ptr..]);
331        read_ptr += name_bytes;
332        let name_ext = &exact_match_block[read_ptr..read_ptr + n as usize];
333        read_ptr += n as usize;
334        let (h, hlen) = AssetEntryHeadingPair::from_bytes_head(&exact_match_block[read_ptr..]);
335        read_ptr += hlen;
336
337        let name_split = name_ext
338            .iter()
339            .position(|&x| x == 0)
340            .unwrap_or(name_ext.len());
341        let n = AssetNameRef {
342            name: unsafe { core::str::from_utf8_unchecked(&name_ext[..name_split]) },
343            ext: if name_split >= name_ext.len() - 1 {
344                // no ext
345                ""
346            } else {
347                unsafe { core::str::from_utf8_unchecked(&name_ext[name_split + 1..]) }
348            },
349        };
350
351        if n == name {
352            // match!
353            return Some(h);
354        }
355    }
356
357    // no match in exact name list
358    None
359}
360
361pub struct OnMemoryArchiveBinReadFuture<'a, 'b, 'aa> {
362    reader: &'a OnMemoryArchiveBinReader<'aa>,
363    pos: u64,
364    buf: &'b mut [MaybeUninit<u8>],
365}
366impl<'a, 'b, 'aa> Future for OnMemoryArchiveBinReadFuture<'a, 'b, 'aa> {
367    type Output = std::io::Result<usize>;
368
369    #[inline(always)]
370    fn poll(
371        self: std::pin::Pin<&mut Self>,
372        _cx: &mut std::task::Context<'_>,
373    ) -> std::task::Poll<Self::Output> {
374        let this = self.get_mut();
375
376        std::task::Poll::Ready(this.reader.read_at(this.pos, this.buf))
377    }
378}
379
380pub struct OnMemoryArchiveBinReadVecFuture<'a, 'b, 'aa, 'bb> {
381    reader: &'a OnMemoryArchiveBinReader<'aa>,
382    pos: u64,
383    buf: &'b mut [IoSliceMut<'bb>],
384}
385impl<'a, 'b, 'aa, 'bb> Future for OnMemoryArchiveBinReadVecFuture<'a, 'b, 'aa, 'bb> {
386    type Output = std::io::Result<usize>;
387
388    #[inline(always)]
389    fn poll(
390        self: std::pin::Pin<&mut Self>,
391        _cx: &mut std::task::Context<'_>,
392    ) -> std::task::Poll<Self::Output> {
393        let this = self.get_mut();
394
395        std::task::Poll::Ready(this.reader.readv_at(this.pos, this.buf))
396    }
397}
398
399pub struct OnMemoryArchiveBinReader<'a> {
400    pub archive: &'a OnMemoryArchive,
401    pub pointer_base: u64,
402    pub pointer: u64,
403    pub pointer_limit: u64,
404}
405impl OnMemoryArchiveBinReader<'_> {
406    fn read_at(&self, pos: u64, buf: &mut [MaybeUninit<u8>]) -> std::io::Result<usize> {
407        let pos_abs = self
408            .pointer_base
409            .checked_add(pos)
410            .ok_or(std::io::ErrorKind::InvalidInput)?;
411        let left_available = self
412            .pointer_limit
413            .checked_sub(pos_abs)
414            .and_then(|x| usize::try_from(x).ok())
415            .ok_or(std::io::ErrorKind::InvalidInput)?;
416        let archive_buf_offset =
417            usize::try_from(pos_abs).map_err(|_| std::io::ErrorKind::InvalidInput)?;
418
419        let read_len = buf.len().min(left_available);
420        if read_len > 0 {
421            unsafe {
422                core::ptr::copy_nonoverlapping(
423                    self.archive.block.as_ptr().add(archive_buf_offset),
424                    buf.as_mut_ptr().cast::<u8>(),
425                    read_len,
426                );
427            }
428        }
429
430        Ok(read_len)
431    }
432
433    fn readv_at(&self, pos: u64, buf: &mut [IoSliceMut]) -> std::io::Result<usize> {
434        let pos_abs = self
435            .pointer_base
436            .checked_add(pos)
437            .ok_or(std::io::ErrorKind::InvalidInput)?;
438        let archive_buf_offset =
439            usize::try_from(pos_abs).map_err(|_| std::io::ErrorKind::InvalidInput)?;
440
441        std::io::Cursor::new(&self.archive.block[archive_buf_offset..]).read_vectored(buf)
442    }
443}
444impl std::io::Read for OnMemoryArchiveBinReader<'_> {
445    fn read(&mut self, buf: &mut [u8]) -> IOResult<usize> {
446        let read_len = buf.len().min((self.pointer_limit - self.pointer) as usize);
447        buf[..read_len].copy_from_slice(
448            &self.archive.block[self.pointer as usize..self.pointer as usize + read_len],
449        );
450        self.pointer += read_len as u64;
451
452        Ok(read_len)
453    }
454}
455impl std::io::Seek for OnMemoryArchiveBinReader<'_> {
456    fn seek(&mut self, pos: std::io::SeekFrom) -> IOResult<u64> {
457        self.pointer = match pos {
458            std::io::SeekFrom::Current(x) => (self.pointer as i64 + x) as _,
459            std::io::SeekFrom::Start(x) => self.pointer_base + x,
460            std::io::SeekFrom::End(x) => (self.pointer_limit as i64 + x) as _,
461        };
462
463        Ok(self.pointer - self.pointer_base)
464    }
465}
466#[cfg(feature = "async-rt-async-std")]
467impl async_std::io::Read for OnMemoryArchiveBinReader<'_> {
468    fn poll_read(
469        self: std::pin::Pin<&mut Self>,
470        _cx: &mut std::task::Context<'_>,
471        buf: &mut [u8],
472    ) -> std::task::Poll<IOResult<usize>> {
473        let read_len = buf.len().min((self.pointer_limit - self.pointer) as usize);
474        buf[..read_len].copy_from_slice(
475            &self.archive.block[self.pointer as usize..self.pointer as usize + read_len],
476        );
477        self.get_mut().pointer += read_len as u64;
478
479        std::task::Poll::Ready(Ok(read_len))
480    }
481}
482
483pub struct OnMemoryArchive {
484    pub head_size: usize,
485    pub block: Vec<u8>,
486    pub content_flags: ContentFlags,
487    pub hash_tree_block_range: core::ops::Range<usize>,
488    pub exact_match_block_range: core::ops::Range<usize>,
489    pub content_baseptr: usize,
490}
491impl OnMemoryArchive {
492    fn new(compression: CompressionMethod, body: Vec<u8>) -> ArchiveReadResult<Self> {
493        let (body, head_size) = match compression {
494            CompressionMethod::Lz4(_) => (lz4_compression::prelude::decompress(&body)?, 4 + 8 + 4),
495            CompressionMethod::Zlib(ub) => {
496                let mut sink = Vec::with_capacity(ub as _);
497                zlib::Decoder::new(std::io::Cursor::new(body)).read_to_end(&mut sink)?;
498                (sink, 4 + 8 + 4)
499            }
500            CompressionMethod::Zstd11(ub) => {
501                let mut sink = Vec::with_capacity(ub as _);
502                zstd::Decoder::new(std::io::Cursor::new(body))?.read_to_end(&mut sink)?;
503                (sink, 4 + 8 + 4)
504            }
505            CompressionMethod::None => (body, 4 + 4),
506        };
507
508        let content_flags = ContentFlags::from_bits_retain(body[0]);
509        let hash_tree_block_len =
510            (u32::from_le_bytes(unsafe { TryFrom::try_from(&body[1..5]).unwrap_unchecked() })
511                as u64)
512                << 3;
513        let exact_match_block_len =
514            u64::from_le_bytes(unsafe { TryFrom::try_from(&body[5..13]).unwrap_unchecked() });
515
516        Ok(Self {
517            head_size,
518            block: body,
519            content_flags,
520            hash_tree_block_range: 13..(13 + hash_tree_block_len) as usize,
521            exact_match_block_range: (13 + hash_tree_block_len) as usize
522                ..(13 + hash_tree_block_len + exact_match_block_len) as usize,
523            content_baseptr: (13 + hash_tree_block_len + exact_match_block_len) as usize,
524        })
525    }
526
527    fn list_entry(&self, callback: impl FnMut(AssetNameRef)) {
528        list_entry(
529            self.head_size,
530            self.content_flags
531                .contains(ContentFlags::ROOT_HASH_TREE_EXACT),
532            &self.block[self.hash_tree_block_range.clone()],
533            &self.block[self.exact_match_block_range.clone()],
534            callback,
535        )
536    }
537
538    fn find_entry(&self, name: &str, ext: &str) -> Option<AssetEntryHeadingPair> {
539        find_entry(
540            self.head_size,
541            name,
542            ext,
543            self.content_flags
544                .contains(ContentFlags::ROOT_HASH_TREE_EXACT),
545            &self.block[self.hash_tree_block_range.clone()],
546            &self.block[self.exact_match_block_range.clone()],
547        )
548    }
549
550    fn read_bin<'a>(&'a self, heading: AssetEntryHeadingPair) -> OnMemoryArchiveBinReader<'a> {
551        OnMemoryArchiveBinReader {
552            archive: self,
553            pointer_base: self.content_baseptr as u64 + heading.relative_offset,
554            pointer: self.content_baseptr as u64 + heading.relative_offset,
555            pointer_limit: self.content_baseptr as u64
556                + heading.relative_offset
557                + heading.byte_length,
558        }
559    }
560}
561
562pub struct FileStreamingArchiveBinReader<'a, R: RandomReadBlob + MemoryMapBlob> {
563    archive: &'a FileStreamingArchive<R>,
564    pointer_base: u64,
565    pointer: u64,
566    pointer_limit: u64,
567}
568impl<R: RandomReadBlob + MemoryMapBlob> FileStreamingArchiveBinReader<'_, R> {
569    fn read_at(&self, pos: u64, buf: &mut [MaybeUninit<u8>]) -> std::io::Result<usize> {
570        let pos_abs = self
571            .pointer_base
572            .checked_add(pos)
573            .ok_or(std::io::ErrorKind::InvalidInput)?
574            .min(self.pointer_limit);
575        let max_readable = usize::try_from(self.pointer_limit - pos_abs)
576            .unwrap_or(buf.len())
577            .min(buf.len());
578
579        self.archive.handle.read(pos_abs, &mut buf[..max_readable])
580    }
581
582    fn readv_at(&self, pos: u64, mut buf: &mut [IoSliceMut]) -> std::io::Result<usize> {
583        let pos_abs = self
584            .pointer_base
585            .checked_add(pos)
586            .ok_or(std::io::ErrorKind::InvalidInput)?
587            .min(self.pointer_limit);
588
589        if let Some(mut max_readable) = usize::try_from(self.pointer_limit - pos_abs).ok() {
590            // truncate iovecs for max_readable
591            let mut avail_count = 0;
592            while avail_count < buf.len() && max_readable > 0 {
593                if let Some(rem) = max_readable.checked_sub(buf[avail_count].len()) {
594                    // take full
595                    max_readable = rem;
596                    avail_count += 1;
597                    continue;
598                }
599
600                // final
601                buf[avail_count] = IoSliceMut::new(unsafe {
602                    core::slice::from_raw_parts_mut(buf[avail_count].as_mut_ptr(), max_readable)
603                });
604                avail_count += 1;
605                break;
606            }
607            buf = &mut buf[..avail_count];
608        }
609
610        self.archive.handle.readv(pos_abs, buf)
611    }
612}
613impl<R: RandomReadBlob + MemoryMapBlob> std::io::Read for FileStreamingArchiveBinReader<'_, R> {
614    #[inline]
615    fn read(&mut self, buf: &mut [u8]) -> IOResult<usize> {
616        let read_len = (buf.len() as u64).min(self.pointer_limit - self.pointer);
617        let r = self.archive.handle.read(self.pointer, unsafe {
618            core::mem::transmute::<&mut [u8], &mut [MaybeUninit<u8>]>(&mut buf[..read_len as _])
619        })?;
620        self.pointer += r as u64;
621
622        Ok(r as _)
623    }
624}
625impl<R: RandomReadBlob + MemoryMapBlob> std::io::Seek for FileStreamingArchiveBinReader<'_, R> {
626    #[inline]
627    fn seek(&mut self, pos: std::io::SeekFrom) -> IOResult<u64> {
628        self.pointer = match pos {
629            std::io::SeekFrom::Current(x) => (self.pointer as i64 + x) as _,
630            std::io::SeekFrom::Start(x) => self.pointer_base + x,
631            std::io::SeekFrom::End(x) => (self.pointer_limit as i64 + x) as _,
632        };
633
634        Ok(self.pointer - self.pointer_base)
635    }
636}
637
638#[pin_project(project = FileStreamingArchiveBinReadFutureStateP)]
639enum FileStreamingArchiveBinReadFutureState<F> {
640    Idle,
641    Pending(#[pin] F),
642}
643
644#[pin_project]
645pub struct FileStreamingArchiveBinReadFuture<
646    'a,
647    'aa,
648    'b,
649    R: RandomReadBlobAsync + MemoryMapBlob + 'aa,
650> {
651    reader: &'a FileStreamingArchiveBinReaderAsync<'aa, R>,
652    pos: u64,
653    buf: &'b mut [MaybeUninit<u8>],
654    #[pin]
655    state: FileStreamingArchiveBinReadFutureState<R::ReadFuture<'aa, 'b>>,
656}
657impl<'a, 'aa, 'b, R: RandomReadBlobAsync + MemoryMapBlob> Future
658    for FileStreamingArchiveBinReadFuture<'a, 'aa, 'b, R>
659{
660    type Output = std::io::Result<usize>;
661
662    fn poll(
663        self: std::pin::Pin<&mut Self>,
664        cx: &mut std::task::Context<'_>,
665    ) -> std::task::Poll<Self::Output> {
666        let mut this = self.project();
667
668        loop {
669            match this.state.as_mut().project() {
670                FileStreamingArchiveBinReadFutureStateP::Idle => {
671                    let pos_abs = this
672                        .reader
673                        .pointer_base
674                        .checked_add(*this.pos)
675                        .ok_or(std::io::ErrorKind::InvalidInput)?
676                        .min(this.reader.pointer_limit);
677                    let max_readable = usize::try_from(this.reader.pointer_limit - pos_abs)
678                        .unwrap_or(this.buf.len())
679                        .min(this.buf.len());
680                    println!("read async {pos_abs} {max_readable}");
681
682                    this.state
683                        .set(FileStreamingArchiveBinReadFutureState::Pending(
684                            this.reader.archive.handle.read_async(pos_abs, unsafe {
685                                core::mem::transmute(&mut this.buf[..max_readable])
686                            }),
687                        ));
688                }
689                FileStreamingArchiveBinReadFutureStateP::Pending(f) => {
690                    let r = core::task::ready!(f.poll(cx));
691                    this.state.set(FileStreamingArchiveBinReadFutureState::Idle);
692                    return core::task::Poll::Ready(r);
693                }
694            }
695        }
696    }
697}
698
699#[pin_project]
700pub struct FileStreamingArchiveBinReadVecFuture<
701    'a,
702    'aa,
703    'b,
704    'bb,
705    R: RandomReadBlobAsync + MemoryMapBlob + 'aa,
706> where
707    'bb: 'b,
708{
709    reader: &'a FileStreamingArchiveBinReaderAsync<'aa, R>,
710    pos: u64,
711    buf: &'b mut [IoSliceMut<'bb>],
712    #[pin]
713    state: FileStreamingArchiveBinReadFutureState<R::ReadVecFuture<'aa, 'b, 'bb>>,
714}
715impl<'a, 'aa, 'b, 'bb, R: RandomReadBlobAsync + MemoryMapBlob> Future
716    for FileStreamingArchiveBinReadVecFuture<'a, 'aa, 'b, 'bb, R>
717where
718    'bb: 'b,
719{
720    type Output = std::io::Result<usize>;
721
722    fn poll(
723        self: std::pin::Pin<&mut Self>,
724        cx: &mut std::task::Context<'_>,
725    ) -> std::task::Poll<Self::Output> {
726        let mut this = self.project();
727
728        loop {
729            match this.state.as_mut().project() {
730                FileStreamingArchiveBinReadFutureStateP::Idle => {
731                    let pos_abs = this
732                        .reader
733                        .pointer_base
734                        .checked_add(*this.pos)
735                        .ok_or(std::io::ErrorKind::InvalidInput)?
736                        .min(this.reader.pointer_limit);
737
738                    let mut buf = &mut this.buf[..];
739                    if let Some(mut max_readable) =
740                        usize::try_from(this.reader.pointer_limit - pos_abs).ok()
741                    {
742                        // truncate iovecs for max_readable
743                        let mut avail_count = 0;
744                        while avail_count < buf.len() && max_readable > 0 {
745                            if let Some(rem) = max_readable.checked_sub(buf[avail_count].len()) {
746                                // take full
747                                max_readable = rem;
748                                avail_count += 1;
749                                continue;
750                            }
751
752                            // final
753                            buf[avail_count] = IoSliceMut::new(unsafe {
754                                core::slice::from_raw_parts_mut(
755                                    buf[avail_count].as_mut_ptr(),
756                                    max_readable,
757                                )
758                            });
759                            avail_count += 1;
760                            break;
761                        }
762                        buf = &mut buf[..avail_count];
763                    }
764
765                    this.state
766                        .set(FileStreamingArchiveBinReadFutureState::Pending(
767                            this.reader
768                                .archive
769                                .handle
770                                .readv_async(pos_abs, unsafe { core::mem::transmute(buf) }),
771                        ));
772                }
773                FileStreamingArchiveBinReadFutureStateP::Pending(f) => {
774                    let r = core::task::ready!(f.poll(cx));
775                    this.state.set(FileStreamingArchiveBinReadFutureState::Idle);
776                    return core::task::Poll::Ready(r);
777                }
778            }
779        }
780    }
781}
782
783pub struct FileStreamingArchiveBinReaderAsync<'a, R: RandomReadBlobAsync + MemoryMapBlob> {
784    archive: &'a FileStreamingArchiveAsync<R>,
785    pointer_base: u64,
786    pointer: u64,
787    pointer_limit: u64,
788}
789impl<'a, R: RandomReadBlobAsync + MemoryMapBlob> FileStreamingArchiveBinReaderAsync<'a, R> {
790    pub async fn read<'b>(&mut self, buf: &'b mut [u8]) -> IOResult<usize> {
791        let read_len = (buf.len() as u64).min(self.pointer_limit - self.pointer);
792        let bytes = self
793            .archive
794            .handle
795            .read_async(self.pointer, unsafe {
796                core::mem::transmute::<&mut [u8], &mut [MaybeUninit<u8>]>(
797                    &mut buf[..read_len as usize],
798                )
799            })
800            .await?;
801        self.pointer += bytes as u64;
802
803        Ok(bytes)
804    }
805
806    pub async fn read_exact(&mut self, mut buf: &mut [u8]) -> IOResult<()> {
807        while !buf.is_empty() {
808            let r = self.read(buf).await?;
809            buf = &mut buf[r..];
810        }
811
812        Ok(())
813    }
814
815    pub async fn read_all(&mut self) -> IOResult<Vec<u8>> {
816        let mut b = vec![0u8; (self.pointer_limit - self.pointer) as usize];
817        self.read_exact(&mut b).await?;
818
819        Ok(b)
820    }
821
822    #[inline]
823    pub async fn seek(&mut self, pos: std::io::SeekFrom) -> IOResult<u64> {
824        self.pointer = match pos {
825            std::io::SeekFrom::Current(x) => (self.pointer as i64 + x) as _,
826            std::io::SeekFrom::Start(x) => self.pointer_base + x,
827            std::io::SeekFrom::End(x) => (self.pointer_limit as i64 + x) as _,
828        };
829
830        Ok(self.pointer - self.pointer_base)
831    }
832}
833
834pub struct FileStreamingArchiveAsync<R: RandomReadBlobAsync + MemoryMapBlob> {
835    pub handle: R,
836    pub entry_mapped_head: core::sync::atomic::AtomicPtr<core::ffi::c_void>,
837    pub entry_unmap_data: Option<R::MemoryUnmapData>,
838    pub content_flags: ContentFlags,
839    pub hash_tree_block_range: core::ops::Range<usize>,
840    pub exact_match_block_range: core::ops::Range<usize>,
841    pub content_baseptr: u64,
842}
843impl<R: RandomReadBlobAsync + MemoryMapBlob> FileStreamingArchiveAsync<R> {
844    async fn new(handle: R, body_start: u64) -> IOResult<Self> {
845        let mut content_flags_buf = [0u8];
846        let mut hash_tree_block_len_buf = [0u8; 4];
847        let mut exact_match_block_len_buf = [0u8; 8];
848
849        let mut ro = body_start;
850        handle
851            .readv_all_async(
852                ro,
853                &mut [
854                    IoSliceMut::new(&mut content_flags_buf),
855                    IoSliceMut::new(&mut hash_tree_block_len_buf),
856                    IoSliceMut::new(&mut exact_match_block_len_buf),
857                ],
858            )
859            .await?;
860        ro += 1 + 4 + 8;
861
862        let content_flags = ContentFlags::from_bits_retain(content_flags_buf[0]);
863        let hash_tree_block_len = (u32::from_le_bytes(hash_tree_block_len_buf) as u64) << 3;
864        let exact_match_block_len = u64::from_le_bytes(exact_match_block_len_buf);
865
866        let entry_block_start_pos = ro;
867        let (entry_mapped_head, entry_unmap_data) = handle.mmap(
868            entry_block_start_pos,
869            (hash_tree_block_len + exact_match_block_len)
870                .try_into()
871                .expect("catalog block size is too large"),
872        )?;
873
874        Ok(Self {
875            handle,
876            entry_mapped_head: core::sync::atomic::AtomicPtr::new(entry_mapped_head),
877            entry_unmap_data: Some(entry_unmap_data),
878            content_flags,
879            hash_tree_block_range: 0..hash_tree_block_len as usize,
880            exact_match_block_range: hash_tree_block_len as usize
881                ..(hash_tree_block_len + exact_match_block_len) as usize,
882            content_baseptr: entry_block_start_pos + hash_tree_block_len + exact_match_block_len,
883        })
884    }
885
886    fn list_entry(&self, callback: impl FnMut(AssetNameRef)) {
887        let entry_ptr = self
888            .entry_mapped_head
889            .load(core::sync::atomic::Ordering::Acquire);
890
891        list_entry(
892            // FileStreamingのときは4+4固定になる(非圧縮でしかこれにならないので)
893            4 + 4,
894            self.content_flags
895                .contains(ContentFlags::ROOT_HASH_TREE_EXACT),
896            unsafe {
897                core::slice::from_raw_parts(
898                    entry_ptr.byte_add(self.hash_tree_block_range.start) as *const u8,
899                    self.hash_tree_block_range.len(),
900                )
901            },
902            unsafe {
903                core::slice::from_raw_parts(
904                    entry_ptr.byte_add(self.exact_match_block_range.start) as *const u8,
905                    self.exact_match_block_range.len(),
906                )
907            },
908            callback,
909        )
910    }
911
912    fn find_entry(&self, name: &str, ext: &str) -> Option<AssetEntryHeadingPair> {
913        let entry_ptr = self
914            .entry_mapped_head
915            .load(core::sync::atomic::Ordering::Acquire);
916
917        find_entry(
918            // FileStreamingのときは4+4固定になる(非圧縮でしかこれにならないので)
919            4 + 4,
920            name,
921            ext,
922            self.content_flags
923                .contains(ContentFlags::ROOT_HASH_TREE_EXACT),
924            unsafe {
925                core::slice::from_raw_parts(
926                    entry_ptr.byte_add(self.hash_tree_block_range.start) as *const u8,
927                    self.hash_tree_block_range.len(),
928                )
929            },
930            unsafe {
931                core::slice::from_raw_parts(
932                    entry_ptr.byte_add(self.exact_match_block_range.start) as *const u8,
933                    self.exact_match_block_range.len(),
934                )
935            },
936        )
937    }
938
939    fn read_bin<'a>(
940        &'a self,
941        heading: AssetEntryHeadingPair,
942    ) -> FileStreamingArchiveBinReaderAsync<'a, R> {
943        FileStreamingArchiveBinReaderAsync {
944            archive: self,
945            pointer_base: self.content_baseptr + heading.relative_offset,
946            pointer: self.content_baseptr + heading.relative_offset,
947            pointer_limit: self.content_baseptr + heading.relative_offset + heading.byte_length,
948        }
949    }
950}
951
952pub struct FileStreamingArchive<R: RandomReadBlob + MemoryMapBlob> {
953    pub handle: R,
954    pub entry_mapped_head: core::sync::atomic::AtomicPtr<core::ffi::c_void>,
955    pub entry_unmap_data: Option<R::MemoryUnmapData>,
956    pub content_flags: ContentFlags,
957    pub hash_tree_block_range: core::ops::Range<usize>,
958    pub exact_match_block_range: core::ops::Range<usize>,
959    pub content_baseptr: u64,
960}
961unsafe impl<R: RandomReadBlob + MemoryMapBlob + Send> Send for FileStreamingArchive<R> {}
962unsafe impl<R: RandomReadBlob + MemoryMapBlob + Sync> Sync for FileStreamingArchive<R> {}
963impl<R: RandomReadBlob + MemoryMapBlob> Drop for FileStreamingArchive<R> {
964    fn drop(&mut self) {
965        let _ = self
966            .handle
967            .munmap(self.entry_unmap_data.take().expect("drop twice!"));
968    }
969}
970impl<R: RandomReadBlob + MemoryMapBlob> FileStreamingArchive<R> {
971    fn new(handle: R, body_offset: u64) -> IOResult<Self> {
972        let mut content_flags_buf = [0u8];
973        let mut hash_tree_block_len_buf = [0u8; 4];
974        let mut exact_match_block_len_buf = [0u8; 8];
975
976        let mut ro = body_offset;
977        handle.readv_all(
978            ro,
979            &mut [
980                IoSliceMut::new(&mut content_flags_buf),
981                IoSliceMut::new(&mut hash_tree_block_len_buf),
982                IoSliceMut::new(&mut exact_match_block_len_buf),
983            ],
984        )?;
985        ro += 1 + 4 + 8;
986
987        let content_flags = ContentFlags::from_bits_retain(content_flags_buf[0]);
988        let hash_tree_block_len = (u32::from_le_bytes(hash_tree_block_len_buf) as u64) << 3;
989        let exact_match_block_len = u64::from_le_bytes(exact_match_block_len_buf);
990
991        let entry_block_start_pos = ro;
992        let (entry_mapped_head, entry_unmap_data) = handle.mmap(
993            dbg!(entry_block_start_pos),
994            (hash_tree_block_len + exact_match_block_len)
995                .try_into()
996                .expect("catalog block size is too large"),
997        )?;
998
999        Ok(Self {
1000            handle,
1001            entry_mapped_head: core::sync::atomic::AtomicPtr::new(entry_mapped_head),
1002            entry_unmap_data: Some(entry_unmap_data),
1003            content_flags,
1004            hash_tree_block_range: 0..hash_tree_block_len as usize,
1005            exact_match_block_range: hash_tree_block_len as usize
1006                ..(hash_tree_block_len + exact_match_block_len) as usize,
1007            content_baseptr: entry_block_start_pos + hash_tree_block_len + exact_match_block_len,
1008        })
1009    }
1010
1011    fn list_entry(&self, callback: impl FnMut(AssetNameRef)) {
1012        let entry_ptr = self
1013            .entry_mapped_head
1014            .load(core::sync::atomic::Ordering::Acquire);
1015
1016        list_entry(
1017            // FileStreamingのときは4+4固定になる(非圧縮でしかこれにならないので)
1018            4 + 4,
1019            self.content_flags
1020                .contains(ContentFlags::ROOT_HASH_TREE_EXACT),
1021            unsafe {
1022                core::slice::from_raw_parts(
1023                    entry_ptr.byte_add(self.hash_tree_block_range.start) as *const u8,
1024                    self.hash_tree_block_range.len(),
1025                )
1026            },
1027            unsafe {
1028                core::slice::from_raw_parts(
1029                    entry_ptr.byte_add(self.exact_match_block_range.start) as *const u8,
1030                    self.exact_match_block_range.len(),
1031                )
1032            },
1033            callback,
1034        )
1035    }
1036
1037    fn find_entry(&self, name: &str, ext: &str) -> Option<AssetEntryHeadingPair> {
1038        let entry_ptr = self
1039            .entry_mapped_head
1040            .load(core::sync::atomic::Ordering::Acquire);
1041
1042        find_entry(
1043            // FileStreamingのときは4+4固定になる(非圧縮でしかこれにならないので)
1044            4 + 4,
1045            name,
1046            ext,
1047            self.content_flags
1048                .contains(ContentFlags::ROOT_HASH_TREE_EXACT),
1049            unsafe {
1050                core::slice::from_raw_parts(
1051                    entry_ptr.byte_add(self.hash_tree_block_range.start) as *const u8,
1052                    self.hash_tree_block_range.len(),
1053                )
1054            },
1055            unsafe {
1056                core::slice::from_raw_parts(
1057                    entry_ptr.byte_add(self.exact_match_block_range.start) as *const u8,
1058                    self.exact_match_block_range.len(),
1059                )
1060            },
1061        )
1062    }
1063
1064    fn read_bin<'a>(
1065        &'a self,
1066        heading: AssetEntryHeadingPair,
1067    ) -> FileStreamingArchiveBinReader<'a, R> {
1068        FileStreamingArchiveBinReader {
1069            archive: self,
1070            pointer_base: self.content_baseptr + heading.relative_offset,
1071            pointer: self.content_baseptr + heading.relative_offset,
1072            pointer_limit: self.content_baseptr + heading.relative_offset + heading.byte_length,
1073        }
1074    }
1075}
1076
1077pub enum ArchiveBinReader<'a, R: RandomReadBlob + MemoryMapBlob> {
1078    OnMemory(OnMemoryArchiveBinReader<'a>),
1079    FileStreaming(FileStreamingArchiveBinReader<'a, R>),
1080}
1081impl<R: RandomReadBlob + MemoryMapBlob> peridot_native_io::BlobMetadata
1082    for ArchiveBinReader<'_, R>
1083{
1084    fn byte_length(&self) -> std::io::Result<u64> {
1085        match self {
1086            Self::OnMemory(r) => Ok(r.pointer_limit - r.pointer_base),
1087            Self::FileStreaming(r) => Ok(r.pointer_limit - r.pointer_base),
1088        }
1089    }
1090}
1091impl<R: RandomReadBlob + MemoryMapBlob> peridot_native_io::RandomReadBlob
1092    for ArchiveBinReader<'_, R>
1093{
1094    fn read(&self, pos: u64, buf: &mut [MaybeUninit<u8>]) -> std::io::Result<usize> {
1095        match self {
1096            Self::OnMemory(r) => r.read_at(pos, buf),
1097            Self::FileStreaming(r) => r.read_at(pos, buf),
1098        }
1099    }
1100
1101    fn readv(&self, offs: u64, iovecs: &mut [IoSliceMut]) -> std::io::Result<usize> {
1102        match self {
1103            Self::OnMemory(r) => r.readv_at(offs, iovecs),
1104            Self::FileStreaming(r) => r.readv_at(offs, iovecs),
1105        }
1106    }
1107}
1108impl<R: RandomReadBlob + MemoryMapBlob> std::io::Read for ArchiveBinReader<'_, R> {
1109    #[inline]
1110    fn read(&mut self, buf: &mut [u8]) -> IOResult<usize> {
1111        match *self {
1112            Self::OnMemory(ref mut x) => x.read(buf),
1113            Self::FileStreaming(ref mut x) => x.read(buf),
1114        }
1115    }
1116}
1117impl<R: RandomReadBlob + MemoryMapBlob> std::io::Seek for ArchiveBinReader<'_, R> {
1118    #[inline]
1119    fn seek(&mut self, pos: std::io::SeekFrom) -> IOResult<u64> {
1120        match *self {
1121            Self::OnMemory(ref mut x) => x.seek(pos),
1122            Self::FileStreaming(ref mut x) => x.seek(pos),
1123        }
1124    }
1125}
1126
1127#[pin_project(project = EnumDispatchFP)]
1128pub enum EnumDispatchF<OnMemoryF, FileStreamingF> {
1129    OnMemory(#[pin] OnMemoryF),
1130    FileStreaming(#[pin] FileStreamingF),
1131}
1132impl<OnMemoryF, FileStreamingF> Future for EnumDispatchF<OnMemoryF, FileStreamingF>
1133where
1134    OnMemoryF: Future,
1135    FileStreamingF: Future<Output = OnMemoryF::Output>,
1136{
1137    type Output = OnMemoryF::Output;
1138
1139    #[inline]
1140    fn poll(
1141        self: std::pin::Pin<&mut Self>,
1142        cx: &mut std::task::Context<'_>,
1143    ) -> std::task::Poll<Self::Output> {
1144        match self.project() {
1145            EnumDispatchFP::OnMemory(f) => f.poll(cx),
1146            EnumDispatchFP::FileStreaming(f) => f.poll(cx),
1147        }
1148    }
1149}
1150
1151pub enum ArchiveBinReaderAsync<'a, R: RandomReadBlobAsync + MemoryMapBlob> {
1152    OnMemory(OnMemoryArchiveBinReader<'a>),
1153    FileStreaming(FileStreamingArchiveBinReaderAsync<'a, R>),
1154}
1155impl<R: RandomReadBlobAsync + MemoryMapBlob> peridot_native_io::BlobMetadataAsync
1156    for ArchiveBinReaderAsync<'_, R>
1157{
1158    fn byte_length_async(&self) -> impl core::future::Future<Output = std::io::Result<u64>> {
1159        async move {
1160            match self {
1161                Self::OnMemory(r) => Ok(r.pointer_limit - r.pointer_base),
1162                Self::FileStreaming(r) => Ok(r.pointer_limit - r.pointer_base),
1163            }
1164        }
1165    }
1166}
1167impl<'aa, R: RandomReadBlobAsync + MemoryMapBlob + 'aa> peridot_native_io::RandomReadBlobAsync
1168    for ArchiveBinReaderAsync<'aa, R>
1169{
1170    type ReadFuture<'a, 'b>
1171        = EnumDispatchF<
1172        OnMemoryArchiveBinReadFuture<'a, 'b, 'aa>,
1173        FileStreamingArchiveBinReadFuture<'a, 'aa, 'b, R>,
1174    >
1175    where
1176        Self: 'a;
1177    type ReadVecFuture<'a, 'b, 'bb>
1178        = EnumDispatchF<
1179        OnMemoryArchiveBinReadVecFuture<'a, 'b, 'aa, 'bb>,
1180        FileStreamingArchiveBinReadVecFuture<'a, 'aa, 'b, 'bb, R>,
1181    >
1182    where
1183        Self: 'a,
1184        'bb: 'b;
1185
1186    fn read_async<'a, 'b>(
1187        &'a self,
1188        offs: u64,
1189        buf: &'b mut [MaybeUninit<u8>],
1190    ) -> Self::ReadFuture<'a, 'b> {
1191        match self {
1192            Self::OnMemory(r) => EnumDispatchF::OnMemory(OnMemoryArchiveBinReadFuture {
1193                reader: r,
1194                pos: offs,
1195                buf,
1196            }),
1197            Self::FileStreaming(r) => {
1198                EnumDispatchF::FileStreaming(FileStreamingArchiveBinReadFuture {
1199                    reader: r,
1200                    pos: offs,
1201                    buf,
1202                    state: FileStreamingArchiveBinReadFutureState::Idle,
1203                })
1204            }
1205        }
1206    }
1207
1208    fn readv_async<'a, 'b, 'bb>(
1209        &'a self,
1210        offs: u64,
1211        iovecs: &'b mut [IoSliceMut<'bb>],
1212    ) -> Self::ReadVecFuture<'a, 'b, 'bb> {
1213        match self {
1214            Self::OnMemory(r) => EnumDispatchF::OnMemory(OnMemoryArchiveBinReadVecFuture {
1215                reader: r,
1216                pos: offs,
1217                buf: iovecs,
1218            }),
1219            Self::FileStreaming(r) => {
1220                EnumDispatchF::FileStreaming(FileStreamingArchiveBinReadVecFuture {
1221                    reader: r,
1222                    pos: offs,
1223                    buf: iovecs,
1224                    state: FileStreamingArchiveBinReadFutureState::Idle,
1225                })
1226            }
1227        }
1228    }
1229}
1230impl<'a, R: RandomReadBlobAsync + MemoryMapBlob> ArchiveBinReaderAsync<'a, R> {
1231    #[inline]
1232    pub async fn read(&mut self, buf: &mut [u8]) -> IOResult<usize> {
1233        match *self {
1234            Self::OnMemory(ref mut x) => x.read(buf),
1235            Self::FileStreaming(ref mut x) => x.read(buf).await,
1236        }
1237    }
1238
1239    #[inline]
1240    pub async fn read_exact(&mut self, buf: &mut [u8]) -> IOResult<()> {
1241        match *self {
1242            Self::OnMemory(ref mut x) => x.read_exact(buf),
1243            Self::FileStreaming(ref mut x) => x.read_exact(buf).await,
1244        }
1245    }
1246
1247    #[inline]
1248    pub async fn read_all(&mut self) -> IOResult<Vec<u8>> {
1249        match *self {
1250            Self::OnMemory(ref mut x) => {
1251                let mut sink = Vec::new();
1252                x.read_to_end(&mut sink)?;
1253
1254                Ok(sink)
1255            }
1256            Self::FileStreaming(ref mut x) => x.read_all().await,
1257        }
1258    }
1259}
1260
1261pub enum ArchiveAsync {
1262    OnMemory(OnMemoryArchive),
1263    FileStreaming(FileStreamingArchiveAsync<PlatformNativeFileReaderAsync>),
1264}
1265impl ArchiveAsync {
1266    /// Creates a new archive reader from a platform-specific blob reader.
1267    pub async fn new(
1268        blob: PlatformNativeFileReaderAsync,
1269        check_integrity: bool,
1270    ) -> ArchiveReadResult<Self> {
1271        let (comp, crc, body_start) = Self::read_file_header(&blob).await?;
1272        if check_integrity {
1273            // read entire file for compute crc32
1274            let body = blob.read_to_end_async(body_start).await?;
1275            let input_crc = crc32::checksum_ieee(&body[..]);
1276            if input_crc != crc {
1277                return Err(ArchiveReadError::IntegrityCheckFailed);
1278            }
1279
1280            // 全部読んじゃったのでOnMemory扱い
1281            Ok(Self::OnMemory(OnMemoryArchive::new(comp, body)?))
1282        } else {
1283            match comp {
1284                CompressionMethod::None => Ok(Self::FileStreaming(
1285                    FileStreamingArchiveAsync::new(blob, body_start).await?,
1286                )),
1287                _ => {
1288                    // read entire file for decompression
1289                    let body = blob.read_to_end_async(body_start).await?;
1290                    Ok(Self::OnMemory(OnMemoryArchive::new(comp, body)?))
1291                }
1292            }
1293        }
1294    }
1295
1296    async fn read_file_header(
1297        reader: &(impl RandomReadBlobAsync + ?Sized),
1298    ) -> ArchiveReadResult<(CompressionMethod, u32, u64)> {
1299        let mut read_ptr = 0;
1300        let mut signature = [const { MaybeUninit::uninit() }; 4];
1301        reader
1302            .read_exact_async(read_ptr, &mut signature[..])
1303            .await?;
1304        read_ptr += 4;
1305        let signature = unsafe { core::mem::transmute::<[MaybeUninit<u8>; _], [u8; _]>(signature) };
1306        let mut sink_64_bits = [const { MaybeUninit::uninit() }; 8];
1307        let comp: CompressionMethod;
1308        match &signature {
1309            b"par " => {
1310                comp = CompressionMethod::None;
1311            }
1312            b"pard" => {
1313                reader.read_exact_async(read_ptr, &mut sink_64_bits).await?;
1314                read_ptr += 8;
1315                comp = CompressionMethod::Zlib(u64::from_le_bytes(unsafe {
1316                    core::mem::transmute::<[MaybeUninit<u8>; _], [u8; _]>(sink_64_bits)
1317                }));
1318            }
1319            b"parz" => {
1320                reader.read_exact_async(read_ptr, &mut sink_64_bits).await?;
1321                read_ptr += 8;
1322                comp = CompressionMethod::Lz4(u64::from_le_bytes(unsafe {
1323                    core::mem::transmute::<[MaybeUninit<u8>; _], [u8; _]>(sink_64_bits)
1324                }));
1325            }
1326            b"par1" => {
1327                reader.read_exact_async(read_ptr, &mut sink_64_bits).await?;
1328                read_ptr += 8;
1329                comp = CompressionMethod::Zstd11(u64::from_le_bytes(unsafe {
1330                    core::mem::transmute::<[MaybeUninit<u8>; _], [u8; _]>(sink_64_bits)
1331                }));
1332            }
1333            _ => return Err(ArchiveReadError::SignatureMismatch),
1334        }
1335
1336        let mut crc32_bytes = [const { MaybeUninit::uninit() }; 4];
1337        reader.read_exact_async(read_ptr, &mut crc32_bytes).await?;
1338        read_ptr += 4;
1339        let crc32_bytes =
1340            unsafe { core::mem::transmute::<[MaybeUninit<u8>; _], [u8; _]>(crc32_bytes) };
1341
1342        Ok((comp, u32::from_le_bytes(crc32_bytes), read_ptr))
1343    }
1344
1345    #[inline]
1346    pub fn list_entry(&self, callback: impl FnMut(AssetNameRef)) {
1347        match *self {
1348            Self::OnMemory(ref x) => x.list_entry(callback),
1349            Self::FileStreaming(ref x) => x.list_entry(callback),
1350        }
1351    }
1352
1353    #[inline]
1354    pub fn find_entry(&self, name: &str, ext: &str) -> Option<AssetEntryHeadingPair> {
1355        match *self {
1356            Self::OnMemory(ref x) => x.find_entry(name, ext),
1357            Self::FileStreaming(ref x) => x.find_entry(name, ext),
1358        }
1359    }
1360
1361    #[inline]
1362    pub fn read_bin<'a>(
1363        &'a self,
1364        heading: AssetEntryHeadingPair,
1365    ) -> ArchiveBinReaderAsync<'a, PlatformNativeFileReaderAsync> {
1366        match *self {
1367            Self::OnMemory(ref x) => ArchiveBinReaderAsync::OnMemory(x.read_bin(heading)),
1368            Self::FileStreaming(ref x) => ArchiveBinReaderAsync::FileStreaming(x.read_bin(heading)),
1369        }
1370    }
1371}
1372
1373pub enum Archive {
1374    OnMemory(OnMemoryArchive),
1375    FileStreaming(FileStreamingArchive<PlatformNativeFileReader>),
1376}
1377impl Archive {
1378    /// Creates a new archive reader from a platform-specific blob reader.
1379    pub fn new(blob: PlatformNativeFileReader, check_integrity: bool) -> ArchiveReadResult<Self> {
1380        let (comp, crc, body_start) = Self::read_file_header(&blob)?;
1381        if check_integrity {
1382            // read entire file for compute crc32
1383            let body = blob.read_to_end(body_start)?;
1384            let input_crc = crc32::checksum_ieee(&body[..]);
1385            if input_crc != crc {
1386                return Err(ArchiveReadError::IntegrityCheckFailed);
1387            }
1388
1389            // 全部読んじゃったのでOnMemory扱い
1390            Ok(Self::OnMemory(OnMemoryArchive::new(comp, body)?))
1391        } else {
1392            match comp {
1393                CompressionMethod::None => Ok(Self::FileStreaming(FileStreamingArchive::new(
1394                    blob, body_start,
1395                )?)),
1396                _ => {
1397                    // read entire file for decompression
1398                    let body = blob.read_to_end(body_start)?;
1399                    Ok(Self::OnMemory(OnMemoryArchive::new(comp, body)?))
1400                }
1401            }
1402        }
1403    }
1404
1405    fn read_file_header(
1406        reader: &(impl RandomReadBlob + ?Sized),
1407    ) -> ArchiveReadResult<(CompressionMethod, u32, u64)> {
1408        let mut read_ptr = 0;
1409        let mut signature = [const { MaybeUninit::uninit() }; 4];
1410        reader.read_exact(read_ptr, &mut signature[..])?;
1411        read_ptr += 4;
1412        let signature = unsafe { core::mem::transmute::<[MaybeUninit<u8>; _], [u8; _]>(signature) };
1413        let mut sink_64_bits = [const { MaybeUninit::uninit() }; 8];
1414        let comp = match &signature {
1415            b"par " => CompressionMethod::None,
1416            b"pard" => {
1417                reader.read_exact(read_ptr, &mut sink_64_bits)?;
1418                read_ptr += 8;
1419                CompressionMethod::Zlib(u64::from_le_bytes(unsafe {
1420                    core::mem::transmute::<[MaybeUninit<u8>; _], [u8; _]>(sink_64_bits)
1421                }))
1422            }
1423            b"parz" => {
1424                reader.read_exact(read_ptr, &mut sink_64_bits)?;
1425                read_ptr += 8;
1426                CompressionMethod::Lz4(u64::from_le_bytes(unsafe {
1427                    core::mem::transmute::<[MaybeUninit<u8>; _], [u8; _]>(sink_64_bits)
1428                }))
1429            }
1430            b"par1" => {
1431                reader.read_exact(read_ptr, &mut sink_64_bits)?;
1432                read_ptr += 8;
1433                CompressionMethod::Zstd11(u64::from_le_bytes(unsafe {
1434                    core::mem::transmute::<[MaybeUninit<u8>; _], [u8; _]>(sink_64_bits)
1435                }))
1436            }
1437            _ => return Err(ArchiveReadError::SignatureMismatch),
1438        };
1439
1440        let mut crc32_bytes = [const { MaybeUninit::uninit() }; 4];
1441        reader.read_exact(read_ptr, &mut crc32_bytes)?;
1442        read_ptr += 4;
1443        let crc32_bytes =
1444            unsafe { core::mem::transmute::<[MaybeUninit<u8>; _], [u8; _]>(crc32_bytes) };
1445
1446        Ok((comp, u32::from_le_bytes(crc32_bytes), read_ptr))
1447    }
1448
1449    #[inline]
1450    pub fn list_entry(&self, callback: impl FnMut(AssetNameRef)) {
1451        match *self {
1452            Self::OnMemory(ref x) => x.list_entry(callback),
1453            Self::FileStreaming(ref x) => x.list_entry(callback),
1454        }
1455    }
1456
1457    #[inline]
1458    pub fn find_entry(&self, name: &str, ext: &str) -> Option<AssetEntryHeadingPair> {
1459        match *self {
1460            Self::OnMemory(ref x) => x.find_entry(name, ext),
1461            Self::FileStreaming(ref x) => x.find_entry(name, ext),
1462        }
1463    }
1464
1465    #[inline]
1466    pub fn read_bin<'a>(
1467        &'a self,
1468        heading: AssetEntryHeadingPair,
1469    ) -> ArchiveBinReader<'a, PlatformNativeFileReader> {
1470        match *self {
1471            Self::OnMemory(ref x) => ArchiveBinReader::OnMemory(x.read_bin(heading)),
1472            Self::FileStreaming(ref x) => ArchiveBinReader::FileStreaming(x.read_bin(heading)),
1473        }
1474    }
1475}