<commit_before>use std::iter::repeat;
use std::io::{self, Write};
use time::{self, Timespec, Duration};
use tty::{Width, terminal_size};

macro_rules! printfl {
    ($($tt:tt)*) => {{
        print!($($tt)*);
        io::stdout().flush().ok().expect("flush() fail");
    }}
}

macro_rules! kb_fmt {
    ($n: ident) => {{
        let kb = 1024f64;
        match $n {
            $n if $n >= kb.powf(4_f64) => format!("{:.*} TB", 2, $n / kb.powf(4_f64)),
            $n if $n >= kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)),
            $n if $n >= kb.powf(2_f64) => format!("{:.*} MB", 2, $n / kb.powf(2_f64)),
            $n if $n >= kb => format!("{:.*} KB", 2, $n / kb),
            _ => format!("{:.*} B", 0, $n)
        }
    }}
}

macro_rules! repeat {
    ($s: expr, $n: expr) => {{
        &repeat($s).take($n).collect::<String>()
    }}
}

static FORMAT: &'static str = "[=>-]";

// Output type format, indicate which format wil be used in
// the speed box.
#[derive(Debug)]
pub enum Units {
    Default,
    Bytes,
}

#[derive(Debug)]
pub struct ProgressBar {
    start_time: Timespec,
    units: Units,
    total: usize,
    current: usize,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_remain: String,
    bar_end: String,
    pub is_finish: bool,
    pub show_bar: bool,
    pub show_speed: bool,
    pub show_percent: bool,
    pub show_counter: bool,
    pub show_time_left: bool,
}

impl ProgressBar {
    /// Create a new ProgressBar with default configuration.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use pbr::{ProgressBar, Units};
    ///
    /// let count = 1000;
    /// let mut pb = ProgressBar::new(count);
    /// pb.set_units(Units::Bytes);
    ///
    /// for _ in 0..count {
    ///    pb.inc();
    ///    thread::sleep_ms(100);
    /// }
    /// ```
    pub fn new(total: usize) -> ProgressBar {
        let mut pb = ProgressBar {
            total: total,
            current: 0,
            start_time: time::get_time(),
            units: Units::Default,
            is_finish: false,
            show_bar: true,
            show_speed: true,
            show_percent: true,
            show_counter: true,
            show_time_left: true,
            bar_start: String::new(),
            bar_current: String::new(),
            bar_current_n: String::new(),
            bar_remain: String::new(),
            bar_end: String::new(),
        };
        pb.format(FORMAT);
        pb
    }

    /// set units, default is simple numbers
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::{ProgressBar, Units};
    ///
    /// let mut pb = ProgressBar::new(n_bytes);
    /// pb.set_units(Units::Bytes);
    /// ```
    pub fn set_units(&mut self, u: Units) {
        self.units = u;
    }

    /// Set custom format to the drawing bar, default is `[=>-]`
    ///
    /// # Examples
    ///
    /// ```no_run
    /// let mut pb = ProgressBar::new(...);
    /// pb.format("[=>_]");
    /// ```
    pub fn format(&mut self, fmt: &str) {
        if fmt.len() >= 5 {
            let v: Vec<&str> = fmt.split("").collect();
            self.bar_start = v[1].to_string();
            self.bar_current = v[2].to_string();
            self.bar_current_n = v[3].to_string();
            self.bar_remain = v[4].to_string();
            self.bar_end = v[5].to_string();
        }
    }

    /// Add to current value
    ///
    /// # Examples
    ///
    /// ```no_run
    /// let mut pb = ProgressBar::new(10);
    /// pb.add(5);
    /// pb.finish();
    /// ```
    pub fn add(&mut self, i: usize) -> usize {
        self.current += i;
        if self.current <= self.total {
            self.draw()
        };
        self.current
    }

    /// Increment current value
    pub fn inc(&mut self) -> usize {
        return self.add(1);
    }

    fn draw(&self) {
        let tty_size = terminal_size();
        let width = if let Some((Width(w), _)) = tty_size {
            w as usize
        } else {
            80
        };
        let mut base = String::new();
        let mut suffix = String::new();
        let mut prefix = String::new();
        let mut out;
        // precent box
        if self.show_percent {
            let percent = self.current as f64 / (self.total as f64 / 100f64);
            suffix = suffix + &format!(" {:.*} % ", 2, percent);
        }
        // speed box
        if self.show_speed {
            let from_start = (time::get_time() - self.start_time).num_nanoseconds().unwrap() as f64;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as f64;
            let speed = self.current as f64 / (from_start / sec_nano);
            suffix = match self.units {
                Units::Default => suffix + &format!("{:.*}/s ", 2, speed),
                Units::Bytes => suffix + &format!("{}/s ", kb_fmt!(speed)),
            };
        }
        // time left box
        if self.show_time_left {
            let from_start = time::get_time() - self.start_time;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as i32;
            let per_entry = from_start / self.current as i32;
            let mut left = per_entry * (self.total - self.current) as i32;
            left = (left / sec_nano) * sec_nano;
            if left.num_seconds() > 0 {
                if left.num_seconds() < Duration::minutes(1).num_seconds() {
                    suffix = suffix + &format!("{}s", left.num_seconds());
                } else {
                    suffix = suffix + &format!("{}m", left.num_minutes());
                }
            }
        }
        // counter box
        if self.show_counter {
            let (c, t) = (self.current as f64, self.total as f64);
            prefix = match self.units {
                Units::Default => format!("{} / {} ", c, t),
                Units::Bytes => format!("{} / {} ", kb_fmt!(c), kb_fmt!(t)),
            };
        }
        // bar box
        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let curr_count =
                    ((self.current as f64 / self.total as f64) * size as f64).ceil() as usize;
                let rema_count = size - curr_count;
                base = self.bar_start.to_string();
                if rema_count > 0 {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count - 1) +
                           &self.bar_current_n;
                } else {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count);
                }
                base = base + repeat!(self.bar_remain.as_ref(), rema_count) + &self.bar_end;
            }
        }
        out = prefix + &base + &suffix;
        // pad
        if out.len() < width {
            let gap = width - out.len();
            out = out + repeat!(" ", gap);
        }
        // print
        printfl!("\r{}", out);
    }

    /// Calling finish manually will set current to total and draw
    /// the last time
    pub fn finish(&mut self) {
        if self.current < self.total {
            self.current = self.total;
            self.draw();
        }
        println!("");
        self.is_finish = true;
    }
}

// Implement io::Writer
impl Write for ProgressBar {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.add(n);
        Ok(n)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use pb::ProgressBar;

    #[test]
    fn add() {
        let mut pb = ProgressBar::new(10);
        pb.add(2);
        assert!(pb.current == 2,
                "should add the given `n` to current");
        assert!(pb.add(2) == pb.current,
                "add should return the current value");
    }

    #[test]
    fn inc() {
        let mut pb = ProgressBar::new(10);
        pb.inc();
        assert!(pb.current == 1, "should increment current by 1");
    }

    #[test]
    fn format() {
        let fmt = "[~> ]";
        let mut pb = ProgressBar::new(1);
        pb.format(fmt);
        assert!(pb.bar_start + &pb.bar_current + &pb.bar_current_n + &pb.bar_remain +
                &pb.bar_end == fmt);
    }

    #[test]
    fn finish() {
        let mut pb = ProgressBar::new(10);
        pb.finish();
        assert!(pb.current == pb.total,
                "should set current to total");
        assert!(pb.is_finish, "should set is_finish to true");
    }

    #[test]
    fn kb_fmt() {
        let kb = 1024f64;
        let mb = kb.powf(2f64);
        let gb = kb.powf(3f64);
        let tb = kb.powf(4f64);
        assert_eq!(kb_fmt!(kb), "1.00 KB");
        assert_eq!(kb_fmt!(mb), "1.00 MB");
        assert_eq!(kb_fmt!(gb), "1.00 GB");
        assert_eq!(kb_fmt!(tb), "1.00 TB");
    }
}
<commit_msg>fix(pb): unneeded return statement<commit_after>use std::iter::repeat;
use std::io::{self, Write};
use time::{self, Timespec, Duration};
use tty::{Width, terminal_size};

macro_rules! printfl {
    ($($tt:tt)*) => {{
        print!($($tt)*);
        io::stdout().flush().ok().expect("flush() fail");
    }}
}

macro_rules! kb_fmt {
    ($n: ident) => {{
        let kb = 1024f64;
        match $n {
            $n if $n >= kb.powf(4_f64) => format!("{:.*} TB", 2, $n / kb.powf(4_f64)),
            $n if $n >= kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)),
            $n if $n >= kb.powf(2_f64) => format!("{:.*} MB", 2, $n / kb.powf(2_f64)),
            $n if $n >= kb => format!("{:.*} KB", 2, $n / kb),
            _ => format!("{:.*} B", 0, $n)
        }
    }}
}

macro_rules! repeat {
    ($s: expr, $n: expr) => {{
        &repeat($s).take($n).collect::<String>()
    }}
}

static FORMAT: &'static str = "[=>-]";

// Output type format, indicate which format wil be used in
// the speed box.
#[derive(Debug)]
pub enum Units {
    Default,
    Bytes,
}

#[derive(Debug)]
pub struct ProgressBar {
    start_time: Timespec,
    units: Units,
    total: usize,
    current: usize,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_remain: String,
    bar_end: String,
    pub is_finish: bool,
    pub show_bar: bool,
    pub show_speed: bool,
    pub show_percent: bool,
    pub show_counter: bool,
    pub show_time_left: bool,
}

impl ProgressBar {
    /// Create a new ProgressBar with default configuration.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use pbr::{ProgressBar, Units};
    ///
    /// let count = 1000;
    /// let mut pb = ProgressBar::new(count);
    /// pb.set_units(Units::Bytes);
    ///
    /// for _ in 0..count {
    ///    pb.inc();
    ///    thread::sleep_ms(100);
    /// }
    /// ```
    pub fn new(total: usize) -> ProgressBar {
        let mut pb = ProgressBar {
            total: total,
            current: 0,
            start_time: time::get_time(),
            units: Units::Default,
            is_finish: false,
            show_bar: true,
            show_speed: true,
            show_percent: true,
            show_counter: true,
            show_time_left: true,
            bar_start: String::new(),
            bar_current: String::new(),
            bar_current_n: String::new(),
            bar_remain: String::new(),
            bar_end: String::new(),
        };
        pb.format(FORMAT);
        pb
    }

    /// set units, default is simple numbers
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::{ProgressBar, Units};
    ///
    /// let mut pb = ProgressBar::new(n_bytes);
    /// pb.set_units(Units::Bytes);
    /// ```
    pub fn set_units(&mut self, u: Units) {
        self.units = u;
    }

