<commit_before>use std::from_str::FromStr;

use super::{ScanCursor, ScanError};

#[macro_export]
macro_rules! from_str_slice_scanner {
	($scan_fn:path -> $T:ty as $name:expr) => {
		impl<'a> Scanner<'a> for $T {
			fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<($T, Cur), ScanError> {
				let err = || Err(cursor.expected($name));

				let end = match $scan_fn(cursor.tail_str()) {
					Some(i) => i,
					None => return err()
				};

				let s = cursor.str_slice_to(end);
				let cursor = cursor.slice_from(end);

				FromStr::from_str(s)
					.map(|i| Ok((i, cursor.clone())))
					.unwrap_or_else(err)
			}
		}
	};
}

pub trait Scanner<'a> {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(Self, Cur), ScanError>;
}

impl<'a> Scanner<'a> for bool {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(bool, Cur), ScanError> {
		cursor.expect_tok("true").map(|c| (true, c))
			.or_else(|_| cursor.expect_tok("false").map(|c| (false, c)))
			.or_else(|_| Err(cursor.expected("`true` or `false`")))
	}
}

impl<'a> Scanner<'a> for char {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(char, Cur), ScanError> {
		let s = cursor.tail_str();
		if s.len() == 0 {
			Err(cursor.expected("character"))
		} else {
			let ::std::str::CharRange { ch, next } = s.char_range_at(0);
			Ok((ch, cursor.slice_from(next)))
		}
	}
}

impl<'a> Scanner<'a> for &'a str {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(&'a str, Cur), ScanError> {
		cursor.pop_token().map(|sc| Ok(sc))
			.unwrap_or_else(|| Err(cursor.expected("any token")))
	}
}

impl<'a> Scanner<'a> for () {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<((), Cur), ScanError> {
		Ok(((), cursor.clone()))
	}
}

from_str_slice_scanner! { scan_float -> f32 as "real number" }
from_str_slice_scanner! { scan_float -> f64 as "real number" }
from_str_slice_scanner! { scan_int -> i8 as "8-bit integer" }
from_str_slice_scanner! { scan_int -> i16 as "16-bit integer" }
from_str_slice_scanner! { scan_int -> i32 as "32-bit integer" }
from_str_slice_scanner! { scan_int -> i64 as "64-bit integer" }
from_str_slice_scanner! { scan_int -> int as "integer" }
from_str_slice_scanner! { scan_uint -> u8 as "8-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u16 as "16-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u32 as "32-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u64 as "64-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> uint as "unsigned integer" }

pub fn next_char_at(s: &str, at: uint) -> uint {
	let ::std::str::CharRange { ch: _, next } = s.char_range_at(at);
	next
}

pub fn scan_float(s: &str) -> Option<uint> {
	enum State {
		Start,
		Whole,
		Suffix,
		ExponentStart,
		Exponent,
	}

	let mut state = Start;

	for (i,c) in s.char_indices() {
		match state {
			Start => {
				assert!(i == 0);
				match c {
					'0'...'9' | '-' => state = Whole,
					_ => return None
				}
			},
			Whole => match c {
				'0'...'9' => (),
				'.' => state = Suffix,
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i)
			},
			Suffix => match c {
				'0'...'9' => (),
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i),
			},
			ExponentStart => match c {
				'+' | '-' | '0'...'9' => state = Exponent,
				_ => return Some(i)
			},
			Exponent => match c {
				'0'...'9' => (),
				_ => return Some(i)
			}
		}
	}

	return Some(s.len())
}

pub fn scan_uint<'a>(s: &'a str) -> Option<uint> {
	s.char_indices()
		.take_while(|&(_,c)| '0' <= c && c <= '9')
		.map(|(i,_)| Some(next_char_at(s, i)))
		.last().unwrap_or(None)
}

pub fn scan_int<'a>(s: &'a str) -> Option<uint> {
	if s.len() == 0 { return None }

	let (s, off) = if s.char_at(0) == '-' {
		(s.slice_from(1), 1)
	} else {
		(s, 0)
	};

	scan_uint(s).map(|end| end+off)
}

#[cfg(test)]
mod test {
	use Cursor;
	use ScanError;
	use super::Scanner;
	use tokenizer::WordsAndInts;
	use whitespace::Ignore;

	fn cur<'a>(s: &'a str) -> Cursor<'a, WordsAndInts, Ignore> {
		Cursor::new(s, WordsAndInts, Ignore)
	}

	fn scan_a<'a, T: Scanner<'a>>(s: &'a str) -> Result<(T, Cursor<'a, WordsAndInts, Ignore>), ScanError> {
		Scanner::scan(&cur(s))
	}

	#[test]
	fn test_bool() {
		assert!(scan_a::<bool>("").err().is_some());
		assert!(scan_a::<bool>("true").unwrap().0 == true);
		assert!(scan_a::<bool>("false").unwrap().0 == false);
		assert!(scan_a::<bool>("yes").err().is_some());
		assert!(scan_a::<bool>("no").err().is_some());
		assert!(scan_a::<bool>("on").err().is_some());
		assert!(scan_a::<bool>("off").err().is_some());
		assert!(scan_a::<bool>("1").err().is_some());
		assert!(scan_a::<bool>("0").err().is_some());
	}

	#[test]
	fn test_char() {
		assert!(scan_a::<char>("").err().is_some());
		assert!(scan_a::<char>("x").unwrap().0 == 'x');
		assert!(scan_a::<char>("xy").unwrap().0 == 'x');
		assert!(scan_a::<char>("日本語").unwrap().0 == '日');
	}

	#[test]
	fn test_floats() {
		use std::from_str::FromStr;
		use std::num::{Float, Zero};

		fn test<'a, F: Float + Scanner<'a> + FromStr>() {
			let f = |v:F| v;
			let fs = |s:&str| -> F FromStr::from_str(s).unwrap();

			assert!(scan_a::<F>("").err().is_some());
			assert!(scan_a::<F>("0").ok().unwrap().0 == f(Zero::zero()));
			assert!(scan_a::<F>("0.0").ok().unwrap().0 == f(Zero::zero()));
			assert!(scan_a::<F>("-0").ok().unwrap().0 == -f(Zero::zero()));
			assert!(scan_a::<F>("1.0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.00").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e1").ok().unwrap().0 == fs("10.0"));
		}

		test::<f32>();
		test::<f64>();
	}

	#[test]
	fn test_sized_ints() {
		use std::fmt::Show;
		use std::num::{Bounded, Int, Zero};

		fn test<'a, I: Int + Scanner<'a> + Show>(check_past: bool) {
			let zero: I = Zero::zero();
			let min: I = Bounded::min_value();
			let max: I = Bounded::max_value();

			assert!(scan_a::<I>("").err().is_some());
			assert!(scan_a::<I>("0").ok().unwrap().0 == zero);
			assert!(scan_a::<I>(format!("{}", min).as_slice()).ok().unwrap().0 == min);
			assert!(scan_a::<I>(format!("{}", max).as_slice()).ok().unwrap().0 == max);

			if check_past {
				let past_min: i64 = (min.to_i64()).unwrap() - 1;
				let past_max: u64 = (max.to_u64()).unwrap() + 1;
				assert!(scan_a::<I>(format!("{}", past_min).as_slice()).err().is_some());
				assert!(scan_a::<I>(format!("{}", past_max).as_slice()).err().is_some());
			}
		}

		test::<i8>(true);
		test::<i16>(true);
		test::<i32>(true);
		test::<i64>(false);
		test::<u8>(true);
		test::<u16>(true);
		test::<u32>(true);
		test::<u64>(false);
	}

	#[test]
	fn test_int() {
		assert!(scan_a::<int>("").err().is_some());
		assert!(scan_a::<int>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<int>("x").err().is_some());
		assert!(scan_a::<int>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("-").err().is_some());
		assert!(scan_a::<int>("-0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("-42").ok().unwrap().0 == -42);
		assert!(scan_a::<int>("-1_234").ok().unwrap().0 == -1);
	}

	#[test]
	fn test_uint() {
		assert!(scan_a::<uint>("").err().is_some());
		assert!(scan_a::<uint>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<uint>("x").err().is_some());
		assert!(scan_a::<uint>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("-").err().is_some());
		assert!(scan_a::<uint>("-0").err().is_some());
		assert!(scan_a::<uint>("-42").err().is_some());
		assert!(scan_a::<uint>("-1_234").err().is_some());
	}

	#[test]
	fn test_str() {
		assert!(scan_a::<&str>("").err().is_some());
		assert!(scan_a::<&str>("a").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("a b").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("abc").ok().unwrap().0 == "abc");
		assert!(scan_a::<&str>("ab-c").ok().unwrap().0 == "ab");
	}
}
<commit_msg>Added scanner for String.<commit_after>use std::from_str::FromStr;

use super::{ScanCursor, ScanError};

#[macro_export]
macro_rules! from_str_slice_scanner {
	($scan_fn:path -> $T:ty as $name:expr) => {
		impl<'a> Scanner<'a> for $T {
			fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<($T, Cur), ScanError> {
				let err = || Err(cursor.expected($name));

				let end = match $scan_fn(cursor.tail_str()) {
					Some(i) => i,
					None => return err()
				};

				let s = cursor.str_slice_to(end);
				let cursor = cursor.slice_from(end);

				FromStr::from_str(s)
					.map(|i| Ok((i, cursor.clone())))
					.unwrap_or_else(err)
			}
		}
	};
}

pub trait Scanner<'a> {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(Self, Cur), ScanError>;
}

impl<'a> Scanner<'a> for bool {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(bool, Cur), ScanError> {
		cursor.expect_tok("true").map(|c| (true, c))
			.or_else(|_| cursor.expect_tok("false").map(|c| (false, c)))
			.or_else(|_| Err(cursor.expected("`true` or `false`")))
	}
}

impl<'a> Scanner<'a> for char {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(char, Cur), ScanError> {
		let s = cursor.tail_str();
		if s.len() == 0 {
			Err(cursor.expected("character"))
		} else {
			let ::std::str::CharRange { ch, next } = s.char_range_at(0);
			Ok((ch, cursor.slice_from(next)))
		}
	}
}

impl<'a> Scanner<'a> for &'a str {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(&'a str, Cur), ScanError> {
		cursor.pop_token().map(|sc| Ok(sc))
			.unwrap_or_else(|| Err(cursor.expected("any token")))
	}
}

impl<'a> Scanner<'a> for String {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(String, Cur), ScanError> {
		cursor.pop_token().map(|(s,c)| Ok((s.into_string(), c)))
			.unwrap_or_else(|| Err(cursor.expected("any token")))
	}
}

impl<'a> Scanner<'a> for () {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<((), Cur), ScanError> {
		Ok(((), cursor.clone()))
	}
}

from_str_slice_scanner! { scan_float -> f32 as "real number" }
from_str_slice_scanner! { scan_float -> f64 as "real number" }
from_str_slice_scanner! { scan_int -> i8 as "8-bit integer" }
from_str_slice_scanner! { scan_int -> i16 as "16-bit integer" }
from_str_slice_scanner! { scan_int -> i32 as "32-bit integer" }
from_str_slice_scanner! { scan_int -> i64 as "64-bit integer" }
from_str_slice_scanner! { scan_int -> int as "integer" }
from_str_slice_scanner! { scan_uint -> u8 as "8-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u16 as "16-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u32 as "32-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u64 as "64-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> uint as "unsigned integer" }

