<commit_before>use std::vec_ng::Vec;
use std::char;
use std::mem;

#[deriving(Clone)]
pub enum Token {
    Text(~str),
    ETag(Vec<~str>, ~str),
    UTag(Vec<~str>, ~str),
    Section(Vec<~str>, bool, Vec<Token>, ~str, ~str, ~str, ~str, ~str),
    IncompleteSection(Vec<~str>, bool, ~str, bool),
    Partial(~str, ~str, ~str),
}

#[deriving(Clone)]
pub enum TokenClass {
    Normal,
    StandAlone,
    WhiteSpace(~str, uint),
    NewLineWhiteSpace(~str, uint),
}

pub struct Parser<'a, T> {
    reader: &'a mut T,
    ch: Option<char>,
    lookahead: Option<char>,
    line: uint,
    col: uint,
    content: ~str,
    state: ParserState,
    otag: ~str,
    ctag: ~str,
    otag_chars: Vec<char>,
    ctag_chars: Vec<char>,
    tag_position: uint,
    tokens: Vec<Token>,
    partials: Vec<~str>,
}

pub enum ParserState { TEXT, OTAG, TAG, CTAG }

impl<'a, T: Iterator<char>> Parser<'a, T> {
    pub fn bump(&mut self) {
        match self.lookahead.take() {
            None => { self.ch = self.reader.next(); }
            Some(ch) => { self.ch = Some(ch); }
        }

        match self.ch {
            Some(ch) => {
                if ch == '\n' {
                    self.line += 1;
                    self.col = 1;
                } else {
                    self.col += 1;
                }
            }
            None => { }
        }
    }

    fn peek(&mut self) -> Option<char> {
        match self.lookahead {
            None => {
                self.lookahead = self.reader.next();
                self.lookahead
            }
            Some(ch) => Some(ch),
        }
    }

    fn ch_is(&self, ch: char) -> bool {
        match self.ch {
            Some(c) => c == ch,
            None => false,
        }
    }

    pub fn parse(&mut self) {
        let mut curly_brace_tag = false;

        loop {
            let ch = match self.ch {
                Some(ch) => ch,
                None => { break; }
            };

            match self.state {
                TEXT => {
                    if ch == *self.otag_chars.get(0) {
                        if self.otag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = OTAG;
                        } else {
                            self.add_text();
                            self.state = TAG;
                        }
                    } else {
                        self.content.push_char(ch);
                    }
                    self.bump();
                }
                OTAG => {
                    if ch == *self.otag_chars.get(self.tag_position) {
                        if self.tag_position == self.otag_chars.len() - 1 {
                            self.add_text();
                            curly_brace_tag = false;
                            self.state = TAG;
                        } else {
                            self.tag_position = self.tag_position + 1;
                        }
                    } else {
                        // We don't have a tag, so add all the tag parts we've seen
                        // so far to the string.
                        self.state = TEXT;
                        self.not_otag();
                        self.content.push_char(ch);
                    }
                    self.bump();
                }
                TAG => {
                    if self.content == ~"" && ch == '{' {
                        curly_brace_tag = true;
                        self.content.push_char(ch);
                        self.bump();
                    } else if curly_brace_tag && ch == '}' {
                        curly_brace_tag = false;
                        self.content.push_char(ch);
                        self.bump();
                    } else if ch == *self.ctag_chars.get(0) {
                        if self.ctag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = CTAG;
                            self.bump();
                        } else {
                            self.add_tag();
                            self.state = TEXT;
                        }
                    } else {
                        self.content.push_char(ch);
                        self.bump();
                    }
                }
                CTAG => {
                    if ch == *self.ctag_chars.get(self.tag_position) {
                        if self.tag_position == self.ctag_chars.len() - 1 {
                            self.add_tag();
                            self.state = TEXT;
                        } else {
                            self.state = TAG;
                            self.not_ctag();
                            self.content.push_char(ch);
                            self.bump();
                        }
                    } else {
                        fail!("character {} is not part of CTAG: {}",
                              ch,
                              *self.ctag_chars.get(self.tag_position));
                    }
                }
            }
        }

        match self.state {
            TEXT => { self.add_text(); }
            OTAG => { self.not_otag(); self.add_text(); }
            TAG => { fail!(~"unclosed tag"); }
            CTAG => { self.not_ctag(); self.add_text(); }
        }

        // Check that we don't have any incomplete sections.
        for token in self.tokens.iter() {
            match *token {
                IncompleteSection(ref path, _, _, _) => {
                    fail!("Unclosed mustache section {}", path.connect("."));
              }
              _ => {}
            }
        };
    }

    fn add_text(&mut self) {
        if self.content != ~"" {
            let mut content = ~"";
            mem::swap(&mut content, &mut self.content);

            self.tokens.push(Text(content));
        }
    }

    // This function classifies whether or not a token is standalone, or if it
    // has trailing whitespace. It's looking for this pattern:
    //
    //   ("\n" | "\r\n") whitespace* token ("\n" | "\r\n")
    //
    fn classify_token(&mut self) -> TokenClass {
        // Exit early if the next character is not '\n' or '\r\n'.
        match self.ch {
            None => { }
            Some(ch) => {
                if !(ch == '\n' || (ch == '\r' && self.peek() == Some('\n'))) {
                    return Normal;
                }
            }
        }

        match self.tokens.last() {
            // If the last token ends with a newline (or there is no previous
            // token), then this token is standalone.
            None => { StandAlone }

            Some(&IncompleteSection(_, _, _, true)) => { StandAlone }

            Some(&Text(ref s)) if !s.is_empty() => {
                // Look for the last newline character that may have whitespace
                // following it.
                match s.rfind(|c:char| c == '\n' || !char::is_whitespace(c)) {
                    // It's all whitespace.
                    None => {
                        if self.tokens.len() == 1 {
                            WhiteSpace(s.to_owned(), 0)
                        } else {
                            Normal
                        }
                    }
                    Some(pos) => {
                        if s.char_at(pos) == '\n' {
                            if pos == s.len() - 1 {
                                StandAlone
                            } else {
                                WhiteSpace(s.to_owned(), pos + 1)
                            }
                        } else { Normal }
                    }
                }
            }
            Some(_) => Normal,
        }
    }

    fn eat_whitespace(&mut self) -> bool {
        // If the next character is a newline, and the last token ends with a
        // newline and whitespace, clear out the whitespace.

        match self.classify_token() {
            Normal => { false }
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                true
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s.slice(0, pos).to_str()));

                true
            }
        }
    }

    fn add_tag(&mut self) {
        self.bump();

        let tag = self.otag + self.content + self.ctag;

        // Move the content to avoid a copy.
        let mut content = ~"";
        mem::swap(&mut content, &mut self.content);
        let len = content.len();

        match content[0] as char {
            '!' => {
                // ignore comments
                self.eat_whitespace();
            }
            '&' => {
                let name = content.slice(1, len);
                let name = self.check_content(name);
                let name = name.split_terminator('.')
                    .map(|x| x.to_owned())
                    .collect();
                self.tokens.push(UTag(name, tag));
            }
            '{' => {
                if content.ends_with("}") {
                    let name = content.slice(1, len - 1);
                    let name = self.check_content(name);
                    let name = name.split_terminator('.')
                        .map(|x| x.to_owned())
                        .collect();
                    self.tokens.push(UTag(name, tag));
                } else { fail!(~"unbalanced \"{\" in tag"); }
            }
            '#' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(content.slice(1, len));
                let name = name.split_terminator('.')
                    .map(|x| x.to_owned())
                    .collect();
                self.tokens.push(IncompleteSection(name, false, tag, newlined));
            }
            '^' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(content.slice(1, len));
                let name = name.split_terminator('.')
                    .map(|x| x.to_owned())
                    .collect();
                self.tokens.push(IncompleteSection(name, true, tag, newlined));
            }
            '/' => {
                self.eat_whitespace();

                let name = self.check_content(content.slice(1, len));
                let name = name.split_terminator('.')
                    .map(|x| x.to_owned())
                    .collect();
                let mut children: Vec<Token> = Vec::new();

                loop {
                    if self.tokens.len() == 0 {
                        fail!(~"closing unopened section");
                    }

                    let last = self.tokens.pop();

                    match last {
                        Some(IncompleteSection(section_name, inverted, osection, _)) => {
                            children.reverse();

                            // Collect all the children's sources.
                            let mut srcs = Vec::new();
                            for child in children.iter() {
                                match *child {
                                    Text(ref s)
                                    | ETag(_, ref s)
                                    | UTag(_, ref s)
                                    | Partial(_, _, ref s) => {
                                        srcs.push(s.clone())
                                    }
                                    Section(_, _, _, _, ref osection, ref src, ref csection, _) => {
                                        srcs.push(osection.clone());
                                        srcs.push(src.clone());
                                        srcs.push(csection.clone());
                                    }
                                    _ => fail!(),
                                }
                            }

                            if section_name == name {
                                // Cache the combination of all the sources in the
                                // section. It's unfortunate, but we need to do this in
                                // case the user uses a function to instantiate the
                                // tag.
                                let mut src = ~"";
                                for s in srcs.iter() { src.push_str(*s); }

                                self.tokens.push(
                                    Section(
                                        name,
                                        inverted,
                                        children,
                                        self.otag.to_owned(),
                                        osection,
                                        src,
                                        tag,
                                        self.ctag.to_owned()));
                                break;
                            } else {
                                fail!(~"Unclosed section");
                            }
                        }
                        _ => { match last {
                            Some(last_token) => {children.push(last_token); }
                            None => ()
                            }
                        }
                    }
                }
            }
            '>' => { self.add_partial(content, tag); }
            '=' => {
                self.eat_whitespace();

                if len > 2u && content.ends_with("=") {
                    let s = self.check_content(content.slice(1, len - 1));

                    let pos = s.find(char::is_whitespace);
                    let pos = match pos {
                      None => { fail!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.otag = s.slice(0, pos).to_str();
                    self.otag_chars = self.otag.chars().collect();

                    let s2 = s.slice_from(pos);
                    let pos = s2.find(|c| !char::is_whitespace(c));
                    let pos = match pos {
                      None => { fail!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.ctag = s2.slice_from(pos).to_str();
                    self.ctag_chars = self.ctag.chars().collect();
                } else {
                    fail!("invalid change delimiter tag content");
                }
            }
            _ => {
                // If the name is "." then we want the top element, which we represent with
                // an empty name.
                let name = self.check_content(content);
                let name = if name == ~"." {
                    Vec::new()
                } else {
                    name.split_terminator('.')
                        .map(|x| x.to_owned())
                        .collect()
                };

                self.tokens.push(ETag(name, tag));
            }
        }
    }

    fn add_partial(&mut self, content: &str, tag: ~str) {
        let indent = match self.classify_token() {
            Normal => ~"",
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                ~""
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                let ws = s.slice(pos, s.len());

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s.slice(0, pos).to_str()));

                ws.to_owned()
            }
        };

        // We can't inline the tokens directly as we may have a recursive
        // partial. So instead, we'll cache the partials we used and look them
        // up later.
        let name = content.slice(1, content.len());
        let name = self.check_content(name);

        self.tokens.push(Partial(name.to_owned(), indent, tag));
        self.partials.push(name);
    }

    fn not_otag(&mut self) {
        let mut i = 0;
        while i < self.tag_position {
            self.content.push_char(*self.otag_chars.get(i));
            i += 1;
        }
    }

    fn not_ctag(&mut self) {
        let mut i = 0;
        while i < self.tag_position {
            self.content.push_char(*self.ctag_chars.get(i));
            i += 1;
        }
    }

    fn check_content(&self, content: &str) -> ~str {
        let trimmed = content.trim();
        if trimmed.len() == 0 {
            fail!(~"empty tag");
        }
        trimmed.to_owned()
    }
}<commit_msg>Use Iterator.enumerate() to reduce boundary checks<commit_after>use std::vec_ng::Vec;
use std::char;
use std::mem;

