<commit_before>extern crate libc;

#[cfg(target_os = "macos")]
use crate::libproc::helpers;

#[cfg(target_os = "macos")]
use self::libc::{c_void, c_int};

#[cfg(target_os = "linux")]
use crate::libproc::helpers::{procfile_field, parse_memory_string};

/// The `PIDRUsage` trait is needed for polymorphism on pidrusage types, also abstracting flavor in order to provide
/// type-guaranteed flavor correctness
pub trait PIDRUsage: Default {
    /// Return the `PidRUsageFlavor` for the implementing struct
    fn flavor() -> PidRUsageFlavor;
}

/// `PidRUsageFlavor` From https://opensource.apple.com/source/xnu/xnu-4903.221.2/bsd/sys/resource.h
pub enum PidRUsageFlavor {
    /// Version 0
    V0 = 0,
    /// Version 1
    V1 = 1,
    /// Version 2
    V2 = 2,
    /// Version 3
    V3 = 3,
    /// Version 4
    V4 = 4,
}

// this extern block links to the libproc library
// Original signatures of functions can be found at http://opensource.apple.com/source/Libc/Libc-594.9.4/darwin/libproc.c
#[cfg(target_os = "macos")]
#[link(name = "proc", kind = "dylib")]
extern {
    fn proc_pid_rusage(pid: c_int, flavor: c_int, buffer: *mut c_void) -> c_int;
}

/// C struct for Resource Usage Version 0
#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV0 {
    /// Unique user id
    pub ri_uuid              : [u8; 16],
    /// User time used
    pub ri_user_time         : u64,
    /// System time used
    pub ri_system_time       : u64,
    /// Wakeups from idle
    pub ri_pkg_idle_wkups    : u64,
    /// Interrupt wakeups
    pub ri_interrupt_wkups   : u64,
    /// Number of pageins
    pub ri_pageins           : u64,
    /// Wired size
    pub ri_wired_size        : u64,
    /// Resident size
    pub ri_resident_size     : u64,
    /// Physical footprint
    pub ri_phys_footprint    : u64,
    /// Process start time
    pub ri_proc_start_abstime: u64,
    /// Process exit time
    pub ri_proc_exit_abstime : u64,
}

impl PIDRUsage for RUsageInfoV0 {
    fn flavor() -> PidRUsageFlavor { PidRUsageFlavor::V0 }
}

/// C struct for Resource Usage Version 1
#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV1 {
    /// Unique user id
    pub ri_uuid                 : [u8; 16],
    /// User time used
    pub ri_user_time            : u64,
    /// System time used
    pub ri_system_time          : u64,
    /// Wakeups from idle
    pub ri_pkg_idle_wkups       : u64,
    /// Interrupt wakeups
    pub ri_interrupt_wkups      : u64,
    /// Number of pageins
    pub ri_pageins              : u64,
    /// Wired size
    pub ri_wired_size           : u64,
    /// Resident size
    pub ri_resident_size        : u64,
    /// Physical footprint
    pub ri_phys_footprint       : u64,
    /// Process start time
    pub ri_proc_start_abstime   : u64,
    /// Process exit time
    pub ri_proc_exit_abstime    : u64,
    /// Child user time
    pub ri_child_user_time      : u64,
    /// Child system time
    pub ri_child_system_time    : u64,
    /// Child wakeups from idle
    pub ri_child_pkg_idle_wkups : u64,
    /// Child interrupt wakeups
    pub ri_child_interrupt_wkups: u64,
    /// Child pageins
    pub ri_child_pageins        : u64,
    /// Child elapse time
    pub ri_child_elapsed_abstime: u64,
}

impl PIDRUsage for RUsageInfoV1 {
    fn flavor() -> PidRUsageFlavor { PidRUsageFlavor::V1 }
}

/// C struct for Resource Usage Version 2
#[repr(C)]
#[derive(Debug, Default)]
pub struct RUsageInfoV2 {
    /// Unique user id
    pub ri_uuid                 : [u8; 16],
    /// User time used
    pub ri_user_time            : u64,
    /// System time used
    pub ri_system_time          : u64,
    /// Wakeups from idle
    pub ri_pkg_idle_wkups       : u64,
    /// Interrupt wakeups
    pub ri_interrupt_wkups      : u64,
    /// Number of pageins
    pub ri_pageins              : u64,
    /// Wired size
    pub ri_wired_size           : u64,
    /// Resident size
    pub ri_resident_size        : u64,
    /// Physical footprint
    pub ri_phys_footprint       : u64,
    /// Process start time
    pub ri_proc_start_abstime   : u64,
    /// Process exit time
    pub ri_proc_exit_abstime    : u64,
    /// Child user time
    pub ri_child_user_time      : u64,
    /// Child system time
    pub ri_child_system_time    : u64,
    /// Child wakeups from idle
    pub ri_child_pkg_idle_wkups : u64,
    /// Child interrupt wakeups
    pub ri_child_interrupt_wkups: u64,
    /// Child pageins
    pub ri_child_pageins        : u64,
    /// Child elapse time
    pub ri_child_elapsed_abstime: u64,
    /// Disk IO bytes read
    pub ri_diskio_bytesread     : u64,
    /// Disk IO bytes written
    pub ri_diskio_byteswritten  : u64,
}

impl PIDRUsage for RUsageInfoV2 {
    fn flavor() -> PidRUsageFlavor { PidRUsageFlavor::V2 }
}

/// C struct for Resource Usage Version 3
#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV3 {
    /// Unique user id
    pub ri_uuid                         : [u8; 16],
    /// User time used
    pub ri_user_time                    : u64,
    /// System time used
    pub ri_system_time                  : u64,
    /// Wakeups from idle
    pub ri_pkg_idle_wkups               : u64,
    /// Interrupt wakeups
    pub ri_interrupt_wkups              : u64,
    /// Number of pageins
    pub ri_pageins                      : u64,
    /// Wired size
    pub ri_wired_size                   : u64,
    /// Resident size
    pub ri_resident_size                : u64,
    /// Physical footprint
    pub ri_phys_footprint               : u64,
    /// Process start time
    pub ri_proc_start_abstime           : u64,
    /// Process exit time
    pub ri_proc_exit_abstime            : u64,
    /// Child user time
    pub ri_child_user_time              : u64,
    /// Child system time
    pub ri_child_system_time            : u64,
    /// Child wakeups from idle
    pub ri_child_pkg_idle_wkups         : u64,
    /// Child interrupt wakeups
    pub ri_child_interrupt_wkups        : u64,
    /// Child pageins
    pub ri_child_pageins                : u64,
    /// Child elapse time
    pub ri_child_elapsed_abstime        : u64,
    /// Disk IO bytes read
    pub ri_diskio_bytesread             : u64,
    /// Disk IO bytes written
    pub ri_diskio_byteswritten          : u64,
    /// CPU time QOS default
    pub ri_cpu_time_qos_default         : u64,
    /// CPU time QOS maintenance
    pub ri_cpu_time_qos_maintenance     : u64,
    /// CPU time QOS background
    pub ri_cpu_time_qos_background      : u64,
    /// CPU time QOS utility
    pub ri_cpu_time_qos_utility         : u64,
    /// CPU time QOS legacy
    pub ri_cpu_time_qos_legacy          : u64,
    /// CPU time QOS user initiated
    pub ri_cpu_time_qos_user_initiated  : u64,
    /// CPU tim QOS user interactive
    pub ri_cpu_time_qos_user_interactive: u64,
    /// Billed system time
    pub ri_billed_system_time           : u64,
    /// Serviced system time
    pub ri_serviced_system_time         : u64,
}

impl PIDRUsage for RUsageInfoV3 {
    fn flavor() -> PidRUsageFlavor { PidRUsageFlavor::V3 }
}

/// C struct for Resource Usage Version 4
#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV4 {
    /// Unique user id
    pub ri_uuid                         : [u8; 16],
    /// User time used
    pub ri_user_time                    : u64,
    /// System time used
    pub ri_system_time                  : u64,
    /// Wakeups from idle
    pub ri_pkg_idle_wkups               : u64,
    /// Child interrupt wakeups
    pub ri_interrupt_wkups              : u64,
    /// Number of pageins
    pub ri_pageins                      : u64,
    /// Wired size
    pub ri_wired_size                   : u64,
    /// Resident size
    pub ri_resident_size                : u64,
    /// Physical footprint
    pub ri_phys_footprint               : u64,
    /// Process start time
    pub ri_proc_start_abstime           : u64,
    /// Process exit time
    pub ri_proc_exit_abstime            : u64,
    /// Child user time
    pub ri_child_user_time              : u64,
    /// Child system time
    pub ri_child_system_time            : u64,
    /// Child wakeups from idle
    pub ri_child_pkg_idle_wkups         : u64,
    /// Child interrupt wakeups
    pub ri_child_interrupt_wkups        : u64,
    /// Child pageins
    pub ri_child_pageins                : u64,
    /// Child elapse time
    pub ri_child_elapsed_abstime        : u64,
    /// Disk IO bytes read
    pub ri_diskio_bytesread             : u64,
    /// Disk IO bytes written
    pub ri_diskio_byteswritten          : u64,
    /// CPU time QOS default
    pub ri_cpu_time_qos_default         : u64,
    /// CPU time QOS maintenance
    pub ri_cpu_time_qos_maintenance     : u64,
    /// CPU time QOS background
    pub ri_cpu_time_qos_background      : u64,
    /// CPU time QOS utility
    pub ri_cpu_time_qos_utility         : u64,
    /// CPU time QOS legacy
    pub ri_cpu_time_qos_legacy          : u64,
    /// CPU time QOS user initiated
    pub ri_cpu_time_qos_user_initiated  : u64,
    /// CPU tim QOS user interactive
    pub ri_cpu_time_qos_user_interactive: u64,
    /// Billed system time
    pub ri_billed_system_time           : u64,
    /// Serviced system time
    pub ri_serviced_system_time         : u64,
    /// Logical writes
    pub ri_logical_writes               : u64,
    /// Lifetime maximum physical footprint
    pub ri_lifetime_max_phys_footprint  : u64,
    /// instructions
    pub ri_instructions                 : u64,
    /// cycles
    pub ri_cycles                       : u64,
    /// billed energy
    pub ri_billed_energy                : u64,
    /// services energy
    pub ri_serviced_energy              : u64,
    /// interval maximum physical footprint
    pub ri_interval_max_phys_footprint  : u64,
    /// unused
    pub ri_unused                       : [u64; 1],
}

impl PIDRUsage for RUsageInfoV4 {
    fn flavor() -> PidRUsageFlavor { PidRUsageFlavor::V4 }
}

#[cfg(target_os = "macos")]
#[cfg(feature = "macosx_10_9")]
/// Returns the information about resources of the process that match pid passed in.
///
/// # Examples
///
/// ```
/// use std::io::Write;
/// use libproc::libproc::pid_rusage::{pidrusage, RUsageInfoV2};
///
/// fn pidrusage_test() {
///     use std::process;
///     let pid = process::id() as i32;
///
/// #[cfg(target_os = "macos")]
///     if let Ok(res) = pidrusage::<RUsageInfoV2>(pid) {
///         println!("UUID: {:?}, Disk Read: {}, Disk Write: {}", res.ri_uuid, res.ri_diskio_bytesread, res.ri_diskio_byteswritten);
///     }
/// }
/// ```
pub fn pidrusage<T: PIDRUsage>(pid : i32) -> Result<T, String> {
    let flavor = T::flavor() as i32;
    let mut pidrusage = T::default();
    let buffer_ptr = &mut pidrusage as *mut _ as *mut c_void;
    let ret: i32;

    unsafe {
        ret = proc_pid_rusage(pid, flavor, buffer_ptr);
    };

    if ret < 0 {
        Err(helpers::get_errno_with_message(ret))
    } else {
        Ok(pidrusage)
    }
}

