<commit_before>use crate::{Event, Image, ImageError, MsfIndex, TrackType};

use std::path::Path;

use chdr::{ChdError, ChdFile};
use chdr::metadata::CdTrackInfo;

use log::debug;

use thiserror::Error;

const BYTES_PER_SECTOR: u32 =  2352 + 96;

// TODO: Can we really assume that the first track's pregap is always
// two seconds long?
const FIRST_TRACK_PREGAP: u32 = 150;

#[derive(Debug)]
struct Track {
    start_lba: u32,
    track_type: TrackType,

    // Tracks are padded to multiples of 4 sectors in CHDs.
    // This is the number of padding sectors that has to be taken
    // into account when calculating the LBAs for a particular track.
    padding_offset: u32,
    track_info: CdTrackInfo,
}

#[derive(Debug, Error)]
pub enum ChdImageError {
    #[error(transparent)]
    ChdError(#[from] ChdError),
    #[error("CHD file does not seem like a CDROM image (wrong hunk size)")]
    WrongHunkSize,
    #[error("Wrong buffer size, needs to be 2352 bytes")]
    WrongBufferSize,
    #[error("Unsupported sector format: {0}")]
    UnsupportedSectorFormat(String)
}

pub struct ChdImage {
    chd: ChdFile,
    tracks: Vec<Track>,

    hunk: Vec<u8>,
    current_hunk_no: u32,
    current_lba: u32,
    // Starts counting from 0
    current_track: usize,

    sectors_per_hunk: u32,
}

impl ChdImage {
    pub fn open<P>(path: P) -> Result<ChdImage, ChdImageError>
        where P: AsRef<Path>
    {
        let mut chd = ChdFile::open(path)?;

        if chd.hunk_len() % BYTES_PER_SECTOR != 0 {
            return Err(ChdImageError::WrongHunkSize);
        }

        let mut hunk = vec![0; chd.hunk_len() as usize];
        chd.read_hunk(0, &mut hunk[..])?;

        let sectors_per_hunk = chd.hunk_len() / BYTES_PER_SECTOR;

        let mut tracks = Vec::new();
        if let Ok(chd_tracks) = chd.cd_tracks() {
            let mut current_lba = FIRST_TRACK_PREGAP;
            let mut total_padding = 0;
            for chd_track in chd_tracks {
                let track_type = match chd_track.track_type.as_str() {
                    "MODE1_RAW" => TrackType::Mode1,
                    "MODE2_RAW" => TrackType::Mode2,
                    "AUDIO" => TrackType::Audio,
                    _ => return Err(ChdImageError::UnsupportedSectorFormat(chd_track.track_type)),
                };
                let start_lba = current_lba;
                current_lba += chd_track.frames;
                let padding_offset = total_padding;
                let align_remainder = chd_track.frames % 4;
                if align_remainder > 0 {
                    total_padding += 4 - align_remainder;
                }
                tracks.push(Track {
                    start_lba,
                    track_type,
                    padding_offset,
                    track_info: chd_track
                });
            }
        }

        Ok(ChdImage {
            chd,
            hunk,
            current_hunk_no: 0,
            current_lba: 150,
            current_track: 0,

            sectors_per_hunk,

            tracks,
        })
    }

    fn update_current_track(&mut self, lba: u32) -> Result<(), ImageError> {
        let current_track = &self.tracks[self.current_track];
        let current_track_range_end = current_track.start_lba + current_track.track_info.frames;
        if !(current_track.start_lba..current_track_range_end).contains(&lba) {
            if let Some(index) = self.tracks.iter().position(
                |x| lba >= x.start_lba && lba < (x.start_lba + x.track_info.frames)
            ) {
                self.current_track = index;
                Ok(())
            } else {
                Err(ImageError::OutOfRange)
            }
        } else {
            Ok(())
        }
    }

    fn set_location_lba(&mut self, lba: u32) -> Result<(), ImageError> {
        self.current_lba = lba;
        // TODO: Can we really assume that the first track's pregap is always
        // two seconds long?
        if lba < FIRST_TRACK_PREGAP {
            self.current_track = 0;
            return Ok(());
        }

        self.update_current_track(lba)?;

        let current_track = &self.tracks[self.current_track];

        let lba = lba + current_track.padding_offset - FIRST_TRACK_PREGAP;
        debug!("set_location_lba {}", lba);
        let hunk_no = lba / self.sectors_per_hunk;
        if hunk_no > self.chd.num_hunks() {
            return Err(ImageError::OutOfRange);
        }
        if hunk_no != self.current_hunk_no {
            let res = self.chd.read_hunk(hunk_no, &mut self.hunk[..]);
            if let Err(e) = res {
                return Err(ChdImageError::from(e).into());
            }
            self.current_hunk_no = hunk_no;
        }
        Ok(())
    }
}

impl Image for ChdImage {
    fn num_tracks(&self) -> usize {
        self.tracks.len()
    }

    fn current_subchannel_q_valid(&self) -> bool {
        // TODO
        true
    }

    fn current_track(&self) -> Result<u8, ImageError> {
        Ok(self.current_track as u8 + 1)
    }

    fn current_index(&self) -> Result<u8, ImageError> {
        let current_track = &self.tracks[self.current_track];
        let track_local_lba = self.current_lba - current_track.start_lba;
        let index = if track_local_lba > current_track.track_info.pregap.unwrap_or(0) {
            1
        } else {
            0
        };
        Ok(index)
    }

    fn current_track_local_msf(&self) -> Result<MsfIndex, ImageError> {
        let current_track = &self.tracks[self.current_track];
        let index01_lba =
            current_track.start_lba + current_track.track_info.pregap.unwrap_or(150);

        if self.current_lba < index01_lba {
            // Negative MSFs are (100,0,0) - x
            let reference = 100 * 60 * 75;
            let offset = index01_lba - self.current_lba;
            Ok(MsfIndex::from_lba(reference - offset)?)
        } else {
            Ok(MsfIndex::from_lba(self.current_lba - index01_lba)?)
        }
    }

    fn current_global_msf(&self) -> Result<MsfIndex, ImageError> {
        Ok(MsfIndex::from_lba(self.current_lba)?)
    }

    fn current_track_type(&self) -> Result<TrackType, ImageError> {
        let current_track = &self.tracks[self.current_track];
        Ok(current_track.track_type)
    }

    fn first_track_type(&self) -> TrackType {
        self.tracks.first().unwrap().track_type
    }

    fn track_start(&self, track: u8) -> Result<MsfIndex, ImageError> {
        // Track 0: Special case for PlayStation, return length of whole disc
        // TODO: Make this less ugly?
        if track == 0 {
            let len = self.chd.num_hunks() * self.chd.hunk_len();
            let num_sectors = FIRST_TRACK_PREGAP + len / BYTES_PER_SECTOR;
            Ok(MsfIndex::from_lba(num_sectors)?)
        } else if track <= self.tracks.len() as u8 {
            let track = &self.tracks[track as usize - 1];
            let start_lba_index01 =
                track.start_lba + track.track_info.pregap.unwrap_or(150);
            debug!("track_start: {:?} {:?}", track, MsfIndex::from_lba(start_lba_index01));
            Ok(MsfIndex::from_lba(start_lba_index01)?)
        } else {
            Err(ImageError::OutOfRange)
        }
    }

    fn set_location(&mut self, target: MsfIndex) -> Result<(), ImageError> {
        self.set_location_lba(target.to_lba())
    }

    fn set_location_to_track(&mut self, track: u8) -> Result<(), ImageError> {
        debug!("set_location_to_track {}", track);
        let track_start = self.track_start(track)?;
        self.set_location(track_start)?;
        Ok(())
    }

    fn advance_position(&mut self) -> Result<Option<Event>, ImageError> {
        let old_track = self.current_track;
        let res = self.set_location_lba(self.current_lba + 1);
        if let Err(e) = res {
            if let ImageError::OutOfRange = e {
                Ok(Some(Event::EndOfDisc))
            } else {
                Err(e)
            }
        } else if self.current_track != old_track {
            Ok(Some(Event::TrackChange))
        } else {
            Ok(None)
        }
    }

    fn copy_current_sector(&self, buf: &mut[u8]) -> Result<(), ImageError> {
        if buf.len() != 2352 {
            return Err(ChdImageError::WrongBufferSize.into())
        }
        if self.current_lba < FIRST_TRACK_PREGAP {
            buf.fill(0);
            return Ok(());
        }
        let current_track = &self.tracks[self.current_track];
        let current_file_lba = self.current_lba + current_track.padding_offset - FIRST_TRACK_PREGAP;
        let sector_in_hunk = current_file_lba % self.sectors_per_hunk;
        let sector_start = (sector_in_hunk * BYTES_PER_SECTOR) as usize;
        let sector = &self.hunk[sector_start..sector_start + 2352];
        buf.clone_from_slice(sector);

        if self.current_track_type().unwrap() == TrackType::Audio {
            for x in buf.chunks_exact_mut(2) {
                x.swap(0, 1);
            }
        }

        Ok(())
    }
}<commit_msg>Add SBI handling for CHD<commit_after>use crate::{Event, Image, ImageError, MsfIndex, TrackType};

use std::collections::BTreeSet;
use std::path::Path;

use chdr::{ChdError, ChdFile};
use chdr::metadata::CdTrackInfo;

use log::{debug, info, warn};

use thiserror::Error;

const BYTES_PER_SECTOR: u32 =  2352 + 96;

// TODO: Can we really assume that the first track's pregap is always
// two seconds long?
const FIRST_TRACK_PREGAP: u32 = 150;

#[derive(Debug)]
struct Track {
    start_lba: u32,
    track_type: TrackType,

    // Tracks are padded to multiples of 4 sectors in CHDs.
    // This is the number of padding sectors that has to be taken
    // into account when calculating the LBAs for a particular track.
    padding_offset: u32,
    track_info: CdTrackInfo,
}

#[derive(Debug, Error)]
pub enum ChdImageError {
    #[error(transparent)]
    ChdError(#[from] ChdError),
    #[error("CHD file does not seem like a CDROM image (wrong hunk size)")]
    WrongHunkSize,
    #[error("Wrong buffer size, needs to be 2352 bytes")]
    WrongBufferSize,
    #[error("Unsupported sector format: {0}")]
    UnsupportedSectorFormat(String)
}

pub struct ChdImage {
    chd: ChdFile,
    tracks: Vec<Track>,

