use ifc_model::guid::Guid;
const FNV_OFFSET: u128 = 0x6c62_272e_07bb_0142_62b8_2175_6295_c58d;
const FNV_PRIME: u128 = 0x0000_0000_0100_0000_0000_0000_0000_013b;
pub(super) fn derived_global_id(
owner: &str,
set: &str,
role: &str,
next_id: u64,
serial: u64,
) -> String {
let mut hash = FNV_OFFSET;
for part in [
"openbim-ifc/property-edit",
owner,
set,
role,
&next_id.to_string(),
&serial.to_string(),
] {
for byte in (part.len() as u64)
.to_le_bytes()
.iter()
.chain(part.as_bytes())
{
hash ^= u128::from(*byte);
hash = hash.wrapping_mul(FNV_PRIME);
}
}
let mut bytes = avalanche(hash).to_be_bytes();
bytes[6] = (bytes[6] & 0x0f) | 0x80;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
Guid::from_uuid(bytes).to_string()
}
fn avalanche(hash: u128) -> u128 {
let mix = |mut x: u64| {
x ^= x >> 33;
x = x.wrapping_mul(0xff51_afd7_ed55_8ccd);
x ^= x >> 33;
x = x.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
x ^ (x >> 33)
};
let high = (hash >> 64) as u64;
let low = hash as u64;
let high = mix(high ^ low.rotate_left(29));
let low = mix(low ^ high);
(u128::from(high) << 64) | u128::from(low)
}
#[cfg(test)]
mod tests {
use super::derived_global_id;
use ifc_model::guid::Guid;
#[test]
fn a_derived_global_id_is_valid_stable_and_distinct() {
let one = derived_global_id("2YvctVUKr0kugbFTf53O9L", "Pset_WallCommon", "set", 40, 0);
assert!(Guid::parse(&one).is_some(), "{one}");
assert_eq!(
one,
derived_global_id("2YvctVUKr0kugbFTf53O9L", "Pset_WallCommon", "set", 40, 0)
);
let others = [
derived_global_id("2YvctVUKr0kugbFTf53O9L", "Pset_WallCommon", "rel", 40, 0),
derived_global_id("2YvctVUKr0kugbFTf53O9L", "Pset_WallCommon", "set", 41, 0),
derived_global_id("2YvctVUKr0kugbFTf53O9L", "Pset_WallCommon", "set", 40, 1),
derived_global_id("3YvctVUKr0kugbFTf53O9L", "Pset_WallCommon", "set", 40, 0),
derived_global_id("2YvctVUKr0kugbFTf53O9L", "Pset_WallCommo", "nset", 40, 0),
];
for other in &others {
assert_ne!(&one, other);
}
}
}