<commit_before>//! Provides the scene struct containing information needed to render a
//! volume from some camera position. Scenes are stored in JSON format, for example:
//!
//! ```json
//! {
//!     "volume": {
//!	    	"file": "csafe-heptane-302-volume.raw",
//!	    	"data_type": "u8",
//!	    	"dimensions": [302, 302, 302]
//!	    },
//!	    "transfer_function": "cool_warm",
//!	    "camera": {
//!		    "pos": [-0.5, 0.5, 1.5],
//!		    "target": [0.5, 0.5, 0.5],
//!		    "up": [0, 1, 0],
//!		    "fovy": 60,
//!	    },
//!	    "width": 512,
//!	    "height": 512,
//!	    "background": [0.1, 0.1, 0.1],
//!	    "n_samples": 4
//! }
//! ```
//!
//! The `transfer_function` element can be one of the default ones `jet`, `cool_warm` or
//! `gray_scale` or the name of a ParaView exported transfer function (any string ending in
//! .json). ParaView transfer function importing is still a TODO though.

use std::io::prelude::*;
use std::path::Path;
use std::fs::File;
use std::ffi::OsStr;

use serde_json::{self, Value};

use ddvol;
use camera::Camera;
use vec3::{Vec3f, Vec3i};
use vol::Volume;
use tfn::TransferFunction;
use raw;

pub type RenderParams = ddvol::RenderParams;

pub struct Scene {
    /// Image width
    pub width: usize,
    /// Image height
    pub height: usize,
    pub camera: Camera,
    pub volume: Volume,
    pub params: RenderParams,
}

impl Scene {
    pub fn load(file: &str) -> Scene {
        let mut f = match File::open(file) {
            Ok(f) => f,
            Err(e) => panic!("Failed to open scene file: {}", e),
        };
        let mut content = String::new();
        if let Err(e) = f.read_to_string(&mut content) {
            panic!("Failed to read scene file: {}", e);
        }
        let data: Value = match serde_json::from_str(&content[..]) {
            Ok(d) => d,
            Err(e) => panic!("JSON parsing error: {}", e),
        };
        assert!(data.is_object(), "Expected a root JSON object. See example scenes");
        let base_path = match Path::new(file).parent() {
            Some(p) => p,
            None => Path::new(file),
        };
        let img_width = data.find("width").expect("image width must be set")
            .as_u64().expect("image width must be an int") as usize;
        let img_height = data.find("height").expect("image height must be set")
            .as_u64().expect("image height must be an int") as usize;
        let mut volume = Scene::load_volume(data.find("volume").expect("A volume must be specified"), &base_path);
        let tfn = Scene::load_transfer_function(data.find("transfer_function")
                                                .expect("A transfer function must be specified"), &base_path);
        volume.set_transfer_function(tfn);

        let camera = Scene::load_camera(data.find("camera").expect("A camera must be specified"),
                                        img_width, img_height);
        let render_params = Scene::load_render_params(&data);
        Scene { width: img_width, height: img_height, camera: camera,
                volume: volume, params: render_params }
    }
    fn load_volume(e: &Value, base_path: &Path) -> Volume {
        let mut vol_file = Path::new(e.find("file").expect("A volume filename must be set")
                                 .as_string().expect("Volume filename must be a string")).to_owned();
        if !vol_file.is_absolute() {
            vol_file = base_path.join(vol_file);
        }
        assert_eq!(vol_file.extension(), Some(OsStr::new("raw")));
        let dimensions = Scene::load_vec3i(e.find("dimensions")
                                           .expect("Volume dims must be set for RAW volume"))
            .expect("Invalid dimensions specified");

        let dtype = e.find("data_type").expect("A data type must be specified for RAW volumes")
                     .as_string().expect("data type must be a string");
        if dtype == "u8" {
            raw::import::<u8>(vol_file.as_path(), dimensions)
        } else if dtype == "u16" {
            raw::import::<u16>(vol_file.as_path(), dimensions)
        } else if dtype == "f32" {
            raw::import::<f32>(vol_file.as_path(), dimensions)
        } else if dtype == "f64" {
            raw::import::<f64>(vol_file.as_path(), dimensions)
        } else {
            panic!("Unrecognized data type {}! Valid options are u8, u16, f32, f64", dtype);
        }
    }
    fn load_transfer_function(e: &Value, base_path: &Path) -> TransferFunction {
        let tfn_file = Path::new(e.as_string().expect("transfer_function filename/name must be a string"));
        // Load the ParaView transfer function file if it's one, otherwise
        // see if it's one of our defaults we can provide
        if tfn_file.extension() == Some(OsStr::new("json")) {
            let mut tfn_buf = tfn_file.to_owned();
            if !tfn_buf.is_absolute() {
                tfn_buf = base_path.join(tfn_buf);
            }
            panic!("TODO: ParaView transferfunction importing is not implemented");
        } else {
            let tfn_name = tfn_file.to_str().unwrap();
            if tfn_name == "grayscale" {
                TransferFunction::grayscale()
            } else if tfn_name == "jet" {
                TransferFunction::jet()
            } else if tfn_name == "cool_warm" {
                TransferFunction::cool_warm()
            } else {
                panic!("Scene error: {} is not a built in transfer function", tfn_name);
            }
        }
    }
    fn load_camera(e: &Value, width: usize, height: usize) -> Camera {
        let pos = Scene::load_vec3f(e.find("pos").expect("Camera view position must be set"))
            .expect("Invalid camera position");
        let target = Scene::load_vec3f(e.find("target").expect("Camera view target must be set"))
            .expect("Invalid camera target");
        let up = Scene::load_vec3f(e.find("up").expect("Camera up vector must be set"))
            .expect("Invalid camera up");
        let fovy = e.find("fovy").expect("Camera FOV Y must be set").as_f64()
            .expect("FOV Y must be a float") as f32;
        Camera::new(pos, target, up, fovy, width as u32, height as u32)
    }
    fn load_render_params(e: &Value) -> RenderParams {
        let background = Scene::load_vec3f(e.find("background").expect("Background color must be set"))
            .expect("Background color must be a vec3f");
        let n_samples = e.find("n_samples").expect("n_samples per pixel must be set")
            .as_i64().expect("n_samples must be an int") as i32;
        RenderParams { background: background, n_samples: n_samples }
    }
    fn load_vec3i(e: &Value) -> Option<Vec3i> {
        e.as_array().map(|x| {
            assert_eq!(x.len(), 3);
            Vec3i::new(x[0].as_i64().unwrap() as i32,
                       x[1].as_i64().unwrap() as i32,
                       x[2].as_i64().unwrap() as i32)
        })
    }
    fn load_vec3f(e: &Value) -> Option<Vec3f> {
        e.as_array().map(|x| {
            assert_eq!(x.len(), 3);
            Vec3f::new(x[0].as_f64().unwrap() as f32,
                       x[1].as_f64().unwrap() as f32,
                       x[2].as_f64().unwrap() as f32)
        })
    }
}

<commit_msg>Add ParaView transfer function converter<commit_after>//! Provides the scene struct containing information needed to render a
//! volume from some camera position. Scenes are stored in JSON format, for example:
//!
//! ```json
//! {
//!     "volume": {
//!	    	"file": "csafe-heptane-302-volume.raw",
//!	    	"data_type": "u8",
//!	    	"dimensions": [302, 302, 302]
//!	    },
//!	    "transfer_function": "cool_warm",
//!	    "camera": {
//!		    "pos": [-0.5, 0.5, 1.5],
//!		    "target": [0.5, 0.5, 0.5],
//!		    "up": [0, 1, 0],
//!		    "fovy": 60,
//!	    },
//!	    "width": 512,
//!	    "height": 512,
//!	    "background": [0.1, 0.1, 0.1],
//!	    "n_samples": 4
//! }
//! ```
//!
//! The `transfer_function` element can be one of the default ones `jet`, `cool_warm` or
//! `gray_scale` or the name of a ParaView exported transfer function (any string ending in
//! .json). ParaView transfer function importing is still a TODO though.

use std::io::prelude::*;
use std::path::Path;
use std::fs::File;
use std::ffi::OsStr;

use serde_json::{self, Value};

use ddvol;
use camera::Camera;
use vec3::{Vec3f, Vec3i};
use vol::Volume;
use tfn::TransferFunction;
use raw;

pub type RenderParams = ddvol::RenderParams;

pub struct Scene {
    /// Image width
    pub width: usize,
    /// Image height
    pub height: usize,
    pub camera: Camera,
    pub volume: Volume,
    pub params: RenderParams,
}

