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

use crate::errors::*;
use clap::{App, AppSettings, Arg};
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
use which::which_in;

arg_enum! {
    #[derive(Debug)]
    #[allow(non_camel_case_types)]
    pub enum StatsFormat {
        text,
        json
    }
}

/// A specific command to run.
pub enum Command {
    /// Show cache statistics and exit.
    ShowStats(StatsFormat),
    /// Run background server.
    InternalStartServer,
    /// Start background server as a subprocess.
    StartServer,
    /// Stop background server.
    StopServer,
    /// Zero cache statistics and exit.
    ZeroStats,
    /// Show the status of the distributed client.
    DistStatus,
    /// Perform a login to authenticate for distributed compilation.
    DistAuth,
    /// Package a toolchain for distributed compilation (executable, out)
    PackageToolchain(PathBuf, PathBuf),
    /// Run a compiler command.
    Compile {
        /// The binary to execute.
        exe: OsString,
        /// The commandline arguments to pass to `exe`.
        cmdline: Vec<OsString>,
        /// The directory in which to execute the command.
        cwd: PathBuf,
        /// The environment variables to use for execution.
        env_vars: Vec<(OsString, OsString)>,
    },
}

/// Get the `App` used for argument parsing.
pub fn get_app<'a, 'b>() -> App<'a, 'b> {
    App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .setting(AppSettings::TrailingVarArg)
        .after_help(concat!(
                "Enabled features:\n",
                "    S3:        ", cfg!(feature = "s3"), "\n",
                "    Redis:     ", cfg!(feature = "redis"), "\n",
                "    Memcached: ", cfg!(feature = "memcached"), "\n",
                "    GCS:       ", cfg!(feature = "gcs"), "\n",
                "    Azure:     ", cfg!(feature = "azure"), "\n")
                )
        .args_from_usage(
            "-s --show-stats  'show cache statistics'
             --start-server   'start background server'
             --stop-server    'stop background server'
             -z, --zero-stats 'zero statistics counters'
             --dist-auth      'authenticate for distributed compilation'
             --dist-status    'show status of the distributed client'"
                )
        .arg(Arg::from_usage("--package-toolchain <executable> <out> 'package toolchain for distributed compilation'")
             .required(false))
        .arg(Arg::from_usage("--stats-format  'set output format of statistics'")
             .possible_values(&StatsFormat::variants())
             .default_value("text"))
        .arg(
            Arg::with_name("cmd")
                .multiple(true)
                .use_delimiter(false)
                )
}

/// Parse the commandline into a `Command` to execute.
pub fn parse() -> Result<Command> {
    trace!("parse");
    let cwd =
        env::current_dir().chain_err(|| "sccache: Couldn't determine current working directory")?;
    // The internal start server command is passed in the environment.
    let internal_start_server = match env::var("SCCACHE_START_SERVER") {
        Ok(val) => val == "1",
        Err(_) => false,
    };
    let mut args: Vec<_> = env::args_os().collect();
    if !internal_start_server {
        if let Ok(exe) = env::current_exe() {
            match exe
                .file_stem()
                .and_then(|s| s.to_str())
                .map(|s| s.to_lowercase())
            {
                // If the executable has its standard name, do nothing.
                Some(ref e) if e == env!("CARGO_PKG_NAME") => {}
                // Otherwise, if it was copied/hardlinked under a different $name, act
                // as if it were invoked with `sccache $name`, but avoid $name resolving
                // to ourselves again if it's in the PATH.
                _ => {
                    if let (Some(path), Some(exe_filename)) = (env::var_os("PATH"), exe.file_name())
                    {
                        match which_in(exe_filename, Some(&path), &cwd) {
                            Ok(ref full_path)
                                if full_path.canonicalize()? == exe.canonicalize()? =>
                            {
                                if let Some(dir) = full_path.parent() {
                                    let path = env::join_paths(
                                        env::split_paths(&path).filter(|p| p != dir),
                                    )
                                    .ok();
                                    match which_in(exe_filename, path, &cwd) {
                                        Ok(full_path) => args[0] = full_path.into(),
                                        Err(_) => {}
                                    }
                                }
                            }
                            Ok(full_path) => args[0] = full_path.into(),
                            Err(_) => {}
                        }
                        args.insert(0, env!("CARGO_PKG_NAME").into());
                    }
                }
            }
        }
    }
    let matches = get_app().get_matches_from(args);

    let show_stats = matches.is_present("show-stats");
    let start_server = matches.is_present("start-server");
    let stop_server = matches.is_present("stop-server");
    let zero_stats = matches.is_present("zero-stats");
    let dist_auth = matches.is_present("dist-auth");
    let dist_status = matches.is_present("dist-status");
    let package_toolchain = matches.is_present("package-toolchain");
    let cmd = matches.values_of_os("cmd");
    // Ensure that we've only received one command to run.
    fn is_some<T>(x: &Option<T>) -> bool {
        x.is_some()
    }
    if [
        internal_start_server,
        show_stats,
        start_server,
        stop_server,
        zero_stats,
        package_toolchain,
        is_some(&cmd),
    ]
    .iter()
    .filter(|&&x| x)
    .count()
        > 1
    {
        bail!("Too many commands specified");
    }
    if internal_start_server {
        Ok(Command::InternalStartServer)
    } else if show_stats {
        let fmt =
            value_t!(matches.value_of("stats-format"), StatsFormat).unwrap_or_else(|e| e.exit());
        Ok(Command::ShowStats(fmt))
    } else if start_server {
        Ok(Command::StartServer)
    } else if stop_server {
        Ok(Command::StopServer)
    } else if zero_stats {
        Ok(Command::ZeroStats)
    } else if dist_auth {
        Ok(Command::DistAuth)
    } else if dist_status {
        Ok(Command::DistStatus)
    } else if package_toolchain {
        let mut values = matches
            .values_of_os("package-toolchain")
            .expect("Parsed package-toolchain but no values");
        assert!(values.len() == 2);
        let (executable, out) = (
            values.next().expect("package-toolchain missing value 1"),
            values.next().expect("package-toolchain missing value 2"),
        );
        Ok(Command::PackageToolchain(executable.into(), out.into()))
    } else if let Some(mut args) = cmd {
        if let Some(exe) = args.next() {
            let cmdline = args.map(|s| s.to_owned()).collect::<Vec<_>>();
            Ok(Command::Compile {
                exe: exe.to_owned(),
                cmdline: cmdline,
                cwd: cwd,
                env_vars: env::vars_os().collect(),
            })
        } else {
            bail!("No compile command");
        }
    } else {
        bail!("No command specified");
    }
}
<commit_msg>cmdline: Allow running sccache under rr without crashing the remote compiler.<commit_after>// Copyright 2016 Mozilla Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::errors::*;
use clap::{App, AppSettings, Arg};
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
use which::which_in;

arg_enum! {
    #[derive(Debug)]
    #[allow(non_camel_case_types)]
    pub enum StatsFormat {
        text,
        json
    }
}

/// A specific command to run.
pub enum Command {
    /// Show cache statistics and exit.
    ShowStats(StatsFormat),
    /// Run background server.
    InternalStartServer,
    /// Start background server as a subprocess.
    StartServer,
    /// Stop background server.
    StopServer,
    /// Zero cache statistics and exit.
    ZeroStats,
    /// Show the status of the distributed client.
    DistStatus,
    /// Perform a login to authenticate for distributed compilation.
    DistAuth,
    /// Package a toolchain for distributed compilation (executable, out)
    PackageToolchain(PathBuf, PathBuf),
    /// Run a compiler command.
    Compile {
        /// The binary to execute.
        exe: OsString,
        /// The commandline arguments to pass to `exe`.
        cmdline: Vec<OsString>,
        /// The directory in which to execute the command.
        cwd: PathBuf,
        /// The environment variables to use for execution.
        env_vars: Vec<(OsString, OsString)>,
    },
}

/// Get the `App` used for argument parsing.
pub fn get_app<'a, 'b>() -> App<'a, 'b> {
    App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .setting(AppSettings::TrailingVarArg)
        .after_help(concat!(
                "Enabled features:\n",
                "    S3:        ", cfg!(feature = "s3"), "\n",
                "    Redis:     ", cfg!(feature = "redis"), "\n",
                "    Memcached: ", cfg!(feature = "memcached"), "\n",
                "    GCS:       ", cfg!(feature = "gcs"), "\n",
                "    Azure:     ", cfg!(feature = "azure"), "\n")
                )
        .args_from_usage(
            "-s --show-stats  'show cache statistics'
             --start-server   'start background server'
             --stop-server    'stop background server'
             -z, --zero-stats 'zero statistics counters'
             --dist-auth      'authenticate for distributed compilation'
             --dist-status    'show status of the distributed client'"
                )
        .arg(Arg::from_usage("--package-toolchain <executable> <out> 'package toolchain for distributed compilation'")
             .required(false))
        .arg(Arg::from_usage("--stats-format  'set output format of statistics'")
             .possible_values(&StatsFormat::variants())
             .default_value("text"))
        .arg(
            Arg::with_name("cmd")
                .multiple(true)
                .use_delimiter(false)
                )
}

