<commit_before>use rules::re::Regex;

#[test]
fn whitespace_ignored() {
    let re = Regex::new(r"pine
                          apple");
    assert!( re.is_match("I bought a pineapple."));

    let re = Regex::new(r"gold fish");
    assert!( re.is_match("Do you have a goldfish?"));
}
#[test]
fn chars() {
    let re = Regex::new(r"Apple");
    assert!( re.is_match("I have an Apple. Do you?"));
    assert!(!re.is_match("I have an Pear. Do you?"));
}
#[test]
fn whitespace_escapes() {
    let re = Regex::new(r"ping\ pong");
    assert!( re.is_match("Do you like ping pong? I do."));
    assert!(!re.is_match("Do you like golf? I do."));
}
#[test]
fn literals_ticks() {
    let re = Regex::new(r" 'two apples' ");
    assert!( re.is_match("Are there two apples? Really?"));
    assert!(!re.is_match("Are there two grapes? Really?"));
}
#[test]
fn literals_quotes() {
    let re = Regex::new(r#" "two apples" "#);
    assert!( re.is_match("Are there two apples? Really?"));
    assert!(!re.is_match("Are there two grapes? Really?"));
}
#[test]
fn literals_regular() {
    let re = Regex::new(r#" '2' and "3" and "4" and '5' "#);
    assert!( re.is_match("Well, 2and3and4and5 there."));
    assert!(!re.is_match("Well, 5and4and3and2 there."));
}
#[test]
fn char_class_range() {
    let re = Regex::new(r"<[ a .. z ]>");
    assert!( re.is_match("a"));
    assert!( re.is_match("p"));
    assert!( re.is_match("z"));
    assert!(!re.is_match("0"));
    assert!(!re.is_match("9"));
    assert!(!re.is_match("こ"));
}
#[test]
fn char_class_multi() {
    let re = Regex::new(r"<[ 0 .. 9 ]> <[ a .. z ]>");
    assert!( re.is_match("0z"));
    assert!( re.is_match("3b"));
    assert!( re.is_match("9h"));
    assert!(!re.is_match("a3"));
    assert!(!re.is_match("3A"));
    assert!(!re.is_match("こ"));

    let re = Regex::new(r"<[ 0 .. 9 ]> ' dogs'");
    assert!( re.is_match("0 dogs"));
    assert!( re.is_match("4 dogz and 3 dogs"));
    assert!( re.is_match("9 dogs more"));
    assert!( re.is_match("4 cats and 7 dogs and 2 pigs"));

    assert!(!re.is_match("五 dogs")); // Japanese number 5
    assert!(!re.is_match("b dogs"));
    assert!(!re.is_match("A dogs"));
    assert!(!re.is_match("0 dogz"));
    assert!(!re.is_match("4 cats and 3 dogz"));
    assert!(!re.is_match("9 dogz and 3 cats"));
    assert!(!re.is_match("4 cats and 7 dogz and 2 pigs"));
}
#[test]
fn comments_are_ignored() {
    let before = Regex::new(r"# This is a comment
                              pineapple");
    let between = Regex::new(r"pine  # This is a comment
                               apple");
    let after = Regex::new(r"pineapple # This is a comment");
    let after_both = Regex::new(r"pine # This is a comment
                                  apple # This is second comment");

    assert!(before    .is_match("pineapple"));
    assert!(between   .is_match("pineapple"));
    assert!(after     .is_match("pineapple"));
    assert!(after_both.is_match("pineapple"));
}

<commit_msg>Add tests and comments for char class match fixes<commit_after>use rules::re::Regex;

#[test]
fn whitespace_ignored() {
    let re = Regex::new(r"pine
                          apple");
    assert!( re.is_match("I bought a pineapple."));

    let re = Regex::new(r"gold fish");
    assert!( re.is_match("Do you have a goldfish?"));
}
#[test]
fn chars() {
    let re = Regex::new(r"Apple");
    assert!( re.is_match("I have an Apple. Do you?"));
    assert!(!re.is_match("I have an Pear. Do you?"));
}
#[test]
fn whitespace_escapes() {
    let re = Regex::new(r"ping\ pong");
    assert!( re.is_match("Do you like ping pong? I do."));
    assert!(!re.is_match("Do you like golf? I do."));
}
#[test]
fn literals_ticks() {
    let re = Regex::new(r" 'two apples' ");
    assert!( re.is_match("Are there two apples? Really?"));
    assert!(!re.is_match("Are there two grapes? Really?"));
}
#[test]
fn literals_quotes() {
    let re = Regex::new(r#" "two apples" "#);
    assert!( re.is_match("Are there two apples? Really?"));
    assert!(!re.is_match("Are there two grapes? Really?"));
}
#[test]
fn literals_regular() {
    let re = Regex::new(r#" '2' and "3" and "4" and '5' "#);
    assert!( re.is_match("Well, 2and3and4and5 there."));
    assert!(!re.is_match("Well, 5and4and3and2 there."));
}
#[test]
fn char_class_range() {
    let re = Regex::new(r"<[ a .. z ]>");
    assert!( re.is_match("a"));
    assert!( re.is_match("p"));
    assert!( re.is_match("z"));
    assert!(!re.is_match("0"));
    assert!(!re.is_match("9"));
    assert!(!re.is_match("こ"));
}
#[test]
fn char_class_multi() {
    let re = Regex::new(r"<[ 0 .. 9 ]> <[ a .. z ]>");
    assert!( re.is_match("0z"));
    assert!( re.is_match("3b"));
    assert!( re.is_match("9h"));

    // Doesn't match with a gap.
    assert!(!re.is_match("0_z"));
    assert!(!re.is_match("3_b"));
    assert!(!re.is_match("9_h"));

    // Must be in the correct order.
    assert!(!re.is_match("a3"));

    // Must be the correct case.
    assert!(!re.is_match("3A"));

    // Combines with other operators properly.
    let re = Regex::new(r"<[ 0 .. 9 ]> ' dogs'");
    assert!( re.is_match("0 dogs"));
    assert!( re.is_match("4 dogz and 3 dogs"));
    assert!( re.is_match("9 dogs more"));
    assert!( re.is_match("4 cats and 7 dogs and 2 pigs"));

    // Doesn't hit partial matches.
    assert!(!re.is_match("五 dogs")); // Japanese number 5
    assert!(!re.is_match("b dogs"));
    assert!(!re.is_match("A dogs"));
    assert!(!re.is_match("0 dogz"));
    assert!(!re.is_match("4 cats and 3 dogz"));
    assert!(!re.is_match("9 dogz and 3 cats"));
    assert!(!re.is_match("4 cats and 7 dogz and 2 pigs"));
}
#[test]
fn comments_are_ignored() {
    let before = Regex::new(r"# This is a comment
                              pineapple");
    let between = Regex::new(r"pine  # This is a comment
                               apple");
    let after = Regex::new(r"pineapple # This is a comment");
    let after_both = Regex::new(r"pine # This is a comment
                                  apple # This is second comment");

    assert!(before    .is_match("pineapple"));
    assert!(between   .is_match("pineapple"));
    assert!(after     .is_match("pineapple"));
    assert!(after_both.is_match("pineapple"));
}

<|endoftext|>


<commit_before>use postgres::types::array::{DimensionInfo, ArrayBase, Array, MutableArray};

#[test]
fn test_from_vec() {
    let a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
    assert!([DimensionInfo { len: 3, lower_bound: -1 }] ==
            a.dimension_info());
    assert_eq!(&0, a.get(-1));
    assert_eq!(&1, a.get(0));
    assert_eq!(&2, a.get(1));
}

#[test]
#[should_fail]
fn test_get_2d_fail() {
    let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
    a.wrap(1);
    a.get(1);
}

#[test]
#[should_fail]
fn test_2d_slice_range_fail_low() {
    let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
    a.wrap(1);
    a.slice(0);
}

#[test]
#[should_fail]
fn test_2d_slice_range_fail_high() {
    let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
    a.wrap(1);
    a.slice(2);
}

#[test]
fn test_2d_slice_get() {
    let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
    a.wrap(1);
    let s = a.slice(1);
    assert_eq!(&0, s.get(-1));
    assert_eq!(&1, s.get(0));
    assert_eq!(&2, s.get(1));
}

#[test]
#[should_fail]
fn test_push_move_wrong_lower_bound() {
    let mut a = ArrayBase::from_vec(vec!(1i), -1);
    a.push_move(ArrayBase::from_vec(vec!(2), 0));
}

#[test]
#[should_fail]
fn test_push_move_wrong_dims() {
    let mut a = ArrayBase::from_vec(vec!(1i), -1);
    a.wrap(1);
    a.push_move(ArrayBase::from_vec(vec!(1, 2), -1));
}

#[test]
#[should_fail]
fn test_push_move_wrong_dim_count() {
    let mut a = ArrayBase::from_vec(vec!(1i), -1);
    a.wrap(1);
    let mut b = ArrayBase::from_vec(vec!(2), -1);
    b.wrap(1);
    a.push_move(b);
}

#[test]
fn test_push_move_ok() {
    let mut a = ArrayBase::from_vec(vec!(1i, 2), 0);
    a.wrap(0);
    a.push_move(ArrayBase::from_vec(vec!(3, 4), 0));
    let s = a.slice(0);
    assert_eq!(&1, s.get(0));
    assert_eq!(&2, s.get(1));
    let s = a.slice(1);
    assert_eq!(&3, s.get(0));
    assert_eq!(&4, s.get(1));
}

#[test]
fn test_3d() {
    let mut a = ArrayBase::from_vec(vec!(0i, 1), 0);
    a.wrap(0);
    a.push_move(ArrayBase::from_vec(vec!(2, 3), 0));
    a.wrap(0);
    let mut b = ArrayBase::from_vec(vec!(4, 5), 0);
    b.wrap(0);
    b.push_move(ArrayBase::from_vec(vec!(6, 7), 0));
    a.push_move(b);
    let s1 = a.slice(0);
    let s2 = s1.slice(0);
    assert_eq!(&0, s2.get(0));
    assert_eq!(&1, s2.get(1));
    let s2 = s1.slice(1);
    assert_eq!(&2, s2.get(0));
    assert_eq!(&3, s2.get(1));
    let s1 = a.slice(1);
    let s2 = s1.slice(0);
    assert_eq!(&4, s2.get(0));
    assert_eq!(&5, s2.get(1));
    let s2 = s1.slice(1);
    assert_eq!(&6, s2.get(0));
    assert_eq!(&7, s2.get(1));
}

#[test]
fn test_mut() {
    let mut a = ArrayBase::from_vec(vec!(1i, 2), 0);
    a.wrap(0);
    {
        let mut s = a.slice_mut(0);
        *s.get_mut(0) = 3;
    }
    let s = a.slice(0);
    assert_eq!(&3, s.get(0));
}

#[test]
#[should_fail]
fn test_base_overslice() {
    let a = ArrayBase::from_vec(vec!(1i), 0);
    a.slice(0);
}

#[test]
#[should_fail]
fn test_slice_overslice() {
    let mut a = ArrayBase::from_vec(vec!(1i), 0);
    a.wrap(0);
    let s = a.slice(0);
    s.slice(0);
}
<commit_msg>Fix test<commit_after>use postgres::types::array::{DimensionInfo, ArrayBase, Array, MutableArray};

#[test]
fn test_from_vec() {
    let a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
    assert!([DimensionInfo { len: 3, lower_bound: -1 }][] ==
            a.dimension_info());
    assert_eq!(&0, a.get(-1));
    assert_eq!(&1, a.get(0));
    assert_eq!(&2, a.get(1));
}

#[test]
#[should_fail]
fn test_get_2d_fail() {
    let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
    a.wrap(1);
    a.get(1);
}

#[test]
#[should_fail]
fn test_2d_slice_range_fail_low() {
    let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
    a.wrap(1);
    a.slice(0);
}

#[test]
#[should_fail]
fn test_2d_slice_range_fail_high() {
    let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
    a.wrap(1);
    a.slice(2);
}

#[test]
fn test_2d_slice_get() {
    let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
    a.wrap(1);
    let s = a.slice(1);
    assert_eq!(&0, s.get(-1));
    assert_eq!(&1, s.get(0));
    assert_eq!(&2, s.get(1));
}

#[test]
#[should_fail]
fn test_push_move_wrong_lower_bound() {
    let mut a = ArrayBase::from_vec(vec!(1i), -1);
    a.push_move(ArrayBase::from_vec(vec!(2), 0));
}

#[test]
#[should_fail]
fn test_push_move_wrong_dims() {
    let mut a = ArrayBase::from_vec(vec!(1i), -1);
    a.wrap(1);
    a.push_move(ArrayBase::from_vec(vec!(1, 2), -1));
}

#[test]
#[should_fail]
fn test_push_move_wrong_dim_count() {
    let mut a = ArrayBase::from_vec(vec!(1i), -1);
    a.wrap(1);
    let mut b = ArrayBase::from_vec(vec!(2), -1);
    b.wrap(1);
    a.push_move(b);
}

#[test]
fn test_push_move_ok() {
    let mut a = ArrayBase::from_vec(vec!(1i, 2), 0);
    a.wrap(0);
    a.push_move(ArrayBase::from_vec(vec!(3, 4), 0));
    let s = a.slice(0);
    assert_eq!(&1, s.get(0));
    assert_eq!(&2, s.get(1));
    let s = a.slice(1);
    assert_eq!(&3, s.get(0));
    assert_eq!(&4, s.get(1));
}

#[test]
fn test_3d() {
    let mut a = ArrayBase::from_vec(vec!(0i, 1), 0);
    a.wrap(0);
    a.push_move(ArrayBase::from_vec(vec!(2, 3), 0));
    a.wrap(0);
    let mut b = ArrayBase::from_vec(vec!(4, 5), 0);
    b.wrap(0);
    b.push_move(ArrayBase::from_vec(vec!(6, 7), 0));
    a.push_move(b);
    let s1 = a.slice(0);
    let s2 = s1.slice(0);
    assert_eq!(&0, s2.get(0));
    assert_eq!(&1, s2.get(1));
    let s2 = s1.slice(1);
    assert_eq!(&2, s2.get(0));
    assert_eq!(&3, s2.get(1));
    let s1 = a.slice(1);
    let s2 = s1.slice(0);
    assert_eq!(&4, s2.get(0));
    assert_eq!(&5, s2.get(1));
    let s2 = s1.slice(1);
    assert_eq!(&6, s2.get(0));
    assert_eq!(&7, s2.get(1));
}

#[test]
fn test_mut() {
    let mut a = ArrayBase::from_vec(vec!(1i, 2), 0);
    a.wrap(0);
    {
        let mut s = a.slice_mut(0);
        *s.get_mut(0) = 3;
    }
    let s = a.slice(0);
    assert_eq!(&3, s.get(0));
}

#[test]
#[should_fail]
fn test_base_overslice() {
    let a = ArrayBase::from_vec(vec!(1i), 0);
    a.slice(0);
}

#[test]
#[should_fail]
fn test_slice_overslice() {
    let mut a = ArrayBase::from_vec(vec!(1i), 0);
    a.wrap(0);
    let s = a.slice(0);
    s.slice(0);
}
<|endoftext|>


<commit_before>use gemlab::mesh::Mesh;
use gemlab::StrError;

fn main() -> Result<(), StrError> {
    // 1.0  5------,6.------7
    //      | [3],'   `.[4] |
    //      |  ,'       `.  |
    //      |,'           `.|
    // 0.5  3      [2]      4
    //      |`.           .'|
    //      |  `.       .'  |
    //      | [0]`.   .'[1] |
    // 0.0  0------`1'------2
    //     0.0     0.5     1.0
    let mesh = Mesh::from_text(
        r"# header
          # space_ndim npoint ncell
                     2      8     5

          # points
          # id    x   y
             0  0.0 0.0
             1  0.5 0.0
             2  1.0 0.0
             3  0.0 0.5
             4  1.0 0.5
             5  0.0 1.0
             6  0.5 1.0
             7  1.0 1.0

          # cells
          # id att geo_ndim nnode  point_ids...
             0   1        2     3  0 1 3
             1   1        2     3  1 2 4
             2   1        2     4  1 4 6 3
             3   1        2     3  5 3 6
             4   1        2     3  7 6 4
         ",
    )?;
    println!("{}", mesh);
    assert_eq!(mesh.points.len(), 8);
    assert_eq!(mesh.cells.len(), 5);
    assert_eq!(mesh.cells[0].points.len(), 3);
    assert_eq!(mesh.cells[2].points.len(), 4);
    Ok(())
}
<commit_msg>Fix example<commit_after>use gemlab::mesh::Mesh;
use gemlab::StrError;

fn main() -> Result<(), StrError> {
    // 1.0  5------,6.------7
    //      | [3],'   `.[4] |
    //      |  ,'       `.  |
    //      |,'           `.|
    // 0.5  3      [2]      4
    //      |`.           .'|
    //      |  `.       .'  |
    //      | [0]`.   .'[1] |
    // 0.0  0------`1'------2
    //     0.0     0.5     1.0
    let mesh = Mesh::from_text(
        r"# header
          # ndim npoint ncell
               2      8     5

          # points
          # id    x   y
             0  0.0 0.0
             1  0.5 0.0
             2  1.0 0.0
             3  0.0 0.5
             4  1.0 0.5
             5  0.0 1.0
             6  0.5 1.0
             7  1.0 1.0

          # cells
          # id att kind  point_ids...
             0   1 tri3  0 1 3
             1   1 tri3  1 2 4
             2   1 qua4  1 4 6 3
             3   1 tri3  5 3 6
             4   1 tri3  7 6 4
         ",
    )?;
    println!("{}", mesh);
    assert_eq!(mesh.points.len(), 8);
    assert_eq!(mesh.cells.len(), 5);
    assert_eq!(mesh.cells[0].points.len(), 3);
    assert_eq!(mesh.cells[2].points.len(), 4);
    Ok(())
}
<|endoftext|>


<commit_before>use helpers::{HelperDef};
use registry::{Registry};
use context::{Context, JsonRender};
use render::{Renderable, RenderContext, RenderError, Helper};

#[derive(Clone, Copy)]
pub struct LookupHelper;

impl HelperDef for LookupHelper {
    fn call(&self, c: &Context, h: &Helper, _: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let value_param = try!(h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"lookup\"")));
        let index_param = try!(h.param(1).ok_or_else(|| RenderError::new("Insuffitient params for helper \"lookup\"")));

        let partial_path: String = if index_param.starts_with("@") {
            rc.get_local_var(index_param).render()
        } else {
            index_param.to_owned()
        };
        let lookup_path = format!("{}.[{}]", value_param, partial_path);
        let value = c.navigate(rc.get_path(), &lookup_path);
        let r = value.render();
        try!(rc.writer.write(r.into_bytes().as_ref()));
        Ok(())
    }
}

pub static LOOKUP_HELPER: LookupHelper = LookupHelper;

#[cfg(test)]
mod test {
    use template::{Template};
    use registry::{Registry};

    use std::collections::BTreeMap;

    #[test]
    fn test_lookup() {
        let t0 = Template::compile("{{#each v1}}{{lookup ../../v2 @index}}{{/each}}".to_string()).ok().unwrap();
        let t1 = Template::compile("{{#each v1}}{{lookup ../../v2 1}}{{/each}}".to_string()).ok().unwrap();
        let t2 = Template::compile("{{lookup kk \"a\"}}".to_string()).ok().unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let mut m :BTreeMap<String, Vec<u16>> = BTreeMap::new();
        m.insert("v1".to_string(), vec![1u16, 2u16, 3u16]);
        m.insert("v2".to_string(), vec![9u16, 8u16, 7u16]);

        let m2 = btreemap!{
            "kk".to_string() => btreemap!{"a".to_string() => "world".to_string()}
        };

        let r0 = handlebars.render("t0", &m);
        assert_eq!(r0.ok().unwrap(), "987".to_string());

        let r1 = handlebars.render("t1", &m);
        assert_eq!(r1.ok().unwrap(), "888".to_string());

        let r2 = handlebars.render("t2", &m2);
        assert_eq!(r2.ok().unwrap(), "world".to_string());
    }
}
<commit_msg>(fix) remove unused imports<commit_after>use helpers::HelperDef;
use registry::Registry;
use context::{Context, JsonRender};
use render::{RenderContext, RenderError, Helper};

#[derive(Clone, Copy)]
pub struct LookupHelper;

impl HelperDef for LookupHelper {
    fn call(&self,
            c: &Context,
            h: &Helper,
            _: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let value_param = try!(h.param(0).ok_or_else(|| {
            RenderError::new("Param not found for helper \"lookup\"")
        }));
        let index_param = try!(h.param(1).ok_or_else(|| {
            RenderError::new("Insuffitient params for helper \"lookup\"")
        }));

        let partial_path: String = if index_param.starts_with("@") {
            rc.get_local_var(index_param).render()
        } else {
            index_param.to_owned()
        };
        let lookup_path = format!("{}.[{}]", value_param, partial_path);
        let value = c.navigate(rc.get_path(), &lookup_path);
        let r = value.render();
        try!(rc.writer.write(r.into_bytes().as_ref()));
        Ok(())
    }
}

pub static LOOKUP_HELPER: LookupHelper = LookupHelper;

#[cfg(test)]
mod test {
    use template::Template;
    use registry::Registry;

    use std::collections::BTreeMap;

    #[test]
    fn test_lookup() {
        let t0 = Template::compile("{{#each v1}}{{lookup ../../v2 @index}}{{/each}}".to_string())
                     .ok()
                     .unwrap();
        let t1 = Template::compile("{{#each v1}}{{lookup ../../v2 1}}{{/each}}".to_string())
                     .ok()
                     .unwrap();
        let t2 = Template::compile("{{lookup kk \"a\"}}".to_string()).ok().unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let mut m: BTreeMap<String, Vec<u16>> = BTreeMap::new();
        m.insert("v1".to_string(), vec![1u16, 2u16, 3u16]);
        m.insert("v2".to_string(), vec![9u16, 8u16, 7u16]);

        let m2 = btreemap!{
            "kk".to_string() => btreemap!{"a".to_string() => "world".to_string()}
        };

        let r0 = handlebars.render("t0", &m);
        assert_eq!(r0.ok().unwrap(), "987".to_string());

        let r1 = handlebars.render("t1", &m);
        assert_eq!(r1.ok().unwrap(), "888".to_string());

        let r2 = handlebars.render("t2", &m2);
        assert_eq!(r2.ok().unwrap(), "world".to_string());
    }
}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/lib/vec.rs
//! Dynamically growable vector type
use core::prelude::*;
use core::iter::{FromIterator,IntoIterator};
use core::ops;
use lib::collections::{MutableSeq};
use core::ptr::Unique;

/// Growable array of items
pub struct Vec<T>
{
	data: Unique<T>,
	size: usize,
	capacity: usize,
}

/// Owning iterator
pub struct MoveItems<T>
{
	data: Unique<T>,
	count: usize,
	ofs: usize,
}

impl<T> Vec<T>
{
	//pub static EMPTY: Vec<T> = Vec { data: Unique(::memory::heap::ZERO_ALLOC), size: 0, capacity: 0 };

	/// Create a new, empty vector
	pub fn new() -> Vec<T>
	{
		Vec::with_capacity(0)
	}
	/// Create a vector with an initialised capacity
	pub fn with_capacity(size: usize) -> Vec<T>
	{
		Vec {
			data: unsafe { Unique::new( ::memory::heap::alloc_array::<T>( size ) ) },
			size: 0,
			capacity: size,
		}
	}
	/// Populate vector using a provided callback
	pub fn from_fn<Fcn>(length: usize, op: Fcn) -> Vec<T>
	where
		Fcn: Fn(usize) -> T
	{
		let mut ret = Vec::with_capacity(length);
		for i in (0 .. length) {
			ret.push( op(i) );
		}
		ret
	}

	/// Obtain a mutable pointer to an item within the vector
	fn get_mut_ptr(&mut self, index: usize) -> *mut T
	{
		if index >= self.size {
			panic!("Index out of range, {} >= {}", index, self.size);
		}
		unsafe { self.data.offset(index as isize) }
	}

	/// Move contents into an iterator (consuming self)
	pub fn into_iter(mut self) -> MoveItems<T>
	{
		let dataptr = ::core::mem::replace(&mut self.data, unsafe { Unique::new(0 as *mut _) } );
		let count = self.size;
		unsafe { ::core::mem::forget(self) };
		MoveItems {
			data: dataptr,
			ofs: 0,
			count: count,
		}
	}

	pub fn clear(&mut self)
	{

	}

	fn reserve(&mut self, size: usize)
	{
		let newcap = ::lib::num::round_up(size, 1 << (64-size.leading_zeros()));
		if newcap > self.capacity
		{
			unsafe {
				let newptr = ::memory::heap::alloc_array::<T>( newcap );
				for i in (0 .. self.size) {
					let val = self.move_ent(i as usize);
					::core::ptr::write(newptr.offset(i as isize), val);
				}
				if self.capacity > 0 {
					::memory::heap::dealloc_array( *self.data, self.capacity );
				}
				self.data = Unique::new(newptr);
				self.capacity = newcap;
			}
		}
	}

	pub fn slice_mut<'a>(&'a mut self) -> &'a mut [T]
	{
		unsafe { ::core::mem::transmute( ::core::raw::Slice { data: *self.data as *const T, len: self.size } ) }
	}

	/// Move out of a slot in the vector, leaving unitialise memory in its place
	unsafe fn move_ent(&mut self, pos: usize) -> T
	{
		::core::ptr::read(self.data.offset(pos as isize) as *const _)
	}

	/// Insert an item at the specified index (moving subsequent items up)
	pub fn insert(&mut self, pos: usize, value: T)
	{
		// Expand by one element
		let ns = self.size + 1;
		self.reserve(ns);
		self.size = ns;
		unsafe
		{
			// Move elements (pos .. len) to (pos+1 .. len+1)
			for i in (pos .. self.size).rev()
			{
				let src = self.data.offset( (i) as isize);
				let dst = self.data.offset( (i+1) as isize);
				::core::ptr::write(dst, ::core::ptr::read(src));
			}
			// Store new element
			::core::ptr::write( self.data.offset(pos as isize), value );
		}
	}

	pub fn truncate(&mut self, newsize: usize)
	{
		if newsize >= self.size
		{
			unsafe
			{
				for i in (newsize .. self.size) {
					::core::mem::drop( ::core::ptr::read(self.get_mut_ptr(i) as *const T) );
				}
				self.size = newsize;
			}
		}
	}
}

impl<T> ::core::default::Default for Vec<T>
{
	fn default() -> Vec<T> { Vec::new() }
}

impl<T> ops::Deref for Vec<T>
{
	type Target = [T];
	fn deref(&self) -> &[T] {
		self.as_slice()
	}
}
impl<T> ops::DerefMut for Vec<T>
{
	fn deref_mut(&mut self) -> &mut [T] {
		self.slice_mut()
	}
}

impl<T:Clone> Vec<T>
{
	pub fn from_elem(size: usize, elem: T) -> Vec<T>
	{
		let mut ret = Vec::with_capacity(size);
		for _ in 0 .. size-1 {
			ret.push(elem.clone());
		}
		ret.push(elem);
		ret
	}

	pub fn push_all(&mut self, other: &[T])
	{
		let newlen = self.size + other.len();
		self.reserve(newlen);
		for v in other.iter() {
			self.push(v.clone());
		}
	}
}

impl<T> ops::Drop for Vec<T>
{
	fn drop(&mut self)
	{
		log_debug!("Vec::drop() - Dropping vector at {:p} w/ {} ents", *self.data, self.size);
		unsafe {
			for i in (0 .. self.size) {
				::core::mem::drop( ::core::ptr::read(self.get_mut_ptr(i) as *const T) );
			}
			::memory::heap::dealloc_array( *self.data, self.capacity );
		}
	}
}

macro_rules! vec_index {
	($T:ident -> $rv:ty : $($idx:ty)*) => { $(
		impl<$T> ops::Index<$idx> for Vec<$T>
		{
			type Output = $rv;
			fn index<'a>(&'a self, index: $idx) -> &'a $rv
			{
				&self.as_slice()[index]
			}
		}
		impl<$T> ops::IndexMut<$idx> for Vec<$T>
		{
			fn index_mut<'a>(&'a mut self, index: $idx) -> &'a mut $rv
			{
				&mut self.slice_mut()[index]
			}
		}
		)* }
	}
vec_index!{ T -> T : usize }
vec_index!{ T -> [T] : ops::Range<usize> ops::RangeTo<usize> ops::RangeFrom<usize> ops::RangeFull }

impl<T> ::core::slice::AsSlice<T> for Vec<T>
{
	fn as_slice<'a>(&'a self) -> &'a [T]
	{
		let rawslice = ::core::raw::Slice { data: *self.data as *const T, len: self.size };
		unsafe { ::core::mem::transmute( rawslice ) }
	}
}

//impl<T: ::core::fmt::Show> ::core::fmt::Show for Vec<T>
//{
//	fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::result::Result<(),::core::fmt::Error>
//	{
//		write!(f, "{}", self.as_slice())
//	}
//}

impl<T> MutableSeq<T> for Vec<T>
{
	fn push(&mut self, t: T)
	{
		let pos = self.size;
		self.reserve(pos + 1);
		self.size += 1;
		let ptr = self.get_mut_ptr(pos);
		//log_debug!("Vec.push {}", HexDump(&t));
		unsafe { ::core::ptr::write(ptr, t); }
	}
	fn pop(&mut self) -> Option<T>
	{
		if self.size == 0
		{
			None
		}
		else
		{
			self.size -= 1;
			let pos = self.size;
			Some( unsafe { self.move_ent(pos) } )
		}
	}
}

impl<T> FromIterator<T> for Vec<T>
{
	fn from_iter<IT>(src: IT) -> Vec<T>
	where
		IT: ::core::iter::IntoIterator<Item=T>
	{
		let iterator = src.into_iter();
		let mut ret = Vec::new();
		if let (_, Some(size)) = iterator.size_hint()
		{
			ret.reserve(size);
		}
		for val in iterator
		{
			ret.push(val);
		}
		ret
	}
}

impl<'a, T> IntoIterator for &'a Vec<T>
{
	type IntoIter = ::core::slice::Iter<'a,T>;
	type Item = &'a T;

	fn into_iter(self) -> ::core::slice::Iter<'a, T> {
		self.iter()
	}
}

impl<'a, T> IntoIterator for &'a mut Vec<T>
{
	type IntoIter = ::core::slice::IterMut<'a,T>;
	type Item = &'a mut T;

	fn into_iter(self) -> ::core::slice::IterMut<'a, T> {
		self.iter_mut()
	}
}

impl<T> MoveItems<T>
{
	/// Pop an item from the iterator
	fn pop_item(&mut self) -> T
	{
		//log_debug!("MoveItems.pop_item() ofs={}, count={}, data={}", self.ofs, self.count, self.data);
		assert!(self.ofs < self.count);
		let v: T = unsafe {
			let ptr = self.data.offset(self.ofs as isize);
			::core::ptr::read(ptr as *const _)
			};
		//log_debug!("MoveItems.pop_item() v = {}", HexDump(&v));
		self.ofs += 1;
		v
	}
}

impl<T> Iterator for MoveItems<T>
{
	type Item = T;
	fn next(&mut self) -> Option<T>
	{
		if self.ofs == self.count
		{
			None
		}
		else
		{
			Some( self.pop_item() )
		}
	}
}

impl<T> ops::Drop for MoveItems<T>
{
	fn drop(&mut self)
	{
		for _ in (self.ofs .. self.count) {
			self.pop_item();
		}
		unsafe {
			::memory::heap::dealloc_array( *self.data, self.count );
		}
	}
}

// vim: ft=rust
<commit_msg>(minor) Add type to Vec::drop log<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/lib/vec.rs
//! Dynamically growable vector type
use core::prelude::*;
use core::iter::{FromIterator,IntoIterator};
use core::ops;
use lib::collections::{MutableSeq};
use core::ptr::Unique;

/// Growable array of items
pub struct Vec<T>
{
	data: Unique<T>,
	size: usize,
	capacity: usize,
}

/// Owning iterator
pub struct MoveItems<T>
{
	data: Unique<T>,
	count: usize,
	ofs: usize,
}

impl<T> Vec<T>
{
	//pub static EMPTY: Vec<T> = Vec { data: Unique(::memory::heap::ZERO_ALLOC), size: 0, capacity: 0 };

	/// Create a new, empty vector
	pub fn new() -> Vec<T>
	{
		Vec::with_capacity(0)
	}
	/// Create a vector with an initialised capacity
	pub fn with_capacity(size: usize) -> Vec<T>
	{
		Vec {
			data: unsafe { Unique::new( ::memory::heap::alloc_array::<T>( size ) ) },
			size: 0,
			capacity: size,
		}
	}
	/// Populate vector using a provided callback
	pub fn from_fn<Fcn>(length: usize, op: Fcn) -> Vec<T>
	where
		Fcn: Fn(usize) -> T
	{
		let mut ret = Vec::with_capacity(length);
		for i in (0 .. length) {
			ret.push( op(i) );
		}
		ret
	}

	/// Obtain a mutable pointer to an item within the vector
	fn get_mut_ptr(&mut self, index: usize) -> *mut T
	{
		if index >= self.size {
			panic!("Index out of range, {} >= {}", index, self.size);
		}
		unsafe { self.data.offset(index as isize) }
	}

	/// Move contents into an iterator (consuming self)
	pub fn into_iter(mut self) -> MoveItems<T>
	{
		let dataptr = ::core::mem::replace(&mut self.data, unsafe { Unique::new(0 as *mut _) } );
		let count = self.size;
		unsafe { ::core::mem::forget(self) };
		MoveItems {
			data: dataptr,
			ofs: 0,
			count: count,
		}
	}

	pub fn clear(&mut self)
	{

	}

	fn reserve(&mut self, size: usize)
	{
		let newcap = ::lib::num::round_up(size, 1 << (64-size.leading_zeros()));
		if newcap > self.capacity
		{
			unsafe {
				let newptr = ::memory::heap::alloc_array::<T>( newcap );
				for i in (0 .. self.size) {
					let val = self.move_ent(i as usize);
					::core::ptr::write(newptr.offset(i as isize), val);
				}
				if self.capacity > 0 {
					::memory::heap::dealloc_array( *self.data, self.capacity );
				}
				self.data = Unique::new(newptr);
				self.capacity = newcap;
			}
		}
	}

	pub fn slice_mut<'a>(&'a mut self) -> &'a mut [T]
	{
		unsafe { ::core::mem::transmute( ::core::raw::Slice { data: *self.data as *const T, len: self.size } ) }
	}

	/// Move out of a slot in the vector, leaving unitialise memory in its place
	unsafe fn move_ent(&mut self, pos: usize) -> T
	{
		::core::ptr::read(self.data.offset(pos as isize) as *const _)
	}

	/// Insert an item at the specified index (moving subsequent items up)
	pub fn insert(&mut self, pos: usize, value: T)
	{
		// Expand by one element
		let ns = self.size + 1;
		self.reserve(ns);
		self.size = ns;
		unsafe
		{
			// Move elements (pos .. len) to (pos+1 .. len+1)
			for i in (pos .. self.size).rev()
			{
				let src = self.data.offset( (i) as isize);
				let dst = self.data.offset( (i+1) as isize);
				::core::ptr::write(dst, ::core::ptr::read(src));
			}
			// Store new element
			::core::ptr::write( self.data.offset(pos as isize), value );
		}
	}

	pub fn truncate(&mut self, newsize: usize)
	{
		if newsize >= self.size
		{
			unsafe
			{
				for i in (newsize .. self.size) {
					::core::mem::drop( ::core::ptr::read(self.get_mut_ptr(i) as *const T) );
				}
				self.size = newsize;
			}
		}
	}
}

impl<T> ::core::default::Default for Vec<T>
{
	fn default() -> Vec<T> { Vec::new() }
}

impl<T> ops::Deref for Vec<T>
{
	type Target = [T];
	fn deref(&self) -> &[T] {
		self.as_slice()
	}
}
impl<T> ops::DerefMut for Vec<T>
{
	fn deref_mut(&mut self) -> &mut [T] {
		self.slice_mut()
	}
}

impl<T:Clone> Vec<T>
{
	pub fn from_elem(size: usize, elem: T) -> Vec<T>
	{
		let mut ret = Vec::with_capacity(size);
		for _ in 0 .. size-1 {
			ret.push(elem.clone());
		}
		ret.push(elem);
		ret
	}

	pub fn push_all(&mut self, other: &[T])
	{
		let newlen = self.size + other.len();
		self.reserve(newlen);
		for v in other.iter() {
			self.push(v.clone());
		}
	}
}

impl<T> ops::Drop for Vec<T>
{
	fn drop(&mut self)
	{
		log_debug!("Vec::<{}>::drop() - {:p} w/ {} ents", type_name!(T), *self.data, self.size);
		unsafe {
			for i in (0 .. self.size) {
				::core::mem::drop( ::core::ptr::read(self.get_mut_ptr(i) as *const T) );
			}
			::memory::heap::dealloc_array( *self.data, self.capacity );
		}
	}
}

macro_rules! vec_index {
	($T:ident -> $rv:ty : $($idx:ty)*) => { $(
		impl<$T> ops::Index<$idx> for Vec<$T>
		{
			type Output = $rv;
			fn index<'a>(&'a self, index: $idx) -> &'a $rv
			{
				&self.as_slice()[index]
			}
		}
		impl<$T> ops::IndexMut<$idx> for Vec<$T>
		{
			fn index_mut<'a>(&'a mut self, index: $idx) -> &'a mut $rv
			{
				&mut self.slice_mut()[index]
			}
		}
		)* }
	}
vec_index!{ T -> T : usize }
vec_index!{ T -> [T] : ops::Range<usize> ops::RangeTo<usize> ops::RangeFrom<usize> ops::RangeFull }

impl<T> ::core::slice::AsSlice<T> for Vec<T>
{
	fn as_slice<'a>(&'a self) -> &'a [T]
	{
		let rawslice = ::core::raw::Slice { data: *self.data as *const T, len: self.size };
		unsafe { ::core::mem::transmute( rawslice ) }
	}
}

