<commit_before>use std::error::Error;

use mio::Token;

use {Response, Time};

#[derive(Debug)]
pub enum ResponseImpl<M, N> {
    Normal(M),
    Deadline(M, Time),
    Spawn(M, N),
    Error(Box<Error>),
    Done,
}

impl<M: Sized, N:Sized> Response<M, N> {
    pub fn ok(machine: M) -> Response<M, N> {
        Response(ResponseImpl::Normal(machine))
    }
    pub fn spawn(machine: M, result: N) -> Response<M, N> {
        Response(ResponseImpl::Spawn(machine, result))
    }
    pub fn done() -> Response<M, N> {
        Response::<M, N>(ResponseImpl::Done)
    }
    pub fn error(e: Box<Error>) -> Response<M, N> {
        Response::<M, N>(ResponseImpl::Error(e))
    }
    pub fn deadline(self, time: Time) -> Response<M, N> {
        let imp = match self.0 {
            ResponseImpl::Normal(x) => ResponseImpl::Deadline(x, time),
            ResponseImpl::Deadline(x, _) => ResponseImpl::Deadline(x, time),
            ResponseImpl::Spawn(..) => {
                panic!("You can't attach a deadline/timeout to the \
                    Response::spawn(). The `spawn` action is synchronous \
                    you must set a deadline in the `spawned` handler."); }
            ResponseImpl::Done => {
                panic!("You can't attach a deadline/timeout to \
                    Response::done() as it's useless. \
                    Timeout will never happen");
            }
            ResponseImpl::Error(_) => {
                panic!("You can't attach a deadline/timeout to \
                    Response::error(_) as it's useless. \
                    Timeout will never happen");
            }
        };
        Response(imp)
    }
    /// Maps state machine and/or spawned result with a function
    ///
    /// Usually it's okay to use constructor of wrapper state machine
    /// here as a mapper
    pub fn map<T, U,  S, R>(self, self_mapper: S, result_mapper: R)
        -> Response<T, U>
        where S: FnOnce(M) -> T,
              R: FnOnce(N) -> U,
    {
        use self::ResponseImpl::*;
        let imp = match self.0 {
            Normal(m) => Normal(self_mapper(m)),
            Deadline(m, time) => Deadline(self_mapper(m), time),
            Spawn(m, n) => Spawn(self_mapper(m), result_mapper(n)),
            Done => Done,
            Error(e) => Error(e),
        };
        Response(imp)
    }
    /// Similar to `map` but only maps state machine
    ///
    /// This is especially useful in state machine constructors, which
    /// have a Void child type.
    pub fn wrap<T, S>(self, self_mapper: S) -> Response<T, N>
        where S: FnOnce(M) -> T
    {
        use self::ResponseImpl::*;
        let imp = match self.0 {
            Normal(m) => Normal(self_mapper(m)),
            Deadline(m, time) => Deadline(self_mapper(m), time),
            Spawn(m, n) => Spawn(self_mapper(m), n),
            Done => Done,
            Error(e) => Error(e),
        };
        Response(imp)
    }
}

pub fn decompose<M, N>(token: Token, res: Response<M, N>)
    -> (Result<M, Option<Box<Error>>>, Option<N>, Option<Time>)
{
    match res.0 {
        ResponseImpl::Normal(m) => (Ok(m), None, None),
        ResponseImpl::Deadline(m, time) => (Ok(m), None, Some(time)),
        ResponseImpl::Spawn(m, n) => (Ok(m), Some(n), None),
        ResponseImpl::Done => (Err(None), None, None),
        ResponseImpl::Error(e) => {
            if cfg!(log_errors) {
                warn!("State machine {:?} exited with error: {}", token, e);
            }
            (Err(Some(e)), None, None)
        }
    }
}

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

    #[test]
    fn size_of_response() {
        assert_eq!(::std::mem::size_of::<Response<u64, u64>>(), 24)
    }
}
<commit_msg>Add few methods for introspection of `rotor::Response`<commit_after>use std::error::Error;

use mio::Token;

use {Response, Time};

#[derive(Debug)]
pub enum ResponseImpl<M, N> {
    Normal(M),
    Deadline(M, Time),
    Spawn(M, N),
    Error(Box<Error>),
    Done,
}

impl<M: Sized, N:Sized> Response<M, N> {
    pub fn ok(machine: M) -> Response<M, N> {
        Response(ResponseImpl::Normal(machine))
    }
    pub fn spawn(machine: M, result: N) -> Response<M, N> {
        Response(ResponseImpl::Spawn(machine, result))
    }
    pub fn done() -> Response<M, N> {
        Response::<M, N>(ResponseImpl::Done)
    }
    pub fn error(e: Box<Error>) -> Response<M, N> {
        Response::<M, N>(ResponseImpl::Error(e))
    }
    pub fn deadline(self, time: Time) -> Response<M, N> {
        let imp = match self.0 {
            ResponseImpl::Normal(x) => ResponseImpl::Deadline(x, time),
            ResponseImpl::Deadline(x, _) => ResponseImpl::Deadline(x, time),
            ResponseImpl::Spawn(..) => {
                panic!("You can't attach a deadline/timeout to the \
                    Response::spawn(). The `spawn` action is synchronous \
                    you must set a deadline in the `spawned` handler."); }
            ResponseImpl::Done => {
                panic!("You can't attach a deadline/timeout to \
                    Response::done() as it's useless. \
                    Timeout will never happen");
            }
            ResponseImpl::Error(_) => {
                panic!("You can't attach a deadline/timeout to \
                    Response::error(_) as it's useless. \
                    Timeout will never happen");
            }
        };
        Response(imp)
    }
    /// Maps state machine and/or spawned result with a function
    ///
    /// Usually it's okay to use constructor of wrapper state machine
    /// here as a mapper
    pub fn map<T, U,  S, R>(self, self_mapper: S, result_mapper: R)
        -> Response<T, U>
        where S: FnOnce(M) -> T,
              R: FnOnce(N) -> U,
    {
        use self::ResponseImpl::*;
        let imp = match self.0 {
            Normal(m) => Normal(self_mapper(m)),
            Deadline(m, time) => Deadline(self_mapper(m), time),
            Spawn(m, n) => Spawn(self_mapper(m), result_mapper(n)),
            Done => Done,
            Error(e) => Error(e),
        };
        Response(imp)
    }
    /// Similar to `map` but only maps state machine
    ///
    /// This is especially useful in state machine constructors, which
    /// have a Void child type.
    pub fn wrap<T, S>(self, self_mapper: S) -> Response<T, N>
        where S: FnOnce(M) -> T
    {
        use self::ResponseImpl::*;
        let imp = match self.0 {
            Normal(m) => Normal(self_mapper(m)),
            Deadline(m, time) => Deadline(self_mapper(m), time),
            Spawn(m, n) => Spawn(self_mapper(m), n),
            Done => Done,
            Error(e) => Error(e),
        };
        Response(imp)
    }

    /// Returns true if state machine is stopped
    ///
    /// I.e. the method returns true if the `Response` was created either with
    /// `Response::done` or `Response::error`
    pub fn is_stopped(&self) -> bool {
        use self::ResponseImpl::*;
        match self.0 {
            Normal(..) => false,
            Deadline(..) => false,
            Spawn(..) => false,
            Done => true,
            Error(..) => true,
        }
    }

    /// Return a reference to an error passed to `Response::error`
    ///
    /// Returns None if any other constructor was used.
    ///
    /// This is mostly useful for printing the error.
    pub fn cause(&self) -> Option<&Error> {
        use self::ResponseImpl::*;
        match self.0 {
            Normal(..) => None,
            Deadline(..) => None,
            Spawn(..) => None,
            Done => None,
            Error(ref e) => Some(&**e),
        }
    }
}

pub fn decompose<M, N>(token: Token, res: Response<M, N>)
    -> (Result<M, Option<Box<Error>>>, Option<N>, Option<Time>)
{
    match res.0 {
        ResponseImpl::Normal(m) => (Ok(m), None, None),
        ResponseImpl::Deadline(m, time) => (Ok(m), None, Some(time)),
        ResponseImpl::Spawn(m, n) => (Ok(m), Some(n), None),
        ResponseImpl::Done => (Err(None), None, None),
        ResponseImpl::Error(e) => {
            if cfg!(log_errors) {
                warn!("State machine {:?} exited with error: {}", token, e);
            }
            (Err(Some(e)), None, None)
        }
    }
}

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

    #[test]
    fn size_of_response() {
        assert_eq!(::std::mem::size_of::<Response<u64, u64>>(), 24)
    }
}
<|endoftext|>


<commit_before>use serde::Deserialize;
use std::collections::HashMap;
use std::os::unix::fs::MetadataExt;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Instant;
use structopt::StructOpt;
use walkdir::WalkDir;
use watchman_client::prelude::*;

#[derive(StructOpt, Debug)]
#[structopt(about = "Audit Watchman's in-memory database with the filesystem")]
pub(crate) struct AuditCmd {
    #[structopt(name = "path", parse(from_os_str))]
    path: PathBuf,
}

query_result_type! {
    struct AuditQueryResult {
        name: NameField,
        mode: ModeAndPermissionsField,
        size: SizeField,
        mtime: MTimeField,
        oclock: ObservedClockField,
        ino: InodeNumberField,
    }
}