/// Parse the commandline into a `Command` to execute.
pub fn parse() -> Result<Command> {
    trace!("parse");
    let cwd =
        env::current_dir().chain_err(|| "sccache: Couldn't determine current working directory")?;
    // The internal start server command is passed in the environment.
    let internal_start_server = match env::var("SCCACHE_START_SERVER") {
        Ok(val) => val == "1",
        Err(_) => false,
    };
    let mut args: Vec<_> = env::args_os().collect();
    if !internal_start_server {
        if let Ok(exe) = env::current_exe() {
            match exe
                .file_stem()
                .and_then(|s| s.to_str())
                .map(|s| s.to_lowercase())
            {
                // If the executable has its standard name, do nothing.
                Some(ref e) if e == env!("CARGO_PKG_NAME") => {}
                // Otherwise, if it was copied/hardlinked under a different $name, act
                // as if it were invoked with `sccache $name`, but avoid $name resolving
                // to ourselves again if it's in the PATH.
                _ => {
                    if let (Some(path), Some(exe_filename)) = (env::var_os("PATH"), exe.file_name())
                    {
                        match which_in(exe_filename, Some(&path), &cwd) {
                            Ok(ref full_path)
                                if full_path.canonicalize()? == exe.canonicalize()? =>
                            {
                                if let Some(dir) = full_path.parent() {
                                    let path = env::join_paths(
                                        env::split_paths(&path).filter(|p| p != dir),
                                    )
                                    .ok();
                                    match which_in(exe_filename, path, &cwd) {
                                        Ok(full_path) => args[0] = full_path.into(),
                                        Err(_) => {}
                                    }
                                }
                            }
                            Ok(full_path) => args[0] = full_path.into(),
                            Err(_) => {}
                        }
                        args.insert(0, env!("CARGO_PKG_NAME").into());
                    }
                }
            }
        }
    }
    let matches = get_app().get_matches_from(args);

    let show_stats = matches.is_present("show-stats");
    let start_server = matches.is_present("start-server");
    let stop_server = matches.is_present("stop-server");
    let zero_stats = matches.is_present("zero-stats");
    let dist_auth = matches.is_present("dist-auth");
    let dist_status = matches.is_present("dist-status");
    let package_toolchain = matches.is_present("package-toolchain");
    let cmd = matches.values_of_os("cmd");
    // Ensure that we've only received one command to run.
    fn is_some<T>(x: &Option<T>) -> bool {
        x.is_some()
    }
    if [
        internal_start_server,
        show_stats,
        start_server,
        stop_server,
        zero_stats,
        package_toolchain,
        is_some(&cmd),
    ]
    .iter()
    .filter(|&&x| x)
    .count()
        > 1
    {
        bail!("Too many commands specified");
    }
    if internal_start_server {
        Ok(Command::InternalStartServer)
    } else if show_stats {
        let fmt =
            value_t!(matches.value_of("stats-format"), StatsFormat).unwrap_or_else(|e| e.exit());
        Ok(Command::ShowStats(fmt))
    } else if start_server {
        Ok(Command::StartServer)
    } else if stop_server {
        Ok(Command::StopServer)
    } else if zero_stats {
        Ok(Command::ZeroStats)
    } else if dist_auth {
        Ok(Command::DistAuth)
    } else if dist_status {
        Ok(Command::DistStatus)
    } else if package_toolchain {
        let mut values = matches
            .values_of_os("package-toolchain")
            .expect("Parsed package-toolchain but no values");
        assert!(values.len() == 2);
        let (executable, out) = (
            values.next().expect("package-toolchain missing value 1"),
            values.next().expect("package-toolchain missing value 2"),
        );
        Ok(Command::PackageToolchain(executable.into(), out.into()))
    } else if let Some(mut args) = cmd {
        if let Some(exe) = args.next() {
            let cmdline = args.map(|s| s.to_owned()).collect::<Vec<_>>();
            let mut env_vars = env::vars_os().collect::<Vec<_>>();

            // If we're running under rr, avoid the `LD_PRELOAD` bits, as it will
            // almost surely do the wrong thing, as the compiler gets executed
            // in a different process tree.
            //
            // FIXME: Maybe we should strip out `LD_PRELOAD` always?
            if env::var_os("RUNNING_UNDER_RR").is_some() {
                env_vars.retain(|(k, _v)| k != "LD_PRELOAD" && k != "RUNNING_UNDER_RR");
            }

            Ok(Command::Compile {
                exe: exe.to_owned(),
                cmdline,
                cwd,
                env_vars,
            })
        } else {
            bail!("No compile command");
        }
    } else {
        bail!("No command specified");
    }
}
<|endoftext|>


<commit_before>use std::{
    collections::hash_map::RandomState,
    hash::{BuildHasher, Hash, Hasher},
    sync::Arc,
    time::{Duration, SystemTime},
};

use axum::http::StatusCode;
use clap::Clap;
use futures_util::StreamExt;
use rustc_hash::FxHashMap;
use shakmaty::{
    uci::Uci, variant::VariantPosition, zobrist::Zobrist, ByColor, CastlingMode, Color, Outcome,
    Position,
};
use tokio::{
    sync::{
        mpsc::{self, error::SendTimeoutError},
        oneshot,
    },
    task::JoinHandle,
};

use crate::{
    db::Database,
    model::{
        GameInfo, GameInfoPlayer, Mode, Month, PersonalEntry, PersonalKeyBuilder, PersonalStatus,
        UserId, UserName,
    },
    util::NevermindExt as _,
};

mod lila;

use lila::{Game, Lila};

#[derive(Clap, Clone)]
pub struct IndexerOpt {
    #[clap(long = "lila", default_value = "https://lichess.org")]
    lila: String,
    #[clap(long = "bearer")]
    bearer: Option<String>,
    #[clap(long = "indexers", default_value = "16")]
    indexers: usize,
}

#[derive(Clone)]
pub struct IndexerStub {
    random_state: RandomState,
    txs: Vec<mpsc::Sender<IndexerMessage>>,
}

impl IndexerStub {
    pub fn spawn(db: Arc<Database>, opt: IndexerOpt) -> (IndexerStub, Vec<JoinHandle<()>>) {
        let mut txs = Vec::with_capacity(opt.indexers);
        let mut join_handles = Vec::with_capacity(opt.indexers);
        for idx in 0..opt.indexers {
            let (tx, rx) = mpsc::channel(500);
            txs.push(tx);
            join_handles.push(tokio::spawn(
                IndexerActor {
                    idx,
                    rx,
                    db: db.clone(),
                    lila: Lila::new(opt.clone()),
                }
                .run(),
            ));
        }
        let random_state = RandomState::new();
        (IndexerStub { txs, random_state }, join_handles)
    }

    pub async fn index_player(&self, player: UserName) -> oneshot::Receiver<()> {
        let (req, res) = oneshot::channel();

        let mut responsible_indexer = self.random_state.build_hasher();
        UserId::from(player.clone()).hash(&mut responsible_indexer);

        match self.txs[responsible_indexer.finish() as usize % self.txs.len()]
            .send_timeout(
                IndexerMessage::IndexPlayer {
                    player,
                    callback: req,
                },
                Duration::from_secs(10),
            )
            .await
        {
            Ok(_) => (),
            Err(SendTimeoutError::Timeout(_)) => {
                log::error!("indexer queue full for more than 10 seconds")
            }
            Err(SendTimeoutError::Closed(_)) => panic!("indexer died"),
        };

        res
    }
}

struct IndexerActor {
    idx: usize,
    rx: mpsc::Receiver<IndexerMessage>,
    db: Arc<Database>,
    lila: Lila,
}

impl IndexerActor {
    async fn run(mut self) {
        while let Some(msg) = self.rx.recv().await {
            match msg {
                IndexerMessage::IndexPlayer { callback, player } => {
                    self.index_player(player).await;
                    callback.send(()).nevermind("requester gone away");
                }
            }
        }
    }

    async fn index_player(&self, player: UserName) {
        let player_id = UserId::from(player.clone());
        let mut status = self
            .db
            .queryable()
            .get_player_status(&player_id)
            .expect("get player status")
            .unwrap_or_default();

        let since_created_at = match status
            .maybe_revisit_ongoing()
            .or_else(|| status.maybe_index())
        {
            Some(since) => since,
            None => {
                log::debug!("not reindexing {} so soon", player);
                return;
            }
        };

        log::info!(
            "indexer {} starting {} (created_at >= {})",
            self.idx,
            player,
            since_created_at
        );
        let mut games = match self.lila.user_games(&player, since_created_at).await {
            Ok(games) => games,
            Err(err) if err.status() == Some(StatusCode::NOT_FOUND) => {
                log::warn!("indexer did not find player {}", player);
                return;
            }
            Err(err) => {
                log::error!("indexer {}: request failed: {}", self.idx, err);
                return;
            }
        };

        let hash = ByColor::new_with(|color| PersonalKeyBuilder::with_user_pov(&player_id, color));
        let mut num_games = 0;
        while let Some(game) = games.next().await {
            let game = match game {
                Ok(game) => game,
                Err(err) => {
                    log::error!("{}", err);
                    continue;
                }
            };

            self.index_game(&player, &hash, game, &mut status);

            num_games += 1;
            if num_games % 1024 == 0 {
                log::info!(
                    "indexer {}: indexed {} games for {}",
                    self.idx,
                    num_games,
                    player
                );
            }
        }

        status.indexed_at = SystemTime::now();
        self.db
            .queryable()
            .put_player_status(&player_id, status)
            .expect("put player status");
        log::info!(
            "indexer {}: finished indexing {} games for {}",
            self.idx,
            num_games,
            player
        );
    }

    fn index_game(
        &self,
        player: &UserName,
        hash: &ByColor<PersonalKeyBuilder>,
        game: Game,
        status: &mut PersonalStatus,
    ) {
        status.latest_created_at = game.created_at;

        if game.status.is_ongoing() {
            if status.revisit_ongoing_created_at.is_none() {
                log::debug!("will revisit ongoing game {} eventually", game.id);
                status.revisit_ongoing_created_at = Some(game.created_at);
            }
            return;
        }

        if game.status.is_unindexable() {
            log::debug!("{} not indexable", game.id);
            return;
        }

        let color = if Some(player) == game.user_name(Color::White) {
            Color::White
        } else if Some(player) == game.user_name(Color::Black) {
            Color::Black
        } else {
            log::error!("{} did not play in {}", player, game.id);
            return;
        };
        let month = Month::from_time_saturating(game.last_move_at);
        let outcome = Outcome::from_winner(game.winner);

        let queryable = self.db.queryable();
        if queryable
            .get_game_info(game.id)
            .expect("get game info")
            .map_or(false, |info| info.indexed.into_color(color))
        {
            log::debug!(
                "{}/{} already indexed",
                game.id,
                color.fold("white", "black")
            );
            return;
        }
        queryable
            .merge_game_info(
                game.id,
                GameInfo {
                    winner: outcome.winner(),
                    speed: game.speed,
                    rated: game.rated,
                    month,
                    players: game.players.map(|p| GameInfoPlayer {
                        name: p.user.map(|p| p.name.to_string()),
                        rating: p.rating,
                    }),
                    indexed: ByColor::new_with(|c| color == c),
                },
            )
            .expect("put game info");

        let variant = game.variant.into();
        let pos = match game.initial_fen {
            Some(fen) => VariantPosition::from_setup(variant, &fen, CastlingMode::Chess960),
            None => Ok(VariantPosition::new(variant)),
        };

        let mut pos: Zobrist<_, u128> = match pos {
            Ok(pos) => Zobrist::new(pos),
            Err(err) => {
                log::warn!("not indexing {}: {}", game.id, err);
                return;
            }
        };

        // Build an intermediate table to remove loops (due to repetitions).
        let mut table: FxHashMap<u128, Uci> =
            FxHashMap::with_capacity_and_hasher(game.moves.len(), Default::default());

        for (ply, san) in game.moves.into_iter().enumerate() {
            let m = match san.to_move(&pos) {
                Ok(m) => m,
                Err(err) => {
                    log::warn!("cutting off {} at ply {}: {}: {}", game.id, ply, err, san);
                    break;
                }
            };

            let uci = m.to_uci(CastlingMode::Chess960);
            table.insert(pos.zobrist_hash(), uci);

            pos.play_unchecked(&m);
        }

        for (zobrist, uci) in table {
            queryable
                .merge_personal(
                    hash.by_color(color)
                        .with_zobrist(variant, zobrist)
                        .with_month(month),
                    PersonalEntry::new_single(
                        uci.clone(),
                        game.speed,
                        Mode::from_rated(game.rated),
                        game.id,
                        outcome,
                    ),
                )
                .expect("merge personal");
        }
    }
}

