<commit_before>use cache;
use canvas::Canvas;
use canvas_zoom::CanvasZoom;
use euclid::{Point2D, Rect, Size2D, TypedPoint2D};
use glium::{self, Surface};
use glium::glutin::{self, EventsLoop};
use glium::index::PrimitiveType;
use vitral::{self, Context};

pub type GliumTexture = glium::texture::SrgbTexture2d;

pub trait MagogContext: Context {
    fn draw_image_2color<U>(
        &mut self,
        image: &vitral::ImageData<usize>,
        pos: TypedPoint2D<f32, U>,
        color: [f32; 4],
        back_color: [f32; 4],
    ) where
        U: vitral::ConvertibleUnit;
}

pub struct Backend {
    events: EventsLoop,
    program: glium::Program,
    textures: Vec<GliumTexture>,

    keypress: Vec<KeyEvent>,

    canvas: Canvas,
    zoom: CanvasZoom,
    window_size: Size2D<u32>,

    ui_state: vitral::State<usize, Vertex>,
}

impl Backend {
    pub fn new(display: &glium::Display, events: EventsLoop, width: u32, height: u32) -> Backend {
        let program = program!(
            display,
            150 => {
            vertex: "
                #version 150 core

                uniform mat4 matrix;

                in vec2 pos;
                in vec4 color;
                in vec4 back_color;
                in vec2 tex_coord;

                out vec4 v_color;
                out vec4 v_back_color;
                out vec2 v_tex_coord;

                void main() {
                    gl_Position = vec4(pos, 0.0, 1.0) * matrix;
                    v_color = color;
                    v_back_color = back_color;
                    v_tex_coord = tex_coord;
                }
            ",

            fragment: "
                #version 150 core
                uniform sampler2D tex;
                in vec4 v_color;
                in vec4 v_back_color;
                in vec2 v_tex_coord;
                out vec4 f_color;

                void main() {
                    vec4 tex_color = texture(tex, v_tex_coord);

                    // Discard fully transparent pixels to keep them from
                    // writing into the depth buffer.
                    if (tex_color.a == 0.0) discard;

                    f_color = v_color * tex_color + v_back_color * (vec4(1, 1, 1, 1) - tex_color);
                }
            "}).unwrap();

        let (w, h) = display.get_framebuffer_dimensions();

        let mut textures = Vec::new();

        let ui_state = vitral::Builder::new()
            .default_font(cache::font())
            .solid_texture(cache::solid())
            .build(Size2D::new(width as f32, height as f32), |img| {
                Self::make_texture(&mut textures, display, img)
            });

        Backend {
            events,
            program,
            textures,

            keypress: Vec::new(),
            canvas: Canvas::new(display, width, height),
            zoom: CanvasZoom::PixelPerfect,
            window_size: Size2D::new(w, h),

            ui_state,
        }
    }

    fn make_empty_texture(&mut self, display: &glium::Display, width: u32, height: u32) -> usize {
        let tex = glium::texture::SrgbTexture2d::empty(display, width, height).unwrap();
        self.textures.push(tex);
        self.textures.len() - 1
    }

    fn write_to_texture(&mut self, img: &vitral::ImageBuffer, texture: usize) {
        assert!(
            texture < self.textures.len(),
            "Trying to write nonexistent texture"
        );
        let rect = glium::Rect {
            left: 0,
            bottom: 0,
            width: img.size.width,
            height: img.size.height,
        };
        let mut raw = glium::texture::RawImage2d::from_raw_rgba(
            img.pixels.clone(),
            (img.size.width, img.size.height),
        );
        raw.format = glium::texture::ClientFormat::U8U8U8U8;

        self.textures[texture].write(rect, raw);
    }

    fn make_texture(
        textures: &mut Vec<GliumTexture>,
        display: &glium::Display,
        img: vitral::ImageBuffer,
    ) -> usize {
        let mut raw = glium::texture::RawImage2d::from_raw_rgba(
            img.pixels,
            (img.size.width, img.size.height),
        );
        raw.format = glium::texture::ClientFormat::U8U8U8U8;

        let tex = GliumTexture::new(display, raw).unwrap();
        textures.push(tex);
        textures.len() - 1
    }

    fn process_events(&mut self) -> bool {
        self.keypress.clear();

        // polling and handling the events received by the window
        let mut event_list = Vec::new();
        self.events.poll_events(|event| event_list.push(event));

        for event in event_list {
            match event {
                glutin::Event::WindowEvent{ event, .. } => match event {
                    glutin::WindowEvent::Closed => { return false; }
                    glutin::WindowEvent::MouseMoved{ position:(x, y), ..} => {
                        let pos = self.zoom.screen_to_canvas(
                            self.window_size,
                            self.canvas.size(),
                            Point2D::new(x as f32, y as f32),
                            );
                        self.input_mouse_move(pos.x as i32, pos.y as i32);
                    }
                    glutin::WindowEvent::MouseInput{state, button, ..} => {
                        self.input_mouse_button(
                            match button {
                                glutin::MouseButton::Left => vitral::MouseButton::Left,
                                glutin::MouseButton::Right => vitral::MouseButton::Right,
                                _ => vitral::MouseButton::Middle,
                            },
                            state == glutin::ElementState::Pressed,
                            )
                    }
                    glutin::WindowEvent::ReceivedCharacter(c) => self.input_char(c),
                    glutin::WindowEvent::KeyboardInput{ input: glutin::KeyboardInput{state, scancode, virtual_keycode: Some(vk), ..}, ..} => {
                        let is_down = state == glutin::ElementState::Pressed;

                        if is_down {
                            self.keypress.push(KeyEvent {
                                key_code: vk,
                                scancode: scancode as u8,
                            });
                        }

                        use glium::glutin::VirtualKeyCode::*;
                        if let Some(vk) = match vk {
                            Tab => Some(vitral::Keycode::Tab),
                            LShift | RShift => Some(vitral::Keycode::Shift),
                            LControl | RControl => Some(vitral::Keycode::Ctrl),
                            NumpadEnter | Return => Some(vitral::Keycode::Enter),
                            Back => Some(vitral::Keycode::Backspace),
                            Delete => Some(vitral::Keycode::Del),
                            Numpad8 | Up => Some(vitral::Keycode::Up),
                            Numpad2 | Down => Some(vitral::Keycode::Down),
                            Numpad4 | Left => Some(vitral::Keycode::Left),
                            Numpad6 | Right => Some(vitral::Keycode::Right),
                            _ => None,
                        }
                        {
                            self.input_key_state(vk, is_down);
                        }
                    }
                    _ => (),
                }
                glutin::Event::Awakened => {
                    // TODO: Suspend/awaken behavior
                }
                glutin::Event::DeviceEvent{ .. } => {}
            }
        }

        true
    }

    pub fn poll_key(&mut self) -> Option<KeyEvent> { self.keypress.pop() }

    pub fn render(&mut self, display: &glium::Display) {
        let batches = self.end_frame();
        let mut target = self.canvas.get_framebuffer_target(display);
        target.clear_color(0.0, 0.0, 0.0, 0.0);
        let (w, h) = target.get_dimensions();

        for batch in batches {
            // With the atlas cache, it's possible to get texture IDs for very recent images in the
            // render pipeline that don't have an actual GL texture associated with them yet.
            // Intercept them here and skip drawing them.
            if batch.texture >= self.textures.len() {
                continue;
            }

            // building the uniforms
            let uniforms =
                uniform! {
                matrix: [
                    [2.0 / w as f32, 0.0, 0.0, -1.0],
                    [0.0, -2.0 / h as f32, 0.0, 1.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0f32]
                ],
                tex: glium::uniforms::Sampler::new(&self.textures[batch.texture])
                    .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
            };

            let vertex_buffer = {
                glium::VertexBuffer::new(display, &batch.vertices).unwrap()
            };

            // building the index buffer
            let index_buffer = glium::IndexBuffer::new(
                display,
                PrimitiveType::TrianglesList,
                &batch.triangle_indices,
            ).unwrap();

            let params = glium::draw_parameters::DrawParameters {
                scissor: batch.clip.map(|clip| {
                    glium::Rect {
                        left: clip.origin.x as u32,
                        bottom: h - (clip.origin.y + clip.size.height) as u32,
                        width: clip.size.width as u32,
                        height: clip.size.height as u32,
                    }
                }),
                blend: glium::Blend::alpha_blending(),
                ..Default::default()
            };

            target
                .draw(
                    &vertex_buffer,
                    &index_buffer,
                    &self.program,
                    &uniforms,
                    &params,
                )
                .unwrap();
        }
    }

    fn refresh_atlas(&mut self, display: &glium::Display) {
        cache::ATLAS.with(|a| for a in a.borrow_mut().atlases_mut() {
            let idx = *a.texture();
            assert!(idx <= self.textures.len());
            if idx == self.textures.len() {
                self.make_empty_texture(display, a.size().width, a.size().height);
            }

            a.update_texture(|buf, &idx| self.write_to_texture(buf, idx));
        });
    }

    fn update_window_size(&mut self, display: &glium::Display) {
        let (w, h) = display.get_framebuffer_dimensions();
        self.window_size = Size2D::new(w, h);
    }

    pub fn update(&mut self, display: &glium::Display) -> bool {
        self.refresh_atlas(display);
        self.update_window_size(display);
        self.render(display);
        self.canvas.draw(display, self.zoom);
        self.process_events()
    }

    pub fn save_screenshot(&mut self, basename: &str) {
        use time;
        use std::path::Path;
        use std::fs::{self, File};
        use image;

        let shot = self.canvas.screenshot();

        let timestamp = time::precise_time_s() as u64;
        // Create screenshot filenames by concatenating the current timestamp in
        // seconds with a running number from 00 to 99. 100 shots per second
        // should be good enough.

        // Default if we fail to generate any of the 100 candidates for this
        // second, just overwrite with the "xx" prefix then.
        let mut filename = format!("{}-{}{}.png", basename, timestamp, "xx");

        // Run through candidates for this second.
        for i in 0..100 {
            let test_filename = format!("{}-{}{:02}.png", basename, timestamp, i);
            // If file does not exist.
            if fs::metadata(&test_filename).is_err() {
                // Thread-safe claiming: create_dir will fail if the dir
                // already exists (it'll exist if another thread is gunning
                // for the same filename and managed to get past us here).
                // At least assuming that create_dir is atomic...
                let squat_dir = format!(".tmp-{}{:02}", timestamp, i);
                if fs::create_dir(&squat_dir).is_ok() {
                    File::create(&test_filename).unwrap();
                    filename = test_filename;
                    fs::remove_dir(&squat_dir).unwrap();
                    break;
                } else {
                    continue;
                }
            }
        }

        let _ = image::save_buffer(
            &Path::new(&filename),
            &shot,
            shot.width(),
            shot.height(),
            image::ColorType::RGB(8),
        );
    }
}

impl Context for Backend {
    type T = usize;
    type V = Vertex;

    fn state(&self) -> &vitral::State<usize, Vertex> { &self.ui_state }

    fn state_mut(&mut self) -> &mut vitral::State<usize, Vertex> { &mut self.ui_state }

    fn new_vertex(
        &mut self,
        pos: Point2D<f32>,
        tex_coord: Point2D<f32>,
        color: [f32; 4],
    ) -> Vertex {
        Vertex {
            pos: [pos.x, pos.y],
            color: color,
            back_color: [0.0, 0.0, 0.0, 1.0],
            tex_coord: [tex_coord.x, tex_coord.y],
        }
    }
}

impl<C: Context<T = usize, V = Vertex>> MagogContext for C {
    fn draw_image_2color<U>(
        &mut self,
        image: &vitral::ImageData<usize>,
        pos: TypedPoint2D<f32, U>,
        color: [f32; 4],
        back_color: [f32; 4],
    ) where
        U: vitral::ConvertibleUnit,
    {
        // XXX: Copy-pasting tex rect code from Vitral.
        let pos = vitral::ConvertibleUnit::convert_point(&self.scale_factor(), pos);

        self.state_mut().start_texture(image.texture);
        let size = Size2D::new(image.size.width as f32, image.size.height as f32);
        let area = Rect::new(pos, size);

        let idx = self.state_mut().push_vertex(Vertex::new(
            area.origin,
            image.tex_coords.origin,
            color,
            back_color,
        ));
        self.state_mut().push_vertex(Vertex::new(
            area.top_right(),
            image.tex_coords.top_right(),
            color,
            back_color,
        ));
        self.state_mut().push_vertex(Vertex::new(
            area.bottom_right(),
            image.tex_coords.bottom_right(),
            color,
            back_color,
        ));
        self.state_mut().push_vertex(Vertex::new(
            area.bottom_left(),
            image.tex_coords.bottom_left(),
            color,
            back_color,
        ));

        self.state_mut().push_triangle(idx, idx + 1, idx + 2);
        self.state_mut().push_triangle(idx, idx + 2, idx + 3);
    }
}

pub struct KeyEvent {
    pub key_code: glutin::VirtualKeyCode,
    pub scancode: u8,
}

#[derive(Copy, Clone)]
pub struct Vertex {
    pub pos: [f32; 2],
    pub color: [f32; 4],
    pub back_color: [f32; 4],
    pub tex_coord: [f32; 2],
}
implement_vertex!(Vertex, pos, color, back_color, tex_coord);

impl Vertex {
    pub fn new(
        pos: Point2D<f32>,
        tex_coord: Point2D<f32>,
        color: [f32; 4],
        back_color: [f32; 4],
    ) -> Vertex {
        Vertex {
            pos: [pos.x, pos.y],
            color: color,
            back_color: back_color,
            tex_coord: [tex_coord.x, tex_coord.y],
        }
    }
}
<commit_msg>Rustfmt<commit_after>use cache;
use canvas::Canvas;
use canvas_zoom::CanvasZoom;
use euclid::{Point2D, Rect, Size2D, TypedPoint2D};
use glium::{self, Surface};
use glium::glutin::{self, EventsLoop};
use glium::index::PrimitiveType;
use vitral::{self, Context};

pub type GliumTexture = glium::texture::SrgbTexture2d;

pub trait MagogContext: Context {
    fn draw_image_2color<U>(
        &mut self,
        image: &vitral::ImageData<usize>,
        pos: TypedPoint2D<f32, U>,
        color: [f32; 4],
        back_color: [f32; 4],
    ) where
        U: vitral::ConvertibleUnit;
}

pub struct Backend {
    events: EventsLoop,
    program: glium::Program,
    textures: Vec<GliumTexture>,

    keypress: Vec<KeyEvent>,

    canvas: Canvas,
    zoom: CanvasZoom,
    window_size: Size2D<u32>,

    ui_state: vitral::State<usize, Vertex>,
}

impl Backend {
    pub fn new(display: &glium::Display, events: EventsLoop, width: u32, height: u32) -> Backend {
        let program = program!(
            display,
            150 => {
            vertex: "
                #version 150 core

                uniform mat4 matrix;

                in vec2 pos;
                in vec4 color;
                in vec4 back_color;
                in vec2 tex_coord;

                out vec4 v_color;
                out vec4 v_back_color;
                out vec2 v_tex_coord;

                void main() {
                    gl_Position = vec4(pos, 0.0, 1.0) * matrix;
                    v_color = color;
                    v_back_color = back_color;
                    v_tex_coord = tex_coord;
                }
            ",

            fragment: "
                #version 150 core
                uniform sampler2D tex;
                in vec4 v_color;
                in vec4 v_back_color;
                in vec2 v_tex_coord;
                out vec4 f_color;

                void main() {
                    vec4 tex_color = texture(tex, v_tex_coord);

                    // Discard fully transparent pixels to keep them from
                    // writing into the depth buffer.
                    if (tex_color.a == 0.0) discard;

                    f_color = v_color * tex_color + v_back_color * (vec4(1, 1, 1, 1) - tex_color);
                }
            "}).unwrap();

        let (w, h) = display.get_framebuffer_dimensions();

        let mut textures = Vec::new();

        let ui_state = vitral::Builder::new()
            .default_font(cache::font())
            .solid_texture(cache::solid())
            .build(Size2D::new(width as f32, height as f32), |img| {
                Self::make_texture(&mut textures, display, img)
            });

        Backend {
            events,
            program,
            textures,

            keypress: Vec::new(),
            canvas: Canvas::new(display, width, height),
            zoom: CanvasZoom::PixelPerfect,
            window_size: Size2D::new(w, h),

            ui_state,
        }
    }

    fn make_empty_texture(&mut self, display: &glium::Display, width: u32, height: u32) -> usize {
        let tex = glium::texture::SrgbTexture2d::empty(display, width, height).unwrap();
        self.textures.push(tex);
        self.textures.len() - 1
    }

    fn write_to_texture(&mut self, img: &vitral::ImageBuffer, texture: usize) {
        assert!(
            texture < self.textures.len(),
            "Trying to write nonexistent texture"
        );
        let rect = glium::Rect {
            left: 0,
            bottom: 0,
            width: img.size.width,
            height: img.size.height,
        };
        let mut raw = glium::texture::RawImage2d::from_raw_rgba(
            img.pixels.clone(),
            (img.size.width, img.size.height),
        );
        raw.format = glium::texture::ClientFormat::U8U8U8U8;

        self.textures[texture].write(rect, raw);
    }

    fn make_texture(
        textures: &mut Vec<GliumTexture>,
        display: &glium::Display,
        img: vitral::ImageBuffer,
    ) -> usize {
        let mut raw = glium::texture::RawImage2d::from_raw_rgba(
            img.pixels,
            (img.size.width, img.size.height),
        );
        raw.format = glium::texture::ClientFormat::U8U8U8U8;

        let tex = GliumTexture::new(display, raw).unwrap();
        textures.push(tex);
        textures.len() - 1
    }

    fn process_events(&mut self) -> bool {
        self.keypress.clear();

        // polling and handling the events received by the window
        let mut event_list = Vec::new();
        self.events.poll_events(|event| event_list.push(event));

        for event in event_list {
            match event {
                glutin::Event::WindowEvent { event, .. } => {
                    match event {
                        glutin::WindowEvent::Closed => {
                            return false;
                        }
                        glutin::WindowEvent::MouseMoved { position: (x, y), .. } => {
                            let pos = self.zoom.screen_to_canvas(
                                self.window_size,
                                self.canvas.size(),
                                Point2D::new(x as f32, y as f32),
                            );
                            self.input_mouse_move(pos.x as i32, pos.y as i32);
                        }
                        glutin::WindowEvent::MouseInput { state, button, .. } => {
                            self.input_mouse_button(
                                match button {
                                    glutin::MouseButton::Left => vitral::MouseButton::Left,
                                    glutin::MouseButton::Right => vitral::MouseButton::Right,
                                    _ => vitral::MouseButton::Middle,
                                },
                                state == glutin::ElementState::Pressed,
                            )
                        }
                        glutin::WindowEvent::ReceivedCharacter(c) => self.input_char(c),
                        glutin::WindowEvent::KeyboardInput {
                            input: glutin::KeyboardInput {
                                state,
                                scancode,
                                virtual_keycode: Some(vk),
                                ..
                            },
                            ..
                        } => {
                            let is_down = state == glutin::ElementState::Pressed;

                            if is_down {
                                self.keypress.push(KeyEvent {
                                    key_code: vk,
                                    scancode: scancode as u8,
                                });
                            }

                            use glium::glutin::VirtualKeyCode::*;
                            if let Some(vk) = match vk {
                                Tab => Some(vitral::Keycode::Tab),
                                LShift | RShift => Some(vitral::Keycode::Shift),
                                LControl | RControl => Some(vitral::Keycode::Ctrl),
                                NumpadEnter | Return => Some(vitral::Keycode::Enter),
                                Back => Some(vitral::Keycode::Backspace),
                                Delete => Some(vitral::Keycode::Del),
                                Numpad8 | Up => Some(vitral::Keycode::Up),
                                Numpad2 | Down => Some(vitral::Keycode::Down),
                                Numpad4 | Left => Some(vitral::Keycode::Left),
                                Numpad6 | Right => Some(vitral::Keycode::Right),
                                _ => None,
                            }
                            {
                                self.input_key_state(vk, is_down);
                            }
                        }
                        _ => (),
                    }
                }
                glutin::Event::Awakened => {
                    // TODO: Suspend/awaken behavior
                }
                glutin::Event::DeviceEvent { .. } => {}
            }
        }

        true
    }

    pub fn poll_key(&mut self) -> Option<KeyEvent> { self.keypress.pop() }

    pub fn render(&mut self, display: &glium::Display) {
        let batches = self.end_frame();
        let mut target = self.canvas.get_framebuffer_target(display);
        target.clear_color(0.0, 0.0, 0.0, 0.0);
        let (w, h) = target.get_dimensions();

        for batch in batches {
            // With the atlas cache, it's possible to get texture IDs for very recent images in the
            // render pipeline that don't have an actual GL texture associated with them yet.
            // Intercept them here and skip drawing them.
            if batch.texture >= self.textures.len() {
                continue;
            }

            // building the uniforms
            let uniforms =
                uniform! {
                matrix: [
                    [2.0 / w as f32, 0.0, 0.0, -1.0],
                    [0.0, -2.0 / h as f32, 0.0, 1.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0f32]
                ],
                tex: glium::uniforms::Sampler::new(&self.textures[batch.texture])
                    .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
            };

            let vertex_buffer = {
                glium::VertexBuffer::new(display, &batch.vertices).unwrap()
            };

            // building the index buffer
            let index_buffer = glium::IndexBuffer::new(
                display,
                PrimitiveType::TrianglesList,
                &batch.triangle_indices,
            ).unwrap();

            let params = glium::draw_parameters::DrawParameters {
                scissor: batch.clip.map(|clip| {
                    glium::Rect {
                        left: clip.origin.x as u32,
                        bottom: h - (clip.origin.y + clip.size.height) as u32,
                        width: clip.size.width as u32,
                        height: clip.size.height as u32,
                    }
                }),
                blend: glium::Blend::alpha_blending(),
                ..Default::default()
            };

            target
                .draw(
                    &vertex_buffer,
                    &index_buffer,
                    &self.program,
                    &uniforms,
                    &params,
                )
                .unwrap();
        }
    }

    fn refresh_atlas(&mut self, display: &glium::Display) {
        cache::ATLAS.with(|a| for a in a.borrow_mut().atlases_mut() {
            let idx = *a.texture();
            assert!(idx <= self.textures.len());
            if idx == self.textures.len() {
                self.make_empty_texture(display, a.size().width, a.size().height);
            }

            a.update_texture(|buf, &idx| self.write_to_texture(buf, idx));
        });
    }

    fn update_window_size(&mut self, display: &glium::Display) {
        let (w, h) = display.get_framebuffer_dimensions();
        self.window_size = Size2D::new(w, h);
    }

    pub fn update(&mut self, display: &glium::Display) -> bool {
        self.refresh_atlas(display);
        self.update_window_size(display);
        self.render(display);
        self.canvas.draw(display, self.zoom);
        self.process_events()
    }

    pub fn save_screenshot(&mut self, basename: &str) {
        use time;
        use std::path::Path;
        use std::fs::{self, File};
        use image;

        let shot = self.canvas.screenshot();

        let timestamp = time::precise_time_s() as u64;
        // Create screenshot filenames by concatenating the current timestamp in
        // seconds with a running number from 00 to 99. 100 shots per second
        // should be good enough.

        // Default if we fail to generate any of the 100 candidates for this
        // second, just overwrite with the "xx" prefix then.
        let mut filename = format!("{}-{}{}.png", basename, timestamp, "xx");

        // Run through candidates for this second.
        for i in 0..100 {
            let test_filename = format!("{}-{}{:02}.png", basename, timestamp, i);
            // If file does not exist.
            if fs::metadata(&test_filename).is_err() {
                // Thread-safe claiming: create_dir will fail if the dir
                // already exists (it'll exist if another thread is gunning
                // for the same filename and managed to get past us here).
                // At least assuming that create_dir is atomic...
                let squat_dir = format!(".tmp-{}{:02}", timestamp, i);
                if fs::create_dir(&squat_dir).is_ok() {
                    File::create(&test_filename).unwrap();
                    filename = test_filename;
                    fs::remove_dir(&squat_dir).unwrap();
                    break;
                } else {
                    continue;
                }
            }
        }

        let _ = image::save_buffer(
            &Path::new(&filename),
            &shot,
            shot.width(),
            shot.height(),
            image::ColorType::RGB(8),
        );
    }
}

impl Context for Backend {
    type T = usize;
    type V = Vertex;

    fn state(&self) -> &vitral::State<usize, Vertex> { &self.ui_state }

    fn state_mut(&mut self) -> &mut vitral::State<usize, Vertex> { &mut self.ui_state }

    fn new_vertex(
        &mut self,
        pos: Point2D<f32>,
        tex_coord: Point2D<f32>,
        color: [f32; 4],
    ) -> Vertex {
        Vertex {
            pos: [pos.x, pos.y],
            color: color,
            back_color: [0.0, 0.0, 0.0, 1.0],
            tex_coord: [tex_coord.x, tex_coord.y],
        }
    }
}

impl<C: Context<T = usize, V = Vertex>> MagogContext for C {
    fn draw_image_2color<U>(
        &mut self,
        image: &vitral::ImageData<usize>,
        pos: TypedPoint2D<f32, U>,
        color: [f32; 4],
        back_color: [f32; 4],
    ) where
        U: vitral::ConvertibleUnit,
    {
        // XXX: Copy-pasting tex rect code from Vitral.
        let pos = vitral::ConvertibleUnit::convert_point(&self.scale_factor(), pos);

        self.state_mut().start_texture(image.texture);
        let size = Size2D::new(image.size.width as f32, image.size.height as f32);
        let area = Rect::new(pos, size);

        let idx = self.state_mut().push_vertex(Vertex::new(
            area.origin,
            image.tex_coords.origin,
            color,
            back_color,
        ));
        self.state_mut().push_vertex(Vertex::new(
            area.top_right(),
            image.tex_coords.top_right(),
            color,
            back_color,
        ));
        self.state_mut().push_vertex(Vertex::new(
            area.bottom_right(),
            image.tex_coords.bottom_right(),
            color,
            back_color,
        ));
        self.state_mut().push_vertex(Vertex::new(
            area.bottom_left(),
            image.tex_coords.bottom_left(),
            color,
            back_color,
        ));

        self.state_mut().push_triangle(idx, idx + 1, idx + 2);
        self.state_mut().push_triangle(idx, idx + 2, idx + 3);
    }
}

pub struct KeyEvent {
    pub key_code: glutin::VirtualKeyCode,
    pub scancode: u8,
}

#[derive(Copy, Clone)]
pub struct Vertex {
    pub pos: [f32; 2],
    pub color: [f32; 4],
    pub back_color: [f32; 4],
    pub tex_coord: [f32; 2],
}
implement_vertex!(Vertex, pos, color, back_color, tex_coord);

impl Vertex {
    pub fn new(
        pos: Point2D<f32>,
        tex_coord: Point2D<f32>,
        color: [f32; 4],
        back_color: [f32; 4],
    ) -> Vertex {
        Vertex {
            pos: [pos.x, pos.y],
            color: color,
            back_color: back_color,
            tex_coord: [tex_coord.x, tex_coord.y],
        }
    }
}
<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u8,OpcodeExecInfo>,
}

impl CpuExecutor {
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u8, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = self.decode(cpu_state,mem);

        cpu_state.pc = cpu_state.pc + cpu_state.decode_register.info.len as u16;
    }
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
    }
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        self.fetch_and_decode(cpu_state,mem);
        self.execute(cpu_state,mem);
    }

    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> DecodeRegister {
        let mut dr = DecodeRegister {
            info : self.op_table[&cpu_state.instruction_register].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.x as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.y as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Accumulator     => { },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Implied         => { },
            //AddressMode::Indirect        => { },
            //AddressMode::IndexedIndirect => { },
            //AddressMode::IndirectIndexed => { },
            AddressMode::Relative        => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            //AddressMode::ZeroPageX       => {},
            //AddressMode::ZeroPageY       => {},
            _ => panic!("unrecognized addressing mode while decoding instruction_register!")
        }

        return dr;
    }
}

