<commit_before>extern crate reqwest;
extern crate serde_json;

use self::reqwest::header::{Authorization, Link, UserAgent};
use self::reqwest::{Error, Response, Url};

use std::io::Read;
use std::collections::HashMap;
use std::{thread, time};

static USERAGENT: &'static str = "release-party-br";

#[derive(Deserialize, Debug)]
pub struct GithubRepo {
    id: i32,
    pub name: String,
    pub url: String,
}

#[derive(Deserialize, Debug)]
pub struct CompareCommitsResponse {
    pub status: String,
    pub behind_by: i32,
}

#[derive(Deserialize, Debug)]
pub struct GithubPullRequest {
    id: i32,
    pub url: String,
    pub html_url: String,
    pub head: Commit,
    pub base: Commit,
}

#[derive(Deserialize, Debug)]
pub struct Commit {
    pub sha: String,
    pub label: String,
}

pub fn is_release_up_to_date_with_master(repo_url: &str, token: &str, client: &reqwest::Client) -> bool {
    let repo_pr_url = format!("{}/{}/{}...{}", repo_url, "compare", "master", "release");
    let url = match Url::parse(&repo_pr_url) {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for compare page: {}", e);
            return true;
        }
    };
    let mut res = match client
        .get(url.clone())
        .expect("Couldn't make a request builder for comparison page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
    {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for compare page: {}", e);
            return true;
        }
    };
    // Check rate limit headers in response
    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error checking commit diff for {}: {}", repo_url, e),
    }

    let commits_diff: CompareCommitsResponse = match serde_json::from_str(&buffer) {
        Ok(compare_response) => compare_response,
        Err(_) => return true,
    };

    if commits_diff.behind_by > 0 {
        return false;
    }

    true
}

fn delay_if_running_out_of_requests(response_headers: &reqwest::header::Headers) {
    if close_to_running_out_of_requests(response_headers) {
        thread::sleep(time::Duration::from_millis(1000));
    }
}

fn close_to_running_out_of_requests(response_headers: &reqwest::header::Headers) -> bool {
    // do a get_raw for X-RateLimit-Remaining ?
    false
}

fn response_has_a_next_link(response_headers: &reqwest::header::Headers) -> bool {
    if response_headers.get::<Link>().is_none() {
        return false;
    }
    for link in response_headers
        .get::<Link>()
        .expect("links in has next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    return true;
                }
            }
            None => return false,
        }
    }
    false
}

// Expects caller to check to ensure the `next` link is present
fn response_next_link(response_headers: &reqwest::header::Headers) -> Result<Url, String> {
    for link in response_headers
        .get::<Link>()
        .expect("links in response next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    let uri = Url::parse(link.link()).expect("Should have been able to parse the nextlink");
                    return Ok(uri);
                }
            }
            None => (),
        }
    }
    Err("Couldn't find a next link: does it exist?".to_string())
}

pub fn get_repos_at(repos_url: &str, token: &str, client: &reqwest::Client) -> Result<Vec<GithubRepo>, String> {
    // We need to pass in the URL from the link headers to github API docs.
    // We'll construct it this first time.
    let url = match Url::parse_with_params(repos_url, &[("per_page", "50")]) {
        Ok(new_url) => new_url,
        Err(e) => return Err(format!("Couldn't parse uri {:?} : {:?}", repos_url, e)),
    };
    let mut response = get_repos_at_url(url, token, client).expect("request failed");
    delay_if_running_out_of_requests(response.headers());
    let mut buffer = String::new();
    match response.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error reading response from github when getting repo list: {}", e),
    }
    let mut repos = repo_list_from_string(&buffer).expect("expected repos");

    if response_has_a_next_link(response.headers()) {
        loop {
            let paging_url = response_next_link(response.headers()).expect("a thing");
            response = get_repos_at_url(paging_url, token, client).expect("request failed");
            delay_if_running_out_of_requests(response.headers());
            buffer = String::new();
            match response.read_to_string(&mut buffer) {
                Ok(_) => (),
                Err(e) => println!("error reading response from github when getting repo list: {}", e),
            }
            repos.append(&mut repo_list_from_string(&buffer).expect("expected repos"));
            if !response_has_a_next_link(response.headers()) {
                break;
            }
        }
    }
    println!("Number of repos to check: {:?}", repos.len());
    Ok(repos)
}

fn get_repos_at_url(url: reqwest::Url, token: &str, client: &reqwest::Client) -> Result<Response, Error> {
    client
        .get(url)
        .expect("Couldn't make a request builder for repos page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
}

fn repo_list_from_string(json_str: &str) -> Result<Vec<GithubRepo>, String> {
    // This looks a bit weird due to supplying type hints to deserialize:
    let _: Vec<GithubRepo> = match serde_json::from_str(json_str) {
        Ok(v) => return Ok(v),
        Err(e) => return Err(format!("Couldn't deserialize repos from github: {}", e)),
    };
}

pub fn existing_release_pr_location(repo: &GithubRepo, token: &str, client: &reqwest::Client) -> Option<String> {
    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let url = match Url::parse_with_params(&repo_pr_url, &[("head", "master"), ("base", "release")]) {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for existing pr location: {}", e);
            return None;
        }
    };
    let mut res = match client
        .get(url)
        .expect("Couldn't make a request builder for existing PRs page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
    {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for existing PR location: {}", e);
            return None;
        }
    };
    delay_if_running_out_of_requests(res.headers());
    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error finding existing pr for {}: {}", repo.name, e),
    }

    let pull_reqs: Vec<GithubPullRequest> = match serde_json::from_str(&buffer) {
        Ok(v) => v,
        Err(_) => Vec::new(),
    };

    if !pull_reqs.is_empty() {
        return Some(pull_reqs[0].html_url.clone());
    }

    None
}

// Try to create the release PR and return the URL of it:
pub fn create_release_pull_request(repo: &GithubRepo, token: &str, client: &reqwest::Client) -> Result<String, String> {
    let mut pr_body = HashMap::new();
    pr_body.insert("title", "automated release partay");
    pr_body.insert("head", "master");
    pr_body.insert("base", "release");

    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let mut res = match client
        .post(&repo_pr_url)
        .expect("Couldn't make a request builder for creating PR url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .json(&pr_body)
        .expect("Couldn't make the JSON payload for creating a PR")
        .send()
    {
        Ok(response) => response,
        Err(e) => return Err(format!("Error in request to github creating new PR: {}", e)),
    };

    delay_if_running_out_of_requests(res.headers());
    if res.status().is_success() {
        let mut buffer = String::new();
        match res.read_to_string(&mut buffer) {
            Ok(_) => (),
            Err(e) => println!("error reading response after creating new release PR for {}: {}", repo.name, e),
        }
        let pull_req: GithubPullRequest = match serde_json::from_str(&buffer) {
            Ok(v) => v,
            Err(e) => {
                return Err(format!(
                    "Couldn't deserialize create pull req response for {}: {}",
                    repo.name,
                    e
                ))
            }
        };
        return Ok(pull_req.html_url);
    }
    // 422 unprocessable means it's there already
    // 422 unprocessable also means the branch is up to date

    Err("Release branch already up to date?".to_string())
}
<commit_msg>Gets bottoming-out rate limiter working.<commit_after>extern crate reqwest;
extern crate serde_json;

use self::reqwest::header::{Authorization, Link, UserAgent};
use self::reqwest::{Error, Response, Url};

use std::io::Read;
use std::collections::HashMap;
use std::{thread, time};

static USERAGENT: &'static str = "release-party-br";

#[derive(Deserialize, Debug)]
pub struct GithubRepo {
    id: i32,
    pub name: String,
    pub url: String,
}

#[derive(Deserialize, Debug)]
pub struct CompareCommitsResponse {
    pub status: String,
    pub behind_by: i32,
}

#[derive(Deserialize, Debug)]
pub struct GithubPullRequest {
    id: i32,
    pub url: String,
    pub html_url: String,
    pub head: Commit,
    pub base: Commit,
}

#[derive(Deserialize, Debug)]
pub struct Commit {
    pub sha: String,
    pub label: String,
}

pub fn is_release_up_to_date_with_master(repo_url: &str, token: &str, client: &reqwest::Client) -> bool {
    let repo_pr_url = format!("{}/{}/{}...{}", repo_url, "compare", "master", "release");
    let url = match Url::parse(&repo_pr_url) {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for compare page: {}", e);
            return true;
        }
    };
    let mut res = match client
        .get(url.clone())
        .expect("Couldn't make a request builder for comparison page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
    {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for compare page: {}", e);
            return true;
        }
    };
    // Check rate limit headers in response
    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error checking commit diff for {}: {}", repo_url, e),
    }

    let commits_diff: CompareCommitsResponse = match serde_json::from_str(&buffer) {
        Ok(compare_response) => compare_response,
        Err(_) => return true,
    };

    if commits_diff.behind_by > 0 {
        return false;
    }

    true
}

fn delay_if_running_out_of_requests(response_headers: &reqwest::header::Headers) {
    if close_to_running_out_of_requests(response_headers) {
        println!("Running low on requests, throttling back...");
        thread::sleep(time::Duration::from_millis(1000));
    }
}

// This is a bit simplistic at the moment by trying to prevent bottoming out completely.
// An improvement could be to use the fraction of requests left over the overall limit.
// EG: 55 requests left out of a limit of 60 is fine.
// 10 requests left out of 60 is time to throttle back.
// TODO: dump the `expects` and handle things more gracefully.
fn close_to_running_out_of_requests(response_headers: &reqwest::header::Headers) -> bool {
    let requests_to_treat_as_running_out = 10;
    let remaining_requests = match response_headers.get_raw("X-RateLimit-Remaining") {
        Some(remaining_req_from_github) => {
            let req_left = String::from_utf8(remaining_req_from_github.one()
                .expect("Should have a single entry for X-RateLimit-Remaining")
                .to_vec())
                .expect("Should be able to parse slice as string")
                .replace('"', ""); // the formatter puts quotes around the number.  EG: "55"
            req_left.parse::<i32>().expect("Expected number in X-RateLimit-Remaining field")
        },
        // If it's not specified, we'll say we have enough to keep going:
        None => requests_to_treat_as_running_out + 1,
    };
    remaining_requests < requests_to_treat_as_running_out
}

fn response_has_a_next_link(response_headers: &reqwest::header::Headers) -> bool {
    if response_headers.get::<Link>().is_none() {
        return false;
    }
    for link in response_headers
        .get::<Link>()
        .expect("links in has next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    return true;
                }
            }
            None => return false,
        }
    }
    false
}

