<commit_before>extern crate pkg_config;

use std::process::Command;
use std::fs;
use std::path::Path;
use std::env;
use std::path::PathBuf;

fn main() {
    let wcd = env::current_dir().unwrap();
    let build = PathBuf::from(&wcd.join("ext/libqmlrswrapper/build"));
    let _ = fs::create_dir_all(&build);

    /*
     * Prameters for supporting QT on OS X installed via homebres
     *
     * Because QT5 conflicts with QT4 the homebrew package manager won't link
     * the QT5 package into the default search paths for libraries, to deal
     * with this we need to give pkg-config and cmake a nudge in the right
     * direction.
     */
    if cfg!(target_os = "macos") {
        // Point at homebrew's QT5 install location, this will likely fail if
        // another package manager was used.
        let qt5_lib_path = Path::new("/usr/local/opt/qt5/lib");

        if Path::exists(qt5_lib_path) {
            // First nudge cmake in the direction of the .cmake files added by
            // homebrew. This clobbers the existing value if present, it's
            // unlikely to be present though.
            env::set_var("CMAKE_PREFIX_PATH", qt5_lib_path.join("cmake"));

            // Nudge pkg-config in the direction of the brewed QT to ensure the
            // correct compiler flags get found for the project.
            env::set_var("PKG_CONFIG_PATH", qt5_lib_path.join("pkgconfig"));
        } else {
            panic!("The QT5 was not found at the expected location ({}) please install it via homebrew.", qt5_lib_path.display());
        }
    }

    if cfg!(windows) {
        let is_msys = env::var("MSYSTEM").is_ok();
        let mut myargs = vec![];

        if is_msys {
            myargs.push("-GMSYS Makefiles");
        } else {
            myargs.push("-GVisual Studio 12 2013 Win64");
        }

        myargs.push("..");

        Command::new("cmake")
            .args(&myargs)
            .current_dir(&build)
            .status().and_then(|x| Ok(x.success()) ).unwrap_or_else(|e| {
                panic!("Failed to run cmake: {}", e);
            });
    } else {
        Command::new("cmake")
            .args(&vec![".."])
            .current_dir(&build)
            .status().and_then(|x| Ok(x.success()) ).unwrap_or_else(|e| {
                panic!("Failed to run cmake: {}", e);
            });
    }

    Command::new("cmake")
        .args(&vec!["--build","."])
        .current_dir(&build)
        .status().and_then(|x| Ok(x.success()) ).unwrap_or_else(|e| {
            panic!("Failed to run build: {}", e);
        });

    println!("cargo:rustc-link-lib=static=qmlrswrapper");

    if cfg!(windows) {
        println!("cargo:rustc-link-search=native={}\\system32",env::var("WINDIR").unwrap());
        println!("cargo:rustc-link-search=native={}\\Debug",build.display());
        println!("cargo:rustc-link-search=native={}\\lib",env::var("QTDIR").unwrap());

        println!("cargo:rustc-link-lib=dylib=Qt5Core");
        println!("cargo:rustc-link-lib=dylib=Qt5Gui");
        println!("cargo:rustc-link-lib=dylib=Qt5Qml");
        println!("cargo:rustc-link-lib=dylib=Qt5Quick");
    } else {
        println!("cargo:rustc-link-search=native={}{}",build.display(),if cfg!(windows) { "\\Debug" } else { "" });
        println!("cargo:rustc-link-lib=dylib=stdc++");
        pkg_config::find_library("Qt5Core Qt5Gui Qt5Qml Qt5Quick").unwrap();
    }
}
<commit_msg>Add support for Qt on BSD-like systems installed via the ports system<commit_after>extern crate pkg_config;

use std::process::Command;
use std::fs;
use std::path::Path;
use std::env;
use std::path::PathBuf;

