<commit_before>#![allow(unused_features)]
#![cfg_attr(test, plugin(quickcheck_macros))]
#![deny(missing_docs, warnings)]
#![feature(collections)]
#![feature(core)]
#![feature(io)]
#![feature(libc)]
#![feature(path)]
#![feature(plugin)]
#![feature(std_misc)]

//! A library for serial port communication

extern crate termios;
#[cfg(test)]
extern crate quickcheck;

use std::old_io::{File, FileAccess, FileMode, IoResult};
use std::os::unix::AsRawFd;

pub use termios::BaudRate;

use termios::prelude::*;

#[cfg(test)]
mod socat;
#[cfg(test)]
mod test;

/// For how long to block `read()` calls
#[derive(Copy, PartialEq)]
pub struct BlockingMode {
    /// The device will block until *at least* `bytes` are received
    pub bytes: u8,
    /// The device will block for at least `deciseconds` after each `read()` call
    pub deciseconds: u8,
}

/// A serial device
pub struct SerialPort(File);

impl SerialPort {
    /// Opens a serial `device` in "raw" mode
    pub fn open(device: &Path, access: FileAccess) -> IoResult<SerialPort> {
        let file = try!(File::open_mode(device, FileMode::Open, access));

        let mut termios = try!(Termios::fetch(file.as_raw_fd()));
        termios.make_raw();

        let sp = SerialPort(file);

        try!(sp.update(termios));

        Ok(sp)
    }

    /// Returns the input and output baud rates
    pub fn baud_rate(&self) -> IoResult<(BaudRate, BaudRate)> {
        self.fetch().map(|termios| {
            (termios.ispeed(), termios.ospeed())
        })
    }

    /// Returns the blocking mode used by the device
    pub fn blocking_mode(&self) -> IoResult<BlockingMode> {
        self.fetch().map(|termios| {
            BlockingMode {
                bytes: termios.cc[control::Char::VMIN],
                deciseconds: termios.cc[control::Char::VTIME],
            }
        })
    }

    /// Returns the number of data bits used per character
    pub fn data_bits(&self) -> IoResult<DataBits> {
        self.fetch().map(|termios| {
            match termios.get::<control::CSIZE>() {
                control::CSIZE::CS5 => DataBits::Five,
                control::CSIZE::CS6 => DataBits::Six,
                control::CSIZE::CS7 => DataBits::Seven,
                control::CSIZE::CS8 => DataBits::Eight,
            }
        })
    }

    /// Returns the flow control used by the device
    pub fn flow_control(&self) -> IoResult<FlowControl> {
        self.fetch().map(|termios| {
            if termios.contains(control::Flag::CRTSCTS) {
                FlowControl::Hardware
            } else if termios.contains(input::Flag::IXANY) &&
                termios.contains(input::Flag::IXOFF) &&
                termios.contains(input::Flag::IXON)
            {
                FlowControl::Software
            } else {
                FlowControl::None
            }
        })
    }

    /// Returns the bit parity used by the device
    pub fn parity(&self) -> IoResult<Parity> {
        self.fetch().map(|termios| {
            match (
                termios.contains(control::Flag::PARENB),
                termios.contains(control::Flag::PARODD),
            ) {
                (true, true) => Parity::Odd,
                (true, false) => Parity::Even,
                (false, _) => Parity::None,
            }
        })
    }

    /// Changes the baud rate of the input/output or both directions
    pub fn set_baud_rate(&mut self, direction: Direction, rate: BaudRate) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match direction {
                Direction::Both => termios.set_speed(rate),
                Direction::Input => termios.set_ispeed(rate),
                Direction::Output => termios.set_ospeed(rate),
            }

            self.update(termios)
        })
    }

    /// Changes the blocking mode used by the device
    pub fn set_blocking_mode(&mut self, mode: BlockingMode) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            termios.cc[control::Char::VMIN] = mode.bytes;
            termios.cc[control::Char::VTIME] = mode.deciseconds;

            self.update(termios)
        })
    }

    /// Changes the number of data bits per character
    pub fn set_data_bits(&mut self, bits: DataBits) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            termios.set(match bits {
                DataBits::Five => control::CSIZE::CS5,
                DataBits::Six => control::CSIZE::CS6,
                DataBits::Seven => control::CSIZE::CS7,
                DataBits::Eight => control::CSIZE::CS8,
            });

            self.update(termios)
        })
    }

    /// Changes the flow control used by the device
    pub fn set_flow_control(&mut self, flow: FlowControl) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match flow {
                FlowControl::Hardware => {
                    termios.clear(input::Flag::IXANY);
                    termios.clear(input::Flag::IXOFF);
                    termios.clear(input::Flag::IXON);
                    termios.set(control::Flag::CRTSCTS);
                },
                FlowControl::None => {
                    termios.clear(control::Flag::CRTSCTS);
                    termios.clear(input::Flag::IXANY);
                    termios.clear(input::Flag::IXOFF);
                    termios.clear(input::Flag::IXON);
                },
                FlowControl::Software => {
                    termios.clear(control::Flag::CRTSCTS);
                    termios.set(input::Flag::IXANY);
                    termios.set(input::Flag::IXOFF);
                    termios.set(input::Flag::IXON);
                },
            }

            self.update(termios)
        })
    }

    /// Changes the bit parity used by the device
    pub fn set_parity(&mut self, parity: Parity) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match parity {
                Parity::Even => {
                    termios.clear(control::Flag::PARODD);
                    termios.set(control::Flag::PARENB);
                },
                Parity::None => termios.clear(control::Flag::PARENB),
                Parity::Odd => {
                    termios.set(control::Flag::PARENB);
                    termios.set(control::Flag::PARODD);
                },
            }

            self.update(termios)
        })
    }

    /// Changes the number of stop bits per character
    pub fn set_stop_bits(&mut self, bits: StopBits) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match bits {
                StopBits::One => termios.clear(control::Flag::CSTOPB),
                StopBits::Two => termios.set(control::Flag::CSTOPB),
            }

            self.update(termios)
        })
    }

    /// Returns the number of stop bits per character
    pub fn stop_bits(&self) -> IoResult<StopBits> {
        self.fetch().map(|termios| {
            if termios.contains(control::Flag::CSTOPB) {
                StopBits::Two
            } else {
                StopBits::One
            }
        })
    }

    /// Fetches the current state of the termios structure
    fn fetch(&self) -> IoResult<Termios> {
        Termios::fetch(self.0.as_raw_fd())
    }

    /// Updates the underlying termios structure
    fn update(&self, termios: Termios) -> IoResult<()> {
        termios.update(self.0.as_raw_fd(), When::Now)
    }
}

impl Reader for SerialPort {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        self.0.read(buf)
    }
}

impl Writer for SerialPort {
    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
        self.0.write_all(buf)
    }
}

#[allow(missing_docs)]
/// Number of data bits
#[derive(Copy, Debug, PartialEq)]
pub enum DataBits {
    Five,
    Six,
    Seven,
    Eight,
}

#[allow(missing_docs)]
#[derive(Copy)]
pub enum Direction {
    Both,
    Input,
    Output,
}

#[allow(missing_docs)]
/// Flow control
#[derive(Copy, Debug, PartialEq)]
pub enum FlowControl {
    Hardware,
    None,
    Software,
}

#[allow(missing_docs)]
/// Parity checking
#[derive(Copy, Debug, PartialEq)]
pub enum Parity {
    Even,
    None,
    Odd,
}

#[allow(missing_docs)]
/// Number of stop bits
#[derive(Copy, Debug, PartialEq)]
pub enum StopBits {
    One,
    Two,
}
<commit_msg>chore: add some feature gates to "fix" some warnings<commit_after>#![allow(unused_features)]
#![cfg_attr(test, plugin(quickcheck_macros))]
#![deny(missing_docs, warnings)]
#![feature(collections)]
#![feature(core)]
#![feature(io)]
#![feature(libc)]
#![feature(old_io)]
#![feature(old_path)]
#![feature(path)]
#![feature(plugin)]
#![feature(std_misc)]

//! A library for serial port communication

extern crate termios;
#[cfg(test)]
extern crate quickcheck;

use std::old_io::{File, FileAccess, FileMode, IoResult};
use std::os::unix::AsRawFd;

pub use termios::BaudRate;

use termios::prelude::*;

#[cfg(test)]
mod socat;
#[cfg(test)]
mod test;

/// For how long to block `read()` calls
#[derive(Copy, PartialEq)]
pub struct BlockingMode {
    /// The device will block until *at least* `bytes` are received
    pub bytes: u8,
    /// The device will block for at least `deciseconds` after each `read()` call
    pub deciseconds: u8,
}

/// A serial device
pub struct SerialPort(File);

impl SerialPort {
    /// Opens a serial `device` in "raw" mode
    pub fn open(device: &Path, access: FileAccess) -> IoResult<SerialPort> {
        let file = try!(File::open_mode(device, FileMode::Open, access));

        let mut termios = try!(Termios::fetch(file.as_raw_fd()));
        termios.make_raw();

        let sp = SerialPort(file);

        try!(sp.update(termios));

        Ok(sp)
    }

    /// Returns the input and output baud rates
    pub fn baud_rate(&self) -> IoResult<(BaudRate, BaudRate)> {
        self.fetch().map(|termios| {
            (termios.ispeed(), termios.ospeed())
        })
    }

    /// Returns the blocking mode used by the device
    pub fn blocking_mode(&self) -> IoResult<BlockingMode> {
        self.fetch().map(|termios| {
            BlockingMode {
                bytes: termios.cc[control::Char::VMIN],
                deciseconds: termios.cc[control::Char::VTIME],
            }
        })
    }

    /// Returns the number of data bits used per character
    pub fn data_bits(&self) -> IoResult<DataBits> {
        self.fetch().map(|termios| {
            match termios.get::<control::CSIZE>() {
                control::CSIZE::CS5 => DataBits::Five,
                control::CSIZE::CS6 => DataBits::Six,
                control::CSIZE::CS7 => DataBits::Seven,
                control::CSIZE::CS8 => DataBits::Eight,
            }
        })
    }

    /// Returns the flow control used by the device
    pub fn flow_control(&self) -> IoResult<FlowControl> {
        self.fetch().map(|termios| {
            if termios.contains(control::Flag::CRTSCTS) {
                FlowControl::Hardware
            } else if termios.contains(input::Flag::IXANY) &&
                termios.contains(input::Flag::IXOFF) &&
                termios.contains(input::Flag::IXON)
            {
                FlowControl::Software
            } else {
                FlowControl::None
            }
        })
    }