// Expects caller to check to ensure the `next` link is present
fn response_next_link(response_headers: &reqwest::header::Headers) -> Result<Url, String> {
    for link in response_headers
        .get::<Link>()
        .expect("links in response next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    let uri = Url::parse(link.link()).expect("Should have been able to parse the nextlink");
                    return Ok(uri);
                }
            }
            None => (),
        }
    }
    Err("Couldn't find a next link: does it exist?".to_string())
}

pub fn get_repos_at(repos_url: &str, token: &str, client: &reqwest::Client) -> Result<Vec<GithubRepo>, String> {
    // We need to pass in the URL from the link headers to github API docs.
    // We'll construct it this first time.
    let url = match Url::parse_with_params(repos_url, &[("per_page", "50")]) {
        Ok(new_url) => new_url,
        Err(e) => return Err(format!("Couldn't parse uri {:?} : {:?}", repos_url, e)),
    };
    let mut response = get_repos_at_url(url, token, client).expect("request failed");
    delay_if_running_out_of_requests(response.headers());
    let mut buffer = String::new();
    match response.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error reading response from github when getting repo list: {}", e),
    }
    let mut repos = repo_list_from_string(&buffer).expect("expected repos");

    if response_has_a_next_link(response.headers()) {
        loop {
            let paging_url = response_next_link(response.headers()).expect("a thing");
            response = get_repos_at_url(paging_url, token, client).expect("request failed");
            delay_if_running_out_of_requests(response.headers());
            buffer = String::new();
            match response.read_to_string(&mut buffer) {
                Ok(_) => (),
                Err(e) => println!("error reading response from github when getting repo list: {}", e),
            }
            repos.append(&mut repo_list_from_string(&buffer).expect("expected repos"));
            if !response_has_a_next_link(response.headers()) {
                break;
            }
        }
    }
    println!("Number of repos to check: {:?}", repos.len());
    Ok(repos)
}

fn get_repos_at_url(url: reqwest::Url, token: &str, client: &reqwest::Client) -> Result<Response, Error> {
    client
        .get(url)
        .expect("Couldn't make a request builder for repos page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
}

fn repo_list_from_string(json_str: &str) -> Result<Vec<GithubRepo>, String> {
    // This looks a bit weird due to supplying type hints to deserialize:
    let _: Vec<GithubRepo> = match serde_json::from_str(json_str) {
        Ok(v) => return Ok(v),
        Err(e) => return Err(format!("Couldn't deserialize repos from github: {}", e)),
    };
}

pub fn existing_release_pr_location(repo: &GithubRepo, token: &str, client: &reqwest::Client) -> Option<String> {
    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let url = match Url::parse_with_params(&repo_pr_url, &[("head", "master"), ("base", "release")]) {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for existing pr location: {}", e);
            return None;
        }
    };
    let mut res = match client
        .get(url)
        .expect("Couldn't make a request builder for existing PRs page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
    {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for existing PR location: {}", e);
            return None;
        }
    };
    delay_if_running_out_of_requests(res.headers());
    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error finding existing pr for {}: {}", repo.name, e),
    }

    let pull_reqs: Vec<GithubPullRequest> = match serde_json::from_str(&buffer) {
        Ok(v) => v,
        Err(_) => Vec::new(),
    };

    if !pull_reqs.is_empty() {
        return Some(pull_reqs[0].html_url.clone());
    }

    None
}

// Try to create the release PR and return the URL of it:
pub fn create_release_pull_request(repo: &GithubRepo, token: &str, client: &reqwest::Client) -> Result<String, String> {
    let mut pr_body = HashMap::new();
    pr_body.insert("title", "automated release partay");
    pr_body.insert("head", "master");
    pr_body.insert("base", "release");

    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let mut res = match client
        .post(&repo_pr_url)
        .expect("Couldn't make a request builder for creating PR url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .json(&pr_body)
        .expect("Couldn't make the JSON payload for creating a PR")
        .send()
    {
        Ok(response) => response,
        Err(e) => return Err(format!("Error in request to github creating new PR: {}", e)),
    };

    delay_if_running_out_of_requests(res.headers());
    if res.status().is_success() {
        let mut buffer = String::new();
        match res.read_to_string(&mut buffer) {
            Ok(_) => (),
            Err(e) => println!("error reading response after creating new release PR for {}: {}", repo.name, e),
        }
        let pull_req: GithubPullRequest = match serde_json::from_str(&buffer) {
            Ok(v) => v,
            Err(e) => {
                return Err(format!(
                    "Couldn't deserialize create pull req response for {}: {}",
                    repo.name,
                    e
                ))
            }
        };
        return Ok(pull_req.html_url);
    }
    // 422 unprocessable means it's there already
    // 422 unprocessable also means the branch is up to date

    Err("Release branch already up to date?".to_string())
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 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::io;
use std::result;
use std::time::Duration;

use libc;

pub use libc::{epoll_event, EPOLLERR, EPOLLET, EPOLLIN, EPOLLONESHOT, EPOLLOUT, EPOLLPRI};

pub type Result<T> = result::Result<T, io::Error>;

fn parse_retval(retval: libc::c_int) -> Result<i32> {
    if retval == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(retval)
    }
}

// We're using EventFd to wake up another thread
// that's waiting for epoll_wait() to return.
#[derive(Debug)]
pub struct EventFd {
    fd: i32,
}

impl EventFd {
    pub fn new() -> Result<EventFd> {
        Ok(EventFd {
            fd: unsafe {
                parse_retval(libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_SEMAPHORE))?
            },
        })
    }

    pub fn notify(&self) -> Result<()> {
        let buffer: u64 = 1;

        if unsafe { libc::write(self.fd, &buffer as *const u64 as *const libc::c_void, 8) } == -1 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }

    pub fn fd(&self) -> i32 {
        self.fd
    }
}

impl Drop for EventFd {
    fn drop(&mut self) {
        unsafe {
            libc::close(self.fd);
        }
    }
}

#[derive(Debug)]
pub struct Epoll {
    fd: libc::c_int,
}

impl Epoll {
    pub fn new() -> Result<Epoll> {
        Ok(Epoll {
            fd: unsafe { parse_retval(libc::epoll_create1(0))? },
        })
    }

    pub fn add(&self, fd: i32, id: u64, event_mask: i32) -> Result<()> {
        let mut event = libc::epoll_event {
            events: event_mask as u32,
            u64: id as u64,
        };

        unsafe {
            parse_retval(libc::epoll_ctl(
                self.fd,
                libc::EPOLL_CTL_ADD,
                fd,
                &mut event,
            ))?;
        }

        Ok(())
    }

    pub fn modify(&self, fd: i32, id: u64, event_mask: i32) -> Result<()> {
        let mut event = libc::epoll_event {
            events: event_mask as u32,
            u64: id as u64,
        };

        unsafe {
            parse_retval(libc::epoll_ctl(
                self.fd,
                libc::EPOLL_CTL_MOD,
                fd,
                &mut event,
            ))?;
        }

        Ok(())
    }

    pub fn delete(&self, fd: i32) -> Result<()> {
        let mut event = libc::epoll_event { events: 0, u64: 0 };

        unsafe {
            parse_retval(libc::epoll_ctl(
                self.fd,
                libc::EPOLL_CTL_DEL,
                fd,
                &mut event,
            ))?;
        }

        Ok(())
    }

    pub fn wait(
        &self,
        events: &mut [libc::epoll_event],
        timeout: Option<Duration>,
    ) -> Result<usize> {
        if events.is_empty() {
            return Ok(0);
        }

        let timeout: i32 = if let Some(duration) = timeout {
            (duration.as_secs() * 1_000) as i32 + duration.subsec_millis() as i32
        } else {
            -1
        };

        unsafe {
            Ok(parse_retval(libc::epoll_wait(
                self.fd,
                events.as_mut_ptr(),
                events.len() as i32,
                timeout,
            ))? as usize)
        }
    }
}

impl Drop for Epoll {
    fn drop(&mut self) {
        unsafe {
            libc::close(self.fd);
        }
    }
}
<commit_msg>Revert use of subsec_millis back to subsec_nanos<commit_after>// Copyright (c) 2017-2018 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.

#![cfg_attr(feature = "cargo-clippy", allow(duration_subsec))]

use std::io;
use std::result;
use std::time::Duration;

use libc;

pub use libc::{epoll_event, EPOLLERR, EPOLLET, EPOLLIN, EPOLLONESHOT, EPOLLOUT, EPOLLPRI};

pub type Result<T> = result::Result<T, io::Error>;

fn parse_retval(retval: libc::c_int) -> Result<i32> {
    if retval == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(retval)
    }
}

// We're using EventFd to wake up another thread
// that's waiting for epoll_wait() to return.
#[derive(Debug)]
pub struct EventFd {
    fd: i32,
}

impl EventFd {
    pub fn new() -> Result<EventFd> {
        Ok(EventFd {
            fd: unsafe {
                parse_retval(libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_SEMAPHORE))?
            },
        })
    }

    pub fn notify(&self) -> Result<()> {
        let buffer: u64 = 1;

        if unsafe { libc::write(self.fd, &buffer as *const u64 as *const libc::c_void, 8) } == -1 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }

    pub fn fd(&self) -> i32 {
        self.fd
    }
}

impl Drop for EventFd {
    fn drop(&mut self) {
        unsafe {
            libc::close(self.fd);
        }
    }
}

#[derive(Debug)]
pub struct Epoll {
    fd: libc::c_int,
}

impl Epoll {
    pub fn new() -> Result<Epoll> {
        Ok(Epoll {
            fd: unsafe { parse_retval(libc::epoll_create1(0))? },
        })
    }

    pub fn add(&self, fd: i32, id: u64, event_mask: i32) -> Result<()> {
        let mut event = libc::epoll_event {
            events: event_mask as u32,
            u64: id as u64,
        };

        unsafe {
            parse_retval(libc::epoll_ctl(
                self.fd,
                libc::EPOLL_CTL_ADD,
                fd,
                &mut event,
            ))?;
        }

        Ok(())
    }

    pub fn modify(&self, fd: i32, id: u64, event_mask: i32) -> Result<()> {
        let mut event = libc::epoll_event {
            events: event_mask as u32,
            u64: id as u64,
        };

        unsafe {
            parse_retval(libc::epoll_ctl(
                self.fd,
                libc::EPOLL_CTL_MOD,
                fd,
                &mut event,
            ))?;
        }

        Ok(())
    }