fn main() {
    let wcd = env::current_dir().unwrap();
    let build = PathBuf::from(&wcd.join("ext/libqmlrswrapper/build"));
    let _ = fs::create_dir_all(&build);

    /*
     * Support Qt installed via the Ports system on BSD-like systems.
     *
     * The native libs are in `/usr/local/lib`, which is not linked against by default.
     * This means that either the user or every package has to add this if they want to link
     * against something that is not part of the core distribution in `/usr/lib`.
     *
     * See https://wiki.freebsd.org/WarnerLosh/UsrLocal for the line of reasoning & how this will
     * change in the future.
     */
    if cfg!(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd",
                target_os = "dragonfly", target_os = "bitrig")) {
        println!("cargo:rustc-link-search=native=/usr/local/lib");
    }

    /*
     * Prameters for supporting QT on OS X installed via homebres
     *
     * Because QT5 conflicts with QT4 the homebrew package manager won't link
     * the QT5 package into the default search paths for libraries, to deal
     * with this we need to give pkg-config and cmake a nudge in the right
     * direction.
     */
    if cfg!(target_os = "macos") {
        // Point at homebrew's QT5 install location, this will likely fail if
        // another package manager was used.
        let qt5_lib_path = Path::new("/usr/local/opt/qt5/lib");

        if Path::exists(qt5_lib_path) {
            // First nudge cmake in the direction of the .cmake files added by
            // homebrew. This clobbers the existing value if present, it's
            // unlikely to be present though.
            env::set_var("CMAKE_PREFIX_PATH", qt5_lib_path.join("cmake"));

            // Nudge pkg-config in the direction of the brewed QT to ensure the
            // correct compiler flags get found for the project.
            env::set_var("PKG_CONFIG_PATH", qt5_lib_path.join("pkgconfig"));
        } else {
            panic!("The QT5 was not found at the expected location ({}) please install it via homebrew.", qt5_lib_path.display());
        }
    }

    if cfg!(windows) {
        let is_msys = env::var("MSYSTEM").is_ok();
        let mut myargs = vec![];

        if is_msys {
            myargs.push("-GMSYS Makefiles");
        } else {
            myargs.push("-GVisual Studio 12 2013 Win64");
        }

        myargs.push("..");

        Command::new("cmake")
            .args(&myargs)
            .current_dir(&build)
            .status().and_then(|x| Ok(x.success()) ).unwrap_or_else(|e| {
                panic!("Failed to run cmake: {}", e);
            });
    } else {
        Command::new("cmake")
            .args(&vec![".."])
            .current_dir(&build)
            .status().and_then(|x| Ok(x.success()) ).unwrap_or_else(|e| {
                panic!("Failed to run cmake: {}", e);
            });
    }

    Command::new("cmake")
        .args(&vec!["--build","."])
        .current_dir(&build)
        .status().and_then(|x| Ok(x.success()) ).unwrap_or_else(|e| {
            panic!("Failed to run build: {}", e);
        });

    println!("cargo:rustc-link-lib=static=qmlrswrapper");

    if cfg!(windows) {
        println!("cargo:rustc-link-search=native={}\\system32",env::var("WINDIR").unwrap());
        println!("cargo:rustc-link-search=native={}\\Debug",build.display());
        println!("cargo:rustc-link-search=native={}\\lib",env::var("QTDIR").unwrap());

        println!("cargo:rustc-link-lib=dylib=Qt5Core");
        println!("cargo:rustc-link-lib=dylib=Qt5Gui");
        println!("cargo:rustc-link-lib=dylib=Qt5Qml");
        println!("cargo:rustc-link-lib=dylib=Qt5Quick");
    } else {
        println!("cargo:rustc-link-search=native={}{}",build.display(),if cfg!(windows) { "\\Debug" } else { "" });
        println!("cargo:rustc-link-lib=dylib=stdc++");
        pkg_config::find_library("Qt5Core Qt5Gui Qt5Qml Qt5Quick").unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate cmake;
extern crate bindgen;

use std::env;
use std::path::PathBuf;
use cmake::Config;

fn main() {
    let bindings = bindgen::Builder::default()
        .clang_arg("-Ivendor/enet/include/")
        .header("wrapper.h")
        .generate()
        .expect("Unable to generate bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");

    let dst = Config::new("vendor/enet")
                .build_target("enet")
                .build();

    eprintln!("LUL: {}", dst.display());

    println!("cargo:rustc-link-search=native={}/build", dst.display());
    println!("cargo:rustc-link-lib=static=enet");
}<commit_msg>link to winmm.lib for windows targets<commit_after>extern crate cmake;
extern crate bindgen;

use std::env;
use std::path::PathBuf;
use cmake::Config;

fn main() {
    let target = env::var("TARGET").unwrap();
    let bindings = bindgen::Builder::default()
        .clang_arg("-Ivendor/enet/include/")
        .header("wrapper.h")
        .generate()
        .expect("Unable to generate bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");

    let dst = Config::new("vendor/enet")
                .build_target("enet")
                .build();

    eprintln!("LUL: {}", dst.display());

    println!("cargo:rustc-link-search=native={}/build", dst.display());
    println!("cargo:rustc-link-lib=static=enet");
    if target.contains("windows") {
        println!("cargo:rustc-link-lib=static=winmm");
    }
}
<|endoftext|>


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

#[cfg(feature = "c-api")]
extern crate cheddar;

fn main() { handle_c_api(); }

#[cfg(not(feature = "c-api"))]
fn handle_c_api() {}

#[cfg(feature = "c-api")]
fn handle_c_api() {
  let top_dir: std::path::PathBuf =
    std::env::var_os("CARGO_MANIFEST_DIR").expect("could not find cargo manifest directory").into();
  cheddar::Cheddar::new()
    .expect("unable to read cargo manifest")
    .module("c_api")
    .expect("malformed header path")
    .run_build(top_dir.join("gen").join("include").join("konane.h"));
}
<commit_msg>whoops<commit_after>// build.rs
// Copyright 2016 Alexander Altman
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[cfg(feature = "c-api")]
extern crate cheddar;

fn main() { handle_c_api(); }

#[cfg(not(feature = "c-api"))]
fn handle_c_api() {}

#[cfg(feature = "c-api")]
fn handle_c_api() {
  let top_dir: std::path::PathBuf =
    std::env::var_os("CARGO_MANIFEST_DIR").expect("could not find cargo manifest directory").into();
  cheddar::Cheddar::new()
    .expect("unable to read cargo manifest")
    .module("c_api")
    .expect("malformed header path")
    .run_build(top_dir.join("target").join("gen").join("include").join("konane.h"));
}
<|endoftext|>


<commit_before>//! A lexicographically ordered pair of timestamps.
//!
//! Two timestamps (s1, t1) and (s2, t2) are ordered either if
//! s1 and s2 are ordered, or if s1 equals s2 and t1 and t2 are
//! ordered.
//!
//! The join of two timestamps should have as its first coordinate
//! the join of the first coordinates, and for its second coordinate
//! the join of the second coordinates for elements whose first
//! coordinate equals the computed join. That may be the minimum
//! element of the second lattice, if neither first element equals
//! the join.

/// A pair of timestamps, partially ordered by the product order.
#[derive(Debug, Hash, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Abomonation, Serialize, Deserialize)]
pub struct AltNeu<T> {
    pub time: T,
    pub neu: bool,  // alt < neu in timestamp comparisons.
}

impl<T> AltNeu<T> {
    pub fn alt(time: T) -> Self { AltNeu { time, neu: false } }
    pub fn neu(time: T) -> Self { AltNeu { time, neu: true } }
}

// Implement timely dataflow's `PartialOrder` trait.
use timely::order::PartialOrder;
impl<T: PartialOrder> PartialOrder for AltNeu<T> {
    fn less_equal(&self, other: &Self) -> bool {
        if self.time.eq(&other.time) {
            self.neu <= other.neu
        }
        else {
            self.time.less_equal(&other.time)
        }
    }
}

// Implement timely dataflow's `PathSummary` trait.
// This is preparation for the `Timestamp` implementation below.
use timely::progress::PathSummary;
impl<T: Timestamp> PathSummary<AltNeu<T>> for () {
    fn results_in(&self, timestamp: &AltNeu<T>) -> Option<AltNeu<T>> {
        Some(timestamp.clone())
    }
    fn followed_by(&self, other: &Self) -> Option<Self> {
        Some(other.clone())
    }
}

// Implement timely dataflow's `Timestamp` trait.
use timely::progress::Timestamp;
impl<T: Timestamp> Timestamp for AltNeu<T> {
    type Summary = ();
}

use timely::progress::timestamp::Refines;

impl<T: Timestamp> Refines<T> for AltNeu<T> {
    fn to_inner(other: T) -> Self {
        AltNeu::alt(other)
    }
    fn to_outer(self: AltNeu<T>) -> T {
        self.time
    }
    fn summarize(_path: ()) -> <T as Timestamp>::Summary {
        Default::default()
    }
}

// Implement differential dataflow's `Lattice` trait.
// This extends the `PartialOrder` implementation with additional structure.
use differential_dataflow::lattice::Lattice;
impl<T: Lattice> Lattice for AltNeu<T> {
    fn minimum() -> Self { AltNeu::alt(T::minimum()) }
    fn maximum() -> Self { AltNeu::neu(T::maximum()) }
    fn join(&self, other: &Self) -> Self {
        let time = self.time.join(&other.time);
        let mut neu = false;
        if time == self.time {
            neu = neu || self.neu;
        }
        if time == other.time {
            neu = neu || other.neu;
        }
        AltNeu { time, neu }
    }
    fn meet(&self, other: &Self) -> Self {
        let time = self.time.meet(&other.time);
        let mut neu = true;
        if time == self.time {
            neu = neu && self.neu;
        }
        if time == other.time {
            neu = neu && other.neu;
        }
        AltNeu { time, neu }
    }
}<commit_msg>Remove maximum for altneu<commit_after>//! A lexicographically ordered pair of timestamps.
//!
//! Two timestamps (s1, t1) and (s2, t2) are ordered either if
//! s1 and s2 are ordered, or if s1 equals s2 and t1 and t2 are
//! ordered.
//!
//! The join of two timestamps should have as its first coordinate
//! the join of the first coordinates, and for its second coordinate
//! the join of the second coordinates for elements whose first
//! coordinate equals the computed join. That may be the minimum
//! element of the second lattice, if neither first element equals
//! the join.

/// A pair of timestamps, partially ordered by the product order.
#[derive(Debug, Hash, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Abomonation, Serialize, Deserialize)]
pub struct AltNeu<T> {
    pub time: T,
    pub neu: bool,  // alt < neu in timestamp comparisons.
}

impl<T> AltNeu<T> {
    pub fn alt(time: T) -> Self { AltNeu { time, neu: false } }
    pub fn neu(time: T) -> Self { AltNeu { time, neu: true } }
}

// Implement timely dataflow's `PartialOrder` trait.
use timely::order::PartialOrder;
impl<T: PartialOrder> PartialOrder for AltNeu<T> {
    fn less_equal(&self, other: &Self) -> bool {
        if self.time.eq(&other.time) {
            self.neu <= other.neu
        }
        else {
            self.time.less_equal(&other.time)
        }
    }
}

// Implement timely dataflow's `PathSummary` trait.
// This is preparation for the `Timestamp` implementation below.
use timely::progress::PathSummary;
impl<T: Timestamp> PathSummary<AltNeu<T>> for () {
    fn results_in(&self, timestamp: &AltNeu<T>) -> Option<AltNeu<T>> {
        Some(timestamp.clone())
    }
    fn followed_by(&self, other: &Self) -> Option<Self> {
        Some(other.clone())
    }
}

// Implement timely dataflow's `Timestamp` trait.
use timely::progress::Timestamp;
impl<T: Timestamp> Timestamp for AltNeu<T> {
    type Summary = ();
}

use timely::progress::timestamp::Refines;

impl<T: Timestamp> Refines<T> for AltNeu<T> {
    fn to_inner(other: T) -> Self {
        AltNeu::alt(other)
    }
    fn to_outer(self: AltNeu<T>) -> T {
        self.time
    }
    fn summarize(_path: ()) -> <T as Timestamp>::Summary {
        Default::default()
    }
}

// Implement differential dataflow's `Lattice` trait.
// This extends the `PartialOrder` implementation with additional structure.
use differential_dataflow::lattice::Lattice;
impl<T: Lattice> Lattice for AltNeu<T> {
    fn minimum() -> Self { AltNeu::alt(T::minimum()) }
    fn join(&self, other: &Self) -> Self {
        let time = self.time.join(&other.time);
        let mut neu = false;
        if time == self.time {
            neu = neu || self.neu;
        }
        if time == other.time {
            neu = neu || other.neu;
        }
        AltNeu { time, neu }
    }
    fn meet(&self, other: &Self) -> Self {
        let time = self.time.meet(&other.time);
        let mut neu = true;
        if time == self.time {
            neu = neu && self.neu;
        }
        if time == other.time {
            neu = neu && other.neu;
        }
        AltNeu { time, neu }
    }
}
<|endoftext|>


<commit_before>#[ macro_use ]
extern crate clap;

extern crate btrfs;
extern crate sha2;
extern crate termion;

#[ doc (hidden) ]
#[ macro_use ]
mod misc;

mod examine;
mod output;
mod scan;
mod types;

use std::path::PathBuf;
use std::process;

use types::*;
use output::Output;

fn main () {

	let arguments =
		parse_arguments ();

	let mut output =
		Output::new ();

	match main_real (
		& arguments,
		& mut output,
	) {

		Ok (_) => (),

		Err (error_message) => {

			output.clear_status ();

			output.message (
				& format! (
					"Error: {}",
					error_message));

			process::exit (1);

		},

	}

}

fn parse_arguments (
) -> Arguments {

	let argument_matches = (
		clap::App::new ("Btrfs Dedupe")

		.arg (
			clap::Arg::with_name ("match-filename")
				.long ("match-filename")
				.help ("Match filename as well as checksum")
		)

		.arg (
			clap::Arg::with_name ("root-path")
				.multiple (true)
				.help ("Root path to scan for files")
		)

	).get_matches ();

	let match_filename =
		argument_matches.is_present (
			"match-filename",
		);

	let root_paths = (
		argument_matches.values_of_os (
			"root-path",
		)
	).map (
		|os_values|

		os_values.map (
			|os_value|

			PathBuf::from (os_value)

		).collect ()

	).unwrap_or (
		Vec::new (),
	);

	Arguments {
		match_filename: match_filename,
		root_paths: root_paths,
	}

}

fn main_real (
	arguments: & Arguments,
	output: & mut Output,
) -> Result <(), String> {

	// create a list of all files with the same name and size

	let file_metadata_lists: FileMetadataLists = try! (
		scan::scan_directories (
			& arguments,
			output)
	);

	// discard any with only a single coincidence

	let file_metadata_lists: FileMetadataLists =
		file_metadata_lists.into_iter ().filter (
			|& (_, ref value)|

		value.len () > 1

	).collect ();

	output.message (
		& format! (
			"Found {} {} that coincide",
			file_metadata_lists.len (),
			if arguments.match_filename {
				"filenames and sizes"
			} else {
				"sizes"
			}));

	// perform a checksum on each one

	let (filename_and_checksum_lists, checksum_error_count) =
		examine::split_by_hash (
			output,
			file_metadata_lists);

	output.message (
		& format! (
			"Found {} filenames and checksums that coincide",
			filename_and_checksum_lists.len ()));

	if checksum_error_count > 0 {

		output.message (
			& format! (
				"Encountered {} errors while calculating checksums",
				checksum_error_count));

	}

	if filename_and_checksum_lists.is_empty () {
		return Ok (());
	}

	// deduplicate the matches

	let mut progress: usize = 0;
	let target = filename_and_checksum_lists.len ();

	let mut error_count: u64 = 0;

	output.status (
		"Deduplication progress: 0%");

	for (_, paths)
	in filename_and_checksum_lists.into_iter () {

		let (first_path_slice, other_paths) =
			paths.split_at (1);

		let first_path =
			first_path_slice.into_iter ().next ().unwrap ();

		for paths_chunk in other_paths.chunks (512) {

			match btrfs::deduplicate_files_with_source (
				first_path,
				paths_chunk,
			) {

				Ok (_) => (),
				Err (_) => error_count += 1,

			}

		}

		progress += 1;

		if progress % 256 == 0 {

			output.status (
				& format! (
					"Deduplication progress: {}%",
					progress * 100 / target));

		}

	};

	output.clear_status ();

	if error_count > 0 {

		output.message (
			& format! (
				"Encountered {} errors during deduplication",
				error_count));

	} else {

		output.message (
			& format! (
				"Deduplicated all files successfully"));

	}

	// return

	Ok (())

}

// ex: noet ts=4 filetype=rust
<commit_msg>improve some messages and variable names<commit_after>#[ macro_use ]
extern crate clap;

extern crate btrfs;
extern crate sha2;
extern crate termion;

#[ doc (hidden) ]
#[ macro_use ]
mod misc;

mod examine;
mod output;
mod scan;
mod types;

use std::path::PathBuf;
use std::process;

use types::*;
use output::Output;

fn main () {

	let arguments =
		parse_arguments ();

	let mut output =
		Output::new ();

	match main_real (
		& arguments,
		& mut output,
	) {

		Ok (_) => (),

		Err (error_message) => {

			output.clear_status ();

			output.message (
				& format! (
					"Error: {}",
					error_message));

			process::exit (1);

		},

	}

}

fn parse_arguments (
) -> Arguments {

	let argument_matches = (
		clap::App::new ("Btrfs Dedupe")

		.arg (
			clap::Arg::with_name ("match-filename")
				.long ("match-filename")
				.help ("Match filename as well as checksum")
		)

		.arg (
			clap::Arg::with_name ("root-path")
				.multiple (true)
				.help ("Root path to scan for files")
		)

	).get_matches ();

	let match_filename =
		argument_matches.is_present (
			"match-filename",
		);

	let root_paths = (
		argument_matches.values_of_os (
			"root-path",
		)
	).map (
		|os_values|

		os_values.map (
			|os_value|

			PathBuf::from (os_value)

		).collect ()

	).unwrap_or (
		Vec::new (),
	);

	Arguments {
		match_filename: match_filename,
		root_paths: root_paths,
	}

}

fn main_real (
	arguments: & Arguments,
	output: & mut Output,
) -> Result <(), String> {

	// create a list of all files with the same name and size

	let file_metadata_lists: FileMetadataLists = try! (
		scan::scan_directories (
			& arguments,
			output)
	);

	// discard any with only a single coincidence

	let file_metadata_lists: FileMetadataLists =
		file_metadata_lists.into_iter ().filter (
			|& (_, ref value)|

		value.len () > 1

	).collect ();

	output.message (
		& format! (
			"Found {} {} that coincide",
			file_metadata_lists.len (),
			if arguments.match_filename {
				"filenames and sizes"
			} else {
				"sizes"
			}));

	// perform a checksum on each one

	let (hash_lists, checksum_error_count) =
		examine::split_by_hash (
			output,
			file_metadata_lists);

	output.message (
		& format! (
			"Found {} {} that coincide",
			hash_lists.len (),
			if arguments.match_filename {
				"filenames and hashes"
			} else {
				"checksums"
			}));

	if checksum_error_count > 0 {

		output.message (
			& format! (
				"Encountered {} errors while calculating checksums",
				checksum_error_count));

	}

	if hash_lists.is_empty () {
		return Ok (());
	}

	// deduplicate the matches

	let mut progress: usize = 0;
	let target = hash_lists.len ();

	let mut error_count: u64 = 0;

	output.status (
		"Deduplication progress: 0%");

	for (_, paths)
	in hash_lists.into_iter () {

		let (first_path_slice, other_paths) =
			paths.split_at (1);

		let first_path =
			first_path_slice.into_iter ().next ().unwrap ();

		for paths_chunk in other_paths.chunks (512) {

			match btrfs::deduplicate_files_with_source (
				first_path,
				paths_chunk,
			) {

				Ok (_) => (),
				Err (_) => error_count += 1,

			}

		}

		progress += 1;

		if progress % 256 == 0 {

			output.status (
				& format! (
					"Deduplication progress: {}%",
					progress * 100 / target));

		}

	};

	output.clear_status ();

	if error_count > 0 {

		output.message (
			& format! (
				"Encountered {} errors during deduplication",
				error_count));

	} else {

		output.message (
			& format! (
				"Deduplicated all files successfully"));

	}

	// return

	Ok (())

}

// ex: noet ts=4 filetype=rust
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::env;
use shellexpand::full_with_context_no_errors;

/// Expands a leading tilde and environment variables in the `input` string.
pub fn expand_variables<S: AsRef<str> + ?Sized>(input: &S) -> Cow<str> {
    let get_env_var = |input: &str| { env::var(input).ok() };
    full_with_context_no_errors(input, env::home_dir, get_env_var)
}

// Note that cargo runs tests in parallel, so there is a danger of tests interfering with each
// other: for this reason, we either always set the environment variable to the same value in all
// tests, or we use discrete environment variables.
#[cfg(test)]
mod tests {
    use super::*;
    use std::env;

    const HOME_VAL: &'static str = "homes";
    const FOO_VAL: &'static str = "foos";
    const THUNK_VAL: &'static str = "thunks";

    #[test]
    fn expand_variables_expands_tilde_if_home_is_set() {
        env::set_var("HOME", HOME_VAL);
        let result = expand_variables("~/Documents");
        assert_eq!(result, format!("{}/Documents", HOME_VAL));
    }

    // Actually this doesn't work, because home_dir() will invoke getpwuid_r
    // and still find my home, on Linux at least.
    // #[test]
    // fn expand_variables_does_not_expand_tilde_if_home_is_not_set() {
    //     env::remove_var("HOME");
    //     let result = expand_variables("~/Documents");
    //     assert_eq!(result, "~/Documents");
    // }

    #[test]
    fn expand_variables_only_expands_tilde_at_the_beginning_of_the_input() {
        env::set_var("HOME", HOME_VAL);
        let result = expand_variables("~/Documents/foo/~/bar/aa~");
        assert_eq!(result, format!("{}/Documents/foo/~/bar/aa~", HOME_VAL));
    }

    #[test]
    fn expand_variables_expands_mutiple_occurrences_of_known_variables() {
        env::set_var("FOO", FOO_VAL);
        env::set_var("THUNK", THUNK_VAL);
        let result = expand_variables("/Documents/$FOO/bar/$FOO/baz/${FOO}/wipple${THUNK}");
        assert_eq!(result, format!("/Documents/{0}/bar/{0}/baz/{0}/wipple{1}", FOO_VAL, THUNK_VAL));
    }

    #[test]
    fn expand_variables_does_not_expand_variables_if_case_differs() {
        env::set_var("FOO", FOO_VAL);
        let result = expand_variables("/Documents/$Foo");
        assert_eq!(result, "/Documents/$Foo");
    }

    #[test]
    fn expand_variables_only_expands_one_level_deep() {
        env::set_var("FEZ", "$FEZ/$FEZ/$BAR");
        env::set_var("BAR", "should not be used");
        let result = expand_variables("/Documents/$FEZ");
        assert_eq!(result, "/Documents/$FEZ/$FEZ/$BAR");
    }

    #[test]
    fn expand_variables_does_not_expand_unknown_variables() {
        env::remove_var("NO_CHANCE_OF_ME_REALLY_EXISTING");
        let result = expand_variables("/Documents/$NO_CHANCE_OF_ME_REALLY_EXISTING");
        assert_eq!(result, "/Documents/$NO_CHANCE_OF_ME_REALLY_EXISTING");
    }
}<commit_msg>Utils: checkin expand_filename before deleting it.<commit_after>use std::borrow::{Cow, Borrow};
use std::env;
use std::fs::canonicalize;
use std::path::{Path, PathBuf};
use shellexpand::full_with_context_no_errors;

/// Expands a leading tilde and environment variables in the `input` string.
pub fn expand_variables<S: AsRef<str> + ?Sized>(input: &S) -> Cow<str> {
    let get_env_var = |input: &str| { env::var(input).ok() };
    full_with_context_no_errors(input, env::home_dir, get_env_var)
}

/// There is a problem: canonicalize does not do what we want. It only
/// works if the file actually exists! Look at the source code and see
/// https://blogs.msdn.microsoft.com/jeremykuhne/2016/04/21/path-normalization/
/// We could implement our own algorithm based on those guidelines, but I can't
/// be bothered right now.

/// Expands a filename by first expanding environment variables and then
/// canonicalizing it.
pub fn expand_filename<S: AsRef<Path> + ?Sized>(input: &S) -> String {
    let t1 = input.as_ref().to_string_lossy().into_owned();
    let t2 = expand_variables(&t1).into_owned();
    t2
}

pub fn expand_filename2<S: AsRef<str> + ?Sized>(input: &S) -> PathBuf {
    //let t1 = input.as_ref().to_string_lossy().into_owned();
    let t2 = expand_variables(input).into_owned();
    let t2 = canonicalize(t2);
    t2.unwrap()
}

// Note that cargo runs tests in parallel, so there is a danger of tests interfering with each
// other: for this reason, we either always set the environment variable to the same value in all
// tests, or we use discrete environment variables.
#[cfg(test)]
mod tests {
    use super::*;
    use std::env;

    const HOME_VAL: &'static str = "homes";
    const FOO_VAL: &'static str = "foos";
    const THUNK_VAL: &'static str = "thunks";

    #[test]
    fn expand_variables_expands_tilde_if_home_is_set() {
        env::set_var("HOME", HOME_VAL);
        let result = expand_variables("~/Documents");
        assert_eq!(result, format!("{}/Documents", HOME_VAL));
    }

    // Actually this doesn't work, because home_dir() will invoke getpwuid_r
    // and still find my home, on Linux at least.
    // #[test]
    // fn expand_variables_does_not_expand_tilde_if_home_is_not_set() {
    //     env::remove_var("HOME");
    //     let result = expand_variables("~/Documents");
    //     assert_eq!(result, "~/Documents");
    // }

    #[test]
    fn expand_variables_only_expands_tilde_at_the_beginning_of_the_input() {
        env::set_var("HOME", HOME_VAL);
        let result = expand_variables("~/Documents/foo/~/bar/aa~");
        assert_eq!(result, format!("{}/Documents/foo/~/bar/aa~", HOME_VAL));
    }

    #[test]
    fn expand_variables_expands_mutiple_occurrences_of_known_variables() {
        env::set_var("FOO", FOO_VAL);
        env::set_var("THUNK", THUNK_VAL);
        let result = expand_variables("/Documents/$FOO/bar/$FOO/baz/${FOO}/wipple${THUNK}");
        assert_eq!(result, format!("/Documents/{0}/bar/{0}/baz/{0}/wipple{1}", FOO_VAL, THUNK_VAL));
    }

    #[test]
    fn expand_variables_does_not_expand_variables_if_case_differs() {
        env::set_var("FOO", FOO_VAL);
        let result = expand_variables("/Documents/$Foo");
        assert_eq!(result, "/Documents/$Foo");
    }

    #[test]
    fn expand_variables_only_expands_one_level_deep() {
        env::set_var("FEZ", "$FEZ/$FEZ/$BAR");
        env::set_var("BAR", "should not be used");
        let result = expand_variables("/Documents/$FEZ");
        assert_eq!(result, "/Documents/$FEZ/$FEZ/$BAR");
    }

    #[test]
    fn expand_variables_does_not_expand_unknown_variables() {
        env::remove_var("NO_CHANCE_OF_ME_REALLY_EXISTING");
        let result = expand_variables("/Documents/$NO_CHANCE_OF_ME_REALLY_EXISTING");
        assert_eq!(result, "/Documents/$NO_CHANCE_OF_ME_REALLY_EXISTING");
    }

    #[test]
    fn expand_filename_expands_and_canonicalizes() {
        env::set_var("HOME", HOME_VAL);
        let result = expand_filename2("~/Documents/foo/..///a.txt");
        assert_eq!(result, PathBuf::from(format!("{}/Documents/a.txt", HOME_VAL)));
    }
}<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::canvas_data::{
    Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, GenericDrawTarget,
    GenericPathBuilder, Path, Pattern, StrokeOptions,
};
use crate::canvas_paint_thread::AntialiasMode;
use canvas_traits::canvas::*;
use cssparser::RGBA;
use euclid::{Rect, Size2D, Transform2D};
use std::marker::PhantomData;

pub struct RaqoteBackend;

impl Backend for RaqoteBackend {
    fn get_composition_op(&self, _opts: &DrawOptions) -> CompositionOp {
        unimplemented!()
    }

    fn need_to_draw_shadow(&self, _color: &Color) -> bool {
        unimplemented!()
    }

    fn size_from_pattern(&self, _rect: &Rect<f32>, _pattern: &Pattern) -> Option<Size2D<f32>> {
        unimplemented!()
    }

    fn set_shadow_color<'a>(&mut self, _color: RGBA, _state: &mut CanvasPaintState<'a>) {
        unimplemented!()
    }

    fn set_fill_style<'a>(
        &mut self,
        _style: FillOrStrokeStyle,
        _state: &mut CanvasPaintState<'a>,
        _drawtarget: &GenericDrawTarget,
    ) {
        unimplemented!()
    }

    fn set_stroke_style<'a>(
        &mut self,
        _style: FillOrStrokeStyle,
        _state: &mut CanvasPaintState<'a>,
        _drawtarget: &GenericDrawTarget,
    ) {
        unimplemented!()
    }

    fn set_global_composition<'a>(
        &mut self,
        _op: CompositionOrBlending,
        _state: &mut CanvasPaintState<'a>,
    ) {
        unimplemented!()
    }

    fn create_drawtarget(&self, _size: Size2D<u64>) -> Box<GenericDrawTarget> {
        unimplemented!()
    }

    fn recreate_paint_state<'a>(&self, _state: &CanvasPaintState<'a>) -> CanvasPaintState<'a> {
        CanvasPaintState::new(AntialiasMode::Default)
    }
}

