<commit_before>//! Easy file downloading

use errors::*;
use notifications::Notification;
use sha2::Sha256;
use std::env;
use std::path::Path;
use url::Url;

pub fn download_file(url: &Url,
                     path: &Path,
                     hasher: Option<&mut Sha256>,
                     notify_handler: &Fn(Notification))
                     -> Result<()> {
    if env::var_os("RUSTUP_USE_HYPER").is_some() {
        notify_handler(Notification::UsingHyper);
        try!(self::hyper::download_file(url, path, hasher, notify_handler));
    } else {
        notify_handler(Notification::UsingCurl);
        try!(self::curl::download_file(url, path, hasher, notify_handler));
    }

    Ok(())
}

/// Download via libcurl; encrypt with the native (or OpenSSl) TLS
/// stack via libcurl
mod curl {
    use curl::easy::Easy;
    use errors::*;
    use notifications::Notification;
    use sha2::{Sha256, Digest};
    use std::cell::RefCell;
    use std::fs;
    use std::path::Path;
    use std::str;
    use std::time::Duration;
    use url::Url;

    pub fn download_file(url: &Url,
                         path: &Path,
                         mut hasher: Option<&mut Sha256>,
                         notify_handler: &Fn(Notification))
                         -> Result<()> {
        use notifications::Notification;
        use std::io::Write;

        let mut file = try!(fs::File::create(&path).chain_err(
            || "error creating file for download"));

        // Fetch either a cached libcurl handle (which will preserve open
        // connections) or create a new one if it isn't listed.
        //
        // Once we've acquired it, reset the lifetime from 'static to our local
        // scope.
        thread_local!(static EASY: RefCell<Easy> = RefCell::new(Easy::new()));
        EASY.with(|handle| {
            let mut handle = handle.borrow_mut();

            try!(handle.url(&url.to_string()).chain_err(|| "failed to set url"));
            try!(handle.follow_location(true).chain_err(|| "failed to set follow redirects"));

            // Take at most 30s to connect
            try!(handle.connect_timeout(Duration::new(30, 0)).chain_err(|| "failed to set connect timeout"));

            // Fail if less than 10 bytes are transferred every 30 seconds
            try!(handle.low_speed_limit(10).chain_err(|| "failed to set low speed limit"));
            try!(handle.low_speed_time(Duration::new(30, 0)).chain_err(|| "failed to set low speed time"));

            {
                let fserr = RefCell::new(None);
                let mut transfer = handle.transfer();

                // Data callback for libcurl which is called with data that's
                // downloaded. We just feed it into our hasher and also write it out
                // to disk.
                try!(transfer.write_function(|data| {
                    if let Some(ref mut h) = hasher {
                        h.input(data);
                    }
                    notify_handler(Notification::DownloadDataReceived(data.len()));
                    match file.write_all(data) {
                        Ok(()) => Ok(data.len()),
                        Err(e) => {
                            *fserr.borrow_mut() = Some(e);
                            Ok(0)
                        }
                    }
                }).chain_err(|| "failed to set write"));

                // Listen for headers and parse out a `Content-Length` if it comes
                // so we know how much we're downloading.
                try!(transfer.header_function(|header| {
                    if let Ok(data) = str::from_utf8(header) {
                        let prefix = "Content-Length: ";
                        if data.starts_with(prefix) {
                            if let Ok(s) = data[prefix.len()..].trim().parse() {
                                let msg = Notification::DownloadContentLengthReceived(s);
                                notify_handler(msg);
                            }
                        }
                    }
                    true
                }).chain_err(|| "failed to set header"));

                // If an error happens check to see if we had a filesystem error up
                // in `fserr`, but we always want to punt it up.
                try!(transfer.perform().or_else(|e| {
                    match fserr.borrow_mut().take() {
                        Some(fs) => Err(fs).chain_err(|| ErrorKind::HttpError(e)),
                        None => Err(ErrorKind::HttpError(e).into())
                    }
                }));
            }

            // If we didn't get a 200 or 0 ("OK" for files) then return an error
            let code = try!(handle.response_code().chain_err(|| "failed to get response code"));
            if code != 200 && code != 0 {
                return Err(ErrorKind::HttpStatus(code).into());
            }

            notify_handler(Notification::DownloadFinished);
            Ok(())
        })
    }
}

/// Download via hyper; encrypt with the native (or OpenSSl) TLS
/// stack via native-tls
mod hyper {
    use hyper;
    use notifications::Notification;
    use sha2::{Digest, Sha256};
    use std::fs;
    use std::io;
    use std::path::Path;
    use std::time::Duration;
    use url::Url;
    use errors::*;

    fn proxy_from_env(url: &Url) -> Option<(String, u16)> {
        use std::env::var_os;

        let mut maybe_https_proxy = var_os("https_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        if maybe_https_proxy.is_none() {
            maybe_https_proxy = var_os("HTTPS_PROXY").map(|ref v| v.to_str().unwrap_or("").to_string());
        }
        let maybe_http_proxy = var_os("http_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        let mut maybe_all_proxy = var_os("all_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        if maybe_all_proxy.is_none() {
            maybe_all_proxy = var_os("ALL_PROXY").map(|ref v| v.to_str().unwrap_or("").to_string());
        }
        if let Some(url_value) = match url.scheme() {
                                     "https" => maybe_https_proxy.or(maybe_http_proxy.or(maybe_all_proxy)),
                                     "http" => maybe_http_proxy.or(maybe_all_proxy),
                                     _ => maybe_all_proxy,
                                 } {
            if let Ok(proxy_url) = Url::parse(&url_value) {
                if let Some(host) = proxy_url.host_str() {
                    let port = proxy_url.port().unwrap_or(8080);
                    return Some((host.to_string(), port));
                }
            }
        }
        None
    }

    pub fn download_file(url: &Url,
                         path: &Path,
                         mut hasher: Option<&mut Sha256>,
                         notify_handler: &Fn(Notification))
                         -> Result<()> {

        // Short-circuit hyper for the "file:" URL scheme
        if try!(download_from_file_url(url, path, &mut hasher)) {
            return Ok(());
        }

        use hyper::client::{Client, ProxyConfig};
        use hyper::error::Result as HyperResult;
        use hyper::header::ContentLength;
        use hyper::net::{SslClient, NetworkStream, HttpsConnector};
        use native_tls;
        use std::io::Result as IoResult;
        use std::io::{Read, Write};
        use std::net::{SocketAddr, Shutdown};
        use std::sync::{Arc, Mutex};

        // The Hyper HTTP client
        let client;

        let maybe_proxy = proxy_from_env(url);
        if url.scheme() == "https" {

            // All the following is adapter code to use native_tls with hyper.

            struct NativeSslClient;

            impl<T: NetworkStream + Send + Clone> SslClient<T> for NativeSslClient {
                type Stream = NativeSslStream<T>;

                fn wrap_client(&self, stream: T, host: &str) -> HyperResult<Self::Stream> {
                    use native_tls::ClientBuilder as TlsClientBuilder;
                    use hyper::error::Error as HyperError;

                    let mut ssl_builder = try!(TlsClientBuilder::new()
                                               .map_err(|e| HyperError::Ssl(Box::new(e))));
                    let ssl_stream = try!(ssl_builder.handshake(host, stream)
                                          .map_err(|e| HyperError::Ssl(Box::new(e))));

                    Ok(NativeSslStream(Arc::new(Mutex::new(ssl_stream))))
                }
            }

            #[derive(Clone)]
            struct NativeSslStream<T>(Arc<Mutex<native_tls::TlsStream<T>>>);

            #[derive(Debug)]
            struct NativeSslPoisonError;

            impl ::std::error::Error for NativeSslPoisonError {
                fn description(&self) -> &str { "mutex poisoned during TLS operation" }
            }

            impl ::std::fmt::Display for NativeSslPoisonError {
                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
                    f.write_str(::std::error::Error::description(self))
                }
            }

            impl<T> NetworkStream for NativeSslStream<T>
                where T: NetworkStream
            {
                fn peer_addr(&mut self) -> IoResult<SocketAddr> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.get_mut().peer_addr())
                }
                fn set_read_timeout(&self, dur: Option<Duration>) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|t| t.get_ref().set_read_timeout(dur))
                }
                fn set_write_timeout(&self, dur: Option<Duration>) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|t| t.get_ref().set_write_timeout(dur))
                }
                fn close(&mut self, how: Shutdown) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.get_mut().close(how))
                }
            }

            impl<T> Read for NativeSslStream<T>
                where T: Read + Write
            {
                fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.read(buf))
                }
            }

            impl<T> Write for NativeSslStream<T>
                where T: Read + Write
            {
                fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.write(buf))
                }
                fn flush(&mut self) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.flush())
                }
            }

            maybe_init_certs();

            if maybe_proxy.is_none() {
                // Connect with hyper + native_tls
                client = Client::with_connector(HttpsConnector::new(NativeSslClient));
            } else {
                let proxy_host_port = maybe_proxy.unwrap();
                client = Client::with_proxy_config(ProxyConfig(proxy_host_port.0, proxy_host_port.1, NativeSslClient));
            }
        } else if url.scheme() == "http" {
            if maybe_proxy.is_none() {
                client = Client::new();
            } else {
                let proxy_host_port = maybe_proxy.unwrap();
                client = Client::with_http_proxy(proxy_host_port.0, proxy_host_port.1);
            }
        } else {
            return Err(format!("unsupported URL scheme: '{}'", url.scheme()).into());
        }

        let mut res = try!(client.get(url.clone()).send()
                           .chain_err(|| "failed to make network request"));
        if res.status != hyper::Ok {
            return Err(ErrorKind::HttpStatus(res.status.to_u16() as u32).into());
        }

        let buffer_size = 0x10000;
        let mut buffer = vec![0u8; buffer_size];

        let mut file = try!(fs::File::create(path).chain_err(
            || "error creating file for download"));

        if let Some(len) = res.headers.get::<ContentLength>().cloned() {
            notify_handler(Notification::DownloadContentLengthReceived(len.0));
        }

        loop {
            let bytes_read = try!(io::Read::read(&mut res, &mut buffer)
                                  .chain_err(|| "error reading from socket"));

            if bytes_read != 0 {
                if let Some(ref mut h) = hasher {
                    h.input(&buffer[0..bytes_read]);
                }
                try!(io::Write::write_all(&mut file, &mut buffer[0..bytes_read])
                     .chain_err(|| "unable to write download to disk"));
                notify_handler(Notification::DownloadDataReceived(bytes_read));
            } else {
                try!(file.sync_data().chain_err(|| "unable to sync download to disk"));
                notify_handler(Notification::DownloadFinished);
                return Ok(());
            }
        }
    }

    // Tell our statically-linked OpenSSL where to find root certs
    // cc https://github.com/alexcrichton/git2-rs/blob/master/libgit2-sys/lib.rs#L1267
    #[cfg(not(any(target_os = "windows", target_os = "macos")))]
    fn maybe_init_certs() {
        use std::sync::{Once, ONCE_INIT};
        static INIT: Once = ONCE_INIT;
        INIT.call_once(|| {
            ::openssl_sys::probe::init_ssl_cert_env_vars();
        });
    }

    #[cfg(any(target_os = "windows", target_os = "macos"))]
    fn maybe_init_certs() { }

    fn download_from_file_url(url: &Url,
                              path: &Path,
                              hasher: &mut Option<&mut Sha256>)
                              -> Result<bool> {
        use raw::is_file;

        // The file scheme is mostly for use by tests to mock the dist server
        if url.scheme() == "file" {
            let src = try!(url.to_file_path()
                           .map_err(|_| Error::from(format!("bogus file url: '{}'", url))));
            if !is_file(&src) {
                // Because some of multirust's logic depends on checking
                // the error when a downloaded file doesn't exist, make
                // the file case return the same error value as the
                // network case.
                return Err(ErrorKind::HttpStatus(hyper::status::StatusCode::NotFound.to_u16() as u32).into());
            }
            try!(fs::copy(&src, path).chain_err(|| "failure copying file"));

            if let Some(ref mut h) = *hasher {
                let ref mut f = try!(fs::File::open(path)
                                     .chain_err(|| "unable to open downloaded file"));

                let ref mut buffer = vec![0u8; 0x10000];
                loop {
                    let bytes_read = try!(io::Read::read(f, buffer)
                                          .chain_err(|| "unable to read downloaded file"));
                    if bytes_read == 0 { break }
                    h.input(&buffer[0..bytes_read]);
                }
            }

            Ok(true)
        } else {
            Ok(false)
        }
    }
}
<commit_msg>Pull DownloadFinished notification into generic download_file fn<commit_after>//! Easy file downloading