    /// Returns the bit parity used by the device
    pub fn parity(&self) -> IoResult<Parity> {
        self.fetch().map(|termios| {
            match (
                termios.contains(control::Flag::PARENB),
                termios.contains(control::Flag::PARODD),
            ) {
                (true, true) => Parity::Odd,
                (true, false) => Parity::Even,
                (false, _) => Parity::None,
            }
        })
    }

    /// Changes the baud rate of the input/output or both directions
    pub fn set_baud_rate(&mut self, direction: Direction, rate: BaudRate) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match direction {
                Direction::Both => termios.set_speed(rate),
                Direction::Input => termios.set_ispeed(rate),
                Direction::Output => termios.set_ospeed(rate),
            }

            self.update(termios)
        })
    }

    /// Changes the blocking mode used by the device
    pub fn set_blocking_mode(&mut self, mode: BlockingMode) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            termios.cc[control::Char::VMIN] = mode.bytes;
            termios.cc[control::Char::VTIME] = mode.deciseconds;

            self.update(termios)
        })
    }

    /// Changes the number of data bits per character
    pub fn set_data_bits(&mut self, bits: DataBits) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            termios.set(match bits {
                DataBits::Five => control::CSIZE::CS5,
                DataBits::Six => control::CSIZE::CS6,
                DataBits::Seven => control::CSIZE::CS7,
                DataBits::Eight => control::CSIZE::CS8,
            });

            self.update(termios)
        })
    }

    /// Changes the flow control used by the device
    pub fn set_flow_control(&mut self, flow: FlowControl) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match flow {
                FlowControl::Hardware => {
                    termios.clear(input::Flag::IXANY);
                    termios.clear(input::Flag::IXOFF);
                    termios.clear(input::Flag::IXON);
                    termios.set(control::Flag::CRTSCTS);
                },
                FlowControl::None => {
                    termios.clear(control::Flag::CRTSCTS);
                    termios.clear(input::Flag::IXANY);
                    termios.clear(input::Flag::IXOFF);
                    termios.clear(input::Flag::IXON);
                },
                FlowControl::Software => {
                    termios.clear(control::Flag::CRTSCTS);
                    termios.set(input::Flag::IXANY);
                    termios.set(input::Flag::IXOFF);
                    termios.set(input::Flag::IXON);
                },
            }

            self.update(termios)
        })
    }

    /// Changes the bit parity used by the device
    pub fn set_parity(&mut self, parity: Parity) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match parity {
                Parity::Even => {
                    termios.clear(control::Flag::PARODD);
                    termios.set(control::Flag::PARENB);
                },
                Parity::None => termios.clear(control::Flag::PARENB),
                Parity::Odd => {
                    termios.set(control::Flag::PARENB);
                    termios.set(control::Flag::PARODD);
                },
            }

            self.update(termios)
        })
    }

    /// Changes the number of stop bits per character
    pub fn set_stop_bits(&mut self, bits: StopBits) -> IoResult<()> {
        self.fetch().and_then(|mut termios| {
            match bits {
                StopBits::One => termios.clear(control::Flag::CSTOPB),
                StopBits::Two => termios.set(control::Flag::CSTOPB),
            }

            self.update(termios)
        })
    }

    /// Returns the number of stop bits per character
    pub fn stop_bits(&self) -> IoResult<StopBits> {
        self.fetch().map(|termios| {
            if termios.contains(control::Flag::CSTOPB) {
                StopBits::Two
            } else {
                StopBits::One
            }
        })
    }

    /// Fetches the current state of the termios structure
    fn fetch(&self) -> IoResult<Termios> {
        Termios::fetch(self.0.as_raw_fd())
    }

    /// Updates the underlying termios structure
    fn update(&self, termios: Termios) -> IoResult<()> {
        termios.update(self.0.as_raw_fd(), When::Now)
    }
}

impl Reader for SerialPort {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        self.0.read(buf)
    }
}

impl Writer for SerialPort {
    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
        self.0.write_all(buf)
    }
}

#[allow(missing_docs)]
/// Number of data bits
#[derive(Copy, Debug, PartialEq)]
pub enum DataBits {
    Five,
    Six,
    Seven,
    Eight,
}

#[allow(missing_docs)]
#[derive(Copy)]
pub enum Direction {
    Both,
    Input,
    Output,
}

#[allow(missing_docs)]
/// Flow control
#[derive(Copy, Debug, PartialEq)]
pub enum FlowControl {
    Hardware,
    None,
    Software,
}

#[allow(missing_docs)]
/// Parity checking
#[derive(Copy, Debug, PartialEq)]
pub enum Parity {
    Even,
    None,
    Odd,
}

#[allow(missing_docs)]
/// Number of stop bits
#[derive(Copy, Debug, PartialEq)]
pub enum StopBits {
    One,
    Two,
}
<|endoftext|>


<commit_before>//! # num_cpus
//!
//! A crate with utilities to determine the number of CPUs available on the
//! current system.
//!
//! ## Example
//!
//! ```
//! let cpus = num_cpus::get();
//! ```
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![doc(html_root_url = "https://docs.rs/num_cpus/1.7.0")]
#![allow(non_snake_case)]

#[cfg(not(windows))]
extern crate libc;

/// Returns the number of available CPUs of the current system.
///
/// # Note
///
/// This will check sched affinity on Linux.
#[inline]
pub fn get() -> usize {
    get_num_cpus()
}

/// Returns the number of physical cores of the current system.
///
/// If not possible on the particular architecture, returns same as `get()`
/// which is the logical CPUs.
#[inline]
pub fn get_physical() -> usize {
    get_num_physical_cpus()
}

#[cfg(not(any(target_os = "linux", target_os = "windows", target_os="macos")))]
#[inline]
fn get_num_physical_cpus() -> usize {
    // Not implemented, fallback
    get_num_cpus()
}

#[cfg(target_os = "windows")]
fn get_num_physical_cpus() -> usize {
    match get_num_physical_cpus_windows() {
        Some(num) => num,
        None => get_num_cpus()
    }
}

#[cfg(target_os = "windows")]
fn get_num_physical_cpus_windows() -> Option<usize> {
    // Inspired by https://msdn.microsoft.com/en-us/library/ms683194

    use std::ptr;
    use std::mem;

    #[allow(non_upper_case_globals)]
    const RelationProcessorCore: u32 = 0;

    #[repr(C)]
    #[allow(non_camel_case_types)]
    struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
        mask: usize,
        relationship: u32,
        _unused: [u64; 2]
    }

    extern "system" {
        fn GetLogicalProcessorInformation(
            info: *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION,
            length: &mut u32
        ) -> u32;
    }

    // First we need to determine how much space to reserve.

    // The required size of the buffer, in bytes.
    let mut needed_size = 0;

    unsafe {
        GetLogicalProcessorInformation(ptr::null_mut(), &mut needed_size);
    }

    let struct_size = mem::size_of::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>() as u32;

    // Could be 0, or some other bogus size.
    if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 {
        return None;
    }

    let count = needed_size / struct_size;

    // Allocate some memory where we will store the processor info.
    let mut buf = Vec::with_capacity(count as usize);

    let result;

    unsafe {
        result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size);
    }

    // Failed for any reason.
    if result == 0 {
        return None;
    }

    let count = needed_size / struct_size;

    unsafe {
        buf.set_len(count as usize);
    }

    let phys_proc_count = buf.iter()
        // Only interested in processor packages (physical processors.)
        .filter(|proc_info| proc_info.relationship == RelationProcessorCore)
        .count();

    if phys_proc_count == 0 {
        None
    } else {
        Some(phys_proc_count)
    }
}

#[cfg(target_os = "linux")]
fn get_num_physical_cpus() -> usize {
    use std::io::BufReader;
    use std::io::BufRead;
    use std::fs::File;
    use std::collections::HashSet;

    let file = match File::open("/proc/cpuinfo") {
        Ok(val) => val,
        Err(_) => {return get_num_cpus()},
    };
    let reader = BufReader::new(file);
    let mut set = HashSet::new();
    let mut coreid: u32 = 0;
    let mut physid: u32 = 0;
    let mut chgcount = 0;
    for line in reader.lines().filter_map(|result| result.ok()) {
        let parts: Vec<&str> = line.split(':').map(|s| s.trim()).collect();
        if parts.len() != 2 {
            continue
        }
        if parts[0] == "core id" || parts[0] == "physical id" {
            let value = match parts[1].trim().parse() {
              Ok(val) => val,
              Err(_) => break,
            };
            match parts[0] {
                "core id"     => coreid = value,
                "physical id" => physid = value,
                _ => {},
            }
            chgcount += 1;
        }
        if chgcount == 2 {
            set.insert((physid, coreid));
            chgcount = 0;
        }
    }
    let count = set.len();
    if count == 0 { get_num_cpus() } else { count }
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
    #[repr(C)]
    struct SYSTEM_INFO {
        wProcessorArchitecture: u16,
        wReserved: u16,
        dwPageSize: u32,
        lpMinimumApplicationAddress: *mut u8,
        lpMaximumApplicationAddress: *mut u8,
        dwActiveProcessorMask: *mut u8,
        dwNumberOfProcessors: u32,
        dwProcessorType: u32,
        dwAllocationGranularity: u32,
        wProcessorLevel: u16,
        wProcessorRevision: u16,
    }

    extern "system" {
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
    }

    unsafe {
        let mut sysinfo: SYSTEM_INFO = std::mem::uninitialized();
        GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    }
}

#[cfg(any(target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "bitrig",
          target_os = "netbsd"))]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    unsafe {
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
    }
    if cpus < 1 {
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
        unsafe {
            libc::sysctl(mib.as_mut_ptr(),
                         2,
                         &mut cpus as *mut _ as *mut _,
                         &mut cpus_size as *mut _ as *mut _,
                         0 as *mut _,
                         0);
        }
        if cpus < 1 {
            cpus = 1;
        }
    }
    cpus as usize
}

#[cfg(target_os = "openbsd")]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

    unsafe {
        libc::sysctl(mib.as_mut_ptr(),
                     2,
                     &mut cpus as *mut _ as *mut _,
                     &mut cpus_size as *mut _ as *mut _,
                     0 as *mut _,
                     0);
    }
    if cpus < 1 {
        cpus = 1;
    }
    cpus as usize
}

