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

//! Tidy check to ensure that unstable features are all in order
//!
//! This check will ensure properties like:
//!
//! * All stability attributes look reasonably well formed
//! * The set of library features is disjoint from the set of language features
//! * Library features have at most one stability level
//! * Library features have at most one `since` value
//! * All unstable lang features have tests to ensure they are actually unstable

use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

#[derive(Debug, PartialEq, Clone)]
pub enum Status {
    Stable,
    Removed,
    Unstable,
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let as_str = match *self {
            Status::Stable => "stable",
            Status::Unstable => "unstable",
            Status::Removed => "removed",
        };
        fmt::Display::fmt(as_str, f)
    }
}

#[derive(Debug, Clone)]
pub struct Feature {
    pub level: Status,
    pub since: String,
    pub has_gate_test: bool,
    pub tracking_issue: Option<u32>,
}

pub type Features = HashMap<String, Feature>;

pub fn check(path: &Path, bad: &mut bool, quiet: bool) {
    let mut features = collect_lang_features(path, bad);
    assert!(!features.is_empty());

    let lib_features = get_and_check_lib_features(path, bad, &features);
    assert!(!lib_features.is_empty());

    let mut contents = String::new();

    super::walk_many(&[&path.join("test/ui-fulldeps"),
                       &path.join("test/ui"),
                       &path.join("test/compile-fail"),
                       &path.join("test/compile-fail-fulldeps"),
                       &path.join("test/parse-fail"),
                       &path.join("test/ui"),],
                     &mut |path| super::filter_dirs(path),
                     &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        if !filename.ends_with(".rs") || filename == "features.rs" ||
           filename == "diagnostic_list.rs" {
            return;
        }

        let filen_underscore = filename.replace('-',"_").replace(".rs","");
        let filename_is_gate_test = test_filen_gate(&filen_underscore, &mut features);

        contents.truncate(0);
        t!(t!(File::open(&file), &file).read_to_string(&mut contents));

        for (i, line) in contents.lines().enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };

            let gate_test_str = "gate-test-";

            if !line.contains(gate_test_str) {
                continue;
            }

            let feature_name = match line.find(gate_test_str) {
                Some(i) => {
                    &line[i+gate_test_str.len()..line[i+1..].find(' ').unwrap_or(line.len())]
                },
                None => continue,
            };
            match features.get_mut(feature_name) {
                Some(f) => {
                    if filename_is_gate_test {
                        err(&format!("The file is already marked as gate test \
                                      through its name, no need for a \
                                      'gate-test-{}' comment",
                                     feature_name));
                    }
                    f.has_gate_test = true;
                }
                None => {
                    err(&format!("gate-test test found referencing a nonexistent feature '{}'",
                                 feature_name));
                }
            }
        }
    });

    // Only check the number of lang features.
    // Obligatory testing for library features is dumb.
    let gate_untested = features.iter()
                                .filter(|&(_, f)| f.level == Status::Unstable)
                                .filter(|&(_, f)| !f.has_gate_test)
                                .collect::<Vec<_>>();

    for &(name, _) in gate_untested.iter() {
        println!("Expected a gate test for the feature '{}'.", name);
        println!("Hint: create a failing test file named 'feature-gate-{}.rs'\
                \n      in the 'ui' test suite, with its failures due to\
                \n      missing usage of #![feature({})].", name, name);
        println!("Hint: If you already have such a test and don't want to rename it,\
                \n      you can also add a // gate-test-{} line to the test file.",
                 name);
    }

    if !gate_untested.is_empty() {
        tidy_error!(bad, "Found {} features without a gate test.", gate_untested.len());
    }

    if *bad {
        return;
    }
    if quiet {
        println!("* {} features", features.len());
        return;
    }

    let mut lines = Vec::new();
    for (name, feature) in features.iter() {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lang",
                           feature.level,
                           feature.since));
    }
    for (name, feature) in lib_features {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lib",
                           feature.level,
                           feature.since));
    }

    lines.sort();
    for line in lines {
        println!("* {}", line);
    }
}

fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
    line.find(attr)
        .and_then(|i| line[i..].find('"').map(|j| i + j + 1))
        .and_then(|i| line[i..].find('"').map(|j| (i, i + j)))
        .map(|(i, j)| &line[i..j])
}

fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
    if filen_underscore.starts_with("feature_gate") {
        for (n, f) in features.iter_mut() {
            if filen_underscore == format!("feature_gate_{}", n) {
                f.has_gate_test = true;
                return true;
            }
        }
    }
    return false;
}

pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
    let mut contents = String::new();
    let path = base_src_path.join("libsyntax/feature_gate.rs");
    t!(t!(File::open(path)).read_to_string(&mut contents));

    // we allow rustc-internal features to omit a tracking issue.
    // these features must be marked with `// rustc internal` in its own group.
    let mut next_feature_is_rustc_internal = false;

    contents.lines().zip(1..)
        .filter_map(|(line, line_number)| {
            let line = line.trim();
            if line.starts_with("// rustc internal") {
                next_feature_is_rustc_internal = true;
                return None;
            } else if line.is_empty() {
                next_feature_is_rustc_internal = false;
                return None;
            }

            let mut parts = line.split(',');
            let level = match parts.next().map(|l| l.trim().trim_left_matches('(')) {
                Some("active") => Status::Unstable,
                Some("removed") => Status::Removed,
                Some("accepted") => Status::Stable,
                _ => return None,
            };
            let name = parts.next().unwrap().trim();
            let since = parts.next().unwrap().trim().trim_matches('"');
            let issue_str = parts.next().unwrap().trim();
            let tracking_issue = if issue_str.starts_with("None") {
                if level == Status::Unstable && !next_feature_is_rustc_internal {
                    *bad = true;
                    tidy_error!(
                        bad,
                        "libsyntax/feature_gate.rs:{}: no tracking issue for feature {}",
                        line_number,
                        name,
                    );
                }
                None
            } else {
                next_feature_is_rustc_internal = false;
                let s = issue_str.split('(').nth(1).unwrap().split(')').nth(0).unwrap();
                Some(s.parse().unwrap())
            };
            Some((name.to_owned(),
                Feature {
                    level,
                    since: since.to_owned(),
                    has_gate_test: false,
                    tracking_issue,
                }))
        })
        .collect()
}

pub fn collect_lib_features(base_src_path: &Path) -> Features {
    let mut lib_features = Features::new();

    // This library feature is defined in the `compiler_builtins` crate, which
    // has been moved out-of-tree. Now it can no longer be auto-discovered by
    // `tidy`, because we need to filter out its (submodule) directory. Manually
    // add it to the set of known library features so we can still generate docs.
    lib_features.insert("compiler_builtins_lib".to_owned(), Feature {
        level: Status::Unstable,
        since: String::new(),
        has_gate_test: false,
        tracking_issue: None,
    });

    map_lib_features(base_src_path,
                     &mut |res, _, _| {
        match res {
            Ok((name, feature)) => {
                if lib_features.get(name).is_some() {
                    return;
                }
                lib_features.insert(name.to_owned(), feature);
            },
            Err(_) => (),
        }
    });
   lib_features
}

fn get_and_check_lib_features(base_src_path: &Path,
                              bad: &mut bool,
                              lang_features: &Features) -> Features {
    let mut lib_features = Features::new();
    map_lib_features(base_src_path,
                     &mut |res, file, line| {
            match res {
                Ok((name, f)) => {
                    let mut check_features = |f: &Feature, list: &Features, display: &str| {
                        if let Some(ref s) = list.get(name) {
                            if f.tracking_issue != s.tracking_issue {
                                tidy_error!(bad,
                                            "{}:{}: mismatches the `issue` in {}",
                                            file.display(),
                                            line,
                                            display);
                            }
                        }
                    };
                    check_features(&f, &lang_features, "corresponding lang feature");
                    check_features(&f, &lib_features, "previous");
                    lib_features.insert(name.to_owned(), f);
                },
                Err(msg) => {
                    tidy_error!(bad, "{}:{}: {}", file.display(), line, msg);
                },
            }

    });
    lib_features
}

fn map_lib_features(base_src_path: &Path,
                    mf: &mut dyn FnMut(Result<(&str, Feature), &str>, &Path, usize)) {
    let mut contents = String::new();
    super::walk(base_src_path,
                &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
                &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        if !filename.ends_with(".rs") || filename == "features.rs" ||
           filename == "diagnostic_list.rs" {
            return;
        }

        contents.truncate(0);
        t!(t!(File::open(&file), &file).read_to_string(&mut contents));

        let mut becoming_feature: Option<(String, Feature)> = None;
        for (i, line) in contents.lines().enumerate() {
            macro_rules! err {
                ($msg:expr) => {{
                    mf(Err($msg), file, i + 1);
                    continue;
                }};
            };
            if let Some((ref name, ref mut f)) = becoming_feature {
                if f.tracking_issue.is_none() {
                    f.tracking_issue = find_attr_val(line, "issue")
                    .map(|s| s.parse().unwrap());
                }
                if line.ends_with(']') {
                    mf(Ok((name, f.clone())), file, i + 1);
                } else if !line.ends_with(',') && !line.ends_with('\\') {
                    // We need to bail here because we might have missed the
                    // end of a stability attribute above because the ']'
                    // might not have been at the end of the line.
                    // We could then get into the very unfortunate situation that
                    // we continue parsing the file assuming the current stability
                    // attribute has not ended, and ignoring possible feature
                    // attributes in the process.
                    err!("malformed stability attribute");
                } else {
                    continue;
                }
            }
            becoming_feature = None;
            if line.contains("rustc_const_unstable(") {
                // const fn features are handled specially
                let feature_name = match find_attr_val(line, "feature") {
                    Some(name) => name,
                    None => err!("malformed stability attribute"),
                };
                let feature = Feature {
                    level: Status::Unstable,
                    since: "None".to_owned(),
                    has_gate_test: false,
                    // Whether there is a common tracking issue
                    // for these feature gates remains an open question
                    // https://github.com/rust-lang/rust/issues/24111#issuecomment-340283184
                    // But we take 24111 otherwise they will be shown as
                    // "internal to the compiler" which they are not.
                    tracking_issue: Some(24111),
                };
                mf(Ok((feature_name, feature)), file, i + 1);
                continue;
            }
            let level = if line.contains("[unstable(") {
                Status::Unstable
            } else if line.contains("[stable(") {
                Status::Stable
            } else {
                continue;
            };
            let feature_name = match find_attr_val(line, "feature") {
                Some(name) => name,
                None => err!("malformed stability attribute"),
            };
            let since = match find_attr_val(line, "since") {
                Some(name) => name,
                None if level == Status::Stable => {
                    err!("malformed stability attribute");
                }
                None => "None",
            };
            let tracking_issue = find_attr_val(line, "issue").map(|s| s.parse().unwrap());

            let feature = Feature {
                level,
                since: since.to_owned(),
                has_gate_test: false,
                tracking_issue,
            };
            if line.contains(']') {
                mf(Ok((feature_name, feature)), file, i + 1);
            } else {
                becoming_feature = Some((feature_name.to_owned(), feature));
            }
        }
    });
}
<commit_msg>tidy: features.rs: collect_lib_features: Simplify<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Tidy check to ensure that unstable features are all in order
//!
//! This check will ensure properties like:
//!
//! * All stability attributes look reasonably well formed
//! * The set of library features is disjoint from the set of language features
//! * Library features have at most one stability level
//! * Library features have at most one `since` value
//! * All unstable lang features have tests to ensure they are actually unstable