use errors::*;
use notifications::Notification;
use sha2::Sha256;
use std::env;
use std::path::Path;
use url::Url;

pub fn download_file(url: &Url,
                     path: &Path,
                     hasher: Option<&mut Sha256>,
                     notify_handler: &Fn(Notification))
                     -> Result<()> {
    if env::var_os("RUSTUP_USE_HYPER").is_some() {
        notify_handler(Notification::UsingHyper);
        try!(self::hyper::download_file(url, path, hasher, notify_handler));
    } else {
        notify_handler(Notification::UsingCurl);
        try!(self::curl::download_file(url, path, hasher, notify_handler));
    }

    notify_handler(Notification::DownloadFinished);

    Ok(())
}

/// Download via libcurl; encrypt with the native (or OpenSSl) TLS
/// stack via libcurl
mod curl {
    use curl::easy::Easy;
    use errors::*;
    use notifications::Notification;
    use sha2::{Sha256, Digest};
    use std::cell::RefCell;
    use std::fs;
    use std::path::Path;
    use std::str;
    use std::time::Duration;
    use url::Url;

    pub fn download_file(url: &Url,
                         path: &Path,
                         mut hasher: Option<&mut Sha256>,
                         notify_handler: &Fn(Notification))
                         -> Result<()> {
        use notifications::Notification;
        use std::io::Write;

        let mut file = try!(fs::File::create(&path).chain_err(
            || "error creating file for download"));

        // Fetch either a cached libcurl handle (which will preserve open
        // connections) or create a new one if it isn't listed.
        //
        // Once we've acquired it, reset the lifetime from 'static to our local
        // scope.
        thread_local!(static EASY: RefCell<Easy> = RefCell::new(Easy::new()));
        EASY.with(|handle| {
            let mut handle = handle.borrow_mut();

            try!(handle.url(&url.to_string()).chain_err(|| "failed to set url"));
            try!(handle.follow_location(true).chain_err(|| "failed to set follow redirects"));

            // Take at most 30s to connect
            try!(handle.connect_timeout(Duration::new(30, 0)).chain_err(|| "failed to set connect timeout"));

            // Fail if less than 10 bytes are transferred every 30 seconds
            try!(handle.low_speed_limit(10).chain_err(|| "failed to set low speed limit"));
            try!(handle.low_speed_time(Duration::new(30, 0)).chain_err(|| "failed to set low speed time"));

            {
                let fserr = RefCell::new(None);
                let mut transfer = handle.transfer();

                // Data callback for libcurl which is called with data that's
                // downloaded. We just feed it into our hasher and also write it out
                // to disk.
                try!(transfer.write_function(|data| {
                    if let Some(ref mut h) = hasher {
                        h.input(data);
                    }
                    notify_handler(Notification::DownloadDataReceived(data.len()));
                    match file.write_all(data) {
                        Ok(()) => Ok(data.len()),
                        Err(e) => {
                            *fserr.borrow_mut() = Some(e);
                            Ok(0)
                        }
                    }
                }).chain_err(|| "failed to set write"));

                // Listen for headers and parse out a `Content-Length` if it comes
                // so we know how much we're downloading.
                try!(transfer.header_function(|header| {
                    if let Ok(data) = str::from_utf8(header) {
                        let prefix = "Content-Length: ";
                        if data.starts_with(prefix) {
                            if let Ok(s) = data[prefix.len()..].trim().parse() {
                                let msg = Notification::DownloadContentLengthReceived(s);
                                notify_handler(msg);
                            }
                        }
                    }
                    true
                }).chain_err(|| "failed to set header"));

                // If an error happens check to see if we had a filesystem error up
                // in `fserr`, but we always want to punt it up.
                try!(transfer.perform().or_else(|e| {
                    match fserr.borrow_mut().take() {
                        Some(fs) => Err(fs).chain_err(|| ErrorKind::HttpError(e)),
                        None => Err(ErrorKind::HttpError(e).into())
                    }
                }));
            }

            // If we didn't get a 200 or 0 ("OK" for files) then return an error
            let code = try!(handle.response_code().chain_err(|| "failed to get response code"));
            if code != 200 && code != 0 {
                return Err(ErrorKind::HttpStatus(code).into());
            }

            Ok(())
        })
    }
}

/// Download via hyper; encrypt with the native (or OpenSSl) TLS
/// stack via native-tls
mod hyper {
    use hyper;
    use notifications::Notification;
    use sha2::{Digest, Sha256};
    use std::fs;
    use std::io;
    use std::path::Path;
    use std::time::Duration;
    use url::Url;
    use errors::*;

