use super::{OperationCodecError, RecordRef};
use ozzy_proto::data::Encoding;
const DOMAIN: &[u8] = b"ozzy producer retry fingerprint\0";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RetryFingerprint([u8; 32]);
impl RetryFingerprint {
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
pub fn retry_fingerprint(record: RecordRef<'_>) -> Result<RetryFingerprint, OperationCodecError> {
if record.message_id.as_bytes() == &[0; 16] {
return Err(OperationCodecError::EmptyValue("message identity"));
}
if record.parts.is_empty() {
return Err(OperationCodecError::EmptyRecordParts);
}
let mut hash = blake3::Hasher::new();
hash.update(DOMAIN);
hash.update(record.message_id.as_bytes());
match record.encoding {
Encoding::Raw => {
hash.update(&[0]);
}
Encoding::Lz4 { decoded_bytes } => {
hash.update(&[1]);
hash.update(&decoded_bytes.to_be_bytes());
}
}
let parts =
u64::try_from(record.parts.len()).map_err(|_| OperationCodecError::LengthOverflow)?;
hash.update(&parts.to_be_bytes());
for part in record.parts.iter() {
let length = u64::try_from(part.len()).map_err(|_| OperationCodecError::LengthOverflow)?;
hash.update(&length.to_be_bytes());
hash.update(part);
}
Ok(RetryFingerprint(*hash.finalize().as_bytes()))
}
#[cfg(test)]
mod tests;