use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

#[derive(Debug, PartialEq, Clone)]
pub enum Status {
    Stable,
    Removed,
    Unstable,
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let as_str = match *self {
            Status::Stable => "stable",
            Status::Unstable => "unstable",
            Status::Removed => "removed",
        };
        fmt::Display::fmt(as_str, f)
    }
}

#[derive(Debug, Clone)]
pub struct Feature {
    pub level: Status,
    pub since: String,
    pub has_gate_test: bool,
    pub tracking_issue: Option<u32>,
}

pub type Features = HashMap<String, Feature>;

pub fn check(path: &Path, bad: &mut bool, quiet: bool) {
    let mut features = collect_lang_features(path, bad);
    assert!(!features.is_empty());

    let lib_features = get_and_check_lib_features(path, bad, &features);
    assert!(!lib_features.is_empty());

    let mut contents = String::new();

    super::walk_many(&[&path.join("test/ui-fulldeps"),
                       &path.join("test/ui"),
                       &path.join("test/compile-fail"),
                       &path.join("test/compile-fail-fulldeps"),
                       &path.join("test/parse-fail"),
                       &path.join("test/ui"),],
                     &mut |path| super::filter_dirs(path),
                     &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        if !filename.ends_with(".rs") || filename == "features.rs" ||
           filename == "diagnostic_list.rs" {
            return;
        }

        let filen_underscore = filename.replace('-',"_").replace(".rs","");
        let filename_is_gate_test = test_filen_gate(&filen_underscore, &mut features);

        contents.truncate(0);
        t!(t!(File::open(&file), &file).read_to_string(&mut contents));

        for (i, line) in contents.lines().enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };

            let gate_test_str = "gate-test-";

            if !line.contains(gate_test_str) {
                continue;
            }

            let feature_name = match line.find(gate_test_str) {
                Some(i) => {
                    &line[i+gate_test_str.len()..line[i+1..].find(' ').unwrap_or(line.len())]
                },
                None => continue,
            };
            match features.get_mut(feature_name) {
                Some(f) => {
                    if filename_is_gate_test {
                        err(&format!("The file is already marked as gate test \
                                      through its name, no need for a \
                                      'gate-test-{}' comment",
                                     feature_name));
                    }
                    f.has_gate_test = true;
                }
                None => {
                    err(&format!("gate-test test found referencing a nonexistent feature '{}'",
                                 feature_name));
                }
            }
        }
    });

    // Only check the number of lang features.
    // Obligatory testing for library features is dumb.
    let gate_untested = features.iter()
                                .filter(|&(_, f)| f.level == Status::Unstable)
                                .filter(|&(_, f)| !f.has_gate_test)
                                .collect::<Vec<_>>();

    for &(name, _) in gate_untested.iter() {
        println!("Expected a gate test for the feature '{}'.", name);
        println!("Hint: create a failing test file named 'feature-gate-{}.rs'\
                \n      in the 'ui' test suite, with its failures due to\
                \n      missing usage of #![feature({})].", name, name);
        println!("Hint: If you already have such a test and don't want to rename it,\
                \n      you can also add a // gate-test-{} line to the test file.",
                 name);
    }

    if !gate_untested.is_empty() {
        tidy_error!(bad, "Found {} features without a gate test.", gate_untested.len());
    }

    if *bad {
        return;
    }
    if quiet {
        println!("* {} features", features.len());
        return;
    }

    let mut lines = Vec::new();
    for (name, feature) in features.iter() {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lang",
                           feature.level,
                           feature.since));
    }
    for (name, feature) in lib_features {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lib",
                           feature.level,
                           feature.since));
    }

    lines.sort();
    for line in lines {
        println!("* {}", line);
    }
}

fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
    line.find(attr)
        .and_then(|i| line[i..].find('"').map(|j| i + j + 1))
        .and_then(|i| line[i..].find('"').map(|j| (i, i + j)))
        .map(|(i, j)| &line[i..j])
}

fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
    if filen_underscore.starts_with("feature_gate") {
        for (n, f) in features.iter_mut() {
            if filen_underscore == format!("feature_gate_{}", n) {
                f.has_gate_test = true;
                return true;
            }
        }
    }
    return false;
}

pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
    let mut contents = String::new();
    let path = base_src_path.join("libsyntax/feature_gate.rs");
    t!(t!(File::open(path)).read_to_string(&mut contents));

    // we allow rustc-internal features to omit a tracking issue.
    // these features must be marked with `// rustc internal` in its own group.
    let mut next_feature_is_rustc_internal = false;

    contents.lines().zip(1..)
        .filter_map(|(line, line_number)| {
            let line = line.trim();
            if line.starts_with("// rustc internal") {
                next_feature_is_rustc_internal = true;
                return None;
            } else if line.is_empty() {
                next_feature_is_rustc_internal = false;
                return None;
            }

            let mut parts = line.split(',');
            let level = match parts.next().map(|l| l.trim().trim_left_matches('(')) {
                Some("active") => Status::Unstable,
                Some("removed") => Status::Removed,
                Some("accepted") => Status::Stable,
                _ => return None,
            };
            let name = parts.next().unwrap().trim();
            let since = parts.next().unwrap().trim().trim_matches('"');
            let issue_str = parts.next().unwrap().trim();
            let tracking_issue = if issue_str.starts_with("None") {
                if level == Status::Unstable && !next_feature_is_rustc_internal {
                    *bad = true;
                    tidy_error!(
                        bad,
                        "libsyntax/feature_gate.rs:{}: no tracking issue for feature {}",
                        line_number,
                        name,
                    );
                }
                None
            } else {
                next_feature_is_rustc_internal = false;
                let s = issue_str.split('(').nth(1).unwrap().split(')').nth(0).unwrap();
                Some(s.parse().unwrap())
            };
            Some((name.to_owned(),
                Feature {
                    level,
                    since: since.to_owned(),
                    has_gate_test: false,
                    tracking_issue,
                }))
        })
        .collect()
}

pub fn collect_lib_features(base_src_path: &Path) -> Features {
    let mut lib_features = Features::new();

    // This library feature is defined in the `compiler_builtins` crate, which
    // has been moved out-of-tree. Now it can no longer be auto-discovered by
    // `tidy`, because we need to filter out its (submodule) directory. Manually
    // add it to the set of known library features so we can still generate docs.
    lib_features.insert("compiler_builtins_lib".to_owned(), Feature {
        level: Status::Unstable,
        since: String::new(),
        has_gate_test: false,
        tracking_issue: None,
    });

    map_lib_features(base_src_path,
                     &mut |res, _, _| {
        if let Ok((name, feature)) = res {
            if lib_features.contains_key(name) {
                return;
            }
            lib_features.insert(name.to_owned(), feature);
        }
    });
   lib_features
}

fn get_and_check_lib_features(base_src_path: &Path,
                              bad: &mut bool,
                              lang_features: &Features) -> Features {
    let mut lib_features = Features::new();
    map_lib_features(base_src_path,
                     &mut |res, file, line| {
            match res {
                Ok((name, f)) => {
                    let mut check_features = |f: &Feature, list: &Features, display: &str| {
                        if let Some(ref s) = list.get(name) {
                            if f.tracking_issue != s.tracking_issue {
                                tidy_error!(bad,
                                            "{}:{}: mismatches the `issue` in {}",
                                            file.display(),
                                            line,
                                            display);
                            }
                        }
                    };
                    check_features(&f, &lang_features, "corresponding lang feature");
                    check_features(&f, &lib_features, "previous");
                    lib_features.insert(name.to_owned(), f);
                },
                Err(msg) => {
                    tidy_error!(bad, "{}:{}: {}", file.display(), line, msg);
                },
            }

    });
    lib_features
}

