<commit_before>use document;
use document::{YamlNode, YamlNodeData};
use ffi::{YamlErrorType, YamlScalarStyle};
use error::{YamlMark, YamlError, YamlErrorContext};

use std::num::FromStrRadix;
use std::f64;
use std::char;
use regex::Regex;

pub trait YamlConstructor<T, E> {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<T, E>;
    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<T, E>;
    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<T, E>;

    fn construct<'r>(&self, node: document::YamlNode<'r>) -> Result<T, E> {
        match node {
            YamlNode::YamlScalarNode(scalar) => self.construct_scalar(scalar),
            YamlNode::YamlSequenceNode(sequence) => self.construct_sequence(sequence),
            YamlNode::YamlMappingNode(mapping) => self.construct_mapping(mapping)
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum YamlStandardData {
    YamlInteger(isize),
    YamlFloat(f64),
    YamlString(String),
    YamlNull,
    YamlBool(bool),
    YamlSequence(Vec<YamlStandardData>),
    YamlMapping(Vec<(YamlStandardData, YamlStandardData)>),
}

#[derive(Copy)]
pub struct YamlStandardConstructor;

fn standard_error(message: String, mark: &YamlMark) -> YamlError {
    let context = YamlErrorContext {
        byte_offset: mark.index,
        problem_mark: *mark,
        context: None,
        context_mark: *mark,
    };

    YamlError {
        kind: YamlErrorType::YAML_PARSER_ERROR,
        problem: Some(message),
        io_error: None,
        context: Some(context)
    }
}

impl YamlStandardConstructor {
    pub fn new() -> YamlStandardConstructor {
        YamlStandardConstructor
    }

    fn parse_double_quoted(value: &str, mark: &YamlMark) -> Result<String, YamlError> {
        let mut buf = String::new();
        let mut it = value.chars();

        loop {
            match it.next() {
                None => return Ok(buf),
                Some('\\') => {
                    // escape sequences
                    match it.next() {
                        None => return Err(standard_error(
                                    "unexpected end of string after escape".to_string(),
                                    mark
                                )),
                        Some('0') => buf.push('\x00'),              // null
                        Some('a') => buf.push('\x07'),              // ASCII bell
                        Some('b') => buf.push('\x08'),              // backspace
                        Some('t') | Some('\t') => buf.push('\t'),   // horizontal tab
                        Some('n') => buf.push('\n'),                // linefeed
                        Some('v') => buf.push('\x0b'),              // vertical tab
                        Some('f') => buf.push('\x0c'),              // form feed
                        Some('r') => buf.push('\x0d'),              // carriage return
                        Some('e') => buf.push('\x1b'),              // ASCII escape
                        Some('N') => buf.push('\u{85}'),            // unicode next line
                        Some('_') => buf.push('\u{a0}'),            // unicode non-breaking space
                        Some('L') => buf.push('\u{2028}'),          // unicode line separator
                        Some('P') => buf.push('\u{2029}'),          // unicode paragraph separator
                        Some('x') => {
                            let code:String = it.take(2).collect();
                            match parse_escape_sequence(code.as_slice(), 2) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some('u') => {
                            let code:String = it.take(4).collect();
                            match parse_escape_sequence(code.as_slice(), 4) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some('U') => {
                            let code:String = it.take(8).collect();
                            match parse_escape_sequence(code.as_slice(), 8) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some(c) => buf.push(c)
                    }
                },
                Some(c) => buf.push(c)
            }
        }
    }
}

fn parse_escape_sequence(rep: &str, expected_len: usize) -> Option<char> {
    match FromStrRadix::from_str_radix(rep, 16) {
        Some(code) if rep.len() == expected_len => char::from_u32(code),
        _ => None
    }
}

fn parse_int(sign: &str, data: &str, radix: usize) -> isize {
    let sign_flag = if sign == "-" {
            -1
        } else {
            1
        };

    let filtered:String = data.chars().filter(|&c| c != '_').collect();
    let unsigned:isize = FromStrRadix::from_str_radix(filtered.as_slice(), radix).unwrap();
    return unsigned * sign_flag;
}

fn parse_float(sign: &str, data: &str) -> f64 {
    let unsigned:f64 = data.parse().unwrap();
    if sign == "-" {
        return -unsigned;
    } else {
        return unsigned;
    }
}

static DEC_INT:Regex = regex!(r"^[-+]?(0|[1-9][0-9_]*)$");
static OCT_INT:Regex = regex!(r"^([-+]?)0o?([0-7_]+)$");
static HEX_INT:Regex = regex!(r"^([-+]?)0x([0-9a-fA-F_]+)$");
static BIN_INT:Regex = regex!(r"^([-+]?)0b([0-1_]+)$");
static FLOAT_PATTERN:Regex = regex!(r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$");
static POS_INF:Regex = regex!(r"^[+]?(\.inf|\.Inf|\.INF)$");
static NEG_INF:Regex = regex!(r"^-(\.inf|\.Inf|\.INF)$");
static NAN_PATTERN:Regex = regex!(r"^(\.nan|\.NaN|\.NAN)$");
static NULL_PATTERN:Regex = regex!(r"^(null|Null|NULL|~)$");
static TRUE_PATTERN:Regex = regex!(r"^(true|True|TRUE|yes|Yes|YES)$");
static FALSE_PATTERN:Regex = regex!(r"^(false|False|FALSE|no|No|NO)$");

impl YamlConstructor<YamlStandardData, YamlError> for YamlStandardConstructor {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<YamlStandardData, YamlError> {
        let value = scalar.get_value();
        let mark = scalar.start_mark();

        match scalar.style() {
            YamlScalarStyle::YamlPlainScalarStyle => {
                match BIN_INT.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 2))),
                    None => ()
                };
                match OCT_INT.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 8))),
                    None => ()
                };
                match HEX_INT.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 16))),
                    None => ()
                };

                if DEC_INT.is_match(value.as_slice()) {
                    return Ok(YamlStandardData::YamlInteger(parse_int("", value.as_slice(), 10)));
                }

                match FLOAT_PATTERN.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlStandardData::YamlFloat(parse_float(
                                caps.at(1).unwrap(), caps.at(2).unwrap()))),
                    None => ()
                };

                if POS_INF.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlFloat(f64::INFINITY))
                } else if NEG_INF.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlFloat(f64::NEG_INFINITY))
                } else if NAN_PATTERN.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlFloat(f64::NAN))
                } else if NULL_PATTERN.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlNull)
                } else if TRUE_PATTERN.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlBool(true))
                } else if FALSE_PATTERN.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlBool(false))
                } else {
                    Ok(YamlStandardData::YamlString(value))
                }
            },
            YamlScalarStyle::YamlDoubleQuotedScalarStyle => {
                YamlStandardConstructor::parse_double_quoted(value.as_slice(), &mark).map(YamlStandardData::YamlString)
            },
            _ => {
                Ok(YamlStandardData::YamlString(value))
            }
        }
    }

    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<YamlStandardData, YamlError> {
        let res:Result<Vec<YamlStandardData>, YamlError> = sequence.values().map(|node| { self.construct(node) }).collect();
        res.map(|list| YamlStandardData::YamlSequence(list))
    }

    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<YamlStandardData, YamlError> {
        let pairs = mapping.pairs().map(|(key_node, value_node)| {
            match self.construct(key_node) {
                Ok(key) => match self.construct(value_node) {
                    Ok(value) => Ok((key, value)),
                    Err(e) => Err(e)
                },
                Err(e) => Err(e)
            }
        });
        let res:Result<Vec<(YamlStandardData, YamlStandardData)>, YamlError> = pairs.collect();
        res.map(YamlStandardData::YamlMapping)
    }
}

#[cfg(test)]
mod test {
    use super::YamlStandardData::*;
    use parser::{YamlParser, YamlByteParser};
    use std::f64;
    use std::num::Float;
    use ffi::YamlEncoding::YamlUtf8Encoding;
    use constructor::{YamlConstructor, YamlStandardConstructor};

