<commit_before>//! Infers how each function uses every register

use analysis::inst_combine;
use frontend::radeco_containers::{RadecoFunction, RadecoModule};
use middle::dce;
use middle::ir;
use middle::regfile::{RegisterId, RegisterUsage, SubRegisterFile};
use middle::ssa::cfg_traits::*;
use middle::ssa::ssa_traits::*;
use middle::ssa::ssastorage::SSAStorage;

use petgraph::visit::{DfsPostOrder, Walker};

use std::collections::{BTreeMap, HashSet};

/// For every function, patch all of its call sites to ignore registers that the
/// callee doesn't read and to preserve register values that the callee
/// preserves. Then, record which registers it reads and which registers it
/// preserves.
///
/// After this, all functions should have mutable [`regusage`][RadecoFunction::regusage]s.
///
/// This analysis is super conservative; for example, if a function preserves a
/// register by pushing it onto the stack and popping it back right before
/// returning, it is considered to be read and not preserved because we can't
/// guarantee that that stack location is never subsequently read or modified.
pub fn run(rmod: &mut RadecoModule, reginfo: &SubRegisterFile) -> () {
    Inferer::new().run(rmod, reginfo);
}

struct Inferer {
    /// Addresses of the functions we've already analyzed
    analyzed: HashSet<u64>,
}

impl Inferer {
    fn new() -> Inferer {
        Inferer {
            analyzed: HashSet::new(),
        }
    }

    /// Calls `patch_fn`, `dce::collect`, and `analyze_fn` on every function,
    /// callees first
    fn run(&mut self, rmod: &mut RadecoModule, reginfo: &SubRegisterFile) -> () {
        // for imports, *ASSUME* that the callconv that r2 says is correct
        {
            let imp_ru_iter = rmod.imports.iter().filter_map(|(&imp_addr, imp_info)| {
                let imp_rfn = imp_info.rfn.borrow();
                let regusage = reginfo.r2callconv_to_register_usage(
                    imp_rfn.callconv.as_ref()?, // ignore imports without callconvs
                    &*imp_rfn.callconv_name,
                )?;
                Some((imp_addr, regusage))
            });
            for (imp_addr, imp_ru) in imp_ru_iter {
                rmod.functions.get_mut(&imp_addr).unwrap().regusage = imp_ru;
                self.analyzed.insert(imp_addr);
            }
        }

        let mut dfs_wi = DfsPostOrder::empty(&rmod.callgraph).iter(&rmod.callgraph);
        // pick a function ...
        for fn_ni in rmod.callgraph.node_indices() {
            // ... and start a dfs on it
            dfs_wi.inner_mut().move_to(fn_ni);
            while let Some(fn_to_anal) = dfs_wi.next() {
                let fn_addr = rmod.callgraph[fn_to_anal];

                // ignore functions already in `call_convs` (probably because its an import)
                if !self.analyzed.contains(&fn_addr) {
                    self.patch_fn(fn_addr, &mut rmod.functions);

                    let rfn = &mut rmod.functions.get_mut(&fn_addr).unwrap();
                    dce::collect(rfn.ssa_mut());
                    inst_combine::run(rfn.ssa_mut());

                    let ru = self.analyze_fn(rfn, reginfo).unwrap_or_else(|| {
                        radeco_err!("Failed to analyze fn: {:?} (@ {:#X})", rfn.name, fn_addr);
                        // if analysis failed, default to "reads and clobbers everything"
                        reginfo.new_register_usage()
                    });

                    rfn.regusage = ru;
                    self.analyzed.insert(fn_addr);
                }
            }
        }
    }

    /// Using the callconv info we've gathered so far, patch-up call sites to
    /// to remove arguments that the callee doesn't read and make values in
    /// callee-saved registers be preserved across the call.
    fn patch_fn(&self, fn_addr: u64, fn_map: &mut BTreeMap<u64, RadecoFunction>) -> () {
        radeco_trace!("patching calls in fn: {}", fn_map[&fn_addr].name);
        for node in fn_map[&fn_addr].ssa().inorder_walk() {
            if let Ok(NodeType::Op(ir::MOpcode::OpCall)) =
                fn_map[&fn_addr].ssa().node_data(node).map(|nd| nd.nt)
            {
                self.patch_call_node(node, fn_addr, fn_map)
                    .unwrap_or_else(|| {
                        radeco_warn!(
                            "failed to remove unused args for call at {:#X}",
                            fn_map[&fn_addr].ssa().address(node).unwrap()
                        );
                    });
            }
        }
    }

    fn patch_call_node(
        &self,
        call_node: <SSAStorage as SSA>::ValueRef,
        fn_addr: u64,
        fn_map: &mut BTreeMap<u64, RadecoFunction>,
    ) -> Option<()> {
        // bail on indirect or weird call
        let (call_tgt_addr, call_reg_map) = direct_call_info(fn_map[&fn_addr].ssa(), call_node)?;

        // remove unread args
        for (reg_idx, &op_node) in (0..).zip(&call_reg_map) {
            if fn_map[&call_tgt_addr]
                .regusage
                .is_ignored(RegisterId::from_usize(reg_idx))
            {
                fn_map
                    .get_mut(&fn_addr)
                    .unwrap()
                    .ssa_mut()
                    .op_unuse(call_node, op_node);
            }
        }

        // bridge preserved registers
        for use_node in fn_map[&fn_addr].ssa().uses_of(call_node) {
            let reg_idx = match fn_map[&fn_addr]
                .ssa()
                .sparse_operands_of(use_node)
                .as_slice()
            {
                &[(reg_idx, _)] => reg_idx,
                _ => panic!("invalid use of call as operand"),
            };
            if fn_map[&call_tgt_addr]
                .regusage
                .is_preserved(RegisterId::from_u8(reg_idx))
            {
                fn_map
                    .get_mut(&fn_addr)
                    .unwrap()
                    .ssa_mut()
                    .replace_value(use_node, call_reg_map[reg_idx as usize]);
            }
        }

        Some(())
    }

    fn analyze_fn(&self, rfn: &RadecoFunction, reginfo: &SubRegisterFile) -> Option<RegisterUsage> {
        radeco_trace!("analyzing fn: {}", rfn.name);
        let ssa = rfn.ssa();
        let entry_regstate_node = ssa.registers_in(ssa.entry_node()?)?;
        let exit_regstate_node = ssa.registers_in(ssa.exit_node()?)?;
        // some registers may not be present in the entry node;
        // this means that the function neither reads nor preserves that register
        let entry_regstate: Vec<Option<_>> = register_state_info(ssa, entry_regstate_node);
        // every register must be present in the exit node though
        let exit_regstate: Vec<_> = register_state_info(ssa, exit_regstate_node)
            .into_iter()
            .collect::<Option<_>>()?;

        let mut ret = reginfo.new_register_usage();
        ret.set_all_ignored();

        // ignore registers not in entry regstate
        let regstate_iter = (0..)
            .zip(exit_regstate)
            .zip(entry_regstate)
            .filter_map(|((i, x), on)| on.map(|n| (i, n, x)));

        for (i, reg_val_entry, reg_val_exit) in regstate_iter {
            if reg_val_exit == reg_val_entry {
                ret.set_preserved(RegisterId::from_u8(i));
            }

            // find all uses, ignoring entry/exit register state
            let mut uses_iter = ssa.uses_of(reg_val_entry)
                .into_iter()
                .filter(|&n| n != entry_regstate_node && n != exit_regstate_node);
            if uses_iter.next().is_some() {
                ret.set_read(RegisterId::from_u8(i));
            }
        }

        Some(ret)
    }
}

