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
use crate::internal::consts;

// ========================================================================= //

/// The CFB format version to use.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Version {
    /// Version 3, which uses 512-byte sectors.
    V3,
    /// Version 4, which uses 4096-byte sectors.
    V4,
}

impl Version {
    /// Returns the version enum for the given version number, or `None`.
    pub fn from_number(number: u16) -> Option<Version> {
        match number {
            3 => Some(Version::V3),
            4 => Some(Version::V4),
            _ => None,
        }
    }

    /// Returns the version number for this version.
    pub fn number(self) -> u16 {
        match self {
            Version::V3 => 3,
            Version::V4 => 4,
        }
    }

    /// Returns the sector shift used in this version.
    pub fn sector_shift(self) -> u16 {
        match self {
            Version::V3 => 9,  // 512-byte sectors
            Version::V4 => 12, // 4096-byte sectors
        }
    }

    /// Returns the length of sectors used in this version.
    ///
    /// ```
    /// use cfb::Version;
    /// assert_eq!(Version::V3.sector_len(), 512);
    /// assert_eq!(Version::V4.sector_len(), 4096);
    /// ```
    pub fn sector_len(self) -> usize {
        1 << (self.sector_shift() as usize)
    }

    /// Returns the bitmask used for reading stream lengths in this version.
    pub fn stream_len_mask(self) -> u64 {
        match self {
            Version::V3 => 0xffffffff,
            Version::V4 => 0xffffffffffffffff,
        }
    }

    /// Returns the number of directory entries per sector in this version.
    pub fn dir_entries_per_sector(self) -> usize {
        self.sector_len() / consts::DIR_ENTRY_LEN
    }
}

// ========================================================================= //

#[cfg(test)]
mod tests {
    use super::Version;

    #[test]
    fn number_round_trip() {
        for &version in &[Version::V3, Version::V4] {
            assert_eq!(Version::from_number(version.number()), Some(version));
        }
    }
}

// ========================================================================= //