<commit_before>use na::{self, Real};

use crate::math::Isometry;
use crate::query::algorithms::{gjk, CSOPoint, VoronoiSimplex};
use crate::query::{Ray, RayCast, RayIntersection};
#[cfg(feature = "dim2")]
use crate::shape::ConvexPolygon;
use crate::shape::{Capsule, Segment, SupportMap, FeatureId};
#[cfg(feature = "dim3")]
use crate::shape::{Cone, ConvexHull, Cylinder};

/// Cast a ray on a shape using the GJK algorithm.
pub fn implicit_toi_and_normal_with_ray<N, G: ?Sized>(
    m: &Isometry<N>,
    shape: &G,
    simplex: &mut VoronoiSimplex<N>,
    ray: &Ray<N>,
    solid: bool,
) -> Option<RayIntersection<N>>
where
    N: Real,
    G: SupportMap<N>,
{
    let supp = shape.support_point(m, &-ray.dir);
    simplex.reset(CSOPoint::single_point(supp - ray.origin.coords));

    let inter = gjk::cast_ray(m, shape, simplex, ray);

    if !solid {
        match inter {
            None => None,
            Some((toi, normal)) => {
                if toi.is_zero() {
                    // the ray is inside of the shape.
                    let ndir = ray.dir.normalize();
                    let supp = shape.support_point(m, &ndir);
                    let shift = (supp - ray.origin).dot(&ndir) + na::convert(0.001f64);
                    let new_ray = Ray::new(ray.origin + ndir * shift, -ray.dir);

                    // FIXME: replace by? : simplex.translate_by(&(ray.origin - new_ray.origin));
                    simplex.reset(CSOPoint::single_point(supp - new_ray.origin.coords));

                    gjk::cast_ray(m, shape, simplex, &new_ray)
                        .map(|(toi, normal)| RayIntersection::new(shift - toi, normal, FeatureId::Unknown))
                } else {
                    Some(RayIntersection::new(toi, normal, FeatureId::Unknown))
                }
            }
        }
    } else {
        inter.map(|(toi, normal)| RayIntersection::new(toi, normal, FeatureId::Unknown))
    }
}

#[cfg(feature = "dim3")]
impl<N: Real> RayCast<N> for Cylinder<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        let ls_ray = ray.inverse_transform_by(m);

        implicit_toi_and_normal_with_ray(
            &Isometry::identity(),
            self,
            &mut VoronoiSimplex::new(),
            &ls_ray,
            solid,
        )
        .map(|mut res| {
            res.normal = m * res.normal;
            res
        })
    }
}

#[cfg(feature = "dim3")]
impl<N: Real> RayCast<N> for Cone<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        let ls_ray = ray.inverse_transform_by(m);

        implicit_toi_and_normal_with_ray(
            &Isometry::identity(),
            self,
            &mut VoronoiSimplex::new(),
            &ls_ray,
            solid,
        )
        .map(|mut res| {
            res.normal = m * res.normal;
            res
        })
    }
}

impl<N: Real> RayCast<N> for Capsule<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        let ls_ray = ray.inverse_transform_by(m);

        implicit_toi_and_normal_with_ray(
            &Isometry::identity(),
            self,
            &mut VoronoiSimplex::new(),
            &ls_ray,
            solid,
        )
        .map(|mut res| {
            res.normal = m * res.normal;
            res
        })
    }
}

#[cfg(feature = "dim3")]
impl<N: Real> RayCast<N> for ConvexHull<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        let ls_ray = ray.inverse_transform_by(m);

        implicit_toi_and_normal_with_ray(
            &Isometry::identity(),
            self,
            &mut VoronoiSimplex::new(),
            &ls_ray,
            solid,
        )
        .map(|mut res| {
            res.normal = m * res.normal;
            res
        })
    }
}

#[cfg(feature = "dim2")]
impl<N: Real> RayCast<N> for ConvexPolygon<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        let ls_ray = ray.inverse_transform_by(m);

        implicit_toi_and_normal_with_ray(
            &Isometry::identity(),
            self,
            &mut VoronoiSimplex::new(),
            &ls_ray,
            solid,
        )
        .map(|mut res| {
            res.normal = m * res.normal;
            res
        })
    }
}

#[allow(unused_variables)]
impl<N: Real> RayCast<N> for Segment<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        #[cfg(feature = "dim2")]
        {
            let seg_dir = self.scaled_direction();
            let (s, t) = query::closest_points_internal::line_against_line_parameters(
                &ray.origin, &ray.dir, self.a(), &seg_dir);

            if s >= N::zero() && t >= N::zero() && t <= N::one() {
                let normal = self.scaled_normal();

                if normal.dot(&ray.dir) > N::zero() {
                    Some(RayIntersection::new(
                        s,
                        -normal,
                        FeatureId::Face(1)
                    ))
                } else {
                    Some(RayIntersection::new(
                        s,
                        normal,
                        FeatureId::Face(0)
                    ))
                }
            } else {
                None
            }
        }
        #[cfg(feature = "dim3")]
        {
            // XXX: implement an analytic solution for 3D too.
            let ls_ray = ray.inverse_transform_by(m);

            implicit_toi_and_normal_with_ray(
                &Isometry::identity(),
                self,
                &mut VoronoiSimplex::new(),
                &ls_ray,
                solid,
            )
                .map(|mut res| {
                    res.normal = m * res.normal;
                    res
                })
        }
    }
}
<commit_msg>Fix 2D compilation error.<commit_after>use na::{self, Real};

use crate::math::Isometry;
use crate::query::algorithms::{gjk, CSOPoint, VoronoiSimplex};
use crate::query::{Ray, RayCast, RayIntersection};
#[cfg(feature = "dim2")]
use crate::query;
#[cfg(feature = "dim2")]
use crate::shape::ConvexPolygon;
use crate::shape::{Capsule, Segment, SupportMap, FeatureId};
#[cfg(feature = "dim3")]
use crate::shape::{Cone, ConvexHull, Cylinder};

/// Cast a ray on a shape using the GJK algorithm.
pub fn implicit_toi_and_normal_with_ray<N, G: ?Sized>(
    m: &Isometry<N>,
    shape: &G,
    simplex: &mut VoronoiSimplex<N>,
    ray: &Ray<N>,
    solid: bool,
) -> Option<RayIntersection<N>>
where
    N: Real,
    G: SupportMap<N>,
{
    let supp = shape.support_point(m, &-ray.dir);
    simplex.reset(CSOPoint::single_point(supp - ray.origin.coords));

    let inter = gjk::cast_ray(m, shape, simplex, ray);

    if !solid {
        match inter {
            None => None,
            Some((toi, normal)) => {
                if toi.is_zero() {
                    // the ray is inside of the shape.
                    let ndir = ray.dir.normalize();
                    let supp = shape.support_point(m, &ndir);
                    let shift = (supp - ray.origin).dot(&ndir) + na::convert(0.001f64);
                    let new_ray = Ray::new(ray.origin + ndir * shift, -ray.dir);

                    // FIXME: replace by? : simplex.translate_by(&(ray.origin - new_ray.origin));
                    simplex.reset(CSOPoint::single_point(supp - new_ray.origin.coords));

                    gjk::cast_ray(m, shape, simplex, &new_ray)
                        .map(|(toi, normal)| RayIntersection::new(shift - toi, normal, FeatureId::Unknown))
                } else {
                    Some(RayIntersection::new(toi, normal, FeatureId::Unknown))
                }
            }
        }
    } else {
        inter.map(|(toi, normal)| RayIntersection::new(toi, normal, FeatureId::Unknown))
    }
}

#[cfg(feature = "dim3")]
impl<N: Real> RayCast<N> for Cylinder<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        let ls_ray = ray.inverse_transform_by(m);

        implicit_toi_and_normal_with_ray(
            &Isometry::identity(),
            self,
            &mut VoronoiSimplex::new(),
            &ls_ray,
            solid,
        )
        .map(|mut res| {
            res.normal = m * res.normal;
            res
        })
    }
}

#[cfg(feature = "dim3")]
impl<N: Real> RayCast<N> for Cone<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        let ls_ray = ray.inverse_transform_by(m);

        implicit_toi_and_normal_with_ray(
            &Isometry::identity(),
            self,
            &mut VoronoiSimplex::new(),
            &ls_ray,
            solid,
        )
        .map(|mut res| {
            res.normal = m * res.normal;
            res
        })
    }
}

impl<N: Real> RayCast<N> for Capsule<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        let ls_ray = ray.inverse_transform_by(m);

        implicit_toi_and_normal_with_ray(
            &Isometry::identity(),
            self,
            &mut VoronoiSimplex::new(),
            &ls_ray,
            solid,
        )
        .map(|mut res| {
            res.normal = m * res.normal;
            res
        })
    }
}

#[cfg(feature = "dim3")]
impl<N: Real> RayCast<N> for ConvexHull<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        let ls_ray = ray.inverse_transform_by(m);

        implicit_toi_and_normal_with_ray(
            &Isometry::identity(),
            self,
            &mut VoronoiSimplex::new(),
            &ls_ray,
            solid,
        )
        .map(|mut res| {
            res.normal = m * res.normal;
            res
        })
    }
}

#[cfg(feature = "dim2")]
impl<N: Real> RayCast<N> for ConvexPolygon<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        let ls_ray = ray.inverse_transform_by(m);

        implicit_toi_and_normal_with_ray(
            &Isometry::identity(),
            self,
            &mut VoronoiSimplex::new(),
            &ls_ray,
            solid,
        )
        .map(|mut res| {
            res.normal = m * res.normal;
            res
        })
    }
}

