<commit_before>use im::HashMap;
use itertools::Itertools;

use crate::{
    builtin_functions::BuiltinFunction,
    compiler::hir::{self, Expression},
    discover::run::run_call,
    input::Input,
};

use super::{
    result::DiscoverResult,
    run::Discover,
    value::{Environment, Value},
};

pub fn run_builtin_function(
    db: &dyn Discover,
    builtin_function: BuiltinFunction,
    arguments: Vec<hir::Id>,
    environment: Environment,
) -> DiscoverResult {
    log::info!(
        "run_builtin_function: builtin{:?} {:?}",
        builtin_function,
        arguments
    );
    // Handle builtin functions that don't need to resolve the arguments.
    match builtin_function {
        BuiltinFunction::IfElse => return if_else(db, arguments, environment),
        _ => {}
    }

    let arguments = arguments
        .iter()
        .map(|it| environment.get(it).transitive())
        .collect::<DiscoverResult<_>>()?;
    match builtin_function {
        BuiltinFunction::Add => add(arguments),
        BuiltinFunction::Equals => equals(arguments),
        BuiltinFunction::GetArgumentCount => get_argument_count(db, arguments),
        BuiltinFunction::Panic => panic(arguments),
        BuiltinFunction::Print => print(arguments),
        BuiltinFunction::StructGet => struct_get(arguments),
        BuiltinFunction::StructGetKeys => struct_get_keys(arguments),
        BuiltinFunction::StructHasKey => struct_has_key(arguments),
        BuiltinFunction::TypeOf => type_of(arguments),
        BuiltinFunction::Use => use_(db, arguments),
        _ => panic!("Unhandled builtin function: {:?}", builtin_function),
    }
}

macro_rules! destructure {
    ($arguments:expr, $enum:pat, $body:block) => {{
        if let $enum = &$arguments[..] {
            $body
        } else {
            DiscoverResult::panic(format!("Invalid arguments").to_owned())
        }
    }};
}

fn add(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [Value::Int(a), Value::Int(b)], {
        Value::Int(a + b).into()
    })
}

fn equals(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [a, b], { Value::bool(a == b).into() })
}

fn get_argument_count(db: &dyn Discover, arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [Value::Lambda(function)], {
        // TODO: support parameter counts > 2^64 on 128-bit systems and better
        let expression = match db.find_expression(function.id.to_owned()).unwrap() {
            Expression::Lambda(lambda) => lambda,
            _ => panic!("Lambda's function"),
        };
        Value::Int(expression.parameters.len() as u64).into()
    })
}

fn if_else(db: &dyn Discover, arguments: Vec<hir::Id>, environment: Environment) -> DiscoverResult {
    if let [condition, then, else_] = &arguments[..] {
        let body_id = match environment.get(condition)? {
            value if value == Value::bool(true) => then,
            value if value == Value::bool(false) => else_,
            value => {
                return DiscoverResult::panic(format!(
                    "Condition must be a boolean, but was {:?}.",
                    value
                ));
            }
        };

        run_call(db, body_id.to_owned(), vec![], environment)
    } else {
        DiscoverResult::panic(format!(
            "Builtin if/else called with wrong number of arguments: {}, expected: {}",
            arguments.len(),
            3
        ))
    }
}

fn panic(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [value], { DiscoverResult::Panic(value.clone()) })
}

fn print(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [value], {
        println!("{:?}", value);
        Value::nothing().into()
    })
}

fn struct_get(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [struct_, key], {
        let struct_ = expect_struct("builtinStructGet".to_owned(), struct_)?;
        struct_
            .get(key)
            .map(|value| value.clone().into())
            .unwrap_or_else(|| {
                DiscoverResult::panic(format!("Struct does not contain key {:?}", key))
            })
    })
}
fn struct_get_keys(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [struct_], {
        let struct_ = expect_struct("builtinStructGetKeys".to_owned(), struct_)?;
        Value::list(struct_.keys().cloned().collect()).into()
    })
}
fn struct_has_key(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [struct_, key], {
        let struct_ = expect_struct("builtinStructHasKey".to_owned(), struct_)?;
        Value::bool(struct_.contains_key(key)).into()
    })
}
fn expect_struct(function_name: String, value: &Value) -> DiscoverResult<&HashMap<Value, Value>> {
    match value {
        Value::Struct(struct_) => struct_.into(),
        _ => {
            return DiscoverResult::panic(format!(
                "`{}` expected a struct as its first parameter, but received: {:?}",
                function_name, value
            ))
        }
    }
}

fn type_of(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [value], {
        match value {
            Value::Int(_) => Value::Symbol("Int".to_owned()).into(),
            Value::Text(_) => Value::Symbol("Text".to_owned()).into(),
            Value::Symbol(_) => Value::Symbol("Symbol".to_owned()).into(),
            Value::Struct(_) => Value::Symbol("Struct".to_owned()).into(),
            Value::Lambda(_) => Value::Symbol("Function".to_owned()).into(),
        }
    })
}

fn use_(db: &dyn Discover, arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [current_path, target], {
        // `current_path` is set by us and not users, hence we don't have to validate it that strictly.
        let current_path_struct = match current_path {
            Value::Struct(value) => value,
            _ => unreachable!(),
        };
        let mut current_path = vec![];
        let mut index = 0;
        loop {
            if let Some(component) = current_path_struct.get(&Value::Int(index)) {
                match component {
                    Value::Text(component) => current_path.push(component.clone()),
                    _ => unreachable!(),
                }
            } else {
                break;
            }
            index += 1;
        }

        let target = match target {
            Value::Text(value) => value,
            it => {
                return DiscoverResult::panic(format!(
                    "`use` expected a text as its second parameter, but received: {:?}",
                    it
                ))
            }
        };
        let target = match UseTarget::parse(target) {
            Ok(target) => target,
            Err(error) => return DiscoverResult::panic(error),
        };

        if target.parent_navigations > current_path.len() {
            return DiscoverResult::panic("Too many parent navigations.".to_owned());
        }

        let inputs = target.resolve(&current_path[..]);
        let input = match inputs
            .iter()
            .filter(|&it| db.get_input(it.to_owned()).is_some())
            .next()
        {
            Some(target) => target,
            None => {
                return DiscoverResult::panic(format!(
                    "Target doesn't exist. Checked the following path(s): {}",
                    inputs.iter().map(|it| format!("{}", it)).join(", ")
                ));
            }
        };

        let (hir, _) = db.hir(input.clone()).unwrap();
        let discover_result = db.run_all(input.to_owned());

        hir.identifiers
            .iter()
            .map(|(id, key)| {
                let mut key = key.to_owned();
                key.get_mut(0..1).unwrap().make_ascii_uppercase();
                let key = Value::Symbol(key.to_owned());

                let value = match discover_result.get(id) {
                    Some(value) => value.to_owned()?,
                    None => return DiscoverResult::ErrorInHir,
                };

                DiscoverResult::Value((key, value))
            })
            .collect::<DiscoverResult<HashMap<Value, Value>>>()
            .map(|it| Value::Struct(it))
    })
}
struct UseTarget {
    parent_navigations: usize,
    path: Vec<String>,
}
impl UseTarget {
    const PARENT_NAVIGATION_CHAR: char = '.';

    fn parse(target: &str) -> Result<Self, String> {
        let mut parent_navigations = 0;
        let mut target = target;
        while target.chars().next() == Some(UseTarget::PARENT_NAVIGATION_CHAR) {
            parent_navigations += 1;
            target = &target[UseTarget::PARENT_NAVIGATION_CHAR.len_utf8()..];
        }

        let mut path = vec![];
        loop {
            let mut chars = vec![];
            while let Some(c) = target.chars().next() {
                if c == UseTarget::PARENT_NAVIGATION_CHAR {
                    break;
                }
                chars.push(c);
                target = &target[c.len_utf8()..];
            }

            if target.is_empty() {
                path.push(chars.into_iter().join(""));
                break;
            }

            if chars.is_empty() {
                return Err("Target contains consecutive dots (`.`) in the path.".to_owned());
            }

            path.push(chars.into_iter().join(""));
        }
        Ok(UseTarget {
            parent_navigations,
            path,
        })
    }

    fn resolve(&self, current_path: &[String]) -> Vec<Input> {
        let mut path = current_path;
        for _ in 0..self.parent_navigations {
            if path.is_empty() {
                return vec![];
            }
            path = &path[..path.len() - 1];
        }

        let mut path = path.to_owned();
        for part in &self.path {
            path.push(part.to_owned());
        }

        let mut result = vec![];

        let mut subdirectory = path.clone();
        subdirectory.push(".candy".to_owned());
        result.push(Input::File(subdirectory));

        if path.len() > 1 {
            let last = path.last_mut().unwrap();
            *last = format!("{}.candy", last);
            result.push(Input::File(path));
        }
        result
    }
}
<commit_msg>fix(compiler): fix use path resolving<commit_after>use im::HashMap;
use itertools::Itertools;

use crate::{
    builtin_functions::BuiltinFunction,
    compiler::hir::{self, Expression},
    discover::run::run_call,
    input::Input,
};

use super::{
    result::DiscoverResult,
    run::Discover,
    value::{Environment, Value},
};

pub fn run_builtin_function(
    db: &dyn Discover,
    builtin_function: BuiltinFunction,
    arguments: Vec<hir::Id>,
    environment: Environment,
) -> DiscoverResult {
    log::info!(
        "run_builtin_function: builtin{:?} {:?}",
        builtin_function,
        arguments
    );
    // Handle builtin functions that don't need to resolve the arguments.
    match builtin_function {
        BuiltinFunction::IfElse => return if_else(db, arguments, environment),
        _ => {}
    }

    let arguments = arguments
        .iter()
        .map(|it| environment.get(it).transitive())
        .collect::<DiscoverResult<_>>()?;
    match builtin_function {
        BuiltinFunction::Add => add(arguments),
        BuiltinFunction::Equals => equals(arguments),
        BuiltinFunction::GetArgumentCount => get_argument_count(db, arguments),
        BuiltinFunction::Panic => panic(arguments),
        BuiltinFunction::Print => print(arguments),
        BuiltinFunction::StructGet => struct_get(arguments),
        BuiltinFunction::StructGetKeys => struct_get_keys(arguments),
        BuiltinFunction::StructHasKey => struct_has_key(arguments),
        BuiltinFunction::TypeOf => type_of(arguments),
        BuiltinFunction::Use => use_(db, arguments),
        _ => panic!("Unhandled builtin function: {:?}", builtin_function),
    }
}

macro_rules! destructure {
    ($arguments:expr, $enum:pat, $body:block) => {{
        if let $enum = &$arguments[..] {
            $body
        } else {
            DiscoverResult::panic(format!("Invalid arguments").to_owned())
        }
    }};
}

fn add(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [Value::Int(a), Value::Int(b)], {
        Value::Int(a + b).into()
    })
}

fn equals(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [a, b], { Value::bool(a == b).into() })
}

fn get_argument_count(db: &dyn Discover, arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [Value::Lambda(function)], {
        // TODO: support parameter counts > 2^64 on 128-bit systems and better
        let expression = match db.find_expression(function.id.to_owned()).unwrap() {
            Expression::Lambda(lambda) => lambda,
            _ => panic!("Lambda's function"),
        };
        Value::Int(expression.parameters.len() as u64).into()
    })
}