pub fn next_char_at(s: &str, at: uint) -> uint {
	let ::std::str::CharRange { ch: _, next } = s.char_range_at(at);
	next
}

pub fn scan_float(s: &str) -> Option<uint> {
	enum State {
		Start,
		Whole,
		Suffix,
		ExponentStart,
		Exponent,
	}

	let mut state = Start;

	for (i,c) in s.char_indices() {
		match state {
			Start => {
				assert!(i == 0);
				match c {
					'0'...'9' | '-' => state = Whole,
					_ => return None
				}
			},
			Whole => match c {
				'0'...'9' => (),
				'.' => state = Suffix,
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i)
			},
			Suffix => match c {
				'0'...'9' => (),
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i),
			},
			ExponentStart => match c {
				'+' | '-' | '0'...'9' => state = Exponent,
				_ => return Some(i)
			},
			Exponent => match c {
				'0'...'9' => (),
				_ => return Some(i)
			}
		}
	}

	return Some(s.len())
}

pub fn scan_uint<'a>(s: &'a str) -> Option<uint> {
	s.char_indices()
		.take_while(|&(_,c)| '0' <= c && c <= '9')
		.map(|(i,_)| Some(next_char_at(s, i)))
		.last().unwrap_or(None)
}

pub fn scan_int<'a>(s: &'a str) -> Option<uint> {
	if s.len() == 0 { return None }

	let (s, off) = if s.char_at(0) == '-' {
		(s.slice_from(1), 1)
	} else {
		(s, 0)
	};

	scan_uint(s).map(|end| end+off)
}

#[cfg(test)]
mod test {
	use Cursor;
	use ScanError;
	use super::Scanner;
	use tokenizer::WordsAndInts;
	use whitespace::Ignore;

	fn cur<'a>(s: &'a str) -> Cursor<'a, WordsAndInts, Ignore> {
		Cursor::new(s, WordsAndInts, Ignore)
	}

	fn scan_a<'a, T: Scanner<'a>>(s: &'a str) -> Result<(T, Cursor<'a, WordsAndInts, Ignore>), ScanError> {
		Scanner::scan(&cur(s))
	}

	#[test]
	fn test_bool() {
		assert!(scan_a::<bool>("").err().is_some());
		assert!(scan_a::<bool>("true").unwrap().0 == true);
		assert!(scan_a::<bool>("false").unwrap().0 == false);
		assert!(scan_a::<bool>("yes").err().is_some());
		assert!(scan_a::<bool>("no").err().is_some());
		assert!(scan_a::<bool>("on").err().is_some());
		assert!(scan_a::<bool>("off").err().is_some());
		assert!(scan_a::<bool>("1").err().is_some());
		assert!(scan_a::<bool>("0").err().is_some());
	}

	#[test]
	fn test_char() {
		assert!(scan_a::<char>("").err().is_some());
		assert!(scan_a::<char>("x").unwrap().0 == 'x');
		assert!(scan_a::<char>("xy").unwrap().0 == 'x');
		assert!(scan_a::<char>("日本語").unwrap().0 == '日');
	}

	#[test]
	fn test_floats() {
		use std::from_str::FromStr;
		use std::num::{Float, Zero};

		fn test<'a, F: Float + Scanner<'a> + FromStr>() {
			let f = |v:F| v;
			let fs = |s:&str| -> F FromStr::from_str(s).unwrap();

			assert!(scan_a::<F>("").err().is_some());
			assert!(scan_a::<F>("0").ok().unwrap().0 == f(Zero::zero()));
			assert!(scan_a::<F>("0.0").ok().unwrap().0 == f(Zero::zero()));
			assert!(scan_a::<F>("-0").ok().unwrap().0 == -f(Zero::zero()));
			assert!(scan_a::<F>("1.0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.00").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e1").ok().unwrap().0 == fs("10.0"));
		}

		test::<f32>();
		test::<f64>();
	}

	#[test]
	fn test_sized_ints() {
		use std::fmt::Show;
		use std::num::{Bounded, Int, Zero};

		fn test<'a, I: Int + Scanner<'a> + Show>(check_past: bool) {
			let zero: I = Zero::zero();
			let min: I = Bounded::min_value();
			let max: I = Bounded::max_value();

			assert!(scan_a::<I>("").err().is_some());
			assert!(scan_a::<I>("0").ok().unwrap().0 == zero);
			assert!(scan_a::<I>(format!("{}", min).as_slice()).ok().unwrap().0 == min);
			assert!(scan_a::<I>(format!("{}", max).as_slice()).ok().unwrap().0 == max);

			if check_past {
				let past_min: i64 = (min.to_i64()).unwrap() - 1;
				let past_max: u64 = (max.to_u64()).unwrap() + 1;
				assert!(scan_a::<I>(format!("{}", past_min).as_slice()).err().is_some());
				assert!(scan_a::<I>(format!("{}", past_max).as_slice()).err().is_some());
			}
		}

		test::<i8>(true);
		test::<i16>(true);
		test::<i32>(true);
		test::<i64>(false);
		test::<u8>(true);
		test::<u16>(true);
		test::<u32>(true);
		test::<u64>(false);
	}

	#[test]
	fn test_int() {
		assert!(scan_a::<int>("").err().is_some());
		assert!(scan_a::<int>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<int>("x").err().is_some());
		assert!(scan_a::<int>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("-").err().is_some());
		assert!(scan_a::<int>("-0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("-42").ok().unwrap().0 == -42);
		assert!(scan_a::<int>("-1_234").ok().unwrap().0 == -1);
	}

	#[test]
	fn test_uint() {
		assert!(scan_a::<uint>("").err().is_some());
		assert!(scan_a::<uint>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<uint>("x").err().is_some());
		assert!(scan_a::<uint>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("-").err().is_some());
		assert!(scan_a::<uint>("-0").err().is_some());
		assert!(scan_a::<uint>("-42").err().is_some());
		assert!(scan_a::<uint>("-1_234").err().is_some());
	}

	#[test]
	fn test_str() {
		assert!(scan_a::<&str>("").err().is_some());
		assert!(scan_a::<&str>("a").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("a b").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("abc").ok().unwrap().0 == "abc");
		assert!(scan_a::<&str>("ab-c").ok().unwrap().0 == "ab");
	}
}
<|endoftext|>


<commit_before>extern crate git2;
const TMP_NAME: &'static str = "refs/centralgit/tmp_fd2db5f8_bac2_4a1e_9487_4ac3414788aa";

use git2::*;
use std::path::Path;
use shell::Shell;
use super::RepoHost;
use std::collections::HashMap;
use super::ModuleToSubdir;

pub struct Scratch
{
    pub repo: Repository,
}

enum CommitKind
{
    Normal(Oid),
    Merge(Oid, Oid),
    Orphan,
}

impl Scratch
{
    pub fn new(path: &Path) -> Scratch
    {
        Scratch { repo: Repository::init_bare(&path).expect("could not init scratch") }
    }

    pub fn module_to_subdir(&self,
                            current: Oid,
                            path: Option<&Path>,
                            module_current: Option<Oid>,
                            new: Oid)
        -> ModuleToSubdir
    {
        let mut current = current;
        let (walk, initial) = if let Some(old) = module_current {

            if old == new {
                return ModuleToSubdir::NoChanges;
            }

            match self.repo.graph_descendant_of(new, old) {
                Err(_) | Ok(false) => {
                    debug!("graph_descendant_of({},{})", new, old);
                    return ModuleToSubdir::RejectNoFF;
                }
                Ok(true) => (),
            }

            debug!("==== walking commits from {} to {}", old, new);

            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            let range = format!("{}..{}", old, new);
            walk.push_range(&range).expect(&format!("walk: invalid range: {}", range));;
            walk.hide(old).expect("walk: can't hide");
            (walk, false)
        }
        else {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(new).expect("walk: can't push");
            (walk, true)
        };

        for rev in walk {
            let rev = rev.expect("walk: invalid rev");

            debug!("==== walking commit {}", rev);

            let module_commit = self.repo
                .find_commit(rev)
                .expect("walk: object is not actually a commit");

            if module_commit.parents().count() > 1 {
                // TODO: invectigate the possibility of allowing merge commits
                return ModuleToSubdir::RejectMerge;
            }

            if let Some(path) = path {
                debug!("==== Rewriting commit {}", rev);

                let tree = module_commit.tree().expect("walk: commit has no tree");
                let parent =
                    self.repo.find_commit(current).expect("walk: current object is no commit");

                let new_tree = self.replace_subtree(path,
                                                    tree.id(),
                                                    &parent.tree()
                                                        .expect("walk: parent has no tree"));

                current = self.rewrite(&module_commit, &vec![&parent], &new_tree);
            }
        }
        return ModuleToSubdir::Done(current, initial);
    }

    pub fn tracking(&self, host: &RepoHost, module: &str, branch: &str) -> Option<Object>
    {
        let remote_name = format!("{}", module);
        let fetch_url = host.local_path(&module);
        let mut remote = if let Ok(remote) = self.repo.find_remote(&remote_name) {
            remote
        }
        else {
            debug!("==== create remote (remote_name:{}, remote_url:{})",
                   &remote_name,
                   &fetch_url);
            self.repo.remote(&remote_name, &fetch_url).expect("can't create remote")
        };

        let rs = remote.get_refspec(0).unwrap().str().unwrap().to_string();
        if let Ok(_) = remote.fetch(&[&rs], None, None) {
            return self.repo
                .revparse_single(&format!("remotes/{}/{}", module, branch))
                .ok();
        }
        else {
            return None;
        }
    }

    // force push of the new revision-object to temp repo
    pub fn transfer(&self, rev: &str, source: &Path) -> Object
    {
        // TODO: implement using libgit
        let target = &self.repo.path();
        let shell = Shell { cwd: source.to_path_buf() };
        shell.command(&format!("git update-ref {} {}", TMP_NAME, rev));
        shell.command(&format!("git push --force {} {}",
                               &target.to_string_lossy(),
                               TMP_NAME));

        let obj = self.repo.revparse_single(rev).expect("can't find transfered ref");
        return obj;
    }

    // takes everything from base except it's tree and replaces it with the tree
    // given
    pub fn rewrite(&self, base: &Commit, parents: &[&Commit], tree: &Tree) -> Oid
    {
        if parents.len() == 0 {
            ::std::fs::remove_file(self.repo.path().join("HEAD")).expect("can't remove HEAD");
        }
        else {
            self.repo.set_head_detached(parents[0].id()).expect("rewrite: can't detach head");
        }
        self.repo
            .commit(Some("HEAD"),
                    &base.author(),
                    &base.committer(),
                    &base.message().unwrap_or("no message"),
                    tree,
                    parents)
            .expect("rewrite: can't commit")
    }

    pub fn push(&self, host: &RepoHost, oid: Oid, module: &str, target: &str) -> String
    {
        self.repo.set_head_detached(oid).expect("can't detach HEAD");
        let cmd = format!("if git push {} HEAD:{};then echo \"====\n==== SUCCESS!\n==== Ignore \
                           the error message below.\n====\";else echo \"####\n#### \
                           FAILED\n####\n\";fi",
                          host.remote_url(module),
                          target);
        let shell = Shell { cwd: self.repo.path().to_path_buf() };
        let (stdout, stderr) = shell.command(&cmd);
        debug!("push: {}\n{}\n{}", cmd, stdout, stderr);
        format!("{}\n\n{}", stderr, stdout)
    }

    fn subtree(&self, tree: &Tree, path: &Path) -> Option<Tree>
    {
        if let Some(oid) = tree.get_path(path).map(|x| x.id()).ok() {
            return self.repo.find_tree(oid).ok();
        }
        else {
            return None;
        }
    }

    fn replace_child(&self, child: &Path, subtree: Oid, full_tree: &Tree) -> Tree
    {
        let full_tree_id = {
            let mut builder = self.repo
                .treebuilder(Some(&full_tree))
                .expect("replace_child: can't create treebuilder");
            builder.insert(child, subtree, 0o0040000) // GIT_FILEMODE_TREE
                .expect("replace_child: can't insert tree");
            builder.write().expect("replace_child: can't write tree")
        };
        return self.repo.find_tree(full_tree_id).expect("replace_child: can't find new tree");
    }

    pub fn replace_subtree(&self, path: &Path, subtree: Oid, full_tree: &Tree) -> Tree
    {
        if path.components().count() == 1 {
            return self.replace_child(path, subtree, full_tree);
        }
        else {
            let name = Path::new(path.file_name().expect("no module name"));
            let path = path.parent().expect("module not in subdir");

            let st = if let Some(st) = self.subtree(&full_tree, path) {
                st
            }
            else {
                let empty = self.repo.treebuilder(None).unwrap().write().unwrap();
                self.repo.find_tree(empty).unwrap()
            };

            let tree = self.replace_child(name, subtree, &st);

            return self.replace_subtree(path, tree.id(), full_tree);
        }
    }

    pub fn split_subdir(&self, module: &str, newrev: Oid) -> Option<Oid>
    {
        let walk = {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(newrev).expect("walk.push");
            walk
        };

        let mut map = HashMap::<Oid, Oid>::new();

        'walk: for commit in walk {
            let commit = self.repo.find_commit(commit.unwrap()).unwrap();
            let tree = commit.tree().expect("commit has no tree");

            let new_tree = if let Ok(tree_entry) = tree.get_path(&Path::new(&module)) {
                self.repo.find_tree(tree_entry.id()).expect("central_submit: can't find tree")
            }
            else {
                continue 'walk;
            };

            match match commit.parents().count() {
                2 => {
                    let parent1 = commit.parents().nth(0).unwrap().id();
                    let parent2 = commit.parents().nth(1).unwrap().id();
                    match (map.get(&parent1), map.get(&parent2)) {
                        (Some(&parent1), Some(&parent2)) => CommitKind::Merge(parent1, parent2),
                        (Some(&parent), None) => CommitKind::Normal(parent),
                        (None, Some(&parent)) => CommitKind::Normal(parent),
                        _ => CommitKind::Orphan,
                    }
                }
                1 => {
                    let parent = commit.parents().nth(0).unwrap().id();
                    match map.get(&parent) {
                        Some(&parent) => CommitKind::Normal(parent),
                        _ => CommitKind::Orphan,
                    }
                }
                _ => CommitKind::Orphan,
            } {
                CommitKind::Merge(parent1, parent2) => {
                    let parent1 = self.repo.find_commit(parent1).unwrap();
                    let parent2 = self.repo.find_commit(parent2).unwrap();
                    map.insert(commit.id(),
                               self.rewrite(&commit, &[&parent1, &parent2], &new_tree));
                }
                CommitKind::Normal(parent) => {
                    let parent = self.repo.find_commit(parent).unwrap();
                    if new_tree.id() == parent.tree().unwrap().id() {
                        map.insert(commit.id(), parent.id());
                    }
                    else {
                        map.insert(commit.id(), self.rewrite(&commit, &[&parent], &new_tree));
                    }
                }
                CommitKind::Orphan => {
                    map.insert(commit.id(), self.rewrite(&commit, &[], &new_tree));
                }
            }
        }

        return map.get(&newrev).map(|&id| id);
    }

    pub fn find_all_subdirs(&self, tree: &Tree) -> Vec<String>
    {
        let mut sd = vec![];
        for item in tree {
            if let Ok(st) = self.repo.find_tree(item.id()) {
                let name = item.name().unwrap();
                if !name.starts_with(".") {
                    sd.push(name.to_string());
                    for r in self.find_all_subdirs(&st) {
                        sd.push(format!("{}/{}", name, r));
                    }
                }
            }
        }
        return sd;
    }

    pub fn join_to_subdir(&self, dst: Oid, path: &Path, src: Oid, signature: &Signature) -> Oid
    {
        let dst = self.repo.find_commit(dst).unwrap();
        let src = self.repo.find_commit(src).unwrap();

        let walk = {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(src.id()).expect("walk.push");
            walk
        };

        let empty =
            self.repo.find_tree(self.repo.treebuilder(None).unwrap().write().unwrap()).unwrap();
        let mut map = HashMap::<Oid, Oid>::new();

        'walk: for commit in walk {
            let commit = self.repo.find_commit(commit.unwrap()).unwrap();
            let tree = commit.tree().expect("commit has no tree");
            let new_tree = self.replace_subtree(path, tree.id(), &empty);

            match commit.parents().count() {
                2 => {
                    let parent1 = commit.parents().nth(0).unwrap().id();
                    let parent2 = commit.parents().nth(1).unwrap().id();
                    if let (Some(&parent1), Some(&parent2)) = (map.get(&parent1),
                                                               map.get(&parent2)) {
                        let parent1 = self.repo.find_commit(parent1).unwrap();
                        let parent2 = self.repo.find_commit(parent2).unwrap();

                        map.insert(commit.id(),
                                   self.rewrite(&commit, &[&parent1, &parent2], &new_tree));
                        continue 'walk;
                    }
                }
                1 => {
                    let parent = commit.parents().nth(0).unwrap().id();
                    let parent = *map.get(&parent).unwrap();
                    let parent = self.repo.find_commit(parent).unwrap();
                    map.insert(commit.id(), self.rewrite(&commit, &[&parent], &new_tree));
                    continue 'walk;
                }
                _ => {}
            }

            map.insert(commit.id(), self.rewrite(&commit, &[], &new_tree));
        }

        let final_tree = self.replace_subtree(path, src.tree().unwrap().id(), &dst.tree().unwrap());

        let parents = [&dst, &self.repo.find_commit(map[&src.id()]).unwrap()];
        self.repo.set_head_detached(parents[0].id()).expect("join: can't detach head");

        let join_commit = self.repo
            .commit(Some("HEAD"),
                    signature,
                    signature,
                    &format!("join repo into {:?}", path),
                    &final_tree,
                    &parents)
            .unwrap();
        return join_commit;

    }
}
<commit_msg>panic when seeing an octopus<commit_after>extern crate git2;
const TMP_NAME: &'static str = "refs/centralgit/tmp_fd2db5f8_bac2_4a1e_9487_4ac3414788aa";

