<commit_before>extern crate target_build_utils;
extern crate walkdir;

use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use target_build_utils::TargetInfo;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	assert!(
		src_dir.exists(),
		"rusty_hermit source folder does not exist"
	);
	let target = TargetInfo::new().expect("Could not get target info");
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	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");

	if target.target_arch() == "x86_64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("x86_64-unknown-none-hermitkernel");
	} else if target.target_arch() == "aarch64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("aarch64-unknown-hermit");
	} else {
		panic!("Try to build for an unsupported platform");
	}

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

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

	// disable all default features
	cmd.arg("--no-default-features");

	if target.target_arch() == "aarch64" {
		cmd.arg("--features");
		cmd.arg("aarch64-qemu-stdout");
	}

	// do we have to enable PCI support?
	#[cfg(feature = "pci")]
	{
		cmd.arg("--features");
		cmd.arg("pci");
	}

	// do we have to enable acpi support?
	#[cfg(feature = "acpi")]
	{
		cmd.arg("--features");
		cmd.arg("acpi");
	}

	// do we have to enable FSGSBASE support?
	#[cfg(feature = "fsgs_base")]
	{
		cmd.arg("--features");
		cmd.arg("fsgs_base");
	}

	// do we support multi-processor systems?
	#[cfg(feature = "smp")]
	{
		cmd.arg("--features");
		cmd.arg("smp");
	}

	// do we have to enable VGA support
	#[cfg(feature = "vga")]
	{
		cmd.arg("--features");
		cmd.arg("vga");
	}

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

	#[cfg(feature = "instrument")]
	{
		rustflags.push("-Zinstrument-mcount".to_string());
		// Add outer rustflags to command
		rustflags.push(outer_rustflags);
	}

	#[cfg(not(feature = "instrument"))]
	{
		// 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 = if target.target_arch() == "x86_64" {
		target_dir
			.join("x86_64-unknown-none-hermitkernel")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else if target.target_arch() == "aarch64" {
		target_dir
			.join("aarch64-unknown-hermit")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else {
		panic!("Try to build for an unsupported platform");
	};
	println!("Lib location: {}", lib_location.display());

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

	rename_symbol("rust_begin_unwind", &lib);
	rename_symbol("rust_oom", &lib);

	#[cfg(feature = "mem")]
	{
		for symbol in ["memcpy", "memmove", "memset", "memcmp", "bcmp"] {
			rename_symbol(symbol, &lib);
		}
	}

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

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

/// 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.
/// In addition, it provides us the offer to create a optimized version of memcpy
/// in user space.
fn rename_symbol(symbol: impl AsRef<OsStr>, lib: impl AsRef<Path>) {
	let arg = IntoIterator::into_iter([symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()])
		.collect::<OsString>();
	let status = Command::new("rust-objcopy")
		.arg("--redefine-sym")
		.arg(arg)
		.arg(lib.as_ref())
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	if !src_dir.as_path().exists() {
		let status = Command::new(env!("CARGO"))
			.current_dir(out_dir)
			.arg("download")
			.arg("--output")
			.arg(src_dir.clone().into_os_string())
			.arg("--extract")
			.arg("rusty-hermit")
			.status()
			.expect("failed to start kernel download");
		assert!(
			status.success(),
			"Unable to download rusty-hermit. Is cargo-download installed?"
		);
	}

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(is_not_ignored)
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<commit_msg>Remove unused "mem" feature<commit_after>extern crate target_build_utils;
extern crate walkdir;

use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use target_build_utils::TargetInfo;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	assert!(
		src_dir.exists(),
		"rusty_hermit source folder does not exist"
	);
	let target = TargetInfo::new().expect("Could not get target info");
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	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");

	if target.target_arch() == "x86_64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("x86_64-unknown-none-hermitkernel");
	} else if target.target_arch() == "aarch64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("aarch64-unknown-hermit");
	} else {
		panic!("Try to build for an unsupported platform");
	}

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

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

	// disable all default features
	cmd.arg("--no-default-features");

	if target.target_arch() == "aarch64" {
		cmd.arg("--features");
		cmd.arg("aarch64-qemu-stdout");
	}

	// do we have to enable PCI support?
	#[cfg(feature = "pci")]
	{
		cmd.arg("--features");
		cmd.arg("pci");
	}

	// do we have to enable acpi support?
	#[cfg(feature = "acpi")]
	{
		cmd.arg("--features");
		cmd.arg("acpi");
	}

	// do we have to enable FSGSBASE support?
	#[cfg(feature = "fsgs_base")]
	{
		cmd.arg("--features");
		cmd.arg("fsgs_base");
	}

	// do we support multi-processor systems?
	#[cfg(feature = "smp")]
	{
		cmd.arg("--features");
		cmd.arg("smp");
	}

	// do we have to enable VGA support
	#[cfg(feature = "vga")]
	{
		cmd.arg("--features");
		cmd.arg("vga");
	}

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

	#[cfg(feature = "instrument")]
	{
		rustflags.push("-Zinstrument-mcount".to_string());
		// Add outer rustflags to command
		rustflags.push(outer_rustflags);
	}

	#[cfg(not(feature = "instrument"))]
	{
		// 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 = if target.target_arch() == "x86_64" {
		target_dir
			.join("x86_64-unknown-none-hermitkernel")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else if target.target_arch() == "aarch64" {
		target_dir
			.join("aarch64-unknown-hermit")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else {
		panic!("Try to build for an unsupported platform");
	};
	println!("Lib location: {}", lib_location.display());

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

	rename_symbol("rust_begin_unwind", &lib);
	rename_symbol("rust_oom", &lib);

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

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

/// 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.
fn rename_symbol(symbol: impl AsRef<OsStr>, lib: impl AsRef<Path>) {
	let arg = IntoIterator::into_iter([symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()])
		.collect::<OsString>();
	let status = Command::new("rust-objcopy")
		.arg("--redefine-sym")
		.arg(arg)
		.arg(lib.as_ref())
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	if !src_dir.as_path().exists() {
		let status = Command::new(env!("CARGO"))
			.current_dir(out_dir)
			.arg("download")
			.arg("--output")
			.arg(src_dir.clone().into_os_string())
			.arg("--extract")
			.arg("rusty-hermit")
			.status()
			.expect("failed to start kernel download");
		assert!(
			status.success(),
			"Unable to download rusty-hermit. Is cargo-download installed?"
		);
	}

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(is_not_ignored)
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<|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"));
	let runs_clippy =
		matches!(env::var_os("CARGO_CFG_FEATURE"), Some(os) if os == OsStr::new("cargo-clippy"));
	if !targets_hermit || runs_clippy {
		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");

		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");
}
<commit_msg>Sys: Refactoring<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"));
	let runs_clippy =
		matches!(env::var_os("CARGO_CFG_FEATURE"), Some(os) if os == OsStr::new("cargo-clippy"));
	if !targets_hermit || runs_clippy {
		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 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),
		};

		let mut cmd = Command::new("cargo");
		cmd.current_dir(&self.src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.args(&["--target", kernel_target])
			.arg("--manifest-path")
			.arg("Cargo.toml")
			.arg("--target-dir")
			.arg(&target_dir);

		cmd.env_remove("RUSTUP_TOOLCHAIN");

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

		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.args(&["--features", &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 syntax::{
    ast::{Expr, GenericArg},
    ast::{LetStmt, Type::InferType},
    AstNode, TextRange,
};

use crate::{
    assist_context::{AssistContext, Assists},
    AssistId, AssistKind,
};

// Assist: replace_turbofish_with_explicit_type
//
// Converts `::<_>` to an explicit type assignment.
//
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a = make$0::<i32>();
// }
// ```
// ->
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a: i32 = make();
// }
// ```
pub(crate) fn replace_turbofish_with_explicit_type(
    acc: &mut Assists,
    ctx: &AssistContext<'_>,
) -> Option<()> {
    let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;

    let initializer = let_stmt.initializer()?;

    let generic_args = match &initializer {
        Expr::MethodCallExpr(ce) => ce.generic_arg_list()?,
        Expr::CallExpr(ce) => {
            if let Expr::PathExpr(pe) = ce.expr()? {
                pe.path()?.segment()?.generic_arg_list()?
            } else {
                cov_mark::hit!(not_applicable_if_non_path_function_call);
                return None;
            }
        }
        _ => {
            cov_mark::hit!(not_applicable_if_non_function_call_initializer);
            return None;
        }
    };

    // Find range of ::<_>
    let colon2 = generic_args.coloncolon_token()?;
    let r_angle = generic_args.r_angle_token()?;
    let turbofish_range = TextRange::new(colon2.text_range().start(), r_angle.text_range().end());

    let turbofish_args: Vec<GenericArg> = generic_args.generic_args().into_iter().collect();

    // Find type of ::<_>
    if turbofish_args.len() != 1 {
        cov_mark::hit!(not_applicable_if_not_single_arg);
        return None;
    }

    // An improvement would be to check that this is correctly part of the return value of the
    // function call, or sub in the actual return type.
    let turbofish_type = &turbofish_args[0];

    let initializer_start = initializer.syntax().text_range().start();
    if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
        cov_mark::hit!(not_applicable_outside_turbofish);
        return None;
    }

    if let None = let_stmt.colon_token() {
        // If there's no colon in a let statement, then there is no explicit type.
        // let x = fn::<...>();
        let ident_range = let_stmt.pat()?.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace turbofish with explicit type",
            TextRange::new(initializer_start, turbofish_range.end()),
            |builder| {
                builder.insert(ident_range.end(), format!(": {}", turbofish_type));
                builder.delete(turbofish_range);
            },
        );
    } else if let Some(InferType(t)) = let_stmt.ty() {
        // If there's a type inference underscore, we can offer to replace it with the type in
        // the turbofish.
        // let x: _ = fn::<...>();
        let underscore_range = t.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace `_` with turbofish type",
            turbofish_range,
            |builder| {
                builder.replace(underscore_range, turbofish_type.to_string());
                builder.delete(turbofish_range);
            },
        );
    }

    None
}

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

    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn replaces_turbofish_for_vec_string() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_method_calls() {
        // foo.make() is a method call which uses a different expr in the let initializer
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = foo.make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = foo.make();
}
"#,
        );
    }

    #[test]
    fn replace_turbofish_target() {
        check_assist_target(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = $0make::<Vec<String>>();
}
"#,
            r#"make::<Vec<String>>"#,
        );
    }

    #[test]
    fn not_applicable_outside_turbofish() {
        cov_mark::check!(not_applicable_outside_turbofish);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let $0a = make::<Vec<String>>();
}
"#,
        );
    }

    #[test]
    fn replace_inferred_type_placeholder() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: _ = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn not_applicable_constant_initializer() {
        cov_mark::check!(not_applicable_if_non_function_call_initializer);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = "foo"$0;
}
"#,
        );
    }

    #[test]
    fn not_applicable_non_path_function_call() {
        cov_mark::check!(not_applicable_if_non_path_function_call);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    $0let a = (|| {})();
}
"#,
        );
    }

    #[test]
    fn non_applicable_multiple_generic_args() {
        cov_mark::check!(not_applicable_if_not_single_arg);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>, i32>();
}
"#,
        );
    }
}
<commit_msg>Use type information to deduce the correct type for "Replace turbofish with explicit type", even when it is not exactly the same as the turbofish type<commit_after>use hir::HirDisplay;
use syntax::{
    ast::{Expr, GenericArg},
    ast::{LetStmt, Type::InferType},
    AstNode, TextRange,
};

