<commit_before>use std::fs::File;
use std::io::Read;
use std::env;
use std::path::{Path,PathBuf};
use util::{path_exists, is_dir};
use std::fs::{read_dir};
use toml;

// otry is 'option try'
macro_rules! otry {
    ($e:expr) => (match $e { Some(e) => e, None => return None })
}

// converts errors into None
macro_rules! otry2 {
    ($e:expr) => (match $e { Ok(e) => e, Err(e) => { error!("ERROR!: {:?} {} {}", e, file!(), line!()); return None } })
}

// converts errors into message + empty vec
macro_rules! vectry {
    ($e:expr) => (match $e { Ok(e) => e, Err(e) => { error!("ERROR!: {:?} {} {}", e, file!(), line!()); return Vec::new() } })
}

fn find_src_via_lockfile(kratename: &str, cargofile: &Path) -> Option<PathBuf> {
    let mut file = otry2!(File::open(cargofile));
    let mut string = String::new();
    otry2!(file.read_to_string(&mut string));
    let mut parser = toml::Parser::new(&string);
    let lock_table = parser.parse().unwrap();

    debug!("find_src_via_lockfile found lock table {:?}", lock_table);

    let t = match lock_table.get("package") {
        Some(&toml::Value::Array(ref t1)) => t1,
        _ => return None
    };

    for item in t {
        if let &toml::Value::Table(ref t) = item {
            if let Some(&toml::Value::String(ref name)) = t.get("name") {
                if name.replace("-", "_") == kratename {
                    debug!("found matching crate {:?}", t);
                    let version = otry!(getstr(t, "version"));
                    let source = otry!(getstr(t, "source"));

                    if Some("registry") == source.split("+").nth(0) {
                        return get_versioned_cratefile(name, &version, cargofile);
                    } else if Some("git") == source.split("+").nth(0) {
                        let sha1 = otry!(source.split("#").last());
                        let mut d = otry!(get_cargo_rootdir(cargofile));
                        d.push("git");
                        d.push("checkouts");
                        d = otry!(find_git_src_dir(d, name, &sha1));
                        d.push("src");
                        d.push("lib.rs");
                        return Some(d);
                    }
                }
            }
        }
    }
    None
}

fn get_cargo_rootdir(cargofile: &Path) -> Option<PathBuf> {
    debug!("get_cargo_rootdir. {:?}",cargofile);
    match env::var_os("CARGO_HOME") {
        Some(cargohome) =>
        {
            debug!("get_cargo_rootdir. CARGO_HOME is set: {:?}",cargohome);
            let d = PathBuf::from(cargohome);
            if path_exists(&d) {
                return Some(d)
            } else {
                return None
            };
        },
        None => ()
    };

    let mut d = otry!(env::home_dir());

    // try multirust first, since people with multirust installed will often still
    // have an old .cargo directory lying around
    d.push(".multirust");

    d.push("overrides");
    debug!("get_cargo_rootdir: looking for overrides file. {:?}", d);
    if let Ok(mut multirust_overides) = File::open(&d) {
        debug!("get_cargo_rootdir: found overrides file! {:?}", d);
        let mut s = String::new();
        otry2!(multirust_overides.read_to_string(&mut s));
        for line in s.lines() {
            let overridepath = line.split(";").nth(0).unwrap();
            if cargofile.starts_with(overridepath) {
                let overridepath = line.split(";").nth(1).unwrap();
                d.pop();
                d.push("toolchains");
                d.push(overridepath.trim());
                d.push("cargo");
                debug!("get_cargo_rootdir override root is {:?}",d);
                return Some(d)
            }
        }
    }

    d.pop();
    d.push("default");
    if let Ok(mut multirustdefault) = File::open(&d) {
        let mut s = String::new();
        otry2!(multirustdefault.read_to_string(&mut s));
        d.pop();
        d.push("toolchains");
        d.push(s.trim());
        d.push("cargo");
        debug!("get_cargo_rootdir root is {:?}",d);
        return Some(d)
    }

    d.pop();
    d.pop();
    d.push(".cargo");
    if path_exists(&d) {
        Some(d)
    } else {
        None
    }
}

fn get_versioned_cratefile(kratename: &str, version: &str, cargofile: &Path) -> Option<PathBuf> {
    let mut d = otry!(get_cargo_rootdir(cargofile));

    debug!("get_versioned_cratefile: cargo rootdir is {:?}",d);
    d.push("registry");
    d.push("src");

    for mut d in find_cratesio_src_dirs(d) {

        // if version=* then search for the first matching folder
        if version == "*" {
            use std::fs::read_dir;
            let mut start_path = d.clone();
            start_path.push(kratename);
            let start_name = start_path.to_str().unwrap();

            if let Ok(reader) = read_dir(d) {
                if let Some(path) = reader
                    .map(|entry| entry.unwrap().path())
                    .find(|path| path.to_str().unwrap().starts_with(start_name)) {
                        d = path.clone();
                    } else {
                        continue;
                    }
            } else {
                continue;
            }
        } else {
            d.push(kratename.to_owned() + "-" + &version);
        }

        d.push("src");
        debug!("crate path {:?}",d);

        // First, check for package name at root (src/kratename/lib.rs)
        d.push(kratename);
        d.push("lib.rs");
        if let Err(_) = File::open(&d) {
            // It doesn't exist, so assume src/lib.rs
            d.pop();
            d.pop();
            d.push("lib.rs");
        }
        debug!("crate path with lib.rs {:?}",d);

        if let Err(_) = File::open(&d) {
            return continue;
        }

        return Some(d)
    }
    None
 }

fn find_src_via_tomlfile(kratename: &str, cargofile: &Path) -> Option<PathBuf> {
    // only look for 'path' references here.
    // We find the git and crates.io stuff via the lockfile

    let mut file = otry2!(File::open(cargofile));
    let mut string = String::new();
    otry2!(file.read_to_string(&mut string));
    let mut parser = toml::Parser::new(&string);
    let table = otry!(parser.parse());

    // is it this lib?  (e.g. you're searching from tests to find the main library crate)
    if let Some(&toml::Value::Table(ref t)) = table.get("lib") {
        if let Some(&toml::Value::String(ref name)) = t.get("name") {
            if name == kratename {
                debug!("found {} as lib entry in Cargo.toml", kratename);
                if let Some(&toml::Value::String(ref pathstr)) = t.get("path") {
                    let p = Path::new(pathstr);
                    let libpath = otry!(cargofile.parent()).join(p);
                    return Some(libpath);
                }
            }
        }
    }

    // otherwise search the dependencies
    let t = match table.get("dependencies") {
        Some(&toml::Value::Table(ref t)) => t,
        _ => return None
    };

    let mut name = kratename;
    let value = if kratename.contains('_') {
        t.iter().find(|&(k, _)| k.replace("-", "_") == name).map(|(k,v)| {
            name = k;
            v
        })
    } else {
        t.get(kratename)
    };

    match value {
        Some(&toml::Value::Table(ref t)) => {
            // local directory
            let relative_path = otry!(getstr(t, "path"));
            Some(otry!(cargofile.parent())
                 .join(relative_path)
                 .join("src")
                 .join("lib.rs"))
        },
        Some(&toml::Value::String(ref version)) => {
            // versioned crate
            get_versioned_cratefile(name, version, cargofile)
        }
        _ => None
    }
}

fn find_cratesio_src_dirs(d: PathBuf) -> Vec<PathBuf> {
    let mut out = Vec::new();
    for entry in vectry!(read_dir(d)) {
        let path = vectry!(entry).path();
        if is_dir(path.as_path()) {
            if let Some(ref fname) = path.file_name().and_then(|s| s.to_str()) {
                if fname.starts_with("github.com-") {
                    out.push(path.clone());
                }
            }
        }
    }
    out
}

fn find_git_src_dir(d: PathBuf, name: &str, sha1: &str) -> Option<PathBuf> {
    for entry in otry2!(read_dir(d)) {
        let path = otry2!(entry).path();
        if is_dir(path.as_path()) {
            if let Some(ref fname) = path.file_name().and_then(|s| s.to_str()) {
                if fname.starts_with(name) {
                    let mut d = path.clone();

                    // dirname can be the sha1 or master.
                    d.push(sha1);

                    if !is_dir(d.as_path()) {
                        d.pop();
                        d.push("master");
                    }

                    let retval = d.clone();

                    // check that the checkout matches the commit sha1
                    d.push(".git");
                    d.push("refs");
                    d.push("heads");
                    d.push("master");

                    let mut headref = String::new();
                    otry2!(otry2!(File::open(d)).read_to_string(&mut headref));

                    debug!("git headref is {:?}", headref);

                    if headref.ends_with("\n") {
                        headref.pop();
                    }

                    if sha1 == headref {
                        return Some(retval);
                    }
                }
            }
        }
    }
    None
}

