<commit_before>extern crate gl;
extern crate sdl2;
extern crate yaglw;

use gl::types::*;
use sdl2::EventPump;
use sdl2::event::Event;
use std::mem;

use yaglw::framebuffer::Framebuffer;
use yaglw::gl_context::GLContext;
use yaglw::shader::Shader;
use yaglw::texture::Texture2D;
use yaglw::vertex_buffer::{ArrayHandle, GLArray, GLBuffer, GLType, VertexAttribData, DrawMode};

const WINDOW_WIDTH: i32 = 800;
const WINDOW_HEIGHT: i32 = 600;

#[repr(C)]
#[derive(Copy, Clone)]
struct Vertex {
  pub position: [GLfloat; 2],
  pub color: [GLfloat; 3],
}

const VERTEX_SHADER: &'static str = "
  #version 330

  in vec2 position;
  in vec3 color;

  out vec3 v_color;

  void main() {
    v_color = color;
    gl_Position = vec4(position, 0, 1);
  }
";

const FRAGMENT_SHADER: &'static str = "
  #version 330

  in vec3 v_color;

  layout (location = 0) out vec4 frag_color;

  void main() {
    frag_color = vec4(v_color, 1.0);
  }
";

const DEFERRED_VERTEX_SHADER: &'static str = "
  #version 330

  void main() {
    if (gl_VertexID == 0) {
      gl_Position = vec4(1, -1, 0, 1);
    } else if (gl_VertexID == 1) {
      gl_Position = vec4(1, 1, 0, 1);
    } else if (gl_VertexID == 2) {
      gl_Position = vec4(-1, -1, 0, 1);
    } else if (gl_VertexID == 3) {
      gl_Position = vec4(-1, 1, 0, 1);
    } else {
      gl_Position = vec4(0, 0, 0, 1);
    }
  }
";

const DEFERRED_FRAGMENT_SHADER: &'static str = "
  #version 330

  uniform sampler2D colors;

  layout (location = 0) out vec4 frag_color;

  void main() {
    vec2 tex_pos = vec2(gl_FragCoord.x / 800, gl_FragCoord.y / 600);
    vec4 color = texture(colors, tex_pos);
    frag_color = color;
  }
";

pub fn main() {
  let sdl = sdl2::init().unwrap();

  let _event = sdl.event().unwrap();
  let video = sdl.video().unwrap();

  let window = make_window(&video);
  let mut event_pump = sdl.event_pump().unwrap();

  let _sdl_gl_context = window.gl_create_context().unwrap();

  // Load the OpenGL function pointers.
  gl::load_with(|s| unsafe {
    mem::transmute(video.gl_get_proc_address(s))
  });

  let mut gl = unsafe {
    GLContext::new()
  };

  let vertices = [
    Vertex {
      position: [0.0, 0.5],
      color: [1.0, 0.0, 0.0],
    },
    Vertex {
      position: [-0.5, -0.5],
      color: [0.0, 1.0, 0.0],
    },
    Vertex {
      position: [0.5, -0.5],
      color: [0.0, 0.0, 1.0],
    },
  ];

  let mut vbo = GLBuffer::new(&mut gl, 3);
  vbo.push(&mut gl, &vertices);

  let attribs = [
    VertexAttribData {
      name: "position",
      size: 2,
      unit: GLType::Float,
    },
    VertexAttribData {
      name: "color",
      size: 3,
      unit: GLType::Float,
    },
  ];

  let components = [
    ((gl::VERTEX_SHADER, VERTEX_SHADER)),
    ((gl::FRAGMENT_SHADER, FRAGMENT_SHADER)),
  ];

  let shader = Shader::new(&gl, components.iter().map(|&(ty, s)| (ty, String::from(s))));
  shader.use_shader(&mut gl);

  let components = [
    ((gl::VERTEX_SHADER, DEFERRED_VERTEX_SHADER)),
    ((gl::FRAGMENT_SHADER, DEFERRED_FRAGMENT_SHADER)),
  ];

  let mut deferred_shader = Shader::new(&gl, components.iter().map(|&(ty, s)| (ty, String::from(s))));
  deferred_shader.use_shader(&mut gl);

  let vao =
    GLArray::new(
      &mut gl,
      &shader,
      &attribs,
      DrawMode::Triangles,
      vbo,
    );
  vao.bind(&mut gl);

  let empty_vao = ArrayHandle::new(&gl);

  match gl.get_error() {
    gl::NO_ERROR => {},
    err => {
      println!("OpenGL error 0x{:x} in setup 1", err);
      return;
    },
  }

  let mut fbo = Framebuffer::new(&gl);
  let colors = Texture2D::new(&gl);

  unsafe {
    gl::ActiveTexture(gl::TEXTURE0);
    gl::BindTexture(gl::TEXTURE_2D, colors.handle.gl_id);
    gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA32F as i32, WINDOW_WIDTH, WINDOW_HEIGHT, 0, gl::RGB, gl::FLOAT, std::ptr::null());

    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
  }

  fbo.bind(&mut gl);
  fbo.attach_2d(&gl, gl::COLOR_ATTACHMENT0, &colors);

  match gl.get_error() {
    gl::NO_ERROR => {},
    err => {
      println!("OpenGL error 0x{:x} in setup 2", err);
      return;
    },
  }

  let color_uniform = deferred_shader.get_uniform_location("colors");
  deferred_shader.use_shader(&mut gl);
  unsafe {
    gl::Uniform1i(color_uniform, 0);
  }

  match gl.get_error() {
    gl::NO_ERROR => {},
    err => {
      println!("OpenGL error 0x{:x} in setup", err);
      return;
    },
  }

  while !quit_event(&mut event_pump) {
    fbo.bind(&mut gl);
    shader.use_shader(&mut gl);

    gl.clear_buffer();
    vao.bind(&mut gl);
    vao.draw(&mut gl);

    unsafe {
      gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);
      gl::BindTexture(gl::TEXTURE_2D, colors.handle.gl_id);
    }

    deferred_shader.use_shader(&mut gl);

    unsafe {
      gl::BindVertexArray(empty_vao.gl_id);
      gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
    }

    // swap buffers
    window.gl_swap_window();

    std::thread::sleep_ms(10);
  }
}

fn make_window(video: &sdl2::VideoSubsystem) -> sdl2::video::Window {
  let gl_attr = video.gl_attr();
  gl_attr.set_context_profile(sdl2::video::GLProfile::Core);
  gl_attr.set_context_version(3, 3);

  // Open the window as fullscreen at the current resolution.
  let mut window =
    video.window(
      "Triangle",
      WINDOW_WIDTH as u32, WINDOW_HEIGHT as u32,
    );

  let window = window.position(0, 0);
  window.opengl();

  window.build().unwrap()
}

fn quit_event(event_pump: &mut EventPump) -> bool {
  loop {
    match event_pump.poll_event() {
      None => {
        return false;
      },
      Some(Event::Quit{..}) => {
        return true;
      }
      Some(Event::AppTerminating{..}) => {
        return true;
      }
      _ => {},
    }
  }
}
<commit_msg>fix build?<commit_after>extern crate gl;
extern crate sdl2;
extern crate yaglw;

use gl::types::*;
use sdl2::EventPump;
use sdl2::event::Event;
use std::mem;

use yaglw::framebuffer::Framebuffer;
use yaglw::gl_context::GLContext;
use yaglw::shader::Shader;
use yaglw::texture::Texture2D;
use yaglw::vertex_buffer::{ArrayHandle, GLArray, GLBuffer, GLType, VertexAttribData, DrawMode};

const WINDOW_WIDTH: i32 = 800;
const WINDOW_HEIGHT: i32 = 600;

#[repr(C)]
#[derive(Copy, Clone)]
struct Vertex {
  pub position: [GLfloat; 2],
  pub color: [GLfloat; 3],
}

const VERTEX_SHADER: &'static str = "
  #version 330

  in vec2 position;
  in vec3 color;

  out vec3 v_color;

  void main() {
    v_color = color;
    gl_Position = vec4(position, 0, 1);
  }
";

const FRAGMENT_SHADER: &'static str = "
  #version 330

  in vec3 v_color;

  layout (location = 0) out vec4 frag_color;

  void main() {
    frag_color = vec4(v_color, 1.0);
  }
";

const DEFERRED_VERTEX_SHADER: &'static str = "
  #version 330

  void main() {
    if (gl_VertexID == 0) {
      gl_Position = vec4(1, -1, 0, 1);
    } else if (gl_VertexID == 1) {
      gl_Position = vec4(1, 1, 0, 1);
    } else if (gl_VertexID == 2) {
      gl_Position = vec4(-1, -1, 0, 1);
    } else if (gl_VertexID == 3) {
      gl_Position = vec4(-1, 1, 0, 1);
    } else {
      gl_Position = vec4(0, 0, 0, 1);
    }
  }
";

const DEFERRED_FRAGMENT_SHADER: &'static str = "
  #version 330

  uniform sampler2D colors;

  layout (location = 0) out vec4 frag_color;

  void main() {
    vec2 tex_pos = vec2(gl_FragCoord.x / 800, gl_FragCoord.y / 600);
    vec4 color = texture(colors, tex_pos);
    frag_color = color;
  }
";

pub fn main() {
  let sdl = sdl2::init().unwrap();

  let _event = sdl.event().unwrap();
  let video = sdl.video().unwrap();

  let window = make_window(&video);
  let mut event_pump = sdl.event_pump().unwrap();

  let _sdl_gl_context = window.gl_create_context().unwrap();

  // Load the OpenGL function pointers.
  gl::load_with(|s| unsafe {
    mem::transmute(video.gl_get_proc_address(s))
  });

  let mut gl = unsafe {
    GLContext::new()
  };

  let vertices = [
    Vertex {
      position: [0.0, 0.5],
      color: [1.0, 0.0, 0.0],
    },
    Vertex {
      position: [-0.5, -0.5],
      color: [0.0, 1.0, 0.0],
    },
    Vertex {
      position: [0.5, -0.5],
      color: [0.0, 0.0, 1.0],
    },
  ];

  let mut vbo = GLBuffer::new(&mut gl, 3);
  vbo.push(&mut gl, &vertices);

  let attribs = [
    VertexAttribData {
      name: "position",
      size: 2,
      unit: GLType::Float,
      divisor: 0,
    },
    VertexAttribData {
      name: "color",
      size: 3,
      unit: GLType::Float,
      divisor: 0,
    },
  ];

  let components = [
    ((gl::VERTEX_SHADER, VERTEX_SHADER)),
    ((gl::FRAGMENT_SHADER, FRAGMENT_SHADER)),
  ];

  let shader = Shader::new(&gl, components.iter().map(|&(ty, s)| (ty, String::from(s))));
  shader.use_shader(&mut gl);

  let components = [
    ((gl::VERTEX_SHADER, DEFERRED_VERTEX_SHADER)),
    ((gl::FRAGMENT_SHADER, DEFERRED_FRAGMENT_SHADER)),
  ];

  let mut deferred_shader = Shader::new(&gl, components.iter().map(|&(ty, s)| (ty, String::from(s))));
  deferred_shader.use_shader(&mut gl);

  let vao =
    GLArray::new(
      &mut gl,
      &shader,
      &attribs,
      DrawMode::Triangles,
      vbo,
    );
  vao.bind(&mut gl);

  let empty_vao = ArrayHandle::new(&gl);

  match gl.get_error() {
    gl::NO_ERROR => {},
    err => {
      println!("OpenGL error 0x{:x} in setup 1", err);
      return;
    },
  }

  let mut fbo = Framebuffer::new(&gl);
  let colors = Texture2D::new(&gl);

  unsafe {
    gl::ActiveTexture(gl::TEXTURE0);
    gl::BindTexture(gl::TEXTURE_2D, colors.handle.gl_id);
    gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA32F as i32, WINDOW_WIDTH, WINDOW_HEIGHT, 0, gl::RGB, gl::FLOAT, std::ptr::null());

    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
  }

  fbo.bind(&mut gl);
  fbo.attach_2d(&gl, gl::COLOR_ATTACHMENT0, &colors);

  match gl.get_error() {
    gl::NO_ERROR => {},
    err => {
      println!("OpenGL error 0x{:x} in setup 2", err);
      return;
    },
  }

  let color_uniform = deferred_shader.get_uniform_location("colors");
  deferred_shader.use_shader(&mut gl);
  unsafe {
    gl::Uniform1i(color_uniform, 0);
  }

  match gl.get_error() {
    gl::NO_ERROR => {},
    err => {
      println!("OpenGL error 0x{:x} in setup", err);
      return;
    },
  }

  while !quit_event(&mut event_pump) {
    fbo.bind(&mut gl);
    shader.use_shader(&mut gl);

    gl.clear_buffer();
    vao.bind(&mut gl);
    vao.draw(&mut gl);

    unsafe {
      gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);
      gl::BindTexture(gl::TEXTURE_2D, colors.handle.gl_id);
    }

    deferred_shader.use_shader(&mut gl);

    unsafe {
      gl::BindVertexArray(empty_vao.gl_id);
      gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
    }

    // swap buffers
    window.gl_swap_window();

    std::thread::sleep_ms(10);
  }
}

