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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
//!`fastuuid` provides fast UUID generation of guessable and unique 192-bit universally unique identifiers and simple support for 128-bit RFC-4122 V4 UUID.
//!Generated UUIDs are not unguessable as every generated UUID is adjacent to the previously generated UUID.
//!
//!It avoids generating reading 192 bit from rand on each UUID generation, and offers a API to fetch 128-bit string reference,
//!with or without allocating a new heap string object as well, with both safe and unsafe versions of the same function.
//!
//!Benchmarks are included. On my machine generation of a 192-bit UUID takes ~7n, while generating the 128-bit string
//!without and with additional heap allocation (unsafe version) takes <20ns & ~95ns respectively. Safe versions take additional ~10ns.
//!
//!It can be depended on with:
//!
//!```toml
//![dependencies]
//!fastuuid = "0.3.0"
//!```
//!
//!## Examples
//!#### 192-bit UUID
//!```rust
//!use fastuuid::Generator;
//!
//!fn main() {
//!    let generator = Generator::new();
//!    let uuid:[u8;24] = generator.next();
//!}
//!```
//!
//!#### 128-bit UUID
//!```rust
//!// with new string allocation
//!use fastuuid::Generator;
//! fn main() {
//!    let generator = Generator::new();
//!    let uuid = generator.hex128_as_string().unwrap();
//!}
//!```
//!```rust
//! // without new string allocation
//!use fastuuid::Generator;
//!
//!fn main() {
//!    let generator = Generator::new();
//!    let mut buffer: [u8; 36] = [0; 36];
//!    let uuid = generator.hex128_as_str(&mut buffer).unwrap();
//!}
//!```
//!
//!Note: there is also an unsafe version of both functions, which uses unsafe cast to string from utf8, making them a bit faster.
//!It is ok to use all of those concurrently.

extern crate faster_hex;
extern crate rand;

use rand::Rng;
use std::convert::TryInto;
use std::error::Error;
use std::sync::atomic::{AtomicUsize, Ordering};

// Generator is a uuid generator that generates unique and guessable 192-bit UUIDs, starting from a random sequence.
pub struct Generator {
    // The constant (random) 192-bit seed.
    // the first 8 bytes are stored in the counter and used for generating new UUIDs
    seed: [u8; 24],
    counter: AtomicUsize,
}

impl Generator {
    #[allow(dead_code)]
    pub fn new() -> Generator {
        let seed = rand::thread_rng().gen::<[u8; 24]>();
        Generator {
            seed,
            counter: AtomicUsize::new(
                u64::from_le_bytes(seed[0..8].try_into().unwrap())
                    .try_into()
                    .unwrap(),
            ),
        }
    }

    // Next returns the next UUID from the generator.
    // Only the first 8 bytes differ from the previous one.
    // It can be used concurrently.
    pub fn next(&self) -> [u8; 24] {
        let current = self.counter.fetch_add(1, Ordering::SeqCst);
        let mut uuid: [u8; 24] = Default::default();
        uuid[..8].copy_from_slice(&current.to_le_bytes());
        uuid[8..].copy_from_slice(&self.seed[8..]);
        return uuid;
    }