<commit_msg>implemented ZeroPageX, ZeroPageY addressing modes<commit_after>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u8,OpcodeExecInfo>,
}

impl CpuExecutor {
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u8, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = self.decode(cpu_state,mem);

        cpu_state.pc = cpu_state.pc + cpu_state.decode_register.info.len as u16;
    }
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
    }
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        self.fetch_and_decode(cpu_state,mem);
        self.execute(cpu_state,mem);
    }

    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> DecodeRegister {
        let mut dr = DecodeRegister {
            info : self.op_table[&cpu_state.instruction_register].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.x as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.y as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Accumulator     => { },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Implied         => { },
            //AddressMode::Indirect        => { },
            //AddressMode::IndexedIndirect => { },
            //AddressMode::IndirectIndexed => { },
            AddressMode::Relative        => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.y) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => panic!("unrecognized addressing mode while decoding instruction_register!")
        }

        return dr;
    }
}

<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u8,OpcodeExecInfo>,
}

impl CpuExecutor {
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u8, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = self.decode(cpu_state,mem);

        cpu_state.pc = cpu_state.pc + cpu_state.decode_register.info.len as u16;
    }
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
    }
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        self.fetch_and_decode(cpu_state,mem);
        self.execute(cpu_state,mem);
    }

    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> DecodeRegister {
        let mut dr = DecodeRegister {
            info : self.op_table[&cpu_state.instruction_register].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.x as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.y as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Accumulator     => { },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Implied         => { },
            AddressMode::Indirect        => {
                dr.addr_final  = Some(mem.read16(mem.read16(cpu_state.pc+1).unwrap()).unwrap());
            },
            //AddressMode::IndexedIndirect => { },
            //AddressMode::IndirectIndexed => { },
            AddressMode::Relative        => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.y) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => panic!("unrecognized addressing mode while decoding instruction_register!")
        }

        return dr;
    }
}