enum IndexerMessage {
    IndexPlayer {
        player: UserName,
        callback: oneshot::Sender<()>,
    },
}
<commit_msg>better message for skipping unidexable games<commit_after>use std::{
    collections::hash_map::RandomState,
    hash::{BuildHasher, Hash, Hasher},
    sync::Arc,
    time::{Duration, SystemTime},
};

use axum::http::StatusCode;
use clap::Clap;
use futures_util::StreamExt;
use rustc_hash::FxHashMap;
use shakmaty::{
    uci::Uci, variant::VariantPosition, zobrist::Zobrist, ByColor, CastlingMode, Color, Outcome,
    Position,
};
use tokio::{
    sync::{
        mpsc::{self, error::SendTimeoutError},
        oneshot,
    },
    task::JoinHandle,
};

use crate::{
    db::Database,
    model::{
        GameInfo, GameInfoPlayer, Mode, Month, PersonalEntry, PersonalKeyBuilder, PersonalStatus,
        UserId, UserName,
    },
    util::NevermindExt as _,
};

mod lila;

use lila::{Game, Lila};

#[derive(Clap, Clone)]
pub struct IndexerOpt {
    #[clap(long = "lila", default_value = "https://lichess.org")]
    lila: String,
    #[clap(long = "bearer")]
    bearer: Option<String>,
    #[clap(long = "indexers", default_value = "16")]
    indexers: usize,
}

#[derive(Clone)]
pub struct IndexerStub {
    random_state: RandomState,
    txs: Vec<mpsc::Sender<IndexerMessage>>,
}

impl IndexerStub {
    pub fn spawn(db: Arc<Database>, opt: IndexerOpt) -> (IndexerStub, Vec<JoinHandle<()>>) {
        let mut txs = Vec::with_capacity(opt.indexers);
        let mut join_handles = Vec::with_capacity(opt.indexers);
        for idx in 0..opt.indexers {
            let (tx, rx) = mpsc::channel(500);
            txs.push(tx);
            join_handles.push(tokio::spawn(
                IndexerActor {
                    idx,
                    rx,
                    db: db.clone(),
                    lila: Lila::new(opt.clone()),
                }
                .run(),
            ));
        }
        let random_state = RandomState::new();
        (IndexerStub { txs, random_state }, join_handles)
    }

    pub async fn index_player(&self, player: UserName) -> oneshot::Receiver<()> {
        let (req, res) = oneshot::channel();

        let mut responsible_indexer = self.random_state.build_hasher();
        UserId::from(player.clone()).hash(&mut responsible_indexer);

        match self.txs[responsible_indexer.finish() as usize % self.txs.len()]
            .send_timeout(
                IndexerMessage::IndexPlayer {
                    player,
                    callback: req,
                },
                Duration::from_secs(10),
            )
            .await
        {
            Ok(_) => (),
            Err(SendTimeoutError::Timeout(_)) => {
                log::error!("indexer queue full for more than 10 seconds")
            }
            Err(SendTimeoutError::Closed(_)) => panic!("indexer died"),
        };

        res
    }
}

struct IndexerActor {
    idx: usize,
    rx: mpsc::Receiver<IndexerMessage>,
    db: Arc<Database>,
    lila: Lila,
}

impl IndexerActor {
    async fn run(mut self) {
        while let Some(msg) = self.rx.recv().await {
            match msg {
                IndexerMessage::IndexPlayer { callback, player } => {
                    self.index_player(player).await;
                    callback.send(()).nevermind("requester gone away");
                }
            }
        }
    }

    async fn index_player(&self, player: UserName) {
        let player_id = UserId::from(player.clone());
        let mut status = self
            .db
            .queryable()
            .get_player_status(&player_id)
            .expect("get player status")
            .unwrap_or_default();

        let since_created_at = match status
            .maybe_revisit_ongoing()
            .or_else(|| status.maybe_index())
        {
            Some(since) => since,
            None => {
                log::debug!("not reindexing {} so soon", player);
                return;
            }
        };

        log::info!(
            "indexer {} starting {} (created_at >= {})",
            self.idx,
            player,
            since_created_at
        );
        let mut games = match self.lila.user_games(&player, since_created_at).await {
            Ok(games) => games,
            Err(err) if err.status() == Some(StatusCode::NOT_FOUND) => {
                log::warn!("indexer did not find player {}", player);
                return;
            }
            Err(err) => {
                log::error!("indexer {}: request failed: {}", self.idx, err);
                return;
            }
        };

        let hash = ByColor::new_with(|color| PersonalKeyBuilder::with_user_pov(&player_id, color));
        let mut num_games = 0;
        while let Some(game) = games.next().await {
            let game = match game {
                Ok(game) => game,
                Err(err) => {
                    log::error!("{}", err);
                    continue;
                }
            };

            self.index_game(&player, &hash, game, &mut status);

            num_games += 1;
            if num_games % 1024 == 0 {
                log::info!(
                    "indexer {}: indexed {} games for {}",
                    self.idx,
                    num_games,
                    player
                );
            }
        }

        status.indexed_at = SystemTime::now();
        self.db
            .queryable()
            .put_player_status(&player_id, status)
            .expect("put player status");
        log::info!(
            "indexer {}: finished indexing {} games for {}",
            self.idx,
            num_games,
            player
        );
    }

    fn index_game(
        &self,
        player: &UserName,
        hash: &ByColor<PersonalKeyBuilder>,
        game: Game,
        status: &mut PersonalStatus,
    ) {
        status.latest_created_at = game.created_at;

        if game.status.is_ongoing() {
            if status.revisit_ongoing_created_at.is_none() {
                log::debug!("will revisit ongoing game {} eventually", game.id);
                status.revisit_ongoing_created_at = Some(game.created_at);
            }
            return;
        }

        if game.status.is_unindexable() {
            log::debug!("{} with {:?} not indexable", game.id, game.status);
            return;
        }

        let color = if Some(player) == game.user_name(Color::White) {
            Color::White
        } else if Some(player) == game.user_name(Color::Black) {
            Color::Black
        } else {
            log::error!("{} did not play in {}", player, game.id);
            return;
        };
        let month = Month::from_time_saturating(game.last_move_at);
        let outcome = Outcome::from_winner(game.winner);

        let queryable = self.db.queryable();
        if queryable
            .get_game_info(game.id)
            .expect("get game info")
            .map_or(false, |info| info.indexed.into_color(color))
        {
            log::debug!(
                "{}/{} already indexed",
                game.id,
                color.fold("white", "black")
            );
            return;
        }
        queryable
            .merge_game_info(
                game.id,
                GameInfo {
                    winner: outcome.winner(),
                    speed: game.speed,
                    rated: game.rated,
                    month,
                    players: game.players.map(|p| GameInfoPlayer {
                        name: p.user.map(|p| p.name.to_string()),
                        rating: p.rating,
                    }),
                    indexed: ByColor::new_with(|c| color == c),
                },
            )
            .expect("put game info");

        let variant = game.variant.into();
        let pos = match game.initial_fen {
            Some(fen) => VariantPosition::from_setup(variant, &fen, CastlingMode::Chess960),
            None => Ok(VariantPosition::new(variant)),
        };

        let mut pos: Zobrist<_, u128> = match pos {
            Ok(pos) => Zobrist::new(pos),
            Err(err) => {
                log::warn!("not indexing {}: {}", game.id, err);
                return;
            }
        };

        // Build an intermediate table to remove loops (due to repetitions).
        let mut table: FxHashMap<u128, Uci> =
            FxHashMap::with_capacity_and_hasher(game.moves.len(), Default::default());

        for (ply, san) in game.moves.into_iter().enumerate() {
            let m = match san.to_move(&pos) {
                Ok(m) => m,
                Err(err) => {
                    log::warn!("cutting off {} at ply {}: {}: {}", game.id, ply, err, san);
                    break;
                }
            };

            let uci = m.to_uci(CastlingMode::Chess960);
            table.insert(pos.zobrist_hash(), uci);

            pos.play_unchecked(&m);
        }

        for (zobrist, uci) in table {
            queryable
                .merge_personal(
                    hash.by_color(color)
                        .with_zobrist(variant, zobrist)
                        .with_month(month),
                    PersonalEntry::new_single(
                        uci.clone(),
                        game.speed,
                        Mode::from_rated(game.rated),
                        game.id,
                        outcome,
                    ),
                )
                .expect("merge personal");
        }
    }
}

enum IndexerMessage {
    IndexPlayer {
        player: UserName,
        callback: oneshot::Sender<()>,
    },
}
<|endoftext|>


<commit_before>use parser::parser::{parse};
use expr::Expr;
use expr::Expr::*;
use expr::Val::*;
use expr::UnOp::*;
use expr::BinOp::*;
use expr::Dec::*;
use state::State;
use runtime_error::RuntimeError;
use std::result;

pub type Result<T> = result::Result<T, RuntimeError>;

pub struct Interpreter {
  pub state: State,
}

impl Interpreter {
  pub fn new() -> Interpreter {
    Interpreter {
      state: State::new(),
    }
  }