impl<'a> CanvasPaintState<'a> {
    pub fn new(_antialias: AntialiasMode) -> CanvasPaintState<'a> {
        CanvasPaintState {
            draw_options: DrawOptions::Raqote(()),
            fill_style: Pattern::Raqote(()),
            stroke_style: Pattern::Raqote(()),
            stroke_opts: StrokeOptions::Raqote(PhantomData),
            transform: Transform2D::identity(),
            shadow_offset_x: 0.0,
            shadow_offset_y: 0.0,
            shadow_blur: 0.0,
            shadow_color: Color::Raqote(()),
        }
    }
}

impl Pattern {
    pub fn is_zero_size_gradient(&self) -> bool {
        match *self {
            Pattern::Raqote(()) => unimplemented!(),
        }
    }
}

impl<'a> StrokeOptions<'a> {
    pub fn set_line_width(&mut self, _val: f32) {
        unimplemented!()
    }
    pub fn set_miter_limit(&mut self, _val: f32) {
        unimplemented!()
    }
    pub fn set_line_join(&mut self, _val: LineJoinStyle) {
        unimplemented!()
    }
    pub fn set_line_cap(&mut self, _val: LineCapStyle) {
        unimplemented!()
    }
}

impl DrawOptions {
    pub fn set_alpha(&mut self, _val: f32) {
        match self {
            DrawOptions::Raqote(()) => unimplemented!(),
        }
    }
}