fn map_lib_features(base_src_path: &Path,
                    mf: &mut dyn FnMut(Result<(&str, Feature), &str>, &Path, usize)) {
    let mut contents = String::new();
    super::walk(base_src_path,
                &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
                &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        if !filename.ends_with(".rs") || filename == "features.rs" ||
           filename == "diagnostic_list.rs" {
            return;
        }

        contents.truncate(0);
        t!(t!(File::open(&file), &file).read_to_string(&mut contents));

        let mut becoming_feature: Option<(String, Feature)> = None;
        for (i, line) in contents.lines().enumerate() {
            macro_rules! err {
                ($msg:expr) => {{
                    mf(Err($msg), file, i + 1);
                    continue;
                }};
            };
            if let Some((ref name, ref mut f)) = becoming_feature {
                if f.tracking_issue.is_none() {
                    f.tracking_issue = find_attr_val(line, "issue")
                    .map(|s| s.parse().unwrap());
                }
                if line.ends_with(']') {
                    mf(Ok((name, f.clone())), file, i + 1);
                } else if !line.ends_with(',') && !line.ends_with('\\') {
                    // We need to bail here because we might have missed the
                    // end of a stability attribute above because the ']'
                    // might not have been at the end of the line.
                    // We could then get into the very unfortunate situation that
                    // we continue parsing the file assuming the current stability
                    // attribute has not ended, and ignoring possible feature
                    // attributes in the process.
                    err!("malformed stability attribute");
                } else {
                    continue;
                }
            }
            becoming_feature = None;
            if line.contains("rustc_const_unstable(") {
                // const fn features are handled specially
                let feature_name = match find_attr_val(line, "feature") {
                    Some(name) => name,
                    None => err!("malformed stability attribute"),
                };
                let feature = Feature {
                    level: Status::Unstable,
                    since: "None".to_owned(),
                    has_gate_test: false,
                    // Whether there is a common tracking issue
                    // for these feature gates remains an open question
                    // https://github.com/rust-lang/rust/issues/24111#issuecomment-340283184
                    // But we take 24111 otherwise they will be shown as
                    // "internal to the compiler" which they are not.
                    tracking_issue: Some(24111),
                };
                mf(Ok((feature_name, feature)), file, i + 1);
                continue;
            }
            let level = if line.contains("[unstable(") {
                Status::Unstable
            } else if line.contains("[stable(") {
                Status::Stable
            } else {
                continue;
            };
            let feature_name = match find_attr_val(line, "feature") {
                Some(name) => name,
                None => err!("malformed stability attribute"),
            };
            let since = match find_attr_val(line, "since") {
                Some(name) => name,
                None if level == Status::Stable => {
                    err!("malformed stability attribute");
                }
                None => "None",
            };
            let tracking_issue = find_attr_val(line, "issue").map(|s| s.parse().unwrap());

            let feature = Feature {
                level,
                since: since.to_owned(),
                has_gate_test: false,
                tracking_issue,
            };
            if line.contains(']') {
                mf(Ok((feature_name, feature)), file, i + 1);
            } else {
                becoming_feature = Some((feature_name.to_owned(), feature));
            }
        }
    });
}
<|endoftext|>


<commit_before>//! This module contains an AST visitor that turns all "ERawOp"s into "proper" expression nodes,
//! ensuring proper operator precedences are used.
//!
//! This makes a tree out of the "raw" expressions parsed by the generated parser.

use std::mem;

use visit::*;
use ast::{Expr, BinOp};
use ast::Expr::*;

#[derive(Copy)]
pub struct ExprParser;

impl Visitor for ExprParser {
    fn visit_expr(&mut self, expr: &mut Expr) {
        match *expr {
            ERawOp(..) => {
                // Build a tree out of the expression
                let mut operands: Vec<Expr> = Vec::new();
                let mut operators: Vec<BinOp> = Vec::new();
                let mut rest = Vec::new();

                if let ERawOp(ref mut left, ref mut old_rest) = *expr {
                    println!("got raw op: {:?} | {:?}", left, old_rest);

                    // take ownership of `left` and `rest` by replacing with an empty Vec<> (saves us
                    // from copying the content). We replace the whole expr anyways, so it doesn't
                    // matter.
                    operands.push(mem::replace(left, EVarArgs));
                    mem::swap(old_rest, &mut rest);
                } else { unreachable!(); }

                for pair in rest {
                    let (op, rhs) = pair;

                    // If the operator on the stack has higher or equal precedence, pop it, pop 2
                    // operands and build a new operand node
                    if operators.len() > 0 {
                        let stackop = operators[operators.len() - 1];
                        if stackop.get_precedence() >= op.get_precedence() {
                            operators.pop();
                            let rhs = operands.pop().unwrap();
                            let lhs = operands.pop().unwrap();

                            let expr = EBinOp(Box::new(lhs), stackop, Box::new(rhs));
                            println!("l>=r | built node: {:?}", &expr);
                            operands.push(expr);
                        }
                    }

                    // Can't build node, put the new operator and operand on the stack
                    operators.push(op);
                    operands.push(rhs);
                }

                // Pop all operators and build nodes until no operators left
                while operators.len() > 0 {
                    let op = operators.pop().unwrap();
                    let rhs = operands.pop().unwrap();
                    let lhs = operands.pop().unwrap();

                    // PUSH UNTIL THERE IS NOTHING LEFT TO PUSH
                    operands.push(EBinOp(Box::new(lhs), op, Box::new(rhs)));
                }

                // If this algorithm works correctly, this always holds, since the input data can
                // always be turned into a tree (since there is always one operand and any number
                // of operator-operand pairs)
                assert_eq!(operands.len(), 1);

                println!("result | {:?}", &operands[0]);

                mem::replace(expr, operands.pop().unwrap());
            },
            _ => {},
        }

        // If this expr contains any subexpressions, convert them as well
        walk_expr(expr, self);
    }
}
<commit_msg>Removed debug output -.-<commit_after>//! This module contains an AST visitor that turns all "ERawOp"s into "proper" expression nodes,
//! ensuring proper operator precedences are used.
//!
//! This makes a tree out of the "raw" expressions parsed by the generated parser.

use std::mem;

use visit::*;
use ast::{Expr, BinOp};
use ast::Expr::*;

#[derive(Copy)]
pub struct ExprParser;

impl Visitor for ExprParser {
    fn visit_expr(&mut self, expr: &mut Expr) {
        match *expr {
            ERawOp(..) => {
                let mut operands: Vec<Expr> = Vec::new();
                let mut operators: Vec<BinOp> = Vec::new();
                let mut rest = Vec::new();

                if let ERawOp(ref mut left, ref mut old_rest) = *expr {
                    // take ownership of `left` and `rest` by replacing with an empty Vec<> (saves
                    // us from copying the content). We replace the whole expr anyways, so it
                    // doesn't matter.
                    operands.push(mem::replace(left, EVarArgs));
                    mem::swap(old_rest, &mut rest);
                } else { unreachable!(); }

                for pair in rest {
                    let (op, rhs) = pair;

                    // If the operator on the stack has higher or equal precedence, pop it, pop 2
                    // operands and build a new operand node
                    if operators.len() > 0 {
                        let stackop = operators[operators.len() - 1];
                        if stackop.get_precedence() >= op.get_precedence() {
                            operators.pop();
                            let rhs = operands.pop().unwrap();
                            let lhs = operands.pop().unwrap();

                            let expr = EBinOp(Box::new(lhs), stackop, Box::new(rhs));
                            operands.push(expr);
                        }
                    }

                    // Can't build node, put the new operator and operand on the stack
                    operators.push(op);
                    operands.push(rhs);
                }

                // Pop all operators and build nodes
                while operators.len() > 0 {
                    let op = operators.pop().unwrap();
                    let rhs = operands.pop().unwrap();
                    let lhs = operands.pop().unwrap();

                    // PUSH UNTIL THERE IS NOTHING LEFT TO PUSH
                    operands.push(EBinOp(Box::new(lhs), op, Box::new(rhs)));
                }

                // If this algorithm works correctly, this always holds, since the input data can
                // always be turned into a tree (since there is always one operand and any number
                // of operator-operand pairs)
                assert_eq!(operands.len(), 1);

                mem::replace(expr, operands.pop().unwrap());
            },
            _ => {},
        }

        walk_expr(expr, self);
    }
}
<|endoftext|>


<commit_before>//! Tests for -Zextra-link-arg.

use cargo_test_support::{basic_bin_manifest, project};