#[cfg(target_os = "linux")]
/// Returns the information about resources of the process that match pid passed in.
///
/// # Examples
///
/// ```
/// use std::io::Write;
/// use libproc::libproc::pid_rusage::{pidrusage, RUsageInfoV2};
///
/// fn pidrusage_test() {
///     use std::process;
///     let pid = process::id() as i32;
///
///     if let Ok(res) = pidrusage(pid) {
///         println!("VmSize (resident_size): {}", res.ri_resident_size );
///     }
/// }
/// ```
pub fn pidrusage(pid : i32) -> Result<RUsageInfoV0, String> {
    let mut pidrusage = RUsageInfoV0::default();
    let vmsize = procfile_field(&format!("/proc/{}/status", pid), "VmSize")?;
    pidrusage.ri_resident_size = parse_memory_string(&vmsize)?;

    Ok(pidrusage)
}

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

    #[test]
    fn pidrusage_test() {
        let usage = pidrusage(std::process::id() as i32).expect("pidrusage() failed");
        assert!(usage.ri_resident_size > 0, "Resident size reports 0")
    }
}<commit_msg>Specify required type for macos tests<commit_after>extern crate libc;

#[cfg(target_os = "macos")]
use crate::libproc::helpers;

#[cfg(target_os = "macos")]
use self::libc::{c_void, c_int};

#[cfg(target_os = "linux")]
use crate::libproc::helpers::{procfile_field, parse_memory_string};

/// The `PIDRUsage` trait is needed for polymorphism on pidrusage types, also abstracting flavor in order to provide
/// type-guaranteed flavor correctness
pub trait PIDRUsage: Default {
    /// Return the `PidRUsageFlavor` for the implementing struct
    fn flavor() -> PidRUsageFlavor;
}

/// `PidRUsageFlavor` From https://opensource.apple.com/source/xnu/xnu-4903.221.2/bsd/sys/resource.h
pub enum PidRUsageFlavor {
    /// Version 0
    V0 = 0,
    /// Version 1
    V1 = 1,
    /// Version 2
    V2 = 2,
    /// Version 3
    V3 = 3,
    /// Version 4
    V4 = 4,
}

// this extern block links to the libproc library
// Original signatures of functions can be found at http://opensource.apple.com/source/Libc/Libc-594.9.4/darwin/libproc.c
#[cfg(target_os = "macos")]
#[link(name = "proc", kind = "dylib")]
extern {
    fn proc_pid_rusage(pid: c_int, flavor: c_int, buffer: *mut c_void) -> c_int;
}

/// C struct for Resource Usage Version 0
#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV0 {
    /// Unique user id
    pub ri_uuid              : [u8; 16],
    /// User time used
    pub ri_user_time         : u64,
    /// System time used
    pub ri_system_time       : u64,
    /// Wakeups from idle
    pub ri_pkg_idle_wkups    : u64,
    /// Interrupt wakeups
    pub ri_interrupt_wkups   : u64,
    /// Number of pageins
    pub ri_pageins           : u64,
    /// Wired size
    pub ri_wired_size        : u64,
    /// Resident size
    pub ri_resident_size     : u64,
    /// Physical footprint
    pub ri_phys_footprint    : u64,
    /// Process start time
    pub ri_proc_start_abstime: u64,
    /// Process exit time
    pub ri_proc_exit_abstime : u64,
}

impl PIDRUsage for RUsageInfoV0 {
    fn flavor() -> PidRUsageFlavor { PidRUsageFlavor::V0 }
}

/// C struct for Resource Usage Version 1
#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV1 {
    /// Unique user id
    pub ri_uuid                 : [u8; 16],
    /// User time used
    pub ri_user_time            : u64,
    /// System time used
    pub ri_system_time          : u64,
    /// Wakeups from idle
    pub ri_pkg_idle_wkups       : u64,
    /// Interrupt wakeups
    pub ri_interrupt_wkups      : u64,
    /// Number of pageins
    pub ri_pageins              : u64,
    /// Wired size
    pub ri_wired_size           : u64,
    /// Resident size
    pub ri_resident_size        : u64,
    /// Physical footprint
    pub ri_phys_footprint       : u64,
    /// Process start time
    pub ri_proc_start_abstime   : u64,
    /// Process exit time
    pub ri_proc_exit_abstime    : u64,
    /// Child user time
    pub ri_child_user_time      : u64,
    /// Child system time
    pub ri_child_system_time    : u64,
    /// Child wakeups from idle
    pub ri_child_pkg_idle_wkups : u64,
    /// Child interrupt wakeups
    pub ri_child_interrupt_wkups: u64,
    /// Child pageins
    pub ri_child_pageins        : u64,
    /// Child elapse time
    pub ri_child_elapsed_abstime: u64,
}

impl PIDRUsage for RUsageInfoV1 {
    fn flavor() -> PidRUsageFlavor { PidRUsageFlavor::V1 }
}

/// C struct for Resource Usage Version 2
#[repr(C)]
#[derive(Debug, Default)]
pub struct RUsageInfoV2 {
    /// Unique user id
    pub ri_uuid                 : [u8; 16],
    /// User time used
    pub ri_user_time            : u64,
    /// System time used
    pub ri_system_time          : u64,
    /// Wakeups from idle
    pub ri_pkg_idle_wkups       : u64,
    /// Interrupt wakeups
    pub ri_interrupt_wkups      : u64,
    /// Number of pageins
    pub ri_pageins              : u64,
    /// Wired size
    pub ri_wired_size           : u64,
    /// Resident size
    pub ri_resident_size        : u64,
    /// Physical footprint
    pub ri_phys_footprint       : u64,
    /// Process start time
    pub ri_proc_start_abstime   : u64,
    /// Process exit time
    pub ri_proc_exit_abstime    : u64,
    /// Child user time
    pub ri_child_user_time      : u64,
    /// Child system time
    pub ri_child_system_time    : u64,
    /// Child wakeups from idle
    pub ri_child_pkg_idle_wkups : u64,
    /// Child interrupt wakeups
    pub ri_child_interrupt_wkups: u64,
    /// Child pageins
    pub ri_child_pageins        : u64,
    /// Child elapse time
    pub ri_child_elapsed_abstime: u64,
    /// Disk IO bytes read
    pub ri_diskio_bytesread     : u64,
    /// Disk IO bytes written
    pub ri_diskio_byteswritten  : u64,
}

impl PIDRUsage for RUsageInfoV2 {
    fn flavor() -> PidRUsageFlavor { PidRUsageFlavor::V2 }
}

/// C struct for Resource Usage Version 3
#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV3 {
    /// Unique user id
    pub ri_uuid                         : [u8; 16],
    /// User time used
    pub ri_user_time                    : u64,
    /// System time used
    pub ri_system_time                  : u64,
    /// Wakeups from idle
    pub ri_pkg_idle_wkups               : u64,
    /// Interrupt wakeups
    pub ri_interrupt_wkups              : u64,
    /// Number of pageins
    pub ri_pageins                      : u64,
    /// Wired size
    pub ri_wired_size                   : u64,
    /// Resident size
    pub ri_resident_size                : u64,
    /// Physical footprint
    pub ri_phys_footprint               : u64,
    /// Process start time
    pub ri_proc_start_abstime           : u64,
    /// Process exit time
    pub ri_proc_exit_abstime            : u64,
    /// Child user time
    pub ri_child_user_time              : u64,
    /// Child system time
    pub ri_child_system_time            : u64,
    /// Child wakeups from idle
    pub ri_child_pkg_idle_wkups         : u64,
    /// Child interrupt wakeups
    pub ri_child_interrupt_wkups        : u64,
    /// Child pageins
    pub ri_child_pageins                : u64,
    /// Child elapse time
    pub ri_child_elapsed_abstime        : u64,
    /// Disk IO bytes read
    pub ri_diskio_bytesread             : u64,
    /// Disk IO bytes written
    pub ri_diskio_byteswritten          : u64,
    /// CPU time QOS default
    pub ri_cpu_time_qos_default         : u64,
    /// CPU time QOS maintenance
    pub ri_cpu_time_qos_maintenance     : u64,
    /// CPU time QOS background
    pub ri_cpu_time_qos_background      : u64,
    /// CPU time QOS utility
    pub ri_cpu_time_qos_utility         : u64,
    /// CPU time QOS legacy
    pub ri_cpu_time_qos_legacy          : u64,
    /// CPU time QOS user initiated
    pub ri_cpu_time_qos_user_initiated  : u64,
    /// CPU tim QOS user interactive
    pub ri_cpu_time_qos_user_interactive: u64,
    /// Billed system time
    pub ri_billed_system_time           : u64,
    /// Serviced system time
    pub ri_serviced_system_time         : u64,
}

impl PIDRUsage for RUsageInfoV3 {
    fn flavor() -> PidRUsageFlavor { PidRUsageFlavor::V3 }
}

/// C struct for Resource Usage Version 4
#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV4 {
    /// Unique user id
    pub ri_uuid                         : [u8; 16],
    /// User time used
    pub ri_user_time                    : u64,
    /// System time used
    pub ri_system_time                  : u64,
    /// Wakeups from idle
    pub ri_pkg_idle_wkups               : u64,
    /// Child interrupt wakeups
    pub ri_interrupt_wkups              : u64,
    /// Number of pageins
    pub ri_pageins                      : u64,
    /// Wired size
    pub ri_wired_size                   : u64,
    /// Resident size
    pub ri_resident_size                : u64,
    /// Physical footprint
    pub ri_phys_footprint               : u64,
    /// Process start time
    pub ri_proc_start_abstime           : u64,
    /// Process exit time
    pub ri_proc_exit_abstime            : u64,
    /// Child user time
    pub ri_child_user_time              : u64,
    /// Child system time
    pub ri_child_system_time            : u64,
    /// Child wakeups from idle
    pub ri_child_pkg_idle_wkups         : u64,
    /// Child interrupt wakeups
    pub ri_child_interrupt_wkups        : u64,
    /// Child pageins
    pub ri_child_pageins                : u64,
    /// Child elapse time
    pub ri_child_elapsed_abstime        : u64,
    /// Disk IO bytes read
    pub ri_diskio_bytesread             : u64,
    /// Disk IO bytes written
    pub ri_diskio_byteswritten          : u64,
    /// CPU time QOS default
    pub ri_cpu_time_qos_default         : u64,
    /// CPU time QOS maintenance
    pub ri_cpu_time_qos_maintenance     : u64,
    /// CPU time QOS background
    pub ri_cpu_time_qos_background      : u64,
    /// CPU time QOS utility
    pub ri_cpu_time_qos_utility         : u64,
    /// CPU time QOS legacy
    pub ri_cpu_time_qos_legacy          : u64,
    /// CPU time QOS user initiated
    pub ri_cpu_time_qos_user_initiated  : u64,
    /// CPU tim QOS user interactive
    pub ri_cpu_time_qos_user_interactive: u64,
    /// Billed system time
    pub ri_billed_system_time           : u64,
    /// Serviced system time
    pub ri_serviced_system_time         : u64,
    /// Logical writes
    pub ri_logical_writes               : u64,
    /// Lifetime maximum physical footprint
    pub ri_lifetime_max_phys_footprint  : u64,
    /// instructions
    pub ri_instructions                 : u64,
    /// cycles
    pub ri_cycles                       : u64,
    /// billed energy
    pub ri_billed_energy                : u64,
    /// services energy
    pub ri_serviced_energy              : u64,
    /// interval maximum physical footprint
    pub ri_interval_max_phys_footprint  : u64,
    /// unused
    pub ri_unused                       : [u64; 1],
}

impl PIDRUsage for RUsageInfoV4 {
    fn flavor() -> PidRUsageFlavor { PidRUsageFlavor::V4 }
}

