<commit_before>use std::{
    collections::HashMap,
    path::{Path, PathBuf},
};

use xshell::{cmd, read_file};
use xtask::{
    codegen::{self, Mode},
    project_root, run_rustfmt, rust_files,
};

#[test]
fn generated_grammar_is_fresh() {
    if let Err(error) = codegen::generate_syntax(Mode::Verify) {
        panic!("{}. Please update it by running `cargo xtask codegen`", error);
    }
}

#[test]
fn generated_tests_are_fresh() {
    if let Err(error) = codegen::generate_parser_tests(Mode::Verify) {
        panic!("{}. Please update tests by running `cargo xtask codegen`", error);
    }
}

#[test]
fn generated_assists_are_fresh() {
    if let Err(error) = codegen::generate_assists_tests(Mode::Verify) {
        panic!("{}. Please update assists by running `cargo xtask codegen`", error);
    }
}

#[test]
fn check_code_formatting() {
    if let Err(error) = run_rustfmt(Mode::Verify) {
        panic!("{}. Please format the code by running `cargo format`", error);
    }
}

#[test]
fn smoke_test_docs_generation() {
    // We don't commit docs to the repo, so we can just overwrite in tests.
    codegen::generate_assists_docs(Mode::Overwrite).unwrap();
    codegen::generate_feature_docs(Mode::Overwrite).unwrap();
    codegen::generate_diagnostic_docs(Mode::Overwrite).unwrap();
}

#[test]
fn check_lsp_extensions_docs() {
    let expected_hash = {
        let lsp_ext_rs =
            read_file(project_root().join("crates/rust-analyzer/src/lsp_ext.rs")).unwrap();
        stable_hash(lsp_ext_rs.as_str())
    };

    let actual_hash = {
        let lsp_extensions_md =
            read_file(project_root().join("docs/dev/lsp-extensions.md")).unwrap();
        let text = lsp_extensions_md
            .lines()
            .find_map(|line| line.strip_prefix("lsp_ext.rs hash:"))
            .unwrap()
            .trim();
        u64::from_str_radix(text, 16).unwrap()
    };

    if actual_hash != expected_hash {
        panic!(
            "
lsp_ext.rs was changed without touching lsp-extensions.md.

Expected hash: {:x}
Actual hash:   {:x}

Please adjust docs/dev/lsp-extensions.md.
",
            expected_hash, actual_hash
        )
    }
}

#[test]
fn rust_files_are_tidy() {
    let mut tidy_docs = TidyDocs::default();
    for path in rust_files(&project_root().join("crates")) {
        let text = read_file(&path).unwrap();
        check_todo(&path, &text);
        check_trailing_ws(&path, &text);
        deny_clippy(&path, &text);
        tidy_docs.visit(&path, &text);
    }
    tidy_docs.finish();
}