//impl<T: ::core::fmt::Show> ::core::fmt::Show for Vec<T>
//{
//	fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::result::Result<(),::core::fmt::Error>
//	{
//		write!(f, "{}", self.as_slice())
//	}
//}

impl<T> MutableSeq<T> for Vec<T>
{
	fn push(&mut self, t: T)
	{
		let pos = self.size;
		self.reserve(pos + 1);
		self.size += 1;
		let ptr = self.get_mut_ptr(pos);
		//log_debug!("Vec.push {}", HexDump(&t));
		unsafe { ::core::ptr::write(ptr, t); }
	}
	fn pop(&mut self) -> Option<T>
	{
		if self.size == 0
		{
			None
		}
		else
		{
			self.size -= 1;
			let pos = self.size;
			Some( unsafe { self.move_ent(pos) } )
		}
	}
}

impl<T> FromIterator<T> for Vec<T>
{
	fn from_iter<IT>(src: IT) -> Vec<T>
	where
		IT: ::core::iter::IntoIterator<Item=T>
	{
		let iterator = src.into_iter();
		let mut ret = Vec::new();
		if let (_, Some(size)) = iterator.size_hint()
		{
			ret.reserve(size);
		}
		for val in iterator
		{
			ret.push(val);
		}
		ret
	}
}

