use serde::{Deserialize, Serialize};
use crate::block::BlockKind;
pub fn fnv1a(bytes: &[u8]) -> u64 {
const OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const PRIME: u64 = 0x0000_0100_0000_01b3;
let mut hash = OFFSET;
for b in bytes {
hash ^= u64::from(*b);
hash = hash.wrapping_mul(PRIME);
}
hash
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct BlockFingerprint {
pub content_hash: u64,
pub prefix_context_hash: u64,
pub suffix_context_hash: u64,
}
impl BlockFingerprint {
pub fn compute(text: &str, start: usize, end: usize) -> Self {
let content_hash = fnv1a(&text.as_bytes()[start..end]);
let prefix_start = floor_char_boundary(text, start.saturating_sub(32));
let suffix_end = ceil_char_boundary(text, (end + 32).min(text.len()));
Self {
content_hash,
prefix_context_hash: fnv1a(&text.as_bytes()[prefix_start..start]),
suffix_context_hash: fnv1a(&text.as_bytes()[end..suffix_end]),
}
}
}
fn floor_char_boundary(text: &str, mut i: usize) -> usize {
while i > 0 && !text.is_char_boundary(i) {
i -= 1;
}
i
}
fn ceil_char_boundary(text: &str, mut i: usize) -> usize {
while i < text.len() && !text.is_char_boundary(i) {
i += 1;
}
i
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct BlockId {
pub revision_created: u64,
pub ordinal: u32,
pub kind: BlockKind,
pub fingerprint: BlockFingerprint,
}