fn getstr(t: &toml::Table, k: &str) -> Option<String> {
    match t.get(k) {
        Some(&toml::Value::String(ref s)) => Some(s.clone()),
        _ => None
    }
}

fn find_cargo_tomlfile(currentfile: &Path) -> Option<PathBuf> {
    let mut f = currentfile.to_path_buf();
    f.push("Cargo.toml");
    if path_exists(f.as_path()) {
        Some(f)
    } else {
        if f.pop() && f.pop() {
            find_cargo_tomlfile(&f)
        } else {
            None
        }
    }
}

pub fn get_crate_file(kratename: &str, from_path: &Path) -> Option<PathBuf> {
    if let Some(tomlfile) = find_cargo_tomlfile(from_path) {
        // look in the lockfile first, if there is one
        debug!("get_crate_file tomlfile is {:?}", tomlfile);
        let mut lockfile = tomlfile.clone();
        lockfile.pop();
        lockfile.push("Cargo.lock");
        if path_exists(lockfile.as_path()) {
            if let Some(f) = find_src_via_lockfile(kratename, &lockfile) {
                return Some(f);
            }
        }

        // oh, no luck with the lockfile. Try the tomlfile
        return find_src_via_tomlfile(kratename, &tomlfile)
    }
    None
}
<commit_msg>search /lib.rs for cargo roots<commit_after>use std::fs::File;
use std::io::Read;
use std::env;
use std::path::{Path,PathBuf};
use util::{path_exists, is_dir};
use std::fs::{read_dir};
use toml;

// otry is 'option try'
macro_rules! otry {
    ($e:expr) => (match $e { Some(e) => e, None => return None })
}

// converts errors into None
macro_rules! otry2 {
    ($e:expr) => (match $e { Ok(e) => e, Err(e) => { error!("ERROR!: {:?} {} {}", e, file!(), line!()); return None } })
}

// converts errors into message + empty vec
macro_rules! vectry {
    ($e:expr) => (match $e { Ok(e) => e, Err(e) => { error!("ERROR!: {:?} {} {}", e, file!(), line!()); return Vec::new() } })
}

fn find_src_via_lockfile(kratename: &str, cargofile: &Path) -> Option<PathBuf> {
    let mut file = otry2!(File::open(cargofile));
    let mut string = String::new();
    otry2!(file.read_to_string(&mut string));
    let mut parser = toml::Parser::new(&string);
    let lock_table = parser.parse().unwrap();

    debug!("find_src_via_lockfile found lock table {:?}", lock_table);

    let t = match lock_table.get("package") {
        Some(&toml::Value::Array(ref t1)) => t1,
        _ => return None
    };

    for item in t {
        if let &toml::Value::Table(ref t) = item {
            if let Some(&toml::Value::String(ref name)) = t.get("name") {
                if name.replace("-", "_") == kratename {
                    debug!("found matching crate {:?}", t);
                    let version = otry!(getstr(t, "version"));
                    let source = otry!(getstr(t, "source"));

                    if Some("registry") == source.split("+").nth(0) {
                        return get_versioned_cratefile(name, &version, cargofile);
                    } else if Some("git") == source.split("+").nth(0) {
                        let sha1 = otry!(source.split("#").last());
                        let mut d = otry!(get_cargo_rootdir(cargofile));
                        d.push("git");
                        d.push("checkouts");
                        d = otry!(find_git_src_dir(d, name, &sha1));
                        d.push("src");
                        d.push("lib.rs");
                        return Some(d);
                    }
                }
            }
        }
    }
    None
}

fn get_cargo_rootdir(cargofile: &Path) -> Option<PathBuf> {
    debug!("get_cargo_rootdir. {:?}",cargofile);
    match env::var_os("CARGO_HOME") {
        Some(cargohome) =>
        {
            debug!("get_cargo_rootdir. CARGO_HOME is set: {:?}",cargohome);
            let d = PathBuf::from(cargohome);
            if path_exists(&d) {
                return Some(d)
            } else {
                return None
            };
        },
        None => ()
    };

    let mut d = otry!(env::home_dir());

    // try multirust first, since people with multirust installed will often still
    // have an old .cargo directory lying around
    d.push(".multirust");

    d.push("overrides");
    debug!("get_cargo_rootdir: looking for overrides file. {:?}", d);
    if let Ok(mut multirust_overides) = File::open(&d) {
        debug!("get_cargo_rootdir: found overrides file! {:?}", d);
        let mut s = String::new();
        otry2!(multirust_overides.read_to_string(&mut s));
        for line in s.lines() {
            let overridepath = line.split(";").nth(0).unwrap();
            if cargofile.starts_with(overridepath) {
                let overridepath = line.split(";").nth(1).unwrap();
                d.pop();
                d.push("toolchains");
                d.push(overridepath.trim());
                d.push("cargo");
                debug!("get_cargo_rootdir override root is {:?}",d);
                return Some(d)
            }
        }
    }

    d.pop();
    d.push("default");
    if let Ok(mut multirustdefault) = File::open(&d) {
        let mut s = String::new();
        otry2!(multirustdefault.read_to_string(&mut s));
        d.pop();
        d.push("toolchains");
        d.push(s.trim());
        d.push("cargo");
        debug!("get_cargo_rootdir root is {:?}",d);
        return Some(d)
    }

    d.pop();
    d.pop();
    d.push(".cargo");
    if path_exists(&d) {
        Some(d)
    } else {
        None
    }
}

fn get_versioned_cratefile(kratename: &str, version: &str, cargofile: &Path) -> Option<PathBuf> {
    let mut d = otry!(get_cargo_rootdir(cargofile));

    debug!("get_versioned_cratefile: cargo rootdir is {:?}",d);
    d.push("registry");
    d.push("src");

    for mut d in find_cratesio_src_dirs(d) {

        // if version=* then search for the first matching folder
        if version == "*" {
            use std::fs::read_dir;
            let mut start_path = d.clone();
            start_path.push(kratename);
            let start_name = start_path.to_str().unwrap();

            if let Ok(reader) = read_dir(d) {
                if let Some(path) = reader
                    .map(|entry| entry.unwrap().path())
                    .find(|path| path.to_str().unwrap().starts_with(start_name)) {
                        d = path.clone();
                    } else {
                        continue;
                    }
            } else {
                continue;
            }
        } else {
            d.push(kratename.to_owned() + "-" + &version);
        }

        d.push("src");
        debug!("crate path {:?}",d);

        // First, check for package name at root (src/kratename/lib.rs)
        d.push(kratename);
        d.push("lib.rs");
        if let Err(_) = File::open(&d) {
            // It doesn't exist, so assume src/lib.rs
            d.pop();
            d.pop();
            d.push("lib.rs");
        }
        debug!("crate path with lib.rs {:?}",d);

        if let Err(_) = File::open(&d) {
            // It doesn't exist, so try /lib.rs
            d.pop();
            d.pop();
            d.push("lib.rs");
        }

        if let Err(_) = File::open(&d) {
            continue;
        }

        return Some(d)
    }
    None
 }

fn find_src_via_tomlfile(kratename: &str, cargofile: &Path) -> Option<PathBuf> {
    // only look for 'path' references here.
    // We find the git and crates.io stuff via the lockfile

    let mut file = otry2!(File::open(cargofile));
    let mut string = String::new();
    otry2!(file.read_to_string(&mut string));
    let mut parser = toml::Parser::new(&string);
    let table = otry!(parser.parse());

    // is it this lib?  (e.g. you're searching from tests to find the main library crate)
    if let Some(&toml::Value::Table(ref t)) = table.get("lib") {
        if let Some(&toml::Value::String(ref name)) = t.get("name") {
            if name == kratename {
                debug!("found {} as lib entry in Cargo.toml", kratename);
                if let Some(&toml::Value::String(ref pathstr)) = t.get("path") {
                    let p = Path::new(pathstr);
                    let libpath = otry!(cargofile.parent()).join(p);
                    return Some(libpath);
                }
            }
        }
    }

    // otherwise search the dependencies
    let t = match table.get("dependencies") {
        Some(&toml::Value::Table(ref t)) => t,
        _ => return None
    };

    let mut name = kratename;
    let value = if kratename.contains('_') {
        t.iter().find(|&(k, _)| k.replace("-", "_") == name).map(|(k,v)| {
            name = k;
            v
        })
    } else {
        t.get(kratename)
    };

    match value {
        Some(&toml::Value::Table(ref t)) => {
            // local directory
            let relative_path = otry!(getstr(t, "path"));
            Some(otry!(cargofile.parent())
                 .join(relative_path)
                 .join("src")
                 .join("lib.rs"))
        },
        Some(&toml::Value::String(ref version)) => {
            // versioned crate
            get_versioned_cratefile(name, version, cargofile)
        }
        _ => None
    }
}