impl<'a, T> IntoIterator for &'a Vec<T>
{
	type IntoIter = ::core::slice::Iter<'a,T>;
	type Item = &'a T;

	fn into_iter(self) -> ::core::slice::Iter<'a, T> {
		self.iter()
	}
}

impl<'a, T> IntoIterator for &'a mut Vec<T>
{
	type IntoIter = ::core::slice::IterMut<'a,T>;
	type Item = &'a mut T;

	fn into_iter(self) -> ::core::slice::IterMut<'a, T> {
		self.iter_mut()
	}
}

impl<T> MoveItems<T>
{
	/// Pop an item from the iterator
	fn pop_item(&mut self) -> T
	{
		//log_debug!("MoveItems.pop_item() ofs={}, count={}, data={}", self.ofs, self.count, self.data);
		assert!(self.ofs < self.count);
		let v: T = unsafe {
			let ptr = self.data.offset(self.ofs as isize);
			::core::ptr::read(ptr as *const _)
			};
		//log_debug!("MoveItems.pop_item() v = {}", HexDump(&v));
		self.ofs += 1;
		v
	}
}

impl<T> Iterator for MoveItems<T>
{
	type Item = T;
	fn next(&mut self) -> Option<T>
	{
		if self.ofs == self.count
		{
			None
		}
		else
		{
			Some( self.pop_item() )
		}
	}
}