<commit_msg>Indirect Addressing Mode is really for realy right this time :)<commit_after>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u8,OpcodeExecInfo>,
}

impl CpuExecutor {
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u8, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = self.decode(cpu_state,mem);

        cpu_state.pc = cpu_state.pc + cpu_state.decode_register.info.len as u16;
    }
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
    }
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        self.fetch_and_decode(cpu_state,mem);
        self.execute(cpu_state,mem);
    }

    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> DecodeRegister {
        let mut dr = DecodeRegister {
            info : self.op_table[&cpu_state.instruction_register].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.x as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.y as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Accumulator     => { },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Implied         => { },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            //AddressMode::IndexedIndirect => { },
            //AddressMode::IndirectIndexed => { },
            AddressMode::Relative        => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.y) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => panic!("unrecognized addressing mode while decoding instruction_register!")
        }

        return dr;
    }
}

<|endoftext|>


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

//! Parameterized string expansion

use core::prelude::*;
use core::{char, int, vec};
use core::iterator::IteratorUtil;

#[deriving(Eq)]
enum States {
    Nothing,
    Percent,
    SetVar,
    GetVar,
    PushParam,
    CharConstant,
    CharClose,
    IntConstant,
    IfCond,
    IfBody
}

/// Types of parameters a capability can use
pub enum Param {
    String(~str),
    Number(int)
}