#[deriving(Clone)]
pub enum Token {
    Text(~str),
    ETag(Vec<~str>, ~str),
    UTag(Vec<~str>, ~str),
    Section(Vec<~str>, bool, Vec<Token>, ~str, ~str, ~str, ~str, ~str),
    IncompleteSection(Vec<~str>, bool, ~str, bool),
    Partial(~str, ~str, ~str),
}

#[deriving(Clone)]
pub enum TokenClass {
    Normal,
    StandAlone,
    WhiteSpace(~str, uint),
    NewLineWhiteSpace(~str, uint),
}

pub struct Parser<'a, T> {
    reader: &'a mut T,
    ch: Option<char>,
    lookahead: Option<char>,
    line: uint,
    col: uint,
    content: ~str,
    state: ParserState,
    otag: ~str,
    ctag: ~str,
    otag_chars: Vec<char>,
    ctag_chars: Vec<char>,
    tag_position: uint,
    tokens: Vec<Token>,
    partials: Vec<~str>,
}

pub enum ParserState { TEXT, OTAG, TAG, CTAG }

impl<'a, T: Iterator<char>> Parser<'a, T> {
    pub fn bump(&mut self) {
        match self.lookahead.take() {
            None => { self.ch = self.reader.next(); }
            Some(ch) => { self.ch = Some(ch); }
        }

        match self.ch {
            Some(ch) => {
                if ch == '\n' {
                    self.line += 1;
                    self.col = 1;
                } else {
                    self.col += 1;
                }
            }
            None => { }
        }
    }

    fn peek(&mut self) -> Option<char> {
        match self.lookahead {
            None => {
                self.lookahead = self.reader.next();
                self.lookahead
            }
            Some(ch) => Some(ch),
        }
    }

    fn ch_is(&self, ch: char) -> bool {
        match self.ch {
            Some(c) => c == ch,
            None => false,
        }
    }