    /// Set custom format to the drawing bar, default is `[=>-]`
    ///
    /// # Examples
    ///
    /// ```no_run
    /// let mut pb = ProgressBar::new(...);
    /// pb.format("[=>_]");
    /// ```
    pub fn format(&mut self, fmt: &str) {
        if fmt.len() >= 5 {
            let v: Vec<&str> = fmt.split("").collect();
            self.bar_start = v[1].to_string();
            self.bar_current = v[2].to_string();
            self.bar_current_n = v[3].to_string();
            self.bar_remain = v[4].to_string();
            self.bar_end = v[5].to_string();
        }
    }

    /// Add to current value
    ///
    /// # Examples
    ///
    /// ```no_run
    /// let mut pb = ProgressBar::new(10);
    /// pb.add(5);
    /// pb.finish();
    /// ```
    pub fn add(&mut self, i: usize) -> usize {
        self.current += i;
        if self.current <= self.total {
            self.draw()
        };
        self.current
    }

    /// Increment current value
    pub fn inc(&mut self) -> usize {
        self.add(1)
    }

    fn draw(&self) {
        let tty_size = terminal_size();
        let width = if let Some((Width(w), _)) = tty_size {
            w as usize
        } else {
            80
        };
        let mut base = String::new();
        let mut suffix = String::new();
        let mut prefix = String::new();
        let mut out;
        // precent box
        if self.show_percent {
            let percent = self.current as f64 / (self.total as f64 / 100f64);
            suffix = suffix + &format!(" {:.*} % ", 2, percent);
        }
        // speed box
        if self.show_speed {
            let from_start = (time::get_time() - self.start_time).num_nanoseconds().unwrap() as f64;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as f64;
            let speed = self.current as f64 / (from_start / sec_nano);
            suffix = match self.units {
                Units::Default => suffix + &format!("{:.*}/s ", 2, speed),
                Units::Bytes => suffix + &format!("{}/s ", kb_fmt!(speed)),
            };
        }
        // time left box
        if self.show_time_left {
            let from_start = time::get_time() - self.start_time;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as i32;
            let per_entry = from_start / self.current as i32;
            let mut left = per_entry * (self.total - self.current) as i32;
            left = (left / sec_nano) * sec_nano;
            if left.num_seconds() > 0 {
                if left.num_seconds() < Duration::minutes(1).num_seconds() {
                    suffix = suffix + &format!("{}s", left.num_seconds());
                } else {
                    suffix = suffix + &format!("{}m", left.num_minutes());
                }
            }
        }
        // counter box
        if self.show_counter {
            let (c, t) = (self.current as f64, self.total as f64);
            prefix = match self.units {
                Units::Default => format!("{} / {} ", c, t),
                Units::Bytes => format!("{} / {} ", kb_fmt!(c), kb_fmt!(t)),
            };
        }
        // bar box
        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let curr_count =
                    ((self.current as f64 / self.total as f64) * size as f64).ceil() as usize;
                let rema_count = size - curr_count;
                base = self.bar_start.to_string();
                if rema_count > 0 {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count - 1) +
                           &self.bar_current_n;
                } else {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count);
                }
                base = base + repeat!(self.bar_remain.as_ref(), rema_count) + &self.bar_end;
            }
        }
        out = prefix + &base + &suffix;
        // pad
        if out.len() < width {
            let gap = width - out.len();
            out = out + repeat!(" ", gap);
        }
        // print
        printfl!("\r{}", out);
    }

    /// Calling finish manually will set current to total and draw
    /// the last time
    pub fn finish(&mut self) {
        if self.current < self.total {
            self.current = self.total;
            self.draw();
        }
        println!("");
        self.is_finish = true;
    }
}

// Implement io::Writer
impl Write for ProgressBar {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.add(n);
        Ok(n)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use pb::ProgressBar;

    #[test]
    fn add() {
        let mut pb = ProgressBar::new(10);
        pb.add(2);
        assert!(pb.current == 2,
                "should add the given `n` to current");
        assert!(pb.add(2) == pb.current,
                "add should return the current value");
    }

    #[test]
    fn inc() {
        let mut pb = ProgressBar::new(10);
        pb.inc();
        assert!(pb.current == 1, "should increment current by 1");
    }

    #[test]
    fn format() {
        let fmt = "[~> ]";
        let mut pb = ProgressBar::new(1);
        pb.format(fmt);
        assert!(pb.bar_start + &pb.bar_current + &pb.bar_current_n + &pb.bar_remain +
                &pb.bar_end == fmt);
    }

    #[test]
    fn finish() {
        let mut pb = ProgressBar::new(10);
        pb.finish();
        assert!(pb.current == pb.total,
                "should set current to total");
        assert!(pb.is_finish, "should set is_finish to true");
    }

    #[test]
    fn kb_fmt() {
        let kb = 1024f64;
        let mb = kb.powf(2f64);
        let gb = kb.powf(3f64);
        let tb = kb.powf(4f64);
        assert_eq!(kb_fmt!(kb), "1.00 KB");
        assert_eq!(kb_fmt!(mb), "1.00 MB");
        assert_eq!(kb_fmt!(gb), "1.00 GB");
        assert_eq!(kb_fmt!(tb), "1.00 TB");
    }
}
<|endoftext|>


<commit_before>use crate::Pixel::generic::RgbFormats;
use crate::Pixel::generic::RgbaFormats;
use crate::Pixel::generic::GrayFormats;

use rgb::alt::Gray;
use rgb::*;

/// Glue for backwards compatibility with old version of the crate
#[deprecated(note="Use APIs based on the newer PixelFormat")]
#[doc(hidden)]
pub trait PixelFormatBackCompatShim: PixelFormat {
    type Subpixel: Copy;
    fn input(arr: &[Self::Subpixel]) -> &[Self::InputPixel];
    fn output(arr: &mut [Self::Subpixel]) -> &mut [Self::OutputPixel];
}

/// Use [`Pixel`](Pixel) presets to specify pixel format.
///
/// Temporary object that adds pixels together
pub trait PixelFormat {
    /// Pixel type in the source image
    type InputPixel: Copy;
    /// Pixel type in the destination image (usually the same as Input)
    type OutputPixel;
    /// Temporary struct for the pixel in floating-point
    type Accumulator: Copy;

    /// Create new floating-point pixel
    fn new() -> Self::Accumulator;
    /// Add new pixel with a given weight (first axis)
    fn add(&self, acc: &mut Self::Accumulator, inp: Self::InputPixel, coeff: f32);
    /// Add bunch of accumulated pixels with a weight (second axis)
    fn add_acc(acc: &mut Self::Accumulator, inp: Self::Accumulator, coeff: f32);
    /// Finalize, convert to output pixel format
    fn into_pixel(&self, acc: Self::Accumulator) -> Self::OutputPixel;
}

#[allow(deprecated)]
impl<F: ToFloat> PixelFormatBackCompatShim for RgbFormats<F, F> {
    type Subpixel = F;

    fn input(arr: &[Self::Subpixel]) -> &[Self::InputPixel] {
        arr.as_rgb()
    }
    fn output(arr: &mut [Self::Subpixel]) -> &mut [Self::OutputPixel] {
        arr.as_rgb_mut()
    }
}

impl<F: ToFloat, T: ToFloat> PixelFormat for RgbFormats<T, F> {
    type InputPixel = RGB<F>;
    type OutputPixel = RGB<T>;
    type Accumulator = RGB<f32>;

    #[inline(always)]
    fn new() -> Self::Accumulator {
        RGB::new(0.,0.,0.)
    }

    #[inline(always)]
    fn add(&self, acc: &mut Self::Accumulator, inp: RGB<F>, coeff: f32) {
        acc.r += inp.r.to_float() * coeff;
        acc.g += inp.g.to_float() * coeff;
        acc.b += inp.b.to_float() * coeff;
    }

    #[inline(always)]
    fn add_acc(acc: &mut Self::Accumulator, inp: Self::Accumulator, coeff: f32) {
        acc.r += inp.r * coeff;
        acc.g += inp.g * coeff;
        acc.b += inp.b * coeff;
    }

    #[inline(always)]
    fn into_pixel(&self, acc: Self::Accumulator) -> RGB<T> {
        RGB {
            r: T::from_float(acc.r),
            g: T::from_float(acc.g),
            b: T::from_float(acc.b),
        }
    }
}

#[allow(deprecated)]
impl<F: ToFloat> PixelFormatBackCompatShim for RgbaFormats<F, F> {
    type Subpixel = F;

    fn input(arr: &[Self::Subpixel]) -> &[Self::InputPixel] {
        arr.as_rgba()
    }
    fn output(arr: &mut [Self::Subpixel]) -> &mut [Self::OutputPixel] {
        arr.as_rgba_mut()
    }
}

impl<F: ToFloat, T: ToFloat> PixelFormat for RgbaFormats<T, F> {
    type InputPixel = RGBA<F>;
    type OutputPixel = RGBA<T>;
    type Accumulator = RGBA<f32>;

    #[inline(always)]
    fn new() -> Self::Accumulator {
        RGBA::new(0.,0.,0.,0.)
    }

    #[inline(always)]
    fn add(&self, acc: &mut Self::Accumulator, inp: RGBA<F>, coeff: f32) {
        acc.r += inp.r.to_float() * coeff;
        acc.g += inp.g.to_float() * coeff;
        acc.b += inp.b.to_float() * coeff;
        acc.a += inp.a.to_float() * coeff;
    }

    #[inline(always)]
    fn add_acc(acc: &mut Self::Accumulator, inp: Self::Accumulator, coeff: f32) {
        acc.r += inp.r * coeff;
        acc.g += inp.g * coeff;
        acc.b += inp.b * coeff;
        acc.a += inp.a * coeff;
    }

    #[inline(always)]
    fn into_pixel(&self, acc: Self::Accumulator) -> RGBA<T> {
        RGBA {
            r: T::from_float(acc.r),
            g: T::from_float(acc.g),
            b: T::from_float(acc.b),
            a: T::from_float(acc.a),
        }
    }
}

#[allow(deprecated)]
impl<F: ToFloat> PixelFormatBackCompatShim for GrayFormats<F, F> {
    type Subpixel = F;

    fn input(arr: &[Self::Subpixel]) -> &[Self::InputPixel] {
        arr.as_gray()
    }
    fn output(arr: &mut [Self::Subpixel]) -> &mut [Self::OutputPixel] {
        arr.as_gray_mut()
    }
}

impl<F: ToFloat, T: ToFloat> PixelFormat for GrayFormats<F, T> {
    type InputPixel = Gray<F>;
    type OutputPixel = Gray<T>;
    type Accumulator = Gray<f32>;

    #[inline(always)]
    fn new() -> Self::Accumulator {
        Gray::new(0.)
    }

    #[inline(always)]
    fn add(&self, acc: &mut Self::Accumulator, inp: Gray<F>, coeff: f32) {
        acc.0 += inp.0.to_float() * coeff;
    }

    #[inline(always)]
    fn add_acc(acc: &mut Self::Accumulator, inp: Self::Accumulator, coeff: f32) {
        acc.0 += inp.0 * coeff;
    }

