<commit_before>use std::{
    cell::{Cell, RefCell},
    collections::BTreeMap,
    rc::Rc,
};

use dces::prelude::{Entity, World};

use crate::{
    application::{Application, Tree},
    backend::{target_backend, BackendRunner},
    event::EventHandler,
    layout::{Layout, RootLayout},
    properties::{Bounds, Point},
    render_object::RenderObject,
    systems::{EventSystem, LayoutSystem, PostLayoutStateSystem, RenderSystem, StateSystem},
    theme::Theme,
    widget::{PropertyResult, State, Template},
    Global,
};

/// Represents a window. Each window has its own tree, event pipline and backend.
pub struct Window {
    pub backend_runner: Box<dyn BackendRunner>,
    pub render_objects: Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
    pub layouts: Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
    pub handlers: Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
    pub states: Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
    pub update: Rc<Cell<bool>>,
    pub debug_flag: Rc<Cell<bool>>,
}

impl Window {
    /// Executes the given window unitl quit is requested.
    pub fn run(&mut self) {
        self.backend_runner.run(self.update.clone());
    }
}

/// The `WindowBuilder` is used to define and build a `Window`.
pub struct WindowBuilder<'a> {
    pub application: &'a mut Application,
    pub bounds: Bounds,
    pub title: String,
    pub theme: Theme,
    pub root: Option<Template>,
    pub debug_flag: bool,
}

impl<'a> WindowBuilder<'a> {
    /// Used to define the render `bounds` of the window.
    pub fn with_bounds(mut self, bounds: Bounds) -> Self {
        self.bounds = bounds;
        self
    }

    /// Used to set the `title` of the window.
    pub fn with_title<S: Into<String>>(mut self, title: S) -> Self {
        self.title = title.into();
        self
    }

    /// Used to set the css `theme` of the window.
    pub fn with_theme(mut self, theme: Theme) -> Self {
        self.theme = theme;
        self
    }

    /// Used to set the `root` template of the window.
    pub fn with_root(mut self, root: Template) -> Self {
        self.root = Some(root);
        self
    }

    /// Used to set the `debug` flag of the window.
    /// If the flag is set to `ture` debug informations will be printed to the console.
    pub fn with_debug_flag(mut self, debug: bool) -> Self {
        self.debug_flag = debug;
        self
    }

    /// Creates the window with the given properties and builds its widget tree.
    pub fn build(self) {
        let (mut runner, backend) = target_backend(&self.title, self.bounds, self.theme);
        let mut world = World::from_container(Tree::default());
        let render_objects = Rc::new(RefCell::new(BTreeMap::new()));
        let layouts = Rc::new(RefCell::new(BTreeMap::new()));
        let handlers = Rc::new(RefCell::new(BTreeMap::new()));
        let states = Rc::new(RefCell::new(BTreeMap::new()));
        let update = Rc::new(Cell::new(true));
        let debug_flag = Rc::new(Cell::new(self.debug_flag));

        if debug_flag.get() {
            println!("------ Start build tree ------\n");
        }

        if let Some(root) = self.root {
            build_tree(
                root,
                &mut world,
                &render_objects,
                &layouts,
                &handlers,
                &states,
                &debug_flag,
            );
        }

        world
            .create_system(EventSystem {
                backend: backend.clone(),
                handlers: handlers.clone(),
                update: update.clone(),
            })
            .with_priority(0)
            .build();

        world
            .create_system(StateSystem {
                backend: backend.clone(),
                states: states.clone(),
                update: update.clone(),
                is_init: Cell::new(false),
            })
            .with_priority(1)
            .build();

        world
            .create_system(LayoutSystem {
                backend: backend.clone(),
                layouts: layouts.clone(),
                update: update.clone(),
            })
            .with_priority(2)
            .build();

        world
            .create_system(PostLayoutStateSystem {
                backend: backend.clone(),
                states: states.clone(),
                update: update.clone(),
            })
            .with_priority(3)
            .build();

        world
            .create_system(RenderSystem {
                backend: backend.clone(),
                render_objects: render_objects.clone(),
                update: update.clone(),
                debug_flag: debug_flag.clone(),
            })
            .with_priority(4)
            .build();

        runner.world(world);

        self.application.windows.push(Window {
            backend_runner: runner,
            render_objects,
            layouts,
            handlers,
            states,
            update,
            debug_flag,
        })
    }
}

// Builds the widget tree.
fn build_tree(
    root: Template,
    world: &mut World<Tree>,
    render_objects: &Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
    layouts: &Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
    handlers: &Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
    states: &Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
    debug_flag: &Rc<Cell<bool>>,
) {
    fn expand(
        world: &mut World<Tree>,
        render_objects: &Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
        layouts: &Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
        handlers: &Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
        states: &Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
        template: Template,
        debug_flag: &Rc<Cell<bool>>,
    ) -> Entity {
        // register window as entity with global properties
        if world.entity_container().is_empty() {
            let root = world
                .create_entity()
                .with(Global::default())
                .with(Bounds::default())
                .with(Point::default())
                .build();

            layouts
                .borrow_mut()
                .insert(root, Box::new(RootLayout));
        }

        let mut template = template;

        let entity = {
            let mut entity_builder = world
                .create_entity()
                .with(Bounds::default())
                .with(Point::default());

            // normal properties
            for (_, value) in template.properties.drain() {
                entity_builder = entity_builder.with_box(value);
            }

            // shared property
            for (_, value) in template.shared_properties {
                match value.build() {
                    PropertyResult::Property(property, source) => {
                        entity_builder = entity_builder.with_box(property);
                        source.set(Some(entity_builder.entity));
                    }
                    PropertyResult::Source(source) => {
                        entity_builder = entity_builder.with_shared_box(source);
                    }
                    PropertyResult::PropertyNotFound => {}
                }
            }

            let entity = entity_builder.build();

            if let Some(render_object) = template.render_object {
                render_objects.borrow_mut().insert(entity, render_object);
            }

            layouts
                .borrow_mut()
                .insert(entity, template.layout);

            let widget_handlers = template.event_handlers;

            if !widget_handlers.is_empty() {
                let mut event_handlers = vec![];

                for handler in widget_handlers {
                    event_handlers.push(handler.clone());
                }

                handlers.borrow_mut().insert(entity, event_handlers);
            }

            if let Some(state) = template.state {
                states.borrow_mut().insert(entity, state.clone());
            }

            entity
        };

        if debug_flag.get() {
            println!(
                "{} (id = {}, children_lenght = {})",
                template.debug_name,
                entity,
                template.children.len()
            );
        }

        if world.entity_container().len() == 2 {
            let root = world.entity_container().root;
            let _result = world.entity_container().append_child(root, entity);
        }

        for child in template.children.drain(0..) {
            let child = expand(
                world,
                render_objects,
                layouts,
                handlers,
                states,
                child,
                debug_flag,
            );
            let _result = world.entity_container().append_child(entity, child);
        }

        entity
    }

    expand(
        world,
        render_objects,
        layouts,
        handlers,
        states,
        root,
        debug_flag,
    );

    if debug_flag.get() {
        println!("\n------  End build tree  ------ ");
    }
}
<commit_msg>Added a depth parameter to the expand function to support pretty-printing the view hierarchy in debug mode.<commit_after>use std::{
    cell::{Cell, RefCell},
    collections::BTreeMap,
    rc::Rc,
};

use dces::prelude::{Entity, World};

use crate::{
    application::{Application, Tree},
    backend::{target_backend, BackendRunner},
    event::EventHandler,
    layout::{Layout, RootLayout},
    properties::{Bounds, Point},
    render_object::RenderObject,
    systems::{EventSystem, LayoutSystem, PostLayoutStateSystem, RenderSystem, StateSystem},
    theme::Theme,
    widget::{PropertyResult, State, Template},
    Global,
};

/// Represents a window. Each window has its own tree, event pipline and backend.
pub struct Window {
    pub backend_runner: Box<dyn BackendRunner>,
    pub render_objects: Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
    pub layouts: Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
    pub handlers: Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
    pub states: Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
    pub update: Rc<Cell<bool>>,
    pub debug_flag: Rc<Cell<bool>>,
}

impl Window {
    /// Executes the given window until quit is requested.
    pub fn run(&mut self) {
        self.backend_runner.run(self.update.clone());
    }
}

/// The `WindowBuilder` is used to define and build a `Window`.
pub struct WindowBuilder<'a> {
    pub application: &'a mut Application,
    pub bounds: Bounds,
    pub title: String,
    pub theme: Theme,
    pub root: Option<Template>,
    pub debug_flag: bool,
}

impl<'a> WindowBuilder<'a> {
    /// Used to define the render `bounds` of the window.
    pub fn with_bounds(mut self, bounds: Bounds) -> Self {
        self.bounds = bounds;
        self
    }

    /// Used to set the `title` of the window.
    pub fn with_title<S: Into<String>>(mut self, title: S) -> Self {
        self.title = title.into();
        self
    }

    /// Used to set the css `theme` of the window.
    pub fn with_theme(mut self, theme: Theme) -> Self {
        self.theme = theme;
        self
    }

    /// Used to set the `root` template of the window.
    pub fn with_root(mut self, root: Template) -> Self {
        self.root = Some(root);
        self
    }

    /// Used to set the `debug` flag of the window.
    /// If the flag is set to `ture` debug informations will be printed to the console.
    pub fn with_debug_flag(mut self, debug: bool) -> Self {
        self.debug_flag = debug;
        self
    }