#[cfg(target_os = "macos")]
fn get_num_physical_cpus() -> usize {
    use std::ffi::CStr;
    use std::ptr;

    let mut cpus: i32 = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    let sysctl_name = CStr::from_bytes_with_nul(b"hw.physicalcpu\0")
        .expect("byte literal is missing NUL");

    unsafe {
        if 0 != libc::sysctlbyname(sysctl_name.as_ptr(),
                                   &mut cpus as *mut _ as *mut _,
                                   &mut cpus_size as *mut _ as *mut _,
                                   ptr::null_mut(),
                                   0) {
            return get_num_cpus();
        }
    }
    cpus as usize
}

#[cfg(target_os = "linux")]
fn get_num_cpus() -> usize {
    let mut set:  libc::cpu_set_t = unsafe { std::mem::zeroed() };
    if unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
        let mut count: u32 = 0;
        for i in 0..libc::CPU_SETSIZE as usize {
            if unsafe { libc::CPU_ISSET(i, &set) } {
                count += 1
            }
        }
        count as usize
    } else {
        let cpus = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) };
        if cpus < 1 {
            1
        } else {
            cpus as usize
        }
    }
}

#[cfg(any(
    target_os = "nacl",
    target_os = "macos",
    target_os = "ios",
    target_os = "android",
    target_os = "solaris",
    target_os = "fuchsia")
)]
fn get_num_cpus() -> usize {
    // On ARM targets, processors could be turned off to save power.
    // Use `_SC_NPROCESSORS_CONF` to get the real number.
    #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
    const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_CONF;
    #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
    const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_ONLN;

    let cpus = unsafe { libc::sysconf(CONF_NAME) };
    if cpus < 1 {
        1
    } else {
        cpus as usize
    }
}

#[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))]
fn get_num_cpus() -> usize {
    1
}

#[cfg(test)]
mod tests {
    fn env_var(name: &'static str) -> Option<usize> {
        ::std::env::var(name).ok().map(|val| val.parse().unwrap())
    }

    #[test]
    fn test_get() {
        let num = super::get();
        if let Some(n) = env_var("NUM_CPUS_TEST_GET") {
            assert_eq!(num, n);
        } else {
            assert!(num > 0);
            assert!(num < 236_451);
        }
    }

    #[test]
    fn test_get_physical() {
        let num = super::get_physical();
        if let Some(n) = env_var("NUM_CPUS_TEST_GET_PHYSICAL") {
            assert_eq!(num, n);
        } else {
            assert!(num > 0);
            assert!(num < 236_451);
        }
    }
}
<commit_msg>Add more documentation (#61)<commit_after>//! A crate with utilities to determine the number of CPUs available on the
//! current system.
//!
//! Sometimes the CPU will exaggerate the number of CPUs it contains, because it can use
//! [processor tricks] to deliver increased performance when there are more threads. This
//! crate provides methods to get both the logical and physical numbers of cores.
//!
//! This information can be used as a guide to how many tasks can be run in parallel.
//! There are many properties of the system architecture that will affect parallelism,
//! for example memory access speeds (for all the caches and RAM) and the physical
//! architecture of the processor, so the number of CPUs should be used as a rough guide
//! only.
//!
//!
//! ## Examples
//!
//! Fetch the number of logical CPUs.
//!
//! ```
//! let cpus = num_cpus::get();
//! ```
//!
//! See [`rayon::Threadpool`] for an example of where the number of CPUs could be
//! used when setting up parallel jobs (Where the threadpool example uses a fixed
//! number 8, it could use the number of CPUs).
//!
//! [processor tricks]: https://en.wikipedia.org/wiki/Simultaneous_multithreading
//! [`rayon::ThreadPool`]: https://docs.rs/rayon/0.8.2/rayon/struct.ThreadPool.html
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![doc(html_root_url = "https://docs.rs/num_cpus/1.7.0")]
#![allow(non_snake_case)]

#[cfg(not(windows))]
extern crate libc;

/// Returns the number of available CPUs of the current system.
///
/// This function will get the number of logical cores. Sometimes this is different from the number
/// of physical cores (See [Simultaneous multithreading on Wikipedia][smt]).
///
/// # Examples
///
/// ```
/// let cpus = num_cpus::get();
/// if cpus > 1 {
///     println!("We are on a multicore system with {} CPUs", cpus);
/// } else {
///     println!("We are on a single core system");
/// }
/// ```
///
/// # Note
///
/// This will check [sched affinity] on Linux, showing a lower number of CPUs if the current
/// thread does not have access to all the computer's CPUs.
///
/// [smt]: https://en.wikipedia.org/wiki/Simultaneous_multithreading
/// [sched affinity]: http://www.gnu.org/software/libc/manual/html_node/CPU-Affinity.html
#[inline]
pub fn get() -> usize {
    get_num_cpus()
}

/// Returns the number of physical cores of the current system.
///
/// If not possible on the particular architecture, returns same as `get()`
/// which is the logical CPUs.
///
/// # Examples
///
/// ```
/// let logical_cpus = num_cpus::get();
/// let physical_cpus = num_cpus::get_physical();
/// if logical_cpus > physical_cpus {
///     println!("We have simultaneous multithreading with about {:.2} \
///               logical cores to 1 physical core.",
///               (logical_cpus as f64) / (physical_cpus as f64));
/// } else if logical_cpus == physical_cpus {
///     println!("Either we don't have simultaneous multithreading, or our \
///               system doesn't support getting the number of physical CPUs.");
/// } else {
///     println!("We have less logical CPUs than physical CPUs, maybe we only have access to \
///               some of the CPUs on our system.");
/// }
/// ```
///
#[inline]
pub fn get_physical() -> usize {
    get_num_physical_cpus()
}

#[cfg(not(any(target_os = "linux", target_os = "windows", target_os="macos")))]
#[inline]
fn get_num_physical_cpus() -> usize {
    // Not implemented, fallback
    get_num_cpus()
}

#[cfg(target_os = "windows")]
fn get_num_physical_cpus() -> usize {
    match get_num_physical_cpus_windows() {
        Some(num) => num,
        None => get_num_cpus()
    }
}

#[cfg(target_os = "windows")]
fn get_num_physical_cpus_windows() -> Option<usize> {
    // Inspired by https://msdn.microsoft.com/en-us/library/ms683194

    use std::ptr;
    use std::mem;

    #[allow(non_upper_case_globals)]
    const RelationProcessorCore: u32 = 0;

    #[repr(C)]
    #[allow(non_camel_case_types)]
    struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
        mask: usize,
        relationship: u32,
        _unused: [u64; 2]
    }

    extern "system" {
        fn GetLogicalProcessorInformation(
            info: *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION,
            length: &mut u32
        ) -> u32;
    }

    // First we need to determine how much space to reserve.

    // The required size of the buffer, in bytes.
    let mut needed_size = 0;

    unsafe {
        GetLogicalProcessorInformation(ptr::null_mut(), &mut needed_size);
    }

    let struct_size = mem::size_of::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>() as u32;

    // Could be 0, or some other bogus size.
    if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 {
        return None;
    }

    let count = needed_size / struct_size;

    // Allocate some memory where we will store the processor info.
    let mut buf = Vec::with_capacity(count as usize);

    let result;

    unsafe {
        result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size);
    }

    // Failed for any reason.
    if result == 0 {
        return None;
    }

    let count = needed_size / struct_size;

    unsafe {
        buf.set_len(count as usize);
    }

    let phys_proc_count = buf.iter()
        // Only interested in processor packages (physical processors.)
        .filter(|proc_info| proc_info.relationship == RelationProcessorCore)
        .count();

    if phys_proc_count == 0 {
        None
    } else {
        Some(phys_proc_count)
    }
}

#[cfg(target_os = "linux")]
fn get_num_physical_cpus() -> usize {
    use std::io::BufReader;
    use std::io::BufRead;
    use std::fs::File;
    use std::collections::HashSet;

    let file = match File::open("/proc/cpuinfo") {
        Ok(val) => val,
        Err(_) => {return get_num_cpus()},
    };
    let reader = BufReader::new(file);
    let mut set = HashSet::new();
    let mut coreid: u32 = 0;
    let mut physid: u32 = 0;
    let mut chgcount = 0;
    for line in reader.lines().filter_map(|result| result.ok()) {
        let parts: Vec<&str> = line.split(':').map(|s| s.trim()).collect();
        if parts.len() != 2 {
            continue
        }
        if parts[0] == "core id" || parts[0] == "physical id" {
            let value = match parts[1].trim().parse() {
              Ok(val) => val,
              Err(_) => break,
            };
            match parts[0] {
                "core id"     => coreid = value,
                "physical id" => physid = value,
                _ => {},
            }
            chgcount += 1;
        }
        if chgcount == 2 {
            set.insert((physid, coreid));
            chgcount = 0;
        }
    }
    let count = set.len();
    if count == 0 { get_num_cpus() } else { count }
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
    #[repr(C)]
    struct SYSTEM_INFO {
        wProcessorArchitecture: u16,
        wReserved: u16,
        dwPageSize: u32,
        lpMinimumApplicationAddress: *mut u8,
        lpMaximumApplicationAddress: *mut u8,
        dwActiveProcessorMask: *mut u8,
        dwNumberOfProcessors: u32,
        dwProcessorType: u32,
        dwAllocationGranularity: u32,
        wProcessorLevel: u16,
        wProcessorRevision: u16,
    }

    extern "system" {
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
    }

    unsafe {
        let mut sysinfo: SYSTEM_INFO = std::mem::uninitialized();
        GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    }
}

#[cfg(any(target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "bitrig",
          target_os = "netbsd"))]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    unsafe {
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
    }
    if cpus < 1 {
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
        unsafe {
            libc::sysctl(mib.as_mut_ptr(),
                         2,
                         &mut cpus as *mut _ as *mut _,
                         &mut cpus_size as *mut _ as *mut _,
                         0 as *mut _,
                         0);
        }
        if cpus < 1 {
            cpus = 1;
        }
    }
    cpus as usize
}

#[cfg(target_os = "openbsd")]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

    unsafe {
        libc::sysctl(mib.as_mut_ptr(),
                     2,
                     &mut cpus as *mut _ as *mut _,
                     &mut cpus_size as *mut _ as *mut _,
                     0 as *mut _,
                     0);
    }
    if cpus < 1 {
        cpus = 1;
    }
    cpus as usize
}