#[cfg(target_os = "macos")]
#[cfg(feature = "macosx_10_9")]
/// Returns the information about resources of the process that match pid passed in.
///
/// # Examples
///
/// ```
/// use std::io::Write;
/// use libproc::libproc::pid_rusage::{pidrusage, RUsageInfoV2};
///
/// fn pidrusage_test() {
///     use std::process;
///     let pid = process::id() as i32;
///
/// #[cfg(target_os = "macos")]
///     if let Ok(res) = pidrusage::<RUsageInfoV2>(pid) {
///         println!("UUID: {:?}, Disk Read: {}, Disk Write: {}", res.ri_uuid, res.ri_diskio_bytesread, res.ri_diskio_byteswritten);
///     }
/// }
/// ```
pub fn pidrusage<T: PIDRUsage>(pid : i32) -> Result<T, String> {
    let flavor = T::flavor() as i32;
    let mut pidrusage = T::default();
    let buffer_ptr = &mut pidrusage as *mut _ as *mut c_void;
    let ret: i32;

    unsafe {
        ret = proc_pid_rusage(pid, flavor, buffer_ptr);
    };

    if ret < 0 {
        Err(helpers::get_errno_with_message(ret))
    } else {
        Ok(pidrusage)
    }
}

#[cfg(target_os = "linux")]
/// Returns the information about resources of the process that match pid passed in.
///
/// # Examples
///
/// ```
/// use std::io::Write;
/// use libproc::libproc::pid_rusage::{pidrusage, RUsageInfoV2};
///
/// fn pidrusage_test() {
///     use std::process;
///     let pid = process::id() as i32;
///
///     if let Ok(res) = pidrusage(pid) {
///         println!("VmSize (resident_size): {}", res.ri_resident_size );
///     }
/// }
/// ```
pub fn pidrusage(pid : i32) -> Result<RUsageInfoV0, String> {
    let mut pidrusage = RUsageInfoV0::default();
    let vmsize = procfile_field(&format!("/proc/{}/status", pid), "VmSize")?;
    pidrusage.ri_resident_size = parse_memory_string(&vmsize)?;

    Ok(pidrusage)
}

#[cfg(test)]
mod test {
    use super::pidrusage;
    use crate::libproc::pid_rusage::RUsageInfoV0;

    #[test]
    fn pidrusage_test() {
        let usage: RUsageInfoV0 = pidrusage(std::process::id() as i32).expect("pidrusage() failed");
        assert!(usage.ri_resident_size > 0, "Resident size reports 0")
    }
}<|endoftext|>


<commit_before>use jansson_sys::json_t;
use std::os::raw::{c_char, c_int, c_void};

#[repr(C)]
#[derive(Debug)]
pub struct janus_callbacks {
    pub push_event: extern "C" fn(
        handle: *mut janus_plugin_session,
        plugin: *mut janus_plugin,
        transaction: *const c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> c_int,
    pub relay_rtp: extern "C" fn(handle: *mut janus_plugin_session, video: c_int, buf: *mut c_char, len: c_int),
    pub relay_rtcp: extern "C" fn(handle: *mut janus_plugin_session, video: c_int, buf: *mut c_char, len: c_int),
    pub relay_data: extern "C" fn(handle: *mut janus_plugin_session, buf: *mut c_char, len: c_int),
    pub close_pc: extern "C" fn(handle: *mut janus_plugin_session),
    pub end_session: extern "C" fn(handle: *mut janus_plugin_session),
    pub events_is_enabled: extern "C" fn() -> c_int,
    pub notify_event: extern "C" fn(plugin: *mut janus_plugin, handle: *mut janus_plugin_session, event: *mut json_t),
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum janus_plugin_result_type {
    JANUS_PLUGIN_ERROR = -1,
    JANUS_PLUGIN_OK = 0,
    JANUS_PLUGIN_OK_WAIT = 1,
}

#[cfg(not(feature="refcount"))]
#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_session {
    pub gateway_handle: *mut c_void,
    pub plugin_handle: *mut c_void,
    pub stopped_bitfield: u8, // todo: clean this up
    pub __padding: [u8; 7usize],
}

#[cfg(feature="refcount")]
#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_session {
    pub gateway_handle: *mut c_void,
    pub plugin_handle: *mut c_void,
    pub stopped: c_int,
    pub ref_: ::janus_refcount,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_result {
    pub type_: janus_plugin_result_type,
    pub text: *const c_char,
    pub content: *mut json_t,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin {
    pub init: unsafe extern "C" fn(callback: *mut janus_callbacks, config_path: *const c_char) -> c_int,
    pub destroy: unsafe extern "C" fn(),
    pub get_api_compatibility: unsafe extern "C" fn() -> c_int,
    pub get_version: unsafe extern "C" fn() -> c_int,
    pub get_version_string: unsafe extern "C" fn() -> *const c_char,
    pub get_description: unsafe extern "C" fn() -> *const c_char,
    pub get_name: unsafe extern "C" fn() -> *const c_char,
    pub get_author: unsafe extern "C" fn() -> *const c_char,
    pub get_package: unsafe extern "C" fn() -> *const c_char,
    pub create_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub handle_message: unsafe extern "C" fn(
        handle: *mut janus_plugin_session,
        transaction: *mut c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> *mut janus_plugin_result,
    pub setup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub incoming_rtp: unsafe extern "C" fn(handle: *mut janus_plugin_session, video: c_int, buf: *mut c_char, len: c_int),
    pub incoming_rtcp: unsafe extern "C" fn(handle: *mut janus_plugin_session, video: c_int, buf: *mut c_char, len: c_int),
    pub incoming_data: unsafe extern "C" fn(handle: *mut janus_plugin_session, buf: *mut c_char, len: c_int),
    pub slow_link: unsafe extern "C" fn(handle: *mut janus_plugin_session, uplink: c_int, video: c_int),
    pub hangup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub destroy_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub query_session: unsafe extern "C" fn(handle: *mut janus_plugin_session) -> *mut json_t,
}

extern "C" {
    pub fn janus_plugin_result_new(type_: janus_plugin_result_type, text: *const c_char, content: *mut json_t) -> *mut janus_plugin_result;
    pub fn janus_plugin_result_destroy(result: *mut janus_plugin_result);
}
<commit_msg>Upgrade to plugin API v13<commit_after>use jansson_sys::json_t;
use std::os::raw::{c_char, c_int, c_void};

#[repr(C)]
#[derive(Debug)]
pub struct janus_callbacks {
    pub push_event: extern "C" fn(
        handle: *mut janus_plugin_session,
        plugin: *mut janus_plugin,
        transaction: *const c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> c_int,
    pub relay_rtp: extern "C" fn(handle: *mut janus_plugin_session, video: c_int, buf: *mut c_char, len: c_int),
    pub relay_rtcp: extern "C" fn(handle: *mut janus_plugin_session, video: c_int, buf: *mut c_char, len: c_int),
    pub relay_data: extern "C" fn(handle: *mut janus_plugin_session, buf: *mut c_char, len: c_int),
    pub close_pc: extern "C" fn(handle: *mut janus_plugin_session),
    pub end_session: extern "C" fn(handle: *mut janus_plugin_session),
    pub events_is_enabled: extern "C" fn() -> c_int,
    pub notify_event: extern "C" fn(plugin: *mut janus_plugin, handle: *mut janus_plugin_session, event: *mut json_t),
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum janus_plugin_result_type {
    JANUS_PLUGIN_ERROR = -1,
    JANUS_PLUGIN_OK = 0,
    JANUS_PLUGIN_OK_WAIT = 1,
}

#[cfg(not(feature="refcount"))]
#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_session {
    pub gateway_handle: *mut c_void,
    pub plugin_handle: *mut c_void,
    pub stopped_bitfield: u8, // todo: clean this up
    pub __padding: [u8; 7usize],
}

#[cfg(feature="refcount")]
#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_session {
    pub gateway_handle: *mut c_void,
    pub plugin_handle: *mut c_void,
    pub stopped: c_int,
    pub ref_: ::janus_refcount,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_result {
    pub type_: janus_plugin_result_type,
    pub text: *const c_char,
    pub content: *mut json_t,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin {
    pub init: unsafe extern "C" fn(callback: *mut janus_callbacks, config_path: *const c_char) -> c_int,
    pub destroy: unsafe extern "C" fn(),
    pub get_api_compatibility: unsafe extern "C" fn() -> c_int,
    pub get_version: unsafe extern "C" fn() -> c_int,
    pub get_version_string: unsafe extern "C" fn() -> *const c_char,
    pub get_description: unsafe extern "C" fn() -> *const c_char,
    pub get_name: unsafe extern "C" fn() -> *const c_char,
    pub get_author: unsafe extern "C" fn() -> *const c_char,
    pub get_package: unsafe extern "C" fn() -> *const c_char,
    pub create_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub handle_message: unsafe extern "C" fn(
        handle: *mut janus_plugin_session,
        transaction: *mut c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> *mut janus_plugin_result,
    pub handle_admin_message: unsafe extern "C" fn(message: *mut json_t) -> *mut json_t,
    pub setup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub incoming_rtp: unsafe extern "C" fn(handle: *mut janus_plugin_session, video: c_int, buf: *mut c_char, len: c_int),
    pub incoming_rtcp: unsafe extern "C" fn(handle: *mut janus_plugin_session, video: c_int, buf: *mut c_char, len: c_int),
    pub incoming_data: unsafe extern "C" fn(handle: *mut janus_plugin_session, buf: *mut c_char, len: c_int),
    pub slow_link: unsafe extern "C" fn(handle: *mut janus_plugin_session, uplink: c_int, video: c_int),
    pub hangup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub destroy_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub query_session: unsafe extern "C" fn(handle: *mut janus_plugin_session) -> *mut json_t,
}

extern "C" {
    pub fn janus_plugin_result_new(type_: janus_plugin_result_type, text: *const c_char, content: *mut json_t) -> *mut janus_plugin_result;
    pub fn janus_plugin_result_destroy(result: *mut janus_plugin_result);
}
<|endoftext|>


<commit_before>use std::mem;

use self::BufferedParserState::{Beginning, Middle, EndMatch, EndFail};
use self::MatchResult::{Undecided, Committed, Matched, Failed};
use self::ConstantParserState::{AtOffset, AtEnd};

// ----------- Types with lifetimes -------------

// Borrowing encoding of paramaterized types from
// https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#encoding-higher-kinded-types

pub trait TypeWithLifetime<'a> {
    type Type;
}

pub type At<'a,T> where T: TypeWithLifetime<'a> = T::Type;

pub struct Always<T> (T);

impl<'a,T> TypeWithLifetime<'a> for Always<T> {
    type Type = T;
}

pub type Unit = Always<()>;

// ----------- Types for consumers ------------

pub trait Consumer<T> where T: 'static+for<'a> TypeWithLifetime<'a> {
    fn accept<'a>(&mut self, arg: At<'a,T>);
}

struct DiscardConsumer;

impl Consumer<Unit> for DiscardConsumer {
    fn accept(&mut self, _: ()) {}
}

// ----------- Types for parsers ------------

// State machine transitions are:
//
// init -Undecided->  init
// init -Committed->  committed
// init -Matched(s)-> matched
// init -Failed(b)->  failed(b)
//
// committed -Committed->     committed
// committed -Matched(s)->    matched
// committed -Failed(false)-> failed(false)
//
// matched -Matched(s)-> matched
//
// failed(b) -Failed(b)-> failed(b)
//
// The Failed(b) action carries a boolean indicating if backtracking is allowed.
// Note that there is no transition . -Committed-> . -Failed(true)-> . so
// once a parser has committed, we can clean up space associated with backtracking.

#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub enum MatchResult<T> {
    Undecided,
    Committed,
    Matched(T),
    Failed(bool),
}

pub trait Parser<S,T> where S: 'static+for<'a> TypeWithLifetime<'a>, T: 'static+for<'a> TypeWithLifetime<'a> {
    // If push returns Undecided or Failed(true), it is side-effect-free
    // In the case where T is "list-like" (e.g. &str or &[T])
    // push(nil) is a no-op
    // push(a ++ b) is the same as push(a); push(b)
    fn push<'a>(&mut self, value: At<'a,S>, downstream: &mut Consumer<T>) -> MatchResult<At<'a,S>>;
    // Resets the parser state back to its initial state
    fn done(&mut self, downstream: &mut Consumer<T>);
}

pub trait BufferableMatcher<S,T> where S: 'static+for<'a> TypeWithLifetime<'a>, T: Parser<S,S> {
    fn buffer(self) -> T;
}

