<commit_before>use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::io::{self, Error, ErrorKind};

fn gurobi_home() -> io::Result<String> {
  env::var("GUROBI_HOME")
    .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
    .and_then(|p| if Path::new(p.as_str()).exists() {
      Ok(p)
    } else {
      Err(Error::new(ErrorKind::Other, "".to_owned()))
    })
}

fn append_path(addpath: &str) {
  if let Some(path) = env::var_os("PATH") {
    let mut paths = env::split_paths(&path).collect::<Vec<_>>();
    paths.push(PathBuf::from(addpath));
    let new_path = env::join_paths(paths).unwrap();
    env::set_var("PATH", &new_path);
  }
}

fn get_version_triple() -> (i32, i32, i32) {
  let mut binpath = PathBuf::from(gurobi_home().unwrap());
  binpath.push("bin");
  append_path(binpath.to_str().unwrap());

  let output = Command::new("gurobi_cl").arg("--version").output().expect("failed to execute gurobi_cl");
  let verno: Vec<_> = String::from_utf8(output.stdout)
    .unwrap()
    .split_whitespace()
    .nth(3)
    .unwrap()
    .split(".")
    .map(|s| s.parse().unwrap())
    .collect();

  (verno[0], verno[1], verno[2])
}

fn main() {
  if let Ok(gurobi_home) = gurobi_home() {
    let mut libpath: PathBuf = PathBuf::from(gurobi_home);
    libpath.push("lib");
    let libpath = libpath.to_str().unwrap();

    let (major, minor, _) = get_version_triple();
    let libname = format!("gurobi{}{}", major, minor);

    println!("cargo:rustc-link-search=native={}", libpath);
    println!("cargo:rustc-link-lib={}", libname);
  }
}
<commit_msg>modify gurobi_sys/build.rs<commit_after>use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;

fn gurobi_home() -> String {
  let var = env::var("GUROBI_HOME").expect("failed to retrieve the value of GUROBI_HOME");
  if !Path::new(&var).exists() {
    panic!("GUROBI_HOME is invalid path");
  }
  var
}

fn append_path(addpath: PathBuf) {
  let path = env::var_os("PATH").expect("failed to retrieve the value of PATH");
  let mut paths: Vec<_> = env::split_paths(&path).collect();

  paths.push(addpath);

  let new_path = env::join_paths(paths).unwrap();
  env::set_var("PATH", &new_path);
}

fn get_version_triple() -> (i32, i32, i32) {
  append_path(PathBuf::from(gurobi_home()).join("bin"));

  let output = Command::new("gurobi_cl").arg("--version").output().expect("failed to execute gurobi_cl");
  let verno: Vec<_> = String::from_utf8_lossy(&output.stdout)
    .into_owned()
    .split_whitespace()
    .nth(3)
    .expect("failed to get version string")
    .split(".")
    .map(|s| s.parse().expect("failed to parse version tuple"))
    .collect();

  (verno[0], verno[1], verno[2])
}

fn main() {
  let gurobi_home = gurobi_home();
  let libpath: PathBuf = PathBuf::from(gurobi_home).join("lib");

  let (major, minor, _) = get_version_triple();
  let libname = format!("gurobi{}{}", major, minor);

  println!("cargo:rustc-link-search=native={}", libpath.display());
  println!("cargo:rustc-link-lib={}", libname);
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::Command;

use flate2::read::GzDecoder;
use tar::Archive;

fn main() {
	// TODO: Replace with is_some_with once stabilized
	// https://github.com/rust-lang/rust/issues/93050
	let targets_hermit =
		matches!(env::var_os("CARGO_CFG_TARGET_OS"), Some(os) if os == OsStr::new("hermit"));
	if !targets_hermit {
		return;
	}

	let kernel_src = if has_feature("with_submodule") {
		KernelSrc::from_submodule()
	} else {
		KernelSrc::download()
	};

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn from_submodule() -> Self {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		Self { src_dir }
	}

	fn download() -> Self {
		let version = "0.3.54";
		let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
		let src_dir = out_dir.join(format!("libhermit-rs-{version}"));

		if !src_dir.exists() {
			let url = format!(
				"https://github.com/hermitcore/libhermit-rs/archive/refs/tags/v{version}.tar.gz"
			);
			let response = ureq::get(url.as_str()).call().unwrap().into_reader();
			let tar = GzDecoder::new(response);
			let mut archive = Archive::new(tar);
			archive.unpack(src_dir.parent().unwrap()).unwrap();
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = {
			let mut target_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
			target_dir.push("target");
			target_dir
		};
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let user_target = env::var("TARGET").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");
		let mut cmd = Command::new("cargo");

		let kernel_target = match user_target.as_str() {
			"x86_64-unknown-hermit" => "x86_64-unknown-none-hermitkernel",
			"aarch64-unknown-hermit" => "aarch64-unknown-none-hermitkernel",
			_ => panic!("Unsupported target: {}", user_target),
		};

		cmd.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg(kernel_target)
			.arg("--manifest-path")
			.arg("Cargo.toml");

		cmd.current_dir(&self.src_dir);

		cmd.env_remove("RUSTUP_TOOLCHAIN");
		if option_env!("RUSTC_WORKSPACE_WRAPPER")
			.unwrap_or_default()
			.ends_with("clippy-driver")
		{
			cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
		}

		cmd.env("CARGO_TERM_COLOR", "always");

		cmd.arg("--target-dir").arg(&target_dir);

		if profile == "release" {
			cmd.arg("--release");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			["acpi", "fsgsbase", "pci", "smp", "vga"].into_iter(),
		);

		let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
		let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

		if has_feature("instrument") {
			rustflags.push("-Zinstrument-mcount".to_string());
			// Add outer rustflags to command
			rustflags.push(outer_rustflags);
		} else {
			// If the `instrument` feature feature is not enabled,
			// filter it from outer rustflags before adding them to the command.
			if !outer_rustflags.is_empty() {
				let flags = outer_rustflags
					.split('\x1f')
					.filter(|&flag| !flag.contains("instrument-mcount"))
					.map(String::from);
				rustflags.extend(flags);
			}
		}

		cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(kernel_target)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("Lib location: {}", lib_location.display());

		let lib = lib_location.join("libhermit.a");

		let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

		match kernel_target {
			"x86_64-unknown-none-hermitkernel" => {
				symbols.extend(include_str!("x86_64-duplicate-symbols").lines())
			}
			"aarch64-unknown-none-hermitkernel" => {
				symbols.extend(include_str!("aarch64-duplicate-symbols").lines())
			}
			_ => (),
		}

		// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
		// Consequently, we rename the functions in the libos to avoid collisions.
		rename_symbols(symbols.iter(), &lib);

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.arg("--features");
		cmd.arg(features.join(" "));
	}
}

fn rename_symbols(symbols: impl Iterator<Item = impl AsRef<OsStr>>, lib: &Path) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib)
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}
<commit_msg>Sys: Extract out_dir(), target_dir()<commit_after>use std::borrow::Cow;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::Command;

use flate2::read::GzDecoder;
use tar::Archive;

fn main() {
	// TODO: Replace with is_some_with once stabilized
	// https://github.com/rust-lang/rust/issues/93050
	let targets_hermit =
		matches!(env::var_os("CARGO_CFG_TARGET_OS"), Some(os) if os == OsStr::new("hermit"));
	if !targets_hermit {
		return;
	}

	let kernel_src = if has_feature("with_submodule") {
		KernelSrc::from_submodule()
	} else {
		KernelSrc::download()
	};

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn from_submodule() -> Self {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		Self { src_dir }
	}

	fn download() -> Self {
		let version = "0.3.54";
		let out_dir = out_dir();
		let src_dir = out_dir.join(format!("libhermit-rs-{version}"));

		if !src_dir.exists() {
			let url = format!(
				"https://github.com/hermitcore/libhermit-rs/archive/refs/tags/v{version}.tar.gz"
			);
			let response = ureq::get(url.as_str()).call().unwrap().into_reader();
			let tar = GzDecoder::new(response);
			let mut archive = Archive::new(tar);
			archive.unpack(src_dir.parent().unwrap()).unwrap();
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = target_dir();
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let user_target = env::var("TARGET").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");
		let mut cmd = Command::new("cargo");

		let kernel_target = match user_target.as_str() {
			"x86_64-unknown-hermit" => "x86_64-unknown-none-hermitkernel",
			"aarch64-unknown-hermit" => "aarch64-unknown-none-hermitkernel",
			_ => panic!("Unsupported target: {}", user_target),
		};

		cmd.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg(kernel_target)
			.arg("--manifest-path")
			.arg("Cargo.toml");

		cmd.current_dir(&self.src_dir);

		cmd.env_remove("RUSTUP_TOOLCHAIN");
		if option_env!("RUSTC_WORKSPACE_WRAPPER")
			.unwrap_or_default()
			.ends_with("clippy-driver")
		{
			cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
		}

		cmd.env("CARGO_TERM_COLOR", "always");

		cmd.arg("--target-dir").arg(&target_dir);

		if profile == "release" {
			cmd.arg("--release");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			["acpi", "fsgsbase", "pci", "smp", "vga"].into_iter(),
		);

		let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
		let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

		if has_feature("instrument") {
			rustflags.push("-Zinstrument-mcount".to_string());
			// Add outer rustflags to command
			rustflags.push(outer_rustflags);
		} else {
			// If the `instrument` feature feature is not enabled,
			// filter it from outer rustflags before adding them to the command.
			if !outer_rustflags.is_empty() {
				let flags = outer_rustflags
					.split('\x1f')
					.filter(|&flag| !flag.contains("instrument-mcount"))
					.map(String::from);
				rustflags.extend(flags);
			}
		}

		cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(kernel_target)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("Lib location: {}", lib_location.display());

		let lib = lib_location.join("libhermit.a");

		let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

		match kernel_target {
			"x86_64-unknown-none-hermitkernel" => {
				symbols.extend(include_str!("x86_64-duplicate-symbols").lines())
			}
			"aarch64-unknown-none-hermitkernel" => {
				symbols.extend(include_str!("aarch64-duplicate-symbols").lines())
			}
			_ => (),
		}

		// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
		// Consequently, we rename the functions in the libos to avoid collisions.
		rename_symbols(symbols.iter(), &lib);

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn out_dir() -> PathBuf {
	env::var_os("OUT_DIR").unwrap().into()
}

fn target_dir() -> PathBuf {
	let mut target_dir = out_dir();
	target_dir.push("target");
	target_dir
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.arg("--features");
		cmd.arg(features.join(" "));
	}
}

fn rename_symbols(symbols: impl Iterator<Item = impl AsRef<OsStr>>, lib: &Path) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib)
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}
<|endoftext|>


