pattern matching in rust no_std with u8 array

58 views Asked by At

Let's say I have an uart receiving fixed size data frames, and I want to reply other data frames according to pattern matching on the received frame. This pattern matching should be able to reply N times a frame, then M times another frame, then indefinitely a thrid frame for the same pattern received.

here is the types I think could fit the need, together with the wanted unit test behaviour:

use heapless::Vec;

pub struct Reply {
    pub until: usize,
    pub reply: Vec<u8, 5>,
}

pub struct Matching {
    pub pattern: Vec<u8, 5>,
    pub mask: Vec<u8, 5>,
    pub replies: Vec<Reply, 16>,
    pub matched_cnt: usize,
}

pub struct Config {
    pub matches: Vec<Matching, 16>,
}

impl Config {
    pub fn get_reply(&mut self, frame: [u8; 5]) -> Option<[u8; 5]> {
        todo!("find efficient implementation");
        None
    }
}

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

    #[test]
    fn test_get_reply() {
        let mut conf: Config = Config {
            matches: Vec::from_slice(&[
                Matching {
                    pattern: Vec::from_slice(&[0x55, 0xaa]).unwrap(),
                    mask: Vec::from_slice(&[0xff, 0xff]).unwrap(),
                    replies: Vec::from_slice(&[
                        Reply {
                            until: 2,
                            reply: Vec::from_slice(&[1u8; 5]).unwrap(),
                        },
                        Reply {
                            until: 3,
                            reply: Vec::from_slice(&[2u8; 5]).unwrap(),
                        },
                        Reply {
                            until: 0,
                            reply: Vec::from_slice(&[3u8; 5]).unwrap(),
                        },
                    ])
                    .unwrap(),
                    matched_cnt: 0,
                },
                Matching {
                    pattern: Vec::from_slice(&[0x55, 0xbb]).unwrap(),
                    mask: Vec::from_slice(&[0xff, 0xff]).unwrap(),
                    replies: Vec::from_slice(&[Reply {
                        until: 0,
                        reply: Vec::from_slice(&[4u8; 5]).unwrap(),
                    }])
                    .unwrap(),
                    matched_cnt: 0,
                },
            ])
            .unwrap(),
        };
        assert_eq!(conf.get_reply([0; 5]), None);
        assert_eq!(conf.get_reply([0x55, 0xaa, 1, 2, 3]), Some(&[1u8; 5]));
        assert_eq!(conf.get_reply([0x55, 0xaa, 2, 2, 3]), Some(&[1u8; 5]));
        assert_eq!(conf.get_reply([0x55, 0xa0, 2, 2, 3]), None);
        assert_eq!(conf.get_reply([0x55, 0xaa, 0, 2, 3]), Some(&[2u8; 5]));
        assert_eq!(conf.get_reply([0x55, 0xaa, 0, 2, 3]), Some(&[3u8; 5]));
        assert_eq!(conf.get_reply([0x55, 0xaa, 0, 2, 3]), Some(&[3u8; 5]));
        assert_eq!(conf.get_reply([0x55, 0xbb, 0, 0, 0]), Some(&[4u8; 5]));
    }
}

I am struggeling implementing the get_reply() with efficiency. Could you propose some hints ?

Edit: the best I can do is :

impl Matching {
    pub fn matching(&self, frame: [u8; 5]) -> bool {
        for (i, _) in self.pattern.iter().enumerate() {
            if self.pattern[i] & self.mask[i] != frame[i] & self.mask[i] {
                return false;
            }
        }
        self.matched_cnt += 1;
        true
    }

    pub fn matched(&mut self) -> &mut Self {
        self.matched_cnt += 1;
        self
    }
}

impl Config {
    pub fn get_reply(&mut self, frame: [u8; 5]) -> Option<&[u8; 5]> {
        self.matches
            .iter_mut()
            .filter(|m| m.pattern.len() == m.mask.len())
            .find(|m| m.matching(frame))
            .map(|m| m.matched())
            .and_then(|m| {
                m.replies
                    .iter()
                    .find(|r| r.until == 0 || m.matched_cnt <= r.until)
                    .map(|r| array_ref![r.reply.as_slice(), 0, 5] as &[u8; 5])
            })
    }
}

but the common iteration by index in matching() seems creepy...

0

There are 0 answers