<commit_before>#[macro_use]
extern crate nom;

#[allow(dead_code)]
pub mod ts {

    use std;
    use std::fmt;
    use std::collections::HashMap;
    use std::str::from_utf8;

    use nom::*;

    #[derive(Debug, PartialEq)]
    pub enum Format {
        RI,
        DB,
        MA,
    }

    impl fmt::Display for Format {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &Format::RI => "RI",
                &Format::DB => "DB",
                &Format::MA => "MA",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for Format {
        fn default() -> Format {
            Format::MA
        }
    }

    #[derive(Debug, PartialEq)]
    pub enum FreqUnits {
        Hz,
        KHz,
        MHz,
        GHz,
    }

    impl fmt::Display for FreqUnits {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &FreqUnits::Hz => "Hz",
                &FreqUnits::KHz => "KHz",
                &FreqUnits::MHz => "MHz",
                &FreqUnits::GHz => "GHz",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for FreqUnits {
        fn default() -> FreqUnits {
            FreqUnits::GHz
        }
    }

    #[derive(Debug, PartialEq)]
    pub enum Domain {
        Scattering,
        Transfer,
        Admittance,
        Impedance,
        HybridH,
        HybridG,
    }

    impl fmt::Display for Domain {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &Domain::Scattering => "S",
                &Domain::Transfer => "T",
                &Domain::Admittance => "Y",
                &Domain::Impedance => "Z",
                &Domain::HybridH => "H",
                &Domain::HybridG => "G",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for Domain {
        fn default() -> Domain {
            Domain::Scattering
        }
    }

    #[derive(Debug, PartialEq)]
    pub struct OptionLine {
        pub freq_units: FreqUnits,
        pub domain: Domain,
        pub format: Format,
        pub system_impedance: Option<f64>,
    }

    impl Default for OptionLine {
        fn default() -> OptionLine {
            OptionLine {
                freq_units: Default::default(),
                domain: Default::default(),
                format: Default::default(),
                system_impedance: Default::default()
            }
        }
    }

    #[allow(non_camel_case_types)]
    #[derive(Debug, PartialEq)]
    pub enum TwoPortOrder {
        TPO_12_21,
        TPO_21_12,
    }

    #[derive(Debug, PartialEq)]
    pub struct NoisePoint {
        pub frequency: f64,
        pub minimum_noise_db: f64,
        pub source_refl_coeff_magnitude: f64,
        pub source_refl_coeff_phase: f64,
        pub effective_noise_resistance: f64,
    }

    #[derive(Debug)]
    pub struct Touchstone2 {
        pub num_ports: i32,
        pub option_line: OptionLine,
        pub two_port_order: Option<TwoPortOrder>,
        pub reference_impedance: Option<Vec<f64>>,
        pub information: HashMap<String, String>,
        pub network_data: Vec<Box<[(f64, f64)]>>,
        pub noise_data: Option<Box<[NoisePoint]>>,
    }

    fn parse<T: std::str::FromStr>(bytes: &[u8]) -> Option<T> {
        from_utf8(bytes).ok().and_then(|s| s.trim().parse().ok())
    }

    named!(
        int<i32>,
        map_res!(
            map_res!(
                ws!(digit),
                from_utf8
            ),
            std::str::FromStr::from_str
        )
    );

    fn is_valid_float_char(c: u8) -> bool {
        (c >= 48u8 && c <= 57) || // is_digit
        (c == 43u8 || c == 45u8 || c == 46u8) || // + or - or .
        (c == 69u8 || c == 101u8) // E or e
    }

    named!(
        float<f64>,
        map_res!(
            map_res!(
                take_while!(is_valid_float_char),
                from_utf8
            ),
            std::str::FromStr::from_str
        )
    );

    named!(
        comment_line<()>,
        value!((), delimited!(char!('!'), take_until!("\n"), char!('\n')))
    );

    named!(
        version<(i32, i32)>,
        do_parse!(
            tag_no_case!("[version]") >> many0!(space) >>
            ver: separated_pair!(int, char!('.'), int) >>
            (ver)
        )
    );

    fn not_whitespace(c: u8) -> bool {
        !(
            c == ' '  as u8 ||
            c == '\t' as u8 ||
            c == '\n' as u8 ||
            c == '\r' as u8
        )
    }

    named!(
        option_line<OptionLine>,
        do_parse!(
            char!('#') >>
            units: opt!(complete!(do_parse!(
                many1!(space) >>
                v: alt!(
                    value!(FreqUnits::Hz,  tag_no_case!("hz"))  |
                    value!(FreqUnits::KHz, tag_no_case!("khz")) |
                    value!(FreqUnits::MHz, tag_no_case!("mhz")) |
                    value!(FreqUnits::GHz, tag_no_case!("ghz"))
                ) >>
                (v)
            ))) >>
            domain: opt!(complete!(do_parse!(
                many1!(space) >>
                v: alt!(
                    value!(Domain::Scattering, tag_no_case!("s")) |
                    value!(Domain::Transfer,   tag_no_case!("t")) |
                    value!(Domain::Admittance, tag_no_case!("y")) |
                    value!(Domain::Impedance,  tag_no_case!("z")) |
                    value!(Domain::HybridH,    tag_no_case!("h")) |
                    value!(Domain::HybridG,    tag_no_case!("g"))
                ) >>
                (v)
            ))) >>
            format: opt!(complete!(do_parse!(
                many1!(space) >>
                v: alt!(
                    value!(Format::DB, tag_no_case!("db")) |
                    value!(Format::MA, tag_no_case!("ma")) |
                    value!(Format::RI, tag_no_case!("ri"))
                ) >>
                (v)
            ))) >>
            system_impedance: opt!(complete!(do_parse!(
                many1!(space) >>
                tag!("R") >>
                many1!(space) >>
                v: float >>
                (v)
            ))) >>
            (OptionLine {
                freq_units: units.unwrap_or(Default::default()),
                domain: domain.unwrap_or(Default::default()),
                format: format.unwrap_or(Default::default()),
                system_impedance: system_impedance
            })
        )
    );

    named!(
        number_of_ports<i32>,
        do_parse!(
            tag_no_case!("[number of ports]") >> many0!(space) >>
            num_ports: int >>
            (num_ports)
        )
    );

    named!(
        two_port_order<TwoPortOrder>,
        do_parse!(
            tag_no_case!("[two-port order]") >> many0!(space) >>
            tpo: alt!(
                value!(TwoPortOrder::TPO_12_21, tag!("12_21")) |
                value!(TwoPortOrder::TPO_21_12, tag!("21_12"))
            ) >>
            (tpo)
        )
    );

    named!(
        number_of_frequencies<i32>,
        do_parse!(
            tag_no_case!("[number of frequencies]") >> many0!(space) >>
            num_freqs: map_opt!(take_until!("\n"), parse) >>
            (num_freqs)
        )
    );

    named!(
        number_of_noise_frequencies<i32>,
        do_parse!(
            tag_no_case!("[number of noise frequencies]") >> many0!(space) >>
            num_freqs: map_opt!(take_until!("\n"), parse) >>
            (num_freqs)
        )
    );

    named!(
        reference<Vec<f64>>,
        do_parse!(
            tag_no_case!("[reference]") >> many0!(multispace) >>
            refs: many1!(ws!(float)) >>
            (refs)
        )
    );

    named!(
        kwarg<(String, String)>,
        do_parse!(
            char!('[') >>
            kw: map_res!(
                take_until!("]"),
                |x| from_utf8(x).map(|s| s.trim().to_string())
            ) >>
            char!(']') >>
            many0!(space) >>
            arg: map_res!(
                take_until!("\n"),
                |x| from_utf8(x).map(|s| s.trim().to_string())
            ) >>
            (kw, "".to_string())
        )
    );

    named!(
        information<HashMap<String, String>>,
        ws!(delimited!(
            tag_no_case!("[begin information]"),
            map!(
                many0!(ws!(kwarg)),
                |v: Vec<(String, String)>| v.iter().cloned().collect()
            ),
            tag_no_case!("[end information]")
        ))
    );

    macro_rules! comments {
        ($i:expr, $($args:tt)*) => ({sep!($i, comment_line, $($args)*)})
    }

    #[allow(dead_code)]
    #[derive(Debug, PartialEq)]
    enum Metadata {
        TwoPortOrder(TwoPortOrder),
        NumberOfFrequencies(i32),
        NumberOfNoiseFrequencies(i32),
        Reference(Vec<f64>),
        Information(HashMap<String, String>),
    }

    named!(
        metadata<Vec<Metadata>>,
        many0!(ws!(alt!(
            map!(two_port_order, Metadata::TwoPortOrder) |
            map!(number_of_frequencies, Metadata::NumberOfFrequencies) |
            map!(number_of_noise_frequencies, Metadata::NumberOfNoiseFrequencies) |
            map!(reference, Metadata::Reference)
        )))
    );

    named!(
        header<((i32, i32), OptionLine, i32, Vec<Metadata>)>,
        ws!(do_parse!(
            ver: version >>
            opt_line: option_line >>
            num_ports: number_of_ports >>
            metadata: metadata >>
            (ver, opt_line, num_ports, metadata)
        ))
    );

    #[allow(unused_imports)]
    mod test {
        use super::*;
        use nom;

        fn done<'a, T>(val: T) -> nom::IResult<&'a [u8], T> {
            nom::IResult::Done(&b""[..], val)
        }

        #[test]
        fn test_comment_line() {
            assert_eq!(comment_line(b"!test\n"), done(()));
        }

        #[test]
        fn test_version() {
            assert_eq!(version(b"[version] 2.0"), done((2, 0)));
        }

        #[test]
        fn test_option_line() {
            assert_eq!(option_line(b"#"), done(Default::default()))
        }

        #[test]
        fn test_number_of_ports() {
            assert_eq!(number_of_ports(b"[number of ports] 1"), done(1));
        }