#[cfg(target_os = "macos")]
fn get_num_physical_cpus() -> usize {
    use std::ffi::CStr;
    use std::ptr;

    let mut cpus: i32 = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    let sysctl_name = CStr::from_bytes_with_nul(b"hw.physicalcpu\0")
        .expect("byte literal is missing NUL");

    unsafe {
        if 0 != libc::sysctlbyname(sysctl_name.as_ptr(),
                                   &mut cpus as *mut _ as *mut _,
                                   &mut cpus_size as *mut _ as *mut _,
                                   ptr::null_mut(),
                                   0) {
            return get_num_cpus();
        }
    }
    cpus as usize
}

#[cfg(target_os = "linux")]
fn get_num_cpus() -> usize {
    let mut set:  libc::cpu_set_t = unsafe { std::mem::zeroed() };
    if unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
        let mut count: u32 = 0;
        for i in 0..libc::CPU_SETSIZE as usize {
            if unsafe { libc::CPU_ISSET(i, &set) } {
                count += 1
            }
        }
        count as usize
    } else {
        let cpus = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) };
        if cpus < 1 {
            1
        } else {
            cpus as usize
        }
    }
}

#[cfg(any(
    target_os = "nacl",
    target_os = "macos",
    target_os = "ios",
    target_os = "android",
    target_os = "solaris",
    target_os = "fuchsia")
)]
fn get_num_cpus() -> usize {
    // On ARM targets, processors could be turned off to save power.
    // Use `_SC_NPROCESSORS_CONF` to get the real number.
    #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
    const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_CONF;
    #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
    const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_ONLN;

    let cpus = unsafe { libc::sysconf(CONF_NAME) };
    if cpus < 1 {
        1
    } else {
        cpus as usize
    }
}

#[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))]
fn get_num_cpus() -> usize {
    1
}

#[cfg(test)]
mod tests {
    fn env_var(name: &'static str) -> Option<usize> {
        ::std::env::var(name).ok().map(|val| val.parse().unwrap())
    }

    #[test]
    fn test_get() {
        let num = super::get();
        if let Some(n) = env_var("NUM_CPUS_TEST_GET") {
            assert_eq!(num, n);
        } else {
            assert!(num > 0);
            assert!(num < 236_451);
        }
    }

    #[test]
    fn test_get_physical() {
        let num = super::get_physical();
        if let Some(n) = env_var("NUM_CPUS_TEST_GET_PHYSICAL") {
            assert_eq!(num, n);
        } else {
            assert!(num > 0);
            assert!(num < 236_451);
        }
    }
}
<|endoftext|>


<commit_before>//! CHIP-8 emulator backend.
//! You can develop your own frontends for it.
//!
//! The reference frontend is
//! [crusty-chip-sfml](https://github.com/crumblingstatue/crusty-chip-sfml).
//!
//! The CHIP-8 technical documentation in the comments is copied from
//! http://devernay.free.fr/hacks/chip8/C8TECH10.HTM,
//! Copyright (c) Thomas P. Greene.

#![warn(missing_docs)]

use std::num::Wrapping;
use std::ops::{Deref, DerefMut};

mod ops;

fn copy_memory(src: &[u8], dst: &mut [u8]) {
        let len_src = src.len();
        assert!(dst.len() >= len_src);
        // `dst` is unaliasable, so we know statically it doesn't overlap
        // with `src`.
        unsafe {
            std::ptr::copy_nonoverlapping(src.as_ptr(),
                                          dst.as_mut_ptr(),
                                          len_src);
        }
    }

const START_ADDR: u16 = 0x200;
const MEM_SIZE: usize = 4096;
/// The width of the Chip8's display in pixels.
pub const DISPLAY_WIDTH: usize = 64;
/// The height of the Chip8's display in pixels.
pub const DISPLAY_HEIGHT: usize = 32;

static FONTSET: [u8; 5 * 0x10] = [
    0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
    0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
    0x90, 0x90, 0xF0, 0x10, 0x10, // 4
    0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
    0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
    0xF0, 0x10, 0x20, 0x40, 0x40, // 7
    0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
    0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
    0xF0, 0x90, 0xF0, 0x90, 0x90, // A
    0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
    0xF0, 0x80, 0x80, 0x80, 0xF0, // C
    0xE0, 0x90, 0x90, 0x90, 0xE0, // D
    0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
    0xF0, 0x80, 0xF0, 0x80, 0x80, // F
];

#[derive(Clone, Copy)]
struct KeypressWait {
    wait: bool,
    vx: usize
}

macro_rules! array_wrap (
    ($name:ident, $typ:ty) => (
        #[derive(Copy)]
        struct $name($typ);
        impl Clone for $name {
            fn clone(&self) -> $name { *self }
        }
        impl Deref for $name {
            type Target = $typ;
            fn deref(&self) -> &$typ { &self.0 }
        }
        impl DerefMut for $name {
            fn deref_mut(&mut self) -> &mut $typ { &mut self.0 }
        }
    )
);

array_wrap!(DisplayArray, [u8; DISPLAY_WIDTH * DISPLAY_HEIGHT]);
array_wrap!(MemArray, [u8; MEM_SIZE]);

/// A CHIP-8 virtual machine.
#[derive(Clone, Copy)]
pub struct VirtualMachine {
    ram: MemArray,
    v: [Wrapping<u8>; 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16; 16],
    display: DisplayArray,
    display_updated: bool,
    keys: [bool; 16],
    keypress_wait: KeypressWait
}

impl VirtualMachine {

    /// Constructs a new VirtualMachine.
    pub fn new() -> VirtualMachine {
        let mut ch8 = VirtualMachine {
            ram: MemArray([0; MEM_SIZE]),
            v: [Wrapping(0); 16],
            i: 0,
            delay_timer: 0,
            sound_timer: 0,
            pc: START_ADDR,
            sp: 0,
            stack: [0; 16],
            display: DisplayArray([0; DISPLAY_WIDTH * DISPLAY_HEIGHT]),
            display_updated: false,
            keys: [false; 16],
            keypress_wait: KeypressWait {
                wait: false,
                vx: 0
            }
        };
        copy_memory(&FONTSET, &mut ch8.ram[0usize..5 * 0x10]);
        ch8
    }

