1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
//! A protocol message is defined as follows:
//!
//! | component | size (bits) | description |
//! | :------------ | :----------: | :------------------------------- |
//! | `msg_type` | 2 | The type (action) of the message |
//! | `msg_length` | 6 | The length of the entire message |
//! | `msg_payload` | 0 - 62 bytes | The payload of the message |
//! | checksum | 8 | The checksum of the message |
//!
//! Note that:
//! 1. the `msg_type` and `msg_length` form the first byte of the message,
//! which is the first two bits of the byte for `msg_type` and the following six bits for `msg_length`.
//! 2. the value of `msg_length` is the length of the entire message in bytes,
//! as 6 (unsigned) bits can hold value from `0` to `63`.
//! Its maximum value is defined as [`MAX_PROTOCOL_SIZE`].
//! So a single protocol message is capable of carrying [`MAX_PROTOCOL_SIZE`]` - 2 = 61` bytes as payload,
//! this value is denoted as [`MAX_PAYLOAD_SIZE`].
//! 3. the checksum is not included in the [`Message`] struct,
//! it is calculated and appended when serializing the message.
//! Similarly, the process of deserializing will also validate the checksum.
/// A byte is logically equivalent to an 8-bit unsigned integer.
pub type Byte = u8;
/// Maximum size of a protocol message.
/// It is counted in bytes.
pub const MAX_PROTOCOL_SIZE: usize = 0b0011_1111;
/// Maximum size of payload
/// comes from `MAX_PROTOCOL_SIZE - 2`.
pub const MAX_PAYLOAD_SIZE: usize = MAX_PROTOCOL_SIZE - 2;
/// The type (action) of a message which is the first two bits of a message.
///
/// | Bits | Type |
/// | :--: | :------- |
/// | `00` | Error |
/// | `01` | Request |
/// | `10` | Response |
///
/// Note that `11` is reserved.
///
// #[derive(Debug, PartialEq)]
pub enum MessageType {
/// Message type for error,
/// maps to `0b00` in the first two bits.
ERROR,
/// Message type for request,
/// maps to `0b01` in the first two bits.
REQUEST,
/// Message type for response,
/// maps to `0b10` in the first two bits.
RESPONSE,
}
impl MessageType {
/// Convert a [`MessageType`] to a [`Byte`] as defined in the protocol.
fn to_byte(&self) -> Byte {
match self {
MessageType::ERROR => 0b00,
MessageType::REQUEST => 0b01,
MessageType::RESPONSE => 0b10,
}
}
/// Convert a [`Byte`] to a [`MessageType`] as defined in the protocol.
pub fn from_byte(byte: Byte) -> Result<MessageType, &'static str> {
match byte {
0b00 => Ok(MessageType::ERROR),
0b01 => Ok(MessageType::REQUEST),
0b10 => Ok(MessageType::RESPONSE),
_ => Err("Invalid message type"),
}
}
}
/// A protocol message.
/// See the module-level documentation for more details.
pub struct Message {
/// The type (action) of the message,
/// which will be encoded in the first two bits of the message.
msg_type: MessageType,
/// The length of the entire message,
/// which will be encoded in the following 6 bits of the message.
msg_length: Byte,
/// The payload of the message,
/// should always be a multiple of 8 bits (1 byte).
msg_payload: Vec<Byte>,
}
impl Message {
/// Create a new message.
pub fn new(msg_type: MessageType, msg_length: Byte, msg_payload: Vec<Byte>) -> Message {
assert!(msg_length as usize <= MAX_PAYLOAD_SIZE);
Message {
msg_type,
msg_length,
msg_payload,
}
}
/// Create a new message in [`MessageType::REQUEST`] type.
pub fn new_request(msg_payload: Vec<Byte>) -> Message {
Message::new(
MessageType::REQUEST,
msg_payload.len() as Byte + 2,
msg_payload,
)
}
/// Get the first byte of the message,
/// which is the first two bits for `msg_type` and the following six bits for `msg_length`.
pub fn get_head(&self) -> Byte {
(self.msg_type.to_byte() << 6) | self.msg_length
}
/// Get the payload of the message in [`Vec<Byte>`].
pub fn get_payload(&self) -> &Vec<Byte> {
&self.msg_payload
}
pub fn get_type(&self) -> &MessageType {
&self.msg_type
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_message_type_to_byte() {
assert_eq!(MessageType::ERROR.to_byte(), 0b00);
assert_eq!(MessageType::REQUEST.to_byte(), 0b01);
assert_eq!(MessageType::RESPONSE.to_byte(), 0b10);
}
#[test]
fn test_message_type_from_byte() {
assert!(matches!(
MessageType::from_byte(0b00).unwrap(),
MessageType::ERROR
));
assert!(matches!(
MessageType::from_byte(0b01).unwrap(),
MessageType::REQUEST
));
assert!(matches!(
MessageType::from_byte(0b10).unwrap(),
MessageType::RESPONSE
));
assert!(MessageType::from_byte(0b11).is_err());
}
#[test]
fn test_message_new() {
let msg_type = MessageType::REQUEST;
let msg_length = 0b0000_0011;
let msg_payload = vec![0x01];
let payload_cpy = msg_payload.clone();
let msg = Message::new(msg_type, msg_length, msg_payload);
assert!(matches!(msg.msg_type, MessageType::REQUEST));
assert_eq!(msg.msg_length, msg_length);
assert_eq!(msg.msg_payload, payload_cpy);
}
#[test]
fn test_message_get_head() {
let msg_type = MessageType::REQUEST;
let msg_length: Byte = 0b0000_0100;
let msg_payload: Vec<Byte> = vec![0x01, 0x02];
let payload_cpy: Vec<Byte> = msg_payload.clone();
let msg = Message::new(msg_type, msg_length, msg_payload);
assert_eq!(msg.get_head(), 0b0100_0100);
let msg_type = MessageType::RESPONSE;
let msg_length: Byte = 0b0000_0011;
let msg = Message::new(msg_type, msg_length, payload_cpy);
assert_eq!(msg.get_head(), 0b1000_0011);
}
#[test]
fn test_message_get_payload() {
let msg_type = MessageType::REQUEST;
let msg_length: Byte = 0b0000_0100;
let msg_payload: Vec<Byte> = vec![0x01, 0x02];
let msg = Message::new(msg_type, msg_length, msg_payload);
assert_eq!(msg.get_payload(), &vec![0x01, 0x02]);
let msg_type = MessageType::RESPONSE;
let msg_length: Byte = 0b0000_0011;
let msg_payload: Vec<Byte> = vec![0x01];
let msg = Message::new(msg_type, msg_length, msg_payload);
assert_eq!(msg.get_payload(), &vec![0x01]);
}
}