    /// Creates the window with the given properties and builds its widget tree.
    pub fn build(self) {
        let (mut runner, backend) = target_backend(&self.title, self.bounds, self.theme);
        let mut world = World::from_container(Tree::default());
        let render_objects = Rc::new(RefCell::new(BTreeMap::new()));
        let layouts = Rc::new(RefCell::new(BTreeMap::new()));
        let handlers = Rc::new(RefCell::new(BTreeMap::new()));
        let states = Rc::new(RefCell::new(BTreeMap::new()));
        let update = Rc::new(Cell::new(true));
        let debug_flag = Rc::new(Cell::new(self.debug_flag));

        if debug_flag.get() {
            println!("------ Start build tree ------\n");
        }

        if let Some(root) = self.root {
            build_tree(
                root,
                &mut world,
                &render_objects,
                &layouts,
                &handlers,
                &states,
                &debug_flag,
            );
        }

        world
            .create_system(EventSystem {
                backend: backend.clone(),
                handlers: handlers.clone(),
                update: update.clone(),
            })
            .with_priority(0)
            .build();

        world
            .create_system(StateSystem {
                backend: backend.clone(),
                states: states.clone(),
                update: update.clone(),
                is_init: Cell::new(false),
            })
            .with_priority(1)
            .build();

        world
            .create_system(LayoutSystem {
                backend: backend.clone(),
                layouts: layouts.clone(),
                update: update.clone(),
            })
            .with_priority(2)
            .build();

        world
            .create_system(PostLayoutStateSystem {
                backend: backend.clone(),
                states: states.clone(),
                update: update.clone(),
            })
            .with_priority(3)
            .build();

        world
            .create_system(RenderSystem {
                backend: backend.clone(),
                render_objects: render_objects.clone(),
                update: update.clone(),
                debug_flag: debug_flag.clone(),
            })
            .with_priority(4)
            .build();

        runner.world(world);

        self.application.windows.push(Window {
            backend_runner: runner,
            render_objects,
            layouts,
            handlers,
            states,
            update,
            debug_flag,
        })
    }
}

// Builds the widget tree.
fn build_tree(
    root: Template,
    world: &mut World<Tree>,
    render_objects: &Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
    layouts: &Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
    handlers: &Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
    states: &Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
    debug_flag: &Rc<Cell<bool>>,
) {
    fn expand(
        world: &mut World<Tree>,
        render_objects: &Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
        layouts: &Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
        handlers: &Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
        states: &Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
        template: Template,
        debug_flag: &Rc<Cell<bool>>,
        depth: usize,
    ) -> Entity {
        // register window as entity with global properties
        if world.entity_container().is_empty() {
            let root = world
                .create_entity()
                .with(Global::default())
                .with(Bounds::default())
                .with(Point::default())
                .build();

            layouts
                .borrow_mut()
                .insert(root, Box::new(RootLayout));
        }

        let mut template = template;

        let entity = {
            let mut entity_builder = world
                .create_entity()
                .with(Bounds::default())
                .with(Point::default());

            // normal properties
            for (_, value) in template.properties.drain() {
                entity_builder = entity_builder.with_box(value);
            }

            // shared property
            for (_, value) in template.shared_properties {
                match value.build() {
                    PropertyResult::Property(property, source) => {
                        entity_builder = entity_builder.with_box(property);
                        source.set(Some(entity_builder.entity));
                    }
                    PropertyResult::Source(source) => {
                        entity_builder = entity_builder.with_shared_box(source);
                    }
                    PropertyResult::PropertyNotFound => {}
                }
            }

            let entity = entity_builder.build();

            if let Some(render_object) = template.render_object {
                render_objects.borrow_mut().insert(entity, render_object);
            }

            layouts
                .borrow_mut()
                .insert(entity, template.layout);

            let widget_handlers = template.event_handlers;

            if !widget_handlers.is_empty() {
                let mut event_handlers = vec![];

                for handler in widget_handlers {
                    event_handlers.push(handler.clone());
                }

                handlers.borrow_mut().insert(entity, event_handlers);
            }

            if let Some(state) = template.state {
                states.borrow_mut().insert(entity, state.clone());
            }

            entity
        };

        if debug_flag.get() {
            println!(
                "{}{} (id = {}, children_len = {})",
                "| ".repeat(depth),
                template.debug_name,
                entity,
                template.children.len()
            );
        }

        if world.entity_container().len() == 2 {
            let root = world.entity_container().root;
            let _result = world.entity_container().append_child(root, entity);
        }

        for child in template.children.drain(0..) {
            let child = expand(
                world,
                render_objects,
                layouts,
                handlers,
                states,
                child,
                debug_flag,
                depth + 1,
            );
            let _result = world.entity_container().append_child(entity, child);
        }

        entity
    }

    expand(
        world,
        render_objects,
        layouts,
        handlers,
        states,
        root,
        debug_flag,
        0
    );

    if debug_flag.get() {
        println!("\n------  End build tree  ------ ");
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015-2017 the original author or authors.
 *
 * 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.
 */

macro_rules! eprintln (
    ($($arg:tt)*) => { {
        use std::io::Write;

        writeln!(&mut ::std::io::stderr(), $($arg)*).expect("write failed");
    } }
);

// writeln_paced is similar to writeln except it reduces the risk of loggregator missing some entries
// by sleeping before each write. Also, it panics if the underlying writeln fails.
macro_rules! writeln_paced (
    ($($arg:tt)*) => { {
        use std::{thread, time};
        #[allow(unused_imports)]
        use std::io::Write;

        thread::sleep(time::Duration::from_millis(1));

        writeln!($($arg)*).expect("write failed");
    } }
);
<commit_msg>Use eprintln from Rust 1.19<commit_after>/*
 * Copyright (c) 2015-2017 the original author or authors.
 *
 * 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.
 */

// writeln_paced is similar to writeln except it reduces the risk of loggregator missing some entries
// by sleeping before each write. Also, it panics if the underlying writeln fails.
macro_rules! writeln_paced (
    ($($arg:tt)*) => { {
        use std::{thread, time};
        #[allow(unused_imports)]
        use std::io::Write;

        thread::sleep(time::Duration::from_millis(1));

        writeln!($($arg)*).expect("write failed");
    } }
);
<|endoftext|>


<commit_before><commit_msg>More gracefull failed malloc handling<commit_after><|endoftext|>


<commit_before>use itertools::Itertools;
use regex::Regex;
use reqwest;
use rust_htslib::bcf;
use rust_htslib::bcf::Read;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::str;

#[derive(Serialize, Deserialize, Debug)]
struct Dgidb {
    #[serde(rename = "matchedTerms")]
    matched_terms: Vec<MatchedTerm>,
}

#[derive(Serialize, Deserialize, Debug)]
struct MatchedTerm {
    #[serde(rename = "geneName")]
    gene_name: String,
    interactions: Vec<Interaction>,
}

#[derive(Serialize, Deserialize, Debug)]
struct Interaction {
    #[serde(rename = "drugName")]
    drug_name: String,
    #[serde(rename = "interactionTypes")]
    interaction_types: Vec<String>,
}

pub fn annotate_dgidb(
    vcf_path: &str,
    api_path: String,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let gene_drug_interactions = request_interaction_drugs(vcf_path, api_path)?;
    modify_vcf_entries(vcf_path, gene_drug_interactions, field_name)
}

fn request_interaction_drugs(
    vcf_path: &str,
    mut api_path: String,
) -> Result<Option<HashMap<String, Vec<(String, Vec<String>)>>>, Box<dyn Error>> {
    let mut genes = collect_genes(vcf_path)?;
    if genes.is_empty() {
        return Ok(None)
    }
    api_path.push_str(genes.drain().join(",").as_str());
    let res: Dgidb = reqwest::get(&api_path)?.json()?;

    let mut gene_drug_interactions: HashMap<String, Vec<(String, Vec<String>)>> = HashMap::new();
    for term in res.matched_terms {
        if !term.interactions.is_empty() {
            gene_drug_interactions.insert(
                term.gene_name,
                term.interactions
                    .iter()
                    .map(|interaction| {
                        (
                            interaction.drug_name.clone(),
                            interaction.interaction_types.clone(),
                        )
                    })
                    .collect(),
            );
        }
    }
    Ok(Some(gene_drug_interactions))
}

fn collect_genes(vcf_path: &str) -> Result<HashSet<String>, Box<dyn Error>> {
    let mut total_genes = HashSet::new();
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    for result in reader.records() {
        let mut rec = result?;
        let genes_opt = extract_genes(&mut rec)?;
        match genes_opt {
            Some(genes) => {
                for gene in genes {
                    total_genes.insert(gene);
                }
            }
            None => {}
        }
    }
    Ok(total_genes)
}

//TODO Remove split by ',' when updating to latest htslib release
fn extract_genes<'a>(
    rec: &'a mut bcf::Record,
) -> Result<Option<impl Iterator<Item = String> + 'a>, Box<dyn Error>> {
    let annotation = rec.info("ANN".as_bytes()).string()?;
    match annotation {
        Some(transcripts) => Ok(Some(transcripts[0].split(|c| *c == b',').map(
            |transcript| {
                str::from_utf8(transcript.split(|c| *c == b'|').nth(3).unwrap())
                    .unwrap()
                    .to_owned()
            },
        ))),
        None => Ok(None),
    }
}