    #[test]
    fn test_standard_constructor() {
        let data = "[1, 2, 3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1), YamlInteger(2), YamlInteger(3)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_integer_parser() {
        let data = "[0o10, 0x21, -30]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(0o10), YamlInteger(0x21), YamlInteger(-30)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_float_parser() {
        let data = "[0.3, -.4, 1e+2, -1.2e-3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                let value = ctor.construct(doc.root().unwrap());
                match value {
                    Ok(YamlSequence(seq)) => {
                        match seq.as_slice() {
                            [YamlFloat(f1), YamlFloat(f2), YamlFloat(f3), YamlFloat(f4)] => {
                                assert!(f1.abs_sub(0.3) < 1.0e-6);
                                assert!(f2.abs_sub(-0.4) < 1.0e-6);
                                assert!(f3.abs_sub(1e+2) < 1.0e-6);
                                assert!(f4.abs_sub(-1.2e-3) < 1.0e-6);
                            },
                            _ => panic!("unexpected sequence")
                        }
                    },
                    _ => panic!("unexpected result")
                }
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_inf_parser() {
        let data = "[.inf, -.INF]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlFloat(f64::INFINITY), YamlFloat(f64::NEG_INFINITY)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_misc_parser() {
        let data = "[yes, False, ~]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlBool(true), YamlBool(false), YamlNull])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_double_quoted_parser() {
        let data = r#""hello, \"world\"""#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString("hello, \"world\"".to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_single_quoted_parser() {
        let data = r#"'here''s to "quotes"'"#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString(r#"here's to "quotes""#.to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_underlined_integer() {
        let data = "[1_000, -2_000_000]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1000), YamlInteger(-2000000)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_negative_radix() {
        let data = "[-0x30, -0700, -0b110]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(-48), YamlInteger(-448), YamlInteger(-6)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }
}
<commit_msg>Rust changes: Iterator::take now moves the iterator<commit_after>use document;
use document::{YamlNode, YamlNodeData};
use ffi::{YamlErrorType, YamlScalarStyle};
use error::{YamlMark, YamlError, YamlErrorContext};

use std::num::FromStrRadix;
use std::f64;
use std::char;
use regex::Regex;

pub trait YamlConstructor<T, E> {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<T, E>;
    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<T, E>;
    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<T, E>;

    fn construct<'r>(&self, node: document::YamlNode<'r>) -> Result<T, E> {
        match node {
            YamlNode::YamlScalarNode(scalar) => self.construct_scalar(scalar),
            YamlNode::YamlSequenceNode(sequence) => self.construct_sequence(sequence),
            YamlNode::YamlMappingNode(mapping) => self.construct_mapping(mapping)
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum YamlStandardData {
    YamlInteger(isize),
    YamlFloat(f64),
    YamlString(String),
    YamlNull,
    YamlBool(bool),
    YamlSequence(Vec<YamlStandardData>),
    YamlMapping(Vec<(YamlStandardData, YamlStandardData)>),
}

#[derive(Copy)]
pub struct YamlStandardConstructor;

fn standard_error(message: String, mark: &YamlMark) -> YamlError {
    let context = YamlErrorContext {
        byte_offset: mark.index,
        problem_mark: *mark,
        context: None,
        context_mark: *mark,
    };

    YamlError {
        kind: YamlErrorType::YAML_PARSER_ERROR,
        problem: Some(message),
        io_error: None,
        context: Some(context)
    }
}

fn take(iter: &mut Iterator<Item=char>, n: usize) -> String
{
    let mut s = String::new();
    for _ in 0..n {
        match iter.next() {
            Some(c) => s.push(c),
            None => break
        }
    }
    return s;
}

impl YamlStandardConstructor {
    pub fn new() -> YamlStandardConstructor {
        YamlStandardConstructor
    }

    fn parse_double_quoted(value: &str, mark: &YamlMark) -> Result<String, YamlError> {
        let mut buf = String::new();
        let mut it = value.chars();

        loop {
            match it.next() {
                None => return Ok(buf),
                Some('\\') => {
                    // escape sequences
                    match it.next() {
                        None => return Err(standard_error(
                                    "unexpected end of string after escape".to_string(),
                                    mark
                                )),
                        Some('0') => buf.push('\x00'),              // null
                        Some('a') => buf.push('\x07'),              // ASCII bell
                        Some('b') => buf.push('\x08'),              // backspace
                        Some('t') | Some('\t') => buf.push('\t'),   // horizontal tab
                        Some('n') => buf.push('\n'),                // linefeed
                        Some('v') => buf.push('\x0b'),              // vertical tab
                        Some('f') => buf.push('\x0c'),              // form feed
                        Some('r') => buf.push('\x0d'),              // carriage return
                        Some('e') => buf.push('\x1b'),              // ASCII escape
                        Some('N') => buf.push('\u{85}'),            // unicode next line
                        Some('_') => buf.push('\u{a0}'),            // unicode non-breaking space
                        Some('L') => buf.push('\u{2028}'),          // unicode line separator
                        Some('P') => buf.push('\u{2029}'),          // unicode paragraph separator
                        Some('x') => {
                            let code:String = take(&mut it, 2);
                            match parse_escape_sequence(code.as_slice(), 2) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some('u') => {
                            let code:String = take(&mut it, 4);
                            match parse_escape_sequence(code.as_slice(), 4) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some('U') => {
                            let code:String = take(&mut it, 8);
                            match parse_escape_sequence(code.as_slice(), 8) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some(c) => buf.push(c)
                    }
                },
                Some(c) => buf.push(c)
            }
        }
    }
}

fn parse_escape_sequence(rep: &str, expected_len: usize) -> Option<char> {
    match FromStrRadix::from_str_radix(rep, 16) {
        Some(code) if rep.len() == expected_len => char::from_u32(code),
        _ => None
    }
}

fn parse_int(sign: &str, data: &str, radix: usize) -> isize {
    let sign_flag = if sign == "-" {
            -1
        } else {
            1
        };

    let filtered:String = data.chars().filter(|&c| c != '_').collect();
    let unsigned:isize = FromStrRadix::from_str_radix(filtered.as_slice(), radix).unwrap();
    return unsigned * sign_flag;
}

fn parse_float(sign: &str, data: &str) -> f64 {
    let unsigned:f64 = data.parse().unwrap();
    if sign == "-" {
        return -unsigned;
    } else {
        return unsigned;
    }
}

static DEC_INT:Regex = regex!(r"^[-+]?(0|[1-9][0-9_]*)$");
static OCT_INT:Regex = regex!(r"^([-+]?)0o?([0-7_]+)$");
static HEX_INT:Regex = regex!(r"^([-+]?)0x([0-9a-fA-F_]+)$");
static BIN_INT:Regex = regex!(r"^([-+]?)0b([0-1_]+)$");
static FLOAT_PATTERN:Regex = regex!(r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$");
static POS_INF:Regex = regex!(r"^[+]?(\.inf|\.Inf|\.INF)$");
static NEG_INF:Regex = regex!(r"^-(\.inf|\.Inf|\.INF)$");
static NAN_PATTERN:Regex = regex!(r"^(\.nan|\.NaN|\.NAN)$");
static NULL_PATTERN:Regex = regex!(r"^(null|Null|NULL|~)$");
static TRUE_PATTERN:Regex = regex!(r"^(true|True|TRUE|yes|Yes|YES)$");
static FALSE_PATTERN:Regex = regex!(r"^(false|False|FALSE|no|No|NO)$");

impl YamlConstructor<YamlStandardData, YamlError> for YamlStandardConstructor {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<YamlStandardData, YamlError> {
        let value = scalar.get_value();
        let mark = scalar.start_mark();

        match scalar.style() {
            YamlScalarStyle::YamlPlainScalarStyle => {
                match BIN_INT.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 2))),
                    None => ()
                };
                match OCT_INT.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 8))),
                    None => ()
                };
                match HEX_INT.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 16))),
                    None => ()
                };

                if DEC_INT.is_match(value.as_slice()) {
                    return Ok(YamlStandardData::YamlInteger(parse_int("", value.as_slice(), 10)));
                }

                match FLOAT_PATTERN.captures(value.as_slice()) {
                    Some(caps) => return Ok(YamlStandardData::YamlFloat(parse_float(
                                caps.at(1).unwrap(), caps.at(2).unwrap()))),
                    None => ()
                };

                if POS_INF.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlFloat(f64::INFINITY))
                } else if NEG_INF.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlFloat(f64::NEG_INFINITY))
                } else if NAN_PATTERN.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlFloat(f64::NAN))
                } else if NULL_PATTERN.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlNull)
                } else if TRUE_PATTERN.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlBool(true))
                } else if FALSE_PATTERN.is_match(value.as_slice()) {
                    Ok(YamlStandardData::YamlBool(false))
                } else {
                    Ok(YamlStandardData::YamlString(value))
                }
            },
            YamlScalarStyle::YamlDoubleQuotedScalarStyle => {
                YamlStandardConstructor::parse_double_quoted(value.as_slice(), &mark).map(YamlStandardData::YamlString)
            },
            _ => {
                Ok(YamlStandardData::YamlString(value))
            }
        }
    }

    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<YamlStandardData, YamlError> {
        let res:Result<Vec<YamlStandardData>, YamlError> = sequence.values().map(|node| { self.construct(node) }).collect();
        res.map(|list| YamlStandardData::YamlSequence(list))
    }

    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<YamlStandardData, YamlError> {
        let pairs = mapping.pairs().map(|(key_node, value_node)| {
            match self.construct(key_node) {
                Ok(key) => match self.construct(value_node) {
                    Ok(value) => Ok((key, value)),
                    Err(e) => Err(e)
                },
                Err(e) => Err(e)
            }
        });
        let res:Result<Vec<(YamlStandardData, YamlStandardData)>, YamlError> = pairs.collect();
        res.map(YamlStandardData::YamlMapping)
    }
}

#[cfg(test)]
mod test {
    use super::YamlStandardData::*;
    use parser::{YamlParser, YamlByteParser};
    use std::f64;
    use std::num::Float;
    use ffi::YamlEncoding::YamlUtf8Encoding;
    use constructor::{YamlConstructor, YamlStandardConstructor};

