<commit_before>use base64;
use hex::FromHex;
use serde_json as json;
use std::collections::HashMap;
use std::fmt::Debug;
use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::{self, BufReader};
use std::path::Path;
use std::process::{Command, Output};
use std::str;
use tar::Archive;

use datatype::{EcuCustom, EcuVersion, Error, InstallCode, InstallOutcome, TufImage,
               TufMeta, Url, Util};
use http::{Client, Response};
use pacman::Credentials;

const NEW_PACKAGE: &'static str = "/tmp/sota-package";
const BOOT_BRANCH: &'static str = "/usr/share/sota/branchname";
const REMOTE_PATH: &'static str = "/etc/ostree/remotes.d/sota-remote.conf";

/// Struct for OSTree related functions.
pub struct Ostree;

impl Ostree {
    fn run<S: AsRef<OsStr> + Debug>(args: &[S]) -> Result<Output, Error> {
        debug!("running `ostree` command with args: {:?}", args);
        Command::new("ostree")
            .args(args)
            .env("OSTREE_REPO", "/sysroot/ostree/repo")
            .env("OSTREE_BOOT_PARTITION", "/boot")
            .output()
            .map_err(|err| Error::OSTree(err.to_string()))
            .and_then(|output| {
                if output.status.success() {
                    Ok(output)
                } else {
                    let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
                    let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
                    Err(Error::OSTree(format!("stdout: {}\nstderr: {}", stdout, stderr)))
                }
            })
    }

    fn hash(commit: &str) -> Result<String, Error> {
        let data = Vec::from_hex(commit)?;
        Ok(base64::encode(&data).replace('/', "_").trim_right_matches('=').into())
    }
}

/// Details of a remote `OSTree` package.
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Default)]
#[allow(non_snake_case)]
pub struct OstreePackage {
    #[serde(default)]
    pub ecu_serial:  String,
    pub refName:     String,
    pub commit:      String,
    pub description: String,
    pub pullUri:     String,
}

impl OstreePackage {
    pub fn new(ecu_serial: String, refname: String, commit: String, desc: String, treehub: &Url) -> Self {
        OstreePackage {
            ecu_serial:  ecu_serial,
            refName:     refname,
            commit:      commit,
            description: desc,
            pullUri:     format!("{}", treehub),
        }
    }

    /// Convert the current `OstreePackage` into an `EcuVersion`.
    pub fn into_version(self, custom: Option<EcuCustom>) -> EcuVersion {
        let mut hashes = HashMap::new();
        hashes.insert("sha256".to_string(), self.commit);

        EcuVersion {
            attacks_detected: "".to_string(),
            custom: custom,
            ecu_serial: self.ecu_serial,
            installed_image: TufImage {
                filepath: self.refName,
                fileinfo: TufMeta {
                    length: 0,
                    hashes: hashes,
                    custom: None
                }
            },
            previous_timeserver_time: "1970-01-01T00:00:00Z".to_string(),
            timeserver_time: "1970-01-01T00:00:00Z".to_string(),
        }
    }

    /// Install this package using the `ostree` command.
    pub fn install(&self, creds: &Credentials) -> Result<InstallOutcome, Error> {
        debug!("installing ostree commit {}", self.commit);
        let from = Self::get_latest(&self.ecu_serial)?;
        if from.commit == self.commit {
            return Ok(InstallOutcome::new(InstallCode::ALREADY_PROCESSED, "".into(), "".into()));
        }
        self.get_delta(creds.client, &self.pullUri, &from.commit)
            .and_then(|dir| Ostree::run(&["static-delta", "apply-offline", &dir]))
            .or_else(|_| self.pull_commit("sota-remote", creds))
            .map(|_| ())?;

        let output = Ostree::run(&["admin", "deploy", "--karg-proc-cmdline", &self.commit])?;
        let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
        let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
        if output.status.success() {
            Util::write_file(NEW_PACKAGE, &json::to_vec(self)?).unwrap_or_else(|err| error!("couldn't save package info: {}", err));
            Ok(InstallOutcome::new(InstallCode::OK, stdout, stderr))
        } else {
            Ok(InstallOutcome::new(InstallCode::INSTALL_FAILED, stdout, stderr))
        }
    }

    /// Get the latest OSTree package (including any new updates pending a reboot).
    pub fn get_latest(serial: &str) -> Result<OstreePackage, Error> {
        if Path::new(NEW_PACKAGE).exists() {
            trace!("getting ostree package from `{}`", NEW_PACKAGE);
            Ok(json::from_reader(BufReader::new(File::open(NEW_PACKAGE)?))?)
        } else if Path::new(BOOT_BRANCH).exists() {
            trace!("getting ostree branch from `{}`", BOOT_BRANCH);
            Ok(Self::get_current(serial, str::from_utf8(&Util::read_file(BOOT_BRANCH)?)?)?)
        } else {
            trace!("unknown ostree branch");
            Ok(Self::get_current(serial, "<unknown>")?)
        }
    }

    /// Get the current OSTree package with `ostree admin status`.
    pub fn get_current(serial: &str, branch: &str) -> Result<OstreePackage, Error> {
        Ostree::run(&["admin", "status"])
            .and_then(|output| OstreeBranch::parse(serial, branch, str::from_utf8(&output.stdout)?))
            .and_then(|branches| {
                branches.into_iter()
                    .filter(|branch| branch.current)
                    .map(|branch| branch.package)
                    .nth(0)
                    .ok_or_else(|| Error::OSTree("current branch unknown".to_string()))
            })
    }

    /// Extract a static delta between two commits (if it exists) and return the path.
    pub fn get_delta(&self, client: &Client, server: &str, current_commit: &str) -> Result<String, Error> {
        debug!("getting a static delta from {}", current_commit);
        let (current, next)  = (Ostree::hash(current_commit)?, Ostree::hash(&self.commit)?);
        let (prefix, suffix) = current.split_at(2);
        let url  = format!("{}/deltas/{}/{}-{}/apply-offline.tar", server, prefix, suffix, next);
        let data = match client.get(url.parse()?, None).recv().expect("get_delta") {
            Response::Success(data) => Ok(data),
            Response::Failed(data)  => Err(data.into()),
            Response::Error(err)    => Err(err)
        }?;

        let tar = format!("/tmp/sota-delta-{}-{}.tar", current_commit, self.commit);
        let mut file = File::create(&tar)?;
        let _ = io::copy(&mut &*data.body, &mut file)?;
        Archive::new(File::open(&tar)?).unpack("/tmp/sota-delta")?;
        Ok(format!("/tmp/sota-delta/{}/{}-{}", prefix, suffix, next))
    }

    /// Pull a commit from a remote repository with `ostree pull`.
    pub fn pull_commit(&self, remote: &str, creds: &Credentials) -> Result<Output, Error> {
        debug!("pulling from ostree remote: {}", remote);
        if ! Path::new(REMOTE_PATH).exists() {
            let _ = self.add_remote(remote, creds)?;
        }

        let mut args = vec!["pull".into(), remote.into()];
        if let Some(token) = creds.token {
            args.push(format!("--http-header='Authorization=Bearer {}'", token));
        }
        args.push(self.commit.clone());
        Ostree::run(&args)
    }

    /// Add a remote repository with `ostree remote add`.
    pub fn add_remote(&self, remote: &str, creds: &Credentials) -> Result<Output, Error> {
        debug!("adding ostree remote: {}", remote);
        if Path::new(REMOTE_PATH).exists() {
            fs::remove_file(REMOTE_PATH)?;
        }

        let mut args = vec!["remote".into(), "add".into(), "--no-gpg-verify".into()];
        if let Some(ca) = creds.ca_file {
            args.push(format!("--set=tls-ca-path={}", ca));
        }
        if let Some(pkey) = creds.pkey_file {
            args.push(format!("--set=tls-client-cert-path={}", pkey));
            args.push(format!("--set=tls-client-key-path={}", pkey));
        }
        args.push(remote.into());
        args.push(self.pullUri.clone());
        Ostree::run(&args)
    }
}

#[derive(Debug)]
struct OstreeBranch {
    current: bool,
    os_name: String,
    package: OstreePackage,
}

impl OstreeBranch {
    /// Parse the output from `ostree admin status`
    fn parse(ecu_serial: &str, branch_name: &str, stdout: &str) -> Result<Vec<OstreeBranch>, Error> {
        stdout.lines()
            .map(str::trim)
            .filter(|line| !line.is_empty())
            .collect::<Vec<_>>()
            .chunks(2)
            .map(|branch| {
                let first  = branch[0].split(' ').collect::<Vec<_>>();
                let second = branch[1].split(' ').collect::<Vec<_>>();

                let (current, os_name, commit_name) = match first.len() {
                    2 => (false, first[0], first[1]),
                    3 if first[0].trim() == "*" => (true, first[1], first[2]),
                    _ => return Err(Error::Parse(format!("couldn't parse branch: {:?}", first)))
                };
                let commit = commit_name.split('.').collect::<Vec<_>>()[0];
                let desc = match second.len() {
                    3 if second[0].trim() == "origin" && second[1].trim() == "refspec:" => second[2],
                    _ => return Err(Error::Parse(format!("couldn't parse branch: {:?}", second)))
                };

                Ok(OstreeBranch {
                    current: current,
                    os_name: os_name.into(),
                    package: OstreePackage {
                        ecu_serial:  ecu_serial.into(),
                        refName:     format!("{}-{}", branch_name, commit),
                        commit:      commit.into(),
                        description: desc.into(),
                        pullUri:     "".into(),
                    },
                })
            })
            .collect()
    }
}

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

    const OSTREE_ADMIN_STATUS: &'static str = r#"
          gnome-ostree 67e382b11d213a402a5313e61cbc69dfd5ab93cb07.0
            origin refspec: gnome-ostree/buildmaster/x86_64-runtime
        * gnome-ostree ce19c41036cc45e49b0cecf6b157523c2105c4de1c.0
            origin refspec: osname:gnome-ostree/buildmaster/x86_64-runtime
        "#;

    #[test]
    fn test_parse_branches() {
        let branches = OstreeBranch::parse("test-serial".into(), "<branch>", OSTREE_ADMIN_STATUS).expect("couldn't parse branches");
        assert_eq!(branches.len(), 2);
        assert_eq!(branches[0].current, false);
        assert_eq!(branches[0].os_name, "gnome-ostree");
        assert_eq!(branches[0].package.commit, "67e382b11d213a402a5313e61cbc69dfd5ab93cb07");
        assert_eq!(branches[0].package.refName, "<branch>-67e382b11d213a402a5313e61cbc69dfd5ab93cb07");
        assert_eq!(branches[0].package.description, "gnome-ostree/buildmaster/x86_64-runtime");
        assert_eq!(branches[1].current, true);
        assert_eq!(branches[1].package.refName, "<branch>-ce19c41036cc45e49b0cecf6b157523c2105c4de1c");
    }
}
<commit_msg>Only ostree remote add if the remote already doesn't exist.<commit_after>use base64;
use hex::FromHex;
use serde_json as json;
use std::collections::HashMap;
use std::fmt::Debug;
use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::{self, BufReader};
use std::path::Path;
use std::process::{Command, Output};
use std::str;
use tar::Archive;