#[allow(unused_variables)]
impl<N: Real> RayCast<N> for Segment<N> {
    fn toi_and_normal_with_ray(
        &self,
        m: &Isometry<N>,
        ray: &Ray<N>,
        solid: bool,
    ) -> Option<RayIntersection<N>>
    {
        #[cfg(feature = "dim2")]
        {
            let seg_dir = self.scaled_direction();
            let (s, t) = query::closest_points_internal::line_against_line_parameters(
                &ray.origin, &ray.dir, self.a(), &seg_dir);

            if s >= N::zero() && t >= N::zero() && t <= N::one() {
                let normal = self.scaled_normal();

                if normal.dot(&ray.dir) > N::zero() {
                    Some(RayIntersection::new(
                        s,
                        -normal,
                        FeatureId::Face(1)
                    ))
                } else {
                    Some(RayIntersection::new(
                        s,
                        normal,
                        FeatureId::Face(0)
                    ))
                }
            } else {
                None
            }
        }
        #[cfg(feature = "dim3")]
        {
            // XXX: implement an analytic solution for 3D too.
            let ls_ray = ray.inverse_transform_by(m);

            implicit_toi_and_normal_with_ray(
                &Isometry::identity(),
                self,
                &mut VoronoiSimplex::new(),
                &ls_ray,
                solid,
            )
                .map(|mut res| {
                    res.normal = m * res.normal;
                    res
                })
        }
    }
}
<|endoftext|>


<commit_before>extern crate libc;

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

use self::libc::{c_void, c_int};

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

// From https://opensource.apple.com/source/xnu/xnu-4903.221.2/bsd/sys/resource.h
pub enum PidRUsageFlavor {
    V0 = 0,
    V1 = 1,
    V2 = 2,
    V3 = 3,
    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;
}

/// 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::<RUsageInfoV2>(pid) {
///         println!("UUID: {:?}, Disk Read: {}, Disk Write: {}", res.ri_uuid, res.ri_diskio_bytesread, res.ri_diskio_byteswritten);
///     }
/// }
/// ```
#[cfg(target_os = "macos")]
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(not(target_os = "macos"))]
pub fn pidrusage<T: PIDRUsage>(pid : i32) -> Result<T, String> {
    unimplemented!()
}

#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV0 {
    pub ri_uuid              : [u8; 16],
    pub ri_user_time         : u64,
    pub ri_system_time       : u64,
    pub ri_pkg_idle_wkups    : u64,
    pub ri_interrupt_wkups   : u64,
    pub ri_pageins           : u64,
    pub ri_wired_size        : u64,
    pub ri_resident_size     : u64,
    pub ri_phys_footprint    : u64,
    pub ri_proc_start_abstime: u64,
    pub ri_proc_exit_abstime : u64,
}

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

#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV1 {
    pub ri_uuid                 : [u8; 16],
    pub ri_user_time            : u64,
    pub ri_system_time          : u64,
    pub ri_pkg_idle_wkups       : u64,
    pub ri_interrupt_wkups      : u64,
    pub ri_pageins              : u64,
    pub ri_wired_size           : u64,
    pub ri_resident_size        : u64,
    pub ri_phys_footprint       : u64,
    pub ri_proc_start_abstime   : u64,
    pub ri_proc_exit_abstime    : u64,
    pub ri_child_user_time      : u64,
    pub ri_child_system_time    : u64,
    pub ri_child_pkg_idle_wkups : u64,
    pub ri_child_interrupt_wkups: u64,
    pub ri_child_pageins        : u64,
    pub ri_child_elapsed_abstime: u64,
}

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

#[repr(C)]
#[derive(Debug, Default)]
pub struct RUsageInfoV2 {
    pub ri_uuid                 : [u8; 16],
    pub ri_user_time            : u64,
    pub ri_system_time          : u64,
    pub ri_pkg_idle_wkups       : u64,
    pub ri_interrupt_wkups      : u64,
    pub ri_pageins              : u64,
    pub ri_wired_size           : u64,
    pub ri_resident_size        : u64,
    pub ri_phys_footprint       : u64,
    pub ri_proc_start_abstime   : u64,
    pub ri_proc_exit_abstime    : u64,
    pub ri_child_user_time      : u64,
    pub ri_child_system_time    : u64,
    pub ri_child_pkg_idle_wkups : u64,
    pub ri_child_interrupt_wkups: u64,
    pub ri_child_pageins        : u64,
    pub ri_child_elapsed_abstime: u64,
    pub ri_diskio_bytesread     : u64,
    pub ri_diskio_byteswritten  : u64,
}

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

#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV3 {
    pub ri_uuid                         : [u8; 16],
    pub ri_user_time                    : u64,
    pub ri_system_time                  : u64,
    pub ri_pkg_idle_wkups               : u64,
    pub ri_interrupt_wkups              : u64,
    pub ri_pageins                      : u64,
    pub ri_wired_size                   : u64,
    pub ri_resident_size                : u64,
    pub ri_phys_footprint               : u64,
    pub ri_proc_start_abstime           : u64,
    pub ri_proc_exit_abstime            : u64,
    pub ri_child_user_time              : u64,
    pub ri_child_system_time            : u64,
    pub ri_child_pkg_idle_wkups         : u64,
    pub ri_child_interrupt_wkups        : u64,
    pub ri_child_pageins                : u64,
    pub ri_child_elapsed_abstime        : u64,
    pub ri_diskio_bytesread             : u64,
    pub ri_diskio_byteswritten          : u64,
    pub ri_cpu_time_qos_default         : u64,
    pub ri_cpu_time_qos_maintenance     : u64,
    pub ri_cpu_time_qos_background      : u64,
    pub ri_cpu_time_qos_utility         : u64,
    pub ri_cpu_time_qos_legacy          : u64,
    pub ri_cpu_time_qos_user_initiated  : u64,
    pub ri_cpu_time_qos_user_interactive: u64,
    pub ri_billed_system_time           : u64,
    pub ri_serviced_system_time         : u64,
}

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

#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV4 {
    pub ri_uuid                         : [u8; 16],
    pub ri_user_time                    : u64,
    pub ri_system_time                  : u64,
    pub ri_pkg_idle_wkups               : u64,
    pub ri_interrupt_wkups              : u64,
    pub ri_pageins                      : u64,
    pub ri_wired_size                   : u64,
    pub ri_resident_size                : u64,
    pub ri_phys_footprint               : u64,
    pub ri_proc_start_abstime           : u64,
    pub ri_proc_exit_abstime            : u64,
    pub ri_child_user_time              : u64,
    pub ri_child_system_time            : u64,
    pub ri_child_pkg_idle_wkups         : u64,
    pub ri_child_interrupt_wkups        : u64,
    pub ri_child_pageins                : u64,
    pub ri_child_elapsed_abstime        : u64,
    pub ri_diskio_bytesread             : u64,
    pub ri_diskio_byteswritten          : u64,
    pub ri_cpu_time_qos_default         : u64,
    pub ri_cpu_time_qos_maintenance     : u64,
    pub ri_cpu_time_qos_background      : u64,
    pub ri_cpu_time_qos_utility         : u64,
    pub ri_cpu_time_qos_legacy          : u64,
    pub ri_cpu_time_qos_user_initiated  : u64,
    pub ri_cpu_time_qos_user_interactive: u64,
    pub ri_billed_system_time           : u64,
    pub ri_serviced_system_time         : u64,
    pub ri_logical_writes               : u64,
    pub ri_lifetime_max_phys_footprint  : u64,
    pub ri_instructions                 : u64,
    pub ri_cycles                       : u64,
    pub ri_billed_energy                : u64,
    pub ri_serviced_energy              : u64,
    pub ri_interval_max_phys_footprint  : u64,
    pub ri_unused                       : [u64; 1],
}

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

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

    #[cfg(target_os = "macos")]
    #[test]
    fn pidrusage_test() {
        use std::process;
        let pid = process::id() as i32;

        let res = pidrusage::<RUsageInfoV2>(pid).unwrap();
        println!("UUID: {:?}, Disk Read: {}, Disk Write: {}", res.ri_uuid, res.ri_diskio_bytesread, res.ri_diskio_byteswritten);
    }
}<commit_msg>Reduce output of tests<commit_after>extern crate libc;

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

use self::libc::{c_void, c_int};

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

// From https://opensource.apple.com/source/xnu/xnu-4903.221.2/bsd/sys/resource.h
pub enum PidRUsageFlavor {
    V0 = 0,
    V1 = 1,
    V2 = 2,
    V3 = 3,
    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;
}

/// 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::<RUsageInfoV2>(pid) {
///         println!("UUID: {:?}, Disk Read: {}, Disk Write: {}", res.ri_uuid, res.ri_diskio_bytesread, res.ri_diskio_byteswritten);
///     }
/// }
/// ```
#[cfg(target_os = "macos")]
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(not(target_os = "macos"))]
pub fn pidrusage<T: PIDRUsage>(pid : i32) -> Result<T, String> {
    unimplemented!()
}

#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV0 {
    pub ri_uuid              : [u8; 16],
    pub ri_user_time         : u64,
    pub ri_system_time       : u64,
    pub ri_pkg_idle_wkups    : u64,
    pub ri_interrupt_wkups   : u64,
    pub ri_pageins           : u64,
    pub ri_wired_size        : u64,
    pub ri_resident_size     : u64,
    pub ri_phys_footprint    : u64,
    pub ri_proc_start_abstime: u64,
    pub ri_proc_exit_abstime : u64,
}

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

#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV1 {
    pub ri_uuid                 : [u8; 16],
    pub ri_user_time            : u64,
    pub ri_system_time          : u64,
    pub ri_pkg_idle_wkups       : u64,
    pub ri_interrupt_wkups      : u64,
    pub ri_pageins              : u64,
    pub ri_wired_size           : u64,
    pub ri_resident_size        : u64,
    pub ri_phys_footprint       : u64,
    pub ri_proc_start_abstime   : u64,
    pub ri_proc_exit_abstime    : u64,
    pub ri_child_user_time      : u64,
    pub ri_child_system_time    : u64,
    pub ri_child_pkg_idle_wkups : u64,
    pub ri_child_interrupt_wkups: u64,
    pub ri_child_pageins        : u64,
    pub ri_child_elapsed_abstime: u64,
}

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

