krabka_records_legacy/message.rs
1//! v0/v1 `Message` wire format.
2//!
3//! Each message sits inside a [`MessageSet`](crate::set) entry, after the
4//! per-entry `(offset:i64, message_size:i32)` framing. The wire layout is:
5//!
6//! ```text
7//! v0: crc:u32 | magic:i8=0 | attrs:i8 | | key | value
8//! v1: crc:u32 | magic:i8=1 | attrs:i8 | timestamp:i64 | key | value
9//! ```
10//!
11//! `key` and `value` are nullable bytes: an i32 length, where -1 means
12//! null. The codec computes the CRC-32 (IEEE polynomial) over the bytes
13//! from `magic` through the end of `value`. That is, over everything
14//! inside the message except the CRC field itself.
15//!
16//! The magic byte and the message fields stay here. `attributes` holds the
17//! bit layout of the `attributes` byte and its compression mapping, `encode`
18//! sizes and writes a message, and `decode` parses one frame back.
19
20use {crate::error::LegacyRecordsError, bytes::Bytes, krabka_compression::CompressionType};
21
22mod attributes;
23mod decode;
24mod encode;
25
26#[cfg(test)]
27mod test_support;
28
29pub use self::attributes::{attrs, attrs_with_compression, compression_from_attrs};
30
31/// Magic byte, that is, the legacy message-format version: 0 or 1.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum Magic {
34 V0,
35 V1,
36}
37
38impl Magic {
39 #[must_use]
40 pub const fn as_i8(self) -> i8 {
41 match self {
42 Self::V0 => 0,
43 Self::V1 => 1,
44 }
45 }
46
47 /// # Errors
48 /// Returns `LegacyRecordsError::UnsupportedMagic` if `b` is not 0 or 1.
49 pub fn from_i8(b: i8) -> Result<Self, LegacyRecordsError> {
50 match b {
51 0 => Ok(Self::V0),
52 1 => Ok(Self::V1),
53 other => Err(LegacyRecordsError::UnsupportedMagic { found: other }),
54 }
55 }
56}
57
58/// Owned legacy message, decoded after the frame parse.
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct Message {
61 pub magic: Magic,
62 pub attributes: i8,
63 /// `Some` when `magic` is `V1`. `None` when `magic` is `V0`.
64 pub timestamp: Option<i64>,
65 pub key: Option<Bytes>,
66 pub value: Option<Bytes>,
67}
68
69impl Message {
70 #[must_use]
71 pub fn compression(&self) -> CompressionType {
72 compression_from_attrs(self.attributes).unwrap_or(CompressionType::None)
73 }
74}