use datatype::{EcuCustom, EcuVersion, Error, InstallCode, InstallOutcome, TufImage,
               TufMeta, Url, Util};
use http::{Client, Response};
use pacman::Credentials;

const NEW_PACKAGE: &'static str = "/tmp/sota-package";
const BOOT_BRANCH: &'static str = "/usr/share/sota/branchname";
const REMOTE_PATH: &'static str = "/etc/ostree/remotes.d/sota-remote.conf";

/// Struct for OSTree related functions.
pub struct Ostree;

impl Ostree {
    fn run<S: AsRef<OsStr> + Debug>(args: &[S]) -> Result<Output, Error> {
        debug!("running `ostree` command with args: {:?}", args);
        Command::new("ostree")
            .args(args)
            .env("OSTREE_REPO", "/sysroot/ostree/repo")
            .env("OSTREE_BOOT_PARTITION", "/boot")
            .output()
            .map_err(|err| Error::OSTree(err.to_string()))
            .and_then(|output| {
                if output.status.success() {
                    Ok(output)
                } else {
                    let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
                    let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
                    Err(Error::OSTree(format!("stdout: {}\nstderr: {}", stdout, stderr)))
                }
            })
    }

    fn hash(commit: &str) -> Result<String, Error> {
        let data = Vec::from_hex(commit)?;
        Ok(base64::encode(&data).replace('/', "_").trim_right_matches('=').into())
    }
}

/// Details of a remote `OSTree` package.
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Default)]
#[allow(non_snake_case)]
pub struct OstreePackage {
    #[serde(default)]
    pub ecu_serial:  String,
    pub refName:     String,
    pub commit:      String,
    pub description: String,
    pub pullUri:     String,
}

impl OstreePackage {
    pub fn new(ecu_serial: String, refname: String, commit: String, desc: String, treehub: &Url) -> Self {
        OstreePackage {
            ecu_serial:  ecu_serial,
            refName:     refname,
            commit:      commit,
            description: desc,
            pullUri:     format!("{}", treehub),
        }
    }

    /// Convert the current `OstreePackage` into an `EcuVersion`.
    pub fn into_version(self, custom: Option<EcuCustom>) -> EcuVersion {
        let mut hashes = HashMap::new();
        hashes.insert("sha256".to_string(), self.commit);

        EcuVersion {
            attacks_detected: "".to_string(),
            custom: custom,
            ecu_serial: self.ecu_serial,
            installed_image: TufImage {
                filepath: self.refName,
                fileinfo: TufMeta {
                    length: 0,
                    hashes: hashes,
                    custom: None
                }
            },
            previous_timeserver_time: "1970-01-01T00:00:00Z".to_string(),
            timeserver_time: "1970-01-01T00:00:00Z".to_string(),
        }
    }

    /// Install this package using the `ostree` command.
    pub fn install(&self, creds: &Credentials) -> Result<InstallOutcome, Error> {
        debug!("installing ostree commit {}", self.commit);
        let from = Self::get_latest(&self.ecu_serial)?;
        if from.commit == self.commit {
            return Ok(InstallOutcome::new(InstallCode::ALREADY_PROCESSED, "".into(), "".into()));
        }
        self.get_delta(creds.client, &self.pullUri, &from.commit)
            .and_then(|dir| Ostree::run(&["static-delta", "apply-offline", &dir]))
            .or_else(|_| self.pull_commit("sota-remote", creds))
            .map(|_| ())?;

        let output = Ostree::run(&["admin", "deploy", "--karg-proc-cmdline", &self.commit])?;
        let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
        let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
        if output.status.success() {
            Util::write_file(NEW_PACKAGE, &json::to_vec(self)?).unwrap_or_else(|err| error!("couldn't save package info: {}", err));
            Ok(InstallOutcome::new(InstallCode::OK, stdout, stderr))
        } else {
            Ok(InstallOutcome::new(InstallCode::INSTALL_FAILED, stdout, stderr))
        }
    }

    /// Get the latest OSTree package (including any new updates pending a reboot).
    pub fn get_latest(serial: &str) -> Result<OstreePackage, Error> {
        if Path::new(NEW_PACKAGE).exists() {
            trace!("getting ostree package from `{}`", NEW_PACKAGE);
            Ok(json::from_reader(BufReader::new(File::open(NEW_PACKAGE)?))?)
        } else if Path::new(BOOT_BRANCH).exists() {
            trace!("getting ostree branch from `{}`", BOOT_BRANCH);
            Ok(Self::get_current(serial, str::from_utf8(&Util::read_file(BOOT_BRANCH)?)?)?)
        } else {
            trace!("unknown ostree branch");
            Ok(Self::get_current(serial, "<unknown>")?)
        }
    }

    /// Get the current OSTree package with `ostree admin status`.
    pub fn get_current(serial: &str, branch: &str) -> Result<OstreePackage, Error> {
        Ostree::run(&["admin", "status"])
            .and_then(|output| OstreeBranch::parse(serial, branch, str::from_utf8(&output.stdout)?))
            .and_then(|branches| {
                branches.into_iter()
                    .filter(|branch| branch.current)
                    .map(|branch| branch.package)
                    .nth(0)
                    .ok_or_else(|| Error::OSTree("current branch unknown".to_string()))
            })
    }

    /// Extract a static delta between two commits (if it exists) and return the path.
    pub fn get_delta(&self, client: &Client, server: &str, current_commit: &str) -> Result<String, Error> {
        debug!("getting a static delta from {}", current_commit);
        let (current, next)  = (Ostree::hash(current_commit)?, Ostree::hash(&self.commit)?);
        let (prefix, suffix) = current.split_at(2);
        let url  = format!("{}/deltas/{}/{}-{}/apply-offline.tar", server, prefix, suffix, next);
        let data = match client.get(url.parse()?, None).recv().expect("get_delta") {
            Response::Success(data) => Ok(data),
            Response::Failed(data)  => Err(data.into()),
            Response::Error(err)    => Err(err)
        }?;

        let tar = format!("/tmp/sota-delta-{}-{}.tar", current_commit, self.commit);
        let mut file = File::create(&tar)?;
        let _ = io::copy(&mut &*data.body, &mut file)?;
        Archive::new(File::open(&tar)?).unpack("/tmp/sota-delta")?;
        Ok(format!("/tmp/sota-delta/{}/{}-{}", prefix, suffix, next))
    }

    /// Pull a commit from a remote repository with `ostree pull`.
    pub fn pull_commit(&self, remote: &str, creds: &Credentials) -> Result<Output, Error> {
        debug!("pulling from ostree remote: {}", remote);
        if ! Path::new(REMOTE_PATH).exists() {
            let output = Ostree::run(&["remote", "list"])?;
            let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
            if stdout.contains(remote) {
                debug!("`ostree remote list`'s stdout, '{}', contains '{}'", stdout, remote);
            } else {
                let _ = self.add_remote(remote, creds)?;
            }
        }

        let mut args = vec!["pull".into(), remote.into()];
        if let Some(token) = creds.token {
            args.push(format!("--http-header='Authorization=Bearer {}'", token));
        }
        args.push(self.commit.clone());
        Ostree::run(&args)
    }

    /// Add a remote repository with `ostree remote add`.
    pub fn add_remote(&self, remote: &str, creds: &Credentials) -> Result<Output, Error> {
        debug!("adding ostree remote: {}", remote);
        if Path::new(REMOTE_PATH).exists() {
            fs::remove_file(REMOTE_PATH)?;
        }

        let mut args = vec!["remote".into(), "add".into(), "--no-gpg-verify".into()];
        if let Some(ca) = creds.ca_file {
            args.push(format!("--set=tls-ca-path={}", ca));
        }
        if let Some(pkey) = creds.pkey_file {
            args.push(format!("--set=tls-client-cert-path={}", pkey));
            args.push(format!("--set=tls-client-key-path={}", pkey));
        }
        args.push(remote.into());
        args.push(self.pullUri.clone());
        Ostree::run(&args)
    }
}

#[derive(Debug)]
struct OstreeBranch {
    current: bool,
    os_name: String,
    package: OstreePackage,
}