    fn proxy_from_env(url: &Url) -> Option<(String, u16)> {
        use std::env::var_os;

        let mut maybe_https_proxy = var_os("https_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        if maybe_https_proxy.is_none() {
            maybe_https_proxy = var_os("HTTPS_PROXY").map(|ref v| v.to_str().unwrap_or("").to_string());
        }
        let maybe_http_proxy = var_os("http_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        let mut maybe_all_proxy = var_os("all_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        if maybe_all_proxy.is_none() {
            maybe_all_proxy = var_os("ALL_PROXY").map(|ref v| v.to_str().unwrap_or("").to_string());
        }
        if let Some(url_value) = match url.scheme() {
                                     "https" => maybe_https_proxy.or(maybe_http_proxy.or(maybe_all_proxy)),
                                     "http" => maybe_http_proxy.or(maybe_all_proxy),
                                     _ => maybe_all_proxy,
                                 } {
            if let Ok(proxy_url) = Url::parse(&url_value) {
                if let Some(host) = proxy_url.host_str() {
                    let port = proxy_url.port().unwrap_or(8080);
                    return Some((host.to_string(), port));
                }
            }
        }
        None
    }

    pub fn download_file(url: &Url,
                         path: &Path,
                         mut hasher: Option<&mut Sha256>,
                         notify_handler: &Fn(Notification))
                         -> Result<()> {

        // Short-circuit hyper for the "file:" URL scheme
        if try!(download_from_file_url(url, path, &mut hasher)) {
            return Ok(());
        }

        use hyper::client::{Client, ProxyConfig};
        use hyper::error::Result as HyperResult;
        use hyper::header::ContentLength;
        use hyper::net::{SslClient, NetworkStream, HttpsConnector};
        use native_tls;
        use std::io::Result as IoResult;
        use std::io::{Read, Write};
        use std::net::{SocketAddr, Shutdown};
        use std::sync::{Arc, Mutex};

        // The Hyper HTTP client
        let client;

        let maybe_proxy = proxy_from_env(url);
        if url.scheme() == "https" {

            // All the following is adapter code to use native_tls with hyper.

            struct NativeSslClient;

            impl<T: NetworkStream + Send + Clone> SslClient<T> for NativeSslClient {
                type Stream = NativeSslStream<T>;

                fn wrap_client(&self, stream: T, host: &str) -> HyperResult<Self::Stream> {
                    use native_tls::ClientBuilder as TlsClientBuilder;
                    use hyper::error::Error as HyperError;

                    let mut ssl_builder = try!(TlsClientBuilder::new()
                                               .map_err(|e| HyperError::Ssl(Box::new(e))));
                    let ssl_stream = try!(ssl_builder.handshake(host, stream)
                                          .map_err(|e| HyperError::Ssl(Box::new(e))));

                    Ok(NativeSslStream(Arc::new(Mutex::new(ssl_stream))))
                }
            }

            #[derive(Clone)]
            struct NativeSslStream<T>(Arc<Mutex<native_tls::TlsStream<T>>>);

            #[derive(Debug)]
            struct NativeSslPoisonError;

            impl ::std::error::Error for NativeSslPoisonError {
                fn description(&self) -> &str { "mutex poisoned during TLS operation" }
            }

            impl ::std::fmt::Display for NativeSslPoisonError {
                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
                    f.write_str(::std::error::Error::description(self))
                }
            }

            impl<T> NetworkStream for NativeSslStream<T>
                where T: NetworkStream
            {
                fn peer_addr(&mut self) -> IoResult<SocketAddr> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.get_mut().peer_addr())
                }
                fn set_read_timeout(&self, dur: Option<Duration>) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|t| t.get_ref().set_read_timeout(dur))
                }
                fn set_write_timeout(&self, dur: Option<Duration>) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|t| t.get_ref().set_write_timeout(dur))
                }
                fn close(&mut self, how: Shutdown) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.get_mut().close(how))
                }
            }

            impl<T> Read for NativeSslStream<T>
                where T: Read + Write
            {
                fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.read(buf))
                }
            }

            impl<T> Write for NativeSslStream<T>
                where T: Read + Write
            {
                fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.write(buf))
                }
                fn flush(&mut self) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.flush())
                }
            }

            maybe_init_certs();

            if maybe_proxy.is_none() {
                // Connect with hyper + native_tls
                client = Client::with_connector(HttpsConnector::new(NativeSslClient));
            } else {
                let proxy_host_port = maybe_proxy.unwrap();
                client = Client::with_proxy_config(ProxyConfig(proxy_host_port.0, proxy_host_port.1, NativeSslClient));
            }
        } else if url.scheme() == "http" {
            if maybe_proxy.is_none() {
                client = Client::new();
            } else {
                let proxy_host_port = maybe_proxy.unwrap();
                client = Client::with_http_proxy(proxy_host_port.0, proxy_host_port.1);
            }
        } else {
            return Err(format!("unsupported URL scheme: '{}'", url.scheme()).into());
        }

        let mut res = try!(client.get(url.clone()).send()
                           .chain_err(|| "failed to make network request"));
        if res.status != hyper::Ok {
            return Err(ErrorKind::HttpStatus(res.status.to_u16() as u32).into());
        }

        let buffer_size = 0x10000;
        let mut buffer = vec![0u8; buffer_size];

        let mut file = try!(fs::File::create(path).chain_err(
            || "error creating file for download"));

        if let Some(len) = res.headers.get::<ContentLength>().cloned() {
            notify_handler(Notification::DownloadContentLengthReceived(len.0));
        }

        loop {
            let bytes_read = try!(io::Read::read(&mut res, &mut buffer)
                                  .chain_err(|| "error reading from socket"));

            if bytes_read != 0 {
                if let Some(ref mut h) = hasher {
                    h.input(&buffer[0..bytes_read]);
                }
                try!(io::Write::write_all(&mut file, &mut buffer[0..bytes_read])
                     .chain_err(|| "unable to write download to disk"));
                notify_handler(Notification::DownloadDataReceived(bytes_read));
            } else {
                try!(file.sync_data().chain_err(|| "unable to sync download to disk"));
                return Ok(());
            }
        }
    }

    // Tell our statically-linked OpenSSL where to find root certs
    // cc https://github.com/alexcrichton/git2-rs/blob/master/libgit2-sys/lib.rs#L1267
    #[cfg(not(any(target_os = "windows", target_os = "macos")))]
    fn maybe_init_certs() {
        use std::sync::{Once, ONCE_INIT};
        static INIT: Once = ONCE_INIT;
        INIT.call_once(|| {
            ::openssl_sys::probe::init_ssl_cert_env_vars();
        });
    }

    #[cfg(any(target_os = "windows", target_os = "macos"))]
    fn maybe_init_certs() { }

    fn download_from_file_url(url: &Url,
                              path: &Path,
                              hasher: &mut Option<&mut Sha256>)
                              -> Result<bool> {
        use raw::is_file;

        // The file scheme is mostly for use by tests to mock the dist server
        if url.scheme() == "file" {
            let src = try!(url.to_file_path()
                           .map_err(|_| Error::from(format!("bogus file url: '{}'", url))));
            if !is_file(&src) {
                // Because some of multirust's logic depends on checking
                // the error when a downloaded file doesn't exist, make
                // the file case return the same error value as the
                // network case.
                return Err(ErrorKind::HttpStatus(hyper::status::StatusCode::NotFound.to_u16() as u32).into());
            }
            try!(fs::copy(&src, path).chain_err(|| "failure copying file"));

            if let Some(ref mut h) = *hasher {
                let ref mut f = try!(fs::File::open(path)
                                     .chain_err(|| "unable to open downloaded file"));

                let ref mut buffer = vec![0u8; 0x10000];
                loop {
                    let bytes_read = try!(io::Read::read(f, buffer)
                                          .chain_err(|| "unable to read downloaded file"));
                    if bytes_read == 0 { break }
                    h.input(&buffer[0..bytes_read]);
                }
            }

            Ok(true)
        } else {
            Ok(false)
        }
    }
}
<|endoftext|>


<commit_before>mod parser;
use parser::Parser;

pub struct Compiler {}

impl Compiler {
    pub fn new() -> Compiler {
        Compiler {}
    }
    pub fn evaluate(&mut self, deck: &str) -> String {
        let mut parser = Parser::new(deck);
        let _statement = parser.parse_statement();
        String::from(deck)
    }
}
<commit_msg>match on the parsed statment and give feedback<commit_after>mod parser;
use parser::Parser;

pub struct Compiler {}

impl Compiler {
    pub fn new() -> Compiler {
        Compiler {}
    }
    pub fn evaluate(&mut self, deck: &str) -> String {
        let mut parser = Parser::new(deck);
        let statement_result = parser.parse_statement();
        match statement_result {
            Ok(statement_option) => {
                match statement_option {
                    None => return String::from("This is no statement.\n"),
                    Some(_statement) => return String::from("Parsed successfully.\n"),
                }

            }
            Err(error) => return String::from(format!("Parser error at {}, {}\n", error.line, error.column)),
        }
    }
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#[macro_use]
extern crate criterion;
use criterion::Criterion;

use rand::distributions::{Alphanumeric, Distribution, Standard};
use rand::Rng;

use std::sync::Arc;

extern crate arrow;

use arrow::array::*;
use arrow::compute::concat;
use arrow::datatypes::*;
use arrow::util::test_util::seedable_rng;

// cast array from specified primitive array type to desired data type
fn create_primitive<T>(size: usize, null_density: f32) -> ArrayRef
where
    T: ArrowPrimitiveType,
    Standard: Distribution<T::Native>,
    PrimitiveArray<T>: std::convert::From<Vec<T::Native>>,
{
    let mut rng = seedable_rng();

    let array: PrimitiveArray<T> = seedable_rng()
        .sample_iter(&Standard)
        .take(size)
        .map(|value| {
            let x = rng.gen::<f32>();
            if x < null_density {
                Some(value)
            } else {
                None
            }
        })
        .collect();

    Arc::new(array) as ArrayRef
}

fn create_strings(size: usize, null_density: f32) -> ArrayRef {
    let rng = &mut seedable_rng();

    let mut builder = StringBuilder::new(size);
    for _ in 0..size {
        let x = rng.gen::<f32>();
        if x < null_density {
            let value = rng.sample_iter(&Alphanumeric).take(4).collect::<String>();
            builder.append_value(&value).unwrap();
        } else {
            builder.append_null().unwrap()
        }
    }
    Arc::new(builder.finish())
}

fn bench_concat(v1: &ArrayRef, v2: &ArrayRef) {
    criterion::black_box(concat(&[v1.as_ref(), v2.as_ref()]).unwrap());
}

fn add_benchmark(c: &mut Criterion) {
    let v1 = create_primitive::<Int32Type>(1024, 0.0);
    let v2 = create_primitive::<Int32Type>(1024, 0.0);
    c.bench_function("concat i32 1024", |b| b.iter(|| bench_concat(&v1, &v2)));

    let v1 = create_primitive::<Int32Type>(1024, 0.5);
    let v2 = create_primitive::<Int32Type>(1024, 0.5);
    c.bench_function("concat i32 nulls 1024", |b| {
        b.iter(|| bench_concat(&v1, &v2))
    });

    let v1 = create_strings(1024, 0.0);
    let v2 = create_strings(1024, 0.0);
    c.bench_function("concat str 1024", |b| b.iter(|| bench_concat(&v1, &v2)));

    let v1 = create_strings(1024, 0.5);
    let v2 = create_strings(1024, 0.5);
    c.bench_function("concat str nulls 1024", |b| {
        b.iter(|| bench_concat(&v1, &v2))
    });
}

criterion_group!(benches, add_benchmark);
criterion_main!(benches);
<commit_msg>ARROW-11358: [Rust] Add benchmark for concatenating small arrays<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#[macro_use]
extern crate criterion;
use criterion::Criterion;

use rand::distributions::{Alphanumeric, Distribution, Standard};
use rand::Rng;

use std::sync::Arc;

extern crate arrow;

use arrow::array::*;
use arrow::compute::concat;
use arrow::datatypes::*;
use arrow::util::test_util::seedable_rng;

// cast array from specified primitive array type to desired data type
fn create_primitive<T>(size: usize, null_density: f32) -> ArrayRef
where
    T: ArrowPrimitiveType,
    Standard: Distribution<T::Native>,
    PrimitiveArray<T>: std::convert::From<Vec<T::Native>>,
{
    let mut rng = seedable_rng();

    let array: PrimitiveArray<T> = seedable_rng()
        .sample_iter(&Standard)
        .take(size)
        .map(|value| {
            let x = rng.gen::<f32>();
            if x < null_density {
                Some(value)
            } else {
                None
            }
        })
        .collect();

    Arc::new(array) as ArrayRef
}

fn create_strings(size: usize, null_density: f32) -> ArrayRef {
    let rng = &mut seedable_rng();

    let mut builder = StringBuilder::new(size);
    for _ in 0..size {
        let x = rng.gen::<f32>();
        if x < null_density {
            let value = rng.sample_iter(&Alphanumeric).take(4).collect::<String>();
            builder.append_value(&value).unwrap();
        } else {
            builder.append_null().unwrap()
        }
    }
    Arc::new(builder.finish())
}

fn bench_concat(v1: &ArrayRef, v2: &ArrayRef) {
    criterion::black_box(concat(&[v1.as_ref(), v2.as_ref()]).unwrap());
}

fn bench_concat_arrays(arrays: &[&dyn Array]) {
    criterion::black_box(concat(arrays).unwrap());
}

fn add_benchmark(c: &mut Criterion) {
    let v1 = create_primitive::<Int32Type>(1024, 0.0);
    let v2 = create_primitive::<Int32Type>(1024, 0.0);
    c.bench_function("concat i32 1024", |b| b.iter(|| bench_concat(&v1, &v2)));

    let v1 = create_primitive::<Int32Type>(1024, 0.5);
    let v2 = create_primitive::<Int32Type>(1024, 0.5);
    c.bench_function("concat i32 nulls 1024", |b| {
        b.iter(|| bench_concat(&v1, &v2))
    });

    let small_array = create_primitive::<Int32Type>(4, 0.0);
    let arrays: Vec<_> = (0..1024).map(|_| small_array.as_ref()).collect();
    c.bench_function("concat 1024 arrays i32 4", |b| {
        b.iter(|| bench_concat_arrays(&arrays))
    });

    let v1 = create_strings(1024, 0.0);
    let v2 = create_strings(1024, 0.0);
    c.bench_function("concat str 1024", |b| b.iter(|| bench_concat(&v1, &v2)));

    let v1 = create_strings(1024, 0.5);
    let v2 = create_strings(1024, 0.5);
    c.bench_function("concat str nulls 1024", |b| {
        b.iter(|| bench_concat(&v1, &v2))
    });
}

criterion_group!(benches, add_benchmark);
criterion_main!(benches);
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use llvm::ValueRef;
use rustc::middle::ty::Ty;
use rustc_mir::repr as mir;
use rustc_mir::tcx::LvalueTy;
use trans::adt;
use trans::base;
use trans::build;
use trans::common::{self, Block};
use trans::debuginfo::DebugLoc;
use trans::machine;

use std::ptr;

use super::{MirContext, TempRef};

#[derive(Copy, Clone)]
pub struct LvalueRef<'tcx> {
    /// Pointer to the contents of the lvalue
    pub llval: ValueRef,

