block_id/
lib.rs

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/// Represents a specific, deterministic two-way mapping between `u64` values and opaque IDs.
21///
22/// For `BlockId<char>`, additional functionality is provided for mapping between `u64`s and
23/// `String`s.
24#[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    /// Construct a block ID mapping. Mappings are deterministic based on the three parameters
41    /// passed at construction: the alphabet, seed, and minimum length.
42    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    /// Encode a given `u64` into an opaque `Vec<T>`.
59    #[inline]
60    pub fn encode(&self, v: u64) -> Option<Vec<T>> {
61        self.forward(v)
62    }
63
64    /// Decode an opaque `Vec<T>` from a given `u64`.
65    #[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
101/// For the special case of `BlockId<char>`, helper encoders/decoders that use strings
102/// rather than vectors are provided.
103impl BlockId<char> {
104    /// Encode a `u64` into an opaque string.
105    pub fn encode_string(&self, v: u64) -> Option<String> {
106        Some(self.forward(v)?.into_iter().collect())
107    }
108
109    /// Decode a `u64` from an opaque string.
110    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}