impl Scene {
    pub fn load(file: &str) -> Scene {
        let mut f = match File::open(file) {
            Ok(f) => f,
            Err(e) => panic!("Failed to open scene file: {}", e),
        };
        let mut content = String::new();
        if let Err(e) = f.read_to_string(&mut content) {
            panic!("Failed to read scene file: {}", e);
        }
        let data: Value = match serde_json::from_str(&content[..]) {
            Ok(d) => d,
            Err(e) => panic!("JSON parsing error: {}", e),
        };
        if !data.is_object() {
            panic!("Expected a root JSON object. See example scenes");
        }
        let base_path = match Path::new(file).parent() {
            Some(p) => p,
            None => Path::new(file),
        };
        let img_width = data.find("width").expect("image width must be set")
            .as_u64().expect("image width must be an int") as usize;
        let img_height = data.find("height").expect("image height must be set")
            .as_u64().expect("image height must be an int") as usize;
        let mut volume = Scene::load_volume(data.find("volume").expect("A volume must be specified"), &base_path);
        let tfn = Scene::load_transfer_function(data.find("transfer_function")
                                                .expect("A transfer function must be specified"), &base_path);
        volume.set_transfer_function(tfn);

        let camera = Scene::load_camera(data.find("camera").expect("A camera must be specified"),
                                        img_width, img_height);
        let render_params = Scene::load_render_params(&data);
        Scene { width: img_width, height: img_height, camera: camera,
                volume: volume, params: render_params }
    }
    fn load_volume(e: &Value, base_path: &Path) -> Volume {
        let mut vol_file = Path::new(e.find("file").expect("A volume filename must be set")
                                 .as_string().expect("Volume filename must be a string")).to_owned();
        if !vol_file.is_absolute() {
            vol_file = base_path.join(vol_file);
        }
        let dimensions = Scene::load_vec3i(e.find("dimensions")
                                           .expect("Volume dims must be set for RAW volume"))
            .expect("Invalid dimensions specified");

        let dtype = e.find("data_type").expect("A data type must be specified for RAW volumes")
                     .as_string().expect("data type must be a string");
        if dtype == "u8" {
            raw::import::<u8>(vol_file.as_path(), dimensions)
        } else if dtype == "u16" {
            raw::import::<u16>(vol_file.as_path(), dimensions)
        } else if dtype == "f32" {
            raw::import::<f32>(vol_file.as_path(), dimensions)
        } else if dtype == "f64" {
            raw::import::<f64>(vol_file.as_path(), dimensions)
        } else {
            panic!("Unrecognized data type {}! Valid options are u8, u16, f32, f64", dtype);
        }
    }
    fn load_transfer_function(e: &Value, base_path: &Path) -> TransferFunction {
        let tfn_file = Path::new(e.as_string().expect("transfer_function filename/name must be a string"));
        // Load the ParaView transfer function file if it's one, otherwise
        // see if it's one of our defaults we can provide
        if tfn_file.extension() == Some(OsStr::new("json")) {
            let mut tfn_buf = tfn_file.to_owned();
            if !tfn_buf.is_absolute() {
                tfn_buf = base_path.join(tfn_buf);
            }
            Scene::import_paraview_tfn(tfn_buf.as_path())
        } else {
            let tfn_name = tfn_file.to_str().unwrap();
            if tfn_name == "grayscale" {
                TransferFunction::grayscale()
            } else if tfn_name == "jet" {
                TransferFunction::jet()
            } else if tfn_name == "cool_warm" {
                TransferFunction::cool_warm()
            } else {
                panic!("Scene error: {} is not a built in transfer function", tfn_name);
            }
        }
    }
    fn import_paraview_tfn(path: &Path) -> TransferFunction {
        println!("Importing ParaView transfer function {}", path.display());
        let mut f = match File::open(path) {
            Ok(f) => f,
            Err(e) => panic!("Failed to open ParaView transfer function file: {}", e),
        };
        let mut content = String::new();
        if let Err(e) = f.read_to_string(&mut content) {
            panic!("Failed to read ParaView transfer function file: {}", e);
        }
        let data: Value = match serde_json::from_str(&content[..]) {
            Ok(d) => d,
            Err(e) => panic!("JSON parsing error: {}", e),
        };
        let pv_tfn = &data.as_array().expect("Expected a root JSON array in ParaView function")[0];
        let color_space = pv_tfn.find("ColorSpace").expect("Expected a color space from ParaView function")
            .as_string().expect("ColorSpace must be a string");
        if color_space == "Diverging" {
            println!("Warning: ParaView's diverging colormap interpolation is not supported, \
                      you may see some incorrect colors");
        }
        let name = pv_tfn.find("Name").expect("Expected a name for ParaView function");
        if pv_tfn.find("Points").is_some() {
            println!("Warning: Opacity values in ParaView transfer functions are currently ignored");
        }
        let rgb_data = pv_tfn.find("RGBPoints").expect("Expected RGBPoints specifying the transfer function")
            .as_array().expect("RGBPoints must be an array");
        let rgb_points: Vec<_> = rgb_data.chunks(4).map(|x| {
            let val = x[0].as_f64().unwrap() as f32;
            let r = x[1].as_f64().unwrap() as f32;
            let g = x[2].as_f64().unwrap() as f32;
            let b = x[3].as_f64().unwrap() as f32;
            (val, Vec3f::new(r, g, b))
        }).collect();
        // Re-sample the ParaView transfer function into an evenly spaced linear transfer function
        let mut colors = Vec::new();
        let mut lo = 0;
        let mut hi = 0;
        colors.push(rgb_points[0].1);
        for i in 1..255 {
            let x = i as f32 / 255.0;
            if x > rgb_points[lo].0 {
                hi = rgb_points.iter().skip(lo).take_while(|pt| x > pt.0).count() + lo;
                lo = hi - 1;
            }
            let delta = x - rgb_points[lo].0;
            let interval = rgb_points[hi].0 - rgb_points[lo].0;
            let col =
                if delta == 0.0 || interval == 0.0 {
                    rgb_points[lo].1
                } else {
                    rgb_points[lo].1 + delta / interval * (rgb_points[hi].1 - rgb_points[lo].1)
                };
            colors.push(col);
        }
        colors.push(rgb_points.last().unwrap().1);
        println!("Imported ParaView transfer function {}", name);
        TransferFunction::new(&colors[..], &[0.0, 0.5])
    }
    fn load_camera(e: &Value, width: usize, height: usize) -> Camera {
        let pos = Scene::load_vec3f(e.find("pos").expect("Camera view position must be set"))
            .expect("Invalid camera position");
        let target = Scene::load_vec3f(e.find("target").expect("Camera view target must be set"))
            .expect("Invalid camera target");
        let up = Scene::load_vec3f(e.find("up").expect("Camera up vector must be set"))
            .expect("Invalid camera up");
        let fovy = e.find("fovy").expect("Camera FOV Y must be set").as_f64()
            .expect("FOV Y must be a float") as f32;
        Camera::new(pos, target, up, fovy, width as u32, height as u32)
    }
    fn load_render_params(e: &Value) -> RenderParams {
        let background = Scene::load_vec3f(e.find("background").expect("Background color must be set"))
            .expect("Background color must be a vec3f");
        let n_samples = e.find("n_samples").expect("n_samples per pixel must be set")
            .as_i64().expect("n_samples must be an int") as i32;
        RenderParams { background: background, n_samples: n_samples }
    }
    fn load_vec3i(e: &Value) -> Option<Vec3i> {
        e.as_array().map(|x| {
            assert_eq!(x.len(), 3);
            Vec3i::new(x[0].as_i64().unwrap() as i32,
                       x[1].as_i64().unwrap() as i32,
                       x[2].as_i64().unwrap() as i32)
        })
    }
    fn load_vec3f(e: &Value) -> Option<Vec3f> {
        e.as_array().map(|x| {
            assert_eq!(x.len(), 3);
            Vec3f::new(x[0].as_f64().unwrap() as f32,
                       x[1].as_f64().unwrap() as f32,
                       x[2].as_f64().unwrap() as f32)
        })
    }
}

<|endoftext|>


<commit_before>// Copyright © 2018 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

use std::rc::Rc;

use client::Client;
use client::render::{self, Palette, Vertex2d};
use client::render::bitmap::BitmapTexture;
use client::render::glyph::GlyphRenderer;
use client::render::pipeline2d;
use common::net::{ClientStat, ItemFlags, MAX_ITEMS};
use common::wad::Wad;

use cgmath::Matrix4;
use cgmath::SquareMatrix;
use chrono::Duration;
use flame;
use gfx::{CommandBuffer, Encoder, Factory, IndexBuffer, Slice};
use gfx::format::R8_G8_B8_A8;
use gfx::handle::{Buffer, ShaderResourceView, Texture};
use gfx::pso::{PipelineData, PipelineState};
use gfx_device_gl::Resources;

use failure::Error;

enum WeaponSlots {
    Shotgun = 0,
    SuperShotgun = 1,
    Nailgun = 2,
    SuperNailgun = 3,
    RocketLauncher = 4,
    GrenadeLauncher = 5,
    LightningGun = 6,
}

enum AmmoSlots {
    Shells = 0,
    Nails = 1,
    Rockets = 2,
    Cells = 3,
}

pub struct HudRenderer {
    glyph_renderer: Rc<GlyphRenderer>,
    digits: Box<[BitmapTexture]>,
    minus: BitmapTexture,
    alt_digits: Box<[BitmapTexture]>,
    alt_minus: BitmapTexture,
    colon: BitmapTexture,
    slash: BitmapTexture,

    weapons: Box<[Box<[BitmapTexture]>]>,
    ammo: Box<[BitmapTexture]>,
    sigils: Box<[BitmapTexture]>,
    armor: Box<[BitmapTexture]>,
    items: Box<[BitmapTexture]>,
    faces: Box<[BitmapTexture]>,
    pain_faces: Box<[BitmapTexture]>,

    face_invis: BitmapTexture,
    face_invuln: BitmapTexture,
    face_invis_invuln: BitmapTexture,
    face_quad: BitmapTexture,
    sbar: BitmapTexture,
    ibar: BitmapTexture,
    scorebar: BitmapTexture,

    vertex_buffer: Buffer<Resources, Vertex2d>,
}