    #[test]
    fn test_standard_constructor() {
        let data = "[1, 2, 3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1), YamlInteger(2), YamlInteger(3)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_integer_parser() {
        let data = "[0o10, 0x21, -30]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(0o10), YamlInteger(0x21), YamlInteger(-30)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_float_parser() {
        let data = "[0.3, -.4, 1e+2, -1.2e-3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                let value = ctor.construct(doc.root().unwrap());
                match value {
                    Ok(YamlSequence(seq)) => {
                        match seq.as_slice() {
                            [YamlFloat(f1), YamlFloat(f2), YamlFloat(f3), YamlFloat(f4)] => {
                                assert!(f1.abs_sub(0.3) < 1.0e-6);
                                assert!(f2.abs_sub(-0.4) < 1.0e-6);
                                assert!(f3.abs_sub(1e+2) < 1.0e-6);
                                assert!(f4.abs_sub(-1.2e-3) < 1.0e-6);
                            },
                            _ => panic!("unexpected sequence")
                        }
                    },
                    _ => panic!("unexpected result")
                }
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_inf_parser() {
        let data = "[.inf, -.INF]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlFloat(f64::INFINITY), YamlFloat(f64::NEG_INFINITY)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_misc_parser() {
        let data = "[yes, False, ~]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlBool(true), YamlBool(false), YamlNull])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_double_quoted_parser() {
        let data = r#""hello, \"world\"""#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString("hello, \"world\"".to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_single_quoted_parser() {
        let data = r#"'here''s to "quotes"'"#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString(r#"here's to "quotes""#.to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_underlined_integer() {
        let data = "[1_000, -2_000_000]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1000), YamlInteger(-2000000)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_negative_radix() {
        let data = "[-0x30, -0700, -0b110]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(-48), YamlInteger(-448), YamlInteger(-6)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::fmt;

use log::trace;

use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{source_map::DUMMY_SP, Span};

use crate::*;

/// Details of premature program termination.
pub enum TerminationInfo {
    Exit(i64),
    Abort(Option<String>),
    UnsupportedInIsolation(String),
    ExperimentalUb { msg: String, url: String },
    Deadlock,
}

impl fmt::Display for TerminationInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use TerminationInfo::*;
        match self {
            Exit(code) =>
                write!(f, "the evaluated program completed with exit code {}", code),
            Abort(None) =>
                write!(f, "the evaluated program aborted execution"),
            Abort(Some(msg)) =>
                write!(f, "the evaluated program aborted execution: {}", msg),
            UnsupportedInIsolation(msg) =>
                write!(f, "{}", msg),
            ExperimentalUb { msg, .. } =>
                write!(f, "{}", msg),
            Deadlock =>
                write!(f, "the evaluated program deadlocked"),
        }
    }
}

impl MachineStopType for TerminationInfo {}

/// Miri specific diagnostics
pub enum NonHaltingDiagnostic {
    PoppedPointerTag(Item),
    CreatedCallId(CallId),
    CreatedAlloc(AllocId),
    FreedAlloc(AllocId),
}

/// Emit a custom diagnostic without going through the miri-engine machinery
pub fn report_error<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
    e: InterpErrorInfo<'tcx>,
) -> Option<i64> {
    use InterpError::*;

    let (title, helps) = match &e.kind {
        MachineStop(info) => {
            let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
            use TerminationInfo::*;
            let title = match info {
                Exit(code) => return Some(*code),
                Abort(_) =>
                    "abnormal termination",
                UnsupportedInIsolation(_) =>
                    "unsupported operation",
                ExperimentalUb { .. } =>
                    "Undefined Behavior",
                Deadlock => "deadlock",
            };
            let helps = match info {
                UnsupportedInIsolation(_) =>
                    vec![format!("pass the flag `-Zmiri-disable-isolation` to disable isolation")],
                ExperimentalUb { url, .. } =>
                    vec![
                        format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental"),
                        format!("see {} for further information", url),
                    ],
                _ => vec![],
            };
            (title, helps)
        }
        _ => {
            let title = match e.kind {
                Unsupported(_) =>
                    "unsupported operation",
                UndefinedBehavior(_) =>
                    "Undefined Behavior",
                ResourceExhaustion(_) =>
                    "resource exhaustion",
                _ =>
                    bug!("This error should be impossible in Miri: {}", e),
            };
            let helps = match e.kind {
                Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
                    vec![format!("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`")],
                Unsupported(UnsupportedOpInfo::ReadBytesAsPointer | UnsupportedOpInfo::ThreadLocalStatic(_) | UnsupportedOpInfo::ReadExternStatic(_)) =>
                    panic!("Error should never be raised by Miri: {:?}", e.kind),
                Unsupported(_) =>
                    vec![format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support")],
                UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. }) =>
                    vec![
                        format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior"),
                        format!("but alignment errors can also be false positives, see https://github.com/rust-lang/miri/issues/1074"),
                        format!("you can disable the alignment check with `-Zmiri-disable-alignment-check`, but that could hide true bugs")
                    ],
                UndefinedBehavior(_) =>
                    vec![
                        format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
                        format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
                    ],
                _ => vec![],
            };
            (title, helps)
        }
    };

    e.print_backtrace();
    let msg = e.to_string();
    report_msg(*ecx.tcx, /*error*/true, &format!("{}: {}", title, msg), msg, helps, &ecx.generate_stacktrace());

    // Debug-dump all locals.
    for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
        trace!("-------------------");
        trace!("Frame {}", i);
        trace!("    return: {:?}", frame.return_place.map(|p| *p));
        for (i, local) in frame.locals.iter().enumerate() {
            trace!("    local {}: {:?}", i, local.value);
        }
    }

    // Extra output to help debug specific issues.
    match e.kind {
        UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some(access))) => {
            eprintln!(
                "Uninitialized read occurred at offsets 0x{:x}..0x{:x} into this allocation:",
                access.uninit_ptr.offset.bytes(),
                access.uninit_ptr.offset.bytes() + access.uninit_size.bytes(),
            );
            eprintln!("{:?}", ecx.memory.dump_alloc(access.uninit_ptr.alloc_id));
        }
        _ => {}
    }

    None
}

/// Report an error or note (depending on the `error` argument) with the given stacktrace.
/// Also emits a full stacktrace of the interpreter stack.
fn report_msg<'tcx>(
    tcx: TyCtxt<'tcx>,
    error: bool,
    title: &str,
    span_msg: String,
    mut helps: Vec<String>,
    stacktrace: &[FrameInfo<'tcx>],
) {
    let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
    let mut err = if error {
        tcx.sess.struct_span_err(span, title)
    } else {
        tcx.sess.diagnostic().span_note_diag(span, title)
    };
    // Show main message.
    if span != DUMMY_SP {
        err.span_label(span, span_msg);
    } else {
        // Make sure we show the message even when it is a dummy span.
        err.note(&span_msg);
        err.note("(no span available)");
    }
    // Show help messages.
    if !helps.is_empty() {
        // Add visual separator before backtrace.
        helps.last_mut().unwrap().push_str("\n");
        for help in helps {
            err.help(&help);
        }
    }
    // Add backtrace
    for (idx, frame_info) in stacktrace.iter().enumerate() {
        let is_local = frame_info.instance.def_id().is_local();
        // No span for non-local frames and the first frame (which is the error site).
        if is_local && idx > 0 {
            err.span_note(frame_info.span, &frame_info.to_string());
        } else {
            err.note(&frame_info.to_string());
        }
    }

    err.emit();
}

thread_local! {
    static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
}

/// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available.
/// The diagnostic will be emitted after the current interpreter step is finished.
pub fn register_diagnostic(e: NonHaltingDiagnostic) {
    DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
}

/// Remember enough about the topmost frame so that we can restore the stack
/// after a step was taken.
pub struct TopFrameInfo<'tcx> {
    stack_size: usize,
    instance: Option<ty::Instance<'tcx>>,
    span: Span,
}

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn preprocess_diagnostics(&self) -> TopFrameInfo<'tcx> {
        // Ensure we have no lingering diagnostics.
        DIAGNOSTICS.with(|diagnostics| assert!(diagnostics.borrow().is_empty()));

        let this = self.eval_context_ref();
        if this.active_thread_stack().is_empty() {
            // Diagnostics can happen even with the empty stack (e.g. deallocation of thread-local statics).
            return TopFrameInfo { stack_size: 0, instance: None, span: DUMMY_SP };
        }
        let frame = this.frame();

        TopFrameInfo {
            stack_size: this.active_thread_stack().len(),
            instance: Some(frame.instance),
            span: frame.current_source_info().map_or(DUMMY_SP, |si| si.span),
        }
    }

    /// Emit all diagnostics that were registed with `register_diagnostics`
    fn process_diagnostics(&self, info: TopFrameInfo<'tcx>) {
        let this = self.eval_context_ref();
        DIAGNOSTICS.with(|diagnostics| {
            let mut diagnostics = diagnostics.borrow_mut();
            if diagnostics.is_empty() {
                return;
            }
            // We need to fix up the stack trace, because the machine has already
            // stepped to the next statement.
            let mut stacktrace = this.generate_stacktrace();
            // Remove newly pushed frames.
            while stacktrace.len() > info.stack_size {
                stacktrace.remove(0);
            }
            // Add popped frame back.
            if stacktrace.len() < info.stack_size {
                assert!(stacktrace.len() == info.stack_size-1, "we should never pop more than one frame at once");
                let frame_info = FrameInfo {
                    instance: info.instance.unwrap(),
                    span: info.span,
                    lint_root: None,
                };
                stacktrace.insert(0, frame_info);
            } else if let Some(instance) = info.instance {
                // Adjust topmost frame.
                stacktrace[0].span = info.span;
                assert_eq!(stacktrace[0].instance, instance, "we should not pop and push a frame in one step");
            }

            // Show diagnostics.
            for e in diagnostics.drain(..) {
                use NonHaltingDiagnostic::*;
                let msg = match e {
                    PoppedPointerTag(item) =>
                        format!("popped tracked tag for item {:?}", item),
                    CreatedCallId(id) =>
                        format!("function call with id {}", id),
                    CreatedAlloc(AllocId(id)) =>
                        format!("created allocation with id {}", id),
                    FreedAlloc(AllocId(id)) =>
                        format!("freed allocation with id {}", id),
                };
                report_msg(*this.tcx, /*error*/false, "tracking was triggered", msg, vec![], &stacktrace);
            }
        });
    }
}
<commit_msg>Auto merge of #1503 - RalfJung:post-mono, r=RalfJung<commit_after>use std::cell::RefCell;
use std::fmt;

use log::trace;

use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{source_map::DUMMY_SP, Span};

use crate::*;

/// Details of premature program termination.
pub enum TerminationInfo {
    Exit(i64),
    Abort(Option<String>),
    UnsupportedInIsolation(String),
    ExperimentalUb { msg: String, url: String },
    Deadlock,
}