impl AuditCmd {
    pub(crate) async fn run(&self) -> crate::Result<()> {
        let client = Connector::new().connect().await?;
        let resolved = Arc::new(
            client
                .resolve_root(CanonicalPath::canonicalize(&self.path)?)
                .await?,
        );

        if resolved.watcher() == "eden" {
            return Err(watchman_client::Error::Generic(format!(
                "{} is an EdenFS mount - no need to audit",
                resolved.project_root().display()
            )));
        }

        let config = client.get_config(&resolved).await?;
        let mut ignore_dirs = config.ignore_dirs.unwrap_or_default().clone();

        // TODO: This list is duplicated in the Watchman query below.
        ignore_dirs.push(".hg".into());
        ignore_dirs.push(".git".into());
        ignore_dirs.push(".svn".into());

        let filesystem_state_handle = {
            let resolved = resolved.clone();
            tokio::spawn(async move {
                let mut filesystem_state: HashMap<PathBuf, std::fs::Metadata> = HashMap::new();

                let start_crawl = Instant::now();

                // Allocate outside the loop to save time.
                let resolved_path = resolved.path();

                for entry in WalkDir::new(&resolved_path)
                    .into_iter()
                    .filter_entry(|entry| {
                        // It sucks we have to do this here and in the loop body below. It would be nice if traversal and filtering were folded into the same callback.
                        let from_root = match entry.path().strip_prefix(&resolved_path) {
                            Ok(from_root) => from_root,
                            Err(_) => return true,
                        };
                        let join_storage;
                        let path_from_root = match resolved.project_relative_path() {
                            Some(relpath) => {
                                join_storage = relpath.join(&from_root);
                                join_storage.as_ref()
                            }
                            None => from_root,
                        };
                        !ignore_dirs.iter().any(|i| i == path_from_root)
                    })
                {
                    let entry = match entry {
                        Ok(entry) => entry,
                        Err(err) => {
                            eprintln!("error while traversing directory: {}", err);
                            continue;
                        }
                    };

                    let relpath = match entry.path().strip_prefix(&resolved_path) {
                        Ok(relpath) => relpath,
                        Err(err) => {
                            eprintln!(
                                "unable to form relative path from {} to {}: {}",
                                resolved_path.display(),
                                entry.path().display(),
                                err
                            );
                            continue;
                        }
                    };

                    let metadata = match entry.metadata() {
                        Ok(metadata) => metadata,
                        Err(err) => {
                            eprintln!(
                                "error fetching metadata for {}: {}",
                                entry.path().display(),
                                err
                            );
                            continue;
                        }
                    };
                    filesystem_state.insert(relpath.to_path_buf(), metadata);
                }
                // Watchman doesn't return information about the root, so remove it here.
                filesystem_state.remove(&PathBuf::new());
                eprintln!("Crawled filesystem in {:#?}", start_crawl.elapsed());

                filesystem_state
            })
        };

        let start_query = Instant::now();

        // TODO: should we ignore fresh instance results?

        use Expr::*;
        let result = client
            .query::<AuditQueryResult>(
                &resolved,
                QueryRequestCommon {
                    expression: Some(All(vec![
                        Exists,
                        Not(Box::new(Any(vec![
                            DirName(DirNameTerm {
                                path: ".git".into(),
                                depth: None,
                            }),
                            DirName(DirNameTerm {
                                path: ".hg".into(),
                                depth: None,
                            }),
                            DirName(DirNameTerm {
                                path: ".svn".into(),
                                depth: None,
                            }),
                            Name(NameTerm {
                                paths: vec![".git".into()],
                                wholename: true,
                            }),
                            Name(NameTerm {
                                paths: vec![".svn".into()],
                                wholename: true,
                            }),
                            Name(NameTerm {
                                paths: vec![".hg".into()],
                                wholename: true,
                            }),
                        ]))),
                    ])),
                    ..Default::default()
                },
            )
            .await?;

        eprintln!("Queried Watchman in {:#?}", start_query.elapsed());

        let filesystem_state = filesystem_state_handle.await.unwrap();

        let diff_start = Instant::now();

        let watchman_files = match result.files {
            Some(files) => files,
            None => {
                return Err(watchman_client::Error::Generic(
                    "No files set in result {}".into(),
                ));
            }
        };

        let mut any_differences = false;
        let mut phantoms = vec![];
        let mut missing = vec![];

        let mut watchman_state: HashMap<&Path, &AuditQueryResult> = HashMap::new();
        for watchman_file in &watchman_files {
            let filename = &*watchman_file.name;
            watchman_state.insert(filename, watchman_file);

            let metadata = match filesystem_state.get(filename) {
                Some(metadata) => metadata,
                None => {
                    phantoms.push(watchman_file);
                    continue;
                }
            };

            let mut diffs = Vec::new();

            if *watchman_file.mode != u64::from(metadata.permissions().mode()) {
                diffs.push(format!(
                    "watchman mode is {} vs. fs {}",
                    *watchman_file.mode,
                    metadata.permissions().mode()
                ));
            }

            if *watchman_file.size != metadata.size() {
                diffs.push(format!(
                    "watchman size is {} vs. fs {}",
                    *watchman_file.size,
                    metadata.len()
                ));
            }

            if *watchman_file.mtime != metadata.mtime() {
                diffs.push(format!(
                    "watchman mtime is {} vs. fs {}",
                    *watchman_file.mtime,
                    metadata.mtime()
                ));
            }

            if *watchman_file.ino != metadata.ino() {
                diffs.push(format!(
                    "watchman ino is {} vs. fs {}",
                    *watchman_file.ino,
                    metadata.ino()
                ));
            }

            if !diffs.is_empty() {
                println!(
                    "Conflicting information for {}:",
                    watchman_file.name.display()
                );
                println!("  oclock is {:#?}", *watchman_file.oclock);
                for diff in diffs {
                    println!("  {}", diff);
                }
                any_differences = true;
            }
        }

        for (path, val) in filesystem_state {
            if !watchman_state.contains_key(&path.as_path()) {
                missing.push((path, val));
            }
        }

        phantoms.sort_by(|x, y| x.name.cmp(&y.name));
        missing.sort_by(|x, y| x.0.cmp(&y.0));

        if !phantoms.is_empty() {
            println!(
                "There are {} items reported by watchman not on the filesystem:",
                phantoms.len()
            );
            for phantom in phantoms {
                println!("  {}", phantom.name.display());
            }
            any_differences = true;
        }

        if !missing.is_empty() {
            println!(
                "There are {} items on the filesystem not reported by watchman:",
                missing.len()
            );
            for (path, _) in missing {
                println!("  {}", path.display());
            }
            any_differences = true;
        }

        if any_differences {
            // This is dumb, but Rust doesn't have a standard way to return
            // nonzero exit codes yet.
            std::process::exit(1);
        }
        eprintln!("Diffed in {:#?}", diff_start.elapsed());
        Ok(())
    }
}
<commit_msg>audit: only compare sizes on files<commit_after>use serde::Deserialize;
use std::collections::HashMap;
use std::os::unix::fs::MetadataExt;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Instant;
use structopt::StructOpt;
use walkdir::WalkDir;
use watchman_client::prelude::*;

#[derive(StructOpt, Debug)]
#[structopt(about = "Audit Watchman's in-memory database with the filesystem")]
pub(crate) struct AuditCmd {
    #[structopt(name = "path", parse(from_os_str))]
    path: PathBuf,
}

query_result_type! {
    struct AuditQueryResult {
        name: NameField,
        mode: ModeAndPermissionsField,
        size: SizeField,
        mtime: MTimeField,
        oclock: ObservedClockField,
        ino: InodeNumberField,
    }
}