#[cargo_test]
fn build_script_extra_link_arg_bin() {
    let p = project()
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-bin-link-arg=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -Zextra-link-arg -v")
        .masquerade_as_nightly_cargo()
        .with_status(101)
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg() {
    let p = project()
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -Zextra-link-arg -v")
        .masquerade_as_nightly_cargo()
        .with_status(101)
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg_warn_without_flag() {
    let p = project()
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -vv")
        .with_status(0)
        .with_stderr_contains("warning: cargo:rustc-link-arg requires -Zextra-link-arg flag")
        .run();
}
<commit_msg>Remove unneeded verbose flag.<commit_after>//! Tests for -Zextra-link-arg.

use cargo_test_support::{basic_bin_manifest, project};

#[cargo_test]
fn build_script_extra_link_arg_bin() {
    let p = project()
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-bin-link-arg=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -Zextra-link-arg -v")
        .masquerade_as_nightly_cargo()
        .with_status(101)
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg() {
    let p = project()
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -Zextra-link-arg -v")
        .masquerade_as_nightly_cargo()
        .with_status(101)
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg_warn_without_flag() {
    let p = project()
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -v")
        .with_status(0)
        .with_stderr_contains("warning: cargo:rustc-link-arg requires -Zextra-link-arg flag")
        .run();
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use renderbuffer::Renderbuffer;
use texture::{Texture, TextureType, ImageTargetType};
use types::GLError;

pub struct Framebuffer {
    gl_id: GLuint
}

impl Framebuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Framebuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteFramebuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_framebuffer(&self) -> Framebuffer {
        unsafe {
            let mut id : GLuint = 0;

            gl::GenFramebuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Framebuffer {
                gl_id: id
            }
        }
    }
}

gl_enum! {
    pub gl_enum FramebufferAttachment {
        ColorAttachment0 as COLOR_ATTACHMENT0 = gl::COLOR_ATTACHMENT0,
        DepthAttachment as DEPTH_ATTACHMENT = gl::DEPTH_ATTACHMENT,
        StencilAttachment as STENCIL_ATTACHMENT = gl::STENCIL_ATTACHMENT
    }
}

pub struct FramebufferBinding<'a> {
    phantom: PhantomData<&'a mut Framebuffer>
}

impl<'a> FramebufferBinding<'a> {
    fn target(&self) -> GLenum {
        gl::FRAMEBUFFER
    }

    pub fn renderbuffer(&mut self,
                        attachment: FramebufferAttachment,
                        renderbuffer: &mut Renderbuffer)
    {
        unsafe {
            gl::FramebufferRenderbuffer(self.target(),
                                        attachment.gl_enum(),
                                        gl::RENDERBUFFER,
                                        renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not a valid attachment point, or `renderbuffer` is not `GL_RENDERBUFFER` and `renderbuffer` is not 0",
                GLError::InvalidOperation => "Framebuffer 0 is bound, or `renderbuffer` is neither 0 nor the name of an existing renderbuffer object",
                _ => "Unknown error"
            }
        }
    }

    pub fn texture2d<T: TextureType>(&mut self,
                                     attachment: FramebufferAttachment,
                                     tex_target: T::ImageTargetType,
                                     texture: &mut Texture<T>,
                                     level: i32)
    {
        debug_assert!(level == 0);

        unsafe {
            gl::FramebufferTexture2D(self.target(),
                                     attachment.gl_enum(),
                                     tex_target.gl_enum(),
                                     texture.gl_id(),
                                     level as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not an accepted attachment point, or `textarget` is not an accepted texture target and texture is not 0",
                GLError::InvalidValue => "`level` is not 0 and `texture` is not 0",
                GLError::InvalidOperation => "Framebuffer object 0 is bound, `texture` is neither 0 nor the name of an existing texture object, or `textarget` is not a valid target for `texture`",
                _ => "Unknown error"
            }
        }
    }

    pub fn clear(&mut self, buffers: BufferBits) {
        unsafe {
            gl::Clear(buffers.bits());
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`mask` includes a bit other than an allowed value",
                _ => "Unkown error"
            }
        }
    }
}

pub struct FramebufferBinder;
impl FramebufferBinder {
    pub fn bind<'a>(&'a mut self, fbo: &mut Framebuffer)
        -> FramebufferBinding<'a>
    {
        let binding = FramebufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindFramebuffer(binding.target(), fbo.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<commit_msg>Add unsafe `Framebuffer.from_gl()`<commit_after>use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use renderbuffer::Renderbuffer;
use texture::{Texture, TextureType, ImageTargetType};
use types::GLError;

pub struct Framebuffer {
    gl_id: GLuint
}

impl Framebuffer {
    pub unsafe fn from_gl(id: GLuint) -> Self {
        Framebuffer { gl_id: id }
    }

    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Framebuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteFramebuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_framebuffer(&self) -> Framebuffer {
        unsafe {
            let mut id : GLuint = 0;

            gl::GenFramebuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Framebuffer {
                gl_id: id
            }
        }
    }
}

gl_enum! {
    pub gl_enum FramebufferAttachment {
        ColorAttachment0 as COLOR_ATTACHMENT0 = gl::COLOR_ATTACHMENT0,
        DepthAttachment as DEPTH_ATTACHMENT = gl::DEPTH_ATTACHMENT,
        StencilAttachment as STENCIL_ATTACHMENT = gl::STENCIL_ATTACHMENT
    }
}

pub struct FramebufferBinding<'a> {
    phantom: PhantomData<&'a mut Framebuffer>
}

impl<'a> FramebufferBinding<'a> {
    fn target(&self) -> GLenum {
        gl::FRAMEBUFFER
    }

    pub fn renderbuffer(&mut self,
                        attachment: FramebufferAttachment,
                        renderbuffer: &mut Renderbuffer)
    {
        unsafe {
            gl::FramebufferRenderbuffer(self.target(),
                                        attachment.gl_enum(),
                                        gl::RENDERBUFFER,
                                        renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not a valid attachment point, or `renderbuffer` is not `GL_RENDERBUFFER` and `renderbuffer` is not 0",
                GLError::InvalidOperation => "Framebuffer 0 is bound, or `renderbuffer` is neither 0 nor the name of an existing renderbuffer object",
                _ => "Unknown error"
            }
        }
    }

    pub fn texture2d<T: TextureType>(&mut self,
                                     attachment: FramebufferAttachment,
                                     tex_target: T::ImageTargetType,
                                     texture: &mut Texture<T>,
                                     level: i32)
    {
        debug_assert!(level == 0);

        unsafe {
            gl::FramebufferTexture2D(self.target(),
                                     attachment.gl_enum(),
                                     tex_target.gl_enum(),
                                     texture.gl_id(),
                                     level as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not an accepted attachment point, or `textarget` is not an accepted texture target and texture is not 0",
                GLError::InvalidValue => "`level` is not 0 and `texture` is not 0",
                GLError::InvalidOperation => "Framebuffer object 0 is bound, `texture` is neither 0 nor the name of an existing texture object, or `textarget` is not a valid target for `texture`",
                _ => "Unknown error"
            }
        }
    }

    pub fn clear(&mut self, buffers: BufferBits) {
        unsafe {
            gl::Clear(buffers.bits());
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`mask` includes a bit other than an allowed value",
                _ => "Unkown error"
            }
        }
    }
}

pub struct FramebufferBinder;
impl FramebufferBinder {
    pub fn bind<'a>(&'a mut self, fbo: &mut Framebuffer)
        -> FramebufferBinding<'a>
    {
        let binding = FramebufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindFramebuffer(binding.target(), fbo.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<|endoftext|>


<commit_before>#[derive(Copy, Clone, Debug)]
pub struct Score {
    /// The computed score value
    pub value: f32
}

impl Score {
    /// Creates a new Score with the provided value
    ///
    /// # Examples
    ///
    /// ```
    /// let score = rff::fuzzy::Score::new(1.0);
    /// assert_eq!(score.value, 1.0);
    /// ```
    pub fn new(value: f32) -> Score {
        Score {
            value: value
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
}
<commit_msg>Implement PartialOrd, PartialEq for Score<commit_after>use std::cmp::Ordering;

#[derive(Copy, Clone, Debug)]
pub struct Score {
    /// The computed score value
    pub value: f32
}

impl Score {
    /// Creates a new Score with the provided value
    ///
    /// # Examples
    ///
    /// ```
    /// let score = rff::fuzzy::Score::new(1.0);
    /// assert_eq!(score.value, 1.0);
    /// ```
    pub fn new(value: f32) -> Score {
        Score {
            value: value
        }
    }
}

impl PartialOrd for Score {
    fn partial_cmp(&self, other: &Score) -> Option<Ordering> {
        self.value.partial_cmp(&other.value)
    }
}

impl PartialEq for Score {
    fn eq(&self, other: &Score) -> bool {
        self.value == other.value
    }
}

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

    #[test]
    fn test_eq() {
        let a = Score::new(1.0);
        let b = Score::new(1.0);
        assert_eq!(a, b);
    }

    #[test]
    fn test_cmp() {
        let a = Score::new(2.0);
        let b = Score::new(1.0);
        assert!(a > b);
        assert!(b < a);

        let b = Score::new(2.0);
        assert!(a == b);
    }
}
<|endoftext|>


<commit_before>use base::world::{CHUNK_SIZE, Chunk, ChunkIndex, HexPillar, PropType, World};
use base::math::*;
use glium::{self, DrawParameters, VertexBuffer};
use glium::draw_parameters::{BackfaceCullingMode, DepthTest};
use glium::backend::Facade;
use glium::texture::Texture2d;
use glium::uniforms::SamplerWrapFunction;
use glium::uniforms::MinifySamplerFilter;
use Camera;
use DayTime;
use SimpleCull;
use LOCATION;
use util::ToArr;
use view::{PlantRenderer, PlantView};
use world::ChunkRenderer;
use std::rc::Rc;
use base::world::ground::GroundMaterial;

/// Graphical representation of the `base::Chunk`.
pub struct ChunkView {
    offset: AxialPoint,
    renderer: Rc<ChunkRenderer>,
    plant_renderer: Rc<PlantRenderer>,
    pillars: Vec<PillarView>,
    /// Instance data buffer.
    pillar_buf: VertexBuffer<Instance>,
    // save corner positions for draw
    corner_ps: Vec<Point3f>,
}

impl ChunkView {
    /// Creates the graphical representation of given chunk at the given chunk
    /// offset
    pub fn from_chunk<F: Facade>(chunk: &Chunk,
                                 offset: AxialPoint,
                                 chunk_renderer: Rc<ChunkRenderer>,
                                 plant_renderer: Rc<PlantRenderer>,
                                 facade: &F)
                                 -> Self {

        let mut sections = Vec::new();
        let mut pillars = Vec::new();
        let mut i = 0;
        let mut vec = Vec::new();
        {
            // scope so we can borrow mut vec
            let mut c = |x, pos: Point2f, z| {
                if x == 0 || x == CHUNK_SIZE || x == (CHUNK_SIZE * CHUNK_SIZE - CHUNK_SIZE) ||
                   x == (CHUNK_SIZE * CHUNK_SIZE - 1) {
                    vec.push(Point3f::new(pos.x, pos.y, z));
                }
            };
            for (axial, pillar) in chunk.pillars() {
                let pos = offset.to_real() + axial.to_real();
                let height = 150.;
                // save if corner (assume fixed location of corners)
                c(i, pos, height);
                c(i, pos, 0.);
                i += 1;
                pillars.push(PillarView::from_pillar(pillar, plant_renderer.clone(), facade));
                for section in pillar.sections() {
                    let g = match section.ground {
                        GroundMaterial::Grass => 1,
                        GroundMaterial::Sand => 2,
                        GroundMaterial::Snow => 3,
                        GroundMaterial::Dirt => 4,
                        GroundMaterial::Stone => 5,
                        GroundMaterial::JungleGrass => 1,
                        GroundMaterial::Mulch => 7,
                        GroundMaterial::Debug => 8,
                    };
                    sections.push(Instance {
                        material_color: section.ground.get_color(),
                        ground: g,
                        offset: [pos.x, pos.y, section.bottom.to_real()],
                        height: (section.top.units() - section.bottom.units()) as f32,
                    });
                }
            }
        }

        ChunkView {
            offset: offset,
            plant_renderer: plant_renderer,
            renderer: chunk_renderer,
            pillars: pillars,
            pillar_buf: VertexBuffer::dynamic(facade, &sections).unwrap(),
            corner_ps: vec,
        }
    }

    pub fn draw_shadow<S: glium::Surface>(&self, surface: &mut S, camera: &Camera) {
        let uniforms = uniform! {
            proj_matrix: camera.proj_matrix().to_arr(),
            view_matrix: camera.view_matrix().to_arr(),
        };

        let params = DrawParameters {
            depth: glium::Depth {
                write: true,
                test: DepthTest::IfLess,
                ..Default::default()
            },
            backface_culling: BackfaceCullingMode::CullClockwise,
            multisampling: true,
            ..Default::default()
        };

        surface.draw((self.renderer.pillar_vertices(), self.pillar_buf.per_instance().unwrap()),
                  self.renderer.pillar_indices(),
                  self.renderer.shadow_program(),
                  &uniforms,
                  &params)
            .unwrap();

        for pillar in &self.pillars {
            for plant in &pillar.plants {
                plant.draw_shadow(surface, camera);
            }
        }
    }

    pub fn update<F: Facade>(&mut self, facade: &F, world: &mut World) {
        let chunk = world.chunk_at(ChunkIndex(self.offset));

        if chunk.is_none() {
            return;
        }

        let mut sections = Vec::new();
        let mut pillars = Vec::new();

        for (axial, pillar) in chunk.unwrap().pillars() {
            let pos = self.offset.to_real() + axial.to_real();
            pillars.push(PillarView::from_pillar(pillar, self.plant_renderer.clone(), facade));
            for section in pillar.sections() {
                let g = match section.ground {
                    GroundMaterial::Grass => 1,
                    GroundMaterial::Sand => 2,
                    GroundMaterial::Snow => 3,
                    GroundMaterial::Dirt => 4,
                    GroundMaterial::Stone => 5,
                    GroundMaterial::JungleGrass => 1,
                    GroundMaterial::Mulch => 7,
                    GroundMaterial::Debug => 8,
                };
                sections.push(Instance {
                    ground: g,
                    material_color: section.ground.get_color(),
                    offset: [pos.x, pos.y, section.bottom.to_real()],
                    height: (section.top.units() - section.bottom.units()) as f32,
                });
            }
        }
        self.pillar_buf = VertexBuffer::dynamic(facade, &sections).unwrap();
    }

    pub fn draw<S: glium::Surface>(&self,
                                   surface: &mut S,
                                   camera: &Camera,
                                   shadow_map: &Texture2d,
                                   depth_view_proj: &Matrix4<f32>,
                                   daytime: &DayTime,
                                   sun_dir: Vector3f,
                                   frustum: &SimpleCull) {

        // such skill much wow :D
        let mut i = 0;
        let mut c = || {
            let a = i;
            i += 1;
            &self.corner_ps[a]
        };
        let corner = [c(), c(), c(), c(), c(), c(), c(), c()];

        let render = match frustum.is_vis(corner) {
            LOCATION::Outside => 0,
            LOCATION::Inside => 1,
            LOCATION::Intersect => 1,
        };
        if render == 0 {
            return;
        }
        let uniforms = uniform! {
            proj_matrix: camera.proj_matrix().to_arr(),
            view_matrix: camera.view_matrix().to_arr(),
            shadow_map: shadow_map.sampled().wrap_function(SamplerWrapFunction::Clamp),
            depth_view_proj: depth_view_proj.to_arr(),
            sun_dir: sun_dir.to_arr(),
            cam_pos: camera.position.to_arr(),
            sun_color: daytime.get_sun_color().to_arr(),
            sky_light: daytime.get_sky_light().to_arr(),

            // Mipmapping and repeating the textures
            sand_texture:  self.renderer.noise_sand.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),
            snow_texture:  self.renderer.noise_snow.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),
            grass_texture: self.renderer.noise_grass.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),
            stone_texture: self.renderer.noise_stone.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),
            dirt_texture: self.renderer.noise_dirt.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),
            mulch_texture: self.renderer.noise_mulch.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),

            normal_sand: &self.renderer.normal_sand,
            normal_snow: &self.renderer.normal_snow,
            normal_grass: &self.renderer.normal_grass,
            normal_stone: &self.renderer.normal_stone,
            normal_dirt: &self.renderer.normal_dirt,
            normal_mulch: &self.renderer.normal_mulch,
        };
        let params = DrawParameters {
            depth: glium::Depth {
                write: true,
                test: DepthTest::IfLess,
                ..Default::default()
            },
            backface_culling: BackfaceCullingMode::CullCounterClockwise,
            ..Default::default()
        };

        // Draw hexagons
        surface.draw((self.renderer.pillar_vertices(), self.pillar_buf.per_instance().unwrap()),
                  self.renderer.pillar_indices(),
                  self.renderer.program(),
                  &uniforms,
                  &params)
            .unwrap();
    }
}

