vcd_ng/
idcode.rs

1use std::fmt::{self, Display};
2use std::str::FromStr;
3use compact_str::CompactString;
4
5use crate::InvalidData;
6
7/// An ID used within the file to refer to a particular variable.
8#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
9pub struct IdCode(pub u64);
10
11const ID_CHAR_MIN: u8 = b'!';
12const ID_CHAR_MAX: u8 = b'~';
13const NUM_ID_CHARS: u64 = (ID_CHAR_MAX - ID_CHAR_MIN + 1) as u64;
14
15impl IdCode {
16    #[inline]
17    pub fn new(v: &[u8]) -> Result<IdCode, InvalidData> {
18        if v.is_empty() {
19            return Err(InvalidData("ID cannot be empty"));
20        }
21        let mut result = 0u64;
22        for &i in v.iter() {
23            if i < ID_CHAR_MIN || i > ID_CHAR_MAX {
24                return Err(InvalidData("invalid characters in ID"));
25            }
26            let c = ((i - ID_CHAR_MIN) as u64) + 1;
27            result = result
28                .checked_mul(NUM_ID_CHARS)
29                .and_then(|x| x.checked_add(c))
30                .ok_or(InvalidData("ID too long"))?;
31        }
32        Ok(IdCode(result - 1))
33    }
34
35    /// An arbitrary IdCode with a short representation.
36    pub const FIRST: IdCode = IdCode(0);
37
38    /// Returns the IdCode following this one in an arbitrary sequence.
39    #[inline]
40    pub fn next(&self) -> IdCode {
41        IdCode(self.0 + 1)
42    }
43
44    pub fn to_compact_string(self) -> CompactString {
45        let mut i = self.0;
46        let mut revname = CompactString::new("");
47        loop {
48            let r = i % NUM_ID_CHARS;
49            revname.push((r as u8 + ID_CHAR_MIN) as char);
50            if i < NUM_ID_CHARS {
51                break;
52            }
53            i = i / NUM_ID_CHARS - 1;
54        }
55        revname.chars().rev().collect()
56    }
57}
58
59impl FromStr for IdCode {
60    type Err = InvalidData;
61    #[inline]
62    fn from_str(s: &str) -> Result<Self, Self::Err> {
63        IdCode::new(s.as_bytes())
64    }
65}
66
67impl From<u32> for IdCode {
68    #[inline]
69    fn from(i: u32) -> IdCode {
70        IdCode(i as u64)
71    }
72}
73
74impl From<u64> for IdCode {
75    #[inline]
76    fn from(i: u64) -> IdCode {
77        IdCode(i)
78    }
79}
80
81impl Display for IdCode {
82    #[inline]
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        write!(f, "{}", self.to_compact_string())
85    }
86}
87
88#[test]
89fn test_id_code() {
90    let mut id = IdCode::FIRST;
91    for i in 0..10000 {
92        println!("{} {}", i, id);
93        assert_eq!(id.to_string().parse::<IdCode>().unwrap(), id);
94        id = id.next();
95    }
96
97    assert_eq!("!".parse::<IdCode>().unwrap().to_string(), "!");
98    assert_eq!(
99        "!!!!!!!!!!".parse::<IdCode>().unwrap().to_string(),
100        "!!!!!!!!!!"
101    );
102    assert_eq!("~".parse::<IdCode>().unwrap().to_string(), "~");
103    assert_eq!(
104        "~~~~~~~~~".parse::<IdCode>().unwrap().to_string(),
105        "~~~~~~~~~"
106    );
107    assert_eq!(
108        "999999999n".parse::<IdCode>().unwrap().to_string(),
109        "999999999n"
110    );
111    assert!("9999999999n".parse::<IdCode>().is_err());
112}