pub const SHM_MAGIC: u64 = 0xAC_C0_47_4D_5F_53_48_01;
pub const SHM_FORMAT_VERSION: u32 = 1;
pub const SHM_HEADER_SIZE: usize = 64;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ShmHeader {
pub magic: u64,
pub format_version: u32,
pub shm_abi_version: u32,
pub layout_hash: u64,
pub total_size: u64,
pub _reserved: [u8; 32],
}
const _: () = assert!(core::mem::size_of::<ShmHeader>() == SHM_HEADER_SIZE);
impl ShmHeader {
pub fn new(layout_hash: u64, total_size: u64, shm_abi_version: u32) -> Self {
ShmHeader {
magic: SHM_MAGIC,
format_version: SHM_FORMAT_VERSION,
shm_abi_version,
layout_hash,
total_size,
_reserved: [0u8; 32],
}
}
}
pub struct LayoutEntry<'a> {
pub name: &'a str,
pub offset: u64,
pub size: u64,
pub data_type: &'a str,
}
const FNV_OFFSET: u64 = 0xcbf29ce484222325;
const FNV_PRIME: u64 = 0x100000001b3;
#[inline]
fn mix(h: &mut u64, bytes: &[u8]) {
for &b in bytes {
*h ^= b as u64;
*h = h.wrapping_mul(FNV_PRIME);
}
}
pub fn layout_hash(entries: &[LayoutEntry<'_>], total_size: u64, shm_abi_version: u32) -> u64 {
let mut h = FNV_OFFSET;
mix(&mut h, &SHM_FORMAT_VERSION.to_le_bytes());
mix(&mut h, &shm_abi_version.to_le_bytes());
mix(&mut h, &total_size.to_le_bytes());
for e in entries {
mix(&mut h, e.name.as_bytes());
mix(&mut h, &[0]);
mix(&mut h, &e.offset.to_le_bytes());
mix(&mut h, &e.size.to_le_bytes());
mix(&mut h, e.data_type.as_bytes());
mix(&mut h, &[0]);
}
h
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn header_is_64_bytes() {
assert_eq!(core::mem::size_of::<ShmHeader>(), 64);
}
#[test]
fn hash_is_order_and_value_sensitive() {
let a = [LayoutEntry { name: "x", offset: 64, size: 4, data_type: "u32" },
LayoutEntry { name: "y", offset: 68, size: 8, data_type: "f64" }];
let b = [LayoutEntry { name: "y", offset: 68, size: 8, data_type: "f64" },
LayoutEntry { name: "x", offset: 64, size: 4, data_type: "u32" }];
let h1 = layout_hash(&a, 128, 1);
assert_eq!(h1, layout_hash(&a, 128, 1), "stable");
assert_ne!(h1, layout_hash(&b, 128, 1), "order-sensitive");
assert_ne!(h1, layout_hash(&a, 128, 2), "abi-sensitive");
assert_ne!(h1, layout_hash(&a, 256, 1), "size-sensitive");
let c = [LayoutEntry { name: "x", offset: 64, size: 4, data_type: "i32" },
LayoutEntry { name: "y", offset: 68, size: 8, data_type: "f64" }];
assert_ne!(h1, layout_hash(&c, 128, 1), "type-sensitive");
}
}