/// Vertex type used to render chunks (or hex pillars).
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub normal: [f32; 3],
    pub radius: f32,
    pub tex_coords: [f32; 2],
}

implement_vertex!(Vertex, position, normal, radius, tex_coords);

/// Instance data for each pillar section.
#[derive(Debug, Copy, Clone)]
pub struct Instance {
    ground: i32,
    /// Material color.
    material_color: [f32; 3],
    /// Offset in world coordinates.
    offset: [f32; 3],
    /// Pillar height.
    height: f32,
}

implement_vertex!(Instance, material_color, offset, ground, height);

pub struct PillarView {
    plants: Vec<PlantView>,
}

impl PillarView {
    // fn from_pillar<F: Facade>( pos: Point2f,
    fn from_pillar<F: Facade>(pillar: &HexPillar,
                              plant_renderer: Rc<PlantRenderer>,
                              facade: &F)
                              -> PillarView {
        PillarView {
            plants: pillar.props()
                .iter()
                .map(|prop| {
                    match prop.prop {
                        PropType::Plant(ref plant) => {
                            PlantView::from_plant(plant, plant_renderer.clone(), facade)
                        }
                    }
                })
                .collect(),
        }
    }
}
<commit_msg>Improve performance by removing redudant draw call<commit_after>use base::world::{CHUNK_SIZE, Chunk, ChunkIndex, HexPillar, PropType, World};
use base::math::*;
use glium::{self, DrawParameters, VertexBuffer};
use glium::draw_parameters::{BackfaceCullingMode, DepthTest};
use glium::backend::Facade;
use glium::texture::Texture2d;
use glium::uniforms::SamplerWrapFunction;
use glium::uniforms::MinifySamplerFilter;
use Camera;
use DayTime;
use SimpleCull;
use LOCATION;
use util::ToArr;
use view::{PlantRenderer, PlantView};
use world::ChunkRenderer;
use std::rc::Rc;
use base::world::ground::GroundMaterial;

/// Graphical representation of the `base::Chunk`.
pub struct ChunkView {
    offset: AxialPoint,
    renderer: Rc<ChunkRenderer>,
    plant_renderer: Rc<PlantRenderer>,
    pillars: Vec<PillarView>,
    /// Instance data buffer.
    pillar_buf: VertexBuffer<Instance>,
    // save corner positions for draw
    corner_ps: Vec<Point3f>,
}

impl ChunkView {
    /// Creates the graphical representation of given chunk at the given chunk
    /// offset
    pub fn from_chunk<F: Facade>(chunk: &Chunk,
                                 offset: AxialPoint,
                                 chunk_renderer: Rc<ChunkRenderer>,
                                 plant_renderer: Rc<PlantRenderer>,
                                 facade: &F)
                                 -> Self {

        let mut sections = Vec::new();
        let mut pillars = Vec::new();
        let mut i = 0;
        let mut vec = Vec::new();
        {
            // scope so we can borrow mut vec
            let mut c = |x, pos: Point2f, z| {
                if x == 0 || x == CHUNK_SIZE || x == (CHUNK_SIZE * CHUNK_SIZE - CHUNK_SIZE) ||
                   x == (CHUNK_SIZE * CHUNK_SIZE - 1) {
                    vec.push(Point3f::new(pos.x, pos.y, z));
                }
            };
            for (axial, pillar) in chunk.pillars() {
                let pos = offset.to_real() + axial.to_real();
                let height = 150.;
                // save if corner (assume fixed location of corners)
                c(i, pos, height);
                c(i, pos, 0.);
                i += 1;
                pillars.push(PillarView::from_pillar(pillar, plant_renderer.clone(), facade));
                for section in pillar.sections() {
                    let g = match section.ground {
                        GroundMaterial::Grass => 1,
                        GroundMaterial::Sand => 2,
                        GroundMaterial::Snow => 3,
                        GroundMaterial::Dirt => 4,
                        GroundMaterial::Stone => 5,
                        GroundMaterial::JungleGrass => 1,
                        GroundMaterial::Mulch => 7,
                        GroundMaterial::Debug => 8,
                    };
                    sections.push(Instance {
                        material_color: section.ground.get_color(),
                        ground: g,
                        offset: [pos.x, pos.y, section.bottom.to_real()],
                        height: (section.top.units() - section.bottom.units()) as f32,
                    });
                }
            }
        }

        ChunkView {
            offset: offset,
            plant_renderer: plant_renderer,
            renderer: chunk_renderer,
            pillars: pillars,
            pillar_buf: VertexBuffer::dynamic(facade, &sections).unwrap(),
            corner_ps: vec,
        }
    }

    pub fn draw_shadow<S: glium::Surface>(&self, surface: &mut S, camera: &Camera) {
        let uniforms = uniform! {
            proj_matrix: camera.proj_matrix().to_arr(),
            view_matrix: camera.view_matrix().to_arr(),
        };

        let params = DrawParameters {
            depth: glium::Depth {
                write: true,
                test: DepthTest::IfLess,
                ..Default::default()
            },
            backface_culling: BackfaceCullingMode::CullClockwise,
            multisampling: true,
            ..Default::default()
        };

        surface.draw((self.renderer.pillar_vertices(), self.pillar_buf.per_instance().unwrap()),
                  self.renderer.pillar_indices(),
                  self.renderer.shadow_program(),
                  &uniforms,
                  &params)
            .unwrap();
    }

    pub fn update<F: Facade>(&mut self, facade: &F, world: &mut World) {
        let chunk = world.chunk_at(ChunkIndex(self.offset));

        if chunk.is_none() {
            return;
        }

        let mut sections = Vec::new();
        let mut pillars = Vec::new();

        for (axial, pillar) in chunk.unwrap().pillars() {
            let pos = self.offset.to_real() + axial.to_real();
            pillars.push(PillarView::from_pillar(pillar, self.plant_renderer.clone(), facade));
            for section in pillar.sections() {
                let g = match section.ground {
                    GroundMaterial::Grass => 1,
                    GroundMaterial::Sand => 2,
                    GroundMaterial::Snow => 3,
                    GroundMaterial::Dirt => 4,
                    GroundMaterial::Stone => 5,
                    GroundMaterial::JungleGrass => 1,
                    GroundMaterial::Mulch => 7,
                    GroundMaterial::Debug => 8,
                };
                sections.push(Instance {
                    ground: g,
                    material_color: section.ground.get_color(),
                    offset: [pos.x, pos.y, section.bottom.to_real()],
                    height: (section.top.units() - section.bottom.units()) as f32,
                });
            }
        }
        self.pillar_buf = VertexBuffer::dynamic(facade, &sections).unwrap();
    }

