<commit_before>//! The parser module is responsible for parsing FITS files.

use std::str;
use std::str::FromStr;
use super::types::{PrimaryHeader, KeywordRecord, Keyword, BlankRecord};

named!(fits<&[u8], (PrimaryHeader, Vec<&[u8]>) >,
       pair!(primary_header, many0!( take!(2880) )));

named!(primary_header<&[u8], PrimaryHeader>,
       do_parse!(
           records: many0!(keyword_record) >>
               end_record >>
               many0!(blank_record) >>
               (PrimaryHeader::new(records))
       ));

named!(keyword_record<&[u8], KeywordRecord>,
       do_parse!(
           key: keyword  >>
               tag!("=") >>
               take!(71) >>
               (KeywordRecord::new(key))
       ));

named!(keyword<&[u8], Keyword>,
       map_res!(
           map_res!(
               take!(8),
               str::from_utf8),
           Keyword::from_str
       ));

named!(end_record<&[u8], Keyword>,
       map!(
           pair!(tag!("END"), count!(tag!(" "), 77)),
           |_| { Keyword::END }
       ));

named!(blank_record<&[u8], BlankRecord>,
       map!(
           count!(tag!(" "), 80),
           |_| { BlankRecord }
       ));

#[cfg(test)]
mod tests {
    use nom::{IResult};
    use super::super::types::{PrimaryHeader, KeywordRecord, Keyword, BlankRecord};
    use super::{fits, primary_header, keyword_record, keyword, end_record, blank_record};

