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

use net_traits::{ControlMsg, CookieSource, LoadData, Metadata, LoadConsumer};
use net_traits::ProgressMsg::{Payload, Done};
use mime_classifier::MIMEClassifier;
use resource_task::{start_sending_opt, start_sending_sniffed_opt};

use log;
use std::collections::HashSet;
use file_loader;
use flate2::read::{DeflateDecoder, GzDecoder};
use hyper::client::Request;
use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host, Location, qitem, Quality, QualityItem};
use hyper::HttpError;
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::net::HttpConnector;
use hyper::status::{StatusCode, StatusClass};
use std::error::Error;
use openssl::ssl::{SslContext, SslVerifyMode};
use std::io::{self, Read, Write};
use std::sync::Arc;
use std::sync::mpsc::{Sender, channel};
use std::thunk::Invoke;
use util::task::spawn_named;
use util::resource_files::resources_dir_path;
use util::opts;
use url::{Url, UrlParser};

use std::borrow::ToOwned;

pub fn factory(cookies_chan: Sender<ControlMsg>)
               -> Box<Invoke<(LoadData, LoadConsumer, Arc<MIMEClassifier>)> + Send> {
    box move |(load_data, senders, classifier)| {
        spawn_named("http_loader".to_owned(), move || load(load_data, senders, classifier, cookies_chan))
    }
}

fn send_error(url: Url, err: String, start_chan: LoadConsumer) {
    let mut metadata: Metadata = Metadata::default(url);
    metadata.status = None;

    match start_sending_opt(start_chan, metadata) {
        Ok(p) => p.send(Done(Err(err))).unwrap(),
        _ => {}
    };
}

enum ReadResult {
    Payload(Vec<u8>),
    EOF,
}

fn read_block<R: Read>(reader: &mut R) -> Result<ReadResult, ()> {
    let mut buf = vec![0; 1024];

    match reader.read(&mut buf) {
        Ok(len) if len > 0 => {
            unsafe { buf.set_len(len); }
            Ok(ReadResult::Payload(buf))
        }
        Ok(_) => Ok(ReadResult::EOF),
        Err(_) => Err(()),
    }
}

fn load(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEClassifier>, cookies_chan: Sender<ControlMsg>) {
    // FIXME: At the time of writing this FIXME, servo didn't have any central
    //        location for configuration. If you're reading this and such a
    //        repository DOES exist, please update this constant to use it.
    let max_redirects = 50;
    let mut iters = 0;
    let mut url = load_data.url.clone();
    let mut redirected_to = HashSet::new();

    // If the URL is a view-source scheme then the scheme data contains the
    // real URL that should be used for which the source is to be viewed.
    // Change our existing URL to that and keep note that we are viewing
    // the source rather than rendering the contents of the URL.
    let viewing_source = url.scheme == "view-source";
    if viewing_source {
        let inner_url = load_data.url.non_relative_scheme_data().unwrap();
        url = Url::parse(inner_url).unwrap();
        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("The {} scheme with view-source is not supported", url.scheme);
                send_error(url, s, start_chan);
                return;
            }
        };
    }

    // Loop to handle redirects.
    loop {
        iters = iters + 1;

        if iters > max_redirects {
            send_error(url, "too many redirects".to_string(), start_chan);
            return;
        }

        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("{} request, but we don't support that scheme", url.scheme);
                send_error(url, s, start_chan);
                return;
            }
        }

        info!("requesting {}", url.serialize());

        fn verifier(ssl: &mut SslContext) {
            ssl.set_verify(SslVerifyMode::SslVerifyPeer, None);
            let mut certs = resources_dir_path();
            certs.push("certs");
            ssl.set_CA_file(&certs);
        };

        let ssl_err_string = "[UnknownError { library: \"SSL routines\", \
function: \"SSL3_GET_SERVER_CERTIFICATE\", \
reason: \"certificate verify failed\" }]";

        let mut connector = if opts::get().nossl {
            HttpConnector(None)
        } else {
            HttpConnector(Some(box verifier as Box<FnMut(&mut SslContext)>))
        };

        let mut req = match Request::with_connector(load_data.method.clone(), url.clone(), &mut connector) {
            Ok(req) => req,
            Err(HttpError::HttpIoError(ref io_error)) if (
                io_error.kind() == io::ErrorKind::Other &&
                io_error.description() == "Error in OpenSSL" &&
                io_error.detail() == Some(ssl_err_string.to_owned())
            ) => {
                let mut image = resources_dir_path();
                image.push("badcert.html");
                let load_data = LoadData::new(Url::from_file_path(&*image).unwrap());
                file_loader::factory(load_data, start_chan, classifier);
                return;
            },
            Err(e) => {
                println!("{:?}", e);
                send_error(url, e.description().to_string(), start_chan);
                return;
            }
        };

        // Preserve the `host` header set automatically by Request.
        let host = req.headers().get::<Host>().unwrap().clone();

        // Avoid automatically preserving request headers when redirects occur.
        // See https://bugzilla.mozilla.org/show_bug.cgi?id=401564 and
        // https://bugzilla.mozilla.org/show_bug.cgi?id=216828 .
        // Only preserve ones which have been explicitly marked as such.
        if iters == 1 {
            let mut combined_headers = load_data.headers.clone();
            combined_headers.extend(load_data.preserved_headers.iter());
            *req.headers_mut() = combined_headers;
        } else {
            *req.headers_mut() = load_data.preserved_headers.clone();
        }

        req.headers_mut().set(host);

        if !req.headers().has::<Accept>() {
            let accept = Accept(vec![
                qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
                qitem(Mime(TopLevel::Application, SubLevel::Ext("xhtml+xml".to_string()), vec![])),
                QualityItem::new(Mime(TopLevel::Application, SubLevel::Xml, vec![]), Quality(900u16)),
                QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]), Quality(800u16)),
            ]);
            req.headers_mut().set(accept);
        }

        let (tx, rx) = channel();
        cookies_chan.send(ControlMsg::GetCookiesForUrl(url.clone(), tx, CookieSource::HTTP)).unwrap();
        if let Some(cookie_list) = rx.recv().unwrap() {
            let mut v = Vec::new();
            v.push(cookie_list.into_bytes());
            req.headers_mut().set_raw("Cookie".to_owned(), v);
        }

        if !req.headers().has::<AcceptEncoding>() {
            req.headers_mut().set_raw("Accept-Encoding".to_owned(), vec![b"gzip, deflate".to_vec()]);
        }
        if log_enabled!(log::INFO) {
            info!("{}", load_data.method);
            for header in req.headers().iter() {
                info!(" - {}", header);
            }
            info!("{:?}", load_data.data);
        }

        // Avoid automatically sending request body if a redirect has occurred.
        let writer = match load_data.data {
            Some(ref data) if iters == 1 => {
                req.headers_mut().set(ContentLength(data.len() as u64));
                let mut writer = match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                };
                match writer.write_all(&*data) {
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                    _ => {}
                };
                writer
            },
            _ => {
                match load_data.method {
                    Method::Get | Method::Head => (),
                    _ => req.headers_mut().set(ContentLength(0))
                }
                match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                }
            }
        };
        let mut response = match writer.send() {
            Ok(r) => r,
            Err(e) => {
                send_error(url, e.description().to_string(), start_chan);
                return;
            }
        };

        // Dump headers, but only do the iteration if info!() is enabled.
        info!("got HTTP response {}, headers:", response.status);
        if log_enabled!(log::INFO) {
            for header in response.headers.iter() {
                info!(" - {}", header);
            }
        }

        if let Some(cookies) = response.headers.get_raw("set-cookie") {
            for cookie in cookies.iter() {
                if let Ok(cookies) = String::from_utf8(cookie.clone()) {
                    cookies_chan.send(ControlMsg::SetCookiesForUrl(url.clone(),
                                                                   cookies,
                                                                   CookieSource::HTTP)).unwrap();
                }
            }
        }

        if response.status.class() == StatusClass::Redirection {
            match response.headers.get::<Location>() {
                Some(&Location(ref new_url)) => {
                    // CORS (https://fetch.spec.whatwg.org/#http-fetch, status section, point 9, 10)
                    match load_data.cors {
                        Some(ref c) => {
                            if c.preflight {
                                // The preflight lied
                                send_error(url, "Preflight fetch inconsistent with main fetch".to_string(), start_chan);
                                return;
                            } else {
                                // XXXManishearth There are some CORS-related steps here,
                                // but they don't seem necessary until credentials are implemented
                            }
                        }
                        _ => {}
                    }
                    let new_url = match UrlParser::new().base_url(&url).parse(&new_url) {
                        Ok(u) => u,
                        Err(e) => {
                            send_error(url, e.to_string(), start_chan);
                            return;
                        }
                    };
                    info!("redirecting to {}", new_url);
                    url = new_url;

                    // According to https://tools.ietf.org/html/rfc7231#section-6.4.2,
                    // historically UAs have rewritten POST->GET on 301 and 302 responses.
                    if load_data.method == Method::Post &&
                        (response.status == StatusCode::MovedPermanently ||
                         response.status == StatusCode::Found) {
                        load_data.method = Method::Get;
                    }

                    if redirected_to.contains(&url) {
                        send_error(url, "redirect loop".to_string(), start_chan);
                        return;
                    }

                    redirected_to.insert(url.clone());
                    continue;
                }
                None => ()
            }
        }

        let mut adjusted_headers = response.headers.clone();
        if viewing_source {
            adjusted_headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        }
        let mut metadata: Metadata = Metadata::default(url);
        metadata.set_content_type(match adjusted_headers.get() {
            Some(&ContentType(ref mime)) => Some(mime),
            None => None
        });
        metadata.headers = Some(adjusted_headers);
        metadata.status = Some(response.status_raw().clone());

        let mut encoding_str: Option<String> = None;
        //FIXME: Implement Content-Encoding Header https://github.com/hyperium/hyper/issues/391
        if let Some(encodings) = response.headers.get_raw("content-encoding") {
            for encoding in encodings.iter() {
                if let Ok(encodings) = String::from_utf8(encoding.clone()) {
                    if encodings == "gzip" || encodings == "deflate" {
                        encoding_str = Some(encodings);
                        break;
                    }
                }
            }
        }

        match encoding_str {
            Some(encoding) => {
                if encoding == "gzip" {
                    let mut response_decoding = GzDecoder::new(response).unwrap();
                    send_data(&mut response_decoding, start_chan, metadata, classifier);
                } else if encoding == "deflate" {
                    let mut response_decoding = DeflateDecoder::new(response);
                    send_data(&mut response_decoding, start_chan, metadata, classifier);
                }
            },
            None => {
                send_data(&mut response, start_chan, metadata, classifier);
            }
        }

        // We didn't get redirected.
        break;
    }
}