#[test]
fn check_merge_commits() {
    let stdout =
        dbg!(cmd!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19.."))
            .read()
            .unwrap();
    if !stdout.is_empty() {
        panic!(
            "
Merge commits are not allowed in the history.

When updating a pull-request, please rebase your feature branch
on top of master by running `git rebase master`. If rebase fails,
you can re-apply your changes like this:

  # Abort in-progress rebase, if any.
  $ git rebase --abort

  # Make the branch point to the latest commit from master,
  # while maintaining your local changes uncommited.
  $ git reset --soft origin/master

  # Commit all changes in a single batch.
  $ git commit -am'My changes'

  # Push the changes. We did a rebase, so we need `--force` option.
  # `--force-with-lease` is a more safe (Rusty) version of `--force`.
  $ git push --force-with-lease

And don't fear to mess something up during a rebase -- you can
always restore the previous state using `git ref-log`:

https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/#redo-after-undo-local
"
        );
    }
}

fn deny_clippy(path: &PathBuf, text: &String) {
    let ignore = &[
        // The documentation in string literals may contain anything for its own purposes
        "completion/src/generated_lint_completions.rs",
    ];
    if ignore.iter().any(|p| path.ends_with(p)) {
        return;
    }

    if text.contains("[\u{61}llow(clippy") {
        panic!(
            "\n\nallowing lints is forbidden: {}.
rust-analyzer intentionally doesn't check clippy on CI.
You can allow lint globally via `xtask clippy`.
See https://github.com/rust-lang/rust-clippy/issues/5537 for discussion.

",
            path.display()
        )
    }
}

#[test]
fn check_licenses() {
    let expected = "
0BSD OR MIT OR Apache-2.0
Apache-2.0
Apache-2.0 OR BSL-1.0
Apache-2.0 OR MIT
Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT
Apache-2.0/MIT
BSD-3-Clause
CC0-1.0
ISC
MIT
MIT / Apache-2.0
MIT OR Apache-2.0
MIT OR Apache-2.0 OR Zlib
MIT OR Zlib OR Apache-2.0
MIT/Apache-2.0
Unlicense OR MIT
Unlicense/MIT
Zlib OR Apache-2.0 OR MIT
"
    .lines()
    .filter(|it| !it.is_empty())
    .collect::<Vec<_>>();

    let meta = cmd!("cargo metadata --format-version 1").read().unwrap();
    let mut licenses = meta
        .split(|c| c == ',' || c == '{' || c == '}')
        .filter(|it| it.contains(r#""license""#))
        .map(|it| it.trim())
        .map(|it| it[r#""license":"#.len()..].trim_matches('"'))
        .collect::<Vec<_>>();
    licenses.sort();
    licenses.dedup();
    if licenses != expected {
        let mut diff = String::new();

        diff += &format!("New Licenses:\n");
        for &l in licenses.iter() {
            if !expected.contains(&l) {
                diff += &format!("  {}\n", l)
            }
        }

        diff += &format!("\nMissing Licenses:\n");
        for &l in expected.iter() {
            if !licenses.contains(&l) {
                diff += &format!("  {}\n", l)
            }
        }

        panic!("different set of licenses!\n{}", diff);
    }
    assert_eq!(licenses, expected);
}

fn check_todo(path: &Path, text: &str) {
    let need_todo = &[
        // This file itself obviously needs to use todo (<- like this!).
        "tests/cli.rs",
        // Some of our assists generate `todo!()`.
        "handlers/add_turbo_fish.rs",
        "handlers/generate_function.rs",
        // To support generating `todo!()` in assists, we have `expr_todo()` in
        // `ast::make`.
        "ast/make.rs",
        // The documentation in string literals may contain anything for its own purposes
        "completion/src/generated_lint_completions.rs",
    ];
    if need_todo.iter().any(|p| path.ends_with(p)) {
        return;
    }
    if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
        // Generated by an assist
        if text.contains("${0:todo!()}") {
            return;
        }

        panic!(
            "\nTODO markers or todo! macros should not be committed to the master branch,\n\
             use FIXME instead\n\
             {}\n",
            path.display(),
        )
    }
}

fn check_trailing_ws(path: &Path, text: &str) {
    if is_exclude_dir(path, &["test_data"]) {
        return;
    }
    for (line_number, line) in text.lines().enumerate() {
        if line.chars().last().map(char::is_whitespace) == Some(true) {
            panic!("Trailing whitespace in {} at line {}", path.display(), line_number)
        }
    }
}

#[derive(Default)]
struct TidyDocs {
    missing_docs: Vec<String>,
    contains_fixme: Vec<PathBuf>,
}

impl TidyDocs {
    fn visit(&mut self, path: &Path, text: &str) {
        // Test hopefully don't really need comments, and for assists we already
        // have special comments which are source of doc tests and user docs.
        if is_exclude_dir(path, &["tests", "test_data"]) {
            return;
        }

        if is_exclude_file(path) {
            return;
        }

        let first_line = match text.lines().next() {
            Some(it) => it,
            None => return,
        };

        if first_line.starts_with("//!") {
            if first_line.contains("FIXME") {
                self.contains_fixme.push(path.to_path_buf());
            }
        } else {
            if text.contains("// Feature:") || text.contains("// Assist:") {
                return;
            }
            self.missing_docs.push(path.display().to_string());
        }

        fn is_exclude_file(d: &Path) -> bool {
            let file_names = ["tests.rs"];

            d.file_name()
                .unwrap_or_default()
                .to_str()
                .map(|f_n| file_names.iter().any(|name| *name == f_n))
                .unwrap_or(false)
        }
    }

    fn finish(self) {
        if !self.missing_docs.is_empty() {
            panic!(
                "\nMissing docs strings\n\n\
                 modules:\n{}\n\n",
                self.missing_docs.join("\n")
            )
        }

        let poorly_documented = [
            "hir",
            "hir_expand",
            "ide",
            "mbe",
            "parser",
            "profile",
            "project_model",
            "syntax",
            "tt",
            "hir_ty",
        ];

        let mut has_fixmes =
            poorly_documented.iter().map(|it| (*it, false)).collect::<HashMap<&str, bool>>();
        'outer: for path in self.contains_fixme {
            for krate in poorly_documented.iter() {
                if path.components().any(|it| it.as_os_str() == *krate) {
                    has_fixmes.insert(krate, true);
                    continue 'outer;
                }
            }
            panic!("FIXME doc in a fully-documented crate: {}", path.display())
        }

        for (krate, has_fixme) in has_fixmes.iter() {
            if !has_fixme {
                panic!("crate {} is fully documented :tada:, remove it from the list of poorly documented crates", krate)
            }
        }
    }
}

fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool {
    p.strip_prefix(project_root())
        .unwrap()
        .components()
        .rev()
        .skip(1)
        .filter_map(|it| it.as_os_str().to_str())
        .any(|it| dirs_to_exclude.contains(&it))
}

#[allow(deprecated)]
fn stable_hash(text: &str) -> u64 {
    use std::hash::{Hash, Hasher, SipHasher};

    let text = text.replace('\r', "");
    let mut hasher = SipHasher::default();
    text.hash(&mut hasher);
    hasher.finish()
}
<commit_msg>deny_clippy test works with cfg_attr<commit_after>use std::{
    collections::HashMap,
    path::{Path, PathBuf},
};

use xshell::{cmd, read_file};
use xtask::{
    codegen::{self, Mode},
    project_root, run_rustfmt, rust_files,
};

#[test]
fn generated_grammar_is_fresh() {
    if let Err(error) = codegen::generate_syntax(Mode::Verify) {
        panic!("{}. Please update it by running `cargo xtask codegen`", error);
    }
}

#[test]
fn generated_tests_are_fresh() {
    if let Err(error) = codegen::generate_parser_tests(Mode::Verify) {
        panic!("{}. Please update tests by running `cargo xtask codegen`", error);
    }
}

#[test]
fn generated_assists_are_fresh() {
    if let Err(error) = codegen::generate_assists_tests(Mode::Verify) {
        panic!("{}. Please update assists by running `cargo xtask codegen`", error);
    }
}

#[test]
fn check_code_formatting() {
    if let Err(error) = run_rustfmt(Mode::Verify) {
        panic!("{}. Please format the code by running `cargo format`", error);
    }
}

#[test]
fn smoke_test_docs_generation() {
    // We don't commit docs to the repo, so we can just overwrite in tests.
    codegen::generate_assists_docs(Mode::Overwrite).unwrap();
    codegen::generate_feature_docs(Mode::Overwrite).unwrap();
    codegen::generate_diagnostic_docs(Mode::Overwrite).unwrap();
}

#[test]
fn check_lsp_extensions_docs() {
    let expected_hash = {
        let lsp_ext_rs =
            read_file(project_root().join("crates/rust-analyzer/src/lsp_ext.rs")).unwrap();
        stable_hash(lsp_ext_rs.as_str())
    };

    let actual_hash = {
        let lsp_extensions_md =
            read_file(project_root().join("docs/dev/lsp-extensions.md")).unwrap();
        let text = lsp_extensions_md
            .lines()
            .find_map(|line| line.strip_prefix("lsp_ext.rs hash:"))
            .unwrap()
            .trim();
        u64::from_str_radix(text, 16).unwrap()
    };

    if actual_hash != expected_hash {
        panic!(
            "
lsp_ext.rs was changed without touching lsp-extensions.md.

Expected hash: {:x}
Actual hash:   {:x}

Please adjust docs/dev/lsp-extensions.md.
",
            expected_hash, actual_hash
        )
    }
}

#[test]
fn rust_files_are_tidy() {
    let mut tidy_docs = TidyDocs::default();
    for path in rust_files(&project_root().join("crates")) {
        let text = read_file(&path).unwrap();
        check_todo(&path, &text);
        check_trailing_ws(&path, &text);
        deny_clippy(&path, &text);
        tidy_docs.visit(&path, &text);
    }
    tidy_docs.finish();
}

#[test]
fn check_merge_commits() {
    let stdout =
        dbg!(cmd!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19.."))
            .read()
            .unwrap();
    if !stdout.is_empty() {
        panic!(
            "
Merge commits are not allowed in the history.

When updating a pull-request, please rebase your feature branch
on top of master by running `git rebase master`. If rebase fails,
you can re-apply your changes like this:

  # Abort in-progress rebase, if any.
  $ git rebase --abort

  # Make the branch point to the latest commit from master,
  # while maintaining your local changes uncommited.
  $ git reset --soft origin/master

  # Commit all changes in a single batch.
  $ git commit -am'My changes'

  # Push the changes. We did a rebase, so we need `--force` option.
  # `--force-with-lease` is a more safe (Rusty) version of `--force`.
  $ git push --force-with-lease

And don't fear to mess something up during a rebase -- you can
always restore the previous state using `git ref-log`:

https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/#redo-after-undo-local
"
        );
    }
}

fn deny_clippy(path: &PathBuf, text: &String) {
    let ignore = &[
        // The documentation in string literals may contain anything for its own purposes
        "completion/src/generated_lint_completions.rs",
    ];
    if ignore.iter().any(|p| path.ends_with(p)) {
        return;
    }

    if text.contains("\u{61}llow(clippy") {
        panic!(
            "\n\nallowing lints is forbidden: {}.
rust-analyzer intentionally doesn't check clippy on CI.
You can allow lint globally via `xtask clippy`.
See https://github.com/rust-lang/rust-clippy/issues/5537 for discussion.

",
            path.display()
        )
    }
}

#[test]
fn check_licenses() {
    let expected = "
0BSD OR MIT OR Apache-2.0
Apache-2.0
Apache-2.0 OR BSL-1.0
Apache-2.0 OR MIT
Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT
Apache-2.0/MIT
BSD-3-Clause
CC0-1.0
ISC
MIT
MIT / Apache-2.0
MIT OR Apache-2.0
MIT OR Apache-2.0 OR Zlib
MIT OR Zlib OR Apache-2.0
MIT/Apache-2.0
Unlicense OR MIT
Unlicense/MIT
Zlib OR Apache-2.0 OR MIT
"
    .lines()
    .filter(|it| !it.is_empty())
    .collect::<Vec<_>>();

    let meta = cmd!("cargo metadata --format-version 1").read().unwrap();
    let mut licenses = meta
        .split(|c| c == ',' || c == '{' || c == '}')
        .filter(|it| it.contains(r#""license""#))
        .map(|it| it.trim())
        .map(|it| it[r#""license":"#.len()..].trim_matches('"'))
        .collect::<Vec<_>>();
    licenses.sort();
    licenses.dedup();
    if licenses != expected {
        let mut diff = String::new();

        diff += &format!("New Licenses:\n");
        for &l in licenses.iter() {
            if !expected.contains(&l) {
                diff += &format!("  {}\n", l)
            }
        }

        diff += &format!("\nMissing Licenses:\n");
        for &l in expected.iter() {
            if !licenses.contains(&l) {
                diff += &format!("  {}\n", l)
            }
        }

        panic!("different set of licenses!\n{}", diff);
    }
    assert_eq!(licenses, expected);
}

fn check_todo(path: &Path, text: &str) {
    let need_todo = &[
        // This file itself obviously needs to use todo (<- like this!).
        "tests/cli.rs",
        // Some of our assists generate `todo!()`.
        "handlers/add_turbo_fish.rs",
        "handlers/generate_function.rs",
        // To support generating `todo!()` in assists, we have `expr_todo()` in
        // `ast::make`.
        "ast/make.rs",
        // The documentation in string literals may contain anything for its own purposes
        "completion/src/generated_lint_completions.rs",
    ];
    if need_todo.iter().any(|p| path.ends_with(p)) {
        return;
    }
    if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
        // Generated by an assist
        if text.contains("${0:todo!()}") {
            return;
        }

        panic!(
            "\nTODO markers or todo! macros should not be committed to the master branch,\n\
             use FIXME instead\n\
             {}\n",
            path.display(),
        )
    }
}

fn check_trailing_ws(path: &Path, text: &str) {
    if is_exclude_dir(path, &["test_data"]) {
        return;
    }
    for (line_number, line) in text.lines().enumerate() {
        if line.chars().last().map(char::is_whitespace) == Some(true) {
            panic!("Trailing whitespace in {} at line {}", path.display(), line_number)
        }
    }
}

#[derive(Default)]
struct TidyDocs {
    missing_docs: Vec<String>,
    contains_fixme: Vec<PathBuf>,
}

impl TidyDocs {
    fn visit(&mut self, path: &Path, text: &str) {
        // Test hopefully don't really need comments, and for assists we already
        // have special comments which are source of doc tests and user docs.
        if is_exclude_dir(path, &["tests", "test_data"]) {
            return;
        }

        if is_exclude_file(path) {
            return;
        }

        let first_line = match text.lines().next() {
            Some(it) => it,
            None => return,
        };

        if first_line.starts_with("//!") {
            if first_line.contains("FIXME") {
                self.contains_fixme.push(path.to_path_buf());
            }
        } else {
            if text.contains("// Feature:") || text.contains("// Assist:") {
                return;
            }
            self.missing_docs.push(path.display().to_string());
        }

        fn is_exclude_file(d: &Path) -> bool {
            let file_names = ["tests.rs"];

            d.file_name()
                .unwrap_or_default()
                .to_str()
                .map(|f_n| file_names.iter().any(|name| *name == f_n))
                .unwrap_or(false)
        }
    }

    fn finish(self) {
        if !self.missing_docs.is_empty() {
            panic!(
                "\nMissing docs strings\n\n\
                 modules:\n{}\n\n",
                self.missing_docs.join("\n")
            )
        }

        let poorly_documented = [
            "hir",
            "hir_expand",
            "ide",
            "mbe",
            "parser",
            "profile",
            "project_model",
            "syntax",
            "tt",
            "hir_ty",
        ];

        let mut has_fixmes =
            poorly_documented.iter().map(|it| (*it, false)).collect::<HashMap<&str, bool>>();
        'outer: for path in self.contains_fixme {
            for krate in poorly_documented.iter() {
                if path.components().any(|it| it.as_os_str() == *krate) {
                    has_fixmes.insert(krate, true);
                    continue 'outer;
                }
            }
            panic!("FIXME doc in a fully-documented crate: {}", path.display())
        }

        for (krate, has_fixme) in has_fixmes.iter() {
            if !has_fixme {
                panic!("crate {} is fully documented :tada:, remove it from the list of poorly documented crates", krate)
            }
        }
    }
}

fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool {
    p.strip_prefix(project_root())
        .unwrap()
        .components()
        .rev()
        .skip(1)
        .filter_map(|it| it.as_os_str().to_str())
        .any(|it| dirs_to_exclude.contains(&it))
}

#[allow(deprecated)]
fn stable_hash(text: &str) -> u64 {
    use std::hash::{Hash, Hasher, SipHasher};

    let text = text.replace('\r', "");
    let mut hasher = SipHasher::default();
    text.hash(&mut hasher);
    hasher.finish()
}
<|endoftext|>


<commit_before>use std::process::{Stdio, Command, Child};
use std::sync::mpsc::Sender;
use std::thread::{Builder, JoinHandle};
use ::prelude::*;
use ::{game, Result, Error, ErrorKind};

/// Builder for a subprocess (child) AI
pub struct Interface {
    command: Command,
    is_yellow: bool,
}

impl Interface {
    pub fn new(command: Command) -> Interface {
        Interface {
            command: command,
            is_yellow: true,
        }
    }

    /// Whether the AI will play with the yellow team, blue otherwise.
    pub fn is_yellow(&mut self, is_yellow: bool) -> &mut Interface {
        self.is_yellow = is_yellow;
        self
    }

    /// Spawn a thread which spawns a child subprocess and feeds it the game_state at 60Hz and
    /// sends commands through tx.
    pub fn spawn(&mut self, game_state: game::SharedState, tx: Sender<Vec<u8>>) -> Result<AiHandle> {
        use std::io::prelude::*;
        use std::io::{BufReader, BufWriter};
        use protocol::Message;
        use protocol::grSim_Packet::grSim_Packet;
        use protocol::grSim_Commands::grSim_Robot_Command;

        let is_yellow = self.is_yellow;
        let mut child = try!(self.command.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn());

        info!("AI subprocess spawned");

        let child_in = try!(child.stdin.take().ok_or_else(|| Error::new(ErrorKind::Io, "missing stdin from child")));
        let child_out = try!(child.stdout.take().ok_or_else(|| Error::new(ErrorKind::Io, "missing stdout from child")));
        let child_err = try!(child.stderr.take().ok_or_else(|| Error::new(ErrorKind::Io, "missing stderr from child")));

        let thread_builder = Builder::new().name("child AI Debug".to_string());
        let debug_thread = try!(thread_builder.spawn(move || -> Result<()> {
            for line in BufReader::new(child_err).lines() {
                // TODO: infrastructure to redirect this, this is different from logging
                println!("AI debug: {}", try!(line));
            }
            Ok(())
        }));

        let thread_builder = Builder::new().name("child AI Game I/O".to_string());
        let child_thread = try!(thread_builder.spawn(move || -> Result<()> {

            macro_rules! throw_err {
                ( $( $tt:tt )* ) => {{
                    return Err(Error::new(ErrorKind::AiProtocol, format!($($tt)*)));
                }}
            }

            // TODO: optimize by using `with_capacity` after calculating a low but enough estimate
            let mut output = BufReader::new(child_out).lines();
            let mut input = BufWriter::new(child_in);

            let version = 1;
            try!(writeln!(input, "ROBOIME_AI_PROTOCOL {}", version));

            // flush to child stdin
            try!(input.flush());

            {
                let line = try!(match output.next() {
                    Some(thing) => thing,
                    None => throw_err!("expected a line"),
                });
                match line.as_ref() {
                    "COMPATIBLE 1" => {}
                    s if s.starts_with("COMPATIBLE") => throw_err!("AI not protocol compatible (implicit)"),
                    s if s.starts_with("NOT_COMPATIBLE") => throw_err!("AI not protocol compatible (explicit)"),
                    s => throw_err!("AI protocol compatibility error, output was '{}'", s),
                }
            }

            {
                let state = try!(game_state.wait_and_read());
                let geom = state.get_field_geom();

                // FIELD_LENGTH
                // FIELD_WIDTH
                // GOAL_WIDTH
                // CENTER_CIRCLE_RADIUS
                // DEFENSE_RADIUS
                // DEFENSE_STRETCH
                // FREE_KICK_FROM_DEFENSE_DIST
                // PENALTY_SPOT_FROM_FIELD_LINE_DIST
                // PENALTY_LINE_FROM_SPOT_DIST
                try!(writeln!(input, "{:.03} {:.03} {:.03} {:.03} {:.03} {:.03} {:.03} {:.03} {:.03}",
                    geom.field_length,
                    geom.field_width,
                    geom.goal_width,
                    geom.center_circle_radius,
                    geom.defense_radius,
                    geom.defense_stretch,
                    geom.free_kick_from_defense_dist,
                    geom.penalty_spot_from_field_line_dist,
                    geom.penalty_line_from_spot_dist,
                ));
            }

            // flush to child stdin
            try!(input.flush());

            loop {
                let state = try!(game_state.wait_and_read());
                let timestamp = state.get_timestamp();
                let counter = state.get_counter();

                let (robots_player, robots_opponent) = {
                    let robots_yellow = state.get_robots_yellow();
                    let robots_blue = state.get_robots_blue();
                    if is_yellow {
                        (robots_yellow, robots_blue)
                    } else {
                        (robots_blue, robots_yellow)
                    }
                };

                // COUNTER
                // TIMESTAMP
                // REFEREE_STATE
                // REFEREE_TIME_LEFT
                // SCORE_PLAYER
                // SCORE_OPPONENT
                // GOALIE_ID_PLAYER
                // GOALIE_ID_OPPONENT
                // ROBOT_COUNT_PLAYER
                // ROBOT_COUNT_OPPONENT
                try!(writeln!(input, "{} {} {} {} {} {} {} {} {} {}",
                    counter,
                    timestamp,
                    'N', // TODO: REFEREE_STATE
                    -1, //  TODO: REFEREE_TIME_LEFT
                    0, //   TODO: SCORE_PLAYER
                    0, //   TODO: SCORE_OPPONENT
                    0, //   TODO: GOALIE_ID_PLAYER
                    0, //   TODO: GOALIE_ID_OPPONENT
                    robots_player.len(),
                    robots_opponent.len(),
                ));

                let ball = state.get_ball();

                // BALL_X
                // BALL_Y
                // BALL_VX
                // BALL_VY
                try!(writeln!(input, "{:.04} {:.04} {:.04} {:.04}",
                    ball.get_x(),
                    ball.get_y(),
                    ball.get_vx(),
                    ball.get_vy(),
                ));

                // ROBOT_COUNT_PLAYER x
                for (robot_id, robot) in robots_player {
                    // ROBOT_ID
                    // ROBOT_X
                    // ROBOT_Y
                    // ROBOT_W
                    // ROBOT_VX
                    // ROBOT_VY
                    // ROBOT_VW
                    try!(writeln!(input, "{} {:.04} {:.04} {:.04} {:.04} {:.04} {:.04}",
                        robot_id,
                        robot.get_x(),
                        robot.get_y(),
                        robot.get_w(),
                        robot.get_vx(),
                        robot.get_vy(),
                        robot.get_vw(),
                    ));
                }

                // ROBOT_COUNT_OPPONENT x
                for (robot_id, robot) in robots_opponent {
                    // ROBOT_ID
                    // ROBOT_X
                    // ROBOT_Y
                    // ROBOT_W
                    // ROBOT_VX
                    // ROBOT_VY
                    // ROBOT_VW
                    try!(writeln!(input, "{} {:.04} {:.04} {:.04} {:.04} {:.04} {:.04}",
                        robot_id,
                        robot.get_x(),
                        robot.get_y(),
                        robot.get_w(),
                        robot.get_vx(),
                        robot.get_vy(),
                        robot.get_vw(),
                     ));
                }

                // flush to child stdin
                try!(input.flush());

                {
                    let line = try!(match output.next() {
                        Some(thing) => thing,
                        None => throw_err!("expected a line"),
                    });
                    // COUNTER
                    let ai_counter: u64 = try!(line.parse());
                    if ai_counter != counter {
                        throw_err!("wrong command counter, expected {} got {}", counter, ai_counter);
                    }
                }

                let mut packet = grSim_Packet::new();
                {
                    let commands = packet.mut_commands();
                    commands.set_timestamp(timestamp);
                    commands.set_isteamyellow(is_yellow);
                    let robot_commands = commands.mut_robot_commands();

                    // ROBOT_COUNT_PLAYER x
                    for (robot_id, _) in robots_player {
                        let mut robot_command = grSim_Robot_Command::new();

                        let line = try!(match output.next() {
                            Some(thing) => thing,
                            None => throw_err!("expected a line"),
                        });

                        let vars: Vec<_> = line.split(' ').collect();
                        let vars_len = vars.len();
                        if vars_len != 6 {
                            throw_err!("expected 6 values for robot command, got {}", vars_len);
                        }

                        // V_TAN
                        // V_NORM
                        // V_ANG
                        // KICK_X
                        // KICK_Z
                        // SPIN
                        let v_tan:  f32 = try!(vars[0].parse());
                        let v_norm: f32 = try!(vars[1].parse());
                        let v_ang:  f32 = try!(vars[2].parse());
                        let kick_x: f32 = try!(vars[3].parse());
                        let kick_z: f32 = try!(vars[4].parse());
                        let spin = try!(vars[5].parse::<i32>()) == 1;

                        robot_command.set_id(*robot_id as u32);
                        robot_command.set_kickspeedx(kick_x);
                        robot_command.set_kickspeedz(kick_z);
                        robot_command.set_veltangent(v_tan);
                        robot_command.set_velnormal(v_norm);
                        robot_command.set_velangular(v_ang);
                        robot_command.set_spinner(spin);
                        robot_command.set_wheelsspeed(false);
                        robot_commands.push(robot_command);
                    }
                }

                match packet.write_to_bytes() {
                    Ok(bytes) => {
                        match tx.send(bytes) {
                            Ok(_) => (),
                            Err(_) => break
                        }
                    }
                    Err(e) => {
                        error!("failed to serialize protobuf packet: {}", e);
                    }
                }
            }

            Ok(())
        }));

        Ok(AiHandle {
            subproc_handle: child,
            debug_handle: debug_thread,
            child_handle: child_thread,
        })
    }
}

pub struct AiHandle {
    subproc_handle: Child,
    debug_handle: JoinHandle<Result<()>>,
    child_handle: JoinHandle<Result<()>>,
}

impl InterfaceHandle for AiHandle {
    fn join(self) -> Result<()> {
        let AiHandle {
            subproc_handle: mut child,
            debug_handle: debug_thread,
            child_handle: child_thread,
        } = self;
        let child_result = try!(child_thread.join());
        let debug_result = try!(debug_thread.join());
        let exit_status = try!(child.wait());
        try!(child_result);
        try!(debug_result);
        if exit_status.success() {
            Ok(())
        } else {
            Err(Error::new(ErrorKind::Io, "child AI exited with failure"))
        }
    }
}
<commit_msg>Add debug info.<commit_after>use std::process::{Stdio, Command, Child};
use std::sync::mpsc::Sender;
use std::thread::{Builder, JoinHandle};
use ::prelude::*;
use ::{game, Result, Error, ErrorKind};

/// Builder for a subprocess (child) AI
pub struct Interface {
    command: Command,
    is_yellow: bool,
}

impl Interface {
    pub fn new(command: Command) -> Interface {
        Interface {
            command: command,
            is_yellow: true,
        }
    }

    /// Whether the AI will play with the yellow team, blue otherwise.
    pub fn is_yellow(&mut self, is_yellow: bool) -> &mut Interface {
        self.is_yellow = is_yellow;
        self
    }

    /// Spawn a thread which spawns a child subprocess and feeds it the game_state at 60Hz and
    /// sends commands through tx.
    pub fn spawn(&mut self, game_state: game::SharedState, tx: Sender<Vec<u8>>) -> Result<AiHandle> {
        use std::io::prelude::*;
        use std::io::{BufReader, BufWriter};
        use protocol::Message;
        use protocol::grSim_Packet::grSim_Packet;
        use protocol::grSim_Commands::grSim_Robot_Command;

        debug!("AI is playing as {} with: {:?}", if self.is_yellow { "yellow" } else { "blue" }, self.command);

        let is_yellow = self.is_yellow;
        let mut child = try!(self.command.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn());

        info!("AI subprocess spawned");

        let child_in = try!(child.stdin.take().ok_or_else(|| Error::new(ErrorKind::Io, "missing stdin from child")));
        let child_out = try!(child.stdout.take().ok_or_else(|| Error::new(ErrorKind::Io, "missing stdout from child")));
        let child_err = try!(child.stderr.take().ok_or_else(|| Error::new(ErrorKind::Io, "missing stderr from child")));

        let thread_builder = Builder::new().name("child AI Debug".to_string());
        let debug_thread = try!(thread_builder.spawn(move || -> Result<()> {
            for line in BufReader::new(child_err).lines() {
                // TODO: infrastructure to redirect this, this is different from logging
                println!("AI debug: {}", try!(line));
            }
            Ok(())
        }));

        let thread_builder = Builder::new().name("child AI Game I/O".to_string());
        let child_thread = try!(thread_builder.spawn(move || -> Result<()> {

            macro_rules! throw_err {
                ( $( $tt:tt )* ) => {{
                    return Err(Error::new(ErrorKind::AiProtocol, format!($($tt)*)));
                }}
            }

            // TODO: optimize by using `with_capacity` after calculating a low but enough estimate
            let mut output = BufReader::new(child_out).lines();
            let mut input = BufWriter::new(child_in);

            let version = 1;
            try!(writeln!(input, "ROBOIME_AI_PROTOCOL {}", version));

            // flush to child stdin
            try!(input.flush());

            {
                let line = try!(match output.next() {
                    Some(thing) => thing,
                    None => throw_err!("expected a line"),
                });
                match line.as_ref() {
                    "COMPATIBLE 1" => {}
                    s if s.starts_with("COMPATIBLE") => throw_err!("AI not protocol compatible (implicit)"),
                    s if s.starts_with("NOT_COMPATIBLE") => throw_err!("AI not protocol compatible (explicit)"),
                    s => throw_err!("AI protocol compatibility error, output was '{}'", s),
                }
            }

            {
                let state = try!(game_state.wait_and_read());
                let geom = state.get_field_geom();

                // FIELD_LENGTH
                // FIELD_WIDTH
                // GOAL_WIDTH
                // CENTER_CIRCLE_RADIUS
                // DEFENSE_RADIUS
                // DEFENSE_STRETCH
                // FREE_KICK_FROM_DEFENSE_DIST
                // PENALTY_SPOT_FROM_FIELD_LINE_DIST
                // PENALTY_LINE_FROM_SPOT_DIST
                try!(writeln!(input, "{:.03} {:.03} {:.03} {:.03} {:.03} {:.03} {:.03} {:.03} {:.03}",
                    geom.field_length,
                    geom.field_width,
                    geom.goal_width,
                    geom.center_circle_radius,
                    geom.defense_radius,
                    geom.defense_stretch,
                    geom.free_kick_from_defense_dist,
                    geom.penalty_spot_from_field_line_dist,
                    geom.penalty_line_from_spot_dist,
                ));
            }

            // flush to child stdin
            try!(input.flush());

            loop {
                let state = try!(game_state.wait_and_read());
                let timestamp = state.get_timestamp();
                let counter = state.get_counter();

                let (robots_player, robots_opponent) = {
                    let robots_yellow = state.get_robots_yellow();
                    let robots_blue = state.get_robots_blue();
                    if is_yellow {
                        (robots_yellow, robots_blue)
                    } else {
                        (robots_blue, robots_yellow)
                    }
                };

                // COUNTER
                // TIMESTAMP
                // REFEREE_STATE
                // REFEREE_TIME_LEFT
                // SCORE_PLAYER
                // SCORE_OPPONENT
                // GOALIE_ID_PLAYER
                // GOALIE_ID_OPPONENT
                // ROBOT_COUNT_PLAYER
                // ROBOT_COUNT_OPPONENT
                try!(writeln!(input, "{} {} {} {} {} {} {} {} {} {}",
                    counter,
                    timestamp,
                    'N', // TODO: REFEREE_STATE
                    -1, //  TODO: REFEREE_TIME_LEFT
                    0, //   TODO: SCORE_PLAYER
                    0, //   TODO: SCORE_OPPONENT
                    0, //   TODO: GOALIE_ID_PLAYER
                    0, //   TODO: GOALIE_ID_OPPONENT
                    robots_player.len(),
                    robots_opponent.len(),
                ));

                let ball = state.get_ball();

                // BALL_X
                // BALL_Y
                // BALL_VX
                // BALL_VY
                try!(writeln!(input, "{:.04} {:.04} {:.04} {:.04}",
                    ball.get_x(),
                    ball.get_y(),
                    ball.get_vx(),
                    ball.get_vy(),
                ));

                // ROBOT_COUNT_PLAYER x
                for (robot_id, robot) in robots_player {
                    // ROBOT_ID
                    // ROBOT_X
                    // ROBOT_Y
                    // ROBOT_W
                    // ROBOT_VX
                    // ROBOT_VY
                    // ROBOT_VW
                    try!(writeln!(input, "{} {:.04} {:.04} {:.04} {:.04} {:.04} {:.04}",
                        robot_id,
                        robot.get_x(),
                        robot.get_y(),
                        robot.get_w(),
                        robot.get_vx(),
                        robot.get_vy(),
                        robot.get_vw(),
                    ));
                }

                // ROBOT_COUNT_OPPONENT x
                for (robot_id, robot) in robots_opponent {
                    // ROBOT_ID
                    // ROBOT_X
                    // ROBOT_Y
                    // ROBOT_W
                    // ROBOT_VX
                    // ROBOT_VY
                    // ROBOT_VW
                    try!(writeln!(input, "{} {:.04} {:.04} {:.04} {:.04} {:.04} {:.04}",
                        robot_id,
                        robot.get_x(),
                        robot.get_y(),
                        robot.get_w(),
                        robot.get_vx(),
                        robot.get_vy(),
                        robot.get_vw(),
                     ));
                }

                // flush to child stdin
                try!(input.flush());

                {
                    let line = try!(match output.next() {
                        Some(thing) => thing,
                        None => throw_err!("expected a line"),
                    });
                    // COUNTER
                    let ai_counter: u64 = try!(line.parse());
                    if ai_counter != counter {
                        throw_err!("wrong command counter, expected {} got {}", counter, ai_counter);
                    }
                }

                let mut packet = grSim_Packet::new();
                {
                    let commands = packet.mut_commands();
                    commands.set_timestamp(timestamp);
                    commands.set_isteamyellow(is_yellow);
                    let robot_commands = commands.mut_robot_commands();

                    // ROBOT_COUNT_PLAYER x
                    for (robot_id, _) in robots_player {
                        let mut robot_command = grSim_Robot_Command::new();

                        let line = try!(match output.next() {
                            Some(thing) => thing,
                            None => throw_err!("expected a line"),
                        });

                        let vars: Vec<_> = line.split(' ').collect();
                        let vars_len = vars.len();
                        if vars_len != 6 {
                            throw_err!("expected 6 values for robot command, got {}", vars_len);
                        }

                        // V_TAN
                        // V_NORM
                        // V_ANG
                        // KICK_X
                        // KICK_Z
                        // SPIN
                        let v_tan:  f32 = try!(vars[0].parse());
                        let v_norm: f32 = try!(vars[1].parse());
                        let v_ang:  f32 = try!(vars[2].parse());
                        let kick_x: f32 = try!(vars[3].parse());
                        let kick_z: f32 = try!(vars[4].parse());
                        let spin = try!(vars[5].parse::<i32>()) == 1;

                        robot_command.set_id(*robot_id as u32);
                        robot_command.set_kickspeedx(kick_x);
                        robot_command.set_kickspeedz(kick_z);
                        robot_command.set_veltangent(v_tan);
                        robot_command.set_velnormal(v_norm);
                        robot_command.set_velangular(v_ang);
                        robot_command.set_spinner(spin);
                        robot_command.set_wheelsspeed(false);
                        robot_commands.push(robot_command);
                    }
                }

                match packet.write_to_bytes() {
                    Ok(bytes) => {
                        match tx.send(bytes) {
                            Ok(_) => (),
                            Err(_) => break
                        }
                    }
                    Err(e) => {
                        error!("failed to serialize protobuf packet: {}", e);
                    }
                }
            }

            Ok(())
        }));

        Ok(AiHandle {
            subproc_handle: child,
            debug_handle: debug_thread,
            child_handle: child_thread,
        })
    }
}

pub struct AiHandle {
    subproc_handle: Child,
    debug_handle: JoinHandle<Result<()>>,
    child_handle: JoinHandle<Result<()>>,
}

impl InterfaceHandle for AiHandle {
    fn join(self) -> Result<()> {
        let AiHandle {
            subproc_handle: mut child,
            debug_handle: debug_thread,
            child_handle: child_thread,
        } = self;
        let child_result = try!(child_thread.join());
        let debug_result = try!(debug_thread.join());
        let exit_status = try!(child.wait());
        try!(child_result);
        try!(debug_result);
        if exit_status.success() {
            Ok(())
        } else {
            Err(Error::new(ErrorKind::Io, "child AI exited with failure"))
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Retrieve cols and rows from FrameMotionVectors<commit_after><|endoftext|>


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

/*!
 * An implementation of the SHA-1 cryptographic hash.
 *
 * First create a `sha1` object using the `sha1` constructor, then
 * feed it input using the `input` or `input_str` methods, which may be
 * called any number of times.
 *
 * After the entire input has been fed to the hash read the result using
 * the `result` or `result_str` methods.
 *
 * The `sha1` object may be reused to create multiple hashes by calling
 * the `reset` method.
 */

use cryptoutil::{write_u32_be, read_u32v_be, add_bytes_to_bits, FixedBuffer, FixedBuffer64,
    StandardPadding};
use digest::Digest;

/*
 * A SHA-1 implementation derived from Paul E. Jones's reference
 * implementation, which is written for clarity, not speed. At some
 * point this will want to be rewritten.
 */

// Some unexported constants
static DIGEST_BUF_LEN: uint = 5u;
static WORK_BUF_LEN: uint = 80u;
static K0: u32 = 0x5A827999u32;
static K1: u32 = 0x6ED9EBA1u32;
static K2: u32 = 0x8F1BBCDCu32;
static K3: u32 = 0xCA62C1D6u32;

/// Structure representing the state of a Sha1 computation
pub struct Sha1 {
    h: [u32, ..DIGEST_BUF_LEN],
    length_bits: u64,
    buffer: FixedBuffer64,
    computed: bool,
}

fn add_input(st: &mut Sha1, msg: &[u8]) {
    assert!((!st.computed));
    // Assumes that msg.len() can be converted to u64 without overflow
    st.length_bits = add_bytes_to_bits(st.length_bits, msg.len() as u64);
    let st_h = &mut st.h;
    st.buffer.input(msg, |d: &[u8]| { process_msg_block(d, st_h); });
}

fn process_msg_block(data: &[u8], h: &mut [u32, ..DIGEST_BUF_LEN]) {
    let mut t: uint; // Loop counter

    let mut w = [0u32, ..WORK_BUF_LEN];

    // Initialize the first 16 words of the vector w
    read_u32v_be(w.mut_slice(0, 16), data);

    // Initialize the rest of vector w
    t = 16u;
    while t < 80 {
        let val = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
        w[t] = circular_shift(1, val);
        t += 1;
    }
    let mut a = h[0];
    let mut b = h[1];
    let mut c = h[2];
    let mut d = h[3];
    let mut e = h[4];
    let mut temp: u32;
    t = 0;
    while t < 20 {
        temp = circular_shift(5, a) + (b & c | !b & d) + e + w[t] + K0;
        e = d;
        d = c;
        c = circular_shift(30, b);
        b = a;
        a = temp;
        t += 1;
    }
    while t < 40 {
        temp = circular_shift(5, a) + (b ^ c ^ d) + e + w[t] + K1;
        e = d;
        d = c;
        c = circular_shift(30, b);
        b = a;
        a = temp;
        t += 1;
    }
    while t < 60 {
        temp =
            circular_shift(5, a) + (b & c | b & d | c & d) + e + w[t] +
                K2;
        e = d;
        d = c;
        c = circular_shift(30, b);
        b = a;
        a = temp;
        t += 1;
    }
    while t < 80 {
        temp = circular_shift(5, a) + (b ^ c ^ d) + e + w[t] + K3;
        e = d;
        d = c;
        c = circular_shift(30, b);
        b = a;
        a = temp;
        t += 1;
    }
    h[0] += a;
    h[1] += b;
    h[2] += c;
    h[3] += d;
    h[4] += e;
}

fn circular_shift(bits: u32, word: u32) -> u32 {
    return word << bits | word >> 32u32 - bits;
}

fn mk_result(st: &mut Sha1, rs: &mut [u8]) {
    if !st.computed {
        let st_h = &mut st.h;
        st.buffer.standard_padding(8, |d: &[u8]| { process_msg_block(d, st_h) });
        write_u32_be(st.buffer.next(4), (st.length_bits >> 32) as u32 );
        write_u32_be(st.buffer.next(4), st.length_bits as u32);
        process_msg_block(st.buffer.full_buffer(), st_h);

        st.computed = true;
    }

    write_u32_be(rs.mut_slice(0, 4), st.h[0]);
    write_u32_be(rs.mut_slice(4, 8), st.h[1]);
    write_u32_be(rs.mut_slice(8, 12), st.h[2]);
    write_u32_be(rs.mut_slice(12, 16), st.h[3]);
    write_u32_be(rs.mut_slice(16, 20), st.h[4]);
}

impl Sha1 {
    /// Construct a `sha` object
    pub fn new() -> Sha1 {
        let mut st = Sha1 {
            h: [0u32, ..DIGEST_BUF_LEN],
            length_bits: 0u64,
            buffer: FixedBuffer64::new(),
            computed: false,
        };
        st.reset();
        return st;
    }
}

impl Digest for Sha1 {
    fn reset(&mut self) {
        self.length_bits = 0;
        self.h[0] = 0x67452301u32;
        self.h[1] = 0xEFCDAB89u32;
        self.h[2] = 0x98BADCFEu32;
        self.h[3] = 0x10325476u32;
        self.h[4] = 0xC3D2E1F0u32;
        self.buffer.reset();
        self.computed = false;
    }
    fn input(&mut self, msg: &[u8]) { add_input(self, msg); }
    fn result(&mut self, out: &mut [u8]) { return mk_result(self, out); }
    fn output_bits(&self) -> uint { 160 }
    fn block_size(&self) -> uint { 64 }
}

#[cfg(test)]
mod tests {
    use cryptoutil::test::test_digest_1million_random;
    use digest::Digest;
    use sha1::Sha1;

    #[deriving(Clone)]
    struct Test {
        input: ~str,
        output: ~[u8],
        output_str: ~str,
    }

    #[test]
    fn test() {
        // Test messages from FIPS 180-1

        let fips_180_1_tests = ~[
            Test {
                input: ~"abc",
                output: ~[
                    0xA9u8, 0x99u8, 0x3Eu8, 0x36u8,
                    0x47u8, 0x06u8, 0x81u8, 0x6Au8,
                    0xBAu8, 0x3Eu8, 0x25u8, 0x71u8,
                    0x78u8, 0x50u8, 0xC2u8, 0x6Cu8,
                    0x9Cu8, 0xD0u8, 0xD8u8, 0x9Du8,
                ],
                output_str: ~"a9993e364706816aba3e25717850c26c9cd0d89d"
            },
            Test {
                input:
                     ~"abcdbcdecdefdefgefghfghighij" +
                     "hijkijkljklmklmnlmnomnopnopq",
                output: ~[
                    0x84u8, 0x98u8, 0x3Eu8, 0x44u8,
                    0x1Cu8, 0x3Bu8, 0xD2u8, 0x6Eu8,
                    0xBAu8, 0xAEu8, 0x4Au8, 0xA1u8,
                    0xF9u8, 0x51u8, 0x29u8, 0xE5u8,
                    0xE5u8, 0x46u8, 0x70u8, 0xF1u8,
                ],
                output_str: ~"84983e441c3bd26ebaae4aa1f95129e5e54670f1"
            },
        ];
        // Examples from wikipedia

        let wikipedia_tests = ~[
            Test {
                input: ~"The quick brown fox jumps over the lazy dog",
                output: ~[
                    0x2fu8, 0xd4u8, 0xe1u8, 0xc6u8,
                    0x7au8, 0x2du8, 0x28u8, 0xfcu8,
                    0xedu8, 0x84u8, 0x9eu8, 0xe1u8,
                    0xbbu8, 0x76u8, 0xe7u8, 0x39u8,
                    0x1bu8, 0x93u8, 0xebu8, 0x12u8,
                ],
                output_str: ~"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",
            },
            Test {
                input: ~"The quick brown fox jumps over the lazy cog",
                output: ~[
                    0xdeu8, 0x9fu8, 0x2cu8, 0x7fu8,
                    0xd2u8, 0x5eu8, 0x1bu8, 0x3au8,
                    0xfau8, 0xd3u8, 0xe8u8, 0x5au8,
                    0x0bu8, 0xd1u8, 0x7du8, 0x9bu8,
                    0x10u8, 0x0du8, 0xb4u8, 0xb3u8,
                ],
                output_str: ~"de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3",
            },
        ];
        let tests = fips_180_1_tests + wikipedia_tests;

        // Test that it works when accepting the message all at once

        let mut out = [0u8, ..20];

        let mut sh = ~Sha1::new();
        for t in tests.iter() {
            (*sh).input_str(t.input);
            sh.result(out);
            assert!(t.output.as_slice() == out);

            let out_str = (*sh).result_str();
            assert_eq!(out_str.len(), 40);
            assert!(out_str == t.output_str);

            sh.reset();
        }

        // Test that it works when accepting the message in pieces
        for t in tests.iter() {
            let len = t.input.len();
            let mut left = len;
            while left > 0u {
                let take = (left + 1u) / 2u;
                (*sh).input_str(t.input.slice(len - left, take + len - left));
                left = left - take;
            }
            sh.result(out);
            assert!(t.output.as_slice() == out);

            let out_str = (*sh).result_str();
            assert_eq!(out_str.len(), 40);
            assert!(out_str == t.output_str);

            sh.reset();
        }
    }

    #[test]
    fn test_1million_random_sha1() {
        let mut sh = Sha1::new();
        test_digest_1million_random(
            &mut sh,
            64,
            "34aa973cd4c4daa4f61eeb2bdbad27316534016f");
    }
}

#[cfg(test)]
mod bench {
    use test::Bencher;
    use digest::Digest;
    use sha1::Sha1;

    #[bench]
    pub fn sha1_10(bh: & mut Bencher) {
        let mut sh = Sha1::new();
        let bytes = [1u8, ..10];
        bh.iter( || {
            sh.input(bytes);
        });
        bh.bytes = bytes.len() as u64;
    }

    #[bench]
    pub fn sha1_1k(bh: & mut Bencher) {
        let mut sh = Sha1::new();
        let bytes = [1u8, ..1024];
        bh.iter( || {
            sh.input(bytes);
        });
        bh.bytes = bytes.len() as u64;
    }

    #[bench]
    pub fn sha1_64k(bh: & mut Bencher) {
        let mut sh = Sha1::new();
        let bytes = [1u8, ..65536];
        bh.iter( || {
            sh.input(bytes);
        });
        bh.bytes = bytes.len() as u64;
    }

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

/*!
 * An implementation of the SHA-1 cryptographic hash.
 *
 * First create a `sha1` object using the `sha1` constructor, then
 * feed it input using the `input` or `input_str` methods, which may be
 * called any number of times.
 *
 * After the entire input has been fed to the hash read the result using
 * the `result` or `result_str` methods.
 *
 * The `sha1` object may be reused to create multiple hashes by calling
 * the `reset` method.
 */

use cryptoutil::{write_u32_be, read_u32v_be, add_bytes_to_bits, FixedBuffer, FixedBuffer64,
    StandardPadding};
use digest::Digest;

/*
 * A SHA-1 implementation derived from Paul E. Jones's reference
 * implementation, which is written for clarity, not speed. At some
 * point this will want to be rewritten.
 */

// Some unexported constants
static DIGEST_BUF_LEN: uint = 5u;
static WORK_BUF_LEN: uint = 80u;
static K0: u32 = 0x5A827999u32;
static K1: u32 = 0x6ED9EBA1u32;
static K2: u32 = 0x8F1BBCDCu32;
static K3: u32 = 0xCA62C1D6u32;

/// Structure representing the state of a Sha1 computation
pub struct Sha1 {
    h: [u32, ..DIGEST_BUF_LEN],
    length_bits: u64,
    buffer: FixedBuffer64,
    computed: bool,
}

fn add_input(st: &mut Sha1, msg: &[u8]) {
    assert!((!st.computed));
    // Assumes that msg.len() can be converted to u64 without overflow
    st.length_bits = add_bytes_to_bits(st.length_bits, msg.len() as u64);
    let st_h = &mut st.h;
    st.buffer.input(msg, |d: &[u8]| {process_msg_block(d, &mut *st_h); });
}

fn process_msg_block(data: &[u8], h: &mut [u32, ..DIGEST_BUF_LEN]) {
    let mut t: uint; // Loop counter

    let mut w = [0u32, ..WORK_BUF_LEN];

    // Initialize the first 16 words of the vector w
    read_u32v_be(w.mut_slice(0, 16), data);

    // Initialize the rest of vector w
    t = 16u;
    while t < 80 {
        let val = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
        w[t] = circular_shift(1, val);
        t += 1;
    }
    let mut a = h[0];
    let mut b = h[1];
    let mut c = h[2];
    let mut d = h[3];
    let mut e = h[4];
    let mut temp: u32;
    t = 0;
    while t < 20 {
        temp = circular_shift(5, a) + (b & c | !b & d) + e + w[t] + K0;
        e = d;
        d = c;
        c = circular_shift(30, b);
        b = a;
        a = temp;
        t += 1;
    }
    while t < 40 {
        temp = circular_shift(5, a) + (b ^ c ^ d) + e + w[t] + K1;
        e = d;
        d = c;
        c = circular_shift(30, b);
        b = a;
        a = temp;
        t += 1;
    }
    while t < 60 {
        temp =
            circular_shift(5, a) + (b & c | b & d | c & d) + e + w[t] +
                K2;
        e = d;
        d = c;
        c = circular_shift(30, b);
        b = a;
        a = temp;
        t += 1;
    }
    while t < 80 {
        temp = circular_shift(5, a) + (b ^ c ^ d) + e + w[t] + K3;
        e = d;
        d = c;
        c = circular_shift(30, b);
        b = a;
        a = temp;
        t += 1;
    }
    h[0] += a;
    h[1] += b;
    h[2] += c;
    h[3] += d;
    h[4] += e;
}

fn circular_shift(bits: u32, word: u32) -> u32 {
    return word << bits | word >> 32u32 - bits;
}

fn mk_result(st: &mut Sha1, rs: &mut [u8]) {
    if !st.computed {
        let st_h = &mut st.h;
        st.buffer.standard_padding(8, |d: &[u8]| { process_msg_block(d, &mut *st_h) });
        write_u32_be(st.buffer.next(4), (st.length_bits >> 32) as u32 );
        write_u32_be(st.buffer.next(4), st.length_bits as u32);
        process_msg_block(st.buffer.full_buffer(), st_h);

        st.computed = true;
    }

    write_u32_be(rs.mut_slice(0, 4), st.h[0]);
    write_u32_be(rs.mut_slice(4, 8), st.h[1]);
    write_u32_be(rs.mut_slice(8, 12), st.h[2]);
    write_u32_be(rs.mut_slice(12, 16), st.h[3]);
    write_u32_be(rs.mut_slice(16, 20), st.h[4]);
}

impl Sha1 {
    /// Construct a `sha` object
    pub fn new() -> Sha1 {
        let mut st = Sha1 {
            h: [0u32, ..DIGEST_BUF_LEN],
            length_bits: 0u64,
            buffer: FixedBuffer64::new(),
            computed: false,
        };
        st.reset();
        return st;
    }
}

impl Digest for Sha1 {
    fn reset(&mut self) {
        self.length_bits = 0;
        self.h[0] = 0x67452301u32;
        self.h[1] = 0xEFCDAB89u32;
        self.h[2] = 0x98BADCFEu32;
        self.h[3] = 0x10325476u32;
        self.h[4] = 0xC3D2E1F0u32;
        self.buffer.reset();
        self.computed = false;
    }
    fn input(&mut self, msg: &[u8]) { add_input(self, msg); }
    fn result(&mut self, out: &mut [u8]) { return mk_result(self, out); }
    fn output_bits(&self) -> uint { 160 }
    fn block_size(&self) -> uint { 64 }
}

#[cfg(test)]
mod tests {
    use cryptoutil::test::test_digest_1million_random;
    use digest::Digest;
    use sha1::Sha1;

    #[deriving(Clone)]
    struct Test {
        input: ~str,
        output: ~[u8],
        output_str: ~str,
    }

    #[test]
    fn test() {
        // Test messages from FIPS 180-1

        let fips_180_1_tests = ~[
            Test {
                input: ~"abc",
                output: ~[
                    0xA9u8, 0x99u8, 0x3Eu8, 0x36u8,
                    0x47u8, 0x06u8, 0x81u8, 0x6Au8,
                    0xBAu8, 0x3Eu8, 0x25u8, 0x71u8,
                    0x78u8, 0x50u8, 0xC2u8, 0x6Cu8,
                    0x9Cu8, 0xD0u8, 0xD8u8, 0x9Du8,
                ],
                output_str: ~"a9993e364706816aba3e25717850c26c9cd0d89d"
            },
            Test {
                input:
                     ~"abcdbcdecdefdefgefghfghighij" +
                     "hijkijkljklmklmnlmnomnopnopq",
                output: ~[
                    0x84u8, 0x98u8, 0x3Eu8, 0x44u8,
                    0x1Cu8, 0x3Bu8, 0xD2u8, 0x6Eu8,
                    0xBAu8, 0xAEu8, 0x4Au8, 0xA1u8,
                    0xF9u8, 0x51u8, 0x29u8, 0xE5u8,
                    0xE5u8, 0x46u8, 0x70u8, 0xF1u8,
                ],
                output_str: ~"84983e441c3bd26ebaae4aa1f95129e5e54670f1"
            },
        ];
        // Examples from wikipedia

        let wikipedia_tests = ~[
            Test {
                input: ~"The quick brown fox jumps over the lazy dog",
                output: ~[
                    0x2fu8, 0xd4u8, 0xe1u8, 0xc6u8,
                    0x7au8, 0x2du8, 0x28u8, 0xfcu8,
                    0xedu8, 0x84u8, 0x9eu8, 0xe1u8,
                    0xbbu8, 0x76u8, 0xe7u8, 0x39u8,
                    0x1bu8, 0x93u8, 0xebu8, 0x12u8,
                ],
                output_str: ~"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",
            },
            Test {
                input: ~"The quick brown fox jumps over the lazy cog",
                output: ~[
                    0xdeu8, 0x9fu8, 0x2cu8, 0x7fu8,
                    0xd2u8, 0x5eu8, 0x1bu8, 0x3au8,
                    0xfau8, 0xd3u8, 0xe8u8, 0x5au8,
                    0x0bu8, 0xd1u8, 0x7du8, 0x9bu8,
                    0x10u8, 0x0du8, 0xb4u8, 0xb3u8,
                ],
                output_str: ~"de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3",
            },
        ];
        let tests = fips_180_1_tests + wikipedia_tests;

        // Test that it works when accepting the message all at once

        let mut out = [0u8, ..20];

        let mut sh = ~Sha1::new();
        for t in tests.iter() {
            (*sh).input_str(t.input);
            sh.result(out);
            assert!(t.output.as_slice() == out);

            let out_str = (*sh).result_str();
            assert_eq!(out_str.len(), 40);
            assert!(out_str == t.output_str);

            sh.reset();
        }

        // Test that it works when accepting the message in pieces
        for t in tests.iter() {
            let len = t.input.len();
            let mut left = len;
            while left > 0u {
                let take = (left + 1u) / 2u;
                (*sh).input_str(t.input.slice(len - left, take + len - left));
                left = left - take;
            }
            sh.result(out);
            assert!(t.output.as_slice() == out);

            let out_str = (*sh).result_str();
            assert_eq!(out_str.len(), 40);
            assert!(out_str == t.output_str);

            sh.reset();
        }
    }

    #[test]
    fn test_1million_random_sha1() {
        let mut sh = Sha1::new();
        test_digest_1million_random(
            &mut sh,
            64,
            "34aa973cd4c4daa4f61eeb2bdbad27316534016f");
    }
}

#[cfg(test)]
mod bench {
    use test::Bencher;
    use digest::Digest;
    use sha1::Sha1;

    #[bench]
    pub fn sha1_10(bh: & mut Bencher) {
        let mut sh = Sha1::new();
        let bytes = [1u8, ..10];
        bh.iter( || {
            sh.input(bytes);
        });
        bh.bytes = bytes.len() as u64;
    }

    #[bench]
    pub fn sha1_1k(bh: & mut Bencher) {
        let mut sh = Sha1::new();
        let bytes = [1u8, ..1024];
        bh.iter( || {
            sh.input(bytes);
        });
        bh.bytes = bytes.len() as u64;
    }

    #[bench]
    pub fn sha1_64k(bh: & mut Bencher) {
        let mut sh = Sha1::new();
        let bytes = [1u8, ..65536];
        bh.iter( || {
            sh.input(bytes);
        });
        bh.bytes = bytes.len() as u64;
    }

}
<|endoftext|>


<commit_before>//
// zhtta.rs
//
// Running on Rust 0.8
//
// Starting code for PS3
//
// Note: it would be very unwise to run this server on a machine that is
// on the Internet and contains any sensitive files!
//
// University of Virginia - cs4414 Fall 2013
// Weilin Xu and David Evans
// Version 0.3

extern mod extra;

use std::rt::io::*;
use std::rt::io::net::ip::SocketAddr;
use std::io::println;
use std::cell::Cell;
use std::{os, str, io, run, uint};
use extra::arc;
use std::comm::*;
use extra::priority_queue::PriorityQueue;
use std::rt::io::net::ip::*;
use std::hashmap::HashMap;

mod gashing;

static PORT:    int = 4414;
static IP: &'static str = "127.0.0.1";

struct sched_msg {
    stream: Option<std::rt::io::net::tcp::TcpStream>,
    filepath: ~std::path::PosixPath,
    ip: IpAddr,
    filesize: Option<uint>, //filesize added to store file size
}

fn main() {
    let mut cache: HashMap<~str, ~[u8]> = HashMap::new();
    let shared_cache = arc::RWArc::new(cache);
    let add_cache = shared_cache.clone();

    let mut req_vec : PriorityQueue<sched_msg> = PriorityQueue::new();
    let shared_req_vec = arc::RWArc::new(req_vec);
    let add_vec = shared_req_vec.clone();
    let take_vec = shared_req_vec.clone();

    let (port, chan) = stream();
    let chan = SharedChan::new(chan);

    // dequeue file requests, and send responses.
    // FIFO
    do spawn {
        let (sm_port, sm_chan) = stream();

        // a task for sending responses.
        do spawn {
            loop {
                let mut tf: sched_msg = sm_port.recv(); // wait for the dequeued request to handle

                let ref file = tf.filepath.components[tf.filepath.components.len()-1];

                let mut cache_data: Option<~[u8]> = None;

                do add_cache.write | map | {
                    let foundfile: Option<&~[u8]> = (*map).find(file);
                    match foundfile {
                        Some(stuff) => {
                            cache_data = Some( stuff.to_owned() );
                        },
                        None => ()
                    }

                }

                let mut file_data: ~[u8] = ~[];
                match cache_data {
                    Some(ref data) => {
                        println("\n[Cache] hit! found "+file.to_owned()+"\n");
                        file_data = (*data).to_owned();
                    },
                    None => {
                        match io::read_whole_file(tf.filepath) {
                            Ok(data) => {
                                println("\n[Cache] miss, could not find "+file.to_owned()+"\n");
                                file_data = data;

                                do add_cache.write | map | {
                                    (*map).swap(file.to_owned(), file_data.clone());
                                }

                            }
                            Err(err) => println(err)
                        }
                    }
                }

                println(fmt!("begin serving file [%?]", tf.filepath));
                // A web server should always reply a HTTP header for any legal HTTP request.

                if file.ends_with(".html") {
                    tf.stream.write("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n".as_bytes());
                    let strval = str::from_utf8(file_data);
                    let gashresult = gashing::gashify(strval);
                    file_data = gashresult.as_bytes().to_owned();
                } else if file.ends_with(".txt") || file.ends_with(".md") {
                    tf.stream.write("HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n".as_bytes());
                } else {
                    tf.stream.write("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream; charset=UTF-8\r\n\r\n".as_bytes());
                }

                tf.stream.write(file_data);

            }
        }

        loop {
            port.recv(); // wait for arrving notification
            do take_vec.write |vec| {
                if ((*vec).len() > 0) {
                    // LIFO didn't make sense in service scheduling, so we modify it as FIFO by using shift_opt() rather than pop().
                    let tf : sched_msg = (*vec).pop();
                    println(fmt!("shift from queue, size: %ud", (*vec).len()));
                    sm_chan.send(tf); // send the request to send-response-task to serve.
                }
            }
        }
    }

    let ip = match FromStr::from_str(IP) { Some(ip) => ip,
                                           None => { println(fmt!("Error: Invalid IP address <%s>", IP));
                                                     return;},
                                         };

    let socket = net::tcp::TcpListener::bind(SocketAddr {ip: ip, port: PORT as u16});

    println(fmt!("Listening on %s:%d ...", ip.to_str(), PORT));
    let mut acceptor = socket.listen().unwrap();

    let mycount: uint = 0;
    let shared_mycount = arc::RWArc::new(mycount);

    for stream in acceptor.incoming() {
        let stream = Cell::new(stream);

        // Start a new task to handle the each connection
        let child_chan = chan.clone();
        let child_add_vec = add_vec.clone();
        let update_count = shared_mycount.clone();
        do spawn {
            let mut visitor_count = 0;
            do update_count.write |c| {
                *c += 1;
                visitor_count = *c;
             }

            let mut stream = stream.take();

            let mut visitor_ip: IpAddr = std::rt::io::net::ip::Ipv4Addr(-1, -1, -1, -1);

            stream.and_then_mut_ref(|x| {
                    match x.peer_name() {
                        Some(sock_addr) => {visitor_ip = sock_addr.ip;},
                        None => ()
                    }
                    Some(x)
                });

            println(fmt!("Visitor IP is %s", visitor_ip.to_str()));

            let mut buf = [0, ..500];
            stream.read(buf);
            let request_str = str::from_utf8(buf);

            let req_group : ~[&str]= request_str.splitn_iter(' ', 3).collect();
            if req_group.len() > 2 {
                let path = req_group[1];
                println(fmt!("Request for path: \n%?", path));

                let file_path = ~os::getcwd().push(path.replace("/../", ""));
                if !os::path_exists(file_path) || os::path_is_dir(file_path) {
                    println(fmt!("Request received:\n%s", request_str));
                    let response: ~str = fmt!(
                        "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                         <doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

Visitor count: %u

                         \r\n", visitor_count);

                    stream.write(response.as_bytes());
                }
                else {
                    // Requests scheduling

		    // Started Problem 3 code here
		    // Creates process that runs and parses file size command for problem 3
		    let mut args : ~[~str] = ~[~"-c", ~"<", file_path.to_str()];
		    let mut pr = run::Process::new("wc", args, run::ProcessOptions::new());
		    let poutput = pr.finish_with_output();
		    let mut poutputc = poutput.output;
		    let mut realstr = str::from_utf8(poutputc);
		    let mut strarray: ~[&str] = realstr.split_iter(' ').collect();
		    let mut formatfsize : Option<uint> = from_str(strarray[0]);

                    let msg: sched_msg = sched_msg{stream: stream, filepath: file_path.clone(), ip: visitor_ip, filesize : formatfsize };

		    println(fmt!("%?", realstr));//print file size served
		    println(fmt!("%?", strarray[0]));//print file size served
		    println(fmt!("%?", formatfsize));//print file size served

		    //Problem 3 Code finishes here

                    let (sm_port, sm_chan) = std::comm::stream();
                    sm_chan.send(msg);

                    do child_add_vec.write |vec| {
                        let msg = sm_port.recv();
                        (*vec).push(msg); // enqueue new request.
                        println("add to queue");
                    }
                    child_chan.send(""); //notify the new arriving request.
                    println(fmt!("get file request: %?", file_path));
                }
            }
            println!("connection terminates")
        }
    }
}

impl Ord for sched_msg {

	fn lt(&self, other: &sched_msg) -> bool {

		let selfIP: IpAddr = self.ip;
		let otherIP: IpAddr = other.ip;

		let selfSize : Option<uint> = self.filesize;
		let otherSize : Option<uint> = other.filesize;

		match selfIP {
			Ipv4Addr(a , b, c, d) => {
				if ((a == 128 && b == 143) || (a == 137 && b == 54)){
					return false;
				}else{
					if (selfSize < otherSize){
						return false;
					}else{
						return true;
					}
				}
			},
			Ipv6Addr(a, b, c, d, e, f, g, h) => {
				if ((a == 128 && b == 143) || (a == 137 && b == 54)){
					return false;
				}else{
					if (selfSize < otherSize){
						return false;
					}else{
						return true;
					}
				}

			}
		}

	}
}

<commit_msg>cache is now fixed size<commit_after>//
// zhtta.rs
//
// Running on Rust 0.8
//
// Starting code for PS3
//
// Note: it would be very unwise to run this server on a machine that is
// on the Internet and contains any sensitive files!
//
// University of Virginia - cs4414 Fall 2013
// Weilin Xu and David Evans
// Version 0.3

extern mod extra;

use std::rt::io::*;
use std::rt::io::net::ip::SocketAddr;
use std::io::println;
use std::cell::Cell;
use std::{os, str, io, run, uint};
use extra::arc;
use std::comm::*;
use extra::priority_queue::PriorityQueue;
use std::rt::io::net::ip::*;
use std::hashmap::HashMap;

mod gashing;

static PORT:    int = 4414;
static IP: &'static str = "127.0.0.1";

struct sched_msg {
    stream: Option<std::rt::io::net::tcp::TcpStream>,
    filepath: ~std::path::PosixPath,
    ip: IpAddr,
    filesize: Option<uint>, //filesize added to store file size
}

fn main() {
    let mut cache: HashMap<~str, ~[u8]> = HashMap::new();
    let shared_cache = arc::RWArc::new(cache);
    let add_cache = shared_cache.clone();

    let mut cache_queue: ~[~str] = ~[];
    let shared_cache_queue = arc::RWArc::new(cache_queue);
    let add_cache_queue = shared_cache_queue.clone();

    let mut req_vec : PriorityQueue<sched_msg> = PriorityQueue::new();
    let shared_req_vec = arc::RWArc::new(req_vec);
    let add_vec = shared_req_vec.clone();
    let take_vec = shared_req_vec.clone();

    let (port, chan) = stream();
    let chan = SharedChan::new(chan);

    // dequeue file requests, and send responses.
    // FIFO
    do spawn {
        let (sm_port, sm_chan) = stream();

        // a task for sending responses.
        do spawn {
            loop {
                let mut tf: sched_msg = sm_port.recv(); // wait for the dequeued request to handle

                let ref file = tf.filepath.components[tf.filepath.components.len()-1];

                do add_cache_queue.write |vec| {
                    vec.push(file.to_owned());

                    let mut index_r = 0;
                    for v in vec.iter() {
                        if v==file {
                            break;
                        }
                        index_r += 1;
                    }
                    vec.remove(index_r);
                }

                let mut cache_data: Option<~[u8]> = None;

                do add_cache.write | map | {

                    let foundfile: Option<&~[u8]> = (*map).find(file);
                    match foundfile {
                        Some(stuff) => {
                            cache_data = Some( stuff.to_owned() );
                        },
                        None => ()
                    }
                }

                let mut file_data: ~[u8] = ~[];
                match cache_data {
                    Some(ref data) => {
                        println("\n[Cache] hit! found "+file.to_owned()+"\n");
                        file_data = (*data).to_owned();
                    },
                    None => {
                        match io::read_whole_file(tf.filepath) {
                            Ok(data) => {
                                println("\n[Cache] miss, could not find "+file.to_owned()+"\n");
                                file_data = data;

                                do add_cache.write | map | {
                                    if (*map).len() > 1000 {
                                        do add_cache_queue.write |vec| {
                                            match vec.shift_opt() {
                                                Some(vecitem) => {
                                                    (*map).swap(vecitem, ~[]);
                                                },
                                                None => {
                                                }
                                            }
                                        }

                                    }

                                    (*map).swap(file.to_owned(), file_data.clone());
                                }

                            }
                            Err(err) => println(err)
                        }
                    }
                }

                println(fmt!("begin serving file [%?]", tf.filepath));
                // A web server should always reply a HTTP header for any legal HTTP request.

                if file.ends_with(".html") {
                    tf.stream.write("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n".as_bytes());
                    let strval = str::from_utf8(file_data);
                    let gashresult = gashing::gashify(strval);
                    file_data = gashresult.as_bytes().to_owned();
                } else if file.ends_with(".txt") || file.ends_with(".md") {
                    tf.stream.write("HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n".as_bytes());
                } else {
                    tf.stream.write("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream; charset=UTF-8\r\n\r\n".as_bytes());
                }

                tf.stream.write(file_data);

            }
        }

        loop {
            port.recv(); // wait for arrving notification
            do take_vec.write |vec| {
                if ((*vec).len() > 0) {
                    // LIFO didn't make sense in service scheduling, so we modify it as FIFO by using shift_opt() rather than pop().
                    let tf : sched_msg = (*vec).pop();
                    println(fmt!("shift from queue, size: %ud", (*vec).len()));
                    sm_chan.send(tf); // send the request to send-response-task to serve.
                }
            }
        }
    }

    let ip = match FromStr::from_str(IP) { Some(ip) => ip,
                                           None => { println(fmt!("Error: Invalid IP address <%s>", IP));
                                                     return;},
                                         };

    let socket = net::tcp::TcpListener::bind(SocketAddr {ip: ip, port: PORT as u16});

    println(fmt!("Listening on %s:%d ...", ip.to_str(), PORT));
    let mut acceptor = socket.listen().unwrap();

    let mycount: uint = 0;
    let shared_mycount = arc::RWArc::new(mycount);

    for stream in acceptor.incoming() {
        let stream = Cell::new(stream);

        // Start a new task to handle the each connection
        let child_chan = chan.clone();
        let child_add_vec = add_vec.clone();
        let update_count = shared_mycount.clone();
        do spawn {
            let mut visitor_count = 0;
            do update_count.write |c| {
                *c += 1;
                visitor_count = *c;
             }

            let mut stream = stream.take();

            let mut visitor_ip: IpAddr = std::rt::io::net::ip::Ipv4Addr(-1, -1, -1, -1);

            stream.and_then_mut_ref(|x| {
                    match x.peer_name() {
                        Some(sock_addr) => {visitor_ip = sock_addr.ip;},
                        None => ()
                    }
                    Some(x)
                });

            println(fmt!("Visitor IP is %s", visitor_ip.to_str()));

            let mut buf = [0, ..500];
            stream.read(buf);
            let request_str = str::from_utf8(buf);

            let req_group : ~[&str]= request_str.splitn_iter(' ', 3).collect();
            if req_group.len() > 2 {
                let path = req_group[1];
                println(fmt!("Request for path: \n%?", path));

                let file_path = ~os::getcwd().push(path.replace("/../", ""));
                if !os::path_exists(file_path) || os::path_is_dir(file_path) {
                    println(fmt!("Request received:\n%s", request_str));
                    let response: ~str = fmt!(
                        "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                         <doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

Visitor count: %u

                         \r\n", visitor_count);

                    stream.write(response.as_bytes());
                }
                else {
                    // Requests scheduling

		    // Started Problem 3 code here
		    // Creates process that runs and parses file size command for problem 3
		    let mut args : ~[~str] = ~[~"-c", ~"<", file_path.to_str()];
		    let mut pr = run::Process::new("wc", args, run::ProcessOptions::new());
		    let poutput = pr.finish_with_output();
		    let mut poutputc = poutput.output;
		    let mut realstr = str::from_utf8(poutputc);
		    let mut strarray: ~[&str] = realstr.split_iter(' ').collect();
		    let mut formatfsize : Option<uint> = from_str(strarray[0]);

                    let msg: sched_msg = sched_msg{stream: stream, filepath: file_path.clone(), ip: visitor_ip, filesize : formatfsize };

		    println(fmt!("%?", realstr));//print file size served
		    println(fmt!("%?", strarray[0]));//print file size served
		    println(fmt!("%?", formatfsize));//print file size served

		    //Problem 3 Code finishes here

                    let (sm_port, sm_chan) = std::comm::stream();
                    sm_chan.send(msg);

                    do child_add_vec.write |vec| {
                        let msg = sm_port.recv();
                        (*vec).push(msg); // enqueue new request.
                        println("add to queue");
                    }
                    child_chan.send(""); //notify the new arriving request.
                    println(fmt!("get file request: %?", file_path));
                }
            }
            println!("connection terminates")
        }
    }
}

impl Ord for sched_msg {

	fn lt(&self, other: &sched_msg) -> bool {

		let selfIP: IpAddr = self.ip;
		let otherIP: IpAddr = other.ip;

		let selfSize : Option<uint> = self.filesize;
		let otherSize : Option<uint> = other.filesize;

		match selfIP {
			Ipv4Addr(a , b, c, d) => {
				if ((a == 128 && b == 143) || (a == 137 && b == 54)){
					return false;
				}else{
					if (selfSize < otherSize){
						return false;
					}else{
						return true;
					}
				}
			},
			Ipv6Addr(a, b, c, d, e, f, g, h) => {
				if ((a == 128 && b == 143) || (a == 137 && b == 54)){
					return false;
				}else{
					if (selfSize < otherSize){
						return false;
					}else{
						return true;
					}
				}

			}
		}

	}
}

<|endoftext|>