<commit_before>/*
 * Copyright (C) 2015-2017 The imgui-rs Developers
 * Copyright (C) 2017-2019 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::time::{Duration, Instant};

use glium::glutin::{self, Api, GlRequest};
use glium::{Display, Surface};
use imgui::{FontGlyphRange, FrameSize, ImFontConfig, ImGui, Ui};
use imgui_glium_renderer::Renderer;

use crate::model::Model;
use crate::update;

const FRAME_DURATION: Duration = Duration::from_millis(16);

#[derive(Copy, Clone, PartialEq, Debug, Default)]
struct MouseState {
    pos: (i32, i32),
    pressed: (bool, bool, bool),
    wheel: f32,
}

pub fn run<F: FnMut(&mut Model, &Ui, (f32, f32)) -> bool>(
    title: String,
    dimensions: (u32, u32),
    clear_color: [f32; 4],
    mut events_loop: glutin::EventsLoop,
    mut model: Model,
    mut run_ui: F,
) {
    let window = glutin::WindowBuilder::new()
        .with_title(title)
        .with_dimensions(dimensions.into());
    let mut context = glutin::ContextBuilder::new().with_vsync(true);
    if cfg!(target_os = "android") {
        // https://github.com/tomaka/android-rs-glue/issues/153#issuecomment-318348732
        // On Android we must specify an OpenGL ES version or glutin will assume we are using an
        // unsupported version and panic
        context = context.with_gl(GlRequest::Specific(Api::OpenGlEs, (2, 0)));
    }

    let display = Display::new(window, context, &events_loop).unwrap();
    let gl_window = display.gl_window();

    let mut imgui = ImGui::init();
    imgui.set_ini_filename(None);

    let config = ImFontConfig::new()
        .oversample_h(4)
        .oversample_v(4)
        .size_pixels(21.0)
        .rasterizer_multiply(1.05);

    config.add_font(
        &mut imgui.fonts(),
        include_bytes!("../../assets/FiraSans-Regular.ttf"),
        &FontGlyphRange::default(),
    );

    let mut renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer");

    let mut last_frame = Instant::now();
    let mut mouse_state = MouseState::default();

    let mut render = |model: &mut Model, imgui: &mut ImGui, last_frame: &mut Instant| {
        let now = Instant::now();
        let delta = now - *last_frame;
        let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0;
        *last_frame = now;

        let frame_size = FrameSize {
            logical_size: gl_window.get_inner_size().unwrap().into(),
            hidpi_factor: gl_window.get_hidpi_factor(),
        };
        let size_points = (
            (frame_size.logical_size.0 / frame_size.hidpi_factor) as f32,
            (frame_size.logical_size.1 / frame_size.hidpi_factor) as f32,
        );

        let ui = imgui.frame(frame_size, delta_s);
        if !run_ui(model, &ui, size_points) {
            return false;
        }

        let mut target = display.draw();
        target.clear_color(
            clear_color[0],
            clear_color[1],
            clear_color[2],
            clear_color[3],
        );
        renderer.render(&mut target, ui).expect("Rendering failed");
        target.finish().unwrap();
        true
    };

    // Render one frame before the event loop so the screen isn't empty
    render(&mut model, &mut imgui, &mut last_frame);

    events_loop.run_forever(|event| {
        use glium::glutin::ElementState::Pressed;
        use glium::glutin::WindowEvent::*;
        use glium::glutin::{
            ControlFlow, Event, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode,
        };

        if let Event::Awakened = event {
            if Instant::now() - last_frame < FRAME_DURATION {
                // Receive the AI move, and queue the next one (if it's a computer-only game)
                update::update(&mut model, None);
                update::update(&mut model, None);

                // If the AI is moving very quickly, then the last move of the game will be
                // throttled and not receive a render. This appears to "freeze" the game. So, we
                // render if the game is finished.
                if model.is_game_over() && !render(&mut model, &mut imgui, &mut last_frame) {
                    return ControlFlow::Break;
                }
            } else {
                // Receive the AI move, then render
                update::update(&mut model, None);
                if !render(&mut model, &mut imgui, &mut last_frame) {
                    return ControlFlow::Break;
                }
            }
        } else if let Event::Suspended(true) = event {
            // This is so that the AI doesn't run in the background on Android. Technically, we
            // should also call update or render on Suspended(false) to restart the AI, but there's
            // no point since the app crashes when it's resumed or even right after it's suspended.
            model.ai.stop();
        } else if let Event::WindowEvent { event, .. } = event {
            match event {
                CloseRequested => return ControlFlow::Break,
                KeyboardInput { input, .. } => {
                    if let Some(VirtualKeyCode::Q) = input.virtual_keycode {
                        if cfg!(target_os = "macos") && input.modifiers.logo {
                            return ControlFlow::Break;
                        }
                    }
                }
                Refresh | Resized(_) | HiDpiFactorChanged(_) => {
                    if !render(&mut model, &mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                CursorMoved { position, .. } => {
                    mouse_state.pos = position.into();
                    update_mouse(&mut imgui, &mut mouse_state);

                    if Instant::now() - last_frame < FRAME_DURATION {
                        return ControlFlow::Continue;
                    } else if !render(&mut model, &mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                MouseWheel {
                    delta,
                    phase: TouchPhase::Moved,
                    ..
                } => {
                    mouse_state.wheel = match delta {
                        MouseScrollDelta::LineDelta(_, y) => y,
                        MouseScrollDelta::PixelDelta(position) => position.y as f32,
                    };
                    update_mouse(&mut imgui, &mut mouse_state);

                    if !render(&mut model, &mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                MouseInput { state, button, .. } => {
                    if MouseButton::Left == button {
                        mouse_state.pressed.0 = state == Pressed;
                        update_mouse(&mut imgui, &mut mouse_state);

                        // Render twice to immediately show the results of the click
                        if !render(&mut model, &mut imgui, &mut last_frame) {
                            return ControlFlow::Break;
                        }
                        if !render(&mut model, &mut imgui, &mut last_frame) {
                            return ControlFlow::Break;
                        }
                    }
                }
                Touch(glutin::Touch {
                    phase, location, ..
                }) => {
                    mouse_state.pos = location.into();
                    mouse_state.pressed.0 =
                        phase == TouchPhase::Started || phase == TouchPhase::Moved;
                    update_mouse(&mut imgui, &mut mouse_state);

                    match phase {
                        TouchPhase::Moved => {
                            if Instant::now() - last_frame < FRAME_DURATION {
                                return ControlFlow::Continue;
                            } else if !render(&mut model, &mut imgui, &mut last_frame) {
                                return ControlFlow::Break;
                            }
                        }
                        _ => {
                            // Render twice to immediately show the results of the touch
                            if !render(&mut model, &mut imgui, &mut last_frame) {
                                return ControlFlow::Break;
                            }
                            if !render(&mut model, &mut imgui, &mut last_frame) {
                                return ControlFlow::Break;
                            }
                        }
                    }
                }
                _ => (),
            }
        }
        ControlFlow::Continue
    });
}

fn update_mouse(imgui: &mut ImGui, mouse_state: &mut MouseState) {
    let scale = imgui.display_framebuffer_scale();
    imgui.set_mouse_pos(
        mouse_state.pos.0 as f32 / scale.0,
        mouse_state.pos.1 as f32 / scale.1,
    );
    imgui.set_mouse_down([
        mouse_state.pressed.0,
        mouse_state.pressed.1,
        mouse_state.pressed.2,
        false,
        false,
    ]);
    imgui.set_mouse_wheel(mouse_state.wheel / scale.1);
    mouse_state.wheel = 0.0;
}
<commit_msg>Use classic imgui colors<commit_after>/*
 * Copyright (C) 2015-2017 The imgui-rs Developers
 * Copyright (C) 2017-2019 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::time::{Duration, Instant};

use glium::glutin::{self, Api, GlRequest};
use glium::{Display, Surface};
use imgui::{FontGlyphRange, FrameSize, ImFontConfig, ImGui, Ui};
use imgui_glium_renderer::Renderer;

use crate::model::Model;
use crate::update;

const FRAME_DURATION: Duration = Duration::from_millis(16);

#[derive(Copy, Clone, PartialEq, Debug, Default)]
struct MouseState {
    pos: (i32, i32),
    pressed: (bool, bool, bool),
    wheel: f32,
}

pub fn run<F: FnMut(&mut Model, &Ui, (f32, f32)) -> bool>(
    title: String,
    dimensions: (u32, u32),
    clear_color: [f32; 4],
    mut events_loop: glutin::EventsLoop,
    mut model: Model,
    mut run_ui: F,
) {
    let window = glutin::WindowBuilder::new()
        .with_title(title)
        .with_dimensions(dimensions.into());
    let mut context = glutin::ContextBuilder::new().with_vsync(true);
    if cfg!(target_os = "android") {
        // https://github.com/tomaka/android-rs-glue/issues/153#issuecomment-318348732
        // On Android we must specify an OpenGL ES version or glutin will assume we are using an
        // unsupported version and panic
        context = context.with_gl(GlRequest::Specific(Api::OpenGlEs, (2, 0)));
    }

    let display = Display::new(window, context, &events_loop).unwrap();
    let gl_window = display.gl_window();

    let mut imgui = ImGui::init();
    unsafe {
        imgui::sys::igStyleColorsClassic(imgui.style_mut());
    }
    imgui.set_ini_filename(None);

    let config = ImFontConfig::new()
        .oversample_h(4)
        .oversample_v(4)
        .size_pixels(21.0)
        .rasterizer_multiply(1.05);

    config.add_font(
        &mut imgui.fonts(),
        include_bytes!("../../assets/FiraSans-Regular.ttf"),
        &FontGlyphRange::default(),
    );

    let mut renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer");

    let mut last_frame = Instant::now();
    let mut mouse_state = MouseState::default();

    let mut render = |model: &mut Model, imgui: &mut ImGui, last_frame: &mut Instant| {
        let now = Instant::now();
        let delta = now - *last_frame;
        let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0;
        *last_frame = now;

        let frame_size = FrameSize {
            logical_size: gl_window.get_inner_size().unwrap().into(),
            hidpi_factor: gl_window.get_hidpi_factor(),
        };
        let size_points = (
            (frame_size.logical_size.0 / frame_size.hidpi_factor) as f32,
            (frame_size.logical_size.1 / frame_size.hidpi_factor) as f32,
        );

        let ui = imgui.frame(frame_size, delta_s);
        if !run_ui(model, &ui, size_points) {
            return false;
        }

        let mut target = display.draw();
        target.clear_color(
            clear_color[0],
            clear_color[1],
            clear_color[2],
            clear_color[3],
        );
        renderer.render(&mut target, ui).expect("Rendering failed");
        target.finish().unwrap();
        true
    };

    // Render one frame before the event loop so the screen isn't empty
    render(&mut model, &mut imgui, &mut last_frame);

    events_loop.run_forever(|event| {
        use glium::glutin::ElementState::Pressed;
        use glium::glutin::WindowEvent::*;
        use glium::glutin::{
            ControlFlow, Event, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode,
        };

        if let Event::Awakened = event {
            if Instant::now() - last_frame < FRAME_DURATION {
                // Receive the AI move, and queue the next one (if it's a computer-only game)
                update::update(&mut model, None);
                update::update(&mut model, None);

                // If the AI is moving very quickly, then the last move of the game will be
                // throttled and not receive a render. This appears to "freeze" the game. So, we
                // render if the game is finished.
                if model.is_game_over() && !render(&mut model, &mut imgui, &mut last_frame) {
                    return ControlFlow::Break;
                }
            } else {
                // Receive the AI move, then render
                update::update(&mut model, None);
                if !render(&mut model, &mut imgui, &mut last_frame) {
                    return ControlFlow::Break;
                }
            }
        } else if let Event::Suspended(true) = event {
            // This is so that the AI doesn't run in the background on Android. Technically, we
            // should also call update or render on Suspended(false) to restart the AI, but there's
            // no point since the app crashes when it's resumed or even right after it's suspended.
            model.ai.stop();
        } else if let Event::WindowEvent { event, .. } = event {
            match event {
                CloseRequested => return ControlFlow::Break,
                KeyboardInput { input, .. } => {
                    if let Some(VirtualKeyCode::Q) = input.virtual_keycode {
                        if cfg!(target_os = "macos") && input.modifiers.logo {
                            return ControlFlow::Break;
                        }
                    }
                }
                Refresh | Resized(_) | HiDpiFactorChanged(_) => {
                    if !render(&mut model, &mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                CursorMoved { position, .. } => {
                    mouse_state.pos = position.into();
                    update_mouse(&mut imgui, &mut mouse_state);

                    if Instant::now() - last_frame < FRAME_DURATION {
                        return ControlFlow::Continue;
                    } else if !render(&mut model, &mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                MouseWheel {
                    delta,
                    phase: TouchPhase::Moved,
                    ..
                } => {
                    mouse_state.wheel = match delta {
                        MouseScrollDelta::LineDelta(_, y) => y,
                        MouseScrollDelta::PixelDelta(position) => position.y as f32,
                    };
                    update_mouse(&mut imgui, &mut mouse_state);

                    if !render(&mut model, &mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                MouseInput { state, button, .. } => {
                    if MouseButton::Left == button {
                        mouse_state.pressed.0 = state == Pressed;
                        update_mouse(&mut imgui, &mut mouse_state);

                        // Render twice to immediately show the results of the click
                        if !render(&mut model, &mut imgui, &mut last_frame) {
                            return ControlFlow::Break;
                        }
                        if !render(&mut model, &mut imgui, &mut last_frame) {
                            return ControlFlow::Break;
                        }
                    }
                }
                Touch(glutin::Touch {
                    phase, location, ..
                }) => {
                    mouse_state.pos = location.into();
                    mouse_state.pressed.0 =
                        phase == TouchPhase::Started || phase == TouchPhase::Moved;
                    update_mouse(&mut imgui, &mut mouse_state);

                    match phase {
                        TouchPhase::Moved => {
                            if Instant::now() - last_frame < FRAME_DURATION {
                                return ControlFlow::Continue;
                            } else if !render(&mut model, &mut imgui, &mut last_frame) {
                                return ControlFlow::Break;
                            }
                        }
                        _ => {
                            // Render twice to immediately show the results of the touch
                            if !render(&mut model, &mut imgui, &mut last_frame) {
                                return ControlFlow::Break;
                            }
                            if !render(&mut model, &mut imgui, &mut last_frame) {
                                return ControlFlow::Break;
                            }
                        }
                    }
                }
                _ => (),
            }
        }
        ControlFlow::Continue
    });
}

fn update_mouse(imgui: &mut ImGui, mouse_state: &mut MouseState) {
    let scale = imgui.display_framebuffer_scale();
    imgui.set_mouse_pos(
        mouse_state.pos.0 as f32 / scale.0,
        mouse_state.pos.1 as f32 / scale.1,
    );
    imgui.set_mouse_down([
        mouse_state.pressed.0,
        mouse_state.pressed.1,
        mouse_state.pressed.2,
        false,
        false,
    ]);
    imgui.set_mouse_wheel(mouse_state.wheel / scale.1);
    mouse_state.wheel = 0.0;
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Vigen%C3%A8re_cipher
#![feature(convert)]

use std::ascii::AsciiExt;

const ASCII_A: u8 = 'A' as u8;

#[cfg(not(test))]
fn main() {
    let msg = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!";
    let key = "VIGENERECIPHER";

    let enc = vigenere(msg, key, true);
    let dec = vigenere(enc.as_str(), key, false);

    println!("msg: {}", msg);
    println!("key: {}", key);
    println!("enc: {}", enc);
    println!("dec: {}", dec);
}

fn vigenere(plaintext: &str, key: &str, encrypt: bool) -> String {
    let plaintext_bytes = to_sanitized_bytes(plaintext);
    let key_bytes = to_sanitized_bytes(key);
    let key_len = key_bytes.len();
    let mut output = String::with_capacity(plaintext_bytes.len());

    for i in 0..plaintext_bytes.len() {
        let c = plaintext_bytes[i];
        let b = key_bytes[i % key_len];

        let output_byte = if encrypt {
            enc_byte(c, b)
        } else {
            dec_byte(c, b)
        };

        output.push(output_byte as char);
    }
    output
}

fn to_sanitized_bytes(string: &str) -> Vec<u8> {
    string.chars()
        // keep only alphabethical characters...
        .filter(|&c| c.is_alphabetic())
        // ... and convert them to upper case
        .map(|c| c.to_ascii_uppercase() as u8 )
        .collect::<Vec<u8>>()
}

fn enc_byte(m: u8, k: u8) -> u8 {
    ASCII_A + (m.wrapping_add(k).wrapping_sub(2 * (ASCII_A))) % 26
}

fn dec_byte(c: u8, k: u8) -> u8 {
    ASCII_A + (c.wrapping_sub(k).wrapping_add(26)) % 26
}

#[test]
fn test_enc_dec() {
    let plaintext = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!";
    let key = "VIGENERECIPHER";

    let enc = vigenere(plaintext, key, true);
    assert_eq!("WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY", enc);
    let dec = vigenere(enc.as_str(), key, false);
    assert_eq!("BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH", dec);
}

#[test]
fn test_equal_len_key_and_plaintext() {
    let plaintext = "VIGENERECIPHER";
    let key = "REHPICERENEGIV";
    // to be sure nobody breaks this test
    assert_eq!(plaintext.len(), key.len());

    let enc = vigenere(plaintext, key, true);
    assert_eq!("MMNTVGVVGVTNMM", enc);
    let dec = vigenere(enc.as_str(), key, false);
    assert_eq!(plaintext, dec);
}

#[test]
fn test_empty_string_enc_dec() {
    let enc = vigenere("", "", true);
    assert_eq!("", enc);
    let dec = vigenere("", "", false);
    assert_eq!("", dec);
}

<commit_msg>Remove #![feature(convert)]<commit_after>// Implements http://rosettacode.org/wiki/Vigen%C3%A8re_cipher
use std::ascii::AsciiExt;

const ASCII_A: u8 = 'A' as u8;

#[cfg(not(test))]
fn main() {
    let msg = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!";
    let key = "VIGENERECIPHER";

    let enc = vigenere(msg, key, true);
    let dec = vigenere(&enc, key, false);

    println!("msg: {}", msg);
    println!("key: {}", key);
    println!("enc: {}", enc);
    println!("dec: {}", dec);
}

fn vigenere(plaintext: &str, key: &str, encrypt: bool) -> String {
    let plaintext_bytes = to_sanitized_bytes(plaintext);
    let key_bytes = to_sanitized_bytes(key);
    let key_len = key_bytes.len();
    let mut output = String::with_capacity(plaintext_bytes.len());

    for i in 0..plaintext_bytes.len() {
        let c = plaintext_bytes[i];
        let b = key_bytes[i % key_len];

        let output_byte = if encrypt {
            enc_byte(c, b)
        } else {
            dec_byte(c, b)
        };

        output.push(output_byte as char);
    }
    output
}

fn to_sanitized_bytes(string: &str) -> Vec<u8> {
    string.chars()
        // keep only alphabethical characters...
        .filter(|&c| c.is_alphabetic())
        // ... and convert them to upper case
        .map(|c| c.to_ascii_uppercase() as u8 )
        .collect::<Vec<u8>>()
}

fn enc_byte(m: u8, k: u8) -> u8 {
    ASCII_A + (m.wrapping_add(k).wrapping_sub(2 * (ASCII_A))) % 26
}

fn dec_byte(c: u8, k: u8) -> u8 {
    ASCII_A + (c.wrapping_sub(k).wrapping_add(26)) % 26
}

#[test]
fn test_enc_dec() {
    let plaintext = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!";
    let key = "VIGENERECIPHER";

    let enc = vigenere(plaintext, key, true);
    assert_eq!("WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY", enc);
    let dec = vigenere(&enc, key, false);
    assert_eq!("BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH", dec);
}

#[test]
fn test_equal_len_key_and_plaintext() {
    let plaintext = "VIGENERECIPHER";
    let key = "REHPICERENEGIV";
    // to be sure nobody breaks this test
    assert_eq!(plaintext.len(), key.len());

    let enc = vigenere(plaintext, key, true);
    assert_eq!("MMNTVGVVGVTNMM", enc);
    let dec = vigenere(&enc, key, false);
    assert_eq!(plaintext, dec);
}

#[test]
fn test_empty_string_enc_dec() {
    let enc = vigenere("", "", true);
    assert_eq!("", enc);
    let dec = vigenere("", "", false);
    assert_eq!("", dec);
}

<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

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

lazy_static! {
    pub static ref COPR_REQ_HISTOGRAM_VEC: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_request_duration_seconds",
            "Bucketed histogram of coprocessor request duration",
            &["req"]
        ).unwrap();

    pub static ref OUTDATED_REQ_WAIT_TIME: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_outdated_request_wait_seconds",
            "Bucketed histogram of outdated coprocessor request wait duration",
            &["req"]
        ).unwrap();

    pub static ref COPR_REQ_HANDLE_TIME: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_request_handle_seconds",
            "Bucketed histogram of coprocessor handle request duration",
            &["req"]
        ).unwrap();

    pub static ref COPR_REQ_WAIT_TIME: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_request_wait_seconds",
            "Bucketed histogram of coprocessor request wait duration",
            &["req"]
        ).unwrap();

    pub static ref COPR_REQ_ERROR: CounterVec =
        register_counter_vec!(
            "tikv_coprocessor_request_error",
            "Total number of push down request error.",
            &["reason"]
        ).unwrap();

    pub static ref COPR_PENDING_REQS: GaugeVec =
        register_gauge_vec!(
            "tikv_coprocessor_pending_request",
            "Total number of pending push down request.",
            &["req"]
        ).unwrap();

    pub static ref COPR_SCAN_KEYS: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_scan_keys",
            "Bucketed histogram of coprocessor per request scan keys",
            &["req"],
            exponential_buckets(1.0, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref COPR_SCAN_DETAILS: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_scan_details",
            "Bucketed histogram of coprocessor scan details for each CF",
            &["req", "cf", "tag"],
             exponential_buckets(1.0, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref COPR_EXECUTOR_COUNT: CounterVec =
        register_counter_vec!(
            "tikv_coprocessor_executor_count",
            "Total number of each executor",
            &["type"]
        ).unwrap();

    pub static ref CORP_GET_OR_SCAN_COUNT: CounterVec =
        register_counter_vec!(
            "tikv_coprocessor_get_or_scan_count",
            "Total number of rocksdb query of get or scan count",
            &["type"]
        ).unwrap();
}
<commit_msg>coprocessor/metircs:init exponential buckets for duration (#2087)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

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

lazy_static! {
    pub static ref COPR_REQ_HISTOGRAM_VEC: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_request_duration_seconds",
            "Bucketed histogram of coprocessor request duration",
            &["req"],
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref OUTDATED_REQ_WAIT_TIME: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_outdated_request_wait_seconds",
            "Bucketed histogram of outdated coprocessor request wait duration",
            &["req"],
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref COPR_REQ_HANDLE_TIME: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_request_handle_seconds",
            "Bucketed histogram of coprocessor handle request duration",
            &["req"],
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref COPR_REQ_WAIT_TIME: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_request_wait_seconds",
            "Bucketed histogram of coprocessor request wait duration",
            &["req"],
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref COPR_REQ_ERROR: CounterVec =
        register_counter_vec!(
            "tikv_coprocessor_request_error",
            "Total number of push down request error.",
            &["reason"]
        ).unwrap();

    pub static ref COPR_PENDING_REQS: GaugeVec =
        register_gauge_vec!(
            "tikv_coprocessor_pending_request",
            "Total number of pending push down request.",
            &["req"]
        ).unwrap();

    pub static ref COPR_SCAN_KEYS: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_scan_keys",
            "Bucketed histogram of coprocessor per request scan keys",
            &["req"],
            exponential_buckets(1.0, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref COPR_SCAN_DETAILS: HistogramVec =
        register_histogram_vec!(
            "tikv_coprocessor_scan_details",
            "Bucketed histogram of coprocessor scan details for each CF",
            &["req", "cf", "tag"],
             exponential_buckets(1.0, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref COPR_EXECUTOR_COUNT: CounterVec =
        register_counter_vec!(
            "tikv_coprocessor_executor_count",
            "Total number of each executor",
            &["type"]
        ).unwrap();

    pub static ref CORP_GET_OR_SCAN_COUNT: CounterVec =
        register_counter_vec!(
            "tikv_coprocessor_get_or_scan_count",
            "Total number of rocksdb query of get or scan count",
            &["type"]
        ).unwrap();
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Examples of module privacy and errors<commit_after>mod outermost {
    pub fn middle_function() {}

    fn middle_secret_function() {}

    pub mod inside {
        pub fn inner_function() {}

        fn secret_function() {}
    }
}

fn try_me() {
    outermost::middle_function();
    outermost::middle_secret_function();
    outermost::inside::inner_function();
    outermost::inside::secret_function();
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


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

#![deny(missing_docs)]

//! Graphics device. Not meant for direct use.

use std::{fmt, mem};
use std::hash::Hash;

pub use draw_state::target;
pub use draw_state::state;

pub mod attrib;
pub mod command;
pub mod draw;
pub mod dummy;
pub mod handle;
pub mod mapping;
pub mod shade;
pub mod tex;

mod arc;

/// Draw vertex count.
pub type VertexCount = u32;
/// Draw number of instances
pub type InstanceCount = u32;
/// Index of a uniform block.
pub type UniformBlockIndex = u8;
/// Slot for an attribute.
pub type AttributeSlot = u8;
/// Slot for a uniform buffer object.
pub type UniformBufferSlot = u8;
/// Slot a texture can be bound to.
pub type TextureSlot = u8;

/// Generic error for features that are not supported
/// by the device capabilities.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct NotSupported;

/// Treat a given slice as `&[u8]` for the given function call
pub fn as_byte_slice<T>(slice: &[T]) -> &[u8] {
    use std::slice;
    let len = mem::size_of::<T>() * slice.len();
    unsafe {
        slice::from_raw_parts(slice.as_ptr() as *const u8, len)
    }
}

/// Features that the device supports.
#[derive(Copy, Clone, Debug)]
#[allow(missing_docs)] // pretty self-explanatory fields!
pub struct Capabilities {
    pub shader_model: shade::ShaderModel,

    pub max_vertex_count: usize,
    pub max_index_count: usize,
    pub max_draw_buffers: usize,
    pub max_texture_size: usize,
    pub max_vertex_attributes: usize,

    /// In GLES it is not allowed to re-bind a buffer to a different
    /// target than the one it was initialized with.
    pub buffer_role_change_allowed: bool,

    pub array_buffer_supported: bool,
    pub fragment_output_supported: bool,
    pub immutable_storage_supported: bool,
    pub instance_base_supported: bool,
    pub instance_call_supported: bool,
    pub instance_rate_supported: bool,
    pub render_targets_supported: bool,
    pub sampler_objects_supported: bool,
    pub srgb_color_supported: bool,
    pub uniform_block_supported: bool,
    pub vertex_base_supported: bool,
}

/// Specifies the access allowed to a buffer mapping.
#[derive(Copy, Clone)]
pub enum MapAccess {
    /// Only allow reads.
    Readable,
    /// Only allow writes.
    Writable,
    /// Allow full access.
    RW
}

/// Describes what geometric primitives are created from vertex data.
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum PrimitiveType {
    /// Each vertex represents a single point.
    Point,
    /// Each pair of vertices represent a single line segment. For example, with `[a, b, c, d,
    /// e]`, `a` and `b` form a line, `c` and `d` form a line, and `e` is discarded.
    Line,
    /// Every two consecutive vertices represent a single line segment. Visually forms a "path" of
    /// lines, as they are all connected. For example, with `[a, b, c]`, `a` and `b` form a line
    /// line, and `b` and `c` form a line.
    LineStrip,
    /// Each triplet of vertices represent a single triangle. For example, with `[a, b, c, d, e]`,
    /// `a`, `b`, and `c` form a triangle, `d` and `e` are discarded.
    TriangleList,
    /// Every three consecutive vertices represent a single triangle. For example, with `[a, b, c,
    /// d]`, `a`, `b`, and `c` form a triangle, and `b`, `c`, and `d` form a triangle.
    TriangleStrip,
    /// The first vertex with the last two are forming a triangle. For example, with `[a, b, c, d
    /// ]`, `a` , `b`, and `c` form a triangle, and `a`, `c`, and `d` form a triangle.
    TriangleFan,
    //Quad,
}

/// A type of each index value in the mesh's index buffer
pub type IndexType = attrib::IntSize;

/// Role of the memory buffer. GLES doesn't chaning bind points for buffers.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum BufferRole {
    /// Generic vertex buffer
    Vertex,
    /// Index buffer
    Index,
    /// Uniform block buffer
    Uniform,
}

/// A hint as to how this buffer will be used.
///
/// The nature of these hints make them very implementation specific. Different drivers on
/// different hardware will handle them differently. Only careful profiling will tell which is the
/// best to use for a specific buffer.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum BufferUsage {
    /// Once uploaded, this buffer will rarely change, but will be read from often.
    Static,
    /// This buffer will be updated "frequently", and will be read from multiple times between
    /// updates.
    Dynamic,
    /// This buffer always or almost always be updated after each read.
    Stream,
}

/// An information block that is immutable and associated with each buffer
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct BufferInfo {
    /// Role
    pub role: BufferRole,
    /// Usage hint
    pub usage: BufferUsage,
    /// Size in bytes
    pub size: usize,
}

/// An error happening on buffer updates.
#[derive(Clone, PartialEq, Debug)]
pub enum BufferUpdateError {
    /// Trying to change the contents outside of the allocation.
    OutOfBounds,
}

/// Resources pertaining to a specific API.
#[allow(missing_docs)]
pub trait Resources:           Clone + Hash + fmt::Debug + Eq + PartialEq {
    type Buffer:        Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync;
    type ArrayBuffer:   Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync;
    type Shader:        Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync;
    type Program:       Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync;
    type FrameBuffer:   Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync;
    type Surface:       Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync;
    type Texture:       Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync;
    type Sampler:       Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync;
    type Fence:         Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync;
}

#[allow(missing_docs)]
pub trait Factory<R: Resources> {
    /// Associated mapper type
    type Mapper: Clone + mapping::Raw;

    /// Returns the capabilities available to the specific API implementation
    fn get_capabilities<'a>(&'a self) -> &'a Capabilities;

    // resource creation
    fn create_buffer_raw(&mut self, size: usize, BufferRole, BufferUsage) -> handle::RawBuffer<R>;
    fn create_buffer_static_raw(&mut self, data: &[u8], BufferRole) -> handle::RawBuffer<R>;
    fn create_buffer_static<T>(&mut self, data: &[T], role: BufferRole) -> handle::Buffer<R, T> {
        handle::Buffer::from_raw(
            self.create_buffer_static_raw(as_byte_slice(data), role))
    }
    fn create_buffer_dynamic<T>(&mut self, num: usize, role: BufferRole) -> handle::Buffer<R, T> {
        handle::Buffer::from_raw(
            self.create_buffer_raw(num * mem::size_of::<T>(), role, BufferUsage::Stream))
    }

    fn create_array_buffer(&mut self) -> Result<handle::ArrayBuffer<R>, NotSupported>;
    fn create_shader(&mut self, stage: shade::Stage, code: &[u8]) ->
                     Result<handle::Shader<R>, shade::CreateShaderError>;
    fn create_program(&mut self, shaders: &[handle::Shader<R>], targets: Option<&[&str]>)
                      -> Result<handle::Program<R>, shade::CreateProgramError>;
    fn create_frame_buffer(&mut self) -> Result<handle::FrameBuffer<R>, NotSupported>;
    fn create_surface(&mut self, tex::SurfaceInfo) -> Result<handle::Surface<R>, tex::SurfaceError>;
    fn create_texture(&mut self, tex::TextureInfo) -> Result<handle::Texture<R>, tex::TextureError>;
    fn create_sampler(&mut self, tex::SamplerInfo) -> handle::Sampler<R>;

    /// Update the information stored in a specific buffer
    fn update_buffer_raw(&mut self, buf: &handle::RawBuffer<R>, data: &[u8], offset_bytes: usize)
                         -> Result<(), BufferUpdateError>;
    fn update_buffer<T>(&mut self, buf: &handle::Buffer<R, T>, data: &[T], offset_elements: usize)
                        -> Result<(), BufferUpdateError> {
        self.update_buffer_raw(buf.raw(), as_byte_slice(data), mem::size_of::<T>() * offset_elements)
    }
    fn map_buffer_raw(&mut self, &handle::RawBuffer<R>, MapAccess) -> Self::Mapper;
    fn unmap_buffer_raw(&mut self, Self::Mapper);
    fn map_buffer_readable<T: Copy>(&mut self, &handle::Buffer<R, T>) -> mapping::Readable<T, R, Self>;
    fn map_buffer_writable<T: Copy>(&mut self, &handle::Buffer<R, T>) -> mapping::Writable<T, R, Self>;
    fn map_buffer_rw<T: Copy>(&mut self, &handle::Buffer<R, T>) -> mapping::RW<T, R, Self>;

    /// Update the information stored in a texture
    fn update_texture_raw(&mut self, tex: &handle::Texture<R>,
                          img: &tex::ImageInfo, data: &[u8],
                          kind: Option<tex::Kind>) -> Result<(), tex::TextureError>;

    fn update_texture<T>(&mut self, tex: &handle::Texture<R>,
                         img: &tex::ImageInfo, data: &[T],
                         kind: Option<tex::Kind>) -> Result<(), tex::TextureError> {
        self.update_texture_raw(tex, img, as_byte_slice(data), kind)
    }

    fn generate_mipmap(&mut self, &handle::Texture<R>);

    /// Create a new texture with given data
    fn create_texture_static<T>(&mut self, info: tex::TextureInfo, data: &[T])
                             -> Result<handle::Texture<R>, tex::TextureError> {
        let image_info = info.into();
        match self.create_texture(info) {
            Ok(handle) => self.update_texture(&handle, &image_info, data, None)
                              .map(|_| handle),
            Err(e) => Err(e),
        }
    }
}

/// All the data needed simultaneously for submitting a command buffer for
/// execution on a device.
pub type SubmitInfo<'a, D: Device> = (
    &'a D::CommandBuffer,
    &'a draw::DataBuffer,
    &'a handle::Manager<D::Resources>
);

/// An interface for performing draw calls using a specific graphics API
pub trait Device {
    /// Associated resources type.
    type Resources: Resources;
    /// Associated command buffer type.
    type CommandBuffer: draw::CommandBuffer<Self::Resources>;

    /// Returns the capabilities available to the specific API implementation.
    fn get_capabilities<'a>(&'a self) -> &'a Capabilities;

    /// Reset all the states to disabled/default.
    fn reset_state(&mut self);

    /// Submit a command buffer for execution.
    fn submit(&mut self, SubmitInfo<Self>);

    /// Cleanup unused resources, to be called between frames.
    fn cleanup(&mut self);
}

/// Extension to the Device that allows for submitting of commands
/// around a fence
pub trait DeviceFence<R: Resources>: Device<Resources=R> {
    /// Submit a command buffer to the stream creating a fence
    /// the fence is signaled after the GPU has executed all commands
    /// in the buffer
    fn fenced_submit(&mut self, SubmitInfo<Self>, after: Option<handle::Fence<R>>) -> handle::Fence<R>;

    /// Wait on the supplied fence stalling the current thread until
    /// the fence is satisfied
    fn fence_wait(&mut self, fence: &handle::Fence<R>);
}
<commit_msg>Added 'static lifetime to Resources<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![deny(missing_docs)]

//! Graphics device. Not meant for direct use.

use std::{fmt, mem};
use std::hash::Hash;

pub use draw_state::target;
pub use draw_state::state;

pub mod attrib;
pub mod command;
pub mod draw;
pub mod dummy;
pub mod handle;
pub mod mapping;
pub mod shade;
pub mod tex;

mod arc;

/// Draw vertex count.
pub type VertexCount = u32;
/// Draw number of instances
pub type InstanceCount = u32;
/// Index of a uniform block.
pub type UniformBlockIndex = u8;
/// Slot for an attribute.
pub type AttributeSlot = u8;
/// Slot for a uniform buffer object.
pub type UniformBufferSlot = u8;
/// Slot a texture can be bound to.
pub type TextureSlot = u8;

/// Generic error for features that are not supported
/// by the device capabilities.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct NotSupported;

/// Treat a given slice as `&[u8]` for the given function call
pub fn as_byte_slice<T>(slice: &[T]) -> &[u8] {
    use std::slice;
    let len = mem::size_of::<T>() * slice.len();
    unsafe {
        slice::from_raw_parts(slice.as_ptr() as *const u8, len)
    }
}

/// Features that the device supports.
#[derive(Copy, Clone, Debug)]
#[allow(missing_docs)] // pretty self-explanatory fields!
pub struct Capabilities {
    pub shader_model: shade::ShaderModel,

    pub max_vertex_count: usize,
    pub max_index_count: usize,
    pub max_draw_buffers: usize,
    pub max_texture_size: usize,
    pub max_vertex_attributes: usize,

    /// In GLES it is not allowed to re-bind a buffer to a different
    /// target than the one it was initialized with.
    pub buffer_role_change_allowed: bool,

    pub array_buffer_supported: bool,
    pub fragment_output_supported: bool,
    pub immutable_storage_supported: bool,
    pub instance_base_supported: bool,
    pub instance_call_supported: bool,
    pub instance_rate_supported: bool,
    pub render_targets_supported: bool,
    pub sampler_objects_supported: bool,
    pub srgb_color_supported: bool,
    pub uniform_block_supported: bool,
    pub vertex_base_supported: bool,
}

/// Specifies the access allowed to a buffer mapping.
#[derive(Copy, Clone)]
pub enum MapAccess {
    /// Only allow reads.
    Readable,
    /// Only allow writes.
    Writable,
    /// Allow full access.
    RW
}

/// Describes what geometric primitives are created from vertex data.
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum PrimitiveType {
    /// Each vertex represents a single point.
    Point,
    /// Each pair of vertices represent a single line segment. For example, with `[a, b, c, d,
    /// e]`, `a` and `b` form a line, `c` and `d` form a line, and `e` is discarded.
    Line,
    /// Every two consecutive vertices represent a single line segment. Visually forms a "path" of
    /// lines, as they are all connected. For example, with `[a, b, c]`, `a` and `b` form a line
    /// line, and `b` and `c` form a line.
    LineStrip,
    /// Each triplet of vertices represent a single triangle. For example, with `[a, b, c, d, e]`,
    /// `a`, `b`, and `c` form a triangle, `d` and `e` are discarded.
    TriangleList,
    /// Every three consecutive vertices represent a single triangle. For example, with `[a, b, c,
    /// d]`, `a`, `b`, and `c` form a triangle, and `b`, `c`, and `d` form a triangle.
    TriangleStrip,
    /// The first vertex with the last two are forming a triangle. For example, with `[a, b, c, d
    /// ]`, `a` , `b`, and `c` form a triangle, and `a`, `c`, and `d` form a triangle.
    TriangleFan,
    //Quad,
}

/// A type of each index value in the mesh's index buffer
pub type IndexType = attrib::IntSize;

/// Role of the memory buffer. GLES doesn't chaning bind points for buffers.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum BufferRole {
    /// Generic vertex buffer
    Vertex,
    /// Index buffer
    Index,
    /// Uniform block buffer
    Uniform,
}

/// A hint as to how this buffer will be used.
///
/// The nature of these hints make them very implementation specific. Different drivers on
/// different hardware will handle them differently. Only careful profiling will tell which is the
/// best to use for a specific buffer.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum BufferUsage {
    /// Once uploaded, this buffer will rarely change, but will be read from often.
    Static,
    /// This buffer will be updated "frequently", and will be read from multiple times between
    /// updates.
    Dynamic,
    /// This buffer always or almost always be updated after each read.
    Stream,
}

/// An information block that is immutable and associated with each buffer
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct BufferInfo {
    /// Role
    pub role: BufferRole,
    /// Usage hint
    pub usage: BufferUsage,
    /// Size in bytes
    pub size: usize,
}

/// An error happening on buffer updates.
#[derive(Clone, PartialEq, Debug)]
pub enum BufferUpdateError {
    /// Trying to change the contents outside of the allocation.
    OutOfBounds,
}

/// Resources pertaining to a specific API.
#[allow(missing_docs)]
pub trait Resources:           Clone + Hash + fmt::Debug + Eq + PartialEq {
    type Buffer:        Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync + 'static;
    type ArrayBuffer:   Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync + 'static;
    type Shader:        Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync + 'static;
    type Program:       Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync + 'static;
    type FrameBuffer:   Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync + 'static;
    type Surface:       Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync + 'static;
    type Texture:       Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync + 'static;
    type Sampler:       Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync + 'static;
    type Fence:         Copy + Clone + Hash + fmt::Debug + Eq + PartialEq + Send + Sync + 'static;
}

#[allow(missing_docs)]
pub trait Factory<R: Resources> {
    /// Associated mapper type
    type Mapper: Clone + mapping::Raw;

    /// Returns the capabilities available to the specific API implementation
    fn get_capabilities<'a>(&'a self) -> &'a Capabilities;

    // resource creation
    fn create_buffer_raw(&mut self, size: usize, BufferRole, BufferUsage) -> handle::RawBuffer<R>;
    fn create_buffer_static_raw(&mut self, data: &[u8], BufferRole) -> handle::RawBuffer<R>;
    fn create_buffer_static<T>(&mut self, data: &[T], role: BufferRole) -> handle::Buffer<R, T> {
        handle::Buffer::from_raw(
            self.create_buffer_static_raw(as_byte_slice(data), role))
    }
    fn create_buffer_dynamic<T>(&mut self, num: usize, role: BufferRole) -> handle::Buffer<R, T> {
        handle::Buffer::from_raw(
            self.create_buffer_raw(num * mem::size_of::<T>(), role, BufferUsage::Stream))
    }

    fn create_array_buffer(&mut self) -> Result<handle::ArrayBuffer<R>, NotSupported>;
    fn create_shader(&mut self, stage: shade::Stage, code: &[u8]) ->
                     Result<handle::Shader<R>, shade::CreateShaderError>;
    fn create_program(&mut self, shaders: &[handle::Shader<R>], targets: Option<&[&str]>)
                      -> Result<handle::Program<R>, shade::CreateProgramError>;
    fn create_frame_buffer(&mut self) -> Result<handle::FrameBuffer<R>, NotSupported>;
    fn create_surface(&mut self, tex::SurfaceInfo) -> Result<handle::Surface<R>, tex::SurfaceError>;
    fn create_texture(&mut self, tex::TextureInfo) -> Result<handle::Texture<R>, tex::TextureError>;
    fn create_sampler(&mut self, tex::SamplerInfo) -> handle::Sampler<R>;

    /// Update the information stored in a specific buffer
    fn update_buffer_raw(&mut self, buf: &handle::RawBuffer<R>, data: &[u8], offset_bytes: usize)
                         -> Result<(), BufferUpdateError>;
    fn update_buffer<T>(&mut self, buf: &handle::Buffer<R, T>, data: &[T], offset_elements: usize)
                        -> Result<(), BufferUpdateError> {
        self.update_buffer_raw(buf.raw(), as_byte_slice(data), mem::size_of::<T>() * offset_elements)
    }
    fn map_buffer_raw(&mut self, &handle::RawBuffer<R>, MapAccess) -> Self::Mapper;
    fn unmap_buffer_raw(&mut self, Self::Mapper);
    fn map_buffer_readable<T: Copy>(&mut self, &handle::Buffer<R, T>) -> mapping::Readable<T, R, Self>;
    fn map_buffer_writable<T: Copy>(&mut self, &handle::Buffer<R, T>) -> mapping::Writable<T, R, Self>;
    fn map_buffer_rw<T: Copy>(&mut self, &handle::Buffer<R, T>) -> mapping::RW<T, R, Self>;

    /// Update the information stored in a texture
    fn update_texture_raw(&mut self, tex: &handle::Texture<R>,
                          img: &tex::ImageInfo, data: &[u8],
                          kind: Option<tex::Kind>) -> Result<(), tex::TextureError>;

    fn update_texture<T>(&mut self, tex: &handle::Texture<R>,
                         img: &tex::ImageInfo, data: &[T],
                         kind: Option<tex::Kind>) -> Result<(), tex::TextureError> {
        self.update_texture_raw(tex, img, as_byte_slice(data), kind)
    }

    fn generate_mipmap(&mut self, &handle::Texture<R>);

    /// Create a new texture with given data
    fn create_texture_static<T>(&mut self, info: tex::TextureInfo, data: &[T])
                             -> Result<handle::Texture<R>, tex::TextureError> {
        let image_info = info.into();
        match self.create_texture(info) {
            Ok(handle) => self.update_texture(&handle, &image_info, data, None)
                              .map(|_| handle),
            Err(e) => Err(e),
        }
    }
}

/// All the data needed simultaneously for submitting a command buffer for
/// execution on a device.
pub type SubmitInfo<'a, D: Device> = (
    &'a D::CommandBuffer,
    &'a draw::DataBuffer,
    &'a handle::Manager<D::Resources>
);

/// An interface for performing draw calls using a specific graphics API
pub trait Device {
    /// Associated resources type.
    type Resources: Resources;
    /// Associated command buffer type.
    type CommandBuffer: draw::CommandBuffer<Self::Resources>;

    /// Returns the capabilities available to the specific API implementation.
    fn get_capabilities<'a>(&'a self) -> &'a Capabilities;

    /// Reset all the states to disabled/default.
    fn reset_state(&mut self);

    /// Submit a command buffer for execution.
    fn submit(&mut self, SubmitInfo<Self>);

    /// Cleanup unused resources, to be called between frames.
    fn cleanup(&mut self);
}

/// Extension to the Device that allows for submitting of commands
/// around a fence
pub trait DeviceFence<R: Resources>: Device<Resources=R> {
    /// Submit a command buffer to the stream creating a fence
    /// the fence is signaled after the GPU has executed all commands
    /// in the buffer
    fn fenced_submit(&mut self, SubmitInfo<Self>, after: Option<handle::Fence<R>>) -> handle::Fence<R>;

    /// Wait on the supplied fence stalling the current thread until
    /// the fence is satisfied
    fn fence_wait(&mut self, fence: &handle::Fence<R>);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate mysql;
extern crate rassoodock;

use rassoodock::readers::mysql::mysql_reader::MySqlReader;
use rassoodock::readers::database_reader::DatabaseReader;
use mysql as my;
use std::env;

fn init() {
    let ip_address_opt = env::var("MYSQL_IP_ADDRESS").ok();
    let username_opt = env::var("MYSQL_USERNAME").ok();
    let password_opt = env::var("MYSQL_PASSWORD").ok();

    let mut builder = my::OptsBuilder::new();
    builder.ip_or_hostname(ip_address_opt)
        .db_name(Some("mysql"))
        .user(username_opt)
        .pass(password_opt);

    let pool = my::Pool::new(my::Opts::from(builder)).unwrap();
    pool.prep_exec("DROP DATABASE IF EXISTS rassoodock_mysql_test", ());
    pool.prep_exec("CREATE DATABASE rassoodock_mysql_test", ()).unwrap();
    pool.prep_exec("CREATE TABLE tuts(
        tutorial_id INT NOT NULL AUTO_INCREMENT,
        tutorial_title VARCHAR(100) NOT NULL,
        tutorial_author VARCHAR(40) NOT NULL,
        submission_date DATE,
        PRIMARY KEY ( tutorial_id )", ()).unwrap();
    pool.prep_exec("DELIMITER //
                    CREATE PROCEDURE get_tuts()
                    BEGIN
                        SELECT * FROM tuts;
                    END//", ()).unwrap();

}

#[test]
fn test_mysql_reader() {
    init();
    let reader = MySqlReader{};
    let stored_procs = reader.read_stored_procedures("rassoodock_mysql_test".to_owned());
    assert_eq!(stored_procs.len(), 1);
    let ref get_tuts = stored_procs[0];
    assert_eq!(get_tuts.stored_proc_sql_string, "get_tuts");
}

<commit_msg>Fix test<commit_after>#[macro_use]
extern crate mysql;
extern crate rassoodock;

use rassoodock::readers::mysql::mysql_reader::MySqlReader;
use rassoodock::readers::database_reader::DatabaseReader;
use mysql as my;
use std::env;

static MYSQL_IP_ENV_VAR_NAME: &'static str = "MYSQL_IP_ADDRESS";
static MYSQL_USERNAME_ENV_VAR_NAME: &'static str = "MYSQL_USERNAME";
static MYSQL_PASSWORD_ENV_VAR_NAME: &'static str = "MYSQL_PASSWORD";
static DATABASE: &'static str = "rassoodock_mysql_test";

fn db_init() {

    let ip_address_opt = env::var(MYSQL_IP_ENV_VAR_NAME).ok();
    let username_opt = env::var(MYSQL_USERNAME_ENV_VAR_NAME).ok();
    let password_opt = env::var(MYSQL_PASSWORD_ENV_VAR_NAME).ok();
    let mut db_builder = my::OptsBuilder::new();
    db_builder.ip_or_hostname(ip_address_opt)
        .db_name(Some("mysql"))
        .user(username_opt)
        .pass(password_opt);

    let db_pool = my::Pool::new(my::Opts::from(db_builder)).unwrap();
    db_pool.prep_exec(format!("DROP DATABASE IF EXISTS {}", DATABASE), ()).unwrap();
    db_pool.prep_exec(format!("CREATE DATABASE {}", DATABASE), ()).unwrap();
}

fn init() {
    db_init();
    let ip_address_opt = env::var(MYSQL_IP_ENV_VAR_NAME).ok();
    let username_opt = env::var(MYSQL_USERNAME_ENV_VAR_NAME).ok();
    let password_opt = env::var(MYSQL_PASSWORD_ENV_VAR_NAME).ok();

    let mut builder = my::OptsBuilder::new();
    builder.ip_or_hostname(ip_address_opt)
        .db_name(Some(DATABASE))
        .user(username_opt)
        .pass(password_opt);

    let pool = my::Pool::new(my::Opts::from(builder)).unwrap();
    pool.prep_exec("CREATE TABLE Persons (
                        ID int NOT NULL AUTO_INCREMENT,
                        LastName varchar(255) NOT NULL,
                        FirstName varchar(255),
                        Age int,
                        PRIMARY KEY (ID)
                    );", ()).unwrap();

    let mut conn = pool.get_conn().unwrap();
    conn.query("CREATE PROCEDURE get_persons()
                BEGIN
                    SELECT * FROM Persons;
                END;").unwrap();
}

#[test]
fn test_mysql_reader() {
    init();
    let reader = MySqlReader{};
    let stored_procs = reader.read_stored_procedures("rassoodock_mysql_test".to_owned());
    assert_eq!(stored_procs.len(), 1);
    let ref get_tuts = stored_procs[0];
    assert_eq!(get_tuts.stored_proc_sql_string, "get_persons");
}

<|endoftext|>


<commit_before>use eew::EEW;
use destination::client::TwitterClient;
use destination::Destination;
use collections::LimitedQueue;
use translator::ja_format_eew_short;

pub struct Twitter {
	client: TwitterClient,
	latest_tw_ids: LimitedQueue<(String, u64)>,
	reply_enabled: bool,
	updown_enabled: bool,
}

impl Twitter {

	pub fn new(consumer_key: String, consumer_secret: String,
		access_key: String, access_secret: String, reply_enabled: bool, updown_enabled: bool) -> Twitter
	{
		let client = TwitterClient::new(consumer_key, consumer_secret, access_key, access_secret);
		let q = LimitedQueue::new(16);

		Twitter { client: client, latest_tw_ids: q,
			reply_enabled: reply_enabled, updown_enabled: updown_enabled }
	}

	pub fn is_valid(&self) -> bool
	{
		self.client.is_valid()
	}
}

impl Destination for Twitter {

	fn emit(&mut self, latest: &EEW, prev: Option<&EEW>)
	{
		let prev = match self.updown_enabled {
			true => prev,
			false => None,
		};

		let out = match ja_format_eew_short(latest, prev) {
			Some(out) => out,
			None => return
		};

		let prev_tw_id = match self.reply_enabled {
			true => self.latest_tw_ids.iter().find(|x| x.0 == latest.id).map(|x| x.1),
			false => None
		};

		match self.client.update_status(&out, prev_tw_id) {

			Ok(tw_id) => {

				if prev_tw_id == None {
					self.latest_tw_ids.push((latest.id.clone(), tw_id));
				} else {
					self.latest_tw_ids.iter_mut().find(|x| x.0 == latest.id).unwrap().1 = tw_id;
				}
			},

			Err(e) => {
				error!("TwitterError: {:?}", e);
			}
		}
	}
}
<commit_msg>Remove direct usage of LimitedQueue in Twitter<commit_after>use eew::EEW;
use destination::client::TwitterClient;
use destination::Destination;
use collections::IndexedLimitedQueue;
use translator::ja_format_eew_short;

pub struct Twitter {
	client: TwitterClient,
	latest_tw_ids: IndexedLimitedQueue<u64>,
	reply_enabled: bool,
	updown_enabled: bool,
}

impl Twitter {

	pub fn new(consumer_key: String, consumer_secret: String,
		access_key: String, access_secret: String, reply_enabled: bool, updown_enabled: bool) -> Twitter
	{
		let client = TwitterClient::new(consumer_key, consumer_secret, access_key, access_secret);
		let q = IndexedLimitedQueue::new(16);

		Twitter { client: client, latest_tw_ids: q,
			reply_enabled: reply_enabled, updown_enabled: updown_enabled }
	}

	pub fn is_valid(&self) -> bool
	{
		self.client.is_valid()
	}
}

impl Destination for Twitter {

	fn emit(&mut self, latest: &EEW, prev: Option<&EEW>)
	{
		let prev = match self.updown_enabled {
			true => prev,
			false => None,
		};

		let out = match ja_format_eew_short(latest, prev) {
			Some(out) => out,
			None => return
		};

		let prev_tw_id = match self.reply_enabled {
			true => self.latest_tw_ids.get(latest.id.as_ref()).map(|id| *id),
			false => None
		};

		match self.client.update_status(&out, prev_tw_id) {

			Ok(tw_id) => {
				self.latest_tw_ids.upsert(latest.id.clone(), tw_id);
			},

			Err(e) => {
				error!("TwitterError: {:?}", e);
			}
		}
	}
}
<|endoftext|>


<commit_before>use std::sync::mpsc::Sender;

use super::Response;

pub trait ResponseSender {
    fn send_response(&mut self, response: Response);
    fn boxed_clone(&self) -> Box<ResponseSender>;
}

impl ResponseSender for Sender<Response> {
    fn send_response(&mut self, response: Response) {
        let _ = self.send(response);
    }
    fn boxed_clone(&self) -> Box<ResponseSender> {
        Box::new(self.clone())
    }
}
<commit_msg>dispatcher/response: add MockResponseSender<commit_after>use std::sync::mpsc::Sender;

use super::Response;

pub trait ResponseSender {
    fn send_response(&mut self, response: Response);
    fn boxed_clone(&self) -> Box<ResponseSender>;
}

impl ResponseSender for Sender<Response> {
    fn send_response(&mut self, response: Response) {
        let _ = self.send(response);
    }
    fn boxed_clone(&self) -> Box<ResponseSender> {
        Box::new(self.clone())
    }
}

#[derive(Clone)]
pub struct MockResponseSender(pub Vec<Response>);

impl MockResponseSender {
    pub fn new() -> MockResponseSender {
        MockResponseSender(Vec::new())
    }
}

impl ResponseSender for MockResponseSender {
    fn send_response(&mut self, response: Response) {
        self.0.push(response);
    }
    fn boxed_clone(&self) -> Box<ResponseSender> {
        Box::new(self.clone())
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 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.

use std::fs::File;
use std::io::{Seek, SeekFrom, Read, Write};
use std::os::unix::io::{AsRawFd, RawFd};
use std::result;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread::spawn;

use byteorder::{ByteOrder, LittleEndian};

use sys_util::Result as SysResult;
use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError, Poller};

use super::{VirtioDevice, Queue, DescriptorChain, INTERRUPT_STATUS_USED_RING, TYPE_BLOCK};

const QUEUE_SIZE: u16 = 256;
const QUEUE_SIZES: &'static [u16] = &[QUEUE_SIZE];
const SECTOR_SHIFT: u8 = 9;

const VIRTIO_BLK_T_IN: u32 = 0;
const VIRTIO_BLK_T_OUT: u32 = 1;
const VIRTIO_BLK_T_FLUSH: u32 = 4;

const VIRTIO_BLK_S_OK: u8 = 0;
const VIRTIO_BLK_S_IOERR: u8 = 1;
const VIRTIO_BLK_S_UNSUPP: u8 = 2;

#[derive(PartialEq)]
enum RequestType {
    In,
    Out,
    Flush,
    Unsupported(u32),
}

#[derive(Debug)]
enum ParseError {
    /// Guest gave us bad memory addresses
    GuestMemory(GuestMemoryError),
    /// Guest gave us offsets that would have overflowed a usize.
    CheckedOffset(GuestAddress, usize),
    /// Guest gave us a write only descriptor that protocol says to read from.
    UnexpectedWriteOnlyDescriptor,
    /// Guest gave us a read only descriptor that protocol says to write to.
    UnexpectedReadOnlyDescriptor,
    /// Guest gave us too few descriptors in a descriptor chain.
    DescriptorChainTooShort,
    /// Guest gave us a descriptor that was too short to use.
    DescriptorLengthTooSmall,
}

fn request_type(mem: &GuestMemory,
                desc_addr: GuestAddress)
                -> result::Result<RequestType, ParseError> {
    let type_ = mem.read_obj_from_addr(desc_addr)
        .map_err(ParseError::GuestMemory)?;
    match type_ {
        VIRTIO_BLK_T_IN => Ok(RequestType::In),
        VIRTIO_BLK_T_OUT => Ok(RequestType::Out),
        VIRTIO_BLK_T_FLUSH => Ok(RequestType::Flush),
        t => Ok(RequestType::Unsupported(t)),
    }
}

fn sector(mem: &GuestMemory, desc_addr: GuestAddress) -> result::Result<u64, ParseError> {
    const SECTOR_OFFSET: usize = 8;
    let addr = match mem.checked_offset(desc_addr, SECTOR_OFFSET) {
        Some(v) => v,
        None => return Err(ParseError::CheckedOffset(desc_addr, SECTOR_OFFSET)),
    };

    mem.read_obj_from_addr(addr)
        .map_err(ParseError::GuestMemory)
}

#[derive(Debug)]
enum ExecuteError {
    Io,
    Unsupported(u32),
}

impl ExecuteError {
    fn status(&self) -> u8 {
        match self {
            &ExecuteError::Io => VIRTIO_BLK_S_IOERR,
            &ExecuteError::Unsupported(_) => VIRTIO_BLK_S_UNSUPP,
        }
    }
}

struct Request {
    request_type: RequestType,
    sector: u64,
    data_addr: GuestAddress,
    data_len: u32,
    status_addr: GuestAddress,
}

impl Request {
    fn parse(avail_desc: &DescriptorChain,
             mem: &GuestMemory)
             -> result::Result<Request, ParseError> {
        // The head contains the request type which MUST be readable.
        if avail_desc.is_write_only() {
            return Err(ParseError::UnexpectedWriteOnlyDescriptor);
        }

        let req_type = request_type(&mem, avail_desc.addr)?;
        let sector = sector(&mem, avail_desc.addr)?;
        let data_desc = avail_desc
            .next_descriptor()
            .ok_or(ParseError::DescriptorChainTooShort)?;
        let status_desc = data_desc
            .next_descriptor()
            .ok_or(ParseError::DescriptorChainTooShort)?;

        if data_desc.is_write_only() && req_type == RequestType::Out {
            return Err(ParseError::UnexpectedWriteOnlyDescriptor);
        }

        if !data_desc.is_write_only() && req_type == RequestType::In {
            return Err(ParseError::UnexpectedReadOnlyDescriptor);
        }

        // The status MUST always be writable
        if !status_desc.is_write_only() {
            return Err(ParseError::UnexpectedReadOnlyDescriptor);
        }

        if status_desc.len < 1 {
            return Err(ParseError::DescriptorLengthTooSmall);
        }

        Ok(Request {
               request_type: req_type,
               sector: sector,
               data_addr: data_desc.addr,
               data_len: data_desc.len,
               status_addr: status_desc.addr,
           })
    }

    fn execute<T: Seek + Read + Write>(&self,
                                       disk: &mut T,
                                       mem: &GuestMemory)
                                       -> result::Result<u32, ExecuteError> {
        // TODO(zachr): When VolatileMemory merges, make ExecuteError::Io more descriptive.
        disk.seek(SeekFrom::Start(self.sector << SECTOR_SHIFT))
            .map_err(|_| ExecuteError::Io)?;
        match self.request_type {
            RequestType::In => {
                mem.read_to_memory(self.data_addr, disk, self.data_len as usize)
                    .map_err(|_| ExecuteError::Io)?;
                return Ok(self.data_len);
            }
            RequestType::Out => {
                mem.write_from_memory(self.data_addr, disk, self.data_len as usize)
                    .map_err(|_| ExecuteError::Io)?
            }
            RequestType::Flush => disk.flush().map_err(|_| ExecuteError::Io)?,
            RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
        };
        Ok(0)
    }
}

struct Worker {
    queues: Vec<Queue>,
    mem: GuestMemory,
    disk_image: File,
    interrupt_status: Arc<AtomicUsize>,
    interrupt_evt: EventFd,
}

impl Worker {
    fn process_queue(&mut self, queue_index: usize) -> bool {
        let queue = &mut self.queues[queue_index];

        let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
        let mut used_count = 0;
        for avail_desc in queue.iter(&self.mem) {
            let len;
            match Request::parse(&avail_desc, &self.mem) {
                Ok(request) => {
                    let status = match request.execute(&mut self.disk_image, &self.mem) {
                        Ok(l) => {
                            len = l;
                            VIRTIO_BLK_S_OK
                        }
                        Err(e) => {
                            println!("block: error executing disk request: {:?}", e);
                            len = 1; // 1 byte for the status
                            e.status()
                        }
                    };
                    // We use unwrap because the request parsing process already checked that the
                    // status_addr was valid.
                    self.mem
                        .write_obj_at_addr(status, request.status_addr)
                        .unwrap();
                }
                Err(e) => {
                    println!("block: error processing available descriptor chain: {:?}",
                             e);
                    len = 0;
                }
            }
            used_desc_heads[used_count] = (avail_desc.index, len);
            used_count += 1;
        }

        for &(desc_index, len) in &used_desc_heads[..used_count] {
            queue.add_used(&self.mem, desc_index, len);
        }
        used_count > 0
    }

    fn signal_used_queue(&self) {
        self.interrupt_status
            .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
        self.interrupt_evt.write(1).unwrap();
    }

    fn run(&mut self, queue_evt: EventFd, kill_evt: EventFd) {
        const Q_AVAIL: u32 = 0;
        const KILL: u32 = 1;

        let mut poller = Poller::new(2);
        'poll: loop {
            let tokens = match poller.poll(&[(Q_AVAIL, &queue_evt), (KILL, &kill_evt)]) {
                Ok(v) => v,
                Err(e) => {
                    println!("block: error polling for events: {:?}", e);
                    break;
                }
            };

            let mut needs_interrupt = false;
            for &token in tokens {
                match token {
                    Q_AVAIL => {
                        if let Err(e) = queue_evt.read() {
                            println!("block: error reading queue EventFd: {:?}", e);
                            break 'poll;
                        }
                        needs_interrupt |= self.process_queue(0);
                    }
                    KILL => break 'poll,
                    _ => unreachable!(),
                }
            }
            if needs_interrupt {
                self.signal_used_queue();
            }
        }
    }
}

/// Virtio device for exposing block level read/write operations on a host file.
pub struct Block {
    kill_evt: Option<EventFd>,
    disk_size: u64,
    disk_image: Option<File>,
}

impl Block {
    /// Create a new virtio block device that operates on the given file.
    ///
    /// The given file must be seekable and sizable.
    pub fn new(mut disk_image: File) -> SysResult<Block> {
        let disk_size = disk_image.seek(SeekFrom::End(0))? as u64;
        Ok(Block {
               kill_evt: None,
               disk_size: disk_size,
               disk_image: Some(disk_image),
           })
    }
}

impl Drop for Block {
    fn drop(&mut self) {
        if let Some(kill_evt) = self.kill_evt.take() {
            // Ignore the result because there is nothing we can do about it.
            let _ = kill_evt.write(1);
        }
    }
}

impl VirtioDevice for Block {
    fn keep_fds(&self) -> Vec<RawFd> {
        let mut keep_fds = Vec::new();

        if let Some(ref disk_image) = self.disk_image {
            keep_fds.push(disk_image.as_raw_fd());
        }

        keep_fds
    }

    fn device_type(&self) -> u32 {
        TYPE_BLOCK
    }

    fn queue_max_sizes(&self) -> &[u16] {
        QUEUE_SIZES
    }

    fn read_config(&self, offset: u64, data: &mut [u8]) {
        // We only support the first word of configuration space.
        let v = match offset {
            0 => ((self.disk_size + 0x511) >> SECTOR_SHIFT) as u32,
            _ => return,
        };

        LittleEndian::write_u32(data, v);
    }

    fn activate(&mut self,
                mem: GuestMemory,
                interrupt_evt: EventFd,
                status: Arc<AtomicUsize>,
                queues: Vec<Queue>,
                mut queue_evts: Vec<EventFd>) {
        if queues.len() != 1 || queue_evts.len() != 1 {
            return;
        }

        let (self_kill_evt, kill_evt) =
            match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
                Ok(v) => v,
                Err(e) => {
                    println!("block: error creating kill EventFd pair: {:?}", e);
                    return;
                }
            };
        self.kill_evt = Some(self_kill_evt);

        if let Some(disk_image) = self.disk_image.take() {
            spawn(move || {
                let mut worker = Worker {
                    queues: queues,
                    mem: mem,
                    disk_image: disk_image,
                    interrupt_status: status,
                    interrupt_evt: interrupt_evt,
                };
                worker.run(queue_evts.remove(0), kill_evt);
            });
        }
    }
}
<commit_msg>crosvm: block - Fill upper 4 bytes of disk size<commit_after>// Copyright 2017 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.

use std::cmp;
use std::fs::File;
use std::io::{Seek, SeekFrom, Read, Write};
use std::os::unix::io::{AsRawFd, RawFd};
use std::result;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread::spawn;

use sys_util::Result as SysResult;
use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError, Poller};

use super::{VirtioDevice, Queue, DescriptorChain, INTERRUPT_STATUS_USED_RING, TYPE_BLOCK};

const QUEUE_SIZE: u16 = 256;
const QUEUE_SIZES: &'static [u16] = &[QUEUE_SIZE];
const SECTOR_SHIFT: u8 = 9;
const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;

const VIRTIO_BLK_T_IN: u32 = 0;
const VIRTIO_BLK_T_OUT: u32 = 1;
const VIRTIO_BLK_T_FLUSH: u32 = 4;

const VIRTIO_BLK_S_OK: u8 = 0;
const VIRTIO_BLK_S_IOERR: u8 = 1;
const VIRTIO_BLK_S_UNSUPP: u8 = 2;

#[derive(PartialEq)]
enum RequestType {
    In,
    Out,
    Flush,
    Unsupported(u32),
}

#[derive(Debug)]
enum ParseError {
    /// Guest gave us bad memory addresses
    GuestMemory(GuestMemoryError),
    /// Guest gave us offsets that would have overflowed a usize.
    CheckedOffset(GuestAddress, usize),
    /// Guest gave us a write only descriptor that protocol says to read from.
    UnexpectedWriteOnlyDescriptor,
    /// Guest gave us a read only descriptor that protocol says to write to.
    UnexpectedReadOnlyDescriptor,
    /// Guest gave us too few descriptors in a descriptor chain.
    DescriptorChainTooShort,
    /// Guest gave us a descriptor that was too short to use.
    DescriptorLengthTooSmall,
}

fn request_type(mem: &GuestMemory,
                desc_addr: GuestAddress)
                -> result::Result<RequestType, ParseError> {
    let type_ = mem.read_obj_from_addr(desc_addr)
        .map_err(ParseError::GuestMemory)?;
    match type_ {
        VIRTIO_BLK_T_IN => Ok(RequestType::In),
        VIRTIO_BLK_T_OUT => Ok(RequestType::Out),
        VIRTIO_BLK_T_FLUSH => Ok(RequestType::Flush),
        t => Ok(RequestType::Unsupported(t)),
    }
}

fn sector(mem: &GuestMemory, desc_addr: GuestAddress) -> result::Result<u64, ParseError> {
    const SECTOR_OFFSET: usize = 8;
    let addr = match mem.checked_offset(desc_addr, SECTOR_OFFSET) {
        Some(v) => v,
        None => return Err(ParseError::CheckedOffset(desc_addr, SECTOR_OFFSET)),
    };

    mem.read_obj_from_addr(addr)
        .map_err(ParseError::GuestMemory)
}

#[derive(Debug)]
enum ExecuteError {
    Io,
    Unsupported(u32),
}

impl ExecuteError {
    fn status(&self) -> u8 {
        match self {
            &ExecuteError::Io => VIRTIO_BLK_S_IOERR,
            &ExecuteError::Unsupported(_) => VIRTIO_BLK_S_UNSUPP,
        }
    }
}

struct Request {
    request_type: RequestType,
    sector: u64,
    data_addr: GuestAddress,
    data_len: u32,
    status_addr: GuestAddress,
}

impl Request {
    fn parse(avail_desc: &DescriptorChain,
             mem: &GuestMemory)
             -> result::Result<Request, ParseError> {
        // The head contains the request type which MUST be readable.
        if avail_desc.is_write_only() {
            return Err(ParseError::UnexpectedWriteOnlyDescriptor);
        }

        let req_type = request_type(&mem, avail_desc.addr)?;
        let sector = sector(&mem, avail_desc.addr)?;
        let data_desc = avail_desc
            .next_descriptor()
            .ok_or(ParseError::DescriptorChainTooShort)?;
        let status_desc = data_desc
            .next_descriptor()
            .ok_or(ParseError::DescriptorChainTooShort)?;

        if data_desc.is_write_only() && req_type == RequestType::Out {
            return Err(ParseError::UnexpectedWriteOnlyDescriptor);
        }

        if !data_desc.is_write_only() && req_type == RequestType::In {
            return Err(ParseError::UnexpectedReadOnlyDescriptor);
        }

        // The status MUST always be writable
        if !status_desc.is_write_only() {
            return Err(ParseError::UnexpectedReadOnlyDescriptor);
        }

        if status_desc.len < 1 {
            return Err(ParseError::DescriptorLengthTooSmall);
        }

        Ok(Request {
               request_type: req_type,
               sector: sector,
               data_addr: data_desc.addr,
               data_len: data_desc.len,
               status_addr: status_desc.addr,
           })
    }

    fn execute<T: Seek + Read + Write>(&self,
                                       disk: &mut T,
                                       mem: &GuestMemory)
                                       -> result::Result<u32, ExecuteError> {
        // TODO(zachr): When VolatileMemory merges, make ExecuteError::Io more descriptive.
        disk.seek(SeekFrom::Start(self.sector << SECTOR_SHIFT))
            .map_err(|_| ExecuteError::Io)?;
        match self.request_type {
            RequestType::In => {
                mem.read_to_memory(self.data_addr, disk, self.data_len as usize)
                    .map_err(|_| ExecuteError::Io)?;
                return Ok(self.data_len);
            }
            RequestType::Out => {
                mem.write_from_memory(self.data_addr, disk, self.data_len as usize)
                    .map_err(|_| ExecuteError::Io)?
            }
            RequestType::Flush => disk.flush().map_err(|_| ExecuteError::Io)?,
            RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
        };
        Ok(0)
    }
}

struct Worker {
    queues: Vec<Queue>,
    mem: GuestMemory,
    disk_image: File,
    interrupt_status: Arc<AtomicUsize>,
    interrupt_evt: EventFd,
}

impl Worker {
    fn process_queue(&mut self, queue_index: usize) -> bool {
        let queue = &mut self.queues[queue_index];

        let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
        let mut used_count = 0;
        for avail_desc in queue.iter(&self.mem) {
            let len;
            match Request::parse(&avail_desc, &self.mem) {
                Ok(request) => {
                    let status = match request.execute(&mut self.disk_image, &self.mem) {
                        Ok(l) => {
                            len = l;
                            VIRTIO_BLK_S_OK
                        }
                        Err(e) => {
                            println!("block: error executing disk request: {:?}", e);
                            len = 1; // 1 byte for the status
                            e.status()
                        }
                    };
                    // We use unwrap because the request parsing process already checked that the
                    // status_addr was valid.
                    self.mem
                        .write_obj_at_addr(status, request.status_addr)
                        .unwrap();
                }
                Err(e) => {
                    println!("block: error processing available descriptor chain: {:?}",
                             e);
                    len = 0;
                }
            }
            used_desc_heads[used_count] = (avail_desc.index, len);
            used_count += 1;
        }

        for &(desc_index, len) in &used_desc_heads[..used_count] {
            queue.add_used(&self.mem, desc_index, len);
        }
        used_count > 0
    }

    fn signal_used_queue(&self) {
        self.interrupt_status
            .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
        self.interrupt_evt.write(1).unwrap();
    }

    fn run(&mut self, queue_evt: EventFd, kill_evt: EventFd) {
        const Q_AVAIL: u32 = 0;
        const KILL: u32 = 1;

        let mut poller = Poller::new(2);
        'poll: loop {
            let tokens = match poller.poll(&[(Q_AVAIL, &queue_evt), (KILL, &kill_evt)]) {
                Ok(v) => v,
                Err(e) => {
                    println!("block: error polling for events: {:?}", e);
                    break;
                }
            };

            let mut needs_interrupt = false;
            for &token in tokens {
                match token {
                    Q_AVAIL => {
                        if let Err(e) = queue_evt.read() {
                            println!("block: error reading queue EventFd: {:?}", e);
                            break 'poll;
                        }
                        needs_interrupt |= self.process_queue(0);
                    }
                    KILL => break 'poll,
                    _ => unreachable!(),
                }
            }
            if needs_interrupt {
                self.signal_used_queue();
            }
        }
    }
}

/// Virtio device for exposing block level read/write operations on a host file.
pub struct Block {
    kill_evt: Option<EventFd>,
    disk_image: Option<File>,
    config_space: Vec<u8>,
}

fn build_config_space(disk_size: u64) -> Vec<u8> {
    // We only support disk size, which uses the first two words of the configuration space.
    // If the image is not a multiple of the sector size, the tail bits are not exposed.
    // The config space is little endian.
    let mut config = Vec::with_capacity(8);
    let num_sectors = disk_size >> SECTOR_SHIFT;
    for i in 0..8 {
        config.push((num_sectors >> (8 * i)) as u8);
    }
    config
}

impl Block {
    /// Create a new virtio block device that operates on the given file.
    ///
    /// The given file must be seekable and sizable.
    pub fn new(mut disk_image: File) -> SysResult<Block> {
        let disk_size = disk_image.seek(SeekFrom::End(0))? as u64;
        if disk_size % SECTOR_SIZE != 0 {
            println!("block: Disk size {} is not a multiple of sector size {}; \
                         the remainder will not be visible to the guest.",
                     disk_size,
                     SECTOR_SIZE);
        }
        Ok(Block {
               kill_evt: None,
               disk_image: Some(disk_image),
               config_space: build_config_space(disk_size),
           })
    }
}

impl Drop for Block {
    fn drop(&mut self) {
        if let Some(kill_evt) = self.kill_evt.take() {
            // Ignore the result because there is nothing we can do about it.
            let _ = kill_evt.write(1);
        }
    }
}

impl VirtioDevice for Block {
    fn keep_fds(&self) -> Vec<RawFd> {
        let mut keep_fds = Vec::new();

        if let Some(ref disk_image) = self.disk_image {
            keep_fds.push(disk_image.as_raw_fd());
        }

        keep_fds
    }

    fn device_type(&self) -> u32 {
        TYPE_BLOCK
    }

    fn queue_max_sizes(&self) -> &[u16] {
        QUEUE_SIZES
    }

    fn read_config(&self, offset: u64, mut data: &mut [u8]) {
        let config_len = self.config_space.len() as u64;
        if offset >= config_len {
            return;
        }
        if let Some(end) = offset.checked_add(data.len() as u64) {
            // This write can't fail, offset and end are checked against config_len.
            data.write(&self.config_space[offset as usize..cmp::min(end, config_len) as usize])
                .unwrap();
        }
    }

    fn activate(&mut self,
                mem: GuestMemory,
                interrupt_evt: EventFd,
                status: Arc<AtomicUsize>,
                queues: Vec<Queue>,
                mut queue_evts: Vec<EventFd>) {
        if queues.len() != 1 || queue_evts.len() != 1 {
            return;
        }

        let (self_kill_evt, kill_evt) =
            match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
                Ok(v) => v,
                Err(e) => {
                    println!("block: error creating kill EventFd pair: {:?}", e);
                    return;
                }
            };
        self.kill_evt = Some(self_kill_evt);

        if let Some(disk_image) = self.disk_image.take() {
            spawn(move || {
                let mut worker = Worker {
                    queues: queues,
                    mem: mem,
                    disk_image: disk_image,
                    interrupt_status: status,
                    interrupt_evt: interrupt_evt,
                };
                worker.run(queue_evts.remove(0), kill_evt);
            });
        }
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;
    use sys_util::TempDir;

    use super::*;

    #[test]
    fn read_size() {
        let tempdir = TempDir::new("/tmp/block_read_test").unwrap();
        let mut path = PathBuf::from(tempdir.as_path().unwrap());
        path.push("disk_image");
        let f = File::create(&path).unwrap();
        f.set_len(0x1000).unwrap();

        let b = Block::new(f).unwrap();
        let mut num_sectors = [0u8; 4];
        b.read_config(0, &mut num_sectors);
        // size is 0x1000, so num_sectors is 8 (4096/512).
        assert_eq!([0x08, 0x00, 0x00, 0x00], num_sectors);
    }
}
<|endoftext|>