use git2::*;
use std::path::Path;
use shell::Shell;
use super::RepoHost;
use std::collections::HashMap;
use super::ModuleToSubdir;

pub struct Scratch
{
    pub repo: Repository,
}

enum CommitKind
{
    Normal(Oid),
    Merge(Oid, Oid),
    Orphan,
}

impl Scratch
{
    pub fn new(path: &Path) -> Scratch
    {
        Scratch { repo: Repository::init_bare(&path).expect("could not init scratch") }
    }

    pub fn module_to_subdir(&self,
                            current: Oid,
                            path: Option<&Path>,
                            module_current: Option<Oid>,
                            new: Oid)
        -> ModuleToSubdir
    {
        let mut current = current;
        let (walk, initial) = if let Some(old) = module_current {

            if old == new {
                return ModuleToSubdir::NoChanges;
            }

            match self.repo.graph_descendant_of(new, old) {
                Err(_) | Ok(false) => {
                    debug!("graph_descendant_of({},{})", new, old);
                    return ModuleToSubdir::RejectNoFF;
                }
                Ok(true) => (),
            }

            debug!("==== walking commits from {} to {}", old, new);

            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            let range = format!("{}..{}", old, new);
            walk.push_range(&range).expect(&format!("walk: invalid range: {}", range));;
            walk.hide(old).expect("walk: can't hide");
            (walk, false)
        }
        else {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(new).expect("walk: can't push");
            (walk, true)
        };

        for rev in walk {
            let rev = rev.expect("walk: invalid rev");

            debug!("==== walking commit {}", rev);

            let module_commit = self.repo
                .find_commit(rev)
                .expect("walk: object is not actually a commit");

            if module_commit.parents().count() > 1 {
                // TODO: invectigate the possibility of allowing merge commits
                return ModuleToSubdir::RejectMerge;
            }

            if let Some(path) = path {
                debug!("==== Rewriting commit {}", rev);

                let tree = module_commit.tree().expect("walk: commit has no tree");
                let parent =
                    self.repo.find_commit(current).expect("walk: current object is no commit");

                let new_tree = self.replace_subtree(path,
                                                    tree.id(),
                                                    &parent.tree()
                                                        .expect("walk: parent has no tree"));

                current = self.rewrite(&module_commit, &vec![&parent], &new_tree);
            }
        }
        return ModuleToSubdir::Done(current, initial);
    }

    pub fn tracking(&self, host: &RepoHost, module: &str, branch: &str) -> Option<Object>
    {
        let remote_name = format!("{}", module);
        let fetch_url = host.local_path(&module);
        let mut remote = if let Ok(remote) = self.repo.find_remote(&remote_name) {
            remote
        }
        else {
            debug!("==== create remote (remote_name:{}, remote_url:{})",
                   &remote_name,
                   &fetch_url);
            self.repo.remote(&remote_name, &fetch_url).expect("can't create remote")
        };

        let rs = remote.get_refspec(0).unwrap().str().unwrap().to_string();
        if let Ok(_) = remote.fetch(&[&rs], None, None) {
            return self.repo
                .revparse_single(&format!("remotes/{}/{}", module, branch))
                .ok();
        }
        else {
            return None;
        }
    }

    // force push of the new revision-object to temp repo
    pub fn transfer(&self, rev: &str, source: &Path) -> Object
    {
        // TODO: implement using libgit
        let target = &self.repo.path();
        let shell = Shell { cwd: source.to_path_buf() };
        shell.command(&format!("git update-ref {} {}", TMP_NAME, rev));
        shell.command(&format!("git push --force {} {}",
                               &target.to_string_lossy(),
                               TMP_NAME));

        let obj = self.repo.revparse_single(rev).expect("can't find transfered ref");
        return obj;
    }

    // takes everything from base except it's tree and replaces it with the tree
    // given
    pub fn rewrite(&self, base: &Commit, parents: &[&Commit], tree: &Tree) -> Oid
    {
        if parents.len() == 0 {
            ::std::fs::remove_file(self.repo.path().join("HEAD")).expect("can't remove HEAD");
        }
        else {
            self.repo.set_head_detached(parents[0].id()).expect("rewrite: can't detach head");
        }
        self.repo
            .commit(Some("HEAD"),
                    &base.author(),
                    &base.committer(),
                    &base.message().unwrap_or("no message"),
                    tree,
                    parents)
            .expect("rewrite: can't commit")
    }

    pub fn push(&self, host: &RepoHost, oid: Oid, module: &str, target: &str) -> String
    {
        self.repo.set_head_detached(oid).expect("can't detach HEAD");
        let cmd = format!("if git push {} HEAD:{};then echo \"====\n==== SUCCESS!\n==== Ignore \
                           the error message below.\n====\";else echo \"####\n#### \
                           FAILED\n####\n\";fi",
                          host.remote_url(module),
                          target);
        let shell = Shell { cwd: self.repo.path().to_path_buf() };
        let (stdout, stderr) = shell.command(&cmd);
        debug!("push: {}\n{}\n{}", cmd, stdout, stderr);
        format!("{}\n\n{}", stderr, stdout)
    }

