<commit_before>extern crate sync;
extern crate geom;

use std::os::args;
use std::path::Path;
use std::io::{File, TempDir, stdio};
use geom::point::Point2D;
use gdal::proj::{Proj, DEG_TO_RAD};
use gdal::dataset::{Dataset, open};
use gdal::driver::get_driver;

static webmerc_limit: f64 = 20037508.342789244;

fn as_point((x, y): (f64, f64)) -> Point2D<f64> {
    return Point2D(x, y);
}

fn mul<T:Clone + Mul<T,T>>(value: &Point2D<T>, factor: T) -> Point2D<T> {
    return Point2D(value.x * factor, value.y * factor);
}

pub fn tile(source: Dataset, (x, y, z): (int, int, int)) -> ~[u8] {
    let memory_driver = get_driver("MEM").unwrap();
    let png_driver = get_driver("PNG").unwrap();

    let wgs84 = Proj::new("+proj=longlat +datum=WGS84 +no_defs").unwrap();
    let webmerc = Proj::new(
        "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 " +
        "+y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  +no_defs").unwrap();

    let tile = Point2D(x, y);
    let tile_size = (webmerc_limit * 4.) / ((2 << z) as f64);
    let tile_min = Point2D(
        tile_size * (tile.x as f64) - webmerc_limit,
        webmerc_limit - tile_size * (tile.y as f64));
    let tile_max = tile_min + Point2D(tile_size, -tile_size);
    let nw = mul(&as_point(webmerc.project(&wgs84, tile_min.x, tile_min.y)), 1./DEG_TO_RAD);
    let se = mul(&as_point(webmerc.project(&wgs84, tile_max.x, tile_max.y)), 1./DEG_TO_RAD);

    let (width, height) = source.get_raster_size();
    let source_bounds = Point2D(width as f64, height as f64);
    assert!(stdio::stderr().write(format!(
        "size: {}, bands: {}\n",
        (width, height),
        source.get_raster_count()
    ).as_bytes()).is_ok());

    fn xy(lng_lat: &Point2D<f64>, source_bounds: &Point2D<f64>) -> Point2D<f64> {
        let x = (lng_lat.x + 180.) / 360. * source_bounds.x;
        let y = (90. - lng_lat.y) / 180. * source_bounds.y;
        return Point2D(x, y);
    }

    let tile = memory_driver.create("", 256, 256, 3).unwrap();
    for band in range(1, 4) {
        let xy_min = xy(&nw, &source_bounds);
        let xy_max = xy(&se, &source_bounds);
        let xy_bounds = xy_max - xy_min;

        let raster = source.read_raster(
            band,
            xy_min.x as int,
            xy_min.y as int,
            xy_bounds.x as uint,
            xy_bounds.y as uint,
            256,
            256
        );
        tile.write_raster(band, 0, 0, 256, 256, raster);
    }

    let tmp = TempDir::new("rustile").unwrap();
    let tile_path = tmp.path().join("tile.png");
    tile.create_copy(png_driver, tile_path.as_str().unwrap());
    return File::open(&tile_path).read_to_end().unwrap();
}

fn main() {
    let source = open(&Path::new(args()[1])).unwrap();
    let tile_data = tile(source, (8, 5, 4));
    assert!(stdio::stdout_raw().write(tile_data).is_ok());
}
<commit_msg>don't print out messages<commit_after>extern crate sync;
extern crate geom;

use std::os::args;
use std::path::Path;
use std::io::{File, TempDir, stdio};
use geom::point::Point2D;
use gdal::proj::{Proj, DEG_TO_RAD};
use gdal::dataset::{Dataset, open};
use gdal::driver::get_driver;

static webmerc_limit: f64 = 20037508.342789244;

fn as_point((x, y): (f64, f64)) -> Point2D<f64> {
    return Point2D(x, y);
}

fn mul<T:Clone + Mul<T,T>>(value: &Point2D<T>, factor: T) -> Point2D<T> {
    return Point2D(value.x * factor, value.y * factor);
}

pub fn tile(source: Dataset, (x, y, z): (int, int, int)) -> ~[u8] {
    let memory_driver = get_driver("MEM").unwrap();
    let png_driver = get_driver("PNG").unwrap();

    let wgs84 = Proj::new("+proj=longlat +datum=WGS84 +no_defs").unwrap();
    let webmerc = Proj::new(
        "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 " +
        "+y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  +no_defs").unwrap();

    let tile = Point2D(x, y);
    let tile_size = (webmerc_limit * 4.) / ((2 << z) as f64);
    let tile_min = Point2D(
        tile_size * (tile.x as f64) - webmerc_limit,
        webmerc_limit - tile_size * (tile.y as f64));
    let tile_max = tile_min + Point2D(tile_size, -tile_size);
    let nw = mul(&as_point(webmerc.project(&wgs84, tile_min.x, tile_min.y)), 1./DEG_TO_RAD);
    let se = mul(&as_point(webmerc.project(&wgs84, tile_max.x, tile_max.y)), 1./DEG_TO_RAD);

    let (width, height) = source.get_raster_size();
    let source_bounds = Point2D(width as f64, height as f64);

    fn xy(lng_lat: &Point2D<f64>, source_bounds: &Point2D<f64>) -> Point2D<f64> {
        let x = (lng_lat.x + 180.) / 360. * source_bounds.x;
        let y = (90. - lng_lat.y) / 180. * source_bounds.y;
        return Point2D(x, y);
    }

    let tile = memory_driver.create("", 256, 256, 3).unwrap();
    for band in range(1, 4) {
        let xy_min = xy(&nw, &source_bounds);
        let xy_max = xy(&se, &source_bounds);
        let xy_bounds = xy_max - xy_min;

        let raster = source.read_raster(
            band,
            xy_min.x as int,
            xy_min.y as int,
            xy_bounds.x as uint,
            xy_bounds.y as uint,
            256,
            256
        );
        tile.write_raster(band, 0, 0, 256, 256, raster);
    }

    let tmp = TempDir::new("rustile").unwrap();
    let tile_path = tmp.path().join("tile.png");
    tile.create_copy(png_driver, tile_path.as_str().unwrap());
    return File::open(&tile_path).read_to_end().unwrap();
}

fn main() {
    let source = open(&Path::new(args()[1])).unwrap();
    let tile_data = tile(source, (8, 5, 4));
    assert!(stdio::stdout_raw().write(tile_data).is_ok());
}
<|endoftext|>


<commit_before>///
/// Implements the force-directed graph layout algorithm as
/// proposed by Fruchterman and Reingold [1].
///
/// [1]: http://emr.cs.iit.edu/~reingold/force-directed.pdf
///

use super::{P2d, Vector};

// k_s == l
#[inline]
fn attractive_force<V>(p1: &V, p2: &V, k_s: f32) -> V
    where V: Vector<Scalar = f32>
{
    let force = p1.sub(p2);
    let length = force.length_squared().sqrt();
    let strength = length / k_s;
    return force.scale(strength);
}

// k_r == l^2
#[inline]
fn repulsive_force<V>(p1: &V, p2: &V, k_r: f32) -> V
    where V: Vector<Scalar = f32>
{
    let force = p1.sub(p2);
    let length_squared = force.length_squared();
    if length_squared > 0.0 {
        let strength = k_r / length_squared;
        return force.scale(strength);
    }
    return force;
}

pub trait ForceDirected<V> where V: Vector<Scalar = f32>
{
    fn reset_forces(&mut self);

    // adds result of `f` to force1 and substracts from force2
    fn update_force_each_node_pair<F: Fn(&V, &V) -> V>(&mut self, f: F);

    // adds result of `f` to force1 and substracts from force2
    fn update_force_each_edge<F: Fn(&V, &V) -> V>(&mut self, f: F);

    fn update_positions<F: FnMut(&V, &V) -> V>(&mut self, f: F);
}

fn iterate<V, FD>(fd: &mut FD, step: f32, k_r: f32, k_s: f32, min_pos: &V, max_pos: &V) -> f32
    where V: Vector<Scalar = f32>,
          FD: ForceDirected<V>
{

    // Reset all forces to zero.
    fd.reset_forces();

    // Calculate repulsive force between all pairs.
    fd.update_force_each_node_pair(|pos1, pos2| repulsive_force(pos1, pos2, k_r));

    // Calculate spring force between adjacent pairs (edges).
    fd.update_force_each_edge(|pos1, pos2| attractive_force(pos1, pos2, k_s).scale(-1.0));

    // update positions
    let mut sum_distance = 0.0;

    fd.update_positions(|position, force| {
        let mut new_pos = position.clone();

        let length = force.length_squared().sqrt();
        if length > 0.0 {
            new_pos.add_scaled(step / length, &force);

            // add up the moved distance. we move by step.
            sum_distance += step;
        }
        new_pos.clip_within(min_pos, max_pos)
    });

    return sum_distance;
}

pub fn layout<V, FD, F>(fd: &mut FD,
                        step_fn: F,
                        max_iter: usize,
                        converge_eps: f32,
                        k_r: f32,
                        k_s: f32,
                        min_pos: &V,
                        max_pos: &V)
    where V: Vector<Scalar = f32>,
          FD: ForceDirected<V>,
          F: Fn(usize) -> f32
{
    let mut iter: usize = 0;
    while iter < max_iter {
        let step = step_fn(iter);
        iter += 1;

        let dist_moved = iterate(fd, step, k_r, k_s, min_pos, max_pos);
        if dist_moved < converge_eps {
            break;
        }
    }
}

struct Layout<'a, 'b, V: 'a> {
    forces: Vec<V>,
    node_positions: &'a mut Vec<V>,
    node_neighbors: &'b [Vec<usize>],
}

impl<'a, 'b, V> Layout<'a, 'b, V> where V: Vector<Scalar = f32>
{
    fn new<'c, 'd>(node_positions: &'c mut Vec<V>,
                   node_neighbors: &'d [Vec<usize>])
                   -> Layout<'c, 'd, V> {
        let n = node_positions.len();
        assert!(node_neighbors.len() == n);
        Layout {
            forces: (0..n).map(|_| V::new()).collect(), // initialize forces
            node_positions: node_positions,
            node_neighbors: node_neighbors,
        }
    }
}

impl<'a, 'b, V> ForceDirected<V> for Layout<'a, 'b, V> where V: Vector<Scalar = f32>
{
    fn reset_forces(&mut self) {
        for f in self.forces.iter_mut() {
            f.reset();
        }
    }

    fn update_force_each_node_pair<F: Fn(&V, &V) -> V>(&mut self, f: F) {
        let n = self.node_positions.len();
        assert!(n == self.forces.len());

        for i1 in 0..n - 1 {
            for i2 in i1 + 1..n {
                let force = f(&self.node_positions[i1], &self.node_positions[i2]);
                self.forces[i1].add_scaled(1.0, &force);
                self.forces[i2].add_scaled(-1.0, &force);
            }
        }
    }

