1use core::fmt;
17use core::num::NonZeroU64;
18use core::str::FromStr;
19
20#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
27pub struct ProcessId(u128);
28
29impl ProcessId {
30 pub fn random(rng: &mut impl rand::Rng) -> Self {
32 Self(rng.random())
33 }
34
35 pub const fn from_raw(raw: u128) -> Self {
41 Self(raw)
42 }
43
44 pub fn to_raw(self) -> u128 {
46 self.0
47 }
48}
49
50impl fmt::Display for ProcessId {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 write!(f, "{:032x}", self.0)
53 }
54}
55
56impl FromStr for ProcessId {
57 type Err = core::num::ParseIntError;
58
59 fn from_str(s: &str) -> Result<Self, Self::Err> {
60 u128::from_str_radix(s, 16).map(ProcessId)
61 }
62}
63
64impl serde::Serialize for ProcessId {
65 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
66 where
67 S: serde::Serializer,
68 {
69 let mut hex_bytes = [0u8; size_of::<u128>() * 2];
70 hex::encode_to_slice(self.0.to_le_bytes(), &mut hex_bytes).unwrap();
71
72 serializer.serialize_str(str::from_utf8(&hex_bytes).unwrap())
73 }
74}
75
76impl<'de> serde::Deserialize<'de> for ProcessId {
77 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
78 where
79 D: serde::Deserializer<'de>,
80 {
81 let bytes: [u8; size_of::<u128>()] = hex::serde::deserialize(deserializer)?;
82
83 Ok(ProcessId(u128::from_le_bytes(bytes)))
84 }
85}
86
87#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
95pub struct ThreadId {
96 pub process: ProcessId,
98
99 raw: NonZeroU64,
101}
102
103impl ThreadId {
104 pub const fn from_raw(process: ProcessId, raw: NonZeroU64) -> Self {
109 Self { process, raw }
110 }
111
112 pub fn raw(&self) -> NonZeroU64 {
114 self.raw
115 }
116}
117
118impl fmt::Display for ThreadId {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 let Self { process, raw } = self;
121 write!(f, "{process}:{raw:016x}")
122 }
123}
124
125#[derive(Clone, Debug)]
127pub enum ParseThreadIdError {
128 MissingSeparator,
130
131 InvalidProcessId(core::num::ParseIntError),
133
134 InvalidThreadId(core::num::ParseIntError),
136
137 ZeroThreadId,
139}
140
141impl fmt::Display for ParseThreadIdError {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 match self {
144 Self::MissingSeparator => f.write_str("missing ':' separator"),
145 Self::InvalidProcessId(_) => f.write_str("failed to parse process id"),
146 Self::InvalidThreadId(_) => f.write_str("failed to parse thread id"),
147 Self::ZeroThreadId => f.write_str("zero thread id"),
148 }
149 }
150}
151
152impl core::error::Error for ParseThreadIdError {
153 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
154 match self {
155 Self::MissingSeparator => None,
156 Self::InvalidProcessId(error) => Some(error),
157 Self::InvalidThreadId(error) => Some(error),
158 Self::ZeroThreadId => None,
159 }
160 }
161}
162
163impl FromStr for ThreadId {
164 type Err = ParseThreadIdError;
165
166 fn from_str(s: &str) -> Result<Self, Self::Err> {
167 let Some((process, thread)) = s.split_once(":") else {
168 return Err(ParseThreadIdError::MissingSeparator);
169 };
170 let process = ProcessId::from_str(process).map_err(ParseThreadIdError::InvalidProcessId)?;
171 let thread = NonZeroU64::new(
172 u64::from_str_radix(thread, 16).map_err(ParseThreadIdError::InvalidThreadId)?,
173 )
174 .ok_or(ParseThreadIdError::ZeroThreadId)?;
175 Ok(Self::from_raw(process, thread))
176 }
177}
178
179impl serde::Serialize for ThreadId {
180 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
181 where
182 S: serde::Serializer,
183 {
184 let mut bytes = [0u8; 49];
185
186 hex::encode_to_slice(self.process.to_raw().to_le_bytes(), &mut bytes[..32]).unwrap();
187 bytes[32] = b':';
188 hex::encode_to_slice(self.raw().get().to_le_bytes(), &mut bytes[33..]).unwrap();
189
190 serializer.serialize_str(str::from_utf8(&bytes).unwrap())
191 }
192}
193
194impl<'de> serde::Deserialize<'de> for ThreadId {
195 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
196 where
197 D: serde::Deserializer<'de>,
198 {
199 use serde::de::Error;
200
201 let string = <&str>::deserialize(deserializer)?;
202
203 if string.len() != 49 {
204 return Err(D::Error::invalid_length(
205 string.len(),
206 &"expected 49 byte string",
207 ));
208 }
209
210 let bytes = string.as_bytes();
211
212 if bytes[32] != b':' {
213 return Err(D::Error::invalid_value(
214 serde::de::Unexpected::Str(string),
215 &"expected : separator at byte 32",
216 ));
217 }
218
219 let mut process = [0; 16];
220 hex::decode_to_slice(&bytes[..32], &mut process).map_err(D::Error::custom)?;
221 let process = ProcessId::from_raw(u128::from_le_bytes(process));
222
223 let mut thread = [0; 8];
224 hex::decode_to_slice(&bytes[33..], &mut thread).map_err(D::Error::custom)?;
225 let thread = NonZeroU64::new(u64::from_le_bytes(thread))
226 .ok_or_else(|| D::Error::custom("zero thread id"))?;
227
228 Ok(Self::from_raw(process, thread))
229 }
230}
231
232#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
234pub struct SpanId(pub u64);
235
236#[cfg(feature = "enable")]
237impl SpanId {
238 #[inline]
239 #[doc(hidden)]
240 pub fn next_id() -> Self {
242 use core::sync::atomic;
243 static COUNTER: atomic::AtomicU64 = atomic::AtomicU64::new(1);
244 SpanId(COUNTER.fetch_add(1, atomic::Ordering::Relaxed))
245 }
246}
247
248impl fmt::Display for SpanId {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 write!(f, "{:016x}", self.0)
251 }
252}
253
254impl FromStr for SpanId {
255 type Err = core::num::ParseIntError;
256
257 fn from_str(s: &str) -> Result<Self, Self::Err> {
258 u64::from_str_radix(s, 16).map(SpanId)
259 }
260}
261
262impl serde::Serialize for SpanId {
263 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
264 where
265 S: serde::Serializer,
266 {
267 let mut hex_bytes = [0u8; size_of::<u64>() * 2];
268 hex::encode_to_slice(self.0.to_le_bytes(), &mut hex_bytes).unwrap();
269
270 serializer.serialize_str(str::from_utf8(&hex_bytes).unwrap())
271 }
272}
273
274impl<'de> serde::Deserialize<'de> for SpanId {
275 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
276 where
277 D: serde::Deserializer<'de>,
278 {
279 let bytes: [u8; size_of::<u64>()] = hex::serde::deserialize(deserializer)?;
280
281 Ok(SpanId(u64::from_le_bytes(bytes)))
282 }
283}
284
285#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
287pub struct SpanContext {
288 pub process_id: ProcessId,
290 pub span_id: SpanId,
292}
293
294impl SpanContext {
295 pub fn new(process_id: ProcessId, span_id: SpanId) -> Self {
305 Self {
306 process_id,
307 span_id,
308 }
309 }
310}
311
312impl fmt::Display for SpanContext {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 let Self {
315 process_id,
316 span_id,
317 } = self;
318 write!(f, "{process_id}:{span_id}")
319 }
320}
321
322#[derive(Clone, Debug)]
324pub enum ParseSpanContextError {
325 MissingSeparator,
327
328 InvalidProcessId(core::num::ParseIntError),
330
331 InvalidSpanId(core::num::ParseIntError),
333}
334
335impl fmt::Display for ParseSpanContextError {
336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337 match self {
338 Self::MissingSeparator => f.write_str("missing ':' separator"),
339 Self::InvalidProcessId(_) => f.write_str("failed to parse process id"),
340 Self::InvalidSpanId(_) => f.write_str("failed to parse span id"),
341 }
342 }
343}
344
345impl core::error::Error for ParseSpanContextError {
346 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
347 match self {
348 Self::MissingSeparator => None,
349 Self::InvalidProcessId(error) => Some(error),
350 Self::InvalidSpanId(error) => Some(error),
351 }
352 }
353}
354
355impl FromStr for SpanContext {
356 type Err = ParseSpanContextError;
357
358 fn from_str(s: &str) -> Result<Self, Self::Err> {
359 let Some((process_id, span_id)) = s.split_once(":") else {
360 return Err(ParseSpanContextError::MissingSeparator);
361 };
362 let process_id =
363 ProcessId::from_str(process_id).map_err(ParseSpanContextError::InvalidProcessId)?;
364 let span_id = SpanId::from_str(span_id).map_err(ParseSpanContextError::InvalidSpanId)?;
365 Ok(Self {
366 process_id,
367 span_id,
368 })
369 }
370}
371
372impl serde::Serialize for SpanContext {
373 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
374 where
375 S: serde::Serializer,
376 {
377 let mut bytes = [0u8; 49];
378
379 hex::encode_to_slice(self.process_id.to_raw().to_le_bytes(), &mut bytes[..32]).unwrap();
380 bytes[32] = b':';
381 hex::encode_to_slice(self.span_id.0.to_le_bytes(), &mut bytes[33..]).unwrap();
382
383 serializer.serialize_str(str::from_utf8(&bytes).unwrap())
384 }
385}
386
387impl<'de> serde::Deserialize<'de> for SpanContext {
388 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
389 where
390 D: serde::Deserializer<'de>,
391 {
392 use serde::de::Error;
393
394 let string = <&str>::deserialize(deserializer)?;
395
396 if string.len() != 49 {
397 return Err(D::Error::invalid_length(
398 string.len(),
399 &"expected 49 byte string",
400 ));
401 }
402
403 let bytes = string.as_bytes();
404
405 if bytes[32] != b':' {
406 return Err(D::Error::invalid_value(
407 serde::de::Unexpected::Str(string),
408 &"expected : separator at byte 32",
409 ));
410 }
411
412 let mut process = [0; 16];
413 hex::decode_to_slice(&bytes[..32], &mut process).map_err(D::Error::custom)?;
414
415 let mut span = [0; 8];
416 hex::decode_to_slice(&bytes[33..], &mut span).map_err(D::Error::custom)?;
417
418 Ok(Self {
419 process_id: ProcessId::from_raw(u128::from_le_bytes(process)),
420 span_id: SpanId(u64::from_le_bytes(span)),
421 })
422 }
423}
424
425#[cfg(all(test, feature = "std"))]
426mod tests {
427 use std::collections::HashSet;
428 use std::format;
429 use std::vec::Vec;
430
431 use test_case::test_case;
432
433 use super::*;
434
435 #[test]
436 #[cfg(not(miri))] #[allow(clippy::needless_collect)]
438 fn unique_id() {
439 let handles = std::iter::repeat_with(|| {
440 std::thread::spawn(|| {
441 std::iter::repeat_with(SpanId::next_id)
442 .take(1000)
443 .collect::<Vec<_>>()
444 })
445 })
446 .take(32)
447 .collect::<Vec<_>>();
448
449 let k = handles
450 .into_iter()
451 .flat_map(|h| h.join().unwrap())
452 .collect::<HashSet<_>>();
453
454 assert_eq!(k.len(), 32 * 1000);
455 }
456
457 #[test]
458 fn span_id_next_id_produces_non_zero_values() {
459 let ids: Vec<SpanId> = (0..100).map(|_| SpanId::next_id()).collect();
460
461 for id in &ids {
462 assert_ne!(id.0, 0, "SpanId::next_id() should not produce zero values");
463 }
464
465 let mut unique_ids = HashSet::new();
466 for id in &ids {
467 assert!(
468 unique_ids.insert(id.0),
469 "SpanId::next_id() should produce unique values"
470 );
471 }
472 }
473
474 #[test_case(SpanId(0), r#""0000000000000000""#, "0000000000000000")]
475 #[test_case(SpanId(1), r#""0100000000000000""#, "0000000000000001")]
476 #[test_case(SpanId(0x123), r#""2301000000000000""#, "0000000000000123")]
477 #[test_case(SpanId(u64::MAX), r#""ffffffffffffffff""#, "ffffffffffffffff")]
478 #[test_case(
479 SpanId(0xFEDCBA9876543210),
480 r#""1032547698badcfe""#,
481 "fedcba9876543210"
482 )]
483 #[test_case(
484 ProcessId::from_raw(0),
485 r#""00000000000000000000000000000000""#,
486 "00000000000000000000000000000000"
487 )]
488 #[test_case(
489 ProcessId::from_raw(1),
490 r#""01000000000000000000000000000000""#,
491 "00000000000000000000000000000001"
492 )]
493 #[test_case(
494 ProcessId::from_raw(0x123),
495 r#""23010000000000000000000000000000""#,
496 "00000000000000000000000000000123"
497 )]
498 #[test_case(
499 ProcessId::from_raw(0x123456789ABCDEF0FEDCBA9876543210),
500 r#""1032547698badcfef0debc9a78563412""#,
501 "123456789abcdef0fedcba9876543210"
502 )]
503 #[test_case(
504 ProcessId::from_raw(u128::MAX),
505 r#""ffffffffffffffffffffffffffffffff""#,
506 "ffffffffffffffffffffffffffffffff"
507 )]
508 #[test_case(
509 ThreadId::from_raw(ProcessId::from_raw(0), NonZeroU64::new(1).unwrap()),
510 r#""00000000000000000000000000000000:0100000000000000""#,
511 "00000000000000000000000000000000:0000000000000001"
512 )]
513 #[test_case(
514 ThreadId::from_raw(ProcessId::from_raw(0x123), NonZeroU64::new(0x456).unwrap()),
515 r#""23010000000000000000000000000000:5604000000000000""#,
516 "00000000000000000000000000000123:0000000000000456"
517 )]
518 #[test_case(
519 ThreadId::from_raw(ProcessId::from_raw(u128::MAX), NonZeroU64::new(u64::MAX).unwrap()),
520 r#""ffffffffffffffffffffffffffffffff:ffffffffffffffff""#,
521 "ffffffffffffffffffffffffffffffff:ffffffffffffffff"
522 )]
523 #[test_case(
524 ThreadId::from_raw(
525 ProcessId::from_raw(0x123456789ABCDEF0FEDCBA9876543210),
526 NonZeroU64::new(0xFEDCBA9876543210).unwrap(),
527 ),
528 r#""1032547698badcfef0debc9a78563412:1032547698badcfe""#,
529 "123456789abcdef0fedcba9876543210:fedcba9876543210"
530 )]
531 #[test_case(
532 SpanContext::new(ProcessId::from_raw(0), SpanId(0)),
533 r#""00000000000000000000000000000000:0000000000000000""#,
534 "00000000000000000000000000000000:0000000000000000"
535 )]
536 #[test_case(
537 SpanContext::new(ProcessId::from_raw(0x123), SpanId(0x456)),
538 r#""23010000000000000000000000000000:5604000000000000""#,
539 "00000000000000000000000000000123:0000000000000456"
540 )]
541 #[test_case(
542 SpanContext::new(ProcessId::from_raw(u128::MAX), SpanId(u64::MAX)),
543 r#""ffffffffffffffffffffffffffffffff:ffffffffffffffff""#,
544 "ffffffffffffffffffffffffffffffff:ffffffffffffffff"
545 )]
546 #[test_case(
547 SpanContext::new(
548 ProcessId::from_raw(0x123456789ABCDEF0FEDCBA9876543210),
549 SpanId(0xFEDCBA9876543210)
550 ),
551 r#""1032547698badcfef0debc9a78563412:1032547698badcfe""#,
552 "123456789abcdef0fedcba9876543210:fedcba9876543210"
553 )]
554 fn serialization<T>(value: T, expected_json: &str, expected_display: &str)
555 where
556 T: fmt::Display
557 + serde::Serialize
558 + FromStr<Err: fmt::Debug>
559 + serde::de::DeserializeOwned
560 + fmt::Debug
561 + Eq,
562 {
563 assert_eq!(serde_json::to_string(&value).unwrap(), expected_json);
564 assert_eq!(value, serde_json::from_str::<T>(expected_json).unwrap());
565
566 assert_eq!(format!("{value}"), expected_display);
567 assert_eq!(value, T::from_str(expected_display).unwrap());
568 }
569
570 #[test_case("")]
571 #[test_case("xyz")]
572 fn span_id_from_str_error(input: &str) {
573 assert!(SpanId::from_str(input).is_err());
574 }
575
576 #[test_case("")]
577 #[test_case("ffffffffffffffffffffffffffffffff0")]
578 #[test_case("xyz")]
579 fn process_id_from_str_error(input: &str) {
580 assert!(ProcessId::from_str(input).is_err());
581 }
582
583 #[test_case("")]
584 #[test_case("00000000000000000000000000000000")]
585 #[test_case("00000000000000000000000000000000:0000000000000000")]
586 #[test_case("00000000000000000000000000000000:xyz")]
587 #[test_case("00000000000000000000000000000001:")]
588 #[test_case(":0000000000000001")]
589 #[test_case("xyz")]
590 #[test_case("xyz:0000000000000001")]
591 fn thread_id_from_str_error(input: &str) {
592 assert!(ThreadId::from_str(input).is_err());
593 }
594
595 #[test_case("")]
596 #[test_case("00000000000000000000000000000000")]
597 #[test_case("00000000000000000000000000000000:xyz")]
598 #[test_case("00000000000000000000000000000001:")]
599 #[test_case(":0000000000000000")]
600 #[test_case("xyz")]
601 #[test_case("xyz:0000000000000000")]
602 fn span_context_from_str_error(input: &str) {
603 assert!(SpanContext::from_str(input).is_err());
604 }
605}