    pub fn parse(&mut self) {
        let mut curly_brace_tag = false;

        loop {
            let ch = match self.ch {
                Some(ch) => ch,
                None => { break; }
            };

            match self.state {
                TEXT => {
                    if ch == *self.otag_chars.get(0) {
                        if self.otag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = OTAG;
                        } else {
                            self.add_text();
                            self.state = TAG;
                        }
                    } else {
                        self.content.push_char(ch);
                    }
                    self.bump();
                }
                OTAG => {
                    if ch == *self.otag_chars.get(self.tag_position) {
                        if self.tag_position == self.otag_chars.len() - 1 {
                            self.add_text();
                            curly_brace_tag = false;
                            self.state = TAG;
                        } else {
                            self.tag_position = self.tag_position + 1;
                        }
                    } else {
                        // We don't have a tag, so add all the tag parts we've seen
                        // so far to the string.
                        self.state = TEXT;
                        self.not_otag();
                        self.content.push_char(ch);
                    }
                    self.bump();
                }
                TAG => {
                    if self.content == ~"" && ch == '{' {
                        curly_brace_tag = true;
                        self.content.push_char(ch);
                        self.bump();
                    } else if curly_brace_tag && ch == '}' {
                        curly_brace_tag = false;
                        self.content.push_char(ch);
                        self.bump();
                    } else if ch == *self.ctag_chars.get(0) {
                        if self.ctag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = CTAG;
                            self.bump();
                        } else {
                            self.add_tag();
                            self.state = TEXT;
                        }
                    } else {
                        self.content.push_char(ch);
                        self.bump();
                    }
                }
                CTAG => {
                    if ch == *self.ctag_chars.get(self.tag_position) {
                        if self.tag_position == self.ctag_chars.len() - 1 {
                            self.add_tag();
                            self.state = TEXT;
                        } else {
                            self.state = TAG;
                            self.not_ctag();
                            self.content.push_char(ch);
                            self.bump();
                        }
                    } else {
                        fail!("character {} is not part of CTAG: {}",
                              ch,
                              *self.ctag_chars.get(self.tag_position));
                    }
                }
            }
        }

        match self.state {
            TEXT => { self.add_text(); }
            OTAG => { self.not_otag(); self.add_text(); }
            TAG => { fail!(~"unclosed tag"); }
            CTAG => { self.not_ctag(); self.add_text(); }
        }

        // Check that we don't have any incomplete sections.
        for token in self.tokens.iter() {
            match *token {
                IncompleteSection(ref path, _, _, _) => {
                    fail!("Unclosed mustache section {}", path.connect("."));
              }
              _ => {}
            }
        };
    }

    fn add_text(&mut self) {
        if self.content != ~"" {
            let mut content = ~"";
            mem::swap(&mut content, &mut self.content);

            self.tokens.push(Text(content));
        }
    }

    // This function classifies whether or not a token is standalone, or if it
    // has trailing whitespace. It's looking for this pattern:
    //
    //   ("\n" | "\r\n") whitespace* token ("\n" | "\r\n")
    //
    fn classify_token(&mut self) -> TokenClass {
        // Exit early if the next character is not '\n' or '\r\n'.
        match self.ch {
            None => { }
            Some(ch) => {
                if !(ch == '\n' || (ch == '\r' && self.peek() == Some('\n'))) {
                    return Normal;
                }
            }
        }

        match self.tokens.last() {
            // If the last token ends with a newline (or there is no previous
            // token), then this token is standalone.
            None => { StandAlone }

            Some(&IncompleteSection(_, _, _, true)) => { StandAlone }

            Some(&Text(ref s)) if !s.is_empty() => {
                // Look for the last newline character that may have whitespace
                // following it.
                match s.rfind(|c:char| c == '\n' || !char::is_whitespace(c)) {
                    // It's all whitespace.
                    None => {
                        if self.tokens.len() == 1 {
                            WhiteSpace(s.to_owned(), 0)
                        } else {
                            Normal
                        }
                    }
                    Some(pos) => {
                        if s.char_at(pos) == '\n' {
                            if pos == s.len() - 1 {
                                StandAlone
                            } else {
                                WhiteSpace(s.to_owned(), pos + 1)
                            }
                        } else { Normal }
                    }
                }
            }
            Some(_) => Normal,
        }
    }

    fn eat_whitespace(&mut self) -> bool {
        // If the next character is a newline, and the last token ends with a
        // newline and whitespace, clear out the whitespace.

        match self.classify_token() {
            Normal => { false }
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                true
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s.slice(0, pos).to_str()));

                true
            }
        }
    }

    fn add_tag(&mut self) {
        self.bump();

        let tag = self.otag + self.content + self.ctag;

        // Move the content to avoid a copy.
        let mut content = ~"";
        mem::swap(&mut content, &mut self.content);
        let len = content.len();

        match content[0] as char {
            '!' => {
                // ignore comments
                self.eat_whitespace();
            }
            '&' => {
                let name = content.slice(1, len);
                let name = self.check_content(name);
                let name = name.split_terminator('.')
                    .map(|x| x.to_owned())
                    .collect();
                self.tokens.push(UTag(name, tag));
            }
            '{' => {
                if content.ends_with("}") {
                    let name = content.slice(1, len - 1);
                    let name = self.check_content(name);
                    let name = name.split_terminator('.')
                        .map(|x| x.to_owned())
                        .collect();
                    self.tokens.push(UTag(name, tag));
                } else { fail!(~"unbalanced \"{\" in tag"); }
            }
            '#' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(content.slice(1, len));
                let name = name.split_terminator('.')
                    .map(|x| x.to_owned())
                    .collect();
                self.tokens.push(IncompleteSection(name, false, tag, newlined));
            }
            '^' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(content.slice(1, len));
                let name = name.split_terminator('.')
                    .map(|x| x.to_owned())
                    .collect();
                self.tokens.push(IncompleteSection(name, true, tag, newlined));
            }
            '/' => {
                self.eat_whitespace();

                let name = self.check_content(content.slice(1, len));
                let name = name.split_terminator('.')
                    .map(|x| x.to_owned())
                    .collect();
                let mut children: Vec<Token> = Vec::new();

                loop {
                    if self.tokens.len() == 0 {
                        fail!(~"closing unopened section");
                    }

                    let last = self.tokens.pop();

                    match last {
                        Some(IncompleteSection(section_name, inverted, osection, _)) => {
                            children.reverse();

                            // Collect all the children's sources.
                            let mut srcs = Vec::new();
                            for child in children.iter() {
                                match *child {
                                    Text(ref s)
                                    | ETag(_, ref s)
                                    | UTag(_, ref s)
                                    | Partial(_, _, ref s) => {
                                        srcs.push(s.clone())
                                    }
                                    Section(_, _, _, _, ref osection, ref src, ref csection, _) => {
                                        srcs.push(osection.clone());
                                        srcs.push(src.clone());
                                        srcs.push(csection.clone());
                                    }
                                    _ => fail!(),
                                }
                            }

                            if section_name == name {
                                // Cache the combination of all the sources in the
                                // section. It's unfortunate, but we need to do this in
                                // case the user uses a function to instantiate the
                                // tag.
                                let mut src = ~"";
                                for s in srcs.iter() { src.push_str(*s); }

                                self.tokens.push(
                                    Section(
                                        name,
                                        inverted,
                                        children,
                                        self.otag.to_owned(),
                                        osection,
                                        src,
                                        tag,
                                        self.ctag.to_owned()));
                                break;
                            } else {
                                fail!(~"Unclosed section");
                            }
                        }
                        _ => { match last {
                            Some(last_token) => {children.push(last_token); }
                            None => ()
                            }
                        }
                    }
                }
            }
            '>' => { self.add_partial(content, tag); }
            '=' => {
                self.eat_whitespace();

                if len > 2u && content.ends_with("=") {
                    let s = self.check_content(content.slice(1, len - 1));

                    let pos = s.find(char::is_whitespace);
                    let pos = match pos {
                      None => { fail!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.otag = s.slice(0, pos).to_str();
                    self.otag_chars = self.otag.chars().collect();

                    let s2 = s.slice_from(pos);
                    let pos = s2.find(|c| !char::is_whitespace(c));
                    let pos = match pos {
                      None => { fail!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.ctag = s2.slice_from(pos).to_str();
                    self.ctag_chars = self.ctag.chars().collect();
                } else {
                    fail!("invalid change delimiter tag content");
                }
            }
            _ => {
                // If the name is "." then we want the top element, which we represent with
                // an empty name.
                let name = self.check_content(content);
                let name = if name == ~"." {
                    Vec::new()
                } else {
                    name.split_terminator('.')
                        .map(|x| x.to_owned())
                        .collect()
                };

                self.tokens.push(ETag(name, tag));
            }
        }
    }

    fn add_partial(&mut self, content: &str, tag: ~str) {
        let indent = match self.classify_token() {
            Normal => ~"",
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                ~""
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                let ws = s.slice(pos, s.len());

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s.slice(0, pos).to_str()));

                ws.to_owned()
            }
        };

        // We can't inline the tokens directly as we may have a recursive
        // partial. So instead, we'll cache the partials we used and look them
        // up later.
        let name = content.slice(1, content.len());
        let name = self.check_content(name);

        self.tokens.push(Partial(name.to_owned(), indent, tag));
        self.partials.push(name);
    }

    fn not_otag(&mut self) {
        for (i, ch) in self.otag_chars.iter().enumerate() {
            if !(i < self.tag_position) {
                break
            }
            self.content.push_char(*ch);
        }
    }

    fn not_ctag(&mut self) {
        for (i, ch) in self.ctag_chars.iter().enumerate() {
            if !(i < self.tag_position) {
                break
            }
            self.content.push_char(*ch);
        }
    }

    fn check_content(&self, content: &str) -> ~str {
        let trimmed = content.trim();
        if trimmed.len() == 0 {
            fail!(~"empty tag");
        }
        trimmed.to_owned()
    }
}<|endoftext|>


<commit_before>use arc = std::arc;
use arc::ARC;
use au = gfx::geometry;
use font::{RunMetrics, Font};
use font_cache::FontCache;
use geom::point::Point2D;
use geom::size::Size2D;
use gfx::geometry::Au;
use glyph::GlyphStore;
use layout::context::LayoutContext;
use libc::{c_void};
use servo_util::color;
use shaper::shape_textrun;
use std::arc;
use servo_util::range::{Range, MutableRange};

pub struct TextRun {
    text: ~str,
    font: @Font,
    priv glyphs: GlyphStore,
}

// This is a hack until TextRuns are normally sendable, or
// we instead use ARC<TextRun> everywhere.
pub struct SendableTextRun {
    text: ~str,
    font_descriptor: (),
    priv glyphs: GlyphStore,
}

impl SendableTextRun {
    pub fn deserialize(&self, cache: @FontCache) -> TextRun {
        TextRun {
            text: copy self.text,
            // TODO: actually deserialize a font descriptor thingy
            font: cache.get_test_font(),
            glyphs: copy self.glyphs
        }
    }
}

impl TextRun {
    pub static fn new(font: @Font, text: ~str) -> TextRun {
        let glyph_store = GlyphStore(text.len());
        let run = TextRun {
            text: move text,
            font: font,
            glyphs: move glyph_store,
        };

        shape_textrun(&run);
        return move run;
    }

    pub pure fn serialize(&self, _cache: @FontCache) -> SendableTextRun {
        SendableTextRun {
            text: copy self.text,
            // TODO: actually serialize a font descriptor thingy
            font_descriptor: (),
            glyphs: copy self.glyphs,
        }
    }

    pure fn glyphs(&self) -> &self/GlyphStore { &self.glyphs }

    pure fn range_is_trimmable_whitespace(&self, range: Range) -> bool {
        let mut i = range.begin();
        while i < range.end() {
            // jump i to each new char
            let {ch, next} = str::char_range_at(self.text, i);
            match ch {
                ' ' | '\t' | '\r'  => {},
                _ => { return false; }
            }
            i = next;
        }
        return true;
    }

    fn metrics_for_range(&self, range: Range) -> RunMetrics {
        self.font.measure_text(self, range)
    }

    fn min_width_for_range(&self, range: Range) -> Au {
        assert range.is_valid_for_string(self.text);

        let mut max_piece_width = Au(0);
        for self.iter_indivisible_pieces_for_range(range) |piece_range| {
            let metrics = self.font.measure_text(self, piece_range);
            max_piece_width = au::max(max_piece_width, metrics.advance_width);
        }
        return max_piece_width;
    }

    fn iter_natural_lines_for_range(&self, range: Range, f: fn(Range) -> bool) {
        assert range.is_valid_for_string(self.text);

        let clump = MutableRange(range.begin(), 0);
        let mut in_clump = false;

        // clump non-linebreaks of nonzero length
        for range.eachi |i| {
            match (self.glyphs.char_is_newline(i), in_clump) {
                (false, true)  => { clump.extend_by(1); }
                (false, false) => { in_clump = true; clump.reset(i, 1); }
                (true, false) => { /* chomp whitespace */ }
                (true, true)  => {
                    in_clump = false;
                    // don't include the linebreak 'glyph'
                    // (we assume there's one GlyphEntry for a newline, and no actual glyphs)
                    if !f(clump.as_immutable()) { break }
                }
            }
        }

        // flush any remaining chars as a line
        if in_clump {
            clump.extend_to(range.end());
            f(clump.as_immutable());
        }
    }

