Skip to main content

chess/id/
base58.rs

1use std::{fmt, str};
2
3use super::Id;
4use crate::finite::Empty;
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
7#[error("invalid base58")]
8pub struct Error;
9
10pub type Result<T, E = Error> = std::result::Result<T, E>;
11
12#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
13#[cfg_attr(feature = "serde", derive(DeserializeFromStr, SerializeDisplay))]
14pub struct Base58 {
15    bytes: Vec<u8>,
16    string: String,
17}
18
19impl Empty for Id {
20    const EMPTY: Self = Id(0);
21
22    fn is_empty(&self) -> bool {
23        *self == Self::EMPTY
24    }
25}
26
27impl fmt::Display for Id {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        Base58::encode(&self.0.to_be_bytes()).fmt(f)
30    }
31}
32
33impl str::FromStr for Id {
34    type Err = Error;
35
36    fn from_str(s: &str) -> Result<Self, Self::Err> {
37        let bytes = Base58::decode(s)?;
38        let bytes: [u8; 16] = bytes.try_into().map_err(|_| Error)?;
39        Ok(Self(u128::from_be_bytes(bytes)))
40    }
41}
42
43#[cfg(feature = "serde")]
44impl serde::Serialize for Id {
45    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
46    where
47        S: serde::Serializer,
48    {
49        if serializer.is_human_readable() {
50            serializer.serialize_str(&self.to_string())
51        } else {
52            serializer.serialize_bytes(&self.0.to_be_bytes())
53        }
54    }
55}
56
57#[cfg(feature = "serde")]
58impl<'de> serde::Deserialize<'de> for Id {
59    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60    where
61        D: serde::Deserializer<'de>,
62    {
63        use serde::de::Visitor;
64
65        struct IdVisitor;
66
67        impl<'de> Visitor<'de> for IdVisitor {
68            type Value = Id;
69
70            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
71                formatter.write_str("a base58 string or 16 bytes")
72            }
73
74            fn visit_str<E>(self, value: &str) -> Result<Id, E>
75            where
76                E: serde::de::Error,
77            {
78                value.parse().map_err(E::custom)
79            }
80
81            fn visit_bytes<E>(self, value: &[u8]) -> Result<Id, E>
82            where
83                E: serde::de::Error,
84            {
85                let bytes: [u8; 16] = value.try_into().map_err(|_| E::custom("invalid id"))?;
86                Ok(Id(u128::from_be_bytes(bytes)))
87            }
88
89            fn visit_seq<A>(self, mut seq: A) -> Result<Id, A::Error>
90            where
91                A: serde::de::SeqAccess<'de>,
92            {
93                let mut bytes = [0u8; 16];
94                for (index, byte) in bytes.iter_mut().enumerate() {
95                    *byte = seq
96                        .next_element()?
97                        .ok_or_else(|| serde::de::Error::invalid_length(index, &self))?;
98                }
99                if seq.next_element::<u8>()?.is_some() {
100                    return Err(serde::de::Error::invalid_length(17, &self));
101                }
102                Ok(Id(u128::from_be_bytes(bytes)))
103            }
104        }
105
106        if deserializer.is_human_readable() {
107            deserializer.deserialize_str(IdVisitor)
108        } else {
109            deserializer.deserialize_bytes(IdVisitor)
110        }
111    }
112}
113
114impl Base58 {
115    pub const LETTERS: [u8; 58] = *b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
116
117    pub const OCTETS: [u8; 128] = {
118        let mut octets = [0xff; 128];
119        let mut index = 0;
120        while index < Self::LETTERS.len() {
121            octets[Self::LETTERS[index] as usize] = index as u8;
122            index += 1;
123        }
124        octets
125    };
126
127    pub fn new(bytes: &[u8]) -> Self {
128        Self { bytes: bytes.to_vec(), string: Self::encode(bytes) }
129    }
130
131    pub fn parse(s: &str) -> Result<Self> {
132        s.parse()
133    }
134
135    pub fn random_bytes(len: usize) -> Self {
136        use rand::RngExt as _;
137
138        let mut vec = vec![0u8; len];
139        rand::rng().fill(vec.as_mut_slice());
140        Self::new(&vec)
141    }
142
143    pub fn random_str(len: usize) -> Self {
144        use rand::RngExt as _;
145
146        let mut rng = rand::rng();
147        let string = (0..len)
148            .map(|_| Self::LETTERS[rng.random_range(0..Self::LETTERS.len())])
149            .collect::<Vec<_>>();
150        str::from_utf8(&string).unwrap().parse().unwrap()
151    }
152
153    pub fn encode(bytes: &[u8]) -> String {
154        let zeros = bytes.iter().take_while(|&&byte| byte == 0).count();
155        let mut digits = Vec::<u8>::new();
156        for &byte in bytes {
157            let mut carry = byte as u32;
158            for digit in &mut digits {
159                carry += (*digit as u32) << 8;
160                *digit = (carry % 58) as u8;
161                carry /= 58;
162            }
163            while carry > 0 {
164                digits.push((carry % 58) as u8);
165                carry /= 58;
166            }
167        }
168
169        let mut string = String::with_capacity(zeros + digits.len());
170        string.extend(core::iter::repeat_n('1', zeros));
171        string.extend(digits.iter().rev().map(|digit| Self::LETTERS[*digit as usize] as char));
172        string
173    }
174
175    pub fn decode(s: &str) -> Result<Vec<u8>> {
176        let zeros = s.bytes().take_while(|&byte| byte == b'1').count();
177        let mut bytes = Vec::<u8>::new();
178        for byte in s.bytes() {
179            if byte as usize >= Self::OCTETS.len() {
180                return Err(Error);
181            }
182            let digit = Self::OCTETS[byte as usize];
183            if digit == 0xff {
184                return Err(Error);
185            }
186            let mut carry = digit as u32;
187            for decoded in &mut bytes {
188                carry += (*decoded as u32) * 58;
189                *decoded = (carry & 0xff) as u8;
190                carry >>= 8;
191            }
192            while carry > 0 {
193                bytes.push((carry & 0xff) as u8);
194                carry >>= 8;
195            }
196        }
197
198        let mut decoded = vec![0; zeros];
199        decoded.extend(bytes.iter().rev());
200        Ok(decoded)
201    }
202
203    pub const fn as_bytes(&self) -> &[u8] {
204        self.bytes.as_slice()
205    }
206
207    pub const fn as_str(&self) -> &str {
208        self.string.as_str()
209    }
210}
211
212impl str::FromStr for Base58 {
213    type Err = Error;
214
215    fn from_str(s: &str) -> Result<Self> {
216        Ok(Self { bytes: Self::decode(s)?, string: s.to_string() })
217    }
218}
219
220impl fmt::Display for Base58 {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        self.string.fmt(f)
223    }
224}
225
226impl From<Base58> for String {
227    fn from(base58: Base58) -> Self {
228        base58.string
229    }
230}
231
232#[cfg(test)]
233mod tests {
234    use super::{Base58, Id};
235
236    #[test]
237    fn id_display_roundtrips() {
238        for id in [Id(0), Id(1), Id(u128::MAX), Id(0x0123_4567_89ab_cdef_fedc_ba98_7654_3210)] {
239            assert_eq!(id.to_string().parse::<Id>().unwrap(), id);
240        }
241    }
242
243    #[cfg(feature = "serde")]
244    #[test]
245    fn json_serializes_id_as_base58_string() {
246        let id = Id(0x0123_4567_89ab_cdef_fedc_ba98_7654_3210);
247        let json = serde_json::to_string(&id).unwrap();
248        assert!(json.starts_with('"'));
249        assert_eq!(serde_json::from_str::<Id>(&json).unwrap(), id);
250    }
251
252    #[test]
253    fn known_vectors() {
254        let vectors: &[(&[u8], &str)] = &[
255            (&[], ""),
256            (&[0], "1"),
257            (&[0, 0, 0, 1], "1112"),
258            (&[0x61], "2g"),
259            (&[0x62, 0x62, 0x62], "a3gV"),
260            (&[0x63, 0x63, 0x63], "aPEr"),
261            (
262                &[
263                    0x73, 0x69, 0x6d, 0x70, 0x6c, 0x79, 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x6e, 0x67,
264                    0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
265                ],
266                "2cFupjhnEsSn59qHXstmK2ffpLv2",
267            ),
268        ];
269
270        for (bytes, encoded) in vectors {
271            assert_eq!(Base58::encode(bytes), *encoded);
272            assert_eq!(Base58::decode(encoded).unwrap(), *bytes);
273            let base58 = Base58::new(bytes);
274            assert_eq!(base58.as_str(), *encoded);
275            assert_eq!(base58.as_bytes(), *bytes);
276        }
277    }
278
279    #[test]
280    fn rejects_invalid_characters() {
281        for invalid in ["0", "O", "I", "l", "+", "/", "abc0"] {
282            assert!(Base58::decode(invalid).is_err());
283            assert!(Base58::parse(invalid).is_err());
284        }
285    }
286}