fn if_else(db: &dyn Discover, arguments: Vec<hir::Id>, environment: Environment) -> DiscoverResult {
    if let [condition, then, else_] = &arguments[..] {
        let body_id = match environment.get(condition)? {
            value if value == Value::bool(true) => then,
            value if value == Value::bool(false) => else_,
            value => {
                return DiscoverResult::panic(format!(
                    "Condition must be a boolean, but was {:?}.",
                    value
                ));
            }
        };

        run_call(db, body_id.to_owned(), vec![], environment)
    } else {
        DiscoverResult::panic(format!(
            "Builtin if/else called with wrong number of arguments: {}, expected: {}",
            arguments.len(),
            3
        ))
    }
}

fn panic(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [value], { DiscoverResult::Panic(value.clone()) })
}

fn print(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [value], {
        println!("{:?}", value);
        Value::nothing().into()
    })
}

fn struct_get(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [struct_, key], {
        let struct_ = expect_struct("builtinStructGet".to_owned(), struct_)?;
        struct_
            .get(key)
            .map(|value| value.clone().into())
            .unwrap_or_else(|| {
                DiscoverResult::panic(format!("Struct does not contain key {:?}", key))
            })
    })
}
fn struct_get_keys(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [struct_], {
        let struct_ = expect_struct("builtinStructGetKeys".to_owned(), struct_)?;
        Value::list(struct_.keys().cloned().collect()).into()
    })
}
fn struct_has_key(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [struct_, key], {
        let struct_ = expect_struct("builtinStructHasKey".to_owned(), struct_)?;
        Value::bool(struct_.contains_key(key)).into()
    })
}
fn expect_struct(function_name: String, value: &Value) -> DiscoverResult<&HashMap<Value, Value>> {
    match value {
        Value::Struct(struct_) => struct_.into(),
        _ => {
            return DiscoverResult::panic(format!(
                "`{}` expected a struct as its first parameter, but received: {:?}",
                function_name, value
            ))
        }
    }
}

fn type_of(arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [value], {
        match value {
            Value::Int(_) => Value::Symbol("Int".to_owned()).into(),
            Value::Text(_) => Value::Symbol("Text".to_owned()).into(),
            Value::Symbol(_) => Value::Symbol("Symbol".to_owned()).into(),
            Value::Struct(_) => Value::Symbol("Struct".to_owned()).into(),
            Value::Lambda(_) => Value::Symbol("Function".to_owned()).into(),
        }
    })
}

fn use_(db: &dyn Discover, arguments: Vec<Value>) -> DiscoverResult {
    destructure!(arguments, [current_path, target], {
        // `current_path` is set by us and not users, hence we don't have to validate it that strictly.
        let current_path_struct = match current_path {
            Value::Struct(value) => value,
            _ => unreachable!(),
        };
        let mut current_path = vec![];
        let mut index = 0;
        loop {
            if let Some(component) = current_path_struct.get(&Value::Int(index)) {
                match component {
                    Value::Text(component) => current_path.push(component.clone()),
                    _ => unreachable!(),
                }
            } else {
                break;
            }
            index += 1;
        }

        let target = match target {
            Value::Text(value) => value,
            it => {
                return DiscoverResult::panic(format!(
                    "`use` expected a text as its second parameter, but received: {:?}",
                    it
                ))
            }
        };
        let target = match UseTarget::parse(target) {
            Ok(target) => target,
            Err(error) => return DiscoverResult::panic(error),
        };

        if target.parent_navigations > current_path.len() {
            return DiscoverResult::panic("Too many parent navigations.".to_owned());
        }

        let inputs = target.resolve(&current_path[..]);
        let input = match inputs
            .iter()
            .filter(|&it| db.get_input(it.to_owned()).is_some())
            .next()
        {
            Some(target) => target,
            None => {
                return DiscoverResult::panic(format!(
                    "Target doesn't exist. Checked the following path(s): {}",
                    inputs.iter().map(|it| format!("{}", it)).join(", ")
                ));
            }
        };

        let (hir, _) = db.hir(input.clone()).unwrap();
        let discover_result = db.run_all(input.to_owned());

        hir.identifiers
            .iter()
            .map(|(id, key)| {
                let mut key = key.to_owned();
                key.get_mut(0..1).unwrap().make_ascii_uppercase();
                let key = Value::Symbol(key.to_owned());

                let value = match discover_result.get(id) {
                    Some(value) => value.to_owned()?,
                    None => return DiscoverResult::ErrorInHir,
                };

                DiscoverResult::Value((key, value))
            })
            .collect::<DiscoverResult<HashMap<Value, Value>>>()
            .map(|it| Value::Struct(it))
    })
}
struct UseTarget {
    parent_navigations: usize,
    path: Vec<String>,
}
impl UseTarget {
    const PARENT_NAVIGATION_CHAR: char = '.';

    fn parse(target: &str) -> Result<Self, String> {
        let mut parent_navigations = 0;
        let mut target = target;
        while target.chars().next() == Some(UseTarget::PARENT_NAVIGATION_CHAR) {
            parent_navigations += 1;
            target = &target[UseTarget::PARENT_NAVIGATION_CHAR.len_utf8()..];
        }

        let mut path = vec![];
        loop {
            let mut chars = vec![];
            while let Some(c) = target.chars().next() {
                if c == UseTarget::PARENT_NAVIGATION_CHAR {
                    break;
                }
                chars.push(c);
                target = &target[c.len_utf8()..];
            }

            if target.is_empty() {
                path.push(chars.into_iter().join(""));
                break;
            }

            if chars.is_empty() {
                return Err("Target contains consecutive dots (`.`) in the path.".to_owned());
            }

            path.push(chars.into_iter().join(""));
        }
        Ok(UseTarget {
            parent_navigations,
            path,
        })
    }

    fn resolve(&self, current_path: &[String]) -> Vec<Input> {
        let mut path = current_path.to_owned();
        if self.parent_navigations == 0 {
            assert!(!path.is_empty());
            let last = path.last_mut().unwrap();
            if last == ".candy" {
                path.pop();
            } else {
                *last = last
                    .strip_suffix(".candy")
                    .expect("File name must end with `.candy`.")
                    .to_owned();
            }
        } else {
            for _ in 0..self.parent_navigations {
                if path.is_empty() {
                    return vec![];
                }
                path.pop();
            }
        }

        for part in &self.path {
            path.push(part.to_owned());
        }

        let mut result = vec![];

        let mut subdirectory = path.clone();
        subdirectory.push(".candy".to_owned());
        result.push(Input::File(subdirectory));

        if path.len() >= 1 {
            let last = path.last_mut().unwrap();
            *last = format!("{}.candy", last);
            result.push(Input::File(path));
        }
        result
    }
}
<|endoftext|>


<commit_before>use std::f64;
use num::Zero;
use approx::ApproxEq;
use smallvec::SmallVec;

use na::{self, Real, Unit};
use utils;
use math::Vector;

#[derive(Clone, Debug)]
pub struct PolyhedralCone<V: Vector> {
    // FIXME: specialization, 2D polycones cannot contain more than 2 generators.
    generators: SmallVec<[Unit<V>; 4]>,
}

impl<V: Vector> PolyhedralCone<V> {
    pub fn new() -> Self {
        Self::from_slice(&[])
    }

    pub fn from_slice(normals: &[Unit<V>]) -> Self {
        PolyhedralCone {
            generators: SmallVec::from_slice(normals),
        }
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.generators.len()
    }

    #[inline]
    pub fn clear(&mut self) {
        self.generators.clear();
    }

    #[inline]
    pub fn add_generator(&mut self, gen: Unit<V>) {
        if na::dimension::<V>() == 2 && self.generators.len() == 2 {
            // FIXME: too restrictive?
            panic!("Polyhedral cone: 2D polyhedral cones cannot contain more than 2 generators.")
        }
        self.generators.push(gen);
    }

    pub fn project(&self, dir: &Unit<V>) -> Unit<V> {
        if self.generators.len() == 0 {
            *dir
        } else if self.generators.len() == 1 {
            self.generators[0]
        } else {
            if na::dimension::<V>() == 2 {
                assert!(self.generators.len() == 2);
                let perp1 = utils::perp2(&**dir, &*self.generators[0]);
                let perp2 = utils::perp2(&**dir, &*self.generators[1]);
                let _0 = V::Real::zero();

                match (perp1 > _0, perp2 > _0) {
                    (true, true) => self.generators[0],
                    (false, false) => self.generators[1],
                    (false, true) => *dir,
                    (true, false) => {
                        if perp1 <= -perp2 {
                            self.generators[0]
                        } else {
                            self.generators[1]
                        }
                    }
                }
            } else {
                unimplemented!()
            }
        }
    }

    pub fn contains(&self, v: &V) -> bool {
        if let Some(dir) = Unit::try_new(*v, V::Real::default_epsilon()) {
            self.contains_dir(&dir)
        } else {
            true
        }
    }

    pub fn contains_dir(&self, dir: &Unit<V>) -> bool {
        if self.generators.len() == 0 {
            true
        } else if na::dimension::<V>() == 2 {
            // NOTE: the following assumes the polycone
            // generator are ordered in CCW order.
            assert!(self.generators.len() == 2);
            let perp1 = utils::perp2(dir.as_ref(), &*self.generators[0]);
            let perp2 = utils::perp2(dir.as_ref(), &*self.generators[1]);
            let _0 = V::Real::zero();

            (perp1 <= _0 && perp2 >= _0)
        } else {
            // NOTE: the following does not makes any assumptions on the
            // polycone orientation.
            let mut sign = V::Real::zero();
            if self.generators.len() == 1 {
                unimplemented!()
            }
            else if self.generators.len() == 2 {
                let eps = na::convert(f64::consts::PI / 180.0 * 0.1);
                let normal = utils::cross3(&*self.generators[1], &*self.generators[0]);

                if let Some(normal) = Unit::try_new(normal, na::zero()) {
                    if na::dot(&*normal, dir.as_ref()).abs() > eps {
                        return false;
                    }

                    let middle = (*self.generators[0] + *self.generators[1]) * na::convert(0.5);
                    if na::dot(&middle, dir.as_ref()) < na::zero() {
                        return false;
                    }

                    let cross1 = utils::cross3(&*self.generators[0], dir.as_ref());
                    let cross2 = utils::cross3(&*self.generators[1], dir.as_ref());

                    na::dot(&cross1, &*normal) * na::dot(&cross2, &*normal) <= na::zero()
                } else {
                    // The polycone is degenerate and actually has only one generactor.
                    let dot = na::dot(&*self.generators[0], dir.as_ref());
                    dot <= eps.cos()
                }
            } else {
                for i1 in 0..self.generators.len() {
                    let i2 = (i1 + 1) % self.generators.len();
                    let cross =
                        utils::cross3(self.generators[i1].as_ref(), self.generators[i2].as_ref());
                    let dot = na::dot(dir.as_ref(), &cross);

                    if sign.is_zero() {
                        sign = dot
                    } else if sign * dot < na::zero() {
                        return false;
                    }
                }

                // true
                // FIXME: how to differenciate the case where the vector is
                // on the cone or on its opposite?
                unimplemented!()
            }
        }
    }
}
<commit_msg>Fix direction inclusion test on non-degenerate 3D polyhedral cone.<commit_after>use std::f64;
use num::Zero;
use approx::ApproxEq;
use smallvec::SmallVec;