    hunk: Vec<u8>,
    current_hunk_no: u32,
    current_lba: u32,
    // Starts counting from 0
    current_track: usize,

    sectors_per_hunk: u32,

    invalid_subq_lbas: Option<BTreeSet<u32>>,
}

impl ChdImage {
    pub fn open<P>(path: P) -> Result<ChdImage, ChdImageError>
        where P: AsRef<Path>
    {
        let mut chd = ChdFile::open(path.as_ref())?;

        if chd.hunk_len() % BYTES_PER_SECTOR != 0 {
            return Err(ChdImageError::WrongHunkSize);
        }

        let mut hunk = vec![0; chd.hunk_len() as usize];
        chd.read_hunk(0, &mut hunk[..])?;

        let sectors_per_hunk = chd.hunk_len() / BYTES_PER_SECTOR;

        let mut tracks = Vec::new();
        if let Ok(chd_tracks) = chd.cd_tracks() {
            let mut current_lba = FIRST_TRACK_PREGAP;
            let mut total_padding = 0;
            for chd_track in chd_tracks {
                let track_type = match chd_track.track_type.as_str() {
                    "MODE1_RAW" => TrackType::Mode1,
                    "MODE2_RAW" => TrackType::Mode2,
                    "AUDIO" => TrackType::Audio,
                    _ => return Err(ChdImageError::UnsupportedSectorFormat(chd_track.track_type)),
                };
                let start_lba = current_lba;
                current_lba += chd_track.frames;
                let padding_offset = total_padding;
                let align_remainder = chd_track.frames % 4;
                if align_remainder > 0 {
                    total_padding += 4 - align_remainder;
                }
                tracks.push(Track {
                    start_lba,
                    track_type,
                    padding_offset,
                    track_info: chd_track
                });
            }
        }

        let sbi_path = path.as_ref().with_extension("sbi");
        let mut invalid_subq_lbas = None;
        if sbi_path.exists() {
            match crate::sbi::load_sbi_file(sbi_path) {
                Ok(set) => {
                    info!("Found and loaded SBI file");
                    invalid_subq_lbas = Some(set);
                }
                Err(e) => warn!("Failed to load SBI file: {}", e),
            }
        }

        Ok(ChdImage {
            chd,
            hunk,
            current_hunk_no: 0,
            current_lba: 150,
            current_track: 0,

            sectors_per_hunk,

            tracks,

            invalid_subq_lbas
        })
    }

    fn update_current_track(&mut self, lba: u32) -> Result<(), ImageError> {
        let current_track = &self.tracks[self.current_track];
        let current_track_range_end = current_track.start_lba + current_track.track_info.frames;
        if !(current_track.start_lba..current_track_range_end).contains(&lba) {
            if let Some(index) = self.tracks.iter().position(
                |x| lba >= x.start_lba && lba < (x.start_lba + x.track_info.frames)
            ) {
                self.current_track = index;
                Ok(())
            } else {
                Err(ImageError::OutOfRange)
            }
        } else {
            Ok(())
        }
    }

    fn set_location_lba(&mut self, lba: u32) -> Result<(), ImageError> {
        self.current_lba = lba;
        // TODO: Can we really assume that the first track's pregap is always
        // two seconds long?
        if lba < FIRST_TRACK_PREGAP {
            self.current_track = 0;
            return Ok(());
        }

        self.update_current_track(lba)?;

        let current_track = &self.tracks[self.current_track];

        let lba = lba + current_track.padding_offset - FIRST_TRACK_PREGAP;
        debug!("set_location_lba {}", lba);
        let hunk_no = lba / self.sectors_per_hunk;
        if hunk_no > self.chd.num_hunks() {
            return Err(ImageError::OutOfRange);
        }
        if hunk_no != self.current_hunk_no {
            let res = self.chd.read_hunk(hunk_no, &mut self.hunk[..]);
            if let Err(e) = res {
                return Err(ChdImageError::from(e).into());
            }
            self.current_hunk_no = hunk_no;
        }
        Ok(())
    }
}

impl Image for ChdImage {
    fn num_tracks(&self) -> usize {
        self.tracks.len()
    }

    fn current_subchannel_q_valid(&self) -> bool {
        if let Some(ref invalid_subq_lbas) = self.invalid_subq_lbas {
            !invalid_subq_lbas.contains(&self.current_lba)
        } else {
            true
        }
    }

    fn current_track(&self) -> Result<u8, ImageError> {
        Ok(self.current_track as u8 + 1)
    }

    fn current_index(&self) -> Result<u8, ImageError> {
        let current_track = &self.tracks[self.current_track];
        let track_local_lba = self.current_lba - current_track.start_lba;
        let index = if track_local_lba > current_track.track_info.pregap.unwrap_or(0) {
            1
        } else {
            0
        };
        Ok(index)
    }

    fn current_track_local_msf(&self) -> Result<MsfIndex, ImageError> {
        let current_track = &self.tracks[self.current_track];
        let index01_lba =
            current_track.start_lba + current_track.track_info.pregap.unwrap_or(150);

        if self.current_lba < index01_lba {
            // Negative MSFs are (100,0,0) - x
            let reference = 100 * 60 * 75;
            let offset = index01_lba - self.current_lba;
            Ok(MsfIndex::from_lba(reference - offset)?)
        } else {
            Ok(MsfIndex::from_lba(self.current_lba - index01_lba)?)
        }
    }

    fn current_global_msf(&self) -> Result<MsfIndex, ImageError> {
        Ok(MsfIndex::from_lba(self.current_lba)?)
    }

    fn current_track_type(&self) -> Result<TrackType, ImageError> {
        let current_track = &self.tracks[self.current_track];
        Ok(current_track.track_type)
    }

    fn first_track_type(&self) -> TrackType {
        self.tracks.first().unwrap().track_type
    }

    fn track_start(&self, track: u8) -> Result<MsfIndex, ImageError> {
        // Track 0: Special case for PlayStation, return length of whole disc
        // TODO: Make this less ugly?
        if track == 0 {
            let len = self.chd.num_hunks() * self.chd.hunk_len();
            let num_sectors = FIRST_TRACK_PREGAP + len / BYTES_PER_SECTOR;
            Ok(MsfIndex::from_lba(num_sectors)?)
        } else if track <= self.tracks.len() as u8 {
            let track = &self.tracks[track as usize - 1];
            let start_lba_index01 =
                track.start_lba + track.track_info.pregap.unwrap_or(150);
            debug!("track_start: {:?} {:?}", track, MsfIndex::from_lba(start_lba_index01));
            Ok(MsfIndex::from_lba(start_lba_index01)?)
        } else {
            Err(ImageError::OutOfRange)
        }
    }

    fn set_location(&mut self, target: MsfIndex) -> Result<(), ImageError> {
        self.set_location_lba(target.to_lba())
    }

    fn set_location_to_track(&mut self, track: u8) -> Result<(), ImageError> {
        debug!("set_location_to_track {}", track);
        let track_start = self.track_start(track)?;
        self.set_location(track_start)?;
        Ok(())
    }

    fn advance_position(&mut self) -> Result<Option<Event>, ImageError> {
        let old_track = self.current_track;
        let res = self.set_location_lba(self.current_lba + 1);
        if let Err(e) = res {
            if let ImageError::OutOfRange = e {
                Ok(Some(Event::EndOfDisc))
            } else {
                Err(e)
            }
        } else if self.current_track != old_track {
            Ok(Some(Event::TrackChange))
        } else {
            Ok(None)
        }
    }