fn make_window(video: &sdl2::VideoSubsystem) -> sdl2::video::Window {
  let gl_attr = video.gl_attr();
  gl_attr.set_context_profile(sdl2::video::GLProfile::Core);
  gl_attr.set_context_version(3, 3);

  // Open the window as fullscreen at the current resolution.
  let mut window =
    video.window(
      "Triangle",
      WINDOW_WIDTH as u32, WINDOW_HEIGHT as u32,
    );

  let window = window.position(0, 0);
  window.opengl();

  window.build().unwrap()
}

fn quit_event(event_pump: &mut EventPump) -> bool {
  loop {
    match event_pump.poll_event() {
      None => {
        return false;
      },
      Some(Event::Quit{..}) => {
        return true;
      }
      Some(Event::AppTerminating{..}) => {
        return true;
      }
      _ => {},
    }
  }
}
<|endoftext|>


<commit_before>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

extern crate wayland_client;
use wayland_client::client::{FromPrimitive, Display,
                             Registry, RegistryEventHandler,
                             Seat, SeatEventHandler, SeatCapability,
                             Shm, ShmEventHandler, ShmFormat,
                             Output, OutputEventHandler, OutputSubpixel,
                             OutputTransform, OutputMode};

use std::collections::HashMap;

#[derive(Default)]
struct SeatData {
    name: String,
    capabilities: Vec<String>,
}

#[derive(Default)]
struct ShmData {
    formats: Vec<String>,
}

#[derive(Default)]
struct OutputData {
    x: i32,
    y: i32,
    physical_width: i32,
    physical_height: i32,
    subpixel: String,
    make: String,
    model: String,
    transform: String,
    modes: Vec<OutputModeData>,
}

struct OutputModeData {
    width: i32,
    height: i32,
    refresh: i32,
    flags: String,
}

struct Info {
    registry: Registry,
    seat: Option<Seat>,
    shm: Option<Shm>,
    output: Option<Output>,
    pub roundtrip: bool,
    data: HashMap<String, String>,
    seat_data: SeatData,
    shm_data: ShmData,
    output_data: OutputData,
}

impl Info {
    fn new(registry: Registry) -> Info {
        return Info {
            registry: registry,
            seat: None,
            shm: None,
            output: None,
            roundtrip: true,
            data: HashMap::new(),
            seat_data: Default::default(),
            shm_data: Default::default(),
            output_data: Default::default(),
        };
    }
}

impl RegistryEventHandler for Info {
    fn get_registry(&mut self) -> &mut Registry {
        return &mut self.registry;
    }

    fn on_global(&mut self, name: u32, interface: String, version: u32) {
        self.data.insert(interface.clone(),
                         format!("interface: '{}', version: {}, name: {}",
                                 interface, version, name));

        if interface == "wl_seat" {
            self.roundtrip = true;
            self.seat = self.registry.bind(name, version).ok();
            SeatEventHandler::connect_dispatcher(self);
        } else if interface == "wl_shm" {
            self.roundtrip = true;
            self.shm = self.registry.bind(name, version).ok();
            ShmEventHandler::connect_dispatcher(self);
        } else if interface == "wl_output" {
            self.roundtrip = true;
            self.output = self.registry.bind(name, version).ok();
            OutputEventHandler::connect_dispatcher(self);
        }
    }
}

impl SeatEventHandler for Info {
    fn get_seat(&mut self) -> &mut Seat {
        return self.seat.as_mut().unwrap();
    }

    fn on_capabilities(&mut self, capabilities: u32) {
        if capabilities & (SeatCapability::Pointer as u32) != 0 {
            self.seat_data.capabilities.push("pointer".to_string());
        }
        if capabilities & (SeatCapability::Keyboard as u32) != 0 {
            self.seat_data.capabilities.push("keyboard".to_string());
        }
        if capabilities & (SeatCapability::Touch as u32) != 0 {
            self.seat_data.capabilities.push("touch".to_string());
        }
    }

    fn on_name(&mut self, name: String) {
        self.seat_data.name = name;
    }
}

impl ShmEventHandler for Info {
    fn get_shm(&mut self) -> &mut Shm {
        return self.shm.as_mut().unwrap();
    }

    fn on_format(&mut self, format: u32) {
        println!("on_format({:?})", ShmFormat::from_u32(format).unwrap());
    }
}

impl OutputEventHandler for Info {
    fn get_output(&mut self) -> &mut Output {
        return self.output.as_mut().unwrap();
    }

    fn on_geometry(&mut self, x: i32, y: i32, physical_width: i32,
                   physical_height: i32, subpixel: i32, make: String, model:
                   String, transform: i32) {
        println!("on_geometry({}, {}, {}, {}, {:?}, {}, {}, {:?})", x, y,
            physical_width, physical_height,
            OutputSubpixel::from_u32(subpixel as u32).unwrap(), make, model,
            OutputTransform::from_u32(transform as u32).unwrap());
    }

    fn on_mode(&mut self, flags: u32, width: i32, height: i32, refresh: i32) {
        println!("on_mode({}, {}, {}, {})", flags, width, height, refresh);
    }

    fn on_done(&mut self) {
        println!("on_done()");
    }

    fn on_scale(&mut self, factor: i32) {
        println!("on_scale({})", factor);
    }
}

fn main() {
    let mut display = Display::connect(None).unwrap();
    let mut info = Info::new(display.get_registry().unwrap());
    RegistryEventHandler::connect_dispatcher(&mut info);

    while info.roundtrip {
        info.roundtrip = false;
        display.roundtrip();
    }
}
<commit_msg>weston-info: gather shm data<commit_after>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

extern crate wayland_client;
use wayland_client::client::{FromPrimitive, Display,
                             Registry, RegistryEventHandler,
                             Seat, SeatEventHandler, SeatCapability,
                             Shm, ShmEventHandler, ShmFormat,
                             Output, OutputEventHandler, OutputSubpixel,
                             OutputTransform, OutputMode};

use std::collections::HashMap;

#[derive(Default)]
struct SeatData {
    name: String,
    capabilities: Vec<String>,
}

#[derive(Default)]
struct ShmData {
    formats: Vec<String>,
}

#[derive(Default)]
struct OutputData {
    x: i32,
    y: i32,
    physical_width: i32,
    physical_height: i32,
    subpixel: String,
    make: String,
    model: String,
    transform: String,
    modes: Vec<OutputModeData>,
}

struct OutputModeData {
    width: i32,
    height: i32,
    refresh: i32,
    flags: String,
}

struct Info {
    registry: Registry,
    seat: Option<Seat>,
    shm: Option<Shm>,
    output: Option<Output>,
    pub roundtrip: bool,
    data: HashMap<String, String>,
    seat_data: SeatData,
    shm_data: ShmData,
    output_data: OutputData,
}

impl Info {
    fn new(registry: Registry) -> Info {
        return Info {
            registry: registry,
            seat: None,
            shm: None,
            output: None,
            roundtrip: true,
            data: HashMap::new(),
            seat_data: Default::default(),
            shm_data: Default::default(),
            output_data: Default::default(),
        };
    }
}

impl RegistryEventHandler for Info {
    fn get_registry(&mut self) -> &mut Registry {
        return &mut self.registry;
    }

    fn on_global(&mut self, name: u32, interface: String, version: u32) {
        self.data.insert(interface.clone(),
                         format!("interface: '{}', version: {}, name: {}",
                                 interface, version, name));

        if interface == "wl_seat" {
            self.roundtrip = true;
            self.seat = self.registry.bind(name, version).ok();
            SeatEventHandler::connect_dispatcher(self);
        } else if interface == "wl_shm" {
            self.roundtrip = true;
            self.shm = self.registry.bind(name, version).ok();
            ShmEventHandler::connect_dispatcher(self);
        } else if interface == "wl_output" {
            self.roundtrip = true;
            self.output = self.registry.bind(name, version).ok();
            OutputEventHandler::connect_dispatcher(self);
        }
    }
}

impl SeatEventHandler for Info {
    fn get_seat(&mut self) -> &mut Seat {
        return self.seat.as_mut().unwrap();
    }

    fn on_capabilities(&mut self, capabilities: u32) {
        if capabilities & (SeatCapability::Pointer as u32) != 0 {
            self.seat_data.capabilities.push("pointer".to_string());
        }
        if capabilities & (SeatCapability::Keyboard as u32) != 0 {
            self.seat_data.capabilities.push("keyboard".to_string());
        }
        if capabilities & (SeatCapability::Touch as u32) != 0 {
            self.seat_data.capabilities.push("touch".to_string());
        }
    }

    fn on_name(&mut self, name: String) {
        self.seat_data.name = name;
    }
}

impl ShmEventHandler for Info {
    fn get_shm(&mut self) -> &mut Shm {
        return self.shm.as_mut().unwrap();
    }

    fn on_format(&mut self, format: u32) {
        self.shm_data.formats.push(format!("{:?}", ShmFormat::from_u32(format).unwrap()));
    }
}

impl OutputEventHandler for Info {
    fn get_output(&mut self) -> &mut Output {
        return self.output.as_mut().unwrap();
    }

    fn on_geometry(&mut self, x: i32, y: i32, physical_width: i32,
                   physical_height: i32, subpixel: i32, make: String, model:
                   String, transform: i32) {
        println!("on_geometry({}, {}, {}, {}, {:?}, {}, {}, {:?})", x, y,
            physical_width, physical_height,
            OutputSubpixel::from_u32(subpixel as u32).unwrap(), make, model,
            OutputTransform::from_u32(transform as u32).unwrap());
    }

    fn on_mode(&mut self, flags: u32, width: i32, height: i32, refresh: i32) {
        println!("on_mode({}, {}, {}, {})", flags, width, height, refresh);
    }

