<commit_before>use crate::error::Error;
use crate::structs::{Config, Member, PublicKey, RateLimit, Repo, Sector, SectorGroup, Team};
use glob::glob;
use hyper::client::HttpConnector;
use hyper::rt::{self, Future, Stream};
use hyper::{header, Body, Chunk, Client, Request, Response};
use hyper_tls::HttpsConnector;
use serde_json;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::sync::mpsc;

pub struct GithubClient {
    pub conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> GithubClient {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        GithubClient { conf: config.clone() }
    }

    fn get_cache_path(&self, url: &str) -> std::path::PathBuf {
        let mut path = std::path::PathBuf::new();
        path.push(&self.conf.cache_dir);
        path.push(url);
        path
    }

    fn load_contents_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), Error> {
        let path = self.get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut contents = String::new();
        f.read_to_string(&mut contents)?;
        Ok((metadata, contents))
    }

    fn store_contents_to_cache(&self, url: &str, contents: &str) -> Result<(), Error> {
        let path = self.get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap_or(std::path::Path::new("/")))?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write_all(contents.as_bytes())?;
        Ok(())
    }

    fn get_contents_from_url(&self, url: &str) -> Result<String, Error> {
        let mut all_contents: Vec<serde_json::value::Value> = Vec::new();
        let mut page = 1;
        loop {
            let mut new_array = self.get_contents_from_url_page(url, page)?;
            if new_array.is_empty() {
                break;
            }
            all_contents.append(&mut new_array);
            page += 1;
        }
        let contents = serde_json::ser::to_string(&all_contents)?;
        self.store_contents_to_cache(url, &contents)?;
        Ok(contents)
    }

    fn build_request(&self, url: &str) -> Result<Request<Body>, Error> {
        let token = String::from("token ") + &self.conf.token;
        Request::get(url).header(header::AUTHORIZATION, token.as_str())
                         .header(header::USER_AGENT, "sectora")
                         .body(Body::empty())
                         .map_err(Error::from)
    }

    fn build_page_request(&self, url: &str, page: u64) -> Result<Request<Body>, Error> {
        let sep = if url.contains('?') { '&' } else { '?' };
        let url_p = format!("{}{}page={}", url, sep, page);
        self.build_request(&url_p)
    }

    fn build_https_client() -> Result<Client<HttpsConnector<HttpConnector>>, Error> {
        Ok(Client::builder().build(HttpsConnector::new(4)?))
    }

    fn run_request(&self, req: Request<Body>) -> Result<Chunk, Error> {
        let concat_body = |res: Response<Body>| res.into_body().concat2();
        let (tx, rx) = mpsc::sync_channel(1);
        let ex = tx.clone();
        let send_res = move |r| tx.send(Ok(r)).expect("send response");
        let send_err = move |e| ex.send(Err(Error::from(e))).expect("send err");
        let hc = Self::build_https_client()?;
        rt::run(rt::lazy(move || hc.request(req).and_then(concat_body).map(send_res).map_err(send_err)));
        rx.recv().expect("recv response")
    }

    fn get_contents_from_url_page(&self, url: &str, page: u64) -> Result<Vec<serde_json::Value>, Error> {
        let req = self.build_page_request(url, page)?;
        self.run_request(req)
            .and_then(|body| serde_json::from_slice(&body).map_err(Error::from))
    }

    fn get_contents(&self, url: &str) -> Result<String, Error> {
        match self.load_contents_from_cache(url) {
            Ok((metadata, cache_contents)) => match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                Ok(caching_duration) => {
                    if caching_duration.as_secs() > self.conf.cache_duration {
                        match self.get_contents_from_url(url) {
                            Ok(contents_from_url) => Ok(contents_from_url),
                            Err(_) => Ok(cache_contents),
                        }
                    } else {
                        Ok(cache_contents)
                    }
                }
                Err(_) => Ok(cache_contents),
            },
            Err(_) => self.get_contents_from_url(url),
        }
    }

    #[allow(dead_code)]
    pub fn get_user_public_key(&self, user: &str) -> Result<String, Error> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let contents = self.get_contents(&url)?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&contents)?;
        Ok(keys.iter().map(|k| k.key.clone()).collect::<Vec<String>>().join("\n"))
    }

    #[allow(dead_code)]
    pub fn check_pam(&self, user: &str) -> Result<bool, Error> {
        let sectors = self.get_sectors()?;
        Ok(sectors.iter().any(|team| team.members.contains_key(user)))
    }

    pub fn get_sectors(&self) -> Result<Vec<SectorGroup>, Error> {
        let mut sectors: Vec<SectorGroup> = self.get_teams_result()?;
        sectors.append(&mut self.get_repos_result()?);
        Ok(sectors)
    }

    fn get_teams_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_teams = self.get_team_map(&self.conf.org)?;
        let mut teams = Vec::new();
        for team_conf in &self.conf.team {
            if let Some(gh_team) = gh_teams.get(&team_conf.name) {
                teams.push(SectorGroup { sector: Sector::from(gh_team.clone()),
                                         gid: team_conf.gid,
                                         group: team_conf.group.clone(),
                                         members: self.get_team_members(gh_team.id)? });
            }
        }
        Ok(teams)
    }

    fn get_team_map(&self, org: &str) -> Result<HashMap<String, Team>, Error> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, org);
        let contents = self.get_contents(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(&contents)?;
        Ok(teams.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_team_members(&self, mid: u64) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    fn get_repos_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_repos = self.get_repo_map(&self.conf.org)?;
        let mut repos = Vec::new();
        for repo_conf in &self.conf.repo {
            if let Some(gh_repo) = gh_repos.get(&repo_conf.name) {
                repos.push(SectorGroup { sector: Sector::from(gh_repo.clone()),
                                         gid: repo_conf.gid,
                                         group: repo_conf.group.clone(),
                                         members: self.get_repo_collaborators(&self.conf.org, &gh_repo.name)? });
            }
        }
        Ok(repos)
    }

    fn get_repo_map(&self, org: &str) -> Result<HashMap<String, Repo>, Error> {
        let url = format!("{}/orgs/{}/repos", self.conf.endpoint, org);
        let contents = self.get_contents(&url)?;
        let repos = serde_json::from_str::<Vec<Repo>>(&contents)?;
        Ok(repos.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_repo_collaborators(&self, org: &str, repo_name: &str) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/repos/{}/{}/collaborators?affiliation=outside",
                          self.conf.endpoint, org, repo_name);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    #[allow(dead_code)]
    pub fn get_rate_limit(&self) -> Result<RateLimit, Error> {
        let url = format!("{}/rate_limit", self.conf.endpoint);
        let req = self.build_request(&url)?;
        self.run_request(req)
            .and_then(|body| serde_json::from_slice(&body).map_err(Error::from))
    }

    #[allow(dead_code)]
    pub fn clear_all_caches(&self) -> Result<(), Error> {
        let mut path = self.get_cache_path("");
        path.push("**/*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<commit_msg>Tweak<commit_after>use crate::error::Error;
use crate::structs::{Config, Member, PublicKey, RateLimit, Repo, Sector, SectorGroup, Team};
use glob::glob;
use hyper::client::HttpConnector;
use hyper::rt::{self, Future, Stream};
use hyper::{header, Body, Chunk, Client, Request, Response};
use hyper_tls::HttpsConnector;
use serde_json;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::sync::mpsc;

pub struct GithubClient {
    pub conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> GithubClient {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        GithubClient { conf: config.clone() }
    }

    fn get_cache_path(&self, url: &str) -> std::path::PathBuf {
        let mut path = std::path::PathBuf::new();
        path.push(&self.conf.cache_dir);
        path.push(url);
        path
    }

    fn load_contents_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), Error> {
        let path = self.get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut contents = String::new();
        f.read_to_string(&mut contents)?;
        Ok((metadata, contents))
    }

    fn store_contents_to_cache(&self, url: &str, contents: &str) -> Result<(), Error> {
        let path = self.get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap_or(std::path::Path::new("/")))?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write_all(contents.as_bytes())?;
        Ok(())
    }

    fn get_contents_from_url(&self, url: &str) -> Result<String, Error> {
        let mut all_contents: Vec<serde_json::value::Value> = Vec::new();
        let mut page = 1;
        loop {
            let mut new_array = self.get_contents_from_url_page(url, page)?;
            if new_array.is_empty() {
                break;
            }
            all_contents.append(&mut new_array);
            page += 1;
        }
        let contents = serde_json::ser::to_string(&all_contents)?;
        self.store_contents_to_cache(url, &contents)?;
        Ok(contents)
    }

    fn build_request(&self, url: &str) -> Result<Request<Body>, Error> {
        let token = String::from("token ") + &self.conf.token;
        Request::get(url).header(header::AUTHORIZATION, token.as_str())
                         .header(header::USER_AGENT, "sectora")
                         .body(Body::empty())
                         .map_err(Error::from)
    }

    fn build_page_request(&self, url: &str, page: u64) -> Result<Request<Body>, Error> {
        let sep = if url.contains('?') { '&' } else { '?' };
        let url_p = format!("{}{}page={}", url, sep, page);
        self.build_request(&url_p)
    }

    fn build_https_client() -> Result<Client<HttpsConnector<HttpConnector>>, Error> {
        Ok(Client::builder().build(HttpsConnector::new(4)?))
    }

    fn run_request(&self, req: Request<Body>) -> Result<Chunk, Error> {
        let concat_body = |res: Response<Body>| res.into_body().concat2();
        let (tx, rx) = mpsc::sync_channel(1);
        let ex = tx.clone();
        let send_res = move |r| tx.send(Ok(r)).expect("send response");
        let send_err = move |e| ex.send(Err(Error::from(e))).expect("send err");
        let hc = Self::build_https_client()?;
        rt::run(rt::lazy(move || hc.request(req).and_then(concat_body).map(send_res).map_err(send_err)));
        rx.recv().expect("recv response")
    }

    fn get_contents_from_url_page(&self, url: &str, page: u64) -> Result<Vec<serde_json::Value>, Error> {
        let req = self.build_page_request(url, page)?;
        self.run_request(req)
            .and_then(|body| serde_json::from_slice(&body).map_err(Error::from))
    }

    fn get_contents(&self, url: &str) -> Result<String, Error> {
        match self.load_contents_from_cache(url) {
            Ok((metadata, cache_contents)) => match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                Ok(caching_duration) => {
                    if caching_duration.as_secs() > self.conf.cache_duration {
                        match self.get_contents_from_url(url) {
                            Ok(contents_from_url) => Ok(contents_from_url),
                            Err(_) => Ok(cache_contents),
                        }
                    } else {
                        Ok(cache_contents)
                    }
                }
                Err(_) => Ok(cache_contents),
            },
            Err(_) => self.get_contents_from_url(url),
        }
    }

    #[allow(dead_code)]
    pub fn get_user_public_key(&self, user: &str) -> Result<String, Error> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let contents = self.get_contents(&url)?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&contents)?;
        Ok(keys.iter().map(|k| k.key.clone()).collect::<Vec<_>>().join("\n"))
    }

    #[allow(dead_code)]
    pub fn check_pam(&self, user: &str) -> Result<bool, Error> {
        let sectors = self.get_sectors()?;
        Ok(sectors.iter().any(|team| team.members.contains_key(user)))
    }

    pub fn get_sectors(&self) -> Result<Vec<SectorGroup>, Error> {
        let mut sectors: Vec<SectorGroup> = self.get_teams_result()?;
        sectors.append(&mut self.get_repos_result()?);
        Ok(sectors)
    }

    fn get_teams_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_teams = self.get_team_map(&self.conf.org)?;
        let mut teams = Vec::new();
        for team_conf in &self.conf.team {
            if let Some(gh_team) = gh_teams.get(&team_conf.name) {
                teams.push(SectorGroup { sector: Sector::from(gh_team.clone()),
                                         gid: team_conf.gid,
                                         group: team_conf.group.clone(),
                                         members: self.get_team_members(gh_team.id)? });
            }
        }
        Ok(teams)
    }

    fn get_team_map(&self, org: &str) -> Result<HashMap<String, Team>, Error> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, org);
        let contents = self.get_contents(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(&contents)?;
        Ok(teams.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_team_members(&self, mid: u64) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    fn get_repos_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_repos = self.get_repo_map(&self.conf.org)?;
        let mut repos = Vec::new();
        for repo_conf in &self.conf.repo {
            if let Some(gh_repo) = gh_repos.get(&repo_conf.name) {
                repos.push(SectorGroup { sector: Sector::from(gh_repo.clone()),
                                         gid: repo_conf.gid,
                                         group: repo_conf.group.clone(),
                                         members: self.get_repo_collaborators(&self.conf.org, &gh_repo.name)? });
            }
        }
        Ok(repos)
    }

    fn get_repo_map(&self, org: &str) -> Result<HashMap<String, Repo>, Error> {
        let url = format!("{}/orgs/{}/repos", self.conf.endpoint, org);
        let contents = self.get_contents(&url)?;
        let repos = serde_json::from_str::<Vec<Repo>>(&contents)?;
        Ok(repos.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_repo_collaborators(&self, org: &str, repo_name: &str) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/repos/{}/{}/collaborators?affiliation=outside",
                          self.conf.endpoint, org, repo_name);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    #[allow(dead_code)]
    pub fn get_rate_limit(&self) -> Result<RateLimit, Error> {
        let url = format!("{}/rate_limit", self.conf.endpoint);
        let req = self.build_request(&url)?;
        self.run_request(req)
            .and_then(|body| serde_json::from_slice(&body).map_err(Error::from))
    }

    #[allow(dead_code)]
    pub fn clear_all_caches(&self) -> Result<(), Error> {
        let mut path = self.get_cache_path("");
        path.push("**/*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::process::Child;
use std::process::Command;
use std::process::Stdio;

pub struct App {
    pub name: String,
    pub port: String,
    process: Child,
}

impl App {
    pub fn new(name: &str, port: &str, app_home: String) -> App {
        let mut path_buf = PathBuf::from(app_home);
        path_buf.push(&name);

        let child_process = Command::new("foreman")
            .arg("start")
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .current_dir(path_buf.as_path())
            .env("PORT", port)
            .spawn().unwrap();

        App{
            name: name.to_string(),
            port: port.to_string(),
            process: child_process,
        }
    }
}
<commit_msg>feature: Keep child stdout and stderr connected to parent<commit_after>use std::path::PathBuf;
use std::process::Child;
use std::process::Command;
use std::process::Stdio;

pub struct App {
    pub name: String,
    pub port: String,
    process: Child,
}

impl App {
    pub fn new(name: &str, port: &str, app_home: String) -> App {
        let mut path_buf = PathBuf::from(app_home);
        path_buf.push(&name);

        let child_process = Command::new("foreman")
            .arg("start")
            .current_dir(path_buf.as_path())
            .env("PORT", port)
            .spawn().unwrap();

        App{
            name: name.to_string(),
            port: port.to_string(),
            process: child_process,
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate criterion;
use criterion::{black_box, Criterion};

use rsvg_internals::path_builder::PathBuilder;
use rsvg_internals::path_parser::{parse_path_into_builder, Lexer};

static INPUT: &'static str = "M10 20 C 30,40 50 60-70,80,90 100,110 120,130,140";

static BYTES: &'static [u8; 49] = b"M10 20 C 30,40 50 60-70,80,90 100,110 120,130,140";

static SLICE_EDGES: [(usize, usize); 14] = [
    (1, 3),
    (4, 6),
    (9, 11),
    (12, 14),
    (15, 17),
    (18, 20),
    (20, 23),
    (24, 26),
    (27, 29),
    (30, 33),
    (34, 37),
    (38, 41),
    (42, 45),
    (46, 49),
];

fn lex_path(input: &str) {
    let lexer = Lexer::new(black_box(INPUT));

    for (_pos, _token) in lexer {
        // no-op
    }
}

fn path_parser(c: &mut Criterion) {
    c.bench_function("parse path into builder", |b| {
        let input = black_box(INPUT);
        let mut builder = PathBuilder::default();

        b.iter(|| {
            let _ = parse_path_into_builder(&input, &mut builder);
        });
    });

    c.bench_function("lex str", |b| {
        let input = black_box(INPUT);

        b.iter(|| {
            lex_path(input);
        });
    });

    // look at how much time *just* the parse::<i32> part of the lexer should be taking...
    c.bench_function("std i32 parse (bytes)", |b| {
        let input = black_box(BYTES);
        let slice_boundaries = black_box(SLICE_EDGES);

        b.iter(|| {
            for (a, b) in slice_boundaries.iter() {
                let a: usize = *a;
                let b: usize = *b;
                unsafe {
                    let _ = std::str::from_utf8_unchecked(&input[a..b])
                        .parse::<i32>()
                        .unwrap();
                }
            }
        });
    });
}

criterion_group!(benches, path_parser);
criterion_main!(benches);
<commit_msg>Fix path_parser benchmark<commit_after>#[macro_use]
extern crate criterion;
use criterion::{black_box, Criterion};

use rsvg_internals::path_builder::PathBuilder;
use rsvg_internals::path_parser::{parse_path_into_builder, Lexer};

static INPUT: &'static str = "M10 20 C 30,40 50 60-70,80,90 100,110 120,130,140";

static BYTES: &'static [u8; 49] = b"M10 20 C 30,40 50 60-70,80,90 100,110 120,130,140";

static SLICE_EDGES: [(usize, usize); 14] = [
    (1, 3),
    (4, 6),
    (9, 11),
    (12, 14),
    (15, 17),
    (18, 20),
    (20, 23),
    (24, 26),
    (27, 29),
    (30, 33),
    (34, 37),
    (38, 41),
    (42, 45),
    (46, 49),
];

fn lex_path(input: &str) {
    let lexer = Lexer::new(black_box(input));

    for (_pos, _token) in lexer {
        // no-op
    }
}

fn path_parser(c: &mut Criterion) {
    c.bench_function("parse path into builder", |b| {
        let input = black_box(INPUT);
        let mut builder = PathBuilder::default();

        b.iter(|| {
            let _ = parse_path_into_builder(&input, &mut builder);
        });
    });

    c.bench_function("lex str", |b| {
        let input = black_box(INPUT);

        b.iter(|| {
            lex_path(input);
        });
    });

    // look at how much time *just* the parse::<i32> part of the lexer should be taking...
    c.bench_function("std i32 parse (bytes)", |b| {
        let input = black_box(BYTES);
        let slice_boundaries = black_box(SLICE_EDGES);

        b.iter(|| {
            for (a, b) in slice_boundaries.iter() {
                let a: usize = *a;
                let b: usize = *b;
                unsafe {
                    let _ = std::str::from_utf8_unchecked(&input[a..b])
                        .parse::<i32>()
                        .unwrap();
                }
            }
        });
    });
}

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


<commit_before>use ::libc::{self, c_int, O_EXCL, O_RDWR, O_CREAT};
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::fs::{self, File, OpenOptions};
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::ffi::{OsStr, CString};
use ::util::tmpname;

const O_CLOEXEC: libc::c_int = 0o2000000;
const O_TMPFILE: libc::c_int = 0o20200000;

// Stolen from std.
fn cstr(path: &Path) -> io::Result<CString> {
    path.as_os_str().to_cstring().ok_or(
        io::Error::new(io::ErrorKind::InvalidInput, "path contained a null"))
}

fn create_unix(dir: &Path) -> io::Result<File> {
    loop {
        let name = tmpname();
        let tmp_path = dir.join(OsStr::from_bytes(&name));
        return match unsafe {
            libc::open(try!(cstr(&tmp_path)).as_ptr(), O_CLOEXEC | O_EXCL | O_RDWR | O_CREAT, 0o600)
        } {
            -1 => {
                let err = io::Error::last_os_error();
                if err.kind() == io::ErrorKind::AlreadyExists {
                    continue;
                } else {
                    Err(err)
                }
            }
            fd => {
                let f = FromRawFd::from_raw_fd(fd);
                // I should probably tell the user this failed but the temporary file creation
                // didn't really fail...
                let _ = fs::remove_file(tmp_path);
                Ok(f)
            }
        }
    }
}

#[cfg(target_os = "linux")]
fn create_linux(dir: &Path) -> io::Result<File> {
    let dir = try!(cstr(dir));

    match unsafe {
        libc::open(dir.as_ptr(), O_CLOEXEC | O_EXCL | O_TMPFILE | O_RDWR, 0o600)
    } {
        -1 => Err(io::Error::last_os_error()),
        fd => Ok(FromRawFd::from_raw_fd(fd)),
    }
}

#[cfg(target_os = "linux")]
pub fn create(dir: &Path) -> io::Result<File> {
    // Fallback on unix create in case the kernel version is < 3.11.
    create_linux(dir).or_else(|_| create_unix(dir))
}

#[cfg(not(target_os = "linux"))]
#[inline(always)]
pub fn create(dir: &Path, num_readers: usize) -> io::Result<File> {
    create_unix(dir, num_readers)
}

pub fn reopen(f: &File) -> io::Result<File> {
    OpenOptions::new().read(true).write(true).create(false).open(format!("/dev/fd/{}", f.as_raw_fd()))
}
<commit_msg>Fix unix impl.<commit_after>use ::libc::{self, c_int, O_EXCL, O_RDWR, O_CREAT};
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::fs::{self, File, OpenOptions};
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::ffi::{OsStr, CString};
use ::util::tmpname;

const O_CLOEXEC: libc::c_int = 0o2000000;
#[cfg(target_os = "linux")]
const O_TMPFILE: libc::c_int = 0o20200000;

// Stolen from std.
fn cstr(path: &Path) -> io::Result<CString> {
    path.as_os_str().to_cstring().ok_or(
        io::Error::new(io::ErrorKind::InvalidInput, "path contained a null"))
}

fn create_unix(dir: &Path) -> io::Result<File> {
    loop {
        let name = tmpname();
        let tmp_path = dir.join(OsStr::from_bytes(&name));
        return match unsafe {
            libc::open(try!(cstr(&tmp_path)).as_ptr(), O_CLOEXEC | O_EXCL | O_RDWR | O_CREAT, 0o600)
        } {
            -1 => {
                let err = io::Error::last_os_error();
                if err.kind() == io::ErrorKind::AlreadyExists {
                    continue;
                } else {
                    Err(err)
                }
            }
            fd => {
                let f = FromRawFd::from_raw_fd(fd);
                // I should probably tell the user this failed but the temporary file creation
                // didn't really fail...
                let _ = fs::remove_file(tmp_path);
                Ok(f)
            }
        }
    }
}

#[cfg(target_os = "linux")]
fn create_linux(dir: &Path) -> io::Result<File> {
    let dir = try!(cstr(dir));

    match unsafe {
        libc::open(dir.as_ptr(), O_CLOEXEC | O_EXCL | O_TMPFILE | O_RDWR, 0o600)
    } {
        -1 => Err(io::Error::last_os_error()),
        fd => Ok(FromRawFd::from_raw_fd(fd)),
    }
}

#[cfg(target_os = "linux")]
pub fn create(dir: &Path) -> io::Result<File> {
    // Fallback on unix create in case the kernel version is < 3.11.
    create_linux(dir).or_else(|_| create_unix(dir))
}

#[cfg(not(target_os = "linux"))]
#[inline(always)]
pub fn create(dir: &Path) -> io::Result<File> {
    create_unix(dir)
}

pub fn reopen(f: &File) -> io::Result<File> {
    OpenOptions::new().read(true).write(true).create(false).open(format!("/dev/fd/{}", f.as_raw_fd()))
}
<|endoftext|>


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

use std::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use devicemapper::consts::SECTOR_SIZE;
use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors, Segment};
use devicemapper::LinearDev;
use devicemapper::{ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};
use time::now;
use uuid::Uuid;
use serde_json;

use super::super::consts::IEC::Mi;
use super::super::engine::{Filesystem, HasName, HasUuid, Pool};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::structures::Table;
use super::super::types::{FilesystemUuid, PoolUuid, RenameAction, Redundancy};

use super::blockdevmgr::BlockDevMgr;
use super::device::wipe_sectors;
use super::dmdevice::{FlexRole, ThinPoolRole, format_flex_name, format_thinpool_name};
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::mdv::MetadataVol;
use super::metadata::MIN_MDA_SECTORS;
use super::serde_structs::{FlexDevsSave, PoolSave, Recordable, ThinPoolDevSave};

const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
const META_LOWATER: u64 = 512;
const DATA_LOWATER: DataBlocks = DataBlocks(512);

const INITIAL_META_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);
const INITIAL_DATA_SIZE: Sectors = Sectors(512 * Mi / SECTOR_SIZE as u64);
const INITIAL_MDV_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: PoolUuid,
    pub block_devs: BlockDevMgr,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
    mdv: MetadataVol,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let mut block_mgr =
            try!(BlockDevMgr::initialize(&pool_uuid, paths, MIN_MDA_SECTORS, force));

        if block_mgr.avail_space() < StratPool::min_initial_size() {
            let avail_size = block_mgr.avail_space().bytes();
            try!(block_mgr.destroy_all());
            return Err(EngineError::Engine(ErrorEnum::Invalid,
                                           format!("Space on pool must be at least {} bytes, \
                                                   available space is only {} bytes",
                                                   StratPool::min_initial_size().bytes(),
                                                   avail_size)));

        }

        let meta_regions = block_mgr
            .alloc_space(INITIAL_META_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::ThinMeta);
        let meta_dev = try!(LinearDev::new(&device_name, dm, meta_regions));

        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), INITIAL_META_SIZE));

        let data_regions = block_mgr
            .alloc_space(INITIAL_DATA_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::ThinData);
        let data_dev = try!(LinearDev::new(&device_name, dm, data_regions));
        let length = try!(data_dev.size());

        let device_name = format_thinpool_name(&pool_uuid, ThinPoolRole::Pool);
        // TODO Fix hard coded data blocksize and low water mark.
        let thinpool_dev = try!(ThinPoolDev::new(&device_name,
                                                 dm,
                                                 length,
                                                 DATA_BLOCK_SIZE,
                                                 DataBlocks(256000),
                                                 meta_dev,
                                                 data_dev));

        let mdv_regions = block_mgr
            .alloc_space(INITIAL_MDV_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::MetadataVolume);
        let mdv_dev = try!(LinearDev::new(&device_name, dm, mdv_regions));
        let mdv = try!(MetadataVol::initialize(&pool_uuid, mdv_dev));

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: block_mgr,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
            mdv: mdv,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Minimum initial size for a pool.
    pub fn min_initial_size() -> Sectors {
        INITIAL_META_SIZE + INITIAL_DATA_SIZE + INITIAL_MDV_SIZE
    }

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&try!(self.record())));
        self.block_devs
            .save_state(&now().to_timespec(), data.as_bytes())
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        if let Err(e) = self.mdv.check() {
            error!("MDV error: {}", e);
            return;
        }

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in &mut self.filesystems {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }

    /// Teardown a pool.
    /// Take down the device mapper devices belonging to the pool.
    /// This method and destroy() must keep their DM teardown operations
    /// in sync.
    pub fn teardown(self) -> EngineResult<()> {
        // TODO: any necessary clean up of filesystems
        if !self.filesystems.is_empty() {
            return Err(EngineError::Engine(ErrorEnum::Busy,
                                           format!("May be unsynced files on device.")));
        }
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        Ok(())
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        // TODO: Roll back on filesystem initialization failure.
        let dm = try!(DM::new());
        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            let new_filesystem = try!(StratFilesystem::initialize(&self.pool_uuid,
                                                                  uuid,
                                                                  name,
                                                                  &dm,
                                                                  &mut self.thin_pool));
            try!(self.mdv.save_fs(&new_filesystem));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let bdev_paths = try!(self.block_devs.add(&self.pool_uuid, paths, force));
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(self) -> EngineResult<()> {
        // Ensure that DM teardown operations in this method are in sync
        // with operations in teardown().
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        try!(self.block_devs.destroy_all());

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        for fsid in fs_uuids {
            try!(self.mdv.rm_fs(fsid));
        }
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &PoolUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}

impl Recordable<PoolSave> for StratPool {
    fn record(&self) -> EngineResult<PoolSave> {

        let mapper = |seg: &Segment| -> EngineResult<(Uuid, Sectors, Sectors)> {
            let bd = try!(self.block_devs
                     .get_by_device(seg.device)
                     .ok_or(EngineError::Engine(ErrorEnum::NotFound,
                                                format!("no block device found for device {:?}",
                                                        seg.device))));
            Ok((*bd.uuid(), seg.start, seg.length))
        };

        let meta_dev = try!(self.mdv.segments().iter().map(&mapper).collect());

        let thin_meta_dev = try!(self.thin_pool
                                     .meta_dev()
                                     .segments()
                                     .iter()
                                     .map(&mapper)
                                     .collect());

        let thin_data_dev = try!(self.thin_pool
                                     .data_dev()
                                     .segments()
                                     .iter()
                                     .map(&mapper)
                                     .collect());

        Ok(PoolSave {
               name: self.name.clone(),
               block_devs: try!(self.block_devs.record()),
               flex_devs: FlexDevsSave {
                   meta_dev: meta_dev,
                   thin_meta_dev: thin_meta_dev,
                   thin_data_dev: thin_data_dev,
               },
               thinpool_dev: ThinPoolDevSave { data_block_size: self.thin_pool.data_block_size() },
           })
    }
}
<commit_msg>Use DATA_LOWATER for ThinPoolDev::new() low water mark<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use devicemapper::consts::SECTOR_SIZE;
use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors, Segment};
use devicemapper::LinearDev;
use devicemapper::{ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};
use time::now;
use uuid::Uuid;
use serde_json;