    /// Loads a ROM into the VirtualMachine.
    ///
    /// ## Arguments ##
    /// * rom - ROM to load
    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(rom, &mut self.ram[START_ADDR as usize .. len]);
    }

    /// Does an emulation cycle.
    pub fn do_cycle(&mut self) {
        self.display_updated = false;
        if self.keypress_wait.wait {
            return;
        }

        let ins = self.fetch_ins();
        self.dispatch(ins);
    }

    // Decode instruction and execute it
    fn dispatch(&mut self, ins: u16) {
        // A 12-bit value, the lowest 12 bits of the instruction
        let nnn = ins & 0x0FFF;
        // A 4-bit value, the lowest 4 bits of the instruction
        let n = ins & 0x000F;
        // A 4-bit value, the lower 4 bits of the high byte of the instruction
        let x = (ins & 0x0F00) >> 8;
        // A 4-bit value, the upper 4 bits of the low byte of the instruction
        let y = (ins & 0x00F0) >> 4;
        // kk or byte - An 8-bit value, the lowest 8 bits of the instruction
        let kk = (ins & 0x00FF) as u8;

        match (ins & 0xF000) >> 12 {
            0x0 => match nnn {
                0x0E0 => ops::clear_display(self, ),
                0x0EE => ops::ret_from_subroutine(self, ),
                _ => ops::jump_to_sys_routine(self, 0)
            },
            0x1 => ops::jump_addr(self, nnn),
            0x2 => ops::call_subroutine(self, nnn as usize),
            0x3 => ops::skip_next_vx_eq(self, x as usize, kk),
            0x4 => ops::skip_next_vx_ne(self, x as usize, kk),
            0x5 => match n {
                0x0 => ops::skip_next_vx_eq_vy(self, x as usize, y as usize),
                _ => panic!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6 => ops::set_vx_byte(self, x as usize, kk),
            0x7 => ops::add_vx_byte(self, x as usize, kk),
            0x8 => {
                let (x, y) = (x as usize, y as usize);
                match n {
                    0x0 => ops::set_vx_to_vy(self, x, y),
                    0x1 => ops::set_vx_to_vx_or_vy(self, x, y),
                    0x2 => ops::set_vx_to_vx_and_vy(self, x, y),
                    0x3 => ops::set_vx_to_vx_xor_vy(self, x, y),
                    0x4 => ops::add_vx_vy(self, x, y),
                    0x5 => ops::sub_vx_vy(self, x, y),
                    0x6 => ops::set_vx_to_vx_shr_1(self, x),
                    0xE => ops::set_vx_to_vx_shl_1(self, x),
                    _ => panic!("Unknown 0x8XXX instruction: {:x}", ins)
                }
            },
            0x9 => match n {
                0x0 => ops::skip_next_vx_ne_vy(self, x as usize, y as usize),
                _ => panic!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA => ops::set_i(self, nnn),
            0xC => ops::set_vx_rand_and(self, x as usize, kk),
            0xD => ops::display_sprite(self, x as usize, y as usize, n as usize),
            0xE => match kk {
                0xA1 => ops::skip_next_key_vx_not_pressed(self, x as usize),
                0x9E => ops::skip_next_key_vx_pressed(self, x as usize),
                _ => panic!("Unknown 0xEXXX instruction: {:x}", ins)
            },
            0xF => {
                match kk {
                    0x07 => ops::set_vx_to_delay_timer(self, x as usize),
                    0x0A => ops::wait_for_keypress_store_in_vx(self, x as usize),
                    0x15 => ops::set_delay_timer(self, x as usize),
                    0x18 => ops::set_sound_timer(self, x as usize),
                    0x1E => ops::add_vx_to_i(self, x as usize),
                    0x29 => ops::set_i_to_loc_of_digit_vx(self, x as usize),
                    0x33 => ops::store_bcd_of_vx_to_i(self, x as usize),
                    0x55 => ops::copy_v0_through_vx_to_mem(self, x as usize),
                    0x65 => ops::read_v0_through_vx_from_mem(self, x as usize),
                    _ => panic!("Unknown 0xFXXX instruction: {:x}", ins)
                }
            },
            _ => panic!("Unknown instruction: {:04x}", ins)
        }
    }

    /// Gets the instruction that the program counter is pointing to.
    pub fn get_ins(&self) -> u16 {
        let b1 = self.ram[self.pc as usize];
        let b2 = self.ram[(self.pc + 1) as usize];
        (b1 as u16) << 8 | b2 as u16
    }

    /// Returns the value of the program counter.
    pub fn pc(&self) -> u16 {
        self.pc
    }

    fn fetch_ins(&mut self) -> u16 {
        let ins = self.get_ins();
        self.pc += 2;
        ins
    }

    /// Presses a key on the hexadecimal keypad.
    ///
    /// `key` should be in the range `0..15`.
    pub fn press_key(&mut self, key: usize) {
        assert!(key <= 15);
        self.keys[key] = true;
        if self.keypress_wait.wait {
            self.v[self.keypress_wait.vx].0 = key as u8;
            self.keypress_wait.wait = false;
        }
    }

    /// Releases a key on the hexadecimal keypad.
    ///
    /// `key` should be in the range `0..15`.
    pub fn release_key(&mut self, key: usize) {
        assert!(key <= 15);
        self.keys[key] = false;
    }

    /// Decrements the sound and delay timers.
    ///
    /// They should be decremented at a rate of 60 Hz.
    pub fn decrement_timers(&mut self) {
        if self.delay_timer > 0 {
            self.delay_timer -= 1;
        }
        if self.sound_timer > 0 {
            self.sound_timer -= 1;
        }
    }

    /// Returns whether the display has been updated.
    pub fn display_updated(&self) -> bool { self.display_updated }
    /// Returns the contents of the display.
    pub fn display(&self) -> &[u8; DISPLAY_WIDTH * DISPLAY_HEIGHT] { &*self.display }
}
<commit_msg>add an Instruction enum type to aid debugging<commit_after>//! CHIP-8 emulator backend.
//! You can develop your own frontends for it.
//!
//! The reference frontend is
//! [crusty-chip-sfml](https://github.com/crumblingstatue/crusty-chip-sfml).
//!
//! The CHIP-8 technical documentation in the comments is copied from
//! http://devernay.free.fr/hacks/chip8/C8TECH10.HTM,
//! Copyright (c) Thomas P. Greene.

#![warn(missing_docs)]

use std::num::Wrapping;
use std::ops::{Deref, DerefMut};

mod ops;

/// 4 bit value extracted from an instruction.
pub type Nibble = u8;
/// 8 bit value extracted from an instruction.
pub type Byte = u8;
/// 12 bit value extracted from an instruction.
pub type Semiword = u16;

#[allow(missing_docs)]
/// A CHIP-8 instruction.
pub enum Instruction {
    ClearDisplay,
    Return,
    JumpToSysRoutine{addr: Semiword},
    JumpToAddress{addr: Semiword},
    CallSubroutine{addr: Semiword},
    SkipNextVxEq{x: Nibble, cmp_with: Byte},
    SkipNextVxNe{x: Nibble, cmp_with: Byte},
    SkipNextVxEqVy{x: Nibble, y: Nibble},
    SetVxByte{x: Nibble, to: Byte},
    AddVxByte{x: Nibble, rhs: Byte},
    SetVxToVy{x: Nibble, y: Nibble},
    SetVxToVxOrVy{x: Nibble, y: Nibble},
    SetVxToVxAndVy{x: Nibble, y: Nibble},
    SetVxToVxXorVy{x: Nibble, y: Nibble},
    AddVxVy{x: Nibble, y: Nibble},
    SubVxVy{x: Nibble, y: Nibble},
    SetVxToVxShr1{x: Nibble},
    SetVxToVxShl1{x: Nibble},
    SkipNextVxNeVy{x: Nibble, y: Nibble},
    SetI{to: Semiword},
    SetVxRandAnd{x: Nibble, and: Byte},
    DisplaySprite{x: Nibble, y: Nibble, n: Nibble},
    SkipNextKeyVxNotPressed{x: Nibble},
    SkipNextKeyVxPressed{x: Nibble},
    SetVxToDelayTimer{x: Nibble},
    WaitForKeypressStoreInVx{x: Nibble},
    SetDelayTimer{x: Nibble},
    SetSoundTimer{x: Nibble},
    AddVxToI{x: Nibble},
    SetIToLocOfDigitVx{x: Nibble},
    StoreBcdOfVxToI{x: Nibble},
    CopyV0ThroughVxToMem{x: Nibble},
    ReadV0ThroughVxFromMem{x: Nibble},
    Unknown,
}

/// Decode a raw instruction into an Instruction structure.
pub fn decode(ins: u16) -> Instruction {
    use self::Instruction::*;

    // A 12-bit value, the lowest 12 bits of the instruction
    let nnn: Semiword = ins & 0x0FFF;
    // A 4-bit value, the lowest 4 bits of the instruction
    let n: Nibble = (ins & 0x000F) as Nibble;
    // A 4-bit value, the lower 4 bits of the high byte of the instruction
    let x: Nibble = ((ins & 0x0F00) >> 8) as Nibble;
    // A 4-bit value, the upper 4 bits of the low byte of the instruction
    let y: Nibble = ((ins & 0x00F0) >> 4) as Nibble;
    // kk or byte - An 8-bit value, the lowest 8 bits of the instruction
    let kk: Byte = (ins & 0x00FF) as Nibble;

    match (ins & 0xF000) >> 12 {
        0x0 => match nnn {
            0x0E0 => ClearDisplay,
            0x0EE => Return,
            _ => JumpToSysRoutine{addr: nnn},
        },
        0x1 => JumpToAddress{addr: nnn},
        0x2 => CallSubroutine{addr: nnn},
        0x3 => SkipNextVxEq{x: x, cmp_with: kk},
        0x4 => SkipNextVxNe{x: x, cmp_with: kk},
        0x5 => match n {
            0x0 => SkipNextVxEqVy{x: x, y: y},
            _ => Unknown,
        },
        0x6 => SetVxByte{x: x, to: kk},
        0x7 => AddVxByte{x: x, rhs: kk},
        0x8 => match n {
            0x0 => SetVxToVy{x: x, y: y},
            0x1 => SetVxToVxOrVy{x: x, y: y},
            0x2 => SetVxToVxAndVy{x: x, y: y},
            0x3 => SetVxToVxXorVy{x: x, y: y},
            0x4 => AddVxVy{x: x, y: y},
            0x5 => SubVxVy{x: x, y: y},
            0x6 => SetVxToVxShr1{x: x},
            0xE => SetVxToVxShl1{x: x},
            _ => Unknown,
        },
        0x9 => match n {
            0x0 => SkipNextVxNeVy{x: x, y: y},
            _ => Unknown,
        },
        0xA => SetI{to: nnn},
        0xC => SetVxRandAnd{x: x, and: kk},
        0xD => DisplaySprite{x: x, y: y, n: n},
        0xE => match kk {
            0xA1 => SkipNextKeyVxNotPressed{x: x},
            0x9E => SkipNextKeyVxPressed{x: x},
            _ => Unknown,
        },
        0xF => {
            match kk {
                0x07 => SetVxToDelayTimer{x: x},
                0x0A => WaitForKeypressStoreInVx{x: x},
                0x15 => SetDelayTimer{x: x},
                0x18 => SetSoundTimer{x: x},
                0x1E => AddVxToI{x: x},
                0x29 => SetIToLocOfDigitVx{x: x},
                0x33 => StoreBcdOfVxToI{x: x},
                0x55 => CopyV0ThroughVxToMem{x: x},
                0x65 => ReadV0ThroughVxFromMem{x: x},
                _ => Unknown
            }
        },
        _ => Unknown,
    }
}

fn copy_memory(src: &[u8], dst: &mut [u8]) {
        let len_src = src.len();
        assert!(dst.len() >= len_src);
        // `dst` is unaliasable, so we know statically it doesn't overlap
        // with `src`.
        unsafe {
            std::ptr::copy_nonoverlapping(src.as_ptr(),
                                          dst.as_mut_ptr(),
                                          len_src);
        }
    }

const START_ADDR: u16 = 0x200;
const MEM_SIZE: usize = 4096;
/// The width of the Chip8's display in pixels.
pub const DISPLAY_WIDTH: usize = 64;
/// The height of the Chip8's display in pixels.
pub const DISPLAY_HEIGHT: usize = 32;

static FONTSET: [u8; 5 * 0x10] = [
    0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
    0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
    0x90, 0x90, 0xF0, 0x10, 0x10, // 4
    0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
    0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
    0xF0, 0x10, 0x20, 0x40, 0x40, // 7
    0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
    0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
    0xF0, 0x90, 0xF0, 0x90, 0x90, // A
    0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
    0xF0, 0x80, 0x80, 0x80, 0xF0, // C
    0xE0, 0x90, 0x90, 0x90, 0xE0, // D
    0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
    0xF0, 0x80, 0xF0, 0x80, 0x80, // F
];

#[derive(Clone, Copy)]
struct KeypressWait {
    wait: bool,
    vx: usize
}

macro_rules! array_wrap (
    ($name:ident, $typ:ty) => (
        #[derive(Copy)]
        struct $name($typ);
        impl Clone for $name {
            fn clone(&self) -> $name { *self }
        }
        impl Deref for $name {
            type Target = $typ;
            fn deref(&self) -> &$typ { &self.0 }
        }
        impl DerefMut for $name {
            fn deref_mut(&mut self) -> &mut $typ { &mut self.0 }
        }
    )
);

array_wrap!(DisplayArray, [u8; DISPLAY_WIDTH * DISPLAY_HEIGHT]);
array_wrap!(MemArray, [u8; MEM_SIZE]);

/// A CHIP-8 virtual machine.
#[derive(Clone, Copy)]
pub struct VirtualMachine {
    ram: MemArray,
    v: [Wrapping<u8>; 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16; 16],
    display: DisplayArray,
    display_updated: bool,
    keys: [bool; 16],
    keypress_wait: KeypressWait
}

impl VirtualMachine {

    /// Constructs a new VirtualMachine.
    pub fn new() -> VirtualMachine {
        let mut ch8 = VirtualMachine {
            ram: MemArray([0; MEM_SIZE]),
            v: [Wrapping(0); 16],
            i: 0,
            delay_timer: 0,
            sound_timer: 0,
            pc: START_ADDR,
            sp: 0,
            stack: [0; 16],
            display: DisplayArray([0; DISPLAY_WIDTH * DISPLAY_HEIGHT]),
            display_updated: false,
            keys: [false; 16],
            keypress_wait: KeypressWait {
                wait: false,
                vx: 0
            }
        };
        copy_memory(&FONTSET, &mut ch8.ram[0usize..5 * 0x10]);
        ch8
    }

    /// Loads a ROM into the VirtualMachine.
    ///
    /// ## Arguments ##
    /// * rom - ROM to load
    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(rom, &mut self.ram[START_ADDR as usize .. len]);
    }

    /// Does an emulation cycle.
    pub fn do_cycle(&mut self) {
        self.display_updated = false;
        if self.keypress_wait.wait {
            return;
        }

        let ins = self.fetch_ins();
        self.dispatch(ins);
    }

    // Decode instruction and execute it
    fn dispatch(&mut self, ins: u16) {
        use Instruction::*;
        use ops::*;
        match decode(ins) {
            ClearDisplay => clear_display(self),
            Return => ret_from_subroutine(self),
            JumpToSysRoutine{addr} => jump_to_sys_routine(self, addr as usize),
            JumpToAddress{addr} => jump_addr(self, addr),
            CallSubroutine{addr} => call_subroutine(self, addr as usize),
            SkipNextVxEq{x, cmp_with} => skip_next_vx_eq(self, x as usize, cmp_with),
            SkipNextVxNe{x, cmp_with} => skip_next_vx_ne(self, x as usize, cmp_with),
            SkipNextVxEqVy{x, y} => skip_next_vx_eq_vy(self, x as usize, y as usize),
            SetVxByte{x, to} => set_vx_byte(self, x as usize, to),
            AddVxByte{x, rhs} => add_vx_byte(self, x as usize, rhs),
            SetVxToVy{x, y} => set_vx_to_vy(self, x as usize, y as usize),
            SetVxToVxOrVy{x, y} => set_vx_to_vx_or_vy(self, x as usize, y as usize),
            SetVxToVxAndVy{x, y} => set_vx_to_vx_and_vy(self, x as usize, y as usize),
            SetVxToVxXorVy{x, y} => set_vx_to_vx_xor_vy(self, x as usize, y as usize),
            AddVxVy{x, y} => add_vx_vy(self, x as usize, y as usize),
            SubVxVy{x, y} => sub_vx_vy(self, x as usize, y as usize),
            SetVxToVxShr1{x} => set_vx_to_vx_shr_1(self, x as usize),
            SetVxToVxShl1{x} => set_vx_to_vx_shl_1(self, x as usize),
            SkipNextVxNeVy{x, y} => skip_next_vx_ne_vy(self, x as usize, y as usize),
            SetI{to} => set_i(self, to),
            SetVxRandAnd{x, and} => set_vx_rand_and(self, x as usize, and),
            DisplaySprite{x, y, n} => display_sprite(self, x as usize, y as usize, n as usize),
            SkipNextKeyVxNotPressed{x} => skip_next_key_vx_not_pressed(self, x as usize),
            SkipNextKeyVxPressed{x} => skip_next_key_vx_pressed(self, x as usize),
            SetVxToDelayTimer{x} => set_vx_to_delay_timer(self, x as usize),
            WaitForKeypressStoreInVx{x} => wait_for_keypress_store_in_vx(self, x as usize),
            SetDelayTimer{x} => set_delay_timer(self, x as usize),
            SetSoundTimer{x} => set_sound_timer(self, x as usize),
            AddVxToI{x} => add_vx_to_i(self, x as usize),
            SetIToLocOfDigitVx{x} => set_i_to_loc_of_digit_vx(self, x as usize),
            StoreBcdOfVxToI{x} => store_bcd_of_vx_to_i(self, x as usize),
            CopyV0ThroughVxToMem{x} => copy_v0_through_vx_to_mem(self, x as usize),
            ReadV0ThroughVxFromMem{x} => read_v0_through_vx_from_mem(self, x as usize),
            Unknown => panic!("Unknown instruction: {}", ins),
        }
    }

    /// Gets the instruction that the program counter is pointing to.
    pub fn get_ins(&self) -> u16 {
        let b1 = self.ram[self.pc as usize];
        let b2 = self.ram[(self.pc + 1) as usize];
        (b1 as u16) << 8 | b2 as u16
    }

    /// Returns the value of the program counter.
    pub fn pc(&self) -> u16 {
        self.pc
    }

    fn fetch_ins(&mut self) -> u16 {
        let ins = self.get_ins();
        self.pc += 2;
        ins
    }

    /// Presses a key on the hexadecimal keypad.
    ///
    /// `key` should be in the range `0..15`.
    pub fn press_key(&mut self, key: usize) {
        assert!(key <= 15);
        self.keys[key] = true;
        if self.keypress_wait.wait {
            self.v[self.keypress_wait.vx].0 = key as u8;
            self.keypress_wait.wait = false;
        }
    }

    /// Releases a key on the hexadecimal keypad.
    ///
    /// `key` should be in the range `0..15`.
    pub fn release_key(&mut self, key: usize) {
        assert!(key <= 15);
        self.keys[key] = false;
    }

    /// Decrements the sound and delay timers.
    ///
    /// They should be decremented at a rate of 60 Hz.
    pub fn decrement_timers(&mut self) {
        if self.delay_timer > 0 {
            self.delay_timer -= 1;
        }
        if self.sound_timer > 0 {
            self.sound_timer -= 1;
        }
    }

    /// Returns whether the display has been updated.
    pub fn display_updated(&self) -> bool { self.display_updated }
    /// Returns the contents of the display.
    pub fn display(&self) -> &[u8; DISPLAY_WIDTH * DISPLAY_HEIGHT] { &*self.display }
}
<|endoftext|>


