use crate::{superblock::ChecksumType, tree::DiskKey};
use bytes::{Buf, BufMut};
use uuid::Uuid;
pub fn get_uuid(buf: &mut &[u8]) -> Uuid {
let bytes: [u8; 16] = buf[..16].try_into().unwrap();
buf.advance(16);
Uuid::from_bytes(bytes)
}
pub fn write_disk_key(buf: &mut [u8], off: usize, key: &DiskKey) {
(&mut buf[off..off + 8]).put_u64_le(key.objectid);
buf[off + 8] = key.key_type.to_raw();
(&mut buf[off + 9..off + 17]).put_u64_le(key.offset);
}
pub fn write_le_u64(buf: &mut [u8], off: usize, val: u64) {
buf[off..off + 8].copy_from_slice(&val.to_le_bytes());
}
pub fn write_le_u32(buf: &mut [u8], off: usize, val: u32) {
buf[off..off + 4].copy_from_slice(&val.to_le_bytes());
}
pub fn write_le_u16(buf: &mut [u8], off: usize, val: u16) {
buf[off..off + 2].copy_from_slice(&val.to_le_bytes());
}
pub fn write_uuid(buf: &mut [u8], off: usize, uuid: &Uuid) {
buf[off..off + 16].copy_from_slice(uuid.as_bytes());
}
#[must_use]
pub fn raw_crc32c(seed: u32, data: &[u8]) -> u32 {
!crc32c::crc32c_append(!seed, data)
}
#[must_use]
pub fn btrfs_name_hash(name: &[u8]) -> u32 {
raw_crc32c(!1u32, name)
}
#[must_use]
pub fn btrfs_csum_data(data: &[u8]) -> u32 {
crc32c::crc32c(data)
}
pub fn csum_tree_block(buf: &mut [u8], csum_type: ChecksumType) {
assert!(buf.len() > 32, "buffer too small for tree block checksum");
let hash = csum_type.compute(&buf[32..]);
let n = csum_type.size();
buf[0..n].copy_from_slice(&hash[..n]);
buf[n..32].fill(0);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_write_le_u64() {
let mut buf = [0u8; 8];
write_le_u64(&mut buf, 0, 0x0807060504030201);
assert_eq!(buf, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
}
#[test]
fn test_write_le_u32() {
let mut buf = [0u8; 4];
write_le_u32(&mut buf, 0, 0x04030201);
assert_eq!(buf, [0x01, 0x02, 0x03, 0x04]);
}
#[test]
fn test_write_le_u16() {
let mut buf = [0u8; 2];
write_le_u16(&mut buf, 0, 0x0201);
assert_eq!(buf, [0x01, 0x02]);
}
#[test]
fn test_write_uuid() {
let uuid =
Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
let mut buf = [0u8; 16];
write_uuid(&mut buf, 0, &uuid);
assert_eq!(buf, *uuid.as_bytes());
}
#[test]
fn test_roundtrip_u64() {
let mut buf = [0u8; 16];
write_le_u64(&mut buf, 4, 0xDEADBEEF_CAFEBABE);
assert_eq!(
u64::from_le_bytes(buf[4..12].try_into().unwrap()),
0xDEADBEEF_CAFEBABE
);
}
#[test]
fn test_btrfs_name_hash() {
assert_eq!(btrfs_name_hash(b"hello.txt"), 0x415f_eb59);
assert_ne!(
btrfs_name_hash(b"hello.txt"),
btrfs_name_hash(b"world.txt")
);
}
#[test]
fn test_roundtrip_uuid() {
let uuid =
Uuid::parse_str("01234567-89ab-cdef-0123-456789abcdef").unwrap();
let mut buf = [0u8; 16];
write_uuid(&mut buf, 0, &uuid);
assert_eq!(Uuid::from_bytes(buf), uuid);
}
}