impl fmt::Display for TerminationInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use TerminationInfo::*;
        match self {
            Exit(code) =>
                write!(f, "the evaluated program completed with exit code {}", code),
            Abort(None) =>
                write!(f, "the evaluated program aborted execution"),
            Abort(Some(msg)) =>
                write!(f, "the evaluated program aborted execution: {}", msg),
            UnsupportedInIsolation(msg) =>
                write!(f, "{}", msg),
            ExperimentalUb { msg, .. } =>
                write!(f, "{}", msg),
            Deadlock =>
                write!(f, "the evaluated program deadlocked"),
        }
    }
}

impl MachineStopType for TerminationInfo {}

/// Miri specific diagnostics
pub enum NonHaltingDiagnostic {
    PoppedPointerTag(Item),
    CreatedCallId(CallId),
    CreatedAlloc(AllocId),
    FreedAlloc(AllocId),
}

/// Emit a custom diagnostic without going through the miri-engine machinery
pub fn report_error<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
    e: InterpErrorInfo<'tcx>,
) -> Option<i64> {
    use InterpError::*;

    let (title, helps) = match &e.kind {
        MachineStop(info) => {
            let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
            use TerminationInfo::*;
            let title = match info {
                Exit(code) => return Some(*code),
                Abort(_) =>
                    "abnormal termination",
                UnsupportedInIsolation(_) =>
                    "unsupported operation",
                ExperimentalUb { .. } =>
                    "Undefined Behavior",
                Deadlock => "deadlock",
            };
            let helps = match info {
                UnsupportedInIsolation(_) =>
                    vec![format!("pass the flag `-Zmiri-disable-isolation` to disable isolation")],
                ExperimentalUb { url, .. } =>
                    vec![
                        format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental"),
                        format!("see {} for further information", url),
                    ],
                _ => vec![],
            };
            (title, helps)
        }
        _ => {
            let title = match e.kind {
                Unsupported(_) =>
                    "unsupported operation",
                UndefinedBehavior(_) =>
                    "Undefined Behavior",
                ResourceExhaustion(_) =>
                    "resource exhaustion",
                InvalidProgram(InvalidProgramInfo::ReferencedConstant) =>
                    "post-monomorphization error",
                _ =>
                    bug!("This error should be impossible in Miri: {}", e),
            };
            let helps = match e.kind {
                Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
                    vec![format!("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`")],
                Unsupported(UnsupportedOpInfo::ReadBytesAsPointer | UnsupportedOpInfo::ThreadLocalStatic(_) | UnsupportedOpInfo::ReadExternStatic(_)) =>
                    panic!("Error should never be raised by Miri: {:?}", e.kind),
                Unsupported(_) =>
                    vec![format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support")],
                UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. }) =>
                    vec![
                        format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior"),
                        format!("but alignment errors can also be false positives, see https://github.com/rust-lang/miri/issues/1074"),
                        format!("you can disable the alignment check with `-Zmiri-disable-alignment-check`, but that could hide true bugs")
                    ],
                UndefinedBehavior(_) =>
                    vec![
                        format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
                        format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
                    ],
                _ => vec![],
            };
            (title, helps)
        }
    };

    e.print_backtrace();
    let msg = e.to_string();
    report_msg(*ecx.tcx, /*error*/true, &format!("{}: {}", title, msg), msg, helps, &ecx.generate_stacktrace());

    // Debug-dump all locals.
    for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
        trace!("-------------------");
        trace!("Frame {}", i);
        trace!("    return: {:?}", frame.return_place.map(|p| *p));
        for (i, local) in frame.locals.iter().enumerate() {
            trace!("    local {}: {:?}", i, local.value);
        }
    }

    // Extra output to help debug specific issues.
    match e.kind {
        UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some(access))) => {
            eprintln!(
                "Uninitialized read occurred at offsets 0x{:x}..0x{:x} into this allocation:",
                access.uninit_ptr.offset.bytes(),
                access.uninit_ptr.offset.bytes() + access.uninit_size.bytes(),
            );
            eprintln!("{:?}", ecx.memory.dump_alloc(access.uninit_ptr.alloc_id));
        }
        _ => {}
    }

    None
}

/// Report an error or note (depending on the `error` argument) with the given stacktrace.
/// Also emits a full stacktrace of the interpreter stack.
fn report_msg<'tcx>(
    tcx: TyCtxt<'tcx>,
    error: bool,
    title: &str,
    span_msg: String,
    mut helps: Vec<String>,
    stacktrace: &[FrameInfo<'tcx>],
) {
    let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
    let mut err = if error {
        tcx.sess.struct_span_err(span, title)
    } else {
        tcx.sess.diagnostic().span_note_diag(span, title)
    };
    // Show main message.
    if span != DUMMY_SP {
        err.span_label(span, span_msg);
    } else {
        // Make sure we show the message even when it is a dummy span.
        err.note(&span_msg);
        err.note("(no span available)");
    }
    // Show help messages.
    if !helps.is_empty() {
        // Add visual separator before backtrace.
        helps.last_mut().unwrap().push_str("\n");
        for help in helps {
            err.help(&help);
        }
    }
    // Add backtrace
    for (idx, frame_info) in stacktrace.iter().enumerate() {
        let is_local = frame_info.instance.def_id().is_local();
        // No span for non-local frames and the first frame (which is the error site).
        if is_local && idx > 0 {
            err.span_note(frame_info.span, &frame_info.to_string());
        } else {
            err.note(&frame_info.to_string());
        }
    }

    err.emit();
}

thread_local! {
    static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
}

/// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available.
/// The diagnostic will be emitted after the current interpreter step is finished.
pub fn register_diagnostic(e: NonHaltingDiagnostic) {
    DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
}

/// Remember enough about the topmost frame so that we can restore the stack
/// after a step was taken.
pub struct TopFrameInfo<'tcx> {
    stack_size: usize,
    instance: Option<ty::Instance<'tcx>>,
    span: Span,
}

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn preprocess_diagnostics(&self) -> TopFrameInfo<'tcx> {
        // Ensure we have no lingering diagnostics.
        DIAGNOSTICS.with(|diagnostics| assert!(diagnostics.borrow().is_empty()));

        let this = self.eval_context_ref();
        if this.active_thread_stack().is_empty() {
            // Diagnostics can happen even with the empty stack (e.g. deallocation of thread-local statics).
            return TopFrameInfo { stack_size: 0, instance: None, span: DUMMY_SP };
        }
        let frame = this.frame();

        TopFrameInfo {
            stack_size: this.active_thread_stack().len(),
            instance: Some(frame.instance),
            span: frame.current_source_info().map_or(DUMMY_SP, |si| si.span),
        }
    }

    /// Emit all diagnostics that were registed with `register_diagnostics`
    fn process_diagnostics(&self, info: TopFrameInfo<'tcx>) {
        let this = self.eval_context_ref();
        DIAGNOSTICS.with(|diagnostics| {
            let mut diagnostics = diagnostics.borrow_mut();
            if diagnostics.is_empty() {
                return;
            }
            // We need to fix up the stack trace, because the machine has already
            // stepped to the next statement.
            let mut stacktrace = this.generate_stacktrace();
            // Remove newly pushed frames.
            while stacktrace.len() > info.stack_size {
                stacktrace.remove(0);
            }
            // Add popped frame back.
            if stacktrace.len() < info.stack_size {
                assert!(stacktrace.len() == info.stack_size-1, "we should never pop more than one frame at once");
                let frame_info = FrameInfo {
                    instance: info.instance.unwrap(),
                    span: info.span,
                    lint_root: None,
                };
                stacktrace.insert(0, frame_info);
            } else if let Some(instance) = info.instance {
                // Adjust topmost frame.
                stacktrace[0].span = info.span;
                assert_eq!(stacktrace[0].instance, instance, "we should not pop and push a frame in one step");
            }

            // Show diagnostics.
            for e in diagnostics.drain(..) {
                use NonHaltingDiagnostic::*;
                let msg = match e {
                    PoppedPointerTag(item) =>
                        format!("popped tracked tag for item {:?}", item),
                    CreatedCallId(id) =>
                        format!("function call with id {}", id),
                    CreatedAlloc(AllocId(id)) =>
                        format!("created allocation with id {}", id),
                    FreedAlloc(AllocId(id)) =>
                        format!("freed allocation with id {}", id),
                };
                report_msg(*this.tcx, /*error*/false, "tracking was triggered", msg, vec![], &stacktrace);
            }
        });
    }
}
<|endoftext|>


<commit_before>use tool::*;

///
/// Represents a tooling environment, which enables tools to be retrieved by name.
///
/// Environments make it possible to use tools without needing to know where the
/// precise implementation is located. Environments also provide a way to perform
/// dependency injection in other tools.
///
pub trait Environment {
    ///
    /// Retrieves a tool using a JSON interface by name
    ///
    fn get_json_tool(&self, name: &str) -> Result<Box<Tool>, RetrieveToolError>;
}

///
/// The reason an environment retrieve failed
///
#[derive(Copy, Clone)]
pub enum RetrieveFailReason {
    /// Reason not listed in this enum
    Generic,

    /// A tool could not be found
    NotFound,
}

///
/// Represents an error that occurred during a request to retrieve a tool
///
pub struct RetrieveToolError {
    /// The reason the failure occurred
    reason: RetrieveFailReason,

    /// A human-readable message associated with this error
    msg: String
}

impl RetrieveToolError {
    ///
    /// Creates a new error
    ///
    pub fn new(message: &str) -> RetrieveToolError {
        RetrieveToolError { reason: RetrieveFailReason::Generic, msg: String::from(message) }
    }