impl HudRenderer {
    pub fn new<F>(
        glyph_renderer: Rc<GlyphRenderer>,
        gfx_wad: &Wad,
        palette: &Palette,
        factory: &mut F
    ) -> Result<HudRenderer, Error>
    where
        F: Factory<Resources>
    {
        use gfx::traits::FactoryExt;
        let vertex_buffer = factory.create_vertex_buffer(&render::QUAD_VERTICES);

        let mut digits = Vec::new();
        let mut alt_digits = Vec::new();

        // just to make the following code a bit less painful
        let mut qpic_to_bitmap = |name: &str| -> Result<BitmapTexture, Error> {
            let qpic = gfx_wad.open_qpic(name)?;
            BitmapTexture::from_qpic(factory, &qpic, palette)
        };

        for i in 0..10 {
            digits.push(qpic_to_bitmap(&format!("NUM_{}", i))?);
            alt_digits.push(qpic_to_bitmap(&format!("ANUM_{}", i))?);
        }

        let minus = qpic_to_bitmap("NUM_MINUS")?;
        let alt_minus = qpic_to_bitmap("ANUM_MINUS")?;
        let colon = qpic_to_bitmap("NUM_COLON")?;
        let slash = qpic_to_bitmap("NUM_SLASH")?;

        let weapon_names = vec![
            "SHOTGUN",
            "SSHOTGUN",
            "NAILGUN",
            "SNAILGUN",
            "RLAUNCH",
            "SRLAUNCH",
            "LIGHTNG"
        ];

        let weapon_prefixes = vec!["", "2", "A1", "A2", "A3", "A4", "A5"];

        let mut weapons = Vec::new();

        for w_name in &weapon_names {
            let mut weapon_frames = Vec::new();

            for w_prefix in &weapon_prefixes {
                weapon_frames.push(qpic_to_bitmap(&format!("INV{}_{}", w_prefix, w_name))?);
            }

            weapons.push(weapon_frames.into_boxed_slice());
        }

        let ammo_names = vec!["SHELLS", "NAILS", "ROCKET", "CELLS"];
        let mut ammo = Vec::new();
        for ammo_name in &ammo_names {
            ammo.push(qpic_to_bitmap(&format!("SB_{}", ammo_name))?);
        }

        let mut armor = Vec::new();
        for i in 1..4 {
            armor.push(qpic_to_bitmap(&format!("SB_ARMOR{}", i))?);
        }

        let item_names = vec!["KEY1", "KEY2", "INVIS", "INVULN", "SUIT", "QUAD"];
        let mut items = Vec::new();
        for item_name in &item_names {
            items.push(qpic_to_bitmap(&format!("SB_{}", item_name))?);
        }

        let mut sigils = Vec::new();
        for i in 1..5 {
            sigils.push(qpic_to_bitmap(&format!("SB_SIGIL{}", i))?);
        }

        let mut faces = Vec::new();
        let mut pain_faces = Vec::new();
        for i in 1..6 {
            faces.push(qpic_to_bitmap(&format!("FACE{}", i))?);
            pain_faces.push(qpic_to_bitmap(&format!("FACE_P{}", i))?);
        }

        let face_invis = qpic_to_bitmap("FACE_INVIS")?;
        let face_invuln = qpic_to_bitmap("FACE_INVUL2")?;
        let face_invis_invuln = qpic_to_bitmap("FACE_INV2")?;
        let face_quad = qpic_to_bitmap("FACE_QUAD")?;

        let sbar = qpic_to_bitmap("SBAR")?;
        let ibar = qpic_to_bitmap("IBAR")?;
        let scorebar = qpic_to_bitmap("SCOREBAR")?;

        // TODO: use a cvar to determine HUD scaling (for now, do 2:1)

        Ok(HudRenderer {
            glyph_renderer,

            digits: digits.into_boxed_slice(),
            minus,
            alt_digits: alt_digits.into_boxed_slice(),
            alt_minus,
            colon,
            slash,

            weapons: weapons.into_boxed_slice(),
            ammo: ammo.into_boxed_slice(),
            sigils: sigils.into_boxed_slice(),
            armor: armor.into_boxed_slice(),
            items: items.into_boxed_slice(),
            faces: faces.into_boxed_slice(),
            pain_faces: pain_faces.into_boxed_slice(),

            face_invis,
            face_invuln,
            face_invis_invuln,
            face_quad,
            sbar,
            ibar,
            scorebar,

            vertex_buffer,
        })
    }

    pub fn render_bitmap<C>(
        &self,
        bitmap: &BitmapTexture,
        encoder: &mut Encoder<Resources, C>,
        pso: &PipelineState<Resources, <pipeline2d::Data<Resources> as PipelineData<Resources>>::Meta>,
        user_data: &mut pipeline2d::Data<Resources>,
        display_width: u32,
        display_height: u32,
        position_x: i32,
        position_y: i32,
    )
    where
        C: CommandBuffer<Resources>,
    {
        user_data.vertex_buffer = self.vertex_buffer.clone();
        user_data.transform = bitmap.transform(display_width, display_height, position_x, position_y).into();
        user_data.sampler.0 = bitmap.view();
        encoder.draw(&render::QUAD_SLICE, pso, user_data);
    }

    pub fn render<F, C>(
        &mut self,
        factory: &mut F,
        encoder: &mut Encoder<Resources, C>,
        pso: &PipelineState<Resources, <pipeline2d::Data<Resources> as PipelineData<Resources>>::Meta>,
        user_data: &mut pipeline2d::Data<Resources>,
        client: &Client,
        display_width: u32,
        display_height: u32,
    ) -> Result<(), Error>
    where
        F: Factory<Resources>,
        C: CommandBuffer<Resources>,
    {
        // TODO: scale using a cvar (`r_hudscale` or something)
        let display_width = display_width / 2;
        let display_height = display_height / 2;

        let _guard = flame::start_guard("HudRenderer::render");

        let sbar_x = (display_width - self.sbar.width()) as i32 / 2;
        let sbar_y = 0i32;

        let ibar_x = sbar_x;
        let ibar_y = sbar_y + self.sbar.height() as i32;

        self.render_bitmap(&self.sbar, encoder, pso, user_data, display_width, display_height, sbar_x, sbar_y);
        self.render_bitmap(
            &self.ibar,
            encoder,
            pso,
            user_data,
            display_width,
            display_height,
            ibar_x,
            ibar_y,
        );

        // weapons
        for i in 0..8 {
            if client.items().contains(ItemFlags::from_bits(ItemFlags::SHOTGUN.bits() << i).unwrap()) {
                let get_time = client.item_get_time()[i];
                let delta = client.get_time() - get_time;
                let flash_on = if delta >= Duration::milliseconds(100) {
                    if client.active_weapon() as u32 == ItemFlags::SHOTGUN.bits() << i {
                        1
                    } else {
                        0
                    }
                } else {
                    (delta.num_milliseconds() * 100) as usize % 5 + 2
                };

                self.render_bitmap(
                    &self.weapons[i][flash_on],
                    encoder,
                    pso,
                    user_data,
                    display_width,
                    display_height,
                    sbar_x + 24 * i as i32,
                    sbar_y + self.sbar.height() as i32
                );
            }
        }

        // ammo
        for i in 0..4 {
            let ammo_str = format!("{: >3}", client.stats()[ClientStat::Shells as usize + i]);
            for (chr_id, chr) in ammo_str.chars().enumerate() {
                if chr != ' ' {
                    self.glyph_renderer.render_glyph(
                        encoder,
                        pso,
                        user_data,
                        18 + chr as u8 - '0' as u8,
                        display_width,
                        display_height,
                        ibar_x + (6 * i + chr_id) as i32 * 8 + 10,
                        ibar_y + 16,
                    )?;
                }
            }
        }

        for i in 0..6 {
            if client.items().contains(ItemFlags::from_bits(ItemFlags::KEY_1.bits() << i).unwrap()) {
                let get_time = client.item_get_time()[17 + i];
                let delta = client.get_time() - get_time;

                // TODO: add !hipnotic as a condition
                if i > 1 {
                    self.render_bitmap(
                        &self.items[i],
                        encoder,
                        pso,
                        user_data,
                        display_width,
                        display_height,
                        sbar_x + 192 + 16 * i as i32,
                        sbar_y + self.sbar.height() as i32,
                    );
                }
            }
        }

        for i in 0..4 {
            if client.items().contains(ItemFlags::from_bits(ItemFlags::SIGIL_1.bits() << i).unwrap()) {
                let get_time = client.item_get_time()[28 + i];
                self.render_bitmap(
                    &self.sigils[i],
                    encoder,
                    pso,
                    user_data,
                    display_width,
                    display_height,
                    sbar_x + 288 + 8 * i as i32,
                    sbar_y + self.sbar.height() as i32,
                );
            }
        }

        Ok(())
    }
}
<commit_msg>Add armor, health & current ammo to HUD<commit_after>// Copyright © 2018 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

use std::rc::Rc;

use client::Client;
use client::render::{self, Palette, Vertex2d};
use client::render::bitmap::BitmapTexture;
use client::render::glyph::GlyphRenderer;
use client::render::pipeline2d;
use common::net::{ClientStat, ItemFlags, MAX_ITEMS};
use common::wad::Wad;

use cgmath::Matrix4;
use cgmath::SquareMatrix;
use chrono::Duration;
use flame;
use gfx::{CommandBuffer, Encoder, Factory, IndexBuffer, Slice};
use gfx::format::R8_G8_B8_A8;
use gfx::handle::{Buffer, ShaderResourceView, Texture};
use gfx::pso::{PipelineData, PipelineState};
use gfx_device_gl::Resources;

use failure::Error;

enum WeaponSlots {
    Shotgun = 0,
    SuperShotgun = 1,
    Nailgun = 2,
    SuperNailgun = 3,
    RocketLauncher = 4,
    GrenadeLauncher = 5,
    LightningGun = 6,
}

enum AmmoSlots {
    Shells = 0,
    Nails = 1,
    Rockets = 2,
    Cells = 3,
}

pub struct HudRenderer {
    glyph_renderer: Rc<GlyphRenderer>,
    digits: Box<[BitmapTexture]>,
    minus: BitmapTexture,
    alt_digits: Box<[BitmapTexture]>,
    alt_minus: BitmapTexture,
    colon: BitmapTexture,
    slash: BitmapTexture,

    weapons: Box<[Box<[BitmapTexture]>]>,
    ammo: Box<[BitmapTexture]>,
    sigils: Box<[BitmapTexture]>,
    armor: Box<[BitmapTexture]>,
    items: Box<[BitmapTexture]>,
    faces: Box<[BitmapTexture]>,
    pain_faces: Box<[BitmapTexture]>,

    face_invis: BitmapTexture,
    face_invuln: BitmapTexture,
    face_invis_invuln: BitmapTexture,
    face_quad: BitmapTexture,
    sbar: BitmapTexture,
    ibar: BitmapTexture,
    scorebar: BitmapTexture,