impl AuditCmd {
    pub(crate) async fn run(&self) -> crate::Result<()> {
        let client = Connector::new().connect().await?;
        let resolved = Arc::new(
            client
                .resolve_root(CanonicalPath::canonicalize(&self.path)?)
                .await?,
        );

        if resolved.watcher() == "eden" {
            return Err(watchman_client::Error::Generic(format!(
                "{} is an EdenFS mount - no need to audit",
                resolved.project_root().display()
            )));
        }

        let config = client.get_config(&resolved).await?;
        let mut ignore_dirs = config.ignore_dirs.unwrap_or_default().clone();

        // TODO: This list is duplicated in the Watchman query below.
        ignore_dirs.push(".hg".into());
        ignore_dirs.push(".git".into());
        ignore_dirs.push(".svn".into());

        let filesystem_state_handle = {
            let resolved = resolved.clone();
            tokio::spawn(async move {
                let mut filesystem_state: HashMap<PathBuf, std::fs::Metadata> = HashMap::new();

                let start_crawl = Instant::now();

                // Allocate outside the loop to save time.
                let resolved_path = resolved.path();

                for entry in WalkDir::new(&resolved_path)
                    .into_iter()
                    .filter_entry(|entry| {
                        // It sucks we have to do this here and in the loop body below. It would be nice if traversal and filtering were folded into the same callback.
                        let from_root = match entry.path().strip_prefix(&resolved_path) {
                            Ok(from_root) => from_root,
                            Err(_) => return true,
                        };
                        let join_storage;
                        let path_from_root = match resolved.project_relative_path() {
                            Some(relpath) => {
                                join_storage = relpath.join(&from_root);
                                join_storage.as_ref()
                            }
                            None => from_root,
                        };
                        !ignore_dirs.iter().any(|i| i == path_from_root)
                    })
                {
                    let entry = match entry {
                        Ok(entry) => entry,
                        Err(err) => {
                            eprintln!("error while traversing directory: {}", err);
                            continue;
                        }
                    };

                    let relpath = match entry.path().strip_prefix(&resolved_path) {
                        Ok(relpath) => relpath,
                        Err(err) => {
                            eprintln!(
                                "unable to form relative path from {} to {}: {}",
                                resolved_path.display(),
                                entry.path().display(),
                                err
                            );
                            continue;
                        }
                    };

                    let metadata = match entry.metadata() {
                        Ok(metadata) => metadata,
                        Err(err) => {
                            eprintln!(
                                "error fetching metadata for {}: {}",
                                entry.path().display(),
                                err
                            );
                            continue;
                        }
                    };
                    filesystem_state.insert(relpath.to_path_buf(), metadata);
                }
                // Watchman doesn't return information about the root, so remove it here.
                filesystem_state.remove(&PathBuf::new());
                eprintln!("Crawled filesystem in {:#?}", start_crawl.elapsed());

                filesystem_state
            })
        };

        let start_query = Instant::now();

        // TODO: should we ignore fresh instance results?

        use Expr::*;
        let result = client
            .query::<AuditQueryResult>(
                &resolved,
                QueryRequestCommon {
                    expression: Some(All(vec![
                        Exists,
                        Not(Box::new(Any(vec![
                            DirName(DirNameTerm {
                                path: ".git".into(),
                                depth: None,
                            }),
                            DirName(DirNameTerm {
                                path: ".hg".into(),
                                depth: None,
                            }),
                            DirName(DirNameTerm {
                                path: ".svn".into(),
                                depth: None,
                            }),
                            Name(NameTerm {
                                paths: vec![".git".into()],
                                wholename: true,
                            }),
                            Name(NameTerm {
                                paths: vec![".svn".into()],
                                wholename: true,
                            }),
                            Name(NameTerm {
                                paths: vec![".hg".into()],
                                wholename: true,
                            }),
                        ]))),
                    ])),
                    ..Default::default()
                },
            )
            .await?;

        eprintln!("Queried Watchman in {:#?}", start_query.elapsed());

        let filesystem_state = filesystem_state_handle.await.unwrap();

        let diff_start = Instant::now();

        let watchman_files = match result.files {
            Some(files) => files,
            None => {
                return Err(watchman_client::Error::Generic(
                    "No files set in result {}".into(),
                ));
            }
        };

        let mut any_differences = false;
        let mut phantoms = vec![];
        let mut missing = vec![];

        let mut watchman_state: HashMap<&Path, &AuditQueryResult> = HashMap::new();
        for watchman_file in &watchman_files {
            let filename = &*watchman_file.name;
            watchman_state.insert(filename, watchman_file);

            let metadata = match filesystem_state.get(filename) {
                Some(metadata) => metadata,
                None => {
                    phantoms.push(watchman_file);
                    continue;
                }
            };

            let mut diffs = Vec::new();

            if *watchman_file.mode != u64::from(metadata.permissions().mode()) {
                diffs.push(format!(
                    "watchman mode is {} vs. fs {}",
                    *watchman_file.mode,
                    metadata.permissions().mode()
                ));
            }

            if metadata.is_file() && *watchman_file.size != metadata.size() {
                diffs.push(format!(
                    "watchman size is {} vs. fs {}",
                    *watchman_file.size,
                    metadata.len()
                ));
            }

            if metadata.is_file() && *watchman_file.mtime != metadata.mtime() {
                diffs.push(format!(
                    "watchman mtime is {} vs. fs {}",
                    *watchman_file.mtime,
                    metadata.mtime()
                ));
            }

            if *watchman_file.ino != metadata.ino() {
                diffs.push(format!(
                    "watchman ino is {} vs. fs {}",
                    *watchman_file.ino,
                    metadata.ino()
                ));
            }

            if !diffs.is_empty() {
                println!(
                    "Conflicting information for {}:",
                    watchman_file.name.display()
                );
                println!("  oclock is {:#?}", *watchman_file.oclock);
                for diff in diffs {
                    println!("  {}", diff);
                }
                any_differences = true;
            }
        }

        for (path, val) in filesystem_state {
            if !watchman_state.contains_key(&path.as_path()) {
                missing.push((path, val));
            }
        }

        phantoms.sort_by(|x, y| x.name.cmp(&y.name));
        missing.sort_by(|x, y| x.0.cmp(&y.0));

        if !phantoms.is_empty() {
            println!(
                "There are {} items reported by watchman not on the filesystem:",
                phantoms.len()
            );
            for phantom in phantoms {
                println!("  {}", phantom.name.display());
            }
            any_differences = true;
        }

        if !missing.is_empty() {
            println!(
                "There are {} items on the filesystem not reported by watchman:",
                missing.len()
            );
            for (path, _) in missing {
                println!("  {}", path.display());
            }
            any_differences = true;
        }

        if any_differences {
            // This is dumb, but Rust doesn't have a standard way to return
            // nonzero exit codes yet.
            std::process::exit(1);
        }
        eprintln!("Diffed in {:#?}", diff_start.elapsed());
        Ok(())
    }
}
<|endoftext|>


<commit_before>// This is a part of Chrono.
// See README.md and LICENSE.txt for details.

//! The time zone which has a fixed offset from UTC.

use std::ops::{Add, Sub};
use std::fmt;
use oldtime::Duration as OldDuration;

use Timelike;
use div::div_mod_floor;
use naive::{NaiveTime, NaiveDate, NaiveDateTime};
use DateTime;
use super::{TimeZone, Offset, LocalResult};

/// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59.
///
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
/// on a `FixedOffset` struct is the preferred way to construct
/// `DateTime<FixedOffset>` instances. See the [`east`](#method.east) and
/// [`west`](#method.west) methods for examples.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct FixedOffset {
    local_minus_utc: i32,
}

impl FixedOffset {
    /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
    /// The negative `secs` means the Western Hemisphere.
    ///
    /// Panics on the out-of-bound `secs`.
    ///
    /// # Example
    ///
    /// ~~~~
    /// use chrono::{FixedOffset, TimeZone};
    /// let hour = 3600;
    /// let datetime = FixedOffset::east(5 * hour).ymd(2016, 11, 08)
    ///                                           .and_hms(0, 0, 0);
    /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00")
    /// ~~~~
    pub fn east(secs: i32) -> FixedOffset {
        FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
    }

    /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
    /// The negative `secs` means the Western Hemisphere.
    ///
    /// Returns `None` on the out-of-bound `secs`.
    pub fn east_opt(secs: i32) -> Option<FixedOffset> {
        if -86_400 < secs && secs < 86_400 {
            Some(FixedOffset { local_minus_utc: secs })
        } else {
            None
        }
    }

    /// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
    /// The negative `secs` means the Eastern Hemisphere.
    ///
    /// Panics on the out-of-bound `secs`.
    ///
    /// # Example
    ///
    /// ~~~~
    /// use chrono::{FixedOffset, TimeZone};
    /// let hour = 3600;
    /// let datetime = FixedOffset::west(5 * hour).ymd(2016, 11, 08)
    ///                                           .and_hms(0, 0, 0);
    /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00")
    /// ~~~~
    pub fn west(secs: i32) -> FixedOffset {
        FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
    }

    /// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
    /// The negative `secs` means the Eastern Hemisphere.
    ///
    /// Returns `None` on the out-of-bound `secs`.
    pub fn west_opt(secs: i32) -> Option<FixedOffset> {
        if -86_400 < secs && secs < 86_400 {
            Some(FixedOffset { local_minus_utc: -secs })
        } else {
            None
        }
    }

    /// Returns the number of seconds to add to convert from UTC to the local time.
    pub fn local_minus_utc(&self) -> i32 {
        self.local_minus_utc
    }

    /// Returns the number of seconds to add to convert from the local time to UTC.
    pub fn utc_minus_local(&self) -> i32 {
        -self.local_minus_utc
    }
}

impl TimeZone for FixedOffset {
    type Offset = FixedOffset;

    fn from_offset(offset: &FixedOffset) -> FixedOffset { *offset }

    fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<FixedOffset> {
        LocalResult::Single(*self)
    }
    fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<FixedOffset> {
        LocalResult::Single(*self)
    }

    fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset { *self }
    fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset { *self }
}

impl Offset for FixedOffset {
    fn fix(&self) -> FixedOffset { *self }
}

impl fmt::Debug for FixedOffset {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let offset = self.local_minus_utc;
        let (sign, offset) = if offset < 0 {('-', -offset)} else {('+', offset)};
        let (mins, sec) = div_mod_floor(offset, 60);
        let (hour, min) = div_mod_floor(mins, 60);
        if sec == 0 {
            write!(f, "{}{:02}:{:02}", sign, hour, min)
        } else {
            write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec)
        }
    }
}

impl fmt::Display for FixedOffset {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
}

// addition or subtraction of FixedOffset to/from Timelike values is same to
// adding or subtracting the offset's local_minus_utc value
// but keep keeps the leap second information.
// this should be implemented more efficiently, but for the time being, this is generic right now.

fn add_with_leapsecond<T>(lhs: &T, rhs: i32) -> T
    where T: Timelike + Add<OldDuration, Output=T>
{
    // extract and temporarily remove the fractional part and later recover it
    let nanos = lhs.nanosecond();
    let lhs = lhs.with_nanosecond(0).unwrap();
    (lhs + OldDuration::seconds(i64::from(rhs))).with_nanosecond(nanos).unwrap()
}

impl Add<FixedOffset> for NaiveTime {
    type Output = NaiveTime;

    #[inline]
    fn add(self, rhs: FixedOffset) -> NaiveTime {
        add_with_leapsecond(&self, rhs.local_minus_utc)
    }
}