<commit_before>use beacon_chain::Bootstrapper;
use clap::ArgMatches;
use client::{BeaconChainStartMethod, ClientConfig, Eth2Config};
use eth2_config::{read_from_file, write_to_file};
use rand::{distributions::Alphanumeric, Rng};
use slog::{crit, info, warn, Logger};
use std::fs;
use std::path::{Path, PathBuf};

pub const DEFAULT_DATA_DIR: &str = ".lighthouse";
pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml";
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";

type Result<T> = std::result::Result<T, String>;
type Config = (ClientConfig, Eth2Config);

/// Gets the fully-initialized global client and eth2 configuration objects.
pub fn get_configs(cli_args: &ArgMatches, log: &Logger) -> Result<Config> {
    let mut builder = ConfigBuilder::new(cli_args, log)?;

    match cli_args.subcommand() {
        ("testnet", Some(sub_cmd_args)) => {
            process_testnet_subcommand(&mut builder, sub_cmd_args, log)?
        }
        // No sub-command assumes a resume operation.
        _ => {
            info!(
                log,
                "Resuming from existing datadir";
                "path" => format!("{:?}", builder.data_dir)
            );

            // If no primary subcommand was given, start the beacon chain from an existing
            // database.
            builder.set_beacon_chain_start_method(BeaconChainStartMethod::Resume);

            // Whilst there is no large testnet or mainnet force the user to specify how they want
            // to start a new chain (e.g., from a genesis YAML file, another node, etc).
            if !builder.data_dir.exists() {
                return Err(
                    "No datadir found. To start a new beacon chain, see `testnet --help`. \
                     Use `--datadir` to specify a different directory"
                        .into(),
                );
            }

            // If the `testnet` command was not provided, attempt to load an existing datadir and
            // continue with an existing chain.
            builder.load_from_datadir()?;
        }
    };

    builder.build(cli_args)
}

/// Process the `testnet` CLI subcommand arguments, updating the `builder`.
fn process_testnet_subcommand(
    builder: &mut ConfigBuilder,
    cli_args: &ArgMatches,
    log: &Logger,
) -> Result<()> {
    if cli_args.is_present("random-datadir") {
        builder.set_random_datadir()?;
    }

    if cli_args.is_present("force") {
        builder.clean_datadir()?;
    }

    if let Some(path_string) = cli_args.value_of("eth2-config") {
        let path = path_string
            .parse::<PathBuf>()
            .map_err(|e| format!("Unable to parse eth2-config path: {:?}", e))?;
        builder.load_eth2_config(path)?;
    } else {
        builder.update_spec_from_subcommand(&cli_args)?;
    }

    if let Some(path_string) = cli_args.value_of("client-config") {
        let path = path_string
            .parse::<PathBuf>()
            .map_err(|e| format!("Unable to parse client config path: {:?}", e))?;
        builder.load_client_config(path)?;
    }

    info!(
        log,
        "Creating new datadir";
        "path" => format!("{:?}", builder.data_dir)
    );

    // Start matching on the second subcommand (e.g., `testnet bootstrap ...`)
    match cli_args.subcommand() {
        ("bootstrap", Some(cli_args)) => {
            let server = cli_args
                .value_of("server")
                .ok_or_else(|| "No bootstrap server specified")?;
            let port: Option<u16> = cli_args
                .value_of("libp2p-port")
                .and_then(|s| s.parse::<u16>().ok());

            builder.import_bootstrap_libp2p_address(server, port)?;

            builder.set_beacon_chain_start_method(BeaconChainStartMethod::HttpBootstrap {
                server: server.to_string(),
                port,
            })
        }
        ("recent", Some(cli_args)) => {
            let validator_count = cli_args
                .value_of("validator_count")
                .ok_or_else(|| "No validator_count specified")?
                .parse::<usize>()
                .map_err(|e| format!("Unable to parse validator_count: {:?}", e))?;

            builder.set_beacon_chain_start_method(BeaconChainStartMethod::RecentGenesis {
                validator_count,
            })
        }
        _ => return Err("No testnet method specified. See 'testnet --help'.".into()),
    };

    builder.write_configs_to_new_datadir()?;

    Ok(())
}

/// Allows for building a set of configurations based upon `clap` arguments.
struct ConfigBuilder<'a> {
    log: &'a Logger,
    pub data_dir: PathBuf,
    eth2_config: Eth2Config,
    client_config: ClientConfig,
}

impl<'a> ConfigBuilder<'a> {
    /// Create a new builder with default settings.
    pub fn new(cli_args: &'a ArgMatches, log: &'a Logger) -> Result<Self> {
        // Read the `--datadir` flag.
        //
        // If it's not present, try and find the home directory (`~`) and push the default data
        // directory onto it.
        let data_dir: PathBuf = cli_args
            .value_of("datadir")
            .map(|string| PathBuf::from(string))
            .or_else(|| {
                dirs::home_dir().map(|mut home| {
                    home.push(DEFAULT_DATA_DIR);
                    home
                })
            })
            .ok_or_else(|| "Unable to find a home directory for the datadir".to_string())?;

        Ok(Self {
            log,
            data_dir,
            eth2_config: Eth2Config::minimal(),
            client_config: ClientConfig::default(),
        })
    }

    /// Clears any configuration files that would interfere with writing new configs.
    ///
    /// Moves the following files in `data_dir` into a backup directory:
    ///
    /// - Client config
    /// - Eth2 config
    /// - The entire database directory
    pub fn clean_datadir(&mut self) -> Result<()> {
        let backup_dir = {
            let mut s = String::from("backup_");
            s.push_str(&random_string(6));
            self.data_dir.join(s)
        };

        fs::create_dir_all(&backup_dir)
            .map_err(|e| format!("Unable to create config backup dir: {:?}", e))?;

        let move_to_backup_dir = |path: &Path| -> Result<()> {
            let file_name = path
                .file_name()
                .ok_or_else(|| "Invalid path found during datadir clean (no filename).")?;

            let mut new = path.to_path_buf();
            new.pop();
            new.push(backup_dir.clone());
            new.push(file_name);

            let _ = fs::rename(path, new);

            Ok(())
        };

        move_to_backup_dir(&self.data_dir.join(CLIENT_CONFIG_FILENAME))?;
        move_to_backup_dir(&self.data_dir.join(ETH2_CONFIG_FILENAME))?;

        if let Some(db_path) = self.client_config.db_path() {
            move_to_backup_dir(&db_path)?;
        }

        Ok(())
    }

    /// Sets the method for starting the beacon chain.
    pub fn set_beacon_chain_start_method(&mut self, method: BeaconChainStartMethod) {
        self.client_config.beacon_chain_start_method = method;
    }

    /// Import the libp2p address for `server` into the list of bootnodes in `self`.
    ///
    /// If `port` is `Some`, it is used as the port for the `Multiaddr`. If `port` is `None`,
    /// attempts to connect to the `server` via HTTP and retrieve it's libp2p listen port.
    pub fn import_bootstrap_libp2p_address(
        &mut self,
        server: &str,
        port: Option<u16>,
    ) -> Result<()> {
        let bootstrapper = Bootstrapper::from_server_string(server.to_string())?;

        if let Some(server_multiaddr) = bootstrapper.best_effort_multiaddr(port) {
            info!(
                self.log,
                "Estimated bootstrapper libp2p address";
                "multiaddr" => format!("{:?}", server_multiaddr)
            );

            self.client_config
                .network
                .libp2p_nodes
                .push(server_multiaddr);
        } else {
            warn!(
                self.log,
                "Unable to estimate a bootstrapper libp2p address, this node may not find any peers."
            );
        };

        Ok(())
    }

    /// Set the config data_dir to be an random directory.
    ///
    /// Useful for easily spinning up ephemeral testnets.
    pub fn set_random_datadir(&mut self) -> Result<()> {
        let mut s = DEFAULT_DATA_DIR.to_string();
        s.push_str("_random_");
        s.push_str(&random_string(6));

        self.data_dir.pop();
        self.data_dir.push(s);

        Ok(())
    }

    /// Reads the subcommand and tries to update `self.eth2_config` based up on the `--spec` flag.
    ///
    /// Returns an error if the `--spec` flag is not present in the given `cli_args`.
    pub fn update_spec_from_subcommand(&mut self, cli_args: &ArgMatches) -> Result<()> {
        // Re-initialise the `Eth2Config`.
        //
        // If a CLI parameter is set, overwrite any config file present.
        // If a parameter is not set, use either the config file present or default to minimal.
        let eth2_config = match cli_args.value_of("spec") {
            Some("mainnet") => Eth2Config::mainnet(),
            Some("minimal") => Eth2Config::minimal(),
            Some("interop") => Eth2Config::interop(),
            _ => return Err("Unable to determine specification type.".into()),
        };

        self.client_config.spec_constants = cli_args
            .value_of("spec")
            .expect("Guarded by prior match statement")
            .to_string();
        self.eth2_config = eth2_config;

        Ok(())
    }

    /// Writes the configs in `self` to `self.data_dir`.
    ///
    /// Returns an error if `self.data_dir` already exists.
    pub fn write_configs_to_new_datadir(&mut self) -> Result<()> {
        let db_exists = self
            .client_config
            .db_path()
            .map(|d| d.exists())
            .unwrap_or_else(|| false);

        // Do not permit creating a new config when the datadir exists.
        if db_exists {
            return Err("Database already exists. See `-f` in `testnet --help`".into());
        }

        // Create `datadir` and any non-existing parent directories.
        fs::create_dir_all(&self.data_dir).map_err(|e| {
            crit!(self.log, "Failed to initialize data dir"; "error" => format!("{}", e));
            format!("{}", e)
        })?;

        let client_config_file = self.data_dir.join(CLIENT_CONFIG_FILENAME);
        if client_config_file.exists() {
            return Err(format!(
                "Datadir is not clean, {} exists. See `-f` in `testnet --help`.",
                CLIENT_CONFIG_FILENAME
            ));
        } else {
            // Write the onfig to a TOML file in the datadir.
            write_to_file(
                self.data_dir.join(CLIENT_CONFIG_FILENAME),
                &self.client_config,
            )
            .map_err(|e| format!("Unable to write {} file: {:?}", CLIENT_CONFIG_FILENAME, e))?;
        }

        let eth2_config_file = self.data_dir.join(ETH2_CONFIG_FILENAME);
        if eth2_config_file.exists() {
            return Err(format!(
                "Datadir is not clean, {} exists. See `-f` in `testnet --help`.",
                ETH2_CONFIG_FILENAME
            ));
        } else {
            // Write the config to a TOML file in the datadir.
            write_to_file(self.data_dir.join(ETH2_CONFIG_FILENAME), &self.eth2_config)
                .map_err(|e| format!("Unable to write {} file: {:?}", ETH2_CONFIG_FILENAME, e))?;
        }

        Ok(())
    }

