<commit_before>// Copyright (c) 2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use crate::api::EncoderConfig;
use crate::api::InterConfig;
use crate::frame::*;
use crate::util::CastFromPrimitive;
use crate::util::Pixel;

use crate::hawktracer::*;

use std::cmp;
use std::collections::BTreeSet;
use std::sync::Arc;

/// Runs keyframe detection on frames from the lookahead queue.
pub(crate) struct SceneChangeDetector {
  /// Minimum average difference between YUV deltas that will trigger a scene change.
  threshold: u8,
  /// Fast scene cut detection mode, ignoing chroma planes.
  fast_mode: bool,
  /// Frames that cannot be marked as keyframes due to the algorithm excluding them.
  /// Storing the frame numbers allows us to avoid looking back more than one frame.
  excluded_frames: BTreeSet<u64>,
}

impl SceneChangeDetector {
  pub fn new(bit_depth: u8, fast_mode: bool) -> Self {
    // This implementation is based on a Python implementation at
    // https://pyscenedetect.readthedocs.io/en/latest/reference/detection-methods/.
    // The Python implementation uses HSV values and a threshold of 30. Comparing the
    // YUV values was sufficient in most cases, and avoided a more costly YUV->RGB->HSV
    // conversion, but the deltas needed to be scaled down. The deltas for keyframes
    // in YUV were about 1/3 to 1/2 of what they were in HSV, but non-keyframes were
    // very unlikely to have a delta greater than 3 in YUV, whereas they may reach into
    // the double digits in HSV. Therefore, 12 was chosen as a reasonable default threshold.
    // This may be adjusted later.
    const BASE_THRESHOLD: u8 = 12;
    Self {
      threshold: BASE_THRESHOLD * bit_depth / 8,
      fast_mode,
      excluded_frames: BTreeSet::new(),
    }
  }

  /// Runs keyframe detection on the next frame in the lookahead queue.
  ///
  /// This function requires that a subset of input frames
  /// is passed to it in order, and that `keyframes` is only
  /// updated from this method. `input_frameno` should correspond
  /// to the first frame in `frame_set`.
  ///
  /// This will gracefully handle the first frame in the video as well.
  #[hawktracer(analyze_next_frame)]
  pub fn analyze_next_frame<T: Pixel>(
    &mut self, previous_frame: Option<Arc<Frame<T>>>,
    frame_set: &[Arc<Frame<T>>], input_frameno: u64, config: &EncoderConfig,
    inter_cfg: &InterConfig, keyframes: &mut BTreeSet<u64>,
    keyframes_forced: &BTreeSet<u64>,
  ) {
    let frame_set = match previous_frame {
      Some(frame) => {
        [frame].iter().chain(frame_set.iter()).cloned().collect::<Vec<_>>()
      }
      None => {
        // The first frame is always a keyframe.
        keyframes.insert(0);
        return;
      }
    };

    self.exclude_scene_flashes(&frame_set, input_frameno, inter_cfg);

    if self.is_key_frame(
      &frame_set[0],
      &frame_set[1],
      input_frameno,
      config,
      keyframes,
      keyframes_forced,
    ) {
      keyframes.insert(input_frameno);
    }
  }

  /// Determines if `current_frame` should be a keyframe.
  fn is_key_frame<T: Pixel>(
    &self, previous_frame: &Frame<T>, current_frame: &Frame<T>,
    current_frameno: u64, config: &EncoderConfig,
    keyframes: &mut BTreeSet<u64>, keyframes_forced: &BTreeSet<u64>,
  ) -> bool {
    if keyframes_forced.contains(&current_frameno) {
      return true;
    }

    // Find the distance to the previous keyframe.
    let previous_keyframe = keyframes.iter().last().unwrap();
    let distance = current_frameno - previous_keyframe;

    // Handle minimum and maximum key frame intervals.
    if distance < config.min_key_frame_interval {
      return false;
    }
    if distance >= config.max_key_frame_interval {
      return true;
    }

    // Skip smart scene detection if it's disabled
    if config.speed_settings.no_scene_detection {
      return false;
    }

    if self.excluded_frames.contains(&current_frameno) {
      return false;
    }

    self.has_scenecut(previous_frame, current_frame)
  }

  /// Uses lookahead to avoid coding short flashes as scenecuts.
  /// Saves excluded frame numbers in `self.excluded_frames`.
  fn exclude_scene_flashes<T: Pixel>(
    &mut self, frame_subset: &[Arc<Frame<T>>], frameno: u64,
    inter_cfg: &InterConfig,
  ) {
    let lookahead_distance = cmp::min(
      inter_cfg.keyframe_lookahead_distance() as usize,
      frame_subset.len() - 1,
    );

    // Where A and B are scenes: AAAAAABBBAAAAAA
    // If BBB is shorter than lookahead_distance, it is detected as a flash
    // and not considered a scenecut.
    if lookahead_distance > 1 {
      for j in 1..=lookahead_distance {
        if !self.has_scenecut(&frame_subset[0], &frame_subset[j]) {
          // Any frame in between `0` and `j` cannot be a real scenecut.
          for i in 0..=j {
            let frameno = frameno + i as u64 - 1;
            self.excluded_frames.insert(frameno);
          }
        }
      }
    }

    // Where A-F are scenes: AAAAABBCCDDEEFFFFFF
    // If each of BB ... EE are shorter than `lookahead_distance`, they are
    // detected as flashes and not considered scenecuts.
    // Instead, the first F frame becomes a scenecut.
    // If the video ends before F, no frame becomes a scenecut.
    for i in 1..=lookahead_distance {
      if i < lookahead_distance
        && self
          .has_scenecut(&frame_subset[i], &frame_subset[lookahead_distance])
      {
        // If the current frame is the frame before a scenecut, it cannot also be the frame of a scenecut.
        let frameno = frameno + i as u64 - 1;
        self.excluded_frames.insert(frameno);
      }
    }
  }

  /// Run a comparison between two frames to determine if they qualify for a scenecut.
  ///
  /// The current algorithm detects fast cuts using changes in colour and intensity between frames.
  /// Since the difference between frames is used, only fast cuts are detected
  /// with this method. This is intended to change via https://github.com/xiph/rav1e/issues/794.
  fn has_scenecut<T: Pixel>(
    &self, frame1: &Frame<T>, frame2: &Frame<T>,
  ) -> bool {
    let y_lines =
      frame1.planes[0].rows_iter().zip(frame2.planes[0].rows_iter());

    let mut len = frame2.planes[0].cfg.width * frame2.planes[0].cfg.height;
    let mut delta = 0;

    for (l1, l2) in y_lines {
      let delta_line = l1
        .iter()
        .zip(l2.iter())
        .map(|(&p1, &p2)| {
          (i16::cast_from(p1) - i16::cast_from(p2)).abs() as u64
        })
        .sum::<u64>();
      delta += delta_line;
    }

    if !self.fast_mode {
      let u_dec = frame1.planes[1].cfg.xdec + frame1.planes[1].cfg.ydec;
      len +=
        (frame2.planes[1].cfg.width * frame2.planes[1].cfg.height) << u_dec;

      let u_lines =
        frame1.planes[1].rows_iter().zip(frame2.planes[1].rows_iter());

      for (l1, l2) in u_lines {
        let delta_line = l1
          .iter()
          .zip(l2.iter())
          .map(|(&p1, &p2)| {
            (i16::cast_from(p1) - i16::cast_from(p2)).abs() as u64
          })
          .sum::<u64>();
        delta += delta_line << u_dec;
      }

      let v_dec = frame1.planes[2].cfg.xdec + frame1.planes[2].cfg.ydec;
      len +=
        (frame2.planes[2].cfg.width * frame2.planes[2].cfg.height) << v_dec;

      let v_lines =
        frame1.planes[2].rows_iter().zip(frame2.planes[2].rows_iter());

      for (l1, l2) in v_lines {
        let delta_line = l1
          .iter()
          .zip(l2.iter())
          .map(|(&p1, &p2)| {
            (i16::cast_from(p1) - i16::cast_from(p2)).abs() as u64
          })
          .sum::<u64>();
        delta += delta_line << v_dec;
      }
    }

    delta >= self.threshold as u64 * len as u64
  }
}
<commit_msg>Simplify exclude_scene_flashes<commit_after>// Copyright (c) 2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use crate::api::EncoderConfig;
use crate::api::InterConfig;
use crate::frame::*;
use crate::util::CastFromPrimitive;
use crate::util::Pixel;

use crate::hawktracer::*;

use std::cmp;
use std::collections::BTreeSet;
use std::sync::Arc;

/// Runs keyframe detection on frames from the lookahead queue.
pub(crate) struct SceneChangeDetector {
  /// Minimum average difference between YUV deltas that will trigger a scene change.
  threshold: u8,
  /// Fast scene cut detection mode, ignoing chroma planes.
  fast_mode: bool,
  /// Frames that cannot be marked as keyframes due to the algorithm excluding them.
  /// Storing the frame numbers allows us to avoid looking back more than one frame.
  excluded_frames: BTreeSet<u64>,
}

