<commit_before>extern crate regex;
#[cfg(windows)]
extern crate winreg;

pub use super::super::compiler::*;

use super::postprocess;
use super::super::utils::filter;
use super::super::io::memstream::MemStream;
use super::super::io::tempfile::TempFile;
use super::super::lazy::Lazy;

use std::fs::File;
use std::io::{Error, Cursor, Write};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use self::regex::bytes::{Regex, NoExpand};

pub struct VsCompiler {
	temp_dir: PathBuf,
	toolchains: ToolchainHolder,
}

impl VsCompiler {
	pub fn new(temp_dir: &Path) -> Self {
		VsCompiler {
			temp_dir: temp_dir.to_path_buf(),
			toolchains: ToolchainHolder::new(),
		}
	}
}

struct VsToolchain {
	temp_dir: PathBuf,
	path: PathBuf,
	identifier: Lazy<Option<String>>,
}

impl VsToolchain {
	pub fn new(path: PathBuf, temp_dir: PathBuf) -> Self {
		VsToolchain {
			temp_dir: temp_dir,
			path: path,
			identifier: Lazy::new(),
		}
	}
}

impl Compiler for VsCompiler {
	fn resolve_toolchain(&self, command: &CommandInfo) -> Option<Arc<Toolchain>> {
		if command.program.file_name().and_then(|n| n.to_str()).map(|n| n.to_lowercase()).map_or(false, |n| (n == "cl.exe") || (n == "cl")) {
			command.find_executable().and_then(|path| self.toolchains.resolve(&path, |path| Arc::new(VsToolchain::new(path, self.temp_dir.clone()))))
		} else {
			None
		}
	}

	fn toolchains(&self) -> Vec<Arc<Toolchain>> {
		self.toolchains.to_vec()
	}

	#[cfg(unix)]
	fn discovery(&mut self) {
	}

	#[cfg(windows)]
	fn discovery(&mut self) {
		use self::winreg::RegKey;
		use self::winreg::enums::*;

		lazy_static!{
			static ref RE:self::regex::Regex = self::regex::Regex::new(r"^\d+\.\d+$").unwrap();
		}

		for reg_path in vec!["SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7", "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7"] {
			let paths = RegKey::predef(HKEY_LOCAL_MACHINE)
			.open_subkey_with_flags(reg_path, KEY_READ)
			.ok()
			.map(|key| -> Vec<String> {
				key.enum_values()
				.filter_map(|x| x.ok())
				.map(|(name, _)| name)
				.filter(|name| RE.is_match(name))
				.filter_map(|name| -> Option<String> { key.get_value(name).ok() })
				.collect()
			})
			.unwrap_or_else(|| Vec::new());
			for path in paths.iter().map(|path| Path::new(path).join("bin/cl.exe")) {
				self.toolchains.resolve(&path, |path| Arc::new(VsToolchain::new(path, self.temp_dir.clone())));
			}
		}
	}

	fn create_task(&self, command: CommandInfo, args: &[String]) -> Result<Option<CompilationTask>, String> {
		self.resolve_toolchain(&command)
		.ok_or(format!("Can't get toolchain for {}", command.program.display()))
		.and_then(|toolchain| super::prepare::create_task(toolchain, command, args))
	}

	fn preprocess_step(&self, task: &CompilationTask) -> Result<PreprocessResult, Error> {
		// Make parameters list for preprocessing.
		let mut args = filter(&task.args, |arg: &Arg| -> Option<String> {
			match arg {
				&Arg::Flag { ref scope, ref flag } => {
					match scope {
						&Scope::Preprocessor | &Scope::Shared => Some("/".to_string() + &flag),
						&Scope::Ignore | &Scope::Compiler => None
					}
				}
				&Arg::Param { ref scope, ref flag, ref value } => {
					match scope {
						&Scope::Preprocessor | &Scope::Shared => Some("/".to_string() + &flag + &value),
						&Scope::Ignore | &Scope::Compiler => None
					}
				}
				&Arg::Input { .. } => None,
				&Arg::Output { .. } => None,
			}
		});

		// Add preprocessor paramters.
		args.push("/nologo".to_string());
		args.push("/T".to_string() + &task.language);
		args.push("/E".to_string());
		args.push("/we4002".to_string()); // C4002: too many actual parameters for macro 'identifier'
		args.push(task.input_source.display().to_string());

		let mut command = task.command.to_command();
		command
		.args(&args)
		.arg(&join_flag("/Fo", &task.output_object)); // /Fo option also set output path for #import directive
		let output = try!(command.output());
		if output.status.success() {
			let mut content = MemStream::new();
			if task.input_precompiled.is_some() || task.output_precompiled.is_some() {
				try!(postprocess::filter_preprocessed(&mut Cursor::new(output.stdout), &mut content, &task.marker_precompiled, task.output_precompiled.is_some()));
			} else {
				try!(content.write(&output.stdout));
			};
			Ok(PreprocessResult::Success(content))
		} else {
			Ok(PreprocessResult::Failed(OutputInfo {
				status: output.status.code(),
				stdout: Vec::new(),
				stderr: output.stderr,
			}))
		}
	}

	// Compile preprocessed file.
	fn compile_prepare_step(&self, task: CompilationTask, preprocessed: MemStream) -> Result<CompileStep, Error> {
		let mut args = filter(&task.args, |arg: &Arg| -> Option<String> {
			match arg {
				&Arg::Flag { ref scope, ref flag } => {
					match scope {
						&Scope::Compiler | &Scope::Shared => Some("/".to_string() + &flag),
						&Scope::Preprocessor if task.output_precompiled.is_some() => Some("/".to_string() + &flag),
						&Scope::Ignore | &Scope::Preprocessor => None
					}
				}
				&Arg::Param { ref scope, ref flag, ref value } => {
					match scope {
						&Scope::Compiler | &Scope::Shared => Some("/".to_string() + &flag + &value),
						&Scope::Preprocessor if task.output_precompiled.is_some() => Some("/".to_string() + &flag + &value),
						&Scope::Ignore | &Scope::Preprocessor => None
					}
				}
				&Arg::Input { .. } => None,
				&Arg::Output { .. } => None
			}
		});
		args.push("/nologo".to_string());
		args.push("/T".to_string() + &task.language);
		match &task.input_precompiled {
			&Some(ref path) => {
				args.push("/Yu".to_string());
				args.push("/Fp".to_string() + &path.display().to_string());
			}
			&None => {}
		}
		if task.output_precompiled.is_some() {
			args.push("/Yc".to_string());
		}
		Ok(CompileStep::new(task, preprocessed, args, true))
	}
}

impl Toolchain for VsToolchain {
	fn identifier(&self) -> Option<String> {
		self.identifier.get(|| vs_identifier(&self.path))
	}

	fn compile_step(&self, task: CompileStep) -> Result<OutputInfo, Error> {
		// Input file path.
		let input_temp = TempFile::new_in(&self.temp_dir, ".i");
		try! (File::create(input_temp.path()).and_then(|mut s| task.preprocessed.copy(&mut s)));
		// Run compiler.
		let mut command = task.command.to_command();
		command
		.arg("/c")
		.args(&task.args)
		.arg(input_temp.path().to_str().unwrap())
		.arg(&join_flag("/Fo", &task.output_object));
		// Output files.
		match &task.output_precompiled {
			&Some(ref path) => { command.arg(join_flag("/Fp", path)); }
			&None => {}
		}
		match &task.input_precompiled {
			&Some(ref path) => { command.arg(join_flag("/Fp", path)); }
			&None => {}
		}
		// Save input file name for output filter.
		let temp_file = input_temp.path().file_name()
		.and_then(|o| o.to_str())
		.map(|o| o.as_bytes())
		.unwrap_or(b"");
		// Execute.
		command.output().map(|o| OutputInfo {
			status: o.status.code(),
			stdout: prepare_output(temp_file, o.stdout, o.status.code() == Some(0)),
			stderr: o.stderr,
		})
	}
}

#[cfg(unix)]
fn vs_identifier(clang: &Path) -> Option<String> {
	None
}

#[cfg(windows)]
fn vs_identifier(path: &Path) -> Option<String> {
	//extern crate winapi;
	extern crate kernel32;
	extern crate version;

	use winapi::*;
	use std::convert::Into;
	use std::ffi::OsStr;
	use std::ptr;
	use std::slice;
	use std::os::windows::ffi::OsStrExt;

	#[repr(C)]
	struct LANGANDCODEPAGE {
		language: WORD,
		codepage: WORD,
	};

	fn utf16<'a, T: Into<&'a OsStr>>(value: T) -> Vec<u16> {
		value.into().encode_wide().chain(Some(0).into_iter()).collect()
	};

	let path_raw = utf16(path.as_os_str());
	// Get version info size
	let size = unsafe {
		version::GetFileVersionInfoSizeW(path_raw.as_ptr(), ptr::null_mut())
	};
	if size == 0 {
		return None;
	}
	// Load version info
	let mut data: Vec<u8> = Vec::with_capacity(size as usize);
	unsafe {
		data.set_len(size as usize);
		if version::GetFileVersionInfoW(path_raw.as_ptr(), 0, size, data.as_mut_ptr() as *mut c_void) == 0 {
			return None;
		}
	}
	// Read translation
	let translation_key = unsafe {
		let mut value_size: DWORD = 0;
		let mut value_data: LPVOID = ptr::null_mut();
		if version::VerQueryValueW(data.as_ptr() as LPCVOID, utf16(OsStr::new("\\VarFileInfo\\Translation")).as_ptr(), &mut value_data, &mut value_size) == 0 {
			return None;
		}
		let codepage = value_data as *const LANGANDCODEPAGE;
		format!("\\StringFileInfo\\{:04X}{:04X}", (*codepage).language, (*codepage).codepage)
	};
	// Read product version
	let product_version = unsafe {
		let mut value_size: DWORD = 0;
		let mut value_data: LPVOID = ptr::null_mut();
		if version::VerQueryValueW(data.as_ptr() as LPCVOID, utf16(OsStr::new(&(translation_key + "\\ProductVersion"))).as_ptr(), &mut value_data, &mut value_size) == 0 {
			return None;
		}
		if value_size == 0 {
			return None;
		}
		String::from_utf16_lossy(slice::from_raw_parts(value_data as *mut u16, (value_size - 1) as usize))
	};
	Some("cl ".to_string() + &product_version)
}

fn prepare_output(line: &[u8], mut buffer: Vec<u8>, success: bool) -> Vec<u8> {
	// Remove strage file name from output
	let mut begin = match (line.len() < buffer.len()) && buffer.starts_with(line) && is_eol(buffer[line.len()]) {
		true => line.len(),
		false => 0
	};
	while begin < buffer.len() && is_eol(buffer[begin]) {
		begin += 1;
	}
	buffer = buffer.split_off(begin);
	if success {
		// Remove some redundant lines
		lazy_static! {
			static ref RE: Regex = Regex::new(r"(?m)^\S+[^:]*\(\d+\) : warning C4628: .*$\n?").unwrap();
		}
		buffer = RE.replace_all(&buffer, NoExpand(b""))
	}
	buffer
}

fn is_eol(c: u8) -> bool {
	match c {
	    b'\r' | b'\n' => true,
	    _ => false,
	}
}

fn join_flag(flag: &str, path: &Path) -> String {
	flag.to_string() + &path.to_str().unwrap()
}

#[cfg(test)]
mod test {
    use std::io::Write;

    fn check_prepare_output(original: &str, expected: &str, line: &str, success: bool) {
        let mut stream: Vec<u8> = Vec::new();
        stream.write(&original.as_bytes()[..]).unwrap();

        let result = super::prepare_output(line.as_bytes(), stream, success);
        assert_eq!(String::from_utf8_lossy(&result), expected);
    }

