Skip to main content

libdd_trace_utils/msgpack_decoder/decode/
string.rs

1// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::msgpack_decoder::decode::buffer::Buffer;
5use crate::msgpack_decoder::decode::error::DecodeError;
6use crate::span::DeserializableTraceData;
7use rmp::decode;
8use std::collections::HashMap;
9
10// https://docs.rs/rmp/latest/rmp/enum.Marker.html#variant.Null (0xc0 == 192)
11const NULL_MARKER: &u8 = &0xc0;
12
13/// Read a nullable string from the slices `buf`.
14///
15/// # Errors
16/// Fails if the buffer doesn't contain a valid utf8 msgpack string or a null marker.
17#[inline]
18pub fn read_nullable_string<T: DeserializableTraceData>(
19    buf: &mut Buffer<T>,
20) -> Result<T::Text, DecodeError> {
21    if handle_null_marker(buf) {
22        Ok(T::Text::default())
23    } else {
24        buf.read_string()
25    }
26}
27
28/// Read a hashmap of (string, string) from the slices `buf`.
29///
30/// # Errors
31/// Fails if the buffer does not contain a valid map length prefix,
32/// or if any key or value is not a valid utf8 msgpack string.
33/// Null values are skipped (key not inserted into map).
34#[inline]
35pub fn read_str_map_to_strings<T: DeserializableTraceData>(
36    buf: &mut Buffer<T>,
37) -> Result<HashMap<T::Text, T::Text>, DecodeError> {
38    let len = decode::read_map_len(buf.as_mut_slice())
39        .map_err(|_| DecodeError::InvalidFormat("Unable to get map len for str map".to_owned()))?;
40
41    #[allow(clippy::expect_used)]
42    let mut map = HashMap::with_capacity(len.try_into().expect("Unable to cast map len to usize"));
43    for _ in 0..len {
44        let key = buf.read_string()?;
45        // Only insert if value is not null
46        if !handle_null_marker(buf) {
47            let value = buf.read_string()?;
48            map.insert(key, value);
49        }
50    }
51    Ok(map)
52}
53
54/// Read a nullable hashmap of (string, string) from the slices `buf`.
55///
56/// # Errors
57/// Fails if the buffer does not contain a valid map length prefix,
58/// or if any key or value is not a valid utf8 msgpack string.
59/// Null values are skipped (key not inserted into map).
60#[inline]
61pub fn read_nullable_str_map_to_strings<T: DeserializableTraceData>(
62    buf: &mut Buffer<T>,
63) -> Result<HashMap<T::Text, T::Text>, DecodeError> {
64    if handle_null_marker(buf) {
65        return Ok(HashMap::default());
66    }
67
68    read_str_map_to_strings(buf)
69}
70
71/// Handle the null value by peeking if the next value is a null marker, and will only advance the
72/// buffer if it is null. If it is not null, you can continue to decode as expected.
73///
74/// # Returns
75/// A boolean indicating whether the next value is null or not.
76#[inline]
77pub fn handle_null_marker<T: DeserializableTraceData>(buf: &mut Buffer<T>) -> bool {
78    let slice = buf.as_mut_slice();
79    if slice.first() == Some(NULL_MARKER) {
80        *slice = &slice[1..];
81        true
82    } else {
83        false
84    }
85}