use crate::{
    assist_context::{AssistContext, Assists},
    AssistId, AssistKind,
};

// Assist: replace_turbofish_with_explicit_type
//
// Converts `::<_>` to an explicit type assignment.
//
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a = make$0::<i32>();
// }
// ```
// ->
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a: i32 = make();
// }
// ```
pub(crate) fn replace_turbofish_with_explicit_type(
    acc: &mut Assists,
    ctx: &AssistContext<'_>,
) -> Option<()> {
    let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;

    let initializer = let_stmt.initializer()?;

    let generic_args = match &initializer {
        Expr::MethodCallExpr(ce) => ce.generic_arg_list()?,
        Expr::CallExpr(ce) => {
            if let Expr::PathExpr(pe) = ce.expr()? {
                pe.path()?.segment()?.generic_arg_list()?
            } else {
                cov_mark::hit!(not_applicable_if_non_path_function_call);
                return None;
            }
        }
        _ => {
            cov_mark::hit!(not_applicable_if_non_function_call_initializer);
            return None;
        }
    };

    // Find range of ::<_>
    let colon2 = generic_args.coloncolon_token()?;
    let r_angle = generic_args.r_angle_token()?;
    let turbofish_range = TextRange::new(colon2.text_range().start(), r_angle.text_range().end());

    let turbofish_args: Vec<GenericArg> = generic_args.generic_args().into_iter().collect();

    // Find type of ::<_>
    if turbofish_args.len() != 1 {
        cov_mark::hit!(not_applicable_if_not_single_arg);
        return None;
    }

    // An improvement would be to check that this is correctly part of the return value of the
    // function call, or sub in the actual return type.
    let returned_type = match ctx.sema.type_of_expr(&initializer) {
        Some(returned_type) if !returned_type.original.contains_unknown() => {
            let module = ctx.sema.scope(let_stmt.syntax())?.module();
            returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
        }
        _ => {
            cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
            turbofish_args[0].to_string()
        }
    };

    let initializer_start = initializer.syntax().text_range().start();
    if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
        cov_mark::hit!(not_applicable_outside_turbofish);
        return None;
    }

    if let None = let_stmt.colon_token() {
        // If there's no colon in a let statement, then there is no explicit type.
        // let x = fn::<...>();
        let ident_range = let_stmt.pat()?.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace turbofish with explicit type",
            TextRange::new(initializer_start, turbofish_range.end()),
            |builder| {
                builder.insert(ident_range.end(), format!(": {}", returned_type));
                builder.delete(turbofish_range);
            },
        );
    } else if let Some(InferType(t)) = let_stmt.ty() {
        // If there's a type inference underscore, we can offer to replace it with the type in
        // the turbofish.
        // let x: _ = fn::<...>();
        let underscore_range = t.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace `_` with turbofish type",
            turbofish_range,
            |builder| {
                builder.replace(underscore_range, returned_type);
                builder.delete(turbofish_range);
            },
        );
    }

    None
}

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

    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn replaces_turbofish_for_vec_string() {
        cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_method_calls() {
        // foo.make() is a method call which uses a different expr in the let initializer
        cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = foo.make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = foo.make();
}
"#,
        );
    }

    #[test]
    fn replace_turbofish_target() {
        check_assist_target(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = $0make::<Vec<String>>();
}
"#,
            r#"make::<Vec<String>>"#,
        );
    }

    #[test]
    fn not_applicable_outside_turbofish() {
        cov_mark::check!(not_applicable_outside_turbofish);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let $0a = make::<Vec<String>>();
}
"#,
        );
    }

    #[test]
    fn replace_inferred_type_placeholder() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: _ = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn not_applicable_constant_initializer() {
        cov_mark::check!(not_applicable_if_non_function_call_initializer);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = "foo"$0;
}
"#,
        );
    }

    #[test]
    fn not_applicable_non_path_function_call() {
        cov_mark::check!(not_applicable_if_non_path_function_call);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    $0let a = (|| {})();
}
"#,
        );
    }

    #[test]
    fn non_applicable_multiple_generic_args() {
        cov_mark::check!(not_applicable_if_not_single_arg);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>, i32>();
}
"#,
        );
    }

    #[test]
    fn replaces_turbofish_for_known_type() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<i32>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: i32 = make();
}
"#,
        );
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
//- minicore: option
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Option<bool>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Option<bool> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_turbofish_not_same_type() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
//- minicore: option
fn make<T>() -> Option<T> {}
fn main() {
    let a = make$0::<u128>();
}
"#,
            r#"
fn make<T>() -> Option<T> {}
fn main() {
    let a: Option<u128> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_turbofish_for_type_with_defaulted_generic_param() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
struct HasDefault<T, U = i32>(T, U);
fn make<T>() -> HasDefault<T> {}
fn main() {
    let a = make$0::<bool>();
}
"#,
            r#"
struct HasDefault<T, U = i32>(T, U);
fn make<T>() -> HasDefault<T> {}
fn main() {
    let a: HasDefault<bool> = make();
}
"#,
        );
    }
}
<|endoftext|>


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

//! Defines a process type that includes all of the CSP language.

use std::fmt::Debug;
use std::fmt::Display;
use std::rc::Rc;

use auto_enums::enum_derive;
use auto_from::From;

use crate::internal_choice::InternalChoice;
use crate::prefix::Prefix;
use crate::primitives::Skip;
use crate::primitives::Stop;
use crate::process::Afters;
use crate::process::Initials;

/// A process type that includes all of the primitive processes and operators in the CSP language.
/// Note that you should never need to construct instances of this type directly; use the helper
/// constructor for each process (e.g. [`stop`]) or operator (TBD) instead.
///
/// [`stop`]: ../primitives/fn.stop.html
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct CSP<E>(Rc<CSPSig<E, CSP<E>>>);

impl<E: Display> Display for CSP<E> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        (self.0.as_ref() as &dyn Display).fmt(f)
    }
}

impl<E: Display> Debug for CSP<E> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        (self.0.as_ref() as &dyn Debug).fmt(f)
    }
}