    #[inline(always)]
    fn into_pixel(&self, acc: Self::Accumulator) -> Gray<T> {
        Gray::new(T::from_float(acc.0))
    }
}

pub trait ToFloat: Sized + Copy + 'static {
    fn to_float(self) -> f32;
    fn from_float(f: f32) -> Self;
}

impl ToFloat for u8 {
    #[inline(always)]
    fn to_float(self) -> f32 {
        self as f32
    }

    #[inline(always)]
    fn from_float(f: f32) -> Self {
        f.round() as u8
    }
}

impl ToFloat for u16 {
    #[inline(always)]
    fn to_float(self) -> f32 {
        self as f32
    }

    #[inline(always)]
    fn from_float(f: f32) -> Self {
        f.round() as u16
    }
}

impl ToFloat for f32 {
    #[inline(always)]
    fn to_float(self) -> f32 {
        self
    }

    #[inline(always)]
    fn from_float(f: f32) -> Self {
        f
    }
}

impl ToFloat for f64 {
    #[inline(always)]
    fn to_float(self) -> f32 {
        self as f32
    }

    #[inline(always)]
    fn from_float(f: f32) -> Self {
        f as f64
    }
}
<commit_msg>Faster float conversion<commit_after>use crate::Pixel::generic::RgbFormats;
use crate::Pixel::generic::RgbaFormats;
use crate::Pixel::generic::GrayFormats;

use rgb::alt::Gray;
use rgb::*;

/// Glue for backwards compatibility with old version of the crate
#[deprecated(note="Use APIs based on the newer PixelFormat")]
#[doc(hidden)]
pub trait PixelFormatBackCompatShim: PixelFormat {
    type Subpixel: Copy;
    fn input(arr: &[Self::Subpixel]) -> &[Self::InputPixel];
    fn output(arr: &mut [Self::Subpixel]) -> &mut [Self::OutputPixel];
}

/// Use [`Pixel`](Pixel) presets to specify pixel format.
///
/// Temporary object that adds pixels together
pub trait PixelFormat {
    /// Pixel type in the source image
    type InputPixel: Copy;
    /// Pixel type in the destination image (usually the same as Input)
    type OutputPixel;
    /// Temporary struct for the pixel in floating-point
    type Accumulator: Copy;

    /// Create new floating-point pixel
    fn new() -> Self::Accumulator;
    /// Add new pixel with a given weight (first axis)
    fn add(&self, acc: &mut Self::Accumulator, inp: Self::InputPixel, coeff: f32);
    /// Add bunch of accumulated pixels with a weight (second axis)
    fn add_acc(acc: &mut Self::Accumulator, inp: Self::Accumulator, coeff: f32);
    /// Finalize, convert to output pixel format
    fn into_pixel(&self, acc: Self::Accumulator) -> Self::OutputPixel;
}

#[allow(deprecated)]
impl<F: ToFloat> PixelFormatBackCompatShim for RgbFormats<F, F> {
    type Subpixel = F;

    fn input(arr: &[Self::Subpixel]) -> &[Self::InputPixel] {
        arr.as_rgb()
    }
    fn output(arr: &mut [Self::Subpixel]) -> &mut [Self::OutputPixel] {
        arr.as_rgb_mut()
    }
}

impl<F: ToFloat, T: ToFloat> PixelFormat for RgbFormats<T, F> {
    type InputPixel = RGB<F>;
    type OutputPixel = RGB<T>;
    type Accumulator = RGB<f32>;

    #[inline(always)]
    fn new() -> Self::Accumulator {
        RGB::new(0.,0.,0.)
    }

    #[inline(always)]
    fn add(&self, acc: &mut Self::Accumulator, inp: RGB<F>, coeff: f32) {
        acc.r += inp.r.to_float() * coeff;
        acc.g += inp.g.to_float() * coeff;
        acc.b += inp.b.to_float() * coeff;
    }

    #[inline(always)]
    fn add_acc(acc: &mut Self::Accumulator, inp: Self::Accumulator, coeff: f32) {
        acc.r += inp.r * coeff;
        acc.g += inp.g * coeff;
        acc.b += inp.b * coeff;
    }

    #[inline(always)]
    fn into_pixel(&self, acc: Self::Accumulator) -> RGB<T> {
        RGB {
            r: T::from_float(acc.r),
            g: T::from_float(acc.g),
            b: T::from_float(acc.b),
        }
    }
}

#[allow(deprecated)]
impl<F: ToFloat> PixelFormatBackCompatShim for RgbaFormats<F, F> {
    type Subpixel = F;

    fn input(arr: &[Self::Subpixel]) -> &[Self::InputPixel] {
        arr.as_rgba()
    }
    fn output(arr: &mut [Self::Subpixel]) -> &mut [Self::OutputPixel] {
        arr.as_rgba_mut()
    }
}

impl<F: ToFloat, T: ToFloat> PixelFormat for RgbaFormats<T, F> {
    type InputPixel = RGBA<F>;
    type OutputPixel = RGBA<T>;
    type Accumulator = RGBA<f32>;

    #[inline(always)]
    fn new() -> Self::Accumulator {
        RGBA::new(0.,0.,0.,0.)
    }

    #[inline(always)]
    fn add(&self, acc: &mut Self::Accumulator, inp: RGBA<F>, coeff: f32) {
        acc.r += inp.r.to_float() * coeff;
        acc.g += inp.g.to_float() * coeff;
        acc.b += inp.b.to_float() * coeff;
        acc.a += inp.a.to_float() * coeff;
    }

    #[inline(always)]
    fn add_acc(acc: &mut Self::Accumulator, inp: Self::Accumulator, coeff: f32) {
        acc.r += inp.r * coeff;
        acc.g += inp.g * coeff;
        acc.b += inp.b * coeff;
        acc.a += inp.a * coeff;
    }

    #[inline(always)]
    fn into_pixel(&self, acc: Self::Accumulator) -> RGBA<T> {
        RGBA {
            r: T::from_float(acc.r),
            g: T::from_float(acc.g),
            b: T::from_float(acc.b),
            a: T::from_float(acc.a),
        }
    }
}

#[allow(deprecated)]
impl<F: ToFloat> PixelFormatBackCompatShim for GrayFormats<F, F> {
    type Subpixel = F;

    fn input(arr: &[Self::Subpixel]) -> &[Self::InputPixel] {
        arr.as_gray()
    }
    fn output(arr: &mut [Self::Subpixel]) -> &mut [Self::OutputPixel] {
        arr.as_gray_mut()
    }
}

impl<F: ToFloat, T: ToFloat> PixelFormat for GrayFormats<F, T> {
    type InputPixel = Gray<F>;
    type OutputPixel = Gray<T>;
    type Accumulator = Gray<f32>;

    #[inline(always)]
    fn new() -> Self::Accumulator {
        Gray::new(0.)
    }

    #[inline(always)]
    fn add(&self, acc: &mut Self::Accumulator, inp: Gray<F>, coeff: f32) {
        acc.0 += inp.0.to_float() * coeff;
    }

    #[inline(always)]
    fn add_acc(acc: &mut Self::Accumulator, inp: Self::Accumulator, coeff: f32) {
        acc.0 += inp.0 * coeff;
    }

    #[inline(always)]
    fn into_pixel(&self, acc: Self::Accumulator) -> Gray<T> {
        Gray::new(T::from_float(acc.0))
    }
}

pub trait ToFloat: Sized + Copy + 'static {
    fn to_float(self) -> f32;
    fn from_float(f: f32) -> Self;
}

impl ToFloat for u8 {
    #[inline(always)]
    fn to_float(self) -> f32 {
        self as f32
    }

    #[inline(always)]
    fn from_float(f: f32) -> Self {
        unsafe {
            (0f32).max(f.round()).min(255.).to_int_unchecked()
        }
    }
}

impl ToFloat for u16 {
    #[inline(always)]
    fn to_float(self) -> f32 {
        self as f32
    }

    #[inline(always)]
    fn from_float(f: f32) -> Self {
        unsafe {
            (0f32).max(f.round()).min(65535.).to_int_unchecked()
        }
    }
}

impl ToFloat for f32 {
    #[inline(always)]
    fn to_float(self) -> f32 {
        self
    }

    #[inline(always)]
    fn from_float(f: f32) -> Self {
        f
    }
}

impl ToFloat for f64 {
    #[inline(always)]
    fn to_float(self) -> f32 {
        self as f32
    }

    #[inline(always)]
    fn from_float(f: f32) -> Self {
        f as f64
    }
}
<|endoftext|>


<commit_before>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use services::*;
use selector::*;
use values::{ Value, Range, TypeError };
use util::{ Exactly, Id };

use transformable_channels::mpsc::*;

use std::collections::HashMap;

/// An error that arose during interaction with either a device, an adapter or the
/// adapter manager
#[derive(Serialize, Debug, Clone)]
pub enum Error {
    /// Attempting to fetch a value from a Channel<Getter> that doesn't support this operation.
    GetterDoesNotSupportPolling(Id<Getter>),

    /// Attempting to watch a value from a Channel<Getter> that doesn't support this operation.
    GetterDoesNotSupportWatching(Id<Getter>),

    /// Attempting to watch all values from a Channel<Getter> that requires a filter.
    /// For instance, some Channel<Getter> may be updated 60 times per second. Attempting to
    /// watch all values could easily exceed the capacity of the network or exhaust the battery.
    /// In such a case, the adapter should return this error.
    GetterRequiresThresholdForWatching(Id<Getter>),

    /// Attempting to send a value with a wrong type.
    TypeError(TypeError),

    /// Attempting to use an inconsistent range. For instance, one with `min > max`.
    RangeError(Range),

    /// Attempting to send an invalid value. For instance, a time of day larger than 24h.
    InvalidValue(Value),

    /// An error internal to the foxbox or an adapter. Normally, these errors should never
    /// arise from the high-level API.
    InternalError(InternalError),
}

#[derive(Serialize, Debug, Clone)]
pub enum InternalError {
    /// Attempting to fetch or watch a getter that isn't registered.
    NoSuchGetter(Id<Getter>),
    /// Attempting to send values to a setter that isn't registered.
    NoSuchSetter(Id<Setter>),
    /// Attempting to access a service that isn't registered.
    NoSuchService(Id<ServiceId>),
    /// Attempting to access an adapter that isn't registered.
    NoSuchAdapter(Id<AdapterId>),

    /// Attempting to register a getter with an id that is already used.
    DuplicateGetter(Id<Getter>),
    /// Attempting to register a setter with an id that is already used.
    DuplicateSetter(Id<Setter>),
    /// Attempting to register a service with an id that is already used.
    DuplicateService(Id<ServiceId>),
    /// Attempting to register an adapter with an id that is already used.
    DuplicateAdapter(Id<AdapterId>),

    /// Attempting to register a channel with an adapter that doesn't match that of its service.
    ConflictingAdapter(Id<AdapterId>, Id<AdapterId>),

