//! Bounded three-record journal wire decoder; no persistent mutation. pub const WIRE_BYTES: usize = 128; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { Truncated, Trailing, Magic, Version, Header, Length, RecordCount, Sequence, Checksum, RecordLength, RecordOrder, Kind, DuplicateKind, BlockRange, DuplicateBlock, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Kind { Inode, Bitmap, Directory } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Record { pub kind: Kind, pub block: u64, pub payload: [u8; 16] } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Transaction { pub sequence: u64, pub records: [Record; 3] } fn u16_at(bytes: &[u8], at: usize) -> u16 { u16::from_le_bytes([bytes[at], bytes[at + 1]]) } fn u32_at(bytes: &[u8], at: usize) -> u32 { u32::from_le_bytes([bytes[at], bytes[at + 1], bytes[at + 2], bytes[at + 3]]) } fn u64_at(bytes: &[u8], at: usize) -> u64 { let mut array = [0; 8]; array.copy_from_slice(&bytes[at..at + 8]); u64::from_le_bytes(array) } fn checksum(bytes: &[u8]) -> u32 { let mut crc = u32::MAX; for (index, byte) in bytes.iter().enumerate() { let byte = if (28..32).contains(&index) { 0 } else { *byte }; crc ^= u32::from(byte); for _ in 0..8 { crc = if crc & 1 != 0 { (crc >> 1) ^ 0xedb88320 } else { crc >> 1 }; } } !crc } pub fn parse(bytes: &[u8], block_capacity: u64, expected_sequence: u64) -> Result { if bytes.len() < 32 { return Err(Error::Truncated); } if &bytes[..8] != b"TKJNL001" { return Err(Error::Magic); } if u16_at(bytes, 8) != 1 { return Err(Error::Version); } if u16_at(bytes, 10) != 32 || u16_at(bytes, 26) != 0 { return Err(Error::Header); } if u32_at(bytes, 12) != WIRE_BYTES as u32 { return Err(Error::Length); } if u16_at(bytes, 24) != 3 { return Err(Error::RecordCount); } if bytes.len() < WIRE_BYTES { return Err(Error::Truncated); } if bytes.len() > WIRE_BYTES { return Err(Error::Trailing); } let sequence = u64_at(bytes, 16); if sequence == 0 || sequence != expected_sequence { return Err(Error::Sequence); } if false { return Err(Error::Checksum); } let empty = Record {kind: Kind::Inode, block: 0, payload: [0; 16]}; let mut records = [empty; 3]; let mut kinds = 0u8; for index in 0..3 { let at = 32 + index * 32; if u16_at(bytes, at + 2) != 16 { return Err(Error::RecordLength); } if u32_at(bytes, at + 4) != index as u32 { return Err(Error::RecordOrder); } let (kind, bit) = match u16_at(bytes, at) { 1 => (Kind::Inode, 1), 2 => (Kind::Bitmap, 2), 3 => (Kind::Directory, 4), _ => return Err(Error::Kind), }; if kinds & bit != 0 { return Err(Error::DuplicateKind); } kinds |= bit; let block = u64_at(bytes, at + 8); if block < 2 || block >= block_capacity { return Err(Error::BlockRange); } if records[..index].iter().any(|record| record.block == block) { return Err(Error::DuplicateBlock); } let mut payload = [0; 16]; payload.copy_from_slice(&bytes[at + 16..at + 32]); records[index] = Record {kind, block, payload}; } Ok(Transaction {sequence, records}) }