  pub fn step(&mut self, e: Expr) -> Result<Expr> {
    debug!("step(e) : {:?}", e);
    debug!("step(self.state) : {:?}", self.state.mem);

    let e1 = match e {
      Var(x) => {
        match self.state.get(x.clone()) {
          Some(e) => e,
          None => return Err(RuntimeError::VariableNotFound(x)),
        }
      },
      /**
       * Values are ineligible for step
       */
      Val(_) => {
        debug!("stepping on a value {:?}", e);
        return Err(RuntimeError::SteppingOnValue(e));
      },
      /**
       * Base cases
       */
      Uop(Not, box Val(Bool(b))) => {
        Val(Bool(!b))
      },
      Uop(Neg, box Val(Int(n))) => {
        Val(Int(-1 * n))
      },
      Bop(And, box Val(Bool(b1)), box Val(Bool(b2))) => {
        Val(Bool(b1 && b2))
      },
      Bop(Or, box Val(Bool(b1)), box Val(Bool(b2))) => {
        Val(Bool(b1 || b2))
      },
      Bop(Eq, box Val(v1), box Val(v2)) => {
        Val(Bool(v1 == v2))
      },
      Bop(Ne, box Val(v1), box Val(v2)) => {
        Val(Bool(v1 != v2))
      },
      Bop(Mod, box Val(Int(n1)), box Val(Int(n2))) => {
        // rust % gives the remainder, not modulus
        // let result = ((n1 % n2) + n2) % n2;
        let result = n1.checked_rem(n2)
          .ok_or(RuntimeError::IntegerUnderflow)?
          .checked_add(n2)
          .ok_or(RuntimeError::IntegerOverflow)?
          .checked_rem(n2)
          .ok_or(RuntimeError::IntegerUnderflow)?;

        Val(Int(result))
      },
      Bop(Lt, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 < n2))
      },
      Bop(Gt, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 > n2))
      },
      Bop(Leq, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 <= n2))
      },
      Bop(Geq, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 >= n2))
      },
      Bop(Plus, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_add(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerOverflow),
        }
      },
      Bop(Minus, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_sub(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerUnderflow),
        }
      },
      Bop(Times, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_mul(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerOverflow),
        }
      },
      Bop(Div, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_div(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerUnderflow),
        }
      },
      Bop(Seq, box Val(_), box e1) => {
        e1
      },
      Bop(Assign, box Var(x), box v @ Val(_)) => {
        self.state.assign(x, v.clone())?;
        debug!("done assigning {:?}", self.state.mem);
        v
      },
      Ternary(box Val(Bool(b)), box e1, box e2) => {
        match b {
          true => e1,
          false => e2,
        }
      },
      Decl(DConst, box Var(x), box v1 @ Val(_), box e1) => {
        self.state.alloc_const(x, v1)?;
        e1
      },
      Decl(DVar, box Var(x), box v1 @ Val(_), box e1) => {
        debug!("allocing {:?}", v1);
        self.state.alloc(x, v1)?;
        e1
      },
      // lambda lift so we can use iter() in guard
      // https://github.com/rust-lang/rfcs/issues/1006
      FnCall(ref v1, ref es) if v1.is_func() && (|| es.iter().all(|v| v.is_value()))() => {
        if let box Val(Func(ref name, ref e1, ref xs)) = *v1 {
          self.state.begin_scope();

          // alloc the params
          for (xn, en) in xs.iter().zip(es.iter()) {
            self.state.alloc(xn.to_var()?, en.clone())?;
          }

          // alloc the fn body for named functions
          if let Some(box Var(ref s)) = *name {
            self.state.alloc(s.clone(), *v1.clone())?;
          }

          Scope(e1.clone())
        } else {
          return Err(RuntimeError::UnexpectedExpr("expected Func".to_string(), *v1.clone()))
        }
      },
      Scope(box v @ Val(_)) => {
        self.state.end_scope();
        v
      },
      While(box Val(Bool(b)), e1o, _, e2o, e3) => {
        match b {
          true => While(e1o.clone(), e1o, e2o.clone(), e2o, e3),
          false => *e3,
        }
      },
      Print(box Val(v)) => {
        println!("{}", v);
        Val(Undefined)
      },
      /**
       * Search Cases
       */
      Bop(op, v1 @ box Val(_), box e2) => {
        Bop(
          op,
          v1,
          Box::new(self.step(e2)?)
        )
      },
      Bop(Assign, v1 @ box Var(_), box e2) => {
        Bop(
          Assign,
          v1,
          Box::new(self.step(e2)?)
        )
      },
      Bop(op, box e1, e2) => {
        Bop(op, Box::new(self.step(e1)?), e2)
      },
      Uop(op, box e1) => {
        Uop(op, Box::new(self.step(e1)?))
      },
      Ternary(box e1, e2, e3) => {
        Ternary(Box::new(self.step(e1)?), e2, e3)
      },
      While(box e1, e1o, v @ box Val(_), e2o, e3) => {
        While(Box::new(self.step(e1)?), e1o, v, e2o, e3)
      },
      While(e1, e1o, box e2, e2o, e3) => {
        While(e1, e1o, Box::new(self.step(e2)?), e2o, e3)
      },
      Decl(dt, box addr, box e1, e2) => {
        Decl(dt, Box::new(addr.clone()), Box::new(self.step(e1)?), e2)
      },
      FnCall(f @ box Val(Func(_, _, _)), mut args) => {
        // find the first nonvalue arg and call step() on it
        if let Some(index) = args.iter().position(|e| {
          match e {
            &Val(_) => false,
            _ => true,
          }
        }) {
          args[index] = self.step(args[index].clone())?
        }

        FnCall(f, args)
      },
      FnCall(box e1, args) => {
        FnCall(Box::new(self.step(e1)?), args)
      },
      Scope(box e1) => {
        Scope(Box::new(self.step(e1)?))
      },
      Print(box e1) => {
        Print(Box::new(self.step(e1)?))
      },
    };

    debug!("returning with mem {:?}" , self.state.mem);
    debug!("returning with e {:?}" , e1);
    Ok(e1)
  }

  pub fn eval(&mut self, input: &str) -> Result<Expr> {
    let mut e = parse(input)?;

    let mut num_iterations = 0;

    loop {
      if num_iterations > 1000000000 {
        return Err(RuntimeError::TooManyIterations(num_iterations))
      }

      debug!("-----------------");
      debug!("--- iterating on e {:?} ", e);
      debug!("--- iterating on m {:?} ", self.state.mem);
      num_iterations += 1;
      if e.is_value() {
        debug!("--- iterations: {}", num_iterations);
        return Ok(e.clone());
      } else {
        e = self.step(e.clone())?;
      }
    }
  }
}
<commit_msg>removing unecessary clones<commit_after>use parser::parser::{parse};
use expr::Expr;
use expr::Expr::*;
use expr::Val::*;
use expr::UnOp::*;
use expr::BinOp::*;
use expr::Dec::*;
use state::State;
use runtime_error::RuntimeError;
use std::result;
use std::mem;

pub type Result<T> = result::Result<T, RuntimeError>;

pub struct Interpreter {
  pub state: State,
}

impl Interpreter {
  pub fn new() -> Interpreter {
    Interpreter {
      state: State::new(),
    }
  }

  pub fn step(&mut self, e: Expr) -> Result<Expr> {
    debug!("step(e) : {:?}", e);
    debug!("step(self.state) : {:?}", self.state.mem);

    let e1 = match e {
      Var(x) => {
        match self.state.get(x.clone()) {
          Some(e) => e,
          None => return Err(RuntimeError::VariableNotFound(x)),
        }
      },
      /**
       * Values are ineligible for step
       */
      Val(_) => {
        debug!("stepping on a value {:?}", e);
        return Err(RuntimeError::SteppingOnValue(e));
      },
      /**
       * Base cases
       */
      Uop(Not, box Val(Bool(b))) => {
        Val(Bool(!b))
      },
      Uop(Neg, box Val(Int(n))) => {
        Val(Int(-1 * n))
      },
      Bop(And, box Val(Bool(b1)), box Val(Bool(b2))) => {
        Val(Bool(b1 && b2))
      },
      Bop(Or, box Val(Bool(b1)), box Val(Bool(b2))) => {
        Val(Bool(b1 || b2))
      },
      Bop(Eq, box Val(v1), box Val(v2)) => {
        Val(Bool(v1 == v2))
      },
      Bop(Ne, box Val(v1), box Val(v2)) => {
        Val(Bool(v1 != v2))
      },
      Bop(Mod, box Val(Int(n1)), box Val(Int(n2))) => {
        // rust % gives the remainder, not modulus
        // let result = ((n1 % n2) + n2) % n2;
        let result = n1.checked_rem(n2)
          .ok_or(RuntimeError::IntegerUnderflow)?
          .checked_add(n2)
          .ok_or(RuntimeError::IntegerOverflow)?
          .checked_rem(n2)
          .ok_or(RuntimeError::IntegerUnderflow)?;

        Val(Int(result))
      },
      Bop(Lt, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 < n2))
      },
      Bop(Gt, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 > n2))
      },
      Bop(Leq, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 <= n2))
      },
      Bop(Geq, box Val(Int(n1)), box Val(Int(n2))) => {
        Val(Bool(n1 >= n2))
      },
      Bop(Plus, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_add(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerOverflow),
        }
      },
      Bop(Minus, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_sub(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerUnderflow),
        }
      },
      Bop(Times, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_mul(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerOverflow),
        }
      },
      Bop(Div, box Val(Int(n1)), box Val(Int(n2))) => {
        match n1.checked_div(n2) {
          Some(n) => Val(Int(n)),
          None => return Err(RuntimeError::IntegerUnderflow),
        }
      },
      Bop(Seq, box Val(_), box e1) => {
        e1
      },
      Bop(Assign, box Var(x), box v @ Val(_)) => {
        self.state.assign(x, v.clone())?;
        debug!("done assigning {:?}", self.state.mem);
        v
      },
      Ternary(box Val(Bool(b)), box e1, box e2) => {
        match b {
          true => e1,
          false => e2,
        }
      },
      Decl(DConst, box Var(x), box v1 @ Val(_), box e1) => {
        self.state.alloc_const(x, v1)?;
        e1
      },
      Decl(DVar, box Var(x), box v1 @ Val(_), box e1) => {
        debug!("allocing {:?}", v1);
        self.state.alloc(x, v1)?;
        e1
      },
      // lambda lift so we can use iter() in guard
      // https://github.com/rust-lang/rfcs/issues/1006
      FnCall(ref v1, ref es) if v1.is_func() && (|| es.iter().all(|v| v.is_value()))() => {
        if let box Val(Func(ref name, ref e1, ref xs)) = *v1 {
          self.state.begin_scope();

          // alloc the params
          for (xn, en) in xs.iter().zip(es.iter()) {
            self.state.alloc(xn.to_var()?, en.clone())?;
          }

          // alloc the fn body for named functions
          if let Some(box Var(ref s)) = *name {
            self.state.alloc(s.clone(), *v1.clone())?;
          }

          Scope(e1.clone())
        } else {
          return Err(RuntimeError::UnexpectedExpr("expected Func".to_string(), *v1.clone()))
        }
      },
      Scope(box v @ Val(_)) => {
        self.state.end_scope();
        v
      },
      While(box Val(Bool(b)), e1o, _, e2o, e3) => {
        match b {
          true => While(e1o.clone(), e1o, e2o.clone(), e2o, e3),
          false => *e3,
        }
      },
      Print(box Val(v)) => {
        println!("{}", v);
        Val(Undefined)
      },
      /**
       * Search Cases
       */
      Bop(op, v1 @ box Val(_), box e2) => {
        Bop(
          op,
          v1,
          Box::new(self.step(e2)?)
        )
      },
      Bop(Assign, v1 @ box Var(_), box e2) => {
        Bop(
          Assign,
          v1,
          Box::new(self.step(e2)?)
        )
      },
      Bop(op, box e1, e2) => {
        Bop(op, Box::new(self.step(e1)?), e2)
      },
      Uop(op, box e1) => {
        Uop(op, Box::new(self.step(e1)?))
      },
      Ternary(box e1, e2, e3) => {
        Ternary(Box::new(self.step(e1)?), e2, e3)
      },
      While(box e1, e1o, v @ box Val(_), e2o, e3) => {
        While(Box::new(self.step(e1)?), e1o, v, e2o, e3)
      },
      While(e1, e1o, box e2, e2o, e3) => {
        While(e1, e1o, Box::new(self.step(e2)?), e2o, e3)
      },
      Decl(dt, box addr, box e1, e2) => {
        Decl(dt, Box::new(addr), Box::new(self.step(e1)?), e2)
      },
      FnCall(f @ box Val(Func(_, _, _)), mut args) => {
        // find the first nonvalue arg and call step() on it
        if let Some(index) = args.iter().position(|e| {
          match e {
            &Val(_) => false,
            _ => true,
          }
        }) {
          // temporary placeholder so we can safely move the value
          // this ensures good vec state in between when data is read and rewritten
          let expr = mem::replace(&mut args[index], Val(Undefined));
          args[index] = self.step(expr)?;
        }

        FnCall(f, args)
      },
      FnCall(box e1, args) => {
        FnCall(Box::new(self.step(e1)?), args)
      },
      Scope(box e1) => {
        Scope(Box::new(self.step(e1)?))
      },
      Print(box e1) => {
        Print(Box::new(self.step(e1)?))
      },
    };

    debug!("returning with mem {:?}" , self.state.mem);
    debug!("returning with e {:?}" , e1);
    Ok(e1)
  }

  pub fn eval(&mut self, input: &str) -> Result<Expr> {
    let mut e = parse(input)?;

    let mut num_iterations = 0;

    loop {
      if num_iterations > 1000000000 {
        return Err(RuntimeError::TooManyIterations(num_iterations))
      }

      debug!("-----------------");
      debug!("--- iterating on e {:?} ", e);
      debug!("--- iterating on m {:?} ", self.state.mem);
      num_iterations += 1;
      if e.is_value() {
        debug!("--- iterations: {}", num_iterations);
        return Ok(e.clone());
      } else {
        e = self.step(e.clone())?;
      }
    }
  }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use common::Atom::{Number, Symbol};
