use crate::{
device::{Page, PageOps},
error::PERes,
io::{read_u16, write_u16, InfallibleWrite, InfallibleWriteFormat},
};
use crc::{Crc, CRC_16_USB};
const CRC: Crc<u16> = Crc::<u16>::new(&CRC_16_USB);
pub fn double_buffer_check(buffer_0: &[u8], buffer_1: &[u8]) -> (u8, bool) {
assert!(buffer_0.len() == buffer_1.len());
let checksum_pos = buffer_0.len() - 2;
let valid_0 = CRC.checksum(&buffer_0[0..checksum_pos]) == read_u16(&buffer_0[checksum_pos..checksum_pos + 2]);
let valid_1 = CRC.checksum(&buffer_1[0..checksum_pos]) == read_u16(&buffer_1[checksum_pos..checksum_pos + 2]);
let flush_number_0 = buffer_0[buffer_0.len() - 3];
let flush_number_1 = buffer_1[buffer_1.len() - 3];
if valid_0 && valid_1 {
if (flush_number_0 == 2 && flush_number_1 == 1) || (flush_number_0 == 0 && flush_number_1 == 3) {
(flush_number_0, true)
} else {
(flush_number_1, false)
}
} else if valid_0 {
(flush_number_0, true)
} else if valid_1 {
(flush_number_1, false)
} else {
panic!("cannot open this persy archive seems to have a corrupted root page");
}
}
pub fn prepare_buffer_flush(buffer: &mut [u8], mut last_flush: u8) -> (u8, u32) {
last_flush += 1;
if last_flush == 4 {
last_flush = 0;
}
let checksum_pos = buffer.len() - 2;
buffer[buffer.len() - 3] = last_flush;
let crc = CRC.checksum(&buffer[0..checksum_pos]);
write_u16(&mut buffer[checksum_pos..checksum_pos + 2], crc);
let offset = if last_flush % 2 == 0 { 0 } else { buffer.len() as u32 };
(last_flush, offset)
}
pub fn write_root_page(root: &mut Page, buffer: &mut [u8], version: u8, last_flush: u8) -> PERes<u8> {
let (last_flush, offset) = prepare_buffer_flush(buffer, last_flush);
root.write_u8(version);
root.seek(offset + 1);
root.write_all(buffer);
Ok(last_flush)
}
#[cfg(test)]
mod tests {
use super::{double_buffer_check, prepare_buffer_flush};
use crate::io::write_u64;
#[test]
fn first_valid() {
let mut buffer_0 = [0; 11];
let buffer_1 = [0; 11];
write_u64(&mut buffer_0, 10);
prepare_buffer_flush(&mut buffer_0, 0);
let (flush, first) = double_buffer_check(&buffer_0, &buffer_1);
assert!(first);
assert_eq!(flush, 1);
let mut buffer_0 = [0; 11];
let mut buffer_1 = [0; 11];
write_u64(&mut buffer_0, 10);
prepare_buffer_flush(&mut buffer_0, 0);
write_u64(&mut buffer_1, 10);
prepare_buffer_flush(&mut buffer_1, 1);
buffer_1[10] = 2;
let (flush, first) = double_buffer_check(&buffer_0, &buffer_1);
assert!(first);
assert_eq!(flush, 1);
let mut buffer_0 = [0; 11];
let mut buffer_1 = [0; 11];
write_u64(&mut buffer_0, 10);
prepare_buffer_flush(&mut buffer_0, 3);
write_u64(&mut buffer_1, 10);
prepare_buffer_flush(&mut buffer_1, 2);
buffer_1[10] = 2;
let (flush, first) = double_buffer_check(&buffer_0, &buffer_1);
assert!(first);
assert_eq!(flush, 0);
}
#[test]
fn second_valid() {
let mut buffer_0 = [0; 11];
let mut buffer_1 = [0; 11];
write_u64(&mut buffer_0, 10);
prepare_buffer_flush(&mut buffer_0, 0);
write_u64(&mut buffer_1, 10);
prepare_buffer_flush(&mut buffer_1, 1);
let (flush, first) = double_buffer_check(&buffer_0, &buffer_1);
assert!(!first);
assert_eq!(flush, 2);
let mut buffer_0 = [0; 11];
let mut buffer_1 = [0; 11];
write_u64(&mut buffer_0, 10);
prepare_buffer_flush(&mut buffer_0, 2);
buffer_0[10] = 4;
write_u64(&mut buffer_1, 10);
prepare_buffer_flush(&mut buffer_1, 1);
let (flush, first) = double_buffer_check(&buffer_0, &buffer_1);
assert!(!first);
assert_eq!(flush, 2);
}
}