    #[ignore]
    #[test]
    fn it_should_parse_a_fits_file(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = fits(data);

        match result {
            IResult::Done(_, (header, blocks)) => {
                assert_eq!(blocks.len(), 3675);
            },
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn primary_header_should_parse_a_primary_header(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = primary_header(&data[0..(2*2880)]);

        match result {
            IResult::Done(_, h) => assert_eq!(h, long_cadence_header()),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    fn long_cadence_header() -> PrimaryHeader {
        PrimaryHeader::new(vec!(
            KeywordRecord::new(Keyword::SIMPLE),
            KeywordRecord::new(Keyword::BITPIX),
            KeywordRecord::new(Keyword::NAXIS),
            KeywordRecord::new(Keyword::EXTEND),
            KeywordRecord::new(Keyword::NEXTEND),
            KeywordRecord::new(Keyword::EXTNAME),
            KeywordRecord::new(Keyword::EXTVER),
            KeywordRecord::new(Keyword::ORIGIN),
            KeywordRecord::new(Keyword::DATE),
            KeywordRecord::new(Keyword::CREATOR),
            KeywordRecord::new(Keyword::PROCVER),
            KeywordRecord::new(Keyword::FILEVER),
            KeywordRecord::new(Keyword::TIMVERSN),
            KeywordRecord::new(Keyword::TELESCOP),
            KeywordRecord::new(Keyword::INSTRUME),
            KeywordRecord::new(Keyword::OBJECT),
            KeywordRecord::new(Keyword::KEPLERID),
            KeywordRecord::new(Keyword::CHANNEL),
            KeywordRecord::new(Keyword::MODULE),
            KeywordRecord::new(Keyword::OUTPUT),
            KeywordRecord::new(Keyword::CAMPAIGN),
            KeywordRecord::new(Keyword::DATA_REL),
            KeywordRecord::new(Keyword::OBSMODE),
            KeywordRecord::new(Keyword::MISSION),
            KeywordRecord::new(Keyword::TTABLEID),
            KeywordRecord::new(Keyword::RADESYS),
            KeywordRecord::new(Keyword::RA_OBJ),
            KeywordRecord::new(Keyword::DEC_OBJ),
            KeywordRecord::new(Keyword::EQUINOX),
            KeywordRecord::new(Keyword::PMRA),
            KeywordRecord::new(Keyword::PMDEC),
            KeywordRecord::new(Keyword::PMTOTAL),
            KeywordRecord::new(Keyword::PARALLAX),
            KeywordRecord::new(Keyword::GLON),
            KeywordRecord::new(Keyword::GLAT),
            KeywordRecord::new(Keyword::GMAG),
            KeywordRecord::new(Keyword::RMAG),
            KeywordRecord::new(Keyword::IMAG),
            KeywordRecord::new(Keyword::ZMAG),
            KeywordRecord::new(Keyword::JMAG),
            KeywordRecord::new(Keyword::HMAG),
            KeywordRecord::new(Keyword::KMAG),
            KeywordRecord::new(Keyword::KEPMAG),
            KeywordRecord::new(Keyword::GRCOLOR),
            KeywordRecord::new(Keyword::JKCOLOR),
            KeywordRecord::new(Keyword::GKCOLOR),
            KeywordRecord::new(Keyword::TEFF),
            KeywordRecord::new(Keyword::LOGG),
            KeywordRecord::new(Keyword::FEH),
            KeywordRecord::new(Keyword::EBMINUSV),
            KeywordRecord::new(Keyword::AV),
            KeywordRecord::new(Keyword::RADIUS),
            KeywordRecord::new(Keyword::TMINDEX),
            KeywordRecord::new(Keyword::CHECKSUM),
            KeywordRecord::new(Keyword::DATASUM),
        ))
    }

    #[test]
    fn keyword_record_should_parse_a_keyword_record(){
        let data = "OBJECT  = 'EPIC 200164267'     / string version of target id                    "
            .as_bytes();

        let result = keyword_record(data);

        match result {
            IResult::Done(_,k) => assert_eq!(k, KeywordRecord::new(Keyword::OBJECT)),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn keyword_should_parse_a_keyword(){
        let data = "OBJECT  "
            .as_bytes();

        let result = keyword(data);

        match result {
            IResult::Done(_, keyword) => assert_eq!(keyword, Keyword::OBJECT),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn end_record_should_parse_an_END_record(){
        let data = "END                                                                             "
            .as_bytes();

        let result = end_record(data);

        match result {
            IResult::Done(_, keyword) => assert_eq!(keyword, Keyword::END),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn blank_record_should_parse_a_BLANK_record(){
        let data = "                                                                                "
            .as_bytes();

        let result = blank_record(data);

        match result {
            IResult::Done(_, record) => assert_eq!(record, BlankRecord),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }
}
<commit_msg>Parse into an actual Fits structure<commit_after>//! The parser module is responsible for parsing FITS files.

use std::str;
use std::str::FromStr;
use super::types::{Fits, PrimaryHeader, KeywordRecord, Keyword, BlankRecord};

named!(fits<&[u8], Fits>,
       do_parse!(
           ph: primary_header >>
               many0!(take!(2880)) >>
               (Fits::new(ph))
       ));

named!(primary_header<&[u8], PrimaryHeader>,
       do_parse!(
           records: many0!(keyword_record) >>
               end_record >>
               many0!(blank_record) >>
               (PrimaryHeader::new(records))
       ));

named!(keyword_record<&[u8], KeywordRecord>,
       do_parse!(
           key: keyword  >>
               tag!("=") >>
               take!(71) >>
               (KeywordRecord::new(key))
       ));

named!(keyword<&[u8], Keyword>,
       map_res!(
           map_res!(
               take!(8),
               str::from_utf8),
           Keyword::from_str
       ));

named!(end_record<&[u8], Keyword>,
       map!(
           pair!(tag!("END"), count!(tag!(" "), 77)),
           |_| { Keyword::END }
       ));

named!(blank_record<&[u8], BlankRecord>,
       map!(
           count!(tag!(" "), 80),
           |_| { BlankRecord }
       ));

#[cfg(test)]
mod tests {
    use nom::{IResult};
    use super::super::types::{Fits, PrimaryHeader, KeywordRecord, Keyword, BlankRecord};
    use super::{fits, primary_header, keyword_record, keyword, end_record, blank_record};

    #[test]
    fn it_should_parse_a_fits_file(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = fits(data);

        match result {
            IResult::Done(_, f) => {
                assert_eq!(f, Fits::new(long_cadence_header()));
            },
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn primary_header_should_parse_a_primary_header(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = primary_header(&data[0..(2*2880)]);

        match result {
            IResult::Done(_, h) => assert_eq!(h, long_cadence_header()),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    fn long_cadence_header() -> PrimaryHeader {
        PrimaryHeader::new(vec!(
            KeywordRecord::new(Keyword::SIMPLE),
            KeywordRecord::new(Keyword::BITPIX),
            KeywordRecord::new(Keyword::NAXIS),
            KeywordRecord::new(Keyword::EXTEND),
            KeywordRecord::new(Keyword::NEXTEND),
            KeywordRecord::new(Keyword::EXTNAME),
            KeywordRecord::new(Keyword::EXTVER),
            KeywordRecord::new(Keyword::ORIGIN),
            KeywordRecord::new(Keyword::DATE),
            KeywordRecord::new(Keyword::CREATOR),
            KeywordRecord::new(Keyword::PROCVER),
            KeywordRecord::new(Keyword::FILEVER),
            KeywordRecord::new(Keyword::TIMVERSN),
            KeywordRecord::new(Keyword::TELESCOP),
            KeywordRecord::new(Keyword::INSTRUME),
            KeywordRecord::new(Keyword::OBJECT),
            KeywordRecord::new(Keyword::KEPLERID),
            KeywordRecord::new(Keyword::CHANNEL),
            KeywordRecord::new(Keyword::MODULE),
            KeywordRecord::new(Keyword::OUTPUT),
            KeywordRecord::new(Keyword::CAMPAIGN),
            KeywordRecord::new(Keyword::DATA_REL),
            KeywordRecord::new(Keyword::OBSMODE),
            KeywordRecord::new(Keyword::MISSION),
            KeywordRecord::new(Keyword::TTABLEID),
            KeywordRecord::new(Keyword::RADESYS),
            KeywordRecord::new(Keyword::RA_OBJ),
            KeywordRecord::new(Keyword::DEC_OBJ),
            KeywordRecord::new(Keyword::EQUINOX),
            KeywordRecord::new(Keyword::PMRA),
            KeywordRecord::new(Keyword::PMDEC),
            KeywordRecord::new(Keyword::PMTOTAL),
            KeywordRecord::new(Keyword::PARALLAX),
            KeywordRecord::new(Keyword::GLON),
            KeywordRecord::new(Keyword::GLAT),
            KeywordRecord::new(Keyword::GMAG),
            KeywordRecord::new(Keyword::RMAG),
            KeywordRecord::new(Keyword::IMAG),
            KeywordRecord::new(Keyword::ZMAG),
            KeywordRecord::new(Keyword::JMAG),
            KeywordRecord::new(Keyword::HMAG),
            KeywordRecord::new(Keyword::KMAG),
            KeywordRecord::new(Keyword::KEPMAG),
            KeywordRecord::new(Keyword::GRCOLOR),
            KeywordRecord::new(Keyword::JKCOLOR),
            KeywordRecord::new(Keyword::GKCOLOR),
            KeywordRecord::new(Keyword::TEFF),
            KeywordRecord::new(Keyword::LOGG),
            KeywordRecord::new(Keyword::FEH),
            KeywordRecord::new(Keyword::EBMINUSV),
            KeywordRecord::new(Keyword::AV),
            KeywordRecord::new(Keyword::RADIUS),
            KeywordRecord::new(Keyword::TMINDEX),
            KeywordRecord::new(Keyword::CHECKSUM),
            KeywordRecord::new(Keyword::DATASUM),
        ))
    }

    #[test]
    fn keyword_record_should_parse_a_keyword_record(){
        let data = "OBJECT  = 'EPIC 200164267'     / string version of target id                    "
            .as_bytes();

        let result = keyword_record(data);

        match result {
            IResult::Done(_,k) => assert_eq!(k, KeywordRecord::new(Keyword::OBJECT)),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn keyword_should_parse_a_keyword(){
        let data = "OBJECT  "
            .as_bytes();

        let result = keyword(data);

        match result {
            IResult::Done(_, keyword) => assert_eq!(keyword, Keyword::OBJECT),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn end_record_should_parse_an_END_record(){
        let data = "END                                                                             "
            .as_bytes();

        let result = end_record(data);

        match result {
            IResult::Done(_, keyword) => assert_eq!(keyword, Keyword::END),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn blank_record_should_parse_a_BLANK_record(){
        let data = "                                                                                "
            .as_bytes();

        let result = blank_record(data);

        match result {
            IResult::Done(_, record) => assert_eq!(record, BlankRecord),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }
}
<|endoftext|>


<commit_before>// Small functions of utility

use core::SearchType::{self, ExactMatch, StartsWith};
use core::Session;
use std;
use std::cmp;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

pub fn getline(filepath: &Path, linenum: usize, session: &Session) -> String {
    let mut i = 0;
    let file = BufReader::new(session.open_file(filepath).unwrap());
    for line in file.lines() {
        //print!("{}", line);
        i += 1;
        if i == linenum {
            return line.unwrap();
        }
    }
    "not found".into()
}

pub fn is_pattern_char(c: char) -> bool {
    c.is_alphanumeric() || c.is_whitespace() || (c == '_') || (c == ':') || (c == '.')
}

pub fn is_search_expr_char(c: char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == ':') || (c == '.')
}

pub fn is_ident_char(c: char) -> bool {
    c.is_alphanumeric() || (c == '_')
}

pub fn txt_matches(stype: SearchType, needle: &str, haystack: &str) -> bool {
    match stype {
        ExactMatch => {
            let nlen = needle.len();
            let hlen = haystack.len();

            if nlen == 0 {
                return true;
            }

            // PD: switch to use .match_indices() when that stabilizes
            let mut n=0;
            while let Some(n1) = haystack[n..].find(needle) {
                n += n1;
                if (n == 0  || !is_ident_char(char_at(haystack, n-1))) &&
                    (n+nlen == hlen || !is_ident_char(char_at(haystack, n+nlen))) {
                    return true;
                }
                n += 1;
            }
            false
        },
        StartsWith => {
            if needle.is_empty() {
                return true;
            }

            // PD: switch to use .match_indices() when that stabilizes
            let mut n=0;
            while let Some(n1) = haystack[n..].find(needle) {
                n += n1;
                if n == 0  || !is_ident_char(char_at(haystack, n-1)) {
                    return true;
                }
                n += 1;
            }
            false
        }
    }
}

pub fn symbol_matches(stype: SearchType, searchstr: &str, candidate: &str) -> bool {
   match stype {
        ExactMatch => searchstr == candidate,
        StartsWith => candidate.starts_with(searchstr)
    }
}

// pub fn get_backtrace() -> String {
//     let mut m = std::old_io::MemWriter::new();
//     let s = std::rt::backtrace::write(&mut m)
//         .ok().map_or("NO backtrace".to_string(),
//                      |_| String::from_utf8_lossy(m.get_ref()).to_string());
//     return s;
// }

pub fn is_double_dot(msrc: &str, i: usize) -> bool {
    (i > 1) && &msrc[i-1..i+1] == ".."
}

#[test]
fn txt_matches_matches_stuff() {
    assert_eq!(true, txt_matches(ExactMatch, "Vec","Vec"));
    assert_eq!(true, txt_matches(StartsWith, "Vec","Vector"));
    assert_eq!(false, txt_matches(ExactMatch, "Vec","use Vector"));
    assert_eq!(true, txt_matches(StartsWith, "Vec","use Vector"));
    assert_eq!(false, txt_matches(StartsWith, "Vec","use aVector"));
    assert_eq!(true, txt_matches(ExactMatch, "Vec","use Vec"));
}

pub fn expand_ident(s: &str, pos: usize) -> (usize, usize) {
    // TODO: Would this better be an assertion ? Why are out-of-bound values getting here ?
    // They are coming from the command-line, question is, if they should be handled beforehand
    // clamp pos into allowed range
    let pos = cmp::min(s.len(), pos);
    let sb = &s[..pos];
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices().rev() {
        if !is_ident_char(c) {
            break;
        }
        start = i;
    }
    (start, pos)
}

pub fn find_ident_end(s: &str, pos: usize) -> usize {
    // find end of word
    let sa = &s[pos..];
    for (i, c) in sa.char_indices() {
        if !is_ident_char(c) {
            return pos + i;
        }
    }
    s.len()
}

#[test]
fn find_ident_end_ascii() {
    assert_eq!(5, find_ident_end("ident", 0));
    assert_eq!(6, find_ident_end("(ident)", 1));
    assert_eq!(17, find_ident_end("let an_identifier = 100;", 4));
}

#[test]
fn find_ident_end_unicode() {
    assert_eq!(7, find_ident_end("num_µs", 0));
    assert_eq!(10, find_ident_end("ends_in_µ", 0));
}

pub fn to_refs(v: &Vec<String>) -> Vec<&str> {
    v.iter().map(|s| s.as_ref()).collect()
}

pub fn find_last_str(needle: &str, mut haystack: &str) -> Option<usize> {
    let mut res = None;
    while let Some(n) = haystack.find(needle) {
        res = Some(n);
        haystack = &haystack[n+1..];
    }
    res
}

// PD: short term replacement for .char_at() function. Should be replaced once
// that stabilizes
pub fn char_at(src: &str, i: usize) -> char {
    src[i..].chars().next().unwrap()
}

// PD: short term replacement for path.exists() (PathExt trait). Replace once
// that stabilizes
pub fn path_exists<P: AsRef<Path>>(path: P) -> bool {
    is_dir(path.as_ref()) || File::open(path).is_ok()
}

// PD: short term replacement for path.is_dir() (PathExt trait). Replace once
// that stabilizes
pub fn is_dir<P: AsRef<Path>>(path: P) -> bool {
    match std::fs::metadata(path) {
        Ok(file_info) => file_info.is_dir(),
        _ => false
    }
}
<commit_msg>util: simplify path related functions<commit_after>// Small functions of utility

use core::SearchType::{self, ExactMatch, StartsWith};
use core::Session;
use std;
use std::cmp;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

pub fn getline(filepath: &Path, linenum: usize, session: &Session) -> String {
    let mut i = 0;
    let file = BufReader::new(session.open_file(filepath).unwrap());
    for line in file.lines() {
        //print!("{}", line);
        i += 1;
        if i == linenum {
            return line.unwrap();
        }
    }
    "not found".into()
}

pub fn is_pattern_char(c: char) -> bool {
    c.is_alphanumeric() || c.is_whitespace() || (c == '_') || (c == ':') || (c == '.')
}

pub fn is_search_expr_char(c: char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == ':') || (c == '.')
}

pub fn is_ident_char(c: char) -> bool {
    c.is_alphanumeric() || (c == '_')
}

pub fn txt_matches(stype: SearchType, needle: &str, haystack: &str) -> bool {
    match stype {
        ExactMatch => {
            let nlen = needle.len();
            let hlen = haystack.len();

            if nlen == 0 {
                return true;
            }

            // PD: switch to use .match_indices() when that stabilizes
            let mut n=0;
            while let Some(n1) = haystack[n..].find(needle) {
                n += n1;
                if (n == 0  || !is_ident_char(char_at(haystack, n-1))) &&
                    (n+nlen == hlen || !is_ident_char(char_at(haystack, n+nlen))) {
                    return true;
                }
                n += 1;
            }
            false
        },
        StartsWith => {
            if needle.is_empty() {
                return true;
            }

            // PD: switch to use .match_indices() when that stabilizes
            let mut n=0;
            while let Some(n1) = haystack[n..].find(needle) {
                n += n1;
                if n == 0  || !is_ident_char(char_at(haystack, n-1)) {
                    return true;
                }
                n += 1;
            }
            false
        }
    }
}

pub fn symbol_matches(stype: SearchType, searchstr: &str, candidate: &str) -> bool {
   match stype {
        ExactMatch => searchstr == candidate,
        StartsWith => candidate.starts_with(searchstr)
    }
}

// pub fn get_backtrace() -> String {
//     let mut m = std::old_io::MemWriter::new();
//     let s = std::rt::backtrace::write(&mut m)
//         .ok().map_or("NO backtrace".to_string(),
//                      |_| String::from_utf8_lossy(m.get_ref()).to_string());
//     return s;
// }

pub fn is_double_dot(msrc: &str, i: usize) -> bool {
    (i > 1) && &msrc[i-1..i+1] == ".."
}

#[test]
fn txt_matches_matches_stuff() {
    assert_eq!(true, txt_matches(ExactMatch, "Vec","Vec"));
    assert_eq!(true, txt_matches(StartsWith, "Vec","Vector"));
    assert_eq!(false, txt_matches(ExactMatch, "Vec","use Vector"));
    assert_eq!(true, txt_matches(StartsWith, "Vec","use Vector"));
    assert_eq!(false, txt_matches(StartsWith, "Vec","use aVector"));
    assert_eq!(true, txt_matches(ExactMatch, "Vec","use Vec"));
}

pub fn expand_ident(s: &str, pos: usize) -> (usize, usize) {
    // TODO: Would this better be an assertion ? Why are out-of-bound values getting here ?
    // They are coming from the command-line, question is, if they should be handled beforehand
    // clamp pos into allowed range
    let pos = cmp::min(s.len(), pos);
    let sb = &s[..pos];
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices().rev() {
        if !is_ident_char(c) {
            break;
        }
        start = i;
    }
    (start, pos)
}

pub fn find_ident_end(s: &str, pos: usize) -> usize {
    // find end of word
    let sa = &s[pos..];
    for (i, c) in sa.char_indices() {
        if !is_ident_char(c) {
            return pos + i;
        }
    }
    s.len()
}

#[test]
fn find_ident_end_ascii() {
    assert_eq!(5, find_ident_end("ident", 0));
    assert_eq!(6, find_ident_end("(ident)", 1));
    assert_eq!(17, find_ident_end("let an_identifier = 100;", 4));
}

#[test]
fn find_ident_end_unicode() {
    assert_eq!(7, find_ident_end("num_µs", 0));
    assert_eq!(10, find_ident_end("ends_in_µ", 0));
}

pub fn to_refs(v: &Vec<String>) -> Vec<&str> {
    v.iter().map(|s| s.as_ref()).collect()
}

pub fn find_last_str(needle: &str, mut haystack: &str) -> Option<usize> {
    let mut res = None;
    while let Some(n) = haystack.find(needle) {
        res = Some(n);
        haystack = &haystack[n+1..];
    }
    res
}

// PD: short term replacement for .char_at() function. Should be replaced once
// that stabilizes
pub fn char_at(src: &str, i: usize) -> char {
    src[i..].chars().next().unwrap()
}

// PD: short term replacement for path.exists() (PathExt trait). Replace once
// that stabilizes
pub fn path_exists<P: AsRef<Path>>(path: P) -> bool {
    is_dir(&path) || File::open(path).is_ok()
}

// PD: short term replacement for path.is_dir() (PathExt trait). Replace once
// that stabilizes
pub fn is_dir<P: AsRef<Path>>(path: P) -> bool {
    std::fs::metadata(path).map(|info| info.is_dir()).unwrap_or(false)
}
<|endoftext|>


<commit_before>#[derive(PartialEq, Eq, Debug)]
struct Age(u8);

struct Person<'a> {
    name: &'static str,
    parent: Option<&'a Person<'a>>,
}

impl<'a> Person<'a> {
    fn new(name: &'static str, parent: Option<&'a Person<'a>>) -> Person<'a> {
        Person{name: name, parent: parent}
    }

    fn parents_name(&self) -> Option<&'static str> {
        match self.parent {
            Some(p) => Some(p.name),
            None => None,
        }
    }
}

// This one can be elided.
fn first_five<'a>(value: &'a String) -> &'a str {
    &value[..5]
}

// Here we are promising Rust that all of the borrowed u8's
// live for the same scope.
// Q: Why do I need a lifetime here?
fn join_ages<'a, T: Iterator>(iters: T) -> Vec<&'a Age>
        where T::Item: IntoIterator<Item=&'a Age> {
    iters.flat_map(|b| b.into_iter())
         .collect()
}

#[test]
fn it_works() {
    let jane = Person::new("Jane", None);
    let tom = Person::new("Tom", Some(&jane));
    let name = "Andrew".to_owned();
    let a = 43;
    let vec_ages = vec![vec![Age(90), Age(80)],
                        vec![Age(2)]];

    let arr_ages = vec![[Age(90), Age(80)],
                        [Age(2), Age(45)]];

    assert_eq!(first_five(&name), "Andre");

    assert_eq!(tom.name, "Tom");
    assert_eq!(jane.name, "Jane");
    assert_eq!(tom.parent.unwrap().name, "Jane");
    assert_eq!(tom.parents_name(), Some("Jane"));
    assert_eq!(jane.parents_name(), None);

    assert_eq!(join_ages(vec_ages.iter()), vec![&Age(90), &Age(80), &Age(2)]);
    assert_eq!(join_ages(arr_ages.iter()), vec![&Age(90), &Age(80), &Age(2), &Age(45)]);
}

// - We are making a promise to the compiler that all of these things live for atleast the same scope.
// - We are not asking the compiler to "create a lifetime". We are just telling it to complain if we
//   don't do what we say we are doing.
// - Why? Because this allows Rust to ensure we cannot cause common memory allocation bugs. It does
//   this at compile time. It's a zero-cost abstraction because there is no runtime penalty.
// - It makes code look "uglier". But this is the wrong mindset for writing systems-level programs.
//   The emphasis here is on safety, not expressiveness.
// - Question: Why can't Rust ensure/infer/elide this scenario? Surely any reference to a Parent
//   must *always* live for the same or a bigger scope???
//   - There is an open RFC for this
//   - It may be complex to implement and/or slow down compilation
//   - Eliding in more cases may prevent newcomer Rust programmers from ever encountering
//     lifetimes, which could have a negative impact in the long run. Pedagogy!
<commit_msg>Added example with multiple input references<commit_after>#[derive(PartialEq, Eq, Debug)]
struct Age(u8);

struct Person<'a> {
    name: &'static str,
    parent: Option<&'a Person<'a>>,
}

impl<'a> Person<'a> {
    fn new(name: &'static str, parent: Option<&'a Person<'a>>) -> Person<'a> {
        Person{name: name, parent: parent}
    }

    fn parents_name(&self) -> Option<&'static str> {
        match self.parent {
            Some(p) => Some(p.name),
            None => None,
        }
    }
}

// This one can be elided.
fn first_five<'a>(value: &'a String) -> &'a str {
    &value[..5]
}

// This cannot be elided because Rust doesn't know to use lifetime of "a" or "b".
fn double_first_five<'a>(a: &'a String, b: &'a String) -> (&'a str, &'a str) {
    (&a[..5], &b[..5])
}

// Here we are promising Rust that all of the borrowed u8's
// live for the same scope.
// Q: Why do I need a lifetime here?
fn join_ages<'a, T: Iterator>(iters: T) -> Vec<&'a Age>
        where T::Item: IntoIterator<Item=&'a Age> {
    iters.flat_map(|b| b.into_iter())
         .collect()
}

#[test]
fn it_works() {
    let jane = Person::new("Jane", None);
    let tom = Person::new("Tom", Some(&jane));
    let name = "Andrew".to_owned();
    let another = "Timothy".to_owned();
    let a = 43;
    let vec_ages = vec![vec![Age(90), Age(80)],
                        vec![Age(2)]];

    let arr_ages = vec![[Age(90), Age(80)],
                        [Age(2), Age(45)]];

    assert_eq!(first_five(&name), "Andre");
    assert_eq!(double_first_five(&name, &another), ("Andre", "Timot"));

    assert_eq!(tom.name, "Tom");
    assert_eq!(jane.name, "Jane");
    assert_eq!(tom.parent.unwrap().name, "Jane");
    assert_eq!(tom.parents_name(), Some("Jane"));
    assert_eq!(jane.parents_name(), None);

    assert_eq!(join_ages(vec_ages.iter()), vec![&Age(90), &Age(80), &Age(2)]);
    assert_eq!(join_ages(arr_ages.iter()), vec![&Age(90), &Age(80), &Age(2), &Age(45)]);
}

// - We are making a promise to the compiler that all of these things live for atleast the same scope.
// - We are not asking the compiler to "create a lifetime". We are just telling it to complain if we
//   don't do what we say we are doing.
// - Why? Because this allows Rust to ensure we cannot cause common memory allocation bugs. It does
//   this at compile time. It's a zero-cost abstraction because there is no runtime penalty.
// - It makes code look "uglier". But this is the wrong mindset for writing systems-level programs.
//   The emphasis here is on safety, not expressiveness.
// - Question: Why can't Rust ensure/infer/elide this scenario? Surely any reference to a Parent
//   must *always* live for the same or a bigger scope???
//   - There is an open RFC for this
//   - It may be complex to implement and/or slow down compilation
//   - Eliding in more cases may prevent newcomer Rust programmers from ever encountering
//     lifetimes, which could have a negative impact in the long run. Pedagogy!
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::{bail, Result};
use regex::Regex;
use std::matches;
use std::time::Duration;
use structopt::StructOpt;

use opentitanlib::app::TransportWrapper;
use opentitanlib::execute_test;
use opentitanlib::spiflash::{
    sfdp, BlockEraseSize, SpiFlash, SupportedAddressModes, WriteGranularity,
};
use opentitanlib::test_utils::init::InitializeTest;
use opentitanlib::uart::console::{ExitStatus, UartConsole};
use opentitanlib::util::parse_int::ParseInt;

#[derive(Debug, StructOpt)]
struct Opts {
    #[structopt(flatten)]
    init: InitializeTest,

    #[structopt(
        long, parse(try_from_str=humantime::parse_duration),
        default_value = "5s",
        help = "Bootstrap detection timeout",
    )]
    timeout: Duration,

    #[structopt(
        long, parse(try_from_str=usize::from_str),
        default_value = "12",
        help = "JEDEC page of manufacturer",
    )]
    jedec_page: usize,

    #[structopt(
        long, parse(try_from_str=u8::from_str),
        default_value = "0xEF",
        help = "JEDEC ID of manufacturer",
    )]
    jedec_id: u8,

    #[structopt(
        long, parse(try_from_str=u8::from_str),
        default_value = "0x08",
        help = "JEDEC manufacturer product ID",
    )]
    jedec_product: u8,

    #[structopt(
        long, parse(try_from_str=u32::from_str),
        default_value = "0x100000",
        help = "Size of the internal flash",
    )]
    flash_size: u32,
}

#[derive(Debug, PartialEq, Eq)]
enum BootstrapRequest {
    No,
    Yes,
}

fn test_bootstrap_entry(
    opts: &Opts,
    transport: &TransportWrapper,
    request: BootstrapRequest,
) -> Result<()> {
    // TODO: Should really `opts.init.uart_params.create()` here, but we need to refactor
    // BootstrapOptions first.
    //let uart = opts.init.uart_params.create(&transport)?;
    let uart = transport.uart("0")?;

    // Look for the bootstrap signal from the ROM.
    // When enabled, we expect the uart to emit a message indicating bootstrap entry.
    // When disabled, we expect the ROM to indicate boot failure.
    let (success, failure) = match request {
        BootstrapRequest::No => (Regex::new(r"BFV:")?, Regex::new(r"bootstrap:1\r\n")?),
        BootstrapRequest::Yes => (Regex::new(r"bootstrap:1\r\n")?, Regex::new(r"BFV:")?),
    };
    let mut console = UartConsole {
        timeout: Some(opts.timeout),
        exit_success: Some(success),
        exit_failure: Some(failure),
        ..Default::default()
    };

    match request {
        BootstrapRequest::No => transport.remove_pin_strapping("ROM_BOOTSTRAP")?,
        BootstrapRequest::Yes => transport.apply_pin_strapping("ROM_BOOTSTRAP")?,
    }
    let reset_delay = opts
        .init
        .bootstrap
        .options
        .reset_delay
        .unwrap_or(Duration::from_millis(50));
    transport.reset_target(reset_delay, true)?;

    // Now watch the console for the exit conditions.
    let mut stdout = std::io::stdout();
    let result = console.interact(&*uart, None, Some(&mut stdout))?;
    match result {
        ExitStatus::ExitSuccess => {}
        _ => {
            bail!("FAIL: {:?}", result);
        }
    };

    // Now check whether the SPI device is responding to status messages.
    // Note: CIPO line is in high-z state when CMD_INFO registers are not configured.
    // Use READ_SFDP instead of READ_STATUS to avoid false negatives when bootstrap is not
    // requested
    let spi = transport.spi("0")?;
    let status = SpiFlash::read_status(&*spi)?;
    match request {
        BootstrapRequest::Yes => assert_eq!(status, 0x00),
        BootstrapRequest::No => assert!(matches!(
            SpiFlash::read_sfdp(&*spi)
                .unwrap_err()
                .downcast::<sfdp::Error>()
                .unwrap(),
            sfdp::Error::WrongHeaderSignature(..)
        )),
    }
    Ok(())
}

fn test_jedec_id(opts: &Opts, transport: &TransportWrapper) -> Result<()> {
    let spi = transport.spi("0")?;
    let id = SpiFlash::read_jedec_id(&*spi, 16)?;
    log::info!("Read jedec id: {:x?}", id);
    let mut index = 0;
    while id[index] == 0x7f {
        index += 1;
    }
    let page = index;
    let manufacturer = id[index];
    let product = id[index + 1];
    let density = id[index + 2];

    assert_eq!(page, opts.jedec_page);
    assert_eq!(manufacturer, opts.jedec_id);
    assert_eq!(product, opts.jedec_product);
    assert_eq!(1u32 << density, opts.flash_size);
    Ok(())
}

fn test_write_enable_disable(transport: &TransportWrapper) -> Result<()> {
    let spi = transport.spi("0")?;

    assert_eq!(SpiFlash::read_status(&*spi)?, 0x0);

    SpiFlash::set_write_enable(&*spi)?;
    assert_eq!(SpiFlash::read_status(&*spi)?, 0x2);

    SpiFlash::set_write_disable(&*spi)?;
    assert_eq!(SpiFlash::read_status(&*spi)?, 0x0);

    Ok(())
}

fn test_sfdp(opts: &Opts, transport: &TransportWrapper) -> Result<()> {
    let spi = transport.spi("0")?;
    let sfdp = SpiFlash::read_sfdp(&*spi)?;
    log::info!("SFDP: {:#x?}", sfdp);

    // We expect a SFDP header of version 1.10 with no additional phdrs
    // except the mandatory JDEC phdr.
    assert_eq!(sfdp.header.signature, 0x50444653);
    assert_eq!(sfdp.header.minor, 0xa);
    assert_eq!(sfdp.header.major, 0x1);
    assert_eq!(sfdp.header.nph, 0x0);
    assert_eq!(sfdp.header.reserved, 0xff);

    // Check the parameters of the mandatory phdr.
    assert_eq!(sfdp.phdr[0].id, 0x0);
    assert_eq!(sfdp.phdr[0].minor, 0x7);
    assert_eq!(sfdp.phdr[0].major, 0x1);
    assert_eq!(sfdp.phdr[0].dwords, 0x17);
    assert_eq!(sfdp.phdr[0].offset, 0x10);

    // Check the values of the JEDEC parameters.
    // TODO(lowRISC/opentitan#14457): Update these checks after updating the
    // SFDP parser in opentitanlib.
    assert_eq!(sfdp.jedec.block_erase_size, BlockEraseSize::Block4KiB);
    assert_eq!(
        sfdp.jedec.write_granularity,
        WriteGranularity::Granularity64Bytes
    );
    assert_eq!(sfdp.jedec.write_en_required, true);
    assert_eq!(sfdp.jedec.write_en_opcode, 0x50);
    assert_eq!(sfdp.jedec.erase_opcode_4kib, 0x20);
    assert_eq!(sfdp.jedec.support_fast_read_112, false);
    assert_eq!(sfdp.jedec.address_modes, SupportedAddressModes::Mode3b);
    assert_eq!(sfdp.jedec.support_double_rate_clocking, false);
    assert_eq!(sfdp.jedec.support_fast_read_122, false);
    assert_eq!(sfdp.jedec.support_fast_read_144, false);
    assert_eq!(sfdp.jedec.support_fast_read_114, false);
    assert_eq!(sfdp.jedec.support_fast_read_222, false);
    assert_eq!(sfdp.jedec.support_fast_read_444, false);
    assert_eq!(sfdp.jedec.density, opts.flash_size);
    Ok(())
}

fn main() -> Result<()> {
    let opts = Opts::from_args();
    opts.init.init_logging();

    let transport = opts.init.init_target()?;
    execute_test!(
        test_bootstrap_entry,
        &opts,
        &transport,
        BootstrapRequest::No
    );
    execute_test!(
        test_bootstrap_entry,
        &opts,
        &transport,
        BootstrapRequest::Yes
    );
    execute_test!(test_jedec_id, &opts, &transport);
    execute_test!(test_sfdp, &opts, &transport);
    execute_test!(test_write_enable_disable, &transport);
    Ok(())
}
<commit_msg>[test] Add BootstrapTest to isolate test points<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::{bail, Result};
use regex::Regex;
use std::matches;
use std::ops::Drop;
use std::thread;
use std::time::Duration;
use structopt::StructOpt;

use opentitanlib::app::TransportWrapper;
use opentitanlib::execute_test;
use opentitanlib::spiflash::{
    sfdp, BlockEraseSize, SpiFlash, SupportedAddressModes, WriteGranularity,
};
use opentitanlib::test_utils::init::InitializeTest;
use opentitanlib::uart::console::{ExitStatus, UartConsole};
use opentitanlib::util::parse_int::ParseInt;

#[derive(Debug, StructOpt)]
struct Opts {
    #[structopt(flatten)]
    init: InitializeTest,

