elfo_core/tracing/
trace_id.rs1use std::{
2 convert::TryFrom,
3 num::{NonZeroU64, TryFromIntError},
4 time::SystemTime,
5};
6
7use derive_more::{Deref, Display, From, Into};
8use serde::{Deserialize, Serialize};
9
10use crate::node::NodeNo;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16#[derive(Serialize, Deserialize, Into, From, Display)]
17#[display(fmt = "{_0}")]
18pub struct TraceId(NonZeroU64);
19
20impl TraceId {
21 pub(crate) fn from_layout(layout: TraceIdLayout) -> Self {
22 let raw = (u64::from(*layout.timestamp)) << 38
23 | u64::from(layout.node_no) << 22
24 | u64::from(*layout.bottom);
25
26 Self::try_from(raw).unwrap()
27 }
28
29 pub(crate) fn to_layout(self) -> TraceIdLayout {
30 let raw = self.0.get();
31
32 TraceIdLayout {
33 timestamp: TruncatedTime((raw >> 38) as u32 & 0x1ff_ffff),
34 node_no: (raw >> 22 & 0xffff) as NodeNo,
35 bottom: Bottom((raw & 0x3f_ffff) as u32),
36 }
37 }
38}
39
40impl TryFrom<u64> for TraceId {
41 type Error = TryFromIntError;
42
43 #[inline]
44 fn try_from(raw: u64) -> Result<Self, Self::Error> {
45 NonZeroU64::try_from(raw).map(TraceId)
46 }
47}
48
49impl From<TraceId> for u64 {
50 #[inline]
51 fn from(trace_id: TraceId) -> Self {
52 trace_id.0.get()
53 }
54}
55
56#[derive(Clone, Copy)]
59pub(crate) struct TraceIdLayout {
60 pub(crate) timestamp: TruncatedTime,
61 pub(crate) node_no: NodeNo,
62 pub(crate) bottom: Bottom,
63}
64
65#[derive(Clone, Copy, Deref)]
67pub(crate) struct TruncatedTime(u32);
68
69impl TruncatedTime {
70 pub(crate) fn abs_delta(self, other: TruncatedTime) -> u32 {
71 let a = self.0.wrapping_sub(other.0) & 0x1ff_ffff;
72 let b = other.0.wrapping_sub(self.0) & 0x1ff_ffff;
73 a.min(b)
74 }
75}
76
77impl From<SystemTime> for TruncatedTime {
78 fn from(time: SystemTime) -> Self {
79 let unixtime = time
80 .duration_since(SystemTime::UNIX_EPOCH)
81 .expect("invalid system time")
82 .as_secs();
83
84 Self(unixtime as u32 & 0x1ff_ffff)
85 }
86}
87
88#[derive(Clone, Copy, Deref)]
90pub(crate) struct Bottom(u32);
91
92impl From<u32> for Bottom {
93 fn from(value: u32) -> Self {
94 debug_assert!((1..=0x3f_ffff).contains(&value));
95 Self(value)
96 }
97}
98
99#[test]
100fn truncated_time_delta_secs() {
101 let check = |a, b, expected| {
102 assert_eq!(
103 TruncatedTime(a).abs_delta(TruncatedTime(b)),
104 expected,
105 "{a} abs {b} != {expected}",
106 );
107 };
108
109 check(5, 5, 0);
110 check(25, 50, 25);
111 check((1 << 25) - 15, (1 << 25) - 5, 10);
112 check(0, (1 << 25) - 5, 5);
113 check(1, (1 << 25) - 5, 6);
114}
115
116#[test]
117fn layout_roundtrip() {
118 fn check(raw_trace_id: u64) {
119 let trace_id = TraceId::try_from(raw_trace_id).unwrap();
120 let layout = trace_id.to_layout();
121 assert_eq!(TraceId::from_layout(layout), trace_id);
122 }
123
124 check(1);
125 check(9223372036854775807);
126
127 check(36203675);
129 check(75997165362483795);
130 check(276561162443934569);
131 check(2765611624439345695);
132 check(5197794958151101819);
133 check(8446744073709551614);
134}