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 self.to_string().serialize(serializer)
60 }
61}
62
63impl<'de> serde::Deserialize<'de> for Address {
64 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
65 where
66 D: serde::Deserializer<'de>,
67 {
68 String::deserialize(deserializer)?
69 .parse()
70 .map_err(<D::Error as serde::de::Error>::custom)
71 }
72}
73
74mod address_str {
75 const CH_GAP: char = ':';
76 const CH_OPEN: char = '<';
77 const CH_CLOSE: char = '>';
78
79 fn digits(mut u: u64) -> [u8; 16] {
80 const MASK: u64 = 0xF000_0000_0000_0000;
81 const OFFSET: u32 = u64::BITS - 4;
82
83 let mut out = [0u8; 16];
84 for d in out.iter_mut() {
85 *d = ((u & MASK) >> OFFSET) as u8;
86 assert!(*d <= 0xF);
87 u <<= 4;
88 }
89
90 out
91 }
92
93 pub(super) fn from_str(s: &str) -> Option<u64> {
94 let bytes = s.as_bytes();
95 if (bytes.first().copied(), bytes.last().copied())
96 != (Some(CH_OPEN as u8), Some(CH_CLOSE as u8))
97 {
98 return None
99 }
100 let bytes = &bytes[1..bytes.len() - 1];
101
102 let mut gap_len = Some(16usize.checked_sub(bytes.len())?);
103
104 let mut out = 0u64;
105
106 for b in bytes.iter().copied() {
107 out <<= 4;
108
109 let c = b as char;
110
111 if c == CH_GAP {
112 let gap_len = gap_len.take()?;
113 out <<= (4 * gap_len) as u32
114 } else {
115 let d = c.to_digit(16)?;
116 out += d as u64;
117 }
118 }
119
120 Some(out)
121 }
122
123 pub(super) fn to_chars(u: u64) -> impl Iterator<Item = char> {
124 #[derive(Default, Clone, Copy)]
125 struct Span {
126 start: usize,
127 len: usize,
128 }
129
130 let digits = digits(u);
131
132 let mut this: Span = Default::default();
133 let mut best: Span = Default::default();
134
135 for (idx, d) in digits.iter().copied().enumerate() {
136 if d == 0 {
137 this.len += 1
138 } else {
139 if this.len > best.len {
140 best = this;
141 }
142 this = Span {
143 start: idx + 1,
144 len: 0,
145 };
146 }
147 }
148 if this.len > best.len {
149 best = this;
150 }
151
152 let left = digits
153 .into_iter()
154 .take(best.start)
155 .map(|d| char::from_digit(d as u32, 16).expect("should be within `0..=F`"));
156
157 let (center, resume_from) = if best.len > 1 {
158 (Some(CH_GAP), best.start + best.len)
159 } else {
160 (None, best.start)
161 };
162
163 let right = digits
164 .into_iter()
165 .skip(resume_from)
166 .map(|d| char::from_digit(d as u32, 16).expect("should be within `0..=F`"));
167
168 [CH_OPEN]
169 .into_iter()
170 .chain(left)
171 .chain(center)
172 .chain(right)
173 .chain([CH_CLOSE])
174 }
175
176 #[cfg(test)]
177 mod tests {
178 use super::*;
179
180 fn to_str(u: u64) -> String {
181 to_chars(u).collect()
182 }
183
184 #[test]
185 fn test_digits() {
186 assert_eq!(
187 [
188 0xa, 0xa, 0xa, 0xa, 0xb, 0xa, 0xb, 0xe, 0xf, 0xa, 0xc, 0xe, 0xd, 0xe, 0xa, 0xd
189 ],
190 digits(0xAAAA_BABE_FACE_DEAD)
191 );
192 }
193
194 #[test]
195 fn test_to_str() {
196 assert_eq!(to_str(0xF0F0_F0F0_F0F0_F0F0), "<f0f0f0f0f0f0f0f0>");
197 assert_eq!(to_str(0xFF00_FF00_FF00_FF00), "<ff:ff00ff00ff00>");
198 assert_eq!(to_str(0xFF00_F000_FF00_FF00), "<ff00f:ff00ff00>");
199 assert_eq!(to_str(0xFF00_0000_FF00_FF00), "<ff:ff00ff00>");
200 assert_eq!(to_str(0xFF00_00FF_0000_FF00), "<ff:ff0000ff00>");
201 assert_eq!(to_str(0xFF00_0000_0000_0000), "<ff:>");
202 }
203
204 #[test]
205 fn test_from_str() {
206 assert_eq!(from_str("<f0f0f0f0f0f0f0f0>"), Some(0xF0F0_F0F0_F0F0_F0F0));
207 assert_eq!(from_str("<ff:ff00ff00ff00>"), Some(0xFF00_FF00_FF00_FF00));
208 assert_eq!(from_str("<ff00f:ff00ff00>"), Some(0xFF00_F000_FF00_FF00));
209 assert_eq!(from_str("<ff:ff00ff00>"), Some(0xFF00_0000_FF00_FF00));
210 assert_eq!(from_str("<ff:ff0000ff00>"), Some(0xFF00_00FF_0000_FF00));
211 assert_eq!(from_str("<ff:>"), Some(0xFF00_0000_0000_0000));
212 }
213 }
214}