    ///
    /// Creates a 'tool not found' error
    ///
    pub fn not_found() -> RetrieveToolError {
        RetrieveToolError { reason: RetrieveFailReason::NotFound, msg: String::from("Tool not found") }
    }

    ///
    /// Retrieves the message for an error
    ///
    pub fn message<'a>(&'a self) -> &'a str {
        &self.msg
    }

    ///
    /// Retrieves the reason attached to this failure
    ///
    pub fn reason(&self) -> RetrieveFailReason {
        self.reason
    }
}
<commit_msg>Make the environment errors implement Debug<commit_after>use tool::*;

///
/// Represents a tooling environment, which enables tools to be retrieved by name.
///
/// Environments make it possible to use tools without needing to know where the
/// precise implementation is located. Environments also provide a way to perform
/// dependency injection in other tools.
///
pub trait Environment {
    ///
    /// Retrieves a tool using a JSON interface by name
    ///
    fn get_json_tool(&self, name: &str) -> Result<Box<Tool>, RetrieveToolError>;
}

///
/// The reason an environment retrieve failed
///
#[derive(Copy, Clone, Debug)]
pub enum RetrieveFailReason {
    /// Reason not listed in this enum
    Generic,

    /// A tool could not be found
    NotFound,
}

///
/// Represents an error that occurred during a request to retrieve a tool
///
#[derive(Debug)]
pub struct RetrieveToolError {
    /// The reason the failure occurred
    reason: RetrieveFailReason,

    /// A human-readable message associated with this error
    msg: String
}

impl RetrieveToolError {
    ///
    /// Creates a new error
    ///
    pub fn new(message: &str) -> RetrieveToolError {
        RetrieveToolError { reason: RetrieveFailReason::Generic, msg: String::from(message) }
    }

    ///
    /// Creates a 'tool not found' error
    ///
    pub fn not_found() -> RetrieveToolError {
        RetrieveToolError { reason: RetrieveFailReason::NotFound, msg: String::from("Tool not found") }
    }

    ///
    /// Retrieves the message for an error
    ///
    pub fn message<'a>(&'a self) -> &'a str {
        &self.msg
    }

    ///
    /// Retrieves the reason attached to this failure
    ///
    pub fn reason(&self) -> RetrieveFailReason {
        self.reason
    }
}
<|endoftext|>


<commit_before>//! a trait for things that can serve font tables

use font_types::Tag;

use crate::{tables, FontData, FontRead, FontReadWithArgs, ReadError};

/// An interface for accessing tables from a font (or font-like object)
pub trait TableProvider<'a> {
    fn data_for_tag(&self, tag: Tag) -> Option<FontData<'a>>;

    fn expect_data_for_tag(&self, tag: Tag) -> Result<FontData<'a>, ReadError> {
        self.data_for_tag(tag).ok_or(ReadError::TableIsMissing(tag))
    }

    fn head(&self) -> Result<tables::head::Head<'a>, ReadError> {
        self.expect_data_for_tag(tables::head::TAG)
            .and_then(FontRead::read)
    }

    fn name(&self) -> Result<tables::name::Name<'a>, ReadError> {
        self.expect_data_for_tag(tables::name::TAG)
            .and_then(FontRead::read)
    }

    fn hhea(&self) -> Result<tables::hhea::Hhea<'a>, ReadError> {
        self.expect_data_for_tag(tables::hhea::TAG)
            .and_then(FontRead::read)
    }

    fn hmtx(&self) -> Result<tables::hmtx::Hmtx<'a>, ReadError> {
        //FIXME: should we make the user pass these in?
        let num_glyphs = self.maxp().map(|maxp| maxp.num_glyphs())?;
        let number_of_h_metrics = self.hhea().map(|hhea| hhea.number_of_h_metrics())?;
        self.expect_data_for_tag(tables::hmtx::TAG)
            .and_then(|data| {
                FontReadWithArgs::read_with_args(data, &(num_glyphs, number_of_h_metrics))
            })
    }

    fn maxp(&self) -> Result<tables::maxp::Maxp<'a>, ReadError> {
        self.expect_data_for_tag(tables::maxp::TAG)
            .and_then(FontRead::read)
    }

    fn post(&self) -> Result<tables::post::Post<'a>, ReadError> {
        self.expect_data_for_tag(tables::post::TAG)
            .and_then(FontRead::read)
    }

    //fn stat(&self) -> Option<stat::Stat> {
    //self.data_for_tag(stat::TAG).and_then(stat::Stat::read)
    //}

    /// is_long can be optionally provided, if known, otherwise we look it up in head.
    fn loca(&self, is_long: impl Into<Option<bool>>) -> Result<tables::loca::Loca<'a>, ReadError> {
        let is_long = match is_long.into() {
            Some(val) => val,
            None => self.head()?.index_to_loc_format() == 1,
        };
        self.expect_data_for_tag(tables::loca::TAG)
            .and_then(|data| FontReadWithArgs::read_with_args(data, &is_long))
    }

    fn glyf(&self) -> Result<tables::glyf::Glyf<'a>, ReadError> {
        self.expect_data_for_tag(tables::glyf::TAG)
            .and_then(FontRead::read)
    }

    fn cmap(&self) -> Result<tables::cmap::Cmap<'a>, ReadError> {
        self.expect_data_for_tag(tables::cmap::TAG)
            .and_then(FontRead::read)
    }

    fn gdef(&self) -> Result<tables::gdef::Gdef<'a>, ReadError> {
        self.expect_data_for_tag(tables::gdef::TAG)
            .and_then(FontRead::read)
    }

    fn gpos(&self) -> Result<tables::gpos::Gpos<'a>, ReadError> {
        self.expect_data_for_tag(tables::gpos::TAG)
            .and_then(FontRead::read)
    }

    fn gsub(&self) -> Result<tables::gsub::Gsub<'a>, ReadError> {
        self.expect_data_for_tag(tables::gsub::TAG)
            .and_then(FontRead::read)
    }

    fn colr(&self) -> Result<tables::colr::Colr<'a>, ReadError> {
        self.expect_data_for_tag(tables::colr::TAG)
            .and_then(FontRead::read)
    }

    fn cpal(&self) -> Result<tables::cpal::Cpal<'a>, ReadError> {
        self.expect_data_for_tag(tables::cpal::TAG)
            .and_then(FontRead::read)
    }

    fn stat(&self) -> Result<tables::stat::Stat<'a>, ReadError> {
        self.expect_data_for_tag(tables::stat::TAG)
            .and_then(FontRead::read)
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    /// https://github.com/googlefonts/fontations/issues/105
    #[test]
    fn bug_105() {
        // serve some dummy versions of the tables used to compute hmtx. The only
        // fields that matter are maxp::num_glyphs and hhea::number_of_h_metrics,
        // everything else is zero'd out
        struct DummyProvider;
        impl TableProvider<'static> for DummyProvider {
            fn data_for_tag(&self, tag: Tag) -> Option<FontData<'static>> {
                if tag == Tag::new(b"maxp") {
                    Some(FontData::new(&[
                        0, 0, 0x50, 0, // version 0.5
                        0, 3, // num_glyphs = 3
                    ]))
                } else if tag == Tag::new(b"hhea") {
                    Some(FontData::new(&[
                        0, 1, 0, 0, // version 1.0
                        0, 0, 0, 0, // ascender/descender
                        0, 0, 0, 0, // line gap/advance width
                        0, 0, 0, 0, // min left/right side bearing
                        0, 0, 0, 0, // x_max, caret_slope_rise
                        0, 0, 0, 0, // caret_slope_run, caret_offset
                        0, 0, 0, 0, // reserved1/2
                        0, 0, 0, 0, // reserved 3/4
                        0, 0, 0, 1, // metric format, number_of_h_metrics
                    ]))
                } else if tag == Tag::new(b"hmtx") {
                    Some(FontData::new(&[
                        0, 4, 0, 6, // LongHorMetric: 4, 6
                        0, 30, 0, 111, // two lsb entries
                    ]))
                } else {
                    None
                }
            }
        }

        let number_of_h_metrics = DummyProvider.hhea().unwrap().number_of_h_metrics();
        let num_glyphs = DummyProvider.maxp().unwrap().num_glyphs();
        let hmtx = DummyProvider.hmtx().unwrap();

        assert_eq!(number_of_h_metrics, 1);
        assert_eq!(num_glyphs, 3);
        assert_eq!(hmtx.h_metrics().len(), 1);
        assert_eq!(hmtx.left_side_bearings().len(), 2);
    }
}
<commit_msg>Fix arg order in TableProvider::hmtx<commit_after>//! a trait for things that can serve font tables

use font_types::Tag;

use crate::{tables, FontData, FontRead, FontReadWithArgs, ReadError};