use common::Sexp::{self, Atom};

#[derive(Debug, PartialEq)]
enum EvalError{
    EvalError
}

fn eval(s: Sexp, e: &HashMap<String, Sexp>) -> Result<Sexp, EvalError> {
    match s {
        Atom(Number(_)) => {
            Ok(s)
        },
        Atom(Symbol(ref name)) => {
            if let Some(s) = e.get(name) {
                Ok(s.clone())
            } else {
                Err(EvalError::EvalError)
            }
        }
        _ => {
            Err(EvalError::EvalError)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use common::Atom::{Number, Symbol, Nil};
    use common::Sexp::Atom;
    use super::EvalError::EvalError;
    use super::eval;

    #[test]
    fn test_eval_atom_number() {
        let number = 10f64;
        let expected_result = Atom(Number(number));
        let actual_result = eval(Atom(Number(number)), &HashMap::new());
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_number() {
        let num = 10f64;
        let mut env = HashMap::new();
        env.insert("a".to_string(), Atom(Number(num)));
        let expected_result = Atom(Number(num));
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_nil() {
        let mut env = HashMap::new();
        env.insert("a".to_string(), Atom(Nil));
        let expected_result = Atom(Nil);
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_non_value() {
        let env = HashMap::new();
        let expected_result = EvalError;
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.err().unwrap());
    }
}<commit_msg>Eval nil to itself<commit_after>use std::collections::HashMap;
use common::Sexp::{self, Atom};
use common::Atom::{Number, Symbol, Nil};

#[derive(Debug, PartialEq)]
enum EvalError{
    EvalError
}

fn eval(s: Sexp, e: &HashMap<String, Sexp>) -> Result<Sexp, EvalError> {
    match s {
        Atom(Number(_)) | Atom(Nil) => {
            Ok(s)
        },
        Atom(Symbol(ref name)) => {
            if let Some(s) = e.get(name) {
                Ok(s.clone())
            } else {
                Err(EvalError::EvalError)
            }
        }
        _ => {
            Err(EvalError::EvalError)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use common::Atom::{Number, Symbol, Nil};
    use common::Sexp::Atom;
    use super::EvalError::EvalError;
    use super::eval;

    #[test]
    fn test_eval_atom_number() {
        let number = 10f64;
        let expected_result = Atom(Number(number));
        let actual_result = eval(Atom(Number(number)), &HashMap::new());
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_number() {
        let num = 10f64;
        let mut env = HashMap::new();
        env.insert("a".to_string(), Atom(Number(num)));
        let expected_result = Atom(Number(num));
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_nil() {
        let mut env = HashMap::new();
        env.insert("a".to_string(), Atom(Nil));
        let expected_result = Atom(Nil);
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_atom_symbol_to_non_value() {
        let env = HashMap::new();
        let expected_result = EvalError;
        let actual_result = eval(Atom(Symbol("a".to_string())), &env);
        assert_eq!(expected_result, actual_result.err().unwrap());
    }

    #[test]
    fn test_eval_atom_nil_to_itself() {
        let env = HashMap::new();
        let expected_result = Atom(Nil);
        let actual_result = eval(Atom(Nil), &env);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }
}
<|endoftext|>


<commit_before>use serialize::json::{Json, ToJson, Object};
use regex::Regex;
use std::collections::{VecDeque, BTreeMap};

/// The context wrap data you render on your templates.
///
#[derive(Debug)]
pub struct Context {
    data: Json,
    default: Json,
    key_matcher: Regex,
}

#[inline]
fn parse_json_visitor<'a>(path_stack: &mut VecDeque<&'a str>, path: &'a str) {
    for p in (*path).split('/') {
        match p {
            "." | "" => {
                continue;
            }
            ".." => {
                path_stack.pop_back();
            }
            _ => {
                for dot_p in p.split('.') {
//                    if dot_p != "this" {
                        path_stack.push_back(dot_p);
//                    }
                }
            }
        }
    }
}

fn merge_json(base: &Json, addition: &Object) -> Json {
    let mut base_map = match base {
        &Json::Object(ref m) => {
            m.clone()
        }
        _ => {
            let mut map: BTreeMap<String, Json> = BTreeMap::new();
            map.insert("this".to_owned(), base.clone());
            map
        }
    };

    for (k, v) in addition.iter() {
        base_map.insert(k.clone(), v.clone());
    }

    Json::Object(base_map)
}

impl Context {
    /// Create a context with null data
    pub fn null() -> Context {
        Context {
            data: Json::Null,
            default: Json::Null,
            key_matcher: Regex::new(r"\[.+\]$").unwrap(),
        }
    }

    /// Create a context with given data
    pub fn wraps<T: ToJson>(e: &T) -> Context {
        Context {
            data: e.to_json(),
            default: Json::Null,
            key_matcher: Regex::new(r"\[.+\]$").unwrap(),
        }
    }

    /// Extend current context with another JSON object
    /// If current context is a JSON object, it's identical to a normal merge
    /// Otherwise, the current value will be stored in new JSON object with key `this`, and merged
    /// keys are also available.
    pub fn extend(&self, hash: &Object) -> Context {
        let new_data = merge_json(&self.data, hash);
        Context {
            data: new_data,
            default: Json::Null,
            key_matcher: Regex::new(r"\[.+\]$").unwrap()
        }
    }

    /// Navigate the context with base path and relative path
    /// Typically you will set base path to `RenderContext.get_path()`
    /// and set relative path to helper argument or so.
    ///
    /// If you want to navigate from top level, set the base path to `"."`
    pub fn navigate(&self, base_path: &str, relative_path: &str) -> &Json {
        let mut path_stack :VecDeque<&str> = VecDeque::new();
        parse_json_visitor(&mut path_stack, base_path);
        parse_json_visitor(&mut path_stack, relative_path);

        let paths :Vec<&str> = path_stack.iter().map(|x| *x).collect();
        let mut data: &Json = &self.data;
        for p in paths.iter() {
            match self.key_matcher.find(*p) {
                Some((s, _)) => {
                    let arr = &p[..s];
                    let idx = &p[s+1 .. p.len()-1];

                    let root = if arr == "this" || arr == "" {
                        Some(data)
                    } else {
                        data.find(arr)
                    };

                    data = match root {
                        Some(d) => {
                            if let Json::Array(ref l) = *d {
                                match idx.parse::<usize>() {
                                    Ok(idx_u) => l.get(idx_u).unwrap(),
                                    Err(_) => &self.default
                                }
                            } else {
                                &self.default
                            }
                        },
                        None => &self.default
                    };
                },
                None => {
                    data = match data.find(*p) {
                        Some(d) => d,
                        None => {
                            if *p == "this" {
                                data
                            } else {
                                &self.default
                            }
                        }
                    };
                }
            }
        }
        data
    }
}

pub trait JsonRender {
    fn render(&self) -> String;
}

pub trait JsonTruthy {
    fn is_truthy(&self) -> bool;
}

impl JsonRender for Json {
    fn render(&self) -> String {
        if let Json::String(_) = *self {
            self.as_string().unwrap_or("").to_string()
        } else {
            format!("{}", *self)
        }
    }
}

impl JsonTruthy for Json {
    fn is_truthy(&self) -> bool {
        match *self {
            Json::I64(i) => i != 0,
            Json::U64(i) => i != 0,
            Json::F64(i) => i != 0.0 || ! i.is_nan(),
            Json::Boolean (ref i) => *i,
            Json::Null => false,
            Json::String (ref i) => i.len() > 0,
            Json::Array (ref i) => i.len() > 0,
            Json::Object (ref i) => i.len() > 0
        }
    }
}

#[cfg(test)]
mod test {
    use context::{JsonRender, Context};
    use std::collections::BTreeMap;
    use serialize::json::{Json, ToJson};

    #[test]
    fn test_json_render() {
        let raw = "
Hello world
\n Json {
            let mut m = BTreeMap::new();
            m.insert("city".to_string(), self.city.to_json());
            m.insert("country".to_string(), self.country.to_json());
            Json::Object(m)
        }
    }

    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>
    }

    impl ToJson for Person {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("name".to_string(), self.name.to_json());
            m.insert("age".to_string(), self.age.to_json());
            m.insert("addr".to_string(), self.addr.to_json());
            m.insert("titles".to_string(), self.titles.to_json());
            Json::Object(m)
        }
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string());
        assert_eq!(ctx.navigate(".", "this").render(), v.to_string());
    }

    #[test]
    fn test_navigation() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr,
            titles: vec!["programmer".to_string(),
                         "cartographier".to_string()]
        };

        let ctx = Context::wraps(&person);
        assert_eq!(ctx.navigate(".", "./name/../addr/country").render(), "China".to_string());

        let v = true;
        let ctx2 = Context::wraps(&v);
        assert_eq!(ctx2.navigate(".", "this").render(), "true".to_string());

        assert_eq!(ctx.navigate(".", "titles[0]").render(), "programmer".to_string());
        assert_eq!(ctx.navigate(".", "titles.[0]").render(), "programmer".to_string());
    }

    #[test]
    fn test_this() {
        let mut map_with_this = BTreeMap::new();
        map_with_this.insert("this".to_string(), "hello".to_json());
        map_with_this.insert("age".to_string(), 5usize.to_json());
        let ctx1 = Context::wraps(&map_with_this);

        let mut map_without_this = BTreeMap::new();
        map_without_this.insert("age".to_string(), 4usize.to_json());
        let ctx2 = Context::wraps(&map_without_this);

        assert_eq!(ctx1.navigate(".", "this").render(), "hello".to_owned());
        assert_eq!(ctx2.navigate(".", "age").render(), "4".to_owned());
    }

    #[test]
    fn test_extend() {
        let mut map = BTreeMap::new();
        map.insert("age".to_string(), 4usize.to_json());
        let ctx1 = Context::wraps(&map);

        let s = "hello".to_owned();
        let ctx2 = Context::wraps(&s);

        let mut hash = BTreeMap::new();
        hash.insert("tag".to_owned(), "h1".to_json());

        let ctx_a1 = ctx1.extend(&hash);
        assert_eq!(ctx_a1.navigate(".", "age").render(), "4".to_owned());
        assert_eq!(ctx_a1.navigate(".", "tag").render(), "h1".to_owned());

        let ctx_a2 = ctx2.extend(&hash);
        assert_eq!(ctx_a2.navigate(".", "this").render(), "hello".to_owned());
        assert_eq!(ctx_a2.navigate(".", "tag").render(), "h1".to_owned());
    }
}
<commit_msg>(refactor) refactoring<commit_after>use serialize::json::{Json, ToJson, Object};
use regex::Regex;
use std::collections::{VecDeque, BTreeMap};

/// The context wrap data you render on your templates.
///
#[derive(Debug)]
pub struct Context {
    data: Json,
    default: Json,
    key_matcher: Regex,
}

#[inline]
fn parse_json_visitor<'a>(path_stack: &mut VecDeque<&'a str>, path: &'a str) {
    for p in (*path).split('/') {
        match p {
            "." | "" => {
                continue;
            }
            ".." => {
                path_stack.pop_back();
            }
            _ => {
                for dot_p in p.split('.') {
//                    if dot_p != "this" {
                        path_stack.push_back(dot_p);
//                    }
                }
            }
        }
    }
}

fn merge_json(base: &Json, addition: &Object) -> Json {
    let mut base_map = match base {
        &Json::Object(ref m) => {
            m.clone()
        }
        _ => {
            let mut map: BTreeMap<String, Json> = BTreeMap::new();
            map.insert("this".to_owned(), base.clone());
            map
        }
    };

    for (k, v) in addition.iter() {
        base_map.insert(k.clone(), v.clone());
    }

    Json::Object(base_map)
}

impl Context {
    /// Create a context with null data
    pub fn null() -> Context {
        Context {
            data: Json::Null,
            default: Json::Null,
            key_matcher: Regex::new(r"\[.+\]$").unwrap(),
        }
    }

    /// Create a context with given data
    pub fn wraps<T: ToJson>(e: &T) -> Context {
        Context {
            data: e.to_json(),
            default: Json::Null,
            key_matcher: Regex::new(r"\[.+\]$").unwrap(),
        }
    }

    /// Extend current context with another JSON object
    /// If current context is a JSON object, it's identical to a normal merge
    /// Otherwise, the current value will be stored in new JSON object with key `this`, and merged
    /// keys are also available.
    pub fn extend(&self, hash: &Object) -> Context {
        let new_data = merge_json(&self.data, hash);
        Context {
            data: new_data,
            default: Json::Null,
            key_matcher: Regex::new(r"\[.+\]$").unwrap()
        }
    }

    /// Navigate the context with base path and relative path
    /// Typically you will set base path to `RenderContext.get_path()`
    /// and set relative path to helper argument or so.
    ///
    /// If you want to navigate from top level, set the base path to `"."`
    pub fn navigate(&self, base_path: &str, relative_path: &str) -> &Json {
        let mut path_stack :VecDeque<&str> = VecDeque::new();
        parse_json_visitor(&mut path_stack, base_path);
        parse_json_visitor(&mut path_stack, relative_path);

        let paths :Vec<&str> = path_stack.iter().map(|x| *x).collect();
        let mut data: &Json = &self.data;
        for p in paths.iter() {
            match self.key_matcher.find(*p) {
                Some((s, _)) => {
                    let arr = &p[..s];
                    let idx = &p[s+1 .. p.len()-1];

                    let root = if arr == "this" || arr == "" {
                        Some(data)
                    } else {
                        data.find(arr)
                    };

                    data = match root {
                        Some(d) => {
                            match *d {
                                Json::Array(ref l) => {
                                    if let Ok(idx_u) = idx.parse::<usize>() {
                                        l.get(idx_u).unwrap_or(&self.default)
                                    } else {
                                        &self.default
                                    }
                                },
                                _ => {
                                    &self.default
                                }
                            }
                        },
                        None => &self.default
                    };
                },
                None => {
                    data = match data.find(*p) {
                        Some(d) => d,
                        None => {
                            if *p == "this" {
                                data
                            } else {
                                &self.default
                            }
                        }
                    };
                }
            }
        }
        data
    }
}

pub trait JsonRender {
    fn render(&self) -> String;
}

pub trait JsonTruthy {
    fn is_truthy(&self) -> bool;
}

impl JsonRender for Json {
    fn render(&self) -> String {
        if let Json::String(_) = *self {
            self.as_string().unwrap_or("").to_string()
        } else {
            format!("{}", *self)
        }
    }
}

impl JsonTruthy for Json {
    fn is_truthy(&self) -> bool {
        match *self {
            Json::I64(i) => i != 0,
            Json::U64(i) => i != 0,
            Json::F64(i) => i != 0.0 || ! i.is_nan(),
            Json::Boolean (ref i) => *i,
            Json::Null => false,
            Json::String (ref i) => i.len() > 0,
            Json::Array (ref i) => i.len() > 0,
            Json::Object (ref i) => i.len() > 0
        }
    }
}

#[cfg(test)]
mod test {
    use context::{JsonRender, Context};
    use std::collections::BTreeMap;
    use serialize::json::{Json, ToJson};

    #[test]
    fn test_json_render() {
        let raw = "
Hello world
\n Json {
            let mut m = BTreeMap::new();
            m.insert("city".to_string(), self.city.to_json());
            m.insert("country".to_string(), self.country.to_json());
            Json::Object(m)
        }
    }

    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>
    }

    impl ToJson for Person {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("name".to_string(), self.name.to_json());
            m.insert("age".to_string(), self.age.to_json());
            m.insert("addr".to_string(), self.addr.to_json());
            m.insert("titles".to_string(), self.titles.to_json());
            Json::Object(m)
        }
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string());
        assert_eq!(ctx.navigate(".", "this").render(), v.to_string());
    }

    #[test]
    fn test_navigation() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr,
            titles: vec!["programmer".to_string(),
                         "cartographier".to_string()]
        };

        let ctx = Context::wraps(&person);
        assert_eq!(ctx.navigate(".", "./name/../addr/country").render(), "China".to_string());

        let v = true;
        let ctx2 = Context::wraps(&v);
        assert_eq!(ctx2.navigate(".", "this").render(), "true".to_string());

        assert_eq!(ctx.navigate(".", "titles[0]").render(), "programmer".to_string());
        assert_eq!(ctx.navigate(".", "titles.[0]").render(), "programmer".to_string());
    }

    #[test]
    fn test_this() {
        let mut map_with_this = BTreeMap::new();
        map_with_this.insert("this".to_string(), "hello".to_json());
        map_with_this.insert("age".to_string(), 5usize.to_json());
        let ctx1 = Context::wraps(&map_with_this);

        let mut map_without_this = BTreeMap::new();
        map_without_this.insert("age".to_string(), 4usize.to_json());
        let ctx2 = Context::wraps(&map_without_this);

        assert_eq!(ctx1.navigate(".", "this").render(), "hello".to_owned());
        assert_eq!(ctx2.navigate(".", "age").render(), "4".to_owned());
    }

    #[test]
    fn test_extend() {
        let mut map = BTreeMap::new();
        map.insert("age".to_string(), 4usize.to_json());
        let ctx1 = Context::wraps(&map);

        let s = "hello".to_owned();
        let ctx2 = Context::wraps(&s);

        let mut hash = BTreeMap::new();
        hash.insert("tag".to_owned(), "h1".to_json());

        let ctx_a1 = ctx1.extend(&hash);
        assert_eq!(ctx_a1.navigate(".", "age").render(), "4".to_owned());
        assert_eq!(ctx_a1.navigate(".", "tag").render(), "h1".to_owned());

        let ctx_a2 = ctx2.extend(&hash);
        assert_eq!(ctx_a2.navigate(".", "this").render(), "hello".to_owned());
        assert_eq!(ctx_a2.navigate(".", "tag").render(), "h1".to_owned());
    }
}
<|endoftext|>