    /// Attempts to load the client and eth2 configs from `self.data_dir`.
    ///
    /// Returns an error if any files are not found or are invalid.
    pub fn load_from_datadir(&mut self) -> Result<()> {
        // Check to ensure the datadir exists.
        //
        // For now we return an error. In the future we may decide to boot a default (e.g.,
        // public testnet or mainnet).
        if !self.data_dir.exists() {
            return Err(
                "No datadir found. Either create a new testnet or specify a different `--datadir`."
                    .into(),
            );
        }

        // If there is a path to a databse in the config, ensure it exists.
        if !self
            .client_config
            .db_path()
            .map(|path| path.exists())
            .unwrap_or_else(|| true)
        {
            return Err(
                "No database found in datadir. Use 'testnet -f' to overwrite the existing \
                 datadir, or specify a different `--datadir`."
                    .into(),
            );
        }

        self.load_eth2_config(self.data_dir.join(ETH2_CONFIG_FILENAME))?;
        self.load_client_config(self.data_dir.join(CLIENT_CONFIG_FILENAME))?;

        Ok(())
    }

    /// Attempts to load the client config from `path`.
    ///
    /// Returns an error if any files are not found or are invalid.
    pub fn load_client_config(&mut self, path: PathBuf) -> Result<()> {
        self.client_config = read_from_file::<ClientConfig>(path.clone())
            .map_err(|e| format!("Unable to parse {:?} file: {:?}", path, e))?
            .ok_or_else(|| format!("{:?} file does not exist", path))?;

        Ok(())
    }

    /// Attempts to load the eth2 config from `path`.
    ///
    /// Returns an error if any files are not found or are invalid.
    pub fn load_eth2_config(&mut self, path: PathBuf) -> Result<()> {
        self.eth2_config = read_from_file::<Eth2Config>(path.clone())
            .map_err(|e| format!("Unable to parse {:?} file: {:?}", path, e))?
            .ok_or_else(|| format!("{:?} file does not exist", path))?;

        Ok(())
    }

    /// Consumes self, returning the configs.
    ///
    /// The supplied `cli_args` should be the base-level `clap` cli_args (i.e., not a subcommand
    /// cli_args).
    pub fn build(mut self, cli_args: &ArgMatches) -> Result<Config> {
        self.eth2_config.apply_cli_args(cli_args)?;
        self.client_config
            .apply_cli_args(cli_args, &mut self.log.clone())?;

        if self.eth2_config.spec_constants != self.client_config.spec_constants {
            crit!(self.log, "Specification constants do not match.";
                  "client_config" => format!("{}", self.client_config.spec_constants),
                  "eth2_config" => format!("{}", self.eth2_config.spec_constants)
            );
            return Err("Specification constant mismatch".into());
        }

        self.client_config.data_dir = self.data_dir;

        Ok((self.client_config, self.eth2_config))
    }
}

fn random_string(len: usize) -> String {
    rand::thread_rng()
        .sample_iter(&Alphanumeric)
        .take(len)
        .collect::<String>()
}
<commit_msg>Clean datadir after config files have been loaded<commit_after>use beacon_chain::Bootstrapper;
use clap::ArgMatches;
use client::{BeaconChainStartMethod, ClientConfig, Eth2Config};
use eth2_config::{read_from_file, write_to_file};
use rand::{distributions::Alphanumeric, Rng};
use slog::{crit, info, warn, Logger};
use std::fs;
use std::path::{Path, PathBuf};

pub const DEFAULT_DATA_DIR: &str = ".lighthouse";
pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml";
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";

type Result<T> = std::result::Result<T, String>;
type Config = (ClientConfig, Eth2Config);

/// Gets the fully-initialized global client and eth2 configuration objects.
pub fn get_configs(cli_args: &ArgMatches, log: &Logger) -> Result<Config> {
    let mut builder = ConfigBuilder::new(cli_args, log)?;

    match cli_args.subcommand() {
        ("testnet", Some(sub_cmd_args)) => {
            process_testnet_subcommand(&mut builder, sub_cmd_args, log)?
        }
        // No sub-command assumes a resume operation.
        _ => {
            info!(
                log,
                "Resuming from existing datadir";
                "path" => format!("{:?}", builder.data_dir)
            );

            // If no primary subcommand was given, start the beacon chain from an existing
            // database.
            builder.set_beacon_chain_start_method(BeaconChainStartMethod::Resume);

            // Whilst there is no large testnet or mainnet force the user to specify how they want
            // to start a new chain (e.g., from a genesis YAML file, another node, etc).
            if !builder.data_dir.exists() {
                return Err(
                    "No datadir found. To start a new beacon chain, see `testnet --help`. \
                     Use `--datadir` to specify a different directory"
                        .into(),
                );
            }

            // If the `testnet` command was not provided, attempt to load an existing datadir and
            // continue with an existing chain.
            builder.load_from_datadir()?;
        }
    };

    builder.build(cli_args)
}

/// Process the `testnet` CLI subcommand arguments, updating the `builder`.
fn process_testnet_subcommand(
    builder: &mut ConfigBuilder,
    cli_args: &ArgMatches,
    log: &Logger,
) -> Result<()> {
    if cli_args.is_present("random-datadir") {
        builder.set_random_datadir()?;
    }

    if let Some(path_string) = cli_args.value_of("eth2-config") {
        let path = path_string
            .parse::<PathBuf>()
            .map_err(|e| format!("Unable to parse eth2-config path: {:?}", e))?;
        builder.load_eth2_config(path)?;
    } else {
        builder.update_spec_from_subcommand(&cli_args)?;
    }

    if let Some(path_string) = cli_args.value_of("client-config") {
        let path = path_string
            .parse::<PathBuf>()
            .map_err(|e| format!("Unable to parse client config path: {:?}", e))?;
        builder.load_client_config(path)?;
    }

    if cli_args.is_present("force") {
        builder.clean_datadir()?;
    }

    info!(
        log,
        "Creating new datadir";
        "path" => format!("{:?}", builder.data_dir)
    );

    // Start matching on the second subcommand (e.g., `testnet bootstrap ...`)
    match cli_args.subcommand() {
        ("bootstrap", Some(cli_args)) => {
            let server = cli_args
                .value_of("server")
                .ok_or_else(|| "No bootstrap server specified")?;
            let port: Option<u16> = cli_args
                .value_of("libp2p-port")
                .and_then(|s| s.parse::<u16>().ok());

            builder.import_bootstrap_libp2p_address(server, port)?;

            builder.set_beacon_chain_start_method(BeaconChainStartMethod::HttpBootstrap {
                server: server.to_string(),
                port,
            })
        }
        ("recent", Some(cli_args)) => {
            let validator_count = cli_args
                .value_of("validator_count")
                .ok_or_else(|| "No validator_count specified")?
                .parse::<usize>()
                .map_err(|e| format!("Unable to parse validator_count: {:?}", e))?;

            builder.set_beacon_chain_start_method(BeaconChainStartMethod::RecentGenesis {
                validator_count,
            })
        }
        _ => return Err("No testnet method specified. See 'testnet --help'.".into()),
    };

    builder.write_configs_to_new_datadir()?;

    Ok(())
}

/// Allows for building a set of configurations based upon `clap` arguments.
struct ConfigBuilder<'a> {
    log: &'a Logger,
    pub data_dir: PathBuf,
    eth2_config: Eth2Config,
    client_config: ClientConfig,
}

impl<'a> ConfigBuilder<'a> {
    /// Create a new builder with default settings.
    pub fn new(cli_args: &'a ArgMatches, log: &'a Logger) -> Result<Self> {
        // Read the `--datadir` flag.
        //
        // If it's not present, try and find the home directory (`~`) and push the default data
        // directory onto it.
        let data_dir: PathBuf = cli_args
            .value_of("datadir")
            .map(|string| PathBuf::from(string))
            .or_else(|| {
                dirs::home_dir().map(|mut home| {
                    home.push(DEFAULT_DATA_DIR);
                    home
                })
            })
            .ok_or_else(|| "Unable to find a home directory for the datadir".to_string())?;

        Ok(Self {
            log,
            data_dir,
            eth2_config: Eth2Config::minimal(),
            client_config: ClientConfig::default(),
        })
    }

    /// Clears any configuration files that would interfere with writing new configs.
    ///
    /// Moves the following files in `data_dir` into a backup directory:
    ///
    /// - Client config
    /// - Eth2 config
    /// - The entire database directory
    pub fn clean_datadir(&mut self) -> Result<()> {
        let backup_dir = {
            let mut s = String::from("backup_");
            s.push_str(&random_string(6));
            self.data_dir.join(s)
        };

        fs::create_dir_all(&backup_dir)
            .map_err(|e| format!("Unable to create config backup dir: {:?}", e))?;

        let move_to_backup_dir = |path: &Path| -> Result<()> {
            let file_name = path
                .file_name()
                .ok_or_else(|| "Invalid path found during datadir clean (no filename).")?;

            let mut new = path.to_path_buf();
            new.pop();
            new.push(backup_dir.clone());
            new.push(file_name);

            let _ = fs::rename(path, new);

            Ok(())
        };

        move_to_backup_dir(&self.data_dir.join(CLIENT_CONFIG_FILENAME))?;
        move_to_backup_dir(&self.data_dir.join(ETH2_CONFIG_FILENAME))?;

        if let Some(db_path) = self.client_config.db_path() {
            move_to_backup_dir(&db_path)?;
        }

        Ok(())
    }

    /// Sets the method for starting the beacon chain.
    pub fn set_beacon_chain_start_method(&mut self, method: BeaconChainStartMethod) {
        self.client_config.beacon_chain_start_method = method;
    }

    /// Import the libp2p address for `server` into the list of bootnodes in `self`.
    ///
    /// If `port` is `Some`, it is used as the port for the `Multiaddr`. If `port` is `None`,
    /// attempts to connect to the `server` via HTTP and retrieve it's libp2p listen port.
    pub fn import_bootstrap_libp2p_address(
        &mut self,
        server: &str,
        port: Option<u16>,
    ) -> Result<()> {
        let bootstrapper = Bootstrapper::from_server_string(server.to_string())?;

        if let Some(server_multiaddr) = bootstrapper.best_effort_multiaddr(port) {
            info!(
                self.log,
                "Estimated bootstrapper libp2p address";
                "multiaddr" => format!("{:?}", server_multiaddr)
            );

            self.client_config
                .network
                .libp2p_nodes
                .push(server_multiaddr);
        } else {
            warn!(
                self.log,
                "Unable to estimate a bootstrapper libp2p address, this node may not find any peers."
            );
        };

        Ok(())
    }