    fn iter_indivisible_pieces_for_range(&self, range: Range, f: fn(Range) -> bool) {
        assert range.is_valid_for_string(self.text);

        let clump = MutableRange(range.begin(), 0);
        loop {
            // find next non-whitespace byte index, then clump all whitespace before it.
            match str::find_between(self.text, clump.begin(), range.end(), |c| !char::is_whitespace(c)) {
                Some(nonws_char_offset) => {
                    clump.extend_to(nonws_char_offset);
                    if !f(clump.as_immutable()) { break }
                    clump.reset(clump.end(), 0);
                },
                None => {
                    // nothing left, flush last piece containing only whitespace
                    if clump.end() < range.end() {
                        clump.extend_to(range.end());
                        f(clump.as_immutable());
                        break;
                    }
                }
            };

            // find next whitespace byte index, then clump all non-whitespace before it.
            match str::find_between(self.text, clump.begin(), range.end(), |c| char::is_whitespace(c)) {
                Some(ws_char_offset) => {
                    clump.extend_to(ws_char_offset);
                    if !f(clump.as_immutable()) { break }
                    clump.reset(clump.end(), 0);
                }
                None => {
                    // nothing left, flush last piece containing only non-whitespaces
                    if clump.end() < range.end() {
                        clump.extend_to(range.end());
                        f(clump.as_immutable());
                        break;
                    }
                }
            }
        }
    }
}

// this test can't run until LayoutContext is removed as an argument
// to min_width_for_range.
/*
#[test]
fn test_calc_min_break_width() {

    fn test_min_width_for_run(text: ~str, width: Au) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, text);
        run.min_width_for_range(0, text.len())
    }

    test_min_width_for_run(~"firecracker", au::from_px(84));
    test_min_width_for_run(~"firecracker yumyum", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker yumyum", au::from_px(84));
}
*/

#[test]
#[ignore]
fn test_iter_indivisible_pieces() {
    fn test_pieces(text: ~str, res: ~[~str]) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, copy text);
        let mut slices : ~[~str] = ~[];
        for run.iter_indivisible_pieces_for_range(Range(0, text.len())) |subrange| {
            slices.push(str::slice(text, subrange.begin(), subrange.length()));
        }
        assert slices == res;
    }

    test_pieces(~"firecracker yumyum woopwoop", ~[~"firecracker", ~" ", ~"yumyum", ~" ", ~"woopwoop"]);
    test_pieces(~"firecracker yumyum     ", ~[~"firecracker", ~" ", ~"yumyum", ~"     "]);
    test_pieces(~"     firecracker yumyum", ~[~"     ", ~"firecracker", ~" ", ~"yumyum"]);
    test_pieces(~"  ", ~[~"  "]);
    test_pieces(~"", ~[]);
}

<commit_msg>Fix test bustage.<commit_after>use arc = std::arc;
use arc::ARC;
use au = gfx::geometry;
use font::{RunMetrics, Font};
use font_cache::FontCache;
use geom::point::Point2D;
use geom::size::Size2D;
use gfx::geometry::Au;
use glyph::GlyphStore;
use layout::context::LayoutContext;
use libc::{c_void};
use servo_util::color;
use shaper::shape_textrun;
use std::arc;
use servo_util::range::{Range, MutableRange};

pub struct TextRun {
    text: ~str,
    font: @Font,
    priv glyphs: GlyphStore,
}

// This is a hack until TextRuns are normally sendable, or
// we instead use ARC<TextRun> everywhere.
pub struct SendableTextRun {
    text: ~str,
    font_descriptor: (),
    priv glyphs: GlyphStore,
}

impl SendableTextRun {
    pub fn deserialize(&self, cache: @FontCache) -> TextRun {
        TextRun {
            text: copy self.text,
            // TODO: actually deserialize a font descriptor thingy
            font: cache.get_test_font(),
            glyphs: copy self.glyphs
        }
    }
}

impl TextRun {
    pub static fn new(font: @Font, text: ~str) -> TextRun {
        let glyph_store = GlyphStore(text.len());
        let run = TextRun {
            text: move text,
            font: font,
            glyphs: move glyph_store,
        };

        shape_textrun(&run);
        return move run;
    }

    pub pure fn serialize(&self, _cache: @FontCache) -> SendableTextRun {
        SendableTextRun {
            text: copy self.text,
            // TODO: actually serialize a font descriptor thingy
            font_descriptor: (),
            glyphs: copy self.glyphs,
        }
    }

    pure fn glyphs(&self) -> &self/GlyphStore { &self.glyphs }

    pure fn range_is_trimmable_whitespace(&self, range: Range) -> bool {
        let mut i = range.begin();
        while i < range.end() {
            // jump i to each new char
            let {ch, next} = str::char_range_at(self.text, i);
            match ch {
                ' ' | '\t' | '\r'  => {},
                _ => { return false; }
            }
            i = next;
        }
        return true;
    }

    fn metrics_for_range(&self, range: Range) -> RunMetrics {
        self.font.measure_text(self, range)
    }

    fn min_width_for_range(&self, range: Range) -> Au {
        assert range.is_valid_for_string(self.text);

        let mut max_piece_width = Au(0);
        for self.iter_indivisible_pieces_for_range(range) |piece_range| {
            let metrics = self.font.measure_text(self, piece_range);
            max_piece_width = au::max(max_piece_width, metrics.advance_width);
        }
        return max_piece_width;
    }

    fn iter_natural_lines_for_range(&self, range: Range, f: fn(Range) -> bool) {
        assert range.is_valid_for_string(self.text);

        let clump = MutableRange(range.begin(), 0);
        let mut in_clump = false;

        // clump non-linebreaks of nonzero length
        for range.eachi |i| {
            match (self.glyphs.char_is_newline(i), in_clump) {
                (false, true)  => { clump.extend_by(1); }
                (false, false) => { in_clump = true; clump.reset(i, 1); }
                (true, false) => { /* chomp whitespace */ }
                (true, true)  => {
                    in_clump = false;
                    // don't include the linebreak 'glyph'
                    // (we assume there's one GlyphEntry for a newline, and no actual glyphs)
                    if !f(clump.as_immutable()) { break }
                }
            }
        }

        // flush any remaining chars as a line
        if in_clump {
            clump.extend_to(range.end());
            f(clump.as_immutable());
        }
    }

    fn iter_indivisible_pieces_for_range(&self, range: Range, f: fn(Range) -> bool) {
        assert range.is_valid_for_string(self.text);

        let clump = MutableRange(range.begin(), 0);
        loop {
            // find next non-whitespace byte index, then clump all whitespace before it.
            match str::find_between(self.text, clump.begin(), range.end(), |c| !char::is_whitespace(c)) {
                Some(nonws_char_offset) => {
                    clump.extend_to(nonws_char_offset);
                    if !f(clump.as_immutable()) { break }
                    clump.reset(clump.end(), 0);
                },
                None => {
                    // nothing left, flush last piece containing only whitespace
                    if clump.end() < range.end() {
                        clump.extend_to(range.end());
                        f(clump.as_immutable());
                        break;
                    }
                }
            };

            // find next whitespace byte index, then clump all non-whitespace before it.
            match str::find_between(self.text, clump.begin(), range.end(), |c| char::is_whitespace(c)) {
                Some(ws_char_offset) => {
                    clump.extend_to(ws_char_offset);
                    if !f(clump.as_immutable()) { break }
                    clump.reset(clump.end(), 0);
                }
                None => {
                    // nothing left, flush last piece containing only non-whitespaces
                    if clump.end() < range.end() {
                        clump.extend_to(range.end());
                        f(clump.as_immutable());
                        break;
                    }
                }
            }
        }
    }
}

// this test can't run until LayoutContext is removed as an argument
// to min_width_for_range.
/*
#[test]
fn test_calc_min_break_width() {

    fn test_min_width_for_run(text: ~str, width: Au) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, text);
        run.min_width_for_range(0, text.len())
    }

    test_min_width_for_run(~"firecracker", au::from_px(84));
    test_min_width_for_run(~"firecracker yumyum", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker yumyum", au::from_px(84));
}
*/

#[test]
#[ignore]
fn test_iter_indivisible_pieces() {
    fn test_pieces(text: ~str, res: ~[~str]) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun::new(font, copy text);
        let mut slices : ~[~str] = ~[];
        for run.iter_indivisible_pieces_for_range(Range(0, text.len())) |subrange| {
            slices.push(str::slice(text, subrange.begin(), subrange.length()));
        }
        assert slices == res;
    }

    test_pieces(~"firecracker yumyum woopwoop", ~[~"firecracker", ~" ", ~"yumyum", ~" ", ~"woopwoop"]);
    test_pieces(~"firecracker yumyum     ", ~[~"firecracker", ~" ", ~"yumyum", ~"     "]);
    test_pieces(~"     firecracker yumyum", ~[~"     ", ~"firecracker", ~" ", ~"yumyum"]);
    test_pieces(~"  ", ~[~"  "]);
    test_pieces(~"", ~[]);
}

<|endoftext|>


<commit_before>//! Various helpers for code-generation.

use std::io;
use std::cmp;
use std::char;
use std::iter;
use std::usize;
use std::ops::{Add, AddAssign, Range};
#[cfg(test)]
use std::iter::FromIterator;

use super::{Atom, Class, ClassMember};

// ================================================================
/// Trait for atom types that can be converted to byte sequences.
pub trait Bytes<'a> {
    /// Iterator type returned by `bytes`.
    type Iter: 'a + Iterator<Item=u8>;

    /// Fetch an iterator over "self" as a series of bytes.
    fn bytes(&self) -> Self::Iter;
}

