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}