    /// Attempting to register a service in an invalid initial state. Typically, a service that
    /// pretends that it already has channels.
    InvalidInitialService,
}

/// An event during watching.
#[derive(Serialize, Debug, Clone)]
pub enum WatchEvent {
    /// If a range was specified when we registered for watching, `EnterRange` is fired whenever
    /// we enter this range. If `Always` was specified, `EnterRange` is fired whenever a new value
    /// is available. Otherwise, never fired.
    EnterRange {
        /// The channel that sent the value.
        from: Id<Getter>,

        /// The actual value.
        value: Value
    },

    /// If a range was specified when we registered for watching, `ExitRange` is fired whenever
    /// we exit this range. Otherwise, never fired.
    ExitRange {
        /// The channel that sent the value.
        from: Id<Getter>,

        /// The actual value.
        value: Value
    },

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// removed. Payload is the id of the device that was removed.
    GetterRemoved(Id<Getter>),

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// added. Payload is the id of the device that was added.
    GetterAdded(Id<Getter>),

    /// One of the channels encountered an error during initialization.
    /// This channel will not be watched, but other channels will remain
    /// watched.
    InitializationError {
        channel: Id<Getter>,
        error: Error
    },
}

/// A bunch of results coming from different sources.
pub type ResultMap<K, T, E> = HashMap<K, Result<T, E>>;

/// A bunch of instructions, going to different targets.
pub type TargetMap<K, T> = Vec<(Vec<K>, T)>;

/// A handle to the public API.
pub trait API: Send {
    /// Get the metadata on services matching some conditions.
    ///
    /// A call to `API::get_services(vec![req1, req2, ...])` will return
    /// the metadata on all services matching _either_ `req1` or `req2`
    /// or ...
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to a `Vec<ServiceSelector>`. See
    /// the implementation of `ServiceSelector` for details.
    ///
    /// ### Example
    ///
    /// Selector all doors in the entrance (tags `door`, `entrance`)
    /// that support setter channel `OpenClose`
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door"],
    ///   "getters": [
    ///     {
    ///       "kind": {
    ///         "Exactly": {
    ///           "OpenClose": []
    ///         }
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    ///
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing an array of `Service`. See the implementation
    /// of `Service` for details.
    ///
    /// ### Example
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door", "somevendor"],
    ///   "id: "some-service-id",
    ///   "getters": [],
    ///   "setters": [
    ///     "tags": [...],
    ///     "id": "some-channel-id",
    ///     "service": "some-service-id",
    ///     "last_seen": "some-date",
    ///     "mechanism": {
    ///       "Setter":  {
    ///         "kind": {
    ///           "OnOff": []
    ///         },
    ///         "push": [5000],
    ///         "updated": "some-date",
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    fn get_services(&self, Vec<ServiceSelector>) -> Vec<Service>;

    /// Label a set of services with a set of tags.
    ///
    /// A call to `API::put_service_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the services matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of services
    /// matching any of the selectors.
    ///
    /// Some of the services may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/tag`
    ///
    /// ## Getters
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<ServiceSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON string representing a number.
    fn add_service_tags(&self, selectors: Vec<ServiceSelector>, tags: Vec<Id<TagId>>) -> usize;

    /// Remove a set of tags from a set of services.
    ///
    /// A call to `API::delete_service_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the services matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of services
    /// matching any of the selectors.
    ///
    /// Some of the services may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/services/tag`
    ///
    /// ## Getters
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<ServiceSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn remove_service_tags(&self, selectors: Vec<ServiceSelector>, tags: Vec<Id<TagId>>) -> usize;

    /// Get a list of getters matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/channels`
    fn get_getter_channels(&self, selectors: Vec<GetterSelector>) -> Vec<Channel<Getter>>;
    fn get_setter_channels(&self, selectors: Vec<SetterSelector>) -> Vec<Channel<Setter>>;

    /// Label a set of channels with a set of tags.
    ///
    /// A call to `API::put_{getter, setter}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the channels matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of channels
    /// matching any of the selectors.
    ///
    /// Some of the channels may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if channels
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/channels/tag`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn add_getter_tags(&self, selectors: Vec<GetterSelector>, tags: Vec<Id<TagId>>) -> usize;
    fn add_setter_tags(&self, selectors: Vec<SetterSelector>, tags: Vec<Id<TagId>>) -> usize;

    /// Remove a set of tags from a set of channels.
    ///
    /// A call to `API::delete_{getter, setter}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the channels matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of channels
    /// matching any of the selectors.
    ///
    /// Some of the channels may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if channels
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/channels/tag`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn remove_getter_tags(&self, selectors: Vec<GetterSelector>, tags: Vec<Id<TagId>>) -> usize;
    fn remove_setter_tags(&self, selectors: Vec<SetterSelector>, tags: Vec<Id<TagId>>) -> usize;

    /// Read the latest value from a set of channels
    ///
    /// # REST API
    ///
    /// `GET /api/v1/channels/value`
    fn fetch_values(&self, Vec<GetterSelector>) -> ResultMap<Id<Getter>, Option<Value>, Error>;

    /// Send a bunch of values to a set of channels
    ///
    /// # REST API
    ///
    /// `POST /api/v1/channels/value`
    fn send_values(&self, TargetMap<SetterSelector, Value>) -> ResultMap<Id<Setter>, (), Error>;

    /// Watch for changes from channels.
    ///
    /// This method registers a closure to watch over events on a set of channels. Argument `watch`
    /// specifies which channels to watch and which events are of interest.
    ///
    /// - If argument `Exactly<Range>` is `Exactly::Exactly(range)`, the watch is interested in
    /// values coming from these channels, if they fall within `range`. This is the most common
    /// case. In this case, `on_event` receives `WatcherEvent::GetterAdded`,
    /// `WatcherEvent::GetterRemoved` and `WatcherEvent::Value`, whenever a new value is available
    /// in the range. Values that do not have the same type as `range` are dropped silently.
    ///
    /// - If argument `Exactly<Range>` is `Exactly::Never`, the watch is not interested in the
    /// values coming from these channels, only in connection/disconnection events. Argument
    /// `on_event` receives `WatchEvent::GetterAdded` and `WatchEvent::GetterRemoved`.
    ///
    /// - If the `Exactly<Range>` argument is `Exactly::Always`, the watch is interested in
    /// receiving *every single value coming from the channels*. This is very rarely a good idea.
    /// Many devices may reject such requests.
    ///
    /// The watcher is disconnected once the `WatchGuard` returned by this method is dropped.
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/channels/watch`
    fn register_channel_watch(&self, watch: TargetMap<GetterSelector, Exactly<Range>>,
            on_event: Box<ExtSender<WatchEvent>>) -> Self::WatchGuard;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}
<commit_msg>Renamed register_channel_watch => watch_values<commit_after>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use services::*;
use selector::*;
use values::{ Value, Range, TypeError };
use util::{ Exactly, Id };

use transformable_channels::mpsc::*;

use std::collections::HashMap;

/// An error that arose during interaction with either a device, an adapter or the
/// adapter manager
#[derive(Serialize, Debug, Clone)]
pub enum Error {
    /// Attempting to fetch a value from a Channel<Getter> that doesn't support this operation.
    GetterDoesNotSupportPolling(Id<Getter>),

    /// Attempting to watch a value from a Channel<Getter> that doesn't support this operation.
    GetterDoesNotSupportWatching(Id<Getter>),

    /// Attempting to watch all values from a Channel<Getter> that requires a filter.
    /// For instance, some Channel<Getter> may be updated 60 times per second. Attempting to
    /// watch all values could easily exceed the capacity of the network or exhaust the battery.
    /// In such a case, the adapter should return this error.
    GetterRequiresThresholdForWatching(Id<Getter>),

    /// Attempting to send a value with a wrong type.
    TypeError(TypeError),

    /// Attempting to use an inconsistent range. For instance, one with `min > max`.
    RangeError(Range),

    /// Attempting to send an invalid value. For instance, a time of day larger than 24h.
    InvalidValue(Value),

    /// An error internal to the foxbox or an adapter. Normally, these errors should never
    /// arise from the high-level API.
    InternalError(InternalError),
}

#[derive(Serialize, Debug, Clone)]
pub enum InternalError {
    /// Attempting to fetch or watch a getter that isn't registered.
    NoSuchGetter(Id<Getter>),
    /// Attempting to send values to a setter that isn't registered.
    NoSuchSetter(Id<Setter>),
    /// Attempting to access a service that isn't registered.
    NoSuchService(Id<ServiceId>),
    /// Attempting to access an adapter that isn't registered.
    NoSuchAdapter(Id<AdapterId>),

    /// Attempting to register a getter with an id that is already used.
    DuplicateGetter(Id<Getter>),
    /// Attempting to register a setter with an id that is already used.
    DuplicateSetter(Id<Setter>),
    /// Attempting to register a service with an id that is already used.
    DuplicateService(Id<ServiceId>),
    /// Attempting to register an adapter with an id that is already used.
    DuplicateAdapter(Id<AdapterId>),

    /// Attempting to register a channel with an adapter that doesn't match that of its service.
    ConflictingAdapter(Id<AdapterId>, Id<AdapterId>),

    /// Attempting to register a service in an invalid initial state. Typically, a service that
    /// pretends that it already has channels.
    InvalidInitialService,
}

/// An event during watching.
#[derive(Serialize, Debug, Clone)]
pub enum WatchEvent {
    /// If a range was specified when we registered for watching, `EnterRange` is fired whenever
    /// we enter this range. If `Always` was specified, `EnterRange` is fired whenever a new value
    /// is available. Otherwise, never fired.
    EnterRange {
        /// The channel that sent the value.
        from: Id<Getter>,

        /// The actual value.
        value: Value
    },

    /// If a range was specified when we registered for watching, `ExitRange` is fired whenever
    /// we exit this range. Otherwise, never fired.
    ExitRange {
        /// The channel that sent the value.
        from: Id<Getter>,

        /// The actual value.
        value: Value
    },

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// removed. Payload is the id of the device that was removed.
    GetterRemoved(Id<Getter>),

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// added. Payload is the id of the device that was added.
    GetterAdded(Id<Getter>),

    /// One of the channels encountered an error during initialization.
    /// This channel will not be watched, but other channels will remain
    /// watched.
    InitializationError {
        channel: Id<Getter>,
        error: Error
    },
}

/// A bunch of results coming from different sources.
pub type ResultMap<K, T, E> = HashMap<K, Result<T, E>>;

/// A bunch of instructions, going to different targets.
pub type TargetMap<K, T> = Vec<(Vec<K>, T)>;