    fn subtree(&self, tree: &Tree, path: &Path) -> Option<Tree>
    {
        if let Some(oid) = tree.get_path(path).map(|x| x.id()).ok() {
            return self.repo.find_tree(oid).ok();
        }
        else {
            return None;
        }
    }

    fn replace_child(&self, child: &Path, subtree: Oid, full_tree: &Tree) -> Tree
    {
        let full_tree_id = {
            let mut builder = self.repo
                .treebuilder(Some(&full_tree))
                .expect("replace_child: can't create treebuilder");
            builder.insert(child, subtree, 0o0040000) // GIT_FILEMODE_TREE
                .expect("replace_child: can't insert tree");
            builder.write().expect("replace_child: can't write tree")
        };
        return self.repo.find_tree(full_tree_id).expect("replace_child: can't find new tree");
    }

    pub fn replace_subtree(&self, path: &Path, subtree: Oid, full_tree: &Tree) -> Tree
    {
        if path.components().count() == 1 {
            return self.replace_child(path, subtree, full_tree);
        }
        else {
            let name = Path::new(path.file_name().expect("no module name"));
            let path = path.parent().expect("module not in subdir");

            let st = if let Some(st) = self.subtree(&full_tree, path) {
                st
            }
            else {
                let empty = self.repo.treebuilder(None).unwrap().write().unwrap();
                self.repo.find_tree(empty).unwrap()
            };

            let tree = self.replace_child(name, subtree, &st);

            return self.replace_subtree(path, tree.id(), full_tree);
        }
    }

    pub fn split_subdir(&self, module: &str, newrev: Oid) -> Option<Oid>
    {
        let walk = {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(newrev).expect("walk.push");
            walk
        };

        let mut map = HashMap::<Oid, Oid>::new();

        'walk: for commit in walk {
            let commit = self.repo.find_commit(commit.unwrap()).unwrap();
            let tree = commit.tree().expect("commit has no tree");

            let new_tree = if let Ok(tree_entry) = tree.get_path(&Path::new(&module)) {
                self.repo.find_tree(tree_entry.id()).expect("central_submit: can't find tree")
            }
            else {
                continue 'walk;
            };

            match match commit.parents().count() {
                2 => {
                    let parent1 = commit.parents().nth(0).unwrap().id();
                    let parent2 = commit.parents().nth(1).unwrap().id();
                    match (map.get(&parent1), map.get(&parent2)) {
                        (Some(&parent1), Some(&parent2)) => CommitKind::Merge(parent1, parent2),
                        (Some(&parent), None) => CommitKind::Normal(parent),
                        (None, Some(&parent)) => CommitKind::Normal(parent),
                        _ => CommitKind::Orphan,
                    }
                }
                1 => {
                    let parent = commit.parents().nth(0).unwrap().id();
                    match map.get(&parent) {
                        Some(&parent) => CommitKind::Normal(parent),
                        _ => CommitKind::Orphan,
                    }
                }
                0 => CommitKind::Orphan,
                _ => {
                    panic!("commit with {} parents: {}",
                           commit.parents().count(),
                           commit.id())
                }
            } {
                CommitKind::Merge(parent1, parent2) => {
                    let parent1 = self.repo.find_commit(parent1).unwrap();
                    let parent2 = self.repo.find_commit(parent2).unwrap();
                    map.insert(commit.id(),
                               self.rewrite(&commit, &[&parent1, &parent2], &new_tree));
                }
                CommitKind::Normal(parent) => {
                    let parent = self.repo.find_commit(parent).unwrap();
                    if new_tree.id() == parent.tree().unwrap().id() {
                        map.insert(commit.id(), parent.id());
                    }
                    else {
                        map.insert(commit.id(), self.rewrite(&commit, &[&parent], &new_tree));
                    }
                }
                CommitKind::Orphan => {
                    map.insert(commit.id(), self.rewrite(&commit, &[], &new_tree));
                }
            }
        }

        return map.get(&newrev).map(|&id| id);
    }

    pub fn find_all_subdirs(&self, tree: &Tree) -> Vec<String>
    {
        let mut sd = vec![];
        for item in tree {
            if let Ok(st) = self.repo.find_tree(item.id()) {
                let name = item.name().unwrap();
                if !name.starts_with(".") {
                    sd.push(name.to_string());
                    for r in self.find_all_subdirs(&st) {
                        sd.push(format!("{}/{}", name, r));
                    }
                }
            }
        }
        return sd;
    }

    pub fn join_to_subdir(&self, dst: Oid, path: &Path, src: Oid, signature: &Signature) -> Oid
    {
        let dst = self.repo.find_commit(dst).unwrap();
        let src = self.repo.find_commit(src).unwrap();

        let walk = {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(src.id()).expect("walk.push");
            walk
        };

        let empty =
            self.repo.find_tree(self.repo.treebuilder(None).unwrap().write().unwrap()).unwrap();
        let mut map = HashMap::<Oid, Oid>::new();

        'walk: for commit in walk {
            let commit = self.repo.find_commit(commit.unwrap()).unwrap();
            let tree = commit.tree().expect("commit has no tree");
            let new_tree = self.replace_subtree(path, tree.id(), &empty);

            match commit.parents().count() {
                2 => {
                    let parent1 = commit.parents().nth(0).unwrap().id();
                    let parent2 = commit.parents().nth(1).unwrap().id();
                    if let (Some(&parent1), Some(&parent2)) = (map.get(&parent1),
                                                               map.get(&parent2)) {
                        let parent1 = self.repo.find_commit(parent1).unwrap();
                        let parent2 = self.repo.find_commit(parent2).unwrap();

                        map.insert(commit.id(),
                                   self.rewrite(&commit, &[&parent1, &parent2], &new_tree));
                        continue 'walk;
                    }
                }
                1 => {
                    let parent = commit.parents().nth(0).unwrap().id();
                    let parent = *map.get(&parent).unwrap();
                    let parent = self.repo.find_commit(parent).unwrap();
                    map.insert(commit.id(), self.rewrite(&commit, &[&parent], &new_tree));
                    continue 'walk;
                }
                0 => {}
                _ => {
                    panic!("commit with {} parents: {}",
                           commit.parents().count(),
                           commit.id())
                }
            }

            map.insert(commit.id(), self.rewrite(&commit, &[], &new_tree));
        }

        let final_tree = self.replace_subtree(path, src.tree().unwrap().id(), &dst.tree().unwrap());

        let parents = [&dst, &self.repo.find_commit(map[&src.id()]).unwrap()];
        self.repo.set_head_detached(parents[0].id()).expect("join: can't detach head");

        let join_commit = self.repo
            .commit(Some("HEAD"),
                    signature,
                    signature,
                    &format!("join repo into {:?}", path),
                    &final_tree,
                    &parents)
            .unwrap();
        return join_commit;

    }
}
<|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 http://mozilla.org/MPL/2.0/. */

use std::collections::HashMap;
use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
use webrender_traits::{GlyphKey, GlyphOptions};
use gamma_lut::{GammaLut, Color as ColorLut};

use dwrote;

lazy_static! {
    static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
        family_name: "Arial".to_owned(),
        weight: dwrote::FontWeight::Regular,
        stretch: dwrote::FontStretch::Normal,
        style: dwrote::FontStyle::Normal,
    };
}

pub struct FontContext {
    fonts: HashMap<FontKey, dwrote::FontFace>,
    gamma_lut: GammaLut,
    gdi_gamma_lut: GammaLut,
}

pub struct RasterizedGlyph {
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

fn dwrite_texture_type(render_mode: FontRenderMode) ->
                       dwrote::DWRITE_TEXTURE_TYPE {
    match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_TEXTURE_ALIASED_1x1 ,
        FontRenderMode::Alpha |
        FontRenderMode::Subpixel => dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1,
    }
}

fn dwrite_measure_mode(render_mode: FontRenderMode, options: Option<GlyphOptions>) ->
                       dwrote::DWRITE_MEASURING_MODE {
    if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options {
        return dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC;
    }

    match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
        FontRenderMode::Alpha |
        FontRenderMode::Subpixel => dwrote::DWRITE_MEASURING_MODE_NATURAL,
    }
}

fn dwrite_render_mode(font_face: &dwrote::FontFace,
                      render_mode: FontRenderMode,
                      em_size: f32,
                      measure_mode: dwrote::DWRITE_MEASURING_MODE,
                      options: Option<GlyphOptions>) ->
                      dwrote::DWRITE_RENDERING_MODE {
    if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options {
        return dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC;
    }

    let dwrite_render_mode = match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_RENDERING_MODE_ALIASED,
        FontRenderMode::Alpha |
        FontRenderMode::Subpixel => {
            font_face.get_recommended_rendering_mode_default_params(em_size,
                                                                    1.0,
                                                                    measure_mode)
        },
    };

    if dwrite_render_mode  == dwrote::DWRITE_RENDERING_MODE_OUTLINE {
        // Outline mode is not supported
        return dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
    }

    dwrite_render_mode
}

fn get_glyph_dimensions_with_analysis(analysis: dwrote::GlyphRunAnalysis,
                                      texture_type: dwrote::DWRITE_TEXTURE_TYPE)
                                      -> Option<GlyphDimensions> {
    let bounds = analysis.get_alpha_texture_bounds(texture_type);

    let width = (bounds.right - bounds.left) as u32;
    let height = (bounds.bottom - bounds.top) as u32;

    // Alpha texture bounds can sometimes return an empty rect
    // Such as for spaces
    if width == 0 || height == 0 {
        return None
    }

    Some(GlyphDimensions {
        left: bounds.left,
        top: -bounds.top,
        width: width,
        height: height,
    })
}

impl FontContext {
    pub fn new() -> FontContext {
        // These are the default values we use in Gecko.
        // We use a gamma value of 2.3 for gdi fonts
        // TODO: Fetch this data from Gecko itself.
        let contrast = 1.0;
        let gamma = 1.8;
        let gdi_gamma = 2.3;
        FontContext {
            fonts: HashMap::new(),
            gamma_lut: GammaLut::new(contrast, gamma, gamma),
            gdi_gamma_lut: GammaLut::new(contrast, gdi_gamma, gdi_gamma),
        }
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, data: &[u8]) {
        if self.fonts.contains_key(font_key) {
            return
        }

        if let Some(font_file) = dwrote::FontFile::new_from_data(data) {
            let face = font_file.create_face(0, dwrote::DWRITE_FONT_SIMULATIONS_NONE);
            self.fonts.insert((*font_key).clone(), face);
        } else {
            // XXX add_raw_font needs to have a way to return an error
            debug!("DWrite WR failed to load font from data, using Arial instead");
            self.add_native_font(font_key, DEFAULT_FONT_DESCRIPTOR.clone());
        }
    }

    pub fn add_native_font(&mut self, font_key: &FontKey, font_handle: dwrote::FontDescriptor) {
        if self.fonts.contains_key(font_key) {
            return
        }

        let system_fc = dwrote::FontCollection::system();
        let font = system_fc.get_font_from_descriptor(&font_handle).unwrap();
        let face = font.create_font_face();
        self.fonts.insert((*font_key).clone(), face);
    }