use super::super::consts::IEC::Mi;
use super::super::engine::{Filesystem, HasName, HasUuid, Pool};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::structures::Table;
use super::super::types::{FilesystemUuid, PoolUuid, RenameAction, Redundancy};

use super::blockdevmgr::BlockDevMgr;
use super::device::wipe_sectors;
use super::dmdevice::{FlexRole, ThinPoolRole, format_flex_name, format_thinpool_name};
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::mdv::MetadataVol;
use super::metadata::MIN_MDA_SECTORS;
use super::serde_structs::{FlexDevsSave, PoolSave, Recordable, ThinPoolDevSave};

const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
const META_LOWATER: u64 = 512;
const DATA_LOWATER: DataBlocks = DataBlocks(512);

const INITIAL_META_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);
const INITIAL_DATA_SIZE: Sectors = Sectors(512 * Mi / SECTOR_SIZE as u64);
const INITIAL_MDV_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: PoolUuid,
    pub block_devs: BlockDevMgr,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
    mdv: MetadataVol,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let mut block_mgr =
            try!(BlockDevMgr::initialize(&pool_uuid, paths, MIN_MDA_SECTORS, force));

        if block_mgr.avail_space() < StratPool::min_initial_size() {
            let avail_size = block_mgr.avail_space().bytes();
            try!(block_mgr.destroy_all());
            return Err(EngineError::Engine(ErrorEnum::Invalid,
                                           format!("Space on pool must be at least {} bytes, \
                                                   available space is only {} bytes",
                                                   StratPool::min_initial_size().bytes(),
                                                   avail_size)));

        }

        let meta_regions = block_mgr
            .alloc_space(INITIAL_META_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::ThinMeta);
        let meta_dev = try!(LinearDev::new(&device_name, dm, meta_regions));

        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), INITIAL_META_SIZE));

        let data_regions = block_mgr
            .alloc_space(INITIAL_DATA_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::ThinData);
        let data_dev = try!(LinearDev::new(&device_name, dm, data_regions));
        let length = try!(data_dev.size());

        let device_name = format_thinpool_name(&pool_uuid, ThinPoolRole::Pool);
        let thinpool_dev = try!(ThinPoolDev::new(&device_name,
                                                 dm,
                                                 length,
                                                 DATA_BLOCK_SIZE,
                                                 DATA_LOWATER,
                                                 meta_dev,
                                                 data_dev));

        let mdv_regions = block_mgr
            .alloc_space(INITIAL_MDV_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::MetadataVolume);
        let mdv_dev = try!(LinearDev::new(&device_name, dm, mdv_regions));
        let mdv = try!(MetadataVol::initialize(&pool_uuid, mdv_dev));

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: block_mgr,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
            mdv: mdv,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Minimum initial size for a pool.
    pub fn min_initial_size() -> Sectors {
        INITIAL_META_SIZE + INITIAL_DATA_SIZE + INITIAL_MDV_SIZE
    }

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&try!(self.record())));
        self.block_devs
            .save_state(&now().to_timespec(), data.as_bytes())
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        if let Err(e) = self.mdv.check() {
            error!("MDV error: {}", e);
            return;
        }

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in &mut self.filesystems {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }

    /// Teardown a pool.
    /// Take down the device mapper devices belonging to the pool.
    /// This method and destroy() must keep their DM teardown operations
    /// in sync.
    pub fn teardown(self) -> EngineResult<()> {
        // TODO: any necessary clean up of filesystems
        if !self.filesystems.is_empty() {
            return Err(EngineError::Engine(ErrorEnum::Busy,
                                           format!("May be unsynced files on device.")));
        }
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        Ok(())
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        // TODO: Roll back on filesystem initialization failure.
        let dm = try!(DM::new());
        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            let new_filesystem = try!(StratFilesystem::initialize(&self.pool_uuid,
                                                                  uuid,
                                                                  name,
                                                                  &dm,
                                                                  &mut self.thin_pool));
            try!(self.mdv.save_fs(&new_filesystem));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let bdev_paths = try!(self.block_devs.add(&self.pool_uuid, paths, force));
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(self) -> EngineResult<()> {
        // Ensure that DM teardown operations in this method are in sync
        // with operations in teardown().
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        try!(self.block_devs.destroy_all());

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        for fsid in fs_uuids {
            try!(self.mdv.rm_fs(fsid));
        }
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &PoolUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}

impl Recordable<PoolSave> for StratPool {
    fn record(&self) -> EngineResult<PoolSave> {

        let mapper = |seg: &Segment| -> EngineResult<(Uuid, Sectors, Sectors)> {
            let bd = try!(self.block_devs
                     .get_by_device(seg.device)
                     .ok_or(EngineError::Engine(ErrorEnum::NotFound,
                                                format!("no block device found for device {:?}",
                                                        seg.device))));
            Ok((*bd.uuid(), seg.start, seg.length))
        };

        let meta_dev = try!(self.mdv.segments().iter().map(&mapper).collect());

        let thin_meta_dev = try!(self.thin_pool
                                     .meta_dev()
                                     .segments()
                                     .iter()
                                     .map(&mapper)
                                     .collect());

        let thin_data_dev = try!(self.thin_pool
                                     .data_dev()
                                     .segments()
                                     .iter()
                                     .map(&mapper)
                                     .collect());

        Ok(PoolSave {
               name: self.name.clone(),
               block_devs: try!(self.block_devs.record()),
               flex_devs: FlexDevsSave {
                   meta_dev: meta_dev,
                   thin_meta_dev: thin_meta_dev,
                   thin_data_dev: thin_data_dev,
               },
               thinpool_dev: ThinPoolDevSave { data_block_size: self.thin_pool.data_block_size() },
           })
    }
}
<|endoftext|>


<commit_before>#![allow(unused_mut, unused_variables)]

use truetype::GlyphID;

use {Result, Tape, Value, Walue};
use glyph_substitution::{
    AlternateSet,
    ChainClassRuleSet,
    ChainRuleSet,
    ClassRuleSet,
    LigatureSet,
    RuleSet,
    Sequence,
    Substitution,
};
use layout::{Class, Coverage};

/// A table.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Table {
    SingleSubstitution(SingleSubstitution),
    MultipleSubstitution(MultipleSubstitution),
    AlternateSubstitution(AlternateSubstitution),
    LigatureSubstitution(LigatureSubstitution),
    ContextSubstitution(ContextSubstitution),
    ChainContextSubstitution(ChainContextSubstitution),
    ExtensionSubstitution(ExtensionSubstitution),
    ReverseChainContextSubstitution(ReverseChainContextSubstitution),
}