    fn copy_current_sector(&self, buf: &mut[u8]) -> Result<(), ImageError> {
        if buf.len() != 2352 {
            return Err(ChdImageError::WrongBufferSize.into())
        }
        if self.current_lba < FIRST_TRACK_PREGAP {
            buf.fill(0);
            return Ok(());
        }
        let current_track = &self.tracks[self.current_track];
        let current_file_lba = self.current_lba + current_track.padding_offset - FIRST_TRACK_PREGAP;
        let sector_in_hunk = current_file_lba % self.sectors_per_hunk;
        let sector_start = (sector_in_hunk * BYTES_PER_SECTOR) as usize;
        let sector = &self.hunk[sector_start..sector_start + 2352];
        buf.clone_from_slice(sector);

        if self.current_track_type().unwrap() == TrackType::Audio {
            for x in buf.chunks_exact_mut(2) {
                x.swap(0, 1);
            }
        }

        Ok(())
    }
}<|endoftext|>


<commit_before>use std::net::{Ipv4Addr, AddrParseError};
use pnet::util::{MacAddr, ParseMacAddrErr};
use pnet::packet::arp::{ArpOperation, ArpOperations};
use clap::{Arg, App};

use config::Config;

const ABOUT: &'static str = "
Send an arp packet.

By default an arp request will be sent.
";

pub fn cli_main() -> Config {
    let matches = App::new("send-arp")
                          .version("0.1.0")
                          .author("Dineshs91 <dineshpy07@gmail.com>")
                          .about(ABOUT)
                          .arg(Arg::with_name("interface")
                              .short("i")
                              .required(true)
                              .takes_value(true)
                              .help("Provide the interface to be used to send packets"))
                          .arg(Arg::with_name("arp_reply")
                              .long("reply")
                              .help("Send an arp reply packet"))
                          .arg(Arg::with_name("source_ip")
                              .long("source-ip")
                              .required(true)
                              .takes_value(true)
                              .help("Set the source ip"))
                          .arg(Arg::with_name("source_mac")
                              .long("source-mac")
                              .required(true)
                              .takes_value(true)
                              .help("Set the source mac address"))
                          .arg(Arg::with_name("target_ip")
                              .long("target-ip")
                              .required(true)
                              .takes_value(true)
                              .help("Set the target ip"))
                          .arg(Arg::with_name("target_mac")
                              .long("target-mac")
                              .required(true)
                              .takes_value(true)
                              .help("Set the target mac address"))
                          .get_matches();

    let interface: String = matches.value_of("interface").unwrap().trim().to_string();

    let source_ip: Result<Ipv4Addr, AddrParseError> = matches.value_of("source_ip").unwrap()
                                                      .trim().parse();
    let source_mac: Result<MacAddr, ParseMacAddrErr> = matches.value_of("source_mac").unwrap()
                                                       .trim().parse();
    let target_ip: Result<Ipv4Addr, AddrParseError> = matches.value_of("target_ip").unwrap()
                                                      .trim().parse();
    let target_mac: Result<MacAddr, ParseMacAddrErr> = matches.value_of("target_mac").unwrap()
                                                       .trim().parse();

    let mut arp_operation: ArpOperation = ArpOperations::Request;

    if matches.is_present("arp_reply") {
        arp_operation = ArpOperations::Reply;
    }

    let config: Config = Config {
        interface: interface,
        source_ip: source_ip.unwrap(),
        source_mac: source_mac.unwrap(),
        target_ip: target_ip.unwrap(),
        target_mac: target_mac.unwrap(),
        arp_operation: arp_operation
    };

    // return cli config.
    config
}<commit_msg>Add long arg for interface<commit_after>use std::net::{Ipv4Addr, AddrParseError};
use pnet::util::{MacAddr, ParseMacAddrErr};
use pnet::packet::arp::{ArpOperation, ArpOperations};
use clap::{Arg, App};

use config::Config;

const ABOUT: &'static str = "
Send an arp packet.

By default an arp request will be sent.
";

pub fn cli_main() -> Config {
    let matches = App::new("send-arp")
                          .version("0.1.0")
                          .author("Dineshs91 <dineshpy07@gmail.com>")
                          .about(ABOUT)
                          .arg(Arg::with_name("interface")
                              .short("i")
                              .long("interface")
                              .required(true)
                              .takes_value(true)
                              .help("Provide the interface to be used to send packets"))
                          .arg(Arg::with_name("arp_reply")
                              .long("reply")
                              .help("Send an arp reply packet"))
                          .arg(Arg::with_name("source_ip")
                              .long("source-ip")
                              .required(true)
                              .takes_value(true)
                              .help("Set the source ip"))
                          .arg(Arg::with_name("source_mac")
                              .long("source-mac")
                              .required(true)
                              .takes_value(true)
                              .help("Set the source mac address"))
                          .arg(Arg::with_name("target_ip")
                              .long("target-ip")
                              .required(true)
                              .takes_value(true)
                              .help("Set the target ip"))
                          .arg(Arg::with_name("target_mac")
                              .long("target-mac")
                              .required(true)
                              .takes_value(true)
                              .help("Set the target mac address"))
                          .get_matches();

    let interface: String = matches.value_of("interface").unwrap().trim().to_string();

    let source_ip: Result<Ipv4Addr, AddrParseError> = matches.value_of("source_ip").unwrap()
                                                      .trim().parse();
    let source_mac: Result<MacAddr, ParseMacAddrErr> = matches.value_of("source_mac").unwrap()
                                                       .trim().parse();
    let target_ip: Result<Ipv4Addr, AddrParseError> = matches.value_of("target_ip").unwrap()
                                                      .trim().parse();
    let target_mac: Result<MacAddr, ParseMacAddrErr> = matches.value_of("target_mac").unwrap()
                                                       .trim().parse();

    let mut arp_operation: ArpOperation = ArpOperations::Request;

    if matches.is_present("arp_reply") {
        arp_operation = ArpOperations::Reply;
    }

    let config: Config = Config {
        interface: interface,
        source_ip: source_ip.unwrap(),
        source_mac: source_mac.unwrap(),
        target_ip: target_ip.unwrap(),
        target_mac: target_mac.unwrap(),
        arp_operation: arp_operation
    };

    // return cli config.
    config
}<|endoftext|>


<commit_before>/// Calculates the 32bit crc from a given set of data. It does not appear to
/// be the typical crc calculation and may be specific to Rocket League and/or
/// Unreal Engine. Implementation is close, but not the same as the crc create
pub fn calc_crc(data: &[u8]) -> u32 {
    !data.iter()
        .fold(!0xefcb_f201, |acc, &x|
            (acc << 8) ^ (TABLE[((u32::from(x)) ^ (acc >> 24)) as usize])
        )
}

/// Precomputed crc table. It is extremely close to IEEE housed in the
/// crc crate, but they are not interchangeable. Still need to determine
/// the differences. Copied from the Octane project.
static TABLE: [u32; 256] =
    [0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2,
     0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3,
     0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
     0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
     0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e,
     0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
     0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90,
     0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
     0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a,
     0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c,
     0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13,
     0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
     0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
     0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20,
     0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f,
     0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
     0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055,
     0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
     0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632,
     0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
     0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0,
     0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91,
     0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
     0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
     0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604,
     0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615,
     0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a,
     0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
     0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f,
     0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
     0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651,
     0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
     0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
     0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa,
     0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5,
     0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
     0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4];

#[cfg(test)]
mod tests {
    use crc::calc_crc;

    #[test]
    fn crc_rumble_test() {
        let data = include_bytes!("../assets/rumble.replay");
        assert_eq!(calc_crc(&data[..]), 2034487435);
    }
}
<commit_msg>Move static table to const<commit_after>/// Calculates the 32bit crc from a given set of data. It does not appear to
/// be the typical crc calculation and may be specific to Rocket League and/or
/// Unreal Engine. Implementation is close, but not the same as the crc create
pub fn calc_crc(data: &[u8]) -> u32 {
    !data.iter()
        .fold(!0xefcb_f201, |acc, &x|
            (acc << 8) ^ (TABLE[((u32::from(x)) ^ (acc >> 24)) as usize])
        )
}

/// Precomputed crc table. It is extremely close to IEEE housed in the
/// crc crate, but they are not interchangeable. Still need to determine
/// the differences. Copied from the Octane project.
const TABLE: [u32; 256] =
    [0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2,
     0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3,
     0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
     0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
     0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e,
     0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
     0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90,
     0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
     0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a,
     0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c,
     0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13,
     0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
     0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
     0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20,
     0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f,
     0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
     0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055,
     0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
     0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632,
     0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
     0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0,
     0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91,
     0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
     0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
     0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604,
     0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615,
     0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a,
     0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
     0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f,
     0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
     0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651,
     0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
     0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
     0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa,
     0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5,
     0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
     0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4];

#[cfg(test)]
mod tests {
    use crc::calc_crc;

    #[test]
    fn crc_rumble_test() {
        let data = include_bytes!("../assets/rumble.replay");
        assert_eq!(calc_crc(&data[..]), 2034487435);
    }
}
<|endoftext|>


<commit_before>use std::path::Path;
use super::{Result,calculate_time_difference};

/// Measurement of cpu stats at a certain time
#[derive(Debug,PartialEq)]
pub struct CpuMeasurement {
    pub precise_time_ns: u64,
    pub user: u64,
    pub nice: u64,
    pub system: u64,
    pub idle: u64,
    pub iowait: u64
}

impl CpuMeasurement {
    /// Calculate the cpu stats based on this measurement and a measurement in the future.
    /// It is advisable to make the next measurement roughly a minute from this one for the
    /// most reliable result.
    pub fn calculate_per_minute(&self, next_measurement: &CpuMeasurement) -> Result<CpuStat> {
        let time_difference = try!(calculate_time_difference(self.precise_time_ns, next_measurement.precise_time_ns));

        Ok(CpuStat {
            user: try!(super::time_adjusted(next_measurement.user, self.user, time_difference)),
            nice: try!(super::time_adjusted(next_measurement.nice, self.nice, time_difference)),
            system: try!(super::time_adjusted(next_measurement.system, self.system, time_difference)),
            idle: try!(super::time_adjusted(next_measurement.idle, self.idle, time_difference)),
            iowait: try!(super::time_adjusted(next_measurement.iowait, self.iowait, time_difference))
        })
    }
}

/// Cpu stats for a minute
#[derive(Debug,PartialEq)]
pub struct CpuStat {
    pub user: u64,
    pub nice: u64,
    pub system: u64,
    pub idle: u64,
    pub iowait: u64
}

impl CpuStat {
    /// Calculate the weight of the various components in percentages
    pub fn in_percentages(&self) -> CpuStatPercentages {
        let total = (self.user + self.system + self.idle) as f64;

        CpuStatPercentages {
            user: Self::percentage_of_total(self.user, total),
            nice: Self::percentage_of_total(self.nice, total),
            system: Self::percentage_of_total(self.system, total),
            idle: Self::percentage_of_total(self.idle, total),
            iowait: Self::percentage_of_total(self.iowait, total)
        }
    }

    fn percentage_of_total(value: u64, total: f64) -> f32 {
        (value as f64 / total * 100.0) as f32
    }
}

/// Cpu stats converted to percentages
#[derive(Debug,PartialEq)]
pub struct CpuStatPercentages {
    pub user: f32,
    pub nice: f32,
    pub system: f32,
    pub idle: f32,
    pub iowait: f32
}

#[cfg(target_os = "linux")]
pub fn read() -> Result<CpuMeasurement> {
    // columns: user nice system idle iowait irq softirq
    os::read_and_parse_proc_stat(&Path::new("/proc/stat"))
}

#[cfg(target_os = "linux")]
mod os {
    use std::path::Path;
    use std::io::BufRead;
    use time;
    use super::super::{Result,file_to_buf_reader,parse_u64};
    use super::CpuMeasurement;
    use error::ProbeError;

    pub fn read_and_parse_proc_stat(path: &Path) -> Result<CpuMeasurement> {
        let mut line = String::new();
        let mut reader = try!(file_to_buf_reader(path));
        let time = time::precise_time_ns();
        try!(reader.read_line(&mut line));

        let stats: Vec<&str> = line
            .split_whitespace()
            .skip(1)
            .collect();

        if stats.len() < 5 {
            return Err(ProbeError::UnexpectedContent("Incorrect number of stats".to_owned()));
        }

        Ok(CpuMeasurement {
            precise_time_ns: time,
            user: try!(parse_u64(stats[0])),
            nice: try!(parse_u64(stats[1])),
            system: try!(parse_u64(stats[2])),
            idle: try!(parse_u64(stats[3])),
            iowait: try!(parse_u64(stats[4]))
        })
    }
}

#[cfg(test)]
mod test {
    use super::{CpuMeasurement,CpuStat,CpuStatPercentages};
    use super::os::read_and_parse_proc_stat;
    use std::path::Path;
    use error::ProbeError;