impl Path {
    pub fn transformed_copy_to_builder(
        &self,
        _transform: &Transform2D<f32>,
    ) -> Box<GenericPathBuilder> {
        unimplemented!()
    }

    pub fn contains_point(&self, _x: f64, _y: f64, _path_transform: &Transform2D<f32>) -> bool {
        unimplemented!()
    }

    pub fn copy_to_builder(&self) -> Box<GenericPathBuilder> {
        unimplemented!()
    }
}
<commit_msg>Most basic raqote draw target stubs.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::canvas_data::{
    Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, ExtendMode, Filter,
    GenericDrawTarget, GenericPathBuilder, GradientStop, GradientStops, Path, Pattern,
    SourceSurface, StrokeOptions, SurfaceFormat,
};
use crate::canvas_paint_thread::AntialiasMode;
use canvas_traits::canvas::*;
use cssparser::RGBA;
use euclid::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use std::marker::PhantomData;

pub struct RaqoteBackend;

impl Backend for RaqoteBackend {
    fn get_composition_op(&self, _opts: &DrawOptions) -> CompositionOp {
        unimplemented!()
    }

    fn need_to_draw_shadow(&self, _color: &Color) -> bool {
        unimplemented!()
    }

    fn size_from_pattern(&self, _rect: &Rect<f32>, _pattern: &Pattern) -> Option<Size2D<f32>> {
        unimplemented!()
    }

    fn set_shadow_color<'a>(&mut self, _color: RGBA, _state: &mut CanvasPaintState<'a>) {
        unimplemented!()
    }

    fn set_fill_style<'a>(
        &mut self,
        _style: FillOrStrokeStyle,
        _state: &mut CanvasPaintState<'a>,
        _drawtarget: &GenericDrawTarget,
    ) {
        unimplemented!()
    }

    fn set_stroke_style<'a>(
        &mut self,
        _style: FillOrStrokeStyle,
        _state: &mut CanvasPaintState<'a>,
        _drawtarget: &GenericDrawTarget,
    ) {
        unimplemented!()
    }

    fn set_global_composition<'a>(
        &mut self,
        _op: CompositionOrBlending,
        _state: &mut CanvasPaintState<'a>,
    ) {
        unimplemented!()
    }

    fn create_drawtarget(&self, size: Size2D<u64>) -> Box<GenericDrawTarget> {
        Box::new(raqote::DrawTarget::new(size.width as i32, size.height as i32))
    }

    fn recreate_paint_state<'a>(&self, _state: &CanvasPaintState<'a>) -> CanvasPaintState<'a> {
        CanvasPaintState::new(AntialiasMode::Default)
    }
}

impl<'a> CanvasPaintState<'a> {
    pub fn new(_antialias: AntialiasMode) -> CanvasPaintState<'a> {
        CanvasPaintState {
            draw_options: DrawOptions::Raqote(()),
            fill_style: Pattern::Raqote(()),
            stroke_style: Pattern::Raqote(()),
            stroke_opts: StrokeOptions::Raqote(PhantomData),
            transform: Transform2D::identity(),
            shadow_offset_x: 0.0,
            shadow_offset_y: 0.0,
            shadow_blur: 0.0,
            shadow_color: Color::Raqote(()),
        }
    }
}

impl Pattern {
    pub fn is_zero_size_gradient(&self) -> bool {
        match *self {
            Pattern::Raqote(()) => unimplemented!(),
        }
    }
}

impl<'a> StrokeOptions<'a> {
    pub fn set_line_width(&mut self, _val: f32) {
        unimplemented!()
    }
    pub fn set_miter_limit(&mut self, _val: f32) {
        unimplemented!()
    }
    pub fn set_line_join(&mut self, _val: LineJoinStyle) {
        unimplemented!()
    }
    pub fn set_line_cap(&mut self, _val: LineCapStyle) {
        unimplemented!()
    }
}

impl DrawOptions {
    pub fn set_alpha(&mut self, _val: f32) {
        match self {
            DrawOptions::Raqote(()) => unimplemented!(),
        }
    }
}

impl Path {
    pub fn transformed_copy_to_builder(
        &self,
        _transform: &Transform2D<f32>,
    ) -> Box<GenericPathBuilder> {
        unimplemented!()
    }

    pub fn contains_point(&self, _x: f64, _y: f64, _path_transform: &Transform2D<f32>) -> bool {
        unimplemented!()
    }

    pub fn copy_to_builder(&self) -> Box<GenericPathBuilder> {
        unimplemented!()
    }
}

impl GenericDrawTarget for raqote::DrawTarget {
    fn clear_rect(&self, _rect: &Rect<f32>) {
        unimplemented!()
    }

    fn copy_surface(&self, _surface: SourceSurface, _source: Rect<i32>, _destination: Point2D<i32>) {
        unimplemented!()
    }

    fn create_gradient_stops(
        &self,
        _gradient_stops: Vec<GradientStop>,
        _extend_mode: ExtendMode,
    ) -> GradientStops {
        unimplemented!()
    }

    fn create_path_builder(&self) -> Box<GenericPathBuilder> {
        unimplemented!()
    }