    // Assumes RGB format from dwrite, which is 3 bytes per pixel as dwrite
    // doesn't output an alpha value via GlyphRunAnalysis::CreateAlphaTexture
    #[allow(dead_code)]
    fn print_glyph_data(&self, data: &Vec<u8>, width: usize, height: usize) {
        // Rust doesn't have step_by support on stable :(
        for i in 0..height {
            let current_height = i * width * 3;

            for pixel in data[current_height .. current_height + (width * 3)].chunks(3) {
                let r = pixel[0];
                let g = pixel[1];
                let b = pixel[2];
                print!("({}, {}, {}) ", r, g, b, );
            }
            println!("");
        }
    }

    fn create_glyph_analysis(&self, key: &GlyphKey,
                            render_mode: FontRenderMode,
                            options: Option<GlyphOptions>) ->
                            dwrote::GlyphRunAnalysis {
        let face = self.fonts.get(&key.font_key).unwrap();
        let glyph = key.index as u16;
        let advance = 0.0f32;
        let offset = dwrote::GlyphOffset { advanceOffset: 0.0, ascenderOffset: 0.0 };

        let glyph_run = dwrote::DWRITE_GLYPH_RUN {
            fontFace: unsafe { face.as_ptr() },
            fontEmSize: key.size.to_f32_px(), // size in DIPs (1/96", same as CSS pixels)
            glyphCount: 1,
            glyphIndices: &glyph,
            glyphAdvances: &advance,
            glyphOffsets: &offset,
            isSideways: 0,
            bidiLevel: 0,
        };

        let dwrite_measure_mode = dwrite_measure_mode(render_mode, options);
        let dwrite_render_mode = dwrite_render_mode(face,
                                                    render_mode,
                                                    key.size.to_f32_px(),
                                                    dwrite_measure_mode,
                                                    options);

        let (x_offset, y_offset) = key.subpixel_point.to_f64();
        let transform = Some(
                        dwrote::DWRITE_MATRIX { m11: 1.0, m12: 0.0, m21: 0.0, m22: 1.0,
                                                dx: x_offset as f32, dy: y_offset as f32 }
                        );

        dwrote::GlyphRunAnalysis::create(&glyph_run, 1.0, transform,
                                         dwrite_render_mode,
                                         dwrite_measure_mode,
                                         0.0, 0.0)
    }

    // TODO: Pipe GlyphOptions into glyph_dimensions too
    pub fn get_glyph_dimensions(&self,
                                key: &GlyphKey)
                                -> Option<GlyphDimensions> {
        // Probably have to default to something else here.
        let render_mode = FontRenderMode::Subpixel;
        let analysis = self.create_glyph_analysis(key, render_mode, None);

        let texture_type = dwrite_texture_type(render_mode);
        get_glyph_dimensions_with_analysis(analysis, texture_type)
    }

    // DWRITE gives us values in RGB. WR doesn't really touch it after. Note, CG returns in BGR
    // TODO: Decide whether all fonts should return RGB or BGR
    fn convert_to_rgba(&self, pixels: &Vec<u8>, render_mode: FontRenderMode) -> Vec<u8> {
        match render_mode {
            FontRenderMode::Mono => {
                let mut rgba_pixels: Vec<u8> = vec![0; pixels.len() * 4];
                for i in 0..pixels.len() {
                    rgba_pixels[i*4+0] = pixels[i];
                    rgba_pixels[i*4+1] = pixels[i];
                    rgba_pixels[i*4+2] = pixels[i];
                    rgba_pixels[i*4+3] = pixels[i];
                }
                rgba_pixels
            }
            FontRenderMode::Alpha => {
                let length = pixels.len() / 3;
                let mut rgba_pixels: Vec<u8> = vec![0; length * 4];
                for i in 0..length {
                    // TODO(vlad): we likely need to do something smarter
                    // This is what skia does
                    let alpha = ((pixels[i*3+0] as u32 +
                                pixels[i*3+1] as u32 +
                                pixels[i*3+2] as u32)
                                / 3) as u8;

                    rgba_pixels[i*4+0] = alpha;
                    rgba_pixels[i*4+1] = alpha;
                    rgba_pixels[i*4+2] = alpha;
                    rgba_pixels[i*4+3] = alpha;
                }
                rgba_pixels
            }
            FontRenderMode::Subpixel => {
                let length = pixels.len() / 3;
                let mut rgba_pixels: Vec<u8> = vec![0; length * 4];
                for i in 0..length {
                    rgba_pixels[i*4+0] = pixels[i*3+0];
                    rgba_pixels[i*4+1] = pixels[i*3+1];
                    rgba_pixels[i*4+2] = pixels[i*3+2];
                    rgba_pixels[i*4+3] = 0xff;
                }
                rgba_pixels
            }
        }
    }

    pub fn rasterize_glyph(&mut self,
                           key: &GlyphKey,
                           render_mode: FontRenderMode,
                           glyph_options: Option<GlyphOptions>)
                           -> Option<RasterizedGlyph> {
        let analysis = self.create_glyph_analysis(key,
                                                  render_mode,
                                                  glyph_options);
        let texture_type = dwrite_texture_type(render_mode);

        let bounds = analysis.get_alpha_texture_bounds(texture_type);
        let width = (bounds.right - bounds.left) as usize;
        let height = (bounds.bottom - bounds.top) as usize;

        // We should not get here since glyph_dimensions would return
        // None for empty glyphs.
        assert!(width > 0 && height > 0);

        let mut pixels = analysis.create_alpha_texture(texture_type, bounds);

        let lut_correction = match glyph_options {
            Some(option) => {
                if option.force_gdi_rendering {
                    &self.gdi_gamma_lut
                } else {
                    &self.gamma_lut
                }
            },
            None => &self.gamma_lut
        };

        lut_correction.preblend_rgb(&mut pixels, width, height,
                                    ColorLut::new(key.color.r,
                                                  key.color.g,
                                                  key.color.b,
                                                  key.color.a));

        let rgba_pixels = self.convert_to_rgba(&mut pixels, render_mode);

        Some(RasterizedGlyph {
            width: width as u32,
            height: height as u32,
            bytes: rgba_pixels,
        })
    }
}
<commit_msg>Auto merge of #928 - changm:font, r=glennw<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 http://mozilla.org/MPL/2.0/. */

use std::collections::HashMap;
use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
use webrender_traits::{GlyphKey, GlyphOptions};
use gamma_lut::{GammaLut, Color as ColorLut};

use dwrote;

lazy_static! {
    static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
        family_name: "Arial".to_owned(),
        weight: dwrote::FontWeight::Regular,
        stretch: dwrote::FontStretch::Normal,
        style: dwrote::FontStyle::Normal,
    };
}

pub struct FontContext {
    fonts: HashMap<FontKey, dwrote::FontFace>,
    gamma_lut: GammaLut,
    gdi_gamma_lut: GammaLut,
}

pub struct RasterizedGlyph {
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

fn dwrite_texture_type(render_mode: FontRenderMode) ->
                       dwrote::DWRITE_TEXTURE_TYPE {
    match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_TEXTURE_ALIASED_1x1 ,
        FontRenderMode::Alpha |
        FontRenderMode::Subpixel => dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1,
    }
}

fn dwrite_measure_mode(render_mode: FontRenderMode, options: Option<GlyphOptions>) ->
                       dwrote::DWRITE_MEASURING_MODE {
    if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options {
        return dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC;
    }

    match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
        FontRenderMode::Alpha |
        FontRenderMode::Subpixel => dwrote::DWRITE_MEASURING_MODE_NATURAL,
    }
}

fn dwrite_render_mode(font_face: &dwrote::FontFace,
                      render_mode: FontRenderMode,
                      em_size: f32,
                      measure_mode: dwrote::DWRITE_MEASURING_MODE,
                      options: Option<GlyphOptions>) ->
                      dwrote::DWRITE_RENDERING_MODE {
    if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options {
        return dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC;
    }

    let dwrite_render_mode = match render_mode {
        FontRenderMode::Mono => dwrote::DWRITE_RENDERING_MODE_ALIASED,
        FontRenderMode::Alpha |
        FontRenderMode::Subpixel => {
            font_face.get_recommended_rendering_mode_default_params(em_size,
                                                                    1.0,
                                                                    measure_mode)
        },
    };

    if dwrite_render_mode  == dwrote::DWRITE_RENDERING_MODE_OUTLINE {
        // Outline mode is not supported
        return dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
    }

    dwrite_render_mode
}

fn get_glyph_dimensions_with_analysis(analysis: dwrote::GlyphRunAnalysis,
                                      texture_type: dwrote::DWRITE_TEXTURE_TYPE)
                                      -> Option<GlyphDimensions> {
    let bounds = analysis.get_alpha_texture_bounds(texture_type);

    let width = (bounds.right - bounds.left) as u32;
    let height = (bounds.bottom - bounds.top) as u32;

    // Alpha texture bounds can sometimes return an empty rect
    // Such as for spaces
    if width == 0 || height == 0 {
        return None
    }

    Some(GlyphDimensions {
        left: bounds.left,
        top: -bounds.top,
        width: width,
        height: height,
    })
}

impl FontContext {
    pub fn new() -> FontContext {
        // These are the default values we use in Gecko.
        // We use a gamma value of 2.3 for gdi fonts
        // TODO: Fetch this data from Gecko itself.
        let contrast = 1.0;
        let gamma = 1.8;
        let gdi_gamma = 2.3;
        FontContext {
            fonts: HashMap::new(),
            gamma_lut: GammaLut::new(contrast, gamma, gamma),
            gdi_gamma_lut: GammaLut::new(contrast, gdi_gamma, gdi_gamma),
        }
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, data: &[u8]) {
        if self.fonts.contains_key(font_key) {
            return
        }

        if let Some(font_file) = dwrote::FontFile::new_from_data(data) {
            let face = font_file.create_face(0, dwrote::DWRITE_FONT_SIMULATIONS_NONE);
            self.fonts.insert((*font_key).clone(), face);
        } else {
            // XXX add_raw_font needs to have a way to return an error
            debug!("DWrite WR failed to load font from data, using Arial instead");
            self.add_native_font(font_key, DEFAULT_FONT_DESCRIPTOR.clone());
        }
    }

    pub fn add_native_font(&mut self, font_key: &FontKey, font_handle: dwrote::FontDescriptor) {
        if self.fonts.contains_key(font_key) {
            return
        }

        let system_fc = dwrote::FontCollection::system();
        let font = system_fc.get_font_from_descriptor(&font_handle).unwrap();
        let face = font.create_font_face();
        self.fonts.insert((*font_key).clone(), face);
    }

    // Assumes RGB format from dwrite, which is 3 bytes per pixel as dwrite
    // doesn't output an alpha value via GlyphRunAnalysis::CreateAlphaTexture
    #[allow(dead_code)]
    fn print_glyph_data(&self, data: &Vec<u8>, width: usize, height: usize) {
        // Rust doesn't have step_by support on stable :(
        for i in 0..height {
            let current_height = i * width * 3;

            for pixel in data[current_height .. current_height + (width * 3)].chunks(3) {
                let r = pixel[0];
                let g = pixel[1];
                let b = pixel[2];
                print!("({}, {}, {}) ", r, g, b, );
            }
            println!("");
        }
    }

