Skip to content
Snippets Groups Projects
route.rs 9.84 KiB
Newer Older
use arrayvec::ArrayVec;

Stephen D's avatar
Stephen D committed
use crate::error::AppendNodeError;

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Route {
    pub max_hops: u8,
    path: ArrayVec<u8, 254>,
    has_future: bool,
}

impl Route {
    pub fn new(max_hops: u8) -> Self {
        let path = ArrayVec::new();

        Self {
            max_hops,
            path,
            has_future: false,
        }
    }

    /// Returns `None` if there isn't enough space for the callsign
    /// Returns `None` if attempting to pushed is_used=true after an existing is_used=false
    /// is_future=false when this callsign has digipeated
    /// is_future=true when this callsign is expected to be part of the route, but isn't yet
    /// see the specs for more information
    pub fn push_callsign(&mut self, callsign: &str, ssid: u8, is_future: bool) -> Option<()> {
        let len = callsign.as_bytes().len() + 2;
        let free_space = self.path.capacity() - self.path.len();

        if len > free_space {
            return None;
        }

        self.has_future = self.has_future || is_future;
        if self.has_future && !is_future {
            return None;
        }

        // safe to unwrap since we already did a length check
        self.path
            .try_extend_from_slice(callsign.as_bytes())
            .unwrap();
        self.path.push(if is_future { 0xFD } else { 0xFF });
        self.path.push(ssid);

        Some(())
    }

    /// Returns `None` if there isn't enough space
    pub fn push_internet(&mut self) -> Option<()> {
        let free_space = self.path.capacity() - self.path.len();

        if free_space < 1 {
            return None;
        }

        self.path.push(0xFE);

        Some(())
    }

Stephen D's avatar
Stephen D committed
    /// Append a callsign/ssid pair to the route, intelligently.
    /// I.e. replace future node if possible
    /// Returns an Err if the route is out of space, or appending the node doesn't make logical sense
    /// (there's a future node that doesn't match)
    pub fn append_node(&mut self, callsign: &str, ssid: u8) -> Result<(), AppendNodeError> {
        let mut new_route = Route::new(self.max_hops);

        let mut already_inserted = false;
        for rn in self.iter() {
            match rn {
                RouteNode::Internet => new_route
                    .push_internet()
                    .ok_or(AppendNodeError::RouteOverflow)?,
                RouteNode::Identity(rc, rs, is_future) => {
                    let us = rc == callsign && rs == ssid;

                    if is_future {
                        if us {
                            if already_inserted {
                                return Err(AppendNodeError::DuplicateNode);
                            } else {
                                new_route.push_callsign(callsign, ssid, false);
                                already_inserted = true;
                            }
                        } else {
                            if already_inserted {
                                new_route.push_callsign(rc, rs, true);
                            } else {
                                return Err(AppendNodeError::SetFuture);
                            }
                        }
                    } else {
                        if us {
                            return Err(AppendNodeError::DuplicateNode);
                        } else {
                            new_route.push_callsign(rc, rs, false);
                        }
                    }
                }
            }
        }

        if !already_inserted {
            new_route.push_callsign(callsign, ssid, false);
        }

        if new_route.iter().count() > usize::from(new_route.max_hops) {
            return Err(AppendNodeError::HopsOverflow);
Stephen D's avatar
Stephen D committed
        }

        *self = new_route;
        Ok(())
Stephen D's avatar
Stephen D committed
    }

    pub fn iter(&'_ self) -> RouteIter<'_> {
        RouteIter::new(self)
    }

    pub fn encode<'a>(&self, buf: &'a mut [u8]) -> Option<&'a [u8]> {
        let packet_len = self.path.len() + 1;
        let buf = buf.get_mut(0..(packet_len + 1))?;
        buf[0] = packet_len.try_into().unwrap();
        buf[1] = self.max_hops;
        buf[2..(packet_len + 1)].copy_from_slice(&self.path);

        Some(buf)
    }

    pub fn decode(data: &[u8]) -> Option<Self> {
        let len: usize = (*data.first()?).into();
        let data = data.get(1..(len + 1))?;

        let max_hops = *data.first()?;

        let mut path = ArrayVec::new();
        path.try_extend_from_slice(&data[1..]).unwrap();

        let has_future = data[1..].iter().any(|x| *x == 0xFD);

            max_hops,
            path,
            has_future,
        if UntrustedRouteIter::new(&s).any(|v| v.is_err()) {
    }
}

#[derive(Debug, Eq, PartialEq)]
pub enum RouteNode<'a> {
    Internet,
    Identity(&'a str, u8, bool),
}

