<commit_before>use std::ascii::AsciiExt;
use std::cmp::*;

extern crate num_cpus;
extern crate crossbeam;

macro_rules! chars {
    ($str:expr) => (
        &$str.chars().collect::<Vec<char>>()
    );
}

#[derive(PartialEq)]
struct ScoredChoice {
    idx: usize,
    score: f64,
}

impl PartialOrd for ScoredChoice {
    fn partial_cmp(&self, other: &ScoredChoice) -> Option<Ordering> {
        if other.score == self.score {
            // We fall back to an array index comparison in order to guarantee a stable sort;
            // otherwise the matches may be displayed in a nondeterministic order.
            Some(self.idx.cmp(&other.idx))
        } else {
            other.score.partial_cmp(&self.score)
        }
    }
}

pub fn compute_matches<'a>(choices: &[&'a str], query: &str, filter_only: bool) -> Vec<&'a str> {
    if choices.len() > 100 {
        compute_matches_multi_threaded(choices, query, filter_only)
    } else {
        compute_matches_single_threaded(choices, query, filter_only)
    }
}

pub fn compute_matches_single_threaded<'a>(choices: &[&'a str], query: &str, filter_only: bool) -> Vec<&'a str> {
    let mut ret = Vec::new();
    for (i, choice) in choices.iter().enumerate() {
        let score = if filter_only { filter(choice, query) } else { score(choice, query) };
        if score > 0_f64 {
            ret.push(ScoredChoice{ idx: i, score: score });
        }
    }

    ret.sort_by(|x, y| x.partial_cmp(y).unwrap());
    ret.iter().map(|x| choices[x.idx]).collect()
}

pub fn compute_matches_multi_threaded<'a>(choices: &[&'a str], query: &str, filter_only: bool) -> Vec<&'a str> {
    use std::sync::mpsc::channel;
    let (tx, rx) = channel();
    let workers = num_cpus::get();
    crossbeam::scope(|scope| {
        for current_worker in 0..workers {
            let tx = tx.clone();
            scope.spawn(move || {
                let (lower_bound, upper_bound) = get_slice_indices(choices.len(), workers, current_worker);
                for i in lower_bound..upper_bound {
                    let score = if filter_only { filter(choices[i], query) } else { score(choices[i], query) };
                    tx.send(ScoredChoice{ idx: i, score: score }).unwrap()
                }
            });
        }
    });

    let mut ret = Vec::new();
    for _ in 0..choices.len() {
        let scored_choice = rx.recv().unwrap();
        if scored_choice.score > 0_f64 {
            ret.push(scored_choice);
        }
    }

    ret.sort_by(|x, y| x.partial_cmp(y).unwrap());
    ret.iter().map(|x| choices[x.idx]).collect()
}

fn get_slice_indices(length: usize, workers: usize, idx: usize) -> (usize, usize) {
    let lb = (length as f64 / workers as f64) * idx as f64;
    let ub = (length as f64 / workers as f64) * (idx + 1) as f64;
    (lb as usize, ub as usize)
}

fn score(choice: &str, query: &str) -> f64 {
    if query.is_empty() {
        return 1.0;
    }
    if choice.is_empty() {
        return 0.0;
    }

    let query = chars!(query);
    let choice = chars!(choice);

    match compute_match_length(choice, query) {
        None => 0.0,
        Some(match_length) => {
            let score = query.len() as f64 / match_length as f64;
            score as f64 / choice.len() as f64
        }
    }
}

fn filter(choice: &str, query: &str) -> f64 {
    if query.is_empty() {
        return 1.0;
    }
    if choice.is_empty() {
        return 0.0;
    }

    let query = chars!(query);
    let choice = chars!(choice);

    match compute_match_length(choice, query) {
        None => 0.0,
        Some(_) => 1.0,
    }
}

// This function is for picking out the matching characters for a given (choice, query) pair for
// rendering purposes. It assumes that the given choice is in fact a match for the given query, and
// will panic if this is not the case.
pub fn visual_score(choice: &str, query: &str) -> Vec<usize> {
    if query.is_empty() || choice.is_empty() {
        return Vec::new();
    }
    let query = chars!(query);
    let choice = chars!(choice);

    let (first_idx, _) = get_longest_match(choice, query).unwrap();
    get_match_indices(choice, &query[1..], first_idx).unwrap()
}

fn compute_match_length(string: &[char], query: &[char]) -> Option<usize> {
    get_match_length(get_longest_match(string, query))
}

fn get_longest_match(string: &[char], query: &[char]) -> Option<(usize, usize)> {
    let first_char = query[0];
    let rest = &query[1..];
    let indices = find_char_in_string(string, first_char);

    let mut current_bounds: Option<(usize, usize)> = None;
    let smallest_possible_match = query.len();
    for &i in &indices {
        if let Some(last_index) = find_end_of_match(string, rest, i) {
            let last_bounds = Some((i, last_index));
            let last_match_len = get_match_length(last_bounds).unwrap();
            if current_bounds.is_none() || last_match_len < get_match_length(current_bounds).unwrap() {
                current_bounds = last_bounds;
                if last_match_len == smallest_possible_match {
                    break;
                }
            }
        }
    }
    current_bounds
}

fn get_match_length(bounds: Option<(usize, usize)>) -> Option<usize> {
    if let Some((lb, ub)) = bounds {
        Some(ub - lb + 1)
    } else {
        None
    }
}

fn find_char_in_string(string: &[char], char: char) -> Vec<usize> {
    let mut indices = Vec::new();
    for (i, c) in string.iter().enumerate() {
        if chars_equal(&char, c) {
            indices.push(i);
        }
    }
    indices
}

fn find_end_of_match(string: &[char], rest_of_query: &[char], first_index: usize) -> Option<usize> {
    if let Some(indices) = get_match_indices(string, rest_of_query, first_index) {
        Some(indices[indices.len() - 1])
    } else {
        None
    }
}

fn get_match_indices(string: &[char], rest_of_query: &[char], first_index: usize) -> Option<Vec<usize>> {
    let mut ret = Vec::new();
    let mut last_index = first_index + 1;
    ret.push(first_index);
    for c in rest_of_query.iter() {
        let current_substring = &string[last_index..];
        let mut index = None;
        for (i, x) in current_substring.iter().enumerate() {
            if chars_equal(c, x) {
                index = Some(i);
                break;
            }
        }
        if index.is_some() {
            last_index += index.unwrap() + 1;
            ret.push(last_index - 1);
        } else {
            return None;
        }
    }
    Some(ret)
}

fn chars_equal(q: &char, c: &char) -> bool {
    q == c || *q == c.to_ascii_lowercase()
}

#[cfg(test)]
mod tests {
    use super::{chars_equal, find_end_of_match, get_match_indices, get_slice_indices, score};

    #[test]
    fn chars_equal_test() {
        assert!(chars_equal(&'a', &'a'));
        assert!(!chars_equal(&'a', &'b'));
        assert!(chars_equal(&'A', &'A'));
        assert!(!chars_equal(&'A', &'a'));
        assert!(chars_equal(&'a', &'A'));
    }

    #[test]
    fn get_match_indices_test() {
        assert_eq!(get_match_indices(chars!("asdf"), chars!("sdf"), 0).unwrap(), vec![0, 1, 2, 3]);
        assert_eq!(get_match_indices(chars!("aoeuasdf"), chars!("sdf"), 4).unwrap(), vec![4, 5, 6, 7]);
        assert_eq!(get_match_indices(chars!(" a s d f"), chars!("sdf"), 1).unwrap(), vec![1, 3, 5, 7]);
    }

    #[test]
    fn find_end_of_match_test() {
        assert_eq!(find_end_of_match(chars!("a"), chars!("a"), 0), None);
        assert_eq!(find_end_of_match(chars!("ba"), chars!("a"), 1), None);
        assert_eq!(find_end_of_match(chars!("aaa"), chars!("aa"), 0), Some(2));
        assert_eq!(find_end_of_match(chars!("aaa"), chars!("b"), 0), None);
        assert_eq!(find_end_of_match(chars!("this is a long match"), chars!("this is a match"), 0), None);
        assert_eq!(find_end_of_match(chars!("this is a long match"), chars!("his is a match"), 0), Some(19));
        assert_eq!(find_end_of_match(chars!("./rust/x86_64-apple-darwin/test/run-pass/process-spawn-with-unicode-params-πЯ音æ∞/child.stage2-x86_64-apple-darwin"), chars!("ust"), 2), Some(5));
    }

    #[test]
    fn unicode_boundary_handling_test() {
        score("./rust/x86_64-apple-darwin/test/run-pass/process-spawn-with-unicode-params-πЯ音æ∞/child.stage2-x86_64-apple-darwin", "he");
    }

    #[test]
    fn basic_scoring() {
        assert_eq!(score("", "a"), 0.0);
        assert_eq!(score("a", ""), 1.0);
        assert_eq!(score("short", "longer"), 0.0);
        assert_eq!(score("a", "b"), 0.0);
        assert_eq!(score("ab", "ac"), 0.0);

        assert!(score("a", "a") > 0.0);
        assert!(score("ab", "a") > 0.0);
        assert!(score("ba", "a") > 0.0);
        assert!(score("bab", "a") > 0.0);
        assert!(score("babababab", "aaaa") > 0.0);

        assert_eq!(score("a", "a"), 1_f64 / "a".len() as f64);
        assert_eq!(score("ab", "ab"), 0.5);
        assert_eq!(score("a long string", "a long string"), 1_f64 / "a long string".len() as f64);
        assert_eq!(score("spec/search_spec.rb", "sear"), 1_f64 / "spec/search_spec.rb".len() as f64);
    }

    #[test]
    fn character_matching() {
        assert!(score("/! symbols $^", "/!$^") > 0.0);

        assert_eq!(score("a", "A"), 0.0);
        assert_eq!(score("A", "a"), 1.0);
        assert_eq!(score("A", "A"), 1.0);

        assert_eq!(score("a", "aa"), 0.0);
    }

    #[test]
    fn match_equality() {
        assert!(score("selecta.gemspec", "asp") > score("algorithm4_spec.rb", "asp"));
        assert!(score("README.md", "em") > score("benchmark.rb", "em"));
        assert!(score("search.rb", "sear") > score("spec/search_spec.rb", "sear"));

        assert!(score("fbb", "fbb") > score("foo bar baz", "fbb"));
        assert!(score("foo", "foo") > score("longer foo", "foo"));
        assert!(score("foo", "foo") > score("foo longer", "foo"));
        assert!(score("1/2/3/4", "1/2/3") > score("1/9/2/3/4", "1/2/3"));

        assert!(score("long 12 long", "12") > score("1 long 2", "12"));

        assert_eq!(score("121padding2", "12"), 1.0 / "121padding2".len() as f64);
        assert_eq!(score("1padding212", "12"), 1.0 / "1padding212".len() as f64);
    }

