<commit_before>use cgmath::{
    Angle as _, EuclideanSpace as _, InnerSpace as _, Rotation as _, Rotation3 as _, Transform as _,
};
use std::ops::Range;

pub type Transform = cgmath::Decomposed<cgmath::Vector3<f32>, cgmath::Quaternion<f32>>;

#[derive(Copy, Clone)]
pub enum Projection {
    Ortho {
        p: cgmath::Ortho<f32>,
        original: (u16, u16),
    },
    Perspective(cgmath::PerspectiveFov<f32>),
}

impl Projection {
    pub fn ortho(w: u16, h: u16, depth: Range<f32>) -> Self {
        Projection::Ortho {
            p: cgmath::Ortho {
                left: -0.5 * w as f32,
                right: 0.5 * w as f32,
                top: -0.5 * h as f32,
                bottom: 0.5 * h as f32,
                near: depth.start,
                far: depth.end,
            },
            original: (w, h),
        }
    }

    pub fn update(&mut self, w: u16, h: u16) {
        match *self {
            Projection::Ortho {
                ref mut p,
                ref mut original,
            } => {
                let scale_x = w as f32 / original.0 as f32;
                let scale_y = h as f32 / original.1 as f32;
                let center_x = 0.5 * p.left + 0.5 * p.right;
                let center_y = 0.5 * p.top + 0.5 * p.bottom;
                *original = (w, h);
                p.left = center_x - scale_x * (center_x - p.left);
                p.right = center_x - scale_x * (center_x - p.right);
                p.top = center_y - scale_y * (center_y - p.top);
                p.bottom = center_y - scale_y * (center_y - p.bottom);
            }
            Projection::Perspective(ref mut p) => {
                p.aspect = w as f32 / h as f32;
            }
        }
    }

    pub fn to_matrix(&self) -> cgmath::Matrix4<f32> {
        match *self {
            Projection::Ortho { p, .. } => p.into(),
            Projection::Perspective(p) => p.into(),
        }
    }
}

#[derive(Copy, Clone)]
pub struct Camera {
    pub loc: cgmath::Vector3<f32>,
    pub rot: cgmath::Quaternion<f32>,
    // this non-uniform scale is used to make the camera left-handed
    pub scale: cgmath::Vector3<f32>,
    pub proj: Projection,
}

#[derive(Debug, Copy, Clone)]
pub struct Follow {
    pub angle_x: cgmath::Deg<f32>,
    pub offset: cgmath::Vector3<f32>,
    pub speed: f32,
}

#[derive(Copy, Clone)]
pub struct Direction {
    pub view: cgmath::Vector3<f32>,
    pub height: f32,
}

pub fn compute_twist(
    quat: cgmath::Quaternion<f32>,
    direction: cgmath::Vector3<f32>,
) -> cgmath::Quaternion<f32> {
    let p = quat.v.project_on(direction);
    cgmath::Quaternion::from_sv(quat.s, p).normalize()
}

impl Camera {
    fn _scale_vec(&self, vec: cgmath::Vector3<f32>) -> cgmath::Vector3<f32> {
        cgmath::Vector3::new(
            self.scale.x * vec.x,
            self.scale.y * vec.y,
            self.scale.z * vec.z,
        )
    }

    pub fn dir(&self) -> cgmath::Vector3<f32> {
        self.rot * -cgmath::Vector3::unit_z()
    }

    pub fn depth_range(&self) -> Range<f32> {
        match self.proj {
            Projection::Ortho { p, .. } => p.near..p.far,
            Projection::Perspective(p) => p.near..p.far,
        }
    }

    fn get_proj_matrix(&self) -> cgmath::Matrix4<f32> {
        let mut proj = self.proj.to_matrix();
        // convert from GL's depth of [-1,1] to wgpu/gfx-rs [0,1]
        proj.x.z = 0.5 * (proj.x.z + proj.x.w);
        proj.y.z = 0.5 * (proj.y.z + proj.y.w);
        proj.z.z = 0.5 * (proj.z.z + proj.z.w);
        proj.w.z = 0.5 * (proj.w.z + proj.w.w);
        proj
    }

    fn view_transform(&self) -> Transform {
        cgmath::Decomposed {
            scale: 1.0,
            rot: self.rot,
            disp: self.loc,
        }
    }

    fn scale_matrix(&self) -> cgmath::Matrix4<f32> {
        cgmath::Matrix4::from_nonuniform_scale(self.scale.x, self.scale.y, self.scale.z)
    }

    pub fn get_view_proj(&self) -> cgmath::Matrix4<f32> {
        let view = self.view_transform();
        let view_mx = cgmath::Matrix4::from(view.inverse_transform().unwrap());
        self.get_proj_matrix() * self.scale_matrix() * view_mx
    }

    fn intersect_ray_height(&self, dir: cgmath::Vector3<f32>, height: f32) -> cgmath::Point3<f32> {
        let t_raw = (height - self.loc.z) / dir.z;
        let range = self.depth_range();
        let t = range.start.max(t_raw).min(range.end);
        cgmath::Point3::from_vec(self.loc) + t * dir
    }

    pub fn intersect_height(&self, height: f32) -> cgmath::Point3<f32> {
        let dir = self.dir();
        self.intersect_ray_height(dir, height)
    }

    pub fn visible_bounds_at(&self, height: f32) -> Range<cgmath::Vector2<f32>> {
        let center = self.intersect_height(height).to_vec().truncate();
        let mut bounds = center..center;

        let proj = self.get_proj_matrix();
        let view = self.view_transform();
        let mx =
            cgmath::Matrix4::from(view) * self.scale_matrix() * proj.inverse_transform().unwrap();
        // Scale vectors in a way that makes their Z footprint to be -1 in local space.
        let scaler = 1.0 / self.depth_range().end;
        let ndc_points = [
            cgmath::vec2(-1.0, -1.0),
            cgmath::vec2(1.0, -1.0),
            cgmath::vec2(1.0, 1.0),
            cgmath::vec2(-1.0, 1.0),
        ];
        for ndc in &ndc_points {
            let wp = cgmath::Point3::from_homogeneous(mx * cgmath::vec4(ndc.x, ndc.y, 1.0, 1.0));
            let pt = if wp.z < self.loc.z {
                let dir = scaler * (wp - cgmath::Point3::from_vec(self.loc));
                self.intersect_ray_height(dir, height)
            } else {
                wp
            };
            bounds.start.x = bounds.start.x.min(pt.x);
            bounds.start.y = bounds.start.y.min(pt.y);
            bounds.end.x = bounds.end.x.max(pt.x);
            bounds.end.y = bounds.end.y.max(pt.y);
        }
        bounds
    }

    pub fn visible_bounds(&self) -> Range<cgmath::Vector2<f32>> {
        let lo = self.visible_bounds_at(0.0);
        let min = cgmath::vec2(self.loc.x.min(lo.start.x), self.loc.y.min(lo.start.y));
        let max = cgmath::vec2(self.loc.x.max(lo.end.x), self.loc.y.max(lo.end.y));
        min..max
    }

    pub fn bound_points(&self, height: f32) -> [cgmath::Point3<f32>; 4] {
        let vb = self.visible_bounds_at(height);
        [
            cgmath::Point3::new(vb.start.x, vb.start.y, height),
            cgmath::Point3::new(vb.end.x, vb.start.y, height),
            cgmath::Point3::new(vb.end.x, vb.end.y, height),
            cgmath::Point3::new(vb.start.x, vb.end.y, height),
        ]
    }

    pub fn follow(&mut self, target: &Transform, dt: f32, follow: &Follow) {
        let k = (dt * -follow.speed).exp();

        // Determine the Z axis rotation around the target
        let swing = cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_x(), follow.angle_x);
        let twist = compute_twist(target.rot, cgmath::Vector3::unit_z());
        let patch = cgmath::Quaternion::from_axis_angle(
            cgmath::Vector3::unit_z(),
            cgmath::Deg::turn_div_2(),
        );
        let rotation = patch * twist * swing;
        self.rot = (rotation * (1.0 - k) + self.rot * k).normalize();

        let location = target.disp + (patch * twist).rotate_vector(follow.offset);
        self.loc = location * (1.0 - k) + self.loc * k;
    }

    pub fn look_by(&mut self, target: &Transform, dir: &Direction) {
        debug_assert!(dir.view.z < 0.0);
        let k = (target.disp.z - self.loc.z) / -dir.view.z;
        self.loc = target.disp + dir.view * k;
        //self.rot = cgmath::Quaternion::look_at(dir.view, cgmath::Vector3::unit_y()).invert();
        self.rot =
            cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_x(), cgmath::Deg(30.0));
    }

    pub fn focus_on(&mut self, target: &Transform) {
        use cgmath::Angle;
        self.loc = target.disp + cgmath::vec3(0.0, -64.0, 40.0);
        self.rot = cgmath::Quaternion::from_axis_angle(
            cgmath::Vector3::unit_x(),
            cgmath::Rad::turn_div_6(),
        );
    }

    pub fn rotate_focus(
        &mut self,
        target: &Transform,
        hor: cgmath::Rad<f32>,
        ver: cgmath::Rad<f32>,
    ) {
        use cgmath::{Decomposed, One, Zero};
        let mut view = Decomposed {
            scale: 1.0,
            rot: self.rot,
            disp: self.loc,
        };
        // old mv: inv(view) * model
        if hor.0 != 0.0 {
            // inv(new) = inv(view) * post * mid * pre
            // new = pre^ * mid^ post^ * view = post * mid^ * pre * view
            let pre = Decomposed {
                scale: 1.0,
                rot: cgmath::Quaternion::one(),
                disp: -target.disp,
            };
            let post = Decomposed {
                scale: 1.0,
                rot: cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), hor),
                disp: target.disp,
            };
            view = post.concat(&pre.concat(&view));
        }
        if ver.0 != 0.0 {
            // inv(new) * model = inv(view) * model * mid
            // model^ * new = mid^ * model^ * view
            // new = model * mid^ * model^ * view
            let target_inv = target.inverse_transform().unwrap();
            let axis_local = target_inv.rot * self.rot * cgmath::Vector3::unit_x();
            let mid = Decomposed {
                scale: 1.0,
                rot: cgmath::Quaternion::from_axis_angle(axis_local, -ver),
                disp: cgmath::Vector3::zero(),
            };
            view = target.concat(&mid.concat(&target_inv.concat(&view)));
        }
        self.loc = view.disp;
        self.rot = view.rot;
    }

    pub fn front_face(&self) -> wgpu::FrontFace {
        if self.scale.x * self.scale.y > 0.0 {
            wgpu::FrontFace::Cw
        } else {
            wgpu::FrontFace::Ccw
        }
    }
}
<commit_msg>Stabilize the camera when the mechos turns upside down<commit_after>use cgmath::{
    Angle as _, EuclideanSpace as _, InnerSpace as _, Rotation as _, Rotation3 as _, Transform as _,
};
use std::ops::Range;