/// A handle to the public API.
pub trait API: Send {
    /// Get the metadata on services matching some conditions.
    ///
    /// A call to `API::get_services(vec![req1, req2, ...])` will return
    /// the metadata on all services matching _either_ `req1` or `req2`
    /// or ...
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to a `Vec<ServiceSelector>`. See
    /// the implementation of `ServiceSelector` for details.
    ///
    /// ### Example
    ///
    /// Selector all doors in the entrance (tags `door`, `entrance`)
    /// that support setter channel `OpenClose`
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door"],
    ///   "getters": [
    ///     {
    ///       "kind": {
    ///         "Exactly": {
    ///           "OpenClose": []
    ///         }
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    ///
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing an array of `Service`. See the implementation
    /// of `Service` for details.
    ///
    /// ### Example
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door", "somevendor"],
    ///   "id: "some-service-id",
    ///   "getters": [],
    ///   "setters": [
    ///     "tags": [...],
    ///     "id": "some-channel-id",
    ///     "service": "some-service-id",
    ///     "last_seen": "some-date",
    ///     "mechanism": {
    ///       "Setter":  {
    ///         "kind": {
    ///           "OnOff": []
    ///         },
    ///         "push": [5000],
    ///         "updated": "some-date",
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    fn get_services(&self, Vec<ServiceSelector>) -> Vec<Service>;

    /// Label a set of services with a set of tags.
    ///
    /// A call to `API::put_service_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the services matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of services
    /// matching any of the selectors.
    ///
    /// Some of the services may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/tag`
    ///
    /// ## Getters
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<ServiceSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON string representing a number.
    fn add_service_tags(&self, selectors: Vec<ServiceSelector>, tags: Vec<Id<TagId>>) -> usize;

    /// Remove a set of tags from a set of services.
    ///
    /// A call to `API::delete_service_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the services matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of services
    /// matching any of the selectors.
    ///
    /// Some of the services may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/services/tag`
    ///
    /// ## Getters
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<ServiceSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn remove_service_tags(&self, selectors: Vec<ServiceSelector>, tags: Vec<Id<TagId>>) -> usize;

    /// Get a list of getters matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/channels`
    fn get_getter_channels(&self, selectors: Vec<GetterSelector>) -> Vec<Channel<Getter>>;
    fn get_setter_channels(&self, selectors: Vec<SetterSelector>) -> Vec<Channel<Setter>>;

    /// Label a set of channels with a set of tags.
    ///
    /// A call to `API::put_{getter, setter}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the channels matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of channels
    /// matching any of the selectors.
    ///
    /// Some of the channels may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if channels
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/channels/tag`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn add_getter_tags(&self, selectors: Vec<GetterSelector>, tags: Vec<Id<TagId>>) -> usize;
    fn add_setter_tags(&self, selectors: Vec<SetterSelector>, tags: Vec<Id<TagId>>) -> usize;

    /// Remove a set of tags from a set of channels.
    ///
    /// A call to `API::delete_{getter, setter}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the channels matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of channels
    /// matching any of the selectors.
    ///
    /// Some of the channels may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if channels
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/channels/tag`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn remove_getter_tags(&self, selectors: Vec<GetterSelector>, tags: Vec<Id<TagId>>) -> usize;
    fn remove_setter_tags(&self, selectors: Vec<SetterSelector>, tags: Vec<Id<TagId>>) -> usize;

    /// Read the latest value from a set of channels
    ///
    /// # REST API
    ///
    /// `GET /api/v1/channels/value`
    fn fetch_values(&self, Vec<GetterSelector>) -> ResultMap<Id<Getter>, Option<Value>, Error>;

    /// Send a bunch of values to a set of channels
    ///
    /// # REST API
    ///
    /// `POST /api/v1/channels/value`
    fn send_values(&self, TargetMap<SetterSelector, Value>) -> ResultMap<Id<Setter>, (), Error>;

    /// Watch for changes from channels.
    ///
    /// This method registers a closure to watch over events on a set of channels. Argument `watch`
    /// specifies which channels to watch and which events are of interest.
    ///
    /// - If argument `Exactly<Range>` is `Exactly::Exactly(range)`, the watch is interested in
    /// values coming from these channels, if they fall within `range`. This is the most common
    /// case. In this case, `on_event` receives `WatcherEvent::GetterAdded`,
    /// `WatcherEvent::GetterRemoved` and `WatcherEvent::Value`, whenever a new value is available
    /// in the range. Values that do not have the same type as `range` are dropped silently.
    ///
    /// - If argument `Exactly<Range>` is `Exactly::Never`, the watch is not interested in the
    /// values coming from these channels, only in connection/disconnection events. Argument
    /// `on_event` receives `WatchEvent::GetterAdded` and `WatchEvent::GetterRemoved`.
    ///
    /// - If the `Exactly<Range>` argument is `Exactly::Always`, the watch is interested in
    /// receiving *every single value coming from the channels*. This is very rarely a good idea.
    /// Many devices may reject such requests.
    ///
    /// The watcher is disconnected once the `WatchGuard` returned by this method is dropped.
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/channels/watch`
    fn watch_values(&self, watch: TargetMap<GetterSelector, Exactly<Range>>,
            on_event: Box<ExtSender<WatchEvent>>) -> Self::WatchGuard;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}
<|endoftext|>


<commit_before>use ndarray::*;
use ndarray_linalg::*;

#[test]
fn cholesky() {
    macro_rules! cholesky {
        ($elem:ty, $rtol:expr) => {
            let a_orig: Array2<$elem> = random_hpd(3);
            println!("a = \n{:?}", a_orig);

            let upper = a_orig.cholesky(UPLO::Upper).unwrap();
            assert_close_l2!(
                &upper.t().mapv(|elem| elem.conj()).dot(&upper.view()),
                &a_orig,
                $rtol
            );

            let lower = a_orig.cholesky(UPLO::Lower).unwrap();
            assert_close_l2!(
                &lower.dot(&lower.t().mapv(|elem| elem.conj())),
                &a_orig,
                $rtol
            );

            let a: Array2<$elem> = replicate(&a_orig);
            let upper = a.cholesky_into(UPLO::Upper).unwrap();
            assert_close_l2!(
                &upper.t().mapv(|elem| elem.conj()).dot(&upper.view()),
                &a_orig,
                $rtol
            );

            let a: Array2<$elem> = replicate(&a_orig);
            let lower = a.cholesky_into(UPLO::Lower).unwrap();
            assert_close_l2!(
                &lower.dot(&lower.t().mapv(|elem| elem.conj())),
                &a_orig,
                $rtol
            );

            let mut a: Array2<$elem> = replicate(&a_orig);
            {
                let upper = a.cholesky_inplace(UPLO::Upper).unwrap();
                assert_close_l2!(
                    &upper.t().mapv(|elem| elem.conj()).dot(&upper.view()),
                    &a_orig,
                    $rtol
                );
            }
            assert_close_l2!(
                &a.t().mapv(|elem| elem.conj()).dot(&upper.view()),
                &a_orig,
                $rtol
            );

            let mut a: Array2<$elem> = replicate(&a_orig);
            {
                let lower = a.cholesky_inplace(UPLO::Lower).unwrap();
                assert_close_l2!(
                    &lower.dot(&lower.t().mapv(|elem| elem.conj())),
                    &a_orig,
                    $rtol
                );
            }
            assert_close_l2!(&a.dot(&lower.t().mapv(|elem| elem.conj())), &a_orig, $rtol);
        };
    }
    cholesky!(f64, 1e-9);
    cholesky!(f32, 1e-5);
    cholesky!(c64, 1e-9);
    cholesky!(c32, 1e-5);
}

#[test]
fn cholesky_into_lower_upper() {
    macro_rules! cholesky_into_lower_upper {
        ($elem:ty, $rtol:expr) => {
            let a: Array2<$elem> = random_hpd(3);
            println!("a = \n{:?}", a);
            let upper = a.cholesky(UPLO::Upper).unwrap();
            let fac_upper = a.factorizec(UPLO::Upper).unwrap();
            let fac_lower = a.factorizec(UPLO::Lower).unwrap();
            assert_close_l2!(&upper, &fac_lower.into_upper(), $rtol);
            assert_close_l2!(&upper, &fac_upper.into_upper(), $rtol);
            let lower = a.cholesky(UPLO::Lower).unwrap();
            let fac_upper = a.factorizec(UPLO::Upper).unwrap();
            let fac_lower = a.factorizec(UPLO::Lower).unwrap();
            assert_close_l2!(&lower, &fac_lower.into_lower(), $rtol);
            assert_close_l2!(&lower, &fac_upper.into_lower(), $rtol);
        };
    }
    cholesky_into_lower_upper!(f64, 1e-9);
    cholesky_into_lower_upper!(f32, 1e-5);
    cholesky_into_lower_upper!(c64, 1e-9);
    cholesky_into_lower_upper!(c32, 1e-5);
}

#[test]
fn cholesky_inverse() {
    macro_rules! cholesky_into_inverse {
        ($elem:ty, $rtol:expr) => {
            let a: Array2<$elem> = random_hpd(3);
            println!("a = \n{:?}", a);
            let inv = a.invc().unwrap();
            assert_close_l2!(&a.dot(&inv), &Array2::eye(3), $rtol);
            let inv_into: Array2<$elem> = replicate(&a).invc_into().unwrap();
            assert_close_l2!(&a.dot(&inv_into), &Array2::eye(3), $rtol);
            let inv_upper = a.factorizec(UPLO::Upper).unwrap().invc().unwrap();
            assert_close_l2!(&a.dot(&inv_upper), &Array2::eye(3), $rtol);
            let inv_upper_into = a.factorizec(UPLO::Upper).unwrap().invc_into().unwrap();
            assert_close_l2!(&a.dot(&inv_upper_into), &Array2::eye(3), $rtol);
            let inv_lower = a.factorizec(UPLO::Lower).unwrap().invc().unwrap();
            assert_close_l2!(&a.dot(&inv_lower), &Array2::eye(3), $rtol);
            let inv_lower_into = a.factorizec(UPLO::Lower).unwrap().invc_into().unwrap();
            assert_close_l2!(&a.dot(&inv_lower_into), &Array2::eye(3), $rtol);
        };
    }
    cholesky_into_inverse!(f64, 1e-9);
    cholesky_into_inverse!(f32, 1e-3);
    cholesky_into_inverse!(c64, 1e-9);
    cholesky_into_inverse!(c32, 1e-3);
}

#[test]
fn cholesky_det() {
    macro_rules! cholesky_det {
        ($elem:ty, $atol:expr) => {
            let a: Array2<$elem> = random_hpd(3);
            println!("a = \n{:?}", a);
            let ln_det = a
                .eigvalsh(UPLO::Upper)
                .unwrap()
                .mapv(|elem| elem.ln())
                .scalar_sum();
            let det = ln_det.exp();
            assert_aclose!(a.factorizec(UPLO::Upper).unwrap().detc(), det, $atol);
            assert_aclose!(a.factorizec(UPLO::Upper).unwrap().ln_detc(), ln_det, $atol);
            assert_aclose!(a.factorizec(UPLO::Lower).unwrap().detc_into(), det, $atol);
            assert_aclose!(
                a.factorizec(UPLO::Lower).unwrap().ln_detc_into(),
                ln_det,
                $atol
            );
            assert_aclose!(a.detc().unwrap(), det, $atol);
            assert_aclose!(a.ln_detc().unwrap(), ln_det, $atol);
            assert_aclose!(a.clone().detc_into().unwrap(), det, $atol);
            assert_aclose!(a.ln_detc_into().unwrap(), ln_det, $atol);
        };
    }
    cholesky_det!(f64, 1e-9);
    cholesky_det!(f32, 1e-3);
    cholesky_det!(c64, 1e-9);
    cholesky_det!(c32, 1e-3);
}