impl<T> ops::Drop for MoveItems<T>
{
	fn drop(&mut self)
	{
		for _ in (self.ofs .. self.count) {
			self.pop_item();
		}
		unsafe {
			::memory::heap::dealloc_array( *self.data, self.count );
		}
	}
}

// vim: ft=rust
<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use super::{
    actor::{self, SharedState},
    Probe, Result, State, StateChangeImpl, StateMachine,
};
use chrono::Utc;
use slog_scope::{debug, info};

#[derive(Debug, PartialEq)]
pub(super) struct Poll {}

create_state_step!(Poll => Probe);

/// Implements the state change for `State<Poll>`.
///
/// This state is used to control when to go to the `State<Probe>`.
#[async_trait::async_trait(?Send)]
impl StateChangeImpl for State<Poll> {
    fn name(&self) -> &'static str {
        "poll"
    }

    async fn handle(
        self,
        shared_state: &mut SharedState,
    ) -> Result<(StateMachine, actor::StepTransition)> {
        crate::logger::start_memory_logging();

        let interval = shared_state.settings.polling.interval;
        let delay = interval
            - Utc::now().signed_duration_since(shared_state.runtime_settings.last_polling());

        if delay > interval || delay.num_seconds() < 0 {
            info!("forcing to Probe state as we are in time");
            return Ok((StateMachine::Probe(self.into()), actor::StepTransition::Immediate));
        }

        debug!("moving to Probe state after delay.");
        Ok((
            StateMachine::Probe(self.into()),
            actor::StepTransition::Delayed(delay.to_std().unwrap()),
        ))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        firmware::{
            tests::{create_fake_metadata, FakeDevice},
            Metadata,
        },
        runtime_settings::RuntimeSettings,
        settings::Settings,
    };
    use chrono::{Duration, Utc};

    #[actix_rt::test]
    async fn normal_delay() {
        let settings = Settings::default();
        let runtime_settings = RuntimeSettings::default();
        let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
        let mut shared_state = SharedState { settings, runtime_settings, firmware };
        shared_state.runtime_settings.polling.last = Utc::now() - Duration::minutes(10);

        let (machine, trans) =
            StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state).await.unwrap();

        assert_state!(machine, Probe);
        match trans {
            actor::StepTransition::Delayed(d)
                if d <= shared_state.settings.polling.interval.to_std().unwrap() => {}
            _ => panic!("Unexpected StepTransition: {:?}", trans),
        }
    }

    #[actix_rt::test]
    async fn update_in_time() {
        let settings = Settings::default();
        let runtime_settings = RuntimeSettings::default();
        let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
        let mut shared_state = SharedState { settings, runtime_settings, firmware };

        let (machine, trans) =
            StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state).await.unwrap();

        assert_state!(machine, Probe);
        match trans {
            actor::StepTransition::Immediate => {}
            _ => panic!("Unexpected StepTransition: {:?}", trans),
        }
    }

    #[actix_rt::test]
    async fn least_probe_in_the_future() {
        let settings = Settings::default();
        let runtime_settings = RuntimeSettings::default();
        let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
        let mut shared_state = SharedState { settings, runtime_settings, firmware };
        shared_state.runtime_settings.polling.last = Utc::now() + Duration::days(1);

        let (machine, trans) =
            StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state).await.unwrap();

        assert_state!(machine, Probe);
        match trans {
            actor::StepTransition::Immediate => {}
            _ => panic!("Unexpected StepTransition: {:?}", trans),
        }
    }
}
<commit_msg>updatehub: Make Poll state preentive<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use super::{
    actor::{self, SharedState},
    Probe, Result, State, StateChangeImpl, StateMachine,
};
use chrono::Utc;
use slog_scope::{debug, info};

#[derive(Debug, PartialEq)]
pub(super) struct Poll {}

create_state_step!(Poll => Probe);