    fn create_similar_draw_target(
        &self,
        _size: &Size2D<i32>,
        _format: SurfaceFormat,
    ) -> Box<GenericDrawTarget> {
        unimplemented!()
    }
    fn create_source_surface_from_data(
        &self,
        _data: &[u8],
        _size: Size2D<i32>,
        _stride: i32,
    ) -> Option<SourceSurface> {
        unimplemented!()
    }
    fn draw_surface(
        &self,
        _surface: SourceSurface,
        _dest: Rect<f64>,
        _source: Rect<f64>,
        _filter: Filter,
        _draw_options: &DrawOptions,
    ) {
        unimplemented!()
    }
    fn draw_surface_with_shadow(
        &self,
        _surface: SourceSurface,
        _dest: &Point2D<f32>,
        _color: &Color,
        _offset: &Vector2D<f32>,
        _sigma: f32,
        _operator: CompositionOp,
    ) {
        unimplemented!()
    }
    fn fill(&self, _path: &Path, _pattern: Pattern, _draw_options: &DrawOptions) {
        unimplemented!()
    }
    fn fill_rect(&self, _rect: &Rect<f32>, _pattern: Pattern, _draw_options: Option<&DrawOptions>) {
        unimplemented!()
    }
    fn get_format(&self) -> SurfaceFormat {
        unimplemented!()
    }
    fn get_size(&self) -> Size2D<i32> {
        unimplemented!()
    }
    fn get_transform(&self) -> Transform2D<f32> {
        unimplemented!()
    }
    fn pop_clip(&self) {
        unimplemented!()
    }
    fn push_clip(&self, _path: &Path) {
        unimplemented!()
    }
    fn set_transform(&self, _matrix: &Transform2D<f32>) {
        unimplemented!()
    }
    fn snapshot(&self) -> SourceSurface {
        unimplemented!()
    }
    fn stroke(
        &self,
        _path: &Path,
        _pattern: Pattern,
        _stroke_options: &StrokeOptions,
        _draw_options: &DrawOptions,
    ) {
        unimplemented!()
    }
    fn stroke_line(
        &self,
        _start: Point2D<f32>,
        _end: Point2D<f32>,
        _pattern: Pattern,
        _stroke_options: &StrokeOptions,
        _draw_options: &DrawOptions,
    ) {
        unimplemented!()
    }
    fn stroke_rect(
        &self,
        _rect: &Rect<f32>,
        _pattern: Pattern,
        _stroke_options: &StrokeOptions,
        _draw_options: &DrawOptions,
    ) {
        unimplemented!()
    }

    fn snapshot_data(&self) -> Vec<u8> {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::collections::BTreeMap;
use std::sync::Arc;
#[cfg(windows)]
use std::thread;
#[cfg(windows)]
use std::time::Duration;

#[cfg(windows)]
use std::io::Read;

use base::Event;
#[cfg(windows)]
use base::Result;
use devices::serial_device::{SerialHardware, SerialParameters, SerialType};
#[cfg(windows)]
use devices::Minijail;
#[cfg(unix)]
use devices::ProxyDevice;
use devices::{Bus, Serial};
use hypervisor::ProtectionType;
#[cfg(unix)]
use minijail::Minijail;
use remain::sorted;
use sync::Mutex;
use thiserror::Error as ThisError;

use crate::DeviceRegistrationError;

/// Add the default serial parameters for serial ports that have not already been specified.
///
/// This ensures that `serial_parameters` will contain parameters for each of the four PC-style
/// serial ports (COM1-COM4).
///
/// It also sets the first `SerialHardware::Serial` to be the default console device if no other
/// serial parameters exist with console=true and the first serial device has not already been
/// configured explicitly.
pub fn set_default_serial_parameters(
    serial_parameters: &mut BTreeMap<(SerialHardware, u8), SerialParameters>,
    is_vhost_user_console_enabled: bool,
) {
    // If no console device exists and the first serial port has not been specified,
    // set the first serial port as a stdout+stdin console.
    let default_console = (SerialHardware::Serial, 1);
    if !serial_parameters.iter().any(|(_, p)| p.console) && !is_vhost_user_console_enabled {
        serial_parameters
            .entry(default_console)
            .or_insert(SerialParameters {
                type_: SerialType::Stdout,
                hardware: SerialHardware::Serial,
                path: None,
                input: None,
                num: 1,
                console: true,
                earlycon: false,
                stdin: true,
                out_timestamp: false,
                ..Default::default()
            });
    }

    // Ensure all four of the COM ports exist.
    // If one of these four SerialHardware::Serial port was not configured by the user,
    // set it up as a sink.
    for num in 1..=4 {
        let key = (SerialHardware::Serial, num);
        serial_parameters.entry(key).or_insert(SerialParameters {
            type_: SerialType::Sink,
            hardware: SerialHardware::Serial,
            path: None,
            input: None,
            num,
            console: false,
            earlycon: false,
            stdin: false,
            out_timestamp: false,
            ..Default::default()
        });
    }
}

/// Address for Serial ports in x86
pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];

/// A type for queueing input bytes to a serial device that abstracts if the device is local or part
/// of a sandboxed device process.
/// TODO(b/160806152) rizhang: Move to different file for readability.
#[cfg(windows)]
pub enum SerialInput {
    #[doc(hidden)]
    Local(Arc<Mutex<Serial>>),
}

#[cfg(windows)]
impl SerialInput {
    /// Creates a `SerialInput` that trivially forwards queued bytes to the device in the local
    /// process.
    pub fn new_local(serial: Arc<Mutex<Serial>>) -> SerialInput {
        SerialInput::Local(serial)
    }

    /// Just like `Serial::queue_input_bytes`, but abstracted over local and sandboxed serial
    /// devices.
    pub fn queue_input_bytes(&self, bytes: &[u8]) -> Result<()> {
        match self {
            SerialInput::Local(device) => device.lock().queue_input_bytes(bytes),
        }
    }
}

/// Adds serial devices to the provided bus based on the serial parameters given.
///
/// Only devices with hardware type `SerialHardware::Serial` are added by this function.
///
/// # Arguments
///
/// * `io_bus` - Bus to add the devices to
/// * `com_evt_1_3` - event for com1 and com3
/// * `com_evt_1_4` - event for com2 and com4
/// * `io_bus` - Bus to add the devices to
/// * `serial_parameters` - definitions of serial parameter configurations.
///   All four of the traditional PC-style serial ports (COM1-COM4) must be specified.
pub fn add_serial_devices(
    protected_vm: ProtectionType,
    io_bus: &Bus,
    com_evt_1_3: &Event,
    com_evt_2_4: &Event,
    serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
    serial_jail: Option<Minijail>,
) -> std::result::Result<(), DeviceRegistrationError> {
    for x in 0..=3 {
        let com_evt = match x {
            0 => &com_evt_1_3,
            1 => &com_evt_2_4,
            2 => &com_evt_1_3,
            3 => &com_evt_2_4,
            _ => &com_evt_1_3,
        };

        let param = serial_parameters
            .get(&(SerialHardware::Serial, x + 1))
            .ok_or(DeviceRegistrationError::MissingRequiredSerialDevice(x + 1))?;

        let mut preserved_descriptors = Vec::new();
        let com = param
            .create_serial_device::<Serial>(protected_vm, com_evt, &mut preserved_descriptors)
            .map_err(DeviceRegistrationError::CreateSerialDevice)?;

        match serial_jail.as_ref() {
            #[cfg(unix)]
            Some(jail) => {
                let com = Arc::new(Mutex::new(
                    ProxyDevice::new(com, jail, preserved_descriptors)
                        .map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
                ));
                io_bus
                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8)
                    .unwrap();
            }
            #[cfg(windows)]
            Some(_) => (),
            None => {
                let com = Arc::new(Mutex::new(com));
                io_bus
                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8)
                    .unwrap();

                #[cfg(windows)]
                {
                    if !param.stdin {
                        if let SerialType::SystemSerialType = param.type_ {
                            let mut in_pipe_result =
                                com.lock().in_stream.as_ref().unwrap().try_clone();
                            thread::spawn(move || {
                                let serial_input = SerialInput::new_local(com);
                                let in_pipe = in_pipe_result.as_mut().unwrap();

                                let mut buffer: [u8; 255] = [0; 255];
                                loop {
                                    let bytes = in_pipe.read(&mut buffer).unwrap_or(0);
                                    if bytes > 0 {
                                        serial_input.queue_input_bytes(&buffer[0..bytes]).unwrap();
                                    }
                                    // We can't issue blocking reads here and overlapped I/O is
                                    // incompatible with the call site where writes to this pipe are being
                                    // made, so instead we issue a small wait to prevent us from hogging
                                    // the CPU. This 20ms delay while typing doesn't seem to be noticeable.
                                    thread::sleep(Duration::from_millis(20));
                                }
                            });
                        }
                    }
                }
            }
        }
    }

    Ok(())
}

#[sorted]
#[derive(ThisError, Debug)]
pub enum GetSerialCmdlineError {
    #[error("Error appending to cmdline: {0}")]
    KernelCmdline(kernel_cmdline::Error),
    #[error("Hardware {0} not supported as earlycon")]
    UnsupportedEarlyconHardware(SerialHardware),
}

pub type GetSerialCmdlineResult<T> = std::result::Result<T, GetSerialCmdlineError>;

