<commit_before>use super::{InternParams, Mime, ParamSource, Source};

macro_rules! mimes {
    ($($id:ident, $($piece:expr),+;)+) => (
        #[allow(non_camel_case_types)]
        enum __Atoms {
            __Dynamic,
            $(
                $id,
            )+
        }

        $(
            mime_constant! {
                $id, $($piece),+
            }
        )+

        #[test]
        fn test_mimes_macro_consts() {
            $(
            mime_constant_test! {
                $id, $($piece),*
            }
            )+
        }
    )
}

pub(super) enum Atoms {}

macro_rules! mime_constant {
    ($id:ident, $src:expr, $slash:expr) => (
        mime_constant!($id, $src, $slash, None);
    );
    ($id:ident, $src:expr, $slash:expr, $plus:expr) => (
        mime_constant!(FULL $id, $src, $slash, $plus, ParamSource::None);
    );

    ($id:ident, $src:expr, $slash:expr, $plus:expr, $params:expr) => (
        mime_constant!(FULL $id, $src, $slash, $plus, ParamSource::Utf8($params));
    );

    (FULL $id:ident, $src:expr, $slash:expr, $plus:expr, $params:expr) => (

        impl Atoms {
            const $id: Source = Source::Atom(__Atoms::$id as u8, $src);
        }

        #[doc = "`"]
        #[doc = $src]
        #[doc = "`"]
        pub const $id: Mime = Mime {
            source: Atoms::$id,
            slash: $slash,
            plus: $plus,
            params: $params,
        };
    )
}

#[cfg(test)]
macro_rules! mime_constant_test {
    ($id:ident, $src:expr, $slash:expr) => (
        mime_constant_test!($id, $src, $slash, None);
    );
    ($id:ident, $src:expr, $slash:expr, $plus:expr) => (
        mime_constant_test!(FULL $id, $src, $slash, $plus, ParamSource::None);
    );

    ($id:ident, $src:expr, $slash:expr, $plus:expr, $params:expr) => (
        mime_constant_test!(FULL $id, $src, $slash, $plus, ParamSource::Utf8($params));
    );

    (FULL $id:ident, $src:expr, $slash:expr, $plus:expr, $params:expr) => ({
        let __mime = $id;

        // check slash, plus, and semicolon are in correct positions
        let __slash = __mime.as_ref().as_bytes()[$slash];
        assert_eq!(__slash, b'/', "{:?} has {:?} at slash position {:?}", __mime, __slash as char, $slash);
        if let Some(plus) = __mime.mime.plus {
            let __c = __mime.as_ref().as_bytes()[plus];
            assert_eq!(__c, b'+', "{:?} has {:?} at plus position {:?}", __mime, __c as char, plus);
        } else {
            assert!(!__mime.as_ref().as_bytes().contains(&b'+'), "{:?} forgot plus", __mime);
        }
        if let ParamSource::Utf8(semicolon) = __mime.mime.params {
            assert_eq!(__mime.as_ref().as_bytes()[semicolon], b';');
            assert_eq!(&__mime.as_ref()[semicolon..], "; charset=utf-8");
        } else if let ParamSource::None = __mime.mime.params {
            assert!(!__mime.as_ref().as_bytes().contains(&b';'));
        } else {
            unreachable!("consts wont have ParamSource::Custom");
        }

        // check that parsing can intern constants
        match __mime.mime.params {
            ParamSource::None | ParamSource::Utf8(_) => {
                let __parsed = parse::parse($src, parse::CanRange::Yes, Atoms::intern).expect("parse const");
                match __parsed.source {
                    Source::Atom(_, $src) => (),
                    Source::Atom(_, src) => {
                        panic!(
                            "did not intern {:?} correctly: {:?}",
                            $src,
                            src,
                        );
                    },
                    _ => {
                        panic!(
                            "did not intern an Atom {:?}: slash={}, sub={}",
                            $src,
                            $slash,
                            $src.len() - $slash - 1,
                        );
                    }
                }
            },
            _ => (),
        }

        // prevent ranges from being MediaTypes
        __mime.test_assert_asterisks();
    })
}

impl Atoms {
    pub(super) fn intern(s: &str, slash: u16, params: InternParams) -> Source {
        let slash = slash as usize;
        debug_assert!(
            s.len() > slash,
            "intern called with illegal slash position: {:?}[{:?}]",
            s,
            slash,
        );

        match params {
            InternParams::Utf8(semicolon) => {
                Atoms::intern_charset_utf8(s, slash, semicolon)
            },
            InternParams::None => {
                Atoms::intern_no_params(s, slash)
            },
        }
    }

    fn intern_charset_utf8(s: &str, slash: usize, semicolon: usize) -> Source {
        use self::names::*;
        let top = &s[..slash];
        let sub = &s[slash + 1..semicolon];

        if top == TEXT {
            if sub == PLAIN {
                return Atoms::TEXT_PLAIN_UTF_8;
            }
            if sub == HTML {
                return Atoms::TEXT_HTML_UTF_8;
            }
            if sub == CSS {
                return Atoms::TEXT_CSS_UTF_8;
            }
            if sub == CSV {
                return Atoms::TEXT_CSV_UTF_8;
            }
            if sub == TAB_SEPARATED_VALUES {
                return Atoms::TEXT_TAB_SEPARATED_VALUES_UTF_8;
            }
        }
        if top == APPLICATION {
            if sub == JAVASCRIPT {
                return Atoms::APPLICATION_JAVASCRIPT_UTF_8;
            }
        }

        Atoms::dynamic(s)
    }

    fn intern_no_params(s: &str, slash: usize) -> Source {
        use self::names::*;
        let top = &s[..slash];
        let sub = &s[slash + 1..];

        match slash {
            4 => {
                if top == TEXT {
                    match sub.len() {
                        1 => {
                            if sub.as_bytes()[0] == b'*' {
                                return Atoms::TEXT_STAR;
                            }
                        }
                        3 => {
                            if sub == CSS {
                                return Atoms::TEXT_CSS;
                            }
                            if sub == XML {
                                return Atoms::TEXT_XML;
                            }
                            if sub == CSV {
                                return Atoms::TEXT_CSV;
                            }
                        },
                        4 => {
                            if sub == HTML {
                                return Atoms::TEXT_HTML;
                            }
                        }
                        5 => {
                            if sub == PLAIN {
                                return Atoms::TEXT_PLAIN;
                            }
                            if sub == VCARD {
                                return Atoms::TEXT_VCARD;
                            }
                        }
                        10 => {
                            if sub == JAVASCRIPT {
                                return Atoms::TEXT_JAVASCRIPT;
                            }
                        }
                        12 => {
                            if sub == EVENT_STREAM {
                                return Atoms::TEXT_EVENT_STREAM;
                            }
                        },
                        20 => {
                            if sub == TAB_SEPARATED_VALUES {
                                return Atoms::TEXT_TAB_SEPARATED_VALUES;
                            }
                        }
                        _ => (),
                    }
                } else if top == FONT {
                    match sub.len() {
                        4 => {
                            if sub == WOFF {
                                return Atoms::FONT_WOFF;
                            }
                        },
                        5 => {
                            if sub == WOFF2 {
                                return Atoms::FONT_WOFF2;
                            }
                        },
                        _ => (),
                    }
                }
            },
            5 => {
                if top == IMAGE {
                    match sub.len() {
                        1 => {
                            if sub.as_bytes()[0] == b'*' {
                                return Atoms::IMAGE_STAR;
                            }
                        }
                        3 => {
                            if sub == PNG {
                                return Atoms::IMAGE_PNG;
                            }
                            if sub == GIF {
                                return Atoms::IMAGE_GIF;
                            }
                            if sub == BMP {
                                return Atoms::IMAGE_BMP;
                            }
                        }
                        4 => {
                            if sub == JPEG {
                                return Atoms::IMAGE_JPEG;
                            }
                        },
                        7 => {
                            if sub == SVG {
                                return Atoms::IMAGE_SVG;
                            }
                        },
                        _ => (),

                    }
                } else if top == VIDEO {
                    match sub.len() {
                        1 => {
                            if sub.as_bytes()[0] == b'*' {
                                return Atoms::VIDEO_STAR;
                            }
                        },
                        _ => (),
                    }
                } else if top == AUDIO {
                    match sub.len() {
                        1 => {
                            if sub.as_bytes()[0] == b'*' {
                                return Atoms::AUDIO_STAR;
                            }
                        },
                        _ => (),
                    }
                }
            },
            11 => {
                if top == APPLICATION {
                    match sub.len() {
                        3 => {
                            if sub == PDF {
                                return Atoms::APPLICATION_PDF;
                            }
                        }
                        4 => {
                            if sub == JSON {
                                return Atoms::APPLICATION_JSON;
                            }
                        },
                        7 => {
                            if sub == MSGPACK {
                                return Atoms::APPLICATION_MSGPACK;
                            }
                        },
                        10 => {
                            if sub == JAVASCRIPT {
                                return Atoms::APPLICATION_JAVASCRIPT;
                            }
                        },
                        11 => {
                            if sub == "dns-message" {
                                return Atoms::APPLICATION_DNS;
                            }
                        },
                        12 => {
                            if sub == OCTET_STREAM {
                                return Atoms::APPLICATION_OCTET_STREAM;
                            }
                        }
                        21 => {
                            if sub == WWW_FORM_URLENCODED {
                                return Atoms::APPLICATION_WWW_FORM_URLENCODED;
                            }
                        }
                        _ => (),
                    }
                }
            }
            _ => (),
        }

        Atoms::dynamic(s)
    }

    fn dynamic(s: &str) -> Source {
        Source::Dynamic(s.to_ascii_lowercase())
    }
}

macro_rules! names {
    ($($id:ident, $e:expr;)*) => (
        pub mod names {
            $(
            names! {
                @DOC concat!("The string literal `\"", $e, "\"`."),
                $id,
                $e
            }
            )*

            #[test]
            fn test_names_macro_consts() {
                $(
                assert_eq!($id.to_ascii_lowercase(), $id);
                )*
            }
        }
    );
    (@DOC $doc:expr, $id:ident, $e:expr) => (
        #[doc = "The string literal `\""]
        #[doc = $e]
        #[doc = "\"`."]
        pub const $id: &'static str = $e;
    )
}

names! {
    STAR, "*";

    TEXT, "text";
    IMAGE, "image";
    AUDIO, "audio";
    VIDEO, "video";
    APPLICATION, "application";
    MULTIPART, "multipart";
    MESSAGE, "message";
    MODEL, "model";
    FONT, "font";

    // common text/ *
    PLAIN, "plain";
    HTML, "html";
    XML, "xml";
    JAVASCRIPT, "javascript";
    CSS, "css";
    CSV, "csv";
    EVENT_STREAM, "event-stream";
    VCARD, "vcard";
    TAB_SEPARATED_VALUES, "tab-separated-values";

    // common application/*
    JSON, "json";
    WWW_FORM_URLENCODED, "x-www-form-urlencoded";
    MSGPACK, "msgpack";
    OCTET_STREAM, "octet-stream";
    PDF, "pdf";

    // common font/*
    WOFF, "woff";
    WOFF2, "woff2";

    // multipart/*
    FORM_DATA, "form-data";

    // common image/*
    BMP, "bmp";
    GIF, "gif";
    JPEG, "jpeg";
    PNG, "png";
    SVG, "svg+xml";

    // audio/*
    BASIC, "basic";
    MPEG, "mpeg";
    MP4, "mp4";
    OGG, "ogg";

    // parameters
    CHARSET, "charset";
    BOUNDARY, "boundary";
}

