Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion libdd-trace-utils/src/msgpack_decoder/decode/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use crate::msgpack_decoder::decode::error::DecodeError;
use crate::span::DeserializableTraceData;
use crate::span::{DeserializableTraceData, DeserializableTraceDataLt};
use rmp::decode;
use rmp::decode::DecodeStringError;

Expand Down Expand Up @@ -72,6 +72,17 @@ impl<T: DeserializableTraceData> Buffer<T> {
}
}

/// Prototype: additive counterpart to `as_mut_slice`, using [`DeserializableTraceDataLt`]'s
/// honest lifetime `'x` instead of an erased `'static`. Kept in a separate `impl` block so the
/// original struct definition and `impl<T: DeserializableTraceData> Buffer<T>` above — and every
/// existing caller of `as_mut_slice` — are untouched.
impl<'x, T: DeserializableTraceDataLt<'x>> Buffer<T> {
/// Returns a mutable reference to the underlying slice, with its genuine lifetime `'x`.
pub fn as_mut_slice_lt(&mut self) -> &mut &'x [u8] {
T::get_mut_slice_lt(&mut self.0)
}
}

impl<T: DeserializableTraceData> Deref for Buffer<T> {
type Target = [u8];

Expand Down
31 changes: 16 additions & 15 deletions libdd-trace-utils/src/msgpack_decoder/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub(super) mod span;
use crate::msgpack_decoder::decode::buffer::Buffer;
use crate::msgpack_decoder::decode::error::DecodeError;
use crate::span::v1::{TraceChunk, TracerPayload, TracerPayloadBytes, TracerPayloadSlice};
use crate::span::DeserializableTraceData;
use crate::span::{DeserializableTraceData, DeserializableTraceDataLt};
use rmp::decode;
use std::borrow::Borrow;

Expand Down Expand Up @@ -196,7 +196,7 @@ pub fn from_slice(data: &[u8]) -> Result<(TracerPayloadSlice<'_>, usize), Decode
}

/// Generic over the deserialization mode (owned `BytesData` or borrowed `SliceData`).
pub fn from_buffer<T: DeserializableTraceData>(
pub fn from_buffer<'x, T: DeserializableTraceDataLt<'x>>(
data: &mut Buffer<T>,
) -> Result<(TracerPayload<T>, usize), DecodeError>
where
Expand All @@ -216,7 +216,7 @@ where
/// Any inline string encountered while skipping (at any nesting depth) is interned into `table`,
/// same as a recognized field would: skipping a value must not desync later back-references to
/// strings that happen to also appear inside it.
pub(super) fn skip_unknown_value<T: DeserializableTraceData>(
pub(super) fn skip_unknown_value<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<(), DecodeError>
Expand All @@ -225,10 +225,11 @@ where
{
// Snapshot the buffer's owning handle *before* advancing past the skipped value: any string
// found inside it will be a substring of this exact allocation, so this is what
// `T::intern_skipped_str` must derive ownership from. Cloning is cheap (a refcount bump for
// `T::Bytes = Bytes`), unaffected by the lied `'static` lifetime `as_mut_slice` exposes.
// `T::intern_skipped_str_lt` must derive ownership from. Cloning is cheap (a refcount bump
// for `T::Bytes = Bytes`) and, for `SliceData`, `value`'s `'x` below is the buffer's real
// lifetime rather than a lie, so `owner` only matters for the `BytesData` case.
let owner = buf.bytes().clone();
let value = rmpv::decode::read_value_ref(buf.as_mut_slice())
let value = rmpv::decode::read_value_ref(buf.as_mut_slice_lt())
.map_err(|_| DecodeError::InvalidFormat("Failed to skip unknown V1 value".to_owned()))?;
record_strings_in_value_ref::<T>(&value, &owner, table);
Ok(())
Expand All @@ -239,11 +240,11 @@ where
/// [`read_interned_string`]'s own encoder-side counterpart, so they can't be the target of a
/// later back-reference either.
///
/// `owner` must be a snapshot of the buffer taken before it was advanced past `value`: the
/// strings inside `value` report a lied `'static` lifetime (see `Buffer::as_mut_slice`) but
/// really borrow from `owner`'s memory.
fn record_strings_in_value_ref<T: DeserializableTraceData>(
value: &rmpv::ValueRef<'static>,
/// `owner` must be a snapshot of the buffer taken before it was advanced past `value`. Unlike the
/// `'static`-erased version, `value`'s lifetime `'x` here is genuine: for `SliceData<'a>`, `'x` is
/// `'a` itself, not a lie.
fn record_strings_in_value_ref<'x, T: DeserializableTraceDataLt<'x>>(
value: &rmpv::ValueRef<'x>,
owner: &T::Bytes,
table: &mut StringTable<T>,
) where
Expand All @@ -252,7 +253,7 @@ fn record_strings_in_value_ref<T: DeserializableTraceData>(
match value {
rmpv::ValueRef::String(s) => {
if let Some(s) = (*s).into_str() {
table.record(&T::intern_skipped_str(owner, s));
table.record(&T::intern_skipped_str_lt(owner, s));
}
}
rmpv::ValueRef::Array(items) => {
Expand All @@ -271,7 +272,7 @@ fn record_strings_in_value_ref<T: DeserializableTraceData>(
}

/// Decodes the top-level V1 payload map: tracer metadata fields + chunks array.
fn decode_payload<T: DeserializableTraceData>(
fn decode_payload<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<TracerPayload<T>, DecodeError>
Expand Down Expand Up @@ -319,7 +320,7 @@ where
Ok(payload)
}

fn decode_chunks<T: DeserializableTraceData>(
fn decode_chunks<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<Vec<TraceChunk<T>>, DecodeError>
Expand All @@ -335,7 +336,7 @@ where
Ok(chunks)
}

fn decode_chunk<T: DeserializableTraceData>(
fn decode_chunk<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<TraceChunk<T>, DecodeError>
Expand Down
16 changes: 8 additions & 8 deletions libdd-trace-utils/src/msgpack_decoder/v1/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ use crate::msgpack_decoder::decode::buffer::Buffer;
use crate::msgpack_decoder::decode::error::DecodeError;
use crate::span::v1::{AttributeValue, Span, SpanEvent, SpanKind, SpanLink, ThinVec};
use crate::span::vec_map::VecMap;
use crate::span::DeserializableTraceData;
use crate::span::{DeserializableTraceData, DeserializableTraceDataLt};
use rmp::decode;
use std::borrow::Borrow;

/// Decodes a V1 span (msgpack map with integer keys) into a [`Span<T>`].
///
/// The streaming `StringTable` is shared across the whole payload, so interned references in
/// this span can resolve to strings that appeared in an earlier chunk or payload header.
pub(super) fn decode_span<T: DeserializableTraceData>(
pub(super) fn decode_span<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<Span<T>, DecodeError>
Expand Down Expand Up @@ -111,7 +111,7 @@ where
}

/// Reads a V1 attributes map encoded as a flat array of `[key, type_uint8, value, ...]` triplets.
pub(super) fn read_attributes_map<T: DeserializableTraceData>(
pub(super) fn read_attributes_map<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<VecMap<T::Text, AttributeValue<T>>, DecodeError>
Expand Down Expand Up @@ -144,7 +144,7 @@ where

/// Reads `[type_uint8, value]` and dispatches by type discriminant. Recurses into `Array` and
/// `KeyValueList`.
pub(super) fn read_typed_attribute_value<T: DeserializableTraceData>(
pub(super) fn read_typed_attribute_value<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<AttributeValue<T>, DecodeError>
Expand Down Expand Up @@ -214,7 +214,7 @@ fn read_bin<T: DeserializableTraceData>(buf: &mut Buffer<T>) -> Result<T::Bytes,
}

/// Reads the span_links array. The `SpanLinks` map key has already been consumed by the caller.
pub(super) fn read_span_links<T: DeserializableTraceData>(
pub(super) fn read_span_links<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<ThinVec<SpanLink<T>>, DecodeError>
Expand All @@ -230,7 +230,7 @@ where
Ok(links)
}

fn decode_span_link<T: DeserializableTraceData>(
fn decode_span_link<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<SpanLink<T>, DecodeError>
Expand Down Expand Up @@ -282,7 +282,7 @@ where
Ok(link)
}

pub(super) fn read_span_events<T: DeserializableTraceData>(
pub(super) fn read_span_events<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<ThinVec<SpanEvent<T>>, DecodeError>
Expand All @@ -298,7 +298,7 @@ where
Ok(events)
}

fn decode_span_event<T: DeserializableTraceData>(
fn decode_span_event<'x, T: DeserializableTraceDataLt<'x>>(
buf: &mut Buffer<T>,
table: &mut StringTable<T>,
) -> Result<SpanEvent<T>, DecodeError>
Expand Down
47 changes: 47 additions & 0 deletions libdd-trace-utils/src/span/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,53 @@ impl<'a> DeserializableTraceData for SliceData<'a> {
}
}

/// Prototype: an honest-lifetime counterpart to [`DeserializableTraceData`].
///
/// `get_mut_slice`/`intern_skipped_str` report `'static` for every implementor, even though for
/// `BytesData` the backing `Bytes` is heap-owned and never truly `'static` — the lifetime is
/// erased via `transmute` and "laundered" by immediately deriving an owned `Self::Text` before
/// it can escape into safe code. For `SliceData<'a>` this erasure is pure overhead: `Self::Bytes`
/// is already `&'a [u8]`, the real target type, so the transmute exists only to satisfy a trait
/// signature that can't express `'a` generically.
///
/// This trait carries the genuine lifetime `'x` explicitly, so `SliceData<'a>` can implement it
/// with no unsafe code at all. It extends `DeserializableTraceData` rather than replacing it, and
/// `Buffer<T>` gains an additive method (`as_mut_slice_lt`) in a new `impl` block — every existing
/// call site on `DeserializableTraceData`/`Buffer::as_mut_slice` is untouched.
pub trait DeserializableTraceDataLt<'x>: DeserializableTraceData {
fn get_mut_slice_lt(buf: &mut Self::Bytes) -> &mut &'x [u8];

/// Same contract as [`DeserializableTraceData::intern_skipped_str`], but `s` carries the
/// real lifetime `'x` instead of an erased `'static`.
fn intern_skipped_str_lt(owner: &Self::Bytes, s: &'x str) -> Self::Text;
}

impl DeserializableTraceDataLt<'static> for BytesData {
#[inline]
fn get_mut_slice_lt(buf: &mut Bytes) -> &mut &'static [u8] {
Self::get_mut_slice(buf)
}

#[inline]
fn intern_skipped_str_lt(owner: &Bytes, s: &'static str) -> BytesString {
Self::intern_skipped_str(owner, s)
}
}

impl<'a> DeserializableTraceDataLt<'a> for SliceData<'a> {
#[inline]
fn get_mut_slice_lt<'b>(buf: &'b mut Self::Bytes) -> &'b mut &'a [u8] {
// No transmute needed: `Self::Bytes` is already `&'a [u8]`, the exact type this method
// claims to return — the `'static` version above only needed unsafe code to lie about it.
buf
}

#[inline]
fn intern_skipped_str_lt(_owner: &&'a [u8], s: &'a str) -> Cow<'a, str> {
Cow::Borrowed(s)
}
}

#[derive(Debug)]
pub struct SpanKeyParseError {
pub message: String,
Expand Down
Loading