        #[test]
        fn test_header() {
            assert_eq!(
                header(b"\
                    [version] 2.0\n\
                    # Hz DB R 25\n\
                    [number of ports] 4\n\
                    [number of frequencies] 20\n\
                    [two-port order] 12_21\n\
                    [Number of noise frequenCIES] 1\n\
                    [Reference]\n\
                    25.0 50 5.0e1 100\n\
                "),
                done((
                    (2, 0),
                    OptionLine {
                        freq_units: FreqUnits::Hz,
                        domain: Domain::Scattering,
                        format: Format::DB,
                        system_impedance: Some(25.0),
                    },
                    4,
                    vec![
                        Metadata::NumberOfFrequencies(20),
                        Metadata::TwoPortOrder(TwoPortOrder::TPO_12_21),
                        Metadata::NumberOfNoiseFrequencies(1),
                        Metadata::Reference(vec![25.0, 50.0, 50.0, 100.0])
                    ]
                ))
            );
        }
    }
}
<commit_msg>Not the cleanest implementation, but I can improve it later.  The important thing is that I can parse a rather nasty looking header (minus the features I'm not supporting at this time and Begin/End Information blocks)<commit_after>#[macro_use]
extern crate nom;

#[allow(dead_code)]
pub mod ts {

    use std;
    use std::fmt;
    use std::collections::HashMap;
    use std::str::from_utf8;

    use nom::*;

    #[derive(Debug, PartialEq)]
    pub enum Format {
        RI,
        DB,
        MA,
    }

    impl fmt::Display for Format {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &Format::RI => "RI",
                &Format::DB => "DB",
                &Format::MA => "MA",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for Format {
        fn default() -> Format {
            Format::MA
        }
    }

    #[derive(Debug, PartialEq)]
    pub enum FreqUnits {
        Hz,
        KHz,
        MHz,
        GHz,
    }

    impl fmt::Display for FreqUnits {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &FreqUnits::Hz => "Hz",
                &FreqUnits::KHz => "KHz",
                &FreqUnits::MHz => "MHz",
                &FreqUnits::GHz => "GHz",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for FreqUnits {
        fn default() -> FreqUnits {
            FreqUnits::GHz
        }
    }

    #[derive(Debug, PartialEq)]
    pub enum Domain {
        Scattering,
        Transfer,
        Admittance,
        Impedance,
        HybridH,
        HybridG,
    }

    impl fmt::Display for Domain {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let val = match self {
                &Domain::Scattering => "S",
                &Domain::Transfer => "T",
                &Domain::Admittance => "Y",
                &Domain::Impedance => "Z",
                &Domain::HybridH => "H",
                &Domain::HybridG => "G",
            };
            write!(f, "{}", val)
        }
    }

    impl Default for Domain {
        fn default() -> Domain {
            Domain::Scattering
        }
    }

    #[derive(Debug, PartialEq)]
    pub struct OptionLine {
        pub freq_units: FreqUnits,
        pub domain: Domain,
        pub format: Format,
        pub system_impedance: Option<f64>,
    }

    impl Default for OptionLine {
        fn default() -> OptionLine {
            OptionLine {
                freq_units: Default::default(),
                domain: Default::default(),
                format: Default::default(),
                system_impedance: Default::default()
            }
        }
    }

    #[allow(non_camel_case_types)]
    #[derive(Debug, PartialEq)]
    pub enum TwoPortOrder {
        TPO_12_21,
        TPO_21_12,
    }

    #[derive(Debug, PartialEq)]
    pub struct NoisePoint {
        pub frequency: f64,
        pub minimum_noise_db: f64,
        pub source_refl_coeff_magnitude: f64,
        pub source_refl_coeff_phase: f64,
        pub effective_noise_resistance: f64,
    }

    #[derive(Debug)]
    pub struct Touchstone2 {
        pub num_ports: i32,
        pub option_line: OptionLine,
        pub two_port_order: Option<TwoPortOrder>,
        pub reference_impedance: Option<Vec<f64>>,
        pub information: HashMap<String, String>,
        pub network_data: Vec<Box<[(f64, f64)]>>,
        pub noise_data: Option<Box<[NoisePoint]>>,
    }

    fn parse<T: std::str::FromStr>(bytes: &[u8]) -> Option<T> {
        from_utf8(bytes).ok().and_then(|s| s.trim().parse().ok())
    }

    named!(
        int<i32>,
        map_res!(
            map_res!(
                ws!(digit),
                from_utf8
            ),
            std::str::FromStr::from_str
        )
    );

    fn is_valid_float_char(c: u8) -> bool {
        (c >= 48u8 && c <= 57) || // is_digit
        (c == 43u8 || c == 45u8 || c == 46u8) || // + or - or .
        (c == 69u8 || c == 101u8) // E or e
    }

    named!(
        float<f64>,
        map_res!(
            map_res!(
                take_while!(is_valid_float_char),
                from_utf8
            ),
            std::str::FromStr::from_str
        )
    );

    named!(
        comment_line<()>,
        value!((), delimited!(char!('!'), take_until!("\n"), char!('\n')))
    );

    named!(
        version<(i32, i32)>,
        do_parse!(
            tag_no_case!("[version]") >> many0!(space) >>
            ver: separated_pair!(int, char!('.'), int) >>
            opt!(complete!(comment_line)) >>
            (ver)
        )
    );

    fn not_whitespace(c: u8) -> bool {
        !(
            c == ' '  as u8 ||
            c == '\t' as u8 ||
            c == '\n' as u8 ||
            c == '\r' as u8
        )
    }

    named!(
        option_line<OptionLine>,
        do_parse!(
            char!('#') >>
            units: opt!(complete!(do_parse!(
                many1!(space) >>
                v: alt!(
                    value!(FreqUnits::Hz,  tag_no_case!("hz"))  |
                    value!(FreqUnits::KHz, tag_no_case!("khz")) |
                    value!(FreqUnits::MHz, tag_no_case!("mhz")) |
                    value!(FreqUnits::GHz, tag_no_case!("ghz"))
                ) >>
                (v)
            ))) >>
            domain: opt!(complete!(do_parse!(
                many1!(space) >>
                v: alt!(
                    value!(Domain::Scattering, tag_no_case!("s")) |
                    value!(Domain::Transfer,   tag_no_case!("t")) |
                    value!(Domain::Admittance, tag_no_case!("y")) |
                    value!(Domain::Impedance,  tag_no_case!("z")) |
                    value!(Domain::HybridH,    tag_no_case!("h")) |
                    value!(Domain::HybridG,    tag_no_case!("g"))
                ) >>
                (v)
            ))) >>
            format: opt!(complete!(do_parse!(
                many1!(space) >>
                v: alt!(
                    value!(Format::DB, tag_no_case!("db")) |
                    value!(Format::MA, tag_no_case!("ma")) |
                    value!(Format::RI, tag_no_case!("ri"))
                ) >>
                (v)
            ))) >>
            system_impedance: opt!(complete!(do_parse!(
                many1!(space) >>
                tag!("R") >>
                many1!(space) >>
                v: float >>
                (v)
            ))) >>
            opt!(complete!(comment_line)) >>
            (OptionLine {
                freq_units: units.unwrap_or(Default::default()),
                domain: domain.unwrap_or(Default::default()),
                format: format.unwrap_or(Default::default()),
                system_impedance: system_impedance
            })
        )
    );

    named!(
        number_of_ports<i32>,
        do_parse!(
            tag_no_case!("[number of ports]") >> many0!(space) >>
            num_ports: int >>
            opt!(complete!(comment_line)) >>
            (num_ports)
        )
    );

    named!(
        two_port_order<TwoPortOrder>,
        do_parse!(
            tag_no_case!("[two-port order]") >> many0!(space) >>
            tpo: alt!(
                value!(TwoPortOrder::TPO_12_21, tag!("12_21")) |
                value!(TwoPortOrder::TPO_21_12, tag!("21_12"))
            ) >>
            opt!(complete!(comment_line)) >>
            (tpo)
        )
    );

    named!(
        number_of_frequencies<i32>,
        do_parse!(
            tag_no_case!("[number of frequencies]") >> many0!(space) >>
            num_freqs: map_opt!(take_until!("\n"), parse) >>
            opt!(complete!(comment_line)) >>
            (num_freqs)
        )
    );

    named!(
        number_of_noise_frequencies<i32>,
        do_parse!(
            tag_no_case!("[number of noise frequencies]") >> many0!(space) >>
            num_freqs: map_opt!(take_until!("\n"), parse) >>
            opt!(complete!(comment_line)) >>
            (num_freqs)
        )
    );

    named!(
        reference<Vec<f64>>,
        do_parse!(
            tag_no_case!("[reference]") >> many0!(alt!(value!((), multispace) | comment_line)) >>
            refs: many1!(ws!(float)) >>
            opt!(complete!(comment_line)) >>
            (refs)
        )
    );

    named!(
        kwarg<(String, String)>,
        do_parse!(
            char!('[') >>
            kw: map_res!(
                take_until!("]"),
                |x| from_utf8(x).map(|s| s.trim().to_string())
            ) >>
            char!(']') >>
            many0!(space) >>
            arg: map_res!(
                take_until!("\n"),
                |x| from_utf8(x).map(|s| s.trim().to_string())
            ) >>
            opt!(complete!(comment_line)) >>
            (kw, "".to_string())
        )
    );

    named!(
        information<HashMap<String, String>>,
        ws!(delimited!(
            tag_no_case!("[begin information]"),
            map!(
                many0!(ws!(kwarg)),
                |v: Vec<(String, String)>| v.iter().cloned().collect()
            ),
            tag_no_case!("[end information]")
        ))
    );

    macro_rules! comments {
        ($i:expr, $($args:tt)*) => ({sep!($i, comment_line, $($args)*)})
    }

    #[allow(dead_code)]
    #[derive(Debug, PartialEq)]
    enum Metadata {
        TwoPortOrder(TwoPortOrder),
        NumberOfFrequencies(i32),
        NumberOfNoiseFrequencies(i32),
        Reference(Vec<f64>),
        Information(HashMap<String, String>),
    }

    named!(
        metadata<Vec<Metadata>>,
        map!(many0!(ws!(alt!(
            map!(two_port_order,              |x| Some(Metadata::TwoPortOrder(x))) |
            map!(number_of_frequencies,       |x| Some(Metadata::NumberOfFrequencies(x))) |
            map!(number_of_noise_frequencies, |x| Some(Metadata::NumberOfNoiseFrequencies(x))) |
            map!(reference,                   |x| Some(Metadata::Reference(x))) |
            value!(None, comment_line)
        ))),
        |v: Vec<Option<Metadata>>| v.into_iter().filter_map(|i| i).collect::<Vec<_>>()
        )
    );

    named!(
        header<((i32, i32), OptionLine, i32, Vec<Metadata>)>,
        ws!(do_parse!(
            opt!(complete!(comment_line)) >>
            ver: version >>
            opt!(complete!(comment_line)) >>
            opt_line: option_line >>
            opt!(complete!(comment_line)) >>
            num_ports: number_of_ports >>
            opt!(complete!(comment_line)) >>
            metadata: metadata >>
            opt!(complete!(comment_line)) >>
            (ver, opt_line, num_ports, metadata)
        ))
    );

    #[allow(unused_imports)]
    mod test {
        use super::*;
        use nom;

        fn done<'a, T>(val: T) -> nom::IResult<&'a [u8], T> {
            nom::IResult::Done(&b""[..], val)
        }

        #[test]
        fn test_comment_line() {
            assert_eq!(comment_line(b"!test\n"), done(()));
        }

        #[test]
        fn test_version() {
            assert_eq!(version(b"[version] 2.0"), done((2, 0)));
            assert_eq!(version(b"[version] 2.0 ! test\n"), done((2, 0)));
        }

        #[test]
        fn test_option_line() {
            assert_eq!(option_line(b"#"), done(Default::default()));
            assert_eq!(option_line(b"#!/usr/bin/bash\n"), done(Default::default()));
        }

        #[test]
        fn test_number_of_ports() {
            assert_eq!(number_of_ports(b"[number of ports] 1"), done(1));
            assert_eq!(number_of_ports(b"[number of ports] 1!\n"), done(1));
        }

        #[test]
        fn test_header() {
            assert_eq!(
                header(b"\
                    [version] 2.0\n\
                    # Hz DB R 25\n\
                    [number of ports] 4\n\
                    [number of frequencies] 20\n\
                    [two-port order] 12_21\n\
                    [Number of noise frequenCIES] 1\n\
                    [Reference]\n\
                    25.0 50 5.0e1 100\n\
                "),
                done((
                    (2, 0),
                    OptionLine {
                        freq_units: FreqUnits::Hz,
                        domain: Domain::Scattering,
                        format: Format::DB,
                        system_impedance: Some(25.0),
                    },
                    4,
                    vec![
                        Metadata::NumberOfFrequencies(20),
                        Metadata::TwoPortOrder(TwoPortOrder::TPO_12_21),
                        Metadata::NumberOfNoiseFrequencies(1),
                        Metadata::Reference(vec![25.0, 50.0, 50.0, 100.0])
                    ]
                ))
            );
            assert_eq!(
                header(b"\
                    !\n\
                    [version] 2.0\n\
                    # Hz DB R 25!foobar\n\
                    [number of ports] 4! = 24\n\
                    ! test\n\
                    [number of frequencies] 20\n\
                    ! test\n\
                    [two-port order] 12_21\n\
                    ! test\n\
                    [Number of noise frequenCIES] 1\n\
                    ! test\n\
                    [Reference]\n\
                    ! This one is just mean\n\
                    25.0 50 5.0e1 100\n\
                    ! test\n\
                "),
                done((
                    (2, 0),
                    OptionLine {
                        freq_units: FreqUnits::Hz,
                        domain: Domain::Scattering,
                        format: Format::DB,
                        system_impedance: Some(25.0),
                    },
                    4,
                    vec![
                        Metadata::NumberOfFrequencies(20),
                        Metadata::TwoPortOrder(TwoPortOrder::TPO_12_21),
                        Metadata::NumberOfNoiseFrequencies(1),
                        Metadata::Reference(vec![25.0, 50.0, 50.0, 100.0])
                    ]
                ))
            );
        }
    }
}
<|endoftext|>


<commit_before>// Module for parsing ISO Base Media Format aka video/mp4 streams.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

/// Basic ISO box structure.
pub struct BoxHeader {
    /// Four character box type
pub name: u32,
    /// Size of the box in bytes
pub size: u64,
    /// Offset to the start of the contained data (or header size).
pub offset: u64,
}

/// File type box 'ftyp'.
pub struct FileTypeBox {
    name: u32,
    size: u64,
    major_brand: u32,
    minor_version: u32,
    compatible_brands: Vec<u32>,
}

/// Movie header box 'mvhd'.
pub struct MovieHeaderBox {
    pub name: u32,
    pub size: u64,
    pub timescale: u32,
    pub duration: u64,
    // Ignore other fields.
}

pub struct TrackHeaderBox {
    pub name: u32,
    pub size: u64,
    pub track_id: u32,
    pub duration: u64,
    pub width: u32,
    pub height: u32,
}

extern crate byteorder;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom, Take};
use std::io::Cursor;

/// Parse a box out of a data buffer.
pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
    let tmp_size = try!(src.read_u32::<BigEndian>());
    let name = try!(src.read_u32::<BigEndian>());
    let size = match tmp_size {
        1 => try!(src.read_u64::<BigEndian>()),
        _ => tmp_size as u64,
    };
    assert!(size >= 8);
    if tmp_size == 1 {
        assert!(size >= 16);
    }
    let offset = match tmp_size {
        1 => 4 + 4 + 8,
        _ => 4 + 4,
    };
    assert!(offset <= size);
    Ok(BoxHeader{
      name: name,
      size: size,
      offset: offset,
    })
}

/// Parse the extra header fields for a full box.
fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> (u8, u32) {
    let version = src.read_u8().unwrap();
    let flags_a = src.read_u8().unwrap();
    let flags_b = src.read_u8().unwrap();
    let flags_c = src.read_u8().unwrap();
    (version, (flags_a as u32) << 16 |
              (flags_b as u32) <<  8 |
              (flags_c as u32))
}

/// Skip over the contents of a box.
pub fn skip_box_content<T: ReadBytesExt + Seek>
  (src: &mut T, header: &BoxHeader)
  -> std::io::Result<u64>
{
    src.seek(SeekFrom::Current((header.size - header.offset) as i64))
}

/// Helper to construct a Take over the contents of a box.
fn limit<'a, T: Read>(f: &'a mut T, h: &BoxHeader) -> Take<&'a mut T> {
    f.take(h.size - h.offset)
}

/// Helper to construct a Cursor over the contents of a box.
fn recurse<T: Read>(f: &mut T, h: &BoxHeader) -> Result<()> {
    println!("{} -- recursing", h);
    // FIXME: I couldn't figure out how to do this without copying.
    // We use Seek on the Read we return in skip_box_content, but
    // that trait isn't implemented for a Take like our limit()
    // returns. Slurping the buffer and wrapping it in a Cursor
    // functions as a work around.
    let buf: Vec<u8> = limit(f, &h)
        .bytes()
        .map(|u| u.unwrap())
        .collect();
    let mut content = Cursor::new(buf);
    loop {
        match read_box(&mut content) {
            Ok(_) => {},
            Err(e) => {
                println!("Error '{:?}' reading box", e.kind());
                return Err(e);
            },
        }
    }
    println!("{} -- end", h);
    Ok(())
}

/// Read the contents of a box, including sub boxes.
/// Right now it just prints the box value rather than
/// returning anything.
pub fn read_box<T: Read + Seek>(f: &mut T) -> Result<()> {
    read_box_header(f).and_then(|h| {
        match &(fourcc_to_string(h.name))[..] {
            "ftyp" => {
                let mut content = limit(f, &h);
                let ftyp = read_ftyp(&mut content, &h).unwrap();
                println!("{}", ftyp);
            },
            "moov" => try!(recurse(f, &h)),
            "mvhd" => {
                let mut content = limit(f, &h);
                let mvhd = read_mvhd(&mut content, &h).unwrap();
                println!("  {}", mvhd);
            },
            "trak" => try!(recurse(f, &h)),
            "tkhd" => {
                let mut content = limit(f, &h);
                let tkhd = read_tkhd(&mut content, &h).unwrap();
                println!("  {}", tkhd);
            },
            _ => {
                // Skip the contents of unknown chunks.
                println!("{} (skipped)", h);
                try!(skip_box_content(f, &h).and(Ok(())));
            },
        };
        Ok(()) // and_then needs a Result to return.
    })
}

/// Entry point for C language callers.
/// Take a buffer and call read_box() on it.
#[no_mangle]
pub unsafe extern fn read_box_from_buffer(buffer: *const u8, size: usize)
  -> bool {
    use std::slice;
    use std::thread;

    // Validate arguments from C.
    if buffer.is_null() || size < 8 {
        return false;
    }

    // Wrap the buffer we've been give in a slice.
    let b = slice::from_raw_parts(buffer, size);
    let mut c = Cursor::new(b);

    // Parse in a subthread.
    let task = thread::spawn(move || {
        read_box(&mut c).unwrap();
    });
    // Catch any panics.
    task.join().is_ok()
}

/// Parse an ftype box.
pub fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Option<FileTypeBox> {
    let major = src.read_u32::<BigEndian>().unwrap();
    let minor = src.read_u32::<BigEndian>().unwrap();
    let brand_count = (head.size - 8 - 8) /4;
    let mut brands = Vec::new();
    for _ in 0..brand_count {
        brands.push(src.read_u32::<BigEndian>().unwrap());
    }
    Some(FileTypeBox{
        name: head.name,
        size: head.size,
        major_brand: major,
        minor_version: minor,
        compatible_brands: brands,
    })
}

/// Parse an mvhd box.
pub fn read_mvhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Option<MovieHeaderBox> {
    let (version, _) = read_fullbox_extra(src);
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = src.read(&mut skip).unwrap();
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = src.read(&mut skip).unwrap();
            assert!(r == skip.len());
        },
        _ => panic!("invalid mhdr version"),
    }
    let timescale = src.read_u32::<BigEndian>().unwrap();
    let duration = match version {
        1 => src.read_u64::<BigEndian>().unwrap(),
        0 => src.read_u32::<BigEndian>().unwrap() as u64,
        _ => panic!("invalid mhdr version"),
    };
    // Skip remaining fields.
    let mut skip: Vec<u8> = vec![0; 80];
    let r = src.read(&mut skip).unwrap();
    assert!(r == skip.len());
    Some(MovieHeaderBox {
        name: head.name,
        size: head.size,
        timescale: timescale,
        duration: duration,
    })
}