/// An interface for accessing tables from a font (or font-like object)
pub trait TableProvider<'a> {
    fn data_for_tag(&self, tag: Tag) -> Option<FontData<'a>>;

    fn expect_data_for_tag(&self, tag: Tag) -> Result<FontData<'a>, ReadError> {
        self.data_for_tag(tag).ok_or(ReadError::TableIsMissing(tag))
    }

    fn head(&self) -> Result<tables::head::Head<'a>, ReadError> {
        self.expect_data_for_tag(tables::head::TAG)
            .and_then(FontRead::read)
    }

    fn name(&self) -> Result<tables::name::Name<'a>, ReadError> {
        self.expect_data_for_tag(tables::name::TAG)
            .and_then(FontRead::read)
    }

    fn hhea(&self) -> Result<tables::hhea::Hhea<'a>, ReadError> {
        self.expect_data_for_tag(tables::hhea::TAG)
            .and_then(FontRead::read)
    }

    fn hmtx(&self) -> Result<tables::hmtx::Hmtx<'a>, ReadError> {
        //FIXME: should we make the user pass these in?
        let num_glyphs = self.maxp().map(|maxp| maxp.num_glyphs())?;
        let number_of_h_metrics = self.hhea().map(|hhea| hhea.number_of_h_metrics())?;
        self.expect_data_for_tag(tables::hmtx::TAG)
            .and_then(|data| {
                FontReadWithArgs::read_with_args(data, &(number_of_h_metrics, num_glyphs))
            })
    }

    fn maxp(&self) -> Result<tables::maxp::Maxp<'a>, ReadError> {
        self.expect_data_for_tag(tables::maxp::TAG)
            .and_then(FontRead::read)
    }

    fn post(&self) -> Result<tables::post::Post<'a>, ReadError> {
        self.expect_data_for_tag(tables::post::TAG)
            .and_then(FontRead::read)
    }

    //fn stat(&self) -> Option<stat::Stat> {
    //self.data_for_tag(stat::TAG).and_then(stat::Stat::read)
    //}

    /// is_long can be optionally provided, if known, otherwise we look it up in head.
    fn loca(&self, is_long: impl Into<Option<bool>>) -> Result<tables::loca::Loca<'a>, ReadError> {
        let is_long = match is_long.into() {
            Some(val) => val,
            None => self.head()?.index_to_loc_format() == 1,
        };
        self.expect_data_for_tag(tables::loca::TAG)
            .and_then(|data| FontReadWithArgs::read_with_args(data, &is_long))
    }

    fn glyf(&self) -> Result<tables::glyf::Glyf<'a>, ReadError> {
        self.expect_data_for_tag(tables::glyf::TAG)
            .and_then(FontRead::read)
    }

    fn cmap(&self) -> Result<tables::cmap::Cmap<'a>, ReadError> {
        self.expect_data_for_tag(tables::cmap::TAG)
            .and_then(FontRead::read)
    }

    fn gdef(&self) -> Result<tables::gdef::Gdef<'a>, ReadError> {
        self.expect_data_for_tag(tables::gdef::TAG)
            .and_then(FontRead::read)
    }

    fn gpos(&self) -> Result<tables::gpos::Gpos<'a>, ReadError> {
        self.expect_data_for_tag(tables::gpos::TAG)
            .and_then(FontRead::read)
    }

    fn gsub(&self) -> Result<tables::gsub::Gsub<'a>, ReadError> {
        self.expect_data_for_tag(tables::gsub::TAG)
            .and_then(FontRead::read)
    }

    fn colr(&self) -> Result<tables::colr::Colr<'a>, ReadError> {
        self.expect_data_for_tag(tables::colr::TAG)
            .and_then(FontRead::read)
    }

    fn cpal(&self) -> Result<tables::cpal::Cpal<'a>, ReadError> {
        self.expect_data_for_tag(tables::cpal::TAG)
            .and_then(FontRead::read)
    }

    fn stat(&self) -> Result<tables::stat::Stat<'a>, ReadError> {
        self.expect_data_for_tag(tables::stat::TAG)
            .and_then(FontRead::read)
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    /// https://github.com/googlefonts/fontations/issues/105
    #[test]
    fn bug_105() {
        // serve some dummy versions of the tables used to compute hmtx. The only
        // fields that matter are maxp::num_glyphs and hhea::number_of_h_metrics,
        // everything else is zero'd out
        struct DummyProvider;
        impl TableProvider<'static> for DummyProvider {
            fn data_for_tag(&self, tag: Tag) -> Option<FontData<'static>> {
                if tag == Tag::new(b"maxp") {
                    Some(FontData::new(&[
                        0, 0, 0x50, 0, // version 0.5
                        0, 3, // num_glyphs = 3
                    ]))
                } else if tag == Tag::new(b"hhea") {
                    Some(FontData::new(&[
                        0, 1, 0, 0, // version 1.0
                        0, 0, 0, 0, // ascender/descender
                        0, 0, 0, 0, // line gap/advance width
                        0, 0, 0, 0, // min left/right side bearing
                        0, 0, 0, 0, // x_max, caret_slope_rise
                        0, 0, 0, 0, // caret_slope_run, caret_offset
                        0, 0, 0, 0, // reserved1/2
                        0, 0, 0, 0, // reserved 3/4
                        0, 0, 0, 1, // metric format, number_of_h_metrics
                    ]))
                } else if tag == Tag::new(b"hmtx") {
                    Some(FontData::new(&[
                        0, 4, 0, 6, // LongHorMetric: 4, 6
                        0, 30, 0, 111, // two lsb entries
                    ]))
                } else {
                    None
                }
            }
        }

        let number_of_h_metrics = DummyProvider.hhea().unwrap().number_of_h_metrics();
        let num_glyphs = DummyProvider.maxp().unwrap().num_glyphs();
        let hmtx = DummyProvider.hmtx().unwrap();

        assert_eq!(number_of_h_metrics, 1);
        assert_eq!(num_glyphs, 3);
        assert_eq!(hmtx.h_metrics().len(), 1);
        assert_eq!(hmtx.left_side_bearings().len(), 2);
    }
}
<|endoftext|>


<commit_before>
use buffer::{ImageBuffer, Pixel, ArrayLike};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

use std::old_io;
use std::old_io::{MemReader, IoResult};
use std::num::Int;

use color::{Rgb, Rgba};
use utils::lzw;
use utils::bitstream::LsbWriter;

use super::{Extension, Block, DisposalMethod};

#[derive(Debug, Copy)]
#[allow(unused_qualifications)]
/// The color mode the encoder will use to encode the image.
pub enum ColorMode {
	/// Image will be encoded in multiple frames if more than 256 colors are present
	TrueColor,
	/// Number of colors will be reduced
	Indexed(u8),
}
use self::ColorMode::{TrueColor, Indexed};

/// A GIF encoder.
///
/// Encodes the image either in true color using indexed colors.
/// If the mode is set to TrueColor the image is split into multiple frames
/// when the number of colors including transparent color exceeds 256.
/// Pixels with an alpha value != 1.0 will be set to alpha = 0.
pub struct Encoder<Image> {
	image: Image,
	bg_color: Option<Rgb<u8>>,
	color_mode: ColorMode
}

const TRANSPARENT: Rgba<u8> = Rgba([0, 0, 0, 0]);

