<commit_before>use std::str;
use std::fmt;
use memchr;

#[derive(PartialEq, Eq)]
pub struct Token<'a> {
    ty: TokenType,
    value: &'a str
}

impl<'a> Token<'a> {
    fn new(ty: TokenType, value: &'a str) -> Self {
        Token { ty: ty, value: value }
    }
}

impl<'a> fmt::Debug for Token<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}Token: {:?}", self.ty, self.value)
    }
}

#[derive(Debug, PartialEq, Eq)]
enum TokenizerType {
    Whitespace,
    Blackspace,
    LineComment,
    BlockComment
}

#[derive(Debug, PartialEq, Eq)]
enum TokenType {
    Whitespace,
    Keyword,
    Identifier,
    Blackspace, // XXX
    LineComment,
    BlockComment
}

#[inline]
fn is_id(c: u8) -> bool {
    (c as char).is_alphabetic() || match c {
        b'$' | b'_' => true,
        _ => false
    }
}

fn is_keyword(s: &str) -> bool {
    s == "var" ||
    s == "let" ||
    s == "function" ||
    s == "return"
}

enum State {
    Unknown,
    Identifier
}

fn tokenize_blackspace<'a>(input: &'a str) -> Vec<Token<'a>> {
    let mut tokens = Vec::with_capacity(input.len());
    let bytes = input.as_bytes();

    let mut start_index = 0;
    let mut state;
    while start_index < bytes.len() {
        if start_index != 0 {
            tokens.push(Token::new(TokenType::Whitespace, ""));
        }

        let mut end_index = start_index + 1;
        if is_id(bytes[start_index]) {
            state = State::Identifier;
            while end_index < bytes.len() && is_id(bytes[end_index]) {
                end_index += 1;
            }
        } else {
            state = State::Unknown;
        }

        let content = unsafe { str::from_utf8_unchecked(&bytes[start_index..end_index]) };
        match state {
            State::Unknown => {
                tokens.push(Token::new(TokenType::Blackspace, content));
            },
            State::Identifier => {
                tokens.push(Token::new(TokenType::Identifier, content));
            }
        }

        start_index = end_index;
    }

    tokens
}

pub fn tokenize<'a>(input: &'a str) -> Vec<Token<'a>> {
    let bytes = input.as_bytes();
    let mut tokens = Vec::new();

    let mut start_index = 0;
    let mut state = TokenizerType::Whitespace;
    while start_index < bytes.len() {
        let mut end_index = start_index;

        if start_index + 1 < bytes.len() && bytes[start_index] == b'/'
            && (bytes[start_index + 1] == b'/' || bytes[start_index + 1] == b'*') {
            if bytes[start_index + 1] == b'/' {
                state = TokenizerType::LineComment;
                end_index = end_index + memchr::memchr(b'\n', &bytes[end_index..]).unwrap_or(bytes.len() - end_index);
            } else if bytes[start_index + 1] == b'*' {
                end_index += 1; // Increment since we can guarantee it's at least one more
                state = TokenizerType::BlockComment;
                loop {
                    let next_position = memchr::memchr(b'/', &bytes[end_index..]);
                    if let Some(pos) = next_position {
                        let star_pos = end_index + pos - 1; // Right before the found slash

                        if bytes[star_pos] == b'*' {
                            end_index = end_index + pos + 1;
                            break;
                        } else {
                            end_index += 1; // Didn't find it here, try again.
                        }
                    } else {
                        end_index = bytes.len() - 1;
                        break;
                    }
                }
            }
        } else {
            while end_index < bytes.len() {
                let is_whitespace = (bytes[end_index] as char).is_whitespace();

                if (state == TokenizerType::Whitespace) != is_whitespace {
                    break;
                }

                end_index += 1;
            }
        }

        assert!(start_index < bytes.len(), "Start index is within range.");
        assert!(end_index <= bytes.len(), "End index is within range.");

        let content = unsafe { str::from_utf8_unchecked(&bytes[start_index..end_index]) };
        match state {
            TokenizerType::Whitespace => {
                tokens.push(Token::new(TokenType::Whitespace, content));
            },
            TokenizerType::Blackspace => {
                if is_keyword(content) {
                    tokens.push(Token::new(TokenType::Keyword, content));
                } else {
                    tokens.append(&mut tokenize_blackspace(content));
                }
            },
            TokenizerType::LineComment => {
                tokens.push(Token::new(TokenType::LineComment, content));
            },
            TokenizerType::BlockComment => {
                tokens.push(Token::new(TokenType::BlockComment, content));
            }
        };

        state = if state == TokenizerType::Whitespace {
            TokenizerType::Blackspace
        } else {
            TokenizerType::Whitespace
        };

        start_index = end_index;
    }

    tokens
}

#[cfg(test)]
mod bench {
    use super::*;
    use test::Bencher;

    macro_rules! _benchmark {
        ($name: ident, $toRun: expr) => (
            #[bench]
            fn $name(b: &mut Bencher) {
                b.iter(|| $toRun);
            }
        );
    }

    macro_rules! benchmark_tokenize_blackspace {
        ($name: ident, $toRun: expr) => (
            _benchmark!($name, super::tokenize_blackspace($toRun));
        )
    }

    macro_rules! benchmark_tokenize {
        ($name: ident, $toRun: expr) => (
            _benchmark!($name, tokenize($toRun));
        )
    }

    mod tokenize {
        use test::Bencher;
        use super::super::{tokenize};

        benchmark_tokenize!(function, "function test() {}");
        benchmark_tokenize!(keyword, "function");
        benchmark_tokenize!(empty, "");
        benchmark_tokenize!(space, " ");
        benchmark_tokenize!(comment_line, "// testing");
        benchmark_tokenize!(comment_block, "/* testi*/");
        benchmark_tokenize!(comment_long_block, "/* testitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitesti*/");
        benchmark_tokenize!(sample, include_str!("../input.js"));
    }

    benchmark_tokenize!(tokenize_ident, "$_very_Z_complex$$ident");
    benchmark_tokenize_blackspace!(tokenize_ident_blackspace, "$_very_Z_complex$$ident");
}

#[cfg(test)]
mod tests {
    use super::{tokenize, Token, TokenType};

    #[test]
    fn tokenize_line_comment() {
        let mut tokens = tokenize("// test");
        assert_eq!(tokens.remove(0), Token::new(TokenType::LineComment, "// test"));
        assert_eq!(tokens.len(), 0);
    }

    #[test]
    fn tokenize_sample() {
        let input = "function test() {
            // test
            /*
             * testing
             * multiline BlockComment
             */
            return this.foobar.TeSt;
        }";
        let mut tokens = tokenize(input);
        println!("{:?}", tokens);
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Keyword, "function"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, " "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Identifier, "test"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Blackspace, "("));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Blackspace, ")"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, " "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Blackspace, "{"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, "\n            "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::LineComment, "// test"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, "\n            "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::BlockComment, "/*\n             * testing\n             * multiline BlockComment\n             */"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, "\n            "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Keyword, "return"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, " "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Identifier, "this"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Blackspace, "."));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Identifier, "foobar"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Blackspace, "."));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Identifier, "TeSt"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Blackspace, ";"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, "\n        "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Blackspace, "}"));
        assert_eq!(tokens.len(), 0);
    }
}
<commit_msg>Implement number literal, string literal, keyword, and blackspace matching.<commit_after>use std::str;
use std::fmt;
use memchr;

#[derive(PartialEq, Eq)]
pub struct Token<'a> {
    ty: TokenType,
    value: &'a str
}

impl<'a> Token<'a> {
    fn new(ty: TokenType, value: &'a str) -> Self {
        Token { ty: ty, value: value }
    }
}

impl<'a> fmt::Debug for Token<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}Token: {:?}", self.ty, self.value)
    }
}

#[derive(Debug, PartialEq, Eq)]
enum TokenizerType {
    Whitespace,
    StringLiteral,
    Blackspace,
    LineComment,
    BlockComment
}

#[derive(Debug, PartialEq, Eq)]
enum TokenType {
    Whitespace,
    Keyword,
    Identifier,
    NumericLiteral,
    StringLiteral,
    Equal,
    DeIncrement,
    LogicalOr,
    LogicalAnd,
    BitwiseOr,
    BitwiseXOR,
    BitwiseAnd,
    Equality,
    Relational,
    BitShift,
    PlusMin,
    Modulo,
    Star,
    Slash,
    Semicolon,
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    LeftBracket,
    RightBracket,
    Dot,
    Comma,
    QuestionMark,
    Colon,
    ExclamationMark,
    Blackspace,
    LineComment,
    BlockComment
}

#[inline]
fn is_id(c: u8) -> bool {
    (c as char).is_alphabetic() ||
    c == b'$' ||
    c == b'_'
}

fn is_keyword(s: &str) -> bool {
    s == "var" ||
    s == "let" ||
    s == "function" ||
    s == "return" ||
    s == "for" ||
    s == "in" ||
    s == "undefined" ||
    s == "break" ||
    s == "case" ||
    s == "continue" ||
    s == "debugger" ||
    s == "default" ||
    s == "do" ||
    s == "if" ||
    s == "finally" ||
    s == "switch" ||
    s == "throw" ||
    s == "try" ||
    s == "const" ||
    s == "while" ||
    s == "with" ||
    s == "new" ||
    s == "this" ||
    s == "super" ||
    s == "class" ||
    s == "extends" ||
    s == "export" ||
    s == "import" ||
    s == "yield" ||
    s == "null" ||
    s == "true" ||
    s == "false" ||
    s == "instanceof" ||
    s == "typeof" ||
    s == "void" ||
    s == "delete"
}

fn is_num(c: u8) -> bool {
    // 100 and 10e10 are both valid numbers
    (c as char).is_numeric() || c == b'e'
}

enum BlackspaceState {
    Unknown,
    Identifier,
    StringLiteral,
    Number
}

fn find_string_literal(bytes: &[u8], start_index: usize) -> usize {
    let mut ignore_next = true;
    let mut end_index = start_index;
    while end_index < bytes.len() {
        if bytes[end_index] == b'"' && !ignore_next {
            end_index += 1;
            break;
        }
        ignore_next = bytes[end_index] == b'\\';
        end_index += 1;
    }

    end_index
}