fn find_cratesio_src_dirs(d: PathBuf) -> Vec<PathBuf> {
    let mut out = Vec::new();
    for entry in vectry!(read_dir(d)) {
        let path = vectry!(entry).path();
        if is_dir(path.as_path()) {
            if let Some(ref fname) = path.file_name().and_then(|s| s.to_str()) {
                if fname.starts_with("github.com-") {
                    out.push(path.clone());
                }
            }
        }
    }
    out
}

fn find_git_src_dir(d: PathBuf, name: &str, sha1: &str) -> Option<PathBuf> {
    for entry in otry2!(read_dir(d)) {
        let path = otry2!(entry).path();
        if is_dir(path.as_path()) {
            if let Some(ref fname) = path.file_name().and_then(|s| s.to_str()) {
                if fname.starts_with(name) {
                    let mut d = path.clone();

                    // dirname can be the sha1 or master.
                    d.push(sha1);

                    if !is_dir(d.as_path()) {
                        d.pop();
                        d.push("master");
                    }

                    let retval = d.clone();

                    // check that the checkout matches the commit sha1
                    d.push(".git");
                    d.push("refs");
                    d.push("heads");
                    d.push("master");

                    let mut headref = String::new();
                    otry2!(otry2!(File::open(d)).read_to_string(&mut headref));

                    debug!("git headref is {:?}", headref);

                    if headref.ends_with("\n") {
                        headref.pop();
                    }

                    if sha1 == headref {
                        return Some(retval);
                    }
                }
            }
        }
    }
    None
}

fn getstr(t: &toml::Table, k: &str) -> Option<String> {
    match t.get(k) {
        Some(&toml::Value::String(ref s)) => Some(s.clone()),
        _ => None
    }
}

fn find_cargo_tomlfile(currentfile: &Path) -> Option<PathBuf> {
    let mut f = currentfile.to_path_buf();
    f.push("Cargo.toml");
    if path_exists(f.as_path()) {
        Some(f)
    } else {
        if f.pop() && f.pop() {
            find_cargo_tomlfile(&f)
        } else {
            None
        }
    }
}

pub fn get_crate_file(kratename: &str, from_path: &Path) -> Option<PathBuf> {
    if let Some(tomlfile) = find_cargo_tomlfile(from_path) {
        // look in the lockfile first, if there is one
        debug!("get_crate_file tomlfile is {:?}", tomlfile);
        let mut lockfile = tomlfile.clone();
        lockfile.pop();
        lockfile.push("Cargo.lock");
        if path_exists(lockfile.as_path()) {
            if let Some(f) = find_src_via_lockfile(kratename, &lockfile) {
                return Some(f);
            }
        }

        // oh, no luck with the lockfile. Try the tomlfile
        return find_src_via_tomlfile(kratename, &tomlfile)
    }
    None
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016-2019 Ben Ashford
 *
 * 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,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Implementation of ElasticSearch Mapping operation

//!
//! Please note: this will grow and become a full implementation of the ElasticSearch
//! [Indices API](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices.html)
//! so subtle (potentially breaking) changes will be made to the API when that happens

use std::collections::HashMap;
use std::hash::Hash;

use reqwest::StatusCode;

use serde::Serialize;
use serde_json::{Map, Value};

use crate::{error::EsError, operations::GenericResult, Client, EsResponse};

pub type DocType<'a> = HashMap<&'a str, HashMap<&'a str, &'a str>>;
pub type Mapping<'a> = HashMap<&'a str, DocType<'a>>;

#[derive(Debug, Serialize)]
pub struct Settings {
    pub number_of_shards: u32,
    pub analysis: Analysis,
}

#[derive(Debug, Serialize)]
pub struct Analysis {
    pub filter: Map<String, Value>,
    pub analyzer: Map<String, Value>,
}

/// An indexing operation
#[derive(Debug)]
pub struct MappingOperation<'a, 'b> {
    /// The HTTP client that this operation will use
    client: &'a mut Client,

    /// The index that will be created and eventually mapped
    index: &'b str,

    /// A map containing the doc types and their mapping
    mapping: Option<&'b Mapping<'b>>,

    /// A struct reflecting the settings that enable the
    /// customization of analyzers
    settings: Option<&'b Settings>,
}

impl<'a, 'b> MappingOperation<'a, 'b> {
    pub fn new(client: &'a mut Client, index: &'b str) -> MappingOperation<'a, 'b> {
        MappingOperation {
            client,
            index,
            mapping: None,
            settings: None,
        }
    }

    /// Set the actual mapping
    pub fn with_mapping(&'b mut self, mapping: &'b Mapping) -> &'b mut Self {
        self.mapping = Some(mapping);
        self
    }

    /// Set the settings
    pub fn with_settings(&'b mut self, settings: &'b Settings) -> &'b mut Self {
        self.settings = Some(settings);
        self
    }

    /// If settings have been provided, the index will be created with them. If the index already
    /// exists, an `Err(EsError)` will be returned.
    /// If mapping have been set too, the properties will be applied. The index will be unavailable
    /// during this process.
    /// Nothing will be done if either mapping and settings are not present.
    pub fn send(&'b mut self) -> Result<MappingResult, EsError> {
        // Return earlier if there is nothing to do
        if self.mapping.is_none() && self.settings.is_none() {
            return Ok(MappingResult);
        }

        if self.settings.is_some() {
            let body = hashmap("settings", self.settings.unwrap());
            let url = self.index.to_owned();
            let _ = self.client.put_body_op(&url, &body)?;

            let _ = self.client.wait_for_status("yellow", "5s");
        }

        if self.mapping.is_some() {
            let _ = self.client.close_index(self.index);

            for (entity, properties) in self.mapping.unwrap().iter() {
                let body = hashmap("properties", properties);
                let url = format!("{}/_mapping/{}", self.index, entity);
                let _ = self.client.put_body_op(&url, &body)?;
            }

            let _ = self.client.open_index(self.index);
        }

        Ok(MappingResult)
    }
}

impl Client {
    /// Open the index, making it available.
    pub fn open_index<'a>(&'a mut self, index: &'a str) -> Result<GenericResult, EsError> {
        let url = format!("{}/_open", index);
        let response = self.post_op(&url)?;

        match response.status_code() {
            StatusCode::OK => Ok(response.read_response()?),
            status_code => Err(EsError::EsError(format!(
                "Unexpected status: {}",
                status_code
            ))),
        }
    }

    /// Close the index, making it unavailable and modifiable.
    pub fn close_index<'a>(&'a mut self, index: &'a str) -> Result<GenericResult, EsError> {
        let url = format!("{}/_close", index);
        let response = self.post_op(&url)?;

        match response.status_code() {
            StatusCode::OK => Ok(response.read_response()?),
            status_code => Err(EsError::EsError(format!(
                "Unexpected status: {}",
                status_code
            ))),
        }
    }

    /// TODO: Return proper health data from
    /// https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-health.html
    pub fn wait_for_status<'a>(
        &'a mut self,
        status: &'a str,
        timeout: &'a str,
    ) -> Result<(), EsError> {
        let url = format!(
            "_cluster/health?wait_for_status={}&timeout={}",
            status, timeout
        );
        let response = self.get_op(&url)?;

        match response.status_code() {
            StatusCode::OK => Ok(()),
            status_code => Err(EsError::EsError(format!(
                "Unexpected status: {}",
                status_code
            ))),
        }
    }
}