/// Add serial options to the provided `cmdline` based on `serial_parameters`.
/// `serial_io_type` should be "io" if the platform uses x86-style I/O ports for serial devices
/// or "mmio" if the serial ports are memory mapped.
// TODO(b/227407433): Support cases where vhost-user console is specified.
pub fn get_serial_cmdline(
    cmdline: &mut kernel_cmdline::Cmdline,
    serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
    serial_io_type: &str,
) -> GetSerialCmdlineResult<()> {
    match serial_parameters
        .iter()
        .filter(|(_, p)| p.console)
        .map(|(k, _)| k)
        .next()
    {
        Some((SerialHardware::Serial, num)) => {
            cmdline
                .insert("console", &format!("ttyS{}", num - 1))
                .map_err(GetSerialCmdlineError::KernelCmdline)?;
        }
        Some((SerialHardware::VirtioConsole, num)) => {
            cmdline
                .insert("console", &format!("hvc{}", num - 1))
                .map_err(GetSerialCmdlineError::KernelCmdline)?;
        }
        Some((SerialHardware::Debugcon, _)) => {}
        None => {}
    }

    match serial_parameters
        .iter()
        .filter(|(_, p)| p.earlycon)
        .map(|(k, _)| k)
        .next()
    {
        Some((SerialHardware::Serial, num)) => {
            if let Some(addr) = SERIAL_ADDR.get(*num as usize - 1) {
                cmdline
                    .insert(
                        "earlycon",
                        &format!("uart8250,{},0x{:x}", serial_io_type, addr),
                    )
                    .map_err(GetSerialCmdlineError::KernelCmdline)?;
            }
        }
        Some((hw, _num)) => {
            return Err(GetSerialCmdlineError::UnsupportedEarlyconHardware(*hw));
        }
        None => {}
    }

    Ok(())
}

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

    #[test]
    fn get_serial_cmdline_default() {
        let mut cmdline = Cmdline::new(4096);
        let mut serial_parameters = BTreeMap::new();

        set_default_serial_parameters(&mut serial_parameters, false);
        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
            .expect("get_serial_cmdline failed");

        let cmdline_str = cmdline.as_str();
        assert!(cmdline_str.contains("console=ttyS0"));
    }

    #[test]
    fn get_serial_cmdline_virtio_console() {
        let mut cmdline = Cmdline::new(4096);
        let mut serial_parameters = BTreeMap::new();

        // Add a virtio-console device with console=true.
        serial_parameters.insert(
            (SerialHardware::VirtioConsole, 1),
            SerialParameters {
                type_: SerialType::Stdout,
                hardware: SerialHardware::VirtioConsole,
                path: None,
                input: None,
                num: 1,
                console: true,
                earlycon: false,
                stdin: true,
                out_timestamp: false,
                debugcon_port: 0,
            },
        );

        set_default_serial_parameters(&mut serial_parameters, false);
        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
            .expect("get_serial_cmdline failed");

        let cmdline_str = cmdline.as_str();
        assert!(cmdline_str.contains("console=hvc0"));
    }

    #[test]
    fn get_serial_cmdline_virtio_console_serial_earlycon() {
        let mut cmdline = Cmdline::new(4096);
        let mut serial_parameters = BTreeMap::new();

        // Add a virtio-console device with console=true.
        serial_parameters.insert(
            (SerialHardware::VirtioConsole, 1),
            SerialParameters {
                type_: SerialType::Stdout,
                hardware: SerialHardware::VirtioConsole,
                path: None,
                input: None,
                num: 1,
                console: true,
                earlycon: false,
                stdin: true,
                out_timestamp: false,
                debugcon_port: 0,
            },
        );

        // Override the default COM1 with an earlycon device.
        serial_parameters.insert(
            (SerialHardware::Serial, 1),
            SerialParameters {
                type_: SerialType::Stdout,
                hardware: SerialHardware::Serial,
                path: None,
                input: None,
                num: 1,
                console: false,
                earlycon: true,
                stdin: false,
                out_timestamp: false,
                debugcon_port: 0,
            },
        );

        set_default_serial_parameters(&mut serial_parameters, false);
        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
            .expect("get_serial_cmdline failed");

        let cmdline_str = cmdline.as_str();
        assert!(cmdline_str.contains("console=hvc0"));
        assert!(cmdline_str.contains("earlycon=uart8250,io,0x3f8"));
    }

    #[test]
    fn get_serial_cmdline_virtio_console_invalid_earlycon() {
        let mut cmdline = Cmdline::new(4096);
        let mut serial_parameters = BTreeMap::new();

        // Try to add a virtio-console device with earlycon=true (unsupported).
        serial_parameters.insert(
            (SerialHardware::VirtioConsole, 1),
            SerialParameters {
                type_: SerialType::Stdout,
                hardware: SerialHardware::VirtioConsole,
                path: None,
                input: None,
                num: 1,
                console: false,
                earlycon: true,
                stdin: true,
                out_timestamp: false,
                debugcon_port: 0,
            },
        );

        set_default_serial_parameters(&mut serial_parameters, false);
        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
            .expect_err("get_serial_cmdline succeeded");
    }
}
<commit_msg>arch: Clone jail object for serial.<commit_after>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::collections::BTreeMap;
use std::sync::Arc;
#[cfg(windows)]
use std::thread;
#[cfg(windows)]
use std::time::Duration;

#[cfg(windows)]
use std::io::Read;

use base::Event;
#[cfg(windows)]
use base::Result;
use devices::serial_device::{SerialHardware, SerialParameters, SerialType};
#[cfg(windows)]
use devices::Minijail;
#[cfg(unix)]
use devices::ProxyDevice;
use devices::{Bus, Serial};
use hypervisor::ProtectionType;
#[cfg(unix)]
use minijail::Minijail;
use remain::sorted;
use sync::Mutex;
use thiserror::Error as ThisError;

use crate::DeviceRegistrationError;

/// Add the default serial parameters for serial ports that have not already been specified.
///
/// This ensures that `serial_parameters` will contain parameters for each of the four PC-style
/// serial ports (COM1-COM4).
///
/// It also sets the first `SerialHardware::Serial` to be the default console device if no other
/// serial parameters exist with console=true and the first serial device has not already been
/// configured explicitly.
pub fn set_default_serial_parameters(
    serial_parameters: &mut BTreeMap<(SerialHardware, u8), SerialParameters>,
    is_vhost_user_console_enabled: bool,
) {
    // If no console device exists and the first serial port has not been specified,
    // set the first serial port as a stdout+stdin console.
    let default_console = (SerialHardware::Serial, 1);
    if !serial_parameters.iter().any(|(_, p)| p.console) && !is_vhost_user_console_enabled {
        serial_parameters
            .entry(default_console)
            .or_insert(SerialParameters {
                type_: SerialType::Stdout,
                hardware: SerialHardware::Serial,
                path: None,
                input: None,
                num: 1,
                console: true,
                earlycon: false,
                stdin: true,
                out_timestamp: false,
                ..Default::default()
            });
    }

    // Ensure all four of the COM ports exist.
    // If one of these four SerialHardware::Serial port was not configured by the user,
    // set it up as a sink.
    for num in 1..=4 {
        let key = (SerialHardware::Serial, num);
        serial_parameters.entry(key).or_insert(SerialParameters {
            type_: SerialType::Sink,
            hardware: SerialHardware::Serial,
            path: None,
            input: None,
            num,
            console: false,
            earlycon: false,
            stdin: false,
            out_timestamp: false,
            ..Default::default()
        });
    }
}

/// Address for Serial ports in x86
pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];

/// A type for queueing input bytes to a serial device that abstracts if the device is local or part
/// of a sandboxed device process.
/// TODO(b/160806152) rizhang: Move to different file for readability.
#[cfg(windows)]
pub enum SerialInput {
    #[doc(hidden)]
    Local(Arc<Mutex<Serial>>),
}

#[cfg(windows)]
impl SerialInput {
    /// Creates a `SerialInput` that trivially forwards queued bytes to the device in the local
    /// process.
    pub fn new_local(serial: Arc<Mutex<Serial>>) -> SerialInput {
        SerialInput::Local(serial)
    }

    /// Just like `Serial::queue_input_bytes`, but abstracted over local and sandboxed serial
    /// devices.
    pub fn queue_input_bytes(&self, bytes: &[u8]) -> Result<()> {
        match self {
            SerialInput::Local(device) => device.lock().queue_input_bytes(bytes),
        }
    }
}

/// Adds serial devices to the provided bus based on the serial parameters given.
///
/// Only devices with hardware type `SerialHardware::Serial` are added by this function.
///
/// # Arguments
///
/// * `io_bus` - Bus to add the devices to
/// * `com_evt_1_3` - event for com1 and com3
/// * `com_evt_1_4` - event for com2 and com4
/// * `io_bus` - Bus to add the devices to
/// * `serial_parameters` - definitions of serial parameter configurations.
///   All four of the traditional PC-style serial ports (COM1-COM4) must be specified.
pub fn add_serial_devices(
    protected_vm: ProtectionType,
    io_bus: &Bus,
    com_evt_1_3: &Event,
    com_evt_2_4: &Event,
    serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
    serial_jail: Option<Minijail>,
) -> std::result::Result<(), DeviceRegistrationError> {
    for x in 0..=3 {
        let com_evt = match x {
            0 => &com_evt_1_3,
            1 => &com_evt_2_4,
            2 => &com_evt_1_3,
            3 => &com_evt_2_4,
            _ => &com_evt_1_3,
        };

        let param = serial_parameters
            .get(&(SerialHardware::Serial, x + 1))
            .ok_or(DeviceRegistrationError::MissingRequiredSerialDevice(x + 1))?;

        let mut preserved_descriptors = Vec::new();
        let com = param
            .create_serial_device::<Serial>(protected_vm, com_evt, &mut preserved_descriptors)
            .map_err(DeviceRegistrationError::CreateSerialDevice)?;

        match serial_jail.as_ref() {
            #[cfg(unix)]
            Some(jail) => {
                let com = Arc::new(Mutex::new(
                    ProxyDevice::new(
                        com,
                        &jail
                            .try_clone()
                            .map_err(DeviceRegistrationError::CloneJail)?,
                        preserved_descriptors,
                    )
                    .map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
                ));
                io_bus
                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8)
                    .unwrap();
            }
            #[cfg(windows)]
            Some(_) => (),
            None => {
                let com = Arc::new(Mutex::new(com));
                io_bus
                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8)
                    .unwrap();

                #[cfg(windows)]
                {
                    if !param.stdin {
                        if let SerialType::SystemSerialType = param.type_ {
                            let mut in_pipe_result =
                                com.lock().in_stream.as_ref().unwrap().try_clone();
                            thread::spawn(move || {
                                let serial_input = SerialInput::new_local(com);
                                let in_pipe = in_pipe_result.as_mut().unwrap();

                                let mut buffer: [u8; 255] = [0; 255];
                                loop {
                                    let bytes = in_pipe.read(&mut buffer).unwrap_or(0);
                                    if bytes > 0 {
                                        serial_input.queue_input_bytes(&buffer[0..bytes]).unwrap();
                                    }
                                    // We can't issue blocking reads here and overlapped I/O is
                                    // incompatible with the call site where writes to this pipe are being
                                    // made, so instead we issue a small wait to prevent us from hogging
                                    // the CPU. This 20ms delay while typing doesn't seem to be noticeable.
                                    thread::sleep(Duration::from_millis(20));
                                }
                            });
                        }
                    }
                }
            }
        }
    }

    Ok(())
}