// ----------- Always commit ---------------

pub struct CommittedParser<P> {
    parser: P,
}

impl<S,T,P> Parser<S,T> for CommittedParser<P> where P: Parser<S,T>, S: 'static+for<'a> TypeWithLifetime<'a>, T: 'static+for<'a> TypeWithLifetime<'a>  {
    fn push<'a>(&mut self, value: At<'a,S>, downstream: &mut Consumer<T>) -> MatchResult<At<'a,S>> {
        match self.parser.push(value, downstream) {
            Undecided     => Committed,
            Committed     => Committed,
            Matched(rest) => Matched(rest),
            Failed(_)     => Failed(false),
        }
    }
    fn done(&mut self, downstream: &mut Consumer<T>) {
        self.parser.done(downstream)
    }
}

// ----------- Sequencing ---------------

pub struct AndThenParser<L,R> {
    lhs: L,
    rhs: CommittedParser<R>,
    in_lhs: bool,
}

impl<S,T,L,R> Parser<S,T> for AndThenParser<L,R> where L: Parser<S,T>, R: Parser<S,T>, S: 'static+for<'a> TypeWithLifetime<'a>, T: 'static+for<'a> TypeWithLifetime<'a>  {
    fn push<'a>(&mut self, value: At<'a,S>, downstream: &mut Consumer<T>) -> MatchResult<At<'a,S>> {
        if self.in_lhs {
            match self.lhs.push(value, downstream) {
                Undecided     => Undecided,
                Committed     => Committed,
                Matched(rest) => { self.in_lhs = false; self.rhs.push(rest, downstream) },
                Failed(b)     => Failed(b),
            }
        } else {
            self.rhs.push(value, downstream)
        }
    }
    fn done(&mut self, downstream: &mut Consumer<T>) {
        self.lhs.done(downstream);
        self.rhs.done(downstream)
    }
}

// ----------- Matching strings -------------

pub struct Str;

impl<'a> TypeWithLifetime<'a> for Str {
    type Type = &'a str;
}

// ----------- Constant parsers -------------

pub enum ConstantParserState {
    AtOffset(usize),
    AtEnd(bool),
}

pub struct ConstantParser {
    constant: String,
    state: ConstantParserState,
}

impl Parser<Str,Unit> for ConstantParser {
    fn push<'a>(&mut self, string: &'a str, downstream: &mut Consumer<Unit>) -> MatchResult<&'a str> {
        match self.state {
            AtOffset(index) if string == &self.constant[index..]           => { downstream.accept(()); self.state = AtEnd(true); Matched("") },
            AtOffset(index) if string.starts_with(&self.constant[index..]) => { downstream.accept(()); self.state = AtEnd(true); Matched(&string[index..]) },
            AtOffset(index) if self.constant[index..].starts_with(string)  => { self.state = AtOffset(index + string.len()); Undecided },
            AtOffset(_)                                                    => { self.state = AtEnd(false); Failed(true) },
            AtEnd(true)                                                    => { Matched(string) },
            AtEnd(false)                                                   => { Failed(true) },
        }
    }
    fn done(&mut self, _: &mut Consumer<Unit>) {
        self.state = AtOffset(0);
    }
}

pub fn constant(string: String) -> ConstantParser {
    ConstantParser{ constant: string, state: AtOffset(0) }
}

// If m is a Parser<Str,Unit> then m.buffer() is a Parser<Str,Str>.
// It does as little buffering as it can, but it does allocate as buffer for the case
// where the boundary marker of the input is misaligned with that of the parser.
// For example, m is matching string literals, and the input is '"abc' followed by 'def"'
// we have to buffer up '"abc'.

enum BufferedParserState {
    Beginning,
    Middle(String),
    EndMatch,
    EndFail(bool),
}

pub struct BufferedParser<P> {
    parser: P,
    state: BufferedParserState,
}

impl<P> Parser<Str,Str> for BufferedParser<P> where P: Parser<Str,Unit> {
    fn push<'a>(&mut self, string: &'a str, downstream: &mut Consumer<Str>) -> MatchResult<&'a str> {
        match mem::replace(&mut self.state, EndMatch) {
            Beginning => {
                let result = self.parser.push(string, &mut DiscardConsumer);
                match result {
                    Undecided     => self.state = Middle(String::from(string)),
                    Committed     => self.state = Middle(String::from(string)),
                    Failed(b)     => self.state = EndFail(b),
                    Matched(rest) => downstream.accept(&string[..(string.len()-rest.len())]),
                }
                result
            },
            Middle(mut buffer) => {
                let result = self.parser.push(string, &mut DiscardConsumer);
                match result {
                    Undecided     => { buffer.push_str(string); self.state = Middle(buffer); },
                    Committed     => { buffer.push_str(string); self.state = Middle(buffer); },
                    Failed(b)     => { self.state = EndFail(b); },
                    Matched(rest) => { buffer.push_str(&string[..(string.len()-rest.len())]); downstream.accept(&*buffer); },
                }
                result
            }
            EndMatch => Matched(string),
            EndFail(b) => Failed(b),
        }
    }
    fn done(&mut self, downstream: &mut Consumer<Str>) {
        self.parser.done(&mut DiscardConsumer);
        match mem::replace(&mut self.state, Beginning) {
            Middle(buffer) => downstream.accept(&*buffer),
            _              => (),
        }
    }
}
<commit_msg>The done method in a parser now returns a bool indicating whether there was a match.<commit_after>use std::mem;

use self::BufferedParserState::{Beginning, Middle, EndMatch, EndFail};
use self::MatchResult::{Undecided, Committed, Matched, Failed};
use self::ConstantParserState::{AtOffset, AtEnd};

// ----------- Types with lifetimes -------------

// Borrowing encoding of paramaterized types from
// https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#encoding-higher-kinded-types

pub trait TypeWithLifetime<'a> {
    type Type;
}

pub type At<'a,T> where T: TypeWithLifetime<'a> = T::Type;

pub struct Always<T> (T);

impl<'a,T> TypeWithLifetime<'a> for Always<T> {
    type Type = T;
}

pub type Unit = Always<()>;

// ----------- Types for consumers ------------

pub trait Consumer<T> where T: 'static+for<'a> TypeWithLifetime<'a> {
    fn accept<'a>(&mut self, arg: At<'a,T>);
}

struct DiscardConsumer;

impl Consumer<Unit> for DiscardConsumer {
    fn accept(&mut self, _: ()) {}
}

// ----------- Types for parsers ------------

// State machine transitions are:
//
// init -Undecided->  init
// init -Committed->  committed
// init -Matched(s)-> matched
// init -Failed(b)->  failed(b)
//
// committed -Committed->     committed
// committed -Matched(s)->    matched
// committed -Failed(false)-> failed(false)
//
// matched -Matched(s)-> matched
//
// failed(b) -Failed(b)-> failed(b)
//
// The Failed(b) action carries a boolean indicating if backtracking is allowed.
// Note that there is no transition . -Committed-> . -Failed(true)-> . so
// once a parser has committed, we can clean up space associated with backtracking.

#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub enum MatchResult<T> {
    Undecided,
    Committed,
    Matched(T),
    Failed(bool),
}

pub trait Parser<S,T> where S: 'static+for<'a> TypeWithLifetime<'a>, T: 'static+for<'a> TypeWithLifetime<'a> {
    // If push returns Undecided or Failed(true), it is side-effect-free
    // In the case where T is "list-like" (e.g. &str or &[T])
    // push(nil) is a no-op
    // push(a ++ b) is the same as push(a); push(b)
    fn push<'a>(&mut self, value: At<'a,S>, downstream: &mut Consumer<T>) -> MatchResult<At<'a,S>>;
    // Resets the parser state back to its initial state
    // Returns true if there was a match.
    fn done(&mut self, downstream: &mut Consumer<T>) -> bool;
}

pub trait BufferableMatcher<S,T> where S: 'static+for<'a> TypeWithLifetime<'a>, T: Parser<S,S> {
    fn buffer(self) -> T;
}

// ----------- Always commit ---------------

pub struct CommittedParser<P> {
    parser: P,
}

impl<S,T,P> Parser<S,T> for CommittedParser<P> where P: Parser<S,T>, S: 'static+for<'a> TypeWithLifetime<'a>, T: 'static+for<'a> TypeWithLifetime<'a>  {
    fn push<'a>(&mut self, value: At<'a,S>, downstream: &mut Consumer<T>) -> MatchResult<At<'a,S>> {
        match self.parser.push(value, downstream) {
            Undecided     => Committed,
            Committed     => Committed,
            Matched(rest) => Matched(rest),
            Failed(_)     => Failed(false),
        }
    }
    fn done(&mut self, downstream: &mut Consumer<T>) -> bool {
        self.parser.done(downstream)
    }
}

// ----------- Sequencing ---------------

pub struct AndThenParser<L,R> {
    lhs: L,
    rhs: CommittedParser<R>,
    in_lhs: bool,
}

impl<S,T,L,R> Parser<S,T> for AndThenParser<L,R> where L: Parser<S,T>, R: Parser<S,T>, S: 'static+for<'a> TypeWithLifetime<'a>, T: 'static+for<'a> TypeWithLifetime<'a>  {
    fn push<'a>(&mut self, value: At<'a,S>, downstream: &mut Consumer<T>) -> MatchResult<At<'a,S>> {
        if self.in_lhs {
            match self.lhs.push(value, downstream) {
                Undecided     => Undecided,
                Committed     => Committed,
                Matched(rest) => { self.in_lhs = false; self.rhs.push(rest, downstream) },
                Failed(b)     => Failed(b),
            }
        } else {
            self.rhs.push(value, downstream)
        }
    }
    fn done(&mut self, downstream: &mut Consumer<T>) -> bool {
        self.lhs.done(downstream) && self.rhs.done(downstream)
    }
}

// ----------- Matching strings -------------

pub struct Str;

impl<'a> TypeWithLifetime<'a> for Str {
    type Type = &'a str;
}

// ----------- Constant parsers -------------

#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub enum ConstantParserState {
    AtOffset(usize),
    AtEnd(bool),
}

pub struct ConstantParser {
    constant: String,
    state: ConstantParserState,
}

impl Parser<Str,Unit> for ConstantParser {
    fn push<'a>(&mut self, string: &'a str, downstream: &mut Consumer<Unit>) -> MatchResult<&'a str> {
        match self.state {
            AtOffset(index) if string == &self.constant[index..]           => { downstream.accept(()); self.state = AtEnd(true); Matched("") },
            AtOffset(index) if string.starts_with(&self.constant[index..]) => { downstream.accept(()); self.state = AtEnd(true); Matched(&string[index..]) },
            AtOffset(index) if self.constant[index..].starts_with(string)  => { self.state = AtOffset(index + string.len()); Undecided },
            AtOffset(_)                                                    => { self.state = AtEnd(false); Failed(true) },
            AtEnd(true)                                                    => { Matched(string) },
            AtEnd(false)                                                   => { Failed(true) },
        }
    }
    fn done(&mut self, _: &mut Consumer<Unit>) -> bool {
        let result = self.state == AtEnd(true);
        self.state = AtOffset(0);
        result
    }
}

pub fn constant(string: String) -> ConstantParser {
    ConstantParser{ constant: string, state: AtOffset(0) }
}

// If m is a Parser<Str,Unit> then m.buffer() is a Parser<Str,Str>.
// It does as little buffering as it can, but it does allocate as buffer for the case
// where the boundary marker of the input is misaligned with that of the parser.
// For example, m is matching string literals, and the input is '"abc' followed by 'def"'
// we have to buffer up '"abc'.

#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
enum BufferedParserState {
    Beginning,
    Middle(String),
    EndMatch,
    EndFail(bool),
}