pub type Transform = cgmath::Decomposed<cgmath::Vector3<f32>, cgmath::Quaternion<f32>>;

#[derive(Copy, Clone)]
pub enum Projection {
    Ortho {
        p: cgmath::Ortho<f32>,
        original: (u16, u16),
    },
    Perspective(cgmath::PerspectiveFov<f32>),
}

impl Projection {
    pub fn ortho(w: u16, h: u16, depth: Range<f32>) -> Self {
        Projection::Ortho {
            p: cgmath::Ortho {
                left: -0.5 * w as f32,
                right: 0.5 * w as f32,
                top: -0.5 * h as f32,
                bottom: 0.5 * h as f32,
                near: depth.start,
                far: depth.end,
            },
            original: (w, h),
        }
    }

    pub fn update(&mut self, w: u16, h: u16) {
        match *self {
            Projection::Ortho {
                ref mut p,
                ref mut original,
            } => {
                let scale_x = w as f32 / original.0 as f32;
                let scale_y = h as f32 / original.1 as f32;
                let center_x = 0.5 * p.left + 0.5 * p.right;
                let center_y = 0.5 * p.top + 0.5 * p.bottom;
                *original = (w, h);
                p.left = center_x - scale_x * (center_x - p.left);
                p.right = center_x - scale_x * (center_x - p.right);
                p.top = center_y - scale_y * (center_y - p.top);
                p.bottom = center_y - scale_y * (center_y - p.bottom);
            }
            Projection::Perspective(ref mut p) => {
                p.aspect = w as f32 / h as f32;
            }
        }
    }

    pub fn to_matrix(&self) -> cgmath::Matrix4<f32> {
        match *self {
            Projection::Ortho { p, .. } => p.into(),
            Projection::Perspective(p) => p.into(),
        }
    }
}

#[derive(Copy, Clone)]
pub struct Camera {
    pub loc: cgmath::Vector3<f32>,
    pub rot: cgmath::Quaternion<f32>,
    // this non-uniform scale is used to make the camera left-handed
    pub scale: cgmath::Vector3<f32>,
    pub proj: Projection,
}

#[derive(Debug, Copy, Clone)]
pub struct Follow {
    pub angle_x: cgmath::Deg<f32>,
    pub offset: cgmath::Vector3<f32>,
    pub speed: f32,
}

#[derive(Copy, Clone)]
pub struct Direction {
    pub view: cgmath::Vector3<f32>,
    pub height: f32,
}

pub fn compute_twist(
    quat: cgmath::Quaternion<f32>,
    direction: cgmath::Vector3<f32>,
) -> cgmath::Quaternion<f32> {
    let p = quat.v.project_on(direction);
    cgmath::Quaternion::from_sv(quat.s, p).normalize()
}

impl Camera {
    fn _scale_vec(&self, vec: cgmath::Vector3<f32>) -> cgmath::Vector3<f32> {
        cgmath::Vector3::new(
            self.scale.x * vec.x,
            self.scale.y * vec.y,
            self.scale.z * vec.z,
        )
    }

    pub fn dir(&self) -> cgmath::Vector3<f32> {
        self.rot * -cgmath::Vector3::unit_z()
    }

    pub fn depth_range(&self) -> Range<f32> {
        match self.proj {
            Projection::Ortho { p, .. } => p.near..p.far,
            Projection::Perspective(p) => p.near..p.far,
        }
    }

    fn get_proj_matrix(&self) -> cgmath::Matrix4<f32> {
        let mut proj = self.proj.to_matrix();
        // convert from GL's depth of [-1,1] to wgpu/gfx-rs [0,1]
        proj.x.z = 0.5 * (proj.x.z + proj.x.w);
        proj.y.z = 0.5 * (proj.y.z + proj.y.w);
        proj.z.z = 0.5 * (proj.z.z + proj.z.w);
        proj.w.z = 0.5 * (proj.w.z + proj.w.w);
        proj
    }

    fn view_transform(&self) -> Transform {
        cgmath::Decomposed {
            scale: 1.0,
            rot: self.rot,
            disp: self.loc,
        }
    }

    fn scale_matrix(&self) -> cgmath::Matrix4<f32> {
        cgmath::Matrix4::from_nonuniform_scale(self.scale.x, self.scale.y, self.scale.z)
    }

    pub fn get_view_proj(&self) -> cgmath::Matrix4<f32> {
        let view = self.view_transform();
        let view_mx = cgmath::Matrix4::from(view.inverse_transform().unwrap());
        self.get_proj_matrix() * self.scale_matrix() * view_mx
    }

    fn intersect_ray_height(&self, dir: cgmath::Vector3<f32>, height: f32) -> cgmath::Point3<f32> {
        let t_raw = (height - self.loc.z) / dir.z;
        let range = self.depth_range();
        let t = range.start.max(t_raw).min(range.end);
        cgmath::Point3::from_vec(self.loc) + t * dir
    }

    pub fn intersect_height(&self, height: f32) -> cgmath::Point3<f32> {
        let dir = self.dir();
        self.intersect_ray_height(dir, height)
    }

    pub fn visible_bounds_at(&self, height: f32) -> Range<cgmath::Vector2<f32>> {
        let center = self.intersect_height(height).to_vec().truncate();
        let mut bounds = center..center;

        let proj = self.get_proj_matrix();
        let view = self.view_transform();
        let mx =
            cgmath::Matrix4::from(view) * self.scale_matrix() * proj.inverse_transform().unwrap();
        // Scale vectors in a way that makes their Z footprint to be -1 in local space.
        let scaler = 1.0 / self.depth_range().end;
        let ndc_points = [
            cgmath::vec2(-1.0, -1.0),
            cgmath::vec2(1.0, -1.0),
            cgmath::vec2(1.0, 1.0),
            cgmath::vec2(-1.0, 1.0),
        ];
        for ndc in &ndc_points {
            let wp = cgmath::Point3::from_homogeneous(mx * cgmath::vec4(ndc.x, ndc.y, 1.0, 1.0));
            let pt = if wp.z < self.loc.z {
                let dir = scaler * (wp - cgmath::Point3::from_vec(self.loc));
                self.intersect_ray_height(dir, height)
            } else {
                wp
            };
            bounds.start.x = bounds.start.x.min(pt.x);
            bounds.start.y = bounds.start.y.min(pt.y);
            bounds.end.x = bounds.end.x.max(pt.x);
            bounds.end.y = bounds.end.y.max(pt.y);
        }
        bounds
    }

    pub fn visible_bounds(&self) -> Range<cgmath::Vector2<f32>> {
        let lo = self.visible_bounds_at(0.0);
        let min = cgmath::vec2(self.loc.x.min(lo.start.x), self.loc.y.min(lo.start.y));
        let max = cgmath::vec2(self.loc.x.max(lo.end.x), self.loc.y.max(lo.end.y));
        min..max
    }

    pub fn bound_points(&self, height: f32) -> [cgmath::Point3<f32>; 4] {
        let vb = self.visible_bounds_at(height);
        [
            cgmath::Point3::new(vb.start.x, vb.start.y, height),
            cgmath::Point3::new(vb.end.x, vb.start.y, height),
            cgmath::Point3::new(vb.end.x, vb.end.y, height),
            cgmath::Point3::new(vb.start.x, vb.end.y, height),
        ]
    }

    pub fn follow(&mut self, target: &Transform, dt: f32, follow: &Follow) {
        let k = (dt * -follow.speed).exp();

        // Determine the Z axis rotation around the target
        let swing = cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_x(), follow.angle_x);
        let is_up = target.rot.rotate_vector(cgmath::Vector3::unit_z()).z >= 0.0;
        let twist = compute_twist(
            target.rot,
            if is_up {
                cgmath::Vector3::unit_z()
            } else {
                -cgmath::Vector3::unit_z()
            },
        );
        let patch = cgmath::Quaternion::from_axis_angle(
            cgmath::Vector3::unit_z(),
            cgmath::Deg::turn_div_2(),
        );
        let rotation = patch * twist * swing;
        self.rot = (rotation * (1.0 - k) + self.rot * k).normalize();

