1#![doc = include_str!("../README.md")]
2
3pub use alphabet::Alphabet;
4use base::BaseConversion;
5use cascade::Cascade;
6use permute::Permute;
7use rotate::Rotate;
8use std::{fmt::Debug, hash::Hash};
9use transform::InvertableTransform;
10
11mod add_mod;
12mod alphabet;
13mod base;
14mod cascade;
15mod permutation;
16mod permute;
17mod rotate;
18mod transform;
19
20#[derive(Clone)]
25pub struct BlockId<T: Copy + Hash + Eq> {
26 alphabet: Alphabet<T>,
27 base_convert: BaseConversion,
28 cascade: Cascade,
29 rotate: Rotate<u8>,
30 permute: Permute,
31}
32
33impl<T: Copy + Hash + Eq> Debug for BlockId<T> {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 write!(f, "BlockId with alphabet size {}", self.alphabet.len())
36 }
37}
38
39impl<T: Copy + Hash + Eq> BlockId<T> {
40 pub fn new(alphabet: Alphabet<T>, seed: u128, min_length: u8) -> Self {
43 let base = alphabet.len();
44 let base_convert = BaseConversion::new_with_min_length(base, min_length);
45 let cascade = Cascade::new(base);
46 let rotate = Rotate::new();
47 let permute = Permute::new_from_seed(base, seed);
48
49 BlockId {
50 alphabet,
51 base_convert,
52 cascade,
53 rotate,
54 permute,
55 }
56 }
57
58 #[inline]
60 pub fn encode(&self, v: u64) -> Option<Vec<T>> {
61 self.forward(v)
62 }
63
64 #[inline]
66 pub fn decode(&self, v: Vec<T>) -> Option<u64> {
67 self.backward(v)
68 }
69}
70
71impl<T: Copy + Hash + Eq> InvertableTransform for BlockId<T> {
72 type Input = u64;
73 type Output = Vec<T>;
74
75 fn forward(&self, v: u64) -> Option<Vec<T>> {
76 let mut v = self.base_convert.forward(v)?;
77
78 for _ in 0..v.len() {
79 v = self.permute.forward(v)?;
80 v = self.cascade.forward(v)?;
81 v = self.rotate.forward(v)?;
82 }
83
84 v.iter().map(|d| self.alphabet.forward(*d)).collect()
85 }
86
87 fn backward(&self, v: Vec<T>) -> Option<u64> {
88 let v: Option<Vec<u8>> = v.iter().map(|d| self.alphabet.backward(*d)).collect();
89 let mut v = v?;
90
91 for _ in 0..v.len() {
92 v = self.rotate.backward(v)?;
93 v = self.cascade.backward(v)?;
94 v = self.permute.backward(v)?;
95 }
96
97 self.base_convert.backward(v)
98 }
99}
100
101impl BlockId<char> {
104 pub fn encode_string(&self, v: u64) -> Option<String> {
106 Some(self.forward(v)?.into_iter().collect())
107 }
108
109 pub fn decode_string(&self, v: &str) -> Option<u64> {
111 self.backward(v.chars().collect())
112 }
113}
114
115#[cfg(test)]
116mod test {
117 use crate::{transform::test::round_trip, Alphabet, BlockId};
118
119 #[test]
120 fn long_str() {
121 let permuter = BlockId::new(Alphabet::lowercase_alpha(), 118, 4);
122 assert_eq!(
123 permuter.decode("dsasfsdnlkdfjsl".chars().collect()),
124 None,
125 "if string is too long should return None since no 1:1 map to u64"
126 );
127 }
128
129 #[test]
130 fn test_round_trip() {
131 let permuter = BlockId::new(Alphabet::lowercase_alpha(), 118, 4);
132
133 for i in 600..800 {
134 round_trip(&permuter, i);
135 }
136 }
137
138 #[test]
139 fn test_debug() {
140 let block1 = BlockId::new(Alphabet::lowercase_alpha(), 118, 4);
141 assert_eq!("BlockId with alphabet size 26", format!("{:?}", block1));
142
143 let block2 = BlockId::new(Alphabet::alphanumeric(), 118, 4);
144 assert_eq!("BlockId with alphabet size 62", format!("{:?}", block2));
145 }
146}