/// A table for substituting one glyph with one glyph.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SingleSubstitution {
    /// Format 1.
    Format1(SingleSubstitution1),
    /// Format 2.
    Format2(SingleSubstitution2),
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one glyph in format 1."]
    pub SingleSubstitution1 { // SingleSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        delta_glyph_id  (i16), // DeltaGlyphID

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one glyph in format 2."]
    pub SingleSubstitution2 { // SingleSubstFormat2
        format          (u16) = { 2 }, // SubstFormat
        coverage_offset (u16), // Coverage
        glyph_count     (u16), // GlyphCount

        glyph_ids (Vec<GlyphID>) |this, tape, _| { // Substitute
            tape.take_given(this.glyph_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with more than one glyph."]
    pub MultipleSubstitution { // MultipleSubstFormat1
        format           (u16) = { 1 }, // SubstFormat
        coverage_offset  (u16), // Coverage
        sequence_count   (u16), // SequenceCount

        sequence_offsets (Vec<u16>) |this, tape, _| { // Sequence
            tape.take_given(this.sequence_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sequences (Vec<Sequence>) |this, tape, position| {
            jump_take!(tape, position, this.sequence_count, this.sequence_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one of many glyphs."]
    pub AlternateSubstitution { // AlternateSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // AlternateSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // AlternateSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<AlternateSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting multiple glyphs with one glyph."]
    pub LigatureSubstitution { // LigatureSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // LigSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // LigatureSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<LigatureSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

/// A table for substituting glyphs in a context.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ContextSubstitution {
    /// Format 1.
    Format1(ContextSubstitution1),
    /// Format 2.
    Format2(ContextSubstitution2),
    /// Format 3.
    Format3(ContextSubstitution3),
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 1."]
    pub ContextSubstitution1 { // ContextSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // SubRuleSetCount

        set_offsets (Vec<u16>) |this, tape, position| { // SubRuleSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<RuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 2."]
    pub ContextSubstitution2 { // ContextSubstFormat2
        format          (u16) = { 2 }, // SubstFormat
        coverage_offset (u16), // Coverage
        class_offset    (u16), // ClassDef
        set_count       (u16), // SubClassSetCnt

        set_offsets (Vec<u16>) |this, tape, _| { // SubClassSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<ClassRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 3."]
    pub ContextSubstitution3 { // ContextSubstFormat3
        format             (u16) = { 3 }, // SubstFormat
        glyph_count        (u16), // GlyphCount
        substitution_count (u16), // SubstCount

        coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.glyph_count as usize)
        },

        substitutions (Vec<Substitution>) |this, tape, _| { // SubstLookupRecord
            tape.take_given(this.substitution_count as usize)
        },

        coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.glyph_count, this.coverage_offsets)
        },
    }
}

/// A table for substituting glyphs in a chaining context.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ChainContextSubstitution {
    /// Format 1.
    Format1(ChainContextSubstitution1),
    /// Format 2.
    Format2(ChainContextSubstitution2),
    /// Format 3.
    Format3(ChainContextSubstitution3),
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 1."]
    pub ChainContextSubstitution1 { // ChainContextSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // ChainSubRuleSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // ChainSubRuleSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<ChainRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 2."]
    pub ChainContextSubstitution2 { // ChainContextSubstFormat2
        format                (u16) = { 2 }, // SubstFormat
        coverage_offset       (u16), // Coverage
        backward_class_offset (u16), // BacktrackClassDef
        input_class_offset    (u16), // InputClassDef
        forward_class_offset  (u16), // LookaheadClassDef
        set_count             (u16), // ChainSubClassSetCnt

        set_offsets (Vec<u16>) |this, tape, _| { // ChainSubClassSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        backward_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.backward_class_offset)
        },

        input_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.input_class_offset)
        },

        forward_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.forward_class_offset)
        },

        sets (Vec<ChainClassRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 3."]
    pub ChainContextSubstitution3 { // ChainContextSubstFormat3
        format               (u16) = { 3 }, // SubstFormat
        backward_glyph_count (u16), // BacktrackGlyphCount

        backward_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.backward_glyph_count as usize)
        },

        input_glyph_count (u16), // InputGlyphCount

        input_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.input_glyph_count as usize)
        },

        forward_glyph_count (u16), // LookaheadGlyphCount

        forward_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.forward_glyph_count as usize)
        },

        substitution_count (u16), // SubstCount

        substitutions (Vec<Substitution>) |this, tape, _| { // SubstLookupRecord
            tape.take_given(this.substitution_count as usize)
        },

        backward_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.backward_glyph_count, this.backward_coverage_offsets)
        },

        input_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.input_glyph_count, this.input_coverage_offsets)
        },

        forward_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.forward_glyph_count, this.forward_coverage_offsets)
        },
    }
}