    /// This lvalue's extra data if it is unsized, or null
    pub llextra: ValueRef,

    /// Monomorphized type of this lvalue, including variant information
    pub ty: LvalueTy<'tcx>,
}

impl<'tcx> LvalueRef<'tcx> {
    pub fn new_sized(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> {
        LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty }
    }

    pub fn alloca<'bcx>(bcx: Block<'bcx, 'tcx>,
                        ty: Ty<'tcx>,
                        name: &str)
                        -> LvalueRef<'tcx>
    {
        let lltemp = base::alloc_ty(bcx, ty, name);
        LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))
    }
}

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
    pub fn lvalue_len(&mut self,
                      _bcx: Block<'bcx, 'tcx>,
                      _lvalue: LvalueRef<'tcx>)
                      -> ValueRef {
        unimplemented!()
    }

    pub fn trans_lvalue(&mut self,
                        bcx: Block<'bcx, 'tcx>,
                        lvalue: &mir::Lvalue<'tcx>)
                        -> LvalueRef<'tcx> {
        debug!("trans_lvalue(lvalue={:?})", lvalue);

        let fcx = bcx.fcx;
        let ccx = fcx.ccx;
        let tcx = bcx.tcx();
        match *lvalue {
            mir::Lvalue::Var(index) => self.vars[index as usize],
            mir::Lvalue::Temp(index) => match self.temps[index as usize] {
                TempRef::Lvalue(lvalue) =>
                    lvalue,
                TempRef::Operand(..) =>
                    tcx.sess.bug(&format!("using operand temp {:?} as lvalue", lvalue)),
            },
            mir::Lvalue::Arg(index) => self.args[index as usize],
            mir::Lvalue::Static(def_id) => {
                let const_ty = self.mir.lvalue_ty(tcx, lvalue);
                LvalueRef::new(common::get_static_val(ccx, def_id, const_ty.to_ty(tcx)), const_ty)
            },
            mir::Lvalue::ReturnPointer => {
                let return_ty = bcx.monomorphize(&self.mir.return_ty);
                let llval = fcx.get_ret_slot(bcx, return_ty, "return");
                LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty.unwrap()))
            }
            mir::Lvalue::Projection(ref projection) => {
                let tr_base = self.trans_lvalue(bcx, &projection.base);
                let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem);
                let (llprojected, llextra) = match projection.elem {
                    mir::ProjectionElem::Deref => {
                        let base_ty = tr_base.ty.to_ty(tcx);
                        if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) {
                            (base::load_ty(bcx, tr_base.llval, base_ty),
                             ptr::null_mut())
                        } else {
                            base::load_fat_ptr(bcx, tr_base.llval, base_ty)
                        }
                    }
                    mir::ProjectionElem::Field(ref field) => {
                        let base_ty = tr_base.ty.to_ty(tcx);
                        let base_repr = adt::represent_type(ccx, base_ty);
                        let discr = match tr_base.ty {
                            LvalueTy::Ty { .. } => 0,
                            LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v,
                        };
                        let discr = discr as u64;
                        (adt::trans_field_ptr(bcx, &base_repr, tr_base.llval, discr, field.index()),
                         if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) {
                             ptr::null_mut()
                         } else {
                             tr_base.llextra
                         })
                    }
                    mir::ProjectionElem::Index(ref index) => {
                        let index = self.trans_operand(bcx, index);
                        let llindex = self.prepare_index(bcx, index.immediate());
                        (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]),
                         ptr::null_mut())
                    }
                    mir::ProjectionElem::ConstantIndex { offset,
                                                         from_end: false,
                                                         min_length: _ } => {
                        let lloffset = common::C_u32(bcx.ccx(), offset);
                        let llindex = self.prepare_index(bcx, lloffset);
                        (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]),
                         ptr::null_mut())
                    }
                    mir::ProjectionElem::ConstantIndex { offset,
                                                         from_end: true,
                                                         min_length: _ } => {
                        let lloffset = common::C_u32(bcx.ccx(), offset);
                        let lllen = self.lvalue_len(bcx, tr_base);
                        let llindex = build::Sub(bcx, lllen, lloffset, DebugLoc::None);
                        let llindex = self.prepare_index(bcx, llindex);
                        (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]),
                         ptr::null_mut())
                    }
                    mir::ProjectionElem::Downcast(..) => {
                        (tr_base.llval, tr_base.llextra)
                    }
                };
                LvalueRef {
                    llval: llprojected,
                    llextra: llextra,
                    ty: projected_ty,
                }
            }
        }
    }

    /// Adjust the bitwidth of an index since LLVM is less forgiving
    /// than we are.
    ///
    /// nmatsakis: is this still necessary? Not sure.
    fn prepare_index(&mut self,
                     bcx: Block<'bcx, 'tcx>,
                     llindex: ValueRef)
                     -> ValueRef
    {
        let ccx = bcx.ccx();
        let index_size = machine::llbitsize_of_real(bcx.ccx(), common::val_ty(llindex));
        let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type());
        if index_size < int_size {
            build::ZExt(bcx, llindex, ccx.int_type())
        } else if index_size > int_size {
            build::Trunc(bcx, llindex, ccx.int_type())
        } else {
            llindex
        }
    }
}
<commit_msg>implement lvalue_len<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use llvm::ValueRef;
use rustc::middle::ty::{self, Ty};
use rustc_mir::repr as mir;
use rustc_mir::tcx::LvalueTy;
use trans::adt;
use trans::base;
use trans::build;
use trans::common::{self, Block};
use trans::debuginfo::DebugLoc;
use trans::machine;

use std::ptr;

use super::{MirContext, TempRef};

#[derive(Copy, Clone)]
pub struct LvalueRef<'tcx> {
    /// Pointer to the contents of the lvalue
    pub llval: ValueRef,

    /// This lvalue's extra data if it is unsized, or null
    pub llextra: ValueRef,

    /// Monomorphized type of this lvalue, including variant information
    pub ty: LvalueTy<'tcx>,
}

impl<'tcx> LvalueRef<'tcx> {
    pub fn new_sized(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> {
        LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty }
    }