    fn update_force_each_edge<F: Fn(&V, &V) -> V>(&mut self, f: F) {
        let n = self.node_positions.len();
        assert!(n == self.forces.len());

        for i1 in 0..n {
            for &i2 in self.node_neighbors[i1].iter() {
                let force = f(&self.node_positions[i1], &self.node_positions[i2]);
                self.forces[i1].add_scaled(1.0, &force);
                self.forces[i2].add_scaled(-1.0, &force);
            }
        }
    }

    fn update_positions<F: FnMut(&V, &V) -> V>(&mut self, mut f: F) {
        let n = self.node_positions.len();
        assert!(n == self.forces.len());

        for i in 0..n {
            let new_pos = f(&self.node_positions[i], &self.forces[i]);
            self.node_positions[i] = new_pos;
        }
    }
}

pub fn layout_typical_2d<'a, 'b>(l: Option<f32>,
                                 node_positions: &'a mut Vec<P2d>,
                                 node_neighbors: &'b [Vec<usize>]) {
    let n = node_positions.len();
    assert!(node_neighbors.len() == n);

    const MAX_ITER: usize = 300;
    const EPS: f32 = 0.01;

    let temp = 0.1f32;
    let dt = temp / (MAX_ITER as f32);
    let min_pos = P2d(0.0, 0.0);
    let max_pos = P2d(1.0, 1.0);
    let step_fn = |iter| temp - (iter as f32 * dt);

    // `l`: ideal length of spring
    let l: f32 = l.unwrap_or((1.0 / n as f32).sqrt());

    let k_r = l * l;
    let k_s = l;

    let mut lay = Layout::new(node_positions, node_neighbors);
    layout(&mut lay,
           step_fn,
           MAX_ITER,
           EPS,
           k_r,
           k_s,
           &min_pos,
           &max_pos);
}
<commit_msg>Allow locking node positions<commit_after>///
/// Implements the force-directed graph layout algorithm as
/// proposed by Fruchterman and Reingold [1].
///
/// [1]: http://emr.cs.iit.edu/~reingold/force-directed.pdf
///

use super::{P2d, Vector};

// k_s == l
#[inline]
fn attractive_force<V>(p1: &V, p2: &V, k_s: f32) -> V
    where V: Vector<Scalar = f32>
{
    let force = p1.sub(p2);
    let length = force.length_squared().sqrt();
    let strength = length / k_s;
    return force.scale(strength);
}

// k_r == l^2
#[inline]
fn repulsive_force<V>(p1: &V, p2: &V, k_r: f32) -> V
    where V: Vector<Scalar = f32>
{
    let force = p1.sub(p2);
    let length_squared = force.length_squared();
    if length_squared > 0.0 {
        let strength = k_r / length_squared;
        return force.scale(strength);
    }
    return force;
}

pub trait ForceDirected<V> where V: Vector<Scalar = f32>
{
    fn reset_forces(&mut self);

    // adds result of `f` to force1 and substracts from force2
    fn update_force_each_node_pair<F: Fn(&V, &V) -> V>(&mut self, f: F);

    // adds result of `f` to force1 and substracts from force2
    fn update_force_each_edge<F: Fn(&V, &V) -> V>(&mut self, f: F);

    fn update_positions<F: FnMut(&V, &V) -> V>(&mut self, f: F);
}

fn iterate<V, FD>(fd: &mut FD, step: f32, k_r: f32, k_s: f32, min_pos: &V, max_pos: &V) -> f32
    where V: Vector<Scalar = f32>,
          FD: ForceDirected<V>
{

    // Reset all forces to zero.
    fd.reset_forces();

    // Calculate repulsive force between all pairs.
    fd.update_force_each_node_pair(|pos1, pos2| repulsive_force(pos1, pos2, k_r));

    // Calculate spring force between adjacent pairs (edges).
    fd.update_force_each_edge(|pos1, pos2| attractive_force(pos1, pos2, k_s).scale(-1.0));

    // update positions
    let mut sum_distance = 0.0;

    fd.update_positions(|position, force| {
        let mut new_pos = position.clone();

        let length = force.length_squared().sqrt();
        if length > 0.0 {
            new_pos.add_scaled(step / length, &force);

            // add up the moved distance. we move by step.
            sum_distance += step;
        }
        new_pos.clip_within(min_pos, max_pos)
    });

    return sum_distance;
}

pub fn layout<V, FD, F>(fd: &mut FD,
                        step_fn: F,
                        max_iter: usize,
                        converge_eps: f32,
                        k_r: f32,
                        k_s: f32,
                        min_pos: &V,
                        max_pos: &V)
    where V: Vector<Scalar = f32>,
          FD: ForceDirected<V>,
          F: Fn(usize) -> f32
{
    let mut iter: usize = 0;
    while iter < max_iter {
        let step = step_fn(iter);
        iter += 1;

        let dist_moved = iterate(fd, step, k_r, k_s, min_pos, max_pos);
        if dist_moved < converge_eps {
            break;
        }
    }
}

struct Layout<'a, 'b, V: 'a> {
    forces: Vec<V>,
    node_positions: &'a mut Vec<V>,
    node_neighbors: &'b [Vec<usize>],
    lock_first_n_positions: usize,
}