impl Sub<FixedOffset> for NaiveTime {
    type Output = NaiveTime;

    #[inline]
    fn sub(self, rhs: FixedOffset) -> NaiveTime {
        add_with_leapsecond(&self, -rhs.local_minus_utc)
    }
}

impl Add<FixedOffset> for NaiveDateTime {
    type Output = NaiveDateTime;

    #[inline]
    fn add(self, rhs: FixedOffset) -> NaiveDateTime {
        add_with_leapsecond(&self, rhs.local_minus_utc)
    }
}

impl Sub<FixedOffset> for NaiveDateTime {
    type Output = NaiveDateTime;

    #[inline]
    fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
        add_with_leapsecond(&self, -rhs.local_minus_utc)
    }
}

impl<Tz: TimeZone> Add<FixedOffset> for DateTime<Tz> {
    type Output = DateTime<Tz>;

    #[inline]
    fn add(self, rhs: FixedOffset) -> DateTime<Tz> {
        add_with_leapsecond(&self, rhs.local_minus_utc)
    }
}

impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
    type Output = DateTime<Tz>;

    #[inline]
    fn sub(self, rhs: FixedOffset) -> DateTime<Tz> {
        add_with_leapsecond(&self, -rhs.local_minus_utc)
    }
}

#[cfg(test)]
mod tests {
    use offset::TimeZone;
    use super::FixedOffset;

    #[test]
    fn test_date_extreme_offset() {
        // starting from 0.3 we don't have an offset exceeding one day.
        // this makes everything easier!
        assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)),
                   "2012-02-29+23:59:59".to_string());
        assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)),
                   "2012-02-29T05:06:07+23:59:59".to_string());
        assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)),
                   "2012-03-04-23:59:59".to_string());
        assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)),
                   "2012-03-04T05:06:07-23:59:59".to_string());
    }
}

<commit_msg>Update fixed.rs<commit_after>// This is a part of Chrono.
// See README.md and LICENSE.txt for details.

//! The time zone which has a fixed offset from UTC.

use std::ops::{Add, Sub};
use std::fmt;
use oldtime::Duration as OldDuration;

use Timelike;
use div::div_mod_floor;
use naive::{NaiveTime, NaiveDate, NaiveDateTime};
use DateTime;
use super::{TimeZone, Offset, LocalResult};

/// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59.
///
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
/// on a `FixedOffset` struct is the preferred way to construct
/// `DateTime<FixedOffset>` instances. See the [`east`](#method.east) and
/// [`west`](#method.west) methods for examples.
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
pub struct FixedOffset {
    local_minus_utc: i32,
}

impl FixedOffset {
    /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
    /// The negative `secs` means the Western Hemisphere.
    ///
    /// Panics on the out-of-bound `secs`.
    ///
    /// # Example
    ///
    /// ~~~~
    /// use chrono::{FixedOffset, TimeZone};
    /// let hour = 3600;
    /// let datetime = FixedOffset::east(5 * hour).ymd(2016, 11, 08)
    ///                                           .and_hms(0, 0, 0);
    /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00")
    /// ~~~~
    pub fn east(secs: i32) -> FixedOffset {
        FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
    }

    /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
    /// The negative `secs` means the Western Hemisphere.
    ///
    /// Returns `None` on the out-of-bound `secs`.
    pub fn east_opt(secs: i32) -> Option<FixedOffset> {
        if -86_400 < secs && secs < 86_400 {
            Some(FixedOffset { local_minus_utc: secs })
        } else {
            None
        }
    }

    /// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
    /// The negative `secs` means the Eastern Hemisphere.
    ///
    /// Panics on the out-of-bound `secs`.
    ///
    /// # Example
    ///
    /// ~~~~
    /// use chrono::{FixedOffset, TimeZone};
    /// let hour = 3600;
    /// let datetime = FixedOffset::west(5 * hour).ymd(2016, 11, 08)
    ///                                           .and_hms(0, 0, 0);
    /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00")
    /// ~~~~
    pub fn west(secs: i32) -> FixedOffset {
        FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
    }

    /// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
    /// The negative `secs` means the Eastern Hemisphere.
    ///
    /// Returns `None` on the out-of-bound `secs`.
    pub fn west_opt(secs: i32) -> Option<FixedOffset> {
        if -86_400 < secs && secs < 86_400 {
            Some(FixedOffset { local_minus_utc: -secs })
        } else {
            None
        }
    }

    /// Returns the number of seconds to add to convert from UTC to the local time.
    pub fn local_minus_utc(&self) -> i32 {
        self.local_minus_utc
    }

    /// Returns the number of seconds to add to convert from the local time to UTC.
    pub fn utc_minus_local(&self) -> i32 {
        -self.local_minus_utc
    }
}

impl TimeZone for FixedOffset {
    type Offset = FixedOffset;

    fn from_offset(offset: &FixedOffset) -> FixedOffset { *offset }

    fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<FixedOffset> {
        LocalResult::Single(*self)
    }
    fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<FixedOffset> {
        LocalResult::Single(*self)
    }

    fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset { *self }
    fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset { *self }
}

impl Offset for FixedOffset {
    fn fix(&self) -> FixedOffset { *self }
}

impl fmt::Debug for FixedOffset {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let offset = self.local_minus_utc;
        let (sign, offset) = if offset < 0 {('-', -offset)} else {('+', offset)};
        let (mins, sec) = div_mod_floor(offset, 60);
        let (hour, min) = div_mod_floor(mins, 60);
        if sec == 0 {
            write!(f, "{}{:02}:{:02}", sign, hour, min)
        } else {
            write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec)
        }
    }
}

impl fmt::Display for FixedOffset {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
}

// addition or subtraction of FixedOffset to/from Timelike values is same to
// adding or subtracting the offset's local_minus_utc value
// but keep keeps the leap second information.
// this should be implemented more efficiently, but for the time being, this is generic right now.

fn add_with_leapsecond<T>(lhs: &T, rhs: i32) -> T
    where T: Timelike + Add<OldDuration, Output=T>
{
    // extract and temporarily remove the fractional part and later recover it
    let nanos = lhs.nanosecond();
    let lhs = lhs.with_nanosecond(0).unwrap();
    (lhs + OldDuration::seconds(i64::from(rhs))).with_nanosecond(nanos).unwrap()
}

impl Add<FixedOffset> for NaiveTime {
    type Output = NaiveTime;

    #[inline]
    fn add(self, rhs: FixedOffset) -> NaiveTime {
        add_with_leapsecond(&self, rhs.local_minus_utc)
    }
}

impl Sub<FixedOffset> for NaiveTime {
    type Output = NaiveTime;

    #[inline]
    fn sub(self, rhs: FixedOffset) -> NaiveTime {
        add_with_leapsecond(&self, -rhs.local_minus_utc)
    }
}

impl Add<FixedOffset> for NaiveDateTime {
    type Output = NaiveDateTime;

    #[inline]
    fn add(self, rhs: FixedOffset) -> NaiveDateTime {
        add_with_leapsecond(&self, rhs.local_minus_utc)
    }
}

impl Sub<FixedOffset> for NaiveDateTime {
    type Output = NaiveDateTime;

    #[inline]
    fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
        add_with_leapsecond(&self, -rhs.local_minus_utc)
    }
}

impl<Tz: TimeZone> Add<FixedOffset> for DateTime<Tz> {
    type Output = DateTime<Tz>;

    #[inline]
    fn add(self, rhs: FixedOffset) -> DateTime<Tz> {
        add_with_leapsecond(&self, rhs.local_minus_utc)
    }
}

impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
    type Output = DateTime<Tz>;

    #[inline]
    fn sub(self, rhs: FixedOffset) -> DateTime<Tz> {
        add_with_leapsecond(&self, -rhs.local_minus_utc)
    }
}

#[cfg(test)]
mod tests {
    use offset::TimeZone;
    use super::FixedOffset;

    #[test]
    fn test_date_extreme_offset() {
        // starting from 0.3 we don't have an offset exceeding one day.
        // this makes everything easier!
        assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)),
                   "2012-02-29+23:59:59".to_string());
        assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)),
                   "2012-02-29T05:06:07+23:59:59".to_string());
        assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)),
                   "2012-03-04-23:59:59".to_string());
        assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)),
                   "2012-03-04T05:06:07-23:59:59".to_string());
    }
}

<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::borrow::BorrowMut;
use gl;
use gl::types::*;
use context::{AContext, ContextOf};
use image_data::{RenderbufferFormat};
use types::GLError;

pub struct Renderbuffer {
    gl_id: GLuint
}

impl Renderbuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Renderbuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteRenderbuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

pub struct RenderbufferBuilder<C>
    where C: RenderbufferContext
{
    gl: C,
    storage_params: Option<(RenderbufferFormat, u32, u32)>
}

impl<C> RenderbufferBuilder<C>
    where C: RenderbufferContext
{
    fn new(gl: C) -> Self {
        RenderbufferBuilder {
            gl: gl,
            storage_params: None
        }
    }

    pub fn storage(mut self,
                   format: RenderbufferFormat,
                   width: u32,
                   height: u32)
        -> Self
    {
        self.storage_params = Some((format, width, height));
        self
    }

    pub fn try_unwrap(self) -> Result<Renderbuffer, GLError> {
        let gl = self.gl;
        let mut rbo = unsafe { gl.gen_renderbuffer() };

        match self.storage_params {
            Some((format, width, height)) => {
                {
                    let (mut gl_rbo, gl) = gl.bind_renderbuffer(&mut rbo);
                    gl.storage(&mut gl_rbo, format, width, height);
                }

                Ok(rbo)
            },
            None => {
                let msg = "Error building renderbuffer: no format or dimensions provided";
                Err(GLError::Message(msg.to_owned()))
            }
        }
    }

    pub fn unwrap(self) -> Renderbuffer {
        self.try_unwrap().unwrap()
    }
}