#[test]
fn cholesky_solve() {
    macro_rules! cholesky_solve {
        ($elem:ty, $rtol:expr) => {
            let a: Array2<$elem> = random_hpd(3);
            let x: Array1<$elem> = random(3);
            let b = a.dot(&x);
            println!("a = \n{:?}", a);
            println!("x = \n{:?}", x);
            assert_close_l2!(&a.solvec(&b).unwrap(), &x, $rtol);
            assert_close_l2!(&a.solvec_into(b.clone()).unwrap(), &x, $rtol);
            assert_close_l2!(&a.solvec_inplace(&mut b.clone()).unwrap(), &x, $rtol);
            assert_close_l2!(
                &a.factorizec(UPLO::Upper).unwrap().solvec(&b).unwrap(),
                &x,
                $rtol
            );
            assert_close_l2!(
                &a.factorizec(UPLO::Lower).unwrap().solvec(&b).unwrap(),
                &x,
                $rtol
            );
            assert_close_l2!(
                &a.factorizec(UPLO::Upper)
                    .unwrap()
                    .solvec_into(b.clone())
                    .unwrap(),
                &x,
                $rtol
            );
            assert_close_l2!(
                &a.factorizec(UPLO::Lower)
                    .unwrap()
                    .solvec_into(b.clone())
                    .unwrap(),
                &x,
                $rtol
            );
            assert_close_l2!(
                &a.factorizec(UPLO::Upper)
                    .unwrap()
                    .solvec_inplace(&mut b.clone())
                    .unwrap(),
                &x,
                $rtol
            );
            assert_close_l2!(
                &a.factorizec(UPLO::Lower)
                    .unwrap()
                    .solvec_inplace(&mut b.clone())
                    .unwrap(),
                &x,
                $rtol
            );
        };
    }
    cholesky_solve!(f64, 1e-9);
    cholesky_solve!(f32, 1e-3);
    cholesky_solve!(c64, 1e-9);
    cholesky_solve!(c32, 1e-3);
}
<commit_msg>Split tests of cholesky<commit_after>use ndarray::*;
use ndarray_linalg::*;

macro_rules! cholesky {
    ($elem:ty, $rtol:expr) => {
        paste::item! {
            #[test]
            fn [<cholesky_ $elem>]() {
                let a_orig: Array2<$elem> = random_hpd(3);
                println!("a = \n{:?}", a_orig);

                let upper = a_orig.cholesky(UPLO::Upper).unwrap();
                assert_close_l2!(
                    &upper.t().mapv(|elem| elem.conj()).dot(&upper.view()),
                    &a_orig,
                    $rtol
                );

                let lower = a_orig.cholesky(UPLO::Lower).unwrap();
                assert_close_l2!(
                    &lower.dot(&lower.t().mapv(|elem| elem.conj())),
                    &a_orig,
                    $rtol
                );

                let a: Array2<$elem> = replicate(&a_orig);
                let upper = a.cholesky_into(UPLO::Upper).unwrap();
                assert_close_l2!(
                    &upper.t().mapv(|elem| elem.conj()).dot(&upper.view()),
                    &a_orig,
                    $rtol
                );

                let a: Array2<$elem> = replicate(&a_orig);
                let lower = a.cholesky_into(UPLO::Lower).unwrap();
                assert_close_l2!(
                    &lower.dot(&lower.t().mapv(|elem| elem.conj())),
                    &a_orig,
                    $rtol
                );

                let mut a: Array2<$elem> = replicate(&a_orig);
                {
                    let upper = a.cholesky_inplace(UPLO::Upper).unwrap();
                    assert_close_l2!(
                        &upper.t().mapv(|elem| elem.conj()).dot(&upper.view()),
                        &a_orig,
                        $rtol
                    );
                }
                assert_close_l2!(
                    &a.t().mapv(|elem| elem.conj()).dot(&upper.view()),
                    &a_orig,
                    $rtol
                );

                let mut a: Array2<$elem> = replicate(&a_orig);
                {
                    let lower = a.cholesky_inplace(UPLO::Lower).unwrap();
                    assert_close_l2!(
                        &lower.dot(&lower.t().mapv(|elem| elem.conj())),
                        &a_orig,
                        $rtol
                    );
                }
                assert_close_l2!(&a.dot(&lower.t().mapv(|elem| elem.conj())), &a_orig, $rtol);
            }
        } // paste::item
    };
}

cholesky!(f64, 1e-9);
cholesky!(f32, 1e-5);
cholesky!(c64, 1e-9);
cholesky!(c32, 1e-5);

macro_rules! cholesky_into_lower_upper {
    ($elem:ty, $rtol:expr) => {
        paste::item! {
            #[test]
            fn [<cholesky_into_lower_upper_ $elem>]() {
                let a: Array2<$elem> = random_hpd(3);
                println!("a = \n{:?}", a);
                let upper = a.cholesky(UPLO::Upper).unwrap();
                let fac_upper = a.factorizec(UPLO::Upper).unwrap();
                let fac_lower = a.factorizec(UPLO::Lower).unwrap();
                assert_close_l2!(&upper, &fac_lower.into_upper(), $rtol);
                assert_close_l2!(&upper, &fac_upper.into_upper(), $rtol);
                let lower = a.cholesky(UPLO::Lower).unwrap();
                let fac_upper = a.factorizec(UPLO::Upper).unwrap();
                let fac_lower = a.factorizec(UPLO::Lower).unwrap();
                assert_close_l2!(&lower, &fac_lower.into_lower(), $rtol);
                assert_close_l2!(&lower, &fac_upper.into_lower(), $rtol);
            }
        }
    };
}

cholesky_into_lower_upper!(f64, 1e-9);
cholesky_into_lower_upper!(f32, 1e-5);
cholesky_into_lower_upper!(c64, 1e-9);
cholesky_into_lower_upper!(c32, 1e-5);

macro_rules! cholesky_into_inverse {
    ($elem:ty, $rtol:expr) => {
        paste::item! {
            #[test]
            fn [<cholesky_inverse_ $elem>]() {
                let a: Array2<$elem> = random_hpd(3);
                println!("a = \n{:?}", a);
                let inv = a.invc().unwrap();
                assert_close_l2!(&a.dot(&inv), &Array2::eye(3), $rtol);
                let inv_into: Array2<$elem> = replicate(&a).invc_into().unwrap();
                assert_close_l2!(&a.dot(&inv_into), &Array2::eye(3), $rtol);
                let inv_upper = a.factorizec(UPLO::Upper).unwrap().invc().unwrap();
                assert_close_l2!(&a.dot(&inv_upper), &Array2::eye(3), $rtol);
                let inv_upper_into = a.factorizec(UPLO::Upper).unwrap().invc_into().unwrap();
                assert_close_l2!(&a.dot(&inv_upper_into), &Array2::eye(3), $rtol);
                let inv_lower = a.factorizec(UPLO::Lower).unwrap().invc().unwrap();
                assert_close_l2!(&a.dot(&inv_lower), &Array2::eye(3), $rtol);
                let inv_lower_into = a.factorizec(UPLO::Lower).unwrap().invc_into().unwrap();
                assert_close_l2!(&a.dot(&inv_lower_into), &Array2::eye(3), $rtol);
            }
        }
    };
}
cholesky_into_inverse!(f64, 1e-9);
cholesky_into_inverse!(f32, 1e-3);
cholesky_into_inverse!(c64, 1e-9);
cholesky_into_inverse!(c32, 1e-3);

macro_rules! cholesky_det {
    ($elem:ty, $atol:expr) => {
        paste::item! {
            #[test]
            fn [<cholesky_det_ $elem>]() {
                let a: Array2<$elem> = random_hpd(3);
                println!("a = \n{:?}", a);
                let ln_det = a
                    .eigvalsh(UPLO::Upper)
                    .unwrap()
                    .mapv(|elem| elem.ln())
                    .scalar_sum();
                let det = ln_det.exp();
                assert_aclose!(a.factorizec(UPLO::Upper).unwrap().detc(), det, $atol);
                assert_aclose!(a.factorizec(UPLO::Upper).unwrap().ln_detc(), ln_det, $atol);
                assert_aclose!(a.factorizec(UPLO::Lower).unwrap().detc_into(), det, $atol);
                assert_aclose!(
                    a.factorizec(UPLO::Lower).unwrap().ln_detc_into(),
                    ln_det,
                    $atol
                );
                assert_aclose!(a.detc().unwrap(), det, $atol);
                assert_aclose!(a.ln_detc().unwrap(), ln_det, $atol);
                assert_aclose!(a.clone().detc_into().unwrap(), det, $atol);
                assert_aclose!(a.ln_detc_into().unwrap(), ln_det, $atol);
            }
        }
    };
}
cholesky_det!(f64, 1e-9);
cholesky_det!(f32, 1e-3);
cholesky_det!(c64, 1e-9);
cholesky_det!(c32, 1e-3);

macro_rules! cholesky_solve {
    ($elem:ty, $rtol:expr) => {
        paste::item! {
            #[test]
            fn [<cholesky_solve_ $elem>]() {
                let a: Array2<$elem> = random_hpd(3);
                let x: Array1<$elem> = random(3);
                let b = a.dot(&x);
                println!("a = \n{:?}", a);
                println!("x = \n{:?}", x);
                assert_close_l2!(&a.solvec(&b).unwrap(), &x, $rtol);
                assert_close_l2!(&a.solvec_into(b.clone()).unwrap(), &x, $rtol);
                assert_close_l2!(&a.solvec_inplace(&mut b.clone()).unwrap(), &x, $rtol);
                assert_close_l2!(
                    &a.factorizec(UPLO::Upper).unwrap().solvec(&b).unwrap(),
                    &x,
                    $rtol
                );
                assert_close_l2!(
                    &a.factorizec(UPLO::Lower).unwrap().solvec(&b).unwrap(),
                    &x,
                    $rtol
                );
                assert_close_l2!(
                    &a.factorizec(UPLO::Upper)
                        .unwrap()
                        .solvec_into(b.clone())
                        .unwrap(),
                    &x,
                    $rtol
                );
                assert_close_l2!(
                    &a.factorizec(UPLO::Lower)
                        .unwrap()
                        .solvec_into(b.clone())
                        .unwrap(),
                    &x,
                    $rtol
                );
                assert_close_l2!(
                    &a.factorizec(UPLO::Upper)
                        .unwrap()
                        .solvec_inplace(&mut b.clone())
                        .unwrap(),
                    &x,
                    $rtol
                );
                assert_close_l2!(
                    &a.factorizec(UPLO::Lower)
                        .unwrap()
                        .solvec_inplace(&mut b.clone())
                        .unwrap(),
                    &x,
                    $rtol
                );
            }
        }
    };
}
cholesky_solve!(f64, 1e-9);
cholesky_solve!(f32, 1e-3);
cholesky_solve!(c64, 1e-9);
cholesky_solve!(c32, 1e-3);
<|endoftext|>