impl OstreeBranch {
    /// Parse the output from `ostree admin status`
    fn parse(ecu_serial: &str, branch_name: &str, stdout: &str) -> Result<Vec<OstreeBranch>, Error> {
        stdout.lines()
            .map(str::trim)
            .filter(|line| !line.is_empty())
            .collect::<Vec<_>>()
            .chunks(2)
            .map(|branch| {
                let first  = branch[0].split(' ').collect::<Vec<_>>();
                let second = branch[1].split(' ').collect::<Vec<_>>();

                let (current, os_name, commit_name) = match first.len() {
                    2 => (false, first[0], first[1]),
                    3 if first[0].trim() == "*" => (true, first[1], first[2]),
                    _ => return Err(Error::Parse(format!("couldn't parse branch: {:?}", first)))
                };
                let commit = commit_name.split('.').collect::<Vec<_>>()[0];
                let desc = match second.len() {
                    3 if second[0].trim() == "origin" && second[1].trim() == "refspec:" => second[2],
                    _ => return Err(Error::Parse(format!("couldn't parse branch: {:?}", second)))
                };

                Ok(OstreeBranch {
                    current: current,
                    os_name: os_name.into(),
                    package: OstreePackage {
                        ecu_serial:  ecu_serial.into(),
                        refName:     format!("{}-{}", branch_name, commit),
                        commit:      commit.into(),
                        description: desc.into(),
                        pullUri:     "".into(),
                    },
                })
            })
            .collect()
    }
}

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

    const OSTREE_ADMIN_STATUS: &'static str = r#"
          gnome-ostree 67e382b11d213a402a5313e61cbc69dfd5ab93cb07.0
            origin refspec: gnome-ostree/buildmaster/x86_64-runtime
        * gnome-ostree ce19c41036cc45e49b0cecf6b157523c2105c4de1c.0
            origin refspec: osname:gnome-ostree/buildmaster/x86_64-runtime
        "#;

    #[test]
    fn test_parse_branches() {
        let branches = OstreeBranch::parse("test-serial".into(), "<branch>", OSTREE_ADMIN_STATUS).expect("couldn't parse branches");
        assert_eq!(branches.len(), 2);
        assert_eq!(branches[0].current, false);
        assert_eq!(branches[0].os_name, "gnome-ostree");
        assert_eq!(branches[0].package.commit, "67e382b11d213a402a5313e61cbc69dfd5ab93cb07");
        assert_eq!(branches[0].package.refName, "<branch>-67e382b11d213a402a5313e61cbc69dfd5ab93cb07");
        assert_eq!(branches[0].package.description, "gnome-ostree/buildmaster/x86_64-runtime");
        assert_eq!(branches[1].current, true);
        assert_eq!(branches[1].package.refName, "<branch>-ce19c41036cc45e49b0cecf6b157523c2105c4de1c");
    }
}
<|endoftext|>


<commit_before>/**
 * Rust Tasks and Communication Tutorial - 2.2 Backgrounding computations: Futures
 * http://static.rust-lang.org/doc/master/tutorial-tasks.html#backgrounding-computations-futures
 *
 * @license MIT license <http://www.opensource.org/licenses/mit-license.php>
 */
extern mod extra;
extern mod fibonacci(name = "fibonacci", vers = "1.0", author = "eliovir");
use std::vec;

fn partial_sum(start: uint) -> f64 {
	// println(fmt!("partial_sum(%u)", start));
	let mut local_sum = 0f64;
	for num in range(start*100000, (start+1)*100000) {
		local_sum += (num as f64 + 1.0).pow(&-2.0);
	}
	local_sum
}
#[test]
fn test_partial_sum_5() {
	let param = 5u;
	let expected = 0.00000033f64;
	let actual = partial_sum(param);
	assert_approx_eq!(expected, actual);
}
#[allow(dead_code)]
fn main() {
	let n = 40;
	println("Setting spawn");
	/*
	 * Note that the future needs to be mutable so that it can save the result for next time get is called.
	 */
	let mut delayed_fib = extra::future::Future::spawn(proc() fibonacci::fibonacci(n));
	println("Doing something else");
	println!("fib({:d}) = {}", n, delayed_fib.get());

	/*
	 * The workload will be distributed on the available cores.
	 */
	let mut futures = vec::from_fn(1000, |ind| do extra::future::Future::spawn { partial_sum(ind) });

	let mut final_res = 0f64;
	for ft in futures.mut_iter() {
		final_res += ft.get();
	}
	println!("π^2/6 is not far from : {}", final_res);
}
<commit_msg>Use of #[cfg(not(test))]<commit_after>/**
 * Rust Tasks and Communication Tutorial - 2.2 Backgrounding computations: Futures
 * http://static.rust-lang.org/doc/master/tutorial-tasks.html#backgrounding-computations-futures
 *
 * @license MIT license <http://www.opensource.org/licenses/mit-license.php>
 */
extern mod extra;
extern mod fibonacci(name = "fibonacci", vers = "1.0", author = "eliovir");
#[cfg(not(test))]
use std::vec;

fn partial_sum(start: uint) -> f64 {
	// println(fmt!("partial_sum(%u)", start));
	let mut local_sum = 0f64;
	for num in range(start*100000, (start+1)*100000) {
		local_sum += (num as f64 + 1.0).pow(&-2.0);
	}
	local_sum
}
#[test]
fn test_partial_sum_5() {
	let param = 5u;
	let expected = 0.00000033f64;
	let actual = partial_sum(param);
	assert_approx_eq!(expected, actual);
}
#[cfg(not(test))]
fn main() {
	let n = 40;
	println("Setting spawn");
	/*
	 * Note that the future needs to be mutable so that it can save the result for next time get is called.
	 */
	let mut delayed_fib = extra::future::Future::spawn(proc() fibonacci::fibonacci(n));
	println("Doing something else");
	println!("fib({:d}) = {}", n, delayed_fib.get());

	/*
	 * The workload will be distributed on the available cores.
	 */
	let mut futures = vec::from_fn(1000, |ind| do extra::future::Future::spawn { partial_sum(ind) });

	let mut final_res = 0f64;
	for ft in futures.mut_iter() {
		final_res += ft.get();
	}
	println!("π^2/6 is not far from : {}", final_res);
}
<|endoftext|>


<commit_before>/// search for instructions with no predecessors (code xrefs to or fallthrough to).
/// assume these are the start of functions.
/// example: RUNTIME_FUNCTION always references code, but not always the start of a function.
/// catch that case here.
///
/// this should be the final analyzer pass.
use std::collections::HashSet;

use log::{debug};
use failure::{Error};

use super::super::arch::{RVA};
use super::super::workspace::Workspace;
use super::{Analyzer};

pub struct OrphanFunctionAnalyzer {}

impl OrphanFunctionAnalyzer {
    pub fn new() -> OrphanFunctionAnalyzer {
        OrphanFunctionAnalyzer {}
    }
}

impl Analyzer for OrphanFunctionAnalyzer {
    fn get_name(&self) -> String {
        "orphan function analyzer".to_string()
    }

    /// ```
    /// use lancelot::rsrc::*;
    /// use lancelot::arch::*;
    /// use lancelot::analysis::Analyzer;
    /// use lancelot::workspace::Workspace;
    /// use lancelot::analysis::pe::RuntimeFunctionAnalyzer;
    /// use lancelot::analysis::OrphanFunctionAnalyzer;
    ///
    /// let mut ws = Workspace::from_bytes("k32.dll", &get_buf(Rsrc::K32))
    ///    .disable_analysis()
    ///    .load().unwrap();
    ///
    /// RuntimeFunctionAnalyzer::new().analyze(&mut ws).unwrap();
    /// OrphanFunctionAnalyzer::new().analyze(&mut ws).unwrap();
    ///
    /// // this function is the impl of the export `GetApplicationRestartSettingsWorker`.
    /// // the exported symbol simply JMP to this function.
    /// // there is a RUNTIME_FUNCTION for this func, but not for the export.
    /// assert!(ws.get_meta(0x645A4).unwrap().is_insn());
    /// assert!(ws.get_functions().find(|&&rva| rva == 0x645A4).is_some());
    ///
    /// //  .text:0000000180001010     mov     r11, rsp
    /// //  .text:0000000180001013     sub     rsp, 48h
    /// //  .text:0000000180001017     mov     rax, [rsp+48h+ContextPointers]
    /// assert!(ws.get_functions().find(|&&rva| rva == 0x1010).is_some());
    /// assert!(ws.get_functions().find(|&&rva| rva == 0x1013).is_none());
    ///
    /// // this is a function referenced only as a function argument.
    /// assert!(ws.get_functions().find(|&&rva| rva == 0x1aa0).is_some());
    /// ```
    fn analyze(&self, ws: &mut Workspace) -> Result<(), Error> {
        let existing_functions: HashSet<&RVA> = ws.get_functions().collect();
        let mut orphans: Vec<RVA> = vec![];

        for (i, section) in ws.module.sections.iter().enumerate() {
            orphans.extend(ws.analysis.flow.meta[i].iter()
                .enumerate()
                .filter(|(_, meta)| meta.is_insn())
                .filter(|(_, meta)| !meta.has_xrefs_to())
                .filter(|(_, meta)| !meta.does_other_fallthrough_to())
                .map(|(j, _)| section.addr + RVA::from(j))
                .filter(|rva| !existing_functions.contains(rva)))
        }

        for rva in orphans.iter() {
            debug!("orphan function: {:#x}", rva);
            ws.make_function(*rva)?;
            ws.analyze()?;
        }

        Ok(())
    }
}
<commit_msg>analysis/orphans: tests passing again<commit_after>/// search for instructions with no predecessors (code xrefs to or fallthrough to).
/// assume these are the start of functions.
/// example: RUNTIME_FUNCTION always references code, but not always the start of a function.
/// catch that case here.
///
/// this should be the final analyzer pass.
use std::collections::HashSet;

use log::{debug};
use failure::{Error};

use super::super::arch::{RVA};
use super::super::workspace::Workspace;
use super::{Analyzer};

pub struct OrphanFunctionAnalyzer {}

impl OrphanFunctionAnalyzer {
    pub fn new() -> OrphanFunctionAnalyzer {
        OrphanFunctionAnalyzer {}
    }
}

impl Analyzer for OrphanFunctionAnalyzer {
    fn get_name(&self) -> String {
        "orphan function analyzer".to_string()
    }