    fn create_glyph_analysis(&self, key: &GlyphKey,
                            render_mode: FontRenderMode,
                            options: Option<GlyphOptions>) ->
                            dwrote::GlyphRunAnalysis {
        let face = self.fonts.get(&key.font_key).unwrap();
        let glyph = key.index as u16;
        let advance = 0.0f32;
        let offset = dwrote::GlyphOffset { advanceOffset: 0.0, ascenderOffset: 0.0 };

        let glyph_run = dwrote::DWRITE_GLYPH_RUN {
            fontFace: unsafe { face.as_ptr() },
            fontEmSize: key.size.to_f32_px(), // size in DIPs (1/96", same as CSS pixels)
            glyphCount: 1,
            glyphIndices: &glyph,
            glyphAdvances: &advance,
            glyphOffsets: &offset,
            isSideways: 0,
            bidiLevel: 0,
        };

        let dwrite_measure_mode = dwrite_measure_mode(render_mode, options);
        let dwrite_render_mode = dwrite_render_mode(face,
                                                    render_mode,
                                                    key.size.to_f32_px(),
                                                    dwrite_measure_mode,
                                                    options);

        let (x_offset, y_offset) = key.subpixel_point.to_f64();
        let transform = Some(
                        dwrote::DWRITE_MATRIX { m11: 1.0, m12: 0.0, m21: 0.0, m22: 1.0,
                                                dx: x_offset as f32, dy: y_offset as f32 }
                        );

        dwrote::GlyphRunAnalysis::create(&glyph_run, 1.0, transform,
                                         dwrite_render_mode,
                                         dwrite_measure_mode,
                                         0.0, 0.0)
    }

    // TODO: Pipe GlyphOptions into glyph_dimensions too
    pub fn get_glyph_dimensions(&self,
                                key: &GlyphKey)
                                -> Option<GlyphDimensions> {
        // Probably have to default to something else here.
        let render_mode = FontRenderMode::Subpixel;
        let analysis = self.create_glyph_analysis(key, render_mode, None);

        let texture_type = dwrite_texture_type(render_mode);
        get_glyph_dimensions_with_analysis(analysis, texture_type)
    }

    // DWRITE gives us values in RGB. WR doesn't really touch it after. Note, CG returns in BGR
    // TODO: Decide whether all fonts should return RGB or BGR
    fn convert_to_rgba(&self, pixels: &Vec<u8>, render_mode: FontRenderMode) -> Vec<u8> {
        match render_mode {
            FontRenderMode::Mono => {
                let mut rgba_pixels: Vec<u8> = vec![0; pixels.len() * 4];
                for i in 0..pixels.len() {
                    rgba_pixels[i*4+0] = pixels[i];
                    rgba_pixels[i*4+1] = pixels[i];
                    rgba_pixels[i*4+2] = pixels[i];
                    rgba_pixels[i*4+3] = pixels[i];
                }
                rgba_pixels
            }
            FontRenderMode::Alpha => {
                let length = pixels.len() / 3;
                let mut rgba_pixels: Vec<u8> = vec![0; length * 4];
                for i in 0..length {
                    // Only take the G channel, as its closest to D2D
                    let alpha = pixels[i*3 + 1] as u8;
                    rgba_pixels[i*4+0] = alpha;
                    rgba_pixels[i*4+1] = alpha;
                    rgba_pixels[i*4+2] = alpha;
                    rgba_pixels[i*4+3] = alpha;
                }
                rgba_pixels
            }
            FontRenderMode::Subpixel => {
                let length = pixels.len() / 3;
                let mut rgba_pixels: Vec<u8> = vec![0; length * 4];
                for i in 0..length {
                    rgba_pixels[i*4+0] = pixels[i*3+0];
                    rgba_pixels[i*4+1] = pixels[i*3+1];
                    rgba_pixels[i*4+2] = pixels[i*3+2];
                    rgba_pixels[i*4+3] = 0xff;
                }
                rgba_pixels
            }
        }
    }

    pub fn rasterize_glyph(&mut self,
                           key: &GlyphKey,
                           render_mode: FontRenderMode,
                           glyph_options: Option<GlyphOptions>)
                           -> Option<RasterizedGlyph> {
        let analysis = self.create_glyph_analysis(key,
                                                  render_mode,
                                                  glyph_options);
        let texture_type = dwrite_texture_type(render_mode);

        let bounds = analysis.get_alpha_texture_bounds(texture_type);
        let width = (bounds.right - bounds.left) as usize;
        let height = (bounds.bottom - bounds.top) as usize;

        // We should not get here since glyph_dimensions would return
        // None for empty glyphs.
        assert!(width > 0 && height > 0);

        let mut pixels = analysis.create_alpha_texture(texture_type, bounds);

        if render_mode != FontRenderMode::Mono {
            let lut_correction = match glyph_options {
                Some(option) => {
                    if option.force_gdi_rendering {
                        &self.gdi_gamma_lut
                    } else {
                        &self.gamma_lut
                    }
                },
                None => &self.gamma_lut
            };

            lut_correction.preblend_rgb(&mut pixels, width, height,
                                        ColorLut::new(key.color.r,
                                                      key.color.g,
                                                      key.color.b,
                                                      key.color.a));
        }

        let rgba_pixels = self.convert_to_rgba(&mut pixels, render_mode);

        Some(RasterizedGlyph {
            width: width as u32,
            height: height as u32,
            bytes: rgba_pixels,
        })
    }
}
<|endoftext|>


<commit_before>//!
//! A session is established with the kernel driver while a userspace
//! filesystem is mounted. The session connects to the kernel driver and
//! runs a loop that receives, dispatches and replies kernel requests.
//!

use std::cell::Cell;
use std::task;
use std::libc::{EAGAIN, EINTR, ENODEV, ENOENT};
use channel::Channel;
use Filesystem;
use request::Request;

/// The session data structure
pub struct Session<FS> {
	filesystem: FS,
	mountpoint: Path,
	ch: Channel,
	proto_major: uint,
	proto_minor: uint,
	initialized: bool,
	destroyed: bool,
}

impl<FS: Filesystem+Send> Session<FS> {
	/// Mount the given filesystem to the given mountpoint
	pub fn mount (filesystem: FS, mountpoint: &Path, options: &[&[u8]]) -> Session<FS> {
		info!("Mounting {}", mountpoint.display());
		let ch = Channel::mount(mountpoint, options).expect("unable to mount filesystem");
		Session {
			filesystem: filesystem,
			mountpoint: mountpoint.clone(),
			ch: ch,
			proto_major: 0,
			proto_minor: 0,
			initialized: false,
			destroyed: false,
		}
	}

	/// Run the session loop that receives, dispatches and replies to kernel requests.
	/// Make sure to run it on a new single threaded scheduler since the I/O in the
	/// session loop can block.
	pub fn run (&mut self) {
		let mut req = Request::new();
		loop {
			match req.read(self) {
				Err(ENOENT) => continue,		// Operation interrupted. Accordingly to FUSE, this is safe to retry
				Err(EINTR) => continue,			// Interrupted system call, retry
				Err(EAGAIN) => continue,		// Explicitly try again
				Err(ENODEV) => break,			// Filesystem was unmounted, quit the loop
				Err(err) => fail!("Lost connection to FUSE device. Error {:i}", err),
				Ok(_) => req.dispatch(self),
			}
		}
	}

	/// Start the session loop in a background task
	pub fn start (self) -> BackgroundSession {
		BackgroundSession::start(self)
	}
}

#[unsafe_destructor]
impl<FS: Filesystem+Send> Drop for Session<FS> {
	fn drop (&mut self) {
		info!("Unmounting {}", self.mountpoint.display());
		self.ch.close();		// Close channel before unnmount to prevent sync unmount deadlock
		Channel::unmount(&self.mountpoint);
	}
}

/// The background session data structure
pub struct BackgroundSession {
	mountpoint: Path,
}

impl BackgroundSession {
	/// Start the session loop of the given session in a background task
	pub fn start<FS: Filesystem+Send> (se: Session<FS>) -> BackgroundSession {
		let mountpoint = se.mountpoint.clone();
		let se = Cell::new(se);
		// The background task is started using a a new single threaded
		// scheduler since I/O in the session loop can block
		do task::spawn_sched(task::SingleThreaded) {
			se.take().run();
		}
		BackgroundSession { mountpoint: mountpoint }
	}

	/// End the session by unmounting the filesystem (which will
	/// eventually end the session loop)
	pub fn unmount (&self) {
		info!("Unmounting {}", self.mountpoint.display());
		Channel::unmount(&self.mountpoint);
	}
}
<commit_msg>Removed use of Cell to move variable into closure<commit_after>//!
//! A session is established with the kernel driver while a userspace
//! filesystem is mounted. The session connects to the kernel driver and
//! runs a loop that receives, dispatches and replies kernel requests.
//!

use std::task;
use std::libc::{EAGAIN, EINTR, ENODEV, ENOENT};
use channel::Channel;
use Filesystem;
use request::Request;

/// The session data structure
pub struct Session<FS> {
	filesystem: FS,
	mountpoint: Path,
	ch: Channel,
	proto_major: uint,
	proto_minor: uint,
	initialized: bool,
	destroyed: bool,
}

impl<FS: Filesystem+Send> Session<FS> {
	/// Mount the given filesystem to the given mountpoint
	pub fn mount (filesystem: FS, mountpoint: &Path, options: &[&[u8]]) -> Session<FS> {
		info!("Mounting {}", mountpoint.display());
		let ch = Channel::mount(mountpoint, options).expect("unable to mount filesystem");
		Session {
			filesystem: filesystem,
			mountpoint: mountpoint.clone(),
			ch: ch,
			proto_major: 0,
			proto_minor: 0,
			initialized: false,
			destroyed: false,
		}
	}

	/// Run the session loop that receives, dispatches and replies to kernel requests.
	/// Make sure to run it on a new single threaded scheduler since the I/O in the
	/// session loop can block.
	pub fn run (&mut self) {
		let mut req = Request::new();
		loop {
			match req.read(self) {
				Err(ENOENT) => continue,		// Operation interrupted. Accordingly to FUSE, this is safe to retry
				Err(EINTR) => continue,			// Interrupted system call, retry
				Err(EAGAIN) => continue,		// Explicitly try again
				Err(ENODEV) => break,			// Filesystem was unmounted, quit the loop
				Err(err) => fail!("Lost connection to FUSE device. Error {:i}", err),
				Ok(_) => req.dispatch(self),
			}
		}
	}

	/// Start the session loop in a background task
	pub fn start (self) -> BackgroundSession {
		BackgroundSession::start(self)
	}
}

#[unsafe_destructor]
impl<FS: Filesystem+Send> Drop for Session<FS> {
	fn drop (&mut self) {
		info!("Unmounting {}", self.mountpoint.display());
		self.ch.close();		// Close channel before unnmount to prevent sync unmount deadlock
		Channel::unmount(&self.mountpoint);
	}
}

/// The background session data structure
pub struct BackgroundSession {
	mountpoint: Path,
}

impl BackgroundSession {
	/// Start the session loop of the given session in a background task
	pub fn start<FS: Filesystem+Send> (se: Session<FS>) -> BackgroundSession {
		let mountpoint = se.mountpoint.clone();
		// The background task is started using a a new single threaded
		// scheduler since I/O in the session loop can block
		do task::spawn_sched(task::SingleThreaded) {
			let mut se = se;
			se.run();
		}
		BackgroundSession { mountpoint: mountpoint }
	}

	/// End the session by unmounting the filesystem (which will
	/// eventually end the session loop)
	pub fn unmount (&self) {
		info!("Unmounting {}", self.mountpoint.display());
		Channel::unmount(&self.mountpoint);
	}
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashSet};

use anyhow::Result;
use log::debug;

use crate::{
    analysis::{cfg, dis},
    aspace::AddressSpace,
    loader::pe::{
        imports,
        imports::{read_best_thunk_data, IMAGE_THUNK_DATA},
        PE,
    },
    RVA, VA,
};

