use core::{fmt, str::FromStr};
use std::{sync::Mutex, time::Duration};
use rand::{random, thread_rng, Rng};
use crate::base32::{self, DecodeError};
static LAST_ID: Mutex<Julid> = Mutex::new(Julid::alpha());
pub const TIME_BITS: u8 = 48;
pub const MBITS: u8 = 16;
pub const UNIQUE_BITS: u8 = 80;
pub const RANDOM_BITS: u8 = 64;
macro_rules! bitmask {
($len:expr) => {
((1 << $len) - 1)
};
}
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
pub struct Julid(pub u128);
impl Julid {
pub fn new() -> Self {
let lsb: u64 = random();
loop {
if let Ok(mut guard) = LAST_ID.try_lock() {
let ts = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or(Duration::ZERO)
.as_millis() as u64;
let ots = guard.timestamp();
if ots < ts {
let new = Julid::new_time(ts, lsb);
*guard = new;
break new;
} else {
let counter = guard.counter().saturating_add(1);
let tbits = ots & bitmask!(TIME_BITS);
let msb = (tbits << MBITS) + counter as u64;
let new: Julid = (msb, lsb).into();
*guard = new;
break new;
}
}
let micros = thread_rng().gen_range(10..50);
std::thread::sleep(Duration::from_micros(micros));
}
}
fn new_time(time: u64, lsb: u64) -> Self {
let tbits = time & bitmask!(TIME_BITS);
let msb = tbits << MBITS;
(msb, lsb).into()
}
pub const fn from_string(encoded: &str) -> Result<Julid, DecodeError> {
match base32::decode(encoded) {
Ok(int_val) => Ok(Julid(int_val)),
Err(err) => Err(err),
}
}
pub const fn alpha() -> Julid {
Julid(0)
}
pub const fn omega() -> Self {
Julid(u128::MAX)
}
pub const fn timestamp(&self) -> u64 {
(self.0 >> UNIQUE_BITS) as u64
}
pub const fn counter(&self) -> u16 {
let mask = bitmask!(MBITS);
((self.0 >> RANDOM_BITS) & mask) as u16
}
pub const fn sortable(&self) -> u64 {
let mask = bitmask!(TIME_BITS + MBITS);
((self.0 >> RANDOM_BITS) & mask) as u64
}
pub const fn random(&self) -> u128 {
self.0 & bitmask!(RANDOM_BITS)
}
pub const fn unique(&self) -> u128 {
self.0 & bitmask!(UNIQUE_BITS)
}
pub fn as_string(self) -> String {
base32::encode(self.0)
}
pub const fn is_alpha(&self) -> bool {
self.0 == 0u128
}
pub const fn from_bytes(bytes: [u8; 16]) -> Julid {
Self(u128::from_be_bytes(bytes))
}
pub const fn to_bytes(self) -> [u8; 16] {
self.0.to_be_bytes()
}
}
impl Default for Julid {
fn default() -> Self {
Julid::alpha()
}
}
impl From<Julid> 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<Julid> for (u64, u64) {
fn from(ulid: Julid) -> (u64, u64) {
((ulid.0 >> 64) as u64, (ulid.0 & bitmask!(64)) as u64)
}
}
impl From<u128> for Julid {
fn from(value: u128) -> Julid {
Julid(value)
}
}
impl From<Julid> 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<Julid> 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<Self, Self::Err> {
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);
}
}