/// The result of a mapping operation
#[derive(Debug)]
pub struct MappingResult;

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

    #[derive(Debug, Serialize)]
    pub struct Author {
        pub name: String,
    }

    #[test]
    fn test_mapping() {
        let index_name = "tests_test_mapping";
        let mut client = crate::tests::make_client();

        // TODO - this fails in many cases (specifically on TravisCI), but we ignore the
        // failures anyway
        let _ = client.delete_index(index_name);

        let mapping = hashmap2(
            "post",
            hashmap2(
                "created_at",
                hashmap2("type", "date", "format", "date_time"),
                "title",
                hashmap2("type", "string", "index", "not_analyzed"),
            ),
            "author",
            hashmap("name", hashmap("type", "string")),
        );

        let settings = Settings {
            number_of_shards: 1,

            analysis: Analysis {
                filter: serde_json::json! ({
                    "autocomplete_filter": {
                        "type": "edge_ngram",
                        "min_gram": 1,
                        "max_gram": 2,
                    }
                })
                .as_object()
                .expect("by construction 'autocomplete_filter' should be a map")
                .clone(),
                analyzer: serde_json::json! ({
                    "autocomplete": {
                        "type": "custom",
                        "tokenizer": "standard",
                        "filter": [ "lowercase", "autocomplete_filter"]
                    }
                })
                .as_object()
                .expect("by construction 'autocomplete' should be a map")
                .clone(),
            },
        };

        // TODO add appropriate functions to the `Client` struct
        let result = MappingOperation::new(&mut client, index_name)
            .with_mapping(&mapping)
            .with_settings(&settings)
            .send();
        assert!(result.is_ok());

        {
            let result_wrapped = client
                .index(index_name, "post")
                .with_doc(&Author {
                    name: "Homu".to_owned(),
                })
                .send();

            assert!(result_wrapped.is_ok());

            let result = result_wrapped.unwrap();
            assert!(result.created);
        }
    }
}

fn hashmap<K, V>(k: K, v: V) -> HashMap<K, V>
where
    K: Eq + Hash,
{
    let mut m = HashMap::with_capacity(1);
    m.insert(k, v);
    m
}

