<commit_before>use core::{BytePos, ByteRange};

/// Type of the string
#[derive(Clone, Copy, Debug)]
enum StrStyle {
    /// normal string starts with "
    Cooked,
    /// Raw(n) => raw string started with n #s
    Raw(usize),
}

#[derive(Clone, Copy)]
enum State {
    Code,
    Comment,
    CommentBlock,
    String(StrStyle),
    Char,
    Finished,
}

#[derive(Clone, Copy)]
pub struct CodeIndicesIter<'a> {
    src: &'a str,
    pos: BytePos,
    state: State,
}

impl<'a> Iterator for CodeIndicesIter<'a> {
    type Item = ByteRange;

    fn next(&mut self) -> Option<ByteRange> {
        match self.state {
            State::Code => Some(self.code()),
            State::Comment => Some(self.comment()),
            State::CommentBlock => Some(self.comment_block()),
            State::String(style) => Some(self.string(style)),
            State::Char => Some(self.char()),
            State::Finished => None,
        }
    }
}

impl<'a> CodeIndicesIter<'a> {
    fn code(&mut self) -> ByteRange {
        let mut pos = self.pos;
        let start = match self.state {
            State::String(_) | State::Char => pos.decrement(), // include quote
            _ => pos,
        };
        let src_bytes = self.src.as_bytes();
        for &b in &src_bytes[pos.0..] {
            pos = pos.increment();
            match b {
                b'/' if src_bytes.len() > pos.0 => match src_bytes[pos.0] {
                    b'/' => {
                        self.state = State::Comment;
                        self.pos = pos.increment();
                        return ByteRange::new(start, pos.decrement());
                    }
                    b'*' => {
                        self.state = State::CommentBlock;
                        self.pos = pos.increment();
                        return ByteRange::new(start, pos.decrement());
                    }
                    _ => {}
                },
                b'"' => {
                    // "
                    let str_type = self.detect_str_type(pos);
                    self.state = State::String(str_type);
                    self.pos = pos;
                    return ByteRange::new(start, pos); // include dblquotes
                }
                b'\'' => {
                    // single quotes are also used for lifetimes, so we need to
                    // be confident that this is not a lifetime.
                    // Look for backslash starting the escape, or a closing quote:
                    if src_bytes.len() > pos.increment().0
                        && (src_bytes[pos.0] == b'\\' || src_bytes[pos.increment().0] == b'\'')
                    {
                        self.state = State::Char;
                        self.pos = pos;
                        return ByteRange::new(start, pos); // include single quote
                    }
                }
                _ => {}
            }
        }

        self.state = State::Finished;
        ByteRange::new(start, self.src.len().into())
    }

    fn comment(&mut self) -> ByteRange {
        let mut pos = self.pos;
        let src_bytes = self.src.as_bytes();
        for &b in &src_bytes[pos.0..] {
            pos = pos.increment();
            if b == b'\n' {
                if pos.0 + 2 <= src_bytes.len() && src_bytes[pos.0..pos.0 + 2] == [b'/', b'/'] {
                    continue;
                }
                break;
            }
        }
        self.pos = pos;
        self.code()
    }

    fn comment_block(&mut self) -> ByteRange {
        let mut nesting_level = 0usize;
        let mut prev = b' ';
        let mut pos = self.pos;
        for &b in &self.src.as_bytes()[pos.0..] {
            pos = pos.increment();
            match b {
                b'/' if prev == b'*' => {
                    if nesting_level == 0 {
                        break;
                    } else {
                        nesting_level -= 1;
                    }
                }
                b'*' if prev == b'/' => {
                    nesting_level += 1;
                }
                _ => {
                    prev = b;
                }
            }
        }
        self.pos = pos;
        self.code()
    }

    fn string(&mut self, str_type: StrStyle) -> ByteRange {
        let src_bytes = self.src.as_bytes();
        let mut pos = self.pos;
        match str_type {
            StrStyle::Raw(level) => {
                // raw string (e.g. br#"\"#)
                #[derive(Debug)]
                enum SharpState {
                    Sharp {
                        // number of preceding #s
                        num_sharps: usize,
                        // Position of last "
                        quote_pos: BytePos,
                    },
                    None, // No preceding "##...
                }
                let mut cur_state = SharpState::None;
                let mut end_was_found = false;
                // detect corresponding end(if start is r##", "##) greedily
                for (i, &b) in src_bytes[self.pos.0..].iter().enumerate() {
                    match cur_state {
                        SharpState::Sharp {
                            num_sharps,
                            quote_pos,
                        } => {
                            cur_state = match b {
                                b'#' => SharpState::Sharp {
                                    num_sharps: num_sharps + 1,
                                    quote_pos,
                                },
                                b'"' => SharpState::Sharp {
                                    num_sharps: 0,
                                    quote_pos: BytePos(i),
                                },
                                _ => SharpState::None,
                            }
                        }
                        SharpState::None => {
                            if b == b'"' {
                                cur_state = SharpState::Sharp {
                                    num_sharps: 0,
                                    quote_pos: BytePos(i),
                                };
                            }
                        }
                    }
                    if let SharpState::Sharp {
                        num_sharps,
                        quote_pos,
                    } = cur_state
                    {
                        if num_sharps == level {
                            end_was_found = true;
                            pos += quote_pos.increment();
                            break;
                        }
                    }
                }
                if !end_was_found {
                    pos = src_bytes.len().into();
                }
            }
            StrStyle::Cooked => {
                let mut is_not_escaped = true;
                for &b in &src_bytes[pos.0..] {
                    pos = pos.increment();
                    match b {
                        b'"' if is_not_escaped => {
                            break;
                        } // "
                        b'\\' => {
                            is_not_escaped = !is_not_escaped;
                        }
                        _ => {
                            is_not_escaped = true;
                        }
                    }
                }
            }
        };
        self.pos = pos;
        self.code()
    }

    fn char(&mut self) -> ByteRange {
        let mut is_not_escaped = true;
        let mut pos = self.pos;
        for &b in &self.src.as_bytes()[pos.0..] {
            pos = pos.increment();
            match b {
                b'\'' if is_not_escaped => {
                    break;
                }
                b'\\' => {
                    is_not_escaped = !is_not_escaped;
                }
                _ => {
                    is_not_escaped = true;
                }
            }
        }
        self.pos = pos;
        self.code()
    }

    fn detect_str_type(&self, pos: BytePos) -> StrStyle {
        let src_bytes = self.src.as_bytes();
        let mut sharp = 0;
        if pos == BytePos::ZERO {
            return StrStyle::Cooked;
        }
        // now pos is at one byte after ", so we have to start at pos - 2
        for &b in src_bytes[..pos.decrement().0].iter().rev() {
            match b {
                b'#' => sharp += 1,
                b'r' => return StrStyle::Raw(sharp),
                _ => return StrStyle::Cooked,
            }
        }
        StrStyle::Cooked
    }
}

/// Returns indices of chunks of code (minus comments and string contents)
pub fn code_chunks(src: &str) -> CodeIndicesIter {
    CodeIndicesIter {
        src,
        state: State::Code,
        pos: BytePos::ZERO,
    }
}

#[cfg(test)]
mod code_indices_iter_test {
    use super::*;
    use testutils::{rejustify, slice};

