pub const PTAG_INIT: u32 = 0xffff_ffff;
pub const ID_NONE: u16 = 0x3ff;
pub const SIZE_DELETED: u16 = 0x3ff;
pub const MAX_SIZE: usize = 0x3fe;
pub const TYPE_NAME: u16 = 0x000;
pub const TYPE_REG: u16 = 0x001;
pub const TYPE_DIR: u16 = 0x002;
pub const TYPE_SUPERBLOCK: u16 = 0x0ff;
pub const TYPE_DIRSTRUCT: u16 = 0x200;
pub const TYPE_INLINESTRUCT: u16 = 0x201;
pub const TYPE_CTZSTRUCT: u16 = 0x202;
pub const TYPE_USERATTR: u16 = 0x300;
pub const TYPE_CREATE: u16 = 0x401;
pub const TYPE_DELETE: u16 = 0x4ff;
pub const TYPE_CCRC: u16 = 0x500;
pub const TYPE_FCRC: u16 = 0x5ff;
pub const TYPE_SOFTTAIL: u16 = 0x600;
pub const TYPE_HARDTAIL: u16 = 0x601;
pub const TYPE_MOVESTATE: u16 = 0x7ff;
pub const T1_NAME: u16 = 0x000;
pub const T1_STRUCT: u16 = 0x200;
pub const T1_USERATTR: u16 = 0x300;
pub const T1_SPLICE: u16 = 0x400;
#[allow(dead_code)]
pub const T1_CRC: u16 = 0x500;
pub const T1_TAIL: u16 = 0x600;
pub const T1_GSTATE: u16 = 0x700;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Tag(pub u32);
impl Tag {
pub fn new(type3: u16, id: u16, size: u16) -> Self {
Self(((type3 as u32) << 20) | ((id as u32) << 10) | size as u32)
}
pub fn is_valid(self) -> bool {
self.0 & 0x8000_0000 == 0
}
pub fn type1(self) -> u16 {
((self.0 & 0x7000_0000) >> 20) as u16
}
pub fn type2(self) -> u16 {
((self.0 & 0x7800_0000) >> 20) as u16
}
pub fn type3(self) -> u16 {
((self.0 & 0x7ff0_0000) >> 20) as u16
}
pub fn chunk(self) -> u8 {
((self.0 & 0x0ff0_0000) >> 20) as u8
}
pub fn id(self) -> u16 {
((self.0 & 0x000f_fc00) >> 10) as u16
}
pub fn size(self) -> u16 {
(self.0 & 0x0000_03ff) as u16
}
pub fn is_delete(self) -> bool {
self.size() == SIZE_DELETED
}
pub fn dsize(self) -> usize {
4 + if self.is_delete() {
0
} else {
self.size() as usize
}
}
}
pub fn crc(state: u32, data: &[u8]) -> u32 {
crate::crc::crc32_ieee_raw(state, data)
}
pub fn be32(b: &[u8]) -> u32 {
u32::from_be_bytes([b[0], b[1], b[2], b[3]])
}
pub fn le32(b: &[u8]) -> u32 {
u32::from_le_bytes([b[0], b[1], b[2], b[3]])
}
pub fn rev_newer(a: u32, b: u32) -> bool {
(a.wrapping_sub(b)) as i32 > 0
}
#[cfg(test)]
mod tests {
use super::*;
use ::alloc::vec::Vec;
#[test]
fn tag_fields_round_trip() {
let t = Tag::new(TYPE_INLINESTRUCT, 3, 24);
assert_eq!(t.type3(), TYPE_INLINESTRUCT);
assert_eq!(t.type1(), T1_STRUCT);
assert_eq!(t.chunk(), 0x01);
assert_eq!(t.id(), 3);
assert_eq!(t.size(), 24);
assert_eq!(t.dsize(), 28);
assert!(t.is_valid());
assert!(!t.is_delete());
}
#[test]
fn deleted_tag_carries_no_data() {
let t = Tag::new(TYPE_USERATTR | 0x42, 1, SIZE_DELETED);
assert!(t.is_delete());
assert_eq!(t.dsize(), 4);
}
#[test]
fn fcrc_is_not_a_commit_crc() {
assert_eq!(Tag::new(TYPE_CCRC, ID_NONE, 4).type2(), 0x500);
assert_ne!(Tag::new(TYPE_FCRC, ID_NONE, 8).type2(), 0x500);
assert_eq!(Tag::new(TYPE_FCRC, ID_NONE, 8).type1(), T1_CRC);
}
#[test]
fn crc_matches_reference() {
let mut b = Vec::new();
b.extend_from_slice(&0u32.to_le_bytes()); b.extend_from_slice(&[0xf0, 0x0f, 0xff, 0xf7]); b.extend_from_slice(b"littlefs");
b.extend_from_slice(&[0x2f, 0xe0, 0x00, 0x10]); b.extend_from_slice(&[
0x01, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x03, 0x00, 0x00, ]);
b.extend_from_slice(&[0x7f, 0xef, 0xfc, 0x10]); b.extend_from_slice(&[0x00, 0x01, 0x00, 0x00, 0xde, 0x57, 0x57, 0x01]);
b.extend_from_slice(&[0x0f, 0xf0, 0x00, 0xcc]); assert_eq!(crc(PTAG_INIT, &b), 0xa52f_adb2);
}
#[test]
fn revision_compare_handles_wraparound() {
assert!(rev_newer(2, 1));
assert!(!rev_newer(1, 2));
assert!(rev_newer(0, u32::MAX));
}
}