    // hex128_as_str returns hex128(Generator::next()) as &str (without heap allocation of the result)
    pub fn hex128_as_str<'a>(&self, buffer: &'a mut [u8; 36]) -> Result<&'a str, Box<dyn Error>> {
        match std::str::from_utf8(Generator::hex128_from_bytes(&self.next(), buffer)) {
            Ok(res) => Ok(res),
            Err(err) => Err(Box::new(err)),
        }
    }

    // hex128_as_str_unchecked returns hex128(Generator::next()) as &str (without heap allocation of the result)
    // Uses unsafe cast to string from utf8
    pub unsafe fn hex128_as_str_unchecked<'a>(&self, buffer: &'a mut [u8; 36]) -> &'a str {
        std::str::from_utf8_unchecked(Generator::hex128_from_bytes(&self.next(), buffer))
    }

    // hex128_as_string returns hex128(Generator::next()) as boxed String value
    pub unsafe fn hex128_as_string_unchecked(&self) -> String {
        let mut buffer: [u8; 36] = [0; 36];
        std::str::from_utf8_unchecked(Generator::hex128_from_bytes(&self.next(), &mut buffer))
            .to_owned()
    }

    // hex128_as_string returns hex128(Generator::next()) as boxed String value
    pub fn hex128_as_string(&self) -> Result<String, Box<dyn Error>> {
        let mut buffer: [u8; 36] = [0; 36];
        match std::str::from_utf8(Generator::hex128_from_bytes(&self.next(), &mut buffer)) {
            Ok(res) => Ok(res.to_owned()),
            Err(err) => Err(Box::new(err)),
        }
    }

    // Hex128 returns an RFC4122 V4 representation of the
    // first 128 bits of the given UUID, with hyphens.
    //
    // Example: 11febf98-c108-4383-bb1e-739ffcd44341
    //
    // Before encoding, it swaps bytes 6 and 9
    // so that all the varying bits of Generator.next()
    // are reflected in the resulting UUID.
    //
    // Note: If you want unpredictable UUIDs, you might want to consider
    // hashing the uuid (using SHA256, for example) before passing it
    // to Hex128.
    fn hex128_from_bytes<'a>(uuid: &[u8; 24], buffer: &'a mut [u8; 36]) -> &'a [u8] {
        let mut temp_uuid: [u8; 24] = [0; 24];
        temp_uuid.copy_from_slice(uuid);
        temp_uuid.swap(6, 9);

        // V4
        temp_uuid[6] = (temp_uuid[6] & 0x0f) | 0x40;
        // RFC4122
        temp_uuid[8] = temp_uuid[8] & 0x3f | 0x80;

        faster_hex::hex_encode(&temp_uuid[0..16], &mut buffer[0..32]).unwrap();
        buffer.copy_within(20..32, 24); // needs rust stable 1.37.0!!
        buffer.copy_within(16..20, 19);
        buffer.copy_within(12..16, 14);
        buffer.copy_within(8..12, 9);
        buffer[8] = b'-';
        buffer[13] = b'-';
        buffer[18] = b'-';
        buffer[23] = b'-';
        &buffer[..]
    }

    //  Returns true if provided string is a valid 128-bit UUID
    pub fn is_valid_hex128(uuid: &str) -> bool {
        let uuid_bytes = uuid.as_bytes();
        if uuid.len() != 36
            || uuid_bytes.len() != 36
            || uuid_bytes[8] != b'-'
            || uuid_bytes[13] != b'-'
            || uuid_bytes[18] != b'-'
            || uuid_bytes[23] != b'-'
        {
            return false;
        }

        return Generator::valid_hex(&uuid[..8])
            && Generator::valid_hex(&uuid[9..13])
            && Generator::valid_hex(&uuid[14..18])
            && Generator::valid_hex(&uuid[19..23])
            && Generator::valid_hex(&uuid[24..]);
    }

    fn valid_hex(hex: &str) -> bool {
        hex.chars()
            .all(|c| '0' <= c && c <= '9' || 'a' <= c && c <= 'f')
    }
}

#[cfg(test)]
mod tests {
    use crate::Generator;
    use std::thread;
    use std::collections::HashMap;
    use std::sync::{RwLock, Arc};

    #[test]
    fn next() {
        let generator = Generator::new();
        let mut first = generator.next();

        for _ in 0..10 {
            let second = generator.next();
            assert_eq!(
                first.len(),
                second.len(),
                "Arrays don't have the same length"
            );

            first = second;
        }
    }

    #[test]
    fn hex128() {
        let generator = Generator::new();
        let mut buffer: [u8; 36] = [0; 36];

        assert!(
            Generator::is_valid_hex128(&generator.hex128_as_str(&mut buffer).unwrap()[..]),
            "should be valid hex"
        );
    }

    #[test]
    fn uniqueness() {
        let mut uuids: HashMap<String, bool> = HashMap::new();
        let generator = Generator::new();
        let mut buffer: [u8; 36] = [0; 36];

        for _ in 0..100000 {
            let next = generator.hex128_as_str(&mut buffer).unwrap();
            assert!(!uuids.contains_key(&next.to_string()), "duplicate found");
            uuids.insert(next.to_string(), true);
        }
    }

    #[test]
    fn uniqueness_concurrent() {
        let generator = Arc::new(Generator::new());
        let data = Arc::new(RwLock::new(HashMap::new()));
        let threads: Vec<_> = (0..100)
            .map(|_| {
                let data = Arc::clone(&data);
                let generator = generator.clone();
                thread::spawn( move || {
                    let mut map = data.write().unwrap();
                    map.insert(generator.hex128_as_string().unwrap(), true);
                })})
            .collect();

        for t in threads {
            t.join().expect("Thread panicked");
        }


        let map = data.read().unwrap();
        assert_eq!(map.len(), 100, "generated non-unique uuids");
    }

    #[test]
    fn valid_hex() {
        // valid v4 uuid
        assert!(
            Generator::is_valid_hex128("11febf98-c108-4383-bb1e-739ffcd44341"),
            "should be valid hex"
        );

        // invalid uuid
        assert!(
            !Generator::is_valid_hex128("11febf98-c108-4383-bb1e-739ffcd4434"),
            "should be invalid hex"
        );
        assert!(
            !Generator::is_valid_hex128("11febf98-c108-4383-bb1e-739ffcd443412"),
            "should be invalid hex"
        );
        assert!(
            !Generator::is_valid_hex128("11febf98c1-08-4383-bb1e-739ffcd44341"),
            "should be invalid hex"
        );
        assert!(
            !Generator::is_valid_hex128("11febf98-c1084-383-bb1e-739ffcd44341"),
            "should be invalid hex"
        );
        assert!(
            !Generator::is_valid_hex128("11febf98-c108-4383bb-1e-739ffcd44341"),
            "should be invalid hex"
        );
        assert!(
            !Generator::is_valid_hex128("11febf98-c108-4383-bb1e7-39ffcd44341"),
            "should be invalid hex"
        );
    }
}