        let location = target.disp + (patch * twist).rotate_vector(follow.offset);
        self.loc = location * (1.0 - k) + self.loc * k;
    }

    pub fn look_by(&mut self, target: &Transform, dir: &Direction) {
        debug_assert!(dir.view.z < 0.0);
        let k = (target.disp.z - self.loc.z) / -dir.view.z;
        self.loc = target.disp + dir.view * k;
        //self.rot = cgmath::Quaternion::look_at(dir.view, cgmath::Vector3::unit_y()).invert();
        self.rot =
            cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_x(), cgmath::Deg(30.0));
    }

    pub fn focus_on(&mut self, target: &Transform) {
        use cgmath::Angle;
        self.loc = target.disp + cgmath::vec3(0.0, -64.0, 40.0);
        self.rot = cgmath::Quaternion::from_axis_angle(
            cgmath::Vector3::unit_x(),
            cgmath::Rad::turn_div_6(),
        );
    }

    pub fn rotate_focus(
        &mut self,
        target: &Transform,
        hor: cgmath::Rad<f32>,
        ver: cgmath::Rad<f32>,
    ) {
        use cgmath::{Decomposed, One, Zero};
        let mut view = Decomposed {
            scale: 1.0,
            rot: self.rot,
            disp: self.loc,
        };
        // old mv: inv(view) * model
        if hor.0 != 0.0 {
            // inv(new) = inv(view) * post * mid * pre
            // new = pre^ * mid^ post^ * view = post * mid^ * pre * view
            let pre = Decomposed {
                scale: 1.0,
                rot: cgmath::Quaternion::one(),
                disp: -target.disp,
            };
            let post = Decomposed {
                scale: 1.0,
                rot: cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), hor),
                disp: target.disp,
            };
            view = post.concat(&pre.concat(&view));
        }
        if ver.0 != 0.0 {
            // inv(new) * model = inv(view) * model * mid
            // model^ * new = mid^ * model^ * view
            // new = model * mid^ * model^ * view
            let target_inv = target.inverse_transform().unwrap();
            let axis_local = target_inv.rot * self.rot * cgmath::Vector3::unit_x();
            let mid = Decomposed {
                scale: 1.0,
                rot: cgmath::Quaternion::from_axis_angle(axis_local, -ver),
                disp: cgmath::Vector3::zero(),
            };
            view = target.concat(&mid.concat(&target_inv.concat(&view)));
        }
        self.loc = view.disp;
        self.rot = view.rot;
    }

    pub fn front_face(&self) -> wgpu::FrontFace {
        if self.scale.x * self.scale.y > 0.0 {
            wgpu::FrontFace::Cw
        } else {
            wgpu::FrontFace::Ccw
        }
    }
}
<|endoftext|>


<commit_before>//! Style.

use std::fmt;

derive_csi_sequence!("Reset SGR parameters.", Reset, "m");
derive_csi_sequence!("Bold text.", Bold, "1m");
derive_csi_sequence!("Fainted text (not widely supported).", Faint, "2m");
derive_csi_sequence!("Italic text.", Italic, "3m");
derive_csi_sequence!("Underlined text.", Underline, "4m");
derive_csi_sequence!("Blinking text (not widely supported).", Blink, "5m");
derive_csi_sequence!("Inverted colors (negative mode).", Invert, "7m");
derive_csi_sequence!("Crossed out text (not widely supported).", CrossedOut, "9m");
derive_csi_sequence!("Framed text (not widely supported).", Framed, "51m");
<commit_msg>Add escapes to undo styles<commit_after>//! Style.

use std::fmt;

derive_csi_sequence!("Reset SGR parameters.", Reset, "m");
derive_csi_sequence!("Bold text.", Bold, "1m");
derive_csi_sequence!("Fainted text (not widely supported).", Faint, "2m");
derive_csi_sequence!("Italic text.", Italic, "3m");
derive_csi_sequence!("Underlined text.", Underline, "4m");
derive_csi_sequence!("Blinking text (not widely supported).", Blink, "5m");
derive_csi_sequence!("Inverted colors (negative mode).", Invert, "7m");
derive_csi_sequence!("Crossed out text (not widely supported).", CrossedOut, "9m");
derive_csi_sequence!("Undo bold text.", NoBold, "21m");
derive_csi_sequence!("Undo fainted text (not widely supported).", NoFaint, "22m");
derive_csi_sequence!("Undo italic text.", NoItalic, "23m");
derive_csi_sequence!("Undo underlined text.", NoUnderline, "24m");
derive_csi_sequence!("Undo blinking text (not widely supported).", NoBlink, "25m");
derive_csi_sequence!("Undo inverted colors (negative mode).", NoInvert, "27m");
derive_csi_sequence!("Undo crossed out text (not widely supported).", NoCrossedOut, "29m");
derive_csi_sequence!("Framed text (not widely supported).", Framed, "51m");
<|endoftext|>


<commit_before>#[allow(missing_docs)]
#[macro_export]
macro_rules! info {
    ($($args:tt)+) => {
        log::info!("{}", paris::formatter::colorize_string(format!($($args)*)));
    };
}

#[allow(missing_docs)]
#[macro_export]
macro_rules! debug {
    ($($args:tt)+) => {
        log::debug!("{}", paris::formatter::colorize_string(format!($($args)*)));
    };
}

#[allow(missing_docs)]
#[macro_export]
macro_rules! trace {
    ($($args:tt)+) => {
        log::trace!("{}", paris::formatter::colorize_string(format!($($args)*)));
    };
}

#[allow(missing_docs)]
#[macro_export]
macro_rules! warn {
    ($($args:tt)+) => {
        log::warn!("{}", paris::formatter::colorize_string(format!($($args)*)));
    };
}

#[allow(missing_docs)]
#[macro_export]
macro_rules! error {
    ($($args:tt)+) => {
        log::error!("{}", paris::formatter::colorize_string(format!($($args)*)));
    };
}
<commit_msg>paris_macros: add macros documentation from log crate<commit_after>/// Logs a message at the info level.
///
/// # Examples
///
/// ```edition2018
/// use log::info;
///
/// # fn main() {
/// # struct Connection { port: u32, speed: f32 }
/// let conn_info = Connection { port: 40, speed: 3.20 };
///
/// info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed);
/// info!(target: "connection_events", "Successfull connection, port: {}, speed: {}",
///       conn_info.port, conn_info.speed);
/// # }
/// ```
#[macro_export]
macro_rules! info {
    ($($args:tt)+) => {
        log::info!("{}", paris::formatter::colorize_string(format!($($args)*)));
    };
}

/// Logs a message at the debug level.
///
/// # Examples
///
/// ```edition2018
/// use log::debug;
///
/// # fn main() {
/// # struct Position { x: f32, y: f32 }
/// let pos = Position { x: 3.234, y: -1.223 };
///
/// debug!("New position: x: {}, y: {}", pos.x, pos.y);
/// debug!(target: "app_events", "New position: x: {}, y: {}", pos.x, pos.y);
/// # }
/// ```
#[macro_export]
macro_rules! debug {
    ($($args:tt)+) => {
        log::debug!("{}", paris::formatter::colorize_string(format!($($args)*)));
    };
}

/// Logs a message at the trace level.
///
/// # Examples
///
/// ```edition2018
/// use log::trace;
///
/// # fn main() {
/// # struct Position { x: f32, y: f32 }
/// let pos = Position { x: 3.234, y: -1.223 };
///
/// trace!("Position is: x: {}, y: {}", pos.x, pos.y);
/// trace!(target: "app_events", "x is {} and y is {}",
///        if pos.x >= 0.0 { "positive" } else { "negative" },
///        if pos.y >= 0.0 { "positive" } else { "negative" });
/// # }
/// ```
#[macro_export]
macro_rules! trace {
    ($($args:tt)+) => {
        log::trace!("{}", paris::formatter::colorize_string(format!($($args)*)));
    };
}

/// Logs a message at the warn level.
///
/// # Examples
///
/// ```edition2018
/// use log::warn;
///
/// # fn main() {
/// let warn_description = "Invalid Input";
///
/// warn!("Warning! {}!", warn_description);
/// warn!(target: "input_events", "App received warning: {}", warn_description);
/// # }
/// ```
#[macro_export]
macro_rules! warn {
    ($($args:tt)+) => {
        log::warn!("{}", paris::formatter::colorize_string(format!($($args)*)));
    };
}

/// Logs a message at the error level.
///
/// # Examples
///
/// ```edition2018
/// use log::error;
///
/// # fn main() {
/// let (err_info, port) = ("No connection", 22);
///
/// error!("Error: {} on port {}", err_info, port);
/// error!(target: "app_events", "App Error: {}, Port: {}", err_info, 22);
/// # }
/// ```
#[macro_export]
macro_rules! error {
    ($($args:tt)+) => {
        log::error!("{}", paris::formatter::colorize_string(format!($($args)*)));
    };
}
<|endoftext|>


<commit_before>//! Stream-based consumer implementation.

use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};

use futures::{ready, Stream};
use log::trace;
use tokio::time::{self, Duration, Instant};

use rdkafka_sys as rdsys;
use rdkafka_sys::types::*;

use crate::client::{ClientContext, NativeClient};
use crate::config::{ClientConfig, FromClientConfig, FromClientConfigAndContext, RDKafkaLogLevel};
use crate::consumer::base_consumer::BaseConsumer;
use crate::consumer::{Consumer, ConsumerContext, DefaultConsumerContext, Rebalance};
use crate::error::{KafkaError, KafkaResult};
use crate::message::BorrowedMessage;
use crate::statistics::Statistics;
use crate::topic_partition_list::TopicPartitionList;
use crate::util::{NativePtr, OnDrop, Timeout};

/// The [`ConsumerContext`] used by the [`StreamConsumer`]. This context will
/// automatically wake up the message stream when new data is available.
///
/// This type is not intended to be used directly. It will be automatically
/// created by the `StreamConsumer` when necessary.
pub struct StreamConsumerContext<C: ConsumerContext + 'static> {
    inner: C,
    waker: Arc<Mutex<Option<Waker>>>,
}

impl<C: ConsumerContext + 'static> StreamConsumerContext<C> {
    fn new(inner: C) -> StreamConsumerContext<C> {
        StreamConsumerContext {
            inner,
            waker: Arc::new(Mutex::new(None)),
        }
    }
}

impl<C: ConsumerContext + 'static> ClientContext for StreamConsumerContext<C> {
    fn log(&self, level: RDKafkaLogLevel, fac: &str, log_message: &str) {
        self.inner.log(level, fac, log_message)
    }

    fn stats(&self, statistics: Statistics) {
        self.inner.stats(statistics)
    }

    fn error(&self, error: KafkaError, reason: &str) {
        self.inner.error(error, reason)
    }
}

