Skip to main content

surfer_translation_types/
lib.rs

1mod field_ref;
2pub mod plugin_types;
3#[cfg(feature = "pyo3")]
4pub mod python;
5mod result;
6mod scope_ref;
7pub mod translator;
8pub mod variable_index;
9mod variable_meta;
10mod variable_ref;
11
12use derive_more::Display;
13use ecolor::Color32;
14#[cfg(feature = "wasm_plugins")]
15use extism_convert::{FromBytes, Json, ToBytes};
16use num::{BigUint, ToPrimitive};
17use serde::{Deserialize, Serialize};
18use std::collections::HashMap;
19
20pub use crate::field_ref::FieldRef;
21pub use crate::result::{
22    HierFormatResult, SubFieldFlatTranslationResult, SubFieldTranslationResult, TranslatedValue,
23    TranslationResult, ValueRepr,
24};
25pub use crate::scope_ref::ScopeRef;
26pub use crate::translator::{
27    BasicTranslator, Translator, VariableNameInfo, WaveSource, translates_all_bit_types,
28};
29pub use crate::variable_index::VariableIndex;
30pub use crate::variable_meta::VariableMeta;
31pub use crate::variable_ref::VariableRef;
32
33#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
34#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
35#[derive(Deserialize, Serialize)]
36pub struct PluginConfig(pub HashMap<String, String>);
37
38/// Quiet NaN representing undefined (X) values in analog signals.
39pub const NAN_UNDEF: f64 = f64::from_bits(0x7FF8_0000_0000_0000_u64);
40
41/// Quiet NaN representing high-impedance (Z) values in analog signals.
42pub const NAN_HIGHIMP: f64 = f64::from_bits(0x7FF8_0000_0000_0001_u64);
43
44/// Check NaN payload to determine if it represents `HighImp`.
45#[must_use]
46pub fn is_nan_highimp(value: f64) -> bool {
47    value.to_bits() == NAN_HIGHIMP.to_bits()
48}
49
50/// Convert [`BigUint`] to f64 efficiently.
51///
52/// For values that fit in u64, uses direct conversion.
53/// For larger values, falls back to `ToPrimitive::to_f64()`.
54#[must_use]
55pub fn biguint_to_f64(v: &BigUint) -> f64 {
56    v.to_u64()
57        .map(|x| x as f64)
58        .or_else(|| v.to_f64())
59        .unwrap_or(f64::INFINITY)
60}
61
62/// Parse a translated string value into a numeric f64.
63///
64/// Uses the translator name to determine parsing strategy:
65/// - Names containing "hex" parse as hexadecimal
66/// - Names containing "bin" parse as binary
67/// - Otherwise tries decimal, with hex fallback
68///
69/// Returns `None` if the string cannot be parsed as a number.
70#[must_use]
71pub fn parse_numeric_string(s: &str, translator_name: &str) -> Option<f64> {
72    let s = s.trim();
73    let translator_lower = translator_name.to_lowercase();
74
75    if translator_lower.contains("hex") {
76        let hex_str = s
77            .strip_prefix("0x")
78            .or_else(|| s.strip_prefix("0X"))
79            .unwrap_or(s);
80        BigUint::parse_bytes(hex_str.as_bytes(), 16).map(|v| biguint_to_f64(&v))
81    } else if translator_lower.contains("bin") {
82        let bin_str = s
83            .strip_prefix("0b")
84            .or_else(|| s.strip_prefix("0B"))
85            .unwrap_or(s);
86        BigUint::parse_bytes(bin_str.as_bytes(), 2).map(|v| biguint_to_f64(&v))
87    } else {
88        if let Ok(v) = s.parse::<f64>() {
89            return Some(v);
90        }
91        // Fallback: try parsing as hex for non-decimal strings
92        BigUint::parse_bytes(s.as_bytes(), 16).map(|v| biguint_to_f64(&v))
93    }
94}
95
96/// Parse [`VariableValue`] to f64 using a conversion function.
97///
98/// Handles X/Z values by returning [`NAN_UNDEF`]/[`NAN_HIGHIMP`].
99/// For valid numeric values, applies the provided conversion function.
100#[must_use]
101pub fn parse_value_to_numeric(value: &VariableValue, to_f64: impl FnOnce(&BigUint) -> f64) -> f64 {
102    match value.parse_biguint() {
103        Ok(v) => to_f64(&v),
104        Err((_, ValueKind::HighImp)) => NAN_HIGHIMP,
105        Err((_, _)) => NAN_UNDEF,
106    }
107}
108
109/// Turn vector variable string into name and corresponding color if it
110/// includes values other than 0 and 1. If only 0 and 1, return None.
111/// Related to [`kind_for_binary_representation`], which returns only the kind.
112#[must_use]
113pub fn check_vector_variable(s: &str) -> Option<(String, ValueKind)> {
114    if s.contains('x') {
115        Some(("UNDEF".to_string(), ValueKind::Undef))
116    } else if s.contains('z') {
117        Some(("HIGHIMP".to_string(), ValueKind::HighImp))
118    } else if s.contains('-') {
119        Some(("DON'T CARE".to_string(), ValueKind::DontCare))
120    } else if s.contains('u') {
121        Some(("UNDEF".to_string(), ValueKind::Undef))
122    } else if s.contains('w') {
123        Some(("UNDEF WEAK".to_string(), ValueKind::Undef))
124    } else if s.contains('h') || s.contains('l') {
125        Some(("WEAK".to_string(), ValueKind::Weak))
126    } else if s.chars().all(|c| matches!(c, '0' | '1')) {
127        None
128    } else {
129        Some(("UNKNOWN VALUES".to_string(), ValueKind::Undef))
130    }
131}
132
133/// Return kind for a binary representation.
134/// Related to [`check_vector_variable`], which returns the same kinds, but also a string.
135/// For strings containing only 0 and 1, this function returns `ValueKind::Normal`.
136#[must_use]
137pub fn kind_for_binary_representation(s: &str) -> ValueKind {
138    if s.contains('x') {
139        ValueKind::Undef
140    } else if s.contains('z') {
141        ValueKind::HighImp
142    } else if s.contains('-') {
143        ValueKind::DontCare
144    } else if s.contains('u') || s.contains('w') {
145        ValueKind::Undef
146    } else if s.contains('h') || s.contains('l') {
147        ValueKind::Weak
148    } else {
149        ValueKind::Normal
150    }
151}
152
153/// VCD bit extension.
154/// This function extends the given string `val` to match `num_bits` by adding
155/// leading characters according to VCD rules:
156/// - '0' and '1' extend with '0'
157/// - 'x' extends with 'x'
158/// - 'z' extends with 'z'
159/// - other leading characters result in no extension
160#[must_use]
161pub fn extend_string(val: &str, num_bits: u32) -> String {
162    if num_bits as usize > val.len() {
163        let extra_count = num_bits as usize - val.len();
164        let extra_value = match val.chars().next() {
165            Some('0' | '1') => "0",
166            Some('x') => "x",
167            Some('z') => "z",
168            // Not part of standard, but useful for VHDL std_logic, at least for the mappings translator
169            Some('u') => "u",
170            Some('w') => "w",
171            Some('l') => "l",
172            Some('h') => "h",
173            Some('-') => "-",
174            // If we got weird characters, this is probably a string, so we don't
175            // do the extension
176            // We may have to add extensions for std_logic values though if simulators save without extension
177            _ => "",
178        };
179        extra_value.repeat(extra_count)
180    } else {
181        String::new()
182    }
183}
184
185#[derive(Debug, PartialEq, Eq, Clone, Display, Hash, Serialize, Deserialize)]
186/// The value of a variable in the waveform as obtained from the waveform source.
187///
188/// Represented either as an unsigned integer ([`BigUint`]) or as a raw [`String`] with one character per bit.
189pub enum VariableValue {
190    #[display("{_0}")]
191    BigUint(BigUint),
192    #[display("{_0}")]
193    String(String),
194}
195
196impl VariableValue {
197    /// Utility function for handling the happy case of the variable value being only 0 and 1,
198    /// with default handling of other values.
199    ///
200    /// The value passed to the handler is guaranteed to only contain 0 and 1, but it is not
201    /// padded to the length of the vector, i.e. leading zeros can be missing. Use [`extend_string`]
202    /// on the result to add the padding.
203    pub fn handle_bits<E>(
204        self,
205        handler: impl Fn(String) -> Result<TranslationResult, E>,
206    ) -> Result<TranslationResult, E> {
207        let value = match self {
208            VariableValue::BigUint(v) => format!("{v:b}"),
209            VariableValue::String(v) => {
210                if let Some((val, kind)) = check_vector_variable(&v) {
211                    return Ok(TranslationResult {
212                        val: ValueRepr::String(val),
213                        subfields: vec![],
214                        kind,
215                    });
216                }
217                // v contains only 0 and 1
218                v
219            }
220        };
221
222        handler(value)
223    }
224}
225
226#[derive(Clone, PartialEq, Copy, Debug, Serialize, Deserialize)]
227/// The kind of a translated value, used to determine how to color it in the UI.
228pub enum ValueKind {
229    Normal,
230    Undef,
231    HighImp,
232    Custom(Color32),
233    Warn,
234    DontCare,
235    Weak,
236    Error,
237    Event,
238}
239
240#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
241#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
242#[derive(PartialEq, Deserialize, Serialize, Debug)]
243pub enum TranslationPreference {
244    /// This translator prefers translating the variable, so it will be selected
245    /// as the default translator for the variable.
246    Prefer,
247    /// This translator is able to translate the variable, but will not be
248    /// selected by default, the user has to select it.
249    Yes,
250    /// This translator is not suitable to translate the variable,
251    /// but can be selected by the user in the "Not recommended" menu.
252    /// No guarantees are made about the correctness of the translation.
253    No,
254}
255
256/// Static information about the structure of a variable.
257#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
258#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
259#[derive(Clone, Debug, Default, Deserialize, Serialize)]
260pub enum VariableInfo {
261    /// A compound variable with subfields.
262    Compound {
263        subfields: Vec<(String, VariableInfo)>,
264    },
265    /// A flat bit-vector variable.
266    Bits,
267    /// A single-bit variable.
268    Bool,
269    /// A clock variable.
270    Clock,
271    // NOTE: only used for state saving where translators will clear this out with the actual value
272    #[default]
273    /// A string variable.
274    String,
275    /// A real-number variable.
276    Real,
277    Event,
278}
279
280#[derive(Debug, Display, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
281/// The type of variable based on information from the waveform source.
282pub enum VariableType {
283    // VCD-specific types
284    #[display("event")]
285    VCDEvent,
286    #[display("reg")]
287    VCDReg,
288    #[display("wire")]
289    VCDWire,
290    #[display("real")]
291    VCDReal,
292    #[display("time")]
293    VCDTime,
294    #[display("string")]
295    VCDString,
296    #[display("parameter")]
297    VCDParameter,
298    #[display("integer")]
299    VCDInteger,
300    #[display("real time")]
301    VCDRealTime,
302    #[display("supply 0")]
303    VCDSupply0,
304    #[display("supply 1")]
305    VCDSupply1,
306    #[display("tri")]
307    VCDTri,
308    #[display("tri and")]
309    VCDTriAnd,
310    #[display("tri or")]
311    VCDTriOr,
312    #[display("tri reg")]
313    VCDTriReg,
314    #[display("tri 0")]
315    VCDTri0,
316    #[display("tri 1")]
317    VCDTri1,
318    #[display("wand")]
319    VCDWAnd,
320    #[display("wor")]
321    VCDWOr,
322    #[display("port")]
323    Port,
324    #[display("sparse array")]
325    SparseArray,
326    #[display("realtime")]
327    RealTime,
328
329    // System Verilog
330    #[display("bit")]
331    Bit,
332    #[display("logic")]
333    Logic,
334    #[display("int")]
335    Int,
336    #[display("shortint")]
337    ShortInt,
338    #[display("longint")]
339    LongInt,
340    #[display("byte")]
341    Byte,
342    #[display("enum")]
343    Enum,
344    #[display("shortreal")]
345    ShortReal,
346    #[display("real parameter")]
347    RealParameter,
348
349    // VHDL (these are the types emitted by GHDL)
350    #[display("boolean")]
351    Boolean,
352    #[display("bit_vector")]
353    BitVector,
354    #[display("std_logic")]
355    StdLogic,
356    #[display("std_logic_vector")]
357    StdLogicVector,
358    #[display("std_ulogic")]
359    StdULogic,
360    #[display("std_ulogic_vector")]
361    StdULogicVector,
362}
363
364#[derive(Clone, Display, Copy, PartialOrd, Debug, Eq, PartialEq, Serialize, Deserialize)]
365pub enum VariableDirection {
366    // Ordering is used for sorting variable list
367    #[display("input")]
368    Input,
369    #[display("output")]
370    Output,
371    #[display("inout")]
372    InOut,
373    #[display("buffer")]
374    Buffer,
375    #[display("linkage")]
376    Linkage,
377    #[display("implicit")]
378    Implicit,
379    #[display("unknown")]
380    Unknown,
381}
382
383#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
384/// Variable values can be encoded in different ways in the waveform source.
385pub enum VariableEncoding {
386    String,
387    Real,
388    BitVector,
389    Event,
390}
391
392#[cfg(test)]
393mod tests {
394    use super::{ValueKind, check_vector_variable, extend_string, parse_numeric_string};
395
396    #[test]
397    fn binary_only_returns_none() {
398        for s in ["0", "1", "0101", "1111", "000000", "101010"].iter() {
399            assert_eq!(check_vector_variable(s), None, "{s}");
400        }
401    }
402
403    #[test]
404    fn x_marks_undef() {
405        let res = check_vector_variable("10x01").unwrap();
406        assert_eq!(res.0, "UNDEF");
407        assert_eq!(res.1, ValueKind::Undef);
408    }
409
410    #[test]
411    fn u_marks_undef() {
412        for s in ["u", "10u", "uuuu"].iter() {
413            let res = check_vector_variable(s).unwrap();
414            assert_eq!(res.0, "UNDEF");
415            assert_eq!(res.1, ValueKind::Undef);
416        }
417    }
418
419    #[test]
420    fn z_marks_highimp() {
421        let res = check_vector_variable("zz01").unwrap();
422        assert_eq!(res.0, "HIGHIMP");
423        assert_eq!(res.1, ValueKind::HighImp);
424    }
425
426    #[test]
427    fn dash_marks_dont_care() {
428        let res = check_vector_variable("-01--").unwrap();
429        assert_eq!(res.0, "DON'T CARE");
430        assert_eq!(res.1, ValueKind::DontCare);
431    }
432
433    #[test]
434    fn w_marks_undef_weak() {
435        let res = check_vector_variable("w101").unwrap();
436        assert_eq!(res.0, "UNDEF WEAK");
437        assert_eq!(res.1, ValueKind::Undef); // intentionally Undef per implementation
438    }
439
440    #[test]
441    fn h_or_l_marks_weak() {
442        let res_h = check_vector_variable("h110").unwrap();
443        assert_eq!(res_h.0, "WEAK");
444        assert_eq!(res_h.1, ValueKind::Weak);
445
446        let res_l = check_vector_variable("l001").unwrap();
447        assert_eq!(res_l.0, "WEAK");
448        assert_eq!(res_l.1, ValueKind::Weak);
449    }
450
451    #[test]
452    fn unknown_values_fallback() {
453        for s in ["2", "a", "?", " "] {
454            let res = check_vector_variable(s).unwrap();
455            assert_eq!(res.0, "UNKNOWN VALUES");
456            assert_eq!(res.1, ValueKind::Undef);
457        }
458    }
459
460    #[test]
461    fn precedence_is_respected() {
462        // contains both x and z -> x handled first (UNDEF)
463        let res = check_vector_variable("xz").unwrap();
464        assert_eq!(res.0, "UNDEF");
465        assert_eq!(res.1, ValueKind::Undef);
466
467        // contains w and h -> w handled before h, yielding UNDEF WEAK not WEAK
468        let res = check_vector_variable("wh").unwrap();
469        assert_eq!(res.0, "UNDEF WEAK");
470        assert_eq!(res.1, ValueKind::Undef);
471    }
472
473    // ---------------- extend_string tests ----------------
474
475    #[test]
476    fn extend_string_zero_extend_from_0_and_1() {
477        // Leading '0' extends with '0'
478        assert_eq!(extend_string("001", 5), "00");
479        assert_eq!(extend_string("0", 3), "00");
480
481        // Leading '1' also extends with '0' per current implementation
482        assert_eq!(extend_string("101", 5), "00");
483        assert_eq!(extend_string("1", 4), "000");
484    }
485
486    #[test]
487    fn extend_string_x_and_z() {
488        // Leading 'x' extends with 'x'
489        assert_eq!(extend_string("x1", 4), "xx");
490        assert_eq!(extend_string("x", 3), "xx");
491
492        // Leading 'z' extends with 'z'
493        assert_eq!(extend_string("z0", 3), "z");
494        assert_eq!(extend_string("z", 5), "zzzz");
495    }
496
497    #[test]
498    fn extend_string_same_or_smaller_returns_empty() {
499        assert_eq!(extend_string("101", 3), "");
500        assert_eq!(extend_string("101", 2), "");
501        assert_eq!(extend_string("", 0), "");
502    }
503
504    #[test]
505    fn extend_string_weird_char_and_empty_input() {
506        // Unknown leading char results in no extension (empty), even if num_bits is larger
507        assert_eq!(extend_string("h101", 6), "");
508        assert_eq!(extend_string("?", 10), "");
509
510        // Empty input yields empty extension as there is no leading char to guide
511        assert_eq!(extend_string("", 5), "");
512    }
513
514    // ---------------- parse_numeric_string tests ----------------
515
516    #[test]
517    fn parse_numeric_string_hex() {
518        assert_eq!(parse_numeric_string("f9", "Hex"), Some(249.0));
519        assert_eq!(parse_numeric_string("ca", "Hexadecimal"), Some(202.0));
520        assert_eq!(parse_numeric_string("80", "Hex"), Some(128.0));
521        assert_eq!(parse_numeric_string("10", "Hex"), Some(16.0));
522        assert_eq!(parse_numeric_string("0x10", "Hex"), Some(16.0));
523        assert_eq!(parse_numeric_string("0xFF", "Hexadecimal"), Some(255.0));
524    }
525
526    #[test]
527    fn parse_numeric_string_decimal() {
528        assert_eq!(parse_numeric_string("123", "Unsigned"), Some(123.0));
529        assert_eq!(parse_numeric_string("123.45", "Float"), Some(123.45));
530        assert_eq!(parse_numeric_string("80", "Unsigned"), Some(80.0));
531        assert_eq!(parse_numeric_string("10", "Signed"), Some(10.0));
532        assert_eq!(parse_numeric_string("1.5e3", "Float"), Some(1500.0));
533        assert_eq!(parse_numeric_string("-3.14e-2", "Float"), Some(-0.0314));
534    }
535
536    #[test]
537    fn parse_numeric_string_binary() {
538        assert_eq!(parse_numeric_string("1010", "Binary"), Some(10.0));
539        assert_eq!(parse_numeric_string("0b1010", "Binary"), Some(10.0));
540        assert_eq!(parse_numeric_string("11111111", "Bin"), Some(255.0));
541    }
542
543    #[test]
544    fn parse_numeric_string_fallback_to_hex() {
545        // Fallback to hex for non-decimal strings
546        assert_eq!(parse_numeric_string("f9", "Unsigned"), Some(249.0));
547        assert_eq!(parse_numeric_string("ca", "Signed"), Some(202.0));
548    }
549
550    #[test]
551    fn parse_numeric_string_invalid() {
552        assert_eq!(parse_numeric_string("xyz", "Hex"), None);
553        assert_eq!(parse_numeric_string("invalid", "Unsigned"), None);
554        assert_eq!(parse_numeric_string("12", "Binary"), None);
555    }
556
557    #[test]
558    fn parse_numeric_string_large_values() {
559        // 128-bit max value in hex: 2^128 - 1 = 340282366920938463463374607431768211455
560        let hex_128bit = "ffffffffffffffffffffffffffffffff";
561        assert_eq!(
562            parse_numeric_string(hex_128bit, "Hexadecimal"),
563            Some(3.402823669209385e38)
564        );
565
566        // 256-bit max value in hex: 2^256 - 1
567        let hex_256bit = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
568        assert_eq!(
569            parse_numeric_string(hex_256bit, "Hex"),
570            Some(1.157920892373162e77)
571        );
572
573        // Large binary value (128 bits): same as hex_128bit
574        let bin_128bit = "1111111111111111111111111111111111111111111111111111111111111111\
575                          1111111111111111111111111111111111111111111111111111111111111111";
576        assert_eq!(
577            parse_numeric_string(bin_128bit, "Binary"),
578            Some(3.402823669209385e38)
579        );
580
581        // 64-bit max value in hex: 2^64 - 1 = 18446744073709551615
582        let hex_64bit = "ffffffffffffffff";
583        assert_eq!(
584            parse_numeric_string(hex_64bit, "Hexadecimal"),
585            Some(1.8446744073709552e19)
586        );
587
588        // Hex and Unsigned should produce same result for same numeric value
589        // 128-bit value as decimal string (from Unsigned translator)
590        let decimal_128bit = "340282366920938463463374607431768211455";
591        assert_eq!(
592            parse_numeric_string(decimal_128bit, "Unsigned"),
593            Some(3.402823669209385e38)
594        );
595    }
596}