    #[structopt(
        long, parse(try_from_str=humantime::parse_duration),
        default_value = "5s",
        help = "Bootstrap detection timeout",
    )]
    timeout: Duration,

    #[structopt(
        long, parse(try_from_str=usize::from_str),
        default_value = "12",
        help = "JEDEC page of manufacturer",
    )]
    jedec_page: usize,

    #[structopt(
        long, parse(try_from_str=u8::from_str),
        default_value = "0xEF",
        help = "JEDEC ID of manufacturer",
    )]
    jedec_id: u8,

    #[structopt(
        long, parse(try_from_str=u8::from_str),
        default_value = "0x08",
        help = "JEDEC manufacturer product ID",
    )]
    jedec_product: u8,

    #[structopt(
        long, parse(try_from_str=u32::from_str),
        default_value = "0x100000",
        help = "Size of the internal flash",
    )]
    flash_size: u32,
}

/// A struct for isolating individual test points.
///
/// This is basically an example of the RAII technique.
/// Calling `BootstrapTest::start()` resets the chip and puts it in bootstrap mode (assuming it's
/// enabled). Strapping pins are released and the chip is reset when the returned struct is
/// dropped, typically at the end of the test point.
struct BootstrapTest<'a> {
    transport: &'a TransportWrapper,
    reset_delay: Duration,
}