impl SceneChangeDetector {
  pub fn new(bit_depth: u8, fast_mode: bool) -> Self {
    // This implementation is based on a Python implementation at
    // https://pyscenedetect.readthedocs.io/en/latest/reference/detection-methods/.
    // The Python implementation uses HSV values and a threshold of 30. Comparing the
    // YUV values was sufficient in most cases, and avoided a more costly YUV->RGB->HSV
    // conversion, but the deltas needed to be scaled down. The deltas for keyframes
    // in YUV were about 1/3 to 1/2 of what they were in HSV, but non-keyframes were
    // very unlikely to have a delta greater than 3 in YUV, whereas they may reach into
    // the double digits in HSV. Therefore, 12 was chosen as a reasonable default threshold.
    // This may be adjusted later.
    const BASE_THRESHOLD: u8 = 12;
    Self {
      threshold: BASE_THRESHOLD * bit_depth / 8,
      fast_mode,
      excluded_frames: BTreeSet::new(),
    }
  }

  /// Runs keyframe detection on the next frame in the lookahead queue.
  ///
  /// This function requires that a subset of input frames
  /// is passed to it in order, and that `keyframes` is only
  /// updated from this method. `input_frameno` should correspond
  /// to the first frame in `frame_set`.
  ///
  /// This will gracefully handle the first frame in the video as well.
  #[hawktracer(analyze_next_frame)]
  pub fn analyze_next_frame<T: Pixel>(
    &mut self, previous_frame: Option<Arc<Frame<T>>>,
    frame_set: &[Arc<Frame<T>>], input_frameno: u64, config: &EncoderConfig,
    inter_cfg: &InterConfig, keyframes: &mut BTreeSet<u64>,
    keyframes_forced: &BTreeSet<u64>,
  ) {
    let frame_set = match previous_frame {
      Some(frame) => {
        [frame].iter().chain(frame_set.iter()).cloned().collect::<Vec<_>>()
      }
      None => {
        // The first frame is always a keyframe.
        keyframes.insert(0);
        return;
      }
    };

    self.exclude_scene_flashes(&frame_set, input_frameno, inter_cfg);

    if self.is_key_frame(
      &frame_set[0],
      &frame_set[1],
      input_frameno,
      config,
      keyframes,
      keyframes_forced,
    ) {
      keyframes.insert(input_frameno);
    }
  }

  /// Determines if `current_frame` should be a keyframe.
  fn is_key_frame<T: Pixel>(
    &self, previous_frame: &Frame<T>, current_frame: &Frame<T>,
    current_frameno: u64, config: &EncoderConfig,
    keyframes: &mut BTreeSet<u64>, keyframes_forced: &BTreeSet<u64>,
  ) -> bool {
    if keyframes_forced.contains(&current_frameno) {
      return true;
    }

    // Find the distance to the previous keyframe.
    let previous_keyframe = keyframes.iter().last().unwrap();
    let distance = current_frameno - previous_keyframe;

    // Handle minimum and maximum key frame intervals.
    if distance < config.min_key_frame_interval {
      return false;
    }
    if distance >= config.max_key_frame_interval {
      return true;
    }

    // Skip smart scene detection if it's disabled
    if config.speed_settings.no_scene_detection {
      return false;
    }

    if self.excluded_frames.contains(&current_frameno) {
      return false;
    }

    self.has_scenecut(previous_frame, current_frame)
  }

  /// Uses lookahead to avoid coding short flashes as scenecuts.
  /// Saves excluded frame numbers in `self.excluded_frames`.
  fn exclude_scene_flashes<T: Pixel>(
    &mut self, frame_subset: &[Arc<Frame<T>>], frameno: u64,
    inter_cfg: &InterConfig,
  ) {
    let lookahead_distance = cmp::min(
      inter_cfg.keyframe_lookahead_distance() as usize,
      frame_subset.len() - 1,
    );

    // Where A and B are scenes: AAAAAABBBAAAAAA
    // If BBB is shorter than lookahead_distance, it is detected as a flash
    // and not considered a scenecut.
    for j in 1..=lookahead_distance {
      if !self.has_scenecut(&frame_subset[0], &frame_subset[j]) {
        // Any frame in between `0` and `j` cannot be a real scenecut.
        for i in 0..=j {
          let frameno = frameno + i as u64 - 1;
          self.excluded_frames.insert(frameno);
        }
      }
    }

    // Where A-F are scenes: AAAAABBCCDDEEFFFFFF
    // If each of BB ... EE are shorter than `lookahead_distance`, they are
    // detected as flashes and not considered scenecuts.
    // Instead, the first F frame becomes a scenecut.
    // If the video ends before F, no frame becomes a scenecut.
    for i in 1..lookahead_distance {
      if self.has_scenecut(&frame_subset[i], &frame_subset[lookahead_distance])
      {
        // If the current frame is the frame before a scenecut, it cannot also be the frame of a scenecut.
        let frameno = frameno + i as u64 - 1;
        self.excluded_frames.insert(frameno);
      }
    }
  }

  /// Run a comparison between two frames to determine if they qualify for a scenecut.
  ///
  /// The current algorithm detects fast cuts using changes in colour and intensity between frames.
  /// Since the difference between frames is used, only fast cuts are detected
  /// with this method. This is intended to change via https://github.com/xiph/rav1e/issues/794.
  fn has_scenecut<T: Pixel>(
    &self, frame1: &Frame<T>, frame2: &Frame<T>,
  ) -> bool {
    let y_lines =
      frame1.planes[0].rows_iter().zip(frame2.planes[0].rows_iter());

    let mut len = frame2.planes[0].cfg.width * frame2.planes[0].cfg.height;
    let mut delta = 0;

    for (l1, l2) in y_lines {
      let delta_line = l1
        .iter()
        .zip(l2.iter())
        .map(|(&p1, &p2)| {
          (i16::cast_from(p1) - i16::cast_from(p2)).abs() as u64
        })
        .sum::<u64>();
      delta += delta_line;
    }

    if !self.fast_mode {
      let u_dec = frame1.planes[1].cfg.xdec + frame1.planes[1].cfg.ydec;
      len +=
        (frame2.planes[1].cfg.width * frame2.planes[1].cfg.height) << u_dec;

      let u_lines =
        frame1.planes[1].rows_iter().zip(frame2.planes[1].rows_iter());

      for (l1, l2) in u_lines {
        let delta_line = l1
          .iter()
          .zip(l2.iter())
          .map(|(&p1, &p2)| {
            (i16::cast_from(p1) - i16::cast_from(p2)).abs() as u64
          })
          .sum::<u64>();
        delta += delta_line << u_dec;
      }

      let v_dec = frame1.planes[2].cfg.xdec + frame1.planes[2].cfg.ydec;
      len +=
        (frame2.planes[2].cfg.width * frame2.planes[2].cfg.height) << v_dec;

      let v_lines =
        frame1.planes[2].rows_iter().zip(frame2.planes[2].rows_iter());

      for (l1, l2) in v_lines {
        let delta_line = l1
          .iter()
          .zip(l2.iter())
          .map(|(&p1, &p2)| {
            (i16::cast_from(p1) - i16::cast_from(p2)).abs() as u64
          })
          .sum::<u64>();
        delta += delta_line << v_dec;
      }
    }

    delta >= self.threshold as u64 * len as u64
  }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::collections::BTreeMap;

use itertools::Itertools;
use image::{GenericImage, Pixel, Rgb, Rgba};
use color_quant::NeuQuant;

/// Palette of colors.
#[derive(Debug, Hash, PartialEq, Eq, Default)]
pub struct Palette {
    /// Palette of Colors represented in RGB
    pub palette: Vec<Rgb<u8>>,
    /// A map of indices in the palette to a count of pixels in approximately that color in the
    pub pixel_counts: BTreeMap<usize, usize>,
}

impl Palette {
    /// Create a new palett from an image
    ///
    /// Color count and quality are given straight to [color_quant], values should be between
    /// 8...512 and 1...30 respectively. (By the way: 10 is a good default quality.)
    ///
    /// [color_quant]: https://github.com/PistonDevelopers/color_quant
    pub fn new<P, G>(image: &G, color_count: usize, quality: i32) -> Palette
        where P: Sized + Pixel<Subpixel = u8>,
              G: Sized + GenericImage<Pixel = P>
    {
        let pixels: Vec<Rgba<u8>> = image.pixels()
                                         .map(|(_, _, pixel)| pixel.to_rgba())
                                         .collect();

        let mut flat_pixels: Vec<u8> = Vec::with_capacity(pixels.len());
        for rgba in &pixels {
            if is_boring_pixel(&rgba) {
                continue;
            }

            for subpixel in rgba.channels() {
                flat_pixels.push(*subpixel);
            }
        }

        let quant = NeuQuant::new(quality, color_count, &flat_pixels);

        let pixel_counts = pixels.iter()
                                 .map(|rgba| quant.index_of(&rgba.channels()))
                                 .fold(BTreeMap::new(),
                                       |mut acc, pixel| {
                                           *acc.entry(pixel).or_insert(0) += 1;
                                           acc
                                       });

        let palette: Vec<Rgb<u8>> = quant.color_map_rgba()
                                         .iter()
                                         .chunks_lazy(4)
                                         .into_iter()
                                         .map(|rgba_iter| {
                                             let rgba_slice: Vec<u8> = rgba_iter.cloned().collect();
                                             Rgba::from_slice(&rgba_slice).clone().to_rgb()
                                         })
                                         .unique()
                                         .collect();

        Palette { palette: palette, pixel_counts: pixel_counts }
    }