<commit_before>#![allow(unstable)]
extern crate num;

use num::{Complex, Zero};
use std::iter::{repeat, range_step_inclusive, range_step};
use std::f32;

pub struct FFT {
    scratch: Vec<Complex<f32>>,
    factors: Vec<(usize, usize)>,
    twiddles: Vec<Complex<f32>>,
}

impl FFT {
    pub fn new(len: usize) -> Self {
        FFT {
            scratch: repeat(Zero::zero()).take(len).collect(),
            factors: factor(len),
            twiddles: range(0, len)
                      .map(|i| -1. * (i as f32) * f32::consts::PI_2 / (len as f32))
                      .map(|phase| Complex::from_polar(&1., &phase))
                      .collect(),
        }
    }

    pub fn process(&mut self, signal: &[Complex<f32>], spectrum: &mut [Complex<f32>]) {
        cooley_tukey(signal, 1,
                     spectrum, 1,
                     self.scratch.as_mut_slice(), 1,
                     self.twiddles.as_slice(),
                     self.factors.as_slice());
    }
}

//TODO can we collapse all these strides into one stride value?
fn cooley_tukey(signal: &[Complex<f32>],
                signal_stride: usize,
                spectrum: &mut [Complex<f32>],
                spectrum_stride: usize,
                scratch: &mut [Complex<f32>],
                scratch_stride: usize,
                twiddles: &[Complex<f32>],
                factors: &[(usize, usize)]) {
    match factors {
        [_] | [] => dft_slice(signal, signal_stride,
                              spectrum, spectrum_stride,
                              twiddles),
        [(n1, n2), other_factors..] => {
            for i in range(0, n1)
            {
                // perform the smaller FFTs from the signal buffer into
                // the scratch buffer, using the spectrum buffer as scratch space
                cooley_tukey(signal.slice_from(i * signal_stride), signal_stride * n1,
                             scratch.slice_from_mut(i * scratch_stride), scratch_stride * n1,
                             spectrum.slice_from_mut(i * spectrum_stride), spectrum_stride * n1,
                             twiddles, other_factors);
            }

            butterfly(scratch, scratch_stride,
                      spectrum, spectrum_stride,
                      twiddles, n1, n2);
        }
    }
}

fn butterfly(input: &[Complex<f32>],
             input_stride: usize,
             output: &mut [Complex<f32>],
             output_stride: usize,
             twiddles: &[Complex<f32>],
             num_cols: usize,
             num_rows: usize) {
    // for each row in input
    for (i, in_row) in input.chunks(num_cols * input_stride).enumerate() {
        let out_col = output.slice_from_mut(i * output_stride);
        let out_col_stride = output_stride * num_rows;
        for (j, spec_bin_idx) in range_step(0, out_col.len(), out_col_stride).enumerate() {
            let spec_bin = unsafe { out_col.get_unchecked_mut(spec_bin_idx) };
            *spec_bin = Zero::zero();
            for (k, sig_bin_idx) in range_step(0, in_row.len(), input_stride).enumerate() {
                let sig_bin = unsafe { in_row.get_unchecked(sig_bin_idx) };
                let twiddle_power = i * k + j * k * num_rows;
                let n = twiddles.len();
                let twiddle = unsafe {
                    twiddles.get_unchecked(((twiddle_power * n ) /
                                           (num_cols * num_rows)) % n)
                };
                *spec_bin = *spec_bin + *twiddle * *sig_bin;
            }
        }
    }
}

pub fn dft_slice(signal: &[Complex<f32>],
                 signal_stride: usize,
                 spectrum: &mut [Complex<f32>],
                 spectrum_stride: usize,
                 twiddles: &[Complex<f32>]) {
    for (k, spec_bin_idx) in range_step(0, spectrum.len(), spectrum_stride).enumerate() {
        let spec_bin = unsafe { spectrum.get_unchecked_mut(spec_bin_idx) };
        *spec_bin = Zero::zero();
        for (i, signal_bin_idx) in range_step(0, signal.len(), signal_stride).enumerate() {
            let signal_bin = unsafe { signal.get_unchecked(signal_bin_idx) };
            let twiddle = unsafe {
                //SPEED we can add and subtract by twiddles.len, instead of modulo
                twiddles.get_unchecked((i * k * signal_stride) % twiddles.len()) };
            *spec_bin = *spec_bin + twiddle * *signal_bin;
        }
    }
}

pub fn dft<'a, 'b, I, O>(signal: I, spectrum: O)
where I: Iterator<Item=&'a Complex<f32>> + ExactSizeIterator + Clone,
      O: Iterator<Item=&'b mut Complex<f32>>
{
    for (k, spec_bin) in spectrum.enumerate()
    {
        let signal_iter = signal.clone();
        let mut sum: Complex<f32> = Zero::zero();
        for (i, &x) in signal_iter.enumerate() {
            let angle = -1. * ((i * k) as f32) * f32::consts::PI_2 / (signal.len() as f32);
            let twiddle: Complex<f32> = Complex::from_polar(&1f32, &angle);
            sum = sum + twiddle * x;
        }
        *spec_bin = sum;
    }
}