    pub fn delete(&self, fd: i32) -> Result<()> {
        let mut event = libc::epoll_event { events: 0, u64: 0 };

        unsafe {
            parse_retval(libc::epoll_ctl(
                self.fd,
                libc::EPOLL_CTL_DEL,
                fd,
                &mut event,
            ))?;
        }

        Ok(())
    }

    pub fn wait(
        &self,
        events: &mut [libc::epoll_event],
        timeout: Option<Duration>,
    ) -> Result<usize> {
        if events.is_empty() {
            return Ok(0);
        }

        let timeout: i32 = if let Some(duration) = timeout {
            (duration.as_secs() * 1_000) as i32 + (duration.subsec_nanos() / 1_000_000) as i32
            // (duration.as_secs() * 1_000) as i32 + duration.subsec_millis() as i32
        } else {
            -1
        };

        unsafe {
            Ok(parse_retval(libc::epoll_wait(
                self.fd,
                events.as_mut_ptr(),
                events.len() as i32,
                timeout,
            ))? as usize)
        }
    }
}

impl Drop for Epoll {
    fn drop(&mut self) {
        unsafe {
            libc::close(self.fd);
        }
    }
}
<|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.

//! A very simple bit-vector that serves the needs of `primal`.

use std::cmp;
use std::fmt;
use std::hash;
use std::iter::repeat;
use std::ops::Index;

const BITS: usize = 64;

static TRUE: bool = true;
static FALSE: bool = false;

/// The bitvector type.
pub struct BitVec {
    /// Internal representation of the bit vector
    storage: Vec<u64>,
    /// The number of valid bits in the internal representation
    nbits: usize
}

impl Index<usize> for BitVec {
    type Output = bool;

    #[inline]
    fn index(&self, i: usize) -> &bool {
        if self.get(i).expect("index out of bounds") {
            &TRUE
        } else {
            &FALSE
        }
    }
}

impl BitVec {
    /// An operation might screw up the unused bits in the last block of the
    /// `BitVec`. As per (3), it's assumed to be all 0s. This method fixes it up.
    fn fix_last_block(&mut self) {
        let extra_bits = self.len() % BITS;
        if extra_bits > 0 {
            let mask = (1 << extra_bits) - 1;
            let storage_len = self.storage.len();
            self.storage[storage_len - 1] &= mask;
        }
    }

    /// Creates an empty `BitVec`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    /// let mut bv = BitVec::new();
    /// ```
    pub fn new() -> BitVec {
        BitVec { storage: Vec::new(), nbits: 0 }
    }

    #[inline]
    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
        unsafe {
            std::slice::from_raw_parts_mut(self.storage.as_mut_ptr() as *mut _,
                                           (self.nbits + 7) / 8)
        }
    }
    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        unsafe {
            std::slice::from_raw_parts(self.storage.as_ptr() as *const _,
                                       (self.nbits + 7) / 8)
        }
    }

    /// Creates a `BitVec` that holds `nbits` elements, setting each element
    /// to `bit`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    ///
    /// let mut bv = BitVec::from_elem(10, false);
    /// assert_eq!(bv.len(), 10);
    /// for x in bv.iter() {
    ///     assert_eq!(x, false);
    /// }
    /// ```
    pub fn from_elem(nbits: usize, bit: bool) -> BitVec {
        let nblocks = nbits.checked_add(BITS - 1).expect("capacity overflow") / BITS;
        let mut bit_vec = BitVec {
            storage: repeat(if bit { !0 } else { 0 }).take(nblocks).collect(),
            nbits: nbits
        };
        bit_vec.fix_last_block();
        bit_vec
    }

    /// Retrieves the value at index `i`, or `None` if the index is out of bounds.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    ///
    /// let bv = BitVec::from_bytes(&[0b01100000]);
    /// assert_eq!(bv.get(0), Some(false));
    /// assert_eq!(bv.get(1), Some(true));
    /// assert_eq!(bv.get(100), None);
    ///
    /// // Can also use array indexing
    /// assert_eq!(bv[1], true);
    /// ```
    #[inline]
    pub fn get(&self, i: usize) -> Option<bool> {
        if i >= self.nbits {
            return None;
        }
        let w = i / BITS;
        let b = i % BITS;
        self.storage.get(w).map(|&block|
            (block & (1 << b)) != 0
        )
    }

    /// Sets the value of a bit at an index `i`.
    ///
    /// # Panics
    ///
    /// Panics if `i` is out of bounds.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    ///
    /// let mut bv = BitVec::from_elem(5, false);
    /// bv.set(3, true);
    /// assert_eq!(bv[3], true);
    /// ```
    #[inline]
    pub fn set(&mut self, i: usize, x: bool) {
        assert!(i < self.nbits);
        unsafe {
            self.set_unchecked(i, x)
        }
    }

    #[inline]
    pub unsafe fn set_unchecked(&mut self, i: usize, x: bool) {
        //
        let w = i / BITS;
        let b = i % BITS;
        let flag = 1 << b;
        let ptr = self.storage.get_unchecked_mut(w);
        let val = if x { *ptr | flag } else { *ptr & !flag };
        *ptr = val;
    }

    /// Sets all bits to 1.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    ///
    /// let before = 0b01100000;
    /// let after  = 0b11111111;
    ///
    /// let mut bv = BitVec::from_bytes(&[before]);
    /// bv.set_all();
    /// assert_eq!(bv, BitVec::from_bytes(&[after]));
    /// ```
    #[inline]
    pub fn set_all(&mut self) {
        for w in &mut self.storage { *w = !0; }
        self.fix_last_block();
    }

    /// Returns an iterator over the elements of the vector in order.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    ///
    /// let bv = BitVec::from_bytes(&[0b01110100, 0b10010010]);
    /// assert_eq!(bv.iter().filter(|x| *x).count(), 7);
    /// ```
    #[inline]
    pub fn iter(&self) -> Iter {
        Iter { bit_vec: self, next_idx: 0, end_idx: self.nbits }
    }

    /// Returns the total number of bits in this vector
    #[inline]
    pub fn len(&self) -> usize { self.nbits }

    /// Returns true if there are no bits in this vector
    #[inline]
    pub fn is_empty(&self) -> bool { self.len() == 0 }

    /// Clears all bits in this vector.
    #[inline]
    pub fn clear(&mut self) {
        for w in &mut self.storage { *w = 0; }
    }
}

impl Default for BitVec {
    #[inline]
    fn default() -> BitVec { BitVec::new() }
}
impl Clone for BitVec {
    #[inline]
    fn clone(&self) -> BitVec {
        BitVec { storage: self.storage.clone(), nbits: self.nbits }
    }

    #[inline]
    fn clone_from(&mut self, source: &BitVec) {
        self.nbits = source.nbits;
        self.storage.clone_from(&source.storage);
    }
}

impl fmt::Debug for BitVec {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for bit in self {
            try!(write!(fmt, "{}", if bit { 1 } else { 0 }));
        }
        Ok(())
    }
}

impl hash::Hash for BitVec {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        self.nbits.hash(state);
        for elem in self.storage.iter().cloned() {
            elem.hash(state);
        }
    }
}

impl cmp::PartialEq for BitVec {
    #[inline]
    fn eq(&self, other: &BitVec) -> bool {
        if self.nbits != other.nbits {
            return false;
        }
        self.storage.iter().cloned().zip(other.storage.iter().cloned()).all(|(w1, w2)| w1 == w2)
    }
}

impl cmp::Eq for BitVec {}

/// An iterator for `BitVec`.
#[derive(Clone)]
pub struct Iter<'a> {
    bit_vec: &'a BitVec,
    next_idx: usize,
    end_idx: usize,
}

impl<'a> Iterator for Iter<'a> {
    type Item = bool;

    #[inline]
    fn next(&mut self) -> Option<bool> {
        if self.next_idx != self.end_idx {
            let idx = self.next_idx;
            self.next_idx += 1;
            Some(self.bit_vec[idx])
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let rem = self.end_idx - self.next_idx;
        (rem, Some(rem))
    }
}

impl<'a> DoubleEndedIterator for Iter<'a> {
    #[inline]
    fn next_back(&mut self) -> Option<bool> {
        if self.next_idx != self.end_idx {
            self.end_idx -= 1;
            Some(self.bit_vec[self.end_idx])
        } else {
            None
        }
    }
}

impl<'a> ExactSizeIterator for Iter<'a> {}

impl<'a> IntoIterator for &'a BitVec {
    type Item = bool;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}
<commit_msg>bit: expose internals directly.<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.

//! A very simple bit-vector that serves the needs of `primal`.

use std::cmp;
use std::fmt;
use std::hash;
use std::iter::repeat;
use std::ops::Index;

const BITS: usize = 64;

static TRUE: bool = true;
static FALSE: bool = false;

/// The bitvector type.
pub struct BitVec {
    /// Internal representation of the bit vector
    storage: Vec<u64>,
    /// The number of valid bits in the internal representation
    nbits: usize
}

impl Index<usize> for BitVec {
    type Output = bool;

    #[inline]
    fn index(&self, i: usize) -> &bool {
        if self.get(i).expect("index out of bounds") {
            &TRUE
        } else {
            &FALSE
        }
    }
}

impl BitVec {
    /// An operation might screw up the unused bits in the last block of the
    /// `BitVec`. As per (3), it's assumed to be all 0s. This method fixes it up.
    fn fix_last_block(&mut self) {
        let extra_bits = self.len() % BITS;
        if extra_bits > 0 {
            let mask = (1 << extra_bits) - 1;
            let storage_len = self.storage.len();
            self.storage[storage_len - 1] &= mask;
        }
    }

    /// Creates an empty `BitVec`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    /// let mut bv = BitVec::new();
    /// ```
    pub fn new() -> BitVec {
        BitVec { storage: Vec::new(), nbits: 0 }
    }