    vertex_buffer: Buffer<Resources, Vertex2d>,
}

impl HudRenderer {
    pub fn new<F>(
        glyph_renderer: Rc<GlyphRenderer>,
        gfx_wad: &Wad,
        palette: &Palette,
        factory: &mut F
    ) -> Result<HudRenderer, Error>
    where
        F: Factory<Resources>
    {
        use gfx::traits::FactoryExt;
        let vertex_buffer = factory.create_vertex_buffer(&render::QUAD_VERTICES);

        let mut digits = Vec::new();
        let mut alt_digits = Vec::new();

        // just to make the following code a bit less painful
        let mut qpic_to_bitmap = |name: &str| -> Result<BitmapTexture, Error> {
            let qpic = gfx_wad.open_qpic(name)?;
            BitmapTexture::from_qpic(factory, &qpic, palette)
        };

        for i in 0..10 {
            digits.push(qpic_to_bitmap(&format!("NUM_{}", i))?);
            alt_digits.push(qpic_to_bitmap(&format!("ANUM_{}", i))?);
        }

        let minus = qpic_to_bitmap("NUM_MINUS")?;
        let alt_minus = qpic_to_bitmap("ANUM_MINUS")?;
        let colon = qpic_to_bitmap("NUM_COLON")?;
        let slash = qpic_to_bitmap("NUM_SLASH")?;

        let weapon_names = vec![
            "SHOTGUN",
            "SSHOTGUN",
            "NAILGUN",
            "SNAILGUN",
            "RLAUNCH",
            "SRLAUNCH",
            "LIGHTNG"
        ];

        let weapon_prefixes = vec!["", "2", "A1", "A2", "A3", "A4", "A5"];

        let mut weapons = Vec::new();

        for w_name in &weapon_names {
            let mut weapon_frames = Vec::new();

            for w_prefix in &weapon_prefixes {
                weapon_frames.push(qpic_to_bitmap(&format!("INV{}_{}", w_prefix, w_name))?);
            }

            weapons.push(weapon_frames.into_boxed_slice());
        }

        let ammo_names = vec!["SHELLS", "NAILS", "ROCKET", "CELLS"];
        let mut ammo = Vec::new();
        for ammo_name in &ammo_names {
            ammo.push(qpic_to_bitmap(&format!("SB_{}", ammo_name))?);
        }

        let mut armor = Vec::new();
        for i in 1..4 {
            armor.push(qpic_to_bitmap(&format!("SB_ARMOR{}", i))?);
        }

        let item_names = vec!["KEY1", "KEY2", "INVIS", "INVULN", "SUIT", "QUAD"];
        let mut items = Vec::new();
        for item_name in &item_names {
            items.push(qpic_to_bitmap(&format!("SB_{}", item_name))?);
        }

        let mut sigils = Vec::new();
        for i in 1..5 {
            sigils.push(qpic_to_bitmap(&format!("SB_SIGIL{}", i))?);
        }

        let mut faces = Vec::new();
        let mut pain_faces = Vec::new();
        for i in 1..6 {
            faces.push(qpic_to_bitmap(&format!("FACE{}", i))?);
            pain_faces.push(qpic_to_bitmap(&format!("FACE_P{}", i))?);
        }

        let face_invis = qpic_to_bitmap("FACE_INVIS")?;
        let face_invuln = qpic_to_bitmap("FACE_INVUL2")?;
        let face_invis_invuln = qpic_to_bitmap("FACE_INV2")?;
        let face_quad = qpic_to_bitmap("FACE_QUAD")?;

        let sbar = qpic_to_bitmap("SBAR")?;
        let ibar = qpic_to_bitmap("IBAR")?;
        let scorebar = qpic_to_bitmap("SCOREBAR")?;

        // TODO: use a cvar to determine HUD scaling (for now, do 2:1)

        Ok(HudRenderer {
            glyph_renderer,

            digits: digits.into_boxed_slice(),
            minus,
            alt_digits: alt_digits.into_boxed_slice(),
            alt_minus,
            colon,
            slash,

            weapons: weapons.into_boxed_slice(),
            ammo: ammo.into_boxed_slice(),
            sigils: sigils.into_boxed_slice(),
            armor: armor.into_boxed_slice(),
            items: items.into_boxed_slice(),
            faces: faces.into_boxed_slice(),
            pain_faces: pain_faces.into_boxed_slice(),

            face_invis,
            face_invuln,
            face_invis_invuln,
            face_quad,
            sbar,
            ibar,
            scorebar,

            vertex_buffer,
        })
    }

    pub fn render_bitmap<C>(
        &self,
        bitmap: &BitmapTexture,
        encoder: &mut Encoder<Resources, C>,
        pso: &PipelineState<Resources, <pipeline2d::Data<Resources> as PipelineData<Resources>>::Meta>,
        user_data: &mut pipeline2d::Data<Resources>,
        display_width: u32,
        display_height: u32,
        position_x: i32,
        position_y: i32,
    )
    where
        C: CommandBuffer<Resources>,
    {
        user_data.vertex_buffer = self.vertex_buffer.clone();
        user_data.transform = bitmap.transform(display_width, display_height, position_x, position_y).into();
        user_data.sampler.0 = bitmap.view();
        encoder.draw(&render::QUAD_SLICE, pso, user_data);
    }

    pub fn render_number<C>(
        &self,
        number: i32,
        max_digits: usize,
        alt_color: bool,
        encoder: &mut Encoder<Resources, C>,
        pso: &PipelineState<Resources, <pipeline2d::Data<Resources> as PipelineData<Resources>>::Meta>,
        user_data: &mut pipeline2d::Data<Resources>,
        display_width: u32,
        display_height: u32,
        position_x: i32,
        position_y: i32,
    )
    where
        C: CommandBuffer<Resources>,
    {
        let number_str = format!("{}", number);
        let number_chars: Vec<char> = number_str.chars().collect();

        let mut skip = 0;
        let mut ofs = 0;
        if number_chars.len() > max_digits {
            skip = number_chars.len() - max_digits;
        } else if max_digits > number_chars.len() {
            ofs = (max_digits - number_chars.len()) as i32 * 24;
        }

        for (chr_id, chr) in number_chars.into_iter().skip(skip).enumerate() {
            self.render_bitmap(
                match chr {
                    '-' => if alt_color { &self.alt_minus } else { &self.minus },
                    '0'..='9' => {
                        let index = chr as usize - '0' as usize;
                        if alt_color {
                            &self.alt_digits[index]
                        } else {
                            &self.digits[index]
                        }
                    }
                    _ => unreachable!(),
                },
                encoder,
                pso,
                user_data,
                display_width,
                display_height,
                position_x + ofs + 24 * chr_id as i32,
                position_y,
            );
        }
    }