#[derive(Debug)]
    route: &'a Route,
    i: usize,
    fn new(route: &'a Route) -> Self {
    // Returns Err(()) if invalid
    fn maybe_next(&mut self) -> Result<RouteNode<'a>, ()> {
        let i_start = self.i;
        self.i += 1;

        if *self.route.path.get(i_start).ok_or(())? == 0xFE {
            return Ok(RouteNode::Internet);
        while *self.route.path.get(self.i).ok_or(())? != 0xFD && self.route.path[self.i] != 0xFF {
        let callsign = core::str::from_utf8(self.route.path.get(i_start..self.i).ok_or(())?)
            .map_err(|_| ())?;
        let is_future = *self.route.path.get(self.i).ok_or(())? == 0xFD;
        if self.seen_future && !is_future {
            // past after future - not allowed
            return Err(());
        self.i += 1;
        let ssid = *self.route.path.get(self.i).ok_or(())?;
        Ok(RouteNode::Identity(callsign, ssid, is_future))
    type Item = Result<RouteNode<'a>, ()>;

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

        if self.i == self.route.path.len() {
            return None;
        }

        let r = self.maybe_next();

        if r.is_err() {
            self.dead = true;
    }
}

pub struct RouteIter<'a> {
    iter: UntrustedRouteIter<'a>,
}

impl<'a> RouteIter<'a> {
    fn new(route: &'a Route) -> Self {
        Self {
            iter: UntrustedRouteIter::new(route),
        }
    }
}

impl<'a> Iterator for RouteIter<'a> {
    type Item = RouteNode<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.iter.next()?.unwrap())
Stephen D's avatar
Stephen D committed

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn append_fails_when_existing_future() {
        let mut r = Route::new(5);
        r.push_callsign("C1", 0, false);
        r.push_callsign("C2", 0, false);
        r.push_callsign("C3", 0, true);

        assert_eq!(
            AppendNodeError::SetFuture,
            r.append_node("C3", 1).unwrap_err()
        );

        assert_eq!(
            AppendNodeError::SetFuture,
            r.append_node("C4", 0).unwrap_err()
        );
    }

    #[test]
    fn append_fails_when_would_exceed_max_hops() {
        let mut r = Route::new(3);
        r.append_node("C1", 0).unwrap();
        r.append_node("C2", 0).unwrap();
        r.append_node("C2", 1).unwrap();
        assert_eq!(
            AppendNodeError::HopsOverflow,
            r.append_node("C4", 0).unwrap_err()
        );
    }

    #[test]
    fn append_fails_when_already_in_route() {
        let mut r = Route::new(3);
        r.append_node("C1", 0).unwrap();

        assert_eq!(
            AppendNodeError::DuplicateNode,
            r.append_node("C1", 0).unwrap_err()
        );
    }

    #[test]
    fn append_overwrites_future() {
        let mut r = Route::new(5);
        r.push_callsign("C1", 0, false);
        r.push_callsign("C2", 0, false);
        r.push_callsign("C3", 0, true);
        r.push_callsign("C4", 0, true);

        r.append_node("C3", 0).unwrap();

        let mut iter = r.iter();
        assert_eq!(Some(RouteNode::Identity("C1", 0, false)), iter.next());
        assert_eq!(Some(RouteNode::Identity("C2", 0, false)), iter.next());
        assert_eq!(Some(RouteNode::Identity("C3", 0, false)), iter.next());
        assert_eq!(Some(RouteNode::Identity("C4", 0, true)), iter.next());
        assert_eq!(None, iter.next());
    }

    #[test]
    fn append_overwrites_future_even_when_at_hop_limit() {
        let mut r = Route::new(4);
        r.push_callsign("C1", 0, false);
        r.push_callsign("C2", 0, false);
        r.push_callsign("C3", 0, true);
        r.push_callsign("C4", 0, true);

        r.append_node("C3", 0).unwrap();
Stephen D's avatar
Stephen D committed

        let mut iter = r.iter();
        assert_eq!(Some(RouteNode::Identity("C1", 0, false)), iter.next());
        assert_eq!(Some(RouteNode::Identity("C2", 0, false)), iter.next());
        assert_eq!(Some(RouteNode::Identity("C3", 0, false)), iter.next());
        assert_eq!(Some(RouteNode::Identity("C4", 0, true)), iter.next());
        assert_eq!(None, iter.next());
    }

    #[test]
    fn append_appends_when_no_future() {
        let mut r = Route::new(5);
        r.push_callsign("C1", 0, false);
        r.push_callsign("C2", 0, false);
        r.push_callsign("C3", 0, false);

        r.append_node("C4", 0).unwrap();

        let mut iter = r.iter();
        assert_eq!(Some(RouteNode::Identity("C1", 0, false)), iter.next());
        assert_eq!(Some(RouteNode::Identity("C2", 0, false)), iter.next());
        assert_eq!(Some(RouteNode::Identity("C3", 0, false)), iter.next());
        assert_eq!(Some(RouteNode::Identity("C4", 0, false)), iter.next());
        assert_eq!(None, iter.next());
    }
}