impl<C: ConsumerContext + 'static> ConsumerContext for StreamConsumerContext<C> {
    fn rebalance(
        &self,
        native_client: &NativeClient,
        err: RDKafkaRespErr,
        tpl: &mut TopicPartitionList,
    ) {
        self.inner.rebalance(native_client, err, tpl)
    }

    fn pre_rebalance<'a>(&self, rebalance: &Rebalance<'a>) {
        self.inner.pre_rebalance(rebalance)
    }

    fn post_rebalance<'a>(&self, rebalance: &Rebalance<'a>) {
        self.inner.post_rebalance(rebalance)
    }

    fn commit_callback(&self, result: KafkaResult<()>, offsets: &TopicPartitionList) {
        self.inner.commit_callback(result, offsets)
    }

    fn main_queue_min_poll_interval(&self) -> Timeout {
        self.inner.main_queue_min_poll_interval()
    }

    fn message_queue_nonempty_callback(&self) {
        if let Some(waker) = self.waker.lock().unwrap().take() {
            waker.wake();
        }
        self.inner.message_queue_nonempty_callback()
    }
}

/// A Kafka consumer implementing [`futures::Stream`].
pub struct MessageStream<'a, C: ConsumerContext + 'static> {
    consumer: &'a StreamConsumer<C>,
    err_interval: Option<Duration>,
    err_delay: Option<time::Delay>,
}

impl<'a, C: ConsumerContext + 'static> MessageStream<'a, C> {
    fn new(
        consumer: &'a StreamConsumer<C>,
        err_interval: Option<Duration>,
    ) -> MessageStream<'a, C> {
        MessageStream {
            consumer,
            err_interval,
            err_delay: None,
        }
    }

    fn context(&self) -> &StreamConsumerContext<C> {
        self.consumer.get_base_consumer().context()
    }

    fn client_ptr(&self) -> *mut RDKafka {
        self.consumer.client().native_ptr()
    }

    fn set_waker(&self, waker: Waker) {
        self.context()
            .waker
            .lock()
            .expect("lock poisoned")
            .replace(waker);
    }

    fn poll(&self) -> Option<KafkaResult<BorrowedMessage<'a>>> {
        unsafe {
            NativePtr::from_ptr(rdsys::rd_kafka_consumer_poll(self.client_ptr(), 0))
                .map(|p| BorrowedMessage::from_consumer(p, self.consumer))
        }
    }
}

impl<'a, C: ConsumerContext + 'a> Stream for MessageStream<'a, C> {
    type Item = KafkaResult<BorrowedMessage<'a>>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
        match self.poll() {
            None => {
                // The consumer queue is empty. Arrange to be notified when the
                // queue is no longer empty.
                self.set_waker(cx.waker().clone());
                if let Some(err_interval) = self.err_interval {
                    // We've been asked to periodically report that there are no
                    // new messages. Check if it's time to do so.
                    let mut err_delay = self
                        .err_delay
                        .get_or_insert_with(|| time::delay_for(err_interval));
                    ready!(Pin::new(&mut err_delay).poll(cx));
                    err_delay.reset(Instant::now() + err_interval);
                    Poll::Ready(Some(Err(KafkaError::NoMessageReceived)))
                } else {
                    Poll::Pending
                }
            }
            Some(message) => {
                self.err_delay = None;
                Poll::Ready(Some(message))
            }
        }
    }
}

/// A Kafka consumer providing a [`futures::Stream`] interface.
///
/// This consumer doesn't need to be polled manually since
/// [`StreamConsumer::start`] will launch a background polling task.
#[must_use = "Consumer polling thread will stop immediately if unused"]
pub struct StreamConsumer<C: ConsumerContext + 'static = DefaultConsumerContext> {
    consumer: Arc<BaseConsumer<StreamConsumerContext<C>>>,
    should_stop: Arc<AtomicBool>,
}

impl<C: ConsumerContext> Consumer<StreamConsumerContext<C>> for StreamConsumer<C> {
    fn get_base_consumer(&self) -> &BaseConsumer<StreamConsumerContext<C>> {
        Arc::as_ref(&self.consumer)
    }
}

impl FromClientConfig for StreamConsumer {
    fn from_config(config: &ClientConfig) -> KafkaResult<StreamConsumer> {
        StreamConsumer::from_config_and_context(config, DefaultConsumerContext)
    }
}

/// Creates a new `StreamConsumer` starting from a [`ClientConfig`].
impl<C: ConsumerContext> FromClientConfigAndContext<C> for StreamConsumer<C> {
    fn from_config_and_context(
        config: &ClientConfig,
        context: C,
    ) -> KafkaResult<StreamConsumer<C>> {
        let context = StreamConsumerContext::new(context);
        let stream_consumer = StreamConsumer {
            consumer: Arc::new(BaseConsumer::from_config_and_context(config, context)?),
            should_stop: Arc::new(AtomicBool::new(false)),
        };
        Ok(stream_consumer)
    }
}

impl<C: ConsumerContext> StreamConsumer<C> {
    /// Starts the stream consumer with default configuration (100ms polling
    /// interval and no `NoMessageReceived` notifications).
    ///
    /// **Note:** this method must be called from within the context of a Tokio
    /// runtime.
    pub fn start(&self) -> MessageStream<C> {
        self.start_with(Duration::from_millis(100), false)
    }

    /// Starts the stream consumer with the specified poll interval.
    ///
    /// If `no_message_error` is set to true, the returned `MessageStream` will
    /// yield an error of type `KafkaError::NoMessageReceived` every time the
    /// poll interval is reached and no message has been received.
    pub fn start_with(&self, poll_interval: Duration, no_message_error: bool) -> MessageStream<C> {
        // TODO: verify called once
        tokio::spawn({
            let consumer = self.consumer.clone();
            let should_stop = self.should_stop.clone();
            async move {
                trace!("Polling task loop started");
                let _on_drop = OnDrop(|| trace!("Polling task loop terminated"));
                while !should_stop.load(Ordering::Relaxed) {
                    unsafe { rdsys::rd_kafka_poll(consumer.client().native_ptr(), 0) };
                    time::delay_for(poll_interval).await;
                }
            }
        });
        let no_message_interval = match no_message_error {
            true => Some(poll_interval),
            false => None,
        };
        MessageStream::new(self, no_message_interval)
    }

    /// Stops the stream consumer.
    pub fn stop(&self) {
        self.should_stop.store(true, Ordering::Relaxed);
    }
}

impl<C: ConsumerContext> Drop for StreamConsumer<C> {
    fn drop(&mut self) {
        trace!("Destroy StreamConsumer");
        self.stop();
    }
}
<commit_msg>Fix race condition in StreamConsumer<commit_after>//! Stream-based consumer implementation.

use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};

use futures::{ready, Stream};
use log::trace;
use tokio::time::{self, Duration, Instant};

use rdkafka_sys as rdsys;
use rdkafka_sys::types::*;

use crate::client::{ClientContext, NativeClient};
use crate::config::{ClientConfig, FromClientConfig, FromClientConfigAndContext, RDKafkaLogLevel};
use crate::consumer::base_consumer::BaseConsumer;
use crate::consumer::{Consumer, ConsumerContext, DefaultConsumerContext, Rebalance};
use crate::error::{KafkaError, KafkaResult};
use crate::message::BorrowedMessage;
use crate::statistics::Statistics;
use crate::topic_partition_list::TopicPartitionList;
use crate::util::{NativePtr, OnDrop, Timeout};

/// The [`ConsumerContext`] used by the [`StreamConsumer`]. This context will
/// automatically wake up the message stream when new data is available.
///
/// This type is not intended to be used directly. It will be automatically
/// created by the `StreamConsumer` when necessary.
pub struct StreamConsumerContext<C: ConsumerContext + 'static> {
    inner: C,
    waker: Arc<Mutex<Option<Waker>>>,
}

impl<C: ConsumerContext + 'static> StreamConsumerContext<C> {
    fn new(inner: C) -> StreamConsumerContext<C> {
        StreamConsumerContext {
            inner,
            waker: Arc::new(Mutex::new(None)),
        }
    }
}

impl<C: ConsumerContext + 'static> ClientContext for StreamConsumerContext<C> {
    fn log(&self, level: RDKafkaLogLevel, fac: &str, log_message: &str) {
        self.inner.log(level, fac, log_message)
    }

    fn stats(&self, statistics: Statistics) {
        self.inner.stats(statistics)
    }

    fn error(&self, error: KafkaError, reason: &str) {
        self.inner.error(error, reason)
    }
}

impl<C: ConsumerContext + 'static> ConsumerContext for StreamConsumerContext<C> {
    fn rebalance(
        &self,
        native_client: &NativeClient,
        err: RDKafkaRespErr,
        tpl: &mut TopicPartitionList,
    ) {
        self.inner.rebalance(native_client, err, tpl)
    }

    fn pre_rebalance<'a>(&self, rebalance: &Rebalance<'a>) {
        self.inner.pre_rebalance(rebalance)
    }

    fn post_rebalance<'a>(&self, rebalance: &Rebalance<'a>) {
        self.inner.post_rebalance(rebalance)
    }

    fn commit_callback(&self, result: KafkaResult<()>, offsets: &TopicPartitionList) {
        self.inner.commit_callback(result, offsets)
    }

    fn main_queue_min_poll_interval(&self) -> Timeout {
        self.inner.main_queue_min_poll_interval()
    }

    fn message_queue_nonempty_callback(&self) {
        if let Some(waker) = self.waker.lock().unwrap().take() {
            waker.wake();
        }
        self.inner.message_queue_nonempty_callback()
    }
}

/// A Kafka consumer implementing [`futures::Stream`].
pub struct MessageStream<'a, C: ConsumerContext + 'static> {
    consumer: &'a StreamConsumer<C>,
    err_interval: Option<Duration>,
    err_delay: Option<time::Delay>,
}