#[allow(dead_code)]
fn hashmap2<K, V>(k1: K, v1: V, k2: K, v2: V) -> HashMap<K, V>
where
    K: Eq + Hash,
{
    let mut m = HashMap::with_capacity(2);
    m.insert(k1, v1);
    m.insert(k2, v2);
    m
}
<commit_msg>Allow setting custom tokenizers and char_filters in mappings<commit_after>/*
 * Copyright 2016-2019 Ben Ashford
 *
 * 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,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Implementation of ElasticSearch Mapping operation

//!
//! Please note: this will grow and become a full implementation of the ElasticSearch
//! [Indices API](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices.html)
//! so subtle (potentially breaking) changes will be made to the API when that happens

use std::collections::HashMap;
use std::hash::Hash;

use reqwest::StatusCode;

use serde::Serialize;
use serde_json::{Map, Value};

use crate::{error::EsError, operations::GenericResult, Client, EsResponse};

pub type DocType<'a> = HashMap<&'a str, HashMap<&'a str, &'a str>>;
pub type Mapping<'a> = HashMap<&'a str, DocType<'a>>;

#[derive(Debug, Serialize)]
pub struct Settings {
    pub number_of_shards: u32,
    pub analysis: Analysis,
}

#[derive(Debug, Serialize, Default)]
pub struct Analysis {
    pub filter: Map<String, Value>,
    pub analyzer: Map<String, Value>,
    pub tokenizer: Map<String, Value>,
    pub char_filter: Map<String, Value>,
}

/// An indexing operation
#[derive(Debug)]
pub struct MappingOperation<'a, 'b> {
    /// The HTTP client that this operation will use
    client: &'a mut Client,

    /// The index that will be created and eventually mapped
    index: &'b str,

    /// A map containing the doc types and their mapping
    mapping: Option<&'b Mapping<'b>>,

    /// A struct reflecting the settings that enable the
    /// customization of analyzers
    settings: Option<&'b Settings>,
}

impl<'a, 'b> MappingOperation<'a, 'b> {
    pub fn new(client: &'a mut Client, index: &'b str) -> MappingOperation<'a, 'b> {
        MappingOperation {
            client,
            index,
            mapping: None,
            settings: None,
        }
    }

    /// Set the actual mapping
    pub fn with_mapping(&'b mut self, mapping: &'b Mapping) -> &'b mut Self {
        self.mapping = Some(mapping);
        self
    }

    /// Set the settings
    pub fn with_settings(&'b mut self, settings: &'b Settings) -> &'b mut Self {
        self.settings = Some(settings);
        self
    }

    /// If settings have been provided, the index will be created with them. If the index already
    /// exists, an `Err(EsError)` will be returned.
    /// If mapping have been set too, the properties will be applied. The index will be unavailable
    /// during this process.
    /// Nothing will be done if either mapping and settings are not present.
    pub fn send(&'b mut self) -> Result<MappingResult, EsError> {
        // Return earlier if there is nothing to do
        if self.mapping.is_none() && self.settings.is_none() {
            return Ok(MappingResult);
        }

        if self.settings.is_some() {
            let body = hashmap("settings", self.settings.unwrap());
            let url = self.index.to_owned();
            let _ = self.client.put_body_op(&url, &body)?;

            let _ = self.client.wait_for_status("yellow", "5s");
        }

        if self.mapping.is_some() {
            let _ = self.client.close_index(self.index);

            for (entity, properties) in self.mapping.unwrap().iter() {
                let body = hashmap("properties", properties);
                let url = format!("{}/_mapping/{}", self.index, entity);
                let _ = self.client.put_body_op(&url, &body)?;
            }

            let _ = self.client.open_index(self.index);
        }

        Ok(MappingResult)
    }
}

impl Client {
    /// Open the index, making it available.
    pub fn open_index<'a>(&'a mut self, index: &'a str) -> Result<GenericResult, EsError> {
        let url = format!("{}/_open", index);
        let response = self.post_op(&url)?;

        match response.status_code() {
            StatusCode::OK => Ok(response.read_response()?),
            status_code => Err(EsError::EsError(format!(
                "Unexpected status: {}",
                status_code
            ))),
        }
    }

    /// Close the index, making it unavailable and modifiable.
    pub fn close_index<'a>(&'a mut self, index: &'a str) -> Result<GenericResult, EsError> {
        let url = format!("{}/_close", index);
        let response = self.post_op(&url)?;

        match response.status_code() {
            StatusCode::OK => Ok(response.read_response()?),
            status_code => Err(EsError::EsError(format!(
                "Unexpected status: {}",
                status_code
            ))),
        }
    }

    /// TODO: Return proper health data from
    /// https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-health.html
    pub fn wait_for_status<'a>(
        &'a mut self,
        status: &'a str,
        timeout: &'a str,
    ) -> Result<(), EsError> {
        let url = format!(
            "_cluster/health?wait_for_status={}&timeout={}",
            status, timeout
        );
        let response = self.get_op(&url)?;

        match response.status_code() {
            StatusCode::OK => Ok(()),
            status_code => Err(EsError::EsError(format!(
                "Unexpected status: {}",
                status_code
            ))),
        }
    }
}

/// The result of a mapping operation
#[derive(Debug)]
pub struct MappingResult;

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

    #[derive(Debug, Serialize)]
    pub struct Author {
        pub name: String,
    }

    #[test]
    fn test_mapping() {
        let index_name = "tests_test_mapping";
        let mut client = crate::tests::make_client();

        // TODO - this fails in many cases (specifically on TravisCI), but we ignore the
        // failures anyway
        let _ = client.delete_index(index_name);

        let mapping = hashmap2(
            "post",
            hashmap2(
                "created_at",
                hashmap2("type", "date", "format", "date_time"),
                "title",
                hashmap2("type", "string", "index", "not_analyzed"),
            ),
            "author",
            hashmap("name", hashmap("type", "string")),
        );

        let settings = Settings {
            number_of_shards: 1,

            analysis: Analysis {
                filter: serde_json::json! ({
                    "autocomplete_filter": {
                        "type": "edge_ngram",
                        "min_gram": 1,
                        "max_gram": 2,
                    }
                })
                .as_object()
                .expect("by construction 'autocomplete_filter' should be a map")
                .clone(),
                analyzer: serde_json::json! ({
                    "autocomplete": {
                        "type": "custom",
                        "tokenizer": "standard",
                        "filter": [ "lowercase", "autocomplete_filter"]
                    }
                })
                .as_object()
                .expect("by construction 'autocomplete' should be a map")
                .clone(),
                char_filter: serde_json::json! ({
                    "char_filter": {
                        "type": "pattern_replace",
                        "pattern": ",",
                        "replacement": " "
                    }
                })
                .as_object()
                .expect("by construction 'char_filter' should be a map")
                .clone(),
                tokenizer: serde_json::json! ({
                })
                .as_object()
                .expect("by construction 'empty tokenizer' should be a map")
                .clone(),
            },
        };

        // TODO add appropriate functions to the `Client` struct
        let result = MappingOperation::new(&mut client, index_name)
            .with_mapping(&mapping)
            .with_settings(&settings)
            .send();
        assert!(result.is_ok());

        {
            let result_wrapped = client
                .index(index_name, "post")
                .with_doc(&Author {
                    name: "Homu".to_owned(),
                })
                .send();

            assert!(result_wrapped.is_ok());

            let result = result_wrapped.unwrap();
            assert!(result.created);
        }
    }
}

fn hashmap<K, V>(k: K, v: V) -> HashMap<K, V>
where
    K: Eq + Hash,
{
    let mut m = HashMap::with_capacity(1);
    m.insert(k, v);
    m
}

#[allow(dead_code)]
fn hashmap2<K, V>(k1: K, v1: V, k2: K, v2: V) -> HashMap<K, V>
where
    K: Eq + Hash,
{
    let mut m = HashMap::with_capacity(2);
    m.insert(k1, v1);
    m.insert(k2, v2);
    m
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, BTreeSet, HashSet};
use std::ops::Not;

use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::ir::{Opcode, InstructionData, ValueDef};
use cranelift_codegen::ir::immediates::Offset32;

use crate::prelude::*;

/// Workaround for `StackSlot` not implementing `Ord`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct OrdStackSlot(StackSlot);

impl PartialOrd for OrdStackSlot {
    fn partial_cmp(&self, rhs: &Self) -> Option<std::cmp::Ordering> {
        self.0.as_u32().partial_cmp(&rhs.0.as_u32())
    }
}

impl Ord for OrdStackSlot {
    fn cmp(&self, rhs: &Self) -> std::cmp::Ordering {
        self.0.as_u32().cmp(&rhs.0.as_u32())
    }
}

#[derive(Debug, Default)]
struct StackSlotUsage {
    stack_addr: HashSet<Inst>,
    stack_load: HashSet<Inst>,
    stack_store: HashSet<Inst>,
}

pub(super) fn optimize_function(
    func: &mut Function,
    clif_comments: &mut crate::pretty_clif::CommentWriter,
    name: String, // FIXME remove
) {
    combine_stack_addr_with_load_store(func);

    // Record all stack_addr, stack_load and stack_store instructions.
    let mut stack_slot_usage_map = BTreeMap::<OrdStackSlot, StackSlotUsage>::new();

    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                InstructionData::StackLoad {
                    opcode: Opcode::StackAddr,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_addr.insert(inst);
                }
                InstructionData::StackLoad {
                    opcode: Opcode::StackLoad,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_load.insert(inst);
                }
                InstructionData::StackStore {
                    opcode: Opcode::StackStore,
                    arg: _,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_store.insert(inst);
                }
                _ => {}
            }
        }
    }

    println!("{}:\nstack slot usage: {:?}", name, stack_slot_usage_map);

    remove_unused_stack_addr_and_stack_load(func, &mut stack_slot_usage_map);

    println!("stack slot usage (after): {:?}", stack_slot_usage_map);

    for (stack_slot, users) in stack_slot_usage_map.iter_mut() {
        if users.stack_addr.is_empty().not() {
            // Stack addr leaked; there may be unknown loads and stores.
            // FIXME use stacked borrows to optimize
            continue;
        }

        let is_loaded = users.stack_load.is_empty().not();
        let is_stored = users.stack_store.is_empty().not();
        match (is_loaded, is_stored) {
            (true, true) => {} // FIXME perform store to load optimization
            (true, false) => println!("[{}] [BUG?] Reading uninitialized memory", name),
            (false, _) => {
                // Never loaded; can safely remove all stores and the stack slot.
                for user in users.stack_store.drain() {
                    println!("[{}] Remove dead stack store {} of {}", name, user, stack_slot.0);
                    func.dfg.replace(user).nop();
                }

                // FIXME make stack_slot zero sized.
            }
        }
    }

    println!();
}

fn combine_stack_addr_with_load_store(func: &mut Function) {
    // Turn load and store into stack_load and stack_store when possible.
    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                InstructionData::Load { opcode: Opcode::Load, arg: addr, flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUD: stack_load.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            let ty = cursor.func.dfg.ctrl_typevar(inst);
                            cursor.func.dfg.replace(inst).stack_load(ty, stack_slot, combined_offset);
                        }
                    }
                }
                InstructionData::Store { opcode: Opcode::Store, args: [value, addr], flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUND: stack_store.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            cursor.func.dfg.replace(inst).stack_store(value, stack_slot, combined_offset);
                        }
                    }
                }
                _ => {}
            }
        }
    }
}

fn remove_unused_stack_addr_and_stack_load(func: &mut Function, stack_slot_usage_map: &mut BTreeMap<OrdStackSlot, StackSlotUsage>) {
    // FIXME incrementally rebuild on each call?
    let mut stack_addr_load_insts_users = HashMap::<Inst, HashSet<Inst>>::new();

    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            for &arg in cursor.func.dfg.inst_args(inst) {
                if let ValueDef::Result(arg_origin, 0) = cursor.func.dfg.value_def(arg) {
                    match cursor.func.dfg[arg_origin].opcode() {
                        Opcode::StackAddr | Opcode::StackLoad => {
                            stack_addr_load_insts_users.entry(arg_origin).or_insert_with(HashSet::new).insert(inst);
                        }
                        _ => {}
                    }
                }
            }
        }
    }

    println!("stack_addr/stack_load users: {:?}", stack_addr_load_insts_users);

    for inst in stack_addr_load_insts_users.keys() {
        let mut is_recorded_stack_addr_or_stack_load = false;
        for stack_slot_users in stack_slot_usage_map.values() {
            is_recorded_stack_addr_or_stack_load |= stack_slot_users.stack_addr.contains(inst) || stack_slot_users.stack_load.contains(inst);
        }
        assert!(is_recorded_stack_addr_or_stack_load);
    }

    // Replace all unused stack_addr and stack_load instructions with nop.
    for stack_slot_users in stack_slot_usage_map.values_mut() {
        // FIXME remove clone
        for &inst in stack_slot_users.stack_addr.clone().iter() {
            if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
                println!("Removing unused stack_addr {}", inst);
                func.dfg.detach_results(inst);
                func.dfg.replace(inst).nop();
                stack_slot_users.stack_addr.remove(&inst);
            }
        }

        for &inst in stack_slot_users.stack_load.clone().iter() {
            if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
                println!("Removing unused stack_addr {}", inst);
                func.dfg.detach_results(inst);
                func.dfg.replace(inst).nop();
                stack_slot_users.stack_load.remove(&inst);
            }
        }
    }
}

fn try_get_stack_slot_and_offset_for_addr(func: &Function, addr: Value) -> Option<(StackSlot, Offset32)> {
    if let ValueDef::Result(addr_inst, 0) = func.dfg.value_def(addr) {
        if let InstructionData::StackLoad {
            opcode: Opcode::StackAddr,
            stack_slot,
            offset,
        } = func.dfg[addr_inst] {
            return Some((stack_slot, offset));
        }
    }
    None
}
<commit_msg>[WIP] Implement basic stack store to load forwarding<commit_after>use std::collections::{BTreeMap, HashSet};
use std::ops::Not;

use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::ir::{InstructionData, Opcode, ProgramOrder, ValueDef};
use cranelift_codegen::ir::immediates::Offset32;

use crate::prelude::*;

/// Workaround for `StackSlot` not implementing `Ord`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct OrdStackSlot(StackSlot);

impl PartialOrd for OrdStackSlot {
    fn partial_cmp(&self, rhs: &Self) -> Option<std::cmp::Ordering> {
        self.0.as_u32().partial_cmp(&rhs.0.as_u32())
    }
}

impl Ord for OrdStackSlot {
    fn cmp(&self, rhs: &Self) -> std::cmp::Ordering {
        self.0.as_u32().cmp(&rhs.0.as_u32())
    }
}

#[derive(Debug, Default)]
struct StackSlotUsage {
    stack_addr: HashSet<Inst>,
    stack_load: HashSet<Inst>,
    stack_store: HashSet<Inst>,
}

pub(super) fn optimize_function(
    func: &mut Function,
    clif_comments: &mut crate::pretty_clif::CommentWriter,
    name: String, // FIXME remove
) {
    combine_stack_addr_with_load_store(func);

    // Record all stack_addr, stack_load and stack_store instructions.
    let mut stack_slot_usage_map = BTreeMap::<OrdStackSlot, StackSlotUsage>::new();

    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                InstructionData::StackLoad {
                    opcode: Opcode::StackAddr,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_addr.insert(inst);
                }
                InstructionData::StackLoad {
                    opcode: Opcode::StackLoad,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_load.insert(inst);
                }
                InstructionData::StackStore {
                    opcode: Opcode::StackStore,
                    arg: _,
                    stack_slot,
                    offset: _,
                } => {
                    stack_slot_usage_map.entry(OrdStackSlot(stack_slot)).or_insert_with(StackSlotUsage::default).stack_store.insert(inst);
                }
                _ => {}
            }
        }
    }

    println!("{}:\nstack slot usage: {:?}", name, stack_slot_usage_map);

    remove_unused_stack_addr_and_stack_load(func, &mut stack_slot_usage_map);

    println!("stack slot usage (after): {:?}", stack_slot_usage_map);

    for (stack_slot, users) in stack_slot_usage_map.iter_mut() {
        if users.stack_addr.is_empty().not() {
            // Stack addr leaked; there may be unknown loads and stores.
            // FIXME use stacked borrows to optimize
            continue;
        }

        let is_loaded = users.stack_load.is_empty().not();
        let is_stored = users.stack_store.is_empty().not();
        match (is_loaded, is_stored) {
            (true, true) => {
                for load in users.stack_load.clone().drain() {
                    let load_ebb = func.layout.inst_ebb(load).unwrap();
                    let loaded_value = func.dfg.inst_results(load)[0];
                    let loaded_type = func.dfg.value_type(loaded_value);

                    let potential_stores = users.stack_store.iter().cloned().filter(|&store| {
                        match spatial_overlap(func, load, store) {
                            SpatialOverlap::No => false, // Can never be the source of the loaded value.
                            SpatialOverlap::Partial | SpatialOverlap::Full => true,
                        }
                    }).filter(|&store| {
                        if load_ebb == func.layout.inst_ebb(store).unwrap() {
                            func.layout.cmp(store, load) == std::cmp::Ordering::Less
                        } else {
                            true // FIXME
                        }
                    }).collect::<Vec<Inst>>();
                    for &store in &potential_stores {
                        println!("Potential store -> load forwarding {} -> {} ({:?})", func.dfg.display_inst(store, None), func.dfg.display_inst(load, None), spatial_overlap(func, load, store));
                    }
                    match *potential_stores {
                        [] => println!("[{}] [BUG?] Reading uninitialized memory", name),
                        [store] if spatial_overlap(func, load, store) == SpatialOverlap::Full => {
                            let store_ebb = func.layout.inst_ebb(store).unwrap();
                            let stored_value = func.dfg.inst_args(store)[0];
                            let stored_type = func.dfg.value_type(stored_value);
                            if stored_type == loaded_type && store_ebb == load_ebb {
                                println!("Store to load forward {} -> {}", store, load);
                                func.dfg.detach_results(load);
                                func.dfg.replace(load).nop();
                                func.dfg.change_to_alias(loaded_value, stored_value);
                            }
                        }
                        _ => {} // FIXME implement this
                    }
                }
            }
            (true, false) => println!("[{}] [BUG?] Reading uninitialized memory", name),
            (false, _) => {
                // Never loaded; can safely remove all stores and the stack slot.
                for user in users.stack_store.drain() {
                    println!("[{}] Remove dead stack store {} of {}", name, user, stack_slot.0);
                    func.dfg.replace(user).nop();
                }

                // FIXME make stack_slot zero sized.
            }
        }
    }

    println!();
}

fn combine_stack_addr_with_load_store(func: &mut Function) {
    // Turn load and store into stack_load and stack_store when possible.
    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                InstructionData::Load { opcode: Opcode::Load, arg: addr, flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUD: stack_load.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            let ty = cursor.func.dfg.ctrl_typevar(inst);
                            cursor.func.dfg.replace(inst).stack_load(ty, stack_slot, combined_offset);
                        }
                    }
                }
                InstructionData::Store { opcode: Opcode::Store, args: [value, addr], flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUND: stack_store.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            cursor.func.dfg.replace(inst).stack_store(value, stack_slot, combined_offset);
                        }
                    }
                }
                _ => {}
            }
        }
    }
}

fn remove_unused_stack_addr_and_stack_load(func: &mut Function, stack_slot_usage_map: &mut BTreeMap<OrdStackSlot, StackSlotUsage>) {
    // FIXME incrementally rebuild on each call?
    let mut stack_addr_load_insts_users = HashMap::<Inst, HashSet<Inst>>::new();

    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            for &arg in cursor.func.dfg.inst_args(inst) {
                if let ValueDef::Result(arg_origin, 0) = cursor.func.dfg.value_def(arg) {
                    match cursor.func.dfg[arg_origin].opcode() {
                        Opcode::StackAddr | Opcode::StackLoad => {
                            stack_addr_load_insts_users.entry(arg_origin).or_insert_with(HashSet::new).insert(inst);
                        }
                        _ => {}
                    }
                }
            }
        }
    }

    println!("stack_addr/stack_load users: {:?}", stack_addr_load_insts_users);

    for inst in stack_addr_load_insts_users.keys() {
        let mut is_recorded_stack_addr_or_stack_load = false;
        for stack_slot_users in stack_slot_usage_map.values() {
            is_recorded_stack_addr_or_stack_load |= stack_slot_users.stack_addr.contains(inst) || stack_slot_users.stack_load.contains(inst);
        }
        assert!(is_recorded_stack_addr_or_stack_load);
    }

    // Replace all unused stack_addr and stack_load instructions with nop.
    for stack_slot_users in stack_slot_usage_map.values_mut() {
        // FIXME remove clone
        for &inst in stack_slot_users.stack_addr.clone().iter() {
            if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
                println!("Removing unused stack_addr {}", inst);
                func.dfg.detach_results(inst);
                func.dfg.replace(inst).nop();
                stack_slot_users.stack_addr.remove(&inst);
            }
        }

        for &inst in stack_slot_users.stack_load.clone().iter() {
            if stack_addr_load_insts_users.get(&inst).map(|users| users.is_empty()).unwrap_or(true) {
                println!("Removing unused stack_addr {}", inst);
                func.dfg.detach_results(inst);
                func.dfg.replace(inst).nop();
                stack_slot_users.stack_load.remove(&inst);
            }
        }
    }
}

fn try_get_stack_slot_and_offset_for_addr(func: &Function, addr: Value) -> Option<(StackSlot, Offset32)> {
    if let ValueDef::Result(addr_inst, 0) = func.dfg.value_def(addr) {
        if let InstructionData::StackLoad {
            opcode: Opcode::StackAddr,
            stack_slot,
            offset,
        } = func.dfg[addr_inst] {
            return Some((stack_slot, offset));
        }
    }
    None
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum SpatialOverlap {
    No,
    Partial,
    Full,
}

fn spatial_overlap(func: &Function, src: Inst, dest: Inst) -> SpatialOverlap {
    fn inst_info(func: &Function, inst: Inst) -> (StackSlot, Offset32, u32) {
        match func.dfg[inst] {
            InstructionData::StackLoad {
                opcode: Opcode::StackAddr,
                stack_slot,
                offset,
            }
            | InstructionData::StackLoad {
                opcode: Opcode::StackLoad,
                stack_slot,
                offset,
            }
            | InstructionData::StackStore {
                opcode: Opcode::StackStore,
                stack_slot,
                offset,
                arg: _,
            } => (stack_slot, offset, func.dfg.ctrl_typevar(inst).bytes()),
            _ => unreachable!("{:?}", func.dfg[inst]),
        }
    }

    let (src_ss, src_offset, src_size) = inst_info(func, src);
    let (dest_ss, dest_offset, dest_size) = inst_info(func, dest);

    if src_ss != dest_ss {
        return SpatialOverlap::No;
    }

    if src_offset == dest_offset && src_size == dest_size {
        return SpatialOverlap::Full;
    }

    let src_end: i64 = src_offset.try_add_i64(i64::from(src_size)).unwrap().into();
    let dest_end: i64 = dest_offset.try_add_i64(i64::from(dest_size)).unwrap().into();
    if src_end <= dest_offset.into() || dest_end <= src_offset.into() {
        return SpatialOverlap::No;
    }

    SpatialOverlap::Partial
}
<|endoftext|>


<commit_before>use aurelius;
use hyper::header::ContentType;
use hyper::mime:: {
    Mime,
    TopLevel,
    SubLevel,
};
use iron::prelude::*;
use iron::Handler;
use iron::status;
use params::{
    Params,
    Value,
};
use router::Router;
use tera::Context;

use Application;
use services;

fn render(req: &mut Request, app: &Application, template: &str, raw: bool) -> IronResult<Response> {
        let ref slug = req.extensions.get::<Router>().unwrap().find("page").unwrap();
        let pool = itry!(app.pool.get());
        let mut page = iexpect!(services::wiki::get(&pool, slug));

        if !raw {
            page.body = aurelius::markdown::to_html(page.body.as_ref());
        }

        let mut context = Context::new();
        context.add("page", &page);
        let body = app.tera.render(template, &context).unwrap();

        let mut resp = Response::with((status::Ok, body));
        resp.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
        Ok(resp)
}

pub struct IndexGet {
    pub app: Application,
}

impl Handler for IndexGet {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        render(req, &self.app, "wiki/index.html", false)
    }
}

pub struct IndexPost {
    pub app: Application,
}

impl Handler for IndexPost {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let map = req.get::<Params>().unwrap();
        let title = iexpect!(match map.find(&["title"]) {
            Some(&Value::String(ref title)) => Some(title),
            _ => None,
        });
        let body = iexpect!(match map.find(&["body"]) {
            Some(&Value::String(ref body)) => Some(body),
            _ => None,
        });

        {
            let slug = req.extensions.get::<Router>().unwrap().find("page").unwrap();
            let pool = itry!(self.app.pool.get());
            services::wiki::update(&pool, slug, title, body);
        }

        render(req, &self.app, "wiki/index.html", false)
    }
}

pub struct EditGet {
    pub app: Application,
}

impl Handler for EditGet {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        render(req, &self.app, "wiki/edit.html", true)
    }
}
<commit_msg>Returns 404 if page not found.<commit_after>use aurelius;
use hyper::header::ContentType;
use hyper::mime:: {
    Mime,
    TopLevel,
    SubLevel,
};
use iron::prelude::*;
use iron::Handler;
use iron::status;
use params::{
    Params,
    Value,
};
use router::Router;
use tera::Context;

use Application;
use services;

fn render(req: &mut Request, app: &Application, template: &str, raw: bool) -> IronResult<Response> {
        let ref slug = req.extensions.get::<Router>().unwrap().find("page").unwrap();
        let pool = itry!(app.pool.get());
        let mut page = iexpect!(services::wiki::get(&pool, slug), status::NotFound);

        if !raw {
            page.body = aurelius::markdown::to_html(page.body.as_ref());
        }

        let mut context = Context::new();
        context.add("page", &page);
        let body = app.tera.render(template, &context).unwrap();

        let mut resp = Response::with((status::Ok, body));
        resp.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
        Ok(resp)
}

pub struct IndexGet {
    pub app: Application,
}

impl Handler for IndexGet {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        render(req, &self.app, "wiki/index.html", false)
    }
}

pub struct IndexPost {
    pub app: Application,
}

impl Handler for IndexPost {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let map = req.get::<Params>().unwrap();
        let title = iexpect!(match map.find(&["title"]) {
            Some(&Value::String(ref title)) => Some(title),
            _ => None,
        });
        let body = iexpect!(match map.find(&["body"]) {
            Some(&Value::String(ref body)) => Some(body),
            _ => None,
        });

        {
            let slug = req.extensions.get::<Router>().unwrap().find("page").unwrap();
            let pool = itry!(self.app.pool.get());
            services::wiki::update(&pool, slug, title, body);
        }

        render(req, &self.app, "wiki/index.html", false)
    }
}

pub struct EditGet {
    pub app: Application,
}

impl Handler for EditGet {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        render(req, &self.app, "wiki/edit.html", true)
    }
}
<|endoftext|>


<commit_before>#![cfg(all(unix, feature = "io_uring"))]

use libc::off_t;
use std::convert::TryFrom;
use std::fs::File;
use std::io;
use std::mem;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;

use crate::LogOffset;
use crate::{Error, Result};

use liburing::*;

/// TODO: support for SQPOLL with idle timeout
pub(crate) struct Uring<T> {
    /// Mutable unsafe FFI struct from liburing::
    ring: io_uring,

    /// File
    file: Arc<File>,

    /// IOVec structures that hold information about buffers
    /// used in SQEs.
    iovecs: Vec<libc::iovec>,

    /// iovec buffs. this is specifically needed to preserve buf
    /// ptr lifetime because all write operations are async.
    buf: Vec<Option<Arc<T>>>,

    /// List of free slots: iovecs, user data, etc.
    free_slots: Vec<usize>,
}

/// Pointers can't be passed safely through threads boundaries.
/// `Uring` must enforce it, yet it is not Sync.
#[allow(unsafe_code)]
unsafe impl<T> Send for Uring<T> {}

impl<T> Uring<T> {
    /// Create and initialize new `io_uring` structure with
    /// `size` queue length and `flags` specified properties.
    ///
    /// Available flags from `io_uring.h`:
    ///     - `IORING_SETUP_IOPOLL`	(1U << 0)	/* `io_context` is polled */
    ///     - `IORING_SETUP_SQPOLL`	(1U << 1)	/* SQ poll thread */
    ///     - `IORING_SETUP_SQ_AFF`	(1U << 2)	/* `sq_thread_cpu` is valid */
    ///     - `IORING_SETUP_CQSIZE`	(1U << 3)	/* app defines CQ size */
    pub fn new(
        file: Arc<File>,
        size: usize,
        flags: libc::c_uint,
    ) -> Result<Self> {
        if size & 1 != 0 || size < 2 {
            return Err(Error::Unsupported("invalid queue size".into()));
        }

        let ring = unsafe {
            let mut s = mem::MaybeUninit::<io_uring>::uninit();
            let ret = io_uring_queue_init(
                u32::try_from(size).unwrap(),
                s.as_mut_ptr(),
                flags,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }

            s.assume_init()
        };

        let mut uring = Uring::<T> {
            ring,
            file,
            iovecs: Vec::with_capacity(size),
            buf: Vec::with_capacity(size),
            free_slots: Vec::with_capacity(size),
        };

        unsafe {
            let ret = io_uring_register_files(
                &mut uring.ring,
                [uring.file.as_raw_fd()].as_ptr(),
                1,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
        };

        uring.iovecs.resize(
            size,
            libc::iovec { iov_base: std::ptr::null_mut(), iov_len: 0 },
        );
        uring.buf.resize(size, None);

        // index free elements
        for i in 0..size {
            uring.free_slots.push(i);
        }

        Ok(uring)
    }

    unsafe fn drain_cqe(&mut self) -> Result<()> {
        loop {
            let mut cqe: *mut io_uring_cqe = std::ptr::null_mut();
            let ret = io_uring_peek_cqe(&mut self.ring, &mut cqe);
            if ret == -libc::EAGAIN {
                // peek found nothing, no completions to drain
                return Ok(());
            }
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
            let i = usize::try_from((*cqe).user_data).unwrap();
            if i < self.free_slots.capacity() {
                self.free_slots.push(i);
                // release
                self.iovecs[i].iov_base = std::ptr::null_mut();
                self.iovecs[i].iov_len = 0;
                // drop arc buf
                self.buf[i] = None;
            }
            io_uring_cqe_seen(&mut self.ring, cqe);
        }
    }

    pub fn pwrite_all(
        &mut self,
        buf: &mut [u8],
        data: Arc<T>,
        offset: LogOffset,
        fsync: bool,
    ) -> Result<()> {
        unsafe {
            // 1. drain completed operations
            self.drain_cqe()?;

            // it is easier to track queue len if num
            // of iovecs will be the same as sqes
            let mut reserve = 1;
            if fsync {
                reserve += 1;
            }

            // 2. reserve SQE slots
            if self.free_slots.len() < reserve {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    libc::c_uint::try_from(reserve).unwrap(),
                );
                if ret < 0 {
                    return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
                }
                self.drain_cqe()?;
                if self.free_slots.len() < reserve {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "wait for free sqes",
                    )));
                }
            }

            // 3. build write() SQE
            {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();
                self.buf[i] = Some(data);
                self.iovecs[i].iov_base =
                    buf.as_mut_ptr() as *mut std::ffi::c_void;
                self.iovecs[i].iov_len = buf.len();

                io_uring_prep_writev(
                    sqe,
                    self.file.as_raw_fd(),
                    &self.iovecs[i],
                    1,
                    off_t::try_from(offset).unwrap(),
                );
                io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
                (*sqe).user_data = u64::try_from(i).unwrap();
            }

            // 4. build fsync() SQE
            if fsync {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();

                io_uring_prep_fsync(
                    sqe,
                    self.file.as_raw_fd(),
                    IORING_FSYNC_DATASYNC,
                );
                io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);
                (*sqe).user_data = u64::try_from(i).unwrap();
            }

            // 5. submit
            let ret = io_uring_submit(&mut self.ring);
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
        };

        Ok(())
    }
}