/// Container for static and dynamic variable arrays
pub struct Variables {
    /// Static variables A-Z
    sta: [Param, ..26],
    /// Dynamic variables a-z
    dyn: [Param, ..26]
}

impl Variables {
    /// Return a new zero-initialized Variables
    pub fn new() -> Variables {
        Variables{ sta: [Number(0), ..26], dyn: [Number(0), ..26] }
    }
}

/**
  Expand a parameterized capability

  # Arguments
  * `cap`    - string to expand
  * `params` - vector of params for %p1 etc
  * `vars`   - Variables struct for %Pa etc

  To be compatible with ncurses, `vars` should be the same between calls to `expand` for
  multiple capabilities for the same terminal.
  */
pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
    -> Result<~[u8], ~str> {
    let mut state = Nothing;

    // expanded cap will only rarely be larger than the cap itself
    let mut output = vec::with_capacity(cap.len());

    let mut stack: ~[Param] = ~[];

    let mut intstate = ~[];

    // Copy parameters into a local vector for mutability
    let mut mparams = [Number(0), ..9];
    for mparams.mut_iter().zip(params.iter()).advance |(dst, &src)| {
        *dst = src;
    }

    for cap.iter().transform(|&x| x).advance |c| {
        let cur = c as char;
        let mut old_state = state;
        match state {
            Nothing => {
                if cur == '%' {
                    state = Percent;
                } else {
                    output.push(c);
                }
            },
            Percent => {
                match cur {
                    '%' => { output.push(c); state = Nothing },
                    'c' => if stack.len() > 0 {
                        match stack.pop() {
                            // if c is 0, use 0200 (128) for ncurses compatibility
                            Number(c) => output.push(if c == 0 { 128 } else { c } as u8),
                            _       => return Err(~"a non-char was used with %c")
                        }
                    } else { return Err(~"stack is empty") },
                    's' => if stack.len() > 0 {
                        match stack.pop() {
                            String(s) => output.push_all(s.as_bytes()),
                            _         => return Err(~"a non-str was used with %s")
                        }
                    } else { return Err(~"stack is empty") },
                    'd' => if stack.len() > 0 {
                        match stack.pop() {
                            Number(x) => {
                                let s = x.to_str();
                                output.push_all(s.as_bytes())
                            }
                            _         => return Err(~"a non-number was used with %d")
                        }
                    } else { return Err(~"stack is empty") },
                    'p' => state = PushParam,
                    'P' => state = SetVar,
                    'g' => state = GetVar,
                    '\'' => state = CharConstant,
                    '{' => state = IntConstant,
                    'l' => if stack.len() > 0 {
                        match stack.pop() {
                            String(s) => stack.push(Number(s.len() as int)),
                            _         => return Err(~"a non-str was used with %l")
                        }
                    } else { return Err(~"stack is empty") },
                    '+' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x + y)),
                            (_, _) => return Err(~"non-numbers on stack with +")
                        }
                    } else { return Err(~"stack is empty") },
                    '-' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x - y)),
                            (_, _) => return Err(~"non-numbers on stack with -")
                        }
                    } else { return Err(~"stack is empty") },
                    '*' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x * y)),
                            (_, _) => return Err(~"non-numbers on stack with *")
                        }
                    } else { return Err(~"stack is empty") },
                    '/' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x / y)),
                            (_, _) => return Err(~"non-numbers on stack with /")
                        }
                    } else { return Err(~"stack is empty") },
                    'm' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x % y)),
                            (_, _) => return Err(~"non-numbers on stack with %")
                        }
                    } else { return Err(~"stack is empty") },
                    '&' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x & y)),
                            (_, _) => return Err(~"non-numbers on stack with &")
                        }
                    } else { return Err(~"stack is empty") },
                    '|' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x | y)),
                            (_, _) => return Err(~"non-numbers on stack with |")
                        }
                    } else { return Err(~"stack is empty") },
                    'A' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(0), Number(_)) => stack.push(Number(0)),
                            (Number(_), Number(0)) => stack.push(Number(0)),
                            (Number(_), Number(_)) => stack.push(Number(1)),
                            _ => return Err(~"non-numbers on stack with logical and")
                        }
                    } else { return Err(~"stack is empty") },
                    'O' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(0), Number(0)) => stack.push(Number(0)),
                            (Number(_), Number(_)) => stack.push(Number(1)),
                            _ => return Err(~"non-numbers on stack with logical or")
                        }
                    } else { return Err(~"stack is empty") },
                    '!' => if stack.len() > 0 {
                        match stack.pop() {
                            Number(0) => stack.push(Number(1)),
                            Number(_) => stack.push(Number(0)),
                            _ => return Err(~"non-number on stack with logical not")
                        }
                    } else { return Err(~"stack is empty") },
                    '~' => if stack.len() > 0 {
                        match stack.pop() {
                            Number(x) => stack.push(Number(!x)),
                            _         => return Err(~"non-number on stack with %~")
                        }
                    } else { return Err(~"stack is empty") },
                    'i' => match (copy mparams[0], copy mparams[1]) {
                        (Number(ref mut x), Number(ref mut y)) => {
                            *x += 1;
                            *y += 1;
                        },
                        (_, _) => return Err(~"first two params not numbers with %i")
                    },
                    '?' => state = return Err(fmt!("if expressions unimplemented (%?)", cap)),
                    _ => return Err(fmt!("unrecognized format option %c", cur))
                }
            },
            PushParam => {
                // params are 1-indexed
                stack.push(copy mparams[match char::to_digit(cur, 10) {
                    Some(d) => d - 1,
                    None => return Err(~"bad param number")
                }]);
            },
            SetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    if stack.len() > 0 {
                        let idx = (cur as u8) - ('A' as u8);
                        vars.sta[idx] = stack.pop();
                    } else { return Err(~"stack is empty") }
                } else if cur >= 'a' && cur <= 'z' {
                    if stack.len() > 0 {
                        let idx = (cur as u8) - ('a' as u8);
                        vars.dyn[idx] = stack.pop();
                    } else { return Err(~"stack is empty") }
                } else {
                    return Err(~"bad variable name in %P");
                }
            },
            GetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    let idx = (cur as u8) - ('A' as u8);
                    stack.push(copy vars.sta[idx]);
                } else if cur >= 'a' && cur <= 'z' {
                    let idx = (cur as u8) - ('a' as u8);
                    stack.push(copy vars.dyn[idx]);
                } else {
                    return Err(~"bad variable name in %g");
                }
            },
            CharConstant => {
                stack.push(Number(c as int));
                state = CharClose;
            },
            CharClose => {
                if cur != '\'' {
                    return Err(~"malformed character constant");
                }
            },
            IntConstant => {
                if cur == '}' {
                    stack.push(match int::parse_bytes(intstate, 10) {
                        Some(n) => Number(n),
                        None => return Err(~"bad int constant")
                    });
                    intstate.clear();
                    state = Nothing;
                } else {
                    intstate.push(cur as u8);
                    old_state = Nothing;
                }
            }
            _ => return Err(~"unimplemented state")
        }
        if state == old_state {
            state = Nothing;
        }
    }
    Ok(output)
}

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

    #[test]
    fn test_basic_setabf() {
        let s = bytes!("\\E[48;5;%p1%dm");
        assert_eq!(expand(s, [Number(1)], &mut Variables::new()).unwrap(), bytes!("\\E[48;5;1m").to_owned());
    }

    #[test]
    fn test_multiple_int_constants() {
        assert_eq!(expand(bytes!("%{1}%{2}%d%d"), [], &mut Variables::new()).unwrap(), bytes!("21").to_owned());
    }

    #[test]
    fn test_param_stack_failure_conditions() {
        let mut varstruct = Variables::new();
        let vars = &mut varstruct;
        let caps = ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
        for caps.iter().advance |cap| {
            let res = expand(cap.as_bytes(), [], vars);
            assert!(res.is_err(),
                    "Op %s succeeded incorrectly with 0 stack entries", *cap);
            let p = if *cap == "%s" || *cap == "%l" { String(~"foo") } else { Number(97) };
            let res = expand((bytes!("%p1")).to_owned() + cap.as_bytes(), [p], vars);
            assert!(res.is_ok(),
                    "Op %s failed with 1 stack entry: %s", *cap, res.unwrap_err());
        }
        let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
        for caps.iter().advance |cap| {
            let res = expand(cap.as_bytes(), [], vars);
            assert!(res.is_err(),
                    "Binop %s succeeded incorrectly with 0 stack entries", *cap);
            let res = expand((bytes!("%{1}")).to_owned() + cap.as_bytes(), [], vars);
            assert!(res.is_err(),
                    "Binop %s succeeded incorrectly with 1 stack entry", *cap);
            let res = expand((bytes!("%{1}%{2}")).to_owned() + cap.as_bytes(), [], vars);
            assert!(res.is_ok(),
                    "Binop %s failed with 2 stack entries: %s", *cap, res.unwrap_err());
        }
    }

    #[test]
    fn test_push_bad_param() {
        assert!(expand(bytes!("%pa"), [], &mut Variables::new()).is_err());
    }
}
<commit_msg>Implement terminfo param conditionals<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Parameterized string expansion

