From fd23240633356edbb0268d1e4a2da130a313eb87 Mon Sep 17 00:00:00 2001 From: trivernis Date: Wed, 9 Sep 2020 11:23:47 +0200 Subject: [PATCH] Add Message struct and implementation Add a Message struct representing a single rpc message that has a field for the called method and the data as well as the length indicator and a crc32 checksum. The struct implements a way of serializing the message into bytes and constructing it from bytes with support for serde. Signed-off-by: trivernis --- .gitignore | 3 ++ Cargo.toml | 14 ++++++++ src/lib.rs | 23 +++++++++++++ src/message.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/message.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..408b8a5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +.idea \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..65e2b20 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "msgrpc" +version = "0.1.0" +authors = ["trivernis "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rmp = "0.8.9" +rmp-serde = "0.14.4" +crc = "1.8.1" +serde = "1.0.115" +byteorder = "1.3.4" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d92bda7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,23 @@ +pub mod message; + +#[cfg(test)] +mod tests { + use crate::message::Message; + + #[test] + fn it_creates_and_serializes_messages() { + let message = Message::new([0x01, 0x00, 0x10, 0x00], vec![0x00, 0x11, 0xFF, 0x00]); + + assert_eq!(message.to_bytes(), vec![0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x10, 0x00, 0x00, 0x11, 0xFF, 0x00, 0x96, 0xA9, 0xB2, 0x2E]) + } + + #[test] + fn it_deserializes_messages() { + let bytes = vec![0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x10, 0x00, 0x00, 0x11, 0xFF, 0x00, 0x96, 0xA9, 0xB2, 0x2E]; + let message = Message::from_bytes(&bytes); + let mut other_message = Message::new([0x01, 0x00, 0x10, 0x00], vec![0x00, 0x11, 0xFF, 0x00]); + other_message.crc = Some( 0x96A9B22E ); + + assert_eq!(message, Ok(other_message)) + } +} diff --git a/src/message.rs b/src/message.rs new file mode 100644 index 0000000..af2a80e --- /dev/null +++ b/src/message.rs @@ -0,0 +1,90 @@ +use serde::Serialize; +use rmp_serde::Serializer; +use byteorder::{BigEndian, ByteOrder}; +use crc::crc32; +use std::convert::TryInto; + +#[derive(Clone, Debug, PartialOrd, PartialEq)] +pub enum DeserializeError { + LengthError, + ChecksumError, +} + +#[derive(Clone, Debug, PartialOrd, PartialEq)] +pub struct Message { + length: u32, + method: [u8; 4], + data: Vec, + pub crc: Option, +} + +impl Message { + /// Creates a new message + pub fn new(method: [u8; 4], data: Vec) -> Self { + Self { + length: (12 + data.len()) as u32, + method, + data, + crc: None, + } + } + + /// Creates a new message with data being a serializable type + pub fn new_with_serialize(method: [u8; 4], data: impl Serialize) -> Self { + let mut buf = Vec::new(); + data.serialize(&mut Serializer::new(&mut buf)).unwrap(); + Self { + method, + length: (12 + buf.len()) as u32, + data: buf, + crc: None, + } + } + + /// Deserializes a vector of bytes into the message + pub fn from_bytes(bytes: &Vec) -> Result { + + if bytes.len() < 4 { + return Err(DeserializeError::LengthError) + } + + let length = BigEndian::read_u32(&bytes[0..4]); + + if bytes.len() != length as usize { + return Err(DeserializeError::LengthError) + } + + let crc = BigEndian::read_u32(&bytes[(length as usize - 4)..(length as usize)]); + let calc_crc = crc32::checksum_ieee(&bytes[0..(length as usize - 4)]); + + if calc_crc != crc { + return Err(DeserializeError::ChecksumError) + } + + let method: [u8; 4] = bytes[4..8].try_into().unwrap(); + let data = bytes[8..(length as usize - 4)].to_vec(); + + Ok(Self { + length, + method, + data, + crc: Some(crc) + }) + } + + /// Returns the serialized bytes version + pub fn to_bytes(&self) -> Vec { + let mut data = Vec::new(); + let mut length_raw = [0u8; 4]; + BigEndian::write_u32(&mut length_raw, self.length); + data.append(&mut length_raw.to_vec()); + data.append(&mut self.method.clone().to_vec()); + data.append(&mut self.data.clone()); + let crc_sum = crc32::checksum_ieee(&data); + let mut checksum_raw = [0u8; 4]; + BigEndian::write_u32(&mut checksum_raw, crc_sum); + data.append(&mut checksum_raw.to_vec()); + + data + } +}