impl<T> Drop for Uring<T> {
    fn drop(&mut self) {
        unsafe {
            // flush unfinished ops
            if self.free_slots.len() < self.iovecs.len() {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    u32::try_from(self.iovecs.len() - self.free_slots.len())
                        .unwrap(),
                );
                if ret < 0 {
                    panic!(Error::Io(io::Error::from_raw_os_error(-ret)));
                }
            }

            self.drain_cqe().unwrap();

            // auto on exit: io_uring_unregister_files(&mut self.ring);
            io_uring_queue_exit(&mut self.ring);
        };
    }
}
<commit_msg>io_uring: add cqe result check, skip failed fsync+drain, fix fixed file fds<commit_after>#![cfg(all(unix, feature = "io_uring"))]

use libc::off_t;
use std::convert::TryFrom;
use std::fs::File;
use std::io;
use std::mem;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;

use crate::LogOffset;
use crate::{Error, Result};

use liburing::*;

/// TODO: support for SQPOLL with idle timeout
pub(crate) struct Uring<T> {
    /// Mutable unsafe FFI struct from liburing::
    ring: io_uring,

    /// File
    file: Arc<File>,

    /// IOVec structures that hold information about buffers
    /// used in SQEs.
    iovecs: Vec<libc::iovec>,

    /// iovec buffs. this is specifically needed to preserve buf
    /// ptr lifetime because all write operations are async.
    buf: Vec<Option<Arc<T>>>,

