const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct Fnv {
hash: u64,
}
impl Fnv {
pub const fn new() -> Self {
Self { hash: FNV_OFFSET }
}
pub const fn byte(mut self, b: u8) -> Self {
self.hash ^= b as u64;
self.hash = self.hash.wrapping_mul(FNV_PRIME);
self
}
pub const fn u16(self, v: u16) -> Self {
self.byte(v as u8).byte((v >> 8) as u8)
}
pub const fn u64(mut self, v: u64) -> Self {
let mut i = 0;
while i < 8 {
self = self.byte((v >> (i * 8)) as u8);
i += 1;
}
self
}
pub const fn str(mut self, s: &str) -> Self {
let bytes = s.as_bytes();
self = self.u16(bytes.len() as u16);
let mut i = 0;
while i < bytes.len() {
self = self.byte(bytes[i]);
i += 1;
}
self
}
pub const fn opt_str(self, s: Option<&str>) -> Self {
match s {
Some(s) => self.byte(1).str(s),
None => self.byte(0),
}
}
pub const fn field_type(self, ty: crate::schema::FieldType) -> Self {
use crate::schema::FieldType::*;
match ty {
U8 => self.byte(0),
U16 => self.byte(1),
U32 => self.byte(2),
U64 => self.byte(3),
I8 => self.byte(4),
I16 => self.byte(5),
I32 => self.byte(6),
I64 => self.byte(7),
Bool => self.byte(8),
Bytes => self.byte(9),
Enum { repr } => self.byte(10).byte(repr),
Interned { dynamic } => self.byte(11).byte(dynamic as u8),
}
}
pub const fn finish(self) -> u64 {
self.hash
}
}
impl Default for Fnv {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct EventId(pub u64);
impl EventId {
pub const fn of(name: &str) -> Self {
let bytes = name.as_bytes();
let mut hash = FNV_OFFSET;
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(FNV_PRIME);
i += 1;
}
EventId(hash)
}
#[inline]
pub const fn get(self) -> u64 {
self.0
}
}
impl core::fmt::Debug for EventId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "EventId({:#018x})", self.0)
}
}
impl core::fmt::Display for EventId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#018x}", self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_known_fnv1a_vectors() {
assert_eq!(EventId::of("").get(), FNV_OFFSET);
assert_eq!(EventId::of("a").get(), 0xaf63_dc4c_8601_ec8c);
assert_eq!(EventId::of("foobar").get(), 0x85944171f73967e8);
}
#[test]
fn distinct_names_distinct_ids() {
assert_ne!(EventId::of("ns::A"), EventId::of("ns::B"));
assert_ne!(EventId::of("ns1::A"), EventId::of("ns2::A"));
}
#[test]
fn is_const_evaluable() {
const ID: EventId = EventId::of("ns::Event");
assert_eq!(ID, EventId::of("ns::Event"));
}
}