use std::mem::{offset_of, size_of};
pub const PAGE_SIZE: u32 = 0x80000;
pub const HEADER_SIZE: u32 = 0x1000;
pub const MAX_SLOTS: u32 = 0x2800;
pub const SLOT_TABLE_SIZE: u32 = MAX_SLOTS * 4;
pub const DATA_AREA_START: u32 = HEADER_SIZE + SLOT_TABLE_SIZE;
const _: () = assert!(DATA_AREA_START == 0xB000);
pub type BlobGuid = [u8; 16];
pub(crate) const ROOT_BLOB_GUID: BlobGuid = [0; 16];
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct BlobHeader {
_pad_0: [u8; 0x50],
pub field_50: u16,
pub field_52: u16,
pub num_slots: u16,
pub root_slot: u16,
pub space_used: u32,
pub num_ext_blobs: u16,
pub field_5e: u16,
pub compact_times: u32,
_pad_64: [u8; 4],
pub gap_space: u32,
pub tombstone_leaf_cnt: u32,
pub free_list_head: [u16; 8],
pub created_epoch: u64,
pub epoch_high_water: u64,
_pad_90: [u8; 0x10],
pub blob_guid: BlobGuid,
_pad_b0: [u8; (HEADER_SIZE as usize) - 0xb0],
}
const _: () = assert!(size_of::<BlobHeader>() == HEADER_SIZE as usize);
const _: () = assert!(offset_of!(BlobHeader, field_50) == 0x50);
const _: () = assert!(offset_of!(BlobHeader, num_slots) == 0x54);
const _: () = assert!(offset_of!(BlobHeader, root_slot) == 0x56);
const _: () = assert!(offset_of!(BlobHeader, space_used) == 0x58);
const _: () = assert!(offset_of!(BlobHeader, num_ext_blobs) == 0x5c);
const _: () = assert!(offset_of!(BlobHeader, compact_times) == 0x60);
const _: () = assert!(offset_of!(BlobHeader, gap_space) == 0x68);
const _: () = assert!(offset_of!(BlobHeader, tombstone_leaf_cnt) == 0x6c);
const _: () = assert!(offset_of!(BlobHeader, free_list_head) == 0x70);
const _: () = assert!(offset_of!(BlobHeader, created_epoch) == 0x80);
const _: () = assert!(offset_of!(BlobHeader, epoch_high_water) == 0x88);
const _: () = assert!(offset_of!(BlobHeader, blob_guid) == 0xa0);
pub const CREATED_EPOCH_OFFSET: usize = offset_of!(BlobHeader, created_epoch);
#[inline]
pub fn set_frame_created_epoch(buf: &mut [u8], epoch: u64) {
buf[CREATED_EPOCH_OFFSET..CREATED_EPOCH_OFFSET + size_of::<u64>()]
.copy_from_slice(&epoch.to_ne_bytes());
}
#[inline]
#[must_use]
pub fn frame_created_epoch(buf: &[u8]) -> u64 {
u64::from_ne_bytes(
buf[CREATED_EPOCH_OFFSET..CREATED_EPOCH_OFFSET + size_of::<u64>()]
.try_into()
.expect("frame buffer is at least HEADER_SIZE bytes"),
)
}
pub const EPOCH_HIGH_WATER_OFFSET: usize = offset_of!(BlobHeader, epoch_high_water);
#[inline]
pub fn set_frame_epoch_high_water(buf: &mut [u8], epoch: u64) {
buf[EPOCH_HIGH_WATER_OFFSET..EPOCH_HIGH_WATER_OFFSET + size_of::<u64>()]
.copy_from_slice(&epoch.to_ne_bytes());
}
#[inline]
#[must_use]
pub fn frame_epoch_high_water(buf: &[u8]) -> u64 {
u64::from_ne_bytes(
buf[EPOCH_HIGH_WATER_OFFSET..EPOCH_HIGH_WATER_OFFSET + size_of::<u64>()]
.try_into()
.expect("frame buffer is at least HEADER_SIZE bytes"),
)
}
pub const BLOB_GUID_OFFSET: usize = offset_of!(BlobHeader, blob_guid);
#[inline]
pub fn set_frame_blob_guid(buf: &mut [u8], guid: BlobGuid) {
buf[BLOB_GUID_OFFSET..BLOB_GUID_OFFSET + size_of::<BlobGuid>()]
.copy_from_slice(guid.as_slice());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn header_size_and_offsets() {
assert_eq!(size_of::<BlobHeader>(), 4096);
assert_eq!(offset_of!(BlobHeader, num_slots), 0x54);
assert_eq!(offset_of!(BlobHeader, root_slot), 0x56);
assert_eq!(offset_of!(BlobHeader, space_used), 0x58);
assert_eq!(offset_of!(BlobHeader, compact_times), 0x60);
assert_eq!(offset_of!(BlobHeader, gap_space), 0x68);
assert_eq!(offset_of!(BlobHeader, tombstone_leaf_cnt), 0x6c);
assert_eq!(offset_of!(BlobHeader, free_list_head), 0x70);
assert_eq!(offset_of!(BlobHeader, created_epoch), 0x80);
assert_eq!(offset_of!(BlobHeader, epoch_high_water), 0x88);
assert_eq!(offset_of!(BlobHeader, blob_guid), 0xa0);
}
#[test]
fn created_epoch_round_trips_through_buffer() {
let mut buf = vec![0u8; PAGE_SIZE as usize];
let span = CREATED_EPOCH_OFFSET..CREATED_EPOCH_OFFSET + 8;
assert_eq!(&buf[span.clone()], &[0u8; 8]);
set_frame_created_epoch(&mut buf, 0x1234_5678_9abc_def0);
assert_eq!(&buf[span], &0x1234_5678_9abc_def0_u64.to_ne_bytes());
assert_eq!(&buf[0xa0..0xb0], &[0u8; 16]);
}
#[test]
fn constants_consistent() {
assert_eq!(PAGE_SIZE, 524_288);
assert_eq!(HEADER_SIZE, 4096);
assert_eq!(MAX_SLOTS, 10_240);
assert_eq!(DATA_AREA_START, 0xB000);
}
}