use na::{self, Real, Unit};
use utils;
use math::Vector;

#[derive(Clone, Debug)]
pub struct PolyhedralCone<V: Vector> {
    // FIXME: specialization, 2D polycones cannot contain more than 2 generators.
    generators: SmallVec<[Unit<V>; 4]>,
}

impl<V: Vector> PolyhedralCone<V> {
    pub fn new() -> Self {
        Self::from_slice(&[])
    }

    pub fn from_slice(normals: &[Unit<V>]) -> Self {
        PolyhedralCone {
            generators: SmallVec::from_slice(normals),
        }
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.generators.len()
    }

    #[inline]
    pub fn clear(&mut self) {
        self.generators.clear();
    }

    #[inline]
    pub fn add_generator(&mut self, gen: Unit<V>) {
        if na::dimension::<V>() == 2 && self.generators.len() == 2 {
            // FIXME: too restrictive?
            panic!("Polyhedral cone: 2D polyhedral cones cannot contain more than 2 generators.")
        }
        self.generators.push(gen);
    }

    pub fn project(&self, dir: &Unit<V>) -> Unit<V> {
        if self.generators.len() == 0 {
            *dir
        } else if self.generators.len() == 1 {
            self.generators[0]
        } else {
            if na::dimension::<V>() == 2 {
                assert!(self.generators.len() == 2);
                let perp1 = utils::perp2(&**dir, &*self.generators[0]);
                let perp2 = utils::perp2(&**dir, &*self.generators[1]);
                let _0 = V::Real::zero();

                match (perp1 > _0, perp2 > _0) {
                    (true, true) => self.generators[0],
                    (false, false) => self.generators[1],
                    (false, true) => *dir,
                    (true, false) => {
                        if perp1 <= -perp2 {
                            self.generators[0]
                        } else {
                            self.generators[1]
                        }
                    }
                }
            } else {
                unimplemented!()
            }
        }
    }

    pub fn contains(&self, v: &V) -> bool {
        if let Some(dir) = Unit::try_new(*v, V::Real::default_epsilon()) {
            self.contains_dir(&dir)
        } else {
            true
        }
    }

    pub fn contains_dir(&self, dir: &Unit<V>) -> bool {
        if self.generators.len() == 0 {
            true
        } else if na::dimension::<V>() == 2 {
            // NOTE: the following assumes the polycone
            // generator are ordered in CCW order.
            assert!(self.generators.len() == 2);
            let perp1 = utils::perp2(dir.as_ref(), &*self.generators[0]);
            let perp2 = utils::perp2(dir.as_ref(), &*self.generators[1]);
            let _0 = V::Real::zero();

            (perp1 <= _0 && perp2 >= _0)
        } else {
            // NOTE: the following does not makes any assumptions on the
            // polycone orientation.
            let mut sign = V::Real::zero();
            if self.generators.len() == 1 {
                unimplemented!()
            }
            else if self.generators.len() == 2 {
                let eps = na::convert(f64::consts::PI / 180.0 * 0.1);
                let normal = utils::cross3(&*self.generators[1], &*self.generators[0]);

                if let Some(normal) = Unit::try_new(normal, na::zero()) {
                    if na::dot(&*normal, dir.as_ref()).abs() > eps {
                        return false;
                    }

                    let middle = (*self.generators[0] + *self.generators[1]) * na::convert(0.5);
                    if na::dot(&middle, dir.as_ref()) < na::zero() {
                        return false;
                    }

                    let cross1 = utils::cross3(&*self.generators[0], dir.as_ref());
                    let cross2 = utils::cross3(&*self.generators[1], dir.as_ref());

                    na::dot(&cross1, &*normal) * na::dot(&cross2, &*normal) <= na::zero()
                } else {
                    // The polycone is degenerate and actually has only one generactor.
                    let dot = na::dot(&*self.generators[0], dir.as_ref());
                    dot <= eps.cos()
                }
            } else {
                let mut sign = V::Real::zero();
                let mut center = V::zero();

                for i1 in 0..self.generators.len() {
                    let i2 = (i1 + 1) % self.generators.len();
                    let cross =
                        utils::cross3(self.generators[i1].as_ref(), self.generators[i2].as_ref());
                    let dot = na::dot(dir.as_ref(), &cross);
                    center += self.generators[i1].unwrap();

                    if sign.is_zero() {
                        sign = dot
                    } else if sign * dot < na::zero() {
                        return false;
                    }
                }

                // FIXME: is this a sufficient condition to determine if the
                // dir is not of the opposite cone?
                na::dot(&center, dir.as_ref()) >= na::zero()
            }
        }
    }
}
<|endoftext|>


<commit_before>use cocoa::base::id;
use std::error::Error;
use std::fmt;
use std::ptr;
use winit;
use Device;

pub use winit::{
    CursorState,
    Event,
    MonitorId,
    MouseCursor,
    PollEventsIterator,
    WaitEventsIterator,
    ElementState,
    MouseButton,
    MouseScrollDelta,
    Touch,
    TouchPhase,
    ScanCode,
    VirtualKeyCode
};

pub struct WindowBuilder {
    window: winit::WindowBuilder
}

#[derive(Debug)]
pub enum CreationError {
    NoDevice,
    Winit(winit::CreationError)
}

impl fmt::Display for CreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl Error for CreationError {
    fn description(&self) -> &str {
        match *self {
            CreationError::NoDevice => "A device was not set for the windowbuilder",
            CreationError::Winit(ref e) => e.description()
        }
    }

    fn cause(&self) -> Option<&Error> {
        if let CreationError::Winit(ref e) = *self {
            Some(e)
        } else {
            None
        }
    }
}

impl From<winit::CreationError> for CreationError {
    fn from(error: winit::CreationError) -> Self {
        CreationError::Winit(error)
    }
}

impl WindowBuilder {
    /// Initializes a new `WindowBuilder` with default values.
    #[inline]
    pub fn new() -> Self {
        WindowBuilder {
            window: winit::WindowBuilder::new()
        }
    }

    /// Requests the window to be of specific dimensions.
    ///
    /// Width and height are in pixels.
    #[inline]
    pub fn with_dimensions(self, width: u32, height: u32) -> WindowBuilder {
        WindowBuilder {window: self.window.with_dimensions(width, height), ..self}
    }

    /// Sets a minimum dimension size for the window
    ///
    /// Width and height are in pixels.
    #[inline]
    pub fn with_min_dimensions(self, width: u32, height: u32) -> WindowBuilder {
        WindowBuilder {window: self.window.with_min_dimensions(width, height), ..self}
    }

    /// Sets a maximum dimension size for the window
    ///
    /// Width and height are in pixels.
    #[inline]
    pub fn with_max_dimensions(self, width: u32, height: u32) -> WindowBuilder {
        WindowBuilder {window: self.window.with_max_dimensions(width, height), ..self}
    }

    /// Requests a specific title for the window.
    #[inline]
    pub fn with_title(self, title: String) -> WindowBuilder {
        WindowBuilder {window: self.window.with_title(title), ..self}
    }

    /// Requests fullscreen mode.
    ///
    /// If you don't specify dimensions for the window, it will match the monitor's.
    #[inline]
    pub fn with_fullscreen(self, monitor: MonitorId) -> WindowBuilder {
        WindowBuilder {window: self.window.with_fullscreen(monitor), ..self}
    }

    /// Sets whether the window will be initially hidden or visible.
    #[inline]
    pub fn with_visibility(self, visible: bool) -> WindowBuilder {
        WindowBuilder {window: self.window.with_visibility(visible), ..self}
    }

    /// Sets whether the background of the window should be transparent.
    #[inline]
    pub fn with_transparency(self, transparent: bool) -> WindowBuilder {
        WindowBuilder {window: self.window.with_transparency(transparent), ..self}
    }

    /// Sets whether the window should have a border, a title bar, etc.
    #[inline]
    pub fn with_decorations(self, decorations: bool) -> WindowBuilder {
        WindowBuilder {window: self.window.with_decorations(decorations), ..self}
    }

    /// Enables multitouch
    #[inline]
    pub fn with_multitouch(self) -> WindowBuilder {
        WindowBuilder {window: self.window.with_multitouch(), ..self}
    }

    /// Builds the window.
    ///
    /// Error should be very rare and only occur in case of permission denied, incompatible system,
    /// out of memory, etc.
    pub fn build(self) -> Result<Window, CreationError> {
        let window = try!(self.window.build());
        let native_window: id = unsafe { window.platform_window() as id };
        Ok(Window {
            window: window,
            view: ptr::null_mut()
        })
    }

    /// Builds the window.
    ///
    /// The context is build in a *strict* way. That means that if the backend couldn't give
    /// you what you requested, an `Err` will be returned.
    #[inline]
    pub fn build_strict(self) -> Result<Window, CreationError> {
        self.build()
    }
}

pub struct Window {
    window: winit::Window,
    view: id
}

impl Window {
    pub fn poll_events(&self) -> PollEventsIterator {
        self.window.poll_events()
    }

    pub fn wait_events(&self) -> WaitEventsIterator {
        self.window.wait_events()
    }
}

pub trait WindowBuilderExt {
    fn build_metal(self, device: Device) -> Result<MetalFacade, CreationError>;
}

impl WindowBuilderExt for WindowBuilder {
    fn build_metal(self, device: Device) -> Result<MetalFacade, CreationError> {
        let window = try!(self.build());
        Ok(MetalFacade {
            window: window,
            device: device
        })
    }
}

pub struct MetalFacade {
    window: Window,
    device: Device
}

impl MetalFacade {
    #[inline]
    pub fn poll_events(&self) -> PollEventsIterator {
        self.window.poll_events()
    }

    #[inline]
    pub fn wait_events(&self) -> WaitEventsIterator {
        self.window.wait_events()
    }
}
<commit_msg>Add a way to get the winit Window from a metl::Window<commit_after>use cocoa::base::id;
use std::error::Error;
use std::fmt;
use std::ops::Deref;
use std::ptr;
use winit;
use Device;

pub use winit::{
    CursorState,
    Event,
    MonitorId,
    MouseCursor,
    PollEventsIterator,
    WaitEventsIterator,
    ElementState,
    MouseButton,
    MouseScrollDelta,
    Touch,
    TouchPhase,
    ScanCode,
    VirtualKeyCode
};

pub struct WindowBuilder {
    window: winit::WindowBuilder
}

#[derive(Debug)]
pub enum CreationError {
    NoDevice,
    Winit(winit::CreationError)
}

impl fmt::Display for CreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl Error for CreationError {
    fn description(&self) -> &str {
        match *self {
            CreationError::NoDevice => "A device was not set for the windowbuilder",
            CreationError::Winit(ref e) => e.description()
        }
    }

    fn cause(&self) -> Option<&Error> {
        if let CreationError::Winit(ref e) = *self {
            Some(e)
        } else {
            None
        }
    }
}

impl From<winit::CreationError> for CreationError {
    fn from(error: winit::CreationError) -> Self {
        CreationError::Winit(error)
    }
}

impl WindowBuilder {
    /// Initializes a new `WindowBuilder` with default values.
    #[inline]
    pub fn new() -> Self {
        WindowBuilder {
            window: winit::WindowBuilder::new()
        }
    }