fn send_data<R: Read>(reader: &mut R,
                      start_chan: LoadConsumer,
                      metadata: Metadata,
                      classifier: Arc<MIMEClassifier>) {
    let (progress_chan, mut chunk) = {
        let buf = match read_block(reader) {
            Ok(ReadResult::Payload(buf)) => buf,
            _ => vec!(),
        };
        let p = match start_sending_sniffed_opt(start_chan, metadata, classifier, &buf) {
            Ok(p) => p,
            _ => return
        };
        (p, buf)
    };

    loop {
        if progress_chan.send(Payload(chunk)).is_err() {
            // The send errors when the receiver is out of scope,
            // which will happen if the fetch has timed out (or has been aborted)
            // so we don't need to continue with the loading of the file here.
            return;
        }

        chunk = match read_block(reader) {
            Ok(ReadResult::Payload(buf)) => buf,
            Ok(ReadResult::EOF) | Err(_) => break,
        };
    }

    let _ = progress_chan.send(Done(Ok(())));
}
<commit_msg>Handle gzip decompression failure (encountered during jquery test suite work) without panic.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use net_traits::{ControlMsg, CookieSource, LoadData, Metadata, LoadConsumer};
use net_traits::ProgressMsg::{Payload, Done};
use mime_classifier::MIMEClassifier;
use resource_task::{start_sending_opt, start_sending_sniffed_opt};

use log;
use std::collections::HashSet;
use file_loader;
use flate2::read::{DeflateDecoder, GzDecoder};
use hyper::client::Request;
use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host, Location, qitem, Quality, QualityItem};
use hyper::HttpError;
use hyper::method::Method;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::net::HttpConnector;
use hyper::status::{StatusCode, StatusClass};
use std::error::Error;
use openssl::ssl::{SslContext, SslVerifyMode};
use std::io::{self, Read, Write};
use std::sync::Arc;
use std::sync::mpsc::{Sender, channel};
use std::thunk::Invoke;
use util::task::spawn_named;
use util::resource_files::resources_dir_path;
use util::opts;
use url::{Url, UrlParser};

use std::borrow::ToOwned;

pub fn factory(cookies_chan: Sender<ControlMsg>)
               -> Box<Invoke<(LoadData, LoadConsumer, Arc<MIMEClassifier>)> + Send> {
    box move |(load_data, senders, classifier)| {
        spawn_named("http_loader".to_owned(), move || load(load_data, senders, classifier, cookies_chan))
    }
}

fn send_error(url: Url, err: String, start_chan: LoadConsumer) {
    let mut metadata: Metadata = Metadata::default(url);
    metadata.status = None;

    match start_sending_opt(start_chan, metadata) {
        Ok(p) => p.send(Done(Err(err))).unwrap(),
        _ => {}
    };
}

enum ReadResult {
    Payload(Vec<u8>),
    EOF,
}

fn read_block<R: Read>(reader: &mut R) -> Result<ReadResult, ()> {
    let mut buf = vec![0; 1024];

    match reader.read(&mut buf) {
        Ok(len) if len > 0 => {
            unsafe { buf.set_len(len); }
            Ok(ReadResult::Payload(buf))
        }
        Ok(_) => Ok(ReadResult::EOF),
        Err(_) => Err(()),
    }
}

fn load(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEClassifier>, cookies_chan: Sender<ControlMsg>) {
    // FIXME: At the time of writing this FIXME, servo didn't have any central
    //        location for configuration. If you're reading this and such a
    //        repository DOES exist, please update this constant to use it.
    let max_redirects = 50;
    let mut iters = 0;
    let mut url = load_data.url.clone();
    let mut redirected_to = HashSet::new();

    // If the URL is a view-source scheme then the scheme data contains the
    // real URL that should be used for which the source is to be viewed.
    // Change our existing URL to that and keep note that we are viewing
    // the source rather than rendering the contents of the URL.
    let viewing_source = url.scheme == "view-source";
    if viewing_source {
        let inner_url = load_data.url.non_relative_scheme_data().unwrap();
        url = Url::parse(inner_url).unwrap();
        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("The {} scheme with view-source is not supported", url.scheme);
                send_error(url, s, start_chan);
                return;
            }
        };
    }

    // Loop to handle redirects.
    loop {
        iters = iters + 1;

        if iters > max_redirects {
            send_error(url, "too many redirects".to_string(), start_chan);
            return;
        }

        match &*url.scheme {
            "http" | "https" => {}
            _ => {
                let s = format!("{} request, but we don't support that scheme", url.scheme);
                send_error(url, s, start_chan);
                return;
            }
        }

        info!("requesting {}", url.serialize());

        fn verifier(ssl: &mut SslContext) {
            ssl.set_verify(SslVerifyMode::SslVerifyPeer, None);
            let mut certs = resources_dir_path();
            certs.push("certs");
            ssl.set_CA_file(&certs);
        };

        let ssl_err_string = "[UnknownError { library: \"SSL routines\", \
function: \"SSL3_GET_SERVER_CERTIFICATE\", \
reason: \"certificate verify failed\" }]";

        let mut connector = if opts::get().nossl {
            HttpConnector(None)
        } else {
            HttpConnector(Some(box verifier as Box<FnMut(&mut SslContext)>))
        };

        let mut req = match Request::with_connector(load_data.method.clone(), url.clone(), &mut connector) {
            Ok(req) => req,
            Err(HttpError::HttpIoError(ref io_error)) if (
                io_error.kind() == io::ErrorKind::Other &&
                io_error.description() == "Error in OpenSSL" &&
                io_error.detail() == Some(ssl_err_string.to_owned())
            ) => {
                let mut image = resources_dir_path();
                image.push("badcert.html");
                let load_data = LoadData::new(Url::from_file_path(&*image).unwrap());
                file_loader::factory(load_data, start_chan, classifier);
                return;
            },
            Err(e) => {
                println!("{:?}", e);
                send_error(url, e.description().to_string(), start_chan);
                return;
            }
        };

        // Preserve the `host` header set automatically by Request.
        let host = req.headers().get::<Host>().unwrap().clone();

        // Avoid automatically preserving request headers when redirects occur.
        // See https://bugzilla.mozilla.org/show_bug.cgi?id=401564 and
        // https://bugzilla.mozilla.org/show_bug.cgi?id=216828 .
        // Only preserve ones which have been explicitly marked as such.
        if iters == 1 {
            let mut combined_headers = load_data.headers.clone();
            combined_headers.extend(load_data.preserved_headers.iter());
            *req.headers_mut() = combined_headers;
        } else {
            *req.headers_mut() = load_data.preserved_headers.clone();
        }

        req.headers_mut().set(host);

        if !req.headers().has::<Accept>() {
            let accept = Accept(vec![
                qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
                qitem(Mime(TopLevel::Application, SubLevel::Ext("xhtml+xml".to_string()), vec![])),
                QualityItem::new(Mime(TopLevel::Application, SubLevel::Xml, vec![]), Quality(900u16)),
                QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]), Quality(800u16)),
            ]);
            req.headers_mut().set(accept);
        }

        let (tx, rx) = channel();
        cookies_chan.send(ControlMsg::GetCookiesForUrl(url.clone(), tx, CookieSource::HTTP)).unwrap();
        if let Some(cookie_list) = rx.recv().unwrap() {
            let mut v = Vec::new();
            v.push(cookie_list.into_bytes());
            req.headers_mut().set_raw("Cookie".to_owned(), v);
        }

        if !req.headers().has::<AcceptEncoding>() {
            req.headers_mut().set_raw("Accept-Encoding".to_owned(), vec![b"gzip, deflate".to_vec()]);
        }
        if log_enabled!(log::INFO) {
            info!("{}", load_data.method);
            for header in req.headers().iter() {
                info!(" - {}", header);
            }
            info!("{:?}", load_data.data);
        }

        // Avoid automatically sending request body if a redirect has occurred.
        let writer = match load_data.data {
            Some(ref data) if iters == 1 => {
                req.headers_mut().set(ContentLength(data.len() as u64));
                let mut writer = match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                };
                match writer.write_all(&*data) {
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                    _ => {}
                };
                writer
            },
            _ => {
                match load_data.method {
                    Method::Get | Method::Head => (),
                    _ => req.headers_mut().set(ContentLength(0))
                }
                match req.start() {
                    Ok(w) => w,
                    Err(e) => {
                        send_error(url, e.description().to_string(), start_chan);
                        return;
                    }
                }
            }
        };
        let mut response = match writer.send() {
            Ok(r) => r,
            Err(e) => {
                send_error(url, e.description().to_string(), start_chan);
                return;
            }
        };

        // Dump headers, but only do the iteration if info!() is enabled.
        info!("got HTTP response {}, headers:", response.status);
        if log_enabled!(log::INFO) {
            for header in response.headers.iter() {
                info!(" - {}", header);
            }
        }

        if let Some(cookies) = response.headers.get_raw("set-cookie") {
            for cookie in cookies.iter() {
                if let Ok(cookies) = String::from_utf8(cookie.clone()) {
                    cookies_chan.send(ControlMsg::SetCookiesForUrl(url.clone(),
                                                                   cookies,
                                                                   CookieSource::HTTP)).unwrap();
                }
            }
        }

        if response.status.class() == StatusClass::Redirection {
            match response.headers.get::<Location>() {
                Some(&Location(ref new_url)) => {
                    // CORS (https://fetch.spec.whatwg.org/#http-fetch, status section, point 9, 10)
                    match load_data.cors {
                        Some(ref c) => {
                            if c.preflight {
                                // The preflight lied
                                send_error(url, "Preflight fetch inconsistent with main fetch".to_string(), start_chan);
                                return;
                            } else {
                                // XXXManishearth There are some CORS-related steps here,
                                // but they don't seem necessary until credentials are implemented
                            }
                        }
                        _ => {}
                    }
                    let new_url = match UrlParser::new().base_url(&url).parse(&new_url) {
                        Ok(u) => u,
                        Err(e) => {
                            send_error(url, e.to_string(), start_chan);
                            return;
                        }
                    };
                    info!("redirecting to {}", new_url);
                    url = new_url;

                    // According to https://tools.ietf.org/html/rfc7231#section-6.4.2,
                    // historically UAs have rewritten POST->GET on 301 and 302 responses.
                    if load_data.method == Method::Post &&
                        (response.status == StatusCode::MovedPermanently ||
                         response.status == StatusCode::Found) {
                        load_data.method = Method::Get;
                    }

                    if redirected_to.contains(&url) {
                        send_error(url, "redirect loop".to_string(), start_chan);
                        return;
                    }

                    redirected_to.insert(url.clone());
                    continue;
                }
                None => ()
            }
        }

        let mut adjusted_headers = response.headers.clone();
        if viewing_source {
            adjusted_headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        }
        let mut metadata: Metadata = Metadata::default(url);
        metadata.set_content_type(match adjusted_headers.get() {
            Some(&ContentType(ref mime)) => Some(mime),
            None => None
        });
        metadata.headers = Some(adjusted_headers);
        metadata.status = Some(response.status_raw().clone());

        let mut encoding_str: Option<String> = None;
        //FIXME: Implement Content-Encoding Header https://github.com/hyperium/hyper/issues/391
        if let Some(encodings) = response.headers.get_raw("content-encoding") {
            for encoding in encodings.iter() {
                if let Ok(encodings) = String::from_utf8(encoding.clone()) {
                    if encodings == "gzip" || encodings == "deflate" {
                        encoding_str = Some(encodings);
                        break;
                    }
                }
            }
        }

        match encoding_str {
            Some(encoding) => {
                if encoding == "gzip" {
                    let result = GzDecoder::new(response);
                    match result {
                        Ok(mut response_decoding) => {
                            send_data(&mut response_decoding, start_chan, metadata, classifier);
                        }
                        Err(err) => {
                            send_error(metadata.final_url, err.to_string(), start_chan);
                            return;
                        }
                    }
                } else if encoding == "deflate" {
                    let mut response_decoding = DeflateDecoder::new(response);
                    send_data(&mut response_decoding, start_chan, metadata, classifier);
                }
            },
            None => {
                send_data(&mut response, start_chan, metadata, classifier);
            }
        }

        // We didn't get redirected.
        break;
    }
}