#[repr(C)]
#[derive(Debug, Default)]
pub struct RUsageInfoV2 {
    pub ri_uuid                 : [u8; 16],
    pub ri_user_time            : u64,
    pub ri_system_time          : u64,
    pub ri_pkg_idle_wkups       : u64,
    pub ri_interrupt_wkups      : u64,
    pub ri_pageins              : u64,
    pub ri_wired_size           : u64,
    pub ri_resident_size        : u64,
    pub ri_phys_footprint       : u64,
    pub ri_proc_start_abstime   : u64,
    pub ri_proc_exit_abstime    : u64,
    pub ri_child_user_time      : u64,
    pub ri_child_system_time    : u64,
    pub ri_child_pkg_idle_wkups : u64,
    pub ri_child_interrupt_wkups: u64,
    pub ri_child_pageins        : u64,
    pub ri_child_elapsed_abstime: u64,
    pub ri_diskio_bytesread     : u64,
    pub ri_diskio_byteswritten  : u64,
}

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

#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV3 {
    pub ri_uuid                         : [u8; 16],
    pub ri_user_time                    : u64,
    pub ri_system_time                  : u64,
    pub ri_pkg_idle_wkups               : u64,
    pub ri_interrupt_wkups              : u64,
    pub ri_pageins                      : u64,
    pub ri_wired_size                   : u64,
    pub ri_resident_size                : u64,
    pub ri_phys_footprint               : u64,
    pub ri_proc_start_abstime           : u64,
    pub ri_proc_exit_abstime            : u64,
    pub ri_child_user_time              : u64,
    pub ri_child_system_time            : u64,
    pub ri_child_pkg_idle_wkups         : u64,
    pub ri_child_interrupt_wkups        : u64,
    pub ri_child_pageins                : u64,
    pub ri_child_elapsed_abstime        : u64,
    pub ri_diskio_bytesread             : u64,
    pub ri_diskio_byteswritten          : u64,
    pub ri_cpu_time_qos_default         : u64,
    pub ri_cpu_time_qos_maintenance     : u64,
    pub ri_cpu_time_qos_background      : u64,
    pub ri_cpu_time_qos_utility         : u64,
    pub ri_cpu_time_qos_legacy          : u64,
    pub ri_cpu_time_qos_user_initiated  : u64,
    pub ri_cpu_time_qos_user_interactive: u64,
    pub ri_billed_system_time           : u64,
    pub ri_serviced_system_time         : u64,
}

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

#[repr(C)]
#[derive(Default)]
pub struct RUsageInfoV4 {
    pub ri_uuid                         : [u8; 16],
    pub ri_user_time                    : u64,
    pub ri_system_time                  : u64,
    pub ri_pkg_idle_wkups               : u64,
    pub ri_interrupt_wkups              : u64,
    pub ri_pageins                      : u64,
    pub ri_wired_size                   : u64,
    pub ri_resident_size                : u64,
    pub ri_phys_footprint               : u64,
    pub ri_proc_start_abstime           : u64,
    pub ri_proc_exit_abstime            : u64,
    pub ri_child_user_time              : u64,
    pub ri_child_system_time            : u64,
    pub ri_child_pkg_idle_wkups         : u64,
    pub ri_child_interrupt_wkups        : u64,
    pub ri_child_pageins                : u64,
    pub ri_child_elapsed_abstime        : u64,
    pub ri_diskio_bytesread             : u64,
    pub ri_diskio_byteswritten          : u64,
    pub ri_cpu_time_qos_default         : u64,
    pub ri_cpu_time_qos_maintenance     : u64,
    pub ri_cpu_time_qos_background      : u64,
    pub ri_cpu_time_qos_utility         : u64,
    pub ri_cpu_time_qos_legacy          : u64,
    pub ri_cpu_time_qos_user_initiated  : u64,
    pub ri_cpu_time_qos_user_interactive: u64,
    pub ri_billed_system_time           : u64,
    pub ri_serviced_system_time         : u64,
    pub ri_logical_writes               : u64,
    pub ri_lifetime_max_phys_footprint  : u64,
    pub ri_instructions                 : u64,
    pub ri_cycles                       : u64,
    pub ri_billed_energy                : u64,
    pub ri_serviced_energy              : u64,
    pub ri_interval_max_phys_footprint  : u64,
    pub ri_unused                       : [u64; 1],
}

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

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

    #[cfg(target_os = "macos")]
    #[test]
    fn pidrusage_test() {
        use std::process;
        let pid = process::id() as i32;

        let _ = pidrusage::<RUsageInfoV2>(pid).unwrap();
    }
}<|endoftext|>


<commit_before>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The implementations of `Rand` for the built-in types.

use core::prelude::*;
use core::char;
use core::int;
use core::uint;

use {Rand,Rng};

impl Rand for int {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> int {
        if int::BITS == 32 {
            rng.gen::<i32>() as int
        } else {
            rng.gen::<i64>() as int
        }
    }
}

impl Rand for i8 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> i8 {
        rng.next_u32() as i8
    }
}

impl Rand for i16 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> i16 {
        rng.next_u32() as i16
    }
}

impl Rand for i32 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> i32 {
        rng.next_u32() as i32
    }
}

impl Rand for i64 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> i64 {
        rng.next_u64() as i64
    }
}

impl Rand for uint {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> uint {
        if uint::BITS == 32 {
            rng.gen::<u32>() as uint
        } else {
            rng.gen::<u64>() as uint
        }
    }
}

impl Rand for u8 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> u8 {
        rng.next_u32() as u8
    }
}

impl Rand for u16 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> u16 {
        rng.next_u32() as u16
    }
}

impl Rand for u32 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> u32 {
        rng.next_u32()
    }
}

impl Rand for u64 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> u64 {
        rng.next_u64()
    }
}

macro_rules! float_impls {
    ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident, $ignored_bits:expr) => {
        mod $mod_name {
            use {Rand, Rng, Open01, Closed01};

            static SCALE: $ty = (1u64 << $mantissa_bits) as $ty;

            impl Rand for $ty {
                /// Generate a floating point number in the half-open
                /// interval `[0,1)`.
                ///
                /// See `Closed01` for the closed interval `[0,1]`,
                /// and `Open01` for the open interval `(0,1)`.
                #[inline]
                fn rand<R: Rng>(rng: &mut R) -> $ty {
                    // using any more than `mantissa_bits` bits will
                    // cause (e.g.) 0xffff_ffff to correspond to 1
                    // exactly, so we need to drop some (8 for f32, 11
                    // for f64) to guarantee the open end.
                    (rng.$method_name() >> $ignored_bits) as $ty / SCALE
                }
            }
            impl Rand for Open01<$ty> {
                #[inline]
                fn rand<R: Rng>(rng: &mut R) -> Open01<$ty> {
                    // add a small amount (specifically 2 bits below
                    // the precision of f64/f32 at 1.0), so that small
                    // numbers are larger than 0, but large numbers
                    // aren't pushed to/above 1.
                    Open01(((rng.$method_name() >> $ignored_bits) as $ty + 0.25) / SCALE)
                }
            }
            impl Rand for Closed01<$ty> {
                #[inline]
                fn rand<R: Rng>(rng: &mut R) -> Closed01<$ty> {
                    // divide by the maximum value of the numerator to
                    // get a non-zero probability of getting exactly
                    // 1.0.
                    Closed01((rng.$method_name() >> $ignored_bits) as $ty / (SCALE - 1.0))
                }
            }
        }
    }
}
float_impls! { f64_rand_impls, f64, 53, next_u64, 11 }
float_impls! { f32_rand_impls, f32, 24, next_u32, 8 }

impl Rand for char {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> char {
        // a char is 21 bits
        static CHAR_MASK: u32 = 0x001f_ffff;
        loop {
            // Rejection sampling. About 0.2% of numbers with at most
            // 21-bits are invalid codepoints (surrogates), so this
            // will succeed first go almost every time.
            match char::from_u32(rng.next_u32() & CHAR_MASK) {
                Some(c) => return c,
                None => {}
            }
        }
    }
}

impl Rand for bool {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> bool {
        rng.gen::<u8>() & 1 == 1
    }
}

macro_rules! tuple_impl {
    // use variables to indicate the arity of the tuple
    ($($tyvar:ident),* ) => {
        // the trailing commas are for the 1 tuple
        impl<
            $( $tyvar : Rand ),*
            > Rand for ( $( $tyvar ),* , ) {

            #[inline]
            fn rand<R: Rng>(_rng: &mut R) -> ( $( $tyvar ),* , ) {
                (
                    // use the $tyvar's to get the appropriate number of
                    // repeats (they're not actually needed)
                    $(
                        _rng.gen::<$tyvar>()
                    ),*
                    ,
                )
            }
        }
    }
}

impl Rand for () {
    #[inline]
    fn rand<R: Rng>(_: &mut R) -> () { () }
}
tuple_impl!{A}
tuple_impl!{A, B}
tuple_impl!{A, B, C}
tuple_impl!{A, B, C, D}
tuple_impl!{A, B, C, D, E}
tuple_impl!{A, B, C, D, E, F}
tuple_impl!{A, B, C, D, E, F, G}
tuple_impl!{A, B, C, D, E, F, G, H}
tuple_impl!{A, B, C, D, E, F, G, H, I}
tuple_impl!{A, B, C, D, E, F, G, H, I, J}