    fn frequency_of(&self, color: &Rgb<u8>) -> usize {
        let index = self.palette.iter().position(|x| x.channels() == color.channels());
        if let Some(index) = index {
            *self.pixel_counts.get(&index).unwrap_or(&0)
        } else {
            0
        }
    }

    /// Change ordering of colors in palette to be of frequency using the pixel count.
    pub fn sort_by_frequency(&self) -> Self {
        let mut colors = self.palette.clone();
        colors.sort_by(|a, b| self.frequency_of(&a).cmp(&self.frequency_of(&b)));

        Palette { palette: colors, pixel_counts: self.pixel_counts.clone() }
    }
}

fn is_boring_pixel(pixel: &Rgba<u8>) -> bool {
    let (r, g, b, a) = (pixel[0], pixel[1], pixel[2], pixel[3]);

    // If pixel is mostly opaque and not white
    const MIN_ALPHA: u8 = 125;
    const MAX_COLOR: u8 = 250;

    let interesting = (a >= MIN_ALPHA) && !(r > MAX_COLOR && g > MAX_COLOR && b > MAX_COLOR);

    !interesting
}

impl fmt::Display for Palette {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let color_list = self.palette
                             .iter()
                             .map(|rgb| format!("#{:02X}{:02X}{:02X}", rgb[0], rgb[1], rgb[2]))
                             .join(", ");

        write!(f, "Color Palette {{ {} }}", color_list)
    }
}
<commit_msg>Fixup: Split Long Doc Comment<commit_after>use std::fmt;
use std::collections::BTreeMap;

use itertools::Itertools;
use image::{GenericImage, Pixel, Rgb, Rgba};
use color_quant::NeuQuant;

/// Palette of colors.
#[derive(Debug, Hash, PartialEq, Eq, Default)]
pub struct Palette {
    /// Palette of Colors represented in RGB
    pub palette: Vec<Rgb<u8>>,
    /// A map of indices in the palette to a count of pixels in approximately that color in the
    /// original image.
    pub pixel_counts: BTreeMap<usize, usize>,
}

impl Palette {
    /// Create a new palett from an image
    ///
    /// Color count and quality are given straight to [color_quant], values should be between
    /// 8...512 and 1...30 respectively. (By the way: 10 is a good default quality.)
    ///
    /// [color_quant]: https://github.com/PistonDevelopers/color_quant
    pub fn new<P, G>(image: &G, color_count: usize, quality: i32) -> Palette
        where P: Sized + Pixel<Subpixel = u8>,
              G: Sized + GenericImage<Pixel = P>
    {
        let pixels: Vec<Rgba<u8>> = image.pixels()
                                         .map(|(_, _, pixel)| pixel.to_rgba())
                                         .collect();

        let mut flat_pixels: Vec<u8> = Vec::with_capacity(pixels.len());
        for rgba in &pixels {
            if is_boring_pixel(&rgba) {
                continue;
            }

            for subpixel in rgba.channels() {
                flat_pixels.push(*subpixel);
            }
        }

        let quant = NeuQuant::new(quality, color_count, &flat_pixels);

        let pixel_counts = pixels.iter()
                                 .map(|rgba| quant.index_of(&rgba.channels()))
                                 .fold(BTreeMap::new(),
                                       |mut acc, pixel| {
                                           *acc.entry(pixel).or_insert(0) += 1;
                                           acc
                                       });

        let palette: Vec<Rgb<u8>> = quant.color_map_rgba()
                                         .iter()
                                         .chunks_lazy(4)
                                         .into_iter()
                                         .map(|rgba_iter| {
                                             let rgba_slice: Vec<u8> = rgba_iter.cloned().collect();
                                             Rgba::from_slice(&rgba_slice).clone().to_rgb()
                                         })
                                         .unique()
                                         .collect();

        Palette {
            palette: palette,
            pixel_counts: pixel_counts,
        }
    }

    fn frequency_of(&self, color: &Rgb<u8>) -> usize {
        let index = self.palette.iter().position(|x| x.channels() == color.channels());
        if let Some(index) = index {
            *self.pixel_counts.get(&index).unwrap_or(&0)
        } else {
            0
        }
    }

    /// Change ordering of colors in palette to be of frequency using the pixel count.
    pub fn sort_by_frequency(&self) -> Self {
        let mut colors = self.palette.clone();
        colors.sort_by(|a, b| self.frequency_of(&a).cmp(&self.frequency_of(&b)));

        Palette {
            palette: colors,
            pixel_counts: self.pixel_counts.clone(),
        }
    }
}

fn is_boring_pixel(pixel: &Rgba<u8>) -> bool {
    let (r, g, b, a) = (pixel[0], pixel[1], pixel[2], pixel[3]);

    // If pixel is mostly opaque and not white
    const MIN_ALPHA: u8 = 125;
    const MAX_COLOR: u8 = 250;

    let interesting = (a >= MIN_ALPHA) && !(r > MAX_COLOR && g > MAX_COLOR && b > MAX_COLOR);

    !interesting
}

impl fmt::Display for Palette {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let color_list = self.palette
                             .iter()
                             .map(|rgb| format!("#{:02X}{:02X}{:02X}", rgb[0], rgb[1], rgb[2]))
                             .join(", ");

        write!(f, "Color Palette {{ {} }}", color_list)
    }
}
<|endoftext|>


<commit_before>//! Decoding of netpbm image formats (pbm, pgm, ppm and pam).
//!
//! The formats pbm, pgm and ppm are fully supported. The pam decoder recognizes the tuple types
//! `BLACKANDWHITE`, `GRAYSCALE` and `RGB` and explicitely recognizes but rejects their `_ALPHA`
//! variants for now as alpha color types are unsupported.
use self::autobreak::AutoBreak;
pub use self::decoder::PnmDecoder;
pub use self::encoder::PNMEncoder;
use self::header::HeaderRecord;
pub use self::header::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader,
                       PixmapHeader};
pub use self::header::{PNMHeader, PNMSubtype, SampleEncoding};

mod autobreak;
mod decoder;
mod encoder;
mod header;

#[cfg(test)]
mod tests {
    use super::*;
    use byteorder::{ByteOrder, NativeEndian};
    use crate::color::ColorType;
    use crate::image::ImageDecoder;

    fn execute_roundtrip_default(buffer: &[u8], width: u32, height: u32, color: ColorType) {
        let mut encoded_buffer = Vec::new();

        {
            let mut encoder = PNMEncoder::new(&mut encoded_buffer);
            encoder
                .encode(buffer, width, height, color)
                .expect("Failed to encode the image buffer");
        }

        let (header, loaded_color, loaded_image) = {
            let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
            let color_type = decoder.color_type();
            let mut image = vec![0; decoder.total_bytes() as usize];
            decoder.read_image(&mut image).expect("Failed to decode the image");
            let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
            (header, color_type, image)
        };

        assert_eq!(header.width(), width);
        assert_eq!(header.height(), height);
        assert_eq!(loaded_color, color);
        assert_eq!(loaded_image.as_slice(), buffer);
    }

    fn execute_roundtrip_with_subtype(
        buffer: &[u8],
        width: u32,
        height: u32,
        color: ColorType,
        subtype: PNMSubtype,
    ) {
        let mut encoded_buffer = Vec::new();

        {
            let mut encoder = PNMEncoder::new(&mut encoded_buffer).with_subtype(subtype);
            encoder
                .encode(buffer, width, height, color)
                .expect("Failed to encode the image buffer");
        }

        let (header, loaded_color, loaded_image) = {
            let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
            let color_type = decoder.color_type();
            let mut image = vec![0; decoder.total_bytes() as usize];
            decoder.read_image(&mut image).expect("Failed to decode the image");
            let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
            (header, color_type, image)
        };

        assert_eq!(header.width(), width);
        assert_eq!(header.height(), height);
        assert_eq!(header.subtype(), subtype);
        assert_eq!(loaded_color, color);
        assert_eq!(loaded_image.as_slice(), buffer);
    }