fn send_data<R: Read>(reader: &mut R,
                      start_chan: LoadConsumer,
                      metadata: Metadata,
                      classifier: Arc<MIMEClassifier>) {
    let (progress_chan, mut chunk) = {
        let buf = match read_block(reader) {
            Ok(ReadResult::Payload(buf)) => buf,
            _ => vec!(),
        };
        let p = match start_sending_sniffed_opt(start_chan, metadata, classifier, &buf) {
            Ok(p) => p,
            _ => return
        };
        (p, buf)
    };

    loop {
        if progress_chan.send(Payload(chunk)).is_err() {
            // The send errors when the receiver is out of scope,
            // which will happen if the fetch has timed out (or has been aborted)
            // so we don't need to continue with the loading of the file here.
            return;
        }

        chunk = match read_block(reader) {
            Ok(ReadResult::Payload(buf)) => buf,
            Ok(ReadResult::EOF) | Err(_) => break,
        };
    }

    let _ = progress_chan.send(Done(Ok(())));
}
<|endoftext|>


<commit_before>extern crate half;
extern crate openexr;
extern crate openexr_sys;

use half::f16;
use openexr::{FrameBuffer, FrameBufferMut, Header, InputFile, ScanlineOutputFile};
use std::fs::File;

// OpenEXR file data.
const NEGATIVE_OFFSET: &[u8] = include_bytes!("data/negative_window.exr");
const POSITIVE_OFFSET: &[u8] = include_bytes!("data/positive_window.exr");

/// Return the offset index of the data pixel at 0,0 with
/// reference to the data pixel at origin.
fn calc_origin_offset(image_width: u32, origin: (i32, i32)) -> isize {
    let (x, y) = origin;
    -(x as isize + y as isize * image_width as isize)
}

fn load_and_test_with_offset_window_read_multiple_channels(data: &[u8]) {
    let mut exr_file = InputFile::from_slice(data).unwrap();

    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    let mut pixel_data = vec![(zero, zero, zero); (width * height) as usize];

    let read_with_offset = |exr_file: &mut InputFile, pixel_data: &mut [_], ox, oy| {
        let mut fb = FrameBufferMut::new_with_origin(ox, oy, width, height);
        println!("Loading from buffer as {},{} {}x{}", ox, oy, width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], pixel_data);
        println!("Reading pixels from {},{},{}", "R", "G", "B");
        (
            exr_file.read_pixels(&mut fb),
            calc_origin_offset(width, (ox, oy)),
        )
    };

    // let's try a few mismatched origins
    assert!(
        read_with_offset(&mut exr_file, &mut pixel_data, 0, 0)
            .0
            .is_err()
    );
    assert!(
        read_with_offset(&mut exr_file, &mut pixel_data, x - 1, y - 1)
            .0
            .is_err()
    );
    assert!(
        read_with_offset(&mut exr_file, &mut pixel_data, -x, -y)
            .0
            .is_err()
    );
    // and then the real thing
    let (read_result, origin_offset) = read_with_offset(&mut exr_file, &mut pixel_data, x, y);
    assert!(read_result.is_ok());
    // check the pixel value at coordinates (0,0) of the data window if 0,0 is within the frame buffer
    if origin_offset >= 0 {
        assert!(f32::abs(pixel_data[origin_offset as usize].0.to_f32() - 0.5f32) < 0.0001f32);
    }

    // we write the file back out with a different offset
    {
        let mut fb = FrameBuffer::new_with_origin(-x, -y, width, height);
        println!("Loading buffer as {}x{}", width, height);
        let mut file =
            File::create("target/positive_window.exr").expect("Could not create output file");
        let mut exr_file = ScanlineOutputFile::new(
            &mut file,
            &Header::new()
                .set_data_window(Header::box2i(-x, -y, width, height))
                .set_display_window(Header::box2i(0, 0, width - 16, height - 16))
                .add_channel("R", openexr::PixelType::HALF)
                .add_channel("G", openexr::PixelType::HALF)
                .add_channel("B", openexr::PixelType::HALF),
        ).unwrap();

        fb.insert_channels(&["R", "G", "B"], &pixel_data);
        exr_file.write_pixels(&fb).unwrap();
    }
}

fn load_and_test_with_offset_window_read_single_channel(data: &[u8]) {
    let mut exr_file = InputFile::from_slice(data).unwrap();
    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    let mut pixel_data = vec![zero; (width * height) as usize];

    let mut read_with_offset = |ox: i32, oy: i32| {
        let mut fb = FrameBufferMut::new_with_origin(ox, oy, width, height);
        println!(
            "Loading from buffer as ({} {}) {}x{}",
            ox, oy, width, height
        );
        fb.insert_channel("R", 0.0, &mut pixel_data);
        println!("Reading pixels from {},{},{}", "R", "G", "B");
        exr_file.read_pixels(&mut fb)
    };
    // let's try a few mismatched origins
    assert!(read_with_offset(0, 0).is_err());
    assert!(read_with_offset(x - 1, y - 1).is_err());
    assert!(read_with_offset(-x, -y).is_err());
    // and then the correct one
    assert!(read_with_offset(x, y).is_ok());
}

#[test]
fn window_read_multiple_channels() {
    load_and_test_with_offset_window_read_multiple_channels(NEGATIVE_OFFSET);
    load_and_test_with_offset_window_read_multiple_channels(POSITIVE_OFFSET);
}

// with one channel only as well
#[test]
fn negative_window_read_single_channel() {
    load_and_test_with_offset_window_read_single_channel(NEGATIVE_OFFSET);
    load_and_test_with_offset_window_read_single_channel(POSITIVE_OFFSET);
}
<commit_msg>Fix test build<commit_after>extern crate half;
extern crate openexr;
extern crate openexr_sys;

use half::f16;
use openexr::{FrameBuffer, FrameBufferMut, Header, InputFile, ScanlineOutputFile};
use std::fs::File;

// OpenEXR file data.
const NEGATIVE_OFFSET: &[u8] = include_bytes!("data/negative_window.exr");
const POSITIVE_OFFSET: &[u8] = include_bytes!("data/positive_window.exr");

/// Return the offset index of the data pixel at 0,0 with
/// reference to the data pixel at origin.
fn calc_origin_offset(image_width: u32, origin: (i32, i32)) -> isize {
    let (x, y) = origin;
    -(x as isize + y as isize * image_width as isize)
}

fn load_and_test_with_offset_window_read_multiple_channels(data: &[u8]) {
    let mut exr_file = InputFile::from_slice(data).unwrap();

    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    let mut pixel_data = vec![(zero, zero, zero); (width * height) as usize];

    let read_with_offset = |exr_file: &mut InputFile, pixel_data: &mut [_], ox, oy| {
        let mut fb = FrameBufferMut::new_with_origin(ox, oy, width, height);
        println!("Loading from buffer as {},{} {}x{}", ox, oy, width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], pixel_data);
        println!("Reading pixels from {},{},{}", "R", "G", "B");
        (
            exr_file.read_pixels(&mut fb),
            calc_origin_offset(width, (ox, oy)),
        )
    };

    // let's try a few mismatched origins
    assert!(
        read_with_offset(&mut exr_file, &mut pixel_data, 0, 0)
            .0
            .is_err()
    );
    assert!(
        read_with_offset(&mut exr_file, &mut pixel_data, x - 1, y - 1)
            .0
            .is_err()
    );
    assert!(
        read_with_offset(&mut exr_file, &mut pixel_data, -x, -y)
            .0
            .is_err()
    );
    // and then the real thing
    let (read_result, origin_offset) = read_with_offset(&mut exr_file, &mut pixel_data, x, y);
    assert!(read_result.is_ok());
    // check the pixel value at coordinates (0,0) of the data window if 0,0 is within the frame buffer
    if origin_offset >= 0 {
        assert!(f32::abs(f32::from(pixel_data[origin_offset as usize].0) - 0.5f32) < 0.0001f32);
    }

    // we write the file back out with a different offset
    {
        let mut fb = FrameBuffer::new_with_origin(-x, -y, width, height);
        println!("Loading buffer as {}x{}", width, height);
        let mut file =
            File::create("target/positive_window.exr").expect("Could not create output file");
        let mut exr_file = ScanlineOutputFile::new(
            &mut file,
            &Header::new()
                .set_data_window(Header::box2i(-x, -y, width, height))
                .set_display_window(Header::box2i(0, 0, width - 16, height - 16))
                .add_channel("R", openexr::PixelType::HALF)
                .add_channel("G", openexr::PixelType::HALF)
                .add_channel("B", openexr::PixelType::HALF),
        ).unwrap();

        fb.insert_channels(&["R", "G", "B"], &pixel_data);
        exr_file.write_pixels(&fb).unwrap();
    }
}