<commit_before>// from: https://github.com/Maktm/FLIRTDB/blob/1f5763535e02d7cccf2f90a96a8ebaa36e9b2495/cmt/windows/libcmt_15_msvc_x86.pat#L354
//
//     6AFF5064A100000000508B44240C64892500000000896C240C8D6C240C50F2C3 00 0000 0020 :0000 __EH_prolog
//
// take that first column and treat it as a byte signature:
//
//     rule __EH_prolog : flirt
//     {
//         strings:
//             $bytes = {6AFF5064A100000000508B44240C64892500000000896C240C8D6C240C50F2C3}
//         condition:
//             $bytes
//     }
//
// and search for it:
//
//     $ yara src/analysis/pe/flirt/__EH_prolog.yara /mnt/c/Windows/SysWOW64/
//     __EH_prolog /mnt/c/Windows/SysWOW64//ucrtbase.dll
//     __EH_prolog /mnt/c/Windows/SysWOW64//ucrtbased.dll
//     __EH_prolog /mnt/c/Windows/SysWOW64//vcruntime140.dll
//     __EH_prolog /mnt/c/Windows/SysWOW64//vcruntime140_clr0400.dll
//     __EH_prolog /mnt/c/Windows/SysWOW64//vcruntime140d.dll
// ```

extern crate nom;
use log::{trace};
use regex::bytes::Regex;

pub mod pat;
pub mod nfa;

#[derive(Debug)]
enum SigElement {
    Byte(u8),
    Wildcard,
}

impl std::fmt::Display for SigElement {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SigElement::Byte(v) => write!(f, "{:02x}", v),
            SigElement::Wildcard => write!(f, ".."),
        }
    }
}

#[derive(Debug)]
pub struct ByteSignature(Vec<SigElement>);

impl std::fmt::Display for ByteSignature {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for elem in self.0.iter() {
            write!(f, "{}", elem)?;
        }
        Ok(())
    }
}

#[derive(Debug)]
enum Offset {
    Public(u16),
    Local(u16),
    Reference(u16),
}

#[derive(Debug)]
pub struct Name {
    offset: u16,
    name: String,
}

#[derive(Debug)]
enum Symbol {
    Public(Name),
    Local(Name),
    Reference(Name),
}

impl std::fmt::Display for Symbol {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Symbol::Public(name) => write!(f, ":{:04x} {}", name.offset, name.name),
            Symbol::Local(name) => write!(f, ":{:04x}@ {}", name.offset, name.name),
            Symbol::Reference(name) => write!(f, "^{:04x} {}", name.offset, name.name),
        }
    }
}

pub struct FlirtSignature {
    byte_sig: ByteSignature,

    /// number of bytes passed to the CRC16 checksum
    size_of_bytes_crc16: u8,  // max: 0xFF
    crc16: u16,
    size_of_function: u16,  // max: 0x8000

    names: Vec<Symbol>,

    footer: Option<ByteSignature>,
}

impl std::fmt::Display for FlirtSignature {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} ", self.byte_sig)?;
        write!(f, "{:02x} ", self.size_of_bytes_crc16)?;
        write!(f, "{:04x} ", self.crc16)?;
        write!(f, "{:04x} ", self.size_of_function)?;
        for (i, name) in self.names.iter().enumerate() {
            write!(f, "{}", name)?;
            if i != self.names.len() - 1 {
                write!(f, " ")?;
            }
        }
        if let Some(footer) = &self.footer {
            write!(f, " ")?;
            write!(f, "{}", footer)?;
        }
        Ok(())
    }
}

impl std::fmt::Debug for FlirtSignature {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}

impl FlirtSignature {
    pub fn create_matcher(&self) -> FlirtSignatureMatcher {
        FlirtSignatureMatcher::new(self)
    }

    pub fn get_name(&self) -> Option<&str> {
        for name in self.names.iter() {
            if let Symbol::Public(name) = name {
                if name.offset == 0x0 {
                    return Some(&name.name);
                }
            }
        }

        return None;
    }
}

pub struct FlirtSignatureMatcher<'a> {
    num_bytes_needed: u16,
    re: Regex,
    sig: &'a FlirtSignature,
}

/// create a binary regular expression pattern for matching the given FLIRT byte signature.
/// using this pattern still requires creating a Regex object with the appropriate options.
fn create_pattern(sig: &FlirtSignature) -> String {
    // the function may be shorter than the 0x20 byte pattern,
    // if so, only generate a pattern long enough to match the function.
    let elements = if sig.size_of_function < 32 {
        &sig.byte_sig.0[..sig.size_of_function as usize]
    } else {
        &sig.byte_sig.0
    };

    elements
        .iter()
        .map(|b| match b {
            // lots of allocations here.
            // could create a static translation table to the &str formats.
            SigElement::Byte(v) => format!("\\x{:02x}", v),
            SigElement::Wildcard => format!("."),
        })
        .collect::<Vec<String>>()
        .join("")
}

impl<'a> FlirtSignatureMatcher<'a> {
    pub fn new(sig: &'a FlirtSignature) -> FlirtSignatureMatcher {
        FlirtSignatureMatcher {
            num_bytes_needed: if sig.size_of_function < 32 {
                sig.size_of_function
            } else {
                (sig.byte_sig.0.len() as u16) + (sig.size_of_bytes_crc16 as u16)
            },
            re: FlirtSignatureMatcher::create_match_re(sig),
            sig: sig,
        }
    }

    /// translate the given FLIRT byte signature into a binary regex.
    ///
    /// it matches from the start of the input string, so its best for the `is_match` operation.
    fn create_match_re(sig: &'a FlirtSignature) -> Regex {
        // the `(?-u)` disables unicode mode, which lets us match raw byte values.
        let pattern = format!("(?-u)(^{})", create_pattern(sig));

        Regex::new(&pattern).expect("failed to compile regex")
    }

    /// compute the IDA-specific CRC16 checksum for the given bytes.
    ///
    /// This is ported from flair tools flair/crc16.cpp
    fn crc16(buf: &[u8]) -> u16 {
        const POLY: u32 = 0x8408;

        if buf.len() == 0 {
            return 0;
        }

        let mut crc: u32 = 0xFFFF;
        for &b in buf {
            let mut b = b as u32;

            for _ in 0..8 {
                if ((crc ^ b) & 1) > 0 {
                    crc = (crc >> 1) ^ POLY;
                } else {
                    crc >>= 1;
                }
                b >>= 1;
            }
        }

        crc = !crc;  // bitwise invert

        // swap u16 byte order
        let h1: u16 = ((crc >> 0) & 0xFF) as u16;
        let h2: u16 = ((crc >> 8) & 0xFF) as u16;

        (h1 << 8) | (h2 << 0)
    }

    /// ```
    /// use flirt;
    /// use flirt::pat;
    ///
    /// let pat_buf = "\
    /// 518B4C240C895C240C8D5C240C508D442408F7D923C18D60F88B43F08904248B 21 B4FE 006E :0000 __EH_prolog3_GS_align ^0041 ___security_cookie ........33C5508941FC8B4DF0895DF08B4304894504FF75F464A1000000008945F48D45F464A300000000F2C3
    /// 518B4C240C895C240C8D5C240C508D442408F7D923C18D60F88B43F08904248B 1F E4CF 0063 :0000 __EH_prolog3_align ^003F ___security_cookie ........33C5508B4304894504FF75F464A1000000008945F48D45F464A300000000F2C3
    /// 518B4C240C895C240C8D5C240C508D442408F7D923C18D60F88B43F08904248B 22 E4CE 006F :0000 __EH_prolog3_catch_GS_align ^0042 ___security_cookie ........33C5508941FC8B4DF08965F08B4304894504FF75F464A1000000008945F48D45F464A300000000F2C3
    /// 518B4C240C895C240C8D5C240C508D442408F7D923C18D60F88B43F08904248B 20 6562 0067 :0000 __EH_prolog3_catch_align ^0040 ___security_cookie ........33C5508965F08B4304894504FF75F464A1000000008945F48D45F464A300000000F2C3
    /// ---";
    ///
    /// let sigs = pat::parse(pat_buf).unwrap();
    /// let __EH_prolog3_catch_align = &sigs[3];
    /// let m = __EH_prolog3_catch_align.create_matcher();
    /// assert!(m.r#match(&[
    ///     // apds.dll / 4FD932C41DF96D019DC265E26E94B81B
    ///     // __EH_prolog3_catch_align
    ///
    ///     // first 0x20
    ///     0x51, 0x8B, 0x4C, 0x24, 0x0C, 0x89, 0x5C, 0x24,
    ///     0x0C, 0x8D, 0x5C, 0x24, 0x0C, 0x50, 0x8D, 0x44,
    ///     0x24, 0x08, 0xF7, 0xD9, 0x23, 0xC1, 0x8D, 0x60,
    ///     0xF8, 0x8B, 0x43, 0xF0, 0x89, 0x04, 0x24, 0x8B,
    ///     // crc16 start
    ///     0x43, 0xF8, 0x50, 0x8B, 0x43, 0xFC, 0x8B, 0x4B,
    ///     0xF4, 0x89, 0x6C, 0x24, 0x0C, 0x8D, 0x6C, 0x24,
    ///     0x0C, 0xC7, 0x44, 0x24, 0x08, 0xFF, 0xFF, 0xFF,
    ///     0xFF, 0x51, 0x53, 0x2B, 0xE0, 0x56, 0x57, 0xA1,
    ///     // crc end
    ///     0xD4, 0xAD, 0x19, 0x01, 0x33, 0xC5, 0x50, 0x89,
    ///     0x65, 0xF0, 0x8B, 0x43, 0x04, 0x89, 0x45, 0x04,
    ///     0xFF, 0x75, 0xF4, 0x64, 0xA1, 0x00, 0x00, 0x00,
    ///     0x00, 0x89, 0x45, 0xF4, 0x8D, 0x45, 0xF4, 0x64,
    ///     0xA3, 0x00, 0x00, 0x00, 0x00, 0xC3]));
    /// ```
    pub fn r#match(&self, buf: &[u8]) -> bool {
        if !self.re.is_match(buf) {
            trace!("flirt signature: pattern fails");
            return false
        }