/// Extracts the value of all registers at a `RegisterState` SSA node.
/// The length of the returned `Vec` is exactly `ssa.regnames.len() + 1`.
fn register_state_info(
    ssa: &SSAStorage,
    regstate_node: <SSAStorage as SSA>::ValueRef,
) -> Vec<Option<<SSAStorage as SSA>::ValueRef>> {
    let mut reg_opt_map: Vec<Option<_>> = vec![None; ssa.regnames.len() + 1];
    for (op_idx, op_node) in ssa.sparse_operands_of(regstate_node) {
        if let Some(p) = reg_opt_map.get_mut(op_idx as usize) {
            *p = Some(op_node);
        }
    }
    reg_opt_map
}

/// Extracts the call target address and the value of all registers
/// The length of the returned `Vec` is exactly `ssa.regnames.len() + 1`.
/// Returns `None` if the call is indirect or if not all registers have a value.
fn direct_call_info(
    ssa: &SSAStorage,
    call_node: <SSAStorage as SSA>::ValueRef,
) -> Option<(u64, Vec<<SSAStorage as SSA>::ValueRef>)> {
    let mut tgt_opt: Option<u64> = None;
    let mut reg_opt_map: Vec<Option<_>> = vec![None; ssa.regnames.len() + 1];

    for (op_idx, op_node) in ssa.sparse_operands_of(call_node) {
        if op_idx == 0 {
            tgt_opt = tgt_opt.or_else(|| ssa.constant(op_node));
        } else {
            if let Some(p) = reg_opt_map.get_mut((op_idx - 1) as usize) {
                *p = Some(op_node);
            }
        }
    }

    let reg_map_opt: Option<Vec<_>> = reg_opt_map.into_iter().collect();
    Some((tgt_opt?, reg_map_opt?))
}
<commit_msg>Add issue reference to `infer_regusage` (#150)<commit_after>//! Infers how each function uses every register

use analysis::inst_combine;
use frontend::radeco_containers::{RadecoFunction, RadecoModule};
use middle::dce;
use middle::ir;
use middle::regfile::{RegisterId, RegisterUsage, SubRegisterFile};
use middle::ssa::cfg_traits::*;
use middle::ssa::ssa_traits::*;
use middle::ssa::ssastorage::SSAStorage;

use petgraph::visit::{DfsPostOrder, Walker};

use std::collections::{BTreeMap, HashSet};

/// For every function, patch all of its call sites to ignore registers that the
/// callee doesn't read and to preserve register values that the callee
/// preserves. Then, record which registers it reads and which registers it
/// preserves.
///
/// After this, all functions should have mutable [`regusage`][RadecoFunction::regusage]s.
///
/// This analysis is super conservative; for example, if a function preserves a
/// register by pushing it onto the stack and popping it back right before
/// returning, it is considered to be read and not preserved because we can't
/// guarantee that that stack location is never subsequently read or modified.
/// See #147 for further discussion
pub fn run(rmod: &mut RadecoModule, reginfo: &SubRegisterFile) -> () {
    Inferer::new().run(rmod, reginfo);
}

struct Inferer {
    /// Addresses of the functions we've already analyzed
    analyzed: HashSet<u64>,
}

impl Inferer {
    fn new() -> Inferer {
        Inferer {
            analyzed: HashSet::new(),
        }
    }

    /// Calls `patch_fn`, `dce::collect`, and `analyze_fn` on every function,
    /// callees first
    fn run(&mut self, rmod: &mut RadecoModule, reginfo: &SubRegisterFile) -> () {
        // for imports, *ASSUME* that the callconv that r2 says is correct
        {
            let imp_ru_iter = rmod.imports.iter().filter_map(|(&imp_addr, imp_info)| {
                let imp_rfn = imp_info.rfn.borrow();
                let regusage = reginfo.r2callconv_to_register_usage(
                    imp_rfn.callconv.as_ref()?, // ignore imports without callconvs
                    &*imp_rfn.callconv_name,
                )?;
                Some((imp_addr, regusage))
            });
            for (imp_addr, imp_ru) in imp_ru_iter {
                rmod.functions.get_mut(&imp_addr).unwrap().regusage = imp_ru;
                self.analyzed.insert(imp_addr);
            }
        }

        let mut dfs_wi = DfsPostOrder::empty(&rmod.callgraph).iter(&rmod.callgraph);
        // pick a function ...
        for fn_ni in rmod.callgraph.node_indices() {
            // ... and start a dfs on it
            dfs_wi.inner_mut().move_to(fn_ni);
            while let Some(fn_to_anal) = dfs_wi.next() {
                let fn_addr = rmod.callgraph[fn_to_anal];

                // ignore functions already in `call_convs` (probably because its an import)
                if !self.analyzed.contains(&fn_addr) {
                    self.patch_fn(fn_addr, &mut rmod.functions);

                    let rfn = &mut rmod.functions.get_mut(&fn_addr).unwrap();
                    dce::collect(rfn.ssa_mut());
                    inst_combine::run(rfn.ssa_mut());

                    let ru = self.analyze_fn(rfn, reginfo).unwrap_or_else(|| {
                        radeco_err!("Failed to analyze fn: {:?} (@ {:#X})", rfn.name, fn_addr);
                        // if analysis failed, default to "reads and clobbers everything"
                        reginfo.new_register_usage()
                    });

                    rfn.regusage = ru;
                    self.analyzed.insert(fn_addr);
                }
            }
        }
    }

    /// Using the callconv info we've gathered so far, patch-up call sites to
    /// to remove arguments that the callee doesn't read and make values in
    /// callee-saved registers be preserved across the call.
    fn patch_fn(&self, fn_addr: u64, fn_map: &mut BTreeMap<u64, RadecoFunction>) -> () {
        radeco_trace!("patching calls in fn: {}", fn_map[&fn_addr].name);
        for node in fn_map[&fn_addr].ssa().inorder_walk() {
            if let Ok(NodeType::Op(ir::MOpcode::OpCall)) =
                fn_map[&fn_addr].ssa().node_data(node).map(|nd| nd.nt)
            {
                self.patch_call_node(node, fn_addr, fn_map)
                    .unwrap_or_else(|| {
                        radeco_warn!(
                            "failed to remove unused args for call at {:#X}",
                            fn_map[&fn_addr].ssa().address(node).unwrap()
                        );
                    });
            }
        }
    }

    fn patch_call_node(
        &self,
        call_node: <SSAStorage as SSA>::ValueRef,
        fn_addr: u64,
        fn_map: &mut BTreeMap<u64, RadecoFunction>,
    ) -> Option<()> {
        // bail on indirect or weird call
        let (call_tgt_addr, call_reg_map) = direct_call_info(fn_map[&fn_addr].ssa(), call_node)?;

        // remove unread args
        for (reg_idx, &op_node) in (0..).zip(&call_reg_map) {
            if fn_map[&call_tgt_addr]
                .regusage
                .is_ignored(RegisterId::from_usize(reg_idx))
            {
                fn_map
                    .get_mut(&fn_addr)
                    .unwrap()
                    .ssa_mut()
                    .op_unuse(call_node, op_node);
            }
        }

        // bridge preserved registers
        for use_node in fn_map[&fn_addr].ssa().uses_of(call_node) {
            let reg_idx = match fn_map[&fn_addr]
                .ssa()
                .sparse_operands_of(use_node)
                .as_slice()
            {
                &[(reg_idx, _)] => reg_idx,
                _ => panic!("invalid use of call as operand"),
            };
            if fn_map[&call_tgt_addr]
                .regusage
                .is_preserved(RegisterId::from_u8(reg_idx))
            {
                fn_map
                    .get_mut(&fn_addr)
                    .unwrap()
                    .ssa_mut()
                    .replace_value(use_node, call_reg_map[reg_idx as usize]);
            }
        }

        Some(())
    }

    fn analyze_fn(&self, rfn: &RadecoFunction, reginfo: &SubRegisterFile) -> Option<RegisterUsage> {
        radeco_trace!("analyzing fn: {}", rfn.name);
        let ssa = rfn.ssa();
        let entry_regstate_node = ssa.registers_in(ssa.entry_node()?)?;
        let exit_regstate_node = ssa.registers_in(ssa.exit_node()?)?;
        // some registers may not be present in the entry node;
        // this means that the function neither reads nor preserves that register
        let entry_regstate: Vec<Option<_>> = register_state_info(ssa, entry_regstate_node);
        // every register must be present in the exit node though
        let exit_regstate: Vec<_> = register_state_info(ssa, exit_regstate_node)
            .into_iter()
            .collect::<Option<_>>()?;

        let mut ret = reginfo.new_register_usage();
        ret.set_all_ignored();

        // ignore registers not in entry regstate
        let regstate_iter = (0..)
            .zip(exit_regstate)
            .zip(entry_regstate)
            .filter_map(|((i, x), on)| on.map(|n| (i, n, x)));

        for (i, reg_val_entry, reg_val_exit) in regstate_iter {
            if reg_val_exit == reg_val_entry {
                ret.set_preserved(RegisterId::from_u8(i));
            }

            // find all uses, ignoring entry/exit register state
            let mut uses_iter = ssa.uses_of(reg_val_entry)
                .into_iter()
                .filter(|&n| n != entry_regstate_node && n != exit_regstate_node);
            if uses_iter.next().is_some() {
                ret.set_read(RegisterId::from_u8(i));
            }
        }

        Some(ret)
    }
}

/// Extracts the value of all registers at a `RegisterState` SSA node.
/// The length of the returned `Vec` is exactly `ssa.regnames.len() + 1`.
fn register_state_info(
    ssa: &SSAStorage,
    regstate_node: <SSAStorage as SSA>::ValueRef,
) -> Vec<Option<<SSAStorage as SSA>::ValueRef>> {
    let mut reg_opt_map: Vec<Option<_>> = vec![None; ssa.regnames.len() + 1];
    for (op_idx, op_node) in ssa.sparse_operands_of(regstate_node) {
        if let Some(p) = reg_opt_map.get_mut(op_idx as usize) {
            *p = Some(op_node);
        }
    }
    reg_opt_map
}

/// Extracts the call target address and the value of all registers
/// The length of the returned `Vec` is exactly `ssa.regnames.len() + 1`.
/// Returns `None` if the call is indirect or if not all registers have a value.
fn direct_call_info(
    ssa: &SSAStorage,
    call_node: <SSAStorage as SSA>::ValueRef,
) -> Option<(u64, Vec<<SSAStorage as SSA>::ValueRef>)> {
    let mut tgt_opt: Option<u64> = None;
    let mut reg_opt_map: Vec<Option<_>> = vec![None; ssa.regnames.len() + 1];

    for (op_idx, op_node) in ssa.sparse_operands_of(call_node) {
        if op_idx == 0 {
            tgt_opt = tgt_opt.or_else(|| ssa.constant(op_node));
        } else {
            if let Some(p) = reg_opt_map.get_mut((op_idx - 1) as usize) {
                *p = Some(op_node);
            }
        }
    }

    let reg_map_opt: Option<Vec<_>> = reg_opt_map.into_iter().collect();
    Some((tgt_opt?, reg_map_opt?))
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::io::Read;
use std::mem::swap;

type Translation = [bool; 512];
type Point = (i32, i32);
type Field = HashSet<Point>;

fn read_input(input: &mut dyn Read) -> (Translation, Field) {
    let mut buffer = Vec::new();
    input.read_to_end(&mut buffer).unwrap();

    let mut translation = [false; 512];

    let mut it = buffer.split(|&b| b == b'\n');

    translation
        .iter_mut()
        .zip(it.next().unwrap())
        .for_each(|(t, &c)| *t = c == b'#');

    let mut field = Field::default();

    for (y, line) in it.skip(1).enumerate() {
        for (x, _) in line.iter().enumerate().filter(|(_, &c)| c == b'#') {
            field.insert((x as i32, y as i32));
        }
    }

    (translation, field)
}

fn find_dimensions(field: &Field) -> ((i32, i32), (i32, i32)) {
    field
        .iter()
        .fold(((0, 0), (0, 0)), |((xmin, xmax), (ymin, ymax)), &(x, y)| {
            ((xmin.min(x), xmax.max(x)), (ymin.min(y), ymax.max(y)))
        })
}

fn advance(translation: &Translation, field: &Field, new_field: &mut Field, infinity: &mut bool) {
    new_field.clear();

    let ((xmin, xmax), (ymin, ymax)) = find_dimensions(field);

    for x in (xmin - 1)..=(xmax + 1) {
        for y in (ymin - 1)..=(ymax + 1) {
            let mut index = 0;
            for dy in -1..=1 {
                for dx in -1..=1 {
                    index <<= 1;

                    let nx = x + dx;
                    let ny = y + dy;

                    if nx < xmin || nx > xmax || ny < ymin || ny > ymax {
                        index |= *infinity as usize;
                    } else if field.contains(&(x + dx, y + dy)) {
                        index |= 1;
                    }
                }
            }

            if translation[index] {
                new_field.insert((x, y));
            }
        }
    }

    *infinity = translation[if *infinity { 511 } else { 0 }]
}

fn parts_common(input: &mut dyn Read, count: usize) -> String {
    let (translation, mut field) = read_input(input);
    let mut new_field = Field::new();
    let mut infinity = false;

    for _ in 0..count {
        advance(&translation, &field, &mut new_field, &mut infinity);
        swap(&mut field, &mut new_field);
    }

    field.len().to_string()
}

pub fn part1(input: &mut dyn Read) -> String {
    parts_common(input, 2)
}

pub fn part2(input: &mut dyn Read) -> String {
    parts_common(input, 50)
}

#[cfg(test)]
mod tests {
    use crate::test_implementation;

    use super::*;

    const SAMPLE: &[u8] = include_bytes!("samples/20.txt");

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 35);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 3351);
    }
}
<commit_msg>Avoid 2/3 hashmap lookups<commit_after>use std::collections::HashSet;
use std::io::Read;
use std::mem::swap;