    /// Set the config data_dir to be an random directory.
    ///
    /// Useful for easily spinning up ephemeral testnets.
    pub fn set_random_datadir(&mut self) -> Result<()> {
        let mut s = DEFAULT_DATA_DIR.to_string();
        s.push_str("_random_");
        s.push_str(&random_string(6));

        self.data_dir.pop();
        self.data_dir.push(s);

        Ok(())
    }

    /// Reads the subcommand and tries to update `self.eth2_config` based up on the `--spec` flag.
    ///
    /// Returns an error if the `--spec` flag is not present in the given `cli_args`.
    pub fn update_spec_from_subcommand(&mut self, cli_args: &ArgMatches) -> Result<()> {
        // Re-initialise the `Eth2Config`.
        //
        // If a CLI parameter is set, overwrite any config file present.
        // If a parameter is not set, use either the config file present or default to minimal.
        let eth2_config = match cli_args.value_of("spec") {
            Some("mainnet") => Eth2Config::mainnet(),
            Some("minimal") => Eth2Config::minimal(),
            Some("interop") => Eth2Config::interop(),
            _ => return Err("Unable to determine specification type.".into()),
        };

        self.client_config.spec_constants = cli_args
            .value_of("spec")
            .expect("Guarded by prior match statement")
            .to_string();
        self.eth2_config = eth2_config;

        Ok(())
    }

    /// Writes the configs in `self` to `self.data_dir`.
    ///
    /// Returns an error if `self.data_dir` already exists.
    pub fn write_configs_to_new_datadir(&mut self) -> Result<()> {
        let db_exists = self
            .client_config
            .db_path()
            .map(|d| d.exists())
            .unwrap_or_else(|| false);

        // Do not permit creating a new config when the datadir exists.
        if db_exists {
            return Err("Database already exists. See `-f` in `testnet --help`".into());
        }

        // Create `datadir` and any non-existing parent directories.
        fs::create_dir_all(&self.data_dir).map_err(|e| {
            crit!(self.log, "Failed to initialize data dir"; "error" => format!("{}", e));
            format!("{}", e)
        })?;

        let client_config_file = self.data_dir.join(CLIENT_CONFIG_FILENAME);
        if client_config_file.exists() {
            return Err(format!(
                "Datadir is not clean, {} exists. See `-f` in `testnet --help`.",
                CLIENT_CONFIG_FILENAME
            ));
        } else {
            // Write the onfig to a TOML file in the datadir.
            write_to_file(
                self.data_dir.join(CLIENT_CONFIG_FILENAME),
                &self.client_config,
            )
            .map_err(|e| format!("Unable to write {} file: {:?}", CLIENT_CONFIG_FILENAME, e))?;
        }

        let eth2_config_file = self.data_dir.join(ETH2_CONFIG_FILENAME);
        if eth2_config_file.exists() {
            return Err(format!(
                "Datadir is not clean, {} exists. See `-f` in `testnet --help`.",
                ETH2_CONFIG_FILENAME
            ));
        } else {
            // Write the config to a TOML file in the datadir.
            write_to_file(self.data_dir.join(ETH2_CONFIG_FILENAME), &self.eth2_config)
                .map_err(|e| format!("Unable to write {} file: {:?}", ETH2_CONFIG_FILENAME, e))?;
        }

        Ok(())
    }

    /// Attempts to load the client and eth2 configs from `self.data_dir`.
    ///
    /// Returns an error if any files are not found or are invalid.
    pub fn load_from_datadir(&mut self) -> Result<()> {
        // Check to ensure the datadir exists.
        //
        // For now we return an error. In the future we may decide to boot a default (e.g.,
        // public testnet or mainnet).
        if !self.data_dir.exists() {
            return Err(
                "No datadir found. Either create a new testnet or specify a different `--datadir`."
                    .into(),
            );
        }

        // If there is a path to a databse in the config, ensure it exists.
        if !self
            .client_config
            .db_path()
            .map(|path| path.exists())
            .unwrap_or_else(|| true)
        {
            return Err(
                "No database found in datadir. Use 'testnet -f' to overwrite the existing \
                 datadir, or specify a different `--datadir`."
                    .into(),
            );
        }

        self.load_eth2_config(self.data_dir.join(ETH2_CONFIG_FILENAME))?;
        self.load_client_config(self.data_dir.join(CLIENT_CONFIG_FILENAME))?;

        Ok(())
    }

    /// Attempts to load the client config from `path`.
    ///
    /// Returns an error if any files are not found or are invalid.
    pub fn load_client_config(&mut self, path: PathBuf) -> Result<()> {
        self.client_config = read_from_file::<ClientConfig>(path.clone())
            .map_err(|e| format!("Unable to parse {:?} file: {:?}", path, e))?
            .ok_or_else(|| format!("{:?} file does not exist", path))?;

        Ok(())
    }

    /// Attempts to load the eth2 config from `path`.
    ///
    /// Returns an error if any files are not found or are invalid.
    pub fn load_eth2_config(&mut self, path: PathBuf) -> Result<()> {
        self.eth2_config = read_from_file::<Eth2Config>(path.clone())
            .map_err(|e| format!("Unable to parse {:?} file: {:?}", path, e))?
            .ok_or_else(|| format!("{:?} file does not exist", path))?;

        Ok(())
    }

    /// Consumes self, returning the configs.
    ///
    /// The supplied `cli_args` should be the base-level `clap` cli_args (i.e., not a subcommand
    /// cli_args).
    pub fn build(mut self, cli_args: &ArgMatches) -> Result<Config> {
        self.eth2_config.apply_cli_args(cli_args)?;
        self.client_config
            .apply_cli_args(cli_args, &mut self.log.clone())?;

        if self.eth2_config.spec_constants != self.client_config.spec_constants {
            crit!(self.log, "Specification constants do not match.";
                  "client_config" => format!("{}", self.client_config.spec_constants),
                  "eth2_config" => format!("{}", self.eth2_config.spec_constants)
            );
            return Err("Specification constant mismatch".into());
        }

        self.client_config.data_dir = self.data_dir;

        Ok((self.client_config, self.eth2_config))
    }
}

fn random_string(len: usize) -> String {
    rand::thread_rng()
        .sample_iter(&Alphanumeric)
        .take(len)
        .collect::<String>()
}
<|endoftext|>


<commit_before>use futures::task::{ArcWake, Context, Poll, Waker};
use futures::Stream;
use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap};
use std::pin::Pin;
use std::sync::atomic;
use std::sync::{Arc, Mutex};

struct QueueInner<S, K> {
    ready_queue: BinaryHeap<PriorityStream<S, K>>,
    pending_streams: HashMap<usize, PriorityStream<S, K>>,
    waker: Option<Waker>,
}

pub struct FairQueue<S, K> {
    counter: atomic::AtomicUsize,
    inner: Arc<Mutex<QueueInner<S, K>>>,
}

struct StreamWaker<S, K> {
    inner: Arc<Mutex<QueueInner<S, K>>>,
    index: usize,
}

impl<S, K> ArcWake for StreamWaker<S, K>
where
    S: Send,
    K: Send,
{
    fn wake_by_ref(arc_self: &Arc<Self>) {
        let mut inner = arc_self.inner.lock().unwrap();
        let s = inner
            .pending_streams
            .remove(&arc_self.index)
            .expect("Corrupted index given to waker");
        inner.ready_queue.push(s);
        if let Some(waker) = inner.waker.take() {
            waker.wake_by_ref();
        }
    }
}

struct PriorityStream<S, K> {
    priority: usize,
    key: K,
    stream: Pin<Box<S>>,
}

impl<S, K> PartialEq for PriorityStream<S, K> {
    fn eq(&self, other: &Self) -> bool {
        self.priority.eq(&other.priority)
    }
}
impl<S, K> Eq for PriorityStream<S, K> {}

impl<S, K> PartialOrd for PriorityStream<S, K> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        other.priority.partial_cmp(&self.priority)
    }
}
impl<S, K> Ord for PriorityStream<S, K> {
    fn cmp(&self, other: &Self) -> Ordering {
        other.priority.cmp(&self.priority)
    }
}

impl<S, T, K> Stream for FairQueue<S, K>
where
    T: Send,
    S: Stream<Item = T> + Send,
    K: Unpin + Clone + Send,
{
    type Item = (K, T);

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let stream = self.get_mut();
        loop {
            let mut s = {
                let mut inner = stream.inner.lock().unwrap();
                inner.waker = Some(cx.waker().clone());
                match inner.ready_queue.pop() {
                    Some(s) => s,
                    None => {
                        return if inner.pending_streams.len() > 0 {
                            Poll::Pending
                        } else {
                            Poll::Ready(None)
                        }
                    }
                }
            };

            let waker = Arc::new(StreamWaker {
                inner: stream.inner.clone(),
                index: s.priority,
            });
            let waker_ref = futures::task::waker_ref(&waker);
            let mut cx = Context::from_waker(&waker_ref);
            match s.stream.as_mut().poll_next(&mut cx) {
                Poll::Ready(Some(res)) => {
                    s.priority = stream.counter.fetch_add(1, atomic::Ordering::Relaxed);
                    let item = Some((s.key.clone(), res));
                    stream.inner.lock().unwrap().ready_queue.push(s);
                    return Poll::Ready(item);
                }
                Poll::Ready(None) => continue,
                Poll::Pending => {
                    stream.inner.lock().unwrap().pending_streams.insert(s.priority, s);
                    return Poll::Pending;
                }
            }
        }
    }
}

impl<S, K> FairQueue<S, K> {
    pub fn new() -> Self {
        Self {
            counter: atomic::AtomicUsize::new(0),
            inner: Arc::new(Mutex::new(QueueInner {
                ready_queue: BinaryHeap::new(),
                pending_streams: HashMap::new(),
                waker: None,
            })),
        }
    }

    pub fn insert(&mut self, k: K, s: S) {
        let mut inner = self.inner.lock().unwrap();
        inner.ready_queue.push(PriorityStream {
            priority: self.counter.fetch_add(1, atomic::Ordering::Relaxed),
            key: k,
            stream: Box::pin(s),
        });
        match &inner.waker {
            Some(w) => w.wake_by_ref(),
            None => (),
        };
    }
}

#[cfg(test)]
mod test {
    use crate::fair_queue::FairQueue;
    use futures::StreamExt;