mimes! {
    //@ MediaType:
    TEXT_PLAIN, "text/plain", 4;
    TEXT_PLAIN_UTF_8, "text/plain; charset=utf-8", 4, None, 10;
    TEXT_HTML, "text/html", 4;
    TEXT_HTML_UTF_8, "text/html; charset=utf-8", 4, None, 9;
    TEXT_CSS, "text/css", 4;
    TEXT_CSS_UTF_8, "text/css; charset=utf-8", 4, None, 8;
    TEXT_JAVASCRIPT, "text/javascript", 4;
    TEXT_XML, "text/xml", 4;
    TEXT_EVENT_STREAM, "text/event-stream", 4;
    TEXT_CSV, "text/csv", 4;
    TEXT_CSV_UTF_8, "text/csv; charset=utf-8", 4, None, 8;
    TEXT_TAB_SEPARATED_VALUES, "text/tab-separated-values", 4;
    TEXT_TAB_SEPARATED_VALUES_UTF_8, "text/tab-separated-values; charset=utf-8", 4, None, 25;
    TEXT_VCARD, "text/vcard", 4;

    IMAGE_JPEG, "image/jpeg", 5;
    IMAGE_GIF, "image/gif", 5;
    IMAGE_PNG, "image/png", 5;
    IMAGE_BMP, "image/bmp", 5;
    IMAGE_SVG, "image/svg+xml", 5, Some(9);

    FONT_WOFF, "font/woff", 4;
    FONT_WOFF2, "font/woff2", 4;

    APPLICATION_JSON, "application/json", 11;
    APPLICATION_JAVASCRIPT, "application/javascript", 11;
    APPLICATION_JAVASCRIPT_UTF_8, "application/javascript; charset=utf-8", 11, None, 22;
    APPLICATION_WWW_FORM_URLENCODED, "application/x-www-form-urlencoded", 11;
    APPLICATION_OCTET_STREAM, "application/octet-stream", 11;
    APPLICATION_MSGPACK, "application/msgpack", 11;
    APPLICATION_PDF, "application/pdf", 11;
    APPLICATION_DNS, "application/dns-message", 11;

    // media-ranges
    //@ MediaRange:
    STAR_STAR, "*/*", 1;
    TEXT_STAR, "text/*", 4;
    IMAGE_STAR, "image/*", 5;
    VIDEO_STAR, "video/*", 5;
    AUDIO_STAR, "audio/*", 5;
}

<commit_msg>fix name constants docs<commit_after>use super::{InternParams, Mime, ParamSource, Source};

macro_rules! mimes {
    ($($id:ident, $($piece:expr),+;)+) => (
        #[allow(non_camel_case_types)]
        enum __Atoms {
            __Dynamic,
            $(
                $id,
            )+
        }

        $(
            mime_constant! {
                $id, $($piece),+
            }
        )+

        #[test]
        fn test_mimes_macro_consts() {
            $(
            mime_constant_test! {
                $id, $($piece),*
            }
            )+
        }
    )
}

pub(super) enum Atoms {}

macro_rules! mime_constant {
    ($id:ident, $src:expr, $slash:expr) => (
        mime_constant!($id, $src, $slash, None);
    );
    ($id:ident, $src:expr, $slash:expr, $plus:expr) => (
        mime_constant!(FULL $id, $src, $slash, $plus, ParamSource::None);
    );

    ($id:ident, $src:expr, $slash:expr, $plus:expr, $params:expr) => (
        mime_constant!(FULL $id, $src, $slash, $plus, ParamSource::Utf8($params));
    );

    (FULL $id:ident, $src:expr, $slash:expr, $plus:expr, $params:expr) => (

        impl Atoms {
            const $id: Source = Source::Atom(__Atoms::$id as u8, $src);
        }

        #[doc = "`"]
        #[doc = $src]
        #[doc = "`"]
        pub const $id: Mime = Mime {
            source: Atoms::$id,
            slash: $slash,
            plus: $plus,
            params: $params,
        };
    )
}

#[cfg(test)]
macro_rules! mime_constant_test {
    ($id:ident, $src:expr, $slash:expr) => (
        mime_constant_test!($id, $src, $slash, None);
    );
    ($id:ident, $src:expr, $slash:expr, $plus:expr) => (
        mime_constant_test!(FULL $id, $src, $slash, $plus, ParamSource::None);
    );

    ($id:ident, $src:expr, $slash:expr, $plus:expr, $params:expr) => (
        mime_constant_test!(FULL $id, $src, $slash, $plus, ParamSource::Utf8($params));
    );

    (FULL $id:ident, $src:expr, $slash:expr, $plus:expr, $params:expr) => ({
        let __mime = $id;

        // check slash, plus, and semicolon are in correct positions
        let __slash = __mime.as_ref().as_bytes()[$slash];
        assert_eq!(__slash, b'/', "{:?} has {:?} at slash position {:?}", __mime, __slash as char, $slash);
        if let Some(plus) = __mime.mime.plus {
            let __c = __mime.as_ref().as_bytes()[plus];
            assert_eq!(__c, b'+', "{:?} has {:?} at plus position {:?}", __mime, __c as char, plus);
        } else {
            assert!(!__mime.as_ref().as_bytes().contains(&b'+'), "{:?} forgot plus", __mime);
        }
        if let ParamSource::Utf8(semicolon) = __mime.mime.params {
            assert_eq!(__mime.as_ref().as_bytes()[semicolon], b';');
            assert_eq!(&__mime.as_ref()[semicolon..], "; charset=utf-8");
        } else if let ParamSource::None = __mime.mime.params {
            assert!(!__mime.as_ref().as_bytes().contains(&b';'));
        } else {
            unreachable!("consts wont have ParamSource::Custom");
        }

        // check that parsing can intern constants
        match __mime.mime.params {
            ParamSource::None | ParamSource::Utf8(_) => {
                let __parsed = parse::parse($src, parse::CanRange::Yes, Atoms::intern).expect("parse const");
                match __parsed.source {
                    Source::Atom(_, $src) => (),
                    Source::Atom(_, src) => {
                        panic!(
                            "did not intern {:?} correctly: {:?}",
                            $src,
                            src,
                        );
                    },
                    _ => {
                        panic!(
                            "did not intern an Atom {:?}: slash={}, sub={}",
                            $src,
                            $slash,
                            $src.len() - $slash - 1,
                        );
                    }
                }
            },
            _ => (),
        }

        // prevent ranges from being MediaTypes
        __mime.test_assert_asterisks();
    })
}

impl Atoms {
    pub(super) fn intern(s: &str, slash: u16, params: InternParams) -> Source {
        let slash = slash as usize;
        debug_assert!(
            s.len() > slash,
            "intern called with illegal slash position: {:?}[{:?}]",
            s,
            slash,
        );

        match params {
            InternParams::Utf8(semicolon) => {
                Atoms::intern_charset_utf8(s, slash, semicolon)
            },
            InternParams::None => {
                Atoms::intern_no_params(s, slash)
            },
        }
    }

    fn intern_charset_utf8(s: &str, slash: usize, semicolon: usize) -> Source {
        use self::names::*;
        let top = &s[..slash];
        let sub = &s[slash + 1..semicolon];

        if top == TEXT {
            if sub == PLAIN {
                return Atoms::TEXT_PLAIN_UTF_8;
            }
            if sub == HTML {
                return Atoms::TEXT_HTML_UTF_8;
            }
            if sub == CSS {
                return Atoms::TEXT_CSS_UTF_8;
            }
            if sub == CSV {
                return Atoms::TEXT_CSV_UTF_8;
            }
            if sub == TAB_SEPARATED_VALUES {
                return Atoms::TEXT_TAB_SEPARATED_VALUES_UTF_8;
            }
        }
        if top == APPLICATION {
            if sub == JAVASCRIPT {
                return Atoms::APPLICATION_JAVASCRIPT_UTF_8;
            }
        }

        Atoms::dynamic(s)
    }

    fn intern_no_params(s: &str, slash: usize) -> Source {
        use self::names::*;
        let top = &s[..slash];
        let sub = &s[slash + 1..];

        match slash {
            4 => {
                if top == TEXT {
                    match sub.len() {
                        1 => {
                            if sub.as_bytes()[0] == b'*' {
                                return Atoms::TEXT_STAR;
                            }
                        }
                        3 => {
                            if sub == CSS {
                                return Atoms::TEXT_CSS;
                            }
                            if sub == XML {
                                return Atoms::TEXT_XML;
                            }
                            if sub == CSV {
                                return Atoms::TEXT_CSV;
                            }
                        },
                        4 => {
                            if sub == HTML {
                                return Atoms::TEXT_HTML;
                            }
                        }
                        5 => {
                            if sub == PLAIN {
                                return Atoms::TEXT_PLAIN;
                            }
                            if sub == VCARD {
                                return Atoms::TEXT_VCARD;
                            }
                        }
                        10 => {
                            if sub == JAVASCRIPT {
                                return Atoms::TEXT_JAVASCRIPT;
                            }
                        }
                        12 => {
                            if sub == EVENT_STREAM {
                                return Atoms::TEXT_EVENT_STREAM;
                            }
                        },
                        20 => {
                            if sub == TAB_SEPARATED_VALUES {
                                return Atoms::TEXT_TAB_SEPARATED_VALUES;
                            }
                        }
                        _ => (),
                    }
                } else if top == FONT {
                    match sub.len() {
                        4 => {
                            if sub == WOFF {
                                return Atoms::FONT_WOFF;
                            }
                        },
                        5 => {
                            if sub == WOFF2 {
                                return Atoms::FONT_WOFF2;
                            }
                        },
                        _ => (),
                    }
                }
            },
            5 => {
                if top == IMAGE {
                    match sub.len() {
                        1 => {
                            if sub.as_bytes()[0] == b'*' {
                                return Atoms::IMAGE_STAR;
                            }
                        }
                        3 => {
                            if sub == PNG {
                                return Atoms::IMAGE_PNG;
                            }
                            if sub == GIF {
                                return Atoms::IMAGE_GIF;
                            }
                            if sub == BMP {
                                return Atoms::IMAGE_BMP;
                            }
                        }
                        4 => {
                            if sub == JPEG {
                                return Atoms::IMAGE_JPEG;
                            }
                        },
                        7 => {
                            if sub == SVG {
                                return Atoms::IMAGE_SVG;
                            }
                        },
                        _ => (),

                    }
                } else if top == VIDEO {
                    match sub.len() {
                        1 => {
                            if sub.as_bytes()[0] == b'*' {
                                return Atoms::VIDEO_STAR;
                            }
                        },
                        _ => (),
                    }
                } else if top == AUDIO {
                    match sub.len() {
                        1 => {
                            if sub.as_bytes()[0] == b'*' {
                                return Atoms::AUDIO_STAR;
                            }
                        },
                        _ => (),
                    }
                }
            },
            11 => {
                if top == APPLICATION {
                    match sub.len() {
                        3 => {
                            if sub == PDF {
                                return Atoms::APPLICATION_PDF;
                            }
                        }
                        4 => {
                            if sub == JSON {
                                return Atoms::APPLICATION_JSON;
                            }
                        },
                        7 => {
                            if sub == MSGPACK {
                                return Atoms::APPLICATION_MSGPACK;
                            }
                        },
                        10 => {
                            if sub == JAVASCRIPT {
                                return Atoms::APPLICATION_JAVASCRIPT;
                            }
                        },
                        11 => {
                            if sub == "dns-message" {
                                return Atoms::APPLICATION_DNS;
                            }
                        },
                        12 => {
                            if sub == OCTET_STREAM {
                                return Atoms::APPLICATION_OCTET_STREAM;
                            }
                        }
                        21 => {
                            if sub == WWW_FORM_URLENCODED {
                                return Atoms::APPLICATION_WWW_FORM_URLENCODED;
                            }
                        }
                        _ => (),
                    }
                }
            }
            _ => (),
        }

        Atoms::dynamic(s)
    }

