Skip to main content

haz_cache/key/
prefix.rs

1//! Schema-version prefix (`CACHE-003`).
2//!
3//! Every cache key incorporates a two-byte prefix before its other
4//! components: a `chapter_revision` byte and a `hash_function_id`
5//! byte. The two bytes are kept distinct so the cross-product of
6//! chapter revisions and hash functions yields a unique prefix for
7//! every pair. Combining them via XOR or addition before hashing is
8//! a normative violation (`CACHE-003`).
9
10use haz_domain::settings::cache::HashAlgo;
11
12/// Revision of the cache-key composition rules in
13/// `docs/spec/09-caching.md`.
14///
15/// Bumped only on a normative change to `CACHE-004`..`CACHE-009`.
16/// Bumping invalidates every prior cache entry by construction:
17/// the prefix changes and no current lookup re-derives the same
18/// key for content already stored.
19pub const CHAPTER_REVISION: u8 = 0;
20
21/// The single byte identifying `algo` in the cache-key prefix,
22/// matching the registry in `CACHE-002`.
23///
24/// The registry is append-only: a new value MUST receive a
25/// previously-unused id.
26#[must_use]
27pub fn hash_function_id(algo: HashAlgo) -> u8 {
28    match algo {
29        HashAlgo::Blake3 => 0x00,
30        HashAlgo::Sha256 => 0x01,
31    }
32}
33
34/// The two-byte schema-version prefix for `algo`.
35///
36/// Layout: `[CHAPTER_REVISION, hash_function_id(algo)]`. Always
37/// fed into the hasher first, ahead of every other component.
38#[must_use]
39pub fn schema_version_prefix(algo: HashAlgo) -> [u8; 2] {
40    [CHAPTER_REVISION, hash_function_id(algo)]
41}
42
43#[cfg(test)]
44mod tests {
45    use haz_domain::settings::cache::HashAlgo;
46
47    use crate::key::prefix::{CHAPTER_REVISION, hash_function_id, schema_version_prefix};
48
49    #[test]
50    fn cache_003_initial_chapter_revision_is_zero() {
51        assert_eq!(CHAPTER_REVISION, 0);
52    }
53
54    #[test]
55    fn cache_002_blake3_hash_function_id_is_zero() {
56        assert_eq!(hash_function_id(HashAlgo::Blake3), 0x00);
57    }
58
59    #[test]
60    fn cache_002_sha256_hash_function_id_is_one() {
61        assert_eq!(hash_function_id(HashAlgo::Sha256), 0x01);
62    }
63
64    #[test]
65    fn cache_003_prefix_keeps_the_two_bytes_distinct() {
66        let blake = schema_version_prefix(HashAlgo::Blake3);
67        let sha = schema_version_prefix(HashAlgo::Sha256);
68        assert_eq!(blake, [0, 0]);
69        assert_eq!(sha, [0, 1]);
70        // Re-asserts CACHE-003: the bytes are NOT collapsed into a
71        // single byte by XOR/addition.
72        assert_ne!(blake[1], sha[1]);
73        assert_eq!(blake[0], sha[0]);
74    }
75}