impl<E, T> From<T> for CSP<E>
where
    CSPSig<E, CSP<E>>: From<T>,
{
    fn from(t: T) -> CSP<E> {
        CSP(Rc::new(CSPSig::from(t)))
    }
}

impl<'a, E> Initials<'a, E> for CSP<E>
where
    CSPSig<E, CSP<E>>: Initials<'a, E>,
{
    type Initials = <CSPSig<E, CSP<E>> as Initials<'a, E>>::Initials;

    fn initials(&'a self) -> Self::Initials {
        self.0.initials()
    }
}

impl<'a, E, P> Afters<'a, E, P> for CSP<E>
where
    CSPSig<E, CSP<E>>: Afters<'a, E, P>,
{
    type Afters = <CSPSig<E, CSP<E>> as Afters<'a, E, P>>::Afters;

    fn afters(&'a self, initial: &E) -> Self::Afters {
        self.0.afters(initial)
    }
}

#[doc(hidden)]
#[enum_derive(Debug, Display)]
#[derive(Clone, Eq, From, Hash, PartialEq)]
pub enum CSPSig<E, P> {
    #[doc(hidden)]
    Stop(Stop),
    #[doc(hidden)]
    Skip(Skip),
    #[doc(hidden)]
    Prefix(Prefix<E, P>),
    #[doc(hidden)]
    InternalChoice(InternalChoice<P>),
}

#[doc(hidden)]
#[enum_derive(Iterator)]
pub enum CSPIter<Stop, Skip, Prefix, InternalChoice> {
    Stop(Stop),
    Skip(Skip),
    Prefix(Prefix),
    InternalChoice(InternalChoice),
}

impl<'a, E, P> Initials<'a, E> for CSPSig<E, P>
where
    Stop: Initials<'a, E>,
    Skip: Initials<'a, E>,
    Prefix<E, P>: Initials<'a, E>,
    InternalChoice<P>: Initials<'a, E>,
{
    type Initials = CSPIter<
        <Stop as Initials<'a, E>>::Initials,
        <Skip as Initials<'a, E>>::Initials,
        <Prefix<E, P> as Initials<'a, E>>::Initials,
        <InternalChoice<P> as Initials<'a, E>>::Initials,
    >;

    fn initials(&'a self) -> Self::Initials {
        match self {
            CSPSig::Stop(this) => CSPIter::Stop(this.initials()),
            CSPSig::Skip(this) => CSPIter::Skip(this.initials()),
            CSPSig::Prefix(this) => CSPIter::Prefix(this.initials()),
            CSPSig::InternalChoice(this) => CSPIter::InternalChoice(this.initials()),
        }
    }
}

impl<'a, E, P> Afters<'a, E, P> for CSPSig<E, P>
where
    Stop: Afters<'a, E, P>,
    Skip: Afters<'a, E, P>,
    Prefix<E, P>: Afters<'a, E, P>,
    InternalChoice<P>: Afters<'a, E, P>,
{
    type Afters = CSPIter<
        <Stop as Afters<'a, E, P>>::Afters,
        <Skip as Afters<'a, E, P>>::Afters,
        <Prefix<E, P> as Afters<'a, E, P>>::Afters,
        <InternalChoice<P> as Afters<'a, E, P>>::Afters,
    >;

    fn afters(&'a self, initial: &E) -> Self::Afters {
        match self {
            CSPSig::Stop(this) => CSPIter::Stop(this.afters(initial)),
            CSPSig::Skip(this) => CSPIter::Skip(this.afters(initial)),
            CSPSig::Prefix(this) => CSPIter::Prefix(this.afters(initial)),
            CSPSig::InternalChoice(this) => CSPIter::InternalChoice(this.afters(initial)),
        }
    }
}

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

    use proptest::arbitrary::any;
    use proptest::arbitrary::Arbitrary;
    use proptest::collection::vec;
    use proptest::prop_oneof;
    use proptest::strategy::BoxedStrategy;
    use proptest::strategy::Just;
    use proptest::strategy::MapInto;
    use proptest::strategy::Strategy;

    use crate::internal_choice::internal_choice;
    use crate::internal_choice::replicated_internal_choice;
    use crate::prefix::prefix;
    use crate::primitives::skip;
    use crate::primitives::stop;
    use crate::test_support::NumberedEvent;
    use crate::test_support::TestEvent;

    pub trait NameableEvents {
        type Strategy: Strategy;
        fn nameable_events() -> Self::Strategy;
    }

    impl NameableEvents for TestEvent {
        type Strategy = MapInto<<NumberedEvent as Arbitrary>::Strategy, TestEvent>;
        fn nameable_events() -> Self::Strategy {
            any::<NumberedEvent>().prop_map_into()
        }
    }

    impl<E> Arbitrary for CSP<E>
    where
        E: Clone + Debug + Display + NameableEvents + 'static,
        E::Strategy: Strategy<Value = E>,
    {
        type Parameters = ();
        type Strategy = BoxedStrategy<CSP<E>>;

        fn arbitrary_with(_args: ()) -> Self::Strategy {
            let leaf = prop_oneof![Just(stop()), Just(skip())];
            leaf.prop_recursive(8, 256, 100, move |inner| {
                prop_oneof![
                    // We use NumberedEvent here because you shouldn't really create processes that
                    // explicitly refer to Tau and Tick; those should only be created as part of
                    // the CSP operators.
                    (E::nameable_events(), inner.clone())
                        .prop_map(|(initial, after)| prefix(initial.into(), after)),
                    (inner.clone(), inner.clone()).prop_map(|(p, q)| internal_choice(p, q)),
                    vec(inner.clone(), 1..100).prop_map(replicated_internal_choice),
                ]
            })
            .boxed()
        }
    }

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

//! Defines a process type that includes all of the CSP language.

use std::fmt::Debug;
use std::fmt::Display;
use std::rc::Rc;

use auto_enums::enum_derive;
use auto_from::From;

use crate::internal_choice::InternalChoice;
use crate::prefix::Prefix;
use crate::primitives::Skip;
use crate::primitives::Stop;
use crate::primitives::Tau;
use crate::primitives::Tick;
use crate::process::Afters;
use crate::process::Initials;

/// A process type that includes all of the primitive processes and operators in the CSP language.
/// Note that you should never need to construct instances of this type directly; use the helper
/// constructor for each process (e.g. [`stop`]) or operator (TBD) instead.
///
/// [`stop`]: ../primitives/fn.stop.html
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct CSP<E>(Rc<CSPSig<E, CSP<E>>>);

impl<E: Display> Display for CSP<E> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        (self.0.as_ref() as &dyn Display).fmt(f)
    }
}

impl<E: Display> Debug for CSP<E> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        (self.0.as_ref() as &dyn Debug).fmt(f)
    }
}

impl<E, T> From<T> for CSP<E>
where
    CSPSig<E, CSP<E>>: From<T>,
{
    fn from(t: T) -> CSP<E> {
        CSP(Rc::new(CSPSig::from(t)))
    }
}

impl<'a, E> Initials<'a, E> for CSP<E>
where
    E: Clone + From<Tau> + From<Tick> + 'a,
{
    type Initials = Box<dyn Iterator<Item = E> + 'a>;

    fn initials(&'a self) -> Self::Initials {
        Box::new(self.0.initials())
    }
}

impl<'a, E> Afters<'a, E, CSP<E>> for CSP<E>
where
    E: Clone + Eq + From<Tau> + From<Tick> + 'a,
{
    type Afters = Box<dyn Iterator<Item = CSP<E>> + 'a>;

    fn afters(&'a self, initial: &E) -> Self::Afters {
        Box::new(self.0.afters(initial))
    }
}

#[doc(hidden)]
#[enum_derive(Debug, Display)]
#[derive(Clone, Eq, From, Hash, PartialEq)]
pub enum CSPSig<E, P> {
    #[doc(hidden)]
    Stop(Stop),
    #[doc(hidden)]
    Skip(Skip),
    #[doc(hidden)]
    Prefix(Prefix<E, P>),
    #[doc(hidden)]
    InternalChoice(InternalChoice<P>),
}

#[doc(hidden)]
#[enum_derive(Iterator)]
pub enum CSPIter<Stop, Skip, Prefix, InternalChoice> {
    Stop(Stop),
    Skip(Skip),
    Prefix(Prefix),
    InternalChoice(InternalChoice),
}

impl<'a, E, P> Initials<'a, E> for CSPSig<E, P>
where
    Stop: Initials<'a, E>,
    Skip: Initials<'a, E>,
    Prefix<E, P>: Initials<'a, E>,
    InternalChoice<P>: Initials<'a, E>,
{
    type Initials = CSPIter<
        <Stop as Initials<'a, E>>::Initials,
        <Skip as Initials<'a, E>>::Initials,
        <Prefix<E, P> as Initials<'a, E>>::Initials,
        <InternalChoice<P> as Initials<'a, E>>::Initials,
    >;

    fn initials(&'a self) -> Self::Initials {
        match self {
            CSPSig::Stop(this) => CSPIter::Stop(this.initials()),
            CSPSig::Skip(this) => CSPIter::Skip(this.initials()),
            CSPSig::Prefix(this) => CSPIter::Prefix(this.initials()),
            CSPSig::InternalChoice(this) => CSPIter::InternalChoice(this.initials()),
        }
    }
}