impl<T:Rand> Rand for Option<T> {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> Option<T> {
        if rng.gen() {
            Some(rng.gen())
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use std::prelude::*;
    use std::rand::{Rng, task_rng, Open01, Closed01};

    struct ConstantRng(u64);
    impl Rng for ConstantRng {
        fn next_u32(&mut self) -> u32 {
            let ConstantRng(v) = *self;
            v as u32
        }
        fn next_u64(&mut self) -> u64 {
            let ConstantRng(v) = *self;
            v
        }
    }

    #[test]
    fn floating_point_edge_cases() {
        // the test for exact equality is correct here.
        assert!(ConstantRng(0xffff_ffff).gen::<f32>() != 1.0)
        assert!(ConstantRng(0xffff_ffff_ffff_ffff).gen::<f64>() != 1.0)
    }

    #[test]
    fn rand_open() {
        // this is unlikely to catch an incorrect implementation that
        // generates exactly 0 or 1, but it keeps it sane.
        let mut rng = task_rng();
        for _ in range(0u, 1_000) {
            // strict inequalities
            let Open01(f) = rng.gen::<Open01<f64>>();
            assert!(0.0 < f && f < 1.0);

            let Open01(f) = rng.gen::<Open01<f32>>();
            assert!(0.0 < f && f < 1.0);
        }
    }

    #[test]
    fn rand_closed() {
        let mut rng = task_rng();
        for _ in range(0u, 1_000) {
            // strict inequalities
            let Closed01(f) = rng.gen::<Closed01<f64>>();
            assert!(0.0 <= f && f <= 1.0);

            let Closed01(f) = rng.gen::<Closed01<f32>>();
            assert!(0.0 <= f && f <= 1.0);
        }
    }
}
<commit_msg>Impl Rand for tuples of arity 11 and 12<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The implementations of `Rand` for the built-in types.

use core::prelude::*;
use core::char;
use core::int;
use core::uint;

use {Rand,Rng};

impl Rand for int {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> int {
        if int::BITS == 32 {
            rng.gen::<i32>() as int
        } else {
            rng.gen::<i64>() as int
        }
    }
}

impl Rand for i8 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> i8 {
        rng.next_u32() as i8
    }
}

impl Rand for i16 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> i16 {
        rng.next_u32() as i16
    }
}

impl Rand for i32 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> i32 {
        rng.next_u32() as i32
    }
}

impl Rand for i64 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> i64 {
        rng.next_u64() as i64
    }
}

impl Rand for uint {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> uint {
        if uint::BITS == 32 {
            rng.gen::<u32>() as uint
        } else {
            rng.gen::<u64>() as uint
        }
    }
}

impl Rand for u8 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> u8 {
        rng.next_u32() as u8
    }
}

impl Rand for u16 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> u16 {
        rng.next_u32() as u16
    }
}

impl Rand for u32 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> u32 {
        rng.next_u32()
    }
}

impl Rand for u64 {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> u64 {
        rng.next_u64()
    }
}

macro_rules! float_impls {
    ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident, $ignored_bits:expr) => {
        mod $mod_name {
            use {Rand, Rng, Open01, Closed01};

            static SCALE: $ty = (1u64 << $mantissa_bits) as $ty;

            impl Rand for $ty {
                /// Generate a floating point number in the half-open
                /// interval `[0,1)`.
                ///
                /// See `Closed01` for the closed interval `[0,1]`,
                /// and `Open01` for the open interval `(0,1)`.
                #[inline]
                fn rand<R: Rng>(rng: &mut R) -> $ty {
                    // using any more than `mantissa_bits` bits will
                    // cause (e.g.) 0xffff_ffff to correspond to 1
                    // exactly, so we need to drop some (8 for f32, 11
                    // for f64) to guarantee the open end.
                    (rng.$method_name() >> $ignored_bits) as $ty / SCALE
                }
            }
            impl Rand for Open01<$ty> {
                #[inline]
                fn rand<R: Rng>(rng: &mut R) -> Open01<$ty> {
                    // add a small amount (specifically 2 bits below
                    // the precision of f64/f32 at 1.0), so that small
                    // numbers are larger than 0, but large numbers
                    // aren't pushed to/above 1.
                    Open01(((rng.$method_name() >> $ignored_bits) as $ty + 0.25) / SCALE)
                }
            }
            impl Rand for Closed01<$ty> {
                #[inline]
                fn rand<R: Rng>(rng: &mut R) -> Closed01<$ty> {
                    // divide by the maximum value of the numerator to
                    // get a non-zero probability of getting exactly
                    // 1.0.
                    Closed01((rng.$method_name() >> $ignored_bits) as $ty / (SCALE - 1.0))
                }
            }
        }
    }
}
float_impls! { f64_rand_impls, f64, 53, next_u64, 11 }
float_impls! { f32_rand_impls, f32, 24, next_u32, 8 }

impl Rand for char {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> char {
        // a char is 21 bits
        static CHAR_MASK: u32 = 0x001f_ffff;
        loop {
            // Rejection sampling. About 0.2% of numbers with at most
            // 21-bits are invalid codepoints (surrogates), so this
            // will succeed first go almost every time.
            match char::from_u32(rng.next_u32() & CHAR_MASK) {
                Some(c) => return c,
                None => {}
            }
        }
    }
}

impl Rand for bool {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> bool {
        rng.gen::<u8>() & 1 == 1
    }
}

macro_rules! tuple_impl {
    // use variables to indicate the arity of the tuple
    ($($tyvar:ident),* ) => {
        // the trailing commas are for the 1 tuple
        impl<
            $( $tyvar : Rand ),*
            > Rand for ( $( $tyvar ),* , ) {

            #[inline]
            fn rand<R: Rng>(_rng: &mut R) -> ( $( $tyvar ),* , ) {
                (
                    // use the $tyvar's to get the appropriate number of
                    // repeats (they're not actually needed)
                    $(
                        _rng.gen::<$tyvar>()
                    ),*
                    ,
                )
            }
        }
    }
}

impl Rand for () {
    #[inline]
    fn rand<R: Rng>(_: &mut R) -> () { () }
}
tuple_impl!{A}
tuple_impl!{A, B}
tuple_impl!{A, B, C}
tuple_impl!{A, B, C, D}
tuple_impl!{A, B, C, D, E}
tuple_impl!{A, B, C, D, E, F}
tuple_impl!{A, B, C, D, E, F, G}
tuple_impl!{A, B, C, D, E, F, G, H}
tuple_impl!{A, B, C, D, E, F, G, H, I}
tuple_impl!{A, B, C, D, E, F, G, H, I, J}
tuple_impl!{A, B, C, D, E, F, G, H, I, J, K}
tuple_impl!{A, B, C, D, E, F, G, H, I, J, K, L}

impl<T:Rand> Rand for Option<T> {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> Option<T> {
        if rng.gen() {
            Some(rng.gen())
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use std::prelude::*;
    use std::rand::{Rng, task_rng, Open01, Closed01};

    struct ConstantRng(u64);
    impl Rng for ConstantRng {
        fn next_u32(&mut self) -> u32 {
            let ConstantRng(v) = *self;
            v as u32
        }
        fn next_u64(&mut self) -> u64 {
            let ConstantRng(v) = *self;
            v
        }
    }

    #[test]
    fn floating_point_edge_cases() {
        // the test for exact equality is correct here.
        assert!(ConstantRng(0xffff_ffff).gen::<f32>() != 1.0)
        assert!(ConstantRng(0xffff_ffff_ffff_ffff).gen::<f64>() != 1.0)
    }

    #[test]
    fn rand_open() {
        // this is unlikely to catch an incorrect implementation that
        // generates exactly 0 or 1, but it keeps it sane.
        let mut rng = task_rng();
        for _ in range(0u, 1_000) {
            // strict inequalities
            let Open01(f) = rng.gen::<Open01<f64>>();
            assert!(0.0 < f && f < 1.0);

            let Open01(f) = rng.gen::<Open01<f32>>();
            assert!(0.0 < f && f < 1.0);
        }
    }

    #[test]
    fn rand_closed() {
        let mut rng = task_rng();
        for _ in range(0u, 1_000) {
            // strict inequalities
            let Closed01(f) = rng.gen::<Closed01<f64>>();
            assert!(0.0 <= f && f <= 1.0);

            let Closed01(f) = rng.gen::<Closed01<f32>>();
            assert!(0.0 <= f && f <= 1.0);
        }
    }
}
<|endoftext|>


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

use std::io::{Read, Seek};

use chrono::{DateTime, Utc};

use crate::{
    engine::{
        strat_engine::{
            backstore::metadata::{
                mda,
                sizes::{BDAExtendedSize, BlockdevSize, MDADataSize, STATIC_HEADER_SIZE},
                static_header::{MetadataLocation, StaticHeader, StratisIdentifiers},
            },
            device::SyncAll,
        },
        types::{DevUuid, PoolUuid},
    },
    stratis::StratisResult,
};

#[derive(Debug)]
pub struct BDA {
    header: StaticHeader,
    regions: mda::MDARegions,
}

impl BDA {
    /// Initialize a blockdev with a Stratis BDA.
    pub fn initialize<F>(
        f: &mut F,
        identifiers: StratisIdentifiers,
        mda_data_size: MDADataSize,
        blkdev_size: BlockdevSize,
        initialization_time: u64,
    ) -> StratisResult<BDA>
    where
        F: Seek + SyncAll,
    {
        let header = StaticHeader::new(
            identifiers,
            mda_data_size.region_size().mda_size(),
            blkdev_size,
            initialization_time,
        );

        header.write(f, MetadataLocation::Both)?;

        let regions =
            mda::MDARegions::initialize(STATIC_HEADER_SIZE.sectors().bytes(), header.mda_size, f)?;

        Ok(BDA { header, regions })
    }

    /// Load a BDA on initial setup of a device.
    /// Returns None if no BDA appears to exist.
    pub fn load<F>(f: &mut F) -> StratisResult<Option<BDA>>
    where
        F: Read + Seek + SyncAll,
    {
        let header = match StaticHeader::setup(f)? {
            Some(header) => header,
            None => return Ok(None),
        };

        // Assume that, since a valid StaticHeader was found on the device,
        // that this implies that BDA::initialize() was succesfully executed
        // sometime in the past. Since that is the case, valid MDA headers
        // were written to the device. Returns an error if there is an error
        // when loading the MDARegions, which can only be caused by an I/O
        // error or invalid MDA headers.
        let regions =
            mda::MDARegions::load(STATIC_HEADER_SIZE.sectors().bytes(), header.mda_size, f)?;

        Ok(Some(BDA { header, regions }))
    }