/// Parse a tkhd box.
pub fn read_tkhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Result<TrackHeaderBox> {
    let (version, flags) = read_fullbox_extra(src);
    if flags & 0x1u32 == 0 || flags & 0x2u32 == 0 {
        return Err(Error::new(ErrorKind::Other, "Track is disabled"));
    }
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        _ => panic!("invalid tkhd version"),
    }
    let track_id = try!(src.read_u32::<BigEndian>());
    let _reserved = try!(src.read_u32::<BigEndian>());
    assert!(_reserved == 0);
    let duration = match version {
        1 => {
            try!(src.read_u64::<BigEndian>())
        },
        0 => try!(src.read_u32::<BigEndian>()) as u64,
        _ => panic!("invalid tkhd version"),
    };
    let _reserved = try!(src.read_u32::<BigEndian>());
    let _reserved = try!(src.read_u32::<BigEndian>());
    // Skip uninterested fields.
    let mut skip: Vec<u8> = vec![0; 44];
    let r = try!(src.read(&mut skip));
    assert!(r == skip.len());
    let width = try!(src.read_u32::<BigEndian>());
    let height = try!(src.read_u32::<BigEndian>());
    Ok(TrackHeaderBox {
        name: head.name,
        size: head.size,
        track_id: track_id,
        duration: duration,
        width: width,
        height: height,
    })
}

/// Convert the iso box type or other 4-character value to a string.
fn fourcc_to_string(name: u32) -> String {
    let u32_to_vec = |u| {
        vec!((u >> 24 & 0xffu32) as u8,
             (u >> 16 & 0xffu32) as u8,
             (u >>  8 & 0xffu32) as u8,
             (u & 0xffu32) as u8)
    };
    let name_bytes = u32_to_vec(name);
    String::from_utf8_lossy(&name_bytes).into_owned()
}

use std::fmt;
impl fmt::Display for BoxHeader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "'{}' {} bytes", fourcc_to_string(self.name), self.size)
    }
}

impl fmt::Display for FileTypeBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        let brand = fourcc_to_string(self.major_brand);
        write!(f, "'{}' {} bytes '{}' v{}", name, self.size,
            brand, self.minor_version)
    }
}

impl fmt::Display for MovieHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        write!(f, "'{}' {} bytes duration {}s", name, self.size,
            (self.duration as f64)/(self.timescale as f64))
    }
}

use std::u16;
impl fmt::Display for TrackHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        // Dimensions are 16.16 fixed-point.
        let base = u16::MAX as f64 + 1.0;
        let width = (self.width as f64) / base;
        let height = (self.height as f64) / base;
        write!(f, "'{}' {} bytes duration {} id {} {}x{}",
            name, self.size, self.duration, self.track_id,
            width, height)
    }
}

#[test]
fn test_read_box_header() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 8];  // minimal box length
    write!(&mut test, "test").unwrap(); // box type
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1952805748);
    assert_eq!(parsed.size, 8);
    println!("box {}", parsed);
}

#[test]
fn test_read_box_header_long() {
    use std::io::Cursor;
    let mut test: Vec<u8> = vec![0, 0, 0, 1]; // long box extension code
    test.extend("long".to_string().into_bytes()); // box type
    test.extend(vec![0, 0, 0, 0, 0, 0, 16, 0]); // 64 bit size
    // Skip generating box content.
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1819242087);
    assert_eq!(parsed.size, 4096);
    println!("box {}", parsed);
}