    fn dynamic(s: &str) -> Source {
        Source::Dynamic(s.to_ascii_lowercase())
    }
}

macro_rules! names {
    ($($id:ident, $e:expr;)*) => (
        pub mod names {
            $(
            names! {
                @DOC concat!("The string literal `\"", $e, "\"`."),
                $id,
                $e
            }
            )*

            #[test]
            fn test_names_macro_consts() {
                $(
                assert_eq!($id.to_ascii_lowercase(), $id);
                )*
            }
        }
    );
    (@DOC $doc:expr, $id:ident, $e:expr) => (
        #[doc = $doc]
        pub const $id: &'static str = $e;
    )
}

names! {
    STAR, "*";

    TEXT, "text";
    IMAGE, "image";
    AUDIO, "audio";
    VIDEO, "video";
    APPLICATION, "application";
    MULTIPART, "multipart";
    MESSAGE, "message";
    MODEL, "model";
    FONT, "font";

    // common text/ *
    PLAIN, "plain";
    HTML, "html";
    XML, "xml";
    JAVASCRIPT, "javascript";
    CSS, "css";
    CSV, "csv";
    EVENT_STREAM, "event-stream";
    VCARD, "vcard";
    TAB_SEPARATED_VALUES, "tab-separated-values";

    // common application/*
    JSON, "json";
    WWW_FORM_URLENCODED, "x-www-form-urlencoded";
    MSGPACK, "msgpack";
    OCTET_STREAM, "octet-stream";
    PDF, "pdf";

    // common font/*
    WOFF, "woff";
    WOFF2, "woff2";

    // multipart/*
    FORM_DATA, "form-data";

    // common image/*
    BMP, "bmp";
    GIF, "gif";
    JPEG, "jpeg";
    PNG, "png";
    SVG, "svg+xml";

    // audio/*
    BASIC, "basic";
    MPEG, "mpeg";
    MP4, "mp4";
    OGG, "ogg";

    // parameters
    CHARSET, "charset";
    BOUNDARY, "boundary";
}

mimes! {
    //@ MediaType:
    TEXT_PLAIN, "text/plain", 4;
    TEXT_PLAIN_UTF_8, "text/plain; charset=utf-8", 4, None, 10;
    TEXT_HTML, "text/html", 4;
    TEXT_HTML_UTF_8, "text/html; charset=utf-8", 4, None, 9;
    TEXT_CSS, "text/css", 4;
    TEXT_CSS_UTF_8, "text/css; charset=utf-8", 4, None, 8;
    TEXT_JAVASCRIPT, "text/javascript", 4;
    TEXT_XML, "text/xml", 4;
    TEXT_EVENT_STREAM, "text/event-stream", 4;
    TEXT_CSV, "text/csv", 4;
    TEXT_CSV_UTF_8, "text/csv; charset=utf-8", 4, None, 8;
    TEXT_TAB_SEPARATED_VALUES, "text/tab-separated-values", 4;
    TEXT_TAB_SEPARATED_VALUES_UTF_8, "text/tab-separated-values; charset=utf-8", 4, None, 25;
    TEXT_VCARD, "text/vcard", 4;

    IMAGE_JPEG, "image/jpeg", 5;
    IMAGE_GIF, "image/gif", 5;
    IMAGE_PNG, "image/png", 5;
    IMAGE_BMP, "image/bmp", 5;
    IMAGE_SVG, "image/svg+xml", 5, Some(9);

    FONT_WOFF, "font/woff", 4;
    FONT_WOFF2, "font/woff2", 4;

    APPLICATION_JSON, "application/json", 11;
    APPLICATION_JAVASCRIPT, "application/javascript", 11;
    APPLICATION_JAVASCRIPT_UTF_8, "application/javascript; charset=utf-8", 11, None, 22;
    APPLICATION_WWW_FORM_URLENCODED, "application/x-www-form-urlencoded", 11;
    APPLICATION_OCTET_STREAM, "application/octet-stream", 11;
    APPLICATION_MSGPACK, "application/msgpack", 11;
    APPLICATION_PDF, "application/pdf", 11;
    APPLICATION_DNS, "application/dns-message", 11;

    // media-ranges
    //@ MediaRange:
    STAR_STAR, "*/*", 1;
    TEXT_STAR, "text/*", 4;
    IMAGE_STAR, "image/*", 5;
    VIDEO_STAR, "video/*", 5;
    AUDIO_STAR, "audio/*", 5;
}

<|endoftext|>


<commit_before>use std::collections::hash_map::{HashMap, IterMut, Values};
use std::fmt;

use core::{Dependency, Package, PackageId, Summary};
use util::CargoResult;

mod source_id;

pub use self::source_id::{GitReference, SourceId};

/// A Source finds and downloads remote packages based on names and
/// versions.
pub trait Source {
    /// Returns the `SourceId` corresponding to this source
    fn source_id(&self) -> &SourceId;

    /// Returns the replaced `SourceId` corresponding to this source
    fn replaced_source_id(&self) -> &SourceId {
        self.source_id()
    }

    /// Returns whether or not this source will return summaries with
    /// checksums listed.
    fn supports_checksums(&self) -> bool;

    /// Returns whether or not this source will return summaries with
    /// the `precise` field in the source id listed.
    fn requires_precise(&self) -> bool;

    /// Attempt to find the packages that match a dependency request.
    fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;

    /// Attempt to find the packages that are close to a dependency request.
    /// Each source gets to define what `close` means for it.
    /// path/git sources may return all dependencies that are at that uri.
    /// where as an Index source may return dependencies that have the same canonicalization.
    fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;

    fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
        let mut ret = Vec::new();
        self.query(dep, &mut |s| ret.push(s))?;
        Ok(ret)
    }

    /// The update method performs any network operations required to
    /// get the entire list of all names, versions and dependencies of
    /// packages managed by the Source.
    fn update(&mut self) -> CargoResult<()>;

    /// The download method fetches the full package for each name and
    /// version specified.
    fn download(&mut self, package: &PackageId) -> CargoResult<MaybePackage>;

    fn finish_download(&mut self, package: &PackageId, contents: Vec<u8>) -> CargoResult<Package>;

    /// Generates a unique string which represents the fingerprint of the
    /// current state of the source.
    ///
    /// This fingerprint is used to determine the "fresheness" of the source
    /// later on. It must be guaranteed that the fingerprint of a source is
    /// constant if and only if the output product will remain constant.
    ///
    /// The `pkg` argument is the package which this fingerprint should only be
    /// interested in for when this source may contain multiple packages.
    fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;

    /// If this source supports it, verifies the source of the package
    /// specified.
    ///
    /// Note that the source may also have performed other checksum-based
    /// verification during the `download` step, but this is intended to be run
    /// just before a crate is compiled so it may perform more expensive checks
    /// which may not be cacheable.
    fn verify(&self, _pkg: &PackageId) -> CargoResult<()> {
        Ok(())
    }

    /// Describes this source in a human readable fashion, used for display in
    /// resolver error messages currently.
    fn describe(&self) -> String;

    /// Returns whether a source is being replaced by another here
    fn is_replaced(&self) -> bool {
        false
    }
}

pub enum MaybePackage {
    Ready(Package),
    Download { url: String, descriptor: String },
}

impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
    /// Forwards to `Source::supports_checksums`
    fn supports_checksums(&self) -> bool {
        (**self).supports_checksums()
    }

    /// Forwards to `Source::requires_precise`
    fn requires_precise(&self) -> bool {
        (**self).requires_precise()
    }

    /// Forwards to `Source::query`
    fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        (**self).query(dep, f)
    }

    /// Forwards to `Source::query`
    fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        (**self).fuzzy_query(dep, f)
    }

    /// Forwards to `Source::source_id`
    fn source_id(&self) -> &SourceId {
        (**self).source_id()
    }

    /// Forwards to `Source::replaced_source_id`
    fn replaced_source_id(&self) -> &SourceId {
        (**self).replaced_source_id()
    }

    /// Forwards to `Source::update`
    fn update(&mut self) -> CargoResult<()> {
        (**self).update()
    }

    /// Forwards to `Source::download`
    fn download(&mut self, id: &PackageId) -> CargoResult<MaybePackage> {
        (**self).download(id)
    }

    fn finish_download(&mut self, id: &PackageId, data: Vec<u8>) -> CargoResult<Package> {
        (**self).finish_download(id, data)
    }

    /// Forwards to `Source::fingerprint`
    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
        (**self).fingerprint(pkg)
    }

    /// Forwards to `Source::verify`
    fn verify(&self, pkg: &PackageId) -> CargoResult<()> {
        (**self).verify(pkg)
    }

    fn describe(&self) -> String {
        (**self).describe()
    }

    fn is_replaced(&self) -> bool {
        (**self).is_replaced()
    }
}

impl<'a, T: Source + ?Sized + 'a> Source for &'a mut T {
    fn supports_checksums(&self) -> bool {
        (**self).supports_checksums()
    }

    fn requires_precise(&self) -> bool {
        (**self).requires_precise()
    }

    fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        (**self).query(dep, f)
    }

    fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        (**self).fuzzy_query(dep, f)
    }

    fn source_id(&self) -> &SourceId {
        (**self).source_id()
    }

    fn replaced_source_id(&self) -> &SourceId {
        (**self).replaced_source_id()
    }

    fn update(&mut self) -> CargoResult<()> {
        (**self).update()
    }

    fn download(&mut self, id: &PackageId) -> CargoResult<MaybePackage> {
        (**self).download(id)
    }

    fn finish_download(&mut self, id: &PackageId, data: Vec<u8>) -> CargoResult<Package> {
        (**self).finish_download(id, data)
    }

    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
        (**self).fingerprint(pkg)
    }

    fn verify(&self, pkg: &PackageId) -> CargoResult<()> {
        (**self).verify(pkg)
    }

    fn describe(&self) -> String {
        (**self).describe()
    }

    fn is_replaced(&self) -> bool {
        (**self).is_replaced()
    }
}

/// A `HashMap` of `SourceId` -> `Box<Source>`
#[derive(Default)]
pub struct SourceMap<'src> {
    map: HashMap<SourceId, Box<Source + 'src>>,
}