        if self.sig.size_of_bytes_crc16 > 0 {
            let byte_sig_size= self.sig.byte_sig.0.len();
            let start = byte_sig_size;
            let end = byte_sig_size + (self.sig.size_of_bytes_crc16 as usize);
            if end > buf.len() {
                trace!("flirt signature: buffer not large enough");
                return false
            }

            let crc16 = FlirtSignatureMatcher::crc16(&buf[start..end]);

            if crc16 != self.sig.crc16 {
                trace!("flirt signature: crc16 fails");
                return false
            }
        }

        trace!("flirt signature: match");
        true
    }
}
<commit_msg>flirt: remove num_bytes_needed<commit_after>// from: https://github.com/Maktm/FLIRTDB/blob/1f5763535e02d7cccf2f90a96a8ebaa36e9b2495/cmt/windows/libcmt_15_msvc_x86.pat#L354
//
//     6AFF5064A100000000508B44240C64892500000000896C240C8D6C240C50F2C3 00 0000 0020 :0000 __EH_prolog
//
// take that first column and treat it as a byte signature:
//
//     rule __EH_prolog : flirt
//     {
//         strings:
//             $bytes = {6AFF5064A100000000508B44240C64892500000000896C240C8D6C240C50F2C3}
//         condition:
//             $bytes
//     }
//
// and search for it:
//
//     $ yara src/analysis/pe/flirt/__EH_prolog.yara /mnt/c/Windows/SysWOW64/
//     __EH_prolog /mnt/c/Windows/SysWOW64//ucrtbase.dll
//     __EH_prolog /mnt/c/Windows/SysWOW64//ucrtbased.dll
//     __EH_prolog /mnt/c/Windows/SysWOW64//vcruntime140.dll
//     __EH_prolog /mnt/c/Windows/SysWOW64//vcruntime140_clr0400.dll
//     __EH_prolog /mnt/c/Windows/SysWOW64//vcruntime140d.dll
// ```

extern crate nom;
use log::{trace};
use regex::bytes::Regex;

pub mod pat;
pub mod nfa;

#[derive(Debug)]
enum SigElement {
    Byte(u8),
    Wildcard,
}

impl std::fmt::Display for SigElement {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SigElement::Byte(v) => write!(f, "{:02x}", v),
            SigElement::Wildcard => write!(f, ".."),
        }
    }
}

#[derive(Debug)]
pub struct ByteSignature(Vec<SigElement>);

impl std::fmt::Display for ByteSignature {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for elem in self.0.iter() {
            write!(f, "{}", elem)?;
        }
        Ok(())
    }
}

#[derive(Debug)]
enum Offset {
    Public(u16),
    Local(u16),
    Reference(u16),
}

#[derive(Debug)]
pub struct Name {
    offset: u16,
    name: String,
}

#[derive(Debug)]
enum Symbol {
    Public(Name),
    Local(Name),
    Reference(Name),
}

impl std::fmt::Display for Symbol {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Symbol::Public(name) => write!(f, ":{:04x} {}", name.offset, name.name),
            Symbol::Local(name) => write!(f, ":{:04x}@ {}", name.offset, name.name),
            Symbol::Reference(name) => write!(f, "^{:04x} {}", name.offset, name.name),
        }
    }
}

pub struct FlirtSignature {
    byte_sig: ByteSignature,

    /// number of bytes passed to the CRC16 checksum
    size_of_bytes_crc16: u8,  // max: 0xFF
    crc16: u16,
    size_of_function: u16,  // max: 0x8000

    names: Vec<Symbol>,

    footer: Option<ByteSignature>,
}

impl std::fmt::Display for FlirtSignature {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} ", self.byte_sig)?;
        write!(f, "{:02x} ", self.size_of_bytes_crc16)?;
        write!(f, "{:04x} ", self.crc16)?;
        write!(f, "{:04x} ", self.size_of_function)?;
        for (i, name) in self.names.iter().enumerate() {
            write!(f, "{}", name)?;
            if i != self.names.len() - 1 {
                write!(f, " ")?;
            }
        }
        if let Some(footer) = &self.footer {
            write!(f, " ")?;
            write!(f, "{}", footer)?;
        }
        Ok(())
    }
}

impl std::fmt::Debug for FlirtSignature {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}

impl FlirtSignature {
    pub fn create_matcher(&self) -> FlirtSignatureMatcher {
        FlirtSignatureMatcher::new(self)
    }

    pub fn get_name(&self) -> Option<&str> {
        for name in self.names.iter() {
            if let Symbol::Public(name) = name {
                if name.offset == 0x0 {
                    return Some(&name.name);
                }
            }
        }

        return None;
    }
}

pub struct FlirtSignatureMatcher<'a> {
    re: Regex,
    sig: &'a FlirtSignature,
}

/// create a binary regular expression pattern for matching the given FLIRT byte signature.
/// using this pattern still requires creating a Regex object with the appropriate options.
fn create_pattern(sig: &FlirtSignature) -> String {
    // the function may be shorter than the 0x20 byte pattern,
    // if so, only generate a pattern long enough to match the function.
    let elements = if sig.size_of_function < 32 {
        &sig.byte_sig.0[..sig.size_of_function as usize]
    } else {
        &sig.byte_sig.0
    };

    elements
        .iter()
        .map(|b| match b {
            // lots of allocations here.
            // could create a static translation table to the &str formats.
            SigElement::Byte(v) => format!("\\x{:02x}", v),
            SigElement::Wildcard => format!("."),
        })
        .collect::<Vec<String>>()
        .join("")
}

impl<'a> FlirtSignatureMatcher<'a> {
    pub fn new(sig: &'a FlirtSignature) -> FlirtSignatureMatcher {
        FlirtSignatureMatcher {
            re: FlirtSignatureMatcher::create_match_re(sig),
            sig: sig,
        }
    }

    /// translate the given FLIRT byte signature into a binary regex.
    ///
    /// it matches from the start of the input string, so its best for the `is_match` operation.
    fn create_match_re(sig: &'a FlirtSignature) -> Regex {
        // the `(?-u)` disables unicode mode, which lets us match raw byte values.
        let pattern = format!("(?-u)(^{})", create_pattern(sig));

        Regex::new(&pattern).expect("failed to compile regex")
    }

    /// compute the IDA-specific CRC16 checksum for the given bytes.
    ///
    /// This is ported from flair tools flair/crc16.cpp
    fn crc16(buf: &[u8]) -> u16 {
        const POLY: u32 = 0x8408;

        if buf.len() == 0 {
            return 0;
        }

        let mut crc: u32 = 0xFFFF;
        for &b in buf {
            let mut b = b as u32;

            for _ in 0..8 {
                if ((crc ^ b) & 1) > 0 {
                    crc = (crc >> 1) ^ POLY;
                } else {
                    crc >>= 1;
                }
                b >>= 1;
            }
        }

        crc = !crc;  // bitwise invert

        // swap u16 byte order
        let h1: u16 = ((crc >> 0) & 0xFF) as u16;
        let h2: u16 = ((crc >> 8) & 0xFF) as u16;

        (h1 << 8) | (h2 << 0)
    }

    /// ```
    /// use flirt;
    /// use flirt::pat;
    ///
    /// let pat_buf = "\
    /// 518B4C240C895C240C8D5C240C508D442408F7D923C18D60F88B43F08904248B 21 B4FE 006E :0000 __EH_prolog3_GS_align ^0041 ___security_cookie ........33C5508941FC8B4DF0895DF08B4304894504FF75F464A1000000008945F48D45F464A300000000F2C3
    /// 518B4C240C895C240C8D5C240C508D442408F7D923C18D60F88B43F08904248B 1F E4CF 0063 :0000 __EH_prolog3_align ^003F ___security_cookie ........33C5508B4304894504FF75F464A1000000008945F48D45F464A300000000F2C3
    /// 518B4C240C895C240C8D5C240C508D442408F7D923C18D60F88B43F08904248B 22 E4CE 006F :0000 __EH_prolog3_catch_GS_align ^0042 ___security_cookie ........33C5508941FC8B4DF08965F08B4304894504FF75F464A1000000008945F48D45F464A300000000F2C3
    /// 518B4C240C895C240C8D5C240C508D442408F7D923C18D60F88B43F08904248B 20 6562 0067 :0000 __EH_prolog3_catch_align ^0040 ___security_cookie ........33C5508965F08B4304894504FF75F464A1000000008945F48D45F464A300000000F2C3
    /// ---";
    ///
    /// let sigs = pat::parse(pat_buf).unwrap();
    /// let __EH_prolog3_catch_align = &sigs[3];
    /// let m = __EH_prolog3_catch_align.create_matcher();
    /// assert!(m.r#match(&[
    ///     // apds.dll / 4FD932C41DF96D019DC265E26E94B81B
    ///     // __EH_prolog3_catch_align
    ///
    ///     // first 0x20
    ///     0x51, 0x8B, 0x4C, 0x24, 0x0C, 0x89, 0x5C, 0x24,
    ///     0x0C, 0x8D, 0x5C, 0x24, 0x0C, 0x50, 0x8D, 0x44,
    ///     0x24, 0x08, 0xF7, 0xD9, 0x23, 0xC1, 0x8D, 0x60,
    ///     0xF8, 0x8B, 0x43, 0xF0, 0x89, 0x04, 0x24, 0x8B,
    ///     // crc16 start
    ///     0x43, 0xF8, 0x50, 0x8B, 0x43, 0xFC, 0x8B, 0x4B,
    ///     0xF4, 0x89, 0x6C, 0x24, 0x0C, 0x8D, 0x6C, 0x24,
    ///     0x0C, 0xC7, 0x44, 0x24, 0x08, 0xFF, 0xFF, 0xFF,
    ///     0xFF, 0x51, 0x53, 0x2B, 0xE0, 0x56, 0x57, 0xA1,
    ///     // crc end
    ///     0xD4, 0xAD, 0x19, 0x01, 0x33, 0xC5, 0x50, 0x89,
    ///     0x65, 0xF0, 0x8B, 0x43, 0x04, 0x89, 0x45, 0x04,
    ///     0xFF, 0x75, 0xF4, 0x64, 0xA1, 0x00, 0x00, 0x00,
    ///     0x00, 0x89, 0x45, 0xF4, 0x8D, 0x45, 0xF4, 0x64,
    ///     0xA3, 0x00, 0x00, 0x00, 0x00, 0xC3]));
    /// ```
    pub fn r#match(&self, buf: &[u8]) -> bool {
        if !self.re.is_match(buf) {
            trace!("flirt signature: pattern fails");
            return false
        }

        if self.sig.size_of_bytes_crc16 > 0 {
            let byte_sig_size= self.sig.byte_sig.0.len();
            let start = byte_sig_size;
            let end = byte_sig_size + (self.sig.size_of_bytes_crc16 as usize);
            if end > buf.len() {
                trace!("flirt signature: buffer not large enough");
                return false
            }

            let crc16 = FlirtSignatureMatcher::crc16(&buf[start..end]);

            if crc16 != self.sig.crc16 {
                trace!("flirt signature: crc16 fails");
                return false
            }
        }

        trace!("flirt signature: match");
        true
    }
}
<|endoftext|>