type Translation = [bool; 512];
type Point = (i32, i32);
type Field = HashSet<Point>;

fn read_input(input: &mut dyn Read) -> (Translation, Field) {
    let mut buffer = Vec::new();
    input.read_to_end(&mut buffer).unwrap();

    let mut translation = [false; 512];

    let mut it = buffer.split(|&b| b == b'\n');

    translation
        .iter_mut()
        .zip(it.next().unwrap())
        .for_each(|(t, &c)| *t = c == b'#');

    let mut field = Field::default();

    for (y, line) in it.skip(1).enumerate() {
        for (x, _) in line.iter().enumerate().filter(|(_, &c)| c == b'#') {
            field.insert((x as i32, y as i32));
        }
    }

    (translation, field)
}

fn find_dimensions(field: &Field) -> ((i32, i32), (i32, i32)) {
    field
        .iter()
        .fold(((0, 0), (0, 0)), |((xmin, xmax), (ymin, ymax)), &(x, y)| {
            ((xmin.min(x), xmax.max(x)), (ymin.min(y), ymax.max(y)))
        })
}

fn advance(translation: &Translation, field: &Field, new_field: &mut Field, infinity: &mut bool) {
    const INDEX_MASK: usize = (1 << 9) - 1;
    new_field.clear();

    let ((xmin, xmax), (ymin, ymax)) = find_dimensions(field);

    for x in (xmin - 1)..=(xmax + 1) {
        let mut index = if *infinity { INDEX_MASK } else { 0 };

        for y in (ymin - 1)..=(ymax + 1) {
            for dx in -1..=1 {
                index <<= 1;

                let nx = x + dx;
                let ny = y + 1;

                if nx < xmin || nx > xmax || ny < ymin || ny > ymax {
                    index |= *infinity as usize;
                } else if field.contains(&(nx, ny)) {
                    index |= 1;
                }
            }

            index &= INDEX_MASK;

            if translation[index] {
                new_field.insert((x, y));
            }
        }
    }

    *infinity = translation[if *infinity { 511 } else { 0 }]
}

