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    #[display("event")]
363    EventParameter,
364}
365
366#[derive(Clone, Display, Copy, PartialOrd, Debug, Eq, PartialEq, Serialize, Deserialize)]
367pub enum VariableDirection {
368    // Ordering is used for sorting variable list
369    #[display("input")]
370    Input,
371    #[display("output")]
372    Output,
373    #[display("inout")]
374    InOut,
375    #[display("buffer")]
376    Buffer,
377    #[display("linkage")]
378    Linkage,
379    #[display("implicit")]
380    Implicit,
381    #[display("unknown")]
382    Unknown,
383}
384
385#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
386/// Variable values can be encoded in different ways in the waveform source.
387pub enum VariableEncoding {
388    String,
389    Real,
390    BitVector,
391}
392
393#[cfg(test)]
394mod tests {
395    use super::{ValueKind, check_vector_variable, extend_string, parse_numeric_string};
396
397    #[test]
398    fn binary_only_returns_none() {
399        for s in ["0", "1", "0101", "1111", "000000", "101010"].iter() {
400            assert_eq!(check_vector_variable(s), None, "{s}");
401        }
402    }
403
404    #[test]
405    fn x_marks_undef() {
406        let res = check_vector_variable("10x01").unwrap();
407        assert_eq!(res.0, "UNDEF");
408        assert_eq!(res.1, ValueKind::Undef);
409    }
410
411    #[test]
412    fn u_marks_undef() {
413        for s in ["u", "10u", "uuuu"].iter() {
414            let res = check_vector_variable(s).unwrap();
415            assert_eq!(res.0, "UNDEF");
416            assert_eq!(res.1, ValueKind::Undef);
417        }
418    }
419
420    #[test]
421    fn z_marks_highimp() {
422        let res = check_vector_variable("zz01").unwrap();
423        assert_eq!(res.0, "HIGHIMP");
424        assert_eq!(res.1, ValueKind::HighImp);
425    }
426
427    #[test]
428    fn dash_marks_dont_care() {
429        let res = check_vector_variable("-01--").unwrap();
430        assert_eq!(res.0, "DON'T CARE");
431        assert_eq!(res.1, ValueKind::DontCare);
432    }
433
434    #[test]
435    fn w_marks_undef_weak() {
436        let res = check_vector_variable("w101").unwrap();
437        assert_eq!(res.0, "UNDEF WEAK");
438        assert_eq!(res.1, ValueKind::Undef); // intentionally Undef per implementation
439    }
440
441    #[test]
442    fn h_or_l_marks_weak() {
443        let res_h = check_vector_variable("h110").unwrap();
444        assert_eq!(res_h.0, "WEAK");
445        assert_eq!(res_h.1, ValueKind::Weak);
446
447        let res_l = check_vector_variable("l001").unwrap();
448        assert_eq!(res_l.0, "WEAK");
449        assert_eq!(res_l.1, ValueKind::Weak);
450    }
451
452    #[test]
453    fn unknown_values_fallback() {
454        for s in ["2", "a", "?", " "] {
455            let res = check_vector_variable(s).unwrap();
456            assert_eq!(res.0, "UNKNOWN VALUES");
457            assert_eq!(res.1, ValueKind::Undef);
458        }
459    }
460
461    #[test]
462    fn precedence_is_respected() {
463        // contains both x and z -> x handled first (UNDEF)
464        let res = check_vector_variable("xz").unwrap();
465        assert_eq!(res.0, "UNDEF");
466        assert_eq!(res.1, ValueKind::Undef);
467
468        // contains w and h -> w handled before h, yielding UNDEF WEAK not WEAK
469        let res = check_vector_variable("wh").unwrap();
470        assert_eq!(res.0, "UNDEF WEAK");
471        assert_eq!(res.1, ValueKind::Undef);
472    }
473
474    // ---------------- extend_string tests ----------------
475
476    #[test]
477    fn extend_string_zero_extend_from_0_and_1() {
478        // Leading '0' extends with '0'
479        assert_eq!(extend_string("001", 5), "00");
480        assert_eq!(extend_string("0", 3), "00");
481
482        // Leading '1' also extends with '0' per current implementation
483        assert_eq!(extend_string("101", 5), "00");
484        assert_eq!(extend_string("1", 4), "000");
485    }
486
487    #[test]
488    fn extend_string_x_and_z() {
489        // Leading 'x' extends with 'x'
490        assert_eq!(extend_string("x1", 4), "xx");
491        assert_eq!(extend_string("x", 3), "xx");
492
493        // Leading 'z' extends with 'z'
494        assert_eq!(extend_string("z0", 3), "z");
495        assert_eq!(extend_string("z", 5), "zzzz");
496    }
497
498    #[test]
499    fn extend_string_same_or_smaller_returns_empty() {
500        assert_eq!(extend_string("101", 3), "");
501        assert_eq!(extend_string("101", 2), "");
502        assert_eq!(extend_string("", 0), "");
503    }
504
505    #[test]
506    fn extend_string_weird_char_and_empty_input() {
507        // Unknown leading char results in no extension (empty), even if num_bits is larger
508        assert_eq!(extend_string("h101", 6), "");
509        assert_eq!(extend_string("?", 10), "");
510
511        // Empty input yields empty extension as there is no leading char to guide
512        assert_eq!(extend_string("", 5), "");
513    }
514
515    // ---------------- parse_numeric_string tests ----------------
516
517    #[test]
518    fn parse_numeric_string_hex() {
519        assert_eq!(parse_numeric_string("f9", "Hex"), Some(249.0));
520        assert_eq!(parse_numeric_string("ca", "Hexadecimal"), Some(202.0));
521        assert_eq!(parse_numeric_string("80", "Hex"), Some(128.0));
522        assert_eq!(parse_numeric_string("10", "Hex"), Some(16.0));
523        assert_eq!(parse_numeric_string("0x10", "Hex"), Some(16.0));
524        assert_eq!(parse_numeric_string("0xFF", "Hexadecimal"), Some(255.0));
525    }
526
527    #[test]
528    fn parse_numeric_string_decimal() {
529        assert_eq!(parse_numeric_string("123", "Unsigned"), Some(123.0));
530        assert_eq!(parse_numeric_string("123.45", "Float"), Some(123.45));
531        assert_eq!(parse_numeric_string("80", "Unsigned"), Some(80.0));
532        assert_eq!(parse_numeric_string("10", "Signed"), Some(10.0));
533        assert_eq!(parse_numeric_string("1.5e3", "Float"), Some(1500.0));
534        assert_eq!(parse_numeric_string("-3.14e-2", "Float"), Some(-0.0314));
535    }
536
537    #[test]
538    fn parse_numeric_string_binary() {
539        assert_eq!(parse_numeric_string("1010", "Binary"), Some(10.0));
540        assert_eq!(parse_numeric_string("0b1010", "Binary"), Some(10.0));
541        assert_eq!(parse_numeric_string("11111111", "Bin"), Some(255.0));
542    }
543
544    #[test]
545    fn parse_numeric_string_fallback_to_hex() {
546        // Fallback to hex for non-decimal strings
547        assert_eq!(parse_numeric_string("f9", "Unsigned"), Some(249.0));
548        assert_eq!(parse_numeric_string("ca", "Signed"), Some(202.0));
549    }
550
551    #[test]
552    fn parse_numeric_string_invalid() {
553        assert_eq!(parse_numeric_string("xyz", "Hex"), None);
554        assert_eq!(parse_numeric_string("invalid", "Unsigned"), None);
555        assert_eq!(parse_numeric_string("12", "Binary"), None);
556    }
557
558    #[test]
559    fn parse_numeric_string_large_values() {
560        // 128-bit max value in hex: 2^128 - 1 = 340282366920938463463374607431768211455
561        let hex_128bit = "ffffffffffffffffffffffffffffffff";
562        assert_eq!(
563            parse_numeric_string(hex_128bit, "Hexadecimal"),
564            Some(3.402823669209385e38)
565        );
566
567        // 256-bit max value in hex: 2^256 - 1
568        let hex_256bit = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
569        assert_eq!(
570            parse_numeric_string(hex_256bit, "Hex"),
571            Some(1.157920892373162e77)
572        );
573
574        // Large binary value (128 bits): same as hex_128bit
575        let bin_128bit = "1111111111111111111111111111111111111111111111111111111111111111\
576                          1111111111111111111111111111111111111111111111111111111111111111";
577        assert_eq!(
578            parse_numeric_string(bin_128bit, "Binary"),
579            Some(3.402823669209385e38)
580        );
581
582        // 64-bit max value in hex: 2^64 - 1 = 18446744073709551615
583        let hex_64bit = "ffffffffffffffff";
584        assert_eq!(
585            parse_numeric_string(hex_64bit, "Hexadecimal"),
586            Some(1.8446744073709552e19)
587        );
588
589        // Hex and Unsigned should produce same result for same numeric value
590        // 128-bit value as decimal string (from Unsigned translator)
591        let decimal_128bit = "340282366920938463463374607431768211455";
592        assert_eq!(
593            parse_numeric_string(decimal_128bit, "Unsigned"),
594            Some(3.402823669209385e38)
595        );
596    }
597}