    /// ```
    /// use lancelot::rsrc::*;
    /// use lancelot::arch::*;
    /// use lancelot::analysis::Analyzer;
    /// use lancelot::workspace::Workspace;
    /// use lancelot::analysis::pe::RuntimeFunctionAnalyzer;
    /// use lancelot::analysis::OrphanFunctionAnalyzer;
    ///
    /// let mut ws = Workspace::from_bytes("k32.dll", &get_buf(Rsrc::K32))
    ///    .disable_analysis()
    ///    .load().unwrap();
    ///
    /// RuntimeFunctionAnalyzer::new().analyze(&mut ws).unwrap();
    /// OrphanFunctionAnalyzer::new().analyze(&mut ws).unwrap();
    ///
    /// // this function is the impl of the export `GetApplicationRestartSettingsWorker`.
    /// // the exported symbol simply JMP to this function.
    /// // there is a RUNTIME_FUNCTION for this func, but not for the export.
    /// assert!(ws.get_meta(RVA(0x645A4)).unwrap().is_insn());
    /// assert!(ws.get_functions().find(|&&rva| rva == RVA(0x645A4)).is_some());
    ///
    /// //  .text:0000000180001010     mov     r11, rsp
    /// //  .text:0000000180001013     sub     rsp, 48h
    /// //  .text:0000000180001017     mov     rax, [rsp+48h+ContextPointers]
    /// assert!(ws.get_functions().find(|&&rva| rva == RVA(0x1010)).is_some());
    /// assert!(ws.get_functions().find(|&&rva| rva == RVA(0x1013)).is_none());
    ///
    /// // this is a function referenced only as a function argument.
    /// assert!(ws.get_functions().find(|&&rva| rva == RVA(0x1aa0)).is_some());
    /// ```
    fn analyze(&self, ws: &mut Workspace) -> Result<(), Error> {
        let existing_functions: HashSet<&RVA> = ws.get_functions().collect();
        let mut orphans: Vec<RVA> = vec![];

        for (i, section) in ws.module.sections.iter().enumerate() {
            orphans.extend(ws.analysis.flow.meta[i].iter()
                .enumerate()
                .filter(|(_, meta)| meta.is_insn())
                .filter(|(_, meta)| !meta.has_xrefs_to())
                .filter(|(_, meta)| !meta.does_other_fallthrough_to())
                .map(|(j, _)| section.addr + RVA::from(j))
                .filter(|rva| !existing_functions.contains(rva)))
        }

        for rva in orphans.iter() {
            debug!("orphan function: {:#x}", rva);
            ws.make_function(*rva)?;
            ws.analyze()?;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! An implementation of something like dual contouring.
//! Dual contouring examines the voxel grid and looks for edges that cross between different material types.
//! If at least one of the materials is non-opaque, polygons are generated by using vertex data from the adjacent voxels.
//! See the SIGGRAPH paper for more details, but note that this probably isn't precisely a dual contouring implementation.

mod voxel {
  pub use voxel_data::impls::surface_vertex::T::*;
  pub use voxel_data::impls::surface_vertex::*;
}

#[allow(missing_docs)]
pub mod material {
  /// This trait provides the functionality required from materials for this algorithm.
  pub trait T : Eq {
    /// Is this material opaque?
    fn is_opaque(&self) -> bool;
  }
}

#[allow(missing_docs)]
pub mod polygon {
  use cgmath::{Point3, Vector3};

  /// The polygon type produced by this algorithm.
  pub struct T<Material> {
    #[allow(missing_docs)]
    pub vertices: [Point3<f32>; 3],
    #[allow(missing_docs)]
    pub normals: [Vector3<f32>; 3],
    #[allow(missing_docs)]
    pub material: Material,
  }
}

#[allow(missing_docs)]
pub mod voxel_storage {
  use cgmath::{Point3, Vector3};
  use voxel_data;

  use super::material;

  #[allow(missing_docs)]
  pub struct VoxelData {
    pub bounds: voxel_data::bounds::T,
    pub vertex: Point3<f32>,
    pub normal: Vector3<f32>,
  }

  /// The voxel storage interface required by dual contouring.
  pub trait T<Material> where Material: material::T {
    #[allow(missing_docs)]
    fn get_material(&mut self, voxel: &voxel_data::bounds::T) -> Option<Material>;
    /// Get the data for the given voxel. This function may also return data from a larger encompassing voxel.
    fn get_voxel_data(&mut self, voxel: &voxel_data::bounds::T) -> Option<VoxelData>;
  }
}

#[allow(missing_docs)]
pub mod edge {
  use cgmath::{Aabb, Point, Point3, Vector, Vector3};
  use voxel_data;

  use super::{voxel_storage, polygon, material};

  #[derive(Debug, Clone, Copy)]
  pub enum Direction { X, Y, Z }

  #[allow(missing_docs)]
  pub struct T {
    pub low_corner: Point3<i32>,
    pub lg_size: i16,
    pub direction: Direction,
  }

  fn corner_bounds(edge: &T) -> [voxel_data::bounds::T; 2] {
    let p0 = edge.low_corner;
    let d =
      match edge.direction {
        Direction::X => Vector3::new(1, 0, 0),
        Direction::Y => Vector3::new(0, 1, 0),
        Direction::Z => Vector3::new(0, 0, 1),
      };
    let p1 = p0.add_v(&d);
    [
      voxel_data::bounds::new(p0.x, p0.y, p0.z, edge.lg_size),
      voxel_data::bounds::new(p1.x, p1.y, p1.z, edge.lg_size),
    ]
  }

  fn neighbors(edge: &T) -> [voxel_data::bounds::T; 4] {
    let (v1, v2) =
      match edge.direction {
        Direction::X => (Vector3::new(0, 0, -1), Vector3::new(0, -1, 0)),
        Direction::Y => (Vector3::new(-1, 0, 0), Vector3::new(0, 0, -1)),
        Direction::Z => (Vector3::new(0, -1, 0), Vector3::new(-1, 0, 0)),
      };
    let make_bounds = |p: Point3<i32>| voxel_data::bounds::new(p.x, p.y, p.z, edge.lg_size);
    [
      make_bounds(edge.low_corner),
      make_bounds(edge.low_corner.add_v(&v1)),
      make_bounds(edge.low_corner.add_v(&v1).add_v(&v2)),
      make_bounds(edge.low_corner.add_v(&v2)),
    ]
  }

  enum Crossing<Material> {
    Undefined,
    None,
    LowInside(Material),
    HighInside(Material),
  }

  fn crossing<Material, Voxels>(
    voxels: &mut Voxels,
    edge: &T,
  ) -> Crossing<Material> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
  {
    let [b0, b1] = corner_bounds(edge);
    let material =
      match voxels.get_material(&b0) {
        None => return Crossing::Undefined,
        Some(m) => m,
      };
    let neighbor_material =
      match voxels.get_material(&b1) {
        None => return Crossing::Undefined,
        Some(m) => m,
      };
    if material == neighbor_material {
      Crossing::None
    } else if material.is_opaque() {
      Crossing::LowInside(material)
    } else {
      Crossing::HighInside(neighbor_material)
    }
  }

  fn resolve_voxels<'a, Material, Voxels, It>(
    voxels: &mut Voxels,
    bounds: It,
  ) -> Option<Vec<(Point3<f32>, Vector3<f32>)>> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
    It: Iterator<Item=&'a voxel_data::bounds::T>,
  {
    let mut resolved_voxel_data = Vec::new();
    let mut resolved_bounds: Vec<voxel_data::bounds::T> = Vec::new();

    'resolve_loop: for bounds in bounds {
      for resolved_bounds in &resolved_bounds {
        if resolved_bounds.contains(&bounds) {
          continue 'resolve_loop;
        }
      }

      let voxel_data =
        match voxels.get_voxel_data(&bounds) {
          None => return None,
          Some(d) => d,
        };
      resolved_bounds.push(voxel_data.bounds);
      resolved_voxel_data.push((voxel_data.vertex, voxel_data.normal));
    }

    Some(resolved_voxel_data)
  }

  /// Run dual contouring on a single edge
  // TODO: Return an iterator
  pub fn extract<Material, Voxels, OnPolygon>(
    voxels: &mut Voxels,
    edge: &T,
    mut poly: OnPolygon,
  ) where
    Material: material::T + Clone,
    Voxels: voxel_storage::T<Material>,
    OnPolygon: FnMut(polygon::T<Material>),
  {
    let (material, vertices_and_normals) =
      match crossing(voxels, edge) {
        Crossing::Undefined => return,
        Crossing::None => return,
        Crossing::HighInside(material) => {
          match resolve_voxels(voxels, neighbors(&edge).iter()) {
            None => return,
            Some(resolved) => (material, resolved)
          }
        },
        Crossing::LowInside(material) => {
          match resolve_voxels(voxels, neighbors(&edge).iter().rev()) {
            None => return,
            Some(resolved) => (material, resolved)
          }
        }
      };

    if vertices_and_normals.len() == 3 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      poly(
        polygon::T {
          vertices: [v0, v1, v2],
          normals: [n0, n1, n2],
          material: material,
        }
      );
    } else if vertices_and_normals.len() == 4 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      let (v3, n3) = vertices_and_normals[3];
      let v_center =
        v0.add_v(&v1.to_vec()).add_v(&v2.to_vec()).add_v(&v3.to_vec()).div_s(4.0);
      let n_center =
        n0.add_v(&n1).add_v(&n2).add_v(&n3).div_s(4.0);
      poly(
        polygon::T {
          vertices: [v0, v1, v_center],
          normals: [n0, n1, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v1, v2, v_center],
          normals: [n1, n2, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v2, v3, v_center],
          normals: [n2, n3, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v3, v0, v_center],
          normals: [n3, n0, n_center],
          material: material.clone(),
        }
      );
    } else {
      panic!("Edge has an unexpected number of neighbors: {}", vertices_and_normals.len());
    }
  }
}
<commit_msg>More instances for things<commit_after>//! An implementation of something like dual contouring.
//! Dual contouring examines the voxel grid and looks for edges that cross between different material types.
//! If at least one of the materials is non-opaque, polygons are generated by using vertex data from the adjacent voxels.
//! See the SIGGRAPH paper for more details, but note that this probably isn't precisely a dual contouring implementation.

mod voxel {
  pub use voxel_data::impls::surface_vertex::T::*;
  pub use voxel_data::impls::surface_vertex::*;
}

#[allow(missing_docs)]
pub mod material {
  /// This trait provides the functionality required from materials for this algorithm.
  pub trait T : Eq {
    /// Is this material opaque?
    fn is_opaque(&self) -> bool;
  }
}

#[allow(missing_docs)]
pub mod polygon {
  use cgmath::{Point3, Vector3};

  /// The polygon type produced by this algorithm.
  pub struct T<Material> {
    #[allow(missing_docs)]
    pub vertices: [Point3<f32>; 3],
    #[allow(missing_docs)]
    pub normals: [Vector3<f32>; 3],
    #[allow(missing_docs)]
    pub material: Material,
  }
}

#[allow(missing_docs)]
pub mod voxel_storage {
  use cgmath::{Point3, Vector3};
  use voxel_data;

  use super::material;

  #[allow(missing_docs)]
  pub struct VoxelData {
    pub bounds: voxel_data::bounds::T,
    pub vertex: Point3<f32>,
    pub normal: Vector3<f32>,
  }