    #[test]
    fn test_read_cpu_measurement() {
        let measurement = read_and_parse_proc_stat(&Path::new("fixtures/linux/cpu/proc_stat")).unwrap();
        assert_eq!(measurement.user, 0);
        assert_eq!(measurement.nice, 1);
        assert_eq!(measurement.system, 2);
        assert_eq!(measurement.idle, 3);
        assert_eq!(measurement.iowait, 4);
    }

    #[test]
    fn test_wrong_path() {
        match read_and_parse_proc_stat(&Path::new("bananas")) {
            Err(ProbeError::IO(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_read_and_parse_proc_stat_incomplete() {
        match read_and_parse_proc_stat(&Path::new("fixtures/linux/cpu/proc_stat_incomplete")) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_read_and_parse_proc_stat_garbage() {
        let path = Path::new("fixtures/linux/cpu/proc_stat_garbage");
        match read_and_parse_proc_stat(&path) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_wrong_times() {
        let measurement1 = CpuMeasurement {
            precise_time_ns: 90_000_000,
            user: 0,
            nice: 0,
            system: 0,
            idle: 0,
            iowait: 0
        };

        let measurement2 = CpuMeasurement {
            precise_time_ns: 60_000_000,
            user: 0,
            nice: 0,
            system: 0,
            idle: 0,
            iowait: 0
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::InvalidInput(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_full_minute() {
        let measurement1 = CpuMeasurement {
            precise_time_ns: 60_000_000,
            user: 1000,
            nice: 1100,
            system: 1200,
            idle: 1300,
            iowait: 1400
        };

        let measurement2 = CpuMeasurement {
            precise_time_ns: 120_000_000,
            user: 1006,
            nice: 1106,
            system: 1206,
            idle: 1306,
            iowait: 1406
        };

        let expected = CpuStat {
            user: 6,
            nice: 6,
            system: 6,
            idle: 6,
            iowait: 6
        };

        let stat = measurement1.calculate_per_minute(&measurement2).unwrap();

        assert_eq!(stat, expected);
    }

    #[test]
    fn test_calculate_per_minute_partial_minute() {
        let measurement1 = CpuMeasurement {
            precise_time_ns: 60_000_000,
            user: 1000,
            nice: 1100,
            system: 1200,
            idle: 1300,
            iowait: 1400
        };

        let measurement2 = CpuMeasurement {
            precise_time_ns: 90_000_000,
            user: 1006,
            nice: 1106,
            system: 1206,
            idle: 1306,
            iowait: 1406
        };

        let expected = CpuStat {
            user: 3,
            nice: 3,
            system: 3,
            idle: 3,
            iowait: 3
        };

        let stat = measurement1.calculate_per_minute(&measurement2).unwrap();

        assert_eq!(stat, expected);
    }

    #[test]
    fn test_calculate_per_minute_values_lower() {
        let measurement1 = CpuMeasurement {
            precise_time_ns: 60_000_000,
            user: 1000,
            nice: 1100,
            system: 1200,
            idle: 1300,
            iowait: 1400
        };

        let measurement2 = CpuMeasurement {
            precise_time_ns: 90_000_000,
            user: 106,
            nice: 116,
            system: 126,
            idle: 136,
            iowait: 146
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_in_percentages() {
        let stat = CpuStat {
            user: 500,
            nice: 100,
            system: 250,
            idle: 250,
            iowait: 100
        };

        let expected = CpuStatPercentages {
            user: 50.0,
            nice: 10.0,
            system: 25.0,
            idle: 25.0,
            iowait: 10.0
        };

        assert_eq!(stat.in_percentages(), expected);
    }

    #[test]
    fn test_in_percentages_fractions() {
        let stat = CpuStat {
            user: 495,
            nice: 100,
            system: 250,
            idle: 255,
            iowait: 100
        };

        let expected = CpuStatPercentages {
            user: 49.5,
            nice: 10.0,
            system: 25.0,
            idle: 25.5,
            iowait: 10.0
        };

        assert_eq!(stat.in_percentages(), expected);
    }

    #[test]
    fn test_in_percentages_integration() {
        let measurement1 = read_and_parse_proc_stat(&Path::new("fixtures/linux/cpu/proc_stat_1")).unwrap();
        let measurement2 = read_and_parse_proc_stat(&Path::new("fixtures/linux/cpu/proc_stat_2")).unwrap();
        let stat = measurement1.calculate_per_minute(&measurement2).unwrap();
        let in_percentages = stat.in_percentages();

        // Rounding in the floating point calculations can vary, so check if this
        // is in the correct range.

        assert!(in_percentages.user > 4.6);
        assert!(in_percentages.user < 4.7);

        assert!(in_percentages.nice < 0.1);

        assert!(in_percentages.system > 1.4);
        assert!(in_percentages.system < 1.5);

        assert!(in_percentages.idle > 93.8);
        assert!(in_percentages.idle < 94.0);

        assert!(in_percentages.iowait < 0.1);

        // The total of all values should be 100.

        let total = in_percentages.user + in_percentages.nice + in_percentages.system +
                      in_percentages.idle + in_percentages.iowait;

        assert!(total < 100.1);
        assert!(total > 99.9);
    }
}
<commit_msg>Refactor cpu stat to bring it more in line with other measurements<commit_after>use std::path::Path;
use super::{Result,calculate_time_difference};

/// Measurement of cpu stats at a certain time
#[derive(Debug,PartialEq)]
pub struct CpuMeasurement {
    pub precise_time_ns: u64,
    pub stat: CpuStat
}

impl CpuMeasurement {
    /// Calculate the cpu stats based on this measurement and a measurement in the future.
    /// It is advisable to make the next measurement roughly a minute from this one for the
    /// most reliable result.
    pub fn calculate_per_minute(&self, next_measurement: &CpuMeasurement) -> Result<CpuStat> {
        let time_difference = try!(calculate_time_difference(self.precise_time_ns, next_measurement.precise_time_ns));

        Ok(CpuStat {
            user: try!(super::time_adjusted(next_measurement.stat.user, self.stat.user, time_difference)),
            nice: try!(super::time_adjusted(next_measurement.stat.nice, self.stat.nice, time_difference)),
            system: try!(super::time_adjusted(next_measurement.stat.system, self.stat.system, time_difference)),
            idle: try!(super::time_adjusted(next_measurement.stat.idle, self.stat.idle, time_difference)),
            iowait: try!(super::time_adjusted(next_measurement.stat.iowait, self.stat.iowait, time_difference))
        })
    }
}

/// Cpu stats for a minute
#[derive(Debug,PartialEq)]
pub struct CpuStat {
    pub user: u64,
    pub nice: u64,
    pub system: u64,
    pub idle: u64,
    pub iowait: u64
}

impl CpuStat {
    /// Calculate the weight of the various components in percentages
    pub fn in_percentages(&self) -> CpuStatPercentages {
        let total = (self.user + self.system + self.idle) as f64;

        CpuStatPercentages {
            user: Self::percentage_of_total(self.user, total),
            nice: Self::percentage_of_total(self.nice, total),
            system: Self::percentage_of_total(self.system, total),
            idle: Self::percentage_of_total(self.idle, total),
            iowait: Self::percentage_of_total(self.iowait, total)
        }
    }

    fn percentage_of_total(value: u64, total: f64) -> f32 {
        (value as f64 / total * 100.0) as f32
    }
}

/// Cpu stats converted to percentages
#[derive(Debug,PartialEq)]
pub struct CpuStatPercentages {
    pub user: f32,
    pub nice: f32,
    pub system: f32,
    pub idle: f32,
    pub iowait: f32
}

#[cfg(target_os = "linux")]
pub fn read() -> Result<CpuMeasurement> {
    // columns: user nice system idle iowait irq softirq
    os::read_and_parse_proc_stat(&Path::new("/proc/stat"))
}

#[cfg(target_os = "linux")]
mod os {
    use std::path::Path;
    use std::io::BufRead;
    use time;
    use super::super::{Result,file_to_buf_reader,parse_u64};
    use super::{CpuMeasurement,CpuStat};
    use error::ProbeError;

    pub fn read_and_parse_proc_stat(path: &Path) -> Result<CpuMeasurement> {
        let mut line = String::new();
        let mut reader = try!(file_to_buf_reader(path));
        let time = time::precise_time_ns();
        try!(reader.read_line(&mut line));

        let stats: Vec<&str> = line
            .split_whitespace()
            .skip(1)
            .collect();

        if stats.len() < 5 {
            return Err(ProbeError::UnexpectedContent("Incorrect number of stats".to_owned()));
        }

        Ok(CpuMeasurement {
            precise_time_ns: time,
            stat: CpuStat {
                user: try!(parse_u64(stats[0])),
                nice: try!(parse_u64(stats[1])),
                system: try!(parse_u64(stats[2])),
                idle: try!(parse_u64(stats[3])),
                iowait: try!(parse_u64(stats[4]))
            }
        })
    }
}

#[cfg(test)]
mod test {
    use super::{CpuMeasurement,CpuStat,CpuStatPercentages};
    use super::os::read_and_parse_proc_stat;
    use std::path::Path;
    use error::ProbeError;

    #[test]
    fn test_read_cpu_measurement() {
        let measurement = read_and_parse_proc_stat(&Path::new("fixtures/linux/cpu/proc_stat")).unwrap();
        assert_eq!(measurement.stat.user, 0);
        assert_eq!(measurement.stat.nice, 1);
        assert_eq!(measurement.stat.system, 2);
        assert_eq!(measurement.stat.idle, 3);
        assert_eq!(measurement.stat.iowait, 4);
    }

    #[test]
    fn test_wrong_path() {
        match read_and_parse_proc_stat(&Path::new("bananas")) {
            Err(ProbeError::IO(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_read_and_parse_proc_stat_incomplete() {
        match read_and_parse_proc_stat(&Path::new("fixtures/linux/cpu/proc_stat_incomplete")) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_read_and_parse_proc_stat_garbage() {
        let path = Path::new("fixtures/linux/cpu/proc_stat_garbage");
        match read_and_parse_proc_stat(&path) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_wrong_times() {
        let measurement1 = CpuMeasurement {
            precise_time_ns: 90_000_000,
            stat: CpuStat {
                user: 0,
                nice: 0,
                system: 0,
                idle: 0,
                iowait: 0
            }
        };

        let measurement2 = CpuMeasurement {
            precise_time_ns: 60_000_000,
            stat: CpuStat {
                user: 0,
                nice: 0,
                system: 0,
                idle: 0,
                iowait: 0
            }
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::InvalidInput(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_full_minute() {
        let measurement1 = CpuMeasurement {
            precise_time_ns: 60_000_000,
            stat: CpuStat {
                user: 1000,
                nice: 1100,
                system: 1200,
                idle: 1300,
                iowait: 1400
            }
        };

        let measurement2 = CpuMeasurement {
            precise_time_ns: 120_000_000,
            stat: CpuStat {
                user: 1006,
                nice: 1106,
                system: 1206,
                idle: 1306,
                iowait: 1406
            }
        };

        let expected = CpuStat {
            user: 6,
            nice: 6,
            system: 6,
            idle: 6,
            iowait: 6
        };

        let stat = measurement1.calculate_per_minute(&measurement2).unwrap();

        assert_eq!(stat, expected);
    }

    #[test]
    fn test_calculate_per_minute_partial_minute() {
        let measurement1 = CpuMeasurement {
            precise_time_ns: 60_000_000,
            stat: CpuStat {
                user: 1000,
                nice: 1100,
                system: 1200,
                idle: 1300,
                iowait: 1400
            }
        };

        let measurement2 = CpuMeasurement {
            precise_time_ns: 90_000_000,
            stat: CpuStat {
                user: 1006,
                nice: 1106,
                system: 1206,
                idle: 1306,
                iowait: 1406
            }
        };

        let expected = CpuStat {
            user: 3,
            nice: 3,
            system: 3,
            idle: 3,
            iowait: 3
        };

        let stat = measurement1.calculate_per_minute(&measurement2).unwrap();

        assert_eq!(stat, expected);
    }

    #[test]
    fn test_calculate_per_minute_values_lower() {
        let measurement1 = CpuMeasurement {
            precise_time_ns: 60_000_000,
            stat: CpuStat {
                user: 1000,
                nice: 1100,
                system: 1200,
                idle: 1300,
                iowait: 1400
            }
        };

        let measurement2 = CpuMeasurement {
            precise_time_ns: 90_000_000,
            stat: CpuStat {
                user: 106,
                nice: 116,
                system: 126,
                idle: 136,
                iowait: 146
            }
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_in_percentages() {
        let stat = CpuStat {
            user: 500,
            nice: 100,
            system: 250,
            idle: 250,
            iowait: 100
        };

        let expected = CpuStatPercentages {
            user: 50.0,
            nice: 10.0,
            system: 25.0,
            idle: 25.0,
            iowait: 10.0
        };

        assert_eq!(stat.in_percentages(), expected);
    }

    #[test]
    fn test_in_percentages_fractions() {
        let stat = CpuStat {
            user: 495,
            nice: 100,
            system: 250,
            idle: 255,
            iowait: 100
        };

        let expected = CpuStatPercentages {
            user: 49.5,
            nice: 10.0,
            system: 25.0,
            idle: 25.5,
            iowait: 10.0
        };

        assert_eq!(stat.in_percentages(), expected);
    }

    #[test]
    fn test_in_percentages_integration() {
        let measurement1 = read_and_parse_proc_stat(&Path::new("fixtures/linux/cpu/proc_stat_1")).unwrap();
        let measurement2 = read_and_parse_proc_stat(&Path::new("fixtures/linux/cpu/proc_stat_2")).unwrap();
        let stat = measurement1.calculate_per_minute(&measurement2).unwrap();
        let in_percentages = stat.in_percentages();

        // Rounding in the floating point calculations can vary, so check if this
        // is in the correct range.

        assert!(in_percentages.user > 4.6);
        assert!(in_percentages.user < 4.7);

        assert!(in_percentages.nice < 0.1);

        assert!(in_percentages.system > 1.4);
        assert!(in_percentages.system < 1.5);

        assert!(in_percentages.idle > 93.8);
        assert!(in_percentages.idle < 94.0);

        assert!(in_percentages.iowait < 0.1);

        // The total of all values should be 100.

        let total = in_percentages.user + in_percentages.nice + in_percentages.system +
                      in_percentages.idle + in_percentages.iowait;

        assert!(total < 100.1);
        assert!(total > 99.9);
    }
}
<|endoftext|>


<commit_before>use num::integer::Integer;
use memory::Memory;
use memory::low_nibble;
use memory::high_nibble;
use extensions::Incrementor;
use ops::{mod};
use std::num::One;

pub struct Cpu<'a> {
    reg: Registers,
    mem: &'a mut Memory
}

impl<'a> Cpu<'a> {
    pub fn new(memory: &'a mut Memory) -> Cpu<'a> {
        return Cpu{reg: Registers::new(), mem: memory}
    }

    /// Execute a cycle on the cpu
    pub fn tick(&mut self) {
        let instr = self.fetch_instruction();

        match instr {
            0x00 => ops::nop(), // NOP
            0x01 => ops::ld_next_two_byte_into_reg_pair(self.mem, &mut self.reg.pc, &mut self.reg.b, &mut self.reg.c), // LD BC, nn
            0x02 => ops::write_value_to_memory_at_address(self.mem, self.reg.a.read(), self.reg.b.read(), self.reg.c.read()), // LD (BC), A
            0x03 => ops::increment_register_pair(&mut self.reg.b, &mut self.reg.c), // INC BC
            0x04 => ops::increment_register(&mut self.reg.b, &mut self.reg.f), // INC B
            0x05 => ops::decrement_register(&mut self.reg.b, &mut self.reg.f), // DEC B
            0x06 => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.b), // LD B, n
            0x07 => ops::rotate_left_with_carry(&mut self.reg.a, &mut self.reg.f), // RLC A
            0x08 => ops::write_sp_to_address_immediate(self.mem, &mut self.reg.pc, &self.reg.sp), // LD (nn), SP
            0x09 => ops::add_register_pair_to_register_pair(&mut self.reg.h, &mut self.reg.l, &self.reg.b, &self.reg.c, &mut self.reg.f), // ADD HL, BC
            0x0A => ops::ld_a_from_reg_pair_as_address(self.mem, &mut self.reg.a, &mut self.reg.b, &mut self.reg.c), // LD A, (BC)
            0x0B => ops::decrement_register_pair(&mut self.reg.b, &mut self.reg.c), // DEC BC
            0x0C => ops::increment_register(&mut self.reg.c, &mut self.reg.f), // INC C
            0x0D => ops::decrement_register(&mut self.reg.c, &mut self.reg.f), // DEC C
            0x0E => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.c), // LD C, n
            0x0F => ops::rotate_right_with_carry(&mut self.reg.a, &mut self.reg.f), // RRC A
            0x10 => return, // STOP
            0x11 => ops::ld_next_two_byte_into_reg_pair(self.mem, &mut self.reg.pc, &mut self.reg.d, &mut self.reg.e), // LD DE, nn
            0x12 => ops::write_value_to_memory_at_address(self.mem, self.reg.a.read(), self.reg.d.read(), self.reg.e.read()), // LD (DE), A
            0x13 => ops::increment_register_pair(&mut self.reg.d, &mut self.reg.e), // INC DE
            0x14 => ops::increment_register(&mut self.reg.d, &mut self.reg.f), // INC D
            0x15 => ops::decrement_register(&mut self.reg.d, &mut self.reg.f), // DEC D
            0x16 => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.d), // LD D, n
            0x17 => ops::rotate_left_with_carry(&mut self.reg.a, &mut self.reg.f), // RL A
            0x18 => ops::jump_by_signed_immediate(self.mem, &mut self.reg.pc), // JR n
            0x19 => ops::add_register_pair_to_register_pair(&mut self.reg.h, &mut self.reg.l, &self.reg.d, &self.reg.e, &mut self.reg.f), // Add HL, DE
            0x1A => ops::ld_a_from_reg_pair_as_address(self.mem, &mut self.reg.a, &mut self.reg.d, &mut self.reg.e), // LD A, (DE)
            0x1B => ops::decrement_register_pair(&mut self.reg.d, &mut self.reg.e), // DEC DE
            0x1C => ops::increment_register(&mut self.reg.e, &mut self.reg.f), // INC E
            0x1E => ops::decrement_register(&mut self.reg.e, &mut self.reg.f), // DEC E
            0x1F => ops::rotate_right_with_carry(&mut self.reg.a, &mut self.reg.f), // RR A
            0x20 => ops::relative_jmp_by_signed_immediate_if_not_zeroflag(self.mem, &mut self.reg.pc, &self.reg.f), // JR NZ, n
            0x21 => ops::ld_next_two_byte_into_reg_pair(self.mem, &mut self.reg.pc, &mut self.reg.h, &mut self.reg.l), // LD HL, nn
            0x22 => ops::write_value_to_memory_at_address_and_increment_register(self.mem, self.reg.a.read(), &mut self.reg.h, &mut self.reg.l), // LD (HL), A
            0x23 => ops::increment_register_pair(&mut self.reg.h, &mut self.reg.l), // INC HL
            0x24 => ops::increment_register(&mut self.reg.h, &mut self.reg.f), // INC H
            0x25 => ops::decrement_register(&mut self.reg.h, &mut self.reg.f), // DEC H
            0x26 => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.h), // LD H, n
            _ => return
        }
    }

    /// Fetches the instruction pointed to by the program counter
    /// and increments the pc by 1
    fn fetch_instruction(&mut self) -> u8 {
        let instr = self.mem.read_byte(self.reg.pc.val);
        self.reg.pc.increment();
        return instr;
    }
}

struct Registers {
    a: Register<u8>, b: Register<u8>, c: Register<u8>,
    d: Register<u8>, e: Register<u8>, f: Register<Flags>,
    h: Register<u8>, l: Register<u8>, // 8-bit registers

    pc: Register<u16>, sp: Register<u16>, // 16-bit registers
    m: Register<u16>, t: Register<u16> // clock
}

impl Registers {
    fn new() -> Registers {
        return Registers{
            a: Register::new(0),
            b: Register::new(0),
            c: Register::new(0),
            d: Register::new(0),
            e: Register::new(0),
            f: Register::new(Flags::empty()),
            h: Register::new(0),
            l: Register::new(0),

            pc: Register::new(0x100),
            sp: Register::new(0xFFFE),

            m: Register::new(0),
            t: Register::new(0)
        }
    }
}

pub struct Register<T: Copy> {
    val: T
}

impl<T: Copy> Register<T> {
    pub fn new(i: T) -> Register<T> {
        return Register { val: i };
    }

    pub fn read(&self) -> T {
        return self.val;
    }

    pub fn write(&mut self, i: T) {
        self.val = i;
    }
}

impl<T: Copy + Unsigned>  Register<T> {
    pub fn increment(&mut self) {
        let i = self.val;
        self.write(i + One::one());
    }

    pub fn decrement(&mut self) {
        let i = self.val;
        self.write(i - One::one());
    }
}

bitflags! {
    flags Flags: u8 {
        const ZeroFlag       = 0b10000000,
        const SubtractFlag   = 0b01000000,
        const HalfCarryFlag  = 0b00100000,
        const CarryFlag      = 0b00010000,
    }
}

#[test]
fn test_Register_new() {
    let reg: Register<u8> = Register::new(10);
    assert!(reg.val == 10);
}

#[test]
fn test_Register_read() {
    let reg: Register<u32> = Register::new(5);
    assert!(reg.read() == 5);
}

#[test]
fn test_Register_write() {
    let mut reg: Register<u16> = Register::new(483);

    reg.write(5);
    assert!(reg.val == 5);
}

#[test]
fn test_Register_increment() {
    let mut reg: Register<u16> = Register::new(483);

    reg.increment();
    assert!(reg.val == 484);

}

#[test]
fn test_Register_decrement() {
    let mut reg: Register<u16> = Register::new(401);

    reg.decrement();
    assert!(reg.val == 400);

}

#[test]
fn test_Registers_new() {
    let a = Registers::new();
    assert!(a.pc.read() == 0x100);
    assert!(a.sp.read() == 0xFFFE);
}
<commit_msg>Add error! if STOP opcode is called<commit_after>use memory::Memory;
use memory::low_nibble;
use memory::high_nibble;
use extensions::Incrementor;
use ops::{mod};
use std::num::One;

pub struct Cpu<'a> {
    reg: Registers,
    mem: &'a mut Memory
}

impl<'a> Cpu<'a> {
    pub fn new(memory: &'a mut Memory) -> Cpu<'a> {
        return Cpu{reg: Registers::new(), mem: memory}
    }

    /// Execute a cycle on the cpu
    pub fn tick(&mut self) {
        let instr = self.fetch_instruction();

        match instr {
            0x00 => ops::nop(), // NOP
            0x01 => ops::ld_next_two_byte_into_reg_pair(self.mem, &mut self.reg.pc, &mut self.reg.b, &mut self.reg.c), // LD BC, nn
            0x02 => ops::write_value_to_memory_at_address(self.mem, self.reg.a.read(), self.reg.b.read(), self.reg.c.read()), // LD (BC), A
            0x03 => ops::increment_register_pair(&mut self.reg.b, &mut self.reg.c), // INC BC
            0x04 => ops::increment_register(&mut self.reg.b, &mut self.reg.f), // INC B
            0x05 => ops::decrement_register(&mut self.reg.b, &mut self.reg.f), // DEC B
            0x06 => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.b), // LD B, n
            0x07 => ops::rotate_left_with_carry(&mut self.reg.a, &mut self.reg.f), // RLC A
            0x08 => ops::write_sp_to_address_immediate(self.mem, &mut self.reg.pc, &self.reg.sp), // LD (nn), SP
            0x09 => ops::add_register_pair_to_register_pair(&mut self.reg.h, &mut self.reg.l, &self.reg.b, &self.reg.c, &mut self.reg.f), // ADD HL, BC
            0x0A => ops::ld_a_from_reg_pair_as_address(self.mem, &mut self.reg.a, &mut self.reg.b, &mut self.reg.c), // LD A, (BC)
            0x0B => ops::decrement_register_pair(&mut self.reg.b, &mut self.reg.c), // DEC BC
            0x0C => ops::increment_register(&mut self.reg.c, &mut self.reg.f), // INC C
            0x0D => ops::decrement_register(&mut self.reg.c, &mut self.reg.f), // DEC C
            0x0E => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.c), // LD C, n
            0x0F => ops::rotate_right_with_carry(&mut self.reg.a, &mut self.reg.f), // RRC A
            0x10 => error!("STOP Op Code not implemented and is being used"), // STOP
            0x11 => ops::ld_next_two_byte_into_reg_pair(self.mem, &mut self.reg.pc, &mut self.reg.d, &mut self.reg.e), // LD DE, nn
            0x12 => ops::write_value_to_memory_at_address(self.mem, self.reg.a.read(), self.reg.d.read(), self.reg.e.read()), // LD (DE), A
            0x13 => ops::increment_register_pair(&mut self.reg.d, &mut self.reg.e), // INC DE
            0x14 => ops::increment_register(&mut self.reg.d, &mut self.reg.f), // INC D
            0x15 => ops::decrement_register(&mut self.reg.d, &mut self.reg.f), // DEC D
            0x16 => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.d), // LD D, n
            0x17 => ops::rotate_left_with_carry(&mut self.reg.a, &mut self.reg.f), // RL A
            0x18 => ops::jump_by_signed_immediate(self.mem, &mut self.reg.pc), // JR n
            0x19 => ops::add_register_pair_to_register_pair(&mut self.reg.h, &mut self.reg.l, &self.reg.d, &self.reg.e, &mut self.reg.f), // Add HL, DE
            0x1A => ops::ld_a_from_reg_pair_as_address(self.mem, &mut self.reg.a, &mut self.reg.d, &mut self.reg.e), // LD A, (DE)
            0x1B => ops::decrement_register_pair(&mut self.reg.d, &mut self.reg.e), // DEC DE
            0x1C => ops::increment_register(&mut self.reg.e, &mut self.reg.f), // INC E
            0x1E => ops::decrement_register(&mut self.reg.e, &mut self.reg.f), // DEC E
            0x1F => ops::rotate_right_with_carry(&mut self.reg.a, &mut self.reg.f), // RR A
            0x20 => ops::relative_jmp_by_signed_immediate_if_not_zeroflag(self.mem, &mut self.reg.pc, &self.reg.f), // JR NZ, n
            0x21 => ops::ld_next_two_byte_into_reg_pair(self.mem, &mut self.reg.pc, &mut self.reg.h, &mut self.reg.l), // LD HL, nn
            0x22 => ops::write_value_to_memory_at_address_and_increment_register(self.mem, self.reg.a.read(), &mut self.reg.h, &mut self.reg.l), // LD (HL), A
            0x23 => ops::increment_register_pair(&mut self.reg.h, &mut self.reg.l), // INC HL
            0x24 => ops::increment_register(&mut self.reg.h, &mut self.reg.f), // INC H
            0x25 => ops::decrement_register(&mut self.reg.h, &mut self.reg.f), // DEC H
            0x26 => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.h), // LD H, n
            _ => return
        }
    }

    /// Fetches the instruction pointed to by the program counter
    /// and increments the pc by 1
    fn fetch_instruction(&mut self) -> u8 {
        let instr = self.mem.read_byte(self.reg.pc.val);
        self.reg.pc.increment();
        return instr;
    }
}

struct Registers {
    a: Register<u8>, b: Register<u8>, c: Register<u8>,
    d: Register<u8>, e: Register<u8>, f: Register<Flags>,
    h: Register<u8>, l: Register<u8>, // 8-bit registers

    pc: Register<u16>, sp: Register<u16>, // 16-bit registers
    m: Register<u16>, t: Register<u16> // clock
}

impl Registers {
    fn new() -> Registers {
        return Registers{
            a: Register::new(0),
            b: Register::new(0),
            c: Register::new(0),
            d: Register::new(0),
            e: Register::new(0),
            f: Register::new(Flags::empty()),
            h: Register::new(0),
            l: Register::new(0),

            pc: Register::new(0x100),
            sp: Register::new(0xFFFE),

            m: Register::new(0),
            t: Register::new(0)
        }
    }
}

pub struct Register<T: Copy> {
    val: T
}

impl<T: Copy> Register<T> {
    pub fn new(i: T) -> Register<T> {
        return Register { val: i };
    }

    pub fn read(&self) -> T {
        return self.val;
    }

    pub fn write(&mut self, i: T) {
        self.val = i;
    }
}

impl<T: Copy + Unsigned>  Register<T> {
    pub fn increment(&mut self) {
        let i = self.val;
        self.write(i + One::one());
    }

    pub fn decrement(&mut self) {
        let i = self.val;
        self.write(i - One::one());
    }
}

bitflags! {
    flags Flags: u8 {
        const ZeroFlag       = 0b10000000,
        const SubtractFlag   = 0b01000000,
        const HalfCarryFlag  = 0b00100000,
        const CarryFlag      = 0b00010000,
    }
}

#[test]
fn test_Register_new() {
    let reg: Register<u8> = Register::new(10);
    assert!(reg.val == 10);
}

#[test]
fn test_Register_read() {
    let reg: Register<u32> = Register::new(5);
    assert!(reg.read() == 5);
}

#[test]
fn test_Register_write() {
    let mut reg: Register<u16> = Register::new(483);

    reg.write(5);
    assert!(reg.val == 5);
}

#[test]
fn test_Register_increment() {
    let mut reg: Register<u16> = Register::new(483);

    reg.increment();
    assert!(reg.val == 484);

}

#[test]
fn test_Register_decrement() {
    let mut reg: Register<u16> = Register::new(401);

    reg.decrement();
    assert!(reg.val == 400);

}

#[test]
fn test_Registers_new() {
    let a = Registers::new();
    assert!(a.pc.read() == 0x100);
    assert!(a.sp.read() == 0xFFFE);
}
<|endoftext|>


<commit_before>//! Implements some higher-level searching facilities.

use std::cmp::min;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use basetypes::*;
use tt::*;
use position::*;
use search::*;

/// Contains information about the current progress of a search.
pub struct SearchStatus {
    /// The starting time for the search.
    pub started_at: SystemTime,

    /// The starting (root) position for the search.
    pub position: Position,

    /// `true` if the search has stopped, `false` otherwise.
    pub done: bool,

    /// The reached search depth.
    pub depth: u8,

    /// The number of legal moves in the starting (root) position.
    pub legal_moves_count: usize,

    /// The best variations found so far. The first move in each
    /// variation will be different.
    pub variations: Vec<Variation>,

    /// The duration of the search in milliseconds.
    pub duration_millis: u64,

    /// The number of analyzed nodes.
    pub searched_nodes: NodeCount,

    /// Average number of analyzed nodes per second.
    pub nps: NodeCount,
}

/// A thread that executes consecutive searches in different starting
/// positions.
pub struct SearchThread {
    tt: Arc<Tt>,
    status: SearchStatus,
    searcher: DeepeningSearcher<MultipvSearcher<AlphabetaSearcher>>,
}

impl SearchThread {
    /// Creates a new instance.
    pub fn new(tt: Arc<Tt>) -> SearchThread {
        SearchThread {
            tt: tt.clone(),
            status: SearchStatus {
                started_at: SystemTime::now(),
                position: Position::from_fen(START_POSITION_FEN).ok().unwrap(),
                done: true,
                depth: 0,
                variations: vec![Variation {
                                     value: VALUE_MIN,
                                     bound: BOUND_LOWER,
                                     moves: vec![],
                                 }],
                legal_moves_count: 20,
                searched_nodes: 0,
                duration_millis: 0,
                nps: 0,
            },
            searcher: DeepeningSearcher::new(tt),
        }
    }

    /// Stops the current search, starts a new search, updates the
    /// status.
    ///
    /// `position` is the starting position for the new
    /// search. `variation_count` specifies how many best lines of
    /// play to calculate (the first move in each line will be
    /// different). `searchmoves` restricts the analysis to the
    /// supplied list of moves only (no restrictions if the suppied
    /// list is empty). The move format is long algebraic
    /// notation. Examples: e2e4, e7e5, e1g1 (white short castling),
    /// e7e8q (for promotion).
    #[allow(unused_variables)]
    pub fn search(&mut self,
                  position: &Position,
                  variation_count: usize,
                  mut searchmoves: Vec<String>) {
        let mut legal_moves = position.legal_moves();
        let legal_moves_count = legal_moves.len();

        // Remove all legal moves not allowed by `searchmoves`.
        if !searchmoves.is_empty() {
            searchmoves.sort();
            let mut moves = vec![];
            for m in legal_moves.iter() {
                if searchmoves.binary_search(&m.notation()).is_ok() {
                    moves.push(*m);
                }
            }
            if !moves.is_empty() {
                legal_moves = moves;
            }
        };

        self.stop();
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: position.clone(),
            depth: MAX_DEPTH,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: legal_moves,
            variation_count: variation_count,
        });

        self.status = SearchStatus {
            started_at: SystemTime::now(),
            position: position.clone(),
            done: false,
            depth: 0,
            legal_moves_count: legal_moves_count,
            variations: vec![Variation {
                                 value: VALUE_MIN,
                                 bound: BOUND_LOWER,
                                 moves: vec![],
                             }], // TODO: Set good initial value (vec![best_move]).
            searched_nodes: 0,
            duration_millis: 0,
            ..self.status
        };
    }

    /// Stops the current search, updates the status.
    ///
    /// Does nothing if the current search is already stopped.
    pub fn stop(&mut self) {
        self.searcher.terminate_search();
        while !self.status().done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    /// Returns the status of the current search.
    ///
    /// **Important note:** Consecutive calls to this method will
    /// return the same unchanged result. Only after calling `search`,
    /// `stop`, or `wait_status_update`, the result returned by
    /// `status` may change.
    #[inline(always)]
    pub fn status(&self) -> &SearchStatus {
        &self.status
    }

    /// Waits for a search status update, timing out after a specified
    /// duration or earlier.
    pub fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    /// A helper method. It updates the current status according to
    /// the received report message.
    fn process_report(&mut self, report: Report) {
        let duration = self.status.started_at.elapsed().unwrap();
        self.status.duration_millis = 1000 * duration.as_secs() +
                                      (duration.subsec_nanos() / 1000000) as u64;
        self.status.searched_nodes = report.searched_nodes;
        self.status.nps = 1000 * (self.status.nps + self.status.searched_nodes) /
                          (1000 + self.status.duration_millis);
        if self.status.depth < report.depth {
            self.status.depth = report.depth;
            self.status.variations = vec![extract_pv(&self.tt,
                                                     &self.status.position,
                                                     report.depth)];
        }
        self.status.done = report.done;
    }
}

/// Decides when the search must be terminated.
pub struct TimeManager {
    move_time_millis: u64, // move time in milliseconds
    must_play: bool,
}

impl TimeManager {
    /// Creates a new instance.
    ///
    /// `position` gives the current position. `pondering_is_allowed`
    /// tells if the engine is allowed to use opponent's time for
    /// thinking. `wtime_millis`, `btime_millis`, `winc_millis`, and
    /// `binc_millis` specify the remaining time in milliseconds, and
    /// the number of milliseconds with which the remaining time will
    /// be incremented on each move (for black and white). `movestogo`
    /// specifies the number of moves to the next time control.
    #[allow(unused_variables)]
    pub fn new(position: &Position,
               pondering_is_allowed: bool,
               wtime_millis: Option<u64>,
               btime_millis: Option<u64>,
               winc_millis: Option<u64>,
               binc_millis: Option<u64>,
               movestogo: Option<u64>)
               -> TimeManager {
        // TODO: We ignore "pondering_is_allowed".

        let (time, inc) = if position.to_move() == WHITE {
            (wtime_millis, winc_millis.unwrap_or(0))
        } else {
            (btime_millis, binc_millis.unwrap_or(0))
        };
        let time = time.unwrap_or(0);
        let movestogo = movestogo.unwrap_or(40);
        let movetime = (time + inc * movestogo) / movestogo;
        TimeManager {
            move_time_millis: min(movetime, time / 2),
            must_play: false,
        }
    }

    /// Registers the current search status with the time manager.
    pub fn update_status(&mut self, new_status: &SearchStatus) {
        // TODO: Implement smarter time management.
        self.must_play = new_status.duration_millis >= self.move_time_millis;
    }

    /// Decides if the search must be terminated.
    #[inline]
    pub fn must_play(&self) -> bool {
        self.must_play
    }
}
<commit_msg>fix docs<commit_after>//! Implements some higher-level searching facilities.

use std::cmp::min;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use basetypes::*;
use tt::*;
use position::*;
use search::*;

/// Contains information about the current progress of a search.
pub struct SearchStatus {
    /// The starting time for the search.
    pub started_at: SystemTime,

    /// The starting (root) position for the search.
    pub position: Position,

    /// `true` if the search has stopped, `false` otherwise.
    pub done: bool,

    /// The reached search depth.
    pub depth: u8,

    /// The number of legal moves in the starting (root) position.
    pub legal_moves_count: usize,

    /// The best variations found so far. The first move in each
    /// variation will be different.
    pub variations: Vec<Variation>,

    /// The duration of the search in milliseconds.
    pub duration_millis: u64,

    /// The number of analyzed nodes.
    pub searched_nodes: NodeCount,

    /// Average number of analyzed nodes per second.
    pub nps: NodeCount,
}

/// A thread that executes consecutive searches in different starting
/// positions.
pub struct SearchThread {
    tt: Arc<Tt>,
    status: SearchStatus,
    searcher: DeepeningSearcher<MultipvSearcher<AlphabetaSearcher>>,
}

impl SearchThread {
    /// Creates a new instance.
    pub fn new(tt: Arc<Tt>) -> SearchThread {
        SearchThread {
            tt: tt.clone(),
            status: SearchStatus {
                started_at: SystemTime::now(),
                position: Position::from_fen(START_POSITION_FEN).ok().unwrap(),
                done: true,
                depth: 0,
                variations: vec![Variation {
                                     value: VALUE_MIN,
                                     bound: BOUND_LOWER,
                                     moves: vec![],
                                 }],
                legal_moves_count: 20,
                searched_nodes: 0,
                duration_millis: 0,
                nps: 0,
            },
            searcher: DeepeningSearcher::new(tt),
        }
    }

    /// Terminates the currently running search (if any), starts a new
    /// search, updates the status.
    ///
    /// `position` is the starting position for the new
    /// search. `variation_count` specifies how many best lines of
    /// play to calculate (the first move in each line will be
    /// different). `searchmoves` restricts the analysis to the
    /// supplied list of moves only (no restrictions if the suppied
    /// list is empty). The move format is long algebraic
    /// notation. Examples: e2e4, e7e5, e1g1 (white short castling),
    /// e7e8q (for promotion).
    #[allow(unused_variables)]
    pub fn search(&mut self,
                  position: &Position,
                  variation_count: usize,
                  mut searchmoves: Vec<String>) {
        let mut legal_moves = position.legal_moves();
        let legal_moves_count = legal_moves.len();

        // Remove all legal moves not allowed by `searchmoves`.
        if !searchmoves.is_empty() {
            searchmoves.sort();
            let mut moves = vec![];
            for m in legal_moves.iter() {
                if searchmoves.binary_search(&m.notation()).is_ok() {
                    moves.push(*m);
                }
            }
            if !moves.is_empty() {
                legal_moves = moves;
            }
        };

        self.stop();
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: position.clone(),
            depth: MAX_DEPTH,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: legal_moves,
            variation_count: variation_count,
        });

        self.status = SearchStatus {
            started_at: SystemTime::now(),
            position: position.clone(),
            done: false,
            depth: 0,
            legal_moves_count: legal_moves_count,
            variations: vec![Variation {
                                 value: VALUE_MIN,
                                 bound: BOUND_LOWER,
                                 moves: vec![],
                             }], // TODO: Set good initial value (vec![best_move]).
            searched_nodes: 0,
            duration_millis: 0,
            ..self.status
        };
    }

    /// Terminates the currently running search (if any), updates the
    /// status.
    pub fn stop(&mut self) {
        self.searcher.terminate_search();
        while !self.status().done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    /// Returns the status of the current search.
    ///
    /// **Important note:** Consecutive calls to this method will
    /// return the same unchanged result. Only after calling `search`,
    /// `stop`, or `wait_status_update`, the result returned by
    /// `status` may change.
    #[inline(always)]
    pub fn status(&self) -> &SearchStatus {
        &self.status
    }

    /// Waits for a search status update, timing out after a specified
    /// duration or earlier.
    pub fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    /// A helper method. It updates the current status according to
    /// the received report message.
    fn process_report(&mut self, report: Report) {
        let duration = self.status.started_at.elapsed().unwrap();
        self.status.duration_millis = 1000 * duration.as_secs() +
                                      (duration.subsec_nanos() / 1000000) as u64;
        self.status.searched_nodes = report.searched_nodes;
        self.status.nps = 1000 * (self.status.nps + self.status.searched_nodes) /
                          (1000 + self.status.duration_millis);
        if self.status.depth < report.depth {
            self.status.depth = report.depth;
            self.status.variations = vec![extract_pv(&self.tt,
                                                     &self.status.position,
                                                     report.depth)];
        }
        self.status.done = report.done;
    }
}

/// Decides when the search must be terminated.
pub struct TimeManager {
    move_time_millis: u64, // move time in milliseconds
    must_play: bool,
}

impl TimeManager {
    /// Creates a new instance.
    ///
    /// `position` gives the current position. `pondering_is_allowed`
    /// tells if the engine is allowed to use opponent's time for
    /// thinking. `wtime_millis`, `btime_millis`, `winc_millis`, and
    /// `binc_millis` specify the remaining time in milliseconds, and
    /// the number of milliseconds with which the remaining time will
    /// be incremented on each move (for black and white). `movestogo`
    /// specifies the number of moves to the next time control.
    #[allow(unused_variables)]
    pub fn new(position: &Position,
               pondering_is_allowed: bool,
               wtime_millis: Option<u64>,
               btime_millis: Option<u64>,
               winc_millis: Option<u64>,
               binc_millis: Option<u64>,
               movestogo: Option<u64>)
               -> TimeManager {
        // TODO: We ignore "pondering_is_allowed".

        let (time, inc) = if position.to_move() == WHITE {
            (wtime_millis, winc_millis.unwrap_or(0))
        } else {
            (btime_millis, binc_millis.unwrap_or(0))
        };
        let time = time.unwrap_or(0);
        let movestogo = movestogo.unwrap_or(40);
        let movetime = (time + inc * movestogo) / movestogo;
        TimeManager {
            move_time_millis: min(movetime, time / 2),
            must_play: false,
        }
    }

    /// Registers the current search status with the time manager.
    pub fn update_status(&mut self, new_status: &SearchStatus) {
        // TODO: Implement smarter time management.
        self.must_play = new_status.duration_millis >= self.move_time_millis;
    }

    /// Decides if the search must be terminated.
    #[inline]
    pub fn must_play(&self) -> bool {
        self.must_play
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::convert::TryInto;
use std::env;

use crate::error::*;
use crate::source::Source;
use crate::value::{Value, ValueKind};

#[derive(Clone, Debug)]
pub struct Environment {
    /// Optional prefix that will limit access to the environment to only keys that
    /// begin with the defined prefix.
    ///
    /// A prefix with a separator of `_` is tested to be present on each key before its considered
    /// to be part of the source environment.
    ///
    /// For example, the key `CONFIG_DEBUG` would become `DEBUG` with a prefix of `config`.
    prefix: Option<String>,

    /// Optional character sequence that separates each key segment in an environment key pattern.
    /// Consider a nested configuration such as `redis.password`, a separator of `_` would allow
    /// an environment key of `REDIS_PASSWORD` to match.
    separator: Option<String>,

    /// Ignore empty env values (treat as unset).
    ignore_empty: bool,

    /// Parses booleans, integers and floats if they're detected (can be safely parsed).
    try_parsing: bool,
}

impl Environment {
    pub fn new() -> Self {
        Environment::default()
    }

    pub fn with_prefix(s: &str) -> Self {
        Environment {
            prefix: Some(s.into()),
            ..Environment::default()
        }
    }

    pub fn prefix(mut self, s: &str) -> Self {
        self.prefix = Some(s.into());
        self
    }

    pub fn separator(mut self, s: &str) -> Self {
        self.separator = Some(s.into());
        self
    }

    pub fn ignore_empty(mut self, ignore: bool) -> Self {
        self.ignore_empty = ignore;
        self
    }

    pub fn try_parsing(mut self, try_parsing: bool) -> Self {
        self.try_parsing = try_parsing;
        self
    }
}

impl Default for Environment {
    fn default() -> Environment {
        Environment {
            prefix: None,
            separator: None,
            ignore_empty: false,
            try_parsing: false,
        }
    }
}

impl Source for Environment {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut m = HashMap::new();
        let uri: String = "the environment".into();

        let separator = match self.separator {
            Some(ref separator) => separator,
            _ => "",
        };

        let group_separator = match self.separator {
            Some(ref separator) => separator,
            _ => "_",
        };

        // Define a prefix pattern to test and exclude from keys
        let prefix_pattern = self
            .prefix
            .as_ref()
            .map(|prefix| format!("{}{}", prefix.clone(), group_separator));

        for (key, value) in env::vars() {
            // Treat empty environment variables as unset
            if self.ignore_empty && value.is_empty() {
                continue;
            }

            let mut key = key.to_string();

            // Check for prefix
            if let Some(ref prefix_pattern) = prefix_pattern {
                if key
                    .to_lowercase()
                    .starts_with(&prefix_pattern.to_lowercase())
                {
                    // Remove this prefix from the key
                    key = key[prefix_pattern.len()..].to_string();
                } else {
                    // Skip this key
                    continue;
                }
            }

            // If separator is given replace with `.`
            if !separator.is_empty() {
                key = key.replace(separator, ".");
            }

            let value = if self.try_parsing {
                // convert to lowercase because bool parsing expects all lowercase
                if let Ok(parsed) = value.to_lowercase().parse::<bool>() {
                    ValueKind::Boolean(parsed)
                } else if let Ok(parsed) = value.parse::<i64>() {
                    ValueKind::Integer(parsed)
                } else if let Ok(parsed) = value.parse::<f64>() {
                    ValueKind::Float(parsed)
                } else {
                    ValueKind::String(value)
                }
            } else {
                ValueKind::String(value)
            };

            m.insert(key.to_lowercase(), Value::new(Some(&uri), value));
        }

        Ok(m)
    }
}
<commit_msg>Add perfromance note to `try_parsing`<commit_after>use std::collections::HashMap;
use std::convert::TryInto;
use std::env;

use crate::error::*;
use crate::source::Source;
use crate::value::{Value, ValueKind};

#[derive(Clone, Debug)]
pub struct Environment {
    /// Optional prefix that will limit access to the environment to only keys that
    /// begin with the defined prefix.
    ///
    /// A prefix with a separator of `_` is tested to be present on each key before its considered
    /// to be part of the source environment.
    ///
    /// For example, the key `CONFIG_DEBUG` would become `DEBUG` with a prefix of `config`.
    prefix: Option<String>,

    /// Optional character sequence that separates each key segment in an environment key pattern.
    /// Consider a nested configuration such as `redis.password`, a separator of `_` would allow
    /// an environment key of `REDIS_PASSWORD` to match.
    separator: Option<String>,

    /// Ignore empty env values (treat as unset).
    ignore_empty: bool,

    /// Parses booleans, integers and floats if they're detected (can be safely parsed).
    try_parsing: bool,
}

impl Environment {
    pub fn new() -> Self {
        Environment::default()
    }

    pub fn with_prefix(s: &str) -> Self {
        Environment {
            prefix: Some(s.into()),
            ..Environment::default()
        }
    }

    pub fn prefix(mut self, s: &str) -> Self {
        self.prefix = Some(s.into());
        self
    }

    pub fn separator(mut self, s: &str) -> Self {
        self.separator = Some(s.into());
        self
    }

    pub fn ignore_empty(mut self, ignore: bool) -> Self {
        self.ignore_empty = ignore;
        self
    }

    /// Note: enabling `try_parsing` can reduce performance it will try and parse
    /// each environment variable 3 times (bool, i64, f64)
    pub fn try_parsing(mut self, try_parsing: bool) -> Self {
        self.try_parsing = try_parsing;
        self
    }
}

impl Default for Environment {
    fn default() -> Environment {
        Environment {
            prefix: None,
            separator: None,
            ignore_empty: false,
            try_parsing: false,
        }
    }
}

impl Source for Environment {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut m = HashMap::new();
        let uri: String = "the environment".into();

        let separator = match self.separator {
            Some(ref separator) => separator,
            _ => "",
        };

        let group_separator = match self.separator {
            Some(ref separator) => separator,
            _ => "_",
        };

        // Define a prefix pattern to test and exclude from keys
        let prefix_pattern = self
            .prefix
            .as_ref()
            .map(|prefix| format!("{}{}", prefix.clone(), group_separator));

        for (key, value) in env::vars() {
            // Treat empty environment variables as unset
            if self.ignore_empty && value.is_empty() {
                continue;
            }

            let mut key = key.to_string();

            // Check for prefix
            if let Some(ref prefix_pattern) = prefix_pattern {
                if key
                    .to_lowercase()
                    .starts_with(&prefix_pattern.to_lowercase())
                {
                    // Remove this prefix from the key
                    key = key[prefix_pattern.len()..].to_string();
                } else {
                    // Skip this key
                    continue;
                }
            }

            // If separator is given replace with `.`
            if !separator.is_empty() {
                key = key.replace(separator, ".");
            }

            let value = if self.try_parsing {
                // convert to lowercase because bool parsing expects all lowercase
                if let Ok(parsed) = value.to_lowercase().parse::<bool>() {
                    ValueKind::Boolean(parsed)
                } else if let Ok(parsed) = value.parse::<i64>() {
                    ValueKind::Integer(parsed)
                } else if let Ok(parsed) = value.parse::<f64>() {
                    ValueKind::Float(parsed)
                } else {
                    ValueKind::String(value)
                }
            } else {
                ValueKind::String(value)
            };

            m.insert(key.to_lowercase(), Value::new(Some(&uri), value));
        }

        Ok(m)
    }
}
<|endoftext|>