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
//! Validator for the cache.

use crate::{ codec::Compression, codec };

#[derive(Debug, Clone)]
pub struct Entry {
    pub crc: u32,
    pub revision: u32,
}

/// Validator for the `Cache`.
/// 
/// The `Checksum` is used to validate if every file used by the cache
/// is still valid. It contains a list of entries, one entry for each index file.
/// Every entry contains a crc and a revision.
/// 
/// In order to create the `Checksum` the 
/// [create_checksum()](struct.Cache.html#method.create_checksum) function has to be 
/// called on `Cache`. 
#[derive(Clone, Debug, Default)]
pub struct Checksum {
    entries: Vec<Entry>
}

impl Checksum {
    pub(crate) const fn new() -> Self {
        Self { entries: Vec::new() }
    }

    pub(crate) fn push(&mut self, entry: Entry) {
        self.entries.push(entry);
    }

    /// Validates the given crcs with internal crcs of the `Checksum`.
    /// 
    /// # Examples
    /// 
    /// ```
    /// # use rscache::OsrsCache;
    /// # fn main() -> rscache::Result<()> {
    /// # let path = "./data/cache";
    /// # let cache = OsrsCache::new(path)?;
    /// # let checksum = cache.create_checksum()?;
    /// // client crcs:
    /// let crcs = vec![1593884597, 1029608590, 16840364, 4209099954, 3716821437, 165713182, 
    ///                 686540367, 4262755489, 2208636505, 3047082366, 586413816, 2890424900, 
    ///                 3411535427, 3178880569, 153718440, 3849392898, 0, 2813112885, 1461700456, 
    ///                 2751169400, 2927815226];
    /// 
    /// let valid = checksum.validate(&crcs);
    /// 
    /// assert!(valid);
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn validate(&self, crcs: &[u32]) -> bool {
        let internal: Vec<u32> = self.entries.iter()
            .map(|entry| entry.crc)
            .collect();
        
        internal == crcs
    }

    /// Consumes the `Checksum` and encodes it into a byte buffer.
    /// 
    /// After encoding the checksum it can be sent to the client.
    /// 
    /// # Errors
    /// 
    /// Returns a `CacheError` if the encoding fails.
    /// 
    /// # Examples
    /// 
    /// ```
    /// # use rscache::Checksum;
    /// # use std::net::TcpStream;
    /// # use std::io::Write;
    /// fn encode_checksum(checksum: Checksum, stream: &mut TcpStream) -> rscache::Result<()> {
    ///     let buffer = checksum.encode()?;
    /// 
    ///     stream.write_all(&buffer)?;
    ///     Ok(())
    /// }
    /// ```
    #[inline]
    pub fn encode(self) -> crate::Result<Vec<u8>> {
        let mut buffer = Vec::with_capacity(self.entries.len() * 8);

		for entry in self.entries {
            buffer.extend(&u32::to_be_bytes(entry.crc));
            buffer.extend(&u32::to_be_bytes(entry.revision));
        }

        Ok(codec::encode(Compression::None, &buffer, None)?)
    }
}