use crate::le::{put_u16, put_u32, u16 as le16, u32 as le32};
use crate::spec::consts::EXTENT_MAGIC;
use crate::{corrupt, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExtentHeader {
pub entries: u16,
pub max: u16,
pub depth: u16,
pub generation: u32,
}
impl ExtentHeader {
pub const LEN: usize = 12;
pub const ENTRY_LEN: usize = 12;
pub fn decode(b: &[u8]) -> Result<ExtentHeader> {
if b.len() < Self::LEN {
return Err(corrupt("extent header", "short buffer"));
}
if le16(b, 0) != EXTENT_MAGIC {
return Err(corrupt(
"extent header",
format!("bad magic {:#06x}", le16(b, 0)),
));
}
Ok(ExtentHeader {
entries: le16(b, 2),
max: le16(b, 4),
depth: le16(b, 6),
generation: le32(b, 8),
})
}
pub fn encode(&self, out: &mut [u8]) {
put_u16(out, 0, EXTENT_MAGIC);
put_u16(out, 2, self.entries);
put_u16(out, 4, self.max);
put_u16(out, 6, self.depth);
put_u32(out, 8, self.generation);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Extent {
pub logical: u32,
pub raw_len: u16,
pub start: u64,
}
impl Extent {
pub fn decode(b: &[u8]) -> Extent {
Extent {
logical: le32(b, 0),
raw_len: le16(b, 4),
start: u64::from(le16(b, 6)) << 32 | u64::from(le32(b, 8)),
}
}
pub fn encode(&self, out: &mut [u8]) {
put_u32(out, 0, self.logical);
put_u16(out, 4, self.raw_len);
put_u16(out, 6, (self.start >> 32) as u16);
put_u32(out, 8, self.start as u32);
}
pub fn len(&self) -> u32 {
if self.raw_len > 32768 {
u32::from(self.raw_len) - 32768
} else {
u32::from(self.raw_len)
}
}
pub fn is_unwritten(&self) -> bool {
self.raw_len > 32768
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExtentIdx {
pub logical: u32,
pub leaf: u64,
}
impl ExtentIdx {
pub fn decode(b: &[u8]) -> ExtentIdx {
ExtentIdx {
logical: le32(b, 0),
leaf: u64::from(le32(b, 4)) | u64::from(le16(b, 8)) << 32,
}
}
pub fn encode(&self, out: &mut [u8]) {
put_u32(out, 0, self.logical);
put_u32(out, 4, self.leaf as u32);
put_u16(out, 8, (self.leaf >> 32) as u16);
put_u16(out, 10, 0);
}
pub fn node_capacity(block_size: usize) -> usize {
(block_size - ExtentHeader::LEN - 4) / ExtentHeader::ENTRY_LEN
}
}
pub fn entry(b: &[u8], i: usize) -> &[u8] {
let off = ExtentHeader::LEN + i * ExtentHeader::ENTRY_LEN;
&b[off..off + ExtentHeader::ENTRY_LEN]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn header_roundtrip() {
let h = ExtentHeader {
entries: 3,
max: 340,
depth: 1,
generation: 0,
};
let mut b = [0u8; 12];
h.encode(&mut b);
assert_eq!(ExtentHeader::decode(&b).unwrap(), h);
}
#[test]
fn raw_len_32768_is_initialized_max() {
let e = Extent {
logical: 0,
raw_len: 32768,
start: 100,
};
assert_eq!(e.len(), 32768);
assert!(!e.is_unwritten());
let u = Extent {
logical: 0,
raw_len: 32769,
start: 100,
};
assert_eq!(u.len(), 1);
assert!(u.is_unwritten());
}
#[test]
fn node_capacity_at_4096() {
assert_eq!(ExtentIdx::node_capacity(4096), 340);
}
#[test]
fn journal_inode_root_parses() {
let raw = std::fs::read(format!(
"{}/testdata/vectors/inode_8_journal.bin",
env!("CARGO_MANIFEST_DIR")
))
.unwrap();
let root = &raw[0x28..0x64];
let h = ExtentHeader::decode(root).unwrap();
assert_eq!(h.depth, 0);
assert_eq!(h.max, 4);
assert!(h.entries >= 1);
let e = Extent::decode(entry(root, 0));
assert_eq!(e.logical, 0);
assert!(!e.is_unwritten());
}
}