re_log_encoding/
lib.rs

1//! Crate that handles encoding of rerun log types.
2
3#[cfg(feature = "decoder")]
4pub mod decoder;
5
6#[cfg(feature = "encoder")]
7pub mod encoder;
8
9pub mod codec;
10
11pub mod protobuf_conversions;
12
13#[cfg(feature = "encoder")]
14#[cfg(not(target_arch = "wasm32"))]
15mod file_sink;
16
17#[cfg(feature = "stream_from_http")]
18pub mod stream_rrd_from_http;
19
20// ---------------------------------------------------------------------
21
22#[cfg(feature = "encoder")]
23#[cfg(not(target_arch = "wasm32"))]
24pub use file_sink::{FileSink, FileSinkError};
25
26// ----------------------------------------------------------------------------
27
28#[cfg(any(feature = "encoder", feature = "decoder"))]
29const RRD_HEADER: &[u8; 4] = b"RRF2";
30
31#[cfg(feature = "decoder")]
32const OLD_RRD_HEADERS: &[[u8; 4]] = &[*b"RRF0", *b"RRF1"];
33
34// ----------------------------------------------------------------------------
35
36/// Compression format used.
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38#[repr(u8)]
39pub enum Compression {
40    Off = 0,
41
42    /// Very fast compression and decompression, but not very good compression ratio.
43    LZ4 = 1,
44}
45
46/// How we serialize the data
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48#[repr(u8)]
49pub enum Serializer {
50    Protobuf = 2,
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54pub struct EncodingOptions {
55    pub compression: Compression,
56    pub serializer: Serializer,
57}
58
59impl EncodingOptions {
60    pub const PROTOBUF_COMPRESSED: Self = Self {
61        compression: Compression::LZ4,
62        serializer: Serializer::Protobuf,
63    };
64    pub const PROTOBUF_UNCOMPRESSED: Self = Self {
65        compression: Compression::Off,
66        serializer: Serializer::Protobuf,
67    };
68
69    pub fn from_bytes(bytes: [u8; 4]) -> Result<Self, OptionsError> {
70        match bytes {
71            [compression, serializer, 0, 0] => {
72                let compression = match compression {
73                    0 => Compression::Off,
74                    1 => Compression::LZ4,
75                    _ => return Err(OptionsError::UnknownCompression(compression)),
76                };
77                let serializer = match serializer {
78                    1 => return Err(OptionsError::RemovedMsgPackSerializer),
79                    2 => Serializer::Protobuf,
80                    _ => return Err(OptionsError::UnknownSerializer(serializer)),
81                };
82                Ok(Self {
83                    compression,
84                    serializer,
85                })
86            }
87            _ => Err(OptionsError::UnknownReservedBytes),
88        }
89    }
90
91    pub fn to_bytes(self) -> [u8; 4] {
92        [
93            self.compression as u8,
94            self.serializer as u8,
95            0, // reserved
96            0, // reserved
97        ]
98    }
99}
100
101/// On failure to decode [`EncodingOptions`]
102#[derive(thiserror::Error, Debug)]
103#[allow(clippy::enum_variant_names)]
104pub enum OptionsError {
105    #[error("Reserved bytes not zero")]
106    UnknownReservedBytes,
107
108    #[error("Unknown compression: {0}")]
109    UnknownCompression(u8),
110
111    // TODO(jan): Remove this at some point, realistically 1-2 releases after 0.23
112    #[error(
113        "You are trying to load an old .rrd file that's not supported by this version of Rerun."
114    )]
115    RemovedMsgPackSerializer,
116
117    #[error("Unknown serializer: {0}")]
118    UnknownSerializer(u8),
119}
120
121#[cfg(any(feature = "encoder", feature = "decoder"))]
122#[derive(Debug, Clone, Copy)]
123pub(crate) struct FileHeader {
124    pub magic: [u8; 4],
125    pub version: [u8; 4],
126    pub options: EncodingOptions,
127}
128
129#[cfg(any(feature = "encoder", feature = "decoder"))]
130impl FileHeader {
131    #[cfg(feature = "decoder")]
132    pub const SIZE: usize = 12;
133
134    #[cfg(feature = "encoder")]
135    pub fn encode(&self, write: &mut impl std::io::Write) -> Result<(), encoder::EncodeError> {
136        write
137            .write_all(&self.magic)
138            .map_err(encoder::EncodeError::Write)?;
139        write
140            .write_all(&self.version)
141            .map_err(encoder::EncodeError::Write)?;
142        write
143            .write_all(&self.options.to_bytes())
144            .map_err(encoder::EncodeError::Write)?;
145        Ok(())
146    }
147
148    #[cfg(feature = "decoder")]
149    pub fn decode(read: &mut impl std::io::Read) -> Result<Self, decoder::DecodeError> {
150        let to_array_4b = |slice: &[u8]| slice.try_into().expect("always returns an Ok() variant");
151
152        let mut buffer = [0_u8; Self::SIZE];
153        read.read_exact(&mut buffer)
154            .map_err(decoder::DecodeError::Read)?;
155        let magic = to_array_4b(&buffer[0..4]);
156        let version = to_array_4b(&buffer[4..8]);
157        let options = EncodingOptions::from_bytes(to_array_4b(&buffer[8..]))?;
158        Ok(Self {
159            magic,
160            version,
161            options,
162        })
163    }
164}