/// Interface for types which can bound the number of bytes required to match
/// a particular instance of themselves.
pub trait SizedRead {
    /// Get the size of this object when read directly from something
    /// implementing `std::io::Read`.
    fn read_size(&self) -> ReadSize;
}

/// Trait for use with fixed-size types
pub trait ReadToBuffer {
    /// Read at most `max_count` instances of this atom type from the given
    /// stream, placing them into the supplied buffer.
    fn read_to_buffer<R>(r: &mut R, dest: &mut [u8], max_count: usize) -> io::Result<usize>
        where R: io::Read;
}

/// Possible results from `SizedRead::read_size`
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ReadSize {
    /// Exact size is known in advance
    Exact(usize),

    /// Size can be within the given range of values.
    Range(usize, usize)
}

impl ReadSize {
    /// Get the minimum number of bytes specified by this ReadSize.
    #[inline]
    fn min(&self) -> usize {
        match self {
            &ReadSize::Exact(s) => s,
            &ReadSize::Range(a, _) => a,
        }
    }
    /// Get the maximum number of bytes specified by this ReadSize.
    #[inline]
    fn max(&self) -> usize {
        match self {
            &ReadSize::Exact(s) => s,
            &ReadSize::Range(_, b) => b,
        }
    }

}

impl iter::Sum<ReadSize> for ReadSize {
    #[inline]
    fn sum<I>(iter: I) -> Self where I: Iterator<Item=ReadSize> {
        let mut sum = ReadSize::Exact(0);
        for rs in iter {
            sum += rs;
        }
        sum
    }
}

impl Add<ReadSize> for ReadSize {
    type Output = ReadSize;
    #[inline]
    fn add(self, rhs: ReadSize) -> Self::Output {
        let min = self.min() + rhs.min();
        let max = self.max() + rhs.max();
        if min == max { ReadSize::Exact(min) }
        else { ReadSize::Range(min, max) }
    }
}

impl AddAssign<ReadSize> for ReadSize {
    #[inline]
    fn add_assign(&mut self, rhs: ReadSize) {
        *self = *self + rhs;
    }
}

impl From<usize> for ReadSize {
    #[inline]
    fn from(s: usize) -> Self {
        ReadSize::Exact(s)
    }
}

impl From<Range<usize>> for ReadSize {
    #[inline]
    fn from(r: Range<usize>) -> Self {
        if r.len() > 1 { ReadSize::Range(r.start, r.end - 1) }
        else { ReadSize::Exact(r.start) }
    }
}

// ----------------------------------------------------------------
// Implementations

impl<'a> Bytes<'a> for u8 {
    type Iter = iter::Once<u8>;
    fn bytes(&self) -> Self::Iter {
        iter::once(*self)
    }
}

impl SizedRead for u8 {
    #[inline(always)]
    fn read_size(&self) -> ReadSize {
        ReadSize::Exact(1)
    }
}

impl ReadToBuffer for u8 {
    #[inline(always)]
    fn read_to_buffer<R>(r: &mut R, dest: &mut [u8], max_count: usize) -> io::Result<usize>
        where R: io::Read {
        r.read(&mut dest[0..max_count])
    }
}

// This table was copied from the Rust source tree,
// file "src/libcore/str/mod.rs".
// https://tools.ietf.org/html/rfc3629
static UTF8_CHAR_WIDTH: [u8; 256] = [
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
];

impl<'a> Bytes<'a> for char {
    type Iter = char::EncodeUtf8;
    fn bytes(&self) -> Self::Iter {
        self.encode_utf8()
    }
}

impl SizedRead for char {
    #[inline]
    fn read_size(&self) -> ReadSize {
        self.len_utf8().into()
    }
}

impl ReadToBuffer for char {
    #[inline]
    fn read_to_buffer<R>(r: &mut R, dest: &mut [u8], max_count: usize) -> io::Result<usize>
        where R: io::Read {
        let mut i = 0;
        let mut n = 0;
        while i < dest.len() && n < max_count {
            try!(r.read_exact(&mut dest[i..(i+1)]));
            let w = UTF8_CHAR_WIDTH[dest[i] as usize];
            try!(r.read_exact(&mut dest[(i+1)..(i + w as usize)]));
            i += w as usize;
            n += 1;
        }
        Ok(i)
    }

}

#[cfg(test)]
#[test]
fn test_sizedread_char() {
    assert_eq!(ReadSize::Exact(1), 'a'.read_size());
    assert_eq!(ReadSize::Exact(3), '✓'.read_size());
}

#[cfg(test)]
#[test]
fn test_readable_char() {
    let mut buf = ['\0' as u8; 32];
    {
        let mut r = io::repeat('a' as u8);
        assert_eq!(3, char::read_to_buffer(&mut r, &mut buf, 3).unwrap());
        assert_eq!(&['a' as u8 as u8, 'a' as u8, 'a' as u8, '\0' as u8], &buf[0..4]);
    }

    {
        let s = "✓☠⚔";
        let mut r = io::Cursor::new(s.as_bytes());
        assert_eq!(9, char::read_to_buffer(&mut r, &mut buf, 3).unwrap());
        assert_eq!(s.as_bytes(), &buf[0..9]);
    }
}

// ----------------------------------------------------------------

/*impl<T> SizedRead for Transition<T>
    where T: Atom + SizedRead
{
    fn read_size(&self) -> ReadSize {
        match self {
            &Transition::Atom(a) => a.read_size(),
            &Transition::Literal(ref atoms) => atoms.iter().map(|a| a.read_size()).sum(),
            &Transition::Wildcard => ReadSize::Range(1, 4),
            &Transition::Anchor(..) => ReadSize::Exact(0),
            &Transition::Class(ref c) => c.read_size(),
        }
    }
}*/

#[cfg(test)]
#[test]
fn test_sizedread_transition() {
    assert_eq!(ReadSize::Exact(1), 'a'.read_size());
    assert_eq!(ReadSize::Exact(3), '✓'.read_size());
    assert_eq!(ReadSize::Range(1, 3), Class::from_iter(['x', 'y', '⚔'].into_iter().cloned()).read_size());
}

// ----------------------------------------------------------------

impl<T> SizedRead for ClassMember<T>
    where T: Atom + SizedRead
{
    fn read_size(&self) -> ReadSize {
        match self {
            &ClassMember::Atom(a) => a.read_size(),
            &ClassMember::Range(first, last) => {
                // We'll assume that any differences in read size between
                // `first` and `last` follow a linear function between the two;
                // this _assumption_ allows us to avoid evaluating `read_size`
                // for every single member of the range.
                let frs = first.read_size();
                let lrs = last.read_size();
                (cmp::min(frs.min(), lrs.min())..(cmp::max(frs.max(), lrs.max()) + 1)).into()
            }
        }
    }
}

impl<T> SizedRead for Class<T>
    where T: Atom + SizedRead
{
    fn read_size(&self) -> ReadSize {
        let mut min = usize::MAX;
        let mut max = usize::MIN;

        for m in self.iter_members() {
            let rs = m.read_size();
            min = cmp::min(min, rs.min());
            max = cmp::max(max, rs.max());
        }

        (min..(max + 1)).into()
    }
}

<commit_msg>pattern/codegen: some whitespace and formatting cleanups<commit_after>//! Various helpers for code-generation.

use std::io;
use std::cmp;
use std::char;
use std::iter;
use std::usize;
use std::ops::{Add, AddAssign, Range};
#[cfg(test)]
use std::iter::FromIterator;

use super::{Atom, Class, ClassMember};

// ================================================================
/// Trait for atom types that can be converted to byte sequences.
pub trait Bytes<'a> {
    /// Iterator type returned by `bytes`.
    type Iter: 'a + Iterator<Item=u8>;

    /// Fetch an iterator over "self" as a series of bytes.
    fn bytes(&self) -> Self::Iter;
}

/// Interface for types which can bound the number of bytes required to match
/// a particular instance of themselves.
pub trait SizedRead {
    /// Get the size of this object when read directly from something
    /// implementing `std::io::Read`.
    fn read_size(&self) -> ReadSize;
}

/// Trait for use with fixed-size types
pub trait ReadToBuffer {
    /// Read at most `max_count` instances of this atom type from the given
    /// stream, placing them into the supplied buffer.
    fn read_to_buffer<R>(r: &mut R, dest: &mut [u8], max_count: usize) -> io::Result<usize>
        where R: io::Read;
}

/// Possible results from `SizedRead::read_size`
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ReadSize {
    /// Exact size is known in advance
    Exact(usize),

    /// Size can be within the given range of values.
    Range(usize, usize)
}

impl ReadSize {
    /// Get the minimum number of bytes specified by this ReadSize.
    #[inline]
    fn min(&self) -> usize {
        match self {
            &ReadSize::Exact(s) => s,
            &ReadSize::Range(a, _) => a,
        }
    }
    /// Get the maximum number of bytes specified by this ReadSize.
    #[inline]
    fn max(&self) -> usize {
        match self {
            &ReadSize::Exact(s) => s,
            &ReadSize::Range(_, b) => b,
        }
    }

}

impl iter::Sum<ReadSize> for ReadSize {
    #[inline]
    fn sum<I>(iter: I) -> Self where I: Iterator<Item=ReadSize> {
        let mut sum = ReadSize::Exact(0);
        for rs in iter {
            sum += rs;
        }
        sum
    }
}