table! {
    #[doc = "A table for other types of substitution."]
    pub ExtensionSubstitution { // ExtensionSubstFormat1
        format (u16) = { 1 }, // SubstFormat
        kind   (u16), // ExtensionLookupType
        offset (u32), // ExtensionOffset
    }
}

table! {
    #[doc = "A table for substituting glyphs in reverse order in a chaining context."]
    pub ReverseChainContextSubstitution { // ReverseChainSingleSubstFormat1
    }
}

impl Walue<u16> for Table {
    fn read<T: Tape>(tape: &mut T, kind: u16) -> Result<Self> {
        Ok(match kind {
            1 => Table::SingleSubstitution(try!(tape.take())),
            2 => Table::MultipleSubstitution(try!(tape.take())),
            3 => Table::AlternateSubstitution(try!(tape.take())),
            4 => Table::LigatureSubstitution(try!(tape.take())),
            5 => Table::ContextSubstitution(try!(tape.take())),
            6 => Table::ChainContextSubstitution(try!(tape.take())),
            7 => Table::ExtensionSubstitution(try!(tape.take())),
            8 => Table::ReverseChainContextSubstitution(try!(tape.take())),
            _ => raise!("found an unknown glyph-substitution type"),
        })
    }
}

impl Value for SingleSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => SingleSubstitution::Format1(try!(tape.take())),
            2 => SingleSubstitution::Format2(try!(tape.take())),
            _ => raise!("found an unknown format of the single-substitution table"),
        })
    }
}