use core::prelude::*;
use core::{char, int, vec};
use core::iterator::IteratorUtil;

#[deriving(Eq)]
enum States {
    Nothing,
    Percent,
    SetVar,
    GetVar,
    PushParam,
    CharConstant,
    CharClose,
    IntConstant,
    SeekIfElse(int),
    SeekIfElsePercent(int),
    SeekIfEnd(int),
    SeekIfEndPercent(int)
}

/// Types of parameters a capability can use
pub enum Param {
    String(~str),
    Number(int)
}

/// Container for static and dynamic variable arrays
pub struct Variables {
    /// Static variables A-Z
    sta: [Param, ..26],
    /// Dynamic variables a-z
    dyn: [Param, ..26]
}

impl Variables {
    /// Return a new zero-initialized Variables
    pub fn new() -> Variables {
        Variables{ sta: [Number(0), ..26], dyn: [Number(0), ..26] }
    }
}

/**
  Expand a parameterized capability

  # Arguments
  * `cap`    - string to expand
  * `params` - vector of params for %p1 etc
  * `vars`   - Variables struct for %Pa etc

  To be compatible with ncurses, `vars` should be the same between calls to `expand` for
  multiple capabilities for the same terminal.
  */
pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
    -> Result<~[u8], ~str> {
    let mut state = Nothing;

    // expanded cap will only rarely be larger than the cap itself
    let mut output = vec::with_capacity(cap.len());

    let mut stack: ~[Param] = ~[];

    let mut intstate = ~[];

    // Copy parameters into a local vector for mutability
    let mut mparams = [Number(0), ..9];
    for mparams.mut_iter().zip(params.iter()).advance |(dst, &src)| {
        *dst = src;
    }

    for cap.iter().transform(|&x| x).advance |c| {
        let cur = c as char;
        let mut old_state = state;
        match state {
            Nothing => {
                if cur == '%' {
                    state = Percent;
                } else {
                    output.push(c);
                }
            },
            Percent => {
                match cur {
                    '%' => { output.push(c); state = Nothing },
                    'c' => if stack.len() > 0 {
                        match stack.pop() {
                            // if c is 0, use 0200 (128) for ncurses compatibility
                            Number(c) => output.push(if c == 0 { 128 } else { c } as u8),
                            _       => return Err(~"a non-char was used with %c")
                        }
                    } else { return Err(~"stack is empty") },
                    's' => if stack.len() > 0 {
                        match stack.pop() {
                            String(s) => output.push_all(s.as_bytes()),
                            _         => return Err(~"a non-str was used with %s")
                        }
                    } else { return Err(~"stack is empty") },
                    'd' => if stack.len() > 0 {
                        match stack.pop() {
                            Number(x) => {
                                let s = x.to_str();
                                output.push_all(s.as_bytes())
                            }
                            _         => return Err(~"a non-number was used with %d")
                        }
                    } else { return Err(~"stack is empty") },
                    'p' => state = PushParam,
                    'P' => state = SetVar,
                    'g' => state = GetVar,
                    '\'' => state = CharConstant,
                    '{' => state = IntConstant,
                    'l' => if stack.len() > 0 {
                        match stack.pop() {
                            String(s) => stack.push(Number(s.len() as int)),
                            _         => return Err(~"a non-str was used with %l")
                        }
                    } else { return Err(~"stack is empty") },
                    '+' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(x + y)),
                            _ => return Err(~"non-numbers on stack with +")
                        }
                    } else { return Err(~"stack is empty") },
                    '-' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(x - y)),
                            _ => return Err(~"non-numbers on stack with -")
                        }
                    } else { return Err(~"stack is empty") },
                    '*' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(x * y)),
                            _ => return Err(~"non-numbers on stack with *")
                        }
                    } else { return Err(~"stack is empty") },
                    '/' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(x / y)),
                            _ => return Err(~"non-numbers on stack with /")
                        }
                    } else { return Err(~"stack is empty") },
                    'm' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(x % y)),
                            _ => return Err(~"non-numbers on stack with %")
                        }
                    } else { return Err(~"stack is empty") },
                    '&' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(x & y)),
                            _ => return Err(~"non-numbers on stack with &")
                        }
                    } else { return Err(~"stack is empty") },
                    '|' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(x | y)),
                            _ => return Err(~"non-numbers on stack with |")
                        }
                    } else { return Err(~"stack is empty") },
                    '^' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(x ^ y)),
                            _ => return Err(~"non-numbers on stack with ^")
                        }
                    } else { return Err(~"stack is empty") },
                    '=' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(if x == y { 1 } else { 0 })),
                            _ => return Err(~"non-numbers on stack with =")
                        }
                    } else { return Err(~"stack is empty") },
                    '>' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(if x > y { 1 } else { 0 })),
                            _ => return Err(~"non-numbers on stack with >")
                        }
                    } else { return Err(~"stack is empty") },
                    '<' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(y), Number(x)) => stack.push(Number(if x < y { 1 } else { 0 })),
                            _ => return Err(~"non-numbers on stack with <")
                        }
                    } else { return Err(~"stack is empty") },
                    'A' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(0), Number(_)) => stack.push(Number(0)),
                            (Number(_), Number(0)) => stack.push(Number(0)),
                            (Number(_), Number(_)) => stack.push(Number(1)),
                            _ => return Err(~"non-numbers on stack with logical and")
                        }
                    } else { return Err(~"stack is empty") },
                    'O' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(0), Number(0)) => stack.push(Number(0)),
                            (Number(_), Number(_)) => stack.push(Number(1)),
                            _ => return Err(~"non-numbers on stack with logical or")
                        }
                    } else { return Err(~"stack is empty") },
                    '!' => if stack.len() > 0 {
                        match stack.pop() {
                            Number(0) => stack.push(Number(1)),
                            Number(_) => stack.push(Number(0)),
                            _ => return Err(~"non-number on stack with logical not")
                        }
                    } else { return Err(~"stack is empty") },
                    '~' => if stack.len() > 0 {
                        match stack.pop() {
                            Number(x) => stack.push(Number(!x)),
                            _         => return Err(~"non-number on stack with %~")
                        }
                    } else { return Err(~"stack is empty") },
                    'i' => match (copy mparams[0], copy mparams[1]) {
                        (Number(ref mut x), Number(ref mut y)) => {
                            *x += 1;
                            *y += 1;
                        },
                        (_, _) => return Err(~"first two params not numbers with %i")
                    },

                    // conditionals
                    '?' => (),
                    't' => if stack.len() > 0 {
                        match stack.pop() {
                            Number(0) => state = SeekIfElse(0),
                            Number(_) => (),
                            _         => return Err(~"non-number on stack with conditional")
                        }
                    } else { return Err(~"stack is empty") },
                    'e' => state = SeekIfEnd(0),
                    ';' => (),

                    _ => return Err(fmt!("unrecognized format option %c", cur))
                }
            },
            PushParam => {
                // params are 1-indexed
                stack.push(copy mparams[match char::to_digit(cur, 10) {
                    Some(d) => d - 1,
                    None => return Err(~"bad param number")
                }]);
            },
            SetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    if stack.len() > 0 {
                        let idx = (cur as u8) - ('A' as u8);
                        vars.sta[idx] = stack.pop();
                    } else { return Err(~"stack is empty") }
                } else if cur >= 'a' && cur <= 'z' {
                    if stack.len() > 0 {
                        let idx = (cur as u8) - ('a' as u8);
                        vars.dyn[idx] = stack.pop();
                    } else { return Err(~"stack is empty") }
                } else {
                    return Err(~"bad variable name in %P");
                }
            },
            GetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    let idx = (cur as u8) - ('A' as u8);
                    stack.push(copy vars.sta[idx]);
                } else if cur >= 'a' && cur <= 'z' {
                    let idx = (cur as u8) - ('a' as u8);
                    stack.push(copy vars.dyn[idx]);
                } else {
                    return Err(~"bad variable name in %g");
                }
            },
            CharConstant => {
                stack.push(Number(c as int));
                state = CharClose;
            },
            CharClose => {
                if cur != '\'' {
                    return Err(~"malformed character constant");
                }
            },
            IntConstant => {
                if cur == '}' {
                    stack.push(match int::parse_bytes(intstate, 10) {
                        Some(n) => Number(n),
                        None => return Err(~"bad int constant")
                    });
                    intstate.clear();
                    state = Nothing;
                } else {
                    intstate.push(cur as u8);
                    old_state = Nothing;
                }
            }
            SeekIfElse(level) => {
                if cur == '%' {
                    state = SeekIfElsePercent(level);
                }
                old_state = Nothing;
            }
            SeekIfElsePercent(level) => {
                if cur == ';' {
                    if level == 0 {
                        state = Nothing;
                    } else {
                        state = SeekIfElse(level-1);
                    }
                } else if cur == 'e' && level == 0 {
                    state = Nothing;
                } else if cur == '?' {
                    state = SeekIfElse(level+1);
                } else {
                    state = SeekIfElse(level);
                }
            }
            SeekIfEnd(level) => {
                if cur == '%' {
                    state = SeekIfEndPercent(level);
                }
                old_state = Nothing;
            }
            SeekIfEndPercent(level) => {
                if cur == ';' {
                    if level == 0 {
                        state = Nothing;
                    } else {
                        state = SeekIfEnd(level-1);
                    }
                } else if cur == '?' {
                    state = SeekIfEnd(level+1);
                } else {
                    state = SeekIfEnd(level);
                }
            }
        }
        if state == old_state {
            state = Nothing;
        }
    }
    Ok(output)
}

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

    #[test]
    fn test_basic_setabf() {
        let s = bytes!("\\E[48;5;%p1%dm");
        assert_eq!(expand(s, [Number(1)], &mut Variables::new()).unwrap(), bytes!("\\E[48;5;1m").to_owned());
    }

    #[test]
    fn test_multiple_int_constants() {
        assert_eq!(expand(bytes!("%{1}%{2}%d%d"), [], &mut Variables::new()).unwrap(), bytes!("21").to_owned());
    }

    #[test]
    fn test_param_stack_failure_conditions() {
        let mut varstruct = Variables::new();
        let vars = &mut varstruct;
        let caps = ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
        for caps.iter().advance |cap| {
            let res = expand(cap.as_bytes(), [], vars);
            assert!(res.is_err(),
                    "Op %s succeeded incorrectly with 0 stack entries", *cap);
            let p = if *cap == "%s" || *cap == "%l" { String(~"foo") } else { Number(97) };
            let res = expand((bytes!("%p1")).to_owned() + cap.as_bytes(), [p], vars);
            assert!(res.is_ok(),
                    "Op %s failed with 1 stack entry: %s", *cap, res.unwrap_err());
        }
        let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
        for caps.iter().advance |cap| {
            let res = expand(cap.as_bytes(), [], vars);
            assert!(res.is_err(),
                    "Binop %s succeeded incorrectly with 0 stack entries", *cap);
            let res = expand((bytes!("%{1}")).to_owned() + cap.as_bytes(), [], vars);
            assert!(res.is_err(),
                    "Binop %s succeeded incorrectly with 1 stack entry", *cap);
            let res = expand((bytes!("%{1}%{2}")).to_owned() + cap.as_bytes(), [], vars);
            assert!(res.is_ok(),
                    "Binop %s failed with 2 stack entries: %s", *cap, res.unwrap_err());
        }
    }

    #[test]
    fn test_push_bad_param() {
        assert!(expand(bytes!("%pa"), [], &mut Variables::new()).is_err());
    }

    #[test]
    fn test_comparison_ops() {
        let v = [('<', [1u8, 0u8, 0u8]), ('=', [0u8, 1u8, 0u8]), ('>', [0u8, 0u8, 1u8])];
        for v.iter().advance |&(op, bs)| {
            let s = fmt!("%%{1}%%{2}%%%c%%d", op);
            let res = expand(s.as_bytes(), [], &mut Variables::new());
            assert!(res.is_ok(), res.unwrap_err());
            assert_eq!(res.unwrap(), ~['0' as u8 + bs[0]]);
            let s = fmt!("%%{1}%%{1}%%%c%%d", op);
            let res = expand(s.as_bytes(), [], &mut Variables::new());
            assert!(res.is_ok(), res.unwrap_err());
            assert_eq!(res.unwrap(), ~['0' as u8 + bs[1]]);
            let s = fmt!("%%{2}%%{1}%%%c%%d", op);
            let res = expand(s.as_bytes(), [], &mut Variables::new());
            assert!(res.is_ok(), res.unwrap_err());
            assert_eq!(res.unwrap(), ~['0' as u8 + bs[2]]);
        }
    }

    #[test]
    fn test_conditionals() {
        let mut vars = Variables::new();
        let s = bytes!("\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m");
        let res = expand(s, [Number(1)], &mut vars);
        assert!(res.is_ok(), res.unwrap_err());
        assert_eq!(res.unwrap(), bytes!("\\E[31m").to_owned());
        let res = expand(s, [Number(8)], &mut vars);
        assert!(res.is_ok(), res.unwrap_err());
        assert_eq!(res.unwrap(), bytes!("\\E[90m").to_owned());
        let res = expand(s, [Number(42)], &mut vars);
        assert!(res.is_ok(), res.unwrap_err());
        assert_eq!(res.unwrap(), bytes!("\\E[38;5;42m").to_owned());
    }
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;
extern crate time;
extern crate fruently;