#[test]
fn test_read_ftyp() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 24]; // size
    write!(&mut test, "ftyp").unwrap(); // type
    write!(&mut test, "mp42").unwrap(); // major brand
    test.extend(vec![0, 0, 0, 0]);      // minor version
    write!(&mut test, "isom").unwrap(); // compatible brands...
    write!(&mut test, "mp42").unwrap();
    assert_eq!(test.len(), 24);

    let mut stream = Cursor::new(test);
    let header = read_box_header(&mut stream).unwrap();
    let parsed = read_ftyp(&mut stream, &header).unwrap();
    assert_eq!(parsed.name, 1718909296);
    assert_eq!(parsed.size, 24);
    assert_eq!(parsed.major_brand, 1836069938);
    assert_eq!(parsed.minor_version, 0);
    assert_eq!(parsed.compatible_brands.len(), 2);
    println!("box {}", parsed);
}
<commit_msg>Return Result from read_mvhd.<commit_after>// Module for parsing ISO Base Media Format aka video/mp4 streams.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

/// Basic ISO box structure.
pub struct BoxHeader {
    /// Four character box type
pub name: u32,
    /// Size of the box in bytes
pub size: u64,
    /// Offset to the start of the contained data (or header size).
pub offset: u64,
}

/// File type box 'ftyp'.
pub struct FileTypeBox {
    name: u32,
    size: u64,
    major_brand: u32,
    minor_version: u32,
    compatible_brands: Vec<u32>,
}

/// Movie header box 'mvhd'.
pub struct MovieHeaderBox {
    pub name: u32,
    pub size: u64,
    pub timescale: u32,
    pub duration: u64,
    // Ignore other fields.
}

pub struct TrackHeaderBox {
    pub name: u32,
    pub size: u64,
    pub track_id: u32,
    pub duration: u64,
    pub width: u32,
    pub height: u32,
}

extern crate byteorder;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom, Take};
use std::io::Cursor;

/// Parse a box out of a data buffer.
pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
    let tmp_size = try!(src.read_u32::<BigEndian>());
    let name = try!(src.read_u32::<BigEndian>());
    let size = match tmp_size {
        1 => try!(src.read_u64::<BigEndian>()),
        _ => tmp_size as u64,
    };
    assert!(size >= 8);
    if tmp_size == 1 {
        assert!(size >= 16);
    }
    let offset = match tmp_size {
        1 => 4 + 4 + 8,
        _ => 4 + 4,
    };
    assert!(offset <= size);
    Ok(BoxHeader{
      name: name,
      size: size,
      offset: offset,
    })
}

/// Parse the extra header fields for a full box.
fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> (u8, u32) {
    let version = src.read_u8().unwrap();
    let flags_a = src.read_u8().unwrap();
    let flags_b = src.read_u8().unwrap();
    let flags_c = src.read_u8().unwrap();
    (version, (flags_a as u32) << 16 |
              (flags_b as u32) <<  8 |
              (flags_c as u32))
}

/// Skip over the contents of a box.
pub fn skip_box_content<T: ReadBytesExt + Seek>
  (src: &mut T, header: &BoxHeader)
  -> std::io::Result<u64>
{
    src.seek(SeekFrom::Current((header.size - header.offset) as i64))
}

/// Helper to construct a Take over the contents of a box.
fn limit<'a, T: Read>(f: &'a mut T, h: &BoxHeader) -> Take<&'a mut T> {
    f.take(h.size - h.offset)
}

/// Helper to construct a Cursor over the contents of a box.
fn recurse<T: Read>(f: &mut T, h: &BoxHeader) -> Result<()> {
    println!("{} -- recursing", h);
    // FIXME: I couldn't figure out how to do this without copying.
    // We use Seek on the Read we return in skip_box_content, but
    // that trait isn't implemented for a Take like our limit()
    // returns. Slurping the buffer and wrapping it in a Cursor
    // functions as a work around.
    let buf: Vec<u8> = limit(f, &h)
        .bytes()
        .map(|u| u.unwrap())
        .collect();
    let mut content = Cursor::new(buf);
    loop {
        match read_box(&mut content) {
            Ok(_) => {},
            Err(e) => {
                println!("Error '{:?}' reading box", e.kind());
                return Err(e);
            },
        }
    }
    println!("{} -- end", h);
    Ok(())
}

/// Read the contents of a box, including sub boxes.
/// Right now it just prints the box value rather than
/// returning anything.
pub fn read_box<T: Read + Seek>(f: &mut T) -> Result<()> {
    read_box_header(f).and_then(|h| {
        match &(fourcc_to_string(h.name))[..] {
            "ftyp" => {
                let mut content = limit(f, &h);
                let ftyp = read_ftyp(&mut content, &h).unwrap();
                println!("{}", ftyp);
            },
            "moov" => try!(recurse(f, &h)),
            "mvhd" => {
                let mut content = limit(f, &h);
                let mvhd = read_mvhd(&mut content, &h).unwrap();
                println!("  {}", mvhd);
            },
            "trak" => try!(recurse(f, &h)),
            "tkhd" => {
                let mut content = limit(f, &h);
                let tkhd = read_tkhd(&mut content, &h).unwrap();
                println!("  {}", tkhd);
            },
            _ => {
                // Skip the contents of unknown chunks.
                println!("{} (skipped)", h);
                try!(skip_box_content(f, &h).and(Ok(())));
            },
        };
        Ok(()) // and_then needs a Result to return.
    })
}

/// Entry point for C language callers.
/// Take a buffer and call read_box() on it.
#[no_mangle]
pub unsafe extern fn read_box_from_buffer(buffer: *const u8, size: usize)
  -> bool {
    use std::slice;
    use std::thread;

    // Validate arguments from C.
    if buffer.is_null() || size < 8 {
        return false;
    }

    // Wrap the buffer we've been give in a slice.
    let b = slice::from_raw_parts(buffer, size);
    let mut c = Cursor::new(b);

    // Parse in a subthread.
    let task = thread::spawn(move || {
        read_box(&mut c).unwrap();
    });
    // Catch any panics.
    task.join().is_ok()
}

/// Parse an ftype box.
pub fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Option<FileTypeBox> {
    let major = src.read_u32::<BigEndian>().unwrap();
    let minor = src.read_u32::<BigEndian>().unwrap();
    let brand_count = (head.size - 8 - 8) /4;
    let mut brands = Vec::new();
    for _ in 0..brand_count {
        brands.push(src.read_u32::<BigEndian>().unwrap());
    }
    Some(FileTypeBox{
        name: head.name,
        size: head.size,
        major_brand: major,
        minor_version: minor,
        compatible_brands: brands,
    })
}

/// Parse an mvhd box.
pub fn read_mvhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Result<MovieHeaderBox> {
    let (version, _) = read_fullbox_extra(src);
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        _ => panic!("invalid mhdr version"),
    }
    let timescale = src.read_u32::<BigEndian>().unwrap();
    let duration = match version {
        1 => try!(src.read_u64::<BigEndian>()),
        0 => try!(src.read_u32::<BigEndian>()) as u64,
        _ => panic!("invalid mhdr version"),
    };
    // Skip remaining fields.
    let mut skip: Vec<u8> = vec![0; 80];
    let r = try!(src.read(&mut skip));
    assert!(r == skip.len());
    Ok(MovieHeaderBox {
        name: head.name,
        size: head.size,
        timescale: timescale,
        duration: duration,
    })
}

/// Parse a tkhd box.
pub fn read_tkhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Result<TrackHeaderBox> {
    let (version, flags) = read_fullbox_extra(src);
    if flags & 0x1u32 == 0 || flags & 0x2u32 == 0 {
        return Err(Error::new(ErrorKind::Other, "Track is disabled"));
    }
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = try!(src.read(&mut skip));
            assert!(r == skip.len());
        },
        _ => panic!("invalid tkhd version"),
    }
    let track_id = try!(src.read_u32::<BigEndian>());
    let _reserved = try!(src.read_u32::<BigEndian>());
    assert!(_reserved == 0);
    let duration = match version {
        1 => {
            try!(src.read_u64::<BigEndian>())
        },
        0 => try!(src.read_u32::<BigEndian>()) as u64,
        _ => panic!("invalid tkhd version"),
    };
    let _reserved = try!(src.read_u32::<BigEndian>());
    let _reserved = try!(src.read_u32::<BigEndian>());
    // Skip uninterested fields.
    let mut skip: Vec<u8> = vec![0; 44];
    let r = try!(src.read(&mut skip));
    assert!(r == skip.len());
    let width = try!(src.read_u32::<BigEndian>());
    let height = try!(src.read_u32::<BigEndian>());
    Ok(TrackHeaderBox {
        name: head.name,
        size: head.size,
        track_id: track_id,
        duration: duration,
        width: width,
        height: height,
    })
}

/// Convert the iso box type or other 4-character value to a string.
fn fourcc_to_string(name: u32) -> String {
    let u32_to_vec = |u| {
        vec!((u >> 24 & 0xffu32) as u8,
             (u >> 16 & 0xffu32) as u8,
             (u >>  8 & 0xffu32) as u8,
             (u & 0xffu32) as u8)
    };
    let name_bytes = u32_to_vec(name);
    String::from_utf8_lossy(&name_bytes).into_owned()
}

use std::fmt;
impl fmt::Display for BoxHeader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "'{}' {} bytes", fourcc_to_string(self.name), self.size)
    }
}

impl fmt::Display for FileTypeBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        let brand = fourcc_to_string(self.major_brand);
        write!(f, "'{}' {} bytes '{}' v{}", name, self.size,
            brand, self.minor_version)
    }
}

impl fmt::Display for MovieHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        write!(f, "'{}' {} bytes duration {}s", name, self.size,
            (self.duration as f64)/(self.timescale as f64))
    }
}

use std::u16;
impl fmt::Display for TrackHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        // Dimensions are 16.16 fixed-point.
        let base = u16::MAX as f64 + 1.0;
        let width = (self.width as f64) / base;
        let height = (self.height as f64) / base;
        write!(f, "'{}' {} bytes duration {} id {} {}x{}",
            name, self.size, self.duration, self.track_id,
            width, height)
    }
}

#[test]
fn test_read_box_header() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 8];  // minimal box length
    write!(&mut test, "test").unwrap(); // box type
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1952805748);
    assert_eq!(parsed.size, 8);
    println!("box {}", parsed);
}

#[test]
fn test_read_box_header_long() {
    use std::io::Cursor;
    let mut test: Vec<u8> = vec![0, 0, 0, 1]; // long box extension code
    test.extend("long".to_string().into_bytes()); // box type
    test.extend(vec![0, 0, 0, 0, 0, 0, 16, 0]); // 64 bit size
    // Skip generating box content.
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1819242087);
    assert_eq!(parsed.size, 4096);
    println!("box {}", parsed);
}

#[test]
fn test_read_ftyp() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 24]; // size
    write!(&mut test, "ftyp").unwrap(); // type
    write!(&mut test, "mp42").unwrap(); // major brand
    test.extend(vec![0, 0, 0, 0]);      // minor version
    write!(&mut test, "isom").unwrap(); // compatible brands...
    write!(&mut test, "mp42").unwrap();
    assert_eq!(test.len(), 24);

    let mut stream = Cursor::new(test);
    let header = read_box_header(&mut stream).unwrap();
    let parsed = read_ftyp(&mut stream, &header).unwrap();
    assert_eq!(parsed.name, 1718909296);
    assert_eq!(parsed.size, 24);
    assert_eq!(parsed.major_brand, 1836069938);
    assert_eq!(parsed.minor_version, 0);
    assert_eq!(parsed.compatible_brands.len(), 2);
    println!("box {}", parsed);
}
<|endoftext|>


<commit_before>#![cfg_attr(feature="nightly", feature(std_misc))]
#![doc(html_root_url = "http://ogeon.github.io/docs/rust-wiringpi/master/")]

extern crate libc;

use std::marker::PhantomData;

use pin::{Pin, RequiresRoot};

