use core::fmt;
use vize_l0::Span;
use vize_l0::hash::StableHasher128;
use super::{ArtifactKey, rebase};
use crate::stage::Stage;
const DOMAIN: &[u8] = b"vize.artifact-key.v2\0";
pub(super) const MANIFEST_DOMAIN: &[u8] = b"vize.key-manifest.v2\0";
const SPAN_RELATIVE: u8 = 0;
const SPAN_OUTSIDE: u8 = 1;
#[derive(Clone)]
pub struct KeySink {
hasher: StableHasher128,
stage: Stage,
schema_version: u32,
block_start: u32,
}
impl KeySink {
#[must_use]
pub fn new(stage: Stage, schema_version: u32, block_start: u32) -> Self {
Self::in_domain(DOMAIN, stage, schema_version, block_start)
}
pub(super) fn in_domain(
domain: &[u8],
stage: Stage,
schema_version: u32,
block_start: u32,
) -> Self {
let mut hasher = StableHasher128::new();
hasher.update(domain);
let id = stage.wire_id().as_bytes();
hasher.update(&(id.len() as u32).to_le_bytes());
hasher.update(id);
hasher.update(&schema_version.to_le_bytes());
Self {
hasher,
stage,
schema_version,
block_start,
}
}
#[must_use]
pub const fn block_start(&self) -> u32 {
self.block_start
}
pub(super) fn feed_digest(&mut self, digest: &[u8; 16]) {
self.hasher.update(digest);
}
pub fn feed_tag(&mut self, tag: u8) {
self.hasher.update(&[tag]);
}
pub fn feed_u32(&mut self, value: u32) {
self.hasher.update(&value.to_le_bytes());
}
pub fn feed_str(&mut self, value: &str) {
self.hasher.update(&(value.len() as u64).to_le_bytes());
self.hasher.update(value.as_bytes());
}
pub fn feed_span(&mut self, span: Span) {
let (tag, span) = match rebase(span, self.block_start) {
Some(relative) => (SPAN_RELATIVE, relative),
None => (SPAN_OUTSIDE, span),
};
self.feed_tag(tag);
self.feed_u32(span.start);
self.feed_u32(span.end);
}
#[must_use]
pub fn finish(self) -> ArtifactKey {
ArtifactKey {
stage: self.stage,
schema_version: self.schema_version,
hash: self.hasher.digest(),
}
}
}
impl fmt::Write for KeySink {
fn write_str(&mut self, text: &str) -> fmt::Result {
self.hasher.update(text.as_bytes());
Ok(())
}
}