    #[test]
    fn get_slice_indices_test() {
        assert_eq!(get_slice_indices(100, 1, 0), (0, 100));

        assert_eq!(get_slice_indices(100, 2, 1), (50, 100));
        assert_eq!(get_slice_indices(100, 2, 0), (0, 50));

        assert_eq!(get_slice_indices(100, 3, 0), (0, 33));
        assert_eq!(get_slice_indices(100, 3, 1), (33, 66));
        assert_eq!(get_slice_indices(100, 3, 2), (66, 100));

        assert_eq!(get_slice_indices(100, 4, 0), (0, 25));
        assert_eq!(get_slice_indices(100, 4, 1), (25, 50));
        assert_eq!(get_slice_indices(100, 4, 2), (50, 75));
        assert_eq!(get_slice_indices(100, 4, 3), (75, 100));

        assert_eq!(get_slice_indices(12, 12, 11), (11, 12));
        assert_eq!(get_slice_indices(12, 12, 0), (0, 1));

        assert_eq!(get_slice_indices(1, 2, 0), (0, 0));
        assert_eq!(get_slice_indices(1, 2, 1), (0, 1));
    }
}
<commit_msg>Suppress AsciiExt warning on Rust 1.23<commit_after>#[allow(unused_imports)]
use std::ascii::AsciiExt;
use std::cmp::*;

extern crate num_cpus;
extern crate crossbeam;

macro_rules! chars {
    ($str:expr) => (
        &$str.chars().collect::<Vec<char>>()
    );
}

#[derive(PartialEq)]
struct ScoredChoice {
    idx: usize,
    score: f64,
}

impl PartialOrd for ScoredChoice {
    fn partial_cmp(&self, other: &ScoredChoice) -> Option<Ordering> {
        if other.score == self.score {
            // We fall back to an array index comparison in order to guarantee a stable sort;
            // otherwise the matches may be displayed in a nondeterministic order.
            Some(self.idx.cmp(&other.idx))
        } else {
            other.score.partial_cmp(&self.score)
        }
    }
}

pub fn compute_matches<'a>(choices: &[&'a str], query: &str, filter_only: bool) -> Vec<&'a str> {
    if choices.len() > 100 {
        compute_matches_multi_threaded(choices, query, filter_only)
    } else {
        compute_matches_single_threaded(choices, query, filter_only)
    }
}

pub fn compute_matches_single_threaded<'a>(choices: &[&'a str], query: &str, filter_only: bool) -> Vec<&'a str> {
    let mut ret = Vec::new();
    for (i, choice) in choices.iter().enumerate() {
        let score = if filter_only { filter(choice, query) } else { score(choice, query) };
        if score > 0_f64 {
            ret.push(ScoredChoice{ idx: i, score: score });
        }
    }

    ret.sort_by(|x, y| x.partial_cmp(y).unwrap());
    ret.iter().map(|x| choices[x.idx]).collect()
}

pub fn compute_matches_multi_threaded<'a>(choices: &[&'a str], query: &str, filter_only: bool) -> Vec<&'a str> {
    use std::sync::mpsc::channel;
    let (tx, rx) = channel();
    let workers = num_cpus::get();
    crossbeam::scope(|scope| {
        for current_worker in 0..workers {
            let tx = tx.clone();
            scope.spawn(move || {
                let (lower_bound, upper_bound) = get_slice_indices(choices.len(), workers, current_worker);
                for i in lower_bound..upper_bound {
                    let score = if filter_only { filter(choices[i], query) } else { score(choices[i], query) };
                    tx.send(ScoredChoice{ idx: i, score: score }).unwrap()
                }
            });
        }
    });

    let mut ret = Vec::new();
    for _ in 0..choices.len() {
        let scored_choice = rx.recv().unwrap();
        if scored_choice.score > 0_f64 {
            ret.push(scored_choice);
        }
    }

    ret.sort_by(|x, y| x.partial_cmp(y).unwrap());
    ret.iter().map(|x| choices[x.idx]).collect()
}

fn get_slice_indices(length: usize, workers: usize, idx: usize) -> (usize, usize) {
    let lb = (length as f64 / workers as f64) * idx as f64;
    let ub = (length as f64 / workers as f64) * (idx + 1) as f64;
    (lb as usize, ub as usize)
}

fn score(choice: &str, query: &str) -> f64 {
    if query.is_empty() {
        return 1.0;
    }
    if choice.is_empty() {
        return 0.0;
    }

    let query = chars!(query);
    let choice = chars!(choice);

    match compute_match_length(choice, query) {
        None => 0.0,
        Some(match_length) => {
            let score = query.len() as f64 / match_length as f64;
            score as f64 / choice.len() as f64
        }
    }
}

fn filter(choice: &str, query: &str) -> f64 {
    if query.is_empty() {
        return 1.0;
    }
    if choice.is_empty() {
        return 0.0;
    }

    let query = chars!(query);
    let choice = chars!(choice);

    match compute_match_length(choice, query) {
        None => 0.0,
        Some(_) => 1.0,
    }
}

// This function is for picking out the matching characters for a given (choice, query) pair for
// rendering purposes. It assumes that the given choice is in fact a match for the given query, and
// will panic if this is not the case.
pub fn visual_score(choice: &str, query: &str) -> Vec<usize> {
    if query.is_empty() || choice.is_empty() {
        return Vec::new();
    }
    let query = chars!(query);
    let choice = chars!(choice);

    let (first_idx, _) = get_longest_match(choice, query).unwrap();
    get_match_indices(choice, &query[1..], first_idx).unwrap()
}

fn compute_match_length(string: &[char], query: &[char]) -> Option<usize> {
    get_match_length(get_longest_match(string, query))
}

fn get_longest_match(string: &[char], query: &[char]) -> Option<(usize, usize)> {
    let first_char = query[0];
    let rest = &query[1..];
    let indices = find_char_in_string(string, first_char);

    let mut current_bounds: Option<(usize, usize)> = None;
    let smallest_possible_match = query.len();
    for &i in &indices {
        if let Some(last_index) = find_end_of_match(string, rest, i) {
            let last_bounds = Some((i, last_index));
            let last_match_len = get_match_length(last_bounds).unwrap();
            if current_bounds.is_none() || last_match_len < get_match_length(current_bounds).unwrap() {
                current_bounds = last_bounds;
                if last_match_len == smallest_possible_match {
                    break;
                }
            }
        }
    }
    current_bounds
}

fn get_match_length(bounds: Option<(usize, usize)>) -> Option<usize> {
    if let Some((lb, ub)) = bounds {
        Some(ub - lb + 1)
    } else {
        None
    }
}

fn find_char_in_string(string: &[char], char: char) -> Vec<usize> {
    let mut indices = Vec::new();
    for (i, c) in string.iter().enumerate() {
        if chars_equal(&char, c) {
            indices.push(i);
        }
    }
    indices
}

fn find_end_of_match(string: &[char], rest_of_query: &[char], first_index: usize) -> Option<usize> {
    if let Some(indices) = get_match_indices(string, rest_of_query, first_index) {
        Some(indices[indices.len() - 1])
    } else {
        None
    }
}

fn get_match_indices(string: &[char], rest_of_query: &[char], first_index: usize) -> Option<Vec<usize>> {
    let mut ret = Vec::new();
    let mut last_index = first_index + 1;
    ret.push(first_index);
    for c in rest_of_query.iter() {
        let current_substring = &string[last_index..];
        let mut index = None;
        for (i, x) in current_substring.iter().enumerate() {
            if chars_equal(c, x) {
                index = Some(i);
                break;
            }
        }
        if index.is_some() {
            last_index += index.unwrap() + 1;
            ret.push(last_index - 1);
        } else {
            return None;
        }
    }
    Some(ret)
}

fn chars_equal(q: &char, c: &char) -> bool {
    q == c || *q == c.to_ascii_lowercase()
}

#[cfg(test)]
mod tests {
    use super::{chars_equal, find_end_of_match, get_match_indices, get_slice_indices, score};

    #[test]
    fn chars_equal_test() {
        assert!(chars_equal(&'a', &'a'));
        assert!(!chars_equal(&'a', &'b'));
        assert!(chars_equal(&'A', &'A'));
        assert!(!chars_equal(&'A', &'a'));
        assert!(chars_equal(&'a', &'A'));
    }

    #[test]
    fn get_match_indices_test() {
        assert_eq!(get_match_indices(chars!("asdf"), chars!("sdf"), 0).unwrap(), vec![0, 1, 2, 3]);
        assert_eq!(get_match_indices(chars!("aoeuasdf"), chars!("sdf"), 4).unwrap(), vec![4, 5, 6, 7]);
        assert_eq!(get_match_indices(chars!(" a s d f"), chars!("sdf"), 1).unwrap(), vec![1, 3, 5, 7]);
    }

    #[test]
    fn find_end_of_match_test() {
        assert_eq!(find_end_of_match(chars!("a"), chars!("a"), 0), None);
        assert_eq!(find_end_of_match(chars!("ba"), chars!("a"), 1), None);
        assert_eq!(find_end_of_match(chars!("aaa"), chars!("aa"), 0), Some(2));
        assert_eq!(find_end_of_match(chars!("aaa"), chars!("b"), 0), None);
        assert_eq!(find_end_of_match(chars!("this is a long match"), chars!("this is a match"), 0), None);
        assert_eq!(find_end_of_match(chars!("this is a long match"), chars!("his is a match"), 0), Some(19));
        assert_eq!(find_end_of_match(chars!("./rust/x86_64-apple-darwin/test/run-pass/process-spawn-with-unicode-params-πЯ音æ∞/child.stage2-x86_64-apple-darwin"), chars!("ust"), 2), Some(5));
    }

    #[test]
    fn unicode_boundary_handling_test() {
        score("./rust/x86_64-apple-darwin/test/run-pass/process-spawn-with-unicode-params-πЯ音æ∞/child.stage2-x86_64-apple-darwin", "he");
    }

    #[test]
    fn basic_scoring() {
        assert_eq!(score("", "a"), 0.0);
        assert_eq!(score("a", ""), 1.0);
        assert_eq!(score("short", "longer"), 0.0);
        assert_eq!(score("a", "b"), 0.0);
        assert_eq!(score("ab", "ac"), 0.0);

        assert!(score("a", "a") > 0.0);
        assert!(score("ab", "a") > 0.0);
        assert!(score("ba", "a") > 0.0);
        assert!(score("bab", "a") > 0.0);
        assert!(score("babababab", "aaaa") > 0.0);

        assert_eq!(score("a", "a"), 1_f64 / "a".len() as f64);
        assert_eq!(score("ab", "ab"), 0.5);
        assert_eq!(score("a long string", "a long string"), 1_f64 / "a long string".len() as f64);
        assert_eq!(score("spec/search_spec.rb", "sear"), 1_f64 / "spec/search_spec.rb".len() as f64);
    }

    #[test]
    fn character_matching() {
        assert!(score("/! symbols $^", "/!$^") > 0.0);

        assert_eq!(score("a", "A"), 0.0);
        assert_eq!(score("A", "a"), 1.0);
        assert_eq!(score("A", "A"), 1.0);

        assert_eq!(score("a", "aa"), 0.0);
    }

    #[test]
    fn match_equality() {
        assert!(score("selecta.gemspec", "asp") > score("algorithm4_spec.rb", "asp"));
        assert!(score("README.md", "em") > score("benchmark.rb", "em"));
        assert!(score("search.rb", "sear") > score("spec/search_spec.rb", "sear"));

        assert!(score("fbb", "fbb") > score("foo bar baz", "fbb"));
        assert!(score("foo", "foo") > score("longer foo", "foo"));
        assert!(score("foo", "foo") > score("foo longer", "foo"));
        assert!(score("1/2/3/4", "1/2/3") > score("1/9/2/3/4", "1/2/3"));

        assert!(score("long 12 long", "12") > score("1 long 2", "12"));

        assert_eq!(score("121padding2", "12"), 1.0 / "121padding2".len() as f64);
        assert_eq!(score("1padding212", "12"), 1.0 / "1padding212".len() as f64);
    }