    #[test]
    fn test_prepare_output_simple() {
        check_prepare_output(
            r#"BLABLABLA
foo.c : warning C4411: foo bar
"#,
            r#"foo.c : warning C4411: foo bar
"#, "BLABLABLA", true);
    }

    #[test]
    fn test_prepare_output_c4628_remove() {
        check_prepare_output(
            r#"BLABLABLA
foo.c(41) : warning C4411: foo bar
foo.c(42) : warning C4628: foo bar
foo.c(43) : warning C4433: foo bar
"#,
            r#"foo.c(41) : warning C4411: foo bar
foo.c(43) : warning C4433: foo bar
"#, "BLABLABLA", true);
    }

    #[test]
    fn test_prepare_output_c4628_keep() {
        check_prepare_output(
            r#"BLABLABLA
foo.c(41) : warning C4411: foo bar
foo.c(42) : warning C4628: foo bar
foo.c(43) : warning C4433: foo bar
"#,
            r#"foo.c(41) : warning C4411: foo bar
foo.c(42) : warning C4628: foo bar
foo.c(43) : warning C4433: foo bar
"#, "BLABLABLA", false);
    }
}
<commit_msg>Remove warning<commit_after>extern crate regex;
#[cfg(windows)]
extern crate winreg;

pub use super::super::compiler::*;

use super::postprocess;
use super::super::utils::filter;
use super::super::io::memstream::MemStream;
use super::super::io::tempfile::TempFile;
use super::super::lazy::Lazy;

use std::fs::File;
use std::io::{Error, Cursor, Write};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use self::regex::bytes::{Regex, NoExpand};

pub struct VsCompiler {
	temp_dir: PathBuf,
	toolchains: ToolchainHolder,
}

impl VsCompiler {
	pub fn new(temp_dir: &Path) -> Self {
		VsCompiler {
			temp_dir: temp_dir.to_path_buf(),
			toolchains: ToolchainHolder::new(),
		}
	}
}

struct VsToolchain {
	temp_dir: PathBuf,
	path: PathBuf,
	identifier: Lazy<Option<String>>,
}

impl VsToolchain {
	pub fn new(path: PathBuf, temp_dir: PathBuf) -> Self {
		VsToolchain {
			temp_dir: temp_dir,
			path: path,
			identifier: Lazy::new(),
		}
	}
}

impl Compiler for VsCompiler {
	fn resolve_toolchain(&self, command: &CommandInfo) -> Option<Arc<Toolchain>> {
		if command.program.file_name().and_then(|n| n.to_str()).map(|n| n.to_lowercase()).map_or(false, |n| (n == "cl.exe") || (n == "cl")) {
			command.find_executable().and_then(|path| self.toolchains.resolve(&path, |path| Arc::new(VsToolchain::new(path, self.temp_dir.clone()))))
		} else {
			None
		}
	}

	fn toolchains(&self) -> Vec<Arc<Toolchain>> {
		self.toolchains.to_vec()
	}

	#[cfg(unix)]
	fn discovery(&mut self) {
	}

	#[cfg(windows)]
	fn discovery(&mut self) {
		use self::winreg::RegKey;
		use self::winreg::enums::*;

		lazy_static!{
			static ref RE:self::regex::Regex = self::regex::Regex::new(r"^\d+\.\d+$").unwrap();
		}

		for reg_path in vec!["SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7", "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7"] {
			let paths = RegKey::predef(HKEY_LOCAL_MACHINE)
			.open_subkey_with_flags(reg_path, KEY_READ)
			.ok()
			.map(|key| -> Vec<String> {
				key.enum_values()
				.filter_map(|x| x.ok())
				.map(|(name, _)| name)
				.filter(|name| RE.is_match(name))
				.filter_map(|name| -> Option<String> { key.get_value(name).ok() })
				.collect()
			})
			.unwrap_or_else(|| Vec::new());
			for path in paths.iter().map(|path| Path::new(path).join("bin/cl.exe")) {
				self.toolchains.resolve(&path, |path| Arc::new(VsToolchain::new(path, self.temp_dir.clone())));
			}
		}
	}

	fn create_task(&self, command: CommandInfo, args: &[String]) -> Result<Option<CompilationTask>, String> {
		self.resolve_toolchain(&command)
		.ok_or(format!("Can't get toolchain for {}", command.program.display()))
		.and_then(|toolchain| super::prepare::create_task(toolchain, command, args))
	}

	fn preprocess_step(&self, task: &CompilationTask) -> Result<PreprocessResult, Error> {
		// Make parameters list for preprocessing.
		let mut args = filter(&task.args, |arg: &Arg| -> Option<String> {
			match arg {
				&Arg::Flag { ref scope, ref flag } => {
					match scope {
						&Scope::Preprocessor | &Scope::Shared => Some("/".to_string() + &flag),
						&Scope::Ignore | &Scope::Compiler => None
					}
				}
				&Arg::Param { ref scope, ref flag, ref value } => {
					match scope {
						&Scope::Preprocessor | &Scope::Shared => Some("/".to_string() + &flag + &value),
						&Scope::Ignore | &Scope::Compiler => None
					}
				}
				&Arg::Input { .. } => None,
				&Arg::Output { .. } => None,
			}
		});

		// Add preprocessor paramters.
		args.push("/nologo".to_string());
		args.push("/T".to_string() + &task.language);
		args.push("/E".to_string());
		args.push("/we4002".to_string()); // C4002: too many actual parameters for macro 'identifier'
		args.push(task.input_source.display().to_string());

		let mut command = task.command.to_command();
		command
		.args(&args)
		.arg(&join_flag("/Fo", &task.output_object)); // /Fo option also set output path for #import directive
		let output = try!(command.output());
		if output.status.success() {
			let mut content = MemStream::new();
			if task.input_precompiled.is_some() || task.output_precompiled.is_some() {
				try!(postprocess::filter_preprocessed(&mut Cursor::new(output.stdout), &mut content, &task.marker_precompiled, task.output_precompiled.is_some()));
			} else {
				try!(content.write(&output.stdout));
			};
			Ok(PreprocessResult::Success(content))
		} else {
			Ok(PreprocessResult::Failed(OutputInfo {
				status: output.status.code(),
				stdout: Vec::new(),
				stderr: output.stderr,
			}))
		}
	}

	// Compile preprocessed file.
	fn compile_prepare_step(&self, task: CompilationTask, preprocessed: MemStream) -> Result<CompileStep, Error> {
		let mut args = filter(&task.args, |arg: &Arg| -> Option<String> {
			match arg {
				&Arg::Flag { ref scope, ref flag } => {
					match scope {
						&Scope::Compiler | &Scope::Shared => Some("/".to_string() + &flag),
						&Scope::Preprocessor if task.output_precompiled.is_some() => Some("/".to_string() + &flag),
						&Scope::Ignore | &Scope::Preprocessor => None
					}
				}
				&Arg::Param { ref scope, ref flag, ref value } => {
					match scope {
						&Scope::Compiler | &Scope::Shared => Some("/".to_string() + &flag + &value),
						&Scope::Preprocessor if task.output_precompiled.is_some() => Some("/".to_string() + &flag + &value),
						&Scope::Ignore | &Scope::Preprocessor => None
					}
				}
				&Arg::Input { .. } => None,
				&Arg::Output { .. } => None
			}
		});
		args.push("/nologo".to_string());
		args.push("/T".to_string() + &task.language);
		match &task.input_precompiled {
			&Some(ref path) => {
				args.push("/Yu".to_string());
				args.push("/Fp".to_string() + &path.display().to_string());
			}
			&None => {}
		}
		if task.output_precompiled.is_some() {
			args.push("/Yc".to_string());
		}
		Ok(CompileStep::new(task, preprocessed, args, true))
	}
}

impl Toolchain for VsToolchain {
	fn identifier(&self) -> Option<String> {
		self.identifier.get(|| vs_identifier(&self.path))
	}

	fn compile_step(&self, task: CompileStep) -> Result<OutputInfo, Error> {
		// Input file path.
		let input_temp = TempFile::new_in(&self.temp_dir, ".i");
		try! (File::create(input_temp.path()).and_then(|mut s| task.preprocessed.copy(&mut s)));
		// Run compiler.
		let mut command = task.command.to_command();
		command
		.arg("/c")
		.args(&task.args)
		.arg(input_temp.path().to_str().unwrap())
		.arg(&join_flag("/Fo", &task.output_object));
		// Output files.
		match &task.output_precompiled {
			&Some(ref path) => { command.arg(join_flag("/Fp", path)); }
			&None => {}
		}
		match &task.input_precompiled {
			&Some(ref path) => { command.arg(join_flag("/Fp", path)); }
			&None => {}
		}
		// Save input file name for output filter.
		let temp_file = input_temp.path().file_name()
		.and_then(|o| o.to_str())
		.map(|o| o.as_bytes())
		.unwrap_or(b"");
		// Execute.
		command.output().map(|o| OutputInfo {
			status: o.status.code(),
			stdout: prepare_output(temp_file, o.stdout, o.status.code() == Some(0)),
			stderr: o.stderr,
		})
	}
}

#[cfg(unix)]
fn vs_identifier(_: &Path) -> Option<String> {
	None
}

#[cfg(windows)]
fn vs_identifier(path: &Path) -> Option<String> {
	//extern crate winapi;
	extern crate kernel32;
	extern crate version;

	use winapi::*;
	use std::convert::Into;
	use std::ffi::OsStr;
	use std::ptr;
	use std::slice;
	use std::os::windows::ffi::OsStrExt;

	#[repr(C)]
	struct LANGANDCODEPAGE {
		language: WORD,
		codepage: WORD,
	};

	fn utf16<'a, T: Into<&'a OsStr>>(value: T) -> Vec<u16> {
		value.into().encode_wide().chain(Some(0).into_iter()).collect()
	};

	let path_raw = utf16(path.as_os_str());
	// Get version info size
	let size = unsafe {
		version::GetFileVersionInfoSizeW(path_raw.as_ptr(), ptr::null_mut())
	};
	if size == 0 {
		return None;
	}
	// Load version info
	let mut data: Vec<u8> = Vec::with_capacity(size as usize);
	unsafe {
		data.set_len(size as usize);
		if version::GetFileVersionInfoW(path_raw.as_ptr(), 0, size, data.as_mut_ptr() as *mut c_void) == 0 {
			return None;
		}
	}
	// Read translation
	let translation_key = unsafe {
		let mut value_size: DWORD = 0;
		let mut value_data: LPVOID = ptr::null_mut();
		if version::VerQueryValueW(data.as_ptr() as LPCVOID, utf16(OsStr::new("\\VarFileInfo\\Translation")).as_ptr(), &mut value_data, &mut value_size) == 0 {
			return None;
		}
		let codepage = value_data as *const LANGANDCODEPAGE;
		format!("\\StringFileInfo\\{:04X}{:04X}", (*codepage).language, (*codepage).codepage)
	};
	// Read product version
	let product_version = unsafe {
		let mut value_size: DWORD = 0;
		let mut value_data: LPVOID = ptr::null_mut();
		if version::VerQueryValueW(data.as_ptr() as LPCVOID, utf16(OsStr::new(&(translation_key + "\\ProductVersion"))).as_ptr(), &mut value_data, &mut value_size) == 0 {
			return None;
		}
		if value_size == 0 {
			return None;
		}
		String::from_utf16_lossy(slice::from_raw_parts(value_data as *mut u16, (value_size - 1) as usize))
	};
	Some("cl ".to_string() + &product_version)
}

fn prepare_output(line: &[u8], mut buffer: Vec<u8>, success: bool) -> Vec<u8> {
	// Remove strage file name from output
	let mut begin = match (line.len() < buffer.len()) && buffer.starts_with(line) && is_eol(buffer[line.len()]) {
		true => line.len(),
		false => 0
	};
	while begin < buffer.len() && is_eol(buffer[begin]) {
		begin += 1;
	}
	buffer = buffer.split_off(begin);
	if success {
		// Remove some redundant lines
		lazy_static! {
			static ref RE: Regex = Regex::new(r"(?m)^\S+[^:]*\(\d+\) : warning C4628: .*$\n?").unwrap();
		}
		buffer = RE.replace_all(&buffer, NoExpand(b""))
	}
	buffer
}

fn is_eol(c: u8) -> bool {
	match c {
	    b'\r' | b'\n' => true,
	    _ => false,
	}
}

fn join_flag(flag: &str, path: &Path) -> String {
	flag.to_string() + &path.to_str().unwrap()
}

#[cfg(test)]
mod test {
    use std::io::Write;

    fn check_prepare_output(original: &str, expected: &str, line: &str, success: bool) {
        let mut stream: Vec<u8> = Vec::new();
        stream.write(&original.as_bytes()[..]).unwrap();

        let result = super::prepare_output(line.as_bytes(), stream, success);
        assert_eq!(String::from_utf8_lossy(&result), expected);
    }

    #[test]
    fn test_prepare_output_simple() {
        check_prepare_output(
            r#"BLABLABLA
foo.c : warning C4411: foo bar
"#,
            r#"foo.c : warning C4411: foo bar
"#, "BLABLABLA", true);
    }

    #[test]
    fn test_prepare_output_c4628_remove() {
        check_prepare_output(
            r#"BLABLABLA
foo.c(41) : warning C4411: foo bar
foo.c(42) : warning C4628: foo bar
foo.c(43) : warning C4433: foo bar
"#,
            r#"foo.c(41) : warning C4411: foo bar
foo.c(43) : warning C4433: foo bar
"#, "BLABLABLA", true);
    }

    #[test]
    fn test_prepare_output_c4628_keep() {
        check_prepare_output(
            r#"BLABLABLA
foo.c(41) : warning C4411: foo bar
foo.c(42) : warning C4628: foo bar
foo.c(43) : warning C4433: foo bar
"#,
            r#"foo.c(41) : warning C4411: foo bar
foo.c(42) : warning C4628: foo bar
foo.c(43) : warning C4433: foo bar
"#, "BLABLABLA", false);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate failure;
extern crate parking_lot;
#[macro_use]
extern crate log;
extern crate once_cell;
extern crate libsyntax2;
extern crate libeditor;
extern crate fst;
extern crate rayon;
extern crate relative_path;

mod symbol_index;
mod module_map;
mod api;

use std::{
    fmt,
    panic,
    sync::{
        Arc,
        atomic::{AtomicBool, Ordering::SeqCst},
    },
    collections::hash_map::HashMap,
    time::Instant,
};

use relative_path::{RelativePath,RelativePathBuf};
use once_cell::sync::OnceCell;
use rayon::prelude::*;

use libsyntax2::{
    File,
    TextUnit, TextRange, SmolStr,
    ast::{self, AstNode, NameOwner},
    SyntaxKind::*,
};
use libeditor::{LineIndex, FileSymbol, find_node_at_offset};

use self::{
    symbol_index::FileSymbols,
    module_map::{ModuleMap, ChangeKind, Problem},
};
pub use self::symbol_index::Query;
pub use self::api::*;

pub type Result<T> = ::std::result::Result<T, ::failure::Error>;

pub trait FileResolver: Send + Sync + 'static {
    fn file_stem(&self, id: FileId) -> String;
    fn resolve(&self, id: FileId, path: &RelativePath) -> Option<FileId>;
}

#[derive(Debug)]
pub struct WorldState {
    data: Arc<WorldData>
}

pub(crate) struct World {
    needs_reindex: AtomicBool,
    file_resolver: Arc<FileResolver>,
    data: Arc<WorldData>,
}

impl fmt::Debug for World {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        (&*self.data).fmt(f)
    }
}

impl Clone for World {
    fn clone(&self) -> World {
        World {
            needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)),
            file_resolver: Arc::clone(&self.file_resolver),
            data: Arc::clone(&self.data),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub u32);

impl WorldState {
    pub fn new() -> WorldState {
        WorldState {
            data: Arc::new(WorldData::default()),
        }
    }

    pub fn analysis(
        &self,
        file_resolver: impl FileResolver,
    ) -> Analysis {
        let imp = World {
            needs_reindex: AtomicBool::new(false),
            file_resolver: Arc::new(file_resolver),
            data: self.data.clone()
        };
        Analysis { imp }
    }

    pub fn change_file(&mut self, file_id: FileId, text: Option<String>) {
        self.change_files(::std::iter::once((file_id, text)));
    }

    pub fn change_files(&mut self, changes: impl Iterator<Item=(FileId, Option<String>)>) {
        let data = self.data_mut();
        for (file_id, text) in changes {
            let change_kind = if data.file_map.remove(&file_id).is_some() {
                if text.is_some() {
                    ChangeKind::Update
                } else {
                    ChangeKind::Delete
                }
            } else {
                ChangeKind::Insert
            };
            data.module_map.update_file(file_id, change_kind);
            data.file_map.remove(&file_id);
            if let Some(text) = text {
                let file_data = FileData::new(text);
                data.file_map.insert(file_id, Arc::new(file_data));
            } else {
                data.file_map.remove(&file_id);
            }
        }
    }

    fn data_mut(&mut self) -> &mut WorldData {
        if Arc::get_mut(&mut self.data).is_none() {
            self.data = Arc::new(WorldData {
                file_map: self.data.file_map.clone(),
                module_map: self.data.module_map.clone(),
            });
        }
        Arc::get_mut(&mut self.data).unwrap()
    }
}

#[derive(Debug)]
pub struct QuickFix {
    pub fs_ops: Vec<FsOp>,
}

#[derive(Debug)]
pub enum FsOp {
    CreateFile {
        anchor: FileId,
        path: RelativePathBuf,
    },
    MoveFile {
        file: FileId,
        path: RelativePathBuf,
    }
}

impl World {
    pub fn file_syntax(&self, file_id: FileId) -> Result<File> {
        let data = self.file_data(file_id)?;
        Ok(data.syntax().clone())
    }

    pub fn file_line_index(&self, id: FileId) -> Result<LineIndex> {
        let data = self.file_data(id)?;
        let index = data.lines
            .get_or_init(|| LineIndex::new(&data.text));
        Ok(index.clone())
    }

    pub fn world_symbols(&self, mut query: Query) -> Vec<(FileId, FileSymbol)> {
        self.reindex();
        self.data.file_map.iter()
            .flat_map(move |(id, data)| {
                let symbols = data.symbols();
                query.process(symbols).into_iter().map(move |s| (*id, s))
            })
            .collect()
    }

    pub fn parent_module(&self, id: FileId) -> Vec<(FileId, FileSymbol)> {
        let module_map = &self.data.module_map;
        let id = module_map.file2module(id);
        module_map
            .parent_modules(
                id,
                &*self.file_resolver,
                &|file_id| self.file_syntax(file_id).unwrap(),
            )
            .into_iter()
            .map(|(id, name, node)| {
                let id = module_map.module2file(id);
                let sym = FileSymbol {
                    name,
                    node_range: node.range(),
                    kind: MODULE,
                };
                (id, sym)
            })
            .collect()
    }

    pub fn approximately_resolve_symbol(
        &self,
        id: FileId,
        offset: TextUnit,
    ) -> Result<Vec<(FileId, FileSymbol)>> {
        let file = self.file_syntax(id)?;
        let syntax = file.syntax();
        if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) {
            return Ok(self.index_resolve(name_ref));
        }
        if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) {
            if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
                if module.has_semi() {
                    let file_ids = self.resolve_module(id, module);

                    let res = file_ids.into_iter().map(|id| {
                        let name = module.name()
                            .map(|n| n.text())
                            .unwrap_or_else(|| SmolStr::new(""));
                        let symbol = FileSymbol {
                            name,
                            node_range: TextRange::offset_len(0.into(), 0.into()),
                            kind: MODULE,
                        };
                        (id, symbol)
                    }).collect();

                    return Ok(res);
                }
            }
        }
        Ok(vec![])
    }

    pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
        let syntax = self.file_syntax(file_id).unwrap();
        let mut res = libeditor::diagnostics(&syntax)
            .into_iter()
            .map(|d| Diagnostic { range: d.range, message: d.msg, fix: None })
            .collect::<Vec<_>>();

        self.data.module_map.problems(
            file_id,
            &*self.file_resolver,
            &|file_id| self.file_syntax(file_id).unwrap(),
            |name_node, problem| {
                let diag = match problem {
                    Problem::UnresolvedModule { candidate } => {
                        let create_file = FileSystemEdit::CreateFile {
                            anchor: file_id,
                            path: candidate.clone(),
                        };
                        let fix = SourceChange {
                            label: "create module".to_string(),
                            source_file_edits: Vec::new(),
                            file_system_edits: vec![create_file],
                            cursor_position: None,
                        };
                        Diagnostic {
                            range: name_node.syntax().range(),
                            message: "unresolved module".to_string(),
                            fix: Some(fix),
                        }
                    }
                    Problem::NotDirOwner { move_to, candidate } => {
                        let move_file = FileSystemEdit::MoveFile { file: file_id, path: move_to.clone() };
                        let create_file = FileSystemEdit::CreateFile { anchor: file_id, path: move_to.join(candidate) };
                        let fix = SourceChange {
                            label: "move file and create module".to_string(),
                            source_file_edits: Vec::new(),
                            file_system_edits: vec![move_file, create_file],
                            cursor_position: None,
                        };
                        Diagnostic {
                            range: name_node.syntax().range(),
                            message: "can't declare module at this location".to_string(),
                            fix: Some(fix),
                        }
                    }
                };
                res.push(diag)
            }
        );
        res
    }

    pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec<SourceChange> {
        let file = self.file_syntax(file_id).unwrap();
        let actions = vec![
            ("flip comma", libeditor::flip_comma(&file, offset).map(|f| f())),
            ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())),
            ("add impl", libeditor::add_impl(&file, offset).map(|f| f())),
        ];
        let mut res = Vec::new();
        for (name, local_edit) in actions {
            if let Some(local_edit) = local_edit {
                res.push(SourceChange::from_local_edit(
                    file_id, name, local_edit
                ))
            }
        }
        res
    }

    fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> {
        let name = name_ref.text();
        let mut query = Query::new(name.to_string());
        query.exact();
        query.limit(4);
        self.world_symbols(query)
    }

    fn resolve_module(&self, id: FileId, module: ast::Module) -> Vec<FileId> {
        let name = match module.name() {
            Some(name) => name.text(),
            None => return Vec::new(),
        };
        let module_map = &self.data.module_map;
        let id = module_map.file2module(id);
        module_map
            .child_module_by_name(
                id, name.as_str(),
                &*self.file_resolver,
                &|file_id| self.file_syntax(file_id).unwrap(),
            )
            .into_iter()
            .map(|id| module_map.module2file(id))
            .collect()
    }

    fn reindex(&self) {
        if self.needs_reindex.compare_and_swap(false, true, SeqCst) {
            let now = Instant::now();
            let data = &*self.data;
            data.file_map
                .par_iter()
                .for_each(|(_, data)| drop(data.symbols()));
            info!("parallel indexing took {:?}", now.elapsed());
        }
    }

    fn file_data(&self, file_id: FileId) -> Result<Arc<FileData>> {
        match self.data.file_map.get(&file_id) {
            Some(data) => Ok(data.clone()),
            None => bail!("unknown file: {:?}", file_id),
        }
    }
}