pub struct BufferedParser<P> {
    parser: P,
    state: BufferedParserState,
}

impl<P> Parser<Str,Str> for BufferedParser<P> where P: Parser<Str,Unit> {
    fn push<'a>(&mut self, string: &'a str, downstream: &mut Consumer<Str>) -> MatchResult<&'a str> {
        match mem::replace(&mut self.state, EndMatch) {
            Beginning => {
                let result = self.parser.push(string, &mut DiscardConsumer);
                match result {
                    Undecided     => self.state = Middle(String::from(string)),
                    Committed     => self.state = Middle(String::from(string)),
                    Failed(b)     => self.state = EndFail(b),
                    Matched(rest) => downstream.accept(&string[..(string.len()-rest.len())]),
                }
                result
            },
            Middle(mut buffer) => {
                let result = self.parser.push(string, &mut DiscardConsumer);
                match result {
                    Undecided     => { buffer.push_str(string); self.state = Middle(buffer); },
                    Committed     => { buffer.push_str(string); self.state = Middle(buffer); },
                    Failed(b)     => { self.state = EndFail(b); },
                    Matched(rest) => { buffer.push_str(&string[..(string.len()-rest.len())]); downstream.accept(&*buffer); },
                }
                result
            }
            EndMatch => Matched(string),
            EndFail(b) => Failed(b),
        }
    }
    fn done(&mut self, downstream: &mut Consumer<Str>) -> bool {
        let result = self.parser.done(&mut DiscardConsumer);
        if result { if let Middle(ref buffer) = self.state { downstream.accept(&*buffer) } }
        self.state = Beginning;
        result
    }
}
<|endoftext|>


<commit_before>extern crate uuid;
use coin_cbc;

use dsl::LpExpression::*;
use dsl::*;
use solvers::{Solution, SolverTrait, Status, WithMaxSeconds, WithNbThreads};
use std::collections::HashMap;

/// Solver that calls cbc through [rust bindings](https://github.com/KardinalAI/coin_cbc)
#[derive(Debug, Clone, Default)]
pub struct NativeCbcSolver {
    name: String,
    threads: Option<u32>,
    seconds: Option<u32>,
}

impl NativeCbcSolver {
    pub fn new() -> NativeCbcSolver {
        NativeCbcSolver {
            name: "CbcNative".to_string(),
            threads: None,
            seconds: None,
        }
    }
}

impl WithMaxSeconds<NativeCbcSolver> for NativeCbcSolver {
    fn max_seconds(&self) -> Option<u32> {
        self.seconds
    }
    fn with_max_seconds(&self, seconds: u32) -> NativeCbcSolver {
        NativeCbcSolver {
            seconds: Some(seconds),
            ..self.clone()
        }
    }
}
impl WithNbThreads<NativeCbcSolver> for NativeCbcSolver {
    fn nb_threads(&self) -> Option<u32> {
        self.threads
    }
    fn with_nb_threads(&self, threads: u32) -> NativeCbcSolver {
        NativeCbcSolver {
            threads: Some(threads),
            ..self.clone()
        }
    }
}

/// Recursively unwrap an expression in a Vec of variables and literals.
fn var_lit(expr: &LpExpression, lst: &mut Vec<(String, f32)>, mul: Option<f32>) {
    let mul = match mul {
        Some(lit) => lit,
        None => 1.,
    };
    match expr {
        &ConsBin(LpBinary { ref name, .. })
        | &ConsInt(LpInteger { ref name, .. })
        | &ConsCont(LpContinuous { ref name, .. }) => {
            let coeff = split_constant_and_expr(expr).0;
            let coeff = if coeff == 0. { mul } else { mul * coeff };
            lst.push((name.clone(), coeff));
        }

        MulExpr(val, ref e) => match **e {
            ConsBin(LpBinary { ref name, .. })
            | ConsInt(LpInteger { ref name, .. })
            | ConsCont(LpContinuous { ref name, .. }) => {
                if let LitVal(lit) = *val.clone() {
                    lst.push((name.clone(), mul * lit))
                }
            }
            MulExpr(..) | AddExpr(..) | SubExpr(..) => {
                let next_mul = match *val.clone() {
                    LitVal(lit) => Some(lit * mul),
                    _ => None,
                };
                var_lit(&*e, lst, next_mul)
            }
            _ => (),
        },
        &AddExpr(ref e1, ref e2) => {
            var_lit(&*e1, lst, None);
            var_lit(&*e2, lst, None);
        }
        &SubExpr(ref e1, ref e2) => {
            var_lit(&*e1, lst, None);
            var_lit(&*e2, lst, Some(-1.));
        }
        _ => (),
    }
}

fn always_literal(expr: &LpExpression) -> f64 {
    match *expr {
        LitVal(num) => num as f64,
        _ => panic!("wrong generalization"),
    }
}

fn add_variable(m: &mut coin_cbc::Model, expr: &LpExpression) -> coin_cbc::Col {
    match expr {
        ConsInt(LpInteger {
            name: _,
            lower_bound,
            upper_bound,
        }) => {
            let col = m.add_integer();
            if let Some(lb) = lower_bound {
                m.set_col_lower(col, *lb as f64)
            }
            if let Some(ub) = upper_bound {
                m.set_col_upper(col, *ub as f64)
            }
            col
        }
        ConsCont(LpContinuous {
            name: _,
            lower_bound,
            upper_bound,
        }) => {
            let col = m.add_col();
            if let Some(lb) = lower_bound {
                m.set_col_lower(col, *lb as f64)
            }
            if let Some(ub) = upper_bound {
                m.set_col_upper(col, *ub as f64)
            }
            col
        }
        ConsBin(_) => m.add_binary(),
        _ => panic!("Unexpected LpExpression on LpProblem.variables()!"),
    }
}

impl SolverTrait for NativeCbcSolver {
    type P = LpProblem;

    fn run<'a>(&self, problem: &'a Self::P) -> Result<Solution<'a>, String> {
        let mut m = coin_cbc::Model::default();
        // columns (variables)
        let cols: HashMap<String, coin_cbc::Col> = problem
            .variables()
            .iter()
            .map(|(name, expr)| (name.clone(), add_variable(&mut m, expr)))
            .collect();
        // rows (constraints)
        problem.constraints.iter().for_each(|cons| {
            let row = m.add_row();
            let general = cons.generalize();
            match general.1 {
                Constraint::GreaterOrEqual => m.set_row_lower(row, always_literal(&general.2)),
                Constraint::LessOrEqual => m.set_row_upper(row, always_literal(&general.2)),
                Constraint::Equal => m.set_row_equal(row, always_literal(&general.2)),
            }
            let mut lst: Vec<_> = Vec::new();
            var_lit(&general.0, &mut lst, None);
            lst.iter()
                .for_each(|(n, lit)| m.set_weight(row, cols[n], *lit as f64));
        });
        // objective
        if let Some(objective) = &problem.obj_expr {
            let mut lst: Vec<_> = Vec::new();
            var_lit(&objective, &mut lst, None);
            lst.iter().for_each(|(n, lit)| m.set_obj_coeff(cols[n], *lit as f64))
        }
        m.set_obj_sense(match problem.objective_type {
            LpObjective::Maximize => coin_cbc::Sense::Maximize,
            LpObjective::Minimize => coin_cbc::Sense::Minimize,
        });

        let sol = m.solve();

        Ok(Solution {
            status: match sol.raw().status() {
                coin_cbc::raw::Status::Finished => Status::Optimal,
                coin_cbc::raw::Status::Abandoned => Status::Infeasible,
                _ => Status::NotSolved,
            },
            results: cols
                .iter()
                .map(|(name, col)| (name.to_owned(), sol.col(*col) as f32))
                .collect(),
            related_problem: Some(problem),
        })
    }
}
<commit_msg>Apply rustfmt<commit_after>extern crate uuid;
use coin_cbc;

use dsl::LpExpression::*;
use dsl::*;
use solvers::{Solution, SolverTrait, Status, WithMaxSeconds, WithNbThreads};
use std::collections::HashMap;

/// Solver that calls cbc through [rust bindings](https://github.com/KardinalAI/coin_cbc)
#[derive(Debug, Clone, Default)]
pub struct NativeCbcSolver {
    name: String,
    threads: Option<u32>,
    seconds: Option<u32>,
}

impl NativeCbcSolver {
    pub fn new() -> NativeCbcSolver {
        NativeCbcSolver {
            name: "CbcNative".to_string(),
            threads: None,
            seconds: None,
        }
    }
}

impl WithMaxSeconds<NativeCbcSolver> for NativeCbcSolver {
    fn max_seconds(&self) -> Option<u32> {
        self.seconds
    }
    fn with_max_seconds(&self, seconds: u32) -> NativeCbcSolver {
        NativeCbcSolver {
            seconds: Some(seconds),
            ..self.clone()
        }
    }
}
impl WithNbThreads<NativeCbcSolver> for NativeCbcSolver {
    fn nb_threads(&self) -> Option<u32> {
        self.threads
    }
    fn with_nb_threads(&self, threads: u32) -> NativeCbcSolver {
        NativeCbcSolver {
            threads: Some(threads),
            ..self.clone()
        }
    }
}

/// Recursively unwrap an expression in a Vec of variables and literals.
fn var_lit(expr: &LpExpression, lst: &mut Vec<(String, f32)>, mul: Option<f32>) {
    let mul = match mul {
        Some(lit) => lit,
        None => 1.,
    };
    match expr {
        &ConsBin(LpBinary { ref name, .. })
        | &ConsInt(LpInteger { ref name, .. })
        | &ConsCont(LpContinuous { ref name, .. }) => {
            let coeff = split_constant_and_expr(expr).0;
            let coeff = if coeff == 0. { mul } else { mul * coeff };
            lst.push((name.clone(), coeff));
        }

        MulExpr(val, ref e) => match **e {
            ConsBin(LpBinary { ref name, .. })
            | ConsInt(LpInteger { ref name, .. })
            | ConsCont(LpContinuous { ref name, .. }) => {
                if let LitVal(lit) = *val.clone() {
                    lst.push((name.clone(), mul * lit))
                }
            }
            MulExpr(..) | AddExpr(..) | SubExpr(..) => {
                let next_mul = match *val.clone() {
                    LitVal(lit) => Some(lit * mul),
                    _ => None,
                };
                var_lit(&*e, lst, next_mul)
            }
            _ => (),
        },
        &AddExpr(ref e1, ref e2) => {
            var_lit(&*e1, lst, None);
            var_lit(&*e2, lst, None);
        }
        &SubExpr(ref e1, ref e2) => {
            var_lit(&*e1, lst, None);
            var_lit(&*e2, lst, Some(-1.));
        }
        _ => (),
    }
}

fn always_literal(expr: &LpExpression) -> f64 {
    match *expr {
        LitVal(num) => num as f64,
        _ => panic!("wrong generalization"),
    }
}

fn add_variable(m: &mut coin_cbc::Model, expr: &LpExpression) -> coin_cbc::Col {
    match expr {
        ConsInt(LpInteger {
            name: _,
            lower_bound,
            upper_bound,
        }) => {
            let col = m.add_integer();
            if let Some(lb) = lower_bound {
                m.set_col_lower(col, *lb as f64)
            }
            if let Some(ub) = upper_bound {
                m.set_col_upper(col, *ub as f64)
            }
            col
        }
        ConsCont(LpContinuous {
            name: _,
            lower_bound,
            upper_bound,
        }) => {
            let col = m.add_col();
            if let Some(lb) = lower_bound {
                m.set_col_lower(col, *lb as f64)
            }
            if let Some(ub) = upper_bound {
                m.set_col_upper(col, *ub as f64)
            }
            col
        }
        ConsBin(_) => m.add_binary(),
        _ => panic!("Unexpected LpExpression on LpProblem.variables()!"),
    }
}

impl SolverTrait for NativeCbcSolver {
    type P = LpProblem;