impl Value for ContextSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => ContextSubstitution::Format1(try!(tape.take())),
            2 => ContextSubstitution::Format2(try!(tape.take())),
            3 => ContextSubstitution::Format3(try!(tape.take())),
            _ => raise!("found an unknown format of the context-substitution table"),
        })
    }
}

impl Value for ChainContextSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => ChainContextSubstitution::Format1(try!(tape.take())),
            2 => ChainContextSubstitution::Format2(try!(tape.take())),
            3 => ChainContextSubstitution::Format3(try!(tape.take())),
            _ => raise!("found an unknown format of the chaining-context-substitution table"),
        })
    }
}
<commit_msg>Read the reverse-chaining-context-substitution table<commit_after>#![allow(unused_mut, unused_variables)]

use truetype::GlyphID;

use {Result, Tape, Value, Walue};
use glyph_substitution::{
    AlternateSet,
    ChainClassRuleSet,
    ChainRuleSet,
    ClassRuleSet,
    LigatureSet,
    RuleSet,
    Sequence,
    Substitution,
};
use layout::{Class, Coverage};

/// A table.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Table {
    SingleSubstitution(SingleSubstitution),
    MultipleSubstitution(MultipleSubstitution),
    AlternateSubstitution(AlternateSubstitution),
    LigatureSubstitution(LigatureSubstitution),
    ContextSubstitution(ContextSubstitution),
    ChainContextSubstitution(ChainContextSubstitution),
    ExtensionSubstitution(ExtensionSubstitution),
    ReverseChainContextSubstitution(ReverseChainContextSubstitution),
}