#[cfg(test)]
mod tests {
    use test;
    use test::Bencher;
    use time;
    use fruently::fluent::Fluent;
    use std::collections::HashMap;

    #[bench]
    fn benchmark_json_forwardable(b: &mut Bencher) {
        use fruently::forwardable::JsonForwardable;

        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("json".to_string(), "fruently".to_string());
        b.iter(|| {
            let fruently = Fluent::new("0.0.0.0:24224", "test");
            let _ = fruently.post(&obj);
        });
    }

    #[bench]
    fn benchmark_msgpack_forwardable(b: &mut Bencher) {
        use fruently::forwardable::MsgpackForwardable;

        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("msgp".to_string(), "fruently".to_string());
        b.iter(|| {
            let fruently = Fluent::new("0.0.0.0:24224", "test");
            let _ = fruently.post(&obj);
        });
    }

    #[bench]
    fn benchmark_forwardable(b: &mut Bencher) {
        use fruently::forwardable::Forwardable;

        let mut obj = HashMap::new();
        let time = time::now().to_timespec().sec;
        b.iter(|| {
            let n = test::black_box(1000);
            let hmap = (0..n).fold(&mut obj, |mut acc, _| {
                {
                    acc.insert("fwd", "fruently".to_string());
                }
                acc
            });
            let entry = (time, &*hmap);
            let fruently = Fluent::new("0.0.0.0:24224", "test");
            let _ = fruently.post(vec![(entry)]);
        })
    }
}
<commit_msg>bench: Generate HashMap at once<commit_after>#![feature(test)]