fn modify_vcf_entries(
    vcf_path: &str,
    gene_drug_interactions_opt: Option<HashMap<String, Vec<(String, Vec<String>)>>>,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    let mut header = bcf::header::Header::from_template(reader.header());
    match gene_drug_interactions_opt {
        None => {
            bcf::Writer::from_stdout(&header, true, true)?;
        },
        Some(gene_drug_interactions) => {
            header.push_record(format!("##INFO=<ID={},Number=.,Type=String,Description=\"Combination of gene, drug, interaction types extracted from dgiDB. Each combination is pipe-seperated annotated as GENE|DRUG|TYPE\">", field_name).as_bytes());
            let mut writer = bcf::Writer::from_stdout(&header, true, true)?;

            for result in reader.records() {
                let mut rec = result?;
                writer.translate(&mut rec);
                let genes = extract_genes(&mut rec)?.map(|genes| genes.collect_vec());
                if let Some(mut genes) = genes {
                    genes.dedup();
                    let field_entries = build_dgidb_field(&gene_drug_interactions, genes)?;
                    let field_entries: Vec<&[u8]> = field_entries.iter().map(|v| v.as_slice()).collect();
                    rec.push_info_string(field_name.as_bytes(), &field_entries[..])?;
                }
                writer.write(&rec)?;
            }
        }
    }

    Ok(())
}

fn build_dgidb_field(
    gene_drug_interactions: &HashMap<String, Vec<(String, Vec<String>)>>,
    genes: Vec<String>,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>> {
    let mut field_entries: Vec<Vec<u8>> = Vec::new();
    let re = Regex::new(r"\s\(\w+\)").unwrap();
    for gene in genes.iter() {
        match gene_drug_interactions.get(gene) {
            Some(drug_interactions) => {
                drug_interactions
                    .iter()
                    .for_each(|(drug, interaction_types)| {
                        if !interaction_types.is_empty() {
                            interaction_types.iter().for_each(|interaction_type| {
                                field_entries.push(
                                    format!(
                                        "{g}|{d}|{t}",
                                        g = gene,
                                        d = re.replace(drug, ""),
                                        t = interaction_type
                                    )
                                    .as_bytes()
                                    .to_vec(),
                                )
                            })
                        } else {
                            field_entries.push(
                                format!("{g}|{d}|.", g = gene, d = re.replace(drug, ""))
                                    .as_bytes()
                                    .to_vec(),
                            )
                        }
                    });
            }
            None => field_entries.push(format!("{g}|.|.", g = gene).as_bytes().to_vec()),
        }
    }
    Ok(field_entries)
}
<commit_msg>Write unchanged records<commit_after>use itertools::Itertools;
use regex::Regex;
use reqwest;
use rust_htslib::bcf;
use rust_htslib::bcf::Read;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::str;

#[derive(Serialize, Deserialize, Debug)]
struct Dgidb {
    #[serde(rename = "matchedTerms")]
    matched_terms: Vec<MatchedTerm>,
}

#[derive(Serialize, Deserialize, Debug)]
struct MatchedTerm {
    #[serde(rename = "geneName")]
    gene_name: String,
    interactions: Vec<Interaction>,
}

#[derive(Serialize, Deserialize, Debug)]
struct Interaction {
    #[serde(rename = "drugName")]
    drug_name: String,
    #[serde(rename = "interactionTypes")]
    interaction_types: Vec<String>,
}

pub fn annotate_dgidb(
    vcf_path: &str,
    api_path: String,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let gene_drug_interactions = request_interaction_drugs(vcf_path, api_path)?;
    modify_vcf_entries(vcf_path, gene_drug_interactions, field_name)
}

fn request_interaction_drugs(
    vcf_path: &str,
    mut api_path: String,
) -> Result<Option<HashMap<String, Vec<(String, Vec<String>)>>>, Box<dyn Error>> {
    let mut genes = collect_genes(vcf_path)?;
    if genes.is_empty() {
        return Ok(None)
    }
    api_path.push_str(genes.drain().join(",").as_str());
    let res: Dgidb = reqwest::get(&api_path)?.json()?;

    let mut gene_drug_interactions: HashMap<String, Vec<(String, Vec<String>)>> = HashMap::new();
    for term in res.matched_terms {
        if !term.interactions.is_empty() {
            gene_drug_interactions.insert(
                term.gene_name,
                term.interactions
                    .iter()
                    .map(|interaction| {
                        (
                            interaction.drug_name.clone(),
                            interaction.interaction_types.clone(),
                        )
                    })
                    .collect(),
            );
        }
    }
    Ok(Some(gene_drug_interactions))
}

fn collect_genes(vcf_path: &str) -> Result<HashSet<String>, Box<dyn Error>> {
    let mut total_genes = HashSet::new();
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    for result in reader.records() {
        let mut rec = result?;
        let genes_opt = extract_genes(&mut rec)?;
        match genes_opt {
            Some(genes) => {
                for gene in genes {
                    total_genes.insert(gene);
                }
            }
            None => {}
        }
    }
    Ok(total_genes)
}

//TODO Remove split by ',' when updating to latest htslib release
fn extract_genes<'a>(
    rec: &'a mut bcf::Record,
) -> Result<Option<impl Iterator<Item = String> + 'a>, Box<dyn Error>> {
    let annotation = rec.info("ANN".as_bytes()).string()?;
    match annotation {
        Some(transcripts) => Ok(Some(transcripts[0].split(|c| *c == b',').map(
            |transcript| {
                str::from_utf8(transcript.split(|c| *c == b'|').nth(3).unwrap())
                    .unwrap()
                    .to_owned()
            },
        ))),
        None => Ok(None),
    }
}

fn modify_vcf_entries(
    vcf_path: &str,
    gene_drug_interactions_opt: Option<HashMap<String, Vec<(String, Vec<String>)>>>,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    let mut header = bcf::header::Header::from_template(reader.header());
    match gene_drug_interactions_opt {
        None => {
            let mut writer = bcf::Writer::from_stdout(&header, true, true)?;
            for result in reader.records() {
                let rec = result?;
                writer.write(&rec)?;
            }
        },
        Some(gene_drug_interactions) => {
            header.push_record(format!("##INFO=<ID={},Number=.,Type=String,Description=\"Combination of gene, drug, interaction types extracted from dgiDB. Each combination is pipe-seperated annotated as GENE|DRUG|TYPE\">", field_name).as_bytes());
            let mut writer = bcf::Writer::from_stdout(&header, true, true)?;

            for result in reader.records() {
                let mut rec = result?;
                writer.translate(&mut rec);
                let genes = extract_genes(&mut rec)?.map(|genes| genes.collect_vec());
                if let Some(mut genes) = genes {
                    genes.dedup();
                    let field_entries = build_dgidb_field(&gene_drug_interactions, genes)?;
                    let field_entries: Vec<&[u8]> = field_entries.iter().map(|v| v.as_slice()).collect();
                    rec.push_info_string(field_name.as_bytes(), &field_entries[..])?;
                }
                writer.write(&rec)?;
            }
        }
    }

    Ok(())
}