    /// Save metadata to the disk
    pub fn save_state<F>(
        &mut self,
        time: &DateTime<Utc>,
        metadata: &[u8],
        f: &mut F,
    ) -> StratisResult<()>
    where
        F: Seek + SyncAll,
    {
        self.regions
            .save_state(STATIC_HEADER_SIZE.sectors().bytes(), time, metadata, f)
    }

    /// Read latest metadata from the disk
    pub fn load_state<F>(&self, mut f: &mut F) -> StratisResult<Option<Vec<u8>>>
    where
        F: Read + Seek,
    {
        self.regions
            .load_state(STATIC_HEADER_SIZE.sectors().bytes(), &mut f)
    }

    /// The time when the most recent metadata was written to the BDA,
    /// if any.
    pub fn last_update_time(&self) -> Option<&DateTime<Utc>> {
        self.regions.last_update_time()
    }

    /// The UUID of the device.
    pub fn dev_uuid(&self) -> DevUuid {
        self.header.identifiers.device_uuid
    }

    /// The UUID of the device's pool.
    pub fn pool_uuid(&self) -> PoolUuid {
        self.header.identifiers.pool_uuid
    }

    /// The size of the device.
    pub fn dev_size(&self) -> BlockdevSize {
        self.header.blkdev_size
    }

    /// The number of sectors the BDA itself occupies.
    pub fn extended_size(&self) -> BDAExtendedSize {
        self.header.bda_extended_size()
    }

    /// The maximum size of variable length metadata that can be accommodated.
    pub fn max_data_size(&self) -> MDADataSize {
        self.regions.max_data_size()
    }

    /// Timestamp when the device was initialized.
    pub fn initialization_time(&self) -> u64 {
        self.header.initialization_time
    }
}

#[cfg(test)]
mod tests {
    use std::io::Cursor;

    use proptest::{collection::vec, num};

    use crate::engine::strat_engine::backstore::metadata::{
        sizes::static_header_size,
        static_header::{tests::random_static_header, tests::static_header_strategy},
    };

    use super::*;

    proptest! {
        #[test]
        /// Construct an arbitrary StaticHeader object.
        /// Initialize a BDA.
        /// Verify that the last update time is None.
        fn empty_bda(ref sh in static_header_strategy()) {
            let buf_size = *sh.mda_size.sectors().bytes() as usize + bytes!(static_header_size::STATIC_HEADER_SECTORS);
            let mut buf = Cursor::new(vec![0; buf_size]);
            let bda = BDA::initialize(
                &mut buf,
                sh.identifiers,
                sh.mda_size.region_size().data_size(),
                sh.blkdev_size,
                Utc::now().timestamp() as u64,
            ).unwrap();
            prop_assert!(bda.last_update_time().is_none());
        }
    }

    #[test]
    /// Construct a BDA and verify that an error is returned if timestamp
    /// of saved data is older than timestamp of most recently written data.
    fn test_early_times_err() {
        let data = [0u8; 3];

        // Construct a BDA.
        let sh = random_static_header(0, 0);
        let mut buf = Cursor::new(vec![0; *sh.blkdev_size.sectors().bytes() as usize]);
        let mut bda = BDA::initialize(
            &mut buf,
            sh.identifiers,
            sh.mda_size.region_size().data_size(),
            sh.blkdev_size,
            Utc::now().timestamp() as u64,
        )
        .unwrap();

        let timestamp0 = Utc::now();
        let timestamp1 = Utc::now();
        assert_ne!(timestamp0, timestamp1);

        let mut buf = Cursor::new(vec![0; *sh.blkdev_size.sectors().bytes() as usize]);
        bda.save_state(&timestamp1, &data, &mut buf).unwrap();

        // Error, because current timestamp is older than written to newer.
        assert_matches!(bda.save_state(&timestamp0, &data, &mut buf), Err(_));

        let timestamp2 = Utc::now();
        let timestamp3 = Utc::now();
        assert_ne!(timestamp2, timestamp3);

        bda.save_state(&timestamp3, &data, &mut buf).unwrap();

        // Error, because current timestamp is older than written to newer.
        assert_matches!(bda.save_state(&timestamp2, &data, &mut buf), Err(_));
    }

    proptest! {
        #[test]
        /// Construct an arbitrary StaticHeader object.
        /// Initialize a BDA.
        /// Save metadata and verify correct update time and state.
        /// Reload BDA and verify that new BDA has correct update time.
        /// Load state using new BDA and verify correct state.
        /// Save metadata again, and reload one more time, verifying new timestamp.
        fn check_state(
            ref sh in static_header_strategy(),
            ref state in vec(num::u8::ANY, 1..100),
            ref next_state in vec(num::u8::ANY, 1..100)
        ) {
            let buf_size = *sh.mda_size.sectors().bytes() as usize + bytes!(static_header_size::STATIC_HEADER_SECTORS);
            let mut buf = Cursor::new(vec![0; buf_size]);
            let mut bda = BDA::initialize(
                &mut buf,
                sh.identifiers,
                sh.mda_size.region_size().data_size(),
                sh.blkdev_size,
                Utc::now().timestamp() as u64,
            ).unwrap();
            let current_time = Utc::now();
            bda.save_state(&current_time, state, &mut buf).unwrap();
            let loaded_state = bda.load_state(&mut buf).unwrap();
            prop_assert!(bda.last_update_time().map(|t| t == &current_time).unwrap_or(false));
            prop_assert!(loaded_state.map(|s| &s == state).unwrap_or(false));

            let mut bda = BDA::load(&mut buf).unwrap().unwrap();
            let loaded_state = bda.load_state(&mut buf).unwrap();
            prop_assert!(loaded_state.map(|s| &s == state).unwrap_or(false));
            prop_assert!(bda.last_update_time().map(|t| t == &current_time).unwrap_or(false));

            let current_time = Utc::now();
            bda.save_state(&current_time, next_state, &mut buf)
                .unwrap();
            let loaded_state = bda.load_state(&mut buf).unwrap();
            prop_assert!(loaded_state.map(|s| &s == next_state).unwrap_or(false));
            prop_assert!(bda.last_update_time().map(|t| t == &current_time).unwrap_or(false));

        }
    }
}
<commit_msg>Use existing size types to get the size of the buffer for the BDA<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::io::{Read, Seek};

use chrono::{DateTime, Utc};

use crate::{
    engine::{
        strat_engine::{
            backstore::metadata::{
                mda,
                sizes::{BDAExtendedSize, BlockdevSize, MDADataSize, STATIC_HEADER_SIZE},
                static_header::{MetadataLocation, StaticHeader, StratisIdentifiers},
            },
            device::SyncAll,
        },
        types::{DevUuid, PoolUuid},
    },
    stratis::StratisResult,
};

#[derive(Debug)]
pub struct BDA {
    header: StaticHeader,
    regions: mda::MDARegions,
}

impl BDA {
    /// Initialize a blockdev with a Stratis BDA.
    pub fn initialize<F>(
        f: &mut F,
        identifiers: StratisIdentifiers,
        mda_data_size: MDADataSize,
        blkdev_size: BlockdevSize,
        initialization_time: u64,
    ) -> StratisResult<BDA>
    where
        F: Seek + SyncAll,
    {
        let header = StaticHeader::new(
            identifiers,
            mda_data_size.region_size().mda_size(),
            blkdev_size,
            initialization_time,
        );

        header.write(f, MetadataLocation::Both)?;

        let regions =
            mda::MDARegions::initialize(STATIC_HEADER_SIZE.sectors().bytes(), header.mda_size, f)?;

        Ok(BDA { header, regions })
    }

    /// Load a BDA on initial setup of a device.
    /// Returns None if no BDA appears to exist.
    pub fn load<F>(f: &mut F) -> StratisResult<Option<BDA>>
    where
        F: Read + Seek + SyncAll,
    {
        let header = match StaticHeader::setup(f)? {
            Some(header) => header,
            None => return Ok(None),
        };

        // Assume that, since a valid StaticHeader was found on the device,
        // that this implies that BDA::initialize() was succesfully executed
        // sometime in the past. Since that is the case, valid MDA headers
        // were written to the device. Returns an error if there is an error
        // when loading the MDARegions, which can only be caused by an I/O
        // error or invalid MDA headers.
        let regions =
            mda::MDARegions::load(STATIC_HEADER_SIZE.sectors().bytes(), header.mda_size, f)?;

        Ok(Some(BDA { header, regions }))
    }

    /// Save metadata to the disk
    pub fn save_state<F>(
        &mut self,
        time: &DateTime<Utc>,
        metadata: &[u8],
        f: &mut F,
    ) -> StratisResult<()>
    where
        F: Seek + SyncAll,
    {
        self.regions
            .save_state(STATIC_HEADER_SIZE.sectors().bytes(), time, metadata, f)
    }

    /// Read latest metadata from the disk
    pub fn load_state<F>(&self, mut f: &mut F) -> StratisResult<Option<Vec<u8>>>
    where
        F: Read + Seek,
    {
        self.regions
            .load_state(STATIC_HEADER_SIZE.sectors().bytes(), &mut f)
    }

    /// The time when the most recent metadata was written to the BDA,
    /// if any.
    pub fn last_update_time(&self) -> Option<&DateTime<Utc>> {
        self.regions.last_update_time()
    }

    /// The UUID of the device.
    pub fn dev_uuid(&self) -> DevUuid {
        self.header.identifiers.device_uuid
    }

    /// The UUID of the device's pool.
    pub fn pool_uuid(&self) -> PoolUuid {
        self.header.identifiers.pool_uuid
    }

    /// The size of the device.
    pub fn dev_size(&self) -> BlockdevSize {
        self.header.blkdev_size
    }

    /// The number of sectors the BDA itself occupies.
    pub fn extended_size(&self) -> BDAExtendedSize {
        self.header.bda_extended_size()
    }

    /// The maximum size of variable length metadata that can be accommodated.
    pub fn max_data_size(&self) -> MDADataSize {
        self.regions.max_data_size()
    }

    /// Timestamp when the device was initialized.
    pub fn initialization_time(&self) -> u64 {
        self.header.initialization_time
    }
}

#[cfg(test)]
mod tests {
    use std::io::Cursor;