    pub fn alloca<'bcx>(bcx: Block<'bcx, 'tcx>,
                        ty: Ty<'tcx>,
                        name: &str)
                        -> LvalueRef<'tcx>
    {
        let lltemp = base::alloc_ty(bcx, ty, name);
        LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))
    }
}

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
    pub fn lvalue_len(&mut self,
                      bcx: Block<'bcx, 'tcx>,
                      lvalue: LvalueRef<'tcx>)
                      -> ValueRef {
        match lvalue.ty.to_ty(bcx.tcx()).sty {
            ty::TyArray(_, n) => common::C_uint(bcx.ccx(), n),
            ty::TySlice(_) | ty::TyStr => {
                assert!(lvalue.llextra != ptr::null_mut());
                lvalue.llextra
            }
            _ => bcx.sess().bug("unexpected type in get_base_and_len"),
        }
    }

    pub fn trans_lvalue(&mut self,
                        bcx: Block<'bcx, 'tcx>,
                        lvalue: &mir::Lvalue<'tcx>)
                        -> LvalueRef<'tcx> {
        debug!("trans_lvalue(lvalue={:?})", lvalue);

        let fcx = bcx.fcx;
        let ccx = fcx.ccx;
        let tcx = bcx.tcx();
        match *lvalue {
            mir::Lvalue::Var(index) => self.vars[index as usize],
            mir::Lvalue::Temp(index) => match self.temps[index as usize] {
                TempRef::Lvalue(lvalue) =>
                    lvalue,
                TempRef::Operand(..) =>
                    tcx.sess.bug(&format!("using operand temp {:?} as lvalue", lvalue)),
            },
            mir::Lvalue::Arg(index) => self.args[index as usize],
            mir::Lvalue::Static(def_id) => {
                let const_ty = self.mir.lvalue_ty(tcx, lvalue);
                LvalueRef::new(common::get_static_val(ccx, def_id, const_ty.to_ty(tcx)), const_ty)
            },
            mir::Lvalue::ReturnPointer => {
                let return_ty = bcx.monomorphize(&self.mir.return_ty);
                let llval = fcx.get_ret_slot(bcx, return_ty, "return");
                LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty.unwrap()))
            }
            mir::Lvalue::Projection(ref projection) => {
                let tr_base = self.trans_lvalue(bcx, &projection.base);
                let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem);
                let (llprojected, llextra) = match projection.elem {
                    mir::ProjectionElem::Deref => {
                        let base_ty = tr_base.ty.to_ty(tcx);
                        if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) {
                            (base::load_ty(bcx, tr_base.llval, base_ty),
                             ptr::null_mut())
                        } else {
                            base::load_fat_ptr(bcx, tr_base.llval, base_ty)
                        }
                    }
                    mir::ProjectionElem::Field(ref field) => {
                        let base_ty = tr_base.ty.to_ty(tcx);
                        let base_repr = adt::represent_type(ccx, base_ty);
                        let discr = match tr_base.ty {
                            LvalueTy::Ty { .. } => 0,
                            LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v,
                        };
                        let discr = discr as u64;
                        (adt::trans_field_ptr(bcx, &base_repr, tr_base.llval, discr, field.index()),
                         if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) {
                             ptr::null_mut()
                         } else {
                             tr_base.llextra
                         })
                    }
                    mir::ProjectionElem::Index(ref index) => {
                        let index = self.trans_operand(bcx, index);
                        let llindex = self.prepare_index(bcx, index.immediate());
                        (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]),
                         ptr::null_mut())
                    }
                    mir::ProjectionElem::ConstantIndex { offset,
                                                         from_end: false,
                                                         min_length: _ } => {
                        let lloffset = common::C_u32(bcx.ccx(), offset);
                        let llindex = self.prepare_index(bcx, lloffset);
                        (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]),
                         ptr::null_mut())
                    }
                    mir::ProjectionElem::ConstantIndex { offset,
                                                         from_end: true,
                                                         min_length: _ } => {
                        let lloffset = common::C_u32(bcx.ccx(), offset);
                        let lllen = self.lvalue_len(bcx, tr_base);
                        let llindex = build::Sub(bcx, lllen, lloffset, DebugLoc::None);
                        let llindex = self.prepare_index(bcx, llindex);
                        (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]),
                         ptr::null_mut())
                    }
                    mir::ProjectionElem::Downcast(..) => {
                        (tr_base.llval, tr_base.llextra)
                    }
                };
                LvalueRef {
                    llval: llprojected,
                    llextra: llextra,
                    ty: projected_ty,
                }
            }
        }
    }

    /// Adjust the bitwidth of an index since LLVM is less forgiving
    /// than we are.
    ///
    /// nmatsakis: is this still necessary? Not sure.
    fn prepare_index(&mut self,
                     bcx: Block<'bcx, 'tcx>,
                     llindex: ValueRef)
                     -> ValueRef
    {
        let ccx = bcx.ccx();
        let index_size = machine::llbitsize_of_real(bcx.ccx(), common::val_ty(llindex));
        let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type());
        if index_size < int_size {
            build::ZExt(bcx, llindex, ccx.int_type())
        } else if index_size > int_size {
            build::Trunc(bcx, llindex, ccx.int_type())
        } else {
            llindex
        }
    }
}
<|endoftext|>


<commit_before>use nom::{IResult, is_space};
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::hash::Hash;
use std::iter::FromIterator;
use std::str::FromStr;

use util::parsing::*;

/// Structure containing the elements of type `A` in a equivalence class of type `B`
#[derive(Debug, Eq, PartialEq)]
pub struct EquivalenceClass<A, B>
    where A: Eq + Hash,
{
    label: B,
    set: Option<HashSet<A>>,
}

impl<A, B> From<(B, Option<Vec<A>>)> for EquivalenceClass<A, B>
    where A: Eq + Hash,
{
    fn from((label, set): (B, Option<Vec<A>>)) -> EquivalenceClass<A, B> {
        EquivalenceClass {
            label,
            set: set.and_then(|elements| Some(HashSet::from_iter(elements))),
        }
    }
}

/// A struct containing a remapping of elements of type `A` into their respective equivalence classes of type `B`.
#[derive(Clone, Debug)]
pub struct EquivalenceRelation<A, B>
    where A: Eq + Hash,
{
    map: HashMap<A, B>,
    default: B,
}

impl<A, B> PartialEq for EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        self.default == other.default && self.map == other.map
    }
}

impl<A, B> EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: Clone + Eq + Hash,
{
    pub fn new(map: HashMap<B, HashSet<A>>, default: B) -> Self {
        let mut rmap = HashMap::new();
        for (class_name, members) in map {
            for value in members {
                match rmap.entry(value) {
                    Entry::Vacant(v) => { v.insert(class_name.clone()); },
                    Entry::Occupied(_) => panic!("The same value occurs in two equivalence classes!"),
                }
            }
        }

        EquivalenceRelation {
            map: rmap,
            default,
        }
    }

    // returns the equivalence class of a given value
    pub fn project(&self, key: &A) -> B {
        match self.map.get(key) {
            None => self.default.clone(),
            Some(e) => e.clone(),
        }
    }
}

impl<A, B> From<Vec<EquivalenceClass<A, B>>> for EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: Clone + Eq + Hash,
{
    fn from(classes: Vec<EquivalenceClass<A, B>>) -> EquivalenceRelation<A, B> {
        let mut map = HashMap::new();
        let mut default = None;

        for EquivalenceClass { label, set } in classes {
            if let Some(elements) = set {
                for element in elements {
                    map.insert(element, label.clone());
                }
            } else {
                default = Some(label);
            }
        }

        EquivalenceRelation { map, default: default.unwrap() }
    }
}

impl<A, B> FromStr for EquivalenceRelation<A, B>
    where A: Clone + Eq + Hash + FromStr,
          A::Err: Debug,
          B: Clone + Eq + Hash + FromStr,
          B::Err: Debug,
{
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut map = HashMap::new();
        let mut default = match parse_token(s.as_bytes()) {
            IResult::Done(_, result) => result,
            _                        => return Err(format!("Could not parse {}", s)),
        };
        for l in s.lines() {
            if !l.is_empty() {
                match l.trim().parse()? {
                    EquivalenceClass { label, set: Some(elements) } => {
                        map.insert(label, elements);
                    },
                    EquivalenceClass { label, set: None } => {
                        default = label;
                    },
                }
            }
        }

        Ok(EquivalenceRelation::new(map, default))

    }
}

impl <A, B> FromStr for EquivalenceClass<A, B>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
          B: FromStr,
          B::Err: Debug,
{
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match parse_set(s.as_bytes()) {
            IResult::Done(_, result) => Ok(result),
            _                        => Err(format!("Could not parse {}", s))
        }
    }
}

fn parse_set<A, B>(input: &[u8]) -> IResult<&[u8], EquivalenceClass<A, B>>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
          B: FromStr,
          B::Err: Debug,
{
    do_parse!(
        input,
        label: parse_token >>
            take_while!(is_space) >>
            set: alt!(
                do_parse!(
                    tag!("*") >> (None)
                )
                    |
                do_parse!(
                    the_set: parse_heap >> (Some(the_set))
                )
            ) >>
            (EquivalenceClass {
                label,
                set,
            })
    )
}

