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