    #[test]
    fn removes_a_comment() {
        let src = &rejustify(
            "
    this is some code // this is a comment
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_consecutive_comments() {
        let src = &rejustify(
            "
    this is some code // this is a comment
    // this is more comment
    // another comment
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_contents() {
        let src = &rejustify(
            "
    this is some code \"this is a string\" more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_char_contents() {
        let src = &rejustify(
            "
    this is some code \'\"\' more code \'\\x00\' and \'\\\'\' that\'s it
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \'", slice(src, it.next().unwrap()));
        assert_eq!("\' more code \'", slice(src, it.next().unwrap()));
        assert_eq!("\' and \'", slice(src, it.next().unwrap()));
        assert_eq!("\' that\'s it", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_contents_with_a_comment_in_it() {
        let src = &rejustify(
            "
    this is some code \"string with a // fake comment \" more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_a_comment_with_a_dbl_quote_in_it() {
        let src = &rejustify(
            "
    this is some code // comment with \" double quote
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_multiline_comment() {
        let src = &rejustify(
            "
    this is some code /* this is a
    \"multiline\" comment */some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_nesting_of_block_comments() {
        let src = &rejustify(
            "
    this is some code /* nested /* block */ comment */ some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!(" some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_with_escaped_dblquote_in_it() {
        let src = &rejustify(
            "
    this is some code \"string with a \\\" escaped dblquote fake comment \" more code
    ",
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_raw_string_with_dangling_escape_in_it() {
        let src = &rejustify(
            "
    this is some code br\" escaped dblquote raw string \\\" more code
    ",
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code br\"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_with_escaped_slash_before_dblquote_in_it() {
        let src = &rejustify("
    this is some code \"string with an escaped slash, so dbl quote does end the string after all \\\\\" more code
    ");

        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_tricky_bit_from_str_rs() {
        let src = &rejustify(
            "
        before(\"\\\\\'\\\\\\\"\\\\\\\\\");
        more_code(\" skip me \")
    ",
        );

        for range in code_chunks(src) {
            let range = || range.to_range();
            println!("BLOB |{}|", &src[range()]);
            if src[range()].contains("skip me") {
                panic!("{}", &src[range()]);
            }
        }
    }

    #[test]
    fn removes_nested_rawstr() {
        let src = &rejustify(
            r####"
    this is some code br###""" r##""##"### more code
    "####,
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code br###\"", slice(src, it.next().unwrap()));
        assert_eq!("\"### more code", slice(src, it.next().unwrap()));
    }

}
<commit_msg>Added test for documentation block comments nested into a block comment.<commit_after>use core::{BytePos, ByteRange};

/// Type of the string
#[derive(Clone, Copy, Debug)]
enum StrStyle {
    /// normal string starts with "
    Cooked,
    /// Raw(n) => raw string started with n #s
    Raw(usize),
}

#[derive(Clone, Copy)]
enum State {
    Code,
    Comment,
    CommentBlock,
    String(StrStyle),
    Char,
    Finished,
}

#[derive(Clone, Copy)]
pub struct CodeIndicesIter<'a> {
    src: &'a str,
    pos: BytePos,
    state: State,
}

impl<'a> Iterator for CodeIndicesIter<'a> {
    type Item = ByteRange;

    fn next(&mut self) -> Option<ByteRange> {
        match self.state {
            State::Code => Some(self.code()),
            State::Comment => Some(self.comment()),
            State::CommentBlock => Some(self.comment_block()),
            State::String(style) => Some(self.string(style)),
            State::Char => Some(self.char()),
            State::Finished => None,
        }
    }
}

impl<'a> CodeIndicesIter<'a> {
    fn code(&mut self) -> ByteRange {
        let mut pos = self.pos;
        let start = match self.state {
            State::String(_) | State::Char => pos.decrement(), // include quote
            _ => pos,
        };
        let src_bytes = self.src.as_bytes();
        for &b in &src_bytes[pos.0..] {
            pos = pos.increment();
            match b {
                b'/' if src_bytes.len() > pos.0 => match src_bytes[pos.0] {
                    b'/' => {
                        self.state = State::Comment;
                        self.pos = pos.increment();
                        return ByteRange::new(start, pos.decrement());
                    }
                    b'*' => {
                        self.state = State::CommentBlock;
                        self.pos = pos.increment();
                        return ByteRange::new(start, pos.decrement());
                    }
                    _ => {}
                },
                b'"' => {
                    // "
                    let str_type = self.detect_str_type(pos);
                    self.state = State::String(str_type);
                    self.pos = pos;
                    return ByteRange::new(start, pos); // include dblquotes
                }
                b'\'' => {
                    // single quotes are also used for lifetimes, so we need to
                    // be confident that this is not a lifetime.
                    // Look for backslash starting the escape, or a closing quote:
                    if src_bytes.len() > pos.increment().0
                        && (src_bytes[pos.0] == b'\\' || src_bytes[pos.increment().0] == b'\'')
                    {
                        self.state = State::Char;
                        self.pos = pos;
                        return ByteRange::new(start, pos); // include single quote
                    }
                }
                _ => {}
            }
        }

        self.state = State::Finished;
        ByteRange::new(start, self.src.len().into())
    }

    fn comment(&mut self) -> ByteRange {
        let mut pos = self.pos;
        let src_bytes = self.src.as_bytes();
        for &b in &src_bytes[pos.0..] {
            pos = pos.increment();
            if b == b'\n' {
                if pos.0 + 2 <= src_bytes.len() && src_bytes[pos.0..pos.0 + 2] == [b'/', b'/'] {
                    continue;
                }
                break;
            }
        }
        self.pos = pos;
        self.code()
    }

    fn comment_block(&mut self) -> ByteRange {
        let mut nesting_level = 0usize;
        let mut prev = b' ';
        let mut pos = self.pos;
        for &b in &self.src.as_bytes()[pos.0..] {
            pos = pos.increment();
            match b {
                b'/' if prev == b'*' => {
                    if nesting_level == 0 {
                        break;
                    } else {
                        nesting_level -= 1;
                    }
                }
                b'*' if prev == b'/' => {
                    nesting_level += 1;
                }
                _ => {
                    prev = b;
                }
            }
        }
        self.pos = pos;
        self.code()
    }

    fn string(&mut self, str_type: StrStyle) -> ByteRange {
        let src_bytes = self.src.as_bytes();
        let mut pos = self.pos;
        match str_type {
            StrStyle::Raw(level) => {
                // raw string (e.g. br#"\"#)
                #[derive(Debug)]
                enum SharpState {
                    Sharp {
                        // number of preceding #s
                        num_sharps: usize,
                        // Position of last "
                        quote_pos: BytePos,
                    },
                    None, // No preceding "##...
                }
                let mut cur_state = SharpState::None;
                let mut end_was_found = false;
                // detect corresponding end(if start is r##", "##) greedily
                for (i, &b) in src_bytes[self.pos.0..].iter().enumerate() {
                    match cur_state {
                        SharpState::Sharp {
                            num_sharps,
                            quote_pos,
                        } => {
                            cur_state = match b {
                                b'#' => SharpState::Sharp {
                                    num_sharps: num_sharps + 1,
                                    quote_pos,
                                },
                                b'"' => SharpState::Sharp {
                                    num_sharps: 0,
                                    quote_pos: BytePos(i),
                                },
                                _ => SharpState::None,
                            }
                        }
                        SharpState::None => {
                            if b == b'"' {
                                cur_state = SharpState::Sharp {
                                    num_sharps: 0,
                                    quote_pos: BytePos(i),
                                };
                            }
                        }
                    }
                    if let SharpState::Sharp {
                        num_sharps,
                        quote_pos,
                    } = cur_state
                    {
                        if num_sharps == level {
                            end_was_found = true;
                            pos += quote_pos.increment();
                            break;
                        }
                    }
                }
                if !end_was_found {
                    pos = src_bytes.len().into();
                }
            }
            StrStyle::Cooked => {
                let mut is_not_escaped = true;
                for &b in &src_bytes[pos.0..] {
                    pos = pos.increment();
                    match b {
                        b'"' if is_not_escaped => {
                            break;
                        } // "
                        b'\\' => {
                            is_not_escaped = !is_not_escaped;
                        }
                        _ => {
                            is_not_escaped = true;
                        }
                    }
                }
            }
        };
        self.pos = pos;
        self.code()
    }

    fn char(&mut self) -> ByteRange {
        let mut is_not_escaped = true;
        let mut pos = self.pos;
        for &b in &self.src.as_bytes()[pos.0..] {
            pos = pos.increment();
            match b {
                b'\'' if is_not_escaped => {
                    break;
                }
                b'\\' => {
                    is_not_escaped = !is_not_escaped;
                }
                _ => {
                    is_not_escaped = true;
                }
            }
        }
        self.pos = pos;
        self.code()
    }

    fn detect_str_type(&self, pos: BytePos) -> StrStyle {
        let src_bytes = self.src.as_bytes();
        let mut sharp = 0;
        if pos == BytePos::ZERO {
            return StrStyle::Cooked;
        }
        // now pos is at one byte after ", so we have to start at pos - 2
        for &b in src_bytes[..pos.decrement().0].iter().rev() {
            match b {
                b'#' => sharp += 1,
                b'r' => return StrStyle::Raw(sharp),
                _ => return StrStyle::Cooked,
            }
        }
        StrStyle::Cooked
    }
}

/// Returns indices of chunks of code (minus comments and string contents)
pub fn code_chunks(src: &str) -> CodeIndicesIter {
    CodeIndicesIter {
        src,
        state: State::Code,
        pos: BytePos::ZERO,
    }
}

#[cfg(test)]
mod code_indices_iter_test {
    use super::*;
    use testutils::{rejustify, slice};

    #[test]
    fn removes_a_comment() {
        let src = &rejustify(
            "
    this is some code // this is a comment
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_consecutive_comments() {
        let src = &rejustify(
            "
    this is some code // this is a comment
    // this is more comment
    // another comment
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_contents() {
        let src = &rejustify(
            "
    this is some code \"this is a string\" more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_char_contents() {
        let src = &rejustify(
            "
    this is some code \'\"\' more code \'\\x00\' and \'\\\'\' that\'s it
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \'", slice(src, it.next().unwrap()));
        assert_eq!("\' more code \'", slice(src, it.next().unwrap()));
        assert_eq!("\' and \'", slice(src, it.next().unwrap()));
        assert_eq!("\' that\'s it", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_contents_with_a_comment_in_it() {
        let src = &rejustify(
            "
    this is some code \"string with a // fake comment \" more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_a_comment_with_a_dbl_quote_in_it() {
        let src = &rejustify(
            "
    this is some code // comment with \" double quote
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_multiline_comment() {
        let src = &rejustify(
            "
    this is some code /* this is a
    \"multiline\" comment */some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_nesting_of_block_comments() {
        let src = &rejustify(
            "
    this is some code /* nested /* block */ comment */ some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!(" some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_documentation_block_comments_nested_into_block_comments() {
        let src = &rejustify(
            "
    this is some code /* nested /** documentation block */ comment */ some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!(" some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_with_escaped_dblquote_in_it() {
        let src = &rejustify(
            "
    this is some code \"string with a \\\" escaped dblquote fake comment \" more code
    ",
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_raw_string_with_dangling_escape_in_it() {
        let src = &rejustify(
            "
    this is some code br\" escaped dblquote raw string \\\" more code
    ",
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code br\"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_with_escaped_slash_before_dblquote_in_it() {
        let src = &rejustify("
    this is some code \"string with an escaped slash, so dbl quote does end the string after all \\\\\" more code
    ");

        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_tricky_bit_from_str_rs() {
        let src = &rejustify(
            "
        before(\"\\\\\'\\\\\\\"\\\\\\\\\");
        more_code(\" skip me \")
    ",
        );

        for range in code_chunks(src) {
            let range = || range.to_range();
            println!("BLOB |{}|", &src[range()]);
            if src[range()].contains("skip me") {
                panic!("{}", &src[range()]);
            }
        }
    }

    #[test]
    fn removes_nested_rawstr() {
        let src = &rejustify(
            r####"
    this is some code br###""" r##""##"### more code
    "####,
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code br###\"", slice(src, it.next().unwrap()));
        assert_eq!("\"### more code", slice(src, it.next().unwrap()));
    }

}
<|endoftext|>


<commit_before>mod age;
mod block;
mod captures;
mod details;
mod ed;
mod gc;
mod na;
mod parse;
mod pool;
mod range;
mod ud;
mod ur;

use std::collections::HashMap;
use std::fs::File;
use std::io::{BufWriter, Write};

use byteorder::{BigEndian, WriteBytesExt};
use failure::Error;

use crate::age::age_handler;
use crate::block::block_handler;
use crate::details::Details;
use crate::ed::ed_handler;
use crate::gc::gc_handler;
use crate::na::na_handler;
use crate::parse::parse;
use crate::pool::{Pool, Popularity};
use crate::ud::ud_handler;
use crate::ur::ur_handler;

fn main() -> Result<(), Error> {
    let mut gc_labels = HashMap::default();

    parse(
        &mut gc_labels,
        gc_handler,
        "PropertyValueAliases.txt",
        r"^gc *; *(?P<key>[^ ]+) *; *(?P<value>([^ ]+))",
    )?;

    let mut ud = points();

    parse(
        &mut ud,
        |sink, captures| ud_handler(&gc_labels, sink, captures),
        "UnicodeData.txt",
        r"^(?P<point>[0-9A-F]+);(?P<name>[^;]+);(?P<gc>[^;]+)",
    )?;

    parse(
        &mut ud,
        block_handler,
        "Blocks.txt",
        r"^(?P<first>[0-9A-F]+)[.][.](?P<last>[0-9A-F]+); (?P<value>.+)",
    )?;

    parse(
        &mut ud,
        age_handler,
        "DerivedAge.txt",
        r"^(?P<first>[0-9A-F]+)(?:[.][.](?P<last>[0-9A-F]+))?\s*;\s*(?P<value>[^ ]+)",
    )?;

    parse(
        &mut ud,
        na_handler,
        "NameAliases.txt",
        r"^(?P<point>[0-9A-F]+);(?P<alias>[^;]+);(?P<type>[^;]+)",
    )?;

    parse(
        &mut ud,
        ur_handler,
        "Unihan_Readings.txt",
        r"^U[+](?P<point>[0-9A-F]+)\t(?P<key>kMandarin|kDefinition)\t(?P<value>.+)",
    )?;

    parse(
        &mut ud,
        ed_handler,
        "emoji-data.txt",
        r"^(?P<first>[0-9A-F]+)(?:[.][.](?P<last>[0-9A-F]+))?\s*;\s*(?P<value>[^ ]+)",
    )?;

    let mut popularity = Popularity::default();

    vote(&mut popularity, &ud, |x| x.name.as_ref());
    vote(&mut popularity, &ud, |x| x.gc.as_ref());
    vote(&mut popularity, &ud, |x| x.block.as_ref());
    vote(&mut popularity, &ud, |x| x.age.as_ref());
    vote(&mut popularity, &ud, |x| x.mpy.as_ref());

    let popularity = popularity.report();

    write("../data.string.json", |mut sink| {
        write!(sink, "{}", serde_json::to_string(&popularity)?)?;

        Ok(())
    })?;

    let mut pool = Pool::default();

    for string in popularity {
        pool.r#use(&string);
    }

    write_pool_indices(&ud, &mut pool, "../data.name.bin", |x| x.name.as_ref())?;
    write_pool_indices(&ud, &mut pool, "../data.gc.bin", |x| x.gc.as_ref())?;
    write_pool_indices(&ud, &mut pool, "../data.block.bin", |x| x.block.as_ref())?;
    write_pool_indices(&ud, &mut pool, "../data.age.bin", |x| x.age.as_ref())?;
    write_pool_indices(&ud, &mut pool, "../data.mpy.bin", |x| x.mpy.as_ref())?;

    Ok(())
}

fn points<T: Default>() -> Vec<T> {
    let mut result = Vec::with_capacity(0x110000);
    result.resize_with(0x110000, Default::default);

    result
}

fn vote<G: FnMut(&Details) -> Option<&String>>(
    sink: &mut Popularity,
    source: &Vec<Details>,
    mut getter: G,
) {
    for details in source {
        if let Some(string) = getter(details) {
            sink.vote(string);
        }
    }
}

fn write<W: FnOnce(BufWriter<File>) -> Result<(), Error>>(
    path: &str,
    writer: W,
) -> Result<(), Error> {
    println!("Writing {} ...", path);

    writer(BufWriter::new(File::create(path)?))
}

fn write_pool_indices<G: FnMut(&Details) -> Option<&String>>(
    source: &Vec<Details>,
    pool: &mut Pool,
    path: &str,
    mut getter: G,
) -> Result<(), Error> {
    write(path, |mut sink| {
        for details in source {
            if let Some(string) = getter(details) {
                let index = pool.r#use(&string);
                assert!(index < 0xFFFF);
                sink.write_u16::<BigEndian>(index)?;
            } else {
                sink.write_u16::<BigEndian>(0xFFFF)?;
            }
        }

        Ok(())
    })
}
<commit_msg>fix pattern for emoji-data.txt<commit_after>mod age;
mod block;
mod captures;
mod details;
mod ed;
mod gc;
mod na;
mod parse;
mod pool;
mod range;
mod ud;
mod ur;

use std::collections::HashMap;
use std::fs::File;
use std::io::{BufWriter, Write};

use byteorder::{BigEndian, WriteBytesExt};
use failure::Error;

use crate::age::age_handler;
use crate::block::block_handler;
use crate::details::Details;
use crate::ed::ed_handler;
use crate::gc::gc_handler;
use crate::na::na_handler;
use crate::parse::parse;
use crate::pool::{Pool, Popularity};
use crate::ud::ud_handler;
use crate::ur::ur_handler;

fn main() -> Result<(), Error> {
    let mut gc_labels = HashMap::default();

    parse(
        &mut gc_labels,
        gc_handler,
        "PropertyValueAliases.txt",
        r"^gc *; *(?P<key>[^ ]+) *; *(?P<value>([^ ]+))",
    )?;

    let mut ud = points();

    parse(
        &mut ud,
        |sink, captures| ud_handler(&gc_labels, sink, captures),
        "UnicodeData.txt",
        r"^(?P<point>[0-9A-F]+);(?P<name>[^;]+);(?P<gc>[^;]+)",
    )?;

    parse(
        &mut ud,
        block_handler,
        "Blocks.txt",
        r"^(?P<first>[0-9A-F]+)[.][.](?P<last>[0-9A-F]+); (?P<value>.+)",
    )?;

    parse(
        &mut ud,
        age_handler,
        "DerivedAge.txt",
        r"^(?P<first>[0-9A-F]+)(?:[.][.](?P<last>[0-9A-F]+))?\s*;\s*(?P<value>[^ ]+)",
    )?;

    parse(
        &mut ud,
        na_handler,
        "NameAliases.txt",
        r"^(?P<point>[0-9A-F]+);(?P<alias>[^;]+);(?P<type>[^;]+)",
    )?;

    parse(
        &mut ud,
        ur_handler,
        "Unihan_Readings.txt",
        r"^U[+](?P<point>[0-9A-F]+)\t(?P<key>kMandarin|kDefinition)\t(?P<value>.+)",
    )?;

    parse(
        &mut ud,
        ed_handler,
        "emoji-data.txt",
        r"^(?P<first>[0-9A-F]+)(?:[.][.](?P<last>[0-9A-F]+))?\s*;\s*Emoji_Presentation(\s|#|$)",
    )?;

    let mut popularity = Popularity::default();

    vote(&mut popularity, &ud, |x| x.name.as_ref());
    vote(&mut popularity, &ud, |x| x.gc.as_ref());
    vote(&mut popularity, &ud, |x| x.block.as_ref());
    vote(&mut popularity, &ud, |x| x.age.as_ref());
    vote(&mut popularity, &ud, |x| x.mpy.as_ref());

    let popularity = popularity.report();

    write("../data.string.json", |mut sink| {
        write!(sink, "{}", serde_json::to_string(&popularity)?)?;

        Ok(())
    })?;

    let mut pool = Pool::default();

    for string in popularity {
        pool.r#use(&string);
    }

    write_pool_indices(&ud, &mut pool, "../data.name.bin", |x| x.name.as_ref())?;
    write_pool_indices(&ud, &mut pool, "../data.gc.bin", |x| x.gc.as_ref())?;
    write_pool_indices(&ud, &mut pool, "../data.block.bin", |x| x.block.as_ref())?;
    write_pool_indices(&ud, &mut pool, "../data.age.bin", |x| x.age.as_ref())?;
    write_pool_indices(&ud, &mut pool, "../data.mpy.bin", |x| x.mpy.as_ref())?;

    Ok(())
}

fn points<T: Default>() -> Vec<T> {
    let mut result = Vec::with_capacity(0x110000);
    result.resize_with(0x110000, Default::default);

    result
}

fn vote<G: FnMut(&Details) -> Option<&String>>(
    sink: &mut Popularity,
    source: &Vec<Details>,
    mut getter: G,
) {
    for details in source {
        if let Some(string) = getter(details) {
            sink.vote(string);
        }
    }
}

fn write<W: FnOnce(BufWriter<File>) -> Result<(), Error>>(
    path: &str,
    writer: W,
) -> Result<(), Error> {
    println!("Writing {} ...", path);

    writer(BufWriter::new(File::create(path)?))
}

fn write_pool_indices<G: FnMut(&Details) -> Option<&String>>(
    source: &Vec<Details>,
    pool: &mut Pool,
    path: &str,
    mut getter: G,
) -> Result<(), Error> {
    write(path, |mut sink| {
        for details in source {
            if let Some(string) = getter(details) {
                let index = pool.r#use(&string);
                assert!(index < 0xFFFF);
                sink.write_u16::<BigEndian>(index)?;
            } else {
                sink.write_u16::<BigEndian>(0xFFFF)?;
            }
        }

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


<commit_before>extern crate libc;

use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;
use std::ptr;
use std::mem;

use drawing_ctx::RsvgDrawingCtx;

use handle::RsvgHandle;

use property_bag::RsvgPropertyBag;

use state::RsvgState;

/* A *const RsvgNode is just a pointer for the C code's benefit: it
 * points to an  Rc<Node>, which is our refcounted Rust representation
 * of nodes.
 */
pub type RsvgNode = Rc<Node>;

/* A *const RsvgCNodeImpl is just an opaque pointer to the C code's
 * struct for a particular node type.
 */
pub enum RsvgCNodeImpl {}

pub trait NodeTrait {
    fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag);
    fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32);
    fn get_c_impl (&self) -> *const RsvgCNodeImpl;
}

pub struct Node {
    node_type: NodeType,
    parent:    Option<Weak<Node>>,       // optional; weak ref to parent
    children:  RefCell<Vec<Rc<Node>>>,   // strong references to children
    state:     *mut RsvgState,
    node_impl: Box<NodeTrait>
}

/* Keep this in sync with rsvg-private.h:RsvgNodeType */
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum NodeType {
    Invalid = 0,

    Chars,
    Circle,
    ClipPath,
    ComponentTransferFunction,
    Defs,
    Ellipse,
    Filter,
    Group,
    Image,
    LightSource,
    Line,
    LinearGradient,
    Marker,
    Mask,
    Path,
    Pattern,
    Polygon,
    Polyline,
    RadialGradient,
    Rect,
    Stop,
    Svg,
    Switch,
    Symbol,
    Text,
    TRef,
    TSpan,
    Use,

    /* Filter primitives */
    FilterPrimitiveFirst,              /* just a marker; not a valid type */
    FilterPrimitiveBlend,
    FilterPrimitiveColorMatrix,
    FilterPrimitiveComponentTransfer,
    FilterPrimitiveComposite,
    FilterPrimitiveConvolveMatrix,
    FilterPrimitiveDiffuseLighting,
    FilterPrimitiveDisplacementMap,
    FilterPrimitiveErode,
    FilterPrimitiveFlood,
    FilterPrimitiveGaussianBlur,
    FilterPrimitiveImage,
    FilterPrimitiveMerge,
    FilterPrimitiveMergeNode,
    FilterPrimitiveOffset,
    FilterPrimitiveSpecularLighting,
    FilterPrimitiveTile,
    FilterPrimitiveTurbulence,
    FilterPrimitiveLast                /* just a marker; not a valid type */
}

impl Node {
    pub fn new (node_type: NodeType,
                parent:    Option<Weak<Node>>,
                state:     *mut RsvgState,
                node_impl: Box<NodeTrait>) -> Node {
        Node {
            node_type: node_type,
            parent:    parent,
            children:  RefCell::new (Vec::new ()),
            state:     state,
            node_impl: node_impl
        }
    }

    pub fn get_type (&self) -> NodeType {
        self.node_type
    }

    pub fn get_state (&self) -> *mut RsvgState {
        self.state
    }

    pub fn add_child (&self, child: &Rc<Node>) {
        self.children.borrow_mut ().push (child.clone ());
    }

    pub fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) {
        self.node_impl.set_atts (node, handle, pbag);
    }

    pub fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        self.node_impl.draw (node, draw_ctx, dominate);
    }

    pub fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        self.node_impl.get_c_impl ()
    }
}

extern "C" {
    fn rsvg_state_free (state: *mut RsvgState);
}

impl Drop for Node {
    fn drop (&mut self) {
//        unsafe { rsvg_state_free (self.state); }
    }
}

#[no_mangle]
pub extern fn rsvg_node_get_type (raw_node: *const RsvgNode) -> NodeType {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.get_type ()
}

pub fn box_node (node: RsvgNode) -> *mut RsvgNode {
    Box::into_raw (Box::new (node))
}

#[no_mangle]
pub extern fn rsvg_node_get_parent (raw_node: *const RsvgNode) -> *const RsvgNode {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    match node.parent {
        None => { ptr::null () }

        Some (ref weak_node) => {
            let strong_node = weak_node.upgrade ().unwrap ();
            box_node (strong_node)
        }
    }
}

#[no_mangle]
pub extern fn rsvg_node_ref (raw_node: *mut RsvgNode) -> *mut RsvgNode {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    box_node (node.clone ())
}

#[no_mangle]
pub extern fn rsvg_node_unref (raw_node: *mut RsvgNode) -> *mut RsvgNode {
    if !raw_node.is_null () {
        let _ = unsafe { Box::from_raw (raw_node) };
    }

    ptr::null_mut () // so the caller can do "node = rsvg_node_unref (node);" and lose access to the node
}

// See https://github.com/rust-lang/rust/issues/36497 - this is what
// added Rc::ptr_eq(), but we don't want to depend on unstable Rust
// just yet.

fn rc_node_ptr_eq<T: ?Sized> (this: &Rc<T>, other: &Rc<T>) -> bool {
    let this_ptr: *const T = &**this;
    let other_ptr: *const T = &**other;
    this_ptr == other_ptr
}

#[no_mangle]
pub extern fn rsvg_node_is_same (raw_node1: *const RsvgNode, raw_node2: *const RsvgNode) -> bool {
    if raw_node1.is_null () && raw_node2.is_null () {
        true
    } else if !raw_node1.is_null () && !raw_node2.is_null () {
        let node1: &RsvgNode = unsafe { & *raw_node1 };
        let node2: &RsvgNode = unsafe { & *raw_node2 };

        rc_node_ptr_eq (node1, node2)
    } else {
        false
    }
}

#[no_mangle]
pub extern fn rsvg_node_get_state (raw_node: *const RsvgNode) -> *mut RsvgState {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.get_state ()
}

#[no_mangle]
pub extern fn rsvg_node_add_child (raw_node: *mut RsvgNode, raw_child: *const RsvgNode) {
    assert! (!raw_node.is_null ());
    assert! (!raw_child.is_null ());
    let node: &mut RsvgNode = unsafe { &mut *raw_node };
    let child: &RsvgNode = unsafe { & *raw_child };

    node.add_child (child);
}

#[no_mangle]
pub extern fn rsvg_node_set_atts (raw_node: *mut RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.set_atts (node, handle, pbag);
}

#[no_mangle]
pub extern fn rsvg_node_draw (raw_node: *const RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.draw (node, draw_ctx, dominate);
}

type NodeForeachChild = unsafe extern "C" fn (node: *const RsvgNode, data: *const libc::c_void) -> bool;

#[no_mangle]
pub extern fn rsvg_node_foreach_child (raw_node: *const RsvgNode, func: NodeForeachChild, data: *const libc::c_void)
{
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    for child in node.children.borrow ().iter () {
        let next = unsafe { func (child as *const RsvgNode, data) };
        if !next {
            break;
        }
    }
}

#[cfg(test)]
mod tests {
    use std::rc::Rc;
    use drawing_ctx::RsvgDrawingCtx;
    use handle::RsvgHandle;
    use property_bag::RsvgPropertyBag;
    use state::RsvgState;
    use super::*;
    use std::ptr;
    use std::mem;

    struct TestNodeImpl {}

    impl NodeTrait for TestNodeImpl {
        fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) {
        }

        fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        }

        fn get_c_impl (&self) -> *const RsvgCNodeImpl {
            return ptr::null ();
        }
    }

    #[test]
    fn node_refs_and_unrefs () {
        let node = Rc::new (Node::new (NodeType::Path,
                                       None,
                                       ptr::null_mut (),
                                       Box::new (TestNodeImpl {})));

        let mut ref1 = box_node (node);

        let new_node: &mut RsvgNode = unsafe { &mut *ref1 };
        let weak = Rc::downgrade (new_node);

        let mut ref2 = unsafe { rsvg_node_ref (new_node) };
        assert! (weak.upgrade ().is_some ());

        ref2 = unsafe { rsvg_node_unref (ref2) };
        assert! (weak.upgrade ().is_some ());

        ref1 = unsafe { rsvg_node_unref (ref1) };
        assert! (weak.upgrade ().is_none ());
    }

    #[test]
    fn reffed_node_is_same_as_original_node () {
        let node = Rc::new (Node::new (NodeType::Path,
                                       None,
                                       ptr::null_mut (),
                                       Box::new (TestNodeImpl {})));

        let mut ref1 = box_node (node);

        let mut ref2 = unsafe { rsvg_node_ref (ref1) };

        unsafe { assert! (rsvg_node_is_same (ref1, ref2)); }

        ref1 = rsvg_node_unref (ref1);
        ref2 = rsvg_node_unref (ref2);
    }

    #[test]
    fn different_nodes_have_different_pointers () {
        let node1 = Rc::new (Node::new (NodeType::Path,
                                       None,
                                       ptr::null_mut (),
                                       Box::new (TestNodeImpl {})));

        let mut ref1 = box_node (node1);

        let node2 = Rc::new (Node::new (NodeType::Path,
                                       None,
                                       ptr::null_mut (),
                                       Box::new (TestNodeImpl {})));

        let mut ref2 = box_node (node2);

        unsafe { assert! (!rsvg_node_is_same (ref1, ref2)); }

        ref1 = rsvg_node_unref (ref1);
        ref2 = rsvg_node_unref (ref2);
    }
}
<commit_msg>fix: implement rsvg_node_add_child()<commit_after>extern crate libc;

use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;
use std::ptr;
use std::mem;

use drawing_ctx::RsvgDrawingCtx;

use handle::RsvgHandle;

use property_bag::RsvgPropertyBag;

use state::RsvgState;

/* A *const RsvgNode is just a pointer for the C code's benefit: it
 * points to an  Rc<Node>, which is our refcounted Rust representation
 * of nodes.
 */
pub type RsvgNode = Rc<Node>;

/* A *const RsvgCNodeImpl is just an opaque pointer to the C code's
 * struct for a particular node type.
 */
pub enum RsvgCNodeImpl {}

pub trait NodeTrait {
    fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag);
    fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32);
    fn get_c_impl (&self) -> *const RsvgCNodeImpl;
}

pub struct Node {
    node_type: NodeType,
    parent:    Option<Weak<Node>>,       // optional; weak ref to parent
    children:  RefCell<Vec<Rc<Node>>>,   // strong references to children
    state:     *mut RsvgState,
    node_impl: Box<NodeTrait>
}

/* Keep this in sync with rsvg-private.h:RsvgNodeType */
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum NodeType {
    Invalid = 0,

    Chars,
    Circle,
    ClipPath,
    ComponentTransferFunction,
    Defs,
    Ellipse,
    Filter,
    Group,
    Image,
    LightSource,
    Line,
    LinearGradient,
    Marker,
    Mask,
    Path,
    Pattern,
    Polygon,
    Polyline,
    RadialGradient,
    Rect,
    Stop,
    Svg,
    Switch,
    Symbol,
    Text,
    TRef,
    TSpan,
    Use,

    /* Filter primitives */
    FilterPrimitiveFirst,              /* just a marker; not a valid type */
    FilterPrimitiveBlend,
    FilterPrimitiveColorMatrix,
    FilterPrimitiveComponentTransfer,
    FilterPrimitiveComposite,
    FilterPrimitiveConvolveMatrix,
    FilterPrimitiveDiffuseLighting,
    FilterPrimitiveDisplacementMap,
    FilterPrimitiveErode,
    FilterPrimitiveFlood,
    FilterPrimitiveGaussianBlur,
    FilterPrimitiveImage,
    FilterPrimitiveMerge,
    FilterPrimitiveMergeNode,
    FilterPrimitiveOffset,
    FilterPrimitiveSpecularLighting,
    FilterPrimitiveTile,
    FilterPrimitiveTurbulence,
    FilterPrimitiveLast                /* just a marker; not a valid type */
}

impl Node {
    pub fn new (node_type: NodeType,
                parent:    Option<Weak<Node>>,
                state:     *mut RsvgState,
                node_impl: Box<NodeTrait>) -> Node {
        Node {
            node_type: node_type,
            parent:    parent,
            children:  RefCell::new (Vec::new ()),
            state:     state,
            node_impl: node_impl
        }
    }

    pub fn get_type (&self) -> NodeType {
        self.node_type
    }

    pub fn get_state (&self) -> *mut RsvgState {
        self.state
    }

    pub fn add_child (&self, child: &Rc<Node>) {
        self.children.borrow_mut ().push (child.clone ());
    }

    pub fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) {
        self.node_impl.set_atts (node, handle, pbag);
    }

    pub fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        self.node_impl.draw (node, draw_ctx, dominate);
    }

    pub fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        self.node_impl.get_c_impl ()
    }
}

extern "C" {
    fn rsvg_state_free (state: *mut RsvgState);
}

impl Drop for Node {
    fn drop (&mut self) {
//        unsafe { rsvg_state_free (self.state); }
    }
}

#[no_mangle]
pub extern fn rsvg_node_get_type (raw_node: *const RsvgNode) -> NodeType {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.get_type ()
}

pub fn box_node (node: RsvgNode) -> *mut RsvgNode {
    Box::into_raw (Box::new (node))
}

#[no_mangle]
pub extern fn rsvg_node_get_parent (raw_node: *const RsvgNode) -> *const RsvgNode {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    match node.parent {
        None => { ptr::null () }

        Some (ref weak_node) => {
            let strong_node = weak_node.upgrade ().unwrap ();
            box_node (strong_node)
        }
    }
}

#[no_mangle]
pub extern fn rsvg_node_ref (raw_node: *mut RsvgNode) -> *mut RsvgNode {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    box_node (node.clone ())
}

#[no_mangle]
pub extern fn rsvg_node_unref (raw_node: *mut RsvgNode) -> *mut RsvgNode {
    if !raw_node.is_null () {
        let _ = unsafe { Box::from_raw (raw_node) };
    }

    ptr::null_mut () // so the caller can do "node = rsvg_node_unref (node);" and lose access to the node
}

// See https://github.com/rust-lang/rust/issues/36497 - this is what
// added Rc::ptr_eq(), but we don't want to depend on unstable Rust
// just yet.

fn rc_node_ptr_eq<T: ?Sized> (this: &Rc<T>, other: &Rc<T>) -> bool {
    let this_ptr: *const T = &**this;
    let other_ptr: *const T = &**other;
    this_ptr == other_ptr
}

#[no_mangle]
pub extern fn rsvg_node_is_same (raw_node1: *const RsvgNode, raw_node2: *const RsvgNode) -> bool {
    if raw_node1.is_null () && raw_node2.is_null () {
        true
    } else if !raw_node1.is_null () && !raw_node2.is_null () {
        let node1: &RsvgNode = unsafe { & *raw_node1 };
        let node2: &RsvgNode = unsafe { & *raw_node2 };

        rc_node_ptr_eq (node1, node2)
    } else {
        false
    }
}

#[no_mangle]
pub extern fn rsvg_node_get_state (raw_node: *const RsvgNode) -> *mut RsvgState {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.get_state ()
}

#[no_mangle]
pub extern fn rsvg_node_add_child (raw_node: *mut RsvgNode, raw_child: *const RsvgNode) {
    assert! (!raw_node.is_null ());
    assert! (!raw_child.is_null ());
    let node: &mut RsvgNode = unsafe { &mut *raw_node };
    let child: &RsvgNode = unsafe { & *raw_child };

    node.add_child (child);
}

#[no_mangle]
pub extern fn rsvg_node_set_atts (raw_node: *mut RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.set_atts (node, handle, pbag);
}

#[no_mangle]
pub extern fn rsvg_node_draw (raw_node: *const RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.draw (node, draw_ctx, dominate);
}

type NodeForeachChild = unsafe extern "C" fn (node: *const RsvgNode, data: *const libc::c_void) -> bool;

#[no_mangle]
pub extern fn rsvg_node_foreach_child (raw_node: *const RsvgNode, func: NodeForeachChild, data: *const libc::c_void)
{
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    for child in &*node.children.borrow () {
        let boxed_child = box_node (child.clone ());

        let next = unsafe { func (boxed_child, data) };

        unsafe { rsvg_node_unref (boxed_child); }

        if !next {
            break;
        }
    }
}

#[cfg(test)]
mod tests {
    use std::rc::Rc;
    use drawing_ctx::RsvgDrawingCtx;
    use handle::RsvgHandle;
    use property_bag::RsvgPropertyBag;
    use state::RsvgState;
    use super::*;
    use std::ptr;
    use std::mem;

    struct TestNodeImpl {}

    impl NodeTrait for TestNodeImpl {
        fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) {
        }

        fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        }

        fn get_c_impl (&self) -> *const RsvgCNodeImpl {
            return ptr::null ();
        }
    }

    #[test]
    fn node_refs_and_unrefs () {
        let node = Rc::new (Node::new (NodeType::Path,
                                       None,
                                       ptr::null_mut (),
                                       Box::new (TestNodeImpl {})));

        let mut ref1 = box_node (node);

        let new_node: &mut RsvgNode = unsafe { &mut *ref1 };
        let weak = Rc::downgrade (new_node);

        let mut ref2 = unsafe { rsvg_node_ref (new_node) };
        assert! (weak.upgrade ().is_some ());

        ref2 = unsafe { rsvg_node_unref (ref2) };
        assert! (weak.upgrade ().is_some ());

        ref1 = unsafe { rsvg_node_unref (ref1) };
        assert! (weak.upgrade ().is_none ());
    }

    #[test]
    fn reffed_node_is_same_as_original_node () {
        let node = Rc::new (Node::new (NodeType::Path,
                                       None,
                                       ptr::null_mut (),
                                       Box::new (TestNodeImpl {})));

        let mut ref1 = box_node (node);

        let mut ref2 = unsafe { rsvg_node_ref (ref1) };

        unsafe { assert! (rsvg_node_is_same (ref1, ref2)); }

        ref1 = rsvg_node_unref (ref1);
        ref2 = rsvg_node_unref (ref2);
    }

    #[test]
    fn different_nodes_have_different_pointers () {
        let node1 = Rc::new (Node::new (NodeType::Path,
                                       None,
                                       ptr::null_mut (),
                                       Box::new (TestNodeImpl {})));

        let mut ref1 = box_node (node1);

        let node2 = Rc::new (Node::new (NodeType::Path,
                                       None,
                                       ptr::null_mut (),
                                       Box::new (TestNodeImpl {})));

        let mut ref2 = box_node (node2);

        unsafe { assert! (!rsvg_node_is_same (ref1, ref2)); }

        ref1 = rsvg_node_unref (ref1);
        ref2 = rsvg_node_unref (ref2);
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::mem;
use std::ptr;
use util::locksteparray;
use util::SliceExt;
use super::jpm::jpm_root::Jpm;
use ::Key;
use ::rudymap::results::{InsertResult, RemoveResult};
use std::iter;
use super::rootptr::RootPtr;

pub trait RootLeaf<K: Key, V> {
    fn get(&self, key: K) -> Option<&V>;
    fn get_mut(&mut self, key: K) -> Option<&mut V>;
    fn insert(&mut self, key: K, value: V) -> InsertResult<V>;
    fn expand(self, key: K, value: V) -> RootPtr<K, V>;
    fn remove(&mut self, key: K) -> RemoveResult<V>;
    fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V);
    fn len(&self) -> usize;
    fn memory_usage(&self) -> usize;
}

pub struct Empty<K: Key, V>(PhantomData<(K, V)>);

impl<K: Key, V> Empty<K, V> {
    pub fn new() -> Empty<K, V> {
        Empty(PhantomData)
    }
}

impl<K: Key, V> RootLeaf<K, V> for Empty<K, V> {
    fn get(&self, key: K) -> Option<&V> {
        None
    }

    fn get_mut(&mut self, key: K) -> Option<&mut V> {
        None
    }

    fn insert(&mut self, key: K, value: V) -> InsertResult<V> {
        InsertResult::Resize(value)
    }

    fn remove(&mut self, key: K) -> RemoveResult<V> {
        RemoveResult::Success(None)
    }

    fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V){
        unreachable!();
    }

    fn expand(self, key: K, value: V) -> RootPtr<K, V> {
        Box::new(Leaf1::new(key, value)).into()
    }

    fn len(&self) -> usize {
        0
    }

    fn memory_usage(&self) -> usize { 0 }
}

impl<K: Key, V> Default for Empty<K, V> {
    fn default() -> Empty<K, V> {
        Empty::new()
    }
}

impl<'a, K: Key + 'a, V: 'a> IntoIterator for &'a Empty<K, V> {
    type Item = (K, &'a V);
    type IntoIter = iter::Empty<Self::Item>;
    fn into_iter(self) -> Self::IntoIter {
        iter::empty()
    }
}

pub struct Leaf1<K: Key, V> {
    key: K,
    value: V
}

impl<K: Key, V> Leaf1<K, V> {
    pub fn new(key: K, value: V) -> Leaf1<K, V> {
        Leaf1 { key, value }
    }
}

impl<'a, K: Key + 'a, V: 'a> IntoIterator for &'a Leaf1<K, V> {
    type Item = (K, &'a V);
    type IntoIter = iter::Once<Self::Item>;
    fn into_iter(self) -> Self::IntoIter {
        iter::once((self.key, &self.value))
    }
}

/// A leaf root with one item.
impl<K: Key, V> RootLeaf<K, V> for Leaf1<K, V> {
    fn get(&self, key: K) -> Option<&V> {
        if self.key == key {
            Some(&self.value)
        } else {
            None
        }
    }

    fn get_mut(&mut self, key: K) -> Option<&mut V> {
        if self.key == key {
            Some(&mut self.value)
        } else {
            None
        }
    }

    fn insert(&mut self, key: K, value: V) -> InsertResult<V> {
        if self.key == key {
            InsertResult::replace(&mut self.value, value)
        } else {
            InsertResult::Resize(value)
        }
    }

    fn expand(self, key: K, value: V) -> RootPtr<K, V> {
        Box::new(Leaf2::new(self.key, self.value, key, value)).into()
    }

    fn remove(&mut self, key: K) -> RemoveResult<V> {
        if self.key == key {
            RemoveResult::Downsize
        } else {
            RemoveResult::Success(None)
        }
    }

    fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V) {
        let Leaf1 { key: node_key, value } = self;
        let ptr = RootPtr::empty();
        debug_assert_eq!(node_key, key);
        (ptr, value)
    }

    fn len(&self) -> usize {
        1
    }

    fn memory_usage(&self) -> usize {
        mem::size_of_val(self)
    }
}

pub struct Leaf2<K: Key, V> {
    keys: [K; 2],
    values: [V; 2]
}

impl<K: Key, V> Leaf2<K, V> {
    pub fn new(key1: K, value1: V, key2: K, value2: V) -> Leaf2<K, V> {
        if key1 < key2 {
            Leaf2 {
                keys: [key1, key2],
                values: [value1, value2]
            }
        } else {
            Leaf2 {
                keys: [key2, key1],
                values: [value2, value1]
            }
        }
    }
}

impl<K: Key, V> RootLeaf<K, V> for Leaf2<K, V> {
    fn get(&self, key: K) -> Option<&V> {
        self.keys.iter()
            .zip(self.values.iter())
            .find(|&(&leaf_key, _)| leaf_key == key)
            .map(|(key, value)| value)
    }

    fn get_mut(&mut self, key: K) -> Option<&mut V> {
        self.keys.iter()
            .zip(self.values.iter_mut())
            .find(|&(&leaf_key, _)| leaf_key == key)
            .map(|(key, value)| value)
    }

    /// Attempt to insert, fail if we didn't find a key to replace
    fn insert(&mut self, key: K, value: V) -> InsertResult<V> {
        for (i, leaf_key) in self.keys.iter().enumerate() {
            if key == *leaf_key {
                return InsertResult::replace(&mut self.values[i], value);
            }
        }
        InsertResult::Resize(value)
    }

    fn expand(self, key: K, value: V) -> RootPtr<K, V> {
        let Leaf2 { keys, values } = self;
        let mut leaf = Box::new(VecLeaf::from_arrays(keys, values));
        leaf.insert(key, value).success();
        leaf.into()
    }

    fn remove(&mut self, key: K) -> RemoveResult<V> {
        self.keys.iter()
            .find(|&&k| k == key)
            .map(|_| RemoveResult::Downsize)
            .unwrap_or(RemoveResult::Success(None))
    }

    fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V) {
        let Leaf2 { keys, mut values } = self;
        let key1 = keys[0];
        let key2 = keys[1];
        let (value1, value2);
        unsafe {
            value1 = ptr::read(&mut values[0]);
            value2 = ptr::read(&mut values[1]);
            mem::forget(values);
        }
        if key1 == key {
            let ptr = Box::new(Leaf1::new(key2, value2)).into();
            (ptr, value1)
        } else {
            let ptr = Box::new(Leaf1::new(key1, value1)).into();
            (ptr, value2)
        }
    }

    fn len(&self) -> usize {
        2
    }
    fn memory_usage(&self) -> usize {
        mem::size_of_val(self)
    }
}

pub struct VecLeaf<K: Key, V> {
    array: locksteparray::LockstepArray<[K; 31], [V; 31]>
}

impl<K: Key, V> VecLeaf<K, V> {
    fn new() -> VecLeaf<K, V> {
        // TODO Copy memory from values
        VecLeaf {
            array: locksteparray::LockstepArray::new()
        }
    }

    fn from_arrays(keys: [K; 2], values: [V; 2]) -> VecLeaf<K, V> {
        VecLeaf {
            array: locksteparray::LockstepArray::from_arrays(keys, values)
        }
    }
}

impl<K: Key, V> IntoIterator for VecLeaf<K, V> {
    type Item = (K, V);
    type IntoIter = locksteparray::IntoIter<[K; 31], [V; 31]>;

    fn into_iter(self) -> Self::IntoIter {
        self.array.into_iter()
    }
}

impl<K: Key, V> RootLeaf<K, V> for VecLeaf<K, V> {
    fn get(&self, key: K) -> Option<&V> {
        self.array.array1()
            .iter()
            .position(|&k| k == key)
            .map(|index| &self.array.array2()[index])
    }

    fn get_mut(&mut self, key: K) -> Option<&mut V> {
        self.array.array1_mut()
            .iter()
            .position(|&k| k == key)
            .map(move |index| &mut self.array.array2_mut()[index])
    }

    fn insert(&mut self, key: K, value: V) -> InsertResult<V> {
        match self.array.array1().linear_search(&key) {
            Ok(replace) => {
                InsertResult::replace(&mut self.array.array2_mut()[replace],
                                      value)
            },
            Err(insert) => match self.array.insert(insert, key, value) {
                Ok(()) => InsertResult::Success(None),
                Err(locksteparray::InsertError::Overflow(key, value)) => {
                    InsertResult::Resize(value)
                },
                Err(locksteparray::InsertError::OutOfBounds(..)) => {
                    unreachable!()
                }
            }
        }
    }

    fn expand(self, key: K, value: V) -> RootPtr<K, V> {
        let mut jpm: Jpm<K, V> = self.into_iter().collect();
        jpm.insert(key, value).success();
        Box::new(jpm).into()
    }

    fn remove(&mut self, key: K) -> RemoveResult<V> {
        let evicted = self.array.array1_mut()
            .iter()
            .position(|&k| k == key)
            .and_then(|index| self.array.remove(index))
            .map(|(_, value)| value);
        RemoveResult::Success(evicted)
    }

    fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V) {
        unreachable!()
    }

    fn len(&self) -> usize {
        self.array.len()
    }
    fn memory_usage(&self) -> usize {
        mem::size_of_val(self)
    }
}
<commit_msg>Consolidate root leaf memory_usage() into a default implementation<commit_after>use std::marker::PhantomData;
use std::mem;
use std::ptr;
use util::locksteparray;
use util::SliceExt;
use super::jpm::jpm_root::Jpm;
use ::Key;
use ::rudymap::results::{InsertResult, RemoveResult};
use std::iter;
use super::rootptr::RootPtr;

pub trait RootLeaf<K: Key, V> {
    fn get(&self, key: K) -> Option<&V>;
    fn get_mut(&mut self, key: K) -> Option<&mut V>;
    fn insert(&mut self, key: K, value: V) -> InsertResult<V>;
    fn expand(self, key: K, value: V) -> RootPtr<K, V>;
    fn remove(&mut self, key: K) -> RemoveResult<V>;
    fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V);
    fn len(&self) -> usize;

    fn memory_usage(&self) -> usize {
        mem::size_of_val(self)
    }
}

pub struct Empty<K: Key, V>(PhantomData<(K, V)>);

impl<K: Key, V> Empty<K, V> {
    pub fn new() -> Empty<K, V> {
        Empty(PhantomData)
    }
}

impl<K: Key, V> RootLeaf<K, V> for Empty<K, V> {
    fn get(&self, key: K) -> Option<&V> {
        None
    }

    fn get_mut(&mut self, key: K) -> Option<&mut V> {
        None
    }

    fn insert(&mut self, key: K, value: V) -> InsertResult<V> {
        InsertResult::Resize(value)
    }

    fn remove(&mut self, key: K) -> RemoveResult<V> {
        RemoveResult::Success(None)
    }

    fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V){
        unreachable!();
    }

    fn expand(self, key: K, value: V) -> RootPtr<K, V> {
        Box::new(Leaf1::new(key, value)).into()
    }

    fn len(&self) -> usize {
        0
    }
}

impl<K: Key, V> Default for Empty<K, V> {
    fn default() -> Empty<K, V> {
        Empty::new()
    }
}

impl<'a, K: Key + 'a, V: 'a> IntoIterator for &'a Empty<K, V> {
    type Item = (K, &'a V);
    type IntoIter = iter::Empty<Self::Item>;
    fn into_iter(self) -> Self::IntoIter {
        iter::empty()
    }
}

pub struct Leaf1<K: Key, V> {
    key: K,
    value: V
}

impl<K: Key, V> Leaf1<K, V> {
    pub fn new(key: K, value: V) -> Leaf1<K, V> {
        Leaf1 { key, value }
    }
}

impl<'a, K: Key + 'a, V: 'a> IntoIterator for &'a Leaf1<K, V> {
    type Item = (K, &'a V);
    type IntoIter = iter::Once<Self::Item>;
    fn into_iter(self) -> Self::IntoIter {
        iter::once((self.key, &self.value))
    }
}

/// A leaf root with one item.
impl<K: Key, V> RootLeaf<K, V> for Leaf1<K, V> {
    fn get(&self, key: K) -> Option<&V> {
        if self.key == key {
            Some(&self.value)
        } else {
            None
        }
    }

    fn get_mut(&mut self, key: K) -> Option<&mut V> {
        if self.key == key {
            Some(&mut self.value)
        } else {
            None
        }
    }

    fn insert(&mut self, key: K, value: V) -> InsertResult<V> {
        if self.key == key {
            InsertResult::replace(&mut self.value, value)
        } else {
            InsertResult::Resize(value)
        }
    }

    fn expand(self, key: K, value: V) -> RootPtr<K, V> {
        Box::new(Leaf2::new(self.key, self.value, key, value)).into()
    }

    fn remove(&mut self, key: K) -> RemoveResult<V> {
        if self.key == key {
            RemoveResult::Downsize
        } else {
            RemoveResult::Success(None)
        }
    }

    fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V) {
        let Leaf1 { key: node_key, value } = self;
        let ptr = RootPtr::empty();
        debug_assert_eq!(node_key, key);
        (ptr, value)
    }

    fn len(&self) -> usize {
        1
    }
}

pub struct Leaf2<K: Key, V> {
    keys: [K; 2],
    values: [V; 2]
}

impl<K: Key, V> Leaf2<K, V> {
    pub fn new(key1: K, value1: V, key2: K, value2: V) -> Leaf2<K, V> {
        if key1 < key2 {
            Leaf2 {
                keys: [key1, key2],
                values: [value1, value2]
            }
        } else {
            Leaf2 {
                keys: [key2, key1],
                values: [value2, value1]
            }
        }
    }
}

impl<K: Key, V> RootLeaf<K, V> for Leaf2<K, V> {
    fn get(&self, key: K) -> Option<&V> {
        self.keys.iter()
            .zip(self.values.iter())
            .find(|&(&leaf_key, _)| leaf_key == key)
            .map(|(key, value)| value)
    }

    fn get_mut(&mut self, key: K) -> Option<&mut V> {
        self.keys.iter()
            .zip(self.values.iter_mut())
            .find(|&(&leaf_key, _)| leaf_key == key)
            .map(|(key, value)| value)
    }

    /// Attempt to insert, fail if we didn't find a key to replace
    fn insert(&mut self, key: K, value: V) -> InsertResult<V> {
        for (i, leaf_key) in self.keys.iter().enumerate() {
            if key == *leaf_key {
                return InsertResult::replace(&mut self.values[i], value);
            }
        }
        InsertResult::Resize(value)
    }

    fn expand(self, key: K, value: V) -> RootPtr<K, V> {
        let Leaf2 { keys, values } = self;
        let mut leaf = Box::new(VecLeaf::from_arrays(keys, values));
        leaf.insert(key, value).success();
        leaf.into()
    }

    fn remove(&mut self, key: K) -> RemoveResult<V> {
        self.keys.iter()
            .find(|&&k| k == key)
            .map(|_| RemoveResult::Downsize)
            .unwrap_or(RemoveResult::Success(None))
    }

    fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V) {
        let Leaf2 { keys, mut values } = self;
        let key1 = keys[0];
        let key2 = keys[1];
        let (value1, value2);
        unsafe {
            value1 = ptr::read(&mut values[0]);
            value2 = ptr::read(&mut values[1]);
            mem::forget(values);
        }
        if key1 == key {
            let ptr = Box::new(Leaf1::new(key2, value2)).into();
            (ptr, value1)
        } else {
            let ptr = Box::new(Leaf1::new(key1, value1)).into();
            (ptr, value2)
        }
    }

    fn len(&self) -> usize {
        2
    }
}

pub struct VecLeaf<K: Key, V> {
    array: locksteparray::LockstepArray<[K; 31], [V; 31]>
}

impl<K: Key, V> VecLeaf<K, V> {
    fn new() -> VecLeaf<K, V> {
        // TODO Copy memory from values
        VecLeaf {
            array: locksteparray::LockstepArray::new()
        }
    }

    fn from_arrays(keys: [K; 2], values: [V; 2]) -> VecLeaf<K, V> {
        VecLeaf {
            array: locksteparray::LockstepArray::from_arrays(keys, values)
        }
    }
}

impl<K: Key, V> IntoIterator for VecLeaf<K, V> {
    type Item = (K, V);
    type IntoIter = locksteparray::IntoIter<[K; 31], [V; 31]>;

    fn into_iter(self) -> Self::IntoIter {
        self.array.into_iter()
    }
}

impl<K: Key, V> RootLeaf<K, V> for VecLeaf<K, V> {
    fn get(&self, key: K) -> Option<&V> {
        self.array.array1()
            .iter()
            .position(|&k| k == key)
            .map(|index| &self.array.array2()[index])
    }

    fn get_mut(&mut self, key: K) -> Option<&mut V> {
        self.array.array1_mut()
            .iter()
            .position(|&k| k == key)
            .map(move |index| &mut self.array.array2_mut()[index])
    }

    fn insert(&mut self, key: K, value: V) -> InsertResult<V> {
        match self.array.array1().linear_search(&key) {
            Ok(replace) => {
                InsertResult::replace(&mut self.array.array2_mut()[replace],
                                      value)
            },
            Err(insert) => match self.array.insert(insert, key, value) {
                Ok(()) => InsertResult::Success(None),
                Err(locksteparray::InsertError::Overflow(key, value)) => {
                    InsertResult::Resize(value)
                },
                Err(locksteparray::InsertError::OutOfBounds(..)) => {
                    unreachable!()
                }
            }
        }
    }

    fn expand(self, key: K, value: V) -> RootPtr<K, V> {
        let mut jpm: Jpm<K, V> = self.into_iter().collect();
        jpm.insert(key, value).success();
        Box::new(jpm).into()
    }

    fn remove(&mut self, key: K) -> RemoveResult<V> {
        let evicted = self.array.array1_mut()
            .iter()
            .position(|&k| k == key)
            .and_then(|index| self.array.remove(index))
            .map(|(_, value)| value);
        RemoveResult::Success(evicted)
    }

    fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V) {
        unreachable!()
    }

    fn len(&self) -> usize {
        self.array.len()
    }
}
<|endoftext|>


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

use rustup;
use rustup_dist::{self, temp};
use rustup_utils;

declare_errors! {
    types {
        Error, ErrorKind, ChainErr, Result;
    }

    links {
        rustup::Error, rustup::ErrorKind, Rustup;
        rustup_dist::Error, rustup_dist::ErrorKind, Dist;
        rustup_utils::Error, rustup_utils::ErrorKind, Utils;
    }

    foreign_links {
        temp::Error, Temp;
    }

    errors {
        PermissionDenied {
            description("permission denied")
        }
        ToolchainNotInstalled(t: String) {
            description("toolchain is not installed")
            display("toolchain '{}' is not installed", t)
        }
        InfiniteRecursion {
            description("infinite recursion detected")
        }
        NoExeName {
            description("couldn't determine self executable name")
        }
        NotSelfInstalled(p: PathBuf) {
            description("rustup is not installed")
            display("rustup is not installed at '{}'", p.display())
        }
        WindowsUninstallMadness {
            description("failure during windows uninstall")
        }
    }
}
<commit_msg>Convert line endings<commit_after>use std::path::PathBuf;

use rustup;
use rustup_dist::{self, temp};
use rustup_utils;

declare_errors! {
    types {
        Error, ErrorKind, ChainErr, Result;
    }

    links {
        rustup::Error, rustup::ErrorKind, Rustup;
        rustup_dist::Error, rustup_dist::ErrorKind, Dist;
        rustup_utils::Error, rustup_utils::ErrorKind, Utils;
    }

    foreign_links {
        temp::Error, Temp;
    }

    errors {
        PermissionDenied {
            description("permission denied")
        }
        ToolchainNotInstalled(t: String) {
            description("toolchain is not installed")
            display("toolchain '{}' is not installed", t)
        }
        InfiniteRecursion {
            description("infinite recursion detected")
        }
        NoExeName {
            description("couldn't determine self executable name")
        }
        NotSelfInstalled(p: PathBuf) {
            description("rustup is not installed")
            display("rustup is not installed at '{}'", p.display())
        }
        WindowsUninstallMadness {
            description("failure during windows uninstall")
        }
    }
}
<|endoftext|>


<commit_before>//! Facilities for implementing quiescence searching.
//!
//! Most chess programs, at the end of the main search perform a more
//! limited quiescence search, containing fewer moves. The purpose of
//! this search is to only evaluate "quiet" positions, or positions
//! where there are no winning tactical moves to be made. Simply
//! stopping your search when you reach the desired depth and then
//! evaluate, is very dangerous. Consider the situation where the last
//! move is QxP. If you stop there and evaluate, you might think that
//! you have won a pawn. But what if you were to search one move
//! deeper and find that the next move is PxQ? You didn't win a pawn,
//! you actually lost a queen. Hence the need to make sure that you
//! are evaluating only quiescent (quiet) positions.

use chesstypes::*;
use board::MoveGenerator;

/// Parameters describing a quiescence search.
pub struct QsearchParams<'a, T: MoveGenerator + 'a> {
    /// A mutable reference to the root position for the search.
    ///
    /// **Important note:** The search routine may use this reference
    /// to do and undo moves, but when the search is finished, all
    /// played moves must be taken back so that the board is restored
    /// to its original state.
    pub position: &'a mut T,

    /// The requested search depth.
    ///
    /// This is the depth at which the main search stops and the
    /// quiescence search takes on. It should be between `DEPTH_MIN`
    /// and `DEPTH_MAX`. Normally, it will be zero or less. The
    /// quiescence search implementation may decide to perform less
    /// thorough analysis when `depth` is smaller.
    pub depth: Depth,

    /// The lower bound for the new search.
    ///
    /// Should be no lesser than `VALUE_MIN`.
    pub lower_bound: Value,

    /// The upper bound for the new search.
    ///
    /// Should be greater than `lower_bound`, but no greater than
    /// `VALUE_MAX`.
    pub upper_bound: Value,

    /// Position's static evaluation, or `VALUE_UNKNOWN`.
    ///
    /// Saves the re-calculation if position's static evaluation is
    /// already available.
    pub static_evaluation: Value,
}

/// Results from a quiescence search.
pub struct QsearchResult<T> {
    /// The calculated evaluation for the analyzed position.
    pub value: Value,

    /// The number of positions that were searched in order to
    /// calculate the evaluation.
    pub searched_nodes: u64,

    /// Auxiliary hints regarding the analyzed position.
    pub hints: T,
}

/// A trait used to perform quiescence searches.
pub trait Qsearch {
    type MoveGenerator: MoveGenerator;
    type Hints;

    /// TODO
    fn qsearch(params: QsearchParams<Self::MoveGenerator>) -> QsearchResult<Self::Hints>;
}
<commit_msg>add Qsearch implementation<commit_after>//! Facilities for implementing quiescence searching.
//!
//! Most chess programs, at the end of the main search perform a more
//! limited quiescence search, containing fewer moves. The purpose of
//! this search is to only evaluate "quiet" positions, or positions
//! where there are no winning tactical moves to be made. Simply
//! stopping your search when you reach the desired depth and then
//! evaluate, is very dangerous. Consider the situation where the last
//! move is QxP. If you stop there and evaluate, you might think that
//! you have won a pawn. But what if you were to search one move
//! deeper and find that the next move is PxQ? You didn't win a pawn,
//! you actually lost a queen. Hence the need to make sure that you
//! are evaluating only quiescent (quiet) positions.

use std::cell::UnsafeCell;
use std::marker::PhantomData;
use chesstypes::*;
use board::{MoveGenerator, BoardEvaluator};
use search::MoveStack;

/// Parameters describing a quiescence search.
pub struct QsearchParams<'a, T: MoveGenerator + 'a> {
    /// A mutable reference to the root position for the search.
    ///
    /// **Important note:** The search routine may use this reference
    /// to do and undo moves, but when the search is finished, all
    /// played moves must be taken back so that the board is restored
    /// to its original state.
    pub position: &'a mut T,

    /// The requested search depth.
    ///
    /// This is the depth at which the main search stops and the
    /// quiescence search takes on. It should be between `DEPTH_MIN`
    /// and `DEPTH_MAX`. Normally, it will be zero or less. The
    /// quiescence search implementation may decide to perform less
    /// thorough analysis when `depth` is smaller.
    pub depth: Depth,

    /// The lower bound for the new search.
    ///
    /// Should be no lesser than `VALUE_MIN`.
    pub lower_bound: Value,

    /// The upper bound for the new search.
    ///
    /// Should be greater than `lower_bound`, but no greater than
    /// `VALUE_MAX`.
    pub upper_bound: Value,

    /// Position's static evaluation, or `VALUE_UNKNOWN`.
    ///
    /// Saves the re-calculation if position's static evaluation is
    /// already available.
    pub static_evaluation: Value,
}

/// Results from a quiescence search.
pub struct QsearchResult<T> {
    /// The calculated evaluation for the analyzed position.
    pub value: Value,

    /// The number of positions that were searched in order to
    /// calculate the evaluation.
    pub searched_nodes: u64,

    /// Auxiliary hints regarding the analyzed position.
    pub hints: T,
}

/// A trait for performing quiescence searches.
pub trait Qsearch {
    type MoveGenerator: MoveGenerator;
    type Hints;

    /// TODO
    fn qsearch(params: QsearchParams<Self::MoveGenerator>) -> QsearchResult<Self::Hints>;
}

/// Implements a classical quiescence search routine.
pub struct StandardQsearch<T: MoveGenerator> {
    phantom: PhantomData<T>,
}

impl<T: MoveGenerator> Qsearch for StandardQsearch<T> {
    type MoveGenerator = T;

    type Hints = ();

    fn qsearch(params: QsearchParams<Self::MoveGenerator>) -> QsearchResult<Self::Hints> {
        let mut searched_nodes = 0;
        let value = MOVE_STACK.with(|s| unsafe {
            qsearch(params.position,
                    params.lower_bound,
                    params.upper_bound,
                    params.static_evaluation,
                    0,
                    -params.depth,
                    &mut *s.get(),
                    &mut searched_nodes)
        });
        QsearchResult {
            value: value,
            searched_nodes: searched_nodes,
            hints: (),
        }
    }
}

/// Performs a "quiescence search" and returns an evaluation.
///
/// The "quiescence search" is a restricted search which considers
/// only a limited set of moves (for example: winning captures,
/// pawn promotions to queen, check evasions). The goal is to
/// statically evaluate only "quiet" positions (positions where
/// there are no winning tactical moves to be made).
fn qsearch<T: MoveGenerator>(position: &mut T,
                             mut lower_bound: Value, // alpha
                             upper_bound: Value, // beta
                             mut stand_pat: Value, // position's static evaluation
                             mut recapture_squares: Bitboard,
                             ply: i8, // the reached `qsearch` depth
                             move_stack: &mut MoveStack,
                             searched_nodes: &mut u64)
                             -> Value {
    debug_assert!(lower_bound < upper_bound);
    debug_assert!(stand_pat == VALUE_UNKNOWN ||
                  stand_pat == position.evaluator().evaluate(position.board()));
    let in_check = position.checkers() != 0;

    // At the beginning of quiescence, position's static
    // evaluation (`stand_pat`) is used to establish a lower bound
    // on the result. We assume that even if none of the forcing
    // moves can improve over the stand pat, there will be at
    // least one "quiet" move that will at least preserve the
    // stand pat value. (Note that this assumption is not true if
    // the the side to move is in check, because in this case all
    // possible check evasions will be tried.)
    if in_check {
        // Position's static evaluation is useless when in check.
        stand_pat = lower_bound;
    } else if stand_pat == VALUE_UNKNOWN {
        stand_pat = position.evaluator().evaluate(position.board());
    }
    if stand_pat >= upper_bound {
        return stand_pat;
    }
    if stand_pat > lower_bound {
        lower_bound = stand_pat;
    }
    let obligatory_material_gain = (lower_bound as isize) - (stand_pat as isize) -
                                   (PIECE_VALUES[KNIGHT] - 4 * PIECE_VALUES[PAWN] / 3) as isize;

    // Generate all forcing moves. (Include checks only during the
    // first ply.)
    move_stack.save();
    position.generate_forcing(ply <= 0, move_stack);

    // Consider the generated moves one by one. See if any of them
    // can raise the lower bound.
    'trymoves: while let Some(m) = move_stack.remove_best() {
        let move_type = m.move_type();
        let dest_square_bb = 1 << m.dest_square();
        let captured_piece = m.captured_piece();

        // Decide whether to try the move. Check evasions,
        // en-passant captures (for them SEE is often wrong), and
        // mandatory recaptures are always tried. (In order to
        // correct SEE errors due to pinned and overloaded pieces,
        // at least one mandatory recapture is always tried at the
        // destination squares of previous moves.) For all other
        // moves, a static exchange evaluation is performed to
        // decide if the move should be tried.
        if !in_check && move_type != MOVE_ENPASSANT && recapture_squares & dest_square_bb == 0 {
            match position.calc_see(m) {
                // A losing move -- do not try it.
                x if x < 0 => continue 'trymoves,

                // An even exchange -- try it only during the first few plys.
                0 if ply >= SEE_EXCHANGE_MAX_PLY && captured_piece < NO_PIECE => continue 'trymoves,

                // A safe or winning move -- try it always.
                _ => (),
            }
        }

        // Try the move.
        if position.do_move(m).is_some() {
            // If the move does not give check, ensure that
            // the immediate material gain from the move is
            // big enough.
            if position.checkers() == 0 {
                let material_gain = if move_type == MOVE_PROMOTION {
                    PIECE_VALUES[captured_piece] +
                    PIECE_VALUES[Move::piece_from_aux_data(m.aux_data())] -
                    PIECE_VALUES[PAWN]
                } else {
                    PIECE_VALUES[captured_piece]
                };
                if (material_gain as isize) < obligatory_material_gain {
                    position.undo_move(m);
                    continue 'trymoves;
                }
            }

            // Recursively call `qsearch`.
            *searched_nodes += 1;
            let value = -qsearch(position,
                                 -upper_bound,
                                 -lower_bound,
                                 VALUE_UNKNOWN,
                                 recapture_squares ^ dest_square_bb,
                                 ply + 1,
                                 move_stack,
                                 searched_nodes);
            position.undo_move(m);

            // Update the lower bound.
            if value >= upper_bound {
                lower_bound = value;
                break 'trymoves;
            }
            if value > lower_bound {
                lower_bound = value;
            }

            // Mark that a recapture at this square has been tried.
            recapture_squares &= !dest_square_bb;
        }
    }
    move_stack.restore();

    // Return the determined lower bound. (We should make sure
    // that the returned value is between `VALUE_EVAL_MIN` and
    // `VALUE_EVAL_MAX`, regardless of the initial bounds passed
    // to `qsearch`. If we do not take this precautions, the
    // search algorithm will abstain from checkmating the
    // opponent, seeking the huge material gain that `qsearch`
    // promised.)
    match lower_bound {
        x if x < VALUE_EVAL_MIN => VALUE_EVAL_MIN,
        x if x > VALUE_EVAL_MAX => VALUE_EVAL_MAX,
        x => x,
    }
}

/// Thread-local storage for the generated moves.
thread_local!(
    static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
);

/// The material value of pieces.
const PIECE_VALUES: [Value; 7] = [10000, 975, 500, 325, 325, 100, 0];

/// Exchanges with SEE==0 will not be tried in `qsearch` once this ply
/// has been reached.
const SEE_EXCHANGE_MAX_PLY: i8 = 2;
<|endoftext|>


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

// ignore-android

#![feature(asm, rustc_attrs)]

#![allow(dead_code, non_upper_case_globals)]

#[cfg(any(target_arch = "x86",
          target_arch = "x86_64"))]
#[rustc_error]
pub fn main() { //~ ERROR compilation successful
    // assignment not dead
    let mut x: isize = 0;
    unsafe {
        // extra colon
        asm!("mov $1, $0" : "=r"(x) : "r"(5_usize), "0"(x) : : "cc");
        //~^ WARNING unrecognized option
    }
    assert_eq!(x, 5);

    unsafe {
        // comma in place of a colon
        asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8_usize) : "cc", "volatile");
        //~^ WARNING expected a clobber, found an option
    }
    assert_eq!(x, 13);
}
<commit_msg>Rollup merge of #33753 - mmatyas:armtestfix, r=alexcrichton<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-android
// ignore-arm
// ignore-aarch64

#![feature(asm, rustc_attrs)]

#![allow(dead_code, non_upper_case_globals)]

#[cfg(any(target_arch = "x86",
          target_arch = "x86_64"))]
#[rustc_error]
pub fn main() { //~ ERROR compilation successful
    // assignment not dead
    let mut x: isize = 0;
    unsafe {
        // extra colon
        asm!("mov $1, $0" : "=r"(x) : "r"(5_usize), "0"(x) : : "cc");
        //~^ WARNING unrecognized option
    }
    assert_eq!(x, 5);

    unsafe {
        // comma in place of a colon
        asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8_usize) : "cc", "volatile");
        //~^ WARNING expected a clobber, found an option
    }
    assert_eq!(x, 13);
}
<|endoftext|>