/// A table for substituting one glyph with one glyph.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SingleSubstitution {
    /// Format 1.
    Format1(SingleSubstitution1),
    /// Format 2.
    Format2(SingleSubstitution2),
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one glyph in format 1."]
    pub SingleSubstitution1 { // SingleSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        delta_glyph_id  (i16), // DeltaGlyphID

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one glyph in format 2."]
    pub SingleSubstitution2 { // SingleSubstFormat2
        format          (u16) = { 2 }, // SubstFormat
        coverage_offset (u16), // Coverage
        glyph_count     (u16), // GlyphCount

        glyph_ids (Vec<GlyphID>) |this, tape, _| { // Substitute
            tape.take_given(this.glyph_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with more than one glyph."]
    pub MultipleSubstitution { // MultipleSubstFormat1
        format           (u16) = { 1 }, // SubstFormat
        coverage_offset  (u16), // Coverage
        sequence_count   (u16), // SequenceCount

        sequence_offsets (Vec<u16>) |this, tape, _| { // Sequence
            tape.take_given(this.sequence_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sequences (Vec<Sequence>) |this, tape, position| {
            jump_take!(tape, position, this.sequence_count, this.sequence_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting one glyph with one of many glyphs."]
    pub AlternateSubstitution { // AlternateSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // AlternateSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // AlternateSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<AlternateSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting multiple glyphs with one glyph."]
    pub LigatureSubstitution { // LigatureSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // LigSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // LigatureSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<LigatureSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

/// A table for substituting glyphs in a context.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ContextSubstitution {
    /// Format 1.
    Format1(ContextSubstitution1),
    /// Format 2.
    Format2(ContextSubstitution2),
    /// Format 3.
    Format3(ContextSubstitution3),
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 1."]
    pub ContextSubstitution1 { // ContextSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // SubRuleSetCount

        set_offsets (Vec<u16>) |this, tape, position| { // SubRuleSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<RuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 2."]
    pub ContextSubstitution2 { // ContextSubstFormat2
        format          (u16) = { 2 }, // SubstFormat
        coverage_offset (u16), // Coverage
        class_offset    (u16), // ClassDef
        set_count       (u16), // SubClassSetCnt

        set_offsets (Vec<u16>) |this, tape, _| { // SubClassSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<ClassRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a context in format 3."]
    pub ContextSubstitution3 { // ContextSubstFormat3
        format             (u16) = { 3 }, // SubstFormat
        glyph_count        (u16), // GlyphCount
        substitution_count (u16), // SubstCount

        coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.glyph_count as usize)
        },

        substitutions (Vec<Substitution>) |this, tape, _| { // SubstLookupRecord
            tape.take_given(this.substitution_count as usize)
        },

        coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.glyph_count, this.coverage_offsets)
        },
    }
}

/// A table for substituting glyphs in a chaining context.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ChainContextSubstitution {
    /// Format 1.
    Format1(ChainContextSubstitution1),
    /// Format 2.
    Format2(ChainContextSubstitution2),
    /// Format 3.
    Format3(ChainContextSubstitution3),
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 1."]
    pub ChainContextSubstitution1 { // ChainContextSubstFormat1
        format          (u16) = { 1 }, // SubstFormat
        coverage_offset (u16), // Coverage
        set_count       (u16), // ChainSubRuleSetCount

        set_offsets (Vec<u16>) |this, tape, _| { // ChainSubRuleSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        sets (Vec<ChainRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 2."]
    pub ChainContextSubstitution2 { // ChainContextSubstFormat2
        format                (u16) = { 2 }, // SubstFormat
        coverage_offset       (u16), // Coverage
        backward_class_offset (u16), // BacktrackClassDef
        input_class_offset    (u16), // InputClassDef
        forward_class_offset  (u16), // LookaheadClassDef
        set_count             (u16), // ChainSubClassSetCnt

        set_offsets (Vec<u16>) |this, tape, _| { // ChainSubClassSet
            tape.take_given(this.set_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        backward_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.backward_class_offset)
        },

        input_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.input_class_offset)
        },

        forward_class (Class) |this, tape, position| {
            jump_take!(tape, position, this.forward_class_offset)
        },

        sets (Vec<ChainClassRuleSet>) |this, tape, position| {
            jump_take!(tape, position, this.set_count, this.set_offsets)
        },
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in a chaining context in format 3."]
    pub ChainContextSubstitution3 { // ChainContextSubstFormat3
        format               (u16) = { 3 }, // SubstFormat
        backward_glyph_count (u16), // BacktrackGlyphCount

        backward_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.backward_glyph_count as usize)
        },

        input_glyph_count (u16), // InputGlyphCount

        input_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.input_glyph_count as usize)
        },

        forward_glyph_count (u16), // LookaheadGlyphCount

        forward_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.forward_glyph_count as usize)
        },

        substitution_count (u16), // SubstCount

        substitutions (Vec<Substitution>) |this, tape, _| { // SubstLookupRecord
            tape.take_given(this.substitution_count as usize)
        },

        backward_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.backward_glyph_count, this.backward_coverage_offsets)
        },

        input_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.input_glyph_count, this.input_coverage_offsets)
        },

        forward_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.forward_glyph_count, this.forward_coverage_offsets)
        },
    }
}