fn tokenize_blackspace<'a>(input: &'a str, pos: usize) -> Vec<Token<'a>> {
    let mut tokens = Vec::with_capacity(input.len());
    let bytes = input.as_bytes();

    let mut start_index = 0;
    let mut state;
    while start_index < bytes.len() {
        if start_index != 0 {
            tokens.push(Token::new(TokenType::Whitespace, ""));
        }

        let mut end_index = start_index + 1;
        if is_id(bytes[start_index]) {
            state = BlackspaceState::Identifier;
            while end_index < bytes.len() && is_id(bytes[end_index]) {
                end_index += 1;
            }
        } else if is_num(bytes[start_index]) {
            state = BlackspaceState::Number;
            while end_index < bytes.len() && is_num(bytes[end_index]) {
                end_index += 1;
            }
        } else if bytes[start_index] == b'"' {
            state = BlackspaceState::StringLiteral;

            end_index = find_string_literal(&bytes, end_index);
        } else {
            state = BlackspaceState::Unknown;
        }

        let content = unsafe { str::from_utf8_unchecked(&bytes[start_index..end_index]) };
        match state {
            BlackspaceState::Unknown => {
                let ty = match content {
                    "=" => TokenType::Equal,
                    "==" | "!=" => TokenType::Equality,
                    "++" | "--" => TokenType::DeIncrement,
                    "||" => TokenType::LogicalOr,
                    "&&" => TokenType::LogicalAnd,
                    "|" => TokenType::BitwiseOr,
                    "&" => TokenType::BitwiseAnd,
                    "^" => TokenType::BitwiseXOR,
                    "<" | ">" => TokenType::Relational,
                    "<<" | ">>" => TokenType::BitShift,
                    "+" | "-" => TokenType::PlusMin,
                    "%" => TokenType::Modulo,
                    "*" => TokenType::Star,
                    "/" => TokenType::Slash,
                    ";" => TokenType::Semicolon,
                    "(" => TokenType::LeftParen,
                    ")" => TokenType::RightParen,
                    "{" => TokenType::LeftBrace,
                    "}" => TokenType::RightBrace,
                    "[" => TokenType::LeftBracket,
                    "]" => TokenType::RightBracket,
                    "." => TokenType::Dot,
                    "," => TokenType::Comma,
                    ":" => TokenType::Colon,
                    "?" => TokenType::QuestionMark,
                    "!" => TokenType::ExclamationMark,
                    _ => TokenType::Blackspace
                };

                // if ty == TokenType::Blackspace {
                //     println!("{:?} @ {} - {}", content, pos + start_index, pos + end_index);
                // }

                tokens.push(Token::new(ty, content));
            },
            BlackspaceState::Identifier => {
                tokens.push(Token::new(TokenType::Identifier, content));
            },
            BlackspaceState::Number => {
                tokens.push(Token::new(TokenType::NumericLiteral, content));
            },
            BlackspaceState::StringLiteral => {
                tokens.push(Token::new(TokenType::StringLiteral, content));
            }
        }

        start_index = end_index;
    }

    tokens
}

pub fn tokenize<'a>(input: &'a str) -> Vec<Token<'a>> {
    let bytes = input.as_bytes();
    let mut tokens = Vec::new();

    let mut start_index = 0;

    let mut state = TokenizerType::Whitespace;
    while start_index < bytes.len() {
        let mut end_index = start_index;

        if start_index + 1 < bytes.len() && bytes[start_index] == b'/'
            && (bytes[start_index + 1] == b'/' || bytes[start_index + 1] == b'*') {
            if bytes[start_index + 1] == b'/' {
                state = TokenizerType::LineComment;
                end_index = end_index + memchr::memchr(b'\n', &bytes[end_index..]).unwrap_or(bytes.len() - end_index);
            } else if bytes[start_index + 1] == b'*' {
                end_index += 1; // Increment since we can guarantee it's at least one more
                state = TokenizerType::BlockComment;
                loop {
                    let next_position = memchr::memchr(b'/', &bytes[end_index..]);
                    if let Some(pos) = next_position {
                        let star_pos = end_index + pos - 1; // Right before the found slash

                        if bytes[star_pos] == b'*' {
                            end_index = end_index + pos + 1;
                            break;
                        } else {
                            end_index += pos + 1; // Didn't find it here, try again.
                        }
                    } else {
                        end_index = bytes.len() - 1;
                        break;
                    }
                }
            }
        } else if bytes[start_index] == b'"' {
            if state == TokenizerType::Whitespace {
                tokens.push(Token::new(TokenType::Whitespace, ""));
            }

            state = TokenizerType::StringLiteral;

            end_index = find_string_literal(&bytes, end_index);
        } else {
            while end_index < bytes.len() {
                let is_whitespace = (bytes[end_index] as char).is_whitespace();

                if (state == TokenizerType::Whitespace) != is_whitespace {
                    break;
                }

                end_index += 1;
            }
        }

        assert!(start_index < bytes.len(), "Start index is within range.");
        assert!(end_index <= bytes.len(), "End index is within range.");

        let content = unsafe { str::from_utf8_unchecked(&bytes[start_index..end_index]) };
        match state {
            TokenizerType::Whitespace => {
                tokens.push(Token::new(TokenType::Whitespace, content));
            },
            TokenizerType::Blackspace => {
                if is_keyword(content) {
                    tokens.push(Token::new(TokenType::Keyword, content));
                } else {
                    tokens.append(&mut tokenize_blackspace(content, start_index));
                }
            },
            TokenizerType::LineComment => {
                tokens.push(Token::new(TokenType::LineComment, content));
            },
            TokenizerType::BlockComment => {
                tokens.push(Token::new(TokenType::BlockComment, content));
            },
            TokenizerType::StringLiteral => {
                tokens.push(Token::new(TokenType::StringLiteral, content));
            }
        };

        state = if state == TokenizerType::Whitespace {
            TokenizerType::Blackspace
        } else {
            TokenizerType::Whitespace
        };

        start_index = end_index;
    }

    if state == TokenizerType::Blackspace {
        tokens.push(Token::new(TokenType::Whitespace, ""));
    }

    tokens
}

#[cfg(test)]
mod bench {
    use super::*;
    use test::Bencher;

    macro_rules! _benchmark {
        ($name: ident, $toRun: expr) => (
            #[bench]
            fn $name(b: &mut Bencher) {
                b.iter(|| $toRun);
            }
        );
    }

    macro_rules! benchmark_tokenize_blackspace {
        ($name: ident, $toRun: expr) => (
            _benchmark!($name, super::tokenize_blackspace($toRun));
        )
    }

    macro_rules! benchmark_tokenize {
        ($name: ident, $toRun: expr) => (
            _benchmark!($name, tokenize($toRun));
        )
    }

    mod tokenize {
        use test::Bencher;
        use super::super::{tokenize};

        benchmark_tokenize!(function, "function test() {}");
        benchmark_tokenize!(keyword, "function");
        benchmark_tokenize!(empty, "");
        benchmark_tokenize!(space, " ");
        benchmark_tokenize!(comment_line, "// testing");
        benchmark_tokenize!(comment_block, "/* testi*/");
        benchmark_tokenize!(comment_long_block, "/* testitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitestitesti*/");
        benchmark_tokenize!(sample, include_str!("../input.js"));
    }

    benchmark_tokenize!(tokenize_ident, "$_very_Z_complex$$ident");
    // benchmark_tokenize_blackspace!(tokenize_ident_blackspace, "$_very_Z_complex$$ident");
}

#[cfg(test)]
mod tests {
    use super::{tokenize, Token, TokenType};

    #[test]
    fn tokenize_line_comment() {
        let mut tokens = tokenize("// test");
        assert_eq!(tokens.remove(0), Token::new(TokenType::LineComment, "// test"));
        assert_eq!(tokens.len(), 0);
    }

    #[test]
    fn tokenize_empty_string() {
        let mut tokens = tokenize("\"\"");
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::StringLiteral, "\"\""));
        assert_eq!(tokens.len(), 0);
    }

    #[test]
    fn tokenize_normal_string() {
        let mut tokens = tokenize("\"test foobar\"");
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::StringLiteral, "\"test foobar\""));
        assert_eq!(tokens.len(), 0);
    }

    #[test]
    fn tokenize_non_quote_escape_string() {
        let mut tokens = tokenize("\"\n\"");
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::StringLiteral, "\"\n\""));
        assert_eq!(tokens.len(), 0);
    }

    #[test]
    fn tokenize_quote_escape_string() {
        let mut tokens = tokenize(r#""\"""#);
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::StringLiteral, r#""\"""#));
        assert_eq!(tokens.len(), 0);
    }

    #[test]
    fn tokenize_blackspace_embedded_string() {
        let mut tokens = tokenize(r#"("auto")"#);
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::LeftParen, "("));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::StringLiteral, r#""auto""#));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::RightParen, ")"));
        assert_eq!(tokens.len(), 0);
    }

    #[test]
    fn tokenize_sample() {
        let input = "function test() {
            // test
            /*
             * testing
             * multiline BlockComment
             */
            return this.foobar.TeSt;
        }";
        let mut tokens = tokenize(input);
        println!("{:?}", tokens);
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Keyword, "function"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, " "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Identifier, "test"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::LeftParen, "("));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::RightParen, ")"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, " "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::LeftBrace, "{"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, "\n            "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::LineComment, "// test"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, "\n            "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::BlockComment, "/*\n             * testing\n             * multiline BlockComment\n             */"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, "\n            "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Keyword, "return"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, " "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Identifier, "this"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Dot, "."));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Identifier, "foobar"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Dot, "."));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Identifier, "TeSt"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, ""));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Semicolon, ";"));
        assert_eq!(tokens.remove(0), Token::new(TokenType::Whitespace, "\n        "));
        assert_eq!(tokens.remove(0), Token::new(TokenType::RightBrace, "}"));
        assert_eq!(tokens.len(), 0);
    }
}
<|endoftext|>


<commit_before>extern crate vec2;

use num::Num;

#[inline(always)]
pub fn compose<T: Num>(out: &mut [T; 6], position: [T; 3], scale: [T; 3], rotation: T) -> &mut [T; 6] {
    let sx = scale[0];
    let sy = scale[1];
    let c = rotation.cos();
    let s = rotation.sin();

    out[0] = c * sx;
    out[1] = s * sx;
    out[2] = -s * sy;
    out[3] = c * sy;
    out[4] = position[0];
    out[5] = position[1];
    out
}

#[inline(always)]
pub fn decompose<T: Num>(out: [T; 6], position: &mut [T; 3], scale: &mut [T; 3]) -> T {
    let m11 = out[0];
    let m12 = out[1];
    let sx = vec2::length_values(m11, m12);
    let sy = vec2::length_values(out[2], out[3]);

    position[0] = out[4];
    position[1] = out[5];

    scale[0] = sx;
    scale[1] = sy;

    m12.atan2(m11)
}

#[inline(always)]
pub fn look_at<T: Num>(out: &mut [T; 6], eye: [T; 3], target: [T; 3]) -> &mut [T; 6] {
    let x = target[0] - eye[0];
    let y = target[1] - eye[1];
    let a = y.atan2(x) - T::half_pi();
    let c = a.cos();
    let s = a.sin();

    out[0] = c;
    out[1] = s;
    out[2] = -s;
    out[3] = c;
    out
}

#[inline(always)]
pub fn set_rotation<T: Num>(out: &mut [T; 6], rotation: T) -> &mut [T; 6] {
    let c = rotation.cos();
    let s = rotation.sin();

    out[0] = c;
    out[1] = s;
    out[2] = -s;
    out[3] = c;
    out
}

#[inline(always)]
pub fn get_rotation<T: Num>(out: [T; 6]) -> T {
    out[1].atan2(out[0])
}

#[inline(always)]
pub fn set_position<T: Num>(out: &mut [T; 6], v: [T; 3]) -> &mut [T; 6] {
    out[4] = v[0];
    out[5] = v[1];
    out
}