impl<Container> Encoder<ImageBuffer<Rgba<u8>, Container>>
where Container: ArrayLike<u8> {
	/// Creates a new GIF encoder
	pub fn new(image: ImageBuffer<Rgba<u8>, Container>,
		       bg_color: Option<Rgb<u8>>,
		       color_mode: ColorMode,
		      ) -> Encoder<ImageBuffer<Rgba<u8>, Container>> {
		Encoder {
			image: image,
			bg_color: bg_color,
			color_mode: color_mode
		}
	}

	/// Encodes the image
	pub fn encode<W: Writer>(&mut self, w: &mut W) -> IoResult<()> {
		// Header
		try!(w.write_all(b"GIF89a"));
		// Logical screen descriptor
		let height = self.image.height();
		let width = self.image.width();
		if width > <u16 as Int>::max_value() as u32 ||
		   height > <u16 as Int>::max_value() as u32 {
		   	return Err(old_io::IoError{
		   		kind: old_io::InvalidInput,
		   		desc: "Image dimensions are to large for the gif format.",
		   		detail: None
		   	})
		}
		try!(w.write_le_u16(width as u16));
		try!(w.write_le_u16(height as u16));

		let (hist, transparent) = self.histogram();
		let num_colors = hist.len();

		try!(self.write_global_table(w, &hist[]));

		match self.color_mode {
			// Global color table has been written, just write the image data now
			TrueColor if num_colors <= 256 => {
				// NOTE: copy paste any changes to case: Indexed(n) if n as usize <= num_colors
				try!(self.write_control_ext(w, 0, transparent));
				try!(self.write_descriptor(w, None));
				try!(self.write_image_simple(w, &hist[], 0, transparent));
			},
			TrueColor => {
				try!(self.write_true_color(w, hist, transparent));
			},
			Indexed(n) if n as usize <= num_colors => {
				// NOTE: copy paste from case: TrueColor if num_colors <= 256
				try!(self.write_control_ext(w, 0, transparent));
				try!(self.write_descriptor(w, None));
				try!(self.write_image_simple(w, &hist[], 0, transparent));
			}
			Indexed(_) => {
				panic!("Indexed(n) unimplemented")
			}

		}
		w.write_u8(Block::Trailer as u8)
	}

	/// Returns a histogram of the colors in the image
	fn histogram(&self) -> (Vec<(Rgba<u8>, usize)>, Option<usize>){
		let mut hist: HashMap<_, usize> = HashMap::new();
		// Add bg_color to table
		if let Some(bg_color) = self.bg_color {
			let _ = hist.insert(bg_color.to_rgba(), 0);
		}
		for p in self.image.pixels() {
			let mut p = *p;
			// Replace transparent pixels with black, alpha = 0
			if let Some(bg_color) = self.bg_color {
				p.blend(&bg_color.to_rgba())
			} else if p[3] != 255 {
				p = TRANSPARENT;
			}
			match hist.entry(p) {
				Occupied(mut entry) => {
					let val = entry.get_mut();
					*val = *val + 1;
				},
				Vacant(entry) => {
					entry.insert(1);
				}
			}
		}
		let mut colors: Vec<(Rgba<u8>, usize)> = hist.into_iter().collect();
		// Sort color map with decreasing color freqs
		colors.sort_by(|a, b| b.1.cmp(&a.1));
		// Add bg_color to table
		let transparent = colors.iter().position(|x| x.0 == TRANSPARENT);
		(colors, transparent)
	}

	/// Write the global color table and the corresponding flags
	fn write_global_table<W: Writer>(&mut self,
									 w: &mut W,
									 hist: &[(Rgba<u8>, usize)]
									) -> IoResult<()>
	{
		let num_colors = hist.len();
		let mut flags = 0;
		flags |= 1 << 4; // sort flag
		let n = flag_n(num_colors);
		flags |= n << 4; // 2^(n+1) colors
		let (global_table, bg_index) = if num_colors <= 256 {
			flags |= 1 << 7; // glocal table flag
			flags |= n;
			let mut bg_index = 0;
			let mut table = Vec::with_capacity(3*(2 << n));
			for (i, &(color, _)) in hist.iter().enumerate() {
				let channels = &color.channels()[..3];
				if let Some(bg_color) = self.bg_color {
					if channels == bg_color.channels() {
						bg_index = i;
					}
				}
				table.push_all(channels)
			}
			// Waste some space as of gif spec
			for _ in 0..((2 << n) - num_colors) {
				table.push_all(&[0, 0, 0]);
			}
			(Some(table), bg_index as u8)
		} else if let Some(bg_color) = self.bg_color {
			flags |= 1 << 7; // glocal table flag
			let mut table = Vec::with_capacity(6);
			table.push_all(&bg_color.channels()[..3]);
			table.push_all(&[0, 0, 0]);
			(Some(table), 0)
		} else {
			(None, 0)
		};
		try!(w.write_u8(flags));
		try!(w.write_u8(bg_index));
		try!(w.write_u8(0)); // aspect ration, disregard
		if let Some(global_table) = global_table {
			try!(w.write_all(&global_table[]));
		}
		Ok(())
	}

	/// Writes the graphics control extension
	fn write_control_ext<W: Writer>(&mut self,
								    w: &mut W,
								    delay: u16,
								    transparent: Option<usize>
								   ) -> IoResult<()>
	{
		try!(w.write_u8(Block::Extension as u8));
		try!(w.write_u8(Extension::Control as u8));
		try!(w.write_u8(4)); // size
		let mut field = 0;
		field |= (DisposalMethod::None as u8) << 2;
		let idx = if let Some(idx) = transparent {
			field |=  1;
			idx as u8
		} else {
			0
		};
		try!(w.write_u8(field));
		try!(w.write_le_u16(delay));
		try!(w.write_u8(idx));
		w.write_u8(0)
	}

	/// Writes the image descriptor
	fn write_descriptor<W: Writer>(&mut self,
								    w: &mut W,
								    table_len: Option<usize>
								   ) -> IoResult<()>
	{
		try!(w.write_u8(Block::Image as u8));
		try!(w.write_le_u16(0)); // left
		try!(w.write_le_u16(0)); // top
		let height = self.image.height();
		let width = self.image.width();
		try!(w.write_le_u16(width as u16));
		try!(w.write_le_u16(height as u16));
		if let Some(len) = table_len {
			w.write_u8(flag_n(len) | 0x80)
		} else {
			w.write_u8(0)
		}
	}

	/// Writes and compresses the indexed data
	fn write_indices<W: Writer>(&mut self,
								    w: &mut W,
								    indices: &[u8],
								   ) -> IoResult<()>
	{
		let code_size = match flag_n(indices.len()) + 1 {
			1 => 2,
			n => n
		};
		let mut encoded_data = Vec::new();
		try!(lzw::encode(indices, LsbWriter::new(&mut encoded_data), code_size));
		try!(w.write_u8(code_size));
		for chunk in encoded_data.chunks(255) {
			try!(w.write_u8((chunk.len()) as u8));
			try!(w.write_all(chunk));
		}
		w.write_u8(0) // block terminator

	}

	/// Writes the image to the file assuming that every pixel is in the color table
	/// If not, the index of the transparent pixel is written
	fn write_image_simple<W: Writer>(&mut self,
								    w: &mut W,
								    hist: &[(Rgba<u8>, usize)],
								    offset: u8,
								    transparent: Option<usize>,
								   ) -> IoResult<()>
	{
		let t_idx = match transparent { Some(i) => i as u8, None => 0 };
		let data: Vec<u8> = self.image.pixels().map(|p| {
			if let Some(idx) = hist.iter().position(|x| x.0 == *p) {
				idx as u8 + offset
			} else {
				t_idx
			}
		}).collect();
		self.write_indices(w, &data[])

	}

	/// Writes the graphics control extension
	fn write_true_color<W: Writer>(&mut self,
								    w: &mut W,
								    hist: Vec<(Rgba<u8>, usize)>,
								    transparent: Option<usize>
								   ) -> IoResult<()>
	{
		let mut hist = hist;
		// We need a transparent pixel we put it to index 0
		if let Some(transparent) = transparent {
			let val = hist.swap_remove(transparent);
			hist.push(val);
		} else {
			hist.reserve(1);
			hist.push((TRANSPARENT, 0));
		}
		let val = hist.swap_remove(0);
		hist.push(val);
		let transparent = Some(0);
		for chunk in hist[1..].chunks(255) {
			// we inject the transparent color in every table thus => +1
			let num_local_colors = chunk.len() + 1;
			let n = flag_n(num_local_colors);
			try!(self.write_control_ext(w, 0, transparent));
			try!(self.write_descriptor(w, Some(num_local_colors)));
			// Transparent color
			try!(w.write_all(&TRANSPARENT.channels()[..3]));
			for &(color, _) in chunk.iter() {
				try!(w.write_all(&color.channels()[..3]));
			}
			// Waste some space as of gif spec
			for _ in 0..((2 << n) - num_local_colors) {
				try!(w.write_all(&[0, 0, 0]));
			}
			try!(self.write_image_simple(w, chunk, 1, transparent));
		}
		Ok(())
	}
}

// Color table len converted to flag bits
fn flag_n(size: usize) -> u8 {
	match size {
		0  ...2   => 0,
		3  ...4   => 1,
		5  ...8   => 2,
		7  ...16  => 3,
		17 ...32  => 4,
		33 ...64  => 5,
		65 ...128 => 6,
		129...256 => 7,
		_ => 7
	}
}<commit_msg>Faster version of true color gif encoding.<commit_after>
use buffer::{ImageBuffer, Pixel, ArrayLike};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

use std::old_io;
use std::old_io::IoResult;
use std::num::Int;

use color::{Rgb, Rgba};
use utils::lzw;
use utils::bitstream::LsbWriter;

use super::{Extension, Block, DisposalMethod};

#[derive(Debug, Copy)]
#[allow(unused_qualifications)]
/// The color mode the encoder will use to encode the image.
pub enum ColorMode {
	/// Image will be encoded in multiple frames if more than 256 colors are present
	TrueColor,
	/// Number of colors will be reduced
	Indexed(u8),
}
use self::ColorMode::{TrueColor, Indexed};

/// A GIF encoder.
///
/// Encodes the image either in true color using indexed colors.
/// If the mode is set to TrueColor the image is split into multiple frames
/// when the number of colors including transparent color exceeds 256.
/// Pixels with an alpha value != 1.0 will be set to alpha = 0.
pub struct Encoder<Image> {
	image: Image,
	bg_color: Option<Rgb<u8>>,
	color_mode: ColorMode
}

const TRANSPARENT: Rgba<u8> = Rgba([0, 0, 0, 0]);