fn load_and_test_with_offset_window_read_single_channel(data: &[u8]) {
    let mut exr_file = InputFile::from_slice(data).unwrap();
    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    let mut pixel_data = vec![zero; (width * height) as usize];

    let mut read_with_offset = |ox: i32, oy: i32| {
        let mut fb = FrameBufferMut::new_with_origin(ox, oy, width, height);
        println!(
            "Loading from buffer as ({} {}) {}x{}",
            ox, oy, width, height
        );
        fb.insert_channel("R", 0.0, &mut pixel_data);
        println!("Reading pixels from {},{},{}", "R", "G", "B");
        exr_file.read_pixels(&mut fb)
    };
    // let's try a few mismatched origins
    assert!(read_with_offset(0, 0).is_err());
    assert!(read_with_offset(x - 1, y - 1).is_err());
    assert!(read_with_offset(-x, -y).is_err());
    // and then the correct one
    assert!(read_with_offset(x, y).is_ok());
}

#[test]
fn window_read_multiple_channels() {
    load_and_test_with_offset_window_read_multiple_channels(NEGATIVE_OFFSET);
    load_and_test_with_offset_window_read_multiple_channels(POSITIVE_OFFSET);
}

// with one channel only as well
#[test]
fn negative_window_read_single_channel() {
    load_and_test_with_offset_window_read_single_channel(NEGATIVE_OFFSET);
    load_and_test_with_offset_window_read_single_channel(POSITIVE_OFFSET);
}
<|endoftext|>


<commit_before>use std;

pub type id = libc::intptr_t;
pub type Class = libc::intptr_t;
pub type IMP = *u8;
pub type SEL = libc::intptr_t;
pub type Ivar = libc::intptr_t;

pub const nil : id = 0 as id;

pub extern mod objc {
    fn class_addMethod(cls : Class,
                       name : SEL,
                       imp : IMP,
                       types : *libc::c_char) -> bool;
    fn class_addIvar(cls : Class,
                     name : *libc::c_char,
                     size : libc::size_t,
                     alignment: u8,
		             types: *libc::c_char) -> bool;
    fn object_setInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  value : *libc::c_void);
    fn object_getInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  outValue : **libc::c_void);
    fn objc_allocateClassPair(superclass : Class,
                              name : *libc::c_char,
                              extraBytes : libc::size_t) -> Class;
    fn objc_getClass(name : *libc::c_char) -> id;
    fn objc_msgSend(theReceiver : id, theSelector : SEL) -> id;
    fn objc_registerClassPair(cls : Class);
    fn sel_registerName(name : *libc::c_char) -> SEL;
}

#[test]
pub fn test_nsapp() {
    let klass = str::as_c_str(~"NSApplication", |s|
        objc::objc_getClass(s)
    );

    let sel = str::as_c_str(~"sharedApplication", |s|
        objc::sel_registerName(s)
    );

    let nsapp = objc::objc_msgSend(klass, sel);

    io::println(fmt!("nsapp: %d", (nsapp as int)));
}

#[test]
pub fn test_custom_obj() {
    extern fn MyObject_doSomething(this : id, _sel : SEL) -> id {
        io::println(~"doSomething");
        return this;
    }

    let NSObject = str::as_c_str(~"NSObject", |s|
        objc::objc_getClass(s)
    );
    let MyObject = str::as_c_str(~"MyObject", |s|
        objc::objc_allocateClassPair(NSObject, s, 0 as libc::size_t)
    );
    let doSomething = str::as_c_str(~"doSomething", |s|
        objc::sel_registerName(s)
    );
    let _ = str::as_c_str(~"@@:", |types|
        objc::class_addMethod(MyObject, doSomething, MyObject_doSomething,
                              types)
    );
    objc::objc_registerClassPair(MyObject);

    let alloc = str::as_c_str(~"alloc", |s| objc::sel_registerName(s));
    let init = str::as_c_str(~"init", |s| objc::sel_registerName(s));

    let mut obj = objc::objc_msgSend(MyObject, alloc);
    obj = objc::objc_msgSend(obj, init);
    objc::objc_msgSend(obj, doSomething);
}

<commit_msg>Update tests for language changes<commit_after>use std;

pub type id = libc::intptr_t;
pub type Class = libc::intptr_t;
pub type IMP = *u8;
pub type SEL = libc::intptr_t;
pub type Ivar = libc::intptr_t;

pub const nil : id = 0 as id;

pub extern mod objc {
    fn class_addMethod(cls : Class,
                       name : SEL,
                       imp : IMP,
                       types : *libc::c_char) -> bool;
    fn class_addIvar(cls : Class,
                     name : *libc::c_char,
                     size : libc::size_t,
                     alignment: u8,
		             types: *libc::c_char) -> bool;
    fn object_setInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  value : *libc::c_void);
    fn object_getInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  outValue : **libc::c_void);
    fn objc_allocateClassPair(superclass : Class,
                              name : *libc::c_char,
                              extraBytes : libc::size_t) -> Class;
    fn objc_getClass(name : *libc::c_char) -> id;
    fn objc_msgSend(theReceiver : id, theSelector : SEL) -> id;
    fn objc_registerClassPair(cls : Class);
    fn sel_registerName(name : *libc::c_char) -> SEL;
}

#[test]
pub fn test_nsapp() {
    let klass = str::as_c_str(~"NSApplication", |s|
        unsafe {
            objc::objc_getClass(s)
        }
    );

    let sel = str::as_c_str(~"sharedApplication", |s|
        unsafe {
            objc::sel_registerName(s)
        }
    );

    unsafe {
        let nsapp = objc::objc_msgSend(klass, sel);
        io::println(fmt!("nsapp: %d", (nsapp as int)));
    }
}

#[test]
pub fn test_custom_obj() {
    extern fn MyObject_doSomething(this : id, _sel : SEL) -> id {
        io::println(~"doSomething");
        return this;
    }

    let NSObject = str::as_c_str(~"NSObject", |s|
        unsafe {
            objc::objc_getClass(s)
        }
    );
    let MyObject = str::as_c_str(~"MyObject", |s|
        unsafe {
            objc::objc_allocateClassPair(NSObject, s, 0 as libc::size_t)
        }
    );
    let doSomething = str::as_c_str(~"doSomething", |s|
        unsafe {
            objc::sel_registerName(s)
        }
    );
    let _ = str::as_c_str(~"@@:", |types|
        unsafe {
            objc::class_addMethod(MyObject,
                                  doSomething,
                                  MyObject_doSomething,
                                  types)
        }
    );

    unsafe {
        objc::objc_registerClassPair(MyObject);
    }

    let alloc = str::as_c_str(~"alloc", |s| unsafe { objc::sel_registerName(s) });
    let init = str::as_c_str(~"init", |s| unsafe { objc::sel_registerName(s) });

    unsafe {
        let mut obj = objc::objc_msgSend(MyObject, alloc);
        obj = objc::objc_msgSend(obj, init);
        objc::objc_msgSend(obj, doSomething);
    }
}

<|endoftext|>


<commit_before>//! Define explicit schemes

use ndarray::*;
use ndarray_linalg::*;
use super::traits::*;

macro_rules! def_explicit {
    ($method:ident, $constructor:ident) => {

pub struct $method<F: Explicit> {
    f: F,
    dt: <F::Scalar as AssociatedReal>::Real,
}

impl<D: Dimension, F: Explicit<Dim = D>> ModelSpec for $method<F> {
    type Dim = D;

    fn model_size(&self) -> D::Pattern {
        self.f.model_size()
    }
}

impl<A: Scalar, F: Explicit<Scalar = A>> TimeStep for $method<F> {
    type Time = A::Real;
    fn get_dt(&self) -> Self::Time {
        self.dt
    }
    fn set_dt(&mut self, dt: Self::Time) {
        self.dt = dt;
    }
}

pub fn $constructor<A, F>(f: F, dt: A::Real) -> $method<F>
where A: Scalar,
      F: Explicit<Scalar = A>
{
    $method { f: f, dt: dt }
}

}} // def_explicit

def_explicit!(Euler, euler);
def_explicit!(Heun, heun);
def_explicit!(RK4, rk4);

pub struct EulerBuffer<A, D> {
    x: Array<A, D>,
}

impl<F> BufferSpec for Euler<F>
    where F: Explicit + ModelSpec
{
    type Buffer = EulerBuffer<F::Scalar, F::Dim>;

    fn new_buffer(&self) -> Self::Buffer {
        EulerBuffer { x: Array::zeros(self.f.model_size()) }
    }
}

impl<F> TimeEvolution for Euler<F>
    where F: Explicit + ModelSpec
{
    type Scalar = F::Scalar;

    fn iterate<'a, S>(&self,
                      mut x: &'a mut ArrayBase<S, F::Dim>,
                      mut buf: &mut Self::Buffer)
                      -> &'a mut ArrayBase<S, F::Dim>
        where S: DataMut<Elem = Self::Scalar>
    {
        buf.x.zip_mut_with(x, |buf, x| *buf = *x);
        let mut fx = self.f.rhs(x);
        Zip::from(&mut *fx)
            .and(&buf.x)
            .apply(|vfx, vx| { *vfx = *vx + vfx.mul_real(self.dt); });
        fx
    }
}

pub struct HeunBuffer<A, D> {
    x: Array<A, D>,
    k1: Array<A, D>,
}

impl<F: Explicit + ModelSpec> BufferSpec for Heun<F> {
    type Buffer = HeunBuffer<F::Scalar, F::Dim>;

    fn new_buffer(&self) -> Self::Buffer {
        HeunBuffer {
            x: Array::zeros(self.f.model_size()),
            k1: Array::zeros(self.f.model_size()),
        }
    }
}

impl<F: Explicit + ModelSpec> TimeEvolution for Heun<F> {
    type Scalar = F::Scalar;

    fn iterate<'a, S>(&self,
                      mut x: &'a mut ArrayBase<S, F::Dim>,
                      mut buf: &mut Self::Buffer)
                      -> &'a mut ArrayBase<S, F::Dim>
        where S: DataMut<Elem = Self::Scalar>
    {
        let dt = self.dt;
        let dt_2 = self.dt * into_scalar(0.5);
        // calc
        buf.x.zip_mut_with(x, |buf, x| *buf = *x);
        let k1 = self.f.rhs(x);
        buf.k1.zip_mut_with(k1, |buf, k1| *buf = *k1);
        Zip::from(&mut *k1)
            .and(&buf.x)
            .apply(|k1, &x_| { *k1 = k1.mul_real(dt) + x_; });
        let k2 = self.f.rhs(k1);
        Zip::from(&mut *k2)
            .and(&buf.x)
            .and(&buf.k1)
            .apply(|k2, &x_, &k1_| { *k2 = x_ + (k1_ + *k2).mul_real(dt_2); });
        k2
    }
}