<commit_before>// Perlin noise benchmark from https://gist.github.com/1170424

use std::float;
use std::rand::{Rng, RngUtil};
use std::rand;

struct Vec2 {
    x: f32,
    y: f32,
}

#[inline(always)]
fn lerp(a: f32, b: f32, v: f32) -> f32 { a * (1.0 - v) + b * v }

#[inline(always)]
fn smooth(v: f32) -> f32 { v * v * (3.0 - 2.0 * v) }

fn random_gradient<R:Rng>(r: &mut R) -> Vec2 {
    let v = 2.0 * float::consts::pi * r.gen();
    Vec2 {
        x: v.cos() as f32,
        y: v.sin() as f32,
    }
}

fn gradient(orig: Vec2, grad: Vec2, p: Vec2) -> f32 {
    let sp = Vec2 {x: p.x - orig.x, y: p.y - orig.y};
    grad.x * sp.x + grad.y + sp.y
}

struct Noise2DContext {
    rgradients: [Vec2, ..256],
    permutations: [int, ..256],
}

impl Noise2DContext {
    pub fn new() -> Noise2DContext {
        let mut r = rand::rng();
        let mut rgradients = [ Vec2 { x: 0.0, y: 0.0 }, ..256 ];
        for i in range(0, 256) {
            rgradients[i] = random_gradient(&mut r);
        }
        let mut permutations = [ 0, ..256 ];
        for i in range(0, 256) {
            permutations[i] = i;
        }
        r.shuffle_mut(permutations);

        Noise2DContext {
            rgradients: rgradients,
            permutations: permutations,
        }
    }

