1use core::cmp::Ordering;
8use core::fmt;
9use core::hash::{Hash, Hasher};
10
11use crate::types::{Base36Id, Base62Id, HexId64};
12
13const GOLDEN_64: u64 = 0x9E37_79B9_7F4A_7C15;
16
17const GOLDEN_64_INV: u64 = 0xF1DE_83E1_9937_733D;
20
21const GOLDEN_32: u32 = 0x9E37_79B9;
23
24const GOLDEN_32_INV: u32 = 0x144C_BC89;
26
27#[derive(Clone, Copy, PartialEq, Eq)]
52#[repr(transparent)]
53pub struct SnowflakeId64<const TS: u8, const WK: u8, const SQ: u8>(pub(crate) u64);
54
55impl<const TS: u8, const WK: u8, const SQ: u8> SnowflakeId64<TS, WK, SQ> {
56 const TS_SHIFT: u8 = WK + SQ;
57 const WK_SHIFT: u8 = SQ;
58 const SEQUENCE_MASK: u64 = (1u64 << SQ) - 1;
59 const WORKER_MASK: u64 = if WK == 0 { 0 } else { (1u64 << WK) - 1 };
60
61 #[inline]
63 pub const fn from_raw(raw: u64) -> Self {
64 Self(raw)
65 }
66
67 #[inline]
69 pub const fn raw(&self) -> u64 {
70 self.0
71 }
72
73 #[inline]
75 pub const fn timestamp(&self) -> u64 {
76 self.0 >> Self::TS_SHIFT
77 }
78
79 #[inline]
81 pub const fn worker(&self) -> u64 {
82 (self.0 >> Self::WK_SHIFT) & Self::WORKER_MASK
83 }
84
85 #[inline]
87 pub const fn sequence(&self) -> u64 {
88 self.0 & Self::SEQUENCE_MASK
89 }
90
91 #[inline]
93 pub const fn unpack(&self) -> (u64, u64, u64) {
94 (self.timestamp(), self.worker(), self.sequence())
95 }
96
97 #[inline]
104 pub const fn mixed(&self) -> MixedId64<TS, WK, SQ> {
105 MixedId64(self.0.wrapping_mul(GOLDEN_64))
106 }
107
108 #[inline]
110 pub fn to_hex(&self) -> HexId64 {
111 HexId64::encode(self.0)
112 }
113
114 #[inline]
116 pub fn to_base62(&self) -> Base62Id {
117 Base62Id::encode(self.0)
118 }
119
120 #[inline]
122 pub fn to_base36(&self) -> Base36Id {
123 Base36Id::encode(self.0)
124 }
125}
126
127impl<const TS: u8, const WK: u8, const SQ: u8> Ord for SnowflakeId64<TS, WK, SQ> {
128 #[inline]
129 fn cmp(&self, other: &Self) -> Ordering {
130 self.0.cmp(&other.0)
132 }
133}
134
135impl<const TS: u8, const WK: u8, const SQ: u8> PartialOrd for SnowflakeId64<TS, WK, SQ> {
136 #[inline]
137 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
138 Some(self.cmp(other))
139 }
140}
141
142impl<const TS: u8, const WK: u8, const SQ: u8> Hash for SnowflakeId64<TS, WK, SQ> {
143 #[inline]
144 fn hash<H: Hasher>(&self, state: &mut H) {
145 state.write_u64(self.0);
146 }
147}
148
149impl<const TS: u8, const WK: u8, const SQ: u8> fmt::Debug for SnowflakeId64<TS, WK, SQ> {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 write!(
152 f,
153 "SnowflakeId64({}, ts={}, w={}, s={})",
154 self.0,
155 self.timestamp(),
156 self.worker(),
157 self.sequence()
158 )
159 }
160}
161
162impl<const TS: u8, const WK: u8, const SQ: u8> fmt::Display for SnowflakeId64<TS, WK, SQ> {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 write!(f, "{}", self.0)
165 }
166}
167
168#[derive(Clone, Copy, PartialEq, Eq)]
183#[repr(transparent)]
184pub struct MixedId64<const TS: u8, const WK: u8, const SQ: u8>(pub(crate) u64);
185
186impl<const TS: u8, const WK: u8, const SQ: u8> MixedId64<TS, WK, SQ> {
187 #[inline]
189 pub const fn from_raw(raw: u64) -> Self {
190 Self(raw)
191 }
192
193 #[inline]
195 pub const fn raw(&self) -> u64 {
196 self.0
197 }
198
199 #[inline]
201 pub const fn unmix(&self) -> SnowflakeId64<TS, WK, SQ> {
202 SnowflakeId64(self.0.wrapping_mul(GOLDEN_64_INV))
203 }
204}
205
206impl<const TS: u8, const WK: u8, const SQ: u8> Hash for MixedId64<TS, WK, SQ> {
207 #[inline]
208 fn hash<H: Hasher>(&self, state: &mut H) {
209 state.write_u64(self.0);
211 }
212}
213
214impl<const TS: u8, const WK: u8, const SQ: u8> fmt::Debug for MixedId64<TS, WK, SQ> {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 write!(f, "MixedId64(0x{:016x})", self.0)
217 }
218}
219
220impl<const TS: u8, const WK: u8, const SQ: u8> fmt::Display for MixedId64<TS, WK, SQ> {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 write!(f, "{}", self.0)
223 }
224}
225
226#[derive(Clone, Copy, PartialEq, Eq)]
234#[repr(transparent)]
235pub struct SnowflakeId32<const TS: u8, const WK: u8, const SQ: u8>(pub(crate) u32);
236
237impl<const TS: u8, const WK: u8, const SQ: u8> SnowflakeId32<TS, WK, SQ> {
238 const TS_SHIFT: u8 = WK + SQ;
239 const WK_SHIFT: u8 = SQ;
240 const SEQUENCE_MASK: u32 = (1u32 << SQ) - 1;
241 const WORKER_MASK: u32 = if WK == 0 { 0 } else { (1u32 << WK) - 1 };
242
243 #[inline]
245 pub const fn from_raw(raw: u32) -> Self {
246 Self(raw)
247 }
248
249 #[inline]
251 pub const fn raw(&self) -> u32 {
252 self.0
253 }
254
255 #[inline]
257 pub const fn timestamp(&self) -> u32 {
258 self.0 >> Self::TS_SHIFT
259 }
260
261 #[inline]
263 pub const fn worker(&self) -> u32 {
264 (self.0 >> Self::WK_SHIFT) & Self::WORKER_MASK
265 }
266
267 #[inline]
269 pub const fn sequence(&self) -> u32 {
270 self.0 & Self::SEQUENCE_MASK
271 }
272
273 #[inline]
275 pub const fn unpack(&self) -> (u32, u32, u32) {
276 (self.timestamp(), self.worker(), self.sequence())
277 }
278
279 #[inline]
281 pub const fn mixed(&self) -> MixedId32<TS, WK, SQ> {
282 MixedId32(self.0.wrapping_mul(GOLDEN_32))
283 }
284
285 #[inline]
287 pub fn to_hex(&self) -> HexId64 {
288 HexId64::encode(self.0 as u64)
289 }
290
291 #[inline]
293 pub fn to_base62(&self) -> Base62Id {
294 Base62Id::encode(self.0 as u64)
295 }
296
297 #[inline]
299 pub fn to_base36(&self) -> Base36Id {
300 Base36Id::encode(self.0 as u64)
301 }
302}
303
304impl<const TS: u8, const WK: u8, const SQ: u8> Ord for SnowflakeId32<TS, WK, SQ> {
305 #[inline]
306 fn cmp(&self, other: &Self) -> Ordering {
307 self.0.cmp(&other.0)
308 }
309}
310
311impl<const TS: u8, const WK: u8, const SQ: u8> PartialOrd for SnowflakeId32<TS, WK, SQ> {
312 #[inline]
313 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
314 Some(self.cmp(other))
315 }
316}
317
318impl<const TS: u8, const WK: u8, const SQ: u8> Hash for SnowflakeId32<TS, WK, SQ> {
319 #[inline]
320 fn hash<H: Hasher>(&self, state: &mut H) {
321 state.write_u32(self.0);
322 }
323}
324
325impl<const TS: u8, const WK: u8, const SQ: u8> fmt::Debug for SnowflakeId32<TS, WK, SQ> {
326 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327 write!(
328 f,
329 "SnowflakeId32({}, ts={}, w={}, s={})",
330 self.0,
331 self.timestamp(),
332 self.worker(),
333 self.sequence()
334 )
335 }
336}
337
338impl<const TS: u8, const WK: u8, const SQ: u8> fmt::Display for SnowflakeId32<TS, WK, SQ> {
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 write!(f, "{}", self.0)
341 }
342}
343
344#[derive(Clone, Copy, PartialEq, Eq)]
350#[repr(transparent)]
351pub struct MixedId32<const TS: u8, const WK: u8, const SQ: u8>(pub(crate) u32);
352
353impl<const TS: u8, const WK: u8, const SQ: u8> MixedId32<TS, WK, SQ> {
354 #[inline]
356 pub const fn from_raw(raw: u32) -> Self {
357 Self(raw)
358 }
359
360 #[inline]
362 pub const fn raw(&self) -> u32 {
363 self.0
364 }
365
366 #[inline]
368 pub const fn unmix(&self) -> SnowflakeId32<TS, WK, SQ> {
369 SnowflakeId32(self.0.wrapping_mul(GOLDEN_32_INV))
370 }
371}
372
373impl<const TS: u8, const WK: u8, const SQ: u8> Hash for MixedId32<TS, WK, SQ> {
374 #[inline]
375 fn hash<H: Hasher>(&self, state: &mut H) {
376 state.write_u32(self.0);
377 }
378}
379
380impl<const TS: u8, const WK: u8, const SQ: u8> fmt::Debug for MixedId32<TS, WK, SQ> {
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 write!(f, "MixedId32(0x{:08x})", self.0)
383 }
384}
385
386impl<const TS: u8, const WK: u8, const SQ: u8> fmt::Display for MixedId32<TS, WK, SQ> {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 write!(f, "{}", self.0)
389 }
390}
391
392impl<const TS: u8, const WK: u8, const SQ: u8> From<SnowflakeId64<TS, WK, SQ>> for u64 {
397 #[inline]
398 fn from(id: SnowflakeId64<TS, WK, SQ>) -> Self {
399 id.0
400 }
401}
402
403impl<const TS: u8, const WK: u8, const SQ: u8> From<MixedId64<TS, WK, SQ>> for u64 {
404 #[inline]
405 fn from(id: MixedId64<TS, WK, SQ>) -> Self {
406 id.0
407 }
408}
409
410impl<const TS: u8, const WK: u8, const SQ: u8> From<SnowflakeId32<TS, WK, SQ>> for u32 {
411 #[inline]
412 fn from(id: SnowflakeId32<TS, WK, SQ>) -> Self {
413 id.0
414 }
415}
416
417impl<const TS: u8, const WK: u8, const SQ: u8> From<MixedId32<TS, WK, SQ>> for u32 {
418 #[inline]
419 fn from(id: MixedId32<TS, WK, SQ>) -> Self {
420 id.0
421 }
422}
423
424#[cfg(all(test, feature = "std"))]
429mod tests {
430 use super::*;
431
432 type Id64 = SnowflakeId64<42, 6, 16>;
433 type Id32 = SnowflakeId32<20, 4, 8>;
434
435 #[test]
436 fn golden_64_inverse_correct() {
437 assert_eq!(GOLDEN_64.wrapping_mul(GOLDEN_64_INV), 1u64);
438 }
439
440 #[test]
441 fn golden_32_inverse_correct() {
442 assert_eq!(GOLDEN_32.wrapping_mul(GOLDEN_32_INV), 1u32);
443 }
444
445 #[test]
446 fn unpack_64() {
447 let raw = (100u64 << 22) | (5u64 << 16) | 0x2A_u64;
449 let id = Id64::from_raw(raw);
450
451 assert_eq!(id.timestamp(), 100);
452 assert_eq!(id.worker(), 5);
453 assert_eq!(id.sequence(), 42);
454 assert_eq!(id.unpack(), (100, 5, 42));
455 }
456
457 #[test]
458 fn unpack_32() {
459 let raw = (50u32 << 12) | (7u32 << 8) | 0xC8_u32;
461 let id = Id32::from_raw(raw);
462
463 assert_eq!(id.timestamp(), 50);
464 assert_eq!(id.worker(), 7);
465 assert_eq!(id.sequence(), 200);
466 }
467
468 #[test]
469 fn mix_unmix_roundtrip_64() {
470 for raw in [0u64, 1, 12345, 0xDEAD_BEEF_CAFE_BABE, u64::MAX] {
471 let id = Id64::from_raw(raw);
472 let mixed = id.mixed();
473 let recovered = mixed.unmix();
474 assert_eq!(recovered.raw(), raw);
475 }
476 }
477
478 #[test]
479 fn mix_unmix_roundtrip_32() {
480 for raw in [0u32, 1, 12345, 0xDEAD_BEEF, u32::MAX] {
481 let id = Id32::from_raw(raw);
482 let mixed = id.mixed();
483 let recovered = mixed.unmix();
484 assert_eq!(recovered.raw(), raw);
485 }
486 }
487
488 #[test]
489 fn mixed_differs_from_raw() {
490 let id = Id64::from_raw(12345);
491 let mixed = id.mixed();
492 assert_ne!(mixed.raw(), id.raw());
493 }
494
495 #[test]
496 fn ordering_preserves_time() {
497 let id1 = Id64::from_raw((100u64 << 22) | (5u64 << 16));
498 let id2 = Id64::from_raw((101u64 << 22) | (5u64 << 16));
499 let id3 = Id64::from_raw((100u64 << 22) | (5u64 << 16) | 1);
500
501 assert!(id2 > id1);
503 assert!(id3 > id1);
505 }
506
507 #[test]
508 fn to_hex_roundtrip() {
509 let id = Id64::from_raw(0xDEAD_BEEF_CAFE_BABE);
510 let hex = id.to_hex();
511 assert_eq!(hex.decode(), id.raw());
512 }
513
514 #[test]
515 fn to_base62_roundtrip() {
516 let id = Id64::from_raw(12_345_678);
517 let b62 = id.to_base62();
518 assert_eq!(b62.decode(), id.raw());
519 }
520
521 #[test]
522 fn to_base36_roundtrip() {
523 let id = Id64::from_raw(12_345_678);
524 let b36 = id.to_base36();
525 assert_eq!(b36.decode(), id.raw());
526 }
527
528 #[test]
529 fn snowflake32_to_hex_roundtrip() {
530 let id = Id32::from_raw(0xDEAD_BEEF);
531 let hex = id.to_hex();
532 assert_eq!(hex.decode(), 0xDEAD_BEEF_u64);
533 }
534
535 #[test]
536 fn snowflake32_to_base62_roundtrip() {
537 let id = Id32::from_raw(12_345_678);
538 let b62 = id.to_base62();
539 assert_eq!(b62.decode(), 12_345_678_u64);
540 }
541
542 #[test]
543 fn snowflake32_to_base36_roundtrip() {
544 let id = Id32::from_raw(12_345_678);
545 let b36 = id.to_base36();
546 assert_eq!(b36.decode(), 12_345_678_u64);
547 }
548
549 #[test]
550 fn display_shows_raw() {
551 let id = Id64::from_raw(42);
552 assert_eq!(format!("{}", id), "42");
553 }
554
555 #[test]
556 fn debug_shows_fields() {
557 let raw = (100u64 << 22) | (5u64 << 16) | 7u64;
558 let id = Id64::from_raw(raw);
559 let dbg = format!("{:?}", id);
560 assert!(dbg.contains("ts=100"));
561 assert!(dbg.contains("w=5"));
562 assert!(dbg.contains("s=7"));
563 }
564}