pub struct RK4Buffer<A, D> {
    x: Array<A, D>,
    k1: Array<A, D>,
    k2: Array<A, D>,
    k3: Array<A, D>,
}

impl<F: Explicit + ModelSpec> BufferSpec for RK4<F> {
    type Buffer = RK4Buffer<F::Scalar, F::Dim>;

    fn new_buffer(&self) -> Self::Buffer {
        RK4Buffer {
            x: Array::zeros(self.f.model_size()),
            k1: Array::zeros(self.f.model_size()),
            k2: Array::zeros(self.f.model_size()),
            k3: Array::zeros(self.f.model_size()),
        }
    }
}

impl<F: Explicit + ModelSpec> TimeEvolution for RK4<F> {
    type Scalar = F::Scalar;

    fn iterate<'a, S>(&self,
                      mut x: &'a mut ArrayBase<S, F::Dim>,
                      mut buf: &mut Self::Buffer)
                      -> &'a mut ArrayBase<S, F::Dim>
        where S: DataMut<Elem = Self::Scalar>
    {
        let two = into_scalar(2.0);
        let dt = self.dt;
        let dt_2 = self.dt * into_scalar(0.5);
        let dt_6 = self.dt / into_scalar(6.0);
        buf.x.zip_mut_with(x, |buf, x| *buf = *x);
        // k1
        let mut k1 = self.f.rhs(x);
        buf.k1.zip_mut_with(k1, |buf, k1| *buf = *k1);
        Zip::from(&mut *k1)
            .and(&buf.x)
            .apply(|k1, &x| { *k1 = k1.mul_real(dt_2) + x; });
        // k2
        let mut k2 = self.f.rhs(k1);
        buf.k2.zip_mut_with(k2, |buf, k| *buf = *k);
        Zip::from(&mut *k2)
            .and(&buf.x)
            .apply(|k2, &x| { *k2 = x + k2.mul_real(dt_2); });
        // k3
        let mut k3 = self.f.rhs(k2);
        buf.k3.zip_mut_with(k3, |buf, k| *buf = *k);
        Zip::from(&mut *k3)
            .and(&buf.x)
            .apply(|k3, &x| { *k3 = x + k3.mul_real(dt); });
        let mut k4 = self.f.rhs(k3);
        Zip::from(&mut *k4)
            .and(&buf.x)
            .and(&buf.k1)
            .and(&buf.k2)
            .and(&buf.k3)
            .apply(|k4, &x, &k1, &k2, &k3| {
                       *k4 = x + (k1 + (k2 + k3).mul_real(two) + *k4).mul_real(dt_6);
                   });
        k4
    }
}
<commit_msg>implmenet explicit schemes for ExplicitBuf<commit_after>//! Define explicit schemes

use ndarray::*;
use ndarray_linalg::*;
use super::traits::*;

macro_rules! def_explicit {
    ($method:ident, $constructor:ident) => {

pub struct $method<F: ExplicitBuf> {
    f: F,
    dt: <F::Scalar as AssociatedReal>::Real,
}

impl<D: Dimension, F: ExplicitBuf<Dim = D>> ModelSpec for $method<F> {
    type Dim = D;

    fn model_size(&self) -> D::Pattern {
        self.f.model_size()
    }
}

impl<A: Scalar, F: ExplicitBuf<Scalar = A>> TimeStep for $method<F> {
    type Time = A::Real;
    fn get_dt(&self) -> Self::Time {
        self.dt
    }
    fn set_dt(&mut self, dt: Self::Time) {
        self.dt = dt;
    }
}

pub fn $constructor<A, F>(f: F, dt: A::Real) -> $method<F>
where A: Scalar,
      F: ExplicitBuf<Scalar = A>
{
    $method { f: f, dt: dt }
}

}} // def_explicit

def_explicit!(Euler, euler);
def_explicit!(Heun, heun);
def_explicit!(RK4, rk4);

pub struct EulerBuffer<ExBuf, Arr> {
    ex: ExBuf,
    x: Arr,
}

impl<F> BufferSpec for Euler<F>
    where F: ExplicitBuf + ModelSpec
{
    type Buffer = EulerBuffer<F::Buffer, Array<F::Scalar, F::Dim>>;

    fn new_buffer(&self) -> Self::Buffer {
        EulerBuffer {
            ex: self.f.new_buffer(),
            x: Array::zeros(self.f.model_size()),
        }
    }
}

impl<F> TimeEvolution for Euler<F>
    where F: ExplicitBuf + ModelSpec
{
    type Scalar = F::Scalar;

    fn iterate<'a, S>(&self,
                      mut x: &'a mut ArrayBase<S, F::Dim>,
                      mut buf: &mut Self::Buffer)
                      -> &'a mut ArrayBase<S, F::Dim>
        where S: DataMut<Elem = Self::Scalar>
    {
        buf.x.zip_mut_with(x, |buf, x| *buf = *x);
        let mut fx = self.f.rhs(x, &mut buf.ex);
        Zip::from(&mut *fx)
            .and(&buf.x)
            .apply(|vfx, vx| { *vfx = *vx + vfx.mul_real(self.dt); });
        fx
    }
}

pub struct HeunBuffer<ExBuf, Arr> {
    ex: ExBuf,
    x: Arr,
    k1: Arr,
}

impl<F: ExplicitBuf + ModelSpec> BufferSpec for Heun<F> {
    type Buffer = HeunBuffer<F::Buffer, Array<F::Scalar, F::Dim>>;

    fn new_buffer(&self) -> Self::Buffer {
        HeunBuffer {
            ex: self.f.new_buffer(),
            x: Array::zeros(self.f.model_size()),
            k1: Array::zeros(self.f.model_size()),
        }
    }
}

impl<F: ExplicitBuf + ModelSpec> TimeEvolution for Heun<F> {
    type Scalar = F::Scalar;

    fn iterate<'a, S>(&self,
                      mut x: &'a mut ArrayBase<S, F::Dim>,
                      mut buf: &mut Self::Buffer)
                      -> &'a mut ArrayBase<S, F::Dim>
        where S: DataMut<Elem = Self::Scalar>
    {
        let dt = self.dt;
        let dt_2 = self.dt * into_scalar(0.5);
        // calc
        buf.x.zip_mut_with(x, |buf, x| *buf = *x);
        let k1 = self.f.rhs(x, &mut buf.ex);
        buf.k1.zip_mut_with(k1, |buf, k1| *buf = *k1);
        Zip::from(&mut *k1)
            .and(&buf.x)
            .apply(|k1, &x_| { *k1 = k1.mul_real(dt) + x_; });
        let k2 = self.f.rhs(k1, &mut buf.ex);
        Zip::from(&mut *k2)
            .and(&buf.x)
            .and(&buf.k1)
            .apply(|k2, &x_, &k1_| { *k2 = x_ + (k1_ + *k2).mul_real(dt_2); });
        k2
    }
}

pub struct RK4Buffer<ExBuf, Arr> {
    ex: ExBuf,
    x: Arr,
    k1: Arr,
    k2: Arr,
    k3: Arr,
}

impl<F: ExplicitBuf + ModelSpec> BufferSpec for RK4<F> {
    type Buffer = RK4Buffer<F::Buffer, Array<F::Scalar, F::Dim>>;

    fn new_buffer(&self) -> Self::Buffer {
        RK4Buffer {
            ex: self.f.new_buffer(),
            x: Array::zeros(self.f.model_size()),
            k1: Array::zeros(self.f.model_size()),
            k2: Array::zeros(self.f.model_size()),
            k3: Array::zeros(self.f.model_size()),
        }
    }
}

impl<F: ExplicitBuf + ModelSpec> TimeEvolution for RK4<F> {
    type Scalar = F::Scalar;

    fn iterate<'a, S>(&self,
                      mut x: &'a mut ArrayBase<S, F::Dim>,
                      mut buf: &mut Self::Buffer)
                      -> &'a mut ArrayBase<S, F::Dim>
        where S: DataMut<Elem = Self::Scalar>
    {
        let two = into_scalar(2.0);
        let dt = self.dt;
        let dt_2 = self.dt * into_scalar(0.5);
        let dt_6 = self.dt / into_scalar(6.0);
        buf.x.zip_mut_with(x, |buf, x| *buf = *x);
        // k1
        let mut k1 = self.f.rhs(x, &mut buf.ex);
        buf.k1.zip_mut_with(k1, |buf, k1| *buf = *k1);
        Zip::from(&mut *k1)
            .and(&buf.x)
            .apply(|k1, &x| { *k1 = k1.mul_real(dt_2) + x; });
        // k2
        let mut k2 = self.f.rhs(k1, &mut buf.ex);
        buf.k2.zip_mut_with(k2, |buf, k| *buf = *k);
        Zip::from(&mut *k2)
            .and(&buf.x)
            .apply(|k2, &x| { *k2 = x + k2.mul_real(dt_2); });
        // k3
        let mut k3 = self.f.rhs(k2, &mut buf.ex);
        buf.k3.zip_mut_with(k3, |buf, k| *buf = *k);
        Zip::from(&mut *k3)
            .and(&buf.x)
            .apply(|k3, &x| { *k3 = x + k3.mul_real(dt); });
        let mut k4 = self.f.rhs(k3, &mut buf.ex);
        Zip::from(&mut *k4)
            .and(&buf.x)
            .and(&buf.k1)
            .and(&buf.k2)
            .and(&buf.k3)
            .apply(|k4, &x, &k1, &k2, &k3| {
                       *k4 = x + (k1 + (k2 + k3).mul_real(two) + *k4).mul_real(dt_6);
                   });
        k4
    }
}
<|endoftext|>


<commit_before>extern crate concrust;

pub use self::concrust::primitives::CountDownLatch;
pub use self::concrust::primitives::Semaphore;

pub use std::thread;
pub use std::sync::Arc;
pub use std::time::Duration;

pub use expectest::prelude::{be_equal_to, be_greater_than, be_ok, be_none, be_some};