impl<'a> BootstrapTest<'a> {
    pub fn start(transport: &'a TransportWrapper, reset_delay: Duration) -> Result<Self> {
        let b = BootstrapTest {
            transport,
            reset_delay,
        };
        transport.apply_pin_strapping("ROM_BOOTSTRAP")?;
        transport.reset_target(b.reset_delay, true)?;

        let spi = transport.spi("0")?;
        while SpiFlash::read_status(&*spi)? != 0 {
            thread::sleep(Duration::from_millis(10));
        }

        Ok(b)
    }
}

impl<'a> Drop for BootstrapTest<'a> {
    fn drop(&mut self) {
        self.transport
            .remove_pin_strapping("ROM_BOOTSTRAP")
            .unwrap();
        self.transport.reset_target(self.reset_delay, true).unwrap();
    }
}

#[derive(Debug, PartialEq, Eq)]
enum BootstrapRequest {
    No,
    Yes,
}

fn test_bootstrap_entry(
    opts: &Opts,
    transport: &TransportWrapper,
    request: BootstrapRequest,
) -> Result<()> {
    // TODO: Should really `opts.init.uart_params.create()` here, but we need to refactor
    // BootstrapOptions first.
    //let uart = opts.init.uart_params.create(&transport)?;
    let uart = transport.uart("0")?;

    // Look for the bootstrap signal from the ROM.
    // When enabled, we expect the uart to emit a message indicating bootstrap entry.
    // When disabled, we expect the ROM to indicate boot failure.
    let (success, failure) = match request {
        BootstrapRequest::No => (Regex::new(r"BFV:")?, Regex::new(r"bootstrap:1\r\n")?),
        BootstrapRequest::Yes => (Regex::new(r"bootstrap:1\r\n")?, Regex::new(r"BFV:")?),
    };
    let mut console = UartConsole {
        timeout: Some(opts.timeout),
        exit_success: Some(success),
        exit_failure: Some(failure),
        ..Default::default()
    };

    match request {
        BootstrapRequest::No => transport.remove_pin_strapping("ROM_BOOTSTRAP")?,
        BootstrapRequest::Yes => transport.apply_pin_strapping("ROM_BOOTSTRAP")?,
    }
    let reset_delay = opts
        .init
        .bootstrap
        .options
        .reset_delay
        .unwrap_or(Duration::from_millis(50));
    transport.reset_target(reset_delay, true)?;

    // Now watch the console for the exit conditions.
    let mut stdout = std::io::stdout();
    let result = console.interact(&*uart, None, Some(&mut stdout))?;
    match result {
        ExitStatus::ExitSuccess => {}
        _ => {
            bail!("FAIL: {:?}", result);
        }
    };

    // Now check whether the SPI device is responding to status messages.
    // Note: CIPO line is in high-z state when CMD_INFO registers are not configured.
    // Use READ_SFDP instead of READ_STATUS to avoid false negatives when bootstrap is not
    // requested
    let spi = transport.spi("0")?;
    let status = SpiFlash::read_status(&*spi)?;
    match request {
        BootstrapRequest::Yes => assert_eq!(status, 0x00),
        BootstrapRequest::No => assert!(matches!(
            SpiFlash::read_sfdp(&*spi)
                .unwrap_err()
                .downcast::<sfdp::Error>()
                .unwrap(),
            sfdp::Error::WrongHeaderSignature(..)
        )),
    }
    Ok(())
}

fn test_jedec_id(opts: &Opts, transport: &TransportWrapper) -> Result<()> {
    let spi = transport.spi("0")?;
    let id = SpiFlash::read_jedec_id(&*spi, 16)?;
    log::info!("Read jedec id: {:x?}", id);
    let mut index = 0;
    while id[index] == 0x7f {
        index += 1;
    }
    let page = index;
    let manufacturer = id[index];
    let product = id[index + 1];
    let density = id[index + 2];

    assert_eq!(page, opts.jedec_page);
    assert_eq!(manufacturer, opts.jedec_id);
    assert_eq!(product, opts.jedec_product);
    assert_eq!(1u32 << density, opts.flash_size);
    Ok(())
}

fn test_write_enable_disable(transport: &TransportWrapper) -> Result<()> {
    let spi = transport.spi("0")?;

    assert_eq!(SpiFlash::read_status(&*spi)?, 0x0);

    SpiFlash::set_write_enable(&*spi)?;
    assert_eq!(SpiFlash::read_status(&*spi)?, 0x2);

    SpiFlash::set_write_disable(&*spi)?;
    assert_eq!(SpiFlash::read_status(&*spi)?, 0x0);

    Ok(())
}