fn parse_heap<A>(input: &[u8]) -> IResult<&[u8], HashSet<A>>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
{
    match parse_vec(input, parse_token, "[", "]", ",") {
        IResult::Done(rest, parsed) => IResult::Done(rest, HashSet::from_iter(parsed)),
        IResult::Incomplete(needed) => IResult::Incomplete(needed),
        IResult::Error(error) => IResult::Error(error),
    }
}

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

    #[test]
    fn test_equivalence_relation_project() {
        let rel: EquivalenceRelation<u8, u8> = String::from("0 [0, 1]\n1 [2, 4]\n2 *").parse().unwrap();
        assert_eq!(0, rel.project(&1));
        assert_eq!(1, rel.project(&2));
        assert_eq!(2, rel.project(&3));
        assert_eq!(1, rel.project(&4));
    }

    #[test]
    fn test_equivalence_class_from_str_legal_input() {
        let legal_inputs = vec![
            ("0 [0, 1]", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0  [1, 0]", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0  [0, 1, 1]", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0 *", EquivalenceClass::from((0, None))),
        ];

        for (legal_input, correct_class) in legal_inputs {
            assert_eq!(correct_class,
                       EquivalenceClass::from_str(legal_input).unwrap());
        }
    }

    #[test]
    fn test_equivalence_class_from_str_illegal_input() {
        let illegal_inputs = vec![
            " 0 [0, 1]",
            "[0, 1]",
            "*",
        ];

        for illegal_input in illegal_inputs {
            match EquivalenceClass::<u8, u8>::from_str(illegal_input) {
                Ok(parsed) =>
                    panic!("Was able to parse the illegal input \'{}\' as \'{:?}\'",
                           illegal_input, parsed),
                Err(_) => (),
            }
        }
    }

    #[test]
    fn test_equivalence_relation_from_str_legal_input() {
        let legal_inputs = vec![
            ("0 [0, 1]\n1 [2, 3]\n2 *", EquivalenceRelation::from(vec![
                EquivalenceClass::from((0, Some(vec![0, 1]))),
                EquivalenceClass::from((1, Some(vec![2, 3]))),
                EquivalenceClass::from((2, None)),
            ])),
        ];

        for (legal_input, correct_relation) in legal_inputs {
            assert_eq!(correct_relation,
                       EquivalenceRelation::from_str(legal_input).unwrap());
        }
    }

    #[test]
    fn test_equivalence_relation_from_str_illegal_input() {
        let illegal_inputs = vec![
            " 0 [0, 1]\n1 [2, 3]\n2 *",
            "0 [0, 1]1 [2, 3]2 *",
            "0 [0, 1]\n1 [1, 2]\n2 *",
        ];

        for illegal_input in illegal_inputs {
            match EquivalenceRelation::<u8, u8>::from_str(illegal_input) {
                Ok(parsed) =>
                    panic!("Was able to parse the illegal input \'{}\' as \'{:?}\'",
                           illegal_input, parsed),
                Err(_) => (),
            }
        }
    }
}
<commit_msg>src/approximation/equivalence_classes.rs: added EquivalenceRelation::new_safe()<commit_after>use nom::{IResult, is_space};
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::hash::Hash;
use std::iter::FromIterator;
use std::str::FromStr;

use util::parsing::*;

/// Structure containing the elements of type `A` in a equivalence class of type `B`
#[derive(Debug, Eq, PartialEq)]
pub struct EquivalenceClass<A, B>
    where A: Eq + Hash,
{
    label: B,
    set: Option<HashSet<A>>,
}

impl<A, B> From<(B, Option<Vec<A>>)> for EquivalenceClass<A, B>
    where A: Eq + Hash,
{
    fn from((label, set): (B, Option<Vec<A>>)) -> EquivalenceClass<A, B> {
        EquivalenceClass {
            label,
            set: set.and_then(|elements| Some(HashSet::from_iter(elements))),
        }
    }
}

/// A struct containing a remapping of elements of type `A` into their respective equivalence classes of type `B`.
#[derive(Clone, Debug)]
pub struct EquivalenceRelation<A, B>
    where A: Eq + Hash,
{
    map: HashMap<A, B>,
    default: B,
}

impl<A, B> PartialEq for EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        self.default == other.default && self.map == other.map
    }
}

impl<A, B> EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: Clone + Eq + Hash,
{
    pub fn new(map: HashMap<B, HashSet<A>>, default: B) -> Self {
        match Self::new_safe(map, default) {
            Ok(relation) => relation,
            Err(error) => panic!(error),
        }
    }

    pub fn new_safe(map: HashMap<B, HashSet<A>>, default: B) -> Result<Self, String> {
        let mut relation_map = HashMap::new();
        for (class_name, members) in map {
            if class_name == default {
                return Err(String::from(
                    "There can only be one default class in the equivalence relation!"
                ));
            }

            for value in members {
                match relation_map.entry(value) {
                    Entry::Vacant(v) => {
                        v.insert(class_name.clone());
                    },
                    Entry::Occupied(_) => {
                        return Err(String::from(
                            "All classes of the equivalence relation must be disjoint!"
                        ));
                    },
                }
            }
        }

        Ok(EquivalenceRelation { map: relation_map, default })
    }

    // returns the equivalence class of a given value
    pub fn project(&self, key: &A) -> B {
        match self.map.get(key) {
            None => self.default.clone(),
            Some(e) => e.clone(),
        }
    }
}

impl<A, B> From<Vec<EquivalenceClass<A, B>>> for EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: Clone + Eq + Hash,
{
    fn from(classes: Vec<EquivalenceClass<A, B>>) -> EquivalenceRelation<A, B> {
        let mut map = HashMap::new();
        let mut default = None;

        for EquivalenceClass { label, set } in classes {
            if let Some(elements) = set {
                for element in elements {
                    map.insert(element, label.clone());
                }
            } else {
                default = Some(label);
            }
        }

        EquivalenceRelation { map, default: default.unwrap() }
    }
}

impl<A, B> FromStr for EquivalenceRelation<A, B>
    where A: Clone + Eq + Hash + FromStr,
          A::Err: Debug,
          B: Clone + Eq + Hash + FromStr,
          B::Err: Debug,
{
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut map = HashMap::new();
        let mut default = match parse_token(s.as_bytes()) {
            IResult::Done(_, result) => result,
            _                        => return Err(format!("Could not parse {}", s)),
        };
        for l in s.lines() {
            if !l.is_empty() {
                match l.trim().parse()? {
                    EquivalenceClass { label, set: Some(elements) } => {
                        map.insert(label, elements);
                    },
                    EquivalenceClass { label, set: None } => {
                        default = label;
                    },
                }
            }
        }

        Ok(EquivalenceRelation::new(map, default))

    }
}

impl <A, B> FromStr for EquivalenceClass<A, B>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
          B: FromStr,
          B::Err: Debug,
{
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match parse_set(s.as_bytes()) {
            IResult::Done(_, result) => Ok(result),
            _                        => Err(format!("Could not parse {}", s))
        }
    }
}

fn parse_set<A, B>(input: &[u8]) -> IResult<&[u8], EquivalenceClass<A, B>>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
          B: FromStr,
          B::Err: Debug,
{
    do_parse!(
        input,
        label: parse_token >>
            take_while!(is_space) >>
            set: alt!(
                do_parse!(
                    tag!("*") >> (None)
                )
                    |
                do_parse!(
                    the_set: parse_heap >> (Some(the_set))
                )
            ) >>
            (EquivalenceClass {
                label,
                set,
            })
    )
}

