use nexus_ascii::AsciiString;
const BASE62_ALPHABET: &[u8; 62] =
b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const BASE36_ALPHABET: &[u8; 36] = b"0123456789abcdefghijklmnopqrstuvwxyz";
const CROCKFORD32_ALPHABET: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
const BASE62_SQ: u64 = 62 * 62;
const BASE36_SQ: u64 = 36 * 36;
#[inline]
pub(crate) fn hex_u64(value: u64) -> AsciiString<16> {
let buf = crate::simd::hex_encode_u64(value);
unsafe { AsciiString::from_bytes_unchecked(&buf) }
}
#[inline]
pub(crate) fn hex_u128(hi: u64, lo: u64) -> AsciiString<32> {
let buf = crate::simd::hex_encode_u128(hi, lo);
unsafe { AsciiString::from_bytes_unchecked(&buf) }
}
#[inline]
pub(crate) fn base62_u64(mut value: u64) -> AsciiString<16> {
let mut buf = [b'0'; 11];
let r = (value % BASE62_SQ) as usize;
value /= BASE62_SQ;
buf[9] = BASE62_ALPHABET[r / 62];
buf[10] = BASE62_ALPHABET[r % 62];
let r = (value % BASE62_SQ) as usize;
value /= BASE62_SQ;
buf[7] = BASE62_ALPHABET[r / 62];
buf[8] = BASE62_ALPHABET[r % 62];
let r = (value % BASE62_SQ) as usize;
value /= BASE62_SQ;
buf[5] = BASE62_ALPHABET[r / 62];
buf[6] = BASE62_ALPHABET[r % 62];
let r = (value % BASE62_SQ) as usize;
value /= BASE62_SQ;
buf[3] = BASE62_ALPHABET[r / 62];
buf[4] = BASE62_ALPHABET[r % 62];
let r = (value % BASE62_SQ) as usize;
value /= BASE62_SQ;
buf[1] = BASE62_ALPHABET[r / 62];
buf[2] = BASE62_ALPHABET[r % 62];
buf[0] = BASE62_ALPHABET[value as usize];
unsafe { AsciiString::from_bytes_unchecked(&buf) }
}
#[inline]
pub(crate) fn base36_u64(mut value: u64) -> AsciiString<16> {
let mut buf = [b'0'; 13];
let r = (value % BASE36_SQ) as usize;
value /= BASE36_SQ;
buf[11] = BASE36_ALPHABET[r / 36];
buf[12] = BASE36_ALPHABET[r % 36];
let r = (value % BASE36_SQ) as usize;
value /= BASE36_SQ;
buf[9] = BASE36_ALPHABET[r / 36];
buf[10] = BASE36_ALPHABET[r % 36];
let r = (value % BASE36_SQ) as usize;
value /= BASE36_SQ;
buf[7] = BASE36_ALPHABET[r / 36];
buf[8] = BASE36_ALPHABET[r % 36];
let r = (value % BASE36_SQ) as usize;
value /= BASE36_SQ;
buf[5] = BASE36_ALPHABET[r / 36];
buf[6] = BASE36_ALPHABET[r % 36];
let r = (value % BASE36_SQ) as usize;
value /= BASE36_SQ;
buf[3] = BASE36_ALPHABET[r / 36];
buf[4] = BASE36_ALPHABET[r % 36];
let r = (value % BASE36_SQ) as usize;
value /= BASE36_SQ;
buf[1] = BASE36_ALPHABET[r / 36];
buf[2] = BASE36_ALPHABET[r % 36];
buf[0] = BASE36_ALPHABET[value as usize];
unsafe { AsciiString::from_bytes_unchecked(&buf) }
}
#[inline]
pub(crate) fn uuid_dashed(hi: u64, lo: u64) -> AsciiString<40> {
let hi_hex = crate::simd::hex_encode_u64(hi);
let lo_hex = crate::simd::hex_encode_u64(lo);
let mut buf = [0u8; 36];
buf[0..8].copy_from_slice(&hi_hex[0..8]);
buf[8] = b'-';
buf[9..13].copy_from_slice(&hi_hex[8..12]);
buf[13] = b'-';
buf[14..18].copy_from_slice(&hi_hex[12..16]);
buf[18] = b'-';
buf[19..23].copy_from_slice(&lo_hex[0..4]);
buf[23] = b'-';
buf[24..36].copy_from_slice(&lo_hex[4..16]);
unsafe { AsciiString::from_bytes_unchecked(&buf) }
}
#[inline]
pub(crate) fn ulid_encode(timestamp_ms: u64, rand_hi: u16, rand_lo: u64) -> AsciiString<32> {
let mut buf = [0u8; 26];
buf[0] = CROCKFORD32_ALPHABET[((timestamp_ms >> 45) & 0x07) as usize]; buf[1] = CROCKFORD32_ALPHABET[((timestamp_ms >> 40) & 0x1F) as usize];
buf[2] = CROCKFORD32_ALPHABET[((timestamp_ms >> 35) & 0x1F) as usize];
buf[3] = CROCKFORD32_ALPHABET[((timestamp_ms >> 30) & 0x1F) as usize];
buf[4] = CROCKFORD32_ALPHABET[((timestamp_ms >> 25) & 0x1F) as usize];
buf[5] = CROCKFORD32_ALPHABET[((timestamp_ms >> 20) & 0x1F) as usize];
buf[6] = CROCKFORD32_ALPHABET[((timestamp_ms >> 15) & 0x1F) as usize];
buf[7] = CROCKFORD32_ALPHABET[((timestamp_ms >> 10) & 0x1F) as usize];
buf[8] = CROCKFORD32_ALPHABET[((timestamp_ms >> 5) & 0x1F) as usize];
buf[9] = CROCKFORD32_ALPHABET[(timestamp_ms & 0x1F) as usize];
let rand_hi = rand_hi as u64;
buf[10] = CROCKFORD32_ALPHABET[((rand_hi >> 11) & 0x1F) as usize]; buf[11] = CROCKFORD32_ALPHABET[((rand_hi >> 6) & 0x1F) as usize]; buf[12] = CROCKFORD32_ALPHABET[((rand_hi >> 1) & 0x1F) as usize];
let combined = ((rand_hi & 0x01) << 4) | ((rand_lo >> 60) & 0x0F);
buf[13] = CROCKFORD32_ALPHABET[combined as usize];
buf[14] = CROCKFORD32_ALPHABET[((rand_lo >> 55) & 0x1F) as usize];
buf[15] = CROCKFORD32_ALPHABET[((rand_lo >> 50) & 0x1F) as usize];
buf[16] = CROCKFORD32_ALPHABET[((rand_lo >> 45) & 0x1F) as usize];
buf[17] = CROCKFORD32_ALPHABET[((rand_lo >> 40) & 0x1F) as usize];
buf[18] = CROCKFORD32_ALPHABET[((rand_lo >> 35) & 0x1F) as usize];
buf[19] = CROCKFORD32_ALPHABET[((rand_lo >> 30) & 0x1F) as usize];
buf[20] = CROCKFORD32_ALPHABET[((rand_lo >> 25) & 0x1F) as usize];
buf[21] = CROCKFORD32_ALPHABET[((rand_lo >> 20) & 0x1F) as usize];
buf[22] = CROCKFORD32_ALPHABET[((rand_lo >> 15) & 0x1F) as usize];
buf[23] = CROCKFORD32_ALPHABET[((rand_lo >> 10) & 0x1F) as usize];
buf[24] = CROCKFORD32_ALPHABET[((rand_lo >> 5) & 0x1F) as usize];
buf[25] = CROCKFORD32_ALPHABET[(rand_lo & 0x1F) as usize];
unsafe { AsciiString::from_bytes_unchecked(&buf) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hex_u64_zero() {
assert_eq!(hex_u64(0).as_str(), "0000000000000000");
}
#[test]
fn hex_u64_max() {
assert_eq!(hex_u64(u64::MAX).as_str(), "ffffffffffffffff");
}
#[test]
fn hex_u64_known_value() {
assert_eq!(hex_u64(0xDEAD_BEEF_CAFE_BABE).as_str(), "deadbeefcafebabe");
}
#[test]
fn hex_u128_known_value() {
let hi = 0x0123_4567_89AB_CDEF;
let lo = 0xFEDC_BA98_7654_3210;
assert_eq!(
hex_u128(hi, lo).as_str(),
"0123456789abcdeffedcba9876543210"
);
}
#[test]
fn base62_zero() {
assert_eq!(base62_u64(0).as_str(), "00000000000");
}
#[test]
fn base62_max() {
let encoded = base62_u64(u64::MAX);
assert_eq!(encoded.len(), 11);
for c in encoded.as_str().chars() {
assert!(c.is_ascii_alphanumeric());
}
}
#[test]
fn base62_known_values() {
assert_eq!(base62_u64(0).as_str(), "00000000000");
assert_eq!(base62_u64(1).as_str(), "00000000001");
assert_eq!(base62_u64(9).as_str(), "00000000009");
assert_eq!(base62_u64(10).as_str(), "0000000000A");
assert_eq!(base62_u64(35).as_str(), "0000000000Z");
assert_eq!(base62_u64(36).as_str(), "0000000000a");
assert_eq!(base62_u64(61).as_str(), "0000000000z");
assert_eq!(base62_u64(62).as_str(), "00000000010");
}
#[test]
fn base36_zero() {
assert_eq!(base36_u64(0).as_str(), "0000000000000");
}
#[test]
fn base36_max() {
let encoded = base36_u64(u64::MAX);
assert_eq!(encoded.len(), 13);
for c in encoded.as_str().chars() {
assert!(c.is_ascii_digit() || (c.is_ascii_lowercase()));
}
}
#[test]
fn base36_known_values() {
assert_eq!(base36_u64(0).as_str(), "0000000000000");
assert_eq!(base36_u64(1).as_str(), "0000000000001");
assert_eq!(base36_u64(9).as_str(), "0000000000009");
assert_eq!(base36_u64(10).as_str(), "000000000000a");
assert_eq!(base36_u64(35).as_str(), "000000000000z");
assert_eq!(base36_u64(36).as_str(), "0000000000010");
}
#[test]
fn uuid_dashed_format() {
let hi = 0x0123_4567_89AB_CDEF;
let lo = 0xFEDC_BA98_7654_3210;
let uuid = uuid_dashed(hi, lo);
assert_eq!(uuid.as_str(), "01234567-89ab-cdef-fedc-ba9876543210");
assert_eq!(uuid.len(), 36);
}
#[test]
fn uuid_dashed_zeros() {
let uuid = uuid_dashed(0, 0);
assert_eq!(uuid.as_str(), "00000000-0000-0000-0000-000000000000");
}
}