1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use super::ReadSeek;
use async_std::{
    io::{prelude::SeekExt, ReadExt},
    sync::{Mutex, MutexGuard},
};
use hff_core::{
    byteorder::ReadBytesExt, ByteOrder, Chunk, ChunkCache, ContentInfo, Ecc, Endian, Error, Header,
    Result, Table, Version, BE, LE,
};
use std::mem::size_of;

/// Implements a std reader wrapper around the source.
pub struct AsyncStdReader {
    source: Mutex<Box<dyn ReadSeek>>,
}

impl std::fmt::Debug for AsyncStdReader {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "AsyncStdReader")
    }
}

impl AsyncStdReader {
    /// Create a new std reader type.
    pub fn new(source: impl ReadSeek + 'static) -> Self {
        Self {
            source: Mutex::new(Box::new(source)),
        }
    }

    /// Read the given content.
    pub async fn read(&self, content: &dyn ContentInfo) -> Result<Vec<u8>> {
        let mut source = self.source.lock().await;
        source
            .seek(std::io::SeekFrom::Start(content.offset()))
            .await?;
        let mut result = vec![0; content.len() as usize];
        source.read_exact(result.as_mut_slice()).await?;
        Ok(result)
    }

    /// Get he appropriate reader implementation.
    pub async fn reader(
        &self,
        content: impl ContentInfo,
    ) -> Result<MutexGuard<'_, Box<dyn ReadSeek>>> {
        let mut source = self.source.lock().await;
        source
            .seek(std::io::SeekFrom::Start(content.offset()))
            .await?;
        Ok(source)
    }

    /// Read the header from the given stream.
    pub(super) async fn read_header(
        reader: &mut (dyn async_std::io::Read + std::marker::Unpin),
    ) -> Result<Header> {
        // Read in the entire header ignorant of the endian.
        let mut header = [0_u8; Header::SIZE];
        reader.read_exact(&mut header).await?;

        // Build a byte reader from the buffer.
        let reader: &mut dyn std::io::Read = &mut header.as_slice();

        // Read out the magic value to detect endian.
        let magic = reader.read_u64::<LE>()?;

        // Detect the file content endianess.  NOTE: This only describes
        // the file structure itself, the chunk content is "not" considered
        // part of this.  It is up to the user to deal with endianess of
        // the chunks.
        match Ecc::HFF_MAGIC.endian(magic.into()) {
            Some(endian) => match endian {
                Endian::Little => Ok(Header::with(
                    magic.into(),
                    Version::read::<LE>(reader)?,
                    reader.read_u32::<LE>()?,
                    Ecc::read::<LE>(reader)?,
                    reader.read_u32::<LE>()?,
                    reader.read_u32::<LE>()?,
                )),
                Endian::Big => Ok(Header::with(
                    magic.into(),
                    Version::read::<BE>(reader)?,
                    reader.read_u32::<BE>()?,
                    Ecc::read::<BE>(reader)?,
                    reader.read_u32::<BE>()?,
                    reader.read_u32::<BE>()?,
                )),
            },
            None => Err(Error::Invalid("Not an HFF file.".into())),
        }
    }

    /// Read the tables from the given stream.
    pub(super) async fn read_tables<E: ByteOrder>(
        reader: &mut (dyn async_std::io::Read + std::marker::Unpin),
        count: u32,
    ) -> Result<Vec<Table>> {
        if count > 0 {
            // Create a buffer with appropriate size.
            let mut buffer = vec![0; count as usize * std::mem::size_of::<Table>()];
            reader.read_exact(&mut buffer.as_mut_slice()).await?;

            // Read all the tables out of the buffer.
            let mut tables = vec![];
            let reader: &mut dyn std::io::Read = &mut buffer.as_slice();
            for _ in 0..count {
                let table = Table::read::<E>(reader)?;
                tables.push(table);
            }

            Ok(tables)
        } else {
            // TODO: Does an empty file make sense?  It's not an error but ....
            Ok(vec![])
        }
    }

    /// Read the chunks from the given stream.
    pub(super) async fn read_chunks<E: ByteOrder>(
        reader: &mut (dyn async_std::io::Read + std::marker::Unpin),
        count: u32,
    ) -> Result<Vec<Chunk>> {
        if count > 0 {
            // Create a buffer with the appropriate size.
            let mut buffer = vec![0; count as usize * std::mem::size_of::<Chunk>()];
            reader.read_exact(&mut buffer.as_mut_slice()).await?;

            // Read the chunks out of the buffer.
            let mut chunks = vec![];
            let reader: &mut dyn std::io::Read = &mut buffer.as_slice();
            for _ in 0..count {
                let chunk = Chunk::read::<E>(reader)?;
                chunks.push(chunk);
            }
            Ok(chunks)
        } else {
            // No chunks, perhaps they put all the real data into the metadata so this is
            // still a viable file.
            Ok(vec![])
        }
    }

    /// Read the body of data from the given stream.  Assumes the stream
    /// has an 'end' to read to.
    pub(super) async fn read_body(
        reader: &mut (dyn async_std::io::Read + std::marker::Unpin),
        tables: usize,
        chunks: usize,
    ) -> Result<ChunkCache> {
        let mut buffer = vec![];
        reader.read_to_end(&mut buffer).await?;

        let offset = Header::SIZE + size_of::<Table>() * tables + size_of::<Chunk>() * chunks;
        Ok(ChunkCache::new(offset, buffer))
    }
}