fn build_dgidb_field(
    gene_drug_interactions: &HashMap<String, Vec<(String, Vec<String>)>>,
    genes: Vec<String>,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>> {
    let mut field_entries: Vec<Vec<u8>> = Vec::new();
    let re = Regex::new(r"\s\(\w+\)").unwrap();
    for gene in genes.iter() {
        match gene_drug_interactions.get(gene) {
            Some(drug_interactions) => {
                drug_interactions
                    .iter()
                    .for_each(|(drug, interaction_types)| {
                        if !interaction_types.is_empty() {
                            interaction_types.iter().for_each(|interaction_type| {
                                field_entries.push(
                                    format!(
                                        "{g}|{d}|{t}",
                                        g = gene,
                                        d = re.replace(drug, ""),
                                        t = interaction_type
                                    )
                                    .as_bytes()
                                    .to_vec(),
                                )
                            })
                        } else {
                            field_entries.push(
                                format!("{g}|{d}|.", g = gene, d = re.replace(drug, ""))
                                    .as_bytes()
                                    .to_vec(),
                            )
                        }
                    });
            }
            None => field_entries.push(format!("{g}|.|.", g = gene).as_bytes().to_vec()),
        }
    }
    Ok(field_entries)
}
<|endoftext|>


<commit_before>use ui::ncurses::*;
use ui::{RIGHT_PANEL_WIDTH, CMD_ENTRY_HEIGHT, Window, Size};

pub struct Chat {
    parent: Window,
    child:  Window,
}

// glibc will panic if a message containing a C-style format string is entered
fn sanitize(string: &str) -> String {
    string.replace('%', "%%")
}

impl Chat {
    pub fn new(size: Size) -> Chat {
        let parent = newwin(size.height - CMD_ENTRY_HEIGHT,
                            size.width - RIGHT_PANEL_WIDTH,
                            0,
                            0);
        box_(parent, 0, 0);
        wrefresh(parent);

        let child = derwin(parent,
                           size.height - CMD_ENTRY_HEIGHT - 2,
                           size.width - RIGHT_PANEL_WIDTH - 2,
                           1,
                           1);
        scrollok(child, true);

        Chat {
            parent: Window::new(parent),
            child:  Window::new(child),
        }
    }

    pub fn add_message(&self, msg: &str) {
        wprintw(self.child.id, &sanitize(&format!("{}\n", msg)));
        wrefresh(self.child.id);
    }
}<commit_msg>Improve readability<commit_after>use ui::ncurses::*;
use ui::{RIGHT_PANEL_WIDTH, CMD_ENTRY_HEIGHT, Window, Size};

pub struct Chat {
    parent: Window,
    child:  Window,
}

// glibc will panic if a message containing a C-style format string is entered
fn sanitize(string: &str) -> String {
    string.replace('%', "%%")
}

impl Chat {
    pub fn new(size: Size) -> Chat {
        let parent = newwin(size.height - CMD_ENTRY_HEIGHT,
                            size.width - RIGHT_PANEL_WIDTH,
                            0,
                            0);
        box_(parent, 0, 0);
        wrefresh(parent);

        let child = derwin(parent,
                           size.height - CMD_ENTRY_HEIGHT - 2,
                           size.width - RIGHT_PANEL_WIDTH - 2,
                           1,
                           1);
        scrollok(child, true);

        Chat {
            parent: Window::new(parent),
            child:  Window::new(child),
        }
    }

    pub fn add_message(&self, msg: &str) {
        wprintw(self.child.id, &format!("{}\n", sanitize(msg)));
        wrefresh(self.child.id);
    }
}<|endoftext|>


<commit_before>use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io;
use std::io::prelude::*;

pub enum Input {
    Exit(i32),
    Fail(io::Error),
    Signal(i32),
    StdIn(Vec<u8>),
    StdInClosed,
    StdOut(Vec<u8>),
    StdErr(Vec<u8>),
}

/// Allocates a buffer of `num` bytes and reads that exact number
/// of bytes from `stream`
pub fn read_bytes(read: &mut Read, num: usize) -> io::Result<Vec<u8>> {
    let mut buf = vec![0; num];

    read.read_exact(&mut buf).map(|_| buf)
}

/// core event loop that reads events from a provided reader and
/// handles the events accordingly
pub fn input_handler<R, W, SW, StdOut, StdErr>(
    pid: i32,
    mut reader: R,
    mut writer: W,
    mut single_session_writer: SW,
    mut std_out: StdOut,
    mut std_err: StdErr,
) -> io::Result<i32>
where
    R: FnMut() -> io::Result<Input>,
    W: FnMut(Vec<u8>) -> io::Result<()>,
    StdOut: FnMut(Vec<u8>) -> io::Result<()>,
    SW: FnMut(Vec<u8>) -> io::Result<()>,
    StdErr: FnMut(Vec<u8>) -> io::Result<()>,
{
    loop {
        match reader() {
            Ok(Input::Exit(s)) => {
                return Ok(s);
            }

            Ok(Input::Fail(e)) => {
                return Err(e);
            }

            Ok(Input::StdIn(b)) => {
                if !b.is_empty() {
                    if let Err(e) = writer(b) {
                        return Err(e);
                    }
                }
            }

            Ok(Input::StdInClosed) => {
                if let Err(e) = writer(vec![]) {
                    return Err(e);
                }
                /*
                let result = encode_i32(pid)
                    .and_then(|ref mut pid_bytes| {
                        let mut data = vec![];

                        data.push(b't');
                        data.append(pid_bytes);

                        single_session_writer(data)
                    });

                if let Err(e) = result {
                    return Err(e);
                }*/
            }

            Ok(Input::Signal(s)) => {
                let result = encode_i32(pid)
                    .and_then(|pid_bytes| encode_i32(s).map(|sig_bytes| (pid_bytes, sig_bytes)))
                    .and_then(|(ref mut pid_bytes, ref mut sig_bytes)| {
                        let mut data = vec![];

                        data.push(b'k');
                        data.append(pid_bytes);
                        data.append(sig_bytes);

                        single_session_writer(data)
                    });

                if let Err(e) = result {
                    return Err(e);
                }
            }

            Ok(Input::StdOut(b)) => {
                if let Err(e) = std_out(b) {
                    return Err(e);
                }
            }

            Ok(Input::StdErr(b)) => {
                if let Err(e) = std_err(b) {
                    return Err(e);
                }
            }

            Err(e) => {
                return Err(e);
            }
        }
    }
}

/// manages reading the socket (landlord protocol)
pub fn read_handler<R, W>(mut reader: R, mut writer: W) -> io::Result<()>
where
    R: FnMut(usize) -> io::Result<Vec<u8>>,
    W: FnMut(Input) -> io::Result<()>,
{
    loop {
        match reader(1).map(|bs| bs[0]) {
            Ok(101) => {
                // UTF8 'e'
                let result = read_payload(&mut reader)
                    .map(|p| Input::StdErr(p))
                    .and_then(|msg| writer(msg));

                if result.is_err() {
                    return result;
                }
            }
            Ok(111) => {
                // UTF8 'o'
                let result = read_payload(&mut reader)
                    .map(|p| Input::StdOut(p))
                    .and_then(|msg| writer(msg));

                if result.is_err() {
                    return result;
                }
            }
            Ok(120) => {
                // UTF8 'x'

                return reader(4)
                    .and_then(|bs| decode_i32(&bs))
                    .and_then(|code| writer(Input::Exit(code)));
            }
            Ok(other) => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("Unknown code: {}", other),
                ))
            }
            Err(err) => {
                return Err(err);
            }
        }
    }
}

/// reads the process id from the provided `stream`
pub fn read_pid_handler(stream: &mut Read) -> Option<i32> {
    read_bytes(stream, 4)
        .ok()
        .and_then(|bs| io::Cursor::new(bs).read_i32::<BigEndian>().ok())
}

/// Creates the first line of data that is sent to landlordd when loading an app.
pub fn app_cmdline(
    class_path_with_names: &Vec<(String, String)>,
    props: &Vec<(String, String)>,
    class: &String,
    args: &Vec<String>,
) -> String {
    let props = props
        .iter()
        .map(|&(ref n, ref v)| format!("-D{}={}", n, v))
        .collect::<Vec<String>>()
        .join("\u{0000}");

    format!(
        "l{}-cp\u{0000}{}\u{0000}{}{}\n",
        if props == "" {
            "".to_string()
        } else {
            format!("{}\u{0000}", props)
        },
        class_path_with_names
            .iter()
            .map(|ref e| e.1.to_string())
            .collect::<Vec<String>>()
            .join(":"),
        class,
        if args.is_empty() {
            format!("")
        } else {
            format!("\u{0000}--\u{0000}{}", args.join("\u{0000}"))
        }
    )
}

/// Given class path entries, returns a new vector containing entries
/// as a tuple, each element: (path, name to store in tar file)
pub fn class_path_with_names(class_path: &Vec<String>) -> Vec<(String, String)> {
    class_path
        .iter()
        .enumerate()
        .map(|(i, e)| (e.to_string(), format!("{}", i)))
        .collect()
}

/// given a reader, reads a landlord payload, i.e. a 4-byte encoded (big endian) size followed
/// by that number of bytes.
fn read_payload<R>(reader: &mut R) -> io::Result<Vec<u8>>
where
    R: FnMut(usize) -> io::Result<Vec<u8>>,
{
    reader(4)
        .and_then(|bs| decode_i32(&bs))
        .and_then(|size| reader(size as usize))
}

fn decode_i32(vec: &Vec<u8>) -> io::Result<i32> {
    io::Cursor::new(vec).read_i32::<BigEndian>()
}

fn encode_i32(value: i32) -> io::Result<Vec<u8>> {
    let mut buf = vec![];

    buf.write_i32::<BigEndian>(value).map(|_| buf)
}

#[test]
fn test_app_cmdline_no_args() {
    assert_eq!(
        app_cmdline(
            &vec![
                ("/test1/one".to_string(), "0".to_string()),
                ("/test1/two".to_string(), "1".to_string()),
            ],
            &vec![],
            &"com.example.HelloWorld1".to_string(),
            &vec![]
        ),
        "l-cp\u{0000}0:1\u{0000}com.example.HelloWorld1\n".to_string()
    )
}

#[test]
fn test_app_cmdline_with_args() {
    assert_eq!(
        app_cmdline(
            &vec![
                ("/test2/one".to_string(), "0".to_string()),
                ("/test2/two".to_string(), "1".to_string()),
            ],
            &vec![],
            &"com.example.HelloWorld2".to_string(),
            &vec!["argone".to_string(), "arg two".to_string()]
        ),
        "l-cp\u{0000}0:1\u{0000}com.example.HelloWorld2\u{0000}--\u{0000}argone\u{0000}arg two\n"
            .to_string()
    )
}

#[test]
fn test_app_cmdline_with_args_props() {
    assert_eq!(
        app_cmdline(
            &vec![
                ("/test2/one".to_string(), "0".to_string()),
                ("/test2/two".to_string(), "1".to_string()),
            ],
            &vec![("one".to_string(), "#1!".to_string()), ("two".to_string(), "#2!".to_string())],
            &"com.example.HelloWorld2".to_string(),
            &vec!["argone".to_string(), "arg two".to_string()]
        ),
        "l-Done=#1!\u{0000}-Dtwo=#2!\u{0000}-cp\u{0000}0:1\u{0000}com.example.HelloWorld2\u{0000}--\u{0000}argone\u{0000}arg two\n"
            .to_string()
    )
}

#[test]
fn test_class_path_with_names() {
    assert_eq!(
        class_path_with_names(&vec!["/test/one".to_string(), "/test/two".to_string()]),
        vec![
            ("/test/one".to_string(), "0".to_string()),
            ("/test/two".to_string(), "1".to_string()),
        ]
    );
}

#[test]
fn test_decode_i32_invalid() {
    assert!(decode_i32(&vec![]).is_err());
    assert!(decode_i32(&vec![0]).is_err());
    assert!(decode_i32(&vec![0, 0]).is_err());
    assert!(decode_i32(&vec![0, 0, 0]).is_err());
}

#[test]
fn test_decode_i32_valid() {
    assert_eq!(decode_i32(&vec![0, 0, 0, 0]).ok(), Some(0));
    assert_eq!(decode_i32(&vec![0, 0, 0, 1]).ok(), Some(1));
    assert_eq!(decode_i32(&vec![1, 0, 0, 0]).ok(), Some(16777216));
    assert_eq!(decode_i32(&vec![1, 0, 0, 1]).ok(), Some(16777217));
}