    #[tokio::test]
    async fn test_fair_queue_ready() {
        let a = futures::stream::iter(vec!["a1", "a2", "a3"]);
        let b = futures::stream::iter(vec!["b1", "b2", "b3"]);
        let c = futures::stream::iter(vec!["c1", "c2", "c3"]);

        let mut f_queue: FairQueue<_, u64> = FairQueue::new();
        f_queue.insert(1, a);
        f_queue.insert(2, b);
        f_queue.insert(3, c);

        let mut results = Vec::new();
        while let Some(i) = f_queue.next().await {
            results.push(i);
        }
        assert_eq!(
            results,
            vec![
                (1, "a1"),
                (2, "b1"),
                (3, "c1"),
                (1, "a2"),
                (2, "b2"),
                (3, "c2"),
                (1, "a3"),
                (2, "b3"),
                (3, "c3")
            ]
        );
    }

    #[tokio::test]
    async fn test_fair_queue_different_size() {
        let a = futures::stream::iter(vec!["a1", "a2", "a3"]);
        let b = futures::stream::iter(vec!["b1"]);
        let c = futures::stream::iter(vec!["c1", "c2"]);

        let mut f_queue: FairQueue<_, u64> = FairQueue::new();
        f_queue.insert(1, a);
        f_queue.insert(2, b);
        f_queue.insert(3, c);

        let mut results = Vec::new();
        while let Some(i) = f_queue.next().await {
            results.push(i);
        }
        assert_eq!(
            results,
            vec![
                (1, "a1"),
                (2, "b1"),
                (3, "c1"),
                (1, "a2"),
                (3, "c2"),
                (1, "a3")
            ]
        );
    }
}
<commit_msg>Formatting<commit_after>use futures::task::{ArcWake, Context, Poll, Waker};
use futures::Stream;
use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap};
use std::pin::Pin;
use std::sync::atomic;
use std::sync::{Arc, Mutex};

struct QueueInner<S, K> {
    ready_queue: BinaryHeap<PriorityStream<S, K>>,
    pending_streams: HashMap<usize, PriorityStream<S, K>>,
    waker: Option<Waker>,
}

pub struct FairQueue<S, K> {
    counter: atomic::AtomicUsize,
    inner: Arc<Mutex<QueueInner<S, K>>>,
}

struct StreamWaker<S, K> {
    inner: Arc<Mutex<QueueInner<S, K>>>,
    index: usize,
}

impl<S, K> ArcWake for StreamWaker<S, K>
where
    S: Send,
    K: Send,
{
    fn wake_by_ref(arc_self: &Arc<Self>) {
        let mut inner = arc_self.inner.lock().unwrap();
        let s = inner
            .pending_streams
            .remove(&arc_self.index)
            .expect("Corrupted index given to waker");
        inner.ready_queue.push(s);
        if let Some(waker) = inner.waker.take() {
            waker.wake_by_ref();
        }
    }
}

struct PriorityStream<S, K> {
    priority: usize,
    key: K,
    stream: Pin<Box<S>>,
}

impl<S, K> PartialEq for PriorityStream<S, K> {
    fn eq(&self, other: &Self) -> bool {
        self.priority.eq(&other.priority)
    }
}
impl<S, K> Eq for PriorityStream<S, K> {}

impl<S, K> PartialOrd for PriorityStream<S, K> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        other.priority.partial_cmp(&self.priority)
    }
}
impl<S, K> Ord for PriorityStream<S, K> {
    fn cmp(&self, other: &Self) -> Ordering {
        other.priority.cmp(&self.priority)
    }
}

impl<S, T, K> Stream for FairQueue<S, K>
where
    T: Send,
    S: Stream<Item = T> + Send,
    K: Unpin + Clone + Send,
{
    type Item = (K, T);

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let stream = self.get_mut();
        loop {
            let mut s = {
                let mut inner = stream.inner.lock().unwrap();
                inner.waker = Some(cx.waker().clone());
                match inner.ready_queue.pop() {
                    Some(s) => s,
                    None => {
                        return if inner.pending_streams.len() > 0 {
                            Poll::Pending
                        } else {
                            Poll::Ready(None)
                        }
                    }
                }
            };

            let waker = Arc::new(StreamWaker {
                inner: stream.inner.clone(),
                index: s.priority,
            });
            let waker_ref = futures::task::waker_ref(&waker);
            let mut cx = Context::from_waker(&waker_ref);
            match s.stream.as_mut().poll_next(&mut cx) {
                Poll::Ready(Some(res)) => {
                    s.priority = stream.counter.fetch_add(1, atomic::Ordering::Relaxed);
                    let item = Some((s.key.clone(), res));
                    stream.inner.lock().unwrap().ready_queue.push(s);
                    return Poll::Ready(item);
                }
                Poll::Ready(None) => continue,
                Poll::Pending => {
                    stream
                        .inner
                        .lock()
                        .unwrap()
                        .pending_streams
                        .insert(s.priority, s);
                    return Poll::Pending;
                }
            }
        }
    }
}

impl<S, K> FairQueue<S, K> {
    pub fn new() -> Self {
        Self {
            counter: atomic::AtomicUsize::new(0),
            inner: Arc::new(Mutex::new(QueueInner {
                ready_queue: BinaryHeap::new(),
                pending_streams: HashMap::new(),
                waker: None,
            })),
        }
    }

    pub fn insert(&mut self, k: K, s: S) {
        let mut inner = self.inner.lock().unwrap();
        inner.ready_queue.push(PriorityStream {
            priority: self.counter.fetch_add(1, atomic::Ordering::Relaxed),
            key: k,
            stream: Box::pin(s),
        });
        match &inner.waker {
            Some(w) => w.wake_by_ref(),
            None => (),
        };
    }
}

#[cfg(test)]
mod test {
    use crate::fair_queue::FairQueue;
    use futures::StreamExt;

    #[tokio::test]
    async fn test_fair_queue_ready() {
        let a = futures::stream::iter(vec!["a1", "a2", "a3"]);
        let b = futures::stream::iter(vec!["b1", "b2", "b3"]);
        let c = futures::stream::iter(vec!["c1", "c2", "c3"]);

        let mut f_queue: FairQueue<_, u64> = FairQueue::new();
        f_queue.insert(1, a);
        f_queue.insert(2, b);
        f_queue.insert(3, c);

        let mut results = Vec::new();
        while let Some(i) = f_queue.next().await {
            results.push(i);
        }
        assert_eq!(
            results,
            vec![
                (1, "a1"),
                (2, "b1"),
                (3, "c1"),
                (1, "a2"),
                (2, "b2"),
                (3, "c2"),
                (1, "a3"),
                (2, "b3"),
                (3, "c3")
            ]
        );
    }

    #[tokio::test]
    async fn test_fair_queue_different_size() {
        let a = futures::stream::iter(vec!["a1", "a2", "a3"]);
        let b = futures::stream::iter(vec!["b1"]);
        let c = futures::stream::iter(vec!["c1", "c2"]);

        let mut f_queue: FairQueue<_, u64> = FairQueue::new();
        f_queue.insert(1, a);
        f_queue.insert(2, b);
        f_queue.insert(3, c);

        let mut results = Vec::new();
        while let Some(i) = f_queue.next().await {
            results.push(i);
        }
        assert_eq!(
            results,
            vec![
                (1, "a1"),
                (2, "b1"),
                (3, "c1"),
                (1, "a2"),
                (3, "c2"),
                (1, "a3")
            ]
        );
    }
}
<|endoftext|>


<commit_before>use std::{
    cell::RefCell,
    process::Command,
    rc::Rc,
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc,
    },
};

use slog::Logger;

#[cfg(feature = "udev")]
use smithay::backend::session::auto::AutoSession;
use smithay::{
    backend::input::{
        self, Event, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, PointerAxisEvent,
        PointerButtonEvent, PointerMotionAbsoluteEvent, PointerMotionEvent,
    },
    wayland::seat::{keysyms as xkb, AxisFrame, KeyboardHandle, Keysym, ModifiersState, PointerHandle},
    wayland_server::protocol::wl_pointer,
};

use shell::MyWindowMap;

pub struct AnvilInputHandler {
    log: Logger,
    pointer: PointerHandle,
    keyboard: KeyboardHandle,
    window_map: Rc<RefCell<MyWindowMap>>,
    pointer_location: Rc<RefCell<(f64, f64)>>,
    screen_size: (u32, u32),
    serial: u32,
    #[cfg(feature = "udev")]
    session: Option<AutoSession>,
    running: Arc<AtomicBool>,
}

impl AnvilInputHandler {
    pub fn new(
        log: Logger,
        pointer: PointerHandle,
        keyboard: KeyboardHandle,
        window_map: Rc<RefCell<MyWindowMap>>,
        screen_size: (u32, u32),
        running: Arc<AtomicBool>,
        pointer_location: Rc<RefCell<(f64, f64)>>,
    ) -> AnvilInputHandler {
        AnvilInputHandler {
            log,
            pointer,
            keyboard,
            window_map,
            screen_size,
            running,
            pointer_location,
            serial: 1,
            #[cfg(feature = "udev")]
            session: None,
        }
    }

    #[cfg(feature = "udev")]
    pub fn new_with_session(
        log: Logger,
        pointer: PointerHandle,
        keyboard: KeyboardHandle,
        window_map: Rc<RefCell<MyWindowMap>>,
        screen_size: (u32, u32),
        running: Arc<AtomicBool>,
        pointer_location: Rc<RefCell<(f64, f64)>>,
        session: AutoSession,
    ) -> AnvilInputHandler {
        AnvilInputHandler {
            log,
            pointer,
            keyboard,
            window_map,
            screen_size,
            running,
            pointer_location,
            serial: 1,
            session: Some(session),
        }
    }

    fn next_serial(&mut self) -> u32 {
        self.serial += 1;
        self.serial
    }
}

impl<B: InputBackend> InputHandler<B> for AnvilInputHandler {
    fn on_seat_created(&mut self, _: &input::Seat) {
        /* currently we just create a single static one */
    }

    fn on_seat_destroyed(&mut self, _: &input::Seat) {
        /* currently we just create a single static one */
    }

    fn on_seat_changed(&mut self, _: &input::Seat) {
        /* currently we just create a single static one */
    }

    fn on_keyboard_key(&mut self, _: &input::Seat, evt: B::KeyboardKeyEvent) {
        let keycode = evt.key_code();
        let state = evt.state();
        debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
        let serial = self.next_serial();
        let log = &self.log;
        let time = Event::time(&evt);
        let mut action = KeyAction::None;
        self.keyboard
            .input(keycode, state, serial, time, |modifiers, keysym| {
                debug!(log, "keysym";
                    "state" => format!("{:?}", state),
                    "mods" => format!("{:?}", modifiers),
                    "keysym" => ::xkbcommon::xkb::keysym_get_name(keysym)
                );
                action = process_keyboard_shortcut(modifiers, keysym);
                // forward to client only if action == KeyAction::Forward
                // both for pressed and released, to avoid inconsistencies
                if let KeyAction::Forward = action {
                    true
                } else {
                    false
                }
            });
        if let KeyState::Released = state {
            // only process special actions on key press, not release
            return;
        }
        match action {
            KeyAction::Quit => {
                info!(self.log, "Quitting.");
                self.running.store(false, Ordering::SeqCst);
            }
            #[cfg(feature = "tty_lauch")]
            KeyAction::VtSwitch(vt) => if let Some(ref mut session) = self.session {
                info!(log, "Trying to switch to vt {}", vt);
                if let Err(err) = session.change_vt(vt) {
                    error!(log, "Error switching to vt {}: {}", vt, err);
                }
            },
            KeyAction::Run(cmd) => {
                info!(self.log, "Starting program"; "cmd" => cmd.clone());
                if let Err(e) = Command::new(&cmd).spawn() {
                    error!(log,
                        "Failed to start program";
                        "cmd" => cmd,
                        "err" => format!("{:?}", e)
                    );
                }
            }
            _ => (),
        }
    }