    #[test]
    fn get_slice_indices_test() {
        assert_eq!(get_slice_indices(100, 1, 0), (0, 100));

        assert_eq!(get_slice_indices(100, 2, 1), (50, 100));
        assert_eq!(get_slice_indices(100, 2, 0), (0, 50));

        assert_eq!(get_slice_indices(100, 3, 0), (0, 33));
        assert_eq!(get_slice_indices(100, 3, 1), (33, 66));
        assert_eq!(get_slice_indices(100, 3, 2), (66, 100));

        assert_eq!(get_slice_indices(100, 4, 0), (0, 25));
        assert_eq!(get_slice_indices(100, 4, 1), (25, 50));
        assert_eq!(get_slice_indices(100, 4, 2), (50, 75));
        assert_eq!(get_slice_indices(100, 4, 3), (75, 100));

        assert_eq!(get_slice_indices(12, 12, 11), (11, 12));
        assert_eq!(get_slice_indices(12, 12, 0), (0, 1));

        assert_eq!(get_slice_indices(1, 2, 0), (0, 0));
        assert_eq!(get_slice_indices(1, 2, 1), (0, 1));
    }
}
<|endoftext|>


<commit_before>use yoga_wrapper;
use yoga_wrapper::Measures;

pub struct Measurer {}

impl Measures for Measurer {
    fn measure(&self, text: &str) -> yoga_wrapper::Size {
        yoga_wrapper::Size {
            width: text.len() as f32,
            height: 1.0,
        }
    }
}
<commit_msg>chore(measurer): tiny import cleanup<commit_after>use yoga_wrapper;

pub struct Measurer {}

impl yoga_wrapper::Measures for Measurer {
    fn measure(&self, text: &str) -> yoga_wrapper::Size {
        yoga_wrapper::Size {
            width: text.len() as f32,
            height: 1.0,
        }
    }
}
<|endoftext|>


<commit_before>//! Copied from my historian crate. - Tyler Neely
//!
//! A zero-config simple histogram collector
//!
//! for use in instrumented optimization.
//! Uses logarithmic bucketing rather than sampling,
//! and has bounded (generally <0.5%) error on percentiles.
//! Performs no allocations after initial creation.
//! Uses Relaxed atomics during collection.
//!
//! When you create it, it allocates 65k AtomicUsize's
//! that it uses for incrementing. Generating reports
//! after running workloads on dozens of `Histogram`'s
//! does not result in a perceptible delay, but it
//! might not be acceptable for use in low-latency
//! reporting paths.
//!
//! The trade-offs taken in this are to minimize latency
//! during collection, while initial allocation and
//! postprocessing delays are acceptable.
//!
//! Future work to further reduce collection latency
//! may include using thread-local caches that perform
//! no atomic operations until they are dropped, when
//! they may atomically aggregate their measurements
//! into the shared collector that will be used for
//! reporting.
#![allow(unused)]
#![allow(unused_results)]

use std::fmt::{self, Debug};
use std::sync::atomic::{AtomicUsize, Ordering};

const PRECISION: f64 = 100.;
const BUCKETS: usize = 1 << 16;

/// A histogram collector that uses zero-configuration logarithmic buckets.
pub struct Histogram {
    vals: Vec<AtomicUsize>,
    sum: AtomicUsize,
    count: AtomicUsize,
}

impl Default for Histogram {
    fn default() -> Histogram {
        let mut vals = Vec::with_capacity(BUCKETS);
        vals.resize_with(BUCKETS, Default::default);

        Histogram { vals, sum: AtomicUsize::new(0), count: AtomicUsize::new(0) }
    }
}

#[allow(unsafe_code)]
unsafe impl Send for Histogram {}

impl Debug for Histogram {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        const PS: [f64; 10] =
            [0., 50., 75., 90., 95., 97.5, 99., 99.9, 99.99, 100.];
        f.write_str("Histogramgram[")?;

        for p in &PS {
            let res = self.percentile(*p).round();
            let line = format!("({} -> {}) ", p, res);
            f.write_str(&*line)?;
        }

        f.write_str("]")
    }
}

impl Histogram {
    /// Record a value.
    #[inline]
    pub fn measure<T: Into<f64>>(&self, raw_value: T) {
        #[cfg(not(feature = "no_metrics"))]
        {
            let value_float: f64 = raw_value.into();
            self.sum.fetch_add(value_float.round() as usize, Ordering::Relaxed);

            self.count.fetch_add(1, Ordering::Relaxed);

            // compress the value to one of 2**16 values
            // using logarithmic bucketing
            let compressed: u16 = compress(value_float);

            // increment the counter for this compressed value
            self.vals[compressed as usize].fetch_add(1, Ordering::Relaxed);
        }
    }

    /// Retrieve a percentile [0-100]. Returns NAN if no metrics have been
    /// collected yet.
    pub fn percentile(&self, p: f64) -> f64 {
        #[cfg(not(feature = "no_metrics"))]
        {
            assert!(p <= 100., "percentiles must not exceed 100.0");

            let count = self.count.load(Ordering::Acquire);

            if count == 0 {
                return std::f64::NAN;
            }

            let mut target = count as f64 * (p / 100.);
            if target == 0. {
                target = 1.;
            }

            let mut sum = 0.;

            for (idx, val) in self.vals.iter().enumerate() {
                let count = val.load(Ordering::Acquire);
                sum += count as f64;

                if sum >= target {
                    return decompress(idx as u16);
                }
            }
        }

        std::f64::NAN
    }

    /// Dump out some common percentiles.
    pub fn print_percentiles(&self) {
        println!("{:?}", self);
    }

    /// Return the sum of all observations in this histogram.
    pub fn sum(&self) -> usize {
        self.sum.load(Ordering::Acquire)
    }

    /// Return the count of observations in this histogram.
    pub fn count(&self) -> usize {
        self.count.load(Ordering::Acquire)
    }
}

// compress takes a value and lossily shrinks it to an u16 to facilitate
// bucketing of histogram values, staying roughly within 1% of the true
// value. This fails for large values of 1e142 and above, and is
// inaccurate for values closer to 0 than +/- 0.51 or +/- math.Inf.
#[inline]
fn compress<T: Into<f64>>(value: T) -> u16 {
    let value: f64 = value.into();
    let abs = value.abs();
    let boosted = 1. + abs;
    let ln = boosted.ln();
    let compressed = PRECISION * ln + 0.5;
    assert!(compressed <= f64::from(std::u16::MAX));
    compressed as u16
}

// decompress takes a lossily shrunken u16 and returns an f64 within 1% of
// the original passed to compress.
#[inline]
fn decompress(compressed: u16) -> f64 {
    let unboosted = f64::from(compressed) / PRECISION;
    (unboosted.exp() - 1.)
}

#[cfg(not(feature = "no_metrics"))]
#[test]
fn it_works() {
    let c = Histogram::default();
    assert_eq!(c.measure(2), 1);
    assert_eq!(c.measure(2), 2);
    assert_eq!(c.measure(3), 1);
    assert_eq!(c.measure(3), 2);
    assert_eq!(c.measure(4), 1);
    assert_eq!(c.percentile(0.).round() as usize, 2);
    assert_eq!(c.percentile(40.).round() as usize, 2);
    assert_eq!(c.percentile(40.1).round() as usize, 3);
    assert_eq!(c.percentile(80.).round() as usize, 3);
    assert_eq!(c.percentile(80.1).round() as usize, 4);
    assert_eq!(c.percentile(100.).round() as usize, 4);
    c.print_percentiles();
}

#[cfg(not(feature = "no_metrics"))]
#[test]
fn high_percentiles() {
    let c = Histogram::default();
    for _ in 0..9000 {
        c.measure(10);
    }
    for _ in 0..900 {
        c.measure(25);
    }
    for _ in 0..90 {
        c.measure(33);
    }
    for _ in 0..9 {
        c.measure(47);
    }
    c.measure(500);
    assert_eq!(c.percentile(0.).round() as usize, 10);
    assert_eq!(c.percentile(99.).round() as usize, 25);
    assert_eq!(c.percentile(99.89).round() as usize, 33);
    assert_eq!(c.percentile(99.91).round() as usize, 47);
    assert_eq!(c.percentile(99.99).round() as usize, 47);
    assert_eq!(c.percentile(100.).round() as usize, 502);
}

#[cfg(not(feature = "no_metrics"))]
#[test]
fn multithreaded() {
    use std::sync::Arc;
    use std::thread;

    let h = Arc::new(Histogram::default());
    let mut threads = vec![];

    for _ in 0..10 {
        let h = h.clone();
        threads.push(thread::spawn(move || {
            h.measure(20);
        }));
    }

    for t in threads.into_iter() {
        t.join().unwrap();
    }

    assert_eq!(h.percentile(50.).round() as usize, 20);
}
<commit_msg>clippy feedback<commit_after>//! Copied from my historian crate. - Tyler Neely
//!
//! A zero-config simple histogram collector
//!
//! for use in instrumented optimization.
//! Uses logarithmic bucketing rather than sampling,
//! and has bounded (generally <0.5%) error on percentiles.
//! Performs no allocations after initial creation.
//! Uses Relaxed atomics during collection.
//!
//! When you create it, it allocates 65k AtomicUsize's
//! that it uses for incrementing. Generating reports
//! after running workloads on dozens of `Histogram`'s
//! does not result in a perceptible delay, but it
//! might not be acceptable for use in low-latency
//! reporting paths.
//!
//! The trade-offs taken in this are to minimize latency
//! during collection, while initial allocation and
//! postprocessing delays are acceptable.
//!
//! Future work to further reduce collection latency
//! may include using thread-local caches that perform
//! no atomic operations until they are dropped, when
//! they may atomically aggregate their measurements
//! into the shared collector that will be used for
//! reporting.
#![allow(unused)]
#![allow(unused_results)]

use std::fmt::{self, Debug};
use std::sync::atomic::{AtomicUsize, Ordering};

const PRECISION: f64 = 100.;
const BUCKETS: usize = 1 << 16;

/// A histogram collector that uses zero-configuration logarithmic buckets.
pub struct Histogram {
    vals: Vec<AtomicUsize>,
    sum: AtomicUsize,
    count: AtomicUsize,
}

impl Default for Histogram {
    fn default() -> Histogram {
        let mut vals = Vec::with_capacity(BUCKETS);
        vals.resize_with(BUCKETS, Default::default);

        Histogram { vals, sum: AtomicUsize::new(0), count: AtomicUsize::new(0) }
    }
}

#[allow(unsafe_code)]
unsafe impl Send for Histogram {}

impl Debug for Histogram {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        const PS: [f64; 10] =
            [0., 50., 75., 90., 95., 97.5, 99., 99.9, 99.99, 100.];
        f.write_str("Histogramgram[")?;

        for p in &PS {
            let res = self.percentile(*p).round();
            let line = format!("({} -> {}) ", p, res);
            f.write_str(&*line)?;
        }