impl<Container> Encoder<ImageBuffer<Rgba<u8>, Container>>
where Container: ArrayLike<u8> {
	/// Creates a new GIF encoder
	pub fn new(image: ImageBuffer<Rgba<u8>, Container>,
		       bg_color: Option<Rgb<u8>>,
		       color_mode: ColorMode,
		      ) -> Encoder<ImageBuffer<Rgba<u8>, Container>> {
		Encoder {
			image: image,
			bg_color: bg_color,
			color_mode: color_mode
		}
	}

	/// Encodes the image
	pub fn encode<W: Writer>(&mut self, w: &mut W) -> IoResult<()> {
		// Header
		try!(w.write_all(b"GIF89a"));
		// Logical screen descriptor
		let height = self.image.height();
		let width = self.image.width();
		if width > <u16 as Int>::max_value() as u32 ||
		   height > <u16 as Int>::max_value() as u32 {
		   	return Err(old_io::IoError{
		   		kind: old_io::InvalidInput,
		   		desc: "Image dimensions are to large for the gif format.",
		   		detail: None
		   	})
		}
		try!(w.write_le_u16(width as u16));
		try!(w.write_le_u16(height as u16));

		let (hist, transparent) = self.histogram();
		let num_colors = hist.len();

		try!(self.write_global_table(w, &hist[]));

		match self.color_mode {
			// Global color table has been written, just write the image data now
			TrueColor if num_colors <= 256 => {
				// NOTE: copy paste any changes to case: Indexed(n) if n as usize <= num_colors
				try!(self.write_control_ext(w, 0, transparent));
				try!(self.write_descriptor(w, None));
				try!(self.write_image_simple(w, &hist[], transparent));
			},
			TrueColor => {
				try!(self.write_true_color(w, hist, transparent));
			},
			Indexed(n) if n as usize <= num_colors => {
				// NOTE: copy paste from case: TrueColor if num_colors <= 256
				try!(self.write_control_ext(w, 0, transparent));
				try!(self.write_descriptor(w, None));
				try!(self.write_image_simple(w, &hist[], transparent));
			}
			Indexed(_) => {
				panic!("Indexed(n) unimplemented")
			}

		}
		w.write_u8(Block::Trailer as u8)
	}

	/// Returns a histogram of the colors in the image
	fn histogram(&self) -> (Vec<(Rgba<u8>, usize)>, Option<usize>){
		let mut hist: HashMap<_, usize> = HashMap::new();
		// Add bg_color to table
		if let Some(bg_color) = self.bg_color {
			let _ = hist.insert(bg_color.to_rgba(), 0);
		}
		for p in self.image.pixels() {
			let mut p = *p;
			// Replace transparent pixels with black, alpha = 0
			if let Some(bg_color) = self.bg_color {
				p.blend(&bg_color.to_rgba())
			} else if p[3] != 255 {
				p = TRANSPARENT;
			}
			match hist.entry(p) {
				Occupied(mut entry) => {
					let val = entry.get_mut();
					*val = *val + 1;
				},
				Vacant(entry) => {
					entry.insert(1);
				}
			}
		}
		let mut colors: Vec<(Rgba<u8>, usize)> = hist.into_iter().collect();
		// Sort color map with decreasing color freqs
		colors.sort_by(|a, b| b.1.cmp(&a.1));
		// Add bg_color to table
		let transparent = colors.iter().position(|x| x.0 == TRANSPARENT);
		(colors, transparent)
	}

	/// Write the global color table and the corresponding flags
	fn write_global_table<W: Writer>(&mut self,
									 w: &mut W,
									 hist: &[(Rgba<u8>, usize)]
									) -> IoResult<()>
	{
		let num_colors = hist.len();
		let mut flags = 0;
		flags |= 1 << 4; // sort flag
		let n = flag_n(num_colors);
		flags |= n << 4; // 2^(n+1) colors
		let (global_table, bg_index) = if num_colors <= 256 {
			flags |= 1 << 7; // glocal table flag
			flags |= n;
			let mut bg_index = 0;
			let mut table = Vec::with_capacity(3*(2 << n));
			for (i, &(color, _)) in hist.iter().enumerate() {
				let channels = &color.channels()[..3];
				if let Some(bg_color) = self.bg_color {
					if channels == bg_color.channels() {
						bg_index = i;
					}
				}
				table.push_all(channels)
			}
			// Waste some space as of gif spec
			for _ in 0..((2 << n) - num_colors) {
				table.push_all(&[0, 0, 0]);
			}
			(Some(table), bg_index as u8)
		} else if let Some(bg_color) = self.bg_color {
			flags |= 1 << 7; // glocal table flag
			let mut table = Vec::with_capacity(6);
			table.push_all(&bg_color.channels()[..3]);
			table.push_all(&[0, 0, 0]);
			(Some(table), 0)
		} else {
			(None, 0)
		};
		try!(w.write_u8(flags));
		try!(w.write_u8(bg_index));
		try!(w.write_u8(0)); // aspect ration, disregard
		if let Some(global_table) = global_table {
			try!(w.write_all(&global_table[]));
		}
		Ok(())
	}

	/// Writes the graphics control extension
	fn write_control_ext<W: Writer>(&mut self,
								    w: &mut W,
								    delay: u16,
								    transparent: Option<usize>
								   ) -> IoResult<()>
	{
		try!(w.write_u8(Block::Extension as u8));
		try!(w.write_u8(Extension::Control as u8));
		try!(w.write_u8(4)); // size
		let mut field = 0;
		field |= (DisposalMethod::None as u8) << 2;
		let idx = if let Some(idx) = transparent {
			field |=  1;
			idx as u8
		} else {
			0
		};
		try!(w.write_u8(field));
		try!(w.write_le_u16(delay));
		try!(w.write_u8(idx));
		w.write_u8(0)
	}

	/// Writes the image descriptor
	fn write_descriptor<W: Writer>(&mut self,
								    w: &mut W,
								    table_len: Option<usize>
								   ) -> IoResult<()>
	{
		try!(w.write_u8(Block::Image as u8));
		try!(w.write_le_u16(0)); // left
		try!(w.write_le_u16(0)); // top
		let height = self.image.height();
		let width = self.image.width();
		try!(w.write_le_u16(width as u16));
		try!(w.write_le_u16(height as u16));
		if let Some(len) = table_len {
			w.write_u8(flag_n(len) | 0x80)
		} else {
			w.write_u8(0)
		}
	}

	/// Writes and compresses the indexed data
	fn write_indices<W: Writer>(&mut self,
								    w: &mut W,
								    indices: &[u8],
								   ) -> IoResult<()>
	{
		let code_size = match flag_n(indices.len()) + 1 {
			1 => 2,
			n => n
		};
		let mut encoded_data = Vec::new();
		try!(lzw::encode(indices, LsbWriter::new(&mut encoded_data), code_size));
		try!(w.write_u8(code_size));
		for chunk in encoded_data.chunks(255) {
			try!(w.write_u8((chunk.len()) as u8));
			try!(w.write_all(chunk));
		}
		w.write_u8(0) // block terminator

	}

	/// Writes the image to the file assuming that every pixel is in the color table
	/// If not, the index of the transparent pixel is written
	fn write_image_simple<W: Writer>(&mut self,
								    w: &mut W,
								    hist: &[(Rgba<u8>, usize)],
								    transparent: Option<usize>,
								   ) -> IoResult<()>
	{
		let t_idx = match transparent { Some(i) => i as u8, None => 0 };
		let data: Vec<u8> = self.image.pixels().map(|p| {
			if let Some(idx) = hist.iter().position(|x| x.0 == *p) {
				idx as u8
			} else {
				t_idx
			}
		}).collect();
		self.write_indices(w, &data[])

	}

	/// Writes the graphics control extension
	fn write_true_color<W: Writer>(&mut self,
								    w: &mut W,
								    hist: Vec<(Rgba<u8>, usize)>,
								    transparent: Option<usize>
								   ) -> IoResult<()>
	{
		let mut hist = hist;
		// We need a transparent pixel we put it to index 0
		if let Some(transparent) = transparent {
			let val = hist.swap_remove(transparent);
			hist.push(val);
		} else {
			hist.reserve(1);
			hist.push((TRANSPARENT, 0));
		}
		let val = hist.swap_remove(0);
		hist.push(val);
		let transparent = Some(0);

		// Calculating the indices is expensive so just do it once
		let indices: Vec<u32> = self.image.pixels().map(|p| {
			if let Some(idx) = hist.iter().position(|x| x.0 == *p) {
				idx as u32
			} else {
				0
			}
		}).collect();
		let mut chunk_indices = Vec::with_capacity(self.image.as_slice().len());
		// starting from 1 since we inject the transparent color in every frame
		for (j, chunk) in hist[1..].chunks(255).enumerate() {
			// Now we can reuse the indices for every subimage
			// if the substract j*255 from each an replace it with 0 if it gets negative
			chunk_indices.clear();
			for &idx in indices.iter() {
				let i: i32 = idx as i32 - j as i32*255;
				chunk_indices.push(if i < 0 { 0 } else { i as u8 })
			}
			// Write local color table
			let num_local_colors = chunk.len() + 1;
			let n = flag_n(num_local_colors);
			try!(self.write_control_ext(w, 0, transparent));
			try!(self.write_descriptor(w, Some(num_local_colors)));
			// Inject transparent color
			try!(w.write_all(&TRANSPARENT.channels()[..3]));
			for &(color, _) in chunk.iter() {
				try!(w.write_all(&color.channels()[..3]));
			}
			// waste some space as of gif spec
			for _ in 0..((2 << n) - num_local_colors) {
				try!(w.write_all(&[0, 0, 0]));
			}
			// write indices
			try!(self.write_indices(w, &chunk_indices[]));
		}
		Ok(())
	}
}

// Color table len converted to flag bits
fn flag_n(size: usize) -> u8 {
	match size {
		0  ...2   => 0,
		3  ...4   => 1,
		5  ...8   => 2,
		7  ...16  => 3,
		17 ...32  => 4,
		33 ...64  => 5,
		65 ...128 => 6,
		129...256 => 7,
		_ => 7
	}
}<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![feature(plugin)]
#![plugin(gfx_macros)]

//! Demonstrates how to initialize gfx-rs using the gl-init-rs library.

extern crate gfx;
extern crate gfx_device_gl;
extern crate glutin;

use gfx::traits::*;

pub fn main() {
    let window = glutin::Window::new().unwrap();
    window.set_title("glutin initialization example");
    unsafe { window.make_current() };
    let (w, h) = window.get_inner_size().unwrap();

    let mut graphics = gfx_device_gl::create(|s| window.get_proc_address(s))
                                     .into_graphics();

    'main: loop {
        // quit when Esc is pressed.
        for event in window.poll_events() {
            match event {
                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) => break 'main,
                glutin::Event::Closed => break 'main,
                _ => {},
            }
        }

        graphics.clear(
            gfx::ClearData {
                color: [0.3, 0.3, 0.3, 1.0],
                depth: 1.0,
                stencil: 0,
            },
            gfx::COLOR,
            &gfx::Frame::new(w as u16, h as u16)
        );

        graphics.end_frame();
        window.swap_buffers();
        graphics.cleanup();
    }
}
<commit_msg>Updated the glutin example<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![feature(plugin)]
#![plugin(gfx_macros)]

//! Demonstrates how to initialize gfx-rs using the glutin library.

extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;

use gfx::traits::*;

pub fn main() {
    let (wrap, device, factory) =
        gfx_window_glutin::init_titled("glutin initialization example")
                          .unwrap();
    let mut graphics = (device, factory).into_graphics();
    'main: loop {
        // quit when Esc is pressed.
        for event in wrap.window.poll_events() {
            match event {
                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) => break 'main,
                glutin::Event::Closed => break 'main,
                _ => {},
            }
        }

        let cdata = gfx::ClearData {
            color: [0.3, 0.3, 0.3, 1.0],
            depth: 1.0,
            stencil: 0,
        };
        graphics.clear(cdata, gfx::COLOR, &wrap);
        graphics.end_frame();
        wrap.window.swap_buffers();
        graphics.cleanup();
    }
}
<|endoftext|>