fn test_sfdp(opts: &Opts, transport: &TransportWrapper) -> Result<()> {
    let spi = transport.spi("0")?;
    let sfdp = SpiFlash::read_sfdp(&*spi)?;
    log::info!("SFDP: {:#x?}", sfdp);

    // We expect a SFDP header of version 1.10 with no additional phdrs
    // except the mandatory JDEC phdr.
    assert_eq!(sfdp.header.signature, 0x50444653);
    assert_eq!(sfdp.header.minor, 0xa);
    assert_eq!(sfdp.header.major, 0x1);
    assert_eq!(sfdp.header.nph, 0x0);
    assert_eq!(sfdp.header.reserved, 0xff);

    // Check the parameters of the mandatory phdr.
    assert_eq!(sfdp.phdr[0].id, 0x0);
    assert_eq!(sfdp.phdr[0].minor, 0x7);
    assert_eq!(sfdp.phdr[0].major, 0x1);
    assert_eq!(sfdp.phdr[0].dwords, 0x17);
    assert_eq!(sfdp.phdr[0].offset, 0x10);

    // Check the values of the JEDEC parameters.
    // TODO(lowRISC/opentitan#14457): Update these checks after updating the
    // SFDP parser in opentitanlib.
    assert_eq!(sfdp.jedec.block_erase_size, BlockEraseSize::Block4KiB);
    assert_eq!(
        sfdp.jedec.write_granularity,
        WriteGranularity::Granularity64Bytes
    );
    assert_eq!(sfdp.jedec.write_en_required, true);
    assert_eq!(sfdp.jedec.write_en_opcode, 0x50);
    assert_eq!(sfdp.jedec.erase_opcode_4kib, 0x20);
    assert_eq!(sfdp.jedec.support_fast_read_112, false);
    assert_eq!(sfdp.jedec.address_modes, SupportedAddressModes::Mode3b);
    assert_eq!(sfdp.jedec.support_double_rate_clocking, false);
    assert_eq!(sfdp.jedec.support_fast_read_122, false);
    assert_eq!(sfdp.jedec.support_fast_read_144, false);
    assert_eq!(sfdp.jedec.support_fast_read_114, false);
    assert_eq!(sfdp.jedec.support_fast_read_222, false);
    assert_eq!(sfdp.jedec.support_fast_read_444, false);
    assert_eq!(sfdp.jedec.density, opts.flash_size);
    Ok(())
}

fn main() -> Result<()> {
    let opts = Opts::from_args();
    opts.init.init_logging();

    let transport = opts.init.init_target()?;
    execute_test!(
        test_bootstrap_entry,
        &opts,
        &transport,
        BootstrapRequest::No
    );
    execute_test!(
        test_bootstrap_entry,
        &opts,
        &transport,
        BootstrapRequest::Yes
    );
    execute_test!(test_jedec_id, &opts, &transport);
    execute_test!(test_sfdp, &opts, &transport);
    execute_test!(test_write_enable_disable, &transport);
    Ok(())
}
<|endoftext|>


<commit_before>// Create an array with at least 100 elements in it where the ??? is.
// Scroll down for hints!

fn main() {
    let a = ???

    if a.len() >= 100 {
        println!("Wow, that's a big array!");
    } else {
        println!("Meh, I eat arrays like that for breakfast.");
    }
}

// There's a shorthand to initialize Arrays with a certain size that does not
// require you to type in 100 items (but you certainly can if you want!)
// Check out the Primitive Types -> Arrays section of the book:
// http://doc.rust-lang.org/stable/book/primitive-types.html#arrays
// Bonus: what are some other things you could have that would return true
// for `a.len() >= 100`?
<commit_msg>Link to second edition of book<commit_after>// Create an array with at least 100 elements in it where the ??? is.
// Scroll down for hints!

fn main() {
    let a = ???

    if a.len() >= 100 {
        println!("Wow, that's a big array!");
    } else {
        println!("Meh, I eat arrays like that for breakfast.");
    }
}

// There's a shorthand to initialize Arrays with a certain size that does not
// require you to type in 100 items (but you certainly can if you want!)
// Check out the Primitive Types -> Arrays section of the book:
// https://doc.rust-lang.org/stable/book/second-edition/ch03-02-data-types.html#arrays
// Bonus: what are some other things you could have that would return true
// for `a.len() >= 100`?
<|endoftext|>


<commit_before>// Copyright (c) 2015, Nathan Zadoks <nathan@nathan7.eu>
// See the LICENSE file included in this distribution.
use core::prelude::*;
use core::mem::{size_of, align_of};
use core::cmp::max;
use core::ptr;

use stack::Stack;

#[allow(non_camel_case_types)]
pub type uintptr_t = u64;

#[allow(raw_pointer_derive)]
#[derive(Copy, Clone)]
pub struct Registers {
  rsp: *mut uintptr_t
}

impl Registers {
  #[inline]
  pub unsafe fn new<S, F>(stack: &mut S, f: F) -> Registers where S: Stack, F: FnOnce() {
    let sp_limit = stack.limit();
    let mut sp = stack.top() as *mut uintptr_t;
    let f_ptr = push(&mut sp, f);

    asm!(include_str!("init.s")
          : "={rdi}"(sp)
          : "{rdi}" (sp),
            "{rsi}" (rust_trampoline::<F>),
            "{rdx}" (f_ptr),
            "{rcx}" (sp_limit)
          :
          : "volatile");

    Registers { rsp: sp }
  }

  #[inline(always)]
  pub unsafe fn swap(&mut self) {
    asm!(include_str!("swap.s")
          :
          : "{rdi}" (&mut self.rsp)
          : "rax",   "rbx",   "rcx",   "rdx",   "rsi",   "rdi", //"rbp",   "rsp",
            "r8",    "r9",    "r10",   "r11",   "r12",   "r13",   "r14",   "r15",
            "xmm0",  "xmm1",  "xmm2",  "xmm3",  "xmm4",  "xmm5",  "xmm6",  "xmm7",
            "xmm8",  "xmm9",  "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
            "xmm16", "xmm17", "xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23",
            "xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29", "xmm30", "xmm31"
            "cc"
          : "volatile");
  }
}

unsafe extern "C" fn rust_trampoline<F: FnOnce()>(f: *const F) {
  ptr::read(f)()
}

unsafe fn push<T>(spp: &mut *mut uintptr_t, value: T) -> *mut T {
  let mut sp = *spp as *mut T;
  sp = offset_mut(sp, -1);
  sp = align_down_mut(sp, max(align_of::<T>(), 16));
  *sp = value;
  *spp = sp as *mut uintptr_t;
  sp
}

fn align_down_mut<T>(sp: *mut T, n: usize) -> *mut T {
  let sp = (sp as usize) & !(n - 1);
  sp as *mut T
}

// ptr::offset_mut is positive ints only
pub fn offset_mut<T>(ptr: *mut T, count: isize) -> *mut T {
  (ptr as isize + count * (size_of::<T>() as isize)) as *mut T
}
<commit_msg>uintptr_t is pointless when usize is the same anyway<commit_after>// Copyright (c) 2015, Nathan Zadoks <nathan@nathan7.eu>
// See the LICENSE file included in this distribution.
use core::prelude::*;
use core::mem::{size_of, align_of};
use core::cmp::max;
use core::ptr;

use stack::Stack;

#[allow(raw_pointer_derive)]
#[derive(Copy, Clone)]
pub struct Registers {
  rsp: *mut usize
}

impl Registers {
  #[inline]
  pub unsafe fn new<S, F>(stack: &mut S, f: F) -> Registers where S: Stack, F: FnOnce() {
    let sp_limit = stack.limit();
    let mut sp = stack.top() as *mut usize;
    let f_ptr = push(&mut sp, f);

    asm!(include_str!("init.s")
          : "={rdi}"(sp)
          : "{rdi}" (sp),
            "{rsi}" (rust_trampoline::<F>),
            "{rdx}" (f_ptr),
            "{rcx}" (sp_limit)
          :
          : "volatile");

    Registers { rsp: sp }
  }

  #[inline(always)]
  pub unsafe fn swap(&mut self) {
    asm!(include_str!("swap.s")
          :
          : "{rdi}" (&mut self.rsp)
          : "rax",   "rbx",   "rcx",   "rdx",   "rsi",   "rdi", //"rbp",   "rsp",
            "r8",    "r9",    "r10",   "r11",   "r12",   "r13",   "r14",   "r15",
            "xmm0",  "xmm1",  "xmm2",  "xmm3",  "xmm4",  "xmm5",  "xmm6",  "xmm7",
            "xmm8",  "xmm9",  "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
            "xmm16", "xmm17", "xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23",
            "xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29", "xmm30", "xmm31"
            "cc"
          : "volatile");
  }
}

unsafe extern "C" fn rust_trampoline<F: FnOnce()>(f: *const F) {
  ptr::read(f)()
}

unsafe fn push<T>(spp: &mut *mut usize, value: T) -> *mut T {
  let mut sp = *spp as *mut T;
  sp = offset_mut(sp, -1);
  sp = align_down_mut(sp, max(align_of::<T>(), 16));
  *sp = value;
  *spp = sp as *mut usize;
  sp
}

fn align_down_mut<T>(sp: *mut T, n: usize) -> *mut T {
  let sp = (sp as usize) & !(n - 1);
  sp as *mut T
}

// ptr::offset_mut is positive ints only
pub fn offset_mut<T>(ptr: *mut T, count: isize) -> *mut T {
  (ptr as isize + count * (size_of::<T>() as isize)) as *mut T
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate libarchive3_sys;

use self::libarchive3_sys::ffi;
use std::ffi::{CStr, CString};
use std::marker;
use std::ptr;
use std::io::{Result, Error, SeekFrom, Read, Seek, ErrorKind};
use std::error::Error as STDError;
use fs::SeekableRead;
use std::cmp::min;
use std::path::PathBuf;

pub fn initialize() {
    unsafe { libc::setlocale(libc::LC_ALL, CString::new("").unwrap().as_ptr()) };
}

struct Proxy<R: SeekableRead> {
    r: R,
    buf: Vec<u8>,
    pos: u64,
}

impl<R: SeekableRead> Proxy<R> {
    fn new(r: R) -> Proxy<R> {
        let mut v = Vec::new();
        v.resize(4096, 0);
        Proxy {
            r: r,
            buf: v,
            pos: 0,
        }
    }

    fn read(&mut self) -> Result<&[u8]> {
        let n = self.r.read(&mut self.buf[..])?;
        self.pos += n as u64;
        Ok(&self.buf[..n])
    }

    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
        self.pos = self.r.seek(pos)?;
        Ok(self.pos)
    }
}

pub struct Archive<R: SeekableRead> {
    raw: *mut ffi::Struct_archive,
    eof: bool,
    _proxy: Box<Proxy<R>>,
}

unsafe fn set_error(raw: *mut ffi::Struct_archive, e: Error) {
    let desc = CString::new(e.description()).unwrap();
    ffi::archive_set_error(raw, e.raw_os_error().unwrap_or(libc::EIO), desc.as_ptr());
}

unsafe fn error_string(raw: *mut ffi::Struct_archive) -> String {
    let p = ffi::archive_error_string(raw);
    if p.is_null() {
        "unknown".to_string()
    } else {
        CStr::from_ptr(p).to_str().unwrap().to_string()
    }
}

unsafe extern "C" fn read_callback<R: SeekableRead>(raw: *mut ffi::Struct_archive,
                                                    client_data: *mut libc::c_void,
                                                    buffer: *mut *const libc::c_void)
                                                    -> libc::ssize_t {
    let proxy = (client_data as *mut Proxy<R>).as_mut().unwrap();
    let err;
    match proxy.read() {
        Ok(data) => {
            *buffer = data.as_ptr() as *const libc::c_void;
            return data.len() as libc::ssize_t;
        }
        Err(e) => err = e,
    };
    set_error(raw, err);
    -1
}