    /// Requests the window to be of specific dimensions.
    ///
    /// Width and height are in pixels.
    #[inline]
    pub fn with_dimensions(self, width: u32, height: u32) -> WindowBuilder {
        WindowBuilder {window: self.window.with_dimensions(width, height), ..self}
    }

    /// Sets a minimum dimension size for the window
    ///
    /// Width and height are in pixels.
    #[inline]
    pub fn with_min_dimensions(self, width: u32, height: u32) -> WindowBuilder {
        WindowBuilder {window: self.window.with_min_dimensions(width, height), ..self}
    }

    /// Sets a maximum dimension size for the window
    ///
    /// Width and height are in pixels.
    #[inline]
    pub fn with_max_dimensions(self, width: u32, height: u32) -> WindowBuilder {
        WindowBuilder {window: self.window.with_max_dimensions(width, height), ..self}
    }

    /// Requests a specific title for the window.
    #[inline]
    pub fn with_title(self, title: String) -> WindowBuilder {
        WindowBuilder {window: self.window.with_title(title), ..self}
    }

    /// Requests fullscreen mode.
    ///
    /// If you don't specify dimensions for the window, it will match the monitor's.
    #[inline]
    pub fn with_fullscreen(self, monitor: MonitorId) -> WindowBuilder {
        WindowBuilder {window: self.window.with_fullscreen(monitor), ..self}
    }

    /// Sets whether the window will be initially hidden or visible.
    #[inline]
    pub fn with_visibility(self, visible: bool) -> WindowBuilder {
        WindowBuilder {window: self.window.with_visibility(visible), ..self}
    }

    /// Sets whether the background of the window should be transparent.
    #[inline]
    pub fn with_transparency(self, transparent: bool) -> WindowBuilder {
        WindowBuilder {window: self.window.with_transparency(transparent), ..self}
    }

    /// Sets whether the window should have a border, a title bar, etc.
    #[inline]
    pub fn with_decorations(self, decorations: bool) -> WindowBuilder {
        WindowBuilder {window: self.window.with_decorations(decorations), ..self}
    }

    /// Enables multitouch
    #[inline]
    pub fn with_multitouch(self) -> WindowBuilder {
        WindowBuilder {window: self.window.with_multitouch(), ..self}
    }

    /// Builds the window.
    ///
    /// Error should be very rare and only occur in case of permission denied, incompatible system,
    /// out of memory, etc.
    pub fn build(self) -> Result<Window, CreationError> {
        let window = try!(self.window.build());
        let native_window: id = unsafe { window.platform_window() as id };
        Ok(Window {
            window: window,
            view: ptr::null_mut()
        })
    }

    /// Builds the window.
    ///
    /// The context is build in a *strict* way. That means that if the backend couldn't give
    /// you what you requested, an `Err` will be returned.
    #[inline]
    pub fn build_strict(self) -> Result<Window, CreationError> {
        self.build()
    }
}

pub struct Window {
    window: winit::Window,
    view: id
}

pub struct WinRef<'a>(&'a winit::Window);

impl<'a> Deref for WinRef<'a> {
    type Target = winit::Window;
    fn deref(&self) -> &Self::Target {
        self.0
    }
}

impl Window {
    pub fn poll_events(&self) -> PollEventsIterator {
        self.window.poll_events()
    }

    pub fn wait_events(&self) -> WaitEventsIterator {
        self.window.wait_events()
    }

    pub fn window(&self) -> WinRef {
        WinRef(&self.window)
    }
}

pub trait WindowBuilderExt {
    fn build_metal(self, device: Device) -> Result<MetalFacade, CreationError>;
}

impl WindowBuilderExt for WindowBuilder {
    fn build_metal(self, device: Device) -> Result<MetalFacade, CreationError> {
        let window = try!(self.build());
        Ok(MetalFacade {
            window: window,
            device: device
        })
    }
}

pub struct MetalFacade {
    pub window: Window,
    pub device: Device
}

impl MetalFacade {
    #[inline]
    pub fn poll_events(&self) -> PollEventsIterator {
        self.window.poll_events()
    }

    #[inline]
    pub fn wait_events(&self) -> WaitEventsIterator {
        self.window.wait_events()
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(not(test), no_std)]

use cstr_core::CStr;
use log::{Metadata, Record};

const MAX_MSG_LEN: usize = 256;

pub struct KataLogger;

impl log::Log for KataLogger {
    fn enabled(&self, _metadata: &Metadata) -> bool {
        true
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            extern "C" {
                fn logger_log(level: u8, msg: *const cstr_core::c_char);
            }
            use bare_io::{Cursor, Write};
            let mut buf = [0 as u8; MAX_MSG_LEN];
            let mut cur = Cursor::new(&mut buf[..]);
            // Log msgs are of the form: '<'<level>'>' <target>::<fmt'd-msg>
            write!(
                &mut cur,
                "<{}> {}::{}\0",
                record.level(),
                record.target(),
                record.args()
            )
            .unwrap_or_else(|_| {
                // Too big, indicate overflow with a trailing "...".
                cur.set_position((MAX_MSG_LEN - 4) as u64);
                cur.write(b"...\0").expect("write!");
                ()
            });
            unsafe {
                // If an embedded nul is identified, replace the message; there
                // are likely better solutions but this should not happen.
                fn embedded_nul_cstr<'a>(
                    buf: &'a mut [u8; MAX_MSG_LEN],
                    record: &Record,
                ) -> &'a cstr_core::CStr {
                    let mut cur = Cursor::new(&mut buf[..]);
                    write!(
                        &mut cur,
                        "<{}> {}::<embedded nul>\0",
                        record.level(),
                        record.target()
                    )
                    .expect("nul!");
                    let pos = cur.position() as usize;
                    CStr::from_bytes_with_nul(&buf[..pos]).unwrap()
                }
                // NB: this releases the ref on buf held by the Cursor
                let pos = cur.position() as usize;
                logger_log(
                    record.level() as u8,
                    match CStr::from_bytes_with_nul(&buf[..pos]) {
                        Ok(cstr) => cstr,
                        Err(_) => embedded_nul_cstr(&mut buf, record),
                    }
                    .as_ptr(),
                );
            }
        }
    }

    fn flush(&self) {}
}

#[cfg(test)]
mod tests {
    use super::*;
    use arrayvec::ArrayVec;
    use log::{debug, error, info, trace, warn};

    static KATA_LOGGER: KataLogger = KataLogger;
    static mut MSGS: ArrayVec<[u8; MAX_MSG_LEN], 8> = ArrayVec::new_const();

    // Resets msg collection; used at the start of each test.
    fn reset_msgs() {
        unsafe {
            MSGS.clear();
        }
    }

    #[no_mangle]
    pub extern "C" fn logger_log(_level: u8, msg: *const cstr_core::c_char) {
        unsafe {
            // NB: this depends on msg pointing to the caller's array
            MSGS.push(*(msg as *const [u8; MAX_MSG_LEN]));
        }
    }

    // Formats a log message as done by KataLogger and returns a zero-padded
    // Vec holding the message.
    fn log_msg(level: log::Level, msg: &str) -> Result<Vec<u8>, std::io::Error> {
        let mut v = Vec::new();
        use std::io::Write;
        write!(&mut v, "<{}> {}::{}\0", level, "kata_logger::tests", msg)?;
        v.resize(MAX_MSG_LEN, 0);
        assert_eq!(v.len(), MAX_MSG_LEN);
        Ok(v)
    }

    fn pop_and_expect_none(level: log::Level) {
        unsafe {
            let msg = MSGS.pop();
            assert_eq!(
                msg, None,
                "Assertioon failed for level {}: expected None, got {:?}",
                level, msg
            );
        }
    }

    fn pop_and_check_result(level: log::Level, test_msg: &str) {
        unsafe {
            let msg = MSGS.pop();
            assert!(
                msg != None,
                "Assertion failed for level {}: no log msg collected",
                level
            );
            let expected = log_msg(level, test_msg).unwrap();
            let observed = msg.unwrap().to_vec();
            assert_eq!(
                expected,
                observed,
                "Assertion failed for level {}: expected {}, got {}",
                level,
                String::from_utf8_lossy(expected.as_slice()),
                String::from_utf8_lossy(observed.as_slice())
            );
        }
    }

    // NB: to run these sequentially use --test-threads=1; otherwise
    // cargo will use multiple threads and you will get failures from
    // multiple users of MSGS and the global logger; e.g.
    //     cargo +nightly test -- --test-threads=1

    #[test]
    fn test_each_log_level_works() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Trace);

        let debug_msg = "hello debug";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, debug_msg);

        let info_msg = "hello info";
        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        let warn_msg = "hello warn";
        warn!("{}", warn_msg);
        pop_and_check_result(log::Level::Warn, warn_msg);

        let error_msg = "hello error";
        error!("{}", error_msg);
        pop_and_check_result(log::Level::Error, error_msg);

        let trace_msg = "hello trace";
        trace!("{}", trace_msg);
        pop_and_check_result(log::Level::Trace, trace_msg);
    }

    #[test]
    fn test_max_log_level() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        // With filtering at Debug level, levels below should come through.
        log::set_max_level(log::LevelFilter::Debug);

        let debug_msg = "hello debug";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, debug_msg);

        let info_msg = "hello info";
        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        let warn_msg = "hello warn";
        warn!("{}", warn_msg);
        pop_and_check_result(log::Level::Warn, warn_msg);

        let error_msg = "hello error";
        error!("{}", error_msg);
        pop_and_check_result(log::Level::Error, error_msg);

        let trace_msg = "hello trace";
        trace!("{}", trace_msg);
        pop_and_expect_none(log::Level::Trace);

        // Dynamically adjust the log level
        log::set_max_level(log::LevelFilter::Info);

        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        debug!("{}", debug_msg);
        pop_and_expect_none(log::Level::Debug);
    }

    #[test]
    fn test_formatting() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        debug!("a {} b {} c {} d {:#x}", 99, "foo", 3.4, 32);
        pop_and_check_result(
            log::Level::Debug,
            &format!("a {} b {} c {} d {:#x}", 99, "foo", 3.4, 32)[..],
        );
    }

    #[test]
    fn test_too_long() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        // Guarantee formatted message is > MAX_MSG_LEN
        let mut debug_msg = "debug".repeat((MAX_MSG_LEN / 5) + 1);
        debug!("{}", debug_msg);

        // Blech, must take into account log msg formatting.
        debug_msg.truncate(MAX_MSG_LEN - 4 - "<DEBUG> kata_logger::tests::".len());
        debug_msg.push_str("...");
        pop_and_check_result(log::Level::Debug, &debug_msg[..]);
    }

    #[test]
    fn test_embedded_nul() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        let debug_msg = "bar\0foo";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, "<embedded nul>");
    }
}
<commit_msg>kata-logger: remove <level> from logged message<commit_after>#![cfg_attr(not(test), no_std)]

use cstr_core::CStr;
use log::{Metadata, Record};

const MAX_MSG_LEN: usize = 256;

pub struct KataLogger;