    fn on_done(&mut self) {
        println!("on_done()");
    }

    fn on_scale(&mut self, factor: i32) {
        println!("on_scale({})", factor);
    }
}

fn main() {
    let mut display = Display::connect(None).unwrap();
    let mut info = Info::new(display.get_registry().unwrap());
    RegistryEventHandler::connect_dispatcher(&mut info);

    while info.roundtrip {
        info.roundtrip = false;
        display.roundtrip();
    }
}
<|endoftext|>


<commit_before>extern crate tabwriter;

use std::io::Write;
use self::tabwriter::TabWriter;

use super::tree::FSNode;

pub fn print_tree(tree: &FSNode) {
    let mut tw = TabWriter::new(Vec::new());
    let prefix = "".to_string();

    print_tree_impl(&tree, &mut tw, &prefix);

    tw.flush().unwrap();
    let tabulated = String::from_utf8(tw.unwrap()).unwrap();
    print!("{}", &tabulated);
}

pub fn print_tree_impl<T: Write>(node: &FSNode, mut tw: &mut TabWriter<T>, prefix: &String) {

    let sum_suffix = if node.is_dir() {
        "(Σ)"
    } else {
        ""
    };

    writeln!(&mut tw,
             "{}\t{}\t{}",
             node.name(),
             human_readable_byte_size(node.size()),
             &sum_suffix,
             )
        .unwrap();

    for (idx, item) in node.children().enumerate() {
        let last = idx == (node.children().count() - 1);
        let (branch, nested) = if last {
            ("└", "    ")
        } else {
            ("├", "│   ")
        };

        write!(&mut tw, "{}{}── ", &prefix, &branch).unwrap();

        let new_prefix = concat(prefix, nested);
        print_tree_impl(&item, &mut tw, &new_prefix);
    }
}

fn concat(a: &str, b: &str) -> String {
    let mut result = a.to_string();
    result.push_str(b);
    return result;
}

fn human_readable_byte_size(bytes: u64) -> String {
    if bytes < 1024 {
        return format!("{}\tB", bytes);
    }

    let metric_prefix = ["B", "KB", "MB", "GB", "TB", "PB", "EB"];

    let which = log2(bytes) / 10;
    let decimal = bytes as f64 / 1024.0_f64.powf(which as f64);
    return format!("{:.1}\t{}", &decimal, &metric_prefix[which as usize]);
}

fn log2(mut x: u64) -> u64 {
    let mut n: u64 = 0;

    while (x >> 1) > 0 {
        x >>= 1;
        n += 1;
    }
    return n;
}
<commit_msg>remove unnecessary pub<commit_after>extern crate tabwriter;

use std::io::Write;
use self::tabwriter::TabWriter;

use super::tree::FSNode;

pub fn print_tree(tree: &FSNode) {
    let mut tw = TabWriter::new(Vec::new());
    let prefix = "".to_string();

    print_tree_impl(&tree, &mut tw, &prefix);

    tw.flush().unwrap();
    let tabulated = String::from_utf8(tw.unwrap()).unwrap();
    print!("{}", &tabulated);
}

fn print_tree_impl<T: Write>(node: &FSNode, mut tw: &mut TabWriter<T>, prefix: &String) {

    let sum_suffix = if node.is_dir() {
        "(Σ)"
    } else {
        ""
    };

    writeln!(&mut tw,
             "{}\t{}\t{}",
             node.name(),
             human_readable_byte_size(node.size()),
             &sum_suffix,
             )
        .unwrap();

    for (idx, item) in node.children().enumerate() {
        let last = idx == (node.children().count() - 1);
        let (branch, nested) = if last {
            ("└", "    ")
        } else {
            ("├", "│   ")
        };

        write!(&mut tw, "{}{}── ", &prefix, &branch).unwrap();

        let new_prefix = concat(prefix, nested);
        print_tree_impl(&item, &mut tw, &new_prefix);
    }
}

fn concat(a: &str, b: &str) -> String {
    let mut result = a.to_string();
    result.push_str(b);
    return result;
}

fn human_readable_byte_size(bytes: u64) -> String {
    if bytes < 1024 {
        return format!("{}\tB", bytes);
    }

    let metric_prefix = ["B", "KB", "MB", "GB", "TB", "PB", "EB"];

    let which = log2(bytes) / 10;
    let decimal = bytes as f64 / 1024.0_f64.powf(which as f64);
    return format!("{:.1}\t{}", &decimal, &metric_prefix[which as usize]);
}

fn log2(mut x: u64) -> u64 {
    let mut n: u64 = 0;

    while (x >> 1) > 0 {
        x >>= 1;
        n += 1;
    }
    return n;
}
<|endoftext|>


<commit_before>#![feature(unboxed_closures)]
#![feature(phase)]
#![feature(globs)]
#![feature(macro_rules)]
#![feature(default_type_params)]
#[phase(plugin, link)]
extern crate log;
extern crate serialize;

pub mod config;
pub mod core;
pub mod handlers;
pub mod layout;
pub mod logger;
pub mod util;
pub mod window_manager;
pub mod window_system;
<commit_msg>allow deprecated orphan check to make Hash work again<commit_after>#![feature(unboxed_closures)]
#![feature(phase)]
#![feature(globs)]
#![feature(macro_rules)]
#![feature(default_type_params)]
#![feature(old_orphan_check)]
#[phase(plugin, link)]
extern crate log;
extern crate serialize;

pub mod config;
pub mod core;
pub mod handlers;
pub mod layout;
pub mod logger;
pub mod util;
pub mod window_manager;
pub mod window_system;
<|endoftext|>


<commit_before>
use byteorder::{ByteOrder, LittleEndian};

use std;

use gameboy;
use gameboy::{Interconnect, Interrupt, Irq, Memory};
use gameboy::gfx::{Color, Frame, GpuMode, GpuStat, SpriteShape, TileRenderOptions, TileRenderType};

pub struct Gpu {
    pub enabled: bool,
    pub ram: Memory,
    pub sprite_data: Memory,

    pub ly: u8,
    pub lyc: u8,

    pub frame: Frame,
    pub backbuffer: Frame,
    pub mode: GpuMode,

    control_register: u8,
    stat: GpuStat,
    scroll_y: u8,
    scroll_x: u8,
    window_y: u8,
    window_x: u8,
    bg_palette: u8,
    palette0: u8,
    palette1: u8,

    cycles: isize,

    counter: u8,
    tile_data_addr: usize,
    background_tilemap_addr: usize,
    window_tilemap_addr: usize,
    sprite_shape: SpriteShape,
    sprites_enabled: bool,
    background_enabled: bool,
    window_enabled: bool,
}

impl Gpu {
    pub fn new() -> Gpu {
        Gpu {
            enabled: true,
            ram: Memory::new(gameboy::gfx::VRAM_SIZE),
            sprite_data: Memory::new(gameboy::gfx::SPRITE_DATA_SIZE),
            control_register: 0x00,
            stat: GpuStat::new(),
            scroll_y: 0x00,
            scroll_x: 0x00,
            window_y: 0x00,
            window_x: 0x00,
            ly: 0x00,
            lyc: 0x00,
            bg_palette: 0x00,
            palette0: 0x00,
            palette1: 0x00,
            cycles: 0x00,
            frame: Frame::new(),
            backbuffer: Frame::new(),
            mode: GpuMode::HBlank,
            counter: 0,
            tile_data_addr: 0x00,
            background_tilemap_addr: 0xC00,
            window_tilemap_addr: 0x00,
            sprite_shape: SpriteShape::Square,
            sprites_enabled: true,
            background_enabled: true,
            window_enabled: true,
        }
    }

    pub fn reset(&mut self) {
        *self = Gpu::new();
    }

    fn set_status(&mut self, irq: &mut Irq) {
        if !self.enabled {
            self.cycles = 0x1C8;
            self.ly = 0x00;
            self.stat = GpuStat::from(0xFD);
        }

        let current_mode = self.mode.clone();

        if self.ly >= 0x90 {
            self.mode = GpuMode::VBlank;
        } else {
            if self.cycles >= 0x178 {
                self.mode = GpuMode::SearchingRam;
            } else if self.cycles >= 0xCC {
                self.mode = GpuMode::TransferringData;
            } else {
                self.mode = GpuMode::HBlank;
            }
        }

        if self.mode != current_mode {
            irq.request(Interrupt::Lcd);
        }

        self.check_coincidence(irq);
    }

    pub fn step(&mut self, irq: &mut Irq, cycles: usize) -> Result<(), String> {
        self.set_status(irq);

        let cycles = cycles as isize;
        if self.enabled {
            self.cycles -= cycles;
        } else {
            return Ok(());
        }

        if self.cycles <= 0 {
            self.ly += 0x01;

            self.cycles = 0x1C8;

            if self.ly == 0x90 {
                irq.request(Interrupt::Vblank);
            } else if self.ly > 0x99 {
                self.ly = 0x00;
            } else if self.ly < 0x90 {
                self.render_scanline();
            }
        }

        Ok(())
    }

    fn render_scanline(&mut self) {
        self.render_background();
        self.render_window();
        self.render_sprites();
        self.frame = self.backbuffer.clone();
    }

    fn render_background(&mut self) {
        requires!(self.background_enabled);

        let options = TileRenderOptions::new(TileRenderType::Background,
                                             self.ly,
                                             self.background_tilemap_addr,
                                             self.tile_data_addr);
        self.render_tile(&options);
    }

    fn render_window(&mut self) {
        requires!(self.window_enabled);
        requires!(self.ly as usize >= self.window_y as usize);

        let options = TileRenderOptions::new(TileRenderType::Window,
                                             self.ly,
                                             self.window_tilemap_addr,
                                             self.tile_data_addr);
        self.render_tile(&options);
    }

    fn render_tile(&mut self, options: &TileRenderOptions) {
        let window = variant_equals!(TileRenderType::Window, options.render_type);
        let y = options.line.wrapping_add(self.scroll_y) as usize;

        // If rendering a window tile, we need to make sure we offset tile line properly
        let window_offset = if window { self.window_y as usize } else { 0x00 };
        let map_row = ((y - window_offset) / 0x08) as usize;

        for i in 0..gameboy::SCREEN_WIDTH {
            // if nowhere near the window, skip
            if window && i < self.window_x as usize {
                continue;
            }

            // If we're at the window, lets negate the window X position from where we
            // need to be in the tile map
            let x = if window {
                i as u8 - self.window_x
            } else {
                // Otherwise, scroll the background
                (i as u8).wrapping_add(self.scroll_x)
            };
            let map_col = (x / 0x08) as usize;
            let raw_tile_number = self.ram[options.map_addr + (map_row * 0x20 + map_col)] as usize;

            let line_offset = (y % 0x08) << 0x01;

            let tile_data_start = options.tile_base_addr +
                                  (if options.tile_base_addr == 0x00 {
                raw_tile_number
            } else {
                (raw_tile_number as i8 as i16 + 0x80) as usize
            }) * 0x10 + line_offset;

            let x_shift = (x % 8).wrapping_sub(0x07).wrapping_mul(0xFF);
            let color_value = Self::build_palette_index(&self.ram[tile_data_start..], x_shift);
            let c = self.get_background_color_for_byte(color_value as u8);
            self.backbuffer.pixels[self.ly as usize * gameboy::SCREEN_WIDTH + i as usize] = c;
        }
    }