describe! count_down_latch_prim {

    before_each {
        let latch = CountDownLatch::new(1);
    }

    it "should decrease counts when latch count down" {
        let counts = latch.get_counts();
        latch.count_down();

        expect!(latch.get_counts()).to(be_equal_to(counts - 1));
    }

    it "should await until counts not equals zero" {
        const NUMBER_OF_THREADS: usize = 10;
        let mut results = Vec::with_capacity(NUMBER_OF_THREADS);

        for _ in 0..NUMBER_OF_THREADS {
            let count_down_latch = latch.clone();
            let jh = thread::spawn(
                move || {
                    expect!(count_down_latch.get_counts()).to(be_greater_than(0));

                    count_down_latch.await();

                    expect!(count_down_latch.get_counts()).to(be_equal_to(0));
                }
            );
            results.push(jh);
        }

        thread::sleep(Duration::from_millis(100));

        latch.count_down();

        expect!(latch.get_counts()).to(be_equal_to(0));
        for jh in results {
            expect!(jh.join()).to(be_ok());
        }
    }
}

describe! semaphore_prim {

    before_each {
        let semaphore = Semaphore::new(1);
    }

    it "should release resource automaticaly" {
        {
            let guard = semaphore.acquire();
            let try_acquire = semaphore.try_acquire();
            expect!(try_acquire).to(be_none());
            drop(guard);
        }

        let try_acquire = semaphore.try_acquire();
        expect!(try_acquire).to(be_some());
    }

    it "should not release more than permissions" {
        semaphore.release();
        let try_acquire = semaphore.try_acquire();
        assert!(try_acquire.is_some());
       // expect!(try_acquire).to(be_some());

        let try_acquire = semaphore.try_acquire();
        expect!(try_acquire).to(be_none());
    }

    it "should block thread until resource will be released" {
        const NUMBER_OF_THREADS: usize = 10;
        let mut results = Vec::with_capacity(NUMBER_OF_THREADS);

        semaphore.acquire();

        for _ in 0..NUMBER_OF_THREADS {
            let semaphore = semaphore.clone();
            let jh = thread::spawn(
                move || {
                    let g = semaphore.acquire();
                    thread::sleep(Duration::from_millis(50));
                    drop(g);
                }
            );
            results.push(jh);
        }
        thread::sleep(Duration::from_millis(50));
        semaphore.release();

        for jh in results {
            expect!(jh.join()).to(be_ok());
        }
    }
}
<commit_msg>test clean up<commit_after>pub use concrust::primitives::CountDownLatch;
pub use concrust::primitives::Semaphore;

pub use std::thread;
pub use std::sync::Arc;
pub use std::time::Duration;

pub use expectest::prelude::{be_equal_to, be_greater_than, be_ok, be_none, be_some};

describe! count_down_latch_prim {

    before_each {
        let latch = CountDownLatch::new(1);
    }

    it "should decrease counts when latch count down" {
        let counts = latch.get_counts();
        latch.count_down();

        expect!(latch.get_counts()).to(be_equal_to(counts - 1));
    }

    it "should await until counts not equals zero" {
        const NUMBER_OF_THREADS: usize = 10;
        let mut results = Vec::with_capacity(NUMBER_OF_THREADS);

        for _ in 0..NUMBER_OF_THREADS {
            let count_down_latch = latch.clone();
            let jh = thread::spawn(
                move || {
                    expect!(count_down_latch.get_counts()).to(be_greater_than(0));

                    count_down_latch.await();

                    expect!(count_down_latch.get_counts()).to(be_equal_to(0));
                }
            );
            results.push(jh);
        }

        thread::sleep(Duration::from_millis(100));

        latch.count_down();

        expect!(latch.get_counts()).to(be_equal_to(0));
        for jh in results {
            expect!(jh.join()).to(be_ok());
        }
    }
}

describe! semaphore_prim {

    before_each {
        let semaphore = Semaphore::new(1);
    }

    it "should release resource automaticaly" {
        {
            let guard = semaphore.acquire();
            let try_acquire = semaphore.try_acquire();
            expect!(try_acquire).to(be_none());
            drop(guard);
        }

        let try_acquire = semaphore.try_acquire();
        expect!(try_acquire).to(be_some());
    }

    it "should not release more than permissions" {
        semaphore.release();
        let try_acquire = semaphore.try_acquire();
        assert!(try_acquire.is_some());
       // expect!(try_acquire).to(be_some());

        let try_acquire = semaphore.try_acquire();
        expect!(try_acquire).to(be_none());
    }

    it "should block thread until resource will be released" {
        const NUMBER_OF_THREADS: usize = 10;
        let mut results = Vec::with_capacity(NUMBER_OF_THREADS);

        semaphore.acquire();

        for _ in 0..NUMBER_OF_THREADS {
            let semaphore = semaphore.clone();
            let jh = thread::spawn(
                move || {
                    let g = semaphore.acquire();
                    thread::sleep(Duration::from_millis(50));
                    drop(g);
                }
            );
            results.push(jh);
        }
        thread::sleep(Duration::from_millis(50));
        semaphore.release();

        for jh in results {
            expect!(jh.join()).to(be_ok());
        }
    }
}
<|endoftext|>


<commit_before>extern crate capstone;

static CODE: &'static [u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00";

fn expose(a: &str) {
}

fn main() {
    match capstone::Capstone::new(capstone::CsArch::ARCH_X86,
                                  capstone::CsMode::MODE_64) {
        Some(cs) => {
            if let Some(insns) = cs.disasm(CODE, 0x1000, 0) {
                println!("Got {} instructions", insns.len());

                for i in insns.iter() {
                    println!("{:?}", i);
                }
            }
        },
        None => {
            println!("Ohnoes");
        }
    }
}
<commit_msg>Nuke unused code<commit_after>extern crate capstone;

static CODE: &'static [u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00";

fn main() {
    match capstone::Capstone::new(capstone::CsArch::ARCH_X86,
                                  capstone::CsMode::MODE_64) {
        Some(cs) => {
            if let Some(insns) = cs.disasm(CODE, 0x1000, 0) {
                println!("Got {} instructions", insns.len());

                for i in insns.iter() {
                    println!("{:?}", i);
                }
            }
        },
        None => {
            println!("Ohnoes");
        }
    }
}
<|endoftext|>


<commit_before>use gl;
use gl::types::*;
use std::mem;
use std::ptr;
use std::raw;
use libc::types::common::c95;
use vertex;

/// Gets the id number for a given input of the shader program.
#[allow(non_snake_case_functions)]
unsafe fn glGetAttribLocation(shader_program: GLuint, name: &str) -> GLint {
  name.with_c_str(|ptr| gl::GetAttribLocation(shader_program, ptr))
}

unsafe fn aligned_slice_to_ptr<T>(vs: &[T], alignment: uint) -> *const c95::c_void {
  let vs_as_slice : raw::Slice<T> = mem::transmute(vs);
  assert_eq!(vs_as_slice.data as uint & (alignment - 1), 0);
  vs_as_slice.data as *const c95::c_void
}

/// A fixed-capacity array of GLfloat-based structures passed to OpenGL.
pub struct GLBuffer<T> {
  vertex_array: u32,
  vertex_buffer: u32,
  length: uint,
  capacity: uint,
}

impl<T: Clone> GLBuffer<T> {
  #[inline]
  /// An empty `GLBuffer`.
  pub unsafe fn null() -> GLBuffer<T> {
    GLBuffer {
      vertex_array: -1 as u32,
      vertex_buffer: -1 as u32,
      length: 0,
      capacity: 0,
    }
  }

  #[inline]
  /// Creates a new array of objects on the GPU.
  pub unsafe fn new(shader_program: GLuint, attribs: &[vertex::AttribData], capacity: uint) -> GLBuffer<T> {
    let mut vertex_array = 0;
    let mut vertex_buffer = 0;
    gl::GenVertexArrays(1, &mut vertex_array);
    gl::GenBuffers(1, &mut vertex_buffer);

    gl::BindVertexArray(vertex_array);
    gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);

    let mut offset = 0;
    for attrib in attribs.iter() {
      let shader_attrib = glGetAttribLocation(shader_program, attrib.name) as GLuint;
      assert!(shader_attrib != -1, "shader attribute \"{}\" not found", attrib.name);

      gl::EnableVertexAttribArray(shader_attrib);
      gl::VertexAttribPointer(
          shader_attrib,
          attrib.size as i32,
          gl::FLOAT,
          gl::FALSE as GLboolean,
          mem::size_of::<T>() as i32,
          ptr::null().offset(offset),
      );
      offset += (attrib.size * mem::size_of::<GLfloat>()) as int;
    }

    // Check that the attribs are sized correctly.
    assert_eq!(offset, mem::size_of::<T>() as int);

    gl::BufferData(
      gl::ARRAY_BUFFER,
      (capacity * mem::size_of::<T>()) as GLsizeiptr,
      ptr::null(),
      gl::DYNAMIC_DRAW,
    );

    GLBuffer {
      vertex_array: vertex_array,
      vertex_buffer: vertex_buffer,
      length: 0,
      capacity: capacity,
    }
  }

  /// Analog of vec::Vector::swap_remove`, but for GLBuffer data.
  pub unsafe fn swap_remove(&mut self, span: uint, i: uint) {
    gl::BindVertexArray(self.vertex_array);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer);

    self.length -= span;
    let size = mem::size_of::<T>();
    let copy_size = (size * span) as uint;
    let mut bytes: Vec<T> = Vec::with_capacity(span);
    bytes.set_len(span);
    gl::GetBufferSubData(
      gl::ARRAY_BUFFER,
      (self.length * size) as i64,
      copy_size as i64,
      mem::transmute(&bytes.as_mut_slice()[0]),
    );
    gl::BufferSubData(
      gl::ARRAY_BUFFER,
      (i * copy_size) as i64,
      copy_size as i64,
      aligned_slice_to_ptr(bytes.slice(0, span), 4),
    );
  }

  #[inline]
  /// Add a set of triangles to the set of triangles to render.
  pub unsafe fn push(&mut self, vs: &[T]) {
    if self.length + vs.len() > self.capacity {
      fail!("GLBuffer::push: {} into a {}/{} full GLbuffer", vs.len(), self.length, self.capacity);
    }

    gl::BindVertexArray(self.vertex_array);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer);

    let size = mem::size_of::<T>() as i64;
    gl::BufferSubData(
      gl::ARRAY_BUFFER,
      size * self.length as i64,
      size * vs.len() as i64,
      aligned_slice_to_ptr(vs, 4)
    );

    self.length += vs.len();
  }

  #[inline]
  /// Draws all the queued triangles to the screen.
  pub fn draw(&self, mode: GLenum) {
    self.draw_slice(mode, 0, self.length);
  }

  /// Draw some subset of the triangle array.
  pub fn draw_slice(&self, mode: GLenum, start: uint, len: uint) {
    gl::BindVertexArray(self.vertex_array);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer);

    gl::DrawArrays(mode, start as i32, len as i32);
  }
}