impl log::Log for KataLogger {
    fn enabled(&self, _metadata: &Metadata) -> bool {
        true
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            extern "C" {
                fn logger_log(level: u8, msg: *const cstr_core::c_char);
            }
            use bare_io::{Cursor, Write};
            let mut buf = [0 as u8; MAX_MSG_LEN];
            let mut cur = Cursor::new(&mut buf[..]);
            // Log msgs are of the form: '<target>::<fmt'd-msg>
            write!(&mut cur, "{}::{}\0", record.target(), record.args())
            .unwrap_or_else(|_| {
                // Too big, indicate overflow with a trailing "...".
                cur.set_position((MAX_MSG_LEN - 4) as u64);
                cur.write(b"...\0").expect("write!");
                ()
            });
            unsafe {
                // If an embedded nul is identified, replace the message; there
                // are likely better solutions but this should not happen.
                fn embedded_nul_cstr<'a>(
                    buf: &'a mut [u8; MAX_MSG_LEN],
                    record: &Record,
                ) -> &'a cstr_core::CStr {
                    let mut cur = Cursor::new(&mut buf[..]);
                    write!(&mut cur, "{}::<embedded nul>\0", record.target())
                    .expect("nul!");
                    let pos = cur.position() as usize;
                    CStr::from_bytes_with_nul(&buf[..pos]).unwrap()
                }
                // NB: this releases the ref on buf held by the Cursor
                let pos = cur.position() as usize;
                logger_log(
                    record.level() as u8,
                    match CStr::from_bytes_with_nul(&buf[..pos]) {
                        Ok(cstr) => cstr,
                        Err(_) => embedded_nul_cstr(&mut buf, record),
                    }
                    .as_ptr(),
                );
            }
        }
    }

    fn flush(&self) {}
}

#[cfg(test)]
mod tests {
    use super::*;
    use arrayvec::ArrayVec;
    use log::{debug, error, info, trace, warn};

    static KATA_LOGGER: KataLogger = KataLogger;
    static mut MSGS: ArrayVec<[u8; MAX_MSG_LEN], 8> = ArrayVec::new_const();

    // Resets msg collection; used at the start of each test.
    fn reset_msgs() {
        unsafe {
            MSGS.clear();
        }
    }

    #[no_mangle]
    pub extern "C" fn logger_log(_level: u8, msg: *const cstr_core::c_char) {
        unsafe {
            // NB: this depends on msg pointing to the caller's array
            MSGS.push(*(msg as *const [u8; MAX_MSG_LEN]));
        }
    }

    // Formats a log message as done by KataLogger and returns a zero-padded
    // Vec holding the message.
    fn log_msg(_level: log::Level, msg: &str) -> Result<Vec<u8>, std::io::Error> {
        let mut v = Vec::new();
        use std::io::Write;
        write!(&mut v, "{}::{}\0", "kata_logger::tests", msg)?;
        v.resize(MAX_MSG_LEN, 0);
        assert_eq!(v.len(), MAX_MSG_LEN);
        Ok(v)
    }

    fn pop_and_expect_none(level: log::Level) {
        unsafe {
            let msg = MSGS.pop();
            assert_eq!(
                msg, None,
                "Assertioon failed for level {}: expected None, got {:?}",
                level, msg
            );
        }
    }

    fn pop_and_check_result(level: log::Level, test_msg: &str) {
        unsafe {
            let msg = MSGS.pop();
            assert!(
                msg != None,
                "Assertion failed for level {}: no log msg collected",
                level
            );
            let expected = log_msg(level, test_msg).unwrap();
            let observed = msg.unwrap().to_vec();
            assert_eq!(
                expected,
                observed,
                "Assertion failed for level {}: expected {}, got {}",
                level,
                String::from_utf8_lossy(expected.as_slice()),
                String::from_utf8_lossy(observed.as_slice())
            );
        }
    }

    // NB: to run these sequentially use --test-threads=1; otherwise
    // cargo will use multiple threads and you will get failures from
    // multiple users of MSGS and the global logger; e.g.
    //     cargo +nightly test -- --test-threads=1

    #[test]
    fn test_each_log_level_works() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Trace);

        let debug_msg = "hello debug";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, debug_msg);

        let info_msg = "hello info";
        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        let warn_msg = "hello warn";
        warn!("{}", warn_msg);
        pop_and_check_result(log::Level::Warn, warn_msg);

        let error_msg = "hello error";
        error!("{}", error_msg);
        pop_and_check_result(log::Level::Error, error_msg);

        let trace_msg = "hello trace";
        trace!("{}", trace_msg);
        pop_and_check_result(log::Level::Trace, trace_msg);
    }

    #[test]
    fn test_max_log_level() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        // With filtering at Debug level, levels below should come through.
        log::set_max_level(log::LevelFilter::Debug);

        let debug_msg = "hello debug";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, debug_msg);

        let info_msg = "hello info";
        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        let warn_msg = "hello warn";
        warn!("{}", warn_msg);
        pop_and_check_result(log::Level::Warn, warn_msg);

        let error_msg = "hello error";
        error!("{}", error_msg);
        pop_and_check_result(log::Level::Error, error_msg);

        let trace_msg = "hello trace";
        trace!("{}", trace_msg);
        pop_and_expect_none(log::Level::Trace);

        // Dynamically adjust the log level
        log::set_max_level(log::LevelFilter::Info);

        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        debug!("{}", debug_msg);
        pop_and_expect_none(log::Level::Debug);
    }

    #[test]
    fn test_formatting() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        debug!("a {} b {} c {} d {:#x}", 99, "foo", 3.4, 32);
        pop_and_check_result(
            log::Level::Debug,
            &format!("a {} b {} c {} d {:#x}", 99, "foo", 3.4, 32)[..],
        );
    }

    #[test]
    fn test_too_long() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        // Guarantee formatted message is > MAX_MSG_LEN
        let mut debug_msg = "debug".repeat((MAX_MSG_LEN / 5) + 1);
        debug!("{}", debug_msg);

        // Blech, must take into account log msg formatting.
        debug_msg.truncate(MAX_MSG_LEN - 4 - "kata_logger::tests::".len());
        debug_msg.push_str("...");
        pop_and_check_result(log::Level::Debug, &debug_msg[..]);
    }

    #[test]
    fn test_embedded_nul() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        let debug_msg = "bar\0foo";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, "<embedded nul>");
    }
}
<|endoftext|>


<commit_before>#![crate_type = "lib"]
#![feature(macro_rules)]

#[cfg(not(test))]
pub use base::{Node, ParseState, Children, NodeContents};

// macro_escape makes macros from annotated module visible in the "super"
// module... and thus in the children of the "super" module as well.
#[macro_escape]
mod macros;

#[macro_escape]
mod base;

macro_rules! rule(
  (
    $name:ident <- $body:expr
  ) => (
    pub fn $name<'a>( parse_state: &base::ParseState<'a> )
         -> std::option::Option< base::ParseResult<'a> > {
      use base::Expression;
      use base::Node;
      use base::ParseResult;
      use std::clone::Clone;
      use std::option::{Some, None};

      #[allow(dead_code)]
      static node_name : &'static str = stringify!( $name );

      match $body.apply( parse_state ) {
        Some( result ) => {
          let state = result.parse_state.clone();
          Some( ParseResult::oneNode(
              Node::newParent( node_name, result.nodes ), state ) )
        }
        _ => None
      }
    }
  );
)

#[cfg(not(test))]
pub fn parse<'a>( input: &'a [u8] ) -> Option< Node<'a> > {
  static root_name : &'static str = "NailedRoot";
  let parse_state = ParseState { input: input, offset: 0 };
  match rules::Grammar( &parse_state ) {
    Some( result ) => Some( Node::newParent( root_name, result.nodes ) ),
    _ => None
  }
}

mod rules {
  #![no_implicit_prelude]

  use base;
  use std;

