1use core::cmp::Ordering;
23use core::fmt;
24use core::hash::{Hash, Hasher};
25use core::ops::Deref;
26use core::str::FromStr;
27
28use nexus_ascii::AsciiString;
29
30use crate::parse::{self, DecodeError, ParseError, UuidParseError};
31
32#[derive(Clone, Copy, PartialEq, Eq)]
63#[repr(transparent)]
64pub struct Uuid<const CAP: usize = 40>(pub(crate) AsciiString<CAP>);
65
66impl<const CAP: usize> Uuid<CAP> {
67 #[inline]
74 pub fn from_raw(hi: u64, lo: u64) -> Self {
75 Self(crate::encode::uuid_dashed(hi, lo))
76 }
77
78 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
86 if bytes.len() != 16 {
87 return Err(ParseError::InvalidLength {
88 expected: 16,
89 got: bytes.len(),
90 });
91 }
92 let hi = u64::from_be_bytes(bytes[0..8].try_into().expect("8-byte slice"));
93 let lo = u64::from_be_bytes(bytes[8..16].try_into().expect("8-byte slice"));
94 Ok(Self::from_raw(hi, lo))
95 }
96
97 #[inline]
104 pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
105 debug_assert!(bytes.len() >= 16);
106 unsafe {
108 let hi = u64::from_be_bytes(bytes.get_unchecked(0..8).try_into().unwrap_unchecked());
109 let lo = u64::from_be_bytes(bytes.get_unchecked(8..16).try_into().unwrap_unchecked());
110 Self::from_raw(hi, lo)
111 }
112 }
113
114 #[inline]
116 pub fn as_str(&self) -> &str {
117 self.0.as_str()
118 }
119
120 #[inline]
122 pub fn as_bytes(&self) -> &[u8] {
123 self.0.as_bytes()
124 }
125
126 #[inline]
131 pub fn decode(&self) -> (u64, u64) {
132 let bytes: &[u8; 36] = self.0.as_bytes().try_into().unwrap();
135 unsafe { crate::simd::uuid_parse_dashed(bytes).unwrap_unchecked() }
136 }
137
138 #[inline]
140 pub fn version(&self) -> u8 {
141 hex_digit(self.0.as_bytes()[14])
143 }
144
145 pub fn parse(s: &str) -> Result<Self, UuidParseError> {
155 let bytes = s.as_bytes();
156 if bytes.len() != 36 {
157 return Err(UuidParseError::InvalidLength {
158 expected: 36,
159 got: bytes.len(),
160 });
161 }
162
163 if bytes[8] != b'-' || bytes[13] != b'-' || bytes[18] != b'-' || bytes[23] != b'-' {
165 return Err(UuidParseError::InvalidFormat);
166 }
167
168 let input: &[u8; 36] = unsafe { &*(bytes.as_ptr().cast::<[u8; 36]>()) };
170
171 let (hi, lo) = crate::simd::uuid_parse_dashed(input).map_err(|pos| {
173 let input_pos = match pos {
175 0..=7 => pos, 8..=11 => pos + 1, 12..=15 => pos + 2, 16..=19 => pos + 3, _ => pos + 4, };
181 UuidParseError::InvalidChar {
182 position: input_pos,
183 byte: bytes[input_pos],
184 }
185 })?;
186
187 Ok(Self::from_raw(hi, lo))
188 }
189
190 #[inline]
194 pub fn to_compact(&self) -> UuidCompact {
195 let (hi, lo) = self.decode();
196 UuidCompact::from_raw(hi, lo)
197 }
198
199 #[inline]
203 pub fn is_nil(&self) -> bool {
204 self.0.as_bytes() == b"00000000-0000-0000-0000-000000000000"
205 }
206
207 #[inline]
211 pub fn timestamp_ms(&self) -> Option<u64> {
212 if self.version() != 7 {
213 return None;
214 }
215 let (hi, _) = self.decode();
216 Some(hi >> 16)
217 }
218
219 pub fn to_bytes(&self) -> [u8; 16] {
221 let (hi, lo) = self.decode();
222 let mut out = [0u8; 16];
223 out[..8].copy_from_slice(&hi.to_be_bytes());
224 out[8..].copy_from_slice(&lo.to_be_bytes());
225 out
226 }
227}
228
229impl<const CAP: usize> Deref for Uuid<CAP> {
230 type Target = str;
231
232 #[inline]
233 fn deref(&self) -> &str {
234 self.0.as_str()
235 }
236}
237
238impl<const CAP: usize> AsRef<str> for Uuid<CAP> {
239 #[inline]
240 fn as_ref(&self) -> &str {
241 self.0.as_str()
242 }
243}
244
245impl<const CAP: usize> fmt::Display for Uuid<CAP> {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 f.write_str(self.0.as_str())
248 }
249}
250
251impl<const CAP: usize> fmt::Debug for Uuid<CAP> {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 write!(f, "Uuid({})", self.0.as_str())
254 }
255}
256
257impl<const CAP: usize> Hash for Uuid<CAP> {
258 #[inline]
259 fn hash<H: Hasher>(&self, state: &mut H) {
260 self.0.hash(state);
261 }
262}
263
264impl<const CAP: usize> Ord for Uuid<CAP> {
265 #[inline]
266 fn cmp(&self, other: &Self) -> Ordering {
267 self.0.cmp(&other.0)
269 }
270}
271
272impl<const CAP: usize> PartialOrd for Uuid<CAP> {
273 #[inline]
274 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
275 Some(self.cmp(other))
276 }
277}
278
279impl<const CAP: usize> FromStr for Uuid<CAP> {
280 type Err = UuidParseError;
281
282 #[inline]
283 fn from_str(s: &str) -> Result<Self, Self::Err> {
284 Self::parse(s)
285 }
286}
287
288#[derive(Clone, Copy, PartialEq, Eq)]
301#[repr(transparent)]
302pub struct UuidCompact<const CAP: usize = 32>(pub(crate) AsciiString<CAP>);
303
304impl<const CAP: usize> UuidCompact<CAP> {
305 #[inline]
310 pub fn from_raw(hi: u64, lo: u64) -> Self {
311 Self(crate::encode::hex_u128(hi, lo))
312 }
313
314 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
322 if bytes.len() != 16 {
323 return Err(ParseError::InvalidLength {
324 expected: 16,
325 got: bytes.len(),
326 });
327 }
328 let hi = u64::from_be_bytes(bytes[0..8].try_into().expect("8-byte slice"));
329 let lo = u64::from_be_bytes(bytes[8..16].try_into().expect("8-byte slice"));
330 Ok(Self::from_raw(hi, lo))
331 }
332
333 #[inline]
339 pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
340 debug_assert!(bytes.len() >= 16);
341 unsafe {
343 let hi = u64::from_be_bytes(bytes.get_unchecked(0..8).try_into().unwrap_unchecked());
344 let lo = u64::from_be_bytes(bytes.get_unchecked(8..16).try_into().unwrap_unchecked());
345 Self::from_raw(hi, lo)
346 }
347 }
348
349 #[inline]
350 pub fn as_str(&self) -> &str {
351 self.0.as_str()
352 }
353
354 #[inline]
355 pub fn as_bytes(&self) -> &[u8] {
356 self.0.as_bytes()
357 }
358
359 pub fn decode(&self) -> (u64, u64) {
361 let bytes: &[u8; 32] = self.0.as_bytes().try_into().expect("32-byte hex string");
362 unsafe { crate::simd::hex_decode_32(bytes).unwrap_unchecked() }
364 }
365
366 pub fn parse(s: &str) -> Result<Self, ParseError> {
370 let bytes = s.as_bytes();
371 if bytes.len() != 32 {
372 return Err(ParseError::InvalidLength {
373 expected: 32,
374 got: bytes.len(),
375 });
376 }
377
378 let hex_bytes: &[u8; 32] = bytes.try_into().expect("32-byte hex string");
380 let (hi, lo) =
381 crate::simd::hex_decode_32(hex_bytes).map_err(|pos| ParseError::InvalidChar {
382 position: pos,
383 byte: bytes[pos],
384 })?;
385
386 Ok(Self::from_raw(hi, lo))
387 }
388
389 #[inline]
393 pub fn to_dashed(&self) -> Uuid {
394 let (hi, lo) = self.decode();
395 Uuid::from_raw(hi, lo)
396 }
397
398 #[inline]
402 pub fn is_nil(&self) -> bool {
403 self.0.as_bytes() == b"00000000000000000000000000000000"
404 }
405
406 pub fn to_bytes(&self) -> [u8; 16] {
408 let (hi, lo) = self.decode();
409 let mut out = [0u8; 16];
410 out[..8].copy_from_slice(&hi.to_be_bytes());
411 out[8..].copy_from_slice(&lo.to_be_bytes());
412 out
413 }
414}
415
416impl<const CAP: usize> Deref for UuidCompact<CAP> {
417 type Target = str;
418
419 #[inline]
420 fn deref(&self) -> &str {
421 self.0.as_str()
422 }
423}
424
425impl<const CAP: usize> AsRef<str> for UuidCompact<CAP> {
426 #[inline]
427 fn as_ref(&self) -> &str {
428 self.0.as_str()
429 }
430}
431
432impl<const CAP: usize> fmt::Display for UuidCompact<CAP> {
433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434 f.write_str(self.0.as_str())
435 }
436}
437
438impl<const CAP: usize> fmt::Debug for UuidCompact<CAP> {
439 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440 write!(f, "UuidCompact({})", self.0.as_str())
441 }
442}
443
444impl<const CAP: usize> Hash for UuidCompact<CAP> {
445 #[inline]
446 fn hash<H: Hasher>(&self, state: &mut H) {
447 self.0.hash(state);
448 }
449}
450
451impl<const CAP: usize> Ord for UuidCompact<CAP> {
452 #[inline]
453 fn cmp(&self, other: &Self) -> Ordering {
454 self.0.cmp(&other.0)
455 }
456}
457
458impl<const CAP: usize> PartialOrd for UuidCompact<CAP> {
459 #[inline]
460 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
461 Some(self.cmp(other))
462 }
463}
464
465impl<const CAP: usize> FromStr for UuidCompact<CAP> {
466 type Err = ParseError;
467
468 #[inline]
469 fn from_str(s: &str) -> Result<Self, Self::Err> {
470 Self::parse(s)
471 }
472}
473
474#[derive(Clone, Copy, PartialEq, Eq)]
487#[repr(transparent)]
488pub struct HexId64<const CAP: usize = 16>(pub(crate) AsciiString<CAP>);
489
490impl<const CAP: usize> HexId64<CAP> {
491 #[inline]
493 pub fn encode(value: u64) -> Self {
494 Self(crate::encode::hex_u64(value))
495 }
496
497 #[inline]
498 pub fn as_str(&self) -> &str {
499 self.0.as_str()
500 }
501
502 #[inline]
503 pub fn as_bytes(&self) -> &[u8] {
504 self.0.as_bytes()
505 }
506
507 pub fn decode(&self) -> u64 {
509 let bytes: &[u8; 16] = self.0.as_bytes().try_into().expect("16-byte hex string");
510 unsafe { crate::simd::hex_decode_16(bytes).unwrap_unchecked() }
512 }
513
514 pub fn parse(s: &str) -> Result<Self, ParseError> {
516 let bytes = s.as_bytes();
517 if bytes.len() != 16 {
518 return Err(ParseError::InvalidLength {
519 expected: 16,
520 got: bytes.len(),
521 });
522 }
523
524 let hex_bytes: &[u8; 16] = bytes.try_into().expect("16-byte hex string");
526 let value =
527 crate::simd::hex_decode_16(hex_bytes).map_err(|pos| ParseError::InvalidChar {
528 position: pos,
529 byte: bytes[pos],
530 })?;
531
532 Ok(Self::encode(value))
533 }
534}
535
536impl<const CAP: usize> Deref for HexId64<CAP> {
537 type Target = str;
538
539 #[inline]
540 fn deref(&self) -> &str {
541 self.0.as_str()
542 }
543}
544
545impl<const CAP: usize> AsRef<str> for HexId64<CAP> {
546 #[inline]
547 fn as_ref(&self) -> &str {
548 self.0.as_str()
549 }
550}
551
552impl<const CAP: usize> fmt::Display for HexId64<CAP> {
553 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
554 f.write_str(self.0.as_str())
555 }
556}
557
558impl<const CAP: usize> fmt::Debug for HexId64<CAP> {
559 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
560 write!(f, "HexId64({})", self.0.as_str())
561 }
562}
563
564impl<const CAP: usize> Hash for HexId64<CAP> {
565 #[inline]
566 fn hash<H: Hasher>(&self, state: &mut H) {
567 self.0.hash(state);
568 }
569}
570
571impl<const CAP: usize> FromStr for HexId64<CAP> {
572 type Err = ParseError;
573
574 #[inline]
575 fn from_str(s: &str) -> Result<Self, Self::Err> {
576 Self::parse(s)
577 }
578}
579
580#[derive(Clone, Copy, PartialEq, Eq)]
593#[repr(transparent)]
594pub struct Base62Id<const CAP: usize = 16>(pub(crate) AsciiString<CAP>);
595
596impl<const CAP: usize> Base62Id<CAP> {
597 #[inline]
599 pub fn encode(value: u64) -> Self {
600 Self(crate::encode::base62_u64(value))
601 }
602
603 #[inline]
604 pub fn as_str(&self) -> &str {
605 self.0.as_str()
606 }
607
608 #[inline]
609 pub fn as_bytes(&self) -> &[u8] {
610 self.0.as_bytes()
611 }
612
613 pub fn decode(&self) -> u64 {
615 let bytes = self.0.as_bytes();
616 let mut value: u64 = 0;
617 for &b in bytes {
618 value = value * 62 + base62_digit(b) as u64;
619 }
620 value
621 }
622
623 pub fn parse(s: &str) -> Result<Self, DecodeError> {
625 let bytes = s.as_bytes();
626 if bytes.len() != 11 {
627 return Err(DecodeError::InvalidLength {
628 expected: 11,
629 got: bytes.len(),
630 });
631 }
632
633 let mut value: u64 = 0;
634 let mut i = 0;
635 while i < 11 {
636 let d = parse::validate_base62(bytes[i], i)?;
637 value = value
638 .checked_mul(62)
639 .and_then(|v| v.checked_add(d as u64))
640 .ok_or(DecodeError::Overflow)?;
641 i += 1;
642 }
643
644 Ok(Self::encode(value))
645 }
646}
647
648impl<const CAP: usize> Deref for Base62Id<CAP> {
649 type Target = str;
650
651 #[inline]
652 fn deref(&self) -> &str {
653 self.0.as_str()
654 }
655}
656
657impl<const CAP: usize> AsRef<str> for Base62Id<CAP> {
658 #[inline]
659 fn as_ref(&self) -> &str {
660 self.0.as_str()
661 }
662}
663
664impl<const CAP: usize> fmt::Display for Base62Id<CAP> {
665 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
666 f.write_str(self.0.as_str())
667 }
668}
669
670impl<const CAP: usize> fmt::Debug for Base62Id<CAP> {
671 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
672 write!(f, "Base62Id({})", self.0.as_str())
673 }
674}
675
676impl<const CAP: usize> Hash for Base62Id<CAP> {
677 #[inline]
678 fn hash<H: Hasher>(&self, state: &mut H) {
679 self.0.hash(state);
680 }
681}
682
683impl<const CAP: usize> FromStr for Base62Id<CAP> {
684 type Err = DecodeError;
685
686 #[inline]
687 fn from_str(s: &str) -> Result<Self, Self::Err> {
688 Self::parse(s)
689 }
690}
691
692#[derive(Clone, Copy, PartialEq, Eq)]
705#[repr(transparent)]
706pub struct Base36Id<const CAP: usize = 16>(pub(crate) AsciiString<CAP>);
707
708impl<const CAP: usize> Base36Id<CAP> {
709 #[inline]
711 pub fn encode(value: u64) -> Self {
712 Self(crate::encode::base36_u64(value))
713 }
714
715 #[inline]
716 pub fn as_str(&self) -> &str {
717 self.0.as_str()
718 }
719
720 #[inline]
721 pub fn as_bytes(&self) -> &[u8] {
722 self.0.as_bytes()
723 }
724
725 pub fn decode(&self) -> u64 {
727 let bytes = self.0.as_bytes();
728 let mut value: u64 = 0;
729 for &b in bytes {
730 value = value * 36 + base36_digit(b) as u64;
731 }
732 value
733 }
734
735 pub fn parse(s: &str) -> Result<Self, DecodeError> {
737 let bytes = s.as_bytes();
738 if bytes.len() != 13 {
739 return Err(DecodeError::InvalidLength {
740 expected: 13,
741 got: bytes.len(),
742 });
743 }
744
745 let mut value: u64 = 0;
746 let mut i = 0;
747 while i < 13 {
748 let d = parse::validate_base36(bytes[i], i)?;
749 value = value
750 .checked_mul(36)
751 .and_then(|v| v.checked_add(d as u64))
752 .ok_or(DecodeError::Overflow)?;
753 i += 1;
754 }
755
756 Ok(Self::encode(value))
757 }
758}
759
760impl<const CAP: usize> Deref for Base36Id<CAP> {
761 type Target = str;
762
763 #[inline]
764 fn deref(&self) -> &str {
765 self.0.as_str()
766 }
767}
768
769impl<const CAP: usize> AsRef<str> for Base36Id<CAP> {
770 #[inline]
771 fn as_ref(&self) -> &str {
772 self.0.as_str()
773 }
774}
775
776impl<const CAP: usize> fmt::Display for Base36Id<CAP> {
777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
778 f.write_str(self.0.as_str())
779 }
780}
781
782impl<const CAP: usize> fmt::Debug for Base36Id<CAP> {
783 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
784 write!(f, "Base36Id({})", self.0.as_str())
785 }
786}
787
788impl<const CAP: usize> Hash for Base36Id<CAP> {
789 #[inline]
790 fn hash<H: Hasher>(&self, state: &mut H) {
791 self.0.hash(state);
792 }
793}
794
795impl<const CAP: usize> FromStr for Base36Id<CAP> {
796 type Err = DecodeError;
797
798 #[inline]
799 fn from_str(s: &str) -> Result<Self, Self::Err> {
800 Self::parse(s)
801 }
802}
803
804#[derive(Clone, Copy, PartialEq, Eq)]
838#[repr(transparent)]
839pub struct Ulid<const CAP: usize = 32>(pub(crate) AsciiString<CAP>);
840
841impl<const CAP: usize> Ulid<CAP> {
842 #[inline]
848 pub fn from_raw(timestamp_ms: u64, rand_hi: u16, rand_lo: u64) -> Self {
849 Self(crate::encode::ulid_encode(timestamp_ms, rand_hi, rand_lo))
850 }
851
852 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
862 if bytes.len() != 16 {
863 return Err(ParseError::InvalidLength {
864 expected: 16,
865 got: bytes.len(),
866 });
867 }
868 let mut ts_buf = [0u8; 8];
870 ts_buf[2..8].copy_from_slice(&bytes[0..6]);
871 let timestamp_ms = u64::from_be_bytes(ts_buf);
872
873 let rand_hi = u16::from_be_bytes(bytes[6..8].try_into().expect("2-byte slice"));
874 let rand_lo = u64::from_be_bytes(bytes[8..16].try_into().expect("8-byte slice"));
875
876 Ok(Self::from_raw(timestamp_ms, rand_hi, rand_lo))
877 }
878
879 #[inline]
885 pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
886 debug_assert!(bytes.len() >= 16);
887 unsafe {
889 let mut ts_buf = [0u8; 8];
890 core::ptr::copy_nonoverlapping(bytes.as_ptr(), ts_buf.as_mut_ptr().add(2), 6);
891 let timestamp_ms = u64::from_be_bytes(ts_buf);
892
893 let rand_hi =
894 u16::from_be_bytes(bytes.get_unchecked(6..8).try_into().unwrap_unchecked());
895 let rand_lo =
896 u64::from_be_bytes(bytes.get_unchecked(8..16).try_into().unwrap_unchecked());
897
898 Self::from_raw(timestamp_ms, rand_hi, rand_lo)
899 }
900 }
901
902 #[inline]
903 pub fn as_str(&self) -> &str {
904 self.0.as_str()
905 }
906
907 #[inline]
908 pub fn as_bytes(&self) -> &[u8] {
909 self.0.as_bytes()
910 }
911
912 pub fn timestamp_ms(&self) -> u64 {
914 let bytes = self.0.as_bytes();
915 let mut ts: u64 = 0;
916
917 ts = (ts << 3) | crockford32_digit(bytes[0]) as u64;
920 for &b in &bytes[1..10] {
921 ts = (ts << 5) | crockford32_digit(b) as u64;
922 }
923
924 ts
925 }
926
927 pub fn parse(s: &str) -> Result<Self, ParseError> {
931 let bytes = s.as_bytes();
932 if bytes.len() != 26 {
933 return Err(ParseError::InvalidLength {
934 expected: 26,
935 got: bytes.len(),
936 });
937 }
938
939 let first = parse::validate_crockford32(bytes[0], 0)?;
943 if first > 7 {
944 return Err(ParseError::InvalidChar {
945 position: 0,
946 byte: bytes[0],
947 });
948 }
949 let mut ts: u64 = first as u64;
950 let mut i = 1;
951 while i < 10 {
952 let d = parse::validate_crockford32(bytes[i], i)? as u64;
953 ts = (ts << 5) | d;
954 i += 1;
955 }
956
957 let c10 = parse::validate_crockford32(bytes[10], 10)? as u16;
959 let c11 = parse::validate_crockford32(bytes[11], 11)? as u16;
960 let c12 = parse::validate_crockford32(bytes[12], 12)? as u16;
961 let c13 = parse::validate_crockford32(bytes[13], 13)? as u64;
962
963 let rand_hi = (c10 << 11) | (c11 << 6) | (c12 << 1) | ((c13 >> 4) as u16);
964
965 let mut rand_lo: u64 = c13 & 0x0F;
966 i = 14;
967 while i < 26 {
968 let d = parse::validate_crockford32(bytes[i], i)? as u64;
969 rand_lo = (rand_lo << 5) | d;
970 i += 1;
971 }
972
973 Ok(Self::from_raw(ts, rand_hi, rand_lo))
974 }
975
976 #[inline]
978 pub fn is_nil(&self) -> bool {
979 self.timestamp_ms() == 0 && {
980 let (hi, lo) = self.random();
981 hi == 0 && lo == 0
982 }
983 }
984
985 pub fn to_uuid(&self) -> Uuid {
998 let ts = self.timestamp_ms();
999 let (rand_hi, rand_lo) = self.random();
1000
1001 let rand_a = (rand_hi >> 4) as u64; let hi = (ts << 16) | (0x7 << 12) | (rand_a & 0xFFF);
1005
1006 let remaining = ((rand_hi as u64 & 0x0F) << 58) | (rand_lo >> 6);
1009 let lo = (0b10u64 << 62) | (remaining & 0x3FFF_FFFF_FFFF_FFFF);
1010
1011 Uuid::from_raw(hi, lo)
1012 }
1013
1014 pub fn to_bytes(&self) -> [u8; 16] {
1016 let ts = self.timestamp_ms();
1017 let (rand_hi, rand_lo) = self.random();
1018
1019 let mut out = [0u8; 16];
1020 let ts_bytes = ts.to_be_bytes();
1022 out[0..6].copy_from_slice(&ts_bytes[2..8]);
1023 out[6..8].copy_from_slice(&rand_hi.to_be_bytes());
1025 out[8..16].copy_from_slice(&rand_lo.to_be_bytes());
1027 out
1028 }
1029
1030 pub fn random(&self) -> (u16, u64) {
1032 let bytes = self.0.as_bytes();
1033
1034 let c10 = crockford32_digit(bytes[10]) as u16;
1041 let c11 = crockford32_digit(bytes[11]) as u16;
1042 let c12 = crockford32_digit(bytes[12]) as u16;
1043 let c13 = crockford32_digit(bytes[13]) as u64;
1044
1045 let rand_hi = (c10 << 11) | (c11 << 6) | (c12 << 1) | ((c13 >> 4) as u16);
1046
1047 let mut rand_lo: u64 = c13 & 0x0F;
1050 for &b in &bytes[14..26] {
1051 rand_lo = (rand_lo << 5) | crockford32_digit(b) as u64;
1052 }
1053
1054 (rand_hi, rand_lo)
1055 }
1056}
1057
1058impl<const CAP: usize> Deref for Ulid<CAP> {
1059 type Target = str;
1060
1061 #[inline]
1062 fn deref(&self) -> &str {
1063 self.0.as_str()
1064 }
1065}
1066
1067impl<const CAP: usize> AsRef<str> for Ulid<CAP> {
1068 #[inline]
1069 fn as_ref(&self) -> &str {
1070 self.0.as_str()
1071 }
1072}
1073
1074impl<const CAP: usize> fmt::Display for Ulid<CAP> {
1075 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1076 f.write_str(self.0.as_str())
1077 }
1078}
1079
1080impl<const CAP: usize> fmt::Debug for Ulid<CAP> {
1081 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1082 write!(f, "Ulid({})", self.0.as_str())
1083 }
1084}
1085
1086impl<const CAP: usize> Hash for Ulid<CAP> {
1087 #[inline]
1088 fn hash<H: Hasher>(&self, state: &mut H) {
1089 self.0.hash(state);
1090 }
1091}
1092
1093impl<const CAP: usize> Ord for Ulid<CAP> {
1094 #[inline]
1095 fn cmp(&self, other: &Self) -> Ordering {
1096 self.0.cmp(&other.0)
1098 }
1099}
1100
1101impl<const CAP: usize> PartialOrd for Ulid<CAP> {
1102 #[inline]
1103 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1104 Some(self.cmp(other))
1105 }
1106}
1107
1108impl<const CAP: usize> FromStr for Ulid<CAP> {
1109 type Err = ParseError;
1110
1111 #[inline]
1112 fn from_str(s: &str) -> Result<Self, Self::Err> {
1113 Self::parse(s)
1114 }
1115}
1116
1117impl<const CAP: usize> From<Uuid<CAP>> for UuidCompact {
1122 #[inline]
1124 fn from(u: Uuid<CAP>) -> Self {
1125 u.to_compact()
1126 }
1127}
1128
1129impl<const CAP: usize> From<UuidCompact<CAP>> for Uuid {
1130 #[inline]
1132 fn from(u: UuidCompact<CAP>) -> Self {
1133 u.to_dashed()
1134 }
1135}
1136
1137impl<const CAP: usize> From<Ulid<CAP>> for Uuid {
1138 #[inline]
1142 fn from(u: Ulid<CAP>) -> Self {
1143 u.to_uuid()
1144 }
1145}
1146
1147#[inline]
1154fn crockford32_digit(b: u8) -> u8 {
1155 parse::CROCKFORD32_DECODE[b as usize]
1156}
1157
1158#[inline]
1160const fn hex_digit(b: u8) -> u8 {
1161 match b {
1162 b'0'..=b'9' => b - b'0',
1163 b'a'..=b'f' => b - b'a' + 10,
1164 b'A'..=b'F' => b - b'A' + 10,
1165 _ => 0, }
1167}
1168
1169#[inline]
1171const fn base62_digit(b: u8) -> u8 {
1172 match b {
1173 b'0'..=b'9' => b - b'0',
1174 b'A'..=b'Z' => b - b'A' + 10,
1175 b'a'..=b'z' => b - b'a' + 36,
1176 _ => 0,
1177 }
1178}
1179
1180#[inline]
1182const fn base36_digit(b: u8) -> u8 {
1183 match b {
1184 b'0'..=b'9' => b - b'0',
1185 b'a'..=b'z' => b - b'a' + 10,
1186 b'A'..=b'Z' => b - b'A' + 10, _ => 0,
1188 }
1189}
1190
1191#[cfg(all(test, feature = "std"))]
1192mod tests {
1193 use super::*;
1194
1195 #[test]
1196 fn uuid_decode_roundtrip() {
1197 let hi = 0x0123_4567_89AB_CDEF_u64;
1198 let lo = 0xFEDC_BA98_7654_3210_u64;
1199
1200 let uuid: Uuid = Uuid::from_raw(hi, lo);
1201 let (decoded_hi, decoded_lo) = uuid.decode();
1202
1203 assert_eq!(hi, decoded_hi);
1204 assert_eq!(lo, decoded_lo);
1205 }
1206
1207 #[test]
1208 fn uuid_larger_cap() {
1209 let hi = 0x0123_4567_89AB_CDEF_u64;
1210 let lo = 0xFEDC_BA98_7654_3210_u64;
1211
1212 let small: Uuid<40> = Uuid::from_raw(hi, lo);
1213 let large: Uuid<64> = Uuid::from_raw(hi, lo);
1214
1215 assert_eq!(small.as_str(), large.as_str());
1216 assert_eq!(small.decode(), large.decode());
1217 }
1218
1219 #[test]
1220 fn uuid_compact_decode_roundtrip() {
1221 let hi = 0x0123_4567_89AB_CDEF_u64;
1222 let lo = 0xFEDC_BA98_7654_3210_u64;
1223
1224 let uuid: UuidCompact = UuidCompact::from_raw(hi, lo);
1225 let (decoded_hi, decoded_lo) = uuid.decode();
1226
1227 assert_eq!(hi, decoded_hi);
1228 assert_eq!(lo, decoded_lo);
1229 }
1230
1231 #[test]
1232 fn hex_id64_decode_roundtrip() {
1233 for value in [0, 1, 12345, u64::MAX, 0xDEAD_BEEF_CAFE_BABE] {
1234 let id: HexId64 = HexId64::encode(value);
1235 assert_eq!(id.decode(), value);
1236 }
1237 }
1238
1239 #[test]
1240 fn hex_id64_larger_cap() {
1241 let id_small: HexId64<16> = HexId64::encode(12345);
1242 let id_large: HexId64<32> = HexId64::encode(12345);
1243 assert_eq!(id_small.as_str(), id_large.as_str());
1244 }
1245
1246 #[test]
1247 fn base62_id_decode_roundtrip() {
1248 for value in [0, 1, 12345, u64::MAX] {
1249 let id: Base62Id = Base62Id::encode(value);
1250 assert_eq!(id.decode(), value);
1251 }
1252 }
1253
1254 #[test]
1255 fn base62_id_larger_cap() {
1256 let id_small: Base62Id<16> = Base62Id::encode(12345);
1257 let id_large: Base62Id<32> = Base62Id::encode(12345);
1258 assert_eq!(id_small.as_str(), id_large.as_str());
1259 }
1260
1261 #[test]
1262 fn base36_id_decode_roundtrip() {
1263 for value in [0, 1, 12345, u64::MAX] {
1264 let id: Base36Id = Base36Id::encode(value);
1265 assert_eq!(id.decode(), value);
1266 }
1267 }
1268
1269 #[test]
1270 fn base36_id_larger_cap() {
1271 let id_small: Base36Id<16> = Base36Id::encode(12345);
1272 let id_large: Base36Id<32> = Base36Id::encode(12345);
1273 assert_eq!(id_small.as_str(), id_large.as_str());
1274 }
1275
1276 #[test]
1277 fn ulid_larger_cap() {
1278 let small: Ulid<32> = Ulid::from_raw(1_700_000_000_000, 0x1234, 0xDEAD_BEEF);
1279 let large: Ulid<64> = Ulid::from_raw(1_700_000_000_000, 0x1234, 0xDEAD_BEEF);
1280 assert_eq!(small.as_str(), large.as_str());
1281 assert_eq!(small.timestamp_ms(), large.timestamp_ms());
1282 }
1283
1284 #[test]
1285 fn uuid_version() {
1286 let hi = 0x0123_4567_89AB_4DEF_u64; let lo = 0x8EDC_BA98_7654_3210_u64;
1289 let uuid: Uuid = Uuid::from_raw(hi, lo);
1290 assert_eq!(uuid.version(), 4);
1291
1292 let hi = 0x0123_4567_89AB_7DEF_u64; let uuid: Uuid = Uuid::from_raw(hi, lo);
1295 assert_eq!(uuid.version(), 7);
1296 }
1297
1298 #[test]
1299 fn display_works() {
1300 let uuid: Uuid = Uuid::from_raw(0x0123_4567_89AB_CDEF, 0xFEDC_BA98_7654_3210);
1301 let s = format!("{}", uuid);
1302 assert_eq!(s, "01234567-89ab-cdef-fedc-ba9876543210");
1303 }
1304
1305 #[test]
1306 fn deref_works() {
1307 let uuid: Uuid = Uuid::from_raw(0x0123_4567_89AB_CDEF, 0xFEDC_BA98_7654_3210);
1308 let s: &str = &uuid;
1309 assert_eq!(s, "01234567-89ab-cdef-fedc-ba9876543210");
1310 }
1311
1312 #[test]
1313 fn uuid_from_bytes_roundtrip() {
1314 let original: Uuid = Uuid::from_raw(0x0123_4567_89AB_CDEF, 0xFEDC_BA98_7654_3210);
1315 let bytes = original.to_bytes();
1316 let recovered: Uuid = Uuid::from_bytes(&bytes).unwrap();
1317 assert_eq!(original, recovered);
1318 }
1319
1320 #[test]
1321 fn uuid_from_bytes_wrong_length() {
1322 assert!(Uuid::<40>::from_bytes(&[0u8; 15]).is_err());
1323 assert!(Uuid::<40>::from_bytes(&[0u8; 17]).is_err());
1324 assert!(Uuid::<40>::from_bytes(&[]).is_err());
1325 }
1326
1327 #[test]
1328 fn uuid_from_bytes_unchecked_roundtrip() {
1329 let original: Uuid = Uuid::from_raw(0xDEAD_BEEF_CAFE_BABE, 0x0123_4567_89AB_CDEF);
1330 let bytes = original.to_bytes();
1331 let recovered: Uuid = unsafe { Uuid::from_bytes_unchecked(&bytes) };
1332 assert_eq!(original, recovered);
1333 }
1334
1335 #[test]
1336 fn uuid_compact_from_bytes_roundtrip() {
1337 let original: UuidCompact =
1338 UuidCompact::from_raw(0x0123_4567_89AB_CDEF, 0xFEDC_BA98_7654_3210);
1339 let bytes = original.to_bytes();
1340 let recovered: UuidCompact = UuidCompact::from_bytes(&bytes).unwrap();
1341 assert_eq!(original, recovered);
1342 }
1343
1344 #[test]
1345 fn uuid_compact_from_bytes_wrong_length() {
1346 assert!(UuidCompact::<32>::from_bytes(&[0u8; 15]).is_err());
1347 assert!(UuidCompact::<32>::from_bytes(&[0u8; 17]).is_err());
1348 }
1349
1350 #[test]
1351 fn ulid_from_bytes_roundtrip() {
1352 let original: Ulid = Ulid::from_raw(1_700_000_000_000, 0xABCD, 0xDEAD_BEEF_CAFE_BABE);
1353 let bytes = original.to_bytes();
1354 let recovered: Ulid = Ulid::from_bytes(&bytes).unwrap();
1355 assert_eq!(original.timestamp_ms(), recovered.timestamp_ms());
1356 assert_eq!(original.random(), recovered.random());
1357 assert_eq!(original, recovered);
1358 }
1359
1360 #[test]
1361 fn ulid_from_bytes_wrong_length() {
1362 assert!(Ulid::<32>::from_bytes(&[0u8; 15]).is_err());
1363 assert!(Ulid::<32>::from_bytes(&[0u8; 17]).is_err());
1364 }
1365
1366 #[test]
1367 fn ulid_from_bytes_unchecked_roundtrip() {
1368 let original: Ulid = Ulid::from_raw(1_700_000_000_000, 0x1234, 0x0123_4567_89AB_CDEF);
1369 let bytes = original.to_bytes();
1370 let recovered: Ulid = unsafe { Ulid::from_bytes_unchecked(&bytes) };
1371 assert_eq!(original, recovered);
1372 }
1373
1374 #[test]
1375 fn ulid_parse_rejects_overflow_first_char() {
1376 let overflow = "80000000000000000000000000";
1379 assert!(Ulid::<32>::parse(overflow).is_err());
1380
1381 let z_first = "Z0000000000000000000000000";
1383 assert!(Ulid::<32>::parse(z_first).is_err());
1384
1385 let max_valid = "70000000000000000000000000";
1387 assert!(Ulid::<32>::parse(max_valid).is_ok());
1388 }
1389
1390 #[test]
1395 fn base62_parse_overflow() {
1396 use crate::parse::DecodeError;
1397
1398 let result = Base62Id::<16>::parse("zzzzzzzzzzz");
1400 assert_eq!(result, Err(DecodeError::Overflow));
1401
1402 let max_id: Base62Id = Base62Id::encode(u64::MAX);
1404 let parsed = Base62Id::<16>::parse(max_id.as_str()).unwrap();
1405 assert_eq!(parsed.decode(), u64::MAX);
1406 }
1407
1408 #[test]
1409 fn base36_parse_overflow() {
1410 use crate::parse::DecodeError;
1411
1412 let result = Base36Id::<16>::parse("zzzzzzzzzzzzz");
1414 assert_eq!(result, Err(DecodeError::Overflow));
1415
1416 let max_id: Base36Id = Base36Id::encode(u64::MAX);
1418 let parsed = Base36Id::<16>::parse(max_id.as_str()).unwrap();
1419 assert_eq!(parsed.decode(), u64::MAX);
1420 }
1421
1422 #[test]
1427 fn uuid_parse_wrong_length() {
1428 use crate::parse::UuidParseError;
1429
1430 let result = Uuid::<40>::parse("01234567-89ab-cdef-fedc-ba987654321"); assert!(matches!(result, Err(UuidParseError::InvalidLength { .. })));
1432
1433 let result = Uuid::<40>::parse("01234567-89ab-cdef-fedc-ba98765432100"); assert!(matches!(result, Err(UuidParseError::InvalidLength { .. })));
1435
1436 let result = Uuid::<40>::parse("");
1437 assert!(matches!(result, Err(UuidParseError::InvalidLength { .. })));
1438 }
1439
1440 #[test]
1441 fn uuid_parse_bad_dashes() {
1442 use crate::parse::UuidParseError;
1443
1444 let result = Uuid::<40>::parse("01234567089ab-cdef-fedc-ba9876543210");
1446 assert!(matches!(result, Err(UuidParseError::InvalidFormat)));
1447
1448 let result = Uuid::<40>::parse("01234567-89ab0cdef-fedc-ba9876543210");
1450 assert!(matches!(result, Err(UuidParseError::InvalidFormat)));
1451
1452 let result = Uuid::<40>::parse("01234567-89ab-cdef0fedc-ba9876543210");
1454 assert!(matches!(result, Err(UuidParseError::InvalidFormat)));
1455
1456 let result = Uuid::<40>::parse("01234567-89ab-cdef-fedc0ba9876543210");
1458 assert!(matches!(result, Err(UuidParseError::InvalidFormat)));
1459 }
1460
1461 #[test]
1462 fn uuid_parse_invalid_hex_char() {
1463 use crate::parse::UuidParseError;
1464
1465 let result = Uuid::<40>::parse("g1234567-89ab-cdef-fedc-ba9876543210");
1467 assert!(matches!(
1468 result,
1469 Err(UuidParseError::InvalidChar { position: 0, .. })
1470 ));
1471
1472 let result = Uuid::<40>::parse("01234567-89xb-cdef-fedc-ba9876543210");
1474 assert!(matches!(
1475 result,
1476 Err(UuidParseError::InvalidChar { position: 11, .. })
1477 ));
1478 }
1479
1480 #[test]
1485 fn uuid_is_nil() {
1486 let nil: Uuid = Uuid::from_raw(0, 0);
1487 assert!(nil.is_nil());
1488
1489 let not_nil: Uuid = Uuid::from_raw(0, 1);
1490 assert!(!not_nil.is_nil());
1491
1492 let not_nil: Uuid = Uuid::from_raw(1, 0);
1493 assert!(!not_nil.is_nil());
1494 }
1495
1496 #[test]
1497 fn uuid_compact_is_nil() {
1498 let nil: UuidCompact = UuidCompact::from_raw(0, 0);
1499 assert!(nil.is_nil());
1500
1501 let not_nil: UuidCompact = UuidCompact::from_raw(0, 1);
1502 assert!(!not_nil.is_nil());
1503 }
1504
1505 #[test]
1506 fn ulid_is_nil() {
1507 let nil: Ulid = Ulid::from_raw(0, 0, 0);
1508 assert!(nil.is_nil());
1509
1510 let not_nil: Ulid = Ulid::from_raw(1, 0, 0);
1512 assert!(!not_nil.is_nil());
1513
1514 let not_nil: Ulid = Ulid::from_raw(0, 1, 0);
1516 assert!(!not_nil.is_nil());
1517
1518 let not_nil: Ulid = Ulid::from_raw(0, 0, 1);
1520 assert!(!not_nil.is_nil());
1521 }
1522
1523 #[test]
1528 fn ulid_parse_crockford_aliases() {
1529 let canonical: Ulid = Ulid::parse("01000000000000000000000000").unwrap();
1532
1533 let with_o: Ulid = Ulid::parse("O1000000000000000000000000").unwrap();
1535 assert_eq!(canonical, with_o);
1536
1537 let with_i: Ulid = Ulid::parse("0I000000000000000000000000").unwrap();
1539 assert_eq!(canonical, with_i);
1540
1541 let with_l: Ulid = Ulid::parse("0L000000000000000000000000").unwrap();
1543 assert_eq!(canonical, with_l);
1544
1545 let with_i_lower: Ulid = Ulid::parse("0i000000000000000000000000").unwrap();
1547 assert_eq!(canonical, with_i_lower);
1548
1549 let with_o_lower: Ulid = Ulid::parse("o1000000000000000000000000").unwrap();
1551 assert_eq!(canonical, with_o_lower);
1552
1553 let with_l_lower: Ulid = Ulid::parse("0l000000000000000000000000").unwrap();
1555 assert_eq!(canonical, with_l_lower);
1556 }
1557
1558 #[test]
1563 fn ulid_to_uuid_preserves_timestamp() {
1564 let ts = 1_700_000_000_000u64;
1565 let ulid: Ulid = Ulid::from_raw(ts, 0x1234, 0xDEAD_BEEF_CAFE_BABE);
1566 let uuid = ulid.to_uuid();
1567
1568 assert_eq!(uuid.version(), 7);
1570
1571 let (hi, _) = uuid.decode();
1573 let extracted_ts = hi >> 16;
1574 assert_eq!(extracted_ts, ts);
1575 }
1576
1577 #[test]
1578 fn ulid_to_uuid_is_lossy() {
1579 let ulid_a: Ulid = Ulid::from_raw(1_700_000_000_000, 0x1234, 0xDEAD_BEEF_CAFE_BA00);
1582 let ulid_b: Ulid = Ulid::from_raw(1_700_000_000_000, 0x1234, 0xDEAD_BEEF_CAFE_BA3F);
1583
1584 assert_ne!(ulid_a, ulid_b);
1586
1587 assert_eq!(ulid_a.to_uuid(), ulid_b.to_uuid());
1589 }
1590
1591 #[test]
1592 fn ulid_to_uuid_sets_variant_bits() {
1593 let ulid: Ulid = Ulid::from_raw(1_700_000_000_000, 0xFFFF, u64::MAX);
1594 let uuid = ulid.to_uuid();
1595 let (_, lo) = uuid.decode();
1596
1597 assert_eq!(lo >> 62, 0b10);
1599 }
1600}