#[unsafe_destructor]
impl<T> Drop for GLBuffer<T> {
  #[inline]
  fn drop(&mut self) {
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer);
      gl::DeleteVertexArrays(1, &self.vertex_array);
    }
  }
}
<commit_msg>doc comment for glbuffer.<commit_after>use gl;
use gl::types::*;
use std::mem;
//! OpenGL arrays of verticies, all shaded with the same program.
use std::ptr;
use std::raw;
use libc::types::common::c95;
use vertex;

/// Gets the id number for a given input of the shader program.
#[allow(non_snake_case_functions)]
unsafe fn glGetAttribLocation(shader_program: GLuint, name: &str) -> GLint {
  name.with_c_str(|ptr| gl::GetAttribLocation(shader_program, ptr))
}

unsafe fn aligned_slice_to_ptr<T>(vs: &[T], alignment: uint) -> *const c95::c_void {
  let vs_as_slice : raw::Slice<T> = mem::transmute(vs);
  assert_eq!(vs_as_slice.data as uint & (alignment - 1), 0);
  vs_as_slice.data as *const c95::c_void
}

/// A fixed-capacity array of GLfloat-based structures passed to OpenGL.
pub struct GLBuffer<T> {
  vertex_array: u32,
  vertex_buffer: u32,
  length: uint,
  capacity: uint,
}

impl<T: Clone> GLBuffer<T> {
  #[inline]
  /// An empty `GLBuffer`.
  pub unsafe fn null() -> GLBuffer<T> {
    GLBuffer {
      vertex_array: -1 as u32,
      vertex_buffer: -1 as u32,
      length: 0,
      capacity: 0,
    }
  }

  #[inline]
  /// Creates a new array of objects on the GPU.
  pub unsafe fn new(shader_program: GLuint, attribs: &[vertex::AttribData], capacity: uint) -> GLBuffer<T> {
    let mut vertex_array = 0;
    let mut vertex_buffer = 0;
    gl::GenVertexArrays(1, &mut vertex_array);
    gl::GenBuffers(1, &mut vertex_buffer);

    gl::BindVertexArray(vertex_array);
    gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);

    let mut offset = 0;
    for attrib in attribs.iter() {
      let shader_attrib = glGetAttribLocation(shader_program, attrib.name) as GLuint;
      assert!(shader_attrib != -1, "shader attribute \"{}\" not found", attrib.name);

      gl::EnableVertexAttribArray(shader_attrib);
      gl::VertexAttribPointer(
          shader_attrib,
          attrib.size as i32,
          gl::FLOAT,
          gl::FALSE as GLboolean,
          mem::size_of::<T>() as i32,
          ptr::null().offset(offset),
      );
      offset += (attrib.size * mem::size_of::<GLfloat>()) as int;
    }

    // Check that the attribs are sized correctly.
    assert_eq!(offset, mem::size_of::<T>() as int);

    gl::BufferData(
      gl::ARRAY_BUFFER,
      (capacity * mem::size_of::<T>()) as GLsizeiptr,
      ptr::null(),
      gl::DYNAMIC_DRAW,
    );

    GLBuffer {
      vertex_array: vertex_array,
      vertex_buffer: vertex_buffer,
      length: 0,
      capacity: capacity,
    }
  }

  /// Analog of vec::Vector::swap_remove`, but for GLBuffer data.
  pub unsafe fn swap_remove(&mut self, span: uint, i: uint) {
    gl::BindVertexArray(self.vertex_array);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer);

    self.length -= span;
    let size = mem::size_of::<T>();
    let copy_size = (size * span) as uint;
    let mut bytes: Vec<T> = Vec::with_capacity(span);
    bytes.set_len(span);
    gl::GetBufferSubData(
      gl::ARRAY_BUFFER,
      (self.length * size) as i64,
      copy_size as i64,
      mem::transmute(&bytes.as_mut_slice()[0]),
    );
    gl::BufferSubData(
      gl::ARRAY_BUFFER,
      (i * copy_size) as i64,
      copy_size as i64,
      aligned_slice_to_ptr(bytes.slice(0, span), 4),
    );
  }

  #[inline]
  /// Add a set of triangles to the set of triangles to render.
  pub unsafe fn push(&mut self, vs: &[T]) {
    if self.length + vs.len() > self.capacity {
      fail!("GLBuffer::push: {} into a {}/{} full GLbuffer", vs.len(), self.length, self.capacity);
    }

    gl::BindVertexArray(self.vertex_array);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer);

    let size = mem::size_of::<T>() as i64;
    gl::BufferSubData(
      gl::ARRAY_BUFFER,
      size * self.length as i64,
      size * vs.len() as i64,
      aligned_slice_to_ptr(vs, 4)
    );

    self.length += vs.len();
  }

  #[inline]
  /// Draws all the queued triangles to the screen.
  pub fn draw(&self, mode: GLenum) {
    self.draw_slice(mode, 0, self.length);
  }

  /// Draw some subset of the triangle array.
  pub fn draw_slice(&self, mode: GLenum, start: uint, len: uint) {
    gl::BindVertexArray(self.vertex_array);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer);

    gl::DrawArrays(mode, start as i32, len as i32);
  }
}

#[unsafe_destructor]
impl<T> Drop for GLBuffer<T> {
  #[inline]
  fn drop(&mut self) {
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer);
      gl::DeleteVertexArrays(1, &self.vertex_array);
    }
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fmt;
use std::fs::OpenOptions;
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;

use libc::{self, c_void, off_t, size_t, MAP_FAILED, MAP_SHARED, O_SYNC, PROT_READ, PROT_WRITE};

use crate::gpio::{Error, Level, Mode, PullUpDown, Result};
use crate::system::DeviceInfo;

const PATH_DEV_GPIOMEM: &str = "/dev/gpiomem";
const PATH_DEV_MEM: &str = "/dev/mem";

// The BCM2835 has 41 32-bit registers related to the GPIO (datasheet @ 6.1).
const GPIO_MEM_REGISTERS: usize = 41;
const GPIO_MEM_SIZE: usize = GPIO_MEM_REGISTERS * std::mem::size_of::<u32>();

const GPFSEL0: usize = 0x00;
const GPSET0: usize = 0x1c / std::mem::size_of::<u32>();
const GPCLR0: usize = 0x28 / std::mem::size_of::<u32>();
const GPLEV0: usize = 0x34 / std::mem::size_of::<u32>();
const GPPUD: usize = 0x94 / std::mem::size_of::<u32>();
const GPPUDCLK0: usize = 0x98 / std::mem::size_of::<u32>();

pub struct GpioMem {
    mem_ptr: *mut u32,
    locks: [AtomicBool; GPIO_MEM_REGISTERS],
}

impl fmt::Debug for GpioMem {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("GpioMem")
            .field("mem_ptr", &self.mem_ptr)
            .field("locks", &format_args!("{{ .. }}"))
            .finish()
    }
}

impl GpioMem {
    pub fn open() -> Result<GpioMem> {
        // Try /dev/gpiomem first. If that fails, try /dev/mem instead. If neither works,
        // report back the error that's the most relevant.
        let mem_ptr = match Self::map_devgpiomem() {
            Ok(ptr) => ptr,
            Err(gpiomem_err) => match Self::map_devmem() {
                Ok(ptr) => ptr,
                Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::PermissionDenied => {
                    // Did /dev/gpiomem also give us a Permission Denied error? If so, return
                    // that path instead of /dev/mem. Solving /dev/gpiomem issues should be
                    // preferred (add user to gpio group) over /dev/mem (use sudo),
                    match gpiomem_err {
                        Error::Io(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {
                            return Err(Error::PermissionDenied(String::from(PATH_DEV_GPIOMEM)));
                        }
                        _ => return Err(Error::PermissionDenied(String::from(PATH_DEV_MEM))),
                    }
                }
                Err(Error::UnknownModel) => return Err(Error::UnknownModel),
                _ => return Err(gpiomem_err),
            },
        };

        let locks = init_array!(AtomicBool::new(false), GPIO_MEM_REGISTERS);

        Ok(GpioMem { mem_ptr, locks })
    }

    fn map_devgpiomem() -> Result<*mut u32> {
        // Open /dev/gpiomem with read/write/sync flags. This might fail if
        // /dev/gpiomem doesn't exist (< Raspbian Jessie), or /dev/gpiomem
        // doesn't have the appropriate permissions, or the current user is
        // not a member of the gpio group.
        let gpiomem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(O_SYNC)
            .open(PATH_DEV_GPIOMEM)?;

        // Memory-map /dev/gpiomem at offset 0
        let gpiomem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                PROT_READ | PROT_WRITE,
                MAP_SHARED,
                gpiomem_file.as_raw_fd(),
                0,
            )
        };