extern crate test;
extern crate time;
extern crate fruently;

#[cfg(test)]
mod tests {
    use test;
    use test::Bencher;
    use time;
    use fruently::fluent::Fluent;
    use std::collections::HashMap;

    #[bench]
    fn benchmark_json_forwardable(b: &mut Bencher) {
        use fruently::forwardable::JsonForwardable;

        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("json".to_string(), "fruently".to_string());
        b.iter(|| {
            let fruently = Fluent::new("0.0.0.0:24224", "test");
            let _ = fruently.post(&obj);
        });
    }

    #[bench]
    fn benchmark_msgpack_forwardable(b: &mut Bencher) {
        use fruently::forwardable::MsgpackForwardable;

        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("msgp".to_string(), "fruently".to_string());
        b.iter(|| {
            let fruently = Fluent::new("0.0.0.0:24224", "test");
            let _ = fruently.post(&obj);
        });
    }

    #[bench]
    fn benchmark_forwardable(b: &mut Bencher) {
        use fruently::forwardable::Forwardable;

        let mut obj = HashMap::new();
        let mut hmap = HashMap::new();
        let time = time::now().to_timespec().sec;
        b.iter(|| {
            let n = test::black_box(1000);
            let thmap = (0..n).fold(&mut obj, |mut acc, _| {
                {
                    acc.insert("fwd", "fruently".to_string());
                }
                acc
            });
            hmap = thmap.clone();
        });
        let entry = (time, hmap);
        let fruently = Fluent::new("0.0.0.0:24224", "test");
        let _ = fruently.post(vec![(entry)]);
    }
}
<|endoftext|>


<commit_before>//! Metamethods for accesssing the registry values!

use super::super::super::registry;
use registry::RegistryAccess;
use super::super::super::lua::json_to_lua;

use hlua::any::AnyLuaValue;

use rustc_serialize::json::{Json, ToJson};
use std::ops::Deref;

pub fn index(table: AnyLuaValue, lua_key: AnyLuaValue)
             -> Result<AnyLuaValue, &'static str> {
    if let AnyLuaValue::LuaString(key) = lua_key {
        let maybe_json = registry::get_json(&key);
        if let Some(json_pair) = maybe_json {
            let (access, json) = json_pair;
            if access == RegistryAccess::Private {
                // TODO In lua_async: return nil
                Err("No value found for that key!")
            }
            else {
                Ok(convert_json(json.deref().to_json()))
            }
        }
        else {
            // Should be nil, nil doesn't seem to be included
            Err("No value found for that key!")
        }
    }
    else {
        Err("The registry can only be indexed with keys!")
    }
}

// Prevent lua from changing the registry?
pub fn new_index(table: AnyLuaValue, key: AnyLuaValue, val: AnyLuaValue)
                 -> Result<(), &'static str> {
    Err("You can't set the registry!")
}
<commit_msg>Fix the rebase conflicts<commit_after>//! Metamethods for accesssing the registry values!

use super::super::super::registry;
use registry::RegistryAccess;
use super::super::super::lua::json_to_lua;

use hlua::any::AnyLuaValue;

use rustc_serialize::json::{Json, ToJson};
use std::ops::Deref;

