Skip to main content

alec/
protocol.rs

1// ALEC - Adaptive Lazy Evolving Compression
2// Copyright (c) 2025 David Martin Venti
3//
4// Dual-licensed under AGPL-3.0 and Commercial License.
5// See LICENSE file for details.
6
7//! Protocol definitions for ALEC
8//!
9//! This module defines the core types used in the ALEC protocol:
10//! - Message structure and headers
11//! - Priority levels
12//! - Encoding types
13//! - Raw data representation
14
15#[cfg(not(feature = "std"))]
16use alloc::vec::Vec;
17
18use crate::error::DecodeError;
19use core::fmt;
20
21/// Checksum size in bytes (xxHash32)
22pub const CHECKSUM_SIZE: usize = 4;
23
24/// Raw data from a sensor or source
25#[derive(Debug, Clone, PartialEq)]
26pub struct RawData {
27    /// Unique identifier for the data source
28    pub source_id: u32,
29    /// Timestamp (relative or absolute)
30    pub timestamp: u64,
31    /// The measured value
32    pub value: f64,
33}
34
35impl RawData {
36    /// Create new raw data with default source_id of 0
37    pub fn new(value: f64, timestamp: u64) -> Self {
38        Self {
39            source_id: 0,
40            timestamp,
41            value,
42        }
43    }
44
45    /// Create new raw data with a specific source_id
46    pub fn with_source(source_id: u32, value: f64, timestamp: u64) -> Self {
47        Self {
48            source_id,
49            timestamp,
50            value,
51        }
52    }
53
54    /// Size of raw data in bytes (for comparison)
55    pub fn raw_size(&self) -> usize {
56        // source_id (4) + timestamp (8) + value (8) = 20 bytes
57        20
58    }
59}
60
61/// Priority levels for data classification
62#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
63#[repr(u8)]
64pub enum Priority {
65    /// Critical - immediate transmission with acknowledgment required
66    P1Critical = 0,
67    /// Important - immediate transmission
68    P2Important = 1,
69    /// Normal - standard transmission
70    #[default]
71    P3Normal = 2,
72    /// Deferred - stored locally, sent on request
73    P4Deferred = 3,
74    /// Disposable - never sent spontaneously
75    P5Disposable = 4,
76}
77
78impl Priority {
79    /// Check if this priority level should be transmitted immediately
80    pub fn should_transmit(&self) -> bool {
81        matches!(
82            self,
83            Priority::P1Critical | Priority::P2Important | Priority::P3Normal
84        )
85    }
86
87    /// Check if this priority requires acknowledgment
88    pub fn requires_ack(&self) -> bool {
89        matches!(self, Priority::P1Critical)
90    }
91
92    /// Convert from u8
93    pub fn from_u8(value: u8) -> Option<Self> {
94        match value {
95            0 => Some(Priority::P1Critical),
96            1 => Some(Priority::P2Important),
97            2 => Some(Priority::P3Normal),
98            3 => Some(Priority::P4Deferred),
99            4 => Some(Priority::P5Disposable),
100            _ => None,
101        }
102    }
103}
104
105impl fmt::Display for Priority {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        match self {
108            Priority::P1Critical => write!(f, "P1-CRITICAL"),
109            Priority::P2Important => write!(f, "P2-IMPORTANT"),
110            Priority::P3Normal => write!(f, "P3-NORMAL"),
111            Priority::P4Deferred => write!(f, "P4-DEFERRED"),
112            Priority::P5Disposable => write!(f, "P5-DISPOSABLE"),
113        }
114    }
115}
116
117/// Message types in the ALEC protocol
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
119#[repr(u8)]
120pub enum MessageType {
121    /// Encoded data payload
122    #[default]
123    Data = 0,
124    /// Context synchronization
125    Sync = 1,
126    /// Request from receiver
127    Request = 2,
128    /// Response to request
129    Response = 3,
130    /// Acknowledgment
131    Ack = 4,
132    /// Negative acknowledgment
133    Nack = 5,
134    /// Keep-alive heartbeat
135    Heartbeat = 6,
136    /// Reserved for future use
137    Reserved = 7,
138}
139
140impl MessageType {
141    /// Convert from u8
142    pub fn from_u8(value: u8) -> Option<Self> {
143        match value {
144            0 => Some(MessageType::Data),
145            1 => Some(MessageType::Sync),
146            2 => Some(MessageType::Request),
147            3 => Some(MessageType::Response),
148            4 => Some(MessageType::Ack),
149            5 => Some(MessageType::Nack),
150            6 => Some(MessageType::Heartbeat),
151            7 => Some(MessageType::Reserved),
152            _ => None,
153        }
154    }
155}
156
157/// Encoding types for data compression
158#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
159#[repr(u8)]
160pub enum EncodingType {
161    /// Raw float64, big-endian (8 bytes)
162    #[default]
163    Raw64 = 0x00,
164    /// Raw float32, big-endian (4 bytes)
165    Raw32 = 0x01,
166    /// Delta encoded as i8 (1 byte)
167    Delta8 = 0x10,
168    /// Delta encoded as i16 big-endian (2 bytes)
169    Delta16 = 0x11,
170    /// Delta encoded as i32 big-endian (4 bytes)
171    Delta32 = 0x12,
172    /// Reference to dictionary pattern
173    Pattern = 0x20,
174    /// Pattern reference with delta8 adjustment
175    PatternDelta = 0x21,
176    /// Same value as previous (0 bytes)
177    Repeated = 0x30,
178    /// Exact predicted value (0 bytes)
179    Interpolated = 0x31,
180    /// Multiple values in one message
181    Multi = 0x40,
182}
183
184impl EncodingType {
185    /// Convert from u8
186    pub fn from_u8(value: u8) -> Option<Self> {
187        match value {
188            0x00 => Some(EncodingType::Raw64),
189            0x01 => Some(EncodingType::Raw32),
190            0x10 => Some(EncodingType::Delta8),
191            0x11 => Some(EncodingType::Delta16),
192            0x12 => Some(EncodingType::Delta32),
193            0x20 => Some(EncodingType::Pattern),
194            0x21 => Some(EncodingType::PatternDelta),
195            0x30 => Some(EncodingType::Repeated),
196            0x31 => Some(EncodingType::Interpolated),
197            0x40 => Some(EncodingType::Multi),
198            _ => None,
199        }
200    }
201
202    /// Get the typical size of this encoding (excluding header)
203    pub fn typical_size(&self) -> usize {
204        match self {
205            EncodingType::Raw64 => 8,
206            EncodingType::Raw32 => 4,
207            EncodingType::Delta8 => 1,
208            EncodingType::Delta16 => 2,
209            EncodingType::Delta32 => 4,
210            EncodingType::Pattern => 2,      // varint typically 1-2 bytes
211            EncodingType::PatternDelta => 3, // varint + 1 byte
212            EncodingType::Repeated => 0,
213            EncodingType::Interpolated => 0,
214            EncodingType::Multi => 0, // variable
215        }
216    }
217}
218
219/// Message header (13 bytes total)
220#[derive(Debug, Clone, PartialEq, Eq)]
221pub struct MessageHeader {
222    /// Protocol version (2 bits in header byte)
223    pub version: u8,
224    /// Message type (3 bits in header byte)
225    pub message_type: MessageType,
226    /// Priority level (3 bits in header byte)
227    pub priority: Priority,
228    /// Sequence number
229    pub sequence: u32,
230    /// Timestamp
231    pub timestamp: u32,
232    /// Context version used for encoding
233    pub context_version: u32,
234}
235
236impl MessageHeader {
237    /// Create a new header with default values
238    pub fn new(message_type: MessageType, priority: Priority) -> Self {
239        Self {
240            version: crate::PROTOCOL_VERSION,
241            message_type,
242            priority,
243            sequence: 0,
244            timestamp: 0,
245            context_version: 0,
246        }
247    }
248
249    /// Header size in bytes
250    pub const SIZE: usize = 13;
251
252    /// Encode the header byte (version + type + priority)
253    pub fn encode_header_byte(&self) -> u8 {
254        let version_bits = (self.version & 0x03) << 6;
255        let type_bits = (self.message_type as u8 & 0x07) << 3;
256        let priority_bits = self.priority as u8 & 0x07;
257        version_bits | type_bits | priority_bits
258    }
259
260    /// Decode the header byte
261    pub fn decode_header_byte(byte: u8) -> (u8, Option<MessageType>, Option<Priority>) {
262        let version = (byte >> 6) & 0x03;
263        let msg_type = MessageType::from_u8((byte >> 3) & 0x07);
264        let priority = Priority::from_u8(byte & 0x07);
265        (version, msg_type, priority)
266    }
267
268    /// Serialize header to bytes
269    pub fn to_bytes(&self) -> [u8; Self::SIZE] {
270        let mut bytes = [0u8; Self::SIZE];
271        bytes[0] = self.encode_header_byte();
272        bytes[1..5].copy_from_slice(&self.sequence.to_be_bytes());
273        bytes[5..9].copy_from_slice(&self.timestamp.to_be_bytes());
274        bytes[9..13].copy_from_slice(&self.context_version.to_be_bytes());
275        bytes
276    }
277
278    /// Deserialize header from bytes
279    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
280        if bytes.len() < Self::SIZE {
281            return None;
282        }
283
284        let (version, msg_type, priority) = Self::decode_header_byte(bytes[0]);
285        let msg_type = msg_type?;
286        let priority = priority?;
287
288        let sequence = u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
289        let timestamp = u32::from_be_bytes([bytes[5], bytes[6], bytes[7], bytes[8]]);
290        let context_version = u32::from_be_bytes([bytes[9], bytes[10], bytes[11], bytes[12]]);
291
292        Some(Self {
293            version,
294            message_type: msg_type,
295            priority,
296            sequence,
297            timestamp,
298            context_version,
299        })
300    }
301}
302
303impl Default for MessageHeader {
304    fn default() -> Self {
305        Self::new(MessageType::Data, Priority::P3Normal)
306    }
307}
308
309/// An encoded message ready for transmission
310#[derive(Debug, Clone, PartialEq)]
311pub struct EncodedMessage {
312    /// Message header
313    pub header: MessageHeader,
314    /// Encoded payload
315    pub payload: Vec<u8>,
316}
317
318impl EncodedMessage {
319    /// Create a new encoded message
320    pub fn new(header: MessageHeader, payload: Vec<u8>) -> Self {
321        Self { header, payload }
322    }
323
324    /// Total size of the message in bytes
325    pub fn len(&self) -> usize {
326        MessageHeader::SIZE + self.payload.len()
327    }
328
329    /// Check if the message is empty (no payload)
330    pub fn is_empty(&self) -> bool {
331        self.payload.is_empty()
332    }
333
334    /// Get the encoding type from the payload (first byte after source_id)
335    pub fn encoding_type(&self) -> Option<EncodingType> {
336        // Payload format: source_id (varint) + encoding_type (1 byte) + value
337        // For simplicity, assuming source_id is 1 byte (< 128)
338        if self.payload.len() >= 2 {
339            EncodingType::from_u8(self.payload[1])
340        } else {
341            None
342        }
343    }
344
345    /// Serialize the entire message to bytes
346    pub fn to_bytes(&self) -> Vec<u8> {
347        let mut bytes = Vec::with_capacity(self.len());
348        bytes.extend_from_slice(&self.header.to_bytes());
349        bytes.extend_from_slice(&self.payload);
350        bytes
351    }
352
353    /// Deserialize message from bytes
354    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
355        if bytes.len() < MessageHeader::SIZE {
356            return None;
357        }
358
359        let header = MessageHeader::from_bytes(&bytes[..MessageHeader::SIZE])?;
360        let payload = bytes[MessageHeader::SIZE..].to_vec();
361
362        Some(Self { header, payload })
363    }
364
365    /// Compute checksum of the message (header + payload)
366    pub fn compute_checksum(&self) -> u32 {
367        use xxhash_rust::xxh32::xxh32;
368
369        let mut data = Vec::with_capacity(MessageHeader::SIZE + self.payload.len());
370        data.extend_from_slice(&self.header.to_bytes());
371        data.extend_from_slice(&self.payload);
372
373        xxh32(&data, 0) // seed = 0
374    }
375
376    /// Serialize message with checksum appended
377    pub fn to_bytes_with_checksum(&self) -> Vec<u8> {
378        let mut bytes = self.to_bytes();
379        let checksum = self.compute_checksum();
380        bytes.extend_from_slice(&checksum.to_be_bytes());
381        bytes
382    }
383
384    /// Deserialize message from bytes with checksum verification
385    pub fn from_bytes_with_checksum(bytes: &[u8]) -> Result<Self, DecodeError> {
386        if bytes.len() < MessageHeader::SIZE + CHECKSUM_SIZE {
387            return Err(DecodeError::BufferTooShort {
388                needed: MessageHeader::SIZE + CHECKSUM_SIZE,
389                available: bytes.len(),
390            });
391        }
392
393        let checksum_offset = bytes.len() - CHECKSUM_SIZE;
394        let expected = u32::from_be_bytes(bytes[checksum_offset..].try_into().unwrap());
395
396        let message =
397            Self::from_bytes(&bytes[..checksum_offset]).ok_or(DecodeError::InvalidHeader)?;
398
399        let actual = message.compute_checksum();
400
401        if actual != expected {
402            return Err(DecodeError::InvalidChecksum { expected, actual });
403        }
404
405        Ok(message)
406    }
407}
408
409/// Decoded data result
410#[derive(Debug, Clone, PartialEq)]
411pub struct DecodedData {
412    /// Source identifier
413    pub source_id: u32,
414    /// Timestamp from header
415    pub timestamp: u64,
416    /// Decoded value
417    pub value: f64,
418    /// Original priority
419    pub priority: Priority,
420    /// Whether deferred data is available
421    pub deferred_available: bool,
422}
423
424impl DecodedData {
425    /// Create new decoded data
426    pub fn new(source_id: u32, timestamp: u64, value: f64, priority: Priority) -> Self {
427        Self {
428            source_id,
429            timestamp,
430            value,
431            priority,
432            deferred_available: false,
433        }
434    }
435}
436
437#[cfg(test)]
438mod tests {
439    use super::*;
440
441    #[test]
442    fn test_priority_ordering() {
443        assert!(Priority::P1Critical < Priority::P2Important);
444        assert!(Priority::P2Important < Priority::P3Normal);
445        assert!(Priority::P3Normal < Priority::P4Deferred);
446        assert!(Priority::P4Deferred < Priority::P5Disposable);
447    }
448
449    #[test]
450    fn test_priority_should_transmit() {
451        assert!(Priority::P1Critical.should_transmit());
452        assert!(Priority::P2Important.should_transmit());
453        assert!(Priority::P3Normal.should_transmit());
454        assert!(!Priority::P4Deferred.should_transmit());
455        assert!(!Priority::P5Disposable.should_transmit());
456    }
457
458    #[test]
459    fn test_header_byte_roundtrip() {
460        let header = MessageHeader {
461            version: 1,
462            message_type: MessageType::Data,
463            priority: Priority::P2Important,
464            sequence: 0,
465            timestamp: 0,
466            context_version: 0,
467        };
468
469        let byte = header.encode_header_byte();
470        let (version, msg_type, priority) = MessageHeader::decode_header_byte(byte);
471
472        assert_eq!(version, 1);
473        assert_eq!(msg_type, Some(MessageType::Data));
474        assert_eq!(priority, Some(Priority::P2Important));
475    }
476
477    #[test]
478    fn test_header_serialization() {
479        let header = MessageHeader {
480            version: 1,
481            message_type: MessageType::Sync,
482            priority: Priority::P1Critical,
483            sequence: 12345,
484            timestamp: 67890,
485            context_version: 42,
486        };
487
488        let bytes = header.to_bytes();
489        let restored = MessageHeader::from_bytes(&bytes).unwrap();
490
491        assert_eq!(header.version, restored.version);
492        assert_eq!(header.message_type, restored.message_type);
493        assert_eq!(header.priority, restored.priority);
494        assert_eq!(header.sequence, restored.sequence);
495        assert_eq!(header.timestamp, restored.timestamp);
496        assert_eq!(header.context_version, restored.context_version);
497    }
498
499    #[test]
500    fn test_message_serialization() {
501        let message = EncodedMessage {
502            header: MessageHeader::default(),
503            payload: vec![0x00, 0x10, 0x42],
504        };
505
506        let bytes = message.to_bytes();
507        let restored = EncodedMessage::from_bytes(&bytes).unwrap();
508
509        assert_eq!(message.header.message_type, restored.header.message_type);
510        assert_eq!(message.payload, restored.payload);
511    }
512
513    #[test]
514    fn test_raw_data() {
515        let data = RawData::new(42.5, 12345);
516        assert_eq!(data.source_id, 0);
517        assert_eq!(data.value, 42.5);
518        assert_eq!(data.timestamp, 12345);
519        assert_eq!(data.raw_size(), 20);
520    }
521
522    #[test]
523    fn test_checksum_computation() {
524        let message = EncodedMessage {
525            header: MessageHeader::default(),
526            payload: vec![0x00, 0x10, 0x42],
527        };
528
529        let checksum1 = message.compute_checksum();
530        let checksum2 = message.compute_checksum();
531
532        // Same message should produce same checksum
533        assert_eq!(checksum1, checksum2);
534
535        // Different message should produce different checksum
536        let message2 = EncodedMessage {
537            header: MessageHeader::default(),
538            payload: vec![0x00, 0x10, 0x43],
539        };
540        let checksum3 = message2.compute_checksum();
541        assert_ne!(checksum1, checksum3);
542    }
543
544    #[test]
545    fn test_checksum_roundtrip() {
546        let message = EncodedMessage {
547            header: MessageHeader {
548                version: 1,
549                message_type: MessageType::Data,
550                priority: Priority::P2Important,
551                sequence: 42,
552                timestamp: 12345,
553                context_version: 7,
554            },
555            payload: vec![0x00, 0x10, 0x42, 0x55, 0xAA],
556        };
557
558        let bytes = message.to_bytes_with_checksum();
559        let restored = EncodedMessage::from_bytes_with_checksum(&bytes).unwrap();
560
561        assert_eq!(message.header.sequence, restored.header.sequence);
562        assert_eq!(message.header.timestamp, restored.header.timestamp);
563        assert_eq!(message.payload, restored.payload);
564    }
565
566    #[test]
567    fn test_checksum_corruption_detected() {
568        let message = EncodedMessage {
569            header: MessageHeader::default(),
570            payload: vec![0x00, 0x10, 0x42],
571        };
572
573        let mut bytes = message.to_bytes_with_checksum();
574
575        // Corrupt a byte in the payload
576        bytes[MessageHeader::SIZE] ^= 0xFF;
577
578        let result = EncodedMessage::from_bytes_with_checksum(&bytes);
579        assert!(matches!(result, Err(DecodeError::InvalidChecksum { .. })));
580    }
581
582    #[test]
583    fn test_checksum_buffer_too_short() {
584        let short_bytes = vec![0u8; MessageHeader::SIZE]; // No checksum
585
586        let result = EncodedMessage::from_bytes_with_checksum(&short_bytes);
587        assert!(matches!(result, Err(DecodeError::BufferTooShort { .. })));
588    }
589}