Skip to main content

redline_core/
decode.rs

1use alloc::{string::String, vec::Vec};
2use core::{fmt, str};
3use smallvec::SmallVec;
4use tracing_core::Level;
5
6use crate::{BinaryFrameKind, CallsiteKind, FieldValue, InlineString, Timestamp};
7
8/// Decoded callsite metadata from a binary metadata frame.
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct DecodedCallsiteMetadata {
11    pub id: u32,
12    pub name: String,
13    pub target: String,
14    pub level: Level,
15    pub file: Option<String>,
16    pub line: Option<u32>,
17    pub module_path: Option<String>,
18    pub fields: SmallVec<[String; 8]>,
19    pub kind: CallsiteKind,
20}
21
22/// Decoded field from a binary event or span.
23#[derive(Clone, Debug, PartialEq)]
24pub struct DecodedField {
25    pub name: String,
26    pub value: FieldValue,
27}
28
29/// Small-vector storage for decoded fields.
30pub type DecodedFields = SmallVec<[DecodedField; 8]>;
31
32/// Decoded span snapshot from a binary frame.
33#[derive(Clone, Debug, PartialEq)]
34pub struct DecodedSpanSnapshot {
35    pub id: u64,
36    pub metadata_id: u32,
37    pub name: String,
38    pub target: String,
39    pub level: Level,
40    pub fields: DecodedFields,
41}
42
43/// Decoded event record from a binary frame.
44#[derive(Clone, Debug, PartialEq)]
45pub struct DecodedRecord {
46    pub timestamp: Timestamp,
47    pub metadata_id: u32,
48    pub current_span_id: u64,
49    pub fields: DecodedFields,
50    pub current_span: Option<DecodedSpanSnapshot>,
51    pub spans: SmallVec<[DecodedSpanSnapshot; 4]>,
52}
53
54/// Decoded binary frame.
55#[derive(Clone, Debug, PartialEq)]
56#[allow(clippy::large_enum_variant)]
57pub enum DecodedBinaryFrame {
58    Metadata(DecodedCallsiteMetadata),
59    Event(DecodedRecord),
60}
61
62/// Error returned when decoding binary frames.
63#[derive(Clone, Debug, PartialEq, Eq)]
64pub enum BinaryDecodeError {
65    UnexpectedEof,
66    InvalidFrameKind(u8),
67    InvalidCallsiteKind(u8),
68    InvalidLevel(u8),
69    InvalidFieldType(u8),
70    InvalidUtf8,
71}
72
73impl fmt::Display for BinaryDecodeError {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self {
76            Self::UnexpectedEof => f.write_str("unexpected end of binary frame"),
77            Self::InvalidFrameKind(kind) => write!(f, "invalid frame kind `{kind}`"),
78            Self::InvalidCallsiteKind(kind) => write!(f, "invalid callsite kind `{kind}`"),
79            Self::InvalidLevel(level) => write!(f, "invalid level code `{level}`"),
80            Self::InvalidFieldType(kind) => write!(f, "invalid field type `{kind}`"),
81            Self::InvalidUtf8 => f.write_str("invalid utf-8 string in binary frame"),
82        }
83    }
84}
85
86/// Decodes the next binary frame and returns the remaining input.
87///
88/// This is useful when reading a stream containing multiple concatenated
89/// metadata and event frames.
90pub fn decode_binary_frame(input: &[u8]) -> Result<(DecodedBinaryFrame, &[u8]), BinaryDecodeError> {
91    let (frame_kind, rest) = take_u8(input)?;
92    let (payload_len, rest) = take_u32(rest)?;
93    let payload_len = payload_len as usize;
94    if rest.len() < payload_len {
95        return Err(BinaryDecodeError::UnexpectedEof);
96    }
97    let (payload, remaining) = rest.split_at(payload_len);
98    let frame = match frame_kind {
99        kind if kind == BinaryFrameKind::Metadata as u8 => {
100            DecodedBinaryFrame::Metadata(decode_metadata_payload(payload)?)
101        }
102        kind if kind == BinaryFrameKind::Event as u8 => {
103            DecodedBinaryFrame::Event(decode_event_payload(payload)?)
104        }
105        other => return Err(BinaryDecodeError::InvalidFrameKind(other)),
106    };
107    Ok((frame, remaining))
108}
109
110fn decode_metadata_payload(mut input: &[u8]) -> Result<DecodedCallsiteMetadata, BinaryDecodeError> {
111    let (id, rest) = take_u32(input)?;
112    input = rest;
113    let (kind, rest) = take_u8(input)?;
114    input = rest;
115    let kind = match kind {
116        1 => CallsiteKind::Event,
117        2 => CallsiteKind::Span,
118        other => return Err(BinaryDecodeError::InvalidCallsiteKind(other)),
119    };
120    let (level_code, rest) = take_u8(input)?;
121    input = rest;
122    let level = decode_level(level_code)?;
123    let (file, rest) = take_optional_string(input)?;
124    input = rest;
125    let (line, rest) = take_optional_u32(input)?;
126    input = rest;
127    let (module_path, rest) = take_optional_string(input)?;
128    input = rest;
129    let (name, rest) = take_string(input)?;
130    input = rest;
131    let (target, rest) = take_string(input)?;
132    input = rest;
133    let (field_count, mut rest) = take_u16(input)?;
134    let mut fields = SmallVec::new();
135    for _ in 0..field_count {
136        let (field, next) = take_string(rest)?;
137        rest = next;
138        fields.push(field);
139    }
140
141    Ok(DecodedCallsiteMetadata {
142        id,
143        name,
144        target,
145        level,
146        file,
147        line,
148        module_path,
149        fields,
150        kind,
151    })
152}
153
154fn decode_event_payload(mut input: &[u8]) -> Result<DecodedRecord, BinaryDecodeError> {
155    let (unix_seconds, rest) = take_u64(input)?;
156    input = rest;
157    let (subsec_nanos, rest) = take_u32(input)?;
158    input = rest;
159    let (metadata_id, rest) = take_u32(input)?;
160    input = rest;
161    let (current_span_id, rest) = take_u64(input)?;
162    input = rest;
163    let (fields, rest) = take_fields(input)?;
164    input = rest;
165    let (has_current_span, rest) = take_u8(input)?;
166    input = rest;
167    let current_span = if has_current_span == 1 {
168        let (span, rest) = take_span(input)?;
169        input = rest;
170        Some(span)
171    } else {
172        None
173    };
174
175    let (span_count, mut rest) = take_u16(input)?;
176    let mut spans = SmallVec::new();
177    for _ in 0..span_count {
178        let (span, next) = take_span(rest)?;
179        rest = next;
180        spans.push(span);
181    }
182
183    Ok(DecodedRecord {
184        timestamp: Timestamp::new(unix_seconds, subsec_nanos),
185        metadata_id,
186        current_span_id,
187        fields,
188        current_span,
189        spans,
190    })
191}
192
193fn take_span(input: &[u8]) -> Result<(DecodedSpanSnapshot, &[u8]), BinaryDecodeError> {
194    let (id, rest) = take_u64(input)?;
195    let (metadata_id, rest) = take_u32(rest)?;
196    let (level_code, rest) = take_u8(rest)?;
197    let level = decode_level(level_code)?;
198    let (name, rest) = take_string(rest)?;
199    let (target, rest) = take_string(rest)?;
200    let (fields, rest) = take_fields(rest)?;
201
202    Ok((
203        DecodedSpanSnapshot {
204            id,
205            metadata_id,
206            name,
207            target,
208            level,
209            fields,
210        },
211        rest,
212    ))
213}
214
215fn take_fields(input: &[u8]) -> Result<(DecodedFields, &[u8]), BinaryDecodeError> {
216    let (field_count, mut input) = take_u16(input)?;
217    let mut fields = SmallVec::new();
218    for _ in 0..field_count {
219        let (name, rest) = take_string(input)?;
220        input = rest;
221        let (value, rest) = take_field_value(input)?;
222        input = rest;
223        fields.push(DecodedField { name, value });
224    }
225    Ok((fields, input))
226}
227
228fn take_field_value(input: &[u8]) -> Result<(FieldValue, &[u8]), BinaryDecodeError> {
229    let (kind, input) = take_u8(input)?;
230    match kind {
231        1 => Ok((FieldValue::Bool(false), input)),
232        2 => Ok((FieldValue::Bool(true), input)),
233        3 => {
234            let (value, input) = take_i64(input)?;
235            Ok((FieldValue::I64(value), input))
236        }
237        4 => {
238            let (value, input) = take_u64(input)?;
239            Ok((FieldValue::U64(value), input))
240        }
241        5 => {
242            let (value, input) = take_i128(input)?;
243            Ok((FieldValue::I128(value), input))
244        }
245        6 => {
246            let (value, input) = take_u128(input)?;
247            Ok((FieldValue::U128(value), input))
248        }
249        7 => {
250            let (value, input) = take_f64(input)?;
251            Ok((FieldValue::F64(value), input))
252        }
253        8 => {
254            let (value, input) = take_string(input)?;
255            Ok((FieldValue::Str(InlineString::from(value)), input))
256        }
257        9 => {
258            let (value, input) = take_string(input)?;
259            Ok((FieldValue::Debug(InlineString::from(value)), input))
260        }
261        10 => {
262            let (value, input) = take_bytes(input)?;
263            Ok((FieldValue::Bytes(value), input))
264        }
265        other => Err(BinaryDecodeError::InvalidFieldType(other)),
266    }
267}
268
269fn take_u8(input: &[u8]) -> Result<(u8, &[u8]), BinaryDecodeError> {
270    let Some((first, rest)) = input.split_first() else {
271        return Err(BinaryDecodeError::UnexpectedEof);
272    };
273    Ok((*first, rest))
274}
275
276fn take_u16(input: &[u8]) -> Result<(u16, &[u8]), BinaryDecodeError> {
277    if input.len() < 2 {
278        return Err(BinaryDecodeError::UnexpectedEof);
279    }
280    let (bytes, rest) = input.split_at(2);
281    Ok((u16::from_le_bytes([bytes[0], bytes[1]]), rest))
282}
283
284fn take_u32(input: &[u8]) -> Result<(u32, &[u8]), BinaryDecodeError> {
285    if input.len() < 4 {
286        return Err(BinaryDecodeError::UnexpectedEof);
287    }
288    let (bytes, rest) = input.split_at(4);
289    Ok((
290        u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
291        rest,
292    ))
293}
294
295fn take_u64(input: &[u8]) -> Result<(u64, &[u8]), BinaryDecodeError> {
296    if input.len() < 8 {
297        return Err(BinaryDecodeError::UnexpectedEof);
298    }
299    let (bytes, rest) = input.split_at(8);
300    Ok((
301        u64::from_le_bytes([
302            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
303        ]),
304        rest,
305    ))
306}
307
308fn take_i64(input: &[u8]) -> Result<(i64, &[u8]), BinaryDecodeError> {
309    let (value, rest) = take_u64(input)?;
310    Ok((value as i64, rest))
311}
312
313fn take_u128(input: &[u8]) -> Result<(u128, &[u8]), BinaryDecodeError> {
314    if input.len() < 16 {
315        return Err(BinaryDecodeError::UnexpectedEof);
316    }
317    let (bytes, rest) = input.split_at(16);
318    let mut array = [0u8; 16];
319    array.copy_from_slice(bytes);
320    Ok((u128::from_le_bytes(array), rest))
321}
322
323fn take_i128(input: &[u8]) -> Result<(i128, &[u8]), BinaryDecodeError> {
324    let (value, rest) = take_u128(input)?;
325    Ok((value as i128, rest))
326}
327
328fn take_f64(input: &[u8]) -> Result<(f64, &[u8]), BinaryDecodeError> {
329    let (value, rest) = take_u64(input)?;
330    Ok((f64::from_le_bytes(value.to_le_bytes()), rest))
331}
332
333fn take_bytes(input: &[u8]) -> Result<(Vec<u8>, &[u8]), BinaryDecodeError> {
334    let (len, input) = take_u32(input)?;
335    let len = len as usize;
336    if input.len() < len {
337        return Err(BinaryDecodeError::UnexpectedEof);
338    }
339    let (bytes, rest) = input.split_at(len);
340    Ok((bytes.to_vec(), rest))
341}
342
343fn take_string(input: &[u8]) -> Result<(String, &[u8]), BinaryDecodeError> {
344    let (len, input) = take_u32(input)?;
345    let len = len as usize;
346    if input.len() < len {
347        return Err(BinaryDecodeError::UnexpectedEof);
348    }
349    let (bytes, rest) = input.split_at(len);
350    let value = str::from_utf8(bytes).map_err(|_| BinaryDecodeError::InvalidUtf8)?;
351    Ok((value.into(), rest))
352}
353
354fn take_optional_string(input: &[u8]) -> Result<(Option<String>, &[u8]), BinaryDecodeError> {
355    let (present, input) = take_u8(input)?;
356    if present == 1 {
357        let (value, rest) = take_string(input)?;
358        Ok((Some(value), rest))
359    } else {
360        Ok((None, input))
361    }
362}
363
364fn take_optional_u32(input: &[u8]) -> Result<(Option<u32>, &[u8]), BinaryDecodeError> {
365    let (present, input) = take_u8(input)?;
366    if present == 1 {
367        let (value, rest) = take_u32(input)?;
368        Ok((Some(value), rest))
369    } else {
370        Ok((None, input))
371    }
372}
373
374fn decode_level(code: u8) -> Result<Level, BinaryDecodeError> {
375    match code {
376        1 => Ok(Level::ERROR),
377        2 => Ok(Level::WARN),
378        3 => Ok(Level::INFO),
379        4 => Ok(Level::DEBUG),
380        5 => Ok(Level::TRACE),
381        other => Err(BinaryDecodeError::InvalidLevel(other)),
382    }
383}
384
385#[cfg(test)]
386mod tests {
387    use alloc::vec;
388
389    use super::*;
390    use crate::{
391        CallsiteMetadata, EncodeConfig, FieldValue, OwnedField, OwnedRecord, SpanSnapshot,
392        encode_binary_metadata, encode_binary_record,
393    };
394
395    #[test]
396    fn decodes_binary_roundtrip() {
397        let metadata = CallsiteMetadata {
398            id: 11,
399            name: "request",
400            target: "app::http",
401            level: Level::INFO,
402            file: Some("src/main.rs"),
403            line: Some(8),
404            module_path: Some("app::http"),
405            fields: SmallVec::from_slice(&["message", "answer"]),
406            kind: CallsiteKind::Event,
407        };
408        let record = OwnedRecord {
409            timestamp: Timestamp::new(10, 20),
410            metadata_id: 11,
411            name: "request",
412            target: "app::http",
413            level: Level::INFO,
414            fields: SmallVec::from_vec(vec![
415                OwnedField {
416                    name: "message",
417                    value: FieldValue::Str("ok".into()),
418                },
419                OwnedField {
420                    name: "answer",
421                    value: FieldValue::U64(42),
422                },
423            ]),
424            current_span: Some(SpanSnapshot {
425                id: 77,
426                metadata_id: 2,
427                name: "span",
428                target: "app::http",
429                level: Level::INFO,
430                fields: SmallVec::new(),
431            }),
432            spans: SmallVec::new(),
433        };
434
435        let mut metadata_frame = Vec::new();
436        let mut event_frame = Vec::new();
437        encode_binary_metadata(&metadata, &mut metadata_frame);
438        encode_binary_record(EncodeConfig::default(), &record, &mut event_frame);
439
440        let mut stream = Vec::new();
441        stream.extend_from_slice(&metadata_frame);
442        stream.extend_from_slice(&event_frame);
443
444        let (first, rest) = decode_binary_frame(&stream).unwrap();
445        let (second, rest) = decode_binary_frame(rest).unwrap();
446        assert!(rest.is_empty());
447
448        match first {
449            DecodedBinaryFrame::Metadata(frame) => {
450                assert_eq!(frame.id, 11);
451                assert_eq!(frame.name, "request");
452                assert_eq!(frame.target, "app::http");
453            }
454            _ => panic!("expected metadata frame"),
455        }
456
457        match second {
458            DecodedBinaryFrame::Event(frame) => {
459                assert_eq!(frame.metadata_id, 11);
460                assert_eq!(frame.fields.len(), 2);
461                assert_eq!(frame.current_span.unwrap().id, 77);
462            }
463            _ => panic!("expected event frame"),
464        }
465    }
466}