macro_rules! impl_pins {
    ($($name:ident),+) => (
        $(
            #[derive(Clone, Copy)]
            pub struct $name;

            impl Pin for $name {}
        )+
    )
}

macro_rules! require_root {
    ($($name:ident: $pwm:expr),+) => (
        $(
            impl RequiresRoot for $name {
                #[inline]
                fn pwm_pin() -> PwmPin<$name> {
                    PwmPin::new($pwm)
                }
            }
        )+
    )
}

mod bindings;

pub mod time {
    use bindings;
    #[cfg(feature="nightly")]
    use std::time::Duration;

/**

This causes program execution to pause for at least the provided duration in
milliseconds.

Due to the multi-tasking nature of Linux it could be longer. Note that the
maximum delay is an unsigned 32-bit integer or approximately 49 days.

*/
    #[cfg(feature="nightly")]
    pub fn delay(duration: Duration) {
        use libc;

        let duration = duration.num_milliseconds();
        if duration <= 0 {
            return;
        }

        unsafe {
            bindings::delay(duration as libc::c_uint);
        }
    }

/**

This causes program execution to pause for at least the provided number of
milliseconds.

Due to the multi-tasking nature of Linux it could be longer. Note that the
maximum delay is an unsigned 32-bit integer or approximately 49 days.

*/
    #[cfg(not(feature="nightly"))]
    pub fn delay(duration: u32) {
        unsafe {
            bindings::delay(duration);
        }
    }

/**

This causes program execution to pause for at least the provided number of
microseconds.

Due to the multi-tasking nature of Linux it could be longer. Note that the
maximum delay is an unsigned 32-bit integer microseconds or approximately 71
minutes.

*/
    pub fn delay_microseconds(microseconds: u32) {
        unsafe {
            bindings::delayMicroseconds(microseconds);
        }
    }
}

pub mod thread {
    use bindings;
    use libc;

/**

This attempts to shift your program (or thread in a multi-threaded
program) to a higher priority and enables a real-time scheduling.

The priority parameter should be from 0 (the default) to 99 (the maximum).
This won’t make your program go any faster, but it will give it a bigger slice
of time when other programs are running. The priority parameter works relative
to others – so you can make one program priority 1 and another priority 2 and
it will have the same effect as setting one to 10 and the other to 90 (as long
as no other programs are running with elevated priorities)

The return value is `true` for success and `false` for error. If an error is returned,
the program should then consult the _errno_ global variable, as per the usual
conventions.

_Note_: Only programs running as root can change their priority. If called from
a non-root program then nothing happens.

*/
    pub fn priority(priority: u8) -> bool {
        unsafe {
            bindings::piHiPri(priority as libc::c_int) >= 0
        }
    }
}

pub mod pin {
    use bindings;
    use libc;
    use self::Value::{Low, High};

    use std::marker::PhantomData;

    impl_pins!(WiringPi, Gpio, Sys);
    require_root!(WiringPi: 1, Gpio: 18);

    pub trait Pin {}

    pub trait RequiresRoot: Pin {
        fn pwm_pin() -> PwmPin<Self>;
    }

    #[derive(Clone, Copy)]
    pub enum Value {
        Low = 0,
        High
    }

    #[derive(Clone, Copy)]
    pub enum Pull {
        Off = 0,
        Down,
        Up
    }

    #[derive(Clone, Copy)]
    pub enum PwmMode {
        MarkSpace = 0,
        Balanced
    }

    pub struct InputPin<Pin>(libc::c_int, PhantomData<Pin>);

    impl<P: Pin> InputPin<P> {
        pub fn new(pin: libc::c_int) -> InputPin<P> {
            unsafe {
                bindings::pinMode(pin, 0);
            }

            InputPin(pin, PhantomData)
        }

        #[inline]
        pub fn number(&self) -> libc::c_int {
            let &InputPin(number, _) = self;
            number
        }

        pub fn into_output(self) -> OutputPin<P> {
            let InputPin(number, _) = self;
            OutputPin::new(number)
        }

        ///This function returns the value read at the given pin.
        ///
        ///It will be `High` or `Low` (1 or 0) depending on the logic level at the pin.
        pub fn digital_read(&self) -> Value {
            let value = unsafe {
                bindings::digitalRead(self.number())
            };

            if value == 0 {
                Low
            } else {
                High
            }
        }
    }

    impl<P: Pin + RequiresRoot> InputPin<P> {
        pub fn into_pwm(self) -> PwmPin<P> {
            let InputPin(number, _) = self;
            PwmPin::new(number)
        }
/**

This sets the pull-up or pull-down resistor mode on the given pin.

Unlike the Arduino, the BCM2835 has both pull-up an down internal resistors.
The parameter pud should be; `Off`, (no pull up/down), `Down` (pull to ground)
or `Up` (pull to 3.3v)

*/
        pub fn pull_up_dn_control(&self, pud: Pull) {
            unsafe {
                bindings::pullUpDnControl(self.number(), pud as libc::c_int);
            }
        }
    }

    pub struct OutputPin<Pin>(libc::c_int, PhantomData<Pin>);

    impl<P: Pin> OutputPin<P> {
        pub fn new(pin: libc::c_int) -> OutputPin<P> {
            unsafe {
                bindings::pinMode(pin, 1);
            }

            OutputPin(pin, PhantomData)
        }

        #[inline]
        pub fn number(&self) -> libc::c_int {
            let &OutputPin(number, _) = self;
            number
        }

        pub fn into_input(self) -> InputPin<P> {
            let OutputPin(number, _) = self;
            InputPin::new(number)
        }

        ///Writes the value `High` or `Low` (1 or 0) to the given pin which must have been previously set as an output.
        pub fn digital_write(&self, value: Value) {
            unsafe {
                bindings::digitalWrite(self.number(), value as libc::c_int);
            }
        }
    }

    impl<P: Pin + RequiresRoot> OutputPin<P> {
        pub fn into_pwm(self) -> PwmPin<P> {
            let OutputPin(number, _) = self;
            PwmPin::new(number)
        }
    }

    ///To understand more about the PWM system, you’ll need to read the Broadcom ARM peripherals manual.
    pub struct PwmPin<Pin>(libc::c_int, PhantomData<Pin>);

    impl<P: Pin + RequiresRoot> PwmPin<P> {
        pub fn new(pin: libc::c_int) -> PwmPin<P> {
            unsafe {
                bindings::pinMode(pin, 2);
            }

            PwmPin(pin, PhantomData)
        }

        #[inline]
        pub fn number(&self) -> libc::c_int {
            let &PwmPin(number, _) = self;
            number
        }

        pub fn into_input(self) -> InputPin<P> {
            let PwmPin(number, _) = self;
            InputPin::new(number)
        }

        pub fn into_output(self) -> OutputPin<P> {
            let PwmPin(number, _) = self;
            OutputPin::new(number)
        }

        ///Writes the value to the PWM register for the given pin.
        ///
        ///The value must be between 0 and 1024.
        pub fn write(&self, value: u16) {
            unsafe {
                bindings::pwmWrite(self.number(), value as libc::c_int);
            }
        }

/**

The PWM generator can run in 2 modes – "balanced" and "mark:space".

The mark:space mode is traditional, however the default mode in the Pi is
"balanced". You can switch modes by supplying the parameter: `Balanced` or
`MarkSpace`.

*/
        pub fn set_mode(&self, mode: PwmMode) {
            unsafe {
                bindings::pwmSetMode(mode as libc::c_int);
            }
        }

        ///This sets the range register in the PWM generator. The default is 1024.
        pub fn set_range(&self, value: u16) {
            unsafe {
                bindings::pwmSetRange(value as libc::c_uint);
            }
        }

        ///This sets the divisor for the PWM clock.
        pub fn set_clock(&self, value: u16) {
            unsafe {
                bindings::pwmSetClock(value as libc::c_int);
            }
        }
    }
}

/**

This initialises the wiringPi system and assumes that the calling program is
going to be using the **wiringPi** pin numbering scheme.

This is a simplified numbering scheme which provides a mapping from virtual
pin numbers 0 through 16 to the real underlying Broadcom GPIO pin numbers. See
the pins page for a table which maps the **wiringPi** pin number to the Broadcom
GPIO pin number to the physical location on the edge connector.

This function needs to be called with root privileges.

*/
pub fn setup() -> Option<WiringPi<pin::WiringPi>> {
    unsafe {
        if bindings::wiringPiSetup() >= 0 {
            Some(WiringPi(PhantomData))
        } else {
            None
        }
    }
}

/**

This is identical `setup()`, however it allows the calling programs to use the
Broadcom GPIO pin numbers directly with no re-mapping.

This function needs to be called with root privileges.

*/
pub fn setup_gpio() -> Option<WiringPi<pin::Gpio>> {
    unsafe {
        if bindings::wiringPiSetupGpio() >= 0 {
            Some(WiringPi(PhantomData))
        } else {
            None
        }
    }
}

/**

This initialises the wiringPi system but uses the /sys/class/gpio interface
rather than accessing the hardware directly.

This can be called as a non-root user provided the GPIO pins have been
exported before-hand using the gpio program. Pin number in this mode is the
native Broadcom GPIO numbers.

_Note_: In this mode you can only use the pins which have been exported via the
/sys/class/gpio interface. You must export these pins before you call your
program. You can do this in a separate shell-script, or by using the system()
function from inside your program.

Also note that some functions have no effect when using this mode as they’re
not currently possible to action unless called with root privileges.

*/
pub fn setup_sys() -> Option<WiringPi<pin::Sys>> {
    unsafe {
        if bindings::wiringPiSetupSys() >= 0 {
            Some(WiringPi(PhantomData))
        } else {
            None
        }
    }
}

/**

This returns the board revision of the Raspberry Pi.

It will be either 1 or 2. Some of the BCM_GPIO pins changed number and
function when moving from board revision 1 to 2, so if you are using BCM_GPIO
pin numbers, then you need to be aware of the differences.

*/
pub fn board_revision() -> i32 {
    unsafe {
        bindings::piBoardRev()
    }
}

///This returns the BCM_GPIO pin number of the supplied **wiringPi** pin.
///
///It takes the board revision into account.
pub fn to_gpio_number(wpi_number: u16) -> u16 {
    unsafe {
        bindings::wpiPinToGpio(wpi_number as libc::c_int) as u16
    }
}

pub struct WiringPi<Pin>(PhantomData<Pin>);

impl<P: Pin> WiringPi<P> {
    pub fn input_pin(&self, pin: u16) -> pin::InputPin<P> {
        let pin = pin as libc::c_int;
        pin::InputPin::new(pin)
    }

    pub fn output_pin(&self, pin: u16) -> pin::OutputPin<P> {
        let pin = pin as libc::c_int;
        pin::OutputPin::new(pin)
    }
/**

This returns a number representing the number if milliseconds since your
program called one of the setup functions.

It returns an unsigned 32-bit number which wraps after 49 days.

*/

    pub fn millis(&self) -> u32 {
        unsafe {
            bindings::millis()
        }
    }

/**

This returns a number representing the number if microseconds since your
program called one of the setup functions.

It returns an unsigned 32-bit number which wraps after 71 minutes.

*/

    pub fn micros(&self) -> u32 {
        unsafe {
            bindings::micros()
        }
    }
}

impl<P: RequiresRoot + Pin> WiringPi<P> {
    pub fn pwm_pin(&self) -> pin::PwmPin<P> {
        RequiresRoot::pwm_pin()
    }
}<commit_msg>Update delay(...) for the new Duration implementation<commit_after>#![cfg_attr(feature="nightly", feature(duration))]
#![doc(html_root_url = "http://ogeon.github.io/docs/rust-wiringpi/master/")]