    /// List of free slots: iovecs, user data, etc.
    free_slots: Vec<usize>,
}

/// Pointers can't be passed safely through threads boundaries.
/// `Uring` must enforce it, yet it is not Sync.
#[allow(unsafe_code)]
unsafe impl<T> Send for Uring<T> {}

impl<T> Uring<T> {
    /// Create and initialize new `io_uring` structure with
    /// `size` queue length and `flags` specified properties.
    ///
    /// Available flags from `io_uring.h`:
    ///     - `IORING_SETUP_IOPOLL`	(1U << 0)	/* `io_context` is polled */
    ///     - `IORING_SETUP_SQPOLL`	(1U << 1)	/* SQ poll thread */
    ///     - `IORING_SETUP_SQ_AFF`	(1U << 2)	/* `sq_thread_cpu` is valid */
    ///     - `IORING_SETUP_CQSIZE`	(1U << 3)	/* app defines CQ size */
    pub fn new(
        file: Arc<File>,
        size: usize,
        flags: libc::c_uint,
    ) -> Result<Self> {
        if size & 1 != 0 || size < 2 {
            return Err(Error::Unsupported("invalid queue size".into()));
        }

        let ring = unsafe {
            let mut s = mem::MaybeUninit::<io_uring>::uninit();
            let ret = io_uring_queue_init(
                u32::try_from(size).unwrap(),
                s.as_mut_ptr(),
                flags,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }

            s.assume_init()
        };

        let mut uring = Uring::<T> {
            ring,
            file,
            iovecs: Vec::with_capacity(size),
            buf: Vec::with_capacity(size),
            free_slots: Vec::with_capacity(size),
        };

        unsafe {
            let ret = io_uring_register_files(
                &mut uring.ring,
                [uring.file.as_raw_fd()].as_ptr(),
                1,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
        };

        uring.iovecs.resize(
            size,
            libc::iovec { iov_base: std::ptr::null_mut(), iov_len: 0 },
        );
        uring.buf.resize(size, None);

        // index free elements
        for i in 0..size {
            uring.free_slots.push(i);
        }

        Ok(uring)
    }

    unsafe fn drain_cqe(&mut self) -> Result<()> {
        loop {
            let mut cqe: *mut io_uring_cqe = std::ptr::null_mut();
            let ret = io_uring_peek_cqe(&mut self.ring, &mut cqe);
            if ret == -libc::EAGAIN {
                // peek found nothing, no completions to drain
                return Ok(());
            }
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
            let i = usize::try_from((*cqe).user_data).unwrap();
            if (*cqe).res < 0 {
                // ignore failed fsyncs with invalid argument due to the
                // overlap with executing ones: 2 drain fsync do not exec twice.
                if (*cqe).res != -libc::EINVAL
                    || i >= self.free_slots.capacity()
                    || self.iovecs[i].iov_len != 0
                {
                    return Err(Error::Io(io::Error::from_raw_os_error(
                        -(*cqe).res,
                    )));
                }
            }
            if i < self.free_slots.capacity() {
                // println!("release i = {}", i);
                self.free_slots.push(i);
                // release
                self.iovecs[i].iov_base = std::ptr::null_mut();
                self.iovecs[i].iov_len = 0;
                // drop arc buf
                self.buf[i] = None;
            }
            io_uring_cqe_seen(&mut self.ring, cqe);
        }
    }

    pub fn pwrite_all(
        &mut self,
        buf: &mut [u8],
        data: Arc<T>,
        offset: LogOffset,
        fsync: bool,
    ) -> Result<()> {
        unsafe {
            // 1. drain completed operations
            self.drain_cqe()?;

            // it is easier to track queue len if num
            // of iovecs will be the same as sqes
            let mut reserve = 1;
            if fsync {
                reserve += 1;
            }

            // 2. reserve SQE slots
            if self.free_slots.len() < reserve {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    libc::c_uint::try_from(reserve).unwrap(),
                );
                if ret < 0 {
                    return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
                }
                self.drain_cqe()?;
                if self.free_slots.len() < reserve {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "wait for free sqes",
                    )));
                }
            }

            // 3. build write() SQE
            {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();
                self.buf[i] = Some(data);
                self.iovecs[i].iov_base =
                    buf.as_mut_ptr() as *mut std::ffi::c_void;
                self.iovecs[i].iov_len = buf.len();

                io_uring_prep_writev(
                    sqe,
                    // for registered file at index 0
                    // self.file.as_raw_fd(),
                    0,
                    &self.iovecs[i],
                    1,
                    off_t::try_from(offset).unwrap(),
                );
                io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
                (*sqe).user_data = u64::try_from(i).unwrap();
                // println!("write with i = {}", i);
            }

            // 4. build fsync() SQE
            if fsync {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();
                // mark explicitly that this is fsync op and it has no data.
                // drain_cqe must ignore fsyncs that got EINVAL due to the
                // overlap with the existing executing fsyncs.
                self.iovecs[i].iov_len = 0;

                io_uring_prep_fsync(
                    sqe,
                    // for registered file at index 0
                    // self.file.as_raw_fd(),
                    0,
                    IORING_FSYNC_DATASYNC,
                );
                // io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
                io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN | IOSQE_FIXED_FILE);
                (*sqe).user_data = u64::try_from(i).unwrap();
                // println!("fsync with i = {}", i);
            }

            // 5. submit
            let ret = io_uring_submit(&mut self.ring);
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
        };

        Ok(())
    }
}