    pub fn draw<S: glium::Surface>(&self,
                                   surface: &mut S,
                                   camera: &Camera,
                                   shadow_map: &Texture2d,
                                   depth_view_proj: &Matrix4<f32>,
                                   daytime: &DayTime,
                                   sun_dir: Vector3f,
                                   frustum: &SimpleCull) {

        // such skill much wow :D
        let mut i = 0;
        let mut c = || {
            let a = i;
            i += 1;
            &self.corner_ps[a]
        };
        let corner = [c(), c(), c(), c(), c(), c(), c(), c()];

        let render = match frustum.is_vis(corner) {
            LOCATION::Outside => 0,
            LOCATION::Inside => 1,
            LOCATION::Intersect => 1,
        };
        if render == 0 {
            return;
        }
        let uniforms = uniform! {
            proj_matrix: camera.proj_matrix().to_arr(),
            view_matrix: camera.view_matrix().to_arr(),
            shadow_map: shadow_map.sampled().wrap_function(SamplerWrapFunction::Clamp),
            depth_view_proj: depth_view_proj.to_arr(),
            sun_dir: sun_dir.to_arr(),
            cam_pos: camera.position.to_arr(),
            sun_color: daytime.get_sun_color().to_arr(),
            sky_light: daytime.get_sky_light().to_arr(),

            // Mipmapping and repeating the textures
            sand_texture:  self.renderer.noise_sand.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),
            snow_texture:  self.renderer.noise_snow.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),
            grass_texture: self.renderer.noise_grass.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),
            stone_texture: self.renderer.noise_stone.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),
            dirt_texture: self.renderer.noise_dirt.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),
            mulch_texture: self.renderer.noise_mulch.sampled()
                .minify_filter(MinifySamplerFilter::NearestMipmapLinear)
                .wrap_function(SamplerWrapFunction::Repeat),

            normal_sand: &self.renderer.normal_sand,
            normal_snow: &self.renderer.normal_snow,
            normal_grass: &self.renderer.normal_grass,
            normal_stone: &self.renderer.normal_stone,
            normal_dirt: &self.renderer.normal_dirt,
            normal_mulch: &self.renderer.normal_mulch,
        };
        let params = DrawParameters {
            depth: glium::Depth {
                write: true,
                test: DepthTest::IfLess,
                ..Default::default()
            },
            backface_culling: BackfaceCullingMode::CullCounterClockwise,
            ..Default::default()
        };

        // Draw hexagons
        surface.draw((self.renderer.pillar_vertices(), self.pillar_buf.per_instance().unwrap()),
                  self.renderer.pillar_indices(),
                  self.renderer.program(),
                  &uniforms,
                  &params)
            .unwrap();
    }
}

/// Vertex type used to render chunks (or hex pillars).
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub normal: [f32; 3],
    pub radius: f32,
    pub tex_coords: [f32; 2],
}

implement_vertex!(Vertex, position, normal, radius, tex_coords);

/// Instance data for each pillar section.
#[derive(Debug, Copy, Clone)]
pub struct Instance {
    ground: i32,
    /// Material color.
    material_color: [f32; 3],
    /// Offset in world coordinates.
    offset: [f32; 3],
    /// Pillar height.
    height: f32,
}

implement_vertex!(Instance, material_color, offset, ground, height);

pub struct PillarView {
    plants: Vec<PlantView>,
}

impl PillarView {
    // fn from_pillar<F: Facade>( pos: Point2f,
    fn from_pillar<F: Facade>(pillar: &HexPillar,
                              plant_renderer: Rc<PlantRenderer>,
                              facade: &F)
                              -> PillarView {
        PillarView {
            plants: pillar.props()
                .iter()
                .map(|prop| {
                    match prop.prop {
                        PropType::Plant(ref plant) => {
                            PlantView::from_plant(plant, plant_renderer.clone(), facade)
                        }
                    }
                })
                .collect(),
        }
    }
}
<|endoftext|>


<commit_before>/* io::mod.rs */

use core::mem::volatile_store;
use kernel::sgash;

mod font;

/* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/BBABEGGE.html */
pub static UART0: *mut u32 = 0x101f1000 as *mut u32;

pub static UART0_IMSC: *mut u32 = (0x101f1000 + 0x038) as *mut u32;
#[allow(dead_code)]
pub static VIC_INTENABLE: *mut u32 = (0x10140000 + 0x010) as *mut u32;

// These store the current position of the cursor
pub static mut CURSOR_X: u32 = 0;
pub static mut CURSOR_Y: u32 = 0;
// These are set by the bitmaps in ./font.rs
pub static CURSOR_HEIGHT: u32 = 16;
pub static CURSOR_WIDTH: u32 = 8;
// Colors have their own setters
pub static mut CURSOR_COLOR: u32 = 0x00FFFFFF;
pub static mut FG_COLOR: u32 = 0x00FFFFFF;
pub static mut BG_COLOR: u32 = 0xF0000000;
pub static mut CURSOR_BUFFER: [u32, ..8*16] = [0x00FFFFFF, ..8*16];
pub static mut SAVE_X: u32 = 0;
pub static mut SAVE_Y: u32 = 0;
pub static START_ADDR: u32 = 1024*1024;
pub static mut SCREEN_WIDTH: u32 = 0;
pub static mut SCREEN_HEIGHT: u32 = 0;