        f.write_str("]")
    }
}

impl Histogram {
    /// Record a value.
    #[inline]
    pub fn measure<T: Into<f64>>(&self, raw_value: T) {
        #[cfg(not(feature = "no_metrics"))]
        {
            let value_float: f64 = raw_value.into();
            self.sum.fetch_add(value_float.round() as usize, Ordering::Relaxed);

            self.count.fetch_add(1, Ordering::Relaxed);

            // compress the value to one of 2**16 values
            // using logarithmic bucketing
            let compressed: u16 = compress(value_float);

            // increment the counter for this compressed value
            self.vals[compressed as usize].fetch_add(1, Ordering::Relaxed);
        }
    }

    /// Retrieve a percentile [0-100]. Returns NAN if no metrics have been
    /// collected yet.
    pub fn percentile(&self, p: f64) -> f64 {
        #[cfg(not(feature = "no_metrics"))]
        {
            assert!(p <= 100., "percentiles must not exceed 100.0");

            let count = self.count.load(Ordering::Acquire);

            if count == 0 {
                return std::f64::NAN;
            }

            let mut target = count as f64 * (p / 100.);
            if target == 0. {
                target = 1.;
            }

            let mut sum = 0.;

            for (idx, val) in self.vals.iter().enumerate() {
                let count = val.load(Ordering::Acquire);
                sum += count as f64;

                if sum >= target {
                    return decompress(idx as u16);
                }
            }
        }

        std::f64::NAN
    }

    /// Dump out some common percentiles.
    pub fn print_percentiles(&self) {
        println!("{:?}", self);
    }

    /// Return the sum of all observations in this histogram.
    pub fn sum(&self) -> usize {
        self.sum.load(Ordering::Acquire)
    }

    /// Return the count of observations in this histogram.
    pub fn count(&self) -> usize {
        self.count.load(Ordering::Acquire)
    }
}

// compress takes a value and lossily shrinks it to an u16 to facilitate
// bucketing of histogram values, staying roughly within 1% of the true
// value. This fails for large values of 1e142 and above, and is
// inaccurate for values closer to 0 than +/- 0.51 or +/- math.Inf.
#[inline]
fn compress<T: Into<f64>>(value: T) -> u16 {
    let value: f64 = value.into();
    let abs = value.abs();
    let boosted = 1. + abs;
    let ln = boosted.ln();
    let compressed = PRECISION.mul_add(ln, 0.5);
    assert!(compressed <= f64::from(std::u16::MAX));
    compressed as u16
}

// decompress takes a lossily shrunken u16 and returns an f64 within 1% of
// the original passed to compress.
#[inline]
fn decompress(compressed: u16) -> f64 {
    let unboosted = f64::from(compressed) / PRECISION;
    (unboosted.exp() - 1.)
}

#[cfg(not(feature = "no_metrics"))]
#[test]
fn it_works() {
    let c = Histogram::default();
    assert_eq!(c.measure(2), 1);
    assert_eq!(c.measure(2), 2);
    assert_eq!(c.measure(3), 1);
    assert_eq!(c.measure(3), 2);
    assert_eq!(c.measure(4), 1);
    assert_eq!(c.percentile(0.).round() as usize, 2);
    assert_eq!(c.percentile(40.).round() as usize, 2);
    assert_eq!(c.percentile(40.1).round() as usize, 3);
    assert_eq!(c.percentile(80.).round() as usize, 3);
    assert_eq!(c.percentile(80.1).round() as usize, 4);
    assert_eq!(c.percentile(100.).round() as usize, 4);
    c.print_percentiles();
}

#[cfg(not(feature = "no_metrics"))]
#[test]
fn high_percentiles() {
    let c = Histogram::default();
    for _ in 0..9000 {
        c.measure(10);
    }
    for _ in 0..900 {
        c.measure(25);
    }
    for _ in 0..90 {
        c.measure(33);
    }
    for _ in 0..9 {
        c.measure(47);
    }
    c.measure(500);
    assert_eq!(c.percentile(0.).round() as usize, 10);
    assert_eq!(c.percentile(99.).round() as usize, 25);
    assert_eq!(c.percentile(99.89).round() as usize, 33);
    assert_eq!(c.percentile(99.91).round() as usize, 47);
    assert_eq!(c.percentile(99.99).round() as usize, 47);
    assert_eq!(c.percentile(100.).round() as usize, 502);
}

#[cfg(not(feature = "no_metrics"))]
#[test]
fn multithreaded() {
    use std::sync::Arc;
    use std::thread;

    let h = Arc::new(Histogram::default());
    let mut threads = vec![];

    for _ in 0..10 {
        let h = h.clone();
        threads.push(thread::spawn(move || {
            h.measure(20);
        }));
    }

    for t in threads.into_iter() {
        t.join().unwrap();
    }

    assert_eq!(h.percentile(50.).round() as usize, 20);
}
<|endoftext|>


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

use serde_ini;

use chrono::Duration;
use std::io;
use std::path::PathBuf;

use serde_helpers::de;

const SYSTEM_SETTINGS_PATH: &str = "/etc/updatehub.conf";

#[derive(Debug, Default, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Settings {
    pub polling: Polling,
    pub storage: Storage,
    pub update: Update,
    pub network: Network,
    pub firmware: Firmware,
}

impl Settings {
    pub fn new() -> Self {
        Settings::default()
    }

    pub fn load(self) -> Result<Self, SettingsError> {
        use std::fs::File;
        use std::io::Read;
        use std::path::Path;

        let path = Path::new(SYSTEM_SETTINGS_PATH);

        if path.exists() {
            info!(
                "Loading system settings from '{}'...",
                path.to_string_lossy()
            );

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;

            Ok(Settings::parse(&content)?)
        } else {
            debug!(
                "System settings file {} does not exists.",
                path.to_string_lossy()
            );
            info!("Using default system settings...");
            Ok(self)
        }
    }

    fn parse(content: &str) -> Result<Self, SettingsError> {
        let settings = serde_ini::from_str::<Settings>(content)?;

        if &settings.polling.interval < &Duration::seconds(60) {
            error!(
                "Invalid setting for polling interval. The interval cannot be less than 60 seconds"
            );
            return Err(SettingsError::InvalidInterval);
        }

        if !&settings.network.server_address.starts_with("http://")
            && !&settings.network.server_address.starts_with("https://")
        {
            error!("Invalid setting for server address. The server address must use the protocol prefix");
            return Err(SettingsError::InvalidServerAddress);
        }

        Ok(settings)
    }
}

#[derive(Debug)]
pub enum SettingsError {
    Io(io::Error),
    Ini(serde_ini::de::Error),
    InvalidInterval,
    InvalidServerAddress,
}

impl From<io::Error> for SettingsError {
    fn from(err: io::Error) -> SettingsError {
        SettingsError::Io(err)
    }
}