    use proptest::{collection::vec, num};

    use crate::engine::strat_engine::backstore::metadata::static_header::{
        tests::random_static_header, tests::static_header_strategy,
    };

    use super::*;

    proptest! {
        #[test]
        /// Construct an arbitrary StaticHeader object.
        /// Initialize a BDA.
        /// Verify that the last update time is None.
        fn empty_bda(ref sh in static_header_strategy()) {
            let buf_size = *sh.mda_size.bda_size().sectors().bytes() as usize;
            let mut buf = Cursor::new(vec![0; buf_size]);
            let bda = BDA::initialize(
                &mut buf,
                sh.identifiers,
                sh.mda_size.region_size().data_size(),
                sh.blkdev_size,
                Utc::now().timestamp() as u64,
            ).unwrap();
            prop_assert!(bda.last_update_time().is_none());
        }
    }

    #[test]
    /// Construct a BDA and verify that an error is returned if timestamp
    /// of saved data is older than timestamp of most recently written data.
    fn test_early_times_err() {
        let data = [0u8; 3];

        // Construct a BDA.
        let sh = random_static_header(0, 0);
        let mut buf = Cursor::new(vec![0; *sh.blkdev_size.sectors().bytes() as usize]);
        let mut bda = BDA::initialize(
            &mut buf,
            sh.identifiers,
            sh.mda_size.region_size().data_size(),
            sh.blkdev_size,
            Utc::now().timestamp() as u64,
        )
        .unwrap();

        let timestamp0 = Utc::now();
        let timestamp1 = Utc::now();
        assert_ne!(timestamp0, timestamp1);

        let mut buf = Cursor::new(vec![0; *sh.blkdev_size.sectors().bytes() as usize]);
        bda.save_state(&timestamp1, &data, &mut buf).unwrap();

        // Error, because current timestamp is older than written to newer.
        assert_matches!(bda.save_state(&timestamp0, &data, &mut buf), Err(_));

        let timestamp2 = Utc::now();
        let timestamp3 = Utc::now();
        assert_ne!(timestamp2, timestamp3);

        bda.save_state(&timestamp3, &data, &mut buf).unwrap();

        // Error, because current timestamp is older than written to newer.
        assert_matches!(bda.save_state(&timestamp2, &data, &mut buf), Err(_));
    }

    proptest! {
        #[test]
        /// Construct an arbitrary StaticHeader object.
        /// Initialize a BDA.
        /// Save metadata and verify correct update time and state.
        /// Reload BDA and verify that new BDA has correct update time.
        /// Load state using new BDA and verify correct state.
        /// Save metadata again, and reload one more time, verifying new timestamp.
        fn check_state(
            ref sh in static_header_strategy(),
            ref state in vec(num::u8::ANY, 1..100),
            ref next_state in vec(num::u8::ANY, 1..100)
        ) {
            let buf_size = *sh.mda_size.bda_size().sectors().bytes() as usize;
            let mut buf = Cursor::new(vec![0; buf_size]);
            let mut bda = BDA::initialize(
                &mut buf,
                sh.identifiers,
                sh.mda_size.region_size().data_size(),
                sh.blkdev_size,
                Utc::now().timestamp() as u64,
            ).unwrap();
            let current_time = Utc::now();
            bda.save_state(&current_time, state, &mut buf).unwrap();
            let loaded_state = bda.load_state(&mut buf).unwrap();
            prop_assert!(bda.last_update_time().map(|t| t == &current_time).unwrap_or(false));
            prop_assert!(loaded_state.map(|s| &s == state).unwrap_or(false));

            let mut bda = BDA::load(&mut buf).unwrap().unwrap();
            let loaded_state = bda.load_state(&mut buf).unwrap();
            prop_assert!(loaded_state.map(|s| &s == state).unwrap_or(false));
            prop_assert!(bda.last_update_time().map(|t| t == &current_time).unwrap_or(false));

            let current_time = Utc::now();
            bda.save_state(&current_time, next_state, &mut buf)
                .unwrap();
            let loaded_state = bda.load_state(&mut buf).unwrap();
            prop_assert!(loaded_state.map(|s| &s == next_state).unwrap_or(false));
            prop_assert!(bda.last_update_time().map(|t| t == &current_time).unwrap_or(false));

        }
    }
}
<|endoftext|>


<commit_before>use std::fmt::{Write, Error as FmtError};

/// Simple pretty printer hand tailored for the output generated by the `quote`
/// crate for schema inference.
///
/// # Rules
///
/// 1. Seeing `{` increases indentation level
/// 2. Seeing `}` decreases indentation level
/// 3. Insert newline after `{`, `}`, `,`, and `;`
/// 4. Don't put spaces:
///   - between ident and `!`,
///   - between path segments and `::`
///   - after `(`, '<' and before `)`, `>`
///   - before `,`
pub fn format_schema(schema: &str) -> Result<String, FmtError> {
    let mut out = String::with_capacity(schema.len());
    let mut indent = String::new();
    let mut skip_space = false;
    let mut last_char = ' ';

    for c in schema.chars() {
        // The `quote!` macro inserts whitespaces at some strange location,
        // let's remove them!
        match c {
            '!' | '(' | ';' | ',' | '<' | ')' | '>' if last_char.is_whitespace() => {
                out.pop();
            },
            ':' if last_char.is_whitespace() => {
                // Unless we are at the beginning of a fully qualified path,
                // remove the whitespace.
                let char_before_whitespace = {
                    let mut chars = out.chars();
                    chars.next_back();
                    chars.next_back()
                };

                if char_before_whitespace != Some('>') {
                    out.pop();
                }
            },
            _=> { }
        }

        if skip_space && c.is_whitespace() && last_char != '>' {
            continue;
        }

        last_char = c;
        skip_space = false;

        // At this point, there is an empty line before `}`. We need to remove
        // the already inserted indent, because the new indent is smaller than
        // the old one.
        if c == '}' {
            while let Some(c) = out.pop() {
                if c == '\n' {
                    break;
                }
            }

            indent.pop();
            write!(out, "\n{}", indent)?;
        }

        write!(out, "{}", c)?;

        // We need to insert newlines in some places and adjust the indent.
        // Also, we need to remember if we could skip the next whitespace.
        match c {
            ';' | ',' | '}' => {
                skip_space = true;
                write!(out, "\n{}", indent)?;
            },
            '{' => {
                skip_space = true;
                indent += "\t";
                write!(out, "\n{}", indent)?;
            },
            ':' | '(' | '<' => skip_space = true,
            _=>{}
        }
    }

    Ok(out.replace("\t", "    "))
}

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

    macro_rules! test_pretty_printing {
        ($($name: ident: $input: expr => $expected: expr);*) => {
            $(
                #[test]
                fn $name() {
                    let actual = format_schema($input).unwrap();
                    assert_eq!($expected, actual);
                }
            )*
        }
    }
    test_pretty_printing! {
        test_remove_whitespace_colon: ":: diesel :: types :: Text" =>
            "::diesel::types::Text";
        test_format_nullable: "Nullable < :: diesel :: types :: Text >" =>
            "Nullable<::diesel::types::Text>";
        test_format_nullable_multispace: "Nullable <  Integer >" => "Nullable<Integer>";
        test_newline_after_comma: "," => ",\n";
        test_increase_indent: "{" => "{\n    ";
        test_decrease_indent: "{abc,}" => "{\n    abc,\n}\n";
        test_format_arrow: "created_at -> :: diesel :: types :: Timestamp" =>
            "created_at -> ::diesel::types::Timestamp";
        test_format_full_line: "created_at -> :: diesel :: types :: Timestamp ," =>
            "created_at -> ::diesel::types::Timestamp,\n";
        test_format_include_line: "pub use self :: infer_locks :: * ;" =>
            "pub use self::infer_locks::*;\n";
        test_format_generated_mod: "mod infer_users { table ! { users ( id ) { id -> :: diesel :: types :: Int4 , username -> :: diesel :: types :: Varchar , password -> :: diesel :: types :: Varchar , } } } pub use self :: infer_users :: * ;" =>
r"mod infer_users {
    table! {
        users(id) {
            id -> ::diesel::types::Int4,
            username -> ::diesel::types::Varchar,
            password -> ::diesel::types::Varchar,
        }
    }
}
pub use self::infer_users::*;
"
    }
}
<commit_msg>Add more infer schema tests<commit_after>use std::fmt::{Write, Error as FmtError};

