1#![deny(unsafe_code)]
26#![warn(missing_docs)]
27
28#[macro_use]
29extern crate lazy_static;
30
31use std::{convert::TryInto, fmt};
32
33use num_bigint::{BigUint, ToBigUint};
34use num_integer::Integer;
35use num_traits::Zero;
36
37const ALPHABET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
38const INVALID: u8 = ALPHABET.len() as u8;
39
40lazy_static! {
41 static ref ALPHABET_INDEX: [u8; 0x100] = {
42 let mut index = [INVALID; 0x100];
43 for i in 0..ALPHABET.len() {
44 index[ALPHABET[i] as usize] = i as u8;
45 }
46 index
47 };
48}
49
50pub fn encode(bytes: impl AsRef<[u8]>) -> String {
63 let bytes = bytes.as_ref();
64 if bytes.is_empty() {
65 return String::with_capacity(0);
66 }
67 let mut n = BigUint::from_bytes_be(bytes);
68 if n == Zero::zero() {
69 return String::from_utf8(vec![ALPHABET[0]]).unwrap();
70 }
71 let mut list: Vec<u8> = Vec::new();
72 let base = ALPHABET.len().to_biguint().unwrap();
73 while n != Zero::zero() {
74 let (q, r) = n.div_mod_floor(&base);
75 list.push(r.try_into().unwrap());
76 n = q;
77 }
78 let mut s = Vec::new();
79 for i in list.iter().rev() {
80 s.push(ALPHABET[*i as usize]);
81 }
82 String::from_utf8(s).unwrap()
83}
84
85#[test]
86fn test_encode() {
87 assert_eq!(encode(b""), "");
88}
89
90#[derive(Clone, PartialEq, Eq, Debug)]
96pub struct InvalidCharacter(char);
97
98impl InvalidCharacter {
99 pub fn char(&self) -> char {
101 self.0
102 }
103}
104
105impl fmt::Display for InvalidCharacter {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 let ch = self.0;
108 let code = u32::from(ch);
109 if ch.is_control() {
110 write!(f, "invalid character ({:#08x}) found", code)
111 } else {
112 write!(f, "invalid character '{}' ({:#08x}) found", ch, code)
113 }
114 }
115}
116
117impl std::error::Error for InvalidCharacter {}
118
119fn to_num(ch: char) -> Result<u8, InvalidCharacter> {
120 let i = ch as usize;
121 if i > 0xff {
122 return Err(InvalidCharacter(ch));
123 }
124 let v = ALPHABET_INDEX[i];
125 if v == INVALID {
126 Err(InvalidCharacter(ch))
127 } else {
128 Ok(v)
129 }
130}
131
132pub fn decode(s: impl AsRef<str>) -> Result<Vec<u8>, InvalidCharacter> {
145 let s = s.as_ref();
146 if s.is_empty() {
147 return Ok(Vec::with_capacity(0));
148 }
149 let base = ALPHABET.len();
150 let mut n: BigUint = Zero::zero();
151 for c in s.chars() {
152 n *= base;
153 n += to_num(c)?;
154 }
155 Ok(n.to_bytes_be())
156}
157
158#[test]
159fn test_decode() {
160 assert_eq!(decode("").unwrap(), b"");
161}