    pub fn render<F, C>(
        &mut self,
        factory: &mut F,
        encoder: &mut Encoder<Resources, C>,
        pso: &PipelineState<Resources, <pipeline2d::Data<Resources> as PipelineData<Resources>>::Meta>,
        user_data: &mut pipeline2d::Data<Resources>,
        client: &Client,
        display_width: u32,
        display_height: u32,
    ) -> Result<(), Error>
    where
        F: Factory<Resources>,
        C: CommandBuffer<Resources>,
    {
        // TODO: scale using a cvar (`r_hudscale` or something)
        let display_width = display_width / 2;
        let display_height = display_height / 2;

        let _guard = flame::start_guard("HudRenderer::render");

        let sbar_x = (display_width - self.sbar.width()) as i32 / 2;
        let sbar_y = 0i32;

        let ibar_x = sbar_x;
        let ibar_y = sbar_y + self.sbar.height() as i32;

        self.render_bitmap(&self.sbar, encoder, pso, user_data, display_width, display_height, sbar_x, sbar_y);
        self.render_bitmap(
            &self.ibar,
            encoder,
            pso,
            user_data,
            display_width,
            display_height,
            ibar_x,
            ibar_y,
        );

        // weapons
        for i in 0..8 {
            if client.items().contains(ItemFlags::from_bits(ItemFlags::SHOTGUN.bits() << i).unwrap()) {
                let get_time = client.item_get_time()[i];
                let delta = client.get_time() - get_time;
                let flash_on = if delta >= Duration::milliseconds(100) {
                    if client.active_weapon() as u32 == ItemFlags::SHOTGUN.bits() << i {
                        1
                    } else {
                        0
                    }
                } else {
                    (delta.num_milliseconds() * 100) as usize % 5 + 2
                };

                self.render_bitmap(
                    &self.weapons[i][flash_on],
                    encoder,
                    pso,
                    user_data,
                    display_width,
                    display_height,
                    sbar_x + 24 * i as i32,
                    sbar_y + self.sbar.height() as i32
                );
            }
        }

        // ammo
        for i in 0..4 {
            let ammo_str = format!("{: >3}", client.stats()[ClientStat::Shells as usize + i]);
            for (chr_id, chr) in ammo_str.chars().enumerate() {
                if chr != ' ' {
                    self.glyph_renderer.render_glyph(
                        encoder,
                        pso,
                        user_data,
                        18 + chr as u8 - '0' as u8,
                        display_width,
                        display_height,
                        ibar_x + (6 * i + chr_id) as i32 * 8 + 10,
                        ibar_y + 16,
                    )?;
                }
            }
        }

        for i in 0..6 {
            if client.items().contains(ItemFlags::from_bits(ItemFlags::KEY_1.bits() << i).unwrap()) {
                let get_time = client.item_get_time()[17 + i];
                let delta = client.get_time() - get_time;

                // TODO: add !hipnotic as a condition
                if i > 1 {
                    self.render_bitmap(
                        &self.items[i],
                        encoder,
                        pso,
                        user_data,
                        display_width,
                        display_height,
                        sbar_x + 192 + 16 * i as i32,
                        sbar_y + self.sbar.height() as i32,
                    );
                }
            }
        }

        for i in 0..4 {
            if client.items().contains(ItemFlags::from_bits(ItemFlags::SIGIL_1.bits() << i).unwrap()) {
                let get_time = client.item_get_time()[28 + i];
                self.render_bitmap(
                    &self.sigils[i],
                    encoder,
                    pso,
                    user_data,
                    display_width,
                    display_height,
                    sbar_x + 288 + 8 * i as i32,
                    sbar_y + self.sbar.height() as i32,
                );
            }
        }

        // armor
        if client.items().contains(ItemFlags::INVULNERABILITY) {
            self.render_number(
                666,
                3,
                true,
                encoder,
                pso,
                user_data,
                display_width,
                display_height,
                sbar_x + 24,
                sbar_y,
            );
            // TODO: draw_disc
        } else {
            let armor = client.stats()[ClientStat::Armor as usize];
            self.render_number(
                armor,
                3,
                armor <= 25,
                encoder,
                pso,
                user_data,
                display_width,
                display_height,
                sbar_x + 24,
                sbar_y,
            );

            let armor_pic_id = if client.items().contains(ItemFlags::ARMOR_3) {
                Some(2)
            } else if client.items().contains(ItemFlags::ARMOR_2) {
                Some(1)
            } else if client.items().contains(ItemFlags::ARMOR_1) {
                Some(0)
            } else {
                None
            };

            if let Some(i) = armor_pic_id {
                self.render_bitmap(
                    &self.armor[i],
                    encoder,
                    pso,
                    user_data,
                    display_width,
                    display_height,
                    sbar_x,
                    sbar_y,
                );
            }
        }

        // health
        let health = client.stats()[ClientStat::Health as usize];
        self.render_number(
            health,
            3,
            health <= 25,
            encoder,
            pso,
            user_data,
            display_width,
            display_height,
            sbar_x + 136,
            sbar_y,
        );

        let ammo_id = if client.items().contains(ItemFlags::SHELLS) {
            Some(0)
        } else if client.items().contains(ItemFlags::NAILS) {
            Some(1)
        } else if client.items().contains(ItemFlags::ROCKETS) {
            Some(2)
        } else if client.items().contains(ItemFlags::CELLS) {
            Some(3)
        } else {
            None
        };

        if let Some(i) = ammo_id {
            self.render_bitmap(
                &self.ammo[i],
                encoder,
                pso,
                user_data,
                display_width,
                display_height,
                sbar_x + 224,
                sbar_y,
            );
        }

        let ammo = client.stats()[ClientStat::Ammo as usize];
        self.render_number(
            ammo,
            3,
            ammo <= 10,
            encoder,
            pso,
            user_data,
            display_width,
            display_height,
            sbar_x + 248,
            sbar_y,
        );

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


<commit_before>use core::libc::size_t;
use core::ptr::{null, to_mut_unsafe_ptr};
use core::cast::transmute;
use ll::*;

fn require_ok(code: lwc_error) {
    match code {
        lwc_error_ok => (),
        e => fail fmt!("lwc error: %?", e)
    }
}

pub struct LwcString {
    priv string: *lwc_string,

    drop {
        rust_lwc_string_unref(self.string);
    }
}

pub fn from_rust_string(s: &str) -> LwcString {
    let mut interned_string = null();
    do str::as_c_str(s) |cs| {
        let code = lwc_intern_string(cs, s.len() as size_t, to_mut_unsafe_ptr(&mut interned_string));
        require_ok(code);
    }

    assert interned_string.is_not_null();

    LwcString {
        string: interned_string
    }
}

pub fn from_lwc_string(s: *lwc_string) -> LwcString {
    rust_lwc_string_ref(s);
    LwcString {
        string: s
    }
}

impl LwcString {
    fn len() -> uint {
        unsafe {
            (*self.string).len as uint
        }
    }

    fn clone() -> LwcString {
        from_lwc_string(self.string)
    }

    fn raw_reffed() -> *lwc_string {
        rust_lwc_string_ref(self.string);
        self.string
    }
}

impl LwcString: ToStr {
    pure fn to_str() -> ~str {
        unsafe {
            // The string is located immediately after the handle
            let p: *c_char = transmute(self.string.offset(1));
            str::raw::from_c_str_len(p, self.len())
        }
    }
}

#[test]
fn smoke_test() {
    let s1 = from_rust_string("test");
    let s2 = from_lwc_string(s1.string);
    debug!("%?", s1.len());
    debug!("%?", s2.len());
    assert s2.len() == 4;
    let s3 = s2.to_str();
    assert s3 == ~"test";
}

#[test]
fn multithreading_test() {
    for uint::range(0, 100) |i| {
        for iter::repeat(50) {
            do task::spawn {
                let s = vec::from_elem(i + 1, 0).to_str();
                let s = from_rust_string(s);
                let _t = s.clone();
            }
        }
    }
}<commit_msg>Allow contents of a LwcString to be converted to Rust without copying<commit_after>use core::libc::size_t;
use core::ptr::{null, to_mut_unsafe_ptr};
use core::cast::transmute;
use ll::*;

fn require_ok(code: lwc_error) {
    match code {
        lwc_error_ok => (),
        e => fail fmt!("lwc error: %?", e)
    }
}

pub struct LwcString {
    priv string: *lwc_string,

    drop {
        rust_lwc_string_unref(self.string);
    }
}

pub fn from_rust_string(s: &str) -> LwcString {
    let mut interned_string = null();
    do str::as_c_str(s) |cs| {
        let code = lwc_intern_string(cs, s.len() as size_t, to_mut_unsafe_ptr(&mut interned_string));
        require_ok(code);
    }

    assert interned_string.is_not_null();

    LwcString {
        string: interned_string
    }
}

pub fn from_lwc_string(s: *lwc_string) -> LwcString {
    rust_lwc_string_ref(s);
    LwcString {
        string: s
    }
}

impl LwcString {
    fn len() -> uint {
        unsafe {
            (*self.string).len as uint
        }
    }

    fn clone() -> LwcString {
        from_lwc_string(self.string)
    }

    fn raw_reffed() -> *lwc_string {
        rust_lwc_string_ref(self.string);
        self.string
    }

    fn to_str_slice(&self) -> &self/str {
        unsafe {
            let p: *u8 = transmute(self.string.offset(1));
            do str::raw::buf_as_slice(p, self.len()) |slice| {
                transmute(slice)
            }
        }
    }
}

impl LwcString: ToStr {
    pure fn to_str() -> ~str {
        unsafe {
            // The string is located immediately after the handle
            let p: *c_char = transmute(self.string.offset(1));
            str::raw::from_c_str_len(p, self.len())
        }
    }
}

#[test]
fn smoke_test() {
    let s1 = from_rust_string("test");
    let s2 = from_lwc_string(s1.string);
    debug!("%?", s1.len());
    debug!("%?", s2.len());
    assert s2.len() == 4;
    let s3 = s2.to_str();
    assert s3 == ~"test";
}

#[test]
fn multithreading_test() {
    for uint::range(0, 100) |i| {
        for iter::repeat(50) {
            do task::spawn {
                let s = vec::from_elem(i + 1, 0).to_str();
                let s = from_rust_string(s);
                let _t = s.clone();
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Simple file-locking apis for each OS.
//!
//! This is not meant to be in the standard library, it does nothing with
//! green/native threading. This is just a bare-bones enough solution for
//! librustdoc, it is not production quality at all.

#![allow(non_camel_case_types)]
#![allow(nonstandard_style)]

use std::io;
use std::path::Path;

cfg_if! {
    if #[cfg(unix)] {
        use std::os::unix::prelude::*;
        use std::fs::{File, OpenOptions};

        #[derive(Debug)]
        pub struct Lock {
            _file: File,
        }

        impl Lock {
            pub fn new(p: &Path,
                       wait: bool,
                       create: bool,
                       exclusive: bool)
                       -> io::Result<Lock> {
                let file = OpenOptions::new()
                           .read(true)
                           .write(true)
                           .create(create)
                           .mode(libc::S_IRWXU as u32)
                           .open(p)?;

                let mut operation = if exclusive {
                    libc::LOCK_EX
                } else {
                    libc::LOCK_SH
                };
                if !wait {
                    operation |= libc::LOCK_NB
                }

                let ret = unsafe { libc::flock(file.as_raw_fd(), operation) };
                if ret == -1 {
                    let err = io::Error::last_os_error();
                    Err(err)
                } else {
                    Ok(Lock { _file: file })
                }
            }
        }

        // Note that we don't need a Drop impl to execute `flock(fd, LOCK_UN)`. Lock acquired by
        // `flock` is associated with the file descriptor and closing the file release it
        // automatically.
    } else if #[cfg(windows)] {
        use std::mem;
        use std::os::windows::prelude::*;
        use std::fs::{File, OpenOptions};

        use winapi::um::minwinbase::{OVERLAPPED, LOCKFILE_FAIL_IMMEDIATELY, LOCKFILE_EXCLUSIVE_LOCK};
        use winapi::um::fileapi::LockFileEx;
        use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};

        #[derive(Debug)]
        pub struct Lock {
            _file: File,
        }

        impl Lock {
            pub fn new(p: &Path,
                       wait: bool,
                       create: bool,
                       exclusive: bool)
                       -> io::Result<Lock> {
                assert!(p.parent().unwrap().exists(),
                    "Parent directory of lock-file must exist: {}",
                    p.display());

                let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;

                let mut open_options = OpenOptions::new();
                open_options.read(true)
                            .share_mode(share_mode);

                if create {
                    open_options.create(true)
                                .write(true);
                }

                debug!("attempting to open lock file `{}`", p.display());
                let file = match open_options.open(p) {
                    Ok(file) => {
                        debug!("lock file opened successfully");
                        file
                    }
                    Err(err) => {
                        debug!("error opening lock file: {}", err);
                        return Err(err)
                    }
                };

                let ret = unsafe {
                    let mut overlapped: OVERLAPPED = mem::zeroed();

                    let mut dwFlags = 0;
                    if !wait {
                        dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
                    }

                    if exclusive {
                        dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
                    }

                    debug!("attempting to acquire lock on lock file `{}`",
                           p.display());
                    LockFileEx(file.as_raw_handle(),
                               dwFlags,
                               0,
                               0xFFFF_FFFF,
                               0xFFFF_FFFF,
                               &mut overlapped)
                };
                if ret == 0 {
                    let err = io::Error::last_os_error();
                    debug!("failed acquiring file lock: {}", err);
                    Err(err)
                } else {
                    debug!("successfully acquired lock");
                    Ok(Lock { _file: file })
                }
            }
        }

        // Note that we don't need a Drop impl on the Windows: The file is unlocked
        // automatically when it's closed.
    } else {
        #[derive(Debug)]
        pub struct Lock(());

        impl Lock {
            pub fn new(_p: &Path, _wait: bool, _create: bool, _exclusive: bool)
                -> io::Result<Lock>
            {
                let msg = "file locks not supported on this platform";
                Err(io::Error::new(io::ErrorKind::Other, msg))
            }
        }
    }
}
<commit_msg>Use fcntl-based file lock for non-Linux unix<commit_after>//! Simple file-locking apis for each OS.
//!
//! This is not meant to be in the standard library, it does nothing with
//! green/native threading. This is just a bare-bones enough solution for
//! librustdoc, it is not production quality at all.

#![allow(non_camel_case_types)]
#![allow(nonstandard_style)]

use std::fs::{File, OpenOptions};
use std::io;
use std::path::Path;

cfg_if! {
    if #[cfg(target_os = "linux")] {
        use std::os::unix::prelude::*;

        #[derive(Debug)]
        pub struct Lock {
            _file: File,
        }

        impl Lock {
            pub fn new(p: &Path,
                       wait: bool,
                       create: bool,
                       exclusive: bool)
                       -> io::Result<Lock> {
                let file = OpenOptions::new()
                    .read(true)
                    .write(true)
                    .create(create)
                    .mode(libc::S_IRWXU as u32)
                    .open(p)?;

                let mut operation = if exclusive {
                    libc::LOCK_EX
                } else {
                    libc::LOCK_SH
                };
                if !wait {
                    operation |= libc::LOCK_NB
                }

                let ret = unsafe { libc::flock(file.as_raw_fd(), operation) };
                if ret == -1 {
                    Err(io::Error::last_os_error())
                } else {
                    Ok(Lock { _file: file })
                }
            }
        }

        // Note that we don't need a Drop impl to execute `flock(fd, LOCK_UN)`. Lock acquired by
        // `flock` is associated with the file descriptor and closing the file release it
        // automatically.
    } else if #[cfg(unix)] {
        use std::mem;
        use std::os::unix::prelude::*;

        #[derive(Debug)]
        pub struct Lock {
            file: File,
        }

        impl Lock {
            pub fn new(p: &Path,
                       wait: bool,
                       create: bool,
                       exclusive: bool)
                       -> io::Result<Lock> {
                let file = OpenOptions::new()
                    .read(true)
                    .write(true)
                    .create(create)
                    .mode(libc::S_IRWXU as u32)
                    .open(p)?;

                let lock_type = if exclusive {
                    libc::F_WRLCK
                } else {
                    libc::F_RDLCK
                };

                let mut flock: libc::flock = unsafe { mem::zeroed() };
                flock.l_type = lock_type as libc::c_short;
                flock.l_whence = libc::SEEK_SET as libc::c_short;
                flock.l_start = 0;
                flock.l_len = 0;

                let cmd = if wait { libc::F_SETLKW } else { libc::F_SETLK };
                let ret = unsafe {
                    libc::fcntl(file.as_raw_fd(), cmd, &flock)
                };
                if ret == -1 {
                    Err(io::Error::last_os_error())
                } else {
                    Ok(Lock { file })
                }
            }
        }

        impl Drop for Lock {
            fn drop(&mut self) {
                let mut flock: libc::flock = unsafe { mem::zeroed() };
                flock.l_type = libc::F_UNLCK as libc::c_short;
                flock.l_whence = libc::SEEK_SET as libc::c_short;
                flock.l_start = 0;
                flock.l_len = 0;

                unsafe {
                    libc::fcntl(self.file.as_raw_fd(), libc::F_SETLK, &flock);
                }
            }
        }
    } else if #[cfg(windows)] {
        use std::mem;
        use std::os::windows::prelude::*;

        use winapi::um::minwinbase::{OVERLAPPED, LOCKFILE_FAIL_IMMEDIATELY, LOCKFILE_EXCLUSIVE_LOCK};
        use winapi::um::fileapi::LockFileEx;
        use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};