    #[inline]
    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
        unsafe {
            std::slice::from_raw_parts_mut(self.storage.as_mut_ptr() as *mut _,
                                           (self.nbits + 7) / 8)
        }
    }
    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        unsafe {
            std::slice::from_raw_parts(self.storage.as_ptr() as *const _,
                                       (self.nbits + 7) / 8)
        }
    }

    #[inline]
    pub fn as_u64s(&self) -> &[u64] {
        &self.storage
    }

    /// Creates a `BitVec` that holds `nbits` elements, setting each element
    /// to `bit`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    ///
    /// let mut bv = BitVec::from_elem(10, false);
    /// assert_eq!(bv.len(), 10);
    /// for x in bv.iter() {
    ///     assert_eq!(x, false);
    /// }
    /// ```
    pub fn from_elem(nbits: usize, bit: bool) -> BitVec {
        let nblocks = nbits.checked_add(BITS - 1).expect("capacity overflow") / BITS;
        let mut bit_vec = BitVec {
            storage: repeat(if bit { !0 } else { 0 }).take(nblocks).collect(),
            nbits: nbits
        };
        bit_vec.fix_last_block();
        bit_vec
    }

    /// Retrieves the value at index `i`, or `None` if the index is out of bounds.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    ///
    /// let bv = BitVec::from_bytes(&[0b01100000]);
    /// assert_eq!(bv.get(0), Some(false));
    /// assert_eq!(bv.get(1), Some(true));
    /// assert_eq!(bv.get(100), None);
    ///
    /// // Can also use array indexing
    /// assert_eq!(bv[1], true);
    /// ```
    #[inline]
    pub fn get(&self, i: usize) -> Option<bool> {
        if i >= self.nbits {
            return None;
        }
        let w = i / BITS;
        let b = i % BITS;
        self.storage.get(w).map(|&block|
            (block & (1 << b)) != 0
        )
    }

    /// Sets the value of a bit at an index `i`.
    ///
    /// # Panics
    ///
    /// Panics if `i` is out of bounds.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    ///
    /// let mut bv = BitVec::from_elem(5, false);
    /// bv.set(3, true);
    /// assert_eq!(bv[3], true);
    /// ```
    #[inline]
    pub fn set(&mut self, i: usize, x: bool) {
        assert!(i < self.nbits);
        unsafe {
            self.set_unchecked(i, x)
        }
    }

    #[inline]
    pub unsafe fn set_unchecked(&mut self, i: usize, x: bool) {
        //
        let w = i / BITS;
        let b = i % BITS;
        let flag = 1 << b;
        let ptr = self.storage.get_unchecked_mut(w);
        let val = if x { *ptr | flag } else { *ptr & !flag };
        *ptr = val;
    }

    /// Sets all bits to 1.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    ///
    /// let before = 0b01100000;
    /// let after  = 0b11111111;
    ///
    /// let mut bv = BitVec::from_bytes(&[before]);
    /// bv.set_all();
    /// assert_eq!(bv, BitVec::from_bytes(&[after]));
    /// ```
    #[inline]
    pub fn set_all(&mut self) {
        for w in &mut self.storage { *w = !0; }
        self.fix_last_block();
    }

    /// Returns an iterator over the elements of the vector in order.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use std::collections::BitVec;
    ///
    /// let bv = BitVec::from_bytes(&[0b01110100, 0b10010010]);
    /// assert_eq!(bv.iter().filter(|x| *x).count(), 7);
    /// ```
    #[inline]
    pub fn iter(&self) -> Iter {
        Iter { bit_vec: self, next_idx: 0, end_idx: self.nbits }
    }

    /// Returns the total number of bits in this vector
    #[inline]
    pub fn len(&self) -> usize { self.nbits }

    /// Returns true if there are no bits in this vector
    #[inline]
    pub fn is_empty(&self) -> bool { self.len() == 0 }

    /// Clears all bits in this vector.
    #[inline]
    pub fn clear(&mut self) {
        for w in &mut self.storage { *w = 0; }
    }
}

impl Default for BitVec {
    #[inline]
    fn default() -> BitVec { BitVec::new() }
}
impl Clone for BitVec {
    #[inline]
    fn clone(&self) -> BitVec {
        BitVec { storage: self.storage.clone(), nbits: self.nbits }
    }

    #[inline]
    fn clone_from(&mut self, source: &BitVec) {
        self.nbits = source.nbits;
        self.storage.clone_from(&source.storage);
    }
}

impl fmt::Debug for BitVec {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for bit in self {
            try!(write!(fmt, "{}", if bit { 1 } else { 0 }));
        }
        Ok(())
    }
}

impl hash::Hash for BitVec {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        self.nbits.hash(state);
        for elem in self.storage.iter().cloned() {
            elem.hash(state);
        }
    }
}

impl cmp::PartialEq for BitVec {
    #[inline]
    fn eq(&self, other: &BitVec) -> bool {
        if self.nbits != other.nbits {
            return false;
        }
        self.storage.iter().cloned().zip(other.storage.iter().cloned()).all(|(w1, w2)| w1 == w2)
    }
}

impl cmp::Eq for BitVec {}

/// An iterator for `BitVec`.
#[derive(Clone)]
pub struct Iter<'a> {
    bit_vec: &'a BitVec,
    next_idx: usize,
    end_idx: usize,
}

impl<'a> Iterator for Iter<'a> {
    type Item = bool;

    #[inline]
    fn next(&mut self) -> Option<bool> {
        if self.next_idx != self.end_idx {
            let idx = self.next_idx;
            self.next_idx += 1;
            Some(self.bit_vec[idx])
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let rem = self.end_idx - self.next_idx;
        (rem, Some(rem))
    }
}

impl<'a> DoubleEndedIterator for Iter<'a> {
    #[inline]
    fn next_back(&mut self) -> Option<bool> {
        if self.next_idx != self.end_idx {
            self.end_idx -= 1;
            Some(self.bit_vec[self.end_idx])
        } else {
            None
        }
    }
}

impl<'a> ExactSizeIterator for Iter<'a> {}

impl<'a> IntoIterator for &'a BitVec {
    type Item = bool;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}
<|endoftext|>


<commit_before>use std::{iter, ptr};
use std::sync::atomic::{AtomicPtr, Ordering};

#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum HashEntryType {
  Alpha,
  Exact,
  Beta
}

#[repr(packed)]
#[derive(Clone, PartialEq, Debug)]
pub struct HashEntry {
  hash: u64,
  depth: u8,
  entry_type: HashEntryType,
  pos: u16,
  estimation: i32
}

pub struct HashTable {
  entries: Vec<AtomicPtr<HashEntry>>
}

impl HashTable {
  #[inline]
  fn index(length: usize, hash: u64) -> usize {
    (hash % (length as u64)) as usize
  }

  pub fn new(length: usize) -> HashTable {
    HashTable {
      entries: iter::repeat(ptr::null_mut()).map(AtomicPtr::new).take(length).collect()
    }
  }

  pub fn len(&self) -> usize {
    self.entries.len()
  }

  #[cfg_attr(feature="clippy", allow(if_same_then_else))]
  fn choose_best(entry1: Box<HashEntry>, entry2: Box<HashEntry>) -> Box<HashEntry> {
    if entry1.entry_type != HashEntryType::Alpha && entry2.entry_type == HashEntryType::Alpha {
      entry1
    } else if entry1.entry_type == HashEntryType::Alpha && entry2.entry_type != HashEntryType::Alpha {
      entry2
    } else if entry1.depth > entry2.depth {
      entry1
    } else if entry1.depth < entry2.depth {
      entry2
    } else if entry1.estimation > entry2.estimation {
      entry1
    } else {
      entry2
    }
  }

  fn insert_to(&self, idx: usize, mut entry: Box<HashEntry>, mut prefer_new: bool) {
    loop {
      let entry_ptr = Box::into_raw(entry);
      if self.entries[idx].compare_and_swap(ptr::null_mut(), entry_ptr, Ordering::Relaxed).is_null() {
        return;
      }
      let cur_entry_ptr = self.entries[idx].swap(ptr::null_mut(), Ordering::Relaxed);
      if cur_entry_ptr.is_null() {
        entry = unsafe {
          Box::from_raw(entry_ptr)
        };
        continue;
      }
      let cur_entry = unsafe {
        Box::from_raw(cur_entry_ptr)
      };
      let new_entry = unsafe {
        Box::from_raw(entry_ptr)
      };
      if cur_entry.hash != new_entry.hash {
        entry = if prefer_new { new_entry } else { cur_entry };
      } else {
        entry = HashTable::choose_best(cur_entry, new_entry);
      }
      prefer_new = false;
    }
  }

  pub fn insert(&self, entry: Box<HashEntry>) {
    let idx = HashTable::index(self.len(), entry.hash);
    self.insert_to(idx, entry, true);
  }

  pub fn get(&self, hash: u64) -> Option<HashEntry> {
    let idx = HashTable::index(self.len(), hash);
    let entry_ptr = self.entries[idx].swap(ptr::null_mut(), Ordering::Relaxed);
    if entry_ptr.is_null() {
      return None;
    }
    let entry = unsafe {
      Box::from_raw(entry_ptr)
    };
    if entry.hash == hash {
      let cloned = (*entry).clone();
      self.insert_to(idx, entry, false);
      Some(cloned)
    } else {
      self.insert_to(idx, entry, false);
      None
    }
  }
}

pub struct HashTablePair<'l> {
  pub cur_hash_table: &'l HashTable,
  pub enemy_hash_table: &'l HashTable
}

impl<'l> HashTablePair<'l> {
  pub fn swap(&self) -> HashTablePair<'l> {
    HashTablePair {
      cur_hash_table: self.enemy_hash_table,
      enemy_hash_table: self.cur_hash_table
    }
  }
}
<commit_msg>Update hash table.<commit_after>use std::{iter, ptr};
use std::sync::atomic::{AtomicPtr, Ordering};

#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum HashEntryType {
  Alpha,
  Exact,
  Beta
}

#[repr(packed)]
#[derive(Clone, PartialEq, Debug)]
pub struct HashEntry {
  hash: u64,
  depth: u8,
  entry_type: HashEntryType,
  pos: u16,
  estimation: i32
}

#[derive(Debug)]
pub struct HashTable {
  entries: Vec<AtomicPtr<HashEntry>>
}

impl HashTable {
  #[inline]
  fn index(length: usize, hash: u64) -> usize {
    (hash % (length as u64)) as usize
  }

  pub fn new(length: usize) -> HashTable {
    HashTable {
      entries: iter::repeat(ptr::null_mut()).map(AtomicPtr::new).take(length).collect()
    }
  }

  pub fn len(&self) -> usize {
    self.entries.len()
  }

  #[cfg_attr(feature="clippy", allow(if_same_then_else))]
  fn choose_best(entry1: Box<HashEntry>, entry2: Box<HashEntry>) -> Box<HashEntry> {
    if entry1.entry_type != HashEntryType::Alpha && entry2.entry_type == HashEntryType::Alpha {
      entry1
    } else if entry1.entry_type == HashEntryType::Alpha && entry2.entry_type != HashEntryType::Alpha {
      entry2
    } else if entry1.depth > entry2.depth {
      entry1
    } else if entry1.depth < entry2.depth {
      entry2
    } else if entry1.estimation > entry2.estimation {
      entry1
    } else {
      entry2
    }
  }