  rule!( Grammar <- seq!( ex!( Spacing ),
                          plus!( ex!( Definition ) ),
                          ex!( EndOfFile ) ) )
  rule!( Definition <- seq!( ex!( Identifier ),
                            ex!( LEFTARROW ),
                            ex!( Expression ) ) )
  rule!( Expression <- seq!( ex!( Sequence ),
                            star!( seq!( ex!( SLASH ), ex!( Sequence ) ) ) ) )
  rule!( Sequence <- star!( ex!( Prefix ) ) )
  rule!( Prefix <- seq!( opt!( or!( ex!( AND ),
                                    ex!( NOT ) ) ),
                        ex!( Suffix ) ) )
  rule!( Suffix <- seq!( ex!( Primary ),
                        opt!( or!( ex!( QUESTION ),
                                    ex!( STAR ),
                                    ex!( PLUS ) ) ) ) )
  rule!( Primary <- or!( seq!( ex!( Identifier ),
                              not!( ex!( LEFTARROW ) ) ),
                        seq!( ex!( OPEN ),
                              ex!( Expression ),
                              ex!( CLOSE ) ),
                        ex!( Literal ),
                        ex!( Class ),
                        ex!( DOT ) ) )
  rule!( Identifier <- seq!( ex!( IdentStart ),
                            star!( ex!( IdentCont ) ),
                            ex!( Spacing ) ) )
  rule!( IdentStart <- class!( "a-zA-Z_" ) )
  rule!( IdentCont <- or!( ex!( IdentStart ), class!( "0-9" ) ) )
  rule!( Literal <- or!( seq!( class!( "'" ),
                              star!( seq!( not!( class!( "'" ) ),
                                            ex!( Char ) ) ),
                              class!( "'" ),
                              ex!( Spacing ) ),
                        seq!( class!( "\"" ),
                              star!( seq!( not!( class!( "\"" ) ),
                                            ex!( Char ) ) ),
                              class!( "\"" ),
                              ex!( Spacing ) ) ) )
  rule!( Class <- seq!( lit!( "[" ),
                        star!( seq!( not!( lit!( "]" ) ), ex!( Range ) ) ),
                        lit!( "]" ),
                        ex!( Spacing ) ) )
  rule!( Range <- or!( seq!( ex!( Char ), lit!( "-" ), ex!( Char ) ),
                      ex!( Char ) ) )
  rule!( Char <- or!( seq!( lit!( r"\" ),
                            class!( r#"nrt'"[]\"# ) ),
                      seq!( lit!( r"\" ),
                            class!( "0-2" ),
                            class!( "0-7" ),
                            class!( "0-7" ) ),
                      seq!( lit!( r"\" ),
                            class!( "0-7" ),
                            opt!( class!( "0-7" ) ) ),
                      seq!( not!( lit!( r"\" ) ),
                            base::Dot ) ) )
  rule!( LEFTARROW <- seq!( lit!( "<-" ), ex!( Spacing ) ) )
  rule!( SLASH <- seq!( lit!( "/" ), ex!( Spacing ) ) )
  rule!( AND <- seq!( lit!( "&" ), ex!( Spacing ) ) )
  rule!( NOT <- seq!( lit!( "!" ), ex!( Spacing ) ) )
  rule!( QUESTION <- seq!( lit!( "?" ), ex!( Spacing ) ) )
  rule!( STAR <- seq!( lit!( "*" ), ex!( Spacing ) ) )
  rule!( PLUS <- seq!( lit!( "+" ), ex!( Spacing ) ) )
  rule!( OPEN <- seq!( lit!( "(" ), ex!( Spacing ) ) )
  rule!( CLOSE <- seq!( lit!( ")" ), ex!( Spacing ) ) )
  rule!( DOT <- seq!( lit!( "." ), ex!( Spacing ) ) )
  rule!( Spacing <- star!( or!( ex!( Space ), ex!( Comment ) ) ) )
  rule!( Comment <- seq!( lit!( "#" ),
                          star!( seq!( not!( ex!( EndOfLine ) ), base::Dot ) ),
                          ex!( EndOfLine ) ) )
  rule!( Space <- or!( lit!( " " ), lit!( "\t" ), ex!( EndOfLine ) ) )
  rule!( EndOfLine <- or!( lit!( "\r\n" ), lit!( "\n" ), lit!( "\r" ) ) )
  rule!( EndOfFile <- not!( base::Dot ) )

  #[cfg(test)]
  mod tests {
    use super::{EndOfFile, EndOfLine, Space, Comment, Spacing, Char, Range,
                Class, Literal, Identifier, Definition, Grammar};

    macro_rules! consumes(
      (
        $name:ident, $input:expr
      ) => (
        {
          use base::ParseResult;
          use std::option::Some;
          use std::container::Container;

          match $name( &input_state!( $input ) ) {
            Some( ParseResult{ nodes: _,
                              parse_state: parse_state } ) => {
              parse_state.input.is_empty()
            }
            _ => false
          }
        }
      );
    )

    macro_rules! matches(
      (
        $name:ident, $input:expr
      ) => (
        $name( &input_state!( $input ) ).is_some()
      );
    )

    #[test]
    fn Grammar_Works() {
      assert!( consumes!( Grammar,
                          r#"
        h16           <- HEXDIGIT (HEXDIGIT (HEXDIGIT HEXDIGIT?)?)?
        ls32          <- h16 ":" h16 / IPv4address
        IPv4address   <- dec_octet "." dec_octet "." dec_octet "." dec_octet"#
        ) )
    }

    #[test]
    fn Definition_Works() {
      assert!( consumes!( Definition,
                          "Grammar <- Spacing Definition+ EndOfFile" ) )
      assert!( consumes!( Definition,
                          r#"Char <- '\\' [nrt'"\[\]\\] /
                                    '\\' [0-2][0-7][0-7] /
                                    '\\' [0-7][0-7]? /
                                    !'\\' ."# ) )
    }

    #[test]
    fn Identifier_Works() {
      assert!( consumes!( Identifier, "abc" ) );
      assert!( consumes!( Identifier, "a" ) );
      assert!( consumes!( Identifier, "_" ) );
      assert!( consumes!( Identifier, "a123" ) );
      assert!( consumes!( Identifier, "a  \n" ) );

      assert!( !consumes!( Identifier, "1a" ) );
      assert!( !consumes!( Identifier, "1" ) );
      assert!( !consumes!( Identifier, "Ć" ) );
    }

    #[test]
    fn Literal_Works() {
      assert!( consumes!( Literal, "'abc'" ) );
      assert!( consumes!( Literal, r#""abc""# ) );
      assert!( consumes!( Literal, "'abc'  \n" ) );
      assert!( !consumes!( Literal, "'abc''bb'" ) );
    }

    #[test]
    fn Class_Works() {
      assert!( consumes!( Class, "[a-z]" ) );
      assert!( consumes!( Class, "[a-z]  \n" ) );
      assert!( consumes!( Class, "[abc]" ) );
      assert!( consumes!( Class, "[abc0-9g]" ) );
    }

    #[test]
    fn Range_Works() {
      assert!( consumes!( Range, "a-z" ) );
      assert!( consumes!( Range, "a" ) );
    }

    #[test]
    fn Char_Works() {
      assert!( consumes!( Char, r"\n" ) );
      assert!( consumes!( Char, r"\]" ) );
      assert!( consumes!( Char, r"\\" ) );
      assert!( consumes!( Char, r"\'" ) );
      assert!( consumes!( Char, "a" ) );
      assert!( consumes!( Char, "x" ) );
      assert!( consumes!( Char, "Ć" ) );
      assert!( consumes!( Char, "€" ) );
      assert!( consumes!( Char, r"\277" ) );
      assert!( consumes!( Char, r"\77" ) );
      assert!( consumes!( Char, r"\7" ) );

      assert!( !consumes!( Char, "aa" ) );
    }

    #[test]
    fn Spacing_Works() {
      assert!( consumes!( Spacing, "  \t #g\n" ) );
      assert!( consumes!( Spacing, "#a\n  #1\n" ) );

      // Spacing DOES match here because at the top level, it is a star
      // expression which can match consuming nothing.
      assert!( matches!( Spacing, "" ) );
      assert!( !consumes!( Spacing, "#" ) );
      assert!( !consumes!( Spacing, "a" ) );
    }

    #[test]
    fn Comment_Works() {
      assert!( consumes!( Comment, "#\n" ) );
      assert!( consumes!( Comment, "# foo! \n" ) );
      assert!( !matches!( Comment, "\n" ) );
      assert!( !matches!( Comment, "#" ) );
      assert!( !matches!( Comment, "a" ) );
    }

    #[test]
    fn Space_Works() {
      assert!( consumes!( Space, " " ) );
      assert!( consumes!( Space, "\t" ) );
      assert!( consumes!( Space, "\n" ) );
      assert!( !matches!( Space, "a" ) );
    }

    #[test]
    fn EndOfLine_Works() {
      assert!( consumes!( EndOfLine, "\n" ) );
      assert!( consumes!( EndOfLine, "\r" ) );
      assert!( consumes!( EndOfLine, "\r\n" ) );
      assert!( !matches!( EndOfLine, "a" ) );
    }

    #[test]
    fn EndOfFile_Works() {
      assert!( consumes!( EndOfFile, "" ) );
      assert!( !matches!( EndOfFile, "a" ) );
    }
  }
}
<commit_msg>Whitespace changes<commit_after>#![crate_type = "lib"]
#![feature(macro_rules)]

#[cfg(not(test))]
pub use base::{Node, ParseState, Children, NodeContents};

// macro_escape makes macros from annotated module visible in the "super"
// module... and thus in the children of the "super" module as well.
#[macro_escape]
mod macros;

#[macro_escape]
mod base;

macro_rules! rule(
  (
    $name:ident <- $body:expr
  ) => (
    pub fn $name<'a>( parse_state: &base::ParseState<'a> )
         -> std::option::Option< base::ParseResult<'a> > {
      use base::Expression;
      use base::Node;
      use base::ParseResult;
      use std::clone::Clone;
      use std::option::{Some, None};

      #[allow(dead_code)]
      static node_name : &'static str = stringify!( $name );

      match $body.apply( parse_state ) {
        Some( result ) => {
          let state = result.parse_state.clone();
          Some( ParseResult::oneNode(
              Node::newParent( node_name, result.nodes ), state ) )
        }
        _ => None
      }
    }
  );
)

#[cfg(not(test))]
pub fn parse<'a>( input: &'a [u8] ) -> Option< Node<'a> > {
  static root_name : &'static str = "NailedRoot";
  let parse_state = ParseState { input: input, offset: 0 };
  match rules::Grammar( &parse_state ) {
    Some( result ) => Some( Node::newParent( root_name, result.nodes ) ),
    _ => None
  }
}

mod rules {
  #![no_implicit_prelude]

  use base;
  use std;

  rule!( Grammar <- seq!( ex!( Spacing ),
                          plus!( ex!( Definition ) ),
                          ex!( EndOfFile ) ) )
  rule!( Definition <- seq!( ex!( Identifier ),
                             ex!( LEFTARROW ),
                             ex!( Expression ) ) )
  rule!( Expression <- seq!( ex!( Sequence ),
                             star!( seq!( ex!( SLASH ), ex!( Sequence ) ) ) ) )
  rule!( Sequence <- star!( ex!( Prefix ) ) )
  rule!( Prefix <- seq!( opt!( or!( ex!( AND ),
                                    ex!( NOT ) ) ),
                         ex!( Suffix ) ) )
  rule!( Suffix <- seq!( ex!( Primary ),
                         opt!( or!( ex!( QUESTION ),
                                    ex!( STAR ),
                                    ex!( PLUS ) ) ) ) )
  rule!( Primary <- or!( seq!( ex!( Identifier ),
                              not!( ex!( LEFTARROW ) ) ),
                         seq!( ex!( OPEN ),
                               ex!( Expression ),
                               ex!( CLOSE ) ),
                         ex!( Literal ),
                         ex!( Class ),
                         ex!( DOT ) ) )
  rule!( Identifier <- seq!( ex!( IdentStart ),
                             star!( ex!( IdentCont ) ),
                             ex!( Spacing ) ) )
  rule!( IdentStart <- class!( "a-zA-Z_" ) )
  rule!( IdentCont <- or!( ex!( IdentStart ), class!( "0-9" ) ) )
  rule!( Literal <- or!( seq!( class!( "'" ),
                               star!( seq!( not!( class!( "'" ) ),
                                            ex!( Char ) ) ),
                               class!( "'" ),
                               ex!( Spacing ) ),
                         seq!( class!( "\"" ),
                               star!( seq!( not!( class!( "\"" ) ),
                                            ex!( Char ) ) ),
                               class!( "\"" ),
                               ex!( Spacing ) ) ) )
  rule!( Class <- seq!( lit!( "[" ),
                        star!( seq!( not!( lit!( "]" ) ), ex!( Range ) ) ),
                        lit!( "]" ),
                        ex!( Spacing ) ) )
  rule!( Range <- or!( seq!( ex!( Char ), lit!( "-" ), ex!( Char ) ),
                      ex!( Char ) ) )
  rule!( Char <- or!( seq!( lit!( r"\" ),
                            class!( r#"nrt'"[]\"# ) ),
                      seq!( lit!( r"\" ),
                            class!( "0-2" ),
                            class!( "0-7" ),
                            class!( "0-7" ) ),
                      seq!( lit!( r"\" ),
                            class!( "0-7" ),
                            opt!( class!( "0-7" ) ) ),
                      seq!( not!( lit!( r"\" ) ),
                            base::Dot ) ) )
  rule!( LEFTARROW <- seq!( lit!( "<-" ), ex!( Spacing ) ) )
  rule!( SLASH <- seq!( lit!( "/" ), ex!( Spacing ) ) )
  rule!( AND <- seq!( lit!( "&" ), ex!( Spacing ) ) )
  rule!( NOT <- seq!( lit!( "!" ), ex!( Spacing ) ) )
  rule!( QUESTION <- seq!( lit!( "?" ), ex!( Spacing ) ) )
  rule!( STAR <- seq!( lit!( "*" ), ex!( Spacing ) ) )
  rule!( PLUS <- seq!( lit!( "+" ), ex!( Spacing ) ) )
  rule!( OPEN <- seq!( lit!( "(" ), ex!( Spacing ) ) )
  rule!( CLOSE <- seq!( lit!( ")" ), ex!( Spacing ) ) )
  rule!( DOT <- seq!( lit!( "." ), ex!( Spacing ) ) )
  rule!( Spacing <- star!( or!( ex!( Space ), ex!( Comment ) ) ) )
  rule!( Comment <- seq!( lit!( "#" ),
                          star!( seq!( not!( ex!( EndOfLine ) ),
                                       base::Dot ) ),
                          ex!( EndOfLine ) ) )
  rule!( Space <- or!( lit!( " " ), lit!( "\t" ), ex!( EndOfLine ) ) )
  rule!( EndOfLine <- or!( lit!( "\r\n" ), lit!( "\n" ), lit!( "\r" ) ) )
  rule!( EndOfFile <- not!( base::Dot ) )

  #[cfg(test)]
  mod tests {
    use super::{EndOfFile, EndOfLine, Space, Comment, Spacing, Char, Range,
                Class, Literal, Identifier, Definition, Grammar};

    macro_rules! consumes(
      (
        $name:ident, $input:expr
      ) => (
        {
          use base::ParseResult;
          use std::option::Some;
          use std::container::Container;

          match $name( &input_state!( $input ) ) {
            Some( ParseResult{ nodes: _,
                              parse_state: parse_state } ) => {
              parse_state.input.is_empty()
            }
            _ => false
          }
        }
      );
    )

    macro_rules! matches(
      (
        $name:ident, $input:expr
      ) => (
        $name( &input_state!( $input ) ).is_some()
      );
    )

    #[test]
    fn Grammar_Works() {
      assert!( consumes!( Grammar,
                          r#"
        h16           <- HEXDIGIT (HEXDIGIT (HEXDIGIT HEXDIGIT?)?)?
        ls32          <- h16 ":" h16 / IPv4address
        IPv4address   <- dec_octet "." dec_octet "." dec_octet "." dec_octet"#
        ) )
    }

    #[test]
    fn Definition_Works() {
      assert!( consumes!( Definition,
                          "Grammar <- Spacing Definition+ EndOfFile" ) )
      assert!( consumes!( Definition,
                          r#"Char <- '\\' [nrt'"\[\]\\] /
                                    '\\' [0-2][0-7][0-7] /
                                    '\\' [0-7][0-7]? /
                                    !'\\' ."# ) )
    }

    #[test]
    fn Identifier_Works() {
      assert!( consumes!( Identifier, "abc" ) );
      assert!( consumes!( Identifier, "a" ) );
      assert!( consumes!( Identifier, "_" ) );
      assert!( consumes!( Identifier, "a123" ) );
      assert!( consumes!( Identifier, "a  \n" ) );

      assert!( !consumes!( Identifier, "1a" ) );
      assert!( !consumes!( Identifier, "1" ) );
      assert!( !consumes!( Identifier, "Ć" ) );
    }

    #[test]
    fn Literal_Works() {
      assert!( consumes!( Literal, "'abc'" ) );
      assert!( consumes!( Literal, r#""abc""# ) );
      assert!( consumes!( Literal, "'abc'  \n" ) );
      assert!( !consumes!( Literal, "'abc''bb'" ) );
    }

    #[test]
    fn Class_Works() {
      assert!( consumes!( Class, "[a-z]" ) );
      assert!( consumes!( Class, "[a-z]  \n" ) );
      assert!( consumes!( Class, "[abc]" ) );
      assert!( consumes!( Class, "[abc0-9g]" ) );
    }

    #[test]
    fn Range_Works() {
      assert!( consumes!( Range, "a-z" ) );
      assert!( consumes!( Range, "a" ) );
    }

    #[test]
    fn Char_Works() {
      assert!( consumes!( Char, r"\n" ) );
      assert!( consumes!( Char, r"\]" ) );
      assert!( consumes!( Char, r"\\" ) );
      assert!( consumes!( Char, r"\'" ) );
      assert!( consumes!( Char, "a" ) );
      assert!( consumes!( Char, "x" ) );
      assert!( consumes!( Char, "Ć" ) );
      assert!( consumes!( Char, "€" ) );
      assert!( consumes!( Char, r"\277" ) );
      assert!( consumes!( Char, r"\77" ) );
      assert!( consumes!( Char, r"\7" ) );

      assert!( !consumes!( Char, "aa" ) );
    }

    #[test]
    fn Spacing_Works() {
      assert!( consumes!( Spacing, "  \t #g\n" ) );
      assert!( consumes!( Spacing, "#a\n  #1\n" ) );

      // Spacing DOES match here because at the top level, it is a star
      // expression which can match consuming nothing.
      assert!( matches!( Spacing, "" ) );
      assert!( !consumes!( Spacing, "#" ) );
      assert!( !consumes!( Spacing, "a" ) );
    }

    #[test]
    fn Comment_Works() {
      assert!( consumes!( Comment, "#\n" ) );
      assert!( consumes!( Comment, "# foo! \n" ) );
      assert!( !matches!( Comment, "\n" ) );
      assert!( !matches!( Comment, "#" ) );
      assert!( !matches!( Comment, "a" ) );
    }

    #[test]
    fn Space_Works() {
      assert!( consumes!( Space, " " ) );
      assert!( consumes!( Space, "\t" ) );
      assert!( consumes!( Space, "\n" ) );
      assert!( !matches!( Space, "a" ) );
    }

    #[test]
    fn EndOfLine_Works() {
      assert!( consumes!( EndOfLine, "\n" ) );
      assert!( consumes!( EndOfLine, "\r" ) );
      assert!( consumes!( EndOfLine, "\r\n" ) );
      assert!( !matches!( EndOfLine, "a" ) );
    }

    #[test]
    fn EndOfFile_Works() {
      assert!( consumes!( EndOfFile, "" ) );
      assert!( !matches!( EndOfFile, "a" ) );
    }
  }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use aptos_logger::{error, info};
use aptos_types::transaction::Version;
use schemadb::SchemaBatch;
use std::{cmp::min, thread::sleep, time::Duration};

/// Defines the trait for pruner for different DB
pub trait DBPruner {
    /// Find out the first undeleted item in the stale node index.
    ///
    /// Seeking from the beginning (version 0) is potentially costly, we do it once upon worker
    /// thread start, record the progress and seek from that position afterwards.
    fn initialize(&self) {
        loop {
            match self.initialize_least_readable_version() {
                Ok(least_readable_version) => {
                    info!(
                        least_readable_version = least_readable_version,
                        "{} initialized.",
                        self.name()
                    );
                    self.record_progress(least_readable_version);
                    return;
                }
                Err(e) => {
                    error!(
                        error = ?e,
                        "{} Error on first seek. Retrying in 1 second.", self.name()
                    );
                    sleep(Duration::from_secs(1));
                }
            }
        }
    }
    fn name(&self) -> &'static str;

    /// Performs the actual pruning, a target version is passed, which is the target the pruner
    /// tries to prune.
    fn prune(&self, db_batch: &mut SchemaBatch, max_versions: u64) -> anyhow::Result<Version>;

    /// Initializes the least readable version stored in underlying DB storage
    fn initialize_least_readable_version(&self) -> anyhow::Result<Version>;

    /// Returns the least readable version stores in the DB pruner
    fn least_readable_version(&self) -> Version;

    /// Sets the target version for the pruner
    fn set_target_version(&self, target_version: Version);

    /// Returns the target version for the DB pruner
    fn target_version(&self) -> Version;

    /// Returns the target version for the current pruning round - this might be different from the
    /// target_version() because we need to keep max_version in account.
    fn get_currrent_batch_target(&self, max_versions: Version) -> Version {
        // Current target version  might be less than the target version to ensure we don't prune
        // more than max_version in one go.
        min(
            self.least_readable_version() + max_versions as u64,
            self.target_version(),
        )
    }
    /// Records the current progress of the pruner by updating the least readable version
    fn record_progress(&self, least_readable_version: Version);

    /// True if there is pruning work pending to be done
    fn is_pruning_pending(&self) -> bool {
        self.target_version() >= self.least_readable_version()
    }
}
<commit_msg>[Storage][Pruner] Fix pruning pending check<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use aptos_logger::{error, info};
use aptos_types::transaction::Version;
use schemadb::SchemaBatch;
use std::{cmp::min, thread::sleep, time::Duration};

/// Defines the trait for pruner for different DB
pub trait DBPruner {
    /// Find out the first undeleted item in the stale node index.
    ///
    /// Seeking from the beginning (version 0) is potentially costly, we do it once upon worker
    /// thread start, record the progress and seek from that position afterwards.
    fn initialize(&self) {
        loop {
            match self.initialize_least_readable_version() {
                Ok(least_readable_version) => {
                    info!(
                        least_readable_version = least_readable_version,
                        "{} initialized.",
                        self.name()
                    );
                    self.record_progress(least_readable_version);
                    return;
                }
                Err(e) => {
                    error!(
                        error = ?e,
                        "{} Error on first seek. Retrying in 1 second.", self.name()
                    );
                    sleep(Duration::from_secs(1));
                }
            }
        }
    }
    fn name(&self) -> &'static str;

    /// Performs the actual pruning, a target version is passed, which is the target the pruner
    /// tries to prune.
    fn prune(&self, db_batch: &mut SchemaBatch, max_versions: u64) -> anyhow::Result<Version>;

    /// Initializes the least readable version stored in underlying DB storage
    fn initialize_least_readable_version(&self) -> anyhow::Result<Version>;

    /// Returns the least readable version stores in the DB pruner
    fn least_readable_version(&self) -> Version;

    /// Sets the target version for the pruner
    fn set_target_version(&self, target_version: Version);

    /// Returns the target version for the DB pruner
    fn target_version(&self) -> Version;

    /// Returns the target version for the current pruning round - this might be different from the
    /// target_version() because we need to keep max_version in account.
    fn get_currrent_batch_target(&self, max_versions: Version) -> Version {
        // Current target version  might be less than the target version to ensure we don't prune
        // more than max_version in one go.
        min(
            self.least_readable_version() + max_versions as u64,
            self.target_version(),
        )
    }
    /// Records the current progress of the pruner by updating the least readable version
    fn record_progress(&self, least_readable_version: Version);

    /// True if there is pruning work pending to be done
    fn is_pruning_pending(&self) -> bool {
        self.target_version() > self.least_readable_version()
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// Prevent warning when casting u32 as i64
#![allow(clippy::cast_lossless)]
#![allow(dead_code)]

use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use libc::{self, CLOCK_MONOTONIC};

use super::{Error, GpioState, Result};

// Only call sleep_ns() if we have enough time remaining
const SLEEP_THRESHOLD: i64 = 250_000;
// Reserve some time for busy waiting
const BUSYWAIT_MAX: i64 = 200_000;
// Subtract from the remaining busy wait time to account for get_time_ns() overhead
const BUSYWAIT_REMAINDER: i64 = 100;

static mut MSG_WAITING: AtomicBool = ATOMIC_BOOL_INIT;

#[derive(Debug)]
enum Msg {
    Reconfigure(Duration, Duration),
    Stop,
}

#[derive(Debug)]
pub(crate) struct SoftPwm {
    pwm_thread: Option<thread::JoinHandle<Result<()>>>,
    sender: Sender<Msg>,
}

impl SoftPwm {
    pub(crate) fn new(
        pin: u8,
        gpio_state: Arc<GpioState>,
        period: Duration,
        pulse_width: Duration,
    ) -> SoftPwm {
        let (sender, receiver): (Sender<Msg>, Receiver<Msg>) = mpsc::channel();

        let pwm_thread = thread::spawn(move || -> Result<()> {
            // Set the scheduling policy to real-time round robin at the highest priority. This
            // will silently fail if we're not running as root.
            let params = libc::sched_param {
                sched_priority: unsafe { libc::sched_get_priority_max(libc::SCHED_RR) },
            };

            unsafe {
                libc::sched_setscheduler(0, libc::SCHED_RR, &params);
            }

            // Set timer slack to 1 ns (default = 50 µs). This is only relevant if we're unable
            // to set a real-time scheduling policy.
            unsafe {
                libc::prctl(libc::PR_SET_TIMERSLACK, 1);
            }

            let mut period_ns =
                (period.as_secs() as i64 * 1_000_000_000) + period.subsec_nanos() as i64;

            let mut pulse_width_ns =
                (pulse_width.as_secs() as i64 * 1_000_000_000) + pulse_width.subsec_nanos() as i64;

            let mut start_ns = get_time_ns();

            loop {
                // PWM active
                if pulse_width_ns > 0 {
                    gpio_state.gpio_mem.set_high(pin);
                }

                // Sleep if we have enough time remaining, while reserving some time
                // for busy waiting to compensate for sleep taking longer than needed.
                if pulse_width_ns >= SLEEP_THRESHOLD {
                    sleep_ns(pulse_width_ns - BUSYWAIT_MAX);
                }

                // Busy-wait for the remaining active time, minus BUSYWAIT_REMAINDER
                // to account for get_time_ns() overhead
                loop {
                    if (pulse_width_ns - (get_time_ns() - start_ns)) <= BUSYWAIT_REMAINDER {
                        break;
                    }
                }

                // PWM inactive
                gpio_state.gpio_mem.set_low(pin);

                // This is slightly faster than directly checking try_recv()
                if unsafe { MSG_WAITING.compare_and_swap(true, false, Ordering::AcqRel) } {
                    while let Ok(msg) = receiver.try_recv() {
                        match msg {
                            Msg::Reconfigure(period, pulse_width) => {
                                // Reconfigure period and pulse width
                                pulse_width_ns = (pulse_width.as_secs() as i64 * 1_000_000_000)
                                    + pulse_width.subsec_nanos() as i64;

                                period_ns = (period.as_secs() as i64 * 1_000_000_000)
                                    + period.subsec_nanos() as i64;

                                if pulse_width_ns > period_ns {
                                    pulse_width_ns = period_ns;
                                }
                            }
                            Msg::Stop => {
                                // The main thread asked us to stop
                                return Ok(());
                            }
                        }
                    }
                }

                let remaining_ns = period_ns - (get_time_ns() - start_ns);

                // Sleep if we have enough time remaining, while reserving some time
                // for busy waiting to compensate for sleep taking longer than needed.
                if remaining_ns >= SLEEP_THRESHOLD {
                    sleep_ns(remaining_ns - BUSYWAIT_MAX);
                }

                // Busy-wait for the remaining inactive time, minus BUSYWAIT_REMAINDER
                // to account for get_time_ns() overhead
                loop {
                    let current_ns = get_time_ns();
                    if (period_ns - (current_ns - start_ns)) <= BUSYWAIT_REMAINDER {
                        start_ns = current_ns;
                        break;
                    }
                }
            }
        });

        SoftPwm {
            pwm_thread: Some(pwm_thread),
            sender,
        }
    }

    pub(crate) fn reconfigure(&mut self, period: Duration, pulse_width: Duration) {
        let _ = self.sender.send(Msg::Reconfigure(period, pulse_width));
        unsafe {
            MSG_WAITING.store(true, Ordering::Release);
        }
    }

    pub(crate) fn stop(&mut self) -> Result<()> {
        let _ = self.sender.send(Msg::Stop);
        unsafe {
            MSG_WAITING.store(true, Ordering::Release);
        }

        if let Some(pwm_thread) = self.pwm_thread.take() {
            match pwm_thread.join() {
                Ok(r) => return r,
                Err(_) => return Err(Error::ThreadPanic),
            }
        }

        Ok(())
    }
}

impl Drop for SoftPwm {
    fn drop(&mut self) {
        // Don't wait for the pwm thread to exit if the main thread is panicking,
        // because we could potentially block indefinitely while unwinding if the
        // pwm thread doesn't respond to the Stop message for some reason.
        if !thread::panicking() {
            let _ = self.stop();
        }
    }
}

#[inline(always)]
fn get_time_ns() -> i64 {
    let mut ts = libc::timespec {
        tv_sec: 0,
        tv_nsec: 0,
    };

    unsafe {
        libc::clock_gettime(CLOCK_MONOTONIC, &mut ts);
    }

    (ts.tv_sec as i64 * 1_000_000_000) + ts.tv_nsec as i64
}

#[inline(always)]
fn sleep_ns(ns: i64) {
    let ts = libc::timespec {
        tv_sec: (ns / 1_000_000_000) as i32,
        tv_nsec: (ns % 1_000_000_000) as i32,
    };

    unsafe {
        libc::clock_nanosleep(libc::CLOCK_MONOTONIC, 0, &ts, ptr::null_mut());
    }
}
<commit_msg>Add additional sched_param fields for musl libc<commit_after>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// Prevent warning when casting u32 as i64
#![allow(clippy::cast_lossless)]
#![allow(dead_code)]

use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use libc::{self, CLOCK_MONOTONIC};

use super::{Error, GpioState, Result};

// Only call sleep_ns() if we have enough time remaining
const SLEEP_THRESHOLD: i64 = 250_000;
// Reserve some time for busy waiting
const BUSYWAIT_MAX: i64 = 200_000;
// Subtract from the remaining busy wait time to account for get_time_ns() overhead
const BUSYWAIT_REMAINDER: i64 = 100;

static mut MSG_WAITING: AtomicBool = ATOMIC_BOOL_INIT;

#[derive(Debug)]
enum Msg {
    Reconfigure(Duration, Duration),
    Stop,
}

#[derive(Debug)]
pub(crate) struct SoftPwm {
    pwm_thread: Option<thread::JoinHandle<Result<()>>>,
    sender: Sender<Msg>,
}

impl SoftPwm {
    pub(crate) fn new(
        pin: u8,
        gpio_state: Arc<GpioState>,
        period: Duration,
        pulse_width: Duration,
    ) -> SoftPwm {
        let (sender, receiver): (Sender<Msg>, Receiver<Msg>) = mpsc::channel();

        let pwm_thread = thread::spawn(move || -> Result<()> {
            // Set the scheduling policy to real-time round robin at the highest priority. This
            // will silently fail if we're not running as root.
            #[cfg(target_env = "gnu")]
            let params = libc::sched_param {
                sched_priority: unsafe { libc::sched_get_priority_max(libc::SCHED_RR) },
            };

            #[cfg(target_env = "musl")]
            let params = libc::sched_param {
                sched_priority: unsafe { libc::sched_get_priority_max(libc::SCHED_RR) },
                sched_ss_low_priority: 0,
                sched_ss_repl_period: libc::timespec {
                    tv_sec: 0,
                    tv_nsec: 0,
                },
                sched_ss_init_budget: libc::timespec {
                    tv_sec: 0,
                    tv_nsec: 0,
                },
                sched_ss_max_repl: 0,
            };

            unsafe {
                libc::sched_setscheduler(0, libc::SCHED_RR, &params);
            }

            // Set timer slack to 1 ns (default = 50 µs). This is only relevant if we're unable
            // to set a real-time scheduling policy.
            unsafe {
                libc::prctl(libc::PR_SET_TIMERSLACK, 1);
            }

            let mut period_ns =
                (period.as_secs() as i64 * 1_000_000_000) + period.subsec_nanos() as i64;

            let mut pulse_width_ns =
                (pulse_width.as_secs() as i64 * 1_000_000_000) + pulse_width.subsec_nanos() as i64;

            let mut start_ns = get_time_ns();

            loop {
                // PWM active
                if pulse_width_ns > 0 {
                    gpio_state.gpio_mem.set_high(pin);
                }

                // Sleep if we have enough time remaining, while reserving some time
                // for busy waiting to compensate for sleep taking longer than needed.
                if pulse_width_ns >= SLEEP_THRESHOLD {
                    sleep_ns(pulse_width_ns - BUSYWAIT_MAX);
                }

                // Busy-wait for the remaining active time, minus BUSYWAIT_REMAINDER
                // to account for get_time_ns() overhead
                loop {
                    if (pulse_width_ns - (get_time_ns() - start_ns)) <= BUSYWAIT_REMAINDER {
                        break;
                    }
                }

                // PWM inactive
                gpio_state.gpio_mem.set_low(pin);

                // This is slightly faster than directly checking try_recv()
                if unsafe { MSG_WAITING.compare_and_swap(true, false, Ordering::AcqRel) } {
                    while let Ok(msg) = receiver.try_recv() {
                        match msg {
                            Msg::Reconfigure(period, pulse_width) => {
                                // Reconfigure period and pulse width
                                pulse_width_ns = (pulse_width.as_secs() as i64 * 1_000_000_000)
                                    + pulse_width.subsec_nanos() as i64;

                                period_ns = (period.as_secs() as i64 * 1_000_000_000)
                                    + period.subsec_nanos() as i64;

                                if pulse_width_ns > period_ns {
                                    pulse_width_ns = period_ns;
                                }
                            }
                            Msg::Stop => {
                                // The main thread asked us to stop
                                return Ok(());
                            }
                        }
                    }
                }

                let remaining_ns = period_ns - (get_time_ns() - start_ns);

                // Sleep if we have enough time remaining, while reserving some time
                // for busy waiting to compensate for sleep taking longer than needed.
                if remaining_ns >= SLEEP_THRESHOLD {
                    sleep_ns(remaining_ns - BUSYWAIT_MAX);
                }

                // Busy-wait for the remaining inactive time, minus BUSYWAIT_REMAINDER
                // to account for get_time_ns() overhead
                loop {
                    let current_ns = get_time_ns();
                    if (period_ns - (current_ns - start_ns)) <= BUSYWAIT_REMAINDER {
                        start_ns = current_ns;
                        break;
                    }
                }
            }
        });

        SoftPwm {
            pwm_thread: Some(pwm_thread),
            sender,
        }
    }

    pub(crate) fn reconfigure(&mut self, period: Duration, pulse_width: Duration) {
        let _ = self.sender.send(Msg::Reconfigure(period, pulse_width));
        unsafe {
            MSG_WAITING.store(true, Ordering::Release);
        }
    }

    pub(crate) fn stop(&mut self) -> Result<()> {
        let _ = self.sender.send(Msg::Stop);
        unsafe {
            MSG_WAITING.store(true, Ordering::Release);
        }

        if let Some(pwm_thread) = self.pwm_thread.take() {
            match pwm_thread.join() {
                Ok(r) => return r,
                Err(_) => return Err(Error::ThreadPanic),
            }
        }

        Ok(())
    }
}

impl Drop for SoftPwm {
    fn drop(&mut self) {
        // Don't wait for the pwm thread to exit if the main thread is panicking,
        // because we could potentially block indefinitely while unwinding if the
        // pwm thread doesn't respond to the Stop message for some reason.
        if !thread::panicking() {
            let _ = self.stop();
        }
    }
}

#[inline(always)]
fn get_time_ns() -> i64 {
    let mut ts = libc::timespec {
        tv_sec: 0,
        tv_nsec: 0,
    };

    unsafe {
        libc::clock_gettime(CLOCK_MONOTONIC, &mut ts);
    }

    (ts.tv_sec as i64 * 1_000_000_000) + ts.tv_nsec as i64
}

#[inline(always)]
fn sleep_ns(ns: i64) {
    let ts = libc::timespec {
        tv_sec: (ns / 1_000_000_000) as i32,
        tv_nsec: (ns % 1_000_000_000) as i32,
    };

    unsafe {
        libc::clock_nanosleep(libc::CLOCK_MONOTONIC, 0, &ts, ptr::null_mut());
    }
}
<|endoftext|>