impl<'a, 'b, V> Layout<'a, 'b, V>
    where V: Vector<Scalar = f32>
{
    fn new<'c, 'd>(node_positions: &'c mut Vec<V>,
                   node_neighbors: &'d [Vec<usize>])
                   -> Layout<'c, 'd, V> {
        let n = node_positions.len();
        assert!(node_neighbors.len() == n);
        Layout {
            forces: (0..n).map(|_| V::new()).collect(), // initialize forces
            node_positions: node_positions,
            node_neighbors: node_neighbors,
            lock_first_n_positions: 0,
        }
    }

    fn lock_first_n_positions(&mut self, n: usize) {
        self.lock_first_n_positions = n;
    }
}

impl<'a, 'b, V> ForceDirected<V> for Layout<'a, 'b, V>
    where V: Vector<Scalar = f32>
{
    fn reset_forces(&mut self) {
        for f in self.forces.iter_mut() {
            f.reset();
        }
    }

    fn update_force_each_node_pair<F: Fn(&V, &V) -> V>(&mut self, f: F) {
        let n = self.node_positions.len();
        assert!(n == self.forces.len());

        for i1 in 0..n - 1 {
            for i2 in i1 + 1..n {
                let force = f(&self.node_positions[i1], &self.node_positions[i2]);
                self.forces[i1].add_scaled(1.0, &force);
                self.forces[i2].add_scaled(-1.0, &force);
            }
        }
    }

    fn update_force_each_edge<F: Fn(&V, &V) -> V>(&mut self, f: F) {
        let n = self.node_positions.len();
        assert!(n == self.forces.len());

        for i1 in 0..n {
            for &i2 in self.node_neighbors[i1].iter() {
                let force = f(&self.node_positions[i1], &self.node_positions[i2]);
                self.forces[i1].add_scaled(1.0, &force);
                self.forces[i2].add_scaled(-1.0, &force);
            }
        }
    }

    fn update_positions<F: FnMut(&V, &V) -> V>(&mut self, mut f: F) {
        let n = self.node_positions.len();
        assert!(n == self.forces.len());

        for i in self.lock_first_n_positions..n {
            let new_pos = f(&self.node_positions[i], &self.forces[i]);
            self.node_positions[i] = new_pos;
        }
    }
}

pub fn layout_typical_2d<'a, 'b>(l: Option<f32>,
                                 node_positions: &'a mut Vec<P2d>,
                                 node_neighbors: &'b [Vec<usize>],
                                 lock_first_n_positions: usize) {
    let n = node_positions.len();
    assert!(node_neighbors.len() == n);

    const MAX_ITER: usize = 300;
    const EPS: f32 = 0.01;

    let temp = 0.1f32;
    let dt = temp / (MAX_ITER as f32);
    let min_pos = P2d(0.0, 0.0);
    let max_pos = P2d(1.0, 1.0);
    let step_fn = |iter| temp - (iter as f32 * dt);

    // `l`: ideal length of spring
    let l: f32 = l.unwrap_or((1.0 / n as f32).sqrt());

    let k_r = l * l;
    let k_s = l;

    let mut lay = Layout::new(node_positions, node_neighbors);
    lay.lock_first_n_positions(lock_first_n_positions);
    layout(&mut lay,
           step_fn,
           MAX_ITER,
           EPS,
           k_r,
           k_s,
           &min_pos,
           &max_pos);
}
<|endoftext|>


<commit_before>use fuse::{ReplyAttr, ReplyData, ReplyEntry, ReplyDirectory, Request};
use libc::ENOENT;

use fs::constants;
use fs::GoodDataFS;
use fs::helpers::create_inode_file_attributes;
use object;

mod feature_flags;
mod ldm;
mod metadata;
mod project_json;
mod user_permissions;
mod user_roles;

use super::item;
use super::project;

use super::super::inode;

use std::path::Path;

pub const PROJECT_FILES: [item::ProjectItem; 4] =
    [feature_flags::ITEM, project_json::ITEM, user_permissions::ITEM, user_roles::ITEM];

pub const PROJECT_DIRS: [item::ProjectItem; 2] = [ldm::ITEM, metadata::ITEM];

pub const PROJECT_ITEMS: [item::ProjectItem; 6] = [feature_flags::ITEM,
                                                   project_json::ITEM,
                                                   user_permissions::ITEM,
                                                   user_roles::ITEM,
                                                   ldm::ITEM,
                                                   metadata::ITEM];

/// Gets project from inode
pub fn project_from_inode<Type: Into<inode::Inode>>(fs: &GoodDataFS, ino: Type) -> object::Project {
    let inode = ino.into();
    let pid = (inode.project - 1) as usize;

    fs.client().projects().as_ref().unwrap()[pid].clone()
}

pub fn getattr(fs: &mut GoodDataFS, req: &Request, ino: u64, reply: ReplyAttr) {
    let inode = inode::Inode::deserialize(ino);

    if inode.project > 0 {
        match inode.category {
            x if x == constants::Category::Internal as u8 => {
                let reserved = constants::ReservedFile::from(inode.reserved);
                match reserved {
                    constants::ReservedFile::FeatureFlagsJson => {
                        (feature_flags::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::ProjectJson => {
                        (project_json::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::PermissionsJson => {
                        (user_permissions::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::RolesJson => {
                        (user_roles::ITEM.getattr)(fs, req, ino, reply)
                    }
                    _ => reply.error(ENOENT),
                }
            }
            x if x == constants::Category::Ldm as u8 => (ldm::ITEM.getattr)(fs, req, ino, reply),
            x if x == constants::Category::Metadata as u8 => {
                (metadata::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataAttributes as u8 => {
                (metadata::attributes::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataAttributes as u8 => {
                if inode.reserved == constants::ReservedFile::KeepMe as u8 {
                    (metadata::attributes::ITEM.getattr)(fs, req, ino, reply)
                } else if inode.reserved == 0 {
                    // JSON ATTRIBUTE
                    let project: &object::Project = &project_from_inode(fs, ino);

                    let fact = &project.attributes(&mut fs.client.connector, false)
                        .objects
                        .items[inode.item as usize];

                    let json: String = fact.clone().into();
                    let attr = create_inode_file_attributes(ino,
                                                            json.len() as u64,
                                                            constants::DEFAULT_CREATE_TIME);
                    reply.attr(&constants::DEFAULT_TTL, &attr);
                } else {
                    reply.error(ENOENT);
                }
            }
            x if x == constants::Category::MetadataFacts as u8 => {
                if inode.reserved == constants::ReservedFile::KeepMe as u8 {
                    (metadata::facts::ITEM.getattr)(fs, req, ino, reply)
                } else if inode.reserved == 0 {
                    // JSON REPORT
                    let project: &object::Project = &project_from_inode(fs, ino);

                    let fact = &project.facts(&mut fs.client.connector, false)
                        .objects
                        .items[inode.item as usize];

                    let json: String = fact.clone().into();
                    let attr = create_inode_file_attributes(ino,
                                                            json.len() as u64,
                                                            constants::DEFAULT_CREATE_TIME);
                    reply.attr(&constants::DEFAULT_TTL, &attr);
                } else {
                    reply.error(ENOENT);
                }
            }
            x if x == constants::Category::MetadataMetrics as u8 => {
                if inode.reserved == constants::ReservedFile::KeepMe as u8 {
                    (metadata::metrics::ITEM.getattr)(fs, req, ino, reply)
                } else if inode.reserved == 0 {
                    // JSON REPORT
                    let project: &object::Project = &project_from_inode(fs, ino);

                    let metric = &project.metrics(&mut fs.client.connector, false)
                        .objects
                        .items[inode.item as usize];

                    let json: String = metric.clone().into();
                    let attr = create_inode_file_attributes(ino,
                                                            json.len() as u64,
                                                            constants::DEFAULT_CREATE_TIME);
                    reply.attr(&constants::DEFAULT_TTL, &attr);
                } else {
                    reply.error(ENOENT);
                }
            }
            x if x == constants::Category::MetadataReports as u8 => {
                if inode.reserved == constants::ReservedFile::KeepMe as u8 {
                    (metadata::reports::ITEM.getattr)(fs, req, ino, reply)
                } else if inode.reserved == 0 {
                    // JSON REPORT
                    let project: &object::Project = &project_from_inode(fs, ino);

                    let report = &project.reports(&mut fs.client.connector, false)
                        .objects
                        .items[inode.item as usize];

                    let json: String = report.clone().into();
                    let attr = create_inode_file_attributes(ino,
                                                            json.len() as u64,
                                                            constants::DEFAULT_CREATE_TIME);
                    reply.attr(&constants::DEFAULT_TTL, &attr);
                } else {
                    reply.error(ENOENT);
                }
            }
            x if x == constants::Category::MetadataReportDefinition as u8 => {
                (metadata::report_definitions::ITEM.getattr)(fs, req, ino, reply)
            }
            _ => {
                warn!("getattr() - not found!");
            }
        }
    } else {
        error!("getattr() - Not found inode {:?}", ino);
        reply.error(ENOENT);
    }
}

pub fn lookup(fs: &mut GoodDataFS, req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
    let inode = inode::Inode::deserialize(parent);

    match name.to_str() {
        Some(constants::FEATURE_FLAGS_JSON_FILENAME) => {
            (feature_flags::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_JSON_FILENAME) => {
            (project_json::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_LDM_DIR) => (ldm::ITEM.lookup)(fs, req, parent, name, reply),
        Some(constants::PROJECT_METADATA_DIR) => {
            (metadata::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(object::metadata::attribute::NAME) => {
            (metadata::attributes::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(object::metadata::fact::NAME) => {
            (metadata::facts::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(object::metadata::metric::NAME) => {
            (metadata::metrics::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(object::metadata::report::NAME) => {
            (metadata::reports::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(object::metadata::report_definition::NAME) => {
            (metadata::report_definitions::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::USER_PERMISSIONS_JSON_FILENAME) => {
            (user_permissions::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::USER_ROLES_JSON_FILENAME) => {
            (user_roles::ITEM.lookup)(fs, req, parent, name, reply)
        }
        _ => {
            if inode.category == constants::Category::MetadataAttributes as u8 &&
               inode.reserved == constants::ReservedFile::KeepMe as u8 {
                let identifier = name.to_str().unwrap().replace(".json", "");
                debug!("lookup() - Looking up parent {} - {:?}, name: {:?}, \
                          identifier: {:?}",
                       parent,
                       inode,
                       name,
                       identifier);

                let project: &object::Project = &project_from_inode(fs, parent);

                let (index, attribute) = project.attributes(&mut fs.client.connector, false)
                    .find_by_identifier(&identifier);
                debug!("{:?}", attribute);

                if !attribute.is_some() {
                    reply.error(ENOENT);
                    return;
                }

                let inode = inode::Inode {
                    project: inode.project,
                    category: constants::Category::MetadataAttributes as u8,
                    item: index,
                    reserved: 0,
                };
                let json: String = attribute.unwrap().into();
                let attr = create_inode_file_attributes(inode::Inode::serialize(&inode),
                                                        json.len() as u64,
                                                        constants::DEFAULT_CREATE_TIME);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else if inode.category == constants::Category::MetadataFacts as u8 &&
               inode.reserved == constants::ReservedFile::KeepMe as u8 {
                let identifier = name.to_str().unwrap().replace(".json", "");
                debug!("lookup() - Looking up parent {} - {:?}, name: {:?}, \
                          identifier: {:?}",
                       parent,
                       inode,
                       name,
                       identifier);

                let project: &object::Project = &project_from_inode(fs, parent);

                let (index, fact) = project.facts(&mut fs.client.connector, false)
                    .find_by_identifier(&identifier);
                debug!("{:?}", fact);

                if !fact.is_some() {
                    reply.error(ENOENT);
                    return;
                }

                let inode = inode::Inode {
                    project: inode.project,
                    category: constants::Category::MetadataFacts as u8,
                    item: index,
                    reserved: 0,
                };
                let json: String = fact.unwrap().into();
                let attr = create_inode_file_attributes(inode::Inode::serialize(&inode),
                                                        json.len() as u64,
                                                        constants::DEFAULT_CREATE_TIME);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else if inode.category == constants::Category::MetadataMetrics as u8 &&
               inode.reserved == constants::ReservedFile::KeepMe as u8 {
                let identifier = name.to_str().unwrap().replace(".json", "");
                debug!("lookup() - Looking up parent {} - {:?}, name: {:?}, \
                          identifier: {:?}",
                       parent,
                       inode,
                       name,
                       identifier);

                let project: &object::Project = &project_from_inode(fs, parent);

                let (index, fact) = project.metrics(&mut fs.client.connector, false)
                    .find_by_identifier(&identifier);
                debug!("{:?}", fact);

                if !fact.is_some() {
                    reply.error(ENOENT);
                    return;
                }

                let inode = inode::Inode {
                    project: inode.project,
                    category: constants::Category::MetadataMetrics as u8,
                    item: index,
                    reserved: 0,
                };
                let json: String = fact.unwrap().into();
                let attr = create_inode_file_attributes(inode::Inode::serialize(&inode),
                                                        json.len() as u64,
                                                        constants::DEFAULT_CREATE_TIME);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else if inode.category == constants::Category::MetadataReports as u8 &&
               inode.reserved == constants::ReservedFile::KeepMe as u8 {
                let identifier = name.to_str().unwrap().replace(".json", "");
                debug!("lookup() - Looking up parent {} - {:?}, name: {:?}, \
                          identifier: {:?}",
                       parent,
                       inode,
                       name,
                       identifier);

                let project: &object::Project = &project_from_inode(fs, parent);

                let (index, report) = project.reports(&mut fs.client.connector, false)
                    .find_by_identifier(&identifier);
                debug!("{:?}", report);

                if !report.is_some() {
                    reply.error(ENOENT);
                    return;
                }

                let inode = inode::Inode {
                    project: inode.project,
                    category: constants::Category::MetadataReports as u8,
                    item: index,
                    reserved: 0,
                };
                let json: String = report.unwrap().into();
                let attr = create_inode_file_attributes(inode::Inode::serialize(&inode),
                                                        json.len() as u64,
                                                        constants::DEFAULT_CREATE_TIME);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else {
                reply.error(ENOENT)
            }
        }
    }
}

pub fn read(fs: &mut GoodDataFS,
            _req: &Request,
            ino: u64,
            _fh: u64,
            offset: u64,
            size: u32,
            reply: ReplyData) {
    let inode = inode::Inode::deserialize(ino);
    let reserved = constants::ReservedFile::from(inode.reserved);
    match reserved {
        constants::ReservedFile::FeatureFlagsJson => {
            (feature_flags::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::ProjectJson => {
            (project_json::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::PermissionsJson => {
            (user_permissions::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::RolesJson => {
            (user_roles::ITEM.read)(fs, inode, reply, offset, size);
        }
        _ => {
            if inode.category >= constants::Category::Metadata as u8 &&
               inode.category <= constants::Category::MetadataLast as u8 {
                metadata::read(fs, inode, reply, offset, size);
            } else {
                reply.error(ENOENT);
            }
        }
    }
}

pub fn readdir(fs: &mut GoodDataFS,
               req: &Request,
               ino: u64,
               fh: u64,
               in_offset: u64,
               mut reply: ReplyDirectory) {
    let mut offset = in_offset;

    let inode = inode::Inode::deserialize(ino);
    match inode.category {
        x if x == constants::Category::Internal as u8 => {
            let projectid = inode.project - 1;

            // Iterate over all project::ITEMS
            if offset + 1 < PROJECT_ITEMS.len() as u64 {
                for item in PROJECT_ITEMS.into_iter().skip(offset as usize) {
                    item.readdir(projectid, &offset, &mut reply);
                    offset += 1;
                }
            }

            reply.ok();
        }
        x if x == constants::Category::Ldm as u8 => {
            project::ldm::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::Metadata as u8 => {
            project::metadata::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::MetadataAttributes as u8 => {
            project::metadata::attributes::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::MetadataFacts as u8 => {
            project::metadata::facts::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::MetadataMetrics as u8 => {
            project::metadata::metrics::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::MetadataReports as u8 => {
            project::metadata::reports::readdir(fs, req, ino, fh, in_offset, reply)
        }
        _ => {
            warn!("readdir() - Unknow Category {}", inode.category);
        }
    }
}
<commit_msg>Fix attributes<commit_after>use fuse::{ReplyAttr, ReplyData, ReplyEntry, ReplyDirectory, Request};
use libc::ENOENT;

use fs::constants;
use fs::GoodDataFS;
use fs::helpers::create_inode_file_attributes;
use object;

mod feature_flags;
mod ldm;
mod metadata;
mod project_json;
mod user_permissions;
mod user_roles;

use super::item;
use super::project;

use super::super::inode;

use std::path::Path;

pub const PROJECT_FILES: [item::ProjectItem; 4] =
    [feature_flags::ITEM, project_json::ITEM, user_permissions::ITEM, user_roles::ITEM];

pub const PROJECT_DIRS: [item::ProjectItem; 2] = [ldm::ITEM, metadata::ITEM];

pub const PROJECT_ITEMS: [item::ProjectItem; 6] = [feature_flags::ITEM,
                                                   project_json::ITEM,
                                                   user_permissions::ITEM,
                                                   user_roles::ITEM,
                                                   ldm::ITEM,
                                                   metadata::ITEM];

/// Gets project from inode
pub fn project_from_inode<Type: Into<inode::Inode>>(fs: &GoodDataFS, ino: Type) -> object::Project {
    let inode = ino.into();
    let pid = (inode.project - 1) as usize;

    fs.client().projects().as_ref().unwrap()[pid].clone()
}

pub fn getattr(fs: &mut GoodDataFS, req: &Request, ino: u64, reply: ReplyAttr) {
    let inode = inode::Inode::deserialize(ino);

    if inode.project > 0 {
        match inode.category {
            x if x == constants::Category::Internal as u8 => {
                let reserved = constants::ReservedFile::from(inode.reserved);
                match reserved {
                    constants::ReservedFile::FeatureFlagsJson => {
                        (feature_flags::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::ProjectJson => {
                        (project_json::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::PermissionsJson => {
                        (user_permissions::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::RolesJson => {
                        (user_roles::ITEM.getattr)(fs, req, ino, reply)
                    }
                    _ => reply.error(ENOENT),
                }
            }
            x if x == constants::Category::Ldm as u8 => (ldm::ITEM.getattr)(fs, req, ino, reply),
            x if x == constants::Category::Metadata as u8 => {
                (metadata::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataAttributes as u8 => {
                if inode.reserved == constants::ReservedFile::KeepMe as u8 {
                    (metadata::attributes::ITEM.getattr)(fs, req, ino, reply)
                } else if inode.reserved == 0 {
                    // JSON ATTRIBUTE
                    let project: &object::Project = &project_from_inode(fs, ino);

                    let fact = &project.attributes(&mut fs.client.connector, false)
                        .objects
                        .items[inode.item as usize];

                    let json: String = fact.clone().into();
                    let attr = create_inode_file_attributes(ino,
                                                            json.len() as u64,
                                                            constants::DEFAULT_CREATE_TIME);
                    reply.attr(&constants::DEFAULT_TTL, &attr);
                } else {
                    reply.error(ENOENT);
                }
            }
            x if x == constants::Category::MetadataFacts as u8 => {
                if inode.reserved == constants::ReservedFile::KeepMe as u8 {
                    (metadata::facts::ITEM.getattr)(fs, req, ino, reply)
                } else if inode.reserved == 0 {
                    // JSON REPORT
                    let project: &object::Project = &project_from_inode(fs, ino);

                    let fact = &project.facts(&mut fs.client.connector, false)
                        .objects
                        .items[inode.item as usize];

                    let json: String = fact.clone().into();
                    let attr = create_inode_file_attributes(ino,
                                                            json.len() as u64,
                                                            constants::DEFAULT_CREATE_TIME);
                    reply.attr(&constants::DEFAULT_TTL, &attr);
                } else {
                    reply.error(ENOENT);
                }
            }
            x if x == constants::Category::MetadataMetrics as u8 => {
                if inode.reserved == constants::ReservedFile::KeepMe as u8 {
                    (metadata::metrics::ITEM.getattr)(fs, req, ino, reply)
                } else if inode.reserved == 0 {
                    // JSON REPORT
                    let project: &object::Project = &project_from_inode(fs, ino);

                    let metric = &project.metrics(&mut fs.client.connector, false)
                        .objects
                        .items[inode.item as usize];

                    let json: String = metric.clone().into();
                    let attr = create_inode_file_attributes(ino,
                                                            json.len() as u64,
                                                            constants::DEFAULT_CREATE_TIME);
                    reply.attr(&constants::DEFAULT_TTL, &attr);
                } else {
                    reply.error(ENOENT);
                }
            }
            x if x == constants::Category::MetadataReports as u8 => {
                if inode.reserved == constants::ReservedFile::KeepMe as u8 {
                    (metadata::reports::ITEM.getattr)(fs, req, ino, reply)
                } else if inode.reserved == 0 {
                    // JSON REPORT
                    let project: &object::Project = &project_from_inode(fs, ino);

                    let report = &project.reports(&mut fs.client.connector, false)
                        .objects
                        .items[inode.item as usize];

                    let json: String = report.clone().into();
                    let attr = create_inode_file_attributes(ino,
                                                            json.len() as u64,
                                                            constants::DEFAULT_CREATE_TIME);
                    reply.attr(&constants::DEFAULT_TTL, &attr);
                } else {
                    reply.error(ENOENT);
                }
            }
            x if x == constants::Category::MetadataReportDefinition as u8 => {
                (metadata::report_definitions::ITEM.getattr)(fs, req, ino, reply)
            }
            _ => {
                warn!("getattr() - not found!");
            }
        }
    } else {
        error!("getattr() - Not found inode {:?}", ino);
        reply.error(ENOENT);
    }
}

pub fn lookup(fs: &mut GoodDataFS, req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
    let inode = inode::Inode::deserialize(parent);

    match name.to_str() {
        Some(constants::FEATURE_FLAGS_JSON_FILENAME) => {
            (feature_flags::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_JSON_FILENAME) => {
            (project_json::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_LDM_DIR) => (ldm::ITEM.lookup)(fs, req, parent, name, reply),
        Some(constants::PROJECT_METADATA_DIR) => {
            (metadata::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(object::metadata::attribute::NAME) => {
            (metadata::attributes::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(object::metadata::fact::NAME) => {
            (metadata::facts::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(object::metadata::metric::NAME) => {
            (metadata::metrics::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(object::metadata::report::NAME) => {
            (metadata::reports::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(object::metadata::report_definition::NAME) => {
            (metadata::report_definitions::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::USER_PERMISSIONS_JSON_FILENAME) => {
            (user_permissions::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::USER_ROLES_JSON_FILENAME) => {
            (user_roles::ITEM.lookup)(fs, req, parent, name, reply)
        }
        _ => {
            if inode.category == constants::Category::MetadataAttributes as u8 &&
               inode.reserved == constants::ReservedFile::KeepMe as u8 {
                let identifier = name.to_str().unwrap().replace(".json", "");
                debug!("lookup() - Looking up parent {} - {:?}, name: {:?}, \
                          identifier: {:?}",
                       parent,
                       inode,
                       name,
                       identifier);

                let project: &object::Project = &project_from_inode(fs, parent);

                let (index, attribute) = project.attributes(&mut fs.client.connector, false)
                    .find_by_identifier(&identifier);
                debug!("{:?}", attribute);

                if !attribute.is_some() {
                    reply.error(ENOENT);
                    return;
                }

                let inode = inode::Inode {
                    project: inode.project,
                    category: constants::Category::MetadataAttributes as u8,
                    item: index,
                    reserved: 0,
                };
                let json: String = attribute.unwrap().into();
                let attr = create_inode_file_attributes(inode::Inode::serialize(&inode),
                                                        json.len() as u64,
                                                        constants::DEFAULT_CREATE_TIME);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else if inode.category == constants::Category::MetadataFacts as u8 &&
               inode.reserved == constants::ReservedFile::KeepMe as u8 {
                let identifier = name.to_str().unwrap().replace(".json", "");
                debug!("lookup() - Looking up parent {} - {:?}, name: {:?}, \
                          identifier: {:?}",
                       parent,
                       inode,
                       name,
                       identifier);

                let project: &object::Project = &project_from_inode(fs, parent);

                let (index, fact) = project.facts(&mut fs.client.connector, false)
                    .find_by_identifier(&identifier);
                debug!("{:?}", fact);

                if !fact.is_some() {
                    reply.error(ENOENT);
                    return;
                }

                let inode = inode::Inode {
                    project: inode.project,
                    category: constants::Category::MetadataFacts as u8,
                    item: index,
                    reserved: 0,
                };
                let json: String = fact.unwrap().into();
                let attr = create_inode_file_attributes(inode::Inode::serialize(&inode),
                                                        json.len() as u64,
                                                        constants::DEFAULT_CREATE_TIME);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else if inode.category == constants::Category::MetadataMetrics as u8 &&
               inode.reserved == constants::ReservedFile::KeepMe as u8 {
                let identifier = name.to_str().unwrap().replace(".json", "");
                debug!("lookup() - Looking up parent {} - {:?}, name: {:?}, \
                          identifier: {:?}",
                       parent,
                       inode,
                       name,
                       identifier);

                let project: &object::Project = &project_from_inode(fs, parent);

                let (index, fact) = project.metrics(&mut fs.client.connector, false)
                    .find_by_identifier(&identifier);
                debug!("{:?}", fact);

                if !fact.is_some() {
                    reply.error(ENOENT);
                    return;
                }

                let inode = inode::Inode {
                    project: inode.project,
                    category: constants::Category::MetadataMetrics as u8,
                    item: index,
                    reserved: 0,
                };
                let json: String = fact.unwrap().into();
                let attr = create_inode_file_attributes(inode::Inode::serialize(&inode),
                                                        json.len() as u64,
                                                        constants::DEFAULT_CREATE_TIME);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else if inode.category == constants::Category::MetadataReports as u8 &&
               inode.reserved == constants::ReservedFile::KeepMe as u8 {
                let identifier = name.to_str().unwrap().replace(".json", "");
                debug!("lookup() - Looking up parent {} - {:?}, name: {:?}, \
                          identifier: {:?}",
                       parent,
                       inode,
                       name,
                       identifier);

                let project: &object::Project = &project_from_inode(fs, parent);

                let (index, report) = project.reports(&mut fs.client.connector, false)
                    .find_by_identifier(&identifier);
                debug!("{:?}", report);

                if !report.is_some() {
                    reply.error(ENOENT);
                    return;
                }

                let inode = inode::Inode {
                    project: inode.project,
                    category: constants::Category::MetadataReports as u8,
                    item: index,
                    reserved: 0,
                };
                let json: String = report.unwrap().into();
                let attr = create_inode_file_attributes(inode::Inode::serialize(&inode),
                                                        json.len() as u64,
                                                        constants::DEFAULT_CREATE_TIME);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else {
                reply.error(ENOENT)
            }
        }
    }
}

pub fn read(fs: &mut GoodDataFS,
            _req: &Request,
            ino: u64,
            _fh: u64,
            offset: u64,
            size: u32,
            reply: ReplyData) {
    let inode = inode::Inode::deserialize(ino);
    let reserved = constants::ReservedFile::from(inode.reserved);
    match reserved {
        constants::ReservedFile::FeatureFlagsJson => {
            (feature_flags::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::ProjectJson => {
            (project_json::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::PermissionsJson => {
            (user_permissions::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::RolesJson => {
            (user_roles::ITEM.read)(fs, inode, reply, offset, size);
        }
        _ => {
            if inode.category >= constants::Category::Metadata as u8 &&
               inode.category <= constants::Category::MetadataLast as u8 {
                metadata::read(fs, inode, reply, offset, size);
            } else {
                reply.error(ENOENT);
            }
        }
    }
}

pub fn readdir(fs: &mut GoodDataFS,
               req: &Request,
               ino: u64,
               fh: u64,
               in_offset: u64,
               mut reply: ReplyDirectory) {
    let mut offset = in_offset;

    let inode = inode::Inode::deserialize(ino);
    match inode.category {
        x if x == constants::Category::Internal as u8 => {
            let projectid = inode.project - 1;

            // Iterate over all project::ITEMS
            if offset + 1 < PROJECT_ITEMS.len() as u64 {
                for item in PROJECT_ITEMS.into_iter().skip(offset as usize) {
                    item.readdir(projectid, &offset, &mut reply);
                    offset += 1;
                }
            }

            reply.ok();
        }
        x if x == constants::Category::Ldm as u8 => {
            project::ldm::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::Metadata as u8 => {
            project::metadata::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::MetadataAttributes as u8 => {
            project::metadata::attributes::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::MetadataFacts as u8 => {
            project::metadata::facts::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::MetadataMetrics as u8 => {
            project::metadata::metrics::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::MetadataReports as u8 => {
            project::metadata::reports::readdir(fs, req, ino, fh, in_offset, reply)
        }
        _ => {
            warn!("readdir() - Unknow Category {}", inode.category);
        }
    }
}
<|endoftext|>


<commit_before>use core::mem;
use {Int, LargeInt};

/// Returns `n / d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 {
    // Special cases
    if d == 0 {
        panic!("Division by zero");
    }

    if n == 0 {
        return 0;
    }

    let mut sr = d.leading_zeros().wrapping_sub(n.leading_zeros());

    // d > n
    if sr > u32::bits() - 1 {
        return 0;
    }

    // d == 1
    if sr == u32::bits() - 1 {
        return n;
    }

    sr += 1;

    // 1 <= sr <= u32::bits() - 1
    let mut q = n << (u32::bits() - sr);
    let mut r = n >> sr;

    let mut carry = 0;
    for _ in 0..sr {
        // r:q = ((r:q) << 1) | carry
        r = (r << 1) | (q >> (u32::bits() - 1));
        q = (q << 1) | carry;

        // carry = 0;
        // if r > d {
        //     r -= d;
        //     carry = 1;
        // }

        let s = (d.wrapping_sub(r).wrapping_sub(1)) as i32 >> (u32::bits() - 1);
        carry = (s & 1) as u32;
        r -= d & s as u32;
    }

    (q << 1) | carry
}

/// Returns `n % d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __umodsi3(n: u32, d: u32) -> u32 {
    n - __udivsi3(n, d) * d
}

/// Returns `n / d` and sets `*rem = n % d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
    let q = __udivsi3(n, d);
    if let Some(rem) = rem {
        *rem = n - (q * d);
    }
    q
}

/// Returns `n / d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __udivdi3(n: u64, d: u64) -> u64 {
    __udivmoddi4(n, d, None)
}

/// Returns `n % d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __umoddi3(a: u64, b: u64) -> u64 {
    let mut rem = unsafe { mem::uninitialized() };
    __udivmoddi4(a, b, Some(&mut rem));
    rem
}

/// Returns `n / d` and sets `*rem = n % d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
    // NOTE X is unknown, K != 0
    if n.high() == 0 {
        if d.high() == 0 {
            // 0 X
            // ---
            // 0 X
            if let Some(rem) = rem {
                *rem = u64::from(n.low() % d.low());
            }
            return u64::from(n.low() / d.low());
        } else {
            // 0 X
            // ---
            // K X
            if let Some(rem) = rem {
                *rem = n;
            }
            return 0;
        };
    }

    let mut sr;
    let mut q;
    let mut r;

    if d.low() == 0 {
        if d.high() == 0 {
            // K X
            // ---
            // 0 0
            panic!("Division by zero");
        }

        if n.low() == 0 {
            // K 0
            // ---
            // K 0
            if let Some(rem) = rem {
                *rem = u64::from_parts(0, n.high() % d.high());
            }
            return u64::from(n.high() / d.high());
        }

        // K K
        // ---
        // K 0

        if d.high().is_power_of_two() {
            if let Some(rem) = rem {
                *rem = u64::from_parts(n.low(), n.high() & (d.high() - 1));
            }
            return u64::from(n.high() >> d.high().trailing_zeros());
        }

        sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros());

        // D > N
        if sr > u32::bits() - 2 {
            if let Some(rem) = rem {
                *rem = n;
            }
            return 0;
        }

        sr += 1;

        // 1 <= sr <= u32::bits() - 1
        q = n << (u64::bits() - sr);
        r = n >> sr;
    } else {
        if d.high() == 0 {
            // K X
            // ---
            // 0 K
            if d.low().is_power_of_two() {
                if let Some(rem) = rem {
                    *rem = u64::from(n.low() & (d.low() - 1));
                }

                if d.low() == 1 {
                    return n;
                } else {
                    let sr = d.low().trailing_zeros();
                    return n >> sr;
                };
            }

            sr = 1 + u32::bits() + d.low().leading_zeros() - n.high().leading_zeros();

            // 2 <= sr <= u64::bits() - 1
            q = n << (u64::bits() - sr);
            r = n >> sr;
        } else {
            // K X
            // ---
            // K K
            sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros());

            // D > N
            if sr > u32::bits() - 1 {
                if let Some(rem) = rem {
                    *rem = n;
                    return 0;
                }
            }

            sr += 1;

            // 1 <= sr <= u32::bits()
            q = n << (u64::bits() - sr);
            r = n >> sr;
        }
    }

    // Not a special case
    // q and r are initialized with
    // q = n << (u64::bits() - sr)
    // r = n >> sr
    // 1 <= sr <= u64::bits() - 1
    let mut carry = 0;

    for _ in 0..sr {
        // r:q = ((r:q) << 1) | carry
        r = (r << 1) | (q >> (u64::bits() - 1));
        q = (q << 1) | carry as u64;

        // carry = 0
        // if r >= d {
        //     r -= d;
        //     carry = 1;
        // }
        let s = (d.wrapping_sub(r).wrapping_sub(1)) as i64 >> (u64::bits() - 1);
        carry = (s & 1) as u32;
        r -= d & s as u64;
    }

    if let Some(rem) = rem {
        *rem = r;
    }
    (q << 1) | carry as u64
}

#[cfg(test)]
mod tests {
    use quickcheck::TestResult;
    use qc::{U32, U64};

    quickcheck!{
        fn udivdi3(n: U64, d: U64) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let q = super::__udivdi3(n, d);
                TestResult::from_bool(q == n / d)
            }
        }

        fn umoddi3(n: U64, d: U64) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let r = super::__umoddi3(n, d);
                TestResult::from_bool(r == n % d)
            }
        }

        fn udivmoddi4(n: U64, d: U64) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let mut r = 0;
                let q = super::__udivmoddi4(n, d, Some(&mut r));
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }

        fn udivsi3(n: U32, d: U32) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let q = super::__udivsi3(n, d);
                TestResult::from_bool(q == n / d)
            }
        }

        fn umodsi3(n: U32, d: U32) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let r = super::__umodsi3(n, d);
                TestResult::from_bool(r == n % d)
            }
        }

        fn udivmodsi4(n: U32, d: U32) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let mut r = 0;
                let q = super::__udivmodsi4(n, d, Some(&mut r));
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }
    }
}
<commit_msg>fix a bug in udivmoddi4<commit_after>use core::mem;
use {Int, LargeInt};

/// Returns `n / d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 {
    // Special cases
    if d == 0 {
        panic!("Division by zero");
    }

    if n == 0 {
        return 0;
    }

    let mut sr = d.leading_zeros().wrapping_sub(n.leading_zeros());

    // d > n
    if sr > u32::bits() - 1 {
        return 0;
    }

    // d == 1
    if sr == u32::bits() - 1 {
        return n;
    }

    sr += 1;

    // 1 <= sr <= u32::bits() - 1
    let mut q = n << (u32::bits() - sr);
    let mut r = n >> sr;

    let mut carry = 0;
    for _ in 0..sr {
        // r:q = ((r:q) << 1) | carry
        r = (r << 1) | (q >> (u32::bits() - 1));
        q = (q << 1) | carry;

        // carry = 0;
        // if r > d {
        //     r -= d;
        //     carry = 1;
        // }

        let s = (d.wrapping_sub(r).wrapping_sub(1)) as i32 >> (u32::bits() - 1);
        carry = (s & 1) as u32;
        r -= d & s as u32;
    }

    (q << 1) | carry
}

/// Returns `n % d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __umodsi3(n: u32, d: u32) -> u32 {
    n - __udivsi3(n, d) * d
}

/// Returns `n / d` and sets `*rem = n % d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
    let q = __udivsi3(n, d);
    if let Some(rem) = rem {
        *rem = n - (q * d);
    }
    q
}

/// Returns `n / d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __udivdi3(n: u64, d: u64) -> u64 {
    __udivmoddi4(n, d, None)
}

/// Returns `n % d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __umoddi3(a: u64, b: u64) -> u64 {
    let mut rem = unsafe { mem::uninitialized() };
    __udivmoddi4(a, b, Some(&mut rem));
    rem
}

/// Returns `n / d` and sets `*rem = n % d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
    // NOTE X is unknown, K != 0
    if n.high() == 0 {
        if d.high() == 0 {
            // 0 X
            // ---
            // 0 X
            if let Some(rem) = rem {
                *rem = u64::from(n.low() % d.low());
            }
            return u64::from(n.low() / d.low());
        } else {
            // 0 X
            // ---
            // K X
            if let Some(rem) = rem {
                *rem = n;
            }
            return 0;
        };
    }

    let mut sr;
    let mut q;
    let mut r;

    if d.low() == 0 {
        if d.high() == 0 {
            // K X
            // ---
            // 0 0
            panic!("Division by zero");
        }

        if n.low() == 0 {
            // K 0
            // ---
            // K 0
            if let Some(rem) = rem {
                *rem = u64::from_parts(0, n.high() % d.high());
            }
            return u64::from(n.high() / d.high());
        }

        // K K
        // ---
        // K 0

        if d.high().is_power_of_two() {
            if let Some(rem) = rem {
                *rem = u64::from_parts(n.low(), n.high() & (d.high() - 1));
            }
            return u64::from(n.high() >> d.high().trailing_zeros());
        }

        sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros());

        // D > N
        if sr > u32::bits() - 2 {
            if let Some(rem) = rem {
                *rem = n;
            }
            return 0;
        }

        sr += 1;

        // 1 <= sr <= u32::bits() - 1
        q = n << (u64::bits() - sr);
        r = n >> sr;
    } else {
        if d.high() == 0 {
            // K X
            // ---
            // 0 K
            if d.low().is_power_of_two() {
                if let Some(rem) = rem {
                    *rem = u64::from(n.low() & (d.low() - 1));
                }

                if d.low() == 1 {
                    return n;
                } else {
                    let sr = d.low().trailing_zeros();
                    return n >> sr;
                };
            }

            sr = 1 + u32::bits() + d.low().leading_zeros() - n.high().leading_zeros();

            // 2 <= sr <= u64::bits() - 1
            q = n << (u64::bits() - sr);
            r = n >> sr;
        } else {
            // K X
            // ---
            // K K
            sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros());

            // D > N
            if sr > u32::bits() - 1 {
                if let Some(rem) = rem {
                    *rem = n;
                }
                return 0;
            }

            sr += 1;

            // 1 <= sr <= u32::bits()
            q = n << (u64::bits() - sr);
            r = n >> sr;
        }
    }

    // Not a special case
    // q and r are initialized with
    // q = n << (u64::bits() - sr)
    // r = n >> sr
    // 1 <= sr <= u64::bits() - 1
    let mut carry = 0;

    for _ in 0..sr {
        // r:q = ((r:q) << 1) | carry
        r = (r << 1) | (q >> (u64::bits() - 1));
        q = (q << 1) | carry as u64;

        // carry = 0
        // if r >= d {
        //     r -= d;
        //     carry = 1;
        // }
        let s = (d.wrapping_sub(r).wrapping_sub(1)) as i64 >> (u64::bits() - 1);
        carry = (s & 1) as u32;
        r -= d & s as u64;
    }

    if let Some(rem) = rem {
        *rem = r;
    }
    (q << 1) | carry as u64
}

#[cfg(test)]
mod tests {
    use quickcheck::TestResult;
    use qc::{U32, U64};

    quickcheck!{
        fn udivdi3(n: U64, d: U64) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let q = super::__udivdi3(n, d);
                TestResult::from_bool(q == n / d)
            }
        }

        fn umoddi3(n: U64, d: U64) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let r = super::__umoddi3(n, d);
                TestResult::from_bool(r == n % d)
            }
        }

        fn udivmoddi4(n: U64, d: U64) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let mut r = 0;
                let q = super::__udivmoddi4(n, d, Some(&mut r));
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }

        fn udivsi3(n: U32, d: U32) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let q = super::__udivsi3(n, d);
                TestResult::from_bool(q == n / d)
            }
        }

        fn umodsi3(n: U32, d: U32) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let r = super::__umodsi3(n, d);
                TestResult::from_bool(r == n % d)
            }
        }

        fn udivmodsi4(n: U32, d: U32) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let mut r = 0;
                let q = super::__udivmodsi4(n, d, Some(&mut r));
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::ops::Range;

use byteorder::{ByteOrder, LittleEndian};

use gameboy::{CPU_FREQUENCY, MAX_CPU_CYCLES, MAX_DIV_REG_CYCLES, Gpu, Irq, Memory, Timer};
use gameboy::cartridge::{Cartridge, CartridgeDetails};
use super::memory_map::{self, Address};

const MAIN_MEM_SIZE: usize = 0x2000;
const ZRAM_SIZE: usize = 0x80;
const MMAP_SIZE: usize = 0x80;

pub struct Interconnect {
    pub gpu: Gpu,
    pub ram: Memory,
    pub zram: Memory,
    pub mmap_io: Memory,
    pub timer: Timer,
    pub irq: Irq,
    pub cart: Option<Cartridge>,
    pub interrupt: u8,
}

impl Interconnect {
    pub fn new() -> Interconnect {
        Interconnect {
            gpu: Gpu::new(),
            ram: Memory::new(MAIN_MEM_SIZE),
            zram: Memory::new(ZRAM_SIZE),
            mmap_io: Memory::new(MMAP_SIZE),
            timer: Timer::new(),
            irq: Irq::new(),
            cart: None,
            interrupt: 0x00,
        }
    }

    pub fn with_cart(cart: Cartridge) -> Interconnect {
        Interconnect {
            gpu: Gpu::new(),
            ram: Memory::new(MAIN_MEM_SIZE),
            zram: Memory::new(ZRAM_SIZE),
            mmap_io: Memory::new(MMAP_SIZE),
            timer: Timer::new(),
            irq: Irq::new(),
            cart: Some(cart),
            interrupt: 0x00,
        }
    }

    pub fn step(&mut self, cycles: usize) {
        self.gpu.step(&mut self.irq, cycles);
        self.timer.step(&mut self.irq, cycles);
    }

    pub fn cart_details(&self) -> CartridgeDetails {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        cart.details(&self)
    }

    pub fn write_u8(&mut self, addr: u16, byte: u8) {
        let cart = self.cart.as_mut().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(a) |
            Address::RamShadow(a) => self.ram.write_u8(a, byte),
            Address::Gfx(a) => self.gpu.ram.write_u8(a, byte),
            Address::CartRam(a) => cart.ram.write_u8(a, byte),
            Address::CartRom(a) => cart.rom.write_u8(a, byte),
            Address::ZRam(a) => self.zram.write_u8(a, byte),
            Address::Oam(a) => self.gpu.sprite_data.write_u8(a, byte),
            Address::Unused(_) => (),
            Address::Io(a) => {
                match a {
                    0x01...0x02 => println!("err: write to serial driver not supported"),
                    0x04...0x07 => self.timer.write_u8(a, byte),
                    0x0F => self.irq.request_flag = byte,
                    0x10...0x26 => println!("err: write to sound driver not supported"),
                    0x40...0x45 => self.gpu.write_u8(a, byte),
                    0x47...0x49 => self.gpu.write_u8(a, byte),
                    0x4A...0x4B => self.gpu.write_u8(a, byte),
                    0x7F => self.mmap_io.write_u8(a, byte),
                    0xFF => self.irq.enable_flag = byte,
                    _ => panic!("write memory mapped I/O in unsupported range: {:04X}", a),
                }
            }
            Address::InterruptEnableRegister(a) => self.interrupt = byte,
            _ => {
                panic!("Unable to write byte to: {:#X}, invalid memory region.",
                       addr)
            }
        }
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(addr) |
            Address::RamShadow(addr) => self.ram.read_u8(addr),
            Address::CartRom(addr) |
            Address::CartRomOtherBank(addr) => cart.rom.read_u8(addr),
            Address::Gfx(value) => self.gpu.ram.read_u8(value),
            Address::CartRam(a) => cart.ram.read_u8(a),
            Address::ZRam(a) => self.zram.read_u8(a),
            Address::Oam(a) => self.gpu.sprite_data.read_u8(a),
            Address::Unused(_) => 0xFF, // Always return high
            Address::Io(a) => {
                match a {
                    0x01...0x02 => {
                        println!("err: read from serial driver not supported");
                        0
                    }
                    0x04...0x07 => self.timer.read_u8(a),
                    0x0F => self.irq.request_flag,
                    0x10...0x26 => {
                        println!("err: read from sound driver not supported");
                        0
                    }
                    0x40...0x45 => self.gpu.read_u8(a),
                    0x47...0x49 => self.gpu.read_u8(a),
                    0x4A...0x4B => self.gpu.read_u8(a),
                    0x7F => self.mmap_io.read_u8(a),
                    0xFF => self.irq.enable_flag,
                    _ => panic!("read memory mapped I/O in unsupported range"),
                }
            }
            Address::InterruptEnableRegister(a) => self.interrupt,
            _ => panic!("Unable to read address: {:#X}", addr),
        }
    }

    pub fn read_bytes(&self, r: Range<u16>) -> &[u8] {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(r.start) {
            Address::Ram(_) |
            Address::RamShadow(_) => self.ram.read_bytes(r),
            Address::CartRom(_) |
            Address::CartRomOtherBank(_) => cart.rom.read_bytes(r),
            Address::Gfx(_) => self.gpu.ram.read_bytes(r),
            Address::CartRam(_) => cart.ram.read_bytes(r),
            Address::ZRam(_) => self.zram.read_bytes(r),
            Address::Io(a) => self.mmap_io.read_bytes(r),
            _ => panic!("Unable to read address range: {:?}", r),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(a) |
            Address::RamShadow(a) => self.ram.write_u16(a, val),
            Address::ZRam(a) => self.zram.write_u16(a, val),
            _ => panic!("Unable to write address: {:#X}", addr),
        }
    }

    pub fn read_u16(&self, addr: u16) -> u16 {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(addr) |
            Address::RamShadow(addr) => self.ram.read_u16(addr),
            Address::CartRom(addr) |
            Address::CartRomOtherBank(addr) => cart.rom.read_u16(addr),
            Address::Gfx(value) => self.gpu.ram.read_u16(value),
            Address::CartRam(a) => cart.ram.read_u16(a),
            Address::ZRam(a) => self.zram.read_u16(a),
            Address::Io(a) => self.mmap_io.read_u16(a),
            _ => panic!("Unable to read address: {:#X}", addr),
        }
    }
}
<commit_msg>Add sink for Joypad read/write<commit_after>use std::ops::Range;

use byteorder::{ByteOrder, LittleEndian};

use gameboy::{CPU_FREQUENCY, MAX_CPU_CYCLES, MAX_DIV_REG_CYCLES, Gpu, Irq, Memory, Timer};
use gameboy::cartridge::{Cartridge, CartridgeDetails};
use super::memory_map::{self, Address};

const MAIN_MEM_SIZE: usize = 0x2000;
const ZRAM_SIZE: usize = 0x80;
const MMAP_SIZE: usize = 0x80;

pub struct Interconnect {
    pub gpu: Gpu,
    pub ram: Memory,
    pub zram: Memory,
    pub mmap_io: Memory,
    pub timer: Timer,
    pub irq: Irq,
    pub cart: Option<Cartridge>,
    pub interrupt: u8,
}

impl Interconnect {
    pub fn new() -> Interconnect {
        Interconnect {
            gpu: Gpu::new(),
            ram: Memory::new(MAIN_MEM_SIZE),
            zram: Memory::new(ZRAM_SIZE),
            mmap_io: Memory::new(MMAP_SIZE),
            timer: Timer::new(),
            irq: Irq::new(),
            cart: None,
            interrupt: 0x00,
        }
    }

    pub fn with_cart(cart: Cartridge) -> Interconnect {
        Interconnect {
            gpu: Gpu::new(),
            ram: Memory::new(MAIN_MEM_SIZE),
            zram: Memory::new(ZRAM_SIZE),
            mmap_io: Memory::new(MMAP_SIZE),
            timer: Timer::new(),
            irq: Irq::new(),
            cart: Some(cart),
            interrupt: 0x00,
        }
    }

    pub fn step(&mut self, cycles: usize) {
        self.gpu.step(&mut self.irq, cycles);
        self.timer.step(&mut self.irq, cycles);
    }

    pub fn cart_details(&self) -> CartridgeDetails {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        cart.details(&self)
    }

    pub fn write_u8(&mut self, addr: u16, byte: u8) {
        let cart = self.cart.as_mut().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(a) |
            Address::RamShadow(a) => self.ram.write_u8(a, byte),
            Address::Gfx(a) => self.gpu.ram.write_u8(a, byte),
            Address::CartRam(a) => cart.ram.write_u8(a, byte),
            Address::CartRom(a) => cart.rom.write_u8(a, byte),
            Address::ZRam(a) => self.zram.write_u8(a, byte),
            Address::Oam(a) => self.gpu.sprite_data.write_u8(a, byte),
            Address::Unused(_) => (),
            Address::Io(a) => {
                match a {
                    0x00 => println!("err: write to joypad not supported"),
                    0x01...0x02 => println!("err: write to serial driver not supported"),
                    0x04...0x07 => self.timer.write_u8(a, byte),
                    0x0F => self.irq.request_flag = byte,
                    0x10...0x26 => println!("err: write to sound driver not supported"),
                    0x40...0x45 => self.gpu.write_u8(a, byte),
                    0x47...0x49 => self.gpu.write_u8(a, byte),
                    0x4A...0x4B => self.gpu.write_u8(a, byte),
                    0x7F => self.mmap_io.write_u8(a, byte),
                    0xFF => self.irq.enable_flag = byte,
                    _ => panic!("write memory mapped I/O in unsupported range: {:04X}", a),
                }
            }
            Address::InterruptEnableRegister(a) => self.interrupt = byte,
            _ => {
                panic!("Unable to write byte to: {:#X}, invalid memory region.",
                       addr)
            }
        }
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(addr) |
            Address::RamShadow(addr) => self.ram.read_u8(addr),
            Address::CartRom(addr) |
            Address::CartRomOtherBank(addr) => cart.rom.read_u8(addr),
            Address::Gfx(value) => self.gpu.ram.read_u8(value),
            Address::CartRam(a) => cart.ram.read_u8(a),
            Address::ZRam(a) => self.zram.read_u8(a),
            Address::Oam(a) => self.gpu.sprite_data.read_u8(a),
            Address::Unused(_) => 0xFF, // Always return high
            Address::Io(a) => {
                match a {
                    0x00 => {
                        println!("err: read from joypad not supported");
                        0
                    }
                    0x01...0x02 => {
                        println!("err: read from serial driver not supported");
                        0
                    }
                    0x04...0x07 => self.timer.read_u8(a),
                    0x0F => self.irq.request_flag,
                    0x10...0x26 => {
                        println!("err: read from sound driver not supported");
                        0
                    }
                    0x40...0x45 => self.gpu.read_u8(a),
                    0x47...0x49 => self.gpu.read_u8(a),
                    0x4A...0x4B => self.gpu.read_u8(a),
                    0x7F => self.mmap_io.read_u8(a),
                    0xFF => self.irq.enable_flag,
                    _ => panic!("read memory mapped I/O in unsupported range"),
                }
            }
            Address::InterruptEnableRegister(a) => self.interrupt,
            _ => panic!("Unable to read address: {:#X}", addr),
        }
    }

    pub fn read_bytes(&self, r: Range<u16>) -> &[u8] {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(r.start) {
            Address::Ram(_) |
            Address::RamShadow(_) => self.ram.read_bytes(r),
            Address::CartRom(_) |
            Address::CartRomOtherBank(_) => cart.rom.read_bytes(r),
            Address::Gfx(_) => self.gpu.ram.read_bytes(r),
            Address::CartRam(_) => cart.ram.read_bytes(r),
            Address::ZRam(_) => self.zram.read_bytes(r),
            Address::Io(a) => self.mmap_io.read_bytes(r),
            _ => panic!("Unable to read address range: {:?}", r),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(a) |
            Address::RamShadow(a) => self.ram.write_u16(a, val),
            Address::ZRam(a) => self.zram.write_u16(a, val),
            _ => panic!("Unable to write address: {:#X}", addr),
        }
    }

    pub fn read_u16(&self, addr: u16) -> u16 {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(addr) |
            Address::RamShadow(addr) => self.ram.read_u16(addr),
            Address::CartRom(addr) |
            Address::CartRomOtherBank(addr) => cart.rom.read_u16(addr),
            Address::Gfx(value) => self.gpu.ram.read_u16(value),
            Address::CartRam(a) => cart.ram.read_u16(a),
            Address::ZRam(a) => self.zram.read_u16(a),
            Address::Io(a) => self.mmap_io.read_u16(a),
            _ => panic!("Unable to read address: {:#X}", addr),
        }
    }
}
<|endoftext|>


<commit_before>use super::{Height, Width};
use std::os::unix::io::RawFd;

/// Returns the size of the terminal defaulting to STDOUT, if available.
///
/// If STDOUT is not a tty, returns `None`
pub fn terminal_size() -> Option<(Width, Height)> {
    terminal_size_using_fd(libc::STDOUT_FILENO)
}

/// Returns the size of the terminal using the given file descriptor, if available.
///
/// If the given file descriptor is not a tty, returns `None`
pub fn terminal_size_using_fd(fd: RawFd) -> Option<(Width, Height)> {
    use libc::ioctl;
    use libc::isatty;
    use libc::{winsize as WinSize, TIOCGWINSZ};
    let is_tty: bool = unsafe { isatty(fd) == 1 };

    if !is_tty {
        return None;
    }

    let mut winsize = WinSize {
        ws_row: 0,
        ws_col: 0,
        ws_xpixel: 0,
        ws_ypixel: 0,
    };

    if unsafe { ioctl(fd, TIOCGWINSZ, &mut winsize) } == -1 {
        return None;
    }

    let rows = winsize.ws_row;
    let cols = winsize.ws_col;

    if rows > 0 && cols > 0 {
        Some((Width(cols), Height(rows)))
    } else {
        None
    }
}

#[test]
/// Compare with the output of `stty size`
fn compare_with_stty() {
    use std::process::Command;
    use std::process::Stdio;

    let output = if cfg!(target_os = "macos") {
        Command::new("stty")
            .arg("-f")
            .arg("/dev/stderr")
            .arg("size")
            .stderr(Stdio::inherit())
            .output()
            .unwrap()
    } else {
        Command::new("stty")
            .arg("size")
            .arg("-F")
            .arg("/dev/stderr")
            .stderr(Stdio::inherit())
            .output()
            .unwrap()
    };
    let stdout = String::from_utf8(output.stdout).unwrap();
    assert!(output.status.success());
    // stdout is "rows cols"
    let mut data = stdout.split_whitespace();
    let rows = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
    let cols = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
    println!("{}", stdout);
    println!("{} {}", rows, cols);

    if let Some((Width(w), Height(h))) = terminal_size() {
        assert_eq!(rows, h);
        assert_eq!(cols, w);
    } else {
        panic!("terminal_size() return None");
    }
}
<commit_msg>Fix build on FreeBSD, fixes #16<commit_after>use super::{Height, Width};
use std::os::unix::io::RawFd;

/// Returns the size of the terminal defaulting to STDOUT, if available.
///
/// If STDOUT is not a tty, returns `None`
pub fn terminal_size() -> Option<(Width, Height)> {
    terminal_size_using_fd(libc::STDOUT_FILENO)
}

/// Returns the size of the terminal using the given file descriptor, if available.
///
/// If the given file descriptor is not a tty, returns `None`
pub fn terminal_size_using_fd(fd: RawFd) -> Option<(Width, Height)> {
    use libc::ioctl;
    use libc::isatty;
    use libc::{winsize as WinSize, TIOCGWINSZ};
    let is_tty: bool = unsafe { isatty(fd) == 1 };

    if !is_tty {
        return None;
    }

    let mut winsize = WinSize {
        ws_row: 0,
        ws_col: 0,
        ws_xpixel: 0,
        ws_ypixel: 0,
    };

    if unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut winsize) } == -1 {
        return None;
    }

    let rows = winsize.ws_row;
    let cols = winsize.ws_col;

    if rows > 0 && cols > 0 {
        Some((Width(cols), Height(rows)))
    } else {
        None
    }
}

#[test]
/// Compare with the output of `stty size`
fn compare_with_stty() {
    use std::process::Command;
    use std::process::Stdio;

    let output = if cfg!(target_os = "macos") {
        Command::new("stty")
            .arg("-f")
            .arg("/dev/stderr")
            .arg("size")
            .stderr(Stdio::inherit())
            .output()
            .unwrap()
    } else {
        Command::new("stty")
            .arg("size")
            .arg("-F")
            .arg("/dev/stderr")
            .stderr(Stdio::inherit())
            .output()
            .unwrap()
    };
    let stdout = String::from_utf8(output.stdout).unwrap();
    assert!(output.status.success());
    // stdout is "rows cols"
    let mut data = stdout.split_whitespace();
    let rows = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
    let cols = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
    println!("{}", stdout);
    println!("{} {}", rows, cols);

    if let Some((Width(w), Height(h))) = terminal_size() {
        assert_eq!(rows, h);
        assert_eq!(cols, w);
    } else {
        panic!("terminal_size() return None");
    }
}
<|endoftext|>


<commit_before>use parking_lot::Mutex;
use std::cmp::{max, min};
use std::sync::Arc;

use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;
use gc::swiper::CollectionKind;
use gc::{align_gen, formatted_size, GEN_SIZE, M};
use timer;

pub fn choose_collection_kind(config: &SharedHeapConfig, young: &YoungGen) -> CollectionKind {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    let rest = {
        let config = config.lock();
        config.old_limit - config.old_size
    };

    if rest < young_size {
        CollectionKind::Full
    } else {
        CollectionKind::Minor
    }
}

const MAX_YOUNG_SIZE: usize = 20 * M;

const INIT_HEAP_SIZE_RATIO: usize = 2;
const INIT_YOUNG_RATIO: usize = 4;
const INIT_SEMI_RATIO: usize = 3;

pub fn init(config: &mut HeapConfig) {
    assert!(config.min_heap_size <= config.max_heap_size);

    let young_size = align_gen(config.max_heap_size / INIT_YOUNG_RATIO);
    let young_size = min(MAX_YOUNG_SIZE, young_size);
    let young_size = max(young_size, GEN_SIZE);

    let semi_size = align_gen(young_size / INIT_SEMI_RATIO);
    let semi_size = max(semi_size, GEN_SIZE);
    let eden_size = young_size - semi_size;

    config.eden_size = eden_size;
    config.semi_size = semi_size;

    let max_old_limit = config.max_heap_size - young_size;
    let min_old_limit = if config.min_heap_size > young_size {
        config.min_heap_size - young_size
    } else {
        0
    };

    let old_limit = min(max_old_limit, config.max_heap_size / INIT_HEAP_SIZE_RATIO);
    let old_limit = max(old_limit, min_old_limit);

    config.old_size = 0;
    config.old_limit = old_limit;
}

pub fn start(config: &SharedHeapConfig, young: &YoungGen, old: &OldGen, large: &LargeSpace) {
    let mut config = config.lock();

    config.gc_start = timer::timestamp();
    config.start_object_size = object_size(young, old, large);
    config.start_memory_size = memory_size(young, old, large);
}

pub fn stop(
    config: &SharedHeapConfig,
    kind: CollectionKind,
    young: &YoungGen,
    old: &OldGen,
    large: &LargeSpace,
    verbose: bool,
) {
    let mut config = config.lock();

    let gc_end = timer::timestamp();
    config.gc_duration = timer::in_ms(gc_end - config.gc_start);

    config.end_object_size = object_size(young, old, large);
    config.end_memory_size = memory_size(young, old, large);

    assert!(young.eden_active().empty());
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    let old_size = old.committed_size() + large.committed_size();
    config.old_size = old_size;

    let max_old_limit = config.max_heap_size - young_size;
    config.old_limit = max_old_limit;

    match kind {
        CollectionKind::Minor => {
            config.total_minor_collections += 1;
            config.total_minor_pause += config.gc_duration;
        }

        CollectionKind::Full => {
            config.total_full_collections += 1;
            config.total_full_pause += config.gc_duration;
        }
    }

    if verbose {
        print(&*config, kind);
    }
}

fn print(config: &HeapConfig, kind: CollectionKind) {
    match kind {
        CollectionKind::Minor => {
            println!(
                "GC: {} {}/{} -> {}/{}; {:.2} ms; {} promoted; {} copied",
                kind,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
                formatted_size(config.minor_promoted),
                formatted_size(config.minor_copied),
            );
        }

        CollectionKind::Full => {
            println!(
                "GC: {} {}/{} -> {}/{}; {:.2} ms",
                kind,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
            );
        }
    }
}

fn object_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    young.active_size() + old.active_size() + large.committed_size()
}

fn memory_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    young_size + old.committed_size() + large.committed_size()
}

pub struct HeapConfig {
    min_heap_size: usize,
    max_heap_size: usize,

    pub eden_size: usize,
    pub semi_size: usize,
    pub old_size: usize,
    pub old_limit: usize,

    gc_start: u64,
    gc_duration: f32,

    start_object_size: usize,
    start_memory_size: usize,
    end_object_size: usize,
    end_memory_size: usize,

    pub minor_promoted: usize,
    pub minor_copied: usize,

    pub total_minor_collections: usize,
    pub total_minor_pause: f32,
    pub total_full_collections: usize,
    pub total_full_pause: f32,
}

impl HeapConfig {
    pub fn new(min_heap_size: usize, max_heap_size: usize) -> HeapConfig {
        assert!(min_heap_size <= max_heap_size);

        HeapConfig {
            min_heap_size: min_heap_size,
            max_heap_size: max_heap_size,

            eden_size: 0,
            semi_size: 0,
            old_size: 0,
            old_limit: 0,

            gc_start: 0,
            gc_duration: 0f32,

            start_object_size: 0,
            start_memory_size: 0,
            end_object_size: 0,
            end_memory_size: 0,

            minor_promoted: 0,
            minor_copied: 0,

            total_minor_collections: 0,
            total_minor_pause: 0f32,
            total_full_collections: 0,
            total_full_pause: 0f32,
        }
    }

    pub fn grow_old(&mut self, size: usize) -> bool {
        if self.old_size + size <= self.old_limit {
            self.old_size += size;
            true
        } else {
            false
        }
    }
}

pub type SharedHeapConfig = Arc<Mutex<HeapConfig>>;
<commit_msg>swiper: align old_limit to generation<commit_after>use parking_lot::Mutex;
use std::cmp::{max, min};
use std::sync::Arc;

use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;
use gc::swiper::CollectionKind;
use gc::{align_gen, formatted_size, GEN_SIZE, M};
use timer;

pub fn choose_collection_kind(config: &SharedHeapConfig, young: &YoungGen) -> CollectionKind {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    let rest = {
        let config = config.lock();
        config.old_limit - config.old_size
    };

    if rest < young_size {
        CollectionKind::Full
    } else {
        CollectionKind::Minor
    }
}

const MAX_YOUNG_SIZE: usize = 20 * M;

const INIT_HEAP_SIZE_RATIO: usize = 2;
const INIT_YOUNG_RATIO: usize = 4;
const INIT_SEMI_RATIO: usize = 3;

pub fn init(config: &mut HeapConfig) {
    assert!(config.min_heap_size <= config.max_heap_size);

    let young_size = align_gen(config.max_heap_size / INIT_YOUNG_RATIO);
    let young_size = min(MAX_YOUNG_SIZE, young_size);
    let young_size = max(young_size, GEN_SIZE);

    let semi_size = align_gen(young_size / INIT_SEMI_RATIO);
    let semi_size = max(semi_size, GEN_SIZE);
    let eden_size = young_size - semi_size;

    config.eden_size = eden_size;
    config.semi_size = semi_size;

    let max_old_limit = config.max_heap_size - young_size;
    let min_old_limit = if config.min_heap_size > young_size {
        config.min_heap_size - young_size
    } else {
        0
    };

    let old_limit = align_gen(config.max_heap_size / INIT_HEAP_SIZE_RATIO);
    let old_limit = min(old_limit, max_old_limit);
    let old_limit = max(old_limit, min_old_limit);

    config.old_size = 0;
    config.old_limit = old_limit;
}

pub fn start(config: &SharedHeapConfig, young: &YoungGen, old: &OldGen, large: &LargeSpace) {
    let mut config = config.lock();

    config.gc_start = timer::timestamp();
    config.start_object_size = object_size(young, old, large);
    config.start_memory_size = memory_size(young, old, large);
}

pub fn stop(
    config: &SharedHeapConfig,
    kind: CollectionKind,
    young: &YoungGen,
    old: &OldGen,
    large: &LargeSpace,
    verbose: bool,
) {
    let mut config = config.lock();

    let gc_end = timer::timestamp();
    config.gc_duration = timer::in_ms(gc_end - config.gc_start);

    config.end_object_size = object_size(young, old, large);
    config.end_memory_size = memory_size(young, old, large);

    assert!(young.eden_active().empty());
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    let old_size = old.committed_size() + large.committed_size();
    config.old_size = old_size;

    let max_old_limit = config.max_heap_size - young_size;
    config.old_limit = max_old_limit;

    match kind {
        CollectionKind::Minor => {
            config.total_minor_collections += 1;
            config.total_minor_pause += config.gc_duration;
        }

        CollectionKind::Full => {
            config.total_full_collections += 1;
            config.total_full_pause += config.gc_duration;
        }
    }

    if verbose {
        print(&*config, kind);
    }
}

fn print(config: &HeapConfig, kind: CollectionKind) {
    match kind {
        CollectionKind::Minor => {
            println!(
                "GC: {} {}/{} -> {}/{}; {:.2} ms; {} promoted; {} copied",
                kind,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
                formatted_size(config.minor_promoted),
                formatted_size(config.minor_copied),
            );
        }

        CollectionKind::Full => {
            println!(
                "GC: {} {}/{} -> {}/{}; {:.2} ms",
                kind,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
            );
        }
    }
}

fn object_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    young.active_size() + old.active_size() + large.committed_size()
}

fn memory_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    young_size + old.committed_size() + large.committed_size()
}

pub struct HeapConfig {
    min_heap_size: usize,
    max_heap_size: usize,

    pub eden_size: usize,
    pub semi_size: usize,
    pub old_size: usize,
    pub old_limit: usize,

    gc_start: u64,
    gc_duration: f32,

    start_object_size: usize,
    start_memory_size: usize,
    end_object_size: usize,
    end_memory_size: usize,

    pub minor_promoted: usize,
    pub minor_copied: usize,

    pub total_minor_collections: usize,
    pub total_minor_pause: f32,
    pub total_full_collections: usize,
    pub total_full_pause: f32,
}

impl HeapConfig {
    pub fn new(min_heap_size: usize, max_heap_size: usize) -> HeapConfig {
        assert!(min_heap_size <= max_heap_size);

        HeapConfig {
            min_heap_size: min_heap_size,
            max_heap_size: max_heap_size,

            eden_size: 0,
            semi_size: 0,
            old_size: 0,
            old_limit: 0,

            gc_start: 0,
            gc_duration: 0f32,

            start_object_size: 0,
            start_memory_size: 0,
            end_object_size: 0,
            end_memory_size: 0,

            minor_promoted: 0,
            minor_copied: 0,

            total_minor_collections: 0,
            total_minor_pause: 0f32,
            total_full_collections: 0,
            total_full_pause: 0f32,
        }
    }

    pub fn grow_old(&mut self, size: usize) -> bool {
        if self.old_size + size <= self.old_limit {
            self.old_size += size;
            true
        } else {
            false
        }
    }
}

pub type SharedHeapConfig = Arc<Mutex<HeapConfig>>;
<|endoftext|>