fn parts_common(input: &mut dyn Read, count: usize) -> String {
    let (translation, mut field) = read_input(input);
    let mut new_field = Field::new();
    let mut infinity = false;

    for _ in 0..count {
        advance(&translation, &field, &mut new_field, &mut infinity);
        swap(&mut field, &mut new_field);
    }

    field.len().to_string()
}

pub fn part1(input: &mut dyn Read) -> String {
    parts_common(input, 2)
}

pub fn part2(input: &mut dyn Read) -> String {
    parts_common(input, 50)
}

#[cfg(test)]
mod tests {
    use crate::test_implementation;

    use super::*;

    const SAMPLE: &[u8] = include_bytes!("samples/20.txt");

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 35);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 3351);
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! `x86` and `x86_64` control registers
use core::fmt;

/// `%cr0` contains flags that modify basic processor operation.
pub mod cr0;

/// `%cr4` contains flags that control protected mode execution.
pub mod cr4;

/// A struct bundling together a snapshot of the control registers state.
#[derive(Copy,Clone,Debug)]
pub struct CrState { /// `$cr0` contains flags that control the CPU's operations
                     pub cr0: cr0::Flags
                   , /// `$cr2` contains the page fault linear address
                     pub cr2: usize
                   , /// `$cr3` contains the page table root pointer
                     pub cr3: usize
                   , /// `$cr4` contains flags that control operations in
                     ///  protected mode
                     pub cr4: cr4::Flags
                   }

impl fmt::Display for CrState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!( f, "CR0: {:#08x} CR2: {:#08x} CR3: {:#08x} CR4: {:#08x}"
                , self.cr0, self.cr2, self.cr3, self.cr4)
    }
}

/// Dump the current contents of the control registers to a `CrState`.
pub fn dump() -> CrState {
    let cr0_: usize; let cr2_: usize;
    let cr3_: usize; let cr4_: usize;
    unsafe {
        asm!(  "mov $0, cr0
                mov $1, cr2
                mov $2, cr3
                mov $3, cr4"
            :   "=r"(cr0_)
              , "=r"(cr2_)
              , "=r"(cr3_)
              , "=r"(cr4_)
            ::: "intel"
              , "volatile");
    }
    CrState { cr0: cr0::Flags::from_bits_truncate(cr0_)
            , cr2: cr2_, cr3: cr3_
            , cr4: cr4::Flags::from_bits_truncate(cr4_)
            }

}

pub mod cr2 {

    /// Read the current value from `$cr2`.
    ///
    /// # Unsafe Because:
    /// + Reading from control registers while not in kernel mode will cause
    ///   a general protection fault.
    pub unsafe fn read() -> usize {
        let result: usize;
        asm!(   "mov $0, cr2"
            :   "=r"(result)
            ::: "intel" );
        result
    }

    /// Write a value to `$cr2`.
    ///
    /// # Unsafe Because:
    /// + Control registers should generally not be modified during normal
    ///   operation.
    pub unsafe fn write(value: usize) {
        asm!(  "mov cr2, $0"
            :: "r"(value)
            :: "intel");
    }
}

pub mod cr3 {
    use memory::paging::PhysicalPage;

    /// Read the current value from `$cr3`.
    ///
    /// # Unsafe Because:
    /// + Reading from control registers while not in kernel mode will cause
    ///   a general protection fault.
    pub unsafe fn read() -> usize {
        let result: usize;
        asm!(   "mov $0, cr3"
            :   "=r"(result)
            ::: "intel" );
        result
    }

    /// Write a value to `$cr3`.
    ///
    /// # Unsafe Because:
    /// + Control registers should generally not be modified during normal
    ///   operation.
    pub unsafe fn write(value: usize) {
        asm!(  "mov cr3, $0"
            :: "r"(value)
            :: "intel");
    }

    /// Returns the current Page Directory base frame.
    #[cfg(target_arch = "x86_64")]
    pub unsafe fn pd_base_frame() -> PhysicalPage {
        use memory::PAddr;
        PhysicalPage::containing_addr(PAddr::from(read() as u64))
    }

    /// Returns the current Page Directory base frame.
    #[cfg(target_arch = "x86")]
    pub unsafe fn pd_base_frame() -> PhysicalPage {
        use memory::PAddr;
        PhysicalPage::containing_addr(PAddr::from(read() as u32))
    }
}
<commit_msg>[x86_all] made control register %cr3 API much more fluent<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! `x86` and `x86_64` control registers
use core::fmt;

/// `%cr0` contains flags that modify basic processor operation.
pub mod cr0;

/// `%cr4` contains flags that control protected mode execution.
pub mod cr4;

/// A struct bundling together a snapshot of the control registers state.
#[derive(Copy,Clone,Debug)]
pub struct CrState { /// `$cr0` contains flags that control the CPU's operations
                     pub cr0: cr0::Flags
                   , /// `$cr2` contains the page fault linear address
                     pub cr2: usize
                   , /// `$cr3` contains the page table root pointer
                     pub cr3: usize
                   , /// `$cr4` contains flags that control operations in
                     ///  protected mode
                     pub cr4: cr4::Flags
                   }

impl fmt::Display for CrState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!( f, "CR0: {:#08x} CR2: {:#08x} CR3: {:#08x} CR4: {:#08x}"
                , self.cr0, self.cr2, self.cr3, self.cr4)
    }
}

/// Dump the current contents of the control registers to a `CrState`.
pub fn dump() -> CrState {
    let cr0_: usize; let cr2_: usize;
    let cr3_: usize; let cr4_: usize;
    unsafe {
        asm!(  "mov $0, cr0
                mov $1, cr2
                mov $2, cr3
                mov $3, cr4"
            :   "=r"(cr0_)
              , "=r"(cr2_)
              , "=r"(cr3_)
              , "=r"(cr4_)
            ::: "intel"
              , "volatile");
    }
    CrState { cr0: cr0::Flags::from_bits_truncate(cr0_)
            , cr2: cr2_, cr3: cr3_
            , cr4: cr4::Flags::from_bits_truncate(cr4_)
            }

}

pub mod cr2 {

    /// Read the current value from `$cr2`.
    ///
    /// # Unsafe Because:
    /// + Reading from control registers while not in kernel mode will cause
    ///   a general protection fault.
    pub unsafe fn read() -> usize {
        let result: usize;
        asm!(   "mov $0, cr2"
            :   "=r"(result)
            ::: "intel" );
        result
    }

    /// Write a value to `$cr2`.
    ///
    /// # Unsafe Because:
    /// + Control registers should generally not be modified during normal
    ///   operation.
    pub unsafe fn write(value: usize) {
        asm!(  "mov cr2, $0"
            :: "r"(value)
            :: "intel");
    }
}