unsafe extern "C" fn seek_callback<R: SeekableRead>(raw: *mut ffi::Struct_archive,
                                                    client_data: *mut libc::c_void,
                                                    offset: libc::int64_t,
                                                    whence: libc::c_int)
                                                    -> libc::int64_t {
    let proxy = (client_data as *mut Proxy<R>).as_mut().unwrap();
    let pos = match whence {
        libc::SEEK_SET => SeekFrom::Start(offset as u64),
        libc::SEEK_CUR => SeekFrom::Current(offset),
        libc::SEEK_END => SeekFrom::End(offset),
        _ => unreachable!(),
    };
    match proxy.seek(pos) {
        Ok(n) => n as libc::int64_t,
        Err(e) => {
            // correct?
            set_error(raw, e);
            -1
        }
    }
}

impl<R: SeekableRead> Archive<R> {
    pub fn new(r: R) -> Self {
        unsafe {
            let raw = ffi::archive_read_new();
            if raw.is_null() {
                panic!("oom");
            }
            if ffi::archive_read_support_format_all(raw) != ffi::ARCHIVE_OK {
                panic!("not support format");
            }
            if ffi::archive_read_support_filter_all(raw) != ffi::ARCHIVE_OK {
                panic!("not support filter");
            }
            if ffi::archive_read_set_seek_callback(raw, Some(seek_callback::<R>)) !=
               ffi::ARCHIVE_OK {
                panic!("failed to set seek");
            }
            let proxy = Box::into_raw(Box::new(Proxy::new(r)));
            if ffi::archive_read_open(raw,
                                      proxy as *mut libc::c_void,
                                      None,
                                      Some(read_callback::<R>),
                                      None) != ffi::ARCHIVE_OK {
                panic!("failed to open");
            }
            Archive {
                raw: raw,
                eof: false,
                _proxy: Box::from_raw(proxy),
            }
        }
    }

    fn next_entry_raw(&mut self) -> Option<Result<Entry>> {
        if self.eof {
            return None;
        }

        let mut entry = ptr::null_mut();
        loop {
            match unsafe { ffi::archive_read_next_header(self.raw, &mut entry) } {
                ffi::ARCHIVE_OK => break,
                ffi::ARCHIVE_WARN => {
                    warn!("archive_read_next_header: {}",
                          unsafe { error_string(self.raw) });
                    break;
                }
                ffi::ARCHIVE_EOF => {
                    self.eof = true;
                    return None;
                }
                ffi::ARCHIVE_RETRY => {
                    // failed but retryable.
                    warn!("archive_read_next_header: {}, retry.",
                          unsafe { error_string(self.raw) });
                    continue;
                }
                ffi::ARCHIVE_FATAL => {
                    return Some(Err(Error::new(ErrorKind::Other,
                                               unsafe { error_string(self.raw) })));
                }
                _ => unreachable!(),
            }

        }
        Some(Ok(Entry::new(entry)))
    }

    pub fn next_entry<'a>(&'a mut self) -> Option<Result<RefEntry<'a, R>>> {
        self.next_entry_raw()
            .map(|r| r.map(|e| RefEntry::new(e)))
    }

    pub fn find_open<P>(mut self, p: P) -> Option<Result<Reader<R>>>
        where P: Fn(&Entry) -> bool
    {
        loop {
            match self.next_entry_raw() {
                Some(Ok(e)) => {
                    if p(&e) {
                        break;
                    }
                }
                Some(Err(e)) => return Some(Err(e)),
                None => return None,
            }
        }
        Some(Ok(Reader::new(self)))
    }
}

impl<R: SeekableRead> Drop for Archive<R> {
    fn drop(&mut self) {
        unsafe { ffi::archive_read_free(self.raw) };
    }
}

pub struct Reader<R: SeekableRead> {
    a: Archive<R>,
    buf: *const libc::c_void,
    read_pos: usize,
    buf_size: libc::size_t,
    offset: libc::off_t,
    eof: bool,
}

impl<R: SeekableRead> Reader<R> {
    fn new(a: Archive<R>) -> Reader<R> {
        Reader {
            a: a,
            buf: ptr::null(),
            read_pos: 0,
            buf_size: 0,
            offset: 0,
            eof: false,
        }
    }

    fn fill_gap(&mut self, buf: &mut [u8]) -> usize {
        if self.read_pos < self.offset as usize {
            let l = min(buf.len(), (self.offset as usize) - self.read_pos);
            for x in &mut buf[..l] {
                *x = 0;
            }
            self.read_pos += l;
            return l;
        }
        0
    }

    fn read_data_block(&mut self) -> Result<()> {
        if self.eof {
            return Ok(());
        }

        while self.offset as usize + self.buf_size as usize <= self.read_pos {
            match unsafe {
                      ffi::archive_read_data_block(self.a.raw,
                                                   &mut self.buf,
                                                   &mut self.buf_size,
                                                   &mut self.offset)
                  } {
                ffi::ARCHIVE_OK => continue,
                ffi::ARCHIVE_WARN => {
                    warn!("archive_read_data_block: {}",
                          unsafe { error_string(self.a.raw) });
                    continue;
                }
                ffi::ARCHIVE_EOF => {
                    self.eof = true;
                    return Ok(());
                }
                ffi::ARCHIVE_RETRY => {
                    // failed but retryable.
                    warn!("archive_read_data_block: {}, retry",
                          unsafe { error_string(self.a.raw) });
                    continue;
                }
                ffi::ARCHIVE_FATAL => {
                    return Err(Error::new(ErrorKind::Other, unsafe { error_string(self.a.raw) }));
                }
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl<R: SeekableRead> Read for Reader<R> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.read_data_block()?;
        let n = self.fill_gap(buf);
        if n > 0 {
            return Ok(n);
        }
        let begin = self.read_pos - self.offset as usize;
        let l = min(buf.len(), self.buf_size - begin);
        unsafe {
            let p = (self.buf as *const u8).offset(begin as isize);
            ptr::copy_nonoverlapping(p, buf.as_mut_ptr(), l);
        }
        self.read_pos += l;
        Ok(l)
    }
}

impl<R: SeekableRead> Seek for Reader<R> {
    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
        match pos {
            SeekFrom::Start(n) => self.read_pos = n as usize,
            SeekFrom::End(_) => unimplemented!(),
            SeekFrom::Current(n) => self.read_pos += n as usize,
        }
        Ok(self.read_pos as u64)
    }
}

pub struct Entry {
    entry: *mut ffi::Struct_archive_entry,
}

impl Entry {
    fn new(entry: *mut ffi::Struct_archive_entry) -> Entry {
        Entry { entry: entry }
    }

    pub fn pathname(&self) -> PathBuf {
        let c_str = unsafe { CStr::from_ptr(ffi::archive_entry_pathname(self.entry)) };
        PathBuf::from(c_str.to_string_lossy().as_ref())
    }

    pub fn size(&self) -> libc::int64_t {
        unsafe { ffi::archive_entry_size(self.entry) }
    }

    pub fn filetype(&self) -> libc::mode_t {
        unsafe { ffi::archive_entry_filetype(self.entry) }
    }
}

pub struct RefEntry<'a, R: SeekableRead + 'a> {
    e: Entry,
    _m: marker::PhantomData<&'a R>,
}

impl<'a, R: SeekableRead> RefEntry<'a, R> {
    fn new(e: Entry) -> RefEntry<'a, R> {
        RefEntry {
            e: e,
            _m: marker::PhantomData,
        }
    }

    pub fn pathname(&self) -> PathBuf {
        self.e.pathname()
    }

    pub fn size(&self) -> libc::int64_t {
        self.e.size()
    }

    pub fn filetype(&self) -> libc::mode_t {
        self.e.filetype()
    }
}
<commit_msg>Treat not specified erorrs. This fixes #1<commit_after>extern crate libc;
extern crate libarchive3_sys;

use self::libarchive3_sys::ffi;
use std::ffi::{CStr, CString};
use std::marker;
use std::ptr;
use std::io::{Result, Error, SeekFrom, Read, Seek, ErrorKind};
use std::error::Error as STDError;
use fs::SeekableRead;
use std::cmp::min;
use std::path::PathBuf;

pub fn initialize() {
    unsafe { libc::setlocale(libc::LC_ALL, CString::new("").unwrap().as_ptr()) };
}

struct Proxy<R: SeekableRead> {
    r: R,
    buf: Vec<u8>,
    pos: u64,
}

impl<R: SeekableRead> Proxy<R> {
    fn new(r: R) -> Proxy<R> {
        let mut v = Vec::new();
        v.resize(4096, 0);
        Proxy {
            r: r,
            buf: v,
            pos: 0,
        }
    }

    fn read(&mut self) -> Result<&[u8]> {
        let n = self.r.read(&mut self.buf[..])?;
        self.pos += n as u64;
        Ok(&self.buf[..n])
    }

    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
        self.pos = self.r.seek(pos)?;
        Ok(self.pos)
    }
}

pub struct Archive<R: SeekableRead> {
    raw: *mut ffi::Struct_archive,
    eof: bool,
    _proxy: Box<Proxy<R>>,
}

unsafe fn set_error(raw: *mut ffi::Struct_archive, e: Error) {
    let desc = CString::new(e.description()).unwrap();
    ffi::archive_set_error(raw, e.raw_os_error().unwrap_or(libc::EIO), desc.as_ptr());
}

unsafe fn error_string(raw: *mut ffi::Struct_archive) -> String {
    let p = ffi::archive_error_string(raw);
    if p.is_null() {
        "unknown".to_string()
    } else {
        CStr::from_ptr(p).to_str().unwrap().to_string()
    }
}

unsafe extern "C" fn read_callback<R: SeekableRead>(raw: *mut ffi::Struct_archive,
                                                    client_data: *mut libc::c_void,
                                                    buffer: *mut *const libc::c_void)
                                                    -> libc::ssize_t {
    let proxy = (client_data as *mut Proxy<R>).as_mut().unwrap();
    let err;
    match proxy.read() {
        Ok(data) => {
            *buffer = data.as_ptr() as *const libc::c_void;
            return data.len() as libc::ssize_t;
        }
        Err(e) => err = e,
    };
    set_error(raw, err);
    -1
}

unsafe extern "C" fn seek_callback<R: SeekableRead>(raw: *mut ffi::Struct_archive,
                                                    client_data: *mut libc::c_void,
                                                    offset: libc::int64_t,
                                                    whence: libc::c_int)
                                                    -> libc::int64_t {
    let proxy = (client_data as *mut Proxy<R>).as_mut().unwrap();
    let pos = match whence {
        libc::SEEK_SET => SeekFrom::Start(offset as u64),
        libc::SEEK_CUR => SeekFrom::Current(offset),
        libc::SEEK_END => SeekFrom::End(offset),
        _ => unreachable!(),
    };
    match proxy.seek(pos) {
        Ok(n) => n as libc::int64_t,
        Err(e) => {
            // correct?
            set_error(raw, e);
            -1
        }
    }
}