impl<'a, C: ConsumerContext + 'static> MessageStream<'a, C> {
    fn new(
        consumer: &'a StreamConsumer<C>,
        err_interval: Option<Duration>,
    ) -> MessageStream<'a, C> {
        MessageStream {
            consumer,
            err_interval,
            err_delay: None,
        }
    }

    fn context(&self) -> &StreamConsumerContext<C> {
        self.consumer.get_base_consumer().context()
    }

    fn client_ptr(&self) -> *mut RDKafka {
        self.consumer.client().native_ptr()
    }

    fn set_waker(&self, waker: Waker) {
        self.context()
            .waker
            .lock()
            .expect("lock poisoned")
            .replace(waker);
    }

    fn poll(&self) -> Option<KafkaResult<BorrowedMessage<'a>>> {
        unsafe {
            NativePtr::from_ptr(rdsys::rd_kafka_consumer_poll(self.client_ptr(), 0))
                .map(|p| BorrowedMessage::from_consumer(p, self.consumer))
        }
    }
}

impl<'a, C: ConsumerContext + 'a> Stream for MessageStream<'a, C> {
    type Item = KafkaResult<BorrowedMessage<'a>>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
        // Unconditionally store the waker so that we are woken up if the queue
        // flips from non-empty to empty. We have to store the waker on every
        // call to poll in case this future migrates between tasks. We also need
        // to store the waker *before* calling poll to avoid a race where `poll`
        // returns None to indicate that the queue is empty, but the queue
        // becomes non-empty before we've installed the waker.
        self.set_waker(cx.waker().clone());

        match self.poll() {
            None => match self.err_interval {
                None => Poll::Pending,
                Some(err_interval) => {
                    // We've been asked to periodically report that there are no
                    // new messages. Check if it's time to do so.
                    let mut err_delay = self
                        .err_delay
                        .get_or_insert_with(|| time::delay_for(err_interval));
                    ready!(Pin::new(&mut err_delay).poll(cx));
                    err_delay.reset(Instant::now() + err_interval);
                    Poll::Ready(Some(Err(KafkaError::NoMessageReceived)))
                }
            },
            Some(message) => {
                self.err_delay = None;
                Poll::Ready(Some(message))
            }
        }
    }
}

/// A Kafka consumer providing a [`futures::Stream`] interface.
///
/// This consumer doesn't need to be polled manually since
/// [`StreamConsumer::start`] will launch a background polling task.
#[must_use = "Consumer polling thread will stop immediately if unused"]
pub struct StreamConsumer<C: ConsumerContext + 'static = DefaultConsumerContext> {
    consumer: Arc<BaseConsumer<StreamConsumerContext<C>>>,
    should_stop: Arc<AtomicBool>,
}

impl<C: ConsumerContext> Consumer<StreamConsumerContext<C>> for StreamConsumer<C> {
    fn get_base_consumer(&self) -> &BaseConsumer<StreamConsumerContext<C>> {
        Arc::as_ref(&self.consumer)
    }
}

impl FromClientConfig for StreamConsumer {
    fn from_config(config: &ClientConfig) -> KafkaResult<StreamConsumer> {
        StreamConsumer::from_config_and_context(config, DefaultConsumerContext)
    }
}

/// Creates a new `StreamConsumer` starting from a [`ClientConfig`].
impl<C: ConsumerContext> FromClientConfigAndContext<C> for StreamConsumer<C> {
    fn from_config_and_context(
        config: &ClientConfig,
        context: C,
    ) -> KafkaResult<StreamConsumer<C>> {
        let context = StreamConsumerContext::new(context);
        let stream_consumer = StreamConsumer {
            consumer: Arc::new(BaseConsumer::from_config_and_context(config, context)?),
            should_stop: Arc::new(AtomicBool::new(false)),
        };
        Ok(stream_consumer)
    }
}

impl<C: ConsumerContext> StreamConsumer<C> {
    /// Starts the stream consumer with default configuration (100ms polling
    /// interval and no `NoMessageReceived` notifications).
    ///
    /// **Note:** this method must be called from within the context of a Tokio
    /// runtime.
    pub fn start(&self) -> MessageStream<C> {
        self.start_with(Duration::from_millis(100), false)
    }

    /// Starts the stream consumer with the specified poll interval.
    ///
    /// If `no_message_error` is set to true, the returned `MessageStream` will
    /// yield an error of type `KafkaError::NoMessageReceived` every time the
    /// poll interval is reached and no message has been received.
    pub fn start_with(&self, poll_interval: Duration, no_message_error: bool) -> MessageStream<C> {
        // TODO: verify called once
        tokio::spawn({
            let consumer = self.consumer.clone();
            let should_stop = self.should_stop.clone();
            async move {
                trace!("Polling task loop started");
                let _on_drop = OnDrop(|| trace!("Polling task loop terminated"));
                while !should_stop.load(Ordering::Relaxed) {
                    unsafe { rdsys::rd_kafka_poll(consumer.client().native_ptr(), 0) };
                    time::delay_for(poll_interval).await;
                }
            }
        });
        let no_message_interval = match no_message_error {
            true => Some(poll_interval),
            false => None,
        };
        MessageStream::new(self, no_message_interval)
    }

    /// Stops the stream consumer.
    pub fn stop(&self) {
        self.should_stop.store(true, Ordering::Relaxed);
    }
}

impl<C: ConsumerContext> Drop for StreamConsumer<C> {
    fn drop(&mut self) {
        trace!("Destroy StreamConsumer");
        self.stop();
    }
}
<|endoftext|>


<commit_before>
// A regex "token" represents a single character in the text
// It can be one of:
//     a character literal
//     a character class
//     a any character (.)
#[derive(PartialEq, Debug)]
pub enum Token {
    Literal(char),
    Class(Vec<char>),
    Any
}

// Represents an upper and lower bound on the multiplicity of a regex token
// Eg. `(Some(0), Some(1))` corresponds to `?` and `(Some(0), None)` corresponds
// to `*`
#[derive(PartialEq, Debug, Clone)]
pub struct Multiplicity {
    pub minimum: Option<usize>,
    pub maximum: Option<usize>
}

// A regex "expression" represents a token with multiplicity, or a position
// Eg. `[abc]*`, `[abc]{1,3}`, `^`
#[derive(PartialEq, Debug)]
pub enum Expression {
    BeginPosition,
    EndPosition,
    Token(Token, Multiplicity)
}

pub fn parse_expressions(text: &str) -> Result<Vec<Expression>,&str> {
    let mut result = Vec::<Expression>::new();
    let mut in_char_class = false;
    let mut chars_in_class = Vec::new();

    for c in text.chars() {
        if in_char_class && c != ']' {
            chars_in_class.push(c);
            continue
        }

        match c {
            '?' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: Some(0),
                                            maximum: Some(1)
                                        }));
                            },
                            _ => { return Err("invalid token before `?` metacharacter"); }
                        }
                    },
                    None => { return Err("no token before `?` metacharacter"); }
                }
            },
            '+' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: Some(1),
                                            maximum: None
                                        }));
                            },
                            _ => { return Err("invalid token before `+` metacharacter"); }
                        }
                    },
                    None => { return Err("no token before `+` metacharacter"); }
                }
            },
            '[' => {
                in_char_class = true;
                chars_in_class.clear();
            },
            ']' => {
                in_char_class = false;
                result.push(Expression::Token(
                        Token::Class(chars_in_class.clone()),
                        Multiplicity {
                            minimum: Some(1),
                            maximum: Some(1)
                        }));
            },
            c => { // Not a meta-character, treat as a literal
                result.push(Expression::Token(
                    Token::Literal(c),
                    Multiplicity {
                        minimum: Some(1),
                        maximum: Some(1)
                    }
                ));
            }

        }
    }

    Ok(result)
}

#[cfg(test)]
mod expression_spec {
    use super::{Token, Multiplicity, Expression, parse_expressions};

    #[test]
    fn parses_single_literal() {
        assert_eq!(vec![
                   Expression::Token(
                       Token::Literal('a'),
                       Multiplicity {
                           minimum: Some(1),
                           maximum: Some(1)
                       }
                   )
        ], parse_expressions("a").unwrap())
    }

    #[test]
    fn parses_many_literals() {
        assert_eq!(6, parse_expressions("abcxyz").unwrap().len());
    }

    #[test]
    fn handles_optional_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: Some(0),
                       maximum: Some(1)
                   })
        ], parse_expressions("a?").unwrap());

        assert!(parse_expressions("?").is_err());
        assert!(parse_expressions("??").is_err());
    }

    #[test]
    fn handles_one_or_more_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: Some(1),
                       maximum: None
                   })
        ], parse_expressions("a+").unwrap());

        assert!(parse_expressions("+").is_err());
        assert!(parse_expressions("++").is_err());
    }

    #[test]
    fn handles_character_class() {
        assert_eq!(vec![
                   Expression::Token(Token::Class(vec!['x', 'y', 'z']),
                                     Multiplicity {
                                         minimum: Some(1),
                                         maximum: Some(1)
                                     })
        ], parse_expressions("[xyz]").unwrap());
    }
}

<commit_msg>add char class error handling<commit_after>
// A regex "token" represents a single character in the text
// It can be one of:
//     a character literal
//     a character class
//     a any character (.)
#[derive(PartialEq, Debug)]
pub enum Token {
    Literal(char),
    Class(Vec<char>),
    Any
}

// Represents an upper and lower bound on the multiplicity of a regex token
// Eg. `(Some(0), Some(1))` corresponds to `?` and `(Some(0), None)` corresponds
// to `*`
#[derive(PartialEq, Debug, Clone)]
pub struct Multiplicity {
    pub minimum: Option<usize>,
    pub maximum: Option<usize>
}

// A regex "expression" represents a token with multiplicity, or a position
// Eg. `[abc]*`, `[abc]{1,3}`, `^`
#[derive(PartialEq, Debug)]
pub enum Expression {
    BeginPosition,
    EndPosition,
    Token(Token, Multiplicity)
}