    fn render_sprites(&mut self) {
        requires!(self.sprites_enabled);

        for i in 0..40 {
            let sprite_table_entry_base = i * 0x04;
            let s_y = self.sprite_data[sprite_table_entry_base] as i16 - 0x10;
            let s_x = self.sprite_data[sprite_table_entry_base + 0x01] as i16 - 0x08;
            let sprite_height = if let SpriteShape::Rectangle = self.sprite_shape {
                0x10
            } else {
                0x08
            };

            if s_y <= self.ly as i16 && (s_y + sprite_height as i16) > self.ly as i16 {
                let tile_number = self.sprite_data[sprite_table_entry_base + 0x02] as i16;
                let attributes = self.sprite_data[sprite_table_entry_base + 0x03];
                let above_background = attributes & 0x80 == 0x00;
                let flip_y = attributes & 0x40 == 0x40;
                let flip_x = attributes & 0x20 == 0x20;

                let sprite_line = (self.ly as i16 - s_y) << 0x01;
                let tile_data_start = (tile_number * 0x10) + sprite_line;
                for x in 0..8 {
                    if s_x + x < 0 || s_x + x >= gameboy::SCREEN_WIDTH as i16 {
                        continue;
                    }
                    let shift = if flip_x { x } else { 0x07 - x };
                    let color_value =
                        Self::build_palette_index(&self.ram[tile_data_start as usize..],
                                                  shift as u8);
                    if color_value == 0x00 {
                        continue;
                    }
                    let c = self.get_sprite_color_for_byte(color_value as u8,
                                                           ((attributes & 0x10) >> 0x04) as u8);
                    let idx = self.ly as usize * gameboy::SCREEN_WIDTH +
                              (s_x as usize + x as usize);
                    if idx > self.backbuffer.pixels.len() - 1 {
                        panic!(format!("Just tried to place a pixel outside framebuffer size. \
                                        Index was {} and backbuffer length is {}. line: {:b}, \
                                        gameboy::SCREEN_WIDTH: {:b}, s_x: {:b}, x: {:b}",
                                       idx,
                                       self.backbuffer.pixels.len(),
                                       self.ly,
                                       gameboy::SCREEN_WIDTH,
                                       s_x,
                                       x));
                    } else {
                        self.backbuffer.pixels[idx] = c;
                    }
                }
            }
        }
    }