  /// The voxel storage interface required by dual contouring.
  pub trait T<Material> where Material: material::T {
    #[allow(missing_docs)]
    fn get_material(&mut self, voxel: &voxel_data::bounds::T) -> Option<Material>;
    /// Get the data for the given voxel. This function may also return data from a larger encompassing voxel.
    fn get_voxel_data(&mut self, voxel: &voxel_data::bounds::T) -> Option<VoxelData>;
  }
}

#[allow(missing_docs)]
pub mod edge {
  use cgmath::{Aabb, Point, Point3, Vector, Vector3};
  use voxel_data;

  use super::{voxel_storage, polygon, material};

  #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
  pub enum Direction { X, Y, Z }

  #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  #[allow(missing_docs)]
  pub struct T {
    pub low_corner: Point3<i32>,
    pub lg_size: i16,
    pub direction: Direction,
  }

  fn corner_bounds(edge: &T) -> [voxel_data::bounds::T; 2] {
    let p0 = edge.low_corner;
    let d =
      match edge.direction {
        Direction::X => Vector3::new(1, 0, 0),
        Direction::Y => Vector3::new(0, 1, 0),
        Direction::Z => Vector3::new(0, 0, 1),
      };
    let p1 = p0.add_v(&d);
    [
      voxel_data::bounds::new(p0.x, p0.y, p0.z, edge.lg_size),
      voxel_data::bounds::new(p1.x, p1.y, p1.z, edge.lg_size),
    ]
  }

  fn neighbors(edge: &T) -> [voxel_data::bounds::T; 4] {
    let (v1, v2) =
      match edge.direction {
        Direction::X => (Vector3::new(0, 0, -1), Vector3::new(0, -1, 0)),
        Direction::Y => (Vector3::new(-1, 0, 0), Vector3::new(0, 0, -1)),
        Direction::Z => (Vector3::new(0, -1, 0), Vector3::new(-1, 0, 0)),
      };
    let make_bounds = |p: Point3<i32>| voxel_data::bounds::new(p.x, p.y, p.z, edge.lg_size);
    [
      make_bounds(edge.low_corner),
      make_bounds(edge.low_corner.add_v(&v1)),
      make_bounds(edge.low_corner.add_v(&v1).add_v(&v2)),
      make_bounds(edge.low_corner.add_v(&v2)),
    ]
  }

  enum Crossing<Material> {
    Undefined,
    None,
    LowInside(Material),
    HighInside(Material),
  }

  fn crossing<Material, Voxels>(
    voxels: &mut Voxels,
    edge: &T,
  ) -> Crossing<Material> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
  {
    let [b0, b1] = corner_bounds(edge);
    let material =
      match voxels.get_material(&b0) {
        None => return Crossing::Undefined,
        Some(m) => m,
      };
    let neighbor_material =
      match voxels.get_material(&b1) {
        None => return Crossing::Undefined,
        Some(m) => m,
      };
    if material == neighbor_material {
      Crossing::None
    } else if material.is_opaque() {
      Crossing::LowInside(material)
    } else {
      Crossing::HighInside(neighbor_material)
    }
  }

  fn resolve_voxels<'a, Material, Voxels, It>(
    voxels: &mut Voxels,
    bounds: It,
  ) -> Option<Vec<(Point3<f32>, Vector3<f32>)>> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
    It: Iterator<Item=&'a voxel_data::bounds::T>,
  {
    let mut resolved_voxel_data = Vec::new();
    let mut resolved_bounds: Vec<voxel_data::bounds::T> = Vec::new();

    'resolve_loop: for bounds in bounds {
      for resolved_bounds in &resolved_bounds {
        if resolved_bounds.contains(&bounds) {
          continue 'resolve_loop;
        }
      }

      let voxel_data =
        match voxels.get_voxel_data(&bounds) {
          None => return None,
          Some(d) => d,
        };
      resolved_bounds.push(voxel_data.bounds);
      resolved_voxel_data.push((voxel_data.vertex, voxel_data.normal));
    }

    Some(resolved_voxel_data)
  }

  /// Run dual contouring on a single edge
  // TODO: Return an iterator
  pub fn extract<Material, Voxels, OnPolygon>(
    voxels: &mut Voxels,
    edge: &T,
    mut poly: OnPolygon,
  ) where
    Material: material::T + Clone,
    Voxels: voxel_storage::T<Material>,
    OnPolygon: FnMut(polygon::T<Material>),
  {
    let (material, vertices_and_normals) =
      match crossing(voxels, edge) {
        Crossing::Undefined => return,
        Crossing::None => return,
        Crossing::HighInside(material) => {
          match resolve_voxels(voxels, neighbors(&edge).iter()) {
            None => return,
            Some(resolved) => (material, resolved)
          }
        },
        Crossing::LowInside(material) => {
          match resolve_voxels(voxels, neighbors(&edge).iter().rev()) {
            None => return,
            Some(resolved) => (material, resolved)
          }
        }
      };

    if vertices_and_normals.len() == 3 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      poly(
        polygon::T {
          vertices: [v0, v1, v2],
          normals: [n0, n1, n2],
          material: material,
        }
      );
    } else if vertices_and_normals.len() == 4 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      let (v3, n3) = vertices_and_normals[3];
      let v_center =
        v0.add_v(&v1.to_vec()).add_v(&v2.to_vec()).add_v(&v3.to_vec()).div_s(4.0);
      let n_center =
        n0.add_v(&n1).add_v(&n2).add_v(&n3).div_s(4.0);
      poly(
        polygon::T {
          vertices: [v0, v1, v_center],
          normals: [n0, n1, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v1, v2, v_center],
          normals: [n1, n2, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v2, v3, v_center],
          normals: [n2, n3, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v3, v0, v_center],
          normals: [n3, n0, n_center],
          material: material.clone(),
        }
      );
    } else {
      panic!("Edge has an unexpected number of neighbors: {}", vertices_and_normals.len());
    }
  }
}
<|endoftext|>


<commit_before>use std::fmt;
use num;
use color::Color;
use rgb::Rgb;
use channel::{PosNormalChannelScalar, ChannelFormatCast};
use encoding::EncodedColor;

pub trait ChannelEncoder {
    fn encode_channel<T>(&self, val: T) -> T where T: num::Float;
}
pub trait ChannelDecoder {
    fn decode_channel<T>(&self, val: T) -> T where T: num::Float;
}

pub trait EncodableColor: Color {
    type IntermediateColor;
    fn encode_color<Encoder>(self, enc: &Encoder) -> Self where Encoder: ChannelEncoder;
    fn decode_color<Decoder>(self, dec: &Decoder) -> Self where Decoder: ChannelDecoder;

    fn with_encoding<Encoding>(self, enc: Encoding) -> EncodedColor<Self, Encoding>
        where Encoding: ColorEncoding
    {
        EncodedColor::new(self, enc)
    }
}

pub trait ColorEncoding: ChannelEncoder + ChannelDecoder + Sized + Clone {}

#[derive(Clone, Debug, PartialEq)]
pub struct SrgbEncoding {}
#[derive(Clone, Debug, PartialEq)]
pub struct LinearEncoding {}
#[derive(Clone, Debug, PartialEq)]
pub struct GammaEncoding<T>(pub T);

impl SrgbEncoding {
    pub fn new() -> Self {
        SrgbEncoding {}
    }
}

impl ChannelDecoder for SrgbEncoding {
    fn decode_channel<T>(&self, val: T) -> T
        where T: num::Float
    {
        let one: T = num::cast(1.0).unwrap();
        let a: T = num::cast(0.055).unwrap();
        let k: T = num::cast(12.92).unwrap();
        let gamma: T = num::cast(2.4).unwrap();
        let linear_threshold: T = num::cast(0.04045).unwrap();

        if val < linear_threshold {
            val / k
        } else {
            ((val + a) / (one + a)).powf(gamma)
        }
    }
}

impl ChannelEncoder for SrgbEncoding {
    fn encode_channel<T>(&self, val: T) -> T
        where T: num::Float
    {
        let one: T = num::cast(1.0).unwrap();
        let a: T = num::cast(0.055).unwrap();
        let k: T = num::cast(12.92).unwrap();
        let gamma: T = num::cast(2.4).unwrap();
        let linear_threshold: T = num::cast(0.0031308).unwrap();

        if val < linear_threshold {
            k * val
        } else {
            (one + a) * val.powf(one / gamma) - a
        }
    }
}

impl ColorEncoding for SrgbEncoding {}

impl fmt::Display for SrgbEncoding {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "sRgb")
    }
}

impl LinearEncoding {
    pub fn new() -> Self {
        LinearEncoding {}
    }
}

impl ChannelDecoder for LinearEncoding {
    fn decode_channel<T>(&self, val: T) -> T
        where T: num::Float
    {
        val
    }
}

impl ChannelEncoder for LinearEncoding {
    fn encode_channel<T>(&self, val: T) -> T
        where T: num::Float
    {
        val
    }
}

impl ColorEncoding for LinearEncoding {}

impl fmt::Display for LinearEncoding {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Linear")
    }
}

impl<T> GammaEncoding<T>
    where T: num::Float
{
    pub fn new(val: T) -> Self {
        GammaEncoding(val)
    }

    pub fn exponent(&self) -> T {
        self.0
    }
}

impl<T> ChannelDecoder for GammaEncoding<T>
    where T: num::Float
{
    fn decode_channel<U>(&self, val: U) -> U
        where U: num::Float
    {
        val.powf(num::cast(self.0).unwrap())
    }
}
impl<T> ChannelEncoder for GammaEncoding<T>
    where T: num::Float
{
    fn encode_channel<U>(&self, val: U) -> U
        where U: num::Float
    {
        let one: T = num::cast(1.0).unwrap();
        val.powf(num::cast(one / self.0).unwrap())
    }
}

impl<T: num::Float> ColorEncoding for GammaEncoding<T> {}

impl<T> fmt::Display for GammaEncoding<T>
    where T: num::Float + fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "γ={}", self.0)
    }
}