#[inline(always)]
pub fn get_position<T: Num>(out: [T; 6], v: &mut [T; 3]) -> &mut [T; 3] {
    v[0] = out[4];
    v[1] = out[5];
    v
}

#[inline(always)]
pub fn extract_position<T: Num>(out: &mut [T; 6], a: [T; 6]) -> &mut [T; 6] {
    out[4] = a[4];
    out[5] = a[5];
    out
}

#[inline(always)]
pub fn extract_rotation<T: Num>(out: &mut [T; 6], a: [T; 6]) -> &mut [T; 6] {
    let m11 = a[0];
    let m12 = a[2];
    let m21 = a[1];
    let m22 = a[3];

    let x = m11 * m11 + m21 * m21;
    let y = m12 * m12 + m22 * m22;

    let sx = if x != T::zero() {T::one() / x.sqrt()} else {T::zero()};
    let sy = if y != T::zero() {T::one() / y.sqrt()} else {T::zero()};

    out[0] = m11 * sx;
    out[1] = m21 * sx;
    out[2] = m12 * sy;
    out[3] = m22 * sy;
    out
}

#[inline(always)]
pub fn translate<T: Num>(out: &mut [T; 6], a: [T; 6], v: [T; 3]) -> &mut [T; 6] {
    out[4] = a[0] * v[0] + a[2] * v[1] + a[4];
    out[5] = a[1] * v[0] + a[3] * v[1] + a[5];
    out
}

#[inline(always)]
pub fn rotate<T: Num>(out: &mut [T; 6], a: [T; 6], rotation: T) -> &mut [T; 6] {
    let m11 = a[0];
    let m12 = a[2];
    let m21 = a[1];
    let m22 = a[3];
    let c = rotation.cos();
    let s = rotation.sin();

    out[0] = m11 * c + m12 * s;
    out[1] = m11 * -s + m12 * c;
    out[2] = m21 * c + m22 * s;
    out[3] = m21 * -s + m22 * c;
    out
}

#[inline(always)]
pub fn scale<T: Num>(out: &mut [T; 6], a: [T; 6], v: [T; 3]) -> &mut [T; 6] {
    let x = v[0];
    let y = v[1];

    out[0] = a[0] * x;
    out[1] = a[1] * x;
    out[4] = a[4] * x;

    out[2] = a[2] * y;
    out[3] = a[3] * y;
    out[5] = a[5] * y;
    out
}

#[inline(always)]
pub fn orthographic<T: Num>(out: &mut [T; 6], left: T, right: T, top: T, bottom: T) -> &mut [T; 6] {
    let w = right - left;
    let h = top - bottom;

    let x = (right + left) / w;
    let y = (top + bottom) / h;

    out[0] = T::from_isize(2isize) / w;
    out[1] = T::zero();
    out[2] = T::zero();
    out[3] = T::from_isize(2isize) / h;
    out[4] = -x;
    out[5] = -y;
    out
}
<commit_msg>bug fixes<commit_after>extern crate vec2;

use num::Num;

#[inline(always)]
pub fn compose<T: Num>(out: &mut [T; 6], position: [T; 2], scale: [T; 2], rotation: T) -> &mut [T; 6] {
    let sx = scale[0];
    let sy = scale[1];
    let c = rotation.cos();
    let s = rotation.sin();

    out[0] = c * sx;
    out[1] = s * sx;
    out[2] = -s * sy;
    out[3] = c * sy;
    out[4] = position[0];
    out[5] = position[1];
    out
}

#[inline(always)]
pub fn decompose<T: Num>(out: [T; 6], position: &mut [T; 2], scale: &mut [T; 2]) -> T {
    let m11 = out[0];
    let m12 = out[1];
    let sx = vec2::length_values(m11, m12);
    let sy = vec2::length_values(out[2], out[3]);

    position[0] = out[4];
    position[1] = out[5];

    scale[0] = sx;
    scale[1] = sy;

    m12.atan2(m11)
}

#[inline(always)]
pub fn look_at<T: Num>(out: &mut [T; 6], eye: [T; 2], target: [T; 2]) -> &mut [T; 6] {
    let x = target[0] - eye[0];
    let y = target[1] - eye[1];
    let a = y.atan2(x) - T::half_pi();
    let c = a.cos();
    let s = a.sin();

    out[0] = c;
    out[1] = s;
    out[2] = -s;
    out[3] = c;
    out
}

#[inline(always)]
pub fn set_rotation<T: Num>(out: &mut [T; 6], rotation: T) -> &mut [T; 6] {
    let c = rotation.cos();
    let s = rotation.sin();

    out[0] = c;
    out[1] = s;
    out[2] = -s;
    out[3] = c;
    out
}

#[inline(always)]
pub fn get_rotation<T: Num>(out: [T; 6]) -> T {
    out[1].atan2(out[0])
}

#[inline(always)]
pub fn set_position<T: Num>(out: &mut [T; 6], v: [T; 2]) -> &mut [T; 6] {
    out[4] = v[0];
    out[5] = v[1];
    out
}

#[inline(always)]
pub fn get_position<T: Num>(out: [T; 6], v: &mut [T; 2]) -> &mut [T; 2] {
    v[0] = out[4];
    v[1] = out[5];
    v
}

#[inline(always)]
pub fn extract_position<T: Num>(out: &mut [T; 6], a: [T; 6]) -> &mut [T; 6] {
    out[4] = a[4];
    out[5] = a[5];
    out
}

#[inline(always)]
pub fn extract_rotation<T: Num>(out: &mut [T; 6], a: [T; 6]) -> &mut [T; 6] {
    let m11 = a[0];
    let m12 = a[2];
    let m21 = a[1];
    let m22 = a[3];

    let x = m11 * m11 + m21 * m21;
    let y = m12 * m12 + m22 * m22;

    let sx = if x != T::zero() {T::one() / x.sqrt()} else {T::zero()};
    let sy = if y != T::zero() {T::one() / y.sqrt()} else {T::zero()};

    out[0] = m11 * sx;
    out[1] = m21 * sx;
    out[2] = m12 * sy;
    out[3] = m22 * sy;
    out
}

#[inline(always)]
pub fn translate<T: Num>(out: &mut [T; 6], a: [T; 6], v: [T; 2]) -> &mut [T; 6] {
    out[4] = a[0] * v[0] + a[2] * v[1] + a[4];
    out[5] = a[1] * v[0] + a[3] * v[1] + a[5];
    out
}

#[inline(always)]
pub fn rotate<T: Num>(out: &mut [T; 6], a: [T; 6], rotation: T) -> &mut [T; 6] {
    let m11 = a[0];
    let m12 = a[2];
    let m21 = a[1];
    let m22 = a[3];
    let c = rotation.cos();
    let s = rotation.sin();

    out[0] = m11 * c + m12 * s;
    out[1] = m11 * -s + m12 * c;
    out[2] = m21 * c + m22 * s;
    out[3] = m21 * -s + m22 * c;
    out
}

#[inline(always)]
pub fn scale<T: Num>(out: &mut [T; 6], a: [T; 6], v: [T; 2]) -> &mut [T; 6] {
    let x = v[0];
    let y = v[1];

    out[0] = a[0] * x;
    out[1] = a[1] * x;
    out[4] = a[4] * x;

    out[2] = a[2] * y;
    out[3] = a[3] * y;
    out[5] = a[5] * y;
    out
}

#[inline(always)]
pub fn orthographic<T: Num>(out: &mut [T; 6], left: T, right: T, top: T, bottom: T) -> &mut [T; 6] {
    let w = right - left;
    let h = top - bottom;

    let x = (right + left) / w;
    let y = (top + bottom) / h;

    out[0] = T::from_isize(2isize) / w;
    out[1] = T::zero();
    out[2] = T::zero();
    out[3] = T::from_isize(2isize) / h;
    out[4] = -x;
    out[5] = -y;
    out
}
<|endoftext|>


<commit_before>
use vertex::{RawVertex, Vertex};
use edge::{RawEdge, Edge};
use graph::TraversableToVertex;
use path::{Path, Element};
use std::iter::Extend;

// use std::iter::Iterator;

// define a traversal as a series of stages

pub struct GraphQuery {
    // many paths
    pub paths: Vec<Path>
}

impl GraphQuery {
    pub fn new(vertices: Vec<Vertex>) -> GraphQuery {
        // create a new path for each proxy, add the proxy
        let mut paths = Vec::new();
        for p in vertices {
            let mut path = Path::new(p);
            paths.push(path)
        }
        GraphQuery{paths:paths}
    }

    pub fn empty() -> GraphQuery {
        // return an empty graph query
        GraphQuery{paths:Vec::new()}
    }

    pub fn get_paths(&self) -> &Vec<Path> {
        &self.paths
    }
    pub fn push(&mut self, path:Path) {
        self.paths.push(path)
    }

    /*
    generic mapper for our queries
    future this may use worker threads to perform
    traversals
     */
    fn map<F: Fn(&Path) -> Vec<Element>>(&self, closure: F) -> GraphQuery  {
        let mut result = GraphQuery::empty(); // result
        for path in self.paths.iter() {
            let mut tmp = closure(&path);
            // let mut tmp2 = tmp.as_slice();
            // currently gets back a Vec<Path> but what if it gets Elements?
            let new_elements = path.permute(&tmp);
            result.paths.extend(new_elements);
        }
        result
    }

    pub fn outV(&self, labels: Vec<String>) -> GraphQuery {
        // map can figure out the rest
        let f = |path: &Path| {
            //take the final element in the path
            let element = path.last().unwrap();

            let result : Vec<Element> = match element {
                &Element::Vertex(v) =>
                    v.outV().iter().map(|v| Element::Vertex(*v)).collect(),
                    // Vec::new(),
                &Element::Edge(ref e) =>
                    Vec::new()
            };
            // apply outV
            result
        };
        self.map(f)
    }

//    pub fn inV(&self) -> GraphQuery {
//        let g = GraphQuery::empty();
//        let f = |element: &Element| {
//            element.inV()
//        };
//        self.map(f)
//    }
}
<commit_msg>outv accepts a vec of labels<commit_after>
use vertex::{RawVertex, Vertex};
use edge::{RawEdge, Edge};
use graph::TraversableToVertex;
use path::{Path, Element};
use std::iter::Extend;

// use std::iter::Iterator;

// define a traversal as a series of stages

pub struct GraphQuery {
    // many paths
    pub paths: Vec<Path>
}

impl GraphQuery {
    pub fn new(vertices: Vec<Vertex>) -> GraphQuery {
        // create a new path for each proxy, add the proxy
        let mut paths = Vec::new();
        for p in vertices {
            let mut path = Path::new(p);
            paths.push(path)
        }
        GraphQuery{paths:paths}
    }

    pub fn empty() -> GraphQuery {
        // return an empty graph query
        GraphQuery{paths:Vec::new()}
    }

    pub fn get_paths(&self) -> &Vec<Path> {
        &self.paths
    }
    pub fn push(&mut self, path:Path) {
        self.paths.push(path)
    }

