1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
use random; use alphabet; pub fn universal(random: fn(usize) -> Vec<u8>, alphabet: &[char], size: usize) -> String { let mask = (2 << ((alphabet.len() as f64 - 1.0).ln() / 2.0_f64.ln()) as i64) - 1; let step: usize = (1.6_f64 * (mask * size) as f64).ceil() as usize; let mut id = String::new(); loop { let bytes = random(step); for i in 0..step { let byte: usize = bytes[i] as usize & mask; if alphabet.len() > byte { id.push(alphabet[byte]); if id.len() == size { return id; } } } } } #[cfg(test)] mod test_universal { use super::*; #[test] fn generates_random_string() { fn random (size: usize) -> Vec<u8> { let sequence: Vec<u8> = vec![2, 255, 0, 1]; let mut bytes: Vec<u8> = vec![]; let mut i = 0; while i < size { let (elements, _) = sequence.split_at(if size - i > sequence.len() { sequence.len() } else { size - i }); for &el in elements { bytes.push(el); } i += sequence.len(); } bytes } assert_eq!(universal(random, &['a', 'b', 'c'], 4), "cabc"); } } pub fn fast(random: fn(usize) -> Vec<u8>, alphabet: &[char], size: usize) -> String { let mut id = String::new(); let bytes = random(size); for i in 0..size { let index = bytes[i] & ((alphabet.len() as u8) - 1); id.push(alphabet[index as usize]); } id } #[cfg(test)] mod test_fast { use super::*; use std::collections::HashMap; #[test] fn correct_length () { let lengths: Vec<usize> = vec![21, 5, 17, 134, 1]; for l in lengths { let id = fast(random::standart, &alphabet::SAFE, l); assert_eq!(id.len(), l); } } #[test] fn url_friendly () { for _ in 0..10 { let id = fast(random::standart, &alphabet::SAFE, 21); for ch in id.chars() { assert!(alphabet::SAFE.contains(&ch)); } } } #[test] fn no_collisions () { let count = 1_000_000; let mut ids = HashMap::new(); for _ in 0..count { let id = fast(random::standart, &alphabet::SAFE, 21); if ids.contains_key(&id) { panic!(); } ids.insert(id, true); } } #[test] fn flat_distribution () { let count = 1_000_000; let length : usize = 21; let mut chars = HashMap::new(); for _ in 0..count { let id = fast(random::standart, &alphabet::SAFE, length); for ch in id.chars() { let counter = chars.entry(ch).or_insert(0); *counter += 1; } } for (_, &value) in &chars { let distribution = (value * alphabet::SAFE.len()) as f32 / (count as f32 * length as f32); assert_eq!(distribution.round(), 1.0) } } }