    #[inline(always)]
    pub fn get_gradient(&self, x: int, y: int) -> Vec2 {
        let idx = self.permutations[x & 255] + self.permutations[y & 255];
        self.rgradients[idx & 255]
    }

    #[inline]
    pub fn get_gradients(&self,
                         gradients: &mut [Vec2, ..4],
                         origins: &mut [Vec2, ..4],
                         x: f32,
                         y: f32) {
        let x0f = x.floor();
        let y0f = y.floor();
        let x0 = x0f as int;
        let y0 = y0f as int;
        let x1 = x0 + 1;
        let y1 = y0 + 1;

        gradients[0] = self.get_gradient(x0, y0);
        gradients[1] = self.get_gradient(x1, y0);
        gradients[2] = self.get_gradient(x0, y1);
        gradients[3] = self.get_gradient(x1, y1);

        origins[0] = Vec2 {x: x0f + 0.0, y: y0f + 0.0};
        origins[1] = Vec2 {x: x0f + 1.0, y: y0f + 0.0};
        origins[2] = Vec2 {x: x0f + 0.0, y: y0f + 1.0};
        origins[3] = Vec2 {x: x0f + 1.0, y: y0f + 1.0};
    }

    #[inline]
    pub fn get(&self, x: f32, y: f32) -> f32 {
        let p = Vec2 {x: x, y: y};
        let mut gradients = [ Vec2 { x: 0.0, y: 0.0 }, ..4 ];
        let mut origins = [ Vec2 { x: 0.0, y: 0.0 }, ..4 ];
        self.get_gradients(&mut gradients, &mut origins, x, y);
        let v0 = gradient(origins[0], gradients[0], p);
        let v1 = gradient(origins[1], gradients[1], p);
        let v2 = gradient(origins[2], gradients[2], p);
        let v3 = gradient(origins[3], gradients[3], p);
        let fx = smooth(x - origins[0].x);
        let vx0 = lerp(v0, v1, fx);
        let vx1 = lerp(v2, v3, fx);
        let fy = smooth(y - origins[0].y);
        lerp(vx0, vx1, fy)
    }
}

fn main() {
    let symbols = [" ", "░", "▒", "▓", "█", "█"];
    let mut pixels = [0f32, ..256*256];
    let n2d = ~Noise2DContext::new();
    for _ in range(0, 100u) {
        for y in range(0, 256) {
            for x in range(0, 256) {
                let v = n2d.get(
                    x as f32 * 0.1f32,
                    y as f32 * 0.1f32
                ) * 0.5f32 + 0.5f32;
                pixels[y*256+x] = v;
            };
        };
    };

    for y in range(0, 256) {
        for x in range(0, 256) {
            print(symbols[(pixels[y*256+x] / 0.2f32) as int]);
        }
        println("");
    }
}
<commit_msg>Fix typo in test/bench/noise.rs. Closes #8574.<commit_after>// Perlin noise benchmark from https://gist.github.com/1170424

use std::float;
use std::rand::{Rng, RngUtil};
use std::rand;

struct Vec2 {
    x: f32,
    y: f32,
}

#[inline(always)]
fn lerp(a: f32, b: f32, v: f32) -> f32 { a * (1.0 - v) + b * v }

#[inline(always)]
fn smooth(v: f32) -> f32 { v * v * (3.0 - 2.0 * v) }

fn random_gradient<R:Rng>(r: &mut R) -> Vec2 {
    let v = 2.0 * float::consts::pi * r.gen();
    Vec2 {
        x: v.cos() as f32,
        y: v.sin() as f32,
    }
}

fn gradient(orig: Vec2, grad: Vec2, p: Vec2) -> f32 {
    let sp = Vec2 {x: p.x - orig.x, y: p.y - orig.y};
    grad.x * sp.x + grad.y * sp.y
}

struct Noise2DContext {
    rgradients: [Vec2, ..256],
    permutations: [int, ..256],
}

impl Noise2DContext {
    pub fn new() -> Noise2DContext {
        let mut r = rand::rng();
        let mut rgradients = [ Vec2 { x: 0.0, y: 0.0 }, ..256 ];
        for i in range(0, 256) {
            rgradients[i] = random_gradient(&mut r);
        }
        let mut permutations = [ 0, ..256 ];
        for i in range(0, 256) {
            permutations[i] = i;
        }
        r.shuffle_mut(permutations);

        Noise2DContext {
            rgradients: rgradients,
            permutations: permutations,
        }
    }

    #[inline(always)]
    pub fn get_gradient(&self, x: int, y: int) -> Vec2 {
        let idx = self.permutations[x & 255] + self.permutations[y & 255];
        self.rgradients[idx & 255]
    }

    #[inline]
    pub fn get_gradients(&self,
                         gradients: &mut [Vec2, ..4],
                         origins: &mut [Vec2, ..4],
                         x: f32,
                         y: f32) {
        let x0f = x.floor();
        let y0f = y.floor();
        let x0 = x0f as int;
        let y0 = y0f as int;
        let x1 = x0 + 1;
        let y1 = y0 + 1;

        gradients[0] = self.get_gradient(x0, y0);
        gradients[1] = self.get_gradient(x1, y0);
        gradients[2] = self.get_gradient(x0, y1);
        gradients[3] = self.get_gradient(x1, y1);

        origins[0] = Vec2 {x: x0f + 0.0, y: y0f + 0.0};
        origins[1] = Vec2 {x: x0f + 1.0, y: y0f + 0.0};
        origins[2] = Vec2 {x: x0f + 0.0, y: y0f + 1.0};
        origins[3] = Vec2 {x: x0f + 1.0, y: y0f + 1.0};
    }

    #[inline]
    pub fn get(&self, x: f32, y: f32) -> f32 {
        let p = Vec2 {x: x, y: y};
        let mut gradients = [ Vec2 { x: 0.0, y: 0.0 }, ..4 ];
        let mut origins = [ Vec2 { x: 0.0, y: 0.0 }, ..4 ];
        self.get_gradients(&mut gradients, &mut origins, x, y);
        let v0 = gradient(origins[0], gradients[0], p);
        let v1 = gradient(origins[1], gradients[1], p);
        let v2 = gradient(origins[2], gradients[2], p);
        let v3 = gradient(origins[3], gradients[3], p);
        let fx = smooth(x - origins[0].x);
        let vx0 = lerp(v0, v1, fx);
        let vx1 = lerp(v2, v3, fx);
        let fy = smooth(y - origins[0].y);
        lerp(vx0, vx1, fy)
    }
}

fn main() {
    let symbols = [" ", "░", "▒", "▓", "█", "█"];
    let mut pixels = [0f32, ..256*256];
    let n2d = ~Noise2DContext::new();
    for _ in range(0, 100u) {
        for y in range(0, 256) {
            for x in range(0, 256) {
                let v = n2d.get(
                    x as f32 * 0.1f32,
                    y as f32 * 0.1f32
                ) * 0.5f32 + 0.5f32;
                pixels[y*256+x] = v;
            };
        };
    };

    for y in range(0, 256) {
        for x in range(0, 256) {
            print(symbols[(pixels[y*256+x] / 0.2f32) as int]);
        }
        println("");
    }
}
<|endoftext|>


<commit_before>use types::{OscPacket, OscType, OscResult, OscMessage, OscMidiMessage, OscBundle};
use errors::OscError;
use utils;

use std::{io, char};
use std::io::{Read, BufRead};

use byteorder::{BigEndian, ReadBytesExt};

/// Common MTP size for ethernet
pub const MTP: usize = 1536;

pub fn decode(msg: &[u8]) -> OscResult<OscPacket> {
    match msg[0] as char {
        '/' => {
            decode_message(msg)
        }
        '#' => {
            decode_bundle(msg)
        }
        _ => Err(OscError::BadPacket("Unknown message format.".to_string())),
    }
}

fn decode_message(msg: &[u8]) -> OscResult<OscPacket> {
    let mut cursor: io::Cursor<&[u8]> = io::Cursor::new(msg);

    let addr: String = try!(read_osc_string(&mut cursor));
    let type_tags: String = try!(read_osc_string(&mut cursor));

    if type_tags.len() > 1 {
        let args: Vec<OscType> = try!(read_osc_args(&mut cursor, type_tags));

        Ok(OscPacket::Message(OscMessage {
            addr: addr,
            args: Some(args),
        }))
    } else {
        Ok(OscPacket::Message(OscMessage {
            addr: addr,
            args: None,
        }))
    }
}

fn decode_bundle(msg: &[u8]) -> OscResult<OscPacket> {
    let mut cursor: io::Cursor<&[u8]> = io::Cursor::new(msg);

    let b = try!(read_osc_string(&mut cursor));
    if b != "bundle" {
        return Err(OscError::BadBundle);
    }

    let time_tag = try!(read_time_tag(&mut cursor));
    let mut bundle: Vec<OscPacket> = Vec::new();
    let size: usize = try!(cursor.read_u32::<BigEndian>()
                                 .map_err(OscError::ByteOrderError)) as usize;
    let mut buf: Vec<u8> = Vec::new();
    let cnt: usize = try!(cursor.take(size as u64)
                                .read_to_end(&mut buf)
                                .map_err(OscError::ReadError));
    if cnt == size {
        try!(decode(&mut buf).map(|p| bundle.push(p)));
    } else {
        return Err(OscError::BadBundle);
    }

    Ok(OscPacket::Bundle(OscBundle {
        timetag: time_tag,
        content: bundle,
    }))
}

