Newer
Older
#[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, ident: RouteIdentity) -> Option<()> {
let len = ident.callsign.as_bytes().len() + 3;
let free_space = self.path.capacity() - self.path.len();
if len > free_space {
return None;
}
self.has_future = self.has_future || ident.is_future;
if self.has_future && !ident.is_future {
return None;
}
// safe to unwrap since we already did a length check
self.path
self.path.push(if ident.is_future { 0xFD } else { 0xFF });
self.path.push(ident.ssid);
self.path.push(ident.rssi);
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(())
}
/// 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,
rssi: Option<f64>,
) -> 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(ident) => {
let us = ident.callsign() == callsign && ident.ssid() == ssid;
if us {
if already_inserted {
return Err(AppendNodeError::DuplicateNode);
} else {
new_route
.push_callsign(RouteIdentity::new(callsign, ssid, rssi, false));
} else if already_inserted {
new_route.push_callsign(ident);
} else if us {
return Err(AppendNodeError::DuplicateNode);
new_route.push_callsign(RouteIdentity::new(callsign, ssid, rssi, false));
if new_route.iter().count() > usize::from(new_route.max_hops) {
return Err(AppendNodeError::HopsOverflow);
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 mut path = ArrayVec::new();
path.try_extend_from_slice(&data[1..]).unwrap();
let has_future = data[1..].iter().any(|x| *x == 0xFD);

Stephen D
committed
let s = Self {

Stephen D
committed
};
if UntrustedRouteIter::new(&s).any(|v| v.is_err()) {

Stephen D
committed
return None;
}
Some(s)
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum RouteNode<'a> {
Internet,
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
Identity(RouteIdentity<'a>),
}
#[derive(Debug, Eq, PartialEq)]
pub struct RouteIdentity<'a> {
callsign: &'a str,
ssid: u8,
rssi: u8,
is_future: bool,
}
impl<'a> RouteIdentity<'a> {
pub fn new(callsign: &'a str, ssid: u8, rssi: Option<f64>, is_future: bool) -> Self {
let rssi = match rssi {
Some(rssi) => (rssi * 1.5 + 240.0).max(1.0) as u8,
None => 0,
};
Self {
callsign,
ssid,
rssi,
is_future,
}
}
pub fn callsign(&self) -> &'a str {
self.callsign
}
pub fn ssid(&self) -> u8 {
self.ssid
}
pub fn rssi(&self) -> Option<f64> {
if self.rssi == 0 {
None
} else {
Some(((self.rssi as f64) - 240.0) / 1.5)
}
}
pub fn is_future(&self) -> bool {
self.is_future
}

Stephen D
committed
struct UntrustedRouteIter<'a> {

Stephen D
committed
seen_future: bool,
dead: bool,

Stephen D
committed
impl<'a> UntrustedRouteIter<'a> {

Stephen D
committed
Self {
route,
i: 0,
seen_future: false,
dead: false,
}
// 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(|_| ())?;

Stephen D
committed
let is_future = *self.route.path.get(self.i).ok_or(())? == 0xFD;

Stephen D
committed
if self.seen_future && !is_future {
// past after future - not allowed

Stephen D
committed
}
self.seen_future |= is_future;
let ssid = *self.route.path.get(self.i + 1).ok_or(())?;
let rssi = *self.route.path.get(self.i + 2).ok_or(())?;
self.i += 3;
Ok(RouteNode::Identity(RouteIdentity {
callsign,
ssid,
rssi,
is_future,
}))

Stephen D
committed
}
}
impl<'a> Iterator for UntrustedRouteIter<'a> {

Stephen D
committed
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;

Stephen D
committed
}

Stephen D
committed
}
}
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())
#[test]
fn route_rssi() {
let x = -66.0;
let r = RouteIdentity::new("C0", 23, Some(x), false);
assert_eq!(x, r.rssi().unwrap());
let x = 10.0;
let r = RouteIdentity::new("C0", 23, Some(x), false);
assert_eq!(x, r.rssi().unwrap());
let x = -158.0;
let r = RouteIdentity::new("C0", 23, Some(x), false);
assert_eq!(x, r.rssi().unwrap());
let r = RouteIdentity::new("C0", 23, None, false);
assert_eq!(None, r.rssi());
}
#[test]
fn append_fails_when_existing_future() {
let mut r = Route::new(5);
r.push_callsign(RouteIdentity::new("C1", 0, None, false))
.unwrap();
r.push_callsign(RouteIdentity::new("C2", 0, None, false))
.unwrap();
r.push_callsign(RouteIdentity::new("C3", 0, None, true))
.unwrap();
);
}
#[test]
fn append_fails_when_would_exceed_max_hops() {
let mut r = Route::new(3);
r.append_node("C1", 0, None).unwrap();
r.append_node("C2", 0, None).unwrap();
r.append_node("C2", 1, None).unwrap();
);
}
#[test]
fn append_fails_when_already_in_route() {
let mut r = Route::new(3);
);
}
#[test]
fn append_overwrites_future() {
let mut r = Route::new(5);
r.push_callsign(RouteIdentity::new("C1", 0, None, false))
.unwrap();
r.push_callsign(RouteIdentity::new("C2", 0, None, false))
.unwrap();
r.push_callsign(RouteIdentity::new("C3", 0, None, true))
.unwrap();
r.push_callsign(RouteIdentity::new("C4", 0, None, true))
.unwrap();
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new(
"C1", 0, None, false
))),
iter.next()
);
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new(
"C2", 0, None, false
))),
iter.next()
);
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new(
"C3", 0, None, false
))),
iter.next()
);
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new("C4", 0, None, 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(RouteIdentity::new("C1", 0, None, false))
.unwrap();
r.push_callsign(RouteIdentity::new("C2", 0, None, false))
.unwrap();
r.push_callsign(RouteIdentity::new("C3", 0, None, true))
.unwrap();
r.push_callsign(RouteIdentity::new("C4", 0, None, true))
.unwrap();
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new(
"C1", 0, None, false
))),
iter.next()
);
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new(
"C2", 0, None, false
))),
iter.next()
);
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new(
"C3", 0, None, false
))),
iter.next()
);
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new("C4", 0, None, true))),
iter.next()
);
assert_eq!(None, iter.next());
}
#[test]
fn append_appends_when_no_future() {
let mut r = Route::new(5);
r.push_callsign(RouteIdentity::new("C1", 0, None, false))
.unwrap();
r.push_callsign(RouteIdentity::new("C2", 0, None, false))
.unwrap();
r.push_callsign(RouteIdentity::new("C3", 0, None, false))
.unwrap();
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new(
"C1", 0, None, false
))),
iter.next()
);
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new(
"C2", 0, None, false
))),
iter.next()
);
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new(
"C3", 0, None, false
))),
iter.next()
);
assert_eq!(
Some(RouteNode::Identity(RouteIdentity::new(
"C4", 0, None, false
))),
iter.next()
);