extern crate libc;

use std::marker::PhantomData;

use pin::{Pin, RequiresRoot};

macro_rules! impl_pins {
    ($($name:ident),+) => (
        $(
            #[derive(Clone, Copy)]
            pub struct $name;

            impl Pin for $name {}
        )+
    )
}

macro_rules! require_root {
    ($($name:ident: $pwm:expr),+) => (
        $(
            impl RequiresRoot for $name {
                #[inline]
                fn pwm_pin() -> PwmPin<$name> {
                    PwmPin::new($pwm)
                }
            }
        )+
    )
}

mod bindings;

pub mod time {
    use bindings;
    #[cfg(feature="nightly")]
    use std::time::Duration;

/**

This causes program execution to pause for at least the provided duration in
milliseconds.

Due to the multi-tasking nature of Linux it could be longer. Note that the
maximum delay is an unsigned 32-bit integer or approximately 49 days.

*/
    #[cfg(feature="nightly")]
    pub fn delay(duration: Duration) {
        use libc;

        let duration = (duration.secs() * 1000) as u32 + duration.extra_nanos() / 1_000_000;
        if duration <= 0 {
            return;
        }

        unsafe {
            bindings::delay(duration as libc::c_uint);
        }
    }

/**

This causes program execution to pause for at least the provided number of
milliseconds.

Due to the multi-tasking nature of Linux it could be longer. Note that the
maximum delay is an unsigned 32-bit integer or approximately 49 days.

*/
    #[cfg(not(feature="nightly"))]
    pub fn delay(duration: u32) {
        unsafe {
            bindings::delay(duration);
        }
    }

/**

This causes program execution to pause for at least the provided number of
microseconds.

Due to the multi-tasking nature of Linux it could be longer. Note that the
maximum delay is an unsigned 32-bit integer microseconds or approximately 71
minutes.

*/
    pub fn delay_microseconds(microseconds: u32) {
        unsafe {
            bindings::delayMicroseconds(microseconds);
        }
    }
}

pub mod thread {
    use bindings;
    use libc;

/**

This attempts to shift your program (or thread in a multi-threaded
program) to a higher priority and enables a real-time scheduling.

The priority parameter should be from 0 (the default) to 99 (the maximum).
This won’t make your program go any faster, but it will give it a bigger slice
of time when other programs are running. The priority parameter works relative
to others – so you can make one program priority 1 and another priority 2 and
it will have the same effect as setting one to 10 and the other to 90 (as long
as no other programs are running with elevated priorities)

The return value is `true` for success and `false` for error. If an error is returned,
the program should then consult the _errno_ global variable, as per the usual
conventions.

_Note_: Only programs running as root can change their priority. If called from
a non-root program then nothing happens.

*/
    pub fn priority(priority: u8) -> bool {
        unsafe {
            bindings::piHiPri(priority as libc::c_int) >= 0
        }
    }
}

pub mod pin {
    use bindings;
    use libc;
    use self::Value::{Low, High};

    use std::marker::PhantomData;

    impl_pins!(WiringPi, Gpio, Sys);
    require_root!(WiringPi: 1, Gpio: 18);

    pub trait Pin {}

    pub trait RequiresRoot: Pin {
        fn pwm_pin() -> PwmPin<Self>;
    }

    #[derive(Clone, Copy)]
    pub enum Value {
        Low = 0,
        High
    }

    #[derive(Clone, Copy)]
    pub enum Pull {
        Off = 0,
        Down,
        Up
    }

    #[derive(Clone, Copy)]
    pub enum PwmMode {
        MarkSpace = 0,
        Balanced
    }

    pub struct InputPin<Pin>(libc::c_int, PhantomData<Pin>);

    impl<P: Pin> InputPin<P> {
        pub fn new(pin: libc::c_int) -> InputPin<P> {
            unsafe {
                bindings::pinMode(pin, 0);
            }

            InputPin(pin, PhantomData)
        }

        #[inline]
        pub fn number(&self) -> libc::c_int {
            let &InputPin(number, _) = self;
            number
        }

        pub fn into_output(self) -> OutputPin<P> {
            let InputPin(number, _) = self;
            OutputPin::new(number)
        }

        ///This function returns the value read at the given pin.
        ///
        ///It will be `High` or `Low` (1 or 0) depending on the logic level at the pin.
        pub fn digital_read(&self) -> Value {
            let value = unsafe {
                bindings::digitalRead(self.number())
            };

            if value == 0 {
                Low
            } else {
                High
            }
        }
    }

    impl<P: Pin + RequiresRoot> InputPin<P> {
        pub fn into_pwm(self) -> PwmPin<P> {
            let InputPin(number, _) = self;
            PwmPin::new(number)
        }
/**

This sets the pull-up or pull-down resistor mode on the given pin.

Unlike the Arduino, the BCM2835 has both pull-up an down internal resistors.
The parameter pud should be; `Off`, (no pull up/down), `Down` (pull to ground)
or `Up` (pull to 3.3v)

*/
        pub fn pull_up_dn_control(&self, pud: Pull) {
            unsafe {
                bindings::pullUpDnControl(self.number(), pud as libc::c_int);
            }
        }
    }

    pub struct OutputPin<Pin>(libc::c_int, PhantomData<Pin>);

    impl<P: Pin> OutputPin<P> {
        pub fn new(pin: libc::c_int) -> OutputPin<P> {
            unsafe {
                bindings::pinMode(pin, 1);
            }

            OutputPin(pin, PhantomData)
        }

        #[inline]
        pub fn number(&self) -> libc::c_int {
            let &OutputPin(number, _) = self;
            number
        }

        pub fn into_input(self) -> InputPin<P> {
            let OutputPin(number, _) = self;
            InputPin::new(number)
        }

        ///Writes the value `High` or `Low` (1 or 0) to the given pin which must have been previously set as an output.
        pub fn digital_write(&self, value: Value) {
            unsafe {
                bindings::digitalWrite(self.number(), value as libc::c_int);
            }
        }
    }

    impl<P: Pin + RequiresRoot> OutputPin<P> {
        pub fn into_pwm(self) -> PwmPin<P> {
            let OutputPin(number, _) = self;
            PwmPin::new(number)
        }
    }

    ///To understand more about the PWM system, you’ll need to read the Broadcom ARM peripherals manual.
    pub struct PwmPin<Pin>(libc::c_int, PhantomData<Pin>);

    impl<P: Pin + RequiresRoot> PwmPin<P> {
        pub fn new(pin: libc::c_int) -> PwmPin<P> {
            unsafe {
                bindings::pinMode(pin, 2);
            }

            PwmPin(pin, PhantomData)
        }

        #[inline]
        pub fn number(&self) -> libc::c_int {
            let &PwmPin(number, _) = self;
            number
        }

        pub fn into_input(self) -> InputPin<P> {
            let PwmPin(number, _) = self;
            InputPin::new(number)
        }

        pub fn into_output(self) -> OutputPin<P> {
            let PwmPin(number, _) = self;
            OutputPin::new(number)
        }

        ///Writes the value to the PWM register for the given pin.
        ///
        ///The value must be between 0 and 1024.
        pub fn write(&self, value: u16) {
            unsafe {
                bindings::pwmWrite(self.number(), value as libc::c_int);
            }
        }

/**

The PWM generator can run in 2 modes – "balanced" and "mark:space".

The mark:space mode is traditional, however the default mode in the Pi is
"balanced". You can switch modes by supplying the parameter: `Balanced` or
`MarkSpace`.

*/
        pub fn set_mode(&self, mode: PwmMode) {
            unsafe {
                bindings::pwmSetMode(mode as libc::c_int);
            }
        }

        ///This sets the range register in the PWM generator. The default is 1024.
        pub fn set_range(&self, value: u16) {
            unsafe {
                bindings::pwmSetRange(value as libc::c_uint);
            }
        }

        ///This sets the divisor for the PWM clock.
        pub fn set_clock(&self, value: u16) {
            unsafe {
                bindings::pwmSetClock(value as libc::c_int);
            }
        }
    }
}

/**

This initialises the wiringPi system and assumes that the calling program is
going to be using the **wiringPi** pin numbering scheme.

This is a simplified numbering scheme which provides a mapping from virtual
pin numbers 0 through 16 to the real underlying Broadcom GPIO pin numbers. See
the pins page for a table which maps the **wiringPi** pin number to the Broadcom
GPIO pin number to the physical location on the edge connector.

This function needs to be called with root privileges.

*/
pub fn setup() -> Option<WiringPi<pin::WiringPi>> {
    unsafe {
        if bindings::wiringPiSetup() >= 0 {
            Some(WiringPi(PhantomData))
        } else {
            None
        }
    }
}

/**

This is identical `setup()`, however it allows the calling programs to use the
Broadcom GPIO pin numbers directly with no re-mapping.

This function needs to be called with root privileges.

*/
pub fn setup_gpio() -> Option<WiringPi<pin::Gpio>> {
    unsafe {
        if bindings::wiringPiSetupGpio() >= 0 {
            Some(WiringPi(PhantomData))
        } else {
            None
        }
    }
}

/**

This initialises the wiringPi system but uses the /sys/class/gpio interface
rather than accessing the hardware directly.

This can be called as a non-root user provided the GPIO pins have been
exported before-hand using the gpio program. Pin number in this mode is the
native Broadcom GPIO numbers.

_Note_: In this mode you can only use the pins which have been exported via the
/sys/class/gpio interface. You must export these pins before you call your
program. You can do this in a separate shell-script, or by using the system()
function from inside your program.

Also note that some functions have no effect when using this mode as they’re
not currently possible to action unless called with root privileges.

*/
pub fn setup_sys() -> Option<WiringPi<pin::Sys>> {
    unsafe {
        if bindings::wiringPiSetupSys() >= 0 {
            Some(WiringPi(PhantomData))
        } else {
            None
        }
    }
}

/**

This returns the board revision of the Raspberry Pi.

It will be either 1 or 2. Some of the BCM_GPIO pins changed number and
function when moving from board revision 1 to 2, so if you are using BCM_GPIO
pin numbers, then you need to be aware of the differences.

*/
pub fn board_revision() -> i32 {
    unsafe {
        bindings::piBoardRev()
    }
}

///This returns the BCM_GPIO pin number of the supplied **wiringPi** pin.
///
///It takes the board revision into account.
pub fn to_gpio_number(wpi_number: u16) -> u16 {
    unsafe {
        bindings::wpiPinToGpio(wpi_number as libc::c_int) as u16
    }
}

pub struct WiringPi<Pin>(PhantomData<Pin>);

impl<P: Pin> WiringPi<P> {
    pub fn input_pin(&self, pin: u16) -> pin::InputPin<P> {
        let pin = pin as libc::c_int;
        pin::InputPin::new(pin)
    }

    pub fn output_pin(&self, pin: u16) -> pin::OutputPin<P> {
        let pin = pin as libc::c_int;
        pin::OutputPin::new(pin)
    }
/**

This returns a number representing the number if milliseconds since your
program called one of the setup functions.

It returns an unsigned 32-bit number which wraps after 49 days.

*/

    pub fn millis(&self) -> u32 {
        unsafe {
            bindings::millis()
        }
    }

/**

This returns a number representing the number if microseconds since your
program called one of the setup functions.

It returns an unsigned 32-bit number which wraps after 71 minutes.

*/