impl From<serde_ini::de::Error> for SettingsError {
    fn from(err: serde_ini::de::Error) -> SettingsError {
        SettingsError::Ini(err)
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Polling {
    #[serde(deserialize_with = "de::duration_from_str")]
    pub interval: Duration,
    #[serde(deserialize_with = "de::bool_from_str")]
    pub enabled: bool,
}

impl Default for Polling {
    fn default() -> Self {
        Polling {
            interval: Duration::days(1),
            enabled: true,
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Storage {
    #[serde(deserialize_with = "de::bool_from_str")]
    pub read_only: bool,
    pub runtime_settings: String,
}

impl Default for Storage {
    fn default() -> Self {
        Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub.conf".into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Update {
    pub download_dir: String,
    #[serde(rename = "SupportedInstallModes")]
    #[serde(deserialize_with = "de::vec_from_str")]
    pub install_modes: Vec<String>,
}

impl Default for Update {
    fn default() -> Self {
        Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs"
            ].iter()
                .map(|i| i.to_string())
                .collect(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Network {
    pub server_address: String,
}

impl Default for Network {
    fn default() -> Self {
        Network {
            server_address: "https://api.updatehub.io".into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Firmware {
    pub metadata_path: PathBuf,
}

impl Default for Firmware {
    fn default() -> Self {
        Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        }
    }
}

#[test]
fn ok() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";

    let expected = Settings {
        polling: Polling {
            interval: Duration::seconds(60),
            enabled: false,
        },
        storage: Storage {
            read_only: true,
            runtime_settings: "/run/updatehub/state".into(),
        },
        update: Update {
            download_dir: "/tmp/download".into(),
            install_modes: ["mode1", "mode2"].iter().map(|i| i.to_string()).collect(),
        },
        network: Network {
            server_address: "http://localhost".into(),
        },
        firmware: Firmware {
            metadata_path: "/tmp/metadata".into(),
        },
    };

    assert!(
        serde_ini::from_str::<Settings>(ini)
            .map_err(|e| println!("{}", e))
            .ok() == Some(expected)
    );
}

#[test]
fn invalid_polling_interval() {
    let ini = r"
[Polling]
Interval=59s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn invalid_network_server_address() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn default() {
    let settings = Settings::new();
    let expected = Settings {
        polling: Polling {
            interval: Duration::days(1),
            enabled: true,
        },
        storage: Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub.conf".into(),
        },
        update: Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs"
            ].iter()
                .map(|i| i.to_string())
                .collect(),
        },
        network: Network {
            server_address: "https://api.updatehub.io".into(),
        },
        firmware: Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        },
    };

    assert!(Some(settings) == Some(expected));
}
<commit_msg>settings: Change Update.download_dir to a PathBuf<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use serde_ini;

use chrono::Duration;
use std::io;
use std::path::PathBuf;

use serde_helpers::de;

const SYSTEM_SETTINGS_PATH: &str = "/etc/updatehub.conf";

#[derive(Debug, Default, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Settings {
    pub polling: Polling,
    pub storage: Storage,
    pub update: Update,
    pub network: Network,
    pub firmware: Firmware,
}

impl Settings {
    pub fn new() -> Self {
        Settings::default()
    }

    pub fn load(self) -> Result<Self, SettingsError> {
        use std::fs::File;
        use std::io::Read;
        use std::path::Path;

        let path = Path::new(SYSTEM_SETTINGS_PATH);

        if path.exists() {
            info!(
                "Loading system settings from '{}'...",
                path.to_string_lossy()
            );

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;

            Ok(Settings::parse(&content)?)
        } else {
            debug!(
                "System settings file {} does not exists.",
                path.to_string_lossy()
            );
            info!("Using default system settings...");
            Ok(self)
        }
    }

    fn parse(content: &str) -> Result<Self, SettingsError> {
        let settings = serde_ini::from_str::<Settings>(content)?;

        if &settings.polling.interval < &Duration::seconds(60) {
            error!(
                "Invalid setting for polling interval. The interval cannot be less than 60 seconds"
            );
            return Err(SettingsError::InvalidInterval);
        }

        if !&settings.network.server_address.starts_with("http://")
            && !&settings.network.server_address.starts_with("https://")
        {
            error!("Invalid setting for server address. The server address must use the protocol prefix");
            return Err(SettingsError::InvalidServerAddress);
        }

        Ok(settings)
    }
}

#[derive(Debug)]
pub enum SettingsError {
    Io(io::Error),
    Ini(serde_ini::de::Error),
    InvalidInterval,
    InvalidServerAddress,
}

impl From<io::Error> for SettingsError {
    fn from(err: io::Error) -> SettingsError {
        SettingsError::Io(err)
    }
}

impl From<serde_ini::de::Error> for SettingsError {
    fn from(err: serde_ini::de::Error) -> SettingsError {
        SettingsError::Ini(err)
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Polling {
    #[serde(deserialize_with = "de::duration_from_str")]
    pub interval: Duration,
    #[serde(deserialize_with = "de::bool_from_str")]
    pub enabled: bool,
}

impl Default for Polling {
    fn default() -> Self {
        Polling {
            interval: Duration::days(1),
            enabled: true,
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Storage {
    #[serde(deserialize_with = "de::bool_from_str")]
    pub read_only: bool,
    pub runtime_settings: String,
}

impl Default for Storage {
    fn default() -> Self {
        Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub.conf".into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Update {
    pub download_dir: PathBuf,
    #[serde(rename = "SupportedInstallModes")]
    #[serde(deserialize_with = "de::vec_from_str")]
    pub install_modes: Vec<String>,
}

impl Default for Update {
    fn default() -> Self {
        Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs"
            ].iter()
                .map(|i| i.to_string())
                .collect(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Network {
    pub server_address: String,
}

impl Default for Network {
    fn default() -> Self {
        Network {
            server_address: "https://api.updatehub.io".into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Firmware {
    pub metadata_path: PathBuf,
}

impl Default for Firmware {
    fn default() -> Self {
        Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        }
    }
}

#[test]
fn ok() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";

    let expected = Settings {
        polling: Polling {
            interval: Duration::seconds(60),
            enabled: false,
        },
        storage: Storage {
            read_only: true,
            runtime_settings: "/run/updatehub/state".into(),
        },
        update: Update {
            download_dir: "/tmp/download".into(),
            install_modes: ["mode1", "mode2"].iter().map(|i| i.to_string()).collect(),
        },
        network: Network {
            server_address: "http://localhost".into(),
        },
        firmware: Firmware {
            metadata_path: "/tmp/metadata".into(),
        },
    };

    assert!(
        serde_ini::from_str::<Settings>(ini)
            .map_err(|e| println!("{}", e))
            .ok() == Some(expected)
    );
}

#[test]
fn invalid_polling_interval() {
    let ini = r"
[Polling]
Interval=59s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn invalid_network_server_address() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn default() {
    let settings = Settings::new();
    let expected = Settings {
        polling: Polling {
            interval: Duration::days(1),
            enabled: true,
        },
        storage: Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub.conf".into(),
        },
        update: Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs"
            ].iter()
                .map(|i| i.to_string())
                .collect(),
        },
        network: Network {
            server_address: "https://api.updatehub.io".into(),
        },
        firmware: Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        },
    };

    assert!(Some(settings) == Some(expected));
}
<|endoftext|>


<commit_before>use crate::asm::program_parsers::program;
use crate::asm::Assembler;
use crate::repl::command_parser::CommandParser;
use crate::vm::VM;

use std::fs;
use std::io;
use std::io::Write;
use std::path::Path;

pub mod command_parser;

const INFO_TAG: char = '\u{29FB}';
const WARN_TAG: char = '\u{26A0}';
const ERROR_TAG: char = '\u{2620}';

pub struct REPL {
    command_buffer: Vec<String>,
    vm: VM,
    asm: Assembler,
}

impl REPL {
    pub fn new() -> REPL {
        REPL {
            vm: VM::new(),
            command_buffer: vec![],
            asm: Assembler::new(),
        }
    }

    pub fn run(&mut self) {
        println!("{} What will you DO today?", INFO_TAG);

        loop {
            let mut buffer = String::new();

            print!("\u{21AA} ");
            io::stdout().flush().expect("Unable to flush stdout");

            io::stdin()
                .read_line(&mut buffer)
                .expect("Unable to read line from user");
            let buffer = buffer.trim();

            self.command_buffer.push(buffer.to_string());

            if buffer.starts_with(':') {
                self.execute_command(&buffer);
            } else {
                let program = match program(buffer.into()) {
                    Ok((_, program)) => program,
                    Err(e) => {
                        println!("{} Unable to parse input: {}", WARN_TAG, e);
                        continue;
                    }
                };
                self.vm
                    .program
                    .append(&mut program.to_bytes(&self.asm.symbols));
                if let Err(e) = self.vm.step() {
                    println!("{} {}", ERROR_TAG, e);
                    continue;
                }
            }
        }
    }

    fn execute_command(&mut self, input: &str) {
        let args = CommandParser::tokenize(input);
        match args[0] {
            ":c" => self.vm = VM::new(),
            ":history" => {
                for command in &self.command_buffer {
                    println!("  {}", command);
                }
            }
            ":list" => self.list_program(),
            ":q" => {
                println!("{} Buh-bye!", INFO_TAG);
                std::process::exit(0);
            }
            ":r" => self.list_registers(),
            ":s" => {
                println!("{} Listing symbols:", INFO_TAG);
                println!("{:#?}", self.asm.symbols);
                println!("{} EOF", INFO_TAG);
            }
            ":load" => self.load_file(&args[1..]),
            _ => println!("Invalid command: '{}'", input),
        }
    }

    fn list_program(&self) {
        println!("{} Listing instructions:", INFO_TAG);
        for instr in self.vm.program.chunks(4) {
            println!("  {:?}", instr);
        }
        println!("{} EOF", INFO_TAG);
    }

    fn list_registers(&self) {
        println!("{} Listing integer registers:", INFO_TAG);
        for reg in self.vm.iregisters.chunks(4) {
            println!("  {}\t{}\t{}\t{}", reg[0], reg[1], reg[2], reg[3]);
        }
        println!("{} EOF", INFO_TAG);

        println!("{} Listing real registers:", INFO_TAG);
        for reg in self.vm.rregisters.chunks(4) {
            println!("  {}\t{}\t{}\t{}", reg[0], reg[1], reg[2], reg[3]);
        }
        println!("{} EOF", INFO_TAG);
    }

    fn load_file(&mut self, args: &[&str]) {
        // TODO: merge with `main.rs` file reading.
        let mut tmp = String::new();
        if args.is_empty() {
            print!("{} Please enter the path to the file: ", INFO_TAG);
            io::stdout().flush().expect("Failed to flush stdout");
            io::stdin()
                .read_line(&mut tmp)
                .expect("Failed to read line from user");
            tmp = tmp.trim().to_string();
        } else {
            tmp = args[1].to_string();
        }
        let path = Path::new(&tmp);
        println!(
            "{} Loading program from '{}'",
            INFO_TAG,
            path.to_str().unwrap()
        );
        let result = fs::read_to_string(path);
        let contents = match result {
            Ok(contents) => contents,
            Err(e) => {
                println!("{} Error loading that file: {:?}", ERROR_TAG, e);
                return;
            }
        };

        match self.asm.assemble(&contents) {
            Ok(assembled) => {
                println!("{} Sending program to VM", INFO_TAG);
                self.vm = VM::new();
                self.vm.set_bytecode(&assembled).unwrap();
                self.list_program();
                let result = self.vm.run();
                if result.is_err() {
                    println!("{} Runtime error: {}", ERROR_TAG, result.unwrap_err());
                }
            }
            Err(errors) => {
                println!("{} Errors encountered while assembling:", ERROR_TAG);
                for error in errors {
                    println!("  {}", error);
                }
            }
        }
    }
}

impl Default for REPL {
    fn default() -> Self {
        REPL::new()
    }
}
<commit_msg>add help command to repl<commit_after>use crate::asm::program_parsers::program;
use crate::asm::Assembler;
use crate::repl::command_parser::CommandParser;
use crate::vm::VM;

use std::fs;
use std::io;
use std::io::Write;
use std::path::Path;

pub mod command_parser;

const INFO_TAG: char = '\u{29FB}';
const WARN_TAG: char = '\u{26A0}';
const ERROR_TAG: char = '\u{2620}';

pub struct REPL {
    command_buffer: Vec<String>,
    vm: VM,
    asm: Assembler,
}

impl REPL {
    pub fn new() -> REPL {
        REPL {
            vm: VM::new(),
            command_buffer: vec![],
            asm: Assembler::new(),
        }
    }

    pub fn run(&mut self) {
        println!("{} What will you DO today?", INFO_TAG);

        loop {
            let mut buffer = String::new();

            print!("\u{21AA} ");
            io::stdout().flush().expect("Unable to flush stdout");

            io::stdin()
                .read_line(&mut buffer)
                .expect("Unable to read line from user");
            let buffer = buffer.trim();

            self.command_buffer.push(buffer.to_string());

            if buffer.starts_with(':') {
                self.execute_command(&buffer);
            } else {
                let program = match program(buffer.into()) {
                    Ok((_, program)) => program,
                    Err(e) => {
                        println!("{} Unable to parse input: {}", WARN_TAG, e);
                        continue;
                    }
                };
                self.vm
                    .program
                    .append(&mut program.to_bytes(&self.asm.symbols));
                if let Err(e) = self.vm.step() {
                    println!("{} {}", ERROR_TAG, e);
                    continue;
                }
            }
        }
    }

    fn execute_command(&mut self, input: &str) {
        let args = CommandParser::tokenize(input);
        match args[0] {
            ":c" => self.vm = VM::new(),
            ":h" => self.print_help(),
            ":history" => {
                for command in &self.command_buffer {
                    println!("  {}", command);
                }
            }
            ":list" => self.list_program(),
            ":load" => self.load_file(&args[1..]),
            ":q" => {
                println!("{} Buh-bye!", INFO_TAG);
                std::process::exit(0);
            }
            ":r" => self.list_registers(),
            ":s" => {
                println!("{} Listing symbols:", INFO_TAG);
                println!("{:#?}", self.asm.symbols);
                println!("{} EOF", INFO_TAG);
            }
            _ => println!("Invalid command: '{}'", input),
        }
    }

    fn print_help(&self) {
        println!(":c clears the VM");
        println!(":h prints this help");
        println!(":history shows the command buffer");
        println!(":list lists the current program");
        println!(":load loads a program from the given path");
        println!(":q quits");
        println!(":r prints the registers");
        println!(":s lists the known symbols");
    }

    fn list_program(&self) {
        println!("{} Listing instructions:", INFO_TAG);
        for instr in self.vm.program.chunks(4) {
            println!("  {:?}", instr);
        }
        println!("{} EOF", INFO_TAG);
    }

    fn list_registers(&self) {
        println!("{} Listing integer registers:", INFO_TAG);
        for reg in self.vm.iregisters.chunks(4) {
            println!("  {}\t{}\t{}\t{}", reg[0], reg[1], reg[2], reg[3]);
        }
        println!("{} EOF", INFO_TAG);

        println!("{} Listing real registers:", INFO_TAG);
        for reg in self.vm.rregisters.chunks(4) {
            println!("  {}\t{}\t{}\t{}", reg[0], reg[1], reg[2], reg[3]);
        }
        println!("{} EOF", INFO_TAG);
    }

    fn load_file(&mut self, args: &[&str]) {
        // TODO: merge with `main.rs` file reading.
        let mut tmp = String::new();
        if args.is_empty() {
            print!("{} Please enter the path to the file: ", INFO_TAG);
            io::stdout().flush().expect("Failed to flush stdout");
            io::stdin()
                .read_line(&mut tmp)
                .expect("Failed to read line from user");
            tmp = tmp.trim().to_string();
        } else {
            tmp = args[1].to_string();
        }
        let path = Path::new(&tmp);
        println!(
            "{} Loading program from '{}'",
            INFO_TAG,
            path.to_str().unwrap()
        );
        let result = fs::read_to_string(path);
        let contents = match result {
            Ok(contents) => contents,
            Err(e) => {
                println!("{} Error loading that file: {:?}", ERROR_TAG, e);
                return;
            }
        };

        match self.asm.assemble(&contents) {
            Ok(assembled) => {
                println!("{} Sending program to VM", INFO_TAG);
                self.vm = VM::new();
                self.vm.set_bytecode(&assembled).unwrap();
                self.list_program();
                let result = self.vm.run();
                if result.is_err() {
                    println!("{} Runtime error: {}", ERROR_TAG, result.unwrap_err());
                }
            }
            Err(errors) => {
                println!("{} Errors encountered while assembling:", ERROR_TAG);
                for error in errors {
                    println!("  {}", error);
                }
            }
        }
    }
}

impl Default for REPL {
    fn default() -> Self {
        REPL::new()
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::{self, Read};

use hyper::header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding};
use hyper::status::StatusCode;
use hyper::version::HttpVersion;
use hyper::Url;
use libflate::gzip;
use serde::Deserialize;
use serde_json;

/// A Response to a submitted `Request`.
pub struct Response {
    inner: Decoder,
}

pub fn new(res: ::hyper::client::Response, gzip: bool) -> Response {
    Response {
        inner: Decoder::from_hyper_response(res, gzip)
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => {
                f.debug_struct("Response")
                    .field("url", &hyper_response.url)
                    .field("status", &hyper_response.status)
                    .field("headers", &hyper_response.headers)
                    .field("version", &hyper_response.version)
                    .finish()
            },
            Decoder::Gzip{ ref head, .. } |
            Decoder::Errored { ref head, .. } => {
                f.debug_struct("Response")
                    .field("url", &head.url)
                    .field("status", &head.status)
                    .field("headers", &head.headers)
                    .field("version", &head.version)
                    .finish()
            }
        }
    }
}

impl Response {
    /// Get the final `Url` of this response.
    #[inline]
    pub fn url(&self) -> &Url {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.url,
            Decoder::Gzip{ ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.url,
        }
    }

    /// Get the `StatusCode`.
    #[inline]
    pub fn status(&self) -> &StatusCode {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.status,
            Decoder::Gzip{ ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.status,
        }
    }

    /// Get the `Headers`.
    #[inline]
    pub fn headers(&self) -> &Headers {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.headers,
            Decoder::Gzip{ ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.headers,
        }
    }

    /// Get the `HttpVersion`.
    #[inline]
    pub fn version(&self) -> &HttpVersion {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.version,
            Decoder::Gzip{ ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.version,
        }
    }

    /// Try and deserialize the response body as JSON.
    #[inline]
    pub fn json<T: Deserialize>(&mut self) -> ::Result<T> {
        serde_json::from_reader(self).map_err(::error::from)
    }
}

enum Decoder {
    /// A `PlainText` decoder just returns the response content as is.
    PlainText(::hyper::client::Response),
    /// A `Gzip` decoder will uncompress the gziped response content before returning it.
    Gzip {
        decoder: gzip::Decoder<Peeked>,
        head: Head,
    },
    /// An error occured reading the Gzip header, so return that error
    /// when the user tries to read on the `Response`.
    Errored {
        err: Option<io::Error>,
        head: Head,
    }
}

impl Decoder {
    /// Constructs a Decoder from a hyper request.
    ///
    /// A decoder is just a wrapper around the hyper request that knows
    /// how to decode the content body of the request.
    ///
    /// Uses the correct variant by inspecting the Content-Encoding header.
    fn from_hyper_response(mut res: ::hyper::client::Response, check_gzip: bool) -> Self {
        if !check_gzip {
            return Decoder::PlainText(res);
        }
        let content_encoding_gzip: bool;
        let mut is_gzip = {
            content_encoding_gzip = res.headers.get::<ContentEncoding>().map_or(false, |encs|{
                encs.contains(&Encoding::Gzip)
            });
            content_encoding_gzip || res.headers.get::<TransferEncoding>().map_or(false, |encs|{
                encs.contains(&Encoding::Gzip)
            })
        };
        if is_gzip {
            if let Some(content_length) = res.headers.get::<ContentLength>() {
                if content_length.0 == 0 {
                    warn!("GZipped response with content-length of 0");
                    is_gzip = false;
                }
            }
        }
        if content_encoding_gzip {
            res.headers.remove::<ContentEncoding>();
            res.headers.remove::<ContentLength>();
        }
        if is_gzip {
            new_gzip(res)
        } else {
            Decoder::PlainText(res)
        }
    }
}

fn new_gzip(mut res: ::hyper::client::Response) -> Decoder {
    // libflate does a read_exact([0; 2]), so its impossible to tell
    // if the stream was empty, or truly had an UnexpectedEof.
    // Therefore, we need to peek a byte to make check for EOF first.
    let mut peek = [0];
    match res.read(&mut peek) {
        Ok(0) => return Decoder::PlainText(res),
        Ok(n) => {
            debug_assert_eq!(n, 1);
        },
        Err(e) => return Decoder::Errored {
            err: Some(e),
            head: Head {
                headers: res.headers.clone(),
                status: res.status,
                url: res.url.clone(),
                version: res.version,
            }
        },
    }

    let head = Head {
        headers: res.headers.clone(),
        status: res.status,
        url: res.url.clone(),
        version: res.version,
    };

    let reader = Peeked {
        peeked: Some(peek[0]),
        inner: res,
    };
    match gzip::Decoder::new(reader) {
        Ok(gzip) => Decoder::Gzip {
            decoder: gzip,
            head: head,
        },
        Err(e) => Decoder::Errored {
            err: Some(e),
            head: head,
        }
    }
}

struct Head {
    headers: ::hyper::header::Headers,
    url: ::hyper::Url,
    version: ::hyper::version::HttpVersion,
    status: ::hyper::status::StatusCode,
}

struct Peeked {
    peeked: Option<u8>,
    inner: ::hyper::client::Response,
}

impl Read for Peeked {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if buf.is_empty() {
            return Ok(0);
        }
        if let Some(byte) = self.peeked.take() {
            buf[0] = byte;
            Ok(1)
        } else {
            self.inner.read(buf)
        }
    }
}

impl Read for Decoder {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Decoder::PlainText(ref mut hyper_response) => {
                hyper_response.read(buf)
            },
            Decoder::Gzip{ref mut decoder, ..} => {
                decoder.read(buf)
            },
            Decoder::Errored { ref mut err, .. } => {
                Err(err.take().unwrap_or_else(previously_errored))
            }
        }
    }
}

#[inline]
fn previously_errored() -> io::Error {
    io::Error::new(io::ErrorKind::Other, "permanently errored")
}

/// Read the body of the Response.
impl Read for Response {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

<commit_msg>Change trait to DeserializeOwned<commit_after>use std::fmt;
use std::io::{self, Read};

use hyper::header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding};
use hyper::status::StatusCode;
use hyper::version::HttpVersion;
use hyper::Url;
use libflate::gzip;
use serde::de::DeserializeOwned;
use serde_json;

/// A Response to a submitted `Request`.
pub struct Response {
    inner: Decoder,
}

pub fn new(res: ::hyper::client::Response, gzip: bool) -> Response {
    Response {
        inner: Decoder::from_hyper_response(res, gzip)
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => {
                f.debug_struct("Response")
                    .field("url", &hyper_response.url)
                    .field("status", &hyper_response.status)
                    .field("headers", &hyper_response.headers)
                    .field("version", &hyper_response.version)
                    .finish()
            },
            Decoder::Gzip{ ref head, .. } |
            Decoder::Errored { ref head, .. } => {
                f.debug_struct("Response")
                    .field("url", &head.url)
                    .field("status", &head.status)
                    .field("headers", &head.headers)
                    .field("version", &head.version)
                    .finish()
            }
        }
    }
}

impl Response {
    /// Get the final `Url` of this response.
    #[inline]
    pub fn url(&self) -> &Url {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.url,
            Decoder::Gzip{ ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.url,
        }
    }

    /// Get the `StatusCode`.
    #[inline]
    pub fn status(&self) -> &StatusCode {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.status,
            Decoder::Gzip{ ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.status,
        }
    }

    /// Get the `Headers`.
    #[inline]
    pub fn headers(&self) -> &Headers {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.headers,
            Decoder::Gzip{ ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.headers,
        }
    }

    /// Get the `HttpVersion`.
    #[inline]
    pub fn version(&self) -> &HttpVersion {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.version,
            Decoder::Gzip{ ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.version,
        }
    }

    /// Try and deserialize the response body as JSON.
    #[inline]
    pub fn json<T: DeserializeOwned>(&mut self) -> ::Result<T> {
        serde_json::from_reader(self).map_err(::error::from)
    }
}

enum Decoder {
    /// A `PlainText` decoder just returns the response content as is.
    PlainText(::hyper::client::Response),
    /// A `Gzip` decoder will uncompress the gziped response content before returning it.
    Gzip {
        decoder: gzip::Decoder<Peeked>,
        head: Head,
    },
    /// An error occured reading the Gzip header, so return that error
    /// when the user tries to read on the `Response`.
    Errored {
        err: Option<io::Error>,
        head: Head,
    }
}

impl Decoder {
    /// Constructs a Decoder from a hyper request.
    ///
    /// A decoder is just a wrapper around the hyper request that knows
    /// how to decode the content body of the request.
    ///
    /// Uses the correct variant by inspecting the Content-Encoding header.
    fn from_hyper_response(mut res: ::hyper::client::Response, check_gzip: bool) -> Self {
        if !check_gzip {
            return Decoder::PlainText(res);
        }
        let content_encoding_gzip: bool;
        let mut is_gzip = {
            content_encoding_gzip = res.headers.get::<ContentEncoding>().map_or(false, |encs|{
                encs.contains(&Encoding::Gzip)
            });
            content_encoding_gzip || res.headers.get::<TransferEncoding>().map_or(false, |encs|{
                encs.contains(&Encoding::Gzip)
            })
        };
        if is_gzip {
            if let Some(content_length) = res.headers.get::<ContentLength>() {
                if content_length.0 == 0 {
                    warn!("GZipped response with content-length of 0");
                    is_gzip = false;
                }
            }
        }
        if content_encoding_gzip {
            res.headers.remove::<ContentEncoding>();
            res.headers.remove::<ContentLength>();
        }
        if is_gzip {
            new_gzip(res)
        } else {
            Decoder::PlainText(res)
        }
    }
}

fn new_gzip(mut res: ::hyper::client::Response) -> Decoder {
    // libflate does a read_exact([0; 2]), so its impossible to tell
    // if the stream was empty, or truly had an UnexpectedEof.
    // Therefore, we need to peek a byte to make check for EOF first.
    let mut peek = [0];
    match res.read(&mut peek) {
        Ok(0) => return Decoder::PlainText(res),
        Ok(n) => {
            debug_assert_eq!(n, 1);
        },
        Err(e) => return Decoder::Errored {
            err: Some(e),
            head: Head {
                headers: res.headers.clone(),
                status: res.status,
                url: res.url.clone(),
                version: res.version,
            }
        },
    }

    let head = Head {
        headers: res.headers.clone(),
        status: res.status,
        url: res.url.clone(),
        version: res.version,
    };

    let reader = Peeked {
        peeked: Some(peek[0]),
        inner: res,
    };
    match gzip::Decoder::new(reader) {
        Ok(gzip) => Decoder::Gzip {
            decoder: gzip,
            head: head,
        },
        Err(e) => Decoder::Errored {
            err: Some(e),
            head: head,
        }
    }
}

struct Head {
    headers: ::hyper::header::Headers,
    url: ::hyper::Url,
    version: ::hyper::version::HttpVersion,
    status: ::hyper::status::StatusCode,
}

struct Peeked {
    peeked: Option<u8>,
    inner: ::hyper::client::Response,
}

impl Read for Peeked {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if buf.is_empty() {
            return Ok(0);
        }
        if let Some(byte) = self.peeked.take() {
            buf[0] = byte;
            Ok(1)
        } else {
            self.inner.read(buf)
        }
    }
}

impl Read for Decoder {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Decoder::PlainText(ref mut hyper_response) => {
                hyper_response.read(buf)
            },
            Decoder::Gzip{ref mut decoder, ..} => {
                decoder.read(buf)
            },
            Decoder::Errored { ref mut err, .. } => {
                Err(err.take().unwrap_or_else(previously_errored))
            }
        }
    }
}

#[inline]
fn previously_errored() -> io::Error {
    io::Error::new(io::ErrorKind::Other, "permanently errored")
}

/// Read the body of the Response.
impl Read for Response {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

<|endoftext|>


<commit_before>use blas::{ToBlasInt, Vector, mod};
use traits::{Collection, Matrix, MatrixCols, MatrixRows, ToOwned};
use {Col, Mat, MutView, Row, Trans, View};

impl<T, V> ToOwned<Box<[T]>> for V where T: blas::Copy, V: Vector<T> {
    fn to_owned(&self) -> Box<[T]> {
        let n = Collection::len(self);

        if n == 0 { return box [] }

        let copy = blas::Copy::copy(None::<T>);
        let x = self.as_ptr();
        let incx = self.stride();
        let mut data = Vec::with_capacity(n);
        let y = data.as_mut_ptr();
        let incy = 1;

        unsafe {
            copy(&n.to_blasint(), x, &incx, y, &incy);
            data.set_len(n)
        }

        data.into_boxed_slice()
    }
}

impl<T, V> ToOwned<Col<Box<[T]>>> for Col<V> where T: blas::Copy, V: Vector<T> {
    fn to_owned(&self) -> Col<Box<[T]>> {
        Col(self.0.to_owned())
    }
}

impl<T, V> ToOwned<Row<Box<[T]>>> for Row<V> where T: blas::Copy, V: Vector<T> {
    fn to_owned(&self) -> Row<Box<[T]>> {
        Row(self.0.to_owned())
    }
}

impl<T> ToOwned<Mat<T>> for Mat<T> where T: blas::Copy {
    fn to_owned(&self) -> Mat<T> {
        Mat {
            data: self.data.to_owned(),
            size: self.size,
        }
    }
}

macro_rules! to_owned {
    () => {
        fn to_owned(&self) -> Mat<T> {
            // XXX Should this use `checked_mul`?
            let n = self.nrows() * self.ncols();

            if n == 0 {
                return Mat {
                    data: box [],
                    size: self.size(),
                }
            }

            let mut data = Vec::with_capacity(n);

            let copy = blas::Copy::copy(None::<T>);

            if self.nrows() < self.ncols() {
                let n = self.ncols().to_blasint();
                let incy = self.nrows().to_blasint();

                for (i, row) in self.rows().enumerate() {
                    let x = row.0.as_ptr();
                    let incx = row.0.stride();
                    let y = unsafe { data.as_mut_ptr().offset(i as int) };

                    unsafe { copy(&n, x, &incx, y, &incy) }
                }
            } else {
                let n = self.nrows().to_blasint();
                let incy = 1;

                for (i, col) in self.cols().enumerate() {
                    let x = col.0.as_ptr();
                    let incx = col.0.stride();
                    let y = unsafe { data.as_mut_ptr().offset((i * self.nrows()) as int) };

                    unsafe { copy(&n, x, &incx, y, &incy) }
                }
            }

            unsafe { data.set_len(n) }

            Mat {
                data: data.into_boxed_slice(),
                size: self.size(),
            }
        }
    }
}

impl<T> ToOwned<Mat<T>> for Trans<Mat<T>> where T: blas::Copy {
    to_owned!()
}

impl<'a, T> ToOwned<Mat<T>> for View<'a, T> where T: blas::Copy {
    to_owned!()
}

impl<'a, T> ToOwned<Mat<T>> for MutView<'a, T> where T: blas::Copy {
    to_owned!()
}

impl<'a, T> ToOwned<Mat<T>> for Trans<View<'a, T>> where T: blas::Copy {
    to_owned!()
}

impl<'a, T> ToOwned<Mat<T>> for Trans<MutView<'a, T>> where T: blas::Copy {
    to_owned!()
}
<commit_msg>refactor(ToOwned): to not expose `blas::Vector` bounds<commit_after>use blas::{ToBlasInt, Vector, mod};
use strided;
use traits::{Collection, Matrix, MatrixCols, MatrixRows, ToOwned};
use {Col, Mat, MutView, Row, Trans, View};

fn to<T, V>(v: &V) -> Box<[T]> where V: Vector<T>, T: blas::Copy {
    let n = Collection::len(v);

    if n == 0 { return box [] }

    let copy = blas::Copy::copy(None::<T>);
    let x = v.as_ptr();
    let incx = v.stride();
    let mut data = Vec::with_capacity(n);
    let y = data.as_mut_ptr();
    let incy = 1;

    unsafe {
        copy(&n.to_blasint(), x, &incx, y, &incy);
        data.set_len(n)
    }

    data.into_boxed_slice()
}

macro_rules! impls {
    ($($ty:ty),+) => {$(
        impl<'a, T> ToOwned<Col<Box<[T]>>> for Col<$ty> where T: blas::Copy {
            fn to_owned(&self) -> Col<Box<[T]>> {
                Col(to(&self.0))
            }
        }

        impl<'a, T> ToOwned<Row<Box<[T]>>> for Row<$ty> where T: blas::Copy {
            fn to_owned(&self) -> Row<Box<[T]>> {
                Row(to(&self.0))
            }
        })+
    }
}

impls!(&'a [T], &'a mut [T], strided::Slice<'a, T>, strided::MutSlice<'a, T>)

impl<'a, T> ToOwned<Col<Box<[T]>>> for Col<Box<[T]>> where T: blas::Copy {
    fn to_owned(&self) -> Col<Box<[T]>> {
        Col(to(&self.0))
    }
}

impl<'a, T> ToOwned<Row<Box<[T]>>> for Row<Box<[T]>> where T: blas::Copy {
    fn to_owned(&self) -> Row<Box<[T]>> {
        Row(to(&self.0))
    }
}

impl<T> ToOwned<Mat<T>> for Mat<T> where T: blas::Copy {
    fn to_owned(&self) -> Mat<T> {
        Mat {
            data: to(&self.data),
            size: self.size,
        }
    }
}

macro_rules! to_owned {
    () => {
        fn to_owned(&self) -> Mat<T> {
            // XXX Should this use `checked_mul`?
            let n = self.nrows() * self.ncols();

            if n == 0 {
                return Mat {
                    data: box [],
                    size: self.size(),
                }
            }

            let mut data = Vec::with_capacity(n);

            let copy = blas::Copy::copy(None::<T>);

            if self.nrows() < self.ncols() {
                let n = self.ncols().to_blasint();
                let incy = self.nrows().to_blasint();

                for (i, row) in self.rows().enumerate() {
                    let x = row.0.as_ptr();
                    let incx = row.0.stride();
                    let y = unsafe { data.as_mut_ptr().offset(i as int) };

                    unsafe { copy(&n, x, &incx, y, &incy) }
                }
            } else {
                let n = self.nrows().to_blasint();
                let incy = 1;

                for (i, col) in self.cols().enumerate() {
                    let x = col.0.as_ptr();
                    let incx = col.0.stride();
                    let y = unsafe { data.as_mut_ptr().offset((i * self.nrows()) as int) };

                    unsafe { copy(&n, x, &incx, y, &incy) }
                }
            }

            unsafe { data.set_len(n) }

            Mat {
                data: data.into_boxed_slice(),
                size: self.size(),
            }
        }
    }
}

impl<T> ToOwned<Mat<T>> for Trans<Mat<T>> where T: blas::Copy {
    to_owned!()
}

impl<'a, T> ToOwned<Mat<T>> for View<'a, T> where T: blas::Copy {
    to_owned!()
}

impl<'a, T> ToOwned<Mat<T>> for MutView<'a, T> where T: blas::Copy {
    to_owned!()
}

impl<'a, T> ToOwned<Mat<T>> for Trans<View<'a, T>> where T: blas::Copy {
    to_owned!()
}

impl<'a, T> ToOwned<Mat<T>> for Trans<MutView<'a, T>> where T: blas::Copy {
    to_owned!()
}
<|endoftext|>


<commit_before>

/**
 * A deque, for fun.  Untested as of yet.  Likely buggy.
 */
type t[T] =
    obj {
        fn size() -> uint ;
        fn add_front(&T) ;
        fn add_back(&T) ;
        fn pop_front() -> T ;
        fn pop_back() -> T ;
        fn peek_front() -> T ;
        fn peek_back() -> T ;
        fn get(int) -> T ;
    };

fn create[T]() -> t[T] {
    type cell[T] = option::t[T];

    let uint initial_capacity = 32u; // 2^5
     /**
      * Grow is only called on full elts, so nelts is also len(elts), unlike
      * elsewhere.
      */

    fn grow[T](uint nelts, uint lo, vec[mutable cell[T]] elts) ->
       vec[mutable cell[T]] {
        assert (nelts == vec::len(elts));
        // FIXME: Making the vector argument an alias is a workaround for
        // issue #375

        fn fill[T](uint i, uint nelts, uint lo, &vec[mutable cell[T]] old) ->
           cell[T] {
            ret if (i < nelts) {
                    old.((lo + i) % nelts)
                } else { option::none };
        }
        let uint nalloc = uint::next_power_of_two(nelts + 1u);
        let vec::init_op[cell[T]] copy_op = bind fill[T](_, nelts, lo, elts);
        ret vec::init_fn_mut[cell[T]](copy_op, nalloc);
    }
    fn get[T](vec[mutable cell[T]] elts, uint i) -> T {
        ret alt (elts.(i)) {
                case (option::some(?t)) { t }
                case (_) { fail }
            };
    }
    obj deque[T](mutable uint nelts,
                 mutable uint lo,
                 mutable uint hi,
                 mutable vec[mutable cell[T]] elts) {
        fn size() -> uint { ret nelts; }
        fn add_front(&T t) {
            let uint oldlo = lo;
            if (lo == 0u) {
                lo = vec::len[cell[T]](elts) - 1u;
            } else { lo -= 1u; }
            if (lo == hi) {
                elts = grow[T](nelts, oldlo, elts);
                lo = vec::len[cell[T]](elts) - 1u;
                hi = nelts;
            }
            elts.(lo) = option::some[T](t);
            nelts += 1u;
        }
        fn add_back(&T t) {
            if (lo == hi && nelts != 0u) {
                elts = grow[T](nelts, lo, elts);
                lo = 0u;
                hi = nelts;
            }
            elts.(hi) = option::some[T](t);
            hi = (hi + 1u) % vec::len[cell[T]](elts);
            nelts += 1u;
        }

        /**
         * We actually release (turn to none()) the T we're popping so
         * that we don't keep anyone's refcount up unexpectedly.
         */
        fn pop_front() -> T {
            let T t = get[T](elts, lo);
            elts.(lo) = option::none[T];
            lo = (lo + 1u) % vec::len[cell[T]](elts);
            nelts -= 1u;
            ret t;
        }
        fn pop_back() -> T {
            if (hi == 0u) {
                hi = vec::len[cell[T]](elts) - 1u;
            } else { hi -= 1u; }
            let T t = get[T](elts, hi);
            elts.(hi) = option::none[T];
            nelts -= 1u;
            ret t;
        }
        fn peek_front() -> T { ret get[T](elts, lo); }
        fn peek_back() -> T { ret get[T](elts, hi - 1u); }
        fn get(int i) -> T {
            let uint idx = (lo + (i as uint)) % vec::len[cell[T]](elts);
            ret get[T](elts, idx);
        }
    }
    let vec[mutable cell[T]] v =
        vec::init_elt_mut(option::none, initial_capacity);
    ret deque[T](0u, 0u, 0u, v);
}
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>stdlib: Switch lib::deque over to interior vectors<commit_after>

/**
 * A deque, for fun.  Untested as of yet.  Likely buggy.
 */
type t[T] =
    obj {
        fn size() -> uint ;
        fn add_front(&T) ;
        fn add_back(&T) ;
        fn pop_front() -> T ;
        fn pop_back() -> T ;
        fn peek_front() -> T ;
        fn peek_back() -> T ;
        fn get(int) -> T ;
    };

fn create[T]() -> t[T] {
    type cell[T] = option::t[T];

    let uint initial_capacity = 32u; // 2^5
     /**
      * Grow is only called on full elts, so nelts is also len(elts), unlike
      * elsewhere.
      */

    fn grow[T](uint nelts, uint lo, &(cell[T])[mutable] elts)
            -> (cell[T])[mutable] {
        assert (nelts == ivec::len(elts));
        auto rv = ~[mutable];

        auto i = 0u;
        auto nalloc = uint::next_power_of_two(nelts + 1u);
        while (i < nalloc) {
            if (i < nelts) {
                rv += ~[mutable elts.((lo + i) % nelts)];
            } else {
                rv += ~[mutable option::none];
            }
            i += 1u;
        }

        ret rv;
    }
    fn get[T](&(cell[T])[mutable] elts, uint i) -> T {
        ret alt (elts.(i)) {
                case (option::some(?t)) { t }
                case (_) { fail }
            };
    }
    obj deque[T](mutable uint nelts,
                 mutable uint lo,
                 mutable uint hi,
                 mutable (cell[T])[mutable] elts) {
        fn size() -> uint { ret nelts; }
        fn add_front(&T t) {
            let uint oldlo = lo;
            if (lo == 0u) {
                lo = ivec::len[cell[T]](elts) - 1u;
            } else { lo -= 1u; }
            if (lo == hi) {
                elts = grow[T](nelts, oldlo, elts);
                lo = ivec::len[cell[T]](elts) - 1u;
                hi = nelts;
            }
            elts.(lo) = option::some[T](t);
            nelts += 1u;
        }
        fn add_back(&T t) {
            if (lo == hi && nelts != 0u) {
                elts = grow[T](nelts, lo, elts);
                lo = 0u;
                hi = nelts;
            }
            elts.(hi) = option::some[T](t);
            hi = (hi + 1u) % ivec::len[cell[T]](elts);
            nelts += 1u;
        }

        /**
         * We actually release (turn to none()) the T we're popping so
         * that we don't keep anyone's refcount up unexpectedly.
         */
        fn pop_front() -> T {
            let T t = get[T](elts, lo);
            elts.(lo) = option::none[T];
            lo = (lo + 1u) % ivec::len[cell[T]](elts);
            nelts -= 1u;
            ret t;
        }
        fn pop_back() -> T {
            if (hi == 0u) {
                hi = ivec::len[cell[T]](elts) - 1u;
            } else {
                hi -= 1u;
            }
            let T t = get[T](elts, hi);
            elts.(hi) = option::none[T];
            nelts -= 1u;
            ret t;
        }
        fn peek_front() -> T { ret get[T](elts, lo); }
        fn peek_back() -> T { ret get[T](elts, hi - 1u); }
        fn get(int i) -> T {
            let uint idx = (lo + (i as uint)) % ivec::len[cell[T]](elts);
            ret get[T](elts, idx);
        }
    }
    let (cell[T])[mutable] v =
        ivec::init_elt_mut(option::none, initial_capacity);
    ret deque[T](0u, 0u, 0u, v);
}
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>use lisp::lex::{tokenize, parse_tree_from_tokens};
use lisp::cell::Cell;
use lisp::env::Environment;

pub fn eval(token: Cell, env: &mut Environment) -> Cell {
    match token {
        Cell::Symbol(name) => {
            match *env.map.get(&name).unwrap() {
                Cell::Number(n) => Cell::Number(n),
                Cell::Symbol(ref n) => Cell::Symbol(n.to_string()),
                _ => Cell::Nil,
            }
        }
        Cell::Number(n) => Cell::Number(n),
        Cell::List(mut tokens) => {
            // This branch does some heavy lifting
            // match on the first token then take action
            let first = tokens.remove(0);

            if let Cell::Symbol(name) = first {

                if let Some(&Cell::Proc(f)) = env.map.get(&name) {
                    // eval each of the following tokens in the list
                    let mut args: Vec<Cell> = Vec::new();

                    for arg in tokens {
                        args.push(eval(arg, env));
                    }
                    // executes the function with evaluated arguments
                    f(Cell::List(args))

                } else {
                    // TODO: some keyword implementations here
                    let result: Cell = match name.as_ref() {
                        "quote" => {
                            // returns the following token literally (should it be converted to a symbol?)
                            tokens.remove(0)
                        }
                        "if" => Cell::Nil,
                        "set!" => {
                            // usage: set! var_name expression
                            if let Cell::Symbol(name) = tokens.remove(0) {
                                let t = eval(tokens.remove(0), env);
                                env.map.insert(name, t);
                            }

                            Cell::Nil
                        }

                        _ => Cell::Nil,
                    };

                    result
                }
            } else {
                // first is not a symbol, it's a regular list
                // TODO: should put back the first element
                tokens.insert(0, first);
                Cell::List(tokens)
            }
        }

        _ => panic!("Unrecognized token"), //Cell::Nil
    }
}

// Utility function
pub fn parse_and_eval(input: &str, env: &mut Environment) -> Cell {
    let mut str_vec = tokenize(input);
    let tokens = parse_tree_from_tokens(&mut str_vec);
    eval(tokens.unwrap(), env)
}

#[test]
fn eval_returns_pi() {
    let t = Cell::Symbol("pi".to_string());
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 3.14159265);
}