// impl debug on source requires specialization, if even desirable at all
impl<'src> fmt::Debug for SourceMap<'src> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SourceMap ")?;
        f.debug_set().entries(self.map.keys()).finish()
    }
}

/// A `std::collection::hash_map::Values` for `SourceMap`
pub type Sources<'a, 'src> = Values<'a, SourceId, Box<Source + 'src>>;

/// A `std::collection::hash_map::IterMut` for `SourceMap`
pub struct SourcesMut<'a, 'src: 'a> {
    inner: IterMut<'a, SourceId, Box<Source + 'src>>,
}

impl<'src> SourceMap<'src> {
    /// Create an empty map
    pub fn new() -> SourceMap<'src> {
        SourceMap {
            map: HashMap::new(),
        }
    }

    /// Like `HashMap::contains_key`
    pub fn contains(&self, id: &SourceId) -> bool {
        self.map.contains_key(id)
    }

    /// Like `HashMap::get`
    pub fn get(&self, id: &SourceId) -> Option<&(Source + 'src)> {
        let source = self.map.get(id);

        source.map(|s| {
            let s: &(Source + 'src) = &**s;
            s
        })
    }

    /// Like `HashMap::get_mut`
    pub fn get_mut(&mut self, id: &SourceId) -> Option<&mut (Source + 'src)> {
        self.map.get_mut(id).map(|s| {
            let s: &mut (Source + 'src) = &mut **s;
            s
        })
    }

    /// Like `HashMap::get`, but first calculates the `SourceId` from a
    /// `PackageId`
    pub fn get_by_package_id(&self, pkg_id: &PackageId) -> Option<&(Source + 'src)> {
        self.get(pkg_id.source_id())
    }

    /// Like `HashMap::insert`, but derives the SourceId key from the Source
    pub fn insert(&mut self, source: Box<Source + 'src>) {
        let id = source.source_id().clone();
        self.map.insert(id, source);
    }

    /// Like `HashMap::is_empty`
    pub fn is_empty(&self) -> bool {
        self.map.is_empty()
    }

    /// Like `HashMap::len`
    pub fn len(&self) -> usize {
        self.map.len()
    }

    /// Like `HashMap::values`
    pub fn sources<'a>(&'a self) -> Sources<'a, 'src> {
        self.map.values()
    }

    /// Like `HashMap::iter_mut`
    pub fn sources_mut<'a>(&'a mut self) -> SourcesMut<'a, 'src> {
        SourcesMut {
            inner: self.map.iter_mut(),
        }
    }
}

impl<'a, 'src> Iterator for SourcesMut<'a, 'src> {
    type Item = (&'a SourceId, &'a mut (Source + 'src));
    fn next(&mut self) -> Option<(&'a SourceId, &'a mut (Source + 'src))> {
        self.inner.next().map(|(a, b)| (a, &mut **b))
    }
}
<commit_msg>let jetbrains reorder some impls to match the definition<commit_after>use std::collections::hash_map::{HashMap, IterMut, Values};
use std::fmt;

use core::{Dependency, Package, PackageId, Summary};
use util::CargoResult;

mod source_id;

pub use self::source_id::{GitReference, SourceId};

/// A Source finds and downloads remote packages based on names and
/// versions.
pub trait Source {
    /// Returns the `SourceId` corresponding to this source
    fn source_id(&self) -> &SourceId;

    /// Returns the replaced `SourceId` corresponding to this source
    fn replaced_source_id(&self) -> &SourceId {
        self.source_id()
    }

    /// Returns whether or not this source will return summaries with
    /// checksums listed.
    fn supports_checksums(&self) -> bool;

    /// Returns whether or not this source will return summaries with
    /// the `precise` field in the source id listed.
    fn requires_precise(&self) -> bool;

    /// Attempt to find the packages that match a dependency request.
    fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;

    /// Attempt to find the packages that are close to a dependency request.
    /// Each source gets to define what `close` means for it.
    /// path/git sources may return all dependencies that are at that uri.
    /// where as an Index source may return dependencies that have the same canonicalization.
    fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;

    fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
        let mut ret = Vec::new();
        self.query(dep, &mut |s| ret.push(s))?;
        Ok(ret)
    }

    /// The update method performs any network operations required to
    /// get the entire list of all names, versions and dependencies of
    /// packages managed by the Source.
    fn update(&mut self) -> CargoResult<()>;

    /// The download method fetches the full package for each name and
    /// version specified.
    fn download(&mut self, package: &PackageId) -> CargoResult<MaybePackage>;

    fn finish_download(&mut self, package: &PackageId, contents: Vec<u8>) -> CargoResult<Package>;

    /// Generates a unique string which represents the fingerprint of the
    /// current state of the source.
    ///
    /// This fingerprint is used to determine the "fresheness" of the source
    /// later on. It must be guaranteed that the fingerprint of a source is
    /// constant if and only if the output product will remain constant.
    ///
    /// The `pkg` argument is the package which this fingerprint should only be
    /// interested in for when this source may contain multiple packages.
    fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;

    /// If this source supports it, verifies the source of the package
    /// specified.
    ///
    /// Note that the source may also have performed other checksum-based
    /// verification during the `download` step, but this is intended to be run
    /// just before a crate is compiled so it may perform more expensive checks
    /// which may not be cacheable.
    fn verify(&self, _pkg: &PackageId) -> CargoResult<()> {
        Ok(())
    }

    /// Describes this source in a human readable fashion, used for display in
    /// resolver error messages currently.
    fn describe(&self) -> String;

    /// Returns whether a source is being replaced by another here
    fn is_replaced(&self) -> bool {
        false
    }
}

pub enum MaybePackage {
    Ready(Package),
    Download { url: String, descriptor: String },
}

impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
    /// Forwards to `Source::source_id`
    fn source_id(&self) -> &SourceId {
        (**self).source_id()
    }

    /// Forwards to `Source::replaced_source_id`
    fn replaced_source_id(&self) -> &SourceId {
        (**self).replaced_source_id()
    }

    /// Forwards to `Source::supports_checksums`
    fn supports_checksums(&self) -> bool {
        (**self).supports_checksums()
    }

    /// Forwards to `Source::requires_precise`
    fn requires_precise(&self) -> bool {
        (**self).requires_precise()
    }

    /// Forwards to `Source::query`
    fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        (**self).query(dep, f)
    }

    /// Forwards to `Source::query`
    fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        (**self).fuzzy_query(dep, f)
    }

    /// Forwards to `Source::update`
    fn update(&mut self) -> CargoResult<()> {
        (**self).update()
    }

    /// Forwards to `Source::download`
    fn download(&mut self, id: &PackageId) -> CargoResult<MaybePackage> {
        (**self).download(id)
    }

    fn finish_download(&mut self, id: &PackageId, data: Vec<u8>) -> CargoResult<Package> {
        (**self).finish_download(id, data)
    }

    /// Forwards to `Source::fingerprint`
    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
        (**self).fingerprint(pkg)
    }

    /// Forwards to `Source::verify`
    fn verify(&self, pkg: &PackageId) -> CargoResult<()> {
        (**self).verify(pkg)
    }

    fn describe(&self) -> String {
        (**self).describe()
    }

    fn is_replaced(&self) -> bool {
        (**self).is_replaced()
    }
}

impl<'a, T: Source + ?Sized + 'a> Source for &'a mut T {
    fn source_id(&self) -> &SourceId {
        (**self).source_id()
    }

    fn replaced_source_id(&self) -> &SourceId {
        (**self).replaced_source_id()
    }

    fn supports_checksums(&self) -> bool {
        (**self).supports_checksums()
    }

    fn requires_precise(&self) -> bool {
        (**self).requires_precise()
    }

    fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        (**self).query(dep, f)
    }

    fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        (**self).fuzzy_query(dep, f)
    }

    fn update(&mut self) -> CargoResult<()> {
        (**self).update()
    }

    fn download(&mut self, id: &PackageId) -> CargoResult<MaybePackage> {
        (**self).download(id)
    }

    fn finish_download(&mut self, id: &PackageId, data: Vec<u8>) -> CargoResult<Package> {
        (**self).finish_download(id, data)
    }

    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
        (**self).fingerprint(pkg)
    }

    fn verify(&self, pkg: &PackageId) -> CargoResult<()> {
        (**self).verify(pkg)
    }

    fn describe(&self) -> String {
        (**self).describe()
    }

    fn is_replaced(&self) -> bool {
        (**self).is_replaced()
    }
}

/// A `HashMap` of `SourceId` -> `Box<Source>`
#[derive(Default)]
pub struct SourceMap<'src> {
    map: HashMap<SourceId, Box<Source + 'src>>,
}

// impl debug on source requires specialization, if even desirable at all
impl<'src> fmt::Debug for SourceMap<'src> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SourceMap ")?;
        f.debug_set().entries(self.map.keys()).finish()
    }
}

/// A `std::collection::hash_map::Values` for `SourceMap`
pub type Sources<'a, 'src> = Values<'a, SourceId, Box<Source + 'src>>;

/// A `std::collection::hash_map::IterMut` for `SourceMap`
pub struct SourcesMut<'a, 'src: 'a> {
    inner: IterMut<'a, SourceId, Box<Source + 'src>>,
}

impl<'src> SourceMap<'src> {
    /// Create an empty map
    pub fn new() -> SourceMap<'src> {
        SourceMap {
            map: HashMap::new(),
        }
    }

    /// Like `HashMap::contains_key`
    pub fn contains(&self, id: &SourceId) -> bool {
        self.map.contains_key(id)
    }

    /// Like `HashMap::get`
    pub fn get(&self, id: &SourceId) -> Option<&(Source + 'src)> {
        let source = self.map.get(id);

        source.map(|s| {
            let s: &(Source + 'src) = &**s;
            s
        })
    }

    /// Like `HashMap::get_mut`
    pub fn get_mut(&mut self, id: &SourceId) -> Option<&mut (Source + 'src)> {
        self.map.get_mut(id).map(|s| {
            let s: &mut (Source + 'src) = &mut **s;
            s
        })
    }

    /// Like `HashMap::get`, but first calculates the `SourceId` from a
    /// `PackageId`
    pub fn get_by_package_id(&self, pkg_id: &PackageId) -> Option<&(Source + 'src)> {
        self.get(pkg_id.source_id())
    }

    /// Like `HashMap::insert`, but derives the SourceId key from the Source
    pub fn insert(&mut self, source: Box<Source + 'src>) {
        let id = source.source_id().clone();
        self.map.insert(id, source);
    }

    /// Like `HashMap::is_empty`
    pub fn is_empty(&self) -> bool {
        self.map.is_empty()
    }

    /// Like `HashMap::len`
    pub fn len(&self) -> usize {
        self.map.len()
    }

    /// Like `HashMap::values`
    pub fn sources<'a>(&'a self) -> Sources<'a, 'src> {
        self.map.values()
    }

    /// Like `HashMap::iter_mut`
    pub fn sources_mut<'a>(&'a mut self) -> SourcesMut<'a, 'src> {
        SourcesMut {
            inner: self.map.iter_mut(),
        }
    }
}