#[test]
fn test_encode_i32_valid() {
    assert_eq!(encode_i32(0).ok(), Some(vec![0, 0, 0, 0]));
    assert_eq!(encode_i32(1).ok(), Some(vec![0, 0, 0, 1]));
    assert_eq!(encode_i32(16777216).ok(), Some(vec![1, 0, 0, 0]));
    assert_eq!(encode_i32(16777217).ok(), Some(vec![1, 0, 0, 1]));
}
<commit_msg>Remove -- from command line<commit_after>use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io;
use std::io::prelude::*;

pub enum Input {
    Exit(i32),
    Fail(io::Error),
    Signal(i32),
    StdIn(Vec<u8>),
    StdInClosed,
    StdOut(Vec<u8>),
    StdErr(Vec<u8>),
}

/// Allocates a buffer of `num` bytes and reads that exact number
/// of bytes from `stream`
pub fn read_bytes(read: &mut Read, num: usize) -> io::Result<Vec<u8>> {
    let mut buf = vec![0; num];

    read.read_exact(&mut buf).map(|_| buf)
}

/// core event loop that reads events from a provided reader and
/// handles the events accordingly
pub fn input_handler<R, W, SW, StdOut, StdErr>(
    pid: i32,
    mut reader: R,
    mut writer: W,
    mut single_session_writer: SW,
    mut std_out: StdOut,
    mut std_err: StdErr,
) -> io::Result<i32>
where
    R: FnMut() -> io::Result<Input>,
    W: FnMut(Vec<u8>) -> io::Result<()>,
    StdOut: FnMut(Vec<u8>) -> io::Result<()>,
    SW: FnMut(Vec<u8>) -> io::Result<()>,
    StdErr: FnMut(Vec<u8>) -> io::Result<()>,
{
    loop {
        match reader() {
            Ok(Input::Exit(s)) => {
                return Ok(s);
            }

            Ok(Input::Fail(e)) => {
                return Err(e);
            }

            Ok(Input::StdIn(b)) => {
                if !b.is_empty() {
                    if let Err(e) = writer(b) {
                        return Err(e);
                    }
                }
            }

            Ok(Input::StdInClosed) => {
                if let Err(e) = writer(vec![]) {
                    return Err(e);
                }
                /*
                let result = encode_i32(pid)
                    .and_then(|ref mut pid_bytes| {
                        let mut data = vec![];

                        data.push(b't');
                        data.append(pid_bytes);

                        single_session_writer(data)
                    });

                if let Err(e) = result {
                    return Err(e);
                }*/
            }

            Ok(Input::Signal(s)) => {
                let result = encode_i32(pid)
                    .and_then(|pid_bytes| encode_i32(s).map(|sig_bytes| (pid_bytes, sig_bytes)))
                    .and_then(|(ref mut pid_bytes, ref mut sig_bytes)| {
                        let mut data = vec![];

                        data.push(b'k');
                        data.append(pid_bytes);
                        data.append(sig_bytes);

                        single_session_writer(data)
                    });

                if let Err(e) = result {
                    return Err(e);
                }
            }

            Ok(Input::StdOut(b)) => {
                if let Err(e) = std_out(b) {
                    return Err(e);
                }
            }

            Ok(Input::StdErr(b)) => {
                if let Err(e) = std_err(b) {
                    return Err(e);
                }
            }

            Err(e) => {
                return Err(e);
            }
        }
    }
}

/// manages reading the socket (landlord protocol)
pub fn read_handler<R, W>(mut reader: R, mut writer: W) -> io::Result<()>
where
    R: FnMut(usize) -> io::Result<Vec<u8>>,
    W: FnMut(Input) -> io::Result<()>,
{
    loop {
        match reader(1).map(|bs| bs[0]) {
            Ok(101) => {
                // UTF8 'e'
                let result = read_payload(&mut reader)
                    .map(|p| Input::StdErr(p))
                    .and_then(|msg| writer(msg));

                if result.is_err() {
                    return result;
                }
            }
            Ok(111) => {
                // UTF8 'o'
                let result = read_payload(&mut reader)
                    .map(|p| Input::StdOut(p))
                    .and_then(|msg| writer(msg));

                if result.is_err() {
                    return result;
                }
            }
            Ok(120) => {
                // UTF8 'x'

                return reader(4)
                    .and_then(|bs| decode_i32(&bs))
                    .and_then(|code| writer(Input::Exit(code)));
            }
            Ok(other) => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("Unknown code: {}", other),
                ))
            }
            Err(err) => {
                return Err(err);
            }
        }
    }
}

/// reads the process id from the provided `stream`
pub fn read_pid_handler(stream: &mut Read) -> Option<i32> {
    read_bytes(stream, 4)
        .ok()
        .and_then(|bs| io::Cursor::new(bs).read_i32::<BigEndian>().ok())
}

/// Creates the first line of data that is sent to landlordd when loading an app.
pub fn app_cmdline(
    class_path_with_names: &Vec<(String, String)>,
    props: &Vec<(String, String)>,
    class: &String,
    args: &Vec<String>,
) -> String {
    let props = props
        .iter()
        .map(|&(ref n, ref v)| format!("-D{}={}", n, v))
        .collect::<Vec<String>>()
        .join("\u{0000}");

    format!(
        "l{}-cp\u{0000}{}\u{0000}{}{}\n",
        if props == "" {
            "".to_string()
        } else {
            format!("{}\u{0000}", props)
        },
        class_path_with_names
            .iter()
            .map(|ref e| e.1.to_string())
            .collect::<Vec<String>>()
            .join(":"),
        class,
        if args.is_empty() {
            format!("")
        } else {
            format!("\u{0000}{}", args.join("\u{0000}"))
        }
    )
}

/// Given class path entries, returns a new vector containing entries
/// as a tuple, each element: (path, name to store in tar file)
pub fn class_path_with_names(class_path: &Vec<String>) -> Vec<(String, String)> {
    class_path
        .iter()
        .enumerate()
        .map(|(i, e)| (e.to_string(), format!("{}", i)))
        .collect()
}

/// given a reader, reads a landlord payload, i.e. a 4-byte encoded (big endian) size followed
/// by that number of bytes.
fn read_payload<R>(reader: &mut R) -> io::Result<Vec<u8>>
where
    R: FnMut(usize) -> io::Result<Vec<u8>>,
{
    reader(4)
        .and_then(|bs| decode_i32(&bs))
        .and_then(|size| reader(size as usize))
}

fn decode_i32(vec: &Vec<u8>) -> io::Result<i32> {
    io::Cursor::new(vec).read_i32::<BigEndian>()
}

fn encode_i32(value: i32) -> io::Result<Vec<u8>> {
    let mut buf = vec![];

    buf.write_i32::<BigEndian>(value).map(|_| buf)
}

#[test]
fn test_app_cmdline_no_args() {
    assert_eq!(
        app_cmdline(
            &vec![
                ("/test1/one".to_string(), "0".to_string()),
                ("/test1/two".to_string(), "1".to_string()),
            ],
            &vec![],
            &"com.example.HelloWorld1".to_string(),
            &vec![]
        ),
        "l-cp\u{0000}0:1\u{0000}com.example.HelloWorld1\n".to_string()
    )
}

#[test]
fn test_app_cmdline_with_args() {
    assert_eq!(
        app_cmdline(
            &vec![
                ("/test2/one".to_string(), "0".to_string()),
                ("/test2/two".to_string(), "1".to_string()),
            ],
            &vec![],
            &"com.example.HelloWorld2".to_string(),
            &vec!["argone".to_string(), "arg two".to_string()]
        ),
        "l-cp\u{0000}0:1\u{0000}com.example.HelloWorld2\u{0000}argone\u{0000}arg two\n"
            .to_string()
    )
}

#[test]
fn test_app_cmdline_with_args_props() {
    assert_eq!(
        app_cmdline(
            &vec![
                ("/test2/one".to_string(), "0".to_string()),
                ("/test2/two".to_string(), "1".to_string()),
            ],
            &vec![("one".to_string(), "#1!".to_string()), ("two".to_string(), "#2!".to_string())],
            &"com.example.HelloWorld2".to_string(),
            &vec!["argone".to_string(), "arg two".to_string()]
        ),
        "l-Done=#1!\u{0000}-Dtwo=#2!\u{0000}-cp\u{0000}0:1\u{0000}com.example.HelloWorld2\u{0000}argone\u{0000}arg two\n"
            .to_string()
    )
}

#[test]
fn test_class_path_with_names() {
    assert_eq!(
        class_path_with_names(&vec!["/test/one".to_string(), "/test/two".to_string()]),
        vec![
            ("/test/one".to_string(), "0".to_string()),
            ("/test/two".to_string(), "1".to_string()),
        ]
    );
}

#[test]
fn test_decode_i32_invalid() {
    assert!(decode_i32(&vec![]).is_err());
    assert!(decode_i32(&vec![0]).is_err());
    assert!(decode_i32(&vec![0, 0]).is_err());
    assert!(decode_i32(&vec![0, 0, 0]).is_err());
}

#[test]
fn test_decode_i32_valid() {
    assert_eq!(decode_i32(&vec![0, 0, 0, 0]).ok(), Some(0));
    assert_eq!(decode_i32(&vec![0, 0, 0, 1]).ok(), Some(1));
    assert_eq!(decode_i32(&vec![1, 0, 0, 0]).ok(), Some(16777216));
    assert_eq!(decode_i32(&vec![1, 0, 0, 1]).ok(), Some(16777217));
}

#[test]
fn test_encode_i32_valid() {
    assert_eq!(encode_i32(0).ok(), Some(vec![0, 0, 0, 0]));
    assert_eq!(encode_i32(1).ok(), Some(vec![0, 0, 0, 1]));
    assert_eq!(encode_i32(16777216).ok(), Some(vec![1, 0, 0, 0]));
    assert_eq!(encode_i32(16777217).ok(), Some(vec![1, 0, 0, 1]));
}
<|endoftext|>