fn factor(n: usize) -> Vec<(usize, usize)>
{
    let mut factors = Vec::new();
    let mut next = n;
    while next > 1
    {
        for div in Some(2us).into_iter().chain(range_step_inclusive(3us, next, 2))
        {
            if next % div == 0
            {
                next = next / div;
                factors.push((div, next));
                break;
            }
        }
    }
    return factors;
}
<commit_msg>Explicitely increase the twiddle idx, instead of calculating it from multiplication and modulo. Large performance gain<commit_after>#![allow(unstable)]
extern crate num;

use num::{Complex, Zero};
use std::iter::{repeat, range_step_inclusive, range_step};
use std::f32;

pub struct FFT {
    scratch: Vec<Complex<f32>>,
    factors: Vec<(usize, usize)>,
    twiddles: Vec<Complex<f32>>,
}

impl FFT {
    pub fn new(len: usize) -> Self {
        FFT {
            scratch: repeat(Zero::zero()).take(len).collect(),
            factors: factor(len),
            twiddles: range(0, len)
                      .map(|i| -1. * (i as f32) * f32::consts::PI_2 / (len as f32))
                      .map(|phase| Complex::from_polar(&1., &phase))
                      .collect(),
        }
    }

    pub fn process(&mut self, signal: &[Complex<f32>], spectrum: &mut [Complex<f32>]) {
        cooley_tukey(signal, 1,
                     spectrum, 1,
                     self.scratch.as_mut_slice(), 1,
                     self.twiddles.as_slice(),
                     self.factors.as_slice());
    }
}

//TODO can we collapse all these strides into one stride value?
fn cooley_tukey(signal: &[Complex<f32>],
                signal_stride: usize,
                spectrum: &mut [Complex<f32>],
                spectrum_stride: usize,
                scratch: &mut [Complex<f32>],
                scratch_stride: usize,
                twiddles: &[Complex<f32>],
                factors: &[(usize, usize)]) {
    match factors {
        [_] | [] => dft_slice(signal, signal_stride,
                              spectrum, spectrum_stride,
                              twiddles),
        [(n1, n2), other_factors..] => {
            for i in range(0, n1)
            {
                // perform the smaller FFTs from the signal buffer into
                // the scratch buffer, using the spectrum buffer as scratch space
                cooley_tukey(signal.slice_from(i * signal_stride), signal_stride * n1,
                             scratch.slice_from_mut(i * scratch_stride), scratch_stride * n1,
                             spectrum.slice_from_mut(i * spectrum_stride), spectrum_stride * n1,
                             twiddles, other_factors);
            }

            butterfly(scratch, scratch_stride,
                      spectrum, spectrum_stride,
                      twiddles, n1, n2);
        }
    }
}

fn butterfly(input: &[Complex<f32>],
             input_stride: usize,
             output: &mut [Complex<f32>],
             output_stride: usize,
             twiddles: &[Complex<f32>],
             num_cols: usize,
             num_rows: usize) {
    // for each row in input
    let mut twiddle_idx_increase_1 = 0;
    for (i, in_row) in input.chunks(num_cols * input_stride).enumerate() {
        let out_col = output.slice_from_mut(i * output_stride);
        let out_col_stride = output_stride * num_rows;
        let mut twiddle_idx_increase_2 = 0;
        for spec_bin_idx in range_step(0, out_col.len(), out_col_stride) {
            let spec_bin = unsafe { out_col.get_unchecked_mut(spec_bin_idx) };
            *spec_bin = Zero::zero();
            let mut twiddle_idx = 0;
            for sig_bin_idx in range_step(0, in_row.len(), input_stride) {
                let sig_bin = unsafe { in_row.get_unchecked(sig_bin_idx) };
                let twiddle = unsafe {
                    twiddles.get_unchecked(twiddle_idx)
                };
                *spec_bin = *spec_bin + *twiddle * *sig_bin;
                twiddle_idx += twiddle_idx_increase_1 + twiddle_idx_increase_2;
                if twiddle_idx > twiddles.len() { twiddle_idx -= twiddles.len() }
            }
            twiddle_idx_increase_2 += input_stride * num_rows;
        }
        twiddle_idx_increase_1 += input_stride;
    }
}

pub fn dft_slice(signal: &[Complex<f32>],
                 signal_stride: usize,
                 spectrum: &mut [Complex<f32>],
                 spectrum_stride: usize,
                 twiddles: &[Complex<f32>]) {
    let mut twiddle_idx_increase = 0;
    for spec_bin_idx in range_step(0, spectrum.len(), spectrum_stride) {
        let spec_bin = unsafe { spectrum.get_unchecked_mut(spec_bin_idx) };
        *spec_bin = Zero::zero();
        let mut twiddle_idx = 0;
        for signal_bin_idx in range_step(0, signal.len(), signal_stride) {
            let signal_bin = unsafe { signal.get_unchecked(signal_bin_idx) };
            let twiddle = unsafe { twiddles.get_unchecked(twiddle_idx) };
            *spec_bin = *spec_bin + twiddle * *signal_bin;
            twiddle_idx += twiddle_idx_increase;
            if twiddle_idx > twiddles.len() {twiddle_idx -= twiddles.len()}
        }
        twiddle_idx_increase += signal_stride;
    }
}

pub fn dft<'a, 'b, I, O>(signal: I, spectrum: O)
where I: Iterator<Item=&'a Complex<f32>> + ExactSizeIterator + Clone,
      O: Iterator<Item=&'b mut Complex<f32>>
{
    for (k, spec_bin) in spectrum.enumerate()
    {
        let signal_iter = signal.clone();
        let mut sum: Complex<f32> = Zero::zero();
        for (i, &x) in signal_iter.enumerate() {
            let angle = -1. * ((i * k) as f32) * f32::consts::PI_2 / (signal.len() as f32);
            let twiddle: Complex<f32> = Complex::from_polar(&1f32, &angle);
            sum = sum + twiddle * x;
        }
        *spec_bin = sum;
    }
}

fn factor(n: usize) -> Vec<(usize, usize)>
{
    let mut factors = Vec::new();
    let mut next = n;
    while next > 1
    {
        for div in Some(2us).into_iter().chain(range_step_inclusive(3us, next, 2))
        {
            if next % div == 0
            {
                next = next / div;
                factors.push((div, next));
                break;
            }
        }
    }
    return factors;
}
<|endoftext|>


<commit_before>//! Hubcaps provides a set of building blocks for interacting with the Github API

#[macro_use]
extern crate serializable_enum;
#[macro_use]
extern crate log;
extern crate hyper;
extern crate serde;
extern crate serde_json;
extern crate url;

use serde::de::Deserialize;
pub mod keys;
pub mod gists;
pub mod deployments;
pub mod errors;
pub mod issues;
pub mod labels;
pub mod rep;
pub mod releases;
pub mod repositories;
pub mod statuses;
pub mod pulls;

pub use rep::*;
pub use errors::Error;
use gists::{Gists, UserGists};
use hyper::Client;
use hyper::client::RequestBuilder;
use hyper::method::Method;
use hyper::header::{Authorization, ContentLength, UserAgent};
use hyper::status::StatusCode;
use repositories::{Repository, Repositories, UserRepositories, OrganizationRepositories};
use std::fmt;
use std::io::Read;
use url::Url;

const DEFAULT_HOST: &'static str = "https://api.github.com";

/// alias for Result that infers hubcaps::Error as Err
pub type Result<T> = std::result::Result<T, Error>;

/// enum representation of github pull and issue state
#[derive(Clone, Debug, PartialEq)]
pub enum State {
    /// Only open issues
    Open,
    /// Only closed issues
    Closed,
    /// All issues, open or closed
    All,
}

impl fmt::Display for State {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{}",
               match *self {
                   State::Open => "open",
                   State::Closed => "closed",
                   State::All => "all",
               })
    }
}

impl Default for State {
    fn default() -> State {
        State::Open
    }
}

/// enum representation of Github list sorting options
#[derive(Clone, Debug, PartialEq)]
pub enum SortDirection {
    /// Sort in ascending order
    Asc,
    /// Sort in descending order
    Desc,
}

impl fmt::Display for SortDirection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{}",
               match *self {
                   SortDirection::Asc => "asc",
                   SortDirection::Desc => "desc",
               })
    }
}

impl Default for SortDirection {
    fn default() -> SortDirection {
        SortDirection::Asc
    }
}

/// Various forms of authentication credentials supported by Github
#[derive(Debug, PartialEq)]
pub enum Credentials {
    /// No authentication
    None,
    /// Oauth token string
    /// https://developer.github.com/v3/#oauth2-token-sent-in-a-header
    Token(String),
    /// Oauth client id and secret
    /// https://developer.github.com/v3/#oauth2-keysecret
    Client(String, String),
}

impl Default for Credentials {
    fn default() -> Credentials {
        Credentials::None
    }
}

/// Entry point interface for interacting with Github API
pub struct Github<'a> {
    host: String,
    agent: String,
    client: &'a Client,
    credentials: Credentials,
}

