changeset 293:20a4c98ca0d5

log: Implement masking CRC checksums to be compatible with the original.
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 26 Sep 2017 19:43:31 +0200
parents fac1ed1b3f7d
children 6caeb99cb216
files src/log.rs
diffstat 1 files changed, 30 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/src/log.rs	Tue Sep 26 19:09:12 2017 +0200
+++ b/src/log.rs	Tue Sep 26 19:43:31 2017 +0200
@@ -10,6 +10,7 @@
 use crc::crc32;
 use crc::Hasher32;
 use integer_encoding::FixedInt;
+use integer_encoding::FixedIntWriter;
 
 const BLOCK_SIZE: usize = 32 * 1024;
 const HEADER_SIZE: usize = 4 + 2 + 1;
@@ -97,11 +98,11 @@
         self.digest.write(&[t as u8]);
         self.digest.write(&data[0..len]);
 
-        let chksum = self.digest.sum32();
+        let chksum = mask_crc(self.digest.sum32());
 
         let mut s = 0;
         s += try!(self.dst.write(&chksum.encode_fixed_vec()));
-        s += try!(self.dst.write(&(len as u16).encode_fixed_vec()));
+        s += try!(self.dst.write_fixedint(len as u16));
         s += try!(self.dst.write(&[t as u8]));
         s += try!(self.dst.write(&data[0..len]));
 
@@ -117,6 +118,7 @@
 
 
 pub struct LogReader<R: Read> {
+    // TODO: Wrap src in a buffer to enhance read performance.
     src: R,
     digest: crc32::Digest,
     blk_off: usize,
@@ -194,16 +196,34 @@
         self.digest.reset();
         self.digest.write(&[typ]);
         self.digest.write(data);
-        expected == self.digest.sum32()
+        unmask_crc(expected) == self.digest.sum32()
     }
 }
 
+const MASK_DELTA: u32 = 0xa282ead8;
+
+fn mask_crc(c: u32) -> u32 {
+    (c.wrapping_shr(15) | c.wrapping_shl(17)).wrapping_add(MASK_DELTA)
+}
+
+fn unmask_crc(mc: u32) -> u32 {
+    let rot = mc.wrapping_sub(MASK_DELTA);
+    (rot.wrapping_shr(17) | rot.wrapping_shl(15))
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
     use std::io::Cursor;
 
     #[test]
+    fn test_crc_mask_crc() {
+        let crc = crc32::checksum_castagnoli("abcde".as_bytes());
+        assert_eq!(crc, unmask_crc(mask_crc(crc)));
+        assert!(crc != mask_crc(crc));
+    }
+
+    #[test]
     fn test_writer() {
         let data = &["hello world. My first log entry.", "and my second", "and my third"];
         let mut lw = LogWriter::new(Vec::new());
@@ -259,12 +279,18 @@
         }
 
         assert_eq!(lw.dst.len(), 93);
+        // Corrupt first record.
+        lw.dst[2] += 1;
 
         let mut lr = LogReader::new(lw.dst.as_slice(), true);
         lr.blocksize = super::HEADER_SIZE + 10;
         let mut dst = Vec::with_capacity(128);
-        let mut i = 0;
 
+        // First record is corrupted.
+        assert_eq!(err(StatusCode::Corruption, "Invalid Checksum"),
+                   lr.read(&mut dst));
+
+        let mut i = 1;
         loop {
             let r = lr.read(&mut dst);