pub fn parse_expressions(text: &str) -> Result<Vec<Expression>,&str> {
    let mut result = Vec::<Expression>::new();
    let mut in_char_class = false;
    let mut chars_in_class = Vec::new();

    for c in text.chars() {
        if in_char_class && c != ']' {
            chars_in_class.push(c);
            continue
        }

        match c {
            '?' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: Some(0),
                                            maximum: Some(1)
                                        }));
                            },
                            _ => { return Err("invalid token before `?` metacharacter"); }
                        }
                    },
                    None => { return Err("no token before `?` metacharacter"); }
                }
            },
            '+' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: Some(1),
                                            maximum: None
                                        }));
                            },
                            _ => { return Err("invalid token before `+` metacharacter"); }
                        }
                    },
                    None => { return Err("no token before `+` metacharacter"); }
                }
            },
            '[' => {
                in_char_class = true;
                chars_in_class.clear();
            },
            ']' => {
                if in_char_class == false {
                    return Err("missing char class start `[`");
                }

                in_char_class = false;
                result.push(Expression::Token(
                        Token::Class(chars_in_class.clone()),
                        Multiplicity {
                            minimum: Some(1),
                            maximum: Some(1)
                        }));
            },
            c => { // Not a meta-character, treat as a literal
                result.push(Expression::Token(
                    Token::Literal(c),
                    Multiplicity {
                        minimum: Some(1),
                        maximum: Some(1)
                    }
                ));
            }

        }
    }

    if in_char_class == true {
        return Err("incomplete char class, expected `]`");
    }

    Ok(result)
}

#[cfg(test)]
mod expression_spec {
    use super::{Token, Multiplicity, Expression, parse_expressions};

    #[test]
    fn parses_single_literal() {
        assert_eq!(vec![
                   Expression::Token(
                       Token::Literal('a'),
                       Multiplicity {
                           minimum: Some(1),
                           maximum: Some(1)
                       }
                   )
        ], parse_expressions("a").unwrap())
    }

    #[test]
    fn parses_many_literals() {
        assert_eq!(6, parse_expressions("abcxyz").unwrap().len());
    }

    #[test]
    fn handles_optional_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: Some(0),
                       maximum: Some(1)
                   })
        ], parse_expressions("a?").unwrap());

        assert!(parse_expressions("?").is_err());
        assert!(parse_expressions("??").is_err());
    }

    #[test]
    fn handles_one_or_more_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: Some(1),
                       maximum: None
                   })
        ], parse_expressions("a+").unwrap());

        assert!(parse_expressions("+").is_err());
        assert!(parse_expressions("++").is_err());
    }

    #[test]
    fn handles_character_class() {
        assert_eq!(vec![
                   Expression::Token(Token::Class(vec!['x', 'y', 'z']),
                                     Multiplicity {
                                         minimum: Some(1),
                                         maximum: Some(1)
                                     })
        ], parse_expressions("[xyz]").unwrap());

        assert!(parse_expressions("]").is_err());
        assert!(parse_expressions("[").is_err());
        assert!(parse_expressions("[]").is_ok());
    }
}

<|endoftext|>


<commit_before>use std::{io, path, ptr, time, fs, mem};
use std::os::unix::io::AsRawFd;
use std::mem::size_of;
use libc::{c_void, c_int, c_uint, c_ulong, c_uchar, ioctl, sysctl, timeval};
use data::*;
use super::common::*;
use super::unix;
use super::bsd;

pub struct PlatformImpl;

macro_rules! sysctl {
    ($mib:expr, $dataptr:expr, $size:expr, $shouldcheck:expr) => {
        {
            let mib = &$mib;
            let mut size = $size;
            if unsafe { sysctl(&mib[0], mib.len() as u32,
                               $dataptr as *mut _ as *mut c_void, &mut size, ptr::null_mut(), 0) } != 0 && $shouldcheck {
                return Err(io::Error::new(io::ErrorKind::Other, "sysctl() failed"))
            }
            size
        }
    };
    ($mib:expr, $dataptr:expr, $size:expr) => {
        sysctl!($mib, $dataptr, $size, true)
    }
}

lazy_static! {
    static ref APM_IOC_GETPOWER: c_ulong = (0x40000000u64 | ((size_of::<apm_power_info>() & 0x1fff) << 16) as u64 | (0x41 << 8) | 3);
    // OpenBSD does not have sysctlnametomib, so more copy-pasting of magic numbers from C headers :(
    static ref HW_NCPU: [c_int; 2] = [6, 3];
    static ref KERN_CPTIME2: [c_int; 3] = [1, 71, 0];
    static ref KERN_BOOTTIME: [c_int; 2] = [1, 21];
    static ref VM_UVMEXP: [c_int; 2] = [2, 4];
    static ref VFS_BCACHESTAT: [c_int; 3] = [10, 0, 3];
}

/// An implementation of `Platform` for OpenBSD.
/// See `Platform` for documentation.
impl Platform for PlatformImpl {
    #[inline(always)]
    fn new() -> Self {
        PlatformImpl
    }

    fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
        let loads = try!(measure_cpu());
        Ok(DelayedMeasurement::new(
                Box::new(move || Ok(loads.iter()
                               .zip(try!(measure_cpu()).iter())
                               .map(|(prev, now)| (*now - prev).to_cpuload())
                               .collect::<Vec<_>>()))))
    }

    fn load_average(&self) -> io::Result<LoadAverage> {
        unix::load_average()
    }

    fn memory(&self) -> io::Result<Memory> {
        let mut uvm_info = uvmexp::default(); sysctl!(VM_UVMEXP, &mut uvm_info, mem::size_of::<uvmexp>());
        let mut bcache_info = bcachestats::default(); sysctl!(VFS_BCACHESTAT, &mut bcache_info, mem::size_of::<bcachestats>());
        let pmem = PlatformMemory {
            active: ByteSize::kib((uvm_info.active << *bsd::PAGESHIFT) as usize),
            inactive: ByteSize::kib((uvm_info.inactive << *bsd::PAGESHIFT) as usize),
            wired: ByteSize::kib((uvm_info.wired << *bsd::PAGESHIFT) as usize),
            cache: ByteSize::kib((bcache_info.numbufpages << *bsd::PAGESHIFT) as usize),
            free: ByteSize::kib((uvm_info.free << *bsd::PAGESHIFT) as usize),
            paging: ByteSize::kib((uvm_info.paging << *bsd::PAGESHIFT) as usize),
        };
        Ok(Memory {
            total: pmem.active + pmem.inactive + pmem.wired + pmem.cache + pmem.free,
            free: pmem.inactive + pmem.free,
            platform_memory: pmem,
        })
    }

    fn boot_time(&self) -> io::Result<DateTime<UTC>> {
        let mut data: timeval = unsafe { mem::zeroed() };
        sysctl!(KERN_BOOTTIME, &mut data, mem::size_of::<timeval>());
        Ok(DateTime::<UTC>::from_utc(NaiveDateTime::from_timestamp(data.tv_sec, data.tv_usec as u32), UTC))
    }

    // /dev/apm is probably the nicest interface I've seen :)
    fn battery_life(&self) -> io::Result<BatteryLife> {
        let f = try!(fs::File::open("/dev/apm"));
        let mut info = apm_power_info::default();
        if unsafe { ioctl(f.as_raw_fd(), *APM_IOC_GETPOWER, &mut info) } == -1 {
            return Err(io::Error::new(io::ErrorKind::Other, "ioctl() failed"))
        }
        if info.battery_state == 0xff { // APM_BATT_UNKNOWN
            return Err(io::Error::new(io::ErrorKind::Other, "Battery state unknown"))
        }
        if info.battery_state == 4 { // APM_BATTERY_ABSENT
            return Err(io::Error::new(io::ErrorKind::Other, "Battery absent"))
        }
        Ok(BatteryLife {
            remaining_capacity: info.battery_life as f32,
            remaining_time: time::Duration::from_secs(info.minutes_left as u64),
        })
    }

    fn on_ac_power(&self) -> io::Result<bool> {
        let f = try!(fs::File::open("/dev/apm"));
        let mut info = apm_power_info::default();
        if unsafe { ioctl(f.as_raw_fd(), *APM_IOC_GETPOWER, &mut info) } == -1 {
            return Err(io::Error::new(io::ErrorKind::Other, "ioctl() failed"))
        }
        Ok(info.ac_state == 0x01) // APM_AC_ON
    }

    fn mounts(&self) -> io::Result<Vec<Filesystem>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn mount_at<P: AsRef<path::Path>>(&self, _: P) -> io::Result<Filesystem> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
        unix::networks()
    }
}

fn measure_cpu() -> io::Result<Vec<bsd::sysctl_cpu>> {
    let mut cpus: usize = 0; sysctl!(HW_NCPU, &mut cpus, mem::size_of::<usize>());
    let mut data: Vec<bsd::sysctl_cpu> = Vec::with_capacity(cpus);
    unsafe { data.set_len(cpus) };
    for i in 0..cpus {
        let mut mib = KERN_CPTIME2.clone();
        mib[2] = i as i32;
        sysctl!(mib, &mut data[i], mem::size_of::<bsd::sysctl_cpu>());
    }
    Ok(data)
}

#[derive(Default, Debug)]
#[repr(C)]
struct apm_power_info {
    battery_state: c_uchar,
    ac_state: c_uchar,
    battery_life: c_uchar,
    spare1: c_uchar,
    minutes_left: c_uint,
    spare2: [c_uint; 6],
}