impl<'a, E, P> Afters<'a, E, P> for CSPSig<E, P>
where
    Stop: Afters<'a, E, P>,
    Skip: Afters<'a, E, P>,
    Prefix<E, P>: Afters<'a, E, P>,
    InternalChoice<P>: Afters<'a, E, P>,
{
    type Afters = CSPIter<
        <Stop as Afters<'a, E, P>>::Afters,
        <Skip as Afters<'a, E, P>>::Afters,
        <Prefix<E, P> as Afters<'a, E, P>>::Afters,
        <InternalChoice<P> as Afters<'a, E, P>>::Afters,
    >;

    fn afters(&'a self, initial: &E) -> Self::Afters {
        match self {
            CSPSig::Stop(this) => CSPIter::Stop(this.afters(initial)),
            CSPSig::Skip(this) => CSPIter::Skip(this.afters(initial)),
            CSPSig::Prefix(this) => CSPIter::Prefix(this.afters(initial)),
            CSPSig::InternalChoice(this) => CSPIter::InternalChoice(this.afters(initial)),
        }
    }
}

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

    use proptest::arbitrary::any;
    use proptest::arbitrary::Arbitrary;
    use proptest::collection::vec;
    use proptest::prop_oneof;
    use proptest::strategy::BoxedStrategy;
    use proptest::strategy::Just;
    use proptest::strategy::MapInto;
    use proptest::strategy::Strategy;

    use crate::internal_choice::internal_choice;
    use crate::internal_choice::replicated_internal_choice;
    use crate::prefix::prefix;
    use crate::primitives::skip;
    use crate::primitives::stop;
    use crate::test_support::NumberedEvent;
    use crate::test_support::TestEvent;

    pub trait NameableEvents {
        type Strategy: Strategy;
        fn nameable_events() -> Self::Strategy;
    }

    impl NameableEvents for TestEvent {
        type Strategy = MapInto<<NumberedEvent as Arbitrary>::Strategy, TestEvent>;
        fn nameable_events() -> Self::Strategy {
            any::<NumberedEvent>().prop_map_into()
        }
    }

    impl<E> Arbitrary for CSP<E>
    where
        E: Clone + Debug + Display + NameableEvents + 'static,
        E::Strategy: Strategy<Value = E>,
    {
        type Parameters = ();
        type Strategy = BoxedStrategy<CSP<E>>;

        fn arbitrary_with(_args: ()) -> Self::Strategy {
            let leaf = prop_oneof![Just(stop()), Just(skip())];
            leaf.prop_recursive(8, 256, 100, move |inner| {
                prop_oneof![
                    // We use NumberedEvent here because you shouldn't really create processes that
                    // explicitly refer to Tau and Tick; those should only be created as part of
                    // the CSP operators.
                    (E::nameable_events(), inner.clone())
                        .prop_map(|(initial, after)| prefix(initial.into(), after)),
                    (inner.clone(), inner.clone()).prop_map(|(p, q)| internal_choice(p, q)),
                    vec(inner.clone(), 1..100).prop_map(replicated_internal_choice),
                ]
            })
            .boxed()
        }
    }

}
<|endoftext|>


<commit_before>extern crate alsa;
extern crate libc;
extern crate nix;

use std;
use std::ffi::CString;
use std::error;
use time::{Time, TimeDelta};
use base::*;

use super::input::*;

const CHANNELS: usize = 2;

const ACCEPTED_RATES: [usize; 4] = [44100, 48000, 88200, 96000];
const MAX_SAMPLE_RATE_ERROR: f32 = 1000.0;

enum State {
    Inactive,
    Active { silent_samples: usize },
}

pub struct AlsaInput {
    spec: DeviceSpec,
    pcm: alsa::PCM,
    sample_rate: usize,
    format: SampleFormat,
    period_size: usize,
    rate_detector: RateDetector,
    exact_rate_detector: RateDetector,
    state: State,
    current_rate: DetectedRate,
    last_rate_update: Time,
}

// Deactivate input after 5 seconds of silence.
const SILENCE_PERIOD_SECONDS: usize = 5;

impl AlsaInput {
    pub fn open(spec: DeviceSpec, period_duration: TimeDelta) -> Result<Box<AlsaInput>> {
        let pcm = try!(alsa::PCM::open(
            &*CString::new(&spec.id[..]).unwrap(),
            alsa::Direction::Capture,
            false
        ));

        let mut sample_rate = spec.sample_rate.unwrap_or(48000);
        let period_size;
        let format;
        {
            let hwp = try!(alsa::pcm::HwParams::any(&pcm));
            try!(hwp.set_channels(CHANNELS as u32));
            try!(hwp.set_rate(sample_rate as u32, alsa::ValueOr::Nearest));

            for fmt in [
                alsa::pcm::Format::S32LE,
                alsa::pcm::Format::S243LE,
                alsa::pcm::Format::S24LE,
                alsa::pcm::Format::S16LE,
            ].iter()
            {
                match hwp.set_format(*fmt) {
                    Ok(_) => break,
                    Err(_) => (),
                }
            }

            let target_period_size = period_duration * sample_rate as i64 / TimeDelta::seconds(1);
            try!(hwp.set_period_size_near(
                target_period_size as alsa::pcm::Frames,
                alsa::ValueOr::Nearest
            ));
            try!(hwp.set_periods(8, alsa::ValueOr::Nearest));
            try!(hwp.set_rate_resample(false));
            try!(hwp.set_access(alsa::pcm::Access::RWInterleaved));
            try!(pcm.hw_params(&hwp));

            sample_rate = try!(hwp.get_rate()) as usize;
            period_size = try!(hwp.get_period_size()) as usize;
            format = match try!(hwp.get_format()) {
                alsa::pcm::Format::S16LE => SampleFormat::S16LE,
                alsa::pcm::Format::S243LE => SampleFormat::S24LE3,
                alsa::pcm::Format::S24LE => SampleFormat::S24LE4,
                alsa::pcm::Format::S32LE => SampleFormat::S32LE,
                fmt => return Err(Error::new(&format!("Unknown format: {:?}", fmt))),
            };

            println!(
                "INFO: Reading {} ({}). Buffer size: {}x{}. {} {}",
                spec.id,
                &spec.name,
                period_size,
                try!(hwp.get_periods()),
                sample_rate,
                format
            );
        }

        Ok(Box::new(AlsaInput {
            spec: spec,
            pcm: pcm,
            sample_rate: sample_rate,
            format: format,
            period_size: period_size,
            rate_detector: RateDetector::new(MAX_SAMPLE_RATE_ERROR),
            exact_rate_detector: RateDetector::new(1.0),
            state: State::Inactive,
            current_rate: DetectedRate { rate: sample_rate as f32, error: sample_rate as f32 },
            last_rate_update: Time::now(),
        }))
    }
}

impl Input for AlsaInput {
    fn read(&mut self) -> Result<Option<Frame>> {
        let mut buf = vec![0u8; self.period_size * CHANNELS * self.format.bytes_per_sample()];
        let read_result = self.pcm.io().readi(&mut buf[..]);
        let samples = match read_result {
            Ok(0) => return Ok(None),
            Ok(r) => r,
            Err(e) => {
                if e.errno().unwrap_or(nix::Errno::UnknownErrno) == nix::errno::EWOULDBLOCK {
                    return Ok(None);
                }
                println!("Recovering AlsaInput {}: {:?}", &self.spec.name, e.errno());
                match self.pcm
                    .recover(e.errno().unwrap_or(nix::Errno::UnknownErrno) as i32, true)
                {
                    Ok(_) => return self.read(),
                    Err(_) => return Err(Error::new(error::Error::description(&e))),
                }
            }
        };

        assert!(samples > 0);

        let now = Time::now();
        let rate = self.rate_detector.update(samples, now);
        if (self.sample_rate as f32 - rate.rate).abs() > MAX_SAMPLE_RATE_ERROR &&
           rate.error < MAX_SAMPLE_RATE_ERROR {
            let mut new_rate = 0;
            for r in &ACCEPTED_RATES[..] {
                if (rate.rate - *r as f32).abs() < MAX_SAMPLE_RATE_ERROR {
                    new_rate = *r;
                }
            }

            if new_rate > 0 {
                println!(
                    "INFO: rate changed for input device {}: {}",
                    &self.spec.name, new_rate
                );
                self.sample_rate = new_rate;
                self.current_rate = DetectedRate { rate: new_rate as f32, error: MAX_SAMPLE_RATE_ERROR };
                self.exact_rate_detector.reset();
            } else if rate.error < 100.0 {
                println!(
                    "WARNING: Unknown sample rate for {}: {}",
                    &self.spec.name, rate.rate
                );
            }
        }

        if self.spec.exact_sample_rate {
            let rate = self.rate_detector.update(samples, now);

            if (now - self.last_rate_update) > TimeDelta::milliseconds(500) {
               self.last_rate_update = now;
               if rate.error < 10.0 {
                    self.current_rate = rate;
               }
            }
        }

        let bytes = samples * CHANNELS * self.format.bytes_per_sample();

        let mut all_zeros = true;
        for i in 0..bytes {
            if buf[i] != 0 {
                all_zeros = false;
                break;
            }
        }

        self.state = match (&self.state, all_zeros) {
            (_, false) => State::Active { silent_samples: 0 },
            (&State::Active { silent_samples }, true) => {
                let silence = silent_samples + samples;
                if silence > SILENCE_PERIOD_SECONDS * self.sample_rate {
                    State::Inactive
                } else {
                    State::Active {
                        silent_samples: silence,
                    }
                }
            }
            (&State::Inactive, true) => State::Inactive,
        };

        match self.state {
            State::Inactive => return Ok(None),
            _ => (),
        }

        let mut frame = Frame::from_buffer_stereo(
            self.format, self.current_rate.rate, &buf[0..bytes], Time::now());
        frame.timestamp -= frame.duration();

        Ok(Some(frame))
    }