impl Add<ReadSize> for ReadSize {
    type Output = ReadSize;
    #[inline]
    fn add(self, rhs: ReadSize) -> Self::Output {
        let min = self.min() + rhs.min();
        let max = self.max() + rhs.max();
        if min == max { ReadSize::Exact(min) }
        else { ReadSize::Range(min, max) }
    }
}

impl AddAssign<ReadSize> for ReadSize {
    #[inline]
    fn add_assign(&mut self, rhs: ReadSize) {
        *self = *self + rhs;
    }
}

impl From<usize> for ReadSize {
    #[inline]
    fn from(s: usize) -> Self {
        ReadSize::Exact(s)
    }
}

impl From<Range<usize>> for ReadSize {
    #[inline]
    fn from(r: Range<usize>) -> Self {
        if r.len() > 1 { ReadSize::Range(r.start, r.end - 1) }
        else { ReadSize::Exact(r.start) }
    }
}

// ----------------------------------------------------------------
// Implementations

impl<'a> Bytes<'a> for u8 {
    type Iter = iter::Once<u8>;
    fn bytes(&self) -> Self::Iter {
        iter::once(*self)
    }
}

impl SizedRead for u8 {
    #[inline(always)]
    fn read_size(&self) -> ReadSize {
        ReadSize::Exact(1)
    }
}

impl ReadToBuffer for u8 {
    #[inline(always)]
    fn read_to_buffer<R>(r: &mut R, dest: &mut [u8], max_count: usize) -> io::Result<usize>
        where R: io::Read {
        r.read(&mut dest[0..max_count])
    }
}

// ----------------------------------------------------------------

// This table was copied from the Rust source tree,
// file "src/libcore/str/mod.rs".
// https://tools.ietf.org/html/rfc3629
static UTF8_CHAR_WIDTH: [u8; 256] = [
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
];

impl<'a> Bytes<'a> for char {
    type Iter = char::EncodeUtf8;
    fn bytes(&self) -> Self::Iter {
        self.encode_utf8()
    }
}

impl SizedRead for char {
    #[inline]
    fn read_size(&self) -> ReadSize {
        self.len_utf8().into()
    }
}

impl ReadToBuffer for char {
    #[inline]
    fn read_to_buffer<R>(r: &mut R, dest: &mut [u8], max_count: usize) -> io::Result<usize>
        where R: io::Read {
        let mut i = 0;
        let mut n = 0;
        while i < dest.len() && n < max_count {
            try!(r.read_exact(&mut dest[i..(i+1)]));
            let w = UTF8_CHAR_WIDTH[dest[i] as usize];
            try!(r.read_exact(&mut dest[(i+1)..(i + w as usize)]));
            i += w as usize;
            n += 1;
        }
        Ok(i)
    }

}

#[cfg(test)]
#[test]
fn test_sizedread_char() {
    assert_eq!(ReadSize::Exact(1), 'a'.read_size());
    assert_eq!(ReadSize::Exact(3), '✓'.read_size());
}

#[cfg(test)]
#[test]
fn test_readable_char() {
    let mut buf = ['\0' as u8; 32];
    {
        let mut r = io::repeat('a' as u8);
        assert_eq!(3, char::read_to_buffer(&mut r, &mut buf, 3).unwrap());
        assert_eq!(&['a' as u8 as u8, 'a' as u8, 'a' as u8, '\0' as u8], &buf[0..4]);
    }

    {
        let s = "✓☠⚔";
        let mut r = io::Cursor::new(s.as_bytes());
        assert_eq!(9, char::read_to_buffer(&mut r, &mut buf, 3).unwrap());
        assert_eq!(s.as_bytes(), &buf[0..9]);
    }
}

// ----------------------------------------------------------------

/*impl<T> SizedRead for Transition<T>
    where T: Atom + SizedRead
{
    fn read_size(&self) -> ReadSize {
        match self {
            &Transition::Atom(a) => a.read_size(),
            &Transition::Literal(ref atoms) => atoms.iter().map(|a| a.read_size()).sum(),
            &Transition::Wildcard => ReadSize::Range(1, 4),
            &Transition::Anchor(..) => ReadSize::Exact(0),
            &Transition::Class(ref c) => c.read_size(),
        }
    }
}*/

#[cfg(test)]
#[test]
fn test_sizedread_transition() {
    assert_eq!(ReadSize::Exact(1), 'a'.read_size());
    assert_eq!(ReadSize::Exact(3), '✓'.read_size());
    assert_eq!(ReadSize::Range(1, 3), Class::from_iter(['x', 'y', '⚔'].into_iter().cloned()).read_size());
}

// ----------------------------------------------------------------

impl<T> SizedRead for ClassMember<T>
    where T: Atom + SizedRead
{
    fn read_size(&self) -> ReadSize {
        match self {
            &ClassMember::Atom(a) => a.read_size(),
            &ClassMember::Range(first, last) => {
                // We'll assume that any differences in read size between
                // `first` and `last` follow a linear function between the two;
                // this _assumption_ allows us to avoid evaluating `read_size`
                // for every single member of the range.
                let frs = first.read_size();
                let lrs = last.read_size();
                (cmp::min(frs.min(), lrs.min())..(cmp::max(frs.max(), lrs.max()) + 1)).into()
            }
        }
    }
}

impl<T> SizedRead for Class<T>
    where T: Atom + SizedRead
{
    fn read_size(&self) -> ReadSize {
        let mut min = usize::MAX;
        let mut max = usize::MIN;

        for m in self.iter_members() {
            let rs = m.read_size();
            min = cmp::min(min, rs.min());
            max = cmp::max(max, rs.max());
        }

        (min..(max + 1)).into()
    }
}
<|endoftext|>


<commit_before>use crate::{
    compiler::hir::{self, Expression, HirDb, Lambda},
    discover::{run::Discover, value::Value},
    input::InputReference,
    language_server::utils::LspPositionConversion,
};

#[salsa::query_group(AnalyzeStorage)]
pub trait Analyze: Discover + HirDb + LspPositionConversion {
    fn analyze(&self, input_reference: InputReference) -> Vec<AnalyzerReport>;
}

fn analyze(db: &dyn Analyze, input_reference: InputReference) -> Vec<AnalyzerReport> {
    let (hir, _) = db.hir(input_reference.clone()).unwrap();
    let ids = collect_hir_ids_for_value_hints_list(
        db,
        input_reference.clone(),
        hir.expressions.keys().cloned().collect(),
    );
    ids.into_iter()
        .map(move |id| (id.clone(), db.run(input_reference.clone(), id)))
        .into_iter()
        .filter_map(move |(id, value)| match value {
            Some(Ok(value)) => Some(AnalyzerReport::ValueOfExpression {
                id: id.to_owned(),
                value,
            }),
            Some(Err(value)) => Some(AnalyzerReport::ExpressionPanics {
                id: id.to_owned(),
                value,
            }),
            None => None,
        })
        .collect()
}