impl<'a, 'src> Iterator for SourcesMut<'a, 'src> {
    type Item = (&'a SourceId, &'a mut (Source + 'src));
    fn next(&mut self) -> Option<(&'a SourceId, &'a mut (Source + 'src))> {
        self.inner.next().map(|(a, b)| (a, &mut **b))
    }
}
<|endoftext|>


<commit_before>extern crate gstreamer as gst;

#[cfg(feature = "profiling-audio-buffer")]
use chrono::Utc;

use byte_slice_cast::AsSliceOf;

use std::i16;

use std::collections::vec_deque::VecDeque;

use super::{DoubleSampleExtractor};

pub const SAMPLES_NORM: f64 = 200f64;
const SAMPLES_OFFSET: f64 = SAMPLES_NORM / 2f64;

pub struct AudioBuffer {
    capacity: usize,
    pub sample_duration: f64,
    channels: usize,
    drain_size: usize,

    pub eos: bool,

    segement_first_sample: usize,
    last_buffer_last_sample: usize,
    pub first_sample: usize,
    pub last_sample: usize,
    pub samples: VecDeque<f64>,

    samples_extractor_opt: Option<DoubleSampleExtractor>,
}

impl AudioBuffer {
    pub fn new(
        caps: &gst::Caps,
        size_duration: u64,
        samples_extractor: DoubleSampleExtractor,
    ) -> Self
    {
        let structure = caps.get_structure(0)
            .expect("Couldn't get structure from audio caps");
        let rate = structure.get::<i32>("rate")
            .expect("Couldn't get rate from audio caps");

        // assert_eq!(format, S16);
        // assert_eq!(layout, Interleaved);

        let sample_duration = 1_000_000_000f64 / (rate as f64);
        let capacity = (size_duration as f64 / sample_duration) as usize;

        let drain_size = capacity / 5;

        AudioBuffer {
            capacity: capacity,
            sample_duration: sample_duration,
            channels: structure.get::<i32>("channels")
                .expect("Couldn't get channels from audio sample")
                as usize,
            drain_size: drain_size,

            eos: false,

            segement_first_sample: 0,
            last_buffer_last_sample: 0,
            first_sample: 0,
            last_sample: 0,
            samples: VecDeque::with_capacity(capacity),

            samples_extractor_opt: Some(samples_extractor),
        }
    }

    // Add samples from the GStreamer pipeline to the AudioBuffer
    // This buffer stores the complete set of samples in a time frame
    // in order to be able to represent the audio at any given precision.
    // Samples are stores as f64 suitable for on screen rendering.
    // This implementation also performs a downmix.
    // TODO: make the representation dependent on the actual context
    //       move this as a trait and make the sample normalization
    //       part of a concrete object
    // Incoming samples are merged to the existing buffer when possible
    pub fn push_gst_sample(&mut self, sample: gst::Sample) {
        #[cfg(feature = "profiling-audio-buffer")]
        let start = Utc::now();

        let buffer = sample.get_buffer()
            .expect("Couldn't get buffer from audio sample");

        let map = buffer.map_readable().unwrap();
        let incoming_samples = map.as_slice().as_slice_of::<i16>()
            .expect("Couldn't get audio samples as i16");

        #[cfg(feature = "profiling-audio-buffer")]
        let before_drain = Utc::now();

        self.eos = false;

        let segment_first_sample = (
            sample.get_segment().unwrap().get_start() as f64 / self.sample_duration
        ) as usize;
        let buffer_sample_len = incoming_samples.len() / self.channels;

        // Identify conditions for this incoming buffer:
        // 1. Incoming buffer fits at the end of current container.
        // 2. Incoming buffer is already container within stored samples.
        //    Nothing to do.
        // 3. Incoming buffer overlaps with stored samples at the end.
        // 4. Incoming buffer overlaps with stored samples at the begining.
        //    Note: this changes the first sample and requires to extend
        //    the internal container from the begining.
        // 5. Incoming buffer doesn't overlap with current buffer. In order
        //    not to let gaps between samples, the internal container is
        //    cleared first.
        // 6. The internal container is empty, import incoming buffer
        //    completely.
        let (first_sample_changed, first_sample_to_add_rel, last_sample_to_add_rel) =
            if !self.samples.is_empty()
            {   // not initializing
                let first_sample =
                    if segment_first_sample == self.segement_first_sample {
                        // receiving next buffer in the same segment
                        self.last_buffer_last_sample
                    } else {
                        // changing segment (seeking)
                        self.segement_first_sample = segment_first_sample;
                        self.last_buffer_last_sample = segment_first_sample;
                        segment_first_sample
                    };
                let last_sample = first_sample + buffer_sample_len;

                if first_sample == self.last_sample {
                    // 1. append incoming buffer to the end of internal storage
                    //println!("AudioBuffer appending full incoming buffer to the end");
                    // self.first_sample unchanged
                    self.last_sample += buffer_sample_len;
                    self.last_buffer_last_sample = self.last_sample;
                    (false, 0, buffer_sample_len)
                } else if first_sample >= self.first_sample
                && last_sample <= self.last_sample {
                    // 2. incoming buffer included in current container
                    println!("AudioBuffer incoming buffer included in current container");
                    self.last_buffer_last_sample = last_sample;
                    return;
                } else if first_sample > self.first_sample
                && first_sample < self.last_sample
                {   // 3. can append [self.last_sample, last_sample] to the end
                    println!("AudioBuffer 3 append to the end [self.last_sample: {}, last_sample: {}] (self.first_sample {}, segment_first_sample {})", self.last_sample, last_sample, self.first_sample, segment_first_sample);
                    // self.first_sample unchanged
                    let previous_last_sample = self.last_sample;
                    self.last_sample = last_sample;
                    // self.first_pts unchanged
                    self.last_buffer_last_sample = last_sample;
                    (false, previous_last_sample - segment_first_sample, buffer_sample_len)
                }
                else if last_sample < self.last_sample
                && last_sample > self.first_sample
                {   // 4. can insert [first_sample, self.first_sample] to the begining
                    println!("AudioBuffer 4. insert to the begining [first_sample, self.first_sample]");
                    let last_sample_to_add = self.first_sample;
                    self.first_sample = first_sample;
                    // self.last_sample unchanged
                    self.last_buffer_last_sample = last_sample;
                    (true, 0, last_sample_to_add - segment_first_sample)
                } else {
                    // 5. can't merge with previous buffer
                    println!("AudioBuffer 5. can't merge self.first_sample {}, self.last_sample {}, segment_first_sample {}, last_sample {}", self.first_sample, self.last_sample, segment_first_sample, last_sample);
                    self.samples.clear();
                    self.first_sample = first_sample;
                    self.last_sample = last_sample;
                    self.last_buffer_last_sample = last_sample;
                    (true, 0, buffer_sample_len)
                }
            } else {
                // 6. initializing
                self.segement_first_sample = segment_first_sample;
                self.first_sample = segment_first_sample;
                self.last_sample = segment_first_sample + buffer_sample_len;
                self.last_buffer_last_sample = self.last_sample;
                (true, 0, buffer_sample_len)
            };

        // TODO rehabilitate drain...
        /*} else if self.samples.len() + incoming_samples.len() > self.capacity
            && self.samples_extractor_opt.as_ref().unwrap().get_first_sample()
                > self.first_sample + self.drain_size
        {   // buffer will reach capacity => drain a chunk of samples
            // only if we have samples in history
            // TODO: it could be worse testing truncate instead
            // (this would require reversing the buffer alimentation
            // and iteration)
            self.samples.drain(..self.drain_size);
            self.first_sample += self.drain_size;
            false*/

        #[cfg(feature = "profiling-audio-buffer")]
        let before_storage = Utc::now();

        // normalize samples in range 0f64..SAMPLES_NORM ready to render
        // TODO: this depends on the actual context (Waveform rendering)
        // do this in a concrete implementation

        // FIXME: use gstreamer downmix
        // FIXME: select the channels using the position info
        // if more than 2 channels,
        // Use 75% for first 2 channels (assuming front left and front right)
        // Use 25% for the others
        let (front_norm_factor, others_norm_factor, front_channels) =
            if self.channels > 2 {
                (
                    0.75f64 / 2f64 / f64::from(i16::MAX) * SAMPLES_NORM / 2f64,
                    0.25f64 / ((self.channels - 2) as f64) / f64::from(i16::MAX) * SAMPLES_NORM / 2f64,
                    2
                )
            } else {
                (
                    1f64 / (self.channels as f64) / f64::from(i16::MAX) * SAMPLES_OFFSET,
                    0f64,
                    self.channels
                )
            };

        // Update container using the conditions identified above
        if !first_sample_changed || self.samples.is_empty()
        {   // samples can be push back to the container
            let mut norm_sample;
            let mut index = first_sample_to_add_rel * self.channels;
            let last = last_sample_to_add_rel * self.channels;
            while index < last {
                norm_sample = 0f64;

                for _ in 0..front_channels {
                    norm_sample += f64::from(incoming_samples[index]) * front_norm_factor;
                    index += 1;
                }
                for _ in front_channels..self.channels {
                    norm_sample += f64::from(incoming_samples[index]) * others_norm_factor;
                    index += 1;
                }
                self.samples.push_back(SAMPLES_OFFSET - norm_sample);
            };
        } else
        {   // samples must be inserted at the begining
            // => push front in reverse order
            let mut norm_sample;
            let mut index = last_sample_to_add_rel * self.channels - 1;
            let first = first_sample_to_add_rel * self.channels;
            while index >= first {
                norm_sample = 0f64;

                for _ in front_channels..self.channels {
                    norm_sample += f64::from(incoming_samples[index]) * others_norm_factor;
                    index -= 1;
                }
                for _ in 0..front_channels {
                    norm_sample += f64::from(incoming_samples[index]) * front_norm_factor;
                    index -= 1;
                }
                self.samples.push_front(SAMPLES_OFFSET - norm_sample);
            };
        }

        // Trigger specific buffer extraction
        #[cfg(feature = "profiling-audio-buffer")]
        let before_extract = Utc::now();

        if !self.samples.is_empty() {
            let mut samples_extractor = self.samples_extractor_opt.take().unwrap();
            samples_extractor.extract_samples(self, first_sample_changed);
            self.samples_extractor_opt = Some(samples_extractor);
        }

        #[cfg(feature = "profiling-audio-buffer")]
        let end = Utc::now();

        #[cfg(feature = "profiling-audio-buffer")]
        println!("audio-buffer,{},{},{},{},{},{}",
            start.time().format("%H:%M:%S%.6f"),
            before_drain.time().format("%H:%M:%S%.6f"),
            before_storage.time().format("%H:%M:%S%.6f"),
            before_extract.time().format("%H:%M:%S%.6f"),
            end.time().format("%H:%M:%S%.6f"),
            index
        );
    }

    pub fn iter(&self, first: usize, last: usize, step: usize) -> Iter {
        if first < self.first_sample {
            println!("iter {}, {}", first, self.first_sample);
        }
        assert!(first >= self.first_sample);
        let last = if last > first { last } else { first };
        Iter::new(self, first, last, step)
    }

    pub fn handle_eos(&mut self) {
        if !self.samples.is_empty() {
            self.eos = true;

            let mut samples_extractor = self.samples_extractor_opt.take().unwrap();
            samples_extractor.extract_samples(self, false);
            self.samples_extractor_opt = Some(samples_extractor);
        }
    }
}

pub struct Iter<'a> {
    buffer: &'a AudioBuffer,
    idx: usize,
    last: usize,
    step: usize,
}

impl<'a> Iter<'a> {
    fn new(buffer: &'a AudioBuffer, first: usize, last: usize, step: usize) -> Iter<'a> {
        Iter {
            buffer: buffer,
            idx: first - buffer.first_sample,
            last: buffer.samples.len().min(last - buffer.first_sample),
            step: step,
        }
    }
}

impl<'a> Iterator for Iter<'a> {
    type Item = &'a f64;

    fn next(&mut self) -> Option<Self::Item> {
        if self.idx >= self.last {
            return None;
        }

        let item = self.buffer.samples.get(self.idx);
        self.idx += self.step;

        item
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.idx >= self.last {
            return (0, Some(0));
        }

        let remaining = (self.last - self.idx) / self.step;

        (remaining, Some(remaining))
    }
}
<commit_msg>AudioBuffer: fix profiling-audio-buffer feature (#24)<commit_after>extern crate gstreamer as gst;

#[cfg(feature = "profiling-audio-buffer")]
use chrono::Utc;

use byte_slice_cast::AsSliceOf;

use std::i16;

use std::collections::vec_deque::VecDeque;

use super::{DoubleSampleExtractor};

pub const SAMPLES_NORM: f64 = 200f64;
const SAMPLES_OFFSET: f64 = SAMPLES_NORM / 2f64;

pub struct AudioBuffer {
    capacity: usize,
    pub sample_duration: f64,
    channels: usize,
    drain_size: usize,

    pub eos: bool,

    segement_first_sample: usize,
    last_buffer_last_sample: usize,
    pub first_sample: usize,
    pub last_sample: usize,
    pub samples: VecDeque<f64>,

    samples_extractor_opt: Option<DoubleSampleExtractor>,
}

impl AudioBuffer {
    pub fn new(
        caps: &gst::Caps,
        size_duration: u64,
        samples_extractor: DoubleSampleExtractor,
    ) -> Self
    {
        let structure = caps.get_structure(0)
            .expect("Couldn't get structure from audio caps");
        let rate = structure.get::<i32>("rate")
            .expect("Couldn't get rate from audio caps");

        // assert_eq!(format, S16);
        // assert_eq!(layout, Interleaved);

        let sample_duration = 1_000_000_000f64 / (rate as f64);
        let capacity = (size_duration as f64 / sample_duration) as usize;

        let drain_size = capacity / 5;

        AudioBuffer {
            capacity: capacity,
            sample_duration: sample_duration,
            channels: structure.get::<i32>("channels")
                .expect("Couldn't get channels from audio sample")
                as usize,
            drain_size: drain_size,

            eos: false,

            segement_first_sample: 0,
            last_buffer_last_sample: 0,
            first_sample: 0,
            last_sample: 0,
            samples: VecDeque::with_capacity(capacity),

            samples_extractor_opt: Some(samples_extractor),
        }
    }

    // Add samples from the GStreamer pipeline to the AudioBuffer
    // This buffer stores the complete set of samples in a time frame
    // in order to be able to represent the audio at any given precision.
    // Samples are stores as f64 suitable for on screen rendering.
    // This implementation also performs a downmix.
    // TODO: make the representation dependent on the actual context
    //       move this as a trait and make the sample normalization
    //       part of a concrete object
    // Incoming samples are merged to the existing buffer when possible
    pub fn push_gst_sample(&mut self, sample: gst::Sample) {
        #[cfg(feature = "profiling-audio-buffer")]
        let start = Utc::now();

        let buffer = sample.get_buffer()
            .expect("Couldn't get buffer from audio sample");

        let map = buffer.map_readable().unwrap();
        let incoming_samples = map.as_slice().as_slice_of::<i16>()
            .expect("Couldn't get audio samples as i16");

        #[cfg(feature = "profiling-audio-buffer")]
        let before_drain = Utc::now();

        self.eos = false;

        let segment_first_sample = (
            sample.get_segment().unwrap().get_start() as f64 / self.sample_duration
        ) as usize;
        let buffer_sample_len = incoming_samples.len() / self.channels;

        // Identify conditions for this incoming buffer:
        // 1. Incoming buffer fits at the end of current container.
        // 2. Incoming buffer is already container within stored samples.
        //    Nothing to do.
        // 3. Incoming buffer overlaps with stored samples at the end.
        // 4. Incoming buffer overlaps with stored samples at the begining.
        //    Note: this changes the first sample and requires to extend
        //    the internal container from the begining.
        // 5. Incoming buffer doesn't overlap with current buffer. In order
        //    not to let gaps between samples, the internal container is
        //    cleared first.
        // 6. The internal container is empty, import incoming buffer
        //    completely.
        let (first_sample_changed, first_sample_to_add_rel, last_sample_to_add_rel) =
            if !self.samples.is_empty()
            {   // not initializing
                let first_sample =
                    if segment_first_sample == self.segement_first_sample {
                        // receiving next buffer in the same segment
                        self.last_buffer_last_sample
                    } else {
                        // changing segment (seeking)
                        self.segement_first_sample = segment_first_sample;
                        self.last_buffer_last_sample = segment_first_sample;
                        segment_first_sample
                    };
                let last_sample = first_sample + buffer_sample_len;

                if first_sample == self.last_sample {
                    // 1. append incoming buffer to the end of internal storage
                    //println!("AudioBuffer appending full incoming buffer to the end");
                    // self.first_sample unchanged
                    self.last_sample += buffer_sample_len;
                    self.last_buffer_last_sample = self.last_sample;
                    (false, 0, buffer_sample_len)
                } else if first_sample >= self.first_sample
                && last_sample <= self.last_sample {
                    // 2. incoming buffer included in current container
                    println!("AudioBuffer incoming buffer included in current container");
                    self.last_buffer_last_sample = last_sample;
                    return;
                } else if first_sample > self.first_sample
                && first_sample < self.last_sample
                {   // 3. can append [self.last_sample, last_sample] to the end
                    println!("AudioBuffer 3 append to the end [self.last_sample: {}, last_sample: {}] (self.first_sample {}, segment_first_sample {})", self.last_sample, last_sample, self.first_sample, segment_first_sample);
                    // self.first_sample unchanged
                    let previous_last_sample = self.last_sample;
                    self.last_sample = last_sample;
                    // self.first_pts unchanged
                    self.last_buffer_last_sample = last_sample;
                    (false, previous_last_sample - segment_first_sample, buffer_sample_len)
                }
                else if last_sample < self.last_sample
                && last_sample > self.first_sample
                {   // 4. can insert [first_sample, self.first_sample] to the begining
                    println!("AudioBuffer 4. insert to the begining [first_sample, self.first_sample]");
                    let last_sample_to_add = self.first_sample;
                    self.first_sample = first_sample;
                    // self.last_sample unchanged
                    self.last_buffer_last_sample = last_sample;
                    (true, 0, last_sample_to_add - segment_first_sample)
                } else {
                    // 5. can't merge with previous buffer
                    println!("AudioBuffer 5. can't merge self.first_sample {}, self.last_sample {}, segment_first_sample {}, last_sample {}", self.first_sample, self.last_sample, segment_first_sample, last_sample);
                    self.samples.clear();
                    self.first_sample = first_sample;
                    self.last_sample = last_sample;
                    self.last_buffer_last_sample = last_sample;
                    (true, 0, buffer_sample_len)
                }
            } else {
                // 6. initializing
                self.segement_first_sample = segment_first_sample;
                self.first_sample = segment_first_sample;
                self.last_sample = segment_first_sample + buffer_sample_len;
                self.last_buffer_last_sample = self.last_sample;
                (true, 0, buffer_sample_len)
            };

        // TODO rehabilitate drain...
        /*} else if self.samples.len() + incoming_samples.len() > self.capacity
            && self.samples_extractor_opt.as_ref().unwrap().get_first_sample()
                > self.first_sample + self.drain_size
        {   // buffer will reach capacity => drain a chunk of samples
            // only if we have samples in history
            // TODO: it could be worse testing truncate instead
            // (this would require reversing the buffer alimentation
            // and iteration)
            self.samples.drain(..self.drain_size);
            self.first_sample += self.drain_size;
            false*/

        #[cfg(feature = "profiling-audio-buffer")]
        let before_storage = Utc::now();

        // normalize samples in range 0f64..SAMPLES_NORM ready to render
        // TODO: this depends on the actual context (Waveform rendering)
        // do this in a concrete implementation

        // FIXME: use gstreamer downmix
        // FIXME: select the channels using the position info
        // if more than 2 channels,
        // Use 75% for first 2 channels (assuming front left and front right)
        // Use 25% for the others
        let (front_norm_factor, others_norm_factor, front_channels) =
            if self.channels > 2 {
                (
                    0.75f64 / 2f64 / f64::from(i16::MAX) * SAMPLES_NORM / 2f64,
                    0.25f64 / ((self.channels - 2) as f64) / f64::from(i16::MAX) * SAMPLES_NORM / 2f64,
                    2
                )
            } else {
                (
                    1f64 / (self.channels as f64) / f64::from(i16::MAX) * SAMPLES_OFFSET,
                    0f64,
                    self.channels
                )
            };

        // Update container using the conditions identified above
        if !first_sample_changed || self.samples.is_empty()
        {   // samples can be push back to the container
            let mut norm_sample;
            let mut index = first_sample_to_add_rel * self.channels;
            let last = last_sample_to_add_rel * self.channels;
            while index < last {
                norm_sample = 0f64;

                for _ in 0..front_channels {
                    norm_sample += f64::from(incoming_samples[index]) * front_norm_factor;
                    index += 1;
                }
                for _ in front_channels..self.channels {
                    norm_sample += f64::from(incoming_samples[index]) * others_norm_factor;
                    index += 1;
                }
                self.samples.push_back(SAMPLES_OFFSET - norm_sample);
            };
        } else
        {   // samples must be inserted at the begining
            // => push front in reverse order
            let mut norm_sample;
            let mut index = last_sample_to_add_rel * self.channels - 1;
            let first = first_sample_to_add_rel * self.channels;
            while index >= first {
                norm_sample = 0f64;

                for _ in front_channels..self.channels {
                    norm_sample += f64::from(incoming_samples[index]) * others_norm_factor;
                    index -= 1;
                }
                for _ in 0..front_channels {
                    norm_sample += f64::from(incoming_samples[index]) * front_norm_factor;
                    index -= 1;
                }
                self.samples.push_front(SAMPLES_OFFSET - norm_sample);
            };
        }

        // Trigger specific buffer extraction
        #[cfg(feature = "profiling-audio-buffer")]
        let before_extract = Utc::now();

        if !self.samples.is_empty() {
            let mut samples_extractor = self.samples_extractor_opt.take().unwrap();
            samples_extractor.extract_samples(self, first_sample_changed);
            self.samples_extractor_opt = Some(samples_extractor);
        }

        #[cfg(feature = "profiling-audio-buffer")]
        let end = Utc::now();

        #[cfg(feature = "profiling-audio-buffer")]
        println!("audio-buffer,{},{},{},{},{}",
            start.time().format("%H:%M:%S%.6f"),
            before_drain.time().format("%H:%M:%S%.6f"),
            before_storage.time().format("%H:%M:%S%.6f"),
            before_extract.time().format("%H:%M:%S%.6f"),
            end.time().format("%H:%M:%S%.6f"),
        );
    }

    pub fn iter(&self, first: usize, last: usize, step: usize) -> Iter {
        if first < self.first_sample {
            println!("iter {}, {}", first, self.first_sample);
        }
        assert!(first >= self.first_sample);
        let last = if last > first { last } else { first };
        Iter::new(self, first, last, step)
    }

    pub fn handle_eos(&mut self) {
        if !self.samples.is_empty() {
            self.eos = true;

            let mut samples_extractor = self.samples_extractor_opt.take().unwrap();
            samples_extractor.extract_samples(self, false);
            self.samples_extractor_opt = Some(samples_extractor);
        }
    }
}

pub struct Iter<'a> {
    buffer: &'a AudioBuffer,
    idx: usize,
    last: usize,
    step: usize,
}

impl<'a> Iter<'a> {
    fn new(buffer: &'a AudioBuffer, first: usize, last: usize, step: usize) -> Iter<'a> {
        Iter {
            buffer: buffer,
            idx: first - buffer.first_sample,
            last: buffer.samples.len().min(last - buffer.first_sample),
            step: step,
        }
    }
}

impl<'a> Iterator for Iter<'a> {
    type Item = &'a f64;

    fn next(&mut self) -> Option<Self::Item> {
        if self.idx >= self.last {
            return None;
        }

        let item = self.buffer.samples.get(self.idx);
        self.idx += self.step;

        item
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.idx >= self.last {
            return (0, Some(0));
        }

        let remaining = (self.last - self.idx) / self.step;

        (remaining, Some(remaining))
    }
}
<|endoftext|>


<commit_before>use ::std::io::Cursor;

use crypto::{PublicKey, KeyPair, SigPair};
use serializer::*;
use asset::Asset;

#[macro_use] mod util;

pub mod tx_type;
pub use self::tx_type::*;

pub trait EncodeTx {
    fn encode(&self, v: &mut Vec<u8>);
}

pub trait DecodeTx<T> {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<T>;
}

pub trait SignTx {
    fn sign(&self, key_pair: &KeyPair) -> SigPair;
    fn append_sign(&mut self, key_pair: &KeyPair);
}

#[derive(Debug, Clone)]
pub enum TxVariant {
    RewardTx(RewardTx),
    BondTx(BondTx),
    TransferTx(TransferTx)
}

impl TxVariant {
    pub fn encode(&self, v: &mut Vec<u8>) {
        match self {
            TxVariant::RewardTx(tx) => { tx.encode(v) },
            TxVariant::BondTx(tx) => { tx.encode(v) },
            TxVariant::TransferTx(tx) => { tx.encode(v) }
        };
    }

    pub fn encode_with_sigs(&self, v: &mut Vec<u8>) {
        macro_rules! encode_sigs {
            ($name:expr, $vec:expr) => {
                {
                    $vec.push_u16($name.signature_pairs.len() as u16);
                    for sig in &$name.signature_pairs { $vec.push_sig_pair(sig) };
                    $name.encode($vec);
                }
            }
        }

        match self {
            TxVariant::RewardTx(tx) => { encode_sigs!(tx, v) },
            TxVariant::BondTx(tx) => { encode_sigs!(tx, v) },
            TxVariant::TransferTx(tx) => { encode_sigs!(tx, v) }
        };
    }

    pub fn decode_with_sigs(cur: &mut Cursor<&[u8]>) -> Option<TxVariant> {
        let sigs = {
            let len = cur.take_u16()?;
            let mut vec = Vec::with_capacity(len as usize);
            for _ in 0..len { vec.push(cur.take_sig_pair()?) };
            vec
        };
        let mut base = Tx::decode_base(cur)?;
        base.signature_pairs = sigs;
        match base.tx_type {
            TxType::REWARD => Some(TxVariant::RewardTx(RewardTx::decode(cur, base)?)),
            TxType::BOND => Some(TxVariant::BondTx(BondTx::decode(cur, base)?)),
            TxType::TRANSFER => Some(TxVariant::TransferTx(TransferTx::decode(cur, base)?))
        }
    }
}

impl ::std::ops::Deref for TxVariant {
    type Target = Tx;

    fn deref(&self) -> &Self::Target {
        match self {
            TxVariant::RewardTx(tx) => &tx.base,
            TxVariant::BondTx(tx) => &tx.base,
            TxVariant::TransferTx(tx) => &tx.base
        }
    }
}

#[derive(Debug, Clone)]
pub struct Tx {
    pub tx_type: TxType,
    pub timestamp: u32,
    pub fee: Asset,
    pub signature_pairs: Vec<SigPair>
}

impl Tx {
    fn encode_base(&self, v: &mut Vec<u8>) {
        v.push(self.tx_type as u8);
        v.push_u32(self.timestamp);
        v.push_asset(&self.fee);
    }

    fn decode_base(cur: &mut Cursor<&[u8]>) -> Option<Tx> {
        let tx_type = match cur.take_u8()? {
            t if t == TxType::REWARD as u8 => TxType::REWARD,
            t if t == TxType::BOND as u8 => TxType::BOND,
            t if t == TxType::TRANSFER as u8 => TxType::TRANSFER,
            _ => return None
        };
        let timestamp = cur.take_u32()?;
        let fee = cur.take_asset()?;

        Some(Tx {
            tx_type,
            timestamp,
            fee,
            signature_pairs: Vec::new()
        })
    }
}

#[derive(Debug, Clone)]
pub struct RewardTx {
    pub base: Tx,
    pub to: PublicKey,
    pub rewards: Vec<Asset>
}

impl EncodeTx for RewardTx {
    fn encode(&self, v: &mut Vec<u8>) {
        debug_assert_eq!(self.base.signature_pairs.len(), 0);
        self.encode_base(v);
        v.push_pub_key(&self.to);
        v.push_u32(self.rewards.len() as u32);
        for r in &self.rewards { v.push_asset(r) };
    }
}

impl DecodeTx<RewardTx> for RewardTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<RewardTx> {
        assert_eq!(tx.tx_type, TxType::REWARD);
        let key = cur.take_pub_key()?;

        let len = cur.take_u32()?;
        let mut rewards = Vec::with_capacity(len as usize);
        for _ in 0..len {
            rewards.push(cur.take_asset()?);
        }

        Some(RewardTx {
            base: tx,
            to: key,
            rewards
        })
    }
}

#[derive(Debug, Clone)]
pub struct BondTx {
    pub base: Tx,
    pub minter: PublicKey, // Key that signs blocks
    pub staker: PublicKey, // Hot wallet that receives rewards and stakes its balance
    pub stake_amt: Asset,
    pub bond_fee: Asset
}

impl EncodeTx for BondTx {
    fn encode(&self, v: &mut Vec<u8>) {
        self.encode_base(v);
        v.push_pub_key(&self.minter);
        v.push_pub_key(&self.staker);
        v.push_asset(&self.stake_amt);
        v.push_asset(&self.bond_fee);
    }
}

impl DecodeTx<BondTx> for BondTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<BondTx> {
        assert_eq!(tx.tx_type, TxType::BOND);
        let minter = cur.take_pub_key()?;
        let staker = cur.take_pub_key()?;
        let stake_amt = cur.take_asset()?;
        let bond_fee = cur.take_asset()?;
        Some(BondTx {
            base: tx,
            minter,
            staker,
            stake_amt,
            bond_fee
        })
    }
}

#[derive(Debug, Clone)]
pub struct TransferTx {
    pub base: Tx,
    pub from: PublicKey,
    pub to: PublicKey,
    pub amount: Asset,
    pub memo: Vec<u8>
}

impl EncodeTx for TransferTx {
    fn encode(&self, v: &mut Vec<u8>) {
        self.encode_base(v);
        v.push_pub_key(&self.from);
        v.push_pub_key(&self.to);
        v.push_asset(&self.amount);
        v.push_bytes(&self.memo);
    }
}

impl DecodeTx<TransferTx> for TransferTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<TransferTx> {
        assert_eq!(tx.tx_type, TxType::TRANSFER);
        let from = cur.take_pub_key()?;
        let to = cur.take_pub_key()?;
        let amount = cur.take_asset()?;
        let memo = cur.take_bytes()?;
        Some(TransferTx {
            base: tx,
            from,
            to,
            amount,
            memo
        })
    }
}

tx_deref!(RewardTx);
tx_deref!(BondTx);
tx_deref!(TransferTx);

tx_sign!(BondTx);
tx_sign!(TransferTx);

#[cfg(test)]
mod tests {
    use ::std::str::FromStr;
    use super::*;
    use crypto;