pub mod cr3 {
    use memory::paging::{Page, PhysicalPage};
    use memory::PAddr;

    #[cfg(target_arch = "x86_64")]
    use arch::memory::paging::table::{Table, PML4Level};

    /// Read the current value from `$cr3`.
    ///
    /// # Unsafe Because:
    /// + Reading from control registers while not in kernel mode will cause
    ///   a general protection fault.
    pub unsafe fn read() -> PAddr {
        let result: PAddr;
        asm!(   "mov $0, cr3"
            :   "=r"(result)
            ::: "intel" );
        result
    }

    /// Write a value to `$cr3`.
    ///
    /// # Unsafe Because:
    /// + Control registers should generally not be modified during normal
    ///   operation.
    pub unsafe fn write(value: PAddr) {
        asm!(  "mov cr3, $0"
            :: "r"(value)
            :: "intel");
    }

    /// Returns the current Page Meta-Level 4 table
    ///
    /// # Unsafe Because:
    /// + Reading from control registers while not in kernel mode will cause
    ///   a general protection fault.
    #[cfg(target_arch = "x86_64")]
    #[inline]
    pub unsafe fn current_pml4() -> Table<PML4Level> {
        use core::mem::transmute;
        transmute(current_pagetable_frame())
    }

    /// Sets the current Page Meta-Level 4 Table
    ///
    /// # Unsafe Because:
    /// + Control registers should generally not be modified during normal
    ///   operation.
    #[cfg(target_arch = "x86_64")]
    pub unsafe fn set_pml4(pml4: Table<PML4Level>) {
        write(pml4.frame().base_addr())
    }

    /// Returns the current Page Directory base frame.
    ///
    /// # Unsafe Because:
    /// + Reading from control registers while not in kernel mode will cause
    ///   a general protection fault.
    pub unsafe fn current_pagetable_frame() -> PhysicalPage {
        PhysicalPage::containing_addr(read())
    }
}
<|endoftext|>


<commit_before>use super::{ActionEdge, ActionNode, CCFGEdge, CCFGNode, CCFG};
use backend::ctrl_flow_struct as flstr;
use backend::ctrl_flow_struct::ast_context::{AstContext, AstContextMut};
use backend::lang_c::c_ast;

use petgraph::prelude::*;
use petgraph::visit::{Dfs, EdgeFiltered, IntoNodeReferences, VisitMap};

pub fn structure_and_convert(ccfg: CCFG) -> Option<c_ast::CAST> {
    let cstore = flstr::condition::Storage::new();
    let flstr_cfg = import(cstore.cctx(), ccfg)?;
    unimplemented!()
}

fn import<'cd>(
    cctx: flstr::condition::Context<'cd, NodeIndex>,
    ccfg: CCFG,
) -> Option<flstr::ControlFlowGraph<'cd, CCFG>> {
    let mut new_graph = {
        let ef = EdgeFiltered::from_fn(&ccfg.g, |e| {
            // ignore `Normal` edges from `Goto` nodes
            match (&ccfg.g[e.source()], e.weight()) {
                (CCFGNode::Action(ActionNode::Goto), CCFGEdge::Action(ActionEdge::Normal)) => false,
                (_, CCFGEdge::Action(_)) => true,
                _ => false,
            }
        });
        let reachable_actions = {
            let mut dfs = Dfs::new(&ef, ccfg.entry);
            while let Some(_) = dfs.next(&ef) {}
            dfs.discovered
        };

        if ccfg
            .g
            .node_references()
            .filter(|(n, _)| reachable_actions.is_visited(n))
            .any(|(_, nw)| !is_action_node(nw))
        {
            return None;
        }

        try_filter_map_to_stable(
            &ccfg.g,
            |n, nw| {
                Some(if reachable_actions.is_visited(&n) {
                    if let CCFGNode::Action(ActionNode::If) = nw {
                        Some(flstr::mk_cond_node(cctx, ccfg.branch_condition(n)?))
                    } else {
                        Some(flstr::mk_code_node(n))
                    }
                } else {
                    None
                })
            },
            |e, ew| {
                // `Some(_)`     <=> keep edge
                // `None`        <=> ignore edge
                // `return None` <=> error
                Some(match (&ccfg.g[ccfg.g.edge_endpoints(e).unwrap().0], ew) {
                    (CCFGNode::Action(ActionNode::If), CCFGEdge::Action(ActionEdge::IfThen)) => {
                        Some(flstr::CfgEdge::True)
                    }
                    (CCFGNode::Action(ActionNode::If), CCFGEdge::Action(ActionEdge::IfElse)) => {
                        return None
                    }
                    (CCFGNode::Action(ActionNode::If), CCFGEdge::Action(ActionEdge::Normal)) => {
                        Some(flstr::CfgEdge::False)
                    }
                    (CCFGNode::Action(ActionNode::Goto), CCFGEdge::Action(ActionEdge::GotoDst)) => {
                        Some(flstr::CfgEdge::True)
                    }
                    (CCFGNode::Action(ActionNode::Goto), CCFGEdge::Action(ActionEdge::Normal)) => {
                        None
                    }
                    (_, CCFGEdge::Action(ActionEdge::Normal)) => Some(flstr::CfgEdge::True),
                    (_, CCFGEdge::Value(_)) => None,
                    (_, CCFGEdge::Action(_)) => return None,
                })
            },
        )?
    };

    let exit = new_graph.add_node(flstr::empty_node());
    Some(flstr::ControlFlowGraph {
        graph: new_graph,
        entry: ccfg.entry,
        exit,
        cctx,
        actx: ccfg,
    })
}

impl AstContext for CCFG {
    type Block = NodeIndex;
    type Variable = String;
    type BoolVariable = String;
    type Condition = NodeIndex;
}