pub trait ContextRenderbufferBuilderExt: RenderbufferContext + Sized {
    fn build_renderbuffer(self) -> RenderbufferBuilder<Self>;
}

impl<'a, C: 'a> ContextRenderbufferBuilderExt for &'a mut C
    where &'a mut C: RenderbufferContext
{
    fn build_renderbuffer(self) -> RenderbufferBuilder<&'a mut C> {
        RenderbufferBuilder::new(self)
    }
}

pub unsafe trait ContextRenderbufferExt {
    unsafe fn gen_renderbuffer(&self) -> Renderbuffer {
        let mut id : GLuint = 0;

        gl::GenRenderbuffers(1, &mut id as *mut GLuint);
        dbg_gl_sanity_check! {
            GLError::InvalidValue => "`n` is negative",
            _ => "Unknown error"
        }

        Renderbuffer {
            gl_id: id
        }
    }

    fn storage(&self,
               gl_rbo: &mut RenderbufferBinding,
               format: RenderbufferFormat,
               width: u32,
               height: u32)
    {
        unsafe {
            gl::RenderbufferStorage(gl_rbo.target().gl_enum(),
                                    format.gl_enum(),
                                    width as GLint,
                                    height as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER` or `internalformat` is not an accepted format",
                GLError::InvalidValue => "`width` or `height` is less than zero or greater than `GL_MAX_RENDERBUFFER_SIZE`",
                GLError::OutOfMemory => "Unable to allocate enough memory for requested size",
                GLError::InvalidOperation => "Renderbuffer object 0 is bound",
                _ => "Unknown error"
            }
        }
    }
}

unsafe impl<B, F, P, R, T> ContextRenderbufferExt for ContextOf<B, F, P, R, T> {

}

unsafe impl<'a, B, F, P, R, T> ContextRenderbufferExt
    for &'a mut ContextOf<B, F, P, R, T>
{

}

pub trait RenderbufferContext: AContext {
    type Rest: AContext;

    fn bind_renderbuffer<'a>(self, rbo: &'a mut Renderbuffer)
        -> (RenderbufferBinding<'a>, Self::Rest);
}

impl<B, F, P, R, T> RenderbufferContext for ContextOf<B, F, P, R, T>
    where R: BorrowMut<RenderbufferBinder>
{
    type Rest = ContextOf<B, F, P, (), T>;

    fn bind_renderbuffer<'a>(self, rbo: &'a mut Renderbuffer)
        -> (RenderbufferBinding<'a>, Self::Rest)
    {
        let (mut rbo_binder, rest) = self.split_renderbuffer();
        (rbo_binder.borrow_mut().bind(rbo), rest)
    }
}

impl<'b, B, F, P, R, T> RenderbufferContext
    for &'b mut ContextOf<B, F, P, R, T>
    where R: BorrowMut<RenderbufferBinder>
{
    type Rest = ContextOf<&'b mut B, &'b mut F, &'b mut P, (), &'b mut T>;

    fn bind_renderbuffer<'a>(self, rbo: &'a mut Renderbuffer)
        -> (RenderbufferBinding<'a>, Self::Rest)
    {
        let gl = self.mut_into();
        let (rbo_binder, rest): (&mut R, _) = gl.split_renderbuffer();
        (rbo_binder.borrow_mut().bind(rbo), rest)
    }
}

gl_enum! {
    pub gl_enum RenderbufferTarget {
        Renderbuffer as RENDERBUFFER = gl::RENDERBUFFER
    }
}

pub struct RenderbufferBinding<'a> {
    phantom: PhantomData<&'a mut Renderbuffer>
}

impl<'a> RenderbufferBinding<'a> {
    fn target(&self) -> RenderbufferTarget {
        RenderbufferTarget::Renderbuffer
    }
}

pub struct RenderbufferBinder;
impl RenderbufferBinder {
    pub fn bind<'a>(&mut self, renderbuffer: &'a mut Renderbuffer)
        -> RenderbufferBinding<'a>
    {
        let binding = RenderbufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindRenderbuffer(binding.target().gl_enum(),
                                 renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<commit_msg>Refactor `RenderbufferContext`<commit_after>use std::marker::PhantomData;
use std::borrow::BorrowMut;
use gl;
use gl::types::*;
use context::{AContext, ContextOf};
use image_data::{RenderbufferFormat};
use types::GLError;

pub struct Renderbuffer {
    gl_id: GLuint
}

impl Renderbuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Renderbuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteRenderbuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

pub struct RenderbufferBuilder<C>
    where C: RenderbufferContext
{
    gl: C,
    storage_params: Option<(RenderbufferFormat, u32, u32)>
}

impl<C> RenderbufferBuilder<C>
    where C: RenderbufferContext
{
    fn new(gl: C) -> Self {
        RenderbufferBuilder {
            gl: gl,
            storage_params: None
        }
    }

    pub fn storage(mut self,
                   format: RenderbufferFormat,
                   width: u32,
                   height: u32)
        -> Self
    {
        self.storage_params = Some((format, width, height));
        self
    }

    pub fn try_unwrap(self) -> Result<Renderbuffer, GLError> {
        let gl = self.gl;
        let mut rbo = unsafe { gl.gen_renderbuffer() };

        match self.storage_params {
            Some((format, width, height)) => {
                {
                    let (mut gl_rbo, gl) = gl.bind_renderbuffer(&mut rbo);
                    gl.storage(&mut gl_rbo, format, width, height);
                }

                Ok(rbo)
            },
            None => {
                let msg = "Error building renderbuffer: no format or dimensions provided";
                Err(GLError::Message(msg.to_owned()))
            }
        }
    }

    pub fn unwrap(self) -> Renderbuffer {
        self.try_unwrap().unwrap()
    }
}

pub trait ContextRenderbufferBuilderExt: RenderbufferContext + Sized {
    fn build_renderbuffer(self) -> RenderbufferBuilder<Self>;
}

impl<'a, C: 'a> ContextRenderbufferBuilderExt for &'a mut C
    where &'a mut C: RenderbufferContext
{
    fn build_renderbuffer(self) -> RenderbufferBuilder<&'a mut C> {
        RenderbufferBuilder::new(self)
    }
}

pub unsafe trait ContextRenderbufferExt {
    unsafe fn gen_renderbuffer(&self) -> Renderbuffer {
        let mut id : GLuint = 0;

        gl::GenRenderbuffers(1, &mut id as *mut GLuint);
        dbg_gl_sanity_check! {
            GLError::InvalidValue => "`n` is negative",
            _ => "Unknown error"
        }

        Renderbuffer {
            gl_id: id
        }
    }

    fn storage(&self,
               gl_rbo: &mut RenderbufferBinding,
               format: RenderbufferFormat,
               width: u32,
               height: u32)
    {
        unsafe {
            gl::RenderbufferStorage(gl_rbo.target().gl_enum(),
                                    format.gl_enum(),
                                    width as GLint,
                                    height as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER` or `internalformat` is not an accepted format",
                GLError::InvalidValue => "`width` or `height` is less than zero or greater than `GL_MAX_RENDERBUFFER_SIZE`",
                GLError::OutOfMemory => "Unable to allocate enough memory for requested size",
                GLError::InvalidOperation => "Renderbuffer object 0 is bound",
                _ => "Unknown error"
            }
        }
    }
}

unsafe impl<B, F, P, R, T> ContextRenderbufferExt for ContextOf<B, F, P, R, T> {

}

unsafe impl<'a, B, F, P, R, T> ContextRenderbufferExt
    for &'a mut ContextOf<B, F, P, R, T>
{

}

pub trait RenderbufferContext: AContext {
    type Binder: BorrowMut<RenderbufferBinder>;
    type Rest: AContext;

    fn split_renderbuffer(self) -> (Self::Binder, Self::Rest);

    fn bind_renderbuffer<'a>(self, rbo: &'a mut Renderbuffer)
        -> (RenderbufferBinding<'a>, Self::Rest)
        where Self: Sized
    {
        let (mut binder, rest) = self.split_renderbuffer();
        (binder.borrow_mut().bind(rbo), rest)
    }
}

impl<B, F, P, R, T> RenderbufferContext for ContextOf<B, F, P, R, T>
    where R: BorrowMut<RenderbufferBinder>
{
    type Binder = R;
    type Rest = ContextOf<B, F, P, (), T>;

    fn split_renderbuffer(self) -> (Self::Binder, Self::Rest) {
        self.split_renderbuffer()
    }
}

impl<'a, B, F, P, R, T> RenderbufferContext
    for &'a mut ContextOf<B, F, P, R, T>
    where R: BorrowMut<RenderbufferBinder>
{
    type Binder = &'a mut RenderbufferBinder;
    type Rest = ContextOf<&'a mut B, &'a mut F, &'a mut P, (), &'a mut T>;

    fn split_renderbuffer(self) -> (Self::Binder, Self::Rest) {
        let gl = self.borrowed_mut();
        gl.split_renderbuffer()
    }
}

gl_enum! {
    pub gl_enum RenderbufferTarget {
        Renderbuffer as RENDERBUFFER = gl::RENDERBUFFER
    }
}