table! {
    #[doc = "A table for other types of substitution."]
    pub ExtensionSubstitution { // ExtensionSubstFormat1
        format (u16) = { 1 }, // SubstFormat
        kind   (u16), // ExtensionLookupType
        offset (u32), // ExtensionOffset
    }
}

table! {
    @position
    #[doc = "A table for substituting glyphs in reverse order in a chaining context."]
    pub ReverseChainContextSubstitution { // ReverseChainSingleSubstFormat1
        format               (u16), // SubstFormat
        coverage_offset      (u16), // Coverage
        backward_glyph_count (u16), // BacktrackGlyphCount

        backward_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.backward_glyph_count as usize)
        },

        forward_glyph_count (u16), // LookaheadGlyphCount

        forward_coverage_offsets (Vec<u16>) |this, tape, _| { // Coverage
            tape.take_given(this.forward_glyph_count as usize)
        },

        glyph_count (u16), // GlyphCount

        glyph_ids (Vec<GlyphID>) |this, tape, _| { // Substitute
            tape.take_given(this.glyph_count as usize)
        },

        coverage (Coverage) |this, tape, position| {
            jump_take!(tape, position, this.coverage_offset)
        },

        backward_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.backward_glyph_count, this.backward_coverage_offsets)
        },

        forward_coverages (Vec<Coverage>) |this, tape, position| {
            jump_take!(tape, position, this.forward_glyph_count, this.forward_coverage_offsets)
        },
    }
}

impl Walue<u16> for Table {
    fn read<T: Tape>(tape: &mut T, kind: u16) -> Result<Self> {
        Ok(match kind {
            1 => Table::SingleSubstitution(try!(tape.take())),
            2 => Table::MultipleSubstitution(try!(tape.take())),
            3 => Table::AlternateSubstitution(try!(tape.take())),
            4 => Table::LigatureSubstitution(try!(tape.take())),
            5 => Table::ContextSubstitution(try!(tape.take())),
            6 => Table::ChainContextSubstitution(try!(tape.take())),
            7 => Table::ExtensionSubstitution(try!(tape.take())),
            8 => Table::ReverseChainContextSubstitution(try!(tape.take())),
            _ => raise!("found an unknown glyph-substitution type"),
        })
    }
}

impl Value for SingleSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => SingleSubstitution::Format1(try!(tape.take())),
            2 => SingleSubstitution::Format2(try!(tape.take())),
            _ => raise!("found an unknown format of the single-substitution table"),
        })
    }
}

impl Value for ContextSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => ContextSubstitution::Format1(try!(tape.take())),
            2 => ContextSubstitution::Format2(try!(tape.take())),
            3 => ContextSubstitution::Format3(try!(tape.take())),
            _ => raise!("found an unknown format of the context-substitution table"),
        })
    }
}

impl Value for ChainContextSubstitution {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u16>()) {
            1 => ChainContextSubstitution::Format1(try!(tape.take())),
            2 => ChainContextSubstitution::Format2(try!(tape.take())),
            3 => ChainContextSubstitution::Format3(try!(tape.take())),
            _ => raise!("found an unknown format of the chaining-context-substitution table"),
        })
    }
}
<|endoftext|>


<commit_before>use index::btree::internal::InNode;
use index::btree::{NodeCellRef, NodeWriteGuard, write_node, BPlusTree, NodeData};
use index::{EntryKey, Slice};
use std::fmt::Debug;