/// based on https://docs.rs/petgraph/0.4.12/src/petgraph/graph_impl/mod.rs.html#1293-1317
fn try_filter_map_to_stable<'a, N, E, F, G, N2, E2>(
    graph: &'a Graph<N, E>,
    mut node_map: F,
    mut edge_map: G,
) -> Option<StableGraph<N2, E2>>
where
    F: FnMut(NodeIndex, &'a N) -> Option<Option<N2>>,
    G: FnMut(EdgeIndex, &'a E) -> Option<Option<E2>>,
{
    let mut g = StableGraph::new();
    // mapping from old node index to new node index, end represents removed.
    let mut node_index_map = vec![NodeIndex::end(); graph.node_count()];
    for (i, node) in graph.node_references() {
        if let Some(nw) = node_map(i, node)? {
            node_index_map[i.index()] = g.add_node(nw);
        }
    }
    for edge in graph.edge_references() {
        // skip edge if any endpoint was removed
        let source = node_index_map[edge.source().index()];
        let target = node_index_map[edge.target().index()];
        if source != NodeIndex::end() && target != NodeIndex::end() {
            if let Some(ew) = edge_map(edge.id(), edge.weight())? {
                g.add_edge(source, target, ew);
            }
        }
    }
    Some(g)
}

fn is_action_node(e: &CCFGNode) -> bool {
    if let CCFGNode::Action(_) = e {
        true
    } else {
        false
    }
}
<commit_msg>[c_cfg] Fix not converting entry node index<commit_after>use super::{ActionEdge, ActionNode, CCFGEdge, CCFGNode, CCFG};
use backend::ctrl_flow_struct as flstr;
use backend::ctrl_flow_struct::ast_context::{AstContext, AstContextMut};
use backend::lang_c::c_ast;

use petgraph::prelude::*;
use petgraph::visit::{Dfs, EdgeFiltered, IntoNodeReferences, VisitMap};

pub fn structure_and_convert(ccfg: CCFG) -> Option<c_ast::CAST> {
    let cstore = flstr::condition::Storage::new();
    let flstr_cfg = import(cstore.cctx(), ccfg)?;
    unimplemented!()
}

fn import<'cd>(
    cctx: flstr::condition::Context<'cd, NodeIndex>,
    ccfg: CCFG,
) -> Option<flstr::ControlFlowGraph<'cd, CCFG>> {
    let (mut new_graph, entry) = {
        let ef = EdgeFiltered::from_fn(&ccfg.g, |e| {
            // ignore `Normal` edges from `Goto` nodes
            match (&ccfg.g[e.source()], e.weight()) {
                (CCFGNode::Action(ActionNode::Goto), CCFGEdge::Action(ActionEdge::Normal)) => false,
                (_, CCFGEdge::Action(_)) => true,
                _ => false,
            }
        });
        let reachable_actions = {
            let mut dfs = Dfs::new(&ef, ccfg.entry);
            while let Some(_) = dfs.next(&ef) {}
            dfs.discovered
        };

        if ccfg
            .g
            .node_references()
            .filter(|(n, _)| reachable_actions.is_visited(n))
            .any(|(_, nw)| !is_action_node(nw))
        {
            return None;
        }

        let (new_graph, node_index_map) = try_filter_map_to_stable(
            &ccfg.g,
            |n, nw| {
                Some(if reachable_actions.is_visited(&n) {
                    if let CCFGNode::Action(ActionNode::If) = nw {
                        Some(flstr::mk_cond_node(cctx, ccfg.branch_condition(n)?))
                    } else {
                        Some(flstr::mk_code_node(n))
                    }
                } else {
                    None
                })
            },
            |e, ew| {
                // `Some(_)`     <=> keep edge
                // `None`        <=> ignore edge
                // `return None` <=> error
                Some(match (&ccfg.g[ccfg.g.edge_endpoints(e).unwrap().0], ew) {
                    (CCFGNode::Action(ActionNode::If), CCFGEdge::Action(ActionEdge::IfThen)) => {
                        Some(flstr::CfgEdge::True)
                    }
                    (CCFGNode::Action(ActionNode::If), CCFGEdge::Action(ActionEdge::IfElse)) => {
                        return None
                    }
                    (CCFGNode::Action(ActionNode::If), CCFGEdge::Action(ActionEdge::Normal)) => {
                        Some(flstr::CfgEdge::False)
                    }
                    (CCFGNode::Action(ActionNode::Goto), CCFGEdge::Action(ActionEdge::GotoDst)) => {
                        Some(flstr::CfgEdge::True)
                    }
                    (CCFGNode::Action(ActionNode::Goto), CCFGEdge::Action(ActionEdge::Normal)) => {
                        None
                    }
                    (_, CCFGEdge::Action(ActionEdge::Normal)) => Some(flstr::CfgEdge::True),
                    (_, CCFGEdge::Value(_)) => None,
                    (_, CCFGEdge::Action(_)) => return None,
                })
            },
        )?;

        (new_graph, node_index_map[ccfg.entry.index()])
    };

    let exit = new_graph.add_node(flstr::empty_node());
    Some(flstr::ControlFlowGraph {
        graph: new_graph,
        entry,
        exit,
        cctx,
        actx: ccfg,
    })
}

impl AstContext for CCFG {
    type Block = NodeIndex;
    type Variable = String;
    type BoolVariable = String;
    type Condition = NodeIndex;
}