pub struct RenderbufferBinding<'a> {
    phantom: PhantomData<&'a mut Renderbuffer>
}

impl<'a> RenderbufferBinding<'a> {
    fn target(&self) -> RenderbufferTarget {
        RenderbufferTarget::Renderbuffer
    }
}

pub struct RenderbufferBinder;
impl RenderbufferBinder {
    pub fn bind<'a>(&mut self, renderbuffer: &'a mut Renderbuffer)
        -> RenderbufferBinding<'a>
    {
        let binding = RenderbufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindRenderbuffer(binding.target().gl_enum(),
                                 renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::borrow::BorrowMut;
use gl;
use gl::types::*;
use context::ContextOf;
use image_data::{RenderbufferFormat};
use types::GLError;

pub struct Renderbuffer {
    gl_id: GLuint
}

impl Renderbuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Renderbuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteRenderbuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

impl<AB, EAB, P, FB, RB, TU> ContextOf<AB, EAB, P, FB, RB, TU> {
    pub fn gen_renderbuffer(&self) -> Renderbuffer {
        unsafe {
            let mut id : GLuint = 0;

            gl::GenRenderbuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Renderbuffer {
                gl_id: id
            }
        }
    }

    pub fn bind_renderbuffer<'a>(self, renderbuffer: &'a mut Renderbuffer)
        -> (
            RenderbufferBinding<'a>,
            ContextOf<AB, EAB, P, FB, (), TU>
        )
        where RB: BorrowMut<RenderbufferBinder>
    {
        let (mut renderbuffer_binder, gl) = self.split_renderbuffer();
        (renderbuffer_binder.borrow_mut().bind(renderbuffer), gl)
    }
}

gl_enum! {
    pub gl_enum RenderbufferTarget {
        Renderbuffer as RENDERBUFFER = gl::RENDERBUFFER
    }
}

pub struct RenderbufferBinding<'a> {
    phantom: PhantomData<&'a mut Renderbuffer>
}

impl<'a> RenderbufferBinding<'a> {
    fn target(&self) -> RenderbufferTarget {
        RenderbufferTarget::Renderbuffer
    }

    pub fn storage(&mut self,
                   format: RenderbufferFormat,
                   width: u32,
                   height: u32)
    {
        unsafe {
            gl::RenderbufferStorage(self.target().gl_enum(),
                                    format.gl_enum(),
                                    width as GLint,
                                    height as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER` or `internalformat` is not an accepted format",
                GLError::InvalidValue => "`width` or `height` is less than zero or greater than `GL_MAX_RENDERBUFFER_SIZE`",
                GLError::OutOfMemory => "Unable to allocate enough memory for requested size",
                GLError::InvalidOperation => "Renderbuffer object 0 is bound",
                _ => "Unknown error"
            }
        }
    }
}

pub struct RenderbufferBinder;
impl RenderbufferBinder {
    pub fn bind<'a>(&mut self, renderbuffer: &'a mut Renderbuffer)
        -> RenderbufferBinding<'a>
    {
        let binding = RenderbufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindRenderbuffer(binding.target().gl_enum(),
                                 renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<commit_msg>Add `RenderbufferBuilder` to build a complete renderbuffer<commit_after>use std::marker::PhantomData;
use std::borrow::BorrowMut;
use gl;
use gl::types::*;
use context::ContextOf;
use image_data::{RenderbufferFormat};
use types::GLError;

pub struct Renderbuffer {
    gl_id: GLuint
}

impl Renderbuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Renderbuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteRenderbuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

pub struct RenderbufferBuilder<'a, AB, EAB, P, FB, RB, TU>
    where  AB: 'a,
          EAB: 'a,
            P: 'a,
           FB: 'a,
           RB: 'a + BorrowMut<RenderbufferBinder>,
           TU: 'a
{
    gl: &'a mut ContextOf<AB, EAB, P, FB, RB, TU>,
    storage_params: Option<(RenderbufferFormat, u32, u32)>
}

impl<'a, AB, EAB, P, FB, RB, TU> RenderbufferBuilder<'a, AB, EAB, P, FB, RB, TU>
    where  AB: 'a,
          EAB: 'a,
            P: 'a,
           FB: 'a,
           RB: 'a + BorrowMut<RenderbufferBinder>,
           TU: 'a
{
    fn new(gl: &'a mut ContextOf<AB, EAB, P, FB, RB, TU>) -> Self {
        RenderbufferBuilder {
            gl: gl,
            storage_params: None
        }
    }

    pub fn storage(mut self,
                   format: RenderbufferFormat,
                   width: u32,
                   height: u32)
        -> Self
    {
        self.storage_params = Some((format, width, height));
        self
    }

    pub fn try_unwrap(self) -> Result<Renderbuffer, GLError> {
        let gl = self.gl.borrowed_mut();
        let mut rbo = gl.gen_renderbuffer();

        match self.storage_params {
            Some((format, width, height)) => {
                {
                    let (mut gl_rbo, _) = gl.bind_renderbuffer(&mut rbo);
                    gl_rbo.storage(format, width, height);
                }

                Ok(rbo)
            },
            None => {
                let msg = "Error building renderbuffer: no format or dimensions provided";
                Err(GLError::Message(msg.to_owned()))
            }
        }
    }

    pub fn unwrap(self) -> Renderbuffer {
        self.try_unwrap().unwrap()
    }
}

impl<AB, EAB, P, FB, RB, TU> ContextOf<AB, EAB, P, FB, RB, TU> {
    pub fn build_renderbuffer<'a>(&'a mut self)
        -> RenderbufferBuilder<'a, AB, EAB, P, FB, RB, TU>
        where RB: BorrowMut<RenderbufferBinder>
    {
        RenderbufferBuilder::new(self)
    }

    pub fn gen_renderbuffer(&self) -> Renderbuffer {
        unsafe {
            let mut id : GLuint = 0;

            gl::GenRenderbuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Renderbuffer {
                gl_id: id
            }
        }
    }

    pub fn bind_renderbuffer<'a>(self, renderbuffer: &'a mut Renderbuffer)
        -> (
            RenderbufferBinding<'a>,
            ContextOf<AB, EAB, P, FB, (), TU>
        )
        where RB: BorrowMut<RenderbufferBinder>
    {
        let (mut renderbuffer_binder, gl) = self.split_renderbuffer();
        (renderbuffer_binder.borrow_mut().bind(renderbuffer), gl)
    }
}

gl_enum! {
    pub gl_enum RenderbufferTarget {
        Renderbuffer as RENDERBUFFER = gl::RENDERBUFFER
    }
}

pub struct RenderbufferBinding<'a> {
    phantom: PhantomData<&'a mut Renderbuffer>
}

impl<'a> RenderbufferBinding<'a> {
    fn target(&self) -> RenderbufferTarget {
        RenderbufferTarget::Renderbuffer
    }

    pub fn storage(&mut self,
                   format: RenderbufferFormat,
                   width: u32,
                   height: u32)
    {
        unsafe {
            gl::RenderbufferStorage(self.target().gl_enum(),
                                    format.gl_enum(),
                                    width as GLint,
                                    height as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER` or `internalformat` is not an accepted format",
                GLError::InvalidValue => "`width` or `height` is less than zero or greater than `GL_MAX_RENDERBUFFER_SIZE`",
                GLError::OutOfMemory => "Unable to allocate enough memory for requested size",
                GLError::InvalidOperation => "Renderbuffer object 0 is bound",
                _ => "Unknown error"
            }
        }
    }
}