    fn run<'a>(&self, problem: &'a Self::P) -> Result<Solution<'a>, String> {
        let mut m = coin_cbc::Model::default();
        // columns (variables)
        let cols: HashMap<String, coin_cbc::Col> = problem
            .variables()
            .iter()
            .map(|(name, expr)| (name.clone(), add_variable(&mut m, expr)))
            .collect();
        // rows (constraints)
        problem.constraints.iter().for_each(|cons| {
            let row = m.add_row();
            let general = cons.generalize();
            match general.1 {
                Constraint::GreaterOrEqual => m.set_row_lower(row, always_literal(&general.2)),
                Constraint::LessOrEqual => m.set_row_upper(row, always_literal(&general.2)),
                Constraint::Equal => m.set_row_equal(row, always_literal(&general.2)),
            }
            let mut lst: Vec<_> = Vec::new();
            var_lit(&general.0, &mut lst, None);
            lst.iter()
                .for_each(|(n, lit)| m.set_weight(row, cols[n], *lit as f64));
        });
        // objective
        if let Some(objective) = &problem.obj_expr {
            let mut lst: Vec<_> = Vec::new();
            var_lit(&objective, &mut lst, None);
            lst.iter()
                .for_each(|(n, lit)| m.set_obj_coeff(cols[n], *lit as f64))
        }
        m.set_obj_sense(match problem.objective_type {
            LpObjective::Maximize => coin_cbc::Sense::Maximize,
            LpObjective::Minimize => coin_cbc::Sense::Minimize,
        });

        let sol = m.solve();

        Ok(Solution {
            status: match sol.raw().status() {
                coin_cbc::raw::Status::Finished => Status::Optimal,
                coin_cbc::raw::Status::Abandoned => Status::Infeasible,
                _ => Status::NotSolved,
            },
            results: cols
                .iter()
                .map(|(name, col)| (name.to_owned(), sol.col(*col) as f32))
                .collect(),
            related_problem: Some(problem),
        })
    }
}
<|endoftext|>


<commit_before>use memory::Memory;
use std::vec::Vec;
use std::rc::Rc;
use std::ops::Deref;
use std::cell::{Ref, RefMut, RefCell};

/// Maps multiple `Memory`s into a single address space.
///
/// The `MappedMemory` allows to unify multiple `Memory`s in one address space.
/// A `Memory` is mounted at a certain address and is from now on responsible
/// for all reads and writes between that starting at that address (the “mount
/// point”) and ending where the mounted memory ends.
/// The read and write calls for the responsible `Memory` will be given an
/// address _relative_ to the mount point.
///
/// ```text
///                   Unmapped
///                <-------->
///       mem_a                   mem_b
/// |--------------|            |------|
///                            NullMemory
///                         |--------------
/// |------------- mapped_mem ----------->
/// |              |            |      |
/// 0            0x100        0x200  0x280
/// ```
///
/// For example: `mapped_mem.get(0x208)` would yield the same value as
/// `mem_b.get(0x008)`.
///
/// # Examples
///
/// ```
/// use osciemu::memory::{Memory, SliceMemory, MappedMemory};
/// use osciemu::memory::mappedmemory::MemoryToken;
///
/// let mut mm = MappedMemory::new();
/// let m1 = MemoryToken::new(SliceMemory::from_slice(Box::new([1])));
/// let m2 = MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
/// mm.mount(0, &m1);
/// mm.mount(8, &m2);
/// // Now mm =~ [1, _, 2, 2]
/// assert_eq!(mm.get(0), 1);
/// assert_eq!(mm.get(12), 2);
/// ```
///
/// The `MemoryToken`s can be used to access the `Memory` even if it has been
/// mounted. They work like `std::cell::RefCell`.
///
/// ```
/// use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// use osciemu::memory::mappedmemory::MemoryToken;
///
/// let mut mm = MappedMemory::new();
/// let m1 = MemoryToken::new(NullMemory::new());
/// mm.mount(0, &m1);
/// let m2 = MemoryToken::new(SliceMemory::from_slice(Box::new(4, [1, 2, 3, 4])));
/// mm.mount(0, &m2);
/// mm.set(0, 99);
/// assert_eq!(mm.get(0), 99);
/// assert_eq!(m2.borrow().get(0), 99);
/// ```
///
/// The `MemoryToken`s are also used to unmount a mounted `Memory`.
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = MemoryToken::new(NullMemory::new());
/// # mm.mount(0, &m1);
/// # let m2 = MemoryToken::new(SliceMemory::from_slice(Box::new(4, [1, 2, 3, 4])));
/// # mm.mount(0, &m2);
/// # mm.set(0, 99);
/// # assert_eq!(mm.get(0), 99);
/// # assert_eq!(m2.borrow().get(0), 99);
/// // ...
/// mm.unmount(&m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(m2.borrow().get(0), 99);
/// ```
///
/// # Panics
/// `MappedMemory` panics when an unmapped address is read or written.
pub struct MappedMemory<'a>(Vec<Entry<'a>>);

struct Entry<'a> {
    start_address: usize,
    size: usize,
    memory: Rc<RefCell<Memory + 'a>>,
}

/// Represents a mountable, shared `Memory`.
#[derive(Clone)]
pub struct MemoryToken<T: Memory> {
    memory: Rc<RefCell<T>>,
}

impl<T: Memory> MemoryToken<T> {
    pub fn new(memory: T) -> MemoryToken<T> {
        MemoryToken { memory: Rc::new(RefCell::new(memory)) }
    }

    /// Borrows a reference to the mounted memory until the `Ref` is destroyed.
    /// See `std::cell::RefCell::borrow()`
    pub fn borrow(&self) -> Ref<T> {
        self.memory.borrow()
    }

    /// Borrows a mutable reference to the mounted memory until the `RefMut`
    /// is destroyed.
    /// See `std::cell::RefCell::borrow_mut()`
    pub fn borrow_mut(&self) -> RefMut<T> {
        self.memory.borrow_mut()
    }
}

impl<'a> MappedMemory<'a> {
    pub fn new() -> MappedMemory<'a> {
        MappedMemory(Vec::new())
    }

    /// Mounts a `Memory` at the given address.
    ///
    /// # Panics
    /// `mount` panics if a mount is not on a word boundary.
    pub fn mount<T>(&mut self, start: usize, memory: &MemoryToken<T>)
        where T: Memory + 'a
    {
        assert!(start % 4 == 0, "Mount needs to be on a word boundary");
        let size = memory.borrow().size();
        let new_entry = Entry {
            start_address: start,
            size: size,
            memory: memory.memory.clone(),
        };
        self.0.push(new_entry);
    }

    /// Unmounts the `Memory` references by the `MemoryToken`. After unmounting,
    /// `MappedMemory` does not hold any references to the `Memory`. If the
    /// `Memory` has already been unmounted, calling `unmount` is a no-op.
    pub fn unmount<T>(&mut self, mount_token: &MemoryToken<T>)
        where T: Memory + 'a
    {
        self.0
            .iter()
            .enumerate()
            .find(|&(_, entry)| rc_ptr_eq(&mount_token.memory, &entry.memory))
            .map(|(idx, _)| idx)
            .map(|idx| self.0.remove(idx));
    }

    fn memory_at_addr(&self, addr: usize) -> Option<&Entry<'a>> {
        self.0
            .iter()
            .rev()
            .find(|entry| entry.start_address <= addr && entry.start_address + entry.size > addr)
    }
}

impl<'a> Memory for MappedMemory<'a> {
    fn get(&self, addr: usize) -> u32 {
        self.memory_at_addr(addr)
            .map(|entry| entry.memory.borrow().get(addr - entry.start_address))
            .expect("Out of bounds")
    }

    fn set(&mut self, addr: usize, value: u32) {
        self.memory_at_addr(addr)
            .map(|entry| entry.memory.borrow_mut().set(addr - entry.start_address, value))
            .expect("Out of bounds")
    }

    fn size(&self) -> usize {
        self.0
            .iter()
            .map(|entry| entry.start_address + entry.size)
            .max()
            .unwrap_or(0)
    }
}

// Helper until feature "ptr_eq" is stabilized.
// See https://github.com/rust-lang/rust/issues/36497
fn rc_ptr_eq<T: ?Sized, U: ?Sized>(this: &Rc<T>, other: &Rc<U>) -> bool {
    let this_ptr = this.deref() as *const T as *const () as usize;
    let other_ptr = other.deref() as *const U as *const () as usize;
    this_ptr == other_ptr
}

#[cfg(test)]
mod tests {
    use memory::{Memory, SliceMemory, NullMemory};

    #[test]
    fn memory_at_addr() {
        let m1 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
        let mut mm = super::MappedMemory::new();
        mm.mount(0, &m1);
        mm.mount(8, &m2);

        assert!(mm.memory_at_addr(0)
            .map_or(false, |entry| entry.memory.borrow().get(0) == 1));
        assert!(mm.memory_at_addr(4).is_none());
        assert!(mm.memory_at_addr(8)
            .map_or(false, |entry| entry.memory.borrow().get(0) == 2));
        assert!(mm.memory_at_addr(12)
            .map_or(false, |entry| entry.memory.borrow().get(0) == 2));
        assert!(mm.memory_at_addr(16).is_none());
    }

    #[test]
    fn overlapping_mounts() {
        let m1 = super::MemoryToken::new(NullMemory::new());
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
        let m3 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([3])));
        let mut mm = super::MappedMemory::new();
        mm.mount(0, &m1);
        mm.mount(4, &m2);
        mm.mount(8, &m3);
        assert_eq!(mm.get(0), 0);
        assert_eq!(mm.get(4), 2);
        assert_eq!(mm.get(8), 3);
        assert_eq!(mm.get(12), 0);
    }

    #[test]
    fn get_and_set() {
        let m1 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
        let mut mm = super::MappedMemory::new();

        mm.mount(0, &m1);
        mm.mount(8, &m2);
        assert_eq!(mm.get(0), 1);
        assert_eq!(mm.get(8), 2);

        mm.set(0, 3);
        assert_eq!(mm.get(0), 3);

        mm.set(12, 0);
        assert_eq!(mm.get(12), 0);
        assert_eq!(m1.borrow().get(0), 3);
        assert_eq!(m2.borrow().get(4), 0);
    }

    #[test]
    fn size() {
        let m1 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
        let mut mm = super::MappedMemory::new();
        assert_eq!(mm.size(), 0);

        mm.mount(0, &m1);
        assert_eq!(mm.size(), 4);

        mm.mount(8, &m2);
        assert_eq!(mm.size(), 16);
    }

    #[test]
    fn size_with_overlap() {
        let m1 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1])));
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
        let m3 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([3, 3, 3])));
        let mut mm = super::MappedMemory::new();

        mm.mount(0, &m1);
        mm.mount(8, &m2);
        assert_eq!(mm.size(), 20);

        mm.mount(24, &m3);
        assert_eq!(mm.size(), 36);
    }

    #[test]
    fn unmount() {
        let m1 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1])));
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2])));
        let m3 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([3, 3, 3, 3, 3])));
        let mut mm = super::MappedMemory::new();
        mm.mount(0, &m1);
        mm.mount(0, &m2);
        mm.mount(0, &m3);

        for i in 0..5 {
            assert_eq!(mm.get(i << 2), 3);
        }
        mm.unmount(&m3);
        for i in 0..5 {
            assert_eq!(mm.get(i << 2), 2);
        }
        mm.unmount(&m3);
        mm.unmount(&m3);
        for i in 0..5 {
            assert_eq!(mm.get(i << 2), 2);
        }
        mm.unmount(&m1);
        for i in 0..5 {
            assert_eq!(mm.get(i << 2), 2);
        }
    }
}
<commit_msg>refactor(mappedmemory): Update for new memory design<commit_after>use memory::Memory;
use std::vec::Vec;
use std::rc::Rc;
use std::ops::Deref;
use std::cell::{Ref, RefMut, RefCell};