impl<T> EncodableColor for Rgb<T>
    where T: PosNormalChannelScalar + ChannelFormatCast<f64>,
          f64: ChannelFormatCast<T>
{
    type IntermediateColor = Rgb<f64>;
    fn encode_color<Encoder>(self, enc: &Encoder) -> Self
        where Encoder: ChannelEncoder
    {
        let flt_color: Self::IntermediateColor = self.color_cast();

        let enc_r = enc.encode_channel(flt_color.red());
        let enc_g = enc.encode_channel(flt_color.green());
        let enc_b = enc.encode_channel(flt_color.blue());

        let out_color: Rgb<T> = Rgb::from_channels(enc_r, enc_g, enc_b).color_cast();

        out_color
    }

    fn decode_color<Decoder>(self, dec: &Decoder) -> Self
        where Decoder: ChannelDecoder
    {
        let flt_color: Self::IntermediateColor = self.color_cast();

        let linear_r = dec.decode_channel(flt_color.red());
        let linear_g = dec.decode_channel(flt_color.green());
        let linear_b = dec.decode_channel(flt_color.blue());

        let out_color: Rgb<T> = Rgb::from_channels(linear_r, linear_g, linear_b).color_cast();

        out_color
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use rgb::Rgb;
    use color::*;

    #[test]
    fn test_gamma_encoding() {
        let c1 = Rgb::from_channels(0.0, 0.0, 0.0).with_encoding(LinearEncoding::new());
        let t1 = c1.clone().encode(GammaEncoding::new(2.0));
        assert_relative_eq!(t1.color(), c1.color(), epsilon=1e-6);

        let c2 = Rgb::from_channels(1.0, 1.0, 1.0).with_encoding(LinearEncoding::new());
        let t2 = c2.clone().encode(GammaEncoding::new(2.2));
        assert_relative_eq!(t2.color(), c2.color(), epsilon=1e-6);

        let c3 = Rgb::from_channels(0.5, 0.5, 0.5).with_encoding(LinearEncoding::new());
        let t3 = c3.clone().encode(GammaEncoding::new(2.2));
        assert_relative_eq!(*t3.color(),
            Rgb::broadcast(0.72974005), epsilon=1e-6);
        assert_relative_eq!(t3.decode(), c3, epsilon=1e-6);

        let c4 = Rgb::from_channels(0.2, 0.8, 0.66).with_encoding(LinearEncoding::new());
        let t4 = c4.clone().encode(GammaEncoding::new(1.8));
        assert_relative_eq!(*t4.color(),
            Rgb::from_channels(0.4089623, 0.88340754, 0.793864955), epsilon=1e-6);
        assert_relative_eq!(t4.decode(), c4, epsilon=1e-6);

        let c5 = Rgb::from_channels(0.5, 0.5, 0.5).with_encoding(GammaEncoding::new(2.4));
        let t5 = c5.clone().decode();
        assert_relative_eq!(*t5.color(), Rgb::broadcast(0.18946457), epsilon=1e-6);
    }

    #[test]
    fn test_srgb_encoding() {
        let c1 = Rgb::from_channels(0.0, 0.0, 0.0).with_encoding(LinearEncoding::new());
        let t1 = c1.clone().encode(SrgbEncoding::new());
        assert_relative_eq!(t1.color(), c1.color(), epsilon=1e-6);

        let c2 = Rgb::from_channels(1.0, 1.0, 1.0).with_encoding(LinearEncoding::new());
        let t2 = c2.clone().encode(SrgbEncoding::new());
        assert_relative_eq!(t2.color(), c2.color(), epsilon=1e-6);

        let c3 = Rgb::from_channels(0.5, 0.5, 0.5).with_encoding(LinearEncoding::new());
        let t3 = c3.clone().encode(SrgbEncoding::new());
        assert_relative_eq!(*t3.color(),
            Rgb::broadcast(0.735356983052), epsilon=1e-6);
        assert_relative_eq!(t3.decode(), c3, epsilon=1e-6);

        let c4 = Rgb::from_channels(0.2, 0.8, 0.66).with_encoding(LinearEncoding::new());
        let t4 = c4.clone().encode(SrgbEncoding::new());
        assert_relative_eq!(*t4.color(),
            Rgb::from_channels(0.4845292044, 0.90633175, 0.83228355590), epsilon=1e-6);
        assert_relative_eq!(t4.decode(), c4, epsilon=1e-6);

        let c5 = Rgb::from_channels(0.5, 0.5, 0.5).with_encoding(SrgbEncoding::new());
        let t5 = c5.clone().decode();
        assert_relative_eq!(*t5.color(), Rgb::broadcast(0.21404114048), epsilon=1e-6);
    }
}
<commit_msg>Added `Default` implementation for all encoders.<commit_after>use std::fmt;
use num;
use color::Color;
use rgb::Rgb;
use channel::{PosNormalChannelScalar, ChannelFormatCast};
use encoding::EncodedColor;

pub trait ChannelEncoder {
    fn encode_channel<T>(&self, val: T) -> T where T: num::Float;
}
pub trait ChannelDecoder {
    fn decode_channel<T>(&self, val: T) -> T where T: num::Float;
}

pub trait EncodableColor: Color {
    type IntermediateColor;
    fn encode_color<Encoder>(self, enc: &Encoder) -> Self where Encoder: ChannelEncoder;
    fn decode_color<Decoder>(self, dec: &Decoder) -> Self where Decoder: ChannelDecoder;

    fn with_encoding<Encoding>(self, enc: Encoding) -> EncodedColor<Self, Encoding>
        where Encoding: ColorEncoding
    {
        EncodedColor::new(self, enc)
    }
}

pub trait ColorEncoding: ChannelEncoder + ChannelDecoder + Sized + Clone {}

#[derive(Clone, Debug, PartialEq)]
pub struct SrgbEncoding {}
#[derive(Clone, Debug, PartialEq)]
pub struct LinearEncoding {}
#[derive(Clone, Debug, PartialEq)]
pub struct GammaEncoding<T>(pub T);

impl SrgbEncoding {
    pub fn new() -> Self {
        SrgbEncoding {}
    }
}

impl ChannelDecoder for SrgbEncoding {
    fn decode_channel<T>(&self, val: T) -> T
        where T: num::Float
    {
        let one: T = num::cast(1.0).unwrap();
        let a: T = num::cast(0.055).unwrap();
        let k: T = num::cast(12.92).unwrap();
        let gamma: T = num::cast(2.4).unwrap();
        let linear_threshold: T = num::cast(0.04045).unwrap();

        if val < linear_threshold {
            val / k
        } else {
            ((val + a) / (one + a)).powf(gamma)
        }
    }
}

impl ChannelEncoder for SrgbEncoding {
    fn encode_channel<T>(&self, val: T) -> T
        where T: num::Float
    {
        let one: T = num::cast(1.0).unwrap();
        let a: T = num::cast(0.055).unwrap();
        let k: T = num::cast(12.92).unwrap();
        let gamma: T = num::cast(2.4).unwrap();
        let linear_threshold: T = num::cast(0.0031308).unwrap();

        if val < linear_threshold {
            k * val
        } else {
            (one + a) * val.powf(one / gamma) - a
        }
    }
}

impl ColorEncoding for SrgbEncoding {}

impl Default for SrgbEncoding {
    fn default() -> Self {
        SrgbEncoding {}
    }
}

impl fmt::Display for SrgbEncoding {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "sRgb")
    }
}

impl LinearEncoding {
    pub fn new() -> Self {
        LinearEncoding {}
    }
}

impl ChannelDecoder for LinearEncoding {
    fn decode_channel<T>(&self, val: T) -> T
        where T: num::Float
    {
        val
    }
}

impl ChannelEncoder for LinearEncoding {
    fn encode_channel<T>(&self, val: T) -> T
        where T: num::Float
    {
        val
    }
}

impl ColorEncoding for LinearEncoding {}

impl Default for LinearEncoding {
    fn default() -> Self {
        LinearEncoding {}
    }
}

impl fmt::Display for LinearEncoding {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Linear")
    }
}

impl<T> GammaEncoding<T>
    where T: num::Float
{
    pub fn new(val: T) -> Self {
        GammaEncoding(val)
    }

    pub fn exponent(&self) -> T {
        self.0
    }
}

impl<T> ChannelDecoder for GammaEncoding<T>
    where T: num::Float
{
    fn decode_channel<U>(&self, val: U) -> U
        where U: num::Float
    {
        val.powf(num::cast(self.0).unwrap())
    }
}
impl<T> ChannelEncoder for GammaEncoding<T>
    where T: num::Float
{
    fn encode_channel<U>(&self, val: U) -> U
        where U: num::Float
    {
        let one: T = num::cast(1.0).unwrap();
        val.powf(num::cast(one / self.0).unwrap())
    }
}

impl<T: num::Float> ColorEncoding for GammaEncoding<T> {}

impl<T: num::Float> Default for GammaEncoding<T> {
    fn default() -> Self {
        GammaEncoding::new(num::cast(2.2).unwrap())
    }
}

impl<T> fmt::Display for GammaEncoding<T>
    where T: num::Float + fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "γ={}", self.0)
    }
}

