#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum RecordType {
SnapMetadata = 1,
Extent = 2,
Inode = 3,
Xattr = 4,
SiblingLink = 5,
DstreamId = 6,
CryptoState = 7,
FileExtent = 8,
DirRec = 9,
DirStats = 10,
SnapName = 11,
SiblingMap = 12,
FileInfo = 13,
}
impl RecordType {
#[must_use]
pub fn from_u4(value: u8) -> Option<Self> {
Some(match value {
1 => RecordType::SnapMetadata,
2 => RecordType::Extent,
3 => RecordType::Inode,
4 => RecordType::Xattr,
5 => RecordType::SiblingLink,
6 => RecordType::DstreamId,
7 => RecordType::CryptoState,
8 => RecordType::FileExtent,
9 => RecordType::DirRec,
10 => RecordType::DirStats,
11 => RecordType::SnapName,
12 => RecordType::SiblingMap,
13 => RecordType::FileInfo,
_ => return None,
})
}
}
pub const OBJ_TYPE_SHIFT: u64 = 60;
pub const OBJ_ID_MASK: u64 = 0x0fff_ffff_ffff_ffff;
#[must_use]
pub fn decode_jkey(obj_id_and_type: u64) -> (u64, Option<RecordType>) {
let oid = obj_id_and_type & OBJ_ID_MASK;
#[allow(clippy::cast_possible_truncation)]
let ty = ((obj_id_and_type >> OBJ_TYPE_SHIFT) & 0xf) as u8;
(oid, RecordType::from_u4(ty))
}
const OFF_XF_NUM_EXTS: usize = 0;
const OFF_XF_USED_DATA: usize = 2;
const XF_DESC_OFF: usize = 4;
const XF_DESC_LEN: usize = 4;
const MAX_XF_NUM_EXTS: usize = 4096;
#[must_use]
pub fn parse_xfields(data: &[u8]) -> Vec<(u8, &[u8])> {
if data.len() < XF_DESC_OFF {
return Vec::new();
}
let num = crate::bytes::le_u16(data, OFF_XF_NUM_EXTS) as usize;
let _used = crate::bytes::le_u16(data, OFF_XF_USED_DATA);
let num = num.min(MAX_XF_NUM_EXTS);
let Some(desc_end) = num
.checked_mul(XF_DESC_LEN)
.and_then(|t| t.checked_add(XF_DESC_OFF))
else {
return Vec::new(); };
if desc_end > data.len() {
return Vec::new();
}
let mut out = Vec::with_capacity(num);
let mut value_off = desc_end;
for i in 0..num {
let d = XF_DESC_OFF + i * XF_DESC_LEN;
let x_type = data[d]; let x_size = crate::bytes::le_u16(data, d + 2) as usize;
let Some(end) = value_off.checked_add(x_size) else {
break; };
let Some(value) = data.get(value_off..end) else {
break;
};
out.push((x_type, value));
let aligned = (x_size + 7) & !7;
let Some(next) = value_off.checked_add(aligned) else {
break; };
value_off = next;
}
out
}