use crate::format::{FormatError, FormatResult, UNDEF_ADDR};
pub const BTREE_V1_SIGNATURE: [u8; 4] = *b"TREE";
#[derive(Debug, Clone)]
pub struct BTreeV1Node {
pub node_type: u8,
pub level: u8,
pub entries_used: u16,
pub left_sibling: u64,
pub right_sibling: u64,
pub keys: Vec<u64>,
pub children: Vec<u64>,
}
impl BTreeV1Node {
pub fn decode(buf: &[u8], sizeof_addr: usize, sizeof_size: usize) -> FormatResult<Self> {
let header_size = 4 + 1 + 1 + 2 + sizeof_addr * 2;
if buf.len() < header_size {
return Err(FormatError::BufferTooShort {
needed: header_size,
available: buf.len(),
});
}
if buf[0..4] != BTREE_V1_SIGNATURE {
return Err(FormatError::InvalidSignature);
}
let node_type = buf[4];
let level = buf[5];
let entries_used = u16::from_le_bytes([buf[6], buf[7]]);
let mut pos = 8;
let left_sibling = read_addr(&buf[pos..], sizeof_addr);
pos += sizeof_addr;
let right_sibling = read_addr(&buf[pos..], sizeof_addr);
pos += sizeof_addr;
let n = entries_used as usize;
if node_type == 0 {
let key_size = sizeof_size;
let child_size = sizeof_addr;
let data_size = (n + 1) * key_size + n * child_size;
let needed = pos + data_size;
if buf.len() < needed {
return Err(FormatError::BufferTooShort {
needed,
available: buf.len(),
});
}
let mut keys = Vec::with_capacity(n + 1);
let mut children = Vec::with_capacity(n);
for _i in 0..n {
keys.push(read_uint(&buf[pos..], key_size));
pos += key_size;
children.push(read_uint(&buf[pos..], child_size));
pos += child_size;
}
keys.push(read_uint(&buf[pos..], key_size));
Ok(BTreeV1Node {
node_type,
level,
entries_used,
left_sibling,
right_sibling,
keys,
children,
})
} else {
Err(FormatError::UnsupportedFeature(format!(
"B-tree v1 type {} not supported (only type 0 for groups)",
node_type
)))
}
}
}
fn read_uint(buf: &[u8], n: usize) -> u64 {
let mut tmp = [0u8; 8];
tmp[..n].copy_from_slice(&buf[..n]);
u64::from_le_bytes(tmp)
}
fn read_addr(buf: &[u8], n: usize) -> u64 {
if buf[..n].iter().all(|&b| b == 0xFF) {
UNDEF_ADDR
} else {
read_uint(buf, n)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn build_group_btree(
level: u8,
keys: &[u64],
children: &[u64],
sizeof_addr: usize,
sizeof_size: usize,
) -> Vec<u8> {
assert_eq!(keys.len(), children.len() + 1);
let entries_used = children.len() as u16;
let mut buf = Vec::new();
buf.extend_from_slice(&BTREE_V1_SIGNATURE);
buf.push(0); buf.push(level);
buf.extend_from_slice(&entries_used.to_le_bytes());
buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
for i in 0..children.len() {
buf.extend_from_slice(&keys[i].to_le_bytes()[..sizeof_size]);
buf.extend_from_slice(&children[i].to_le_bytes()[..sizeof_addr]);
}
buf.extend_from_slice(&keys[children.len()].to_le_bytes()[..sizeof_size]);
buf
}
#[test]
fn decode_leaf_node() {
let buf = build_group_btree(
0, &[0, 8, 16], &[0x100, 0x200], 8,
8,
);
let node = BTreeV1Node::decode(&buf, 8, 8).unwrap();
assert_eq!(node.node_type, 0);
assert_eq!(node.level, 0);
assert_eq!(node.entries_used, 2);
assert_eq!(node.keys, vec![0, 8, 16]);
assert_eq!(node.children, vec![0x100, 0x200]);
assert_eq!(node.left_sibling, UNDEF_ADDR);
assert_eq!(node.right_sibling, UNDEF_ADDR);
}
#[test]
fn decode_internal_node() {
let buf = build_group_btree(
1, &[0, 100], &[0x500], 8,
8,
);
let node = BTreeV1Node::decode(&buf, 8, 8).unwrap();
assert_eq!(node.level, 1);
assert_eq!(node.entries_used, 1);
assert_eq!(node.children, vec![0x500]);
}
#[test]
fn decode_single_entry() {
let buf = build_group_btree(0, &[0, 8], &[0x100], 8, 8);
let node = BTreeV1Node::decode(&buf, 8, 8).unwrap();
assert_eq!(node.entries_used, 1);
assert_eq!(node.children.len(), 1);
}
#[test]
fn decode_4byte() {
let buf = build_group_btree(0, &[0, 4], &[0x80], 4, 4);
let node = BTreeV1Node::decode(&buf, 4, 4).unwrap();
assert_eq!(node.entries_used, 1);
assert_eq!(node.children, vec![0x80]);
}
#[test]
fn decode_bad_sig() {
let mut buf = build_group_btree(0, &[0, 8], &[0x100], 8, 8);
buf[0] = b'X';
assert!(matches!(
BTreeV1Node::decode(&buf, 8, 8).unwrap_err(),
FormatError::InvalidSignature
));
}
#[test]
fn decode_too_short() {
assert!(matches!(
BTreeV1Node::decode(&[0u8; 4], 8, 8).unwrap_err(),
FormatError::BufferTooShort { .. }
));
}
#[test]
fn decode_unsupported_type() {
let mut buf = build_group_btree(0, &[0, 8], &[0x100], 8, 8);
buf[4] = 1; assert!(matches!(
BTreeV1Node::decode(&buf, 8, 8).unwrap_err(),
FormatError::UnsupportedFeature(_)
));
}
}