    fn get_background_color_for_byte(&self, b: u8) -> Color {
        let palette_index = match b {
            0x00 => self.bg_palette & 0x0003,
            0x01 => (self.bg_palette & 0x000C) >> 0x02,
            0x02 => (self.bg_palette & 0x0030) >> 0x04,
            0x03 => (self.bg_palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from(palette_index as u8)
    }

    fn get_sprite_color_for_byte(&self, b: u8, palette_entry: u8) -> Color {
        let palette = if palette_entry == 0x00 {
            self.palette0
        } else {
            self.palette1
        };

        let palette_index = match b {
            0x00 => palette & 0x0003,
            0x01 => (palette & 0x000C) >> 0x02,
            0x02 => (palette & 0x0030) >> 0x04,
            0x03 => (palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from(palette_index as u8)
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        match addr {
            0x40 => self.control_register,
            0x41 => self.stat.to_u8(self),
            0x42 => self.scroll_y,
            0x43 => self.scroll_x,
            0x4A => self.window_y,
            0x4B => self.window_x,
            0x44 => self.ly,
            0x45 => self.lyc,
            0x47 => self.bg_palette,
            0x48 => self.palette0,
            0x49 => self.palette1,
            _ => panic!("tried to read GPU memory that is not mapped"),
        }
    }

    pub fn write_u8(&mut self, addr: u16, val: u8) {
        match addr {
            0x40 => {
                self.control_register = val;
                self.enabled = self.control_register & 0x80 == 0x80;
                self.tile_data_addr = if self.control_register & 0x10 == 0x10 {
                    0x00
                } else {
                    0x800
                };
                self.window_tilemap_addr = if self.control_register & 0x40 == 0x40 {
                    0x1C00
                } else {
                    0x1800
                };
                self.background_tilemap_addr = if self.control_register & 0x08 == 0x08 {
                    0x1C00
                } else {
                    0x1800
                };
                self.sprite_shape = if self.control_register & 0x04 == 0x04 {
                    SpriteShape::Rectangle
                } else {
                    SpriteShape::Square
                };
                self.window_enabled = self.control_register & 0x20 == 0x20;
                self.sprites_enabled = self.control_register & 0x02 == 0x02;
                self.background_enabled = self.control_register & 0x01 == 0x01;
            }
            0x41 => self.stat = GpuStat::from(val),
            0x42 => self.scroll_y = val,
            0x43 => self.scroll_x = val,
            0x44 => (),
            0x45 => {
                self.lyc = val;
            }
            0x47 => self.bg_palette = val,
            0x48 => self.palette0 = val,
            0x49 => self.palette1 = val,
            0x4A => self.window_y = val,
            0x4B => self.window_x = val,
            _ => panic!("tried to write GPU memory that is not mapped: {:04}", addr),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let addr = addr as usize;
        LittleEndian::write_u16(&mut self.ram[addr..], val)
    }

    fn check_coincidence(&mut self, irq: &mut Irq) {
        // If LY == LYC then set the coincidence flag
        if self.lyc == self.ly && self.stat.coincidence_interrupt_enabled {
            irq.request(Interrupt::Lcd);
        }
    }

    fn build_palette_index(data: &[u8], x_shift: u8) -> u8 {
        let tile_data1 = (data[0x00] >> x_shift) & 0x01;
        let tile_data2 = (data[0x01] >> x_shift) & 0x01;
        let pixel_palette_entry = (tile_data2 << 1) | tile_data1;
        pixel_palette_entry
    }
}
<commit_msg>Fix window scroll_y issue<commit_after>
use byteorder::{ByteOrder, LittleEndian};

use std;

use gameboy;
use gameboy::{Interconnect, Interrupt, Irq, Memory};
use gameboy::gfx::{Color, Frame, GpuMode, GpuStat, SpriteShape, TileRenderOptions, TileRenderType};

pub struct Gpu {
    pub enabled: bool,
    pub ram: Memory,
    pub sprite_data: Memory,

    pub ly: u8,
    pub lyc: u8,

    pub frame: Frame,
    pub backbuffer: Frame,
    pub mode: GpuMode,

    control_register: u8,
    stat: GpuStat,
    scroll_y: u8,
    scroll_x: u8,
    window_y: u8,
    window_x: u8,
    bg_palette: u8,
    palette0: u8,
    palette1: u8,

    cycles: isize,

    counter: u8,
    tile_data_addr: usize,
    background_tilemap_addr: usize,
    window_tilemap_addr: usize,
    sprite_shape: SpriteShape,
    sprites_enabled: bool,
    background_enabled: bool,
    window_enabled: bool,
}

impl Gpu {
    pub fn new() -> Gpu {
        Gpu {
            enabled: true,
            ram: Memory::new(gameboy::gfx::VRAM_SIZE),
            sprite_data: Memory::new(gameboy::gfx::SPRITE_DATA_SIZE),
            control_register: 0x00,
            stat: GpuStat::new(),
            scroll_y: 0x00,
            scroll_x: 0x00,
            window_y: 0x00,
            window_x: 0x00,
            ly: 0x00,
            lyc: 0x00,
            bg_palette: 0x00,
            palette0: 0x00,
            palette1: 0x00,
            cycles: 0x00,
            frame: Frame::new(),
            backbuffer: Frame::new(),
            mode: GpuMode::HBlank,
            counter: 0,
            tile_data_addr: 0x00,
            background_tilemap_addr: 0xC00,
            window_tilemap_addr: 0x00,
            sprite_shape: SpriteShape::Square,
            sprites_enabled: true,
            background_enabled: true,
            window_enabled: true,
        }
    }

    pub fn reset(&mut self) {
        *self = Gpu::new();
    }

    fn set_status(&mut self, irq: &mut Irq) {
        if !self.enabled {
            self.cycles = 0x1C8;
            self.ly = 0x00;
            self.stat = GpuStat::from(0xFD);
        }

        let current_mode = self.mode.clone();

        if self.ly >= 0x90 {
            self.mode = GpuMode::VBlank;
        } else {
            if self.cycles >= 0x178 {
                self.mode = GpuMode::SearchingRam;
            } else if self.cycles >= 0xCC {
                self.mode = GpuMode::TransferringData;
            } else {
                self.mode = GpuMode::HBlank;
            }
        }

        if self.mode != current_mode {
            irq.request(Interrupt::Lcd);
        }

        self.check_coincidence(irq);
    }

    pub fn step(&mut self, irq: &mut Irq, cycles: usize) -> Result<(), String> {
        self.set_status(irq);

        let cycles = cycles as isize;
        if self.enabled {
            self.cycles -= cycles;
        } else {
            return Ok(());
        }

        if self.cycles <= 0 {
            self.ly += 0x01;

            self.cycles = 0x1C8;

            if self.ly == 0x90 {
                irq.request(Interrupt::Vblank);
            } else if self.ly > 0x99 {
                self.ly = 0x00;
            } else if self.ly < 0x90 {
                self.render_scanline();
            }
        }

        Ok(())
    }

    fn render_scanline(&mut self) {
        self.render_background();
        self.render_window();
        self.render_sprites();
        self.frame = self.backbuffer.clone();
    }

    fn render_background(&mut self) {
        requires!(self.background_enabled);

        let options = TileRenderOptions::new(TileRenderType::Background,
                                             self.ly,
                                             self.background_tilemap_addr,
                                             self.tile_data_addr);
        self.render_tile(&options);
    }

    fn render_window(&mut self) {
        requires!(self.window_enabled);
        requires!(self.ly as usize >= self.window_y as usize);

        let options = TileRenderOptions::new(TileRenderType::Window,
                                             self.ly,
                                             self.window_tilemap_addr,
                                             self.tile_data_addr);
        self.render_tile(&options);
    }

    fn render_tile(&mut self, options: &TileRenderOptions) {
        let window = variant_equals!(TileRenderType::Window, options.render_type);
        let y = if window {
            options.line as usize
        } else {
            options.line.wrapping_add(self.scroll_y) as usize
        };

        // If rendering a window tile, we need to make sure we offset tile line properly
        let window_offset = if window { self.window_y as usize } else { 0x00 };
        let map_row = ((y - window_offset) / 0x08) as usize;

        for i in 0..gameboy::SCREEN_WIDTH {
            // if nowhere near the window, skip
            if window && i < self.window_x as usize {
                continue;
            }

            // If we're at the window, lets negate the window X position from where we
            // need to be in the tile map
            let x = if window {
                i as u8 - self.window_x
            } else {
                // Otherwise, scroll the background
                (i as u8).wrapping_add(self.scroll_x)
            };
            let map_col = (x / 0x08) as usize;
            let raw_tile_number = self.ram[options.map_addr + (map_row * 0x20 + map_col)] as usize;

            let line_offset = (y % 0x08) << 0x01;

            let tile_data_start = options.tile_base_addr +
                                  (if options.tile_base_addr == 0x00 {
                raw_tile_number
            } else {
                (raw_tile_number as i8 as i16 + 0x80) as usize
            }) * 0x10 + line_offset;

            let x_shift = (x % 8).wrapping_sub(0x07).wrapping_mul(0xFF);
            let color_value = Self::build_palette_index(&self.ram[tile_data_start..], x_shift);
            let c = self.get_background_color_for_byte(color_value as u8);
            self.backbuffer.pixels[self.ly as usize * gameboy::SCREEN_WIDTH + i as usize] = c;
        }
    }

    fn render_sprites(&mut self) {
        requires!(self.sprites_enabled);

        for i in 0..40 {
            let sprite_table_entry_base = i * 0x04;
            let s_y = self.sprite_data[sprite_table_entry_base] as i16 - 0x10;
            let s_x = self.sprite_data[sprite_table_entry_base + 0x01] as i16 - 0x08;
            let sprite_height = if let SpriteShape::Rectangle = self.sprite_shape {
                0x10
            } else {
                0x08
            };

            if s_y <= self.ly as i16 && (s_y + sprite_height as i16) > self.ly as i16 {
                let tile_number = self.sprite_data[sprite_table_entry_base + 0x02] as i16;
                let attributes = self.sprite_data[sprite_table_entry_base + 0x03];
                let above_background = attributes & 0x80 == 0x00;
                let flip_y = attributes & 0x40 == 0x40;
                let flip_x = attributes & 0x20 == 0x20;

                let sprite_line = (self.ly as i16 - s_y) << 0x01;
                let tile_data_start = (tile_number * 0x10) + sprite_line;
                for x in 0..8 {
                    if s_x + x < 0 || s_x + x >= gameboy::SCREEN_WIDTH as i16 {
                        continue;
                    }
                    let shift = if flip_x { x } else { 0x07 - x };
                    let color_value =
                        Self::build_palette_index(&self.ram[tile_data_start as usize..],
                                                  shift as u8);
                    if color_value == 0x00 {
                        continue;
                    }
                    let c = self.get_sprite_color_for_byte(color_value as u8,
                                                           ((attributes & 0x10) >> 0x04) as u8);
                    let idx = self.ly as usize * gameboy::SCREEN_WIDTH +
                              (s_x as usize + x as usize);
                    if idx > self.backbuffer.pixels.len() - 1 {
                        panic!(format!("Just tried to place a pixel outside framebuffer size. \
                                        Index was {} and backbuffer length is {}. line: {:b}, \
                                        gameboy::SCREEN_WIDTH: {:b}, s_x: {:b}, x: {:b}",
                                       idx,
                                       self.backbuffer.pixels.len(),
                                       self.ly,
                                       gameboy::SCREEN_WIDTH,
                                       s_x,
                                       x));
                    } else {
                        self.backbuffer.pixels[idx] = c;
                    }
                }
            }
        }
    }

    fn get_background_color_for_byte(&self, b: u8) -> Color {
        let palette_index = match b {
            0x00 => self.bg_palette & 0x0003,
            0x01 => (self.bg_palette & 0x000C) >> 0x02,
            0x02 => (self.bg_palette & 0x0030) >> 0x04,
            0x03 => (self.bg_palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from(palette_index as u8)
    }

    fn get_sprite_color_for_byte(&self, b: u8, palette_entry: u8) -> Color {
        let palette = if palette_entry == 0x00 {
            self.palette0
        } else {
            self.palette1
        };

        let palette_index = match b {
            0x00 => palette & 0x0003,
            0x01 => (palette & 0x000C) >> 0x02,
            0x02 => (palette & 0x0030) >> 0x04,
            0x03 => (palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from(palette_index as u8)
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        match addr {
            0x40 => self.control_register,
            0x41 => self.stat.to_u8(self),
            0x42 => self.scroll_y,
            0x43 => self.scroll_x,
            0x4A => self.window_y,
            0x4B => self.window_x,
            0x44 => self.ly,
            0x45 => self.lyc,
            0x47 => self.bg_palette,
            0x48 => self.palette0,
            0x49 => self.palette1,
            _ => panic!("tried to read GPU memory that is not mapped"),
        }
    }

    pub fn write_u8(&mut self, addr: u16, val: u8) {
        match addr {
            0x40 => {
                self.control_register = val;
                self.enabled = self.control_register & 0x80 == 0x80;
                self.tile_data_addr = if self.control_register & 0x10 == 0x10 {
                    0x00
                } else {
                    0x800
                };
                self.window_tilemap_addr = if self.control_register & 0x40 == 0x40 {
                    0x1C00
                } else {
                    0x1800
                };
                self.background_tilemap_addr = if self.control_register & 0x08 == 0x08 {
                    0x1C00
                } else {
                    0x1800
                };
                self.sprite_shape = if self.control_register & 0x04 == 0x04 {
                    SpriteShape::Rectangle
                } else {
                    SpriteShape::Square
                };
                self.window_enabled = self.control_register & 0x20 == 0x20;
                self.sprites_enabled = self.control_register & 0x02 == 0x02;
                self.background_enabled = self.control_register & 0x01 == 0x01;
            }
            0x41 => self.stat = GpuStat::from(val),
            0x42 => self.scroll_y = val,
            0x43 => self.scroll_x = val,
            0x44 => (),
            0x45 => {
                self.lyc = val;
            }
            0x47 => self.bg_palette = val,
            0x48 => self.palette0 = val,
            0x49 => self.palette1 = val,
            0x4A => self.window_y = val,
            0x4B => self.window_x = val,
            _ => panic!("tried to write GPU memory that is not mapped: {:04}", addr),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let addr = addr as usize;
        LittleEndian::write_u16(&mut self.ram[addr..], val)
    }

    fn check_coincidence(&mut self, irq: &mut Irq) {
        // If LY == LYC then set the coincidence flag
        if self.lyc == self.ly && self.stat.coincidence_interrupt_enabled {
            irq.request(Interrupt::Lcd);
        }
    }

    fn build_palette_index(data: &[u8], x_shift: u8) -> u8 {
        let tile_data1 = (data[0x00] >> x_shift) & 0x01;
        let tile_data2 = (data[0x01] >> x_shift) & 0x01;
        let pixel_palette_entry = (tile_data2 << 1) | tile_data1;
        pixel_palette_entry
    }
}
<|endoftext|>


<commit_before>use std::mem;

pub struct Any {
    ptr: *mut (),
    drop: fn(*mut ()),
    fingerprint: Fingerprint,
}

impl Any {
    pub fn new<T>(t: T) -> Self {
        let ptr = Box::into_raw(Box::new(t));
        Any {
            ptr: ptr as *mut (),
            drop: |ptr| drop(unsafe { Box::from_raw(ptr as *mut T) }),
            fingerprint: Fingerprint::of::<T>(),
        }
    }

    pub fn view<T>(&mut self) -> &mut T {
        if self.fingerprint != Fingerprint::of::<T>() {
            panic!("invalid cast");
        }
        let ptr = self.ptr as *mut T;
        unsafe { &mut *ptr }
    }

    pub fn take<T>(self) -> T {
        if self.fingerprint != Fingerprint::of::<T>() {
            panic!("invalid cast");
        }
        let ptr = self.ptr as *mut T;
        let box_t = unsafe { Box::from_raw(ptr) };
        mem::forget(self);
        *box_t
    }
}

impl Drop for Any {
    fn drop(&mut self) {
        (self.drop)(self.ptr);
    }
}

#[derive(Eq, PartialEq)]
struct Fingerprint {
    size: usize,
    align: usize,
    id: usize,
}

impl Fingerprint {
    fn of<T>() -> Fingerprint {
        Fingerprint {
            size: mem::size_of::<T>(),
            align: mem::align_of::<T>(),
            id: Fingerprint::of::<T> as usize,
        }
    }
}
<commit_msg>Disclaimer for function pointer as type id<commit_after>use std::mem;

pub struct Any {
    ptr: *mut (),
    drop: fn(*mut ()),
    fingerprint: Fingerprint,
}

impl Any {
    pub fn new<T>(t: T) -> Self {
        let ptr = Box::into_raw(Box::new(t));
        Any {
            ptr: ptr as *mut (),
            drop: |ptr| drop(unsafe { Box::from_raw(ptr as *mut T) }),
            fingerprint: Fingerprint::of::<T>(),
        }
    }

    pub fn view<T>(&mut self) -> &mut T {
        if self.fingerprint != Fingerprint::of::<T>() {
            panic!("invalid cast");
        }
        let ptr = self.ptr as *mut T;
        unsafe { &mut *ptr }
    }

    pub fn take<T>(self) -> T {
        if self.fingerprint != Fingerprint::of::<T>() {
            panic!("invalid cast");
        }
        let ptr = self.ptr as *mut T;
        let box_t = unsafe { Box::from_raw(ptr) };
        mem::forget(self);
        *box_t
    }
}

impl Drop for Any {
    fn drop(&mut self) {
        (self.drop)(self.ptr);
    }
}

#[derive(Eq, PartialEq)]
struct Fingerprint {
    size: usize,
    align: usize,
    id: usize,
}

impl Fingerprint {
    fn of<T>() -> Fingerprint {
        Fingerprint {
            size: mem::size_of::<T>(),
            align: mem::align_of::<T>(),
            // This is not foolproof -- theoretically Rust or LLVM could
            // deduplicate some or all of these methods. But in practice it's
            // great and I am comfortable relying on this in debug mode to catch
            // bugs early.
            id: Fingerprint::of::<T> as usize,
        }
    }
}
<|endoftext|>


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

#![feature(test)]
#![allow(dead_code)]

extern crate test;
extern crate tempdir;
#[macro_use]
extern crate exonum;

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

    use tempdir::TempDir;

    use exonum::storage::{ProofMapIndex, Database, Fork, LevelDB, LevelDBOptions, LevelDBCache,
                          StorageValue, Patch};

    use std::collections::BTreeMap;
    use exonum::blockchain::{Blockchain, Transaction};
    use exonum::crypto::{gen_keypair, Hash, PublicKey, SecretKey};
    use exonum::messages::Message;

    fn execute_timestamping(db: Box<Database>, b: &mut Bencher) {
        let mut blockchain = Blockchain::new(db, Vec::new());

        message! {
            struct Tx {
                const TYPE = 1;
                const ID = 0;
                const SIZE = 64;

                field from: &PublicKey [00 => 32]
                field data: &Hash      [32 => 64]
            }
        }

        impl Transaction for Tx {
            fn verify(&self) -> bool {
                self.verify_signature(self.from())
            }

            fn execute(&self, _: &mut Fork) {}
        }

        fn prepare_txs(height: u64, count: u64) -> (Vec<Hash>, BTreeMap<Hash, Box<Transaction>>) {
            let (pub_key, sec_key) = gen_keypair();
            let mut txs = Vec::new();
            let mut pool = BTreeMap::new();
            for i in (height * count)..((height + 1) * count) {
                let tx = Tx::new(&pub_key, &i.hash(), &sec_key);
                let tx_hash = Transaction::hash(&tx);
                txs.push(tx_hash);
                pool.insert(tx_hash, Box::new(tx) as Box<Transaction>);
            }
            (txs, pool)
        }

        fn execute_block(
            blockchain: &Blockchain,
            height: u64,
            txs: &[Hash],
            pool: &BTreeMap<Hash, Box<Transaction>>,
        ) -> Patch {
            blockchain.create_patch(0, height, txs, pool).1
        }

        for i in 0..100 {
            let (txs, pool) = prepare_txs(i, 1000);
            let patch = execute_block(&blockchain, i, &txs, &pool);
            blockchain.merge(patch).unwrap();
        }

        let (txs, pool) = prepare_txs(100, 1000);

        b.iter(|| execute_block(&blockchain, 100, &txs, &pool));
    }

    fn execute_cryptocurrency(db: Box<Database>, b: &mut Bencher) {
        let mut blockchain = Blockchain::new(db, Vec::new());

        message! {
            struct Tx {
                const TYPE = 1;
                const ID = 0;
                const SIZE = 64;

                field from: &PublicKey [00 => 32]
                field to:   &PublicKey [32 => 64]
            }
        }

        impl Transaction for Tx {
            fn verify(&self) -> bool {
                self.verify_signature(self.from())
            }

            fn execute(&self, view: &mut Fork) {
                let mut index = ProofMapIndex::new(vec![1], view);
                let from_balance = index.get(self.from()).unwrap_or(0u64);
                let to_balance = index.get(self.to()).unwrap_or(0u64);
                index.put(self.from(), from_balance - 1);
                index.put(self.to(), to_balance + 1);
            }
        }

        let mut keys = Vec::new();

        for _ in 0..10_000 {
            keys.push(gen_keypair());
        }

        fn prepare_txs(
            height: u64,
            count: u64,
            keys: &[(PublicKey, SecretKey)],
        ) -> (Vec<Hash>, BTreeMap<Hash, Box<Transaction>>) {
            let mut txs = Vec::new();
            let mut pool = BTreeMap::new();
            for i in (height * count)..((height + 1) * count) {
                let tx = Tx::new(
                    &keys[i as usize % 10_000].0,
                    &keys[(i as usize + 3_456) % 10_000].0,
                    &keys[i as usize % 10_000].1,
                );
                let tx_hash = Transaction::hash(&tx);
                txs.push(tx_hash);
                pool.insert(tx_hash, Box::new(tx) as Box<Transaction>);
            }
            (txs, pool)
        }

        fn execute_block(
            blockchain: &Blockchain,
            height: u64,
            txs: &[Hash],
            pool: &BTreeMap<Hash, Box<Transaction>>,
        ) -> Patch {
            blockchain.create_patch(0, height, txs, pool).1
        }

        for i in 0..100 {
            let (txs, pool) = prepare_txs(i, 1000, &keys);
            let patch = execute_block(&blockchain, i, &txs, &pool);
            blockchain.merge(patch).unwrap();
        }

        let (txs, pool) = prepare_txs(100, 1000, &keys);

        b.iter(|| execute_block(&blockchain, 100, &txs, &pool));
    }

    #[bench]
    fn bench_execute_block_timestamping_leveldb(b: &mut Bencher) {
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        let path = TempDir::new("exonum").unwrap();
        let db = Box::new(LevelDB::open(path.path(), options).unwrap()) as Box<Database>;
        execute_timestamping(db, b)
    }

    #[bench]
    fn bench_execute_block_timestamping_leveldb_cache(b: &mut Bencher) {
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        options.cache = Some(LevelDBCache::new(100_000_000));
        let path = TempDir::new("exonum").unwrap();
        let db = Box::new(LevelDB::open(path.path(), options).unwrap()) as Box<Database>;
        execute_timestamping(db, b)
    }

    #[bench]
    fn bench_execute_block_cryptocurrency_leveldb(b: &mut Bencher) {
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        let path = TempDir::new("exonum").unwrap();
        let db = Box::new(LevelDB::open(path.path(), options).unwrap()) as Box<Database>;
        execute_cryptocurrency(db, b)
    }

    #[bench]
    fn bench_execute_block_cryptocurrency_leveldb_cache(b: &mut Bencher) {
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        options.cache = Some(LevelDBCache::new(100_000_000));
        let path = TempDir::new("exonum").unwrap();
        let db = Box::new(LevelDB::open(path.path(), options).unwrap()) as Box<Database>;
        execute_cryptocurrency(db, b)
    }
}
<commit_msg>Fix benches<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![feature(test)]
#![allow(dead_code)]

extern crate test;
extern crate tempdir;
#[macro_use]
extern crate exonum;

#[cfg(test)]
mod tests {
    use tempdir::TempDir;

    use test::Bencher;
    use std::collections::BTreeMap;

    use exonum::storage::{ProofMapIndex, Database, Fork, LevelDB, LevelDBOptions, LevelDBCache,
                          StorageValue, Patch};
    use exonum::blockchain::{Blockchain, Transaction};
    use exonum::crypto::{gen_keypair, Hash, PublicKey, SecretKey};
    use exonum::messages::Message;
    use exonum::helpers::ValidatorId;

    fn execute_timestamping(db: Box<Database>, b: &mut Bencher) {
        let mut blockchain = Blockchain::new(db, Vec::new());

        message! {
            struct Tx {
                const TYPE = 1;
                const ID = 0;
                const SIZE = 64;

                field from: &PublicKey [00 => 32]
                field data: &Hash      [32 => 64]
            }
        }

        impl Transaction for Tx {
            fn verify(&self) -> bool {
                self.verify_signature(self.from())
            }

            fn execute(&self, _: &mut Fork) {}
        }

        fn prepare_txs(height: u64, count: u64) -> (Vec<Hash>, BTreeMap<Hash, Box<Transaction>>) {
            let (pub_key, sec_key) = gen_keypair();
            let mut txs = Vec::new();
            let mut pool = BTreeMap::new();
            for i in (height * count)..((height + 1) * count) {
                let tx = Tx::new(&pub_key, &i.hash(), &sec_key);
                let tx_hash = Transaction::hash(&tx);
                txs.push(tx_hash);
                pool.insert(tx_hash, Box::new(tx) as Box<Transaction>);
            }
            (txs, pool)
        }

        fn execute_block(
            blockchain: &Blockchain,
            height: u64,
            txs: &[Hash],
            pool: &BTreeMap<Hash, Box<Transaction>>,
        ) -> Patch {
            blockchain.create_patch(ValidatorId::zero(), Height(height), txs, pool).1
        }

        for i in 0..100 {
            let (txs, pool) = prepare_txs(i, 1000);
            let patch = execute_block(&blockchain, i, &txs, &pool);
            blockchain.merge(patch).unwrap();
        }

        let (txs, pool) = prepare_txs(100, 1000);

        b.iter(|| execute_block(&blockchain, 100, &txs, &pool));
    }

    fn execute_cryptocurrency(db: Box<Database>, b: &mut Bencher) {
        let mut blockchain = Blockchain::new(db, Vec::new());

        message! {
            struct Tx {
                const TYPE = 1;
                const ID = 0;
                const SIZE = 64;

                field from: &PublicKey [00 => 32]
                field to:   &PublicKey [32 => 64]
            }
        }

        impl Transaction for Tx {
            fn verify(&self) -> bool {
                self.verify_signature(self.from())
            }

            fn execute(&self, view: &mut Fork) {
                let mut index = ProofMapIndex::new(vec![1], view);
                let from_balance = index.get(self.from()).unwrap_or(0u64);
                let to_balance = index.get(self.to()).unwrap_or(0u64);
                index.put(self.from(), from_balance - 1);
                index.put(self.to(), to_balance + 1);
            }
        }

        let mut keys = Vec::new();

        for _ in 0..10_000 {
            keys.push(gen_keypair());
        }

        fn prepare_txs(
            height: u64,
            count: u64,
            keys: &[(PublicKey, SecretKey)],
        ) -> (Vec<Hash>, BTreeMap<Hash, Box<Transaction>>) {
            let mut txs = Vec::new();
            let mut pool = BTreeMap::new();
            for i in (height * count)..((height + 1) * count) {
                let tx = Tx::new(
                    &keys[i as usize % 10_000].0,
                    &keys[(i as usize + 3_456) % 10_000].0,
                    &keys[i as usize % 10_000].1,
                );
                let tx_hash = Transaction::hash(&tx);
                txs.push(tx_hash);
                pool.insert(tx_hash, Box::new(tx) as Box<Transaction>);
            }
            (txs, pool)
        }

        fn execute_block(
            blockchain: &Blockchain,
            height: u64,
            txs: &[Hash],
            pool: &BTreeMap<Hash, Box<Transaction>>,
        ) -> Patch {
            blockchain.create_patch(ValidatorId::zero(), Height(height), txs, pool).1
        }

        for i in 0..100 {
            let (txs, pool) = prepare_txs(i, 1000, &keys);
            let patch = execute_block(&blockchain, i, &txs, &pool);
            blockchain.merge(patch).unwrap();
        }

        let (txs, pool) = prepare_txs(100, 1000, &keys);

        b.iter(|| execute_block(&blockchain, 100, &txs, &pool));
    }

    #[bench]
    fn bench_execute_block_timestamping_leveldb(b: &mut Bencher) {
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        let path = TempDir::new("exonum").unwrap();
        let db = Box::new(LevelDB::open(path.path(), options).unwrap()) as Box<Database>;
        execute_timestamping(db, b)
    }

    #[bench]
    fn bench_execute_block_timestamping_leveldb_cache(b: &mut Bencher) {
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        options.cache = Some(LevelDBCache::new(100_000_000));
        let path = TempDir::new("exonum").unwrap();
        let db = Box::new(LevelDB::open(path.path(), options).unwrap()) as Box<Database>;
        execute_timestamping(db, b)
    }

    #[bench]
    fn bench_execute_block_cryptocurrency_leveldb(b: &mut Bencher) {
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        let path = TempDir::new("exonum").unwrap();
        let db = Box::new(LevelDB::open(path.path(), options).unwrap()) as Box<Database>;
        execute_cryptocurrency(db, b)
    }

    #[bench]
    fn bench_execute_block_cryptocurrency_leveldb_cache(b: &mut Bencher) {
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        options.cache = Some(LevelDBCache::new(100_000_000));
        let path = TempDir::new("exonum").unwrap();
        let db = Box::new(LevelDB::open(path.path(), options).unwrap()) as Box<Database>;
        execute_cryptocurrency(db, b)
    }
}
<|endoftext|>


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

#[cfg(feature = "wee-alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[cfg_attr(
    all(target_arch = "wasm32", target_os = "unknown"),
    export_name = "malloc"
)]
#[no_mangle]
pub extern "C" fn proxy_on_memory_allocate(size: usize) -> *mut u8 {
    let mut vec: Vec<u8> = Vec::with_capacity(size);
    unsafe {
        vec.set_len(size);
    }
    let slice = vec.into_boxed_slice();
    Box::into_raw(slice) as *mut u8
}
<commit_msg>Fix Clippy warning on memory allocation. (#130)<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::mem::MaybeUninit;

#[cfg(feature = "wee-alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[cfg_attr(
    all(target_arch = "wasm32", target_os = "unknown"),
    export_name = "malloc"
)]
#[no_mangle]
pub extern "C" fn proxy_on_memory_allocate(size: usize) -> *mut u8 {
    let mut vec: Vec<MaybeUninit<u8>> = Vec::with_capacity(size);
    unsafe {
        vec.set_len(size);
    }
    let slice = vec.into_boxed_slice();
    Box::into_raw(slice) as *mut u8
}
<|endoftext|>


<commit_before>extern crate byteorder;
pub mod rom_loader;
pub mod memory;
pub mod cpu;
pub mod logger;

use cpu::CpuState;
use memory::Memory;

fn main() {
    let rom = rom_loader::load_ines("roms/nestest.nes").unwrap();
    let mut cpu: CpuState = Default::default();
    let mut mem:Memory = Memory::new();

    // TODO: handle this properly instead of just unwrapping
    let opcode_info = cpu::opcode::load_from_file("resources/opcodes.csv").unwrap();
    let executor = cpu::CpuExecutor::new(opcode_info.0);
    let mut logger = logger::NesTest::new("resources/nestest.out",opcode_info.1);

    executor.power_on(&mut cpu, &mut mem);
    cpu.pc = 0xC000;
    mem.write(0,&rom);

    for i in 0..10 {
        executor.fetch_and_decode(&mut cpu,&mut mem).unwrap();
        logger.log_after_fetch(&cpu,&mem);
        executor.execute(&mut cpu,&mut mem).unwrap();
    }
}

<commit_msg>added is_ok checks<commit_after>extern crate byteorder;
pub mod rom_loader;
pub mod memory;
pub mod cpu;
pub mod logger;

use cpu::CpuState;
use memory::Memory;

fn main() {
    let rom = rom_loader::load_ines("roms/nestest.nes").unwrap();
    let mut cpu: CpuState = Default::default();
    let mut mem:Memory = Memory::new();

    // TODO: handle this properly instead of just unwrapping
    let opcode_info = cpu::opcode::load_from_file("resources/opcodes.csv").unwrap();
    let executor = cpu::CpuExecutor::new(opcode_info.0);
    let mut logger = logger::NesTest::new("resources/nestest.out",opcode_info.1);

    executor.power_on(&mut cpu, &mut mem);
    cpu.pc = 0xC000;
    mem.write(0,&rom);

    for i in 0..10 {
        executor.fetch_and_decode(&mut cpu,&mut mem).is_ok();
        logger.log_after_fetch(&cpu,&mem);
        executor.execute(&mut cpu,&mut mem).is_ok();
    }
}

<|endoftext|>


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

use address::Address;
use basicblock::{BasicBlock, BasicBlockEdge, EdgeType};
use disassembler::Disassembler;
use instruction::Instruction;
use petgraph::graph::{Graph, NodeIndex};
use std::collections::BTreeMap;

/// A [control flow graph].
///
/// [control flow graph]: https://en.wikipedia.org/wiki/Control_flow_graph
pub struct ControlFlowGraph<'f, I: 'f + Instruction> {
    /// The [`Graph`] that stores the actual ControlFlowGraph
    ///
    /// [`Graph`]: ../petgraph/graph/struct.Graph.html
    pub graph: Graph<BasicBlock<'f, I>, BasicBlockEdge>,
    /// The [`NodeIndex`] for the entry [`BasicBlock`] for this function.
    ///
    /// [`BasicBlock`]: struct.BasicBlock.html
    /// [`NodeIndex`]: ../petgraph/graph/struct.NodeIndex.html
    pub entry_block: Option<NodeIndex>,
    /// Map an [address] to the corresponding [basic block].
    ///
    /// [address]: struct.Address.html
    /// [basic block]: struct.BasicBlock.html
    pub block_finder: BTreeMap<Address, NodeIndex>,
}

impl<'f, I: Instruction> ControlFlowGraph<'f, I> {
    /// Build the ControlFlowGraph from the [`instructions`].
    ///
    /// This is conducted in a 2 step process:
    ///
    /// * First, each instruction is examined to identify block boundaries.
    /// * Then, we go through each instruction again, looking for the
    ///   previously identified block boundaries and build the edges.
    ///
    /// This two step process prevents us from having to construct and then
    /// subsequently split blocks as we find backward edges.
    ///
    /// [`instructions`]: trait.Instruction.html
    pub fn new(instructions: &'f [I], disassembler: &Disassembler) -> Self {
        let mut cfg = ControlFlowGraph {
            graph: Graph::new(),
            entry_block: None,
            block_finder: BTreeMap::new(),
        };
        if !instructions.is_empty() {
            cfg.identify_blocks(instructions, disassembler);
            cfg.build_edges(instructions, disassembler);
        }
        cfg
    }

    /// Identify basic blocks by their boundaries.
    ///
    /// Inspect each instruction to see if it is a 'leader' or the start of
    /// a new basic block. An instruction is a leader if:
    ///
    /// * It is the first instruction in the basic block.
    /// * It comes after a branch of any sort. We identify these as things
    ///   for which `Instruction::is_block_terminator` returns `true`.
    /// * It is the target of a jump (conditional or unconditional) within
    ///   the function.
    fn identify_blocks(&mut self, instructions: &[I], disassembler: &Disassembler) {
        let start_addr = instructions[0].address(disassembler);
        let end_addr = instructions.last().map(|i| i.address(disassembler)).unwrap();
        let mut next_is_leader: bool = true;
        for inst in instructions {
            if next_is_leader {
                let idx = self.graph.add_node(BasicBlock::new(inst.address(disassembler)));
                self.block_finder.insert(inst.address(disassembler), idx);
                next_is_leader = false;
            }
            if inst.is_block_terminator(disassembler) {
                if let Some(target_addr) = inst.target_address(disassembler) {
                    if !self.block_finder.contains_key(&target_addr) &&
                       target_addr >= start_addr && target_addr <= end_addr {
                        let idx = self.graph.add_node(BasicBlock::new(target_addr));
                        self.block_finder.insert(target_addr, idx);
                    }
                }
                // The next instruction, if any, will be the start of a new block.
                next_is_leader = true;
            }
        }
        self.entry_block = Some(self.block_finder[&instructions[0].address(disassembler)]);
    }

    /// Build an edge between 2 basic blocks.
    fn build_edge(&mut self,
                  current_block_idx: NodeIndex,
                  next_block_idx: NodeIndex,
                  current_inst: &I,
                  disassembler: &Disassembler) {
        if current_inst.is_local_conditional_jump(disassembler) {
            // We have one edge for the jump target and one for the fallthrough.
            // We jump through some hoops here to keep the borrow checker happy.
            if let Some(target_addr) = current_inst.target_address(disassembler) {
                if let Some(target_block_idx) = self.block_finder.get(&target_addr) {
                    let edge = BasicBlockEdge { edge_type: EdgeType::ConditionalTaken };
                    self.graph.add_edge(current_block_idx, *target_block_idx, edge);
                }
                let edge = BasicBlockEdge { edge_type: EdgeType::ConditionalFallthrough };
                self.graph.add_edge(current_block_idx, next_block_idx, edge);
            }
        } else if current_inst.is_local_jump(disassembler) || current_inst.is_call(disassembler) {
            let edge = BasicBlockEdge { edge_type: EdgeType::Unconditional };
            self.graph.add_edge(current_block_idx, next_block_idx, edge);
        } else if current_inst.is_return(disassembler) {
            // Do we want to record this exit anywhere?
        }
    }

    /// Build edges after having identified the basic blocks.
    ///
    /// After the basic blocks have been identified, we can go back and
    /// connect the basic blocks with their appropriate edges.
    ///
    /// We do this by iterating through the instructions looking for
    /// boundaries between the basic blocks and then setting up the
    /// new edges.
    fn build_edges(&mut self, instructions: &'f [I], disassembler: &Disassembler) {
        // Here, we're going to walk through the instructions again,
        // looking at the current instruction, while also maintaining
        // a separate iterator giving us the next instruction (if there
        // is one.
        let current_inst_iter = instructions.iter();
        let mut next_inst_iter = current_inst_iter.clone();
        // Skip the first one so that we're actually working with the next one.
        next_inst_iter.next();
        let mut current_block_idx = self.entry_block.unwrap();
        for current_inst in current_inst_iter {
            // Add this instruction to the current block
            if let Some(current_block) = self.graph.node_weight_mut(current_block_idx) {
                current_block.instructions.push(current_inst);
            }
            if let Some(next_inst) = next_inst_iter.next() {
                // Does the next instruction begin a basic block?
                let next_block_idx = *self.block_finder
                                          .get(&next_inst.address(disassembler))
                                          .unwrap_or(&current_block_idx);
                // If we're at a block boundary, create an edge between the
                // current and next blocks. The type of the edge is determined
                // by looking at the current instruction.
                if next_block_idx != current_block_idx {
                    self.build_edge(current_block_idx,
                                    next_block_idx,
                                    current_inst,
                                    disassembler);
                    current_block_idx = next_block_idx;
                }
            } else {
                // No next instruction, so we're at the end.
                // Do we want to record this exit anywhere?
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use petgraph::EdgeDirection;
    use super::ControlFlowGraph;
    use tests::*;

    #[test]
    fn construct() {
        let insts: Vec<TestInstruction> = vec![];
        let disasm = TestDisassembler::new();
        let cfg = ControlFlowGraph::new(&insts, &disasm);
        assert!(cfg.entry_block.is_none());
        assert_eq!(cfg.graph.node_count(), 0);
    }

    #[test]
    fn build_one_basic_block() {
        let insts = [TestInstruction::new(0, Opcode::Add),
                     TestInstruction::new(1, Opcode::Add),
                     TestInstruction::new(2, Opcode::Ret)];
        let disasm = TestDisassembler::new();
        let cfg = ControlFlowGraph::new(&insts, &disasm);
        assert!(cfg.entry_block.is_some());
        assert_eq!(cfg.graph.node_count(), 1);

        let inbound = cfg.graph.externals(EdgeDirection::Incoming);
        assert_eq!(inbound.count(), 1);

        let outbound = cfg.graph.externals(EdgeDirection::Outgoing);
        assert_eq!(outbound.count(), 1);
    }
}
<commit_msg>Added tests for all the fixed cases<commit_after>// 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.

use address::Address;
use basicblock::{BasicBlock, BasicBlockEdge, EdgeType};
use disassembler::Disassembler;
use instruction::Instruction;
use petgraph::graph::{Graph, NodeIndex};
use std::collections::BTreeMap;

/// A [control flow graph].
///
/// [control flow graph]: https://en.wikipedia.org/wiki/Control_flow_graph
pub struct ControlFlowGraph<'f, I: 'f + Instruction> {
    /// The [`Graph`] that stores the actual ControlFlowGraph
    ///
    /// [`Graph`]: ../petgraph/graph/struct.Graph.html
    pub graph: Graph<BasicBlock<'f, I>, BasicBlockEdge>,
    /// The [`NodeIndex`] for the entry [`BasicBlock`] for this function.
    ///
    /// [`BasicBlock`]: struct.BasicBlock.html
    /// [`NodeIndex`]: ../petgraph/graph/struct.NodeIndex.html
    pub entry_block: Option<NodeIndex>,
    /// Map an [address] to the corresponding [basic block].
    ///
    /// [address]: struct.Address.html
    /// [basic block]: struct.BasicBlock.html
    pub block_finder: BTreeMap<Address, NodeIndex>,
}

impl<'f, I: Instruction> ControlFlowGraph<'f, I> {
    /// Build the ControlFlowGraph from the [`instructions`].
    ///
    /// This is conducted in a 2 step process:
    ///
    /// * First, each instruction is examined to identify block boundaries.
    /// * Then, we go through each instruction again, looking for the
    ///   previously identified block boundaries and build the edges.
    ///
    /// This two step process prevents us from having to construct and then
    /// subsequently split blocks as we find backward edges.
    ///
    /// [`instructions`]: trait.Instruction.html
    pub fn new(instructions: &'f [I], disassembler: &Disassembler) -> Self {
        let mut cfg = ControlFlowGraph {
            graph: Graph::new(),
            entry_block: None,
            block_finder: BTreeMap::new(),
        };
        if !instructions.is_empty() {
            cfg.identify_blocks(instructions, disassembler);
            cfg.build_edges(instructions, disassembler);
        }
        cfg
    }

    /// Identify basic blocks by their boundaries.
    ///
    /// Inspect each instruction to see if it is a 'leader' or the start of
    /// a new basic block. An instruction is a leader if:
    ///
    /// * It is the first instruction in the basic block.
    /// * It comes after a branch of any sort. We identify these as things
    ///   for which `Instruction::is_block_terminator` returns `true`.
    /// * It is the target of a jump (conditional or unconditional) within
    ///   the function.
    fn identify_blocks(&mut self, instructions: &[I], disassembler: &Disassembler) {
        let start_addr = instructions[0].address(disassembler);
        let end_addr = instructions.last().map(|i| i.address(disassembler)).unwrap();
        let mut next_is_leader: bool = true;
        for inst in instructions {
            if next_is_leader {
                let idx = self.graph.add_node(BasicBlock::new(inst.address(disassembler)));
                self.block_finder.insert(inst.address(disassembler), idx);
                next_is_leader = false;
            }
            if inst.is_block_terminator(disassembler) {
                if let Some(target_addr) = inst.target_address(disassembler) {
                    if !self.block_finder.contains_key(&target_addr) &&
                       target_addr >= start_addr && target_addr <= end_addr {
                        let idx = self.graph.add_node(BasicBlock::new(target_addr));
                        self.block_finder.insert(target_addr, idx);
                    }
                }
                // The next instruction, if any, will be the start of a new block.
                next_is_leader = true;
            }
        }
        self.entry_block = Some(self.block_finder[&instructions[0].address(disassembler)]);
    }

    /// Build an edge between 2 basic blocks.
    fn build_edge(&mut self,
                  current_block_idx: NodeIndex,
                  next_block_idx: NodeIndex,
                  current_inst: &I,
                  disassembler: &Disassembler) {
        if current_inst.is_local_conditional_jump(disassembler) {
            // We have one edge for the jump target and one for the fallthrough.
            // We jump through some hoops here to keep the borrow checker happy.
            if let Some(target_addr) = current_inst.target_address(disassembler) {
                if let Some(target_block_idx) = self.block_finder.get(&target_addr) {
                    let edge = BasicBlockEdge { edge_type: EdgeType::ConditionalTaken };
                    self.graph.add_edge(current_block_idx, *target_block_idx, edge);
                }
                let edge = BasicBlockEdge { edge_type: EdgeType::ConditionalFallthrough };
                self.graph.add_edge(current_block_idx, next_block_idx, edge);
            }
        } else if current_inst.is_local_jump(disassembler) || current_inst.is_call(disassembler) {
            let edge = BasicBlockEdge { edge_type: EdgeType::Unconditional };
            self.graph.add_edge(current_block_idx, next_block_idx, edge);
        } else if current_inst.is_return(disassembler) {
            // Do we want to record this exit anywhere?
        }
    }

    /// Build edges after having identified the basic blocks.
    ///
    /// After the basic blocks have been identified, we can go back and
    /// connect the basic blocks with their appropriate edges.
    ///
    /// We do this by iterating through the instructions looking for
    /// boundaries between the basic blocks and then setting up the
    /// new edges.
    fn build_edges(&mut self, instructions: &'f [I], disassembler: &Disassembler) {
        // Here, we're going to walk through the instructions again,
        // looking at the current instruction, while also maintaining
        // a separate iterator giving us the next instruction (if there
        // is one.
        let current_inst_iter = instructions.iter();
        let mut next_inst_iter = current_inst_iter.clone();
        // Skip the first one so that we're actually working with the next one.
        next_inst_iter.next();
        let mut current_block_idx = self.entry_block.unwrap();
        for current_inst in current_inst_iter {
            // Add this instruction to the current block
            if let Some(current_block) = self.graph.node_weight_mut(current_block_idx) {
                current_block.instructions.push(current_inst);
            }
            if let Some(next_inst) = next_inst_iter.next() {
                // Does the next instruction begin a basic block?
                let next_block_idx = *self.block_finder
                                          .get(&next_inst.address(disassembler))
                                          .unwrap_or(&current_block_idx);
                // If we're at a block boundary, create an edge between the
                // current and next blocks. The type of the edge is determined
                // by looking at the current instruction.
                if next_block_idx != current_block_idx {
                    self.build_edge(current_block_idx,
                                    next_block_idx,
                                    current_inst,
                                    disassembler);
                    current_block_idx = next_block_idx;
                }
            } else {
                // No next instruction, so we're at the end.
                // Do we want to record this exit anywhere?
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use petgraph::EdgeDirection;
    use petgraph::graph::NodeIndex;
    use super::ControlFlowGraph;
    use tests::*;
    use address::Address;
    use std::collections::HashSet;

    #[test]
    fn construct() {
        let insts: Vec<TestInstruction> = vec![];
        let disasm = TestDisassembler::new();
        let cfg = ControlFlowGraph::new(&insts, &disasm);
        assert!(cfg.entry_block.is_none());
        assert_eq!(cfg.graph.node_count(), 0);
    }

    #[test]
    fn build_one_basic_block() {
        let insts = [TestInstruction::new(0, Opcode::Add),
                     TestInstruction::new(1, Opcode::Add),
                     TestInstruction::new(2, Opcode::Ret)];
        let disasm = TestDisassembler::new();
        let cfg = ControlFlowGraph::new(&insts, &disasm);
        assert!(cfg.entry_block.is_some());
        assert_eq!(cfg.graph.node_count(), 1);

        let inbound = cfg.graph.externals(EdgeDirection::Incoming);
        assert_eq!(inbound.count(), 1);

        let outbound = cfg.graph.externals(EdgeDirection::Outgoing);
        assert_eq!(outbound.count(), 1);
    }

    #[test]
    fn build_cfg_containing_local_unconditional_jump() {
        let insts = [
            TestInstruction::new(0, Opcode::Add),
            TestInstruction::new(1, Opcode::CJmp(Address::new(4))),
            TestInstruction::new(2, Opcode::Add),
            TestInstruction::new(3, Opcode::Jmp(Address::new(5))),
            TestInstruction::new(4, Opcode::Add),
            TestInstruction::new(5, Opcode::Ret),
        ];

        let disasm = TestDisassembler::new();
        let cfg = ControlFlowGraph::new(&insts, &disasm);

        assert_eq!(4, cfg.graph.edge_count());
        assert_eq!(4, cfg.graph.node_count());

        let root_idx = *cfg.block_finder.get(&Address::new(0)).unwrap();
        let cond_taken_idx = *cfg.block_finder.get(&Address::new(4)).unwrap();
        let falltrough_idx = *cfg.block_finder.get(&Address::new(2)).unwrap();
        let ret_idx = *cfg.block_finder.get(&Address::new(5)).unwrap();

        let neighbours = cfg.graph.neighbors(root_idx).into_iter().collect();
        assert_neighbours(neighbours, vec![cond_taken_idx, falltrough_idx]);

        let neighbours = cfg.graph.neighbors(cond_taken_idx).into_iter().collect();
        assert_neighbours(neighbours, vec![ret_idx]);

        let neighbours = cfg.graph.neighbors(falltrough_idx).into_iter().collect();
        assert_neighbours(neighbours, vec![ret_idx]);

        let neighbours = cfg.graph.neighbors(ret_idx).into_iter().collect();
        assert_neighbours(neighbours, vec![]);
    }

    #[test]
    fn build_cfg_with_goto_ending() {
        let insts = [
            TestInstruction::new(0, Opcode::Add),
            TestInstruction::new(1, Opcode::CJmp(Address::new(3))),
            TestInstruction::new(2, Opcode::Ret),
            TestInstruction::new(3, Opcode::Jmp(Address::new(2))),
        ];

        let disasm = TestDisassembler::new();
        let cfg = ControlFlowGraph::new(&insts, &disasm);

        assert_eq!(3, cfg.graph.edge_count());
        assert_eq!(3, cfg.graph.node_count());

        let root_idx = *cfg.block_finder.get(&Address::new(0)).unwrap();
        let ret_idx = *cfg.block_finder.get(&Address::new(2)).unwrap();
        let goto_idx = *cfg.block_finder.get(&Address::new(3)).unwrap();

        let neighbours = cfg.graph.neighbors(root_idx).into_iter().collect();
        assert_neighbours(neighbours, vec![ret_idx, goto_idx]);

        let neighbours = cfg.graph.neighbors(ret_idx).into_iter().collect();
        assert_neighbours(neighbours, vec![]);

        let neighbours = cfg.graph.neighbors(goto_idx).into_iter().collect();
        assert_neighbours(neighbours, vec![ret_idx]);
    }

    #[test]
    fn build_cfg_with_branch_ending() {
        let insts = [
            TestInstruction::new(0, Opcode::Add),
            TestInstruction::new(1, Opcode::CJmp(Address::new(0))),
        ];

        let disasm = TestDisassembler::new();
        let cfg = ControlFlowGraph::new(&insts, &disasm);

        assert_eq!(1, cfg.graph.edge_count());
        assert_eq!(1, cfg.graph.node_count());

        let root_idx = *cfg.block_finder.get(&Address::new(0)).unwrap();

        let neighbours = cfg.graph.neighbors(root_idx).into_iter().collect();
        assert_neighbours(neighbours, vec![root_idx]);
    }

    #[test]
    fn build_cfg_with_call_ending() {
        let insts = [
            TestInstruction::new(0, Opcode::Add),
            TestInstruction::new(1, Opcode::Call(Address::new(0))),
        ];

        let disasm = TestDisassembler::new();
        let cfg = ControlFlowGraph::new(&insts, &disasm);

        assert_eq!(0, cfg.graph.edge_count());
        assert_eq!(1, cfg.graph.node_count());
    }

    #[test]
    fn build_cfg_with_call_before_return() {
        let insts = [
            TestInstruction::new(0, Opcode::Add),
            TestInstruction::new(1, Opcode::Call(Address::new(0))),
            TestInstruction::new(2, Opcode::Ret),
        ];

        let disasm = TestDisassembler::new();
        let cfg = ControlFlowGraph::new(&insts, &disasm);

        assert_eq!(1, cfg.graph.edge_count());
        assert_eq!(2, cfg.graph.node_count());

        let root_idx = *cfg.block_finder.get(&Address::new(0)).unwrap();
        let ret_idx = *cfg.block_finder.get(&Address::new(2)).unwrap();

        let neighbours = cfg.graph.neighbors(root_idx).into_iter().collect();
        assert_neighbours(neighbours, vec![ret_idx]);
    }

    #[test]
    fn build_cfg_with_regular_instruction_ending() {
        let insts = [
            TestInstruction::new(0, Opcode::Add),
            TestInstruction::new(1, Opcode::Jmp(Address::new(2))),
            TestInstruction::new(2, Opcode::Add),
        ];

        let disasm = TestDisassembler::new();
        let cfg = ControlFlowGraph::new(&insts, &disasm);

        assert_eq!(1, cfg.graph.edge_count());
        assert_eq!(2, cfg.graph.node_count());

        let root_idx = *cfg.block_finder.get(&Address::new(0)).unwrap();
        let add_idx = *cfg.block_finder.get(&Address::new(2)).unwrap();

        let neighbours = cfg.graph.neighbors(root_idx).into_iter().collect();
        assert_neighbours(neighbours, vec![add_idx]);
    }

    fn assert_neighbours(actual: Vec<NodeIndex>, expected: Vec<NodeIndex>) {
        let actual_set: HashSet<NodeIndex> = actual.into_iter().collect();
        let expected_set: HashSet<NodeIndex> = expected.into_iter().collect();

        let diff = actual_set.difference(&expected_set);

        if diff.count() != 0 {
            assert_eq!(actual_set, expected_set);
        }
    }
}
<|endoftext|>