  fn insert_to(&self, idx: usize, mut entry: Box<HashEntry>, mut prefer_new: bool) {
    loop {
      let entry_ptr = Box::into_raw(entry);
      if self.entries[idx].compare_and_swap(ptr::null_mut(), entry_ptr, Ordering::Relaxed).is_null() {
        return;
      }
      let cur_entry_ptr = self.entries[idx].swap(ptr::null_mut(), Ordering::Relaxed);
      if cur_entry_ptr.is_null() {
        entry = unsafe {
          Box::from_raw(entry_ptr)
        };
        continue;
      }
      let cur_entry = unsafe {
        Box::from_raw(cur_entry_ptr)
      };
      let new_entry = unsafe {
        Box::from_raw(entry_ptr)
      };
      if cur_entry.hash != new_entry.hash {
        entry = if prefer_new { new_entry } else { cur_entry };
      } else {
        entry = HashTable::choose_best(cur_entry, new_entry);
      }
      prefer_new = false;
    }
  }

  pub fn insert(&self, entry: Box<HashEntry>) {
    let idx = HashTable::index(self.len(), entry.hash);
    self.insert_to(idx, entry, true);
  }

  pub fn get(&self, hash: u64) -> Option<HashEntry> {
    let idx = HashTable::index(self.len(), hash);
    let entry_ptr = self.entries[idx].swap(ptr::null_mut(), Ordering::Relaxed);
    if entry_ptr.is_null() {
      return None;
    }
    let entry = unsafe {
      Box::from_raw(entry_ptr)
    };
    if entry.hash == hash {
      let cloned = (*entry).clone();
      self.insert_to(idx, entry, false);
      Some(cloned)
    } else {
      self.insert_to(idx, entry, false);
      None
    }
  }
}

#[derive(Clone, Debug)]
pub struct HashTablePair<'l> {
  pub cur_hash_table: &'l HashTable,
  pub enemy_hash_table: &'l HashTable
}

impl<'l> HashTablePair<'l> {
  pub fn swap(&self) -> HashTablePair<'l> {
    HashTablePair {
      cur_hash_table: self.enemy_hash_table,
      enemy_hash_table: self.cur_hash_table
    }
  }
}
<|endoftext|>


<commit_before>// Copyright 2017 PingCAP, Inc.
//
// Licensed 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,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gcc;
extern crate cmake;
extern crate pkg_config;

const GRPC_VERSION: &'static str = "1.2.5";

#[cfg(not(feature = "static-link"))]
mod imp {
    use super::GRPC_VERSION;

    pub fn build_or_link_grpc(cc: &mut ::gcc::Config) {
        if let Ok(lib) = ::pkg_config::Config::new()
            .atleast_version(GRPC_VERSION)
            .statik(true)
            .probe("grpc_unsecure") {
            for inc_path in lib.include_paths {
                cc.include(inc_path);
            }
        } else {
            panic!("can't find dynamic grpc library");
        }
    }
}

#[cfg(feature = "static-link")]
mod imp {
    use std::path::Path;
    use std::process::Command;

    use super::GRPC_VERSION;

    const ZLIB_VERSION: &'static str = "1.2.8";
    const BORINGSSL_GIT_HASH: &'static str = "78684e5b222645828ca302e56b40b9daff2b2d27";

    fn wget(url: &str, out: &str) -> Result<(), String> {
        Command::new("wget")
            .args(&["-q", "-c", "-O", out, url])
            .status()
            .map_err(|err| format!("wget execute failed: {}", err))
            .and_then(|status| {
                if status.success() {
                    Ok(())
                } else {
                    Err(format!("wget exit with {}", status))
                }
            })
    }

    fn tar_xf(file: &str) -> Result<(), String> {
        Command::new("tar")
            .args(&["zxf", file])
            .status()
            .map_err(|err| format!("tar execute failed: {}", err))
            .and_then(|status| {
                if status.success() {
                    Ok(())
                } else {
                    Err(format!("tar exit with {}", status))
                }
            })
    }

    pub fn build_or_link_grpc(cc: &mut ::gcc::Config) {

        if !Path::new(&format!("grpc-{}", GRPC_VERSION)).exists() {
            wget(&format!("https://github.com/grpc/grpc/archive/v{}.tar.gz",
                          GRPC_VERSION),
                 "grpc.tar.gz")
                .and_then(|_| tar_xf("grpc.tar.gz"))
                .unwrap();
        }

        if !Path::new(&format!("zlib-{}", ZLIB_VERSION)).exists() {
            wget(&format!("https://github.com/madler/zlib/archive/v{}.tar.gz",
                          ZLIB_VERSION),
                 "zlib.tar.gz")
                .and_then(|_| tar_xf("zlib.tar.gz"))
                .unwrap();
        }

        if !Path::new(&format!("boringssl-{}", BORINGSSL_GIT_HASH)).exists() {
            wget(&format!("https://github.com/google/boringssl/archive/{}.tar.gz",
                          BORINGSSL_GIT_HASH),
                 "boringssl.tar.gz")
                .and_then(|_| tar_xf("boringssl.tar.gz"))
                .unwrap();
        }

        let dst = ::cmake::Config::new(format!("zlib-{}", ZLIB_VERSION)).build();

        println!("cargo:rustc-link-search=native={}/lib", dst.display());

        let dst = ::cmake::Config::new(format!("grpc-{}", GRPC_VERSION))
        .define("ZLIB_ROOT_DIR", format!("../zlib-{}", ZLIB_VERSION)) // relative to grpc dir
        .define("BORINGSSL_ROOT_DIR", format!("../boringssl-{}", BORINGSSL_GIT_HASH))
        .build_target("grpc_unsecure")
        .build();

        cc.include(format!("grpc-{}/include", GRPC_VERSION));

        println!("cargo:rustc-link-search=native={}/build", dst.display());
        println!("cargo:rustc-link-search=native={}/build/third_party/zlib",
                 dst.display());

        println!("cargo:rustc-link-lib=static=z");
        println!("cargo:rustc-link-lib=static=gpr");
        println!("cargo:rustc-link-lib=static=grpc_unsecure");
    }
}

fn main() {
    println!("cargo:rerun-if-changed=build.rs");

    let mut cc = ::gcc::Config::new();

    imp::build_or_link_grpc(&mut cc);

    cc.file("grpc_wrap.c")
        .flag("-fPIC")
        .flag("-O2")
        .compile("libgrpc_wrap.a");
}
<commit_msg>grpc-sys: update to grpc 1.3.0<commit_after>// Copyright 2017 PingCAP, Inc.
//
// Licensed 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,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gcc;
extern crate cmake;
extern crate pkg_config;

const GRPC_VERSION: &'static str = "1.3.0";

#[cfg(not(feature = "static-link"))]
mod imp {
    use super::GRPC_VERSION;

    pub fn build_or_link_grpc(cc: &mut ::gcc::Config) {
        if let Ok(lib) = ::pkg_config::Config::new()
            .atleast_version(GRPC_VERSION)
            .statik(true)
            .probe("grpc_unsecure") {
            for inc_path in lib.include_paths {
                cc.include(inc_path);
            }
        } else {
            panic!("can't find dynamic grpc library");
        }
    }
}

#[cfg(feature = "static-link")]
mod imp {
    use std::env;
    use std::fs::DirBuilder;
    use std::path::{Path, PathBuf};
    use std::process::Command;

    use super::GRPC_VERSION;

    const ZLIB_VERSION: &'static str = "1.2.8";
    const BORINGSSL_GIT_HASH: &'static str = "78684e5b222645828ca302e56b40b9daff2b2d27";
    const CARES_VERSION: &'static str = "cares-1_12_0";

    fn wget(url: &str, out: &str) -> Result<(), String> {
        Command::new("wget")
            .args(&["-q", "-c", "-O", out, url])
            .status()
            .map_err(|err| format!("wget execute failed: {}", err))
            .and_then(|status| {
                if status.success() {
                    Ok(())
                } else {
                    Err(format!("wget exit with {}", status))
                }
            })
    }

    fn tar_xf(file: &str) -> Result<(), String> {
        Command::new("tar")
            .args(&["zxf", file])
            .status()
            .map_err(|err| format!("tar execute failed: {}", err))
            .and_then(|status| {
                if status.success() {
                    Ok(())
                } else {
                    Err(format!("tar exit with {}", status))
                }
            })
    }

    pub fn build_or_link_grpc(cc: &mut ::gcc::Config) {
        let cur_dir = env::current_dir().expect("Can't access current working directory");
        let crate_dir = cur_dir.as_path().display();

        let out_dir = env::var("OUT_DIR").expect("Can't access OUT_DIR");
        for sub_dir in &["grpc", "zlib", "ssl", "cares"] {
            DirBuilder::new().recursive(true).create(format!("{}/{}", out_dir, sub_dir)).unwrap();
        }

        if !Path::new(&format!("grpc-{}", GRPC_VERSION)).exists() {
            wget(&format!("https://github.com/grpc/grpc/archive/v{}.tar.gz",
                          GRPC_VERSION),
                 "grpc.tar.gz")
                .and_then(|_| tar_xf("grpc.tar.gz"))
                .unwrap();
        }

        if !Path::new(&format!("zlib-{}", ZLIB_VERSION)).exists() {
            wget(&format!("https://github.com/madler/zlib/archive/v{}.tar.gz",
                          ZLIB_VERSION),
                 "zlib.tar.gz")
                .and_then(|_| tar_xf("zlib.tar.gz"))
                .unwrap();
        }

        if !Path::new(&format!("boringssl-{}", BORINGSSL_GIT_HASH)).exists() {
            wget(&format!("https://github.com/google/boringssl/archive/{}.tar.gz",
                          BORINGSSL_GIT_HASH),
                 "boringssl.tar.gz")
                .and_then(|_| tar_xf("boringssl.tar.gz"))
                .unwrap();
        }

        let cares_path = PathBuf::from(format!("grpc-{}/third_party/cares/cares", GRPC_VERSION));
        if !cares_path.with_file_name("README.md").exists() {
            wget(&format!("https://github.com/c-ares/c-ares/archive/{}.tar.gz",
                          CARES_VERSION),
                 "c-ares.tar.gz")
                .and_then(|_| tar_xf("c-ares.tar.gz"))
                .unwrap();
            let _ = Command::new("rm")
                .current_dir(&format!("c-ares-{}/", CARES_VERSION))
                .args(&["acountry.c", "adig.c", "ahost.c"])
                .status();
            Command::new("cp")
                .args(&["-a", &format!("c-ares-{}/", CARES_VERSION), cares_path.to_str().unwrap()])
                .status()
                .map_err(|err| format!("cp execute failed: {}", err))
                .and_then(|status| {
                    if status.success() {
                        Ok(())
                    } else {
                        Err(format!("cp -a exit with {}", status))
                    }
                })
                .unwrap();
        }

        let zlib_dst = ::cmake::Config::new(format!("zlib-{}", ZLIB_VERSION))
            .out_dir(format!("{}/{}", out_dir, "zlib"))
            .build();

        println!("cargo:rustc-link-search=native={}/build",
                 zlib_dst.display());

        let cares_dst = ::cmake::Config::new(format!("grpc-{}/src/c-ares", GRPC_VERSION))
            .out_dir(format!("{}/{}", out_dir, "cares"))
            .build_target("cares")
            .build();

        println!("cargo:rustc-link-search=native={}/build",
                 cares_dst.display());

        let dst = ::cmake::Config::new(format!("grpc-{}", GRPC_VERSION))
            .define("ZLIB_ROOT_DIR",
                    format!("{}/zlib-{}", crate_dir, ZLIB_VERSION))
            .define("BORINGSSL_ROOT_DIR",
                    format!("{}/boringssl-{}", crate_dir, BORINGSSL_GIT_HASH))
            .out_dir(format!("{}/{}", out_dir, "grpc"))
            .build_target("grpc_unsecure")
            .build();

        println!("cargo:rustc-link-search=native={}/build", dst.display());

        println!("cargo:rustc-link-lib=static=z");
        println!("cargo:rustc-link-lib=static=cares");
        println!("cargo:rustc-link-lib=static=gpr");
        println!("cargo:rustc-link-lib=static=grpc_unsecure");

        cc.include(format!("grpc-{}/include", GRPC_VERSION));
    }
}

