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 {
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
103/// TraceData implementation using `Bytes` and `BytesString`.
104#[derive(Clone, Default, Debug, PartialEq, Serialize)]
105pub struct BytesData;
106impl TraceData for BytesData {
107    type Text = BytesString;
108    type Bytes = Bytes;
109}
110
111impl DeserializableTraceData for BytesData {
112    #[inline]
113    fn get_mut_slice(buf: &mut Bytes) -> &mut &'static [u8] {
114        // SAFETY: Bytes has the same layout
115        unsafe { std::mem::transmute::<&mut Bytes, &mut &[u8]>(buf) }
116    }
117
118    #[inline]
119    fn try_slice_and_advance(buf: &mut Bytes, bytes: usize) -> Option<Bytes> {
120        if bytes > buf.len() {
121            return None;
122        }
123        let data = buf.slice_ref(&buf[0..bytes])?;
124        unsafe {
125            // SAFETY: forwarding the buffer requires that buf is borrowed from static.
126            let (ptr, len, underlying) = ptr::read(buf).into_raw();
127            ptr::write(
128                buf,
129                Bytes::from_raw(ptr.add(bytes), len - bytes, underlying),
130            );
131        }
132        Some(data)
133    }
134
135    #[inline]
136    fn read_string(buf: &mut Bytes) -> Result<BytesString, DecodeError> {
137        // Note: we need to pass a &'static lifetime here, otherwise it'll complain
138        let (str, newbuf) = read_string_ref_nomut(buf.as_ref())?;
139        let string = BytesString::from_bytes_slice(buf, str);
140        unsafe {
141            // SAFETY: forwarding the buffer requires that buf is borrowed from static.
142            let (_, _, underlying) = ptr::read(buf).into_raw();
143            let new = Bytes::from_raw(
144                NonNull::new_unchecked(newbuf.as_ptr() as *mut _),
145                newbuf.len(),
146                underlying,
147            );
148            ptr::write(buf, new);
149        }
150        Ok(string)
151    }
152}
153
154/// TraceData implementation using `&str` and `&[u8]`.
155#[derive(Clone, Default, Debug, PartialEq, Serialize)]
156pub struct SliceData<'a>(PhantomData<&'a u8>);
157impl<'a> TraceData for SliceData<'a> {
158    type Text = &'a str;
159    type Bytes = &'a [u8];
160}
161
162impl<'a> DeserializableTraceData for SliceData<'a> {
163    #[inline]
164    fn get_mut_slice<'b>(buf: &'b mut Self::Bytes) -> &'b mut &'static [u8] {
165        unsafe { std::mem::transmute::<&'b mut &[u8], &'b mut &'static [u8]>(buf) }
166    }
167
168    #[inline]
169    fn try_slice_and_advance(buf: &mut &'a [u8], bytes: usize) -> Option<&'a [u8]> {
170        let slice = buf.get(0..bytes)?;
171        *buf = &buf[bytes..];
172        Some(slice)
173    }
174
175    #[inline]
176    fn read_string(buf: &mut &'a [u8]) -> Result<&'a str, DecodeError> {
177        read_string_ref_nomut(buf).map(|(str, newbuf)| {
178            *buf = newbuf;
179            str
180        })
181    }
182}
183
184#[derive(Debug)]
185pub struct SpanKeyParseError {
186    pub message: String,
187}
188
189impl SpanKeyParseError {
190    pub fn new(message: impl Into<String>) -> Self {
191        SpanKeyParseError {
192            message: message.into(),
193        }
194    }
195}
196impl fmt::Display for SpanKeyParseError {
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        write!(f, "SpanKeyParseError: {}", self.message)
199    }
200}
201impl std::error::Error for SpanKeyParseError {}
202
203pub type SharedDictBytes = SharedDict<BytesString>;