        if gpiomem_ptr == MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }

        Ok(gpiomem_ptr as *mut u32)
    }

    fn map_devmem() -> Result<*mut u32> {
        // Identify which SoC we're using, so we know what offset to start at
        let device_info = DeviceInfo::new().map_err(|_| Error::UnknownModel)?;

        let mem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(O_SYNC)
            .open(PATH_DEV_MEM)?;

        // Memory-map /dev/mem at the appropriate offset for our SoC
        let mem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                PROT_READ | PROT_WRITE,
                MAP_SHARED,
                mem_file.as_raw_fd(),
                (device_info.peripheral_base() + device_info.gpio_offset()) as off_t,
            )
        };

        if mem_ptr == MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }

        Ok(mem_ptr as *mut u32)
    }

    #[inline(always)]
    fn read(&self, offset: usize) -> u32 {
        unsafe { ptr::read_volatile(self.mem_ptr.add(offset)) }
    }

    #[inline(always)]
    fn write(&self, offset: usize, value: u32) {
        unsafe {
            ptr::write_volatile(self.mem_ptr.add(offset), value);
        }
    }

    #[inline(always)]
    pub(crate) fn set_high(&self, pin: u8) {
        let offset = GPSET0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }

    #[inline(always)]
    pub(crate) fn set_low(&self, pin: u8) {
        let offset = GPCLR0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }

    #[inline(always)]
    pub(crate) fn level(&self, pin: u8) -> Level {
        let offset = GPLEV0 + pin as usize / 32;
        let shift = pin % 32;

        let reg_value = self.read(offset);

        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b1) }
    }

    pub(crate) fn mode(&self, pin: u8) -> Mode {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;

        let reg_value = self.read(offset);

        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b111) }
    }

    pub(crate) fn set_mode(&self, pin: u8, mode: Mode) {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;

        loop {
            if !self.locks[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                break;
            }
        }

        let reg_value = self.read(offset);
        self.write(
            offset,
            (reg_value & !(0b111 << shift)) | ((mode as u32) << shift),
        );

        self.locks[offset].store(false, Ordering::SeqCst);
    }

    pub(crate) fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
        let offset = GPPUDCLK0 + pin as usize / 32;
        let shift = pin % 32;

        loop {
            if !self.locks[GPPUD].compare_and_swap(false, true, Ordering::SeqCst) {
                if !self.locks[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                    break;
                } else {
                    self.locks[GPPUD].store(false, Ordering::SeqCst);
                }
            }
        }

        // Set the control signal in GPPUD.
        let reg_value = self.read(GPPUD);
        self.write(GPPUD, (reg_value & !0b11) | ((pud as u32) & 0b11));

        // The datasheet mentions waiting at least 150 cycles for set-up and hold, but
        // doesn't state which clock is used. This is likely the VPU clock (see
        // https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=163352). At either
        // 250MHz or 400MHz, a 5µs delay + overhead is more than adequate.

        // Set-up time for the control signal.
        thread::sleep(Duration::new(0, 5000)); // >= 5µs

        // Clock the control signal into the selected pin.
        self.write(offset, 1 << shift);

        // Hold time for the control signal.
        thread::sleep(Duration::new(0, 5000)); // >= 5µs

        // Remove the control signal and clock.
        self.write(GPPUD, reg_value & !0b11);
        self.write(offset, 0);

        self.locks[offset].store(false, Ordering::SeqCst);
        self.locks[GPPUD].store(false, Ordering::SeqCst);
    }
}

impl Drop for GpioMem {
    fn drop(&mut self) {
        unsafe {
            libc::munmap(self.mem_ptr as *mut c_void, GPIO_MEM_SIZE as size_t);
        }
    }
}

// Required because of the raw pointer to our memory-mapped file
unsafe impl Send for GpioMem {}
unsafe impl Sync for GpioMem {}
<commit_msg>Update to handle setting GPIO pull ups and pull downs correctly for Raspberry Pi 4.<commit_after>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fmt;
use std::fs::OpenOptions;
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;
use libc::{self, c_void, MAP_FAILED, MAP_SHARED, O_SYNC, off_t, PROT_READ, PROT_WRITE, size_t};
use crate::gpio::{Error, Level, Mode, PullUpDown, Result};
use crate::system::{DeviceInfo, SoC};

const PATH_DEV_GPIOMEM: &str = "/dev/gpiomem";
const PATH_DEV_MEM: &str = "/dev/mem";
// The BCM2835 has 41 32-bit registers related to the GPIO (datasheet @ 6.1).
// The BCM2711 (RPi4) has 58 32-bit registers related to the GPIO.
const GPIO_MEM_REGISTERS: usize = 58;
const GPIO_MEM_SIZE: usize = GPIO_MEM_REGISTERS * std::mem::size_of::<u32>();
const GPFSEL0: usize = 0x00;
const GPSET0: usize = 0x1c / std::mem::size_of::<u32>();
const GPCLR0: usize = 0x28 / std::mem::size_of::<u32>();
const GPLEV0: usize = 0x34 / std::mem::size_of::<u32>();
const GPPUD: usize = 0x94 / std::mem::size_of::<u32>();
const GPPUDCLK0: usize = 0x98 / std::mem::size_of::<u32>();
// Only available in BCM2711 (RPi4).
const GPPUD_CNTRL_REG0: usize = 0xe4 / std::mem::size_of::<u32>();

pub struct GpioMem {
    mem_ptr: *mut u32,
    locks: [AtomicBool; GPIO_MEM_REGISTERS],
    soc: SoC,
}

impl fmt::Debug for GpioMem {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("GpioMem")
         .field("mem_ptr", &self.mem_ptr)
         .field("locks", &format_args!("{{ .. }}"))
         .field("soc", &self.soc)
         .finish()
    }
}

impl GpioMem {
    pub fn open() -> Result<GpioMem> {
        // Try /dev/gpiomem first. If that fails, try /dev/mem instead. If neither works,
        // report back the error that's the most relevant.
        let mem_ptr = match Self::map_devgpiomem() {
            Ok(ptr) => ptr,
            Err(gpiomem_err) => match Self::map_devmem() {
                Ok(ptr) => ptr,
                Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::PermissionDenied => {
                    // Did /dev/gpiomem also give us a Permission Denied error? If so, return
                    // that path instead of /dev/mem. Solving /dev/gpiomem issues should be
                    // preferred (add user to gpio group) over /dev/mem (use sudo),
                    match gpiomem_err {
                        Error::Io(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {
                            return Err(Error::PermissionDenied(String::from(PATH_DEV_GPIOMEM)));
                        }
                        _ => return Err(Error::PermissionDenied(String::from(PATH_DEV_MEM))),
                    }
                }
                Err(Error::UnknownModel) => return Err(Error::UnknownModel),
                _ => return Err(gpiomem_err),
            },
        };
        let locks = init_array!(AtomicBool::new(false), GPIO_MEM_REGISTERS);
        // Identify which SoC we're using.
        let soc = DeviceInfo::new().map_err(|_| Error::UnknownModel)?.soc();
        Ok(GpioMem { mem_ptr, locks, soc })
    }
    fn map_devgpiomem() -> Result<*mut u32> {
        // Open /dev/gpiomem with read/write/sync flags. This might fail if
        // /dev/gpiomem doesn't exist (< Raspbian Jessie), or /dev/gpiomem
        // doesn't have the appropriate permissions, or the current user is
        // not a member of the gpio group.
        let gpiomem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(O_SYNC)
            .open(PATH_DEV_GPIOMEM)?;
        // Memory-map /dev/gpiomem at offset 0
        let gpiomem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                PROT_READ | PROT_WRITE,
                MAP_SHARED,
                gpiomem_file.as_raw_fd(),
                0,
            )
        };
        if gpiomem_ptr == MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }
        Ok(gpiomem_ptr as *mut u32)
    }
    fn map_devmem() -> Result<*mut u32> {
        // Identify which SoC we're using, so we know what offset to start at
        let device_info = DeviceInfo::new().map_err(|_| Error::UnknownModel)?;
        let mem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(O_SYNC)
            .open(PATH_DEV_MEM)?;
        // Memory-map /dev/mem at the appropriate offset for our SoC
        let mem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                PROT_READ | PROT_WRITE,
                MAP_SHARED,
                mem_file.as_raw_fd(),
                (device_info.peripheral_base() + device_info.gpio_offset()) as off_t,
            )
        };
        if mem_ptr == MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }
        Ok(mem_ptr as *mut u32)
    }
    #[inline(always)]
    fn read(&self, offset: usize) -> u32 {
        unsafe { ptr::read_volatile(self.mem_ptr.add(offset)) }
    }
    #[inline(always)]
    fn write(&self, offset: usize, value: u32) {
        unsafe {
            ptr::write_volatile(self.mem_ptr.add(offset), value);
        }
    }
    #[inline(always)]
    pub(crate) fn set_high(&self, pin: u8) {
        let offset = GPSET0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }
    #[inline(always)]
    pub(crate) fn set_low(&self, pin: u8) {
        let offset = GPCLR0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }
    #[inline(always)]
    pub(crate) fn level(&self, pin: u8) -> Level {
        let offset = GPLEV0 + pin as usize / 32;
        let shift = pin % 32;
        let reg_value = self.read(offset);
        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b1) }
    }
    pub(crate) fn mode(&self, pin: u8) -> Mode {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;
        let reg_value = self.read(offset);
        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b111) }
    }
    pub(crate) fn set_mode(&self, pin: u8, mode: Mode) {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;
        loop {
            if !self.locks[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                break;
            }
        }
        let reg_value = self.read(offset);
        self.write(
            offset,
            (reg_value & !(0b111 << shift)) | ((mode as u32) << shift),
        );
        self.locks[offset].store(false, Ordering::SeqCst);
    }
    pub(crate) fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
        let lock: usize;
        let offset: usize;
        let shift: u8;
        if self.soc == SoC::Bcm2711 {
            lock = GPPUD_CNTRL_REG0 + pin as usize / 32;
            offset = GPPUD_CNTRL_REG0 + pin as usize / 16;
            shift = (pin & 0xf) << 1;
            loop {
                if !self.locks[lock].compare_and_swap(false, true, Ordering::SeqCst) {
                    break;
                }
            }
            let reg_value = self.read(offset);
            self.write(
                offset,
                (reg_value & !(0b11 << shift)) | ((pud as u32) << shift),
            );
            self.locks[lock].store(false, Ordering::SeqCst);
        } else {
            lock = GPPUDCLK0 + pin as usize / 32;
            offset = lock;
            shift = pin % 32;
            loop {
                if !self.locks[GPPUD].compare_and_swap(false, true, Ordering::SeqCst) {
                    if !self.locks[lock].compare_and_swap(false, true, Ordering::SeqCst) {
                        break;
                    } else {
                        self.locks[GPPUD].store(false, Ordering::SeqCst);
                    }
                }
            }
            // Set the control signal in GPPUD.
            let reg_value = self.read(GPPUD);
            self.write(GPPUD, (reg_value & !0b11) | ((pud as u32) & 0b11));
            // The datasheet mentions waiting at least 150 cycles for set-up and hold, but
            // doesn't state which clock is used. This is likely the VPU clock (see
            // https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=163352). At either
            // 250MHz or 400MHz, a 5µs delay + overhead is more than adequate.
            // Set-up time for the control signal.
            thread::sleep(Duration::new(0, 5000)); // >= 5µs
            // Clock the control signal into the selected pin.
            self.write(offset, 1 << shift);
            // Hold time for the control signal.
            thread::sleep(Duration::new(0, 5000)); // >= 5µs
            // Remove the control signal and clock.
            self.write(GPPUD, reg_value & !0b11);
            self.write(offset, 0);
            self.locks[lock].store(false, Ordering::SeqCst);
            self.locks[GPPUD].store(false, Ordering::SeqCst);
        }
    }
}

impl Drop for GpioMem {
    fn drop(&mut self) {
        unsafe {
            libc::munmap(self.mem_ptr as *mut c_void, GPIO_MEM_SIZE as size_t);
        }
    }
}

// Required because of the raw pointer to our memory-mapped file
unsafe impl Send for GpioMem {}

unsafe impl Sync for GpioMem {}
<|endoftext|>