pub struct RenderbufferBinder;
impl RenderbufferBinder {
    pub fn bind<'a>(&mut self, renderbuffer: &'a mut Renderbuffer)
        -> RenderbufferBinding<'a>
    {
        let binding = RenderbufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindRenderbuffer(binding.target().gl_enum(),
                                 renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<|endoftext|>


<commit_before>#![cfg(test)]

use std::collections::HashMap;
use rustc_serialize::json;
use rustc_serialize::json::{Json, Object};
use rustc_serialize::{Decoder, Decodable};
use std::io::prelude::*;
use std::fs::File;
use std::io::Result;
use token::{Token, ReservedWord};

pub struct DynJson {
    json: Json
}

impl Decodable for DynJson {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, Error> {
        unimplemented!()
    }
}

#[derive(RustcDecodable)]
pub struct ExpectPass {
    source: String,
    expected: DynJson,
    options: Option<DynJson>
}

pub struct ExpectFail {
    source: String,
    error: String,
    options: Option<Json>
}

pub enum TestCase {
    Pass(ExpectPass),
    Fail(ExpectFail)
}

trait JsonExt {
    fn into_array(self) -> json::Array;
    fn into_string(self) -> String;
    fn into_string_opt(self) -> Option<String>;
}

impl JsonExt for Json {
    fn into_array(self) -> json::Array {
        match self {
            Json::Array(array) => array,
            _ => panic!("expected array")
        }
    }
    fn into_string(self) -> String {
        match self {
            Json::String(string) => string,
            _ => panic!("expected string")
        }
    }
    fn into_string_opt(self) -> Option<String> {
        match self {
            Json::Null => None,
            Json::String(string) => Some(string),
            _ => panic!("expected string or null")
        }
    }
}

<commit_msg>trying a deliberately bogus type argument to see if compiler helps -- clue! "expected associated type" src/test/mod.rs:16:5: 18:6 error: method `decode` has an incompatible type for trait: expected associated type, found u32 [E0053] src/test/mod.rs:16     fn decode<D: Decoder>(d: &mut D) -> Result<Self, u32> { src/test/mod.rs:17         unimplemented!() src/test/mod.rs:18     }<commit_after>#![cfg(test)]

use std::collections::HashMap;
use rustc_serialize::json;
use rustc_serialize::json::{Json, Object};
use rustc_serialize::{Decoder, Decodable};
use std::io::prelude::*;
use std::fs::File;
use token::{Token, ReservedWord};

pub struct DynJson {
    json: Json
}

impl Decodable for DynJson {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, u32> {
        unimplemented!()
    }
}

#[derive(RustcDecodable)]
pub struct ExpectPass {
    source: String,
    expected: DynJson,
    options: Option<DynJson>
}

pub struct ExpectFail {
    source: String,
    error: String,
    options: Option<Json>
}

pub enum TestCase {
    Pass(ExpectPass),
    Fail(ExpectFail)
}

trait JsonExt {
    fn into_array(self) -> json::Array;
    fn into_string(self) -> String;
    fn into_string_opt(self) -> Option<String>;
}

impl JsonExt for Json {
    fn into_array(self) -> json::Array {
        match self {
            Json::Array(array) => array,
            _ => panic!("expected array")
        }
    }
    fn into_string(self) -> String {
        match self {
            Json::String(string) => string,
            _ => panic!("expected string")
        }
    }
    fn into_string_opt(self) -> Option<String> {
        match self {
            Json::Null => None,
            Json::String(string) => Some(string),
            _ => panic!("expected string or null")
        }
    }
}

<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::convert::TryFrom;
use std::ffi::{OsStr, OsString};
use std::iter;
use std::path::{Path, PathBuf};

#[cfg(unix)]
use std::os::unix::ffi::{OsStrExt, OsStringExt};
#[cfg(windows)]
use std::os::windows::ffi::{OsStrExt, OsStringExt};

use rustc::ty::layout::LayoutOf;

use crate::*;

/// Represent how path separator conversion should be done.
enum Pathconversion {
    HostToTarget,
    TargetToHost,
}

/// Perform path separator conversion if needed.
fn convert_path_separator<'a>(
    os_str: &'a OsStr,
    target_os: &str,
    direction: Pathconversion,
) -> Cow<'a, OsStr> {
    #[cfg(windows)]
    return if target_os == "windows" {
        // Windows-on-Windows, all fine.
        Cow::Borrowed(os_str)
    } else {
        // Unix target, Windows host.
        let (from, to) = match direction {
            Pathconversion::HostToTarget => ('\\', '/'),
            Pathconversion::TargetToHost => ('/', '\\'),
        };
        let converted = os_str
            .encode_wide()
            .map(|wchar| if wchar == from as u16 { to as u16 } else { wchar })
            .collect::<Vec<_>>();
        Cow::Owned(OsString::from_wide(&converted))
    };
    #[cfg(unix)]
    return if target_os == "windows" {
        // Windows target, Unix host.
        let (from, to) = match direction {
            Pathconversion::HostToTarget => ('/', '\\'),
            Pathconversion::TargetToHost => ('\\', '/'),
        };
        let converted = os_str
            .as_bytes()
            .iter()
            .map(|&wchar| if wchar == from as u8 { to as u8 } else { wchar })
            .collect::<Vec<_>>();
        Cow::Owned(OsString::from_vec(converted))
    } else {
        // Unix-on-Unix, all is fine.
        Cow::Borrowed(os_str)
    };
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    /// Helper function to read an OsString from a null-terminated sequence of bytes, which is what
    /// the Unix APIs usually handle.
    fn read_os_str_from_c_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, &'a OsStr>
    where
        'tcx: 'a,
        'mir: 'a,
    {
        #[cfg(unix)]
        fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> {
            Ok(OsStr::from_bytes(bytes))
        }
        #[cfg(not(unix))]
        fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> {
            let s = std::str::from_utf8(bytes)
                .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?;
            Ok(OsStr::new(s))
        }

        let this = self.eval_context_ref();
        let bytes = this.memory.read_c_str(scalar)?;
        bytes_to_os_str(bytes)
    }

    /// Helper function to read an OsString from a 0x0000-terminated sequence of u16,
    /// which is what the Windows APIs usually handle.
    fn read_os_str_from_wide_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, OsString>
    where
        'tcx: 'a,
        'mir: 'a,
    {
        #[cfg(windows)]
        pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
            Ok(OsString::from_wide(&u16_vec[..]))
        }
        #[cfg(not(windows))]
        pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
            let s = String::from_utf16(&u16_vec[..])
                .map_err(|_| err_unsup_format!("{:?} is not a valid utf-16 string", u16_vec))?;
            Ok(s.into())
        }

        let u16_vec = self.eval_context_ref().memory.read_wide_str(scalar)?;
        u16vec_to_osstring(u16_vec)
    }

    /// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
    /// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
    /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
    /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
    /// string length returned does not include the null terminator.
    fn write_os_str_to_c_str(
        &mut self,
        os_str: &OsStr,
        scalar: Scalar<Tag>,
        size: u64,
    ) -> InterpResult<'tcx, (bool, u64)> {
        #[cfg(unix)]
        fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
            Ok(os_str.as_bytes())
        }
        #[cfg(not(unix))]
        fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
            // On non-unix platforms the best we can do to transform bytes from/to OS strings is to do the
            // intermediate transformation into strings. Which invalidates non-utf8 paths that are actually
            // valid.
            os_str
                .to_str()
                .map(|s| s.as_bytes())
                .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into())
        }

        let bytes = os_str_to_bytes(os_str)?;
        // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
        // terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
        let string_length = u64::try_from(bytes.len()).unwrap();
        if size <= string_length {
            return Ok((false, string_length));
        }
        self.eval_context_mut()
            .memory
            .write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?;
        Ok((true, string_length))
    }

    /// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what
    /// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
    /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
    /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
    /// string length returned does not include the null terminator.
    fn write_os_str_to_wide_str(
        &mut self,
        os_str: &OsStr,
        scalar: Scalar<Tag>,
        size: u64,
    ) -> InterpResult<'tcx, (bool, u64)> {
        #[cfg(windows)]
        fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
            Ok(os_str.encode_wide().collect())
        }
        #[cfg(not(windows))]
        fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
            // On non-Windows platforms the best we can do to transform Vec<u16> from/to OS strings is to do the
            // intermediate transformation into strings. Which invalidates non-utf8 paths that are actually
            // valid.
            os_str
                .to_str()
                .map(|s| s.encode_utf16().collect())
                .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into())
        }

        let u16_vec = os_str_to_u16vec(os_str)?;
        // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required
        // 0x0000 terminator to memory would cause an out-of-bounds access.
        let string_length = u64::try_from(u16_vec.len()).unwrap();
        if size <= string_length {
            return Ok((false, string_length));
        }

        // Store the UTF-16 string.
        self.eval_context_mut()
            .memory
            .write_u16s(scalar, u16_vec.into_iter().chain(iter::once(0x0000)))?;
        Ok((true, string_length))
    }

    /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
    fn alloc_os_str_as_c_str(
        &mut self,
        os_str: &OsStr,
        memkind: MemoryKind<MiriMemoryKind>,
    ) -> Pointer<Tag> {
        let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator.
        let this = self.eval_context_mut();

        let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
        let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind);
        assert!(self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap().0);
        arg_place.ptr.assert_ptr()
    }

    /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of `u16`.
    fn alloc_os_str_as_wide_str(
        &mut self,
        os_str: &OsStr,
        memkind: MemoryKind<MiriMemoryKind>,
    ) -> Pointer<Tag> {
        let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator.
        let this = self.eval_context_mut();

        let arg_type = this.tcx.mk_array(this.tcx.types.u16, size);
        let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind);
        assert!(self.write_os_str_to_wide_str(os_str, arg_place.ptr, size).unwrap().0);
        arg_place.ptr.assert_ptr()
    }

    /// Read a null-terminated sequence of bytes, and perform path separator conversion if needed.
    fn read_path_from_c_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, Cow<'a, Path>>
    where
        'tcx: 'a,
        'mir: 'a,
    {
        let this = self.eval_context_ref();
        let os_str = this.read_os_str_from_c_str(scalar)?;

        Ok(match convert_path_separator(os_str, &this.tcx.sess.target.target.target_os, Pathconversion::TargetToHost) {
            Cow::Borrowed(x) => Cow::Borrowed(Path::new(x)),
            Cow::Owned(y) => Cow::Owned(PathBuf::from(y)),
        })
    }

    /// Read a null-terminated sequence of `u16`s, and perform path separator conversion if needed.
    fn read_path_from_wide_str(&self, scalar: Scalar<Tag>) -> InterpResult<'tcx, PathBuf> {
        let this = self.eval_context_ref();
        let os_str = this.read_os_str_from_wide_str(scalar)?;

        Ok(PathBuf::from(&convert_path_separator(&os_str, &this.tcx.sess.target.target.target_os, Pathconversion::TargetToHost)))
    }

    /// Write a Path to the machine memory (as a null-terminated sequence of bytes),
    /// adjusting path separators if needed.
    fn write_path_to_c_str(
        &mut self,
        path: &Path,
        scalar: Scalar<Tag>,
        size: u64,
    ) -> InterpResult<'tcx, (bool, u64)> {
        let this = self.eval_context_mut();
        let os_str = convert_path_separator(path.as_os_str(), &this.tcx.sess.target.target.target_os, Pathconversion::HostToTarget);
        this.write_os_str_to_c_str(&os_str, scalar, size)
    }

    /// Write a Path to the machine memory (as a null-terminated sequence of `u16`s),
    /// adjusting path separators if needed.
    fn write_path_to_wide_str(
        &mut self,
        path: &Path,
        scalar: Scalar<Tag>,
        size: u64,
    ) -> InterpResult<'tcx, (bool, u64)> {
        let this = self.eval_context_mut();
        let os_str = convert_path_separator(path.as_os_str(), &this.tcx.sess.target.target.target_os, Pathconversion::HostToTarget);
        this.write_os_str_to_wide_str(&os_str, scalar, size)
    }
}
<commit_msg>Auto merge of #1278 - JOE1994:cow, r=RalfJung<commit_after>use std::borrow::Cow;
use std::convert::TryFrom;
use std::ffi::{OsStr, OsString};
use std::iter;
use std::path::{Path, PathBuf};

