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