fn read_osc_string(cursor: &mut io::Cursor<&[u8]>) -> OscResult<String> {
    let mut str_buf: Vec<u8> = Vec::new();
    try!(cursor.read_until(0, &mut str_buf)
               .map_err(OscError::ReadError)); // ignore returned byte count
    pad_cursor(cursor);
    // convert to String and remove nul bytes
    String::from_utf8(str_buf)
        .map_err(|e| OscError::StringError(e))
        .map(|s| {
            s.trim_matches(0u8 as char)
             .to_string()
        })
}

fn read_osc_args(cursor: &mut io::Cursor<&[u8]>, raw_type_tags: String) -> OscResult<Vec<OscType>> {
    let type_tags: Vec<char> = raw_type_tags.chars()
                                            .skip(1)
                                            .map(|c| c as char)
                                            .collect();
    let mut args: Vec<OscType> = Vec::with_capacity(type_tags.len());
    for tag in type_tags {
        let arg: OscType = try!(read_osc_arg(cursor, tag));
        args.push(arg);
    }
    Ok(args)
}

fn read_osc_arg(cursor: &mut io::Cursor<&[u8]>, tag: char) -> OscResult<OscType> {
    match tag {
        'f' => {
            cursor.read_f32::<BigEndian>()
                  .map(OscType::Float)
                  .map_err(OscError::ByteOrderError)
        }
        'd' => {
            cursor.read_f64::<BigEndian>()
                  .map(OscType::Double)
                  .map_err(OscError::ByteOrderError)
        }
        'i' => {
            cursor.read_i32::<BigEndian>()
                  .map(OscType::Int)
                  .map_err(OscError::ByteOrderError)
        }
        'h' => {
            cursor.read_i64::<BigEndian>()
                  .map(OscType::Long)
                  .map_err(OscError::ByteOrderError)
        }
        's' => {
            read_osc_string(cursor).map(|s| OscType::String(s))
        }
        't' => {
            read_time_tag(cursor)
        }
        'b' => {
            let size: usize =
                try!(cursor.read_u32::<BigEndian>()
                           .map_err(OscError::ByteOrderError)) as usize;
            let mut byte_buf: Vec<u8> = Vec::with_capacity(size);
            try!(cursor.take(size as u64)
                       .read_to_end(&mut byte_buf)
                       .map_err(OscError::ReadError));
            pad_cursor(cursor);
            Ok(OscType::Blob(byte_buf))

        }
        'T' => {
            Ok(OscType::Bool(true))
        }
        'F' => {
            Ok(OscType::Bool(false))
        }
        'N' => {
            Ok(OscType::Nil)
        }
        'I' => {
            Ok(OscType::Inf)
        }
        'c' => {
            let opt_char = try!(cursor.read_u32::<BigEndian>()
                                      .map(char::from_u32)
                                      .map_err(OscError::ByteOrderError));
            match opt_char {
                Some(c) => Ok(OscType::Char(c)),
                None => Err(OscError::BadArg("Argument is not a char!".to_string())),
            }
        }
        'm' => {
            read_midi_message(cursor)
        }
        _ => Err(OscError::BadArg(format!("Type tag \"{}\" is not implemented!", tag))),
    }
}

fn read_time_tag(cursor: &mut io::Cursor<&[u8]>) -> OscResult<OscType> {
    let date = try!(cursor.read_u32::<BigEndian>()
                          .map_err(OscError::ByteOrderError));
    let frac = try!(cursor.read_u32::<BigEndian>()
                          .map_err(OscError::ByteOrderError));

    Ok(OscType::Time(date, frac))
}

fn read_midi_message(cursor: &mut io::Cursor<&[u8]>) -> OscResult<OscType> {
    let mut buf: Vec<u8> = Vec::with_capacity(4);
    try!(cursor.take(4).read_to_end(&mut buf).map_err(OscError::ReadError));

    Ok(OscType::Midi(OscMidiMessage {
        port: buf[0],
        status: buf[1],
        data1: buf[2],
        data2: buf[3],
    }))

}

fn pad_cursor(cursor: &mut io::Cursor<&[u8]>) {
    let pos = cursor.position();
    cursor.set_position(utils::pad(pos));
}
<commit_msg>Refactor blob decoding to `read_blob` function<commit_after>use types::{OscPacket, OscType, OscResult, OscMessage, OscMidiMessage, OscBundle};
use errors::OscError;
use utils;

use std::{io, char};
use std::io::{Read, BufRead};

use byteorder::{BigEndian, ReadBytesExt};

/// Common MTP size for ethernet
pub const MTP: usize = 1536;

pub fn decode(msg: &[u8]) -> OscResult<OscPacket> {
    match msg[0] as char {
        '/' => {
            decode_message(msg)
        }
        '#' => {
            decode_bundle(msg)
        }
        _ => Err(OscError::BadPacket("Unknown message format.".to_string())),
    }
}

fn decode_message(msg: &[u8]) -> OscResult<OscPacket> {
    let mut cursor: io::Cursor<&[u8]> = io::Cursor::new(msg);

    let addr: String = try!(read_osc_string(&mut cursor));
    let type_tags: String = try!(read_osc_string(&mut cursor));

    if type_tags.len() > 1 {
        let args: Vec<OscType> = try!(read_osc_args(&mut cursor, type_tags));

        Ok(OscPacket::Message(OscMessage {
            addr: addr,
            args: Some(args),
        }))
    } else {
        Ok(OscPacket::Message(OscMessage {
            addr: addr,
            args: None,
        }))
    }
}

fn decode_bundle(msg: &[u8]) -> OscResult<OscPacket> {
    let mut cursor: io::Cursor<&[u8]> = io::Cursor::new(msg);

    let b = try!(read_osc_string(&mut cursor));
    if b != "bundle" {
        return Err(OscError::BadBundle);
    }

    let time_tag = try!(read_time_tag(&mut cursor));
    let mut bundle: Vec<OscPacket> = Vec::new();
    let size: usize = try!(cursor.read_u32::<BigEndian>()
                                 .map_err(OscError::ByteOrderError)) as usize;
    let mut buf: Vec<u8> = Vec::new();
    let cnt: usize = try!(cursor.take(size as u64)
                                .read_to_end(&mut buf)
                                .map_err(OscError::ReadError));
    if cnt == size {
        try!(decode(&mut buf).map(|p| bundle.push(p)));
    } else {
        return Err(OscError::BadBundle);
    }

    Ok(OscPacket::Bundle(OscBundle {
        timetag: time_tag,
        content: bundle,
    }))
}

fn read_osc_string(cursor: &mut io::Cursor<&[u8]>) -> OscResult<String> {
    let mut str_buf: Vec<u8> = Vec::new();
    try!(cursor.read_until(0, &mut str_buf)
               .map_err(OscError::ReadError)); // ignore returned byte count
    pad_cursor(cursor);
    // convert to String and remove nul bytes
    String::from_utf8(str_buf)
        .map_err(|e| OscError::StringError(e))
        .map(|s| {
            s.trim_matches(0u8 as char)
             .to_string()
        })
}

fn read_osc_args(cursor: &mut io::Cursor<&[u8]>, raw_type_tags: String) -> OscResult<Vec<OscType>> {
    let type_tags: Vec<char> = raw_type_tags.chars()
                                            .skip(1)
                                            .map(|c| c as char)
                                            .collect();
    let mut args: Vec<OscType> = Vec::with_capacity(type_tags.len());
    for tag in type_tags {
        let arg: OscType = try!(read_osc_arg(cursor, tag));
        args.push(arg);
    }
    Ok(args)
}

fn read_osc_arg(cursor: &mut io::Cursor<&[u8]>, tag: char) -> OscResult<OscType> {
    match tag {
        'f' => {
            cursor.read_f32::<BigEndian>()
                  .map(OscType::Float)
                  .map_err(OscError::ByteOrderError)
        }
        'd' => {
            cursor.read_f64::<BigEndian>()
                  .map(OscType::Double)
                  .map_err(OscError::ByteOrderError)
        }
        'i' => {
            cursor.read_i32::<BigEndian>()
                  .map(OscType::Int)
                  .map_err(OscError::ByteOrderError)
        }
        'h' => {
            cursor.read_i64::<BigEndian>()
                  .map(OscType::Long)
                  .map_err(OscError::ByteOrderError)
        }
        's' => {
            read_osc_string(cursor).map(|s| OscType::String(s))
        }
        't' => {
            read_time_tag(cursor)
        }
        'b' => {
            read_blob(cursor)
        }
        'T' => {
            Ok(OscType::Bool(true))
        }
        'F' => {
            Ok(OscType::Bool(false))
        }
        'N' => {
            Ok(OscType::Nil)
        }
        'I' => {
            Ok(OscType::Inf)
        }
        'c' => {
            let opt_char = try!(cursor.read_u32::<BigEndian>()
                                      .map(char::from_u32)
                                      .map_err(OscError::ByteOrderError));
            match opt_char {
                Some(c) => Ok(OscType::Char(c)),
                None => Err(OscError::BadArg("Argument is not a char!".to_string())),
            }
        }
        'm' => {
            read_midi_message(cursor)
        }
        _ => Err(OscError::BadArg(format!("Type tag \"{}\" is not implemented!", tag))),
    }
}

fn read_blob(cursor: &mut io::Cursor<&[u8]>) -> OscResult<OscType> {
    let size: usize = try!(cursor.read_u32::<BigEndian>()
                                 .map_err(OscError::ByteOrderError)) as usize;
    let mut byte_buf: Vec<u8> = Vec::with_capacity(size);

    try!(cursor.take(size as u64)
               .read_to_end(&mut byte_buf)
               .map_err(OscError::ReadError));

    pad_cursor(cursor);

    Ok(OscType::Blob(byte_buf))
}

fn read_time_tag(cursor: &mut io::Cursor<&[u8]>) -> OscResult<OscType> {
    let date = try!(cursor.read_u32::<BigEndian>()
                          .map_err(OscError::ByteOrderError));
    let frac = try!(cursor.read_u32::<BigEndian>()
                          .map_err(OscError::ByteOrderError));

    Ok(OscType::Time(date, frac))
}

fn read_midi_message(cursor: &mut io::Cursor<&[u8]>) -> OscResult<OscType> {
    let mut buf: Vec<u8> = Vec::with_capacity(4);
    try!(cursor.take(4).read_to_end(&mut buf).map_err(OscError::ReadError));

    Ok(OscType::Midi(OscMidiMessage {
        port: buf[0],
        status: buf[1],
        data1: buf[2],
        data2: buf[3],
    }))

}

fn pad_cursor(cursor: &mut io::Cursor<&[u8]>) {
    let pos = cursor.position();
    cursor.set_position(utils::pad(pos));
}
<|endoftext|>