summaryrefslogtreecommitdiff
path: root/src/message.rs
diff options
context:
space:
mode:
authorStuart Stock <stuart@int08h.com>2017-06-17 15:17:37 -0500
committerStuart Stock <stuart@int08h.com>2017-06-17 15:17:37 -0500
commit13d53fec92fddae9c312d323db81bbcb97adbe21 (patch)
treea00f72390e02aec158bd46e5605c0a2b8e74773e /src/message.rs
parentfea8a33a58e302ba83f86b80c3ea556847beec44 (diff)
downloadroughenough-13d53fec92fddae9c312d323db81bbcb97adbe21.zip
restructuring as a proper crate
Diffstat (limited to 'src/message.rs')
-rw-r--r--src/message.rs202
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());
+ }
+}