/// Maps multiple `Memory`s into a single address space.
///
/// The `MappedMemory` allows to unify multiple `Memory`s in one address space.
/// A `Memory` is mounted at a certain address and is from now on responsible
/// for all reads and writes between that starting at that address (the “mount
/// point”) and ending where the mounted memory ends.
/// The read and write calls for the responsible `Memory` will be given an
/// address _relative_ to the mount point.
///
/// ```text
///                   Unmapped
///                <-------->
///       mem_a                   mem_b
/// |--------------|            |------|
///                            NullMemory
///                         |--------------
/// |------------- mapped_mem ----------->
/// |              |            |      |
/// 0            0x100        0x200  0x280
/// ```
///
/// For example: `mapped_mem.get(0x208)` would yield the same value as
/// `mem_b.get(0x008)`.
///
/// # Examples
///
/// ```
/// use osciemu::memory::{Memory, SliceMemory, MappedMemory};
/// use osciemu::memory::mappedmemory::MemoryToken;
///
/// let mut mm = MappedMemory::new();
/// let m1 = MemoryToken::new(SliceMemory::from_slice(Box::new([1])));
/// let m2 = MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
/// mm.mount(0, &m1);
/// mm.mount(2, &m2);
/// // Now mm =~ [1, _, 2, 2]
/// assert_eq!(mm.get(0), 1);
/// assert_eq!(mm.get(3), 2);
/// ```
///
/// The `MemoryToken`s can be used to access the `Memory` even if it has been
/// mounted. They work like `std::cell::RefCell`.
///
/// ```
/// use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// use osciemu::memory::mappedmemory::MemoryToken;
///
/// let mut mm = MappedMemory::new();
/// let m1 = MemoryToken::new(NullMemory::new());
/// mm.mount(0, &m1);
/// let m2 = MemoryToken::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4])));
/// mm.mount(0, &m2);
/// mm.set(0, 99);
/// assert_eq!(mm.get(0), 99);
/// assert_eq!(m2.borrow().get(0), 99);
/// ```
///
/// The `MemoryToken`s are also used to unmount a mounted `Memory`.
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = MemoryToken::new(NullMemory::new());
/// # mm.mount(0, &m1);
/// # let m2 = MemoryToken::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4])));
/// # mm.mount(0, &m2);
/// # mm.set(0, 99);
/// # assert_eq!(mm.get(0), 99);
/// # assert_eq!(m2.borrow().get(0), 99);
/// // ...
/// mm.unmount(&m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(m2.borrow().get(0), 99);
/// ```
///
/// # Panics
/// `MappedMemory` panics when an unmapped address is read or written.
pub struct MappedMemory<'a>(Vec<Entry<'a>>);

struct Entry<'a> {
    start_address: usize,
    size: usize,
    memory: Rc<RefCell<Memory + 'a>>,
}

/// Represents a mountable, shared `Memory`.
#[derive(Clone)]
pub struct MemoryToken<T: Memory> {
    memory: Rc<RefCell<T>>,
}

impl<T: Memory> MemoryToken<T> {
    pub fn new(memory: T) -> MemoryToken<T> {
        MemoryToken { memory: Rc::new(RefCell::new(memory)) }
    }

    /// Borrows a reference to the mounted memory until the `Ref` is destroyed.
    /// See `std::cell::RefCell::borrow()`
    pub fn borrow(&self) -> Ref<T> {
        self.memory.borrow()
    }

    /// Borrows a mutable reference to the mounted memory until the `RefMut`
    /// is destroyed.
    /// See `std::cell::RefCell::borrow_mut()`
    pub fn borrow_mut(&self) -> RefMut<T> {
        self.memory.borrow_mut()
    }
}

impl<'a> MappedMemory<'a> {
    pub fn new() -> MappedMemory<'a> {
        MappedMemory(Vec::new())
    }

    /// Mounts a `Memory` at the given address.
    ///
    /// # Panics
    /// `mount` panics if a mount is not on a word boundary.
    pub fn mount<T>(&mut self, start: usize, memory: &MemoryToken<T>)
        where T: Memory + 'a
    {
        let size = memory.borrow().size();
        let new_entry = Entry {
            start_address: start,
            size: size,
            memory: memory.memory.clone(),
        };
        self.0.push(new_entry);
    }

    /// Unmounts the `Memory` references by the `MemoryToken`. After unmounting,
    /// `MappedMemory` does not hold any references to the `Memory`. If the
    /// `Memory` has already been unmounted, calling `unmount` is a no-op.
    pub fn unmount<T>(&mut self, mount_token: &MemoryToken<T>)
        where T: Memory + 'a
    {
        self.0
            .iter()
            .enumerate()
            .find(|&(_, entry)| rc_ptr_eq(&mount_token.memory, &entry.memory))
            .map(|(idx, _)| idx)
            .map(|idx| self.0.remove(idx));
    }

    fn memory_at_addr(&self, addr: usize) -> Option<&Entry<'a>> {
        self.0
            .iter()
            .rev()
            .find(|entry| entry.start_address <= addr && entry.start_address + entry.size > addr)
    }
}

impl<'a> Memory for MappedMemory<'a> {
    fn get(&self, addr: usize) -> u32 {
        self.memory_at_addr(addr)
            .map(|entry| entry.memory.borrow().get(addr - entry.start_address))
            .expect("Out of bounds")
    }

    fn set(&mut self, addr: usize, value: u32) {
        self.memory_at_addr(addr)
            .map(|entry| entry.memory.borrow_mut().set(addr - entry.start_address, value))
            .expect("Out of bounds")
    }

    fn size(&self) -> usize {
        self.0
            .iter()
            .map(|entry| entry.start_address + entry.size)
            .max()
            .unwrap_or(0)
    }
}

// Helper until feature "ptr_eq" is stabilized.
// See https://github.com/rust-lang/rust/issues/36497
fn rc_ptr_eq<T: ?Sized, U: ?Sized>(this: &Rc<T>, other: &Rc<U>) -> bool {
    let this_ptr = this.deref() as *const T as *const () as usize;
    let other_ptr = other.deref() as *const U as *const () as usize;
    this_ptr == other_ptr
}

#[cfg(test)]
mod tests {
    use memory::{Memory, SliceMemory, NullMemory};

    #[test]
    fn memory_at_addr() {
        let m1 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
        let mut mm = super::MappedMemory::new();
        mm.mount(0, &m1);
        mm.mount(2, &m2);

        assert!(mm.memory_at_addr(0)
            .map_or(false, |entry| entry.memory.borrow().get(0) == 1));
        assert!(mm.memory_at_addr(1).is_none());
        assert!(mm.memory_at_addr(2)
            .map_or(false, |entry| entry.memory.borrow().get(0) == 2));
        assert!(mm.memory_at_addr(3)
            .map_or(false, |entry| entry.memory.borrow().get(0) == 2));
        assert!(mm.memory_at_addr(4).is_none());
    }

    #[test]
    fn overlapping_mounts() {
        let m1 = super::MemoryToken::new(NullMemory::new());
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
        let m3 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([3])));
        let mut mm = super::MappedMemory::new();
        mm.mount(0, &m1);
        mm.mount(1, &m2);
        mm.mount(2, &m3);
        assert_eq!(mm.get(0), 0);
        assert_eq!(mm.get(1), 2);
        assert_eq!(mm.get(2), 3);
        assert_eq!(mm.get(3), 0);
    }

    #[test]
    fn get_and_set() {
        let m1 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
        let mut mm = super::MappedMemory::new();

        mm.mount(0, &m1);
        mm.mount(2, &m2);
        assert_eq!(mm.get(0), 1);
        assert_eq!(mm.get(2), 2);

        mm.set(0, 3);
        assert_eq!(mm.get(0), 3);

        mm.set(3, 0);
        assert_eq!(mm.get(3), 0);
        assert_eq!(m1.borrow().get(0), 3);
        assert_eq!(m2.borrow().get(1), 0);
    }

    #[test]
    fn size() {
        let m1 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
        let mut mm = super::MappedMemory::new();
        assert_eq!(mm.size(), 0);

        mm.mount(0, &m1);
        assert_eq!(mm.size(), 1);

        mm.mount(2, &m2);
        assert_eq!(mm.size(), 4);
    }

    #[test]
    fn size_with_overlap() {
        let m1 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1])));
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2])));
        let m3 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([3, 3, 3])));
        let mut mm = super::MappedMemory::new();

        mm.mount(0, &m1);
        mm.mount(2, &m2);
        assert_eq!(mm.size(), 5);

        mm.mount(6, &m3);
        assert_eq!(mm.size(), 9);
    }

    #[test]
    fn unmount() {
        let m1 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1])));
        let m2 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2])));
        let m3 = super::MemoryToken::new(SliceMemory::from_slice(Box::new([3, 3, 3, 3, 3])));
        let mut mm = super::MappedMemory::new();
        mm.mount(0, &m1);
        mm.mount(0, &m2);
        mm.mount(0, &m3);

        for i in 0..5 {
            assert_eq!(mm.get(i), 3);
        }
        mm.unmount(&m3);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.unmount(&m3);
        mm.unmount(&m3);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.unmount(&m1);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2015-2016 William (B.J.) Snow Orvis
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A piston window-based renderer and event loop.

pub mod chaosgame;
pub mod escapetime;
pub mod turtle;

use graphics;
use gfx_device_gl;
use piston_window::*;

pub use graphics::math::Vec2d;
pub use super::color::*;

/// State machine for WindowHandlers that want to animate across the double buffered frames.
#[derive(Debug,PartialEq)]
pub enum WhichFrame {
    FirstFrame,
    SecondFrame,
    AllOtherFrames,
}

/// Information about the viewport and graphical backend needed by WindowHandler::render_frame.
pub struct RenderContext<'a, 'b: 'a> {
    /// Graphics context, describing the viewport, base transform, etc.
    pub context: graphics::context::Context,
    /// Graphics backend
    pub gfx: &'a mut G2d<'b>,
    /// graphics backend factory
    pub factory: &'a mut gfx_device_gl::Factory,
}

/// An object that can render frames of a drawing/animation/program/game.
pub trait WindowHandler {
    /// When the window is resized, we may need to plan to re-render.
    fn window_resized(&mut self, new_size: Vec2d);

    /// Render a frame.
    fn render_frame(&mut self,
                    context: &mut RenderContext,
                    frame_num: u32);

    /// Optional: used to indicate that the user selected an area of the window to zoom in on.
    fn zoom(&mut self, rect: [ Vec2d; 2]) {
        println!("Selected: {:?}, {:?}", rect[0], rect[1]);
    }

    /// Optional: used to indicate that the user wants to revert to the default view.
    fn reset_view(&mut self) {
        println!("Reset zoom");
    }
}

