Skip to content

Commit

Permalink
Use single Options struct for decoding options
Browse files Browse the repository at this point in the history
Signed-off-by: Sean Young <[email protected]>
  • Loading branch information
seanyoung committed Apr 7, 2024
1 parent adac1a4 commit b051b26
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 94 deletions.
12 changes: 9 additions & 3 deletions irp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,17 +187,23 @@ we compile the DFA state machine, for decoding. Then we create a decoder, which
needs some matching parameters, and then we can feed it input.

```rust
use irp::{Irp, InfraredData, Decoder};
use irp::{Irp, InfraredData, Options, Decoder};

fn main() {
let irp = Irp::parse(r#"
{36k,msb,889}<1,-1|-1,1>((1,~F:1:6,T:1,D:5,F:6,^114m)*,T=1-T)
[D:0..31,F:0..127,T@:0..1=0]"#)
.expect("parse should succeed");
let dfa = irp.compile(100, 3).expect("build dfa should succeed");
let options = Options {
aeps: 100,
eps: 30,
max_gap: 20000,
..Default::default()
};
let dfa = irp.compile(&options).expect("build dfa should succeed");
// Create a decoder with 100 microsecond tolerance, 30% relative tolerance,
// and 20000 microseconds maximum gap.
let mut decoder = Decoder::new(100, 30, 20000);
let mut decoder = Decoder::new(options);
for ir in InfraredData::from_rawir(
"+940 -860 +1790 -1750 +880 -880 +900 -890 +870 -900 +1750
-900 +890 -910 +840 -920 +870 -920 +840 -920 +870 -1810 +840 -125000").unwrap() {
Expand Down
25 changes: 4 additions & 21 deletions irp/src/build_bpf.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{
build_dfa::DFA,
build_nfa::{Action, Length},
Event, Expression,
Event, Expression, Options,
};
use inkwell::{
builder,
Expand All @@ -13,31 +13,14 @@ use inkwell::{
AddressSpace, IntPredicate, OptimizationLevel,
};
use once_cell::sync::OnceCell;
use std::{collections::HashMap, fs::File, io::Write, path::Path, rc::Rc};
use std::{collections::HashMap, fs::File, io::Write, rc::Rc};

static LLVM_INIT: OnceCell<()> = OnceCell::new();
static LLVM_TARGET_TRIPLE: &str = "bpf-unknown-unknown";

/// Options for code bpf decoder codegen
#[derive(Default)]
pub struct BpfOptions<'a> {
/// Name of the decoder
pub name: &'a str,
// Name of the source file
pub source: &'a str,
/// Protocol no which will be passed to bpf_rc_keydown()
pub protocol: u32,
/// If Some(path) the llvm IR intermediate file will be saved
pub llvm_ir: Option<&'a Path>,
/// If Some(path) the assembly intermediate file will be saved
pub assembly: Option<&'a Path>,
/// If Some(path) the object intermediate file will be saved
pub object: Option<&'a Path>,
}

impl DFA {
/// Compile the DFA to a BPF program for Linux kernel IR decoding
pub fn compile_bpf(&self, options: &BpfOptions) -> Result<(Vec<u8>, Vec<String>), String> {
pub fn compile_bpf(&self, options: &Options) -> Result<(Vec<u8>, Vec<String>), String> {
LLVM_INIT.get_or_init(|| {
Target::initialize_bpf(&Default::default());
});
Expand Down Expand Up @@ -134,7 +117,7 @@ impl DFA {
}

struct Builder<'a> {
options: &'a BpfOptions<'a>,
options: &'a Options<'a>,
dfa: &'a DFA,
module: Module<'a>,
builder: builder::Builder<'a>,
Expand Down
23 changes: 12 additions & 11 deletions irp/src/build_dfa.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{
build_nfa::{Action, Edge, Length, Vertex, NFA},
expression::clone_filter,
Expression, Irp,
Expression, Irp, Options,
};
use std::{
collections::{HashMap, HashSet},
Expand Down Expand Up @@ -34,15 +34,14 @@ struct DfaEdge {

impl NFA {
/// Build the DFA from the NFA
pub fn build_dfa(&self, aeps: u32, eps: u32) -> DFA {
pub fn build_dfa(&self, options: &Options) -> DFA {
let mut builder = Builder {
options,
verts: Vec::new(),
nfa_to_dfa: HashMap::new(),
edges: HashMap::new(),
nfa: self,
visited: HashSet::new(),
aeps,
eps,
};

builder.build();
Expand All @@ -62,21 +61,20 @@ impl DFA {

impl Irp {
/// Generate an DFA decoder state machine for this IRP
pub fn compile(&self, aeps: u32, eps: u32) -> Result<DFA, String> {
pub fn compile(&self, options: &Options) -> Result<DFA, String> {
let nfa = self.build_nfa()?;

Ok(nfa.build_dfa(aeps, eps))
Ok(nfa.build_dfa(options))
}
}

struct Builder<'a> {
options: &'a Options<'a>,
nfa: &'a NFA,
nfa_to_dfa: HashMap<usize, usize>,
edges: HashMap<DfaEdge, usize>,
verts: Vec<Vertex>,
visited: HashSet<usize>,
aeps: u32,
eps: u32,
}

impl<'a> Builder<'a> {
Expand Down Expand Up @@ -253,11 +251,14 @@ impl<'a> Builder<'a> {
if let Expression::Number(length) = length.as_ref() {
let length = *length as u32;
let min = std::cmp::min(
length.saturating_sub(self.aeps),
(length * (100 - self.eps)) / 100,
length.saturating_sub(self.options.aeps),
(length * (100 - self.options.eps)) / 100,
);

let max = std::cmp::min(length + self.aeps, (length * (100 + self.eps)) / 100);
let max = std::cmp::min(
length + self.options.aeps,
(length * (100 + self.options.eps)) / 100,
);

Length::Range(min, max)
} else {
Expand Down
46 changes: 28 additions & 18 deletions irp/src/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{
build_dfa::DFA,
build_nfa::{Action, Length, NFA},
InfraredData, Vartable,
InfraredData, Options, Vartable,
};
use crate::{build_nfa::Vertex, Event, Message};
use log::trace;
Expand All @@ -11,22 +11,17 @@ use std::{collections::HashMap, fmt, fmt::Write};
#[derive(Debug)]
pub struct Decoder<'a> {
pos: Vec<(usize, Vartable<'a>)>,
options: Options<'a>,
dfa: bool,
abs_tolerance: u32,
rel_tolerance: u32,
max_gap: u32,
}

impl<'a> Decoder<'a> {
/// Create a decoder with parameters. abs_tolerance is microseconds, rel_tolerance is in percentage,
/// and trailing gap is the minimum gap in microseconds which must follow.
pub fn new(abs_tolerance: u32, rel_tolerance: u32, max_gap: u32) -> Decoder<'a> {
/// Create a decoder with parameters.
pub fn new(options: Options<'a>) -> Decoder<'a> {
Decoder {
options,
pos: Vec::new(),
dfa: false,
abs_tolerance,
rel_tolerance,
max_gap,
}
}
}
Expand Down Expand Up @@ -112,11 +107,11 @@ impl<'a> Decoder<'a> {
fn tolerance_eq(&self, expected: u32, received: u32) -> bool {
let diff = expected.abs_diff(received);

if diff <= self.abs_tolerance {
if diff <= self.options.aeps {
true
} else {
// upcast to u64 since diff * 100 may overflow
(diff as u64 * 100) <= self.rel_tolerance as u64 * expected as u64
(diff as u64 * 100) <= self.options.eps as u64 * expected as u64
}
}

Expand Down Expand Up @@ -187,7 +182,7 @@ impl<'a> Decoder<'a> {
) -> bool {
match ir {
Some(InfraredData::Gap(received)) => {
if expected > self.max_gap as i64 && *received >= self.max_gap {
if expected > self.options.max_gap as i64 && *received >= self.options.max_gap {
trace!("large gap matched gap {} (expected {})", received, expected,);
*ir = None;
true
Expand Down Expand Up @@ -222,7 +217,7 @@ impl<'a> Decoder<'a> {
Some(InfraredData::Gap(received)) => {
let received = *received as i64;

if max > self.max_gap as i64 && received >= self.max_gap as i64 {
if max > self.options.max_gap as i64 && received >= self.options.max_gap as i64 {
trace!(
"large gap matched gap {} (range {}..{})",
received,
Expand Down Expand Up @@ -483,7 +478,7 @@ impl<'a> Decoder<'a> {
#[cfg(test)]
mod test {
use super::{Decoder, InfraredData};
use crate::{Event, Irp};
use crate::{Event, Irp, Options};
use std::collections::HashMap;

#[test]
Expand All @@ -495,7 +490,12 @@ mod test {

let mut res: Vec<(Event, HashMap<String, i64>)> = Vec::new();

let mut matcher = Decoder::new(100, 3, 20000);
let mut matcher = Decoder::new(Options {
aeps: 100,
eps: 3,
max_gap: 20000,
..Default::default()
});

for ir in InfraredData::from_rawir(
"+2400 -600 +600 -600 +600 -600 +1200 -600 +600 -600 +600 -600 +600 -600 +1200 -600 +1200 -31200").unwrap() {
Expand All @@ -518,7 +518,12 @@ mod test {

let mut res: Vec<(Event, HashMap<String, i64>)> = Vec::new();

let mut matcher = Decoder::new(100, 3, 20000);
let mut matcher = Decoder::new(Options {
aeps: 100,
eps: 3,
max_gap: 20000,
..Default::default()
});

for ir in InfraredData::from_rawir(
"+9024 -4512 +564 -564 +564 -564 +564 -564 +564 -564 +564 -564 +564 -564 +564 -1692 +564 -564 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -564 +564 -1692 +564 -564 +564 -564 +564 -1692 +564 -564 +564 -564 +564 -564 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -564 +564 -1692 +564 -1692 +564 -1692 +564 -564 +564 -564 +564 -39756").unwrap() {
Expand Down Expand Up @@ -587,7 +592,12 @@ mod test {

let mut res: Vec<(Event, HashMap<String, i64>)> = Vec::new();

let mut matcher = Decoder::new(100, 3, 20000);
let mut matcher = Decoder::new(Options {
aeps: 100,
eps: 3,
max_gap: 20000,
..Default::default()
});

for ir in InfraredData::from_rawir(
"+889 -889 +1778 -1778 +889 -889 +889 -889 +889 -889 +1778 -889 +889 -889 +889 -889 +889 -889 +889 -889 +889 -1778 +889 -89997").unwrap() {
Expand Down
27 changes: 24 additions & 3 deletions irp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ mod pronto;
pub mod protocols;
mod variants;

#[cfg(feature = "bpf")]
pub use build_bpf::BpfOptions;
pub use build_dfa::DFA;
pub use build_nfa::NFA;
pub use decoder::Decoder;

use num_rational::Rational64;
use std::{collections::HashMap, fmt, rc::Rc};
use std::{collections::HashMap, fmt, path::Path, rc::Rc};

#[derive(Debug, PartialEq, Default, Eq)]
/// An encoded raw infrared message
Expand Down Expand Up @@ -221,3 +219,26 @@ impl fmt::Display for Event {
}
}
}

/// Options for the decoder
#[derive(Default, Debug)]
pub struct Options<'a> {
/// Name of the decoder
pub name: &'a str,
// Name of the source file
pub source: &'a str,
/// Absolute tolerance in microsecondes
pub aeps: u32,
/// Relative tolerance in percentage
pub eps: u32,
/// Maximum gap the input will contains
pub max_gap: u32,
/// Protocol no which will be passed to bpf_rc_keydown()
pub protocol: u32,
/// If Some(path) the llvm IR intermediate file will be saved
pub llvm_ir: Option<&'a Path>,
/// If Some(path) the assembly intermediate file will be saved
pub assembly: Option<&'a Path>,
/// If Some(path) the object intermediate file will be saved
pub object: Option<&'a Path>,
}
19 changes: 10 additions & 9 deletions irp/tests/bpf_decoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use aya_obj::{
generated::{bpf_insn, bpf_map_type::BPF_MAP_TYPE_ARRAY},
Map, Object,
};
use irp::{BpfOptions, Irp, Vartable};
use irp::{Irp, Options, Vartable};
use itertools::Itertools;
use num::Integer;
use std::collections::{HashMap, HashSet};
Expand All @@ -18,18 +18,19 @@ fn rc5() {
let mut vars = Vartable::new();
vars.set("CODE".into(), 102);
let message = irp.encode_raw(vars, 0).unwrap();
let options = Options {
name: "rc5",
source: file!(),
aeps: 100,
eps: 3,
..Default::default()
};

let dfa = irp.compile(100, 3).unwrap();
let dfa = irp.compile(&options).unwrap();

dfa.dotgraphviz("lircd.dot");

let (object, vars) = dfa
.compile_bpf(&BpfOptions {
name: "rc5",
source: file!(),
..Default::default()
})
.unwrap();
let (object, vars) = dfa.compile_bpf(&options).unwrap();

let mut obj = Object::parse(&object).unwrap();
let text_sections = HashSet::new();
Expand Down
15 changes: 11 additions & 4 deletions irp/tests/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use irp::{
protocols::{parse, Protocol},
Decoder, Event, InfraredData, Irp, Message, Vartable,
Decoder, Event, InfraredData, Irp, Message, Options, Vartable,
};
use irptransmogrifier::{create_jvm, IrpTransmogrifierRender};
use itertools::Itertools;
Expand Down Expand Up @@ -450,15 +450,22 @@ fn decode_all() {
}
};

let dfa = nfa.build_dfa(10, 3);

let max_gap = if protocol.name == "Epson" {
100000
} else {
20000
};

let mut decoder = Decoder::new(10, 3, max_gap);
let options = Options {
aeps: 10,
eps: 3,
max_gap,
..Default::default()
};

let dfa = nfa.build_dfa(&options);

let mut decoder = Decoder::new(options);

let first = if irp.has_ending() { 1 } else { 0 };

Expand Down
Loading

0 comments on commit b051b26

Please sign in to comment.