pub mod call_targets;
pub mod control_flow_guard;
pub mod entrypoints;
pub mod exports;
pub mod patterns;
pub mod pointers;
pub mod runtime_functions;
pub mod safeseh;

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum ImportedSymbol {
    Ordinal(u32),
    Name(smol_str::SmolStr),
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Import {
    /// the address of the First Thunk.
    /// that is, the thing that will be referenced by code.
    address: VA,
    dll:     smol_str::SmolStr,
    symbol:  ImportedSymbol,
}

impl std::fmt::Display for Import {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.symbol {
            ImportedSymbol::Ordinal(ord) => write!(f, "{}!#{}", self.dll, ord),
            ImportedSymbol::Name(name) => write!(f, "{}!{}", self.dll, name),
        }
    }
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Thunk {
    /// the address of the function thunk
    address: VA,
    import:  Import,
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum Function {
    Local(VA),
    Thunk(Thunk),
    Import(Import),
}

pub fn get_imports(pe: &PE) -> Result<BTreeMap<VA, Import>> {
    let mut imports: BTreeMap<VA, Import> = Default::default();

    if let Some(import_directory) = imports::get_import_directory(pe)? {
        let base_address = pe.module.address_space.base_address;
        let psize = pe.module.arch.pointer_size();

        for import_descriptor in imports::read_import_descriptors(pe, import_directory) {
            let dll = smol_str::SmolStr::new(pe.module.address_space.relative.read_ascii(import_descriptor.name, 1)?);
            debug!("imports: {}", dll);

            for i in 0.. {
                let oft = base_address + import_descriptor.original_first_thunk + (i * psize) as RVA;
                let ft = base_address + import_descriptor.first_thunk + (i * psize) as RVA;

                if pe.module.read_rva_at_va(ft)? == 0x0 {
                    break;
                }

                let symbol = match read_best_thunk_data(pe, oft, ft)? {
                    IMAGE_THUNK_DATA::Function(name_rva) => {
                        // u16    hint
                        // asciiz name
                        let name = pe.module.address_space.relative.read_ascii(name_rva + 2, 1)?;
                        debug!("imports: {}!{}", dll, name);
                        ImportedSymbol::Name(smol_str::SmolStr::new(name))
                    }
                    IMAGE_THUNK_DATA::Ordinal(ord) => {
                        debug!("imports: {}!#{}", dll, ord);
                        ImportedSymbol::Ordinal(ord)
                    }
                };

                imports.insert(
                    ft,
                    Import {
                        address: ft,
                        dll: dll.clone(),
                        symbol,
                    },
                );
            }
        }
    }

    Ok(imports)
}

pub fn find_thunks(pe: &PE, imports: &BTreeMap<VA, Import>, functions: &HashSet<VA>) -> Result<BTreeMap<VA, Thunk>> {
    let mut thunks: BTreeMap<VA, Thunk> = Default::default();
    let decoder = dis::get_disassembler(&pe.module)?;

    for &function in functions.iter() {
        if let Ok(insn_buf) = pe.module.address_space.read_bytes(function, 0x10) {
            if let Ok(Some(insn)) = decoder.decode(&insn_buf) {
                if insn.mnemonic != zydis::Mnemonic::JMP {
                    continue;
                }

                let op = cfg::get_first_operand(&insn).expect("JMP has no target");

                if let zydis::OperandType::MEMORY = op.ty {
                    // 32-bit
                    if op.mem.base == zydis::Register::NONE
                        && op.mem.index == zydis::Register::NONE
                        && op.mem.scale == 0
                        && op.mem.disp.has_displacement
                    {
                        // the operand is a deref of a memory address.
                        // for example: JMP [0x0]
                        // this means: read the ptr from 0x0, and then jump to it.

                        if op.mem.disp.displacement < 0 {
                            continue;
                        }
                        let ptr: VA = op.mem.disp.displacement as u64;

                        if let Some(import) = imports.get(&ptr) {
                            let thunk = Thunk {
                                address: function,
                                import:  import.clone(),
                            };
                            debug!("thunk: {:#x} -> {}", thunk.address, thunk.import);
                            thunks.insert(thunk.address, thunk);
                        }
                    } else if op.mem.base == zydis::Register::RIP
                            // only valid on x64
                            && op.mem.index == zydis::Register::NONE
                            && op.mem.scale == 0
                            && op.mem.disp.has_displacement
                    {
                        // this is RIP-relative addressing.
                        // it works like a relative immediate,
                        // that is: dst = *(rva + displacement + instruction len)

                        let ptr =
                            match cfg::va_add_signed(function + insn.length as u64, op.mem.disp.displacement as i64) {
                                None => continue,
                                Some(ptr) => ptr,
                            };

                        if let Some(import) = imports.get(&ptr) {
                            let thunk = Thunk {
                                address: function,
                                import:  import.clone(),
                            };
                            debug!("thunk: {:#x} -> {}", thunk.address, thunk.import);
                            thunks.insert(thunk.address, thunk);
                        }
                    }
                }
            }
        }
    }

    Ok(thunks)
}

pub fn find_function_starts(pe: &PE) -> Result<Vec<VA>> {
    let imports = get_imports(pe)?;
    debug!("imports: found {} imports", imports.len());

    let mut function_starts: HashSet<VA> = Default::default();
    function_starts.extend(crate::analysis::pe::entrypoints::find_pe_entrypoint(&pe)?);
    function_starts.extend(crate::analysis::pe::exports::find_pe_exports(&pe)?);
    function_starts.extend(crate::analysis::pe::safeseh::find_pe_safeseh_handlers(&pe)?);
    function_starts.extend(crate::analysis::pe::runtime_functions::find_pe_runtime_functions(&pe)?);
    function_starts.extend(crate::analysis::pe::control_flow_guard::find_pe_cfguard_functions(&pe)?);
    function_starts.extend(crate::analysis::pe::call_targets::find_pe_call_targets(&pe)?);
    function_starts.extend(crate::analysis::pe::patterns::find_function_prologues(&pe)?);
    function_starts.extend(crate::analysis::pe::pointers::find_pe_nonrelocated_executable_pointers(
        &pe,
    )?);

    // TODO: validate that the code looks ok

    let thunks = find_thunks(pe, &imports, &function_starts)?;
    debug!("functions: found {} function candidates", function_starts.len());
    debug!("functions: found {} thunks", thunks.len());

    let function_starts: Vec<_> = function_starts
        .difference(&thunks.keys().cloned().collect())
        .cloned()
        .collect();
    debug!("functions: found {} functions", function_starts.len());

    let mut functions: Vec<Function> = Default::default();
    functions.extend(function_starts.iter().map(|&f| Function::Local(f)));
    functions.extend(thunks.values().cloned().map(Function::Thunk));
    functions.extend(imports.values().cloned().map(Function::Import));
    functions.sort_unstable();

    Ok(function_starts)
}
<commit_msg>pe: add find_functions with structured results<commit_after>use std::collections::{BTreeMap, HashSet};

use anyhow::Result;
use log::debug;

use crate::{
    analysis::{cfg, dis},
    aspace::AddressSpace,
    loader::pe::{
        imports,
        imports::{read_best_thunk_data, IMAGE_THUNK_DATA},
        PE,
    },
    RVA, VA,
};

pub mod call_targets;
pub mod control_flow_guard;
pub mod entrypoints;
pub mod exports;
pub mod patterns;
pub mod pointers;
pub mod runtime_functions;
pub mod safeseh;

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum ImportedSymbol {
    Ordinal(u32),
    Name(smol_str::SmolStr),
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Import {
    /// the address of the First Thunk.
    /// that is, the thing that will be referenced by code.
    address: VA,
    dll:     smol_str::SmolStr,
    symbol:  ImportedSymbol,
}

impl std::fmt::Display for Import {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.symbol {
            ImportedSymbol::Ordinal(ord) => write!(f, "{}!#{}", self.dll, ord),
            ImportedSymbol::Name(name) => write!(f, "{}!{}", self.dll, name),
        }
    }
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Thunk {
    /// the address of the function thunk
    address: VA,
    import:  Import,
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum Function {
    Local(VA),
    Thunk(Thunk),
    Import(Import),
}

pub fn get_imports(pe: &PE) -> Result<BTreeMap<VA, Import>> {
    let mut imports: BTreeMap<VA, Import> = Default::default();

    if let Some(import_directory) = imports::get_import_directory(pe)? {
        let base_address = pe.module.address_space.base_address;
        let psize = pe.module.arch.pointer_size();

        for import_descriptor in imports::read_import_descriptors(pe, import_directory) {
            let dll = smol_str::SmolStr::new(pe.module.address_space.relative.read_ascii(import_descriptor.name, 1)?);
            debug!("imports: {}", dll);

            for i in 0.. {
                let oft = base_address + import_descriptor.original_first_thunk + (i * psize) as RVA;
                let ft = base_address + import_descriptor.first_thunk + (i * psize) as RVA;

                if pe.module.read_rva_at_va(ft)? == 0x0 {
                    break;
                }

                let symbol = match read_best_thunk_data(pe, oft, ft)? {
                    IMAGE_THUNK_DATA::Function(name_rva) => {
                        // u16    hint
                        // asciiz name
                        let name = pe.module.address_space.relative.read_ascii(name_rva + 2, 1)?;
                        debug!("imports: {}!{}", dll, name);
                        ImportedSymbol::Name(smol_str::SmolStr::new(name))
                    }
                    IMAGE_THUNK_DATA::Ordinal(ord) => {
                        debug!("imports: {}!#{}", dll, ord);
                        ImportedSymbol::Ordinal(ord)
                    }
                };

                imports.insert(
                    ft,
                    Import {
                        address: ft,
                        dll: dll.clone(),
                        symbol,
                    },
                );
            }
        }
    }

    Ok(imports)
}

pub fn find_thunks(pe: &PE, imports: &BTreeMap<VA, Import>, functions: &HashSet<VA>) -> Result<BTreeMap<VA, Thunk>> {
    let mut thunks: BTreeMap<VA, Thunk> = Default::default();
    let decoder = dis::get_disassembler(&pe.module)?;

    for &function in functions.iter() {
        if let Ok(insn_buf) = pe.module.address_space.read_bytes(function, 0x10) {
            if let Ok(Some(insn)) = decoder.decode(&insn_buf) {
                if insn.mnemonic != zydis::Mnemonic::JMP {
                    continue;
                }

                let op = cfg::get_first_operand(&insn).expect("JMP has no target");

                if let zydis::OperandType::MEMORY = op.ty {
                    // 32-bit
                    if op.mem.base == zydis::Register::NONE
                        && op.mem.index == zydis::Register::NONE
                        && op.mem.scale == 0
                        && op.mem.disp.has_displacement
                    {
                        // the operand is a deref of a memory address.
                        // for example: JMP [0x0]
                        // this means: read the ptr from 0x0, and then jump to it.

                        if op.mem.disp.displacement < 0 {
                            continue;
                        }
                        let ptr: VA = op.mem.disp.displacement as u64;

                        if let Some(import) = imports.get(&ptr) {
                            let thunk = Thunk {
                                address: function,
                                import:  import.clone(),
                            };
                            debug!("thunk: {:#x} -> {}", thunk.address, thunk.import);
                            thunks.insert(thunk.address, thunk);
                        }
                    } else if op.mem.base == zydis::Register::RIP
                            // only valid on x64
                            && op.mem.index == zydis::Register::NONE
                            && op.mem.scale == 0
                            && op.mem.disp.has_displacement
                    {
                        // this is RIP-relative addressing.
                        // it works like a relative immediate,
                        // that is: dst = *(rva + displacement + instruction len)

                        let ptr =
                            match cfg::va_add_signed(function + insn.length as u64, op.mem.disp.displacement as i64) {
                                None => continue,
                                Some(ptr) => ptr,
                            };

                        if let Some(import) = imports.get(&ptr) {
                            let thunk = Thunk {
                                address: function,
                                import:  import.clone(),
                            };
                            debug!("thunk: {:#x} -> {}", thunk.address, thunk.import);
                            thunks.insert(thunk.address, thunk);
                        }
                    }
                }
            }
        }
    }

    Ok(thunks)
}

pub fn find_functions(pe: &PE) -> Result<Vec<Function>> {
    let imports = get_imports(pe)?;
    debug!("imports: found {} imports", imports.len());

    let mut function_starts: HashSet<VA> = Default::default();
    function_starts.extend(crate::analysis::pe::entrypoints::find_pe_entrypoint(&pe)?);
    function_starts.extend(crate::analysis::pe::exports::find_pe_exports(&pe)?);
    function_starts.extend(crate::analysis::pe::safeseh::find_pe_safeseh_handlers(&pe)?);
    function_starts.extend(crate::analysis::pe::runtime_functions::find_pe_runtime_functions(&pe)?);
    function_starts.extend(crate::analysis::pe::control_flow_guard::find_pe_cfguard_functions(&pe)?);
    function_starts.extend(crate::analysis::pe::call_targets::find_pe_call_targets(&pe)?);
    function_starts.extend(crate::analysis::pe::patterns::find_function_prologues(&pe)?);
    function_starts.extend(crate::analysis::pe::pointers::find_pe_nonrelocated_executable_pointers(
        &pe,
    )?);

    // TODO: validate that the code looks ok

    let thunks = find_thunks(pe, &imports, &function_starts)?;
    debug!("functions: found {} function candidates", function_starts.len());
    debug!("functions: found {} thunks", thunks.len());

    let function_starts: Vec<_> = function_starts
        .difference(&thunks.keys().cloned().collect())
        .cloned()
        .collect();
    debug!("functions: found {} functions", function_starts.len());

    let mut functions: Vec<Function> = Default::default();
    functions.extend(function_starts.iter().map(|&f| Function::Local(f)));
    functions.extend(thunks.values().cloned().map(Function::Thunk));
    functions.extend(imports.values().cloned().map(Function::Import));
    functions.sort_unstable();

    Ok(functions)
}

pub fn find_function_starts(pe: &PE) -> Result<Vec<VA>> {
    Ok(find_functions(pe)?
        .into_iter()
        .filter(|f| matches!(f, Function::Local(_)))
        .map(|f| match f {
            Function::Local(va) => va,
            _ => unreachable!(),
        })
        .collect())
}
<|endoftext|>


<commit_before>extern crate num;

#[allow(dead_code)]
pub struct ZeroPaddedSignal<T> {
  values: Vec<T>,
}

impl<T: num::traits::Num + Clone> ZeroPaddedSignal<T> {
  /**
    Returns the number of initialized values.
  */
  #[allow(dead_code)]
  pub fn size(&self) -> usize {
    self.values.len()
  }

  /**
    Returns the value at the given index, zero if index
    is negative or uninitialized.
  */
  #[allow(dead_code)]
  pub fn get(&self, idx: isize) -> T {
    /* Check for negative index: */
    if idx < 0 {
      /* Negative, return zero: */
      T::zero()
    } else {
      /* Not negative, cast to unsigned: */
      let idx = idx as usize;
      /* Check if the index is initialized: */
      if let Some(v) = self.values.get(idx).cloned() {
        /* It is, return the value: */
        v
      } else {
        /* It's not, return zero: */
        T::zero()
      }
    }
  }

  /**
    Returns a vector of signal values, starting with index start,
    ending with index end.
  */
  #[allow(dead_code)]
  pub fn to_vector(&self, start: isize, end: isize) -> Vec<T> {
    /* Create an empty vector: */
    let mut x: Vec<T> = Vec::new();
    /* Loop through the given range: */
    for i in start..(end+1) {
      /* Add the values to the vector: */
      x.push(self.get(i));
    }
    /* Make the vector immutable: */
    let x = x;
    /* Return the vector: */
    x
  }

  /**
    Creates a new signal by doing Linear Prediction using
    the given coefficients.
  */
  #[allow(dead_code)]
  pub fn linear_prediction(&self, a: Vec<T>) -> ZeroPaddedSignal<T> {
    /* Create a vector for the values: */
    let mut vals: Vec<T> = Vec::new();
    /* Calculate the size of the new signal: */
    let size: usize = self.size() + a.len();
    /* Iterate over the new indices: */
    for i in 0..size {
      let mut val: T = T::zero();
      /* ... and the coefficients: */
      for j in 0..a.len() {
        /* Calculate and typecast the index in the old signal: */
        let idx = (i as isize) - ((j+1) as isize);
        /* Weight the value and add it up: */
        if let Some(v) = a.get(j).cloned() {
          val = val + v*self.get(idx);
        }
      }
      /* Add the value to the vector: */
      vals.push(val);
    }
    /* Create a signal from the vector and return it: */
    let x: ZeroPaddedSignal<T> = ZeroPaddedSignal { values: vals };
    x
  }
}

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

  #[test]
  fn zero_padded_signal() {
    /* Create test signals: */
    let x1: ZeroPaddedSignal<u32> = ZeroPaddedSignal {
      values: vec![42,7,11]
    };
    /* Test size method: */
    assert_eq!( 3, x1.size());
    /* Test get method: */
    assert_eq!( 0, x1.get(  -1));
    assert_eq!(42, x1.get(   0));
    assert_eq!( 7, x1.get(   1));
    assert_eq!(11, x1.get(   2));
    assert_eq!( 0, x1.get(   3));
    assert_eq!( 0, x1.get( 100));
    assert_eq!( 0, x1.get(-100));
    /* Test to_vector method: */
    assert_eq!(vec![0,0,0,42,7,11,0], x1.to_vector(-3,3));
    assert_eq!(0, x1.to_vector(3,-3).len());
  }

  #[test]
  fn linear_prediction() {
    /* Create test signals: */
    let x2: ZeroPaddedSignal<f64> = ZeroPaddedSignal {
      values: vec![1.,1.,1.,1.,1.,1.]
    };
    /* Test linear_prediction method: */
    assert_eq_floatvec!(
      vec![0.,0.8,0.8,0.8,0.7,0.7,0.7,-0.1,-0.1,-0.1],
      x2.linear_prediction(vec![0.8,0.,0.,-0.1]).values,
      1e-15
    );
  }
}
<commit_msg>signals.rs: Added ZeroPaddedSignal::set(...)<commit_after>extern crate num;

#[allow(dead_code)]
pub struct ZeroPaddedSignal<T> {
  values: Vec<T>,
}

impl<T: num::traits::Num + Clone> ZeroPaddedSignal<T> {
  /**
    Returns the number of initialized values.
  */
  #[allow(dead_code)]
  pub fn size(&self) -> usize {
    self.values.len()
  }

  /**
    Returns the value at the given index, zero if index
    is negative or uninitialized.
  */
  #[allow(dead_code)]
  pub fn get(&self, idx: isize) -> T {
    /* Check for negative index: */
    if idx < 0 {
      /* Negative, return zero: */
      T::zero()
    } else {
      /* Not negative, cast to unsigned: */
      let idx = idx as usize;
      /* Check if the index is initialized: */
      if let Some(v) = self.values.get(idx).cloned() {
        /* It is, return the value: */
        v
      } else {
        /* It's not, return zero: */
        T::zero()
      }
    }
  }

  /**
    Returns a vector of signal values, starting with index start,
    ending with index end.
  */
  #[allow(dead_code)]
  pub fn to_vector(&self, start: isize, end: isize) -> Vec<T> {
    /* Create an empty vector: */
    let mut x: Vec<T> = Vec::new();
    /* Loop through the given range: */
    for i in start..(end+1) {
      /* Add the values to the vector: */
      x.push(self.get(i));
    }
    /* Make the vector immutable: */
    let x = x;
    /* Return the vector: */
    x
  }

  /**
    Creates a new signal by doing Linear Prediction using
    the given coefficients.
  */
  #[allow(dead_code)]
  pub fn linear_prediction(&self, a: Vec<T>) -> ZeroPaddedSignal<T> {
    /* Create a vector for the values: */
    let mut vals: Vec<T> = Vec::new();
    /* Calculate the size of the new signal: */
    let size: usize = self.size() + a.len();
    /* Iterate over the new indices: */
    for i in 0..size {
      let mut val: T = T::zero();
      /* ... and the coefficients: */
      for j in 0..a.len() {
        /* Calculate and typecast the index in the old signal: */
        let idx = (i as isize) - ((j+1) as isize);
        /* Weight the value and add it up: */
        if let Some(v) = a.get(j).cloned() {
          val = val + v*self.get(idx);
        }
      }
      /* Add the value to the vector: */
      vals.push(val);
    }
    /* Create a signal from the vector and return it: */
    let x: ZeroPaddedSignal<T> = ZeroPaddedSignal { values: vals };
    x
  }

  /**
    Sets the signal value at the given index. If there's
    a gap between the last initialized index and the given
    one, the values between are initialized with zero.
  */
  #[allow(dead_code)]
  pub fn set(&mut self, idx: usize, val: T) {
    /* Initialize the gap values with zero, if necessary: */
    for _ in self.size()..(idx+1) {
      self.values.push(T::zero());
    }
    /* Set the new value: */
    self.values[idx] = val;
  }
}

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

  #[test]
  fn zero_padded_signal() {
    /* Create test signals: */
    let mut x1: ZeroPaddedSignal<u32> = ZeroPaddedSignal {
      values: vec![42,7,11]
    };
    /* Test size method: */
    assert_eq!( 3, x1.size());
    /* Test get method: */
    assert_eq!( 0, x1.get(  -1));
    assert_eq!(42, x1.get(   0));
    assert_eq!( 7, x1.get(   1));
    assert_eq!(11, x1.get(   2));
    assert_eq!( 0, x1.get(   3));
    assert_eq!( 0, x1.get( 100));
    assert_eq!( 0, x1.get(-100));
    /* Test to_vector method: */
    assert_eq!(vec![0,0,0,42,7,11,0], x1.to_vector(-3,3));
    assert_eq!(0, x1.to_vector(3,-3).len());
    /* Test set method: */
    x1.set(0, 5);
    assert_eq!(vec![5,7,11], x1.values);
    x1.set(5, 12);
    assert_eq!(vec![5,7,11,0,0,12], x1.values);
    x1.set(6,100);
    assert_eq!(vec![5,7,11,0,0,12,100], x1.values);
  }

  #[test]
  fn linear_prediction() {
    /* Create test signals: */
    let x2: ZeroPaddedSignal<f64> = ZeroPaddedSignal {
      values: vec![1.,1.,1.,1.,1.,1.]
    };
    /* Test linear_prediction method: */
    assert_eq_floatvec!(
      vec![0.,0.8,0.8,0.8,0.7,0.7,0.7,-0.1,-0.1,-0.1],
      x2.linear_prediction(vec![0.8,0.,0.,-0.1]).values,
      1e-15
    );
  }
}
<|endoftext|>