impl<T> EncodableColor for Rgb<T>
    where T: PosNormalChannelScalar + ChannelFormatCast<f64>,
          f64: ChannelFormatCast<T>
{
    type IntermediateColor = Rgb<f64>;
    fn encode_color<Encoder>(self, enc: &Encoder) -> Self
        where Encoder: ChannelEncoder
    {
        let flt_color: Self::IntermediateColor = self.color_cast();

        let enc_r = enc.encode_channel(flt_color.red());
        let enc_g = enc.encode_channel(flt_color.green());
        let enc_b = enc.encode_channel(flt_color.blue());

        let out_color: Rgb<T> = Rgb::from_channels(enc_r, enc_g, enc_b).color_cast();

        out_color
    }

    fn decode_color<Decoder>(self, dec: &Decoder) -> Self
        where Decoder: ChannelDecoder
    {
        let flt_color: Self::IntermediateColor = self.color_cast();

        let linear_r = dec.decode_channel(flt_color.red());
        let linear_g = dec.decode_channel(flt_color.green());
        let linear_b = dec.decode_channel(flt_color.blue());

        let out_color: Rgb<T> = Rgb::from_channels(linear_r, linear_g, linear_b).color_cast();

        out_color
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use rgb::Rgb;
    use color::*;

    #[test]
    fn test_gamma_encoding() {
        let c1 = Rgb::from_channels(0.0, 0.0, 0.0).with_encoding(LinearEncoding::new());
        let t1 = c1.clone().encode(GammaEncoding::new(2.0));
        assert_relative_eq!(t1.color(), c1.color(), epsilon=1e-6);

        let c2 = Rgb::from_channels(1.0, 1.0, 1.0).with_encoding(LinearEncoding::new());
        let t2 = c2.clone().encode(GammaEncoding::new(2.2));
        assert_relative_eq!(t2.color(), c2.color(), epsilon=1e-6);

        let c3 = Rgb::from_channels(0.5, 0.5, 0.5).with_encoding(LinearEncoding::new());
        let t3 = c3.clone().encode(GammaEncoding::new(2.2));
        assert_relative_eq!(*t3.color(),
            Rgb::broadcast(0.72974005), epsilon=1e-6);
        assert_relative_eq!(t3.decode(), c3, epsilon=1e-6);

        let c4 = Rgb::from_channels(0.2, 0.8, 0.66).with_encoding(LinearEncoding::new());
        let t4 = c4.clone().encode(GammaEncoding::new(1.8));
        assert_relative_eq!(*t4.color(),
            Rgb::from_channels(0.4089623, 0.88340754, 0.793864955), epsilon=1e-6);
        assert_relative_eq!(t4.decode(), c4, epsilon=1e-6);

        let c5 = Rgb::from_channels(0.5, 0.5, 0.5).with_encoding(GammaEncoding::new(2.4));
        let t5 = c5.clone().decode();
        assert_relative_eq!(*t5.color(), Rgb::broadcast(0.18946457), epsilon=1e-6);
    }

    #[test]
    fn test_srgb_encoding() {
        let c1 = Rgb::from_channels(0.0, 0.0, 0.0).with_encoding(LinearEncoding::new());
        let t1 = c1.clone().encode(SrgbEncoding::new());
        assert_relative_eq!(t1.color(), c1.color(), epsilon=1e-6);

        let c2 = Rgb::from_channels(1.0, 1.0, 1.0).with_encoding(LinearEncoding::new());
        let t2 = c2.clone().encode(SrgbEncoding::new());
        assert_relative_eq!(t2.color(), c2.color(), epsilon=1e-6);

        let c3 = Rgb::from_channels(0.5, 0.5, 0.5).with_encoding(LinearEncoding::new());
        let t3 = c3.clone().encode(SrgbEncoding::new());
        assert_relative_eq!(*t3.color(),
            Rgb::broadcast(0.735356983052), epsilon=1e-6);
        assert_relative_eq!(t3.decode(), c3, epsilon=1e-6);

        let c4 = Rgb::from_channels(0.2, 0.8, 0.66).with_encoding(LinearEncoding::new());
        let t4 = c4.clone().encode(SrgbEncoding::new());
        assert_relative_eq!(*t4.color(),
            Rgb::from_channels(0.4845292044, 0.90633175, 0.83228355590), epsilon=1e-6);
        assert_relative_eq!(t4.decode(), c4, epsilon=1e-6);

        let c5 = Rgb::from_channels(0.5, 0.5, 0.5).with_encoding(SrgbEncoding::new());
        let t5 = c5.clone().decode();
        assert_relative_eq!(*t5.color(), Rgb::broadcast(0.21404114048), epsilon=1e-6);
    }
}
<|endoftext|>


<commit_before>#![feature(never_type)]
#![feature(const_panic)]
#![feature(control_flow_enum)]

#[macro_use]
extern crate bitflags;

use rustc_data_structures::stable_hasher::{HashStable, StableHasher};

bitflags! {
    /// Flags that we track on types. These flags are propagated upwards
    /// through the type during type construction, so that we can quickly check
    /// whether the type has various kinds of types in it without recursing
    /// over the type itself.
    pub struct TypeFlags: u32 {
        // Does this have parameters? Used to determine whether substitution is
        // required.
        /// Does this have `Param`?
        const HAS_TY_PARAM                = 1 << 0;
        /// Does this have `ReEarlyBound`?
        const HAS_RE_PARAM                = 1 << 1;
        /// Does this have `ConstKind::Param`?
        const HAS_CT_PARAM                = 1 << 2;

        const NEEDS_SUBST                 = TypeFlags::HAS_TY_PARAM.bits
                                          | TypeFlags::HAS_RE_PARAM.bits
                                          | TypeFlags::HAS_CT_PARAM.bits;

        /// Does this have `Infer`?
        const HAS_TY_INFER                = 1 << 3;
        /// Does this have `ReVar`?
        const HAS_RE_INFER                = 1 << 4;
        /// Does this have `ConstKind::Infer`?
        const HAS_CT_INFER                = 1 << 5;

        /// Does this have inference variables? Used to determine whether
        /// inference is required.
        const NEEDS_INFER                 = TypeFlags::HAS_TY_INFER.bits
                                          | TypeFlags::HAS_RE_INFER.bits
                                          | TypeFlags::HAS_CT_INFER.bits;

        /// Does this have `Placeholder`?
        const HAS_TY_PLACEHOLDER          = 1 << 6;
        /// Does this have `RePlaceholder`?
        const HAS_RE_PLACEHOLDER          = 1 << 7;
        /// Does this have `ConstKind::Placeholder`?
        const HAS_CT_PLACEHOLDER          = 1 << 8;

        /// `true` if there are "names" of regions and so forth
        /// that are local to a particular fn/inferctxt
        const HAS_FREE_LOCAL_REGIONS      = 1 << 9;

        /// `true` if there are "names" of types and regions and so forth
        /// that are local to a particular fn
        const HAS_FREE_LOCAL_NAMES        = TypeFlags::HAS_TY_PARAM.bits
                                          | TypeFlags::HAS_CT_PARAM.bits
                                          | TypeFlags::HAS_TY_INFER.bits
                                          | TypeFlags::HAS_CT_INFER.bits
                                          | TypeFlags::HAS_TY_PLACEHOLDER.bits
                                          | TypeFlags::HAS_CT_PLACEHOLDER.bits
                                          | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits;

        /// Does this have `Projection`?
        const HAS_TY_PROJECTION           = 1 << 10;
        /// Does this have `Opaque`?
        const HAS_TY_OPAQUE               = 1 << 11;
        /// Does this have `ConstKind::Unevaluated`?
        const HAS_CT_PROJECTION           = 1 << 12;

        /// Could this type be normalized further?
        const HAS_PROJECTION              = TypeFlags::HAS_TY_PROJECTION.bits
                                          | TypeFlags::HAS_TY_OPAQUE.bits
                                          | TypeFlags::HAS_CT_PROJECTION.bits;

        /// Is an error type/const reachable?
        const HAS_ERROR                   = 1 << 13;

        /// Does this have any region that "appears free" in the type?
        /// Basically anything but `ReLateBound` and `ReErased`.
        const HAS_FREE_REGIONS            = 1 << 14;

        /// Does this have any `ReLateBound` regions? Used to check
        /// if a global bound is safe to evaluate.
        const HAS_RE_LATE_BOUND           = 1 << 15;

        /// Does this have any `ReErased` regions?
        const HAS_RE_ERASED               = 1 << 16;

        /// Does this value have parameters/placeholders/inference variables which could be
        /// replaced later, in a way that would change the results of `impl` specialization?
        const STILL_FURTHER_SPECIALIZABLE = 1 << 17;
    }
}

rustc_index::newtype_index! {
    /// A [De Bruijn index][dbi] is a standard means of representing
    /// regions (and perhaps later types) in a higher-ranked setting. In
    /// particular, imagine a type like this:
    ///
    ///     for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char)
    ///     ^          ^            |          |           |
    ///     |          |            |          |           |
    ///     |          +------------+ 0        |           |
    ///     |                                  |           |
    ///     +----------------------------------+ 1         |
    ///     |                                              |
    ///     +----------------------------------------------+ 0
    ///
    /// In this type, there are two binders (the outer fn and the inner
    /// fn). We need to be able to determine, for any given region, which
    /// fn type it is bound by, the inner or the outer one. There are
    /// various ways you can do this, but a De Bruijn index is one of the
    /// more convenient and has some nice properties. The basic idea is to
    /// count the number of binders, inside out. Some examples should help
    /// clarify what I mean.
    ///
    /// Let's start with the reference type `&'b isize` that is the first
    /// argument to the inner function. This region `'b` is assigned a De
    /// Bruijn index of 0, meaning "the innermost binder" (in this case, a
    /// fn). The region `'a` that appears in the second argument type (`&'a
    /// isize`) would then be assigned a De Bruijn index of 1, meaning "the
    /// second-innermost binder". (These indices are written on the arrays
    /// in the diagram).
    ///
    /// What is interesting is that De Bruijn index attached to a particular
    /// variable will vary depending on where it appears. For example,
    /// the final type `&'a char` also refers to the region `'a` declared on
    /// the outermost fn. But this time, this reference is not nested within
    /// any other binders (i.e., it is not an argument to the inner fn, but
    /// rather the outer one). Therefore, in this case, it is assigned a
    /// De Bruijn index of 0, because the innermost binder in that location
    /// is the outer fn.
    ///
    /// [dbi]: https://en.wikipedia.org/wiki/De_Bruijn_index
    pub struct DebruijnIndex {
        DEBUG_FORMAT = "DebruijnIndex({})",
        const INNERMOST = 0,
    }
}

impl DebruijnIndex {
    /// Returns the resulting index when this value is moved into
    /// `amount` number of new binders. So, e.g., if you had
    ///
    ///    for<'a> fn(&'a x)
    ///
    /// and you wanted to change it to
    ///
    ///    for<'a> fn(for<'b> fn(&'a x))
    ///
    /// you would need to shift the index for `'a` into a new binder.
    #[must_use]
    pub fn shifted_in(self, amount: u32) -> DebruijnIndex {
        DebruijnIndex::from_u32(self.as_u32() + amount)
    }

    /// Update this index in place by shifting it "in" through
    /// `amount` number of binders.
    pub fn shift_in(&mut self, amount: u32) {
        *self = self.shifted_in(amount);
    }

    /// Returns the resulting index when this value is moved out from
    /// `amount` number of new binders.
    #[must_use]
    pub fn shifted_out(self, amount: u32) -> DebruijnIndex {
        DebruijnIndex::from_u32(self.as_u32() - amount)
    }

    /// Update in place by shifting out from `amount` binders.
    pub fn shift_out(&mut self, amount: u32) {
        *self = self.shifted_out(amount);
    }

    /// Adjusts any De Bruijn indices so as to make `to_binder` the
    /// innermost binder. That is, if we have something bound at `to_binder`,
    /// it will now be bound at INNERMOST. This is an appropriate thing to do
    /// when moving a region out from inside binders:
    ///
    /// ```
    ///             for<'a>   fn(for<'b>   for<'c>   fn(&'a u32), _)
    /// // Binder:  D3           D2        D1            ^^
    /// ```
    ///
    /// Here, the region `'a` would have the De Bruijn index D3,
    /// because it is the bound 3 binders out. However, if we wanted
    /// to refer to that region `'a` in the second argument (the `_`),
    /// those two binders would not be in scope. In that case, we
    /// might invoke `shift_out_to_binder(D3)`. This would adjust the
    /// De Bruijn index of `'a` to D1 (the innermost binder).
    ///
    /// If we invoke `shift_out_to_binder` and the region is in fact
    /// bound by one of the binders we are shifting out of, that is an
    /// error (and should fail an assertion failure).
    pub fn shifted_out_to_binder(self, to_binder: DebruijnIndex) -> Self {
        self.shifted_out(to_binder.as_u32() - INNERMOST.as_u32())
    }
}

impl<CTX> HashStable<CTX> for DebruijnIndex {
    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
        self.as_u32().hash_stable(ctx, hasher);
    }
}
<commit_msg>Rollup merge of #79963 - LeSeulArtichaut:debruijn-typo, r=Dylan-DPC<commit_after>#![feature(never_type)]
#![feature(const_panic)]
#![feature(control_flow_enum)]