pub fn is_node_serial<KS, PS>(node: &NodeWriteGuard<KS, PS>) -> bool
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    // check keys
    for i in 0..node.len() {
        if node.keys()[i] <= smallvec!() {
            error!("EMPTY KEY DETECTED !!!");
            return false;
        }
    }
    for i in 1..node.len() {
        if node.keys()[i - 1] >= node.keys()[i] {
            error!("serial check failed for key ordering");
            return false;
        }
    }
    true
}

pub fn is_node_list_serial<KS, PS>(nodes: &Vec<NodeWriteGuard<KS, PS>>) -> bool
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    if nodes.len() == 0 {
        return true;
    }
    for (i, n) in nodes.iter().enumerate() {
        if !is_node_serial(n) {
            error!("node at {} not serial", i);
            return false;
        }
    }

    // check right ref
    for i in 0..nodes.len() - 1 {
        let first_right_bound = nodes[i].right_bound();
        let second_first_node = nodes[i + 1].first_key();
        let second_right_bound = nodes[i + 1].right_bound();
        if first_right_bound >= second_right_bound {
            error!("right bound at {} larger than right right bound", i);
            return false;
        }
        if first_right_bound > second_first_node {
            error!("right bound at {} larger than right first node", i);
            return false;
        }
    }
    return true;
}

pub fn is_node_level_serial<KS, PS>(mut node: NodeWriteGuard<KS, PS>, lsm_level: usize, tree_level: usize) -> bool
    where
        KS: Slice<EntryKey> + Debug + 'static,
        PS: Slice<NodeCellRef> + 'static,
{
    loop {
        let right_ref = node.right_ref().unwrap();
        let right_bound = node.right_bound();
        if !is_node_serial(&node) {
            error!("Node not serial - {} - {}", lsm_level, tree_level);
            return false;
        }
        if node.first_key() > right_bound {
            error!("Node have right key smaller than the first - {} - {}", lsm_level, tree_level);
            return false;
        }
        if node.last_key() > right_bound {
            error!("Node have right key smaller than the last - {} - {}", lsm_level, tree_level);
            return false;
        }
        let next = write_node(right_ref);
        if next.is_none() {
            debug!("Node level check reached non node - {} - {}", lsm_level, tree_level);
            return true;
        }
        if next.first_key() < right_bound {
            error!("next first key smaller than right bound - {} - {}", lsm_level, tree_level);
            return false;
        }
        if next.right_bound() < right_bound {
            error!("next right bound key smaller than right bound - {} - {}", lsm_level, tree_level);
            return false
        }
        node = next;
    }
}

pub fn is_tree_in_order<KS, PS>(tree: &BPlusTree<KS, PS>, level: usize) -> bool
    where
        KS: Slice<EntryKey> + Debug + 'static,
        PS: Slice<NodeCellRef> + 'static,
{
    return ensure_level_in_order::<KS, PS>(&tree.get_root(), level, 0);
}

fn ensure_level_in_order<KS, PS>(node: &NodeCellRef, lsm_level: usize, tree_level: usize) -> bool
    where
        KS: Slice<EntryKey> + Debug + 'static,
        PS: Slice<NodeCellRef> + 'static,
{
    let first_node = write_node::<KS, PS>(&node);
    let sub_ref = match &*first_node {
        &NodeData::Internal(ref n) => Some(n.ptrs.as_slice_immute()[0].clone()),
        _ => None
    };
    if !is_node_level_serial(first_node, lsm_level, tree_level) {
        return false;
    }
    if let Some(sub_level) = sub_ref {
        return ensure_level_in_order::<KS, PS>(&sub_level, lsm_level, tree_level + 1);
    }
    return true;
}<commit_msg>more information on the failing serial check<commit_after>use index::btree::internal::InNode;
use index::btree::{NodeCellRef, NodeWriteGuard, write_node, BPlusTree, NodeData};
use index::{EntryKey, Slice};
use std::fmt::Debug;

pub fn is_node_serial<KS, PS>(node: &NodeWriteGuard<KS, PS>) -> bool
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    // check keys
    for i in 0..node.len() {
        if node.keys()[i] <= smallvec!() {
            error!("EMPTY KEY DETECTED !!!");
            return false;
        }
    }
    for i in 1..node.len() {
        if node.keys()[i - 1] >= node.keys()[i] {
            error!("serial check failed for key ordering");
            return false;
        }
    }
    true
}

pub fn is_node_list_serial<KS, PS>(nodes: &Vec<NodeWriteGuard<KS, PS>>) -> bool
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    if nodes.len() == 0 {
        return true;
    }
    for (i, n) in nodes.iter().enumerate() {
        if !is_node_serial(n) {
            error!("node at {} not serial", i);
            return false;
        }
    }

    // check right ref
    for i in 0..nodes.len() - 1 {
        let first_right_bound = nodes[i].right_bound();
        let second_first_node = nodes[i + 1].first_key();
        let second_right_bound = nodes[i + 1].right_bound();
        if first_right_bound >= second_right_bound {
            error!("right bound at {} larger than right right bound", i);
            return false;
        }
        if first_right_bound > second_first_node {
            error!("right bound at {} larger than right first node", i);
            return false;
        }
    }
    return true;
}

pub fn is_node_level_serial<KS, PS>(mut node: NodeWriteGuard<KS, PS>, lsm_level: usize, tree_level: usize) -> bool
    where
        KS: Slice<EntryKey> + Debug + 'static,
        PS: Slice<NodeCellRef> + 'static,
{
    loop {
        let right_ref = node.right_ref().unwrap();
        let right_bound = node.right_bound();
        if !is_node_serial(&node) {
            error!("Node not serial - {} - {}", lsm_level, tree_level);
            return false;
        }
        if node.first_key() > right_bound {
            error!("Node have right key smaller than the first - {} - {}", lsm_level, tree_level);
            return false;
        }
        if node.last_key() > right_bound {
            error!("Node have right key smaller than the last - {} - {}", lsm_level, tree_level);
            return false;
        }
        let next = write_node(right_ref);
        if next.is_none() {
            debug!("Node level check reached non node - {} - {}", lsm_level, tree_level);
            return true;
        }
        if next.first_key() < right_bound {
            error!("next first key smaller than right bound - {} - {}, type {}. Left keys {:?}, right keys {:?}, right bound {:?}, next right bound {:?}",
                   lsm_level, tree_level, node.type_name(), node.keys(), next.keys(), node.right_bound(), next.right_bound());
            return false;
        }
        if next.right_bound() < right_bound {
            error!("next right bound key smaller than right bound - {} - {}", lsm_level, tree_level);
            return false
        }
        node = next;
    }
}

pub fn is_tree_in_order<KS, PS>(tree: &BPlusTree<KS, PS>, level: usize) -> bool
    where
        KS: Slice<EntryKey> + Debug + 'static,
        PS: Slice<NodeCellRef> + 'static,
{
    return ensure_level_in_order::<KS, PS>(&tree.get_root(), level, 0);
}

fn ensure_level_in_order<KS, PS>(node: &NodeCellRef, lsm_level: usize, tree_level: usize) -> bool
    where
        KS: Slice<EntryKey> + Debug + 'static,
        PS: Slice<NodeCellRef> + 'static,
{
    let first_node = write_node::<KS, PS>(&node);
    let sub_ref = match &*first_node {
        &NodeData::Internal(ref n) => Some(n.ptrs.as_slice_immute()[0].clone()),
        _ => None
    };
    if !is_node_level_serial(first_node, lsm_level, tree_level) {
        return false;
    }
    if let Some(sub_level) = sub_ref {
        return ensure_level_in_order::<KS, PS>(&sub_level, lsm_level, tree_level + 1);
    }
    return true;
}<|endoftext|>