fn parse_heap<A>(input: &[u8]) -> IResult<&[u8], HashSet<A>>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
{
    match parse_vec(input, parse_token, "[", "]", ",") {
        IResult::Done(rest, parsed) => IResult::Done(rest, HashSet::from_iter(parsed)),
        IResult::Incomplete(needed) => IResult::Incomplete(needed),
        IResult::Error(error) => IResult::Error(error),
    }
}

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

    #[test]
    fn test_equivalence_relation_project() {
        let rel: EquivalenceRelation<u8, u8> = String::from("0 [0, 1]\n1 [2, 4]\n2 *").parse().unwrap();
        assert_eq!(0, rel.project(&1));
        assert_eq!(1, rel.project(&2));
        assert_eq!(2, rel.project(&3));
        assert_eq!(1, rel.project(&4));
    }

    #[test]
    fn test_equivalence_class_from_str_legal_input() {
        let legal_inputs = vec![
            ("0 [0, 1]", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0  [1, 0]", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0  [0, 1, 1]", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0 *", EquivalenceClass::from((0, None))),
        ];

        for (legal_input, correct_class) in legal_inputs {
            assert_eq!(correct_class,
                       EquivalenceClass::from_str(legal_input).unwrap());
        }
    }

    #[test]
    fn test_equivalence_class_from_str_illegal_input() {
        let illegal_inputs = vec![
            " 0 [0, 1]",
            "[0, 1]",
            "*",
        ];

        for illegal_input in illegal_inputs {
            match EquivalenceClass::<u8, u8>::from_str(illegal_input) {
                Ok(parsed) =>
                    panic!("Was able to parse the illegal input \'{}\' as \'{:?}\'",
                           illegal_input, parsed),
                Err(_) => (),
            }
        }
    }

    #[test]
    fn test_equivalence_relation_from_str_legal_input() {
        let legal_inputs = vec![
            ("0 [0, 1]\n1 [2, 3]\n2 *", EquivalenceRelation::from(vec![
                EquivalenceClass::from((0, Some(vec![0, 1]))),
                EquivalenceClass::from((1, Some(vec![2, 3]))),
                EquivalenceClass::from((2, None)),
            ])),
        ];

        for (legal_input, correct_relation) in legal_inputs {
            assert_eq!(correct_relation,
                       EquivalenceRelation::from_str(legal_input).unwrap());
        }
    }

    #[test]
    fn test_equivalence_relation_from_str_illegal_input() {
        let illegal_inputs = vec![
            " 0 [0, 1]\n1 [2, 3]\n2 *",
            "0 [0, 1]1 [2, 3]2 *",
            "0 [0, 1]\n1 [1, 2]\n2 *",
        ];

        for illegal_input in illegal_inputs {
            match EquivalenceRelation::<u8, u8>::from_str(illegal_input) {
                Ok(parsed) =>
                    panic!("Was able to parse the illegal input \'{}\' as \'{:?}\'",
                           illegal_input, parsed),
                Err(_) => (),
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Warns when a deprecated Lua operator is used (such as `~=` or `not`).

use compiler::Warning;
use compiler::visit::*;
use compiler::ast::*;
use op::*;

use std::ops::{Deref, DerefMut};
use std::collections::HashMap;

// Maps deprecated ops to their non-deprecated replacements
lazy_static! {
    pub static ref DEPR_BINOPS: HashMap<BinOp, BinOp> = {
        let mut m = HashMap::new();
        m.insert(BinOp::NEqLua, BinOp::NEq);
        m.insert(BinOp::LAndLua, BinOp::LAnd);
        m.insert(BinOp::LOrLua, BinOp::LOr);
        m
    };

    pub static ref DEPR_UNOPS: HashMap<UnOp, UnOp> = {
        let mut m = HashMap::new();
        m.insert(UnOp::LNotLua, UnOp::LNot);
        m
    };
}

struct DeprOps(Vec<Warning>);

impl Deref for DeprOps {
    type Target = Vec<Warning>;
    fn deref(&self) -> &Vec<Warning> {
        &self.0
    }
}

impl DerefMut for DeprOps {
    fn deref_mut(&mut self) -> &mut Vec<Warning> {
        &mut self.0
    }
}

impl <'a> Visitor<'a> for DeprOps {
    fn visit_expr(&mut self, e: &Expr) {
        match e.value {
            EBinOp(ref lhs, op, ref rhs) => {
                self.visit_expr(&**lhs);
                self.visit_expr(&**rhs);

                match DEPR_BINOPS.get(&op) {
                    Some(newop) => {
                        self.push(Warning::with_info(e.span, format!("use of deprecated operator `{}`", op),
                            vec![format!("use `{}` instead", newop)]));
                    }
                    _ => {},
                }
            },
            EUnOp(op, ref p) => {
                self.visit_expr(&**p);

                match DEPR_UNOPS.get(&op) {
                    Some(newop) => {
                        self.push(Warning::with_info(e.span, format!("use of deprecated operator `{}`", op),
                            vec![format!("use `{}` instead", newop)]));
                    },
                    _ => {},
                }
            },
            _ => { walk_expr_ref(e, self); },
        };
    }
}

pub fn run(main: Function) -> (Function, Vec<Warning>) {
    let mut v = DeprOps(vec![]);
    v.visit_func(&main);

    (main, v.0)
}
<commit_msg>Reformat warnings<commit_after>//! Warns when a deprecated Lua operator is used (such as `~=` or `not`).

use compiler::Warning;
use compiler::visit::*;
use compiler::ast::*;
use op::*;

use std::ops::{Deref, DerefMut};
use std::collections::HashMap;

// Maps deprecated ops to their non-deprecated replacements
lazy_static! {
    pub static ref DEPR_BINOPS: HashMap<BinOp, BinOp> = {
        let mut m = HashMap::new();
        m.insert(BinOp::NEqLua, BinOp::NEq);
        m.insert(BinOp::LAndLua, BinOp::LAnd);
        m.insert(BinOp::LOrLua, BinOp::LOr);
        m
    };

    pub static ref DEPR_UNOPS: HashMap<UnOp, UnOp> = {
        let mut m = HashMap::new();
        m.insert(UnOp::LNotLua, UnOp::LNot);
        m
    };
}

struct DeprOps(Vec<Warning>);

impl Deref for DeprOps {
    type Target = Vec<Warning>;
    fn deref(&self) -> &Vec<Warning> {
        &self.0
    }
}

impl DerefMut for DeprOps {
    fn deref_mut(&mut self) -> &mut Vec<Warning> {
        &mut self.0
    }
}

impl <'a> Visitor<'a> for DeprOps {
    fn visit_expr(&mut self, e: &Expr) {
        match e.value {
            EBinOp(ref lhs, op, ref rhs) => {
                self.visit_expr(&**lhs);
                self.visit_expr(&**rhs);

                match DEPR_BINOPS.get(&op) {
                    Some(newop) => {
                        self.push(Warning::with_info(e.span,
                            format!("use of deprecated operator `{}`", op),
                            vec![format!("use `{}` instead", newop)]
                        ));
                    }
                    _ => {},
                }
            },
            EUnOp(op, ref p) => {
                self.visit_expr(&**p);

                match DEPR_UNOPS.get(&op) {
                    Some(newop) => {
                        self.push(Warning::with_info(e.span,
                            format!("use of deprecated operator `{}`", op),
                            vec![format!("use `{}` instead", newop)]
                        ));
                    },
                    _ => {},
                }
            },
            _ => { walk_expr_ref(e, self); },
        };
    }
}

pub fn run(main: Function) -> (Function, Vec<Warning>) {
    let mut v = DeprOps(vec![]);
    v.visit_func(&main);

    (main, v.0)
}
<|endoftext|>


<commit_before>#[cfg(target_arch = "wasm32")]
macro_rules! check {
    ($check:expr) => {{
        $check
    }};
}

#[cfg(not(target_arch = "wasm32"))]
macro_rules! check {
    ($check:expr) => {{
        let result = $check;
        if br::ScInternalResult::Success != result {
            if br::ScInternalResult::CompilationError == result {
                let mut message_ptr = ptr::null();

                if br::ScInternalResult::Success
                    != br::sc_internal_get_latest_exception_message(&mut message_ptr)
                {
                    return Err(ErrorCode::Unhandled);
                }

                let message = match std::ffi::CStr::from_ptr(message_ptr)
                    .to_owned()
                    .into_string()
                {
                    Err(_) => return Err(ErrorCode::Unhandled),
                    Ok(v) => v,
                };

                if br::ScInternalResult::Success
                    != br::sc_internal_free_pointer(message_ptr as *mut std::os::raw::c_void)
                {
                    return Err(ErrorCode::Unhandled);
                }

                return Err(ErrorCode::CompilationError(message));
            }

            return Err(ErrorCode::Unhandled);
        }
    }};
}

mod compiler;

#[cfg(feature = "glsl")]
pub mod glsl;
#[cfg(all(feature = "hlsl", not(target_arch = "wasm32")))]
pub mod hlsl;
#[cfg(all(feature = "msl", not(target_arch = "wasm32")))]
pub mod msl;

pub mod spirv;

#[cfg(target_arch = "wasm32")]
pub(crate) mod emscripten;
pub(crate) mod ptr_util;

#[cfg(target_arch = "wasm32")]
mod bindings_wasm_functions;

#[cfg(target_arch = "wasm32")]
mod bindings {
    #![allow(dead_code)]
    #![allow(non_upper_case_globals)]
    #![allow(non_camel_case_types)]
    #![allow(non_snake_case)]
    include!(concat!("bindings_wasm.rs"));
    pub use crate::bindings_wasm_functions::*;
    pub use root::*;
}

#[cfg(not(target_arch = "wasm32"))]
mod bindings {
    #![allow(dead_code)]
    #![allow(non_upper_case_globals)]
    #![allow(non_camel_case_types)]
    #![allow(non_snake_case)]
    include!(concat!("bindings_native.rs"));
    pub use root::*;
}

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum ErrorCode {
    Unhandled,
    CompilationError(String),
}
<commit_msg>implement std::error::Error for ErrorCode<commit_after>#[cfg(target_arch = "wasm32")]
macro_rules! check {
    ($check:expr) => {{
        $check
    }};
}

#[cfg(not(target_arch = "wasm32"))]
macro_rules! check {
    ($check:expr) => {{
        let result = $check;
        if br::ScInternalResult::Success != result {
            if br::ScInternalResult::CompilationError == result {
                let mut message_ptr = ptr::null();

                if br::ScInternalResult::Success
                    != br::sc_internal_get_latest_exception_message(&mut message_ptr)
                {
                    return Err(ErrorCode::Unhandled);
                }

                let message = match std::ffi::CStr::from_ptr(message_ptr)
                    .to_owned()
                    .into_string()
                {
                    Err(_) => return Err(ErrorCode::Unhandled),
                    Ok(v) => v,
                };

                if br::ScInternalResult::Success
                    != br::sc_internal_free_pointer(message_ptr as *mut std::os::raw::c_void)
                {
                    return Err(ErrorCode::Unhandled);
                }

                return Err(ErrorCode::CompilationError(message));
            }

            return Err(ErrorCode::Unhandled);
        }
    }};
}

mod compiler;

#[cfg(feature = "glsl")]
pub mod glsl;
#[cfg(all(feature = "hlsl", not(target_arch = "wasm32")))]
pub mod hlsl;
#[cfg(all(feature = "msl", not(target_arch = "wasm32")))]
pub mod msl;

pub mod spirv;

#[cfg(target_arch = "wasm32")]
pub(crate) mod emscripten;
pub(crate) mod ptr_util;

#[cfg(target_arch = "wasm32")]
mod bindings_wasm_functions;

#[cfg(target_arch = "wasm32")]
mod bindings {
    #![allow(dead_code)]
    #![allow(non_upper_case_globals)]
    #![allow(non_camel_case_types)]
    #![allow(non_snake_case)]
    include!(concat!("bindings_wasm.rs"));
    pub use crate::bindings_wasm_functions::*;
    pub use root::*;
}

#[cfg(not(target_arch = "wasm32"))]
mod bindings {
    #![allow(dead_code)]
    #![allow(non_upper_case_globals)]
    #![allow(non_camel_case_types)]
    #![allow(non_snake_case)]
    include!(concat!("bindings_native.rs"));
    pub use root::*;
}

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum ErrorCode {
    Unhandled,
    CompilationError(String),
}

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

impl std::error::Error for ErrorCode {}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_upper_case_globals)]

use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
use llvm::{StructRetAttribute, ZExtAttribute};
use trans::cabi::{FnType, ArgType};
use trans::context::CrateContext;
use trans::type_::Type;

use std::cmp;

fn align_up_to(off: usize, a: usize) -> usize {
    return (off + a - 1) / a * a;
}

fn align(off: usize, ty: Type) -> usize {
    let a = ty_align(ty);
    return align_up_to(off, a);
}

fn ty_align(ty: Type) -> usize {
    match ty.kind() {
        Integer => ((ty.int_width() as usize) + 7) / 8,
        Pointer => 8,
        Float => 4,
        Double => 8,
        Struct => {
            if ty.is_packed() {
                1
            } else {
                let str_tys = ty.field_types();
                str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t)))
            }
        }
        Array => {
            let elt = ty.element_type();
            ty_align(elt)
        }
        Vector => {
            let len = ty.vector_length();
            let elt = ty.element_type();
            ty_align(elt) * len
        }
        _ => panic!("ty_align: unhandled type")
    }
}

