lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

frame.rs (12028B)


      1 use crate::error::RadrootsSimplexSmpTransportError;
      2 use alloc::vec::Vec;
      3 use radroots_simplex_smp_proto::prelude::{
      4     RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, RadrootsSimplexSmpBrokerTransmission,
      5     RadrootsSimplexSmpCommandTransmission,
      6 };
      7 
      8 pub const RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE: usize = 16_384;
      9 pub const RADROOTS_SIMPLEX_SMP_TRANSPORT_PAD_BYTE: u8 = b'#';
     10 const PADDED_PAYLOAD_PREFIX_LEN: usize = 2;
     11 const MAX_TRANSPORT_PAYLOAD_LEN: usize =
     12     RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE - PADDED_PAYLOAD_PREFIX_LEN;
     13 
     14 #[derive(Debug, Clone, PartialEq, Eq)]
     15 pub struct RadrootsSimplexSmpTransportBlock {
     16     pub transmissions: Vec<Vec<u8>>,
     17 }
     18 
     19 impl RadrootsSimplexSmpTransportBlock {
     20     pub fn new(transmissions: Vec<Vec<u8>>) -> Result<Self, RadrootsSimplexSmpTransportError> {
     21         if transmissions.is_empty() {
     22             return Err(RadrootsSimplexSmpTransportError::EmptyTransportBlock);
     23         }
     24         if transmissions.len() > u8::MAX as usize {
     25             return Err(RadrootsSimplexSmpTransportError::TransmissionCountOverflow(
     26                 transmissions.len(),
     27             ));
     28         }
     29 
     30         let mut payload_len = 1_usize;
     31         for transmission in &transmissions {
     32             if transmission.len() > u16::MAX as usize {
     33                 return Err(RadrootsSimplexSmpTransportError::TransmissionTooLarge(
     34                     transmission.len(),
     35                 ));
     36             }
     37             payload_len = payload_len.checked_add(2 + transmission.len()).ok_or(
     38                 RadrootsSimplexSmpTransportError::TransportPayloadTooLarge(usize::MAX),
     39             )?;
     40         }
     41         if payload_len > MAX_TRANSPORT_PAYLOAD_LEN {
     42             return Err(RadrootsSimplexSmpTransportError::TransportPayloadTooLarge(
     43                 payload_len,
     44             ));
     45         }
     46 
     47         Ok(Self { transmissions })
     48     }
     49 
     50     pub fn encode(&self) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> {
     51         let payload = self.encode_payload()?;
     52         encode_padded_bytes(
     53             &payload,
     54             RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE,
     55             RADROOTS_SIMPLEX_SMP_TRANSPORT_PAD_BYTE,
     56         )
     57     }
     58 
     59     pub fn decode(bytes: &[u8]) -> Result<Self, RadrootsSimplexSmpTransportError> {
     60         let payload = decode_padded_bytes(
     61             bytes,
     62             RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE,
     63             RADROOTS_SIMPLEX_SMP_TRANSPORT_PAD_BYTE,
     64         )?;
     65         Self::from_payload(&payload)
     66     }
     67 
     68     pub fn encode_payload(&self) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> {
     69         encode_transport_payload(&self.transmissions)
     70     }
     71 
     72     pub fn from_payload(payload: &[u8]) -> Result<Self, RadrootsSimplexSmpTransportError> {
     73         let transmissions = decode_transport_payload(payload)?;
     74         Self::new(transmissions)
     75     }
     76 
     77     pub fn from_command_transmissions(
     78         transmissions: &[RadrootsSimplexSmpCommandTransmission],
     79         transport_version: u16,
     80     ) -> Result<Self, RadrootsSimplexSmpTransportError> {
     81         let encoded = transmissions
     82             .iter()
     83             .map(|transmission| transmission.encode_for_version(transport_version))
     84             .collect::<Result<Vec<_>, _>>()?;
     85         Self::new(encoded)
     86     }
     87 
     88     pub fn from_current_command_transmissions(
     89         transmissions: &[RadrootsSimplexSmpCommandTransmission],
     90     ) -> Result<Self, RadrootsSimplexSmpTransportError> {
     91         Self::from_command_transmissions(
     92             transmissions,
     93             RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION,
     94         )
     95     }
     96 
     97     pub fn decode_command_transmissions(
     98         &self,
     99         transport_version: u16,
    100     ) -> Result<Vec<RadrootsSimplexSmpCommandTransmission>, RadrootsSimplexSmpTransportError> {
    101         self.transmissions
    102             .iter()
    103             .map(|transmission| {
    104                 RadrootsSimplexSmpCommandTransmission::decode_for_version(
    105                     transport_version,
    106                     transmission,
    107                 )
    108                 .map_err(Into::into)
    109             })
    110             .collect()
    111     }
    112 
    113     pub fn from_broker_transmissions(
    114         transmissions: &[RadrootsSimplexSmpBrokerTransmission],
    115         transport_version: u16,
    116     ) -> Result<Self, RadrootsSimplexSmpTransportError> {
    117         let encoded = transmissions
    118             .iter()
    119             .map(|transmission| transmission.encode_for_version(transport_version))
    120             .collect::<Result<Vec<_>, _>>()?;
    121         Self::new(encoded)
    122     }
    123 
    124     pub fn decode_broker_transmissions(
    125         &self,
    126         transport_version: u16,
    127     ) -> Result<Vec<RadrootsSimplexSmpBrokerTransmission>, RadrootsSimplexSmpTransportError> {
    128         self.transmissions
    129             .iter()
    130             .map(|transmission| {
    131                 RadrootsSimplexSmpBrokerTransmission::decode_for_version(
    132                     transport_version,
    133                     transmission,
    134                 )
    135                 .map_err(Into::into)
    136             })
    137             .collect()
    138     }
    139 }
    140 
    141 pub fn encode_padded_bytes(
    142     payload: &[u8],
    143     padded_len: usize,
    144     pad_byte: u8,
    145 ) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> {
    146     let max_payload_len = padded_len.checked_sub(PADDED_PAYLOAD_PREFIX_LEN).ok_or(
    147         RadrootsSimplexSmpTransportError::TransportPayloadTooLarge(payload.len()),
    148     )?;
    149     if payload.len() > max_payload_len || payload.len() > u16::MAX as usize {
    150         return Err(RadrootsSimplexSmpTransportError::TransportPayloadTooLarge(
    151             payload.len(),
    152         ));
    153     }
    154 
    155     let mut buffer = Vec::with_capacity(padded_len);
    156     buffer.extend_from_slice(&(payload.len() as u16).to_be_bytes());
    157     buffer.extend_from_slice(payload);
    158     buffer.resize(padded_len, pad_byte);
    159     Ok(buffer)
    160 }
    161 
    162 pub fn decode_padded_bytes(
    163     bytes: &[u8],
    164     padded_len: usize,
    165     pad_byte: u8,
    166 ) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> {
    167     if bytes.len() != padded_len {
    168         return Err(RadrootsSimplexSmpTransportError::InvalidPaddedBlockLength {
    169             expected: padded_len,
    170             actual: bytes.len(),
    171         });
    172     }
    173     let Some(length_bytes) = bytes.get(..2) else {
    174         return Err(RadrootsSimplexSmpTransportError::InvalidPaddedBlockLength {
    175             expected: padded_len,
    176             actual: bytes.len(),
    177         });
    178     };
    179     let payload_len = u16::from_be_bytes([length_bytes[0], length_bytes[1]]) as usize;
    180     let max_payload_len = padded_len - PADDED_PAYLOAD_PREFIX_LEN;
    181     if payload_len > max_payload_len {
    182         return Err(RadrootsSimplexSmpTransportError::TransportPayloadTooLarge(
    183             payload_len,
    184         ));
    185     }
    186     let end = PADDED_PAYLOAD_PREFIX_LEN + payload_len;
    187     for (offset, byte) in bytes[end..].iter().enumerate() {
    188         if *byte != pad_byte {
    189             return Err(RadrootsSimplexSmpTransportError::InvalidPadding {
    190                 index: end + offset,
    191                 value: *byte,
    192             });
    193         }
    194     }
    195     Ok(bytes[PADDED_PAYLOAD_PREFIX_LEN..end].to_vec())
    196 }
    197 
    198 fn encode_transport_payload(
    199     transmissions: &[Vec<u8>],
    200 ) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> {
    201     if transmissions.is_empty() {
    202         return Err(RadrootsSimplexSmpTransportError::EmptyTransportBlock);
    203     }
    204     if transmissions.len() > u8::MAX as usize {
    205         return Err(RadrootsSimplexSmpTransportError::TransmissionCountOverflow(
    206             transmissions.len(),
    207         ));
    208     }
    209 
    210     let mut buffer = Vec::new();
    211     buffer.push(transmissions.len() as u8);
    212     for transmission in transmissions {
    213         if transmission.len() > u16::MAX as usize {
    214             return Err(RadrootsSimplexSmpTransportError::TransmissionTooLarge(
    215                 transmission.len(),
    216             ));
    217         }
    218         buffer.extend_from_slice(&(transmission.len() as u16).to_be_bytes());
    219         buffer.extend_from_slice(transmission);
    220     }
    221     if buffer.len() > MAX_TRANSPORT_PAYLOAD_LEN {
    222         return Err(RadrootsSimplexSmpTransportError::TransportPayloadTooLarge(
    223             buffer.len(),
    224         ));
    225     }
    226     Ok(buffer)
    227 }
    228 
    229 fn decode_transport_payload(
    230     payload: &[u8],
    231 ) -> Result<Vec<Vec<u8>>, RadrootsSimplexSmpTransportError> {
    232     let Some((&declared_count, mut remainder)) = payload.split_first() else {
    233         return Err(RadrootsSimplexSmpTransportError::EmptyTransportBlock);
    234     };
    235     if declared_count == 0 {
    236         return Err(RadrootsSimplexSmpTransportError::EmptyTransportBlock);
    237     }
    238 
    239     let mut transmissions = Vec::with_capacity(declared_count as usize);
    240     for _ in 0..declared_count {
    241         let Some(length_bytes) = remainder.get(..2) else {
    242             return Err(
    243                 radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpProtoError::UnexpectedEof
    244                     .into(),
    245             );
    246         };
    247         let transmission_len = u16::from_be_bytes([length_bytes[0], length_bytes[1]]) as usize;
    248         let Some(transmission) = remainder.get(2..2 + transmission_len) else {
    249             return Err(
    250                 radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpProtoError::UnexpectedEof
    251                     .into(),
    252             );
    253         };
    254         transmissions.push(transmission.to_vec());
    255         remainder = &remainder[2 + transmission_len..];
    256     }
    257 
    258     if !remainder.is_empty() {
    259         return Err(RadrootsSimplexSmpTransportError::TrailingTransportBytes(
    260             remainder.len(),
    261         ));
    262     }
    263     if transmissions.len() != declared_count as usize {
    264         return Err(
    265             RadrootsSimplexSmpTransportError::UnexpectedTransmissionCount {
    266                 declared: declared_count,
    267                 actual: transmissions.len(),
    268             },
    269         );
    270     }
    271     Ok(transmissions)
    272 }
    273 
    274 #[cfg(test)]
    275 mod tests {
    276     use super::*;
    277     use radroots_simplex_smp_proto::prelude::{
    278         RadrootsSimplexSmpCommand, RadrootsSimplexSmpCommandTransmission,
    279         RadrootsSimplexSmpCorrelationId,
    280     };
    281 
    282     #[test]
    283     fn roundtrips_command_transmissions_through_transport_block() {
    284         let transmissions = vec![
    285             RadrootsSimplexSmpCommandTransmission {
    286                 authorization: b"sig-a".to_vec(),
    287                 correlation_id: Some(RadrootsSimplexSmpCorrelationId::new([7_u8; 24])),
    288                 entity_id: b"queue-a".to_vec(),
    289                 command: RadrootsSimplexSmpCommand::Ping,
    290             },
    291             RadrootsSimplexSmpCommandTransmission {
    292                 authorization: b"sig-b".to_vec(),
    293                 correlation_id: Some(RadrootsSimplexSmpCorrelationId::new([9_u8; 24])),
    294                 entity_id: b"queue-b".to_vec(),
    295                 command: RadrootsSimplexSmpCommand::Get,
    296             },
    297         ];
    298 
    299         let block =
    300             RadrootsSimplexSmpTransportBlock::from_current_command_transmissions(&transmissions)
    301                 .unwrap();
    302         let encoded = block.encode().unwrap();
    303         assert_eq!(encoded.len(), RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE);
    304 
    305         let decoded = RadrootsSimplexSmpTransportBlock::decode(&encoded).unwrap();
    306         let roundtrip = decoded
    307             .decode_command_transmissions(RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION)
    308             .unwrap();
    309         assert_eq!(roundtrip, transmissions);
    310     }
    311 
    312     #[test]
    313     fn rejects_invalid_padding() {
    314         let block = RadrootsSimplexSmpTransportBlock::new(vec![b"PING".to_vec()]).unwrap();
    315         let mut encoded = block.encode().unwrap();
    316         encoded[RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE - 1] = b'!';
    317 
    318         let error = RadrootsSimplexSmpTransportBlock::decode(&encoded).unwrap_err();
    319         assert!(matches!(
    320             error,
    321             RadrootsSimplexSmpTransportError::InvalidPadding { .. }
    322         ));
    323     }
    324 
    325     #[test]
    326     fn roundtrips_transport_payload_without_padding() {
    327         let block =
    328             RadrootsSimplexSmpTransportBlock::new(vec![b"one".to_vec(), b"two".to_vec()]).unwrap();
    329         let payload = block.encode_payload().unwrap();
    330         let decoded = RadrootsSimplexSmpTransportBlock::from_payload(&payload).unwrap();
    331         assert_eq!(decoded, block);
    332     }
    333 }