#[derive(Default, Debug)]
struct WorldData {
    file_map: HashMap<FileId, Arc<FileData>>,
    module_map: ModuleMap,
}

#[derive(Debug)]
struct FileData {
    text: String,
    symbols: OnceCell<FileSymbols>,
    syntax: OnceCell<File>,
    lines: OnceCell<LineIndex>,
}

impl FileData {
    fn new(text: String) -> FileData {
        FileData {
            text,
            symbols: OnceCell::new(),
            syntax: OnceCell::new(),
            lines: OnceCell::new(),
        }
    }

    fn syntax(&self) -> &File {
        let text = &self.text;
        let syntax = &self.syntax;
        match panic::catch_unwind(panic::AssertUnwindSafe(|| syntax.get_or_init(|| File::parse(text)))) {
            Ok(file) => file,
            Err(err) => {
                error!("Parser paniced on:\n------\n{}\n------\n", &self.text);
                panic::resume_unwind(err)
            }
        }
    }

    fn syntax_transient(&self) -> File {
        self.syntax.get().map(|s| s.clone())
            .unwrap_or_else(|| File::parse(&self.text))
    }

    fn symbols(&self) -> &FileSymbols {
        let syntax = self.syntax_transient();
        self.symbols
            .get_or_init(|| FileSymbols::new(&syntax))
    }
}
<commit_msg>Remove dead code<commit_after>#[macro_use]
extern crate failure;
extern crate parking_lot;
#[macro_use]
extern crate log;
extern crate once_cell;
extern crate libsyntax2;
extern crate libeditor;
extern crate fst;
extern crate rayon;
extern crate relative_path;

mod symbol_index;
mod module_map;
mod api;

use std::{
    fmt,
    panic,
    sync::{
        Arc,
        atomic::{AtomicBool, Ordering::SeqCst},
    },
    collections::hash_map::HashMap,
    time::Instant,
};

use relative_path::RelativePath;
use once_cell::sync::OnceCell;
use rayon::prelude::*;

use libsyntax2::{
    File,
    TextUnit, TextRange, SmolStr,
    ast::{self, AstNode, NameOwner},
    SyntaxKind::*,
};
use libeditor::{LineIndex, FileSymbol, find_node_at_offset};

use self::{
    symbol_index::FileSymbols,
    module_map::{ModuleMap, ChangeKind, Problem},
};
pub use self::symbol_index::Query;
pub use self::api::*;

pub type Result<T> = ::std::result::Result<T, ::failure::Error>;

pub trait FileResolver: Send + Sync + 'static {
    fn file_stem(&self, id: FileId) -> String;
    fn resolve(&self, id: FileId, path: &RelativePath) -> Option<FileId>;
}

#[derive(Debug)]
pub struct WorldState {
    data: Arc<WorldData>
}

pub(crate) struct World {
    needs_reindex: AtomicBool,
    file_resolver: Arc<FileResolver>,
    data: Arc<WorldData>,
}

impl fmt::Debug for World {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        (&*self.data).fmt(f)
    }
}

impl Clone for World {
    fn clone(&self) -> World {
        World {
            needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)),
            file_resolver: Arc::clone(&self.file_resolver),
            data: Arc::clone(&self.data),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub u32);

impl WorldState {
    pub fn new() -> WorldState {
        WorldState {
            data: Arc::new(WorldData::default()),
        }
    }

    pub fn analysis(
        &self,
        file_resolver: impl FileResolver,
    ) -> Analysis {
        let imp = World {
            needs_reindex: AtomicBool::new(false),
            file_resolver: Arc::new(file_resolver),
            data: self.data.clone()
        };
        Analysis { imp }
    }

    pub fn change_file(&mut self, file_id: FileId, text: Option<String>) {
        self.change_files(::std::iter::once((file_id, text)));
    }

    pub fn change_files(&mut self, changes: impl Iterator<Item=(FileId, Option<String>)>) {
        let data = self.data_mut();
        for (file_id, text) in changes {
            let change_kind = if data.file_map.remove(&file_id).is_some() {
                if text.is_some() {
                    ChangeKind::Update
                } else {
                    ChangeKind::Delete
                }
            } else {
                ChangeKind::Insert
            };
            data.module_map.update_file(file_id, change_kind);
            data.file_map.remove(&file_id);
            if let Some(text) = text {
                let file_data = FileData::new(text);
                data.file_map.insert(file_id, Arc::new(file_data));
            } else {
                data.file_map.remove(&file_id);
            }
        }
    }

    fn data_mut(&mut self) -> &mut WorldData {
        if Arc::get_mut(&mut self.data).is_none() {
            self.data = Arc::new(WorldData {
                file_map: self.data.file_map.clone(),
                module_map: self.data.module_map.clone(),
            });
        }
        Arc::get_mut(&mut self.data).unwrap()
    }
}

impl World {
    pub fn file_syntax(&self, file_id: FileId) -> Result<File> {
        let data = self.file_data(file_id)?;
        Ok(data.syntax().clone())
    }

    pub fn file_line_index(&self, id: FileId) -> Result<LineIndex> {
        let data = self.file_data(id)?;
        let index = data.lines
            .get_or_init(|| LineIndex::new(&data.text));
        Ok(index.clone())
    }

    pub fn world_symbols(&self, mut query: Query) -> Vec<(FileId, FileSymbol)> {
        self.reindex();
        self.data.file_map.iter()
            .flat_map(move |(id, data)| {
                let symbols = data.symbols();
                query.process(symbols).into_iter().map(move |s| (*id, s))
            })
            .collect()
    }

    pub fn parent_module(&self, id: FileId) -> Vec<(FileId, FileSymbol)> {
        let module_map = &self.data.module_map;
        let id = module_map.file2module(id);
        module_map
            .parent_modules(
                id,
                &*self.file_resolver,
                &|file_id| self.file_syntax(file_id).unwrap(),
            )
            .into_iter()
            .map(|(id, name, node)| {
                let id = module_map.module2file(id);
                let sym = FileSymbol {
                    name,
                    node_range: node.range(),
                    kind: MODULE,
                };
                (id, sym)
            })
            .collect()
    }

    pub fn approximately_resolve_symbol(
        &self,
        id: FileId,
        offset: TextUnit,
    ) -> Result<Vec<(FileId, FileSymbol)>> {
        let file = self.file_syntax(id)?;
        let syntax = file.syntax();
        if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) {
            return Ok(self.index_resolve(name_ref));
        }
        if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) {
            if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
                if module.has_semi() {
                    let file_ids = self.resolve_module(id, module);

                    let res = file_ids.into_iter().map(|id| {
                        let name = module.name()
                            .map(|n| n.text())
                            .unwrap_or_else(|| SmolStr::new(""));
                        let symbol = FileSymbol {
                            name,
                            node_range: TextRange::offset_len(0.into(), 0.into()),
                            kind: MODULE,
                        };
                        (id, symbol)
                    }).collect();

                    return Ok(res);
                }
            }
        }
        Ok(vec![])
    }

    pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
        let syntax = self.file_syntax(file_id).unwrap();
        let mut res = libeditor::diagnostics(&syntax)
            .into_iter()
            .map(|d| Diagnostic { range: d.range, message: d.msg, fix: None })
            .collect::<Vec<_>>();

        self.data.module_map.problems(
            file_id,
            &*self.file_resolver,
            &|file_id| self.file_syntax(file_id).unwrap(),
            |name_node, problem| {
                let diag = match problem {
                    Problem::UnresolvedModule { candidate } => {
                        let create_file = FileSystemEdit::CreateFile {
                            anchor: file_id,
                            path: candidate.clone(),
                        };
                        let fix = SourceChange {
                            label: "create module".to_string(),
                            source_file_edits: Vec::new(),
                            file_system_edits: vec![create_file],
                            cursor_position: None,
                        };
                        Diagnostic {
                            range: name_node.syntax().range(),
                            message: "unresolved module".to_string(),
                            fix: Some(fix),
                        }
                    }
                    Problem::NotDirOwner { move_to, candidate } => {
                        let move_file = FileSystemEdit::MoveFile { file: file_id, path: move_to.clone() };
                        let create_file = FileSystemEdit::CreateFile { anchor: file_id, path: move_to.join(candidate) };
                        let fix = SourceChange {
                            label: "move file and create module".to_string(),
                            source_file_edits: Vec::new(),
                            file_system_edits: vec![move_file, create_file],
                            cursor_position: None,
                        };
                        Diagnostic {
                            range: name_node.syntax().range(),
                            message: "can't declare module at this location".to_string(),
                            fix: Some(fix),
                        }
                    }
                };
                res.push(diag)
            }
        );
        res
    }

    pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec<SourceChange> {
        let file = self.file_syntax(file_id).unwrap();
        let actions = vec![
            ("flip comma", libeditor::flip_comma(&file, offset).map(|f| f())),
            ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())),
            ("add impl", libeditor::add_impl(&file, offset).map(|f| f())),
        ];
        let mut res = Vec::new();
        for (name, local_edit) in actions {
            if let Some(local_edit) = local_edit {
                res.push(SourceChange::from_local_edit(
                    file_id, name, local_edit
                ))
            }
        }
        res
    }

    fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> {
        let name = name_ref.text();
        let mut query = Query::new(name.to_string());
        query.exact();
        query.limit(4);
        self.world_symbols(query)
    }

    fn resolve_module(&self, id: FileId, module: ast::Module) -> Vec<FileId> {
        let name = match module.name() {
            Some(name) => name.text(),
            None => return Vec::new(),
        };
        let module_map = &self.data.module_map;
        let id = module_map.file2module(id);
        module_map
            .child_module_by_name(
                id, name.as_str(),
                &*self.file_resolver,
                &|file_id| self.file_syntax(file_id).unwrap(),
            )
            .into_iter()
            .map(|id| module_map.module2file(id))
            .collect()
    }

    fn reindex(&self) {
        if self.needs_reindex.compare_and_swap(false, true, SeqCst) {
            let now = Instant::now();
            let data = &*self.data;
            data.file_map
                .par_iter()
                .for_each(|(_, data)| drop(data.symbols()));
            info!("parallel indexing took {:?}", now.elapsed());
        }
    }

    fn file_data(&self, file_id: FileId) -> Result<Arc<FileData>> {
        match self.data.file_map.get(&file_id) {
            Some(data) => Ok(data.clone()),
            None => bail!("unknown file: {:?}", file_id),
        }
    }
}

#[derive(Default, Debug)]
struct WorldData {
    file_map: HashMap<FileId, Arc<FileData>>,
    module_map: ModuleMap,
}

#[derive(Debug)]
struct FileData {
    text: String,
    symbols: OnceCell<FileSymbols>,
    syntax: OnceCell<File>,
    lines: OnceCell<LineIndex>,
}

impl FileData {
    fn new(text: String) -> FileData {
        FileData {
            text,
            symbols: OnceCell::new(),
            syntax: OnceCell::new(),
            lines: OnceCell::new(),
        }
    }

    fn syntax(&self) -> &File {
        let text = &self.text;
        let syntax = &self.syntax;
        match panic::catch_unwind(panic::AssertUnwindSafe(|| syntax.get_or_init(|| File::parse(text)))) {
            Ok(file) => file,
            Err(err) => {
                error!("Parser paniced on:\n------\n{}\n------\n", &self.text);
                panic::resume_unwind(err)
            }
        }
    }

    fn syntax_transient(&self) -> File {
        self.syntax.get().map(|s| s.clone())
            .unwrap_or_else(|| File::parse(&self.text))
    }

    fn symbols(&self) -> &FileSymbols {
        let syntax = self.syntax_transient();
        self.symbols
            .get_or_init(|| FileSymbols::new(&syntax))
    }
}
<|endoftext|>