fn main() {
    let mut cc = ::gcc::Config::new();

    imp::build_or_link_grpc(&mut cc);

    cc.file("grpc_wrap.c")
        .flag("-fPIC")
        .flag("-O2")
        .compile("libgrpc_wrap.a");
}
<|endoftext|>


<commit_before>//! The module contains the implementation of HTTP/2 frames.

/// An alias for the 9-byte buffer that each HTTP/2 frame header must be stored
/// in.
type FrameHeaderBuffer = [u8; 9];
/// An alias for the 4-tuple representing the components of each HTTP/2 frame
/// header.
type FrameHeader = (u32, u8, u8, u32);

/// Deconstructs a `FrameHeader` into its corresponding 4 components,
/// represented as a 4-tuple: `(length, frame_type, flags, stream_id)`.
///
/// The frame `type` and `flags` components are returned as their original
/// octet representation, rather than reinterpreted.
fn unpack_header(header: &FrameHeaderBuffer) -> FrameHeader {
    let length: u32 = (
        ((header[0] as u32) << 16) |
        ((header[1] as u32) <<  8) |
        ((header[2] as u32) <<  0));
    let frame_type = header[3];
    let flags = header[4];
    let stream_id: u32 = (
        ((header[5] as u32) << 24) |
        ((header[6] as u32) << 16) |
        ((header[7] as u32) <<  8) |
        ((header[8] as u32) <<  0));

    (length, frame_type, flags, stream_id)
}

fn pack_header(header: &FrameHeader) -> FrameHeaderBuffer {
    let &(length, frame_type, flags, stream_id) = header;

    [
        (((length >> 16) & 0x000000FF) as u8),
        (((length >>  8) & 0x000000FF) as u8),
        (((length >>  0) & 0x000000FF) as u8),
        frame_type,
        flags,
        (((stream_id >> 24) & 0x000000FF) as u8),
        (((stream_id >> 16) & 0x000000FF) as u8),
        (((stream_id >>  8) & 0x000000FF) as u8),
        (((stream_id >>  0) & 0x000000FF) as u8),
    ]
}

#[cfg(test)]
mod tests {
    use super::{
        unpack_header,
        pack_header
    };

    /// Tests that the `unpack_header` function correctly returns the
    /// components of HTTP/2 frame headers.
    #[test]
    fn test_unpack_header() {
        {
            let header = [0; 9];
            assert_eq!((0, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 2, 3, 0, 0, 0, 4];
            assert_eq!((1, 2, 3, 4), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 200, 100, 0, 0, 0, 4];
            assert_eq!((1, 200, 100, 4), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 0, 0, 0, 0, 0, 0];
            assert_eq!((1, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 1, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!((256, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [1, 0, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!((256 * 256, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 0, 0, 0, 0, 0, 0, 0, 1];
            assert_eq!((0, 0, 0, 1), unpack_header(&header));
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1];
            assert_eq!(((1 << 24) - 1, 0, 0, 1), unpack_header(&header));
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 1, 1, 1, 1];
            assert_eq!(
                ((1 << 24) - 1, 0, 0, 1 + (1 << 8) + (1 << 16) + (1 << 24)),
                unpack_header(&header));
        }
    }

    #[test]
    fn test_pack_header() {
        {
            let header = [0; 9];
            assert_eq!(pack_header(&(0, 0, 0, 0)), header);
        }
        {
            let header = [0, 0, 1, 2, 3, 0, 0, 0, 4];
            assert_eq!(pack_header(&(1, 2, 3, 4)), header);
        }
        {
            let header = [0, 0, 1, 200, 100, 0, 0, 0, 4];
            assert_eq!(pack_header(&(1, 200, 100, 4)), header);
        }
        {
            let header = [0, 0, 1, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(1, 0, 0, 0)), header);
        }
        {
            let header = [0, 1, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(256, 0, 0, 0)), header);
        }
        {
            let header = [1, 0, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(256 * 256, 0, 0, 0)), header);
        }
        {
            let header = [0, 0, 0, 0, 0, 0, 0, 0, 1];
            assert_eq!(pack_header(&(0, 0, 0, 1)), header);
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1];
            assert_eq!(pack_header(&((1 << 24) - 1, 0, 0, 1)), header);
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 1, 1, 1, 1];
            let header_components = (
                (1 << 24) - 1, 0, 0, 1 + (1 << 8) + (1 << 16) + (1 << 24)
            );
            assert_eq!(pack_header(&header_components), header);
        }
    }
}
<commit_msg>Add missing function docs<commit_after>//! The module contains the implementation of HTTP/2 frames.

/// An alias for the 9-byte buffer that each HTTP/2 frame header must be stored
/// in.
type FrameHeaderBuffer = [u8; 9];
/// An alias for the 4-tuple representing the components of each HTTP/2 frame
/// header.
type FrameHeader = (u32, u8, u8, u32);

/// Deconstructs a `FrameHeader` into its corresponding 4 components,
/// represented as a 4-tuple: `(length, frame_type, flags, stream_id)`.
///
/// The frame `type` and `flags` components are returned as their original
/// octet representation, rather than reinterpreted.
fn unpack_header(header: &FrameHeaderBuffer) -> FrameHeader {
    let length: u32 = (
        ((header[0] as u32) << 16) |
        ((header[1] as u32) <<  8) |
        ((header[2] as u32) <<  0));
    let frame_type = header[3];
    let flags = header[4];
    let stream_id: u32 = (
        ((header[5] as u32) << 24) |
        ((header[6] as u32) << 16) |
        ((header[7] as u32) <<  8) |
        ((header[8] as u32) <<  0));

    (length, frame_type, flags, stream_id)
}

/// Constructs a buffer of 9 bytes that represents the given `FrameHeader`.
fn pack_header(header: &FrameHeader) -> FrameHeaderBuffer {
    let &(length, frame_type, flags, stream_id) = header;

    [
        (((length >> 16) & 0x000000FF) as u8),
        (((length >>  8) & 0x000000FF) as u8),
        (((length >>  0) & 0x000000FF) as u8),
        frame_type,
        flags,
        (((stream_id >> 24) & 0x000000FF) as u8),
        (((stream_id >> 16) & 0x000000FF) as u8),
        (((stream_id >>  8) & 0x000000FF) as u8),
        (((stream_id >>  0) & 0x000000FF) as u8),
    ]
}

#[cfg(test)]
mod tests {
    use super::{
        unpack_header,
        pack_header
    };

    /// Tests that the `unpack_header` function correctly returns the
    /// components of HTTP/2 frame headers.
    #[test]
    fn test_unpack_header() {
        {
            let header = [0; 9];
            assert_eq!((0, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 2, 3, 0, 0, 0, 4];
            assert_eq!((1, 2, 3, 4), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 200, 100, 0, 0, 0, 4];
            assert_eq!((1, 200, 100, 4), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 0, 0, 0, 0, 0, 0];
            assert_eq!((1, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 1, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!((256, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [1, 0, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!((256 * 256, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 0, 0, 0, 0, 0, 0, 0, 1];
            assert_eq!((0, 0, 0, 1), unpack_header(&header));
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1];
            assert_eq!(((1 << 24) - 1, 0, 0, 1), unpack_header(&header));
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 1, 1, 1, 1];
            assert_eq!(
                ((1 << 24) - 1, 0, 0, 1 + (1 << 8) + (1 << 16) + (1 << 24)),
                unpack_header(&header));
        }
    }

    /// Tests that the `pack_header` function correctly returns the buffer
    /// corresponding to components of HTTP/2 frame headers.
    #[test]
    fn test_pack_header() {
        {
            let header = [0; 9];
            assert_eq!(pack_header(&(0, 0, 0, 0)), header);
        }
        {
            let header = [0, 0, 1, 2, 3, 0, 0, 0, 4];
            assert_eq!(pack_header(&(1, 2, 3, 4)), header);
        }
        {
            let header = [0, 0, 1, 200, 100, 0, 0, 0, 4];
            assert_eq!(pack_header(&(1, 200, 100, 4)), header);
        }
        {
            let header = [0, 0, 1, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(1, 0, 0, 0)), header);
        }
        {
            let header = [0, 1, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(256, 0, 0, 0)), header);
        }
        {
            let header = [1, 0, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(256 * 256, 0, 0, 0)), header);
        }
        {
            let header = [0, 0, 0, 0, 0, 0, 0, 0, 1];
            assert_eq!(pack_header(&(0, 0, 0, 1)), header);
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1];
            assert_eq!(pack_header(&((1 << 24) - 1, 0, 0, 1)), header);
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 1, 1, 1, 1];
            let header_components = (
                (1 << 24) - 1, 0, 0, 1 + (1 << 8) + (1 << 16) + (1 << 24)
            );
            assert_eq!(pack_header(&header_components), header);
        }
    }
}
<|endoftext|>