#[derive(Default, Debug)]
#[repr(C)]
struct bcachestats {
    numbufs: i64,		/* number of buffers allocated */
    numbufpages: i64,		/* number of pages in buffer cache */
    numdirtypages: i64, 		/* number of dirty free pages */
    numcleanpages: i64, 		/* number of clean free pages */
    pendingwrites: i64,		/* number of pending writes */
    pendingreads: i64,		/* number of pending reads */
    numwrites: i64,		/* total writes started */
    numreads: i64,		/* total reads started */
    cachehits: i64,		/* total reads found in cache */
    busymapped: i64,		/* number of busy and mapped buffers */
    dmapages: i64,		/* dma reachable pages in buffer cache */
    highpages: i64,		/* pages above dma region */
    delwribufs: i64,		/* delayed write buffers */
    kvaslots: i64,		/* kva slots total */
    kvaslots_avail: i64,		/* available kva slots */
}

#[derive(Default, Debug)]
#[repr(C)]
struct uvmexp {
    /* vm_page constants */
    pagesize: c_int,   /* size of a page (PAGE_SIZE): must be power of 2 */
    pagemask: c_int,   /* page mask */
    pageshift: c_int,  /* page shift */

    /* vm_page counters */
    npages: c_int,     /* number of pages we manage */
    free: c_int,       /* number of free pages */
    active: c_int,     /* number of active pages */
    inactive: c_int,   /* number of pages that we free'd but may want back */
    paging: c_int,	/* number of pages in the process of being paged out */
    wired: c_int,      /* number of wired pages */

    zeropages: c_int,		/* number of zero'd pages */
    reserve_pagedaemon: c_int, /* number of pages reserved for pagedaemon */
    reserve_kernel: c_int,	/* number of pages reserved for kernel */
    anonpages: c_int,		/* number of pages used by anon pagers */
    vnodepages: c_int,		/* number of pages used by vnode page cache */
    vtextpages: c_int,		/* number of pages used by vtext vnodes */

    /* pageout params */
    freemin: c_int,    /* min number of free pages */
    freetarg: c_int,   /* target number of free pages */
    inactarg: c_int,   /* target number of inactive pages */
    wiredmax: c_int,   /* max number of wired pages */
    anonmin: c_int,	/* min threshold for anon pages */
    vtextmin: c_int,	/* min threshold for vtext pages */
    vnodemin: c_int,	/* min threshold for vnode pages */
    anonminpct: c_int,	/* min percent anon pages */
    vtextminpct: c_int,/* min percent vtext pages */
    vnodeminpct: c_int,/* min percent vnode pages */

    /* swap */
    nswapdev: c_int,	/* number of configured swap devices in system */
    swpages: c_int,	/* number of PAGE_SIZE'ed swap pages */
    swpginuse: c_int,	/* number of swap pages in use */
    swpgonly: c_int,	/* number of swap pages in use, not also in RAM */
    nswget: c_int,	/* number of times fault calls uvm_swap_get() */
    nanon: c_int,	/* number total of anon's in system */
    nanonneeded: c_int,/* number of anons currently needed */
    nfreeanon: c_int,	/* number of free anon's */

    /* stat counters */
    faults: c_int,		/* page fault count */
    traps: c_int,		/* trap count */
    intrs: c_int,		/* interrupt count */
    swtch: c_int,		/* context switch count */
    softs: c_int,		/* software interrupt count */
    syscalls: c_int,		/* system calls */
    pageins: c_int,		/* pagein operation count */
    /* pageouts are in pdpageouts below */
    obsolete_swapins: c_int,	/* swapins */
    obsolete_swapouts: c_int,	/* swapouts */
    pgswapin: c_int,		/* pages swapped in */
    pgswapout: c_int,		/* pages swapped out */
    forks: c_int,  		/* forks */
    forks_ppwait: c_int,	/* forks where parent waits */
    forks_sharevm: c_int,	/* forks where vmspace is shared */
    pga_zerohit: c_int,	/* pagealloc where zero wanted and zero
                           was available */
    pga_zeromiss: c_int,	/* pagealloc where zero wanted and zero
                               not available */
    zeroaborts: c_int,		/* number of times page zeroing was
                               aborted */

    /* fault subcounters */
    fltnoram: c_int,	/* number of times fault was out of ram */
    fltnoanon: c_int,	/* number of times fault was out of anons */
    fltnoamap: c_int,	/* number of times fault was out of amap chunks */
    fltpgwait: c_int,	/* number of times fault had to wait on a page */
    fltpgrele: c_int,	/* number of times fault found a released page */
    fltrelck: c_int,	/* number of times fault relock called */
    fltrelckok: c_int,	/* number of times fault relock is a success */
    fltanget: c_int,	/* number of times fault gets anon page */
    fltanretry: c_int,	/* number of times fault retrys an anon get */
    fltamcopy: c_int,	/* number of times fault clears "needs copy" */
    fltnamap: c_int,	/* number of times fault maps a neighbor anon page */
    fltnomap: c_int,	/* number of times fault maps a neighbor obj page */
    fltlget: c_int,	/* number of times fault does a locked pgo_get */
    fltget: c_int,	/* number of times fault does an unlocked get */
    flt_anon: c_int,	/* number of times fault anon (case 1a) */
    flt_acow: c_int,	/* number of times fault anon cow (case 1b) */
    flt_obj: c_int,	/* number of times fault is on object page (2a) */
    flt_prcopy: c_int,	/* number of times fault promotes with copy (2b) */
    flt_przero: c_int,	/* number of times fault promotes with zerofill (2b) */

    /* daemon counters */
    pdwoke: c_int,	/* number of times daemon woke up */
    pdrevs: c_int,	/* number of times daemon rev'd clock hand */
    pdswout: c_int,	/* number of times daemon called for swapout */
    pdfreed: c_int,	/* number of pages daemon freed since boot */
    pdscans: c_int,	/* number of pages daemon scanned since boot */
    pdanscan: c_int,	/* number of anonymous pages scanned by daemon */
    pdobscan: c_int,	/* number of object pages scanned by daemon */
    pdreact: c_int,	/* number of pages daemon reactivated since boot */
    pdbusy: c_int,	/* number of times daemon found a busy page */
    pdpageouts: c_int,	/* number of times daemon started a pageout */
    pdpending: c_int,	/* number of times daemon got a pending pagout */
    pddeact: c_int,	/* number of pages daemon deactivates */
    pdreanon: c_int,	/* anon pages reactivated due to min threshold */
    pdrevnode: c_int,	/* vnode pages reactivated due to min threshold */
    pdrevtext: c_int,	/* vtext pages reactivated due to min threshold */

    fpswtch: c_int,	/* FPU context switches */
    kmapent: c_int,	/* number of kernel map entries */
}
<commit_msg>Fix OpenBSD memory<commit_after>use std::{io, path, ptr, time, fs, mem};
use std::os::unix::io::AsRawFd;
use std::mem::size_of;
use libc::{c_void, c_int, c_uint, c_ulong, c_uchar, ioctl, sysctl, timeval};
use data::*;
use super::common::*;
use super::unix;
use super::bsd;

pub struct PlatformImpl;

macro_rules! sysctl {
    ($mib:expr, $dataptr:expr, $size:expr, $shouldcheck:expr) => {
        {
            let mib = &$mib;
            let mut size = $size;
            if unsafe { sysctl(&mib[0], mib.len() as u32,
                               $dataptr as *mut _ as *mut c_void, &mut size, ptr::null_mut(), 0) } != 0 && $shouldcheck {
                return Err(io::Error::new(io::ErrorKind::Other, "sysctl() failed"))
            }
            size
        }
    };
    ($mib:expr, $dataptr:expr, $size:expr) => {
        sysctl!($mib, $dataptr, $size, true)
    }
}

lazy_static! {
    static ref APM_IOC_GETPOWER: c_ulong = (0x40000000u64 | ((size_of::<apm_power_info>() & 0x1fff) << 16) as u64 | (0x41 << 8) | 3);
    // OpenBSD does not have sysctlnametomib, so more copy-pasting of magic numbers from C headers :(
    static ref HW_NCPU: [c_int; 2] = [6, 3];
    static ref KERN_CPTIME2: [c_int; 3] = [1, 71, 0];
    static ref KERN_BOOTTIME: [c_int; 2] = [1, 21];
    static ref VM_UVMEXP: [c_int; 2] = [2, 4];
    static ref VFS_BCACHESTAT: [c_int; 3] = [10, 0, 3];
}

/// An implementation of `Platform` for OpenBSD.
/// See `Platform` for documentation.
impl Platform for PlatformImpl {
    #[inline(always)]
    fn new() -> Self {
        PlatformImpl
    }

    fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
        let loads = try!(measure_cpu());
        Ok(DelayedMeasurement::new(
                Box::new(move || Ok(loads.iter()
                               .zip(try!(measure_cpu()).iter())
                               .map(|(prev, now)| (*now - prev).to_cpuload())
                               .collect::<Vec<_>>()))))
    }

    fn load_average(&self) -> io::Result<LoadAverage> {
        unix::load_average()
    }

    fn memory(&self) -> io::Result<Memory> {
        let mut uvm_info = uvmexp::default(); sysctl!(VM_UVMEXP, &mut uvm_info, mem::size_of::<uvmexp>());
        let mut bcache_info = bcachestats::default(); sysctl!(VFS_BCACHESTAT, &mut bcache_info, mem::size_of::<bcachestats>());
        let total = ByteSize::kib((uvm_info.npages << *bsd::PAGESHIFT) as usize);
        let pmem = PlatformMemory {
            active: ByteSize::kib((uvm_info.active << *bsd::PAGESHIFT) as usize),
            inactive: ByteSize::kib((uvm_info.inactive << *bsd::PAGESHIFT) as usize),
            wired: ByteSize::kib((uvm_info.wired << *bsd::PAGESHIFT) as usize),
            cache: ByteSize::kib((bcache_info.numbufpages << *bsd::PAGESHIFT) as usize),
            free: ByteSize::kib((uvm_info.free << *bsd::PAGESHIFT) as usize),
            paging: ByteSize::kib((uvm_info.paging << *bsd::PAGESHIFT) as usize),
        };
        Ok(Memory {
            total: total,
            free: pmem.inactive + pmem.cache + pmem.free + pmem.paging,
            platform_memory: pmem,
        })
    }

    fn boot_time(&self) -> io::Result<DateTime<UTC>> {
        let mut data: timeval = unsafe { mem::zeroed() };
        sysctl!(KERN_BOOTTIME, &mut data, mem::size_of::<timeval>());
        Ok(DateTime::<UTC>::from_utc(NaiveDateTime::from_timestamp(data.tv_sec, data.tv_usec as u32), UTC))
    }

    // /dev/apm is probably the nicest interface I've seen :)
    fn battery_life(&self) -> io::Result<BatteryLife> {
        let f = try!(fs::File::open("/dev/apm"));
        let mut info = apm_power_info::default();
        if unsafe { ioctl(f.as_raw_fd(), *APM_IOC_GETPOWER, &mut info) } == -1 {
            return Err(io::Error::new(io::ErrorKind::Other, "ioctl() failed"))
        }
        if info.battery_state == 0xff { // APM_BATT_UNKNOWN
            return Err(io::Error::new(io::ErrorKind::Other, "Battery state unknown"))
        }
        if info.battery_state == 4 { // APM_BATTERY_ABSENT
            return Err(io::Error::new(io::ErrorKind::Other, "Battery absent"))
        }
        Ok(BatteryLife {
            remaining_capacity: info.battery_life as f32,
            remaining_time: time::Duration::from_secs(info.minutes_left as u64),
        })
    }

    fn on_ac_power(&self) -> io::Result<bool> {
        let f = try!(fs::File::open("/dev/apm"));
        let mut info = apm_power_info::default();
        if unsafe { ioctl(f.as_raw_fd(), *APM_IOC_GETPOWER, &mut info) } == -1 {
            return Err(io::Error::new(io::ErrorKind::Other, "ioctl() failed"))
        }
        Ok(info.ac_state == 0x01) // APM_AC_ON
    }

    fn mounts(&self) -> io::Result<Vec<Filesystem>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn mount_at<P: AsRef<path::Path>>(&self, _: P) -> io::Result<Filesystem> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
        unix::networks()
    }
}

fn measure_cpu() -> io::Result<Vec<bsd::sysctl_cpu>> {
    let mut cpus: usize = 0; sysctl!(HW_NCPU, &mut cpus, mem::size_of::<usize>());
    let mut data: Vec<bsd::sysctl_cpu> = Vec::with_capacity(cpus);
    unsafe { data.set_len(cpus) };
    for i in 0..cpus {
        let mut mib = KERN_CPTIME2.clone();
        mib[2] = i as i32;
        sysctl!(mib, &mut data[i], mem::size_of::<bsd::sysctl_cpu>());
    }
    Ok(data)
}

#[derive(Default, Debug)]
#[repr(C)]
struct apm_power_info {
    battery_state: c_uchar,
    ac_state: c_uchar,
    battery_life: c_uchar,
    spare1: c_uchar,
    minutes_left: c_uint,
    spare2: [c_uint; 6],
}

#[derive(Default, Debug)]
#[repr(C)]
struct bcachestats {
    numbufs: i64,		/* number of buffers allocated */
    numbufpages: i64,		/* number of pages in buffer cache */
    numdirtypages: i64, 		/* number of dirty free pages */
    numcleanpages: i64, 		/* number of clean free pages */
    pendingwrites: i64,		/* number of pending writes */
    pendingreads: i64,		/* number of pending reads */
    numwrites: i64,		/* total writes started */
    numreads: i64,		/* total reads started */
    cachehits: i64,		/* total reads found in cache */
    busymapped: i64,		/* number of busy and mapped buffers */
    dmapages: i64,		/* dma reachable pages in buffer cache */
    highpages: i64,		/* pages above dma region */
    delwribufs: i64,		/* delayed write buffers */
    kvaslots: i64,		/* kva slots total */
    kvaslots_avail: i64,		/* available kva slots */
    highflips: i64,		/* total flips to above DMA */
    highflops: i64,		/* total failed flips to above DMA */
    dmaflips: i64,		/* total flips from high to DMA */
}

#[derive(Default, Debug)]
#[repr(C)]
struct uvmexp {
    /* vm_page constants */
    pagesize: c_int,   /* size of a page (PAGE_SIZE): must be power of 2 */
    pagemask: c_int,   /* page mask */
    pageshift: c_int,  /* page shift */

    /* vm_page counters */
    npages: c_int,     /* number of pages we manage */
    free: c_int,       /* number of free pages */
    active: c_int,     /* number of active pages */
    inactive: c_int,   /* number of pages that we free'd but may want back */
    paging: c_int,	/* number of pages in the process of being paged out */
    wired: c_int,      /* number of wired pages */

    zeropages: c_int,		/* number of zero'd pages */
    reserve_pagedaemon: c_int, /* number of pages reserved for pagedaemon */
    reserve_kernel: c_int,	/* number of pages reserved for kernel */
    anonpages: c_int,		/* number of pages used by anon pagers */
    vnodepages: c_int,		/* number of pages used by vnode page cache */
    vtextpages: c_int,		/* number of pages used by vtext vnodes */

    /* pageout params */
    freemin: c_int,    /* min number of free pages */
    freetarg: c_int,   /* target number of free pages */
    inactarg: c_int,   /* target number of inactive pages */
    wiredmax: c_int,   /* max number of wired pages */
    anonmin: c_int,	/* min threshold for anon pages */
    vtextmin: c_int,	/* min threshold for vtext pages */
    vnodemin: c_int,	/* min threshold for vnode pages */
    anonminpct: c_int,	/* min percent anon pages */
    vtextminpct: c_int,/* min percent vtext pages */
    vnodeminpct: c_int,/* min percent vnode pages */

    /* swap */
    nswapdev: c_int,	/* number of configured swap devices in system */
    swpages: c_int,	/* number of PAGE_SIZE'ed swap pages */
    swpginuse: c_int,	/* number of swap pages in use */
    swpgonly: c_int,	/* number of swap pages in use, not also in RAM */
    nswget: c_int,	/* number of times fault calls uvm_swap_get() */
    nanon: c_int,	/* number total of anon's in system */
    nanonneeded: c_int,/* number of anons currently needed */
    nfreeanon: c_int,	/* number of free anon's */

    /* stat counters */
    faults: c_int,		/* page fault count */
    traps: c_int,		/* trap count */
    intrs: c_int,		/* interrupt count */
    swtch: c_int,		/* context switch count */
    softs: c_int,		/* software interrupt count */
    syscalls: c_int,		/* system calls */
    pageins: c_int,		/* pagein operation count */
    /* pageouts are in pdpageouts below */
    obsolete_swapins: c_int,	/* swapins */
    obsolete_swapouts: c_int,	/* swapouts */
    pgswapin: c_int,		/* pages swapped in */
    pgswapout: c_int,		/* pages swapped out */
    forks: c_int,  		/* forks */
    forks_ppwait: c_int,	/* forks where parent waits */
    forks_sharevm: c_int,	/* forks where vmspace is shared */
    pga_zerohit: c_int,	/* pagealloc where zero wanted and zero
                           was available */
    pga_zeromiss: c_int,	/* pagealloc where zero wanted and zero
                               not available */
    zeroaborts: c_int,		/* number of times page zeroing was
                               aborted */

    /* fault subcounters */
    fltnoram: c_int,	/* number of times fault was out of ram */
    fltnoanon: c_int,	/* number of times fault was out of anons */
    fltnoamap: c_int,	/* number of times fault was out of amap chunks */
    fltpgwait: c_int,	/* number of times fault had to wait on a page */
    fltpgrele: c_int,	/* number of times fault found a released page */
    fltrelck: c_int,	/* number of times fault relock called */
    fltrelckok: c_int,	/* number of times fault relock is a success */
    fltanget: c_int,	/* number of times fault gets anon page */
    fltanretry: c_int,	/* number of times fault retrys an anon get */
    fltamcopy: c_int,	/* number of times fault clears "needs copy" */
    fltnamap: c_int,	/* number of times fault maps a neighbor anon page */
    fltnomap: c_int,	/* number of times fault maps a neighbor obj page */
    fltlget: c_int,	/* number of times fault does a locked pgo_get */
    fltget: c_int,	/* number of times fault does an unlocked get */
    flt_anon: c_int,	/* number of times fault anon (case 1a) */
    flt_acow: c_int,	/* number of times fault anon cow (case 1b) */
    flt_obj: c_int,	/* number of times fault is on object page (2a) */
    flt_prcopy: c_int,	/* number of times fault promotes with copy (2b) */
    flt_przero: c_int,	/* number of times fault promotes with zerofill (2b) */

    /* daemon counters */
    pdwoke: c_int,	/* number of times daemon woke up */
    pdrevs: c_int,	/* number of times daemon rev'd clock hand */
    pdswout: c_int,	/* number of times daemon called for swapout */
    pdfreed: c_int,	/* number of pages daemon freed since boot */
    pdscans: c_int,	/* number of pages daemon scanned since boot */
    pdanscan: c_int,	/* number of anonymous pages scanned by daemon */
    pdobscan: c_int,	/* number of object pages scanned by daemon */
    pdreact: c_int,	/* number of pages daemon reactivated since boot */
    pdbusy: c_int,	/* number of times daemon found a busy page */
    pdpageouts: c_int,	/* number of times daemon started a pageout */
    pdpending: c_int,	/* number of times daemon got a pending pagout */
    pddeact: c_int,	/* number of pages daemon deactivates */
    pdreanon: c_int,	/* anon pages reactivated due to min threshold */
    pdrevnode: c_int,	/* vnode pages reactivated due to min threshold */
    pdrevtext: c_int,	/* vtext pages reactivated due to min threshold */

    fpswtch: c_int,	/* FPU context switches */
    kmapent: c_int,	/* number of kernel map entries */
}
<|endoftext|>