    fn execute_roundtrip_u16(buffer: &[u16], width: u32, height: u32, color: ColorType) {
        let mut encoded_buffer = Vec::new();

        {
            let mut encoder = PNMEncoder::new(&mut encoded_buffer);
            encoder
                .encode(buffer, width, height, color)
                .expect("Failed to encode the image buffer");
        }

        let (header, loaded_color, loaded_image) = {
            let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
            let color_type = decoder.color_type();
            let mut image = vec![0; decoder.total_bytes() as usize];
            decoder.read_image(&mut image).expect("Failed to decode the image");
            let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
            (header, color_type, image)
        };

        let mut buffer_u8 = vec![0; buffer.len() * 2];
        NativeEndian::write_u16_into(buffer, &mut buffer_u8[..]);

        assert_eq!(header.width(), width);
        assert_eq!(header.height(), height);
        assert_eq!(loaded_color, color);
        assert_eq!(loaded_image, buffer_u8);
    }

    #[test]
    fn roundtrip_rgb() {
        #[rustfmt::skip]
        let buf: [u8; 27] = [
              0,   0,   0,
              0,   0, 255,
              0, 255,   0,
              0, 255, 255,
            255,   0,   0,
            255,   0, 255,
            255, 255,   0,
            255, 255, 255,
            255, 255, 255,
        ];
        execute_roundtrip_default(&buf, 3, 3, ColorType::Rgb8);
        execute_roundtrip_with_subtype(&buf, 3, 3, ColorType::Rgb8, PNMSubtype::ArbitraryMap);
        execute_roundtrip_with_subtype(
            &buf,
            3,
            3,
            ColorType::Rgb8,
            PNMSubtype::Pixmap(SampleEncoding::Binary),
        );
        execute_roundtrip_with_subtype(
            &buf,
            3,
            3,
            ColorType::Rgb8,
            PNMSubtype::Pixmap(SampleEncoding::Ascii),
        );
    }

    #[test]
    fn roundtrip_u16() {
        let buf: [u16; 6] = [0, 1, 0xFFFF, 0x1234, 0x3412, 0xBEAF];

        execute_roundtrip_u16(&buf, 6, 1, ColorType::L16);
    }
}
<commit_msg>Add an extra roundtrip test for gray data<commit_after>//! Decoding of netpbm image formats (pbm, pgm, ppm and pam).
//!
//! The formats pbm, pgm and ppm are fully supported. The pam decoder recognizes the tuple types
//! `BLACKANDWHITE`, `GRAYSCALE` and `RGB` and explicitely recognizes but rejects their `_ALPHA`
//! variants for now as alpha color types are unsupported.
use self::autobreak::AutoBreak;
pub use self::decoder::PnmDecoder;
pub use self::encoder::PNMEncoder;
use self::header::HeaderRecord;
pub use self::header::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader,
                       PixmapHeader};
pub use self::header::{PNMHeader, PNMSubtype, SampleEncoding};

mod autobreak;
mod decoder;
mod encoder;
mod header;

#[cfg(test)]
mod tests {
    use super::*;
    use byteorder::{ByteOrder, NativeEndian};
    use crate::color::ColorType;
    use crate::image::ImageDecoder;

    fn execute_roundtrip_default(buffer: &[u8], width: u32, height: u32, color: ColorType) {
        let mut encoded_buffer = Vec::new();

        {
            let mut encoder = PNMEncoder::new(&mut encoded_buffer);
            encoder
                .encode(buffer, width, height, color)
                .expect("Failed to encode the image buffer");
        }

        let (header, loaded_color, loaded_image) = {
            let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
            let color_type = decoder.color_type();
            let mut image = vec![0; decoder.total_bytes() as usize];
            decoder.read_image(&mut image).expect("Failed to decode the image");
            let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
            (header, color_type, image)
        };

        assert_eq!(header.width(), width);
        assert_eq!(header.height(), height);
        assert_eq!(loaded_color, color);
        assert_eq!(loaded_image.as_slice(), buffer);
    }

    fn execute_roundtrip_with_subtype(
        buffer: &[u8],
        width: u32,
        height: u32,
        color: ColorType,
        subtype: PNMSubtype,
    ) {
        let mut encoded_buffer = Vec::new();

        {
            let mut encoder = PNMEncoder::new(&mut encoded_buffer).with_subtype(subtype);
            encoder
                .encode(buffer, width, height, color)
                .expect("Failed to encode the image buffer");
        }

        let (header, loaded_color, loaded_image) = {
            let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
            let color_type = decoder.color_type();
            let mut image = vec![0; decoder.total_bytes() as usize];
            decoder.read_image(&mut image).expect("Failed to decode the image");
            let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
            (header, color_type, image)
        };

        assert_eq!(header.width(), width);
        assert_eq!(header.height(), height);
        assert_eq!(header.subtype(), subtype);
        assert_eq!(loaded_color, color);
        assert_eq!(loaded_image.as_slice(), buffer);
    }

    fn execute_roundtrip_u16(buffer: &[u16], width: u32, height: u32, color: ColorType) {
        let mut encoded_buffer = Vec::new();

        {
            let mut encoder = PNMEncoder::new(&mut encoded_buffer);
            encoder
                .encode(buffer, width, height, color)
                .expect("Failed to encode the image buffer");
        }

        let (header, loaded_color, loaded_image) = {
            let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
            let color_type = decoder.color_type();
            let mut image = vec![0; decoder.total_bytes() as usize];
            decoder.read_image(&mut image).expect("Failed to decode the image");
            let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
            (header, color_type, image)
        };

        let mut buffer_u8 = vec![0; buffer.len() * 2];
        NativeEndian::write_u16_into(buffer, &mut buffer_u8[..]);

        assert_eq!(header.width(), width);
        assert_eq!(header.height(), height);
        assert_eq!(loaded_color, color);
        assert_eq!(loaded_image, buffer_u8);
    }

    #[test]
    fn roundtrip_gray() {
        #[rustfmt::skip]
        let buf: [u8; 16] = [
            0, 0, 0, 255,
            255, 255, 255, 255,
            255, 0, 255, 0,
            255, 0, 0, 0,
        ];

        execute_roundtrip_default(&buf, 4, 4, ColorType::L8);
        execute_roundtrip_with_subtype(&buf, 4, 4, ColorType::L8, PNMSubtype::ArbitraryMap);
        execute_roundtrip_with_subtype(&buf, 4, 4, ColorType::L8, PNMSubtype::Graymap(SampleEncoding::Ascii));
        execute_roundtrip_with_subtype(&buf, 4, 4, ColorType::L8, PNMSubtype::Graymap(SampleEncoding::Binary));
    }

    #[test]
    fn roundtrip_rgb() {
        #[rustfmt::skip]
        let buf: [u8; 27] = [
              0,   0,   0,
              0,   0, 255,
              0, 255,   0,
              0, 255, 255,
            255,   0,   0,
            255,   0, 255,
            255, 255,   0,
            255, 255, 255,
            255, 255, 255,
        ];
        execute_roundtrip_default(&buf, 3, 3, ColorType::Rgb8);
        execute_roundtrip_with_subtype(&buf, 3, 3, ColorType::Rgb8, PNMSubtype::ArbitraryMap);
        execute_roundtrip_with_subtype(
            &buf,
            3,
            3,
            ColorType::Rgb8,
            PNMSubtype::Pixmap(SampleEncoding::Binary),
        );
        execute_roundtrip_with_subtype(
            &buf,
            3,
            3,
            ColorType::Rgb8,
            PNMSubtype::Pixmap(SampleEncoding::Ascii),
        );
    }

