disk_utils/wal/
record.rs

1use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
2use crc::crc32;
3
4use std::io;
5use std::io::{Cursor, Read, Write};
6use std::mem;
7
8#[repr(u8)]
9#[derive(Clone, Copy, Debug, PartialEq)]
10pub enum RecordType {
11    Zero = 1,
12    Full = 2,
13
14    First = 3,
15    Middle = 4,
16    Last = 5,
17}
18
19impl RecordType {
20    pub fn from_u8(i: u8) -> Option<RecordType> {
21        if i >= RecordType::Zero as u8 && i <= RecordType::Last as u8 {
22            return Some(unsafe { mem::transmute(i) });
23        }
24        None
25    }
26}
27
28/// 32KB Block size.
29pub const BLOCK_SIZE: i64 = 32768;
30/// 7B Header size for record.
31pub const HEADER_SIZE: usize = 7;
32
33/// A single entry of the write ahead log stored in blocks.
34///
35/// # Examples
36///
37/// ```
38/// extern crate disk_utils;
39/// use disk_utils::wal::record::{Record, RecordType};
40///
41/// fn main() {
42///     let record = Record::new(RecordType::Full, vec![123; 12345]);
43///
44///     // Write record into a byte buffer.
45///     let mut bytes = Vec::new();
46///     record.write(&mut bytes).unwrap();
47///
48///     // Read record from the byte buffer.
49///     let test_record = Record::read(&mut &bytes[..]).unwrap();
50///     assert_eq!(record, test_record);
51/// }
52/// ```
53#[derive(Clone, Debug, PartialEq)]
54pub struct Record {
55    pub crc: u32,
56    pub size: u16,
57    pub record_type: RecordType,
58    pub payload: Vec<u8>,
59}
60
61impl Record {
62    pub fn new(record_type: RecordType, payload: Vec<u8>) -> Record {
63        let crc = crc32::checksum_ieee(&payload[..]);
64        Record {
65            crc: crc,
66            size: payload.len() as u16,
67            record_type: record_type,
68            payload: payload,
69        }
70    }
71
72    pub fn read<R: Read>(reader: &mut R) -> io::Result<Record> {
73        let mut buf = [0; HEADER_SIZE];
74        reader.read_exact(&mut buf)?;
75
76        let record_type = match RecordType::from_u8(buf[0]) {
77            Some(rt) => rt,
78            None => return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid record type")),
79        };
80
81        let mut rdr = Cursor::new(buf[1..5].to_vec());
82        let crc = rdr.read_u32::<BigEndian>()?;
83
84        rdr = Cursor::new(buf[5..7].to_vec());
85        let size = rdr.read_u16::<BigEndian>()?;
86
87        let mut payload = vec![0; size as usize];
88        reader.read_exact(&mut payload)?;
89
90        let payload_crc = crc32::checksum_ieee(&payload[..]);
91        if payload_crc != crc {
92            return Err(io::Error::new(io::ErrorKind::InvalidData,
93                                      "CRC checksum failed, possibly corrupted record data"));
94        }
95
96        Ok(Record {
97            crc: crc,
98            size: size,
99            record_type: record_type,
100            payload: payload,
101        })
102    }
103
104    pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
105        let record_type = self.record_type as u8;
106
107        let mut wtr = Vec::new();
108        wtr.write_u32::<BigEndian>(self.crc)?;
109        let (crc1, crc2, crc3, crc4) = (wtr[0], wtr[1], wtr[2], wtr[3]);
110
111        wtr = Vec::new();
112        wtr.write_u16::<BigEndian>(self.size)?;
113        let (size1, size2) = (wtr[0], wtr[1]);
114
115        writer.write(&[record_type, crc1, crc2, crc3, crc4, size1, size2])?;
116        writer.write(&self.payload)?;
117        writer.flush()?;
118
119        Ok(())
120    }
121}