/// based on https://docs.rs/petgraph/0.4.12/src/petgraph/graph_impl/mod.rs.html#1293-1317
fn try_filter_map_to_stable<'a, N, E, F, G, N2, E2>(
    graph: &'a Graph<N, E>,
    mut node_map: F,
    mut edge_map: G,
) -> Option<(StableGraph<N2, E2>, Vec<NodeIndex>)>
where
    F: FnMut(NodeIndex, &'a N) -> Option<Option<N2>>,
    G: FnMut(EdgeIndex, &'a E) -> Option<Option<E2>>,
{
    let mut g = StableGraph::new();
    // mapping from old node index to new node index, end represents removed.
    let mut node_index_map = vec![NodeIndex::end(); graph.node_count()];
    for (i, node) in graph.node_references() {
        if let Some(nw) = node_map(i, node)? {
            node_index_map[i.index()] = g.add_node(nw);
        }
    }
    assert!(node_index_map.iter().enumerate().all(|(i, ni)| i == ni.index()));
    for edge in graph.edge_references() {
        // skip edge if any endpoint was removed
        let source = node_index_map[edge.source().index()];
        let target = node_index_map[edge.target().index()];
        if source != NodeIndex::end() && target != NodeIndex::end() {
            if let Some(ew) = edge_map(edge.id(), edge.weight())? {
                g.add_edge(source, target, ew);
            }
        }
    }
    Some((g, node_index_map))
}

fn is_action_node(e: &CCFGNode) -> bool {
    if let CCFGNode::Action(_) = e {
        true
    } else {
        false
    }
}
<|endoftext|>


<commit_before>extern crate onmf;
use onmf::helpers::{ToImage, Normalize, magnify};
use onmf::testimage_generator;

extern crate nalgebra;
use self::nalgebra::{DMat};

extern crate rand;
use rand::{StdRng, SeedableRng};

extern crate num;
use num::{Float};

fn main() {
    let mag_factor = 10;

    let seed: &[_] = &[1, 2, 3, 4];
    let mut rng: StdRng = SeedableRng::from_seed(seed);

    let steps = 12;
    let per_step = 1000;
    let nsamples = steps * per_step;
    let nobserved = 10 * 10;
    let nhidden = 20 + steps;
    // let nhidden = 40;

    let mut data = DMat::<f64>::new_zeros(nsamples, nobserved);

    // write one testimage into each row of data
    for (horizontal, vertical, i, factor) in testimage_generator::testimages::<f64, _>(per_step, &mut rng) {
        let row = (horizontal + vertical) * i;
        for (col, val) in factor.as_vec().iter().enumerate() {
            data[(row, col)] = val.clone();
        }
    }

    let mut ortho_nmf = onmf::OrthogonalNMF::<f64>::init_random01(
        nhidden, nobserved, nsamples, &mut rng);

    let mut iteration: i32 = 0;
    loop {
        // alpha gets larger and larger with each iteration
        // 0.1, 0.101, 0.102, 0.103, ...
        // at iteration 232 alpha first goes above 1.0
        // iteration = 232 -> alpha = 1.005
        let alpha = 0.1 * 1.01.powi(iteration);
        // TODO this could converge faster
        // let alpha = 0.1 * 1.001.powi(iteration);

        // let alpha = 0.1;

        // TODO maybe try an even smaller alpha
        // let alpha = 0.01;

        println!("iteration = {} alpha = {}", iteration, alpha);

        ortho_nmf.iterate(alpha, &data);

        // read testimage out of each row of nmf.hidden
        for irow in 0..nhidden {
            let mut column = Vec::<f64>::new();
            for icol in 0..nobserved {
                column.push(ortho_nmf.hidden[(irow, icol)]);
            }
            let image = DMat::<f64>::from_col_vec(10, 10, &column[..]);
            // println!("image {}", irow);
            // println!("{:?}", image);
            magnify(image.normalize(), mag_factor).save_to_png(&format!("orthogonal-nmf-hidden-{}.png", irow)[..]).unwrap();
        }

        iteration += 1;
    }
}
<commit_msg>blas backed ortho nmf: seems to be working and holy shit its fast :)<commit_after>extern crate onmf;
use onmf::helpers::{ToImage, Normalize, magnify};
use onmf::testimage_generator;

extern crate nalgebra;
use self::nalgebra::{DMat};

extern crate rand;
use rand::{StdRng, SeedableRng, ChaChaRng};

extern crate num;
use num::{Float};

extern crate ndarray;
use ndarray::ArrayBase;

fn main() {
    let mag_factor = 10;

    let seed: &[_] = &[1, 2, 3, 4];
    // let mut rng: ChaChaRng = ChaChaRng::from_seed(seed);
    let mut rng: StdRng = SeedableRng::from_seed(seed);

    let steps = 12;
    let per_step = 1000;
    let nsamples = steps * per_step;
    let nobserved = 10 * 10;
    let nhidden = 20 + steps;
    // let nhidden = 40;

    let mut data: ArrayBase<Vec<f64>, (usize, usize)> =
        ArrayBase::zeros((nsamples, nobserved));

    // write one testimage into each row of data
    for (horizontal, vertical, i, factor) in testimage_generator::testimages::<f64, _>(per_step, &mut rng) {
        let row = (horizontal + vertical) * i;
        for (col, val) in factor.as_vec().iter().enumerate() {
            data[(row, col)] = val.clone();
        }
    }

    // let mut ortho_nmf = onmf::OrthogonalNMF::<f64>::init_random01(
    let mut ortho_nmf = onmf::OrthogonalNMFBlas::init_random01(
        nhidden, nobserved, nsamples, &mut rng);

    let mut iteration: i32 = 0;
    loop {
        // alpha gets larger and larger with each iteration
        // 0.1, 0.101, 0.102, 0.103, ...
        // at iteration 232 alpha first goes above 1.0
        // iteration = 232 -> alpha = 1.005
        let alpha = 0.1 * 1.01.powi(iteration);
        // TODO this could converge faster
        // let alpha = 0.1 * 1.0001.powi(iteration);

        // let alpha = 0.1;

        // TODO maybe try an even smaller alpha
        // let alpha = 0.01;

        println!("iteration = {} alpha = {}", iteration, alpha);

        ortho_nmf.iterate(alpha, &mut data);

        // read testimage out of each row of nmf.hidden
        for irow in 0..nhidden {
            let mut column = Vec::<f64>::new();
            for icol in 0..nobserved {
                column.push(ortho_nmf.hidden[(irow, icol)]);
            }
            let image = DMat::<f64>::from_col_vec(10, 10, &column[..]);
            // println!("image {}", irow);
            // println!("{:?}", image);
            magnify(image.normalize(), mag_factor).save_to_png(&format!("orthogonal-nmf-hidden-{}.png", irow)[..]).unwrap();
        }

        iteration += 1;
    }
}
<|endoftext|>


<commit_before>use devices::{NodeId, ServiceKind, Service, Input, Output};
use util::{Exactly, Id};
use values;

use serde::ser::Serializer;
use serde::de::Deserializer;

use std::cmp;

fn merge<T>(mut a: Vec<T>, mut b: Vec<T>) -> Vec<T> where T: Ord {
    a.append(&mut b);
    a.sort();
    a.dedup();
    a
}

/// A selector for one or more nodes.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = NodeSelector::new()
///   .with_tags(vec!["entrance".to_owned()])
///   .with_inputs(vec![InputSelector::new() /* can be more restrictive */]);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NodeSelector {
    /// If `Exactly(id)`, return only the node with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<NodeId>>,

    ///  Restrict results to nodes that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// Restrict results to nodes that have all the inputs in `inputs`.
    #[serde(default)]
    pub inputs: Vec<InputSelector>,

    /// Restrict results to nodes that have all the outputs in `outputs`.
    #[serde(default)]
    pub outputs: Vec<OutputSelector>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl NodeSelector {
    /// Create a new selector that accepts all nodes.
    pub fn new() -> Self {
        Self::default()
    }

    /// Selector for a node with a specific id.
    pub fn with_id(self, id: Id<NodeId>) -> Self {
        NodeSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    ///  Restrict results to nodes that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        NodeSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict results to nodes that have all the inputs in `inputs`.
    pub fn with_inputs(mut self, mut inputs: Vec<InputSelector>) -> Self {
        NodeSelector {
            inputs: {self.inputs.append(&mut inputs); self.inputs},
            .. self
        }
    }

    /// Restrict results to nodes that have all the outputs in `outputs`.
    pub fn with_outputs(mut self, mut outputs: Vec<OutputSelector>) -> Self {
        NodeSelector {
            outputs: {self.outputs.append(&mut outputs); self.outputs},
            .. self
        }
    }

    /// Restrict results to nodes that are accepted by two selector.
    pub fn and(mut self, mut other: NodeSelector) -> Self {
        NodeSelector {
            id: self.id.and(other.id),
            tags: merge(self.tags, other.tags),
            inputs: {self.inputs.append(&mut other.inputs); self.inputs},
            outputs: {self.outputs.append(&mut other.outputs); self.outputs},
            private: (),
        }
    }
}

/// A selector for one or more input services.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
/// use fxbox_taxonomy::util::Id;
///
/// let selector = InputSelector::new()
///   .with_parent(Id<NodeId>::new("foxbox".to_owned()))
///   .with_kind(ServiceKind::CurrentTimeOfDay);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct InputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Input>>,

    /// If `Eactly(id)`, return only services that are children of
    /// node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exatly(k)`, restrict results to services that produce values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support polling
    /// with the acceptable period.
    #[serde(default)]
    pub poll: Option<Period>,

    /// If `Some(r)`, restrict results to services that support trigger
    /// with the acceptable period.
    #[serde(default)]
    pub trigger: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}
impl InputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        Self::default()
    }

    /// Restrict to a service with a specific id.
    pub fn with_id(self, id: Id<Input>) -> Self {
        InputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        InputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        InputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        InputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support polling with the acceptable
    /// period
    pub fn with_poll(self, period: Period) -> Self {
        InputSelector {
            poll: Period::and_option(self.poll, Some(period)),
            .. self
        }
    }

    /// Restrict to services that support trigger with the acceptable
    /// period
    pub fn with_trigger(self, period: Period) -> Self {
        InputSelector {
            trigger: Period::and_option(self.trigger, Some(period)),
            .. self
        }
    }

    /// Restrict to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        InputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            poll: Period::and_option(self.poll, other.poll),
            trigger: Period::and_option(self.trigger, other.trigger),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Input>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.poll, &service.mechanism.poll) {
            return false;
        }
        if !Period::matches_option(&self.trigger, &service.mechanism.trigger) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// A selector for one or more output services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct OutputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Output>>,

    /// If `Exactly(id)`, return only services that are immediate children
    /// of node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exactly(k)`, restrict results to services that accept values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support pushing
    /// with the acceptable period.
    #[serde(default)]
    pub push: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl OutputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        OutputSelector::default()
    }

    /// Selector to a service with a specific id.
    pub fn with_id(self, id: Id<Output>) -> Self {
        OutputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        OutputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        OutputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        OutputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support push with the acceptable
    /// period
    pub fn with_push(self, period: Period) -> Self {
        OutputSelector {
            push: Period::and_option(self.push, Some(period)),
            .. self
        }
    }

    /// Restrict results to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        OutputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            push: Period::and_option(self.push, other.push),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Output>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.push, &service.mechanism.push) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// An acceptable interval of time.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Period {
    #[serde(default)]
    pub min: Option<values::ValDuration>,
    #[serde(default)]
    pub max: Option<values::ValDuration>,
}
impl Period {
    pub fn and(self, other: Self) -> Self {
        let min = match (self.min, other.min) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(min1), Some(min2)) => Some(cmp::max(min1, min2))
        };
        let max = match (self.max, other.max) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(max1), Some(max2)) => Some(cmp::min(max1, max2))
        };
        Period {
            min: min,
            max: max
        }
    }

    pub fn and_option(a: Option<Self>, b: Option<Self>) -> Option<Self> {
        match (a, b) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(a), Some(b)) => Some(a.and(b))
        }
    }

    pub fn matches(&self, duration: &values::ValDuration) -> bool {
        if let Some(ref min) = self.min {
            if min > duration {
                return false;
            }
        }
        if let Some(ref max) = self.max {
            if max < duration {
                return false;
            }
        }
        return true;
    }

    pub fn matches_option(period: &Option<Self>, duration: &Option<values::ValDuration>) -> bool {
        match (period, duration) {
            (&Some(ref period), &Some(ref duration))
                if !period.matches(duration) => false,
            (&Some(_), &None) => false,
            _ => true,
        }
    }
}