<commit_before>//! This crate provides bindings to the official wayland protocol extensions
//! provided in https://cgit.freedesktop.org/wayland/wayland-protocols
//!
//! These bindings are built on top of the crates wayland-client and wayland-server.
//!
//! Each protocol module contains a `client` and a `server` submodules, for each side of the
//! protocol. The creation of these modules (and the dependency on the associated crate) is
//! controlled by the two cargo features `client` and `server`.
//!
//! The cargo feature `unstable_protocols` adds an `unstable` module, containings bindings
//! to protocols that are not yet considered stable. As such, no stability guarantee is
//! given for these protocols.
//!
//! Some protocols require unstable rust features, the inclusion of them is controlled
//! by the cargo feature `nightly`.

#![warn(missing_docs)]

#![cfg_attr(feature = "nightly", feature(static_recursion))]

#[cfg(feature = "client")]
extern crate wayland_client;

#[cfg(feature = "server")]
extern crate wayland_server;

#[macro_use]
extern crate wayland_sys;

extern crate bitflags;

#[macro_use]
mod protocol_macro;

#[cfg(feature = "unstable_protocols")]
pub mod unstable;

mod stable;
pub use stable::*;
<commit_msg>Restore wrongly removed attribute<commit_after>//! This crate provides bindings to the official wayland protocol extensions
//! provided in https://cgit.freedesktop.org/wayland/wayland-protocols
//!
//! These bindings are built on top of the crates wayland-client and wayland-server.
//!
//! Each protocol module contains a `client` and a `server` submodules, for each side of the
//! protocol. The creation of these modules (and the dependency on the associated crate) is
//! controlled by the two cargo features `client` and `server`.
//!
//! The cargo feature `unstable_protocols` adds an `unstable` module, containings bindings
//! to protocols that are not yet considered stable. As such, no stability guarantee is
//! given for these protocols.
//!
//! Some protocols require unstable rust features, the inclusion of them is controlled
//! by the cargo feature `nightly`.

#![warn(missing_docs)]

#![cfg_attr(feature = "nightly", feature(static_recursion))]

#[cfg(feature = "client")]
extern crate wayland_client;

#[cfg(feature = "server")]
extern crate wayland_server;

#[macro_use]
extern crate wayland_sys;

#[macro_use]
extern crate bitflags;

#[macro_use]
mod protocol_macro;

#[cfg(feature = "unstable_protocols")]
pub mod unstable;

mod stable;
pub use stable::*;
<|endoftext|>


<commit_before>//! This is the Buffer module for Xt.

// This file is part of Xt.
//
// This is the Xt text editor; it edits text.
// Copyright (C) 2016-2018 The Xt Developers

// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see
// <http://www.gnu.org/licenses/>.

extern crate gapbuffer;

use self::gapbuffer::GapBuffer;
use std::path::PathBuf;

use server::modes::major_mode::MajorMode;
use server::modes::minor_mode::MinorMode;

/// Struct for a Buffer object in Xt.
/// Stores buffer state & metadata.
#[derive(Debug)]
pub struct Buffer {
    /// File path of a buffer.
    pub file_path: Option<PathBuf>,
    /// Active status of a buffer.
    pub active: bool,
    /// Temporary status of a buffer.
    pub temporary: bool,
    /// If a buffer is r/o.
    /// If this is `true`, then r/w is `false.`
    /// Likewise, if this is `false`, then r/w is `true`.
    pub read_only: bool,
    /// Major mode of a buffer
    pub major_mode: MajorMode,
    /// Array of Minor modes in a buffer
    pub minor_modes: Vec<MinorMode>,
    /// Dirty status of a buffer
    pub dirty: bool,
    /// Contents of a buffer.
    pub text: GapBuffer<u8>,
}

impl Buffer {
    /// Return a new instance of `Buffer`.
    pub fn new() -> Buffer {
        Buffer {
            file_path: None,
            active: false,
            temporary: false,
            read_only: false,
            major_mode: MajorMode::new("fundamental-mode".to_owned()),
            minor_modes: Vec::new(),
            dirty: false,
            text: GapBuffer::new(),
        }
    }

    /// Return true if a buffer is active.
    /// That is to say, a 'focused' buffer, or otherwise in-use, and
    /// *not* inactive.
    /// If it's not a active buffer, then return false.
    pub fn is_active(&self) -> bool {
        self.active
    }

    /// Return true if a buffer is temporary.
    /// If it's not a temporary buffer, then return false.
    /// A temporary buffer is defined by the characteristic of having
    /// no 'target' to save to.
    pub fn is_temporary(&self) -> bool {
        self.temporary
    }

    /// Return true if a buffer is read only.

    /// If it's not read-only, then it returns false, and the buffer
    /// will be presumed to be read-write.

    /// A read-only buffer is defined by the characteristic of the FS
    /// denying write access, or being set manually.
    pub fn is_ro(&self) -> bool {
        self.read_only
    }

    /// Return true if a buffer is dirty.
    /// If it's not dirty, then return false.
    /// A dirty buffer is defined by the characteristic of the buffer
    /// having changed since the last save.
    /// Temporary buffers are *exempt* from this field.
    pub fn is_dirty(&self) -> bool {
        self.dirty
    }

    /// Return the current major mode of a buffer.
    pub fn get_major_mode(&self) -> &MajorMode {
        &self.major_mode
    }

    /// Return a `Vec<&'static str>` of minor modes.
    pub fn get_minor_modes(&self) -> &Vec<MinorMode> {
        &self.minor_modes
    }

    /// Return the length of a buffer.
    pub fn get_buffer_length(&self) -> usize {
        self.text.len()
    }
}

impl Default for Buffer {
    fn default() -> Buffer {
        Buffer::new()
    }
}

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

    #[test]
    fn test_default_values_buffer() {
        let buf = Buffer::new();

        assert_eq!(None, buf.file_path);

        assert_eq!(false, buf.active);
        assert_eq!(false, buf.is_active());

        assert_eq!(false, buf.temporary);
        assert_eq!(false, buf.is_temporary());

        assert_eq!(false, buf.read_only);
        assert_eq!(false, buf.is_ro());

        assert_eq!(0, buf.text.len());

        assert_eq!("fundamental-mode", buf.major_mode.hname);
        assert_eq!(0, buf.minor_modes.len());
        assert_eq!(false, buf.dirty);
    }
}
<commit_msg>server/buffer: use extern crate from mod.rs<commit_after>//! This is the Buffer module for Xt.

// This file is part of Xt.
//
// This is the Xt text editor; it edits text.
// Copyright (C) 2016-2018 The Xt Developers

// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see
// <http://www.gnu.org/licenses/>.

use super::gapbuffer::GapBuffer;
use std::path::PathBuf;

use server::modes::major_mode::MajorMode;
use server::modes::minor_mode::MinorMode;

/// Struct for a Buffer object in Xt.
/// Stores buffer state & metadata.
#[derive(Debug)]
pub struct Buffer {
    /// File path of a buffer.
    pub file_path: Option<PathBuf>,
    /// Active status of a buffer.
    pub active: bool,
    /// Temporary status of a buffer.
    pub temporary: bool,
    /// If a buffer is r/o.
    /// If this is `true`, then r/w is `false.`
    /// Likewise, if this is `false`, then r/w is `true`.
    pub read_only: bool,
    /// Major mode of a buffer
    pub major_mode: MajorMode,
    /// Array of Minor modes in a buffer
    pub minor_modes: Vec<MinorMode>,
    /// Dirty status of a buffer
    pub dirty: bool,
    /// Contents of a buffer.
    pub text: GapBuffer<u8>,
}

impl Buffer {
    /// Return a new instance of `Buffer`.
    pub fn new() -> Buffer {
        Buffer {
            file_path: None,
            active: false,
            temporary: false,
            read_only: false,
            major_mode: MajorMode::new("fundamental-mode".to_owned()),
            minor_modes: Vec::new(),
            dirty: false,
            text: GapBuffer::new(),
        }
    }

    /// Return true if a buffer is active.
    /// That is to say, a 'focused' buffer, or otherwise in-use, and
    /// *not* inactive.
    /// If it's not a active buffer, then return false.
    pub fn is_active(&self) -> bool {
        self.active
    }

    /// Return true if a buffer is temporary.
    /// If it's not a temporary buffer, then return false.
    /// A temporary buffer is defined by the characteristic of having
    /// no 'target' to save to.
    pub fn is_temporary(&self) -> bool {
        self.temporary
    }

    /// Return true if a buffer is read only.

    /// If it's not read-only, then it returns false, and the buffer
    /// will be presumed to be read-write.

    /// A read-only buffer is defined by the characteristic of the FS
    /// denying write access, or being set manually.
    pub fn is_ro(&self) -> bool {
        self.read_only
    }

    /// Return true if a buffer is dirty.
    /// If it's not dirty, then return false.
    /// A dirty buffer is defined by the characteristic of the buffer
    /// having changed since the last save.
    /// Temporary buffers are *exempt* from this field.
    pub fn is_dirty(&self) -> bool {
        self.dirty
    }

    /// Return the current major mode of a buffer.
    pub fn get_major_mode(&self) -> &MajorMode {
        &self.major_mode
    }

    /// Return a `Vec<&'static str>` of minor modes.
    pub fn get_minor_modes(&self) -> &Vec<MinorMode> {
        &self.minor_modes
    }

    /// Return the length of a buffer.
    pub fn get_buffer_length(&self) -> usize {
        self.text.len()
    }
}

impl Default for Buffer {
    fn default() -> Buffer {
        Buffer::new()
    }
}

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

    #[test]
    fn test_default_values_buffer() {
        let buf = Buffer::new();

        assert_eq!(None, buf.file_path);

        assert_eq!(false, buf.active);
        assert_eq!(false, buf.is_active());

        assert_eq!(false, buf.temporary);
        assert_eq!(false, buf.is_temporary());

        assert_eq!(false, buf.read_only);
        assert_eq!(false, buf.is_ro());

        assert_eq!(0, buf.text.len());

        assert_eq!("fundamental-mode", buf.major_mode.hname);
        assert_eq!(0, buf.minor_modes.len());
        assert_eq!(false, buf.dirty);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
mod macros;

use std::str::FromStr;

use proc_macro2::TokenStream;
use syn::{Expr, ExprRange};

#[test]
fn test_expr_parse() {
    let code = "..100u32";
    let tt = TokenStream::from_str(code).unwrap();
    let expr: Expr = syn::parse2(tt.clone()).unwrap();
    let expr_range: ExprRange = syn::parse2(tt).unwrap();
    assert_eq!(expr, Expr::Range(expr_range));
}

#[test]
fn test_await() {
    // Must not parse as Expr::Field.
    let expr = syn::parse_str::<Expr>("fut.await").unwrap();

    snapshot!(expr, @r###"
    Expr::Await {
        base: Expr::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "fut",
                        arguments: None,
                    },
                ],
            },
        },
    }
    "###);
}
<commit_msg>Add test for fn call with interpolated fn name<commit_after>#[macro_use]
mod macros;

use std::str::FromStr;

use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
use quote::quote;
use std::iter::FromIterator;
use syn::{Expr, ExprRange};

#[test]
fn test_expr_parse() {
    let code = "..100u32";
    let tt = TokenStream::from_str(code).unwrap();
    let expr: Expr = syn::parse2(tt.clone()).unwrap();
    let expr_range: ExprRange = syn::parse2(tt).unwrap();
    assert_eq!(expr, Expr::Range(expr_range));
}

#[test]
fn test_await() {
    // Must not parse as Expr::Field.
    let expr = syn::parse_str::<Expr>("fut.await").unwrap();

    snapshot!(expr, @r###"
    Expr::Await {
        base: Expr::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "fut",
                        arguments: None,
                    },
                ],
            },
        },
    }
    "###);
}