    #[test]
    fn roundtrip_u16() {
        let buf: [u16; 6] = [0, 1, 0xFFFF, 0x1234, 0x3412, 0xBEAF];

        execute_roundtrip_u16(&buf, 6, 1, ColorType::L16);
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use dbus::{arg::IterAppend, Path};
use dbus_tree::{MTSync, MethodErr, PropInfo, Tree};
use futures::executor::block_on;

use crate::{
    dbus_api::{blockdev::prop_conv, types::TData},
    engine::{BlockDev, BlockDevTier, Engine, LockKey, Pool},
};

/// Perform an operation on a `BlockDev` object for a given
/// DBus implicit argument that is a block device
pub fn blockdev_operation<F, R, E>(
    tree: &Tree<MTSync<TData<E>>, TData<E>>,
    object_path: &Path<'static>,
    closure: F,
) -> Result<R, String>
where
    F: Fn(BlockDevTier, &<E::Pool as Pool>::BlockDev) -> Result<R, String>,
    R: dbus::arg::Append,
    E: Engine,
{
    let dbus_context = tree.get_data();

    let blockdev_path = tree
        .get(object_path)
        .expect("tree must contain implicit argument");

    let blockdev_data = blockdev_path
        .get_data()
        .as_ref()
        .ok_or_else(|| format!("no data for object path {}", object_path))?;
    let blockdev_uuid = typed_uuid_string_err!(blockdev_data.uuid; Dev);

    let pool_path = tree
        .get(&blockdev_data.parent)
        .ok_or_else(|| format!("no path for parent object path {}", &blockdev_data.parent))?;

    let pool_uuid = typed_uuid_string_err!(
        pool_path
            .get_data()
            .as_ref()
            .ok_or_else(|| format!("no data for object path {}", object_path))?
            .uuid;
        Pool
    );

    let pool = block_on(dbus_context.engine.get_pool(LockKey::Uuid(pool_uuid)))
        .ok_or_else(|| format!("no pool corresponding to uuid {}", &pool_uuid))?;
    let (tier, blockdev) = pool
        .get_blockdev(blockdev_uuid)
        .ok_or_else(|| format!("no blockdev with uuid {}", blockdev_data.uuid))?;
    closure(tier, blockdev)
}

/// Get a blockdev property and place it on the D-Bus. The property is
/// found by means of the getter method which takes a reference to a
/// blockdev and obtains the property from the blockdev.
pub fn get_blockdev_property<F, R, E>(
    i: &mut IterAppend<'_>,
    p: &PropInfo<'_, MTSync<TData<E>>, TData<E>>,
    getter: F,
) -> Result<(), MethodErr>
where
    F: Fn(BlockDevTier, &<E::Pool as Pool>::BlockDev) -> Result<R, String>,
    R: dbus::arg::Append,
    E: Engine,
{
    i.append(
        blockdev_operation(p.tree, p.path.get_name(), getter)
            .map_err(|ref e| MethodErr::failed(e))?,
    );
    Ok(())
}

/// Generate D-Bus representation of devnode property.
#[inline]
pub fn blockdev_devnode_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> String
where
    E: Engine,
{
    dev.metadata_path().display().to_string()
}

/// Generate D-Bus representation of hardware info property.
#[inline]
pub fn blockdev_hardware_info_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> (bool, String)
where
    E: Engine,
{
    dev.hardware_info()
        .map_or_else(|| (false, "".to_owned()), |val| (true, val.to_owned()))
}

/// Generate D-Bus representation of user info property.
#[inline]
pub fn blockdev_user_info_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> (bool, String)
where
    E: Engine,
{
    dev.user_info()
        .map_or_else(|| (false, "".to_owned()), |val| (true, val.to_owned()))
}

/// Generate D-Bus representation of initialization time property.
#[inline]
pub fn blockdev_init_time_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> u64
where
    E: Engine,
{
    dev.initialization_time().timestamp() as u64
}

/// Generate D-Bus representation of tier property.
#[inline]
pub fn blockdev_tier_prop(tier: BlockDevTier) -> u16 {
    tier as u16
}

/// Generate a D-Bus representation of the physical path
#[inline]
pub fn blockdev_physical_path_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> String
where
    E: Engine,
{
    dev.devnode().display().to_string()
}

/// Generate D-Bus representation of devnode size.
#[inline]
pub fn blockdev_size_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> String
where
    E: Engine,
{
    (*dev.size().bytes()).to_string()
}

/// Generate D-Bus representation of new block device size.
#[inline]
pub fn blockdev_new_size_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> (bool, String)
where
    E: Engine,
{
    prop_conv::blockdev_new_size_to_prop(dev.new_size())
}
<commit_msg>Use utility option_to_tuple function<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use dbus::{arg::IterAppend, Path};
use dbus_tree::{MTSync, MethodErr, PropInfo, Tree};
use futures::executor::block_on;

use crate::{
    dbus_api::{blockdev::prop_conv, types::TData, util::option_to_tuple},
    engine::{BlockDev, BlockDevTier, Engine, LockKey, Pool},
};

/// Perform an operation on a `BlockDev` object for a given
/// DBus implicit argument that is a block device
pub fn blockdev_operation<F, R, E>(
    tree: &Tree<MTSync<TData<E>>, TData<E>>,
    object_path: &Path<'static>,
    closure: F,
) -> Result<R, String>
where
    F: Fn(BlockDevTier, &<E::Pool as Pool>::BlockDev) -> Result<R, String>,
    R: dbus::arg::Append,
    E: Engine,
{
    let dbus_context = tree.get_data();

    let blockdev_path = tree
        .get(object_path)
        .expect("tree must contain implicit argument");

    let blockdev_data = blockdev_path
        .get_data()
        .as_ref()
        .ok_or_else(|| format!("no data for object path {}", object_path))?;
    let blockdev_uuid = typed_uuid_string_err!(blockdev_data.uuid; Dev);

    let pool_path = tree
        .get(&blockdev_data.parent)
        .ok_or_else(|| format!("no path for parent object path {}", &blockdev_data.parent))?;

    let pool_uuid = typed_uuid_string_err!(
        pool_path
            .get_data()
            .as_ref()
            .ok_or_else(|| format!("no data for object path {}", object_path))?
            .uuid;
        Pool
    );

    let pool = block_on(dbus_context.engine.get_pool(LockKey::Uuid(pool_uuid)))
        .ok_or_else(|| format!("no pool corresponding to uuid {}", &pool_uuid))?;
    let (tier, blockdev) = pool
        .get_blockdev(blockdev_uuid)
        .ok_or_else(|| format!("no blockdev with uuid {}", blockdev_data.uuid))?;
    closure(tier, blockdev)
}

/// Get a blockdev property and place it on the D-Bus. The property is
/// found by means of the getter method which takes a reference to a
/// blockdev and obtains the property from the blockdev.
pub fn get_blockdev_property<F, R, E>(
    i: &mut IterAppend<'_>,
    p: &PropInfo<'_, MTSync<TData<E>>, TData<E>>,
    getter: F,
) -> Result<(), MethodErr>
where
    F: Fn(BlockDevTier, &<E::Pool as Pool>::BlockDev) -> Result<R, String>,
    R: dbus::arg::Append,
    E: Engine,
{
    i.append(
        blockdev_operation(p.tree, p.path.get_name(), getter)
            .map_err(|ref e| MethodErr::failed(e))?,
    );
    Ok(())
}

/// Generate D-Bus representation of devnode property.
#[inline]
pub fn blockdev_devnode_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> String
where
    E: Engine,
{
    dev.metadata_path().display().to_string()
}

/// Generate D-Bus representation of hardware info property.
#[inline]
pub fn blockdev_hardware_info_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> (bool, String)
where
    E: Engine,
{
    option_to_tuple(dev.hardware_info().map(|s| s.to_owned()), String::new())
}

/// Generate D-Bus representation of user info property.
#[inline]
pub fn blockdev_user_info_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> (bool, String)
where
    E: Engine,
{
    option_to_tuple(dev.user_info().map(|s| s.to_owned()), String::new())
}

/// Generate D-Bus representation of initialization time property.
#[inline]
pub fn blockdev_init_time_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> u64
where
    E: Engine,
{
    dev.initialization_time().timestamp() as u64
}

/// Generate D-Bus representation of tier property.
#[inline]
pub fn blockdev_tier_prop(tier: BlockDevTier) -> u16 {
    tier as u16
}

/// Generate a D-Bus representation of the physical path
#[inline]
pub fn blockdev_physical_path_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> String
where
    E: Engine,
{
    dev.devnode().display().to_string()
}

/// Generate D-Bus representation of devnode size.
#[inline]
pub fn blockdev_size_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> String
where
    E: Engine,
{
    (*dev.size().bytes()).to_string()
}

/// Generate D-Bus representation of new block device size.
#[inline]
pub fn blockdev_new_size_prop<E>(dev: &<E::Pool as Pool>::BlockDev) -> (bool, String)
where
    E: Engine,
{
    prop_conv::blockdev_new_size_to_prop(dev.new_size())
}
<|endoftext|>


<commit_before>pub use std::{
    any::{Any, TypeId},
    cell::RefCell,
    collections::HashMap,
    fmt::Debug,
    rc::Rc,
};

pub use dces::prelude::*;

pub use crate::{
    api::*,
    css_engine::{Selector, SelectorRelation, Theme as ThemeValue, ThemeBuilder},
    proc_macros::*,
    render,
    shell::Key,
    theme::{
	colors,
	default_theme,
	fonts,
	light_theme,
	vector_graphics::{
	    material_icons_font_ttf,
	    material_icons_font,
	    material_icons_round_font,
	    material_icons_sharp_font,
	    material_icons_twotone_font,
	}
    },
    tree::*,
    utils::*,
    widgets::*,
};
<commit_msg>prelude: update woff2 'material_icons_baseline_fonts'<commit_after>pub use std::{
    any::{Any, TypeId},
    cell::RefCell,
    collections::HashMap,
    fmt::Debug,
    rc::Rc,
};

pub use dces::prelude::*;

pub use crate::{
    api::*,
    css_engine::{Selector, SelectorRelation, Theme as ThemeValue, ThemeBuilder},
    proc_macros::*,
    render,
    shell::Key,
    theme::{
        colors,
        default_theme,
        fonts,
        light_theme,
        vector_graphics::{
            material_icons_font_ttf,
            material_icons_baseline_font,
            material_icons_round_font,
            material_icons_sharp_font,
            material_icons_twotone_font,
        }
    },
    tree::*,
    utils::*,
    widgets::*,
};
<|endoftext|>


<commit_before>//! Commonly used imports, conveniently grouped.
//!
//! To easily import a bunch of commonly-used definitions, import this bundle:
//!
//! ```
//! use cursive::prelude::*;
//! ```

pub use {Cursive, Printer, With};
pub use event::{Event, Key};
pub use view::{BoxView, Checkbox, Dialog, EditView, FullView, IdView,
               KeyEventView, LinearLayout, ListView, SelectView, Selector,
               TextView, View};
pub use vec::Vec2;
pub use menu::MenuTree;
<commit_msg>Add Button to prelude.<commit_after>//! Commonly used imports, conveniently grouped.
//!
//! To easily import a bunch of commonly-used definitions, import this bundle:
//!
//! ```
//! use cursive::prelude::*;
//! ```

pub use {Cursive, Printer, With};
pub use event::{Event, Key};
pub use view::{BoxView, Button, Checkbox, Dialog, EditView, FullView, IdView,
               KeyEventView, LinearLayout, ListView, SelectView, Selector,
               TextView, View};
pub use vec::Vec2;
pub use menu::MenuTree;
<|endoftext|>


<commit_before>//	MIT License
//
//  Copyright © 2017 Michael J Simms. All rights reserved.
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in all
//	copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//	SOFTWARE.

use std::collections::HashMap;

/// Each feature has a name and value.
pub struct Feature {
    name: String,
    value: u64,
}

impl Feature {
    pub fn new (name: &str, value: u64) -> Feature {
        Feature { name: name.to_string(), value: value }
    }
}

pub type FeatureList = Vec<Feature>;
pub type Uint64Vec = Vec<u64>;
pub type FeatureNameToValuesMap<'a> = HashMap<&'a String, Uint64Vec>;

/// This class represents a sample.
/// Each sample has a name and list of features.
pub struct Sample {
    name: String,
    features: FeatureList,
}

impl Sample {
    pub fn new (name: &str) -> Sample {
        Sample { name: name.to_string(), features: Sample::create_feature_list() }
    }

    fn create_feature_list() -> FeatureList {
        let v: FeatureList = vec![];
        v
    }

    pub fn add_features(&mut self, features: &mut FeatureList) {
        self.features.append(features);
    }

    pub fn add_feature(&mut self, feature: Feature) {
        self.features.push(feature);
    }
}

pub type SampleList = Vec<Sample>;

/// Tree node, used internally.
struct Node {
    feature_name: String,
    split_value: u64,
    left: NodeLink,
    right: NodeLink,
}

impl Node {
    pub fn new (feature_name: &str, split_value: u64) -> Node {
        Node { feature_name: feature_name.to_string(), split_value: split_value, left: None, right: None }
    }

	fn set_left_subtree(&mut self, subtree: NodeBox) {
		self.left = Some(subtree);
	}

	fn set_right_subtree(&mut self, subtree: NodeBox) {
		self.right = Some(subtree);
	}
}

type NodeBox = Box<Node>;
type NodeLink = Option<Box<Node>>;
type NodeList = Vec<Box<Node>>;

/// Isolation Forest implementation.
pub struct Forest<'a> {
    feature_values: FeatureNameToValuesMap<'a>, // Lists each feature and maps it to all unique values in the training set
    trees: NodeList, // The decision trees that comprise the forest
    num_trees_to_create: u32, // The maximum number of trees to create
    sub_sampling_size: u32, // The maximum depth of a tree
}

impl<'a> Forest<'a> {
    pub fn new (num_trees_to_create: u32, sub_sampling_size: u32) -> Forest<'a> {
        Forest { num_trees_to_create: num_trees_to_create, sub_sampling_size: sub_sampling_size, trees: Forest::initialize_trees(), feature_values: Forest::create_feature_name_to_values_map() }
    }