        #[derive(Debug)]
        pub struct Lock {
            _file: File,
        }

        impl Lock {
            pub fn new(p: &Path,
                       wait: bool,
                       create: bool,
                       exclusive: bool)
                       -> io::Result<Lock> {
                assert!(p.parent().unwrap().exists(),
                    "Parent directory of lock-file must exist: {}",
                    p.display());

                let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;

                let mut open_options = OpenOptions::new();
                open_options.read(true)
                            .share_mode(share_mode);

                if create {
                    open_options.create(true)
                                .write(true);
                }

                debug!("attempting to open lock file `{}`", p.display());
                let file = match open_options.open(p) {
                    Ok(file) => {
                        debug!("lock file opened successfully");
                        file
                    }
                    Err(err) => {
                        debug!("error opening lock file: {}", err);
                        return Err(err)
                    }
                };

                let ret = unsafe {
                    let mut overlapped: OVERLAPPED = mem::zeroed();

                    let mut dwFlags = 0;
                    if !wait {
                        dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
                    }

                    if exclusive {
                        dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
                    }

                    debug!("attempting to acquire lock on lock file `{}`",
                           p.display());
                    LockFileEx(file.as_raw_handle(),
                               dwFlags,
                               0,
                               0xFFFF_FFFF,
                               0xFFFF_FFFF,
                               &mut overlapped)
                };
                if ret == 0 {
                    let err = io::Error::last_os_error();
                    debug!("failed acquiring file lock: {}", err);
                    Err(err)
                } else {
                    debug!("successfully acquired lock");
                    Ok(Lock { _file: file })
                }
            }
        }

