mm1_address/
address.rs

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