    fn on_pointer_move(&mut self, _: &input::Seat, evt: B::PointerMotionEvent) {
        let (x, y) = (evt.delta_x(), evt.delta_y());
        let serial = self.next_serial();
        let mut location = self.pointer_location.borrow_mut();
        location.0 += x as f64;
        location.1 += y as f64;
        // clamp to screen limits
        // this event is never generated by winit so self.screen_size is relevant
        location.0 = (location.0).max(0.0).min(self.screen_size.0 as f64);
        location.1 = (location.1).max(0.0).min(self.screen_size.1 as f64);
        let under = self
            .window_map
            .borrow()
            .get_surface_under((location.0, location.1));
        self.pointer.motion(*location, under, serial, evt.time());
    }

    fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: B::PointerMotionAbsoluteEvent) {
        // different cases depending on the context:
        let (x, y) = {
            #[cfg(feature = "udev")]
            {
                if self.session.is_some() {
                    // we are started on a tty
                    let (ux, uy) = evt.position_transformed(self.screen_size);
                    (ux as f64, uy as f64)
                } else {
                    // we are started in winit
                    evt.position()
                }
            }
            #[cfg(not(feature = "udev"))]
            {
                evt.position()
            }
        };
        *self.pointer_location.borrow_mut() = (x, y);
        let serial = self.next_serial();
        let under = self.window_map.borrow().get_surface_under((x as f64, y as f64));
        self.pointer.motion((x, y), under, serial, evt.time());
    }

    fn on_pointer_button(&mut self, _: &input::Seat, evt: B::PointerButtonEvent) {
        let serial = self.next_serial();
        let button = match evt.button() {
            input::MouseButton::Left => 0x110,
            input::MouseButton::Right => 0x111,
            input::MouseButton::Middle => 0x112,
            input::MouseButton::Other(b) => b as u32,
        };
        let state = match evt.state() {
            input::MouseButtonState::Pressed => {
                // change the keyboard focus unless the pointer is grabbed
                if !self.pointer.is_grabbed() {
                    let under = self
                        .window_map
                        .borrow_mut()
                        .get_surface_and_bring_to_top(*self.pointer_location.borrow());
                    self.keyboard
                        .set_focus(under.as_ref().map(|&(ref s, _)| s), serial);
                }
                wl_pointer::ButtonState::Pressed
            }
            input::MouseButtonState::Released => wl_pointer::ButtonState::Released,
        };
        self.pointer.button(button, state, serial, evt.time());
    }

    fn on_pointer_axis(&mut self, _: &input::Seat, evt: B::PointerAxisEvent) {
        let source = match evt.source() {
            input::AxisSource::Continuous => wl_pointer::AxisSource::Continuous,
            input::AxisSource::Finger => wl_pointer::AxisSource::Finger,
            input::AxisSource::Wheel | input::AxisSource::WheelTilt => wl_pointer::AxisSource::Wheel,
        };
        let horizontal_amount = evt
            .amount(&input::Axis::Horizontal)
            .unwrap_or_else(|| evt.amount_discrete(&input::Axis::Horizontal).unwrap() * 3.0);
        let vertical_amount = evt
            .amount(&input::Axis::Vertical)
            .unwrap_or_else(|| evt.amount_discrete(&input::Axis::Vertical).unwrap() * 3.0);
        let horizontal_amount_discrete = evt.amount_discrete(&input::Axis::Horizontal);
        let vertical_amount_discrete = evt.amount_discrete(&input::Axis::Vertical);

        {
            let mut frame = AxisFrame::new(evt.time()).source(source);
            if horizontal_amount != 0.0 {
                frame = frame.value(wl_pointer::Axis::HorizontalScroll, horizontal_amount);
                if let Some(discrete) = horizontal_amount_discrete {
                    frame = frame.discrete(wl_pointer::Axis::HorizontalScroll, discrete as i32);
                }
            } else if source == wl_pointer::AxisSource::Finger {
                frame = frame.stop(wl_pointer::Axis::HorizontalScroll);
            }
            if vertical_amount != 0.0 {
                frame = frame.value(wl_pointer::Axis::VerticalScroll, vertical_amount);
                if let Some(discrete) = vertical_amount_discrete {
                    frame = frame.discrete(wl_pointer::Axis::VerticalScroll, discrete as i32);
                }
            } else if source == wl_pointer::AxisSource::Finger {
                frame = frame.stop(wl_pointer::Axis::VerticalScroll);
            }
            self.pointer.axis(frame);
        }
    }

    fn on_touch_down(&mut self, _: &input::Seat, _: B::TouchDownEvent) {
        /* not done in this example */
    }
    fn on_touch_motion(&mut self, _: &input::Seat, _: B::TouchMotionEvent) {
        /* not done in this example */
    }
    fn on_touch_up(&mut self, _: &input::Seat, _: B::TouchUpEvent) {
        /* not done in this example */
    }
    fn on_touch_cancel(&mut self, _: &input::Seat, _: B::TouchCancelEvent) {
        /* not done in this example */
    }
    fn on_touch_frame(&mut self, _: &input::Seat, _: B::TouchFrameEvent) {
        /* not done in this example */
    }
    fn on_input_config_changed(&mut self, _: &mut B::InputConfig) {
        /* not done in this example */
    }
}

/// Possible results of a keyboard action
enum KeyAction {
    /// Quit the compositor
    Quit,
    /// Trigger a vt-switch
    VtSwitch(i32),
    /// run a command
    Run(String),
    /// Forward the key to the client
    Forward,
    /// Do nothing more
    None,
}

fn process_keyboard_shortcut(modifiers: &ModifiersState, keysym: Keysym) -> KeyAction {
    if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace
        || modifiers.logo && keysym == xkb::KEY_q
    {
        // ctrl+alt+backspace = quit
        // logo + q = quit
        KeyAction::Quit
    } else if keysym >= xkb::KEY_XF86Switch_VT_1 && keysym <= xkb::KEY_XF86Switch_VT_12 {
        // VTSwicth
        KeyAction::VtSwitch((keysym - xkb::KEY_XF86Switch_VT_1 + 1) as i32)
    } else if modifiers.logo && keysym == xkb::KEY_Return {
        // run terminal
        KeyAction::Run("weston-terminal".into())
    } else {
        KeyAction::Forward
    }
}
<commit_msg>anvil: use smithay's global SerialCounter<commit_after>use std::{
    cell::RefCell,
    process::Command,
    rc::Rc,
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc,
    },
};

use slog::Logger;

#[cfg(feature = "udev")]
use smithay::backend::session::auto::AutoSession;
use smithay::{
    backend::input::{
        self, Event, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, PointerAxisEvent,
        PointerButtonEvent, PointerMotionAbsoluteEvent, PointerMotionEvent,
    },
    wayland::{
        seat::{keysyms as xkb, AxisFrame, KeyboardHandle, Keysym, ModifiersState, PointerHandle},
        SERIAL_COUNTER as SCOUNTER,
    },
    wayland_server::protocol::wl_pointer,
};

use shell::MyWindowMap;

pub struct AnvilInputHandler {
    log: Logger,
    pointer: PointerHandle,
    keyboard: KeyboardHandle,
    window_map: Rc<RefCell<MyWindowMap>>,
    pointer_location: Rc<RefCell<(f64, f64)>>,
    screen_size: (u32, u32),
    #[cfg(feature = "udev")]
    session: Option<AutoSession>,
    running: Arc<AtomicBool>,
}

impl AnvilInputHandler {
    pub fn new(
        log: Logger,
        pointer: PointerHandle,
        keyboard: KeyboardHandle,
        window_map: Rc<RefCell<MyWindowMap>>,
        screen_size: (u32, u32),
        running: Arc<AtomicBool>,
        pointer_location: Rc<RefCell<(f64, f64)>>,
    ) -> AnvilInputHandler {
        AnvilInputHandler {
            log,
            pointer,
            keyboard,
            window_map,
            screen_size,
            running,
            pointer_location,
            #[cfg(feature = "udev")]
            session: None,
        }
    }

    #[cfg(feature = "udev")]
    pub fn new_with_session(
        log: Logger,
        pointer: PointerHandle,
        keyboard: KeyboardHandle,
        window_map: Rc<RefCell<MyWindowMap>>,
        screen_size: (u32, u32),
        running: Arc<AtomicBool>,
        pointer_location: Rc<RefCell<(f64, f64)>>,
        session: AutoSession,
    ) -> AnvilInputHandler {
        AnvilInputHandler {
            log,
            pointer,
            keyboard,
            window_map,
            screen_size,
            running,
            pointer_location,
            session: Some(session),
        }
    }
}

impl<B: InputBackend> InputHandler<B> for AnvilInputHandler {
    fn on_seat_created(&mut self, _: &input::Seat) {
        /* currently we just create a single static one */
    }

    fn on_seat_destroyed(&mut self, _: &input::Seat) {
        /* currently we just create a single static one */
    }

    fn on_seat_changed(&mut self, _: &input::Seat) {
        /* currently we just create a single static one */
    }