pub fn index(table: AnyLuaValue, lua_key: AnyLuaValue)
             -> Result<AnyLuaValue, &'static str> {
    if let AnyLuaValue::LuaString(key) = lua_key {
        let maybe_json = registry::get_json(&key);
        if let Some(json_pair) = maybe_json {
            let (access, json) = json_pair;
            if access == RegistryAccess::Private {
                // TODO In lua_async: return nil
                Err("No value found for that key!")
            }
            else {
                Ok(json_to_lua(json.deref().to_json()))
            }
        }
        else {
            // Should be nil, nil doesn't seem to be included
            Err("No value found for that key!")
        }
    }
    else {
        Err("The registry can only be indexed with keys!")
    }
}

// Prevent lua from changing the registry?
pub fn new_index(table: AnyLuaValue, key: AnyLuaValue, val: AnyLuaValue)
                 -> Result<(), &'static str> {
    Err("You can't set the registry!")
}
<|endoftext|>


<commit_before>//! This module is UNSTABLE, the reason is - recently added.

use std::convert::From;
use std::error;
use std::fmt;
use std::io::Write;

use super::super::value::{Float, Integer, ValueRef};
use super::FixedValueWriteError;
use super::ValueWriteError;
use super::WriteError;
use super::write_array_len;
use super::write_bin;
use super::write_bool;
use super::write_ext_meta;
use super::write_f32;
use super::write_f64;
use super::write_map_len;
use super::write_nil;
use super::write_sint;
use super::write_str;
use super::write_uint;

#[derive(Debug)]
pub struct Error(WriteError);

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error(ref err) => err.fmt(fmt),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &Error(ref err) => err.cause(),
        }
    }
}

impl From<FixedValueWriteError> for Error {
    fn from(err: FixedValueWriteError) -> Error {
        match err {
            FixedValueWriteError(err) => Error(err)
        }
    }
}

impl From<ValueWriteError> for Error {
    fn from(err: ValueWriteError) -> Error {
        match err {
            ValueWriteError::InvalidMarkerWrite(err) => Error(err),
            ValueWriteError::InvalidDataWrite(err) => Error(err)
        }
    }
}

/// Encodes and attempts to write the given non-owning ValueRef into the given Write.
///
/// # Errors
///
/// This function returns Error with an underlying I/O error if unable to properly write entire
/// value. Interruption errors are handled internally by silent operation restarting.
///
/// # Examples
/// ```
/// use rmp::ValueRef;
/// use rmp::encode::value_ref::write_value_ref;
///
/// let mut buf = Vec::new();
/// let val = ValueRef::String("le message");
///
/// write_value_ref(&mut buf, &val).unwrap();
/// assert_eq!(vec![0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65], buf);
/// ```
pub fn write_value_ref<W>(wr: &mut W, val: &ValueRef) -> Result<(), Error>
    where W: Write
{
    match val {
        &ValueRef::Nil => try!(write_nil(wr)),
        &ValueRef::Boolean(val) => try!(write_bool(wr, val)),
        &ValueRef::Integer(Integer::U64(val)) => {
            try!(write_uint(wr, val));
        }
        &ValueRef::Integer(Integer::I64(val)) => {
            try!(write_sint(wr, val));
        }
        // TODO: Replace with generic write_float(...).
        &ValueRef::Float(Float::F32(val)) => try!(write_f32(wr, val)),
        &ValueRef::Float(Float::F64(val)) => try!(write_f64(wr, val)),
        &ValueRef::String(val) => {
            try!(write_str(wr, val));
        }
        &ValueRef::Binary(val) => {
            try!(write_bin(wr, val));
        }
        &ValueRef::Array(ref val) => {
            let len = val.len() as u32;

            try!(write_array_len(wr, len));

            for item in val {
                try!(write_value_ref(wr, item));
            }
        }
        &ValueRef::Map(ref val) => {
            let len = val.len() as u32;

            try!(write_map_len(wr, len));

            for &(ref key, ref val) in val {
                try!(write_value_ref(wr, key));
                try!(write_value_ref(wr, val));
            }
        }
        &ValueRef::Ext(ty, data) => {
            try!(write_ext_meta(wr, data.len() as u32, ty));
            try!(wr.write_all(data).map_err(|err| ValueWriteError::InvalidDataWrite(WriteError(err))));
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use std::error;
    use std::io;
    use super::super::WriteError;
    use super::Error;

    #[test]
    fn display_trait() {
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        assert_eq!("err: error while writing MessagePack'ed value", format!("err: {}", err));
    }

    #[test]
    fn error_trait() {
        // Delegates to I/O Error.
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        fn test<E: error::Error>(err: E) {
            assert_eq!("error while writing MessagePack'ed value", err.description());
            assert!(err.cause().is_some());
            assert_eq!("unexpected EOF", err.cause().unwrap().description());
        }

        test(err);
    }
}
<commit_msg>Misc: planning.<commit_after>//! This module is UNSTABLE, the reason is - recently added.

use std::convert::From;
use std::error;
use std::fmt;
use std::io::Write;

// TODO: Includes cleanup is required.
use super::super::value::{Float, Integer, ValueRef};
use super::FixedValueWriteError;
use super::ValueWriteError;
use super::WriteError;
use super::write_array_len;
use super::write_bin;
use super::write_bool;
use super::write_ext_meta;
use super::write_f32;
use super::write_f64;
use super::write_map_len;
use super::write_nil;
use super::write_sint;
use super::write_str;
use super::write_uint;

#[derive(Debug)]
pub struct Error(WriteError);

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error(ref err) => err.fmt(fmt),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &Error(ref err) => err.cause(),
        }
    }
}

impl From<FixedValueWriteError> for Error {
    fn from(err: FixedValueWriteError) -> Error {
        match err {
            FixedValueWriteError(err) => Error(err)
        }
    }
}

impl From<ValueWriteError> for Error {
    fn from(err: ValueWriteError) -> Error {
        match err {
            ValueWriteError::InvalidMarkerWrite(err) => Error(err),
            ValueWriteError::InvalidDataWrite(err) => Error(err)
        }
    }
}

/// Encodes and attempts to write the given non-owning ValueRef into the given Write.
///
/// # Errors
///
/// This function returns Error with an underlying I/O error if unable to properly write entire
/// value. Interruption errors are handled internally by silent operation restarting.
///
/// # Examples
/// ```
/// use rmp::ValueRef;
/// use rmp::encode::value_ref::write_value_ref;
///
/// let mut buf = Vec::new();
/// let val = ValueRef::String("le message");
///
/// write_value_ref(&mut buf, &val).unwrap();
/// assert_eq!(vec![0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65], buf);
/// ```
pub fn write_value_ref<W>(wr: &mut W, val: &ValueRef) -> Result<(), Error>
    where W: Write
{
    match val {
        &ValueRef::Nil => try!(write_nil(wr)),
        &ValueRef::Boolean(val) => try!(write_bool(wr, val)),
        &ValueRef::Integer(Integer::U64(val)) => {
            try!(write_uint(wr, val));
        }
        &ValueRef::Integer(Integer::I64(val)) => {
            try!(write_sint(wr, val));
        }
        // TODO: Replace with generic write_float(...).
        &ValueRef::Float(Float::F32(val)) => try!(write_f32(wr, val)),
        &ValueRef::Float(Float::F64(val)) => try!(write_f64(wr, val)),
        &ValueRef::String(val) => {
            try!(write_str(wr, val));
        }
        &ValueRef::Binary(val) => {
            try!(write_bin(wr, val));
        }
        &ValueRef::Array(ref val) => {
            let len = val.len() as u32;

            try!(write_array_len(wr, len));

            for item in val {
                try!(write_value_ref(wr, item));
            }
        }
        &ValueRef::Map(ref val) => {
            let len = val.len() as u32;

            try!(write_map_len(wr, len));

            for &(ref key, ref val) in val {
                try!(write_value_ref(wr, key));
                try!(write_value_ref(wr, val));
            }
        }
        &ValueRef::Ext(ty, data) => {
            try!(write_ext_meta(wr, data.len() as u32, ty));
            try!(wr.write_all(data).map_err(|err| ValueWriteError::InvalidDataWrite(WriteError(err))));
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use std::error;
    use std::io;
    use super::super::WriteError;
    use super::Error;

    #[test]
    fn display_trait() {
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        assert_eq!("err: error while writing MessagePack'ed value", format!("err: {}", err));
    }

    #[test]
    fn error_trait() {
        // Delegates to I/O Error.
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        fn test<E: error::Error>(err: E) {
            assert_eq!("error while writing MessagePack'ed value", err.description());
            assert!(err.cause().is_some());
            assert_eq!("unexpected EOF", err.cause().unwrap().description());
        }

        test(err);
    }
}
<|endoftext|>