coap-message 0.0.3

Interface to CoAP messages
Documentation
/// A minimal (primarily for testing / comparison) implementation of CoAP messages.
///
/// It only deals with the encoding of options; other common message properties stored in the
/// message header are currently ignored.

use crate::message::{Message, DecodeError};

type BufferedMessage = [u8];

/// This is only public because it is returned by BufferedMessage.get_optiter()
pub struct BufferedMessageIter<'a> {
    buf: &'a BufferedMessage,
    cursor: usize,
    lastnum: u16,
}

/// Given the first nibble of a variable-length field (option delta or option
/// length) and the rest of the message, return the numeric value of the field
/// and how many bytes to advance the read cursor, or None if the nibble was
/// reserved or the buffer too short.
// FIXME how do i properly get rid of this warning? afair it didn't show up when
// the code was inline.
#[allow(unreachable_patterns)]
fn decode_variable_length(firstnibble: u8, buffer: &[u8]) -> Result<(u16, usize), DecodeError> {
    match firstnibble {
        x if x < 13 => Ok((x as u16, 0)),
        15 => Err(DecodeError::InvalidOptionNibble),
        14 => {
            if buffer.len() < 1 {
                Err(DecodeError::TooShortForOption)
            } else {
                Ok((13 + buffer[0] as u16, 1))
            }
        }
        15 => {
            if buffer.len() < 2 {
                Err(DecodeError::TooShortForOption)
            } else {
                // FIXME could use the byteorder crate
                let byteordered = (buffer[0] as u16) * 256 + (buffer[1] as u16);
                match byteordered.overflowing_add(269) {
                    (x, false) => Ok((x, 2)),
                    (_, true) => Err(DecodeError::OptionOverflow),
                }
            }
        }
        _ => unreachable!(),
    }
}

impl<'a> Iterator for BufferedMessageIter<'a> {
    type Item = Result<(Option<u16>, &'a [u8]), DecodeError>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.cursor >= self.buf.len() {
            return None;
        }

        if self.buf[self.cursor] == 0xff {
            let payload = &self.buf[self.cursor + 1..];
            self.cursor = self.buf.len();
            return Some(Ok((None, payload)));
        }

        let short_delta = self.buf[self.cursor] >> 4;
        let short_length = self.buf[self.cursor] & 0x0f;

        self.cursor += 1;

        // I'd rather have decode_variable_length()?, but that won't wrap the Err in a Some
        let (delta, shift) = match decode_variable_length(short_delta, &self.buf[self.cursor..]) {
            Err(x) => return Some(Err(x)),
            Ok(x) => x
        };
        self.cursor += shift;
        let (length, shift) = match decode_variable_length(short_length, &self.buf[self.cursor..]) {
            Err(x) => return Some(Err(x)),
            Ok(x) => x
        };
        self.cursor += shift;

        self.lastnum = match self.lastnum.overflowing_add(delta) {
            (new, false) => new,
            (_, true) => return Some(Err(DecodeError::OptionOverflow)),
        };
        self.lastnum += delta;
        let newcursor = self.cursor + length as usize; // FIXME this assumes usize > u16
        if newcursor > self.buf.len() {
            return Some(Err(DecodeError::TooShortForOption));
        }

        let optiondata = &self.buf[self.cursor..newcursor];
        self.cursor = newcursor;

        return Some(Ok((Some(self.lastnum), optiondata)));
    }
}

impl<'a> Message<'a> for BufferedMessage {
    type IteratorType = BufferedMessageIter<'a>;

    fn get_optiter(&'a self) -> BufferedMessageIter<'a> {
        BufferedMessageIter {
            buf: self,
            cursor: 0,
            lastnum: 0,
        }
    }
}