#[test]
fn test_macro_variable_func() {
    // mimics the token stream corresponding to `$fn()`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Group(Group::new(Delimiter::None, quote! { f })),
        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
    ]);

    syn::parse2::<Expr>(tokens).unwrap();
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.

//! Peripheral driver for device attestation (personality) data.  This
//! is per-device data that will be stored durably on the device; this
//! implementations currently stores it in RAM.

use core::cmp;
use core::mem;
use core::cell::Cell;
use hil::personality::{Client, Personality, PersonalityData};
use hil::flash;
use kernel::ReturnCode;
use kernel::common::cells::{OptionalCell, TakeCell};

#[derive(Copy, Clone, Debug, PartialEq)]
enum State {
    Idle,
    ErasingU8,
    WritingU8,
    ErasingStruct,
    WritingStruct,
}

pub struct PersonalityDriver<'a> {
    state: Cell<State>,
    client: OptionalCell<&'a Client<'a>>,
    flash: OptionalCell<&'a flash::Flash<'a>>,
    write_buffer: TakeCell<'a, [u32]>,
}

pub static mut PERSONALITY: PersonalityDriver<'static> = unsafe {PersonalityDriver::new() };

pub static mut BUFFER: [u32; PAGE_SIZE_U32] = [0; PAGE_SIZE_U32];

// Personality data is stored as the third-to-last (N-3) page of flash;
// it is followed by the two pages used as a counter.
const PERSONALITY_ADDRESS: usize = flash::h1b_hw::H1B_FLASH_SIZE - (3 * flash::h1b_hw::H1B_FLASH_PAGE_SIZE) ;
const PERSONALITY_ADDRESS_U32: usize = PERSONALITY_ADDRESS / 4;
const PERSONALITY_SIZE: usize = flash::h1b_hw::H1B_FLASH_PAGE_SIZE;
const PAGE_SIZE_U32: usize    = flash::h1b_hw::H1B_FLASH_PAGE_SIZE / 4;

impl<'a> PersonalityDriver<'a> {
    const unsafe fn new() -> PersonalityDriver<'a> {
        PersonalityDriver {
            state: Cell::new(State::Idle),
            client: OptionalCell::empty(),
            flash: OptionalCell::empty(),
            write_buffer: TakeCell::empty(),
        }
    }

    pub fn set_flash(&self, flash: &'a flash::Flash<'a>) {
        self.flash.set(flash);
    }

    pub fn set_buffer(&self, buf: &'a mut [u32]) {
        self.write_buffer.replace(buf);
    }

    fn start_write(&self, target: usize) -> bool {
        debug!("Starting flash write target {}", target);
        if self.flash.is_none() || self.write_buffer.is_none() {
            false
        } else {
            let buf = self.write_buffer.take().unwrap();
            self.flash.map(move |flash| {
                let (_rcode, opt) = flash.write(target, buf);
                match opt {
                    None => true,  // Operation successful
                    Some(buf) => { // Not successful
                        self.write_buffer.replace(buf);
                        false
                    }
                }
            }).unwrap()
        }
    }

}

impl<'a> Personality<'a> for PersonalityDriver<'a> {

    fn set_client(&self, client: &'a Client<'a>) {
        self.client.set(client);
    }

    fn get(&self, data: &mut PersonalityData) -> ReturnCode {
        unsafe {
            self.flash.map_or(ReturnCode::ENOMEM, |flash| {
                let mut personality_ptr = mem::transmute::<*mut PersonalityData, *mut u32>(data);
                let word_count = PAGE_SIZE_U32;
                for i in 0..word_count {
                    *personality_ptr = flash.read(PERSONALITY_ADDRESS_U32 + i);
                    personality_ptr = personality_ptr.offset(1);
                }
                ReturnCode::SUCCESS
            })
        }
    }

    fn get_u8(&self, data: &mut [u8]) -> ReturnCode {
        debug!("Getting Personality as U8");
        if data.len() < PERSONALITY_SIZE {
            ReturnCode::ESIZE
        } else {
            unsafe {
                self.flash.map_or(ReturnCode::ENOMEM, |flash| {
                    let ptr = data.as_mut_ptr();
                    let mut personality_ptr = mem::transmute::<*mut u8, *mut u32>(ptr);
                    let word_count = PAGE_SIZE_U32;
                    for i in 0..word_count {
                        *personality_ptr = flash.read(PERSONALITY_ADDRESS_U32 + i);
                        personality_ptr = personality_ptr.offset(1);
                    }
                    ReturnCode::SUCCESS
                })
            }
        }
    }

    fn set(&self, data: &mut PersonalityData) -> ReturnCode {
        if self.state.get() != State::Idle {
            return ReturnCode::EBUSY;
        }
        if self.flash.is_some() {
            self.flash.map(move |flash| {
                let offset = PERSONALITY_ADDRESS;
                let page = offset / flash::h1b_hw::H1B_FLASH_PAGE_SIZE;
                let rval = flash.erase(page);
                match rval {
                    ReturnCode::SUCCESS => {
                        self.write_buffer.map(|buffer| {
                            self.state.set(State::ErasingStruct);
                            unsafe {
                                let mut ptr = mem::transmute::<*mut PersonalityData, *mut u32>(data);
                                let word_count = PAGE_SIZE_U32;
                                for i in 0..word_count {
                                    buffer[i] = *ptr;
                                    ptr = ptr.offset(1);
                                }
                            }
                        });
                        ReturnCode::SUCCESS
                    },
                    _ => {
                        rval
                    }
                }
            }).unwrap()
        } else {
            ReturnCode::ENOMEM
        }
    }

    fn set_u8(&self, data: &mut [u8]) -> ReturnCode {
        debug!("Setting Personality as U8");
        if data.len() < PERSONALITY_SIZE {
            debug!(" - ESIZE");
            ReturnCode::ESIZE
        }
        else if self.state.get() != State::Idle {
            debug!(" - EBUSY");
            ReturnCode::EBUSY
        } else {
            if self.flash.is_some() {
                self.flash.map(move |flash| {
                    let offset = PERSONALITY_ADDRESS;
                    let page = offset / flash::h1b_hw::H1B_FLASH_PAGE_SIZE;
                    let rval = flash.erase(page);
                    debug!("Erasing page {} with rval {:?}", page, rval);

                    match rval {
                        ReturnCode::SUCCESS => {
                            self.write_buffer.map(|buffer| {
                                self.state.set(State::ErasingU8);
                                let len = cmp::min(data.len(), flash::h1b_hw::H1B_FLASH_PAGE_SIZE);
                                unsafe {
                                    let mut ptr = mem::transmute::<*mut u32, *mut u8>(buffer.as_mut_ptr());
                                    for i in 0..len {
                                        *ptr = data[i];
                                        ptr = ptr.offset(1);
                                    }
                                }
                            });
                            debug!("Finished writing buffer");
                            ReturnCode::SUCCESS
                        },
                        _ => {
                            rval
                        }
                    }
                }).unwrap()
            } else {
                ReturnCode::ENOMEM
            }
        }
    }
}

impl<'a> flash::Client<'a> for PersonalityDriver<'a> {
    fn erase_done(&self, _rcode: ReturnCode) {
        let state = self.state.get();
        let offset = PERSONALITY_ADDRESS;
        let target = offset / 4; // Write offset is in words
        match state {
            State::ErasingStruct => {
                if self.start_write(target) {
                    self.state.set(State::WritingStruct);
                } else {
                    self.client.map(|c| c.set_done(ReturnCode::FAIL));
                    self.state.set(State::Idle);
                }
            }

            State::ErasingU8 => {
                if self.start_write(target) {
                    self.state.set(State::WritingU8);
                } else {
                    debug!("WriteU8 failed");
                    self.client.map(|c| c.set_u8_done(ReturnCode::FAIL));
                    self.state.set(State::Idle);
                }
            },
            _ => { // Should never happen -pal
                debug!("Erase done called but in state {:?}", state);
            }
        }
    }

    fn write_done(&self, _data: &'a mut [u32], _rcode: ReturnCode) {
        let state = self.state.get();
        match state {
            State::WritingStruct => {
                self.state.set(State::Idle);
                self.client.map(|c| c.set_done(ReturnCode::SUCCESS));
            },
            State::WritingU8 => {
                self.state.set(State::Idle);
                self.client.map(|c| c.set_u8_done(ReturnCode::SUCCESS));
            },
            _ => { // Should never happen -pal
            },
        }
    }
}
<commit_msg>Remove redundant constant declarations.<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.

//! Peripheral driver for device attestation (personality) data.  This
//! is per-device data that will be stored durably on the device; this
//! implementations currently stores it in RAM.

use core::cmp;
use core::mem;
use core::cell::Cell;
use hil::personality::{Client, Personality, PersonalityData};
use hil::flash;
use kernel::ReturnCode;
use kernel::common::cells::{OptionalCell, TakeCell};

#[derive(Copy, Clone, Debug, PartialEq)]
enum State {
    Idle,
    ErasingU8,
    WritingU8,
    ErasingStruct,
    WritingStruct,
}

pub struct PersonalityDriver<'a> {
    state: Cell<State>,
    client: OptionalCell<&'a Client<'a>>,
    flash: OptionalCell<&'a flash::Flash<'a>>,
    write_buffer: TakeCell<'a, [u32]>,
}

pub static mut PERSONALITY: PersonalityDriver<'static> = unsafe {PersonalityDriver::new() };

pub static mut BUFFER: [u32; PAGE_SIZE_U32] = [0; PAGE_SIZE_U32];

// Personality data is stored as the third-to-last (N-3) page of flash;
// it is followed by the two pages used as a counter.
const PERSONALITY_ADDRESS: usize = flash::h1b_hw::H1B_FLASH_SIZE - (3 * flash::h1b_hw::H1B_FLASH_PAGE_SIZE) ;
const PERSONALITY_ADDRESS_U32: usize = PERSONALITY_ADDRESS / 4;
const PERSONALITY_SIZE: usize = flash::h1b_hw::H1B_FLASH_PAGE_SIZE;
const PAGE_SIZE_U32: usize    = flash::h1b_hw::H1B_FLASH_PAGE_SIZE / 4;

impl<'a> PersonalityDriver<'a> {
    const unsafe fn new() -> PersonalityDriver<'a> {
        PersonalityDriver {
            state: Cell::new(State::Idle),
            client: OptionalCell::empty(),
            flash: OptionalCell::empty(),
            write_buffer: TakeCell::empty(),
        }
    }

    pub fn set_flash(&self, flash: &'a flash::Flash<'a>) {
        self.flash.set(flash);
    }

    pub fn set_buffer(&self, buf: &'a mut [u32]) {
        self.write_buffer.replace(buf);
    }

    fn start_write(&self, target: usize) -> bool {
        debug!("Starting flash write target {}", target);
        if self.flash.is_none() || self.write_buffer.is_none() {
            false
        } else {
            let buf = self.write_buffer.take().unwrap();
            self.flash.map(move |flash| {
                let (_rcode, opt) = flash.write(target, buf);
                match opt {
                    None => true,  // Operation successful
                    Some(buf) => { // Not successful
                        self.write_buffer.replace(buf);
                        false
                    }
                }
            }).unwrap()
        }
    }

}