fn has_selected_tags(actual: &Vec<String>, requested: &Vec<String>) -> bool {
    for tag in &*actual {
        if requested.iter().find(|x| *x == tag).is_none() {
            return false;
        }
    }
    return true;
}
<commit_msg>Fixed doctest, part 2<commit_after>use devices::{NodeId, ServiceKind, Service, Input, Output};
use util::{Exactly, Id};
use values;

use serde::ser::Serializer;
use serde::de::Deserializer;

use std::cmp;

fn merge<T>(mut a: Vec<T>, mut b: Vec<T>) -> Vec<T> where T: Ord {
    a.append(&mut b);
    a.sort();
    a.dedup();
    a
}

/// A selector for one or more nodes.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = NodeSelector::new()
///   .with_tags(vec!["entrance".to_owned()])
///   .with_inputs(vec![InputSelector::new() /* can be more restrictive */]);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NodeSelector {
    /// If `Exactly(id)`, return only the node with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<NodeId>>,

    ///  Restrict results to nodes that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// Restrict results to nodes that have all the inputs in `inputs`.
    #[serde(default)]
    pub inputs: Vec<InputSelector>,

    /// Restrict results to nodes that have all the outputs in `outputs`.
    #[serde(default)]
    pub outputs: Vec<OutputSelector>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl NodeSelector {
    /// Create a new selector that accepts all nodes.
    pub fn new() -> Self {
        Self::default()
    }

    /// Selector for a node with a specific id.
    pub fn with_id(self, id: Id<NodeId>) -> Self {
        NodeSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    ///  Restrict results to nodes that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        NodeSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict results to nodes that have all the inputs in `inputs`.
    pub fn with_inputs(mut self, mut inputs: Vec<InputSelector>) -> Self {
        NodeSelector {
            inputs: {self.inputs.append(&mut inputs); self.inputs},
            .. self
        }
    }

    /// Restrict results to nodes that have all the outputs in `outputs`.
    pub fn with_outputs(mut self, mut outputs: Vec<OutputSelector>) -> Self {
        NodeSelector {
            outputs: {self.outputs.append(&mut outputs); self.outputs},
            .. self
        }
    }

    /// Restrict results to nodes that are accepted by two selector.
    pub fn and(mut self, mut other: NodeSelector) -> Self {
        NodeSelector {
            id: self.id.and(other.id),
            tags: merge(self.tags, other.tags),
            inputs: {self.inputs.append(&mut other.inputs); self.inputs},
            outputs: {self.outputs.append(&mut other.outputs); self.outputs},
            private: (),
        }
    }
}

/// A selector for one or more input services.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
/// use fxbox_taxonomy::util::Id;
///
/// let selector = InputSelector::new()
///   .with_parent(Id::new("foxbox".to_owned()))
///   .with_kind(ServiceKind::CurrentTimeOfDay);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct InputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Input>>,

    /// If `Eactly(id)`, return only services that are children of
    /// node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exatly(k)`, restrict results to services that produce values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support polling
    /// with the acceptable period.
    #[serde(default)]
    pub poll: Option<Period>,

    /// If `Some(r)`, restrict results to services that support trigger
    /// with the acceptable period.
    #[serde(default)]
    pub trigger: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}
impl InputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        Self::default()
    }

    /// Restrict to a service with a specific id.
    pub fn with_id(self, id: Id<Input>) -> Self {
        InputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        InputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        InputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        InputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support polling with the acceptable
    /// period
    pub fn with_poll(self, period: Period) -> Self {
        InputSelector {
            poll: Period::and_option(self.poll, Some(period)),
            .. self
        }
    }

    /// Restrict to services that support trigger with the acceptable
    /// period
    pub fn with_trigger(self, period: Period) -> Self {
        InputSelector {
            trigger: Period::and_option(self.trigger, Some(period)),
            .. self
        }
    }

    /// Restrict to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        InputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            poll: Period::and_option(self.poll, other.poll),
            trigger: Period::and_option(self.trigger, other.trigger),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Input>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.poll, &service.mechanism.poll) {
            return false;
        }
        if !Period::matches_option(&self.trigger, &service.mechanism.trigger) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// A selector for one or more output services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct OutputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Output>>,

    /// If `Exactly(id)`, return only services that are immediate children
    /// of node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exactly(k)`, restrict results to services that accept values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support pushing
    /// with the acceptable period.
    #[serde(default)]
    pub push: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl OutputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        OutputSelector::default()
    }

    /// Selector to a service with a specific id.
    pub fn with_id(self, id: Id<Output>) -> Self {
        OutputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        OutputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        OutputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        OutputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support push with the acceptable
    /// period
    pub fn with_push(self, period: Period) -> Self {
        OutputSelector {
            push: Period::and_option(self.push, Some(period)),
            .. self
        }
    }

    /// Restrict results to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        OutputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            push: Period::and_option(self.push, other.push),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Output>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.push, &service.mechanism.push) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// An acceptable interval of time.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Period {
    #[serde(default)]
    pub min: Option<values::ValDuration>,
    #[serde(default)]
    pub max: Option<values::ValDuration>,
}
impl Period {
    pub fn and(self, other: Self) -> Self {
        let min = match (self.min, other.min) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(min1), Some(min2)) => Some(cmp::max(min1, min2))
        };
        let max = match (self.max, other.max) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(max1), Some(max2)) => Some(cmp::min(max1, max2))
        };
        Period {
            min: min,
            max: max
        }
    }

    pub fn and_option(a: Option<Self>, b: Option<Self>) -> Option<Self> {
        match (a, b) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(a), Some(b)) => Some(a.and(b))
        }
    }

    pub fn matches(&self, duration: &values::ValDuration) -> bool {
        if let Some(ref min) = self.min {
            if min > duration {
                return false;
            }
        }
        if let Some(ref max) = self.max {
            if max < duration {
                return false;
            }
        }
        return true;
    }

    pub fn matches_option(period: &Option<Self>, duration: &Option<values::ValDuration>) -> bool {
        match (period, duration) {
            (&Some(ref period), &Some(ref duration))
                if !period.matches(duration) => false,
            (&Some(_), &None) => false,
            _ => true,
        }
    }
}

fn has_selected_tags(actual: &Vec<String>, requested: &Vec<String>) -> bool {
    for tag in &*actual {
        if requested.iter().find(|x| *x == tag).is_none() {
            return false;
        }
    }
    return true;
}
<|endoftext|>