/// Implements the state change for `State<Poll>`.
///
/// This state is used to control when to go to the `State<Probe>`.
#[async_trait::async_trait(?Send)]
impl StateChangeImpl for State<Poll> {
    fn name(&self) -> &'static str {
        "poll"
    }

    fn is_preemptive_state(&self) -> bool {
        true
    }

    async fn handle(
        self,
        shared_state: &mut SharedState,
    ) -> Result<(StateMachine, actor::StepTransition)> {
        crate::logger::start_memory_logging();

        let interval = shared_state.settings.polling.interval;
        let delay = interval
            - Utc::now().signed_duration_since(shared_state.runtime_settings.last_polling());

        if delay > interval || delay.num_seconds() < 0 {
            info!("forcing to Probe state as we are in time");
            return Ok((StateMachine::Probe(self.into()), actor::StepTransition::Immediate));
        }

        debug!("moving to Probe state after delay.");
        Ok((
            StateMachine::Probe(self.into()),
            actor::StepTransition::Delayed(delay.to_std().unwrap()),
        ))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        firmware::{
            tests::{create_fake_metadata, FakeDevice},
            Metadata,
        },
        runtime_settings::RuntimeSettings,
        settings::Settings,
    };
    use chrono::{Duration, Utc};

    #[actix_rt::test]
    async fn normal_delay() {
        let settings = Settings::default();
        let runtime_settings = RuntimeSettings::default();
        let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
        let mut shared_state = SharedState { settings, runtime_settings, firmware };
        shared_state.runtime_settings.polling.last = Utc::now() - Duration::minutes(10);

        let (machine, trans) =
            StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state).await.unwrap();

        assert_state!(machine, Probe);
        match trans {
            actor::StepTransition::Delayed(d)
                if d <= shared_state.settings.polling.interval.to_std().unwrap() => {}
            _ => panic!("Unexpected StepTransition: {:?}", trans),
        }
    }

    #[actix_rt::test]
    async fn update_in_time() {
        let settings = Settings::default();
        let runtime_settings = RuntimeSettings::default();
        let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
        let mut shared_state = SharedState { settings, runtime_settings, firmware };

        let (machine, trans) =
            StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state).await.unwrap();

        assert_state!(machine, Probe);
        match trans {
            actor::StepTransition::Immediate => {}
            _ => panic!("Unexpected StepTransition: {:?}", trans),
        }
    }

    #[actix_rt::test]
    async fn least_probe_in_the_future() {
        let settings = Settings::default();
        let runtime_settings = RuntimeSettings::default();
        let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
        let mut shared_state = SharedState { settings, runtime_settings, firmware };
        shared_state.runtime_settings.polling.last = Utc::now() + Duration::days(1);

        let (machine, trans) =
            StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state).await.unwrap();

        assert_state!(machine, Probe);
        match trans {
            actor::StepTransition::Immediate => {}
            _ => panic!("Unexpected StepTransition: {:?}", trans),
        }
    }
}
<|endoftext|>


<commit_before>use super::{
    flow_control::{ExportAction, LocalAction}, status::*, Shell,
};
use itoa;
use parser::assignments::*;
use smallvec::SmallVec;
use shell::{
    history::ShellHistory,
    variables::VariableType
};
use std::{
    collections::HashMap,
    env, ffi::OsStr, fmt::{self, Display}, io::{self, BufWriter, Write}, mem,
    os::unix::ffi::OsStrExt, str, simd,
};

fn list_vars(shell: &Shell) {
    let stdout = io::stdout();
    let mut buffer = BufWriter::new(stdout.lock());

    // Small function for formatting and append an array entry to a string buffer.
    fn print_array<W: Write>(buffer: &mut W, key: &str, array: &[String]) {
        let _ = buffer.write([key, " = [ "].concat().as_bytes());
        if array.len() > 1 {
            let mut vars = array.iter();
            if let Some(ref var) = vars.next() {
                let _ = buffer.write(["'", var, "', "].concat().as_bytes());
                vars.for_each(|var| {
                    let _ = buffer.write(["'", var, "' "].concat().as_bytes());
                });
            }
            let _ = buffer.write(b"]\n");
        } else {
            let _ = buffer.write(["'", &array[0], "' ]\n"].concat().as_bytes());
        }
    }

    // Write all the string variables to the buffer.
    let _ = buffer.write(b"# String Variables\n");
    for (key, val) in shell.variables.variables() {
        let _ = buffer.write([key, " = ", val.as_str(), "\n"].concat().as_bytes());
    }

    // Then immediately follow that with a list of array variables.
    let _ = buffer.write(b"\n# Array Variables\n");
    for (key, val) in shell.variables.arrays() {
        print_array(&mut buffer, &key, &val)
    }
}

/// Represents: A variable store capable of setting local variables or
/// exporting variables to some global environment
pub(crate) trait VariableStore {
    /// Set a local variable given a binding
    fn local(&mut self, LocalAction) -> i32;
    /// Export a variable to the process environment given a binding
    fn export(&mut self, ExportAction) -> i32;
}

impl VariableStore for Shell {
    fn export(&mut self, action: ExportAction) -> i32 {
        let actions = match action {
            ExportAction::Assign(ref keys, op, ref vals) => AssignmentActions::new(keys, op, vals),
            ExportAction::LocalExport(ref key) => match self.get_var(key) {
                Some(var) => {
                    env::set_var(key, &var);
                    return SUCCESS;
                }
                None => {
                    eprintln!("ion: cannot export {} because it does not exist.", key);
                    return FAILURE;
                }
            },
            ExportAction::List => {
                let stdout = io::stdout();
                let mut stdout = stdout.lock();
                for (key, val) in env::vars() {
                    let _ = writeln!(stdout, "{} =\"{}\"", key, val);
                }
                return SUCCESS;
            }
        };

        for action in actions {
            match action {
                Ok(Action::UpdateArray(key, Operator::Equal, expression)) => {
                    match value_check(self, &expression, key.kind) {
                        Ok(ReturnValue::Vector(values)) => env::set_var(key.name, values.join(" ")),
                        Err(why) => {
                            eprintln!("ion: assignment error: {}: {}", key.name, why);
                            return FAILURE;
                        }
                        _ => unreachable!(),
                    }
                }
                Ok(Action::UpdateArray(..)) => {
                    eprintln!(
                        "ion: arithmetic operators on array expressions aren't supported yet."
                    );
                    return FAILURE;
                }
                Ok(Action::UpdateString(key, operator, expression)) => {
                    match value_check(self, &expression, key.kind) {
                        Ok(ReturnValue::Str(value)) => {
                            let key_name: &str = &key.name;
                            let lhs = self
                                .variables
                                .get_var(key_name)
                                .unwrap_or_else(|| String::from("0"));

                            let result = math(&lhs, key.kind, operator, &value, |value| {
                                env::set_var(key_name, &OsStr::from_bytes(value))
                            });

                            if let Err(why) = result {
                                eprintln!("ion: assignment error: {}", why);
                                return FAILURE;
                            }
                        }
                        Err(why) => {
                            eprintln!("ion: assignment error: {}: {}", key.name, why);
                            return FAILURE;
                        }
                        _ => unreachable!(),
                    }
                }
                Err(why) => {
                    eprintln!("ion: assignment error: {}", why);
                    return FAILURE;
                }
            }
        }

        SUCCESS
    }

    fn local(&mut self, action: LocalAction) -> i32 {
        let mut collected: HashMap<&str, ReturnValue> = HashMap::new();
        let (actions_step1, actions_step2) = match action {
            LocalAction::List => {
                list_vars(&self);
                return SUCCESS;
            }
            LocalAction::Assign(ref keys, op, ref vals) => (AssignmentActions::new(keys, op, vals), AssignmentActions::new(keys, op, vals)),
        };
        for action in actions_step1 {
            match action {
                Ok(Action::UpdateArray(key, Operator::Equal, expression)) => {
                    match value_check(self, &expression, key.kind) {
                        Ok(ReturnValue::Vector(values)) => {
                            // When we changed the HISTORY_IGNORE variable, update the
                            // ignore patterns. This happens first because `set_array`
                            // consumes 'values'
                            if key.name == "HISTORY_IGNORE" {
                                self.update_ignore_patterns(&values);
                            }
                            collected.insert(key.name, ReturnValue::Vector(values));
                        }
                        Err(why) => {
                            eprintln!("ion: assignment error: {}: {}", key.name, why);
                            return FAILURE;
                        }
                        _ => unreachable!(),
                    }
                }
                Ok(Action::UpdateArray(..)) => {
                    eprintln!(
                        "ion: arithmetic operators on array expressions aren't supported yet."
                    );
                    return FAILURE;
                }
                Ok(Action::UpdateString(key, operator, expression)) => {
                    if ["HOME", "HOST", "PWD", "MWD", "SWD", "?"].contains(&key.name) {
                        eprintln!("ion: not allowed to set {}", key.name);
                        return FAILURE;
                    }

                    match value_check(self, &expression, key.kind) {
                        Ok(ReturnValue::Str(value)) => {
                            match self.variables.lookup_any(key.name) {
                                Some(VariableType::Variable(lhs)) => {
                                    let result = math(&lhs, key.kind, operator, &value, |value| {
                                        collected.insert(key.name, ReturnValue::Str(unsafe {
                                            str::from_utf8_unchecked(value)
                                        }.to_owned()));
                                    });

                                    if let Err(why) = result {
                                        eprintln!("ion: assignment error: {}", why);
                                        return FAILURE;
                                    }
                                },
                                Some(VariableType::Array(array)) => {
                                    let mut output = SmallVec::with_capacity(array.len());
                                    let value = match value.parse::<f64>() {
                                        Ok(n) => n,
                                        Err(_) => {
                                            eprintln!("ion: assignment error: value is not a float");
                                            return FAILURE;
                                        }
                                    };
                                    for part in array.chunks(8) {
                                        let mut vec = simd::f64x8::splat(0.0);

                                        for (i, value) in part.iter().enumerate() {
                                            vec = vec.replace(i, match value.parse::<f64>() {
                                                Ok(n) => n,
                                                Err(_) => {
                                                    eprintln!("ion: assignment error: array item is not a float");
                                                    return FAILURE;
                                                }
                                            });
                                        }

                                        match operator {
                                            Operator::Add => vec += value,
                                            Operator::Divide => vec /= value,
                                            Operator::Subtract => vec -= value,
                                            Operator::Multiply => vec *= value,
                                            Operator::Equal => vec = simd::f64x8::splat(value),
                                            _ => {
                                                eprintln!("ion: assignment error: operator does not yet work on arrays");
                                                return FAILURE;
                                            }
                                        }

                                        for i in 0..part.len() {
                                            output.push(vec.extract(i).to_string());
                                        }
                                    }

                                    collected.insert(key.name, ReturnValue::Vector(output));
                                },
                                _ => {
                                    if operator == Operator::Equal {
                                        collected.insert(key.name, ReturnValue::Str(value));
                                    } else {
                                        eprintln!("ion: assignment error: type does not support this operator");
                                        return FAILURE;
                                    }
                                }
                            }
                        }
                        Err(why) => {
                            eprintln!("ion: assignment error: {}: {}", key.name, why);
                            return FAILURE;
                        }
                        _ => unreachable!(),
                    }
                }
                Err(why) => {
                    eprintln!("ion: assignment error: {}", why);
                    return FAILURE;
                }
            }
        }

        for action in actions_step2 {
            match action {
                Ok(Action::UpdateArray(key, _, _)) => {
                    if let Some(ReturnValue::Vector(values)) = collected.remove(key.name) {
                        self.variables.set_array(key.name, values);
                    }
                }
                Ok(Action::UpdateString(key, _, _)) => {
                    match collected.remove(key.name) {
                        Some(ReturnValue::Str(value)) => self.set_var(key.name, &value),
                        Some(ReturnValue::Vector(values)) => self.variables.set_array(key.name, values),
                        _ => ()
                    }
                }
                _ => unreachable!(),
            }
        }

        SUCCESS
    }
}

#[derive(Debug)]
enum MathError {
    RHS,
    LHS,
    Unsupported,
}

impl Display for MathError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MathError::RHS => write!(fmt, "right hand side has invalid type"),
            MathError::LHS => write!(fmt, "left hand side has invalid type"),
            MathError::Unsupported => write!(fmt, "type does not support operation"),
        }
    }
}

fn parse_f64<F: Fn(f64, f64) -> f64>(lhs: &str, rhs: &str, operation: F) -> Result<f64, MathError> {
    lhs.parse::<f64>()
        .map_err(|_| MathError::LHS)
        .and_then(|lhs| {
            rhs.parse::<f64>()
                .map_err(|_| MathError::RHS)
                .map(|rhs| operation(lhs, rhs))
        })
}

fn parse_i64<F: Fn(i64, i64) -> i64>(lhs: &str, rhs: &str, operation: F) -> Result<i64, MathError> {
    lhs.parse::<i64>()
        .map_err(|_| MathError::LHS)
        .and_then(|lhs| {
            rhs.parse::<i64>()
                .map_err(|_| MathError::RHS)
                .map(|rhs| operation(lhs, rhs))
        })
}