impl<R: SeekableRead> Archive<R> {
    pub fn new(r: R) -> Self {
        unsafe {
            let raw = ffi::archive_read_new();
            if raw.is_null() {
                panic!("oom");
            }
            if ffi::archive_read_support_format_all(raw) != ffi::ARCHIVE_OK {
                panic!("not support format");
            }
            if ffi::archive_read_support_filter_all(raw) != ffi::ARCHIVE_OK {
                panic!("not support filter");
            }
            if ffi::archive_read_set_seek_callback(raw, Some(seek_callback::<R>)) !=
               ffi::ARCHIVE_OK {
                panic!("failed to set seek");
            }
            let proxy = Box::into_raw(Box::new(Proxy::new(r)));
            if ffi::archive_read_open(raw,
                                      proxy as *mut libc::c_void,
                                      None,
                                      Some(read_callback::<R>),
                                      None) != ffi::ARCHIVE_OK {
                panic!("failed to open");
            }
            Archive {
                raw: raw,
                eof: false,
                _proxy: Box::from_raw(proxy),
            }
        }
    }

    fn next_entry_raw(&mut self) -> Option<Result<Entry>> {
        if self.eof {
            return None;
        }

        let mut entry = ptr::null_mut();
        loop {
            match unsafe { ffi::archive_read_next_header(self.raw, &mut entry) } {
                ffi::ARCHIVE_OK => break,
                ffi::ARCHIVE_WARN => {
                    warn!("archive_read_next_header: {}",
                          unsafe { error_string(self.raw) });
                    break;
                }
                ffi::ARCHIVE_EOF => {
                    self.eof = true;
                    return None;
                }
                ffi::ARCHIVE_RETRY => {
                    // failed but retryable.
                    warn!("archive_read_next_header: {}, retry.",
                          unsafe { error_string(self.raw) });
                    continue;
                }
                ffi::ARCHIVE_FATAL => {
                    return Some(Err(Error::new(ErrorKind::Other,
                                               unsafe { error_string(self.raw) })));
                }
                _ => unreachable!(),
            }

        }
        Some(Ok(Entry::new(entry)))
    }

    pub fn next_entry<'a>(&'a mut self) -> Option<Result<RefEntry<'a, R>>> {
        self.next_entry_raw()
            .map(|r| r.map(|e| RefEntry::new(e)))
    }

    pub fn find_open<P>(mut self, p: P) -> Option<Result<Reader<R>>>
        where P: Fn(&Entry) -> bool
    {
        loop {
            match self.next_entry_raw() {
                Some(Ok(e)) => {
                    if p(&e) {
                        break;
                    }
                }
                Some(Err(e)) => return Some(Err(e)),
                None => return None,
            }
        }
        Some(Ok(Reader::new(self)))
    }
}

impl<R: SeekableRead> Drop for Archive<R> {
    fn drop(&mut self) {
        unsafe { ffi::archive_read_free(self.raw) };
    }
}

pub struct Reader<R: SeekableRead> {
    a: Archive<R>,
    buf: *const libc::c_void,
    read_pos: usize,
    buf_size: libc::size_t,
    offset: libc::off_t,
    eof: bool,
}

impl<R: SeekableRead> Reader<R> {
    fn new(a: Archive<R>) -> Reader<R> {
        Reader {
            a: a,
            buf: ptr::null(),
            read_pos: 0,
            buf_size: 0,
            offset: 0,
            eof: false,
        }
    }

    fn fill_gap(&mut self, buf: &mut [u8]) -> usize {
        if self.read_pos < self.offset as usize {
            let l = min(buf.len(), (self.offset as usize) - self.read_pos);
            for x in &mut buf[..l] {
                *x = 0;
            }
            self.read_pos += l;
            return l;
        }
        0
    }

    fn read_data_block(&mut self) -> Result<()> {
        if self.eof {
            return Ok(());
        }

        while self.offset as usize + self.buf_size as usize <= self.read_pos {
            match unsafe {
                      ffi::archive_read_data_block(self.a.raw,
                                                   &mut self.buf,
                                                   &mut self.buf_size,
                                                   &mut self.offset)
                  } {
                ffi::ARCHIVE_OK => continue,
                ffi::ARCHIVE_WARN => {
                    warn!("archive_read_data_block: {}",
                          unsafe { error_string(self.a.raw) });
                    continue;
                }
                ffi::ARCHIVE_EOF => {
                    self.eof = true;
                    return Ok(());
                }
                ffi::ARCHIVE_RETRY => {
                    // failed but retryable.
                    warn!("archive_read_data_block: {}, retry",
                          unsafe { error_string(self.a.raw) });
                    continue;
                }
                ffi::ARCHIVE_FATAL => {
                    return Err(Error::new(ErrorKind::Other, unsafe { error_string(self.a.raw) }));
                }
                n if n < 0 => {
                    return Err(Error::new(ErrorKind::Other,
                                          format!("unknown error {} from libarchive", n)));
                }
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl<R: SeekableRead> Read for Reader<R> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.read_data_block()?;
        let n = self.fill_gap(buf);
        if n > 0 {
            return Ok(n);
        }
        let begin = self.read_pos - self.offset as usize;
        let l = min(buf.len(), self.buf_size - begin);
        unsafe {
            let p = (self.buf as *const u8).offset(begin as isize);
            ptr::copy_nonoverlapping(p, buf.as_mut_ptr(), l);
        }
        self.read_pos += l;
        Ok(l)
    }
}

impl<R: SeekableRead> Seek for Reader<R> {
    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
        match pos {
            SeekFrom::Start(n) => self.read_pos = n as usize,
            SeekFrom::End(_) => unimplemented!(),
            SeekFrom::Current(n) => self.read_pos += n as usize,
        }
        Ok(self.read_pos as u64)
    }
}

pub struct Entry {
    entry: *mut ffi::Struct_archive_entry,
}

impl Entry {
    fn new(entry: *mut ffi::Struct_archive_entry) -> Entry {
        Entry { entry: entry }
    }

    pub fn pathname(&self) -> PathBuf {
        let c_str = unsafe { CStr::from_ptr(ffi::archive_entry_pathname(self.entry)) };
        PathBuf::from(c_str.to_string_lossy().as_ref())
    }

    pub fn size(&self) -> libc::int64_t {
        unsafe { ffi::archive_entry_size(self.entry) }
    }

    pub fn filetype(&self) -> libc::mode_t {
        unsafe { ffi::archive_entry_filetype(self.entry) }
    }
}

pub struct RefEntry<'a, R: SeekableRead + 'a> {
    e: Entry,
    _m: marker::PhantomData<&'a R>,
}

impl<'a, R: SeekableRead> RefEntry<'a, R> {
    fn new(e: Entry) -> RefEntry<'a, R> {
        RefEntry {
            e: e,
            _m: marker::PhantomData,
        }
    }

    pub fn pathname(&self) -> PathBuf {
        self.e.pathname()
    }

    pub fn size(&self) -> libc::int64_t {
        self.e.size()
    }

    pub fn filetype(&self) -> libc::mode_t {
        self.e.filetype()
    }
}
<|endoftext|>


<commit_before>use cairo::{self, MatrixTrait};
use std::cell::Cell;

use crate::attributes::Attribute;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::DrawingCtx;
use crate::error::RenderingError;
use crate::length::{LengthHorizontal, LengthVertical};
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{Parse, ParseValue};
use crate::properties::Opacity;
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    shared_surface::SurfaceType,
    ImageSurfaceDataExt,
};
use crate::unit_interval::UnitInterval;

coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);

pub struct NodeMask {
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    width: Cell<LengthHorizontal>,
    height: Cell<LengthVertical>,

    units: Cell<MaskUnits>,
    content_units: Cell<MaskContentUnits>,
}

impl NodeMask {
    pub fn new() -> NodeMask {
        NodeMask {
            // these values are per the spec
            x: Cell::new(LengthHorizontal::parse_str("-10%").unwrap()),
            y: Cell::new(LengthVertical::parse_str("-10%").unwrap()),

            width: Cell::new(LengthHorizontal::parse_str("120%").unwrap()),
            height: Cell::new(LengthVertical::parse_str("120%").unwrap()),

            units: Cell::new(MaskUnits::default()),
            content_units: Cell::new(MaskContentUnits::default()),
        }
    }

    pub fn generate_cairo_mask(
        &self,
        node: &RsvgNode,
        affine_before_mask: &cairo::Matrix,
        draw_ctx: &mut DrawingCtx,
        bbox: &BoundingBox,
    ) -> Result<(), RenderingError> {
        if bbox.rect.is_none() {
            // The node being masked is empty / doesn't have a
            // bounding box, so there's nothing to mask!
            return Ok(());
        }

        let bbox_rect = bbox.rect.as_ref().unwrap();

        let cascaded = node.get_cascaded_values();
        let values = cascaded.get();

        let surface = draw_ctx.create_surface_for_toplevel_viewport()?;

        let mask_units = CoordUnits::from(self.units.get());
        let content_units = CoordUnits::from(self.content_units.get());

        let (x, y, w, h) = {
            let params = if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let x = self.x.get().normalize(&values, &params);
            let y = self.y.get().normalize(&values, &params);
            let w = self.width.get().normalize(&values, &params);
            let h = self.height.get().normalize(&values, &params);

            (x, y, w, h)
        };

        // Use a scope because mask_cr needs to release the
        // reference to the surface before we access the pixels
        {
            let save_cr = draw_ctx.get_cairo_context();

            let mask_cr = cairo::Context::new(&surface);
            mask_cr.set_matrix(*affine_before_mask);
            mask_cr.transform(node.get_transform());

            draw_ctx.set_cairo_context(&mask_cr);

            if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.clip(
                    x * bbox_rect.width + bbox_rect.x,
                    y * bbox_rect.height + bbox_rect.y,
                    w * bbox_rect.width,
                    h * bbox_rect.height,
                );
            } else {
                draw_ctx.clip(x, y, w, h);
            }

            {
                let _params = if content_units == CoordUnits::ObjectBoundingBox {
                    let bbtransform = cairo::Matrix::new(
                        bbox_rect.width,
                        0.0,
                        0.0,
                        bbox_rect.height,
                        bbox_rect.x,
                        bbox_rect.y,
                    );

                    mask_cr.transform(bbtransform);

                    draw_ctx.push_view_box(1.0, 1.0)
                } else {
                    draw_ctx.get_view_params()
                };

                let res = draw_ctx.with_discrete_layer(node, values, false, &mut |dc| {
                    node.draw_children(&cascaded, dc, false)
                });

                draw_ctx.set_cairo_context(&save_cr);

                res
            }
        }?;

        let Opacity(opacity) = values.opacity;
        let mask_surface = compute_luminance_to_alpha(surface, opacity)?;

        let cr = draw_ctx.get_cairo_context();

        cr.identity_matrix();

        cr.mask_surface(&mask_surface, 0.0, 0.0);

        Ok(())
    }
}

// Returns a surface whose alpha channel for each pixel is equal to the
// luminance of that pixel's unpremultiplied RGB values.  The resulting
// surface's RGB values are not meanignful; only the alpha channel has
// useful luminance data.
//
// This is to get a mask suitable for use with cairo_mask_surface().
fn compute_luminance_to_alpha(
    surface: cairo::ImageSurface,
    opacity: UnitInterval,
) -> Result<cairo::ImageSurface, cairo::Status> {
    let surface = SharedImageSurface::new(surface, SurfaceType::SRgb)?;

    let width = surface.width();
    let height = surface.height();

    let bounds = IRect {
        x0: 0,
        y0: 0,
        x1: width,
        y1: height,
    };

    let opacity = u8::from(opacity);
    let mut output = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
    let output_stride = output.get_stride() as usize;

    {
        let mut output_data = output.get_data().unwrap();

        for (x, y, pixel) in Pixels::new(&surface, bounds) {
            output_data.set_pixel(output_stride, pixel.to_mask(opacity), x, y);
        }
    }

    Ok(output)
}