    fn min_delay(&self) -> TimeDelta {
        samples_to_timedelta(self.sample_rate as f32, self.period_size as i64)
    }
}

const TARGET_SAMPLE_RATES: [usize; 2] = [44100, 48000];

const RETRY_PERIOD_MS: i64 = 3000;
const PROBE_TIME_MS: i64 = 500;

pub struct ResilientAlsaInput {
    spec: DeviceSpec,
    period_duration: TimeDelta,
    input: Option<Box<AlsaInput>>,
    last_open_attempt: Time,
    probe_sample_rate: bool,
    last_active: Time,
    next_sample_rate_to_probe: usize,
}

impl ResilientAlsaInput {
    pub fn new(spec: DeviceSpec, period_duration: TimeDelta, probe_sample_rate: bool) -> Box<ResilientAlsaInput> {
        Box::new(ResilientAlsaInput {
            spec: spec,
            period_duration: period_duration,
            input: None,
            probe_sample_rate: probe_sample_rate,
            last_open_attempt: Time::now(),
            last_active: Time::now(),
            next_sample_rate_to_probe: 0,
        })
    }

    fn try_reopen(&mut self) {
        let now = Time::now();
        if now - self.last_open_attempt >= TimeDelta::milliseconds(RETRY_PERIOD_MS) {
            self.last_open_attempt = now;
            let mut spec = self.spec.clone();
            if spec.sample_rate.is_none() {
                spec.sample_rate = Some(TARGET_SAMPLE_RATES[self.next_sample_rate_to_probe]);
                self.next_sample_rate_to_probe =
                    (self.next_sample_rate_to_probe + 1) % TARGET_SAMPLE_RATES.len();
            }
            match AlsaInput::open(spec, self.period_duration) {
                Ok(input) => {
                    self.input = Some(input);
                    self.last_active = now;
                }
                Err(_) => (),
            };
        }
    }
}

impl Input for ResilientAlsaInput {
    fn read(&mut self) -> Result<Option<Frame>> {
        if self.input.is_none() {
            self.try_reopen()
        }
        let mut reset = false;
        let result = match self.input.as_mut() {
            Some(input) => match input.read() {
                Ok(Some(frame)) => {
                    self.last_active = Time::now();
                    Some(frame)
                }
                Ok(None) => {
                    if self.probe_sample_rate &&
                       Time::now() - self.last_active > TimeDelta::milliseconds(PROBE_TIME_MS) {
                        reset = true;
                    }
                    None
                }
                Err(e) => {
                    println!("WARNING: input read from {} failed: {}", &self.spec.name, e);
                    reset = true;
                    None
                }
            },
            None => None,
        };
        if reset {
            self.input = None;
        }
        if self.input.is_none() {
            std::thread::sleep(self.period_duration.as_duration());
        }

        Ok(result)
    }

    fn min_delay(&self) -> TimeDelta {
        match self.input.as_ref() {
            Some(input) => input.min_delay(),
            None => self.period_duration,
        }
    }
}
<commit_msg>Fix input rate detection<commit_after>extern crate alsa;
extern crate libc;
extern crate nix;

use std;
use std::ffi::CString;
use std::error;
use time::{Time, TimeDelta};
use base::*;

use super::input::*;

const CHANNELS: usize = 2;

const ACCEPTED_RATES: [usize; 4] = [44100, 48000, 88200, 96000];
const MAX_SAMPLE_RATE_ERROR: f32 = 1000.0;

enum State {
    Inactive,
    Active { silent_samples: usize },
}

pub struct AlsaInput {
    spec: DeviceSpec,
    pcm: alsa::PCM,
    sample_rate: usize,
    format: SampleFormat,
    period_size: usize,
    rate_detector: RateDetector,
    exact_rate_detector: RateDetector,
    state: State,
    current_rate: DetectedRate,
    last_rate_update: Time,
}

// Deactivate input after 5 seconds of silence.
const SILENCE_PERIOD_SECONDS: usize = 5;

impl AlsaInput {
    pub fn open(spec: DeviceSpec, period_duration: TimeDelta) -> Result<Box<AlsaInput>> {
        let pcm = try!(alsa::PCM::open(
            &*CString::new(&spec.id[..]).unwrap(),
            alsa::Direction::Capture,
            false
        ));

        let mut sample_rate = spec.sample_rate.unwrap_or(48000);
        let period_size;
        let format;
        {
            let hwp = try!(alsa::pcm::HwParams::any(&pcm));
            try!(hwp.set_channels(CHANNELS as u32));
            try!(hwp.set_rate(sample_rate as u32, alsa::ValueOr::Nearest));

            for fmt in [
                alsa::pcm::Format::S32LE,
                alsa::pcm::Format::S243LE,
                alsa::pcm::Format::S24LE,
                alsa::pcm::Format::S16LE,
            ].iter()
            {
                match hwp.set_format(*fmt) {
                    Ok(_) => break,
                    Err(_) => (),
                }
            }

            let target_period_size = period_duration * sample_rate as i64 / TimeDelta::seconds(1);
            try!(hwp.set_period_size_near(
                target_period_size as alsa::pcm::Frames,
                alsa::ValueOr::Nearest
            ));
            try!(hwp.set_periods(8, alsa::ValueOr::Nearest));
            try!(hwp.set_rate_resample(false));
            try!(hwp.set_access(alsa::pcm::Access::RWInterleaved));
            try!(pcm.hw_params(&hwp));

            sample_rate = try!(hwp.get_rate()) as usize;
            period_size = try!(hwp.get_period_size()) as usize;
            format = match try!(hwp.get_format()) {
                alsa::pcm::Format::S16LE => SampleFormat::S16LE,
                alsa::pcm::Format::S243LE => SampleFormat::S24LE3,
                alsa::pcm::Format::S24LE => SampleFormat::S24LE4,
                alsa::pcm::Format::S32LE => SampleFormat::S32LE,
                fmt => return Err(Error::new(&format!("Unknown format: {:?}", fmt))),
            };

            println!(
                "INFO: Reading {} ({}). Buffer size: {}x{}. {} {}",
                spec.id,
                &spec.name,
                period_size,
                try!(hwp.get_periods()),
                sample_rate,
                format
            );
        }

        Ok(Box::new(AlsaInput {
            spec: spec,
            pcm: pcm,
            sample_rate: sample_rate,
            format: format,
            period_size: period_size,
            rate_detector: RateDetector::new(MAX_SAMPLE_RATE_ERROR),
            exact_rate_detector: RateDetector::new(1.0),
            state: State::Inactive,
            current_rate: DetectedRate { rate: sample_rate as f32, error: sample_rate as f32 },
            last_rate_update: Time::now(),
        }))
    }
}