#[cfg(unix)]
use std::os::unix::ffi::{OsStrExt, OsStringExt};
#[cfg(windows)]
use std::os::windows::ffi::{OsStrExt, OsStringExt};

use rustc::ty::layout::LayoutOf;

use crate::*;

/// Represent how path separator conversion should be done.
enum Pathconversion {
    HostToTarget,
    TargetToHost,
}

/// Perform path separator conversion if needed.
fn convert_path_separator<'a>(
    os_str: Cow<'a, OsStr>,
    target_os: &str,
    direction: Pathconversion,
) -> Cow<'a, OsStr> {
    #[cfg(windows)]
    return if target_os == "windows" {
        // Windows-on-Windows, all fine.
        os_str
    } else {
        // Unix target, Windows host.
        let (from, to) = match direction {
            Pathconversion::HostToTarget => ('\\', '/'),
            Pathconversion::TargetToHost => ('/', '\\'),
        };
        let converted = os_str
            .encode_wide()
            .map(|wchar| if wchar == from as u16 { to as u16 } else { wchar })
            .collect::<Vec<_>>();
        Cow::Owned(OsString::from_wide(&converted))
    };
    #[cfg(unix)]
    return if target_os == "windows" {
        // Windows target, Unix host.
        let (from, to) = match direction {
            Pathconversion::HostToTarget => ('/', '\\'),
            Pathconversion::TargetToHost => ('\\', '/'),
        };
        let converted = os_str
            .as_bytes()
            .iter()
            .map(|&wchar| if wchar == from as u8 { to as u8 } else { wchar })
            .collect::<Vec<_>>();
        Cow::Owned(OsString::from_vec(converted))
    } else {
        // Unix-on-Unix, all is fine.
        os_str
    };
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    /// Helper function to read an OsString from a null-terminated sequence of bytes, which is what
    /// the Unix APIs usually handle.
    fn read_os_str_from_c_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, &'a OsStr>
    where
        'tcx: 'a,
        'mir: 'a,
    {
        #[cfg(unix)]
        fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> {
            Ok(OsStr::from_bytes(bytes))
        }
        #[cfg(not(unix))]
        fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> {
            let s = std::str::from_utf8(bytes)
                .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?;
            Ok(OsStr::new(s))
        }

        let this = self.eval_context_ref();
        let bytes = this.memory.read_c_str(scalar)?;
        bytes_to_os_str(bytes)
    }

    /// Helper function to read an OsString from a 0x0000-terminated sequence of u16,
    /// which is what the Windows APIs usually handle.
    fn read_os_str_from_wide_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, OsString>
    where
        'tcx: 'a,
        'mir: 'a,
    {
        #[cfg(windows)]
        pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
            Ok(OsString::from_wide(&u16_vec[..]))
        }
        #[cfg(not(windows))]
        pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
            let s = String::from_utf16(&u16_vec[..])
                .map_err(|_| err_unsup_format!("{:?} is not a valid utf-16 string", u16_vec))?;
            Ok(s.into())
        }

        let u16_vec = self.eval_context_ref().memory.read_wide_str(scalar)?;
        u16vec_to_osstring(u16_vec)
    }

    /// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
    /// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
    /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
    /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
    /// string length returned does not include the null terminator.
    fn write_os_str_to_c_str(
        &mut self,
        os_str: &OsStr,
        scalar: Scalar<Tag>,
        size: u64,
    ) -> InterpResult<'tcx, (bool, u64)> {
        #[cfg(unix)]
        fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
            Ok(os_str.as_bytes())
        }
        #[cfg(not(unix))]
        fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
            // On non-unix platforms the best we can do to transform bytes from/to OS strings is to do the
            // intermediate transformation into strings. Which invalidates non-utf8 paths that are actually
            // valid.
            os_str
                .to_str()
                .map(|s| s.as_bytes())
                .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into())
        }

        let bytes = os_str_to_bytes(os_str)?;
        // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
        // terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
        let string_length = u64::try_from(bytes.len()).unwrap();
        if size <= string_length {
            return Ok((false, string_length));
        }
        self.eval_context_mut()
            .memory
            .write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?;
        Ok((true, string_length))
    }

    /// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what
    /// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
    /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
    /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
    /// string length returned does not include the null terminator.
    fn write_os_str_to_wide_str(
        &mut self,
        os_str: &OsStr,
        scalar: Scalar<Tag>,
        size: u64,
    ) -> InterpResult<'tcx, (bool, u64)> {
        #[cfg(windows)]
        fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
            Ok(os_str.encode_wide().collect())
        }
        #[cfg(not(windows))]
        fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
            // On non-Windows platforms the best we can do to transform Vec<u16> from/to OS strings is to do the
            // intermediate transformation into strings. Which invalidates non-utf8 paths that are actually
            // valid.
            os_str
                .to_str()
                .map(|s| s.encode_utf16().collect())
                .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into())
        }

        let u16_vec = os_str_to_u16vec(os_str)?;
        // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required
        // 0x0000 terminator to memory would cause an out-of-bounds access.
        let string_length = u64::try_from(u16_vec.len()).unwrap();
        if size <= string_length {
            return Ok((false, string_length));
        }

        // Store the UTF-16 string.
        self.eval_context_mut()
            .memory
            .write_u16s(scalar, u16_vec.into_iter().chain(iter::once(0x0000)))?;
        Ok((true, string_length))
    }

    /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
    fn alloc_os_str_as_c_str(
        &mut self,
        os_str: &OsStr,
        memkind: MemoryKind<MiriMemoryKind>,
    ) -> Pointer<Tag> {
        let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator.
        let this = self.eval_context_mut();

        let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
        let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind);
        assert!(self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap().0);
        arg_place.ptr.assert_ptr()
    }

    /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of `u16`.
    fn alloc_os_str_as_wide_str(
        &mut self,
        os_str: &OsStr,
        memkind: MemoryKind<MiriMemoryKind>,
    ) -> Pointer<Tag> {
        let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator.
        let this = self.eval_context_mut();

        let arg_type = this.tcx.mk_array(this.tcx.types.u16, size);
        let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind);
        assert!(self.write_os_str_to_wide_str(os_str, arg_place.ptr, size).unwrap().0);
        arg_place.ptr.assert_ptr()
    }

    /// Read a null-terminated sequence of bytes, and perform path separator conversion if needed.
    fn read_path_from_c_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, Cow<'a, Path>>
    where
        'tcx: 'a,
        'mir: 'a,
    {
        let this = self.eval_context_ref();
        let os_str = this.read_os_str_from_c_str(scalar)?;

        Ok(match convert_path_separator(Cow::Borrowed(os_str), &this.tcx.sess.target.target.target_os, Pathconversion::TargetToHost) {
            Cow::Borrowed(x) => Cow::Borrowed(Path::new(x)),
            Cow::Owned(y) => Cow::Owned(PathBuf::from(y)),
        })
    }

    /// Read a null-terminated sequence of `u16`s, and perform path separator conversion if needed.
    fn read_path_from_wide_str(&self, scalar: Scalar<Tag>) -> InterpResult<'tcx, PathBuf> {
        let this = self.eval_context_ref();
        let os_str = this.read_os_str_from_wide_str(scalar)?;

        Ok(convert_path_separator(Cow::Owned(os_str), &this.tcx.sess.target.target.target_os, Pathconversion::TargetToHost).into_owned().into())
    }

    /// Write a Path to the machine memory (as a null-terminated sequence of bytes),
    /// adjusting path separators if needed.
    fn write_path_to_c_str(
        &mut self,
        path: &Path,
        scalar: Scalar<Tag>,
        size: u64,
    ) -> InterpResult<'tcx, (bool, u64)> {
        let this = self.eval_context_mut();
        let os_str = convert_path_separator(Cow::Borrowed(path.as_os_str()), &this.tcx.sess.target.target.target_os, Pathconversion::HostToTarget);
        this.write_os_str_to_c_str(&os_str, scalar, size)
    }

    /// Write a Path to the machine memory (as a null-terminated sequence of `u16`s),
    /// adjusting path separators if needed.
    fn write_path_to_wide_str(
        &mut self,
        path: &Path,
        scalar: Scalar<Tag>,
        size: u64,
    ) -> InterpResult<'tcx, (bool, u64)> {
        let this = self.eval_context_mut();
        let os_str = convert_path_separator(Cow::Borrowed(path.as_os_str()), &this.tcx.sess.target.target.target_os, Pathconversion::HostToTarget);
        this.write_os_str_to_wide_str(&os_str, scalar, size)
    }
}
<|endoftext|>