#[test]
fn eval_sum() {
    let args = vec![Cell::Symbol("+".to_string()), Cell::Number(2.0), Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 3.0);

    let args = vec![Cell::Symbol("+".to_string()),
                    Cell::Number(3.0),
                    Cell::Number(2.0),
                    Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 6.0);
}

#[test]
fn eval_sub() {
    let args = vec![Cell::Symbol("-".to_string()),
                    Cell::Number(3.0),
                    Cell::Number(2.0),
                    Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => -10.0,
    };

    assert_eq!(res, 0.0);
}
<commit_msg>Implemented 'if' keyword, refactored keywords<commit_after>use lisp::lex::{tokenize, parse_tree_from_tokens};
use lisp::cell::Cell;
use lisp::env::Environment;

// TODO: eval is getting big, refactor me maybe?
pub fn eval(token: Cell, env: &mut Environment) -> Cell {
    match token {
        Cell::Symbol(name) => {
            match *env.map.get(&name).unwrap() {
                Cell::Number(n) => Cell::Number(n),
                Cell::Symbol(ref n) => Cell::Symbol(n.to_string()),
                _ => Cell::Nil,
            }
        }
        Cell::Number(n) => Cell::Number(n),
        Cell::List(mut tokens) => {
            // match on the first token then take action
            let first = tokens.remove(0);

            if let Cell::Symbol(name) = first {

                if let Some(&Cell::Proc(function)) = env.map.get(&name) {
                    // eager: eval each of the arguments
                    let mut args: Vec<Cell> = Vec::new();

                    for arg in tokens {
                        args.push(eval(arg, env));
                    }

                    // executes the function with arguments
                    function(Cell::List(args))
                } else {
                    // TODO: should keywords be part of eval?
                    let result = match name.as_ref() {
                        "quote" => eval_quote(&mut tokens),
                        "if" => eval_if(&mut tokens, env),
                        "set!" => eval_set(&mut tokens, env),
                        _ => Cell::Nil,
                    };

                    result
                }
            } else {
                // first is not a symbol, it's a regular list
                tokens.insert(0, first);
                Cell::List(tokens)
            }
        }

        _ => panic!("Unrecognized token"), //Cell::Nil
    }
}