fn ty_size(ty: Type) -> usize {
    match ty.kind() {
        Integer => ((ty.int_width() as usize) + 7) / 8,
        Pointer => 8,
        Float => 4,
        Double => 8,
        Struct => {
            if ty.is_packed() {
                let str_tys = ty.field_types();
                str_tys.iter().fold(0, |s, t| s + ty_size(*t))
            } else {
                let str_tys = ty.field_types();
                let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t));
                align(size, ty)
            }
        }
        Array => {
            let len = ty.array_length();
            let elt = ty.element_type();
            let eltsz = ty_size(elt);
            len * eltsz
        }
        Vector => {
            let len = ty.vector_length();
            let elt = ty.element_type();
            let eltsz = ty_size(elt);
            len * eltsz
        }
        _ => panic!("ty_size: unhandled type")
    }
}

fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
    if is_reg_ty(ty) {
        let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None };
        return ArgType::direct(ty, None, None, attr);
    }
    let size = ty_size(ty);
    if size <= 16 {
        let llty = if size <= 1 {
            Type::i8(ccx)
        } else if size <= 2 {
            Type::i16(ccx)
        } else if size <= 4 {
            Type::i32(ccx)
        } else if size <= 8 {
            Type::i64(ccx)
        } else {
            Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
        };
        return ArgType::direct(ty, Some(llty), None, None);
    }
    ArgType::indirect(ty, Some(StructRetAttribute))
}

fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType {
    if is_reg_ty(ty) {
        let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None };
        return ArgType::direct(ty, None, None, attr);
    }
    let size = ty_size(ty);
    if size <= 16 {
        let llty = if size == 0 {
            Type::array(&Type::i64(ccx), 0)
        } else if size == 1 {
            Type::i8(ccx)
        } else if size == 2 {
            Type::i16(ccx)
        } else if size <= 4 {
            Type::i32(ccx)
        } else if size <= 8 {
            Type::i64(ccx)
        } else {
            Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
        };
        return ArgType::direct(ty, Some(llty), None, None);
    }
    ArgType::indirect(ty, None)
}

fn is_reg_ty(ty: Type) -> bool {
    match ty.kind() {
        Integer
        | Pointer
        | Float
        | Double
        | Vector => true,
        _ => false
    }
}

pub fn compute_abi_info(ccx: &CrateContext,
                        atys: &[Type],
                        rty: Type,
                        ret_def: bool) -> FnType {
    let mut arg_tys = Vec::new();
    for &aty in atys {
        let ty = classify_arg_ty(ccx, aty);
        arg_tys.push(ty);
    }

    let ret_ty = if ret_def {
        classify_ret_ty(ccx, rty)
    } else {
        ArgType::direct(Type::void(ccx), None, None, None)
    };

    return FnType {
        arg_tys: arg_tys,
        ret_ty: ret_ty,
    };
}
<commit_msg>Auto merge of #24181 - jgallagher:aarch64-hfa, r=pnkfelix<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_upper_case_globals)]

use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
use llvm::{StructRetAttribute, ZExtAttribute};
use trans::cabi::{FnType, ArgType};
use trans::context::CrateContext;
use trans::type_::Type;

use std::cmp;

fn align_up_to(off: usize, a: usize) -> usize {
    return (off + a - 1) / a * a;
}

fn align(off: usize, ty: Type) -> usize {
    let a = ty_align(ty);
    return align_up_to(off, a);
}

fn ty_align(ty: Type) -> usize {
    match ty.kind() {
        Integer => ((ty.int_width() as usize) + 7) / 8,
        Pointer => 8,
        Float => 4,
        Double => 8,
        Struct => {
            if ty.is_packed() {
                1
            } else {
                let str_tys = ty.field_types();
                str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t)))
            }
        }
        Array => {
            let elt = ty.element_type();
            ty_align(elt)
        }
        Vector => {
            let len = ty.vector_length();
            let elt = ty.element_type();
            ty_align(elt) * len
        }
        _ => panic!("ty_align: unhandled type")
    }
}

fn ty_size(ty: Type) -> usize {
    match ty.kind() {
        Integer => ((ty.int_width() as usize) + 7) / 8,
        Pointer => 8,
        Float => 4,
        Double => 8,
        Struct => {
            if ty.is_packed() {
                let str_tys = ty.field_types();
                str_tys.iter().fold(0, |s, t| s + ty_size(*t))
            } else {
                let str_tys = ty.field_types();
                let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t));
                align(size, ty)
            }
        }
        Array => {
            let len = ty.array_length();
            let elt = ty.element_type();
            let eltsz = ty_size(elt);
            len * eltsz
        }
        Vector => {
            let len = ty.vector_length();
            let elt = ty.element_type();
            let eltsz = ty_size(elt);
            len * eltsz
        }
        _ => panic!("ty_size: unhandled type")
    }
}

fn is_homogenous_aggregate_ty(ty: Type) -> Option<(Type, u64)> {
    fn check_array(ty: Type) -> Option<(Type, u64)> {
        let len = ty.array_length() as u64;
        if len == 0 {
            return None
        }
        let elt = ty.element_type();

        // if our element is an HFA/HVA, so are we; multiply members by our len
        is_homogenous_aggregate_ty(elt).map(|(base_ty, members)| (base_ty, len * members))
    }

    fn check_struct(ty: Type) -> Option<(Type, u64)> {
        let str_tys = ty.field_types();
        if str_tys.len() == 0 {
            return None
        }

        let mut prev_base_ty = None;
        let mut members = 0;
        for opt_homog_agg in str_tys.iter().map(|t| is_homogenous_aggregate_ty(*t)) {
            match (prev_base_ty, opt_homog_agg) {
                // field isn't itself an HFA, so we aren't either
                (_, None) => return None,

                // first field - store its type and number of members
                (None, Some((field_ty, field_members))) => {
                    prev_base_ty = Some(field_ty);
                    members = field_members;
                },

                // 2nd or later field - give up if it's a different type; otherwise incr. members
                (Some(prev_ty), Some((field_ty, field_members))) => {
                    if prev_ty != field_ty {
                        return None;
                    }
                    members += field_members;
                }
            }
        }

        // Because of previous checks, we know prev_base_ty is Some(...) because
        //   1. str_tys has at least one element; and
        //   2. prev_base_ty was filled in (or we would've returned early)
        let (base_ty, members) = (prev_base_ty.unwrap(), members);

        // Ensure there is no padding.
        if ty_size(ty) == ty_size(base_ty) * (members as usize) {
            Some((base_ty, members))
        } else {
            None
        }
    }

    let homog_agg = match ty.kind() {
        Float  => Some((ty, 1)),
        Double => Some((ty, 1)),
        Array  => check_array(ty),
        Struct => check_struct(ty),
        Vector => match ty_size(ty) {
            4|8 => Some((ty, 1)),
            _   => None
        },
        _ => None
    };

    // Ensure we have at most four uniquely addressable members
    homog_agg.and_then(|(base_ty, members)| {
        if members > 0 && members <= 4 {
            Some((base_ty, members))
        } else {
            None
        }
    })
}

fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
    if is_reg_ty(ty) {
        let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None };
        return ArgType::direct(ty, None, None, attr);
    }
    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) {
        let llty = Type::array(&base_ty, members);
        return ArgType::direct(ty, Some(llty), None, None);
    }
    let size = ty_size(ty);
    if size <= 16 {
        let llty = if size <= 1 {
            Type::i8(ccx)
        } else if size <= 2 {
            Type::i16(ccx)
        } else if size <= 4 {
            Type::i32(ccx)
        } else if size <= 8 {
            Type::i64(ccx)
        } else {
            Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
        };
        return ArgType::direct(ty, Some(llty), None, None);
    }
    ArgType::indirect(ty, Some(StructRetAttribute))
}

fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType {
    if is_reg_ty(ty) {
        let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None };
        return ArgType::direct(ty, None, None, attr);
    }
    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) {
        let llty = Type::array(&base_ty, members);
        return ArgType::direct(ty, Some(llty), None, None);
    }
    let size = ty_size(ty);
    if size <= 16 {
        let llty = if size == 0 {
            Type::array(&Type::i64(ccx), 0)
        } else if size == 1 {
            Type::i8(ccx)
        } else if size == 2 {
            Type::i16(ccx)
        } else if size <= 4 {
            Type::i32(ccx)
        } else if size <= 8 {
            Type::i64(ccx)
        } else {
            Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
        };
        return ArgType::direct(ty, Some(llty), None, None);
    }
    ArgType::indirect(ty, None)
}

fn is_reg_ty(ty: Type) -> bool {
    match ty.kind() {
        Integer
        | Pointer
        | Float
        | Double
        | Vector => true,
        _ => false
    }
}

pub fn compute_abi_info(ccx: &CrateContext,
                        atys: &[Type],
                        rty: Type,
                        ret_def: bool) -> FnType {
    let mut arg_tys = Vec::new();
    for &aty in atys {
        let ty = classify_arg_ty(ccx, aty);
        arg_tys.push(ty);
    }

    let ret_ty = if ret_def {
        classify_ret_ty(ccx, rty)
    } else {
        ArgType::direct(Type::void(ccx), None, None, None)
    };

    return FnType {
        arg_tys: arg_tys,
        ret_ty: ret_ty,
    };
}
<|endoftext|>