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