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
use std::fs::File as StdFile;
use std::slice::Iter;

use constants::{BLOCK_SIZE, CHUNK_HEADER_SIZE};
use convert::TryInto;
use headers::ChunkType;

pub type ChunkIter<'a> = Iter<'a, Chunk>;

#[derive(Debug, Default)]
pub struct File {
    chunks: Vec<Chunk>,
}

impl File {
    pub fn new() -> Self {
        Self { chunks: Vec::new() }
    }

    pub fn checksum(&self) -> u32 {
        // TODO
        0
    }

    pub fn num_blocks(&self) -> u32 {
        self.chunks
            .iter()
            .fold(0, |sum, chunk| sum + chunk.num_blocks())
    }

    pub fn num_chunks(&self) -> u32 {
        self.chunks
            .len()
            .try_into()
            .expect("number of chunks doesn't fit into u32")
    }

    pub fn add_chunk(&mut self, chunk: Chunk) {
        self.chunks.push(chunk);
    }

    pub fn chunk_iter(&self) -> ChunkIter {
        self.chunks.iter()
    }
}

#[derive(Debug)]
pub enum Chunk {
    Raw {
        file: StdFile,
        offset: u64,
        num_blocks: u32,
    },
    Fill { fill: [u8; 4], num_blocks: u32 },
    DontCare { num_blocks: u32 },
    Crc32 { crc: u32 },
}

impl Chunk {
    pub fn sparse_size(&self) -> u32 {
        let body_size = match *self {
            Chunk::Raw { num_blocks, .. } => num_blocks * BLOCK_SIZE,
            Chunk::Fill { .. } | Chunk::Crc32 { .. } => 4,
            Chunk::DontCare { .. } => 0,
        };
        u32::from(CHUNK_HEADER_SIZE) + body_size
    }

    pub fn raw_size(&self) -> u32 {
        self.num_blocks() * BLOCK_SIZE
    }

    pub fn num_blocks(&self) -> u32 {
        match *self {
            Chunk::Raw { num_blocks, .. } |
            Chunk::Fill { num_blocks, .. } |
            Chunk::DontCare { num_blocks } => num_blocks,
            Chunk::Crc32 { .. } => 0,
        }
    }

    pub fn chunk_type(&self) -> ChunkType {
        match *self {
            Chunk::Raw { .. } => ChunkType::Raw,
            Chunk::Fill { .. } => ChunkType::Fill,
            Chunk::DontCare { .. } => ChunkType::DontCare,
            Chunk::Crc32 { .. } => ChunkType::Crc32,
        }
    }
}