    /*
    generic mapper for our queries
    future this may use worker threads to perform
    traversals
     */
    fn map<F: Fn(&Path) -> Vec<Element>>(&self, closure: F) -> GraphQuery  {
        let mut result = GraphQuery::empty(); // result
        for path in self.paths.iter() {
            let mut tmp = closure(&path);
            // let mut tmp2 = tmp.as_slice();
            // currently gets back a Vec<Path> but what if it gets Elements?
            let new_elements = path.permute(&tmp);
            result.paths.extend(new_elements);
        }
        result
    }

    pub fn outV(&self, labels: &[String]) -> GraphQuery {
        // map can figure out the rest
        let f = |path: &Path| {
            //take the final element in the path
            let element = path.last().unwrap();

            let result : Vec<Element> = match element {
                &Element::Vertex(v) =>
                    v.outV().iter().map(|v| Element::Vertex(*v)).collect(),
                    // Vec::new(),
                &Element::Edge(ref e) =>
                    Vec::new()
            };
            // apply outV
            result
        };
        self.map(f)
    }

//    pub fn inV(&self) -> GraphQuery {
//        let g = GraphQuery::empty();
//        let f = |element: &Element| {
//            element.inV()
//        };
//        self.map(f)
//    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
use std::io::SeekFrom;
use std::path::Path;

use c64::cpu;
use c64::opcodes;

// helper macros to easily extract references from Option<RefCell<...>>
macro_rules! as_ref {
    ($x:expr) => ($x.as_ref().unwrap().borrow_mut())
}

macro_rules! as_mut {
    ($x:expr) => ($x.as_mut().unwrap().borrow_mut())
}

pub fn open_file(filename: &str, offset: u64) -> Vec<u8>
{
    let path = Path::new(&filename);

    let mut file = match File::open(&path) {
        Err(why) => panic!("Couldn't open {}: {}", path.display(), Error::description(&why)),
        Ok(file) => file,
    };

    let mut file_data = Vec::<u8>::new();

    let _ = file.seek(SeekFrom::Start(offset));
    let result = file.read_to_end(&mut file_data);

    match result {
        Err(why)   => panic!("Error reading file: {}", Error::description(&why)),
        Ok(result) => println!("Read {}: {} bytes", path.display(), result),
    };

    file_data
}

// instruction debugging
pub struct OpDebugger
{
    pub jump_queue: Vec<u8>
}

impl OpDebugger
{
    pub fn new() -> OpDebugger
    {
        OpDebugger
        {
            jump_queue: Vec::<u8>::new()
        }
    }
}

pub fn debug_instruction(opcode: u8, instruction: Option<(&opcodes::Op, u8, &opcodes::AddrMode)>, cpu: &mut cpu::CPU)
{
    match instruction
    {
        Some((instruction, num_cycles, addr_mode)) => {
            let mut operand_hex: String;
            let mut operand: String;

            // RTS? pop from queue to continue logging
            match *instruction
            {
                opcodes::Op::RTS => { let _ = cpu.op_debugger.jump_queue.pop(); return; },
                opcodes::Op::JSR => if !cpu.op_debugger.jump_queue.is_empty() { cpu.op_debugger.jump_queue.push(opcode); return; },
                _ => if !cpu.op_debugger.jump_queue.is_empty() { return; }
            }

            match *addr_mode {
                opcodes::AddrMode::Implied => {
                    operand_hex = format!("       ");
                    operand = format!("       ");
                },
                opcodes::AddrMode::Accumulator => {
                    operand_hex = format!("       ");
                    operand = format!("A      ");
                },
                opcodes::AddrMode::Immediate(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("#${:02X}   ", cpu.read_byte(cpu.prev_PC));
                },
                opcodes::AddrMode::Absolute(..) => {
                    operand_hex = format!(" {:02X} {:02X} ", cpu.read_byte(cpu.prev_PC), cpu.read_byte(cpu.prev_PC + 0x01));
                    operand = format!("${:04X}  ", cpu.read_word_le(cpu.prev_PC));
                },
                opcodes::AddrMode::AbsoluteIndexedX(..) => {
                    operand_hex = format!(" {:02X} {:02X} ", cpu.read_byte(cpu.prev_PC), cpu.read_byte(cpu.prev_PC + 0x01));
                    operand = format!("${:04X},X", cpu.read_word_le(cpu.prev_PC));
                },
                opcodes::AddrMode::AbsoluteIndexedY(..) => {
                    operand_hex = format!(" {:02X} {:02X} ", cpu.read_byte(cpu.prev_PC), cpu.read_byte(cpu.prev_PC + 0x01));
                    operand = format!("${:04X},Y", cpu.read_word_le(cpu.prev_PC));
                },
                opcodes::AddrMode::Zeropage(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("${:02X}    ", cpu.read_byte(cpu.prev_PC));
                },
                opcodes::AddrMode::ZeropageIndexedX(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("${:02X},X", cpu.read_byte(cpu.prev_PC));
                },
                opcodes::AddrMode::ZeropageIndexedY(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("${:02X},Y", cpu.read_byte(cpu.prev_PC));
                },
                opcodes::AddrMode::Relative(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    let b: i8 = cpu.read_byte(cpu.prev_PC) as i8;
                    operand = format!("${:04X}  ", ((cpu.prev_PC + 1) as i16 + b as i16) as u16);
                },
                opcodes::AddrMode::Indirect(..) => {
                    operand_hex = format!(" {:02X} {:02X} ", cpu.read_byte(cpu.prev_PC), cpu.read_byte(cpu.prev_PC + 0x01));
                    operand = format!("(${:04X})", cpu.read_word_le(cpu.prev_PC));
                },
                opcodes::AddrMode::IndexedIndirectX(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("(${:02X},X)", cpu.read_byte(cpu.prev_PC));
                },
                opcodes::AddrMode::IndirectIndexedY(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("(${:02X}),Y", cpu.read_byte(cpu.prev_PC));
                },
            }

            println!("${:04X}: {:02X}{} {} {}    <- A: {:02X} X: {:02X} Y: {:02X} SP: {:02X} 00: {:02X} 01: {:02X} CZIDB-VN: [{:08b}] ({} cycles)", cpu.prev_PC - 1, opcode, operand_hex, instruction, operand, cpu.A, cpu.X, cpu.Y, cpu.SP, cpu.read_byte(0x0000), cpu.read_byte(0x0001), cpu.P, num_cycles);

            // JSR? push on queue to supress logging
            match *instruction
            {
                opcodes::Op::JSR => cpu.op_debugger.jump_queue.push(opcode),
                _ => ()
            }
        },
        None => ()
    }
}
<commit_msg>added memset8<commit_after>use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
use std::io::SeekFrom;
use std::path::Path;

use c64::cpu;
use c64::opcodes;

// helper macros to easily extract references from Option<RefCell<...>>
macro_rules! as_ref {
    ($x:expr) => ($x.as_ref().unwrap().borrow_mut())
}

macro_rules! as_mut {
    ($x:expr) => ($x.as_mut().unwrap().borrow_mut())
}

pub fn open_file(filename: &str, offset: u64) -> Vec<u8>
{
    let path = Path::new(&filename);

    let mut file = match File::open(&path) {
        Err(why) => panic!("Couldn't open {}: {}", path.display(), Error::description(&why)),
        Ok(file) => file,
    };

    let mut file_data = Vec::<u8>::new();

    let _ = file.seek(SeekFrom::Start(offset));
    let result = file.read_to_end(&mut file_data);

    match result {
        Err(why)   => panic!("Error reading file: {}", Error::description(&why)),
        Ok(result) => println!("Read {}: {} bytes", path.display(), result),
    };

    file_data
}

// set 8 consecutive buffer elements to single value for faster update of
// a single 8-pixel screen chunk
pub fn memset8(buffer: &mut [u32], start: usize, value: u8)
{
    buffer[start]   = value as u32;
    buffer[start+1] = buffer[start];
    buffer[start+2] = buffer[start];
    buffer[start+3] = buffer[start];
    buffer[start+4] = buffer[start];
    buffer[start+5] = buffer[start];
    buffer[start+6] = buffer[start];
    buffer[start+7] = buffer[start];
}

// instruction debugging
pub struct OpDebugger
{
    pub jump_queue: Vec<u8>
}

impl OpDebugger
{
    pub fn new() -> OpDebugger
    {
        OpDebugger
        {
            jump_queue: Vec::<u8>::new()
        }
    }
}

pub fn debug_instruction(opcode: u8, instruction: Option<(&opcodes::Op, u8, &opcodes::AddrMode)>, cpu: &mut cpu::CPU)
{
    match instruction
    {
        Some((instruction, num_cycles, addr_mode)) => {
            let mut operand_hex: String;
            let mut operand: String;

            // RTS? pop from queue to continue logging
            match *instruction
            {
                opcodes::Op::RTS => { let _ = cpu.op_debugger.jump_queue.pop(); return; },
                opcodes::Op::JSR => if !cpu.op_debugger.jump_queue.is_empty() { cpu.op_debugger.jump_queue.push(opcode); return; },
                _ => if !cpu.op_debugger.jump_queue.is_empty() { return; }
            }

            match *addr_mode {
                opcodes::AddrMode::Implied => {
                    operand_hex = format!("       ");
                    operand = format!("       ");
                },
                opcodes::AddrMode::Accumulator => {
                    operand_hex = format!("       ");
                    operand = format!("A      ");
                },
                opcodes::AddrMode::Immediate(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("#${:02X}   ", cpu.read_byte(cpu.prev_PC));
                },
                opcodes::AddrMode::Absolute(..) => {
                    operand_hex = format!(" {:02X} {:02X} ", cpu.read_byte(cpu.prev_PC), cpu.read_byte(cpu.prev_PC + 0x01));
                    operand = format!("${:04X}  ", cpu.read_word_le(cpu.prev_PC));
                },
                opcodes::AddrMode::AbsoluteIndexedX(..) => {
                    operand_hex = format!(" {:02X} {:02X} ", cpu.read_byte(cpu.prev_PC), cpu.read_byte(cpu.prev_PC + 0x01));
                    operand = format!("${:04X},X", cpu.read_word_le(cpu.prev_PC));
                },
                opcodes::AddrMode::AbsoluteIndexedY(..) => {
                    operand_hex = format!(" {:02X} {:02X} ", cpu.read_byte(cpu.prev_PC), cpu.read_byte(cpu.prev_PC + 0x01));
                    operand = format!("${:04X},Y", cpu.read_word_le(cpu.prev_PC));
                },
                opcodes::AddrMode::Zeropage(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("${:02X}    ", cpu.read_byte(cpu.prev_PC));
                },
                opcodes::AddrMode::ZeropageIndexedX(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("${:02X},X", cpu.read_byte(cpu.prev_PC));
                },
                opcodes::AddrMode::ZeropageIndexedY(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("${:02X},Y", cpu.read_byte(cpu.prev_PC));
                },
                opcodes::AddrMode::Relative(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    let b: i8 = cpu.read_byte(cpu.prev_PC) as i8;
                    operand = format!("${:04X}  ", ((cpu.prev_PC + 1) as i16 + b as i16) as u16);
                },
                opcodes::AddrMode::Indirect(..) => {
                    operand_hex = format!(" {:02X} {:02X} ", cpu.read_byte(cpu.prev_PC), cpu.read_byte(cpu.prev_PC + 0x01));
                    operand = format!("(${:04X})", cpu.read_word_le(cpu.prev_PC));
                },
                opcodes::AddrMode::IndexedIndirectX(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("(${:02X},X)", cpu.read_byte(cpu.prev_PC));
                },
                opcodes::AddrMode::IndirectIndexedY(..) => {
                    operand_hex = format!(" {:02X}    ", cpu.read_byte(cpu.prev_PC));
                    operand = format!("(${:02X}),Y", cpu.read_byte(cpu.prev_PC));
                },
            }

            println!("${:04X}: {:02X}{} {} {}    <- A: {:02X} X: {:02X} Y: {:02X} SP: {:02X} 00: {:02X} 01: {:02X} CZIDB-VN: [{:08b}] ({} cycles)", cpu.prev_PC - 1, opcode, operand_hex, instruction, operand, cpu.A, cpu.X, cpu.Y, cpu.SP, cpu.read_byte(0x0000), cpu.read_byte(0x0001), cpu.P, num_cycles);

            // JSR? push on queue to supress logging
            match *instruction
            {
                opcodes::Op::JSR => cpu.op_debugger.jump_queue.push(opcode),
                _ => ()
            }
        },
        None => ()
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::path::PathBuf;

use failure::{bail, format_err, Error};
use if_chain::if_chain;
use lazy_static::lazy_static;
use log::{debug, info};
use regex::Regex;

use crate::api::{Ref, Repo};

#[derive(Copy, Clone)]
pub enum GitReference<'a> {
    Commit(git2::Oid),
    Symbolic(&'a str),
}

impl<'a> fmt::Display for GitReference<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            GitReference::Commit(ref c) => write!(f, "{}", c),
            GitReference::Symbolic(ref s) => write!(f, "{}", s),
        }
    }
}