fn eval_set(args: &mut Vec<Cell>, env: &mut Environment) -> Cell {
    // usage: set! var_name expression
    if let Cell::Symbol(name) = args.remove(0) {
        let t = eval(args.remove(0), env);
        env.map.insert(name, t);
    }
    // assignment expressions return Nil
    Cell::Nil
}

fn eval_quote(args: &mut Vec<Cell>) -> Cell {
    args.remove(0)
}

// implementation of "if" keyword
fn eval_if(args: &mut Vec<Cell>, env: &mut Environment) -> Cell {
    let has_else = if args.len() == 3 {
        true
    } else {
        false
    };
    let condition = args.remove(0);

    match eval(condition, env) {
        Cell::True => eval(args.remove(0), env),
        Cell::False if has_else => eval(args.remove(1), env),
        _ => Cell::Nil,
    }
}

// Utility function
pub fn parse_and_eval(input: &str, env: &mut Environment) -> Cell {
    let mut str_vec = tokenize(input);
    let tokens = parse_tree_from_tokens(&mut str_vec);
    eval(tokens.unwrap(), env)
}

// TODO: move tests in separate file
#[test]
fn eval_returns_pi() {
    let t = Cell::Symbol("pi".to_string());
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 3.14159265);
}

#[test]
fn eval_sum() {
    let args = vec![Cell::Symbol("+".to_string()), Cell::Number(2.0), Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 3.0);

    let args = vec![Cell::Symbol("+".to_string()),
                    Cell::Number(3.0),
                    Cell::Number(2.0),
                    Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 6.0);
}

#[test]
fn eval_sub() {
    let args = vec![Cell::Symbol("-".to_string()),
                    Cell::Number(3.0),
                    Cell::Number(2.0),
                    Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => -10.0,
    };

    assert_eq!(res, 0.0);
}
<|endoftext|>