Skip to main content

libdd_trace_utils/span/
mod.rs

1// Copyright 2023-Present Datadog, Inc. https://www.datadoghq.com/
2// SPDX-License-Identifier: Apache-2.0
3
4pub mod trace_utils;
5pub mod v04;
6pub mod v05;
7pub mod v1;
8pub mod vec_map;
9
10use crate::msgpack_decoder::decode::buffer::read_string_ref_nomut;
11use crate::msgpack_decoder::decode::error::DecodeError;
12use crate::span::v05::dict::SharedDict;
13use libdd_tinybytes::{Bytes, BytesString};
14use serde::Serialize;
15use std::borrow::Borrow;
16use std::fmt::Debug;
17use std::hash::Hash;
18use std::marker::PhantomData;
19use std::ptr::NonNull;
20use std::{fmt, ptr};
21
22/// A `SpanLink`'s `flags` field reserves bit 31 to mean "a value was explicitly set", separate
23/// from the sampling decision carried in the low bits. The sentinel bit distinguishes
24/// `flags == 0` (never set) from an explicit decision of `0`, for example a dropped context.
25/// Without the sentinel, both cases look identical on the wire.
26///
27/// Every non-JSON wire format keeps this bit raw, except OTLP. The native v0.4 msgpack format
28/// (`msgpack_encoder::v04::span_v04`), the v1 msgpack format, and the native protobuf format
29/// (`libdd_trace_protobuf::pb::SpanLink`) all keep the sentinel raw in `flags`. Tracers already
30/// send the bit set in these formats. JSON formats and OTLP protobuf must mask this bit before
31/// they emit `flags`, because those consumers treat `flags` as the real W3C trace-flags value.
32/// The JSON formats are the v0.5 `_dd.span_links` dictionary, agentless JSON, and structured
33/// JSON logging.
34pub(crate) const SPAN_LINK_FLAGS_SET_SENTINEL: u32 = 1 << 31;
35
36/// Trait representing the requirements for a type to be used as a Span "string" type.
37/// Note: Borrow<str> is not required by the derived traits, but allows to access HashMap elements
38/// from a static str and check if the string is empty.
39pub trait SpanText: Debug + Eq + Hash + Borrow<str> + Serialize + Default {
40    fn from_static_str(value: &'static str) -> Self;
41
42    /// Copies this text into an owned [`BytesString`].
43    ///
44    /// Used by the v0.5 conversion, whose shared dictionary always owns its strings so it
45    /// can hold both interned span text and dynamically-built JSON (span links / events).
46    /// The default copies the bytes; owned text types (e.g. `BytesString`) should override
47    /// with a cheaper reference-counted clone.
48    fn to_bytes_string(&self) -> BytesString {
49        BytesString::from(<Self as Borrow<str>>::borrow(self).to_string())
50    }
51}
52
53impl SpanText for &str {
54    fn from_static_str(value: &'static str) -> Self {
55        value
56    }
57}
58
59impl SpanText for BytesString {
60    fn from_static_str(value: &'static str) -> Self {
61        BytesString::from_static(value)
62    }
63
64    fn to_bytes_string(&self) -> BytesString {
65        self.clone()
66    }
67}
68
69pub trait SpanBytes: Debug + Eq + Hash + Borrow<[u8]> + Serialize + Default {
70    fn from_static_bytes(value: &'static [u8]) -> Self;
71}
72
73impl SpanBytes for &[u8] {
74    fn from_static_bytes(value: &'static [u8]) -> Self {
75        value
76    }
77}
78
79impl SpanBytes for Bytes {
80    fn from_static_bytes(value: &'static [u8]) -> Self {
81        Bytes::from_static(value)
82    }
83}
84
85/// Trait representing a tuple of (Text, Bytes) types used for different underlying data structures.
86/// Note: The functions are internal to the msgpack decoder and should not be used directly: they're
87/// only exposed here due to the unavailability of min_specialization in stable Rust.
88/// Also note that the Clone and PartialEq bounds are only present for tests.
89pub trait TraceData: Default + Clone + Debug + PartialEq {
90    type Text: SpanText;
91    type Bytes: SpanBytes;
92}
93
94pub trait DeserializableTraceData: TraceData {
95    fn get_mut_slice(buf: &mut Self::Bytes) -> &mut &'static [u8];
96
97    fn try_slice_and_advance(buf: &mut Self::Bytes, bytes: usize) -> Option<Self::Bytes>;
98
99    fn read_string(buf: &mut Self::Bytes) -> Result<Self::Text, DecodeError>;
100}
101
102/// TraceData implementation using `Bytes` and `BytesString`.
103#[derive(Clone, Default, Debug, PartialEq, Serialize)]
104pub struct BytesData;
105impl TraceData for BytesData {
106    type Text = BytesString;
107    type Bytes = Bytes;
108}
109
110impl DeserializableTraceData for BytesData {
111    #[inline]
112    fn get_mut_slice(buf: &mut Bytes) -> &mut &'static [u8] {
113        // SAFETY: Bytes has the same layout
114        unsafe { std::mem::transmute::<&mut Bytes, &mut &[u8]>(buf) }
115    }
116
117    #[inline]
118    fn try_slice_and_advance(buf: &mut Bytes, bytes: usize) -> Option<Bytes> {
119        if bytes > buf.len() {
120            return None;
121        }
122        let data = buf.slice_ref(&buf[0..bytes])?;
123        unsafe {
124            // SAFETY: forwarding the buffer requires that buf is borrowed from static.
125            let (ptr, len, underlying) = ptr::read(buf).into_raw();
126            ptr::write(
127                buf,
128                Bytes::from_raw(ptr.add(bytes), len - bytes, underlying),
129            );
130        }
131        Some(data)
132    }
133
134    #[inline]
135    fn read_string(buf: &mut Bytes) -> Result<BytesString, DecodeError> {
136        // Note: we need to pass a &'static lifetime here, otherwise it'll complain
137        let (str, newbuf) = read_string_ref_nomut(buf.as_ref())?;
138        let string = BytesString::from_bytes_slice(buf, str);
139        unsafe {
140            // SAFETY: forwarding the buffer requires that buf is borrowed from static.
141            let (_, _, underlying) = ptr::read(buf).into_raw();
142            let new = Bytes::from_raw(
143                NonNull::new_unchecked(newbuf.as_ptr() as *mut _),
144                newbuf.len(),
145                underlying,
146            );
147            ptr::write(buf, new);
148        }
149        Ok(string)
150    }
151}
152
153/// TraceData implementation using `&str` and `&[u8]`.
154#[derive(Clone, Default, Debug, PartialEq, Serialize)]
155pub struct SliceData<'a>(PhantomData<&'a u8>);
156impl<'a> TraceData for SliceData<'a> {
157    type Text = &'a str;
158    type Bytes = &'a [u8];
159}
160
161impl<'a> DeserializableTraceData for SliceData<'a> {
162    #[inline]
163    fn get_mut_slice<'b>(buf: &'b mut Self::Bytes) -> &'b mut &'static [u8] {
164        unsafe { std::mem::transmute::<&'b mut &[u8], &'b mut &'static [u8]>(buf) }
165    }
166
167    #[inline]
168    fn try_slice_and_advance(buf: &mut &'a [u8], bytes: usize) -> Option<&'a [u8]> {
169        let slice = buf.get(0..bytes)?;
170        *buf = &buf[bytes..];
171        Some(slice)
172    }
173
174    #[inline]
175    fn read_string(buf: &mut &'a [u8]) -> Result<&'a str, DecodeError> {
176        read_string_ref_nomut(buf).map(|(str, newbuf)| {
177            *buf = newbuf;
178            str
179        })
180    }
181}
182
183#[derive(Debug)]
184pub struct SpanKeyParseError {
185    pub message: String,
186}
187
188impl SpanKeyParseError {
189    pub fn new(message: impl Into<String>) -> Self {
190        SpanKeyParseError {
191            message: message.into(),
192        }
193    }
194}
195impl fmt::Display for SpanKeyParseError {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        write!(f, "SpanKeyParseError: {}", self.message)
198    }
199}
200impl std::error::Error for SpanKeyParseError {}
201
202pub type SharedDictBytes = SharedDict<BytesString>;