fn write_integer<F: FnMut(&[u8])>(integer: i64, mut func: F) {
    let mut buffer: [u8; 20] = unsafe { mem::uninitialized() };
    let capacity = itoa::write(&mut buffer[..], integer).unwrap();
    func(&buffer[..capacity]);
}

fn math<'a, F: FnMut(&[u8])>(
    lhs: &str,
    key: Primitive,
    operator: Operator,
    value: &'a str,
    mut writefn: F,
) -> Result<(), MathError> {
    match operator {
        Operator::Add => if Primitive::Any == key || Primitive::Float == key {
            writefn(
                parse_f64(lhs, value, |lhs, rhs| lhs + rhs)?
                    .to_string()
                    .as_bytes(),
            );
        } else if let Primitive::Integer = key {
            write_integer(parse_i64(lhs, value, |lhs, rhs| lhs + rhs)?, writefn);
        } else {
            return Err(MathError::Unsupported);
        },
        Operator::Divide => {
            if Primitive::Any == key || Primitive::Float == key || Primitive::Integer == key {
                writefn(
                    parse_f64(lhs, value, |lhs, rhs| lhs / rhs)?
                        .to_string()
                        .as_bytes(),
                );
            } else {
                return Err(MathError::Unsupported);
            }
        }
        Operator::IntegerDivide => if Primitive::Any == key || Primitive::Float == key {
            write_integer(parse_i64(lhs, value, |lhs, rhs| lhs / rhs)?, writefn);
        } else {
            return Err(MathError::Unsupported);
        },
        Operator::Subtract => if Primitive::Any == key || Primitive::Float == key {
            writefn(
                parse_f64(lhs, value, |lhs, rhs| lhs - rhs)?
                    .to_string()
                    .as_bytes(),
            );
        } else if let Primitive::Integer = key {
            write_integer(parse_i64(lhs, value, |lhs, rhs| lhs - rhs)?, writefn);
        } else {
            return Err(MathError::Unsupported);
        },
        Operator::Multiply => if Primitive::Any == key || Primitive::Float == key {
            writefn(
                parse_f64(lhs, value, |lhs, rhs| lhs * rhs)?
                    .to_string()
                    .as_bytes(),
            );
        } else if let Primitive::Integer = key {
            write_integer(parse_i64(lhs, value, |lhs, rhs| lhs * rhs)?, writefn);
        } else {
            return Err(MathError::Unsupported);
        },
        Operator::Exponent => if Primitive::Any == key || Primitive::Float == key {
            writefn(
                parse_f64(lhs, value, |lhs, rhs| lhs.powf(rhs))?
                    .to_string()
                    .as_bytes(),
            );
        } else if let Primitive::Integer = key {
            write_integer(
                parse_i64(lhs, value, |lhs, rhs| lhs.pow(rhs as u32))?,
                writefn,
            );
        } else {
            return Err(MathError::Unsupported);
        },
        Operator::Equal => writefn(value.as_bytes()),
    };

    Ok(())
}
<commit_msg>Revert behavior for Operator::Equal and arrays<commit_after>use super::{
    flow_control::{ExportAction, LocalAction}, status::*, Shell,
};
use itoa;
use parser::assignments::*;
use smallvec::SmallVec;
use shell::{
    history::ShellHistory,
    variables::VariableType
};
use std::{
    collections::HashMap,
    env, ffi::OsStr, fmt::{self, Display}, io::{self, BufWriter, Write}, mem,
    os::unix::ffi::OsStrExt, str, simd,
};

fn list_vars(shell: &Shell) {
    let stdout = io::stdout();
    let mut buffer = BufWriter::new(stdout.lock());

    // Small function for formatting and append an array entry to a string buffer.
    fn print_array<W: Write>(buffer: &mut W, key: &str, array: &[String]) {
        let _ = buffer.write([key, " = [ "].concat().as_bytes());
        if array.len() > 1 {
            let mut vars = array.iter();
            if let Some(ref var) = vars.next() {
                let _ = buffer.write(["'", var, "', "].concat().as_bytes());
                vars.for_each(|var| {
                    let _ = buffer.write(["'", var, "' "].concat().as_bytes());
                });
            }
            let _ = buffer.write(b"]\n");
        } else {
            let _ = buffer.write(["'", &array[0], "' ]\n"].concat().as_bytes());
        }
    }

    // Write all the string variables to the buffer.
    let _ = buffer.write(b"# String Variables\n");
    for (key, val) in shell.variables.variables() {
        let _ = buffer.write([key, " = ", val.as_str(), "\n"].concat().as_bytes());
    }

    // Then immediately follow that with a list of array variables.
    let _ = buffer.write(b"\n# Array Variables\n");
    for (key, val) in shell.variables.arrays() {
        print_array(&mut buffer, &key, &val)
    }
}

/// Represents: A variable store capable of setting local variables or
/// exporting variables to some global environment
pub(crate) trait VariableStore {
    /// Set a local variable given a binding
    fn local(&mut self, LocalAction) -> i32;
    /// Export a variable to the process environment given a binding
    fn export(&mut self, ExportAction) -> i32;
}

impl VariableStore for Shell {
    fn export(&mut self, action: ExportAction) -> i32 {
        let actions = match action {
            ExportAction::Assign(ref keys, op, ref vals) => AssignmentActions::new(keys, op, vals),
            ExportAction::LocalExport(ref key) => match self.get_var(key) {
                Some(var) => {
                    env::set_var(key, &var);
                    return SUCCESS;
                }
                None => {
                    eprintln!("ion: cannot export {} because it does not exist.", key);
                    return FAILURE;
                }
            },
            ExportAction::List => {
                let stdout = io::stdout();
                let mut stdout = stdout.lock();
                for (key, val) in env::vars() {
                    let _ = writeln!(stdout, "{} =\"{}\"", key, val);
                }
                return SUCCESS;
            }
        };

        for action in actions {
            match action {
                Ok(Action::UpdateArray(key, Operator::Equal, expression)) => {
                    match value_check(self, &expression, key.kind) {
                        Ok(ReturnValue::Vector(values)) => env::set_var(key.name, values.join(" ")),
                        Err(why) => {
                            eprintln!("ion: assignment error: {}: {}", key.name, why);
                            return FAILURE;
                        }
                        _ => unreachable!(),
                    }
                }
                Ok(Action::UpdateArray(..)) => {
                    eprintln!(
                        "ion: arithmetic operators on array expressions aren't supported yet."
                    );
                    return FAILURE;
                }
                Ok(Action::UpdateString(key, operator, expression)) => {
                    match value_check(self, &expression, key.kind) {
                        Ok(ReturnValue::Str(value)) => {
                            let key_name: &str = &key.name;
                            let lhs = self
                                .variables
                                .get_var(key_name)
                                .unwrap_or_else(|| String::from("0"));

                            let result = math(&lhs, key.kind, operator, &value, |value| {
                                env::set_var(key_name, &OsStr::from_bytes(value))
                            });

                            if let Err(why) = result {
                                eprintln!("ion: assignment error: {}", why);
                                return FAILURE;
                            }
                        }
                        Err(why) => {
                            eprintln!("ion: assignment error: {}: {}", key.name, why);
                            return FAILURE;
                        }
                        _ => unreachable!(),
                    }
                }
                Err(why) => {
                    eprintln!("ion: assignment error: {}", why);
                    return FAILURE;
                }
            }
        }

        SUCCESS
    }

    fn local(&mut self, action: LocalAction) -> i32 {
        let mut collected: HashMap<&str, ReturnValue> = HashMap::new();
        let (actions_step1, actions_step2) = match action {
            LocalAction::List => {
                list_vars(&self);
                return SUCCESS;
            }
            LocalAction::Assign(ref keys, op, ref vals) => (AssignmentActions::new(keys, op, vals), AssignmentActions::new(keys, op, vals)),
        };
        for action in actions_step1 {
            match action {
                Ok(Action::UpdateArray(key, Operator::Equal, expression)) => {
                    match value_check(self, &expression, key.kind) {
                        Ok(ReturnValue::Vector(values)) => {
                            // When we changed the HISTORY_IGNORE variable, update the
                            // ignore patterns. This happens first because `set_array`
                            // consumes 'values'
                            if key.name == "HISTORY_IGNORE" {
                                self.update_ignore_patterns(&values);
                            }
                            collected.insert(key.name, ReturnValue::Vector(values));
                        }
                        Err(why) => {
                            eprintln!("ion: assignment error: {}: {}", key.name, why);
                            return FAILURE;
                        }
                        _ => unreachable!(),
                    }
                }
                Ok(Action::UpdateArray(..)) => {
                    eprintln!(
                        "ion: arithmetic operators on array expressions aren't supported yet."
                    );
                    return FAILURE;
                }
                Ok(Action::UpdateString(key, operator, expression)) => {
                    if ["HOME", "HOST", "PWD", "MWD", "SWD", "?"].contains(&key.name) {
                        eprintln!("ion: not allowed to set {}", key.name);
                        return FAILURE;
                    }

                    match value_check(self, &expression, key.kind) {
                        Ok(ReturnValue::Str(value)) => {
                            if operator == Operator::Equal {
                                collected.insert(key.name, ReturnValue::Str(value));
                                continue;
                            }
                            match self.variables.lookup_any(key.name) {
                                Some(VariableType::Variable(lhs)) => {
                                    let result = math(&lhs, key.kind, operator, &value, |value| {
                                        collected.insert(key.name, ReturnValue::Str(unsafe {
                                            str::from_utf8_unchecked(value)
                                        }.to_owned()));
                                    });

                                    if let Err(why) = result {
                                        eprintln!("ion: assignment error: {}", why);
                                        return FAILURE;
                                    }
                                },
                                Some(VariableType::Array(array)) => {
                                    let mut output = SmallVec::with_capacity(array.len());

                                    let value = match value.parse::<f64>() {
                                        Ok(n) => n,
                                        Err(_) => {
                                            eprintln!("ion: assignment error: value is not a float");
                                            return FAILURE;
                                        }
                                    };

                                    for part in array.chunks(8) {
                                        let mut vec = simd::f64x8::splat(0.0);

                                        for (i, value) in part.iter().enumerate() {
                                            vec = vec.replace(i, match value.parse::<f64>() {
                                                Ok(n) => n,
                                                Err(_) => {
                                                    eprintln!("ion: assignment error: array item is not a float");
                                                    return FAILURE;
                                                }
                                            });
                                        }

                                        match operator {
                                            Operator::Add => vec += value,
                                            Operator::Divide => vec /= value,
                                            Operator::Subtract => vec -= value,
                                            Operator::Multiply => vec *= value,
                                            _ => {
                                                eprintln!("ion: assignment error: operator does not work on arrays");
                                                return FAILURE;
                                            }
                                        }

                                        for i in 0..part.len() {
                                            output.push(vec.extract(i).to_string());
                                        }
                                    }

                                    collected.insert(key.name, ReturnValue::Vector(output));
                                },
                                _ => {
                                    eprintln!("ion: assignment error: type does not support this operator");
                                    return FAILURE;
                                }
                            }
                        }
                        Err(why) => {
                            eprintln!("ion: assignment error: {}: {}", key.name, why);
                            return FAILURE;
                        }
                        _ => unreachable!(),
                    }
                }
                Err(why) => {
                    eprintln!("ion: assignment error: {}", why);
                    return FAILURE;
                }
            }
        }

        for action in actions_step2 {
            match action {
                Ok(Action::UpdateArray(key, _, _)) => {
                    if let Some(ReturnValue::Vector(values)) = collected.remove(key.name) {
                        self.variables.set_array(key.name, values);
                    }
                }
                Ok(Action::UpdateString(key, _, _)) => {
                    match collected.remove(key.name) {
                        Some(ReturnValue::Str(value)) => self.set_var(key.name, &value),
                        Some(ReturnValue::Vector(values)) => self.variables.set_array(key.name, values),
                        _ => ()
                    }
                }
                _ => unreachable!(),
            }
        }

        SUCCESS
    }
}