#[derive(Debug)]
pub struct CommitSpec {
    pub repo: String,
    pub path: Option<PathBuf>,
    pub rev: String,
    pub prev_rev: Option<String>,
}

impl fmt::Display for CommitSpec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}@{}", &self.repo, &self.rev)
    }
}

#[derive(Debug, PartialEq, Eq)]
struct VcsUrl {
    pub provider: String,
    pub id: String,
}

fn parse_rev_range(rng: &str) -> (Option<String>, String) {
    if rng == "" {
        return (None, "HEAD".into());
    }
    let mut iter = rng.rsplitn(2, "..");
    let rev = iter.next().unwrap_or("HEAD");
    (iter.next().map(str::to_owned), rev.to_string())
}

impl CommitSpec {
    pub fn parse(s: &str) -> Result<CommitSpec, Error> {
        lazy_static! {
            static ref SPEC_RE: Regex = Regex::new(r"^([^@#]+)(?:#([^@]+))?(?:@(.+))?$").unwrap();
        }
        if let Some(caps) = SPEC_RE.captures(s) {
            let (prev_rev, rev) = parse_rev_range(caps.get(3).map(|x| x.as_str()).unwrap_or(""));
            Ok(CommitSpec {
                repo: caps[1].to_string(),
                path: caps.get(2).map(|x| PathBuf::from(x.as_str())),
                rev,
                prev_rev,
            })
        } else {
            bail!("Could not parse commit spec '{}'", s)
        }
    }

    pub fn reference(&self) -> GitReference<'_> {
        if let Ok(oid) = git2::Oid::from_str(&self.rev) {
            GitReference::Commit(oid)
        } else {
            GitReference::Symbolic(&self.rev)
        }
    }

    pub fn prev_reference(&self) -> Option<GitReference<'_>> {
        self.prev_rev.as_ref().map(|rev| {
            if let Ok(oid) = git2::Oid::from_str(rev) {
                GitReference::Commit(oid)
            } else {
                GitReference::Symbolic(rev)
            }
        })
    }
}

fn strip_git_suffix(s: &str) -> &str {
    s.trim_end_matches(".git")
}

impl VcsUrl {
    pub fn parse(url: &str) -> VcsUrl {
        lazy_static! {
            static ref GIT_URL_RE: Regex =
                Regex::new(r"^(?:ssh|https?)://(?:[^@]+@)?([^/]+)/(.+)$").unwrap();
            static ref GIT_SSH_RE: Regex = Regex::new(r"^(?:[^@]+@)?([^/]+):(.+)$").unwrap();
        }

        if let Some(caps) = GIT_URL_RE.captures(url) {
            return VcsUrl::from_git_parts(&caps[1], &caps[2]);
        }

        if let Some(caps) = GIT_SSH_RE.captures(url) {
            return VcsUrl::from_git_parts(&caps[1], &caps[2]);
        }

        VcsUrl {
            provider: "".into(),
            id: url.into(),
        }
    }

    fn from_git_parts(host: &str, path: &str) -> VcsUrl {
        lazy_static! {
            static ref VS_DOMAIN_RE: Regex = Regex::new(r"^([^.]+)\.visualstudio.com$").unwrap();
            static ref VS_GIT_PATH_RE: Regex = Regex::new(r"^_git/(.+?)(?:\.git)?$").unwrap();
            static ref VS_TRAILING_GIT_PATH_RE: Regex = Regex::new(r"(.+?)/_git$").unwrap();
        }
        if let Some(caps) = VS_DOMAIN_RE.captures(host) {
            let username = &caps[1];
            if let Some(caps) = VS_GIT_PATH_RE.captures(path) {
                return VcsUrl {
                    provider: host.to_lowercase(),
                    id: format!("{}/{}", username.to_lowercase(), &caps[1].to_lowercase()),
                };
            }
            if let Some(caps) = VS_TRAILING_GIT_PATH_RE.captures(path) {
                return VcsUrl {
                    provider: host.into(),
                    id: caps[1].to_string(),
                };
            }
        }
        VcsUrl {
            provider: host.to_lowercase(),
            id: strip_git_suffix(path).to_lowercase(),
        }
    }
}

fn is_matching_url(a: &str, b: &str) -> bool {
    VcsUrl::parse(a) == VcsUrl::parse(b)
}

fn find_reference_url(repo: &str, repos: &[Repo]) -> Result<String, Error> {
    let mut found_non_git = false;
    for configured_repo in repos {
        if configured_repo.name != repo {
            continue;
        }

        match configured_repo.provider.id.as_str() {
            "git"
            | "github"
            | "bitbucket"
            | "visualstudio"
            | "integrations:github"
            | "integrations:github_enterprise"
            | "integrations:gitlab"
            | "integrations:bitbucket"
            | "integrations:vsts" => {
                if let Some(ref url) = configured_repo.url {
                    debug!("  Got reference URL for repo {}: {}", repo, url);
                    return Ok(url.clone());
                }
            }
            _ => {
                debug!("  unknown repository {} skipped", configured_repo);
                found_non_git = true;
            }
        }
    }

    if found_non_git {
        bail!("For non git repositories explicit revisions are required");
    } else {
        bail!("Could not find matching repository for {}", repo);
    }
}

fn find_matching_rev(
    reference: GitReference<'_>,
    spec: &CommitSpec,
    repos: &[Repo],
    disable_discovery: bool,
) -> Result<Option<String>, Error> {
    macro_rules! log_match {
        ($ex:expr) => {{
            let val = $ex;
            info!("  -> found matching revision {}", val);
            val
        }};
    }

    info!("Resolving {} ({})", &reference, spec);

    let r = match reference {
        GitReference::Commit(commit) => {
            return Ok(Some(log_match!(commit.to_string())));
        }
        GitReference::Symbolic(r) => r,
    };

    let (repo, discovery) = if let Some(ref path) = spec.path {
        (git2::Repository::open(path)?, false)
    } else {
        (git2::Repository::open_from_env()?, !disable_discovery)
    };

    let reference_url = find_reference_url(&spec.repo, repos)?;
    debug!("  Looking for reference URL {}", &reference_url);

    // direct reference in root repository found.  If we are in discovery
    // mode we want to also check for matching URLs.
    if_chain! {
        if let Ok(remote) = repo.find_remote("origin");
        if let Some(url) = remote.url();
        then {
            if !discovery || is_matching_url(url, &reference_url) {
                debug!("  found match: {} == {}", url, &reference_url);
                let head = repo.revparse_single(r)?;
                return Ok(Some(log_match!(head.id().to_string())));
            } else {
                debug!("  not a match: {} != {}", url, &reference_url);
            }
        }
    }

    // in discovery mode we want to find that repo in associated submodules.
    for submodule in repo.submodules()? {
        if let Some(submodule_url) = submodule.url() {
            debug!("  found submodule with URL {}", submodule_url);
            if is_matching_url(submodule_url, &reference_url) {
                debug!(
                    "  found submodule match: {} == {}",
                    submodule_url, &reference_url
                );
                // heads on submodules is easier so let's start with that
                // because that does not require the submodule to be
                // checked out.
                if r == "HEAD" {
                    if let Some(head_oid) = submodule.head_id() {
                        return Ok(Some(log_match!(head_oid.to_string())));
                    }
                }

                // otherwise we need to open the submodule which requires
                // it to be checked out.
                if let Ok(subrepo) = submodule.open() {
                    let head = subrepo.revparse_single(r)?;
                    return Ok(Some(log_match!(head.id().to_string())));
                }
            } else {
                debug!(
                    "  not a submodule match: {} != {}",
                    submodule_url, &reference_url
                );
            }
        }
    }

    info!("  -> no matching revision found");
    Ok(None)
}

fn find_matching_revs(
    spec: &CommitSpec,
    repos: &[Repo],
    disable_discovery: bool,
) -> Result<(Option<String>, String), Error> {
    fn error(r: GitReference<'_>, repo: &str) -> Error {
        format_err!(
            "Could not find commit '{}' for '{}'. If you do not have local \
             checkouts of the repositories in question referencing tags or \
             other references will not work and you need to refer to \
             revisions explicitly.",
            r,
            repo
        )
    }

    let rev = if let Some(rev) =
        find_matching_rev(spec.reference(), &spec, &repos[..], disable_discovery)?
    {
        rev
    } else {
        return Err(error(spec.reference(), &spec.repo));
    };

    let prev_rev = if let Some(rev) = spec.prev_reference() {
        if let Some(rv) = find_matching_rev(rev, &spec, &repos[..], disable_discovery)? {
            Some(rv)
        } else {
            return Err(error(rev, &spec.repo));
        }
    } else {
        None
    };

    Ok((prev_rev, rev))
}

pub fn find_head() -> Result<String, Error> {
    let repo = git2::Repository::open_from_env()?;
    let head = repo.revparse_single("HEAD")?;
    Ok(head.id().to_string())
}