fn collect_hir_ids_for_value_hints_list(
    db: &dyn Analyze,
    input_reference: InputReference,
    ids: Vec<hir::Id>,
) -> Vec<hir::Id> {
    ids.into_iter()
        .flat_map(|id| collect_hir_ids_for_value_hints(db, input_reference.clone(), id))
        .collect()
}
fn collect_hir_ids_for_value_hints(
    db: &dyn Analyze,
    input_reference: InputReference,
    id: hir::Id,
) -> Vec<hir::Id> {
    match db
        .find_expression(input_reference.clone(), id.clone())
        .unwrap()
    {
        Expression::Int(_) => vec![],
        Expression::Text(_) => vec![],
        Expression::Reference(_) => {
            vec![id]
        }
        Expression::Symbol(_) => vec![],
        Expression::Lambda(Lambda { body, .. }) => collect_hir_ids_for_value_hints_list(
            db,
            input_reference,
            body.expressions.keys().cloned().collect(),
        ),
        Expression::Body(body) => collect_hir_ids_for_value_hints_list(
            db,
            input_reference,
            body.expressions.keys().cloned().collect(),
        ),
        Expression::Call { arguments, .. } => {
            let mut ids = collect_hir_ids_for_value_hints_list(db, input_reference, arguments);
            ids.push(id.to_owned());
            ids
        }
        Expression::Error => vec![],
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum AnalyzerReport {
    ValueOfExpression {
        id: hir::Id,
        value: Value,
    },
    ExpressionPanics {
        id: hir::Id,
        value: Value,
    },
    FunctionHasError {
        function: hir::Id,
        error_inducing_inputs: Vec<Value>,
    },
}
<commit_msg>refactor(compiler): simplify iter mapping<commit_after>use crate::{
    compiler::hir::{self, Expression, HirDb, Lambda},
    discover::{run::Discover, value::Value},
    input::InputReference,
    language_server::utils::LspPositionConversion,
};

#[salsa::query_group(AnalyzeStorage)]
pub trait Analyze: Discover + HirDb + LspPositionConversion {
    fn analyze(&self, input_reference: InputReference) -> Vec<AnalyzerReport>;
}

fn analyze(db: &dyn Analyze, input_reference: InputReference) -> Vec<AnalyzerReport> {
    let (hir, _) = db.hir(input_reference.clone()).unwrap();
    let ids = collect_hir_ids_for_value_hints_list(
        db,
        input_reference.clone(),
        hir.expressions.keys().cloned().collect(),
    );
    ids.into_iter()
        .map(move |id| (id.clone(), db.run(input_reference.clone(), id)))
        .into_iter()
        .filter_map(|(id, value)| value.map(|it| (id, it)))
        .map(move |(id, value)| match value {
            Ok(value) => AnalyzerReport::ValueOfExpression {
                id: id.to_owned(),
                value,
            },
            Err(value) => AnalyzerReport::ExpressionPanics {
                id: id.to_owned(),
                value,
            },
        })
        .collect()
}

fn collect_hir_ids_for_value_hints_list(
    db: &dyn Analyze,
    input_reference: InputReference,
    ids: Vec<hir::Id>,
) -> Vec<hir::Id> {
    ids.into_iter()
        .flat_map(|id| collect_hir_ids_for_value_hints(db, input_reference.clone(), id))
        .collect()
}
fn collect_hir_ids_for_value_hints(
    db: &dyn Analyze,
    input_reference: InputReference,
    id: hir::Id,
) -> Vec<hir::Id> {
    match db
        .find_expression(input_reference.clone(), id.clone())
        .unwrap()
    {
        Expression::Int(_) => vec![],
        Expression::Text(_) => vec![],
        Expression::Reference(_) => {
            vec![id]
        }
        Expression::Symbol(_) => vec![],
        Expression::Lambda(Lambda { body, .. }) => collect_hir_ids_for_value_hints_list(
            db,
            input_reference,
            body.expressions.keys().cloned().collect(),
        ),
        Expression::Body(body) => collect_hir_ids_for_value_hints_list(
            db,
            input_reference,
            body.expressions.keys().cloned().collect(),
        ),
        Expression::Call { arguments, .. } => {
            let mut ids = collect_hir_ids_for_value_hints_list(db, input_reference, arguments);
            ids.push(id.to_owned());
            ids
        }
        Expression::Error => vec![],
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum AnalyzerReport {
    ValueOfExpression {
        id: hir::Id,
        value: Value,
    },
    ExpressionPanics {
        id: hir::Id,
        value: Value,
    },
    FunctionHasError {
        function: hir::Id,
        error_inducing_inputs: Vec<Value>,
    },
}
<|endoftext|>


<commit_before>extern crate tokio_core;
extern crate env_logger;
extern crate futures;

use std::io;
use std::net::Shutdown;

use futures::{Future, Stream, Sink};
use tokio_core::io::{write_all, read, Encode, Decode, EasyBuf, Io};
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::Core;

pub struct Line(EasyBuf);

impl Decode for Line {
    fn decode(buf: &mut EasyBuf) -> Result<Option<Line>, io::Error> {
        match buf.as_slice().iter().position(|&b| b == b'\n') {
            Some(i) => Ok(Some(Line(buf.drain_to(i + 1).into()))),
            None => Ok(None),
        }
    }

    fn done(buf: &mut EasyBuf) -> io::Result<Line> {
        let amt = buf.len();
        Ok(Line(buf.drain_to(amt)))
    }
}

impl Encode for Line {
    fn encode(self, into: &mut Vec<u8>) {
        into.extend_from_slice(self.0.as_slice());
    }
}

#[test]
fn echo() {
    drop(env_logger::init());

    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &handle).unwrap();
    let addr = listener.local_addr().unwrap();
    let srv = listener.incoming().for_each(move |(socket, _)| {
        let (stream, sink) = socket.framed::<Line, Line>().split();
        handle.spawn(sink.send_all(stream).map(|_| ()).map_err(|_| ()));
        Ok(())
    });

    let handle = core.handle();
    handle.spawn(srv.map_err(|e| panic!("srv error: {}", e)));

    let client = TcpStream::connect(&addr, &handle);
    let client = core.run(client).unwrap();
    let (client, _) = core.run(write_all(client, b"a\n")).unwrap();
    let (client, buf, amt) = core.run(read(client, vec![0; 1024])).unwrap();
    assert_eq!(amt, 2);
    assert_eq!(&buf[..2], b"a\n");

    let (client, _) = core.run(write_all(client, b"\nb\n")).unwrap();
    let (client, buf, amt) = core.run(read(client, buf)).unwrap();
    assert_eq!(amt, 3);
    assert_eq!(&buf[..3], b"\nb\n");

    let (client, _) = core.run(write_all(client, b"b")).unwrap();
    client.shutdown(Shutdown::Write).unwrap();
    let (_client, buf, amt) = core.run(read(client, buf)).unwrap();
    assert_eq!(amt, 1);
    assert_eq!(&buf[..1], b"b");
}
<commit_msg>Fix line-frames test<commit_after>extern crate tokio_core;
extern crate env_logger;
extern crate futures;

use std::io;
use std::net::Shutdown;

use futures::{Future, Stream, Sink};
use tokio_core::io::{write_all, read, Encode, Decode, EasyBuf, Io};
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::Core;

pub struct Line(EasyBuf);

impl Decode for Line {
    fn decode(buf: &mut EasyBuf) -> Result<Option<Line>, io::Error> {
        match buf.as_slice().iter().position(|&b| b == b'\n') {
            Some(i) => Ok(Some(Line(buf.drain_to(i + 1).into()))),
            None => Ok(None),
        }
    }

    fn done(buf: &mut EasyBuf) -> io::Result<Line> {
        let amt = buf.len();
        Ok(Line(buf.drain_to(amt)))
    }
}

impl Encode for Line {
    fn encode(self, into: &mut Vec<u8>) {
        into.extend_from_slice(self.0.as_slice());
    }
}

#[test]
fn echo() {
    drop(env_logger::init());

    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &handle).unwrap();
    let addr = listener.local_addr().unwrap();
    let srv = listener.incoming().for_each(move |(socket, _)| {
        let (stream, sink) = socket.framed::<Line, Line>().split();
        handle.spawn(sink.send_all(stream).map(|_| ()).map_err(|_| ()));
        Ok(())
    });

    let handle = core.handle();
    handle.spawn(srv.map_err(|e| panic!("srv error: {}", e)));

    let client = TcpStream::connect(&addr, &handle);
    let client = core.run(client).unwrap();
    let (client, _) = core.run(write_all(client, b"a\n")).unwrap();
    let (client, buf, amt) = core.run(read(client, vec![0; 1024])).unwrap();
    assert_eq!(amt, 2);
    assert_eq!(&buf[..2], b"a\n");

    let (client, _) = core.run(write_all(client, b"\n")).unwrap();
    let (client, buf, amt) = core.run(read(client, buf)).unwrap();
    assert_eq!(amt, 1);
    assert_eq!(&buf[..1], b"\n");

    let (client, _) = core.run(write_all(client, b"b")).unwrap();
    client.shutdown(Shutdown::Write).unwrap();
    let (_client, buf, amt) = core.run(read(client, buf)).unwrap();
    assert_eq!(amt, 1);
    assert_eq!(&buf[..1], b"b");
}
<|endoftext|>


<commit_before>use std::io::File;
use std::mem;
use std::os;

// https://stackoverflow.com/questions/24633784/is-there-a-gzip-library-available-for-rust

fn pointer_size() -> uint {
    let tmp = 0u8;
    let boxed = box tmp;
    mem::size_of_val(&boxed)
}

fn main() {
    let args = os::args();
    if args.len() != 2 {
        println!("usage: read_blend_file /path/to/file.blend");
    } else {
        let slice: &str = args[1].as_slice();
        println!("open \"{}\" ...", slice);
        {
            // read 12 bytes from the Blender file
            let path = Path::new(slice);
            let display = path.display();
            let mut file = match File::open(&path) {
                Err(why) => fail!("couldn't open {}: {}", display, why.desc),
                Ok(file) => file,
            };
            let mut buf = [0u8, ..12];
            match file.read(buf) {
                Err(why) => println!("{}", why),
                Ok(_) => (),
            }
            // pack those 12 bytes into a string ...
            let mut header = String::new();
            for n in range(0u, 12) {
                header.push(buf[n] as char);
            }
            // ... to be able to compare them
            let slice = header.as_slice();
            let blender: &str = slice.slice(0, 7); // first 7 chars
            if blender == "BLENDER" {
                println!("INFO: a Blender file.");
                let ptr_size = pointer_size();
                println!("size of pointer in bytes: {}", ptr_size);
                println!("Header (12 chars): {}", header);
                let mut ptr_size_differs = false;
                if buf[7] as char == '_' {
                    // 32-bit pointers expected
                    if ptr_size != 4 {
                        ptr_size_differs = true;
                    }
                    println!("32-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                } else {
                    // 64-bit pointers expected
                    if ptr_size != 8 {
                        ptr_size_differs = true;
                    }
                    println!("64-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                }
                // little endian or big endian?
                let mut l_endian;
                let endian_test = 1u16; // value one stored in two bytes
                if endian_test.to_le() != endian_test {
                    l_endian = false;
                } else {
                    // if we are on a little endian machine to_le() is a no-op
                    l_endian = true;
                }
                println!("l_endian = {}", l_endian);
                let mut switch_endian;
                if buf[8] as char == 'v' {
                    // file stores little endian
                    if !l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                } else {
                    // file stores big endian
                    if l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                }
                println!("switch_endian = {}", switch_endian);
                // get the version number
                let mut num = String::new();
                for n in range(9u, 12) {
                    num.push(buf[n] as char);
                }
                println!("num = {}", num);
                // assumes 64-bit pointers (in file as well as on platform)
                loop {
                    // read_file_dna
                    let mut bhead8 = [0u8, ..24]; // 4 * int + 64-bit pointer
                    match file.read(bhead8) {
                        Err(why) => println!("{}", why),
                        Ok(_) => (),
                    }
                    // pack those 24 bytes into a string ...
                    let mut bhead = String::new();
                    for n in range(0u, 24) {
                        bhead.push(bhead8[n] as char);
                    }
                    let slice = bhead.as_slice();
                    let code: &str = slice.slice(0, 4); // first 4 chars
                    println!("code = {}", code);
                    let mut len: uint = 0;
                    len += (bhead8[4] as uint) <<  0;
                    len += (bhead8[5] as uint) <<  8;
                    len += (bhead8[6] as uint) << 16;
                    len += (bhead8[7] as uint) << 24;
                    println!("len = {}", len);
                    if code == "ENDB" && len == 0 {
                        break;
                    } else if code == "DNA1" {
                        // DNA_sdna_from_data
                        let mut counter = 0u;
                        // SDNA
                        let mut char4 = [0u8, ..4];
                        match file.read(char4) {
                            Err(why) => println!("{}", why),
                            Ok(_) => (),
                        }
                        counter += 4;
                        let mut str4 = String::new();
                        for n in range(0u, 4) {
                            str4.push(char4[n] as char);
                        }
                        let slice = str4.as_slice();
                        let code: &str = slice.slice(0, 4);
                        println!("  code = {}", code);
                        if code == "SDNA" {
                            // NAME
                            let mut char4 = [0u8, ..4];
                            match file.read(char4) {
                                Err(why) => println!("{}", why),
                                Ok(_) => (),
                            }
                            counter += 4;
                            let mut str4 = String::new();
                            for n in range(0u, 4) {
                                str4.push(char4[n] as char);
                            }
                            let slice = str4.as_slice();
                            let code: &str = slice.slice(0, 4);
                            println!("  code = {}", code);
                            if code == "NAME" {
                                // nr_names
                                let io_result = file.read_le_u32();
                                let nr_names: u32 = io_result.unwrap();
                                counter += 4;
                                println!("  nr_names = {}", nr_names);
                            }
                        }
                        // read remaining stuff
                        file.read_exact(len - counter);
                    } else {
                        file.read_exact(len);
                    }
                }
            } else {
                println!("ERROR: not a Blender file.");
            }
        } // file gets closed here
    }
}
<commit_msg>Avoid '#[warn(unused_must_use)]' compiler message.<commit_after>use std::io::File;
use std::mem;
use std::os;

// https://stackoverflow.com/questions/24633784/is-there-a-gzip-library-available-for-rust

fn pointer_size() -> uint {
    let tmp = 0u8;
    let boxed = box tmp;
    mem::size_of_val(&boxed)
}

fn main() {
    let args = os::args();
    if args.len() != 2 {
        println!("usage: read_blend_file /path/to/file.blend");
    } else {
        let slice: &str = args[1].as_slice();
        println!("open \"{}\" ...", slice);
        {
            // read 12 bytes from the Blender file
            let path = Path::new(slice);
            let display = path.display();
            let mut file = match File::open(&path) {
                Err(why) => fail!("couldn't open {}: {}", display, why.desc),
                Ok(file) => file,
            };
            let mut buf = [0u8, ..12];
            match file.read(buf) {
                Err(why) => println!("{}", why),
                Ok(_) => (),
            }
            // pack those 12 bytes into a string ...
            let mut header = String::new();
            for n in range(0u, 12) {
                header.push(buf[n] as char);
            }
            // ... to be able to compare them
            let slice = header.as_slice();
            let blender: &str = slice.slice(0, 7); // first 7 chars
            if blender == "BLENDER" {
                println!("INFO: a Blender file.");
                let ptr_size = pointer_size();
                println!("size of pointer in bytes: {}", ptr_size);
                println!("Header (12 chars): {}", header);
                let mut ptr_size_differs = false;
                if buf[7] as char == '_' {
                    // 32-bit pointers expected
                    if ptr_size != 4 {
                        ptr_size_differs = true;
                    }
                    println!("32-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                } else {
                    // 64-bit pointers expected
                    if ptr_size != 8 {
                        ptr_size_differs = true;
                    }
                    println!("64-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                }
                // little endian or big endian?
                let mut l_endian;
                let endian_test = 1u16; // value one stored in two bytes
                if endian_test.to_le() != endian_test {
                    l_endian = false;
                } else {
                    // if we are on a little endian machine to_le() is a no-op
                    l_endian = true;
                }
                println!("l_endian = {}", l_endian);
                let mut switch_endian;
                if buf[8] as char == 'v' {
                    // file stores little endian
                    if !l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                } else {
                    // file stores big endian
                    if l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                }
                println!("switch_endian = {}", switch_endian);
                // get the version number
                let mut num = String::new();
                for n in range(9u, 12) {
                    num.push(buf[n] as char);
                }
                println!("num = {}", num);
                // assumes 64-bit pointers (in file as well as on platform)
                loop {
                    // read_file_dna
                    let mut bhead8 = [0u8, ..24]; // 4 * int + 64-bit pointer
                    match file.read(bhead8) {
                        Err(why) => println!("{}", why),
                        Ok(_) => (),
                    }
                    // pack those 24 bytes into a string ...
                    let mut bhead = String::new();
                    for n in range(0u, 24) {
                        bhead.push(bhead8[n] as char);
                    }
                    let slice = bhead.as_slice();
                    let code: &str = slice.slice(0, 4); // first 4 chars
                    println!("code = {}", code);
                    let mut len: uint = 0;
                    len += (bhead8[4] as uint) <<  0;
                    len += (bhead8[5] as uint) <<  8;
                    len += (bhead8[6] as uint) << 16;
                    len += (bhead8[7] as uint) << 24;
                    println!("len = {}", len);
                    if code == "ENDB" && len == 0 {
                        break;
                    } else if code == "DNA1" {
                        // DNA_sdna_from_data
                        let mut counter = 0u;
                        // SDNA
                        let mut char4 = [0u8, ..4];
                        match file.read(char4) {
                            Err(why) => println!("{}", why),
                            Ok(_) => (),
                        }
                        counter += 4;
                        let mut str4 = String::new();
                        for n in range(0u, 4) {
                            str4.push(char4[n] as char);
                        }
                        let slice = str4.as_slice();
                        let code: &str = slice.slice(0, 4);
                        println!("  code = {}", code);
                        if code == "SDNA" {
                            // NAME
                            let mut char4 = [0u8, ..4];
                            match file.read(char4) {
                                Err(why) => println!("{}", why),
                                Ok(_) => (),
                            }
                            counter += 4;
                            let mut str4 = String::new();
                            for n in range(0u, 4) {
                                str4.push(char4[n] as char);
                            }
                            let slice = str4.as_slice();
                            let code: &str = slice.slice(0, 4);
                            println!("  code = {}", code);
                            if code == "NAME" {
                                // nr_names
                                let io_result = file.read_le_u32();
                                let nr_names: u32 = io_result.unwrap();
                                counter += 4;
                                println!("  nr_names = {}", nr_names);
                            }
                        }
                        // read remaining stuff
                        let _dummy = file.read_exact(len - counter);
                    } else {
                        let _dummy = file.read_exact(len);
                    }
                }
            } else {
                println!("ERROR: not a Blender file.");
            }
        } // file gets closed here
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Sean Patrick Santos
//
// Licensed under the Apache License, Version 2.0.
// License available at:
// http://www.apache.org/licenses/LICENSE-2.0
//
// Copyright and license information should be present in the top
// level directory of this package.

#![crate_type="bin"]

extern crate probe_c_api;

use std::default::Default;

use probe_c_api::Probe;

#[test]
fn try_compile_pass() {
    let probe = <Probe>::default();
    assert!(probe.try_compile("int main() { return 0; }".as_bytes()).unwrap()
                 .status.success());
}

#[test]
fn try_compile_fail() {
    let probe = <Probe>::default();
    assert!(!probe.try_compile("ain't it a C progarm, bub!".as_bytes()).unwrap()
                  .status.success());
}
<commit_msg>Remove `crate_type` attribute from test file.<commit_after>// Copyright 2015 Sean Patrick Santos
//
// Licensed under the Apache License, Version 2.0.
// License available at:
// http://www.apache.org/licenses/LICENSE-2.0
//
// Copyright and license information should be present in the top
// level directory of this package.

extern crate probe_c_api;

use std::default::Default;

use probe_c_api::Probe;

#[test]
fn try_compile_pass() {
    let probe = <Probe>::default();
    assert!(probe.try_compile("int main() { return 0; }".as_bytes()).unwrap()
                 .status.success());
}

#[test]
fn try_compile_fail() {
    let probe = <Probe>::default();
    assert!(!probe.try_compile("ain't it a C progarm, bub!".as_bytes()).unwrap()
                  .status.success());
}
<|endoftext|>