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;
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
54impl SpanText for &str {
55    fn from_static_str(value: &'static str) -> Self {
56        value
57    }
58}
59
60impl SpanText for BytesString {
61    fn from_static_str(value: &'static str) -> Self {
62        BytesString::from_static(value)
63    }
64
65    fn to_bytes_string(&self) -> BytesString {
66        self.clone()
67    }
68}
69
70pub trait SpanBytes: Debug + Eq + Hash + Borrow<[u8]> + Serialize + Default + Clone {
71    fn from_static_bytes(value: &'static [u8]) -> Self;
72}
73
74impl SpanBytes for &[u8] {
75    fn from_static_bytes(value: &'static [u8]) -> Self {
76        value
77    }
78}
79
80impl SpanBytes for Bytes {
81    fn from_static_bytes(value: &'static [u8]) -> Self {
82        Bytes::from_static(value)
83    }
84}
85
86/// Trait representing a tuple of (Text, Bytes) types used for different underlying data structures.
87/// Note: The functions are internal to the msgpack decoder and should not be used directly: they're
88/// only exposed here due to the unavailability of min_specialization in stable Rust.
89/// Also note that the Clone and PartialEq bounds are only present for tests.
90pub trait TraceData: Default + Clone + Debug + PartialEq {
91    type Text: SpanText;
92    type Bytes: SpanBytes;
93}
94
95pub trait DeserializableTraceData: TraceData {
96    fn get_mut_slice(buf: &mut Self::Bytes) -> &mut &'static [u8];
97
98    fn try_slice_and_advance(buf: &mut Self::Bytes, bytes: usize) -> Option<Self::Bytes>;
99
100    fn read_string(buf: &mut Self::Bytes) -> Result<Self::Text, DecodeError>;
101
102    /// Interns a string found while walking a value through `get_mut_slice`'s lied `'static`
103    /// view (e.g. skipping an unrecognized V1 field for forward compatibility). `s` really
104    /// borrows from `owner`'s memory, not `'static`: implementations must derive `Self::Text`
105    /// from `owner` itself rather than trusting that lifetime, so a refcounted backing
106    /// allocation isn't freed out from under the interned string.
107    fn intern_skipped_str(owner: &Self::Bytes, s: &'static str) -> Self::Text;
108}
109
110/// TraceData implementation using `Bytes` and `BytesString`.
111#[derive(Clone, Default, Debug, PartialEq, Serialize)]
112pub struct BytesData;
113impl TraceData for BytesData {
114    type Text = BytesString;
115    type Bytes = Bytes;
116}
117
118impl DeserializableTraceData for BytesData {
119    #[inline]
120    fn get_mut_slice(buf: &mut Bytes) -> &mut &'static [u8] {
121        // SAFETY: Bytes has the same layout
122        unsafe { std::mem::transmute::<&mut Bytes, &mut &[u8]>(buf) }
123    }
124
125    #[inline]
126    fn try_slice_and_advance(buf: &mut Bytes, bytes: usize) -> Option<Bytes> {
127        if bytes > buf.len() {
128            return None;
129        }
130        let data = buf.slice_ref(&buf[0..bytes])?;
131        unsafe {
132            // SAFETY: forwarding the buffer requires that buf is borrowed from static.
133            let (ptr, len, underlying) = ptr::read(buf).into_raw();
134            ptr::write(
135                buf,
136                Bytes::from_raw(ptr.add(bytes), len - bytes, underlying),
137            );
138        }
139        Some(data)
140    }
141
142    #[inline]
143    fn read_string(buf: &mut Bytes) -> Result<BytesString, DecodeError> {
144        // Note: we need to pass a &'static lifetime here, otherwise it'll complain
145        let (str, newbuf) = read_string_ref_nomut(buf.as_ref())?;
146        let string = BytesString::from_bytes_slice(buf, str);
147        unsafe {
148            // SAFETY: forwarding the buffer requires that buf is borrowed from static.
149            let (_, _, underlying) = ptr::read(buf).into_raw();
150            let new = Bytes::from_raw(
151                NonNull::new_unchecked(newbuf.as_ptr() as *mut _),
152                newbuf.len(),
153                underlying,
154            );
155            ptr::write(buf, new);
156        }
157        Ok(string)
158    }
159
160    #[inline]
161    fn intern_skipped_str(owner: &Bytes, s: &'static str) -> BytesString {
162        BytesString::from_bytes_slice(owner, s)
163    }
164}
165
166/// TraceData implementation using `&str` and `&[u8]`.
167#[derive(Clone, Default, Debug, PartialEq, Serialize)]
168pub struct SliceData<'a>(PhantomData<&'a u8>);
169impl<'a> TraceData for SliceData<'a> {
170    type Text = &'a str;
171    type Bytes = &'a [u8];
172}
173
174impl<'a> DeserializableTraceData for SliceData<'a> {
175    #[inline]
176    fn get_mut_slice<'b>(buf: &'b mut Self::Bytes) -> &'b mut &'static [u8] {
177        unsafe { std::mem::transmute::<&'b mut &[u8], &'b mut &'static [u8]>(buf) }
178    }
179
180    #[inline]
181    fn try_slice_and_advance(buf: &mut &'a [u8], bytes: usize) -> Option<&'a [u8]> {
182        let slice = buf.get(0..bytes)?;
183        *buf = &buf[bytes..];
184        Some(slice)
185    }
186
187    #[inline]
188    fn read_string(buf: &mut &'a [u8]) -> Result<&'a str, DecodeError> {
189        read_string_ref_nomut(buf).map(|(str, newbuf)| {
190            *buf = newbuf;
191            str
192        })
193    }
194
195    #[inline]
196    fn intern_skipped_str(_owner: &&'a [u8], s: &'static str) -> &'a str {
197        // No refcounted allocation to preserve here: `s` borrows from a plain slice the
198        // caller owns for `'a`, and a `'static` reference is always a valid `'a` reference.
199        s
200    }
201}
202
203#[derive(Debug)]
204pub struct SpanKeyParseError {
205    pub message: String,
206}
207
208impl SpanKeyParseError {
209    pub fn new(message: impl Into<String>) -> Self {
210        SpanKeyParseError {
211            message: message.into(),
212        }
213    }
214}
215impl fmt::Display for SpanKeyParseError {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        write!(f, "SpanKeyParseError: {}", self.message)
218    }
219}
220impl std::error::Error for SpanKeyParseError {}
221
222pub type SharedDictBytes = SharedDict<BytesString>;