impl<T> Drop for Uring<T> {
    fn drop(&mut self) {
        unsafe {
            // flush unfinished ops
            if self.free_slots.len() < self.iovecs.len() {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    u32::try_from(self.iovecs.len() - self.free_slots.len())
                        .unwrap(),
                );
                if ret < 0 {
                    panic!(Error::Io(io::Error::from_raw_os_error(-ret)));
                }
            }

            self.drain_cqe().unwrap();

            // auto on exit: io_uring_unregister_files(&mut self.ring);
            io_uring_queue_exit(&mut self.ring);
        };
    }
}
<|endoftext|>


<commit_before>extern crate serde_json;
extern crate term;

use self::term::{Attr, color};
use prettytable::Table;
use prettytable::row::Row;
use prettytable::cell::Cell;

pub fn print_table(keys: &Vec<(String, String)>, rows: &Vec<serde_json::Value>) {
    let mut table = Table::new();

    let headers = keys.iter().clone()
        .map(|&(_, ref header)| Cell::new(header)
            .with_style(Attr::Bold)
            .with_style(Attr::ForegroundColor(color::GREEN))
        ).collect::<Vec<Cell>>();

    table.add_row(Row::new(headers));

    for row in rows {
        let columns = keys.iter().clone()
            .map(|&(ref key, _)| Cell::new(row[key].as_str().unwrap_or("-")))
            .collect::<Vec<Cell>>();

        table.add_row(Row::new(columns));
    }

    table.printstd();
}<commit_msg>Renamed variables<commit_after>extern crate serde_json;
extern crate term;

use self::term::{Attr, color};
use prettytable::Table;
use prettytable::row::Row;
use prettytable::cell::Cell;

pub fn print_table(headers: &Vec<(String, String)>, rows: &Vec<serde_json::Value>) {
    let mut table = Table::new();

    let tittles = headers.iter().clone()
        .map(|&(_, ref header)| Cell::new(header)
            .with_style(Attr::Bold)
            .with_style(Attr::ForegroundColor(color::GREEN))
        ).collect::<Vec<Cell>>();

    table.add_row(Row::new(tittles));

    for row in rows {
        let columns = headers.iter().clone()
            .map(|&(ref key, _)| Cell::new(row[key].as_str().unwrap_or("-")))
            .collect::<Vec<Cell>>();

        table.add_row(Row::new(columns));
    }

    table.printstd();
}<|endoftext|>