tawasuyu

tawasuyu/brahman

Rust 496TOML 195Markdown 55JSON 197001 Definiciones

⎇ 53dbdf0f1deea10d6fc567ea2b43e1e75d6d60cd crates/modules/semantic_dht/minga-core/src/cas.rs Rust · 95 líneas · 2.6 KB Historia Quién tocó qué crudo

chore: monorepo inicial con arje + minga + yahweh absorbidos 53dbdf0f1 · Sergio ·

1use crate::ast::SemanticNode;
2use blake3::Hasher;
3
4/// Hash de 32 bytes que identifica unívocamente un `SemanticNode` por su
5/// estructura lógica. Dos nodos con misma estructura → mismo hash, sin
6/// importar formato, comentarios o posición en el archivo fuente.
7#[derive(
8    Debug,
9    Clone,
10    Copy,
11    PartialEq,
12    Eq,
13    Hash,
14    PartialOrd,
15    Ord,
16    serde::Serialize,
17    serde::Deserialize,
18)]
19#[serde(transparent)]
20pub struct ContentHash(pub [u8; 32]);
21
22impl ContentHash {
23    pub fn as_bytes(&self) -> &[u8; 32] {
24        &self.0
25    }
26}
27
28impl std::fmt::Display for ContentHash {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        for b in &self.0 {
31            write!(f, "{:02x}", b)?;
32        }
33        Ok(())
34    }
35}
36
37/// Hash Merkle de un `SemanticNode`. El hash es función pura de
38/// `(kind, field_name, leaf_text, &[child_hash])`. Esquema estricto:
39/// los hijos contribuyen como hash, no como bytestream completo. Eso
40/// permite verificar un nodo recibido por la red **sin tener** sus
41/// hijos: basta con tener los hashes de los hijos (que vienen en el
42/// `StoredNode.children`) y reproducir esta función.
43pub fn hash_node(node: &SemanticNode) -> ContentHash {
44    let child_hashes: Vec<ContentHash> = node.children.iter().map(hash_node).collect();
45    hash_components(
46        &node.kind,
47        node.field_name.as_deref(),
48        node.leaf_text.as_deref(),
49        &child_hashes,
50    )
51}
52
53/// Primitiva canónica del hash estructural. Es la única definición
54/// authoritativa: cualquier otra función que produzca un hash de
55/// contenido debe expresarse encima de ésta. Garantiza que
56/// `hash_node(&semantic)` y `hash_stored(&stored)` coincidan bit a bit
57/// para representaciones equivalentes del mismo árbol.
58pub fn hash_components(
59    kind: &str,
60    field_name: Option<&str>,
61    leaf_text: Option<&[u8]>,
62    child_hashes: &[ContentHash],
63) -> ContentHash {
64    let mut h = Hasher::new();
65    write_str(&mut h, kind);
66    match field_name {
67        Some(f) => {
68            h.update(&[1]);
69            write_str(&mut h, f);
70        }
71        None => {
72            h.update(&[0]);
73        }
74    }
75    match leaf_text {
76        Some(t) => {
77            h.update(&[1]);
78            h.update(&(t.len() as u64).to_le_bytes());
79            h.update(t);
80        }
81        None => {
82            h.update(&[0]);
83        }
84    }
85    h.update(&(child_hashes.len() as u64).to_le_bytes());
86    for ch in child_hashes {
87        h.update(&ch.0);
88    }
89    ContentHash(*h.finalize().as_bytes())
90}
91
92fn write_str(h: &mut Hasher, s: &str) {
93    h.update(&(s.len() as u64).to_le_bytes());
94    h.update(s.as_bytes());
95}