Skip to main content

vexil_codegen_rust/
delta.rs

1use vexil_lang::ast::PrimitiveType;
2use vexil_lang::ir::{Encoding, FieldEncoding, MessageDef, ResolvedType, TypeRegistry};
3
4use crate::emit::CodeWriter;
5use crate::message::{emit_read, emit_write};
6use crate::types::rust_type;
7
8use std::collections::HashSet;
9use vexil_lang::ir::TypeId;
10
11// ---------------------------------------------------------------------------
12// Helpers
13// ---------------------------------------------------------------------------
14
15/// Returns true if a field uses delta encoding.
16fn is_delta(enc: &FieldEncoding) -> bool {
17    matches!(enc.encoding, Encoding::Delta(_))
18}
19
20/// Returns true if the type is a float primitive (f32 or f64).
21fn is_float(ty: &ResolvedType) -> bool {
22    matches!(
23        ty,
24        ResolvedType::Primitive(PrimitiveType::F32) | ResolvedType::Primitive(PrimitiveType::F64)
25    )
26}
27
28/// Returns the zero literal for delta prev-state initialisation.
29fn zero_literal(ty: &ResolvedType) -> &'static str {
30    match ty {
31        ResolvedType::Primitive(PrimitiveType::F32) => "0.0_f32",
32        ResolvedType::Primitive(PrimitiveType::F64) => "0.0_f64",
33        _ => "0",
34    }
35}
36
37/// Strip the outer `Delta` wrapper from a `FieldEncoding`, returning the inner
38/// encoding.  If the encoding is not `Delta`, returns a clone unchanged.
39fn strip_delta(enc: &FieldEncoding) -> FieldEncoding {
40    match &enc.encoding {
41        Encoding::Delta(inner) => FieldEncoding {
42            encoding: *inner.clone(),
43            limit: enc.limit,
44        },
45        _ => enc.clone(),
46    }
47}
48
49// ---------------------------------------------------------------------------
50// Public entry point
51// ---------------------------------------------------------------------------
52
53/// Emit `{Name}Encoder` and `{Name}Decoder` structs for a message that
54/// contains at least one `@delta` field.
55///
56/// If the message has no delta fields this function is a no-op.
57pub fn emit_delta(w: &mut CodeWriter, msg: &MessageDef, registry: &TypeRegistry) {
58    // Collect delta fields up-front.
59    let delta_fields: Vec<_> = msg
60        .fields
61        .iter()
62        .filter(|f| is_delta(&f.encoding))
63        .collect();
64
65    if delta_fields.is_empty() {
66        return;
67    }
68
69    let name = msg.name.as_str();
70    // We never need Box wrapping for the generated encoder/decoder fields — they
71    // only store scalar prev-state values.
72    let empty_needs_box: HashSet<(TypeId, usize)> = HashSet::new();
73
74    // -----------------------------------------------------------------------
75    // {Name}Encoder
76    // -----------------------------------------------------------------------
77
78    w.open_block(&format!("pub struct {name}Encoder"));
79    for field in &delta_fields {
80        let rust_ty = rust_type(&field.resolved_type, registry, &empty_needs_box, None);
81        w.line(&format!("prev_{}: {},", field.name, rust_ty));
82    }
83    w.close_block();
84    w.blank();
85
86    w.open_block(&format!("impl {name}Encoder"));
87
88    // new()
89    w.open_block("pub fn new() -> Self");
90    w.open_block("Self");
91    for field in &delta_fields {
92        let zero = zero_literal(&field.resolved_type);
93        w.line(&format!("prev_{}: {},", field.name, zero));
94    }
95    w.close_block();
96    w.close_block();
97    w.blank();
98
99    // pack()
100    w.open_block(&format!(
101        "pub fn pack(&mut self, val: &{name}, w: &mut vexil_runtime::BitWriter) -> Result<(), vexil_runtime::EncodeError>"
102    ));
103    for field in &msg.fields {
104        let fname = field.name.as_str();
105        if is_delta(&field.encoding) {
106            if is_float(&field.resolved_type) {
107                w.line(&format!(
108                    "let delta_{fname} = val.{fname} - self.prev_{fname};"
109                ));
110            } else {
111                w.line(&format!(
112                    "let delta_{fname} = val.{fname}.wrapping_sub(self.prev_{fname});"
113                ));
114            }
115            // Emit write using the INNER encoding (without the Delta wrapper).
116            let inner_enc = strip_delta(&field.encoding);
117            emit_write(
118                w,
119                &format!("delta_{fname}"),
120                &field.resolved_type,
121                &inner_enc,
122                registry,
123                fname,
124            );
125            w.line(&format!("self.prev_{fname} = val.{fname};"));
126        } else {
127            let access = format!("val.{fname}");
128            emit_write(
129                w,
130                &access,
131                &field.resolved_type,
132                &field.encoding,
133                registry,
134                fname,
135            );
136        }
137    }
138    w.line("w.flush_to_byte_boundary();");
139    w.open_block("if !val._unknown.is_empty()");
140    w.line("w.write_raw_bytes(&val._unknown);");
141    w.close_block();
142    w.line("Ok(())");
143    w.close_block();
144    w.blank();
145
146    // reset()
147    w.open_block("pub fn reset(&mut self)");
148    for field in &delta_fields {
149        let zero = zero_literal(&field.resolved_type);
150        w.line(&format!("self.prev_{} = {};", field.name, zero));
151    }
152    w.close_block();
153
154    w.close_block(); // impl {Name}Encoder
155    w.blank();
156
157    // -----------------------------------------------------------------------
158    // {Name}Decoder
159    // -----------------------------------------------------------------------
160
161    w.open_block(&format!("pub struct {name}Decoder"));
162    for field in &delta_fields {
163        let rust_ty = rust_type(&field.resolved_type, registry, &empty_needs_box, None);
164        w.line(&format!("prev_{}: {},", field.name, rust_ty));
165    }
166    w.close_block();
167    w.blank();
168
169    w.open_block(&format!("impl {name}Decoder"));
170
171    // new()
172    w.open_block("pub fn new() -> Self");
173    w.open_block("Self");
174    for field in &delta_fields {
175        let zero = zero_literal(&field.resolved_type);
176        w.line(&format!("prev_{}: {},", field.name, zero));
177    }
178    w.close_block();
179    w.close_block();
180    w.blank();
181
182    // unpack()
183    w.open_block(&format!(
184        "pub fn unpack(&mut self, r: &mut vexil_runtime::BitReader<'_>) -> Result<{name}, vexil_runtime::DecodeError>"
185    ));
186    for field in &msg.fields {
187        let fname = field.name.as_str();
188        if is_delta(&field.encoding) {
189            let inner_enc = strip_delta(&field.encoding);
190            let delta_var = format!("delta_{fname}");
191            // Read the delta value using the INNER encoding.
192            emit_read(
193                w,
194                &delta_var,
195                &field.resolved_type,
196                &inner_enc,
197                registry,
198                fname,
199            );
200            // Reconstruct the original value.
201            if is_float(&field.resolved_type) {
202                w.line(&format!("let {fname} = self.prev_{fname} + {delta_var};"));
203            } else {
204                w.line(&format!(
205                    "let {fname} = self.prev_{fname}.wrapping_add({delta_var});"
206                ));
207            }
208            w.line(&format!("self.prev_{fname} = {fname};"));
209        } else {
210            emit_read(
211                w,
212                fname,
213                &field.resolved_type,
214                &field.encoding,
215                registry,
216                fname,
217            );
218        }
219    }
220    w.line("r.flush_to_byte_boundary();");
221    w.line("let _unknown = r.read_remaining();");
222    w.open_block(&format!("Ok({name}"));
223    for field in &msg.fields {
224        w.line(&format!("{},", field.name));
225    }
226    w.line("_unknown,");
227    w.dedent();
228    w.line("})");
229
230    w.close_block(); // fn unpack
231    w.blank();
232
233    // reset()
234    w.open_block("pub fn reset(&mut self)");
235    for field in &delta_fields {
236        let zero = zero_literal(&field.resolved_type);
237        w.line(&format!("self.prev_{} = {};", field.name, zero));
238    }
239    w.close_block();
240
241    w.close_block(); // impl {Name}Decoder
242    w.blank();
243}