<commit_before>use crate::util::refined_tcp_stream::Stream as RefinedStream;
use std::error::Error;
use std::io::{Read, Write};
use std::net::{Shutdown, SocketAddr, TcpStream};
use std::sync::{Arc, Mutex};
use zeroize::Zeroizing;

pub(crate) struct OpenSslStream {
    inner: openssl::ssl::SslStream<TcpStream>,
}

/// An OpenSSL stream which has been split into two mutually exclusive streams (e.g. for read / write)
pub(crate) struct SplitOpenSslStream(Arc<Mutex<OpenSslStream>>);

// These struct methods form the implict contract for swappable TLS implementations
impl SplitOpenSslStream {
    pub(crate) fn peer_addr(&mut self) -> std::io::Result<SocketAddr> {
        self.0.lock().unwrap().inner.get_mut().peer_addr()
    }

    pub(crate) fn shutdown(&mut self, how: Shutdown) -> std::io::Result<()> {
        self.0.lock().unwrap().inner.get_mut().shutdown(how)
    }
}

impl Clone for SplitOpenSslStream {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl Read for SplitOpenSslStream {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.0.lock().unwrap().read(buf)
    }
}

impl Write for SplitOpenSslStream {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.0.lock().unwrap().write(buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.0.lock().unwrap().flush()
    }
}

impl Read for OpenSslStream {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.inner.read(buf)
    }
}

impl Write for OpenSslStream {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.inner.write(buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.inner.flush()
    }
}

pub(crate) struct OpenSslContext(openssl::ssl::SslContext);

impl OpenSslContext {
    pub fn from_pem(
        certificates: Vec<u8>,
        private_key: Zeroizing<Vec<u8>>,
    ) -> Result<Self, Box<dyn Error + Send + Sync>> {
        use openssl::pkey::PKey;
        use openssl::ssl::{self, SslVerifyMode};
        use openssl::x509::X509;

        let mut ctx = openssl::ssl::SslContext::builder(ssl::SslMethod::tls())?;
        ctx.set_cipher_list("DEFAULT")?;
        let cert = X509::from_pem(&certificates)?;
        ctx.set_certificate(&cert)?;
        let key = PKey::private_key_from_pem(&private_key)?;
        ctx.set_private_key(&key)?;
        ctx.set_verify(SslVerifyMode::NONE);
        ctx.check_private_key()?;

        Ok(Self(ctx.build()))
    }

    pub fn accept(
        &self,
        stream: TcpStream,
    ) -> Result<OpenSslStream, Box<dyn Error + Send + Sync + 'static>> {
        use openssl::ssl::Ssl;
        let session = Ssl::new(&self.0).expect("Failed to create new OpenSSL session");
        let stream = session.accept(stream)?;
        Ok(OpenSslStream { inner: stream })
    }
}

impl From<OpenSslStream> for RefinedStream {
    fn from(stream: OpenSslStream) -> Self {
        RefinedStream::Https(SplitOpenSslStream(Arc::new(Mutex::new(stream))))
    }
}
<commit_msg>Correctly load certificate chains into OpenSSL<commit_after>use crate::util::refined_tcp_stream::Stream as RefinedStream;
use std::error::Error;
use std::io::{Read, Write};
use std::net::{Shutdown, SocketAddr, TcpStream};
use std::sync::{Arc, Mutex};
use zeroize::Zeroizing;

pub(crate) struct OpenSslStream {
    inner: openssl::ssl::SslStream<TcpStream>,
}

/// An OpenSSL stream which has been split into two mutually exclusive streams (e.g. for read / write)
pub(crate) struct SplitOpenSslStream(Arc<Mutex<OpenSslStream>>);

// These struct methods form the implict contract for swappable TLS implementations
impl SplitOpenSslStream {
    pub(crate) fn peer_addr(&mut self) -> std::io::Result<SocketAddr> {
        self.0.lock().unwrap().inner.get_mut().peer_addr()
    }

    pub(crate) fn shutdown(&mut self, how: Shutdown) -> std::io::Result<()> {
        self.0.lock().unwrap().inner.get_mut().shutdown(how)
    }
}

impl Clone for SplitOpenSslStream {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl Read for SplitOpenSslStream {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.0.lock().unwrap().read(buf)
    }
}

impl Write for SplitOpenSslStream {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.0.lock().unwrap().write(buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.0.lock().unwrap().flush()
    }
}

impl Read for OpenSslStream {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.inner.read(buf)
    }
}

impl Write for OpenSslStream {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.inner.write(buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.inner.flush()
    }
}

pub(crate) struct OpenSslContext(openssl::ssl::SslContext);

impl OpenSslContext {
    pub fn from_pem(
        certificates: Vec<u8>,
        private_key: Zeroizing<Vec<u8>>,
    ) -> Result<Self, Box<dyn Error + Send + Sync>> {
        use openssl::pkey::PKey;
        use openssl::ssl::{self, SslVerifyMode};
        use openssl::x509::X509;

        let mut ctx = openssl::ssl::SslContext::builder(ssl::SslMethod::tls())?;
        ctx.set_cipher_list("DEFAULT")?;
        let certificate_chain = X509::stack_from_pem(&certificates)?;
        if certificate_chain.is_empty() {
            return Err("Couldn't extract certificate chain from config.".into());
        }
        // The leaf certificate must always be first in the PEM file
        ctx.set_certificate(&certificate_chain[0])?;
        for chain_cert in certificate_chain.into_iter().skip(1) {
            ctx.add_extra_chain_cert(chain_cert)?;
        }
        let key = PKey::private_key_from_pem(&private_key)?;
        ctx.set_private_key(&key)?;
        ctx.set_verify(SslVerifyMode::NONE);
        ctx.check_private_key()?;

        Ok(Self(ctx.build()))
    }

    pub fn accept(
        &self,
        stream: TcpStream,
    ) -> Result<OpenSslStream, Box<dyn Error + Send + Sync + 'static>> {
        use openssl::ssl::Ssl;
        let session = Ssl::new(&self.0).expect("Failed to create new OpenSSL session");
        let stream = session.accept(stream)?;
        Ok(OpenSslStream { inner: stream })
    }
}

impl From<OpenSslStream> for RefinedStream {
    fn from(stream: OpenSslStream) -> Self {
        RefinedStream::Https(SplitOpenSslStream(Arc::new(Mutex::new(stream))))
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use crate::firmware::{
    self,
    installation_set::{self, Set},
};
use chrono::{DateTime, NaiveDateTime, Utc};
use derive_more::{Deref, DerefMut, Display, Error, From};
use sdk::api::info::runtime_settings as api;
use slog_scope::{debug, warn};
use std::{fs, io, path::Path};

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Display, Error, From)]
pub enum Error {
    Io(io::Error),
    SerdeJson(serde_json::Error),
    FirmwareError(firmware::Error),

    #[display(fmt = "invalid runtime settings destination")]
    InvalidDestination,
}

#[derive(Clone, Debug, Deref, DerefMut, PartialEq)]
pub struct RuntimeSettings(pub api::RuntimeSettings);

impl Default for RuntimeSettings {
    fn default() -> Self {
        RuntimeSettings(api::RuntimeSettings {
            polling: api::RuntimePolling {
                last: DateTime::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc),
                retries: 0,
                now: false,
                server_address: api::ServerAddress::Default,
            },
            update: api::RuntimeUpdate { upgrade_to_installation: None, applied_package_uid: None },
            path: std::path::PathBuf::new(),
            persistent: false,
        })
    }
}

impl RuntimeSettings {
    pub fn load(path: &Path) -> Result<Self> {
        let mut this = if path.exists() {
            debug!("loading runtime settings from {:?}", path);
            match fs::read_to_string(path).map_err(Error::from).and_then(|ref s| Self::parse(s)) {
                Ok(v) => v,
                Err(e) => {
                    warn!("failed to load current runtime settings: {}", e);
                    let _ = fs::rename(
                        path,
                        path.with_file_name(format!(
                            "{}.old",
                            path.file_name().unwrap().to_str().unwrap()
                        )),
                    );
                    debug!("using default runtime settings");
                    Self::default()
                }
            }
        } else {
            debug!("runtime settings file {:?} does not exists, using default settings", path);
            Self::default()
        };

        this.path = path.to_path_buf();
        Ok(this)
    }

    fn parse(content: &str) -> Result<Self> {
        Ok(RuntimeSettings(serde_json::from_str::<api::RuntimeSettings>(&content)?))
    }

    fn save(&self) -> Result<()> {
        if !self.persistent {
            debug!("skipping runtime settings save, using non-persistent");
            return Ok(());
        }

        let parent = self.path.parent().ok_or(Error::InvalidDestination)?;
        if !parent.exists() {
            debug!("creating runtime settings to store state");
            fs::create_dir_all(parent)?;
        }

        fs::write(&self.path, self.serialize()?)?;
        debug!("saved runtime settings to {:?}", &self.path);

        Ok(())
    }

    fn serialize(&self) -> Result<String> {
        Ok(serde_json::to_string(&self.0)?)
    }

    pub(crate) fn get_inactive_installation_set(&self) -> Result<Set> {
        // If the `upgrade_to_installation` is defined, the current inactive
        // installation_set has already been swapped.
        if let Some(s) = self.update.upgrade_to_installation {
            return Ok(Set(s));
        }

        Ok(installation_set::inactive()?)
    }

    pub(crate) fn enable_persistency(&mut self) {
        self.persistent = true;
    }

    pub(crate) fn is_polling_forced(&self) -> bool {
        self.polling.now
    }

    pub(crate) fn disable_force_poll(&mut self) -> Result<()> {
        debug!("disabling foce poll");
        self.polling.now = false;
        self.save()
    }

    pub(crate) fn retries(&self) -> usize {
        self.polling.retries
    }

    pub(crate) fn inc_retries(&mut self) {
        self.polling.retries += 1;
    }

    pub(crate) fn clear_retries(&mut self) {
        self.polling.retries = 0;
    }

    pub(crate) fn last_polling(&self) -> DateTime<Utc> {
        self.polling.last
    }

    pub(crate) fn set_last_polling(&mut self, last_polling: DateTime<Utc>) -> Result<()> {
        debug!("updating last polling time");
        self.polling.last = last_polling;
        self.save()
    }

    pub(crate) fn applied_package_uid(&self) -> Option<String> {
        self.update.applied_package_uid.clone()
    }

    pub(crate) fn set_applied_package_uid(&mut self, applied_package_uid: &str) -> Result<()> {
        debug!("marking package {} as installed", applied_package_uid);
        self.update.applied_package_uid = Some(applied_package_uid.to_string());
        self.save()
    }