    pub fn micros(&self) -> u32 {
        unsafe {
            bindings::micros()
        }
    }
}

impl<P: RequiresRoot + Pin> WiringPi<P> {
    pub fn pwm_pin(&self) -> pin::PwmPin<P> {
        RequiresRoot::pwm_pin()
    }
}<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(json_macros)]

extern crate time;
extern crate rand;
extern crate url;
extern crate sxd_document;
extern crate sxd_xpath;
extern crate rustc_serialize;
extern crate byteorder;
extern crate hyper;
extern crate openssl;
extern crate redis;
extern crate multipart;
#[macro_use]
extern crate log;

#[macro_use]
mod macros;

mod types;
mod xmlutil;
mod cryptography;
mod events;
mod parser;
mod errors;
mod utils;

pub mod messages;
pub mod replies;
pub mod client;
pub mod session;

pub use self::types::WeChatResult;
pub use self::messages::MessageParser;
pub use self::messages::Message;
pub use self::parser::parse_message;
pub use self::errors::WeChatError;
pub use self::cryptography::WeChatCrypto;
pub use self::replies::ReplyRenderer;
pub use self::replies::Reply;
pub use self::client::WeChatClient;
pub use self::utils::check_signature;
<commit_msg>Make parse_message, MessageParser, ReplyRenderer private.<commit_after>#![feature(plugin)]
#![plugin(json_macros)]

extern crate time;
extern crate rand;
extern crate url;
extern crate sxd_document;
extern crate sxd_xpath;
extern crate rustc_serialize;
extern crate byteorder;
extern crate hyper;
extern crate openssl;
extern crate redis;
extern crate multipart;
#[macro_use]
extern crate log;

#[macro_use]
mod macros;

mod types;
mod xmlutil;
mod cryptography;
mod events;
mod parser;
mod errors;
mod utils;

pub mod messages;
pub mod replies;
pub mod client;
pub mod session;

pub use self::types::WeChatResult;
pub use self::messages::Message;
pub use self::errors::WeChatError;
pub use self::cryptography::WeChatCrypto;
pub use self::replies::Reply;
pub use self::client::WeChatClient;
pub use self::utils::check_signature;
<|endoftext|>


<commit_before>//! Demangle Rust compiler symbol names.
//!
//! This crate provides a `demangle` function which will return a `Demangle`
//! sentinel value that can be used to learn about the demangled version of a
//! symbol name. The demangled representation will be the same as the original
//! if it doesn't look like a mangled symbol name.
//!
//! `Demangle` can be formatted with the `Display` trait. The alternate
//! modifier (`#`) can be used to format the symbol name without the
//! trailing hash value.
//!
//! # Examples
//!
//! ```
//! use rustc_demangle::demangle;
//!
//! assert_eq!(demangle("_ZN4testE").to_string(), "test");
//! assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar");
//! assert_eq!(demangle("foo").to_string(), "foo");
//! // With hash
//! assert_eq!(format!("{}", demangle("_ZN3foo17h05af221e174051e9E")), "foo::h05af221e174051e9");
//! // Without hash
//! assert_eq!(format!("{:#}", demangle("_ZN3foo17h05af221e174051e9E")), "foo");
//! ```

#![no_std]
#![deny(missing_docs)]

#[cfg(test)]
#[macro_use]
extern crate std;

use core::fmt;

/// Representation of a demangled symbol name.
pub struct Demangle<'a> {
    original: &'a str,
    inner: &'a str,
    valid: bool,
    /// The number of ::-separated elements in the original name.
    elements: usize,
}

/// De-mangles a Rust symbol into a more readable version
///
/// All rust symbols by default are mangled as they contain characters that
/// cannot be represented in all object files. The mangling mechanism is similar
/// to C++'s, but Rust has a few specifics to handle items like lifetimes in
/// symbols.
///
/// This function will take a **mangled** symbol (typically acquired from a
/// `Symbol` which is in turn resolved from a `Frame`) and then writes the
/// de-mangled version into the given `writer`. If the symbol does not look like
/// a mangled symbol, it is still written to `writer`.
///
/// # Examples
///
/// ```
/// use rustc_demangle::demangle;
///
/// assert_eq!(demangle("_ZN4testE").to_string(), "test");
/// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar");
/// assert_eq!(demangle("foo").to_string(), "foo");
/// ```

// All rust symbols are in theory lists of "::"-separated identifiers. Some
// assemblers, however, can't handle these characters in symbol names. To get
// around this, we use C++-style mangling. The mangling method is:
//
// 1. Prefix the symbol with "_ZN"
// 2. For each element of the path, emit the length plus the element
// 3. End the path with "E"
//
// For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar".
//
// We're the ones printing our backtraces, so we can't rely on anything else to
// demangle our symbols. It's *much* nicer to look at demangled symbols, so
// this function is implemented to give us nice pretty output.
//
// Note that this demangler isn't quite as fancy as it could be. We have lots
// of other information in our symbols like hashes, version, type information,
// etc. Additionally, this doesn't handle glue symbols at all.
pub fn demangle(s: &str) -> Demangle {
    // First validate the symbol. If it doesn't look like anything we're
    // expecting, we just print it literally. Note that we must handle non-rust
    // symbols because we could have any function in the backtrace.
    let mut valid = true;
    let mut inner = s;
    if s.len() > 4 && s.starts_with("_ZN") && s.ends_with('E') {
        inner = &s[3..s.len() - 1];
    } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with('E') {
        // On Windows, dbghelp strips leading underscores, so we accept "ZN...E"
        // form too.
        inner = &s[2..s.len() - 1];
    } else {
        valid = false;
    }

    let mut elements = 0;
    if valid {
        let mut chars = inner.chars();
        while valid {
            let mut i = 0;
            for c in chars.by_ref() {
                if c.is_digit(10) {
                    i = i * 10 + c as usize - '0' as usize;
                } else {
                    break;
                }
            }
            if i == 0 {
                valid = chars.next().is_none();
                break;
            } else if chars.by_ref().take(i - 1).count() != i - 1 {
                valid = false;
            } else {
                elements += 1;
            }
        }
    }

    Demangle {
        inner: inner,
        valid: valid,
        elements: elements,
        original: s,
    }
}

/// The same as `demangle`, except return an `Err` if the string does not appear
/// to be a Rust symbol, rather than "demangling" the given string as a no-op.
///
/// ```
/// extern crate rustc_demangle;
///
/// let not_a_rust_symbol = "la la la";
///
/// // The `try_demangle` function will reject strings which are not Rust symbols.
/// assert!(rustc_demangle::try_demangle(not_a_rust_symbol).is_err());
///
/// // While `demangle` will just pass the non-symbol through as a no-op.
/// assert_eq!(rustc_demangle::demangle(not_a_rust_symbol).as_str(), not_a_rust_symbol);
/// ```
pub fn try_demangle(s: &str) -> Result<Demangle, ()> {
    let sym = demangle(s);
    if sym.valid {
        Ok(sym)
    } else {
        Err(())
    }
}

impl<'a> Demangle<'a> {
    /// Returns the underlying string that's being demangled.
    pub fn as_str(&self) -> &'a str {
        self.original
    }
}

// Rust hashes are hex digits with an `h` prepended.
fn is_rust_hash(s: &str) -> bool {
    s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16))
}

impl<'a> fmt::Display for Demangle<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Alright, let's do this.
        if !self.valid {
            return f.write_str(self.inner);
        }

        let mut inner = self.inner;
        for element in 0..self.elements {
            let mut rest = inner;
            while rest.chars().next().unwrap().is_digit(10) {
                rest = &rest[1..];
            }
            let i: usize = inner[..(inner.len() - rest.len())].parse().unwrap();
            inner = &rest[i..];
            rest = &rest[..i];
            // Skip printing the hash if alternate formatting
            // was requested.
            if f.alternate() && element+1 == self.elements && is_rust_hash(&rest) {
                break;
            }
            if element != 0 {
                try!(f.write_str("::"));
            }
            if rest.starts_with("_$") {
                rest = &rest[1..];
            }
            while !rest.is_empty() {
                if rest.starts_with('.') {
                    if let Some('.') = rest[1..].chars().next() {
                        try!(f.write_str("::"));
                        rest = &rest[2..];
                    } else {
                        try!(f.write_str("."));
                        rest = &rest[1..];
                    }
                } else if rest.starts_with('$') {
                    macro_rules! demangle {
                        ($($pat:expr => $demangled:expr),*) => ({
                            $(if rest.starts_with($pat) {
                                try!(f.write_str($demangled));
                                rest = &rest[$pat.len()..];
                              } else)*
                            {
                                try!(f.write_str(rest));
                                break;
                            }

                        })
                    }

                    // see src/librustc/back/link.rs for these mappings
                    demangle! {
                        "$SP$" => "@",
                        "$BP$" => "*",
                        "$RF$" => "&",
                        "$LT$" => "<",
                        "$GT$" => ">",
                        "$LP$" => "(",
                        "$RP$" => ")",
                        "$C$" => ",",

                        // in theory we can demangle any Unicode code point, but
                        // for simplicity we just catch the common ones.
                        "$u7e$" => "~",
                        "$u20$" => " ",
                        "$u27$" => "'",
                        "$u5b$" => "[",
                        "$u5d$" => "]",
                        "$u7b$" => "{",
                        "$u7d$" => "}",
                        "$u3b$" => ";",
                        "$u2b$" => "+",
                        "$u22$" => "\""
                    }
                } else {
                    let idx = match rest.char_indices().find(|&(_, c)| c == '$' || c == '.') {
                        None => rest.len(),
                        Some((i, _)) => i,
                    };
                    try!(f.write_str(&rest[..idx]));
                    rest = &rest[idx..];
                }
            }
        }

        Ok(())
    }
}

impl<'a> fmt::Debug for Demangle<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

#[cfg(test)]
mod tests {
    use std::prelude::v1::*;

    macro_rules! t {
        ($a:expr, $b:expr) => ({
            assert_eq!(super::demangle($a).to_string(), $b);
        })
    }

    macro_rules! t_nohash {
        ($a:expr, $b:expr) => ({
            assert_eq!(format!("{:#}", super::demangle($a)), $b);
        })
    }

    #[test]
    fn demangle() {
        t!("test", "test");
        t!("_ZN4testE", "test");
        t!("_ZN4test", "_ZN4test");
        t!("_ZN4test1a2bcE", "test::a::bc");
    }

    #[test]
    fn demangle_dollars() {
        t!("_ZN4$RP$E", ")");
        t!("_ZN8$RF$testE", "&test");
        t!("_ZN8$BP$test4foobE", "*test::foob");
        t!("_ZN9$u20$test4foobE", " test::foob");
        t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>");
    }

    #[test]
    fn demangle_many_dollars() {
        t!("_ZN13test$u20$test4foobE", "test test::foob");
        t!("_ZN12test$BP$test4foobE", "test*test::foob");
    }

    #[test]
    fn demangle_windows() {
        t!("ZN4testE", "test");
        t!("ZN13test$u20$test4foobE", "test test::foob");
        t!("ZN12test$RF$test4foobE", "test&test::foob");
    }