#[sorted]
#[derive(ThisError, Debug)]
pub enum GetSerialCmdlineError {
    #[error("Error appending to cmdline: {0}")]
    KernelCmdline(kernel_cmdline::Error),
    #[error("Hardware {0} not supported as earlycon")]
    UnsupportedEarlyconHardware(SerialHardware),
}

pub type GetSerialCmdlineResult<T> = std::result::Result<T, GetSerialCmdlineError>;

/// Add serial options to the provided `cmdline` based on `serial_parameters`.
/// `serial_io_type` should be "io" if the platform uses x86-style I/O ports for serial devices
/// or "mmio" if the serial ports are memory mapped.
// TODO(b/227407433): Support cases where vhost-user console is specified.
pub fn get_serial_cmdline(
    cmdline: &mut kernel_cmdline::Cmdline,
    serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
    serial_io_type: &str,
) -> GetSerialCmdlineResult<()> {
    match serial_parameters
        .iter()
        .filter(|(_, p)| p.console)
        .map(|(k, _)| k)
        .next()
    {
        Some((SerialHardware::Serial, num)) => {
            cmdline
                .insert("console", &format!("ttyS{}", num - 1))
                .map_err(GetSerialCmdlineError::KernelCmdline)?;
        }
        Some((SerialHardware::VirtioConsole, num)) => {
            cmdline
                .insert("console", &format!("hvc{}", num - 1))
                .map_err(GetSerialCmdlineError::KernelCmdline)?;
        }
        Some((SerialHardware::Debugcon, _)) => {}
        None => {}
    }

    match serial_parameters
        .iter()
        .filter(|(_, p)| p.earlycon)
        .map(|(k, _)| k)
        .next()
    {
        Some((SerialHardware::Serial, num)) => {
            if let Some(addr) = SERIAL_ADDR.get(*num as usize - 1) {
                cmdline
                    .insert(
                        "earlycon",
                        &format!("uart8250,{},0x{:x}", serial_io_type, addr),
                    )
                    .map_err(GetSerialCmdlineError::KernelCmdline)?;
            }
        }
        Some((hw, _num)) => {
            return Err(GetSerialCmdlineError::UnsupportedEarlyconHardware(*hw));
        }
        None => {}
    }

    Ok(())
}

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

    #[test]
    fn get_serial_cmdline_default() {
        let mut cmdline = Cmdline::new(4096);
        let mut serial_parameters = BTreeMap::new();

        set_default_serial_parameters(&mut serial_parameters, false);
        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
            .expect("get_serial_cmdline failed");

        let cmdline_str = cmdline.as_str();
        assert!(cmdline_str.contains("console=ttyS0"));
    }

    #[test]
    fn get_serial_cmdline_virtio_console() {
        let mut cmdline = Cmdline::new(4096);
        let mut serial_parameters = BTreeMap::new();

        // Add a virtio-console device with console=true.
        serial_parameters.insert(
            (SerialHardware::VirtioConsole, 1),
            SerialParameters {
                type_: SerialType::Stdout,
                hardware: SerialHardware::VirtioConsole,
                path: None,
                input: None,
                num: 1,
                console: true,
                earlycon: false,
                stdin: true,
                out_timestamp: false,
                debugcon_port: 0,
            },
        );

        set_default_serial_parameters(&mut serial_parameters, false);
        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
            .expect("get_serial_cmdline failed");

        let cmdline_str = cmdline.as_str();
        assert!(cmdline_str.contains("console=hvc0"));
    }

    #[test]
    fn get_serial_cmdline_virtio_console_serial_earlycon() {
        let mut cmdline = Cmdline::new(4096);
        let mut serial_parameters = BTreeMap::new();

        // Add a virtio-console device with console=true.
        serial_parameters.insert(
            (SerialHardware::VirtioConsole, 1),
            SerialParameters {
                type_: SerialType::Stdout,
                hardware: SerialHardware::VirtioConsole,
                path: None,
                input: None,
                num: 1,
                console: true,
                earlycon: false,
                stdin: true,
                out_timestamp: false,
                debugcon_port: 0,
            },
        );

        // Override the default COM1 with an earlycon device.
        serial_parameters.insert(
            (SerialHardware::Serial, 1),
            SerialParameters {
                type_: SerialType::Stdout,
                hardware: SerialHardware::Serial,
                path: None,
                input: None,
                num: 1,
                console: false,
                earlycon: true,
                stdin: false,
                out_timestamp: false,
                debugcon_port: 0,
            },
        );

        set_default_serial_parameters(&mut serial_parameters, false);
        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
            .expect("get_serial_cmdline failed");

        let cmdline_str = cmdline.as_str();
        assert!(cmdline_str.contains("console=hvc0"));
        assert!(cmdline_str.contains("earlycon=uart8250,io,0x3f8"));
    }

    #[test]
    fn get_serial_cmdline_virtio_console_invalid_earlycon() {
        let mut cmdline = Cmdline::new(4096);
        let mut serial_parameters = BTreeMap::new();

        // Try to add a virtio-console device with earlycon=true (unsupported).
        serial_parameters.insert(
            (SerialHardware::VirtioConsole, 1),
            SerialParameters {
                type_: SerialType::Stdout,
                hardware: SerialHardware::VirtioConsole,
                path: None,
                input: None,
                num: 1,
                console: false,
                earlycon: true,
                stdin: true,
                out_timestamp: false,
                debugcon_port: 0,
            },
        );

        set_default_serial_parameters(&mut serial_parameters, false);
        get_serial_cmdline(&mut cmdline, &serial_parameters, "io")
            .expect_err("get_serial_cmdline succeeded");
    }
}
<|endoftext|>


<commit_before>use std::hash::Hash;
use std::collections::{BTreeMap, HashSet};
use std::cmp::Ordering;
use parking_lot::RwLock;
use bifrost_hasher::hash_str;

#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub enum Relation {
    Equal,
    Before,
    After,
    Concurrent
}

#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq)]
pub struct VectorClock<S: Ord + Eq + Copy> {
    map: BTreeMap<S, u64>
}

impl <S: Eq + Copy + Ord> PartialOrd for VectorClock<S> {
    fn partial_cmp(&self, other: &VectorClock<S>) -> Option<Ordering> {
        let rel = self.relation(other);
        match rel {
            Relation::Before => Some(Ordering::Less),
            Relation::After => Some(Ordering::Greater),
            Relation::Equal => Some(Ordering::Equal),
            Relation::Concurrent => None
        }
    }

    fn lt(&self, other: &VectorClock<S>) -> bool {
        self.relation(other) == Relation::Before
    }

    fn le(&self, other: &VectorClock<S>) -> bool {
        let rel = self.relation(other);
        rel == Relation::Before || rel == Relation::Equal
    }

    fn gt(&self, other: &VectorClock<S>) -> bool {
        self.relation(other) == Relation::After
    }

    fn ge(&self, other: &VectorClock<S>) -> bool {
        let rel = self.relation(other);
        rel == Relation::After || rel == Relation::Equal
    }
}

impl <S: Eq + Copy + Ord> Ord for VectorClock<S> {
    fn cmp(&self, other: &Self) -> Ordering {
        let rel = self.relation(other);
        match rel {
            Relation::Before => Ordering::Less,
            Relation::After => Ordering::Greater,
            _ => Ordering::Equal // not justified, but sufficient for BTreeSet data structure
        }
    }
}

impl <S: Eq + Copy + Ord> PartialEq for VectorClock<S> {
    fn eq(&self, other: &VectorClock<S>) -> bool {
        let rel = self.relation(other);
        rel == Relation::Equal
    }

    fn ne(&self, other: &VectorClock<S>) -> bool {
        let rel = self.relation(other);
        !(rel == Relation::After || rel == Relation::Before)
    }
}

impl <S: Ord + Eq + Copy>VectorClock<S> {
    pub fn new() -> VectorClock<S> {
        VectorClock {
            map: BTreeMap::new()
        }
    }

    pub fn inc(&mut self, server: S) -> VectorClock<S> {
        *self.map.entry(server).or_insert(0) += 1;
        self.clone()
    }

    pub fn happened_before(&self, clock_b: &VectorClock<S>) -> bool {
        let mut a_lt_b = false;
        for (server, ai) in self.map.iter() {
            let bi = *clock_b.map.get(server).unwrap_or(&0);
            if *ai > bi {return false;}
            a_lt_b = a_lt_b || *ai < bi;
        }
        for (server, bi) in clock_b.map.iter() {
            let ai = *self.map.get(server).unwrap_or(&0);
            if ai > *bi {return false;}
            a_lt_b = a_lt_b || ai < *bi;
        }
        return a_lt_b;
    }
    pub fn equals(&self, clock_b: &VectorClock<S>) -> bool {
        for (server, ac) in self.map.iter() {
            if let Some(bc) = clock_b.map.get(server) {
                if ac != bc {return false;}
            }
        }
        return true;
    }
    pub fn relation(&self, clock_b: &VectorClock<S>) -> Relation {
        if self.equals(clock_b) {
            return Relation::Equal;
        }
        if self.happened_before(clock_b) {
            return Relation::Before;
        }
        if clock_b.happened_before(self) {
            return Relation::After;
        }
        return Relation::Concurrent;
    }
    pub fn merge_with(&mut self, clock_b: &VectorClock<S>) {
        // merge_with is used to update counter for other servers (also learn from it)
        for (server, bc) in clock_b.map.iter() {
            let mut ba = self.map.entry(*server).or_insert(0);
            if *ba < *bc {*ba = *bc}
        }
    }
    pub fn learn_from(&mut self, clock_b: &VectorClock<S>) {
        // learn_from only insert missing servers into the clock
        for (server, bc) in clock_b.map.iter() {
            self.map.entry(*server).or_insert(*bc);
        }
    }
}