    fn initialize_trees() -> NodeList {
        let v: NodeList = vec![];
        v
    }

    fn create_feature_name_to_values_map() -> FeatureNameToValuesMap<'a> {
        let m = HashMap::new();
        m
    }

    pub fn add_sample(&mut self, sample: Sample) {
		// Add each of this sample's features to the list of known features
		// with the corresponding set of unique values.

        for feature in &sample.features {
            if self.feature_values.contains_key(&feature.name) {
                let mut feature_value_set = &mut self.feature_values[&feature.name];
                feature_value_set.push(feature.value);
            }
            else {
                let mut feature_value_set = Vec::new();
                feature_value_set.push(feature.value);
                self.feature_values.insert(&feature.name, feature_value_set);
            }
        }
    }

    fn create_tree(&mut self, feature_values: FeatureNameToValuesMap<'a>, depth: u32) -> NodeLink {
        // Creates and returns a single tree. As this is a recursive function, depth indicates the current depth of the recursion.

		// Sanity check.
		if feature_values.len() <= 1 {
			return None;
		}

		// If we've exceeded the maximum desired depth, then stop.
		if (self.sub_sampling_size > 0) && (depth >= self.sub_sampling_size) {
			return None;
		}

		// Randomly select a feature.
		let mut selected_feature_index = 0;
		if feature_values.len() > 1 {
			return None;
		}

		// Get the value list to split on.

		// Randomly select a split value.
		let mut split_value_index = 0;

		// Create a tree node to hold the split value.
        let mut tree = Some(Box::new(Node::new("", split_value_index)));

        // Create two versions of the feature value set that we just used,
        // one for the left side of the tree and one for the right.

        // Create the left subtree.

        // Create the right subtree.

        tree
    }

    pub fn create(&mut self) {
        // Creates a forest containing the number of trees specified to the constructor.

    	for _i in 0..self.num_trees_to_create {
            let tree = self.create_tree(self.feature_values, 0);
            match tree {
                None => {
                }
                Some(ref new_tree) => {
                    self.trees.push(*new_tree);
                }
            }
        }
    }

    fn score_tree(&self, sample: &Sample, tree: &NodeBox) -> f64 {
        // Scores the sample against the specified tree.

        let mut depth = 0.0;
        let mut current_node = tree;
        let mut done = false;

        while !done {
            let mut found_feature = false;

            for feature in &sample.features {
                if feature.name == current_node.feature_name {
					if feature.value < current_node.split_value {
                        match current_node.left {
                            None => {
                                done = true;
                            }
                            Some(ref next_node) => {
                                current_node = next_node;
                            }
                        }
                    } else {
                        match current_node.right {
                            None => {
                                done = true;
                            }
                            Some(ref next_node) => {
                                current_node = next_node;
                            }
                        }
                    }

                    depth = depth + 1.0;
                    found_feature = true;
                }
            }

			// If the tree contained a feature not in the sample then take
			// both sides of the tree and average the scores together.
			if found_feature == false {
                let left_tree = &current_node.left;
                let right_tree = &current_node.right;
                let mut left_depth = depth;
                let mut right_depth = depth;

                match left_tree {
                    &None => {
                    }
                    &Some(ref left_tree) => {
        			    left_depth = left_depth + self.score_tree(sample, left_tree);
                    }
                }
                match right_tree {
                    &None => {
                    }
                    &Some(ref right_tree) => {
        			    right_depth = right_depth + self.score_tree(sample, right_tree);
                    }
                }
				return (left_depth + right_depth) / 2.0;
            }
        }
        depth
    }

    pub fn score(&self, sample: &Sample) -> f64 {
        // Scores the sample against the entire forest of trees.

        let mut score = 0.0;

        if self.trees.len() > 0 {
            for tree in &self.trees {
                score += self.score_tree(sample, tree);
            }
            score /= self.trees.len() as f64;
        }
        score
    }
}
<commit_msg>Fixed clone of feature value set.<commit_after>//	MIT License
//
//  Copyright © 2017 Michael J Simms. All rights reserved.
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in all
//	copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//	SOFTWARE.

use std::collections::HashMap;

/// Each feature has a name and value.
pub struct Feature {
    name: String,
    value: u64,
}

impl Feature {
    pub fn new (name: &str, value: u64) -> Feature {
        Feature { name: name.to_string(), value: value }
    }
}

pub type FeatureList = Vec<Feature>;
pub type Uint64Vec = Vec<u64>;
pub type FeatureNameToValuesMap<'a> = HashMap<&'a String, Uint64Vec>;

/// This class represents a sample.
/// Each sample has a name and list of features.
pub struct Sample {
    name: String,
    features: FeatureList,
}

impl Sample {
    pub fn new (name: &str) -> Sample {
        Sample { name: name.to_string(), features: Sample::create_feature_list() }
    }

    fn create_feature_list() -> FeatureList {
        let v: FeatureList = vec![];
        v
    }

    pub fn add_features(&mut self, features: &mut FeatureList) {
        self.features.append(features);
    }

    pub fn add_feature(&mut self, feature: Feature) {
        self.features.push(feature);
    }
}

pub type SampleList = Vec<Sample>;

/// Tree node, used internally.
struct Node {
    feature_name: String,
    split_value: u64,
    left: NodeLink,
    right: NodeLink,
}

impl Node {
    pub fn new (feature_name: &str, split_value: u64) -> Node {
        Node { feature_name: feature_name.to_string(), split_value: split_value, left: None, right: None }
    }

	fn set_left_subtree(&mut self, subtree: NodeBox) {
		self.left = Some(subtree);
	}

