atomic_id/lib.rs
1//! # atomic-id
2//!
3//! A high-performance, thread-safe, and globally unique ID generator.
4//! Inspired by Snowflake, it supports multiple bit-widths (24, 32, 64, 128, 256)
5//! and encodings, making it suitable for a wide range of applications from database keys
6//! to distributed service identifiers.
7//!
8//! ## Features
9//! - **Multiple Bit-Widths**: Generate IDs of 24, 32, 64, 128, or 256 bits.
10//! - **Thread-Safe**: Uses atomic operations to guarantee uniqueness across threads.
11//! - **Distributed-System Ready**: Incorporates node and shard IDs for global uniqueness.
12//! - **Customizable Epoch**: Set a custom epoch for timestamp-based generation.
13//! - **Flexible Encodings**: Output IDs in base36, base58, base91, or hexadecimal.
14//! - **High Performance**: Optimized for low-latency ID generation in high-throughput systems.
15//!
16//! ## Feature Flags
17//! - `short`: Enables support for 24-bit and 32-bit IDs (`x24`, `x32`). This feature is disabled by default to keep the library lightweight.
18//!
19//! ## Quick Start
20//!
21//! Add `atomic-id` to your `Cargo.toml`. To use 24-bit or 32-bit IDs, enable the `short` feature.
22//! ```toml
23//! [dependencies]
24//! atomic-id = { version = "0.1", features = ["short"] } # Replace with the latest version
25//! ```
26//!
27//! ### Generating a 64-bit ID
28//! ```rust
29//! use atomic_id::{AtomicId, x64};
30//!
31//! // Generate a new base36-encoded 64-bit ID.
32//! let id = AtomicId::<x64>::new();
33//! println!("64-bit ID: {}", id);
34//! ```
35//!
36//! ## ID Structure
37//!
38//! The library generates IDs with different structures depending on the bit-width:
39//! - **24-bit**: `24-bit sequence`
40//! - **32-bit**: `8-bit thread ID | 24-bit sequence`
41//! - **64-bit**: `20-bit timestamp | 12-bit node ID | 8-bit shard ID | 8-bit thread ID | 16-bit sequence`
42//! - **128-bit & 256-bit**: More complex structures with higher entropy from timestamps, nanoseconds, and sequences.
43//!
44//! ## Advanced Usage
45//!
46//! ### Custom Epoch
47//! For timestamp-based IDs (64, 128, 256-bit), you can set a custom epoch.
48//! ```rust
49//! use atomic_id::AtomicOption;
50//! // Set a custom epoch to `2024-01-01 00:00:00 UTC` in milliseconds.
51//! AtomicOption::epoch(1704067200000);
52//! ```
53//!
54//! ### Different Encodings
55//! ```rust
56//! use atomic_id::{AtomicId, x64};
57//!
58//! let id_base58 = AtomicId::<x64>::base58();
59//! let id_hex = AtomicId::<x64>::hex();
60//!
61//! println!("Base58: {}", id_base58);
62//! println!("Hex: {}", id_hex);
63//! ```
64
65use std::collections::hash_map::DefaultHasher;
66use std::hash::{Hash, Hasher};
67use std::sync::atomic::{AtomicU64, Ordering};
68use std::time::{SystemTime, UNIX_EPOCH};
69
70/// Default custom epoch: 2022-05-01 00:00:00 UTC (milliseconds since UNIX_EPOCH)
71const DEFAULT_EPOCH: u64 = 1651363200000;
72
73/// Global custom epoch (milliseconds since UNIX_EPOCH).
74/// Used as the reference point for all timestamp-based IDs.
75/// Can be set/reset via [`AtomicOption`].
76static CUSTOM_EPOCH: AtomicU64 = AtomicU64::new(DEFAULT_EPOCH);
77
78/// Global sequence counters for each bit mode.
79/// These ensure atomic, thread-safe, and unique sequence numbers for each ID width.
80static SEQ_24: AtomicU64 = AtomicU64::new(0);
81static SEQ_32: AtomicU64 = AtomicU64::new(0);
82static SEQ_64: AtomicU64 = AtomicU64::new(0);
83static SEQ_128: AtomicU64 = AtomicU64::new(0);
84static SEQ_256: AtomicU64 = AtomicU64::new(0);
85
86// Thread-local storage for thread ID.
87// Each thread gets a unique ID (1-128) to add entropy to generated IDs.
88thread_local! {
89 static THREAD_ID: std::cell::Cell<u8> = std::cell::Cell::new(0);
90}
91
92/// The core generator struct for producing unique IDs.
93///
94/// This struct holds node and shard identifiers, which are incorporated into
95/// 64, 128, and 256-bit IDs to ensure uniqueness in a distributed environment.
96///
97/// While you can create an `IdGenerator` instance, the library is designed
98/// to be used through the static methods on [`AtomicId`], which manage a global generator.
99pub struct IdGenerator {
100 /// Node identifier (0-4095), used in 64, 128, and 256-bit IDs.
101 pub node_id: u16,
102 /// Shard identifier (0-255), used in 64, 128, and 256-bit IDs.
103 pub shard_id: u8,
104}
105
106impl IdGenerator {
107 /// Create a new generator with the given node and shard IDs.
108 ///
109 /// # Arguments
110 /// * `node_id` - Node identifier (0..=4095).
111 /// * `shard_id` - Shard identifier (0..=255).
112 ///
113 /// # Returns
114 /// A new [`IdGenerator`] instance.
115 pub fn new(node_id: u16, shard_id: u8) -> Self {
116 Self { node_id, shard_id }
117 }
118
119 /// Get the current timestamp in milliseconds, relative to the global custom epoch.
120 ///
121 /// # Returns
122 /// Milliseconds since the current epoch (see [`AtomicOption`]).
123 fn timestamp(&self) -> u64 {
124 let now = SystemTime::now()
125 .duration_since(UNIX_EPOCH)
126 .unwrap()
127 .as_millis() as u64;
128 now.saturating_sub(CUSTOM_EPOCH.load(Ordering::Relaxed))
129 }
130
131 /// Get the current timestamp in nanoseconds since the UNIX epoch.
132 ///
133 /// # Returns
134 /// Nanoseconds since UNIX_EPOCH as a `u64`.
135 fn nanos(&self) -> u64 {
136 SystemTime::now()
137 .duration_since(UNIX_EPOCH)
138 .unwrap()
139 .as_nanos() as u64
140 }
141
142 /// Get or assign a unique ID for the current thread.
143 ///
144 /// This method provides a thread-local ID from 1 to 128, which is used
145 /// as a source of entropy in ID generation.
146 ///
147 /// # Returns
148 /// A thread-local unique ID in the range 1..=128.
149 fn thread_id(&self) -> u8 {
150 THREAD_ID.with(|id| {
151 let tid = id.get();
152 if tid == 0 {
153 let mut hasher = DefaultHasher::new();
154 std::thread::current().id().hash(&mut hasher);
155 let new_id = ((hasher.finish() & 0x7F) as u8) + 1; // 1-128
156 id.set(new_id);
157 new_id
158 } else {
159 tid
160 }
161 })
162 }
163
164 /// Generate a 24-bit unique ID.
165 ///
166 /// The ID is generated from a single atomic counter that wraps around.
167 /// It is suitable for short-lived, high-throughput scenarios where a small ID is needed.
168 ///
169 /// - **Structure**: 24 bits for the sequence.
170 /// - **Uniqueness**: Guarantees up to 16.7 million (2^24) unique IDs before the counter wraps around.
171 ///
172 /// # Returns
173 /// A 24-bit unique ID as a `u32`.
174 pub fn gen24(&self) -> u32 {
175 let seq = SEQ_24.fetch_add(1, Ordering::Relaxed);
176 (seq & 0xFFFFFF) as u32
177 }
178
179 /// Generate a 32-bit unique ID.
180 ///
181 /// This ID combines a thread-specific identifier with a sequence number,
182 /// providing better collision resistance in multi-threaded applications than `gen24`.
183 ///
184 /// - **Structure**: 8 bits for the thread ID | 24 bits for the sequence.
185 ///
186 /// # Returns
187 /// A 32-bit unique ID as a `u32`.
188 pub fn gen32(&self) -> u32 {
189 let thread_id = self.thread_id();
190 let seq = SEQ_32.fetch_add(1, Ordering::Relaxed);
191
192 let thread_bits = ((thread_id as u32) & 0xFF) << 24;
193 let seq_bits = (seq & 0xFFFFFF) as u32;
194
195 thread_bits | seq_bits
196 }
197
198 /// Generate a 64-bit unique ID, inspired by Twitter's Snowflake.
199 ///
200 /// This ID is ideal for distributed systems, as it combines a timestamp,
201 /// node/shard identifiers, and a sequence number to ensure global uniqueness.
202 ///
203 /// - **Structure**: 20-bit timestamp | 12-bit node ID | 8-bit shard ID | 8-bit thread ID | 16-bit sequence.
204 /// - **Timestamp**: Milliseconds since the custom epoch, providing a lifespan of ~34 years (2^20 ms).
205 /// - **Node ID**: Supports up to 4096 nodes (2^12).
206 /// - **Shard ID**: Supports up to 256 shards per node (2^8).
207 /// - **Sequence**: Supports up to 65,536 IDs per millisecond per thread (2^16).
208 ///
209 /// # Returns
210 /// A 64-bit unique ID as a `u64`.
211 pub fn gen64(&self) -> u64 {
212 let ts = self.timestamp();
213 let thread_id = self.thread_id();
214 let seq = SEQ_64.fetch_add(1, Ordering::Relaxed);
215
216 let ts_bits = (ts & 0xFFFFF) << 44;
217 let node_bits = ((self.node_id & 0xFFF) as u64) << 32;
218 let shard_bits = ((self.shard_id as u64) & 0xFF) << 24;
219 let thread_bits = ((thread_id as u64) & 0xFF) << 16;
220 let seq_bits = seq & 0xFFFF;
221
222 ts_bits | node_bits | shard_bits | thread_bits | seq_bits
223 }
224
225 /// Generate a 128-bit unique ID with enhanced collision resistance.
226 ///
227 /// This ID uses a two-part structure to maximize entropy, combining a timestamp-based
228 /// part with a high-entropy part derived from nanoseconds and sequence numbers.
229 ///
230 /// - **High 64 bits**: 32-bit timestamp | 12-bit node | 8-bit shard | 8-bit thread | 4-bit reserved.
231 /// - **Low 64 bits**: 32-bit nanoseconds | 24-bit sequence | 8-bit rotated thread ID.
232 ///
233 /// # Returns
234 /// A 128-bit unique ID as a `u128`.
235 pub fn gen128(&self) -> u128 {
236 let ts = self.timestamp();
237 let thread_id = self.thread_id();
238 let nanos = self.nanos();
239 let seq = SEQ_128.fetch_add(1, Ordering::Relaxed);
240
241 // First 64 bits: Enhanced timestamp-based entropy
242 // 32-bit timestamp | 12-bit node | 8-bit shard | 8-bit thread | 4-bit reserved
243 let high_part = {
244 let ts_bits = (ts & 0xFFFFFFFF) << 32;
245 let node_bits = ((self.node_id & 0xFFF) as u64) << 20;
246 let shard_bits = ((self.shard_id as u64) & 0xFF) << 12;
247 let thread_bits = ((thread_id as u64) & 0xFF) << 4;
248 let reserved = (nanos.rotate_right(16)) & 0xF;
249
250 ts_bits | node_bits | shard_bits | thread_bits | reserved
251 };
252
253 // Second 64 bits: Maximum entropy mixing
254 // 32-bit nanos | 24-bit sequence | 8-bit thread rotated
255 let low_part = {
256 let nanos_bits = (nanos & 0xFFFFFFFF) << 32;
257 let seq_bits = (seq & 0xFFFFFF) << 8;
258 let thread_rot = thread_id.rotate_left(3) as u64;
259
260 nanos_bits | seq_bits | thread_rot
261 };
262
263 ((high_part as u128) << 64) | (low_part as u128)
264 }
265
266 /// Generate a 256-bit unique ID for maximum entropy and uniqueness.
267 ///
268 /// This ID is constructed from four 64-bit parts, each derived from different
269 /// sources of entropy (timestamps, nanoseconds, node/shard/thread IDs, and sequences).
270 /// It is suitable for applications requiring cryptographic-level uniqueness.
271 ///
272 /// # Returns
273 /// An array of four `u64` values representing the 256-bit ID.
274 pub fn gen256(&self) -> [u64; 4] {
275 let ts = self.timestamp();
276 let thread_id = self.thread_id();
277 let nanos = self.nanos();
278 let seq = SEQ_256.fetch_add(1, Ordering::Relaxed);
279
280 // Part 0: Base 64-bit structure (like gen64 but with different sequence)
281 let part0 = {
282 let ts_bits = (ts & 0xFFFFF) << 44;
283 let node_bits = ((self.node_id & 0xFFF) as u64) << 32;
284 let shard_bits = ((self.shard_id as u64) & 0xFF) << 24;
285 let thread_bits = ((thread_id as u64) & 0xFF) << 16;
286 let seq_bits = seq & 0xFFFF;
287
288 ts_bits | node_bits | shard_bits | thread_bits | seq_bits
289 };
290
291 // Part 1: Nanosecond precision with entropy mixing
292 let part1 = {
293 let nanos_high = (nanos >> 32) & 0xFFFFFFFF;
294 let nanos_low = nanos & 0xFFFFFFFF;
295 let mixed = nanos_high.rotate_left(16) ^ nanos_low;
296
297 (mixed << 32) | ((seq.rotate_right(8)) & 0xFFFFFFFF)
298 };
299
300 // Part 2: Thread and sequence entropy
301 let part2 = {
302 let thread_expanded = ((thread_id as u64) << 56) |
303 ((thread_id as u64).rotate_left(8) << 48) |
304 ((thread_id as u64).rotate_left(16) << 40) |
305 ((thread_id as u64).rotate_left(24) << 32);
306 let seq_mixed = (seq.rotate_left(16)) & 0xFFFFFFFF;
307
308 thread_expanded | seq_mixed
309 };
310
311 // Part 3: Additional entropy sources
312 let part3 = {
313 let ts_rotated = ts.rotate_right(12);
314 let node_expanded = ((self.node_id as u64) << 48) |
315 ((self.node_id as u64).rotate_left(4) << 32);
316 let shard_expanded = ((self.shard_id as u64) << 24) |
317 ((self.shard_id as u64).rotate_left(2) << 16) |
318 ((self.shard_id as u64).rotate_left(4) << 8) |
319 (self.shard_id as u64).rotate_left(6);
320 let mixed_entropy = (ts_rotated & 0xFFFF) | node_expanded | shard_expanded;
321
322 mixed_entropy
323 };
324
325 [part0, part1, part2, part3]
326 }
327}
328
329/// Encoding utilities for converting numeric IDs to various string representations.
330///
331/// Supported encodings:
332/// - `base36`: `[0-9a-z]`
333/// - `base58`: Bitcoin alphabet (e.g., for short URLs)
334/// - `base91`: ASCII-safe and URL-safe
335/// - `hex`: `[0-9a-f]`
336mod encode {
337 /// Bitcoin-style base58 alphabet (no `0`, `O`, `I`, `l`).
338 const BASE58: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
339 /// Base91 alphabet (ASCII-safe, URL-safe).
340 const BASE91: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~\"";
341 /// Base36 alphabet (0-9, a-z).
342 const BASE36: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
343
344 /// Convert a number to a string in the given base and alphabet.
345 ///
346 /// # Arguments
347 /// * `n` - The number to encode.
348 /// * `base` - The target base (e.g., 36, 58, 91).
349 /// * `alphabet` - The character set for encoding.
350 /// * `min_width` - The minimum width of the output string, padded with the first alphabet character.
351 ///
352 /// # Returns
353 /// The encoded string.
354 pub fn to_base(mut n: u128, base: usize, alphabet: &[u8], min_width: usize) -> String {
355 if n == 0 {
356 return String::from_utf8(vec![alphabet[0]; min_width]).unwrap();
357 }
358
359 let mut chars = Vec::with_capacity(min_width);
360 while n > 0 {
361 chars.push(alphabet[(n % base as u128) as usize]);
362 n /= base as u128;
363 }
364
365 while chars.len() < min_width {
366 chars.push(alphabet[0]);
367 }
368
369 chars.reverse();
370 String::from_utf8(chars).unwrap()
371 }
372
373 /// Encode a number as a base58 string.
374 pub fn base58(n: u128, width: usize) -> String {
375 to_base(n, 58, BASE58, width)
376 }
377
378 /// Encode a number as a base91 string.
379 pub fn base91(n: u128, width: usize) -> String {
380 to_base(n, 91, BASE91, width)
381 }
382
383 /// Encode a number as a base36 string.
384 pub fn base36(n: u128, width: usize) -> String {
385 to_base(n, 36, BASE36, width)
386 }
387
388 /// Encode a number as a hexadecimal string.
389 pub fn hex(n: u128, width: usize) -> String {
390 format!("{:0width$x}", n, width = width)
391 }
392}
393
394/// Global generator instance, initialized on first use.
395/// Used by all [`AtomicId`] operations.
396static GENERATOR: std::sync::OnceLock<IdGenerator> = std::sync::OnceLock::new();
397
398/// Get a reference to the global [`IdGenerator`] instance.
399///
400/// Initializes the generator with default values (node_id=1, shard_id=0) on first call.
401fn xgen() -> &'static IdGenerator {
402 GENERATOR.get_or_init(|| IdGenerator::new(1, 0))
403}
404
405// Bit mode constants for compile-time selection.
406#[cfg(feature = "short")]
407#[allow(non_upper_case_globals)]
408/// Constant for 24-bit mode (requires the `short` feature).
409pub const x24: usize = 24;
410#[cfg(feature = "short")]
411#[allow(non_upper_case_globals)]
412/// Constant for 32-bit mode (requires the `short` feature).
413pub const x32: usize = 32;
414#[allow(non_upper_case_globals)]
415/// Constant for 64-bit mode.
416pub const x64: usize = 64;
417#[allow(non_upper_case_globals)]
418/// Constant for 128-bit mode.
419pub const x128: usize = 128;
420#[allow(non_upper_case_globals)]
421/// Constant for 256-bit mode.
422pub const x256: usize = 256;
423
424/// The main entry point for generating atomic IDs of a specific bit width.
425///
426/// Use the const generic `BITS` parameter to select the desired ID size.
427/// For 24 and 32-bit IDs, the `short` feature must be enabled.
428///
429/// # Examples
430///
431/// ```
432/// use atomic_id::{AtomicId, x64, x128};
433///
434/// // Generate a 64-bit ID
435/// let id64 = AtomicId::<x64>::new();
436///
437/// // Generate a 128-bit ID
438/// let id128 = AtomicId::<x128>::new();
439/// ```
440pub struct AtomicId<const BITS: usize>;
441
442#[cfg(feature = "short")]
443impl AtomicId<24> {
444 /// Generate a new 24-bit ID, encoded as a 5-character base36 string.
445 ///
446 /// # Example
447 /// ```
448 /// use atomic_id::{AtomicId, x24};
449 /// let id = AtomicId::<x24>::new();
450 /// assert_eq!(id.len(), 5);
451 /// ```
452 pub fn new() -> String {
453 encode::base36(xgen().gen24() as u128, 5)
454 }
455 /// Generate a new 24-bit ID, encoded as a 5-character base58 string.
456 ///
457 /// # Example
458 /// ```
459 /// use atomic_id::{AtomicId, x24};
460 /// let id = AtomicId::<x24>::base58();
461 /// assert_eq!(id.len(), 5);
462 /// ```
463 pub fn base58() -> String {
464 encode::base58(xgen().gen24() as u128, 5)
465 }
466 /// Generate a new 24-bit ID, encoded as a 4-character base91 string.
467 ///
468 /// # Example
469 /// ```
470 /// use atomic_id::{AtomicId, x24};
471 /// let id = AtomicId::<x24>::base91();
472 /// assert_eq!(id.len(), 4);
473 /// ```
474 pub fn base91() -> String {
475 encode::base91(xgen().gen24() as u128, 4)
476 }
477 /// Generate a new 24-bit ID, encoded as a 5-character base36 string.
478 ///
479 /// # Example
480 /// ```
481 /// use atomic_id::{AtomicId, x24};
482 /// let id = AtomicId::<x24>::base36();
483 /// assert_eq!(id.len(), 5);
484 /// ```
485 pub fn base36() -> String {
486 encode::base36(xgen().gen24() as u128, 5)
487 }
488 /// Generate a new 24-bit ID, encoded as a 6-character hexadecimal string.
489 ///
490 /// # Example
491 /// ```
492 /// use atomic_id::{AtomicId, x24};
493 /// let id = AtomicId::<x24>::hex();
494 /// assert_eq!(id.len(), 6);
495 /// ```
496 pub fn hex() -> String {
497 encode::hex(xgen().gen24() as u128, 6)
498 }
499
500 /// Generate a batch of 24-bit IDs, encoded as base36 strings.
501 ///
502 /// # Arguments
503 /// * `n` - The number of IDs to generate.
504 ///
505 /// # Returns
506 /// A vector of base36-encoded ID strings.
507 ///
508 /// # Example
509 /// ```
510 /// use atomic_id::{AtomicId, x24};
511 /// let ids = AtomicId::<x24>::batch(3);
512 /// assert_eq!(ids.len(), 3);
513 /// ```
514 pub fn batch(n: usize) -> Vec<String> {
515 (0..n).map(|_| Self::new()).collect()
516 }
517 /// Generate a batch of 24-bit IDs as base58 strings.
518 pub fn base58_batch(n: usize) -> Vec<String> {
519 (0..n).map(|_| Self::base58()).collect()
520 }
521 /// Generate a batch of 24-bit IDs as base91 strings.
522 pub fn base91_batch(n: usize) -> Vec<String> {
523 (0..n).map(|_| Self::base91()).collect()
524 }
525 /// Generate a batch of 24-bit IDs as base36 strings.
526 pub fn base36_batch(n: usize) -> Vec<String> {
527 (0..n).map(|_| Self::base36()).collect()
528 }
529 /// Generate a batch of 24-bit IDs as hexadecimal strings.
530 pub fn hex_batch(n: usize) -> Vec<String> {
531 (0..n).map(|_| Self::hex()).collect()
532 }
533}
534
535#[cfg(feature = "short")]
536impl AtomicId<32> {
537 /// Generate a new 32-bit ID, encoded as a 7-character base36 string.
538 ///
539 /// # Example
540 /// ```
541 /// use atomic_id::{AtomicId, x32};
542 /// let id = AtomicId::<x32>::new();
543 /// assert_eq!(id.len(), 7);
544 /// ```
545 pub fn new() -> String {
546 encode::base36(xgen().gen32() as u128, 7)
547 }
548 /// Generate a new 32-bit ID, encoded as a 6-character base58 string.
549 ///
550 /// # Example
551 /// ```
552 /// use atomic_id::{AtomicId, x32};
553 /// let id = AtomicId::<x32>::base58();
554 /// assert_eq!(id.len(), 6);
555 /// ```
556 pub fn base58() -> String {
557 encode::base58(xgen().gen32() as u128, 6)
558 }
559 /// Generate a new 32-bit ID, encoded as a 5-character base91 string.
560 ///
561 /// # Example
562 /// ```
563 /// use atomic_id::{AtomicId, x32};
564 /// let id = AtomicId::<x32>::base91();
565 /// assert_eq!(id.len(), 5);
566 /// ```
567 pub fn base91() -> String {
568 encode::base91(xgen().gen32() as u128, 5)
569 }
570 /// Generate a new 32-bit ID, encoded as a 7-character base36 string.
571 ///
572 /// # Example
573 /// ```
574 /// use atomic_id::{AtomicId, x32};
575 /// let id = AtomicId::<x32>::base36();
576 /// assert_eq!(id.len(), 7);
577 /// ```
578 pub fn base36() -> String {
579 encode::base36(xgen().gen32() as u128, 7)
580 }
581 /// Generate a new 32-bit ID, encoded as an 8-character hexadecimal string.
582 ///
583 /// # Example
584 /// ```
585 /// use atomic_id::{AtomicId, x32};
586 /// let id = AtomicId::<x32>::hex();
587 /// assert_eq!(id.len(), 8);
588 /// ```
589 pub fn hex() -> String {
590 encode::hex(xgen().gen32() as u128, 8)
591 }
592
593 /// Generate a batch of 32-bit IDs, encoded as base36 strings.
594 ///
595 /// # Example
596 /// ```
597 /// use atomic_id::{AtomicId, x32};
598 /// let ids = AtomicId::<x32>::batch(3);
599 /// assert_eq!(ids.len(), 3);
600 /// ```
601 pub fn batch(n: usize) -> Vec<String> {
602 (0..n).map(|_| Self::new()).collect()
603 }
604 /// Generate a batch of 32-bit IDs as base58 strings.
605 pub fn base58_batch(n: usize) -> Vec<String> {
606 (0..n).map(|_| Self::base58()).collect()
607 }
608 /// Generate a batch of 32-bit IDs as base91 strings.
609 pub fn base91_batch(n: usize) -> Vec<String> {
610 (0..n).map(|_| Self::base91()).collect()
611 }
612 /// Generate a batch of 32-bit IDs as base36 strings.
613 pub fn base36_batch(n: usize) -> Vec<String> {
614 (0..n).map(|_| Self::base36()).collect()
615 }
616 /// Generate a batch of 32-bit IDs as hexadecimal strings.
617 pub fn hex_batch(n: usize) -> Vec<String> {
618 (0..n).map(|_| Self::hex()).collect()
619 }
620}
621
622impl AtomicId<64> {
623 /// Generate a new 64-bit ID, encoded as a 13-character base36 string.
624 ///
625 /// # Example
626 /// ```
627 /// use atomic_id::{AtomicId, x64};
628 /// let id = AtomicId::<x64>::new();
629 /// assert_eq!(id.len(), 13);
630 /// ```
631 pub fn new() -> String {
632 encode::base36(xgen().gen64() as u128, 13)
633 }
634 /// Generate a new 64-bit ID, encoded as an 11-character base58 string.
635 ///
636 /// # Example
637 /// ```
638 /// use atomic_id::{AtomicId, x64};
639 /// let id = AtomicId::<x64>::base58();
640 /// assert_eq!(id.len(), 11);
641 /// ```
642 pub fn base58() -> String {
643 encode::base58(xgen().gen64() as u128, 11)
644 }
645 /// Generate a new 64-bit ID, encoded as a 10-character base91 string.
646 ///
647 /// # Example
648 /// ```
649 /// use atomic_id::{AtomicId, x64};
650 /// let id = AtomicId::<x64>::base91();
651 /// assert_eq!(id.len(), 10);
652 /// ```
653 pub fn base91() -> String {
654 encode::base91(xgen().gen64() as u128, 10)
655 }
656 /// Generate a new 64-bit ID, encoded as a 13-character base36 string.
657 ///
658 /// # Example
659 /// ```
660 /// use atomic_id::{AtomicId, x64};
661 /// let id = AtomicId::<x64>::base36();
662 /// assert_eq!(id.len(), 13);
663 /// ```
664 pub fn base36() -> String {
665 encode::base36(xgen().gen64() as u128, 13)
666 }
667 /// Generate a new 64-bit ID, encoded as a 16-character hexadecimal string.
668 ///
669 /// # Example
670 /// ```
671 /// use atomic_id::{AtomicId, x64};
672 /// let id = AtomicId::<x64>::hex();
673 /// assert_eq!(id.len(), 16);
674 /// ```
675 pub fn hex() -> String {
676 encode::hex(xgen().gen64() as u128, 16)
677 }
678
679 /// Generate a batch of 64-bit IDs, encoded as base36 strings.
680 ///
681 /// # Example
682 /// ```
683 /// use atomic_id::{AtomicId, x64};
684 /// let ids = AtomicId::<x64>::batch(3);
685 /// assert_eq!(ids.len(), 3);
686 /// ```
687 pub fn batch(n: usize) -> Vec<String> {
688 (0..n).map(|_| Self::new()).collect()
689 }
690 /// Generate a batch of 64-bit IDs as base58 strings.
691 pub fn base58_batch(n: usize) -> Vec<String> {
692 (0..n).map(|_| Self::base58()).collect()
693 }
694 /// Generate a batch of 64-bit IDs as base91 strings.
695 pub fn base91_batch(n: usize) -> Vec<String> {
696 (0..n).map(|_| Self::base91()).collect()
697 }
698 /// Generate a batch of 64-bit IDs as base36 strings.
699 pub fn base36_batch(n: usize) -> Vec<String> {
700 (0..n).map(|_| Self::base36()).collect()
701 }
702 /// Generate a batch of 64-bit IDs as hexadecimal strings.
703 pub fn hex_batch(n: usize) -> Vec<String> {
704 (0..n).map(|_| Self::hex()).collect()
705 }
706
707 /// Generate a sequential 64-bit ID as a base36 string.
708 ///
709 /// This method uses a simple atomic counter, making the IDs sequential but not
710 /// time-sortable. It is useful for scenarios where strict ordering is more
711 /// important than distributed uniqueness.
712 ///
713 /// # Example
714 /// ```
715 /// use atomic_id::{AtomicId, x64};
716 /// let id1 = AtomicId::<x64>::sequential();
717 /// let id2 = AtomicId::<x64>::sequential();
718 /// // id2 will be lexicographically greater than id1
719 /// assert!(id2 > id1);
720 /// ```
721 pub fn sequential() -> String {
722 static COUNTER: AtomicU64 = AtomicU64::new(0);
723 let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
724 encode::base36(seq as u128, 13)
725 }
726
727 /// Generate a sequential 64-bit ID as a base58 string.
728 pub fn sequential_base58() -> String {
729 static COUNTER: AtomicU64 = AtomicU64::new(0);
730 let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
731 encode::base58(seq as u128, 11)
732 }
733
734 /// Generate a sequential 64-bit ID as a base91 string.
735 pub fn sequential_base91() -> String {
736 static COUNTER: AtomicU64 = AtomicU64::new(0);
737 let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
738 encode::base91(seq as u128, 10)
739 }
740
741 /// Generate a sequential 64-bit ID as a base36 string.
742 pub fn sequential_base36() -> String {
743 static COUNTER: AtomicU64 = AtomicU64::new(0);
744 let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
745 encode::base36(seq as u128, 13)
746 }
747
748 /// Generate a sequential 64-bit ID as a hexadecimal string.
749 pub fn sequential_hex() -> String {
750 static COUNTER: AtomicU64 = AtomicU64::new(0);
751 let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
752 encode::hex(seq as u128, 16)
753 }
754
755 /// Generate a batch of sequential 64-bit IDs as base36 strings.
756 pub fn sequential_batch(n: usize) -> Vec<String> {
757 (0..n).map(|_| Self::sequential()).collect()
758 }
759 /// Generate a batch of sequential 64-bit IDs as base58 strings.
760 pub fn sequential_base58_batch(n: usize) -> Vec<String> {
761 (0..n).map(|_| Self::sequential_base58()).collect()
762 }
763 /// Generate a batch of sequential 64-bit IDs as base91 strings.
764 pub fn sequential_base91_batch(n: usize) -> Vec<String> {
765 (0..n).map(|_| Self::sequential_base91()).collect()
766 }
767 /// Generate a batch of sequential 64-bit IDs as base36 strings.
768 pub fn sequential_base36_batch(n: usize) -> Vec<String> {
769 (0..n).map(|_| Self::sequential_base36()).collect()
770 }
771 /// Generate a batch of sequential 64-bit IDs as hexadecimal strings.
772 pub fn sequential_hex_batch(n: usize) -> Vec<String> {
773 (0..n).map(|_| Self::sequential_hex()).collect()
774 }
775}
776
777impl AtomicId<128> {
778 /// Generate a new 128-bit ID, encoded as a 25-character base36 string.
779 ///
780 /// # Example
781 /// ```
782 /// use atomic_id::{AtomicId, x128};
783 /// let id = AtomicId::<x128>::new();
784 /// assert_eq!(id.len(), 25);
785 /// ```
786 pub fn new() -> String {
787 encode::base36(xgen().gen128(), 25)
788 }
789 /// Generate a new 128-bit ID, encoded as a 22-character base58 string.
790 ///
791 /// # Example
792 /// ```
793 /// use atomic_id::{AtomicId, x128};
794 /// let id = AtomicId::<x128>::base58();
795 /// assert_eq!(id.len(), 22);
796 /// ```
797 pub fn base58() -> String {
798 encode::base58(xgen().gen128(), 22)
799 }
800 /// Generate a new 128-bit ID, encoded as a 20-character base91 string.
801 ///
802 /// # Example
803 /// ```
804 /// use atomic_id::{AtomicId, x128};
805 /// let id = AtomicId::<x128>::base91();
806 /// assert_eq!(id.len(), 20);
807 /// ```
808 pub fn base91() -> String {
809 encode::base91(xgen().gen128(), 20)
810 }
811 /// Generate a new 128-bit ID, encoded as a 25-character base36 string.
812 ///
813 /// # Example
814 /// ```
815 /// use atomic_id::{AtomicId, x128};
816 /// let id = AtomicId::<x128>::base36();
817 /// assert_eq!(id.len(), 25);
818 /// ```
819 pub fn base36() -> String {
820 encode::base36(xgen().gen128(), 25)
821 }
822 /// Generate a new 128-bit ID, encoded as a 32-character hexadecimal string.
823 ///
824 /// # Example
825 /// ```
826 /// use atomic_id::{AtomicId, x128};
827 /// let id = AtomicId::<x128>::hex();
828 /// assert_eq!(id.len(), 32);
829 /// ```
830 pub fn hex() -> String {
831 encode::hex(xgen().gen128(), 32)
832 }
833
834 /// Generate a batch of 128-bit IDs, encoded as base36 strings.
835 ///
836 /// # Example
837 /// ```
838 /// use atomic_id::{AtomicId, x128};
839 /// let ids = AtomicId::<x128>::batch(3);
840 /// assert_eq!(ids.len(), 3);
841 /// ```
842 pub fn batch(n: usize) -> Vec<String> {
843 (0..n).map(|_| Self::new()).collect()
844 }
845 /// Generate a batch of 128-bit IDs as base58 strings.
846 pub fn base58_batch(n: usize) -> Vec<String> {
847 (0..n).map(|_| Self::base58()).collect()
848 }
849 /// Generate a batch of 128-bit IDs as base91 strings.
850 pub fn base91_batch(n: usize) -> Vec<String> {
851 (0..n).map(|_| Self::base91()).collect()
852 }
853 /// Generate a batch of 128-bit IDs as base36 strings.
854 pub fn base36_batch(n: usize) -> Vec<String> {
855 (0..n).map(|_| Self::base36()).collect()
856 }
857 /// Generate a batch of 128-bit IDs as hexadecimal strings.
858 pub fn hex_batch(n: usize) -> Vec<String> {
859 (0..n).map(|_| Self::hex()).collect()
860 }
861}
862
863impl AtomicId<256> {
864 /// Generate a new 256-bit ID, encoded as a 52-character base36 string.
865 ///
866 /// The raw 256-bit ID is composed of four 64-bit parts, each of which is
867 /// encoded into a 13-character base36 string and then joined.
868 ///
869 /// # Example
870 /// ```
871 /// use atomic_id::{AtomicId, x256};
872 /// let id = AtomicId::<x256>::new();
873 /// assert_eq!(id.len(), 52);
874 /// ```
875 pub fn new() -> String {
876 let parts = xgen().gen256();
877 parts
878 .iter()
879 .map(|&p| encode::base36(p as u128, 13))
880 .collect::<Vec<_>>()
881 .join("")
882 }
883
884 /// Generate a new 256-bit ID, encoded as a 44-character base58 string.
885 ///
886 /// # Example
887 /// ```
888 /// use atomic_id::{AtomicId, x256};
889 /// let id = AtomicId::<x256>::base58();
890 /// assert_eq!(id.len(), 44);
891 /// ```
892 pub fn base58() -> String {
893 let parts = xgen().gen256();
894 parts
895 .iter()
896 .map(|&p| encode::base58(p as u128, 11))
897 .collect::<Vec<_>>()
898 .join("")
899 }
900
901 /// Generate a new 256-bit ID, encoded as a 40-character base91 string.
902 ///
903 /// # Example
904 /// ```
905 /// use atomic_id::{AtomicId, x256};
906 /// let id = AtomicId::<x256>::base91();
907 /// assert_eq!(id.len(), 40);
908 /// ```
909 pub fn base91() -> String {
910 let parts = xgen().gen256();
911 parts
912 .iter()
913 .map(|&p| encode::base91(p as u128, 10))
914 .collect::<Vec<_>>()
915 .join("")
916 }
917
918 /// Generate a new 256-bit ID, encoded as a 52-character base36 string.
919 ///
920 /// # Example
921 /// ```
922 /// use atomic_id::{AtomicId, x256};
923 /// let id = AtomicId::<x256>::base36();
924 /// assert_eq!(id.len(), 52);
925 /// ```
926 pub fn base36() -> String {
927 let parts = xgen().gen256();
928 parts
929 .iter()
930 .map(|&p| encode::base36(p as u128, 13))
931 .collect::<Vec<_>>()
932 .join("")
933 }
934
935 /// Generate a new 256-bit ID, encoded as a 64-character hexadecimal string.
936 ///
937 /// # Example
938 /// ```
939 /// use atomic_id::{AtomicId, x256};
940 /// let id = AtomicId::<x256>::hex();
941 /// assert_eq!(id.len(), 64);
942 /// ```
943 pub fn hex() -> String {
944 let parts = xgen().gen256();
945 format!(
946 "{:016x}{:016x}{:016x}{:016x}",
947 parts[0], parts[1], parts[2], parts[3]
948 )
949 }
950
951 /// Generate a batch of 256-bit IDs, encoded as base36 strings.
952 ///
953 /// # Example
954 /// ```
955 /// use atomic_id::{AtomicId, x256};
956 /// let ids = AtomicId::<x256>::batch(3);
957 /// assert_eq!(ids.len(), 3);
958 /// ```
959 pub fn batch(n: usize) -> Vec<String> {
960 (0..n).map(|_| Self::new()).collect()
961 }
962 /// Generate a batch of 256-bit IDs as base58 strings.
963 pub fn base58_batch(n: usize) -> Vec<String> {
964 (0..n).map(|_| Self::base58()).collect()
965 }
966 /// Generate a batch of 256-bit IDs as base91 strings.
967 pub fn base91_batch(n: usize) -> Vec<String> {
968 (0..n).map(|_| Self::base91()).collect()
969 }
970 /// Generate a batch of 256-bit IDs as base36 strings.
971 pub fn base36_batch(n: usize) -> Vec<String> {
972 (0..n).map(|_| Self::base36()).collect()
973 }
974 /// Generate a batch of 256-bit IDs as hexadecimal strings.
975 pub fn hex_batch(n: usize) -> Vec<String> {
976 (0..n).map(|_| Self::hex()).collect()
977 }
978}
979
980/// Provides methods for configuring global settings for `atomic-id`.
981///
982/// Use this struct to manage the global epoch for timestamp-based ID generation.
983pub struct AtomicOption;
984
985impl AtomicOption {
986 /// Set the global custom epoch for timestamp-based IDs.
987 ///
988 /// The epoch is the point in time from which the timestamp portion of an ID is measured.
989 /// Setting a more recent epoch can extend the lifespan of the generator.
990 ///
991 /// # Arguments
992 /// * `ms` - The epoch timestamp in milliseconds since the UNIX epoch.
993 ///
994 /// # Example
995 /// ```
996 /// use atomic_id::AtomicOption;
997 /// // Set the epoch to January 1, 2024
998 /// AtomicOption::epoch(1704067200000);
999 /// ```
1000 pub fn epoch(ms: u64) {
1001 CUSTOM_EPOCH.store(ms, Ordering::Relaxed);
1002 }
1003
1004 /// Get the current global epoch value.
1005 ///
1006 /// # Returns
1007 /// The current epoch in milliseconds since the UNIX epoch.
1008 pub fn get_epoch() -> u64 {
1009 CUSTOM_EPOCH.load(Ordering::Relaxed)
1010 }
1011
1012 /// Reset the global epoch to its default value (`2022-05-01 00:00:00 UTC`).
1013 pub fn reset_epoch() {
1014 CUSTOM_EPOCH.store(DEFAULT_EPOCH, Ordering::Relaxed);
1015 }
1016}
1017
1018
1019#[cfg(test)]
1020mod tests {
1021 use super::*;
1022
1023 /// Test generation of 64-bit IDs in all encodings.
1024 #[test]
1025 fn test_gen64() {
1026 let id = AtomicId::<64>::new();
1027 println!("Generated ID: {}", id);
1028 let id = AtomicId::<64>::base36();
1029 println!("Generated ID: {}", id);
1030 let id = AtomicId::<64>::base58();
1031 println!("Generated ID: {}", id);
1032 let id = AtomicId::<64>::base91();
1033 println!("Generated ID: {}", id);
1034 let id = AtomicId::<64>::hex();
1035 println!("Generated ID: {}", id);
1036 }
1037
1038 /// Test generation of 128-bit IDs.
1039 #[test]
1040 fn test_gen128() {
1041 let id = AtomicId::<128>::base36();
1042 println!("Generated ID: {}", id);
1043 }
1044
1045 /// Test generation of 256-bit IDs.
1046 #[test]
1047 fn test_gen256() {
1048 let id = AtomicId::<256>::base36();
1049 println!("Generated ID: {}", id);
1050 }
1051
1052 /// Test uniqueness of 64-bit IDs over 10 million generations.
1053 #[test]
1054 #[ignore] // This test is long-running and should be run manually.
1055 fn test_uniqueness_64(){
1056 // test uniqueness of 64-bit IDs
1057 let mut ids = std::collections::HashSet::new();
1058 for _ in 0..10_000_000 {
1059 let id = AtomicId::<64>::new();
1060 assert!(ids.insert(id), "Duplicate ID found");
1061 }
1062 println!("All IDs are unique");
1063 }
1064}