        // Note that we don't need a Drop impl on the Windows: The file is unlocked
        // automatically when it's closed.
    } else {
        #[derive(Debug)]
        pub struct Lock(());

        impl Lock {
            pub fn new(_p: &Path, _wait: bool, _create: bool, _exclusive: bool)
                -> io::Result<Lock>
            {
                let msg = "file locks not supported on this platform";
                Err(io::Error::new(io::ErrorKind::Other, msg))
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Implement a struct that works as a `vmm_vhost`'s backend.

use std::io::{IoSlice, IoSliceMut};
use std::mem;
use std::os::unix::io::RawFd;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread;

use anyhow::{anyhow, bail, Context, Result};
use base::{error, info, Event};
use cros_async::{EventAsync, Executor};
use futures::{pin_mut, select, FutureExt};
use sync::Mutex;
use vmm_vhost::connection::vfio::{Device as VfioDeviceTrait, RecvIntoBufsError};

use crate::virtio::vhost::user::device::vvu::{
    pci::{QueueNotifier, VvuPciDevice},
    queue::UserQueue,
};

async fn process_rxq(
    evt: EventAsync,
    mut rxq: UserQueue,
    rxq_sender: Sender<Vec<u8>>,
    rxq_evt: Event,
) -> Result<()> {
    loop {
        if let Err(e) = evt.next_val().await {
            error!("Failed to read the next queue event: {}", e);
            continue;
        }

        while let Some(slice) = rxq.read_data()? {
            let mut buf = vec![0; slice.size()];
            slice.copy_to(&mut buf);
            rxq_sender.send(buf)?;
            // Increment the event counter as we sent one buffer.
            rxq_evt.write(1).context("process_rxq")?;
        }
    }
}

async fn process_txq(evt: EventAsync, txq: Arc<Mutex<UserQueue>>) -> Result<()> {
    loop {
        if let Err(e) = evt.next_val().await {
            error!("Failed to read the next queue event: {}", e);
            continue;
        }

        txq.lock().ack_used()?;
    }
}

fn run_worker(
    ex: Executor,
    rx_queue: UserQueue,
    rx_irq: Event,
    rx_sender: Sender<Vec<u8>>,
    rx_evt: Event,
    tx_queue: Arc<Mutex<UserQueue>>,
    tx_irq: Event,
) -> Result<()> {
    let rx_irq = EventAsync::new(rx_irq, &ex).context("failed to create async event")?;
    let rxq = process_rxq(rx_irq, rx_queue, rx_sender, rx_evt);
    pin_mut!(rxq);

    let tx_irq = EventAsync::new(tx_irq, &ex).context("failed to create async event")?;
    let txq = process_txq(tx_irq, Arc::clone(&tx_queue));
    pin_mut!(txq);

    let done = async {
        select! {
            res = rxq.fuse() => res.context("failed to handle rxq"),
            res = txq.fuse() => res.context("failed to handle txq"),
        }
    };

    match ex.run_until(done) {
        Ok(_) => Ok(()),
        Err(e) => {
            bail!("failed to process virtio-vhost-user queues: {}", e);
        }
    }
}

enum DeviceState {
    Initialized {
        // TODO(keiichiw): Update `VfioDeviceTrait::start()` to take `VvuPciDevice` so that we can
        // drop this field.
        device: VvuPciDevice,
    },
    Running {
        rxq_notifier: Arc<Mutex<QueueNotifier>>,
        rxq_receiver: Receiver<Vec<u8>>,
        /// Store data that was provided by rxq_receiver but not consumed yet.
        rxq_buf: Vec<u8>,

        txq: Arc<Mutex<UserQueue>>,
        txq_notifier: Arc<Mutex<QueueNotifier>>,
    },
}

pub struct VvuDevice {
    state: DeviceState,
    rxq_evt: Event,
}

impl VvuDevice {
    pub fn new(device: VvuPciDevice) -> Self {
        Self {
            state: DeviceState::Initialized { device },
            rxq_evt: Event::new().expect("failed to create VvuDevice's rxq_evt"),
        }
    }
}

impl VfioDeviceTrait for VvuDevice {
    fn event(&self) -> &Event {
        &self.rxq_evt
    }

    fn start(&mut self) -> Result<()> {
        let device = match &mut self.state {
            DeviceState::Initialized { device } => device,
            DeviceState::Running { .. } => {
                bail!("VvuDevice has already started");
            }
        };
        let ex = Executor::new().expect("Failed to create an executor");

        let mut irqs = mem::take(&mut device.irqs);
        let mut queues = mem::take(&mut device.queues);
        let mut queue_notifiers = mem::take(&mut device.queue_notifiers);

        let rxq = queues.remove(0);
        let rxq_irq = irqs.remove(0);
        let rxq_notifier = Arc::new(Mutex::new(queue_notifiers.remove(0)));
        // TODO: Can we use async channel instead so we don't need `rxq_evt`?
        let (rxq_sender, rxq_receiver) = channel();
        let rxq_evt = self.rxq_evt.try_clone().expect("rxq_evt clone");

        let txq = Arc::new(Mutex::new(queues.remove(0)));
        let txq_cloned = Arc::clone(&txq);
        let txq_irq = irqs.remove(0);
        let txq_notifier = Arc::new(Mutex::new(queue_notifiers.remove(0)));

        let old_state = std::mem::replace(
            &mut self.state,
            DeviceState::Running {
                rxq_notifier,
                rxq_receiver,
                rxq_buf: vec![],
                txq,
                txq_notifier,
            },
        );

        let device = match old_state {
            DeviceState::Initialized { device } => device,
            _ => unreachable!(),
        };

        thread::Builder::new()
            .name("virtio-vhost-user driver".to_string())
            .spawn(move || {
                device.start().expect("failed to start device");
                if let Err(e) =
                    run_worker(ex, rxq, rxq_irq, rxq_sender, rxq_evt, txq_cloned, txq_irq)
                {
                    error!("worker thread exited with error: {}", e);
                }
            })?;

        Ok(())
    }

    fn send_bufs(&mut self, iovs: &[IoSlice], fds: Option<&[RawFd]>) -> Result<usize> {
        if fds.is_some() {
            bail!("cannot send FDs");
        }

        let (txq, txq_notifier) = match &mut self.state {
            DeviceState::Initialized { .. } => {
                bail!("VvuDevice hasn't started yet");
            }
            DeviceState::Running {
                txq, txq_notifier, ..
            } => (txq, txq_notifier),
        };

        let size = iovs.iter().map(|v| v.len()).sum();
        let data: Vec<u8> = iovs.iter().flat_map(|v| v.to_vec()).collect();

        txq.lock().write(&data).context("Failed to send data")?;
        txq_notifier.lock().notify();

        Ok(size)
    }

    fn recv_into_bufs(&mut self, bufs: &mut [IoSliceMut]) -> Result<usize, RecvIntoBufsError> {
        let (rxq_receiver, rxq_notifier, rxq_buf) = match &mut self.state {
            DeviceState::Initialized { .. } => {
                return Err(RecvIntoBufsError::Fatal(anyhow!(
                    "VvuDevice hasn't started yet"
                )));
            }
            DeviceState::Running {
                rxq_receiver,
                rxq_notifier,
                rxq_buf,
                ..
            } => (rxq_receiver, rxq_notifier, rxq_buf),
        };

        let mut size = 0;
        for buf in bufs {
            let len = buf.len();

            while rxq_buf.len() < len {
                let mut data = rxq_receiver
                    .recv()
                    .context("failed to receive data")
                    .map_err(RecvIntoBufsError::Fatal)?;

                if data.len() == 0 {
                    // TODO(b/216407443): We should change `self.state` and exit gracefully.
                    info!("VVU connection is closed");
                    return Err(RecvIntoBufsError::Disconnect);
                }

                rxq_buf.append(&mut data);
                // Decrement the event counter as we received one buffer.
                self.rxq_evt
                    .read()
                    .and_then(|c| self.rxq_evt.write(c - 1))
                    .context("failed to decrease event counter")
                    .map_err(RecvIntoBufsError::Fatal)?;
            }

            buf.clone_from_slice(&rxq_buf[..len]);

            rxq_buf.drain(0..len);
            size += len;

            rxq_notifier.lock().notify();
        }

        Ok(size)
    }
}
<commit_msg>devices: vvu: do rx notify after processing vq<commit_after>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Implement a struct that works as a `vmm_vhost`'s backend.

use std::io::{IoSlice, IoSliceMut};
use std::mem;
use std::os::unix::io::RawFd;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread;

use anyhow::{anyhow, bail, Context, Result};
use base::{error, info, Event};
use cros_async::{EventAsync, Executor};
use futures::{pin_mut, select, FutureExt};
use sync::Mutex;
use vmm_vhost::connection::vfio::{Device as VfioDeviceTrait, RecvIntoBufsError};

use crate::virtio::vhost::user::device::vvu::{
    pci::{QueueNotifier, VvuPciDevice},
    queue::UserQueue,
};

async fn process_rxq(
    evt: EventAsync,
    mut rxq: UserQueue,
    rxq_sender: Sender<Vec<u8>>,
    rxq_notifier: QueueNotifier,
    rxq_evt: Event,
) -> Result<()> {
    loop {
        if let Err(e) = evt.next_val().await {
            error!("Failed to read the next queue event: {}", e);
            continue;
        }

        while let Some(slice) = rxq.read_data()? {
            let mut buf = vec![0; slice.size()];
            slice.copy_to(&mut buf);
            rxq_sender.send(buf)?;
            // Increment the event counter as we sent one buffer.
            rxq_evt.write(1).context("process_rxq")?;
        }

        rxq_notifier.notify();
    }
}

async fn process_txq(evt: EventAsync, txq: Arc<Mutex<UserQueue>>) -> Result<()> {
    loop {
        if let Err(e) = evt.next_val().await {
            error!("Failed to read the next queue event: {}", e);
            continue;
        }

        txq.lock().ack_used()?;
    }
}

fn run_worker(
    ex: Executor,
    rx_queue: UserQueue,
    rx_irq: Event,
    rx_sender: Sender<Vec<u8>>,
    rx_notifier: QueueNotifier,
    rx_evt: Event,
    tx_queue: Arc<Mutex<UserQueue>>,
    tx_irq: Event,
) -> Result<()> {
    let rx_irq = EventAsync::new(rx_irq, &ex).context("failed to create async event")?;
    let rxq = process_rxq(rx_irq, rx_queue, rx_sender, rx_notifier, rx_evt);
    pin_mut!(rxq);

    let tx_irq = EventAsync::new(tx_irq, &ex).context("failed to create async event")?;
    let txq = process_txq(tx_irq, Arc::clone(&tx_queue));
    pin_mut!(txq);

    let done = async {
        select! {
            res = rxq.fuse() => res.context("failed to handle rxq"),
            res = txq.fuse() => res.context("failed to handle txq"),
        }
    };

    match ex.run_until(done) {
        Ok(_) => Ok(()),
        Err(e) => {
            bail!("failed to process virtio-vhost-user queues: {}", e);
        }
    }
}

enum DeviceState {
    Initialized {
        // TODO(keiichiw): Update `VfioDeviceTrait::start()` to take `VvuPciDevice` so that we can
        // drop this field.
        device: VvuPciDevice,
    },
    Running {
        rxq_receiver: Receiver<Vec<u8>>,
        /// Store data that was provided by rxq_receiver but not consumed yet.
        rxq_buf: Vec<u8>,

        txq: Arc<Mutex<UserQueue>>,
        txq_notifier: Arc<Mutex<QueueNotifier>>,
    },
}

pub struct VvuDevice {
    state: DeviceState,
    rxq_evt: Event,
}

impl VvuDevice {
    pub fn new(device: VvuPciDevice) -> Self {
        Self {
            state: DeviceState::Initialized { device },
            rxq_evt: Event::new().expect("failed to create VvuDevice's rxq_evt"),
        }
    }
}

impl VfioDeviceTrait for VvuDevice {
    fn event(&self) -> &Event {
        &self.rxq_evt
    }

    fn start(&mut self) -> Result<()> {
        let device = match &mut self.state {
            DeviceState::Initialized { device } => device,
            DeviceState::Running { .. } => {
                bail!("VvuDevice has already started");
            }
        };
        let ex = Executor::new().expect("Failed to create an executor");

        let mut irqs = mem::take(&mut device.irqs);
        let mut queues = mem::take(&mut device.queues);
        let mut queue_notifiers = mem::take(&mut device.queue_notifiers);

        let rxq = queues.remove(0);
        let rxq_irq = irqs.remove(0);
        let rxq_notifier = queue_notifiers.remove(0);
        // TODO: Can we use async channel instead so we don't need `rxq_evt`?
        let (rxq_sender, rxq_receiver) = channel();
        let rxq_evt = self.rxq_evt.try_clone().expect("rxq_evt clone");

        let txq = Arc::new(Mutex::new(queues.remove(0)));
        let txq_cloned = Arc::clone(&txq);
        let txq_irq = irqs.remove(0);
        let txq_notifier = Arc::new(Mutex::new(queue_notifiers.remove(0)));

        let old_state = std::mem::replace(
            &mut self.state,
            DeviceState::Running {
                rxq_receiver,
                rxq_buf: vec![],
                txq,
                txq_notifier,
            },
        );

        let device = match old_state {
            DeviceState::Initialized { device } => device,
            _ => unreachable!(),
        };

        thread::Builder::new()
            .name("virtio-vhost-user driver".to_string())
            .spawn(move || {
                device.start().expect("failed to start device");
                if let Err(e) = run_worker(
                    ex,
                    rxq,
                    rxq_irq,
                    rxq_sender,
                    rxq_notifier,
                    rxq_evt,
                    txq_cloned,
                    txq_irq,
                ) {
                    error!("worker thread exited with error: {}", e);
                }
            })?;

        Ok(())
    }

    fn send_bufs(&mut self, iovs: &[IoSlice], fds: Option<&[RawFd]>) -> Result<usize> {
        if fds.is_some() {
            bail!("cannot send FDs");
        }

        let (txq, txq_notifier) = match &mut self.state {
            DeviceState::Initialized { .. } => {
                bail!("VvuDevice hasn't started yet");
            }
            DeviceState::Running {
                txq, txq_notifier, ..
            } => (txq, txq_notifier),
        };

        let size = iovs.iter().map(|v| v.len()).sum();
        let data: Vec<u8> = iovs.iter().flat_map(|v| v.to_vec()).collect();

        txq.lock().write(&data).context("Failed to send data")?;
        txq_notifier.lock().notify();

        Ok(size)
    }

    fn recv_into_bufs(&mut self, bufs: &mut [IoSliceMut]) -> Result<usize, RecvIntoBufsError> {
        let (rxq_receiver, rxq_buf) = match &mut self.state {
            DeviceState::Initialized { .. } => {
                return Err(RecvIntoBufsError::Fatal(anyhow!(
                    "VvuDevice hasn't started yet"
                )));
            }
            DeviceState::Running {
                rxq_receiver,
                rxq_buf,
                ..
            } => (rxq_receiver, rxq_buf),
        };

        let mut size = 0;
        for buf in bufs {
            let len = buf.len();

            while rxq_buf.len() < len {
                let mut data = rxq_receiver
                    .recv()
                    .context("failed to receive data")
                    .map_err(RecvIntoBufsError::Fatal)?;

                if data.len() == 0 {
                    // TODO(b/216407443): We should change `self.state` and exit gracefully.
                    info!("VVU connection is closed");
                    return Err(RecvIntoBufsError::Disconnect);
                }

                rxq_buf.append(&mut data);
                // Decrement the event counter as we received one buffer.
                self.rxq_evt
                    .read()
                    .and_then(|c| self.rxq_evt.write(c - 1))
                    .context("failed to decrease event counter")
                    .map_err(RecvIntoBufsError::Fatal)?;
            }

            buf.clone_from_slice(&rxq_buf[..len]);

            rxq_buf.drain(0..len);
            size += len;
        }

        Ok(size)
    }
}
<|endoftext|>


<commit_before>extern crate circular_buffer;

#[allow(unused_must_use)]
mod tests {

    use circular_buffer::{CircularBuffer, Error};

    #[test]
    fn error_on_read_empty_buffer() {
        let mut buffer = CircularBuffer::<char>::new(1);
        assert_eq!(Err(Error::EmptyBuffer), buffer.read());
    }

    #[test]
    #[ignore]
    fn write_and_read_back_item() {
        let mut buffer = CircularBuffer::new(1);
        buffer.write('1');
        assert_eq!(Ok('1'), buffer.read());
        assert_eq!(Err(Error::EmptyBuffer), buffer.read());
    }

    #[test]
    #[ignore]
    fn write_and_read_back_multiple_items() {
        let mut buffer = CircularBuffer::new(2);
        buffer.write('1');
        buffer.write('2');
        assert_eq!(Ok('1'), buffer.read());
        assert_eq!(Ok('2'), buffer.read());
        assert_eq!(Err(Error::EmptyBuffer), buffer.read());
    }

    #[test]
    #[ignore]
    fn alternate_write_and_read() {
        let mut buffer = CircularBuffer::new(2);
        buffer.write('1');
        assert_eq!(Ok('1'), buffer.read());
        buffer.write('2');
        assert_eq!(Ok('2'), buffer.read());
    }

    #[test]
    #[ignore]
    fn clear_buffer() {
        let mut buffer = CircularBuffer::new(3);
        buffer.write('1');
        buffer.write('2');
        buffer.write('3');
        buffer.clear();
        assert_eq!(Err(Error::EmptyBuffer), buffer.read());
        buffer.write('1');
        buffer.write('2');
        assert_eq!(Ok('1'), buffer.read());
        buffer.write('3');
        assert_eq!(Ok('2'), buffer.read());
    }

    #[test]
    #[ignore]
    fn full_buffer_error() {
        let mut buffer = CircularBuffer::new(2);
        buffer.write('1');
        buffer.write('2');
        assert_eq!(Err(Error::FullBuffer), buffer.write('3'));
    }

    #[test]
    #[ignore]
    fn overwrite_item_in_non_full_buffer() {
        let mut buffer = CircularBuffer::new(2);
        buffer.write('1');
        buffer.overwrite('2');
        assert_eq!(Ok('1'), buffer.read());
        assert_eq!(Ok('2'), buffer.read());
        assert_eq!(Err(Error::EmptyBuffer), buffer.read());
    }

    #[test]
    #[ignore]
    fn overwrite_item_in_full_buffer() {
        let mut buffer = CircularBuffer::new(2);
        buffer.write('1');
        buffer.write('2');
        buffer.overwrite('A');
        assert_eq!(Ok('2'), buffer.read());
        assert_eq!(Ok('A'), buffer.read());
    }

    #[test]
    #[ignore]
    fn integer_buffer() {
        let mut buffer = CircularBuffer::new(2);
        buffer.write(1);
        buffer.write(2);
        assert_eq!(Ok(1), buffer.read());
        buffer.write(-1);
        assert_eq!(Ok(2), buffer.read());
        assert_eq!(Ok(-1), buffer.read());
        assert_eq!(Err(Error::EmptyBuffer), buffer.read());
    }

    #[test]
    #[ignore]
    fn string_buffer() {
        let mut buffer = CircularBuffer::new(2);
        buffer.write("".to_string());
        buffer.write("Testing".to_string());
        assert_eq!(0, buffer.read().unwrap().len());
        assert_eq!(Ok("Testing".to_string()), buffer.read());
    }
}
<commit_msg>circular buffer: remove allow(unused_must_use) (#490)<commit_after>extern crate circular_buffer;
use circular_buffer::{CircularBuffer, Error};

#[test]
fn error_on_read_empty_buffer() {
    let mut buffer = CircularBuffer::<char>::new(1);
    assert_eq!(Err(Error::EmptyBuffer), buffer.read());
}

#[test]
#[ignore]
fn write_and_read_back_item() {
    let mut buffer = CircularBuffer::new(1);
    assert!(buffer.write('1').is_ok());
    assert_eq!(Ok('1'), buffer.read());
    assert_eq!(Err(Error::EmptyBuffer), buffer.read());
}

#[test]
#[ignore]
fn write_and_read_back_multiple_items() {
    let mut buffer = CircularBuffer::new(2);
    assert!(buffer.write('1').is_ok());
    assert!(buffer.write('2').is_ok());
    assert_eq!(Ok('1'), buffer.read());
    assert_eq!(Ok('2'), buffer.read());
    assert_eq!(Err(Error::EmptyBuffer), buffer.read());
}

#[test]
#[ignore]
fn alternate_write_and_read() {
    let mut buffer = CircularBuffer::new(2);
    assert!(buffer.write('1').is_ok());
    assert_eq!(Ok('1'), buffer.read());
    assert!(buffer.write('2').is_ok());
    assert_eq!(Ok('2'), buffer.read());
}

#[test]
#[ignore]
fn clear_buffer() {
    let mut buffer = CircularBuffer::new(3);
    assert!(buffer.write('1').is_ok());
    assert!(buffer.write('2').is_ok());
    assert!(buffer.write('3').is_ok());
    buffer.clear();
    assert_eq!(Err(Error::EmptyBuffer), buffer.read());
    assert!(buffer.write('1').is_ok());
    assert!(buffer.write('2').is_ok());
    assert_eq!(Ok('1'), buffer.read());
    assert!(buffer.write('3').is_ok());
    assert_eq!(Ok('2'), buffer.read());
}

#[test]
#[ignore]
fn full_buffer_error() {
    let mut buffer = CircularBuffer::new(2);
    assert!(buffer.write('1').is_ok());
    assert!(buffer.write('2').is_ok());
    assert_eq!(Err(Error::FullBuffer), buffer.write('3'));
}

#[test]
#[ignore]
fn overwrite_item_in_non_full_buffer() {
    let mut buffer = CircularBuffer::new(2);
    assert!(buffer.write('1').is_ok());
    buffer.overwrite('2');
    assert_eq!(Ok('1'), buffer.read());
    assert_eq!(Ok('2'), buffer.read());
    assert_eq!(Err(Error::EmptyBuffer), buffer.read());
}

#[test]
#[ignore]
fn overwrite_item_in_full_buffer() {
    let mut buffer = CircularBuffer::new(2);
    assert!(buffer.write('1').is_ok());
    assert!(buffer.write('2').is_ok());
    buffer.overwrite('A');
    assert_eq!(Ok('2'), buffer.read());
    assert_eq!(Ok('A'), buffer.read());
}

#[test]
#[ignore]
fn integer_buffer() {
    let mut buffer = CircularBuffer::new(2);
    assert!(buffer.write(1).is_ok());
    assert!(buffer.write(2).is_ok());
    assert_eq!(Ok(1), buffer.read());
    assert!(buffer.write(-1).is_ok());
    assert_eq!(Ok(2), buffer.read());
    assert_eq!(Ok(-1), buffer.read());
    assert_eq!(Err(Error::EmptyBuffer), buffer.read());
}

#[test]
#[ignore]
fn string_buffer() {
    let mut buffer = CircularBuffer::new(2);
    buffer.write("".to_string()).unwrap();
    buffer.write("Testing".to_string()).unwrap();
    assert_eq!(0, buffer.read().unwrap().len());
    assert_eq!(Ok("Testing".to_string()), buffer.read());
}
<|endoftext|>