<commit_before>extern crate rayon;
use self::rayon::prelude::*;

pub mod gofloat;
pub mod demosaic;
pub mod level;
pub mod colorspaces;
pub mod curves;
pub mod gamma;
pub mod transform;

use decoders::RawImage;

extern crate time;

#[derive(Debug, Clone, PartialEq)]
pub struct OpBuffer {
  pub width: usize,
  pub height: usize,
  pub colors: usize,
  pub data: Vec<f32>,
}

impl OpBuffer {
  pub fn new(width: usize, height: usize, colors: usize) -> OpBuffer {
    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: vec![0.0; width*height*(colors as usize)],
    }
  }

  pub fn mutate_lines<F>(&mut self, closure: &F)
    where F : Fn(&mut [f32], usize)+Sync {

    self.data.par_chunks_mut(self.width*self.colors).enumerate().for_each(|(row, line)| {
      closure(line, row);
    });
  }

  pub fn process_into_new<F>(&self, colors: usize, closure: &F) -> OpBuffer
    where F : Fn(&mut [f32], &[f32])+Sync {

    let mut out = OpBuffer::new(self.width, self.height, colors);
    out.data.par_chunks_mut(out.width*out.colors).enumerate().for_each(|(row, line)| {
      closure(line, &self.data[self.width*self.colors*row..]);
    });
    out
  }

  /// Helper function to allow human readable creation of `OpBuffer` instances
  pub fn from_rgb_str_vec(data: Vec<&str>) -> OpBuffer {
    let width = data.first().expect("Invalid data for rgb helper function").len();
    let height = data.len();
    let colors = 3;

    let mut pixel_data: Vec<f32> = Vec::with_capacity(width * height * colors);
    for row in data {
      for col in row.chars() {
        let (r, g, b) = match col {
            'R' => (1.0, 0.0, 0.0),
            'G' => (0.0, 1.0, 0.0),
            'B' => (0.0, 0.0, 1.0),
            'O' => (1.0, 1.0, 1.0),
            ' ' => (0.0, 0.0, 0.0),
            c @ _ => panic!(format!(
              "Invalid color '{}' sent to rgb expected any of 'RGBO '", c)),
        };

        pixel_data.push(r);
        pixel_data.push(g);
        pixel_data.push(b);
      }
    }

    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: pixel_data,
    }
  }
}

fn do_timing<O, F: FnMut() -> O>(name: &str, mut closure: F) -> O {
  let from_time = time::precise_time_ns();
  let ret = closure();
  let to_time = time::precise_time_ns();
  println!("{} ms for '{}'", (to_time - from_time)/1000000, name);

  ret
}

pub trait ImageOp {
  fn name(&self) -> &str;
  fn run(&self, pipeline: &Pipeline, buf: &OpBuffer) -> OpBuffer;
}

#[derive(Clone, Debug)]
pub struct Pipeline<'a> {
  maxwidth: usize,
  maxheight: usize,
  linear: bool,
  image: &'a RawImage,
  gofloat: gofloat::OpGoFloat,
  demosaic: demosaic::OpDemosaic,
  level: level::OpLevel,
  tolab: colorspaces::OpToLab,
  basecurve: curves::OpBaseCurve,
  fromlab: colorspaces::OpFromLab,
  gamma: gamma::OpGamma,
  transform: transform::OpTransform,
}

impl<'a> Pipeline<'a> {
  pub fn new(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> Pipeline {
    // Check if the image's orientation results in a rotation that
    // swaps the maximum width with the maximum height
    let (transpose, ..) = img.orientation.to_flips();
    let (maxwidth, maxheight) = if transpose {
      (maxheight, maxwidth)
    } else {
      (maxwidth, maxheight)
    };

    Pipeline {
      maxwidth,
      maxheight,
      linear,
      image: img,
      gofloat: gofloat::OpGoFloat::new(img),
      demosaic: demosaic::OpDemosaic::new(img),
      level: level::OpLevel::new(img),
      tolab: colorspaces::OpToLab::new(img),
      basecurve: curves::OpBaseCurve::new(img),
      fromlab: colorspaces::OpFromLab::new(img),
      gamma: gamma::OpGamma::new(img),
      transform: transform::OpTransform::new(img),
    }
  }

  pub fn run(&self) -> OpBuffer {
    // Start with a dummy buffer, gofloat doesn't use it
    let mut buf = OpBuffer::new(0,0,0);
    let ops: Vec<Box<&ImageOp>> = vec![
      Box::new(&self.gofloat),
      Box::new(&self.demosaic),
      Box::new(&self.level),
      Box::new(&self.tolab),
      Box::new(&self.basecurve),
      Box::new(&self.fromlab),
      Box::new(&self.gamma),
      Box::new(&self.transform),
    ];
    for op in ops {
      buf = do_timing(op.name(), ||op.run(self, &buf));
    }
    buf
  }
}

pub fn simple_decode(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  let pipeline = Pipeline::new(img, maxwidth, maxheight, false);
  pipeline.run()
}

pub fn simple_decode_linear(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  let pipeline = Pipeline::new(img, maxwidth, maxheight, true);
  pipeline.run()
}
<commit_msg>pipeline: move ops list into its own function<commit_after>extern crate rayon;
use self::rayon::prelude::*;

pub mod gofloat;
pub mod demosaic;
pub mod level;
pub mod colorspaces;
pub mod curves;
pub mod gamma;
pub mod transform;

use decoders::RawImage;

extern crate time;

#[derive(Debug, Clone, PartialEq)]
pub struct OpBuffer {
  pub width: usize,
  pub height: usize,
  pub colors: usize,
  pub data: Vec<f32>,
}

impl OpBuffer {
  pub fn new(width: usize, height: usize, colors: usize) -> OpBuffer {
    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: vec![0.0; width*height*(colors as usize)],
    }
  }

  pub fn mutate_lines<F>(&mut self, closure: &F)
    where F : Fn(&mut [f32], usize)+Sync {

    self.data.par_chunks_mut(self.width*self.colors).enumerate().for_each(|(row, line)| {
      closure(line, row);
    });
  }

  pub fn process_into_new<F>(&self, colors: usize, closure: &F) -> OpBuffer
    where F : Fn(&mut [f32], &[f32])+Sync {

    let mut out = OpBuffer::new(self.width, self.height, colors);
    out.data.par_chunks_mut(out.width*out.colors).enumerate().for_each(|(row, line)| {
      closure(line, &self.data[self.width*self.colors*row..]);
    });
    out
  }

  /// Helper function to allow human readable creation of `OpBuffer` instances
  pub fn from_rgb_str_vec(data: Vec<&str>) -> OpBuffer {
    let width = data.first().expect("Invalid data for rgb helper function").len();
    let height = data.len();
    let colors = 3;

    let mut pixel_data: Vec<f32> = Vec::with_capacity(width * height * colors);
    for row in data {
      for col in row.chars() {
        let (r, g, b) = match col {
            'R' => (1.0, 0.0, 0.0),
            'G' => (0.0, 1.0, 0.0),
            'B' => (0.0, 0.0, 1.0),
            'O' => (1.0, 1.0, 1.0),
            ' ' => (0.0, 0.0, 0.0),
            c @ _ => panic!(format!(
              "Invalid color '{}' sent to rgb expected any of 'RGBO '", c)),
        };

        pixel_data.push(r);
        pixel_data.push(g);
        pixel_data.push(b);
      }
    }

    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: pixel_data,
    }
  }
}

fn do_timing<O, F: FnMut() -> O>(name: &str, mut closure: F) -> O {
  let from_time = time::precise_time_ns();
  let ret = closure();
  let to_time = time::precise_time_ns();
  println!("{} ms for '{}'", (to_time - from_time)/1000000, name);

  ret
}

pub trait ImageOp {
  fn name(&self) -> &str;
  fn run(&self, pipeline: &Pipeline, buf: &OpBuffer) -> OpBuffer;
}

#[derive(Clone, Debug)]
pub struct Pipeline<'a> {
  maxwidth: usize,
  maxheight: usize,
  linear: bool,
  image: &'a RawImage,
  gofloat: gofloat::OpGoFloat,
  demosaic: demosaic::OpDemosaic,
  level: level::OpLevel,
  tolab: colorspaces::OpToLab,
  basecurve: curves::OpBaseCurve,
  fromlab: colorspaces::OpFromLab,
  gamma: gamma::OpGamma,
  transform: transform::OpTransform,
}

