1pub(super) mod span;
5
6use crate::msgpack_decoder::decode::buffer::Buffer;
7use crate::msgpack_decoder::decode::error::DecodeError;
8use crate::span::v1::{TraceChunk, TracerPayload, TracerPayloadBytes, TracerPayloadSlice};
9use crate::span::DeserializableTraceData;
10use rmp::decode;
11use std::borrow::Borrow;
12
13pub(super) mod trace_key {
17 pub const CONTAINER_ID: u8 = 2;
18 pub const LANGUAGE_NAME: u8 = 3;
19 pub const LANGUAGE_VERSION: u8 = 4;
20 pub const TRACER_VERSION: u8 = 5;
21 pub const RUNTIME_ID: u8 = 6;
22 pub const ENV_REF: u8 = 7;
23 pub const HOSTNAME_REF: u8 = 8;
24 pub const APP_VERSION_REF: u8 = 9;
25 pub const ATTRIBUTES: u8 = 10;
26 pub const CHUNKS: u8 = 11;
27}
28
29pub(super) mod chunk_key {
30 pub const PRIORITY: u8 = 1;
31 pub const ORIGIN: u8 = 2;
32 pub const ATTRIBUTES: u8 = 3;
33 pub const SPANS: u8 = 4;
34 pub const DROPPED_TRACE: u8 = 5;
35 pub const TRACE_ID: u8 = 6;
36 pub const SAMPLING_MECHANISM: u8 = 7;
37}
38
39pub(super) mod span_key {
40 pub const SERVICE: u8 = 1;
41 pub const NAME: u8 = 2;
42 pub const RESOURCE: u8 = 3;
43 pub const SPAN_ID: u8 = 4;
44 pub const PARENT_ID: u8 = 5;
45 pub const START: u8 = 6;
46 pub const DURATION: u8 = 7;
47 pub const ERROR: u8 = 8;
48 pub const ATTRIBUTES: u8 = 9;
49 pub const TYPE: u8 = 10;
50 pub const SPAN_LINKS: u8 = 11;
51 pub const SPAN_EVENTS: u8 = 12;
52 pub const ENV: u8 = 13;
53 pub const VERSION: u8 = 14;
54 pub const COMPONENT: u8 = 15;
55 pub const KIND: u8 = 16;
56}
57
58pub(super) mod span_link_key {
59 pub const TRACE_ID: u8 = 1;
60 pub const SPAN_ID: u8 = 2;
61 pub const ATTRIBUTES: u8 = 3;
62 pub const TRACE_STATE: u8 = 4;
63 pub const FLAGS: u8 = 5;
64}
65
66pub(super) mod span_event_key {
67 pub const TIME: u8 = 1;
68 pub const NAME: u8 = 2;
69 pub const ATTRIBUTES: u8 = 3;
70}
71
72pub(super) const ANY_VALUE_KEY_STRING: u8 = 1;
73pub(super) const ANY_VALUE_KEY_BOOL: u8 = 2;
74pub(super) const ANY_VALUE_KEY_DOUBLE: u8 = 3;
75pub(super) const ANY_VALUE_KEY_INT64: u8 = 4;
76pub(super) const ANY_VALUE_KEY_BYTES: u8 = 5;
77pub(super) const ANY_VALUE_KEY_ARRAY: u8 = 6;
78pub(super) const ANY_VALUE_KEY_KEY_VALUE_LIST: u8 = 7;
79
80pub(super) const TYPED_VALUE_STRIDE: u32 = 2;
82
83pub(super) const FLAT_ATTR_STRIDE: u32 = 3;
85
86pub(super) const TRACE_ID_LEN: u32 = 16;
88
89pub(super) struct StringTable<T: DeserializableTraceData>
95where
96 T::Text: Clone,
97{
98 seen: Vec<T::Text>,
99}
100
101impl<T: DeserializableTraceData> StringTable<T>
102where
103 T::Text: Clone,
104{
105 pub(super) fn new() -> Self {
106 Self {
107 seen: vec![T::Text::default()],
108 }
109 }
110
111 fn resolve(&self, id: u64) -> Result<T::Text, DecodeError> {
113 usize::try_from(id)
114 .ok()
115 .and_then(|i| self.seen.get(i).cloned())
116 .ok_or_else(|| {
117 DecodeError::InvalidFormat(format!(
118 "V1 string table reference out of range: id={id}, table_len={}",
119 self.seen.len()
120 ))
121 })
122 }
123
124 fn record(&mut self, s: &T::Text) {
126 self.seen.push(s.clone());
127 }
128}
129
130pub(super) fn read_interned_string<T: DeserializableTraceData>(
136 buf: &mut Buffer<T>,
137 table: &mut StringTable<T>,
138) -> Result<T::Text, DecodeError>
139where
140 T::Text: Clone,
141{
142 let slice: &[u8] = buf.as_slice();
143 let marker_byte = *slice.first().ok_or_else(|| {
144 DecodeError::InvalidFormat(
145 "Unexpected end of V1 buffer when reading interned string".to_owned(),
146 )
147 })?;
148
149 match marker_byte {
155 0xa0..=0xbf | 0xd9 | 0xda | 0xdb => {
156 let s = buf.read_string()?;
157 table.record(&s);
158 Ok(s)
159 }
160 0x00..=0x7f | 0xcc | 0xcd | 0xce | 0xcf => {
161 let id: u64 = decode::read_int(buf.as_mut_slice()).map_err(|_| {
162 DecodeError::InvalidFormat(
163 "V1 interned string reference uint read failure".to_owned(),
164 )
165 })?;
166 table.resolve(id)
167 }
168 _ => Err(DecodeError::InvalidFormat(format!(
169 "Unexpected msgpack marker 0x{marker_byte:02x} for V1 interned string"
170 ))),
171 }
172}
173
174pub fn from_bytes(
187 data: libdd_tinybytes::Bytes,
188) -> Result<(TracerPayloadBytes, usize), DecodeError> {
189 from_buffer(&mut Buffer::new(data))
190}
191
192pub fn from_slice(data: &[u8]) -> Result<(TracerPayloadSlice<'_>, usize), DecodeError> {
195 from_buffer(&mut Buffer::new(data))
196}
197
198pub fn from_buffer<T: DeserializableTraceData>(
200 data: &mut Buffer<T>,
201) -> Result<(TracerPayload<T>, usize), DecodeError>
202where
203 T::Text: Clone,
204{
205 let start_len = data.len();
206 let mut table = StringTable::<T>::new();
207 let payload = decode_payload(data, &mut table)?;
208 let consumed = start_len - data.len();
209 Ok((payload, consumed))
210}
211
212pub(super) fn skip_unknown_value<T: DeserializableTraceData>(
216 buf: &mut Buffer<T>,
217) -> Result<(), DecodeError> {
218 rmpv::decode::read_value(buf.as_mut_slice())
219 .map_err(|_| DecodeError::InvalidFormat("Failed to skip unknown V1 value".to_owned()))?;
220 Ok(())
221}
222
223fn decode_payload<T: DeserializableTraceData>(
225 buf: &mut Buffer<T>,
226 table: &mut StringTable<T>,
227) -> Result<TracerPayload<T>, DecodeError>
228where
229 T::Text: Clone,
230{
231 let map_len = decode::read_map_len(buf.as_mut_slice())
232 .map_err(|_| DecodeError::InvalidFormat("Unable to read V1 payload map len".to_owned()))?;
233
234 let mut payload = TracerPayload::<T>::default();
235 let mut saw_chunks = false;
236
237 for _ in 0..map_len {
238 let key = decode::read_int::<u8, _>(buf.as_mut_slice()).map_err(|_| {
239 DecodeError::InvalidFormat("V1 payload key (u8) read failure".to_owned())
240 })?;
241 match key {
242 trace_key::CHUNKS => {
243 payload.chunks = decode_chunks(buf, table)?;
244 saw_chunks = true;
245 }
246 trace_key::CONTAINER_ID => payload.container_id = read_interned_string(buf, table)?,
247 trace_key::LANGUAGE_NAME => payload.language_name = read_interned_string(buf, table)?,
248 trace_key::LANGUAGE_VERSION => {
249 payload.language_version = read_interned_string(buf, table)?
250 }
251 trace_key::TRACER_VERSION => payload.tracer_version = read_interned_string(buf, table)?,
252 trace_key::RUNTIME_ID => payload.runtime_id = read_interned_string(buf, table)?,
253 trace_key::ENV_REF => payload.env = read_interned_string(buf, table)?,
254 trace_key::HOSTNAME_REF => payload.hostname = read_interned_string(buf, table)?,
255 trace_key::APP_VERSION_REF => payload.app_version = read_interned_string(buf, table)?,
256 trace_key::ATTRIBUTES => {
257 payload.attributes = span::read_attributes_map(buf, table)?;
258 }
259 _unknown => skip_unknown_value(buf)?,
260 }
261 }
262
263 if !saw_chunks {
264 return Err(DecodeError::InvalidFormat(
265 "V1 payload is missing the chunks field".to_owned(),
266 ));
267 }
268
269 Ok(payload)
270}
271
272fn decode_chunks<T: DeserializableTraceData>(
273 buf: &mut Buffer<T>,
274 table: &mut StringTable<T>,
275) -> Result<Vec<TraceChunk<T>>, DecodeError>
276where
277 T::Text: Clone,
278{
279 let count = decode::read_array_len(buf.as_mut_slice())
280 .map_err(|_| DecodeError::InvalidFormat("V1 chunks array len read failure".to_owned()))?;
281 let mut chunks = Vec::with_capacity(buf.capped_capacity(count as usize));
282 for _ in 0..count {
283 chunks.push(decode_chunk(buf, table)?);
284 }
285 Ok(chunks)
286}
287
288fn decode_chunk<T: DeserializableTraceData>(
289 buf: &mut Buffer<T>,
290 table: &mut StringTable<T>,
291) -> Result<TraceChunk<T>, DecodeError>
292where
293 T::Text: Clone,
294{
295 let map_len = decode::read_map_len(buf.as_mut_slice())
296 .map_err(|_| DecodeError::InvalidFormat("V1 chunk map len read failure".to_owned()))?;
297 let mut chunk = TraceChunk::<T>::default();
298 let mut saw_trace_id = false;
299 let mut saw_spans = false;
300
301 for _ in 0..map_len {
302 let key = decode::read_int::<u8, _>(buf.as_mut_slice())
303 .map_err(|_| DecodeError::InvalidFormat("V1 chunk key (u8) read failure".to_owned()))?;
304 match key {
305 chunk_key::TRACE_ID => {
306 let len = decode::read_bin_len(buf.as_mut_slice()).map_err(|_| {
307 DecodeError::InvalidFormat("V1 chunk trace_id bin len read failure".to_owned())
308 })?;
309 if len != TRACE_ID_LEN {
310 return Err(DecodeError::InvalidFormat(format!(
311 "V1 chunk trace_id must be {TRACE_ID_LEN} bytes, got {len}"
312 )));
313 }
314 let bytes = buf
315 .try_slice_and_advance(TRACE_ID_LEN as usize)
316 .ok_or_else(|| {
317 DecodeError::InvalidFormat("V1 chunk trace_id payload truncated".to_owned())
318 })?;
319 let slice: &[u8] = bytes.borrow();
320 chunk.trace_id.copy_from_slice(slice);
321 saw_trace_id = true;
322 }
323 chunk_key::SPANS => {
324 let count = decode::read_array_len(buf.as_mut_slice()).map_err(|_| {
325 DecodeError::InvalidFormat("V1 chunk spans array len read failure".to_owned())
326 })?;
327 let mut spans = Vec::with_capacity(buf.capped_capacity(count as usize));
328 for _ in 0..count {
329 spans.push(span::decode_span(buf, table)?);
330 }
331 chunk.spans = spans;
332 saw_spans = true;
333 }
334 chunk_key::ORIGIN => chunk.origin = read_interned_string(buf, table)?,
335 chunk_key::PRIORITY => {
336 let v: i64 = decode::read_int(buf.as_mut_slice()).map_err(|_| {
337 DecodeError::InvalidFormat("V1 chunk priority read failure".to_owned())
338 })?;
339 chunk.priority = Some(i32::try_from(v).map_err(|_| {
340 DecodeError::InvalidFormat(format!("V1 chunk priority {v} exceeds i32 range"))
341 })?);
342 }
343 chunk_key::SAMPLING_MECHANISM => {
344 let v: u64 = decode::read_int(buf.as_mut_slice()).map_err(|_| {
345 DecodeError::InvalidFormat(
346 "V1 chunk sampling_mechanism read failure".to_owned(),
347 )
348 })?;
349 chunk.sampling_mechanism = Some(u32::try_from(v).map_err(|_| {
350 DecodeError::InvalidFormat(format!(
351 "V1 chunk sampling_mechanism {v} exceeds u32::MAX"
352 ))
353 })?);
354 }
355 chunk_key::ATTRIBUTES => {
356 chunk.attributes = span::read_attributes_map(buf, table)?;
357 }
358 chunk_key::DROPPED_TRACE => {
359 chunk.dropped_trace = decode::read_bool(buf.as_mut_slice()).map_err(|_| {
360 DecodeError::InvalidFormat(
361 "V1 chunk dropped_trace bool read failure".to_owned(),
362 )
363 })?;
364 }
365 _unknown => skip_unknown_value(buf)?,
366 }
367 }
368
369 if !saw_trace_id {
370 return Err(DecodeError::InvalidFormat(
371 "V1 chunk is missing trace_id".to_owned(),
372 ));
373 }
374 if !saw_spans {
375 return Err(DecodeError::InvalidFormat(
376 "V1 chunk is missing spans array".to_owned(),
377 ));
378 }
379
380 Ok(chunk)
381}
382
383#[cfg(test)]
384mod tests {
385 use super::*;
386 use crate::msgpack_encoder::v1::to_vec_from_v1;
387 use crate::span::v1::{
388 AttributeValue, Span as V1Span, SpanBytes as V1SpanBytes, SpanKind, TraceChunkBytes,
389 TracerPayloadBytes,
390 };
391 use crate::span::vec_map::VecMap;
392 use bolero::check;
393 use libdd_tinybytes::{Bytes, BytesString};
394
395 fn bs(s: &str) -> BytesString {
396 BytesString::from_slice(s.as_bytes()).expect("test string must fit in BytesString")
397 }
398
399 fn sample_payload() -> TracerPayloadBytes {
400 let mut attrs = VecMap::<BytesString, AttributeValue<_>>::new();
401 attrs.insert(bs("http.method"), AttributeValue::String(bs("GET")));
402 attrs.insert(bs("http.status"), AttributeValue::Int(200));
403 attrs.insert(bs("is_root"), AttributeValue::Bool(true));
404 attrs.insert(bs("ratio"), AttributeValue::Float(0.75));
405 attrs.insert(
406 bs("ids"),
407 AttributeValue::List(vec![AttributeValue::Int(1), AttributeValue::Int(2)]),
408 );
409
410 let span = V1Span {
411 service: bs("svc"),
412 name: bs("GET /users"),
413 resource: bs("/users"),
414 r#type: bs("web"),
415 span_id: 42,
416 parent_id: 7,
417 start: 1_700_000_000_000,
418 duration: 1_500,
419 error: true,
420 span_kind: SpanKind::Server,
421 env: bs("prod"),
422 version: bs("1.2.3"),
423 component: bs("net/http"),
424 attributes: attrs,
425 ..Default::default()
426 };
427
428 let mut chunk_attrs = VecMap::<BytesString, AttributeValue<_>>::new();
429 chunk_attrs.insert(bs("_dd.p.dm"), AttributeValue::String(bs("-1")));
430
431 let chunk = TraceChunkBytes {
432 trace_id: [1u8; 16],
433 priority: Some(1),
434 origin: bs("synthetic"),
435 sampling_mechanism: Some(2),
436 dropped_trace: false,
437 attributes: chunk_attrs,
438 spans: vec![span],
439 };
440
441 TracerPayloadBytes {
442 language_name: bs("rust"),
443 language_version: bs("1.87"),
444 tracer_version: bs("9.9.9"),
445 runtime_id: bs("abcd-1234"),
446 env: bs("prod"),
447 hostname: bs("host-1"),
448 app_version: bs("1.2.3"),
449 chunks: vec![chunk],
450 ..Default::default()
451 }
452 }
453
454 #[test]
455 fn roundtrip_full_payload() {
456 let original = sample_payload();
457 let bytes = to_vec_from_v1(&original);
458 let payload_len = bytes.len();
459 let (decoded, consumed) =
460 from_bytes(Bytes::from(bytes)).expect("decoder should succeed on encoder output");
461
462 assert_eq!(consumed, payload_len, "decoder should consume all bytes");
463
464 assert_eq!(decoded.language_name.as_str(), "rust");
466 assert_eq!(decoded.language_version.as_str(), "1.87");
467 assert_eq!(decoded.tracer_version.as_str(), "9.9.9");
468 assert_eq!(decoded.runtime_id.as_str(), "abcd-1234");
469 assert_eq!(decoded.env.as_str(), "prod");
470 assert_eq!(decoded.hostname.as_str(), "host-1");
471 assert_eq!(decoded.app_version.as_str(), "1.2.3");
472
473 assert_eq!(decoded.chunks.len(), 1);
475 let chunk = &decoded.chunks[0];
476 assert_eq!(chunk.trace_id, [1u8; 16]);
477 assert_eq!(chunk.priority, Some(1));
478 assert_eq!(chunk.sampling_mechanism, Some(2));
479 assert_eq!(chunk.origin.as_str(), "synthetic");
480 assert_eq!(chunk.attributes.len(), 1);
481
482 assert_eq!(chunk.spans.len(), 1);
484 let span = &chunk.spans[0];
485 assert_eq!(span.service.as_str(), "svc");
486 assert_eq!(span.name.as_str(), "GET /users");
487 assert_eq!(span.resource.as_str(), "/users");
488 assert_eq!(span.r#type.as_str(), "web");
489 assert_eq!(span.span_id, 42);
490 assert_eq!(span.parent_id, 7);
491 assert_eq!(span.start, 1_700_000_000_000);
492 assert_eq!(span.duration, 1_500);
493 assert!(span.error);
494 assert_eq!(span.span_kind, SpanKind::Server);
495 assert_eq!(span.env.as_str(), "prod");
496 assert_eq!(span.version.as_str(), "1.2.3");
497 assert_eq!(span.component.as_str(), "net/http");
498 assert_eq!(span.attributes.len(), 5);
499 }
500
501 #[test]
502 fn empty_payload_roundtrip() {
503 let original = TracerPayloadBytes::default();
504 let bytes = to_vec_from_v1(&original);
505 let (decoded, _) =
506 from_bytes(Bytes::from(bytes)).expect("decoder should succeed on empty payload");
507 assert!(decoded.chunks.is_empty());
508 assert!(decoded.language_name.as_str().is_empty());
509 }
510
511 #[test]
512 fn missing_chunks_field_is_rejected() {
513 let bytes = vec![0x81, 0x07, 0xa1, 0x78];
516 let err = from_bytes(Bytes::from(bytes)).expect_err("missing chunks must error");
517 assert!(matches!(err, DecodeError::InvalidFormat(_)));
518 }
519
520 #[test]
521 fn truncated_trace_id_is_rejected_not_panicking() {
522 let bytes = vec![
526 0x81,
527 trace_key::CHUNKS,
528 0x91, 0x81, chunk_key::TRACE_ID,
531 0xc4, 0x10, 0x01,
534 0x02,
535 0x03,
536 0x04, ];
538 let err = from_bytes(Bytes::from(bytes)).expect_err("truncated trace_id must error");
539 assert!(matches!(err, DecodeError::InvalidFormat(_)));
540 }
541
542 #[test]
543 fn string_interning_resolves_across_chunks() {
544 let span_a = V1Span {
547 service: bs("shared"),
548 name: bs("a"),
549 span_id: 1,
550 start: 1,
551 ..Default::default()
552 };
553 let span_b = V1Span {
554 service: bs("shared"),
555 name: bs("b"),
556 span_id: 2,
557 start: 1,
558 ..Default::default()
559 };
560 let payload = TracerPayloadBytes {
561 chunks: vec![
562 TraceChunkBytes {
563 trace_id: [1u8; 16],
564 spans: vec![span_a],
565 ..Default::default()
566 },
567 TraceChunkBytes {
568 trace_id: [2u8; 16],
569 spans: vec![span_b],
570 ..Default::default()
571 },
572 ],
573 ..Default::default()
574 };
575 let bytes = to_vec_from_v1(&payload);
576 let (decoded, _) =
577 from_bytes(Bytes::from(bytes)).expect("decoder should resolve interned strings");
578 assert_eq!(decoded.chunks[0].spans[0].service.as_str(), "shared");
579 assert_eq!(decoded.chunks[1].spans[0].service.as_str(), "shared");
580 }
581
582 #[test]
583 fn nested_keyvalue_attribute_roundtrip() {
584 let mut inner = VecMap::<BytesString, AttributeValue<_>>::new();
585 inner.insert(bs("k"), AttributeValue::String(bs("v")));
586 let mut attrs = VecMap::<BytesString, AttributeValue<_>>::new();
587 attrs.insert(bs("nested"), AttributeValue::KeyValue(inner));
588
589 let span = V1Span {
590 service: bs("svc"),
591 name: bs("op"),
592 span_id: 1,
593 start: 1,
594 attributes: attrs,
595 ..Default::default()
596 };
597 let payload = TracerPayloadBytes {
598 chunks: vec![TraceChunkBytes {
599 trace_id: [0u8; 16],
600 spans: vec![span],
601 ..Default::default()
602 }],
603 ..Default::default()
604 };
605 let bytes = to_vec_from_v1(&payload);
606 let (decoded, _) = from_bytes(Bytes::from(bytes)).expect("nested KeyValue roundtrip");
607
608 let decoded_attrs = &decoded.chunks[0].spans[0].attributes;
609 match decoded_attrs.get(&bs("nested")) {
610 Some(AttributeValue::KeyValue(map)) => {
611 assert_eq!(map.len(), 1);
612 match map.get(&bs("k")) {
613 Some(AttributeValue::String(v)) => assert_eq!(v.as_str(), "v"),
614 _ => panic!("inner value should be String"),
615 }
616 }
617 _ => panic!("attribute should decode as KeyValue"),
618 }
619 }
620
621 #[test]
626 #[cfg_attr(miri, ignore)]
627 fn fuzz_from_bytes() {
628 check!()
629 .with_type::<(
630 String, String, String, String, String, String, String, String, u64, u64, u64, bool, )>()
643 .cloned()
644 .for_each(
645 |(
646 lang,
647 payload_env,
648 service,
649 name,
650 resource,
651 span_env,
652 attr_key,
653 attr_value,
654 span_id,
655 parent_id,
656 start,
657 error,
658 )| {
659 let bs = |s: &str| BytesString::from_slice(s.as_ref()).unwrap();
660 let mut attrs = VecMap::<BytesString, AttributeValue<_>>::new();
661 attrs.insert(bs(&attr_key), AttributeValue::String(bs(&attr_value)));
662
663 let span = V1SpanBytes {
664 service: bs(&service),
665 name: bs(&name),
666 resource: bs(&resource),
667 span_id,
668 parent_id,
669 start: start as i64,
670 error,
671 env: bs(&span_env),
672 attributes: attrs,
673 ..Default::default()
674 };
675
676 let payload = TracerPayloadBytes {
677 language_name: bs(&lang),
678 env: bs(&payload_env),
679 chunks: vec![TraceChunkBytes {
680 trace_id: [0xab; 16],
681 spans: vec![span],
682 ..Default::default()
683 }],
684 ..Default::default()
685 };
686
687 let encoded = to_vec_from_v1(&payload);
688 let result = from_bytes(Bytes::from(encoded));
689 assert!(
690 result.is_ok(),
691 "decoder rejected its own encoded output: {result:?}"
692 );
693 },
694 );
695 }
696}