#[derive(Debug)]
enum MathError {
    RHS,
    LHS,
    Unsupported,
}

impl Display for MathError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MathError::RHS => write!(fmt, "right hand side has invalid type"),
            MathError::LHS => write!(fmt, "left hand side has invalid type"),
            MathError::Unsupported => write!(fmt, "type does not support operation"),
        }
    }
}

fn parse_f64<F: Fn(f64, f64) -> f64>(lhs: &str, rhs: &str, operation: F) -> Result<f64, MathError> {
    lhs.parse::<f64>()
        .map_err(|_| MathError::LHS)
        .and_then(|lhs| {
            rhs.parse::<f64>()
                .map_err(|_| MathError::RHS)
                .map(|rhs| operation(lhs, rhs))
        })
}

fn parse_i64<F: Fn(i64, i64) -> i64>(lhs: &str, rhs: &str, operation: F) -> Result<i64, MathError> {
    lhs.parse::<i64>()
        .map_err(|_| MathError::LHS)
        .and_then(|lhs| {
            rhs.parse::<i64>()
                .map_err(|_| MathError::RHS)
                .map(|rhs| operation(lhs, rhs))
        })
}

fn write_integer<F: FnMut(&[u8])>(integer: i64, mut func: F) {
    let mut buffer: [u8; 20] = unsafe { mem::uninitialized() };
    let capacity = itoa::write(&mut buffer[..], integer).unwrap();
    func(&buffer[..capacity]);
}

fn math<'a, F: FnMut(&[u8])>(
    lhs: &str,
    key: Primitive,
    operator: Operator,
    value: &'a str,
    mut writefn: F,
) -> Result<(), MathError> {
    match operator {
        Operator::Add => if Primitive::Any == key || Primitive::Float == key {
            writefn(
                parse_f64(lhs, value, |lhs, rhs| lhs + rhs)?
                    .to_string()
                    .as_bytes(),
            );
        } else if let Primitive::Integer = key {
            write_integer(parse_i64(lhs, value, |lhs, rhs| lhs + rhs)?, writefn);
        } else {
            return Err(MathError::Unsupported);
        },
        Operator::Divide => {
            if Primitive::Any == key || Primitive::Float == key || Primitive::Integer == key {
                writefn(
                    parse_f64(lhs, value, |lhs, rhs| lhs / rhs)?
                        .to_string()
                        .as_bytes(),
                );
            } else {
                return Err(MathError::Unsupported);
            }
        }
        Operator::IntegerDivide => if Primitive::Any == key || Primitive::Float == key {
            write_integer(parse_i64(lhs, value, |lhs, rhs| lhs / rhs)?, writefn);
        } else {
            return Err(MathError::Unsupported);
        },
        Operator::Subtract => if Primitive::Any == key || Primitive::Float == key {
            writefn(
                parse_f64(lhs, value, |lhs, rhs| lhs - rhs)?
                    .to_string()
                    .as_bytes(),
            );
        } else if let Primitive::Integer = key {
            write_integer(parse_i64(lhs, value, |lhs, rhs| lhs - rhs)?, writefn);
        } else {
            return Err(MathError::Unsupported);
        },
        Operator::Multiply => if Primitive::Any == key || Primitive::Float == key {
            writefn(
                parse_f64(lhs, value, |lhs, rhs| lhs * rhs)?
                    .to_string()
                    .as_bytes(),
            );
        } else if let Primitive::Integer = key {
            write_integer(parse_i64(lhs, value, |lhs, rhs| lhs * rhs)?, writefn);
        } else {
            return Err(MathError::Unsupported);
        },
        Operator::Exponent => if Primitive::Any == key || Primitive::Float == key {
            writefn(
                parse_f64(lhs, value, |lhs, rhs| lhs.powf(rhs))?
                    .to_string()
                    .as_bytes(),
            );
        } else if let Primitive::Integer = key {
            write_integer(
                parse_i64(lhs, value, |lhs, rhs| lhs.pow(rhs as u32))?,
                writefn,
            );
        } else {
            return Err(MathError::Unsupported);
        },
        Operator::Equal => writefn(value.as_bytes()),
    };

    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A helper class for dealing with static archives

use driver::session::Session;
use metadata::filesearch;
use lib::llvm::{ArchiveRef, llvm};

use std::cast;
use std::io::fs;
use std::libc;
use std::os;
use std::run::{ProcessOptions, Process, ProcessOutput};
use std::str;
use std::unstable::raw;
use extra::tempfile::TempDir;
use syntax::abi;

pub static METADATA_FILENAME: &'static str = "rust.metadata.bin";

pub struct Archive {
    priv sess: Session,
    priv dst: Path,
}

pub struct ArchiveRO {
    priv ptr: ArchiveRef,
}

fn run_ar(sess: Session, args: &str, cwd: Option<&Path>,
        paths: &[&Path]) -> ProcessOutput {
    let ar = sess.opts.ar.clone().unwrap_or_else(|| ~"ar");
    let mut args = ~[args.to_owned()];
    let mut paths = paths.iter().map(|p| p.as_str().unwrap().to_owned());
    args.extend(&mut paths);
    let mut opts = ProcessOptions::new();
    opts.dir = cwd;
    debug!("{} {}", ar, args.connect(" "));
    match cwd {
        Some(p) => { debug!("inside {}", p.display()); }
        None => {}
    }
    let mut opt_prog = Process::new(ar, args.as_slice(), opts);
    match opt_prog {
        Some(ref mut prog) => {
            let o = prog.finish_with_output();
            if !o.status.success() {
                sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
                                 o.status));
                sess.note(format!("stdout ---\n{}", str::from_utf8(o.output)));
                sess.note(format!("stderr ---\n{}", str::from_utf8(o.error)));
                sess.abort_if_errors();
            }
            o
        },
        None => {
            sess.err(format!("could not exec `{}`", ar));
            sess.abort_if_errors();
            fail!("rustc::back::archive::run_ar() should not reach this point");
        }
    }
}

impl Archive {
    /// Initializes a new static archive with the given object file
    pub fn create<'a>(sess: Session, dst: &'a Path,
                      initial_object: &'a Path) -> Archive {
        run_ar(sess, "crus", None, [dst, initial_object]);
        Archive { sess: sess, dst: dst.clone() }
    }

    /// Opens an existing static archive
    pub fn open(sess: Session, dst: Path) -> Archive {
        assert!(dst.exists());
        Archive { sess: sess, dst: dst }
    }

    /// Read a file in the archive
    pub fn read(&self, file: &str) -> ~[u8] {
        // Apparently if "ar p" is used on windows, it generates a corrupt file
        // which has bad headers and LLVM will immediately choke on it
        if cfg!(windows) && cfg!(windows) { // FIXME(#10734) double-and
            let loc = TempDir::new("rsar").unwrap();
            let archive = os::make_absolute(&self.dst);
            run_ar(self.sess, "x", Some(loc.path()), [&archive,
                                                      &Path::new(file)]);
            fs::File::open(&loc.path().join(file)).read_to_end()
        } else {
            run_ar(self.sess, "p", None, [&self.dst, &Path::new(file)]).output
        }
    }

    /// Adds all of the contents of a native library to this archive. This will
    /// search in the relevant locations for a library named `name`.
    pub fn add_native_library(&mut self, name: &str) {
        let location = self.find_library(name);
        self.add_archive(&location, name, []);
    }

    /// Adds all of the contents of the rlib at the specified path to this
    /// archive.
    ///
    /// This ignores adding the bytecode from the rlib, and if LTO is enabled
    /// then the object file also isn't added.
    pub fn add_rlib(&mut self, rlib: &Path, name: &str, lto: bool) {
        let object = format!("{}.o", name);
        let bytecode = format!("{}.bc", name);
        let mut ignore = ~[METADATA_FILENAME, bytecode.as_slice()];
        if lto {
            ignore.push(object.as_slice());
        }
        self.add_archive(rlib, name, ignore);
    }

    /// Adds an arbitrary file to this archive
    pub fn add_file(&mut self, file: &Path, has_symbols: bool) {
        let cmd = if has_symbols {"r"} else {"rS"};
        run_ar(self.sess, cmd, None, [&self.dst, file]);
    }

    /// Removes a file from this archive
    pub fn remove_file(&mut self, file: &str) {
        run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
    }

    /// Update all symbols in the archive (runs 'ar s' over it)
    pub fn update_symbols(&mut self) {
        run_ar(self.sess, "s", None, [&self.dst]);
    }

    /// List all files in an archive
    pub fn files(&self) -> ~[~str] {
        let output = run_ar(self.sess, "t", None, [&self.dst]);
        str::from_utf8(output.output).lines().map(|s| s.to_owned()).collect()
    }

    fn add_archive(&mut self, archive: &Path, name: &str, skip: &[&str]) {
        let loc = TempDir::new("rsar").unwrap();

        // First, extract the contents of the archive to a temporary directory
        let archive = os::make_absolute(archive);
        run_ar(self.sess, "x", Some(loc.path()), [&archive]);

        // Next, we must rename all of the inputs to "guaranteed unique names".
        // The reason for this is that archives are keyed off the name of the
        // files, so if two files have the same name they will override one
        // another in the archive (bad).
        //
        // We skip any files explicitly desired for skipping, and we also skip
        // all SYMDEF files as these are just magical placeholders which get
        // re-created when we make a new archive anyway.
        let files = fs::readdir(loc.path());
        let mut inputs = ~[];
        for file in files.iter() {
            let filename = file.filename_str().unwrap();
            if skip.iter().any(|s| *s == filename) { continue }
            if filename.contains(".SYMDEF") { continue }

            let filename = format!("r-{}-{}", name, filename);
            let new_filename = file.with_filename(filename);
            fs::rename(file, &new_filename);
            inputs.push(new_filename);
        }

        // Finally, add all the renamed files to this archive
        let mut args = ~[&self.dst];
        args.extend(&mut inputs.iter());
        run_ar(self.sess, "r", None, args.as_slice());
    }

    fn find_library(&self, name: &str) -> Path {
        let (osprefix, osext) = match self.sess.targ_cfg.os {
            abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
        };
        // On windows, static libraries sometimes show up as libfoo.a and other
        // times show up as foo.lib
        let oslibname = format!("{}{}.{}", osprefix, name, osext);
        let unixlibname = format!("lib{}.a", name);

        let mut rustpath = filesearch::rust_path();
        rustpath.push(self.sess.filesearch.get_target_lib_path());
        let addl_lib_search_paths = self.sess
                                        .opts
                                        .addl_lib_search_paths
                                        .borrow();
        let path = addl_lib_search_paths.get().iter();
        for path in path.chain(rustpath.iter()) {
            debug!("looking for {} inside {}", name, path.display());
            let test = path.join(oslibname.as_slice());
            if test.exists() { return test }
            if oslibname != unixlibname {
                let test = path.join(unixlibname.as_slice());
                if test.exists() { return test }
            }
        }
        self.sess.fatal(format!("could not find native static library `{}`, \
                                 perhaps an -L flag is missing?", name));
    }
}