/// Runs a WindowHandler in a PistonWindow.
pub fn run(window_handler: &mut WindowHandler) {
    println!("Use the mouse to select an area to zoom in on");
    println!("Press backspace to reset the view back to the initial view");
    println!("Press esc to exit");

    let opengl = OpenGL::V3_2;
    let window: PistonWindow = WindowSettings::new("Fractal", [800, 600])
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap_or_else(|e| panic!("Failed to build Window: {}", e));

    let mut frame_num: u32 = 0;
    let mut old_size: Vec2d = [0.0,0.0];

    let mut mouse_pos: Vec2d = [0.0,0.0];
    let mut mouse_down_pos = None;

    for e in window {
        e.draw_2d(|context, gfx| {
            let size = context.get_view_size();
            if size != old_size {
                println!("resized");
                old_size = size;
                window_handler.window_resized(size);
            }
            frame_num += 1;
            // println!("Render frame {}, window: {:?}", frame_num, size);
            let mut factory = e.factory.borrow_mut();
            let mut render_context = RenderContext {
                context: context,
                gfx: gfx,
                factory: &mut *factory,
            };
            window_handler.render_frame(&mut render_context, frame_num);
        });
        e.mouse_cursor(|x,y| {
            // mouse moved
            mouse_pos = [x,y];
        });
        e.press(|button| {
            match button {
                Button::Mouse(mbutton) => {
                    match mbutton {
                        MouseButton::Left => {
                            // mouse down
                            mouse_down_pos = Some(mouse_pos);
                            println!("Pressed mouse left: {:?}", mouse_down_pos.as_ref().unwrap());
                        },
                        _ => {}
                    }
                },
                Button::Keyboard(key) => {
                    match key {
                        Key::Backspace => {
                            // "backspace" key down
                            println!("reset zoom");
                            window_handler.reset_view();
                        },
                        Key::Minus => {
                            println!("zoom out");
                            let halves = [old_size[0]/2.0,old_size[1]/2.0];
                            let top_left = [-halves[0],-halves[1]];
                            let bot_right = [old_size[0]+halves[0], old_size[1]+halves[1]];

                            window_handler.zoom([top_left, bot_right]);
                        }
                        Key::Equals => {
                            println!("zoom in");
                            let quarters = [old_size[0]/4.0,old_size[1]/4.0];
                            let top_left = [quarters[0],quarters[1]];
                            let bot_right = [old_size[0]- quarters[0], old_size[1]-quarters[1]];
                            window_handler.zoom([top_left, bot_right]);
                        }
                        _ => {}
                    }
                },
                _ => {}
            }
        });
        e.release(|button| {
            // mouse up
            if button == Button::Mouse(MouseButton::Left) {
                let p2 = mouse_pos;
                println!("Released mouse left: {:?}", p2);
                if let Some(p1) = mouse_down_pos {
                    let top_left = [p1[0].min(p2[0]), p1[1].min(p2[1])];
                    let bot_right = [p1[0].max(p2[0]), p1[1].max(p2[1])];
                    window_handler.zoom([top_left, bot_right]);
                }
                mouse_down_pos = None;
            }
        });
    }
}
<commit_msg>Arrow keys now move the view around for escape time fractals<commit_after>// Copyright (c) 2015-2016 William (B.J.) Snow Orvis
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A piston window-based renderer and event loop.

pub mod chaosgame;
pub mod escapetime;
pub mod turtle;

use graphics;
use gfx_device_gl;
use piston_window::*;

pub use graphics::math::Vec2d;
pub use super::color::*;

/// State machine for WindowHandlers that want to animate across the double buffered frames.
#[derive(Debug,PartialEq)]
pub enum WhichFrame {
    FirstFrame,
    SecondFrame,
    AllOtherFrames,
}

/// Information about the viewport and graphical backend needed by WindowHandler::render_frame.
pub struct RenderContext<'a, 'b: 'a> {
    /// Graphics context, describing the viewport, base transform, etc.
    pub context: graphics::context::Context,
    /// Graphics backend
    pub gfx: &'a mut G2d<'b>,
    /// graphics backend factory
    pub factory: &'a mut gfx_device_gl::Factory,
}

/// An object that can render frames of a drawing/animation/program/game.
pub trait WindowHandler {
    /// When the window is resized, we may need to plan to re-render.
    fn window_resized(&mut self, new_size: Vec2d);

    /// Render a frame.
    fn render_frame(&mut self,
                    context: &mut RenderContext,
                    frame_num: u32);

    /// Optional: used to indicate that the user selected an area of the window to zoom in on.
    fn zoom(&mut self, rect: [ Vec2d; 2]) {
        println!("Selected: {:?}, {:?}", rect[0], rect[1]);
    }

    /// Optional: used to indicate that the user wants to revert to the default view.
    fn reset_view(&mut self) {
        println!("Reset zoom");
    }
}

/// Runs a WindowHandler in a PistonWindow.
pub fn run(window_handler: &mut WindowHandler) {
    println!("Use the mouse to select an area to zoom in on");
    println!("Press backspace to reset the view back to the initial view");
    println!("Press esc to exit");

    let opengl = OpenGL::V3_2;
    let window: PistonWindow = WindowSettings::new("Fractal", [800, 600])
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap_or_else(|e| panic!("Failed to build Window: {}", e));

    let mut frame_num: u32 = 0;
    let mut old_size: Vec2d = [0.0,0.0];

    let mut mouse_pos: Vec2d = [0.0,0.0];
    let mut mouse_down_pos = None;

    for e in window {
        e.draw_2d(|context, gfx| {
            let size = context.get_view_size();
            if size != old_size {
                println!("resized");
                old_size = size;
                window_handler.window_resized(size);
            }
            frame_num += 1;
            // println!("Render frame {}, window: {:?}", frame_num, size);
            let mut factory = e.factory.borrow_mut();
            let mut render_context = RenderContext {
                context: context,
                gfx: gfx,
                factory: &mut *factory,
            };
            window_handler.render_frame(&mut render_context, frame_num);
        });
        e.mouse_cursor(|x,y| {
            // mouse moved
            mouse_pos = [x,y];
        });
        e.press(|button| {
            match button {
                Button::Mouse(mbutton) => {
                    match mbutton {
                        MouseButton::Left => {
                            // mouse down
                            mouse_down_pos = Some(mouse_pos);
                            println!("Pressed mouse left: {:?}", mouse_down_pos.as_ref().unwrap());
                        },
                        _ => {}
                    }
                },
                Button::Keyboard(key) => {
                    match key {
                        Key::Backspace => {
                            // "backspace" key down
                            println!("reset zoom");
                            window_handler.reset_view();
                        },
                        Key::Minus => {
                            println!("zoom out");
                            // make the current screen's dimension become 50% the new view
                            let halves = [old_size[0]/2.0,old_size[1]/2.0];
                            let new_top_left = [-halves[0],-halves[1]];
                            let new_bot_right = [old_size[0]+halves[0], old_size[1]+halves[1]];

                            window_handler.zoom([new_top_left, new_bot_right]);
                        },
                        Key::Equals => {
                            println!("zoom in");
                            // zoom in so that 50% of the current view is the new view
                            let quarters = [old_size[0]/4.0,old_size[1]/4.0];
                            let new_top_left = [quarters[0],quarters[1]];
                            let new_bot_right = [old_size[0]-quarters[0], old_size[1]-quarters[1]];

                            window_handler.zoom([new_top_left, new_bot_right]);
                        },
                        Key::Up => {
                            println!("up");
                            // scroll up by 25%
                            let move_up = old_size[1] * 0.25;
                            let new_top_left = [0.0, -move_up];
                            let new_bot_right = [old_size[0], old_size[1] - move_up];

                            window_handler.zoom([new_top_left, new_bot_right]);
                        },
                        Key::Down => {
                            let move_down = old_size[1] * 0.25;
                            let new_top_left = [0.0, move_down];
                            let new_bot_right = [old_size[0], old_size[1] + move_down];

                            window_handler.zoom([new_top_left, new_bot_right]);
                        }
                        ,
                        Key::Right => {
                            let move_right = old_size[0] * 0.25;
                            let new_top_left = [move_right, 0.0];
                            let new_bot_right = [old_size[0] + move_right , old_size[1]];

                            window_handler.zoom([new_top_left, new_bot_right]);
                        },
                        Key::Left => {
                            let move_left = old_size[0] * 0.25;
                            let new_top_left = [-move_left, 0.0];
                            let new_bot_right = [old_size[0] - move_left , old_size[1]];

                            window_handler.zoom([new_top_left, new_bot_right]);
                        },
                        _ => {}
                    }
                },
                _ => {}
            }
        });
        e.release(|button| {
            // mouse up
            if button == Button::Mouse(MouseButton::Left) {
                let p2 = mouse_pos;
                println!("Released mouse left: {:?}", p2);
                if let Some(p1) = mouse_down_pos {
                    let top_left = [p1[0].min(p2[0]), p1[1].min(p2[1])];
                    let bot_right = [p1[0].max(p2[0]), p1[1].max(p2[1])];
                    window_handler.zoom([top_left, bot_right]);
                }
                mouse_down_pos = None;
            }
        });
    }
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::fs::File;
use std::io::Write;
use std::env;

static USER_AGENT_FILE_NAME: &str = "user_agent";

fn main() {
    let package_name = option_env!("CARGO_PKG_NAME").unwrap_or("Exonum");
    let package_version = option_env!("CARGO_PKG_VERSION").unwrap_or("?");
    let rust_version = option_env!("CARGO_RUST_VERSION").unwrap_or("?");
    let user_agent = format!("{} {}/Rust {}", package_name, package_version, rust_version);

    let out_dir = env::var("OUT_DIR").expect("Unable to get OUT_DIR");
    let dest_path = Path::new(&out_dir).join(USER_AGENT_FILE_NAME);
    let mut file = File::create(dest_path).expect("Unable to create output file");
    file.write_all(user_agent.as_bytes()).expect(
        "Unable to data to file",
    );
}
<commit_msg>Get rust versions correctly<commit_after>use std::path::Path;
use std::fs::File;
use std::io::Write;
use std::env;
use std::process::Command;

static USER_AGENT_FILE_NAME: &str = "user_agent";

fn main() {
    let package_name = option_env!("CARGO_PKG_NAME").unwrap_or("exonum");
    let package_version = option_env!("CARGO_PKG_VERSION").unwrap_or("?");
    let rust_version = rust_version().unwrap_or("rust ?".to_string());
    let user_agent = format!("{} {}/{}", package_name, package_version, rust_version);

    let out_dir = env::var("OUT_DIR").expect("Unable to get OUT_DIR");
    let dest_path = Path::new(&out_dir).join(USER_AGENT_FILE_NAME);
    let mut file = File::create(dest_path).expect("Unable to create output file");
    file.write_all(user_agent.as_bytes()).expect(
        "Unable to data to file",
    );
}

fn rust_version() -> Option<String> {
    let rustc = env!("RUSTC");

    if let Ok(output) = Command::new(rustc).arg("-V").output().map(|x| x.stdout) {
        String::from_utf8(output).ok()
    } else {
        None
    }
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Prime_decomposition

fn factor(mut nb: i32) -> Vec<i32> {
	let mut result = vec!();

	// First we take out all even factors.
	while nb % 2 == 0 {
		result.push(2);
		nb /= 2;
	}

	// Then (if any left) we take out the odd ones.
	let mut cand = 3;
	let mut max_bound = (nb as f32).sqrt() as i32 + 1;

	while cand <= max_bound {
		while nb % cand == 0 {
			result.push(cand);
			nb /= cand;
		}
		max_bound = (nb as f32).sqrt() as i32 + 1;
		cand += 2;
	}

	if nb > 1 {
		result.push(nb);
	}

	result
}

#[cfg(not(test))]
fn main() {
	println!("{:?}", factor(5) == vec!(5));
	println!("{:?}", factor(15) == vec!(3, 5));
	println!("{:?}", factor(16) == vec!(2, 2, 2, 2));
	println!("{:?}", factor(10287) == vec!(3, 3, 3, 3, 127));
}
<commit_msg>Added tests to prime decomposition<commit_after>// Implements http://rosettacode.org/wiki/Prime_decomposition

fn factor(mut nb: i32) -> Vec<i32> {
	let mut result = vec!();

	// First we take out all even factors.
	while nb % 2 == 0 {
		result.push(2);
		nb /= 2;
	}

	// Then (if any left) we take out the odd ones.
	let mut cand = 3;
	let mut max_bound = (nb as f32).sqrt() as i32 + 1;

	while cand <= max_bound {
		while nb % cand == 0 {
			result.push(cand);
			nb /= cand;
		}
		max_bound = (nb as f32).sqrt() as i32 + 1;
		cand += 2;
	}

	if nb > 1 {
		result.push(nb);
	}

	result
}

#[cfg(not(test))]
fn main() {
	println!("Factors of 5: {}", factor(5));
	println!("Factors of 15: {}", factor(15));
	println!("Factors of 16: {}", factor(16));
	println!("Factors of 10287: {}", factor(10287));
}

#[test]
fn test_basic() {
    assert!(factor(5) == vec!(5));
    assert!(factor(15) == vec!(3, 5));
    assert!(factor(16) == vec!(2, 2, 2, 2));
    assert!(factor(10287) == vec!(3, 3, 3, 3, 127));
}
<|endoftext|>