sva_engine/refs/
identity.rs1use sva_formula::{
4 ClosedForm, Hash, NodeId, SpectralSum, Var, hash_closed_form, hash_spectral_sum,
5};
6
7use crate::error::{Diagnostic, EngineError};
8use crate::offset::Offset;
9use crate::typing::{Typing, Value};
10
11use super::{cyclic, nodes_in, spectral_sum_of, substituted_closed_form};
12
13pub fn symbolic_hash(typing: &Typing, node: NodeId, want: Var) -> Result<Hash, EngineError> {
15 spectral_sum_of(typing, node, want).map(|n| hash_spectral_sum(&n))
16}
17
18pub fn identity(typing: &Typing, node: NodeId) -> Result<Hash, EngineError> {
20 identity_of(typing, node, &mut Vec::new())
21}
22
23fn identity_of(typing: &Typing, node: NodeId, open: &mut Vec<NodeId>) -> Result<Hash, EngineError> {
24 if typing.ty(node).is_closed_form() {
25 let named = closed_form_identity(
26 &spectral_sum_of(typing, node, typing.var(node)),
27 substituted_closed_form(typing, node).as_ref(),
28 );
29 return match named {
30 Ok(hash) => Ok(hash),
31 Err(form) => built(typing, node, open).map_err(|tree| neither(form, tree)),
32 };
33 }
34 built(typing, node, open)
35}
36
37fn neither(form: EngineError, tree: EngineError) -> EngineError {
40 EngineError::refused(Diagnostic {
41 code: tree.code().to_string(),
42 message: format!("{form} and the tree it was built from: {tree}"),
43 location: tree.at().or_else(|| form.at()).cloned().unwrap_or_default(),
44 help: "write the construct inside the node it reads, or sample it".to_string(),
45 })
46}
47
48fn built(typing: &Typing, node: NodeId, open: &mut Vec<NodeId>) -> Result<Hash, EngineError> {
49 if open.contains(&node) {
50 return Err(cyclic(typing, node));
51 }
52 open.push(node);
53 let mut sink = Sink::new();
54 match typing.value(node) {
55 Value::ClosedForm(form) => {
56 sink.text("closed form");
57 sink.hash(hash_closed_form(form));
58 for id in nodes_in(&form.body) {
59 sink.hash(identity_of(typing, id, open)?);
60 }
61 }
62 Value::Cast(cast, source) => {
63 sink.text(cast.name());
64 sink.hash(identity_of(typing, *source, open)?);
65 }
66 Value::Read { source, at, .. } => {
67 sink.text("read");
68 sink.hash(identity_of(typing, *source, open)?);
69 match at {
70 Offset::Steps(steps) => {
71 sink.text("sp");
72 sink.word(*steps as u64);
73 }
74 Offset::Secs(secs) => {
75 sink.text("s");
76 sink.word(secs.to_bits());
77 }
78 }
79 }
80 Value::SelfAt(delay) => sink.text(&format!("self {delay:?}")),
81 Value::Grid(count) => sink.text(&format!("sp {count}")),
82 Value::Solver(params) => sink.text(&format!("{params:?}")),
83 Value::Filter {
84 shape,
85 x,
86 cutoff,
87 q,
88 gain,
89 } => {
90 sink.text(shape.name());
91 for operand in [x, cutoff, q, gain] {
92 sink.hash(identity_of(typing, *operand, open)?);
93 }
94 }
95 Value::Op { name, args } => {
96 sink.text(name);
97 for arg in args {
98 sink.hash(identity_of(typing, *arg, open)?);
99 }
100 }
101 }
102 open.pop();
103 Ok(sink.finish())
104}
105
106const IDENTITY_ROTATE: u32 = 23;
107
108struct Sink(sva_formula::Lanes<IDENTITY_ROTATE>);
109
110impl Sink {
111 fn new() -> Sink {
112 Sink(sva_formula::Lanes::default())
113 }
114
115 fn word(&mut self, part: u64) {
116 self.0.word(part);
117 }
118
119 fn text(&mut self, what: &str) {
120 self.word(what.len() as u64);
121 for byte in what.as_bytes() {
122 self.word(u64::from(*byte));
123 }
124 }
125
126 fn hash(&mut self, held: Hash) {
127 self.word(held.0);
128 self.word(held.1);
129 }
130
131 fn finish(&self) -> Hash {
132 self.0.finish()
133 }
134}
135
136pub fn closed_form_identity(
138 sum: &Result<SpectralSum, EngineError>,
139 written: Option<&ClosedForm>,
140) -> Result<Hash, EngineError> {
141 match (sum, written) {
142 (Ok(sum), _) => Ok(hash_spectral_sum(sum)),
143 (Err(_), Some(form)) => Ok(hash_closed_form(form)),
144 (Err(e), None) => Err(e.clone()),
145 }
146}