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