1extern crate core;
2
3use rand::{RngCore};
4use rand::thread_rng;
5
6pub const DEFAULT_ALPHABET: &[u8] = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".as_bytes();
7const DEFAULT_SIZE: usize = 21;
8
9const LEN8TAB: &[u8] = "\x00\x01\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04\x04\x04\x04\x04\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08".as_bytes();
10
11fn len_32(mut x: u32) -> i32 {
12 let mut n = 0;
13 if x >= 1 << 16 {
14 x >>= 16;
15 n = 16;
16 }
17 if x >= 1 << 8 {
18 x >>= 8;
19 n += 8;
20 }
21 return n + LEN8TAB[x as usize] as i32;
22}
23
24fn leading_zeros_32(x: u32) -> i32 {
25 return 32 - len_32(x);
26}
27
28
29type BytesGenerator = fn(size: usize) -> Vec<u8>;
30
31fn generate_random_buffer(size: usize) -> Vec<u8> {
32 let mut rng = thread_rng();
33 let mut buffer: Vec<u8> = Vec::with_capacity(size);
34 buffer.resize(size, 0);
35 rng.fill_bytes(buffer.as_mut_slice());
36 return buffer;
37}
38
39fn format_string(generate_random_buffer: BytesGenerator, alphabet: &[u8], size: usize) -> String {
40 let mask = (2 << (31 - leading_zeros_32((alphabet.len() - 1 | 1) as u32)) as u32) - 1;
41 let step = ((1.6 * (mask * size) as f64 / (alphabet.len()) as f64).ceil()) as usize;
42 let mut id = String::new();
43 loop {
44 let random_buffer = generate_random_buffer(step);
45 for i in 0..step {
46 let current_index = (random_buffer[i] & mask as u8) as usize;
47 if current_index < alphabet.len() {
48 id.push(char::from(alphabet[current_index]));
49 if id.len() == size as usize {
50 return id;
51 }
52 }
53 }
54 }
55}
56
57
58pub fn generate_string(alphabet: &[u8], size: usize) -> String {
59 return format_string(generate_random_buffer, alphabet, size);
60}
61
62pub fn new() -> String {
63 return generate_string(DEFAULT_ALPHABET, DEFAULT_SIZE);
64}
65
66#[macro_export]
67macro_rules! id {
68 () => {
69 new()
70 };
71 ($size: expr) => {
72 generate_string(DEFAULT_ALPHABET, $size)
73 };
74 ($size: expr, $alphabet: expr) => {
75 generate_string($alphabet, $size)
76 };
77
78}
79
80#[cfg(test)]
81mod tests {
82 use std::collections::HashMap;
83 use crate::{DEFAULT_ALPHABET, DEFAULT_SIZE, format_string, generate_random_buffer, generate_string, LEN8TAB, len_32, new};
84
85 #[test]
86 fn test_len_32() {
87 assert_eq!(0, len_32(0));
88 assert_eq!(3, len_32(4));
89 assert_eq!(4, len_32(8));
90 assert_eq!(5, len_32(21));
91 }
92
93 #[test]
94 fn default_alphabet() {
95 assert_eq!("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".as_bytes(), DEFAULT_ALPHABET);
96 }
97
98 #[test]
99 fn default_size_is_21() {
100 assert_eq!(21, DEFAULT_SIZE);
101 }
102
103 #[test]
104 fn test_generate_url_friendly_ids() {
105 let alphabet = DEFAULT_ALPHABET;
106 let size = DEFAULT_SIZE;
107 for _i in 0..100 {
108 let id = generate_string(alphabet, size);
109 for ch in id.bytes() {
110 if !alphabet.contains(&ch) {
111 assert_eq!("contains url not frinedly chars", "");
112 }
113 }
114 }
115 }
116
117 #[test]
118 fn test_change_id_length() {
119 let alphabet = DEFAULT_ALPHABET;
120 let size = 10;
121 let id = generate_string(alphabet, size);
122 assert_eq!(size, id.len());
123 }
124
125 #[test]
126 fn test_no_collisions() {
127 let count = 100 * 1000;
128 let mut used: HashMap<String, usize> = HashMap::new();
129 for _i in 0..count {
130 let id = new();
131 if used.contains_key(&id) {
132 assert_eq!("repeated id has been generated", "");
133 }
134 used.insert(id, 1);
135 }
136 }
137
138 #[test]
139 fn test_has_flat_distribution() {
140 let alphabet = "abcdefghijklmnopqrstuvwxyz".as_bytes();
141 let size = 5;
142
143 let count = 100 * 1000;
144 let mut chars: HashMap<u8, i32> = HashMap::new();
145 for _i in 0..count {
146 let id = generate_string(alphabet, size);
147 let id_bytes = id.as_bytes();
148 for j in 0..id_bytes.len() {
149 let ch = id_bytes[j];
150 if !chars.contains_key(&ch) {
151 chars.insert(ch, 0);
152 } else {
153 *chars.get_mut(&ch).unwrap() += 1;
154 }
155 }
156 }
157
158 assert_eq!(chars.len(), alphabet.len());
159
160 let mut max = 0.0;
161 let mut min = i32::MAX as f64;
162 for v in chars {
163 let distribution = (v.1 * alphabet.len() as i32) as f64 / (count * size) as f64;
164 if distribution > max {
165 max = distribution;
166 }
167 if distribution < min {
168 min = distribution;
169 }
170 }
171 let distribution = max - min;
172 assert_eq!(true, distribution < 0.05);
173 }
174
175 #[test]
176 fn test_has_options() {
177 let id = generate_string("a".as_bytes(), 5);
178 let target = "aaaaa".to_string();
179 assert_eq!(target, id);
180 }
181
182 const SEQUENCE: [u8; 10] = [2, 255, 3, 7, 7, 7, 7, 7, 0, 1];
183
184 fn generate_bytes_buffer(step: usize) -> Vec<u8> {
185 let mut buffer: Vec<u8> = Vec::new();
186 let mut i = 0;
187 loop {
188 if i < step {
189 for j in 0..step {
190 buffer.push(SEQUENCE[j]);
191 }
192 } else {
193 break;
194 }
195 i += SEQUENCE.len();
196 }
197 return buffer;
198 }
199
200 #[test]
201 fn test_generate_random_string() {
202 let id = format_string(generate_bytes_buffer, "abcde".as_bytes(), 4);
203 let target = "cdac".to_string();
204 assert_eq!(target, id);
205 }
206
207 #[test]
208 fn len8tab_is_256_len() {
209 assert_eq!(256, LEN8TAB.len());
210 assert_eq!(0, LEN8TAB[0] as i32);
211 assert_eq!(8, LEN8TAB[255] as i32);
212 }
213
214 #[test]
215 fn gen_random_buffer_len() {
216 let buf = generate_random_buffer(DEFAULT_SIZE);
217 assert_eq!(DEFAULT_SIZE, buf.len());
218 }
219
220 #[test]
221 fn generate_id() {
222 let _id = new();
223 }
224
225 #[test]
226 fn test_macros() {
227 let id = id!();
229 assert_eq!(id.len(), DEFAULT_SIZE);
230
231 let id_10 = id!(10);
233 assert_eq!(id_10.len(), 10);
234
235 let id_alphabet_10 = id!(10, "01234567890".as_bytes());
237 assert_eq!(id_alphabet_10.len(), 10);
238 }
239}