pub struct ServerVectorClock {
    server: u64,
    clock: RwLock<VectorClock<u64>>
}

impl ServerVectorClock {
    pub fn new(server_address: &String) -> ServerVectorClock {
        ServerVectorClock {
            server: hash_str(server_address),
            clock: RwLock::new(VectorClock::new())
        }
    }
    pub fn inc(&self) -> StandardVectorClock {
        let mut clock = self.clock.write();
        clock.inc(self.server)
    }

    pub fn happened_before(&self, clock_b: &StandardVectorClock) -> bool {
        let clock = self.clock.read();
        clock.happened_before(clock_b)
    }
    pub fn equals(&self, clock_b: &StandardVectorClock) -> bool {
        let clock = self.clock.read();
        clock.equals(clock_b)
    }
    pub fn relation(&self, clock_b: &StandardVectorClock) -> Relation {
        let clock = self.clock.read();
        clock.relation(clock_b)
    }
    pub fn merge_with(&self, clock_b: &StandardVectorClock) {
        let mut clock = self.clock.write();
        clock.merge_with(clock_b)
    }
    pub fn learn_from(&self, clock_b: &StandardVectorClock) {
        let mut clock = self.clock.write();
        clock.learn_from(clock_b)
    }
    pub fn to_clock(&self) -> StandardVectorClock {
        let clock = self.clock.read();
        clock.clone()
    }
}

pub type StandardVectorClock = VectorClock<u64>;<commit_msg>remove redudant override<commit_after>use std::hash::Hash;
use std::collections::{BTreeMap, HashSet};
use std::cmp::Ordering;
use parking_lot::RwLock;
use bifrost_hasher::hash_str;

#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub enum Relation {
    Equal,
    Before,
    After,
    Concurrent
}

#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq)]
pub struct VectorClock<S: Ord + Eq + Copy> {
    map: BTreeMap<S, u64>
}

impl <S: Eq + Copy + Ord> PartialOrd for VectorClock<S> {
    fn partial_cmp(&self, other: &VectorClock<S>) -> Option<Ordering> {
        let rel = self.relation(other);
        match rel {
            Relation::Before => Some(Ordering::Less),
            Relation::After => Some(Ordering::Greater),
            Relation::Equal => Some(Ordering::Equal),
            Relation::Concurrent => None
        }
    }
}

impl <S: Eq + Copy + Ord> Ord for VectorClock<S> {
    fn cmp(&self, other: &Self) -> Ordering {
        let rel = self.relation(other);
        match rel {
            Relation::Before => Ordering::Less,
            Relation::After => Ordering::Greater,
            _ => Ordering::Equal // not justified, but sufficient for BTreeSet data structure
        }
    }
}

impl <S: Eq + Copy + Ord> PartialEq for VectorClock<S> {
    fn eq(&self, other: &VectorClock<S>) -> bool {
        let rel = self.relation(other);
        rel == Relation::Equal
    }
}

impl <S: Ord + Eq + Copy>VectorClock<S> {
    pub fn new() -> VectorClock<S> {
        VectorClock {
            map: BTreeMap::new()
        }
    }

    pub fn inc(&mut self, server: S) -> VectorClock<S> {
        *self.map.entry(server).or_insert(0) += 1;
        self.clone()
    }

    pub fn happened_before(&self, clock_b: &VectorClock<S>) -> bool {
        let mut a_lt_b = false;
        for (server, ai) in self.map.iter() {
            let bi = *clock_b.map.get(server).unwrap_or(&0);
            if *ai > bi {return false;}
            a_lt_b = a_lt_b || *ai < bi;
        }
        for (server, bi) in clock_b.map.iter() {
            let ai = *self.map.get(server).unwrap_or(&0);
            if ai > *bi {return false;}
            a_lt_b = a_lt_b || ai < *bi;
        }
        return a_lt_b;
    }
    pub fn equals(&self, clock_b: &VectorClock<S>) -> bool {
        for (server, ac) in self.map.iter() {
            if let Some(bc) = clock_b.map.get(server) {
                if ac != bc {return false;}
            }
        }
        return true;
    }
    pub fn relation(&self, clock_b: &VectorClock<S>) -> Relation {
        if self.equals(clock_b) {
            return Relation::Equal;
        }
        if self.happened_before(clock_b) {
            return Relation::Before;
        }
        if clock_b.happened_before(self) {
            return Relation::After;
        }
        return Relation::Concurrent;
    }
    pub fn merge_with(&mut self, clock_b: &VectorClock<S>) {
        // merge_with is used to update counter for other servers (also learn from it)
        for (server, bc) in clock_b.map.iter() {
            let mut ba = self.map.entry(*server).or_insert(0);
            if *ba < *bc {*ba = *bc}
        }
    }
    pub fn learn_from(&mut self, clock_b: &VectorClock<S>) {
        // learn_from only insert missing servers into the clock
        for (server, bc) in clock_b.map.iter() {
            self.map.entry(*server).or_insert(*bc);
        }
    }
}

pub struct ServerVectorClock {
    server: u64,
    clock: RwLock<VectorClock<u64>>
}

impl ServerVectorClock {
    pub fn new(server_address: &String) -> ServerVectorClock {
        ServerVectorClock {
            server: hash_str(server_address),
            clock: RwLock::new(VectorClock::new())
        }
    }
    pub fn inc(&self) -> StandardVectorClock {
        let mut clock = self.clock.write();
        clock.inc(self.server)
    }

    pub fn happened_before(&self, clock_b: &StandardVectorClock) -> bool {
        let clock = self.clock.read();
        clock.happened_before(clock_b)
    }
    pub fn equals(&self, clock_b: &StandardVectorClock) -> bool {
        let clock = self.clock.read();
        clock.equals(clock_b)
    }
    pub fn relation(&self, clock_b: &StandardVectorClock) -> Relation {
        let clock = self.clock.read();
        clock.relation(clock_b)
    }
    pub fn merge_with(&self, clock_b: &StandardVectorClock) {
        let mut clock = self.clock.write();
        clock.merge_with(clock_b)
    }
    pub fn learn_from(&self, clock_b: &StandardVectorClock) {
        let mut clock = self.clock.write();
        clock.learn_from(clock_b)
    }
    pub fn to_clock(&self) -> StandardVectorClock {
        let clock = self.clock.read();
        clock.clone()
    }
}

pub type StandardVectorClock = VectorClock<u64>;<|endoftext|>


<commit_before>use docopt_args::DocoptArgs;
use ProtonConfig;
use util;

/// Handles all "proton vixen ..." commands
pub fn handle_vixen(args: DocoptArgs, config: ProtonConfig) {
    if args.cmd_import_layout.unwrap_or(false) {
        vixen_import_layout(args, config)
    } else if args.cmd_import_sequence.unwrap_or(false) {
        vixen_import_sequence(args, config)
    } else {
        panic!("Docopt parsing failed!")
    }
}

// proton vixen import-layout <layout-file>
pub fn vixen_import_layout(args: DocoptArgs, config: ProtonConfig) {
    let command = "proton-vixen-converter.py import-layout <layout-file>";
    let args = [
        "import-layout".to_string(),
        args.arg_layout_file.unwrap().to_string()];

    let _ = util::run_vixen_converter(command, &args);
}

// proton vixen import-sequence --seq=<seq-file> --audio=<audio-file> --layout-name=<layout-name>
pub fn vixen_import_sequence(args: DocoptArgs, config: ProtonConfig) {
    let command = "proton-vixen-converter.py import-sequence <admin-key> <seq-file> <audio-file> <layout-name>";

    let sequence_path = config.vixen_folder.clone() + "/Sequences/" + &args.flag_seq.unwrap();
    let audio_path = config.vixen_folder + "/Audio/" + &args.flag_audio.unwrap();

    let args = [
        "import-sequence".to_string(),
        config.key,
        sequence_path,
        audio_path,
        args.flag_layout_name.unwrap().to_string()];

    let _ = util::run_vixen_converter(command, &args);
}

/*

converter.py import-sequence <admin-key> <seq-file> <audio-file> <layout-id>
converter.py import-layout <pro-file>
*/
<commit_msg>vixen import-layout now uses vixen_folder to find .pro file<commit_after>use docopt_args::DocoptArgs;
use ProtonConfig;
use util;

/// Handles all "proton vixen ..." commands
pub fn handle_vixen(args: DocoptArgs, config: ProtonConfig) {
    if args.cmd_import_layout.unwrap_or(false) {
        vixen_import_layout(args, config)
    } else if args.cmd_import_sequence.unwrap_or(false) {
        vixen_import_sequence(args, config)
    } else {
        panic!("Docopt parsing failed!")
    }
}

// proton vixen import-layout <layout-file>
pub fn vixen_import_layout(args: DocoptArgs, config: ProtonConfig) {
    let command = "proton-vixen-converter.py import-layout <layout-file>";

    let layout_path = format!("{}/Profiles/{}", config.vixen_folder.clone(), args.arg_layout_file.unwrap());

    let args = [
        "import-layout".to_string(),
        layout_path];

    let _ = util::run_vixen_converter(command, &args);
}

// proton vixen import-sequence --seq=<seq-file> --audio=<audio-file> --layout-name=<layout-name>
pub fn vixen_import_sequence(args: DocoptArgs, config: ProtonConfig) {
    let command = "proton-vixen-converter.py import-sequence <admin-key> <seq-file> <audio-file> <layout-name>";

    let sequence_path = config.vixen_folder.clone() + "/Sequences/" + &args.flag_seq.unwrap();
    let audio_path = config.vixen_folder + "/Audio/" + &args.flag_audio.unwrap();

    let args = [
        "import-sequence".to_string(),
        config.key,
        sequence_path,
        audio_path,
        args.flag_layout_name.unwrap().to_string()];

    let _ = util::run_vixen_converter(command, &args);
}

/*

converter.py import-sequence <admin-key> <seq-file> <audio-file> <layout-id>
converter.py import-layout <pro-file>
*/
<|endoftext|>