    macro_rules! cmp_base_tx {
        ($id:ident, $ty:expr, $ts:expr, $fee:expr) => {
            assert_eq!($id.tx_type, $ty);
            assert_eq!($id.timestamp, $ts);
            assert_eq!($id.fee.to_string(), $fee);
        }
    }

    #[test]
    fn test_encode_tx_with_sigs() {
        let to = crypto::KeyPair::gen_keypair();
        let reward_tx = TxVariant::RewardTx(RewardTx {
            base: Tx {
                tx_type: TxType::REWARD,
                timestamp: 123,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            to: to.0,
            rewards: vec![get_asset("1.50 GOLD"), get_asset("1.0 SILVER")]
        });

        let mut v = vec![];
        reward_tx.encode_with_sigs(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        TxVariant::decode_with_sigs(&mut c).unwrap();
    }

    #[test]
    fn test_encode_reward() {
        let to = crypto::KeyPair::gen_keypair();
        let reward_tx = RewardTx {
            base: Tx {
                tx_type: TxType::REWARD,
                timestamp: 123,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            to: to.0,
            rewards: vec![get_asset("1.50 GOLD"), get_asset("1.0 SILVER")]
        };

        let mut v = vec![];
        reward_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = RewardTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::REWARD, 123, "123 GOLD");
        assert_eq!(reward_tx.to, dec.to);
        assert_eq!(reward_tx.rewards.len(), dec.rewards.len());
        assert_eq!(reward_tx.rewards[0].to_string(), dec.rewards[0].to_string());
        assert_eq!(reward_tx.rewards[1].to_string(), dec.rewards[1].to_string());
    }

    #[test]
    fn test_encode_bond() {
        let minter = crypto::KeyPair::gen_keypair();
        let staker = crypto::KeyPair::gen_keypair();
        let bond_tx = BondTx {
            base: Tx {
                tx_type: TxType::BOND,
                timestamp: 1230,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            minter: minter.0,
            staker: staker.0,
            stake_amt: get_asset("1.0456 GOLD"),
            bond_fee: get_asset("1.00000000 GOLD")
        };

        let mut v = vec![];
        bond_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = BondTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::BOND, 1230, "123 GOLD");
        assert_eq!(bond_tx.minter, dec.minter);
        assert_eq!(bond_tx.staker, dec.staker);
        assert_eq!(bond_tx.stake_amt.to_string(), dec.stake_amt.to_string());
        assert_eq!(bond_tx.bond_fee.to_string(), dec.bond_fee.to_string());
    }

    #[test]
    fn test_encode_transfer() {
        let from = crypto::KeyPair::gen_keypair();
        let to = crypto::KeyPair::gen_keypair();
        let transfer_tx = TransferTx {
            base: Tx {
                tx_type: TxType::TRANSFER,
                timestamp: 1234567890,
                fee: get_asset("1.23 GOLD"),
                signature_pairs: vec![]
            },
            from: from.0,
            to: to.0,
            amount: get_asset("1.0456 GOLD"),
            memo: Vec::from(b"Hello world!")
        };

        let mut v = vec![];
        transfer_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = TransferTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::TRANSFER, 1234567890, "1.23 GOLD");
        assert_eq!(transfer_tx.from, dec.from);
        assert_eq!(transfer_tx.to, dec.to);
        assert_eq!(transfer_tx.amount.to_string(), dec.amount.to_string());
        assert_eq!(transfer_tx.memo, dec.memo);
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}

<commit_msg>Revert "Simplify encode_transfer test"<commit_after>use ::std::io::Cursor;

use crypto::{PublicKey, KeyPair, SigPair};
use serializer::*;
use asset::Asset;

#[macro_use] mod util;

pub mod tx_type;
pub use self::tx_type::*;

pub trait EncodeTx {
    fn encode(&self, v: &mut Vec<u8>);
}

pub trait DecodeTx<T> {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<T>;
}

pub trait SignTx {
    fn sign(&self, key_pair: &KeyPair) -> SigPair;
    fn append_sign(&mut self, key_pair: &KeyPair);
}

#[derive(Debug, Clone)]
pub enum TxVariant {
    RewardTx(RewardTx),
    BondTx(BondTx),
    TransferTx(TransferTx)
}

impl TxVariant {
    pub fn encode(&self, v: &mut Vec<u8>) {
        match self {
            TxVariant::RewardTx(tx) => { tx.encode(v) },
            TxVariant::BondTx(tx) => { tx.encode(v) },
            TxVariant::TransferTx(tx) => { tx.encode(v) }
        };
    }

    pub fn encode_with_sigs(&self, v: &mut Vec<u8>) {
        macro_rules! encode_sigs {
            ($name:expr, $vec:expr) => {
                {
                    $vec.push_u16($name.signature_pairs.len() as u16);
                    for sig in &$name.signature_pairs { $vec.push_sig_pair(sig) };
                    $name.encode($vec);
                }
            }
        }

        match self {
            TxVariant::RewardTx(tx) => { encode_sigs!(tx, v) },
            TxVariant::BondTx(tx) => { encode_sigs!(tx, v) },
            TxVariant::TransferTx(tx) => { encode_sigs!(tx, v) }
        };
    }

    pub fn decode_with_sigs(cur: &mut Cursor<&[u8]>) -> Option<TxVariant> {
        let sigs = {
            let len = cur.take_u16()?;
            let mut vec = Vec::with_capacity(len as usize);
            for _ in 0..len { vec.push(cur.take_sig_pair()?) };
            vec
        };
        let mut base = Tx::decode_base(cur)?;
        base.signature_pairs = sigs;
        match base.tx_type {
            TxType::REWARD => Some(TxVariant::RewardTx(RewardTx::decode(cur, base)?)),
            TxType::BOND => Some(TxVariant::BondTx(BondTx::decode(cur, base)?)),
            TxType::TRANSFER => Some(TxVariant::TransferTx(TransferTx::decode(cur, base)?))
        }
    }
}

impl ::std::ops::Deref for TxVariant {
    type Target = Tx;

    fn deref(&self) -> &Self::Target {
        match self {
            TxVariant::RewardTx(tx) => &tx.base,
            TxVariant::BondTx(tx) => &tx.base,
            TxVariant::TransferTx(tx) => &tx.base
        }
    }
}

#[derive(Debug, Clone)]
pub struct Tx {
    pub tx_type: TxType,
    pub timestamp: u32,
    pub fee: Asset,
    pub signature_pairs: Vec<SigPair>
}

impl Tx {
    fn encode_base(&self, v: &mut Vec<u8>) {
        v.push(self.tx_type as u8);
        v.push_u32(self.timestamp);
        v.push_asset(&self.fee);
    }

    fn decode_base(cur: &mut Cursor<&[u8]>) -> Option<Tx> {
        let tx_type = match cur.take_u8()? {
            t if t == TxType::REWARD as u8 => TxType::REWARD,
            t if t == TxType::BOND as u8 => TxType::BOND,
            t if t == TxType::TRANSFER as u8 => TxType::TRANSFER,
            _ => return None
        };
        let timestamp = cur.take_u32()?;
        let fee = cur.take_asset()?;

        Some(Tx {
            tx_type,
            timestamp,
            fee,
            signature_pairs: Vec::new()
        })
    }
}

#[derive(Debug, Clone)]
pub struct RewardTx {
    pub base: Tx,
    pub to: PublicKey,
    pub rewards: Vec<Asset>
}

impl EncodeTx for RewardTx {
    fn encode(&self, v: &mut Vec<u8>) {
        debug_assert_eq!(self.base.signature_pairs.len(), 0);
        self.encode_base(v);
        v.push_pub_key(&self.to);
        v.push_u32(self.rewards.len() as u32);
        for r in &self.rewards { v.push_asset(r) };
    }
}

impl DecodeTx<RewardTx> for RewardTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<RewardTx> {
        assert_eq!(tx.tx_type, TxType::REWARD);
        let key = cur.take_pub_key()?;

        let len = cur.take_u32()?;
        let mut rewards = Vec::with_capacity(len as usize);
        for _ in 0..len {
            rewards.push(cur.take_asset()?);
        }

        Some(RewardTx {
            base: tx,
            to: key,
            rewards
        })
    }
}

#[derive(Debug, Clone)]
pub struct BondTx {
    pub base: Tx,
    pub minter: PublicKey, // Key that signs blocks
    pub staker: PublicKey, // Hot wallet that receives rewards and stakes its balance
    pub stake_amt: Asset,
    pub bond_fee: Asset
}

impl EncodeTx for BondTx {
    fn encode(&self, v: &mut Vec<u8>) {
        self.encode_base(v);
        v.push_pub_key(&self.minter);
        v.push_pub_key(&self.staker);
        v.push_asset(&self.stake_amt);
        v.push_asset(&self.bond_fee);
    }
}

impl DecodeTx<BondTx> for BondTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<BondTx> {
        assert_eq!(tx.tx_type, TxType::BOND);
        let minter = cur.take_pub_key()?;
        let staker = cur.take_pub_key()?;
        let stake_amt = cur.take_asset()?;
        let bond_fee = cur.take_asset()?;
        Some(BondTx {
            base: tx,
            minter,
            staker,
            stake_amt,
            bond_fee
        })
    }
}

#[derive(Debug, Clone)]
pub struct TransferTx {
    pub base: Tx,
    pub from: PublicKey,
    pub to: PublicKey,
    pub amount: Asset,
    pub memo: Vec<u8>
}

impl EncodeTx for TransferTx {
    fn encode(&self, v: &mut Vec<u8>) {
        self.encode_base(v);
        v.push_pub_key(&self.from);
        v.push_pub_key(&self.to);
        v.push_asset(&self.amount);
        v.push_bytes(&self.memo);
    }
}

impl DecodeTx<TransferTx> for TransferTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<TransferTx> {
        assert_eq!(tx.tx_type, TxType::TRANSFER);
        let from = cur.take_pub_key()?;
        let to = cur.take_pub_key()?;
        let amount = cur.take_asset()?;
        let memo = cur.take_bytes()?;
        Some(TransferTx {
            base: tx,
            from,
            to,
            amount,
            memo
        })
    }
}