	fn set_right_subtree(&mut self, subtree: NodeBox) {
		self.right = Some(subtree);
	}
}

type NodeBox = Box<Node>;
type NodeLink = Option<Box<Node>>;
type NodeList = Vec<Box<Node>>;

/// Isolation Forest implementation.
pub struct Forest<'a> {
    feature_values: FeatureNameToValuesMap<'a>, // Lists each feature and maps it to all unique values in the training set
    trees: NodeList, // The decision trees that comprise the forest
    num_trees_to_create: u32, // The maximum number of trees to create
    sub_sampling_size: u32, // The maximum depth of a tree
}

impl<'a> Forest<'a> {
    pub fn new (num_trees_to_create: u32, sub_sampling_size: u32) -> Forest<'a> {
        Forest { num_trees_to_create: num_trees_to_create, sub_sampling_size: sub_sampling_size, trees: Forest::initialize_trees(), feature_values: Forest::create_feature_name_to_values_map() }
    }

    fn initialize_trees() -> NodeList {
        let v: NodeList = vec![];
        v
    }

    fn create_feature_name_to_values_map() -> FeatureNameToValuesMap<'a> {
        let m = HashMap::new();
        m
    }

    pub fn add_sample(&mut self, sample: Sample) {
		// Add each of this sample's features to the list of known features
		// with the corresponding set of unique values.

        for feature in &sample.features {
            if self.feature_values.contains_key(&feature.name) {
                let mut feature_value_set = &mut self.feature_values[&feature.name];
                feature_value_set.push(feature.value);
            }
            else {
                let mut feature_value_set = Vec::new();
                feature_value_set.push(feature.value);
                self.feature_values.insert(&feature.name, feature_value_set);
            }
        }
    }

    fn create_tree(&mut self, feature_values: FeatureNameToValuesMap, depth: u32) -> NodeLink {
        // Creates and returns a single tree. As this is a recursive function, depth indicates the current depth of the recursion.

		// Sanity check.
		if feature_values.len() <= 1 {
			return None;
		}

		// If we've exceeded the maximum desired depth, then stop.
		if (self.sub_sampling_size > 0) && (depth >= self.sub_sampling_size) {
			return None;
		}

		// Randomly select a feature.
		let mut selected_feature_index = 0;
		if feature_values.len() > 1 {
			return None;
		}

		// Get the value list to split on.

		// Randomly select a split value.
		let mut split_value_index = 0;

		// Create a tree node to hold the split value.
        let mut tree = Some(Box::new(Node::new("", split_value_index)));

        // Create two versions of the feature value set that we just used,
        // one for the left side of the tree and one for the right.

        // Create the left subtree.

        // Create the right subtree.

        tree
    }

    pub fn create(&mut self) {
        // Creates a forest containing the number of trees specified to the constructor.

    	for _i in 0..self.num_trees_to_create {
            let temp_feature_values = self.feature_values.clone();
            let tree = self.create_tree(temp_feature_values, 0);
            match tree {
                None => {
                }
                Some(ref new_tree) => {
                    self.trees.push(*new_tree);
                }
            }
        }
    }

    fn score_tree(&self, sample: &Sample, tree: &NodeBox) -> f64 {
        // Scores the sample against the specified tree.

        let mut depth = 0.0;
        let mut current_node = tree;
        let mut done = false;

        while !done {
            let mut found_feature = false;

            for feature in &sample.features {
                if feature.name == current_node.feature_name {
					if feature.value < current_node.split_value {
                        match current_node.left {
                            None => {
                                done = true;
                            }
                            Some(ref next_node) => {
                                current_node = next_node;
                            }
                        }
                    } else {
                        match current_node.right {
                            None => {
                                done = true;
                            }
                            Some(ref next_node) => {
                                current_node = next_node;
                            }
                        }
                    }

                    depth = depth + 1.0;
                    found_feature = true;
                }
            }

			// If the tree contained a feature not in the sample then take
			// both sides of the tree and average the scores together.
			if found_feature == false {
                let left_tree = &current_node.left;
                let right_tree = &current_node.right;
                let mut left_depth = depth;
                let mut right_depth = depth;

                match left_tree {
                    &None => {
                    }
                    &Some(ref left_tree) => {
        			    left_depth = left_depth + self.score_tree(sample, left_tree);
                    }
                }
                match right_tree {
                    &None => {
                    }
                    &Some(ref right_tree) => {
        			    right_depth = right_depth + self.score_tree(sample, right_tree);
                    }
                }
				return (left_depth + right_depth) / 2.0;
            }
        }
        depth
    }

    pub fn score(&self, sample: &Sample) -> f64 {
        // Scores the sample against the entire forest of trees.

        let mut score = 0.0;

        if self.trees.len() > 0 {
            for tree in &self.trees {
                score += self.score_tree(sample, tree);
            }
            score /= self.trees.len() as f64;
        }
        score
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the PWM peripheral.
//!
//! ## PWM channels
//!
//!
//! Note: Overlapping pins with SPI/I2C
//!
//! ## Using PWM without superuser privileges (`sudo`)
//!
//! As of kernel version 4.14.34, released on April 16 2018, it's possible to
//! configure your Raspberry Pi to allow non-root access to PWM. 4.14.34 includes
//! a [patch] that allows udev to change file permissions when a
//! PWM channel is exported. This will let any user that's a member of the `gpio`
//! group configure PWM without having to use `sudo`.
//!
//! The udev rules needed to make this work haven't been patched in yet as of
//! June 2018, but you can easily add them yourself. Make sure you're running
//! 4.14.34 or later, and append the following snippet to
//! `/etc/udev/rules.d/99-com.rules`. Reboot the Raspberry Pi afterwards.
//!
//! ```text
//! SUBSYSTEM=="pwm*", PROGRAM="/bin/sh -c '\
//!     chown -R root:gpio /sys/class/pwm && chmod -R 770 /sys/class/pwm;\
//!     chown -R root:gpio /sys/devices/platform/soc/*.pwm/pwm/pwmchip* &&\
//!     chmod -R 770 /sys/devices/platform/soc/*.pwm/pwm/pwmchip*\
//! '"
//! ```
//!
//! ## Troubleshooting
//!
//! ### Permission Denied
//!
//! [patch]: https://github.com/raspberrypi/linux/issues/1983

use std::io;
use std::result;
use std::time::Duration;

mod sysfs;

quick_error! {
/// Errors that can occur when accessing the PWM peripheral.
    #[derive(Debug)]
    pub enum Error {
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
    }
}

/// Result type returned from methods that can have `pwm::Error`s.
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Channel {
    Pwm0 = 0,
    Pwm1 = 1,
}

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Polarity {
    Normal,
    Inverse,
}

pub struct Pwm {
    channel: Channel,
}

impl Pwm {
    pub fn new(channel: Channel) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        // Always reset "enable" to 0. The sysfs interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        sysfs::set_enabled(channel as u8, false).ok();

        Ok(Pwm { channel })
    }

    pub fn with_settings(
        channel: Channel,
        period: Duration,
        duty_cycle: Duration,
        polarity: Polarity,
        enabled: bool,
    ) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs pwm driver has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        pwm.set_enabled(false).ok();

        pwm.set_period(period)?;
        pwm.set_duty_cycle(duty_cycle)?;
        pwm.set_polarity(polarity)?;
        pwm.set_enabled(enabled)?;

        Ok(pwm)
    }

    pub fn period(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::period(self.channel as u8)?))
    }

    pub fn set_period(&self, period: Duration) -> Result<()> {
        sysfs::set_period(
            self.channel as u8,
            u64::from(period.subsec_nanos())
                .saturating_add(period.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    pub fn duty_cycle(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::duty_cycle(self.channel as u8)?))
    }

    pub fn set_duty_cycle(&self, duty_cycle: Duration) -> Result<()> {
        sysfs::set_duty_cycle(
            self.channel as u8,
            u64::from(duty_cycle.subsec_nanos())
                .saturating_add(duty_cycle.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    pub fn polarity(&self) -> Result<Polarity> {
        Ok(sysfs::polarity(self.channel as u8)?)
    }

    pub fn set_polarity(&self, polarity: Polarity) -> Result<()> {
        sysfs::set_polarity(self.channel as u8, polarity)?;

        Ok(())
    }

    pub fn enabled(&self) -> Result<bool> {
        Ok(sysfs::enabled(self.channel as u8)?)
    }

    pub fn set_enabled(&self, enabled: bool) -> Result<()> {
        sysfs::set_enabled(self.channel as u8, enabled)?;

        Ok(())
    }
}

impl Drop for Pwm {
    fn drop(&mut self) {
        sysfs::set_enabled(self.channel as u8, false).ok();
        sysfs::unexport(self.channel as u8).ok();
    }
}
<commit_msg>Update documentation<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the PWM peripheral.
//!
//! ## PWM channels
//!
//!
//! Note: Overlapping pins with SPI/I2C
//!
//! ## Using PWM without superuser privileges (`sudo`)
//!
//! As of kernel version 4.14.34, released on April 16 2018, it's possible to
//! configure your Raspberry Pi to allow non-root access to PWM. 4.14.34 includes
//! a [patch] that allows udev to change file permissions when a
//! PWM channel is exported. This will let any user that's a member of the `gpio`
//! group configure PWM without having to use `sudo`.
//!
//! The udev rules needed to make this work haven't been patched in yet as of
//! June 2018, but you can easily add them yourself. Make sure you're running
//! 4.14.34 or later, and append the following snippet to
//! `/etc/udev/rules.d/99-com.rules`. Reboot the Raspberry Pi afterwards.
//!
//! ```text
//! SUBSYSTEM=="pwm*", PROGRAM="/bin/sh -c '\
//!     chown -R root:gpio /sys/class/pwm && chmod -R 770 /sys/class/pwm;\
//!     chown -R root:gpio /sys/devices/platform/soc/*.pwm/pwm/pwmchip* &&\
//!     chmod -R 770 /sys/devices/platform/soc/*.pwm/pwm/pwmchip*\
//! '"
//! ```
//!
//! ## Troubleshooting
//!
//! ### Permission Denied
//!
//! [patch]: https://github.com/raspberrypi/linux/issues/1983

use std::io;
use std::result;
use std::time::Duration;

mod sysfs;

quick_error! {
/// Errors that can occur when accessing the PWM peripheral.
    #[derive(Debug)]
    pub enum Error {
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
    }
}

/// Result type returned from methods that can have `pwm::Error`s.
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Channel {
    Pwm0 = 0,
    Pwm1 = 1,
}

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Polarity {
    Normal,
    Inverse,
}

pub struct Pwm {
    channel: Channel,
}

impl Pwm {
    /// Constructs a new `Pwm`.
    pub fn new(channel: Channel) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        // Always reset "enable" to 0. The sysfs interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        sysfs::set_enabled(channel as u8, false).ok();

        Ok(Pwm { channel })
    }

    /// Constructs a new `Pwm` using the specified settings.
    pub fn with_settings(
        channel: Channel,
        period: Duration,
        duty_cycle: Duration,
        polarity: Polarity,
        enabled: bool,
    ) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs pwm interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        pwm.set_enabled(false).ok();

        pwm.set_period(period)?;
        pwm.set_duty_cycle(duty_cycle)?;
        pwm.set_polarity(polarity)?;
        pwm.set_enabled(enabled)?;

        Ok(pwm)
    }

    pub fn period(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::period(self.channel as u8)?))
    }

    pub fn set_period(&self, period: Duration) -> Result<()> {
        sysfs::set_period(
            self.channel as u8,
            u64::from(period.subsec_nanos())
                .saturating_add(period.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    pub fn duty_cycle(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::duty_cycle(self.channel as u8)?))
    }

    pub fn set_duty_cycle(&self, duty_cycle: Duration) -> Result<()> {
        sysfs::set_duty_cycle(
            self.channel as u8,
            u64::from(duty_cycle.subsec_nanos())
                .saturating_add(duty_cycle.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    pub fn polarity(&self) -> Result<Polarity> {
        Ok(sysfs::polarity(self.channel as u8)?)
    }

    pub fn set_polarity(&self, polarity: Polarity) -> Result<()> {
        sysfs::set_polarity(self.channel as u8, polarity)?;

        Ok(())
    }

    pub fn enabled(&self) -> Result<bool> {
        Ok(sysfs::enabled(self.channel as u8)?)
    }

    pub fn set_enabled(&self, enabled: bool) -> Result<()> {
        sysfs::set_enabled(self.channel as u8, enabled)?;

        Ok(())
    }
}

impl Drop for Pwm {
    fn drop(&mut self) {
        sysfs::set_enabled(self.channel as u8, false).ok();
        sysfs::unexport(self.channel as u8).ok();
    }
}
<|endoftext|>


<commit_before>use crate::util::errors::CargoResult;
use std::hash::{self, Hash};
use url::Url;

/// A newtype wrapper around `Url` which represents a "canonical" version of an
/// original URL.
///
/// A "canonical" url is only intended for internal comparison purposes in
/// Cargo. It's to help paper over mistakes such as depending on
/// `github.com/foo/bar` vs `github.com/foo/bar.git`. This is **only** for
/// internal purposes within Cargo and provides no means to actually read the
/// underlying string value of the `Url` it contains. This is intentional,
/// because all fetching should still happen within the context of the original
/// URL.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct CanonicalUrl(Url);

impl CanonicalUrl {
    pub fn new(url: &Url) -> CargoResult<CanonicalUrl> {
        let mut url = url.clone();

        // cannot-be-a-base-urls (e.g., `github.com:rust-lang-nursery/rustfmt.git`)
        // are not supported.
        if url.cannot_be_a_base() {
            anyhow::bail!(
                "invalid url `{}`: cannot-be-a-base-URLs are not supported",
                url
            )
        }

        // Strip a trailing slash.
        if url.path().ends_with('/') {
            url.path_segments_mut().unwrap().pop_if_empty();
        }

        // For GitHub URLs specifically, just lower-case everything. GitHub
        // treats both the same, but they hash differently, and we're gonna be
        // hashing them. This wants a more general solution, and also we're
        // almost certainly not using the same case conversion rules that GitHub
        // does. (See issue #84)
        if url.host_str() == Some("github.com") {
            url.set_scheme("https").unwrap();
            let path = url.path().to_lowercase();
            url.set_path(&path);
        }

        // Repos can generally be accessed with or without `.git` extension.
        let needs_chopping = url.path().ends_with(".git");
        if needs_chopping {
            let last = {
                let last = url.path_segments().unwrap().next_back().unwrap();
                last[..last.len() - 4].to_owned()
            };
            url.path_segments_mut().unwrap().pop().push(&last);
        }

        Ok(CanonicalUrl(url))
    }

    /// Returns the raw canonicalized URL, although beware that this should
    /// never be used/displayed/etc, it should only be used for internal data
    /// structures and hashes and such.
    pub fn raw_canonicalized_url(&self) -> &Url {
        &self.0
    }
}

// See comment in `source_id.rs` for why we explicitly use `as_str()` here.
impl Hash for CanonicalUrl {
    fn hash<S: hash::Hasher>(&self, into: &mut S) {
        self.0.as_str().hash(into);
    }
}
<commit_msg>Auto merge of #7787 - alexcrichton:fix-tests, r=alexcrichton<commit_after>use crate::util::errors::CargoResult;
use std::hash::{self, Hash};
use url::Url;

/// A newtype wrapper around `Url` which represents a "canonical" version of an
/// original URL.
///
/// A "canonical" url is only intended for internal comparison purposes in
/// Cargo. It's to help paper over mistakes such as depending on
/// `github.com/foo/bar` vs `github.com/foo/bar.git`. This is **only** for
/// internal purposes within Cargo and provides no means to actually read the
/// underlying string value of the `Url` it contains. This is intentional,
/// because all fetching should still happen within the context of the original
/// URL.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct CanonicalUrl(Url);

impl CanonicalUrl {
    pub fn new(url: &Url) -> CargoResult<CanonicalUrl> {
        let mut url = url.clone();

        // cannot-be-a-base-urls (e.g., `github.com:rust-lang-nursery/rustfmt.git`)
        // are not supported.
        if url.cannot_be_a_base() {
            anyhow::bail!(
                "invalid url `{}`: cannot-be-a-base-URLs are not supported",
                url
            )
        }

        // Strip a trailing slash.
        if url.path().ends_with('/') {
            url.path_segments_mut().unwrap().pop_if_empty();
        }

        // For GitHub URLs specifically, just lower-case everything. GitHub
        // treats both the same, but they hash differently, and we're gonna be
        // hashing them. This wants a more general solution, and also we're
        // almost certainly not using the same case conversion rules that GitHub
        // does. (See issue #84)
        if url.host_str() == Some("github.com") {
            url = format!("https{}", &url[url::Position::AfterScheme..])
                .parse()
                .unwrap();
            let path = url.path().to_lowercase();
            url.set_path(&path);
        }

        // Repos can generally be accessed with or without `.git` extension.
        let needs_chopping = url.path().ends_with(".git");
        if needs_chopping {
            let last = {
                let last = url.path_segments().unwrap().next_back().unwrap();
                last[..last.len() - 4].to_owned()
            };
            url.path_segments_mut().unwrap().pop().push(&last);
        }

        Ok(CanonicalUrl(url))
    }

    /// Returns the raw canonicalized URL, although beware that this should
    /// never be used/displayed/etc, it should only be used for internal data
    /// structures and hashes and such.
    pub fn raw_canonicalized_url(&self) -> &Url {
        &self.0
    }
}

// See comment in `source_id.rs` for why we explicitly use `as_str()` here.
impl Hash for CanonicalUrl {
    fn hash<S: hash::Hasher>(&self, into: &mut S) {
        self.0.as_str().hash(into);
    }
}
<|endoftext|>