/// Given commit specs and repos this returns a list of head commits
/// from it.
pub fn find_heads(specs: Option<Vec<CommitSpec>>, repos: &[Repo]) -> Result<Vec<Ref>, Error> {
    let mut rv = vec![];

    // if commit specs were explicitly provided find head commits with
    // limited amounts of magic.
    if let Some(specs) = specs {
        for spec in &specs {
            let (prev_rev, rev) = find_matching_revs(&spec, &repos[..], specs.len() == 1)?;
            rv.push(Ref {
                repo: spec.repo.clone(),
                rev,
                prev_rev,
            });
        }

    // otherwise apply all the magic available
    } else {
        for repo in repos {
            let spec = CommitSpec {
                repo: repo.name.to_string(),
                path: None,
                rev: "HEAD".into(),
                prev_rev: None,
            };
            if let Some(rev) = find_matching_rev(spec.reference(), &spec, &repos[..], false)? {
                rv.push(Ref {
                    repo: repo.name.to_string(),
                    rev,
                    prev_rev: None,
                });
            }
        }
    }

    Ok(rv)
}

#[test]
fn test_url_parsing() {
    assert_eq!(
        VcsUrl::parse("http://github.com/mitsuhiko/flask"),
        VcsUrl {
            provider: "github.com".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@github.com:mitsuhiko/flask.git"),
        VcsUrl {
            provider: "github.com".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("http://bitbucket.org/mitsuhiko/flask"),
        VcsUrl {
            provider: "bitbucket.org".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@bitbucket.org:mitsuhiko/flask.git"),
        VcsUrl {
            provider: "bitbucket.org".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://neilmanvar.visualstudio.com/_git/sentry-demo"),
        VcsUrl {
            provider: "neilmanvar.visualstudio.com".into(),
            id: "neilmanvar/sentry-demo".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://project@mydomain.visualstudio.com/project/repo/_git"),
        VcsUrl {
            provider: "mydomain.visualstudio.com".into(),
            id: "project/repo".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://github.myenterprise.com/mitsuhiko/flask.git"),
        VcsUrl {
            provider: "github.myenterprise.com".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://gitlab.example.com/gitlab-org/gitlab-ce"),
        VcsUrl {
            provider: "gitlab.example.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@gitlab.example.com:gitlab-org/gitlab-ce.git"),
        VcsUrl {
            provider: "gitlab.example.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://gitlab.com/gitlab-org/gitlab-ce"),
        VcsUrl {
            provider: "gitlab.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@gitlab.com:gitlab-org/gitlab-ce.git"),
        VcsUrl {
            provider: "gitlab.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@gitlab.com:gitlab-org/GitLab-CE.git"),
        VcsUrl {
            provider: "gitlab.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    )
}

#[test]
fn test_url_normalization() {
    assert!(!is_matching_url(
        "http://github.mycompany.com/mitsuhiko/flask",
        "git@github.com:mitsuhiko/flask.git"
    ));
    assert!(!is_matching_url(
        "git@github.mycompany.com/mitsuhiko/flask",
        "git@github.com:mitsuhiko/flask.git"
    ));
    assert!(is_matching_url(
        "http://github.com/mitsuhiko/flask",
        "git@github.com:mitsuhiko/flask.git"
    ));
    assert!(is_matching_url(
        "https://gitlab.com/gitlab-org/gitlab-ce",
        "git@gitlab.com:gitlab-org/gitlab-ce.git"
    ));
    assert!(is_matching_url(
        "https://gitlab.example.com/gitlab-org/gitlab-ce",
        "git@gitlab.example.com:gitlab-org/gitlab-ce.git"
    ));
    assert!(is_matching_url(
        "https://gitlab.example.com/gitlab-org/GitLab-CE",
        "git@gitlab.example.com:gitlab-org/gitlab-ce.git"
    ))
}
<commit_msg>feat(releases): Allow port in url<commit_after>use std::fmt;
use std::path::PathBuf;

use failure::{bail, format_err, Error};
use if_chain::if_chain;
use lazy_static::lazy_static;
use log::{debug, info};
use regex::Regex;

use crate::api::{Ref, Repo};

#[derive(Copy, Clone)]
pub enum GitReference<'a> {
    Commit(git2::Oid),
    Symbolic(&'a str),
}

impl<'a> fmt::Display for GitReference<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            GitReference::Commit(ref c) => write!(f, "{}", c),
            GitReference::Symbolic(ref s) => write!(f, "{}", s),
        }
    }
}

#[derive(Debug)]
pub struct CommitSpec {
    pub repo: String,
    pub path: Option<PathBuf>,
    pub rev: String,
    pub prev_rev: Option<String>,
}

impl fmt::Display for CommitSpec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}@{}", &self.repo, &self.rev)
    }
}

#[derive(Debug, PartialEq, Eq)]
struct VcsUrl {
    pub provider: String,
    pub id: String,
}

fn parse_rev_range(rng: &str) -> (Option<String>, String) {
    if rng == "" {
        return (None, "HEAD".into());
    }
    let mut iter = rng.rsplitn(2, "..");
    let rev = iter.next().unwrap_or("HEAD");
    (iter.next().map(str::to_owned), rev.to_string())
}

impl CommitSpec {
    pub fn parse(s: &str) -> Result<CommitSpec, Error> {
        lazy_static! {
            static ref SPEC_RE: Regex = Regex::new(r"^([^@#]+)(?:#([^@]+))?(?:@(.+))?$").unwrap();
        }
        if let Some(caps) = SPEC_RE.captures(s) {
            let (prev_rev, rev) = parse_rev_range(caps.get(3).map(|x| x.as_str()).unwrap_or(""));
            Ok(CommitSpec {
                repo: caps[1].to_string(),
                path: caps.get(2).map(|x| PathBuf::from(x.as_str())),
                rev,
                prev_rev,
            })
        } else {
            bail!("Could not parse commit spec '{}'", s)
        }
    }

    pub fn reference(&self) -> GitReference<'_> {
        if let Ok(oid) = git2::Oid::from_str(&self.rev) {
            GitReference::Commit(oid)
        } else {
            GitReference::Symbolic(&self.rev)
        }
    }

    pub fn prev_reference(&self) -> Option<GitReference<'_>> {
        self.prev_rev.as_ref().map(|rev| {
            if let Ok(oid) = git2::Oid::from_str(rev) {
                GitReference::Commit(oid)
            } else {
                GitReference::Symbolic(rev)
            }
        })
    }
}

fn strip_git_suffix(s: &str) -> &str {
    s.trim_end_matches(".git")
}

impl VcsUrl {
    pub fn parse(url: &str) -> VcsUrl {
        lazy_static! {
            static ref GIT_URL_RE: Regex =
                Regex::new(r"^(?:ssh|https?)://(?:[^@]+@)?([^/]+)/(.+)$").unwrap();
            static ref GIT_SSH_RE: Regex = Regex::new(r"^(?:[^@]+@)?([^/]+):(.+)$").unwrap();
        }

        if let Some(caps) = GIT_URL_RE.captures(url) {
            return VcsUrl::from_git_parts(&caps[1], &caps[2]);
        }

        if let Some(caps) = GIT_SSH_RE.captures(url) {
            return VcsUrl::from_git_parts(&caps[1], &caps[2]);
        }

        VcsUrl {
            provider: "".into(),
            id: url.into(),
        }
    }

    fn from_git_parts(host: &str, path: &str) -> VcsUrl {
        lazy_static! {
            static ref VS_DOMAIN_RE: Regex = Regex::new(r"^([^.]+)\.visualstudio.com$").unwrap();
            static ref VS_GIT_PATH_RE: Regex = Regex::new(r"^_git/(.+?)(?:\.git)?$").unwrap();
            static ref VS_TRAILING_GIT_PATH_RE: Regex = Regex::new(r"(.+?)/_git$").unwrap();
            static ref HOST_WITH_PORT: Regex = Regex::new(r"(.*):\d+$").unwrap();
        }

        if let Some(caps) = HOST_WITH_PORT.captures(host) {
            return VcsUrl::from_git_parts(&caps[1], path);
        }
        if let Some(caps) = VS_DOMAIN_RE.captures(host) {
            let username = &caps[1];
            if let Some(caps) = VS_GIT_PATH_RE.captures(path) {
                return VcsUrl {
                    provider: host.to_lowercase(),
                    id: format!("{}/{}", username.to_lowercase(), &caps[1].to_lowercase()),
                };
            }
            if let Some(caps) = VS_TRAILING_GIT_PATH_RE.captures(path) {
                return VcsUrl {
                    provider: host.into(),
                    id: caps[1].to_string(),
                };
            }
        }
        VcsUrl {
            provider: host.to_lowercase(),
            id: strip_git_suffix(path).to_lowercase(),
        }
    }
}

fn is_matching_url(a: &str, b: &str) -> bool {
    VcsUrl::parse(a) == VcsUrl::parse(b)
}

fn find_reference_url(repo: &str, repos: &[Repo]) -> Result<String, Error> {
    let mut found_non_git = false;
    for configured_repo in repos {
        if configured_repo.name != repo {
            continue;
        }

        match configured_repo.provider.id.as_str() {
            "git"
            | "github"
            | "bitbucket"
            | "visualstudio"
            | "integrations:github"
            | "integrations:github_enterprise"
            | "integrations:gitlab"
            | "integrations:bitbucket"
            | "integrations:vsts" => {
                if let Some(ref url) = configured_repo.url {
                    debug!("  Got reference URL for repo {}: {}", repo, url);
                    return Ok(url.clone());
                }
            }
            _ => {
                debug!("  unknown repository {} skipped", configured_repo);
                found_non_git = true;
            }
        }
    }

    if found_non_git {
        bail!("For non git repositories explicit revisions are required");
    } else {
        bail!("Could not find matching repository for {}", repo);
    }
}

fn find_matching_rev(
    reference: GitReference<'_>,
    spec: &CommitSpec,
    repos: &[Repo],
    disable_discovery: bool,
) -> Result<Option<String>, Error> {
    macro_rules! log_match {
        ($ex:expr) => {{
            let val = $ex;
            info!("  -> found matching revision {}", val);
            val
        }};
    }

    info!("Resolving {} ({})", &reference, spec);

    let r = match reference {
        GitReference::Commit(commit) => {
            return Ok(Some(log_match!(commit.to_string())));
        }
        GitReference::Symbolic(r) => r,
    };

    let (repo, discovery) = if let Some(ref path) = spec.path {
        (git2::Repository::open(path)?, false)
    } else {
        (git2::Repository::open_from_env()?, !disable_discovery)
    };

    let reference_url = find_reference_url(&spec.repo, repos)?;
    debug!("  Looking for reference URL {}", &reference_url);

    // direct reference in root repository found.  If we are in discovery
    // mode we want to also check for matching URLs.
    if_chain! {
        if let Ok(remote) = repo.find_remote("origin");
        if let Some(url) = remote.url();
        then {
            if !discovery || is_matching_url(url, &reference_url) {
                debug!("  found match: {} == {}", url, &reference_url);
                let head = repo.revparse_single(r)?;
                return Ok(Some(log_match!(head.id().to_string())));
            } else {
                debug!("  not a match: {} != {}", url, &reference_url);
            }
        }
    }

    // in discovery mode we want to find that repo in associated submodules.
    for submodule in repo.submodules()? {
        if let Some(submodule_url) = submodule.url() {
            debug!("  found submodule with URL {}", submodule_url);
            if is_matching_url(submodule_url, &reference_url) {
                debug!(
                    "  found submodule match: {} == {}",
                    submodule_url, &reference_url
                );
                // heads on submodules is easier so let's start with that
                // because that does not require the submodule to be
                // checked out.
                if r == "HEAD" {
                    if let Some(head_oid) = submodule.head_id() {
                        return Ok(Some(log_match!(head_oid.to_string())));
                    }
                }

                // otherwise we need to open the submodule which requires
                // it to be checked out.
                if let Ok(subrepo) = submodule.open() {
                    let head = subrepo.revparse_single(r)?;
                    return Ok(Some(log_match!(head.id().to_string())));
                }
            } else {
                debug!(
                    "  not a submodule match: {} != {}",
                    submodule_url, &reference_url
                );
            }
        }
    }

    info!("  -> no matching revision found");
    Ok(None)
}

fn find_matching_revs(
    spec: &CommitSpec,
    repos: &[Repo],
    disable_discovery: bool,
) -> Result<(Option<String>, String), Error> {
    fn error(r: GitReference<'_>, repo: &str) -> Error {
        format_err!(
            "Could not find commit '{}' for '{}'. If you do not have local \
             checkouts of the repositories in question referencing tags or \
             other references will not work and you need to refer to \
             revisions explicitly.",
            r,
            repo
        )
    }

    let rev = if let Some(rev) =
        find_matching_rev(spec.reference(), &spec, &repos[..], disable_discovery)?
    {
        rev
    } else {
        return Err(error(spec.reference(), &spec.repo));
    };

    let prev_rev = if let Some(rev) = spec.prev_reference() {
        if let Some(rv) = find_matching_rev(rev, &spec, &repos[..], disable_discovery)? {
            Some(rv)
        } else {
            return Err(error(rev, &spec.repo));
        }
    } else {
        None
    };

    Ok((prev_rev, rev))
}

pub fn find_head() -> Result<String, Error> {
    let repo = git2::Repository::open_from_env()?;
    let head = repo.revparse_single("HEAD")?;
    Ok(head.id().to_string())
}

/// Given commit specs and repos this returns a list of head commits
/// from it.
pub fn find_heads(specs: Option<Vec<CommitSpec>>, repos: &[Repo]) -> Result<Vec<Ref>, Error> {
    let mut rv = vec![];

    // if commit specs were explicitly provided find head commits with
    // limited amounts of magic.
    if let Some(specs) = specs {
        for spec in &specs {
            let (prev_rev, rev) = find_matching_revs(&spec, &repos[..], specs.len() == 1)?;
            rv.push(Ref {
                repo: spec.repo.clone(),
                rev,
                prev_rev,
            });
        }

    // otherwise apply all the magic available
    } else {
        for repo in repos {
            let spec = CommitSpec {
                repo: repo.name.to_string(),
                path: None,
                rev: "HEAD".into(),
                prev_rev: None,
            };
            if let Some(rev) = find_matching_rev(spec.reference(), &spec, &repos[..], false)? {
                rv.push(Ref {
                    repo: repo.name.to_string(),
                    rev,
                    prev_rev: None,
                });
            }
        }
    }

    Ok(rv)
}

#[test]
fn test_url_parsing() {
    assert_eq!(
        VcsUrl::parse("http://github.com/mitsuhiko/flask"),
        VcsUrl {
            provider: "github.com".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@github.com:mitsuhiko/flask.git"),
        VcsUrl {
            provider: "github.com".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("http://bitbucket.org/mitsuhiko/flask"),
        VcsUrl {
            provider: "bitbucket.org".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@bitbucket.org:mitsuhiko/flask.git"),
        VcsUrl {
            provider: "bitbucket.org".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://neilmanvar.visualstudio.com/_git/sentry-demo"),
        VcsUrl {
            provider: "neilmanvar.visualstudio.com".into(),
            id: "neilmanvar/sentry-demo".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://project@mydomain.visualstudio.com/project/repo/_git"),
        VcsUrl {
            provider: "mydomain.visualstudio.com".into(),
            id: "project/repo".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://github.myenterprise.com/mitsuhiko/flask.git"),
        VcsUrl {
            provider: "github.myenterprise.com".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://gitlab.example.com/gitlab-org/gitlab-ce"),
        VcsUrl {
            provider: "gitlab.example.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@gitlab.example.com:gitlab-org/gitlab-ce.git"),
        VcsUrl {
            provider: "gitlab.example.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://gitlab.com/gitlab-org/gitlab-ce"),
        VcsUrl {
            provider: "gitlab.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@gitlab.com:gitlab-org/gitlab-ce.git"),
        VcsUrl {
            provider: "gitlab.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@gitlab.com:gitlab-org/GitLab-CE.git"),
        VcsUrl {
            provider: "gitlab.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    )
}

#[test]
fn test_url_normalization() {
    assert!(!is_matching_url(
        "http://github.mycompany.com/mitsuhiko/flask",
        "git@github.com:mitsuhiko/flask.git"
    ));
    assert!(!is_matching_url(
        "git@github.mycompany.com/mitsuhiko/flask",
        "git@github.com:mitsuhiko/flask.git"
    ));
    assert!(is_matching_url(
        "http://github.com/mitsuhiko/flask",
        "git@github.com:mitsuhiko/flask.git"
    ));
    assert!(is_matching_url(
        "https://gitlab.com/gitlab-org/gitlab-ce",
        "git@gitlab.com:gitlab-org/gitlab-ce.git"
    ));
    assert!(is_matching_url(
        "https://gitlab.example.com/gitlab-org/gitlab-ce",
        "git@gitlab.example.com:gitlab-org/gitlab-ce.git"
    ));
    assert!(is_matching_url(
        "https://gitlab.example.com/gitlab-org/GitLab-CE",
        "git@gitlab.example.com:gitlab-org/gitlab-ce.git"
    ));
    assert!(is_matching_url(
        "https://gitlab.example.com/gitlab-org/GitLab-CE",
        "ssh://git@gitlab.example.com:22/gitlab-org/GitLab-CE"
    ))
}
<|endoftext|>


<commit_before>//! `UnionFind<K>` is a disjoint-set data structure.

use super::graph::IndexType;

/// `UnionFind<K>` is a disjoint-set data structure. It tracks set membership of *n* elements
/// indexed from *0* to *n - 1*. The scalar type is `K` which must be an unsigned integer type.
///
/// <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>
///
/// Too awesome not to quote:
///
/// “The amortized time per operation is **O(α(n))** where **α(n)** is the
/// inverse of **f(x) = A(x, x)** with **A** being the extremely fast-growing Ackermann function.”
#[derive(Debug, Clone)]
pub struct UnionFind<K>
{
    // For element at index *i*, store the index of its parent; the representative itself
    // stores its own index. This forms equivalence classes which are the disjoint sets, each
    // with a unique representative.
    parent: Vec<K>,
    // It is a balancing tree structure,
    // so the ranks are logarithmic in the size of the container -- a byte is more than enough.
    //
    // Rank is separated out both to save space and to save cache in when searching in the parent
    // vector.
    rank: Vec<u8>,
}

#[inline]
unsafe fn get_unchecked<K>(xs: &[K], index: usize) -> &K
{
    debug_assert!(index < xs.len());
    xs.get_unchecked(index)
}

impl<K> UnionFind<K>
    where K: IndexType
{
    /// Create a new `UnionFind` of `n` disjoint sets.
    pub fn new(n: usize) -> Self
    {
        let rank = vec![0; n];
        let parent = (0..n).map(K::new).collect::<Vec<K>>();

        UnionFind{parent: parent, rank: rank}
    }

    /// Return the representative for `x`.
    ///
    /// **Panics** if `x` is out of bounds.
    pub fn find(&self, x: K) -> K
    {
        assert!(x.index() < self.parent.len());
        unsafe {
            let mut x = x;
            loop {
                // Use unchecked indexing because we can trust the internal set ids.
                let xparent = *get_unchecked(&self.parent, x.index());
                if xparent == x {
                    break
                }
                x = xparent;
            }
            x
        }
    }

    /// Return the representative for `x`.
    ///
    /// Write back the found representative, flattening the internal
    /// datastructure in the process and quicken future lookups.
    ///
    /// **Panics** if `x` is out of bounds.
    pub fn find_mut(&mut self, x: K) -> K
    {
        assert!(x.index() < self.parent.len());
        unsafe {
            self.find_mut_recursive(x)
        }
    }

    unsafe fn find_mut_recursive(&mut self, x: K) -> K
    {
        let xparent = *get_unchecked(&self.parent, x.index());
        if xparent != x {
            let xrep = self.find_mut_recursive(xparent);
            let xparent = self.parent.get_unchecked_mut(x.index());
            *xparent = xrep;
            *xparent
        } else {
            xparent
        }
    }

    /// Unify the two sets containing `x` and `y`.
    ///
    /// Return `false` if the sets were already the same, `true` if they were unified.
    ///
    /// **Panics** if `x` or `y` is out of bounds.
    pub fn union(&mut self, x: K, y: K) -> bool
    {
        if x == y {
            return false
        }
        let xrep = self.find_mut(x);
        let yrep = self.find_mut(y);

        if xrep == yrep {
            return false
        }

        let xrepu = xrep.index();
        let yrepu = yrep.index();
        let xrank = self.rank[xrepu];
        let yrank = self.rank[yrepu];

        // The rank corresponds roughly to the depth of the treeset, so put the
        // smaller set below the larger
        if xrank < yrank {
            self.parent[xrepu] = yrep;
        } else if xrank > yrank {
            self.parent[yrepu] = xrep;
        } else {
            // put y below x when equal.
            self.parent[yrepu] = xrep;
            self.rank[xrepu] += 1;
        }
        true
    }

    /// Return a vector mapping each element to its representative.
    pub fn into_labeling(mut self) -> Vec<K>
    {
        // write in the labeling of each element
        unsafe {
            for ix in 0..self.parent.len() {
                let k = *get_unchecked(&self.parent, ix);
                let xrep = self.find_mut_recursive(k);
                *self.parent.get_unchecked_mut(ix) = xrep;
            }
        }
        self.parent
    }
}
<commit_msg>FEAT: Improve performance of UnionFind's find_mut<commit_after>//! `UnionFind<K>` is a disjoint-set data structure.

use super::graph::IndexType;

/// `UnionFind<K>` is a disjoint-set data structure. It tracks set membership of *n* elements
/// indexed from *0* to *n - 1*. The scalar type is `K` which must be an unsigned integer type.
///
/// <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>
///
/// Too awesome not to quote:
///
/// “The amortized time per operation is **O(α(n))** where **α(n)** is the
/// inverse of **f(x) = A(x, x)** with **A** being the extremely fast-growing Ackermann function.”
#[derive(Debug, Clone)]
pub struct UnionFind<K>
{
    // For element at index *i*, store the index of its parent; the representative itself
    // stores its own index. This forms equivalence classes which are the disjoint sets, each
    // with a unique representative.
    parent: Vec<K>,
    // It is a balancing tree structure,
    // so the ranks are logarithmic in the size of the container -- a byte is more than enough.
    //
    // Rank is separated out both to save space and to save cache in when searching in the parent
    // vector.
    rank: Vec<u8>,
}

#[inline]
unsafe fn get_unchecked<K>(xs: &[K], index: usize) -> &K
{
    debug_assert!(index < xs.len());
    xs.get_unchecked(index)
}

#[inline]
unsafe fn get_unchecked_mut<K>(xs: &mut [K], index: usize) -> &mut K
{
    debug_assert!(index < xs.len());
    xs.get_unchecked_mut(index)
}

impl<K> UnionFind<K>
    where K: IndexType
{
    /// Create a new `UnionFind` of `n` disjoint sets.
    pub fn new(n: usize) -> Self
    {
        let rank = vec![0; n];
        let parent = (0..n).map(K::new).collect::<Vec<K>>();

        UnionFind{parent: parent, rank: rank}
    }

    /// Return the representative for `x`.
    ///
    /// **Panics** if `x` is out of bounds.
    pub fn find(&self, x: K) -> K
    {
        assert!(x.index() < self.parent.len());
        unsafe {
            let mut x = x;
            loop {
                // Use unchecked indexing because we can trust the internal set ids.
                let xparent = *get_unchecked(&self.parent, x.index());
                if xparent == x {
                    break
                }
                x = xparent;
            }
            x
        }
    }

    /// Return the representative for `x`.
    ///
    /// Write back the found representative, flattening the internal
    /// datastructure in the process and quicken future lookups.
    ///
    /// **Panics** if `x` is out of bounds.
    pub fn find_mut(&mut self, x: K) -> K
    {
        assert!(x.index() < self.parent.len());
        unsafe {
            self.find_mut_recursive(x)
        }
    }

    unsafe fn find_mut_recursive(&mut self, mut x: K) -> K
    {
        let mut parent = *get_unchecked(&self.parent, x.index());
        while parent != x {
            let grandparent = *get_unchecked(&self.parent, parent.index());
            *get_unchecked_mut(&mut self.parent, x.index()) = grandparent;
            x = parent;
            parent = grandparent;
        }
        x
    }

    /// Unify the two sets containing `x` and `y`.
    ///
    /// Return `false` if the sets were already the same, `true` if they were unified.
    ///
    /// **Panics** if `x` or `y` is out of bounds.
    pub fn union(&mut self, x: K, y: K) -> bool
    {
        if x == y {
            return false
        }
        let xrep = self.find_mut(x);
        let yrep = self.find_mut(y);

        if xrep == yrep {
            return false
        }

        let xrepu = xrep.index();
        let yrepu = yrep.index();
        let xrank = self.rank[xrepu];
        let yrank = self.rank[yrepu];

        // The rank corresponds roughly to the depth of the treeset, so put the
        // smaller set below the larger
        if xrank < yrank {
            self.parent[xrepu] = yrep;
        } else if xrank > yrank {
            self.parent[yrepu] = xrep;
        } else {
            // put y below x when equal.
            self.parent[yrepu] = xrep;
            self.rank[xrepu] += 1;
        }
        true
    }

    /// Return a vector mapping each element to its representative.
    pub fn into_labeling(mut self) -> Vec<K>
    {
        // write in the labeling of each element
        unsafe {
            for ix in 0..self.parent.len() {
                let k = *get_unchecked(&self.parent, ix);
                let xrep = self.find_mut_recursive(k);
                *self.parent.get_unchecked_mut(ix) = xrep;
            }
        }
        self.parent
    }
}
<|endoftext|>


<commit_before><commit_msg>gigasecond: add stub with type signature (#306)<commit_after>extern crate chrono;
use chrono::*;

// Returns a UTC DateTime one billion seconds after start.
pub fn after(start: DateTime<UTC>) -> DateTime<UTC> {
    unimplemented!()
}
<|endoftext|>


<commit_before>//! Shared mathematical utility functions.

/// Cut value to be inside given range
///
/// ```
/// use image::math::utils;
///
/// assert_eq!(utils::clamp(-5, 0, 10),  0);
/// assert_eq!(utils::clamp( 6, 0, 10),  6);
/// assert_eq!(utils::clamp(15, 0, 10), 10);
/// ```
#[inline]
#[deprecated]
pub fn clamp<N>(a: N, min: N, max: N) -> N
where
    N: PartialOrd,
{
    if a < min {
        return min;
    }
    if a > max {
        return max;
    }
    a
}

/// Calculates the width and height an image should be resized to.
/// This preserves aspect ratio, and based on the `fill` parameter
/// will either fill the dimensions to fit inside the smaller constraint
/// (will overflow the specified bounds on one axis to preserve
/// aspect ratio), or will shrink so that both dimensions are
/// completely contained with in the given `width` and `height`,
/// with empty space on one axis.
pub(crate) fn resize_dimensions(width: u32, height: u32, nwidth: u32, nheight: u32, fill: bool) -> (u32, u32) {
    let ratio = u64::from(width) * u64::from(nheight);
    let nratio = u64::from(nwidth) * u64::from(height);

    let use_width = if fill {
        nratio > ratio
    } else {
        nratio <= ratio
    };
    let intermediate = if use_width {
        u64::from(height) * u64::from(nwidth) / u64::from(width)
    } else {
        u64::from(width) * u64::from(nheight) / u64::from(height)
    };
    if use_width {
        if intermediate <= u64::from(::std::u32::MAX) {
            (nwidth, intermediate as u32)
        } else {
            (
                (u64::from(nwidth) * u64::from(::std::u32::MAX) / intermediate) as u32,
                ::std::u32::MAX,
            )
        }
    } else if intermediate <= u64::from(::std::u32::MAX) {
        (intermediate as u32, nheight)
    } else {
        (
            ::std::u32::MAX,
            (u64::from(nheight) * u64::from(::std::u32::MAX) / intermediate) as u32,
        )
    }
}

#[cfg(test)]
mod test {
    quickcheck! {
        fn resize_bounds_correctly_width(old_w: u32, new_w: u32) -> bool {
            if old_w == 0 || new_w == 0 { return true; }
            let result = super::resize_dimensions(old_w, 400, new_w, ::std::u32::MAX, false);
            result.0 == new_w && result.1 == (400 as f64 * new_w as f64 / old_w as f64) as u32
        }
    }

    quickcheck! {
        fn resize_bounds_correctly_height(old_h: u32, new_h: u32) -> bool {
            if old_h == 0 || new_h == 0 { return true; }
            let result = super::resize_dimensions(400, old_h, ::std::u32::MAX, new_h, false);
            result.1 == new_h && result.0 == (400 as f64 * new_h as f64 / old_h as f64) as u32
        }
    }

    #[test]
    fn resize_handles_fill() {
        let result = super::resize_dimensions(100, 200, 200, 500, true);
        assert!(result.0 == 250);
        assert!(result.1 == 500);

        let result = super::resize_dimensions(200, 100, 500, 200, true);
        assert!(result.0 == 500);
        assert!(result.1 == 250);
    }

    #[test]
    fn resize_handles_overflow() {
        let result = super::resize_dimensions(100, ::std::u32::MAX, 200, ::std::u32::MAX, true);
        assert!(result.0 == 100);
        assert!(result.1 == ::std::u32::MAX);

        let result = super::resize_dimensions(::std::u32::MAX, 100, ::std::u32::MAX, 200, true);
        assert!(result.0 == ::std::u32::MAX);
        assert!(result.1 == 100);
    }
}
<commit_msg>Fixes #1109. Adds a default 1 when resizing dims.<commit_after>//! Shared mathematical utility functions.

/// Cut value to be inside given range
///
/// ```
/// use image::math::utils;
///
/// assert_eq!(utils::clamp(-5, 0, 10),  0);
/// assert_eq!(utils::clamp( 6, 0, 10),  6);
/// assert_eq!(utils::clamp(15, 0, 10), 10);
/// ```
#[inline]
#[deprecated]
pub fn clamp<N>(a: N, min: N, max: N) -> N
where
    N: PartialOrd,
{
    if a < min {
        return min;
    }
    if a > max {
        return max;
    }
    a
}

/// Calculates the width and height an image should be resized to.
/// This preserves aspect ratio, and based on the `fill` parameter
/// will either fill the dimensions to fit inside the smaller constraint
/// (will overflow the specified bounds on one axis to preserve
/// aspect ratio), or will shrink so that both dimensions are
/// completely contained with in the given `width` and `height`,
/// with empty space on one axis.
pub(crate) fn resize_dimensions(width: u32, height: u32, nwidth: u32, nheight: u32, fill: bool) -> (u32, u32) {
    let ratio = u64::from(width) * u64::from(nheight);
    let nratio = u64::from(nwidth) * u64::from(height);

    let use_width = if fill {
        nratio > ratio
    } else {
        nratio <= ratio
    };
    let intermediate = if use_width {
        u64::from(height) * u64::from(nwidth) / u64::from(width)
    } else {
        u64::from(width) * u64::from(nheight) / u64::from(height)
    };
    let intermediate = std::cmp::max(1, intermediate);
    if use_width {
        if intermediate <= u64::from(::std::u32::MAX) {
            (nwidth, intermediate as u32)
        } else {
            (
                (u64::from(nwidth) * u64::from(::std::u32::MAX) / intermediate) as u32,
                ::std::u32::MAX,
            )
        }
    } else if intermediate <= u64::from(::std::u32::MAX) {
        (intermediate as u32, nheight)
    } else {
        (
            ::std::u32::MAX,
            (u64::from(nheight) * u64::from(::std::u32::MAX) / intermediate) as u32,
        )
    }
}

#[cfg(test)]
mod test {
    quickcheck! {
        fn resize_bounds_correctly_width(old_w: u32, new_w: u32) -> bool {
            if old_w == 0 || new_w == 0 { return true; }
            let result = super::resize_dimensions(old_w, 400, new_w, ::std::u32::MAX, false);
            result.0 == new_w && result.1 == (400 as f64 * new_w as f64 / old_w as f64) as u32
        }
    }

    quickcheck! {
        fn resize_bounds_correctly_height(old_h: u32, new_h: u32) -> bool {
            if old_h == 0 || new_h == 0 { return true; }
            let result = super::resize_dimensions(400, old_h, ::std::u32::MAX, new_h, false);
            result.1 == new_h && result.0 == (400 as f64 * new_h as f64 / old_h as f64) as u32
        }
    }

    #[test]
    fn resize_handles_fill() {
        let result = super::resize_dimensions(100, 200, 200, 500, true);
        assert!(result.0 == 250);
        assert!(result.1 == 500);

        let result = super::resize_dimensions(200, 100, 500, 200, true);
        assert!(result.0 == 500);
        assert!(result.1 == 250);
    }

    #[test]
    fn resize_never_rounds_to_zero() {
        let result = super::resize_dimensions(1, 150, 128, 128, false);
        assert!(result.0 > 0);
        assert!(result.1 > 0);
    }

    #[test]
    fn resize_handles_overflow() {
        let result = super::resize_dimensions(100, ::std::u32::MAX, 200, ::std::u32::MAX, true);
        assert!(result.0 == 100);
        assert!(result.1 == ::std::u32::MAX);

        let result = super::resize_dimensions(::std::u32::MAX, 100, ::std::u32::MAX, 200, true);
        assert!(result.0 == ::std::u32::MAX);
        assert!(result.1 == 100);
    }
}
<|endoftext|>