    fn on_keyboard_key(&mut self, _: &input::Seat, evt: B::KeyboardKeyEvent) {
        let keycode = evt.key_code();
        let state = evt.state();
        debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
        let serial = SCOUNTER.next_serial();
        let log = &self.log;
        let time = Event::time(&evt);
        let mut action = KeyAction::None;
        self.keyboard
            .input(keycode, state, serial, time, |modifiers, keysym| {
                debug!(log, "keysym";
                    "state" => format!("{:?}", state),
                    "mods" => format!("{:?}", modifiers),
                    "keysym" => ::xkbcommon::xkb::keysym_get_name(keysym)
                );
                action = process_keyboard_shortcut(modifiers, keysym);
                // forward to client only if action == KeyAction::Forward
                // both for pressed and released, to avoid inconsistencies
                if let KeyAction::Forward = action {
                    true
                } else {
                    false
                }
            });
        if let KeyState::Released = state {
            // only process special actions on key press, not release
            return;
        }
        match action {
            KeyAction::Quit => {
                info!(self.log, "Quitting.");
                self.running.store(false, Ordering::SeqCst);
            }
            #[cfg(feature = "tty_lauch")]
            KeyAction::VtSwitch(vt) => if let Some(ref mut session) = self.session {
                info!(log, "Trying to switch to vt {}", vt);
                if let Err(err) = session.change_vt(vt) {
                    error!(log, "Error switching to vt {}: {}", vt, err);
                }
            },
            KeyAction::Run(cmd) => {
                info!(self.log, "Starting program"; "cmd" => cmd.clone());
                if let Err(e) = Command::new(&cmd).spawn() {
                    error!(log,
                        "Failed to start program";
                        "cmd" => cmd,
                        "err" => format!("{:?}", e)
                    );
                }
            }
            _ => (),
        }
    }

    fn on_pointer_move(&mut self, _: &input::Seat, evt: B::PointerMotionEvent) {
        let (x, y) = (evt.delta_x(), evt.delta_y());
        let serial = SCOUNTER.next_serial();
        let mut location = self.pointer_location.borrow_mut();
        location.0 += x as f64;
        location.1 += y as f64;
        // clamp to screen limits
        // this event is never generated by winit so self.screen_size is relevant
        location.0 = (location.0).max(0.0).min(self.screen_size.0 as f64);
        location.1 = (location.1).max(0.0).min(self.screen_size.1 as f64);
        let under = self
            .window_map
            .borrow()
            .get_surface_under((location.0, location.1));
        self.pointer.motion(*location, under, serial, evt.time());
    }

    fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: B::PointerMotionAbsoluteEvent) {
        // different cases depending on the context:
        let (x, y) = {
            #[cfg(feature = "udev")]
            {
                if self.session.is_some() {
                    // we are started on a tty
                    let (ux, uy) = evt.position_transformed(self.screen_size);
                    (ux as f64, uy as f64)
                } else {
                    // we are started in winit
                    evt.position()
                }
            }
            #[cfg(not(feature = "udev"))]
            {
                evt.position()
            }
        };
        *self.pointer_location.borrow_mut() = (x, y);
        let serial = SCOUNTER.next_serial();
        let under = self.window_map.borrow().get_surface_under((x as f64, y as f64));
        self.pointer.motion((x, y), under, serial, evt.time());
    }

    fn on_pointer_button(&mut self, _: &input::Seat, evt: B::PointerButtonEvent) {
        let serial = SCOUNTER.next_serial();
        let button = match evt.button() {
            input::MouseButton::Left => 0x110,
            input::MouseButton::Right => 0x111,
            input::MouseButton::Middle => 0x112,
            input::MouseButton::Other(b) => b as u32,
        };
        let state = match evt.state() {
            input::MouseButtonState::Pressed => {
                // change the keyboard focus unless the pointer is grabbed
                if !self.pointer.is_grabbed() {
                    let under = self
                        .window_map
                        .borrow_mut()
                        .get_surface_and_bring_to_top(*self.pointer_location.borrow());
                    self.keyboard
                        .set_focus(under.as_ref().map(|&(ref s, _)| s), serial);
                }
                wl_pointer::ButtonState::Pressed
            }
            input::MouseButtonState::Released => wl_pointer::ButtonState::Released,
        };
        self.pointer.button(button, state, serial, evt.time());
    }

    fn on_pointer_axis(&mut self, _: &input::Seat, evt: B::PointerAxisEvent) {
        let source = match evt.source() {
            input::AxisSource::Continuous => wl_pointer::AxisSource::Continuous,
            input::AxisSource::Finger => wl_pointer::AxisSource::Finger,
            input::AxisSource::Wheel | input::AxisSource::WheelTilt => wl_pointer::AxisSource::Wheel,
        };
        let horizontal_amount = evt
            .amount(&input::Axis::Horizontal)
            .unwrap_or_else(|| evt.amount_discrete(&input::Axis::Horizontal).unwrap() * 3.0);
        let vertical_amount = evt
            .amount(&input::Axis::Vertical)
            .unwrap_or_else(|| evt.amount_discrete(&input::Axis::Vertical).unwrap() * 3.0);
        let horizontal_amount_discrete = evt.amount_discrete(&input::Axis::Horizontal);
        let vertical_amount_discrete = evt.amount_discrete(&input::Axis::Vertical);

        {
            let mut frame = AxisFrame::new(evt.time()).source(source);
            if horizontal_amount != 0.0 {
                frame = frame.value(wl_pointer::Axis::HorizontalScroll, horizontal_amount);
                if let Some(discrete) = horizontal_amount_discrete {
                    frame = frame.discrete(wl_pointer::Axis::HorizontalScroll, discrete as i32);
                }
            } else if source == wl_pointer::AxisSource::Finger {
                frame = frame.stop(wl_pointer::Axis::HorizontalScroll);
            }
            if vertical_amount != 0.0 {
                frame = frame.value(wl_pointer::Axis::VerticalScroll, vertical_amount);
                if let Some(discrete) = vertical_amount_discrete {
                    frame = frame.discrete(wl_pointer::Axis::VerticalScroll, discrete as i32);
                }
            } else if source == wl_pointer::AxisSource::Finger {
                frame = frame.stop(wl_pointer::Axis::VerticalScroll);
            }
            self.pointer.axis(frame);
        }
    }

    fn on_touch_down(&mut self, _: &input::Seat, _: B::TouchDownEvent) {
        /* not done in this example */
    }
    fn on_touch_motion(&mut self, _: &input::Seat, _: B::TouchMotionEvent) {
        /* not done in this example */
    }
    fn on_touch_up(&mut self, _: &input::Seat, _: B::TouchUpEvent) {
        /* not done in this example */
    }
    fn on_touch_cancel(&mut self, _: &input::Seat, _: B::TouchCancelEvent) {
        /* not done in this example */
    }
    fn on_touch_frame(&mut self, _: &input::Seat, _: B::TouchFrameEvent) {
        /* not done in this example */
    }
    fn on_input_config_changed(&mut self, _: &mut B::InputConfig) {
        /* not done in this example */
    }
}

/// Possible results of a keyboard action
enum KeyAction {
    /// Quit the compositor
    Quit,
    /// Trigger a vt-switch
    VtSwitch(i32),
    /// run a command
    Run(String),
    /// Forward the key to the client
    Forward,
    /// Do nothing more
    None,
}

fn process_keyboard_shortcut(modifiers: &ModifiersState, keysym: Keysym) -> KeyAction {
    if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace
        || modifiers.logo && keysym == xkb::KEY_q
    {
        // ctrl+alt+backspace = quit
        // logo + q = quit
        KeyAction::Quit
    } else if keysym >= xkb::KEY_XF86Switch_VT_1 && keysym <= xkb::KEY_XF86Switch_VT_12 {
        // VTSwicth
        KeyAction::VtSwitch((keysym - xkb::KEY_XF86Switch_VT_1 + 1) as i32)
    } else if modifiers.logo && keysym == xkb::KEY_Return {
        // run terminal
        KeyAction::Run("weston-terminal".into())
    } else {
        KeyAction::Forward
    }
}
<|endoftext|>


<commit_before>use std::uint::range;

static N: uint = 5000000;

fn main() {
  stieve_atkin();
}

fn stieve_atkin() {
  let nsqrt: uint = ((N as float).sqrt() + 1.0) as uint;
  let mut n: uint = 0;
  let mut primes = [false, ..N];

  for range(1, nsqrt) |x| {
    for range(1, nsqrt) |y| {
      // println(fmt!("%?", x));
      n = 4 * (x*x) + (y*y);
      if n <= N && (n % 12 == 1 || n % 12 == 5) { primes[n] = !primes[n]; }

      n = 3 * (x*x) + y*y;
      if n <= N && n % 12 == 7 { primes[n] = !primes[n]; }

      n = 3 * (x*x) - y*y;
      if x > y && n <= N && n % 12 == 11 { primes[n] = !primes[n]; }
    }
  }

  for range(5, nsqrt) |x| {
    if primes[x] {
    }
  }
}
<commit_msg>complete the first implementation o sieve atkin<commit_after>static N: uint = 100000000;
fn main() {
  stieve_atkin();
}

fn stieve_atkin() {
  let nsqrt: uint = ((N as float).sqrt() + 1.0) as uint;
  let mut is_prime = ~[false, ..N];
  let mut n: uint;
  let mut primes = ~[];

  for x in range(1, nsqrt) {
    for y in range(1, nsqrt) {
      n = 4 * (x*x) + (y*y);
      if n <= N && (n % 12 == 1 || n % 12 == 5) { is_prime[n] = !is_prime[n]; }

      n = 3 * (x*x) + y*y;
      if n <= N && n % 12 == 7 { is_prime[n] = !is_prime[n]; }

      n = 3 * (x*x) - y*y;
      if x > y && n <= N && n % 12 == 11 { is_prime[n] = !is_prime[n]; }
    }
  }

  for x in range(5, nsqrt) {
    if is_prime[x] {
      let mut y = x*x;
      while y < N {
        is_prime[y] = false;
        y += x*x;
      }
    }
  }

  is_prime[2] = true;
  is_prime[3] = true;

  for x in range(0, N) { if is_prime[x] { primes.push(x) } }
}
<|endoftext|>


<commit_before>//! The Tessellated Intelligence System is a
//!
//! > massively parallel computer architecture comprised of non-uniformly
//! > interconnected heterogeneous nodes.
//!
//! The manual can be found [online](https://www.vidarholen.net/contents/junk/files/TIS-100%20Reference%20Manual.pdf).
//! Another useful resource is the unofficial [TIS-100 Hackers Guide](https://alandesmet.github.io/TIS-100-Hackers-Guide/).
//!
//! # Examples
//! Here we take a node and execute some instructions and check if double 1 has
//! the expected result.
//!
//! ```rust
//! extern crate tis_100_superoptimizer;
//!
//! use tis_100_superoptimizer::TIS_100::{Node,Instruction,Source,Destination,Register};
//!
//! fn main() {
//!     let node: Node = Node::new();
//!     let last: Node = node
//!         .execute(Instruction::MOV(Source::Literal(1), Destination::Register(Register::ACC)))
//!         .execute(Instruction::ADD(Source::Register(Register::ACC)));
//!
//!     assert_eq!(2, last.acc);
//! }
//! ```

/// A `Node` models the basic execution node in TIS-100. You change a node state
/// by executing an `Instruction` on it.
#[derive(Debug,PartialEq,Eq)]
pub struct Node {
    /// The accumulator for the basic execution node.
    pub acc: i32,
    bac: i32,
    pc: isize,
}