/// Simple pretty printer hand tailored for the output generated by the `quote`
/// crate for schema inference.
///
/// # Rules
///
/// 1. Seeing `{` increases indentation level
/// 2. Seeing `}` decreases indentation level
/// 3. Insert newline after `{`, `}`, `,`, and `;`
/// 4. Don't put spaces:
///   - between ident and `!`,
///   - between path segments and `::`
///   - after `(`, '<' and before `)`, `>`
///   - before `,`
pub fn format_schema(schema: &str) -> Result<String, FmtError> {
    let mut out = String::with_capacity(schema.len());
    let mut indent = String::new();
    let mut skip_space = false;
    let mut last_char = ' ';

    for c in schema.chars() {
        // The `quote!` macro inserts whitespaces at some strange location,
        // let's remove them!
        match c {
            '!' | '(' | ';' | ',' | '<' | ')' | '>' if last_char.is_whitespace() => {
                out.pop();
            },
            ':' if last_char.is_whitespace() => {
                // Unless we are at the beginning of a fully qualified path,
                // remove the whitespace.
                let char_before_whitespace = {
                    let mut chars = out.chars();
                    chars.next_back();
                    chars.next_back()
                };

                if char_before_whitespace != Some('>') {
                    out.pop();
                }
            },
            _=> { }
        }

        if skip_space && c.is_whitespace() && last_char != '>' {
            continue;
        }

        last_char = c;
        skip_space = false;

        // At this point, there is an empty line before `}`. We need to remove
        // the already inserted indent, because the new indent is smaller than
        // the old one.
        if c == '}' {
            while let Some(c) = out.pop() {
                if c == '\n' {
                    break;
                }
            }

            indent.pop();
            write!(out, "\n{}", indent)?;
        }

        write!(out, "{}", c)?;

        // We need to insert newlines in some places and adjust the indent.
        // Also, we need to remember if we could skip the next whitespace.
        match c {
            ';' | ',' | '}' => {
                skip_space = true;
                write!(out, "\n{}", indent)?;
            },
            '{' => {
                skip_space = true;
                indent += "\t";
                write!(out, "\n{}", indent)?;
            },
            ':' | '(' | '<' => skip_space = true,
            _=>{}
        }
    }

    Ok(out.replace("\t", "    "))
}

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

    macro_rules! test_pretty_printing {
        ($($name:ident: $input:expr => $expected:expr);*) => {
            $(
                #[test]
                fn $name() {
                    let actual = format_schema($input).unwrap();
                    assert_eq!($expected, actual);
                }
            )*
        }
    }

    test_pretty_printing! {
        test_increase_indent:
            "{" =>
            "{\n    ";

        test_decrease_indent:
            "{abc,}" =>
            "{\n    abc,\n}\n";

        test_newline_after_comma:
            "," =>
            ",\n";

        test_newline_after_semicolon:
            ";" =>
            ";\n";

        test_remove_whitespace_macro_call:
            "table ! { }" =>
            "table! {\n}\n";

        test_remove_whitespace_path_segments:
            ":: diesel :: types :: Text" =>
            "::diesel::types::Text";

        test_remove_whitespace_parenthesis:
            "foo ( 42 )" =>
            "foo(42)";

        test_remove_whitespace_angular_brackets:
            "Option < i32 >" =>
            "Option<i32>";

        test_remove_whitespace_before_comma:
            "id -> Int4 , username -> Varchar" =>
            "id -> Int4,\nusername -> Varchar";

        test_format_nullable:
            "Nullable < :: diesel :: types :: Text >" =>
            "Nullable<::diesel::types::Text>";

        test_format_nullable_multispace:
            "Nullable <  Integer >" =>
            "Nullable<Integer>";

        test_format_arrow:
            "created_at -> :: diesel :: types :: Timestamp" =>
            "created_at -> ::diesel::types::Timestamp";

        test_format_full_line:
            "created_at -> :: diesel :: types :: Timestamp ," =>
            "created_at -> ::diesel::types::Timestamp,\n";

        test_format_include_line:
            "pub use self :: infer_locks :: * ;" =>
            "pub use self::infer_locks::*;\n";

        test_format_generated_mod:
            "mod infer_users { table ! { users ( id ) { id -> :: diesel :: types :: Int4 , \
            username -> :: diesel :: types :: Varchar , password -> :: diesel :: types :: Varchar \
            , } } } pub use self :: infer_users :: * ;" =>
r"mod infer_users {
    table! {
        users(id) {
            id -> ::diesel::types::Int4,
            username -> ::diesel::types::Varchar,
            password -> ::diesel::types::Varchar,
        }
    }
}
pub use self::infer_users::*;
"

    }
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use prometheus::{CounterVec, HistogramVec, Histogram, exponential_buckets};

lazy_static! {
    pub static ref PD_REQ_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_raftstore_pd_request_sent_total",
            "Total number of pd client request sent.",
            &["type", "status"]
        ).unwrap();

    pub static ref PD_HEARTBEAT_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_raftstore_pd_heartbeat_sent_total",
            "Total number of raftstore pd client heartbeat sent.",
            &["type"]
        ).unwrap();

    pub static ref PD_VALIDATE_PEER_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_raftstore_pd_validate_peer_total",
            "Total number of raftstore pd worker validate peer task.",
            &["type"]
        ).unwrap();

    pub static ref SNAP_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_raftstore_snapshot_total",
            "Total number of raftstore snapshot processed.",
            &["type", "status"]
        ).unwrap();

    pub static ref CHECK_SPILT_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_raftstore_check_split_total",
            "Total number of raftstore split check.",
            &["type"]
        ).unwrap();

    pub static ref SNAP_HISTOGRAM: HistogramVec =
        register_histogram_vec!(
            "tikv_raftstore_snapshot_duration_seconds",
            "Bucketed histogram of raftstore snapshot process duration",
            &["type"],
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref CHECK_SPILT_HISTOGRAM: Histogram =
        register_histogram!(
            "tikv_raftstore_check_split_duration_seconds",
            "Bucketed histogram of raftstore split check duration",
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref COMPACT_RANGE_CF: HistogramVec =
        register_histogram_vec!(
            "tikv_compact_range_cf_duration_seconds",
            "Bucketed histogram of compact range for cf execution",
            &["cf"]
        ).unwrap();

    pub static ref REGION_HASH_HISTOGRAM: Histogram =
        register_histogram!(
            "tikv_raftstore_hash_duration_seconds",
            "Bucketed histogram of raftstore hash compution duration"
        ).unwrap();

    pub static ref APPLY_PROPOSAL: Histogram =
        register_histogram!(
            "tikv_raftstore_apply_proposal",
            "Proposal count of every region in a mio tick"
        ).unwrap();
}
<commit_msg>add exponential_buckets for APPLY_PROPOSAL metrics (#2085)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use prometheus::{CounterVec, HistogramVec, Histogram, exponential_buckets};

lazy_static! {
    pub static ref PD_REQ_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_raftstore_pd_request_sent_total",
            "Total number of pd client request sent.",
            &["type", "status"]
        ).unwrap();

    pub static ref PD_HEARTBEAT_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_raftstore_pd_heartbeat_sent_total",
            "Total number of raftstore pd client heartbeat sent.",
            &["type"]
        ).unwrap();

    pub static ref PD_VALIDATE_PEER_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_raftstore_pd_validate_peer_total",
            "Total number of raftstore pd worker validate peer task.",
            &["type"]
        ).unwrap();

    pub static ref SNAP_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_raftstore_snapshot_total",
            "Total number of raftstore snapshot processed.",
            &["type", "status"]
        ).unwrap();

    pub static ref CHECK_SPILT_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_raftstore_check_split_total",
            "Total number of raftstore split check.",
            &["type"]
        ).unwrap();

    pub static ref SNAP_HISTOGRAM: HistogramVec =
        register_histogram_vec!(
            "tikv_raftstore_snapshot_duration_seconds",
            "Bucketed histogram of raftstore snapshot process duration",
            &["type"],
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref CHECK_SPILT_HISTOGRAM: Histogram =
        register_histogram!(
            "tikv_raftstore_check_split_duration_seconds",
            "Bucketed histogram of raftstore split check duration",
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref COMPACT_RANGE_CF: HistogramVec =
        register_histogram_vec!(
            "tikv_compact_range_cf_duration_seconds",
            "Bucketed histogram of compact range for cf execution",
            &["cf"]
        ).unwrap();

    pub static ref REGION_HASH_HISTOGRAM: Histogram =
        register_histogram!(
            "tikv_raftstore_hash_duration_seconds",
            "Bucketed histogram of raftstore hash compution duration"
        ).unwrap();

    pub static ref APPLY_PROPOSAL: Histogram =
        register_histogram!(
            "tikv_raftstore_apply_proposal",
            "Proposal count of all regions in a mio tick",
            exponential_buckets(1.0, 2.0, 20).unwrap()
        ).unwrap();
}
<|endoftext|>


<commit_before>#![deny(warnings)]
#![feature(std_misc, thread_sleep)]

extern crate cargo_registry;
extern crate postgres;
extern crate semver;
extern crate time;

use std::env;
use std::collections::HashMap;
use std::time::Duration;

use cargo_registry::{VersionDownload, Version, Model};

static LIMIT: i64 = 1000;

#[allow(dead_code)] // dead in tests
fn main() {
    let daemon = env::args().nth(1).as_ref().map(|s| &s[..])
                    == Some("daemon");
    let sleep = env::args().nth(2).map(|s| s.parse::<i64>().unwrap());
    loop {
        let conn = postgres::Connection::connect(&env("DATABASE_URL")[..],
                                                 &postgres::SslMode::None).unwrap();
        {
            let tx = conn.transaction().unwrap();
            update(&tx).unwrap();
            tx.set_commit();
            tx.finish().unwrap();
        }
        drop(conn);
        if daemon {
            #[allow(deprecated)]
            fn do_sleep(sleep: Option<i64>) {
                std::thread::sleep(Duration::seconds(sleep.unwrap()));
            }
            do_sleep(sleep);
        } else {
            break
        }
    }
}

fn env(s: &str) -> String {
    match env::var(s).ok() {
        Some(s) => s,
        None => panic!("must have `{}` defined", s),
    }
}

fn update(tx: &postgres::Transaction) -> postgres::Result<()> {
    let mut max = 0;
    loop {
        let tx = try!(tx.transaction());
        {
            let stmt = try!(tx.prepare("SELECT * FROM version_downloads \
                                        WHERE processed = FALSE AND id > $1
                                        ORDER BY id ASC
                                        LIMIT $2"));
            let mut rows = try!(stmt.query(&[&max, &LIMIT]));
            match try!(collect(&tx, &mut rows)) {
                None => break,
                Some(m) => max = m,
            }
        }
        tx.set_commit();
        try!(tx.finish());
    }
    Ok(())
}

fn collect(tx: &postgres::Transaction,
           rows: &mut postgres::Rows) -> postgres::Result<Option<i32>> {

    // Anything older than 24 hours ago will be frozen and will not be queried
    // against again.
    let cutoff = time::now_utc().to_timespec();
    let cutoff = cutoff + Duration::days(-1);

    let mut map = HashMap::new();
    for row in rows.iter() {
        let download: VersionDownload = Model::from_row(&row);
        assert!(map.insert(download.id, download).is_none());
    }
    println!("updating {} versions (cutoff {})", map.len(),
             time::at(cutoff).rfc822());
    if map.len() == 0 { return Ok(None) }

    let mut max = 0;
    let mut total = 0;
    for (id, download) in map.iter() {
        if *id > max { max = *id; }
        if download.date > cutoff && download.counted == download.downloads {
            continue
        }
        let amt = download.downloads - download.counted;

        let crate_id = Version::find(tx, download.version_id).unwrap().crate_id;

        // Update the total number of version downloads
        try!(tx.execute("UPDATE versions
                         SET downloads = downloads + $1
                         WHERE id = $2",
                        &[&amt, &download.version_id]));
        // Update the total number of crate downloads
        try!(tx.execute("UPDATE crates SET downloads = downloads + $1
                         WHERE id = $2", &[&amt, &crate_id]));

        // Update the total number of crate downloads for today
        let cnt = try!(tx.execute("UPDATE crate_downloads
                                   SET downloads = downloads + $2
                                   WHERE crate_id = $1 AND date = date($3)",
                                  &[&crate_id, &amt, &download.date]));
        if cnt == 0 {
            try!(tx.execute("INSERT INTO crate_downloads
                             (crate_id, downloads, date)
                             VALUES ($1, $2, $3)",
                            &[&crate_id, &amt, &download.date]));
        }

        // Flag this row as having been processed if we're passed the cutoff,
        // and unconditionally increment the number of counted downloads.
        try!(tx.execute("UPDATE version_downloads
                         SET processed = $2, counted = counted + $3
                         WHERE id = $1",
                        &[id, &(download.date < cutoff), &amt]));
        total += amt as i64;
    }

    // After everything else is done, update the global counter of total
    // downloads.
    try!(tx.execute("UPDATE metadata SET total_downloads = total_downloads + $1",
                    &[&total]));

    Ok(Some(max))
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use postgres;
    use semver;

    use cargo_registry::{Version, Crate, User};

    fn conn() -> postgres::Connection {
        postgres::Connection::connect(&::env("TEST_DATABASE_URL")[..],
                                      &postgres::SslMode::None).unwrap()
    }

    fn user(conn: &postgres::Transaction) -> User{
        User::find_or_insert(conn, "login", None, None, None,
                             "access_token", "api_token").unwrap()
    }

    fn crate_downloads(tx: &postgres::Transaction, id: i32, expected: usize) {
        let stmt = tx.prepare("SELECT * FROM crate_downloads
                               WHERE crate_id = $1").unwrap();
        let dl: i32 = stmt.query(&[&id]).unwrap().iter()
                          .next().unwrap().get("downloads");
        assert_eq!(dl, expected as i32);
    }

    #[test]
    fn increment() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None, &None,
                                          &None, &None, &[], &None, &None,
                                          &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, false)",
                   &[&version.id]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, true)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 1);
        assert_eq!(Crate::find(&tx, krate.id).unwrap().downloads, 1);
        crate_downloads(&tx, krate.id, 1);
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 1);
    }

    #[test]
    fn set_processed_true() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None,
                                          &None, &None, &None, &[], &None,
                                          &None, &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 2, 2, current_date - interval '2 days', false)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        let stmt = tx.prepare("SELECT processed FROM version_downloads
                               WHERE version_id = $1").unwrap();
        let processed: bool = stmt.query(&[&version.id]).unwrap().iter()
                                  .next().unwrap().get("processed");
        assert!(processed);
    }

    #[test]
    fn increment_a_little() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None,
                                          &None, &None, &None, &[], &None,
                                          &None, &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 2, 1, current_date, false)",
                   &[&version.id]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, false)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 2);
        assert_eq!(Crate::find(&tx, krate.id).unwrap().downloads, 2);
        crate_downloads(&tx, krate.id, 2);
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 2);
    }
}
<commit_msg>Don't always use nested transactions when updating downloads<commit_after>#![deny(warnings)]
#![feature(std_misc, thread_sleep)]