impl<'a> Personality<'a> for PersonalityDriver<'a> {

    fn set_client(&self, client: &'a Client<'a>) {
        self.client.set(client);
    }

    fn get(&self, data: &mut PersonalityData) -> ReturnCode {
        unsafe {
            self.flash.map_or(ReturnCode::ENOMEM, |flash| {
                let mut personality_ptr = mem::transmute::<*mut PersonalityData, *mut u32>(data);
                let word_count = PAGE_SIZE_U32;
                for i in 0..word_count {
                    *personality_ptr = flash.read(PERSONALITY_ADDRESS_U32 + i);
                    personality_ptr = personality_ptr.offset(1);
                }
                ReturnCode::SUCCESS
            })
        }
    }

    fn get_u8(&self, data: &mut [u8]) -> ReturnCode {
        debug!("Getting Personality as U8");
        if data.len() < PERSONALITY_SIZE {
            ReturnCode::ESIZE
        } else {
            unsafe {
                self.flash.map_or(ReturnCode::ENOMEM, |flash| {
                    let ptr = data.as_mut_ptr();
                    let mut personality_ptr = mem::transmute::<*mut u8, *mut u32>(ptr);
                    let word_count = PAGE_SIZE_U32;
                    for i in 0..word_count {
                        *personality_ptr = flash.read(PERSONALITY_ADDRESS_U32 + i);
                        personality_ptr = personality_ptr.offset(1);
                    }
                    ReturnCode::SUCCESS
                })
            }
        }
    }

    fn set(&self, data: &mut PersonalityData) -> ReturnCode {
        if self.state.get() != State::Idle {
            return ReturnCode::EBUSY;
        }
        if self.flash.is_some() {
            self.flash.map(move |flash| {
                let offset = PERSONALITY_ADDRESS;
                let page = offset / flash::h1b_hw::H1B_FLASH_PAGE_SIZE;
                let rval = flash.erase(page);
                match rval {
                    ReturnCode::SUCCESS => {
                        self.write_buffer.map(|buffer| {
                            self.state.set(State::ErasingStruct);
                            unsafe {
                                let mut ptr = mem::transmute::<*mut PersonalityData, *mut u32>(data);
                                let word_count = PAGE_SIZE_U32;
                                for i in 0..word_count {
                                    buffer[i] = *ptr;
                                    ptr = ptr.offset(1);
                                }
                            }
                        });
                        ReturnCode::SUCCESS
                    },
                    _ => {
                        rval
                    }
                }
            }).unwrap()
        } else {
            ReturnCode::ENOMEM
        }
    }

    fn set_u8(&self, data: &mut [u8]) -> ReturnCode {
        debug!("Setting Personality as U8");
        if data.len() < PERSONALITY_SIZE {
            debug!(" - ESIZE");
            ReturnCode::ESIZE
        }
        else if self.state.get() != State::Idle {
            debug!(" - EBUSY");
            ReturnCode::EBUSY
        } else {
            if self.flash.is_some() {
                self.flash.map(move |flash| {
                    let offset = PERSONALITY_ADDRESS;
                    let page = offset / flash::h1b_hw::H1B_FLASH_PAGE_SIZE;
                    let rval = flash.erase(page);
                    debug!("Erasing page {} with rval {:?}", page, rval);

                    match rval {
                        ReturnCode::SUCCESS => {
                            self.write_buffer.map(|buffer| {
                                self.state.set(State::ErasingU8);
                                let len = cmp::min(data.len(), flash::h1b_hw::H1B_FLASH_PAGE_SIZE);
                                unsafe {
                                    let mut ptr = mem::transmute::<*mut u32, *mut u8>(buffer.as_mut_ptr());
                                    for i in 0..len {
                                        *ptr = data[i];
                                        ptr = ptr.offset(1);
                                    }
                                }
                            });
                            debug!("Finished writing buffer");
                            ReturnCode::SUCCESS
                        },
                        _ => {
                            rval
                        }
                    }
                }).unwrap()
            } else {
                ReturnCode::ENOMEM
            }
        }
    }
}