/// `Instruction`s are executed by a `Node`.
pub enum Instruction {
    /// Does nothing
    NOP,
    /// Moves value from `Source` to `Destination`
    MOV(Source, Destination),
    /// Swaps the value of the accumulator (acc) and the backup (bac) register
    SWP,
    /// Saves the value of the accumulator (acc) to the backup register
    SAV,
    /// Add value from `Source` to accumulator (acc), storing result in acc
    ADD(Source),
    /// Subtracts value from `Source` from accumulator (acc), storing result in acc
    SUB(Source),
}

/// `Source` are either ports, registers or literals
pub enum Source {
    /// A register
    Register(Register),
    /// A literal value
    Literal(i32)
}

/// Different types of Registers known in TIS-100
pub enum Register {
    /// the NIL register, reading from it provides with zero
    NIL,
    /// The accumulator
    ACC,
}

/// `Destination` are either ports or registers
pub enum Destination {
    /// A register
    Register(Register),
}

impl Node {
    /// Create a `Node` with defaults for accumulator and backup registers
    pub fn new() -> Node {
        Node { acc: 0, bac: 0, pc: 0 }
    }

    /// Create a `Node` with prescribed values for accumulator and backup registers
    pub fn with(acc: i32, bac: i32, pc: isize) -> Node {
        Node { acc: acc, bac: bac, pc: pc }
    }

    /// Create a `Node` from self with the program counter incremented
    pub fn increment_pc(&self) -> Node {
        Node { pc: self.pc + 1, .. *self }
    }

    /// Create a `Node` from self with a prescribed accumulator register value
    pub fn set_acc(&self, acc: i32) -> Node {
        Node { acc: acc, .. *self }
    }

    /// Create a `Node` from self with a prescribed backup register value
    pub fn set_bac(&self, bac: i32) -> Node {
        Node { bac: bac, .. *self }
    }

    /// Execute the `instruction` on this `Node`. Returns a `Node` that reflects
    /// the changes the `instruction` would have on this `Node`.
    pub fn execute(&self, instruction: Instruction) -> Node {
        match instruction {
            Instruction::NOP => self.nop(),
            Instruction::MOV(source, destination) => self.mov(source, destination) ,
            Instruction::SWP => self.swap(),
            Instruction::SAV => self.save(),
            Instruction::ADD(source) => self.add(source),
            Instruction::SUB(source) => self.subtract(source),
        }
    }

    fn nop(&self) -> Node {
        self.increment_pc()
    }

    fn mov(&self, source: Source, destination: Destination) -> Node {
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.move_value(value, destination)
    }

    fn move_value(&self, value: i32, destination: Destination) -> Node {
        match destination {
            Destination::Register(Register::ACC) => self.increment_pc().set_acc(value),
            _ => self.nop(),
        }
    }

    fn swap(&self) -> Node {
        let acc: i32 = self.acc;
        let bac: i32 = self.bac;

        self.increment_pc().set_acc(bac).set_bac(acc)
    }

    fn save(&self) -> Node {
        self.increment_pc().set_bac(self.acc)
    }

    fn add(&self, source: Source) -> Node{
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.add_value(value)
    }

    fn add_value(&self, value: i32) -> Node {
        self.increment_pc().set_acc(self.acc + value)
    }

    fn subtract(&self, source: Source) -> Node {
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.subtract_value(value)
    }

    fn subtract_value(&self, value: i32) -> Node {
        self.increment_pc().set_acc(self.acc - value)
    }
}

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

    #[test]
    fn node_should_start_with_accumulator_zero() {
        let node: Node = Node::new();

        assert_eq!(0, node.acc);
    }

    #[test]
    fn node_should_start_with_backup_zero() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(0, next.acc);
    }

    #[test]
    fn node_should_execute_NOP_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::NOP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_NIL_to_NIL_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::NIL),
                                                        Destination::Register(Register::NIL));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_ACC_to_ACC_correctly() {
        let node: Node = Node::with(2, 1, 0);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::ACC),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 1, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_Literal_to_ACC_correctly() {
        let node: Node = Node::with(0, 0, 0);
        let instruction: Instruction = Instruction::MOV(Source::Literal(1),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_SWP_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(0, 1, 1), next);
    }

    #[test]
    fn node_should_execute_SAV_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::SAV;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 1, 1), next);
    }

    #[test]
    fn node_should_execute_ADD_from_Literal_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::ADD(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 0, 1), next);
    }

    #[test]
    fn node_should_execute_SUB_from_Literal_correctly() {
        let node: Node = Node::with(2, 0, 0);
        let instruction: Instruction = Instruction::SUB(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }
}

<commit_msg>Introduce a set_pc for Node<commit_after>//! The Tessellated Intelligence System is a
//!
//! > massively parallel computer architecture comprised of non-uniformly
//! > interconnected heterogeneous nodes.
//!
//! The manual can be found [online](https://www.vidarholen.net/contents/junk/files/TIS-100%20Reference%20Manual.pdf).
//! Another useful resource is the unofficial [TIS-100 Hackers Guide](https://alandesmet.github.io/TIS-100-Hackers-Guide/).
//!
//! # Examples
//! Here we take a node and execute some instructions and check if double 1 has
//! the expected result.
//!
//! ```rust
//! extern crate tis_100_superoptimizer;
//!
//! use tis_100_superoptimizer::TIS_100::{Node,Instruction,Source,Destination,Register};
//!
//! fn main() {
//!     let node: Node = Node::new();
//!     let last: Node = node
//!         .execute(Instruction::MOV(Source::Literal(1), Destination::Register(Register::ACC)))
//!         .execute(Instruction::ADD(Source::Register(Register::ACC)));
//!
//!     assert_eq!(2, last.acc);
//! }
//! ```

/// A `Node` models the basic execution node in TIS-100. You change a node state
/// by executing an `Instruction` on it.
#[derive(Debug,PartialEq,Eq)]
pub struct Node {
    /// The accumulator for the basic execution node.
    pub acc: i32,
    bac: i32,
    pc: isize,
}

/// `Instruction`s are executed by a `Node`.
pub enum Instruction {
    /// Does nothing
    NOP,
    /// Moves value from `Source` to `Destination`
    MOV(Source, Destination),
    /// Swaps the value of the accumulator (acc) and the backup (bac) register
    SWP,
    /// Saves the value of the accumulator (acc) to the backup register
    SAV,
    /// Add value from `Source` to accumulator (acc), storing result in acc
    ADD(Source),
    /// Subtracts value from `Source` from accumulator (acc), storing result in acc
    SUB(Source),
}

/// `Source` are either ports, registers or literals
pub enum Source {
    /// A register
    Register(Register),
    /// A literal value
    Literal(i32)
}

/// Different types of Registers known in TIS-100
pub enum Register {
    /// the NIL register, reading from it provides with zero
    NIL,
    /// The accumulator
    ACC,
}

/// `Destination` are either ports or registers
pub enum Destination {
    /// A register
    Register(Register),
}

impl Node {
    /// Create a `Node` with defaults for accumulator and backup registers
    pub fn new() -> Node {
        Node { acc: 0, bac: 0, pc: 0 }
    }

    /// Create a `Node` with prescribed values for accumulator and backup registers
    pub fn with(acc: i32, bac: i32, pc: isize) -> Node {
        Node::new().set_acc(acc).set_bac(bac).set_pc(pc)
    }

    /// Create a `Node` from self with the program counter incremented
    pub fn increment_pc(&self) -> Node {
        Node { pc: self.pc + 1, .. *self }
    }

    /// Create a `Node` from self with a prescribed program counter value
    pub fn set_pc(&self, pc: isize) -> Node {
        Node { pc: pc, .. *self }
    }

    /// Create a `Node` from self with a prescribed accumulator register value
    pub fn set_acc(&self, acc: i32) -> Node {
        Node { acc: acc, .. *self }
    }

    /// Create a `Node` from self with a prescribed backup register value
    pub fn set_bac(&self, bac: i32) -> Node {
        Node { bac: bac, .. *self }
    }

    /// Execute the `instruction` on this `Node`. Returns a `Node` that reflects
    /// the changes the `instruction` would have on this `Node`.
    pub fn execute(&self, instruction: Instruction) -> Node {
        match instruction {
            Instruction::NOP => self.nop(),
            Instruction::MOV(source, destination) => self.mov(source, destination) ,
            Instruction::SWP => self.swap(),
            Instruction::SAV => self.save(),
            Instruction::ADD(source) => self.add(source),
            Instruction::SUB(source) => self.subtract(source),
        }
    }

    fn nop(&self) -> Node {
        self.increment_pc()
    }

    fn mov(&self, source: Source, destination: Destination) -> Node {
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.move_value(value, destination)
    }

    fn move_value(&self, value: i32, destination: Destination) -> Node {
        match destination {
            Destination::Register(Register::ACC) => self.increment_pc().set_acc(value),
            _ => self.nop(),
        }
    }

    fn swap(&self) -> Node {
        let acc: i32 = self.acc;
        let bac: i32 = self.bac;

        self.increment_pc().set_acc(bac).set_bac(acc)
    }

    fn save(&self) -> Node {
        self.increment_pc().set_bac(self.acc)
    }

    fn add(&self, source: Source) -> Node{
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.add_value(value)
    }

    fn add_value(&self, value: i32) -> Node {
        self.increment_pc().set_acc(self.acc + value)
    }

    fn subtract(&self, source: Source) -> Node {
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.subtract_value(value)
    }

    fn subtract_value(&self, value: i32) -> Node {
        self.increment_pc().set_acc(self.acc - value)
    }
}

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

    #[test]
    fn node_should_start_with_accumulator_zero() {
        let node: Node = Node::new();

        assert_eq!(0, node.acc);
    }

    #[test]
    fn node_should_start_with_backup_zero() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(0, next.acc);
    }

    #[test]
    fn node_should_execute_NOP_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::NOP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_NIL_to_NIL_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::NIL),
                                                        Destination::Register(Register::NIL));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_ACC_to_ACC_correctly() {
        let node: Node = Node::with(2, 1, 0);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::ACC),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 1, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_Literal_to_ACC_correctly() {
        let node: Node = Node::with(0, 0, 0);
        let instruction: Instruction = Instruction::MOV(Source::Literal(1),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_SWP_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(0, 1, 1), next);
    }

    #[test]
    fn node_should_execute_SAV_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::SAV;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 1, 1), next);
    }

    #[test]
    fn node_should_execute_ADD_from_Literal_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::ADD(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 0, 1), next);
    }

    #[test]
    fn node_should_execute_SUB_from_Literal_correctly() {
        let node: Node = Node::with(2, 0, 0);
        let instruction: Instruction = Instruction::SUB(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }
}

<|endoftext|>