extern crate cargo_registry;
extern crate postgres;
extern crate semver;
extern crate time;

use std::env;
use std::collections::HashMap;
use std::time::Duration;

use cargo_registry::{VersionDownload, Version, Model};

static LIMIT: i64 = 1000;

#[allow(dead_code)] // dead in tests
fn main() {
    let daemon = env::args().nth(1).as_ref().map(|s| &s[..])
                    == Some("daemon");
    let sleep = env::args().nth(2).map(|s| s.parse::<i64>().unwrap());
    loop {
        let conn = postgres::Connection::connect(&env("DATABASE_URL")[..],
                                                 &postgres::SslMode::None).unwrap();
        update(&conn).unwrap();
        drop(conn);
        if daemon {
            std::thread::sleep(Duration::seconds(sleep.unwrap()));
        } else {
            break
        }
    }
}

fn env(s: &str) -> String {
    match env::var(s).ok() {
        Some(s) => s,
        None => panic!("must have `{}` defined", s),
    }
}

fn update(conn: &postgres::GenericConnection) -> postgres::Result<()> {
    let mut max = 0;
    loop {
        let tx = try!(conn.transaction());
        {
            let stmt = try!(tx.prepare("SELECT * FROM version_downloads \
                                        WHERE processed = FALSE AND id > $1
                                        ORDER BY id ASC
                                        LIMIT $2"));
            let mut rows = try!(stmt.query(&[&max, &LIMIT]));
            match try!(collect(&tx, &mut rows)) {
                None => break,
                Some(m) => max = m,
            }
        }
        tx.set_commit();
        try!(tx.finish());
    }
    Ok(())
}

fn collect(tx: &postgres::Transaction,
           rows: &mut postgres::Rows) -> postgres::Result<Option<i32>> {

    // Anything older than 24 hours ago will be frozen and will not be queried
    // against again.
    let cutoff = time::now_utc().to_timespec();
    let cutoff = cutoff + Duration::days(-1);

    let mut map = HashMap::new();
    for row in rows.iter() {
        let download: VersionDownload = Model::from_row(&row);
        assert!(map.insert(download.id, download).is_none());
    }
    println!("updating {} versions (cutoff {})", map.len(),
             time::at(cutoff).rfc822());
    if map.len() == 0 { return Ok(None) }

    let mut max = 0;
    let mut total = 0;
    for (id, download) in map.iter() {
        if *id > max { max = *id; }
        if download.date > cutoff && download.counted == download.downloads {
            continue
        }
        let amt = download.downloads - download.counted;

        let crate_id = Version::find(tx, download.version_id).unwrap().crate_id;

        // Update the total number of version downloads
        try!(tx.execute("UPDATE versions
                         SET downloads = downloads + $1
                         WHERE id = $2",
                        &[&amt, &download.version_id]));
        // Update the total number of crate downloads
        try!(tx.execute("UPDATE crates SET downloads = downloads + $1
                         WHERE id = $2", &[&amt, &crate_id]));

        // Update the total number of crate downloads for today
        let cnt = try!(tx.execute("UPDATE crate_downloads
                                   SET downloads = downloads + $2
                                   WHERE crate_id = $1 AND date = date($3)",
                                  &[&crate_id, &amt, &download.date]));
        if cnt == 0 {
            try!(tx.execute("INSERT INTO crate_downloads
                             (crate_id, downloads, date)
                             VALUES ($1, $2, $3)",
                            &[&crate_id, &amt, &download.date]));
        }

        // Flag this row as having been processed if we're passed the cutoff,
        // and unconditionally increment the number of counted downloads.
        try!(tx.execute("UPDATE version_downloads
                         SET processed = $2, counted = counted + $3
                         WHERE id = $1",
                        &[id, &(download.date < cutoff), &amt]));
        total += amt as i64;
    }

    // After everything else is done, update the global counter of total
    // downloads.
    try!(tx.execute("UPDATE metadata SET total_downloads = total_downloads + $1",
                    &[&total]));

    Ok(Some(max))
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use postgres;
    use semver;

    use cargo_registry::{Version, Crate, User};

    fn conn() -> postgres::Connection {
        postgres::Connection::connect(&::env("TEST_DATABASE_URL")[..],
                                      &postgres::SslMode::None).unwrap()
    }

    fn user(conn: &postgres::Transaction) -> User{
        User::find_or_insert(conn, "login", None, None, None,
                             "access_token", "api_token").unwrap()
    }

    fn crate_downloads(tx: &postgres::Transaction, id: i32, expected: usize) {
        let stmt = tx.prepare("SELECT * FROM crate_downloads
                               WHERE crate_id = $1").unwrap();
        let dl: i32 = stmt.query(&[&id]).unwrap().iter()
                          .next().unwrap().get("downloads");
        assert_eq!(dl, expected as i32);
    }

    #[test]
    fn increment() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None, &None,
                                          &None, &None, &[], &None, &None,
                                          &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, false)",
                   &[&version.id]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, true)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 1);
        assert_eq!(Crate::find(&tx, krate.id).unwrap().downloads, 1);
        crate_downloads(&tx, krate.id, 1);
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 1);
    }

    #[test]
    fn set_processed_true() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None,
                                          &None, &None, &None, &[], &None,
                                          &None, &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 2, 2, current_date - interval '2 days', false)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        let stmt = tx.prepare("SELECT processed FROM version_downloads
                               WHERE version_id = $1").unwrap();
        let processed: bool = stmt.query(&[&version.id]).unwrap().iter()
                                  .next().unwrap().get("processed");
        assert!(processed);
    }

    #[test]
    fn increment_a_little() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None,
                                          &None, &None, &None, &[], &None,
                                          &None, &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 2, 1, current_date, false)",
                   &[&version.id]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, false)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 2);
        assert_eq!(Crate::find(&tx, krate.id).unwrap().downloads, 2);
        crate_downloads(&tx, krate.id, 2);
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 2);
    }
}
<|endoftext|>


<commit_before>/// Represents a PhotoSize object
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct PhotoSize {
    pub file_id: String,
    pub width: i64,
    pub height: i64,
    pub file_size: Option<i64>,
}
<commit_msg>added photo_path to PhotoSize because it is missing in telegram docs but can be present<commit_after>/// Represents a PhotoSize object
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct PhotoSize {
    pub file_id: String,
    pub file_path: Option<String>,
    pub width: i64,
    pub height: i64,
    pub file_size: Option<i64>,
}
<|endoftext|>