use core::{fmt, str::FromStr}; use std::{ sync::atomic::{AtomicU64, Ordering}, time::Duration, }; use rand::{random, thread_rng, Rng}; use crate::base32::{self, DecodeError}; /// This is used to ensure monotonicity for new IDs. static LAST_SORTABLE: AtomicU64 = AtomicU64::new(0); /// The number of bits in a Julid's time portion pub const TIME_BITS: u8 = 48; /// The number of bits in the monotonic counter for intra-millisecond IDs pub const COUNTER_BITS: u8 = 16; /// The number of random bits + bits in the monotonic counter pub const UNIQUE_BITS: u8 = 80; pub const RANDOM_BITS: u8 = 64; macro_rules! bitmask { ($len:expr) => { ((1 << $len) - 1) }; } /// A Julid is a unique 128-bit lexicographically sortable identifier, /// compatible with ULIDs. /// /// Canonically, it is represented as a 26 character Crockford Base32 encoded /// string, or as a sequence of 16 bytes in big-endian order. /// /// Of the 128-bits, the 48 most-significant are a unix timestamp in /// milliseconds. The next 16 bits are a monotonic counter for IDs created in /// the same millisecond. The remaining 64 least-significant bits are fully /// random. #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)] pub struct Julid(pub u128); impl Julid { /// Return a new Julid. If a previous ID was generated in the same /// millisecond, increment the monotonic counter, up to u16::MAX. The random /// bits are always fresh, so once the monotonic counter is saturated, /// subsequent IDs from the current millisecond will not have an /// inherent ordering. See discussion at https://github.com/ahawker/ulid/issues/306#issuecomment-451850395 pub fn new() -> Self { let lsb: u64 = random(); loop { let ts = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap_or(Duration::ZERO) .as_millis() as u64; let last = LAST_SORTABLE.load(Ordering::SeqCst); let ots = last >> COUNTER_BITS; if ots < ts { let msb = ts << COUNTER_BITS; if LAST_SORTABLE .compare_exchange(last, msb, Ordering::SeqCst, Ordering::Relaxed) .is_ok() { break (msb, lsb).into(); } } else { let counter = ((last & bitmask!(COUNTER_BITS) as u64) as u16).saturating_add(1); let msb = (ots << COUNTER_BITS) + counter as u64; if LAST_SORTABLE .compare_exchange(last, msb, Ordering::SeqCst, Ordering::Relaxed) .is_ok() { break (msb, lsb).into(); } } // we didn't update the global counter, try again let micros = thread_rng().gen_range(10..50); std::thread::sleep(Duration::from_micros(micros)); } } /// Creates a Julid from a Crockford Base32 encoded string /// /// An DecodeError will be returned when the given string is not formated /// properly. /// /// # Example /// ```rust /// use julid::julid::Julid; /// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ"; /// let result = Julid::from_string(text); /// /// assert!(result.is_ok()); /// assert_eq!(&result.unwrap().to_string(), text); /// ``` pub const fn from_string(encoded: &str) -> Result { match base32::decode(encoded) { Ok(int_val) => Ok(Julid(int_val)), Err(err) => Err(err), } } /// The 'Alpha Julid'. /// /// The Alpha Julid is special form of Julid that is specified to have /// all 128 bits set to zero. pub const fn alpha() -> Julid { Julid(0) } /// The 'Omega Julid'. /// /// The Omega Julid is special form of Julid that is specified to have /// all 128 bits set to one. pub const fn omega() -> Self { Julid(u128::MAX) } /// Gets the timestamp section of this ulid pub const fn timestamp(&self) -> u64 { (self.0 >> UNIQUE_BITS) as u64 } pub const fn counter(&self) -> u16 { let mask = bitmask!(COUNTER_BITS); ((self.0 >> RANDOM_BITS) & mask) as u16 } pub const fn sortable(&self) -> u64 { let mask = bitmask!(TIME_BITS + COUNTER_BITS); ((self.0 >> RANDOM_BITS) & mask) as u64 } pub const fn random(&self) -> u128 { self.0 & bitmask!(RANDOM_BITS) } /// Gets the non-timestamp section of this Julid (random + counter bits). pub const fn unique(&self) -> u128 { self.0 & bitmask!(UNIQUE_BITS) } /// Creates a Crockford Base32 encoded string that represents this Julid /// /// # Example /// ```rust /// use julid::julid::Julid; /// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ"; /// let id = Julid::from_string(text).unwrap(); /// /// assert_eq!(&id.to_string(), text); /// ``` pub fn as_string(self) -> String { base32::encode(self.0) } /// Test if the Julid is Alpha pub const fn is_alpha(&self) -> bool { self.0 == 0u128 } /// Creates a Julid using the provided bytes array, assumed big-endian. pub const fn from_bytes(bytes: [u8; 16]) -> Julid { Self(u128::from_be_bytes(bytes)) } /// Returns the bytes of the Julid in big-endian order. pub const fn to_bytes(self) -> [u8; 16] { self.0.to_be_bytes() } } impl Default for Julid { fn default() -> Self { Julid::alpha() } } impl From for String { fn from(ulid: Julid) -> String { ulid.as_string() } } impl From<(u64, u64)> for Julid { fn from((msb, lsb): (u64, u64)) -> Self { Julid(u128::from(msb) << 64 | u128::from(lsb)) } } impl From for (u64, u64) { fn from(ulid: Julid) -> (u64, u64) { ((ulid.0 >> 64) as u64, (ulid.0 & bitmask!(64)) as u64) } } impl From for Julid { fn from(value: u128) -> Julid { Julid(value) } } impl From for u128 { fn from(ulid: Julid) -> u128 { ulid.0 } } impl From<[u8; 16]> for Julid { fn from(bytes: [u8; 16]) -> Self { Self(u128::from_be_bytes(bytes)) } } impl From for [u8; 16] { fn from(ulid: Julid) -> Self { ulid.0.to_be_bytes() } } impl FromStr for Julid { type Err = DecodeError; fn from_str(s: &str) -> Result { Julid::from_string(s) } } impl fmt::Display for Julid { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!(f, "{}", self.as_string()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_static() { let s = Julid(0x41414141414141414141414141414141).as_string(); let u = Julid::from_string(&s).unwrap(); assert_eq!(&s, "21850M2GA1850M2GA1850M2GA1"); assert_eq!(u.0, 0x41414141414141414141414141414141); } #[test] fn can_into_thing() { let ulid = Julid::from_str("01FKMG6GAG0PJANMWFN84TNXCD").unwrap(); let s: String = ulid.into(); let u: u128 = ulid.into(); let uu: (u64, u64) = ulid.into(); let bytes: [u8; 16] = ulid.into(); assert_eq!(Julid::from_str(&s).unwrap(), ulid); assert_eq!(Julid::from(u), ulid); assert_eq!(Julid::from(uu), ulid); assert_eq!(Julid::from(bytes), ulid); } #[test] fn default_is_nil() { assert_eq!(Julid::default(), Julid::alpha()); } #[test] fn can_display_things() { println!("{}", Julid::alpha()); println!("{}", DecodeError::InvalidLength(0)); println!("{}", DecodeError::InvalidChar('^')); } #[test] fn can_increment() { let mut max = 0; for i in 0..100 { let id = Julid::new(); max = id.counter().max(max); assert!(max <= i); } assert!(max > 49); } }