tx_deref!(RewardTx);
tx_deref!(BondTx);
tx_deref!(TransferTx);

tx_sign!(BondTx);
tx_sign!(TransferTx);

#[cfg(test)]
mod tests {
    use ::std::str::FromStr;
    use super::*;
    use crypto;

    macro_rules! cmp_base_tx {
        ($id:ident, $ty:expr, $ts:expr, $fee:expr) => {
            assert_eq!($id.tx_type, $ty);
            assert_eq!($id.timestamp, $ts);
            assert_eq!($id.fee.to_string(), $fee);
        }
    }

    #[test]
    fn test_encode_tx_with_sigs() {
        let to = crypto::KeyPair::gen_keypair();
        let reward_tx = TxVariant::RewardTx(RewardTx {
            base: Tx {
                tx_type: TxType::REWARD,
                timestamp: 123,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            to: to.0,
            rewards: vec![get_asset("1.50 GOLD"), get_asset("1.0 SILVER")]
        });

        let mut v = vec![];
        reward_tx.encode_with_sigs(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        TxVariant::decode_with_sigs(&mut c).unwrap();
    }

    #[test]
    fn test_encode_reward() {
        let to = crypto::KeyPair::gen_keypair();
        let reward_tx = RewardTx {
            base: Tx {
                tx_type: TxType::REWARD,
                timestamp: 123,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            to: to.0,
            rewards: vec![get_asset("1.50 GOLD"), get_asset("1.0 SILVER")]
        };

        let mut v = vec![];
        reward_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = RewardTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::REWARD, 123, "123 GOLD");
        assert_eq!(reward_tx.to, dec.to);
        assert_eq!(reward_tx.rewards.len(), dec.rewards.len());
        assert_eq!(reward_tx.rewards[0].to_string(), dec.rewards[0].to_string());
        assert_eq!(reward_tx.rewards[1].to_string(), dec.rewards[1].to_string());
    }

    #[test]
    fn test_encode_bond() {
        let minter = crypto::KeyPair::gen_keypair();
        let staker = crypto::KeyPair::gen_keypair();
        let bond_tx = BondTx {
            base: Tx {
                tx_type: TxType::BOND,
                timestamp: 1230,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            minter: minter.0,
            staker: staker.0,
            stake_amt: get_asset("1.0456 GOLD"),
            bond_fee: get_asset("1.00000000 GOLD")
        };

        let mut v = vec![];
        bond_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = BondTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::BOND, 1230, "123 GOLD");
        assert_eq!(bond_tx.minter, dec.minter);
        assert_eq!(bond_tx.staker, dec.staker);
        assert_eq!(bond_tx.stake_amt.to_string(), dec.stake_amt.to_string());
        assert_eq!(bond_tx.bond_fee.to_string(), dec.bond_fee.to_string());
    }

    #[test]
    fn test_encode_transfer() {
        let from = crypto::KeyPair::gen_keypair();
        let to = crypto::KeyPair::gen_keypair();
        let transfer_tx = TransferTx {
            base: Tx {
                tx_type: TxType::TRANSFER,
                timestamp: 1234567890,
                fee: get_asset("1.23 GOLD"),
                signature_pairs: vec![]
            },
            from: from.0,
            to: to.0,
            amount: get_asset("1.0456 GOLD"),
            memo: Vec::from(String::from("Hello world!").as_bytes())
        };

        let mut v = vec![];
        transfer_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = TransferTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::TRANSFER, 1234567890, "1.23 GOLD");
        assert_eq!(transfer_tx.from, dec.from);
        assert_eq!(transfer_tx.to, dec.to);
        assert_eq!(transfer_tx.amount.to_string(), dec.amount.to_string());
        assert_eq!(transfer_tx.memo, dec.memo);
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}

<|endoftext|>