pub unsafe fn init(width: u32, height: u32)
{
    SCREEN_WIDTH = width;
    SCREEN_HEIGHT= height;

    /* For the following magic values, see
    * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACHEDGD.html
    */

    // 800x600
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    if (SCREEN_WIDTH == 800 && SCREEN_HEIGHT == 600)
    {
        ws(0x10000010, 0x2CAC);
        ws(0x10120000, 0x1313A4C4);
        ws(0x10120004, 0x0505F657);
        ws(0x10120008, 0x071F1800);

        /* See http://forum.osdev.org/viewtopic.php?p=195000 */
        ws(0x10120010, START_ADDR);

        /* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
        ws(0x10120018, 0x82B);
    }

    // 640x480
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    else if (SCREEN_WIDTH == 640 && SCREEN_HEIGHT == 480)
    {
        ws(0x10000010, 0x2C77);
        ws(0x10120000, 0x3F1F3F9C);
        ws(0x10120004, 0x090B61DF);
        ws(0x10120008, 0x067F1800);

        /* See http://forum.osdev.org/viewtopic.php?p=195000 */
        ws(0x10120010, START_ADDR);

        /* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
        ws(0x10120018, 0x82B);

    }
    set_bg(0x000000);
    set_fg(0x79DF51);
    set_cursor_color(0xFFFFFF);
    fill_bg();
    draw_cursor();

    sgash::init();
}

pub unsafe fn write_char(c: char, address: *mut u32) {
    volatile_store(address, c as u32);
}

pub unsafe fn scrollup()
{
    let mut i = CURSOR_HEIGHT*SCREEN_WIDTH;
    while i < (SCREEN_WIDTH*SCREEN_HEIGHT)
    {
        *((START_ADDR + ((i-16*SCREEN_WIDTH)*4)) as *mut u32) = *((START_ADDR+(i*4)) as *u32);
        i += 1;
    }
    i = 4*(SCREEN_WIDTH*SCREEN_HEIGHT - CURSOR_HEIGHT*SCREEN_WIDTH);
    while i < 4*SCREEN_WIDTH*SCREEN_HEIGHT
    {
        *((START_ADDR + (i as u32)) as *mut u32) = BG_COLOR;
        i += 4;
    }
    CURSOR_X = 0x0u32;
    CURSOR_Y -= CURSOR_HEIGHT;
}
pub unsafe fn draw_char(c: char)
{
    while CURSOR_X+(SCREEN_WIDTH*CURSOR_Y) >= SCREEN_WIDTH*SCREEN_HEIGHT
    {
        scrollup();
    }
    let font_offset = (c as u8) - 0x20;
    let map = font::bitmaps[font_offset];

    let mut i = -1;
    let mut j = 0;
    let mut addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + 1 + SCREEN_WIDTH*CURSOR_Y);
    while j < CURSOR_HEIGHT
    {
        while i < CURSOR_WIDTH
        {
            //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH - i + SCREEN_WIDTH*(CURSOR_Y + j));
            //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + SCREEN_WIDTH*CURSOR_Y) - 4*i + 4*SCREEN_WIDTH*j
            if ((map[j] >> 4*i) & 1) == 1
            {
                *(addr as *mut u32) = FG_COLOR;
            }
            else
            {
                *(addr as *mut u32) = BG_COLOR;
            }

            addr-= 4;
            i += 1;
        }
        addr += 4*(i+SCREEN_WIDTH);
        i = 0;
        j += 1;
    }
}

pub unsafe fn backup()
{
    while CURSOR_X+(SCREEN_WIDTH*CURSOR_Y) >= SCREEN_WIDTH*SCREEN_HEIGHT
    {
        scrollup();
    }
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
        while i < CURSOR_WIDTH
        {
            let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
            CURSOR_BUFFER[i + j*8] = *(addr as *mut u32);
            i += 1;
        }
        i = 0;
        j += 1;
    }
    SAVE_X = CURSOR_X;
    SAVE_Y = CURSOR_Y;
}

pub unsafe fn restore()
{
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
        while i < CURSOR_WIDTH
        {
            let addr = START_ADDR + 4*(SAVE_X + i + SCREEN_WIDTH*(SAVE_Y + j));
            *(addr as *mut u32) = CURSOR_BUFFER[i + j*8];
            i += 1;
        }
        i = 0;
        j += 1;
    }
}

pub unsafe fn draw_cursor()
{

    let mut i = 0;
    let mut j = 0;

    while j < CURSOR_HEIGHT
    {
        while i < CURSOR_WIDTH
        {
            let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
            *(addr as *mut u32) = CURSOR_COLOR;
            i += 1;
        }
        i = 0;
        j += 1;
    }

}

pub unsafe fn paint(color: u32)
{
    let mut i = 0;
    while i < SCREEN_WIDTH*SCREEN_HEIGHT
    {
        *((START_ADDR as u32 + i*4) as *mut u32) = color;
        i+=1;
    }
}

pub unsafe fn fill_bg()
{
    paint(BG_COLOR);
}

#[allow(dead_code)]
pub unsafe fn read(addr: u32)   ->  u32
{
    *(addr as *mut u32)
}

pub unsafe fn ws(addr: u32, value: u32)
{
    *(addr as *mut u32) = *(addr as *mut u32) | value;
}

#[allow(dead_code)]
pub unsafe fn wh(addr: u32, value: u32)
{
    *(addr as *mut u32) = value;
}

pub unsafe fn set_fg(color: u32)
{
    FG_COLOR = color;
}

pub unsafe fn set_bg(color: u32)
{
    BG_COLOR = color;
}

pub unsafe fn set_cursor_color(color: u32)
{
    CURSOR_COLOR = color;
}
<commit_msg>Upside Down Done. Closes #3<commit_after>/* io::mod.rs */

use core::mem::volatile_store;
use kernel::sgash;

mod font;

/* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/BBABEGGE.html */
pub static UART0: *mut u32 = 0x101f1000 as *mut u32;

pub static UART0_IMSC: *mut u32 = (0x101f1000 + 0x038) as *mut u32;
#[allow(dead_code)]
pub static VIC_INTENABLE: *mut u32 = (0x10140000 + 0x010) as *mut u32;

// These store the current position of the cursor
pub static mut CURSOR_X: u32 = 0;
pub static mut CURSOR_Y: u32 = 0;
// These are set by the bitmaps in ./font.rs
pub static CURSOR_HEIGHT: u32 = 16;
pub static CURSOR_WIDTH: u32 = 8;
// Colors have their own setters
pub static mut CURSOR_COLOR: u32 = 0x00FFFFFF;
pub static mut FG_COLOR: u32 = 0x00FFFFFF;
pub static mut BG_COLOR: u32 = 0xF0000000;
pub static mut CURSOR_BUFFER: [u32, ..8*16] = [0x00FFFFFF, ..8*16];
pub static mut SAVE_X: u32 = 0;
pub static mut SAVE_Y: u32 = 0;
pub static START_ADDR: u32 = 1024*1024;
pub static mut SCREEN_WIDTH: u32 = 0;
pub static mut SCREEN_HEIGHT: u32 = 0;

// Draw all the chars upside down.
static UPSIDE_DOWN : bool = false;

pub unsafe fn init(width: u32, height: u32)
{
    SCREEN_WIDTH = width;
    SCREEN_HEIGHT= height;

    /* For the following magic values, see
    * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACHEDGD.html
    */

    // 800x600
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    if (SCREEN_WIDTH == 800 && SCREEN_HEIGHT == 600)
    {
        ws(0x10000010, 0x2CAC);
        ws(0x10120000, 0x1313A4C4);
        ws(0x10120004, 0x0505F657);
        ws(0x10120008, 0x071F1800);

        /* See http://forum.osdev.org/viewtopic.php?p=195000 */
        ws(0x10120010, START_ADDR);

        /* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
        ws(0x10120018, 0x82B);
    }

    // 640x480
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    else if (SCREEN_WIDTH == 640 && SCREEN_HEIGHT == 480)
    {
        ws(0x10000010, 0x2C77);
        ws(0x10120000, 0x3F1F3F9C);
        ws(0x10120004, 0x090B61DF);
        ws(0x10120008, 0x067F1800);

        /* See http://forum.osdev.org/viewtopic.php?p=195000 */
        ws(0x10120010, START_ADDR);

        /* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
        ws(0x10120018, 0x82B);

    }
    set_bg(0x000000);
    set_fg(0x79DF51);
    set_cursor_color(0xFFFFFF);
    fill_bg();
    draw_cursor();

    sgash::init();
}

pub unsafe fn write_char(c: char, address: *mut u32) {
    volatile_store(address, c as u32);
}

pub unsafe fn scrollup()
{
    let mut i = CURSOR_HEIGHT*SCREEN_WIDTH;
    while i < (SCREEN_WIDTH*SCREEN_HEIGHT)
    {
        *((START_ADDR + ((i-16*SCREEN_WIDTH)*4)) as *mut u32) = *((START_ADDR+(i*4)) as *u32);
        i += 1;
    }
    i = 4*(SCREEN_WIDTH*SCREEN_HEIGHT - CURSOR_HEIGHT*SCREEN_WIDTH);
    while i < 4*SCREEN_WIDTH*SCREEN_HEIGHT
    {
        *((START_ADDR + (i as u32)) as *mut u32) = BG_COLOR;
        i += 4;
    }
    CURSOR_X = 0x0u32;
    CURSOR_Y -= CURSOR_HEIGHT;
}
pub unsafe fn draw_char(c: char)
{
    while CURSOR_X+(SCREEN_WIDTH*CURSOR_Y) >= SCREEN_WIDTH*SCREEN_HEIGHT
    {
        scrollup();
    }
    let font_offset = (c as u8) - 0x20;
    let map = font::bitmaps[font_offset];

    let mut i = -1;
    let mut j = 0;
    let mut addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + 1 + SCREEN_WIDTH*CURSOR_Y);
    while j < CURSOR_HEIGHT
    {
        while i < CURSOR_WIDTH
        {
            //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH - i + SCREEN_WIDTH*(CURSOR_Y + j));
            //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + SCREEN_WIDTH*CURSOR_Y) - 4*i + 4*SCREEN_WIDTH*j

            let map_loc = if !UPSIDE_DOWN { j } else { 15-j };
            if ((map[map_loc] >> 4*i) & 1) == 1
            {
                *(addr as *mut u32) = FG_COLOR;
            }
            else
            {
                *(addr as *mut u32) = BG_COLOR;
            }

            addr-= 4;
            i += 1;
        }
        addr += 4*(i+SCREEN_WIDTH);
        i = 0;
        j += 1;
    }
}

pub unsafe fn backup()
{
    while CURSOR_X+(SCREEN_WIDTH*CURSOR_Y) >= SCREEN_WIDTH*SCREEN_HEIGHT
    {
        scrollup();
    }
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
        while i < CURSOR_WIDTH
        {
            let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
            CURSOR_BUFFER[i + j*8] = *(addr as *mut u32);
            i += 1;
        }
        i = 0;
        j += 1;
    }
    SAVE_X = CURSOR_X;
    SAVE_Y = CURSOR_Y;
}

pub unsafe fn restore()
{
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
        while i < CURSOR_WIDTH
        {
            let addr = START_ADDR + 4*(SAVE_X + i + SCREEN_WIDTH*(SAVE_Y + j));
            *(addr as *mut u32) = CURSOR_BUFFER[i + j*8];
            i += 1;
        }
        i = 0;
        j += 1;
    }
}

pub unsafe fn draw_cursor()
{

    let mut i = 0;
    let mut j = 0;

    while j < CURSOR_HEIGHT
    {
        while i < CURSOR_WIDTH
        {
            let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
            *(addr as *mut u32) = CURSOR_COLOR;
            i += 1;
        }
        i = 0;
        j += 1;
    }

}

pub unsafe fn paint(color: u32)
{
    let mut i = 0;
    while i < SCREEN_WIDTH*SCREEN_HEIGHT
    {
        *((START_ADDR as u32 + i*4) as *mut u32) = color;
        i+=1;
    }
}

pub unsafe fn fill_bg()
{
    paint(BG_COLOR);
}

#[allow(dead_code)]
pub unsafe fn read(addr: u32)   ->  u32
{
    *(addr as *mut u32)
}

pub unsafe fn ws(addr: u32, value: u32)
{
    *(addr as *mut u32) = *(addr as *mut u32) | value;
}

#[allow(dead_code)]
pub unsafe fn wh(addr: u32, value: u32)
{
    *(addr as *mut u32) = value;
}

pub unsafe fn set_fg(color: u32)
{
    FG_COLOR = color;
}

pub unsafe fn set_bg(color: u32)
{
    BG_COLOR = color;
}

pub unsafe fn set_cursor_color(color: u32)
{
    CURSOR_COLOR = color;
}
<|endoftext|>


<commit_before>/* io::mod.rs */

use core::mem::volatile_store;
use kernel::sgash;

mod font;

/* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/BBABEGGE.html */
pub static UART0: *mut u32 = 0x101f1000 as *mut u32;

pub static UART0_IMSC: *mut u32 = (0x101f1000 + 0x038) as *mut u32;
#[allow(dead_code)]
pub static VIC_INTENABLE: *mut u32 = (0x10140000 + 0x010) as *mut u32;

pub static mut CURSOR_X: u32 = 0;
pub static mut CURSOR_Y: u32 = 0;
pub static CURSOR_HEIGHT: u32 = 16;
pub static CURSOR_WIDTH: u32 = 8;
pub static mut CURSOR_COLOR: u32 = 0x000000FF;
pub static mut FG_COLOR: u32 = 0x00FFFFFF;
pub static mut BG_COLOR: u32 = 0xF0000000;
pub static mut CURSOR_BUFFER: [u32, ..8*16] = [0x00FF0000, ..8*16];
pub static mut SAVE_X: u32 = 0;
pub static mut SAVE_Y: u32 = 0;
pub static START_ADDR: u32 = 1024*1024;
pub static mut SCREEN_WIDTH: u32 = 0;
pub static mut SCREEN_HEIGHT: u32 = 0;

pub unsafe fn init(width: u32, height: u32)
{
    SCREEN_WIDTH = width;
    SCREEN_HEIGHT= height;
    sgash::init();

    /* For the following magic values, see
     * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACHEDGD.html
     */

    // 800x600
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    if (SCREEN_WIDTH == 800 && SCREEN_HEIGHT == 600)
    {
    	ws(0x10000010, 0x2CAC);
    	ws(0x10120000, 0x1313A4C4);
    	ws(0x10120004, 0x0505F657);
    	ws(0x10120008, 0x071F1800);

	/* See http://forum.osdev.org/viewtopic.php?p=195000 */
	ws(0x10120010, START_ADDR);

	/* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
	ws(0x10120018, 0x82B);
    }

    // 640x480
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    else if (SCREEN_WIDTH == 640 && SCREEN_HEIGHT == 480)
    {
	ws(0x10000010, 0x2C77);
	ws(0x10120000, 0x3F1F3F9C);
	ws(0x10120004, 0x090B61DF);
	ws(0x10120008, 0x067F1800);

	/* See http://forum.osdev.org/viewtopic.php?p=195000 */
	ws(0x10120010, START_ADDR);

	/* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
	ws(0x10120018, 0x82B);

    }
    set_bg(0x222C38);
    set_fg(0xFAFCFF);
    set_cursor_color(0xFAFCFF);
    fill_bg();
    sgash::drawstr(&"sgash > ");
    draw_cursor();
}

pub unsafe fn write_char(c: char, address: *mut u32) {
    volatile_store(address, c as u32);
}

pub unsafe fn scrollup()
{
    let mut i = CURSOR_HEIGHT*SCREEN_WIDTH;
    while i < (SCREEN_WIDTH*SCREEN_HEIGHT)
    {
	*((START_ADDR + ((i-16*SCREEN_WIDTH)*4)) as *mut u32) = *((START_ADDR+(i*4)) as *u32);
	i += 1;
    }
    i = 4*(SCREEN_WIDTH*SCREEN_HEIGHT - CURSOR_HEIGHT*SCREEN_WIDTH);
    while i < 4*SCREEN_WIDTH*SCREEN_HEIGHT
    {
	*((START_ADDR + (i as u32)) as *mut u32) = BG_COLOR;
	i += 4;
    }
    CURSOR_X = 0x0u32;
    CURSOR_Y -= CURSOR_HEIGHT;
}
pub unsafe fn draw_char(c: char)
{
    if CURSOR_X+(SCREEN_WIDTH*CURSOR_Y) >= SCREEN_WIDTH*SCREEN_HEIGHT
    {
	scrollup();
    }
    let font_offset = (c as u8) - 0x20;
    let map = font::bitmaps[font_offset];

    let mut i = -1;
    let mut j = 0;
    let mut addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + 1 + SCREEN_WIDTH*CURSOR_Y);
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH - i + SCREEN_WIDTH*(CURSOR_Y + j));
	    //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + SCREEN_WIDTH*CURSOR_Y) - 4*i + 4*SCREEN_WIDTH*j
	    if ((map[j] >> 4*i) & 1) == 1
	    {
		*(addr as *mut u32) = FG_COLOR;
	    }
	    else
	    {
		*(addr as *mut u32) = BG_COLOR;
	    }

	    addr-= 4;
	    i += 1;
	}
	addr += 4*(i+SCREEN_WIDTH);
	i = 0;
	j += 1;
    }
}

pub unsafe fn backup()
{
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
	    CURSOR_BUFFER[i + j*8] = *(addr as *mut u32);
	    i += 1;
	}
	i = 0;
	j += 1;
    }
    SAVE_X = CURSOR_X;
    SAVE_Y = CURSOR_Y;
}

pub unsafe fn restore()
{
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(SAVE_X + i + SCREEN_WIDTH*(SAVE_Y + j));
	    *(addr as *mut u32) = CURSOR_BUFFER[i + j*8];
	    i += 1;
	}
	i = 0;
	j += 1;
    }
}

pub unsafe fn draw_cursor()
{

    let mut i = 0;
    let mut j = 0;

    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
	    *(addr as *mut u32) = CURSOR_COLOR;
	    i += 1;
	}
	i = 0;
	j += 1;
    }

}

pub unsafe fn paint(color: u32)
{
    let mut i = 0;
    while i < SCREEN_WIDTH*SCREEN_HEIGHT
    {
	*((START_ADDR as u32 + i*4) as *mut u32) = color;
	i+=1;
    }
}

pub unsafe fn fill_bg()
{
    paint(BG_COLOR);
}

#[allow(dead_code)]
pub unsafe fn read(addr: u32)	->	u32
{
    *(addr as *mut u32)
}

pub unsafe fn ws(addr: u32, value: u32)
{
    *(addr as *mut u32) = *(addr as *mut u32) | value;
}

#[allow(dead_code)]
pub unsafe fn wh(addr: u32, value: u32)
{
    *(addr as *mut u32) = value;
}

pub unsafe fn set_fg(color: u32)
{
    FG_COLOR = color;
}

pub unsafe fn set_bg(color: u32)
{
    BG_COLOR = color;
}

pub unsafe fn set_cursor_color(color: u32)
{
    CURSOR_COLOR = color;
}
<commit_msg>changed background and cursor color in arch/arm/io/mod.rs<commit_after>/* io::mod.rs */

use core::mem::volatile_store;
use kernel::sgash;

mod font;

/* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/BBABEGGE.html */
pub static UART0: *mut u32 = 0x101f1000 as *mut u32;

pub static UART0_IMSC: *mut u32 = (0x101f1000 + 0x038) as *mut u32;
#[allow(dead_code)]
pub static VIC_INTENABLE: *mut u32 = (0x10140000 + 0x010) as *mut u32;

pub static mut CURSOR_X: u32 = 0;
pub static mut CURSOR_Y: u32 = 0;
pub static CURSOR_HEIGHT: u32 = 16;
pub static CURSOR_WIDTH: u32 = 8;
pub static mut CURSOR_COLOR: u32 = 0x0FADFA00;
pub static mut FG_COLOR: u32 = 0x00FFFFFF;
pub static mut BG_COLOR: u32 = 0xFA270F00;
pub static mut CURSOR_BUFFER: [u32, ..8*16] = [0x00FF0000, ..8*16];
pub static mut SAVE_X: u32 = 0;
pub static mut SAVE_Y: u32 = 0;
pub static START_ADDR: u32 = 1024*1024;
pub static mut SCREEN_WIDTH: u32 = 0;
pub static mut SCREEN_HEIGHT: u32 = 0;

pub unsafe fn init(width: u32, height: u32)
{
    SCREEN_WIDTH = width;
    SCREEN_HEIGHT= height;
    sgash::init();

    /* For the following magic values, see
     * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACHEDGD.html
     */

    // 800x600
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    if (SCREEN_WIDTH == 800 && SCREEN_HEIGHT == 600)
    {
    	ws(0x10000010, 0x2CAC);
    	ws(0x10120000, 0x1313A4C4);
    	ws(0x10120004, 0x0505F657);
    	ws(0x10120008, 0x071F1800);

	/* See http://forum.osdev.org/viewtopic.php?p=195000 */
	ws(0x10120010, START_ADDR);

	/* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
	ws(0x10120018, 0x82B);
    }

    // 640x480
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    else if (SCREEN_WIDTH == 640 && SCREEN_HEIGHT == 480)
    {
	ws(0x10000010, 0x2C77);
	ws(0x10120000, 0x3F1F3F9C);
	ws(0x10120004, 0x090B61DF);
	ws(0x10120008, 0x067F1800);

	/* See http://forum.osdev.org/viewtopic.php?p=195000 */
	ws(0x10120010, START_ADDR);

	/* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
	ws(0x10120018, 0x82B);

    }
    set_bg(0x222C38);
    set_fg(0xFAFCFF);
    set_cursor_color(0xFAFCFF);
    fill_bg();
    sgash::drawstr(&"sgash > ");
    draw_cursor();
}

pub unsafe fn write_char(c: char, address: *mut u32) {
    volatile_store(address, c as u32);
}

pub unsafe fn scrollup()
{
    let mut i = CURSOR_HEIGHT*SCREEN_WIDTH;
    while i < (SCREEN_WIDTH*SCREEN_HEIGHT)
    {
	*((START_ADDR + ((i-16*SCREEN_WIDTH)*4)) as *mut u32) = *((START_ADDR+(i*4)) as *u32);
	i += 1;
    }
    i = 4*(SCREEN_WIDTH*SCREEN_HEIGHT - CURSOR_HEIGHT*SCREEN_WIDTH);
    while i < 4*SCREEN_WIDTH*SCREEN_HEIGHT
    {
	*((START_ADDR + (i as u32)) as *mut u32) = BG_COLOR;
	i += 4;
    }
    CURSOR_X = 0x0u32;
    CURSOR_Y -= CURSOR_HEIGHT;
}
pub unsafe fn draw_char(c: char)
{
    if CURSOR_X+(SCREEN_WIDTH*CURSOR_Y) >= SCREEN_WIDTH*SCREEN_HEIGHT
    {
	scrollup();
    }
    let font_offset = (c as u8) - 0x20;
    let map = font::bitmaps[font_offset];

    let mut i = -1;
    let mut j = 0;
    let mut addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + 1 + SCREEN_WIDTH*CURSOR_Y);
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH - i + SCREEN_WIDTH*(CURSOR_Y + j));
	    //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + SCREEN_WIDTH*CURSOR_Y) - 4*i + 4*SCREEN_WIDTH*j
	    if ((map[j] >> 4*i) & 1) == 1
	    {
		*(addr as *mut u32) = FG_COLOR;
	    }
	    else
	    {
		*(addr as *mut u32) = BG_COLOR;
	    }

	    addr-= 4;
	    i += 1;
	}
	addr += 4*(i+SCREEN_WIDTH);
	i = 0;
	j += 1;
    }
}

pub unsafe fn backup()
{
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
	    CURSOR_BUFFER[i + j*8] = *(addr as *mut u32);
	    i += 1;
	}
	i = 0;
	j += 1;
    }
    SAVE_X = CURSOR_X;
    SAVE_Y = CURSOR_Y;
}

pub unsafe fn restore()
{
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(SAVE_X + i + SCREEN_WIDTH*(SAVE_Y + j));
	    *(addr as *mut u32) = CURSOR_BUFFER[i + j*8];
	    i += 1;
	}
	i = 0;
	j += 1;
    }
}

pub unsafe fn draw_cursor()
{

    let mut i = 0;
    let mut j = 0;

    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
	    *(addr as *mut u32) = CURSOR_COLOR;
	    i += 1;
	}
	i = 0;
	j += 1;
    }

}

pub unsafe fn paint(color: u32)
{
    let mut i = 0;
    while i < SCREEN_WIDTH*SCREEN_HEIGHT
    {
	*((START_ADDR as u32 + i*4) as *mut u32) = color;
	i+=1;
    }
}

pub unsafe fn fill_bg()
{
    paint(BG_COLOR);
}

#[allow(dead_code)]
pub unsafe fn read(addr: u32)	->	u32
{
    *(addr as *mut u32)
}

pub unsafe fn ws(addr: u32, value: u32)
{
    *(addr as *mut u32) = *(addr as *mut u32) | value;
}

#[allow(dead_code)]
pub unsafe fn wh(addr: u32, value: u32)
{
    *(addr as *mut u32) = value;
}

pub unsafe fn set_fg(color: u32)
{
    FG_COLOR = color;
}

pub unsafe fn set_bg(color: u32)
{
    BG_COLOR = color;
}

pub unsafe fn set_cursor_color(color: u32)
{
    CURSOR_COLOR = color;
}
<|endoftext|>