#[macro_use]
extern crate bitflags;

use rustc_data_structures::stable_hasher::{HashStable, StableHasher};

bitflags! {
    /// Flags that we track on types. These flags are propagated upwards
    /// through the type during type construction, so that we can quickly check
    /// whether the type has various kinds of types in it without recursing
    /// over the type itself.
    pub struct TypeFlags: u32 {
        // Does this have parameters? Used to determine whether substitution is
        // required.
        /// Does this have `Param`?
        const HAS_TY_PARAM                = 1 << 0;
        /// Does this have `ReEarlyBound`?
        const HAS_RE_PARAM                = 1 << 1;
        /// Does this have `ConstKind::Param`?
        const HAS_CT_PARAM                = 1 << 2;

        const NEEDS_SUBST                 = TypeFlags::HAS_TY_PARAM.bits
                                          | TypeFlags::HAS_RE_PARAM.bits
                                          | TypeFlags::HAS_CT_PARAM.bits;

        /// Does this have `Infer`?
        const HAS_TY_INFER                = 1 << 3;
        /// Does this have `ReVar`?
        const HAS_RE_INFER                = 1 << 4;
        /// Does this have `ConstKind::Infer`?
        const HAS_CT_INFER                = 1 << 5;

        /// Does this have inference variables? Used to determine whether
        /// inference is required.
        const NEEDS_INFER                 = TypeFlags::HAS_TY_INFER.bits
                                          | TypeFlags::HAS_RE_INFER.bits
                                          | TypeFlags::HAS_CT_INFER.bits;

        /// Does this have `Placeholder`?
        const HAS_TY_PLACEHOLDER          = 1 << 6;
        /// Does this have `RePlaceholder`?
        const HAS_RE_PLACEHOLDER          = 1 << 7;
        /// Does this have `ConstKind::Placeholder`?
        const HAS_CT_PLACEHOLDER          = 1 << 8;

        /// `true` if there are "names" of regions and so forth
        /// that are local to a particular fn/inferctxt
        const HAS_FREE_LOCAL_REGIONS      = 1 << 9;

        /// `true` if there are "names" of types and regions and so forth
        /// that are local to a particular fn
        const HAS_FREE_LOCAL_NAMES        = TypeFlags::HAS_TY_PARAM.bits
                                          | TypeFlags::HAS_CT_PARAM.bits
                                          | TypeFlags::HAS_TY_INFER.bits
                                          | TypeFlags::HAS_CT_INFER.bits
                                          | TypeFlags::HAS_TY_PLACEHOLDER.bits
                                          | TypeFlags::HAS_CT_PLACEHOLDER.bits
                                          | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits;

        /// Does this have `Projection`?
        const HAS_TY_PROJECTION           = 1 << 10;
        /// Does this have `Opaque`?
        const HAS_TY_OPAQUE               = 1 << 11;
        /// Does this have `ConstKind::Unevaluated`?
        const HAS_CT_PROJECTION           = 1 << 12;

        /// Could this type be normalized further?
        const HAS_PROJECTION              = TypeFlags::HAS_TY_PROJECTION.bits
                                          | TypeFlags::HAS_TY_OPAQUE.bits
                                          | TypeFlags::HAS_CT_PROJECTION.bits;

        /// Is an error type/const reachable?
        const HAS_ERROR                   = 1 << 13;

        /// Does this have any region that "appears free" in the type?
        /// Basically anything but `ReLateBound` and `ReErased`.
        const HAS_FREE_REGIONS            = 1 << 14;

        /// Does this have any `ReLateBound` regions? Used to check
        /// if a global bound is safe to evaluate.
        const HAS_RE_LATE_BOUND           = 1 << 15;

        /// Does this have any `ReErased` regions?
        const HAS_RE_ERASED               = 1 << 16;

        /// Does this value have parameters/placeholders/inference variables which could be
        /// replaced later, in a way that would change the results of `impl` specialization?
        const STILL_FURTHER_SPECIALIZABLE = 1 << 17;
    }
}

rustc_index::newtype_index! {
    /// A [De Bruijn index][dbi] is a standard means of representing
    /// regions (and perhaps later types) in a higher-ranked setting. In
    /// particular, imagine a type like this:
    ///
    ///     for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char)
    ///     ^          ^            |          |           |
    ///     |          |            |          |           |
    ///     |          +------------+ 0        |           |
    ///     |                                  |           |
    ///     +----------------------------------+ 1         |
    ///     |                                              |
    ///     +----------------------------------------------+ 0
    ///
    /// In this type, there are two binders (the outer fn and the inner
    /// fn). We need to be able to determine, for any given region, which
    /// fn type it is bound by, the inner or the outer one. There are
    /// various ways you can do this, but a De Bruijn index is one of the
    /// more convenient and has some nice properties. The basic idea is to
    /// count the number of binders, inside out. Some examples should help
    /// clarify what I mean.
    ///
    /// Let's start with the reference type `&'b isize` that is the first
    /// argument to the inner function. This region `'b` is assigned a De
    /// Bruijn index of 0, meaning "the innermost binder" (in this case, a
    /// fn). The region `'a` that appears in the second argument type (`&'a
    /// isize`) would then be assigned a De Bruijn index of 1, meaning "the
    /// second-innermost binder". (These indices are written on the arrows
    /// in the diagram).
    ///
    /// What is interesting is that De Bruijn index attached to a particular
    /// variable will vary depending on where it appears. For example,
    /// the final type `&'a char` also refers to the region `'a` declared on
    /// the outermost fn. But this time, this reference is not nested within
    /// any other binders (i.e., it is not an argument to the inner fn, but
    /// rather the outer one). Therefore, in this case, it is assigned a
    /// De Bruijn index of 0, because the innermost binder in that location
    /// is the outer fn.
    ///
    /// [dbi]: https://en.wikipedia.org/wiki/De_Bruijn_index
    pub struct DebruijnIndex {
        DEBUG_FORMAT = "DebruijnIndex({})",
        const INNERMOST = 0,
    }
}

impl DebruijnIndex {
    /// Returns the resulting index when this value is moved into
    /// `amount` number of new binders. So, e.g., if you had
    ///
    ///    for<'a> fn(&'a x)
    ///
    /// and you wanted to change it to
    ///
    ///    for<'a> fn(for<'b> fn(&'a x))
    ///
    /// you would need to shift the index for `'a` into a new binder.
    #[must_use]
    pub fn shifted_in(self, amount: u32) -> DebruijnIndex {
        DebruijnIndex::from_u32(self.as_u32() + amount)
    }

    /// Update this index in place by shifting it "in" through
    /// `amount` number of binders.
    pub fn shift_in(&mut self, amount: u32) {
        *self = self.shifted_in(amount);
    }

    /// Returns the resulting index when this value is moved out from
    /// `amount` number of new binders.
    #[must_use]
    pub fn shifted_out(self, amount: u32) -> DebruijnIndex {
        DebruijnIndex::from_u32(self.as_u32() - amount)
    }

    /// Update in place by shifting out from `amount` binders.
    pub fn shift_out(&mut self, amount: u32) {
        *self = self.shifted_out(amount);
    }

    /// Adjusts any De Bruijn indices so as to make `to_binder` the
    /// innermost binder. That is, if we have something bound at `to_binder`,
    /// it will now be bound at INNERMOST. This is an appropriate thing to do
    /// when moving a region out from inside binders:
    ///
    /// ```
    ///             for<'a>   fn(for<'b>   for<'c>   fn(&'a u32), _)
    /// // Binder:  D3           D2        D1            ^^
    /// ```
    ///
    /// Here, the region `'a` would have the De Bruijn index D3,
    /// because it is the bound 3 binders out. However, if we wanted
    /// to refer to that region `'a` in the second argument (the `_`),
    /// those two binders would not be in scope. In that case, we
    /// might invoke `shift_out_to_binder(D3)`. This would adjust the
    /// De Bruijn index of `'a` to D1 (the innermost binder).
    ///
    /// If we invoke `shift_out_to_binder` and the region is in fact
    /// bound by one of the binders we are shifting out of, that is an
    /// error (and should fail an assertion failure).
    pub fn shifted_out_to_binder(self, to_binder: DebruijnIndex) -> Self {
        self.shifted_out(to_binder.as_u32() - INNERMOST.as_u32())
    }
}

impl<CTX> HashStable<CTX> for DebruijnIndex {
    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
        self.as_u32().hash_stable(ctx, hasher);
    }
}
<|endoftext|>