reifydb_type/util/
base58.rs1use std::{error, fmt};
7const BASE58_CHARS: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
8
9pub fn encode(input: &[u8]) -> String {
11 if input.is_empty() {
12 return String::new();
13 }
14
15 let leading_zeros = input.iter().take_while(|&&b| b == 0).count();
17
18 let mut bytes = input.to_vec();
21 let mut result = Vec::new();
22
23 while !bytes.iter().all(|&b| b == 0) {
24 let mut remainder = 0u32;
25 for byte in bytes.iter_mut() {
26 let value = (remainder << 8) | (*byte as u32);
27 *byte = (value / 58) as u8;
28 remainder = value % 58;
29 }
30 result.push(BASE58_CHARS[remainder as usize]);
31 }
32
33 for _ in 0..leading_zeros {
35 result.push(b'1');
36 }
37
38 result.reverse();
40 String::from_utf8(result).unwrap()
41}
42
43pub fn decode(input: &str) -> Result<Vec<u8>, DecodeError> {
45 if input.is_empty() {
46 return Ok(Vec::new());
47 }
48
49 let leading_ones = input.chars().take_while(|&c| c == '1').count();
51
52 let mut bytes: Vec<u8> = Vec::new();
54
55 for ch in input.chars() {
56 let value = char_to_value(ch)?;
57
58 let mut carry = value as u32;
60 for byte in bytes.iter_mut().rev() {
61 let val = (*byte as u32) * 58 + carry;
62 *byte = (val & 0xFF) as u8;
63 carry = val >> 8;
64 }
65
66 while carry > 0 {
67 bytes.insert(0, (carry & 0xFF) as u8);
68 carry >>= 8;
69 }
70 }
71
72 let mut result = vec![0u8; leading_ones];
74 result.extend(bytes);
75
76 Ok(result)
77}
78
79fn char_to_value(ch: char) -> Result<u8, DecodeError> {
80 let byte = ch as u8;
81 BASE58_CHARS.iter().position(|&b| b == byte).map(|pos| pos as u8).ok_or(DecodeError::InvalidCharacter(ch))
82}
83
84#[derive(Debug)]
85pub enum DecodeError {
86 InvalidCharacter(char),
87}
88
89impl fmt::Display for DecodeError {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 match self {
92 DecodeError::InvalidCharacter(ch) => {
93 write!(f, "Invalid base58 character: '{}'", ch)
94 }
95 }
96 }
97}
98
99impl error::Error for DecodeError {}
100
101#[cfg(test)]
102pub mod tests {
103 use super::*;
104
105 #[test]
106 fn test_encode_empty() {
107 assert_eq!(encode(b""), "");
108 }
109
110 #[test]
111 fn test_encode_hello() {
112 assert_eq!(encode(b"Hello"), "9Ajdvzr");
114 }
115
116 #[test]
117 fn test_encode_hello_world() {
118 assert_eq!(encode(b"Hello, World!"), "72k1xXWG59fYdzSNoA");
120 }
121
122 #[test]
123 fn test_encode_leading_zeros() {
124 assert_eq!(encode(&[0, 0, 1]), "112");
126 assert_eq!(encode(&[0, 0, 0]), "111");
127 }
128
129 #[test]
130 fn test_decode_empty() {
131 assert_eq!(decode("").unwrap(), b"");
132 }
133
134 #[test]
135 fn test_decode_hello() {
136 assert_eq!(decode("9Ajdvzr").unwrap(), b"Hello");
137 }
138
139 #[test]
140 fn test_decode_hello_world() {
141 assert_eq!(decode("72k1xXWG59fYdzSNoA").unwrap(), b"Hello, World!");
142 }
143
144 #[test]
145 fn test_decode_leading_ones() {
146 assert_eq!(decode("112").unwrap(), &[0, 0, 1]);
147 assert_eq!(decode("111").unwrap(), &[0, 0, 0]);
148 }
149
150 #[test]
151 fn test_roundtrip() {
152 let data = b"Hello, World! \x00\x01\x02\xFF";
153 let encoded = encode(data);
154 let decoded = decode(&encoded).unwrap();
155 assert_eq!(decoded, data);
156 }
157
158 #[test]
159 fn test_roundtrip_binary() {
160 let data = &[0xde, 0xad, 0xbe, 0xef];
161 let encoded = encode(data);
162 let decoded = decode(&encoded).unwrap();
163 assert_eq!(decoded, data);
164 }
165
166 #[test]
167 fn test_invalid_character() {
168 assert!(decode("0").is_err());
170 assert!(decode("O").is_err());
171 assert!(decode("I").is_err());
172 assert!(decode("l").is_err());
173 assert!(decode("invalid!").is_err());
174 }
175}