impl Input for AlsaInput {
    fn read(&mut self) -> Result<Option<Frame>> {
        let mut buf = vec![0u8; self.period_size * CHANNELS * self.format.bytes_per_sample()];
        let read_result = self.pcm.io().readi(&mut buf[..]);
        let samples = match read_result {
            Ok(0) => return Ok(None),
            Ok(r) => r,
            Err(e) => {
                if e.errno().unwrap_or(nix::Errno::UnknownErrno) == nix::errno::EWOULDBLOCK {
                    return Ok(None);
                }
                println!("Recovering AlsaInput {}: {:?}", &self.spec.name, e.errno());
                match self.pcm
                    .recover(e.errno().unwrap_or(nix::Errno::UnknownErrno) as i32, true)
                {
                    Ok(_) => return self.read(),
                    Err(_) => return Err(Error::new(error::Error::description(&e))),
                }
            }
        };

        assert!(samples > 0);

        let now = Time::now();
        let rate = self.rate_detector.update(samples, now);
        if (self.sample_rate as f32 - rate.rate).abs() > MAX_SAMPLE_RATE_ERROR &&
           rate.error < MAX_SAMPLE_RATE_ERROR {
            let mut new_rate = 0;
            for r in &ACCEPTED_RATES[..] {
                if (rate.rate - *r as f32).abs() < MAX_SAMPLE_RATE_ERROR {
                    new_rate = *r;
                }
            }

            if new_rate > 0 {
                println!(
                    "INFO: rate changed for input device {}: {}",
                    &self.spec.name, new_rate
                );
                self.sample_rate = new_rate;
                self.current_rate = DetectedRate { rate: new_rate as f32, error: MAX_SAMPLE_RATE_ERROR };
                self.exact_rate_detector.reset();
            } else if rate.error < 100.0 {
                println!(
                    "WARNING: Unknown sample rate for {}: {}",
                    &self.spec.name, rate.rate
                );
            }
        }

        if self.spec.exact_sample_rate {
            let rate = self.exact_rate_detector.update(samples, now);

            if (now - self.last_rate_update) > TimeDelta::milliseconds(500) {
               self.last_rate_update = now;
               if rate.error < 10.0 {
                    self.current_rate = rate;
               }
            }
        }

        let bytes = samples * CHANNELS * self.format.bytes_per_sample();

        let mut all_zeros = true;
        for i in 0..bytes {
            if buf[i] != 0 {
                all_zeros = false;
                break;
            }
        }

        self.state = match (&self.state, all_zeros) {
            (_, false) => State::Active { silent_samples: 0 },
            (&State::Active { silent_samples }, true) => {
                let silence = silent_samples + samples;
                if silence > SILENCE_PERIOD_SECONDS * self.sample_rate {
                    State::Inactive
                } else {
                    State::Active {
                        silent_samples: silence,
                    }
                }
            }
            (&State::Inactive, true) => State::Inactive,
        };

        match self.state {
            State::Inactive => return Ok(None),
            _ => (),
        }

        let mut frame = Frame::from_buffer_stereo(
            self.format, self.current_rate.rate, &buf[0..bytes], Time::now());
        frame.timestamp -= frame.duration();

        Ok(Some(frame))
    }

    fn min_delay(&self) -> TimeDelta {
        samples_to_timedelta(self.sample_rate as f32, self.period_size as i64)
    }
}

const TARGET_SAMPLE_RATES: [usize; 2] = [44100, 48000];

const RETRY_PERIOD_MS: i64 = 3000;
const PROBE_TIME_MS: i64 = 500;

pub struct ResilientAlsaInput {
    spec: DeviceSpec,
    period_duration: TimeDelta,
    input: Option<Box<AlsaInput>>,
    last_open_attempt: Time,
    probe_sample_rate: bool,
    last_active: Time,
    next_sample_rate_to_probe: usize,
}

impl ResilientAlsaInput {
    pub fn new(spec: DeviceSpec, period_duration: TimeDelta, probe_sample_rate: bool) -> Box<ResilientAlsaInput> {
        Box::new(ResilientAlsaInput {
            spec: spec,
            period_duration: period_duration,
            input: None,
            probe_sample_rate: probe_sample_rate,
            last_open_attempt: Time::now(),
            last_active: Time::now(),
            next_sample_rate_to_probe: 0,
        })
    }

    fn try_reopen(&mut self) {
        let now = Time::now();
        if now - self.last_open_attempt >= TimeDelta::milliseconds(RETRY_PERIOD_MS) {
            self.last_open_attempt = now;
            let mut spec = self.spec.clone();
            if spec.sample_rate.is_none() {
                spec.sample_rate = Some(TARGET_SAMPLE_RATES[self.next_sample_rate_to_probe]);
                self.next_sample_rate_to_probe =
                    (self.next_sample_rate_to_probe + 1) % TARGET_SAMPLE_RATES.len();
            }
            match AlsaInput::open(spec, self.period_duration) {
                Ok(input) => {
                    self.input = Some(input);
                    self.last_active = now;
                }
                Err(_) => (),
            };
        }
    }
}

impl Input for ResilientAlsaInput {
    fn read(&mut self) -> Result<Option<Frame>> {
        if self.input.is_none() {
            self.try_reopen()
        }
        let mut reset = false;
        let result = match self.input.as_mut() {
            Some(input) => match input.read() {
                Ok(Some(frame)) => {
                    self.last_active = Time::now();
                    Some(frame)
                }
                Ok(None) => {
                    if self.probe_sample_rate &&
                       Time::now() - self.last_active > TimeDelta::milliseconds(PROBE_TIME_MS) {
                        reset = true;
                    }
                    None
                }
                Err(e) => {
                    println!("WARNING: input read from {} failed: {}", &self.spec.name, e);
                    reset = true;
                    None
                }
            },
            None => None,
        };
        if reset {
            self.input = None;
        }
        if self.input.is_none() {
            std::thread::sleep(self.period_duration.as_duration());
        }

        Ok(result)
    }