impl ArchiveRO {
    /// Opens a static archive for read-only purposes. This is more optimized
    /// than the `open` method because it uses LLVM's internal `Archive` class
    /// rather than shelling out to `ar` for everything.
    ///
    /// If this archive is used with a mutable method, then an error will be
    /// raised.
    pub fn open(dst: &Path) -> Option<ArchiveRO> {
        unsafe {
            let ar = dst.with_c_str(|dst| {
                llvm::LLVMRustOpenArchive(dst)
            });
            if ar.is_null() {
                None
            } else {
                Some(ArchiveRO { ptr: ar })
            }
        }
    }

    /// Read a file in the archive
    pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
        unsafe {
            let mut size = 0 as libc::size_t;
            let ptr = file.with_c_str(|file| {
                llvm::LLVMRustArchiveReadSection(self.ptr, file, &mut size)
            });
            if ptr.is_null() {
                None
            } else {
                Some(cast::transmute(raw::Slice {
                    data: ptr,
                    len: size as uint,
                }))
            }
        }
    }
}

impl Drop for ArchiveRO {
    fn drop(&mut self) {
        unsafe {
            llvm::LLVMRustDestroyArchive(self.ptr);
        }
    }
}
<commit_msg>librustc: ar call fix to support android cross compile on mac<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A helper class for dealing with static archives

use driver::session::Session;
use metadata::filesearch;
use lib::llvm::{ArchiveRef, llvm};

use std::cast;
use std::io::fs;
use std::libc;
use std::os;
use std::run::{ProcessOptions, Process, ProcessOutput};
use std::str;
use std::unstable::raw;
use extra::tempfile::TempDir;
use syntax::abi;

pub static METADATA_FILENAME: &'static str = "rust.metadata.bin";

pub struct Archive {
    priv sess: Session,
    priv dst: Path,
}

pub struct ArchiveRO {
    priv ptr: ArchiveRef,
}

fn run_ar(sess: Session, args: &str, cwd: Option<&Path>,
        paths: &[&Path]) -> ProcessOutput {
    let ar = if sess.opts.target_triple == ~"arm-linux-androideabi" {
        match sess.opts.android_cross_path {
            Some(ref path) => *path + "/bin/" + "arm-linux-androideabi-ar",
            None => ~"arm-linux-androideabi-ar"
        }
    } else {
        sess.opts.ar.clone().unwrap_or_else(|| ~"ar")
    };

    let mut args = ~[args.to_owned()];
    let mut paths = paths.iter().map(|p| p.as_str().unwrap().to_owned());
    args.extend(&mut paths);
    let mut opts = ProcessOptions::new();
    opts.dir = cwd;
    debug!("{} {}", ar, args.connect(" "));
    match cwd {
        Some(p) => { debug!("inside {}", p.display()); }
        None => {}
    }
    let mut opt_prog = Process::new(ar, args.as_slice(), opts);
    match opt_prog {
        Some(ref mut prog) => {
            let o = prog.finish_with_output();
            if !o.status.success() {
                sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
                                 o.status));
                sess.note(format!("stdout ---\n{}", str::from_utf8(o.output)));
                sess.note(format!("stderr ---\n{}", str::from_utf8(o.error)));
                sess.abort_if_errors();
            }
            o
        },
        None => {
            sess.err(format!("could not exec `{}`", ar));
            sess.abort_if_errors();
            fail!("rustc::back::archive::run_ar() should not reach this point");
        }
    }
}

impl Archive {
    /// Initializes a new static archive with the given object file
    pub fn create<'a>(sess: Session, dst: &'a Path,
                      initial_object: &'a Path) -> Archive {
        run_ar(sess, "crus", None, [dst, initial_object]);
        Archive { sess: sess, dst: dst.clone() }
    }

    /// Opens an existing static archive
    pub fn open(sess: Session, dst: Path) -> Archive {
        assert!(dst.exists());
        Archive { sess: sess, dst: dst }
    }

    /// Read a file in the archive
    pub fn read(&self, file: &str) -> ~[u8] {
        // Apparently if "ar p" is used on windows, it generates a corrupt file
        // which has bad headers and LLVM will immediately choke on it
        if cfg!(windows) && cfg!(windows) { // FIXME(#10734) double-and
            let loc = TempDir::new("rsar").unwrap();
            let archive = os::make_absolute(&self.dst);
            run_ar(self.sess, "x", Some(loc.path()), [&archive,
                                                      &Path::new(file)]);
            fs::File::open(&loc.path().join(file)).read_to_end()
        } else {
            run_ar(self.sess, "p", None, [&self.dst, &Path::new(file)]).output
        }
    }

    /// Adds all of the contents of a native library to this archive. This will
    /// search in the relevant locations for a library named `name`.
    pub fn add_native_library(&mut self, name: &str) {
        let location = self.find_library(name);
        self.add_archive(&location, name, []);
    }

    /// Adds all of the contents of the rlib at the specified path to this
    /// archive.
    ///
    /// This ignores adding the bytecode from the rlib, and if LTO is enabled
    /// then the object file also isn't added.
    pub fn add_rlib(&mut self, rlib: &Path, name: &str, lto: bool) {
        let object = format!("{}.o", name);
        let bytecode = format!("{}.bc", name);
        let mut ignore = ~[METADATA_FILENAME, bytecode.as_slice()];
        if lto {
            ignore.push(object.as_slice());
        }
        self.add_archive(rlib, name, ignore);
    }

    /// Adds an arbitrary file to this archive
    pub fn add_file(&mut self, file: &Path, has_symbols: bool) {
        let cmd = if has_symbols {"r"} else {"rS"};
        run_ar(self.sess, cmd, None, [&self.dst, file]);
    }

    /// Removes a file from this archive
    pub fn remove_file(&mut self, file: &str) {
        run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
    }

    /// Update all symbols in the archive (runs 'ar s' over it)
    pub fn update_symbols(&mut self) {
        run_ar(self.sess, "s", None, [&self.dst]);
    }

    /// List all files in an archive
    pub fn files(&self) -> ~[~str] {
        let output = run_ar(self.sess, "t", None, [&self.dst]);
        str::from_utf8(output.output).lines().map(|s| s.to_owned()).collect()
    }

    fn add_archive(&mut self, archive: &Path, name: &str, skip: &[&str]) {
        let loc = TempDir::new("rsar").unwrap();

        // First, extract the contents of the archive to a temporary directory
        let archive = os::make_absolute(archive);
        run_ar(self.sess, "x", Some(loc.path()), [&archive]);

        // Next, we must rename all of the inputs to "guaranteed unique names".
        // The reason for this is that archives are keyed off the name of the
        // files, so if two files have the same name they will override one
        // another in the archive (bad).
        //
        // We skip any files explicitly desired for skipping, and we also skip
        // all SYMDEF files as these are just magical placeholders which get
        // re-created when we make a new archive anyway.
        let files = fs::readdir(loc.path());
        let mut inputs = ~[];
        for file in files.iter() {
            let filename = file.filename_str().unwrap();
            if skip.iter().any(|s| *s == filename) { continue }
            if filename.contains(".SYMDEF") { continue }

            let filename = format!("r-{}-{}", name, filename);
            let new_filename = file.with_filename(filename);
            fs::rename(file, &new_filename);
            inputs.push(new_filename);
        }

        // Finally, add all the renamed files to this archive
        let mut args = ~[&self.dst];
        args.extend(&mut inputs.iter());
        run_ar(self.sess, "r", None, args.as_slice());
    }

    fn find_library(&self, name: &str) -> Path {
        let (osprefix, osext) = match self.sess.targ_cfg.os {
            abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
        };
        // On windows, static libraries sometimes show up as libfoo.a and other
        // times show up as foo.lib
        let oslibname = format!("{}{}.{}", osprefix, name, osext);
        let unixlibname = format!("lib{}.a", name);

        let mut rustpath = filesearch::rust_path();
        rustpath.push(self.sess.filesearch.get_target_lib_path());
        let addl_lib_search_paths = self.sess
                                        .opts
                                        .addl_lib_search_paths
                                        .borrow();
        let path = addl_lib_search_paths.get().iter();
        for path in path.chain(rustpath.iter()) {
            debug!("looking for {} inside {}", name, path.display());
            let test = path.join(oslibname.as_slice());
            if test.exists() { return test }
            if oslibname != unixlibname {
                let test = path.join(unixlibname.as_slice());
                if test.exists() { return test }
            }
        }
        self.sess.fatal(format!("could not find native static library `{}`, \
                                 perhaps an -L flag is missing?", name));
    }
}

impl ArchiveRO {
    /// Opens a static archive for read-only purposes. This is more optimized
    /// than the `open` method because it uses LLVM's internal `Archive` class
    /// rather than shelling out to `ar` for everything.
    ///
    /// If this archive is used with a mutable method, then an error will be
    /// raised.
    pub fn open(dst: &Path) -> Option<ArchiveRO> {
        unsafe {
            let ar = dst.with_c_str(|dst| {
                llvm::LLVMRustOpenArchive(dst)
            });
            if ar.is_null() {
                None
            } else {
                Some(ArchiveRO { ptr: ar })
            }
        }
    }

    /// Read a file in the archive
    pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
        unsafe {
            let mut size = 0 as libc::size_t;
            let ptr = file.with_c_str(|file| {
                llvm::LLVMRustArchiveReadSection(self.ptr, file, &mut size)
            });
            if ptr.is_null() {
                None
            } else {
                Some(cast::transmute(raw::Slice {
                    data: ptr,
                    len: size as uint,
                }))
            }
        }
    }
}

impl Drop for ArchiveRO {
    fn drop(&mut self) {
        unsafe {
            llvm::LLVMRustDestroyArchive(self.ptr);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![crate_name = "rustc_borrowck"]
#![experimental]
#![crate_type = "dylib"]
#![crate_type = "rlib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "http://www.rust-lang.org/favicon.ico",
      html_root_url = "http://doc.rust-lang.org/nightly/")]

#![feature(default_type_params, globs, if_let, import_shadowing, macro_rules, phase, quote)]
#![feature(slicing_syntax, tuple_indexing, unsafe_destructor)]
#![feature(rustc_diagnostic_macros)]
#![feature(unboxed_closures)]
#![allow(non_camel_case_types)]

#[phase(plugin, link)] extern crate log;
#[phase(plugin, link)] extern crate syntax;

// for "clarity", rename the graphviz crate to dot; graphviz within `borrowck`
// refers to the borrowck-specific graphviz adapter traits.
extern crate "graphviz" as dot;
extern crate rustc;

pub use borrowck::check_crate;
pub use borrowck::build_borrowck_dataflow_data_for_fn;
pub use borrowck::FnPartsWithCFG;

mod borrowck;

pub mod graphviz;

<commit_msg>rollup merge of #19881: larroy/master<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![crate_name = "rustc_borrowck"]
#![experimental]
#![crate_type = "dylib"]
#![crate_type = "rlib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "http://www.rust-lang.org/favicon.ico",
      html_root_url = "http://doc.rust-lang.org/nightly/")]

#![feature(default_type_params, globs, import_shadowing, macro_rules, phase, quote)]
#![feature(slicing_syntax, unsafe_destructor)]
#![feature(rustc_diagnostic_macros)]
#![feature(unboxed_closures)]
#![allow(non_camel_case_types)]

#[phase(plugin, link)] extern crate log;
#[phase(plugin, link)] extern crate syntax;

// for "clarity", rename the graphviz crate to dot; graphviz within `borrowck`
// refers to the borrowck-specific graphviz adapter traits.
extern crate "graphviz" as dot;
extern crate rustc;

pub use borrowck::check_crate;
pub use borrowck::build_borrowck_dataflow_data_for_fn;
pub use borrowck::FnPartsWithCFG;

mod borrowck;

pub mod graphviz;

<|endoftext|>