    pub(crate) fn set_upgrading_to(&mut self, new_install_set: Set) -> Result<()> {
        debug!("setting upgrading to {}", new_install_set);
        self.update.upgrade_to_installation = Some(new_install_set.0);
        self.save()
    }

    pub(crate) fn custom_server_address(&self) -> Option<&str> {
        match &self.polling.server_address {
            api::ServerAddress::Custom(s) => Some(s),
            api::ServerAddress::Default => None,
        }
    }

    pub(crate) fn set_custom_server_address(&mut self, server_address: &str) {
        self.polling.server_address = api::ServerAddress::Custom(server_address.to_owned());
    }

    /// Reset settings that are only need through a single installation
    pub(crate) fn reset_transient_settings(&mut self) {
        // Server address is reset so it doesn't keep probing the last custom server
        // requested
        self.polling.server_address = api::ServerAddress::Default;
    }

    pub(crate) fn reset_installation_settings(&mut self) -> Result<()> {
        debug!("reseting installation settings");
        self.update.upgrade_to_installation = None;
        self.update.applied_package_uid = None;

        // Ensure we do a probe as soon as possible so full update
        // cycle can be finished.
        self.polling.now = true;

        self.save()
    }
}

#[test]
fn default() {
    use pretty_assertions::assert_eq;
    let settings = RuntimeSettings::default();
    let expected = RuntimeSettings(api::RuntimeSettings {
        polling: api::RuntimePolling {
            last: DateTime::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc),
            retries: 0,
            now: false,
            server_address: api::ServerAddress::Default,
        },
        update: api::RuntimeUpdate { upgrade_to_installation: None, applied_package_uid: None },
        path: std::path::PathBuf::new(),
        persistent: false,
    });

    assert_eq!(Some(settings), Some(expected));
}

#[test]
fn load_and_save() {
    use pretty_assertions::assert_eq;
    use std::fs;
    use tempfile::NamedTempFile;

    let tempfile = NamedTempFile::new().unwrap();
    let settings_file = tempfile.path();
    fs::remove_file(&settings_file).unwrap();

    let mut settings = RuntimeSettings::load(settings_file).unwrap();
    settings.reset_installation_settings().unwrap();

    let new_settings = RuntimeSettings::load(settings_file).unwrap();

    assert_eq!(settings.update, new_settings.update);
}

#[test]
fn load_bad_formated_file() {
    use pretty_assertions::assert_eq;
    use std::fs;
    use tempfile::NamedTempFile;

    let tempfile = NamedTempFile::new().unwrap();
    let settings_file = tempfile.path();
    fs::write(&settings_file, "foo").unwrap();

    let load_result = RuntimeSettings::load(settings_file);
    assert!(load_result.is_ok(), "We should fail when reading a unformated formatted file");

    let old_file = settings_file
        .with_file_name(format!("{}.old", settings_file.file_name().unwrap().to_str().unwrap()));
    let old_content = fs::read_to_string(&old_file).unwrap();
    assert_eq!(
        old_content, "foo",
        "Old file should still be accessible as a .old file in the same directory"
    );
    fs::remove_file(old_file).unwrap();
}
<commit_msg>updatehub: runtime_settings: move tests under a module<commit_after>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use crate::firmware::{
    self,
    installation_set::{self, Set},
};
use chrono::{DateTime, NaiveDateTime, Utc};
use derive_more::{Deref, DerefMut, Display, Error, From};
use sdk::api::info::runtime_settings as api;
use slog_scope::{debug, warn};
use std::{fs, io, path::Path};

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Display, Error, From)]
pub enum Error {
    Io(io::Error),
    SerdeJson(serde_json::Error),
    FirmwareError(firmware::Error),

    #[display(fmt = "invalid runtime settings destination")]
    InvalidDestination,
}

#[derive(Clone, Debug, Deref, DerefMut, PartialEq)]
pub struct RuntimeSettings(pub api::RuntimeSettings);

impl Default for RuntimeSettings {
    fn default() -> Self {
        RuntimeSettings(api::RuntimeSettings {
            polling: api::RuntimePolling {
                last: DateTime::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc),
                retries: 0,
                now: false,
                server_address: api::ServerAddress::Default,
            },
            update: api::RuntimeUpdate { upgrade_to_installation: None, applied_package_uid: None },
            path: std::path::PathBuf::new(),
            persistent: false,
        })
    }
}

impl RuntimeSettings {
    pub fn load(path: &Path) -> Result<Self> {
        let mut this = if path.exists() {
            debug!("loading runtime settings from {:?}", path);
            match fs::read_to_string(path).map_err(Error::from).and_then(|ref s| Self::parse(s)) {
                Ok(v) => v,
                Err(e) => {
                    warn!("failed to load current runtime settings: {}", e);
                    let _ = fs::rename(
                        path,
                        path.with_file_name(format!(
                            "{}.old",
                            path.file_name().unwrap().to_str().unwrap()
                        )),
                    );
                    debug!("using default runtime settings");
                    Self::default()
                }
            }
        } else {
            debug!("runtime settings file {:?} does not exists, using default settings", path);
            Self::default()
        };

        this.path = path.to_path_buf();
        Ok(this)
    }

    fn parse(content: &str) -> Result<Self> {
        Ok(RuntimeSettings(serde_json::from_str::<api::RuntimeSettings>(&content)?))
    }

    fn save(&self) -> Result<()> {
        if !self.persistent {
            debug!("skipping runtime settings save, using non-persistent");
            return Ok(());
        }

        let parent = self.path.parent().ok_or(Error::InvalidDestination)?;
        if !parent.exists() {
            debug!("creating runtime settings to store state");
            fs::create_dir_all(parent)?;
        }

        fs::write(&self.path, self.serialize()?)?;
        debug!("saved runtime settings to {:?}", &self.path);

        Ok(())
    }

    fn serialize(&self) -> Result<String> {
        Ok(serde_json::to_string(&self.0)?)
    }

    pub(crate) fn get_inactive_installation_set(&self) -> Result<Set> {
        // If the `upgrade_to_installation` is defined, the current inactive
        // installation_set has already been swapped.
        if let Some(s) = self.update.upgrade_to_installation {
            return Ok(Set(s));
        }

        Ok(installation_set::inactive()?)
    }

    pub(crate) fn enable_persistency(&mut self) {
        self.persistent = true;
    }

    pub(crate) fn is_polling_forced(&self) -> bool {
        self.polling.now
    }

    pub(crate) fn disable_force_poll(&mut self) -> Result<()> {
        debug!("disabling foce poll");
        self.polling.now = false;
        self.save()
    }

    pub(crate) fn retries(&self) -> usize {
        self.polling.retries
    }

    pub(crate) fn inc_retries(&mut self) {
        self.polling.retries += 1;
    }

    pub(crate) fn clear_retries(&mut self) {
        self.polling.retries = 0;
    }

    pub(crate) fn last_polling(&self) -> DateTime<Utc> {
        self.polling.last
    }

    pub(crate) fn set_last_polling(&mut self, last_polling: DateTime<Utc>) -> Result<()> {
        debug!("updating last polling time");
        self.polling.last = last_polling;
        self.save()
    }

    pub(crate) fn applied_package_uid(&self) -> Option<String> {
        self.update.applied_package_uid.clone()
    }

    pub(crate) fn set_applied_package_uid(&mut self, applied_package_uid: &str) -> Result<()> {
        debug!("marking package {} as installed", applied_package_uid);
        self.update.applied_package_uid = Some(applied_package_uid.to_string());
        self.save()
    }

    pub(crate) fn set_upgrading_to(&mut self, new_install_set: Set) -> Result<()> {
        debug!("setting upgrading to {}", new_install_set);
        self.update.upgrade_to_installation = Some(new_install_set.0);
        self.save()
    }

    pub(crate) fn custom_server_address(&self) -> Option<&str> {
        match &self.polling.server_address {
            api::ServerAddress::Custom(s) => Some(s),
            api::ServerAddress::Default => None,
        }
    }

    pub(crate) fn set_custom_server_address(&mut self, server_address: &str) {
        self.polling.server_address = api::ServerAddress::Custom(server_address.to_owned());
    }

    /// Reset settings that are only need through a single installation
    pub(crate) fn reset_transient_settings(&mut self) {
        // Server address is reset so it doesn't keep probing the last custom server
        // requested
        self.polling.server_address = api::ServerAddress::Default;
    }

    pub(crate) fn reset_installation_settings(&mut self) -> Result<()> {
        debug!("reseting installation settings");
        self.update.upgrade_to_installation = None;
        self.update.applied_package_uid = None;

        // Ensure we do a probe as soon as possible so full update
        // cycle can be finished.
        self.polling.now = true;

        self.save()
    }
}

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

    #[test]
    fn default() {
        let settings = RuntimeSettings::default();
        let expected = RuntimeSettings(api::RuntimeSettings {
            polling: api::RuntimePolling {
                last: DateTime::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc),
                retries: 0,
                now: false,
                server_address: api::ServerAddress::Default,
            },
            update: api::RuntimeUpdate { upgrade_to_installation: None, applied_package_uid: None },
            path: std::path::PathBuf::new(),
            persistent: false,
        });

        assert_eq!(Some(settings), Some(expected));
    }

    #[test]
    fn load_and_save() {
        use std::fs;
        use tempfile::NamedTempFile;

        let tempfile = NamedTempFile::new().unwrap();
        let settings_file = tempfile.path();
        fs::remove_file(&settings_file).unwrap();

        let mut settings = RuntimeSettings::load(settings_file).unwrap();
        settings.reset_installation_settings().unwrap();

        let new_settings = RuntimeSettings::load(settings_file).unwrap();

        assert_eq!(settings.update, new_settings.update);
    }

    #[test]
    fn load_bad_formated_file() {
        use std::fs;
        use tempfile::NamedTempFile;

        let tempfile = NamedTempFile::new().unwrap();
        let settings_file = tempfile.path();
        fs::write(&settings_file, "foo").unwrap();

        let load_result = RuntimeSettings::load(settings_file);
        assert!(load_result.is_ok(), "We should fail when reading a unformated formatted file");

        let old_file = settings_file.with_file_name(format!(
            "{}.old",
            settings_file.file_name().unwrap().to_str().unwrap()
        ));
        let old_content = fs::read_to_string(&old_file).unwrap();
        assert_eq!(
            old_content, "foo",
            "Old file should still be accessible as a .old file in the same directory"
        );
        fs::remove_file(old_file).unwrap();
    }
}
<|endoftext|>