impl<'a> Pipeline<'a> {
  pub fn new(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> Pipeline {
    // Check if the image's orientation results in a rotation that
    // swaps the maximum width with the maximum height
    let (transpose, ..) = img.orientation.to_flips();
    let (maxwidth, maxheight) = if transpose {
      (maxheight, maxwidth)
    } else {
      (maxwidth, maxheight)
    };

    Pipeline {
      maxwidth,
      maxheight,
      linear,
      image: img,
      gofloat: gofloat::OpGoFloat::new(img),
      demosaic: demosaic::OpDemosaic::new(img),
      level: level::OpLevel::new(img),
      tolab: colorspaces::OpToLab::new(img),
      basecurve: curves::OpBaseCurve::new(img),
      fromlab: colorspaces::OpFromLab::new(img),
      gamma: gamma::OpGamma::new(img),
      transform: transform::OpTransform::new(img),
    }
  }

  pub fn ops(&self) -> Vec<&ImageOp> {
    vec![
      &self.gofloat,
      &self.demosaic,
      &self.level,
      &self.tolab,
      &self.basecurve,
      &self.fromlab,
      &self.gamma,
      &self.transform,
    ]
  }

  pub fn run(&self) -> OpBuffer {
    // Start with a dummy buffer, gofloat doesn't use it
    let mut buf = OpBuffer::new(0,0,0);
    for op in self.ops() {
      buf = do_timing(op.name(), ||op.run(self, &buf));
    }
    buf
  }
}

pub fn simple_decode(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  let pipeline = Pipeline::new(img, maxwidth, maxheight, false);
  pipeline.run()
}

pub fn simple_decode_linear(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  let pipeline = Pipeline::new(img, maxwidth, maxheight, true);
  pipeline.run()
}
<|endoftext|>


<commit_before>#[cfg(not(feature = "runtime-dispatch-simd"))]
use core::{mem, ptr, usize};
#[cfg(feature = "runtime-dispatch-simd")]
use std::{mem, ptr, usize};

fn splat(byte: u8) -> usize {
    let lo = usize::MAX / 0xFF;
    lo * byte as usize
}

unsafe fn usize_load_unchecked(bytes: &[u8], offset: usize) -> usize {
    let mut output = 0;
    ptr::copy_nonoverlapping(
        bytes.as_ptr().offset(offset as isize),
        &mut output as *mut usize as *mut u8,
        mem::size_of::<usize>()
    );
    output
}

fn bytewise_equal(lhs: usize, rhs: usize) -> usize {
    let lo = usize::MAX / 0xFF;
    let hi = lo << 7;

    let x = lhs ^ rhs;
    !((((x & !hi) + !hi) | x) >> 7) & lo
}

fn sum_usize(values: usize) -> usize {
    let every_other_byte_lo = usize::MAX / 0xFFFF;
    let every_other_byte = every_other_byte_lo * 0xFF;

    // Pairwise reduction to avoid overflow on next step.
    let pair_sum: usize = (values & every_other_byte) + ((values >> 8) & every_other_byte);

    // Multiplication results in top two bytes holding sum.
    pair_sum.wrapping_mul(every_other_byte_lo) >> ((mem::size_of::<usize>() - 2) * 8)
}

fn is_leading_utf8_byte(values: usize) -> usize {
    // a leading UTF-8 byte is one which does not start with the bits 10.
    ((!values >> 7) | (values >> 6)) & splat(1)
}

pub fn chunk_count(haystack: &[u8], needle: u8) -> usize {
    let chunksize = mem::size_of::<usize>();
    assert!(haystack.len() >= chunksize);

    unsafe {
        let mut offset = 0;
        let mut count = 0;

        let needles = splat(needle);

        // 2040
        while haystack.len() >= offset + chunksize * 255 {
            let mut counts = 0;
            for _ in 0..255 {
                counts += bytewise_equal(usize_load_unchecked(haystack, offset), needles);
                offset += chunksize;
            }
            count += sum_usize(counts);
        }

        // 8
        let mut counts = 0;
        for i in 0..(haystack.len() - offset) / chunksize {
            counts += bytewise_equal(usize_load_unchecked(haystack, offset + i * chunksize), needles);
        }
        if haystack.len() % 8 != 0 {
            let mask = !(!0 >> ((haystack.len() % chunksize) * 8));
            counts += bytewise_equal(usize_load_unchecked(haystack, haystack.len() - chunksize), needles) & mask;
        }
        count += sum_usize(counts);

        count
    }
}

pub fn chunk_num_chars(utf8_chars: &[u8]) -> usize {
    let chunksize = mem::size_of::<usize>();
    assert!(utf8_chars.len() >= chunksize);

    unsafe {
        let mut offset = 0;
        let mut count = 0;

        // 2040
        while utf8_chars.len() >= offset + chunksize * 255 {
            let mut counts = 0;
            for _ in 0..255 {
                counts += is_leading_utf8_byte(usize_load_unchecked(utf8_chars, offset));
                offset += chunksize;
            }
            count += sum_usize(counts);
        }

        // 8
        let mut counts = 0;
        for i in 0..(utf8_chars.len() - offset) / chunksize {
            counts += is_leading_utf8_byte(usize_load_unchecked(utf8_chars, offset + i * chunksize));
        }
        if utf8_chars.len() % 8 != 0 {
            let mask = !(!0 >> ((utf8_chars.len() % chunksize) * 8));
            counts += is_leading_utf8_byte(usize_load_unchecked(utf8_chars, utf8_chars.len() - chunksize)) & mask;
        }
        count += sum_usize(counts);

        count
    }
}
<commit_msg>Algorithm failed on big endian machines #53<commit_after>#[cfg(not(feature = "runtime-dispatch-simd"))]
use core::{mem, ptr, usize};
#[cfg(feature = "runtime-dispatch-simd")]
use std::{mem, ptr, usize};

fn splat(byte: u8) -> usize {
    let lo = usize::MAX / 0xFF;
    lo * byte as usize
}

unsafe fn usize_load_unchecked(bytes: &[u8], offset: usize) -> usize {
    let mut output = 0;
    ptr::copy_nonoverlapping(
        bytes.as_ptr().offset(offset as isize),
        &mut output as *mut usize as *mut u8,
        mem::size_of::<usize>()
    );
    output
}

fn bytewise_equal(lhs: usize, rhs: usize) -> usize {
    let lo = usize::MAX / 0xFF;
    let hi = lo << 7;

    let x = lhs ^ rhs;
    !((((x & !hi) + !hi) | x) >> 7) & lo
}

fn sum_usize(values: usize) -> usize {
    let every_other_byte_lo = usize::MAX / 0xFFFF;
    let every_other_byte = every_other_byte_lo * 0xFF;

    // Pairwise reduction to avoid overflow on next step.
    let pair_sum: usize = (values & every_other_byte) + ((values >> 8) & every_other_byte);

    // Multiplication results in top two bytes holding sum.
    pair_sum.wrapping_mul(every_other_byte_lo) >> ((mem::size_of::<usize>() - 2) * 8)
}

fn is_leading_utf8_byte(values: usize) -> usize {
    // a leading UTF-8 byte is one which does not start with the bits 10.
    ((!values >> 7) | (values >> 6)) & splat(1)
}

pub fn chunk_count(haystack: &[u8], needle: u8) -> usize {
    let chunksize = mem::size_of::<usize>();
    assert!(haystack.len() >= chunksize);

    unsafe {
        let mut offset = 0;
        let mut count = 0;

        let needles = splat(needle);

        // 2040
        while haystack.len() >= offset + chunksize * 255 {
            let mut counts = 0;
            for _ in 0..255 {
                counts += bytewise_equal(usize_load_unchecked(haystack, offset), needles);
                offset += chunksize;
            }
            count += sum_usize(counts);
        }

        // 8
        let mut counts = 0;
        for i in 0..(haystack.len() - offset) / chunksize {
            counts += bytewise_equal(usize_load_unchecked(haystack, offset + i * chunksize), needles);
        }
        if haystack.len() % 8 != 0 {
            let mask = usize::from_le(!(!0 >> ((haystack.len() % chunksize) * 8)));
            counts += bytewise_equal(usize_load_unchecked(haystack, haystack.len() - chunksize), needles) & mask;
        }
        count += sum_usize(counts);

        count
    }
}

pub fn chunk_num_chars(utf8_chars: &[u8]) -> usize {
    let chunksize = mem::size_of::<usize>();
    assert!(utf8_chars.len() >= chunksize);

    unsafe {
        let mut offset = 0;
        let mut count = 0;

        // 2040
        while utf8_chars.len() >= offset + chunksize * 255 {
            let mut counts = 0;
            for _ in 0..255 {
                counts += is_leading_utf8_byte(usize_load_unchecked(utf8_chars, offset));
                offset += chunksize;
            }
            count += sum_usize(counts);
        }

        // 8
        let mut counts = 0;
        for i in 0..(utf8_chars.len() - offset) / chunksize {
            counts += is_leading_utf8_byte(usize_load_unchecked(utf8_chars, offset + i * chunksize));
        }
        if utf8_chars.len() % 8 != 0 {
            let mask = usize::from_le(!(!0 >> ((utf8_chars.len() % chunksize) * 8)));
            counts += is_leading_utf8_byte(usize_load_unchecked(utf8_chars, utf8_chars.len() - chunksize)) & mask;
        }
        count += sum_usize(counts);

        count
    }
}
<|endoftext|>


<commit_before>use dynasmrt::x64::Assembler;
use dynasmrt::{DynasmApi, DynasmLabelApi};

use super::*;
use peephole;

dynasm!(asm
    ; .alias pointer, r12
    ; .alias mem_start, r13
    ; .alias mem_limit, r14
    ; .alias rts, r15
);

pub fn compile(program: &peephole::Program) -> Program {
    let mut asm = dynasmrt::x64::Assembler::new();
    let start = asm.offset();

    dynasm!(asm
        ; push r12
        ; push r13
        ; push r14
        ; push r15
        ; mov pointer, rcx
        ; mov mem_start, rcx
        ; mov mem_limit, rcx
        ; add mem_limit, rdx
        ; mov rts, r8
    );

    compile_sequence(&mut asm, program);

    dynasm!(asm
        ; mov rax, rts::OKAY as i32
        ; jmp ->finish

        ; ->underflow:
        ; mov rax, rts::UNDERFLOW as i32
        ; jmp ->finish

        ; ->overflow:
        ; mov rax, rts::OVERFLOW as i32

        ; ->finish:
        ; pop r15
        ; pop r14
        ; pop r13
        ; pop r12
        ; ret
    );

    let code = asm.finalize().unwrap();

    Program {
        code: code,
        start: start,
    }
}

pub fn compile_sequence(asm: &mut Assembler, program: &[peephole::Instruction]) {
    for instruction in program {
        compile_instruction(asm, instruction);
    }
}

pub fn compile_instruction(asm: &mut Assembler, instruction: &peephole::Instruction) {
    use peephole::Instruction::*;

    match *instruction {
        Right(count) => {
            dynasm!(asm
                ;; load_pos_offset(asm, count)
                ; add pointer, rax
            );
        }

        Left(count) => {
            dynasm!(asm
                ;; load_neg_offset(asm, count)
                ; sub pointer, rax
            );
        }

        Change(count) => {
            dynasm!(asm
                ; add [pointer], BYTE u8_to_i8(count)
            );
        }

        In => {
            dynasm!(asm
                ; mov rax, QWORD rts::RtsState::read as _
                ; mov rcx, rts
                ; sub rsp, BYTE 0x28
                ; call rax
                ; add rsp, BYTE 0x28
                ; mov [pointer], al
            );
        }

        Out => {
            dynasm!(asm
                ; mov rax, QWORD rts::RtsState::write as _
                ; mov rcx, rts
                ; xor rdx, rdx
                ; mov dl, [pointer]
                ; sub rsp, BYTE 0x28
                ; call rax
                ; add rsp, BYTE 0x28
            );
        }

        SetZero => {
            dynasm!(asm
                ; mov BYTE [pointer], 0
            )
        }

        FindZeroRight(skip) => {
            dynasm!(asm
                ; jmp >end_loop
                ; begin_loop:
                ;; load_pos_offset(asm, skip)
                ; add pointer, rax
                ; end_loop:
                ; cmp BYTE [pointer], 0
                ; jnz <begin_loop
            )
        }

        FindZeroLeft(skip) => {
            dynasm!(asm
                ; jmp >end_loop
                ; begin_loop:
                ;; load_neg_offset(asm, skip)
                ; sub pointer, rax
                ; end_loop:
                ; cmp BYTE [pointer], 0
                ; jnz <begin_loop
            )
        }

        OffsetAddRight(offset) => {
            dynasm!(asm
                ; cmp BYTE [pointer], 0
                ; jz >skip
                ;; load_pos_offset(asm, offset)
                ; mov cl, BYTE [pointer]
                ; mov BYTE [pointer], 0
                ; add BYTE [pointer + rax], cl
                ; skip:
            );
        }

        OffsetAddLeft(offset) => {
            dynasm!(asm
                ; cmp BYTE [pointer], 0
                ; jz >skip
                ;; load_neg_offset(asm, offset)
                ; mov cl, BYTE [pointer]
                ; mov BYTE [pointer], 0
                ; neg rax
                ; add BYTE [pointer + rax], cl
                ; skip:
            );
        }

        Loop(ref body) => {
            let begin_label = asm.new_dynamic_label();
            let end_label   = asm.new_dynamic_label();

            dynasm!(asm
                ; jmp =>end_label
                ; =>begin_label
            );

            compile_sequence(asm, body);

            dynasm!(asm
                ; =>end_label
                ; cmp BYTE [pointer], 0
                ; jnz =>begin_label
            );
        }
    }
}

fn load_pos_offset(asm: &mut Assembler, offset: usize) {
    dynasm!(asm
        ; mov rax, QWORD offset as i64
    );

    if cfg!(not(feature = "omit_bounds_checks")) {
        dynasm!(asm
            ; mov rcx, mem_limit
            ; sub rcx, pointer
            ; cmp rcx, rax
            ; jle ->overflow
        );
    }
}

fn load_neg_offset(asm: &mut Assembler, offset: usize) {
    dynasm!(asm
        ; mov rax, QWORD offset as i64
    );

    if cfg!(not(feature = "omit_bounds_checks")) {
        dynasm!(asm
            ; mov rcx, pointer
            ; sub rcx, mem_start
            ; cmp rcx, rax
            ; jl ->underflow
        );
    }
}

fn u8_to_i8(n: u8) -> i8 {
    let mut n = n as isize % 256;
    if n > 127 { n -= 256 };
    n as i8
}
<commit_msg>Bounds checking in the JIT is now disabled by a (dynamic) flag.<commit_after>use dynasmrt::x64::Assembler;
use dynasmrt::{DynasmApi, DynasmLabelApi};

use super::*;
use peephole;

dynasm!(asm
    ; .alias pointer, r12
    ; .alias mem_start, r13
    ; .alias mem_limit, r14
    ; .alias rts, r15
);

pub fn compile(program: &peephole::Program) -> Program {
    let mut asm = dynasmrt::x64::Assembler::new();
    let start = asm.offset();

    dynasm!(asm
        ; push r12
        ; push r13
        ; push r14
        ; push r15
        ; mov pointer, rcx
        ; mov mem_start, rcx
        ; mov mem_limit, rcx
        ; add mem_limit, rdx
        ; mov rts, r8
    );

    compile_sequence(&mut asm, program, true);

    dynasm!(asm
        ; mov rax, rts::OKAY as i32
        ; jmp ->finish

        ; ->underflow:
        ; mov rax, rts::UNDERFLOW as i32
        ; jmp ->finish

        ; ->overflow:
        ; mov rax, rts::OVERFLOW as i32

        ; ->finish:
        ; pop r15
        ; pop r14
        ; pop r13
        ; pop r12
        ; ret
    );

    let code = asm.finalize().unwrap();

    Program {
        code: code,
        start: start,
    }
}

pub fn compile_sequence(asm: &mut Assembler,
                        program: &[peephole::Instruction],
                        checked: bool)
{
    for instruction in program {
        compile_instruction(asm, instruction, checked);
    }
}

pub fn compile_instruction(asm: &mut Assembler,
                           instruction: &peephole::Instruction,
                           checked: bool)
{
    use peephole::Instruction::*;

    match *instruction {
        Right(count) => {
            dynasm!(asm
                ;; load_pos_offset(asm, count, checked)
                ; add pointer, rax
            );
        }

        Left(count) => {
            dynasm!(asm
                ;; load_neg_offset(asm, count, checked)
                ; sub pointer, rax
            );
        }

        Change(count) => {
            dynasm!(asm
                ; add [pointer], BYTE u8_to_i8(count)
            );
        }

        In => {
            dynasm!(asm
                ; mov rax, QWORD rts::RtsState::read as _
                ; mov rcx, rts
                ; sub rsp, BYTE 0x28
                ; call rax
                ; add rsp, BYTE 0x28
                ; mov [pointer], al
            );
        }

        Out => {
            dynasm!(asm
                ; mov rax, QWORD rts::RtsState::write as _
                ; mov rcx, rts
                ; xor rdx, rdx
                ; mov dl, [pointer]
                ; sub rsp, BYTE 0x28
                ; call rax
                ; add rsp, BYTE 0x28
            );
        }

        SetZero => {
            dynasm!(asm
                ; mov BYTE [pointer], 0
            )
        }

        FindZeroRight(skip) => {
            dynasm!(asm
                ; jmp >end_loop
                ; begin_loop:
                ;; load_pos_offset(asm, skip, checked)
                ; add pointer, rax
                ; end_loop:
                ; cmp BYTE [pointer], 0
                ; jnz <begin_loop
            )
        }

        FindZeroLeft(skip) => {
            dynasm!(asm
                ; jmp >end_loop
                ; begin_loop:
                ;; load_neg_offset(asm, skip, checked)
                ; sub pointer, rax
                ; end_loop:
                ; cmp BYTE [pointer], 0
                ; jnz <begin_loop
            )
        }

        OffsetAddRight(offset) => {
            dynasm!(asm
                ; cmp BYTE [pointer], 0
                ; jz >skip
                ;; load_pos_offset(asm, offset, checked)
                ; mov cl, BYTE [pointer]
                ; mov BYTE [pointer], 0
                ; add BYTE [pointer + rax], cl
                ; skip:
            );
        }

        OffsetAddLeft(offset) => {
            dynasm!(asm
                ; cmp BYTE [pointer], 0
                ; jz >skip
                ;; load_neg_offset(asm, offset, checked)
                ; mov cl, BYTE [pointer]
                ; mov BYTE [pointer], 0
                ; neg rax
                ; add BYTE [pointer + rax], cl
                ; skip:
            );
        }

        Loop(ref body) => {
            let begin_label = asm.new_dynamic_label();
            let end_label   = asm.new_dynamic_label();

            dynasm!(asm
                ; jmp =>end_label
                ; =>begin_label
            );

            compile_sequence(asm, body, checked);

            dynasm!(asm
                ; =>end_label
                ; cmp BYTE [pointer], 0
                ; jnz =>begin_label
            );
        }
    }
}

fn load_pos_offset(asm: &mut Assembler, offset: usize, checked: bool) {
    dynasm!(asm
        ; mov rax, QWORD offset as i64
    );

    if checked {
        dynasm!(asm
            ; mov rcx, mem_limit
            ; sub rcx, pointer
            ; cmp rcx, rax
            ; jle ->overflow
        );
    }
}

fn load_neg_offset(asm: &mut Assembler, offset: usize, checked: bool) {
    dynasm!(asm
        ; mov rax, QWORD offset as i64
    );

    if checked {
        dynasm!(asm
            ; mov rcx, pointer
            ; sub rcx, mem_start
            ; cmp rcx, rax
            ; jl ->underflow
        );
    }
}

fn u8_to_i8(n: u8) -> i8 {
    let mut n = n as isize % 256;
    if n > 127 { n -= 256 };
    n as i8
}
<|endoftext|>