1use crate::util::ArenaIndex;
7use core::fmt;
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9use std::ops::Add;
10use std::sync::atomic::{AtomicU32, Ordering};
11use std::time::Duration;
12
13static EPHEMERAL_REGION_COUNTER: AtomicU32 = AtomicU32::new(1);
14static EPHEMERAL_TASK_COUNTER: AtomicU32 = AtomicU32::new(1);
15
16#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
20pub struct RegionId(pub(crate) ArenaIndex);
21
22impl RegionId {
23 #[inline]
25 #[must_use]
26 #[cfg_attr(feature = "test-internals", visibility::make(pub))]
27 pub(crate) const fn from_arena(index: ArenaIndex) -> Self {
28 Self(index)
29 }
30
31 #[inline]
33 #[must_use]
34 pub fn as_u64(&self) -> u64 {
35 ((self.0.generation() as u64) << 32) | (self.0.index() as u64)
36 }
37
38 #[inline]
40 #[must_use]
41 #[allow(dead_code)]
42 #[cfg(not(feature = "test-internals"))]
43 pub(crate) const fn arena_index(self) -> ArenaIndex {
44 self.0
45 }
46
47 #[inline]
49 #[must_use]
50 #[allow(dead_code)]
51 #[cfg(feature = "test-internals")]
52 pub const fn arena_index(self) -> ArenaIndex {
53 self.0
54 }
55
56 #[doc(hidden)]
58 #[inline]
59 #[must_use]
60 pub const fn new_for_test(index: u32, generation: u32) -> Self {
61 Self(ArenaIndex::new(index, generation))
62 }
63
64 #[doc(hidden)]
69 #[inline]
70 #[must_use]
71 pub const fn testing_default() -> Self {
72 Self(ArenaIndex::new(0, 0))
73 }
74
75 #[inline]
81 #[must_use]
82 pub fn new_ephemeral() -> Self {
83 let index = EPHEMERAL_REGION_COUNTER.fetch_add(1, Ordering::Relaxed);
84 Self(ArenaIndex::new(index, 1))
85 }
86}
87
88impl fmt::Debug for RegionId {
89 #[inline]
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 write!(f, "RegionId({}:{})", self.0.index(), self.0.generation())
92 }
93}
94
95impl fmt::Display for RegionId {
96 #[inline]
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 write!(f, "R{}", self.0.index())
99 }
100}
101
102#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
103struct SerdeArenaIndex {
104 index: u32,
105 generation: u32,
106}
107
108impl SerdeArenaIndex {
109 #[inline]
110 const fn to_arena(self) -> ArenaIndex {
111 ArenaIndex::new(self.index, self.generation)
112 }
113}
114
115impl From<ArenaIndex> for SerdeArenaIndex {
116 #[inline]
117 fn from(value: ArenaIndex) -> Self {
118 Self {
119 index: value.index(),
120 generation: value.generation(),
121 }
122 }
123}
124
125impl Serialize for RegionId {
126 #[inline]
127 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
128 where
129 S: Serializer,
130 {
131 SerdeArenaIndex::from(self.0).serialize(serializer)
132 }
133}
134
135impl<'de> Deserialize<'de> for RegionId {
136 #[inline]
137 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138 where
139 D: Deserializer<'de>,
140 {
141 let idx = SerdeArenaIndex::deserialize(deserializer)?;
142 Ok(Self(idx.to_arena()))
143 }
144}
145
146#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
150pub struct TaskId(pub(crate) ArenaIndex);
151
152impl TaskId {
153 #[inline]
155 #[must_use]
156 #[allow(dead_code)]
157 #[cfg_attr(feature = "test-internals", visibility::make(pub))]
158 pub(crate) const fn from_arena(index: ArenaIndex) -> Self {
159 Self(index)
160 }
161
162 #[inline]
164 #[must_use]
165 pub fn as_u64(&self) -> u64 {
166 ((self.0.generation() as u64) << 32) | (self.0.index() as u64)
167 }
168
169 #[inline]
171 #[must_use]
172 #[allow(dead_code)]
173 #[cfg(not(feature = "test-internals"))]
174 pub(crate) const fn arena_index(self) -> ArenaIndex {
175 self.0
176 }
177
178 #[inline]
180 #[must_use]
181 #[allow(dead_code)]
182 #[cfg(feature = "test-internals")]
183 pub const fn arena_index(self) -> ArenaIndex {
184 self.0
185 }
186
187 #[doc(hidden)]
189 #[inline]
190 #[must_use]
191 pub const fn new_for_test(index: u32, generation: u32) -> Self {
192 Self(ArenaIndex::new(index, generation))
193 }
194
195 #[doc(hidden)]
200 #[inline]
201 #[must_use]
202 pub const fn testing_default() -> Self {
203 Self(ArenaIndex::new(0, 0))
204 }
205
206 #[inline]
209 #[must_use]
210 pub fn new_ephemeral() -> Self {
211 let index = EPHEMERAL_TASK_COUNTER.fetch_add(1, Ordering::Relaxed);
212 Self(ArenaIndex::new(index, 1))
213 }
214}
215
216impl fmt::Debug for TaskId {
217 #[inline]
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 write!(f, "TaskId({}:{})", self.0.index(), self.0.generation())
220 }
221}
222
223impl fmt::Display for TaskId {
224 #[inline]
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 write!(f, "T{}", self.0.index())
227 }
228}
229
230impl Serialize for TaskId {
231 #[inline]
232 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
233 where
234 S: Serializer,
235 {
236 SerdeArenaIndex::from(self.0).serialize(serializer)
237 }
238}
239
240impl<'de> Deserialize<'de> for TaskId {
241 #[inline]
242 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
243 where
244 D: Deserializer<'de>,
245 {
246 let idx = SerdeArenaIndex::deserialize(deserializer)?;
247 Ok(Self(idx.to_arena()))
248 }
249}
250
251#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
256pub struct ObligationId(pub(crate) ArenaIndex);
257
258impl ObligationId {
259 #[inline]
261 #[must_use]
262 #[allow(dead_code)]
263 pub(crate) const fn from_arena(index: ArenaIndex) -> Self {
264 Self(index)
265 }
266
267 #[inline]
269 #[must_use]
270 #[allow(dead_code)]
271 #[cfg(not(feature = "test-internals"))]
272 pub(crate) const fn arena_index(self) -> ArenaIndex {
273 self.0
274 }
275
276 #[inline]
278 #[must_use]
279 #[allow(dead_code)]
280 #[cfg(feature = "test-internals")]
281 pub const fn arena_index(self) -> ArenaIndex {
282 self.0
283 }
284
285 #[doc(hidden)]
287 #[inline]
288 #[must_use]
289 pub const fn new_for_test(index: u32, generation: u32) -> Self {
290 Self(ArenaIndex::new(index, generation))
291 }
292}
293
294impl fmt::Debug for ObligationId {
295 #[inline]
296 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297 write!(
298 f,
299 "ObligationId({}:{})",
300 self.0.index(),
301 self.0.generation()
302 )
303 }
304}
305
306impl fmt::Display for ObligationId {
307 #[inline]
308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309 write!(f, "O{}", self.0.index())
310 }
311}
312
313impl Serialize for ObligationId {
314 #[inline]
315 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
316 where
317 S: Serializer,
318 {
319 SerdeArenaIndex::from(self.0).serialize(serializer)
320 }
321}
322
323impl<'de> Deserialize<'de> for ObligationId {
324 #[inline]
325 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
326 where
327 D: Deserializer<'de>,
328 {
329 let idx = SerdeArenaIndex::deserialize(deserializer)?;
330 Ok(Self(idx.to_arena()))
331 }
332}
333
334#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
339pub struct Time(u64);
340
341impl Time {
342 pub const ZERO: Self = Self(0);
344
345 pub const MAX: Self = Self(u64::MAX);
347
348 #[inline]
350 #[must_use]
351 pub const fn from_nanos(nanos: u64) -> Self {
352 Self(nanos)
353 }
354
355 #[inline]
357 #[must_use]
358 pub const fn from_millis(millis: u64) -> Self {
359 Self(millis.saturating_mul(1_000_000))
360 }
361
362 #[inline]
364 #[must_use]
365 pub const fn from_secs(secs: u64) -> Self {
366 Self(secs.saturating_mul(1_000_000_000))
367 }
368
369 #[inline]
371 #[must_use]
372 pub const fn as_nanos(self) -> u64 {
373 self.0
374 }
375
376 #[inline]
378 #[must_use]
379 pub const fn as_millis(self) -> u64 {
380 self.0 / 1_000_000
381 }
382
383 #[inline]
385 #[must_use]
386 pub const fn as_secs(self) -> u64 {
387 self.0 / 1_000_000_000
388 }
389
390 #[inline]
392 #[must_use]
393 pub const fn saturating_add_nanos(self, nanos: u64) -> Self {
394 Self(self.0.saturating_add(nanos))
395 }
396
397 #[inline]
399 #[must_use]
400 pub const fn saturating_sub_nanos(self, nanos: u64) -> Self {
401 Self(self.0.saturating_sub(nanos))
402 }
403
404 #[inline]
409 #[must_use]
410 pub const fn duration_since(self, earlier: Self) -> u64 {
411 self.0.saturating_sub(earlier.0)
412 }
413}
414
415impl Add<Duration> for Time {
416 type Output = Self;
417
418 #[inline]
419 fn add(self, rhs: Duration) -> Self::Output {
420 let nanos: u64 = rhs.as_nanos().min(u128::from(u64::MAX)) as u64;
421 self.saturating_add_nanos(nanos)
422 }
423}
424
425impl fmt::Debug for Time {
426 #[inline]
427 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428 write!(f, "Time({}ns)", self.0)
429 }
430}
431
432impl fmt::Display for Time {
433 #[inline]
434 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435 if self.0 >= 1_000_000_000 {
436 write!(
437 f,
438 "{}.{:03}s",
439 self.0 / 1_000_000_000,
440 (self.0 / 1_000_000) % 1000
441 )
442 } else if self.0 >= 1_000_000 {
443 write!(f, "{}ms", self.0 / 1_000_000)
444 } else if self.0 >= 1_000 {
445 write!(f, "{}us", self.0 / 1_000)
446 } else {
447 write!(f, "{}ns", self.0)
448 }
449 }
450}
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455
456 #[test]
457 fn time_conversions() {
458 assert_eq!(Time::from_secs(1).as_nanos(), 1_000_000_000);
459 assert_eq!(Time::from_millis(1).as_nanos(), 1_000_000);
460 assert_eq!(Time::from_nanos(1).as_nanos(), 1);
461
462 assert_eq!(Time::from_nanos(1_500_000_000).as_secs(), 1);
463 assert_eq!(Time::from_nanos(1_500_000_000).as_millis(), 1500);
464 }
465
466 #[test]
467 fn time_arithmetic() {
468 let t1 = Time::from_secs(1);
469 let t2 = t1.saturating_add_nanos(500_000_000);
470 assert_eq!(t2.as_millis(), 1500);
471
472 let t3 = t2.saturating_sub_nanos(2_000_000_000);
473 assert_eq!(t3, Time::ZERO);
474 }
475
476 #[test]
477 fn time_ordering() {
478 assert!(Time::from_secs(1) < Time::from_secs(2));
479 assert!(Time::from_millis(1000) == Time::from_secs(1));
480 }
481
482 #[test]
485 fn region_id_debug_format() {
486 let id = RegionId::new_for_test(5, 3);
487 let dbg = format!("{id:?}");
488 assert!(dbg.contains("RegionId"), "{dbg}");
489 assert!(dbg.contains('5'), "{dbg}");
490 assert!(dbg.contains('3'), "{dbg}");
491 }
492
493 #[test]
494 fn region_id_display_format() {
495 let id = RegionId::new_for_test(42, 0);
496 assert_eq!(format!("{id}"), "R42");
497 }
498
499 #[test]
500 fn region_id_equality_and_hash() {
501 use crate::util::DetHasher;
502 use std::hash::{Hash, Hasher};
503
504 let a = RegionId::new_for_test(1, 2);
505 let b = RegionId::new_for_test(1, 2);
506 let c = RegionId::new_for_test(1, 3);
507
508 assert_eq!(a, b);
509 assert_ne!(a, c);
510
511 let mut ha = DetHasher::default();
512 let mut hb = DetHasher::default();
513 a.hash(&mut ha);
514 b.hash(&mut hb);
515 assert_eq!(ha.finish(), hb.finish());
516 }
517
518 #[test]
519 fn region_id_ordering() {
520 let a = RegionId::new_for_test(1, 0);
521 let b = RegionId::new_for_test(2, 0);
522 assert!(a < b);
523 assert!(a <= b);
524 assert!(b > a);
525 }
526
527 #[test]
528 fn region_id_copy_clone() {
529 let id = RegionId::new_for_test(1, 0);
530 let copied = id;
531 let cloned = id;
532 assert_eq!(id, copied);
533 assert_eq!(id, cloned);
534 }
535
536 #[test]
537 fn region_id_testing_default() {
538 let id = RegionId::testing_default();
539 assert_eq!(format!("{id}"), "R0");
540 }
541
542 #[test]
543 fn region_id_ephemeral_unique() {
544 let a = RegionId::new_ephemeral();
545 let b = RegionId::new_ephemeral();
546 assert_ne!(a, b);
547 }
548
549 #[test]
550 fn region_id_serde_roundtrip() {
551 let id = RegionId::new_for_test(99, 7);
552 let json = serde_json::to_string(&id).expect("serialize");
553 let deserialized: RegionId = serde_json::from_str(&json).expect("deserialize");
554 assert_eq!(id, deserialized);
555 }
556
557 #[test]
560 fn task_id_debug_format() {
561 let id = TaskId::new_for_test(10, 2);
562 let dbg = format!("{id:?}");
563 assert!(dbg.contains("TaskId"), "{dbg}");
564 assert!(dbg.contains("10"), "{dbg}");
565 assert!(dbg.contains('2'), "{dbg}");
566 }
567
568 #[test]
569 fn task_id_display_format() {
570 let id = TaskId::new_for_test(7, 0);
571 assert_eq!(format!("{id}"), "T7");
572 }
573
574 #[test]
575 fn task_id_equality_and_hash() {
576 use crate::util::DetHasher;
577 use std::hash::{Hash, Hasher};
578
579 let a = TaskId::new_for_test(3, 1);
580 let b = TaskId::new_for_test(3, 1);
581 let c = TaskId::new_for_test(3, 2);
582
583 assert_eq!(a, b);
584 assert_ne!(a, c);
585
586 let mut ha = DetHasher::default();
587 let mut hb = DetHasher::default();
588 a.hash(&mut ha);
589 b.hash(&mut hb);
590 assert_eq!(ha.finish(), hb.finish());
591 }
592
593 #[test]
594 fn task_id_ordering() {
595 let a = TaskId::new_for_test(1, 0);
596 let b = TaskId::new_for_test(2, 0);
597 assert!(a < b);
598 }
599
600 #[test]
601 fn task_id_copy_clone() {
602 let id = TaskId::new_for_test(5, 1);
603 let copied = id;
604 let cloned = id;
605 assert_eq!(id, copied);
606 assert_eq!(id, cloned);
607 }
608
609 #[test]
610 fn task_id_testing_default() {
611 let id = TaskId::testing_default();
612 assert_eq!(format!("{id}"), "T0");
613 }
614
615 #[test]
616 fn task_id_ephemeral_unique() {
617 let a = TaskId::new_ephemeral();
618 let b = TaskId::new_ephemeral();
619 assert_ne!(a, b);
620 }
621
622 #[test]
623 fn task_id_serde_roundtrip() {
624 let id = TaskId::new_for_test(42, 5);
625 let json = serde_json::to_string(&id).expect("serialize");
626 let deserialized: TaskId = serde_json::from_str(&json).expect("deserialize");
627 assert_eq!(id, deserialized);
628 }
629
630 #[test]
633 fn obligation_id_debug_format() {
634 let id = ObligationId::new_for_test(8, 1);
635 let dbg = format!("{id:?}");
636 assert!(dbg.contains("ObligationId"), "{dbg}");
637 assert!(dbg.contains('8'), "{dbg}");
638 }
639
640 #[test]
641 fn obligation_id_display_format() {
642 let id = ObligationId::new_for_test(3, 0);
643 assert_eq!(format!("{id}"), "O3");
644 }
645
646 #[test]
647 fn obligation_id_equality_and_hash() {
648 use crate::util::DetHasher;
649 use std::hash::{Hash, Hasher};
650
651 let a = ObligationId::new_for_test(1, 1);
652 let b = ObligationId::new_for_test(1, 1);
653 let c = ObligationId::new_for_test(2, 1);
654
655 assert_eq!(a, b);
656 assert_ne!(a, c);
657
658 let mut ha = DetHasher::default();
659 let mut hb = DetHasher::default();
660 a.hash(&mut ha);
661 b.hash(&mut hb);
662 assert_eq!(ha.finish(), hb.finish());
663 }
664
665 #[test]
666 fn obligation_id_ordering() {
667 let a = ObligationId::new_for_test(1, 0);
668 let b = ObligationId::new_for_test(2, 0);
669 assert!(a < b);
670 }
671
672 #[test]
673 fn obligation_id_copy_clone() {
674 let id = ObligationId::new_for_test(1, 0);
675 let copied = id;
676 let cloned = id;
677 assert_eq!(id, copied);
678 assert_eq!(id, cloned);
679 }
680
681 #[test]
682 fn obligation_id_serde_roundtrip() {
683 let id = ObligationId::new_for_test(77, 3);
684 let json = serde_json::to_string(&id).expect("serialize");
685 let deserialized: ObligationId = serde_json::from_str(&json).expect("deserialize");
686 assert_eq!(id, deserialized);
687 }
688
689 #[test]
692 fn time_display_seconds() {
693 let t = Time::from_secs(2);
694 let disp = format!("{t}");
695 assert_eq!(disp, "2.000s");
696 }
697
698 #[test]
699 fn time_display_seconds_with_millis() {
700 let t = Time::from_nanos(1_234_000_000);
701 let disp = format!("{t}");
702 assert_eq!(disp, "1.234s");
703 }
704
705 #[test]
706 fn time_display_milliseconds() {
707 let t = Time::from_millis(500);
708 let disp = format!("{t}");
709 assert_eq!(disp, "500ms");
710 }
711
712 #[test]
713 fn time_display_microseconds() {
714 let t = Time::from_nanos(5_000);
715 let disp = format!("{t}");
716 assert_eq!(disp, "5us");
717 }
718
719 #[test]
720 fn time_display_nanoseconds() {
721 let t = Time::from_nanos(42);
722 let disp = format!("{t}");
723 assert_eq!(disp, "42ns");
724 }
725
726 #[test]
727 fn time_display_zero() {
728 assert_eq!(format!("{}", Time::ZERO), "0ns");
729 }
730
731 #[test]
734 fn time_debug_format() {
735 let t = Time::from_nanos(100);
736 let dbg = format!("{t:?}");
737 assert_eq!(dbg, "Time(100ns)");
738 }
739
740 #[test]
741 fn time_default_is_zero() {
742 assert_eq!(Time::default(), Time::ZERO);
743 }
744
745 #[test]
746 fn time_max_constant() {
747 assert_eq!(Time::MAX.as_nanos(), u64::MAX);
748 }
749
750 #[test]
751 fn time_saturating_add_overflow() {
752 let t = Time::MAX;
753 let result = t.saturating_add_nanos(1);
754 assert_eq!(result, Time::MAX);
755 }
756
757 #[test]
758 fn time_saturating_sub_underflow() {
759 let t = Time::ZERO;
760 let result = t.saturating_sub_nanos(100);
761 assert_eq!(result, Time::ZERO);
762 }
763
764 #[test]
765 fn time_duration_since() {
766 let t1 = Time::from_secs(5);
767 let t2 = Time::from_secs(3);
768 assert_eq!(t1.duration_since(t2), 2_000_000_000);
769 assert_eq!(t2.duration_since(t1), 0); }
771
772 #[test]
773 fn time_add_duration() {
774 let t = Time::from_secs(1);
775 let result = t + Duration::from_millis(500);
776 assert_eq!(result.as_millis(), 1500);
777 }
778
779 #[test]
780 fn time_from_millis_saturation() {
781 let t = Time::from_millis(u64::MAX);
782 assert_eq!(t, Time::MAX);
784 }
785
786 #[test]
787 fn time_from_secs_saturation() {
788 let t = Time::from_secs(u64::MAX);
789 assert_eq!(t, Time::MAX);
790 }
791
792 #[test]
793 fn time_serde_roundtrip() {
794 let t = Time::from_nanos(12345);
795 let json = serde_json::to_string(&t).expect("serialize");
796 let deserialized: Time = serde_json::from_str(&json).expect("deserialize");
797 assert_eq!(t, deserialized);
798 }
799
800 #[test]
801 fn time_hash_consistency() {
802 use crate::util::DetHasher;
803 use std::hash::{Hash, Hasher};
804
805 let a = Time::from_secs(1);
806 let b = Time::from_millis(1000);
807 assert_eq!(a, b);
808
809 let mut ha = DetHasher::default();
810 let mut hb = DetHasher::default();
811 a.hash(&mut ha);
812 b.hash(&mut hb);
813 assert_eq!(ha.finish(), hb.finish());
814 }
815}