use crate::span::v04::Span;
use crate::span::v1::TracerPayload;
use crate::span::TraceData;
use libdd_common::ResultInfallibleExt;
use rmp::encode::{write_array_len, ByteBuf, RmpWrite, ValueWriteError};
const fn msgpack_string_encoding_len(s: &str) -> usize {
const U16_MAX: usize = u16::MAX as usize;
let length_marker_len = match s.len() {
0..32 => 1,
32..256 => 2,
256..=U16_MAX => 3,
_ => 5,
};
length_marker_len + s.len()
}
const fn msgpack_const_string_encoding<const ENCODING_LEN: usize>(s: &str) -> [u8; ENCODING_LEN] {
const fn copy_to_slice(dest: &mut [u8], src: &[u8], n: usize) {
let mut i = 0;
while i < n {
dest[i] = src[i];
i += 1;
}
}
let mut storage = [0; ENCODING_LEN];
let len = s.len() as u64;
let len_bytes = if len < 32 {
storage[0] = 0xa0 | (len as u8 & 0x1f);
0
} else if len < 256 {
storage[0] = 0xd9;
1
} else if len <= (u16::MAX as u64) {
storage[0] = 0xda;
2
} else {
storage[0] = 0xdb;
4
};
let len_be_bytes = len.to_be_bytes();
copy_to_slice(
storage.split_at_mut(1).1,
len_be_bytes.split_at(8 - len_bytes).1,
len_bytes,
);
copy_to_slice(storage.split_at_mut(1 + len_bytes).1, s.as_bytes(), s.len());
storage
}
macro_rules! write_const_msgpack_str {
($writer:expr, $str:expr) => {{
use rmp::encode::ValueWriteError;
const STRING_ENCODING_LEN: usize = super::msgpack_string_encoding_len($str);
const STRING_ENCODING: [u8; STRING_ENCODING_LEN] =
super::msgpack_const_string_encoding($str);
$writer
.write_bytes(&STRING_ENCODING)
.map_err(ValueWriteError::InvalidDataWrite)
}};
}
mod span_v04;
mod span_v1;
#[inline(always)]
fn to_writer<W: RmpWrite, T: TraceData, S: AsRef<[Span<T>]>>(
writer: &mut W,
traces: &[S],
) -> Result<(), ValueWriteError<W::Error>> {
write_array_len(writer, traces.len() as u32)?;
for trace in traces {
write_array_len(writer, trace.as_ref().len() as u32)?;
for span in trace.as_ref() {
span_v04::encode_span(writer, span)?;
}
}
Ok(())
}
pub fn write_to_slice_from_v04<T: TraceData, S: AsRef<[Span<T>]>>(
slice: &mut &mut [u8],
traces: &[S],
) -> Result<(), ValueWriteError> {
to_writer(slice, traces)
}
pub fn to_vec_from_v04<T: TraceData, S: AsRef<[Span<T>]>>(traces: &[S]) -> Vec<u8> {
to_vec_with_capacity_from_v04(traces, 0)
}
pub fn to_vec_with_capacity_from_v04<T: TraceData, S: AsRef<[Span<T>]>>(
traces: &[S],
capacity: u32,
) -> Vec<u8> {
let mut buf = ByteBuf::with_capacity(capacity as usize);
to_writer(&mut buf, traces)
.map_err(super::flatten_value_write_infallible)
.unwrap_infallible();
buf.into_vec()
}
pub fn to_encoded_byte_len_from_v04<T: TraceData, S: AsRef<[Span<T>]>>(traces: &[S]) -> u32 {
let mut counter = super::CountLength(0);
let _ = to_writer(&mut counter, traces);
counter.0
}
fn encode_payload_from_v1<W: RmpWrite, T: TraceData>(
writer: &mut W,
payload: &TracerPayload<T>,
) -> Result<(), ValueWriteError<W::Error>> {
use span_v1::{encode_span, ChunkContext};
write_array_len(writer, payload.chunks.len() as u32)?;
for chunk in &payload.chunks {
let priority = if chunk.dropped_trace {
Some(chunk.priority.filter(|&p| p < 0).unwrap_or(-1))
} else {
chunk.priority
};
let ctx = ChunkContext::new(
&chunk.trace_id,
priority,
&chunk.origin,
chunk.sampling_mechanism,
&chunk.attributes,
&payload.env,
&payload.app_version,
&payload.attributes,
);
write_array_len(writer, chunk.spans.len() as u32)?;
for span in &chunk.spans {
encode_span(writer, span, &ctx)?;
}
}
Ok(())
}
pub fn to_vec_from_v1<T: TraceData>(payload: &TracerPayload<T>) -> Vec<u8> {
to_vec_with_capacity_from_v1(payload, 0)
}
pub fn to_vec_with_capacity_from_v1<T: TraceData>(
payload: &TracerPayload<T>,
capacity: u32,
) -> Vec<u8> {
let mut buf = ByteBuf::with_capacity(capacity as usize);
encode_payload_from_v1(&mut buf, payload)
.map_err(super::flatten_value_write_infallible)
.unwrap_infallible();
buf.into_vec()
}
pub fn write_to_slice_from_v1<T: TraceData>(
slice: &mut &mut [u8],
payload: &TracerPayload<T>,
) -> Result<(), ValueWriteError> {
encode_payload_from_v1(slice, payload)
}
pub fn to_encoded_byte_len_from_v1<T: TraceData>(payload: &TracerPayload<T>) -> u32 {
let mut counter = super::CountLength(0);
let _ = encode_payload_from_v1(&mut counter, payload);
counter.0
}
#[cfg(test)]
mod tests {
use super::msgpack_const_string_encoding;
fn encode<const N: usize>(s: &str) -> [u8; N] {
msgpack_const_string_encoding::<N>(s)
}
#[test]
fn fixstr_boundary_31_bytes() {
let s = "a".repeat(31);
let bytes: [u8; 32] = encode(&s);
let value = rmpv::decode::read_value(&mut &bytes[..]).expect("decode failed");
assert_eq!(value.as_str(), Some(s.as_str()));
}
#[test]
fn str8_boundary_200_bytes() {
let s = "b".repeat(200);
let bytes: [u8; 202] = encode(&s);
let value = rmpv::decode::read_value(&mut &bytes[..]).expect("decode failed");
assert_eq!(value.as_str(), Some(s.as_str()));
}
#[test]
fn str16_boundary_300_bytes() {
let s = "c".repeat(300);
let bytes: [u8; 303] = encode(&s);
let value = rmpv::decode::read_value(&mut &bytes[..]).expect("decode failed");
assert_eq!(value.as_str(), Some(s.as_str()));
}
}