impl<'a> Github<'a> {
    /// Create a new Github instance
    pub fn new<A>(agent: A, client: &'a Client, credentials: Credentials) -> Github<'a>
        where A: Into<String>
    {
        Github::host(DEFAULT_HOST, agent, client, credentials)
    }

    /// Create a new Github instance hosted at a custom location.
    /// Useful for github enterprise installations ( yourdomain.com/api/v3/ )
    pub fn host<H, A>(host: H, agent: A, client: &'a Client, credentials: Credentials) -> Github<'a>
        where H: Into<String>,
              A: Into<String>
    {
        Github {
            host: host.into(),
            agent: agent.into(),
            client: client,
            credentials: credentials,
        }
    }

    /// Return a reference to a Github reposistory
    pub fn repo<O, R>(&self, owner: O, repo: R) -> Repository
        where O: Into<String>,
              R: Into<String>
    {
        Repository::new(self, owner, repo)
    }

    /// Return a reference to the collection of repositories owned by an
    /// associated with an owner
    pub fn user_repos<S>(&self, owner: S) -> UserRepositories
        where S: Into<String>
    {
        UserRepositories::new(self, owner)
    }

    /// Return a reference to  the collection of repositores owned by the user
    /// associated with the current authentication credentials
    pub fn repos(&self) -> Repositories {
        Repositories::new(self)
    }

    /// Return a reference to an interface that provides access to a user's gists
    pub fn user_gists<O>(&self, owner: O) -> UserGists
        where O: Into<String>
    {
        UserGists::new(self, owner)
    }

    /// Return a reference to an interface that provides access to the
    /// gists belonging to the owner of the token used to configure this client
    pub fn gists(&self) -> Gists {
        Gists::new(self)
    }

    /// Return a reference to a Github Organization
    pub fn org_repos<O>(&self, org: O) -> OrganizationRepositories
        where O: Into<String>
    {
        OrganizationRepositories::new(self, org)
    }

    fn authenticate(&self, method: Method, uri: &str) -> RequestBuilder {
        let url = format!("{}{}", self.host, uri);
        match self.credentials {
            Credentials::Token(ref token) => {
                self.client.request(method, &url).header(Authorization(format!("token {}", token)))
            }
            Credentials::Client(ref id, ref secret) => {
                let mut parsed = Url::parse(&url).unwrap();
                let mut query = parsed.query_pairs().unwrap_or(vec![]);
                query.push(("client_id".to_owned(), id.to_owned()));
                query.push(("client_secret".to_owned(), secret.to_owned()));
                parsed.set_query_from_pairs(query);
                self.client.request(method, parsed)
            }
            Credentials::None => self.client.request(method, &url),
        }
    }

    fn request<D>(&self, method: Method, uri: &str, body: Option<&'a [u8]>) -> Result<D>
        where D: Deserialize
    {
        let builder = self.authenticate(method, uri).header(UserAgent(self.agent.to_owned()));
        let mut res = try!(match body {
            Some(ref bod) => builder.body(*bod).send(),
            _ => builder.send(),
        });
        let mut body = match res.headers.clone().get::<ContentLength>() {
            Some(&ContentLength(len)) => String::with_capacity(len as usize),
            _ => String::new(),
        };
        try!(res.read_to_string(&mut body));
        debug!("rev response {:#?} {:#?} {:#?}",
               res.status,
               res.headers,
               body);
        match res.status {
            StatusCode::Conflict |
            StatusCode::BadRequest |
            StatusCode::UnprocessableEntity |
            StatusCode::Unauthorized |
            StatusCode::NotFound |
            StatusCode::Forbidden => {
                Err(Error::Fault {
                    code: res.status,
                    error: try!(serde_json::from_str::<ClientError>(&body)),
                })
            }
            _ => Ok(try!(serde_json::from_str::<D>(&body))),
        }
    }

    fn get<D>(&self, uri: &str) -> Result<D>
        where D: Deserialize
    {
        self.request(Method::Get, uri, None)
    }

    fn delete(&self, uri: &str) -> Result<()> {
        match self.request::<()>(Method::Delete, uri, None) {
            Err(Error::Codec(_)) => Ok(()),
            otherwise => otherwise,
        }
    }

    fn post<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request(Method::Post, uri, Some(message))
    }

    fn patch<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request(Method::Patch, uri, Some(message))
    }

    fn put<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request(Method::Put, uri, Some(message))
    }
}

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

    #[test]
    fn default_state() {
        let default: State = Default::default();
        assert_eq!(default, State::Open)
    }

    #[test]
    fn default_sort_direction() {
        let default: SortDirection = Default::default();
        assert_eq!(default, SortDirection::Asc)
    }

    #[test]
    fn default_credentials() {
        let default: Credentials = Default::default();
        assert_eq!(default, Credentials::None)
    }
}
<commit_msg>Clarify doc comments<commit_after>//! Hubcaps provides a set of building blocks for interacting with the Github API

#[macro_use]
extern crate serializable_enum;
#[macro_use]
extern crate log;
extern crate hyper;
extern crate serde;
extern crate serde_json;
extern crate url;

use serde::de::Deserialize;
pub mod keys;
pub mod gists;
pub mod deployments;
pub mod errors;
pub mod issues;
pub mod labels;
pub mod rep;
pub mod releases;
pub mod repositories;
pub mod statuses;
pub mod pulls;

pub use rep::*;
pub use errors::Error;
use gists::{Gists, UserGists};
use hyper::Client;
use hyper::client::RequestBuilder;
use hyper::method::Method;
use hyper::header::{Authorization, ContentLength, UserAgent};
use hyper::status::StatusCode;
use repositories::{Repository, Repositories, UserRepositories, OrganizationRepositories};
use std::fmt;
use std::io::Read;
use url::Url;

const DEFAULT_HOST: &'static str = "https://api.github.com";

/// alias for Result that infers hubcaps::Error as Err
pub type Result<T> = std::result::Result<T, Error>;

/// enum representation of github pull and issue state
#[derive(Clone, Debug, PartialEq)]
pub enum State {
    /// Only open issues
    Open,
    /// Only closed issues
    Closed,
    /// All issues, open or closed
    All,
}

impl fmt::Display for State {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{}",
               match *self {
                   State::Open => "open",
                   State::Closed => "closed",
                   State::All => "all",
               })
    }
}

impl Default for State {
    fn default() -> State {
        State::Open
    }
}

/// enum representation of Github list sorting options
#[derive(Clone, Debug, PartialEq)]
pub enum SortDirection {
    /// Sort in ascending order
    Asc,
    /// Sort in descending order
    Desc,
}

impl fmt::Display for SortDirection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{}",
               match *self {
                   SortDirection::Asc => "asc",
                   SortDirection::Desc => "desc",
               })
    }
}

impl Default for SortDirection {
    fn default() -> SortDirection {
        SortDirection::Asc
    }
}

/// Various forms of authentication credentials supported by Github
#[derive(Debug, PartialEq)]
pub enum Credentials {
    /// No authentication
    None,
    /// Oauth token string
    /// https://developer.github.com/v3/#oauth2-token-sent-in-a-header
    Token(String),
    /// Oauth client id and secret
    /// https://developer.github.com/v3/#oauth2-keysecret
    Client(String, String),
}

impl Default for Credentials {
    fn default() -> Credentials {
        Credentials::None
    }
}

/// Entry point interface for interacting with Github API
pub struct Github<'a> {
    host: String,
    agent: String,
    client: &'a Client,
    credentials: Credentials,
}

impl<'a> Github<'a> {
    /// Create a new Github instance
    pub fn new<A>(agent: A, client: &'a Client, credentials: Credentials) -> Github<'a>
        where A: Into<String>
    {
        Github::host(DEFAULT_HOST, agent, client, credentials)
    }

    /// Create a new Github instance hosted at a custom location.
    /// Useful for github enterprise installations ( yourdomain.com/api/v3/ )
    pub fn host<H, A>(host: H, agent: A, client: &'a Client, credentials: Credentials) -> Github<'a>
        where H: Into<String>,
              A: Into<String>
    {
        Github {
            host: host.into(),
            agent: agent.into(),
            client: client,
            credentials: credentials,
        }
    }

    /// Return a reference to a Github reposistory
    pub fn repo<O, R>(&self, owner: O, repo: R) -> Repository
        where O: Into<String>,
              R: Into<String>
    {
        Repository::new(self, owner, repo)
    }

    /// Return a reference to the collection of repositories owned by and
    /// associated with an owner
    pub fn user_repos<S>(&self, owner: S) -> UserRepositories
        where S: Into<String>
    {
        UserRepositories::new(self, owner)
    }

    /// Return a reference to the collection of repositories owned by the user
    /// associated with the current authentication credentials
    pub fn repos(&self) -> Repositories {
        Repositories::new(self)
    }

    /// Return a reference to an interface that provides access to a user's gists
    pub fn user_gists<O>(&self, owner: O) -> UserGists
        where O: Into<String>
    {
        UserGists::new(self, owner)
    }

    /// Return a reference to an interface that provides access to the
    /// gists belonging to the owner of the token used to configure this client
    pub fn gists(&self) -> Gists {
        Gists::new(self)
    }

    /// Return a reference to the collection of repositories owned by and
    /// associated with an organization
    pub fn org_repos<O>(&self, org: O) -> OrganizationRepositories
        where O: Into<String>
    {
        OrganizationRepositories::new(self, org)
    }

    fn authenticate(&self, method: Method, uri: &str) -> RequestBuilder {
        let url = format!("{}{}", self.host, uri);
        match self.credentials {
            Credentials::Token(ref token) => {
                self.client.request(method, &url).header(Authorization(format!("token {}", token)))
            }
            Credentials::Client(ref id, ref secret) => {
                let mut parsed = Url::parse(&url).unwrap();
                let mut query = parsed.query_pairs().unwrap_or(vec![]);
                query.push(("client_id".to_owned(), id.to_owned()));
                query.push(("client_secret".to_owned(), secret.to_owned()));
                parsed.set_query_from_pairs(query);
                self.client.request(method, parsed)
            }
            Credentials::None => self.client.request(method, &url),
        }
    }

    fn request<D>(&self, method: Method, uri: &str, body: Option<&'a [u8]>) -> Result<D>
        where D: Deserialize
    {
        let builder = self.authenticate(method, uri).header(UserAgent(self.agent.to_owned()));
        let mut res = try!(match body {
            Some(ref bod) => builder.body(*bod).send(),
            _ => builder.send(),
        });
        let mut body = match res.headers.clone().get::<ContentLength>() {
            Some(&ContentLength(len)) => String::with_capacity(len as usize),
            _ => String::new(),
        };
        try!(res.read_to_string(&mut body));
        debug!("rev response {:#?} {:#?} {:#?}",
               res.status,
               res.headers,
               body);
        match res.status {
            StatusCode::Conflict |
            StatusCode::BadRequest |
            StatusCode::UnprocessableEntity |
            StatusCode::Unauthorized |
            StatusCode::NotFound |
            StatusCode::Forbidden => {
                Err(Error::Fault {
                    code: res.status,
                    error: try!(serde_json::from_str::<ClientError>(&body)),
                })
            }
            _ => Ok(try!(serde_json::from_str::<D>(&body))),
        }
    }

    fn get<D>(&self, uri: &str) -> Result<D>
        where D: Deserialize
    {
        self.request(Method::Get, uri, None)
    }

    fn delete(&self, uri: &str) -> Result<()> {
        match self.request::<()>(Method::Delete, uri, None) {
            Err(Error::Codec(_)) => Ok(()),
            otherwise => otherwise,
        }
    }

    fn post<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request(Method::Post, uri, Some(message))
    }

    fn patch<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request(Method::Patch, uri, Some(message))
    }

    fn put<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request(Method::Put, uri, Some(message))
    }
}

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

    #[test]
    fn default_state() {
        let default: State = Default::default();
        assert_eq!(default, State::Open)
    }

    #[test]
    fn default_sort_direction() {
        let default: SortDirection = Default::default();
        assert_eq!(default, SortDirection::Asc)
    }

    #[test]
    fn default_credentials() {
        let default: Credentials = Default::default();
        assert_eq!(default, Credentials::None)
    }
}
<|endoftext|>