diff options
author | Stuart Stock <stuart@int08h.com> | 2017-06-17 15:17:37 -0500 |
---|---|---|
committer | Stuart Stock <stuart@int08h.com> | 2017-06-17 15:17:37 -0500 |
commit | 13d53fec92fddae9c312d323db81bbcb97adbe21 (patch) | |
tree | a00f72390e02aec158bd46e5605c0a2b8e74773e /src/message.rs | |
parent | fea8a33a58e302ba83f86b80c3ea556847beec44 (diff) | |
download | roughenough-13d53fec92fddae9c312d323db81bbcb97adbe21.zip |
restructuring as a proper crate
Diffstat (limited to 'src/message.rs')
-rw-r--r-- | src/message.rs | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/message.rs b/src/message.rs new file mode 100644 index 0000000..4e7e84b --- /dev/null +++ b/src/message.rs @@ -0,0 +1,202 @@ +use std::io::Write; +use byteorder::{LittleEndian, WriteBytesExt}; + +use tag::Tag; +use error::Error; + +#[derive(Debug)] +pub struct RtMessage<'a> { + tags: Vec<Tag>, + values: Vec<&'a [u8]>, +} + +impl<'a> RtMessage<'a> { + pub fn new(num_fields: u8) -> Self { + RtMessage { + tags: Vec::with_capacity(num_fields as usize), + values: Vec::with_capacity(num_fields as usize) + } + } + + pub fn add_field(&mut self, tag: Tag, value: &'a [u8]) -> Result<(), Error> { + if let Some(last_tag) = self.tags.last() { + if tag <= *last_tag { + return Err(Error::TagNotStrictlyIncreasing(tag)); + } + } + + self.tags.push(tag); + self.values.push(value); + + Ok(()) + } + + pub fn num_fields(&self) -> u32 { + self.tags.len() as u32 + } + + pub fn encode(&self) -> Result<Vec<u8>, Error> { + let num_tags = self.tags.len(); + let mut out = Vec::with_capacity(self.encoded_size()); + + // number of tags + out.write_u32::<LittleEndian>(num_tags as u32)?; + + // offset(s) to values, IFF there are two or more tags + if num_tags > 1 { + let mut offset_sum = self.values[0].len(); + + for val in &self.values[1..] { + out.write_u32::<LittleEndian>(offset_sum as u32)?; + offset_sum += val.len(); + } + } + + // write tags + for tag in &self.tags { + out.write_all(tag.wire_value())?; + } + + // write values + for value in &self.values { + out.write_all(value)?; + } + + // check we wrote exactly what we expected + assert!(out.len() == self.encoded_size(), "unexpected length"); + + Ok(out) + } + + pub fn encoded_size(&self) -> usize { + let num_tags = self.tags.len(); + let tags_size = 4 * num_tags; + let offsets_size = if num_tags < 2 { 0 } else { 4 * (num_tags - 1) }; + let values_size: usize = self.values.iter().map(|&v| v.len()).sum(); + + 4 + tags_size + offsets_size + values_size + } +} + +#[cfg(test)] +mod test { + use std::io::{Cursor, Read}; + use byteorder::{LittleEndian, ReadBytesExt}; + use message::*; + use tag::Tag; + + #[test] + fn empty_message_size() { + let msg = RtMessage::new(0); + + assert_eq!(msg.num_fields(), 0); + // Empty message is 4 bytes, a single num_tags value + assert_eq!(msg.encoded_size(), 4); + } + + #[test] + fn single_field_message_size() { + let mut msg = RtMessage::new(1); + msg.add_field(Tag::NONC, "1234".as_bytes()).unwrap(); + + assert_eq!(msg.num_fields(), 1); + // Single tag message is 4 (num_tags) + 4 (NONC) + 4 (value) + assert_eq!(msg.encoded_size(), 12); + } + + #[test] + fn two_field_message_size() { + let mut msg = RtMessage::new(2); + msg.add_field(Tag::NONC, "1234".as_bytes()).unwrap(); + msg.add_field(Tag::PAD, "abcd".as_bytes()).unwrap(); + + assert_eq!(msg.num_fields(), 2); + // Two tag message + // 4 num_tags + // 8 (NONC, PAD) tags + // 4 PAD offset + // 8 values + assert_eq!(msg.encoded_size(), 24); + } + + #[test] + fn empty_message_encoding() { + let msg = RtMessage::new(0); + let mut encoded = Cursor::new(msg.encode().unwrap()); + + assert_eq!(encoded.read_u32::<LittleEndian>().unwrap(), 0); + } + + #[test] + fn single_field_message_encoding() { + let value = vec![b'a'; 64]; + let mut msg = RtMessage::new(1); + + msg.add_field(Tag::CERT, &value).unwrap(); + + let mut encoded = Cursor::new(msg.encode().unwrap()); + + // num tags + assert_eq!(encoded.read_u32::<LittleEndian>().unwrap(), 1); + + // CERT tag + let mut cert = [0u8; 4]; + encoded.read_exact(&mut cert).unwrap(); + assert_eq!(cert, Tag::CERT.wire_value()); + + // CERT value + let mut read_val = vec![0u8; 64]; + encoded.read_exact(&mut read_val).unwrap(); + assert_eq!(value, read_val); + + // Entire message was read + assert_eq!(encoded.position(), 72); + } + + #[test] + fn two_field_message_encoding() { + let dele_value = vec![b'a'; 24]; + let maxt_value = vec![b'z'; 32]; + + let mut msg = RtMessage::new(2); + msg.add_field(Tag::DELE, &dele_value).unwrap(); + msg.add_field(Tag::MAXT, &maxt_value).unwrap(); + + let mut encoded = Cursor::new(msg.encode().unwrap()); + // Wire encoding + // 4 num_tags + // 8 (DELE, MAXT) tags + // 4 MAXT offset + // 24 DELE value + // 32 MAXT value + + // num tags + assert_eq!(encoded.read_u32::<LittleEndian>().unwrap(), 2); + + // Offset past DELE value to start of MAXT value + assert_eq!(encoded.read_u32::<LittleEndian>().unwrap(), dele_value.len() as u32); + + // DELE tag + let mut dele = [0u8; 4]; + encoded.read_exact(&mut dele).unwrap(); + assert_eq!(dele, Tag::DELE.wire_value()); + + // MAXT tag + let mut maxt = [0u8; 4]; + encoded.read_exact(&mut maxt).unwrap(); + assert_eq!(maxt, Tag::MAXT.wire_value()); + + // DELE value + let mut read_dele_val = vec![0u8; 24]; + encoded.read_exact(&mut read_dele_val).unwrap(); + assert_eq!(dele_value, read_dele_val); + + // MAXT value + let mut read_maxt_val = vec![0u8; 32]; + encoded.read_exact(&mut read_maxt_val).unwrap(); + assert_eq!(maxt_value, read_maxt_val); + + // Everything was read + assert_eq!(encoded.position() as usize, msg.encoded_size()); + } +} |