    fn min_delay(&self) -> TimeDelta {
        match self.input.as_ref() {
            Some(input) => input.min_delay(),
            None => self.period_duration,
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

mod driver;
use driver::*;

#[test]
fn test3() {
    test(NO_VERIFY, &["test_mvir/test3.mvir"]);
}

#[test]
fn test_arithmetic() {
    test(NO_VERIFY, &["test_mvir/test-arithmetic.mvir"]);
}

#[test]
fn test_control_flow() {
    test(NO_VERIFY, &["test_mvir/test-control-flow.mvir"]);
}

#[test]
fn test_func_call() {
    test(NO_VERIFY, &["test_mvir/test-func-call.mvir"]);
}

#[test]
fn test_reference() {
    test(NO_VERIFY, &["test_mvir/test-reference.mvir"]);
}

#[test]
fn test_struct() {
    test(NO_VERIFY, &["test_mvir/test-struct.mvir"]);
}

#[test]
fn test_aborts_if() {
    test(VERIFY, &["test_mvir/test-aborts-if.mvir"]);
}

#[test]
fn test_access_path() {
    test(
        VERIFY,
        &[
            &std_mvir("u64_util"),
            &std_mvir("address_util"),
            &std_mvir("bytearray_util"),
            &std_mvir("hash"),
            &std_mvir("libra_coin"),
            &std_mvir("libra_account"),
            "test_mvir/test-access-path.mvir",
        ],
    );
}

#[test]
fn test_lib() {
    test(
        // We have not yet created .prover.bpl stubs for native functions in library, so instruct
        // translator to generate them.
        &["--native-stubs", "-B=-noVerify"],
        &[
            &std_mvir("vector"),
            &std_mvir("u64_util"),
            &std_mvir("address_util"),
            &std_mvir("bytearray_util"),
            &std_mvir("hash"),
            &std_mvir("signature"),
            &std_mvir("gas_schedule"),
            &std_mvir("validator_config"),
            &std_mvir("libra_coin"),
            &std_mvir("libra_account"),
            // TODO(wrwg): this currently fails with boogie compilation errors
            //   call to undeclared procedure: Vector_contains (etc)
            // &std_mvir("libra_system"),
            "test_mvir/test-lib.mvir",
        ],
    );
}

#[test]
fn test_generics() {
    test(
        NO_VERIFY,
        &[&std_mvir("vector"), "test_mvir/test-generics.mvir"],
    );
}

#[test]
fn test_specs_translate() {
    test(NO_VERIFY, &["test_mvir/test-specs-translate.mvir"]);
}
<commit_msg>Update translator_tests.rs<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

mod driver;
use driver::*;

#[test]
fn test3() {
    test(NO_VERIFY, &["test_mvir/test3.mvir"]);
}

#[test]
fn test_arithmetic() {
    test(NO_VERIFY, &["test_mvir/test-arithmetic.mvir"]);
}

#[test]
fn test_control_flow() {
    test(NO_VERIFY, &["test_mvir/test-control-flow.mvir"]);
}

#[test]
fn test_func_call() {
    test(NO_VERIFY, &["test_mvir/test-func-call.mvir"]);
}

#[test]
fn test_reference() {
    test(NO_VERIFY, &["test_mvir/test-reference.mvir"]);
}

#[test]
fn test_struct() {
    test(NO_VERIFY, &["test_mvir/test-struct.mvir"]);
}

#[test]
fn test_aborts_if() {
    test(VERIFY, &["test_mvir/test-aborts-if.mvir"]);
}

#[test]
fn test_access_path() {
    test(
        &["--native-stubs"],
        &[
            &std_mvir("u64_util"),
            &std_mvir("address_util"),
            &std_mvir("bytearray_util"),
            &std_mvir("hash"),
            &std_mvir("libra_coin"),
            &std_mvir("libra_account"),
            "test_mvir/test-access-path.mvir",
        ],
    );
}

#[test]
fn test_lib() {
    test(
        // We have not yet created .prover.bpl stubs for native functions in library, so instruct
        // translator to generate them.
        &["--native-stubs", "-B=-noVerify"],
        &[
            &std_mvir("vector"),
            &std_mvir("u64_util"),
            &std_mvir("address_util"),
            &std_mvir("bytearray_util"),
            &std_mvir("hash"),
            &std_mvir("signature"),
            &std_mvir("gas_schedule"),
            &std_mvir("validator_config"),
            &std_mvir("libra_coin"),
            &std_mvir("libra_account"),
            // TODO(wrwg): this currently fails with boogie compilation errors
            //   call to undeclared procedure: Vector_contains (etc)
            // &std_mvir("libra_system"),
            "test_mvir/test-lib.mvir",
        ],
    );
}

#[test]
fn test_generics() {
    test(
        NO_VERIFY,
        &[&std_mvir("vector"), "test_mvir/test-generics.mvir"],
    );
}

#[test]
fn test_specs_translate() {
    test(NO_VERIFY, &["test_mvir/test-specs-translate.mvir"]);
}
<|endoftext|>


<commit_before><commit_msg>Use libc++ on FreeBSD.<commit_after><|endoftext|>


<commit_before><commit_msg>Rebuild when clang_sys environment changes<commit_after><|endoftext|>


<commit_before>// Copyright 2017 Rich Lane <lanerl@gmail.com>
//
// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! This module parses eBPF assembly language source code.

use combine::char::{alpha_num, char, digit, hex_digit, spaces, string};
use combine::{between, eof, many, many1, one_of, optional, Parser, ParseError, ParseResult, parser,
              sep_by, try, State, Stream};
use combine::primitives::{Error, Info};

/// Operand of an instruction.
#[derive(Debug, PartialEq)]
pub enum Operand {
    /// Register number.
    Register(i64),
    /// Jump offset or immediate.
    Integer(i64),
    /// Register number and offset.
    Memory(i64, i64),
}

/// Parsed instruction.
#[derive(Debug, PartialEq)]
pub struct Instruction {
    /// Instruction name.
    pub name: String,
    /// Operands.
    pub operands: Vec<Operand>,
}

fn ident<I>(input: I) -> ParseResult<String, I>
    where I: Stream<Item = char>
{
    many1(alpha_num()).parse_stream(input)
}

fn integer<I>(input: I) -> ParseResult<i64, I>
    where I: Stream<Item = char>
{
    let sign = optional(one_of("-+".chars())).map(|x| match x {
        Some('-') => -1,
        _ => 1,
    });
    let hex =
        string("0x").with(many1(hex_digit())).map(|x: String| i64::from_str_radix(&x, 16).unwrap());
    let dec = many1(digit()).map(|x: String| i64::from_str_radix(&x, 10).unwrap());
    (sign, try(hex).or(dec)).map(|(s, x)| s * x).parse_stream(input)
}

fn register<I>(input: I) -> ParseResult<i64, I>
    where I: Stream<Item = char>
{
    char('r')
        .with(many1(digit()))
        .map(|x: String| i64::from_str_radix(&x, 10).unwrap())
        .parse_stream(input)
}

fn operand<I>(input: I) -> ParseResult<Operand, I>
    where I: Stream<Item = char>
{
    let register_operand = parser(register).map(Operand::Register);
    let immediate = parser(integer).map(Operand::Integer);
    let memory = between(char('['),
                         char(']'),
                         (parser(register), optional(parser(integer))))
        .map(|t| Operand::Memory(t.0, t.1.unwrap_or(0)));
    register_operand.or(immediate).or(memory).parse_stream(input)
}

fn instruction<I>(input: I) -> ParseResult<Instruction, I>
    where I: Stream<Item = char>
{
    let operands = sep_by(parser(operand), char(',').skip(spaces()));
    (parser(ident).skip(spaces()), operands, spaces())
        .map(|t| {
            Instruction {
                name: t.0,
                operands: t.1,
            }
        })
        .parse_stream(input)
}

fn format_info(info: &Info<char, &str>) -> String {
    match *info {
        Info::Token(x) => format!("{:?}", x),
        Info::Range(ref x) => format!("{:?}", x),
        Info::Owned(ref x) => x.clone(),
        Info::Borrowed(ref x) => x.to_string(),
    }
}

fn format_error(error: &Error<char, &str>) -> String {
    match *error {
        Error::Unexpected(ref x) => format!("unexpected {}", format_info(x)),
        Error::Expected(ref x) => format!("expected {}", format_info(x)),
        Error::Message(ref x) => format_info(x),
        Error::Other(ref x) => format!("{:?}", x),
    }
}

fn format_parse_error(parse_error: ParseError<State<&str>>) -> String {
    format!("Parse error at line {} column {}: {}",
            parse_error.position.line,
            parse_error.position.column,
            parse_error.errors.iter().map(format_error).collect::<Vec<String>>().join(", "))
}

/// Parse a string into a list of instructions.
///
/// The instructions are not validated and may have invalid names and operand types.
pub fn parse(input: &str) -> Result<Vec<Instruction>, String> {
    match many(parser(instruction)).skip(eof()).parse(State::new(input)) {
        Ok((insts, _)) => Ok(insts),
        Err(err) => Err(format_parse_error(err)),
    }
}

#[cfg(test)]
mod tests {
    use combine::{Parser, parser};
    use super::{ident, integer, register, operand, instruction, Operand, Instruction};

    #[test]
    fn test_ident() {
        assert_eq!(parser(ident).parse("nop"), Ok(("nop".to_string(), "")));
        assert_eq!(parser(ident).parse("add32"), Ok(("add32".to_string(), "")));
        assert_eq!(parser(ident).parse("add32*"),
                   Ok(("add32".to_string(), "*")));
    }

    #[test]
    fn test_integer() {
        assert_eq!(parser(integer).parse("0"), Ok((0, "")));
        assert_eq!(parser(integer).parse("42"), Ok((42, "")));
        assert_eq!(parser(integer).parse("+42"), Ok((42, "")));
        assert_eq!(parser(integer).parse("-42"), Ok((-42, "")));
        assert_eq!(parser(integer).parse("0x0"), Ok((0, "")));
        assert_eq!(parser(integer).parse("0x123456789abcdef0"),
                   Ok((0x123456789abcdef0, "")));
        assert_eq!(parser(integer).parse("-0x1f"), Ok((-31, "")));
    }

    #[test]
    fn test_register() {
        assert_eq!(parser(register).parse("r0"), Ok((0, "")));
        assert_eq!(parser(register).parse("r15"), Ok((15, "")));
    }

    #[test]
    fn test_operand() {
        assert_eq!(parser(operand).parse("r0"), Ok((Operand::Register(0), "")));
        assert_eq!(parser(operand).parse("r15"),
                   Ok((Operand::Register(15), "")));
        assert_eq!(parser(operand).parse("0"), Ok((Operand::Integer(0), "")));
        assert_eq!(parser(operand).parse("42"), Ok((Operand::Integer(42), "")));
        assert_eq!(parser(operand).parse("[r1]"),
                   Ok((Operand::Memory(1, 0), "")));
        assert_eq!(parser(operand).parse("[r3+5]"),
                   Ok((Operand::Memory(3, 5), "")));
        assert_eq!(parser(operand).parse("[r3+0x1f]"),
                   Ok((Operand::Memory(3, 31), "")));
        assert_eq!(parser(operand).parse("[r3-0x1f]"),
                   Ok((Operand::Memory(3, -31), "")));
    }

    #[test]
    fn test_instruction() {
        assert_eq!(parser(instruction).parse("exit"),
                   Ok((Instruction {
                           name: "exit".to_string(),
                           operands: vec![],
                       },
                       "")));

        assert_eq!(parser(instruction).parse("call 2"),
                   Ok((Instruction {
                           name: "call".to_string(),
                           operands: vec![Operand::Integer(2)],
                       },
                       "")));

        assert_eq!(parser(instruction).parse("addi r1, 2"),
                   Ok((Instruction {
                           name: "addi".to_string(),
                           operands: vec![Operand::Register(1), Operand::Integer(2)],
                       },
                       "")));

        assert_eq!(parser(instruction).parse("ldxb r2, [r1+12]"),
                   Ok((Instruction {
                           name: "ldxb".to_string(),
                           operands: vec![Operand::Register(2), Operand::Memory(1, 12)],
                       },
                       "")));

        assert_eq!(parser(instruction).parse("lsh r3, 0x8"),
                   Ok((Instruction {
                           name: "lsh".to_string(),
                           operands: vec![Operand::Register(3), Operand::Integer(8)],
                       },
                       "")));

        assert_eq!(parser(instruction).parse("jne r3, 0x8, +37"),
                   Ok((Instruction {
                           name: "jne".to_string(),
                           operands: vec![Operand::Register(3),
                                          Operand::Integer(8),
                                          Operand::Integer(37)],
                       },
                       "")));

        // Whitespace between operands is optional.
        assert_eq!(parser(instruction).parse("jne r3,0x8,+37"),
                   Ok((Instruction {
                           name: "jne".to_string(),
                           operands: vec![Operand::Register(3),
                                          Operand::Integer(8),
                                          Operand::Integer(37)],
                       },
                       "")));
    }

}
<commit_msg>src/asm_parser.rs: remove unnecessary refs<commit_after>// Copyright 2017 Rich Lane <lanerl@gmail.com>
//
// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! This module parses eBPF assembly language source code.

use combine::char::{alpha_num, char, digit, hex_digit, spaces, string};
use combine::{between, eof, many, many1, one_of, optional, Parser, ParseError, ParseResult, parser,
              sep_by, try, State, Stream};
use combine::primitives::{Error, Info};

/// Operand of an instruction.
#[derive(Debug, PartialEq)]
pub enum Operand {
    /// Register number.
    Register(i64),
    /// Jump offset or immediate.
    Integer(i64),
    /// Register number and offset.
    Memory(i64, i64),
}

/// Parsed instruction.
#[derive(Debug, PartialEq)]
pub struct Instruction {
    /// Instruction name.
    pub name: String,
    /// Operands.
    pub operands: Vec<Operand>,
}

fn ident<I>(input: I) -> ParseResult<String, I>
    where I: Stream<Item = char>
{
    many1(alpha_num()).parse_stream(input)
}

fn integer<I>(input: I) -> ParseResult<i64, I>
    where I: Stream<Item = char>
{
    let sign = optional(one_of("-+".chars())).map(|x| match x {
        Some('-') => -1,
        _ => 1,
    });
    let hex =
        string("0x").with(many1(hex_digit())).map(|x: String| i64::from_str_radix(&x, 16).unwrap());
    let dec = many1(digit()).map(|x: String| i64::from_str_radix(&x, 10).unwrap());
    (sign, try(hex).or(dec)).map(|(s, x)| s * x).parse_stream(input)
}

fn register<I>(input: I) -> ParseResult<i64, I>
    where I: Stream<Item = char>
{
    char('r')
        .with(many1(digit()))
        .map(|x: String| i64::from_str_radix(&x, 10).unwrap())
        .parse_stream(input)
}

fn operand<I>(input: I) -> ParseResult<Operand, I>
    where I: Stream<Item = char>
{
    let register_operand = parser(register).map(Operand::Register);
    let immediate = parser(integer).map(Operand::Integer);
    let memory = between(char('['),
                         char(']'),
                         (parser(register), optional(parser(integer))))
        .map(|t| Operand::Memory(t.0, t.1.unwrap_or(0)));
    register_operand.or(immediate).or(memory).parse_stream(input)
}

fn instruction<I>(input: I) -> ParseResult<Instruction, I>
    where I: Stream<Item = char>
{
    let operands = sep_by(parser(operand), char(',').skip(spaces()));
    (parser(ident).skip(spaces()), operands, spaces())
        .map(|t| {
            Instruction {
                name: t.0,
                operands: t.1,
            }
        })
        .parse_stream(input)
}

fn format_info(info: &Info<char, &str>) -> String {
    match *info {
        Info::Token(x) => format!("{:?}", x),
        Info::Range(x) => format!("{:?}", x),
        Info::Owned(ref x) => x.clone(),
        Info::Borrowed(x) => x.to_string(),
    }
}

fn format_error(error: &Error<char, &str>) -> String {
    match *error {
        Error::Unexpected(ref x) => format!("unexpected {}", format_info(x)),
        Error::Expected(ref x) => format!("expected {}", format_info(x)),
        Error::Message(ref x) => format_info(x),
        Error::Other(ref x) => format!("{:?}", x),
    }
}

fn format_parse_error(parse_error: ParseError<State<&str>>) -> String {
    format!("Parse error at line {} column {}: {}",
            parse_error.position.line,
            parse_error.position.column,
            parse_error.errors.iter().map(format_error).collect::<Vec<String>>().join(", "))
}

/// Parse a string into a list of instructions.
///
/// The instructions are not validated and may have invalid names and operand types.
pub fn parse(input: &str) -> Result<Vec<Instruction>, String> {
    match many(parser(instruction)).skip(eof()).parse(State::new(input)) {
        Ok((insts, _)) => Ok(insts),
        Err(err) => Err(format_parse_error(err)),
    }
}

#[cfg(test)]
mod tests {
    use combine::{Parser, parser};
    use super::{ident, integer, register, operand, instruction, Operand, Instruction};

    #[test]
    fn test_ident() {
        assert_eq!(parser(ident).parse("nop"), Ok(("nop".to_string(), "")));
        assert_eq!(parser(ident).parse("add32"), Ok(("add32".to_string(), "")));
        assert_eq!(parser(ident).parse("add32*"),
                   Ok(("add32".to_string(), "*")));
    }

    #[test]
    fn test_integer() {
        assert_eq!(parser(integer).parse("0"), Ok((0, "")));
        assert_eq!(parser(integer).parse("42"), Ok((42, "")));
        assert_eq!(parser(integer).parse("+42"), Ok((42, "")));
        assert_eq!(parser(integer).parse("-42"), Ok((-42, "")));
        assert_eq!(parser(integer).parse("0x0"), Ok((0, "")));
        assert_eq!(parser(integer).parse("0x123456789abcdef0"),
                   Ok((0x123456789abcdef0, "")));
        assert_eq!(parser(integer).parse("-0x1f"), Ok((-31, "")));
    }

    #[test]
    fn test_register() {
        assert_eq!(parser(register).parse("r0"), Ok((0, "")));
        assert_eq!(parser(register).parse("r15"), Ok((15, "")));
    }

    #[test]
    fn test_operand() {
        assert_eq!(parser(operand).parse("r0"), Ok((Operand::Register(0), "")));
        assert_eq!(parser(operand).parse("r15"),
                   Ok((Operand::Register(15), "")));
        assert_eq!(parser(operand).parse("0"), Ok((Operand::Integer(0), "")));
        assert_eq!(parser(operand).parse("42"), Ok((Operand::Integer(42), "")));
        assert_eq!(parser(operand).parse("[r1]"),
                   Ok((Operand::Memory(1, 0), "")));
        assert_eq!(parser(operand).parse("[r3+5]"),
                   Ok((Operand::Memory(3, 5), "")));
        assert_eq!(parser(operand).parse("[r3+0x1f]"),
                   Ok((Operand::Memory(3, 31), "")));
        assert_eq!(parser(operand).parse("[r3-0x1f]"),
                   Ok((Operand::Memory(3, -31), "")));
    }

    #[test]
    fn test_instruction() {
        assert_eq!(parser(instruction).parse("exit"),
                   Ok((Instruction {
                           name: "exit".to_string(),
                           operands: vec![],
                       },
                       "")));

        assert_eq!(parser(instruction).parse("call 2"),
                   Ok((Instruction {
                           name: "call".to_string(),
                           operands: vec![Operand::Integer(2)],
                       },
                       "")));

        assert_eq!(parser(instruction).parse("addi r1, 2"),
                   Ok((Instruction {
                           name: "addi".to_string(),
                           operands: vec![Operand::Register(1), Operand::Integer(2)],
                       },
                       "")));

        assert_eq!(parser(instruction).parse("ldxb r2, [r1+12]"),
                   Ok((Instruction {
                           name: "ldxb".to_string(),
                           operands: vec![Operand::Register(2), Operand::Memory(1, 12)],
                       },
                       "")));

        assert_eq!(parser(instruction).parse("lsh r3, 0x8"),
                   Ok((Instruction {
                           name: "lsh".to_string(),
                           operands: vec![Operand::Register(3), Operand::Integer(8)],
                       },
                       "")));

        assert_eq!(parser(instruction).parse("jne r3, 0x8, +37"),
                   Ok((Instruction {
                           name: "jne".to_string(),
                           operands: vec![Operand::Register(3),
                                          Operand::Integer(8),
                                          Operand::Integer(37)],
                       },
                       "")));

        // Whitespace between operands is optional.
        assert_eq!(parser(instruction).parse("jne r3,0x8,+37"),
                   Ok((Instruction {
                           name: "jne".to_string(),
                           operands: vec![Operand::Register(3),
                                          Operand::Integer(8),
                                          Operand::Integer(37)],
                       },
                       "")));
    }

}
<|endoftext|>