impl NodeTrait for NodeMask {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::X => self.x.set(attr.parse(value)?),
                Attribute::Y => self.y.set(attr.parse(value)?),
                Attribute::Width => self
                    .width
                    .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),
                Attribute::Height => self
                    .height
                    .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),
                Attribute::MaskUnits => self.units.set(attr.parse(value)?),
                Attribute::MaskContentUnits => self.content_units.set(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}
<commit_msg>Rename variable<commit_after>use cairo::{self, MatrixTrait};
use std::cell::Cell;

use crate::attributes::Attribute;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::DrawingCtx;
use crate::error::RenderingError;
use crate::length::{LengthHorizontal, LengthVertical};
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{Parse, ParseValue};
use crate::properties::Opacity;
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    shared_surface::SurfaceType,
    ImageSurfaceDataExt,
};
use crate::unit_interval::UnitInterval;

coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);

pub struct NodeMask {
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    width: Cell<LengthHorizontal>,
    height: Cell<LengthVertical>,

    units: Cell<MaskUnits>,
    content_units: Cell<MaskContentUnits>,
}

impl NodeMask {
    pub fn new() -> NodeMask {
        NodeMask {
            // these values are per the spec
            x: Cell::new(LengthHorizontal::parse_str("-10%").unwrap()),
            y: Cell::new(LengthVertical::parse_str("-10%").unwrap()),

            width: Cell::new(LengthHorizontal::parse_str("120%").unwrap()),
            height: Cell::new(LengthVertical::parse_str("120%").unwrap()),

            units: Cell::new(MaskUnits::default()),
            content_units: Cell::new(MaskContentUnits::default()),
        }
    }

    pub fn generate_cairo_mask(
        &self,
        node: &RsvgNode,
        affine_before_mask: &cairo::Matrix,
        draw_ctx: &mut DrawingCtx,
        bbox: &BoundingBox,
    ) -> Result<(), RenderingError> {
        if bbox.rect.is_none() {
            // The node being masked is empty / doesn't have a
            // bounding box, so there's nothing to mask!
            return Ok(());
        }

        let bbox_rect = bbox.rect.as_ref().unwrap();

        let cascaded = node.get_cascaded_values();
        let values = cascaded.get();

        let mask_content_surface = draw_ctx.create_surface_for_toplevel_viewport()?;

        let mask_units = CoordUnits::from(self.units.get());
        let content_units = CoordUnits::from(self.content_units.get());

        let (x, y, w, h) = {
            let params = if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let x = self.x.get().normalize(&values, &params);
            let y = self.y.get().normalize(&values, &params);
            let w = self.width.get().normalize(&values, &params);
            let h = self.height.get().normalize(&values, &params);

            (x, y, w, h)
        };

        // Use a scope because mask_cr needs to release the
        // reference to the surface before we access the pixels
        {
            let save_cr = draw_ctx.get_cairo_context();

            let mask_cr = cairo::Context::new(&mask_content_surface);
            mask_cr.set_matrix(*affine_before_mask);
            mask_cr.transform(node.get_transform());

            draw_ctx.set_cairo_context(&mask_cr);

            if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.clip(
                    x * bbox_rect.width + bbox_rect.x,
                    y * bbox_rect.height + bbox_rect.y,
                    w * bbox_rect.width,
                    h * bbox_rect.height,
                );
            } else {
                draw_ctx.clip(x, y, w, h);
            }

            {
                let _params = if content_units == CoordUnits::ObjectBoundingBox {
                    let bbtransform = cairo::Matrix::new(
                        bbox_rect.width,
                        0.0,
                        0.0,
                        bbox_rect.height,
                        bbox_rect.x,
                        bbox_rect.y,
                    );

                    mask_cr.transform(bbtransform);

                    draw_ctx.push_view_box(1.0, 1.0)
                } else {
                    draw_ctx.get_view_params()
                };

                let res = draw_ctx.with_discrete_layer(node, values, false, &mut |dc| {
                    node.draw_children(&cascaded, dc, false)
                });

                draw_ctx.set_cairo_context(&save_cr);

                res
            }
        }?;

        let Opacity(opacity) = values.opacity;
        let mask_surface = compute_luminance_to_alpha(mask_content_surface, opacity)?;

        let cr = draw_ctx.get_cairo_context();

        cr.identity_matrix();

        cr.mask_surface(&mask_surface, 0.0, 0.0);

        Ok(())
    }
}

// Returns a surface whose alpha channel for each pixel is equal to the
// luminance of that pixel's unpremultiplied RGB values.  The resulting
// surface's RGB values are not meanignful; only the alpha channel has
// useful luminance data.
//
// This is to get a mask suitable for use with cairo_mask_surface().
fn compute_luminance_to_alpha(
    surface: cairo::ImageSurface,
    opacity: UnitInterval,
) -> Result<cairo::ImageSurface, cairo::Status> {
    let surface = SharedImageSurface::new(surface, SurfaceType::SRgb)?;

    let width = surface.width();
    let height = surface.height();

    let bounds = IRect {
        x0: 0,
        y0: 0,
        x1: width,
        y1: height,
    };

    let opacity = u8::from(opacity);
    let mut output = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
    let output_stride = output.get_stride() as usize;

    {
        let mut output_data = output.get_data().unwrap();

        for (x, y, pixel) in Pixels::new(&surface, bounds) {
            output_data.set_pixel(output_stride, pixel.to_mask(opacity), x, y);
        }
    }

    Ok(output)
}

impl NodeTrait for NodeMask {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::X => self.x.set(attr.parse(value)?),
                Attribute::Y => self.y.set(attr.parse(value)?),
                Attribute::Width => self
                    .width
                    .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),
                Attribute::Height => self
                    .height
                    .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),
                Attribute::MaskUnits => self.units.set(attr.parse(value)?),
                Attribute::MaskContentUnits => self.content_units.set(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>// FIXME: Link below is required to render in index
/// Asserts that the traits are
/// [object-safe](https://doc.rust-lang.org/book/ch17-02-trait-objects.html#object-safety-is-required-for-trait-objects).
///
/// This is useful for when changes are made to a trait that accidentally
/// prevent it from being used as an [object]. Such a case would be adding a
/// generic method and forgetting to add `where Self: Sized` after it. If left
/// unnoticed, that mistake will affect crate users and break both forward and
/// backward compatibility.
///
/// # Examples
///
/// When exposing a public API, it's important that traits that could previously
/// use dynamic dispatch can still do so in future compatible crate versions.
///
/// ```
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// trait MySafeTrait {
///     fn foo(&self) -> u32;
/// }
///
/// assert_obj_safe!(std::fmt::Write, MySafeTrait);
/// ```
///
/// Works with traits that are not in the calling module:
///
/// ```
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// mod inner {
///     pub trait BasicTrait {
///         fn bar(&self);
///     }
///     assert_obj_safe!(BasicTrait);
/// }
///
/// assert_obj_safe!(inner::BasicTrait);
/// ```
///
/// The following example fails to compile because raw pointers cannot be sent
///  between threads safely:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// assert_impl!(*const u8, Send);
/// ```
///
/// The following example fails to compile because generics without
/// `where Self: Sized` are not allowed in [object-safe][object] trait methods:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// trait MyUnsafeTrait {
///     fn baz<T>(&self) -> T;
/// }
///
/// assert_obj_safe!(MyUnsafeTrait);
/// ```
///
/// When we fix that, the previous code will compile:
///
/// ```
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// trait MyUnsafeTrait {
///     fn baz<T>(&self) -> T where Self: Sized;
/// }
///
/// assert_obj_safe!(MyUnsafeTrait);
/// ```
///
/// [object]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#object-safety-is-required-for-trait-objects
#[macro_export]
macro_rules! assert_obj_safe {
    ($($xs:path),+ $(,)?) => {
        $(const _: Option<&$xs> = None;)+
    };
}

#[doc(hidden)]
#[cfg(feature = "nightly")]
#[macro_export]
macro_rules! _assert_obj_safe {
    ($($xs:ty),+ $(,)?) => {
        $(const _: Option<&$xs> = None;)+
    };
}

#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
#[macro_export]
macro_rules! _assert_obj_safe {
    ($($xs:ty),+ $(,)?) => {
        $(let _: &$xs;)+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_obj_safe!($($xs)+); }
    };
}
<commit_msg>Reword `assert_obj_safe!` docs<commit_after>// FIXME: Link below is required to render in index
/// Asserts that the traits support dynamic dispatch
/// ([object-safety](https://doc.rust-lang.org/book/ch17-02-trait-objects.html#object-safety-is-required-for-trait-objects)).
///
/// This is useful for when changes are made to a trait that accidentally
/// prevent it from being used as an [object]. Such a case would be adding a
/// generic method and forgetting to add `where Self: Sized` after it. If left
/// unnoticed, that mistake will affect crate users and break both forward and
/// backward compatibility.
///
/// # Examples
///
/// When exposing a public API, it's important that traits that could previously
/// use dynamic dispatch can still do so in future compatible crate versions.
///
/// ```
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// trait MySafeTrait {
///     fn foo(&self) -> u32;
/// }
///
/// assert_obj_safe!(std::fmt::Write, MySafeTrait);
/// ```
///
/// Works with traits that are not in the calling module:
///
/// ```
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// mod inner {
///     pub trait BasicTrait {
///         fn bar(&self);
///     }
///     assert_obj_safe!(BasicTrait);
/// }
///
/// assert_obj_safe!(inner::BasicTrait);
/// ```
///
/// The following example fails to compile because raw pointers cannot be sent
///  between threads safely:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// assert_impl!(*const u8, Send);
/// ```
///
/// The following example fails to compile because generics without
/// `where Self: Sized` are not allowed in [object-safe][object] trait methods:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// trait MyUnsafeTrait {
///     fn baz<T>(&self) -> T;
/// }
///
/// assert_obj_safe!(MyUnsafeTrait);
/// ```
///
/// When we fix that, the previous code will compile:
///
/// ```
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// trait MyUnsafeTrait {
///     fn baz<T>(&self) -> T where Self: Sized;
/// }
///
/// assert_obj_safe!(MyUnsafeTrait);
/// ```
///
/// [object]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#object-safety-is-required-for-trait-objects
#[macro_export]
macro_rules! assert_obj_safe {
    ($($xs:path),+ $(,)?) => {
        $(const _: Option<&$xs> = None;)+
    };
}

#[doc(hidden)]
#[cfg(feature = "nightly")]
#[macro_export]
macro_rules! _assert_obj_safe {
    ($($xs:ty),+ $(,)?) => {
        $(const _: Option<&$xs> = None;)+
    };
}

#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
#[macro_export]
macro_rules! _assert_obj_safe {
    ($($xs:ty),+ $(,)?) => {
        $(let _: &$xs;)+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_obj_safe!($($xs)+); }
    };
}
<|endoftext|>