mm1_address/
address.rs

1use std::convert::Infallible;
2use std::fmt::{self, Write};
3use std::str::FromStr;
4
5#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub struct Address(u64);
7
8#[derive(Debug, thiserror::Error)]
9#[error("couldn't parse address")]
10pub struct AddressParseError;
11
12impl Address {
13    pub const fn from_u64(inner: u64) -> Self {
14        Self(inner)
15    }
16
17    pub const fn into_u64(self) -> u64 {
18        self.0
19    }
20}
21
22impl TryFrom<u64> for Address {
23    type Error = Infallible;
24
25    fn try_from(value: u64) -> Result<Self, Self::Error> {
26        Ok(Self(value))
27    }
28}
29
30impl FromStr for Address {
31    type Err = AddressParseError;
32
33    fn from_str(s: &str) -> Result<Self, Self::Err> {
34        let address = address_str::from_str(s).ok_or(AddressParseError)?;
35        Ok(Self(address))
36    }
37}
38
39impl fmt::Display for Address {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        for c in address_str::to_chars(self.0) {
42            f.write_char(c)?;
43        }
44        Ok(())
45    }
46}
47impl fmt::Debug for Address {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        fmt::Display::fmt(self, f)
50    }
51}
52
53impl serde::Serialize for Address {
54    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55    where
56        S: serde::Serializer,
57    {
58        self.to_string().serialize(serializer)
59    }
60}
61impl<'de> serde::Deserialize<'de> for Address {
62    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
63    where
64        D: serde::Deserializer<'de>,
65    {
66        String::deserialize(deserializer)?
67            .parse()
68            .map_err(<D::Error as serde::de::Error>::custom)
69    }
70}
71
72mod address_str {
73    const CH_GAP: char = ':';
74    const CH_OPEN: char = '<';
75    const CH_CLOSE: char = '>';
76
77    fn digits(mut u: u64) -> [u8; 16] {
78        const MASK: u64 = 0xF000_0000_0000_0000;
79        const OFFSET: u32 = u64::BITS - 4;
80
81        let mut out = [0u8; 16];
82        for d in out.iter_mut() {
83            *d = ((u & MASK) >> OFFSET) as u8;
84            assert!(*d <= 0xF);
85            u <<= 4;
86        }
87
88        out
89    }
90
91    pub(super) fn from_str(s: &str) -> Option<u64> {
92        let bytes = s.as_bytes();
93        if (bytes.first().copied(), bytes.last().copied())
94            != (Some(CH_OPEN as u8), Some(CH_CLOSE as u8))
95        {
96            return None
97        }
98        let bytes = &bytes[1..bytes.len() - 1];
99
100        let mut gap_len = Some(16usize.checked_sub(bytes.len())?);
101
102        let mut out = 0u64;
103
104        for b in bytes.iter().copied() {
105            out <<= 4;
106
107            let c = b as char;
108
109            if c == CH_GAP {
110                let gap_len = gap_len.take()?;
111                out <<= (4 * gap_len) as u32
112            } else {
113                let d = c.to_digit(16)?;
114                out += d as u64;
115            }
116        }
117
118        Some(out)
119    }
120
121    pub(super) fn to_chars(u: u64) -> impl Iterator<Item = char> {
122        #[derive(Default, Clone, Copy)]
123        struct Span {
124            start: usize,
125            len:   usize,
126        }
127
128        let digits = digits(u);
129
130        let mut this: Span = Default::default();
131        let mut best: Span = Default::default();
132
133        for (idx, d) in digits.iter().copied().enumerate() {
134            if d == 0 {
135                this.len += 1
136            } else {
137                if this.len > best.len {
138                    best = this;
139                }
140                this = Span {
141                    start: idx + 1,
142                    len:   0,
143                };
144            }
145        }
146        if this.len > best.len {
147            best = this;
148        }
149
150        let left = digits
151            .into_iter()
152            .take(best.start)
153            .map(|d| char::from_digit(d as u32, 16).expect("should be within `0..=F`"));
154
155        let (center, resume_from) = if best.len > 1 {
156            (Some(CH_GAP), best.start + best.len)
157        } else {
158            (None, best.start)
159        };
160
161        let right = digits
162            .into_iter()
163            .skip(resume_from)
164            .map(|d| char::from_digit(d as u32, 16).expect("should be within `0..=F`"));
165
166        [CH_OPEN]
167            .into_iter()
168            .chain(left)
169            .chain(center)
170            .chain(right)
171            .chain([CH_CLOSE])
172    }
173
174    #[cfg(test)]
175    mod tests {
176        use super::*;
177
178        fn to_str(u: u64) -> String {
179            to_chars(u).collect()
180        }
181
182        #[test]
183        fn test_digits() {
184            assert_eq!(
185                [0xa, 0xa, 0xa, 0xa, 0xb, 0xa, 0xb, 0xe, 0xf, 0xa, 0xc, 0xe, 0xd, 0xe, 0xa, 0xd],
186                digits(0xAAAA_BABE_FACE_DEAD)
187            );
188        }
189
190        #[test]
191        fn test_to_str() {
192            assert_eq!(to_str(0xF0F0_F0F0_F0F0_F0F0), "<f0f0f0f0f0f0f0f0>");
193            assert_eq!(to_str(0xFF00_FF00_FF00_FF00), "<ff:ff00ff00ff00>");
194            assert_eq!(to_str(0xFF00_F000_FF00_FF00), "<ff00f:ff00ff00>");
195            assert_eq!(to_str(0xFF00_0000_FF00_FF00), "<ff:ff00ff00>");
196            assert_eq!(to_str(0xFF00_00FF_0000_FF00), "<ff:ff0000ff00>");
197            assert_eq!(to_str(0xFF00_0000_0000_0000), "<ff:>");
198        }
199
200        #[test]
201        fn test_from_str() {
202            assert_eq!(from_str("<f0f0f0f0f0f0f0f0>"), Some(0xF0F0_F0F0_F0F0_F0F0));
203            assert_eq!(from_str("<ff:ff00ff00ff00>"), Some(0xFF00_FF00_FF00_FF00));
204            assert_eq!(from_str("<ff00f:ff00ff00>"), Some(0xFF00_F000_FF00_FF00));
205            assert_eq!(from_str("<ff:ff00ff00>"), Some(0xFF00_0000_FF00_FF00));
206            assert_eq!(from_str("<ff:ff0000ff00>"), Some(0xFF00_00FF_0000_FF00));
207            assert_eq!(from_str("<ff:>"), Some(0xFF00_0000_0000_0000));
208        }
209    }
210}