impl<'a> flash::Client<'a> for PersonalityDriver<'a> {
    fn erase_done(&self, _rcode: ReturnCode) {
        let state = self.state.get();
        let target = PERSONALITY_ADDRESS_U32; // Write offset is in words
        match state {
            State::ErasingStruct => {
                if self.start_write(target) {
                    self.state.set(State::WritingStruct);
                } else {
                    self.client.map(|c| c.set_done(ReturnCode::FAIL));
                    self.state.set(State::Idle);
                }
            }

            State::ErasingU8 => {
                if self.start_write(target) {
                    self.state.set(State::WritingU8);
                } else {
                    debug!("WriteU8 failed");
                    self.client.map(|c| c.set_u8_done(ReturnCode::FAIL));
                    self.state.set(State::Idle);
                }
            },
            _ => { // Should never happen -pal
                debug!("Erase done called but in state {:?}", state);
            }
        }
    }

    fn write_done(&self, _data: &'a mut [u32], _rcode: ReturnCode) {
        let state = self.state.get();
        match state {
            State::WritingStruct => {
                self.state.set(State::Idle);
                self.client.map(|c| c.set_done(ReturnCode::SUCCESS));
            },
            State::WritingU8 => {
                self.state.set(State::Idle);
                self.client.map(|c| c.set_u8_done(ReturnCode::SUCCESS));
            },
            _ => { // Should never happen -pal
            },
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Bump hammer<commit_after><|endoftext|>


<commit_before>extern crate timely;
extern crate differential_dataflow;
extern crate graph_map;
extern crate core_affinity;

use std::rc::Rc;

use timely::dataflow::*;

use timely::order::Product;
use timely::dataflow::operators::ToStream;

use differential_dataflow::input::Input;
use differential_dataflow::Collection;
use differential_dataflow::operators::*;
use differential_dataflow::operators::arrange::ArrangeByKey;
use differential_dataflow::operators::arrange::ArrangeBySelf;
use differential_dataflow::operators::iterate::Variable;
use differential_dataflow::trace::implementations::spine_fueled::Spine;
use differential_dataflow::AsCollection;

use graph_map::GraphMMap;

type Node = u32;
type Iter = u32;
type Diff = i32;

// use differential_dataflow::trace::implementations::graph::GraphBatch;
// type GraphTrace = Spine<Node, Node, (), isize, Rc<GraphBatch<Node>>>;

use differential_dataflow::trace::implementations::ord::OrdValBatch;
type GraphTrace = Spine<Node, Node, (), Diff, Rc<OrdValBatch<Node, Node, (), Diff>>>;

fn main() {

    let filename = std::env::args().nth(1).expect("Must supply filename");
    let rootnode = std::env::args().nth(2).expect("Must supply root node").parse().expect("Invalid root node");

    timely::execute_from_args(std::env::args(), move |worker| {

        let index = worker.index();
        let peers = worker.peers();
        let timer = ::std::time::Instant::now();

        let core_ids = core_affinity::get_core_ids().unwrap();
        core_affinity::set_for_current(core_ids[index % core_ids.len()]);

        // Phase 1: Forward index.
        let mut forward = worker.dataflow(|scope| {

            let graph = GraphMMap::new(&filename);
            let nodes = graph.nodes();
            let edges = (0..nodes).filter(move |node| node % peers == index)
                                  .flat_map(move |node| {
                                      let vec = graph.edges(node).to_vec();
                                      vec.into_iter().map(move |edge| ((node as Node, edge as Node), (), 1))
                                  })
                                  .to_stream(scope)
                                  .as_collection();

            edges.arrange_by_key().trace
        });

        while worker.step() { }
        if index == 0 { println!("{:?}\tphase 1:\tforward graph indexed", timer.elapsed()); }
        let timer = ::std::time::Instant::now();

        // Phase 2: Reachability.
        let mut roots = worker.dataflow(|scope| {
            let (roots_input, roots) = scope.new_collection();
            reach(&mut forward, roots);
            roots_input
        });

        if index == 0 { roots.update(rootnode, 1); }
        roots.close();
        while worker.step() { }
        if index == 0 { println!("{:?}\tphase 2:\treach complete", timer.elapsed()); }
        let timer = ::std::time::Instant::now();

        // Phase 3: Breadth-first distance labeling.
        let mut roots = worker.dataflow(|scope| {
            let (roots_input, roots) = scope.new_collection();
            bfs(&mut forward, roots);
            roots_input
        });

        if index == 0 { roots.update(rootnode, 1); }
        roots.close();
        while worker.step() { }
        if index == 0 { println!("{:?}\tphase 3:\tbfs complete", timer.elapsed()); }
        let timer = ::std::time::Instant::now();

        // Phase 4: Reverse index.
        let mut reverse = worker.dataflow(|scope| {
            forward
                .import(scope)
                .as_collection(|&k,&v| (v,k))
                .arrange_by_key()
                .trace
        });
        while worker.step() { }
        if index == 0 { println!("{:?}\tphase 4:\treverse graph indexed", timer.elapsed()); }
        let timer = ::std::time::Instant::now();

        // Phase 5: Undirected connectivity.
        worker.dataflow(|scope| { connected_components(scope, &mut forward, &mut reverse); });

        while worker.step() { }
        if index == 0 { println!("{:?}\tphase 5:\tcc complete", timer.elapsed()); }

    }).unwrap();
}

use differential_dataflow::operators::arrange::TraceAgent;

type TraceHandle = TraceAgent<Node, Node, (), Diff, GraphTrace>;

fn reach<G: Scope<Timestamp = ()>> (
    graph: &mut TraceHandle,
    roots: Collection<G, Node, Diff>
) -> Collection<G, Node, Diff> {

    let graph = graph.import(&roots.scope());

    roots.scope().iterative::<Iter,_,_>(|scope| {

        let graph = graph.enter(scope);
        let roots = roots.enter(scope);

        let inner = Variable::new_from(roots.clone(), Product::new(Default::default(), 1));

        let result =
        graph.join_core(&inner.arrange_by_self(), |_src,&dst,&()| Some(dst))
             .concat(&roots)
             .threshold_total(|_,_| 1);

        inner.set(&result);
        result.leave()
    })
}

fn bfs<G: Scope<Timestamp = ()>> (
    graph: &mut TraceHandle,
    roots: Collection<G, Node, Diff>
) -> Collection<G, (Node, u32), Diff> {

    let graph = graph.import(&roots.scope());
    let roots = roots.map(|r| (r,0));

    roots.scope().iterative::<Iter,_,_>(|scope| {

        let graph = graph.enter(scope);
        let roots = roots.enter(scope);

        let inner = Variable::new_from(roots.clone(), Product::new(Default::default(), 1));
        let result =
        graph.join_map(&inner, |_src,&dest,&dist| (dest, dist+1))
             .concat(&roots)
             .reduce(|_key, input, output| output.push((*input[0].0,1)));

        inner.set(&result);
        result.leave()
    })
}

fn connected_components<G: Scope<Timestamp = ()>>(
    scope: &mut G,
    forward: &mut TraceHandle,
    reverse: &mut TraceHandle,
) -> Collection<G, (Node, Node), Diff> {

    let forward = forward.import(scope);
    let reverse = reverse.import(scope);

    // each edge (x,y) means that we need at least a label for the min of x and y.
    let nodes_f = forward.flat_map_ref(|k,v| if k < v { Some(*k) } else { None });
    let nodes_r = reverse.flat_map_ref(|k,v| if k < v { Some(*k) } else { None });
    let nodes = nodes_f.concat(&nodes_r).consolidate().map(|x| (x,x));

    scope.iterative(|scope| {

        // import arrangements, nodes.
        let forward = forward.enter(scope);
        let reverse = reverse.enter(scope);
        let nodes = nodes.enter(scope);

        use differential_dataflow::operators::iterate::Variable;
        let inner = Variable::new(scope, Product::new(Default::default(), 1));

        let labels = inner.arrange_by_key();
        let f_prop = labels.join_core(&forward, |_k,l,d| Some((*d,*l)));
        let r_prop = labels.join_core(&reverse, |_k,l,d| Some((*d,*l)));

        use timely::dataflow::operators::{Map, Concat, Delay};

        let result =
        nodes
            .inner
            .map_in_place(|dtr| (dtr.1).inner = 256 * ((((::std::mem::size_of::<Node>() * 8) as u32) - (dtr.0).1.leading_zeros())))
            .concat(&inner.filter(|_| false).inner)
            .delay(|dtr,_| dtr.1.clone())
            .as_collection()
            .concat(&f_prop)
            .concat(&r_prop)
            .reduce(|_, s, t| { t.push((*s[0].0, 1)); });

        inner.set(&result);
        result.leave()
    })
}

fn _connected_components_odd<G: Scope<Timestamp = ()>>(
    scope: &mut G,
    forward: &mut TraceHandle,
    reverse: &mut TraceHandle,
) -> Collection<G, (Node, Node), Diff> {

    let forward = forward.import(scope);
    let reverse = reverse.import(scope);

    // each edge (x,y) means that we need at least a label for the min of x and y.
    let nodes_f = forward.flat_map_ref(|k,v| if k < v { Some(*k) } else { None });
    let nodes_r = reverse.flat_map_ref(|k,v| if k < v { Some(*k) } else { None });
    let nodes = nodes_f.concat(&nodes_r).consolidate().map(|x| (x,x));

    nodes.scope().iterative(|scope| {

        // import arrangements, nodes.
        let forward = forward.enter(scope);
        let reverse = reverse.enter(scope);
        let nodes = nodes.enter(scope);

        use differential_dataflow::operators::iterate::Variable;
        let inner = Variable::new(scope, Product::new(Default::default(), 1));

        // let labels = inner.arrange_by_key();

        use timely::dataflow::operators::{Map, Concat, Delay};

        let proposals =
        nodes
            .inner
            .map_in_place(|dtr| (dtr.1).inner = 256 * ((((::std::mem::size_of::<Node>() * 8) as u32) - (dtr.0).1.leading_zeros())))
            .concat(&inner.inner)
            .delay(|dtr,_| dtr.1.clone())
            .as_collection();

        use differential_dataflow::operators::reduce::ReduceCore;

        let labels =
        proposals
            .reduce_abelian::<_,_,Spine<Node, Node, _, Diff, Rc<OrdValBatch<Node, Node, _, Diff>>>,_>(|_, s, t| { t.push((*s[0].0, 1)); });

        let f_prop = labels.join_core(&forward, |_k,l,d| Some((*d,*l)));
        let r_prop = labels.join_core(&reverse, |_k,l,d| Some((*d,*l)));

        inner.set(&f_prop.concat(&r_prop).consolidate());
        labels.as_collection(|k,v| (*k,*v)).leave()
    })
}

<commit_msg>update generic parameters<commit_after>extern crate timely;
extern crate differential_dataflow;
extern crate graph_map;
extern crate core_affinity;

use std::rc::Rc;

use timely::dataflow::*;

use timely::order::Product;
use timely::dataflow::operators::ToStream;

use differential_dataflow::input::Input;
use differential_dataflow::Collection;
use differential_dataflow::operators::*;
use differential_dataflow::operators::arrange::ArrangeByKey;
use differential_dataflow::operators::arrange::ArrangeBySelf;
use differential_dataflow::operators::iterate::Variable;
use differential_dataflow::trace::implementations::spine_fueled::Spine;
use differential_dataflow::AsCollection;

use graph_map::GraphMMap;

type Node = u32;
type Iter = u32;
type Diff = i32;

// use differential_dataflow::trace::implementations::graph::GraphBatch;
// type GraphTrace = Spine<Node, Node, (), isize, Rc<GraphBatch<Node>>>;

use differential_dataflow::trace::implementations::ord::OrdValBatch;
type GraphTrace = Spine<Node, Node, (), Diff, Rc<OrdValBatch<Node, Node, (), Diff>>>;

fn main() {

    let filename = std::env::args().nth(1).expect("Must supply filename");
    let rootnode = std::env::args().nth(2).expect("Must supply root node").parse().expect("Invalid root node");

    timely::execute_from_args(std::env::args(), move |worker| {

        let index = worker.index();
        let peers = worker.peers();
        let timer = ::std::time::Instant::now();

        let core_ids = core_affinity::get_core_ids().unwrap();
        core_affinity::set_for_current(core_ids[index % core_ids.len()]);

        // Phase 1: Forward index.
        let mut forward = worker.dataflow(|scope| {

            let graph = GraphMMap::new(&filename);
            let nodes = graph.nodes();
            let edges = (0..nodes).filter(move |node| node % peers == index)
                                  .flat_map(move |node| {
                                      let vec = graph.edges(node).to_vec();
                                      vec.into_iter().map(move |edge| ((node as Node, edge as Node), (), 1))
                                  })
                                  .to_stream(scope)
                                  .as_collection();

            edges.arrange_by_key().trace
        });

        while worker.step() { }
        if index == 0 { println!("{:?}\tphase 1:\tforward graph indexed", timer.elapsed()); }
        let timer = ::std::time::Instant::now();

        // Phase 2: Reachability.
        let mut roots = worker.dataflow(|scope| {
            let (roots_input, roots) = scope.new_collection();
            reach(&mut forward, roots);
            roots_input
        });

        if index == 0 { roots.update(rootnode, 1); }
        roots.close();
        while worker.step() { }
        if index == 0 { println!("{:?}\tphase 2:\treach complete", timer.elapsed()); }
        let timer = ::std::time::Instant::now();

        // Phase 3: Breadth-first distance labeling.
        let mut roots = worker.dataflow(|scope| {
            let (roots_input, roots) = scope.new_collection();
            bfs(&mut forward, roots);
            roots_input
        });

        if index == 0 { roots.update(rootnode, 1); }
        roots.close();
        while worker.step() { }
        if index == 0 { println!("{:?}\tphase 3:\tbfs complete", timer.elapsed()); }
        let timer = ::std::time::Instant::now();

        // Phase 4: Reverse index.
        let mut reverse = worker.dataflow(|scope| {
            forward
                .import(scope)
                .as_collection(|&k,&v| (v,k))
                .arrange_by_key()
                .trace
        });
        while worker.step() { }
        if index == 0 { println!("{:?}\tphase 4:\treverse graph indexed", timer.elapsed()); }
        let timer = ::std::time::Instant::now();

        // Phase 5: Undirected connectivity.
        worker.dataflow(|scope| { connected_components(scope, &mut forward, &mut reverse); });

        while worker.step() { }
        if index == 0 { println!("{:?}\tphase 5:\tcc complete", timer.elapsed()); }

    }).unwrap();
}

use differential_dataflow::operators::arrange::TraceAgent;

type TraceHandle = TraceAgent<GraphTrace>;

fn reach<G: Scope<Timestamp = ()>> (
    graph: &mut TraceHandle,
    roots: Collection<G, Node, Diff>
) -> Collection<G, Node, Diff> {

    let graph = graph.import(&roots.scope());

    roots.scope().iterative::<Iter,_,_>(|scope| {

        let graph = graph.enter(scope);
        let roots = roots.enter(scope);

        let inner = Variable::new_from(roots.clone(), Product::new(Default::default(), 1));

        let result =
        graph.join_core(&inner.arrange_by_self(), |_src,&dst,&()| Some(dst))
             .concat(&roots)
             .threshold_total(|_,_| 1);

        inner.set(&result);
        result.leave()
    })
}

fn bfs<G: Scope<Timestamp = ()>> (
    graph: &mut TraceHandle,
    roots: Collection<G, Node, Diff>
) -> Collection<G, (Node, u32), Diff> {

    let graph = graph.import(&roots.scope());
    let roots = roots.map(|r| (r,0));

    roots.scope().iterative::<Iter,_,_>(|scope| {

        let graph = graph.enter(scope);
        let roots = roots.enter(scope);

        let inner = Variable::new_from(roots.clone(), Product::new(Default::default(), 1));
        let result =
        graph.join_map(&inner, |_src,&dest,&dist| (dest, dist+1))
             .concat(&roots)
             .reduce(|_key, input, output| output.push((*input[0].0,1)));

        inner.set(&result);
        result.leave()
    })
}

fn connected_components<G: Scope<Timestamp = ()>>(
    scope: &mut G,
    forward: &mut TraceHandle,
    reverse: &mut TraceHandle,
) -> Collection<G, (Node, Node), Diff> {

    let forward = forward.import(scope);
    let reverse = reverse.import(scope);

    // each edge (x,y) means that we need at least a label for the min of x and y.
    let nodes_f = forward.flat_map_ref(|k,v| if k < v { Some(*k) } else { None });
    let nodes_r = reverse.flat_map_ref(|k,v| if k < v { Some(*k) } else { None });
    let nodes = nodes_f.concat(&nodes_r).consolidate().map(|x| (x,x));

    scope.iterative(|scope| {

        // import arrangements, nodes.
        let forward = forward.enter(scope);
        let reverse = reverse.enter(scope);
        let nodes = nodes.enter(scope);

        use differential_dataflow::operators::iterate::Variable;
        let inner = Variable::new(scope, Product::new(Default::default(), 1));

        let labels = inner.arrange_by_key();
        let f_prop = labels.join_core(&forward, |_k,l,d| Some((*d,*l)));
        let r_prop = labels.join_core(&reverse, |_k,l,d| Some((*d,*l)));

        use timely::dataflow::operators::{Map, Concat, Delay};

        let result =
        nodes
            .inner
            .map_in_place(|dtr| (dtr.1).inner = 256 * ((((::std::mem::size_of::<Node>() * 8) as u32) - (dtr.0).1.leading_zeros())))
            .concat(&inner.filter(|_| false).inner)
            .delay(|dtr,_| dtr.1.clone())
            .as_collection()
            .concat(&f_prop)
            .concat(&r_prop)
            .reduce(|_, s, t| { t.push((*s[0].0, 1)); });

        inner.set(&result);
        result.leave()
    })
}

fn _connected_components_odd<G: Scope<Timestamp = ()>>(
    scope: &mut G,
    forward: &mut TraceHandle,
    reverse: &mut TraceHandle,
) -> Collection<G, (Node, Node), Diff> {

    let forward = forward.import(scope);
    let reverse = reverse.import(scope);

    // each edge (x,y) means that we need at least a label for the min of x and y.
    let nodes_f = forward.flat_map_ref(|k,v| if k < v { Some(*k) } else { None });
    let nodes_r = reverse.flat_map_ref(|k,v| if k < v { Some(*k) } else { None });
    let nodes = nodes_f.concat(&nodes_r).consolidate().map(|x| (x,x));

    nodes.scope().iterative(|scope| {

        // import arrangements, nodes.
        let forward = forward.enter(scope);
        let reverse = reverse.enter(scope);
        let nodes = nodes.enter(scope);

        use differential_dataflow::operators::iterate::Variable;
        let inner = Variable::new(scope, Product::new(Default::default(), 1));

        // let labels = inner.arrange_by_key();

        use timely::dataflow::operators::{Map, Concat, Delay};

        let proposals =
        nodes
            .inner
            .map_in_place(|dtr| (dtr.1).inner = 256 * ((((::std::mem::size_of::<Node>() * 8) as u32) - (dtr.0).1.leading_zeros())))
            .concat(&inner.inner)
            .delay(|dtr,_| dtr.1.clone())
            .as_collection();

        use differential_dataflow::operators::reduce::ReduceCore;

        let labels =
        proposals
            .reduce_abelian::<_,Spine<Node, Node, _, Diff, Rc<OrdValBatch<Node, Node, _, Diff>>>>(|_, s, t| { t.push((*s[0].0, 1)); });

        let f_prop = labels.join_core(&forward, |_k,l,d| Some((*d,*l)));
        let r_prop = labels.join_core(&reverse, |_k,l,d| Some((*d,*l)));

        inner.set(&f_prop.concat(&r_prop).consolidate());
        labels.as_collection(|k,v| (*k,*v)).leave()
    })
}

<|endoftext|>