    #[test]
    fn demangle_elements_beginning_with_underscore() {
        t!("_ZN13_$LT$test$GT$E", "<test>");
        t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}");
        t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR");
    }

    #[test]
    fn demangle_trait_impls() {
        t!("_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE",
           "<Test + 'static as foo::Bar<Test>>::bar");
    }

    #[test]
    fn demangle_without_hash() {
        let s = "_ZN3foo17h05af221e174051e9E";
        t!(s, "foo::h05af221e174051e9");
        t_nohash!(s, "foo");
    }

    #[test]
    fn demangle_without_hash_edgecases() {
        // One element, no hash.
        t_nohash!("_ZN3fooE", "foo");
        // Two elements, no hash.
        t_nohash!("_ZN3foo3barE", "foo::bar");
        // Longer-than-normal hash.
        t_nohash!("_ZN3foo20h05af221e174051e9abcE", "foo");
        // Shorter-than-normal hash.
        t_nohash!("_ZN3foo5h05afE", "foo");
        // Valid hash, but not at the end.
        t_nohash!("_ZN17h05af221e174051e93fooE", "h05af221e174051e9::foo");
        // Not a valid hash, missing the 'h'.
        t_nohash!("_ZN3foo16ffaf221e174051e9E", "foo::ffaf221e174051e9");
        // Not a valid hash, has a non-hex-digit.
        t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9");
    }
}
<commit_msg>Add a standalone error type for try_demangle<commit_after>//! Demangle Rust compiler symbol names.
//!
//! This crate provides a `demangle` function which will return a `Demangle`
//! sentinel value that can be used to learn about the demangled version of a
//! symbol name. The demangled representation will be the same as the original
//! if it doesn't look like a mangled symbol name.
//!
//! `Demangle` can be formatted with the `Display` trait. The alternate
//! modifier (`#`) can be used to format the symbol name without the
//! trailing hash value.
//!
//! # Examples
//!
//! ```
//! use rustc_demangle::demangle;
//!
//! assert_eq!(demangle("_ZN4testE").to_string(), "test");
//! assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar");
//! assert_eq!(demangle("foo").to_string(), "foo");
//! // With hash
//! assert_eq!(format!("{}", demangle("_ZN3foo17h05af221e174051e9E")), "foo::h05af221e174051e9");
//! // Without hash
//! assert_eq!(format!("{:#}", demangle("_ZN3foo17h05af221e174051e9E")), "foo");
//! ```

#![no_std]
#![deny(missing_docs)]

#[cfg(test)]
#[macro_use]
extern crate std;

use core::fmt;

/// Representation of a demangled symbol name.
pub struct Demangle<'a> {
    original: &'a str,
    inner: &'a str,
    valid: bool,
    /// The number of ::-separated elements in the original name.
    elements: usize,
}

/// De-mangles a Rust symbol into a more readable version
///
/// All rust symbols by default are mangled as they contain characters that
/// cannot be represented in all object files. The mangling mechanism is similar
/// to C++'s, but Rust has a few specifics to handle items like lifetimes in
/// symbols.
///
/// This function will take a **mangled** symbol (typically acquired from a
/// `Symbol` which is in turn resolved from a `Frame`) and then writes the
/// de-mangled version into the given `writer`. If the symbol does not look like
/// a mangled symbol, it is still written to `writer`.
///
/// # Examples
///
/// ```
/// use rustc_demangle::demangle;
///
/// assert_eq!(demangle("_ZN4testE").to_string(), "test");
/// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar");
/// assert_eq!(demangle("foo").to_string(), "foo");
/// ```

// All rust symbols are in theory lists of "::"-separated identifiers. Some
// assemblers, however, can't handle these characters in symbol names. To get
// around this, we use C++-style mangling. The mangling method is:
//
// 1. Prefix the symbol with "_ZN"
// 2. For each element of the path, emit the length plus the element
// 3. End the path with "E"
//
// For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar".
//
// We're the ones printing our backtraces, so we can't rely on anything else to
// demangle our symbols. It's *much* nicer to look at demangled symbols, so
// this function is implemented to give us nice pretty output.
//
// Note that this demangler isn't quite as fancy as it could be. We have lots
// of other information in our symbols like hashes, version, type information,
// etc. Additionally, this doesn't handle glue symbols at all.
pub fn demangle(s: &str) -> Demangle {
    // First validate the symbol. If it doesn't look like anything we're
    // expecting, we just print it literally. Note that we must handle non-rust
    // symbols because we could have any function in the backtrace.
    let mut valid = true;
    let mut inner = s;
    if s.len() > 4 && s.starts_with("_ZN") && s.ends_with('E') {
        inner = &s[3..s.len() - 1];
    } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with('E') {
        // On Windows, dbghelp strips leading underscores, so we accept "ZN...E"
        // form too.
        inner = &s[2..s.len() - 1];
    } else {
        valid = false;
    }

    let mut elements = 0;
    if valid {
        let mut chars = inner.chars();
        while valid {
            let mut i = 0;
            for c in chars.by_ref() {
                if c.is_digit(10) {
                    i = i * 10 + c as usize - '0' as usize;
                } else {
                    break;
                }
            }
            if i == 0 {
                valid = chars.next().is_none();
                break;
            } else if chars.by_ref().take(i - 1).count() != i - 1 {
                valid = false;
            } else {
                elements += 1;
            }
        }
    }

    Demangle {
        inner: inner,
        valid: valid,
        elements: elements,
        original: s,
    }
}

/// Error returned from the `try_demangle` function below when demangling fails.
#[derive(Debug, Clone)]
pub struct TryDemangleError {
    _priv: (),
}

/// The same as `demangle`, except return an `Err` if the string does not appear
/// to be a Rust symbol, rather than "demangling" the given string as a no-op.
///
/// ```
/// extern crate rustc_demangle;
///
/// let not_a_rust_symbol = "la la la";
///
/// // The `try_demangle` function will reject strings which are not Rust symbols.
/// assert!(rustc_demangle::try_demangle(not_a_rust_symbol).is_err());
///
/// // While `demangle` will just pass the non-symbol through as a no-op.
/// assert_eq!(rustc_demangle::demangle(not_a_rust_symbol).as_str(), not_a_rust_symbol);
/// ```
pub fn try_demangle(s: &str) -> Result<Demangle, TryDemangleError> {
    let sym = demangle(s);
    if sym.valid {
        Ok(sym)
    } else {
        Err(TryDemangleError { _priv: () })
    }
}

impl<'a> Demangle<'a> {
    /// Returns the underlying string that's being demangled.
    pub fn as_str(&self) -> &'a str {
        self.original
    }
}

// Rust hashes are hex digits with an `h` prepended.
fn is_rust_hash(s: &str) -> bool {
    s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16))
}

impl<'a> fmt::Display for Demangle<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Alright, let's do this.
        if !self.valid {
            return f.write_str(self.inner);
        }

        let mut inner = self.inner;
        for element in 0..self.elements {
            let mut rest = inner;
            while rest.chars().next().unwrap().is_digit(10) {
                rest = &rest[1..];
            }
            let i: usize = inner[..(inner.len() - rest.len())].parse().unwrap();
            inner = &rest[i..];
            rest = &rest[..i];
            // Skip printing the hash if alternate formatting
            // was requested.
            if f.alternate() && element+1 == self.elements && is_rust_hash(&rest) {
                break;
            }
            if element != 0 {
                try!(f.write_str("::"));
            }
            if rest.starts_with("_$") {
                rest = &rest[1..];
            }
            while !rest.is_empty() {
                if rest.starts_with('.') {
                    if let Some('.') = rest[1..].chars().next() {
                        try!(f.write_str("::"));
                        rest = &rest[2..];
                    } else {
                        try!(f.write_str("."));
                        rest = &rest[1..];
                    }
                } else if rest.starts_with('$') {
                    macro_rules! demangle {
                        ($($pat:expr => $demangled:expr),*) => ({
                            $(if rest.starts_with($pat) {
                                try!(f.write_str($demangled));
                                rest = &rest[$pat.len()..];
                              } else)*
                            {
                                try!(f.write_str(rest));
                                break;
                            }

                        })
                    }

                    // see src/librustc/back/link.rs for these mappings
                    demangle! {
                        "$SP$" => "@",
                        "$BP$" => "*",
                        "$RF$" => "&",
                        "$LT$" => "<",
                        "$GT$" => ">",
                        "$LP$" => "(",
                        "$RP$" => ")",
                        "$C$" => ",",

                        // in theory we can demangle any Unicode code point, but
                        // for simplicity we just catch the common ones.
                        "$u7e$" => "~",
                        "$u20$" => " ",
                        "$u27$" => "'",
                        "$u5b$" => "[",
                        "$u5d$" => "]",
                        "$u7b$" => "{",
                        "$u7d$" => "}",
                        "$u3b$" => ";",
                        "$u2b$" => "+",
                        "$u22$" => "\""
                    }
                } else {
                    let idx = match rest.char_indices().find(|&(_, c)| c == '$' || c == '.') {
                        None => rest.len(),
                        Some((i, _)) => i,
                    };
                    try!(f.write_str(&rest[..idx]));
                    rest = &rest[idx..];
                }
            }
        }

        Ok(())
    }
}

impl<'a> fmt::Debug for Demangle<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

#[cfg(test)]
mod tests {
    use std::prelude::v1::*;

    macro_rules! t {
        ($a:expr, $b:expr) => ({
            assert_eq!(super::demangle($a).to_string(), $b);
        })
    }

    macro_rules! t_nohash {
        ($a:expr, $b:expr) => ({
            assert_eq!(format!("{:#}", super::demangle($a)), $b);
        })
    }

    #[test]
    fn demangle() {
        t!("test", "test");
        t!("_ZN4testE", "test");
        t!("_ZN4test", "_ZN4test");
        t!("_ZN4test1a2bcE", "test::a::bc");
    }

    #[test]
    fn demangle_dollars() {
        t!("_ZN4$RP$E", ")");
        t!("_ZN8$RF$testE", "&test");
        t!("_ZN8$BP$test4foobE", "*test::foob");
        t!("_ZN9$u20$test4foobE", " test::foob");
        t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>");
    }

    #[test]
    fn demangle_many_dollars() {
        t!("_ZN13test$u20$test4foobE", "test test::foob");
        t!("_ZN12test$BP$test4foobE", "test*test::foob");
    }

    #[test]
    fn demangle_windows() {
        t!("ZN4testE", "test");
        t!("ZN13test$u20$test4foobE", "test test::foob");
        t!("ZN12test$RF$test4foobE", "test&test::foob");
    }

    #[test]
    fn demangle_elements_beginning_with_underscore() {
        t!("_ZN13_$LT$test$GT$E", "<test>");
        t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}");
        t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR");
    }

    #[test]
    fn demangle_trait_impls() {
        t!("_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE",
           "<Test + 'static as foo::Bar<Test>>::bar");
    }

    #[test]
    fn demangle_without_hash() {
        let s = "_ZN3foo17h05af221e174051e9E";
        t!(s, "foo::h05af221e174051e9");
        t_nohash!(s, "foo");
    }

    #[test]
    fn demangle_without_hash_edgecases() {
        // One element, no hash.
        t_nohash!("_ZN3fooE", "foo");
        // Two elements, no hash.
        t_nohash!("_ZN3foo3barE", "foo::bar");
        // Longer-than-normal hash.
        t_nohash!("_ZN3foo20h05af221e174051e9abcE", "foo");
        // Shorter-than-normal hash.
        t_nohash!("_ZN3foo5h05afE", "foo");
        // Valid hash, but not at the end.
        t_nohash!("_ZN17h05af221e174051e93fooE", "h05af221e174051e9::foo");
        // Not a valid hash, missing the 'h'.
        t_nohash!("_ZN3foo16ffaf221e174051e9E", "foo::ffaf221e174051e9");
        // Not a valid hash, has a non-hex-digit.
        t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9");
    }
}
<|endoftext|>